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
    }
    
  • 相关阅读:
    BZOJ 1040 (ZJOI 2008) 骑士
    BZOJ 1037 (ZJOI 2008) 生日聚会
    ZJOI 2006 物流运输 bzoj1003
    ZJOI 2006 物流运输 bzoj1003
    NOI2001 炮兵阵地 洛谷2704
    NOI2001 炮兵阵地 洛谷2704
    JLOI 2013 卡牌游戏 bzoj3191
    JLOI 2013 卡牌游戏 bzoj3191
    Noip 2012 day2t1 同余方程
    bzoj 1191 [HNOI2006]超级英雄Hero——二分图匹配
  • 原文地址:https://www.cnblogs.com/laolieren/p/complete_fabric_sdk_go_func.html
Copyright © 2011-2022 走看看