Hyperledger Fabric— Chaincode 的生命週期
什麼是Chaincode?
Chaincode 是一個程序,用 Go、Node.js 或 Java 來開發,實現了規定的接口。 Chaincode 在與背書對等節點是運行在不同的Docker容器中。 Chaincode 透過應用程式提交的交易來初始化和管理帳本狀態。
Chaincode通常處理網路成員所一致同意的業務邏輯,因此它可以被視為“智慧合約”。 由Chaincode建立的"該次"帳本更新只限於”該chaincode”,並且不能被另一個chaincode直接存取。 但是,在同一個網路中,如果獲得適當的權限,Chaincode是可以呼叫另一個chaincode來存取此次的狀態。
在本文中,我們將通過區塊鏈網路維運人員而不是應用程式開發人員的視角來探討Chaincode。 chaincode維運人員可以使用本文作為指南,了解如何使用 Fabric chaincode生命週期在其網路上進行chaincode的部署和管理。
部署Chaincode
Fabric Chaincode生命週期是一個過程,它允許多個組織讓chaincode在渠道中被使用之前就如何維運chaincode達成一致。 Fabric網路維運人員將使用 Fabric 生命週期來執行以下任務:
- 安裝與定義Chaincode
- 升級chaincode
- 部署的場景
- 遷移到新的Fabric生命週期
我們可以透過建立新渠道並將渠道功能設置為 V2_0 來使用 Fabric chaincode生命週期。 我們將無法在有著V2_0 capabilites功能的渠道上用舊的生命週期進行Chaincode的安裝、實例化(instantiate)或更新chaincode。 但是,還是可以呼叫已經用舊版的生命週期模型完成安裝的chaincode。
安裝與定義一個Chaincode
Fabric Chaincode生命週期要求組織要去定義chaincode的參數,例如名稱、版本和chaicode背書政策。 渠道成員通過以下四個步驟達成一致的政策(也就是參數設定)。 但並非渠道上的每個組織都需要完成每個步驟。
- 打包Chaincode — 這一步可以由一個組織完成,也可以由每個組織完成。
- 將chaincode安裝在對等節點上 — 每個會使用chaincode來為交易背書或查詢帳本的組織都需要完成此步驟。
- 為組織批准Chaincode定義 — 每個會使用chaincode的組織都需要完成此步驟。 chaincode definition需要得到足夠數量的組織的批准,以滿足渠道的生命週期背書(Lifecycle Endorsement) 政策(預設為多數,就是超過一半),然後才能在渠道上啟動chaincode。
- 將chaincode definition提交到渠道 — 一旦渠道上獲得所需數量的組織批准,交易的發生就會由一個組織提交。 提交者首先需要收集足夠多的組織的背書節點,然後提交交易以將chaincode definition寫入帳本。
本文只會講解 Fabric chaincode生命週期的維運過程,而不是具體的指令。 了解有關如何使用 Peer CLI 運作Fabric 生命週期的資訊可參考Deploying a smart contract to a channel tutorial 或 peer lifecycle command reference。
第一步: 封裝智慧合約
Chaincode 要打包在一個 tar 檔中,然後才能安裝到對等節點上。 我們可以使用 Fabric peer 工具來打包檔案、Node Fabric SDK 或第三方工具(例如 GNU tar)打包chaincode。 當我們創建一個chaincode package時,我們需要提供一個chaincode package標籤來建立容易辨識(就是人看得懂)chaincode package描述。
如果我們使用第三方工具打包chaincode,則產生的檔案需要採用以下格式。 如果是用Fabric peer 工具和 Fabric SDK打包檔案 將自動以這種格式建立檔案。
- chaincode需要打包在一個 tar 檔中,帶有 .tar.gz結尾的附檔名 。
- tar 檔需要包含兩個檔案(無目錄):一個metadata file “metadata.json”和另一個包含chaincode 檔案的 tar“code.tar.gz”。
- “metadata.json”包含所指定chaincode開發語言、代碼路徑和package標籤的 JSON。 下面是一個metadata檔案的範例:
{"Path":"fabric-jasonkao/asset-transfer-basic/chaincode-go","Type":"golang","Label":jasonkao"}
Chaincode由 Org1 和 Org2 分別打包(如下圖)。 這兩個組織都使用 MYCC_1 作為他們的打包檔案的標籤,以便使用名稱和版本來識別打包檔。 組織不必使用相同的打包標籤。
第二步:將Chaincode安裝在對等節點
我們需要在會執行交易和認可交易的每個對等節點上安裝chaincode package。無論是使用 CLI 工具還是 SDK,我們都有 Peer Administrator權限 完成這個作業。對等節點將在安裝chaincode之後 build chaincode,如果chaincode有問題,則顯示build error。建議每次只打包一個chaincode,然後在屬於其組織的每個對等節點上安裝相同的package。如果一個渠道想要確保每個組織都運行相同的chaincode,一個組織可以打包一個chaincode並將用其他方式(例如GitHub)傳給渠道中的成員。
成功的安裝後將返回一個chaincode package identifier,它是package標籤與package本身的hash值。這個package identifier用在將會已安裝在對等節點上的chaincode與組織批准的chaincode定義相關聯。下一步我們將需要這個package identifier。我們還可以通過使用 Peer CLI 查詢安裝在對等節上的package來查看package identifier。
Org1 和 Org2 的 對等節點管理員在加入渠道的對等節點上安裝chaincode package :MYCC_1。 安裝chaincode package會build chaincode並建立一個package identifier MYCC_1:hash。
第三步:為組織批准一個chaincode definition
Chaincode由chaincode definition管理。 當渠道成員批准chaincode definition時,這一個批准動作會當做組織對其接受chaincode參數的投票。 這些經批准的組織定義讓渠道成員在chaincode用於渠道之前就要達成一致。 Chaincode definition包括以下參數,這些參數需要在組織之間保持一致:
- Name — 應用程式在呼叫chaincode時將使用的名稱。
- Version — chaincode package的版本號。 如果我們升級chaincode檔案,我們就需要更改chaincode版本。 版本號可以包含任何字串,但通常使用 v1.2.3 等格式。 對等節點不檢查版本號,它只是一個指標。
- Sequence — 在渠道上定義chaincode的次數。 這個值是一個整數,用於追踪chaincode升級。 例如,當我們第一次在渠道上批准並提交chaincode definition時,必須將sequence設定成 1。我們您下次升級或更新chaincode definition時,將sequence變成 2。sequence由 對等節點來確任所有組織在他們批准和提交的chaincode definition方面保持同步。
- Endorsement Policy — 哪些組織需要執行和驗證交易結果。 背書政策可以表示為傳遞給 CLI 的字串,也可以引用渠道配置中的政策。 預設情況下,背書政策設置在 Channel/Application/Endorsement這個路徑下,預設參數要求渠道中的大多數組織對交易進行背書。
- Collection Configuration — 這跟我們的chaincode關聯的private data collection definition file的路徑。
- ESCC/VSCC Plugins — 這個chaincode要使用的自定義背書或驗證插件的名稱。
- Initialization —如果我們使用 Fabric Chaincode Shim API 提供的Shim API,我們的chaincode需要包含一個用於初始化chaincode的 Init 函數。 chaincode interface需要這個函數,但不一定需要由我們的應用程式呼叫。 當我們批准chaincode definition時,我們可以指定是否必須在 Invokes 之前呼叫 Init。 如果我們指定 Init函數 是必需的,Fabric 將確保 Init 函數在chaincode中的任何其他函數之前就被呼叫,而且只被呼叫一次。 請求執行 Init 函數允許我們實現在初始化chaincode時運行的邏輯,例如設置一些初始狀態。 每次變動chaincode的版本時,我們都需要呼叫Init 來初始化chaincode。
如果我們使用的是 Fabric peer CLI,我們可以在批准並提交chaincode definition時使用 — init-required flag來指示必須呼叫Init 函數來初始化新的chaincode版本。 要使用 Fabric peer CLI 呼叫Init,要使用 peer chaincode invoke 指令並傳遞 — isInit 標誌。
如果我們使用 Fabric Contract API,則不需要在chaincode中使用 Init 函數。 但是,如果是透過我們應用程式來初始化Chaincode,我們仍然可以使用 — init-required flag來完成。 如果我們使用 — init-required flag,則需要將 — isInit flag或參數傳遞給chaincode來呼叫,以便在每次增加chaincode版本時初始化chaincode。 我們可以傳遞 — isInit 並使用chaincode中的任何函數初始化chaincode。
chaincode definition還包括Package Identifier。 這是每個想要使用chaincode的組織的必要的參數。 所有組織的package ID 不必相同。 組織可以在核准chaincode definition是不用安裝chaincode packagec或是在chaincode definition中包含identifier的情況下批准chaincode definition。
每個想要使用chaincode的渠道成員都需要為其組織批准chaincode definition。 此批准需要提交給排序服務,然後分發給所有對等節點。 此批准需要由組織管理員提交。 成功提交批准交易後,批准的定義將存儲在可供組織的所有對等節點使用的集合中。 因此,即使我們有多個對等節點,我們也只需為組織批准一次chaincode。
上圖中Org1 和 Org2 的組織管理員為其組織批准 MYCC 的chaincode definition。 chaincode definition包括Chaincode Name、Version和endorsement policy等參數。 由於兩個組織都將使用chaincode來背書交易,因此兩個組織的批准的 chaincode definition都需要包含 packageID。
第四步:將chaincode definition提交到渠道中
一旦足夠數量的渠道成員批准了chaincode definition,一個組織就可以將definition提交給渠道。在使用 peer CLI指命 將definition提交到渠道之前,我們可以使用 checkcommitreadiness 指令檢查提交的chaincode definition是否成功寫入帳本,具體取決於哪些渠道成員已批准定義。已寫入的交易提案首先發送給渠道成員的對等節點,這些渠道成員會查詢自己組織已批准的chaincode definition,如果他們的組織批准,則會對這個chaincode definition背書。然後將交易發送給排序服務,然後將chaincode definition提交給渠道。這個已提交的definition transaction需要以組織管理員(Organization Administrator)的身份提交。
需要批准definition才能成功提交到渠道的組織數量是由Channel/Application/LifecycleEndorsement策略來控制。預設情況下,此政策要求渠道中的大多數組織認可交易。 LifecycleEndorsement 政策與chaincode背書政策是分開的。例如,即使chaincode背書政策只需要一個或兩個組織的簽名,大多數渠道成員仍然需要根據預設政策批准chaincode definition。提交channel definition時,我們需要針對渠道中足夠多的對等組織來滿足我們的 LifecycleEndorsement 政策。更多Fabric policy(政策)介紹可參閱此篇文章。
您還可以將 Channel/Application/LifecycleEndorsement 政策設置為簽名政策(signature policy),並明確指定渠道上可以批准chaincode definition的組織群。這允許我們建立一個渠道,其中選定一些組織充當cahincode 管理員並管理渠道使用的業務邏輯。如果我們的渠道擁有大量的組織混合在一起,我們也可以使用簽名政策,這些組織無法批准chaincode definition或對chaincode進行背書,並可能因此無法讓渠道能達成多數共識的結果。
來自 Org1 或 Org2 的一位組織管理員將chaincode definition提交到渠道。渠道上的定義不包括 packageID。
組織可以在不安裝chaincode package的情況下批准chaincode definition。如果組織不需要使用chaincode,他們可以批准沒有package identifier的chaincode definition,以確保有達成生命週期背書政策。
將chaincode definition提交到渠道後,chaincode container將在有安裝了chaincode的所有對等節點上啟動,允許渠道成員開始使用chaincode。chaincode container可能需要幾分鐘才能啟動。我們可以使用chaincode definition來要求呼叫 Init 函數來初始化chaincode。如果呼叫 Init 函數,則chaincode 的第一次呼叫必須是對 Init 函數的呼叫。 Init 函數的呼叫受chaincode背書政策的約束。
一旦在渠道上定義了 MYCC,Org1 和 Org2 就可以開始使用chaincode。 每個對等節點上的chaincode的第一次呼叫會啟動該對等節點上的Chaincode container。
升級 Chaincode
我們可以使用與安裝和啟動chaincode相同的 Fabric lifecycle process來升級chaincode。 我們可以升級chaincode binaries,或只更新chaincode 政策。 以下為升級chaincode的步驟:
步驟一: 重新打包chaincode
Org1 和 Org2 升級chaincode binaries並重新打包chaincode。 兩個組織都使用不同的package label。
步驟二:將新的chaincode package安裝在對等節點上
安裝新的chaincode package會產生一個package ID,我們需要修改chaincode definition。 另外還需要修改chaincode版本,lifecycle process使用該版本來檢查chaincode binaries是否已升級。
Org1 和 Org2 將新的package安裝到它們的對等節點上。 這個安裝作業會建立一個新的 "packageID"。
步驟三:批准新的chaincode definition
上面的步驟提到如果是要升級chaincode binaries時,我們就需要修改chaincode 版本與package ID。 但如果我們只是要修改chaincode背書政策,就不需重新打包chaincode binaries。 渠道成員只需批准新政策的chaincode definition。 新的chaincode definition需要將定義中的Sequence variable加1。
Org1 和 Org2 的組織管理員為其各自的組織批准新的chaincode definition。 新的chaincode definition引用新的 packageID 並修改chaincode版本。 由於這是chaincode的第一次更新,因此Sequence從1變成2。
步驟三:將chaincode definition提交至渠道中
當足夠數量的渠道成員批准新的chaincode definition時,一個組織可以提交新的chaincode definition升級渠道中的chaincode。 作為lifecycle process的一部分,這裡沒有單獨升級的指令。
提交chaincode definition後,一個含有最新的chaincode binaries的容器就會產生。 如果我們在chaincode definition中請求執行 Init 函數,則需要在新的chaincode definition成功提交後再次呼叫 Init 函數來初始化升級後的chaincode。 如果我們更新了chaincode definition而沒有修改chaincode版本,則原本的cahincode容器將保持不變,也不需呼叫 Init 函數。也就是說如果chaincode版本有變就一定要呼叫Init 函數。
Fabric chaincode生命週期使用chaincode definition中的Sequence來定義是否升級。 所有渠道成員都需要將sequence的號碼加1,這樣才是批准chaincode definition才會升級chaincode。 version 參數用來追蹤chaincode binaries,只有在升級chaincode binaries時才需要修改。
部署的範例場景
以下範例說明如何使用 Fabric chaincode lifecycle來管理渠道和chaincode。
場景一:加入渠道
新組織可以加入已經有chaincode definition的渠道,並在安裝chaincode package和批准已經提交到渠道的chaincode definition後開始使用chaincode。
批准cahincode definition後,新加入的組織可以在package安裝到其對等節點後開始使用cahincode。 這個cahincode definition不需要再次提交。 如果背書政策設置為需要大多數渠道成員背書的預設政策,則背書政策將自動更新以包含新加入的組織。
場景二:更新背書政策
我們可以使用chaincode definition來更新背書政策,而無需重新打包或重新安裝chaincode。 渠道成員可以使用新的背書政策來批准chaincode definition並將其提交給渠道。
上圖中,Org1、Org2 和 Org3 批准新的背書政策,要求所有組織都要背書這一個新的背書政策。 它們將definition sequence從1增加到2,但不需要更新chaincode version。
新的背書政策將在新的cahincode definition提交到渠道後生效。 渠道成員不必通過呼叫chaincode或執行 Init 函數來啟動新的chaincode容器來更新背書政策。
場景三:批准definition而無需安裝chaincode
我們可以在不安裝chaincode package的情況下批准chaincode definition。 這允許我們在將chaincode definition提交到渠道之前就對其進行背書,即使我們需要要這個chaincode來背書交易或查詢帳本資料。 我們需要批准與渠道的其他成員相同的參數,但不需要將 packageID 作為chaincode definition的一部分,因為我們沒有要安裝chaincode package。
上圖中,Org3 不安裝chaincode package。 因此,他們不需要提供 packageID 作為chaincode definition的一部分。 但是,Org3 仍然可以認可已經提交給渠道的 MYCC 的chaincode definition。
場景四:一個組織不同意chaincode definition
不批准一個已提交到渠道的chaincode definition的組織無法使用該chaincode。 未批准chaincode definition或批准不同的chaincode definition的組織將無法在其對等節點上執行chaincode definition。
上圖中,Org3 批准一個跟 Org1 和 Org2 不同的背書政策的chaincode definition。 因此,Org3 無法在渠道上使用 MYCC chaincode。 但是,Org1 或 Org2 仍然可以獲得足夠的背書來將chaincode definition提交到渠道並使用chaincode,因為政策中3個組織只要有兩個同意就可以。 來自chaincode的交易仍將被加入到帳本中並存儲在 Org3 對等節點上。 但是,Org3 將無法為這個chaincode產生的交易背書。
組織可以批准具有任何sequence number或version的新的chaincode definition。 這允許我們批准已經提交到渠道的chaincode definition並開始使用chaincode。 我們還可以批准新的chaincode definition,以糾正在批准或打包chaincode過程中所犯的任何錯誤。
場景五:渠道不同意這一個chaincode definition
如果渠道上的組織不同意chaincode definition,則無法將chaincode definition提交給渠道。 所有渠道成員都無法使用這一個chaincode。
上圖中,Org1、Org2 和 Org3 都批准不同的chaincode definition。 結果,渠道中的任何成員都無法獲得足夠的背書來向渠道提交chaincode definition。 任何渠道成員都無法使用chaincode。
場景五:組織安裝不同的chaincode package
每個組織在批准chaincode definition時都可以使用不同的 packageID。 這允許渠道成員安裝使用相同背書政策的不同chaincode binaries,並讀取和寫入在相同chaincode namespace的資料。
組織可以使用此功能來安裝包含特定於其組織的業務邏輯的智慧合約。 每個組織的智慧合約都可以包含該組織在其對等節點認可交易之前需要的額外驗證。 每個組織還可以編寫代碼,幫助將智慧合約與其現有系統中的資料整合。
場景六:用一個package製作多個chaincode
我們可以通過批准和提交多個chaincode definitions,使用一個chaincode package在渠道上建立多個chaincode instance。 每個chaincode definitions都需要指定不同的chaincode名稱。 這允許我們在渠道上運行智慧合約的多個insatnce,但讓合約受制於不同的背書政策。
Org1 和 Org2 使用 MYCC_1 chaincode package來批准和提交兩個不同的chaincode definition。 結果,兩個對等節點都有兩個chaincode容器在它們的對等節點上運行。 MYCC1 的背書政策為 二分之一,而 MYCC2 的背書政策為 三分之二。