Hyperledger Fabric实践
背景
传统商业网络面临的挑战
- 每个参与方都有自己的账本,在交易发生时各自更改;
- 因此产生为了协同各参与方而带来额外的工作及中介等附加成本;
- 由于业务条件“合同”重复分散在各个参与方造成整体业务流程的不有效性;
- 整个业务网络依赖于一个或几个中心系统,一旦发生问题包括欺诈、网络攻击或错误致使整个商业网络是脆弱的;
区块链架构带来以下改变
- 区块链架构使每一个商业网络的参与方都具有一个共享的帐本,当交易发生时,通过点对点的复制更改所有账本;
- 使用密码算法确保网路上的参与者仅仅可以看到和他们相关的账本内容,交易是安全的、授权的和验证的;
- 区块链也将资产转移交易相关的合同条款嵌入交易数据库以做到满足商务条件下交易才发生;
- 网络参与者基于共识机制或类似的机制来保证交易是共同验证的。商业网络满足政府监管、合规及审计;
区块链架构具有以下特性
- 同样的商业参与方,并不是脱媒的游戏;
- 共识(CONSENSUS) – 所有的参与方认同交易的有效性 ;
- 可证明性(PROVENANCE) – 每个参与方了解资产从哪里来,其所有权是如何改变的;
- 永恒性(IMMUTABILITY) – 每个参与方一旦交易被同意发生则无法篡改。 如果交易是错误的,必须由新交易冲正并全可跟踪;
- 权威性(FINALITY) – 只有一个地方来决定资产的归属权及交易的完整性。 这就是共享账本的作用;
Hyperledger Fabric整体架构
Hyperledger Fabric逻辑架构
Hyperledger Fabric区块链网络
Hyperledger Fabric运行时架构
多通道 & 子账本
- 通道提供⼀种通讯机制,将peer和 orderer连接在⼀起,形成⼀个个具有保密性的通讯链路(虚拟)
- Fabric的区块链⺴⽹网络缺省包含⼀一个账本(系统账本)和⼀一个通道;
- ⼦账本可以被创建,并绑定到一个通道
事务
事务为ChainCode的一次调⽤。每个ChainCode在部署时,都需要和背书(ESCC)和验证(VSCC)的系统ChainCode关联。
- ESCC决定如何对proposal进⾏行背书;
- VSCC决定事务的有效性(包括背书的正确性);
事务处理流
- 应⽤用向一个或多个Peer节点发送对事务的背书请求;
- 背书节点执⾏行ChainCode,但并不将结果提交到本地账本,只是将结果返回给应用;
- 应⽤收集所有背书节点的结果后,将结果广播给Orderers;
- Orderers执⾏行共识过程,并生成Block,通过消息通道批量的将Block发布给Peer节点;
- 各个Peer节点验证交易,并提交到本地账本中。
账本
在Fabric 1.0中,我们存在3种类型的数据存储:
- Block数据:⽂件系统方式存储区块链数据;
- State Database:以键值对的方式存储了我们在ChainCode中操作的业务数据;
- 索引数据库:对历史数据和区块链索引的数据库;
应用场景
- 积分交换平台
IBM与中国银联正在联手创建一个基于区块链技 术的银行卡积分交换平台。在线上,消费者可 以与他人交易自己通过购物和其他奖励措施所 获得的积分,而在线下,积分平台用户可以去 任何一家配备智能POS机的超市或商场,使用奖 励积分兑换商品。在IBM和中国银联的设想中, 这一概念还能够被应用于航空里程、话费帐单 或者加油卡积分的交易上
- 商品身份溯源
区块链技术将实现食品的全程数字化跟踪,从供应商 生态系统到商店货架,最终到消费者。对于食品供应 商而言,数字产品信息-诸如农场原产地信息、批号、 工厂和加工数据、有效期、存储温度和运输等都将与 相应的食品建立数字化关联,而整个过程中每一环节 的信息将被输入到区块链中。每笔交易的信息都须得 到商业网络中所有成员的共同许可;一旦达成共识, 就会形成无法更改的永久性记录,以此确保所有的商 品相关信息都准确无误。
- 托管业务的投资运营
区块链作为共享账本,在资产管理、托管 业务、审计之间共享托管业务的核心业务 数据。区块链智能合约自动判断交易的合理性和合规性通过基于区块链的系统来发送投资指令, 代替原来的传真电话方式。
Hyperledger fabric术语
Anchor Peer - 锚节点
锚节点是通道中能被所有对等节点探测、并能与之进行通信的一种对等节点。通道中的每个成员都有一个(或多个,以防单点故障)锚节点,允许属于不同成员身份的节点来发现通道中存在的其它节点。
Leading Peer - 主导节点
每一个Member在其订阅的channel上可以拥有多个peer,其中一个peer会作为 channel的leading peer代表该Member与ordering service通信。Ordering service将block传递给leading peer,该peer再将此block分发给同一member下的其他peer。
Block - 区块
在一个通道上,(区块是)一组有序交易的集合。区块往往通过密码学手段(Hash 值)连接到前导区块。区块是一组有序的交易集合,在通道中经过加密(哈希加密)后与前序区块连接。
Chain - 链
Chain就是block之间以hash连接为结构的交易日志。Peer从order service接 收交易 block,并根据背书策略和并发冲突标记 block 上的交易是否有效,然后 将该block追加到peer 文件系统中的hash chain上。
Transaction - 交易
Chaincode的invoke或instantiate操作。Invoke是从ledger中请求read-write set;Instantiate是请求在peer上启动Chaincode容器。
Ledger - 账本
Ledger是个channel的chain和由channel中每个peer维护的数据库。
Member - 成员
拥有网络唯一根证书的合法独立实体。像peer节点和app client这样的网络组件会链接到一个Member。
Membership Service Provider - MSP
MSP是指为client和peer提供证书的系统抽象组件。Client用证书来认证他们的交易;peer用证书认证其交易背书。该接口与系统的交易处理组件密切相关,旨在使已定义的成员身份服务组件以这种方式顺利插入而不会修改系统的交易处理组件的核心。
Membership Services - 成员服务
成员服务在许可的区块链网络上认证、授权和管理身份。在peer和order中运行的成员服务的代码都会认证和授权区块链操作。它是基于PKI的MSP实现。Fabric-ca 组件实现了成员服务,来管理身份。特别的,它处理ECert和TCert的颁发和撤销。ECert是长期的身份凭证;TCert是短期的身份凭证,是匿名和不可链接的。
Ordering Service - 排序服务和共识服务
将交易排序放入block的节点的集合。Ordering service独立于peer流程之外,并以先到先得的方式为网络上所有的channel作交易排序。Ordering service支 持可插拔实现,目前默认 Solo(单节点共识)、kafka(分布式队列)和 SBFT(简 单拜占庭容错)、ordering service是整个网络的公用binding,包含与每个Member相关的加密材料。
Peer - 节点
一个网络实体,维护ledger并运行Chaincode容器来对ledger执行read-write操作。Peer由Member拥有和维护。
Proposal - 提案
一种针对channel中某peer的背书请求。每个proposal要么是Chaincode
instantiate要么是Chaincode invoke。
Chaincode - 链码
链码是一个运行在账本上的软件,它可以对资产进行编码,其中的交易指令(或
者叫业务逻辑)也可以用来修改资产。
- Initialize - 初始化
将chaincode放到peer的文件系统的过程。
- Install - 安装
将chaincode放到peer的文件系统的过程。
- Instantiate - 实例化
实例化chaincode(同时启动chaincode容器)的过程。
- Query - 查询
对于current state中某个key的value的查询请求。
- Invoke - 调用
用于调用chaincode内的函数。Chaincode invoke就是一个交易proposal,然后执行 模块化的流程(背书、共识、验证、提交)。Invoke的结构就是一个函数和一个 参数数组。
Channel - 通道
通道是构建在“Fabric”网络上的私有区块链,实现了数据的隔离和保密。通道特定的账本在通道中是与所有对等节点共享的,并且交易方必须通过该通道的正确验证才能与账本逬行交互。通道是由一个“配置块”来定义的。
Concurrency Control Version Check - 并发控制版本检查(CCVC)
CCVC 是保持通道中各对等节点间状态同步的一种方法。对等节点并行的执行交易,在交易提交至账本之前,对等节点会检查交易在执行期间读到的数据是否被修改。如果读取的数据在执行和提交之间被改变,就会引发CCVC冲突,该交易就会在账本中被标记为无效,而且值不会更新到状态数据库中。
Configuration Block - 配置区块
包含为系统链(排序服务)或通道定义成员和策略的配置数据。对某个通道或整个网络的配置修改(比如,成员离开或加入)都将导致生成一个新的配置区块并追加到适当的链上。这个配置区块会包含创始区块的内容加上增量。
Consensus - 共识
用于产生一个对于交易排序的结果共识以及确认构成区块的交易集的正确性。
Current State - 当前状态
ledger 的 current state 表示其 chain 交易 log 中所有 key 的最新值。peer 会将处 理过的block中的每个交易对应的修改value提交到ledger的current state,由 于 current state 表示 channel 所知的所有最新的 k-v,所以 current state 也被称 为 World State。Chaincode 执行交易 proposal 就是针对的 current state。
State Database – stateDB
为了从Chaincode中高效的读写,current state数据存储在stateDB中,包括 levelDB和couchDB。
Endorsement - 背书
Endorsement是指一个peer执行一个交易并返回YES-N0给生成交易proposal的 client app的过程。chaincode具有相应的endorsement policies,其中指定了endorsing peer。
Fabric-ca 证书节点
Fabric-ca是默认的证书管理组件,它向网络成员及其用户颁发基于PKI的证书。 CA为每个成员颁发一个根证书(rootCert),为每个授权用户颁发一个注册证书 (eCert),为每个注册证书颁发大量交易证书(tCerts)。
Genesis Block - 创世区块
Genesis Block是初始化区块链网络或channel的配置区块,也是链上的第一个区块。
Gossip Protocol - Gossip 协议
- 管理peer发现和channel成员;
- channel上的所有peer间广播账本数据;
- channel上的所有peer间同步账本数据;
环境部署
环境准备
- 安装Go环境
- 安装Docker
- 安装Docker-compose
- Fabric源码下载
- 下载docker镜像
aws云环境
1 Order + 2 Peer
order.example.com (Order)
peer0.org1.example.com (Peer1)
peer0.org2.example.com (Peer2)
生成公私钥和证书
- 安装依赖
1 | sudo apt-get install build-essential libtool libltdl3-dev |
- 编译cryptogen
1 | cd ~/go/src/github.com/hyperledger/fabric |
在build/bin文件夹下就可以看到编译出来的cryptogen程序。
配置crypto-config.yaml文件
主要修改组织的Name,Domain,Count等配置
成生公私钥和证书
1 | cd examples/e2e_cli/ |
成生文件在crypto-config文件夹
1 | tree crypto-config |
生成创世区块和Channel配置区块
- 编译configtxgen
1 | cd ~/go/src/github.com/hyperledger/fabric |
配置configtx.yaml
TwoOrgsOrdererGenesis:配置了由两个Org参与的Orderer共识配置
TwoOrgsChannel:由2个Org参与的Channel配置
Organizations:Peer节点的配置包含了MSP的配置,锚节点的配置
- 生成创世区块
1 | cd examples/e2e_cli/ |
- 生成Channel配置区块
1 | ../../build/bin/configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID mychannel |
- 生成锚节点更新文件
1 | ../../build/bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID mychannel -asOrg Org1MSP |
最终我们可以在channel-artifacts文件夹中看到4个文件:
1 | channel-artifacts/ |
修改docker-compose文件
修改base/docker-compose-base.yaml文件(涉及peer节点、端口映射等)
设置orderer节点的docker-compose文件
设置peer节点的docker-compose文件(涉及extra_hosts、depends、couchdb配置、command命令删除等)
启动Fabric网络
启动orderer节点
1 | cd ~/go/src/github.com/hyperledger/fabric/examples/e2e_cli |
启动peer节点
- 创建couchdb数据目录
1 | mkdir -p ~/couchdb |
- 启动couchdb实例
1 | docker run -p 5984:5984 -d --name my-couchdb -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=admin123 -v ~/couchdb:/opt/couchdb/data hyperledger/fabric-couchdb |
- 验证couchdb服务
访问http://peerip:5984/_utils ,若服务正常可以看到CouchDB的Web管理界面
- 启动peer节点和cli容器
1 | cd ~/go/src/github.com/hyperledger/fabric/examples/e2e_cli |
创建channel
- 进入cli容器
1 | docker exec -it cli bash |
- 创建channel
创建Channel的命令是peer channel create,由于前面创建Channel的配置区块时,指定了Channel的名字是mychannel,所以这里我们必须创建同样名字的Channel。
(注:开放防火墙端口)
1 | ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem |
- 各个peer加入mychannel
1 | peer channel join -b mychannel.block |
- 更新相应锚节点(Org1,Org2)
1 | CORE_PEER_LOCALMSPID="Org1MSP" |
chaincode的安装与运行
安装chaincode
在cli上为每一个peer安装链上代码,用peer chaincode install命令可以安装指定的ChainCode并对其命名
1 | CORE_PEER_LOCALMSPID="Org1MSP" |
安装的过程其实就是对CLI中指定的代码进行编译打包,并把打包好的文件发送到Peer,等待接下来的实例化。切换到其它peer节点服务器,分别安装链码
实例化chaincode
实例化链上代码主要是在Peer所在的机器上对前面安装好的链上代码进行包装,生成对应Channel的Docker镜像和Docker容器。并且在实例化时我们可以指定背书策略。(注:instantiate/upgrade均只需要在一个peer上执行一次即可)
1 | CORE_PEER_LOCALMSPID="Org1MSP" |
使用docker ps可以看到有新的容器 dev-peer0.org1.example.com-mycc-1.0正在运行。
chaincode功能测试(不同peer上执行均可)
- initMarble
1 | peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -C mychannel -n mycc -c '{"Args":["initMarble","marble1","blue","35","tom"]}' |
- readMarble
1 | peer chaincode query -C mychannel -n mycc -c '{"Args":["readMarble","marble1"]}' |
- transferMarble
1 | peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -C mychannel -n mycc -c '{"Args":["transferMarble","marble2","jerry"]}' |
- deleteMarble
1 | peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA -C mychannel -n mycc -c '{"Args":["deleteMarble","marble1"]}' |
- getMarblesByRange
1 | peer chaincode query -C mychannel -n mycc -c '{"Args":["getMarblesByRange","marble1","marble2"]}' |
- getHistoryForMarble
1 | peer chaincode query -C mychannel -n mycc -c '{"Args":["getHistoryForMarble","marble1"]}' |
- queryMarblesByOwner
1 | peer chaincode query -C mychannel -n mycc -c '{"Args":["queryMarblesByOwner","tom"]}' |
使用Farbic Node SDK
安装node、npm
Fabric Node SDK支持的Node版本是v6,不支持最新的v8版本,执行以下命令即可安装NodeJS的最新v6版本。
1 | curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash - |
package.json添加依赖模块
1 | "fabric-ca-client": "^1.0.0", |
编写完package.json后,执行npm install
即可完成模块安装。
编写fabric的Query和Invoke方法
略
升级Chaincode
安装新版本chaincode,打包到peer节点
1 | docker exec -it cli bash |
升级chaincode
1 | peer chaincode upgrade -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n mycc -v 2.0 -c '{"Args":["init"]}' -P "OR ('Org1MSP.member','Org2MSP.member')" |
开发模式
进入链码开发⽬目录
1 | cd fabric-samples/chaincode-docker-devmode |
打开3个终端,
- 终端 1
1 | $ docker-compose -f docker-compose-simple.yaml up |
- 终端 2
1 | $ docker exec -it chaincode bash |
- 终端 3
1 | $ docker exec -it cli bash |
背书策略
chaincode在实例化的时候,需要指定背书策略。这里的背书策略就是需要什么节点背书交易才能生效。
发起交易的时候,发起端需要指定交易发给哪些节点进行背书验证。发送后等待背书节点的返回,收集到足够的背书后将交易发送给orderer进行排序打包分发。最后,当每个Peer接受到block数据后,会对其中的交易进行验证,如果交易不符合背书策略,就不会在本地生效。
1 | - AND(‘Org1.member’, ‘Org2.member’, ‘Org3.member’) ,请求3个principle的签名。 |
Chaincode
什么是链码
chaincode简称链码,一般是用户使用go语言编写的应用代码。链码被部署在 Fabric 网络节点上,运行在Docker 容器中,并通过gRPC协
议与相应的Peer节点进行交互,以操作分布式账本中的数据。
链码的生命周期
Fabric 提供了 , 和 upgrade 4 个命令管理链码的生 命周期。
通过install安装链码,通过 instantiate实例化链码,然后可以通过query调用链码和查询链码。
如果需要升级链码,则需要先 install安装新版本的链码,通过upgrade升级链码。
在install安装链码前,可以通过package打包并签名生成打包文件,然后在通 过 install 安装。
Chaincode操作
1 | # 安装链码 |
Chaincode 接口
每个链码都需要实现一下 Chaincode 接口
1 | type Chaincode interface { |
Init:当链码实例化或者升级的时候,Init方法会被调用
Invoke:当链码收到调用(invoke)或者查询的时候,Invoke 会被调用
链码常用api
- 参数读取 API
GetFunctionAndParameters 提取调用链码交易中的参数,其中第一个作为 被调用的函数名称,剩下的参数作为函数的执行参数
1 | func (stub *ChaincodeStub) GetFunctionAndParameters() (function string, |
GetStringArgs 提取链码交易的指定参数
1 | func (stub *ChaincodeStub) GetStringArgs() []string |
- 账本状态交互 API
PutState 在账本中添加或更新一对键值。
1 | func (stub *ChaincodeStub) PutState(key string, value []byte) error |
GetState 负责查询账本,返回指定键的对应值
1 | func (stub *ChaincodeStub) GetState(key string) ([]byte, error) |
DelState 删除一对键值
1 | func (stub *ChaincodeStub) DelState(key string) error |
GetStateByRange 查询指定范围内的键值,startKey为起始key,endKey 为终止 key
1 | func (stub *ChaincodeStub) GetStateByRange(startKey, endKey string) |
GetHistoryForKey 返回某个键的历史记录
1 | func (stub *ChaincodeStub) GetHistoryForKey(key string) |
链码的基本结构
1 | package main |
参考
Fabric 1.0的多机部署
深入理解Fabric环境搭建的详细过程
在HyperLedger Fabric中启用CouchDB作为State Database
IBM 开源技术微讲堂 —— 区块链和 HyperLedger 系列