• 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

  • 相关阅读:
    CodeForces
    处女座的测验(一)(素数筛+思维)
    Codeforces-D-Diverse Garland(思维)
    linux中open函数使用
    linux管道通信
    linux中memset的正确用法
    在linux中read、write函数
    Ubuntu+Win7+Samba实现文件共享
    【转】教你如何实现linux和W…
    《转》我的ARM学习经历
  • 原文地址:https://www.cnblogs.com/jiftle/p/15228050.html
Copyright © 2020-2023  润新知