本文已参与「新人创作礼」活动,一起开启掘金创作之路。
环境配置等问题可以参考上一篇博客 Fabric2.0,使用test-network
以test-network的fabcar的chaincode为例,他需要如下的步骤才能部署到channel中
- 第一步:打包智能合约
- 第二步:安装chaincode包
- 第三步:许可chaincode定义
- 第四步:提交chaincode定义到channel中
在进行如下步骤之前,首先需要关闭网络,然后再重启网络,同时创建channel,即如下两条命令。
./network.sh down
./network.sh up createChannel
之后某步会用到npm,亲测aptget直接安装的版本会失败,所以这里需要先安装我这里当前最新版的npm 6.14.5(==如果node版本已经足够高了忽略这几句话就行==),用如下步骤来安装。
wget https://npm.taobao.org/mirrors/node/v14.4.0/node-v14.4.0-linux-x64.tar.xz
tar -xvf node-v14.4.0-linux-x64.tar.xz
cd node-v14.4.0-linux-x64/
cd bin
sudo ln -s ${PWD}/node /usr/bin/node
sudo ln -s ${PWD}/npm /usr/bin/npm
1.打包智能合约-以Go语言方式
首先需要到包含Fabcar chaincode的Go语言文件的文件夹中
cd fabric-samples/chaincode/fabcar/go
这个例子使用了Go module的方式来安装chaincode的依赖,可以查看文件夹下的go.mod文件来得知依赖的信息。
在fabric.go文件中可以看到整个chaincode的内容,首先是智能合约的定义。
// SmartContract provides functions for managing a car
type SmartContract struct {
contractapi.Contract
}
有了这个定义,之后就可以在函数中利用智能合约来对账本进行操作了。
// CreateCar adds a new car to the world state with given details
func (s *SmartContract) CreateCar(ctx contractapi.TransactionContextInterface, carNumber string, make string, model string, colour string, owner string) error {
car := Car{
Make: make,
Model: model,
Colour: colour,
Owner: owner,
}
carAsBytes, _ := json.Marshal(car)
return ctx.GetStub().PutState(carNumber, carAsBytes)
}
剩下的就是一些chaincode的逻辑了,包括初始化、查询内容等。
为了安装智能合约的依赖,首先需要执行如下命令。
GO111MODULE=on go mod vendor
如果命令执行成功,会在vender文件夹中查看到安装好的依赖。
既然现在依赖已经就绪,接下来就可以创建chaincode包了,首先回到工作目录下,这样就可以将chaincode和网络中的其他部分一起打包了。
cd ../../../test-network
接下来的部分需要用到peer命令,可以通过peer version
命令来查看peer命令是否已经准备就绪。
就绪之后执行如下命令就可以建立chaincode包了。
peer lifecycle chaincode package fabcar.tar.gz --path ../chaincode/fabcar/go/ --lang golang --label fabcar_1
这条命令会在当前目录下创建fabcar.tar.gz
包,--lang参数用于指明chaincode所用语言,--path指明智能合约代码所在位置,--label用于标识chaincode,建议设置时包含名称和版本。
接下来就可以将这个打包好的chaincode安装到peer上了。
2.安装chaincode包
chaincode需要安装到每一个背书交易的peer上,因为这个网络的背书策略是让Org1和Org2都背书,所以他们都需要安装chaincode。
首先安装到Org1的peer上,执行如下命令设置环境变量让peer操作Org1的peer。
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051
然后运行如下命令安装chaincode。
peer lifecycle chaincode install fabcar.tar.gz
==这里如果运行时可能会有权限问题==,可能会提示说没有找到加密信息,这是因为使用sudo ./network.sh up
启动网络的时候,创建的文件夹的所有者是root,所以普通用户没有访问信息,如果用sudo执行该命令会提示找不到peer命令,所以这里只需要执行如下的命令,把工作目录下所有的文件的所有者都设置为自己就行了,比如当前用户是zekdot组下的zekdot,可以用如下命令:
sudo chown -R zekdot.zekdot .
这样再执行peer命令就成功了。
可以看到peer节点生成并返回了包的标识符,这个id会用在下一步的许可chaincode的步骤,这里id值为469a86090d7e3b537d6495abaae326fc5909d45692e4b19d43348a76e5fe4eb0
。
然后在Org2上也执行同样的操作。
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
export CORE_PEER_ADDRESS=localhost:9051
peer lifecycle chaincode install fabcar.tar.gz
peer成功安装chaincode之后会自动对其进行创建。
3.许可chaincode定义
在成功安装chaincode包之后,需要为组织许可chaincode的定义,定义包括chaincode治理相关的重要参数,比如名称、版本以及chaincode背书策略。
channel的成员集合需要许可chaincode,之后才能将其投入使用。这部分由Application/Channel/lifeycleEndorsement
策略负责,这个策略默认情况下需要channel的大部分成员许可chaincode,因为这里我们只有两个成员,2的大多数就是2,所以两个成员都得许可。
许可时,需要用到上一步生成的两个包的标识符,如果之前没有注意这个值,那么可以用如下命令查看当前peer上安装的chaincode。
peer lifecycle chaincode queryinstalled
Package ID就是所需要的包id。
接下来我们将其设置为环境变量,用于之后的许可chaincode操作。
export CC_PACKAGE_ID=fabcar_1:469a86090d7e3b537d6495abaae326fc5909d45692e4b19d43348a76e5fe4eb0
==这里的id按照自己的实际情况修改==
因为在之前的步骤中,我们设置了以Org2来操作peer命令,所以这里我们以Org2的身份对chaincode进行许可,成功之后,许可会通过gossip协议传播到其他peer上去。用如下的命令来完成许可操作。
peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name fabcar --version 1.0 --package-id $CC_PACKAGE_ID --sequence 1 --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
--package-id参数指明包标识符,--sequence参数用于指示需要追踪多少次chaincode被更新的信息,这里由于是刚安装,所以指定为1,当fabcar的chaincode更新时,需要把这个数增加为2。
这里也可以使用--signature-policy
和--channel-config-policy
帮助approveformyorg命令来指明背书策略。由于命令中没有使用这两个参数,所以会采用默认的背书策略,即需要大多数确认。
许可chaincode需要管理员身份,因此需要设置CORE_PEER_MSPCONFIGPATH
变量来指明包含管理员信息的文件夹,许可需要被提交给排序服务,它将会验证管理员签名然后把许可分发给peers。
接下来利用Org1进行许可。
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_ADDRESS=localhost:7051
peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name fabcar --version 1.0 --package-id $CC_PACKAGE_ID --sequence 1 --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
这步完成之后所有的组织都许可了chaincode,接下来就可以将chaincode定义提交到channel中去了,这里如果某个组织在大多数组织许可之前就提交了chaincode,那么这个组织将无法参与交易的背书。
4.提交chaincode定义到channel中
当大多数成员都许可了chaincode,那么就可以让其中一个组织来提交chaincode定义到channel中了,这时提交会成功,而且chaincode的定义将会被应用到channel中。
为了查看有多少成员已经许可了chaincode,可以执行如下命令。
peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name fabcar --version 1.0 --sequence 1 --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --output json
执行后可以看到如下的输出:
可以看到两个成员都许可了chaincode,接下来就可以提交了。
用如下命令进行提交,这里同样需要管理员身份来执行该命令。
peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name fabcar --version 1.0 --sequence 1 --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
这里用peerAddresses参数来指明两个成员,因为需要说明这两个成员已经许可了这个chaincode。
执行成功后可以看到如下输出。
由channel各成员提交给排序服务的chaincode定义背书会被加入区块,然后分发到整个channel中,所有peer都会验证是否有足够多的成员已经许可了chaincode定义,上述命令会等待所有peer返回他们的验证。
可以用如下的命令来确认chaincode定义已经提交到了channel中。
peer lifecycle chaincode querycommitted --channelID mychannel --name fabcar --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
5.调用chaincode
上述工作全部完成之后,chaincode就可以被调用执行业务逻辑喽。这里要注意,调用时需要指明足够多的peer来达到背书协议的要求。
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n fabcar --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"initLedger","Args":[]}'
比如这里执行了初始账本的函数。
执行之后,可以利用如下的命令执行账本查询的函数。
==这里的函数指的都是写在之前那个fabric.go中的函数==
peer chaincode query -C mychannel -n fabcar -c '{"Args":["queryAllCars"]}'
可以看到如下输出,即刚才初始化时添加的车辆。
6.升级智能合约
可以利用chaincode生命周期进程来更新已经部署到channel的chaincode,具体方式就是部署新的chaincode然后再用新的id来进行许可,成功提交之后新的chaincode就可以投入使用了。
升级过程也可以用于修改chaincode的背书策略,只要许可新的策略即可。
接下来假设我们要部署一种由新语言实现的智能合约来代替原来的go语言版本,这里以JavaScript版本的为例。
在test-network
文件夹中执行如下命令。
cd ../chaincode/fabcar/javascript
npm install
cd ../../../test-network
这几条命令用于解决chaincode的依赖问题,然后再回到当前的工作目录test-network
文件夹。
然后用如下的命令来打包chaincode。
export PATH=${PWD}/../bin:$PATH
export FABRIC_CFG_PATH=$PWD/../config/
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
peer lifecycle chaincode package fabcar_2.tar.gz --path ../chaincode/fabcar/javascript/ --lang node --label fabcar_2
然后用如下的命令来作为Org1的管理员安装chaincode。
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051
peer lifecycle chaincode install fabcar_2.tar.gz
安装好之后可以通过如下的命令查看当前在peer节点中安装的所有智能合约。
peer lifecycle chaincode queryinstalled
可以看到,目前安装好了两个chaincode包。
接下来把这个新的id保存到环境变量中,这里按照自己的实际情况设置。
export NEW_CC_PACKAGE_ID=fabcar_2:16073b2035bb41d01ff4aa7f29b243d24aeda14f2cfcedd83bda928a0c61e409
然后用Org1的身份来许可chaincode。
peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name fabcar --version 2.0 --package-id $NEW_CC_PACKAGE_ID --sequence 2 --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
接下来是Org2的操作,首先需要设置环境变量,成为他的管理员。
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
export CORE_PEER_ADDRESS=localhost:9051
然后用如下命令让Org2安装chaincode。
peer lifecycle chaincode install fabcar_2.tar.gz
然后是许可。
peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name fabcar --version 2.0 --package-id $NEW_CC_PACKAGE_ID --sequence 2 --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
如果需要,可以查看第4节使用checkcommitreadiness子命令来查看是否两个成员都许可了chaincode。确定两个都许可之后,用如下命令让Org2提交新的chaincode达成更新的目的。
peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name fabcar --version 2.0 --sequence 2 --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
可以用docker ps
来查看更新后的chaincode。
可以看到前两条都是fabcar_2的信息。
接下来用新的chaincode来创建一辆新车。
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n fabcar --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"createCar","Args":["CAR11","Honda","Accord","Black","Tom"]}'
然后查询车辆列表可以看到这辆新车。
peer chaincode query -C mychannel -n fabcar -c '{"Args":["queryAllCars"]}'
可见新的chaincode也能正常使用。