前言
鏈碼不需要"實例化",可以同時運行java和go鏈碼,同一個鏈碼多次實例化。
<strong>之前發的這篇 被頭條彈窗了,但是寫的不夠詳細,今天特別把這篇文章補充完整。 此外歡迎大家關注我的github:https://github.com/haojunsheng
想要了解上面的特性,請看下面的分解。
Fabric 2.0 在2020年1月29號終於release了,我們來看下有哪些新的變化。
主要體現在:對新應用和隱私的支持,增強了智能合約的管理,增加了對節點的操作。
需要注意的是,只能由fabric-1.4.x升級到2.0。
ps:在網上有個翻譯,那一字一句的翻譯,真的讓我很難受。
下面我嘗試使用自己的理解來解讀。
1. 智能合約的去中心化管理
fabric 2.0引入了智能合約的去中心化管理,在此之前,鏈碼的安裝和實例化都是一個由組織在操作,現在則發生了變化。新的鏈碼的生命週期中,只有多個組織達成了共識,才可以和賬本才可以進行交互。
· 多個組織必須同意鏈碼的參數 。在2.0之前,一個組織可以為channel中的所有成員設置鏈碼的參數(例如實例化鏈碼時指定的背書策略),拒絕安裝鏈碼的組織將不能參與鏈碼的調用。在2.0中,同時提供中心化的模型和去中心化的模型。
· 鏈碼的升級更加安全。在之前的鏈碼生命週期中,一個組織即可升級鏈碼。在新的版本中,需要別的組織進行同意。
· 簡化了背書策略和private data的更新。我們不必重新打包/安裝鏈碼即可更新背書策略和private data集合的配置。同時我們設置了默認的背書策略,默認的背書策略在我們增加或者刪除組織的時候會自動生效。
· 打包鏈碼:會打包為tar文件,方便進行閱讀。
· 一次打包多次複用:之前鏈碼是通過名字+版本號來決定的,現在一次打包生成多個名字,可以多次安裝(在相同或者不同的通道上)
· 不需要所有人的同意即可打包chaincode:組織可以擴展鏈碼,不需要所有人的同意,只要符合背書要求,這些交易即可被更新到賬本中。這樣做的好處是,不需要所有人的同意,即可小規模的修改鏈碼的bug。
1.1 鏈碼新的生命週期
1.1.1 鏈碼的安裝和定義
新的鏈碼週期要求組織對鏈碼的名字,版本,背書策略達成一致,需要執行以下四步,但不需要每個組織都執行:
· 打包鏈碼:一個或者每一個組織完成。
· 自己的節點安裝鏈碼。每個組織要執行,因為需要交易或者查詢賬本。
· 同意鏈碼的定義:需要滿足channel LifecycleEndorsment(默認是大多數)策略的足夠數量的組織來執行。
· 提交chaincode的定義:第一個收集到足夠數量的節點來執行。
下面來詳細的看上面4步:
1.1.1.1 打包鏈碼
鏈碼在安裝前需要打包為tar文件。我們可以使用peer命令,node sdk,或者第三方工具。
第三方的打包工具需要滿足以下要求:
· 鏈碼以tar.gz結尾;
· tar文件需要包含2個文件(不是目錄),元文件Chaincode-Package-Metadata.json和chaincode文件。
· Chaincode-Package-Metadata.json文件長成下面這樣。
· {"Path":"fabric-samples/chaincode/fabcar/go","Type":"golang","Label":"fabcarv1"}
一個demo如下。2個組織不需要使用相同的名字。
![一文帶大家瞭解Fabric 2.0的新特性(詳細版)](http://p2.ttnews.xyz/loading.gif)
1.1.1.2 安裝鏈碼
每個節點上都需要安裝。強烈建議每個組織只打包一次鏈碼,然後把該鏈碼安裝在該組織的所有節點上。如果一個channel想要保證所有的組織運行相同的鏈碼,那麼打包命令應該由一個組織來進行。
安裝成功後會返回MYCC_1:hash.這樣的格式,我們需要進行保存,方便後面的使用,如果忘記了,可以進行查詢。
![一文帶大家瞭解Fabric 2.0的新特性(詳細版)](http://p2.ttnews.xyz/loading.gif)
1.1.1.3 同意鏈碼的定義
我的理解是,在上面,每個組織都給chaincode起了一個名字,這樣,在實際中是無法使用的,所以現在大家來投票來確定一個統一的名字,包含下面的參數:
· 名字
· 版本:chaincode打包的時候生成的。
· Sequence:用來追蹤鏈碼的升級過程。是自增的。
· 背書策略:哪些組織可以執行可以驗證交易。
· Collection Configuration:私有數據相關。
· Initialization:原來chaincode的默認的init函數不執行,現在可以了。
· ESCC/VSCC Plugins
1.1.1.4 提交鏈碼的定義
一旦得到了絕大多數成員的同意,就可以提交鏈碼的定義了。
我們可以使用checkcommitreadiness命令來檢查是否已經有鏈碼的定義了,首先會發送給所有的peer節點,在發送給order節點。提交必須是組織的管理員來完成的。
Channel/Application/LifecycleEndorsement來管理認可的組織的數量,默認是大多數。LifecycleEndorsement和chaincode的背書策略是分離的,沒有任何關係的。
即使一個組織沒有安裝鏈碼,仍然可以響應鏈碼的定義。
當鏈碼的定義被確認後,將會在所有安裝鏈碼的節點上啟動鏈碼容器。如果我們在定義鏈碼的時候要求使用init函數,那麼init函數將會被調用。
1.1.2 鏈碼的升級
升級和安裝類似,我們既可以升級鏈碼的內容,還可以升級鏈碼的背書策略。
1. 打包鏈碼。只有在升級鏈碼內容的時候需要。
·
·
1. 安裝新鏈碼。同上。
·
·
1. 鏈碼定義投票。sequence將會自增1。
·
·
1. 提交定義。
·
·
將會啟動新的鏈碼容器。
1.1.3 完整的demo
下面是chaincode的比較完整的操作。來自。
<code>## at first we package the chaincodepackageChaincode 1## Install chaincode on peer0.org1 and peer0.org2echo "Installing chaincode on peer0.org1..."installChaincode 1echo "Install chaincode on peer0.org2..."installChaincode 2## query whether the chaincode is installedqueryInstalled 1## approve the definition for org1approveForMyOrg 1## check whether the chaincode definition is ready to be committed## expect org1 to have approved and org2 not tocheckCommitReadiness 1 "\"Org1MSP\": true" "\"Org2MSP\": false"checkCommitReadiness 2 "\"Org1MSP\": true" "\"Org2MSP\": false"## now approve also for org2approveForMyOrg 2## check whether the chaincode definition is ready to be committed## expect them both to have approvedcheckCommitReadiness 1 "\"Org1MSP\": true" "\"Org2MSP\": true"checkCommitReadiness 2 "\"Org1MSP\": true" "\"Org2MSP\": true"## now that we know for sure both orgs have approved, commit the definitioncommitChaincodeDefinition 1 2## query on both orgs to see that the definition committed successfullyqueryCommitted 1queryCommitted 2## Invoke the chaincodechaincodeInvokeInit 1 2sleep 10## Invoke the chaincodechaincodeInvoke 1 2# Query chaincode on peer0.org1echo "Querying chaincode on peer0.org1..."chaincodeQuery 1/<code>
下面來看圖:
加入通道:如果一個channel已經有了定義好的chaincode,那麼新加入的組織在安裝鏈碼後可以直接使用原來的名字。
如果背書策略是默認的大多數,那麼背書策略會自動更新,把新的組織計算在內。
升級背書策略
我們不必重新打包或者安裝鏈碼即可升級背書策略。channel中的成員會重新生成一個chaincode定義。
新的背書策略在新的鏈碼定義通過後,即可生效,我們不必重啟容器即可更新背書策略。
無法安裝鏈碼即可同意鏈碼的定義:
不同意鏈碼定義的組織將不能使用鏈碼:
上圖中的組織三不可以使用鏈碼。
channel不認可鏈碼的定義:這裡比較繞,說的是channel中的組織沒有對鏈碼的定義達成共識。
組織安裝了不同類型的鏈碼:這裡的意思是說只要鏈碼產生相同的讀寫集,那麼可以安裝不同語言寫的鏈碼,比如java和go。
一次打包,多次使用:
我們可以打包一次,給鏈碼創建不同的定義,從而運行多個智能合約實例(但是背書策略要有區別)。
1.1.4 比較
做了個表格,把舊的聲明週期和新的進行了比較。
2. private data增強
Fabric 2.0增強了private data,我們不需要創建私有數據集合即可使用私有數據。做了以下增強:
· 私有數據的共享和驗證。當私有數據向非原來的集合中的成員共享時,該成員可以通過GetPrivateDataHash() 函數來驗證hash是不是和鏈上保存的hash一致。
· 集合級別的背書策略。我們可以使用背書策略來定義私有數據集合。
· 每個組織都有暗含的私有數據集合。
2.1 什麼是private data 集合?
在同一個channel中,A組織的數據不想給其他的組織看的數據。從v1.2開始,創造了private data collections,我們可以背書,提交和查詢私有數據,在不創建一個獨立channel的情況下。
private data collections由兩部分組成:
· 實際的私有數據。在不同的節點間通過gossip協議來發送。私有數據存儲在授權的peer節點上的sidedb數據庫中,可以通過Chaincode來訪問。order節點無法看到private data。注意,必須配置錨節點信息,設置COREPEERGOSSIP_EXTERNALENDPOINT變量。
· 私有數據的hash,會寫入到區塊鏈網絡中,其他人可以進行審計。
private-data.private-data
當集合中的成員需要把該私有數據向第三方共享時,第三方可以通過比較該數據的hash和鏈上保存的hash,看是否一致。
還有一些特殊情況,每個組織都可以創建一個私有數據集合,之後可以共享給其他成員。
我們把private data和channel進行一個比較。
· channel:所有的交易和賬本都是私密的。
· 私有數據集合:通道中組織的子集共享數據時。直接通過p2p來傳播每條具體的交易,而不是區塊,order節點無法看到真實的交易。
2.2 一個demo
有下面5個角色:
Farmer出售商品,Distributor分銷商負責把商品運到海外,Shipper負責在兩個角色之間運貨,Wholesaler批發商從distributors批發商品,Retailer零售商從shippers和wholesaler購買商品。
場景是:
· Distributor想和Farmer,Shipper共享數據,但是不想讓Retailer和wholesaler看到數據;
· Distributor賣給Retailer和wholesaler的價格不同;
· wholesaler和Retailer,Shipper之間也需要共享數據;
為了滿足上面的場景,我們不需要建立這麼多的channel,可以使用PDC。
· PDC1: Distributor, Farmer and Shipper
· PDC2: Distributor and Wholesaler
· PDC3: Wholesaler, Retailer and Shipper
上面場景下,peer節點的賬本如下,也稱為SideDB。
2.3 private交易流程
1. 客戶端發送提案給授權的背書節點,提案中加入transient 字段;
1. 私有數據存儲在transient data store(臨時的存儲在peer節點);
1. 背書節點發送提案響應到客戶端,響應的內容是private data的hash值;
1. 客戶端節點把hash值發送給order節點;
1. 在提交階段,授權的節點將會檢查策略,自己是否有權限訪問private data,如果有的話,將會檢查transient data store 字段,看看是否在背書階段拿到了private data。沒有的話,會從其他節點去拉取。在驗證和提交階段,private data將會被存儲到數據庫中,同時把transient data store 刪除。
2.4 私有數據的共享
我們可能會有把私有數據向其他組織或者其他集合共享的需求,接受方需要驗證hash:
· 只要滿足背書策略(fabric 2.0中,我們可以定義鏈碼級別,鍵和集合級別的背書策略),不需要是集合中的成員,即可訪問私有數據的鍵
· 我們可以使用GetPrivateDataHash()來驗證hash
在實際中,我們可能會創建大量的私有數據集合,這個不利於我們的維護。更好的情況是每個組織都是一個集合,然後共享就可以了。更好的是我們不必為此進行定義,因為在2.0中默認設置了。
2.4.1 私有數據共享模型
下面這個是每個組織一個集合的模型:
· 使用相應的公鑰來追蹤公共狀態的變化:
· 鏈碼訪問控制:我們可以在鏈碼實現訪問控制,指定哪些客戶端可以查看私有數據。
· 共享私有數據:通過hash來確認;
· 和其他集合共享私有數據:
· 可以把私有數據轉移到其他的集合。這個時候會刪除原來的集合。
· 在交易達成之前,可以使用私有數據進行預請求;
· 保護交易者的隱私
2.4.2 私有數據實例
把私有數據模型和鏈碼結合可以發揮出很大的作用,具體如下所示:
· 可以通過處於公共鏈碼狀態的UUID密鑰來跟蹤資產。僅記錄資產的所有權,關於資產的其他信息一無所知。
· 鏈碼將要求任何轉移請求都必須來自擁有權限的客戶,並且密鑰受基於狀態的認可約束,要求所有者組織和監管機構的同級必須認可任何轉移請求。
· 資產的所有者可以看到該資產的所有交易詳情,其他的組織只可以看到hash。
· 監管者可以保留私有數據。
具體的交易流如下所示:
1. 資產所有者和買家在線下達成交易價格;
1. 賣家需要證明資產的所有權。既可以線下提供私有數據的細節,也可以提供線上的憑證;
1. 賣家線上驗證hash;
1. 買家調用鏈碼記錄出價的細節到自己的private data中。監管者可能也需要記錄。
1. 賣家調用鏈碼轉移資產,需要資產和出價細節的隱私數據,需要賣家,買家,監管者參與,除此之外,還需要滿足背書策略;
1. 鏈碼會對上述信息進行驗證;
1. 賣家把公開的數據和私有數據的hash提交給order節點,打包成區塊;
1. 其他節點將會驗證是否滿足背書策略,私有數據的狀態是否被其他的交易更改;
1. 所有節點會進行記賬;
1. 至此交易完成,其他的節點可以查詢這筆資產的公開的信息,但無法獲取私有信息。
2.5 刪除私有數據
對於非常敏感的數據,比如政府要求的。我們可以從peer節點上徹底的刪除,只留下hash來證明該數據確實存在過。數據刪除後,無法從鏈碼進行查詢,其他的peer節點也不可查詢。
2.6 私有數據集合的定義
從fabric 2.0開始,在chaincode定義階段來進行定義:
· name:集合的名字;
· policy:private data的policy必須比鏈碼的背書策略更加廣泛,因為背書節點必須有private data才可以進行背書。比如一個channel裡面包含了10個組織,5個組織需要有private data的權限,背書策略可以指定為5箇中的三個;
· requiredPeerCount:在背書節點把提案響應返回到客戶端之前,最少把private data傳遞到其他節點的數量。不建議寫0,因為這樣的話,將會導致private data的丟失。
· maxPeerCount:如果設置為0,在背書階段,private data將不會傳播,在commit階段,數據才會傳播;
· blockToLive:私有數據的存活時間,到期自動刪除。設為0表示,永不刪除;
· memberOnlyRead:表示只有授權的人可以讀。
· memberOnlyWrite:
· endorsementPolicy:
3. 外部鏈碼啟動器
我們可以使用自己喜歡的方式來構建和啟動鏈碼,不必使用docker。
· 解除了對docker daemon的依賴。之前的fabric要求peer節點可以訪問到docker daemon,而這在生產環境不一定是現實的。
· 容器的替代品:我們不一定在使用容器了。
· 鏈碼作為外部的服務。之前鏈碼是被peer啟動的,現在鏈碼可以作為單獨的外部服務。
在Hyperledger Fabric 2.0之前,用於構建和啟動鏈碼的過程是peer節點實現的一部分,無法輕鬆自定義。必須使用特定的語言。這種方法限制了鏈碼的語言,必須依賴容器,chaincode無法作為單獨運行的服務。
從2.0開始,我們在peer的core.yaml中,加入了一個externalBuilder的配置來自定義自己的服務。
3.1 外部構建模型
fabric的構建器使用了。
外部構建和運行期由下面四個部分組成:
· bin/detect: 判斷是否由我們自定義的模型來運行。
· bin/build: 把打包後的鏈碼變為可執行版本。用來構建,編譯鏈碼。
· bin/release (optional): 提供chaincode的元數據。
· bin/run (optional): 運行鏈碼。
下面分別是四個腳本的內容:
detect:
<code>
build
<code>#!/bin/bashCHAINCODE_SOURCE_DIR="$1"CHAINCODE_METADATA_DIR="$2"BUILD_OUTPUT_DIR="$3"# extract package path from metadata.jsonGO_PACKAGE_PATH="$(jq -r .path "$CHAINCODE_METADATA_DIR/metadata.json")"if [ -f "$CHAINCODE_SOURCE_DIR/src/go.mod" ]; then cd "$CHAINCODE_SOURCE_DIR/src" go build -v -mod=readonly -o "$BUILD_OUTPUT_DIR/chaincode" "$GO_PACKAGE_PATH"else GO111MODULE=off go build -v -o "$BUILD_OUTPUT_DIR/chaincode" "$GO_PACKAGE_PATH"fi# save statedb index metadata to provide at releaseif [ -d "$CHAINCODE_SOURCE_DIR/META-INF" ]; then cp -a "$CHAINCODE_SOURCE_DIR/META-INF" "$BUILD_OUTPUT_DIR/"fi/<code>
release
<code>#!/bin/bashBUILD_OUTPUT_DIR="$1"RELEASE_OUTPUT_DIR="$2"# copy indexes from META-INF/* to the output directoryif [ -d "$BUILD_OUTPUT_DIR/META-INF" ] ; then cp -a "$BUILD_OUTPUT_DIR/META-INF/"* "$RELEASE_OUTPUT_DIR/"fi/<code>
run
<code>
3.2 配置外部的構建器和運行器
上面說了,這個是在core.yaml中配置的,一個demo如下所示:
chaincode: externalBuilders: - name: my-golang-builder path: /builders/golang environmentWhitelist: - GOPROXY - GONOPROXY - GOSUMDB - GONOSUMDB - name: noop-builder path: /builders/binary
4. CouchDB中使用了狀態數據庫緩存來提高性能
· 使用外部CouchDB狀態數據庫時,背書和驗證階段的讀取延遲歷來是性能瓶頸。
· fabric 2.0中,每個peer都進行了緩存,在core.yaml中的cacheSize來進行配置。
5. 基於Alpine來打包docker鏡像
從 v2.0 開始,Hyperledger Fabric Docker 鏡像將使用 Alpine Linux 作為基礎鏡像,這是一個面向安全的輕量級 Linux 發行版。這意味著 Docker 鏡像現在要小得多,提供更快的下載和啟動時間,以及佔用主機系統上更少的磁盤空間。Alpine Linux 的設計從一開始就考慮到了安全性,Alpine 發行版的最小化特性大大降低了安全漏洞的風險。
6. Release notes
新特性
FAB-11237:去中心化的智能合約管理
新的應用程序模式:
· FAB-10889: Implicit org-specific collections
·
FAB-15066: Endorsement policies for collections· FAB-13581: memberOnlyWrite collection configuration option
· FAB-13527: GetPrivateDataHash chaincode API
· FAB-12043: Option to include private data in block events
FAB-103: State database cache for CouchDB
重要變化
· FAB-5177: The ccenv build image no longer includes the shim
· FAB-15366: Logger removed from chaincode shim
· FAB-16213: The go chaincode entities extension has been removed
· FAB-12075: Client Identity (CID) library has moved
· FAB-14720: Support for CAR chaincode package format removed
· FAB-15285: Support for invoking system chaincodes from user chaincodes has been removed.
· FAB-15390: Support for peer's Admin service has been removed.
·
FAB-16303: GetHistoryForKey returns results from newest to oldest· FAB-16722: The 'provisional' genesis method of generating the system channel for orderers has been removed.
· FAB-16477 and FAB-17116: New configuration for orderer genesismethod and genesisfile
· FAB-15343: System Chaincode Plugins have been removed.
· FAB-11096: Docker images with Alpine Linux
· FAB-11096: Bash not available in Docker images with Alpine Linux
· 使用sh或者ash
· FAB-15499: Ledger data format upgrade
· FAB-16866: Chaincode built upon installation on peer
· FAB-15837: Orderer FileLedger location moved if specified with relative path
· FAB-14271: Policies must be specified in configtx.yaml
· FAB-17000: Warn when certificates are about to expire
· FAB-16987: Go version has been updated to 1.13.4.
廢除
· FAB-15754: The 'Solo' consensus type is deprecated.
· FAB-16408: The 'Kafka' consensus type is deprecated.
· FAB-7559: Support for specifying orderer endpoints at the global level in channel configuration is deprecated.
· FAB-17428: Support for configtxgen flag --outputAnchorPeersUpdate is deprecated.
閱讀更多 區塊鏈指北 的文章