比特币的区块链架构主要围绕支持虚拟货币的实现,虽然它有一定的灵活性,但用来支撑虚拟货币以外的应用场景还显得非常局限。近年来,区块链逐渐引起IT业界的关注,并逐渐成为独立于比特币的一个平台架构,其重要性越来越受到重视。区块链2.0的概念也随之产生。其核心理念是把区块链作为一个可编程的分布式信用基础设施,支撑智能合约应用,以与过去比特币区块链作为一个虚拟货币支撑平台区别开来。具体说来就是,不仅仅把区块链作为一个去中心化的虚拟货币和支付平台,而是通过增加链上的扩展性功能,把区块链的技术范围扩展到支撑一个去中心化的市场,交易内容可以包括房产的契约、权益及债务凭证、知识产权,甚至汽车、艺术品等。

区块链2.0提供一套新的协议(区块链2.0协议)支撑新型的去中心化应用。如果用互联网协议来做类比,区块链1.0就相当于TCP/IP协议,而区块链2.0就相当于HTTP、SMTP和FTP等高级协议。甚至有把区块链1.0比做电话,而区块链2.0相当于智能电话的比喻。在比特币后,出现很多被称为区块链2.0的平台,其中,最具代表性的是以太坊平台。下面简单介绍一下以太坊架构。

以太坊的设计主要还是以比特币架构为基础。前面几章已经介绍了以太坊的基本架构,本章不再详细叙述,下面只对和比特币架构不同的几个主要方面做重点讨论。以太坊架构图3-9所示。

1.账户设计

比特币没有账户的概念。每个用户的余额都是从他们在区块链上的UTXO计算出来的。以太坊则有两种类型的账户:一种是外部所有账户(EOA),另一种是合约(Contract)账户。外部所有账户就是我们一般意义上的用户账户,它由私钥控制。合约是一种特殊的可编程账户,合约存在以太坊区块链上,它是代码(它的功能)和数据(它的状态)的集合。合约受代码控制并由外部所有账户激活。

以太坊的设计是将区块链作为一个通用的管理对象状态转换的去中心化平台,账户就是有状态的对象。外部所有账户的状态就是余额,而合约账户的状态可以是余额、代码执行情况,以及合约的存储。以太坊网络的状态就是所有账户的状态,该状态由每个区块的交易来更新,同时需在全网形成共识。用户和以太坊区块链的交互需要通过对账户的交易来实现。

每个以太坊的外部所有账户由一对密钥定义,一个是私钥,一个是公钥。区块链的EOA账户由它们的地址来做索引。取公钥的后20位作为地址,这和比特币的地址不一样。每个公私钥对被编码存放在一个密钥文件(Keyfile)中。密钥文件采用JSON格式,可以用文本编辑器打开来看。密钥文件的私钥都是用在建立账户时输入的口令来加密的。密钥文件存在以太坊节点的数据目录中的keystore子目录中。密钥文件需要经常备份,否则如果失掉密钥文件,账户里的以太币也就无法找回了。

合约账户可以执行图灵完备的计算任务,也可在合约账户之间传递消息,合约编译成以太坊虚拟机字节码(EthereumVirtual Machine Bytecode),并记录在区块链上。

外部所有账户可以通过发送交易到合约来实现对合同的调用。这需要提供几个参数,例如EOA的地址、合约的地址,以及数据。数据部分包括需要调用的合约里的方法(method)以及其传递的参数。这个需要用到Application Binary Interface(ABI)来作为传递数据的编码和解码的标准。关于ABI的详细信息可以参考以太坊wiki网页https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI 。

2.区块链设计

比特币采用Merkle树来将交易的哈希值按一定算法组成二叉树状结构 1 ,顶层节点的哈希值相当于整个交易清单的指纹,可以用来校验交易清单。中本聪采用Merkle树设计,也是为了轻量级节点能通过SPV(简化支付验证)方式来方便地校验交易。SPV不用下载整个交易清单,而是只需要区块报文头中交易清单顶层节点的哈希值,以及与自身节点相关的交易,然后可以通过向其他节点查询其他相邻交易,就可以完成对某个交易是否包含在区块链中某个区块的验证。 区块链的核心原理 http://www.868qkl.com/shenme/9.html 以太坊的区块链的每个区块不但保存着交易清单,还保存最新的状态。以太坊作为一个通用的区块链编程平台,引入了账户概念,由此它也带来更为复杂的校验和查询需求。例如要查询账户的余额或判断一个账户是否存在,光用比特币的Merkle树就满足不了要求。因此以太坊采用Merkle Patricia树来实现对交易和状态的校验和查询 [2] 。下面看看交易和状态面临的问题。

以太坊的状态包含一个键值表(key-value map),其中键是地址,值是账户里声明的变量,包括余额、随机数(nonce)、代码和账户的存储(存储也以一棵树的形式来组织)。与交易数据只能增不能改不一样,账户的状态经常被改变,其余额、随机数经常变。

另外,新的账户也经常被插入,键在存储里也被经常插入和删除。因此Merkle树不适合这种情况,而需要一种可以在插入、更新和删除操作后快速计算新的树根哈希值,而不需要重新计算整棵树的数据结构。同时,树的深度是有限的,即使在有攻击者试图通过故意发很多交易来尽量增加树的深度的情况下,不然一个攻击者可以通过操纵树的深度,以使得每个更新都变得非常慢,来对平台实施拒绝服务攻击。

还有一个要求是树的根哈希只是与树的数据有关,而与更新的顺序无关。不同的更新顺序或者甚至重新计算整个树的根哈希值都不会改变树的根哈希值。Patricia树是符合这些要求的数据结构。

简单来说,以太坊的账户的状态由键值表(Key-Value Map)来表示,在Patricia树里,键被编码成向下访问树的路径。在以太坊的Patricia里,每个节点有16个子节点,所以路径用十六进制来编码。例如,键“dog”的十六进制编码是6461567,所以要访问键“dog”所对应的值,就必须先从根节点开始,向下到第6个节点,然后再由该节点向下到第4个节点,以此类推,一直到最后。

在区块链的区块报文头中,不像比特币那样仅仅存放一个交易清单的Merkle树根哈希值,而是存放了3个根哈希值:一个是交易的Merkle根哈希值,另外一个是状态的根哈希值,还有一个是收据的根哈希值。 另外一个和比特币的不同是,以太坊的区块链中的每个区块保存区块链号和区块难度。

3.PoW机制

以太坊的PoW(工作量证明)算法叫Ethash算法(是一个经过修改的Dagger-Hashimoto算法),该算法主要寻找一个随机数作为输入,使得运算结果小于一个特定的难度门槛。PoW机制的前提是,不存在比逐个试更好的找到该随机数的方法,同时验证结果必须非常方便且成本小。由于哈希运算的结果是均匀分布(Uniform Distribution)的,所以可以保证,通常找到该随机数的时间取决于难度门槛。这样的话可以通过控制难度来控制在网络上找到一个新区块的时间。以太坊是通过动态调控难度来达到平均每15s在全网中找到一个新区块

的。每15s的“心跳”基本上是全网更新系统状态的节奏,并保证当攻击者的计算能力不超过全网的计算能力的一半时,攻击者无法改写交易记录或进行分叉(以便进行双花交易)

。这就是所谓的“51%”攻击。网络上的矿工的挖矿收入期望直接反映他们拥有的计算力,或者哈希速率在整个网络中的占比。

比特币的工作量证明机制依靠的仅仅是CPU计算难度问题,以太坊的Ethash工作量证明机制加入内存难度,使得它具有抵抗单凭哈希运算优化的ASIC挖矿机的属性。内存难度是通过

在算法设计中要求选择由随机数和区块报文头决定的一部分固定资源,这些资源一般是几个GB的数据,叫“有向无环图”(DAG)。每30000个区块后需要有一个全新的DAG。这相当

于一个125小时的窗口,或5.2天,称为一个epoch。这个图需要一段时间才能生成。因为DAG只和区块链深度相关,因此可以提前生成。如果没有现成的,以太坊的客户端需要等生成

了DAG后才能产生新的区块。这样的话在每个epoch转换的时候,如果客户端不预先生成DAG,网络就会出现大规模的延迟。当一个矿工节点第一次启动时,需要等DAG生成之后才能开

始挖矿。

以太坊的Go语言实现程序geth和C语言实现的挖矿程序ethminer都实现自动DAG生成,并在epoch转换的过程中维护两个DAG。以太坊的Ethash算法可以在较慢的CPU环境中进行哈希运

算,但在挖矿节点上可以通过增加内存和带宽来提升挖矿速度。对内存的高要求使得大型矿的矿主没有太大的比较性超线性收益;对带宽的高要求使得用堆超快的计算单元来共享存

储的方法并不能带来更好的收益。这样对矿池挖矿来说没有太多好处,因此以太坊从设计上希望避免出现像比特币那样的矿池算力集中化的问题。

类似比特币,以太坊的挖矿静态收益将随着时间推移而逐渐减少,目前静态收益是每挖到一个区块获得5以太币。未来矿工的收益将主要依靠发送交易的用户支付的“燃料”来获取

收益。以太坊的矿工奖励制度比比特币复杂。很多参考资料没有给出具体的奖励数额,有些甚至是错误的,例如对挖到“叔区块”的矿工奖励的描述,有些资料认为是(7/8)

×5=4.375以太币,其实这是不准确的。笔者参考了计算奖金的源代码,具体的奖金机制描述如下。

每当一个矿工挖到一个区块,他将获得5.0以太币(Ether)的静态收益,同时获得在区块上的“燃料”(gas),价值取决于当前的“燃料”价格。另外矿工也获得一个将“叔区块

”(uncle)包含进区块链的额外奖励,相当于每包含一个uncle区块将获得(1/32)×5以太币的收益。而产生“叔区块”的矿工将按下面的公式获得奖励:

挖到“叔区块”矿工奖励=(叔区块ID+8-当前区块ID)×5/8

例如,假设当前区块ID是1600,叔区块ID是1598,那么挖到叔区块1598的矿工将获得(6/8)×5,等于3.75以太币。如果叔区块的ID是1599,那么挖到叔区块1599的矿工将获得

(7/8)×5,也就是4.375以太币的奖励。

注意:所谓“叔区块”,是指符合难度条件,但区块里的交易不被确认的区块,或叫“废块”(Stale)。比如矿工A挖到一个符合难度规定的合规区块a,而几乎同时矿工B也挖到符

合标准的区块b,但由于网络延迟,区块b没有被确认,成了废块,而a成了网络共识的区块,被包括在区块链中。由于以太坊产生区块的速度比比特币产生区块的速度要快很多,因

此在网络繁忙的时候,相对于比特币系统更容易出现“废块”。在比特币系统中,生产废块的矿工只能自认倒霉,是没有奖励的。而在以太坊中,产生“叔区块”的矿工和将“叔区

块”包括在区块链上的矿工都能得到奖励。这样产生废块的算力也被包括进来,有效地增强了安全性,使得攻击者不容易追上一个带“叔区块”的主链。同时通过给“叔区块”奖励

,也避免出现像比特币那样计算力高度集中的矿池,因为矿池相对来说不像单个挖矿节点那样容易产生废块。严格说来,“叔区块”是在当前链接区块往前推最多6个的“祖先”废

块,每个区块最多能链接两个“叔区块”。

以太坊采用一个与比特币不同的算法叫GHOST(幽灵)来构建区块链。GHOST的全称是Greedy Heaviest Observed Subtree,中文直译是“贪婪最重观察子树”。严格来说,以太坊的

区块链不是一个链条,而像一棵树,它包含前面提到的“叔区块”。

在比特币系统中,矿工按一定的优先级把未确认的交易打包到新发现的区块上。交易的优先级按交易额和链龄(指UTXO存在的时间)来决定。交易额越大、链龄越高,优先级就越高

。交易费用是用户的输入和输出之差。如果输入和输出之差为零,随着交易发生的时间越来越久,其在交易池的优先级会逐渐升高。因此一般来说,即使付给矿工零交易费用的交易

都有机会被矿工包含在区块链上。当然个别矿工可以有自己的规则,可以拒绝零交易费的交易。而在以太坊平台,不提供“燃料”的交易不会被执行,也不会被包含在区块链上。以

太坊的交易费用按以下公式计算:

Total cost=gasUsed×gasPrice

其中gasUsed是执行该交易所消耗的燃料,燃料的价格由用户和矿工决定,一般来说在用户建一个交易的时候,可以提一个燃料价格。在以太坊发布的第一版本Frontier(前线)中

,以太坊客户端的默认燃料价格是0.05e12wei,大约是一亿分之五个以太币。矿工一般不会接受低于普遍价的燃料价格。

4.计算和图灵完备

以太坊作为通用的区块链平台,需要提供比比特币更强大的计算能力。前面说过,从安全角度出发,比特币的设计专门选择一个不具图灵完备性的脚本引擎,目前能通行的比特币脚

本指令也不多,但在虚拟货币的应用场景已经是绰绰有余了。而在以太坊上,一个和比特币非常大的不同点就是选择了图灵完备的计算环境——以太坊虚拟机(EVM)。这就意味着

在EVM上可以做所有的能想得到计算,包括无限循环。EVM指令包括一个JUMP的跳转指令,可让程序跳回前面的程序代码,也可以像条件判断语句那样做条件跳转,当满足一定条件时

将程序跳转到另一个地方执行。另外,一个合约可以调用其他合约,这提供了潜在的递归调用的功能。这就很自然地会导致一个问题:一个搞破坏的用户能否通过强制矿工或全节点

进入死循环而将他们基本关掉呢?这个其实也是一个“停机问题”,也就是说,通常没有任何办法去判定一个程序会否停机。以太坊怎么解决这个问题呢?它首先要求每个交易要给

出最大的计算步骤,交易的发起人要提供Gas作为交易费以供矿工把交易加进区块。如果实际运行超过了该最大计算步骤,计算将被终止,而交易费会归挖到区块的矿工所有。因此

以太坊采用经济的方法来保证以太坊平台的安全。

以太坊网络的每个节点都运行EVM并执行合约代码,因此以太坊就像一个并行运行的“世界电脑”,在所有的节点上同时进行账户的状态转换,并形成网络层面对所有账户状态的共

识。虽然这种P2P的运行方式并不是最高效的,但却是最有安全保障的,可以说,这部“世界电脑”永不停机。

5.EVM高级语言

比特币不提供高级语言的支持,以太坊则提供高级语言让用户编写智能合约。以太坊的高级语言最后会编译成在EVM中执行的EVM字节码(bytecode),部署在以太坊区块链上。以太

坊提供3种编程语言:Solidity、Serpent和LLL。

·Solidity类似JavaScript语言,是目前以太坊上最流行的智能合约编程语言。

·Serpent类似Python编程语言,它结合了低级语言的效率和易用的编程方式。Serpent用LLL语言来编译。

·LLL是Lisp Like Language的简称,顾名思义是一个像Lisp的语言。它有些像汇编语言,设计得非常简约,基本上就是在EVM上的一个微小的封装。

另外一个类似C的语言Mutan已经基本弃用,不再被维护。

6.以太坊P2P网络

(1)RLPx协议

以太坊网络节点间的通信采用DΞVp2p线上协议。节点间采用RPLx [3] 编码及认证的通信传输协议来传输消息包,即提供发送和接收消息的协议功能。节点可以自由地在任何TCP端

口发布和接受连接,默认的端口是30303。目前正式版的RLPx实现了以下功能:

·单一协议的UDP节点发现

·ECDSA签名的UDP

·加密握手/认证

·节点持久性

·加密/认证TCP

·TCP帧处理