fabric根据Txid查询交易信息
⼀、QueryTransaction接⼝
和以太坊类似,fabric提交⼀笔写交易后会得到⼀个交易ID,我们可以根据这个交易ID去查询交易相关信息,然⽽fabric-sdk-go中提供的查询接⼝QueryTransaction返回的信息很有限(或者说不易读)。
这⾥是GoDoc中的该接⼝的相关⽂档,有兴趣的读者可以看⼀下,QueryTransaction,如果没有兴趣,接着往下看。
通过交易ID查询交易的接⼝如下:
func (c *Client) QueryTransaction(transactionID fab.TransactionID, options ...RequestOption) (*pb.ProcessedTransaction, error)
该函数的返回值对应的结构体为:
type ProcessedTransaction struct {
// An Envelope which includes a processed transaction
TransactionEnvelope *common.Envelope `protobuf:"bytes,1,opt,name=transactionEnvelope,proto3" json:"transactionEnvelope,omitempty"`
// An indication of whether the transaction was validated or invalidated by committing peer
ValidationCode      int32    `protobuf:"varint,2,opt,name=validationCode,proto3" json:"validationCode,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized    []byte  `json:"-"`
XXX_sizecache        int32    `json:"-"`
}
可以看到,有⼀个ValidationCode字段代表验证状态码,我们再查看⼀下TransactionEnvelope对应的common.Envelope结构。
type Envelope struct {
/
/ A marshaled Payload
Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"`
// A signature by the creator specified in the Payload header
Signature            []byte  `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized    []byte  `json:"-"`
XXX_sizecache        int32    `json:"-"`
}
可以看到,它有⼀个Signature,代表签名,可以直接转化成字符串。另外⼀个有⽤字段为Payload,也就是交易数据了,但是为[]byte类型,⽆法直接获取其内容,需要⾃⼰解析了。
这⾥可以看到,fabric-sdk-go提供的QueryTransaction接⼝返回的直接有⽤的数据只有验证状态码和签名,其Payload需要解析,但其并没有提供直接解析的⽅法,这个就需要⾃⼰动⼿解决了。
⼆、⼀些有⽤的内部接⼝
经过研究,发现fabric-sdk-go其实⾃带了相应的⼀些解析接⼝,只是都是些内部接⼝,需要⾃⼰根据实际需求来组合。话不多说,直接切重点,上代码。
所有⽤到的接⼝均位于:fabric-sdk-go/internal/github/hyperledger/fabric/中,例如如下代码⽚断:交易时间字符串是什么
// UnmarshalPayload unmarshals bytes to a Payload
func UnmarshalPayload(encoded []byte) (*cb.Payload, error) {
payload := &cb.Payload{}
err := proto.Unmarshal(encoded, payload)
return payload, errors.Wrap(err, "error unmarshaling Payload")
}
这个⽅法就是解析上⾯提到的Payload的。
本⽂中所有解析都是基于提供的⽅法。
三、解析基本交易信息
有眼尖的读者可能看到了,上⾯提到的位于internal下⾯的⼦⽬录下,也就是对应的包protoutil为⼀个内部包,在外部⽆法直接引⽤。为此,我们需要稍微修改⼀下fabric-sdk-go,在fabric-sdk-go⽬录下添加⼀个⾃定义的包来引⽤这些内部包。
在该⽬录下建⽴⼀个myutils包,并在该包下建⽴⼀个⽂件,代码如下:
package myutils
import (
"encoding/pem"
"fmt"
"time"
"github/hyperledger/fabric-sdk-go/internal/github/hyperledger/fabric/protoutil"
"github/tjfoc/gmsm/sm2"
)
//TransactionInfo 解析后的交易信息
type TransactionInfo struct {
CreateTime      string  //交易创建时间
ChaincodeID      string  //交易调⽤链码ID
ChaincodeVersion string  //交易调⽤链码版本
Nonce            []byte  //随机数
Mspid            string  //交易发起者MSPID
Name            string  //交易发起者名称
OUTypes          string  //交易发起者OU分组
Args            []string //输⼊参数
Type            int32    //交易类型
TxID            string  //交易ID
}
// UnmarshalTransaction 从某个交易的payload来解析它
func UnmarshalTransaction(payloadRaw []byte) (*TransactionInfo, error) {
result := &TransactionInfo{}
//解析成payload
payload, err := protoutil.UnmarshalPayload(payloadRaw)
if err != nil {
return nil, err
}
//解析成ChannelHeader(包含通道ID、交易ID及交易创建时间等)
channelHeader, err := protoutil.UnmarshalChannelHeader(payload.Header.ChannelHeader)
if err != nil {
return nil, err
}
//解析成SignatureHeader(包含创建者和nonce)
signHeader, err := protoutil.UnmarshalSignatureHeader(payload.Header.SignatureHeader)
if err != nil {
return nil, err
}
/
/解析成SerializedIdentity(包含证书和MSPID)
identity, err := protoutil.UnmarshalSerializedIdentity(signHeader.GetCreator())
if err != nil {
return nil, err
}
//下⾯为解析证书
block, _ := pem.Decode(identity.GetIdBytes())
if block == nil {
return nil, fmt.Errorf("identity could not be decoded from credential")
}
cert, err := sm2.ParseCertificate(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse certificate: %s", err)
}
//解析⽤户名和OU分组
uname := cert.Subject.CommonName
outypes := cert.Subject.OrganizationalUnit
//解析成transaction
tx, err := protoutil.UnmarshalTransaction(payload.Data)
if err != nil {
return nil, err
}
/
/进⼀步从transaction中解析成ChaincodeActionPayload
chaincodeActionPayload, err := protoutil.UnmarshalChaincodeActionPayload(tx.Actions[0].Payload)
if err != nil {
return nil, err
}
//进⼀步解析成proposalPayload
proposalPayload, err := protoutil.UnmarshalChaincodeProposalPayload(chaincodeActionPayload.ChaincodeProposalPayload)    if err != nil {
return nil, err
}
//得到交易调⽤的链码信息
chaincodeInvocationSpec, err := protoutil.UnmarshalChaincodeInvocationSpec(proposalPayload.Input)
if err != nil {
return nil, err
}
//得到调⽤的链码的ID,版本和PATH(这⾥PATH省略了)
result.ChaincodeID = chaincodeInvocationSpec.ChaincodeSpec.ChaincodeId.Name
result.ChaincodeVersion = chaincodeInvocationSpec.ChaincodeSpec.ChaincodeId.Version
//得到输⼊参数
var args []string
for _, v := range chaincodeInvocationSpec.ChaincodeSpec.Input.Args {
args = append(args, string(v))
}
result.Args = args
result.Nonce = signHeader.GetNonce()
result.Type = channelHeader.GetType()
result.TxID = channelHeader.GetTxId()
result.Mspid = identity.GetMspid()
result.Name = uname
result.OUTypes = outypes[0]
result.CreateTime = time.Unix(channelHeader.Timestamp.Seconds, 0).Format("2006-01-02 15:04:05")
return result, nil
}
需要说明的是,因为使⽤了国密版本,所以这⾥导⼊了sm2包。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。