查看chaincode
閱讀scripts/script.sh
和scripts/util.sh
可以發現,對于這個fabric網絡來說,執行chaincode的方式是,在cli容器中,執行peer命令。
比如,查詢a的值:
$ docker exec cli peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'
90
查看peer的help可見:
Available Commands:
chaincode Operate a chaincode: install|instantiate|invoke|package|query|signpackage|upgrade|list.
channel Operate a channel: create|fetch|join|list|update|signconfigtx|getinfo.
help Help about any command
logging Log levels: getlevel|setlevel|revertlevels.
node Operate a peer node: start|status.
version Print fabric peer version.
peer工具可以執行對chaincode的各種操作。(還可以操作channel/node/logging level)
可以看下當前installed的chaincode:
$ docker exec cli peer chaincode list --installed
Get installed chaincodes on peer:
Name: mycc, Version: 1.0, Path: github.com/chaincode/chaincode_example02/go/, Id: 333a19b11063d0ade7be691f9f22c04ad369baba15660f7ae9511fd1a6488209
這里我們知道,之前執行的chaincode mycc到底是什么了。
查看這個文件(只留下query函數):
package main
import (
"fmt"
"strconv"
"github.com/hyperledger/fabric/core/chaincode/shim"
pb "github.com/hyperledger/fabric/protos/peer"
)
// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
......
}
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
fmt.Println("ex02 Invoke")
function, args := stub.GetFunctionAndParameters()
if function == "query" {
// the old "Query" is now implemtned in invoke
return t.query(stub, args)
}
return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"")
}
// query callback representing the query of a chaincode
func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.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]
// Get the state from the ledger
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)
}
func main() {
err := shim.Start(new(SimpleChaincode))
if err != nil {
fmt.Printf("Error starting Simple chaincode: %s", err)
}
}
從整體來看,一個chaincode的寫法就是:
-
type A struct {}
。 - 實現
func (a *A) Init(stub shim.ChaincodeStubInterface) pb.Response
方法。 - 實現
func (a *A) Invoke(stub shim.ChaincodeStubInterface) pb.Response
方法。內部分發工作到其他功能函數。 - 實現功能函數。形式為
func (a *A) fn(stub shim.ChaincodeStubInterface, args []string) pb.Response
。 -
shim.Start(new(SimpleChaincode))
。
其中github.com/hyperledger/fabric/core/chaincode/shim是一個重要的輔助包,貫穿chaincode編寫的各方面。
再看query函數,實際上就是做了一件事:stub.GetState(args[0])
,即獲取"a"的state。
同文件中的invoke方法,則是令A-X,B+X,可以認為是A賬戶到B賬戶轉移X值。
這個chaincode的nodejs版本在這里。因為語言特性,寫起來更簡單一些。
現在,我們可以修改這個chaincode,再重新啟動整個網絡,查看不同的結果了。
動態增加chaincode
同樣閱讀scripts/script.sh
和scripts/util.sh
可以發現,注冊chaincode的方法是:
peer chaincode install -n mycc -v ${VERSION} -l ${LANGUAGE} -p ${CC_SRC_PATH}
需要注意的是,由于chaincode實質上是一個類,所以調用其方法前,需要先用peer instantiate
實例化它。
開發fabric項目的通用方法
從之前的分析我們了解到,開發一個fabric項目,最主要的是兩個步驟:
- 構造一個合理的fabric network。
- 根據業務編寫chaincode,并應用于network。