回顾到endorser.go#SimulateProposal,执行完callChaincode后,回顾前面,在这里有两次调用chaincode,分别是lscc的Inovke和acc的Init,callChaincode这里返回的是第一次调用的lscc的response。调用acc Init的结果只判断调用是否正常完成返回pb.ChaincodeMessage_COMPLETED。这是因为在handleInit里对初始化失败,即pb.Response.status不等于200的统一返回pb.ChaincodeMessage_ERROR,这点与Invoke的处理方式是不一样的。Invoke允许pb.ChaincodeMessage.Type=pb.ChaincodeMessage_COMPLETED,但是内层的pb.Response.status不等于200. 若返回pb.ChaincodeMessage_ERROR,则向上层返回error,最终返回&pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}到peer chainncode instantiate...这个命令的执行节点peer。
紧接着调用txsim.GetTxSimulationResults()获取上节提到的lockbased_tx_simulator的读写集合。先看下这个txsim是如何传递的。首先初始化后在callChaincode加入contextctxt = context.WithValue(ctxt, chaincode.TXSimulatorKey, txsim)。之后在chaincode/handler里构建TransactionContext实体对象时将txsim从context取出,包裹进这个新建的TransactionContext对象内。这个对象前面有提及,是关联于chainID, txID这两个存放于chaincode/handler.TXContexts.map[string]*TransactionContext里。后面在chaincode/handler.go#handlerTranction里调用txContext, err = h.isValidTxSim(msg.ChannelId, msg.Txid)}也是以此取出该txsim,将读写结果放进读写集readwriteSet。
txsim.GetTxSimulationResults()这里在返回前,先对读写集合按照key排序。这里包含pub和pri两部分数据,先关注pub部分。rwset这部分以后展开专题介绍。然后调用txsim.Done(),签名初始化txsim时候提到获取了ledger的读锁,因此这里需要释放h.txmgr.commitRWLock.RUnlock()。返回共有数据的读写集到endorser#ProcessProposal。
第二步,背书模拟结果。主要是signer对payload进行签名,并且附上自身签名的identityBytes构造成response。然后这个resp就返回方法,通过grpc发送回调用方peer.
1 | // endorse the proposal by calling the ESCC |
最后,返回的结构体为
pb.ProposalResponse1 | { |