美文网首页
fabric golang链码示例

fabric golang链码示例

作者: 链播学院 | 来源:发表于2019-03-22 08:59 被阅读0次

    0 导言

    智能合约是区块链中一个非常重要的概念和组成部分。在Fabric中内成为Chaincode,中文翻译为链码。涉及到链码地方都是 Chaincode.

    本示例是一个资产交易的示例

    主要实现如下的功能:

    • 初始化 A、B 两个账户,并为两个账户赋初始资产值;
    • 在 A、B 两个账户之间进行资产交易;
    • 分别查询 A、B 两个账户上的余额,确认交易成功;
    • 删除账户。
    • 新增账户

    主要函数

    • Init:初始化 A、B 两个账户;
    • Invoke:调用其它函数的入口;
    • transfer:实现 A、B 账户间的转账;
    • query:查询 A、B 账户上的余额;
    • delete:删除账户。
    • create: 新增账户

    1 创建项目目录

    $ cd $GOPATH/src/github.com
    $ mkdir -p fabric-study/chaincode-study1
    

    注:如果上述mkdir 不能执行,可能是没有权限,加上sudo就可以了

    $ sudo mkdir chaincode-study1
    

    2 搭建运行网络

    我们不另行去搭建节点网络了,直接拷贝官网提供的chaincode-docker-devmode过来用,执行cp命令进行拷贝

    $ cd fabric-study/chaincode-study1/
    $ cp -r ../../hyperledger/fabric-samples/chaincode-docker-devmode ./
    

    3 创建golang链码放置目录

    $ mkdir -p chaincode/go
    

    4 用开发工具goland打开chaincode-study1目录

    Snipaste_2019-03-22_07-29-47.png

    5 新建go文件example.go

    Snipaste_2019-03-22_07-30-32.png

    6 创建结构体

    package main
    
    import (
        "fmt"
        "strconv"
    
        "github.com/hyperledger/fabric/core/chaincode/shim"
        "github.com/hyperledger/fabric/protos/peer"
    )
    
    type SimpleChaincode struct {
    }
    

    7 创建Init函数

    /**
    链码实例化,对应peer chaincode instantiate
     */
    func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) peer.Response {
        fmt.Println("链码实例例化")
        _, args := stub.GetFunctionAndParameters()
        var A, B string    // Entities
        var Aval, Bval int // Asset holdings
        var err error
    
        if len(args) != 4 {
            return shim.Error("Incorrect number of arguments. Expecting 4")
        }
    
        // Initialize the chaincode
        A = args[0]
        Aval, err = strconv.Atoi(args[1])
        if err != nil {
            return shim.Error("Expecting integer value for asset holding")
        }
        B = args[2]
        Bval, err = strconv.Atoi(args[3])
        if err != nil {
            return shim.Error("Expecting integer value for asset holding")
        }
        fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
    
        // Write the state to the ledger
        err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
        if err != nil {
            return shim.Error(err.Error())
        }
    
        err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
        if err != nil {
            return shim.Error(err.Error())
        }
    
        return shim.Success(nil)
    }
    
    

    8 创建Invoke函数

    /**
    调用链码,对应peer chaincode invoke
     */
    func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
        fmt.Println("链码调用")
        function, args := stub.GetFunctionAndParameters()
        if function == "transfer" {
            // 从A账户转移资产给B账户
            return t.transfer(stub, args)
        } else if function == "delete" {
            // 删除某个账户
            return t.delete(stub, args)
        } else if function == "query" {
            //查询某个账户的资产
            return t.query(stub, args)
        } else if function == "create" {
            //创建一个账户
            return t.create(stub, args)
        }
    
        return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\" \"create\"")
    }
    

    9 创建transfer函数

    // 从A账户转移资产给B账户
    func (t *SimpleChaincode) transfer(stub shim.ChaincodeStubInterface, args []string) peer.Response {
        var A, B string    // Entities
        var Aval, Bval int // Asset holdings
        var X int          // Transaction value
        var err error
    
        if len(args) != 3 {
            return shim.Error("Incorrect number of arguments. Expecting 3")
        }
    
        A = args[0]
        B = args[1]
    
        // Get the state from the ledger
        // TODO: will be nice to have a GetAllState call to ledger
        Avalbytes, err := stub.GetState(A)
        if err != nil {
            return shim.Error("Failed to get state")
        }
        if Avalbytes == nil {
            return shim.Error("Entity not found")
        }
        Aval, _ = strconv.Atoi(string(Avalbytes))
    
        Bvalbytes, err := stub.GetState(B)
        if err != nil {
            return shim.Error("Failed to get state")
        }
        if Bvalbytes == nil {
            return shim.Error("Entity not found")
        }
        Bval, _ = strconv.Atoi(string(Bvalbytes))
    
        // Perform the execution
        X, err = strconv.Atoi(args[2])
        if err != nil {
            return shim.Error("Invalid transaction amount, expecting a integer value")
        }
        
        if Aval < X {
            return shim.Error("Not enough balance")
        }
        
        Aval = Aval - X
        Bval = Bval + X
        fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
    
        // Write the state back to the ledger
        err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
        if err != nil {
            return shim.Error(err.Error())
        }
    
        err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
        if err != nil {
            return shim.Error(err.Error())
        }
    
        return shim.Success(nil)
    }
    

    10 创建delete函数

    // 删除某个账户实体
    func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) peer.Response {
        if len(args) != 1 {
            return shim.Error("Incorrect number of arguments. Expecting 1")
        }
    
        A := args[0]
    
        // Delete the key from the state in ledger
        err := stub.DelState(A)
        if err != nil {
            return shim.Error("Failed to delete state")
        }
    
        return shim.Success(nil)
    }
    

    11 创建query函数

    // 查询账户的资产,对应peer chaincode query
    func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) peer.Response {
        var A string // Entities
        var err error
    
        if len(args) != 1 {
            return shim.Error("Incorrect number of arguments. Expecting name of the person to query")
        }
    
        A = args[0]
    
        // 从账本中获取状态
        Avalbytes, err := stub.GetState(A)
        if err != nil {
            jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}"
            return shim.Error(jsonResp)
        }
    
        if Avalbytes == nil {
            jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}"
            return shim.Error(jsonResp)
        }
    
        jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}"
        fmt.Printf("Query Response:%s\n", jsonResp)
        return shim.Success(Avalbytes)
    }
    

    12 创建create函数

    
    //创建账户
    func (t *SimpleChaincode) create(stub shim.ChaincodeStubInterface, args []string) peer.Response {
        var A string   // Entities
        var Aval int // Asset holdings
        var err error
    
        if len(args) != 2 {
            return shim.Error("Incorrect number of arguments. Expecting 2")
        }
    
        // Initialize the chaincode
        A = args[0]
        Aval, err = strconv.Atoi(args[1])
        if err != nil {
            return shim.Error("Expecting integer value for asset holding")
        }
        
        fmt.Printf("Aval = %d\n", Aval)
    
        // 写入状态到账本
        err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
        if err != nil {
            return shim.Error(err.Error())
        }
    
        return shim.Success(nil)
    }
    

    13 启动节点网络

    打开第1个终端

    先停掉和删除已有的容器

    $ docker stop $(docker ps -aq)
    $ docker rm $(docker ps -aq)
    

    执行下面的命令启动节点网络

    $ cd chaincode-docker-devmode
    $ docker-compose -f docker-compose-simple.yaml up
    

    14 编译和执行

    打开第2个终端

    进入chaincode这个容器

    $ docker exec -it chaincode bash
    

    cd到chaincode/go目录下

    $ cd go
    

    编译

    $ go build example.go 
    

    运行

    $ CORE_PEER_ADDRESS=peer:7052 CORE_CHAINCODE_ID_NAME=mycc:0 ./example
    

    15 安装和实例化链码

    打开第3个终端

    进入cli这个容器

    $ docker exec -it cli bash
    

    安装链码

    $ peer chaincode install -p chaincodedev/chaincode/go -n mycc -v 0
    

    实例化链码

    $ peer chaincode instantiate -n mycc -v 0 -c '{"Args":["init","tom","100","bob","200"]}' -C myc
    

    16 执行新建,查询,转账,删除等函数

    还是在 第3个终端

    新建账户实体

    $ peer chaincode invoke -n mycc -c '{"Args":["create","lily","150"]}' -C myc
    

    查询

    $ peer chaincode query -n mycc -c '{"Args":["query","lily"]}' -C myc
    $ peer chaincode query -n mycc -c '{"Args":["query","tom"]}' -C myc
    $ peer chaincode query -n mycc -c '{"Args":["query","bob"]}' -C myc
    

    转账

    bob给lily转账30

    $ peer chaincode invoke -n mycc -c '{"Args":["transfer","bob","lily","30"]}' -C myc
    

    转账后再查询

    $ peer chaincode query -n mycc -c '{"Args":["query","lily"]}' -C myc
    $ peer chaincode query -n mycc -c '{"Args":["query","bob"]}' -C myc
    

    删除tom账户实体

    $ peer chaincode invoke -n mycc -c '{"Args":["delete","tom"]}' -C myc
    

    17 Chaincode 说明

    Fabric中的Chaincode包含了一个Chaincode代码和Chaincode管理命令这两部分。
    Chaincode 代码是业务的承载体,负责具体的业务逻辑
    Chaincode 管理命令负责 Chaincode的部署,安装,维护等工作

    17.1 Chaincode代码

    Fabric的Chaincode是一段运行在容器中的程序。Chaincode是客户端程序和Fabric之间的桥梁。通过Chaincode客户端程序可以发起交易,查询交易。Chaincode是运行在Dokcer容器中,因此相对来说安全。

    目前支持 java,node,go。

    17.2 Chaincode的管理命令

    Chaincode管理命令主要用来对Chaincode进行安装,实例化,调用,打包,签名操作。Chaincode命令包含在Peer模块中,是peer模块中一个子命令. 可通过执行peer chaincode --help查看如何使用。

    peer chaincode --help
    
    Operate a chaincode: install|instantiate|invoke|package|query|signpackage|upgrade|list.
    
    Usage:
      peer chaincode [command]
    
    Available Commands:
      install     Package the specified chaincode into a deployment spec and save it on the peer's path.
      instantiate Deploy the specified chaincode to the network.
      invoke      Invoke the specified chaincode.
      list        Get the instantiated chaincodes on a channel or installed chaincodes on a peer.
      package     Package the specified chaincode into a deployment spec.
      query       Query using the specified chaincode.
      signpackage Sign the specified chaincode package
      upgrade     Upgrade chaincode.
    
    

    17.3 chaincode的生命周期

    fabric 提供了4种命令去管理chaincode生命周期:package、install、instantiate、upgrade。将来发布的版本的会增加stop以及start。

    transaction用于停止与开启chaincode,而不用去卸载chaincode。chaincode在成功install以及instantiate之后,chaincode则是运行状态,能够通过invoke transaction来处理交易。后续也能够对chaincode进行升级。


    Snipaste_2019-03-22_06-53-26.png

    17.4 fabric的链码结构

    每个链码都需要实现一下 Chaincode 接口

    Chaincode接口

    type Chaincode interface {
        Init(stub shim.ChaincodeStubInterface) peer.Response
        Invoke(stub shim.ChaincodeStubInterface) peer.Response
    }
    

    Init: 当链码实例化或者升级的时候,Init方法会被调用

    Invoke 当链码收到调用(invoke)或者查询的时候,Invoke 会被调用

    链码的基本结构

    package main
    
    // 引入相应的依赖包
    import (
        "fmt"
        "github.com/hyperledger/fabric/core/chaincode/shim"
        "github.com/hyperledger/fabric/protos/peer"
    )
    
    type SimpleChaincode struct {
    
    }
    
    // 链码实例化(instantiate)或 升级(upgrade)时调用 Init 方法
    func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) peer.Response{
    
        return shim.Success(nil)
    }
    
    // 链码收到调用(invoke) 或 查询 (query)时调用 Invoke 方法
    func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
        return shim.Success(nil)
    }
    
    // 主函数 ,调用 shim.Start 方法
    func main() {
        err := shim.Start(new(SimpleChaincode))
    
        if( err!= nil){
            fmt.Printf("Error starting Simple Chaincode is %s \n",err)
        }
    }
    

    相关文章

      网友评论

          本文标题:fabric golang链码示例

          本文链接:https://www.haomeiwen.com/subject/vpikvqtx.html