
区块链中不同的链有不同的交易形式和交易结构,本文采用fabric v2.2版本对fabric的交易做一个具体分析,以便跟深入的了解fabric。
fabric的交易结构都是通过protobuf定义,不同的结构之间通过byte数组来链接,交易使用的是GRPC通信。具体的proto文件在项目https://gitclone.com/github.com/hyperledger/fabric-protos中定义,生成的.pb.go文件位于https://gitclone.com/github.com/hyperledger/fabric-protos-go中。通过查看这两个项目里的源码或者向fabric节点请求交易,可以看到返回的结构中基本是字节数组,下面以一个完整的已经处理的交易对象做一个具体的说明。
message ProcessedTransaction {// An Envelope which includes a processed transactioncommon.Envelope transactionEnvelope = 1;// An indication of whether the transaction was validated or invalidated by committing peerint32 validationCode = 2;}
交易信封里包含交易负载字节流和一个签名
message Envelope {// A marshaled Payloadbytes payload = 1;// A signature by the creator specified in the Payload headerbytes signature = 2;}
通过 protobuf 可以反序列化上面payload 字节数组可以得到交易负载对象,里面包括了交易头部和交易数据
message Payload {// Header is included to provide identity and prevent replayHeader header = 1;// Data, the encoding of which is defined by the type in the headerbytes data = 2;}
交易头部包含通道头部字节流和签名头部字节流
message Header {bytes channel_header = 1;bytes signature_header = 2;}
通过 protobuf 可以反序列化上面 channel_header 字节流为通道头部对象,包含了类型、版本、时间戳、通道ID 、交易ID、 时期、 扩展字节数组、TLS证书Hash
message ChannelHeader {int32 type = 1; // Header types 0-10000 are reserved and defined by HeaderType// Version indicates message protocol versionint32 version = 2;// Timestamp is the local time when the message was created// by the sendergoogle.protobuf.Timestamp timestamp = 3;// Identifier of the channel this message is bound forstring channel_id = 4;// An unique identifier that is used end-to-end.// - set by higher layers such as end user or SDK// - passed to the endorser (which will check for uniqueness)// - as the header is passed along unchanged, it will be// be retrieved by the committer (uniqueness check here as well)// - to be stored in the ledgerstring tx_id = 5;// The epoch in which this header was generated, where epoch is defined based on block height// Epoch in which the response has been generated. This field identifies a// logical window of time. A proposal response is accepted by a peer only if// two conditions hold:// 1. the epoch specified in the message is the current epoch// 2. this message has been only seen once during this epoch (i.e. it hasn't// been replayed)uint64 epoch = 6;// Extension that may be attached based on the header typebytes extension = 7;// If mutual TLS is employed, this represents// the hash of the client's TLS certificatebytes tls_cert_hash = 8;}
通过 protobuf 可以反序列化上面 signature_header 字节流为签名头部对象,包含了一个创作者字节数组和一个随机数
message SignatureHeader {// Creator of the message, a marshaled msp.SerializedIdentitybytes creator = 1;// Arbitrary number that may only be used once. Can be used to detect replay attacks.bytes nonce = 2;}
通过protobuf 可以反序列化上面 creator 字节流为创作者对象,包含了MSP ID和身份序列化数组,身份序列化数组反序列化后可以获得签名证书里的字段
message SerializedIdentity {// The identifier of the associated membership service providerstring mspid = 1;// the Identity, serialized according to the rules of its MPSbytes id_bytes = 2;}
至此,payload 对象的头部结构反序列化结束
下面对 payload 对象的数据字节数组进行反序列化
通过 protobuf 可以反序列化上面payload 里的 data 字节数组可以得到交易对象,里面包括了交易行为数组;每个交易行为包括了头部字节数组和负载字节数组
message Transaction {// The payload is an array of TransactionAction. An array is necessary to// accommodate multiple actions per transactionrepeated TransactionAction actions = 1;}message TransactionAction {// The header of the proposal action, which is the proposal headerbytes header = 1;// The payload of the action as defined by the type in the header For// chaincode, it's the bytes of ChaincodeActionPayloadbytes payload = 2;}
通过 protobuf 可以反序列化上面 header 字节数组可以得到一个签名头部对象对象,具体结构和上面一样,包含一个创作者字节数组和一个随机数,创造者字节数组包含MSP ID和身份序列化数组,身份序列化数组反序列化后可以获得签名证书里的字段
通过 protobuf 可以反序列化上面payload 字节数组可以得到链码行为负载对象,里面包括了链码提议负载字节数组和链码背书行为
message ChaincodeActionPayload {// This field contains the bytes of the ChaincodeProposalPayload message from// the original invocation (essentially the arguments) after the application// of the visibility function. The main visibility modes are "full" (the// entire ChaincodeProposalPayload message is included here), "hash" (only// the hash of the ChaincodeProposalPayload message is included) or// "nothing". This field will be used to check the consistency of// ProposalResponsePayload.proposalHash. For the CHAINCODE type,// ProposalResponsePayload.proposalHash is supposed to be H(ProposalHeader ||// f(ChaincodeProposalPayload)) where f is the visibility function.bytes chaincode_proposal_payload = 1;// The list of actions to apply to the ledgerChaincodeEndorsedAction action = 2;}
通过 protobuf 可以反序列化上面 chaincode_proposal_payload 字节数组可以得到链码提议负载对象,里面包括了输入字节数组和一个临时表
message ChaincodeProposalPayload {// Input contains the arguments for this invocation. If this invocation// deploys a new chaincode, ESCC/VSCC are part of this field.// This is usually a marshaled ChaincodeInvocationSpecbytes input = 1;// TransientMap contains data (e.g. cryptographic material) that might be used// to implement some form of application-level confidentiality. The contents// of this field are supposed to always be omitted from the transaction and// excluded from the ledger.map<string, bytes> TransientMap = 2;}
通过 protobuf 可以反序列化上面 input 字节数组可以得到链码调用规范对象,里面包括了链码规范对象;链码规范对象里包含了类型、链码ID对象、链码输入对象和超时时间戳;链码ID对象包含了链码路径、名字和版本;链码输入对象包含了参数字节数组、描述和是否初始化
message ChaincodeInvocationSpec {// Prevent removed tag re-usereserved 2;reserved "id_generation_alg";ChaincodeSpec chaincode_spec = 1;}message ChaincodeSpec {enum Type {UNDEFINED = 0;GOLANG = 1;NODE = 2;CAR = 3;JAVA = 4;}Type type = 1;ChaincodeID chaincode_id = 2;ChaincodeInput input = 3;int32 timeout = 4;}message ChaincodeID {//deploy transaction will use the pathstring path = 1;//all other requests will use the name (really a hashcode) generated by//the deploy transactionstring name = 2;//user friendly version name for the chaincodestring version = 3;}message ChaincodeInput {repeated bytes args = 1;map<string, bytes> decorations = 2;// is_init is used for the application to signal that an invocation is to be routed// to the legacy 'Init' function for compatibility with chaincodes which handled// Init in the old way. New applications should manage their initialized state// themselves.bool is_init = 3;}
下面介绍下链码背书行为对象,背书行为对象包括了一个提议响应负载字节数组和背书对象数组;每个背书对象包含了一个背书身份的字节数组和一个签名字节数组
message ChaincodeEndorsedAction {// This is the bytes of the ProposalResponsePayload message signed by the// endorsers. Recall that for the CHAINCODE type, the// ProposalResponsePayload's extenstion field carries a ChaincodeActionbytes proposal_response_payload = 1;// The endorsement of the proposal, basically the endorser's signature over// proposalResponsePayloadrepeated Endorsement endorsements = 2;}message Endorsement {// Identity of the endorser (e.g. its certificate)bytes endorser = 1;// Signature of the payload included in ProposalResponse concatenated with// the endorser's certificate; ie, sign(ProposalResponse.payload + endorser)bytes signature = 2;}
通过 protobuf 可以反序列化上面 proposal_response_payload 字节数组可以得到提议响应负载对象,里面包括了提议哈希和扩展字节数组
message ProposalResponsePayload {// Hash of the proposal that triggered this response. The hash is used to// link a response with its proposal, both for bookeeping purposes on an// asynchronous system and for security reasons (accountability,// non-repudiation). The hash usually covers the entire Proposal message// (byte-by-byte).bytes proposal_hash = 1;// Extension should be unmarshaled to a type-specific message. The type of// the extension in any proposal response depends on the type of the proposal// that the client selected when the proposal was initially sent out. In// particular, this information is stored in the type field of a Header. For// chaincode, it's a ChaincodeAction messagebytes extension = 2;}
通过 protobuf 可以反序列化上面 extension 字节数组可以得到链码行为对象,里面包括了结果字节数组、事件字节数组、响应对象、链码ID对象;事件字节流的解析对象为链码事件,有时为空,这儿不做深入解析;响应对象包括了状态、消息和响应负载字节数组,不同的响应字节数组内容不同有时为空;链码ID对象和上面介绍的一样,包括了链码路径、名字和版本
message ChaincodeAction {reserved 5;reserved "token_operations";// This field contains the read set and the write set produced by the// chaincode executing this invocation.bytes results = 1;// This field contains the event generated by the chaincode.// Only a single marshaled ChaincodeEvent is included.bytes events = 2;// This field contains the result of executing this invocation.Response response = 3;// This field contains the ChaincodeID of executing this invocation. Endorser// will set it with the ChaincodeID called by endorser while simulating proposal.// Committer will validate the version matching with latest chaincode version.// Adding ChaincodeID to keep version opens up the possibility of multiple// ChaincodeAction per transaction.ChaincodeID chaincode_id = 4;}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;}
通过 protobuf 可以反序列化上面 results 字节数组可以得到交易读写集对象,里面包括了数据模型和命名空间读写集;命名空间读写集包括了一个命名空间、一个读写集字节数组和一个集合哈希读写集
message TxReadWriteSet {enum DataModel {KV = 0;}DataModel data_model = 1;repeated NsReadWriteSet ns_rwset = 2;}message NsReadWriteSet {string namespace = 1;bytes rwset = 2; Data model specific serialized proto message (e.g., kvrwset.KVRWSet for KV and Document data models)repeated CollectionHashedReadWriteSet collection_hashed_rwset = 3;}message CollectionHashedReadWriteSet {string collection_name = 1;bytes hashed_rwset = 2; Data model specific serialized proto message (e.g., kvrwset.HashedRWSet for KV and Document data models)bytes pvt_rwset_hash = 3; Hash of entire private read-write set for a specific collection. This helps in authenticating the private read-write set efficiently}
通过 protobuf 可以反序列化上面 rwset 字节数组可以得到键值读写集对象,里面包括了键值读对象数组、范围查询对象数组、键值写对象数组和键值元数据写对象数组
message KVRWSet {repeated KVRead reads = 1;repeated RangeQueryInfo range_queries_info = 2;repeated KVWrite writes = 3;repeated KVMetadataWrite metadata_writes = 4;}message KVRead {string key = 1;Version version = 2;}message KVWrite {string key = 1;bool is_delete = 2;bytes value = 3;}message KVMetadataWrite {string key = 1;repeated KVMetadataEntry entries = 2;}message RangeQueryInfo {string start_key = 1;string end_key = 2;bool itr_exhausted = 3;oneof reads_info {QueryReads raw_reads = 4;QueryReadsMerkleSummary reads_merkle_hashes = 5;}}message QueryReads {repeated KVRead kv_reads = 1;}
以上就是一个fabric交易的完整结构解析,相对于其他链的交易结构比较复杂。
文章转载自求真得趣,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




