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

相关文章
相关标签/搜索