根据TxID获取上链数据
根据TxID获取上链信息
前段时间应甲方爸爸的要求,需要在现有的业务系统中新增一个根据TxID来查询上链信息的接口。搜了一圈发现相关的信息很少,最后只能祭出终极大招:Read Source Code。
本文主要记录我实现这一功能的过程。
1、获取交易信息
首先要做的就是拿到交易信息,我这里是通过fabric-sdk-go提供的接口ledger.Client.QueryTransaction(transactionID fab.TransactionID, options ...RequestOption) (*pb.ProcessedTransaction, error)
来获取交易信息;交易信息就包含在ProcessedTransaction
结构中。
1.1、ProcessedTransaction
ProcessedTransaction定义如下:
type ProcessedTransaction struct { // 封装了所有的交易信息 TransactionEnvelope *common.Envelope `protobuf:"bytes,1,opt,name=transactionEnvelope,proto3" json:"transactionEnvelope,omitempty"` // 标识交易是否通过验证 ValidationCode int32 `protobuf:"varint,2,opt,name=validationCode,proto3" json:"validationCode,omitempty"` }
2、解析TransactionEnvelope
交易信息全都封装在common.Envelope
结构中:
// Envelope wraps a Payload with a signature so that the message may be authenticated type Envelope struct { // 编码后的交易信息,是common.Payload编码后的 Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` // 交易发起人的签名 Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` }
2.1、common.Payload
common.Payload定义如下:
// Payload is the message contents (and header to allow for signing) type Payload struct { // Header is included to provide identity and prevent replay Header *Header `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"` // 编码后的peer.Transaction,该结构保存交易信息 Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` }
2.2、peer.Transaction
peer.Transaction结构中包含了该交易的所有活动:
type Transaction struct { // The payload is an array of TransactionAction. An array is necessary to // accommodate multiple actions per transaction Actions []*TransactionAction `protobuf:"bytes,1,rep,name=actions,proto3" json:"actions,omitempty"` }
这个结构就是整个接口的核心,说白了就是要解析TransactionAction
结构。
3、解析peer.TransactionAction
peer.TransactionAction定义如下:
type TransactionAction struct { // The header of the proposal action, which is the proposal header Header []byte `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"` // bytes格式的ChaincodeActionPayload Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` }
其中Payload中包含着我们想要的信息。
3.1、peer.ChaincodeActionPayload
peer.ChaincodeActionPayload定义如下:
type ChaincodeActionPayload struct { ChaincodeProposalPayload []byte `protobuf:"bytes,1,opt,name=chaincode_proposal_payload,json=chaincodeProposalPayload,proto3" json:"chaincode_proposal_payload,omitempty"` // 在账本中执行的操作 Action *ChaincodeEndorsedAction `protobuf:"bytes,2,opt,name=action,proto3" json:"action,omitempty"` }
其中的我们要找的信息就在这个Action
里。
3.2、peer.ChaincodeEndorsedAction
peer.ChaincodeEndorsedAction定义如下:
type ChaincodeEndorsedAction struct { // 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 ChaincodeAction ProposalResponsePayload []byte `protobuf:"bytes,1,opt,name=proposal_response_payload,json=proposalResponsePayload,proto3" json:"proposal_response_payload,omitempty"` Endorsements []*Endorsement `protobuf:"bytes,2,rep,name=endorsements,proto3" json:"endorsements,omitempty"` }
从ProposalResponsePayload
字段解出peer.ProposalResponsePayload
结构:
type ProposalResponsePayload struct { ProposalHash []byte `protobuf:"bytes,1,opt,name=proposal_hash,json=proposalHash,proto3" json:"proposal_hash,omitempty"` // 对chaincode来说, Extension保存ChaincodeAction信息 Extension []byte `protobuf:"bytes,2,opt,name=extension,proto3" json:"extension,omitempty"` }
接下来就是解Extension
。
3.3、peer.ChaincodeAction
peer.ChaincodeAction定义如下:
type ChaincodeAction struct { // chaincode执行影响到的读写集 Results []byte `protobuf:"bytes,1,opt,name=results,proto3" json:"results,omitempty"` // This field contains the events generated by the chaincode executing this // invocation. Events []byte `protobuf:"bytes,2,opt,name=events,proto3" json:"events,omitempty"` // chaincode调用结果 Response *Response `protobuf:"bytes,3,opt,name=response,proto3" json:"response,omitempty"` // 调用的chaincode的信息 ChaincodeId *ChaincodeID `protobuf:"bytes,4,opt,name=chaincode_id,json=chaincodeId,proto3" json:"chaincode_id,omitempty"` // This field contains the token expectation generated by the chaincode // executing this invocation TokenExpectation *token.TokenExpectation `protobuf:"bytes,5,opt,name=token_expectation,json=tokenExpectation,proto3" json:"token_expectation,omitempty"` }
至此,我们才算拿到我们真正想要的东西 --- 没错,就是这个Results
。
4、解析TxReadWriteSet
rwset.TxReadWriteSet结构定义如下:
type TxReadWriteSet struct { DataModel TxReadWriteSet_DataModel `protobuf:"varint,1,opt,name=data_model,json=dataModel,proto3,enum=rwset.TxReadWriteSet_DataModel" json:"data_model,omitempty"` NsRwset []*NsReadWriteSet `protobuf:"bytes,2,rep,name=ns_rwset,json=nsRwset,proto3" json:"ns_rwset,omitempty"` }
NsReadWriteSet
定义:
type NsReadWriteSet struct { Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` Rwset []byte `protobuf:"bytes,2,opt,name=rwset,proto3" json:"rwset,omitempty"` CollectionHashedRwset []*CollectionHashedReadWriteSet `protobuf:"bytes,3,rep,name=collection_hashed_rwset,json=collectionHashedRwset,proto3" json:"collection_hashed_rwset,omitempty"` }
其中这个Rwset
就是我们的目标,它是kvrwset.KVRWSet
编码后得到的。
4.1、kvrwset.KVRWSet
kvrwset.KVRWSet定义如下:
type KVRWSet struct { Reads []*KVRead `protobuf:"bytes,1,rep,name=reads,proto3" json:"reads,omitempty"` RangeQueriesInfo []*RangeQueryInfo `protobuf:"bytes,2,rep,name=range_queries_info,json=rangeQueriesInfo,proto3" json:"range_queries_info,omitempty"` Writes []*KVWrite `protobuf:"bytes,3,rep,name=writes,proto3" json:"writes,omitempty"` MetadataWrites []*KVMetadataWrite `protobuf:"bytes,4,rep,name=metadata_writes,json=metadataWrites,proto3" json:"metadata_writes,omitempty"` }
之后通过解析Writes
就能获取到上链的数据,即调用shim.PutState(key string, value []byte)
使用的key和value。
声明:本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)进行许可,使用时请注明出处。
Author: MengBin
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· .NET 9 new features-C#13新的锁类型和语义
· Spring AI + Ollama 实现 deepseek-r1 的API服务和调用
· 《HelloGitHub》第 106 期
· 数据库服务器 SQL Server 版本升级公告
· 深入理解Mybatis分库分表执行原理
· 使用 Dify + LLM 构建精确任务处理应用