Fabric 1.4源码分析 - chaincode install(1)peer端的install流程

Building Your First Network例子中,命令为

1
peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/

客户端调用peer chaincode install命令安装chaincode,代码在‘peer/chaincode/install.go’这个文件下,命令关联的方法是chaincodeInstall[install的流程与前面介绍的instantiate大体相似,重复不再展开,只勾勒差别处]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// chaincodeInstall installs the chaincode. If remoteinstall, does it via a lscc call
func chaincodeInstall(cmd *cobra.Command, ccpackfile string, cf *ChaincodeCmdFactory) error {
if cf == nil {
cf, err = InitCmdFactory(cmd.Name(), true, false)
}

var ccpackmsg proto.Message
// ccpackfile = args[0]
if ccpackfile == "" {
// 检查提供的flag参数, 例如chaincodePath(-p), chaincodeVersion(-v), chaincodeName(-n)

//generate a raw ChaincodeDeploymentSpec
ccpackmsg, err = genChaincodeDeploymentSpec(cmd, chaincodeName, chaincodeVersion)
} else {
//read in a package generated by the "package" sub-command (and perhaps signed by multiple owners with the "signpackage" sub-command)
ccpackmsg, cds, err = getPackageFromFile(ccpackfile)

//get the chaincode details from cds and validate ChaincodeId.Name, ChaincodeId.Version if user provided
}

err = install(ccpackmsg, cf)

return err
}

//install the depspec to "peer.address"
func install(msg proto.Message, cf *ChaincodeCmdFactory) error {
creator, err := cf.Signer.Serialize()
prop, _, err := utils.CreateInstallProposalFromCDS(msg, creator)
signedProp, err = utils.GetSignedProposal(prop, cf.Signer)

// install is currently only supported for one peer
proposalResponse, err := cf.EndorserClients[0].ProcessProposal(context.Background(), signedProp)

return nil
}
  1. install与instantiate方法类似,首先InitCmdFactory初始化命令工厂辅助,参见instantiate(1).
  2. 获取chaincode的文件描述。这里分为两种方式,区别在于是否先使用peer chaincode package命令,得到的package作为chaincode istall参数,通常还经过多个参与者签名。具体可以参看chaincode lifecycle。package命令以后再详述。Tutorial里的例子没有先经过package,参数ccpackfile为空,则进入genChaincodeDeploymentSpec第一种方式产生cds。

    genChaincodeDeploymentSpec过程与instantiate大致一致,可以参考instantiate(2),这里不同的是peer/chaincode/common.go:getChaincodeDeploymentSpec里会进入到if条件里,执行到codePackageBytes, err = container.GetChaincodePackageBytes(spec)。这个方法会区分不同的语言平台(这个语言选择是命令行里-l指定的)将代码gzip压缩,tar打包(以golang为例,为了精简包大小,去除golang标准库,fabric框架包等,加入第三方依赖包,META-INF信息等),返回[]byte。后面的流程与instantiate类似,可参考不再详述,可以认真对比下区别在于cis的Input和cds。

产生ccpackmsg(pb.ChaincodeDeploymentSpec结构)执行install。正如源码里的注释和前面instantiate(1)介绍的InitCmdFactory,install方法只能作用于唯一一个endorser(”peer.address”)。注意,这里与instantiate不同,在instantiate里还有第三步cf.BroadcastClient.Send(env).这里说明,install命令不需要将message发往orderer去排序和产生区块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// peer.ChaincodeInvokeSpec(cis)
{
"ChaincodeInvokeSpec" : {
"ChaincodeSpec" : { // pb.ChaincodeSpec
"Type" : "ChaincodeSpec_GOLANG",
"ChaincodeId" : {
"Name" : "lscc"
},
"Input" : {
"Args" : ["install", "${chaincodDeploySpec}"]
}
}
}
}

// pb.chaincodDeploySpec(cds)
{
"ChaincodeSpec": {
"Type" : "ChaincodeSpec_GOLANG",
"ChaincodeId" : {
"Path" : "${chaincodePath}",
"Name" : "${chaincodeName}",
"Version" : "${chaincodeVersion}"
},
"Input" : "nil"
},
"CodePackage": "#{container.GetChaincodePackageBytes(spec)}"
}

至此,peer端的install完成,该节点仅仅是作为执行install命令,需要本地拥有install的代码包,install并不是安装到该peer节点而是endorser节点。