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.com/hyperledger/fabric/protoutil/unmarshalers.go中,例如如下代碼片斷:

// 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的。

本文中所有解析都是基於unmarshalers.go提供的方法。
三、解析基本交易信息

有眼尖的讀者可能看到了,上面提到的unmarshalers.go位於internal下面的子目錄下,也就是對應的包protoutil為一個內部包,在外部無法直接引用。為此,我們需要稍微修改一下fabric-sdk-go,在fabric-sdk-go目錄下添加一個自定義的包來引用這些內部包。

在該目錄下建立一個myutils包,並在該包下建立一個myutils.go文件,代碼如下:

package myutils

import (
    "encoding/pem"
    "fmt"
    "time"

    "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/protoutil"
    "github.com/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包。

從這里可以看出,我們已經解析了幾乎所有關鍵的交易信息。這里面也還有一些其它屬性筆者並未獲取或者解析,有興趣的讀者可以自己試着獲取一下。
————————————————
版權聲明:本文為CSDN博主「Zero_Nothing」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_39430411/article/details/110942774


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM