本文參考
http://hyperledger-fabric.readthedocs.io/en/latest/chaincode4ade.html
代碼編寫
要實作的接口
type Chaincode interface {
// Init is called during Instantiate transaction after the chaincode container
// has been established for the first time, allowing the chaincode to
// initialize its internal data
Init(stub ChaincodeStubInterface) pb.Response
// Invoke is called to update or query the ledger in a proposal transaction.
// Updated state variables are not committed to the ledger until the
// transaction is committed.
Invoke(stub ChaincodeStubInterface) pb.Response
}
Init方法在chaincode被建立或者更新交易的時候被調用,Invode方法在chaincode執行invoke動作時調用。
我們的類名叫SimpleAsset
type SimpleAsset struct {
}
然後我們寫它的方法,
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Reponse {
//擷取輸入參數,檢查有效性
args := stub.GetStringArgs()
if len(args) != {
return shim.Error("Incorrect arguments. expecting a key and a value")
}
err := stub.PutState(args], []byte(args]))
if err != nil {
return shim.Error(fmt.Sprintf("Failed to create asset: %s", args]))
}
return shim.Success(nil)
}
GetStringArgs可以擷取Init和Invoke傳遞的string類型的參數,以string數組的方式傳回。我們的輸入參數是個key-value資料,是以args[0]是key, args[1]是value
PutState會把key和value先放入fabric的寫集,并不會馬上更新賬本。
shim.Success和shim.Error是兩個公共函數,它們都傳回peer.Reponse對象。
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,
}
}
接下來實作Invode方法
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 the result as success payload
return shim.Success([]byte(result))
}
/ GetFunctionAndParameters returns the first argument as the function
// name and the rest of the arguments as parameters in a string array.
// Only use GetFunctionAndParameters if the client passes arguments intended
// to be used as strings.
GetFunctionAndParameters() (string, []string)
Invode方法提供了執行的入口,但是具體幹活的是set和get方法,來看看他們的實作。
//set可以把資料(key-value)存儲在賬本上
func set(stub shim.ChaincodeStubInterface, args []string) {
if len(args) != {
return "", fmt.Errorf("Incorrect arguments, expecting a key and a value")
}
err := stub.PutState(args], []byte(args]))
if err != nil {
return "", fmt.Errorf("Failed to set asset: %s", args])
}
//傳回值是新的value和nil
return args], nil
}
func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {
if len(args) != {
return "", fmt.Errorf("Incorrect arguments. Expecting a key")
}
value, err := stub.GetState(args])
if err != nil {
return "", fmt.Errorf("Failed to get asset: %s with error: %s", args], err)
}
if value == nil {
return "", fmt.Errorf("Asset not found: %s", args])
}
//以string格式傳回對應key的value值
return string(value), nil
}
代碼比較簡單,不多說。
測試
先寫個main函數,
func main() {
if err := shim.Start(new(SimpleAsset)); err != nil {
fmt.Printf("Error starting SimpleAsset chaincode: %s", err)
}
}
上面的代碼在容器裡啟動chaincode。
環境依賴
主要是裝一堆docker鏡像,我已經安裝好了:
hyperledger/fabric-ca latest f38f1c8e80 weeks ago MB
hyperledger/fabric-ca x86_64- f38f1c8e80 weeks ago MB
hyperledger/fabric-tools latest ac1f4a1e58a6 weeks ago GB
hyperledger/fabric-tools x86_64- ac1f4a1e58a6 weeks ago GB
hyperledger/fabric-couchdb latest b2188fa55138 weeks ago GB
hyperledger/fabric-couchdb x86_64- b2188fa55138 weeks ago GB
hyperledger/fabric-kafka latest a425c9dd6 weeks ago GB
hyperledger/fabric-kafka x86_64- a425c9dd6 weeks ago GB
hyperledger/fabric-zookeeper latest b50cfad9af3 weeks ago GB
hyperledger/fabric-zookeeper x86_64- b50cfad9af3 weeks ago GB
hyperledger/fabric-orderer latest fd1055ee597a weeks ago MB
hyperledger/fabric-orderer x86_64- fd1055ee597a weeks ago MB
hyperledger/fabric-peer latest b7f253e87c0c weeks ago MB
hyperledger/fabric-peer x86_64- b7f253e87c0c weeks ago MB
hyperledger/fabric-javaenv latest d778fcc14c0 weeks ago GB
hyperledger/fabric-javaenv x86_64- d778fcc14c0 weeks ago GB
hyperledger/fabric-ccenv latest d8b21b weeks ago GB
hyperledger/fabric-ccenv x86_64- d8b21b weeks ago GB
hyperledger/fabric-ca x86_64- f30bda5f7ee months ago MB
這裡不描述具體過程了。可以參考如下網址:
http://hyperledger-fabric.readthedocs.io/en/latest/samples.html
不過要注意文檔裡有一處我實際測試不行,要做修改。
改為
curl -sSL https://raw.githubusercontent.com/hyperledger/fabric/master/scripts/bootstrap-..sh | bash
編譯chaincode
go get -u --tags nopkcs11 github.com/hyperledger/fabric/core/chaincode/shim
go build --tags nopkcs11
go get可以根據要求和實際情況從網際網路上下載下傳或更新指定的代碼包及其依賴包,并對它們進行編譯和安裝。
go build指令主要用于測試編譯。在包的編譯過程中,若有必要,會同時編譯與之相關聯的包。
成功執行後,在sacc目錄下會生成一個同名sacc執行檔案。
開發者模式下測試
生産環境的的fabric部署,測試是相當麻煩的,不過好在fabric給我們提供了開發者模式進行測試。
首先我們啟動三個終端,全部切換到目錄:
$GOPATH/src/github.com/hyperledger/fabric-samples/chaincode-docker-devmode
Terminal 1 - Start the network
docker-compose -f docker-compose-simple.yaml up
第一個終端啟動了fabric整個網絡環境,包括peer和orderer,另外還啟動了兩個額外的docker容器,一個是chaincode環境,一個是CLI,用來和chaincode進行互動。
啟動後我們可以通過docker ps指令檢視,會看到四個容器:
NAMES
a0d8ca1db66d hyperledger/fabric-ccenv "/bin/bash -c 'sle..." minutes ago Up minutes chaincode
a75d777aab06 hyperledger/fabric-tools "/bin/bash -c ./sc..." minutes ago Up minutes cli
ed1678b9feb hyperledger/fabric-peer "peer node start -..." minutes ago Up minutes :->/tcp, :->/tcp peer
b665817027e hyperledger/fabric-orderer "orderer" minutes ago Up minutes :->/tcp orderer
Terminal 2 - Build & start the chaincode
進入chaincode容器,
docker exec -it chaincode bash
root@d2629980e76b:/opt/gopath/src/chaincode#
cd sacc
go build
運作chaincode,
Terminal 3 - Use the chaincode
啟動一個互動容器來測試我們的chaincode
docker exec -it cli bash
下面兩條指令把我們的chaincode上鍊。
日志雖然很長,我就不貼出來了。
[“a”,”10”]就是我們的參數key-value
先來查詢下a的值
root@d41baa160b:/opt/gopath/src/chaincodedev# peer chaincode query -n mycc -c '{"Args":["query","a"]}' -C myc
-- :: UTC [msp] GetLocalMSP -> DEBU Returning existing local MSP
-- :: UTC [msp] GetDefaultSigningIdentity -> DEBU Obtaining default signing identity
-- :: UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO Using default escc
-- :: UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO Using default vscc
-- :: UTC [msp/identity] Sign -> DEBU Sign: plaintext: AA8080A6008031A0B0889E7E0CF0510..D7963631A0A0A0571756572790A0161
-- :: UTC [msp/identity] Sign -> DEBU Sign: digest: BEE337BA8316D79449B12B892A1B1FD0EFAA3678B1474CF42CF96B6952C655DA
Query Result:
結果是10,正确。
然後修改下a的值,
再次查詢發現a的值确實變為20了。日志我就不貼了。
退出
Terminal2和Terminal3用exit指令退出docker。
Terminal 1直接ctrl+c退出,然後再執行
docker-compose -f docker-compose-simple.yaml down