Hyperledger Fabric 1.0 实战开发系列 第三课 chaincode开发
chaincode是由go語言寫的,實現(xiàn)了定義的接口。其他語言例如JAVA也是支持的。通過application體積的transaction,chaincode可以初始化并管理Ledger狀態(tài)。
一個chaincode創(chuàng)建的Ledger狀態(tài)是獨(dú)立的,不能被其他chaincode直接訪問。在合適的許可下,chaincode能夠調(diào)用在相同網(wǎng)絡(luò)下的其他chaincode用于訪問其Ledger狀態(tài)。
接下來,我們以chaincode開發(fā)者的視野來看看chaincode。下面我們以一個chaincode案例來看看chaincode Shim API的每一個方法。
1.Chaincode API
每一個chaincode需要實現(xiàn)Chaincode接口,其方法是用于響應(yīng)接收到的transaction。當(dāng)chaincode接收到instantiate或者upgrade transaction時Init方法被調(diào)用了,以便chaincode能夠執(zhí)行任何必要的初始化,包括application state的初始化。當(dāng)chaincode接收到invoke transaction時調(diào)用invoke方法,用于處理transaction proposal。
“shim”API中其他的接口是?ChaincodeStubInterface? 用于訪問及改變Ledger,以及在chaincode之間調(diào)用。
在本教程中,我們將通過實現(xiàn)簡單的chaincode應(yīng)用程序(管理簡單的“資產(chǎn)”)來演示這些API的使用。
2.簡單的資產(chǎn)chaincode
我們的application是一個基本的樣例chaincode,用于在Ledger上創(chuàng)建資產(chǎn)(鍵值對)。
推薦使用vscode進(jìn)行g(shù)o代碼編寫,
打開終端并運(yùn)行下面的命令:
sudo add-apt-repository ppa:ubuntu-desktop/ubuntu-make sudo apt-get update sudo apt-get install ubuntu-make 如果已經(jīng)安裝,運(yùn)行下面的命令:
umake web visual-studio-code
請注意,在安裝過程中, 你會問,給出路徑 insatllation 軟件包. 在那之后它會詢問提交您的權(quán)限,安裝 Visual Studio 代碼. 請按 ‘’ 若要安裝 (‘’ 表示接受條款和條件).
? ? ? 設(shè)置GOPATH
vscode安裝go插件時會提示GOPATH not set錯誤,則打開launch.json在里面env中設(shè)置?
安裝vscode go
在vscode應(yīng)用商店里面搜索go,點(diǎn)擊安裝即可。?
2.1 選擇代碼的位置
首選需要確定Go的環(huán)境被安裝以及被合適的配置。
為了簡單起見,我們使用以下命令:
mkdir -p $GOPATH/src/asset && cd $GOPATH/src/asset接下來,我們創(chuàng)建源碼文件
touch asset.go2.2 引入包
每一個chaincode都實現(xiàn)了Chaincode接口?<https://github.com/hyperledger/fabric/blob/master/core/chaincode/shim/interfaces.go#L28>,特別是Init以及Invoke函數(shù)。因此,我們引入
package mainimport ("fmt""github.com/hyperledger/fabric/core/chaincode/shim""github.com/hyperledger/fabric/protos/peer" )2.3 初始化chaincode
我們的類名叫SimpleAsset
type SimpleAsset struct { }接下來,我們事先Init函數(shù)
// Init is called during chaincode instantiation to initialize any data. func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {}注:chaincode upgrade也調(diào)用這個函數(shù)。當(dāng)一個chaincode要升級時,確保合適修正Init函數(shù)。如果沒有需要遷移的東西,或者沒有需要在升級時初始化的東西,需要提供的空的init函數(shù)。
接下來,我們調(diào)用ChaincodeStubInterface.GetStringArgs方法獲取Init中需要的參數(shù),并檢查參數(shù)的有效性。我們期望獲取的參數(shù)是一個鍵值對。
// Init is called during chaincode instantiation to initialize any // data. Note that chaincode upgrade also calls this function to reset // or to migrate data, so be careful to avoid a scenario where you // inadvertently clobber your ledger's data! func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {// Get the args from the transaction proposalargs := stub.GetStringArgs()if len(args) != 2 {return shim.Error("Incorrect arguments. Expecting a key and a value")} }接下來,由于我們的調(diào)用是有效的,我們將會在Ledger上存儲初始狀態(tài)。為了實現(xiàn)狀態(tài)的存儲,我們將會調(diào)用ChaincodeStubInterface.PutState方法,并把鍵值作為參數(shù)進(jìn)行輸入。假設(shè)一切都工作正常,則會返回一個peer.Response對象,表面初始化成功。
// Init is called during chaincode instantiation to initialize any // data. Note that chaincode upgrade also calls this function to reset // or to migrate data, so be careful to avoid a scenario where you // inadvertently clobber your ledger's data! func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {// Get the args from the transaction proposalargs := stub.GetStringArgs()if len(args) != 2 {return shim.Error("Incorrect arguments. Expecting a key and a value")}// Set up any variables or assets here by calling stub.PutState()// We store the key and the value on the ledgererr := 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) }
2.4 調(diào)用chaincode
首先,添加Invoke函數(shù)簽名
// Invoke is called per transaction on the chaincode. Each transaction is // either a 'get' or a 'set' on the asset created by Init function. The 'set' // method may create a new asset by specifying a new key-value pair. func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {}就像上述Init的函數(shù)那樣,我們需要通過ChaincodeStubInterface獲取參數(shù)。Invoke函數(shù)的參數(shù)就是需要調(diào)用chaincode應(yīng)用的名稱。在我們的例子中,我們的應(yīng)用有兩個簡單的函數(shù)set與get,允許asset的值被設(shè)定,同時允許獲取現(xiàn)在的狀態(tài)。我們首先調(diào)用ChaincodeStubInterface.GetFunctionAndParameters用來獲取chaincode應(yīng)用的函數(shù)名稱與參數(shù)。
// Invoke is called per transaction on the chaincode. Each transaction is // either a 'get' or a 'set' on the asset created by Init function. The Set // method may create a new asset by specifying a new key-value pair. func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {// Extract the function and args from the transaction proposalfn, args := stub.GetFunctionAndParameters()}
接著,我們設(shè)置set與get函數(shù)名稱,并調(diào)用這些chaincode應(yīng)用函數(shù),通過shim返回一個合適的響應(yīng)。Error函數(shù)將會把一個響應(yīng)序列化成gRPC protobuf消息。
// Invoke is called per transaction on the chaincode. Each transaction is // either a 'get' or a 'set' on the asset created by Init function. The Set // method may create a new asset by specifying a new key-value pair. func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {// Extract the function and args from the transaction proposalfn, args := stub.GetFunctionAndParameters()var result stringvar err errorif 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 payloadreturn shim.Success([]byte(result)) }
2.5 實現(xiàn)chaincode應(yīng)用
我們的chaincode應(yīng)用實現(xiàn)了兩個函數(shù),能夠通過Invoke進(jìn)行調(diào)用。接下來實現(xiàn)這些函數(shù)。就像我們上面所提到的,我使用chaincode shim API的ChaincodeStubInterface.PutState與ChaincodeStubInterface.GetState來訪問access的狀態(tài)。
// Set stores the asset (both key and value) on the ledger. If the key exists, // it will override the value with the new one func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {if len(args) != 2 {return "", fmt.Errorf("Incorrect arguments. Expecting a key and a 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 }// Get returns the value of the specified asset key 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 }
2.6 合并上述代碼
package mainimport ("fmt""github.com/hyperledger/fabric/core/chaincode/shim""github.com/hyperledger/fabric/protos/peer" )// SimpleAsset implements a simple chaincode to manage an asset type SimpleAsset struct { }// Init is called during chaincode instantiation to initialize any // data. Note that chaincode upgrade also calls this function to reset // or to migrate data. func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {// Get the args from the transaction proposalargs := stub.GetStringArgs()if len(args) != 2 {return shim.Error("Incorrect arguments. Expecting a key and a value")}// Set up any variables or assets here by calling stub.PutState()// We store the key and the value on the ledgererr := 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) }// Invoke is called per transaction on the chaincode. Each transaction is // either a 'get' or a 'set' on the asset created by Init function. The Set // method may create a new asset by specifying a new key-value pair. func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {// Extract the function and args from the transaction proposalfn, args := stub.GetFunctionAndParameters()var result stringvar err errorif fn == "set" {result, err = set(stub, args)} else { // assume 'get' even if fn is nilresult, err = get(stub, args)}if err != nil {return shim.Error(err.Error())}// Return the result as success payloadreturn shim.Success([]byte(result)) }// Set stores the asset (both key and value) on the ledger. If the key exists, // it will override the value with the new one func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {if len(args) != 2 {return "", fmt.Errorf("Incorrect arguments. Expecting a key and a 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 }// Get returns the value of the specified asset key 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 }// main function starts up the chaincode in the container during instantiate func main() {if err := shim.Start(new(SimpleAsset)); err != nil {fmt.Printf("Error starting SimpleAsset chaincode: %s", err)} }
2.7 build chaincode
編譯chaincode
go get -u --tags nopkcs11 github.com/hyperledger/fabric/core/chaincode/shim go build --tags nopkcs11接下來測試chaincode
2.8 使用dev模式測試
一般來說,peer啟動并持有chaincode。然而在開發(fā)模式下,chaincode由用戶build并啟動。在快速代碼/構(gòu)建/運(yùn)行/調(diào)試周期周轉(zhuǎn)期間的鏈碼開發(fā)階段,此模式非常有用。
我們通過為利用預(yù)先生成的order和channel artifacts來啟動一個簡單的開發(fā)網(wǎng)絡(luò)的“開發(fā)模式”。 因此,用戶可以立即編譯chaincode和調(diào)用函數(shù)。
3.安裝hyberLedger fabric 樣例
請先安裝hyberLedger fabric 樣例。
進(jìn)入fabric-samples以及chaincode-docker-devmode目錄
cd chaincode-docker-devmode4.下載docker鏡像
我們需要四個Docker鏡像用于開發(fā)模式允許docker compose script.腳本,如果你已經(jīng)安裝了fabric-samples倉庫克隆,并且按照說明下載了platform-specific-binaries,接下來你應(yīng)該在本地按照Docker鏡像。輸入docker images命令去展示Docker鏡像。應(yīng)該能看到如下:
docker images REPOSITORY TAG IMAGE ID CREATED SIZE hyperledger/fabric-tools latest e09f38f8928d 4 hours ago 1.32 GB hyperledger/fabric-tools x86_64-1.0.0-rc1-snapshot-f20846c6 e09f38f8928d 4 hours ago 1.32 GB hyperledger/fabric-orderer latest 0df93ba35a25 4 hours ago 179 MB hyperledger/fabric-orderer x86_64-1.0.0-rc1-snapshot-f20846c6 0df93ba35a25 4 hours ago 179 MB hyperledger/fabric-peer latest 533aec3f5a01 4 hours ago 182 MB hyperledger/fabric-peer x86_64-1.0.0-rc1-snapshot-f20846c6 533aec3f5a01 4 hours ago 182 MB hyperledger/fabric-ccenv latest 4b70698a71d3 4 hours ago 1.29 GB hyperledger/fabric-ccenv x86_64-1.0.0-rc1-snapshot-f20846c6 4b70698a71d3 4 hours ago 1.29 GB
5.啟動網(wǎng)絡(luò)
docker-compose -f docker-compose-simple.yaml up上述代碼啟動了包括SingleSampleMSPSolo?orderer profile的網(wǎng)絡(luò),同時啟動開發(fā)模式的peer。這個啟動了另外兩個容器,一個是chaincode的環(huán)境以及與chaincode交互的CLI。在CLI容器中進(jìn)行創(chuàng)建與加入channel的命令,因此我們可以開始chaincode的調(diào)用。
6.build與啟動chaincode
docker exec -it chaincode bash進(jìn)入容器,
root@d2629980e76b:/opt/gopath/src/chaincode#接下來,編譯chaincode
cd asset go build現(xiàn)在運(yùn)行chaincode:
CORE_PEER_ADDRESS=peer:7051 CORE_CHAINCODE_ID_NAME=mycc:0 ./assetpeer啟動了chaincode,以及chaincode日志表明peer成功注冊了chaincode。該階段chaincode沒有與任何channel產(chǎn)生關(guān)聯(lián)。這在使用實例化命令的后續(xù)步驟中完成。
7.使用chaincode
即使現(xiàn)在在--peer-chaincodedev模式下,仍然需要安裝chaincode,以便生命周期的chaincode能夠正常檢查。
我們利用CLI容器去調(diào)用這些方法
docker exec -it cli bashpeer chaincode install -p chaincodedev/chaincode/asset -n mycc -v 0 peer chaincode instantiate -n mycc -v 0 -c '{"Args":["a","10"]}' -C myc接下來改變a的值為20.
peer chaincode invoke -n mycc -c '{"Args":["set", "a", "20"]}' -C myc最后查詢
peer chaincode query -n mycc -c '{"Args":["query","a"]}' -C myc8.測試新的chaincode
這個案例中,我們只實現(xiàn)了asset。我們可以很輕松的測試其他的chaincode通過吧這些chaincode加入到chaincode子目錄下,然后重啟網(wǎng)絡(luò)。此時,他們能夠在chaincode容器中被訪問。
總結(jié)
以上是生活随笔為你收集整理的Hyperledger Fabric 1.0 实战开发系列 第三课 chaincode开发的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Hyperledger Fabric 1
- 下一篇: Hyperledger Fabric 1