Fabric官方文档:https://hyperledger-fabric.readthedocs.io/en/release-2.2/
API文档:https://pkg.go.dev/github.com/hyperledger/fabric-chaincode-go/shim#Chaincode
在本节中,完成了一个简单的fabric中GO链码的编写,实现数据存储和数据查看,并进行了测试。
编写链码主要分为如下几步:
接下来一步一步实现吧!
首先需要明确一点,单纯的链码开发并不需要额外的环境,它只需要GO语言开发环境的就可以直接进行开发和测试。而之前配置的fabric环境主要用于组件网络以及链码的部署,本节不做额外考虑。
让我们进入正题吧!首先,新开一个项目:
//新建目录
mkdir fabric_test
cd fabric_test
//创建编写代码的源文件
go mod init
touch test.go
然后在代码中导入必须的包,和一个必须的结构体:
package main
import (
"github.com/hyperledger/fabric-chaincode-go/shim"
"github.com/hyperledger/fabric-protos-go/peer"
)
// 作为 Chaincode shim 方法的接收者
type TestChaincode struct {
}
这两个包中,包含了fabric区块链的大部分API,通过调用API即可完成存储、查看等各种功能。而TestChaincode结构体是必须的(名字可以自己指定), 作为 Chaincode shim 方法的接收者。
查看官方文档可知,ChaIncode接口包含init方法和invoke方法,链码必须要实现这个接口,因此我们要实现Init和invoke函数。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JS4b1J0w-1676978416413)(https://gitee.com/yu88888/myimage/raw/master/image/image-20211225192909374.png)]
Init方法在初始化链码的时候调用,我们可以在这个方法里面写一些初始化需要的函数,注意,Init函数也是可以接受参数的,接下来写个简单的Init函数吧!
func (t *TestChaincode) Init(stub shim.ChaincodeStubInterface) peer.Response {
// 获取参数
args := stub.GetStringArgs()
if len(args) != 2 {
return shim.Error("Incorrect arguments. Expecting a key and a value")
}
// 通过调用stub.PutState()来存储参数
err := stub.PutState(args[0], []byte(args[1]))
if err != nil {
return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0]))
}
//返回结果
return shim.Success(nil)
}
在链码每次被调用的时候都会调用Invoke函数,在编写Invoke函数时,普遍的方法是先获取方法名和参数,然后根据方法名,来调用不同的业务方法,在本例中,有set方法和get方法。
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
//获取方法名字和参数
fn, args := stub.GetFunctionAndParameters()
var result string
var err error
if fn == "set" {
result, err = set(stub, args)
} else if fn == "get"{
result, err = get(stub, args)
}
if err != nil {
return shim.Error(err.Error())
}
return shim.Success([]byte(result))
}
本小节主要实现最简单的set和get函数。
// Set stores the asset (both key and value) on the ledger. If the key exists,
// it will override the value with the new one
func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
if len(args) != 2 {
return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")
}
err := stub.PutState(args[0], []byte(args[1]))
if err != nil {
return "", fmt.Errorf("Failed to set asset: %s", args[0])
}
return args[1], nil
}
// Get returns the value of the specified asset key
func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {
if len(args) != 1 {
return "", fmt.Errorf("Incorrect arguments. Expecting a key")
}
value, err := stub.GetState(args[0])
if err != nil {
return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err)
}
if value == nil {
return "", fmt.Errorf("Asset not found: %s", args[0])
}
return string(value), nil
}
最后还需要一个main函数。
// main function starts up the chaincode in the container during instantiate
func main() {
if err := shim.Start(new(SimpleAsset)); err != nil {
fmt.Printf("Error starting SimpleAsset chaincode: %s", err)
}
}
测试使用的是shimtest 包,其文档为https://pkg.go.dev/github.com/hyperledger/[email protected]/shimtest
单元测试不需要启动任何网络节点,通过测试文件就可以在本地对链码中的接口进行调用测试,其原理就是利用shimtest包中的API,新建一个MockStub类