天天看點

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) != {
        return shim.Error("Incorrect arguments. expecting a key and a value")
    }

    err := stub.PutState(args], []byte(args]))
    if err != nil {
        return shim.Error(fmt.Sprintf("Failed to create asset: %s", args]))
    }

    return shim.Success(nil)

}
           

GetStringArgs可以擷取Init和Invoke傳遞的string類型的參數,以string數組的方式傳回。我們的輸入參數是個key-value資料,是以args[0]是key, args[1]是value

PutState會把key和value先放入fabric的寫集,并不會馬上更新賬本。

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) != {

        return "", fmt.Errorf("Incorrect arguments, expecting a key and a value")

    }

    err := stub.PutState(args], []byte(args]))

    if err != nil {
        return "", fmt.Errorf("Failed to set asset: %s", args])
    }

    //傳回值是新的value和nil
    return args], nil

}

func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {
    if len(args) != {
        return "", fmt.Errorf("Incorrect arguments. Expecting a key")
    }

    value, err := stub.GetState(args])
    if err != nil {
        return "", fmt.Errorf("Failed to get asset: %s with error: %s", args], err)
    }
    if value == nil {
        return "", fmt.Errorf("Asset not found: %s", args])
    }
    //以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              f38f1c8e80         weeks ago         MB
hyperledger/fabric-ca          x86_64-        f38f1c8e80         weeks ago         MB
hyperledger/fabric-tools       latest              ac1f4a1e58a6         weeks ago         GB
hyperledger/fabric-tools       x86_64-        ac1f4a1e58a6         weeks ago         GB
hyperledger/fabric-couchdb     latest              b2188fa55138         weeks ago         GB
hyperledger/fabric-couchdb     x86_64-        b2188fa55138         weeks ago         GB
hyperledger/fabric-kafka       latest              a425c9dd6         weeks ago         GB
hyperledger/fabric-kafka       x86_64-        a425c9dd6         weeks ago         GB
hyperledger/fabric-zookeeper   latest              b50cfad9af3         weeks ago         GB
hyperledger/fabric-zookeeper   x86_64-        b50cfad9af3         weeks ago         GB
hyperledger/fabric-orderer     latest              fd1055ee597a         weeks ago         MB
hyperledger/fabric-orderer     x86_64-        fd1055ee597a         weeks ago         MB
hyperledger/fabric-peer        latest              b7f253e87c0c         weeks ago         MB
hyperledger/fabric-peer        x86_64-        b7f253e87c0c         weeks ago         MB
hyperledger/fabric-javaenv     latest              d778fcc14c0         weeks ago         GB
hyperledger/fabric-javaenv     x86_64-        d778fcc14c0         weeks ago         GB
hyperledger/fabric-ccenv       latest              d8b21b         weeks ago         GB
hyperledger/fabric-ccenv       x86_64-        d8b21b         weeks ago         GB
hyperledger/fabric-ca          x86_64-        f30bda5f7ee         months ago        MB
           

這裡不描述具體過程了。可以參考如下網址:

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

不過要注意文檔裡有一處我實際測試不行,要做修改。

改為

curl -sSL https://raw.githubusercontent.com/hyperledger/fabric/master/scripts/bootstrap-..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..."    minutes ago       Up  minutes                                                         chaincode
a75d777aab06        hyperledger/fabric-tools     "/bin/bash -c ./sc..."    minutes ago       Up  minutes                                                         cli
ed1678b9feb        hyperledger/fabric-peer      "peer node start -..."    minutes ago       Up  minutes        :->/tcp, :->/tcp   peer
b665817027e        hyperledger/fabric-orderer   "orderer"                 minutes ago       Up  minutes        :->/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,

Terminal 3 - Use the chaincode

啟動一個互動容器來測試我們的chaincode

docker exec -it cli bash
           

下面兩條指令把我們的chaincode上鍊。

日志雖然很長,我就不貼出來了。

[“a”,”10”]就是我們的參數key-value

先來查詢下a的值

root@d41baa160b:/opt/gopath/src/chaincodedev# peer chaincode query -n mycc -c '{"Args":["query","a"]}' -C myc
-- :: UTC [msp] GetLocalMSP -> DEBU  Returning existing local MSP
-- :: UTC [msp] GetDefaultSigningIdentity -> DEBU  Obtaining default signing identity
-- :: UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO  Using default escc
-- :: UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO  Using default vscc
-- :: UTC [msp/identity] Sign -> DEBU  Sign: plaintext: AA8080A6008031A0B0889E7E0CF0510..D7963631A0A0A0571756572790A0161 
-- :: UTC [msp/identity] Sign -> DEBU  Sign: digest: BEE337BA8316D79449B12B892A1B1FD0EFAA3678B1474CF42CF96B6952C655DA 
Query Result: 
           

結果是10,正确。

然後修改下a的值,

再次查詢發現a的值确實變為20了。日志我就不貼了。

退出

Terminal2和Terminal3用exit指令退出docker。

Terminal 1直接ctrl+c退出,然後再執行

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

繼續閱讀