超级账本chaincode编写与升级

一、 编写chaincode
    本文以fabric-samples工程为例,在该示例的基础上编译chaincode,该工程可以从github上下载。Fabric-samples的启动参考“chaincode开发手册”,fabric-samples会启动4个docker容器,名称如下:peer、chaincode、cli、orderer。
编写chaincode需要实现hyperledger/fabric/core/chaincode/shim/interfaces_stable.go文件中的Init和Invoke方法(接口Chaincode中),在init和invoke方法中,传递的参数为ChaincodeStubInterface接口,该接口也定义在interfaces_stable.go文件中。通过invoke方法为入口,实现调用自定义chaincode中的其他业务方法。

    下面的gbivcc.go的chaincode,分别实现了方法:Init,Invoke,verify,storeResult,getResult方法。通过invoke方法,可以分别调用verify,storeResult,getResult方法。Gbivcc的代码见文章最后,下面按照启动fabric-samples的终端个数来说明如何安装chaincode。

1号终端

    启动fabric-samples示例

docker-compose -f docker-compose-simple.yaml up
2号终端

    docker exec –it chaincode bash

root@d2629980e76b:/opt/gopath/src/chaincode#
mkdir gbivcc && cd gbivcc
touch gbivcc.go (拷贝文件末尾的chaincode到该文件中)
go build gbivcc.go
运行chaincode
CORE_PEER_ADDRESS=peer:7052 CORE_CHAINCODE_ID_NAME=gbivcc:0 ./gbivcc
3号终端
    进入cli,安装chaincode

    docker exec –it cli bash

安装chaincode
peer chaincode install -p chaincodedev/chaincode/gb_chaincode/ -n gbivcc -v 0 
peer chaincode instantiate -n gbivcc -v 0 -c '{"Args":[]}' -C myc 
调用getResult方法
peer chaincode invoke -n gbivcc -c '{"Args":["getResult", "request_id"]}' -C myc
二、 升级chaincode
升级chaincode时,不能修改chaincode的name,只能修改chaincode的版本号。通过以下三个步骤即可完成gbivcc的版本升级:
     1) 替换gbivcc.go的源码为新的chaincode代码

在2号终端中,修改gbivcc.go的源码为新版本的chaincode代码,或者新添加一个文件也可以。修改完执行如下操作编译chaincode代码,如意命令行中的红色字体,代表版本号的变化(0→1)。

vim gbivcc.go 
go build gbivcc.go 
CORE_PEER_ADDRESS=peer:7052 CORE_CHAINCODE_ID_NAME=gbivcc:1 ./gbivcc
    2) 安装和更新新版本的chaincode

在3号终端中,安装并升级新的chaincode,注意命令中的红色字体部分,代表版本号的变化(0→1)。

peer chaincode install -p chaincodedev/chaincode/gb_chaincode/ -n gbivcc -v 1 
peer chaincode upgrade -n gbivcc -v 1 -c '{"Args":[]}' -C myc 
    3) 调用方法,验证结果

在3号终端中,调用chaincode中的方法,验证方法是否运行正常。调用方法时没有版本号的修改。

peer chaincode invoke -n gbivcc -c '{"Args":["getResult", "request_id"]}' -C myc 
peer chaincode invoke -n gbivcc -c '{"Args":["storeResult", "request_id", "verify_result"]}' -C myc 

三、 gbivcc.go的chaincode完整代码

    下载地址:gbivcc.go

package main

import (
	"encoding/json"
	"encoding/hex"
	"crypto/md5"
	"strings"
    "fmt"
    "math/rand"
    "time"
    "net/http"
    "io/ioutil"

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


const (
    //LVWANCenterURL = "http://127.0.0.1:8080"
    LVWANCenterURL = "http://www.baidu.com"
    REQUEST_PARAMS_HASH = "params_hash"
    REQUEST_VERIFY_RESULT = "verify_result"
    SUCCESS = "success"
    FAILURE = "failure"
)

//GBIdcardVerify implements a simple chaincode to manage an idcard verify
type GBIdcardVerify struct{
}

// Init is called during chaincode instantiation to initialize any
// data. Note that chaincode upgrade also calls this function to reset
// or to migrate data.

func (t *GBIdcardVerify) Init(stub shim.ChaincodeStubInterface) peer.Response{
    // Get the args from the transaction proposal
    args := stub.GetStringArgs()
    log.SetFlags(log.Lshortfile | log.LstdFlags)
    log.Printf("Init receive args: %s\n", args)
    return shim.Success(nil)
}

// Invoke is called per transaction on the chaincode. Each transaction is
// either a query or verify  on a varify idcard created by Init function.
func(t * GBIdcardVerify) Invoke(stub shim.ChaincodeStubInterface) peer.Response{
    fn, args := stub.GetFunctionAndParameters()
    var result string
    var err error
    log.Printf("fn: %s, args: %s", fn, args)
    //1.validate right

    //2.execute function
    if fn == "verify"{
        result, err = verify(stub, args)
    }else if fn =="storeResult"{
        result, err = storeResult(stub, args)
    }else if fn=="getResult"{
        result, err = getResult(stub,args)
    }else{
        desc :=[]string{"没有实现函数:", fn}
        shim.Error(strings.Join(desc, ""))
    }

    //3.judge error
    if err !=nil{
        return shim.Error(fmt.Sprintf("function executer error. %s , err: %s", fn, err))
    }

    // Return the result as success payload
    return shim.Success([]byte(result))
}

//Post parameters to lvwan data center.
//return request_id,error
func verify(stub shim.ChaincodeStubInterface, args []string) (string, error){
    request_id := stub.GetTxID()
    args = append(args,"request_id", request_id)
    log.Println("verify receive args: %s", args)

    //1.hypteledger
    var stateMap map[string]interface{}
    stateMap = make(map[string]interface{})
    stateMap["REQUEST_PARAMS_HASH"] = getHash(args)
    marshaledData, err := json.Marshal(stateMap)
    if err !=nil{
        return request_id,fmt.Errorf("json marshal failed.request_id: %s, err: %s", request_id, err)
    }
    err = stub.PutState(request_id, marshaledData)
    if err !=nil{
        return request_id, fmt.Errorf("put state failed. request_id: %s, err: %s", request_id, err)
    }

    //2.do request
    verifyUrl := getVerifyUrl()
    log.Printf("verifyUrl: %s", verifyUrl)
    paramsMap :=converArrayToMap(args)
    _, err = postData2LvwanCenter(verifyUrl, paramsMap)
    if err !=nil{
        return request_id, fmt.Errorf("post data to lvwan center failed. request_id: %s, err: %s", request_id, err)
    }
    return request_id, nil
}

//store task result to state
//return store_result, error
func storeResult(stub shim.ChaincodeStubInterface, args []string) (string,error){
    //1.get request_id
    if len(args)<2{
        return FAILURE,fmt.Errorf("require request_id param.")
    }
    request_id := args[0]
    byteValue, err := stub.GetState(request_id)
    log.Printf("store result, receive request_id: %s, byteValue: %s, new result: %s", request_id, byteValue, args[1])
    if err !=nil{
        return FAILURE, fmt.Errorf("get state failed. request_id: %s, err: %s", request_id, err)
    }
    var r map[string]interface{}
    r = make(map[string]interface{})
    json.Unmarshal(byteValue, &r)
    r[REQUEST_VERIFY_RESULT] = args[1]

    //store result
    marshaledData, err := json.Marshal(r)
    if err !=nil{
        return request_id,fmt.Errorf("store result,json marshal failed.request_id: %s, err: %s", request_id, err)
    }
    err = stub.PutState(request_id, marshaledData)
    if err !=nil{
        return request_id, fmt.Errorf("store result,put state failed. request_id: %s, err: %s", request_id, err)
    }

    return SUCCESS, nil
}

//get task result by request_id,return result, error
func getResult(stub shim.ChaincodeStubInterface, args []string)(string, error){
    //1.get request_id
    log.Printf("getResult receive args: %s\n", args)
    log.Printf("stub.GetTxID is %s\n", stub.GetTxID())
    if len(args)<1{
        return "",fmt.Errorf("require request_id param.")
    }
    request_id := args[0]
    byteValue, err := stub.GetState(request_id)
    log.Printf("receive request_id: %s", request_id)
    log.Printf("getSate result, byteValue: %s", byteValue)
    log.Printf("getSate result, err: %s", err)
    if err !=nil{
        return "", fmt.Errorf("get state failed. request_id: %s, err: %s", request_id, err)
    }
    if byteValue == nil{
        return "",nil
    }
    var r map[string]interface{}
    json.Unmarshal(byteValue, &r)
    fmt.Printf("result map: %s\n", r)
    if r[REQUEST_VERIFY_RESULT] ==nil{
        return "",nil
    }
    return (r[REQUEST_VERIFY_RESULT]).(string), nil
}

//conver string[] to map
func converArrayToMap(params []string) map[string]string{
	if params ==nil || len(params) <=0{
		return nil
	}

	if len(params)%2 !=0{
		log.Fatalf("length of params is invalid. length: %d", len(params))
	}
	var paramMap map[string]string
	paramMap = make(map[string]string)
	for i:=0; i

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