前面的文章实现了如何搭建自己的区块链网络,使用命令行或SDK创建通道、安装部署链码。链码调用有两种方式:通道客户端调用和fabric-gateway调用。以官方示例sacc链码为例,需要初始化的链码必须先初始化,代码参考上篇。
1、通过通道客户端调用
使用通道客户端调用链码函数为同步调用,响应时间与区块生成时间大致相同。
package main
import (
"fmt"
"github.com/hyperledger/fabric-sdk-go/pkg/client/channel"
"github.com/hyperledger/fabric-sdk-go/pkg/core/config"
"github.com/hyperledger/fabric-sdk-go/pkg/fabsdk"
"log"
"os"
)
func main() {
// 通道名称
var channelID = "mychannel"
// 组织名称
var orgName = "Org1"
// 组织用户
var orgUser = "User1"
// 链码名称
var ChaincodeID = "sacc"
// 初始化sdk
sdk, err := fabsdk.New(config.FromFile("./sdkInit/config.yaml"))
if err != nil {
log.Fatalf("初始化fabric sdk失败,error:%v", err)
}
// 实例化通道客户端
clientChannelContext := sdk.ChannelContext(channelID, fabsdk.WithUser(orgUser), fabsdk.WithOrg(orgName))
// Channel client is used to query and execute transactions (Org1 is default org)
client, err := channel.New(clientChannelContext)
if err != nil {
log.Fatalf("通道客户端创建失败: %s", err)
os.Exit(-1)
}
// 调用链码 set 函数, 设置a的值为100
req := channel.Request{ChaincodeID: ChaincodeID, Fcn: "set", Args: [][]byte{[]byte("a"), []byte("100")}}
respone, err := client.Execute(req)
// 打印交易ID
fmt.Println("set Txid:", respone.TransactionID)
// 调用链码 get 函数,查询a的值
req1 := channel.Request{ChaincodeID: ChaincodeID, Fcn: "get", Args: [][]byte{[]byte("a")}}
respone1, err := client.Query(req1)
fmt.Println("a的值:", string(respone1.Payload))
}
通过fabric-gateway调用
fabric-gateway调用链码有以下几个方法:
- SubmitSync:将事务提交到账本,并在成功发送到后立即返回结果,此方法为异步提交,提交成功后数据并没有立即保存到账本
- SubmitTransaction:将交易提交到账本,并仅在将其提交到账本后返回结果,同步提交
- EvaluateTransaction:查询指定数据,key不存在时报错
package main
import (
"crypto/x509"
"fmt"
"github.com/hyperledger/fabric-gateway/pkg/client"
"github.com/hyperledger/fabric-gateway/pkg/identity"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"io/ioutil"
"log"
"path"
"time"
)
var (
// 组织的MSPID
mspID = "Org1MSP"
// 中间变量
cryptoPath = "./fixtures/crypto-config/peerOrganizations"
// client 用户的签名证书
certPath = "/org1.maakees.com/users/User1@org1.maakees.com/msp/signcerts/User1@org1.maakees.com-cert.pem"
// client 用户的私钥路径
keyPath = "/org1.maakees.com/users/User1@org1.maakees.com/msp/keystore/"
// client 用户的 tls 通信证书
tlsCertPath = "/org1.maakees.com/users/User1@org1.maakees.com/msp/tlscacerts/tlsca.org1.maakees.com-cert.pem"
// 所连 peer 节点的地址
peerEndpoint = "localhost:7051"
// 网关 peer 节点名称
gatewayPeer = "peer0.org1.maakees.com"
// 连接的通道
channelName = "mychannel"
// 链码名称
chaincodeName = "sacc"
)
func main() {
clientConnection := newGrpcConnection()
defer clientConnection.Close()
id := newIdentity()
sign := newSign()
gateway, err := client.Connect(
id,
client.WithSign(sign),
client.WithClientConnection(clientConnection),
client.WithEvaluateTimeout(5*time.Second),
client.WithEndorseTimeout(15*time.Second),
client.WithSubmitTimeout(5*time.Second),
client.WithCommitStatusTimeout(1*time.Minute),
)
if err != nil {
log.Fatalf("client.Connect error: %s", err)
}
defer gateway.Close()
network := gateway.GetNetwork(channelName)
sacc := network.GetContract(chaincodeName)
// 调用链码 set 函数,设置b的值为1000
_, err = sacc.SubmitTransaction("set", "b", "1000")
if err != nil {
log.Fatalln(err)
}
// 调用链码 get 函数,查询b的值
res1, err := sacc.EvaluateTransaction("get", "b")
if err != nil {
log.Fatalln(err)
}
fmt.Println(string(res1))
}
// 创建指gRPC连接.
func newGrpcConnection() *grpc.ClientConn {
certificate, err := loadCertificate(cryptoPath + tlsCertPath)
if err != nil {
panic(err)
}
certPool := x509.NewCertPool()
certPool.AddCert(certificate)
transportCredentials := credentials.NewClientTLSFromCert(certPool, gatewayPeer)
connection, err := grpc.Dial(peerEndpoint, grpc.WithTransportCredentials(transportCredentials))
if err != nil {
panic(fmt.Errorf("failed to create gRPC connection: %w", err))
}
return connection
}
// 创建客户端标识。
func newIdentity() *identity.X509Identity {
certificate, err := loadCertificate(cryptoPath + certPath)
if err != nil {
panic(err)
}
id, err := identity.NewX509Identity(mspID, certificate)
if err != nil {
panic(err)
}
return id
}
// 加载证书文件
func loadCertificate(filename string) (*x509.Certificate, error) {
certificatePEM, err := ioutil.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("failed to read certificate file: %w", err)
}
return identity.CertificateFromPEM(certificatePEM)
}
// 使用私钥生成签名
func newSign() identity.Sign {
files, err := ioutil.ReadDir(cryptoPath + keyPath)
if err != nil {
panic(fmt.Errorf("failed to read private key directory: %w", err))
}
privateKeyPEM, err := ioutil.ReadFile(path.Join(cryptoPath+keyPath, files[0].Name()))
if err != nil {
panic(fmt.Errorf("failed to read private key file: %w", err))
}
privateKey, err := identity.PrivateKeyFromPEM(privateKeyPEM)
if err != nil {
panic(err)
}
sign, err := identity.NewPrivateKeySign(privateKey)
if err != nil {
panic(err)
}
return sign
}