什么是chaincode?

Chaincode是一段能让你和网络中共享的账本进行交互的代码。每当你在网络上进行交易,都是在调用chaincode的函数来读取或写入账本。

实现chaincode的接口

第一,要在你的golang代码中实现chaincode shim接口。有三个主要的函数:Init,Invoke和Query。这三个函数都有相同的原型,他们都需要一个被命名为function的字符串参数和一个字符串数组参数。他们之间的不同之处在于被调用的时机。

依赖

import语句列出了几个需要的依赖:

  • fmt - 包含Println用于调试和记录日志。
  • errors - 标准go错误格式。
  • github.com/hyperledger/fabric/core/chaincode/shim - 和peer交互的代码。

Init()

Init会在第一次发布chaincode时被调用。正如函数名的含义,Init函数应该被用于chaincode初始化。在我们的例子中,我们用Init配置了账本上一个变量的初始状态。

现在来修改chaincode.go。修改Init函数,把args参数的第一个元素的存储到键”hello_world”上。

func (t *SimpleChaincode) Init(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {
    if len(args) != 1 {
        return nil, errors.New("Incorrect number of arguments. Expecting 1")
    }

    err := stub.PutState("hello_world", []byte(args[0]))
    if err != nil {
        return nil, err
    }

    return nil, nil
}

通过shim的stub.PutState函数来完成,stub.PutState的第一个参数是字符串类型的键,第二个参数是byte数组类型的值。这个函数可能会返回一个错误,我们的代码里做了检查,如果错误出现了,就把这个错误返回。

Invoke()

要用chaincode函数真正开始工作,那就该调用Invoke函数了。交易调用会成为链上的块。Invoke的结构很简单。它接收一个被命名为function的字符串参数,并基于这个参数调用chaincode的Go函数。

在你的chaincode.go文件中,修改Invoke函数,调用write方法。

func (t *SimpleChaincode) Invoke(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {
    fmt.Println("invoke is running " + function)

    // Handle different functions
    if function == "init" {
        return t.Init(stub, "init", args)
    } else if function == "write" {
        return t.write(stub, args)
    }
    fmt.Println("invoke did not find func: " + function)

    return nil, errors.New("Received unknown function invocation")
}

现在在chaincode.go中写下write函数:

func (t *SimpleChaincode) write(stub *shim.ChaincodeStub, args []string) ([]byte, error) {
    var name, value string
    var err error
    fmt.Println("running write()")

    if len(args) != 2 {
        return nil, errors.New("Incorrect number of arguments. Expecting 2. name of the variable and value to set")
    }

    name = args[0]                            //rename for funsies
    value = args[1]
    err = stub.PutState(name, []byte(value))  //write the variable into the chaincode state
    if err != nil {
        return nil, err
    }
    return nil, nil
}

write函数应该和上面改过的Init函数很像。一个主要的不同在于现在你可以自己设置PutState的key和value了。PutState会将key/value存储到区块链账本中。

Query()

正如这个名字的含义,Query会在查询chaincode状态时被调用。Query不会导致区块被添加到链中。你将使用Query读取chaincode中的值。

在chaincode.go中,修改Query函数,调用read函数:

func (t *SimpleChaincode) Query(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) {
    fmt.Println("query is running " + function)

    // Handle different functions
    if function == "read" {                            //read a variable
        return t.read(stub, args)
    }
    fmt.Println("query did not find func: " + function)

    return nil, errors.New("Received unknown function query")
}

现在来在chaincode.go中写下read函数:

func (t *SimpleChaincode) read(stub *shim.ChaincodeStub, args []string) ([]byte, error) {
    var name, jsonResp string
    var err error

    if len(args) != 1 {
        return nil, errors.New("Incorrect number of arguments. Expecting name of the var to query")
    }

    name = args[0]
    valAsbytes, err := stub.GetState(name)
    if err != nil {
        jsonResp = "{\"Error\":\"Failed to get state for " + name + "\"}"
        return nil, errors.New(jsonResp)
    }

    return valAsbytes, nil
}

read函数使用了PutState的补充方法GetState。这个shim的方法只有一个参数,这个参数是要查询的key。这个函数会返回一个byte数组。

Main()

最后,创建一个main函数,在为这个chaincode部署每个peer实例的时候,这个main方法就会被调用。这个main方法启动了chaincode,并把自己注册到peer中。拷贝下面的代码就行了,不需要修改:

func main() {
    err := shim.Start(new(SimpleChaincode))
    if err != nil {
        fmt.Printf("Error starting Simple chaincode: %s", err)
    }
}