Fabric1.0的架構(gòu)中引入了
client->endorser->order->commiter的概念??蛻舳耸紫葘⒔灰装l(fā)送到背書節(jié)點進行模擬執(zhí)行,然后收集一定數(shù)量的背書結(jié)果構(gòu)造交易發(fā)送到排序節(jié)點進行排序,最后排序節(jié)點對收到的交易進行排序并打包發(fā)送給commiter節(jié)點進行驗證和計入賬本。本文將針對交易執(zhí)行的第一個階段client -> endorser的相關(guān)流程結(jié)合源碼進行分析。
一、Proposal交互流程
1.客戶端向endorser 發(fā)送proposal, proposal 定義如下:
message SignedProposal {
// Proposal 消息序列化bytes
bytes proposal_bytes = 1;
//針對proposal_bytes的簽名
bytes signature = 2;
}
//Proposal消息的實際定義
message Proposal {
// The header of the proposal. It is the bytes of the Header
bytes header = 1;
// 具體的Proposal消息體的序列化內(nèi)容,消息體類型由header類型確定
bytes payload = 2;
// 擴展字段對于 CHAINCODE類型, 其可能使
bytes extension = 3;
}
-
endorser將proposal執(zhí)行結(jié)果返回給客戶端,propose response的定義如下:
message ProposalResponse {
// Version indicates message protocol version
int32 version = 1;
google.protobuf.Timestamp timestamp = 2;
//表示執(zhí)行是否成功
Response response = 4;
// 返回結(jié)果ProposalResponsePayload的序列化bytes
bytes payload = 5;
// The endorsement of the proposal, basically
// the endorser's signature over the payload
Endorsement endorsement = 6;
}
message Response {
// A status code that should follow the HTTP status codes.
int32 status = 1;
// A message associated with the response code.
string message = 2;
// A payload that can be used to include metadata with this response.
bytes payload = 3;
}
message ProposalResponsePayload {
bytes proposal_hash = 1;
bytes extension = 2;
}
message Endorsement {
bytes endorser = 1; // endorser id
bytes signature = 2;// endorser 的簽名
}
- 客戶端收集背書組裝成一個交易transaction
一個完整的transaction包含一個或者多個proposal以及其對應的返回response。交易將會被發(fā)送到共識節(jié)點orders, 經(jīng)過排序之后batch形式的交易會被廣播到peer節(jié)點進行驗證以及寫入賬本。
二、Endorser節(jié)點的處理流程
源文件fabric/core/endorser.go中ProcessProposal函數(shù)負責具體的Proposel的處理。
- 檢查message是否有效,消息體個個字段的完整性以及chaincode id以及調(diào)用類型的檢查,例如proposal的消息不能調(diào)用system chaincode;
- 檢查proposal是否滿足channel的policy, tx的重復性判斷;
- 模擬執(zhí)行proposal中的chaincode調(diào)用,其實也就是實際執(zhí)行,這里需要兩個關(guān)鍵組件:
var txsim ledger.TxSimulator
var historyQueryExecutor ledger.HistoryQueryExecutor
...
//1 -- simulate
cd, res, simulationResult, ccevent, err := e.simulateProposal(ctx, chainID, txid, signedProp, prop, hdrExt.ChaincodeId, txsim)
4.生成endorsement結(jié)果返回
pResp, err = e.endorseProposal(ctx, chainID, txid, signedProp, prop, res, simulationResult, ccevent, hdrExt.PayloadVisibility, hdrExt.ChaincodeId, txsim, cd)
2.1 simulate proposal 詳細流程
以上便是ProcessProposal的整體流程,這里關(guān)鍵的是simulateProposal這一步,接下來對該函數(shù)進行展開分析。
解析chaincode調(diào)用參數(shù):
cis, err := putils.GetChaincodeInvocationSpec(prop)為chancode檢查ESCC和VSCC, 該函數(shù)暫時未實現(xiàn)
if err = e.checkEsccAndVscc(prop); err != nil {
return nil, nil, nil, nil, err
}
- 獲取chaincode的相關(guān)數(shù)據(jù)ChancodeData
cdLedger, err = e.getCDSFromLSCC(ctx, chainID, txid, signedProp, prop, cid.Name, txsim)
- 執(zhí)行chaincode并獲取結(jié)果
var simResult *ledger.TxSimulationResults
var pubSimResBytes []byte
var res *pb.Response
var ccevent *pb.ChaincodeEvent
res, ccevent, err = e.callChaincode(ctx, chainID, version, txid, signedProp, prop, cis, cid, txsim)
if err != nil {
endorserLogger.Errorf("failed to invoke chaincode %s on transaction %s, error: %s", cid, txid, err)
return nil, nil, nil, nil, err
}
if txsim != nil {
if simResult, err = txsim.GetTxSimulationResults(); err != nil {
return nil, nil, nil, nil, err
}
if pubSimResBytes, err = simResult.GetPubSimulationBytes(); err != nil {
return nil, nil, nil, nil, err
}
}
return cdLedger, res, pubSimResBytes, ccevent, nil