Fabric2.0 智能合约(链码)实践 - 智能合约安装、定义 及 如何升级
本系列文章如下: HyperLedger Fabric 2.0 测试网络部署 Fabric2.0 样例 first-network 生成配置说明 Fabric2.0启动网络脚本配置剖析 Fabric 2.0 创建通道与加入通道 Fabric 2.0 智能合约部署及升级 Fabric 2.0 实战 - 设置背书策略
从Fabric 2.0开始,对链码的管理是完全分散(去中心化)的:多个组织可以使用Fabric链码生命周期来就链码的参数(例如链码认可策略)达成协议,然后再使用链码与分类帐进行交互。
新的智能合约在生命周期中提供了一些改进:
在Fabric的1.x版本中,一个组织可以为所有其他渠道成员设置链码的参数(例如,认可策略)。新的Fabric链码生命周期更加灵活,因为它既支持集中式信任模型(例如先前生命周期模型的模型),也支持分散模型,这些模型需要足够数量的组织才能在背书策略上生效。
在先前的链码生命周期中,升级事务可能由单个组织发出,这给尚未安装新链码的渠道成员带来了风险。新模型仅在足够数量的组织批准升级后才允许升级链码。
Fabric 生命周期允许您更改认可策略,而无需重新打包或重新安装链码。用户还可以利用新的默认策略,该策略需要频道中大多数成员的认可。在从渠道中添加或删除组织时,该策略会自动更新。
Fabric 生命周期将链码打包在易于阅读的tar文件中。这使得检查链码包和协调跨多个组织的安装变得更加容易。
上一个生命周期使用安装链码包时指定的名称和版本定义了通道上的每个链码。现在,您可以使用单个 chaincode 程序包,并在相同或不同的通道上以不同的名称多次部署它。
智能合约的 lifecycle
实践主要分成以下4个部分
Fabric 2.0 的智能合约需要打包成一个.tar.gz
压缩包。
打开控制台,执行 docker exec -it cli
进入cli容器
由于没接触过新的智能合约命令,我们先输入help
,获取下帮助文档,控制台输入:
> peer lifecycle chaincode --help
peer lifecycle chaincode
命令定义说明:
#智能合约所有操作命令:
Perform chaincode operations: package|install|queryinstalled|getinstalledpackage|approveformyorg|checkcommitreadiness|commit|querycommitted
Usage:
peer lifecycle chaincode [command]
Available Commands:
approveformyorg 同意智能合约定义
checkcommitreadiness 检查合约是否在通道上已经定义
commit 提交合约定义到指定通道
getinstalledpackage 从节点中获取已经安装的链码包
install 部署链码
package 打包链码
querycommitted 查询节点上已提交的链码定义
queryinstalled 查询节点已经安装的链码
由于要打包合约此处我们使用 peer lifecycle chaincode package
, 在容器内部执行命令:
> peer lifecycle chaincode package mycc2.tar.gz --path github.com/hyperledger/fabric-samples/chaincode/abstore/go/ --lang golang --label mycc_2
参数解释:
mycc2.tar.gz
:打包合约包文件名
--path
:智能合约路径
--lang
:智能合约语言 支持golang、node、java
--label
:智能合约标签,描述作用
执行完成后,查看当前目录,出现mycc2.tar.gz
查看打包后的文件内容,主要包含:
(1).chaincode-Package-Metadata.json 文件,主要是定义合约的代码路径、合约语言以及合约标签
{ "path" : "github.com/hyperledger/fabric-samples/chaincode/abstore/go/",
"type":"golang",
"label":"mycc_2"}
(2)压缩的合约代码文件code.tar.gz,包含合约代码以及依赖包
继续在cli执行以下命令:
> peer lifecycle chaincode install mycc2.tar.gz
mycc2.tar.gz: 智能合约压缩包路径
控制台输出:
验证合约安装是否安装到节点
> peer lifecycle chaincode queryinstalled
控制台输出:
在宿主机执行docker ps
发现目前合约容器还未启动。
Fabric 2.0 智能合约由合约定义控制。当通道成员批准合约定义时,该批准作为组织对其接受的合约参数的投票。这些经过批准的组织定义允许通道成员在链码在通道上使用之前对链码达成一致。链码定义包括以下参数,这些参数需要在组织之间保持一致:
名称: 智能合约名称 版本: 与给定的合约包相关联的版本号或值。如果您升级了合约二进制文件,那么您也需要更改合约版本。 序列: 定义链码的次数。此值为整数,用于跟踪合约升级。例如,当您第一次安装并批准一个chaincode定义时,序列号将是1。下次升级合约时,序列号将增加到2。 背书策略: 哪些组织需要执行和验证事务输出。背书策略可以表示为传递给CLI或SDK的字符串,也可以在通道配置中引用策略。默认情况下,背书策略被设置为通道/应用程序/背书,这默认要求通道中的大多数组织对交易进行背书。 集合配置: 指向与您的chaincode关联的私有数据集合定义文件的路径。有关私有数据收集的更多信息,请参见私有数据体系结构参考。 初始化: 所有的链码需要包含一个初始化函数来初始化链码。默认情况下,这个函数永远不会执行。但是,您可以使用chaincode定义来请求Init函数是可调用的。如果请求执行Init,则fabric将确保在调用任何其他函数之前调用Init,并且只调用一次。 ESCC/VSCC插件: 自定义认证或验证插件的名称。
cli容器输入以下命令:
> peer lifecycle chaincode approveformyorg --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID channel2 --name mycc2 --version 1 --init-required --package-id mycc_2:33c137ade1dcc3dd383174a451549a1eda509336cf957dfb472854686d565b9e --sequence 1 --waitForEvent
命令选项说明如下:
--tls
:是否启动tls
--ca
:ca证书路径
--channelID
:智能合约安装通道
--name
:合约名
--version
:合约版本
--package-id queryinstalled
:查询的合约ID
--sequence
:序列号
--waitForEvent
:等待peer提交交易返回
--init-required
:合约是否必须执行init
控制台日志: 能看到通过orderer.example.com产生了一个交易
Fabric 2.0 的智能合约的部署必须满足一定合约定义策略,策略的定义在通道配置中具体定义 控制台输入以下命令:
> peer lifecycle chaincode checkcommitreadiness --channelID channel2 --name mycc2–version 1 --sequence 1 --output json --init-required
输出以下结果,可以得知当前合约获取了Org1MSP的同意,参考2.1.3,而还没得到Org2MSP的定义
查看原本的lifecycle策略定义是满足过半数即可
现在明显不满足,我们还是执行commit合约看一下会怎么样 Error: transaction invalidated with status (ENDORSEMENT_POLICY_FAILURE) :不满足背书策略
这时候我们需要把Org2MSP也同意这个合约定义,切换环境变量为peer0.org2.example.com的配置
重复执行2.1.2 跟2.1.3操作后,再执行策略查询,可以看到两个都true了,满足策略。
在满足合约定义的策略后,可以提交合约 控制台执行以下命令提交合约
> peer lifecycle chaincode commit -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt --version 1 --sequence 1 --init-required
命令选项说明如下:
--tls
:是否启动tls
--ca
:ca证书路径
--channelID
:智能合约安装通道
--name
:合约名
--version
:合约版本
--package-id queryinstalled
:查询的合约ID
--sequence
:序列号
--waitForEvent
:等待peer提交交易返回
--init-required
:合约是否必须执行init
--peerAddresses
:节点路径
--tlsRootCertFiles
:节点ca根证书路径(--peerAddresses --tlsRootCertFiles 连用,可多个节点,多个节点即将合约部署到对应节点集合上)
控制台输出日志:
查看智能合约容器,此时合约容器已经启动
cli容器内输入以下命令
> peer lifecycle chaincode querycommitted --channelID mychannel --name mycc
控制台输出合约具体信息如下:
Fabric 2.0 智能合约操作主要有invoke
跟query
, 此处与1.x完全一致
初始化合约,执行init方法,设置a:100, b:100
> peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt --isInit -c '{"Args":["Init","a","100","b","100"]}'
控制台输出:
查询a的余额:
> peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'
输出值为100。
还是先在控制台输入 docker exec -it cli bash
进入cli的控制台,默认cli的环境变量节点为peer0.org1.example.com
.
目前已部署一个智能合约到peer0.org1.example.com
,cli控制台输入命令:
> peer lifecycle chaincode queryinstalled
查看mycc
合约的package_id如下:
查看mycc
合约在mychannel通道的合约定义,cli控制台输入命令:
> peer lifecycle chaincode querycommitted -C mychannel
可以看到mycc合约具体信息:
参数 | 中文描述 | 值 |
---|---|---|
Name | 合约名称 | mycc |
Version | 合约版本 | 1 |
Sequence | 合约序列号(升级1次,加1) | 1 |
Endorsement Plugin | 背书插件 | escc |
Validation Plugin | 校验插件 | vscc |
执行一次query操作方便后面对比:
> peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'
此时控制台输出a的值为90:
假如本地不存在该合约代码,2.0提供从节点上获取代码包的操作 控制台输入以下命令:
> peer lifecycle chaincode getinstalledpackage --package-id mycc_1:00ef9e95ea103b2c27eacd5a62efd9b34863c672d236a1ce99a7d539b2f9ef7a
packge-id:使用1步骤中查询获取的合约 packge_id
当前目录出现mycc.tar.gz
包为合约包,解压合约包里面的code.tar,编辑合约代码
添加一个方法默认加10:
控制台输入以下命令打包合约:
> peer lifecycle chaincode package mycc.tar.gz --path github.com/hyperledger/fabric-samples/chaincode/abstore/go/ --lang golang --label mycc_1
将根据新的合约代码打包成新的智能合约包mycc.tar.gz
更新合约代码需要重新将合约安装到节点上:
> peer lifecycle chaincode install mycc.tar.gz
控制台输出合约安装信息:
此时我们再查询一下节点安装合约信息,控制台输入:
> peer lifecycle chaincode queryinstalled
控制台输出:
可以看到新增了一个package_id不一样的mycc
合约,原来的还在。
真正升级的操作其实是在合约定义这一步骤,控制台输入以下命令,与原来相比将package_id修改为新安装的合约package_id,sequence要改成2,因为install了2次:
> peer lifecycle chaincode approveformyorg --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name mycc --version 1 --init-required --package-id mycc_1:2f358faa3475e5c37a90be9a7c0db2f608ecb09b13f64b001f83799be9fccc77 --sequence 2 --waitForEvent
检查approve
状态,控制台输入:
> peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name mycc --version 1 --sequence 2 --output json --init-required
sequence要改成2,因为install了2次,否则报错Error: query failed with status: 500 - failed to invoke backing implementation of 'CheckCommitReadiness': requested sequence is 1, but new definition must be sequence 2
环境变量切换peer0.org2.example.com
,重复3.3,3.4,3.5步,知道出现以下结果:
完成智能合约lifecycle策略后,重新提交合约定义:
> peer lifecycle chaincode commit -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID mychannel --name mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt --version 1 --sequence 2 --init-required
注意序列号sequence
控制台输出以下结果:
此时通过新的智能合约查询a的值,还是为90
刚刚添加了addTen这个方法,理论上a应该加10等于100 控制台输入以下命令调用addTen
> peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["addTen","a"]}'
查看结果a果然为100 升级成功
Fabric 2.0 的智能合约合约安装部署过程做了很多的修改,首先是合约打包,合约打包更方便了,打包一次,部署多次,从安全性上,合约定义的同意策略控制极大提高了合约的合规性。并且提供了更安全的链码共识升级方案。 虽然步骤麻烦,但是总体来说 Fabric 2.0 智能合约的改动还是比较认可。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!