区块链基本概念
什么是区块链
区块链是一个数据库,准确的说它是针对“可信”领域的数据库。可以类比成TSDB针对以时间为索引维度的数据;MongoDB针对弱schema的数据。
区块链如何工作

上图是三种数据写入模式,在单机模式中数据直接写入到数据库中(如:单机Mysql);主从模式中数据会先交给Master然后再写入到Salve(实践上会考虑让Salve也接收“写请求”,但是这个请求会转交给Master。如:Mysql集群、etcd)。区块链的特别有两点:1. 多写,多个组织共同组成一个共享数据库(数据变化会对所有人“可见”),任何一方都可以对数据库进行读写。2. 允许网络攻击、节点不可信,可以在“拜占庭环境”工作。
什么是共识算法
区块链是“多写”的架构,所以必须解决“哪个事务在前哪个事务在后”,解决这个问题的算法就叫共识算法。
什么是PoW
区块链会有多个写入请求,必须有一种策略决定哪条先写入哪条后写入。PoW是让集群中节点去做某个无意义的计算,谁算的快就按谁给出的顺序。这种做法本质上是降低吞吐率,这恰恰是数据库最关心的性能指标,所以它是一种“开玩笑”的做法。
比特币、以太坊和Hyperledger Fabric是什么

可以把区块链的发展划分为三个节点,前两个阶段以“发币”为主。第三个阶段区块链被定义为解决各行各业数据可信存储的问题,变成一个真正的数据库。由于本人对币圈没有好感所以本文无视前两个阶段,只讨论第三个阶段。
Fabric基本概念
Fabric如何解决“共识”
排序,这是Fabric解决共识的基本算法。写入请求都会被排序节点排序后再进行提交。
如何理解Fabric和PBFT
Fabric的共识算法是基于PBFT实现的。Practical Byzantine Fault Tolerance(PBFT)算法是解决拜占庭容错(Byzantine Fault Tolerance)的一种算法。这里不对对拜占庭问题做过多讨论,笔者觉得——拜占庭将军的例子是一个非常非常非常烂的例子,故事情节过于丰富掩盖了问题的真相。归纳问题的本质,BFT问题就是要解决在不可信情况下(网络攻击、节点不可信)实现数据的一致。我们看一下PBFT算法的过程:
从全网节点选举出一个主节点,新区块由主节点负责生成。
每个节点把新交易向全网广播,主节点把从网络收集到需放在新区块内的多个交易排序后存入列表,并将该列表向全网广播。
每个节点接收到交易列表后,依据排序模拟执行交易。所有交易执行完后,基于交易结果计算新区块的哈希摘要,并向全网广播。
如果一个节点收到的 2f(f 为可容忍的恶意节点数)条其它节点发来的摘要都和自己相同,就向全网广播一条 commit 消息。
如果一个节点收到 2f+1 条 commit 消息, 即可正式提交新区块及其交易到本地的区块链和 状态数据库。
PBFT的第二步会对多个交易进行排序来解决并发写入的问题,所以Fabric中的排序节点就是算法描述中的主节点。
Fabric架构中涉及到哪些组件
Fabric只有两个二进制程序组成,一个叫orderer另一个叫peer。运行的时候对应两个进程,代表Fabric中的两个角色。

上图中的Applicaiton是基于Fabric的应用程序,membership是一个抽象组件,它还有个更常见的名字叫Membership Service Providers(MSP),它主要完成用户身份管理与认证(MSP没有权限管理,仅做认证)。在图中它和orderer、peer之间是虚线连接表示运行时在同一个进程。
所以运行Fabric只需要启动Orderer好Peer进程就可以了。
什么是Chaincode
Chaincode(链码),它还有一个更加常见的名字——smart contract(智能合约)。我们操作关系型数据库用SQL,与之对应的操作区块链(可信数据库)则用Chaincode。
什么是Channel
Channel(通道),Fabric支持多链,也就是允许多个区块链同时工作。用数据库打比方,你可以把它理解为“数据库”或者叫“schema”。
准备环境
最小化运行Fabric只需要Orderer和Peer两个进程,为了方便部署Fabric提供了两个Docker镜像分别运行两个进程。
一般一个区块链会有多个“涉众”,在Fabric中称为“组织”。比如商品溯源,里面涉及到生产厂家和销售商家两个组织,那么他们就可以共同组成一个区块链。

灰色的Org1表示生产厂家,紫色Org1表示销售商家,他们各自在自己的环境中运行Peer进程,生产厂家比较财大气粗拿出一台服务器做为排序节点。图中每个组织都只能有一个Peer和Orderer网络通讯,这个节点叫 Anchor Peer。Ordder节点只要把数据交给Anchor Peer节点就完成任务了,剩下的事情是Peer之间内部复制。
Channel 和Orderer是一对多的关系(一个Channel只能绑定一个Orderer节点,一个Orderer可以关联多个Channel)。在创建Channel的时候可以指定采用那个Orderer节点,虽然图中只画了一个Orderer节点,但是实践上它可能是由多个节点组成的一组服务(注意,Orderer节点彼此之间没有关系)。
在启动Orderer、Peer进程之前我们必须完成两件事情
创建MSP证书,在Fabric中节点和节点之间通讯必须经过证书交换,以标识“你是谁”。经过这一步后理论上Orderer节点和Peer节点就可以启动了,但是这时候的Fabric是“空”的,Orderer、Peer之间并不会产生网络通讯,它们是通过Channel“连接”起来的,所以我们需要创建第一个“数据库”。
创建Channel要经过三步。创建创世块(第一个数据块),初始化Channel事务,初始化Anchor Peer。经过这三部之后就通过Channel把Orderer、Peer关联起来。(注意Ordder、Peer之间并不会直接通讯,除非通过Channel建立起关联)
为了完成上述操作Fabric提供了两个工具,创建MSP证书的cryptogen,创建Channel的configtxgen。这两个工具都包含在Fabric的下载包中,官方网站上给出的是一个“404链接”(你懂得)。真实的下载链接是 https://nexus.hyperledger.org/content/repositories/releases/org/hyperledger/fabric/hyperledger-fabric/
根据不同的操作系统下载不同类型的二进制文件,比如我的是Mac操作系统演示使用的是1.1.0版本,下载的是darwin-amd64-1.1.0文件夹下的内容。
下载解压后在bin文件夹中包含了Fabric的所有二进制文件,除了我们后面需要的cryptogen、configtxgen还有orderer、peer,这两个就是orderer节点和peer节点进程的二进制文件。除非我们不在Docker上部署Fabric否则不需要这两个二进制文件。

创建MSP证书
./bin/cryptogen showtemplate会显示一个模板,把它导出到crypto-config.yaml
进行编辑。
OrdererOrgs
节点对应排序服务,里面只定义了一个一个节点;PeerOrgs
对应Peer节点,里面定义了两个组织。下图是我的配置

你不需要真正的是一个公网域名,Fabric只是用这个名字作为MSP的身份标识。这里我的域名是fireflyc.im
执行./bin/cryptogen generate --config=./crypto-config.yaml

生成了一堆证书、密钥,其实我们用到的部分不多。Orderer服务我们需要用到crypto-config/ordererOrganizations/fireflyc.im/orderers/orderer.fireflyc.im/msp
Peer服务我们需要用到crypto-config/peerOrganizations/org1.fireflyc.im/peers/peer0.org1.fireflyc.im/msp
代码中我们需要用到crypto-config/peerOrganizations/org1.fireflyc.im/users/Admin@org1.fireflyc.im`/msp
中的keystore
和signcerts
。其他证书可以暂时不用管它。
创建Channel
一共有三步操作,首先生成创世块,然后分别生成Org1和Org2的anchor Peer配置。
configtxgen
程序没有showtemplate命令,我从fabric-samples中复制出configtx.yaml
进行修改。

这里用到了YAML中不常见的一个语法——结点引用,比如配置中的*Org1是对后续定义的&Org1的引用。
详细的配置见文末的github地址。
执行./bin/configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./configtx/genesis.block
生成创世块,里面包含了Orderer信息和Org信息。可以用cat打开看一下,它其实是把crypto-config
下的证书做了一些合并。这个文件在Orderer节点启动的时候会用到。
执行./bin/configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./configtx/channel.tx -channelID mychannel
生成Channel的配置文件,这个文件在创建Channel时会用到。mychannel是Channel的名字。
执行./bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./configtx/Org1MSPanchors.tx -channelID mychannel -asOrg Org1MSP
生成Channel的Anchor Peer配置文件,同样的方法生成Org2的Anchor Peer配置文件,执行./bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./configtx/Org2MSPanchors.tx -channelID mychannel -asOrg Org2MSP
这两个文件在我们更新Channel配置指定Anchor Peer的时候用到。
总结:
这两个工具虽然生成了一堆文件,但是实际上用到的文件比较少,可以参考文末提供的github上的docker-compose文件。
启动
我们用Docker Compose来实现上面的拓扑,为了简化问题每个组织启动2个Peer。

首先定义的是排序节点,GENESISMETHOD、GENESISFILE用于指定创世块。
为什么需要创世块?在Fabric中所有的数据——包括Channel、ChainCode、Anchors Peer信息都是存放在系统链上的。这里的创世块就是指系统链的第一个数据块。
LOCALMSPID、LOCALMSPDIR设置了MSP中的标识和MSP证书的所在位置,在Fabric中节点之间通讯都必须提供MSPID和相对应的证书。
Orderer的证书是通过cryptogen生成的,在目录crypto-config下,通过docker compose的volumes指令挂载到容器中。docker容器都是无状态的,删掉之后数据也就丢失了,为了保存状态我们挂载var目录到orderer的/var/hyperledger/production/orderer
(orderer节点的数据文件所在目录)把数据持久化到容器外部。

Peer节点要运行ChainCode,在Fabric中是通过Docker作为“虚拟机”来运行ChainCode的,所以我们必须把Docker的sock文件导入到Peer中。运行成功后的ChainCode会和Peer通讯所以要保证它们用同一个网络,通过CORE_VM_DOCKER\_HOSTCONFIG_NETWORKMODE
指定ChainCode使用的网络。
LOCALMSPID 、MSPCONFIGPATH还是和MSP相关的证书和Peer的MSP标识。GOSSIP部分和gossip选举协议有关,其中bootstrap是指goosip的“目录节点”。
关于gossip的广播。一般情况下在IP网络中没有真正的广播,不是一次发送一条数据多终端收到;而是一条一条的发送给每个终端。所有要一条一条的发送就必须知道每个终端的IP地址,就需要有一个“目录节点”存放所有节点的地址。

CLI节点是一个命令行工具,Fabric提供的SDK太简陋,初始化Channel、更新Channel、安装、升级ChinaCode的工作建议采用命令行完成。
Peer进程是互斥的,所以不能直接登录到运行有peer进程的容器中再次执行peer
通过执行./dev.sh up
其中系统
启动成功后可以看到每个peer的日志

orderer节点的日志

HelloWorld
首先创建一个Channel,并且然后把四个节点加入到Channel,最后更新Channel的Anchor Peers
执行docker exec -it cli bash
进入到CLI中,执行下面的命令

执行成功后会在orderer节点上看到如下日志,证明增加了一个新的块

channel.tx包含的是一个事务,执行成功后会生成一个新的块,这个块就是该channel的创世块(和系统链创世块没有关系)。这个块在加入到Channel的时候会用到,所以系统会写到“当前目录下”(比如我在cli中的/
目录下执行的)

为了操作方便我把它放到/opt/configtx下。接下来设置一下环境变量,把org1中的peer0加入channel中。

加入成功后可以在peer0.org1.fireflyc.im的日志中看到如下输出。

接下来我们把peer1也加进来

然后切换到org2中,把另外两台也加进来


现在四个Peers都加入进来了,我们更新一下Anchor Peers。

接下来安装Chaincode、实例化、测试第一个Chaincode

执行成功后可以看到多了一个docker容器

通过docker logs 验证Chaincode的输出

完整的命令在demo.sh中
总结
网络上大部分关于Fabric的资料都是基于fabric sample的,本文是用最精简的方式提供了一个可运行的fabric环境。
目前的区块链产品还不是很成熟,发币肯定没问题,真正的商用还有一定距离。所以fabric的官方资料都不是特别完整(ethereum?这个除了用来发币还能干别的?),希望本文能够抛砖引玉。
Hyperledger-Fabric最小化系统地址:
https://github.com/fireflyc/tiny-fabric-env
欢迎关注公众账号了解更多信息“写程序的康德——思考、批判、理性”





