Hyperledger Fabric ChainCode開發
預覽
Hyperledger Fabric的chaincode開發目前支援Go、Java、Node.js語言,下面以Go語言作為例子,我們先看下面的一個官方提供chaincode模板
···
package main import ( "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/protos/peer" "fmt" ) type SimpleAsset struct { } func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response { args := stub.GetStringArgs() if len(args) != 2 { return shim.Error("Incorrect arguments. Expecting a key and a value") } err := stub.PutState(args[0], []byte(args[1])) if err != nil { return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0])) } return shim.Success(nil) } func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response { fn, args := stub.GetFunctionAndParameters() var result string var err error if fn == "set" { result, err = set(stub, args) }else { result, err = get(stub, args) } if err != nil { return shim.Error(err.Error()) } return shim.Success([]byte(result)) } func set(stub shim.ChaincodeStubInterface, args []string) (string, error) { if len(args) != 2 { return "", fmt.Errorf("Incorrect arguments. Expecting a key and value") } err := stub.PutState(args[0], []byte(args[1])) if err != nil { return "", fmt.Errorf("Failed to set asset: %s", args[0]) } return args[1], nil } func get(stub shim.ChaincodeStubInterface, args []string) (string, error) { if len(args) != 1 { return "", fmt.Errorf("Incorrect arguments. Expecting a key") } value, err := stub.GetState(args[0]) if err != nil { return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err) } if value == nil { return "", fmt.Errorf("Asset not found: %s", args[0]) } return string(value), nil } func main() { if err := shim.Start(new(SimpleAsset)); err != nil { fmt.Printf("Error starting SimpleAsset chaincode: %s", err) } }
···
我們下面具體看看上面這段程式碼做了什麼。
開發chaincode時我們要實現ChainCode介面,ChainCode裡面有Init和Invoke這兩個函式。我們在前7行匯入了需要的依賴。接著定義了一個名為SimpleAsset的結構體,這個結構體實現了ChainCode介面。
初始化chaincode
接下來,我們實現Init方法
···
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response { args := stub.GetStringArgs() if len(args) != 2 { return shim.Error("Incorrect arguments. Expecting a key and a value") } err := stub.PutState(args[0], []byte(args[1])) if err != nil { return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0])) } return shim.Success(nil) }
···
該方法在chaincode例項化時被呼叫,可以根據實際需要做一些初始化資料的操作。需要注意的是在chaincode升級時這個Init方法也會被呼叫。
呼叫chaincode
接著,我們繼續看Invoke方法
···
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response { fn, args := stub.GetFunctionAndParameters() var result string var err error if fn == "set" { result, err = set(stub, args) }else { result, err = get(stub, args) } if err != nil { return shim.Error(err.Error()) } return shim.Success([]byte(result)) }
···
Invoke方法主要就是根據傳過來的具體引數呼叫chaincode的其他業務函式。如這裡,就是根據傳過來的引數fn,如果fn=="set"則呼叫下面的set函式來把資料儲存到區塊鏈上(若狀態資料庫的key存在則是修改操作)。如果fn=="get"則呼叫後面的get函式來獲取鏈上的資料。
shim.ChaincodeStubInterface
上面的chaincode模板中很多地方都用到了ChaincodeStubInterface這個介面,這個介面為我們編寫chaincode提供了大量的方法。在這裡只介紹幾個,想詳細瞭解,可查閱官方文件。
獲得呼叫引數
上面的chaincode模板中Init方法用到了GetStringArgs()函式,Invoke方法中用到了GetFunctionAndParameters()函式。這兩個函式都是用來解析傳入的引數的,除了這兩個函式外,ChaincodeStubInterface介面還提供了另外幾種函式。
- GetArgs() [][]byte 以byte陣列的陣列的形式獲得傳入的引數列表
- GetStringArgs() []string 以字串陣列的形式獲得傳入的引數列表
- GetFunctionAndParameters() (string, []string) 將字串陣列的引數分為兩部分,陣列第一個字是Function,剩下的都是Parameter
- GetArgsSlice() ([]byte, error) 以byte切片的形式獲得引數列表
增刪改查狀態資料庫(State Database)
- PutState(key string, value []byte) error 增改資料
由於狀態資料庫是個key-value資料庫,所以如果key存在就是修改操作,如果key不存在就是增加操作。value是經過JSON序列化後的字串
- GetState(key string) ([]byte, error) 根據key獲取狀態資料庫資料
- DelState(key string) error 根據key刪除狀態資料庫資料
參考連結
https://hyperledger-fabric.readthedocs.io/en/release-1.3/chaincode4ade.html
https://godoc.org/github.com/hyperledger/fabric/core/chaincode/shim#Chaincode
https://godoc.org/github.com/hyperledger/fabric/core/chaincode/shim#ChaincodeStubInterface