这是“网易云课堂IT技术快速入门学院”使用的素材。操作、配置文件和代码讲解视频在网易云课堂《HyperLeger Fabric进阶实战课》第一章中。
Hyperledger fabric的chaincode可以使用Go、Node.js、Java等语言开发。Chaincode将在Peer节点上以容器的方式运行,实现与背书节点进程之间的隔离。这里讲解一下怎样用Go语言开发Chaincode。
视频演示:
文字介绍:
问题汇总:
chaincode的代码结构大体如下,直接调用shim.Start()启动chaincode:
package main
import (
"github.com/hyperledger/fabric/core/chaincode/shim"
)
// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}
func main() {
err := shim.Start(new(SimpleChaincode))
if err != nil {
fmt.Printf("Error starting Simple chaincode: %s", err)
}
}
然后需要做的就是为SimpleChaincode实现一些接口,其中Init
和Invoke
是约定好的,必须有的。
首先增加一个Init方法,这个方法将在chaincode初始化的时候调用,用来初始化chaincode。
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
fmt.Println("ex02 Init")
_, args := stub.GetFunctionAndParameters()
var A, B string // Entities
var Aval, Bval int // Asset holdings
var err error
if len(args) != 4 {
return shim.Error("Incorrect number of arguments. Expecting 4")
}
// Initialize the chaincode
A = args[0]
Aval, err = strconv.Atoi(args[1])
if err != nil {
return shim.Error("Expecting integer value for asset holding")
}
B = args[2]
Bval, err = strconv.Atoi(args[3])
if err != nil {
return shim.Error("Expecting integer value for asset holding")
}
fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
// Write the state to the ledger
err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
if err != nil {
return shim.Error(err.Error())
}
err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(nil)
}
通过Invoke接口,调用请求将被转发到Invoke,然后可以在这里将请求转发给不通的函数处理:
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
fmt.Println("ex02 Invoke")
function, args := stub.GetFunctionAndParameters()
if function == "invoke" {
// Make payment of X units from A to B
return t.invoke(stub, args)
} else if function == "delete" {
// Deletes an entity from its state
return t.delete(stub, args)
} else 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\"")
}
在Init和Invoke方法中,都有一个stub参数,通过这个参数可以做很多操作,例如读取数据、写入数据、查看提案等。
接口在“github.com/hyperledger/fabric/core/chaincode/shim”中的ChaincodeStubInterface中定义。
ChaincodeStubInterface : interface
[methods]
+CreateCompositeKey(objectType string, attributes []string) : string, error
+DelState(key string) : error
+GetArgs() : [][]byte
+GetArgsSlice() : []byte, error
+GetBinding() : []byte, error
+GetChannelID() : string
+GetCreator() : []byte, error
+GetDecorations() : map[string][]byte
+GetFunctionAndParameters() : string, []string
+GetHistoryForKey(key string) : HistoryQueryIteratorInterface, error
+GetQueryResult(query string) : StateQueryIteratorInterface, error
+GetSignedProposal() : *pb.SignedProposal, error
+GetState(key string) : []byte, error
+GetStateByPartialCompositeKey(objectType string, keys []string) : StateQueryIteratorInterface, error
+GetStateByRange(startKey, endKey string) : StateQueryIteratorInterface, error
+GetStringArgs() : []string
+GetTransient() : map[string][]byte, error
+GetTxID() : string
+GetTxTimestamp() : *timestamp.Timestamp, error
+InvokeChaincode(chaincodeName string, args [][]byte, channel string) : pb.Response
+PutState(key string, value []byte) : error
+SetEvent(name string, payload []byte) : error
+SplitCompositeKey(compositeKey string) : string, []string, error
传入参数通过stub.GetFunctionAndParameters()
获取,得到的是一个数组,记录了所有传入参数。
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
function, args := stub.GetFunctionAndParameters()
..
if len(args) != 4 {
return shim.Error("Incorrect number of arguments. Expecting 4")
}
// Initialize the chaincode
A = args[0]
...
}
使用stub.PutState()
方法以key-value
的方式将数据写入账本:
//A:="a"
err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
if err != nil {
return shim.Error(err.Error())
}
//B:="a"
err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
if err != nil {
return shim.Error(err.Error())
}
使用stub.GetState()
方法查询区块:
//A:="a"
Avalbytes, err := stub.GetState(A)
if err != nil {
jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}"
return shim.Error(jsonResp)
}
使用stub.Success()
或者stub.Error()
将数据返回给调用者:
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,
}
}
合约的使用参考:《超级账本HyperLedger:Fabric的Chaincode(智能合约、链码)开发、使用演示》