HyperLedger Fabric協議規範【轉】

協議規範

前言

這份文檔是帶有權限的區塊鏈的工業界實現的協議規範。它不會詳細的解釋實現細節,而是描述系統和應用之間的接口和關係。php

目標讀者

這份規範的目標讀者包括:css

  • 想實現符合這份規範的區塊鏈的廠商
  • 想擴展 fabric 功能的工具開發者
  • 想利用區塊鏈技術來豐富他們應用的應用開發者

做者

下面這些做者編寫了這份分檔: Binh Q Nguyen, Elli Androulaki, Angelo De Caro, Sheehan Anderson, Manish Sethi, Thorsten Kramp, Alessandro Sorniotti, Marko Vukolic, Florian Simon Schubert, Jason K Yellick, Konstantinos Christidis, Srinivasan Muralidharan, Anna D Derbakova, Dulce Ponceleon, David Kravitz, Diego Masini.html

評審

下面這些評審人評審了這份文檔: Frank Lu, John Wolpert, Bishop Brock, Nitin Gaur, Sharon Weed, Konrad Pabjan.前端

致謝

下面這些貢獻者對這份規範提供了技術支持: 
Gennaro Cuomo, Joseph A Latone, Christian Cachinnode


目錄

1. 介紹

  • 1.1 什麼是 fabric ?
  • 1.2 爲何是 fabric ?
  • 1.3 術語

2. Fabric

  • 2.1 架構
  • 2.1.1 Membership 服務
  • 2.1.2 Blockchain 服務
  • 2.1.3 Chaincode 服務
  • 2.1.4 事件
  • 2.1.5 應用程序接口
  • 2.1.6 命令行界面
  • 2.2 拓撲
  • 2.2.1 單驗證 Peer
  • 2.2.2 多驗證 Peers
  • 2.2.3 多鏈

3. 協議

  • 3.1 消息
  • 3.1.1 發現消息
  • 3.1.2 交易消息
  • 3.1.2.1 交易數據結構
  • 3.1.2.2 交易規範
  • 3.1.2.3 交易部署
  • 3.1.2.4 交易調用
  • 3.1.2.5 交易查詢
  • 3.1.3 同步消息
  • 3.1.4 共識消息
  • 3.2 總帳
  • 3.2.1 區塊鏈
  • 3.2.1.1 塊
  • 3.2.1.2 塊 Hashing
  • 3.2.1.3 非散列數據(NonHashData)
  • 3.2.1.4 交易
  • 3.2.2 世界狀態(World State)
  • 3.2.2.1 世界狀態的 Hashing
  • 3.2.2.1.1 Bucket-tree
  • 3.3 Chaincode
  • 3.3.1 Virtual Machine 實例化
  • 3.3.2 Chaincode 協議
  • 3.3.2.1 Chaincode 部署
  • 3.3.2.2 Chaincode 調用
  • 3.3.2.3 Chaincode 查詢
  • 3.3.2.4 Chaincode 狀態
  • 3.4 可插拔的共識框架
  • 3.4.1 共識者接口
  • 3.4.2 共識程序接口
  • 3.4.3 Inquirer 接口
  • 3.4.4 Communicator 接口
  • 3.4.5 SecurityUtils 接口
  • 3.4.6 LedgerStack 接口
  • 3.4.7 Executor 接口
  • 3.4.7.1 開始批量交易
  • 3.4.7.2 執行交易
  • 3.4.7.3 提交與回滾交易
  • 3.4.8 Ledger 接口
  • 3.4.8.1 ReadOnlyLedger 接口
  • 3.4.8.2 UtilLedger 接口
  • 3.4.8.3 WritableLedger 接口
  • 3.4.9 RemoteLedgers 接口
  • 3.4.10 Controller 包
  • 3.4.11 Helper 包
  • 3.5 事件
  • 3.5.1 事件流
  • 3.5.2 事件結構
  • 3.5.3 事件適配器

4. 安全

    1. 安全
  • 4.1 商業安全需求
  • 4.2 使用成員管理的用戶隱私
  • 4.2.1 用戶/客戶端註冊過程
  • 4.2.2 過時和廢止證書
  • 4.3 基礎設施層面提供的交易安全
  • 4.3.1 交易的安全生命週期
  • 4.3.2 交易保密性
  • 4.3.2.1 針對用戶的保密
  • 4.3.2.2 針對驗證器的保密
  • 4.3.3 防重放攻擊
  • 4.4 應用的訪問控制功能
  • 4.4.1 調用訪問控制
  • 4.4.2 讀訪問控制
  • 4.5 在線錢包服務
  • 4.6 網絡安全(TLS)
  • 4.7 當前版本的限制
  • 4.7.1 簡化客戶端
  • 4.7.2 簡化交易保密

5. 拜占庭共識

  • 5.1 概覽
  • 5.2 Core PBFT

6. 應用編程接口

  • 6.1 REST 服務
  • 6.2 REST API
  • 6.3 CLI

7. 應用模型

  • 7.1 應用組成
  • 7.2 應用樣例

8. 將來發展方向

  • 8.1 企業集成
  • 8.2 性能與可擴展性
  • 8.3 附加的共識插件
  • 8.4 附加的語言

9. References


1. 介紹

這份文檔規範了適用於工業界的區塊鏈的概念,架構和協議。git

1.1 什麼是 fabric?

fabric 是在系統中數字事件,交易調用,不一樣參與者共享的總帳。總帳只能經過共識的參與者來更新,並且一旦被記錄,信息永遠不能被修改。每個記錄的事件均可以根據參與者的協議進行加密驗證。github

交易是安全的,私有的而且可信的。每一個參與者經過向網絡membership服務證實本身的身份來訪問系統。交易是經過發放給各個的參與者,不可鏈接的,提供在網絡上徹底匿名的證書來生成的。交易內容經過複雜的密鑰加密來保證只有參與者才能看到,確保業務交易私密性。golang

總帳能夠按照規定規則來審計所有或部分總帳分錄。在與參與者合做中,審計員能夠經過基於時間的證書來得到總帳的查看,鏈接交易來提供實際的資產操做。算法

fabric 是區塊鏈技術的一種實現,比特幣是能夠在fabric上構建的一種簡單應用。它經過模塊化的架構來容許組件的「插入-運行」來實現這份協議規範。它具備強大的容器技術來支持任何主流的語言來開發智能合約。利用熟悉的和被證實的技術是fabric的座右銘。sql

1.2 爲何是 fabric?

早期的區塊鏈技術提供一個目的集合,可是一般對具體的工業應用支持的不是很好。爲了知足現代市場的需求,fabric 是基於工業關注點針對特定行業的多種多樣的需求來設計的,並引入了這個領域內的開拓者的經驗,如擴展性。fabric 爲權限網絡,隱私,和多個區塊鏈網絡的私密信息提供一種新的方法。

1.3 術語

如下術語在此規範的有限範圍內定義,以幫助讀者清楚準確的瞭解這裏所描述的概念。

交易(Transaction) 是區塊鏈上執行功能的一個請求。功能是使用鏈節點(chainnode)來實現的。

交易者(Transactor) 是向客戶端應用這樣發出交易的實體。

總帳(Ledger) 是一系列包含交易和當前世界狀態(World State)的加密的連接塊。

世界狀態(World State) 是包含交易執行結果的變量集合。

鏈碼(Chaincode) 是做爲交易的一部分保存在總帳上的應用級的代碼(如智能合約)。鏈節點運行的交易可能會改變世界狀態。

驗證Peer(Validating Peer) 是網絡中負責達成共識,驗證交易並維護總帳的一個計算節點。

非驗證Peer(Non-validating Peer) 是網絡上做爲代理把交易員鏈接到附近驗證節點的計算節點。非驗證Peer只驗證交易但不執行它們。它還承載事件流服務和REST服務。

帶有權限的總帳(Permissioned Ledger) 是一個由每一個實體或節點都是網絡成員所組成的區塊鏈網絡。匿名節點是不容許鏈接的。

隱私(Privacy) 是鏈上的交易者須要隱瞞本身在網絡上身份。雖然網絡的成員能夠查看交易,可是交易在沒有獲得特殊的權限前不能鏈接到交易者。

保密(Confidentiality) 是交易的內容不能被非利益相關者訪問到的功能。

可審計性(Auditability) 做爲商業用途的區塊鏈須要遵照法規,很容易讓監管機構審計交易記錄。因此區塊鏈是必須的。

2. Fabric

fabric是由下面這個小節所描述的核心組件所組成的。

2.1 架構

這個架構參考關注在三個類別中:會員(Membership),區塊鏈(Blockchan)和鏈碼(chaincode)。這些類別是邏輯結構,而不是物理上的把不一樣的組件分割到獨立的進程,地址空間,(虛擬)機器中。

Reference architecture

2.1.1 成員服務

成員服務爲網絡提供身份管理,隱私,保密和可審計性的服務。在一個不帶權限的區塊鏈中,參與者是不須要被受權的,且全部的節點均可以一樣的提交交易並把它們聚集到可接受的塊中,如:它們沒有角色的區分。成員服務經過公鑰基礎設施(Public Key Infrastructure 
(PKI))和去中心化的/共識技術使得不帶權限的區塊鏈變成帶權限的區塊鏈。在後者中,經過實體註冊來得到長時間的,可能根據實體類型生成的身份憑證(登記證書enrollment certificates)。在用戶使用過程當中,這樣的證書容許交易證書頒發機構(Transaction Certificate Authority 
(TCA))頒發匿名證書。這樣的證書,如交易證書,被用來對提交交易受權。交易證書存儲在區塊鏈中,並對審計集羣受權,不然交易是不可連接的。

2.1.2 區塊鏈服務

區塊鏈服務經過 HTTP/2 上的點對點(peer-to-peer)協議來管理分佈式總帳。爲了提供最高效的哈希算法來維護世界狀態的複製,數據結構進行了高度的優化。每一個部署中能夠插入和配置不一樣的共識算法(PBFT, Raft, PoW, PoS)。

2.1.3 鏈碼服務

鏈碼服務提供一個安全的,輕量的沙箱在驗證節點上執行鏈碼。環境是一個「鎖定的」且安全的包含簽過名的安全操做系統鏡像和鏈碼語言,Go,Java 和 Node.js 的運行時和 SDK 層。能夠根據須要來啓用其餘語言。

2.1.4 事件

驗證 peers 和鏈碼能夠向在網絡上監聽並採起行動的應用發送事件。這是一些預約義好的事件集合,鏈碼能夠生成客戶化的事件。事件會被一個或多個事件適配器消費。以後適配器可能會把事件投遞到其餘設備,如 Web hooks 或 Kafka。

2.1.5 應用編程接口(API)

fabric的主要接口是 REST API,並經過 Swagger 2.0 來改變。API 容許註冊用戶,區塊鏈查詢和發佈交易。鏈碼與執行交易的堆間的交互和交易的結果查詢會由 API 集合來規範。

2.1.6 命令行界面(CLI)

CLI包含REST API的一個子集使得開發者能更快的測試鏈碼或查詢交易狀態。CLI 是經過 Go 語言來實現,並可在多種操做系統上操做。

2.2 拓撲

fabric 的一個部署是由成員服務,多個驗證 peers、非驗證 peers 和一個或多個應用所組成一個鏈。也能夠有多個鏈,各個鏈具備不一樣的操做參數和安全要求。

2.2.1 單驗證Peer

功能上講,一個非驗證 peer 是驗證 peer 的子集;非驗證 peer 上的功能均可以在驗證 peer 上啓用,因此在最簡單的網絡上只有一個驗證peer組成。這個配置一般使用在開發環境:單個驗證 peer 在編輯-編譯-調試周期中被啓動。

Single Validating Peer

單個驗證 peer 不須要共識,默認狀況下使用noops插件來處理接收到的交易。這使得在開發中,開發人員能當即收到返回。

2.2.2 多驗證 Peer

生產或測試網絡須要有多個驗證和非驗證 peers 組成。非驗證 peer 能夠爲驗證 peer 分擔像 API 請求處理或事件處理這樣的壓力。

Multiple Validating Peers

網狀網絡(每一個驗證peer須要和其它驗證peer都相連)中的驗證 peer 來傳播信息。一個非驗證 peer 鏈接到附近的,容許它鏈接的驗證 peer。當應用可能直接鏈接到驗證 peer 時,非驗證 peer 是可選的。

2.2.3 多鏈

驗證和非驗證 peer 的各個網絡組成一個鏈。能夠根據不一樣的需求建立不一樣的鏈,就像根據不一樣的目的建立不一樣的 Web 站點。

3. 協議

fabric的點對點(peer-to-peer)通訊是創建在容許雙向的基於流的消息gRPC上的。它使用Protocol Buffers來序列化peer之間傳輸的數據結構。Protocol buffers 是語言無關,平臺無關並具備可擴展機制來序列化結構化的數據的技術。數據結構,消息和服務是使用 proto3 language註釋來描述的。

3.1 消息

消息在節點之間經過Messageproto 結構封裝來傳遞的,能夠分爲 4 種類型:發現(Discovery), 交易(Transaction), 同步(Synchronization)和共識(Consensus)。每種類型在payload中定義了多種子類型。

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
message Message { enum Type { UNDEFINED = 0; DISC_HELLO = 1; DISC_DISCONNECT = 2; DISC_GET_PEERS = 3; DISC_PEERS = 4; DISC_NEWMSG = 5; CHAIN_STATUS = 6; CHAIN_TRANSACTION = 7; CHAIN_GET_TRANSACTIONS = 8; CHAIN_QUERY = 9; SYNC_GET_BLOCKS = 11; SYNC_BLOCKS = 12; SYNC_BLOCK_ADDED = 13; SYNC_STATE_GET_SNAPSHOT = 14; SYNC_STATE_SNAPSHOT = 15; SYNC_STATE_GET_DELTAS = 16; SYNC_STATE_DELTAS = 17; RESPONSE = 20; CONSENSUS = 21; } Type type = 1; bytes payload = 2; google.protobuf.Timestamp timestamp = 3; }

payload是由不一樣的消息類型所包含的不一樣的像TransactionResponse這樣的對象的不透明的字節數組。例如:typeCHAIN_TRANSACTION那麼payload就是一個Transaction對象。

3.1.1 發現消息

在啓動時,若是CORE_PEER_DISCOVERY_ROOTNODE被指定,那麼 peer 就會運行發現協議。CORE_PEER_DISCOVERY_ROOTNODE是網絡(任意peer)中扮演用來發現全部 peer 的起點角色的另外一個 peer 的 IP 地址。協議序列以payload是一個包含:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
message HelloMessage { PeerEndpoint peerEndpoint = 1; uint64 blockNumber = 2; } message PeerEndpoint { PeerID ID = 1; string address = 2; enum Type { UNDEFINED = 0; VALIDATOR = 1; NON_VALIDATOR = 2; } Type type = 3; bytes pkiID = 4; } message PeerID { string name = 1; }

這樣的端點的HelloMessage對象的DISC_HELLO消息開始的。

域的定義:

  • PeerID 是在啓動時或配置文件中定義的 peer 的任意名字
  • PeerEndpoint 描述了端點和它是驗證仍是非驗證 peer
  • pkiID 是 peer 的加密ID
  • address 以ip:port這樣的格式表示的 peer 的主機名或IP和端口
  • blockNumber 是 peer 的區塊鏈的當前的高度

若是收到的DISC_HELLO 消息的塊的高度比當前 peer 的塊的高度高,那麼它立刻初始化同步協議來追上當前的網絡。

DISC_HELLO以後,peer 會週期性的發送DISC_GET_PEERS來發現任意想要加入網絡的 peer。收到DISC_GET_PEERS後,peer 會發送payload 
包含PeerEndpoint的數組的DISC_PEERS做爲響應。這是不會使用其它的發現消息類型。

3.1.2 交易消息

有三種不一樣的交易類型:部署(Deploy),調用(Invoke)和查詢(Query)。部署交易向鏈上安裝指定的鏈碼,調用和查詢交易會調用部署號的鏈碼。另外一種須要考慮的類型是建立(Create)交易,其中部署好的鏈碼是能夠在鏈上實例化並尋址的。這種類型在寫這份文檔時尚未被實現。

3.1.2.1 交易的數據結構

CHAIN_TRANSACTIONCHAIN_QUERY類型的消息會在payload帶有Transaction對象:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
message Transaction { enum Type { UNDEFINED = 0; CHAINCODE_DEPLOY = 1; CHAINCODE_INVOKE = 2; CHAINCODE_QUERY = 3; CHAINCODE_TERMINATE = 4; } Type type = 1; string uuid = 5; bytes chaincodeID = 2; bytes payloadHash = 3; ConfidentialityLevel confidentialityLevel = 7; bytes nonce = 8; bytes cert = 9; bytes signature = 10; bytes metadata = 4; google.protobuf.Timestamp timestamp = 6; } message TransactionPayload { bytes payload = 1; } enum ConfidentialityLevel { PUBLIC = 0; CONFIDENTIAL = 1; }

域的定義: 
type - 交易的類型, 爲1時表示: 
UNDEFINED - 爲將來的使用所保留. 
CHAINCODE_DEPLOY - 表明部署新的鏈碼. 
CHAINCODE_INVOKE - 表明一個鏈碼函數被執行並修改了世界狀態 
CHAINCODE_QUERY - 表明一個鏈碼函數被執行並可能只讀取了世界狀態 
CHAINCODE_TERMINATE - 標記的鏈碼不可用,因此鏈碼中的函數將不能被調用 
chaincodeID - 鏈碼源碼,路徑,構造函數和參數哈希所獲得的ID 
payloadHash - TransactionPayload.payload所定義的哈希字節. 
metadata - 應用可能使用的,由本身定義的任意交易相關的元數據 
uuid - 交易的惟一ID 
timestamp - peer 收到交易時的時間戳 
confidentialityLevel - 數據保密的級別。當前有兩個級別。將來可能會有多個級別。 
nonce - 爲安全而使用 
cert - 交易者的證書 
signature - 交易者的簽名 
TransactionPayload.payload - 交易的payload所定義的字節。因爲payload能夠很大,因此交易消息只包含payload的哈希

交易安全的詳細信息能夠在第四節找到

3.1.2.2 交易規範

一個交易一般會關聯鏈碼定義及其執行環境(像語言和安全上下文)的鏈碼規範。如今,有一個使用Go語言來編寫鏈碼的實現。未來可能會添加新的語言。

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
message ChaincodeSpec { enum Type { UNDEFINED = 0; GOLANG = 1; NODE = 2; } Type type = 1; ChaincodeID chaincodeID = 2; ChaincodeInput ctorMsg = 3; int32 timeout = 4; string secureContext = 5; ConfidentialityLevel confidentialityLevel = 6; bytes metadata = 7; } message ChaincodeID { string path = 1; string name = 2; } message ChaincodeInput { string function = 1; repeated string args = 2; }

域的定義: 
chaincodeID - 鏈碼源碼的路徑和名字 
ctorMsg - 調用的函數名及參數 
timeout - 執行交易所需的時間(以毫秒錶示) 
confidentialityLevel - 這個交易的保密級別 
secureContext - 交易者的安全上下文 
metadata - 應用想要傳遞下去的任何數據

當 peer 收到chaincodeSpec後以合適的交易消息包裝它並廣播到網絡

3.1.2.3 部署交易

部署交易的類型是CHAINCODE_DEPLOY,且它的payload包含ChaincodeDeploymentSpec對象。

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
message ChaincodeDeploymentSpec { ChaincodeSpec chaincodeSpec = 1; google.protobuf.Timestamp effectiveDate = 2; bytes codePackage = 3; }

域的定義: 
chaincodeSpec - 參看上面的3.1.2.2節. 
effectiveDate - 鏈碼準備好可被調用的時間 
codePackage - 鏈碼源碼的gzip

當驗證 peer 部署鏈碼時,它一般會校驗codePackage的哈希來保證交易被部署到網絡後沒有被篡改。

3.1.2.4 調用交易

調用交易的類型是CHAINCODE_DEPLOY,且它的payload包含ChaincodeInvocationSpec對象。

   
   
   
   
  • 1
  • 2
  • 3
message ChaincodeInvocationSpec { ChaincodeSpec chaincodeSpec = 1; }

3.1.2.5 查詢交易

查詢交易除了消息類型是CHAINCODE_QUERY其它和調用交易同樣

3.1.3 同步消息

同步協議以3.1.1節描述的,當 peer 知道它本身的區塊落後於其它 peer 或和它們不同後所發起的。peer 廣播SYNC_GET_BLOCKSSYNC_STATE_GET_SNAPSHOTSYNC_STATE_GET_DELTAS並分別接收SYNC_BLOCKSSYNC_STATE_SNAPSHOT或 SYNC_STATE_DELTAS

安裝的共識插件(如:pbft)決定同步協議是如何被應用的。每一個小時是針對具體的狀態來設計的:

SYNC_GET_BLOCKS 是一個SyncBlockRange對象,包含一個連續區塊的範圍的payload的請求。

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
message SyncBlockRange { uint64 start = 1; uint64 end = 2; uint64 end = 3; }

接收peer使用包含 SyncBlocks對象的payloadSYNC_BLOCKS信息來響應

   
   
   
   
  • 1
  • 2
  • 3
  • 4
message SyncBlocks { SyncBlockRange range = 1; repeated Block blocks = 2; }

startend標識包含的區塊的開始和結束,返回區塊的順序由startend的值定義。如:當start=3,end=5時區塊的順序將會是3,4,5。當start=5,end=3時區塊的順序將會是5,4,3。

SYNC_STATE_GET_SNAPSHOT 請求當前世界狀態的快照。 payload是一個SyncStateSnapshotRequest對象

   
   
   
   
  • 1
  • 2
  • 3
message SyncStateSnapshotRequest { uint64 correlationId = 1; }

correlationId是請求 peer 用來追蹤響應消息的。接受 peer 回覆payloadSyncStateSnapshot實例的SYNC_STATE_SNAPSHOT信息

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
message SyncStateSnapshot { bytes delta = 1; uint64 sequence = 2; uint64 blockNumber = 3; SyncStateSnapshotRequest request = 4; }

這條消息包含快照或以0開始的快照流序列中的一塊。終止消息是len(delta) == 0的塊

SYNC_STATE_GET_DELTAS 請求連續區塊的狀態變化。默認狀況下總帳維護500筆交易變化。 delta(j)是block(i)和block(j)之間的狀態轉變,其中i=j-1。 payload包含SyncStateDeltasRequest實例

   
   
   
   
  • 1
  • 2
  • 3
message SyncStateDeltasRequest { SyncBlockRange range = 1; }

接收 peer 使用包含 SyncStateDeltas實例的payloadSYNC_STATE_DELTAS信息來響應

   
   
   
   
  • 1
  • 2
  • 3
  • 4
message SyncStateDeltas { SyncBlockRange range = 1; repeated bytes deltas = 2; }

delta可能以順序(從i到j)或倒序(從j到i)來表示狀態轉變

3.1.4 共識消息

共識處理交易,一個CONSENSUS消息是由共識框架接收到CHAIN_TRANSACTION消息時在內部初始化的。框架把CHAIN_TRANSACTION轉換爲 CONSENSUS而後以相同的payload廣播到驗證 peer。共識插件接收這條消息並根據內部算法來處理。插件可能建立自定義的子類型來管理共識有窮狀態機。3.4節會介紹詳細信息。

3.2 總帳

總帳由兩個主要的部分組成,一個是區塊鏈,一個是世界狀態。區塊鏈是在總帳中的一系列鏈接好的用來記錄交易的區塊。世界狀態是一個用來存儲交易執行狀態的鍵-值(key-value)數據庫

3.2.1 區塊鏈

3.2.1.1 區塊

區塊鏈是由一個區塊鏈表定義的,每一個區塊包含它在鏈中前一個區塊的哈希。區塊包含的另外兩個重要信息是它包含區塊執行全部交易後的交易列表和世界狀態的哈希

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
message Block { version = 1; google.protobuf.Timestamp timestamp = 2; bytes transactionsHash = 3; bytes stateHash = 4; bytes previousBlockHash = 5; bytes consensusMetadata = 6; NonHashData nonHashData = 7; } message BlockTransactions { repeated Transaction transactions = 1; }

域的定義: 
version - 用來追蹤協議變化的版本號 
timestamp - 由區塊提議者填充的時間戳 
transactionsHash - 區塊中交易的merkle root hash 
stateHash - 世界狀態的merkle root hash 
previousBlockHash - 前一個區塊的hash 
consensusMetadata - 共識可能會引入的一些可選的元數據 
nonHashData - NonHashData消息會在計算區塊的哈希前設置爲nil,可是在數據庫中存儲爲區塊的一部分 
BlockTransactions.transactions - 交易消息的數組,因爲交易的大小,它們不會被直接包含在區塊中

3.2.1.2 區塊哈希

  • previousBlockHash哈希是經過下面算法計算的

    1. 使用protocol buffer庫把區塊消息序列化爲字節碼

    2. 使用FIPS 202描述的SHA3 SHAKE256算法來對序列化後的區塊消息計算大小爲512位的哈希值

  • transactionHash是交易merkle樹的根。定義merkle tree實現是一個代辦

  • stateHash在3.2.2.1節中定義.

3.2.1.3 非散列數據(NonHashData)

NonHashData消息是用來存儲不須要全部 peer 都具備相同值的塊元數據。他們是建議值。

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
message NonHashData { google.protobuf.Timestamp localLedgerCommitTimestamp = 1; repeated TransactionResult transactionResults = 2; } message TransactionResult { string uuid = 1; bytes result = 2; uint32 errorCode = 3; string error = 4; }
  • localLedgerCommitTimestamp - 標識區塊提交到本地總帳的時間戳

  • TransactionResult - 交易結果的數組

  • TransactionResult.uuid - 交易的ID

  • TransactionResult.result - 交易的返回值

  • TransactionResult.errorCode - 能夠用來記錄關聯交易的錯誤信息的代碼

  • TransactionResult.error - 用來記錄關聯交易的錯誤信息的字符串

3.2.1.4 交易執行

一個交易定義了它們部署或執行的鏈碼。區塊中的全部交易均可以在記錄到總帳中的區塊以前運行。當鏈碼執行時,他們可能會改變世界狀態。以後世界狀態的哈希會被記錄在區塊中。

3.2.2 世界狀態

peer 的世界狀態涉及到全部被部署的鏈碼的狀態集合。進一步說,鏈碼的狀態由鍵值對集合來表示。因此,邏輯上說,peer 的世界狀態也是鍵值對的集合,其中鍵有元組{chaincodeID, ckey}組成。這裏咱們使用術語key來標識世界狀態的鍵,如:元組{chaincodeID, ckey} ,並且咱們使用cKey來標識鏈碼中的惟一鍵。

爲了下面描述的目的,假定chaincodeID是有效的utf8字符串,且ckeyvalue是一個或多個任意的字節的序列

3.2.2.1 世界狀態的哈希

當網絡活動時,不少像交易提交和同步 peer 這樣的場合可能須要計算 peer 觀察到的世界狀態的加密-哈希。例如,共識協議可能須要保證網絡中最小數量的 peer 觀察到一樣的世界狀態。

應爲計算世界狀態的加密-哈希是一個很是昂貴的操做,組織世界狀態來使得當它改變時能高效效的計算加密-哈希是很是可取的。未來,能夠根據不一樣的負載條件來設計不一樣的組織形式。

因爲fabric是被指望在不一樣的負載條件下都能正常工做,因此須要一個可拔插的機制來支持世界狀態的組織。

3.2.2.1.1 Bucket-tree

Bucket-tree 是世界狀態的組織方式的實現。爲了下面描述的目的,世界狀態的鍵被表示成兩個組件(chaincodeID and ckey) 的經過nil字節的級聯,如:key = chaincodeID+nil+cKey

這個方法的模型是一個merkle-treehash table桶的頂部來計算世界狀態的加密-哈希

這個方法的核心是世界狀態的key-values被假定存儲在由預先決定的桶的數量(numBuckets)所組成的哈希表中。一個哈希函數(hashFunction) 被用來肯定包含給定鍵的桶數量。注意hashFunction不表明SHA3這樣的加密-哈希方法,而是決定給定的鍵的桶的數量的正規的編程語言散列函數。

爲了對 merkle-tree建模,有序桶扮演了樹上的葉子節點-編號最低的桶是樹中的最左邊的葉子節點。爲了構造樹的最後第二層,葉子節點的預約義數量 (maxGroupingAtEachLevel),從左邊開始把每一個這樣的分組組合在一塊兒,一個節點被看成組中全部葉子節點的共同父節點來插入到最後第二層中。注意最後的父節點的數量可能會少於maxGroupingAtEachLevel這個構造方式繼續使用在更高的層級上直到樹的根節點被構造。

下面這個表展現的在{numBuckets=10009 and maxGroupingAtEachLevel=10}的配置下會獲得的樹在不一樣層級上的節點數。

Level Number of nodes
0 1
1 2
2 11
3 101
4 1001
5 10009

爲了計算世界狀態的加密-哈希,須要計算每一個桶的加密-哈希,並假設它們是merkle-tree的葉子節點的加密-哈希。爲了計算桶的加密-哈希,存儲在桶中的鍵值對首先被序列化爲字節碼並在其上應用加密-哈希函數。爲了序列化桶的鍵值對,全部具備公共chaincodeID前綴的鍵值對分別序列化並以chaincodeID的升序的方式追加在一塊兒。爲了序列化一個chaincodeID的鍵值對,會涉及到下面的信息:

  1. chaincodeID的長度(chaincodeID的字節數) 
    • chaincodeID的utf8字節碼
    • chaincodeID的鍵值對數量
    • 對於每一個鍵值對(以ckey排序) 
      • ckey的長度
      • ckey的字節碼
      • 值的長度
      • 值的字節碼

對於上面列表的全部數值類型項(如:chaincodeID的長度),使用protobuf的變體編碼方式。上面這種編碼方式的目的是爲了桶中的鍵值對的字節表示方式不會被任意其餘鍵值對的組合所產生,並減小了序列化字節碼的整體大小。

例如:考慮具備chaincodeID1_key1:value1, chaincodeID1_key2:value2, 和 chaincodeID2_key1:value1這樣名字的鍵值對的桶。序列化後的桶看上去會像:12 + chaincodeID1 + 2 + 4 + key1 + 6 + value1 + 4 + key2 + 6 + value2 + 12 + chaincodeID2 + 1 + 4 + key1 + 6 + value1

若是桶中沒有鍵值對,那麼加密-哈希爲nil

中間節點和根節點的加密-哈希與標準merkle-tree的計算方法同樣,即:應用加密-哈希函數到全部子節點的加密-哈希從左到右級聯後獲得的字節碼。進一步說,若是一個子節點的加密-哈希爲nil,那麼這個子節點的加密-哈希在級聯子節點的加密-哈希是就被省略。若是它只有一個子節點,那麼它的加密-哈希就是子節點的加密-哈希。最後,根節點的加密-哈希就是世界狀態的加密-哈希。

上面這種方法在狀態中少數鍵值對改變時計算加密-哈希是有性能優點的。主要的優點包括: 
- 那些沒有變化的桶的計算會被跳過 
- merkle-tree的寬度和深度能夠經過配置numBucketsmaxGroupingAtEachLevel參數來控制。樹的不一樣深度和寬度對性能和不一樣的資源都會產生不一樣的影響。

在一個具體的部署中,全部的 peer 都指望使用相同的numBuckets, maxGroupingAtEachLevel, 和 hashFunction的配置。進一步說,若是任何一個配置在以後的階段被改變,那麼這些改變須要應用到全部的 peer 中,來保證 peer 節點之間的加密-哈希的比較是有意義的。即便,這可能會致使基於實現的已有數據的遷移。例如:一種實現但願存儲樹中全部節點最後計算的加密-哈希,那麼它就須要被從新計算。

3.3 鏈碼(Chaincode)

鏈碼是在交易(參看3.1.2節)被部署是分發到網絡上,並被全部驗證 peer 經過隔離的沙箱來管理的應用級代碼。儘管任意的虛擬技術均可以支持沙箱,如今是經過Docker容器來運行鏈碼的。這節中描述的協議能夠啓用不一樣虛擬實現的插入與運行。

3.3.1 虛擬機實例化

一個實現VM接口的虛擬機

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
type VM interface { build(ctxt context.Context, id string, args []string, env []string, attachstdin bool, attachstdout bool, reader io.Reader) error start(ctxt context.Context, id string, args []string, env []string, attachstdin bool, attachstdout bool) error stop(ctxt context.Context, id string, timeout uint, dontkill bool, dontremove bool) error }

fabric在處理鏈碼上的部署交易或其餘交易時,若是這個鏈碼的VM未啓動(崩潰或以前的不活動致使的關閉)時實例化VM。每一個鏈碼鏡像經過build函數構建,經過start函數啓動,並使用stop函數中止。

一旦鏈碼容器被啓動,它使用gRPC來鏈接到啓動這個鏈碼的驗證 peer,併爲鏈碼上的調用和查詢交易創建通道。

3.3.2 鏈碼協議

驗證 peer 和它的鏈碼之間是經過gRPC流來通訊的。鏈碼容器上有shim層來處理鏈碼與驗證 peer 之間的protobuf消息協議。

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
message ChaincodeMessage { enum Type { UNDEFINED = 0; REGISTER = 1; REGISTERED = 2; INIT = 3; READY = 4; TRANSACTION = 5; COMPLETED = 6; ERROR = 7; GET_STATE = 8; PUT_STATE = 9; DEL_STATE = 10; INVOKE_CHAINCODE = 11; INVOKE_QUERY = 12; RESPONSE = 13; QUERY = 14; QUERY_COMPLETED = 15; QUERY_ERROR = 16; RANGE_QUERY_STATE = 17; } Type type = 1; google.protobuf.Timestamp timestamp = 2; bytes payload = 3; string uuid = 4; }

域的定義: 
Type 是消息的類型 
payload 是消息的payload. 每一個payload取決於Type
uuid 消息惟一的ID

消息的類型在下面的小節中描述

鏈碼實現被驗證 peer 在處理部署,調用或查詢交易時調用的Chaincode接口

   
   
   
   
  • 1
  • 2
  • 3
  • 4
type Chaincode interface { Invoke(stub *ChaincodeStub, function string, args []string) (error) Query(stub *ChaincodeStub, function string, args []string) ([]byte, error) }

InitInvoke 和 Query函數使用function and args參數來支持多種交易。Init是構造函數,它只在部署交易是被執行。Query函數是不容許修改鏈碼的狀態的;它只能讀取和計算並以byte數組的形式返回。

3.3.2.1 鏈碼部署

當部署時(鏈碼容器已經啓動),shim層發送一次性的具備包含ChaincodeIDpayloadREGISTER消息給驗證 peer。而後 peer 以REGISTEREDERROR來響應成功或失敗。當收到ERROR後shim關閉鏈接並退出。

註冊以後,驗證 peer 發送具備包含ChaincodeInput對象的INIT消息。shim使用從ChaincodeInput得到的參數來調用Init函數,經過像設置持久化狀態這樣操做來初始化鏈碼。

shim根據Init函數的返回值,響應RESPONSEERROR消息。若是沒有錯誤,那麼鏈碼初始化完成,並準備好接收調用和查詢交易。

3.3.2.2 鏈碼調用

當處理調用交易時,驗證 peer 發送TRANSACTION消息給鏈碼容器的shim,由它來調用鏈碼的Invoke函數,並傳遞從ChaincodeInput獲得的參數。shim響應RESPONSEERROR消息來表示函數完成。若是接收到ERROR函數,payload包含鏈碼所產生的錯誤信息。

3.3.2.3 來代碼查詢

與調用交易同樣,驗證 peer 發送QUERY消息給鏈碼容器的shim,由它來調用鏈碼的Query函數,並傳遞從ChaincodeInput獲得的參數。Query函數可能會返回狀態值或錯誤,它會把它經過RESPONSEERROR消息來傳遞給驗證 peer。

3.3.2.4 鏈碼狀態

每一個鏈碼可能都定義了它本身的持久化狀態變量。例如,一個鏈碼可能建立電視,汽車或股票這樣的資產來保存資產屬性。當Invoke函數處理時,鏈碼可能會更新狀態變量,例如改變資產全部者。鏈碼會根據下面這些消息類型類操做狀態變量:

PUT_STATE

鏈碼發送一個payload包含PutStateInfo對象的PU_STATE消息來保存鍵值對。

   
   
   
   
  • 1
  • 2
  • 3
  • 4
message PutStateInfo { string key = 1; bytes value = 2; }

GET_STATE

鏈碼發送一個由payload指定要獲取值的鍵的GET_STATE消息。

DEL_STATE

鏈碼發送一個由payload指定要刪除值的鍵的DEL_STATE消息。

RANGE_QUERY_STATE

鏈碼發送一個payload包含RANGE_QUERY_STATE對象的RANGE_QUERY_STATE來獲取一個範圍內的值。

   
   
   
   
  • 1
  • 2
  • 3
  • 4
message RangeQueryState { string startKey = 1; string endKey = 2; }

startKeyendKey假設是經過字典排序的. 驗證 peer 響應一個payloadRangeQueryStateResponse對象的RESPONSE消息

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
message RangeQueryStateResponse { repeated RangeQueryStateKeyValue keysAndValues = 1; bool hasMore = 2; string ID = 3; } message RangeQueryStateKeyValue { string key = 1; bytes value = 2; }

若是相應中hasMore=true,這表示有在請求的返回中還有另外的鍵。鏈碼能夠經過發送包含與響應中ID相同的ID的RangeQueryStateNext消息來獲取下一集合。

   
   
   
   
  • 1
  • 2
  • 3
message RangeQueryStateNext { string ID = 1; }

當鏈碼結束讀取範圍,它會發送帶有ID的RangeQueryStateClose消息來指望它關閉。

   
   
   
   
  • 1
  • 2
  • 3
message RangeQueryStateClose { string ID = 1; }

INVOKE_CHAINCODE

鏈碼能夠經過發送payload包含 ChaincodeSpec對象的INVOKE_CHAINCODE消息給驗證 peer 來在相同的交易上下文中調用另外一個鏈碼

QUERY_CHAINCODE

鏈碼能夠經過發送payload包含 ChaincodeSpec對象的QUERY_CHAINCODE消息給驗證 peer 來在相同的交易上下文中查詢另外一個鏈碼

3.4 插拔式共識框架

共識框架定義了每一個共識插件都須要實現的接口:

  • consensus.Consenter: 容許共識插件從網絡上接收消息的接口
  • consensus.CPI: 共識編程接口Consensus Programming Interface (CPI) 是共識插件用來與棧交互的,這個接口能夠分爲兩部分: 
    • consensus.Communicator: 用來發送(廣播或單播)消息到其餘的驗證 peer
    • consensus.LedgerStack: 這個接口使得執行框架像總帳同樣方便

就像下面描述的細節同樣,consensus.LedgerStack封裝了其餘接口,consensus.Executor接口是共識框架的核心部分。換句話說,consensus.Executor接口容許一個(批量)交易啓動,執行,根據須要回滾,預覽和提交。每個共識插件都須要知足以全部驗證 peer 上全序的方式把批量(塊)交易(經過consensus.Executor.CommitTxBatch)被提交到總帳中(參看下面的consensus.Executor接口得到詳細細節)。

當前,共識框架由consensuscontrollerhelper這三個包組成。使用controllerhelper包的主要緣由是防止Go語言的「循環引入」和當插件更新時的最小化代碼變化。

  • controller 包規範了驗證 peer 所使用的共識插件
  • helper 是圍繞公式插件的墊片,它是用來與剩下的棧交互的,如爲其餘 peer 維護消息。

這裏有2個共識插件提供:pbftnoops

  • obcpbft包包含實現 PBFT [1] 和 Sieve 共識協議的共識插件。參看第5節的詳細介紹
  • noops 是一個爲開發和測試提供的」假的」共識插件. 它處理全部共識消息但不提供共識功能,它也是一個好的學習如何開發一個共識插件的簡單例子。

3.4.1 Consenter 接口

定義:

   
   
   
   
  • 1
  • 2
  • 3
type Consenter interface { RecvMsg(msg *pb.Message) error }

Consenter接口是插件對(外部的)客戶端請求的入口,當處理共識時,共識消息在內部(如從共識模塊)產生。NewConsenter建立Consenter插件。RecvMsg`以到達共識的順序來處理進來的交易。

閱讀下面的helper.HandleMessage來理解 peer 是如何和這個接口來交互的。

3.4.2 CPI接口

定義:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
type CPI interface { Inquirer Communicator SecurityUtils LedgerStack }

CPI 容許插件和棧交互。它是由helper.Helper對象實現的。回想一下這個對象是:

  1. helper.NewConsensusHandler被調用時初始化的
  2. 當它們的插件構造了consensus.Consenter對象,那麼它對插件的做者是可訪問的

3.4.3 Inquirer接口

定義:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
type Inquirer interface { GetNetworkInfo() (self *pb.PeerEndpoint, network []*pb.PeerEndpoint, err error) GetNetworkHandles() (self *pb.PeerID, network []*pb.PeerID, err error) }

這個接口是consensus.CPI接口的一部分。它是用來獲取網絡中驗證 peer 的(GetNetworkHandles)處理,以及那些驗證 peer 的明細(GetNetworkInfo):

注意pees由pb.PeerID對象肯定。這是一個protobuf消息,當前定義爲(注意這個定義極可能會被修改):

   
   
   
   
  • 1
  • 2
  • 3
message PeerID { string name = 1; }

3.4.4 Communicator接口

定義:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
type Communicator interface { Broadcast(msg *pb.Message) error Unicast(msg *pb.Message, receiverHandle *pb.PeerID) error }

這個接口是consensus.CPI接口的一部分。它是用來與網絡上其它 peer 通訊的(helper.Broadcasthelper.Unicast):

3.4.5 SecurityUtils接口

定義:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
type SecurityUtils interface { Sign(msg []byte) ([]byte, error) Verify(peerID *pb.PeerID, signature []byte, message []byte) error }

這個接口是consensus.CPI接口的一部分。它用來處理消息簽名(Sign)的加密操做和驗證簽名(Verify)

3.4.6 LedgerStack 接口

定義:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
type LedgerStack interface { Executor Ledger RemoteLedgers }

CPI接口的主要成員,LedgerStack 組與fabric的其它部分與共識相互做用,如執行交易,查詢和更新總帳。這個接口支持對本地區塊鏈和狀體的查詢,更新本地區塊鏈和狀態,查詢共識網絡上其它節點的區塊鏈和狀態。它是有ExecutorLedgerRemoteLedgers這三個接口組成的。下面會描述它們。

3.4.7 Executor 接口

定義:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
type Executor interface { BeginTxBatch(id interface{}) error ExecTXs(id interface{}, txs []*pb.Transaction) ([]byte, []error) CommitTxBatch(id interface{}, transactions []*pb.Transaction, transactionsResults []*pb.TransactionResult, metadata []byte) error RollbackTxBatch(id interface{}) error PreviewCommitTxBatchBlock(id interface{}, transactions []*pb.Transaction, metadata []byte) (*pb.Block, error) }

executor接口是LedgerStack接口最常使用的部分,且是共識網絡工做的必要部分。接口容許交易啓動,執行,根據須要回滾,預覽和提交。這個接口由下面這些方法組成。

3.4.7.1 開始批量交易

   
   
   
   
  • 1
BeginTxBatch(id interface{}) error

這個調用接受任意的,故意含糊的id,來使得共識插件能夠保證與這個具體的批量相關的交易纔會被執行。例如:在pbft實現中,這個id是被執行交易的編碼過的哈希。

3.4.7.2 執行交易

   
   
   
   
  • 1
ExecTXs(id interface{}, txs []*pb.Transaction) ([]byte, []error)

這個調用根據總帳當前的狀態接受一組交易,並返回帶有對應着交易組的錯誤信息組的當前狀態的哈希。注意一個交易所產生的錯誤不影響批量交易的安全提交。當遇到失敗所採用的策略取決與共識插件的實現。這個接口調用屢次是安全的。

3.4.7.3 提交與回滾交易

   
   
   
   
  • 1
RollbackTxBatch(id interface{}) error

這個調用忽略了批量執行。這會廢棄掉對當前狀態的操做,並把總帳狀態迴歸到以前的狀態。批量是從BeginBatchTx開始的,若是須要開始一個新的就須要在執行任意交易以前從新建立一個。

   
   
   
   
  • 1
PreviewCommitTxBatchBlock(id interface{}, transactions []*pb.Transaction, metadata []byte) (*pb.Block, error)

這個調用是共識插件對非肯定性交易執行的測試時最有用的方法。區塊返回的哈希表部分會保證,當CommitTxBatch被當即調用時的區塊是同一個。這個保證會被任意新的交易的執行所打破。

   
   
   
   
  • 1
CommitTxBatch(id interface{}, transactions []*pb.Transaction, transactionsResults []*pb.TransactionResult, metadata []byte) error

這個調用提交區塊到區塊鏈中。區塊必須以全序提交到區塊鏈中,CommitTxBatch結束批量交易,在執行或提交任意的交易以前必須先調用BeginTxBatch

3.4.8 Ledger 接口

定義:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
type Ledger interface { ReadOnlyLedger UtilLedger WritableLedger }

Ledger 接口是爲了容許共識插件詢問或可能改變區塊鏈當前狀態。它是由下面描述的三個接口組成的

3.4.8.1 ReadOnlyLedger 接口

定義:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
type ReadOnlyLedger interface { GetBlock(id uint64) (block *pb.Block, err error) GetCurrentStateHash() (stateHash []byte, err error) GetBlockchainSize() (uint64, error) }

ReadOnlyLedger 接口是爲了查詢總帳的本地備份,而不會修改它。它是由下面這些函數組成的。

   
   
   
   
  • 1
GetBlockchainSize() (uint64, error)

這個函數返回區塊鏈總帳的長度。通常來講,這個函數永遠不會失敗,在這種不太可能發生狀況下,錯誤被傳遞給調用者,由它肯定是否須要恢復。具備最大區塊值的區塊的值爲GetBlockchainSize()-1

注意在區塊鏈總帳的本地副本是腐壞或不完整的狀況下,這個調用會返回鏈中最大的區塊值+1。這容許節點在舊的塊是腐壞或丟失的狀況下能繼續操做當前狀態/塊。

   
   
   
   
  • 1
GetBlock(id uint64) (block *pb.Block, err error)

這個調用返回區塊鏈中塊的數值id。通常來講這個調用是不會失敗的,除非請求的區塊超出當前區塊鏈的長度,或者底層的區塊鏈被腐壞了。GetBlock的失敗可能能夠經過狀態轉換機制來取回它。

   
   
   
   
  • 1
GetCurrentStateHash() (stateHash []byte, err error)

這個盜用返回總帳的當前狀態的哈希。通常來講,這個函數永遠不會失敗,在這種不太可能發生狀況下,錯誤被傳遞給調用者,由它肯定是否須要恢復。

3.4.8.2 UtilLedger 接口

定義:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
type UtilLedger interface { HashBlock(block *pb.Block) ([]byte, error) VerifyBlockchain(start, finish uint64) (uint64, error) }

UtilLedger 接口定義了一些由本地總帳提供的有用的功能。使用mock接口來重載這些功能在測試時很是有用。這個接口由兩個函數構成。 
會會

   
   
   
   
  • 1
HashBlock(block *pb.Block) ([]byte, error)

儘管*pb.Block定義了GetHash方法,爲了mock測試,重載這個方法會很是有用。所以,建議GetHash方法不直接調用,而是經過UtilLedger.HashBlock接口來調用這個方法。通常來講,這個函數永遠不會失敗,可是錯誤仍是會傳遞給調用者,讓它決定是否使用適當的恢復。

   
   
   
   
  • 1
VerifyBlockchain(start, finish uint64) (uint64, error)

這個方法是用來校驗區塊鏈中的大的區域。它會從高的塊start到低的塊finish,返回第一個塊的PreviousBlockHash與塊的前一個塊的哈希不相符的塊編號以及錯誤信息。注意,它通常會標識最後一個好的塊的編號,而不是第一個壞的塊的編號。

3.4.8.3 WritableLedger 接口

定義:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
type WritableLedger interface { PutBlock(blockNumber uint64, block *pb.Block) error ApplyStateDelta(id interface{}, delta *statemgmt.StateDelta) error CommitStateDelta(id interface{}) error RollbackStateDelta(id interface{}) error EmptyState() error }

WritableLedger 接口容許調用者更新區塊鏈。注意這NOT 不是共識插件的一般用法。當前的狀態須要經過Executor接口執行交易來修改,新的區塊在交易提交時生成。相反的,這個接口主要是用來狀態改變和腐化恢復。特別的,這個接口下的函數永遠不能直接暴露給共識消息,這樣會致使打破區塊鏈所承諾的不可修改這一律念。這個結構包含下面這些函數。



PutBlock(blockNumber uint64, block *pb.Block) error 

這個函數根據給定的區塊編號把底層區塊插入到區塊鏈中。注意這是一個不安全的接口,因此它不會有錯誤返回或返回。插入一個比當前區塊高度更高的區塊是被容許的,通用,重寫一個已經提交的區塊也是被容許的。記住,因爲哈希技術使得建立一個鏈上的更早的塊是不可行的,因此這並不影響鏈的可審計性和不可變性。任未嘗試重寫區塊鏈的歷史的操做都能很容易的被偵測到。這個函數通常只用於狀態轉移API。



ApplyStateDelta(id interface{}, delta *statemgmt.StateDelta) error 

   
   
   
   
  • 1
  • 2
這個函數接收狀態變化,並把它應用到當前的狀態。變化量的應用會使得狀態向前或向後轉變,這取決於狀態變化量的構造,與`Executor`方法同樣,`ApplyStateDelta`接受一個一樣會被傳遞給`CommitStateDelta` or `RollbackStateDelta`不透明的接口`id`



CommitStateDelta(id interface{}) error 

   
   
   
   
  • 1
  • 2
這個方法提交在`ApplyStateDelta`中應用的狀態變化。這一般是在調用者調用`ApplyStateDelta`後經過校驗由`GetCurrentStateHash()`得到的狀態哈希以後調用的。這個函數接受與傳遞給`ApplyStateDelta`同樣的`id`。



RollbackStateDelta(id interface{}) error 

   
   
   
   
  • 1
  • 2
這個函數撤銷在`ApplyStateDelta`中應用的狀態變化量。這一般是在調用者調用`ApplyStateDelta`後與由`GetCurrentStateHash()`得到的狀態哈希校驗失敗後調用的。這個函數接受與傳遞給`ApplyStateDelta`同樣的`id`。



EmptyState() error 

   
   
   
   
  • 1
  • 2
這個函數將會刪除整個當前狀態,獲得原始的空狀態。這一般是經過變化量加載整個新的狀態時調用的。這同樣只對狀態轉移API有用。

3.4.9 RemoteLedgers 接口

定義:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
type RemoteLedgers interface { GetRemoteBlocks(peerID uint64, start, finish uint64) (<-chan *pb.SyncBlocks, error) GetRemoteStateSnapshot(peerID uint64) (<-chan *pb.SyncStateSnapshot, error) GetRemoteStateDeltas(peerID uint64, start, finish uint64) (<-chan *pb.SyncStateDeltas, error) }

RemoteLedgers 接口的存在主要是爲了啓用狀態轉移,和向其它副本詢問區塊鏈的狀態。和WritableLedger接口同樣,這不是給正常的操做使用,而是爲追趕,錯誤恢復等操做而設計的。這個接口中的全部函數調用這都有責任來處理超時。這個接口包含下面這些函數:

  •      
         
         
         
    • 1
    GetRemoteBlocks(peerID uint64, start, finish uint64) (<-chan *pb.SyncBlocks, error)

    這個函數嘗試從由peerID指定的 peer 中取出由startfinish標識的範圍中的*pb.SyncBlocks流。通常狀況下,因爲區塊鏈必須是從結束到開始這樣的順序來驗證的,因此start是比finish更高的塊編號。因爲慢速的結構,其它請求的返回可能出如今這個通道中,因此調用者必須驗證返回的是指望的塊。第二次以一樣的peerID來調用這個方法會致使第一次的通道關閉。

  •      
         
         
         
    • 1
    GetRemoteStateSnapshot(peerID uint64) (<-chan *pb.SyncStateSnapshot, error)

    這個函數嘗試從由peerID指定的 peer 中取出*pb.SyncStateSnapshot流。爲了應用結果,首先須要經過WritableLedgerEmptyState調用來清空存在在狀態,而後順序應用包含在流中的變化量。

    -

         
         
         
         
    • 1
    GetRemoteStateDeltas(peerID uint64, start, finish uint64) (<-chan *pb.SyncStateDeltas, error)

    這個函數嘗試從由peerID指定的 peer 中取出由startfinish標識的範圍中的*pb.SyncStateDeltas流。因爲慢速的結構,其它請求的返回可能出如今這個通道中,因此調用者必須驗證返回的是指望的塊變化量。第二次以一樣的peerID來調用這個方法會致使第一次的通道關閉。

3.4.10 controller

3.4.10.1 controller.NewConsenter

簽名:

   
   
   
   
  • 1
func NewConsenter(cpi consensus.CPI) (consenter consensus.Consenter)

這個函數讀取爲peer過程指定的core.yaml配置文件中的peer.validator.consensus的值。鍵peer.validator.consensus的有效值指定運行noops仍是obcpbft共識。(注意,它最終被改變爲noopscustom。在custom狀況下,驗證 peer 將會運行由consensus/config.yaml中定義的共識插件)

插件的做者須要編輯函數體,來保證路由到它們包中正確的構造函數。例如,對於obcpbft 咱們指向obcpft.GetPlugin構造器。

這個函數是當設置返回信息處理器的consenter域時,被helper.NewConsensusHandler調用的。輸入參數cpi是由helper.NewHelper構造器輸出的,並實現了consensus.CPI接口

3.4.11 helper

3.4.11.1 高層次概述

驗證 peer 經過helper.NewConsesusHandler函數(一個處理器工廠),爲每一個鏈接的 peer 創建消息處理器(helper.ConsensusHandler)。每一個進來的消息都會檢查它的類型(helper.HandleMessage);若是這是爲了共識必須到達的消息,它會傳遞到 peer 的共識對象(consensus.Consenter)。其它的信息會傳遞到棧中的下一個信息處理器。

3.4.11.2 helper.ConsensusHandler

定義:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
type ConsensusHandler struct { chatStream peer.ChatStream consenter consensus.Consenter coordinator peer.MessageHandlerCoordinator done chan struct{} peerHandler peer.MessageHandler }

共識中的上下文,咱們只關注域coordinatorconsentercoordinator就像名字隱含的那樣,它被用來在 peer 的信息處理器之間作協調。例如,當 peer 但願Broadcast時,對象被訪問。共識須要到達的共識者會接收到消息並處理它們。

注意,fabric/peer/peer.go定義了peer.MessageHandler (接口),和peer.MessageHandlerCoordinator(接口)類型。

3.4.11.3 helper.NewConsensusHandler

簽名:

   
   
   
   
  • 1
func NewConsensusHandler(coord peer.MessageHandlerCoordinator, stream peer.ChatStream, initiatedStream bool, next peer.MessageHandler) (peer.MessageHandler, error)

建立一個helper.ConsensusHandler對象。爲每一個coordinator設置一樣的消息處理器。同時把consenter設置爲controller.NewConsenter(NewHelper(coord))

3.4.11.4 helper.Helper

定義:

   
   
   
   
  • 1
  • 2
  • 3
type Helper struct { coordinator peer.MessageHandlerCoordinator }

包含驗證peer的coordinator的引用。對象是否爲peer實現了consensus.CPI接口。

3.4.11.5 helper.NewHelper

簽名:

   
   
   
   
  • 1
func NewHelper(mhc peer.MessageHandlerCoordinator) consensus.CPI

返回coordinator被設置爲輸入參數mhchelper.ConsensusHandler消息處理器的coordinator域)的helper.Helper對象。這個對象實現了consensus.CPI接口,從而容許插件與棧進行交互。

3.4.11.6 helper.HandleMessage

回憶一下,helper.NewConsensusHandler返回的helper.ConsesusHandler對象實現了 peer.MessageHandler 接口:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
type MessageHandler interface { RemoteLedger HandleMessage(msg *pb.Message) error SendMessage(msg *pb.Message) error To() (pb.PeerEndpoint, error) Stop() error }

在共識的上下文中,咱們只關心HandleMessage方法。簽名:

   
   
   
   
  • 1
func (handler *ConsensusHandler) HandleMessage(msg *pb.Message) error

這個函數檢查進來的MessageType。有四種狀況:

  1. 等於pb.Message_CONSENSUS:傳遞給處理器的consenter.RecvMsg函數。
  2. 等於pb.Message_CHAIN_TRANSACTION (如:一個外部部署的請求): 一個響應請求首先被髮送給用戶,而後把消息傳遞給consenter.RecvMsg函數
  3. 等於pb.Message_CHAIN_QUERY (如:查詢): 傳遞給helper.doChainQuery方法來在本地執行
  4. 其它: 傳遞給棧中下一個處理器的HandleMessage方法

3.5 事件

事件框架提供了生產和消費預約義或自定義的事件的能力。它有3個基礎組件: 
- 事件流 
- 事件適配器 
- 事件結構

3.5.1 事件流

事件流是用來發送和接收事件的gRPC通道。每一個消費者會與事件框架創建事件流,並快速傳遞它感興趣的事件。事件生成者經過事件流只發送合適的事件給鏈接到生產者的消費者。

事件流初始化緩衝和超時參數。緩衝保存着幾個等待投遞的事件,超時參數在緩衝滿時有三個選項:

  • 若是超時小於0,丟棄新到來的事件
  • 若是超時等於0,阻塞事件知道緩衝再次可用
  • 若是超時大於0,等待指定的超時時間,若是緩衝仍是滿的話就丟棄事件

3.5.1.1 事件生產者

事件生產者暴露函數Send(e *pb.Event)來發送事件,其中Event能夠是預約義的BlockGeneric事件。未來會定義更多的事件來包括其它的fabric元素。

   
   
   
   
  • 1
  • 2
  • 3
  • 4
message Generic { string eventType = 1; bytes payload = 2; }

eventTypepayload是由事件生產者任意定義的。例如,JSON數據可能被用在payload中。鏈碼或插件發出Generic事件來與消費者通信。

3.5.1.2 事件消費者

事件消費者容許外部應用監聽事件。每一個事件消費者經過時間流注冊事件適配器。消費者框架能夠當作是事件流與適配器之間的橋樑。一種典型的事件消費者使用方式:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
adapter = <adapter supplied by the client application to register and receive events> consumerClient = NewEventsClient(<event consumer address>, adapter) consumerClient.Start() ... ... consumerClient.Stop()

3.5.2 事件適配器

事件適配器封裝了三種流交互的切面: 
- 返回全部感興趣的事件列表的接口 
- 當事件消費者框架接受到事件後調用的接口 
- 當事件總線終止時,事件消費者框架會調用的接口

引用的實現提供了Golang指定語言綁定

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
EventAdapter interface { GetInterestedEvents() ([]*ehpb.Interest, error) Recv(msg *ehpb.Event) (bool,error) Disconnected(err error) }

把gRPC當成事件總線協議來使用,容許事件消費者框架對於不一樣的語言的綁定可移植而不影響事件生成者框架。

3.5.3 事件框架

這節詳細描述了事件系統的消息結構。爲了簡單起見,消息直接使用Golang描述。

事件消費者和生產者之間通訊的核心消息是事件。

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
message Event { oneof Event { //consumer events Register register = 1; //producer events Block block = 2; Generic generic = 3; } }

每個上面的定義必須是RegisterBlockGeneric中的一種。

就像以前提到過的同樣,消費者經過與生產者創建鏈接來建立事件總線,併發送Register事件。Register事件實質上是一組聲明消費者感興趣的事件的Interest消息。

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
message Interest { enum ResponseType { //don't send events (used to cancel interest) DONTSEND = 0; //send protobuf objects PROTOBUF = 1; //marshall into JSON structure JSON = 2; } string eventType = 1; ResponseType responseType = 2; }

事件能夠經過protobuf結構直接發送,也能夠經過指定適當的responseType來發送JSON結構。

當前,生產者框架能夠生成BlockGeneric事件。Block是用來封裝區塊鏈中區塊屬性的消息。

4. 安全

這一節將討論下面的圖所展現的設置描述。特別的,系統是由下面這些實體構成的:成員管理基礎架構,如從一個實體集合中區分出不一樣用戶身份的職責(使用系統中任意形式的標識,如:信用卡,身份證),爲這個用戶註冊開戶,並生成必要的證書以便經過fabric成功的建立交易,部署或調用鏈碼。

figure-architecture

  • Peers, 它們被分爲驗證 peer 和非驗證 peer。驗證 peer(也被稱爲驗證器)是爲了規範並處理(有效性檢查,執行並添加到區塊鏈中)用戶消息(交易)提交到網絡上。非驗證 peer(也被稱爲 peer)表明用戶接受用戶交易,並經過一些基本的有效性檢查,而後把交易發送到它們附近的驗證 peer。peer 維護一個最新的區塊鏈副本,只是爲了作驗證,而不會執行交易(處理過程也被稱爲交易驗證)。
  • 註冊到咱們的成員服務管理系統的終端用戶是在獲取被系統認定的身份的全部權以後,並將獲取到的證書安裝到客戶端軟件後,提交交易到系統。
  • 客戶端軟件,爲了以後能完成註冊到咱們成員服務和提交交易到系統所須要安裝在客戶端的軟件
  • 在線錢包,用戶信任的用來維護他們證書的實體,並獨自根據用戶的請求向網絡提交交易。在線錢包配置在他們本身的客戶端軟件中。這個軟件一般是輕量級的,它只需有對本身和本身的錢包的請求作受權。也有 peer 爲一些用戶扮演在線錢包的角色,在接下來的會話中,對在線錢包作了詳細區分。

但願使用fabric的用戶,經過提供以前所討論的身份全部權,在成員管理系統中開立一個帳戶,新的鏈碼被鏈碼建立者(開發)以開發者的形式經過客戶端軟件部署交易的手段,公佈到區塊鏈網絡中。這樣的交易是第一次被 peer 或驗證器接收到,並流傳到整個驗證器網絡中,這個交易被區塊鏈網絡執行並找到本身的位置。用戶一樣能夠經過調用交易調用一個已經部署了的鏈碼

下一節提供了由商業目標所驅動的安全性需求的摘要。而後咱們遊覽一下安全組件和它們的操做,以及如何設計來知足安全需求。

4.1 商業安全需求

這一節表述的與fabric相關的商業安全需求。 
身份和角色管理相結合

爲了充分的支持實際業務的需求,有必要超越確保加密連續性來進行演進。一個可工做的B2B系統必須致力於證實/展現身份或其餘屬性來開展業務。商業交易和金融機構的消費交互須要明確的映射到帳戶的全部者。商務合同一般須要與特定的機構和/或擁有交易的其餘特定性質的各方保證有從屬關係。身份管理是此類系統的關鍵組成部分的兩個緣由是問責制和不可陷害的。

問責制意味着系統的用戶,我的或公司,誰的胡做非爲均可以追溯到併爲本身的行爲負責。在不少狀況下,B2B系統須要它們的會員使用他們的身份(在某些形式)加入到系統中,以確保問責制的實行。問責制和不可陷害的。都是B2B系統的核心安全需求,而且他們很是相關。B2B系統須要保證系統的誠實用戶不會由於其餘用戶的交易而被指控。

此外,一個B2B系統須要具備可再生性和靈活性,以知足參與者角色和/或從屬關係的改變。

交易隱私.

B2B系統對交易的隱私有着強烈的需求,如:容許系統的終端用戶控制他與環境交互和共享的信息。例如:一家企業在交易型B2B系統上開展業務,要求它的交易得其餘企業不可見,而他的行業合做夥伴無權分享機密信息。

在fabric中交易隱私是經過下面非受權用戶的兩個屬性來實現的:

  • 交易匿名,交易的全部者隱藏在一個被稱爲匿名集的組建中,在fabric中,它是用戶的一個集合。

  • 交易不可關聯,同一用戶的兩個或多個交易不能被關聯起來。

根據上下文,非受權用戶能夠是系統之外的任何人,或用戶的子集。

交易隱私與B2B系統的兩個或多個成員之間的保密協議的內容強烈相關。任何受權機制的匿名性和不可關聯性須要在交易時考慮。

經過身份管理協調交易隱私.

就像文檔以後描述的那樣,這裏所採用的方法是使戶隱私來協調身份管理,並使有競爭力的機構能夠像下面同樣在公共的區塊鏈(用於內部和機構間交易)上快速的交易:

  1. 爲交易添加證書來實現「有權限的」區塊鏈

  2. 使用兩層系統:

    1. 向登記的證頒發機構(CA)註冊來得到(相對的) 靜態登記證書 (ECerts)

    2. 經過交易CA獲取能如實但僞匿名的表明登記用戶的交易證書(TCerts).

  3. 提供對系統中未受權會員隱藏交易內用的機制

審計支持. 商業系統偶爾會受到審覈。在這種狀況下,將給予審計員檢查某些交易,某組交易,系統中某些特定用戶或系統本身的一些操做的手段。所以,任意與商業夥伴經過合同協議進行交易的系統都應該提供這樣的能力。

4.2 使用成員管理的用戶隱私

成員管理服務是由網絡上管理用戶身份和隱私的幾個基礎架構來組成的。這些服務驗證用戶的身份,在系統中註冊用戶,併爲他/她提供全部做爲可用、兼容的參數者來建立和/或調用交易所須要的證書。公告密鑰體系(Public Key Infrastructure 
,PKI)是一個基於不只對公共網絡上交換的數據的加密並且能確認對方身份的公共密鑰加密的。PKI管理密鑰和數字證書的生成,發佈和廢止。數字證書是用來創建用戶證書,並對消息簽名的。使用證書籤名的消息保證信息不被篡改。典型的PKI有一個證書頒發機構(CA),一個登記機構(RA),一個證書數據庫,一個證書的存儲。RA是對用戶進行身份驗證,校驗數據的合法性,提交憑據或其餘證據來支持用戶請求一個或多我的反映用戶身份或其餘屬性的可信任機構。CA根據RA的建議爲特定的用戶發放根CA能直接或分級的認證的數字證書。另外,RA的面向用戶的通訊和盡職調查的責任能夠看做CA的一部分。成員服務由下圖展現的實體組成。整套PKI體系的引入增強了B2B系統的強度(超過,如:比特幣)

Figure 1

根證書頒發機構(根CA): 它表明PKI體系中的信任錨。數字證書的驗證遵循信任鏈。根CA是PKI層次結構中最上層的CA。

登記機構(RA): 它是一個能夠肯定想要加入到帶權限區塊鏈的用戶的有效性和身份的可信實體。它是負責與用戶外的帶外通訊來驗證他/她的身份和做用。它是負責與用戶進行頻外通訊來驗證他/她的身份和角色。它建立登記時所須要的註冊證書和根信任上的信息。

註冊證書頒發機構(ECA): 負責給經過提供的註冊憑證驗證的用戶頒發註冊證書(ECerts)

交易認證中心(TCA): 負責給提供了有效註冊證書的用戶頒發交易證書(TCerts)

TLS證書頒發機構(TLS-CA): 負責簽發容許用戶訪問其網絡的TLS證書和憑證。它驗證用戶提供的包含該用戶的特定信息的,用來簽發TLS證書的,證書或證據。

註冊證書(ECerts) 
ECerts是長期證書。它們是頒發給全部角色的,如用戶,非驗證 peer,驗證 peer。在給用戶頒發的狀況下,誰向區塊鏈提交候選人申請誰就擁有TCerts(在下面討論),ECerts有兩種可能的結構和使用模式:

  • Model A: ECerts 包含全部者的身份/註冊ID,並能夠在交易中爲TCert請求提供只用來對名義實體身份作驗證。它們包含兩個密鑰對的公共部分:簽名密鑰對和加密/密鑰協商密鑰對。 ECerts是每一個人均可以訪問。

  • Model B: ECerts 包含全部者的身份/註冊ID,並只爲TCert請求提供名義實體的身份驗證。它們包含一個簽名密鑰對的公共部分,即,簽名驗證公鑰的公共部分。做爲依賴方,ECerts最好只由TCA和審計人員訪問。他們對交易是不可見的,所以(不像TCerts)簽名密鑰對不在這一層級發揮不可抵賴的做用。

交易證書(TCerts) 
TCerts是每一個交易的短時間證書。它們是由TCA根據受權的用戶請求頒發的。它們安全的給一個交易受權,並能夠被配置爲隱藏誰參與了交易或選擇性地暴露這樣身份註冊ID這樣的信息。他們包含簽名密鑰對的公共部分,並能夠被配置爲包含一個密鑰協議的密鑰對的公共部分。他們僅頒發給用戶。它們惟一的關聯到全部者,它們能夠被配置爲這個關聯只有TCA知道知道(和受權審覈員)。TCert能夠配置成不攜帶用戶的身份信息。它們使得用戶不只以匿名方式參與到系統中,並且阻止了交易之間的關聯性。

然而,審計能力和問責制的要求TCA可以獲取給定身份的TCert,或者獲取指定TCert的全部者。有關TCerts如何在部署和調用交易中使用的詳細信息參見4.3節,交易安全是在基礎設施層面提供的。

TCerts可容納的加密或密鑰協議的公共密鑰(以及數字簽名的驗證公鑰)。 
若是配備好TCert,那麼就須要註冊證書不能包含加密或密鑰協議的公鑰。

這樣的密鑰協議的公鑰,Key_Agreement_TCertPub_Key,能夠由交易認證機構(TCA)使用與生成Signature_Verification_TCertPub_Key一樣的方法,使用TCertIndex + 1 而不是TCertIndex來做爲索引個值來生成,其中TCertIndex是由TCA爲了恢復而隱藏在TCert中的。

交易證書(TCert)的結構以下所述: 
* TCertID – 交易證書ID(爲了不經過隱藏的註冊ID發生的意外可關聯性,最好由TCA隨機生成). 
* Hidden Enrollment ID: AES_EncryptK(enrollmentID), 其中密鑰K = [HMAC(Pre-K, TCertID)]256位截斷其中爲每一個K定義三個不一樣的密鑰分配方案:(a), (b) and (c)。 
* Hidden Private Keys Extraction: AES_EncryptTCertOwner_EncryptKey(TCertIndex || 已知的填充/校驗檢查向量) 其中||表示鏈接,其中各個批次具備被加到計數器的惟一(每一個批次)的時間戳/隨機偏移量(這個實現中初始化爲1)來生成TCertIndex。該計數器能夠經過每次增長2來適應TCA生成公鑰,並由這兩種類型的私鑰的TCert全部者來恢復,如簽名密鑰對和密鑰協商密鑰對。 
* Sign Verification Public Key – TCert簽名驗證的公共密鑰。 
* Key Agreement Public Key – TCert密鑰協商的公鑰. 
* Validity period – 該交易證書可用於交易的外/外部簽名的時間窗口。

這裏至少有三種方式來配置考慮了隱藏註冊ID域密鑰的分配方案: 
(a) Pre-K在註冊期間發給用戶,peer 和審計員,並對TCA和受權的審計員可用。它可能,例如由Kchain派生(會在這個規範的後面描述)或爲了鏈碼的保密性使用獨立的key(s)。

(b) Pre-K對驗證器,TCA和受權的審計員可用。K是在驗證器成功響應用戶的查詢交易(經過TLS)後可用給的。查詢交易可使用與調用交易相同的格式。對應下面的例1,若是查詢用戶又有部署交易的ACL中的一張TCert,那麼就能夠獲得建立這個部署交易的用戶的註冊ID(enrollmentID)。對應下面的例2,若是查詢所使用TCert的註冊ID(enrollmentID)與部署交易中訪問控制域的其中一個隸屬關係/角色匹配,那麼就能夠獲得建立這個部署交易的用戶的註冊ID(enrollmentID)。

Example 1:

Example 1

Example 2:

Example 2

(c) 
Pre-K對TCA和受權的審計員可用。對於批量中的全部TCert,TCert特有的K能夠和TCert一塊兒分發給TCert的全部者(經過TLS)。這樣就經過K的TCert全部者啓用目標釋放(TCert全部者的註冊ID的可信通知)。這樣的目標釋放可使用預約收件人的密鑰協商公鑰和/或PKchain其中SKchain就像規範的後面描述的那樣對驗證器可用。這樣目標釋放給其它合同的參與者也能夠被歸入到這個交易或在頻外完成。

若是TCert與上述的ECert模型A的結合使用,那麼使用K不發送給TCert的全部者的方案(c)就足夠了。 
若是TCert與上述的ECert模型A的結合使用,那麼TCert中的密鑰協商的公鑰域可能就不須要了。

交易認證中心(TCA)以批量的方式返回TCert,每一個批量包含不是每一個TCert都有的,可是和TCert的批量一塊兒傳遞到客戶端的KeyDF_Key(Key-Derivation-Function Key) (通用TLS)。KeyDF_Key容許TCert的擁有者派生出真正用於從AES_EncryptTCertOwner_EncryptKey(TCertIndex || 已知的填充/校驗檢查向量)的TCertIndex恢復的TCertOwner_EncryptKey。

TLS證書(TLS-Certs) 
TLS-Certs 是用於系統/組件到系統/組件間通信所使用的證書。他們包含全部者的身份信息,使用是爲了保證網絡基本的安全。

成員管理的這個實現提供下面描述的基礎功能:ECerts是沒有到期/廢止的;TCert的過時是由驗證週期的時間窗口提供的。TCerts是沒有廢止的。ECA,TCA和TLS CA證書是自簽名的,其中TLS CA提供信任錨點。

4.2.1 用戶/客戶端註冊過程

下面這個圖高度歸納了用戶註冊過程,它具備離線和在線階段。

Registration

離線處理: 在第一步中,每一個用戶/非驗證 peer /驗證 peer 有權在線下將較強的識別憑證(身份證實)到導入到註冊機構(RA)。這須要在頻外給RA提供爲用戶建立(存儲)帳號的證據憑證。第二步,RA返回對應的用戶名/密碼和信任錨點(這個實現中是TLS-CA Cert)給用戶。若是用戶訪問了本地客戶端,那麼這是客戶端能夠以TLS-CA證書做爲信任錨點來提供安全保障的一種方法。

在線階段: 第三步,用戶鏈接客戶端來請求註冊到系統中。用戶發送它的用戶名和密碼給客戶端。客戶端表明用戶發送請求給PKI框架。第四步,接受包,第五步,包含其中一些對應於由客戶端私有/祕密密鑰的若干證書。一旦客戶端驗證包中全部加密材料是正確/有效的,他就把證書存儲在本地並通知用戶。這時候用戶註冊就完成了。

Figure 4

圖4展現了註冊的詳細過程。PKI框架具備RA, ECA, 
TCA和TLS-CA這些實體。第一步只收,RA調用「AddEntry」函數爲它的數據庫輸入(用戶名/密碼)。這時候用戶已正式註冊到系統數據庫中。客戶端須要TLS-CA證書(看成信任錨點)來驗證與服務器之間的TLS握手是正確的。第四步,客戶端發送包含註冊公鑰和像用戶名,密碼這樣的附加身份信息的註冊請求到ECA(經過TLS備案層協議)。ECA驗證這個用戶是否真實存在於數據庫。一旦它確認用戶有權限提交他/她的註冊公鑰,那麼ECA就會驗證它。這個註冊信息是一次性的。ECA會更新它的數據庫來標識這條註冊信息(用戶名,密碼)不能再被使用。ECA構造,簽名並送回一張包含用戶註冊公鑰的(步驟5)註冊證書(ECert)。它一樣會發送未來會用到(客戶端須要向TCA證實他/她的ECert使用爭取的ECA建立的)的ECA證書(ECA-Cert))。(儘管ECA-Cert在最初的實現中是自簽名的,TCA,TLS-CA和ECA是共址)第六步,客戶端驗證ECert中的公鑰是最初由客戶端提交的(即ECA沒有做弊)。它一樣驗證ECert中的全部指望的信息存在且形式正確。

一樣的,在第七步,客戶端發送包含它的公鑰和身份的註冊信息到TLS-CA。TLS-CA驗證該用戶在數據庫中真實存在。TLS-CA生成,簽名包含用戶TLS公鑰的一張TLS-Cert(步驟8)。TLS-CA發送TLS-Cert和它的證書(TLS-CA Cert)。第九步相似於第六步,客戶端驗證TLS Cert中的公鑰是最初由客戶端提交的,TLS Cert中的信息是完整且形式正確。在第十步,客戶端在本地存儲中保存這兩張證書的全部證書。這時候用戶就註冊完成了。

在這個版本的實現中驗證器的註冊過程和 peer 的是同樣的。儘管,不一樣的實現可能使得驗證器直接經過在線過程來註冊。

Figure 5 
Figure 6

客戶端: 請求TCert批量須要包含(另外計數),ECert和使用ECert私鑰簽名的請求(其中ECert的私鑰使用本地存儲獲取的)

TCA爲批量生成TCerts: 生成密鑰派生函數的密鑰,KeyDF_Key, 看成HMAC(TCA_KDF_Key, EnrollPub_Key). 爲每張TCert生成公鑰(使用TCertPub_Key = EnrollPub_Key + ExpansionValue G, 其中384位的ExpansionValue = HMAC(Expansion_Key, TCertIndex) 和384位的Expansion_Key = HMAC(KeyDF_Key, 「2」)). 生成每一個AES_EncryptTCertOwner_EncryptKey(TCertIndex || 已知的填充/校驗檢查向量), 其中|| 表示鏈接,且TCertOwner_EncryptKey被看成[HMAC(KeyDF_Key, 
「1」)]派生256位截斷.

客戶端: 爲部署,調用和查詢,根據TCert來生成TCert的私鑰:KeyDF_Key和ECert的私鑰須要從本地存儲中獲取。KeyDF_Key是用來派生被看成[HMAC(KeyDF_Key, 「1」)]256位截斷的TCertOwner_EncryptKey;TCertOwner_EncryptKey是用來對TCert中的 AES_EncryptTCertOwner_EncryptKey(TCertIndex || 
已知的填充/校驗檢查向量)域解密的;TCertIndex是用來派生TCert的私鑰的: TCertPriv_Key = (EnrollPriv_Key + ExpansionValue)模n,其中384位的ExpansionValue = HMAC(Expansion_Key, TCertIndex),384位的Expansion_Key = HMAC(KeyDF_Key, 「2」)。

4.2.2 過時和廢止證書

實際是支持交易證書過時的。一張交易證書能使用的時間窗是由‘validity period’標識的。實現過時支持的挑戰在於系統的分佈式特性。也就是說,全部驗證明體必須共享相同的信息;即,與交易相關的有效期驗證須要保證一致性。爲了保證有效期的驗證在全部的驗證器間保持一致,有效期標識這一律念被引入。這個標識扮演着邏輯時鐘,使得系統能夠惟一識別有效期。在創世紀時,鏈的「當前有效期」由TCA初始化。相當重要的是,此有效期標識符給出隨時間單調增長的值,這使得它規定了有效期間總次序。

對於指定類型的交易,系統交易有效週期標識是用來一塊兒向區塊鏈公佈有效期滿的。系統交易涉及已經在創世紀塊被定義和做爲基礎設施的一部分的合同。有效週期標識是由TCA週期性的調用鏈碼來更新的。注意,只有TCA容許更新有效期。TCA經過給定義了有效期區間的‘not-before’和‘not-after’這兩個域設置合適的整數值來爲每一個交易證書設置有效期。

TCert過時: 
在處理TCert時,驗證器從狀態表中讀取與總帳中的‘current validity period’相關的值來驗證與交易相關的外部證書目前是否有效。狀態表中的當前值須要落在TCert的‘not-before’和‘not-after’這兩個子域所定義的區間中。若是知足,那麼驗證器就繼續處理交易。若是當前值沒有在這個區間中,那麼TCert已通過期或還沒生效,那麼驗證器就中止處理交易。

ECert過時: 
註冊證書與交易證書具備不一樣的有效期長度。

廢止是由證書廢止列表(CRLs)來支持的,CRLs鑑定廢止的證書。CRLs的改變,增量的差別經過區塊鏈來公佈

4.3 基礎設施層面提供的交易安全

fabric中的交易是經過提交用戶-消息來引入到總帳中的。就像以前章節討論的那樣,這些信息具備指定的結構,且容許用戶部署新的鏈碼,調用已經存在的鏈碼,或查詢已經存在的鏈碼的狀態。所以交易的方式被規範,公佈和處理在整個系統提供的隱私和安全中起着重要的做用。

一方面咱們的成員服務經過檢查交易是由系統的有效用戶建立的來提供驗證交易的手段,爲了把用戶身份和交易撇清,可是在特定條件下又須要追蹤特定個體的交易(執法,審計)。也就是說,成員服務提供結合用戶隱私與問責制和不可抵賴性來提供交易認證機制。

另外一方面,fabric的成員服務不能單獨提供完整的用戶活動隱私。首先fabric提供完整的隱私保護條款,隱私保護認證機制須要經過交易保密協同。很明顯,若是認爲鏈碼的內容可能會泄漏建立者的信息,那麼這就打破了鏈碼建立者的隱私要求。第一小節討論交易的保密性。



爲鏈碼的調用強制訪問控制是一個重要的安全要求。fabric暴露給應用程序(例如,鏈碼建立者)這意味着當應用利用fabric的成員服務是,須要應用本身調用訪問控制。4.4節詳細闡述了這一點。

重放攻擊是鏈碼安全的另外一個重要方面,做爲惡意用戶可能複製一個以前的,已經加入到區塊鏈中的交易,並向網絡重放它來篡改它的操做。這是第4.3.3節的話題。

本節的其他部分介紹了基礎設施中的安全機制是如何歸入到交易的生命週期中,並分別詳細介紹每個安全機制。

4.3.1 交易安全的生命週期

交易在客戶端建立。客戶端能夠是普通的客戶端,或更專用的應用,即,經過區塊鏈處理(服務器)或調用(客戶端)具體鏈碼的軟件部分。這樣的應用是創建在平臺(客戶端)上的,並在4.4節中詳細介紹。

新鏈碼的開發者能夠經過這些fabric的基礎設施來新部署交易: 
* 但願交易符合保密/安全的版本和類型 
* 但願訪問部分鏈碼的用戶有適當的(讀)訪問權限 

* 鏈碼規範 
* 代碼元數據,包含的信息須要在鏈碼執行時傳遞給它(即,配置參數),和 
* 附加在交易結構上的並只在應用部署鏈碼時使用的交易元數據

具備保密限制的鏈碼的調用和查詢交易都是用相似的方式建立。交易者提供須要執行的鏈碼的標識,要調用的函數的名稱及其參數。可選的,調用者能夠傳遞在鏈碼執行的時候所須要提供的代碼調用元數據給交易建立函數。交易元數據是調用者的應用程序或調用者自己爲了它本身的目的所使用的另一個域。

最後,交易在客戶端,經過它們的建立者的證書籤名,併發送給驗證器網絡。 
驗證器接受私密交易,並經過下列階段傳遞它們: 
預驗證階段,驗證器根據根證書頒發機構來驗證交易證書,驗證交易(靜態的)中包含交易證書籤名,並驗證交易是否爲重放(參見,下面關於重放攻擊的詳細信息) 
Validators receive the confidential transactions, and pass them through the following phases: 
共識階段, 驗證器把這筆交易加入到交易的全序列表中(最終包含在總帳中) 
預執行階段, 驗證交易/註冊證書是否在當前的有效期中 
解密交易(若是交易是加密的),並驗證交易明文的形式正確(即,符合調用訪問控制,包含TCert形式正確) 
在當前處理塊的事務中,也執行了簡單的重放攻擊檢查。 
執行階段, (解密的) 鏈碼和相關的代碼元數據被傳遞給容器,並執行。 
提交 階段, (解密的)更新的鏈碼的狀態和交易自己被提交到總帳中。

4.3.2 交易保密性

在開發人員的要求下,交易機密性要求鏈碼的原文,即代碼,描述,是不能被未受權的實體(即,未被開發人員受權的用戶或 peer)訪問或推導(assuming a computational attacker)出來。對於後者,部署調用交易的內容始終被隱藏對鏈碼的保密需求是相當重要的。本着一樣的精神,未受權方,不該該能聯繫鏈碼(調用交易)與鏈碼自己(部署交易)之間的調用關係或他們之間的調用。

任何候選的解決方案的附加要求是,知足並支持底層的成員服務的隱私和安全規定。此外,在fabric中他不該該阻止任何鏈碼函數的調用訪問控制,或在應用上實現強制的訪問控制機制(參看4.4小結)。

下面提供了以用戶的粒度來設置的交易機密性機制的規範。最後小結提供了一些如何在驗證器的層次來擴展這個功能的方針。當前版本所支持的特性和他的安全條款能夠在4.7節中找到。

目標是達到容許任意的子集實體被容許或限制訪問鏈碼的下面所展現的部分: 
1. 鏈碼函數頭,即,包含在鏈碼中函數的原型 


2. 鏈碼[調用&] 狀態,即, 當一個或多個函數被調用時,連續更新的特定鏈碼的狀態。 
3. 全部上面所說的

注意,這樣的設計爲應用提供利用fabric的成員管理基礎設施和公鑰基礎設施來創建本身的訪問控制策略和執法機制的能力。

4.3.2.1 針對用戶的保密

爲了支持細粒度的保密控制,即,爲鏈碼建立者定義的用戶的子集,限制鏈碼的明文讀權限,一條綁定到單個長週期的加密密鑰對的鏈(PKchain, SKchain)。

儘管這個密鑰對的初始化是經過每條鏈的PKI來存儲和維護的,在以後的版本中,這個限制將會去除。鏈(和相關的密鑰對)能夠由任意帶有特定(管理)權限的用戶經過區塊鏈來觸發(參看4.3.2.2小節)

搭建. 在註冊階段, 用戶獲取(像以前同樣)一張註冊證書,爲用戶ui標記爲Certui,其中每一個驗證器vj獲取的註冊證書標記爲Certvj。註冊會給用戶或驗證器發放下面這些證書:

  1. 用戶:

    a. 聲明並授予本身簽名密鑰對(spku, ssku)

    b. 申明並授予他們加密密鑰對(epku, esku),

    c. 獲取鏈PKchain的加密(公共)密鑰

  2. 驗證器:

    a. 聲明並授予他們簽名密鑰對(spkv, sskv)

    b. 申明並授予他們加密密鑰對 (epkv, eskv),

    c. 獲取鏈SKchain的解密(祕密)密鑰

所以,註冊證書包含兩個密鑰對的公共部分: 
* 一個簽名密鑰對[爲驗證器標記爲(spkvj,sskvj),爲用戶標記爲(spkui, sskui)] 和 
* 一個加密密鑰對[爲驗證器標記爲(epkvj,eskvj),爲用戶標記爲(epkui, eskui)]

鏈,驗證器和用戶註冊公鑰是全部人均可以訪問的。

除了註冊證書,用戶但願經過交易證書的方式匿名的參與到交易中。用戶的簡單交易證書ui被標記爲TCertui。交易證書包含的簽名密鑰對的公共部分標記爲(tpkui,tskui)。

下面的章節歸納性的描述瞭如何以用戶粒度的方式提供訪問控制。

部署交易的結構. 
下圖描繪了典型的啓用了保密性的部署交易的結構。

FirstRelease-deploy

注意,部署交易由幾部分組成: 
基本信息部分: 包含交易管理員的詳細信息,即這個交易對應於哪一個鏈(連接的),交易的類型(設置」deplTrans」),實現的保密協議的版本號,建立者的身份(由註冊證書的交易證書來表達),和主要爲了防止重放攻擊的Nonce。 
代碼信息部分: 包含鏈碼的源碼,函數頭信息。就像下圖所展現的那樣,有一個對稱密鑰(KC)用於鏈碼的源代碼,另外一個對稱密鑰(KH)用於函數原型。鏈碼的建立者會對明文代碼作簽名,使得信函不能脫離交易,也不能被其餘東西替代。 
鏈驗證器部分: 爲了(i)解密鏈碼的源碼(KC),(ii)解密函數頭,和(iii)當鏈碼根據(KS)調用時加密狀態。尤爲是鏈碼的建立者爲他部署的鏈碼生產加密密鑰對(PKC, SKC)。它而後使用PKC加密全部與鏈碼相關的密鑰: 

[(」code」,K C) ,(」headr」,K H),(」code-state」,K S), Sig TCertuc(*)] PKc,
並把 
where appropriate key material is passed to the 
In particular, the chain-code creator 
generates an encryption key-pair for the chain-code it deploys 
(PK C , SK C ). It then uses PK C  
to encrypt all the keys associated to the chain-code: 
[(」code」,K C) ,(」headr」,K H),(」code-state」,K S), Sig TCertuc(*)] PKc,
私鑰SK C 經過鏈指定的公鑰: 
[(」chaincode」,SK C), Sig TCertuc(*)] PKchain.

傳遞給驗證器。 
合同用戶 部分: 合同用戶的公共密鑰,即具備部分鏈碼讀權限的用戶,根據他們的訪問權限加密密鑰:

  1. SKc使得用戶能讀取與這段鏈碼相關的任意信息(調用,狀態,等)

  2. KC使用戶只能讀取合同代碼

  3. KH 使用戶只能讀取頭信息

  4. KS使用戶只能讀取與合同相關的狀態

    最後給用戶發放一個合同的公鑰PKc,使得他們能夠根據合同加密信息,從而驗證器(or any in possession of SKc)能夠讀取它。每一個合同用戶的交易證書被添加到交易中,並跟隨在用戶信息以後。這可使得用戶能夠很容易的搜索到有他們參與的交易。注意,爲了信函能夠在本地不保存任何狀態的狀況下也能經過分析總帳來獲取這筆交易,部署交易也會添加信息到鏈碼建立者uc

整個交易由鏈碼的建立者的證書籤名,即:有信函決定使用註冊仍是交易證書。 
兩個值得注意的要點: 
* 交易中的信息是以加密的方式存儲的,即,code-functions, 
* code-hdrs在使用TCert加密整個交易以前會用想用的TCert簽名,或使用不一樣的TCert或ECert(若是交易的部署須要帶上用戶的身份。一個綁定到底層交易的載體須要包含在簽名信息中,即,交易的TCert的哈希是簽名的,所以mix\&match攻擊是不可能的。咱們在4.4節中詳細討論這樣的攻擊,在這種狀況下,攻擊者不能從他看到的交易中分離出對應的密文,即,代碼信息,並在另外一個交易中使用它。很明顯,這樣會打亂整個系統的操做,鏈碼首先有用戶A建立,如今還屬於惡意用戶B(可能沒有權限讀取它) 
* 爲了給用戶提供交叉驗證的能力,會給他們訪問正確密鑰的權限,即給其餘用戶相同的密鑰,使用密鑰K對交易加密成密文,伴隨着對K的承諾,而這一承諾值開放給全部在合同中有權訪問K的用戶,和鏈驗證器。 

在這種狀況下,誰有權訪問該密鑰,誰就能夠驗證密鑰是否正確傳遞給它。爲了不混亂,這部分在上圖中省略了。

調用交易的結構. 
下圖結構化描述了,交易調用鏈碼會觸發使用用戶指定的參數來執行鏈碼中的函數

FirstRelease-deploy

調用交易和部署交易同樣由一個基本信息, 代碼信息鏈驗證器和一個合同用戶,並使用一張調用者的交易證書對全部進行簽名。

  • 基本信息 與部署交易中對應部分遵循相同的結構。惟一的不一樣是交易類型被設置爲」InvocTx」,鏈碼的標識符或名字是經過鏈指定的加密(公共)密鑰來加密的。

  • 代碼信息 部署交易中的對應結構具備相同展示。在部署交易中做爲代碼有效載荷,如今由函數調用明細(調用函數的名字,對應的參數),由應用提供的代碼元數據和交易建立者(調用者 
    u)的證書,TCertu。在部署交易的狀況下,代碼有效載荷和是經過調用者u的交易證書TCertu簽名的。在部署交易的狀況下,代碼元數據,交易數據是由應用提供來使得信函能夠實現他本身的訪問控制機制和角色(詳見4.4節)。

  • 最後,合同用戶和鏈驗證器部分提供密鑰和有效荷載是使用調用者的密鑰加密的,並分別鏈加密密鑰。在收到此類交易,驗證器解密 [code-name]PKchain使用鏈指定的密鑰SKchain ,並獲取被調用的鏈碼身份。給定的信封,驗證器從本地的獲取鏈碼的解密密鑰SKc,並使用他來解密鏈驗證器的信息,使用對稱密鑰 
    KI對調用交易的有效荷載加密。給定信函,驗證器解密代碼信息,並使用指定的參數和附加的代碼元數據(參看4.4節的代碼元數據詳細信息)執行鏈碼。當鏈碼執行後,鏈碼的狀態可能就更新了。 
    加密所使用的狀態特定的密鑰Ks在鏈碼部署的時候就定義了。尤爲是,在當前版本中Ks 和KiTx被設計成同樣的(參看4.7節)。

查詢交易的結構. 
查詢交易和調用交易具備一樣的格式。惟一的區別是查詢交易對鏈碼的狀態沒有影響,且不須要在執行完成以後獲取(解密的)並/或更新(加密的)狀態。

4.3.2.2 針對驗證器的保密

這節闡述瞭如何處理當前鏈中的不一樣(或子集)集合的驗證器下的一些交易的執行。本節中抑制IP限制,將在接下的幾個星期中進行擴展。

4.3.3 防重放攻擊

在重放攻擊中,攻擊者「重放」他在網絡上「竊聽」或在區塊鏈」看到」的消息 
因爲這樣會致使整個驗證明體重作計算密集型的動做(鏈碼調用)和/或影響對應的鏈碼的狀態,同時它在攻擊側又只須要不多或沒有資源,因此重放攻擊在這裏是一個比較大的問題。若是交易是一個支付交易,那麼問題就更大了,重放可能會致使在不須要付款人的參與下,多於一次的支付。 
當前系統使用如下方式來防止重放攻擊: 
* 在系統中記錄交易的哈希。這個方法要求驗證器爲每一個交易維護一個哈希日誌,發佈到網絡上,並把每一個新來的交易與本地存儲的交易記錄作對比。很明顯這樣的方法是不能擴展到大網絡的,也很容易致使驗證器花了比真正作交易還多的時間在檢查交易是否是重放上。 
* 利用每一個用戶身份維護的狀態(Ethereum).Ethereum保存一些狀態,即,對每一個身份/僞匿名維護他們本身的計數器(初始化爲1)。每次用戶使用他的身份/僞匿名發送交易是,他都把他的本地計數器加一,並把結果加入到交易中。交易隨後使用用戶的身份簽名,併發送到網絡上。當收到交易時,驗證器檢查計數器並與本地存儲的作比較;若是值是同樣的,那就增長這個身份在本地的計數器,並接受交易。不然把交易看成無效或重放的而拒絕掉。儘管這樣的方法在有限個用戶身份/僞匿名(即,不太多)下工做良好。它最終在用戶每次交易都使用不一樣的標識(交易證書),用戶的僞匿名與交易數量成正比時沒法擴展。

其餘資產管理系統,即比特幣,雖然沒有直接處理重放攻擊,但它防止了重放。在管理(數字)資產的系統中,狀態是基於每一個資產來維護的,即,驗證器只保存誰擁有什麼的記錄。由於交易的重放根據協議(由於只能由資產/硬幣舊的全部者衍生出來)能夠直接認爲無效的,因此防重放攻擊是這種方式的直接結果。儘管這合適資產管理系統,可是這並不表示在更通常的資產管理中須要比特幣系統。

在fabric中,防重放攻擊使用混合方法。 
這就是,用戶在交易中添加一個依賴於交易是匿名(經過交易證書籤名)或不匿名(經過長期的註冊證書籤名)來生成的nonce。更具體的: 
* 用戶經過註冊證書來提交的交易須要包含nonce。其中nonce是在以前使用同一證書的交易中的nonce函數(即計數器或哈希)。包含在每張註冊證書的第一次交易中的nonce能夠是系統預約義的(即,包含在創始塊中)或由用戶指定。在第一種狀況中,創世區塊須要包含nonceall,即,一個固定的數字和nonce被用戶與身份IDA一塊兒用來爲他的第一筆註冊證書籤名的交易將會 

nonce round0IDA <- hash(IDA, nonce all),

其中IDA出如今註冊證書中。從該點以後的這個用戶關於註冊證書的連續交易須要包含下面這樣的nonce 
nonce roundiIDA <- hash(nonce round{i-1}IDA),

這表示第i次交易的nonce須要使用這樣證書第{i-1}次交易的nonce的哈希。驗證器持續處理他們收到的只要其知足上述條件的交易。一旦交易格式驗證成功,驗證器就使用nonce更新他們的數據庫。

存儲開銷:

  1. 在用戶側:只有最近使用的nonce

  2. 在驗證器側: O(n), 其中n是用戶的數量

    • 用戶使用交易證書提交的交易須要包含一個隨機的nonce,這樣就保證兩個交易不會產生一樣的哈希。若是交易證書沒有過時的話,驗證器就向本地數據庫存儲這筆交易的哈希。爲了防止存儲大量的哈希,交易證書的有效期被利用。特別是驗證器爲當前或將來有效週期來維護一個接受交易哈希的更新記錄。

    存儲開銷 (這裏隻影響驗證器): O(m), 其中m近似於有效期內的交易和對應的有效標識的數量(見下方)

4.4 應用的訪問控制功能

應用是抱在區塊鏈客戶端軟件上的一個具備特定功能的軟件。如餐桌預訂。應用軟件有一個版本開發商,使後者能夠生成和管理一些這個應用所服務的行業所須要的鏈碼,並且,客戶端版本能夠容許應用的終端用戶調用這些鏈碼。應用能夠選擇是否對終端用戶屏蔽區塊鏈。

本節介紹應用中如何使用鏈碼來實現本身的訪問控制策略,並提供如何使用成員服務來達到相同的目的。

這個報告能夠根據應用分爲調用訪問控制,和讀取訪問控制。

4.4.1 調用訪問控制

爲了容許應用在應用層安全的實現本身的訪問問控制,fabric須要提供特定的支持。在下面的章節中,咱們詳細的說明的fabric爲了達到這個目的而給應用提供的工具,併爲應用如何來使用它們使得後者能安全的執行訪問控制提供方針。

來自基礎設施的支持. 
把鏈碼的建立者標記爲 uc,爲了安全的實現應用層本身的調用訪問控制,fabric必須須要提供特定的支持。 
更具體的,fabric層提供下面的訪問能力:

  1. 客戶端-應用能夠請求fabric使用指定的客戶端擁有的交易證書或註冊證書來簽名和驗證任何消息; 這是由Certificate Handler interface來處理的。

  2. 客戶端-應用能夠請求fabric一個綁定將身份驗證數據綁定到底層的交易傳輸的應用程序;這是由Certificate Handler interface來處理的。

  3. 爲了支持交易格式,容許指定被傳遞給鏈碼在部署和調用時間的應用的元數據;後者被標記爲代碼元數據。

Certificate Handler接口容許使用底層證書的密鑰對來對任意消息進行簽名和驗證。證書能夠是TCert或ECert。

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
// CertificateHandler exposes methods to deal with an ECert/TCert type CertificateHandler interface { // GetCertificate returns the certificate's DER GetCertificate() []byte // Sign signs msg using the signing key corresponding to the certificate Sign(msg []byte) ([]byte, error) // Verify verifies msg using the verifying key corresponding to the certificate Verify(signature []byte, msg []byte) error // GetTransactionHandler returns a new transaction handler relative to this certificate GetTransactionHandler() (TransactionHandler, error) }

Transaction Handler藉口容許建立交易和訪問可利用的底層綁定來連接應用數據到底層交易。綁定是在網絡傳輸協議引入的概念(參見,https://tools.ietf.org/html/rfc5056)記做通道綁定,*容許應用在網絡層兩端的創建安全通道,與在高層的認證綁定和在低層是同樣的。 
這容許應用代理保護低層會話,這具備不少性能優點。* 
交易綁定提供識別fabric層次交易的身份,這就是應用數據要加入到總帳的容器。

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
// TransactionHandler represents a single transaction that can be uniquely determined or identified by the output of the GetBinding method. // This transaction is linked to a single Certificate (TCert or ECert). type TransactionHandler interface { // GetCertificateHandler returns the certificate handler relative to the certificate mapped to this transaction GetCertificateHandler() (CertificateHandler, error) // GetBinding returns a binding to the underlying transaction (container) GetBinding() ([]byte, error) // NewChaincodeDeployTransaction is used to deploy chaincode NewChaincodeDeployTransaction(chaincodeDeploymentSpec *obc.ChaincodeDeploymentSpec, uuid string) (*obc.Transaction, error) // NewChaincodeExecute is used to execute chaincode's functions NewChaincodeExecute(chaincodeInvocation *obc.ChaincodeInvocationSpec, uuid string) (*obc.Transaction, error) // NewChaincodeQuery is used to query chaincode's functions NewChaincodeQuery(chaincodeInvocation *obc.ChaincodeInvocationSpec, uuid string) (*obc.Transaction, error) }

對於版本1,綁定hash(TCert, Nonce)組成,其中TCert是給整個交易簽名的交易證書,Nonce是交易所使用的nonce。

Client接口更通用,提供以前接口實例的手段。

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
type Client interface { ... // GetEnrollmentCertHandler returns a CertificateHandler whose certificate is the enrollment certificate GetEnrollmentCertificateHandler() (CertificateHandler, error) // GetTCertHandlerNext returns a CertificateHandler whose certificate is the next available TCert GetTCertificateHandlerNext() (CertificateHandler, error) // GetTCertHandlerFromDER returns a CertificateHandler whose certificate is the one passed GetTCertificateHandlerFromDER(der []byte) (CertificateHandler, error) }

爲了向鏈碼調用控制提供應用級別的的訪問控制列表,fabric的交易和鏈碼指定的格式須要存儲在應用特定元數據的額外的域。 
這個域在圖1中經過元數據展現出來。這個域的內容是由應用在交易建立的時候決定的。fabric成把它看成非結構化的字節流。

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
message ChaincodeSpec { ... ConfidentialityLevel confidentialityLevel; bytes metadata; ... } message Transaction { ... bytes payload; bytes metadata; ... }

爲了幫助鏈碼執行,在鏈碼調用的時候,驗證器爲鏈碼提供額外信息,如元數據和綁定。

應用調用訪問控制. 
這一節描述應用如何使用fabric提供的手段在它的鏈碼函數上實現它本身的訪問控制。 
這裏考慮的狀況包括:

  1. C: 是隻包含一個函數的鏈碼,如,被成爲hello

  2. uc: 是C的部署;

  3. ui: 是被受權調用C的用戶。用戶uc但願只有ui能夠調用函數hello

鏈碼部署: 在部署的時候,uc具備被部署交易元數據的徹底控制權,可硬存儲一個ACL的列表(每一個函數一個),或一個應用所須要的角色的列表。存儲在ACL中的格式取決於部署的交易,鏈碼須要在執行時解析元數據。 
爲了定義每一個列表/角色,uc可使用ui的任意TCerts/Certs(或,若是可接受,其餘分配了權限或角色的用戶)。把它記做TCertui。 
開發者和受權用戶之間的TCerts和 Certs交換實在頻外渠道進行的。

假設應用的uc須要調用 hello函數,某個消息M就被受權給受權的調用者(在咱們的例子中是ui)。 
能夠區分爲如下兩種狀況:

  1. M是鏈碼的其中一個函數參數;

  2. M是調用信息本事,如函數名,函數參數。

鏈碼調用: 
爲了調用C, ui的應用須要使用TCert/ECert對M簽名,用來識別ui在相關的部署交易的元數據中的參與身份。即,TCertui。更具體的,ui的客戶端應用作一下步驟:

  1. Certui, cHandler獲取CertificateHandler

  2. 獲取新的TransactionHandler來執行交易, txHandler相對與他的下一個有效的TCert或他的ECert

  3. 經過調用txHandler.getBinding()來獲得txHandler的綁定

  4. 經過調用cHandler.Sign(‘*M || txBinding’)來對M || txBinding’簽名, *sigma是簽名函數的輸出。

  5. 經過調用來發佈一個新的執行交易,txHandler.NewChaincodeExecute(…). 如今, sigma能夠以一個傳遞給函數(情形1)參數或payload的元數據段的一部分(情形2)的身份包含在交易中。

鏈碼處理: 
驗證器, 從ui處接受到的執行交易將提供如下信息:

  1. 執行交易的綁定,他能夠在驗證端獨立的執行;

  2. 執行交易的元數據(交易中的代碼元數據);

  3. 部署交易的元數據(對應部署交易的代碼元數據組建).

注意sigma是被調用函數參數的一部分,或者是存儲在調用交易的代碼元數據內部的(被客戶端應用合理的格式化)。 
應用ACL包含在代碼元數據段中,在執行時一樣被傳遞給鏈碼。 
函數hello負責檢查sigma的確是經過TCertui在’M || txBinding’上的有效簽名。

4.4.2 讀訪問控制

這節描述fabric基礎設施如何支持應用在用戶層面執行它本身的讀訪問控制策略。和調用訪問控制的狀況同樣,第一部分描述了能夠被應用程序爲實現此目的利用的基礎設施的功能,接下來介紹應用使用這些工具的方法。

爲了說明這個問題,咱們使用和指點同樣的例子,即:

  1. C: 是隻包含一個函數的鏈碼,如,被成爲hello

  2. uA: 是C的部署者,也被成爲應用;

  3. ur: 是被受權調用C的用戶。用戶uA但願只有ur能夠讀取函數hello

來自基礎設施的支持. 
爲了讓uA在應用層安全的實現本身的讀取訪問控制咱們的基礎設施須要像下面描述的那樣來支持代碼的部署和調用交易格式。

SecRelease-RACappDepl title="Deployment transaction format supporting application-level read access control."

SecRelease-RACappInv title="Invocation transaction format supporting application-level read access control."

更具體的fabric層須要提供下面這些功能:

  1. 爲數據只能經過驗證(基礎設施)側解密,提供最低限度的加密功能;這意味着基礎設施在咱們的將來版本中應該更傾向於使用非對稱加密方案來加密交易。更具體的,在鏈中使用在上圖中標記爲 Kchain 的非對稱密鑰對。具體參看交易保密小節

  2. 客戶端-引用能夠請求基礎設施,基於客戶端側使用特定的公共加密密鑰或客戶端的長期解密密鑰來加密/解密信息。

  3. 交易格式提供應用存儲額外的交易元數據的能力,這些元數據能夠在後者請求後傳遞給客戶端應用。交易元數據相對於代碼元數據,在執行時是沒有加密或傳遞給鏈碼的。由於驗證器是不負責檢查他們的有效性的,因此把它們看成字節列表。

應用讀訪問控制. 
應用能夠請求並獲取訪問用戶ur的公共加密密鑰,咱們把它標記爲PKur。可選的,ur 可能提供 uA的一張證書給應用,使應用能夠利用,標記爲TCertur。如:爲了跟蹤用戶關於應用的鏈碼的交易。TCertur和PKur實在頻外渠道交換的。

部署時,應用 uA執行下面步驟:

  1. 使用底層基礎設施來加密C的信息,應用使用PKur來訪問ur。標記Cur爲獲得的密文。

  2. (可選) Cur能夠和TCertur鏈接

  3. 保密交易被構造爲」Tx-metadata」來傳遞

在調用的時候,在 ur節點上的客戶端-應用能夠獲取部署交易來獲得C的內容。 
這隻須要獲得相關聯的部署交易的 tx-metadata域,並觸發區塊鏈基礎設施客戶端爲Cur提供的解密函數。注意,爲ur正確加密C是應用的責任。 
此外,使用tx-metadata域能夠通常性的知足應用需求。即,調用者能夠利用調用交易的同一域來傳遞信息給應用的開發者。

Important Note: 
要注意的是驗證器在整個執行鏈碼過程當中不提供任何解密預測。 
對payload解密由基礎設施本身負責(以及它附近的代碼元數據域)。並提供他們給部署/執行的容器。

4.5 在線錢包服務

這一節描述了錢包服務的安全設計,這是一個用戶能夠註冊,移動他們的祕密材料到,辦理交易的節點。 
因爲錢包服務是一個用戶祕密材料全部權的服務,因此要杜絕沒有安全受權機制的惡意錢包服務能夠成功模擬客戶。 
所以,咱們強調的是,設計一種值得信賴的,只有在表明用戶的客戶端贊成的狀況下,錢包服務才能執行交易。 
這裏有兩種終端用戶註冊到在線錢包服務的狀況:

  1. 當用戶註冊到註冊機構並得到他/她的 <enrollID, enrollPWD>,可是沒有安裝客戶端來觸發完整的註冊過程。

  2. 用戶已經安裝客戶端並完成註冊階段

首先,用戶與在線錢包服務交互,容許他們進行身份驗證的錢包服務發佈證書。即用戶給定用戶名和密碼,其中用戶名在成員服務中識別用戶,標記爲AccPub,密碼是關聯的祕密,標記爲AccSec,這是由用戶和服務分享的。

爲了經過在線錢包服務註冊,用戶必須提供下面請求對象到錢包服務:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
AccountRequest /* account request of u \*/ { OBCSecCtx , /* credentials associated to network \*/ AccPub<sub>u</sub>, /* account identifier of u \*/ AccSecProof<sub>u</sub> /* proof of AccSec<sub>u</sub>\*/ }

OBCSecCtx指向用戶證書,它依賴於註冊過程當中的階段。能夠是用戶的註冊ID和密碼,<enrollID, enrollPWD> 或他的註冊證書和關聯的密鑰(ECertu, sku), 其中 sku是用戶簽名和解密密鑰的簡化標記。 
OBCSecCtx須要給在線錢包服務必要的信息來註冊用戶和發佈須要的TCerts。

對於後續的請求,用戶u須要提供給錢包服務的請求與蝦子面這個格式相似。

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
TransactionRequest /* account request of u \*/ { TxDetails, /* specifications for the new transaction \*/ AccPub<sub>u</sub>, /* account identifier of u \*/ AccSecProof<sub>u</sub> /* proof of AccSec<sub>u</sub> \*/ }

這裏,TxDetails指向在線服務表明用戶構造交易所須要的信息,如類型,和用戶指定的交易的內容。

AccSecProofu是對應請求中剩下的域的使用共享密鑰的HMAC。 
Nonce-based方法與咱們在fabric中同樣能夠防止重放攻擊。

TLS鏈接能夠用在每種服務器端認證的狀況,在網絡層對請求加密(保密,防止重放攻擊,等)

4.6 網絡安全(TLS)

TLS CA須要給(非驗證)peer,驗證器,和單獨的客戶端(或具備存儲私鑰的遊覽器)發放TLS證書的能力。最好,這些證書可使用以前的類型來區分。 
各個類型的CA(如TLS CA, ECA, TCA)的TLS證書有能夠經過中間CA(如,一個根CA的下屬CA)發放。這裏沒有特定流量分析的問題,任意給定的TLS鏈接均可以相互驗證,除了請求TLS CA的TLS證書的時候。

在當前的實現中,惟一的信任錨點是TLS CA的自簽名證書來適應與全部三個(共址)服務器(即TLS CA,TCA和ECA)進行通訊的單個端口限制。所以,與TLS CA的TLS握手來與TLS CA創建鏈接,所獲得的會話密鑰會傳遞給共址的TCA和ECA。所以,TCA和ECA的自簽名證書的有效性的信任繼承自對TLS CA的信任。在不提升TLS CA在其餘CA之上的實現中,信任錨點須要由TLS CA和其餘CA都認證的根CA替代。

4.7 當前版本的限制

這一小節列出了當前版本的fabric的限制。 
具體的關注點是客戶端操做和交易保密性設計,如4.7.1和4.7.2所述。

  • 客戶端註冊和交易的建立是由受信任不會模擬用戶的非驗證 peer 來徹底執行。參看4.7.1節獲得更多j信息。
  • 鏈碼只能被系統的成員實體訪問是保密性的最低要求,即,註冊到咱們成員服務的驗證器和用戶,其它的都不能訪問。後者包含能夠訪問到存儲區域維護的總帳,或者能夠看到在驗證器網絡上公佈的交易。第一個發佈版本在4.7.2小節中詳細介紹。
  • 代碼爲註冊CA(ECA)和交易CA(TCA)使用自簽名的證書
  • 防重放攻擊機制還不可用
  • 調用訪問控制能夠在應用層強制執行: 
    安全性的保證取決於應用對基礎設施工具的正確使用。這說明若是應用忽略了fabric提供的交易綁定綁定安全交易的處理可能在存在風險。

4.7.1 簡化客戶端

客戶端的註冊和交易的建立是由非驗證 peer 以在線錢包的角色所有執行的。 
集體的,終端用戶利用註冊證書

4.7.2 簡化交易保密

免責聲明: 當前版本的交易保密是最小的,這被用來做爲中間步驟來達到容許在將來版本中的細粒度(調用)的訪問控制的執行設計。

在當前的格式,交易的保密僅僅在鏈層面提供,即,保存在總帳中的交易內容對鏈的全部成員,如,驗證器和用戶,都是可讀的。於此同時,不是系統的成員的應用審計人員,能夠給予被動的觀察區塊鏈數據的手段。同時保證給予他們只是爲了與被審計應用程序相關的交易。狀態經過一種加密,同時不破壞底層共識網絡的正常運行的方式來知足這樣的審計要求

更具體的,當前使用對稱密鑰加密來提供交易保密性。 
在這種背景下,一個最主要的挑戰是特定於區塊鏈的設置,驗證器須要在區塊鏈的狀態上打成共識,即,除了交易自己,還包括我的合同或鏈碼的狀態更新。 
雖然對於非機密鏈碼這是微不足道的,對於機密鏈碼,須要設計狀態的加密機制,使得所得的密文語義安全,然而,若是明文狀態是相同的那麼他們就相等。

爲了克服這一難題,fabric利用了密鑰的層級,使用相同的密鑰進行加密來下降密文數。同時,因爲部分這些密鑰被用於IV的生成,這使得驗證方執行相同的事務時產生徹底相同的密文(這是必要的,以保持不可知到底層共識算法),並經過只披露給審計實體最相關的密鑰來提供控制審計的可能性。

方法描述: 
成員服務爲總帳生成對稱密鑰 (Kchain),這是在註冊到區塊鏈系統全部實體時發佈的,如,客戶端和驗證明體已經過鏈的成員服務發放證書。 
在註冊階段,用戶獲取(像以前同樣)一張註冊證書,爲用戶ui記做Certui,每一個驗證器vj獲取它的被記做Certvj的證書。

實體註冊將獲得提升,以下所示。除了註冊證書,用戶但願以匿名方式參與交易發放交易證書。 
爲了簡化咱們把用戶 ui 的交易證書記做 TCertui。 
交易證書包含簽名密鑰對的公共部分記做 (tpkui,tskui)。

爲了防止密碼分析和執行保密,下面的密鑰層級被用來生成和驗證保密的交易: 
爲了提交保密交易(Tx)到總帳,客戶端首先選擇一個nonce(N),這是須要提交區塊鏈的全部交易中是惟一的,並經過以Kchain做爲密鑰,nonce做爲輸入的HMAC函數生成一個交易對稱密鑰(KTx))KTx= HMAC(Kchain, N)。 
對於KTx,客戶端生成兩個AES密鑰: 
KTxCID看成HMAC(KTx, c1), KTxP 看成 HMAC(KTx, c2)) 分別加密鏈碼名稱或標識CID和代碼(或payload)P. 
c1, c2 是公共常量。nonce,加密的鏈碼ID(ECID)和加密的Payload(EP)被添加到交易Tx結構中,即最終簽名和認證的。 
下面的圖顯示瞭如何產生用於客戶端的事務的加密密鑰。這張圖中的剪頭表示HMAC的應用,源由密鑰鎖定和使用在箭頭中的數量做爲參數。部署/調用交易的密鑰鍵分別用d/I表示。

FirstRelease-clientSide

爲了驗證客戶端提交到區塊鏈的保密交易Tx,驗證明體首先經過和以前同樣的Kchain和Tx.Nonce再生成KTxCID和KTxP來解密ECID和EP。一旦鏈碼和Payload被恢復就能夠處理交易了。

FirstRelease-validatorSide

當V驗證一個機密事務,相應的鏈碼能夠訪問和修改鏈碼的狀態。V保持鏈碼的狀態加密。爲了作到這一點,V生成如上圖所示的對稱密鑰 
。讓iTX是一個以前由保密交易dTx部署的保密的交易調用一個函數(注意iTx能夠是dTx自己)在這種狀況下,例如,dTx具備初始化鏈碼狀態的設置函數。而後V像下面同樣生成兩個對稱密鑰KIV和Kstate

  1. 計算KdTx如,對應部署交易的交易密鑰和Nstate = HMAC(Kdtx ,hash(Ni))其中Ni是在調用交易中出現的nonce, hash是哈希函數
  2. 它設Kstate = HMAC(KdTx, c3 || Nstate),截斷用來加密底層密碼; c3 是一個常數
  3. 它設KIV = HMAC(KdTx, c4 || Nstate); c4 是一個常數

爲了加密狀態變量S,驗證器首先生成IV像 HMAC(KIV, crtstate)正確截斷,其中 crtstate是計數器值,並在每次一樣鏈碼調用時請求狀態更新時增長。當鏈碼執行終止是計數器丟棄。IV產生以後,V認證加密(即,GSM模式)S的值鏈接Nstate(實際上,Nstate只須要認證而不須要加密)。在所得的密文(CT), Nstate和IV被追加。爲了解密加密狀態CT|| Nstate’,驗證器首次生成對稱密鑰KdTX’ ,Kstate‘,而後解密CT。

IV的生成: 任何底層共識算法是不可知的,全部的驗證各方須要一種方法以產生相同的確切密文。爲了作到這一點,須要驗證使用相同的IV。重用具備相同的對稱密鑰相同的IV徹底打破了底層密碼的安全性。所以,前面所描述的方法制備。特別是,V首先經過計算HMAC(KdTX, c4 || Nstate )派生的IV生成密鑰KIV,其中c4是一個常數,併爲(dTx, 
iTx)保存計數器crtstate初始設置爲0。而後,每次必須生成一個新的密文,驗證器經過計算HMAC(KIV, crtstate)做爲輸出生成新的IV,而後爲crtstate增長1。

上述密鑰層次結構的另外一個好處是控制了審計的能力。 
例如,當發佈Kchain會提供對整個供應鏈的讀取權限,當只爲交易的(dTx,iTx)發佈Kstate訪問只授予由iTx更新的狀態,等等

下圖展現一個部署和調用交易在目前在代碼中的形式。

FirstRelease-deploy

FirstRelease-deploy

能夠注意到,部署和調用交易由兩部分組成:

  • 基本信息部分: 包含交易管理細節,如,把這個交易連接到的(被連接到的),交易的類型(被設置爲」deploymTx」或」invocTx」),保密策略實現的版本號,它的建立者標識(由TCert,Cert表達)和一個nonce(主要爲了防止重放攻擊)

  • 代碼信息部分: 包含在鏈碼的源代碼的信息。本質上是鏈碼標識符/名稱和源代碼的部署交易,而對調用鏈碼是是被調用函數名稱和它的參數。就像在兩張圖中展現的代碼信息那樣他們最終是使用鏈指定的對稱密鑰Kchain加密的。

5. 拜占庭共識

obcpbft包是PBFT共識協議[1]的實現,其中提供了驗證器之間的共識,雖然驗證器的閾做爲Byzantine,即,惡意的或不可預測的方式失敗。在默認的配置中,PBFT容忍t

5.1 概覽

obcpbft插件提供實現了CPI接口的模塊,他能夠配置運行PBFT仍是Sieve共識協議。模塊化來自於,在內部,obcpbft定義了innerCPI 接口(即, inner consensus programming interface),如今包含在 pbft-core.go中。

innerCPI接口定義的全部PBFT內部共識(這裏稱爲core PBFT並在pbft-core.go實現)和使用core PBFT的外部共識之間的相互做用。obcpbft包包含幾個core PBFT消費者實現

  • obc-classic.go, core PBFT周圍的shim,實現了innerCPI接口並調用CPI接口;
  • obc-batch.go, obc-classic的變種,爲PBFT添加批量能力;
  • obc-sieve.go, core PBFT消費者,實現Sieve共識協議和innerCPI接口, 調用CPI interface.

總之,除了調用發送消息給其餘 peer(innerCPI.broadcast 和 innerCPI.unicast),innerCPI接口定義了給消費者暴露的共識協議。 
這使用了用來表示信息的原子投遞的innerCPI.execute調用的一個經典的總序(原子)廣播 API[2]。經典的總序廣播在external validity checks [2]中詳細討論(innerCPI.verify)和一個功能類似的對不可靠的領導失敗的檢查Ω [3] (innerCPI.viewChange).

除了innerCPI, core PBFT 定義了core PBFT的方法。core PBFT最重要的方法是request有效地調用總序廣播原語[2]。在下文中,咱們首先概述core PBFT的方法和innerCPI接口的明細。而後,咱們簡要地描述,這將在更多的細節Sieve共識協議。

5.2 Core PBFT函數

下面的函數使用非遞歸鎖來控制併發,所以能夠從多個並行線程調用。然而,函數通常運行到完成,可能調用從CPI傳入的函數。必須當心,以防止活鎖。

5.2.1 newPbftCore

簽名:

   
   
   
   
  • 1
func newPbftCore(id uint64, config *viper.Viper, consumer innerCPI, ledger consensus.Ledger) *pbftCore

newPbftCore構造器使用指定的id來實例化一個新的PBFT箱子實例。config參數定義了PBFT網絡的操做參數:副本數量N,檢查點週期K,請求完成的超時時間,視圖改變週期。

configuration key type example value description
general.N integer 4 Number of replicas
general.K integer 10 Checkpoint period
general.timeout.request duration 2s Max delay between request reception and execution
general.timeout.viewchange duration 2s Max delay between view-change start and next request execution

接口中傳遞的consumerledger參數是一旦它們所有排好序後用來查詢應用狀態和調用應用請求的。參閱下面這些接口的相應部分。

6. 應用編程接口

fabric的主要接口是REST API。 REST API容許應用註冊用戶,查詢區塊鏈,併發布交易。 CLI爲了開發,一樣提供有效API的子集。CLI容許開發人員可以快速測試鏈碼或查詢交易狀態。

應用程序經過REST API與非驗證的 peer 節點,這將須要某種形式的認證,以確保實體有適當的權限進行交互。該應用程序是負責實現合適的身份驗證機制和 peer 節點隨後將使用客戶身份對發出消息簽名。

Reference architecture

fabric API 設計涵蓋的類別以下,雖然當前版本的其中一些實現不完整。[REST API(#62-REST的API)節將說明API當前支持。

  • 身份 - 註冊來得到或吊銷一張證書
  • Address - 交易的源或目的
  • Transaction - 總帳上的執行單元
  • Chaincode - 總帳上運行的程序
  • Blockchain - 總帳的內容
  • Network - 區塊鏈 peer 網絡的信息
  • Storage - 文件或文檔的外部存儲
  • Event Stream - 區塊鏈上訂閱/發佈事件

6.1 REST Service

REST服務能夠(經過配置)在驗證和非驗證 peer 被啓用,可是建議在生產環境中只啓用非驗證 peer 的REST服務。

   
   
   
   
  • 1
func StartOpenchainRESTServer(server *oc.ServerOpenchain, devops *oc.Devops)

這個函數讀取core.yaml``peer處理的配置文件中的rest.addressrest.address鍵定義了 peer 的HTTP REST服務默認監聽的地址和端口。

假定REST服務接收來已經認證的終端用戶的應用請求。

6.2 REST API

您能夠經過您所選擇的任何工具與REST API的工做。例如,curl命令行實用程序或一個基於瀏覽器的客戶端,如Firefox的REST客戶端或Chrome Postman。一樣,能夠經過[Swagger](http://swagger.io/)直接觸發REST請求。爲了得到REST API Swagger描述,點擊[這裏](https://github.com/hyperledger/fabric/blob/master/core/rest/rest_api.json)。目前可用的API總結於如下部分。

6.2.1 REST Endpoints

  • Block 
    • GET /chain/blocks/{block-id}
  • Blockchain 
    • GET /chain
  • Chaincode 
    • POST /chaincode
  • Network 
    • GET /network/peers
  • Registrar 
    • POST /registrar
    • GET /registrar/{enrollmentID}
    • DELETE /registrar/{enrollmentID}
    • GET /registrar/{enrollmentID}/ecert
    • GET /registrar/{enrollmentID}/tcert
  • Transactions 
    • GET /transactions/{UUID}

6.2.1.1 塊API

  • GET /chain/blocks/{block-id}

使用塊API來從區塊鏈中檢索各個塊的內容。返回的塊信息結構是在3.2.1.1節中定義

塊檢索請求:

   
   
   
   
  • 1
GET host:port/chain/blocks/173

塊檢索響應:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
{ "transactions": [ { "type": 3, "chaincodeID": "EgRteWNj", "payload": "Ch4IARIGEgRteWNjGhIKBmludm9rZRIBYRIBYhICMTA=", "uuid": "f5978e82-6d8c-47d1-adec-f18b794f570e", "timestamp": { "seconds": 1453758316, "nanos": 206716775 }, "cert": "MIIB/zCCAYWgAwIBAgIBATAKBggqhkjOPQQDAzApMQswCQYDVQQGEwJVUzEMMAoGA1UEChMDSUJNMQwwCgYDVQQDEwN0Y2EwHhcNMTYwMTI1MjE0MTE3WhcNMTYwNDI0MjE0MTE3WjArMQswCQYDVQQGEwJVUzEMMAoGA1UEChMDSUJNMQ4wDAYDVQQDEwVsdWthczB2MBAGByqGSM49AgEGBSuBBAAiA2IABC/BBkt8izf6Ew8UDd62EdWFikJhyCPY5VO9Wxq9JVzt3D6nubx2jO5JdfWt49q8V1Aythia50MZEDpmKhtM6z7LHOU1RxuxdjcYDOvkNJo6pX144U4N1J8/D3A+97qZpKN/MH0wDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwDQYDVR0OBAYEBAECAwQwDwYDVR0jBAgwBoAEAQIDBDA9BgYqAwQFBgcBAf8EMABNbPHZ0e/2EToi0H8mkouuUDwurgBYuUB+vZfeMewBre3wXG0irzMtfwHlfECRDDAKBggqhkjOPQQDAwNoADBlAjAoote5zYFv91lHzpbEwTfJL/+r+CG7oMVFUFuoSlvBSCObK2bDIbNkW4VQ+ZC9GTsCMQC5GCgy2oZdHw/x7XYzG2BiqmRkLRTiCS7vYCVJXLivU65P984HopxW0cEqeFM9co0=", "signature": "MGUCMCIJaCT3YRsjXt4TzwfmD9hg9pxYnV13kWgf7e1hAW5Nar//05kFtpVlq83X+YtcmAIxAK0IQlCgS6nqQzZEGCLd9r7cg1AkQOT/RgoWB8zcaVjh3bCmgYHsoPAPgMsi3TJktg==" } ], "stateHash": "7ftCvPeHIpsvSavxUoZM0u7o67MPU81ImOJIO7ZdMoH2mjnAaAAafYy9MIH3HjrWM1/Zla/Q6LsLzIjuYdYdlQ==", "previousBlockHash": "lT0InRg4Cvk4cKykWpCRKWDZ9YNYMzuHdUzsaeTeAcH3HdfriLEcTuxrFJ76W4jrWVvTBdI1etxuIV9AO6UF4Q==", "nonHashData": { "localLedgerCommitTimestamp": { "seconds": 1453758316, "nanos": 250834782 } } }

6.2.1.2 區塊鏈API

  • GET /chain

使用鏈API來檢索區塊鏈的當前狀態。返回區塊鏈信息消息被定義以下。

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
message BlockchainInfo { uint64 height = 1; bytes currentBlockHash = 2; bytes previousBlockHash = 3; }
  • height - 區塊鏈中塊的數量,包括創始區塊

  • currentBlockHash - 當前或最後區塊的哈希

  • previousBlockHash - 前一區塊的哈希

區塊鏈檢索請求:

   
   
   
   
  • 1
GET host:port/chain

區塊鏈檢索響應:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
{ "height": 174, "currentBlockHash": "lIfbDax2NZMU3rG3cDR11OGicPLp1yebIkia33Zte9AnfqvffK6tsHRyKwsw0hZFZkCGIa9wHVkOGyFTcFxM5w==", "previousBlockHash": "Vlz6Dv5OSy0OZpJvijrU1cmY2cNS5Ar3xX5DxAi/seaHHRPdssrljDeppDLzGx6ZVyayt8Ru6jO+E68IwMrXLQ==" }

6.2.1.3 鏈碼API

  • POST /chaincode

使用鏈碼API來部署,調用和查詢鏈碼 
部署請求須要客戶端提供path參數,執行文件系統中鏈碼的目錄。部署請求的響應要麼是包含成功的鏈碼部署確認消息要麼是包含失敗的緣由的錯誤。 
它還含有所生成的鏈碼的name域在消息中,這是在隨後的調用和查詢交易中使用的已部署鏈碼的惟一標識。

要部署鏈碼,須要提供ChaincodeSpec的payload,在3.1.2.2節中定義。

部署請求:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
POST host:port/chaincode { "jsonrpc": "2.0", "method": "deploy", "params": { "type": "GOLANG", "chaincodeID":{ "path":"github.com/hyperledger/fabic/examples/chaincode/go/chaincode_example02" }, "ctorMsg": { "function":"init", "args":["a", "1000", "b", "2000"] } }, "id": "1" }

部署響應:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
{ "jsonrpc": "2.0", "result": { "status": "OK", "message": "52b0d803fc395b5e34d8d4a7cd69fb6aa00099b8fabed83504ac1c5d61a425aca5b3ad3bf96643ea4fdaac132c417c37b00f88fa800de7ece387d008a76d3586" }, "id": 1 }

當啓用安全時,修改所需的payload包括傳遞的登陸用戶註冊ID的secureContext元素以下:

啓用安全的部署請求:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
POST host:port/chaincode { "jsonrpc": "2.0", "method": "deploy", "params": { "type": "GOLANG", "chaincodeID":{ "path":"github.com/hyperledger/fabic/examples/chaincode/go/chaincode_example02" }, "ctorMsg": { "function":"init", "args":["a", "1000", "b", "2000"] }, "secureContext": "lukas" }, "id": "1" }

該調用請求要求客戶端提供一個name參數,這是以前從部署交易響應獲得的。調用請求的響應要麼是包含成功執行的確認消息,要麼是包含失敗的緣由的錯誤。

要調用鏈碼,須要提供ChaincodeSpec的payload,在3.1.2.2節中定義

調用請求:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
POST host:port/chaincode { "jsonrpc": "2.0", "method": "invoke", "params": { "type": "GOLANG", "chaincodeID":{ "name":"52b0d803fc395b5e34d8d4a7cd69fb6aa00099b8fabed83504ac1c5d61a425aca5b3ad3bf96643ea4fdaac132c417c37b00f88fa800de7ece387d008a76d3586" }, "ctorMsg": { "function":"invoke", "args":["a", "b", "100"] } }, "id": "3" }

調用響應:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
{ "jsonrpc": "2.0", "result": { "status": "OK", "message": "5a4540e5-902b-422d-a6ab-e70ab36a2e6d" }, "id": 3 }

當啓用安全時,修改所需的payload包括傳遞的登陸用戶註冊ID的secureContext元素以下:

啓用安全的調用請求:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
{ "jsonrpc": "2.0", "method": "invoke", "params": { "type": "GOLANG", "chaincodeID":{ "name":"52b0d803fc395b5e34d8d4a7cd69fb6aa00099b8fabed83504ac1c5d61a425aca5b3ad3bf96643ea4fdaac132c417c37b00f88fa800de7ece387d008a76d3586" }, "ctorMsg": { "function":"invoke", "args":["a", "b", "100"] }, "secureContext": "lukas" }, "id": "3" }

查詢請求須要在客戶端提供一個name參數,這是以前在部署交易響應中獲得了。查詢請求的響應取決於鏈碼的實現。響應要麼是包含成功執行的確認消息,要麼是包含失敗的緣由的錯誤。在成功執行的狀況下,響應將包含鏈碼請求的狀態變量的值

要查詢鏈碼,須要提供ChaincodeSpec的payload,在3.1.2.2節中定義。

查詢請求:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
POST host:port/chaincode/ { "jsonrpc": "2.0", "method": "query", "params": { "type": "GOLANG", "chaincodeID":{ "name":"52b0d803fc395b5e34d8d4a7cd69fb6aa00099b8fabed83504ac1c5d61a425aca5b3ad3bf96643ea4fdaac132c417c37b00f88fa800de7ece387d008a76d3586" }, "ctorMsg": { "function":"query", "args":["a"] } }, "id": "5" }

查詢響應:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
{ "jsonrpc": "2.0", "result": { "status": "OK", "message": "-400" }, "id": 5 }

當啓用安全時,修改所需的payload包括傳遞的登陸用戶註冊ID的secureContext元素以下:

啓用安全的查詢請求:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
{ "jsonrpc": "2.0", "method": "query", "params": { "type": "GOLANG", "chaincodeID":{ "name":"52b0d803fc395b5e34d8d4a7cd69fb6aa00099b8fabed83504ac1c5d61a425aca5b3ad3bf96643ea4fdaac132c417c37b00f88fa800de7ece387d008a76d3586" }, "ctorMsg": { "function":"query", "args":["a"] }, "secureContext": "lukas" }, "id": "5" }

6.2.1.4 網絡API

使用網絡API來獲取組成區塊鏈 fabric 的 peer 節點的網絡信息

/network/peers 端點返回的目標 peer 節點的全部現有的網絡鏈接的列表。該列表包括驗證和非驗證 peer。peer 的列表被返回類型PeersMessage是包含PeerEndpoint的數組,在第[3.1.1](#311-discovery-messages發現的消息)定義。

   
   
   
   
  • 1
  • 2
  • 3
message PeersMessage { repeated PeerEndpoint peers = 1; }

網絡請求:

   
   
   
   
  • 1
GET host:port/network/peers

網絡響應:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
{ "peers": [ { "ID": { "name": "vp1" }, "address": "172.17.0.4:30303", "type": 1, "pkiID": "rUA+vX2jVCXev6JsXDNgNBMX03IV9mHRPWo6h6SI0KLMypBJLd+JoGGlqFgi+eq/" }, { "ID": { "name": "vp3" }, "address": "172.17.0.5:30303", "type": 1, "pkiID": "OBduaZJ72gmM+B9wp3aErQlofE0ulQfXfTHh377ruJjOpsUn0MyvsJELUTHpAbHI" }, { "ID": { "name": "vp2" }, "address": "172.17.0.6:30303", "type": 1, "pkiID": "GhtP0Y+o/XVmRNXGF6pcm9KLNTfCZp+XahTBqVRmaIumJZnBpom4ACayVbg4Q/Eb" } ] }

6.2.1.5 註冊API (成員服務)

  • POST /registrar
  • GET /registrar/{enrollmentID}
  • DELETE /registrar/{enrollmentID}
  • GET /registrar/{enrollmentID}/ecert
  • GET /registrar/{enrollmentID}/tcert

使用註冊API來管理的證書頒發機構(CA)的最終用戶註冊。這些API端點用於註冊與CA用戶,肯定指定用戶是否已註冊,並從本地存儲中刪除任何目標用戶的登陸令牌,防止他們執行任何進一步的交易。註冊API也用於從系統中檢索用戶註冊和交易證書。

/registrar端點使用與CA註冊用戶所需的祕密payload定義以下。註冊請求的響應能夠是一個成功的註冊的確認或包含失敗的緣由的錯誤。

   
   
   
   
  • 1
  • 2
  • 3
  • 4
message Secret { string enrollId = 1; string enrollSecret = 2; }
  • enrollId - 在證書頒發機構的註冊ID
  • enrollSecret - 在證書頒發機構的密碼

註冊請求:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
POST host:port/registrar { "enrollId": "lukas", "enrollSecret": "NPKYL39uKbkj" }

註冊響應:

   
   
   
   
  • 1
  • 2
  • 3
{ "OK": "Login successful for user 'lukas'." }

GET /registrar/{enrollmentID}端點用於確認一個給定的用戶是否與CA註冊若是是,確認將被反悔。不然,將致使受權錯誤。

註冊驗證請求:

   
   
   
   
  • 1
GET host:port/registrar/jim

註冊驗證返回:

   
   
   
   
  • 1
  • 2
  • 3
{ "OK": "User jim is already logged in." }

註冊驗證請求:

   
   
   
   
  • 1
GET host:port/registrar/alex

註冊驗證返回:

   
   
   
   
  • 1
  • 2
  • 3
{ "Error": "User alex must log in." }

DELETE /registrar/{enrollmentID} 端點用於刪除一個目標用戶的登陸令牌。若是登陸令牌成功刪除,確認將被反悔。不然,將致使受權錯誤。此端點不須要payload。

刪除註冊請求:

   
   
   
   
  • 1
DELETE host:port/registrar/lukas

刪除註冊返回:

   
   
   
   
  • 1
  • 2
  • 3
{ "OK": "Deleted login token and directory for user lukas." }

GET /registrar/{enrollmentID}/ecert 
端點用於檢索從本地存儲給定用戶的登記證書。若是目標用戶已與CA註冊,響應將包括註冊證書的URL-encoded版本。若是目標用戶還沒有註冊,將返回一個錯誤。若是客戶但願使用檢索後返回的註冊證書,請記住,它必須是URL-decoded。

註冊證書檢索請求:

   
   
   
   
  • 1
GET host:port/registrar/jim/ecert

註冊證書檢索響應:

   
   
   
   
  • 1
  • 2
  • 3
{ "OK": "-----BEGIN+CERTIFICATE-----%0AMIIBzTCCAVSgAwIBAgIBATAKBggqhkjOPQQDAzApMQswCQYDVQQGEwJVUzEMMAoG%0AA1UEChMDSUJNMQwwCgYDVQQDEwNPQkMwHhcNMTYwMTIxMDYzNjEwWhcNMTYwNDIw%0AMDYzNjEwWjApMQswCQYDVQQGEwJVUzEMMAoGA1UEChMDSUJNMQwwCgYDVQQDEwNP%0AQkMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARSLgjGD0omuJKYrJF5ClyYb3sGEGTU%0AH1mombSAOJ6GAOKEULt4L919sbSSChs0AEvTX7UDf4KNaKTrKrqo4khCoboMg1VS%0AXVTTPrJ%2BOxSJTXFZCohVgbhWh6ZZX2tfb7%2BjUDBOMA4GA1UdDwEB%2FwQEAwIHgDAM%0ABgNVHRMBAf8EAjAAMA0GA1UdDgQGBAQBAgMEMA8GA1UdIwQIMAaABAECAwQwDgYG%0AUQMEBQYHAQH%2FBAE0MAoGCCqGSM49BAMDA2cAMGQCMGz2RR0NsJOhxbo0CeVts2C5%0A%2BsAkKQ7v1Llbg78A1pyC5uBmoBvSnv5Dd0w2yOmj7QIwY%2Bn5pkLiwisxWurkHfiD%0AxizmN6vWQ8uhTd3PTdJiEEckjHKiq9pwD%2FGMt%2BWjP7zF%0A-----END+CERTIFICATE-----%0A" }

/registrar/{enrollmentID}/tcert端點檢索已與證書機關登記給定用戶的交易證書。若是用戶已註冊,確認消息將包含URL-encoded交易證書的列表被返回。不然,將會致使一個錯誤。交易證書的所需數量由可選的’count’查詢參數指定。返回交易證書的默認數量爲1;500是能夠與單個請求中檢索證書的最大數量。若是客戶端但願使用取回後的交易證書,請記住,他們必須是URL-decoded。

交易證書檢索請求:

   
   
   
   
  • 1
GET host:port/registrar/jim/tcert

交易證書檢索響應:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
{ "OK": [ "-----BEGIN+CERTIFICATE-----%0AMIIBwDCCAWagAwIBAgIBATAKBggqhkjOPQQDAzApMQswCQYDVQQGEwJVUzEMMAoG%0AA1UEChMDSUJNMQwwCgYDVQQDEwN0Y2EwHhcNMTYwMzExMjEwMTI2WhcNMTYwNjA5%0AMjEwMTI2WjApMQswCQYDVQQGEwJVUzEMMAoGA1UEChMDSUJNMQwwCgYDVQQDEwNq%0AaW0wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQfwJORRED9RAsmSl%2FEowq1STBb%0A%2FoFteymZ96RUr%2BsKmF9PNrrUNvFZFhvukxZZjqhEcGiQqFyRf%2FBnVN%2BbtRzMo38w%0AfTAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH%2FBAIwADANBgNVHQ4EBgQEAQIDBDAP%0ABgNVHSMECDAGgAQBAgMEMD0GBioDBAUGBwEB%2FwQwSRWQFmErr0SmQO9AFP4GJYzQ%0APQMmcsCjKiJf%2Bw1df%2FLnXunCsCUlf%2FalIUaeSrT7MAoGCCqGSM49BAMDA0gAMEUC%0AIQC%2FnE71FBJd0hwNTLXWmlCJff4Yi0J%2BnDi%2BYnujp%2Fn9nQIgYWg0m0QFzddyJ0%2FF%0AKzIZEJlKgZTt8ZTlGg3BBrgl7qY%3D%0A-----END+CERTIFICATE-----%0A" ] }

交易證書檢索請求:

   
   
   
   
  • 1
GET host:port/registrar/jim/tcert?count=5

交易證書檢索響應:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
{ "OK": [ "-----BEGIN+CERTIFICATE-----%0AMIIBwDCCAWagAwIBAgIBATAKBggqhkjOPQQDAzApMQswCQYDVQQGEwJVUzEMMAoG%0AA1UEChMDSUJNMQwwCgYDVQQDEwN0Y2EwHhcNMTYwMzExMjEwMTI2WhcNMTYwNjA5%0AMjEwMTI2WjApMQswCQYDVQQGEwJVUzEMMAoGA1UEChMDSUJNMQwwCgYDVQQDEwNq%0AaW0wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARwJxVezgDcTAgj2LtTKVm65qft%0AhRTYnIOQhhOx%2B%2B2NRu5r3Kn%2FXTf1php3NXOFY8ZQbY%2FQbFAwn%2FB0O68wlHiro38w%0AfTAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH%2FBAIwADANBgNVHQ4EBgQEAQIDBDAP%0ABgNVHSMECDAGgAQBAgMEMD0GBioDBAUGBwEB%2FwQwRVPMSKVcHsk4aGHxBWc8PGKj%0AqtTVTtuXnN45BynIx6lP6urpqkSuILgB1YOdRNefMAoGCCqGSM49BAMDA0gAMEUC%0AIAIjESYDp%2FXePKANGpsY3Tu%2F4A2IfeczbC3uB%2BpziltWAiEA6Stp%2FX4DmbJGgZe8%0APMNBgRKeoU6UbgTmed0ZEALLZP8%3D%0A-----END+CERTIFICATE-----%0A", "-----BEGIN+CERTIFICATE-----%0AMIIBwDCCAWagAwIBAgIBATAKBggqhkjOPQQDAzApMQswCQYDVQQGEwJVUzEMMAoG%0AA1UEChMDSUJNMQwwCgYDVQQDEwN0Y2EwHhcNMTYwMzExMjEwMTI2WhcNMTYwNjA5%0AMjEwMTI2WjApMQswCQYDVQQGEwJVUzEMMAoGA1UEChMDSUJNMQwwCgYDVQQDEwNq%0AaW0wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARwJxVezgDcTAgj2LtTKVm65qft%0AhRTYnIOQhhOx%2B%2B2NRu5r3Kn%2FXTf1php3NXOFY8ZQbY%2FQbFAwn%2FB0O68wlHiro38w%0AfTAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH%2FBAIwADANBgNVHQ4EBgQEAQIDBDAP%0ABgNVHSMECDAGgAQBAgMEMD0GBioDBAUGBwEB%2FwQwRVPMSKVcHsk4aGHxBWc8PGKj%0AqtTVTtuXnN45BynIx6lP6urpqkSuILgB1YOdRNefMAoGCCqGSM49BAMDA0gAMEUC%0AIAIjESYDp%2FXePKANGpsY3Tu%2F4A2IfeczbC3uB%2BpziltWAiEA6Stp%2FX4DmbJGgZe8%0APMNBgRKeoU6UbgTmed0ZEALLZP8%3D%0A-----END+CERTIFICATE-----%0A", "-----BEGIN+CERTIFICATE-----%0AMIIBwDCCAWagAwIBAgIBATAKBggqhkjOPQQDAzApMQswCQYDVQQGEwJVUzEMMAoG%0AA1UEChMDSUJNMQwwCgYDVQQDEwN0Y2EwHhcNMTYwMzExMjEwMTI2WhcNMTYwNjA5%0AMjEwMTI2WjApMQswCQYDVQQGEwJVUzEMMAoGA1UEChMDSUJNMQwwCgYDVQQDEwNq%0AaW0wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARwJxVezgDcTAgj2LtTKVm65qft%0AhRTYnIOQhhOx%2B%2B2NRu5r3Kn%2FXTf1php3NXOFY8ZQbY%2FQbFAwn%2FB0O68wlHiro38w%0AfTAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH%2FBAIwADANBgNVHQ4EBgQEAQIDBDAP%0ABgNVHSMECDAGgAQBAgMEMD0GBioDBAUGBwEB%2FwQwRVPMSKVcHsk4aGHxBWc8PGKj%0AqtTVTtuXnN45BynIx6lP6urpqkSuILgB1YOdRNefMAoGCCqGSM49BAMDA0gAMEUC%0AIAIjESYDp%2FXePKANGpsY3Tu%2F4A2IfeczbC3uB%2BpziltWAiEA6Stp%2FX4DmbJGgZe8%0APMNBgRKeoU6UbgTmed0ZEALLZP8%3D%0A-----END+CERTIFICATE-----%0A", "-----BEGIN+CERTIFICATE-----%0AMIIBwDCCAWagAwIBAgIBATAKBggqhkjOPQQDAzApMQswCQYDVQQGEwJVUzEMMAoG%0AA1UEChMDSUJNMQwwCgYDVQQDEwN0Y2EwHhcNMTYwMzExMjEwMTI2WhcNMTYwNjA5%0AMjEwMTI2WjApMQswCQYDVQQGEwJVUzEMMAoGA1UEChMDSUJNMQwwCgYDVQQDEwNq%0AaW0wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARwJxVezgDcTAgj2LtTKVm65qft%0AhRTYnIOQhhOx%2B%2B2NRu5r3Kn%2FXTf1php3NXOFY8ZQbY%2FQbFAwn%2FB0O68wlHiro38w%0AfTAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH%2FBAIwADANBgNVHQ4EBgQEAQIDBDAP%0ABgNVHSMECDAGgAQBAgMEMD0GBioDBAUGBwEB%2FwQwRVPMSKVcHsk4aGHxBWc8PGKj%0AqtTVTtuXnN45BynIx6lP6urpqkSuILgB1YOdRNefMAoGCCqGSM49BAMDA0gAMEUC%0AIAIjESYDp%2FXePKANGpsY3Tu%2F4A2IfeczbC3uB%2BpziltWAiEA6Stp%2FX4DmbJGgZe8%0APMNBgRKeoU6UbgTmed0ZEALLZP8%3D%0A-----END+CERTIFICATE-----%0A", "-----BEGIN+CERTIFICATE-----%0AMIIBwDCCAWagAwIBAgIBATAKBggqhkjOPQQDAzApMQswCQYDVQQGEwJVUzEMMAoG%0AA1UEChMDSUJNMQwwCgYDVQQDEwN0Y2EwHhcNMTYwMzExMjEwMTI2WhcNMTYwNjA5%0AMjEwMTI2WjApMQswCQYDVQQGEwJVUzEMMAoGA1UEChMDSUJNMQwwCgYDVQQDEwNq%0AaW0wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARwJxVezgDcTAgj2LtTKVm65qft%0AhRTYnIOQhhOx%2B%2B2NRu5r3Kn%2FXTf1php3NXOFY8ZQbY%2FQbFAwn%2FB0O68wlHiro38w%0AfTAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH%2FBAIwADANBgNVHQ4EBgQEAQIDBDAP%0ABgNVHSMECDAGgAQBAgMEMD0GBioDBAUGBwEB%2FwQwRVPMSKVcHsk4aGHxBWc8PGKj%0AqtTVTtuXnN45BynIx6lP6urpqkSuILgB1YOdRNefMAoGCCqGSM49BAMDA0gAMEUC%0AIAIjESYDp%2FXePKANGpsY3Tu%2F4A2IfeczbC3uB%2BpziltWAiEA6Stp%2FX4DmbJGgZe8%0APMNBgRKeoU6UbgTmed0ZEALLZP8%3D%0A-----END+CERTIFICATE-----%0A" ] }

6.2.1.6 交易API

  • GET /transactions/{UUID}

使用交易API來從區塊鏈中檢索匹配UUID的單個交易。返回的交易消息在3.1.2.1小節定義

交易檢索請求:

   
   
   
   
  • 1
GET host:port/transactions/f5978e82-6d8c-47d1-adec-f18b794f570e

交易檢索響應:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
{ "type": 3, "chaincodeID": "EgRteWNj", "payload": "Ch4IARIGEgRteWNjGhIKBmludm9rZRIBYRIBYhICMTA=", "uuid": "f5978e82-6d8c-47d1-adec-f18b794f570e", "timestamp": { "seconds": 1453758316, "nanos": 206716775 }, "cert": "MIIB/zCCAYWgAwIBAgIBATAKBggqhkjOPQQDAzApMQswCQYDVQQGEwJVUzEMMAoGA1UEChMDSUJNMQwwCgYDVQQDEwN0Y2EwHhcNMTYwMTI1MjE0MTE3WhcNMTYwNDI0MjE0MTE3WjArMQswCQYDVQQGEwJVUzEMMAoGA1UEChMDSUJNMQ4wDAYDVQQDEwVsdWthczB2MBAGByqGSM49AgEGBSuBBAAiA2IABC/BBkt8izf6Ew8UDd62EdWFikJhyCPY5VO9Wxq9JVzt3D6nubx2jO5JdfWt49q8V1Aythia50MZEDpmKhtM6z7LHOU1RxuxdjcYDOvkNJo6pX144U4N1J8/D3A+97qZpKN/MH0wDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwDQYDVR0OBAYEBAECAwQwDwYDVR0jBAgwBoAEAQIDBDA9BgYqAwQFBgcBAf8EMABNbPHZ0e/2EToi0H8mkouuUDwurgBYuUB+vZfeMewBre3wXG0irzMtfwHlfECRDDAKBggqhkjOPQQDAwNoADBlAjAoote5zYFv91lHzpbEwTfJL/+r+CG7oMVFUFuoSlvBSCObK2bDIbNkW4VQ+ZC9GTsCMQC5GCgy2oZdHw/x7XYzG2BiqmRkLRTiCS7vYCVJXLivU65P984HopxW0cEqeFM9co0=", "signature": "MGUCMCIJaCT3YRsjXt4TzwfmD9hg9pxYnV13kWgf7e1hAW5Nar//05kFtpVlq83X+YtcmAIxAK0IQlCgS6nqQzZEGCLd9r7cg1AkQOT/RgoWB8zcaVjh3bCmgYHsoPAPgMsi3TJktg==" }

6.3 CLI

CLI包括可用的API的一個子集,使開發人員可以快速測試和調試鏈碼或查詢交易狀態。CLI由Golang實現和可在多個操做系統上操做。當前可用的CLI命令概括在下面的部分:

6.3.1 CLI命令

To see what CLI commands are currently available in the implementation, execute the following:

要查看當前可用的CLI命令,執行以下命令

   
   
   
   
  • 1
  • 2
  • 3
cd $GOPATH/src/github.com/hyperledger/fabic/peer ./peer

你能夠得到和下面相似的響應:

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
Usage: peer [command] Available Commands: peer Run the peer. status Status of the peer. stop Stop the peer. login Login user on CLI. vm VM functionality on the fabric. chaincode chaincode specific commands. help Help about any command Flags: -h, --help[=false]: help Use "peer [command] --help" for more information about a command.

Some of the available command line arguments for the peer command are listed below:

  • -c - 構造函數: 用來爲部署觸發初始化鏈碼狀態的函數

  • -l - 語言: 指定鏈碼的實現語言,目前只支持Golang

  • -n - 名字: 部署交易返回的鏈碼的標識。在後續的調用和查詢交易中必須使用

  • -p - 路徑: 鏈碼在本地文件系統中的標識。在部署交易時必須提供。

  • -u - 用戶名: 調用交易的登入的用戶的註冊ID

上述全部命令並不是徹底在當前版本中實現。以下所述全面支持的命令是有助於鏈碼的開發和調試的。

全部 peer 節點的設置都被列在core.yaml這個peer處理的配置文件中,可能經過命令行的環境變量而被修改。如,設置peer.id或 peer.ddressAutoDetect,只須要傳遞CORE_PEER_ID=vp1CORE_PEER_ADDRESSAUTODETECT=true給命令行。

6.3.1.1 peer

peerCLI命令在開發和生產環境中都會執行 peer 處理。開發模式會在本地運行單個 peer 節點和本地的鏈碼部署。這使得在鏈碼開修改和調試代碼,不須要啓動一個完整的網絡。在開發模式啓動 peer 的一個例子:

   
   
   
   
  • 1
./peer peer --peer-chaincodedev

在生產環境中啓動peer進程,像下面同樣修改上面的命令:

   
   
   
   
  • 1
./peer peer

6.3.1.2 登陸

登陸的CLI命令會登入一個已經在CA註冊的用戶。要經過CLI登陸,發出如下命令,其中username是註冊用戶的註冊ID。

   
   
   
   
  • 1
./peer login <username>

下面的例子演示了用戶jim登陸過程。

   
   
   
   
  • 1
./peer login jim

該命令會提示輸入密碼,密碼必須爲此用戶使用證書頒發機構註冊登記的密碼相匹配。若是輸入的密碼不正確的密碼匹配,將致使一個錯誤。

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
22:21:31.246 [main] login -> INFO 001 CLI client login... 22:21:31.247 [main] login -> INFO 002 Local data store for client loginToken: /var/hyperledger/production/client/ Enter password for user 'jim': ************ 22:21:40.183 [main] login -> INFO 003 Logging in user 'jim' on CLI interface... 22:21:40.623 [main] login -> INFO 004 Storing login token for user 'jim'. 22:21:40.624 [main] login -> INFO 005 Login successful for user 'jim'.

您也能夠與-p參數來提供用戶的密碼。下面是一個例子。

   
   
   
   
  • 1
./peer login jim -p 123456

6.3.1.3 鏈碼部署

deployCLI命令爲鏈碼和接下來的部署包到驗證 peer 建立 docker 鏡像。以下面的例子。

   
   
   
   
  • 1
./peer chaincode deploy -p github.com/hyperledger/fabric/example/chaincode/go/chaincode_example02 -c '{"Function":"init", "Args": ["a","100", "b", "200"]}'

啓用安全性時,命令必須修改來經過-u參數傳遞用戶登陸的註冊ID。下面是一個例子

   
   
   
   
  • 1
./peer chaincode deploy -u jim -p github.com/hyperledger/fabric/example/chaincode/go/chaincode_example02 -c '{"Function":"init", "Args": ["a","100", "b", "200"]}'

6.3.1.4 鏈碼調用

invokeCLI命令執行目標來代碼中的指定函數。以下:

   
   
   
   
  • 1
./peer chaincode invoke -n <name_value_returned_from_deploy_command> -c '{"Function": "invoke", "Args": ["a", "b", "10"]}'

啓用安全性時,命令必須修改來經過-u參數傳遞用戶登陸的註冊ID。下面是一個例子

   
   
   
   
  • 1
./peer chaincode invoke -u jim -n <name_value_returned_from_deploy_command> -c '{"Function": "invoke", "Args": ["a", "b", "10"]}'

6.3.1.5 鏈碼查詢

queryCLI命令在目標鏈碼上觸發指定的查詢。返回的響應取決於鏈碼實現。下面是一個例子。

   
   
   
   
  • 1
./peer chaincode query -l golang -n <name_value_returned_from_deploy_command> -c '{"Function": "query", "Args": ["a"]}'

啓用安全性時,命令必須修改來經過-u參數傳遞用戶登陸的註冊ID。下面是一個例子

   
   
   
   
  • 1
./peer chaincode query -u jim -l golang -n <name_value_returned_from_deploy_command> -c '{"Function": "query", "Args": ["a"]}'

7. 應用模型

7.1 應用的組成

一個遵循MVC-B架構的應用– Model, View, Control, BlockChain.

  • VIEW LOGIC – 與控制邏輯集成的移動或WEB 用戶界面。
  • CONTROL LOGIC – 協調用戶界面、數據模型和交易與鏈碼的API
  • DATA MODEL – 應用數據模型– 管理包括文檔和大文件這樣的非鏈(off-chain)數據
  • BLOCKCHAIN LOGIC – 區塊鏈邏輯是控制邏輯和數據模型在區塊鏈領域的擴展,鏈碼(chaincode)增強了控制邏輯,區塊鏈上的交易增強了數據模型。

例如,使用 Node.js 的一個 Bluemix PaaS 的應用程序可能有一個 Web 前端用戶界面或與 Cloudant 數據服務後端模型中的原生移動應用。控制邏輯能夠被 1 或多個鏈碼交互以處理對區塊鏈交易。

7.2 應用樣例

8. 將來發展方向

8.1 企業集成

8.2 性能與可擴展性

8.3 附加的共識插件

8.4 附加的語言

9. References

  • [1] Miguel Castro, Barbara Liskov: Practical Byzantine fault tolerance and proactive recovery. ACM Trans. Comput. Syst. 20(4): 398-461 (2002)

  • [2] Christian Cachin, Rachid Guerraoui, Luís E. T. Rodrigues: Introduction to Reliable and Secure Distributed Programming (2. ed.). Springer 2011, ISBN 978-3-642-15259-7, pp. I-XIX, 1-367

  • [3] Tushar Deepak Chandra, Vassos Hadzilacos, Sam Toueg: The Weakest Failure Detector for Solving Consensus. J. ACM 43(4): 685-722 (1996)

  • [4] Cynthia Dwork, Nancy A. Lynch, Larry J. Stockmeyer: Consensus in the presence of partial synchrony. J. ACM 35(2): 288-323 (1988)

  • [5] Manos Kapritsos, Yang Wang, Vivien Quéma, Allen Clement, Lorenzo Alvisi, Mike Dahlin: All about Eve: Execute-Verify Replication for Multi-Core Servers. OSDI 2012: 237-250

  • [6] Pierre-Louis Aublin, Rachid Guerraoui, Nikola Knezevic, Vivien Quéma, Marko Vukolic: The Next 700 BFT Protocols. ACM Trans. Comput. Syst. 32(4): 12:1-12:45 (2015)

  • [7] Christian Cachin, Simon Schubert, Marko Vukolić: Non-determinism in Byzantine Fault-Tolerant Replication


轉自http://blog.csdn.net/zxzxzxzx2121/article/details/53034151

相關文章
相關標籤/搜索