区块链轻松上手:原理、源码、搭建与应用
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.2 理解区块链的概念

1.2.1 深入理解Blockchain

比特币的狂潮引发了人们对其底层运行机制和原理的探索,并且衍生出了Blockchain这个新概念,Blockchain被翻译为“区块链”,由此,区块链开启了独立于比特币的研究探索新方向。随着研究的深入,人们逐步意识到区块链这一新技术的独特价值,越来越多的商业服务和互联网应用开始搭上区块链这趟快车。

2018年,一场让人始料未及又具有颠覆性意义的技术革命疯狂来袭,主角就是区块链。区块链技术被认为是继蒸汽机、电力、互联网之后的下一代颠覆性的核心技术,如果说蒸汽机释放了人们的生产力,电力解决了人们的基本生活需求,互联网彻底改变了信息传递的方式,那么区块链作为建立信任的机器,将可能彻底改变整个人类社会的价值传递方式。区块链技术的热度,从整个互联网和金融行业对比特币的狂热,就可见一斑。Web浏览器拓荒者Marc Andressen指出:

在20年后,我们就会像讨论今天的互联网一样,讨论区块链。

Blockchain顾名思义,就是由一个个 Block(块)串联成的一个链条,在每个 Block里保存的是一笔笔交易(Transaction)数据,以比特币为例,每隔十分钟左右就会有矿机挖出新币。之前说过,挖出新币也是一种交易,被称为“Coinbase交易”,它作为这段时间内的第一笔交易,会连同在这段时间内产生的其他交易被“打包”为一个Block,每个Block与前后紧邻的Block相互链接,组成一个链表。从数据结构上来看,Blockchain非常类似于我们熟悉的传统链表的数据结构,如图1-3所示。

图1-3

但Blockchain与普通的链表又有区别,因为链表一般是“水平”视角的,比如从左到右增加长度,Blockchain则是“上下”视角,从下到上一层层叠加Block,每增加一个Block,则Blockchain的高度+1。从另一个角度来看,区块链的高度代表区块链数据的“版本”,比如在高度为2的区块链与高度为3的区块链之间相差一个版本,如果某个节点本地记录的区块链高度为2,而与之相连的某个节点的区块链高度为3,则此节点只要从邻居节点那里拉取第3个Block数据,即可完成账本的同步。

那么,Blockchain设计的独特之处在哪里呢?

首先,Blockchain记录的交易具有很强的公信力,具备可信任与防篡改特性。可信任这一点是通过数字证书机制来保证的。以比特币为例,我们知道一个用户可用的比特币余额对应的是UTXO,这笔钱存放在该用户的某个比特币钱包地址中,并且这个地址只能用对应的私钥打开(解密),因此,只有该用户才能动用这笔钱来发起一个新的交易,在发起的新交易中,该用户的私钥签名信息也一同被登记到交易记录中,在有了数字证书签名后,在随后的传输及持久化保存到Block的过程中,任何节点都无法篡改这笔交易的记录,并且任一节点都可以通过该用户公开的公钥来验证这笔交易的合法性,然后予以承认并记录到账本中。此外,Blockchain里的每个Block除了用指针链条来记录前后关系,还用了基于哈希摘要的算法来加强这个链条,具体做法是在每个新生成的Block里都记录前一个Block内容的哈希值(32字节)。我们知道,由于哈希算法的特殊性,输入原文的任一字节发生变化,都会导致哈希值不同,因而任一Block的内容在被伪造变动后,它的哈希值都会发生变化,后续所有Block都必须继续伪造,从而引发连锁效应,这显然是极其困难的。

其次,在比特币的Blockchain里还自带“索引”设计。在比特币的平台里,平均每个交易的数据为250字节左右,每个Block平均包含2000个Transaction,为了能在这2000多个交易中快速查找和定位某个交易,比特币采用了一种特殊的二叉树结构Merkle Tree来实现“索引”,因为二叉树这种数据结构非常适合实现索引,所以常见的关系型数据库普遍采用二叉树做索引。Merkle Tree通常被称作Hash Tree,顾名思义,就是存储哈希值的一棵树。如图1-4所示,Merkle Tree的叶子是数据块(比如一个字符串)的哈希值,而非叶节点是其对应的子节点串联形成的字符串的哈希值,简单来说,非叶节点总是所有子节点内容的哈希值。

图1-4

我们可以对Block中的每个交易计算32字节的哈希值,然后把它们当作所有叶子节点,以此构建出一棵完整的 Merkle Tree。在比特币平台上每个交易的数据平均为250字节,可以看出,Merkle Tree相当于把一个Block压缩到了1/8大小,这样一来,客户端程序如果只想验证某个交易是否完成或存在,则无须下载完整的Block数据,只需获取这个Block里的Merkle Tree,对目标交易计算哈希值,然后将哈希结果与 Merkle Tree 中各个中间节点的哈希值依次进行对比,就能够很轻松地确定这个交易是否存在,以及位于哪个叶子节点上,这对于不参与比特币挖矿的轻量级客户端来说尤为关键。图1-5和图1-6给出了包括Merkle Tree在内的Blockchain的完整示意图。

图1-5

图1-6

从图1-5和图1-6可以看出,在比特币的每个Block里还记录了一个叫作Nonce的字段,是4个字节的整数。前面讲到,比特币挖矿就是寻找这个Nonce随机数的过程。Nonce是Number once的缩写,在密码学中是一个只被使用了一次的随机数值,在加密技术中初始向量和加密散列函数都发挥着重要的作用。挖矿就是要计算出新Block的哈希值,计算这个哈希值的输入参数有:比特币版本号、前一个Block的哈希值、Merkle Tree的根、区块时间戳及Nonce等数值,基本上可变的参数只有 Nonce,因此比特币矿机需要不断修改Nonce的值,才可能计算出符合条件的目标哈希值,这个目标哈希值要小于某个指定的阈值——nBits,因此nBits字段也被称为挖矿难度系数,它也存在于Block的Header字段中,图1-7很形象地给出了挖矿过程涉及的上述参数。

图1-7

Blockchain通常采用文件存储结合KV存储的方式来实现,其中,文件用来记录Block的具体信息,KV数据库则用来存储与区块相关的元数据,例如区块头、交易ID、交易状态等,可以用于快速检索。例如,比特币程序在从网络中接收Block数据后,会采用网络格式直接将 Block 数据存储在本地磁盘文件中(一个块文件大约有128MB),并采用LevelDB作为区块元数据的存储模块,LevelDB存储了Block索引与元数据相关的内容,以加速检索数据;Hyperledger Fabric(简称Fabric)则支持LevelDB(默认)、独立部署的CouchDB及关系型数据库MySQL;瑞波币比较特殊,它采用了关系型数据库SQLite来存储Block的具体信息,这样处理的主要目的是在单纯查询区块头信息和具体的每笔交易时,可以直接从关系型数据库中查找,这是瑞波币和其他三种类型的区块链系统在存储方面的最大不同。

在1.2.2节会讲解Blockchain的一个基本特性:数字账本。

1.2.2 数字账本

Blockchain在本质上就是一个数字账本(Ledger),而每个Block就是账本中的一页,任一Block被修改或者“丢失”都会导致整个链条无效,这与传统的账本在功能和作用上是完全一致的。此外,在数字化账本以后,对账问题不再是一个令人头疼的问题了,以编程方式随时可以精确对账,也更容易快速分析和追踪账务的往来信息,财务造假及洗钱将变得很难,收税也会变得更加方便。

既然比特币宣称是一个去中心化的数字货币系统,那么其中最为关键的基础数据——“账本”肯定是要分布式存储的,并且账本所采用的分布式技术一定与传统的数据分布式技术有所不同,因为它是账本数据,需要高度完整、绝对安全可靠并且高度一致。所以,比特币的账本采用了“一致性的多副本机制”,即比特币网络中的每个矿工节点(Miner Peer)都需要在本地独立保存一份完整的账本数据(Blockchain),如图1-8所示。

图1-8

既然是分布式的账本,那么问题来了:比特币网络中的某个节点在宕机一段时间后,重新加入比特币网络时,它的账本数据就缺失了,如何重新同步?与此相关的另外一个问题:加入比特币网络的新节点如何获取之前的所有账本记录?

答案很简单,因为Block一旦生成就不可变,并且组成账本的基本单元是Block,因此客户端可以告诉服务器,跳过某个Block编号之前的数据,从而实现简单的“断点续传”同步功能。此外,每隔10分钟才生成一个新的Block,因此不用太频繁地同步,也能很快保持全网账本的数据一致性。

区块链技术的这种“分布式账本”功能,不但可以监控资金及数字货币的转移,同样可以监控任何有价资产的交易,例如股票、债券、期货、不动产、珠宝等,目前这些交易大部分都需要由中心化的银行或金融机构来负责记录,也导致了额外的时间和成本。一些研究机构的分析师预测:区块链技术能够在五年之内为全球交易的清算和结算节省超过160亿美元,并且分布式账本模式为金融市场带来的影响远不止简化流程这么简单。甚至有人认为:分布式账本模式将会颠覆华尔街的金融业。此外,分布式账本技术也将给会计、审计行业带来颠覆性的改变,已经使用了几百年的复式记账法慢慢地不再适应数字化社会的需要,新的记账机制需要与分布式账本技术有机结合,一定会带来更高的效率和更低的成本,以及更多的被迫改行的会计。

既然要做通用化的数字账本,那么Blockchain就不能只存放比特币的交易记录了,是否有一种机制可以让程序员自主编程决定在 Blockchain里存放什么交易记录?这个机制就是智能合约(Smart Contract,在Fabric项目里叫作Chaincode)。

1.2.3 智能合约

设想一下,我们把在Block里存放的用户交易数据建模为键值对(Key/Value)的一个集合,这些键值对的定义与数量都由程序员自主决定,区块链平台则提供了针对这些数据的CRUD接口,并且提供了一个入口,可以让程序员把自己编写的操作这些键值对的一段程序嵌入平台里,这样系统在生成Block的同时会自动触发该程序的执行,从而完成将用户数据写入账本的特定逻辑操作,这个场景就是智能合约。

作为以太坊前身的比特币内置了一套基于栈的脚本执行引擎,通过一段脚本对交易进行验证,比如签名验证和多重签名验证等,这套脚本语言是非图灵完备的(图灵完备通常指具有无限存储能力的通用物理机器或编程语言),足够简单却也足以应对货币转账的各种需求。而以太坊的目标是将区块链发展成可以承载更多业务而非只能承载单纯的数字货币的通用型平台,因此它首先提出并实现了区块链2.0的核心特性——智能合约。以太坊的智能合约是首个具备图灵完备能力的智能合约,简单来说,能计算一切可计算的问题,比如可以执行判断、无限循环等指令,这样的编程语言就叫作图灵完备的语言。有了智能合约后,用户便可以在以太坊的平台上创建自己的合约,而合约的内容可以包含货币转账在内的任意逻辑。也正是因为区块链2.0中智能合约的出现,让区块链从一项小众技术演变成一个热点平台。

智能合约这个概念在很早之前就有了,但在区块链出现后才开始进行有实际价值的应用。下面以常规的银行账号与转账流程这个业务场景为例,来说明与Fabric智能合约相关的一些概念与机制。假设有两个账号A与B,A账号的初始值为100元,B账号的初始值为50元,那么可以通过以下KV数据操作来记录这两个账号的账本信息:

Set   A=100

Set   B=50

此时,在Blockchain里就产生了一个交易,交易记录的内容为:A=100,B=50。接下来编写一个智能合约的函数来完成转账操作,这个函数很简单,方法签名如下:

void transferTo(from,to, amount)

用伪代码实现的转账逻辑也很简单(省略了校验):

fromAccount=getValue(from)

toAccount=getValue(to)

setValue(from, fromAccount-amount)

setValue(to, toAccount+amount)

在用户触发transferTo(A,B,50)这个智能合约函数的调用之后,A与B的账号余额就发生了改变,并且被持久记录到 Blockchain里,扩散到全网。在 Fabric智能合约里,这些KV键值对的当前最新值组成了 World State(世界态),所有 KV键值对都被存放在一个KV数据库里,目前默认使用的是Level DB。有了World State这个扩展,智能合约的编写就变得更加简单,因为我们不需要遍历所有交易记录来计算某个 Key的最新值。

要实现Fabric上的智能合约,则需要经过以下步骤。

(1)编写智能合约代码(用Go或者Java语言)。

(2)部署智能合约到Fabric网络节点中。

(3)初始化智能合约(相当于Set A=100及Set B=50的操作)。

(4)调用智能合约,例如transferTo(from,to,amount)。

以太坊里的智能合约采用了一种类似 JavaScript的编程语言Solidity,运行在以太坊虚拟机(EVM)中。EVM提供了堆栈、内存、存储器等虚拟硬件,以及一套专用的指令集,所有代码都在沙盒中运行;还提供了合约间相互调用的能力,甚至可以在运行时动态加载其他合约的代码来执行,这种能力使得以太坊的合约具有非常高的灵活性,但也可能会使合约的功能具有不确定性。

而Fabric上的智能合约也是运行在一个类似的沙箱环境即Docker容器中的,这种设计的重要出发点是系统的安全性,通过进程隔离,避免用户的智能合约代码系统入侵平台,引发重大安全漏洞。

在去中心化的分布式网络环境中,每个节点都有权利和机会产生一个新的 Block并要求记入公共账本中,在这种情况下,如何让大家相信Block的发起人非常可靠呢?如何确保账本数据不会被“一小撮别有用心的人”肆意编造呢?这就涉及区块链中另外一个复杂的话题——共识机制(Consensus Mechanism)。

1.2.4 共识机制

什么是“共识机制”?共识机制或者共识算法处理的是大家针对某个 Proposal(提案)达成一致意见(同意提案或者否定提案)的过程。Proposal在分布式系统中是一个十分宽泛的话题,例如多个事件发生的先后顺序、多副本情况下某个键对应的最终值、集群中谁是Leader,等等,这些需要集群中多个节点“达成共识”的话题都可以被看作一个Proposal。区块链上的共识机制主要解决由谁来构造区块,以及如何维护区块链统一的问题。

如果分布式系统中的各个节点都能保证以十分强大的性能(瞬间响应、高吞吐)无故障地运行,则实现共识过程的逻辑并不复杂,只需简单地通过多波过程投票即可。但在现实中这样“完美”的系统并不存在,例如响应请求往往存在时延、高峰期可能临时假死且不响应请求、网络会发生中断、节点会发生故障,甚至存在恶意节点故意破坏系统,等等,因此在实际的分布式系统中要实现高效的共识机制是非常复杂的数学及编程问题。

我们可以将分布式集群中的节点错误问题归为两类:在一般情况下,把节点故障(不响应)的情况称为非拜占庭错误,把节点恶意响应的情况称为拜占庭错误(Byzantine Failures)。拜占庭将军问题的最初描述是:N个将军被分隔在不同的地方,希望通过某种协议达成某个命令的一致性(比如一起进攻或者一起后退),但其中一些叛徒会通过发送错误的消息阻挠忠诚的将军达成命令上的一致性。莱斯利·兰伯特证明在将军总数n超过叛徒总数m的3倍时,即满足n≥3m+1时,可以达成命令上的一致性,否则无法达成。简单解释如下:

假设只有3个将军:A、B、C,三人中有一个是叛徒。当A发出进攻命令时,B如果是叛徒,他便可能告诉C,他收到的是“撤退”的命令。这时C收到一个“进攻”的命令和一个“撤退”的命令,于是被信息所迷惑,无所适从。因此在只有三个将军的系统中,只要有一个是叛徒,拜占庭将军问题便不可解。

在一个由互不信任的各个组织(个人)所构成的分布式网络中,要获得最大的利益,就必须一起努力才能完成,如何达成一致的共识就变成了一个难题,因为根本不知道谁是可信任的,特别是在一个“商场如战场”的金钱帝国里。莱斯利·兰伯特提出了拜占庭将军问题,但真正第一次巧妙解决这个问题的人是中本聪,中本聪的解题思路如下。

(1)首先,如果运行系统中的每个“将军”都同时发出自己的提议,便会产生大量的通信,大大增加了达成一致性的困难,因此,中本聪巧妙地在比特币系统中加入了节点发送提议的成本,即通过挖矿方式的工作量证明机制(Proof of Work,PoW),使得在一段时间内只有一个节点可以发出提议。PoW机制同时巧妙地解决了集群中陌生节点之间的信任问题——一个努力工作的人要比游手好闲的人可靠得多,现实中的毕业证、五一劳动奖章等都属于工作量证明,它证明我们过去付出了多少努力。在拜占庭系统里加入工作量证明,其实就是简单粗暴地引入了一个条件:大家都别忙着发出提议,都来做个难题,看谁付出最大的努力去解决了这个难题,谁就有资格第一个发出提议并被大家认可。如果不同的将军先后解出了这个难题,那怎么办呢?只有最早解出难题的将军发出的提议才是有效的,因此,我们看到在比特币的Block里有一个时间戳字段。

(2)其次,用非对称加密技术将一个不可信的分布式网络变成了一个可信的网络。比特币网络中的每个节点都有一对公私钥证书,任何节点发出的消息都可以用自己的私钥签名,其他节点无法伪造这个签名,并可以用消息发送方的公钥验证这段消息的真伪并且确定消息发送方的真实身份。由此,一个不可信的分布式网络变成了一个可信的网络,所有参与者都可以在某件事上达成一致了。在比特币这个分布式网络里:

◎每个将军(节点)都有一份与其他将军实时同步的消息账本;

◎在账本里有每个将军的签名,都是可以验证身份的。如果有消息不一致,就可以知道是哪些将军的消息不一致;

◎尽管有消息不一致的现象发生,但只要超过一半的将军同意进攻(生成区块),则少数服从多数,达成共识。在比特币网络里,想要掌握控制权和改写交易规则,则必须掌握全网51%的计算力,面对比特币现在的网络规模,这已经很难做到了。

中本聪在比特币中其实是“绕过”了拜占庭算法的。原始的拜占庭算法效率很低,算法复杂度为指数级。1999年,Miguel Castro和Barbara Liskov提出了改进版的拜占庭算法PBFT(Practical Byzantine Fault Tolerance),将算法复杂度由原来的指数级降低到多项式级,使得拜占庭容错算法在实际的系统应用中变得可行,其中 Barbara Liskov就是提出著名的里氏替换原则(LSP)的人,为2008年图灵奖得主。在PBFT的论文中,作者使用这个算法实现了一个很厉害的具备拜占庭容错特性的网络文件系统(NFS),通过性能测试证明了该系统仅比无副本复制的标准NFS慢3%。Fabric在1.0版本之前曾实现过基于PBFT的共识算法,但在发布1.0版本时取消了这一算法,可能出于对算法复杂度与成熟度的考虑。

除了PoW、PBFT这两种经典的共识机制,还有以下几种常见的数字货币共识机制。

◎权益证明(PoS)。账本理应由持有最大经济利益的人去维护,是完全的资本主义形式——拥有的钱越多,拥有的权力就越大。以太坊下一代的共识机制Casper也属于PoS的一种,要求验证人将大部分保证金对共识结果进行下注,验证人必须猜测其他人会赌哪个块儿胜出,同时下注这个块儿,如果赌对了,就可以拿回保证金外加交易费用,也许还会得到一些新发的货币;如果下注没有迅速达成一致,就只能拿回部分保证金。

◎股份授权证明(DPoS)。引入了“受托人”的角色,所有持币者先选出受托人负责签署区块:选举过程类似于由股东会选举出董事会,代替股东会做出日常运营决策。在授权董事会后,决策会更有效率。

◎瑞波共识机制(Ripple Consensus)。使一组中心化的特殊节点列表达成共识,初始特殊节点列表就像一个俱乐部,要接纳一个新成员,则必须有51%的成员投票通过,由于该俱乐部从“中心化”开始,所以一直是中心化的系统。

◎基于传统的分布式一致性技术及数据验证机制。在成熟的分布式一致性算法(Paxos、Raft)的基础上实现秒级共识验证,但去中心化程度不如比特币,更适合多方参与的多中心商业模式。

在理解了区块链的基本概念以后,接下来快速体验本书的主角——Fabric的功能。Fabric是IBM开源并主导的区块链基础设施,或者说是区块链中间件,也是基于区块链3.0技术的企业联盟链的代表,采用了模块化的设计思路,因此其成员服务、共识算法、区块链存储等关键模块都能得到灵活扩展;此外,Fabric还使用了领先的基于Docker容器技术的智能合约。综合上述特性,Fabric可以作为领先的、企业级的、通用的开源区块链平台。