推荐阅读Fabric v1.x 智能合约的生命周期,了解Fabric v1.x的chaincode生命周期模型。
推荐阅读Hyperledger Fabric v2.0发行版的新增功能介绍,了解Fabric v2.0的新增功能。
Fabric v2.0引入了智能合约的去中心化治理,升级到了新的chaincode生命周期管理模式。下面将进行实际的生命周期管理操作演练。
Fabric生命周期将chaincode打包在易于阅读的tar文件中,方便协调跨多个组织的安装。
执行docker exec -it cli
命令进入cli容器,使用下面命令打包chaincode:
peer lifecycle chaincode package mycc2.tar.gz --path github.com/hyperledger/fabric-samples/chaincode/abstore/go/ --lang golang --label mycc_1
打包后文件mycc1.tar.gz解压后,可以看到里面包含Metadata.json文件和code.tar.gz文件,Metadata.json文件的内容就是我们刚刚打包时指定的参数,为合约的代码路径path、语言lang以及标签label,code.tar.gz中则是合约源代码以及依赖包。
在cli中执行下面命令安装chaincode,mycc1.tar.gz为智能合约合约压缩包路径:
peer lifecycle chaincode install mycc1.tar.gz
查询已安装的chaincode:
peer lifecycle chaincode queryinstalled
Fabric v1.x的生命周期使用安装chaincode包时指定的名称和版本定义channel上的每个chaincode。现在,您可以使用单个chaincode程序包,并在channel上以不同的名称多次部署它。
channel成员需要认可合约定义,该认可将作为组织接受合约参数定义的投票。对合约定义的认可,保证了在使用chaincode之前,channel成员达成了对chaincode定义的共识。chaincode定义包括以下参数,这些参数需要在组织之间保持一致:
在cli中使用下面命令认可智能合约定义:
peer lifecycle chaincode approveformyorg --tls true --cafile $ORDERER_CA --channelID mychannel --name mycc --version 1 --init-required --package-id mycc_1:33c137ade1dcc3dd383174a451549a1eda509336cf957dfb472854686d565b9e --sequence 1 --waitForEvent
合约的生命周期背书策略在channel配置中定义,见下面配置中的LifecycleEndorsement,需要大多数组织同意:
Application: &ApplicationDefaults
# Organizations is the list of orgs which are defined as participants on
# the application side of the network
Organizations:
# Policies defines the set of policies at this level of the config tree
# For Application policies, their canonical path is
# /Channel/Application/
Policies:
Readers:
Type: ImplicitMeta
Rule: "ANY Readers"
Writers:
Type: ImplicitMeta
Rule: "ANY Writers"
Admins:
Type: ImplicitMeta
Rule: "MAJORITY Admins"
LifecycleEndorsement:
Type: ImplicitMeta
Rule: "MAJORITY Endorsement"
Endorsement:
Type: ImplicitMeta
Rule: "MAJORITY Endorsement"
Capabilities:
<<: *ApplicationCapabilities
在cli中使用下面命令查询是否满足提交智能合约生命周期背书策略:
peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name mycc --version 1 --sequence 1 --output json --init-required
可以查看到channel中各个组织是否已同意该合约定义,如果已同意会显示true,否则为false。在不满足合约定义策略的情况下提交合约定义,将会报错Error: transaction invalidated with status (ENDORSEMENT_POLICY_FAILURE)
,表示不满足合约定义策略。
我们需要切换环境变量到其他组织(切换方法见文章末尾),重复安装、认可的步骤,使过半数的组织同意该合约定义。
在满足合约定义的策略后,可以在cli中进行提交合约的操作了:
peer lifecycle chaincode commit -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA --channelID mychannel --name mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $PEER0_ORG1_CA --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles $PEER0_ORG2_CA --version 1 --sequence 1 --init-required
我们使用了两对peerAddresses和tlsRootCertFiles,将向peer0.org1.example.com和peer0.org2.example.com两个节点发送提交交易,操作完成后这两个节点都会提交该智能合约定义,使用docker ps
可查看到合约容器都启动成功了。
cli中使用下面命令可查看已提交的智能合约:
peer lifecycle chaincode querycommitted --channelID mychannel --name mycc
调用chaincode的Init方法,设置初始值:
peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -C mychannel -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $PEER0_ORG1_CA --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles $PEER0_ORG2_CA --isInit -c '{"Args":["Init","a","100","b","100"]}' --waitForEvent
查询a的余额:
peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'
a转账给b:
peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -C mychannel -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $PEER0_ORG1_CA --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles $PEER0_ORG2_CA -c '{"Args":["invoke","a","b","10"]}' --waitForEvent
再次查询a的余额,变成了90。
在先前的chaincode生命周期中,升级事务可能由单个组织发出,这给尚未安装新链码的channel成员带来了风险。新模型仅在足够数量的组织认可升级后才允许升级chaincode。
从peer节点上获取chaincode压缩包:
peer lifecycle chaincode getinstalledpackage --package-id mycc_1:33c137ade1dcc3dd383174a451549a1eda509336cf957dfb472854686d565b9e
packge_id为peer lifecycle chaincode querycommitted查询到的packge_id。
修改完代码后打包智能合约:
peer lifecycle chaincode package mycc_2.tar.gz --path github.com/hyperledger/fabric-samples/chaincode/myabstore/go/ --lang golang --label mycc_2
然后在不同节点(切换方法见文章末尾)执行安装合约包、认可合约定义的操作。
安装合约包:
peer lifecycle chaincode install mycc_2.tar.gz
查询已安装的合约包,可以看到新安装的合约包package_id信息:
peer lifecycle chaincode queryinstalled
认可新的合约定义,package_id改为新安装的合约包package_id,因为第二次定义,sequence改为2:
peer lifecycle chaincode approveformyorg --tls true --cafile $ORDERER_CA --channelID mychannel --name mycc --version 1 --init-required --sequence 2 --package-id mycc_2:a455444dd2f0cd9b07117260422a2ac0f3e25a28fcc329c7e054545ea0271e1a --waitForEvent
查看是否满足生命周期背书策略:
peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name mycc --version 1 --init-required --sequence 2 --output json
提交新的智能合约定义:
peer lifecycle chaincode commit -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA --channelID mychannel --name mycc --version 1 --sequence 2 --init-required --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $PEER0_ORG1_CA --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles $PEER0_ORG2_CA
提交完成后,智能合约的升级也就成功了。
关于背书策略的详细介绍,推荐阅读Fabric v1.x 背书策略。
Fabric v2.0 生命周期允许您更改认可策略,而无需重新打包或重新安装链码。用户还可以利用新的默认策略,该策略需要频道中大多数成员的认可。在从channel中添加或删除组织时,该策略会自动更新。
默认情况下,合约的背书策略需要过半数的channel成员,见下面配置中的Endorsement:
Application: &ApplicationDefaults
# Organizations is the list of orgs which are defined as participants on
# the application side of the network
Organizations:
# Policies defines the set of policies at this level of the config tree
# For Application policies, their canonical path is
# /Channel/Application/
Policies:
Readers:
Type: ImplicitMeta
Rule: "ANY Readers"
Writers:
Type: ImplicitMeta
Rule: "ANY Writers"
Admins:
Type: ImplicitMeta
Rule: "MAJORITY Admins"
LifecycleEndorsement:
Type: ImplicitMeta
Rule: "MAJORITY Endorsement"
Endorsement:
Type: ImplicitMeta
Rule: "MAJORITY Endorsement"
Capabilities:
<<: *ApplicationCapabilities
在cli中设置合约级别的背书策略为OR('Org1.member', 'Org2.member')
:
peer lifecycle chaincode approveformyorg --signature-policy "OR('Org1MSP.member','Org2MSP.member')" --tls true --cafile $ORDERER_CA --channelID mychannel --name mycc --version 1 --init-required --package-id mycc_2:0d395e922a99405e12fdd51b3e1c392808cb1c4031a684e9a61354f095ba40aa --sequence 3 --waitForEvent
切换不同节点(切换方法见文章末尾)执行认可新背书策略的合约定义。
查询是否已满足生命周期背书策略:
peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name mycc --version 1 --sequence 3 --output json --init-required
提交新的合约定义:
peer lifecycle chaincode commit -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA --channelID mychannel --name mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $PEER0_ORG1_CA --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles $PEER0_ORG2_CA --version 1 --sequence 3 --init-required --signature-policy "OR('Org1MSP.member', 'Org2MSP.member')"
提交完成后,合约级别的背书策略也就更新成功了。
有了key级别的背书策略,不需要升级chaincoe,只需要对某一些特定的key做背书策略配置。
shim包提供了获取和设置背书策略的API:
GetStateValidationParameter(key string) ([]byte, error)
SetStateValidationParameter(key string, ep []byte) error
statebased包提供了接口KeyEndorsementPolicy,用于表示背书策略:
type KeyEndorsementPolicy interface {
Policy() ([]byte, error)
AddOrgs(roleType RoleType, organizations ...string) error
DelOrgs(organizations ...string)
ListOrgs() []string
}
调用statebased包的NewStateEP函数可获得KeyEndorsementPolicy接口类型的值,调用AddOrgs方法增加组织,调用DelOrgs方法删除组织,调用Policy方法获得策略的字节数组。由于只能增加或删除,支持“AND”类型的背书策略。
下面是chaincode中修改背书策略的实现:
func (t *ABstore) changeEP(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) != 3 {
return shim.Error("Incorrect number of arguments.")
}
key := args[0]
role := args[1]
op := args[2]
newEP, err := statebased.NewStateEP(nil)
if err != nil {
return shim.Error(err.Error())
}
if op == "del" {
newEP.DelOrgs(role)
} else if op == "add" {
err = newEP.AddOrgs(statebased.RoleTypeMember, role)
}
if err != nil {
return shim.Error(err.Error())
}
policyByte, err := newEP.Policy()
if err != nil {
return shim.Error(err.Error())
}
err = stub.SetStateValidationParameter(key, policyByte)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(nil)
}
升级合约的操作参照本文第7章节。
修改背书策略为AND("Org1MSP.member", "Org2MSP.member")
peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -C mychannel -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $PEER0_ORG1_CA --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles $PEER0_ORG2_CA -c '{"Args":["changeEP","a","Org1MSP","add"]}'
peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -C mychannel -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $PEER0_ORG1_CA --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles $PEER0_ORG2_CA -c '{"Args":["changeEP","a","Org2MSP","add"]}'
只向peer0.org1.example.com请求背书,交易将会验证失败:
peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -C mychannel -n mycc2 --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $PEER0_ORG1_CA -c '{"Args":["invoke","a","b","10"]}' --waitForEvent
再次修改背书策略,删除Org2MSP:
peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -C mychannel -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $PEER0_ORG1_CA --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles $PEER0_ORG2_CA -c '{"Args":["changeEP","a","Org2MSP","del"]}'
再次只向peer0.org1.example.com请求背书,将会交易满足背书策略,执行成功:
peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -C mychannel -n mycc2 --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $PEER0_ORG1_CA -c '{"Args":["invoke","a","b","10"]}' --waitForEvent
本文命令中使用的环境变量配置:
ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
PEER0_ORG1_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
PEER0_ORG2_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
cli中设置连接不同的节点,需要设置相应的环境变量:
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected]/msp
export CORE_PEER_ADDRESS=peer0.org1.example.com:7051
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/[email protected]/msp
export CORE_PEER_ADDRESS=peer0.org2.example.com:9051
感谢富强的博客:
https://blog.csdn.net/qq_28540443/article/details/104379132
参考资料:
github.com/hyperledger/fabric-samples/first-network/scripts/script.sh
github.com/hyperledger/fabric-samples/first-network/scripts/utils.sh