查看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。