Hyperledger Fabric 1.2 --- Chaincode Operator 解读和测试(一)
前言
本文主要目的是用于整理Hyperledger? Fabric中關于chaincode 管理和操作的內容,作者以release-1.2為范本進行講解。
主要參考鏈接: https://hyperledger-fabric.readthedocs.io/en/release-1.2/chaincode4noah.html
本文總計有兩節:
第一節: chaincode?Operator 介紹
第二節: 測試和驗證
? 新入門的建議看一看有所了解,已經熟練也可以查缺補漏。
關鍵詞
chaincode: 可以安裝部署在fabric網絡中的鏈上可執行程序;
CDS(ChaincodeDeploymentSpec ): 安裝部署時向fabric節點提交的數據,其中包含 版本、名稱、源碼等等
SignedCDS: 帶有簽名的CDS,其中比CDS多包含了 ownerlist 和 instantiation policy 數據
Instantiation Policy: 實例化策略,指定實例化chaincode時需要滿足的條件
Endorsement Policy:背書策略,指定交易背書結果需要滿足的條件。
lscc(Lifecycle system chaincode ):用于管理application chaincode的生命周期的系統級別chaincode
正文
chaincode 的生命周期
一個chaincode從編寫到部署需要經歷以下幾個步驟:
- package(optional)
- install,
- instantiate,
- upgrade(optional)
- stop(no implemented)
整個流程和我們安裝發布一個web服務很相似,fabric chaincode現在支持 go、java、nodejs多種語言,入門的門檻要比ethereum、eos等公鏈低很多,而且利用fabirc提供的test庫測試起來很方便,作者通常都是用go來開發chaincode,用protobuf作為數據傳輸協議。
?
chaincode的組成部分
ChaincodeDeploymentSpec:包含版本和需要安裝部署的chaincode;
Instantiation Policy(Optional): 和設置背書策略所用的語法是一致;
Owner Signatures: 由“擁有”鏈碼的實體簽署的一組簽名。
owner Signatures 用于以下目的:
<1>標明鏈碼的所有權;
<2> 允許驗證包的內容;
<3>允許校驗包的完整性;
?
在一個channel中根據chaincode的實例化策略來驗證chaincode實例化交易創建者的身份。光翻譯文檔太沒有意思了啊,不直觀,咱們直接上SingedCDS的數據結構看看,一目了然:
?????????? <1>最外層 SignedCDS的結構
// SignedChaincodeDeploymentSpec carries the CDS along with endorsements message SignedChaincodeDeploymentSpec {// This is the bytes of the ChaincodeDeploymentSpecbytes chaincode_deployment_spec = 1;// This is the instantiation policy which is identical in structure// to endorsement policy. This policy is checked by the VSCC at commit// time on the instantiation (all peers will get the same policy as it// will be part of the LSCC instantation record and will be part of the// hash as well)bytes instantiation_policy = 2;// The endorsements of the above deployment spec, the owner's signature over// chaincode_deployment_spec and Endorsement.endorser.repeated Endorsement owner_endorsements = 3; }?
結構里面的三個成員和上面三個組成部分對應 .
<2> ChaincodeDeploymentSpec
// Specify the deployment of a chaincode. // TODO: Define `codePackage`. message ChaincodeDeploymentSpec {// Prevent removed tag re-usereserved 2;reserved "effective_date";enum ExecutionEnvironment {DOCKER = 0; SYSTEM = 1;}ChaincodeSpec chaincode_spec = 1;bytes code_package = 3;ExecutionEnvironment exec_env= 4;}? ExecutionEnvironment 里面包含兩種執行環境,分別對應了fabric中的兩種chaincode類型: system chaincode(直接運行在節點上) 和 application chaincode(運行在docker 容器環境中);
code_package: chaincode 源代碼的壓縮包
exce_env: 傳入給chaincode的環境變量
如果對fabric有過深入了解的讀者應該明白,實際上所謂的chaincode部署安裝后就是一個執行在docker 容器中的一段程序,這個程序像一個服務一樣一直運行并且監聽著從peer節點轉發過來的外部請求,我們隨便用docker inspect 命令去查看以下chaincode容器就能明白:
<3> ChaincodeSpec
// Carries the chaincode specification. This is the actual metadata required for // defining a chaincode. message ChaincodeSpec {enum Type {UNDEFINED = 0;GOLANG = 1;NODE = 2;CAR = 3;JAVA = 4;}Type type = 1;ChaincodeID chaincode_id = 2;ChaincodeInput input = 3;int32 timeout = 4; }這個結構就包含了一些其他相關信息比如:chaincode的id,它是該chaincode在一個channel中的唯一識別符; Type:使用哪種語言編寫的;?timeout:執行chaincode請求的超時時間(這是為了避免chaincode內部死鎖,導致peer節點無法返回執行結果給client); input ,傳入給chaincode的參數,這時invoke時候執行用的.
作者就不在這里多講整個chaincode的運行機制了,畢竟今天的重點不是這個,有空單獨開一個文章給大家說說。
<4>Endorsement
// An endorsement is a signature of an endorser over a proposal response. By // producing an endorsement message, an endorser implicitly "approves" that // proposal response and the actions contained therein. When enough // endorsements have been collected, a transaction can be generated out of a // set of proposal responses. Note that this message only contains an identity // and a signature but no signed payload. This is intentional because // endorsements are supposed to be collected in a transaction, and they are all // expected to endorse a single proposal response/action (many endorsements // over a single proposal response) message Endorsement {// Identity of the endorser (e.g. its certificate)bytes endorser = 1; // Signature of the payload included in ProposalResponse concatenated with// the endorser's certificate; ie, sign(ProposalResponse.payload + endorser) bytes signature = 2; }? 上面是一個主要用在proposal_response 標明應答者身份和保證數據完整性的數據結構,endorser實際上就是公鑰證書,可以標明身份;signature是簽名,這樣你可以直接用endorser里的公鑰去驗證簽名是否正確,看上面注釋寫的很清楚,這個簽名是對(data + endorser)一起簽的名,不光能校驗數據的完整性,還能校驗證書的完整性,一舉兩得。在給chaincodespec簽名的時候也是一樣,不單單是對cds簽名:
// sign the concatenation of cds, instpolicy and the serialized endorser identity with this endorser's keysignature, err := owner.Sign(append(cdsbytes, append(instpolicybytes, endorser...)...)) // each owner starts off the endorsements with one element. All such endorsed// packages will be collected in a final package by CreateSignedCCDepSpecForInstall
// when endorsements will have all the entries
endorsements = make([]*peer.Endorsement, 1)
endorsements[0] = &peer.Endorsement{Signature: signature, Endorser: endorser}
?
創建一個Chaincode Package
有兩種方式去打包一個chaincode:
第一種,一個chaincode對應多個owner
第二種,一個chaincode只有一個owner,更簡單的工作流程適用于部署僅具有發出安裝事務的節點標識的簽名的SignedCDS
可以用peer chaincode package 命令進行打包操作,大家可以自己動手體驗一下,關于package的參數介紹如下:
Package the specified chaincode into a deployment spec.Usage:peer chaincode package [flags]Flags:-s, --cc-package create CC deployment spec for owner endorsements instead of raw CC deployment spec-c, --ctor string Constructor message for the chaincode in JSON format (default "{}")-i, --instantiate-policy string instantiation policy for the chaincode-l, --lang string Language the chaincode is written in (default "golang")-n, --name string Name of the chaincode-p, --path string Path to chaincode-S, --sign if creating CC deployment spec package for owner endorsements, also sign it with local MSP-v, --version string Version of the chaincode specified in install/instantiate/upgrade commands這時關于package命令的help介紹:
讀者們需要主要關注 -s -S -i 三個主要命令:
-s : 指定了-s 就會創建一個可以被簽名的sign CDS 來代替raw CDS,可以在其中附加實例化策略和owner signatures
-S : (optional)如果指定了該參數則會調用本地localMspId身份去對CDS簽名;如果不指定則不會簽名,同時其他的owner也不能對它進行簽名。
-i:指定一個實例化chaincode的策略,它會指明那些身份可以實例化chaincode,如果沒有指定策略,則使用缺省的策略:僅允許peer的admin身份來實例化chaincode.
?
?
如果我們不使用-s 命令的話,那么-S 和 -i 即便是指定了也無法生效,peer-cli 會生成一個raw CDS ,就是上面那個結構二中的ChaincodeDeploymentSpec,它根本不包含其他兩種設置。
示例命令如下:
#這是用 -i 指定了一個實例化策略,創建一個名為 ccpack.out的包peer chaincode package -n mycc -p github.com/hyperledger/fabric/examples/chaincode/go/example02/cmd -v 0 -s -S -i "AND('OrgA.admin')" ccpack.out
? 讀者們可以執行 `cat? ccpack.out` 去看一下,除了上面一片亂碼以外,下面還有一個字符串和一個證書以及簽名,這說明peer-cli 默默的就幫我們用本地的LocalMSP(需要用戶自己指定)進行了簽名。然后把-S的符號去掉重復上面的動作你就會發現,簽名沒有了。
下面貼一段原文:
The optional -i option allows one to specify an instantiation policy for the chaincode. The instantiation policy has the same format as an endorsement policyand specifies which identities can instantiate the chaincode. In the example above, only the admin of OrgA is allowed to instantiate the chaincode.
If no policy is provided, the default policy is used, which only allows the admin identity of the peer’s MSP to instantiate chaincode.
截止作者發文期間,這個文檔的描述還是這樣的,之所以單獨貼出來是因為這個部分是一個坑,很大很大的坑。它里面所說的默認策略不是指當你不填加`insatiation policy`時,fabric定義的默認權限,而是指peer-cli在你不填加該屬性時,自動為你添加的權限!它寫的沒問題,但是容易讓人混淆,至于為什么耐心往下看就明白了。
?
Package 簽名
在創建時簽署的鏈代碼包可以移交給其他所有者輪流進行檢查和簽名。ChaincodeDeploymentSpec可以選擇由集體所有者簽名,以創建SignedChaincodeDeploymentSpec(或SignedCDS)。 SignedCDS包含3個元素:
CDS包含源代碼,鏈碼的名稱和版本;
鏈碼的實例化策略,語法和背書策略相同;
通過Endorserment數組組成的chainod的owner list;
在有多個owner的情況要輪流順序簽名:
?
#org1peer chaincode signpackage ccpack.out signedccpack.out
#org2 peer chaincode signpackage signedccpack.out signedccpack2.out
?
? 還是像上面所說的操作一樣,用 `cat? signedccpack2.out` 命令查看一下,你就會看到底部多出來的簽名和證書。
下面貼一段原文:
Note that this endorsement policy is determined out-of-band to provide proper MSP principals when the chaincode is instantiated on some channels.If the instantiation policy is not specified, the default policy is any MSP administrator of the channel.
? 這里面也提到了default policy 和上面提到的不一樣,作者當時也是經歷了很多曲折才發現這個區別,最終為了確定到底default policy是什么,從源碼中找出了以下注釋:
the default instantiation policy allows any of the channel MSP admins to be able to instantiate? 即:這句話很容易讓人產生混淆,實際上來講就是允許任意一個包含在channel內的組織的admin身份去實例化chaincode.
Install chaincode
在fabric中 install chaincode ,必須由peer對應的 admin Identity 來安裝,這個是不需要設置的,也是無法更改的, 而且要向每一個需要安裝的peer分別發送指令。install chaincode 可以指定一個上面的安裝包,也可以直接指定路徑。不過需要注意的是,直接指定chaincode源碼路徑安裝是沒有自定義instantiate policy的,它將被賦予一個default policy(在Package 簽名說的default policy)。
?
接觸過fabric環境搭建的讀者因該很清楚,Install 命令是可以通過 -p 參數直接指定 chaincode 路徑來安裝的,也就是說現在有兩種安裝方式:第一種,直接安裝; 第二種,打包安裝。
示例命令:
#直接指定源碼路徑安裝 peer chaincode install -n mycc -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/example02/cmd #安裝chaicode package peer chaincode install ccpack.out? 為什么fabric要搞的這么復雜?install chaincode必須要由peer 的admin 來安裝,還要分別安裝不支持廣播:一方面是出于數據的隱私保護來考慮,這一點在fabirc中sidedb的特性上有體現,即便是同一個channel中的組織也未必所有的服務都是公共通用的;另一方面,即便是同一個組織的peer也不都需要安裝 同一個chaincode,太浪費了。
另外還要提一點是 install 這個請求是不需要打包成交易發送給 orderer的,這是作者通過sdk得出的結論,client向peer發送完請求后就結束了,沒有打包交易去廣播。這又是為什么?很簡單,如果打包成交易廣播除去,每一個組織都能獲取到chaincode了,還有什么隱私可言?
這里作者多說一句,Hyperledger fabric 確實功能豐富,配置靈活,但就是太靈活了,給使用造成不小的障礙。
?
Instantiation chaincode
? 在實例化的時候,會根據所實例化的chaincode的policy來校驗signed proposal creator的簽名是不是與chaincode instantiate policy所吻合。如install chaincode中所闡述,如果是一個default實例化策略的chaincode,部署時任意一個channel msp Admin.在實例化的時候會指定該chaincode的背書策略,該背書策略指明了交易生效需要的背書條件。
示例命令:
#用 -P 指定了背書策略peer chaincode instantiate -C mychannel -n mycc -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "OR('Org1MSP.peer','Org2MSP.peer')"
? 這里需要給各位讀者一個提醒:在使用 peer-cli去實例化智能合約的時候,如果是要在多個組織和peer上部署,需要在同一個區塊時間內提交到orderer當中,否則后提交的會不成功,顯示錯誤: xxx chaincode 已經存在。這個大家可以去試一試,一個channel內最起碼要兩個peer節點 A 和B, 無需指定實例化策略,直接用install -p 的方式安裝,全都安裝同一個chaincode,等PeerA 節點實例化交易被打包進區塊后,再去提交向PeerB節點發送實例化交易。
為什么會這樣?這是因為一個chaincode實例化交易被廣播到本地后,及時是那些還沒來得及實例化或是不安裝的節點也會把已經實例化的結果寫入到賬本,所以再向PeerB發送實例化請求的時候發現本地的賬本中記錄該chaincode已經被實例化了。
但是作者上面說了,install是不需要廣播的,為什么instantiation 需要廣播?這時因為instantiation的時候需要調用chaincode中的init 函數,初始化一些參數(由 -c 傳入)。第一,假如instantiation 請求不被打包成交易廣播出去寫入本地賬本,那么初始化的參數是無法生效的(fabric 的兩段校驗機制);第二,fabric也好bitcoin也好實際上都和狀態機有異曲同工之妙,都追求數據的最終一致性,不過bitcoin是異步一致(commit tx 后并不立即生效),而fabric和狀態機(raft等)追求的是同步一致(在commit tx的同時,保證執行的tx是生效的),這個時候對不同節點之間的數據狀態一致性是有很高要求的,如果沒有上面的限制,有可能會導致不同節點之間數據的狀態不一致。
peer-cli用上面的命令發送實例化請求,只會發送給實例化請求給一個peer 及 ?CORE_PEER_ADDRESS所指定的peer,在實際生成中肯定不能這樣做。可參考fabric-sdk-go中的示例,由一個client 同時向多個peer節點發送實例化請求,然后在將peer返回的實例化結果打包成tx,廣播出去,這樣就不會造成沖突。
另外,再說一點,如上文所說chaincode實例化成功后會創建一個chaincode 容器,那么這個容器是什么時候創建的?是接收到實例化請求后,還是在實例化交易寫入賬本?答案是接收到實例化請求之后,也就是說可能tx提交不成功chaincode容器也可能會被創建,這點需要注意。
?
Upgrade Chaincode
在升級之前,必須在所需的endorsor上安裝新版本的鏈代碼。而且它與實例化一樣,只會在指定的channel內生效,其他channel不影響。由于鏈接代碼的多個版本可能同時處于活動狀態,因此升級過程不會自動刪除舊版本,暫時需要用戶手動管理。所謂手動管理就是手動清理那些舊版本的容器和緩存在節點本地的chaincode包。
下面貼一段原文:
?
There’s one subtle difference with the instantiate transaction: the upgrade transaction is checked against the current chaincode instantiation policy, not the new policy (if specified).This is to ensure that only existing members specified in the current instantiation policy may upgrade the chaincode.
?
? 執行更新操作的時候 Upgrade 需要去按照當前的chaincode的實例化策略去檢測,而不是新chaincode指定的實例化策略。 這句話是錯的,因為根據作者的測試結果顯示,并非如此,而當作者看了源碼后更加確信這句話是錯的:
// executeUpgrade implements the "upgrade" Invoke transaction.[executeUpgrade](https://github.com/hyperledger/fabric/blob/9e9ebe651225104823d228a09e94432592252ca3/core/scc/lscc/lscc.go#L540)(...) {//cdLedger.InstantiationPolicy 當前chaincode的實例化策略
err = lscc.support.CheckInstantiationPolicy(signedProp, chainName, cdLedger.InstantiationPolicy)
if err != nil {
return nil, err
} .... ....//retain chaincode specific data and fill channel specific onescdfs.Escc = string(escc)cdfs.Vscc = string(vscc)cdfs.Policy = policy **// retrieve and evaluate new instantiation policy**
//cdfs.InstantiationPolicy 新chaincode 的實例化策略cdfs.InstantiationPolicy, err = lscc.support.GetInstantiationPolicy(chainName, ccpackfs)if err != nil {return nil, err}
// CheckInstantiationPolicy checks whether the supplied signed proposal
// complies with the supplied instantiation policyerr = lscc.support.CheckInstantiationPolicy(signedProp, chainName, cdfs.InstantiationPolicy)if err != nil {return nil, err}…… …… return cdfs, nil }
? 這是lscc中管理更新的源碼,我們可以看到它清清楚楚的在注釋中寫著“retrieve and evaluate new instantiation policy”, 也就是說它既要檢測已經存在的實例化策略,還要檢查新chaincode 指定的策略, 如果signedproposal 有一個策略不符合就不會成功。
示例命令:
#安裝新版本的chaincode,安裝規則不變peer chaincode install -n mycc -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/example02/cmd
#更新智能合約
peer chaincode upgrade -o orderer.example.com:7050 -n mycc -v 1.2 -c '{"Args":["init","a","100","b","200"]}' -P "AND('Org1MSP.peer','Org2MSP.peer')" -C mychannel
? 和instantiation Tx一樣, upgrade Tx也是要廣播的。
?
Stop 和 start
這個它暫時還沒有實現,需要自己手動刪除peer上的cds 和 在peer創建的docker容器。
Invoke Transaction
發起一筆交易到fabric網絡,執行invoke操作的時候需要Endorser節點的背書,在Endorser執行背書請求的時候會檢測client提交的 signed Proposal 是否符合channel的策略(是否符合讀寫策略); 同時在commit Tx到賬本時候,committer 也會去檢測Tx中的背書結果是否符合背書策略,來決定是否修改state,而這個背書策略是在實例化和更新的時候指定的。
示例命令:
peer chaincode invoke -o orderer.example.com:7050 -C mychanel -n mycc -c '{"Args":["invoke","a","b","10"]}'orderer是不會對Tx執行的結果進行檢測的,它只會對提交的Tx是否符合channel 的write policy進行檢測,只有peer在commit的時候會對Tx的進行檢查(是否符合背書策略,正確與否),來決定Tx的結果是否生效。在fabric中即便你提交的交易中包含的背書不符合策略或者`雙花交易`,它仍然會被寫入到賬本中,而fabric會特意提取出一個只包含有效交易的部分。
?
實驗部分請見下一節。
轉載于:https://www.cnblogs.com/cnblogs-wangzhipeng/p/9478044.html
總結
以上是生活随笔為你收集整理的Hyperledger Fabric 1.2 --- Chaincode Operator 解读和测试(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 手把手教你安装Navicat——靠谱的N
- 下一篇: d3 力导向图 force graph