一、 编写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中的其他业务方法。
2号终端
3号终端
进入cli,安装chaincode
二、 升级chaincode
升级chaincode时,不能修改chaincode的name,只能修改chaincode的版本号。通过以下三个步骤即可完成gbivcc的版本升级:
1) 替换gbivcc.go的源码为新的chaincode代码
2) 安装和更新新版本的chaincode
3) 调用方法,验证结果
本文以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 |
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 |
进入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的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 |
在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号终端中,调用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<len(params); i+=2{
paramMap[params[i]]=params[i+1]
}
return paramMap
}
//Post data to lvwan center
//return str_response_body, error
func postData2LvwanCenter(url string, paramsMap map[string]string) (string,error){
//post的body内容,当前为json格式
// reqbody := `
// {
// "touser":"OPENID",
// "msgtype":"text",
// "text":
// {
// "content":"THECONTENT"
// }
// }
// `
reqbody,err := json.Marshal(paramsMap)
if err!=nil{
log.Println("paramsMap marshal failed", err)
return "",fmt.Errorf("paramsMap marshal failed,err: %s", err)
}
//创建请求
postReq, err := http.NewRequest("POST",url,strings.NewReader(string(reqbody)))
if err !=nil{
log.Println("Post请求:创建请求失败", err)
return "",fmt.Errorf("创建请求失败,err: %s", err)
}
//增加header
postReq.Header.Set("Content-type","application/json; encoding=utf-8")
//执行请求
client := &http.Client{}
resp, err := client.Do(postReq)
if err!=nil{
log.Println("Post请求:创建请求失败", err)
return "",fmt.Errorf("创建请求失败,err: %s", err)
}
//读取响应
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err!=nil{
log.Println("Post请求:读取body失败", err)
return "",fmt.Errorf("读取body失败,err: %s", err)
}
log.Println("Post请求: 创建成功", string(body))
return string(body), nil
}
//get lvwan center full url
func getVerifyUrl() string{
fullUrl :=fmt.Sprintf("%s/idcard/verify",LVWANCenterURL)
return fullUrl
}
//generate uuid
func getUUID() string{
r := rand.New(rand.NewSource(time.Now().UnixNano()))
return string(r.Intn(10000000))
}
//calculate hash
func getHash(args []string) string{
if len(args) == 0{
return ""
}
var joinStr = strings.Join(args,"")
var signByte = []byte(joinStr)
hash :=md5.New()
hash.Write(signByte)
return hex.EncodeToString(hash.Sum(nil))
}
// main function starts up the chaincode in the container during instantiate
func main() {
log.SetFlags(log.Lshortfile | log.LstdFlags)
if err := shim.Start(new(GBIdcardVerify)); err != nil{
fmt.Printf("Error starting GBIdcardVerify chaincode: %s", err)
}
}