fabric golang鏈碼示例

0 導(dǎo)言

智能合約是區(qū)塊鏈中一個非常重要的概念和組成部分。在Fabric中內(nèi)成為Chaincode,中文翻譯為鏈碼。涉及到鏈碼地方都是 Chaincode.

本示例是一個資產(chǎn)交易的示例

主要實現(xiàn)如下的功能:

  • 初始化 A、B 兩個賬戶,并為兩個賬戶賦初始資產(chǎn)值;
  • 在 A、B 兩個賬戶之間進(jìn)行資產(chǎn)交易;
  • 分別查詢 A、B 兩個賬戶上的余額,確認(rèn)交易成功;
  • 刪除賬戶。
  • 新增賬戶

主要函數(shù)

  • Init:初始化 A、B 兩個賬戶;
  • Invoke:調(diào)用其它函數(shù)的入口;
  • transfer:實現(xiàn) A、B 賬戶間的轉(zhuǎn)賬;
  • query:查詢 A、B 賬戶上的余額;
  • delete:刪除賬戶。
  • create: 新增賬戶

1 創(chuàng)建項目目錄

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

注:如果上述mkdir 不能執(zhí)行,可能是沒有權(quán)限,加上sudo就可以了

$ sudo mkdir chaincode-study1

2 搭建運行網(wǎng)絡(luò)

我們不另行去搭建節(jié)點網(wǎng)絡(luò)了,直接拷貝官網(wǎng)提供的chaincode-docker-devmode過來用,執(zhí)行cp命令進(jìn)行拷貝

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

3 創(chuàng)建golang鏈碼放置目錄

$ mkdir -p chaincode/go

4 用開發(fā)工具goland打開chaincode-study1目錄

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

5 新建go文件example.go

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

6 創(chuàng)建結(jié)構(gòu)體

package main

import (
    "fmt"
    "strconv"

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

type SimpleChaincode struct {
}

7 創(chuàng)建Init函數(shù)

/**
鏈碼實例化,對應(yīng)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 創(chuàng)建Invoke函數(shù)

/**
調(diào)用鏈碼,對應(yīng)peer chaincode invoke
 */
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
    fmt.Println("鏈碼調(diào)用")
    function, args := stub.GetFunctionAndParameters()
    if function == "transfer" {
        // 從A賬戶轉(zhuǎn)移資產(chǎn)給B賬戶
        return t.transfer(stub, args)
    } else if function == "delete" {
        // 刪除某個賬戶
        return t.delete(stub, args)
    } else if function == "query" {
        //查詢某個賬戶的資產(chǎn)
        return t.query(stub, args)
    } else if function == "create" {
        //創(chuàng)建一個賬戶
        return t.create(stub, args)
    }

    return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\" \"create\"")
}

9 創(chuàng)建transfer函數(shù)

// 從A賬戶轉(zhuǎn)移資產(chǎn)給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 創(chuàng)建delete函數(shù)

// 刪除某個賬戶實體
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 創(chuàng)建query函數(shù)

// 查詢賬戶的資產(chǎn),對應(yīng)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]

    // 從賬本中獲取狀態(tài)
    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 創(chuàng)建create函數(shù)


//創(chuàng)建賬戶
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)

    // 寫入狀態(tài)到賬本
    err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
    if err != nil {
        return shim.Error(err.Error())
    }

    return shim.Success(nil)
}

13 啟動節(jié)點網(wǎng)絡(luò)

打開第1個終端

先停掉和刪除已有的容器

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

執(zhí)行下面的命令啟動節(jié)點網(wǎng)絡(luò)

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

14 編譯和執(zhí)行

打開第2個終端

進(jìn)入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個終端

進(jìn)入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 執(zhí)行新建,查詢,轉(zhuǎn)賬,刪除等函數(shù)

還是在 第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

轉(zhuǎn)賬

bob給lily轉(zhuǎn)賬30

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

轉(zhuǎn)賬后再查詢

$ 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 代碼是業(yè)務(wù)的承載體,負(fù)責(zé)具體的業(yè)務(wù)邏輯
Chaincode 管理命令負(fù)責(zé) Chaincode的部署,安裝,維護(hù)等工作

17.1 Chaincode代碼

Fabric的Chaincode是一段運行在容器中的程序。Chaincode是客戶端程序和Fabric之間的橋梁。通過Chaincode客戶端程序可以發(fā)起交易,查詢交易。Chaincode是運行在Dokcer容器中,因此相對來說安全。

目前支持 java,node,go。

17.2 Chaincode的管理命令

Chaincode管理命令主要用來對Chaincode進(jìn)行安裝,實例化,調(diào)用,打包,簽名操作。Chaincode命令包含在Peer模塊中,是peer模塊中一個子命令. 可通過執(zhí)行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。將來發(fā)布的版本的會增加stop以及start。

transaction用于停止與開啟chaincode,而不用去卸載chaincode。chaincode在成功install以及instantiate之后,chaincode則是運行狀態(tài),能夠通過invoke transaction來處理交易。后續(xù)也能夠?qū)haincode進(jìn)行升級。


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

17.4 fabric的鏈碼結(jié)構(gòu)

每個鏈碼都需要實現(xiàn)一下 Chaincode 接口

Chaincode接口

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

Init: 當(dāng)鏈碼實例化或者升級的時候,Init方法會被調(diào)用

Invoke 當(dāng)鏈碼收到調(diào)用(invoke)或者查詢的時候,Invoke 會被調(diào)用

鏈碼的基本結(jié)構(gòu)

package main

// 引入相應(yīng)的依賴包
import (
    "fmt"
    "github.com/hyperledger/fabric/core/chaincode/shim"
    "github.com/hyperledger/fabric/protos/peer"
)

type SimpleChaincode struct {

}

// 鏈碼實例化(instantiate)或 升級(upgrade)時調(diào)用 Init 方法
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) peer.Response{

    return shim.Success(nil)
}

// 鏈碼收到調(diào)用(invoke) 或 查詢 (query)時調(diào)用 Invoke 方法
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
    return shim.Success(nil)
}

// 主函數(shù) ,調(diào)用 shim.Start 方法
func main() {
    err := shim.Start(new(SimpleChaincode))

    if( err!= nil){
        fmt.Printf("Error starting Simple Chaincode is %s \n",err)
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容