簡介
數字貨幣曾是區塊鏈技術的唯一應用情境
對智能合約的支援突破了情境限制, 豐富了區塊鏈應用的適用範圍, 可以支援多行業、大規模的商業應用
區塊鏈應用
區塊鏈應用: 一般由若干部署在區塊鏈網路中的智能合約, 以及調用這些智能合約的應用程式組成
使用者專註於與業務本身相關的應用程式
智能合約則封裝了與區塊鏈賬本直接互動的相關過程, 被應用程式調用
智能合約開發
智能合約本質上是為了對上層商務邏輯進行支援且直接與賬本結構打交道, 處於核心位置.
所以設計得當可以簡化上層應用開發的過程
應用程式開發
應用程式通過調用智能合約提供的方法介面實現商務邏輯, 可以使用JavaScript、Python、Go、Java等主流語言進行開發
鏈碼的原理
鏈碼延伸自智能合約的概念, 支援使用主流進階程式設計語言實現
區塊鏈網路中的成員商定商務邏輯後, 可將商務邏輯編程到鏈碼中, 所有人遵守合約執行
鏈碼會建立一些狀態(state)並寫入賬本中。狀態帶有綁定到鏈碼的命名空間,僅限於建立他的鏈碼使用,不能被其他鏈碼直接存取。不過,在合適的範圍內,一個鏈碼也可以調用另一個鏈碼,間接訪問其狀態
鏈碼在Fabric節點上的隔離沙箱(目前為Docker容器)中運行, 並通過gRPC協議與節點進行互動
Fabric中支援多種語言實現鏈碼,包括Golang、JavaScript、Java等
基本工作原理
- 首先使用者通過用戶端向Fabric的背書節點發出調用鏈碼的交易提案
- 節點對交易提案進行包括ACL許可權檢查在內的各種檢驗, 通過後則建立類比執行這一交易的環境
- 之後, 節點和鏈碼容器之間通過gRPC訊息來互動, 類比執行交易並給出背書結論
- 當鏈碼的代碼邏輯需要讀寫賬本時,通過shim層發送相應操作類型給節點, 節點本地操作賬本後返迴響應訊息
- 用戶端收到足夠的背書節點的支援後, 便可以將這筆交易發送給排序節點進行排序, 並最終寫入區塊鏈
鏈碼介面與結構
依賴包
鏈碼實現需要引入如下依賴包
- "github.com/hyperledger/fabric/core/chaincode/shim"
- shim包提供了鏈碼與賬本互動的中介層
- 鏈碼通過shim.ChaincodeStub提供的方法來讀取和修改賬本狀態
- "github.com/hyperledger/fabric/protos/peer"
鏈碼介面
每個鏈碼都需要實現chaincode介面:
type Chaincode interface{ Init(stub ChaincodeStubInterface) peer.Response Invoke(stub ChaincodeStubInterface) peer.Response}
Init與Invoke方法
編寫鏈碼, 關鍵是實現Init與Invoke兩個方法
Init方法在鏈碼部署或升級時被調用, 完成初始化工作
對資料進行操作時, Invoke方法被調用, 因此響應調用或查詢的商務邏輯都需要在此方法中實現
必要結構
一個鏈碼的必要結構如下
package main//引入必要的包import( "fmt" "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/protos/peer")//聲明一個結構體type SimpleChaincode struct { }//為結構體添加Init方法func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) peer.Response{ //在該方法中實現鏈碼初始化或升級時的處理邏輯 //編寫時可靈活使用stub中的API}//為結構體添加Invoke方法func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response{ //在該方法中實現鏈碼運行中被調用或查詢時的處理邏輯 //編寫時可靈活使用stub中的API}//主函數,需要調用shim.Start( )方法func main() { err := shim.Start(new(SimpleChaincode)) if err != nil { fmt.Printf("Error starting Simple chaincode: %s", err) }}
鏈碼API
賬本狀態互動API
鏈碼需要將資料記錄在分散式總帳中.需要記錄的資料稱為狀態, 以K-V對的形式儲存
賬本狀態互動API可以對賬本狀態進行操作
GetState(key string) ([]byte, error) 通過Key來返回數組的特定值
PutState(key string, value []byte) error 賬本中寫入特定的鍵和值
DelState(key string) error 從賬本中移除指定的鍵和值
GetStateByRange(startKey, endKey string) (StateQueryIteratorInterface, error) 根據指定的範圍內的健值
GetHistoryForKey(key string) (HistoryQueryIteratorInterface, error) 返回指定健的所有曆史值
GetQueryResult(query string) (StateQueryIteratorInterface, error) 對(支援富查詢功能的)狀態資料庫進行富查詢
交易資訊相關API
GetTxID() string 返回交易提案中指定的交易ID
GetTxTimestamp() (*timestamp.Timestamp, error) 返回交易建立的時間戳記,這個時間戳記是peer收到交易的目前時間
GetBinding() ([]byte, error) 返回交易的binding資訊
GetSignedProposal() (*pb.SignedProposal, error) 返回與交易提案相關的所有資料
GetCreator() ([]byte, error) 返回該交易的提交者的身份資訊
GetTransient() (map[string][]byte, error) 返回交易中不會被寫至賬本中的一些臨時資訊
參數API
GetArgs() [][]byte 返回調用鏈碼時交易提案中指定的參數
GetArgsSlice() ([]byte, error) 返回調用鏈碼時交易提案中指定的參數
GetFunctionAndParameters() (function string, params []string) 返回調用鏈碼時交易提案中指定的被調用的函數名稱及其參數
GetStringArgs() []string 返回調用鏈碼時指定的參數
-c '{"Args":["fn", "param1", "param2", "paramN"]}'
樣本(HelloWorld)
Init方法
- 擷取參數並判斷參數長度是否為2
- 調用PutState方法將狀態寫入賬本中
- 如果有錯誤, 則返回
- 列印輸出提示資訊
- 返回成功
Invoke方法
- 擷取參數並判斷長度是否為1
- 利用第1個參數擷取對應狀態GetState(key)
- 如果有錯誤則返回
- 如果傳回值為空白則返回錯誤
- 返回成功狀態