HyperLedger Fabric協議規範

協議規範

前言

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

目標讀者

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

  • 想實現符合這份規範的區塊鏈的廠商
  • 想擴展 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.

評審

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

致謝

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


目錄

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. 介紹

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

1.1 什麼是 fabric?

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

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

總賬可以按照規定規則來審計全部或部分總賬分錄。在與參與者合作中,審計員可以通過基於時間的證書來獲得總賬的查看,連接交易來提供實際的資產操作。

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

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中定義了多種子類型。

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是一個包含:

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
  • addressip: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對象:

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語言來編寫鏈碼的實現。將來可能會添加新的語言。

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對象。

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對象。

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_BLOCKS, SYNC_STATE_SNAPSHOTSYNC_STATE_DELTAS

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

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

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

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

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對象

message SyncStateSnapshotRequest {
  uint64 correlationId = 1;
}

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

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實例

message SyncStateDeltasRequest {
    SyncBlockRange range = 1;
}

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

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 區塊

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

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 都具有相同值的塊元數據。他們是建議值。

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接口的虛擬機

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消息協議。

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接口

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

Init, InvokeQuery函數使用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消息來保存鍵值對。

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來獲取一個範圍內的值。

message RangeQueryState {
    string startKey = 1;
    string endKey = 2;
}

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

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消息來獲取下一集合。

message RangeQueryStateNext {
    string ID = 1;
}

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

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接口獲得詳細細節)。

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

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

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

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

3.4.1 Consenter 接口

定義:

type Consenter interface {
    RecvMsg(msg *pb.Message) error
}

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

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

3.4.2 CPI接口

定義:

type CPI interface {
    Inquirer
    Communicator
    SecurityUtils
    LedgerStack
}

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

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

3.4.3 Inquirer接口

定義:

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消息,當前定義爲(注意這個定義很可能會被修改):

message PeerID {
    string name = 1;
}

3.4.4 Communicator接口

定義:

type Communicator interface {
    Broadcast(msg *pb.Message) error
    Unicast(msg *pb.Message, receiverHandle *pb.PeerID) error
}

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

3.4.5 SecurityUtils接口

定義:

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 接口

定義:

type LedgerStack interface {
    Executor
    Ledger
    RemoteLedgers
}

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

3.4.7 Executor 接口

定義:

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 開始批量交易

BeginTxBatch(id interface{}) error

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

3.4.7.2 執行交易

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

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

3.4.7.3 提交與回滾交易

RollbackTxBatch(id interface{}) error

這個調用忽略了批量執行。這會廢棄掉對當前狀態的操作,並把總賬狀態迴歸到之前的狀態。批量是從BeginBatchTx開始的,如果需要開始一個新的就需要在執行任意交易之前重新創建一個。

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

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

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

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

3.4.8 Ledger 接口

定義:

type Ledger interface {
    ReadOnlyLedger
    UtilLedger
    WritableLedger
}

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

3.4.8.1 ReadOnlyLedger 接口

定義:

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

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

GetBlockchainSize() (uint64, error)

這個函數返回區塊鏈總賬的長度。一般來說,這個函數永遠不會失敗,在這種不太可能發生情況下,錯誤被傳遞給調用者,由它確定是否需要恢復。具有最大區塊值的區塊的值爲GetBlockchainSize()-1

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

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

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

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

這個盜用返回總賬的當前狀態的哈希。一般來說,這個函數永遠不會失敗,在這種不太可能發生情況下,錯誤被傳遞給調用者,由它確定是否需要恢復。

3.4.8.2 UtilLedger 接口

定義:

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

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

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

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

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

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

3.4.8.3 WritableLedger 接口

定義:

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

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

-

CommitStateDelta(id interface{}) error

這個方法提交在`ApplyStateDelta`中應用的狀態變化。這通常是在調用者調用`ApplyStateDelta`後通過校驗由`GetCurrentStateHash()`獲得的狀態哈希之後調用的。這個函數接受與傳遞給`ApplyStateDelta`一樣的`id`。

-

RollbackStateDelta(id interface{}) error

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

-

EmptyState() error

這個函數將會刪除整個當前狀態,得到原始的空狀態。這通常是通過變化量加載整個新的狀態時調用的。這一樣只對狀態轉移API有用。

3.4.9 RemoteLedgers 接口

定義:

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接口一樣,這不是給正常的操作使用,而是爲追趕,錯誤恢復等操作而設計的。這個接口中的所有函數調用這都有責任來處理超時。這個接口包含下面這些函數:

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

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

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

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

    -

    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

簽名:

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

定義:

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

簽名:

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

定義:

type Helper struct {
    coordinator peer.MessageHandlerCoordinator
}

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

3.4.11.5 helper.NewHelper

簽名:

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 接口:

type MessageHandler interface {
    RemoteLedger
    HandleMessage(msg *pb.Message) error
    SendMessage(msg *pb.Message) error
    To() (pb.PeerEndpoint, error)
    Stop() error
}

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

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元素。

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

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

3.5.1.2 事件消費者

事件消費者允許外部應用監聽事件。每個事件消費者通過時間流注冊事件適配器。消費者框架可以看成是事件流與適配器之間的橋樑。一種典型的事件消費者使用方式:

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指定語言綁定

      EventAdapter interface {
         GetInterestedEvents() ([]*ehpb.Interest, error)
         Recv(msg *ehpb.Event) (bool,error)
         Disconnected(err error)
      }

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

3.5.3 事件框架

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

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

    message Event {
        oneof Event {
            //consumer events
            Register register = 1;

            //producer events
            Block block = 2;
            Generic generic = 3;
       }
    }

每一個上面的定義必須是Register, BlockGeneric中的一種。

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

    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”,KC) ,(”headr”,KH),(”code-state”,KS), SigTCertuc(*)]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
(PKC, SKC). It then uses PKC
to encrypt all the keys associated to the chain-code:
[(”code”,KC) ,(”headr”,KH),(”code-state”,KS), SigTCertuc(*)]PKc,
私鑰SKC通過鏈指定的公鑰:
[(”chaincode”,SKC), SigTCertuc(*)]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一起用來爲他的第一筆註冊證書籤名的交易將會

nonceround0IDA <- hash(IDA, nonceall),

其中IDA出現在註冊證書中。從該點之後的這個用戶關於註冊證書的連續交易需要包含下面這樣的nonce
nonceroundiIDA <- hash(nonceround{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。

// 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層次交易的身份,這就是應用數據要加入到總賬的容器。

// 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接口更通用,提供之前接口實例的手段。

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成把它當作非結構化的字節流。


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. CertuicHandler獲取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,這是由用戶和服務分享的。

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

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需要提供給錢包服務的請求與蝦子面這個格式類似。

 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.goobc-classic的變種,爲PBFT添加批量能力;
  • obc-sieve.go, core PBFT消費者,實現Sieve共識協議和innerCPI接口, 調用CPI interface.

總之,除了調用發送消息給其他 peer(innerCPI.broadcastinnerCPI.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

簽名:

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服務。

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節中定義

塊檢索請求:

GET host:port/chain/blocks/173

塊檢索響應:

{
    "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來檢索區塊鏈的當前狀態。返回區塊鏈信息消息被定義如下。

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

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

  • previousBlockHash - 前一區塊的哈希

區塊鏈檢索請求:

GET host:port/chain

區塊鏈檢索響應:

{
    "height": 174,
    "currentBlockHash": "lIfbDax2NZMU3rG3cDR11OGicPLp1yebIkia33Zte9AnfqvffK6tsHRyKwsw0hZFZkCGIa9wHVkOGyFTcFxM5w==",
    "previousBlockHash": "Vlz6Dv5OSy0OZpJvijrU1cmY2cNS5Ar3xX5DxAi/seaHHRPdssrljDeppDLzGx6ZVyayt8Ru6jO+E68IwMrXLQ=="
}

6.2.1.3 鏈碼API

  • POST /chaincode

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

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

部署請求:

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"  
}

部署響應:

{
    "jsonrpc": "2.0",
    "result": {
        "status": "OK",
        "message": "52b0d803fc395b5e34d8d4a7cd69fb6aa00099b8fabed83504ac1c5d61a425aca5b3ad3bf96643ea4fdaac132c417c37b00f88fa800de7ece387d008a76d3586"
    },
    "id": 1
}

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

啓用安全的部署請求:

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節中定義

調用請求:

POST host:port/chaincode

{
  "jsonrpc": "2.0",
  "method": "invoke",
  "params": {
    "type": "GOLANG",
    "chaincodeID":{
      "name":"52b0d803fc395b5e34d8d4a7cd69fb6aa00099b8fabed83504ac1c5d61a425aca5b3ad3bf96643ea4fdaac132c417c37b00f88fa800de7ece387d008a76d3586"
    },
    "ctorMsg": {
        "function":"invoke",
        "args":["a", "b", "100"]
    }
  },
  "id": "3"  
}

調用響應:

{
    "jsonrpc": "2.0",
    "result": {
        "status": "OK",
        "message": "5a4540e5-902b-422d-a6ab-e70ab36a2e6d"
    },
    "id": 3
}

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

啓用安全的調用請求:

{
  "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節中定義。

查詢請求:

POST host:port/chaincode/

{
  "jsonrpc": "2.0",
  "method": "query",
  "params": {
    "type": "GOLANG",
    "chaincodeID":{
      "name":"52b0d803fc395b5e34d8d4a7cd69fb6aa00099b8fabed83504ac1c5d61a425aca5b3ad3bf96643ea4fdaac132c417c37b00f88fa800de7ece387d008a76d3586"
    },
    "ctorMsg": {
        "function":"query",
        "args":["a"]
    }
  },
  "id": "5"  
}

查詢響應:

{
    "jsonrpc": "2.0",
    "result": {
        "status": "OK",
        "message": "-400"
    },
    "id": 5
}

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

啓用安全的查詢請求:

{
  "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發現的消息)定義。

message PeersMessage {
    repeated PeerEndpoint peers = 1;
}

網絡請求:

GET host:port/network/peers

網絡響應:

{
    "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定義如下。註冊請求的響應可以是一個成功的註冊的確認或包含失敗的原因的錯誤。

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

註冊請求:

POST host:port/registrar

{
  "enrollId": "lukas",
  "enrollSecret": "NPKYL39uKbkj"
}

註冊響應:

{
    "OK": "Login successful for user 'lukas'."
}

GET /registrar/{enrollmentID}端點用於確認一個給定的用戶是否與CA註冊如果是,確認將被反悔。否則,將導致授權錯誤。

註冊驗證請求:

GET host:port/registrar/jim

註冊驗證返回:

{
    "OK": "User jim is already logged in."
}

註冊驗證請求:

GET host:port/registrar/alex

註冊驗證返回:

{
    "Error": "User alex must log in."
}

DELETE /registrar/{enrollmentID} 端點用於刪除一個目標用戶的登錄令牌。如果登錄令牌成功刪除,確認將被反悔。否則,將導致授權錯誤。此端點不需要payload。

刪除註冊請求:

DELETE host:port/registrar/lukas

刪除註冊返回:

{
    "OK": "Deleted login token and directory for user lukas."
}

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

註冊證書檢索請求:

GET host:port/registrar/jim/ecert

註冊證書檢索響應:

{
    "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。

交易證書檢索請求:

GET host:port/registrar/jim/tcert

交易證書檢索響應:

{
    "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"
    ]
}

交易證書檢索請求:

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

交易證書檢索響應:

{
    "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小節定義

交易檢索請求:

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

交易檢索響應:

{
    "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命令,執行如下命令

cd $GOPATH/src/github.com/hyperledger/fabic/peer
./peer

你可以獲得和下面類似的響應:

    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.idpeer.ddressAutoDetect,只需要傳遞CORE_PEER_ID=vp1CORE_PEER_ADDRESSAUTODETECT=true給命令行。

6.3.1.1 peer

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

./peer peer --peer-chaincodedev

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

./peer peer

6.3.1.2 登錄

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

./peer login <username>

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

./peer login jim

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

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參數來提供用戶的密碼。下面是一個例子。

./peer login jim -p 123456

6.3.1.3 鏈碼部署

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

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

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

./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命令執行目標來代碼中的指定函數。如下:

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

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

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

6.3.1.5 鏈碼查詢

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

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

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

./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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章