Fabric1.0的架构中引入了
client
->endorser
->order
->commiter
的概念。客户端首先将交易发送到背书节点进行模拟执行,然后收集一定数量的背书结果构造交易发送到排序节点进行排序,最后排序节点对收到的交易进行排序并打包发送给commiter节点进行验证和计入账本。本文将针对交易执行的第一个阶段client -> endorser的相关流程结合源码进行分析。
一、Proposal交互流程
1.客户端向endorser
发送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消息体的序列化内容,消息体类型由header类型确定
bytes payload = 2;
// 扩展字段对于 CHAINCODE类型, 其可能使
bytes extension = 3;
}
-
endorser
将proposal
执行结果返回给客户端,propose response的定义如下:
message ProposalResponse {
// Version indicates message protocol version
int32 version = 1;
google.protobuf.Timestamp timestamp = 2;
//表示执行是否成功
Response response = 4;
// 返回结果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。交易将会被发送到共识节点orders, 经过排序之后batch形式的交易会被广播到peer节点进行验证以及写入账本。
二、Endorser节点的处理流程
源文件fabric/core/endorser.go中ProcessProposal
函数负责具体的Proposel的处理。
- 检查message是否有效,消息体个个字段的完整性以及chaincode id以及调用类型的检查,例如proposal的消息不能调用system chaincode;
- 检查proposal是否满足channel的policy, tx的重复性判断;
- 模拟执行proposal中的chaincode调用,其实也就是实际执行,这里需要两个关键组件:
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结果返回
pResp, err = e.endorseProposal(ctx, chainID, txid, signedProp, prop, res, simulationResult, ccevent, hdrExt.PayloadVisibility, hdrExt.ChaincodeId, txsim, cd)
2.1 simulate proposal 详细流程
以上便是ProcessProposal的整体流程,这里关键的是simulateProposal这一步,接下来对该函数进行展开分析。
解析chaincode调用参数:
cis, err := putils.GetChaincodeInvocationSpec(prop)
为chancode检查ESCC和VSCC, 该函数暂时未实现
if err = e.checkEsccAndVscc(prop); err != nil {
return nil, nil, nil, nil, err
}
- 获取chaincode的相关数据ChancodeData
cdLedger, err = e.getCDSFromLSCC(ctx, chainID, txid, signedProp, prop, cid.Name, txsim)
- 执行chaincode并获取结果
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