接著上圖分析,經過consensus commit流程生成批資料後,是如何送入到chaincode呢?我們還是以invoke命令分析。
1)在consensus的helper中呼叫chaincode的executetransactions 進入transaction處理流程
[cpp] 程式碼
func (h *helper) exectxs(id interface{}, txs []*pb.transaction) ([]byte, error) {
succeededtxs, res, ccevents, txerrs, err := chaincode.executetransactions(context.background(), chaincode.defaultchain, txs)
}
2)該函式在core/chaincode 中處理,將命令封裝成chaincode識別的格式。其中的chain物件則是訪問chaincode對應的chaincodesupport,這樣就說明訪問chaincode的介面類是chaincodesupportserver。
[cpp] 程式碼
func execute(ctxt context.context, chain *chaincodesupport, t *pb.transaction) ([]byte, *pb.chaincodeevent, error) {
ccmsg, err = createtransactionmessage(t.txid, cmsg)
resp, err := chain.execute(ctxt, chaincode, ccmsg, timeout, t)
}
3)該函式在chaincodesupport檔案中,首先檢測chaincode是否建立成功、能否正常執行。其中chrte.handler的得來是比較複雜的,見下描述
[cpp] 程式碼
func (chaincodesupport *chaincodesupport) execute(ctxt context.context, chaincode string, msg *pb.chaincodemessage, timeout time.duration, tx *pb.transaction) (*pb.chaincodemessage, error) {
chrte, ok := chaincodesupport.chaincodehasbeenlaunched(chaincode)
chrte.handler.sendexecutemessage(msg, tx)
}
3.1)在建立chaincodesupport的時候registerchaincodesupport 呼叫 newchaincodesupport 例項化chaincodesupport(start.go),伺服器的name:
[cpp] 程式碼
protos.chaincodesupport
[cpp] 程式碼
ccstartuptimeout := time.duration(tout) * time.millisecond
ccsrv := chaincode.newchaincodesupport(chainname, peer.getpeerendpoint, userrunscc,
ccstartuptimeout, sechelper)
//now that chaincode is initialized, register all system chaincodes.
system_chaincode.registersysccs()
pb.registerchaincodesupportserver(grpcserver, ccsrv)
[cpp] 程式碼
var _chaincodesupport_servicedesc = grpc.servicedesc{
servicename: "protos.chaincodesupport",
handlertype: (*chaincodesupportserver)(nil),
methods: []grpc.methoddesc{},
streams: []grpc.streamdesc{
{
streamname: "register",
handler: _chaincodesupport_register_handler,
serverstreams: true,
clientstreams: true,
},
},
}
3.2)chaincode 呼叫 err := shim.start(new(simplechaincode)) 接入到chaincodesupportserver
[cpp] 程式碼
err := shim.start(new(simplechaincode))
3.3)連線chaincodesupprotserver同時呼叫register函式
[cpp] 程式碼
func start(cc chaincode) error {
chaincodesupportclient := pb.newchaincodesupportclient(clientconn)
stream, err := chaincodesupportclient.register(context.background())
err = chatwithpeer(chaincodename, stream, cc)
}
3.4)與此同時chaincodesupportserver會根據client呼叫註冊函式建立該stream的handler處理控制代碼,建立訊息響應迴圈,等待client傳送命令.(注意該handler就是我們關心的
handler.sendexecutemessage)
[cpp] 程式碼
func handlechaincodestream(chaincodesupport *chaincodesupport, ctxt context.context, stream ccintf.chaincodestream) error {</span>
[cpp] 程式碼
deadline, ok := ctxt.deadline()
chaincodelogger.debugf("current context deadline = %s, ok = %v", deadline, ok)
handler := newchaincodesupporthandler(chaincodesupport, stream)
return handler.processstream()
}
3.5) 客戶端shim/chaincode傳送registermessage
[cpp] 程式碼
handler.serialsend(&pb.chaincodemessage{type: pb.chaincodemessage_register, payload: payload})
3.6)對於server而言,我們剛剛建立了handler又有processstream訊息響應迴圈,這樣registermessage就交到了processstream手裡,processstream根據訊息型別執行命令分發
呼叫beforeregisterevent函式。
[cpp] 程式碼
func (handler *handler) beforeregisterevent(e *fsm.event, state string) {
err = handler.chaincodesupport.registerhandler(handler)
}
3.7)同理在client端(shim/chaincode)也建立響應的訊息響應迴圈。
4)到目前為止還沒有完,我們將invoke命令送給了client的委託模組shim進行處理。shim模組根據來訪事件型別送入指定處理函式
[cpp] 程式碼
func (handler *handler) entertransactionstate(e *fsm.event) {
msg, ok := e.args[0].(*pb.chaincodemessage)
if !ok {
e.cancel(fmt.errorf("received unexpected message type"))
return
}
chaincodelogger.debugf("[%s]received %s, invoking transaction on chaincode(src:%s, dst:%s)", shorttxid(msg.txid), msg.type.string(), e.src, e.dst)
if msg.type.string() == pb.chaincodemessage_transaction.string() {
// call the chaincode's run function to invoke transaction
handler.handletransaction(msg)
}
}
5)呼叫chaincode的invoke函式
[cpp] 程式碼
func (handler *handler) handletransaction(msg *pb.chaincodemessage) {
res, err := handler.cc.invoke(stub, function, params)
}
以上分析涉及兩個過程 1) consensus結束後如何將命令送入chaincode 2)chaincodesupportserver與chaincode如何建立通訊關係。
畫一個 chaincodesupportserver與chaincode如何建立通訊關係 圖: