返回

Hyperledger Fabric 链码编写(四)

写在前面

本文粗略介绍如何编写链码、链码的整体结构以及链码的API,以官方示例代码讲解

链码整体结构

链码开发者教程 — hyperledger-fabricdocs master 文档

  1. 写链码之前要先导入两个包:shimpeer,所有用到的API都在这两个包里
  2. 链码的开头一定要定义一个 SimpleAsset 结构体来作为Chaincode shim方法的接收者,这个结构体里面可以什么也不写,我们只是在后面函数调用一下它
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package main

import (
    "fmt"

    "github.com/hyperledger/fabric-chaincode-go/shim"
    "github.com/hyperledger/fabric-protos-go/peer"
)

// SimpleAsset implements a simple chaincode to manage an asset
type SimpleAsset struct {
}
  1. 实现链码初始化方法Init,使用 ChaincodeStubInterface.GetStringArgs 方法获取 Init 调用的参数,并且检查其合法性。在我们的用例中,我们希望得到一个键-值对。
  2. 确定了调用是合法的,我们将把初始状态存入账本中。我们将调用 ChaincodeStubInterface.PutState 并将键和值作为参数传递给它。假设一切正常,将返回一个peer.Response对象,表明初始化成功。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
  // Get the args from the transaction proposal
  args := stub.GetStringArgs()
  if len(args) != 2 {
    return shim.Error("Incorrect arguments. Expecting a key and a value")
  }
  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)
}
  1. 增加一个 Invoke 函数,Invoke 函数的参数是将要调用的链码应用程序的函数名。在我们的用例中,我们的应用程序将有两个方法: setget ,用来设置或者获取资产当前的状态。先调用 ChaincodeStubInterface.GetFunctionAndParameters 解析方法名和参数
  2. 然后,我们将验证函数名是否为 set 或者 get ,并执行链码应用程序的方法,通过 shim.Successshim.Error 返回一个适当的响应,这个响应将被序列化为 gRPC protobuf 消息。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
    // Extract the function and args from the transaction proposal
    fn, args := stub.GetFunctionAndParameters()

    var result string
    var err error
    if fn == "set" {
        result, err = set(stub, args)
    } else {
        result, err = get(stub, args)
    }
    if err != nil {
        return shim.Error(err.Error())
    }

    // Return the result as success payload
    return shim.Success([]byte(result))
}
  1. 实现Invoke中调用的两个方法setget,要访问账本状态,我们需要使用链码 shim API 中的 ChaincodeStubInterface.PutStateChaincodeStubInterface.GetState 方法
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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
}
  1. 最后,我们增加一个 main 方法,它将调用 shim.Start 方法。
1
2
3
4
5
func main() {
    if err := shim.Start(new(SimpleAsset)); err != nil {
        fmt.Printf("Error starting SimpleAsset chaincode: %s", err)
    }
}
  1. Go 链码需要 Go 标准库之外的一些依赖包(比如链码 shim)。当链码安装到 peer 的时候,这些包的源码必须被包含在链码包中。如果将链码构造为一个模块,最简单的方法就是在打包链码之前使用 go mod vendor 来 “vendor” 依赖
1
2
go mod tidy
go mod vendor

这就把链码的扩展依赖放进了本地的 vendor 目录。

当依赖都引入到链码目录后, peer chaincode packagepeer chaincode install 操作将把这些依赖一起放入链码包中

Built with Hugo
Theme Stack designed by Jimmy