Fabric1.0的架構(gòu)中引入了
client->endorser->order->commiter的概念??蛻舳耸紫葘⒔灰装l(fā)送到背書節(jié)點(diǎn)進(jìn)行模擬執(zhí)行,然后收集一定數(shù)量的背書結(jié)果構(gòu)造交易發(fā)送到排序節(jié)點(diǎn)進(jìn)行排序,最后排序節(jié)點(diǎn)對(duì)收到的交易進(jìn)行排序并打包發(fā)送給commiter節(jié)點(diǎn)進(jìn)行驗(yàn)證和計(jì)入賬本。本文將針對(duì)交易執(zhí)行的第一個(gè)階段client -> endorser的相關(guān)流程結(jié)合源碼進(jìn)行分析。
一、Proposal交互流程
1.客戶端向endorser 發(fā)送proposal, proposal 定義如下:
message SignedProposal {
// Proposal 消息序列化bytes
bytes proposal_bytes = 1;
//針對(duì)proposal_bytes的簽名
bytes signature = 2;
}
//Proposal消息的實(shí)際定義
message Proposal {
// The header of the proposal. It is the bytes of the Header
bytes header = 1;
// 具體的Proposal消息體的序列化內(nèi)容,消息體類型由header類型確定
bytes payload = 2;
// 擴(kuò)展字段對(duì)于 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 的簽名
}
- 客戶端收集背書組裝成一個(gè)交易transaction
一個(gè)完整的transaction包含一個(gè)或者多個(gè)proposal以及其對(duì)應(yīng)的返回response。交易將會(huì)被發(fā)送到共識(shí)節(jié)點(diǎn)orders, 經(jīng)過排序之后batch形式的交易會(huì)被廣播到peer節(jié)點(diǎn)進(jìn)行驗(yàn)證以及寫入賬本。
二、Endorser節(jié)點(diǎn)的處理流程
源文件fabric/core/endorser.go中ProcessProposal函數(shù)負(fù)責(zé)具體的Proposel的處理。
- 檢查message是否有效,消息體個(gè)個(gè)字段的完整性以及chaincode id以及調(diào)用類型的檢查,例如proposal的消息不能調(diào)用system chaincode;
- 檢查proposal是否滿足channel的policy, tx的重復(fù)性判斷;
- 模擬執(zhí)行proposal中的chaincode調(diào)用,其實(shí)也就是實(shí)際執(zhí)行,這里需要兩個(gè)關(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 詳細(xì)流程
以上便是ProcessProposal的整體流程,這里關(guān)鍵的是simulateProposal這一步,接下來對(duì)該函數(shù)進(jìn)行展開分析。
解析chaincode調(diào)用參數(shù):
cis, err := putils.GetChaincodeInvocationSpec(prop)為chancode檢查ESCC和VSCC, 該函數(shù)暫時(shí)未實(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