Fabric 0.6與1.0+
Fabric結(jié)構(gòu)
Fabric 0.6的特點(diǎn)
- 結(jié)構(gòu)簡(jiǎn)單: 應(yīng)用-成員管理-Peer的三角形關(guān)系,主要業(yè)務(wù)功能全部集中于Peer節(jié)點(diǎn);
- 架構(gòu)問(wèn)題:由于peer節(jié)點(diǎn)承擔(dān)了太多的功能,所以帶來(lái)擴(kuò)展性、可維護(hù)性、安全性、業(yè)務(wù)隔離等方面的諸多問(wèn)題,所以0.6版本在推出后,并沒(méi)有大規(guī)模被行業(yè)使用,只是在一些零星的案例中進(jìn)行業(yè)務(wù)驗(yàn)證
Fabric 1.0
- 分拆Peer的功能,將Blockchain的數(shù)據(jù)維護(hù)和共識(shí)服務(wù)進(jìn)行分離,共識(shí)服務(wù)從Peer節(jié)點(diǎn)中完全分離出來(lái),獨(dú)立為Orderer節(jié)點(diǎn)提供共識(shí)服務(wù);
- 基于新的架構(gòu),實(shí)現(xiàn)多通道(channel)的結(jié)構(gòu),實(shí)現(xiàn)了更為靈活的業(yè)務(wù)適應(yīng)性(業(yè)務(wù)隔離、安全性等方面)
- 支持更強(qiáng)的配置功能和策略管理功能,進(jìn)一步增強(qiáng)系統(tǒng)的靈活性和適應(yīng)性;
Fabric結(jié)構(gòu)理解
交易流程(數(shù)據(jù)傳輸流程)
在交代Fabric各個(gè)關(guān)鍵之前,先理清發(fā)起一筆交易需要經(jīng)歷的整個(gè)數(shù)據(jù)流程是什么。
總體流程如下圖所示:
生命周期中的流程
完整生命周期
流程步驟:
-
應(yīng)用程序通過(guò)SDK發(fā)送請(qǐng)求到Peer節(jié)點(diǎn)(一個(gè)或多個(gè)) 即發(fā)起交易
- 客戶(hù)A發(fā)起交易請(qǐng)求:合約設(shè)置的背書(shū)策略規(guī)定所有交易需要經(jīng)過(guò)兩個(gè)Peer節(jié)點(diǎn)的簽名背書(shū),因此請(qǐng)求需要被同時(shí)發(fā)往Peer A和Peer B.
- 客戶(hù)端應(yīng)用程序利用任意SDK(nodeJS,java,python)構(gòu)造交易提案。該提案是一個(gè)調(diào)用智能合約功能函數(shù)的請(qǐng)求,用來(lái)確認(rèn)哪些數(shù)據(jù)可以讀取或?qū)懭胭~本(即更新資產(chǎn)的Key/Value)。
- SDK將交易提案打包為可識(shí)別的格式(如gRPC上的protocol buffer),并使用用戶(hù)的加密憑證為該交易提案生成唯一的簽名。
-
peer節(jié)點(diǎn)(Endorser節(jié)點(diǎn))通過(guò)chaincode分別執(zhí)行交易,但是并不將執(zhí)行結(jié)果提交到本地的賬本中 (可以認(rèn)為是模擬執(zhí)行,交易處于掛起狀態(tài),放置于候選池)
- 參與背書(shū)的peer將執(zhí)行結(jié)果返回給應(yīng)用程序(其中包括自身對(duì)背書(shū)結(jié)果的簽名)
- 背書(shū)節(jié)點(diǎn)(使用MSP)驗(yàn)證簽名(ProcessPropsal()->preProcess()-->Verify()驗(yàn)證簽名)并確定提交者是否有權(quán)執(zhí)行操作(使用通道的ACL)。
- 背書(shū)節(jié)點(diǎn)將交易提案的參數(shù)作為輸入,在當(dāng)前狀態(tài)數(shù)據(jù)庫(kù)上執(zhí)行交易,生成包含執(zhí)行返回值、讀操作集合和寫(xiě)操作集合的交易結(jié)果(此時(shí)不會(huì)更新賬本),這些值的集合、背書(shū)節(jié)點(diǎn)的簽名和背書(shū)結(jié)果(YES / NO)作為“提案的結(jié)果”返回給SDK
- SDK解析這些信息判斷是否應(yīng)用于后續(xù)的交易。
-
應(yīng)用程序收集背書(shū)結(jié)果并將結(jié)果提交給Ordering服務(wù)節(jié)點(diǎn)
- 應(yīng)用程序(SDK)驗(yàn)證背書(shū)節(jié)點(diǎn)簽名,并比較各節(jié)點(diǎn)返回的提案結(jié)果,判斷提案結(jié)果是否一致以及是否參照指定的背書(shū)策略執(zhí)行。
- 應(yīng)用程序(SDK)將交易提案和結(jié)果以消息形式廣播到排序服務(wù)(Orderers/Consenter)。交易包含讀/寫(xiě)操作集合、背書(shū)節(jié)點(diǎn)的簽名和通道ID。
- 排序服務(wù)不讀取交易的詳細(xì)信息,它從整個(gè)區(qū)塊鏈網(wǎng)絡(luò)接收交易信息,按通道分類(lèi)進(jìn)行排序,并為每個(gè)通道創(chuàng)建包含交易的區(qū)塊。
-
Ordering服務(wù)節(jié)點(diǎn)執(zhí)行共識(shí)過(guò)程并生成block,通過(guò)消息通道發(fā)布給Peer節(jié)點(diǎn)(所有節(jié)點(diǎn)包括committer, submitter, endorser),由peer節(jié)點(diǎn)各自驗(yàn)證交易并提交到本地的ledger中(包括state狀態(tài)的變化)
- 排序服務(wù)將區(qū)塊發(fā)送到通道上的所有節(jié)點(diǎn),所有交易需要被驗(yàn)證,確保滿(mǎn)足背書(shū)策略
- 同時(shí),需要確保全部讀操作集合在交易生成之后,賬本上的狀態(tài)值沒(méi)有改變。
- 經(jīng)過(guò)驗(yàn)證,區(qū)塊中的交易會(huì)被標(biāo)記為有效或無(wú)效,通過(guò)Event通知客戶(hù)端
-
所有節(jié)點(diǎn)賬本更新
- 所有通道上的區(qū)塊鏈節(jié)點(diǎn)將新區(qū)塊加入?yún)^(qū)塊鏈,并且對(duì)于所有有效的交易,將寫(xiě)操作集合提交更新到狀態(tài)數(shù)據(jù)庫(kù)中。
- 節(jié)點(diǎn)通過(guò)事件(Event)通知客戶(hù)端交易是否已被加入?yún)^(qū)塊鏈、以及交易是否有效。
Fabric概念結(jié)構(gòu)
可以由下面的圖大致概括:
(Peers + Orderers + Channels)
- Orderers: 即Consenter,共識(shí)插件,提供共識(shí)服務(wù)的網(wǎng)絡(luò)節(jié)點(diǎn),負(fù)責(zé)接受交易信息進(jìn)行排序,以及對(duì)交易進(jìn)行切割并打包,打包后返回批量交易。例如,使用Kafka或PBFT(SBFT),單節(jié)點(diǎn)使用solo單節(jié)點(diǎn)。
- Peers: 維護(hù)賬本的網(wǎng)絡(luò)節(jié)點(diǎn),通常在Hyperledger Fabric架構(gòu)中存在各種角色,如endorser和committer。
- 通道:通道是有共識(shí)服務(wù)(ordering)提供的一種通訊機(jī)制,類(lèi)似于消息系統(tǒng)中的發(fā)布-訂閱(PUB/SUB)中的topic;基于這種發(fā)布-訂閱關(guān)系,將peer和orderer連接在一起,形成一個(gè)個(gè)具有保密性的通訊鏈路(虛擬),實(shí)現(xiàn)了業(yè)務(wù)隔離的要求;通道也與賬本(ledger)-狀態(tài)(worldstate)緊密相關(guān); peer可以在訂閱多個(gè)通道,并且只能訪(fǎng)問(wèn)訂閱通道上的交易;且通道上的數(shù)據(jù)僅與peer有關(guān),與order無(wú)關(guān)。
- 賬本:賬本保存Orders提交經(jīng)節(jié)點(diǎn)確認(rèn)的交易記錄。
- 成員:訪(fǎng)問(wèn)和使用賬本的網(wǎng)絡(luò)節(jié)點(diǎn)。
- 成員管理: 每個(gè)membership(MSP組織)可以有自己的fabric-ca作為第三方認(rèn)證機(jī)構(gòu),與背書(shū)策略對(duì)應(yīng)。成員都需要在MSP中注冊(cè)
- 鏈:基本上,一個(gè)鏈由1個(gè)通道+ 1個(gè)賬本+ N個(gè)成員組成。非鏈的成員無(wú)法訪(fǎng)問(wèn)該鏈上的交易。鏈的成員可以由應(yīng)用程序動(dòng)態(tài)指定。
我們這時(shí)候再看一下前面的流程圖,回憶一下交易經(jīng)過(guò)了哪些節(jié)點(diǎn)
SDK --> Commiters --> Endorsers --> SDK --> Orderers --> Commiters&Endorsers --> SDK
可參考下圖:
在節(jié)點(diǎn)Peers(Committer)提交到Endorsers時(shí),會(huì)根據(jù)交易的需求,要求一部分背書(shū)節(jié)點(diǎn)進(jìn)行交易所有權(quán)驗(yàn)證,背書(shū)簽名并返回背書(shū)結(jié)果。
下面我們了解一下背書(shū)策略:
背書(shū)策略與設(shè)計(jì)
背書(shū)策略?xún)?nèi)容
背書(shū)策略,用于指示區(qū)塊鏈節(jié)點(diǎn)交易驗(yàn)證的規(guī)則。作為交易驗(yàn)證流程的一部分,當(dāng)背書(shū)節(jié)點(diǎn)收到一個(gè)交易請(qǐng)求的時(shí)候, 該節(jié)點(diǎn)會(huì)調(diào)用 VSCC (驗(yàn)證用途的系統(tǒng)合約程序) 并與執(zhí)行交易的合約相關(guān)聯(lián)。
為了確定交易的有效性,一個(gè)交易應(yīng)該包含來(lái)自盡可能多的背書(shū)節(jié)點(diǎn)的一個(gè)或多個(gè)背書(shū)。VSCC用于判定下面的內(nèi)容:
- 所有背書(shū)是有效的 (即它們是來(lái)自預(yù)期消息上的有效證書(shū)的有效簽名)
- 得到一定數(shù)量的背書(shū)
- 背書(shū)來(lái)自預(yù)期的來(lái)源(指定背書(shū)節(jié)點(diǎn))
背書(shū)策略設(shè)計(jì)
背書(shū)策略有兩個(gè)主要組成部分:
- 主體principal
- 閥門(mén)threshold gate
P 標(biāo)識(shí)期望背書(shū)的區(qū)塊鏈節(jié)點(diǎn),T有兩個(gè)輸入?yún)?shù):整數(shù)t(背書(shū)數(shù)量)和n (背書(shū)節(jié)點(diǎn)列表),即滿(mǎn)足t的條件,并得到n的背書(shū)。
例如:
- T(2, 'A', 'B', 'C') 請(qǐng)求來(lái)自'A'、'B'、'C'的任意2個(gè)背書(shū)節(jié)點(diǎn)的簽名
- T(1, 'A', T(2, 'B', 'C')) 請(qǐng)求來(lái)自A或來(lái)自B和C中的一個(gè)簽名
命令行下的背書(shū)策略語(yǔ)法
在Fabric CLI中,使用了一種簡(jiǎn)單的boolean表達(dá)式來(lái)解釋Endorse節(jié)點(diǎn)的背書(shū)策略。
Fabric 1.0使用MSP(成員管理服務(wù))來(lái)描述主體principal,該MSP用于驗(yàn)證簽名者的身份以及簽名者在該MSP內(nèi)所具有的權(quán)限。
目前支持兩種角色:成員和管理員。主體Principals的通用表現(xiàn)形式是MSP.ROLE,其中MSP是指MSP 的ID,ROLE是 member 或admin。 一個(gè)有效主體的示例是“Org0.admin”(Org0 MSP的任意管理員)或“Org1.member”(Org1 MSP的任意成員)。
示例:
命令行語(yǔ)法是這樣的:
EXPR(E[, E...])
其中EXPR可以是AND或OR,代表兩個(gè)boolean表達(dá)式,E是主體或?qū)XPR的另一個(gè)嵌套調(diào)用。
例如:
AND('Org1.member', 'Org2.member', 'Org3.member') 請(qǐng)求三個(gè)背書(shū)節(jié)點(diǎn)的簽名
OR('Org1.member', 'Org2.member') 請(qǐng)求兩個(gè)背書(shū)節(jié)點(diǎn)中的任意一個(gè)的簽名
OR('Org1.member', AND('Org2.member', 'Org3.member'))
請(qǐng)求來(lái)自O(shè)rg1 MSP成員或來(lái)自O(shè)rg2 MSP成員和來(lái)自O(shè)rg3 MSP成員的任意一個(gè)簽名
指定智能合約的背書(shū)策略
部署合約的開(kāi)發(fā)人員可以指定背書(shū)策略來(lái)驗(yàn)證執(zhí)行的合約。
注,默認(rèn)策略需要來(lái)自DEFAULT MSP成員的一個(gè)簽名。如果未在CLI中指定策略,則默認(rèn)使用此選項(xiàng)。
背書(shū)策略可以在部署合約時(shí)使用"-P選項(xiàng)"指定,后面跟策略?xún)?nèi)容。
例如:
peer chaincode deploy -C testchainid -n mycc -p http://github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02 -c '{"Args":["init","a","100","b","200"]}' -P "AND('Org1.member', 'Org2.member')"
執(zhí)行這條命令將在testchainid這條鏈上使用背書(shū)策略AND('Org1.member', 'Org2.member').部署智能合約mycc
Orderers共識(shí)(Consenter)
共識(shí)機(jī)制
Fabric 0.6采用的是PBFT共識(shí)機(jī)制,但已經(jīng)暫時(shí)取消該機(jī)制改為了Kafka共識(shí)。原因如下:
- 交易性能達(dá)不到要求
- Fabric 面向的聯(lián)盟鏈環(huán)境中,因?yàn)楣?jié)點(diǎn)都是有準(zhǔn)入控制的,拜贊庭容錯(cuò)的需求不是很強(qiáng)烈,反而是并發(fā)性能最重要
在測(cè)試開(kāi)發(fā)中較多使用SOLO共識(shí)機(jī)制
SOLO機(jī)制是一個(gè)非常容易部署的非生產(chǎn)環(huán)境的共識(shí)排序節(jié)點(diǎn)。由一個(gè)為所有客戶(hù)服務(wù)的單一節(jié)點(diǎn)組成,所以不需要“共識(shí)”,因?yàn)橛幸粋€(gè)中央權(quán)威機(jī)構(gòu)。相應(yīng)地沒(méi)有高可用性或可擴(kuò)展性。這使得獨(dú)立開(kāi)發(fā)和測(cè)試很理想,但不適合生產(chǎn)環(huán)境部署。order-solo模式作為單節(jié)點(diǎn)通信模式,所有從peer收到的消息都在本節(jié)點(diǎn)進(jìn)行排序與生成數(shù)據(jù)塊
在Fabric 1.0往后版本中,采用的是Kafka共識(shí)機(jī)制,將來(lái)或采用SBFT(簡(jiǎn)化拜占庭容錯(cuò)共識(shí))。
基于 Kafka 實(shí)現(xiàn)的共識(shí)
一個(gè)共識(shí)集群由多個(gè) orderer 節(jié)點(diǎn)(OSN)和一個(gè) kafka 集群組成。orderer 之間并不直接通信,他們僅僅和 Kafka 集群通信。
在 orderer 的實(shí)現(xiàn)里,通道(Channel)在 kafka 中是以 主題topic 的形式隔離。
每個(gè) orderer 內(nèi)部,針對(duì)每個(gè)通道都會(huì)建立與 kafka 集群對(duì)應(yīng) topic 的生產(chǎn)者及消費(fèi)者。生產(chǎn)者將 orderer 節(jié)點(diǎn)收到的交易發(fā)送到 kafka 集群進(jìn)行排序,在生產(chǎn)的同時(shí),消費(fèi)者也同步消費(fèi)排序后的交易。
如何鑒別某個(gè)交易屬于哪個(gè)區(qū)塊
Fabric 的區(qū)塊結(jié)塊由兩個(gè)條件決定,區(qū)塊交易量和區(qū)塊時(shí)間間隔。
- 當(dāng)配置的交易量達(dá)到閾值時(shí),無(wú)論是否達(dá)到時(shí)間間隔,都會(huì)觸發(fā)結(jié)塊操作;
- 另一方面,如果觸發(fā)了設(shè)置的時(shí)間間隔閾值,只要有交易就會(huì)觸發(fā)結(jié)塊操作,也就是說(shuō) fabric 中不會(huì)有空塊。
- 結(jié)塊操作是由 orderer 節(jié)點(diǎn)中的 kafka 生產(chǎn)者發(fā)送一條 TTC-X(Time to cut block x)消息到 kafka 集群,
- 當(dāng)任意 orderer 節(jié)點(diǎn)的 kafka 消費(fèi)者接收到任意節(jié)點(diǎn)發(fā)出的 TTC-X 消息時(shí),都會(huì)將之前收到的交易打包結(jié)塊,保存在 orderer 本地,之后再分發(fā)到各 peer 節(jié)點(diǎn)。
個(gè)人理解:OSN內(nèi)部有每個(gè)通道對(duì)應(yīng)topic的kafka生產(chǎn)者、kafka消費(fèi)者,OSN里的生產(chǎn)者同時(shí)發(fā)送交易到集群,OSN同時(shí)消費(fèi)來(lái)自集群的交易,共識(shí)的交易達(dá)到交易量或達(dá)到時(shí)間間隔后,OSN的kafka生產(chǎn)者會(huì)發(fā)送TTC-X到集群,OSN的kafka消費(fèi)者收到后會(huì)將之前收到的交易結(jié)塊,保存在OSN本地,然后分發(fā)到peers節(jié)點(diǎn)
以上過(guò)程可由下圖描述:
Kafka共識(shí)集群結(jié)構(gòu)
Kafka簡(jiǎn)介
注,Orderers(OSN)與Consenter(一致性)不一樣,Consenter在kafka集群里,是真正實(shí)現(xiàn)共識(shí)的共識(shí)插件,而OSN是與集群cluster通信獲得共識(shí)結(jié)果的節(jié)點(diǎn)
Kafka是一種分布式的,基于發(fā)布/訂閱的消息系統(tǒng)。主要設(shè)計(jì)目標(biāo)如下:
- 以時(shí)間復(fù)雜度為O(1)的方式提供消息持久化能力,即使對(duì)TB級(jí)以上數(shù)據(jù)也能保證常數(shù)時(shí)間的訪(fǎng)問(wèn)性能
- 高吞吐率。即使在非常廉價(jià)的商用機(jī)器上也能做到單機(jī)支持每秒100K條消息的傳輸
- 支持Kafka Server間的消息分區(qū),及分布式消費(fèi),同時(shí)保證每個(gè)partition內(nèi)的消息順序傳輸
- 同時(shí)支持離線(xiàn)數(shù)據(jù)處理和實(shí)時(shí)數(shù)據(jù)處理
Kafka架構(gòu)
一個(gè)典型的kafka集群中包含若干producer,若干broker,若干consumer group,以及一個(gè)Zookeeper集群。
- Kafka通過(guò)Zookeeper管理集群配置,選舉leader,以及在consumer group發(fā)生變化時(shí)進(jìn)行rebalance
- producer使用push模式將消息發(fā)布到broker
- consumer使用pull模式從broker訂閱并消費(fèi)消息。
各個(gè)角色的功能分別是:
-
Brokers(經(jīng)紀(jì)人)
代理是負(fù)責(zé)維護(hù)發(fā)布數(shù)據(jù)的簡(jiǎn)單系統(tǒng)。- 每個(gè)代理可以每個(gè)主題具有零個(gè)或多個(gè)分區(qū)。
- push模式的目標(biāo)是盡可能以最快速度傳遞消息,但是這樣很容易造成consumer來(lái)不及處理消息,而pull模式下Broker則可以根據(jù)consumer的消費(fèi)能力以適當(dāng)?shù)乃俾氏M(fèi)消息。
- 假設(shè),如果在一個(gè)主題和N個(gè)代理中有N個(gè)分區(qū),每個(gè)代理將有一個(gè)分區(qū)。
-
Producers(生產(chǎn)者)
生產(chǎn)者是發(fā)送給一個(gè)或多個(gè)Kafka主題的消息的發(fā)布者。- 生產(chǎn)者向Kafka經(jīng)紀(jì)人發(fā)送數(shù)據(jù)。
- 每當(dāng)生產(chǎn)者將消息發(fā)布給代理時(shí),代理只需將消息附加到最后一個(gè)段文件。實(shí)際上,該消息將被附加到分區(qū)。
- 生產(chǎn)者還可以向他們選擇的分區(qū)發(fā)送消息。
-
Consumers(消費(fèi)者)
Consumers從經(jīng)紀(jì)人處讀取數(shù)據(jù)。 消費(fèi)者訂閱一個(gè)或多個(gè)主題,并通過(guò)從代理中提取數(shù)據(jù)來(lái)使用已發(fā)布的消息。- Consumer自己維護(hù)消費(fèi)到哪個(gè)offet,線(xiàn)性增加
- 每個(gè)Consumer都有對(duì)應(yīng)的group
- group內(nèi)是queue消費(fèi)模型:各個(gè)Consumer消費(fèi)不同的partition,因此一個(gè)消息在group內(nèi)只消費(fèi)一次
- group間是publish-subscribe消費(fèi)模型:各個(gè)group各自獨(dú)立消費(fèi),互不影響,因此一個(gè)消息被每個(gè)group消費(fèi)一次
-
Topics(主題)
屬于特定類(lèi)別的消息流稱(chēng)為主題。- 數(shù)據(jù)存儲(chǔ)在主題中。Topic相當(dāng)于Queue。
- 主題被拆分成分區(qū)。
- 每個(gè)這樣的分區(qū)包含不可變有序序列的消息。
- 分區(qū)被實(shí)現(xiàn)為具有相等大小的一組分段文件。
-
Partition(分區(qū))
一個(gè)Topic可以分成多個(gè)Partition,這是為了平行化處理。- 每個(gè)Partition內(nèi)部消息有序,其中每個(gè)消息都有一個(gè)offset序號(hào)
- 一個(gè)Partition只對(duì)應(yīng)一個(gè)Broker,一個(gè)Broker可以管理多個(gè)Partition
- 因?yàn)槊織l消息都被append到該partition中,是順序?qū)懘疟P(pán)。因此效率非常高(經(jīng)驗(yàn)證,順序?qū)懘疟P(pán)效率比隨機(jī)寫(xiě)內(nèi)存還要高,這是Kafka高吞吐率的一個(gè)很重要的保證)
-
Replicas of partition(分區(qū)備份)
- 副本只是一個(gè)分區(qū)的備份。 副本從不讀取或?qū)懭霐?shù)據(jù)。 它們用于防止數(shù)據(jù)丟失。
- 對(duì)于傳統(tǒng)的message queue而言,一般會(huì)刪除已經(jīng)被消費(fèi)的消息,而Kafka集群會(huì)保留所有的消息,無(wú)論其被消費(fèi)與否。
- 當(dāng)然,因?yàn)榇疟P(pán)限制,不可能永久保留所有數(shù)據(jù)(實(shí)際上也沒(méi)必要),因此Kafka提供兩種策略去刪除舊數(shù)據(jù)。一是基于時(shí)間,二是基于partition文件大小。
Ledger賬本結(jié)構(gòu)
Peer節(jié)點(diǎn)負(fù)責(zé)維護(hù)區(qū)塊鏈的賬本(ledger)和狀態(tài)(State),本地的賬本稱(chēng)為PeerLedger。
整個(gè)區(qū)塊結(jié)構(gòu)分為文件系統(tǒng)存儲(chǔ)的Block結(jié)構(gòu)和數(shù)據(jù)庫(kù)維護(hù)的State狀態(tài),其中state的存儲(chǔ)結(jié)構(gòu)是可以替換的,可選的實(shí)現(xiàn)包括各種KV數(shù)據(jù)庫(kù)(LEVELDB,CouchDB等)
賬本簡(jiǎn)單的說(shuō),是一系列有序的、不可篡改的狀態(tài)轉(zhuǎn)移記錄日志
- 狀態(tài)轉(zhuǎn)移是鏈碼(chaincode)執(zhí)行(交易)的結(jié)果,每個(gè)交易都是通過(guò)增刪改操作提交一系列鍵值對(duì)到賬本。一系列有序的交易被打包成塊,這樣就將賬本串聯(lián)成了區(qū)塊鏈。
- 同時(shí),一個(gè)狀態(tài)數(shù)據(jù)庫(kù)維護(hù)賬本當(dāng)前的狀態(tài),因此也被叫做世界狀態(tài)。
在 1.0 版本的 Fabric 中,每個(gè)通道都有其賬本,每個(gè) peer 節(jié)點(diǎn)都保存著其加入的通道的賬本,包含著交易日志(賬本數(shù)據(jù)庫(kù))、狀態(tài)數(shù)據(jù)庫(kù)以及歷史數(shù)據(jù)庫(kù)。
賬本狀態(tài)數(shù)據(jù)庫(kù)實(shí)際上存儲(chǔ)的是所有曾經(jīng)在交易中出現(xiàn)的鍵值對(duì)的最新值
- 調(diào)用鏈碼執(zhí)行交易可以改變狀態(tài)數(shù)據(jù),為了高效的執(zhí)行鏈碼調(diào)用,所有數(shù)據(jù)的最新值都被存放在狀態(tài)數(shù)據(jù)庫(kù)中。
- 就邏輯上來(lái)說(shuō),狀態(tài)數(shù)據(jù)庫(kù)僅僅是有序交易日志的快照,因此在任何時(shí)候都可以根據(jù)交易日志重新生成。
- 狀態(tài)數(shù)據(jù)庫(kù)會(huì)在peer節(jié)點(diǎn)啟動(dòng)的時(shí)候自動(dòng)恢復(fù)或重構(gòu),未完備前,該節(jié)點(diǎn)不會(huì)接受新的交易。
- 狀態(tài)數(shù)據(jù)庫(kù)可以使用 LevelDB 或者 CouchDB。
- 跟 LevelDB 一樣,CouchDB 也能夠存儲(chǔ)任意的二進(jìn)制數(shù)據(jù),
- CouchDB 額外的支撐 JSON 富文本查詢(xún),如果鏈碼的鍵值對(duì)存儲(chǔ)的是 JSON,那么可以很好的利用 CouchDB 的富文本查詢(xún)功能。
Fabric 的賬本結(jié)構(gòu)中還有一個(gè)可選的歷史狀態(tài)數(shù)據(jù)庫(kù),用于查詢(xún)某個(gè) key 的歷史修改記錄,需要注意的是,歷史數(shù)據(jù)庫(kù)并不存儲(chǔ) key 具體的值,而只記錄在某個(gè)區(qū)塊的某個(gè)交易里,某 key 變動(dòng)了一次。后續(xù)需要查詢(xún)的時(shí)候,根據(jù)變動(dòng)歷史去查詢(xún)實(shí)際變動(dòng)的值。
賬本數(shù)據(jù)庫(kù)是基于文件系統(tǒng),將區(qū)塊存儲(chǔ)于文件塊中,然后在 LevelDB 中存儲(chǔ)區(qū)塊交易對(duì)應(yīng)的文件塊及其偏移,也就是將 LevelDB 作為賬本數(shù)據(jù)庫(kù)的索引。
現(xiàn)階段支持的索引有:
- 區(qū)塊編號(hào)
- 區(qū)塊哈希
- 交易 ID 索引交易
- 區(qū)塊交易編號(hào)
- 交易 ID 索引區(qū)塊
- 交易 ID 索引交易驗(yàn)證碼
Chaincode鏈碼系統(tǒng)
前面我們提到,狀態(tài)轉(zhuǎn)移是鏈碼(chaincode)執(zhí)行(交易)的結(jié)果。實(shí)際上,鏈碼(chaincode)是 Hyperledger Fabric 提供的智能合約,是上層應(yīng)用與底層區(qū)塊鏈平臺(tái)交互的媒介。即,合約與交易都與鏈碼相關(guān)。
CSCC配置系統(tǒng)
介紹一個(gè)新的系統(tǒng)chaincode,叫做配置系統(tǒng)chaincode(CSCC),主要負(fù)責(zé)處理所有的配置相關(guān)的事務(wù)。CSCC提供方法查詢(xún)眾多的配置數(shù)據(jù),包括通道配置。
鏈碼的通道配置
引導(dǎo)
共識(shí)服務(wù)由1個(gè)或多個(gè)Orderers組成。 每個(gè)Orderer配置有匹配的創(chuàng)世區(qū)塊,其由引導(dǎo)CLI命令生成,其提供了一些必要的數(shù)據(jù),包括一系列可信根節(jié)點(diǎn)的列表,Order證書(shū)和IP地址的列表,一組特定的共識(shí)算法配置以及訪(fǎng)問(wèn)控制策略(誰(shuí)可以創(chuàng)建信道)。
要啟動(dòng)并連接到共識(shí)服務(wù),peer至少需要以下配置:
- 準(zhǔn)入網(wǎng)絡(luò)的注冊(cè)證書(shū)。 證書(shū)可以來(lái)自任意CA,只要CA是peer將連接到的共識(shí)服務(wù)的可信任根的一部分
- 來(lái)自共識(shí)服務(wù)管理CLI生成的Orderer證書(shū)和IP地址的列表
- 可信任根節(jié)點(diǎn)列表
- peer可以訂閱的通道可選列表。 除非明確配置,否則peer在啟動(dòng)時(shí)不訂閱任何通道
注意,#2和#3來(lái)自引導(dǎo)的創(chuàng)世區(qū)塊,我們可以從引導(dǎo)CLI命令獲得。
通過(guò)CLI或使用SDK API的應(yīng)用程序,peer可以訂閱已經(jīng)存在的通道。 orderer通過(guò)在通道創(chuàng)建或重新配置期間收到的消息決定誰(shuí)可以加入通道。
例如,假設(shè)peer A和B屬于2個(gè)不同成員Alice和Bob。 請(qǐng)注意,Alice和Bob可能在網(wǎng)絡(luò)上有多個(gè)Peer,并且他們的任何Peer都可以加入通道。 以下是一個(gè)典型的序列:
創(chuàng)建通道
- 應(yīng)用程序/ SDK獲得A和B的背書(shū)用于創(chuàng)建通道“foo”的配置交易。
- 應(yīng)用程序/ SDK調(diào)用Broadcast RPC,將背書(shū)過(guò)的配置交易傳遞給order服務(wù)。
- 應(yīng)用程序/ SDK然后調(diào)用在通道foo上deliver RPC。此RPC將返回一個(gè)錯(cuò)誤,直到order服務(wù)成功創(chuàng)建通道。
- 當(dāng)通道最終被創(chuàng)建后,Deliver RPC將返回通道的信息到應(yīng)用程序/ SDK。在這一時(shí)點(diǎn),通道foo應(yīng)當(dāng)僅具有包含相關(guān)訂閱者的創(chuàng)世區(qū)塊,并且與該配置交易一起被(或最近的重新配置交易)引導(dǎo)。
- 應(yīng)用程序/ SDK在A和B上調(diào)用JoinChannel API,將通道foo的創(chuàng)世區(qū)塊傳遞給A和B,添加CSCC到通道上。
- A和B上的CSCC檢查創(chuàng)世區(qū)塊,包括檢查區(qū)塊中的配置交易的背書(shū)。如果一切正確,他們調(diào)用在通道上的Deliver RPC來(lái)開(kāi)始接收塊。
如果通道已經(jīng)存在,則參與者列表將被替換。Orderers自動(dòng)替換訂閱者并且將該交易與該通道上的其他交易一起發(fā)送給新成員,新成員將會(huì)同步完整的塊。
關(guān)閉通道
應(yīng)用程序可以通過(guò)發(fā)送類(lèi)似于創(chuàng)建通道的配置交易來(lái)關(guān)閉其創(chuàng)建的通道。 它需要根據(jù)應(yīng)用程序設(shè)置的策略從通道參與方得到背書(shū)。
peer不會(huì)自動(dòng)銷(xiāo)毀相關(guān)的賬本,但是裁剪進(jìn)程會(huì)在適當(dāng)?shù)臅r(shí)候處理。
應(yīng)用程序可以繼續(xù)從已關(guān)閉的賬本中讀取數(shù)據(jù),只要該賬本尚未被刪除,但由于通道已被銷(xiāo)毀,因此不能執(zhí)行交易了。
查詢(xún)通道
通道只能被該通道的成員查詢(xún)。也就是說(shuō),交易發(fā)起方的簽名能夠被存儲(chǔ)在賬本配置區(qū)塊中的CA證書(shū)驗(yàn)證通過(guò)。這是通過(guò)發(fā)起一個(gè)查詢(xún)交易到CSCC,同時(shí)附上鏈的ID,返回的結(jié)果是一個(gè)配置區(qū)塊,里面包含了成員證書(shū)和一些其他的配置信息。
鏈上的交易
一個(gè)交易必須包含目標(biāo)的鏈ID(鏈ID =通道ID =賬本ID)。
共識(shí)服務(wù)將把交易放置在由鏈ID標(biāo)識(shí)的指定通道上,并且在該通道內(nèi)被排序,而與其它通道上的交易無(wú)關(guān)。 最終在該通道上產(chǎn)生一個(gè)包含交易的區(qū)塊并發(fā)送到訂閱了該通道的那些peer。
注意,每個(gè)鏈都是獨(dú)立和并行的,因此一個(gè)peer可以同時(shí)接收和處理不同鏈上的區(qū)塊。
chaincode事務(wù)只能操作指定鏈中的狀態(tài)變量
chaincode限制
- 從交易調(diào)用chaincode總是在交易被發(fā)送的鏈上進(jìn)行操作
- 只有系統(tǒng)鏈上的chaincode可以被私有鏈上的其他chaincode調(diào)用并且是只讀的
API 配置交易(通道與節(jié)點(diǎn)綁定)
- 在peer上增加一個(gè)新的gRPC API和一個(gè)新的頂層交易類(lèi)型。API允許App / SDK通知peer已成功加入的通道。
加入通道API的輸入是由新創(chuàng)建的通道上的共識(shí)服務(wù)返回的創(chuàng)世區(qū)塊,peer使用此區(qū)塊設(shè)置與通道關(guān)聯(lián)的賬本。 - 新的交易類(lèi)型稱(chēng)為配置交易,這種類(lèi)型的交易可以由Orderer和peer處理。 創(chuàng)建或重新配置通道的交易都屬于配置交易,其中背書(shū)請(qǐng)求是讓peer批準(zhǔn)和不批準(zhǔn)它們創(chuàng)建或重新配置通道。 peer可以通過(guò)提案請(qǐng)求返回接受或拒絕。 為了保持靈活性,我們將提供一個(gè)系統(tǒng)chaincode來(lái)處理通道創(chuàng)建的背書(shū)請(qǐng)求,它將自動(dòng)響應(yīng)簽名請(qǐng)求。
- chaincode還提供查詢(xún)此通道上參與成員列表的功能。
- 新配置交易必須包含所有先前的配置條目,并且所有新/修改的配置條目必須用其包含配置包絡(luò)的序列號(hào)和鏈ID標(biāo)記。每個(gè)配置條目都具有枚舉類(lèi)型,唯一(按類(lèi)型劃分)ID以及由名稱(chēng)引用的修改策略。Order服務(wù)將根據(jù)現(xiàn)有配置策略驗(yàn)證配置交易,如果不滿(mǎn)足全部修改策略,則拒絕它。
- SDK可以向API提供進(jìn)一步的抽象。 例如,它可以提供1個(gè)API,創(chuàng)建通道(成員證書(shū)列表),它將執(zhí)行在創(chuàng)建通道部分中討論的所有6個(gè)步驟。
- 最后,SDK將調(diào)用應(yīng)用程序上的回調(diào),返回創(chuàng)建通道的狀態(tài)。
鏈碼的合約作用
鏈碼(chaincode)是 Hyperledger Fabric 提供的智能合約,是上層應(yīng)用與底層區(qū)塊鏈平臺(tái)交互的媒介?,F(xiàn)階段,F(xiàn)abric 提供 Go、Java 等語(yǔ)言編寫(xiě)的鏈碼
所有的鏈碼都實(shí)現(xiàn)兩個(gè)接口,init 和 invoke。
- init 接口用于初始化合約,在整個(gè)鏈碼的生命周期里,該接口僅僅執(zhí)行一次。
- invoke 接口是編寫(xiě)業(yè)務(wù)邏輯的唯一入口,雖然只有一個(gè)入口,但是可以根據(jù)參數(shù)傳遞的不同自由區(qū)分不同業(yè)務(wù)邏輯,靈活性很高。比如應(yīng)用開(kāi)發(fā)者規(guī)定 Invoke 接口的第一個(gè)參數(shù)是合約方法名,剩余的 Invoke 參數(shù)列表是傳遞給該方法的參數(shù),那么就可以在 Invoke 接口方法體中根據(jù)方法名的不同分流不同業(yè)務(wù)了。
合約里能夠獲取的內(nèi)容
- 輸入?yún)?shù)獲取。這點(diǎn)很好理解,我們只有知道此次調(diào)用的輸入,才能處理邏輯,推導(dǎo)輸出;
- 與狀態(tài)數(shù)據(jù)庫(kù)和歷史數(shù)據(jù)庫(kù)交互。在合約層,我們可以將區(qū)塊鏈底層當(dāng)做是一個(gè)鍵值對(duì)數(shù)據(jù)庫(kù),合約就是對(duì)數(shù)據(jù)庫(kù)中鍵值的增刪改查;
- 與其他合約的交互。在合約執(zhí)行的過(guò)程中,可以與其他合約交換數(shù)據(jù),做到類(lèi)似跨鏈的效果。有了這種形式的數(shù)據(jù)獲取方式,其實(shí)就可以將聯(lián)系不緊密的業(yè)務(wù)邏輯拆分為多個(gè)合約,只在必要的時(shí)候跨合約調(diào)用,非常類(lèi)似于現(xiàn)在提倡的微服務(wù)架構(gòu)。
編寫(xiě)鏈碼還有一個(gè)非常重要的原則:不要出現(xiàn)任何本地化和隨機(jī)邏輯。此處的本地化,不是指語(yǔ)言本地化,而是執(zhí)行環(huán)境本地化。區(qū)塊鏈因?yàn)槭侨ブ行募軜?gòu),業(yè)務(wù)邏輯不是只在某一個(gè)節(jié)點(diǎn)執(zhí)行,而是在所有的共識(shí)節(jié)點(diǎn)都執(zhí)行,如果鏈碼輸出與本地化數(shù)據(jù)相關(guān),那么可能會(huì)導(dǎo)致結(jié)果差異,從而不能達(dá)成共識(shí)。
鏈碼部署
Peers是獨(dú)立實(shí)體,通道就是業(yè)務(wù)載體,鏈碼就是業(yè)務(wù);不同的通道即便是運(yùn)行相同的鏈碼,因?yàn)檩d體不同,可認(rèn)為是兩個(gè)不同業(yè)務(wù)。
- 創(chuàng)建業(yè)務(wù)載體通道;
- 將通道與 peer 節(jié)點(diǎn)綁定;
- 在通道上實(shí)例化鏈碼。
通道的管理
通道只有創(chuàng)建,而沒(méi)有刪除功能。但是在使用 kafka 共識(shí)的過(guò)程中,如果數(shù)據(jù)操作不當(dāng),直接在 kafka 中刪除數(shù)據(jù),而 orderer 沒(méi)有邏輯去處理這種異常刪除,因此會(huì)不斷的重試,在達(dá)到重試極限后直接崩潰整個(gè)進(jìn)程
沒(méi)有完善的數(shù)據(jù)管理方案
在我們的使用場(chǎng)景中,數(shù)據(jù)增長(zhǎng)是很快的,如果使用 CouchDB 作為底層數(shù)據(jù)引擎,數(shù)據(jù)更是幾何倍數(shù)的爆發(fā)。現(xiàn)有的解決方案只能是在云上部署節(jié)點(diǎn),提供可持續(xù)擴(kuò)充的云硬盤(pán),再者使用 LevelDB 替換掉 CouchDB,避免使用模糊查詢(xún)。
Event事件流
事件框架支持發(fā)出2種類(lèi)型的event(事件)
- Block event
- 自定義/chaincode event(在events.proto中定義的ChaincodeEvent類(lèi)型)
基本思想
client(event consumers\事件消費(fèi)者)將注冊(cè)event類(lèi)型(block或chaincode)。在chaincode的情況下,它們可以指定附加的注冊(cè)標(biāo)準(zhǔn),即chaincodeID和eventname。
- ChaincodeID標(biāo)識(shí)client想要查看event的特定Chaincode。
- eventname是Chaincode開(kāi)發(fā)人員,在調(diào)用Chaincode中的SetEvent API時(shí)嵌入的字符串。
調(diào)用transaction是當(dāng)前唯一可以發(fā)出event的操作,并且每個(gè)調(diào)用,在每個(gè)transaction中只能發(fā)出一個(gè)event。
一般Event類(lèi)型與ChaincodeEvent的關(guān)系
Event與event類(lèi)型相關(guān)聯(lián)。 客戶(hù)注冊(cè)他們想要接收event的event類(lèi)型。
event類(lèi)型的生命周期由“block”event來(lái)說(shuō)明
- 在啟動(dòng)peer時(shí),在支持的event類(lèi)型中添加“block”
- client可以與peer(或多個(gè)peers)一起注冊(cè)感興趣的“block” event類(lèi)型
- 創(chuàng)建Block的Peers,向所有注冊(cè)client發(fā)布event
- 客戶(hù)收到“block” event并處理Block中的事務(wù)
Chaincode event添加了額外的注冊(cè)過(guò)濾級(jí)別。 Chaincode event不是注冊(cè)給定event類(lèi)型的所有event,而是允許client從特定Chaincode注冊(cè)特定event。 對(duì)于目前的第一個(gè)版本,為了簡(jiǎn)單起見(jiàn),沒(méi)有在eventname上實(shí)現(xiàn)通配符或正則表達(dá)式匹配,但后續(xù)會(huì)提供該功能
Fabric通信方式
節(jié)點(diǎn)通信、client與節(jié)點(diǎn)Api通信:使用http/2下的gRPC
- http: 基于TCP/IP協(xié)議,需要三次握手
- rpc:遠(yuǎn)程進(jìn)程調(diào)用,需要統(tǒng)一的序列化,不適用于頻繁連接
- gRPC: 使用HTTP/2協(xié)議并用ProtoBuf作為序列化工具
與REST比較
- 和REST一樣遵循HTTP協(xié)議(明確的說(shuō)是HTTP/2),但是gRPC提供了全雙工流
- 和傳統(tǒng)的REST不同的是gRPC使用了靜態(tài)路徑,從而提高性能
- 用一些格式化的錯(cuò)誤碼代替了HTTP的狀態(tài)碼更好的標(biāo)示錯(cuò)誤
gRPC
- xxx.proto, 定義rpc,輸入?yún)?shù)與返回參數(shù)的數(shù)據(jù)命名結(jié)構(gòu)
- 命令行中protoc編譯生成對(duì)應(yīng)的xxx.pb.go源碼,編寫(xiě)clientAPI for EventService供客戶(hù)端使用的接口定義、接口實(shí)例、接口實(shí)例的初始化函數(shù),和server API for EventService供服務(wù)端使用的接口定義,注冊(cè)函數(shù)。
HTTP/2特點(diǎn)
-
將所有傳輸?shù)男畔⒎指顬楦〉南⒑蛶?,并?duì)它們采用二進(jìn)制格式的編碼。在HTTP/2中,數(shù)據(jù)流以消息的形式發(fā)送,而消息由一個(gè)或多個(gè)幀組成,幀可以在數(shù)據(jù)流上亂序發(fā)送,然后再根據(jù)每個(gè)幀首部的流標(biāo)識(shí)符重新組裝。二進(jìn)制分幀是HTTP/2的基石,其他優(yōu)化都是在這一基礎(chǔ)上來(lái)實(shí)現(xiàn)的。我們先了解幾個(gè)概念:
- 幀(Frame):HTTP/2通信的最小單位,每個(gè)幀包含幀首部,至少也會(huì)標(biāo)識(shí)出當(dāng)前幀所屬的流。
- 消息(Message):由一個(gè)或多個(gè)幀組合而成,例如請(qǐng)求和響應(yīng)。
- 連接(Connection):與 HTTP/1 相同,都是指對(duì)應(yīng)的 TCP 連接;
- 流(Stream):已建立的連接上的雙向字節(jié)流。
-
支持請(qǐng)求與響應(yīng)的多路復(fù)用來(lái)減少延遲
- 同域名下所有通信都在單個(gè)連接上完成。
- 單個(gè)連接可以承載任意數(shù)量的雙向數(shù)據(jù)流。
- 數(shù)據(jù)流以消息的形式發(fā)送,而消息又由一個(gè)或多個(gè)幀組成,多個(gè)幀之間可以亂序發(fā)送,因?yàn)楦鶕?jù)幀首部的流標(biāo)識(shí)可以重新組裝。
這一特性,性能會(huì)有極大的提升,因?yàn)椋?/p>
- 同個(gè)域名只需要占用一個(gè)TCP連接,消除了因多個(gè)TCP連接而帶來(lái)的延時(shí)和內(nèi)存消耗。
- 單個(gè)連接上可以并行交錯(cuò)的請(qǐng)求和響應(yīng),之間互不干擾。
-
壓縮HTTP首部字段將協(xié)議開(kāi)銷(xiāo)降至最低
- HTTP/2在客戶(hù)端和服務(wù)器端使用“首部表”來(lái)跟蹤和存儲(chǔ)之前發(fā)送的鍵-值對(duì),對(duì)于相同的數(shù)據(jù),不再通過(guò)每次請(qǐng)求和響應(yīng)發(fā)送;
- 首部表在HTTP/2的連接存續(xù)期內(nèi)始終存在,由客戶(hù)端和服務(wù)器共同漸進(jìn)地更新;
- 每個(gè)新的首部鍵-值對(duì)要么被追加到當(dāng)前表的末尾,要么替換表中之前的值。
對(duì)請(qǐng)求劃分優(yōu)先級(jí)
支持服務(wù)端Push消息到客戶(hù)端
ProtoBuf:
- 一套用于數(shù)據(jù)存儲(chǔ),網(wǎng)絡(luò)通信時(shí)用于協(xié)議編解碼的工具庫(kù).它和XML和Json數(shù)據(jù)差不多,把數(shù)據(jù)已某種形式保存起來(lái).Protobuf相對(duì)與XML和Json的不同之處,它是一種二進(jìn)制的數(shù)據(jù)格式,具有更高的傳輸,打包和解包效率.
- 如果使用protobuf實(shí)現(xiàn),首先要寫(xiě)一個(gè)proto文件(不妨叫Order.proto),在該文件中添加一個(gè)名為"Order"的message結(jié)構(gòu),用來(lái)描述通訊協(xié)議中的結(jié)構(gòu)化數(shù)據(jù)。使用protobuf內(nèi)置的編譯器編譯 該proto。
參考資料:
Hyperledger文檔:(詳細(xì)研讀)
https://hyperledger-fabric.readthedocs.io/en/latest/whatis.html
Hyperledger github文檔
https://github.com/hyperledger/fabric/tree/release-1.1/docs/source
常用的Q&A
https://hyperledger-fabric.readthedocs.io/en/latest/Fabric-FAQ.html?highlight=consenter
知乎專(zhuān)欄翻譯:
https://zhuanlan.zhihu.com/p/23356616
專(zhuān)欄-fabric生命周期
https://zhuanlan.zhihu.com/p/25119939
hyperledger-MSP event等側(cè)面分析 圖解
https://blog.csdn.net/maixia24/article/category/7507736