1. Go 版本以太坊
1.1 以太坊的客户端
首先是以太坊技术社区的一些客户端的实现。以太坊技术协议本身是协议, 它包含了一些接口协议,规范或参数定义和内部具体的实现逻辑和流程等。基于这个技术协议,可以用各自语言实现一个以太坊 节点。以太坊的官方团队使用Go 语言实现了官方版本。 partiy 的实现是第二大的客户端。
1.2 以太坊的工具组
核心组件包括 Solidity,Web3.js。对于大多数的开发者来说,最关心 的是这两个组件。Swarm 一种外部存储的实现,希望拓展以太坊本身对于较大数据存放的问题,另外 IPFS 也是类似的功能实现。
1.3 以太坊公联网络拓扑
图3
图3 是以太坊网络的拓扑图。包含了各种不同 的客户端,他们之间相互组成了图中的网络。通过图3 就可以看到整个以太坊的公有链是非常开放的。
2. 为何选择DPOS机制
2.1 共识机制对比
DPOS 是一种共识机制,源自于石墨烯技术。简单对比一下以太坊的官方社区提供的 POW 机制。POW 是消耗你的 计算力来产块,它出块速度慢,确认慢。而 DPOS 是代理人的模式。现阶段实现的 DPOS 的机制 TPS 可达 1000,平均确认的时间在 1~ 3 秒钟。
2.2 DPOS机制的优势
我们实现了的 DPOS 机制的优势主要在于两个方面。第一是系统可靠性。在商业场景下,网络性能可控 ,在异常情况下能快速处理并恢复,同时对TPS/QPS,以及确认时间有一定的要求。 第二,共识机制是一种思想,以公有链为基础,可对外开放,任何人都可以参与,设立理事会和见证人角色,理事会管理区块链网络 ,见证人生产并验证区块,构成了一个良性的生态圈。
2.3 DPOS机制的理念
图4
图4表示,我们把一个公司和社区定义为三种角色,普通社区成员会通过自己手上的投票权选举理事会 。理事会通过自己的判断或认知来委任见证人。见证人根据自己实际情况维持整个链 的运作,这是 DPOS 机制的理念。
3. 拓展共识改造实践
3.1 共识框架引擎
3.1.1 改造共识层逻辑
图5
图5显示,如何实现 DPOS 的机制。以太坊的接口服务是提供客户端调用, 网络通讯是为 p2p 提供服务。 中间是一个共识层,是去做挖矿还是去做 DPOS 等其他共识,在这个上面都可以做一个扩展。最下面就是存储,区块的存储和状态都是通过这层做的。
3.1.2 官方实现引擎 :Ethash/Clique
图6
图6 显示 ,我们通过共识层来达到DPOS目的 。图上代码直接截取了以太坊的代码,这是一个接口引擎,提供了一些方法,看到这些 方法名字,很多都是在 Verify。Verify 对整个以太坊非常重要,区块链是不可篡改的,它需要对各种各样的异常情况做验证。 最上面的文字写到官方上有两个引擎,第一个是在公有链上 所使用的引擎,是 POW 机制的实现。第二个是 Clique,修改为 Dpos 很大的灵感来自于 Clique
引擎。
3.1.3 Seal 核心方法调用
图7
图7有点复杂,它能帮助我们了解以太坊内部的逻辑,首先通过 agent 和 worker 两个方法,去监听他们自己的对象,这两个 channel 一旦有数据会触发下面的事情。我们先看 worker channel,这个 channel 里面有新的 work出来会发生不同的事情。worker 是有工作的命令,一旦收到 这个 worker 的 channel,就要判断自己有没有能力挖矿,是否要做打包工作,触发这个 channel
之后,就调用引擎里面的方法,然后 seal 就去解决问题 。最终的题目是谁解出来,谁就有权利或者资格去挖这个 区块,这个 seal 方法就成功了,接着会产生一个Result 的对象,会触发下面的工作,一旦产生出块之后 就会有新的 worker 放在新的 channel 上去,如果我一直可以产块,就可以不断地做这个流程,这样就 构成了以太坊矿工节点里面核心挖矿的流程。此外 work channel 也有一个自己的触发机制。这个时候就需要依赖外部的节点
了,当其他节点产生区块的时候,会向我们这个节点广播,我就会同步出块,通知这个 channel 有新的工作区 去做。刚刚讲的是接口引擎里面最重要的 Seal 方法,我们可以通过修改这个方法,把原来需要解决题目的挖矿改成 不解决题目。
3.2 借鉴 Clique (POA) 的实现
图8
另一种共识机制是 Clique。以太坊公链使用 POW 机制 ,通过节点数量来防止别人恶意攻击。同时,以太坊提供一些测试链。 但测试链用 POW 发布之后有一个问题,测试链没有很多人同时参与维护,如果有人恶意搞它就非常简单,通过一些机器和算力很容易把 它搞瘫痪掉。为了能够让测试链比较稳定的运行,开发了另外一种共识机制取代了 POW,只有授权的机器才可以生产区块,这样 维护成本非常低,找一些机器授权给他就能够去维持这个网络。整个网络通过授权过的人去出块,节点之间通过投票的方式来授权或者剔除授权,这些额外的投票机制记录在区块头的
extra data 字段里面。
图8右边的图是测试链的产块逻辑。假定有 A、B、 C 三个节点的话,正常逻辑 A 完成之后到 B,B 完成之后到 C 。图中有一个竞争出块的概念,出 C 的时候不仅仅是 C 产块,A 也可以产块,为什么呢? 因为避免 C 节点有问题的时候,不能产块,A 节点的块也能提供出来加入到链里面。同时,如果 C 在当前 轮次里面,字段有一个 difficult 字段值就是 2,而这个
A 虽然可以产块,但不在这个轮次里面,所以 difficult 就是 1,尽可能保证 C 这个节点产生的块 被大多数的节点所接受,这是以太坊最常见的概念。
图9
图9 是轮次问题,也做了小的改动,这两个节点可以竞争出块,对于它 ,我们希望 C 节点的块尽量被接受,所以对 A 节点来说,如果不是这个轮次里面的话,可以让A节点出块的时间变得稍微延后一些。 比如在现在这个时间点应该可以出块的,但我要控制让它晚一些时候出块,这 样就避免会产生过多的分叉,虽然可以通过 difficult 值来决定谁是最长链,但过多的分叉会 导致链的状态越不稳定。
3.3 扩展区块头结构
3.3.1 增加见证人列表
图10
我们借鉴 Clique 的想法之后,会想怎么扩展我们的 DPOS 机制。首先我们会先修改数据结构, 对区块的 Header 的结构,定义新的字段,下面的 WitnessVotes 是我们自己定义的字段,我们希望 通过这个字段来为 DPOS 机制提供节点授权,我们扩展这个字段,把见证人的地址列出,按照字典做排序,这样能避免出块轮次发生变化的问题。以太坊对于字段扩展是非常容易做的,在 代码的框架里体统了框架的主体架构,我们只需要增加一些代码,就可以
很容易扩容它的 Header 了。
3.3.2 见证人列表生成规则
图11
图11是 witness 的列表。对以太坊来的验证机制而言,两个区块, 即便区块数据一样,但是区块的成产者不一样,也是不被接受的。为了达到这个目的,我们就需要约定好方案,对于见证人的列表来说,必须依托于 父节点。如果现在区块链上被确认的区块高度是 Block N,现在要产生 Block N+1 的区块,我们如何填充区块的见证人列表? 我们必须通过 Block N 的区块达成共识,当前的 Block N 上面的见证人列表是谁, 就填到下一个见证人列表当中去。对创始块来说,我们通过创世配置文件,把见证人列表直接写在创世块中。