按理说,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关键字入手,自然而然的我们找到了这么个方法。

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

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
}

令人懊恼的阉割版fabric sdk功能缺失的更多相关文章

  1. Windows下fabric sdk连接Linux上fabric网络的调试过程

    上个月刚入职一家公司从事区块链研发工作,选型采用Hyperledger Fabric作为开发平台.团队的小组成员全部采用的是在VirtualBox上面安装桌面版的Ubuntu 16.04虚拟机,开发工 ...

  2. HyperLedger/Fabric SDK使用Docker容器镜像快速部署上线

    HyperLedger/Fabric SDK Docker Image 该项目在github上的地址是:https://github.com/aberic/fabric-sdk-container ( ...

  3. [Android Pro] 开发一流的 Android SDK:Fabric SDK 的创建经验

    cp from : https://academy.realm.io/cn/posts/oredev-ty-smith-building-android-sdks-fabric/ Ty Smith T ...

  4. OD调试6—使未注册版软件的功能得以实现

    OD调试6—使未注册版软件的功能得以实现 本节使用的软件下载链接 (想动手试验的朋友可以下载来试试) 继续开始我OD调试教程的学习笔记. 本次试验对真正的程序进行逆向.(之前的都是为破解而专门设计的小 ...

  5. JQuery向导插件Step——第一个阉割版插件

    如果使用过JQuery Steps的朋友一定会发现这个插件有一个缺点,就是页面在第一次进入的时候,会进行一次很明显的DOM重绘--页面会闪一下. 尤其是前端代码比较庞大的时候,效果更为明显. 为了解决 ...

  6. 编译原理作业(第一次)-完成retinf.c(阉割版)

    首先,作业要求概括如下: 根据前缀表达式文法,实现statements() 和expression() 两个函数. 并且要求使得语义分析在完成分析前缀表达式并输出中间代码的同时,也能够将前缀表达式翻译 ...

  7. WordPress版微信小程序3.1.5版的新功能

    产品的完善是无止境,每过段时间就会发现产品的新问题,使用的人越多,提的需求也会越多,我听得最多的一句话就是:如果加上某某功能就完美了.其实,完美是不存在的,每个人的视角不一样,完美的定义也是不一样的. ...

  8. Java的Spring内实现的mini版内存"计数器"功能

    工期紧急,不让用Redis,自己实现了一个Spring内的mini版内存"计数器"功能,很简陋,和业务耦合太紧密,需要改进. public Long getCreationCoun ...

  9. 阉割版BBBlack安装Debian

    开门见山,直入主题 咸鱼入手3块阉割ARM板,经过快递近6天运输到手,不过价格便宜 东西下面这样的(借了咸鱼的图): 发现这块板是阉割版的国外beagleboard.org型号为BeagleBone ...

随机推荐

  1. ORACLE_TO_CHAR Function

    TECHONTHENNTE  WEBSITE: https://www.techonthenet.com/oracle/functions/to_char.php Oracle / PLSQL: TO ...

  2. ZT linux 线程私有数据之 一键多值技术

    这个原作者的这个地方写错了 且他举的例子非常不好.最后有我的修正版本 pthread_setspecific(key, (void *)&my_errno); linux 线程私有数据之一键多 ...

  3. 科普文:从人人网看网络科学(Network Science)的X个经典问题

    转:https://zr9558.wordpress.com/2013/12/05/科普文:从人人网看网络科学(network-science)的x个经典问/ 长文,写了N个小时写完的.你肯定能看懂, ...

  4. win10 x64 注册ZQDeviceOcx.ocx控件

    正常的方式在32位系统下可行, 但是在64位系统下是不可行的. 在64位系统中正确的注册步骤是: 1. 将对应的ocx和dll放到C:\Windows\SysWOW64目录下. 2. 然后找到C:\W ...

  5. Hadoop-2.2.0中文文档—— Common - 超级用户模拟别的用户

    简单介绍 此文档描写叙述了一个超级用户怎样在安全的方式下以还有一用户的名义提交作业或訪问hdfs. Use Case 下一部分描写叙述的的代码演示样例对此用户用例是可用的. 一个username为's ...

  6. C/C++心得-结构体

    先说句题外话,个人认为,基本上所有的高级语言被设计出来的最终目的是降低软件开发难度,提升软件开发人员素质和团队协作能力,降低软件维护的难度.在学习语言的时候,可以从这么方面来推测各种语言语法设计的原因 ...

  7. 十七、IntelliJ IDEA 中的 Maven 项目初体验及搭建 Spring MVC 框架

    我们已经将 IntelliJ IDEA 中的 Maven 项目的框架搭建完成.接着上文,在本文中,我们更近一步,利用 Tomcat 运行我们的 Web 项目. 如上图所示,我们进一步扩展了项目的结构, ...

  8. windows下如何安装pip以及如何查看pip是否已经安装成功?

    最近刚学习python,发现很多关于安装以及查看pip是否安装成的例子都比较老,不太适合于现在(python 3.6 )因此,下一个入门级别的教程. 0:首先如何安装python我就不做介绍了. 1: ...

  9. vue 方法相互调用注意事项与详解

    vue在同一个组件内: methods中的一个方法调用methods中的另外一个方法: 可以直接这样调用:this.$options.methods.test(); this.$options.met ...

  10. 大数据框架-Zookeeper

    Hadoop的分布式协同服务,让分布式系统碰到失败时候,能够正确处理此类问题.基础功能:master选举,数据同步.Zooleeper集群有Client和Server(leader和follower) ...