文章的开头,先贴一段使用chaincode dev mode调试智能合约的官方文档。
应用的官网文档开始
Building Chaincode
Now let’s compile your chaincode.
go get -u github.com/hyperledger/fabric/core/chaincode/shim
go build
Assuming there are no errors, now we can proceed to the next step, testing your chaincode.
Testing Using dev mode
Normally chaincodes are started and maintained by peer. However in “dev mode", chaincode is built and started by the user. This mode is useful during chaincode development phase for rapid code/build/run/debug cycle turnaround.
We start “dev mode” by leveraging pre-generated orderer and channel artifacts for a sample dev network. As such, the user can immediately jump into the process of compiling chaincode and driving calls.
Install Hyperledger Fabric Samples
If you haven’t already done so, please :doc:install
.
Navigate to the chaincode-docker-devmode
directory of the fabric-samples
clone:
cd chaincode-docker-devmode
Now open three terminals and navigate to your chaincode-docker-devmode
directory in each.
Terminal 1 - Start the network
docker-compose -f docker-compose-simple.yaml up
The above starts the network with the SingleSampleMSPSolo
orderer profile and launches the peer in “dev mode”. It also launches two additional containers - one for the chaincode environment and a CLI to interact with the chaincode. The commands for create and join channel are embedded in the CLI container, so we can jump immediately to the chaincode calls.
Terminal 2 - Build & start the chaincode
docker exec -it chaincode bash
You should see the following:
root@d2629980e76b:/opt/gopath/src/chaincode#
Now, compile your chaincode:
cd sacc
go build
Now run the chaincode:
CORE_PEER_ADDRESS=peer:7052 CORE_CHAINCODE_ID_NAME=mycc:0 ./sacc
The chaincode is started with peer and chaincode logs indicating successful registration with the peer.
Note that at this stage the chaincode is not associated with any channel. This is done in subsequent steps
using the instantiate
command.
Terminal 3 - Use the chaincode
Even though you are in --peer-chaincodedev
mode, you still have to install the chaincode so the life-cycle system chaincode can go through its checks normally.
This requirement may be removed in future when in --peer-chaincodedev
mode.
We’ll leverage the CLI container to drive these calls.
docker exec -it cli bash
peer chaincode install -p chaincodedev/chaincode/sacc -n mycc -v 0
peer chaincode instantiate -n mycc -v 0 -c '{"Args":["a","10"]}' -C myc
Now issue an invoke to change the value of “a” to “20”.
peer chaincode invoke -n mycc -c '{"Args":["set", "a", "20"]}' -C myc
Finally, query a
. We should see a value of 20
.
peer chaincode query -n mycc -c '{"Args":["query","a"]}' -C myc
Testing new chaincode
By default, we mount only sacc
. However, you can easily test different chaincodes by adding them to the chaincode
subdirectory and relaunching your network. At this point they will be accessible in your chaincode
container.
Chaincode access control
Chaincode can utilize the client (submitter) certificate for access
control decisions by calling the GetCreator() function. Additionally
the Go shim provides extension APIs that extract client identity
from the submitter’s certificate that can be used for access control decisions, whether that is based on client identity itself, or the org identity, or on a client identity attribute.
For example an asset that is represented as a key/value may include the client’s identity as part of the value (for example as a JSON attribute indicating that asset owner), and only this client may be authorized to make updates to the key/value in the future. The client identity library extension APIs can be used within chaincode to retrieve this submitter information to make such access control decisions.
See the `client identity (CID) library documentation for more details.
To add the client identity shim extension to your chaincode as a dependency, see :ref:vendoring
.
应用官网文档结束
按照上面的步骤,我在Terminal 1,cli容器就退出了
orderer | 2019-04-29 07:47:22.284 UTC [comm.grpc.server] 1 -> INFO 3c3 streaming call completed grpc.service=orderer.AtomicBroadcast grpc.method=Broadcast grpc.peer_address=172.18.0.4:53470 grpc.code=OK grpc.call_duration=1.796324ms
orderer | 2019-04-29 07:47:22.286 UTC [grpc] warningf -> DEBU 3c4 transport: http2Server.HandleStreams failed to read frame: read tcp 172.18.0.2:7050->172.18.0.4:53470: read: connection reset by peer
orderer | 2019-04-29 07:47:22.286 UTC [grpc] infof -> DEBU 3c5 transport: loopyWriter.run returning. connection error: desc = "transport is closing"
orderer | 2019-04-29 07:47:22.286 UTC [grpc] infof -> DEBU 3c6 transport: loopyWriter.run returning. connection error: desc = "transport is closing"
orderer | 2019-04-29 07:47:22.286 UTC [common.deliver] Handle -> WARN 3c7 Error reading from 172.18.0.4:53468: rpc error: code = Canceled desc = context canceled
orderer | 2019-04-29 07:47:22.286 UTC [orderer.common.server] func1 -> DEBU 3c8 Closing Deliver stream
orderer | 2019-04-29 07:47:22.286 UTC [comm.grpc.server] 1 -> INFO 3c9 streaming call completed grpc.service=orderer.AtomicBroadcast grpc.method=Deliver grpc.peer_address=172.18.0.4:53468 error="rpc error: code = Canceled desc = context canceled" grpc.code=Canceled grpc.call_duration=5.854975ms
cli exited with code 1
一开始试多几次,总有一次能成功,且还有其他问题(即将介绍),我就先去解决那个问题,后面一直都失败,逼迫我去寻找解决办法,
解决办法:
注释docker-compose-simple.yaml文件中cli容器的command字段,cli容器即可正常启动。
# command: /bin/bash -c './script.sh'
这个脚本用来在cli容器启动的时候自动创建通道,我们可以后面手动启动,即在进入cli容器再启动。
因为我的chaincode需要身份信息,百度了一翻,看到了CSDN一位同学的代码,引用了CID包(github.com/hyperledger/fabric/core/chaincode/lib/cid)。
但是这个包没有被打包到ccenv镜像(chaincode所用镜像),在chaincode容器(ccenv镜像)中go build失败,在cli容器(tools镜像)install chaincode的时候报错:
2019-04-17 08:46:54.280 UTC [chaincode.platform.golang] getCodeFromFS -> DEBU 045 getCodeFromFS chaincodedev/chaincode/billManager
2019-04-17 08:46:54.466 UTC [chaincode.platform.golang] func1 -> DEBU 046 Discarding GOROOT package encoding/json
2019-04-17 08:46:54.466 UTC [chaincode.platform.golang] func1 -> DEBU 047 Discarding GOROOT package fmt
2019-04-17 08:46:54.466 UTC [chaincode.platform.golang] func1 -> DEBU 048 Accepting import: github.com/hyperledger/fabric/core/chaincode/lib/cid
2019-04-17 08:46:54.466 UTC [chaincode.platform.golang] func1 -> DEBU 049 Discarding provided package github.com/hyperledger/fabric/core/chaincode/shim
2019-04-17 08:46:54.466 UTC [chaincode.platform.golang] func1 -> DEBU 04a Discarding provided package github.com/hyperledger/fabric/protos/peer
2019-04-17 08:46:54.466 UTC [chaincode.platform.golang] func1 -> DEBU 04b Discarding GOROOT package strconv
Error: error getting chaincode code bill: error getting chaincode package bytes: Error obtaining dependencies for github.com/hyperledger/fabric/core/chaincode/lib/cid: command <go list -f {{ join .Deps "\n"}} github.com/hyperledger/fabric/core/chaincode/lib/cid>: failed with error: "exit status 1"
can't load package: package github.com/hyperledger/fabric/core/chaincode/lib/cid: cannot find package "github.com/hyperledger/fabric/core/chaincode/lib/cid" in any of:
/opt/go/src/github.com/hyperledger/fabric/core/chaincode/lib/cid (from $GOROOT)
/opt/gopath/src/github.com/hyperledger/fabric/core/chaincode/lib/cid (from $GOPATH)
分析打印信息,我觉得打印中的[chaincode.platform.golang]对应于代码库,果然在core/chaincode/platform/golang/platform找到了对于的代码。
// --------------------------------------------------------------------------------------
// Remove any imports that are provided by the ccenv or system
// --------------------------------------------------------------------------------------
var provided = map[string]bool{
"github.com/hyperledger/fabric/core/chaincode/shim": true,
"github.com/hyperledger/fabric/protos/peer": true,
}
这段代码的作用应该是编译的时候忽略这些包的检查,添加CID包应该就可以了
// --------------------------------------------------------------------------------------
// Remove any imports that are provided by the ccenv or system
// --------------------------------------------------------------------------------------
var provided = map[string]bool{
"github.com/hyperledger/fabric/core/chaincode/shim": true,
"github.com/hyperledger/fabric/protos/peer": true,
"github.com/hyperledger/fabric/core/chaincode/shim/ext/cid": true,
}
然后重新编译就可以了。
值得注意的是,修改了core里面的代码,需要clean之后再编译,编译出来的.build里面的代码库才能同步,因为.build的代码库用于创建镜像的代码库。
因为对代码工程不熟,不知道要怎么改,只知道要重新编译镜像,make all一直有问题,一路过关斩将,注释掉一些东西,试了十几遍,最后还是编译失败了。
后面才发现可以直接make docker,这才走入了快车道。通过查看ccenv镜像的Dockerfile,发现制作镜像时,fabric库被打包成goshim.tar.bz2,拷贝到ccenv镜像中
# Copyright Greg Haskins All Rights Reserved
#
# SPDX-License-Identifier: Apache-2.0
#
FROM _BASE_NS_/fabric-baseimage:_BASE_TAG_
COPY payload/chaintool payload/protoc-gen-go /usr/local/bin/
ADD payload/goshim.tar.bz2 $GOPATH/src/
RUN mkdir -p /chaincode/input /chaincode/output
再往上回溯,发现源头在Makefile上
GOSHIM_DEPS = $(shell ./scripts/goListFiles.sh $(PKGNAME)/core/chaincode/shim)
$(BUILD_DIR)/goshim.tar.bz2: $(GOSHIM_DEPS)
@echo "Creating $@"
@tar -jhc -C $(GOPATH)/src $(patsubst $(GOPATH)/src/%,%,$(GOSHIM_DEPS)) > $@
修改第一行,以便包含CID的库
GOSHIM_DEPS = $(shell ./scripts/goListFiles.sh $(PKGNAME)/core/chaincode)
就在我满怀希望重新make docker,就在我闲逛的时候,我发现CID居然就在shim/ext/cid包里面,也就是说原来的ccenv镜像已经包含了CID包,只是路径和以前不同。
修改包的路径,然后在chaincode容器中就能编译成功了,在cli容器中也能正常安装chaincode了。