本文基於Hyperledger Fabric 1.4版本。
官方文檔地址:傳送門
動態添加一個組織到Fabric網絡中也是一個比較重要的功能。官方文檔寫的已經很詳細了,有能力的盡量還是看官方文檔,本文只是根據官方文檔進行整理同時兼翻譯。
1.前提條件
這個不再解釋了,前提條件自然是搭建Fabric的環境了並跑通官方的例子,具體的看這里.
2.啟動網絡
還是以官方的byfn
為例好了,不多說,對Fabric有一定了解的都能明白,不明白的看上面文檔:
./byfn.sh up
#或者是
./byfn.sh up -s couchdb
#區別不大,只不過換了一個數據庫而已,對本文內容沒多少關系
動態添加組織官方腳本自動化操作就簡單執行以下命令:
./eyfn.sh up
本文重點不在這里,因為自動化操作省略了所有的內容,固然簡單,但是仍然不懂其中過程。所以本文的重點還是下一部分,手動地一步一步完成動態增加組織。
3手動添加組織到網絡中
byfn
網絡中的節點為:
- Order -> orderer.example.com
- Org1 -> peer0.org1.example.com
- Org1 -> peer1.org1.example.com
- Org2 -> peer0.org2.example.com
- Org2 -> peer1.org2.example.com
而我們要添加的為:
- Org3 -> peer0.org3.example.com
- Org3 -> peer1.org3.example.com
在這里,我們假設工作目錄在$GOPATH/.../fabric-samples/first-network
文件夾。上面的五個節點也通過
./byfn.sh up
命令成功啟動。
Fabric網絡的啟動過程總的來說沒有幾步(錨節點那部分先省略掉,對本文沒有影響):
- 為每一個節點生成證書文件
- 生成系統通道的創世區塊(也是配置文件)
- 生成通道配置文件
- 啟動節點
- 根據通道配置文件創建通道生成應用通道創世區塊
- 加入通道
- ...
根據這個流程來考慮動態增加節點:
- 首先為每一個節點生成證書文件是肯定要做的。
- 第二步生成創世區塊(系統通道配置文件)是不需要的
- 第三步生成應用通道配置文件需要變為更新應用通道配置文件
- 第四步啟動節點步驟不變
- 第五步創建通道也不需要了,直接到第六步加入通道
- ...(網絡啟動之后的步驟最后再說)
既然分析完了,我們只要按照步驟完成就可以了。
3.1生成證書文件
怎么生成證書文件呢,這個直接使用官方的文件就可以了,當然有定制化需求的請自行修改。文件在工作目錄下的org3-artifacts
文件夾下的org3-crypto.yaml
文件。
這一步比較簡單,直接執行命令行工具就可以了,當然對Fabric CA
比較熟悉的也可以采用手動生成證書的方法,本文為了簡便,直接使用工具生成:
cd org3-artifacts
cryptogen generate --config=./org3-crypto.yaml
完成之后在org3-artifacts
目錄下生成一個crypto-config
文件夾。里面就是需要添加的新組織的證書文件。
如果網絡開啟TLS
的話,在多機環境下還需要將Orderer
的TLS
根證書拷貝一份過來用於之后的與Orderer
節點進行通信,而單機環境下也可以直接將Orderer
的TLS
根證書掛載到之后需要啟動的Org3
的容器內部。而本文采用和官方文檔相同的方法,直接拷貝文件:
cd ../ && cp -r crypto-config/ordererOrganizations org3-artifacts/crypto-config/
3.2更新通道配置文件
接下來第三步:更新通道配置文件,可以分為以下步驟:
- 獲取網絡中當前通道之前最新的配置區塊
- 把需要更新的內容添加進去
- 把最新的配置文件更新到網絡中
3.2.1獲取最新的配置區塊
看一下第一步獲取網絡中之前最新的配置區塊,如何獲取呢,自然是通過網絡中現有的節點進行獲取,並且使從peer
節點向Orderer
節點發起通信獲取配置區塊。
首先進入cli
容器:
docker exec -it cli bash
配置需要的環境變量:
export ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
export CHANNEL_NAME=mychannel
如果操作中途退出了cli
容器,那么再次進入時都需要重新配置環境變量.
接下來獲取之前最新的配置區塊:
peer channel fetch config config_block.pb -o orderer.example.com:7050 -c $CHANNEL_NAME --tls --cafile $ORDERER_CA
peer channel fetch
: 從指定的通道獲取具體的區塊並寫入文件。config
:指定獲取的區塊是配置區塊.(Fabric網絡中區塊類型可分為普通交易區塊和配置區塊)config_block.pb
:將配置區塊寫入到這個文件中-o
:指定向具體的排序節點發起通信-c
:指定通道名稱--tls
:如果開啟了TLS
則需要指定這個參數--cafile
:TLS
根證書文件
執行完畢后命令行會打印這些信息:
UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
UTC [cli.common] readBlock -> INFO 002 Received block: 4
UTC [cli.common] readBlock -> INFO 003 Received block: 2
UTC [channelCmd] fetch -> INFO 004 Retrieving last config block: 2
可以看到mychannel
通道中共生成了5個區塊(創世區塊序號為0).但是最新的配置區塊序號為2:
- 配置區塊0:創世區塊
- 配置區塊1:組織一的錨節點更新
- 配置區塊2:組織二的錨節點更新
- 普通區塊3:實例化鏈碼
- 普通區塊4:調用鏈碼
而本文獲取到了最新的配置區塊也是是區塊2,並將該區塊寫入到了config_block.pb
文件中。
3.2.2將配置信息添加到配置文件中
我們已經獲取到了最新的配置文件,接下來如何更新它呢,因為區塊內容是編碼過的,而且還包括區塊頭,元數據以及簽名等信息,對更新配置是用不到的。所以需要先將區塊進行解碼成我們可讀的文件,而且為了簡單化,可以將不相關的區塊頭等信息去掉(當然不去掉也沒有問題)。
這里用到了兩個工具:Fabric官方的命令行工具configtxlator
,以及jq
工具:
configtxlator
工具可以幫助我們進行編解碼轉換
jq
工具和Linux
中的grep
,awk
命令較為相似,都是對數據進行處理的(當然不使用這個工具也沒什么問題,只不過需要手動修改數據而已)。
接下來就是將區塊信息解碼去除不相關的信息后並以json
格式保存到文件中:
configtxlator proto_decode --input config_block.pb --type common.Block | jq .data.data[0].payload.data.config > config.json
proto_decode
:解碼操作--input
:需要解碼的文件作為輸入--type
:輸入文件的類型
解碼后通過jq
工具提取需要的數據並保存到了config.json
文件中。
接下來呢,就是將組織三的配置信息寫到這里面,組織三的配置信息呢?我們還沒有生成它,之前只是為組織三生成了證書文件。所以我們還需要生成組織三的配置信息。
同樣的,用於生成配置信息的源文件官方也給了,在工作目錄下的org3-artifacts
文件夾下的configtx.yaml
文件。
因為上一步我們將通道內的最新的配置文件轉換為了json
格式,所以這里我們也需要將這個文件內的配置信息轉換為json
格式:
#打開新的終端進入以下目錄中
cd $GOPATH/.../fabric-samples/first-network/org3-argifacts/
#指定配置文件所在路徑 或者是通過-configPath路徑指定
export FABRIC_CFG_PATH=$PWD
#直接通過工具將配置信息寫到org3.json文件中。
configtxgen -printOrg Org3MSP > ../channel-artifacts/org3.json
現在讓我們回到之前的終端繼續操作,將剛剛生成的org3.json
文件添加到config.json
文件中,通過jq
工具:
jq -s '.[0] * {"channel_group":{"groups":{"Application":{"groups": {"Org3MSP":.[1]}}}}}' config.json ./channel-artifacts/org3.json > modified_config.json
這一行命令就是將org3.json
這個文件添加到config.json
文件的channel_group->groups->Application->groups->Org3MSP
下,並保存到modified_config.json
文件。
接下來就是獲取原始配置文件和新的配置文件的不同點了,官方文檔的意思是只保留組織3的定義以及一個指向組織1與組織2的高級指針,因為沒有必要連同之前的配置文件一起更新,所以只需要一個指針指向原配置(個人理解)。
具體的操作方法是將上面兩個json
文件編碼回去,然后使用configtxlator
工具進行比較更新。
操作命令:
config.json
文件,編碼后輸出到config.pb
文件。
configtxlator proto_encode --input config.json --type common.Config --output config.pb
modified_config.json
文件,編碼后輸出到modified_config.pb
文件。
configtxlator proto_encode --input modified_config.json --type common.Config --output modified_config.pb
- 計算兩個文件的差異並輸出到
org3_update.pb
文件:
# --original 指定原配置文件 --updated 指定新配置文件
configtxlator compute_update --channel_id $CHANNEL_NAME --original config.pb --updated modified_config.pb --output org3_update.pb
接下來還需要做一件事,就是封裝一個更新配置的文件,將org3_update.pb
寫進去,畢竟向Fabric添加組織需要更新Fabric的配置,自然是需要將配置文件按照Fabric規定的文件類型封裝好才能更新網絡。
然后封裝配置信息又會涉及到一些額外的信息,說簡單點就是Fabric規定的文件類型的標識符之類的,所以需要我們再次解碼,然后添加這些額外的信息進去:
configtxlator proto_decode --input org3_update.pb --type common.ConfigUpdate | jq . > org3_update.json
添加額外的數據:
echo '{"payload":{"header":{"channel_header":{"channel_id":"mychannel", "type":2}},"data":{"config_update":'$(cat org3_update.json)'}}}' | jq . > org3_update_in_envelope.json
到最后一步配置更新消息就完成了,那就是將文件以特定的文件類型封裝起來:
configtxlator proto_encode --input org3_update_in_envelope.json --type common.Envelope --output org3_update_in_envelope.pb
3.2.3更新應用通道配置文件
配置更新消息已經處理好了,接下來就是更新到網絡中了。在此時,新添加的組織信息還沒有更新進去,所以還是需要使用之前的組織將配置進行更新,首先就是需要帶有Admin
身份的多數節點進行簽名(策略這塊以后再講),所以需要每個組織中各一個節點進行簽名,首先是peer0.org1
,由於之前打開的cli
容器默認身份就是peer0.org1
,所以不需要配置環境變量直接進行簽名:
peer channel signconfigtx -f org3_update_in_envelope.pb
接下來是組織二的節點:
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
export CORE_PEER_ADDRESS=peer0.org2.example.com:9051
實際上我們只需要進行配置文件更新就行了,因為在配置更新操作中如果沒有簽名默認會先進行簽名的:
peer channel update -f org3_update_in_envelope.pb -c $CHANNEL_NAME -o orderer.example.com:7050 --tls --cafile $ORDERER_CA
如果命令行日志打印出一下內容說明更新通道配置成功:
UTC [channelCmd] update -> INFO 002 Successfully submitted channel update
在此時,區塊5將會生成並寫到每一個節點的賬本,比如我們查看peer0.org1
的日志信息,可以看到以下內容:
#打開一個新的命令行
docker logs -f peer0.org1.example.com
##日志內容
UTC [gossip.privdata] StoreBlock -> INFO 07c [mychannel] Received block [5] from buffer
...
UTC [gossip.gossip] JoinChan -> INFO 07d Joining gossip network of channel mychannel with 3 organizations
...
UTC [committer.txvalidator] Validate -> INFO 082 [mychannel] Validated block [5] in 238ms
...
UTC [kvledger] CommitWithPvtData -> INFO 08b [mychannel] Committed block [5] with 1 transaction(s) in 238ms
...
3.4啟動節點並加入通道
到這里,組織三的信息已經更新到網絡中了,所以我們可以啟動組織三的節點了:
docker-compose -f docker-compose-org3.yaml up -d
啟動成功后進入組織三的cli
容器:
docker exec -it Org3cli bash
第一步還是配置環境變量,還記得一開始我們將排序節點的根證書復制的那一步吧,現在就派上用場了:
export ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
export CHANNEL_NAME=mychannel
#檢查一下是否配置成功
echo $ORDERER_CA && echo $CHANNEL_NAME
沒問題的話就可以進行加入通道了,如果加入通道呢,肯定是需要創世區塊了,所以需要從排序節點處獲取它:
#這里不能用peer channel fetch config ... 否則獲取到的是剛生產的區塊5,只有使用創世區塊才能加入通道
peer channel fetch 0 mychannel.block -o orderer.example.com:7050 -c $CHANNEL_NAME --tls --cafile $ORDERER_CA
###命令行打印出一下內容
UTC [cli.common] readBlock -> INFO 002 Received block: 0
最后加入通道:
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.example.com/peers/peer1.org3.example.com/tls/ca.crt && export CORE_PEER_ADDRESS=peer1.org3.example.com:12051
peer channel join -b mychannel.block
3.5測試
一切都沒有問題,就差測試鏈碼能不能用了。
首先這里注意一點,在新的組織添加進通道之前,鏈碼的背書策略並沒有涉及到新的組織,所以之前的鏈碼對於新的組織是不能使用的,包括查詢,調用以及更新操作。但是安裝鏈碼是可以用的(前提是版本和鏈碼名稱不能全部相同),所以我們需要通過之前的組織更新鏈碼,並制定背書策略將新的組織添加進來。
切換到組織一的節點:
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=peer0.org1.example.com:7051
安裝新版本的鏈碼:
peer chaincode install -n mycc -v 2.0 -p github.com/chaincode/chaincode_example02/go/
## 更新背書策略將新的組織添加進來
peer chaincode upgrade -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA -C $CHANNEL_NAME -n mycc -v 2.0 -c '{"Args":["init","a","90","b","210"]}' -P "OR ('Org1MSP.peer','Org2MSP.peer','Org3MSP.peer')"
#測試一下更新是否成功
peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'
## Query Result: 90
切換回組織三的節點容器:
docker exec -it Org3cli bash
export ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
export CHANNEL_NAME=mychannel
安裝鏈碼:
peer chaincode install -n mycc -v 2.0 -p github.com/chaincode/chaincode_example02/go/
安裝完測試一下:
peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'
# Query Result: 90
查詢沒問題,調用一下試試:
peer chaincode invoke -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA -C $CHANNEL_NAME -n mycc -c '{"Args":["invoke","a","b","10"]}'
再次查詢:
peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'
# Query Result: 80
沒問題了,到這里我們成功將組織三動態添加到網絡中了。
3.5更新組織三的錨節點
錨節點說簡單點就是用於跨組織通信的。初始的跨組織通信啟動信息需要通過錨節點的設置提供。在最后一小部分,說明一下如何更新組織三的錨節點。
和前面的步驟相似:
- 獲取最新的配置區塊
- 更新配置信息
- 將更新后的配置信息更新到鏈上。
3.5.1獲取最新的配置區塊
#還是之前的組織三的CLI容器,並且環境變量$CHANNEL_NAME,$ORDERER_CA需要提前配置好
peer channel fetch config config_block.pb -o orderer.example.com:7050 -c $CHANNEL_NAME --tls --cafile $ORDERER_CA
- 解碼配置信息為JSON格式,並去除用不到的信息:
configtxlator proto_decode --input config_block.pb --type common.Block | jq .data.data[0].payload.data.config > config.json
- 將組織三的錨節點的配置信息寫進去並保存為一個新的文件:
jq '.channel_group.groups.Application.groups.Org3MSP.values += {"AnchorPeers":{"mod_policy": "Admins","value":{"anchor_peers": [{"host": "peer0.org3.example.com","port": 11051}]},"version": "0"}}' config.json > modified_anchor_config.json
- 將原有的配置信息與新的配置信息編碼為
common.Config
格式:
configtxlator proto_encode --input config.json --type common.Config --output config.pb
configtxlator proto_encode --input modified_anchor_config.json --type common.Config --output modified_anchor_config.pb
- 計算兩個文件的差異:
configtxlator compute_update --channel_id $CHANNEL_NAME --original config.pb --updated modified_anchor_config.pb --output anchor_update.pb
- 再次解碼:
configtxlator proto_decode --input anchor_update.pb --type common.ConfigUpdate | jq . > anchor_update.json
- 添加頭部信息:
echo '{"payload":{"header":{"channel_header":{"channel_id":"'$CHANNEL_NAME'", "type":2}},"data":{"config_update":'$(cat anchor_update.json)'}}}' | jq . > anchor_update_in_envelope.json
- 編碼為
Fabric
可讀的配置文件類型:
configtxlator proto_encode --input anchor_update_in_envelope.json --type common.Envelope --output anchor_update_in_envelope.pb
- 配置文件寫完了,更新上去:
peer channel update -f anchor_update_in_envelope.pb -c $CHANNEL_NAME -o orderer.example.com:7050 --tls --cafile $ORDERER_CA
到這里錨節點更新完了,剩下的自行測試。