hyperledger fabric chaincode开发示例

本文参考

http://hyperledger-fabric.readthedocs.io/en/latest/chaincode4ade.html


代码编写

要实现的接口

type Chaincode interface {
    // Init is called during Instantiate transaction after the chaincode container
    // has been established for the first time, allowing the chaincode to
    // initialize its internal data
    Init(stub ChaincodeStubInterface) pb.Response

    // Invoke is called to update or query the ledger in a proposal transaction.
    // Updated state variables are not committed to the ledger until the
    // transaction is committed.
    Invoke(stub ChaincodeStubInterface) pb.Response
}

Init方法在chaincode被创建或者升级交易的时候被调用,Invode方法在chaincode执行invoke动作时调用。

我们的类名叫SimpleAsset

type SimpleAsset struct {
}

然后我们写它的方法,

func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Reponse {

    //获取输入参数,检查有效性
    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)

}

GetStringArgs可以获取Init和Invoke传递的string类型的参数,以string数组的方式返回。我们的输入参数是个key-value数据,所以args[0]是key, args[1]是value

PutState会把key和value先放入fabric的写集,并不会马上更新账本。

func (stub *ChaincodeStub) PutState(key string, value []byte) error

shim.Success和shim.Error是两个公共函数,它们都返回peer.Reponse对象。

func Success(payload []byte) pb.Response {
    return pb.Response{
        Status:  OK,
        Payload: payload,
    }
}

func Error(msg string) pb.Response {
    return pb.Response{
        Status:  ERROR,
        Message: msg,
    }
}

接下来实现Invode方法

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 {
        result, err = get(stub, args)
    }

    if err != nil {
        return shim.Error(err.Error())
    }

    // Return the result as success payload
    return shim.Success([]byte(result))
}
/ GetFunctionAndParameters returns the first argument as the function
    // name and the rest of the arguments as parameters in a string array.
    // Only use GetFunctionAndParameters if the client passes arguments intended
    // to be used as strings.
    GetFunctionAndParameters() (string, []string)

Invode方法提供了执行的入口,但是具体干活的是set和get方法,来看看他们的实现。

//set可以把数据(key-value)存储在账本上
func set(stub shim.ChaincodeStubInterface, args []string) {

    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])
    }

    //返回值是新的value和nil
    return args[1], nil

}

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])
    }
    //以string格式返回对应key的value值
    return string(value), nil
}

代码比较简单,不多说。

测试

先写个main函数,

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

上面的代码在容器里启动chaincode。

环境依赖

主要是装一堆docker镜像,我已经安装好了:

hyperledger/fabric-ca          latest              17f38f1c8e80        3 weeks ago         238MB
hyperledger/fabric-ca          x86_64-1.0.3        17f38f1c8e80        3 weeks ago         238MB
hyperledger/fabric-tools       latest              ac1f4a1e58a6        3 weeks ago         1.33GB
hyperledger/fabric-tools       x86_64-1.0.3        ac1f4a1e58a6        3 weeks ago         1.33GB
hyperledger/fabric-couchdb     latest              b2188fa55138        3 weeks ago         1.47GB
hyperledger/fabric-couchdb     x86_64-1.0.3        b2188fa55138        3 weeks ago         1.47GB
hyperledger/fabric-kafka       latest              9e2a425c9dd6        3 weeks ago         1.29GB
hyperledger/fabric-kafka       x86_64-1.0.3        9e2a425c9dd6        3 weeks ago         1.29GB
hyperledger/fabric-zookeeper   latest              3b50cfad9af3        3 weeks ago         1.3GB
hyperledger/fabric-zookeeper   x86_64-1.0.3        3b50cfad9af3        3 weeks ago         1.3GB
hyperledger/fabric-orderer     latest              fd1055ee597a        3 weeks ago         151MB
hyperledger/fabric-orderer     x86_64-1.0.3        fd1055ee597a        3 weeks ago         151MB
hyperledger/fabric-peer        latest              b7f253e87c0c        3 weeks ago         154MB
hyperledger/fabric-peer        x86_64-1.0.3        b7f253e87c0c        3 weeks ago         154MB
hyperledger/fabric-javaenv     latest              1d778fcc14c0        3 weeks ago         1.41GB
hyperledger/fabric-javaenv     x86_64-1.0.3        1d778fcc14c0        3 weeks ago         1.41GB
hyperledger/fabric-ccenv       latest              2e5898d8b21b        3 weeks ago         1.28GB
hyperledger/fabric-ccenv       x86_64-1.0.3        2e5898d8b21b        3 weeks ago         1.28GB
hyperledger/fabric-ca          x86_64-1.0.1        5f30bda5f7ee        2 months ago        238MB

这里不描述具体过程了。可以参考如下网址:

http://hyperledger-fabric.readthedocs.io/en/latest/samples.html

不过要注意文档里有一处我实际测试不行,要做修改。

curl -sSL https://goo.gl/Q3YRTi | bash

改为

curl -sSL https://raw.githubusercontent.com/hyperledger/fabric/master/scripts/bootstrap-1.0.3.sh | bash

编译chaincode

go get -u --tags nopkcs11 github.com/hyperledger/fabric/core/chaincode/shim
go build --tags nopkcs11

go get可以根据要求和实际情况从互联网上下载或更新指定的代码包及其依赖包,并对它们进行编译和安装。

go build命令主要用于测试编译。在包的编译过程中,若有必要,会同时编译与之相关联的包。

成功执行后,在sacc目录下会生成一个同名sacc执行文件。

开发者模式下测试

生产环境的的fabric部署,测试是相当麻烦的,不过好在fabric给我们提供了开发者模式进行测试。

首先我们启动三个终端,全部切换到目录:

$GOPATH/src/github.com/hyperledger/fabric-samples/chaincode-docker-devmode

Terminal 1 - Start the network

docker-compose -f docker-compose-simple.yaml up

第一个终端启动了fabric整个网络环境,包括peer和orderer,另外还启动了两个额外的docker容器,一个是chaincode环境,一个是CLI,用来和chaincode进行交互。

启动后我们可以通过docker ps命令查看,会看到四个容器:

              NAMES
a0d8ca1db66d        hyperledger/fabric-ccenv     "/bin/bash -c 'sle..."   6 minutes ago       Up 6 minutes                                                         chaincode
a75d777aab06        hyperledger/fabric-tools     "/bin/bash -c ./sc..."   6 minutes ago       Up 6 minutes                                                         cli
5ed1678b9feb        hyperledger/fabric-peer      "peer node start -..."   6 minutes ago       Up 6 minutes        0.0.0.0:7051->7051/tcp, 0.0.0.0:7053->7053/tcp   peer
2b665817027e        hyperledger/fabric-orderer   "orderer"                6 minutes ago       Up 6 minutes        0.0.0.0:7050->7050/tcp                           orderer

Terminal 2 - Build & start the chaincode

进入chaincode容器,

docker exec -it chaincode bash
root@d2629980e76b:/opt/gopath/src/chaincode#

cd sacc
go build

运行chaincode,

CORE_PEER_ADDRESS=peer:7051 CORE_CHAINCODE_ID_NAME=mycc:0 ./sacc

Terminal 3 - Use the chaincode

启动一个交互容器来测试我们的chaincode

docker exec -it cli bash

下面两条指令把我们的chaincode上链。

root@76d41baa160b:/opt/gopath/src/chaincodedev# peer chaincode install -p chaincodedev/chaincode/sacc -n mycc -v 0

root@76d41baa160b:/opt/gopath/src/chaincodedev# peer chaincode instantiate -n mycc -v 0 -c '{"Args":["a","10"]}' -C myc

日志虽然很长,我就不贴出来了。

[“a”,”10”]就是我们的参数key-value

先来查询下a的值

root@76d41baa160b:/opt/gopath/src/chaincodedev# peer chaincode query -n mycc -c '{"Args":["query","a"]}' -C myc
2017-10-31 08:25:45.249 UTC [msp] GetLocalMSP -> DEBU 001 Returning existing local MSP
2017-10-31 08:25:45.249 UTC [msp] GetDefaultSigningIdentity -> DEBU 002 Obtaining default signing identity
2017-10-31 08:25:45.249 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 003 Using default escc
2017-10-31 08:25:45.249 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 004 Using default vscc
2017-10-31 08:25:45.250 UTC [msp/identity] Sign -> DEBU 005 Sign: plaintext: 0AA8080A6008031A0B0889E7E0CF0510...6D7963631A0A0A0571756572790A0161 
2017-10-31 08:25:45.250 UTC [msp/identity] Sign -> DEBU 006 Sign: digest: BEE337BA8316D79449B12B892A1B1FD0EFAA3678B1474CF42CF96B6952C655DA 
Query Result: 10

结果是10,正确。

然后修改下a的值,

root@76d41baa160b:/opt/gopath/src/chaincodedev# peer chaincode invoke -n mycc -c '{"Args":["set", "a", "20"]}' -C myc

再次查询发现a的值确实变为20了。日志我就不贴了。

退出

Terminal2和Terminal3用exit命令退出docker。

Terminal 1直接ctrl+c退出,然后再执行

docker-compose -f docker-compose-simple.yaml down

你可能感兴趣的:(区块链技术)