区块链以太坊DApp开发实战(txt+pdf+epub+mobi电子书下载)


发布时间:2020-07-23 05:43:30

点击下载

作者:林冠宏

出版社:清华大学出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

区块链以太坊DApp开发实战

区块链以太坊DApp开发实战试读:

前言

出版这本书之前,我是一位已经撰写技术博文5年之久的程序员,技术栈范围包含但不限于安卓应用开发、后端开发以及现在所从业的区块链DApp开发。

曾经有不少出版社联系我出书,但限于对知识的敬畏和对出书的谨慎,都一一婉拒了。正式签约出版这本书是2018年10月中旬,那时我处于一个对区块链和以太坊知识非常热衷的阶段,当时的工作也正好是基于区块链做各种DApp的开发,比如具有代表性的钱包、中心化交易所和去中心化交易所应用。对区块链、以太坊的各个方面构建了一套完整的知识体系,所以在清华大学出版社的编辑联系我的时候,市面上关于以太坊DApp技术开发的书籍几乎为零,而理论性的书籍过多,在深思熟虑之后,便决定编写此书。

写书最怕的是误人子弟。后面正式编写的时候才发现,将整个以太坊的知识体系展开来讲的话,有很多的细节是自己之前还没有掌握的,比如:区块链浏览器上所看到的非ETH交易记录不能作为资产转移成功的依据等。编写此书的过程中,也遇到了一些疑惑点,通过借鉴优秀的博客文章、阅读源码和咨询业界一些技术大佬的意见,反复检查、检验整理编写入书内,对我自己来说也是一种提升,丰富并拓展了我的以太坊知识体系。我在这里衷心感谢他们并将会在书后列举出这些文章的链接和相关大佬的名字。

全书的内容关联性很强,篇幅适中,非必要的理论性内容几乎没有谈及。在术语阐述上,我尽力做到用通俗的语言去讲解,如果读者在阅读的过程中依然无法理解某一个知识点,欢迎通过我的联系方式直接询问,我会为你们一一解答。

虽然,笔者已尽最大努力避免书中内容出现错误,但由于水平所限,难免会有错误,如果读者在书中发现错误的结论,欢迎联系我进行勘误。我将十分感谢您!对于被纠正的内容,我将会在技术博客中公布。此外,技术交流方面可以加入QQ群,群号等联系信息参见本书的后记。林冠宏2019年3月第1章 区块链基础知识准备

本章我们将首先从区块链的基本概念入手,逐步介绍共识机制、共识算法、链的分叉等概念,以帮助读者建立有关区块链的知识体系,为后续的开发工作做好准备。1.1 认识区块链1.1.1 区块链的概念

我们一般意识形态中的链是铁链,由铁铸成,一环扣一环。区块链也可以这么理解,只不过它不是由铁铸成,而是由拥有一定数据结构的块连接而成,呈链状结构,这种结构就是链表。

区块抽象到计算机语言中就是一个对象、一个结构体、一个类,同样类中也可以定义属性、变量和方法,但区块里包括的内容可以自己来定义。比如,以太坊公链的区块结构,它有变量,我们就可以自己进行定义。以下是我们设置一个区块包括变量的例子。

上述的type Block struct表示定义一个区块,其中定义了变量Number、PreHash、Hash、Value、Create。

当链表中的每个数据个体是上述区块的时候就构成了一条区块链。区块是区块链每一环的实体。这是一种最简单的区块链。如图1-1所示,其中箭头的方向代表的是子块关联父块,也可以将箭头反过来,表示父块连接子块。图1-1 正常形态的链

由于链中的区块包含数据,例如上面的Value变量,因此我们能够在这个区块被打包到链中的时候向Value填充值,此后我们通过访问这个区块内部的数据可对它打包的数据进行读取,然后输出,展示给用户。

在上面的例子中,我们用来存储打包到区块中的数据变量只有一个Value,那么请想象一下,如果把Value换成一个数组或者更多变量,这个区块就会变得更复杂,它的功能也会跟着变得更多。

此外,链中的区块被规定是唯一的,即相同区块号的区块不能以同一个身份(以太坊中允许有区块号一样的不同含义块)在同一条链中出现两次,如果出现了,那么链会将其纠正过来。

下面是网上对区块链的定义解释:“区块链是分布式数据存储、点对点传输、共识机制、加密算法等计算机技术的新型应用模式。”

这个概念其实是一个广义的解释,笔者更趋向于把这个解释理解为区块链节点程序,而不是区块链,因为一个区块链的节点服务程序就包含了这个概念中的各个模块,实际上还有很多其他的模块。

一般来说,区块链公链包含但不限于下面的技术模块:(1)数据加密签名技术模块。(2)共识机制技术模块。(3)分布式数据存储技术模块。(4)点对点通信传输技术模块。(5)智能合约技术模块。(6)应用程序接口技术模块。

当我们把这些模块技术实现的代码整合到一个程序中时,它便是一个区块链应用,例如某一条公链。

那么是不是区块链应用一定要全部实现这些技术模块呢?不是的,你可以开发自己的区块链公链,哪怕是超级简单的雏形,只要是链状的区块存储应用,就可以称为区块链。请记住,任何一个复杂的区块链应用,例如知名的公链,都是在简单的模型上进行技术的添砖加瓦打造出来的。此外,区块链的各个技术模块所包含的知识点也是非常丰富的,可以说每一个知识点都属于一个领域。1.1.2 链的分类

区块链的链分类通常有3类,即公有链、私有链和联盟链。这3类链的主要区别是:(1)公有链的维护节点比较多,节点网络对所有人开放,任何人都可以进行特定的数据访问。(2)私有链是面向个人或某个组织的。(3)联盟链是多个组织团体的节点联合在一起维护的,对组织开放。

目前被广泛接受、认可、有价值的“代币”(Token)几乎都是基于公有链的。

不同种类的公有链之间要实现相互通信,比如比特币公链和以太坊公链进行BTC兑换ETH的交易,需要借助技术手段来实现,例如跨链通信技术。1.1.3 区块链能做什么

从区块链普遍的去中心化的特点来看,在节点网络中,如果某条公链的合法节点数目达到一定的数量级,那么我们可以认为当前公链的去中心化程度接近100%,这意味着链上的数据不会再被篡改了,于是我们所传递到链上被保存在区块中的数据会一直存在下去,真实而永久。

基于这个特点,我们可以将区块链应用到数据的溯源存储方面。除此之外,还可以根据区块链具体提供的功能进行各种应用。例如,以太坊公链,它是区块链,而且提供了智能合约这类具备图灵完备的功能模块,我们可以基于它来开发智能合约去中心化应用DApp,其中最为普遍的便是ERC20智能合约所对应的“代币”。

要理解区块链能做什么,可以从实际的区块链应用所具备的特点进行思考,从而得出答案。1.2 共识的作用

每条区块链的节点,例如以太坊节点,都拥有自己存储数据的地方,节点之间虽然会相互通信,但又彼此不依赖,这是因为互不信任。

在这种情况下,各个节点如何保证在互相通信的过程中维护数据的一致性,从而使链上相同区块号的区块只有一个呢?此时就诞生了区块链技术栈中的另一个知识点:共识,又称共识机制。

所谓共识,通俗来讲,就是我们大家对某种事物的理解达成一致的意思。比如说日常开会讨论问题,又比如判断一个动物是不是猫,我们肉眼看了后觉得像猫,其符合猫的特征,那么我们认为它就是猫。这就是共识,可见共识是一种规则。

继续上述会议的例子。参与会议的人,通过开会的方式达到解决问题的目的。对比区块链中参与挖矿的节点,节点中有矿工这么一种角色,它在代码中对应某一个功能模块。节点矿工通过某种共识方式(算法)来解决该节点的账本与其他节点的账本保持一致。账本保持一致的意思是:各个节点同步的区块的信息保持一致,以维护同一条区块链。

那么为什么需要共识呢?没有共识可不可以?当然不可以,这样会出现问题,假如生活中没有共识规则,那么一切都会乱套。区块链与此类似,没了共识规则,各个节点各干各的,会失去一致性,区块链也不会达成统一。

上述会议和区块链的对应关系如下:(1)参会的人=挖矿的矿工(2)开会=共识方式(算法)(3)讨论解决问题=让自己的账本跟其他节点的账本保持一致

你可能会对上面的内容产生一些疑问:(1)区块链节点和矿工是什么关系?(2)让节点账本保持一致,账本的内容是什么?(3)为什么需要共识算法去保持账本一致?

首先,我们来看一下区块链节点和矿工的关系。矿工是区块链节点中的一个角色,从编程的角度来看,就是程序中的一个功能模块。因此可见,矿工与区块链就是包含与被包含的关系。

其次,让节点账本保持一致,账本的内容是什么?账本的内容就是所有节点所维护的那条公链中的区块以及该区块的相关信息。要保持这条链不出差错,块与块之间必须正常相连。

最后,为什么需要共识算法来保持账本一致呢?因为区块会被节点中的一些功能模块生成,在众多节点且相同的时间流逝中,A节点有可能诞生一个区块1,B节点也有可能诞生一个区块1,这样它们诞生的区块号就发生重复了。在同一条链中,相同区块号的区块最终只能挑选一个串接到链中,这时取谁的好呢?此时就需要用共识算法这一规则来做出选择了。这个选择的大致形式可参考图1-2。图1-2 用共识算法选出胜出块1.3 常见的共识算法

目前在区块链中,使节点账本保持一致的共识算法常见的有如下几种:● PoW,代表者是比特币(BTC),区块链1.0。● PoS,代表者是以太坊(ETH),以太坊正在从PoW过渡到PoS,区块链2.0。● DPoS,代表者是柚子(EOS),区块链3.0。● PBFT拜占庭容错,联盟链中常用。

下面通俗地介绍前3种共识算法的概念及优缺点。1.3.1 PoW算法

PoW(Proof of Work,工作量证明)的字面意思是谁干的活多,谁的话语权就大,在一定层面上类似于现实生活中“多劳多得”的概念。

以比特币为例,比特币挖矿就是通过计算符合某一个比特币区块头的哈希散列值争夺记账权。这个过程需要通过大量的计算实现,简单理解就是挖矿者进行的计算量越大(工作量大),它尝试解答问题的次数也就变得越多,解出正确答案的概率自然越高,从而就有大概率获得记账权,即该矿工所挖出的区块被串接入主链。

下面对上述一段话所涉及的几个术语做一下解释。(1)区块头(Header):区块链中区块的头部。比如你有一个饭盒,饭盒的第一层类似动物头部,称之为头部;第一层放着米饭,米饭就是头部装载的东西。(2)哈希(Hash):数学中的散列函数(数学公式)。(3)哈希散列值:通过哈希函数得出的值。例如,有加法公式“1+2=3”,那么哈希公式hash(1,2)计算出来的结果即为哈希散列值。(4)区块头的哈希散列值:饭盒第一层装的是米饭,那么这个值就是区块头装的东西。(5)记账权,话语权:在大家都参与挖区块的情况下,谁挖出的区块是有效的,谁就有记账权或话语权。

在PoW共识算法下,当很多个节点都在挖矿时,每个节点都有可能挖出一个区块。比特币区块链定义了区块被挖出后,随之要被广播到其他节点中去,然后每个节点根据对应的验证方式对区块进行是否合法的验证操作,被确认合法的区块便会被并入主链中去。

对比现实生活,比如数学竞赛,参赛者相当于矿工,一道题目,谁先做出就公布计算过程和答案,不由裁判判断,由参赛者来一起验证;大家都认可后,宣布该题目结束,解题者及相关信息被记录到纸质册子或数据库,之后继续下一道题。

回到比特币挖矿中,其实就是计算出正确的哈希散列值,一旦计算出来,就生成新区块,并将生成的区块信息以广播的形式告诉其他节点。其他节点收到广播信息后,停下手上的计算工作,开始验证该区块的信息。若信息有效,则当前最新区块被节点承认,各个节点开始挖下一个区块;若信息无效,则各个节点继续自己的计算工作。这里的难题在于哈希散列值的计算随着比特币中难度系数(一个能增加计算难度的变量数字)的增大会越来越困难,导致计算需要耗费大量的电力资源,工作量巨大。

此外,成功挖出有效区块的矿工(节点)将会获得奖励。在不同的区块链体系中,奖励的东西不同,比特币区块链体系中的奖励是比特币。

图1-3是节点使用PoW共识算法共同承认胜出块,并将胜出块串接上链的模型图。图1-3 用PoW共识算法选出胜出块

PoW共识算法具有下面的优缺点:(1)优点● 机制设计独特。例如挖矿难度系数自动调整、区块奖励逐步减半等,这些因素都是基于经济学原理的,能吸引和鼓励更多的节点参与挖矿。● 早参与早获利。越早参与的人获得的越多。在初始阶段,会促使加密货币迅速发展,节点网络迅速扩大。● 通过“挖矿”的方式发行币,把代币分散给个人,实现了相对公平。(2)缺点● 算力是计算机硬件(CPU、GPU等)提供的,要耗费电力,是对能源的直接消耗,与人类追求节能、清洁、环保的理念相悖。● PoW机制发展到今天,算力的提供已经不再是单纯的CPU了,而是逐步发展到GPU、FPGA乃至ASIC矿机。用户也从个人挖矿发展到大的矿池、矿场,算力集中越来越明显。这与去中心化的思想背道而驰。● 按照目前的挖矿速度,随着难度越来越大,当挖矿的成本高于挖矿收益时,人们挖矿的积极性会降低,造成大量算力减少。● 基于PoW节点网络的安全性令人堪忧。● 大于51%算力的攻击。在“PoW共识机制的51%算力攻击”一节中会详细介绍。

问题解答:(1)如果遇到同时解出问题的情况怎么办?

确实存在会同时解出的情况,即使我们把区块生成时间的时间戳定义到秒或者毫秒级别,依然会有同时间挖到矿的情况。对于这种情况,PoW共识算法无能为力。具体的解决方法会在“链的分叉”一节中谈到。(2)为什么是51%算力,而不是50.1%?

这个问题将会在“PoW共识机制的51%算力攻击”一节中进行解答。1.3.2 PoS算法

PoS(Proof of Stake,股权证明)是由点点币(PPCoin)首先应用的。该算法没有挖矿过程,而是在创世区块内写明股权分配比例,之后通过转让、交易的方式,也就是我们说的IPO(Initial Public Offerings)公开募股方式,逐渐分散到用户钱包地址中去,并通过“利息”的方式新增货币,实现对节点地址的奖励。

PoS的意思是股份制。也就是说,谁的股份多,谁的话语权就大,这和现实生活中股份制公司的股东差不多。但是,在区块链的应用中,我们不可能真实地给链中的节点分配股份,取而代之的是另外一些东西,例如代币,让这些东西来充当股份,再将这些东西分配给链中的各节点。下面我们通过示例来阐述这个概念。

例如,在虚拟货币的应用中,我们可以把持币量的多少看作拥有股权、股份的多少,假设某区块链公链使用了最基础的还没进行变种开发的PoS共识机制,以节点所拥有的XXX代币的数量来衡量这个节点拥有的股份是多少。假设共有3个节点,A、B和C,其中A节点拥有10000个XXX代币,而B、C节点分别有1000个、2000个,那么在这个区块链网络中A节点产生的区块是最有可能被选中的,它的话语权是比较大的。

再例如,假设某条非虚拟货币相关的与实体业结合的公有链,汽车链,我们可以把每一位车主所拥有的车辆数目和他的车价值多少钱来分配股份(比如规定一个公式:车数×车价值=股份的多少)。

可见,在PoS中,股份只是一个衡量话语权的概念。我们可以在自己的PoS应用中进行更加复杂的实现,比如使用多个变量参与到股份值的计算中。

PoS共识算法以拥有某样东西的数量来衡量话语权的多少,只要节点拥有这类东西,哪怕只拥有一个,也是有话语权的,即使这种话语权很小。

在PoS中,块是已经铸造好的。PoW有挖矿的概念,而PoS没有。在BTC比特币公链中,可以挖矿;而在没有使用PoS共识算法的公链节点中,就没有挖矿这一回事。当然,如果将挖矿的概念进行其他拓展,则另当别论。

PoS共识算法具有下面的优缺点:(1)优点● 缩短了共识达成的时间,链中共识块的速度更快。● 不再需要大量消耗能源挖矿,节能。● 作弊得不偿失。如果一名持有多于50%以上股权的人(节点)作弊,相当于他坑了自己,因为他是拥有股权最多的人,作弊导致的结果往往是拥有股权越多的人损失越多。(2)缺点● 攻击成本低,只要节点有物品数量,例如代币数量,就能发起脏数据的区块攻击。● 初始的代币分配是通过IPO方式发行的,这就导致“少数人”(通常是开发者)获得了大量成本极低的加密货币,在利益面前,很难保证这些人不会大量抛售。● 拥有代币数量大的节点获得记账权的概率会更大,使得网络共识受少数富裕账户支配,从而失去公正性。1.3.3 DPoS算法

PoW和PoS虽然都能在一定程度上有效地解决记账行为的一致性共识问题,也各有各的优缺点,但是现有的比特币PoW机制纯粹依赖算力,导致专业从事挖矿的矿工群体似乎已和比特币社区完全分隔,某些矿池的巨大算力俨然成为另一个中心,这与比特币的去中心化思想相冲突。PoS机制虽然考虑到了PoW的不足,但依据IPO的方式发行代币数量,导致少部分账户代币量巨大,权力也很大,有可能支配记账权。DPoS(Delegated Proof of Stake,股份授权证明机制)共识算法的出现就是为了解决PoW和PoS的不足。

DPoS引入了“见证者节点”这个概念。见证者节点可以生成区块。注意,这里有权限生成区块的是见证者节点,而不是持股节点。

下面我们主要以EOS区块链为例介绍DPoS算法。

持有EOS代币的节点为持股节点,但不一定是见证者节点。见证者节点由持股节点投票选举产生。DPoS的选举方式如下:每一个持有股份的节点都可以投票选举见证者节点,得到总同意票数中的前N位候选者可以当选为见证者节点。这个N值需满足:至少一半的参与投票者相信N已经充分地去中心化(至少有一半参与投票的持股节点数认为,当达到了N位见证者的时候,这条区块链已经充分地去中心化了),且最好是奇数。请注意,最好是奇数的原因会在分叉一节中进行说明。

见证者节点的候选名单每个维护周期更新一次,见证者节点们被选出之后,会进行随机排列,每个见证者节点按顺序有一定的权限时间生成区块,若见证人在给定的时间片不能生成区块,区块生成权限将交给下一个时间片对应的见证人。DPoS的这种设计使得区块的生成更为快速,也更加节能。这里“一定的权限时间”不受算法硬性限制。此外,见证者节点的排序是根据一定算法随机进行的。

DPoS共识算法具有下面的优缺点:(1)优点● 能耗更低。DPoS机制将节点数量进一步减少到N个,在保证网络安全的前提下,整个网络的能耗进一步降低,网络运行成本最低。● 更加去中心化,选举的N值必须充分体现中心化。● 避免了PoS的少部分账户代币量巨大导致权力太大的问题,话语权在被选举出的N个节点中。● 更快的确认速度,由见证者节点进行确认,而不是所有的持股节点。(2)缺点● 投票的积极性并不高。绝大多数持股节点未参与投票。因为投票需要时间、精力等。● 选举固定数量的见证人作为记账候选人有可能不适合完全去中心化的场景,在网络节点很少的场景,选举的见证人的代表性也不强。● 对于坏节点的处理存在诸多困难。社区选举不能及时有效地阻止一些破坏节点的出现,给节点网络造成安全隐患。

图1-4所示是DPoS共识算法选举的大致模型图。图1-4 用DPoS共识算法选出胜出块

目前,EOS的超级节点有21个,也就是说N=21,但是拥有EOS代币能投票的节点却有很多,那么为什么N这么小呢?原因是这样的:虽然N代表的是节点们认同的一个能够代表已经足够去中心化的值,但是N可以取23,也可以取25,或者更大,这些数看起来都足够去中心化了,事实上在EOS公链的发展过程中,N值渐渐地趋向于既要足够去中心化又要让性能跟得上,即处于中间值,以达到平衡性能的同时又满足去中心化。提示EOS的投票事实上还是一种抵押。投票的EOS会被抵押成资源,例如CPU和网络资源,但是抵押的EOS也可以被换回来。把抵押的资源换回EOS通常需要3天时间。1.3.4 共识算法的编码尝试

本节尝试使用伪代码来实现3种共识算法,之所以使用伪代码是为了使读者更容易理解。1. 实现PoW共识算法

首先,区块链中的各个节点会互相通信以广播新生成的区块。这里既可以用“生成”一词来描述,也可以使用“挖出”一词来描述,表达的意思是一样的,都是指区块的产生。

此时,我们需要使用一个候选区块数组来保存每一个节点广播过来的和自己当前节点生成的区块对象,以及一个全局的区块数组来表示当前公链的区块。区块数组的定义如下:

假设Block结构体内的数据类型如下所示:

然后我们需要一个难度系数的变量,例如difficulty,用来控制PoW算法的难度。这个数不一定越大就代表越难,只需要体现出PoW算法所描述的工作量难度即可。假设它是整型数,数值越大,计算难度就越大,那么此时difficulty系数会处于被随时调节的状态中。在区块链的设计中,例如比特币BTC的难度系数就有其动态调节的算法。

这里,我们假设难度系数difficulty=1。

有了难度系数后,还需要一个专门用来根据difficulty校验区块哈希值的函数。我们现在需要假设一种难度的验证算法,假设用哈希值前缀0(值0x后的0)的个数来和difficulty做比较,如果哈希值包含这些前缀0,那么校验通过。请注意,这是一种很简单的验证算法,且个数很有限,而在比特币公链中,则要复杂得多。

现在假设节点启动了一个子协程,一个用来生成区块的方法,并添加到候选区块数组中去,等待校验。下面的这个方法(函数)是用来生成新区块的:

假设节点启动了一个子协程且在不断地计算候选区块数组中区块的哈希值,所计算出的哈希值满足难度系数difficulty的检验。

在各个节点确认的过程中,如果达到了所规定的节点数量,那么我们就判断该区块胜出,最终被公链接纳。

最后解答一下伪代码中留下的疑问——为什么还要进行一次校验才广播块呢?因为难度系数difficulty是动态改变的,且候选块数组中的difficulty不一定就是我们当前的节点所生产的,即使是当前节点生产的,也有可能在生成的时候难度系数已经被出块了,所以在最后广播的时候还需要根据最新的difficulty难度系数再做一次校验。2. 实现PoS共识算法

相对于PoW,由于PoS共识算法没有“挖矿”的概念,且它不是靠计算工作量来进行共识的,体现在代码上也会是另外一种情形。

首先我们依然需要定义一个候选区块数组来保存每一个节点广播过来的和自己当前节点生成的区块对象: candidateBlocks []Blocks //候选区块数组

每个区块结构体有一个变量,用来记录生成这个区块的节点地址。这个变量在PoW的伪代码实现中并没发挥作用,但是在PoS中却很重要。同样地,和上述PoW一样,我们定义如下的区块结构体:

其中,NodeAddress变量用来记录区块的节点地址。

其次,需要有一个子协程,专门负责遍历候选区块数组,并根据区块的节点地址nodeAddress获取节点所拥有的代币数量,然后分配股权。

接下来,从stakeRecord中选出一个竞选胜利者。这个概率和上面的coinNum有关,coinNum越大就越有机会。为什么呢?因为它的统计方式是用coinNum作为循环界限,然后对应添加coinNum次的nodeAddress,所以coinNum越大,这个nodeAddress就被添加得越多,后面节点能被选上出块的概率也就越大。

这里还要解答一个疑点,为什么已经包含了的就不再重复添加,因为当前的候选区块数组candidateBlocks中可能含有同一个节点中的多个区块,而每一个节点中的股权只需要统计一次,即coinNum只需要循环一次即可。如果是多次循环,就会造成不公平,因为会造成多次添加。

在股权被分配好后,接下来准备选出节点胜利者。选择的方式也是使用算法,在这个例子中我们依然采取最简单的随机数的形式进行选择。注意,切勿被这样的方式限制了思维,这个选择算法是可以自定义的,因而可以是更加复杂的算法。 index := randInt() // 得出一个整型随机数 winner := stakeRecord[index] // 取出胜利者节点的地址

在最后的步骤中,就能根据这个winner去所有候选区块中选出节点地址和它一样的区块,这个区块就是胜利区块,将会被广播出去。

以上是一个很简单的PoS算法机制的代码实现,仅单纯地根据持币数量来进行股权分配。事实上,事情往往是比较复杂的。设想股权的分配不仅只和代币数量有关,例如以太坊设想的PoS共识算法的实现中加入了币龄,情况又会如何呢?这时在候选成功后,以太坊会扣除币龄。作为开发者应当理解PoS的精髓——其算法的实现往往会衍生出各种各样的变种,只有了解了这一点,才能在开发自己的公有链时随心而行。3. 实现DPoS共识算法

DPoS的伪代码实现可以理解为PoS的升级版,之前例子中相同的数据结构体的定义这里不再重复。

首先定义好见证者节点的结构体:

然后我们用一个由各个见证者节点组成的数组代表这一批见证者节点,往后的随机排序操作也将会在这个数组中进行。 var WitnessList []WitnessNode // 见证者节点

现在我们需要准备一个专门用来对WitnessList进行随机排序的方法,这个方法须依赖某种算法对WitnessList进行排序,具体算法可以自定义,但要根据不同的业务需求而定。

下面我们依然以一个最简单的随机数排序为例。

上面NeedRestVotes()的作用是判断是否需要重新投票选出见证者节点,对应DPoS算法描述中的每过一个周期就开始重新排名,在这个阶段还需要进行节点的剔除,例如剔除一些坏节点。

这里以检查坏节点为例,因为坏节点的检查时刻在进行,所以我们可以用一个子协程(Go语言中,协程是一种轻量级线程,为了更加贴切,下面的Go代码中统称线程为协程)来专门检查坏节点。

同时,还要不断地检测是否有新的见证者节点被投票选出,是的话,就要添加这个节点的信息进入到见证者节点数组中。同时要对见证者节点总数的数量进行N值限制。

最后我们使用出块函数从WitnessList见证者列表中从上到下逐个找出出块节点,进行出块,并检测当前轮到的节点是否出块超时,超时就轮到下一个,以此类推,对应DPoS共识算法的伪代码如下:

在广播块出去后,其他见证者接收到了广播,会对这个区块进行签名见证,当达到了某个我们所设定的认为见证已经足够了的值时,那么这个区块就被确认了。

从上述伪代码发现,传统的DPoS算法直接应用的时候存在如下问题:每个见证者节点都是循环着使用别的节点信息去生成块,而不是使用自己节点的信息。假设见证者节点A、B、C在一轮的出块顺序中,节点C排在第一位,且在节点A和B中使用节点C所生成的区块都没有出差错,那么节点C就会在节点A和B中都生成一次,由于时间戳不一样,导致该块的哈希不确定,但最终只能有一个块被选上,这样就导致了算力浪费。当然,也有可能不止节点A和B,还可能有更多的节点都使用节点C生成了区块,那么结果就是更多的认证者节点生成了一个节点的块,去广播。

要解决上述问题,可以使用EOS的做法:EOS通过见证者节点信息注册,使得每个节点都知道所有见证者节点的信息,同时被注册的节点都必须是满足投票条件的。

当每个见证节点都有了所有见证者节点的信息后,在每一次的最终块出现后,都会使用特定的算法对节点列表进行排序。如此,当需要出块的时候,节点会根据区块链中最后一个区块的时间来参与到某些计算中,得出当前应该出块的见证者节点在列表中的下标,然后判断这个节点是不是自己。不是的话,就会让自己延迟(delay)一定的时间,然后重复上面的步骤,这个延迟的时间就是DPoS中的出块超时,然后会自动轮到下一个见证者节点。如果是自己就出块,出块后就广播出去,等到2/3的见证者节点都签名确认了,那么这个块就是最终有效的。所以,EOS中的DPoS并不是传统示范代码中的那样,一个节点循环着生成含有别的节点的信息的块。

EOS的要点是,每个见证者节点的自身代码对所有见证者节点的排序是不一样的,各节点存在同时出块的可能,但其提供了2/3见证者节点都签名确认这一环节,即谁最快被2/3的见证者节点确认,谁才是最终有效的,解决了多个节点同时生成一个节点块的问题。1.4 链的分叉

上一节我们介绍了3种常见的共识算法:PoW、PoS、DPoS。虽然它们都让区块链中的各个节点在一定程度上做到了共识,但是也会产生不可避免的问题——链的分叉。本节我们来认识一下什么是链的分叉。

我们知道,区块链中的每一个区块在节点中被生成后都会通过P2P网络广播到其他节点中去,这些节点都是同一类节点,它们组成一类节点的节点网络。例如,比特币公链的节点就是比特币公链的,以太坊就是以太坊的,而不能是比特币公链的节点广播区块到以太坊的节点中去。

广播后的区块在到达了其他节点后,其他节点要对该区块进行操作,例如进行签名操作。然后,各个节点在广播给它的一批又一批的区块和它自己所产生的区块中做出抉择,即选出一个获胜的区块。

然而,共识算法仍然无法保证不出现确认冲突的问题,例如比特币中的PoW共识算法依赖谁算出合法哈希且谁算得快来抉择最终选谁。事实上,即使我们产生区块的时间戳精确到毫秒级,依然会出现同时算出哈希值的多个节点(至少有两个节点),例如节点A和节点B同时算出了合法的哈希值,产生了区块1,广播出去了。节点C也陆续收到了节点A和节点B的区块1,但是节点C首先收到的是节点A的区块1,此时虽然节点B的区块1也合法,但是也不采纳了。同时,节点D也收到了节点A和节点B的区块1,但是节点D先收到节点B的区块1,为什么?因为节点D的网络路由距离节点B的网络近,离节点A的网络远,那么节点D就会先采纳节点B的区块1而不采纳节点A的区块1。此时,链就分叉了,如图1-5所示。图1-5 节点广播块时路由距离的影响

我们把这个例子定为情况①。这是一个很简单的分叉模型。

链的分叉主要有以下两种情况:(1)硬分叉。一旦出现,最后的结果是一分为二,专业的说法是:旧节点无法认可新节点产生的区块,称为硬分叉。(2)软分叉。一旦出现,最后的结果是能掰正的,专业的说法是:旧节点能够认可新节点产生的区块,称为软分叉。1.4.1 软分叉

情况①就是软分叉的一种。当有两个或多个节点同时挖出了同区块号码的一个区块,然后它们同时广播信息出去,假设一个是节点A,而另一个是节点B,那么距离节点A比较近的节点,还没收到其他节点的消息就先收到了节点A的信息,并开始确认节点A所挖出的这个区块的信息,随后把节点A挖出的这个区块加入自己所在的公链中;同理,距离节点B比较近的节点也会先处理节点B挖出的区块信息,并把节点B挖出的这个区块加入自己所在的公链中,如图1-6所示。图1-6 节点网络

情况①的链分叉是各个节点在使用了同样的共识算法下导致的分叉,如图1-7所示。图1-7 链的分叉

出现这种情况,矿工是比较容易自我纠正的。由于节点网络的整体解题能力和矿工的数量成正比,因此链的增长速度也是不一样的,在一段时间之后,总有一条链的长度会超过另一条。当矿工发现全网有一条更长的链时,他就会抛弃当前的链,把新的更长的链复制过来,在这条链的基础上继续挖矿。所有矿工都这样操作,这条链就成为了主链,分叉出来的那个链便会被抛弃掉。但是,并不是所有的分叉都能被自动纠正,这点请注意,具体会在后面的章节中进行说明。

这种软分叉的自我纠正机制也是区块链的一个重要特点,就是最优链的选择。注意,不同的公链,它们的最优链选择算法并不一样。常见的选择机制有:(1)最长链机制。整条区块链以最长链为主,且各个节点根据长链不断同步,目前比特币公链采用的就是这种机制。(2)其他链选择机制。例如,以太坊的“Ghost协议”机制,将会在“以太坊Ghost协议”一节中进行详细介绍。

现在我们可以解答在“DPoS算法”一节中的问题——为什么DPoS共识算法下的N必须取奇数的原因:取奇数是为了避免在DPoS共识算法下出现链的分叉。因为在DPoS中,区块在广播后必须被见证者节点签名认证,在达到2/3数目的见证者节点签名后就宣布该块胜出。在奇数的情况下,是永远不可能出现对半的情况的,例如10个节点签名了区块A、另外10个签名了区块B。所以,奇数的见证者节点数,即N,很好地避免了链的分叉问题。

注意,链的分叉在不同的共识算法中对应着不同的解决策略。

图1-8所示为链分叉后最长链自我纠正机制的模型图。图1-8 最长链自我纠正机制

软分叉除了上面的情况①之外,还有另外一种情况,因共识规则改变,旧节点能够识别新节点产生的区块,但旧的区块不能被新节点接受,这种情况又分为下面的两种形式:● 新节点全网算力大于等于51%。● 新节点全网算力小于50%。

这种软分叉不一定能由节点自我纠正,解决办法是必须依赖人力升级节点到同一版本。(1)当新节点的全网算力大于等于51%时,无论旧节点升级不升级,最长的链最终都是由全部新节点生成的区块所组成的链,而且这条最长链都是新旧节点双方认为合法的一条,原因参考上面讲解的最长链复制机制:● 旧的能接收新的,在分叉点之后的区块掺杂着:● ► 旧节点的区块。● ► 新节点的区块。● 新的不能接收旧的,但最终新的总比旧的长。(2)当新节点的全网算力小于50%时,最终不能通过短的复制长的达到统一,结果是硬分叉。原因如下:● 旧节点比新节点的链要长。● 新的总是不能接受旧的,不会去复制一条含有自己不能接受的区块的链。1.4.2 硬分叉

如果区块链软件的共识规则被改变,并且这种规则改变无法向前兼容,旧节点无法认可新节点产生的区块,且旧节点偏偏就是不进行软件层面的升级,那么该分叉将导致链一分为二。

分叉点后的链互不影响,节点在“站队不同派别”后也不会再互相广播区块信息。新节点和旧节点都开始在不同的区块链上运行(挖矿、交易、验证等)。

举个简单的例子,如果节点版本1.0所接收的区块结构字段是10个,1年后发布节点2.0版本,2.0兼容1.0,但是1.0的不能接受2.0版本中多出的字段,即出现了硬分叉。

硬分叉的过程如下:(1)开发者发布新的节点代码,新的节点代码改变了区块链的共识规则且不被旧的兼容,于是节点程序出现了分叉(Software Fork)。(2)区块链网络部分节点开始运行新的节点代码,在新规则下产生的交易与区块将被旧节点拒绝,旧节点开始短暂地断开与这些发送被自己拒绝的交易与区块新节点的连接,于是整个区块链网络出现了分叉(Network Fork)。(3)新节点的矿工开始基于新规则挖矿,旧节点的矿工依然用旧的规则,不同的矿工算力导致出现分叉(Mining Fork)。

最终,整个区块链出现了分叉(Chain Fork)。

实例:“2017年8月1号,Bitcoin Cash(BCH)区块链成功地在区块高度478559与主链分离。这一新的加密货币默认区块大小为8MB,并且可以实现区块容量的动态调整。由于旧节点只认可小于1MB的区块,所以运行BCH客户端节点产生的区块无法向前兼容,将被旧节点拒绝,最后运行不同客户端的矿工将会长期运行在两条不同的区块链上(BTC和BCH)。”1.4.3 常见的分叉情况

本节我们从正常的区块的生成流程开始,使用DPoS共识算法模式,分析可能会出现分叉的各种情况。

在正常操作模式下,区块生产者每3秒钟轮流生成一个区块(见图1-9)。假设没有节点错过自己的轮次,后续便进入到选出胜出区块的步骤。注意,区块生产者在被调度轮次之外的任何时间段出块都是无效的。图1-9 正常形态的链

下面假定节点网络中共有5个节点,分别是A、B、C、A1、B1。1. 少数节点分叉

这是最常见最简单的一种软分叉,是由不超过节点总数1/3的恶意节点创建或因区块确认时间差导致的分叉现象。在这种情况下,假设少数节点分叉每6秒只能产生2个块,而多数节点分叉每6秒可以产生3个块(因为多数节点在同步速度上是比单节点向别的节点确认它的块的时间要短),这样诚实的2/3多数节点维护的链将永远比分叉的链更长。

如图1-10所示,每个块中的字母代表是哪个节点产生的。图1-10 分叉链含有较少的块2. 网络分片化分叉

当节点网络中部分节点由于网络波动或其他原因导致自己与全网节点的网络断开了连接,即会出现网络分片化分叉,如图1-11所示。图1-11 Node节点网络部分节点掉线

节点A1和节点B1断开了与主网的连接,此时它们的块生产的部分代码依然还在运行着,这样生成的块就只能归纳到本地所维护的公链区块数组中,节点网络被分成了3部分。请注意,无论如何分叉,根据链选择算法,其中总会有一条最优链,所以最终依然只有一条主链。如图1-12所示,每个块中的字母代表是哪个节点产生的。图1-12 多条分叉链

如果断线的节点网络恢复后,重新连接上了主网,那么它会自动进行最优链的复制,最终的结果也是只有一条最优链。

如果分叉节点永远地脱离了主网,结果就会造成硬分叉。如果脱离出的节点数目不多,那么这些脱离的节点就会变成私有节点或组成一个联盟链网络。3. 多数节点舞弊分叉

所谓舞弊,就是我们所理解的作弊的意思。节点作弊是指节点不遵循共识规则或做了一些非法操作,例如尝试修改块。这种情况下所导致的链分叉称为舞弊分叉。这类分叉和网络分片化的模型图很类似,不同点在于,舞弊是节点们都还在同一个主网中产生的。

因此,舞弊导致的分叉不会太久,最终还是会被诚实节点的最优链纠正过来。假设节点A1和节点B1是舞弊者,A、B、C是遵守规则的节点,其模型图如图1-13所示。图1-13 节点模型图

以上是目前常见的链分叉情况。实际中还有很多其他复杂的情况,但是无论何种情形,只要不是硬分叉,最终都是可以被纠正的。纠正的主要手段有以下两种:(1)最优链复制同步。(2)人工通过技术手段纠正。1.4.4 PoW共识机制的51%算力攻击

51%算力攻击目前仅在“PoW”共识机制中存在,因为“PoW”共识机制依赖算力计算获胜,也就是谁算得快,谁的胜率就高。在使用了“PoW”共识机制的区块链网络中,我们称参与计算哈希的所有计算机资源为算力,那么全网络的算力就是100%,当超过51%的算力掌握在同一阵营中时,这个阵营的计算哈希胜出的概率将会大幅提高。

为什么是51%?50.1%不行吗?当然也是可以的,之所以取51%是为了取一个最接近50%,且比50%大的整数百分比,这样当算力值达到51%后的效果将会比50.1%的计算效果更明显。举个例子,如果诚实节点的算力值是50.1%,那么坏节点的算力值就是49.9%。两者的差距不算太大,这样容易导致最终的区块竞争你来我往、长期不分上下。

如果算力资源分散,不是高度集中的,那么整个区块链网络是可信的。然而,当算力资源集中于某一阵营的时候,算力的拥有者就能使用算力资源去逆转区块,导致区块链分叉严重,如下面的例子。

假设图1-14是一条区块链目前的状态。一个攻击者想要逆转区块8中的一笔交易,他就会从区块7后面引入一个分叉来使区块8变得无效,在分叉块中设置给某个地址几百或者几千个BTC。不过,由于比特币公链的最长链规则的限制,所有的诚实节点都会遵循最长链规则,将新产生出来的区块链接在最长链的尾部,从而避免攻击者得逞。图1-14 某条区块链的状态

当系统出块率比较低且块大小较小时,网络延迟相对于出块时间来讲是比较小的,这样诚实的节点所产生的区块基本上就是顺序的。只要诚实节点的总算力超过50%,攻击者就不能够使它们自己产生的链成为最长链。然而,当诚实节点的总算力不及坏节点的算力时,即坏节点算力总和超过了51%,最长链机制将会被坏节点利用,因为此时坏节点的出块速度整体比诚实节点快,获胜率高,这样坏节点产生的区块将会形成最长链。

此外,如果出块率很高,会使得区块产生的时间和区块在网络上传播的延迟相对变得较小,这样一个新块在产生以后还来不及传播到全网就会有其他的节点产生别的新块,互相竞争剧烈,导致链上分叉情况严重。虽然最终只会有一条最长链,但是出块率越高,块大小越大,分叉的情况就会越严重,最终区块链就会发展成有很多分叉的样子,如图1-15所示。

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载