Fabric2.0 demo,部署智能合约

577 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

环境配置等问题可以参考上一篇博客 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命令就成功了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n7TPSr48-1593240751495)(./blog-pic/4.png)]

可以看到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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ecddAlnL-1593240751499)(./blog-pic/5.png)]

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

执行后可以看到如下的输出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O1fovD1j-1593240751500)(./blog-pic/7.png)]

可以看到两个成员都许可了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。

执行成功后可以看到如下输出。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zmat5kU8-1593240751502)(./blog-pic/8.png)]

由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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZMmqSWaE-1593240751504)(./blog-pic/9.png)]

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"]}'

可以看到如下输出,即刚才初始化时添加的车辆。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QwOTEkCQ-1593240751506)(./blog-pic/10.png)]

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包。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hZ52fKND-1593240751509)(./blog-pic/11.png)]

接下来把这个新的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。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-93Pvkoei-1593240751510)(./blog-pic/14.png)]

可以看到前两条都是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"]}'

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ijl5Jx97-1593240751512)(blog-pic/15.png)]

可见新的chaincode也能正常使用。