Fabric chaincode

 ChaincodeStubInterface:

Chaincode是一段由Go語言編寫(支持其他編程語言,如Java),並能實現預定義接口的程序。

Chaincode運行在一個受保護的Docker容器當中,與背書節點的運行互相隔離。

Chaincode可通過應用提交的交易對賬本狀態初始化並進行管理。

一段chaincode通常處理由網絡中的成員一致認可的業務邏輯,故我們很可能用“智能合約”來代指chaincode。

一段chiancode創建的(賬本)狀態是與其他chaincode互相隔離的,故而不能被其他chaincode直接訪問。

不過,如果是在相同的網絡中,一段chiancode在獲取相應許可後則可以調用其他chiancode來訪問它的賬本。


可參考:https://hyperledgercn.github.io/hyperledgerDocs/chaincode_operators_zh/


Chaincode 開發手冊

Chaincode API

Init

Init(初始化)方法會在chaincode接收到instantiate(實例化)或者upgrade(升級)交易時被調用,

進而使得chaincode順利執行必要的初始化操作,包括初始化應用的狀態;

例子:

 

// 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 proposal
args := 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 ledger
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)

}


Invoke

Invoke(調用)方法會在響應invoke(調用)交易時被調用以執行交易。

例子:

 

// 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 proposal
fn, args := stub.GetFunctionAndParameters()

 

var result string
var err error
if fn == "set" {

result, err = set(stub, args)

} else { // assume 'get' even if fn is nil

result, err = get(stub, args)

}
if err != nil {

return shim.Error(err.Error())

}

 

// Return the result as success payload
return 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

}


chaincode的依賴

chaincode引入必要的依賴,chaincode shim package和peer protobuf package

例子:

package main

import (

 "fmt" 

 "github.com/hyperledger/fabric/core/chaincode/shim" 

 "github.com/hyperledger/fabric/protos/peer"

  )


main函數

在main函數中通過API shim.start()來向特定peer結點註冊該chaincode

例子:

 

// 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)

}

}


ChaincodeStub提供的API

ChaincodeStubInterface: 

第一大類與state操作相關。

通過這些API可以根據key來查詢/添加/更新相應的state。

這些API提供了單key的讀寫操作、key字典序範圍讀取操作、composite key讀取操作、底層數據庫語法的查詢操作等。

第二大類與與參數相關。

fabric1.0修改了chaincode接口的定義,需要開發者自己調用API獲取傳入的參數。

注意,傳入的參數第一個是函數名稱,之後纔是相應的函數輸入參數。

第三大類與Transaction有關。

並且這一類都是讀操作,讀取transaction中各種信息,比如transaction id、timestamp等。

第四類是與chaincode間相互調用有關的一個API。

Fabric允許在一個chaincode中根據chaincode name和channel name去調用另一個chaincode。

可以看到並沒有deploy的API,也就是說,fabric不允許在一個chaincode中去部署新的chaincode。

第五類也只有一個API,SetEvent。

Fabric允許開發者定義自己的event,然後這個event會在transaction寫進block時觸發,

因此開發者就可以自己寫相應的event handler程序做一些相關的工作。

例子:

shim.ChaincodeStubInterface.GetStringArgs:獲取參數

shim.ChaincodeStubInterface.PutState:寫入賬本數據

shim.ChaincodeStubInterface.GetState:讀取賬本數據

shim.ChaincodeStubInterface.DelState:刪除賬本數據


Chaincode 操作手冊

創建包

打包chaincode有兩種方式。

第一種是當你想要讓chaincode有多個所有者的時候,此時就需要讓chaincode包被多個所有者簽名。

這種情況下需要我們創建一個被簽名的chaincode包(SignedCDS),這個包依次被每個所有者簽名。

另一種就比較簡單了,這是當你要建立只有一個節點的簽名的時候(該節點執行install交易)。

如果您對多用戶的情況不感興趣,您可以直接跳到後面的安裝chaincode部分。

要創建一個簽名過的chaincode包,請用下面的指令:

peer chaincode package -n mycc -p github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02 -v 0 -s -S -i "AND('OrgA.admin')" ccpack.out

-s選項創建了一個可被多個所有者簽名的包,而非簡單地創建一個CDS。

如果使用-s,那麼當其他所有者要簽名的時候,-S也必須同時使用。

否則,該過程將創建一個僅包含實例化策略的簽名chaincode包(SignedCDS)。

-S選項可以使在core.yaml文件中被localMspid相關屬性值定義好的MSP對包進行簽名。

-S選項是可選的。不過,如果我們創建了一個沒有簽名的包,

那麼它就不能被任何其他所有者用signpackage指令進行簽名。

-i選項也是可選的,它允許我們爲chaincode指定實例化策略。

實例化策略與背書策略格式相同,它指明誰可以實例化chaincode。

在上面的例子中,只有OrgA的管理員纔有資格實例化chaincode。

如果沒有提供任何策略,那麼系統會採用默認策略,

該策略只允許peer節點MSP的管理員去實例化chaincode。

包的簽名

一個在創建時就被簽名的chaincode包可以交給其他所有者進行檢查與簽名。

具體的工作流程支持帶外對chaincode包簽名。

每個(chaincode的)所有者通過將ChaincodeDeploymentSpec與其本人的身份信息(證書)結合並對組合結果簽名來認證ChaincodeDeploymentSpec。

一個chaincode所有者可以對一個之前創建好的帶簽名的包進行簽名,具體使用如下指令:

peer chaincode signpackage ccpack.out signedccpack.out

指令中的ccpack.outsignedccpack.out分別是輸入與輸出包。

signedccpack.out則包含一個用本地MSP對包進行的附加簽名。

安裝chaincode

install交易的過程會將chaincode的源碼以一種被稱爲ChaincodeDeploymentSpec(CDS)的規定格式打包,

並把它安裝在一個將要運行該chaincode的peer節點上。

注意:

請務必在一條channel上每一個要運行你chaincode的背書節點上安裝你的chaincode

如果只是簡單地給install API一個ChaincodeDeploymentSpec,它將使用默認實例化策略並添加一個空的所有者列表。

注意:

Chaincode應該僅僅被安裝於chaincode所有者的背書節點上,以使該chaincode邏輯對整個網絡的其他成員保密。

其他沒有chaincode的成員將無權成爲chaincode影響下的交易的認證節點(endorser)。

也就是說,他們不能執行chaincode。不過,他們仍可以驗證交易並提交到賬本上。

下面安裝chaincode。此時會發送一條 SignedProposal 到生命週期系統chaincode (LSCC),該chaincode在系統chaincode部分會仔細描述。

舉個例子,使用CLI安裝簡單的賬本管理chaincode章節的sacc chaincode樣例時,命令如下:

peer chaincode install -n asset_mgmt -v 1.0 -p sacc

在CLI內部會爲sacc創建SignedChaincodeDeploymentSpec,並將其發送到本地peer節點。

這些節點會調用LSCC上的Install方法。上述的-p選項指明chaincode的路徑,

其必須在用戶的GOPATH目錄下(比如$GOPATH/src/sacc)。完整的命令選項詳見CLI部分。

注意:爲了在peer節點上安裝(chaincode),SignedProposal的簽名必須來自peer節點本地MSP的管理員中的一位。

實例化chaincode

實例化交易會調用生命週期系統chaincode (LSCC)來在一個channel上創建並初始化一段chaincode。

下面是一個chaincode-channel綁定的具體過程:

一段chaincode可能會與任意數量的channel綁定並在每個channel上獨立運行。

換句話說,chaincode在多少個channel上安裝並實例化並沒有什麼影響,對於每個提交交易的channel,其狀態都是獨立而互不影響的。

一個實例化交易的創建者必須符合在SignedCDS中chaincode的實例化策略,

且必須充當channel的寫入器(這會成爲channel創建配置的一部分)。

這對於channel的安全至關重要,因爲這樣可以防止惡意實體在未綁定的channel上部署chaincode,也能防止間諜成員在未綁定的channel上執行chaincode。

舉個例子,我們提到過默認的實例化策略是任何channel MSP的管理員(可以執行),

所以chaincode創建者要實例化交易,其本人必須是channel管理員的一員。

當交易提議到達背書成員時,它會驗證創建者的簽名是否符合實例化策略。

在交易被提交到賬本之前的交易驗證階段,以上操作還會再來一遍。

實例化交易的過程還會爲channel上的chaincode建立背書策略。背書策略描述了交易的相關認證要求,以使得交易能被channel中的成員認可。

例如,使用CLI去實例化上一章的sacc chaincode並初始化john的狀態爲0,指令具體如下:

peer chaincode instantiate -n sacc -v 1.0 -c '{"Args":["john","0"]}' -P "OR ('Org1.member','Org2.member')"

注意:

注意,上述背書策略(CLI使用波蘭表示法)向Org1或Org2的成員詢問所有sacc處理的交易。也就是說,爲確保交易有效,Org1或Org2必須爲調用sacc的結果簽名。

在成功實例化後,channel上的chaincode就進入激活狀態,並時刻準備執行任何ENDORSER_TRANSACTION類型的交易提議。交易會在到達背書節點的同時被處理。

升級chaincode

一段chaincode可以通過更改它的版本(SignedCDS的一部分)來隨時進行更新。

至於SignedCDS的其他部分,比如所有者及實例化策略,都是可選的。

不過,chaincode的名稱必須一致,否則它會被當做完全不同的另一段chaincode。

在升級之前,chaincode的新版本必須安裝在需要它的背書節點上。

升級是一個類似於實例化交易的交易,它會將新版本的chaincode與channel綁定。其他與舊版本綁定的channel則仍舊運行舊版本的chaincode。換句話說,升級交易只會一次影響一個提交它的channel。

注意:

注意:由於多個版本的chaincode可能同時運行,所以升級過程不會自動移除舊版本,用戶必須親自處理。

升級交易與實例化交易有一處微妙的區別:升級交易採用當前的chaincode實例化策略進行檢查,而非比對新的策略(如果指定了的話)。這是爲了確保只有當前實例化策略指定的已有成員才能升級chaincode。

注意:

注意:在升級過程中,chaincode的Init函數會被調用以執行數據相關的操作,或者重新初始化數據;所以要多加小心避免在升級chaincode時重設狀態信息。

1 從宿主機將chaincode拷貝進容器

docker cp community2.0 cli:/opt/gopath/src/github.com/hyperledger/fabric/examples/chaincode/go

2 安裝新版本chaincode,打包到peer節點

docker exec -it cli bash
peer chaincode install -n mycc -v 2.0 -p github.com/hyperledger/fabric/examples/chaincode/go/community2.0

3 升級chaincode

peer chaincode upgrade -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n mycc -v 2.0 -c '{"Args":["init"]}' -P "OR ('Org1MSP.member','Org2MSP.member')"



發佈了21 篇原創文章 · 獲贊 2 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章