接下来具体执行env, err := instantiate(cmd, cf)
,这里可以看到入参是cobra.Command
和刚刚初始化的ChaincodeCmdFactory
,结果是提交给Orderer的Envelope messageprotos/common.Envelope
.
1 | //instantiate the command via Endorser |
getChaincodeSpec
和getChaincodeDeploymentSpec
从提交的command里获取相应的参数,检查以及设置参数(例如是否指定escc(endorsement system chaincode), vscc(verification system chaincode), ConstructorJSON 构造参数),进而构造chaincode描述结构体pb.ChaincodeSpec
和部署结构体pb.ChaincodeDeploymentSpec
。pb.ChaincodeSpec
(cs)里指定了ChaincodeSpec_Type
语言类型,默认为go,ChaincodeID
,chaincode的标识(包括路径Path,名称Name,版本Version),以及Input(即ConstructorJSON 构造参数,初始化的值,命令行里-c参数值,例子里的’{“Args”:[“init”,”a”, “100”, “b”,”200”]}’)。pb.ChaincodeDeploymentSpec
(cds)里包裹ChaincodeSpec
,以及CodePackage
,打包后用于安装部署的代码,ExecEnv
,指定运行于同一系统中或者docker容器。getChaincodeDeploymentSpec
入参区分是否当前是dev开发模式,而且是否需要构造CodePackage。构造打包是在install安装时进行的,后面分析install时详述。
CreateDeployProposalFromCDS
从上面构造的部署结构体得到peer.ChaincodeInvocationSpec
(cis),进而得到peer.Proposal
,然后使用初始化时候得到的signer对这个提案进行签名,这个步骤与初始化时候非常相似。Transaction的结构可以参看总结Hyperledger Fabric V1.0: Block Structure
pb.ChaincodeInvokeSpec
(cis)里指定Type
为golang,ChaincodeID
指定为lscc(lifecycle system chaincode),然后将上面构造的cds序列化在[]byte作为Input
字段。CreateDeployProposalFromCDS
这个方法从部署结构体cds出发,生成随机字符串NONCE
,使用签名生成ProposalTxID
; 指定类型Type
为HeaderType_ENDORSER_TRANSACTION
(此处多补充一句,初始化时获取配置的类型为HeaderType_CONFIG
),指定channelId
,chaincodeID
(包括chaincode的version,path,name)。这里的chaincodeID
是构造的peer.ChaincodeInvocationSpec
的参数,指定了Name为lscc,lifecycle systme chaincode。此外,还有初始化过程MSP获取的creator
(即序列化后的signer)表明身份以及创建时间Timestamp
等。这些构成了transaction的header,连同序列化后的ChaincodeInvocationSpec
对象作为payload,共同组成了peer.Proposal{Header: hdrBytes, Payload: ccPropPayloadBytes}
.最后使用signer进行签名。rpc调用。初始化时候构建了endorser client组(实际上instantiate只允许有一个)。使用该client发送请求
grpc.Invoke(ctx, "/protos.Endorser/ProcessProposal", in, out, c.cc, opts...)
,这里ctx传入了golang的context.Background()
,后面的rpc调用派生自该顶层context。服务端的处理后面再详述。注意这里是同步调用。endorser rpc的数据结构SignedProposal
如下
// peer.SignedProposal:
{
"ProposalBytes": "${propBytes}",
"Signature": "${signature}" // signature, err := signer.Sign(propBytes)
}
// peer.Proposal :
{
"Header": "${hdrBytes}",
"Payload": "${ccPropPayloadBytes}"
}
// common.Header(hdrBytes) :
{
"ChannelHeader" : { // common.ChannelHeader
"Type" : "HeaderType_ENDORSER_TRANSACTION", // common.HeaderType
"TxId" : "#ComputeProposalTxID(nonce, creator)",
"Timestamp": "timestamp", // util.CreateUtcTimestamp()
"ChannelId": "${chainID}",
"Extension": "${ccHdrExtBytes}",
"Epoch": "epoch" // always 0
},
"SignatureHeader" : { // common.SignatureHeader
"Nonce": "nonce", // nonce, err := crypto.GetRandomNonce()
"Creator": "creator" // creator, err := cf.Signer.Serialize()
}
}
// peer.ChaincodeProposalPayload(ccPropPayload)
{
"Input": "cisBytes",
"TransientMap": nil
}
// peer.ChaincodeHeaderExtension(ccHdrExtBytes) :
{
"ChaincodeId" : "${cis.ChaincodeSpec.ChaincodeId}"
}
// peer.ChaincodeInvokeSpec(cis)
{
"ChaincodeInvokeSpec" : {
"ChaincodeSpec" : { // pb.ChaincodeSpec
"Type" : "ChaincodeSpec_GOLANG",
"ChaincodeId" : {
"Name" : "lscc"
},
"Input" : {
"Args" : ["deploy", "${channelId}", "${chaincodDeploySpec}"]
}
}
}
}
// pb.chaincodDeploySpec(cds)
{
"ChaincodeSpec": {
"Type" : "ChaincodeSpec_GOLANG",
"ChaincodeId" : {
"Path" : "${chaincodePath}",
"Name" : "${chaincodeName}",
"Version" : "${chaincodeVersion}"
},
"Input" : {
"Args" : ["init","a", "100", "b","200"]
}
},
"CodePackage": nil
}
- 收集endorser的response,然后构建签名后的transaction。下面的方法是普适的。
// CreateSignedTx assembles an Envelope message from proposal, endorsements, and a signer.
// This function should be called by a client when it has collected enough endorsements
// for a proposal to create a transaction and submit it to peers for ordering
func CreateSignedTx(proposal *peer.Proposal, signer msp.SigningIdentity, resps ...*peer.ProposalResponse) (*common.Envelope, error) {
// the original header
hdr, err := GetHeader(proposal.Header)
// the original payload
pPayl, err := GetChaincodeProposalPayload(proposal.Payload)
shdr, err := GetSignatureHeader(hdr.SignatureHeader)
// get header extensions so we have the visibility field
hdrExt, err := GetChaincodeHeaderExtension(hdr)
// ensure that all actions are bitwise equal and that they are successful
// 比较所有的response返回的结果是一致的
var a1 []byte
for n, r := range resps {
if n == 0 {
a1 = r.Payload
if r.Response.Status != 200 {
return nil, fmt.Errorf("Proposal response was not successful, error code %d, msg %s", r.Response.Status, r.Response.Message)
}
continue
}
if bytes.Compare(a1, r.Payload) != 0 {
return nil, fmt.Errorf("ProposalResponsePayloads do not match")
}
}
// fill endorsements
endorsements := make([]*peer.Endorsement, len(resps))
for n, r := range resps {
endorsements[n] = r.Endorsement
}
// 构造各级数据结构并序列化,包含原始的proposal的payload,header等信息,
// 以及repsponse的payload,各个resp相应的endorsement,
// 构造了新的peer.Transaction,然后使用signer进行签名。
// 最后将序列化后的transaction和签名组装成common.Envelope并返回。
}
最后,初始化时候得到的cf.BroadcastClient.Send(env)
.至此,完成了chaincode instantiate在peer端的所有操作。