阅读提示
Fabric Gateway是Hyperledger fabric区块链网络的核心组件,用于代表客户应用程序来调用提交交易和查询账本状态等所需的操作。通过gateway,客户端应用程序只需要连接到fabric网络中的单个peer节点即可达到上述目的。
Fabric Gateway 是 Hyperledger Fabric v2.4 版中引入的一项服务,同以前的fabric go sdk,gateway为向 Fabric 网络提交交易提供了简化、最小化的 API与Hyperledger fabric区块链网络进行交互,简化应用程序开发。
一、引用gateway必要依赖
开发客户端应用程序过程中,需引入以下依赖,将gateway添加到项目中
github.com/hyperledger/fabric-gateway
二、兼容性
此API要求Fabric v2.4(或更高版本)具有启动网关的peer节点。如下文档中提供了其他兼容信息https://hyperledger.github.io/fabric-gateway/
当前最新详细版本要求表:
三、gateway概述与旧版本fabric-go-sdk对比
- 新版gateway中,客户端通过调用client.Connect(),传入带有客户端表示、客户端签名和客户端连接详细信息,来建立与fabric gateway的链接,返回的
*client.Gateway
对象可用于访问fabric中的智能合约或获取fabric区块链上信息。 - 旧版fabric-go-sdk中,Gateway对象是使用Connect()创建,用于使用存储在钱包中的身份连接到网络配置文件中指定的网关
peer
节点,然后在此网关链接的上下文中调用与智能合约交互。
fabric gate SDK是fabric-go-SDK和fabric编程模型的演变结果。总体来看,新API结构与功能和旧版SDK大致相同。
相似之处包括:
- 网关:连接fabric peer 节点, 提供对区块链网络的访问。
- 网络:托管共享账本(类似于通道channel)的节点的区块链网络。
- 合约:部署到区块链网络的智能合约。
- 提交交易:调用智能合约交易函数来更新分类账状态。
- 查询交易:调用智能合约交易函数来查询分类账状态。
- Chaincode事件:接收已提交事务发出的事件,以触发业务流。
- 阻止事件:接收提交到分类账的阻止事件。
- 事件检查点:保持当前事件位置以支持事件恢复。
用于连接网关实例,提交或查询交易的高级API部分与原来的部分几乎相同。
对于更高级的事务调用,例如那些涉及瞬态数据的调用,旧版本SDK在Contract对象上提供了createTransaction()方法,Fabric Gateway客户端API在Contract对象上提供了一个newProposal()方法来执行相同的功能。具体请参阅(gateway API文档)
主要差异:
- gRPC连接由应用程序管理,并且可以由网关实例共享。
- 不需要连接配置文件。
- 不再需要钱包(Wallet)机制,由客户端应用程序自身来选择如何管理凭据存储。
- 背书要求通常不再需要具体说明。
- 事件重新连接由客户端应用程序来控制。
gRPC相关优化
在旧版本SDK中,每个网关实例都维护到网络节点的内部gRPC连接,用于查询和提交交易以及获取事件。可以为每个网关实例创建许多gRPC连接,并且这些gRPC连接不会和其他网关实例共享。这会产生不小的开销,进而导致资源不足问题。
在新版本SDK中,每个网关实例的所有操作都使用到单个gRPC连接。网关实例的gRPC连接由客户端应用提供,并由多个网关实例共享,允许客户端程序主动控制gRPC连接配置和资源分配。
连接配置文件
新版本SDK中,不再使用通用连接配置文件。只需要fabric网关服务的端点地址,就可以建立gRPC连接。由于Fabric网关服务是由Fabric Peer提供的,因此端点地址可以是将在连接配置文件中定义的Peer地址之一。它也可以是负载均衡器或入口控制器的地址,用于将连接转发到网络Peer,从而提供高可用性。
钱包
传统SDK提供用于凭据管理的钱包。钱包有两个功能:
- 永久存储凭据
- 基于凭据类型的网关客户端配置
使用现在的Fabric网关客户端API,存储凭据的机制是客户端应用程序的一种选择。应用程序可以继续使用遗留SDK来访问存储在钱包中的凭证,或者可以使用不同的机制来存储和访问凭证。
要连接Gateway实例,应用程序只需提供一个Identity对象和一个签名实现。API中提供了帮助程序函数,用于从X.509证书创建Identity对象,还用于从私钥或HSM管理的身份创建签名实现。为了使用替代签名机制,应用程序可以提供自己的签名实现。
背书要求
在更复杂的场景中使用旧版本SDK时,比如涉及私有数据收集、链代码嵌套调用或基于密钥的背书策略的场景,客户端应用程序通常需要明确指定事务调用的背书要求。这可以采用指定链代码兴趣、认可组织或认可Peer节点的形式。
使用现在的Fabric网关客户端API,客户端应用程序通常不需要指定认可要求。Fabric Gateway服务动态地确定给定事务调用的认可要求,并使用最合适的Peer节点来获得认可。
对于包含瞬态数据的交易方案,有两种显著的场景确实需要应用程序明确指定可用于背书的组织:
- Fabric Gateway服务的组织无法认可事务建议。
- 对没有读取权限的私有数据集合执行盲写操作的事务。
事件重连
在事件侦听期间发生Peer节点或网络故障的情况下,遗留SDK会透明地尝试重新建立连接,并在成功重新连接后继续传递事件。现在的Fabric网关客户端API则在请求下一个事件时向客户端应用程序显示事件错误。要重新建立事件,应用程序必须启动一个具有适当起始位置的新事件侦听会话。
事件检查点跟踪当前事件位置,可用于在重新连接时在正确的开始位置恢复事件。
四、新版本代码举例
package main
import (
"fmt"
"os"
"github.com/hyperledger/fabric-gateway/pkg/client"
"github.com/hyperledger/fabric-gateway/pkg/identity"
"github.com/hyperledger/fabric-protos-go-apiv2/common"
"github.com/hyperledger/fabric-protos-go/peer"
"github.com/pkg/errors"
"go.starlark.net/lib/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func main() {
clientConnection, err := grpc.Dial("gateway.example.org:1337", grpc.WithTransportCredentials(insecure.NewCredentials()))
panicOnError(err)
defer clientConnection.Close()
id := NewIdentity()
sign := NewSign()
// Create a Gateway connection for a specific client identity.
gateway, err := client.Connect(id, client.WithSign(sign), client.WithClientConnection(clientConnection))
panicOnError(err)
defer gateway.Close()
// Obtain smart contract deployed on the network.
network := gateway.GetNetwork("channelName")
contract := network.GetContract("chaincodeName")
// Submit transactions that store state to the ledger.
submitResult, err := contract.SubmitTransaction("transactionName", "arg1", "arg2")
panicOnError(err)
fmt.Printf("Submit result: %s", string(submitResult))
// Evaluate transactions that query state from the ledger.
evaluateResult, err := contract.EvaluateTransaction("transactionName", "arg1", "arg2")
panicOnError(err)
fmt.Printf("Evaluate result: %s", string(evaluateResult))
}
/*
查询指定的peer节点加入了哪些通道
*/
func QueryChannels(network *client.Network) ([]string, error) {
chaincodeName := "cscc"
contract := network.GetContract(chaincodeName)
evaluateResult, err := contract.EvaluateTransaction("GetChannels")
if err != nil {
return nil, errors.WithMessage(err, "failed to query channels: %s")
}
cqr := peer.ChannelQueryResponse{}
err = proto.Unmarshal(evaluateResult, &cqr)
if err != nil {
return nil, errors.WithMessage(err, "failed to unmarshal: %s")
}
channels := make([]string, len(cqr.Channels))
for i := range cqr.Channels {
channels[i] = cqr.Channels[i].ChannelId
}
return channels, nil
}
/*
查询指定通道的区块高度
*/
func QueryBlockHeight(network *client.Network, channelName string) (uint64, error) {
BCI, err := QueryChainInfo(network, channelName)
if err != nil {
return ^uint64(0), err
}
return BCI.Height, nil
}
/*
查询指定通道的区块信息,包括高度、当前区块哈希、前一个区块哈希
*/
func QueryChainInfo(network *client.Network, channelName string) (*common.BlockchainInfo, error) {
chaincodeName := "qscc"
contract := network.GetContract(chaincodeName)
evaluateResult, err := contract.EvaluateTransaction("GetChainInfo", channelName)
if err != nil {
return nil, errors.WithMessage(err, "failed to get chainInfo: %s")
}
bci := common.BlockchainInfo{}
err = proto.Unmarshal(evaluateResult, &bci)
if err != nil {
return nil, errors.WithMessage(err, "failed to unmarshal: %s")
}
return &bci, nil
}
func NewIdentity() *identity.X509Identity {
certificatePEM, err := os.ReadFile("certificate.pem")
panicOnError(err)
certificate, err := identity.CertificateFromPEM(certificatePEM)
panicOnError(err)
id, err := identity.NewX509Identity("mspID", certificate)
panicOnError(err)
return id
}
func NewSign() identity.Sign {
privateKeyPEM, err := os.ReadFile("privateKey.pem")
panicOnError(err)
privateKey, err := identity.PrivateKeyToPEM(privateKeyPEM)
panicOnError(err)
sign, err := identity.NewPrivateKeySign(privateKey)
panicOnError(err)
return sign
}
func panicOnError(err error) {
if err != nil {
panic(err)
}
}
五 参考文章
最新版本gateway库: https://hyperledger.github.io/fabric-gateway/
最新版本gatewayAPI文档: https://pkg.go.dev/github.com/hyperledger/fabric-gateway/pkg/client
旧版本gatewayAPI文档:https://pkg.go.dev/github.com/hyperledger/fabric-gateway/pkg/client
迁移指南:https://hyperledger.github.io/fabric-gateway/migration