zoukankan      html  css  js  c++  java
  • 令人懊恼的阉割版fabric sdk功能缺失

    按理说,fabric本身都是用golang开发的,那么fabric-sdk-go作为其亲儿子,功能应该是最为完善的。然而,与我们想法相左的是,golang版本的sdk反而是最不完备的,开发进度滞后,功能缺失。如果要选用最完备的sdk,推荐使用nodejs版本的,再次之java版本。然而,不巧的是,出于某些原因,我们项目偏偏采用了golang版本。

    那么我们只能自己来克服一些困难了。比如我们就有这个么需求:显示某一笔交易的以下信息:

    • 创建时间
    • 调用的参数

    不用找了,目前的go sdk里是没有直接获得这两个参数的方法的(也有可能是我自己没找到)。那怎么办呢?放弃是不可能放弃的,我们去借鉴下nodejs版本的sdk。

    可以我又不想研究nodejs的源码,golang版本基本上算是没有使用文档了,而nodejs则有较为完备的文档网站,于是我就去看nodejs版本sdk的文档。

    让我们开始吧!

    一、切入口:借鉴nodejs版本

    这两个字段虽然目前没有办法直接获取,但是由于nodejs版本有方法返回,而不同的sdk与之交互是相同的fabric镜像,那说明这个字段fabric是返回了的,只是golang的sdk没有解析,应该是存在返回的payload里面的。

    那我们去nodejs版本文档里去找找这个payload是什么结构可好?

    QueryTransaction 方法

    首先,因为我们是获取交易的信息,很明显,我们应该通过Transaction关键字入手,自然而然的我们找到了这么个方法。
    QueryTransaction 方法

    我们看一下最后的返回参数就好

    A Promise for a fully decoded ProcessedTransaction object.
    

    从连接点进去,我们就看到了

    ProcessedTransaction 结构


    每个字段我都点进去看过了,最后锁定了这个字段transactionEnvelope > payload > data
    点击data连接进入,我们看到了这个结构

    payload > data 结构

    真是庞大的结构体,但是大部分我们都用不着,我们只关心payload > action > chaincode_proposal_payload > input部分

    actions {array}
    	header -- {SignatureHeader}
    	payload
    		chaincode_proposal_payload
    			input -- {ChaincodeInvocationSpec} for a endorser transaction
    		action
    			proposal_response_payload
    				proposal_hash -- {byte[]}
    				extension
    					results
    						data_model -- {int}
    						ns_rwset -- {array}
    							namespace -- {string}
    							rwset
    								reads -- {array}
    									key -- {string}
    									version
    										block_num -- {number}
    										tx_num -- {number}
    								range_queries_info -- {array}
    								writes -- {array}
    									key -- {string}
    									is_delete -- {boolean}
    									value -- {string}
    								metadata_writes -- {array}
    									key -- {string}
    									entries -- {array}
    										name -- {string}
    										value -- {byte[]}
    						collection_hashed_rwset -- {array}
    							collection_name -- {string}
    							hashed_rwset
    								hashed_reads -- {array}
    									key_hash -- {byte[]}
    									version
    										block_num -- {number}
    										tx_num -- {number}
    								hashed_writes -- {array}
    									key_hash -- {byte[]}
    									is_delete -- {boolean}
    									value_hash -- {byte[]}
    								metadata_writes -- {array}
    									key_hash -- {byte[]}
    									entries -- {array}
    										name -- {string}
    										value -- {byte[]}
    							pvt_rwset_hash -- {byte[]}
    					events
    						chaincode_id --  {string}
    						tx_id -- {string}
    						event_name -- {string}
    						payload -- {byte[]}
    					response
    						status -- {int}
    						message -- {string}
    						payload -- {byte[]}
    			endorsements -- {Endorsement[]}
    

    ChaincodeInvocationSpec点进去,我们距离最终结果就很近了!

    ChaincodeInvocationSpec结构体

    看看我们发现了什么?!

    chaincode_spec
    	type -- {int}
    	chaincode_id
    		path -- {string}
    		name -- {string}
    		version -- {string}
    	input
    		args -- {byte[][]}
    		decorations -- {map of string to byte[]}
    	timeout -- {int}
    

    很明显,我们看到了合约的参数字段args ,甚至还有合约路径名称版本等。

    那么我们接下来就开始按图索骥,到golang的sdk里面寻找相应功能

    二、对照fabric-sdk-go

    我们按照上面的流程,可以在go的sdk里找到上面这个结构相应的结构体:

    ProcessedTransaction

    # transaction.pb.go
    type ProcessedTransaction struct {
    	// An Envelope which includes a processed transaction
    	TransactionEnvelope *common.Envelope `protobuf:"bytes,1,opt,name=transactionEnvelope" json:"transactionEnvelope,omitempty"`
    	// An indication of whether the transaction was validated or invalidated by committing peer
    	ValidationCode       int32    `protobuf:"varint,2,opt,name=validationCode" json:"validationCode,omitempty"`
    	XXX_NoUnkeyedLiteral struct{} `json:"-"`
    	XXX_unrecognized     []byte   `json:"-"`
    	XXX_sizecache        int32    `json:"-"`
    }
    

    TransactionEnvelope

    # 文件common.pb
    // Envelope wraps a Payload with a signature so that the message may be authenticated
    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:"-"`
    }
    

    Payload

    # 文件common.pb
    // 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" json:"header,omitempty"`
    	// Data, the encoding of which is defined by the type in the header
    	Data                 []byte   `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
    	XXX_NoUnkeyedLiteral struct{} `json:"-"`
    	XXX_unrecognized     []byte   `json:"-"`
    	XXX_sizecache        int32    `json:"-"`
    }
    

    Transaction

    # 文件common.pb
    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" json:"actions,omitempty"`
    	XXX_NoUnkeyedLiteral struct{}             `json:"-"`
    	XXX_unrecognized     []byte               `json:"-"`
    	XXX_sizecache        int32                `json:"-"`
    }
    

    ChaincodeInvocationSpec

    # 文件chaincode.pb.go
    type ChaincodeInvocationSpec struct {
    	ChaincodeSpec *ChaincodeSpec `protobuf:"bytes,1,opt,name=chaincode_spec,json=chaincodeSpec" json:"chaincode_spec,omitempty"`
    	// This field can contain a user-specified ID generation algorithm
    	// If supplied, this will be used to generate a ID
    	// If not supplied (left empty), sha256base64 will be used
    	// The algorithm consists of two parts:
    	//  1, a hash function
    	//  2, a decoding used to decode user (string) input to bytes
    	// Currently, SHA256 with BASE64 is supported (e.g. idGenerationAlg='sha256base64')
    	IdGenerationAlg string `protobuf:"bytes,2,opt,name=id_generation_alg,json=idGenerationAlg" json:"id_generation_alg,omitempty"`
    }
    

    三、抽出我们要的args字段

    下面我们要做的就是抽丝剥茧般地获取到我们所需的字段,通过一层一层地解析各个结构体中的payload等字段

    1、数据来源

    首先我们可以通过原sdk就有的功能得到某个区块里的所有交易,即

    // This is finalized block structure to be shared among the orderer and peer
    // Note that the BlockHeader chains to the previous BlockHeader, and the BlockData hash is embedded
    // in the BlockHeader.  This makes it natural and obvious that the Data is included in the hash, but
    // the Metadata is not.
    type Block struct {
    	Header               *BlockHeader   `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"`
    	Data                 *BlockData     `protobuf:"bytes,2,opt,name=data" json:"data,omitempty"`
    	Metadata             *BlockMetadata `protobuf:"bytes,3,opt,name=metadata" json:"metadata,omitempty"`
    	XXX_NoUnkeyedLiteral struct{}       `json:"-"`
    	XXX_unrecognized     []byte         `json:"-"`
    	XXX_sizecache        int32          `json:"-"`
    }
    

    该结构体中的Data是一个数组对象

    type BlockData struct {
    	Data                 [][]byte `protobuf:"bytes,1,rep,name=data,proto3" json:"data,omitempty"`
    	XXX_NoUnkeyedLiteral struct{} `json:"-"`
    	XXX_unrecognized     []byte   `json:"-"`
    	XXX_sizecache        int32    `json:"-"`
    }
    

    每个[]byte即其包含的单条交易信息

    2、逐层解析

    假设data即获取的单条交易的内容,比如前文所说的BlockData.Data[0]

    获取 common.Envelope

    	env, err := putils.GetEnvelopeFromBlock(data)
    	if err != nil {
    		return nil, errors.Wrap(err, "error extracting Envelope from block")
    	}
    	if env == nil {
    		return nil, errors.New("nil envelope")
    	}
    

    获取 common.Payload

    	payload, err := putils.GetPayload(env)
    	if err != nil {
    		return nil, errors.Wrap(err, "error extracting Payload from envelope")
    	}
    

    获取 peer.Transaction

    	tx, err := putils.GetTransaction(payload.Data)
    	if err != nil {
    		return nil, errors.Wrap(err, "error unmarshalling transaction payload")
    	}
    

    获取 peer.ChaincodeActionPayload

    	chaincodeActionPayload, err := putils.GetChaincodeActionPayload(tx.Actions[0].Payload)
    	if err != nil {
    		return nil, errors.Wrap(err, "error unmarshalling chaincode action payload")
    	}
    	propPayload := &pb.ChaincodeProposalPayload{}
    

    获取 pb.ChaincodeProposalPayload

    	if err := proto.Unmarshal(chaincodeActionPayload.ChaincodeProposalPayload, propPayload); err != nil {
    		return nil, errors.Wrap(err, "error extracting ChannelHeader from payload")
    	}
    

    获取 pb.ChaincodeInvocationSpec

    	invokeSpec := &pb.ChaincodeInvocationSpec{}
    	err = proto.Unmarshal(propPayload.Input, invokeSpec)
    	if err != nil {
    		return nil, errors.Wrap(err, "error extracting ChannelHeader from payload")
    	}
    

    这么一来,便获取到咱们所需的args字段了

    完整代码如下:

    交易创建时间的获取方式,与之类似,在此不赘述了。

    import (
    	"github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common"
    	cb "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common"
    	putils "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/utils"
    	pb "github.com/hyperledger/fabric/protos/peer"
    )
    
    // TransactionDetail获取了交易的具体信息
    type TransactionDetail struct {
    	TransactionId string
    	CreateTime    string
    	Args          []string
    }
    
    // 从SDK中Block.BlockDara.Data中提取交易具体信息
    func GetTransactionInfoFromData(data []byte, needArgs bool) (*TransactionDetail, error) {
    	env, err := putils.GetEnvelopeFromBlock(data)
    	if err != nil {
    		return nil, errors.Wrap(err, "error extracting Envelope from block")
    	}
    	if env == nil {
    		return nil, errors.New("nil envelope")
    	}
    	payload, err := putils.GetPayload(env)
    	if err != nil {
    		return nil, errors.Wrap(err, "error extracting Payload from envelope")
    	}
    	channelHeaderBytes := payload.Header.ChannelHeader
    	channelHeader := &cb.ChannelHeader{}
    	if err := proto.Unmarshal(channelHeaderBytes, channelHeader); err != nil {
    		return nil, errors.Wrap(err, "error extracting ChannelHeader from payload")
    	}
    	var (
    		args []string
    	)
    	if needArgs {
    		tx, err := putils.GetTransaction(payload.Data)
    		if err != nil {
    			return nil, errors.Wrap(err, "error unmarshalling transaction payload")
    		}
    		chaincodeActionPayload, err := putils.GetChaincodeActionPayload(tx.Actions[0].Payload)
    		if err != nil {
    			return nil, errors.Wrap(err, "error unmarshalling chaincode action payload")
    		}
    		propPayload := &pb.ChaincodeProposalPayload{}
    		if err := proto.Unmarshal(chaincodeActionPayload.ChaincodeProposalPayload, propPayload); err != nil {
    			return nil, errors.Wrap(err, "error extracting ChannelHeader from payload")
    		}
    		invokeSpec := &pb.ChaincodeInvocationSpec{}
    		err = proto.Unmarshal(propPayload.Input, invokeSpec)
    		if err != nil {
    			return nil, errors.Wrap(err, "error extracting ChannelHeader from payload")
    		}
    		for _, v := range invokeSpec.ChaincodeSpec.Input.Args {
    			args = append(args, string(v))
    		}
    	}
    	result := &TransactionDetail{
    		TransactionId: channelHeader.TxId,
    		Args:          args,
    		CreateTime:    time.Unix(channelHeader.Timestamp.Seconds, 0).Format(pconst.TimeFormatStandard),
    	}
    	return result, nil
    }
    
  • 相关阅读:
    单片机多功能调试助手
    《划时代51单片机C语言全新教程》第十九章 网络通信 概览
    《划时代51单片机C语言全新教程》第十五章 按键计数器 概览
    《划时代51单片机C语言全新教程》第十七章 频率计 概览
    前后台程序框架实例2
    《划时代51单片机C语言全新教程》第二十一章 深入编程 概览
    《划时代51单片机C语言全新教程》第二十二章 界面开发 概览
    《划时代51单片机C语言全新教程》第十八章 USB通信 概览
    《划时代51单片机C语言全新教程》第二十章 深入接口 概览
    《划时代51单片机C语言全新教程》第十六章 交通灯 概览
  • 原文地址:https://www.cnblogs.com/laolieren/p/complete_fabric_sdk_go_func.html
Copyright © 2011-2022 走看看