想要了解以太坊?
本白皮书发布于 2014 年,即以太坊上线之前。经过 10 多年的发展、重大升级和生态系统的增长,最初的白皮书已无法反映以太坊如今的样貌。
虽然已经过去了好几年,但我们仍然保留了下面的原始论文,因为它仍然是一个有用的参考资料,并且准确地表达了以太坊及其愿景。
下一代智能合约与去中心化应用平台
中本聪在 2009 年开发的比特币常被认为是货币领域的根本性变革,作为首个数字资产实例,它既没有背书或“内在价值 (在新标签页中打开)”,也没有中心化的发行方或控制者。然而,比特币实验中另一个(可以说更重要的)部分是作为分布式共识工具的底层区块链技术,人们的注意力正迅速开始转移到比特币的这一方面。区块链技术常被提及的其他应用包括:使用链上数字资产来代表自定义货币和金融工具(“彩色币 (在新标签页中打开)”),底层物理设备的所有权(“智能资产 (在新标签页中打开)”),域名等不可替代资产(“Namecoin (在新标签页中打开)”),以及更复杂的应用,涉及由实现任意规则的一段代码直接控制数字资产(“智能合约 (在新标签页中打开)”),甚至基于区块链的“去中心化自治组织 (在新标签页中打开)”(DAO)。以太坊旨在提供的是一个内置了成熟的图灵完备编程语言的区块链,该语言可用于创建“合约”,这些合约可用于编码任意状态转换函数,允许用户只需用几行代码编写逻辑,即可创建上述任何系统,以及许多我们尚未想象到的其他系统。
比特币与现有概念简介
历史
去中心化数字货币以及财产登记等替代应用的概念已经存在了几十年。20 世纪 80 年代和 90 年代的匿名电子现金协议,主要依赖于一种被称为乔姆盲签(Chaumian blinding)的密码学原语,提供了一种具有高度隐私性的货币,但这些协议由于依赖中心化中介而大多未能获得关注。1998 年,戴伟的 b-money (在新标签页中打开) 成为第一个提出通过解决计算难题和去中心化共识来创造货币的想法的提案,但该提案在如何实际实施去中心化共识方面缺乏细节。2005 年,哈尔·芬尼(Hal Finney)引入了“可复用的工作量证明 (在新标签页中打开)”概念,该系统结合了 b-money 的思想和亚当·巴克(Adam Back)计算困难的 Hashcash 难题来创建一个加密货币概念,但由于依赖可信计算作为后端,再次未能达到理想状态。2009 年,中本聪(Satoshi Nakamoto)首次在实践中实现了一种去中心化货币,将通过公钥密码学管理所有权的既定原语与一种用于跟踪谁拥有硬币的共识算法(被称为“工作量证明 (PoW)”)结合起来。
工作量证明 (PoW) 背后的机制是该领域的一项突破,因为它同时解决了两个问题。首先,它提供了一种简单且适度有效的共识算法,允许网络中的节点共同就比特币账本状态的一组规范更新达成一致。其次,它提供了一种允许自由进入共识过程的机制,解决了决定谁能影响共识的政治问题,同时防止了女巫攻击。它通过用经济壁垒取代参与的正式壁垒(例如要求在特定列表中注册为唯一实体)来实现这一点——单个节点在共识投票过程中的权重与其带来的计算能力成正比。从那时起,人们提出了一种称为_权益证明 (PoS)_的替代方法,计算节点的权重与其持有的货币成正比,而不是计算资源;讨论这两种方法的相对优缺点超出了本文的范围,但应该注意的是,这两种方法都可以用作加密货币的骨干。
作为状态转换系统的比特币
从技术角度来看,诸如比特币之类的加密货币账本可以被认为是一个状态转换系统,其中存在一个由所有现有比特币的所有权状态组成的“状态”,以及一个接收状态和交易并输出作为结果的新状态的“状态转换函数”。例如,在标准银行系统中,状态是资产负债表,交易是将 $X 从 A 转移到 B 的请求,状态转换函数将 A 账户中的价值减少 $X,并将 B 账户中的价值增加 $X。如果 A 的账户一开始少于 $X,则状态转换函数返回错误。因此,可以正式定义:
APPLY(S,TX) -> S' or ERROR
在上面定义的银行系统中:
APPLY({ Alice: $50, Bob: $50 },"send $20 from Alice to Bob") = { Alice: $30, Bob: $70 }
但是:
APPLY({ Alice: $50, Bob: $50 },"send $70 from Alice to Bob") = ERROR
比特币中的“状态”是所有已铸造且尚未花费的硬币(技术上称为“未花费的交易输出”或 UTXO)的集合,每个 UTXO 都有一个面额和一个所有者(由一个 20 字节的地址定义,该地址本质上是一个密码学公钥fn1)。一笔交易包含一个或多个输入,每个输入包含对现有 UTXO 的引用以及由与所有者地址关联的私钥生成的密码学签名,以及一个或多个输出,每个输出包含要添加到状态中的新 UTXO。
状态转换函数 APPLY(S,TX) -> S' 可以大致定义如下:
对于
TX中的每个输入:- 如果引用的 UTXO 不在
S中,则返回错误。 - 如果提供的签名与 UTXO 的所有者不匹配,则返回错误。
- 如果引用的 UTXO 不在
- 如果所有输入 UTXO 的面额总和小于所有输出 UTXO 的面额总和,则返回错误。
- 返回移除了所有输入 UTXO 并添加了所有输出 UTXO 的
S。
第一步的前半部分防止交易发送者花费不存在的硬币,第一步的后半部分防止交易发送者花费他人的硬币,第二步强制执行价值守恒。为了将其用于支付,协议如下。假设 Alice 想向 Bob 发送 11.7 BTC。首先,Alice 将寻找一组她拥有的可用 UTXO,总计至少为 11.7 BTC。实际上,Alice 将无法获得恰好 11.7 BTC;假设她能获得的最小组合是 6+4+2=12。然后,她使用这三个输入和两个输出创建一笔交易。第一个输出将是 11.7 BTC,其所有者为 Bob 的地址,第二个输出将是剩余的 0.3 BTC“找零”,所有者是 Alice 自己。
挖矿
如果我们能够访问值得信赖的中心化服务,这个系统将很容易实现;它可以完全按照描述进行编码,使用中心化服务器的硬盘来跟踪状态。然而,对于比特币,我们试图建立一个去中心化的货币系统,因此我们需要将状态转换系统与共识系统结合起来,以确保每个人都对交易顺序达成一致。比特币的去中心化共识过程要求网络中的节点不断尝试生成称为“区块”的交易包。网络旨在每十分钟左右生成一个区块,每个区块包含一个时间戳、一个随机数、对前一个区块的引用(即哈希)以及自前一个区块以来发生的所有交易的列表。随着时间的推移,这创建了一个持久的、不断增长的“区块链”,它不断更新以代表比特币账本的最新状态。
在这种范式中,检查区块是否有效的算法如下:
- 检查该区块引用的前一个区块是否存在且有效。
- 检查该区块的时间戳是否大于前一个区块的时间戳fn2,并且小于未来 2 小时。
- 检查该区块上的工作量证明 (PoW) 是否有效。
- 设
S[0]为前一个区块末尾的状态。 - 假设
TX是包含n笔交易的区块交易列表。对于0...n-1中的所有i,设置S[i+1] = APPLY(S[i],TX[i])。如果任何应用返回错误,则退出并返回 false。 - 返回 true,并将
S[n]注册为该区块末尾的状态。
本质上,区块中的每笔交易都必须提供一个有效的状态转换,从执行交易前的规范状态转换到某个新状态。请注意,状态并未以任何方式编码在区块中;它纯粹是验证节点要记住的抽象概念,并且只能通过从创世状态开始并按顺序应用每个区块中的每笔交易来(安全地)计算任何区块的状态。此外,请注意矿工将交易包含在区块中的顺序很重要;如果区块中有两笔交易 A 和 B,使得 B 花费了 A 创建的 UTXO,那么如果 A 在 B 之前,该区块将是有效的,否则无效。
上述列表中存在的一个在其他系统中找不到的有效性条件是“工作量证明 (PoW)”的要求。确切的条件是,每个区块的双重 SHA-256 哈希(被视为 256 位数字)必须小于动态调整的目标值,在撰写本文时,该目标值约为 2187。这样做的目的是使区块创建在计算上变得“困难”,从而防止女巫攻击者为了自己的利益而重构整个区块链。因为 SHA-256 被设计为完全不可预测的伪随机函数,所以创建有效区块的唯一方法就是反复试验,不断增加随机数并查看新哈希是否匹配。
在当前约 2187 的目标下,网络平均必须尝试约 269 次才能找到一个有效的区块;通常,网络每 2016 个区块重新校准一次目标,以便网络中的某个节点平均每十分钟生成一个新区块。为了补偿矿工的这项计算工作,每个区块的矿工都有权包含一笔凭空给自己 25 BTC 的交易。此外,如果任何交易的输入总面额高于输出总面额,差额也将作为“交易费”归矿工所有。顺便说一句,这也是发行 BTC 的唯一机制;创世状态根本不包含任何硬币。
为了更好地理解挖矿的目的,让我们研究一下在发生恶意攻击者时会发生什么。由于已知比特币的底层密码学是安全的,攻击者将瞄准比特币系统中未受密码学直接保护的一个部分:交易顺序。攻击者的策略很简单:
- 向商家发送 100 BTC 以换取某些产品(最好是快速交付的数字商品)
- 等待产品交付
- 生成另一笔交易,将相同的 100 BTC 发送给自己
- 试图说服网络,他发送给自己的交易是先发生的。
一旦步骤 (1) 发生,几分钟后,某个矿工将把该交易包含在一个区块中,假设是第 270000 号区块。大约一小时后,该区块之后的链上将添加五个区块,每个区块都间接指向该交易,从而“确认”它。此时,商家将接受付款为已最终确定并交付产品;因为我们假设这是一种数字商品,所以交付是即时的。现在,攻击者创建另一笔交易,将 100 BTC 发送给自己。如果攻击者只是将其发布到实际网络中,该交易将不会被处理;矿工将尝试运行 APPLY(S,TX) 并注意到 TX 消耗了一个不再处于状态中的 UTXO。因此,攻击者创建了区块链的“分叉”,首先挖掘另一个版本的区块 270000,指向同一个区块 269999 作为父区块,但用新交易代替旧交易。因为区块数据不同,这需要重新进行工作量证明 (PoW)。此外,攻击者的新版本区块 270000 具有不同的哈希,因此原始区块 270001 到 270005 不会“指向”它;因此,原始链和攻击者的新链是完全分开的。规则是,在分叉中,最长的区块链被认为是真实的,因此合法的矿工将在 270005 链上工作,而攻击者独自在 270000 链上工作。为了使攻击者的区块链成为最长的,他需要拥有比网络其余部分总和更多的计算能力才能赶上(因此称为“51%攻击”)。
默克尔树
左图:只需提供默克尔树中的少量节点即可证明分支的有效性。
右图:任何试图更改默克尔树任何部分的尝试最终都会导致链上某处出现不一致。
比特币的一个重要可扩展性特征是区块存储在多级数据结构中。区块的“哈希”实际上只是区块头的哈希,区块头是一段大约 200 字节的数据,包含时间戳、随机数、前一个区块哈希以及称为默克尔树的数据结构的根哈希,该数据结构存储了区块中的所有交易。默克尔树是一种二叉树,由一组节点组成,树底部有大量包含底层数据的叶节点,一组中间节点(每个节点是其两个子节点的哈希),最后是一个单一的根节点(也由其两个子节点的哈希形成),代表树的“顶部”。默克尔树的目的是允许区块中的数据零散地传递:节点可以从一个来源仅下载区块头,从另一个来源下载与它们相关的树的一小部分,并且仍然确信所有数据都是正确的。这样做的原因是哈希向上层传播:如果恶意用户试图将伪造的交易交换到默克尔树的底部,这种更改将导致上方节点的更改,然后导致更上方节点的更改,最终改变树的根,从而改变区块的哈希,导致协议将其注册为完全不同的区块(几乎可以肯定具有无效的工作量证明 (PoW))。
默克尔树协议可以说是长期可持续性所必不可少的。比特币网络中的“全节点”(存储并处理每个区块的全部内容的节点)截至 2014 年 4 月在比特币网络中占用约 15 GB 的磁盘空间,并且每月增长超过 1 GB。目前,这对于某些台式电脑是可行的,但对于手机则不可行,而在未来,只有企业和爱好者才能参与。一种称为“简化支付验证”(SPV)的协议允许存在另一类节点,称为“轻节点”,它们下载区块头,验证区块头上的工作量证明 (PoW),然后仅下载与它们相关的交易关联的“分支”。这使得轻节点能够在仅下载整个区块链的一小部分的情况下,以强有力的安全保证确定任何比特币交易的状态及其当前余额。
替代区块链应用
将底层区块链思想应用于其他概念的想法也有着悠久的历史。2005 年,尼克·萨博(Nick Szabo)提出了“具有所有者权限的安全产权 (在新标签页中打开)”的概念,该文件描述了“复制数据库技术的新进展”将如何允许基于区块链的系统存储谁拥有什么土地的登记册,创建了一个精心设计的框架,包括宅基地、逆权占有和乔治亚土地税等概念。然而,不幸的是,当时没有有效的复制数据库系统可用,因此该协议从未在实践中实施。然而,在 2009 年之后,一旦比特币的去中心化共识被开发出来,许多替代应用迅速开始出现。
- Namecoin - 创建于 2010 年,Namecoin (在新标签页中打开) 最好被描述为一个去中心化的名称注册数据库。在 Tor、比特币和 BitMessage 等去中心化协议中,需要有某种方法来识别账户,以便其他人可以与它们交互,但在所有现有解决方案中,唯一可用的标识符是像
1LW79wp5ZBqaHW1jL5TCiBCrhQYtHagUWy这样的伪随机哈希。理想情况下,人们希望能够拥有一个名为“george”的账户。然而,问题是,如果一个人可以创建一个名为“george”的账户,那么其他人也可以使用相同的过程为自己注册“george”并冒充他们。唯一的解决方案是先申请范式,即第一个注册者成功,第二个注册者失败——这个问题非常适合比特币共识协议。Namecoin 是使用这种想法的名称注册系统中最古老、最成功的实现。 - 彩色币 (Colored coins) - 彩色币 (在新标签页中打开) 的目的是作为一种协议,允许人们在比特币区块链上创建自己的数字货币——或者,在具有一个单位的货币的重要平凡情况下,创建数字代币。在彩色币协议中,人们通过公开为特定的比特币 UTXO 分配一种颜色来“发行”一种新货币,并且该协议递归地将其他 UTXO 的颜色定义为与创建它们的交易所花费的输入的颜色相同(在混合颜色输入的情况下适用一些特殊规则)。这允许用户维护仅包含特定颜色 UTXO 的钱包,并像常规比特币一样发送它们,通过区块链回溯以确定他们收到的任何 UTXO 的颜色。
- 元币 (Metacoins) - 元币背后的想法是拥有一个存在于比特币之上的协议,使用比特币交易来存储元币交易,但具有不同的状态转换函数
APPLY'。因为元币协议无法防止无效的元币交易出现在比特币区块链中,所以添加了一条规则:如果APPLY'(S,TX)返回错误,则协议默认为APPLY'(S,TX) = S。这提供了一种创建任意加密货币协议的简单机制,可能具有无法在比特币本身内部实现的高级功能,但开发成本非常低,因为挖矿和网络的复杂性已经由比特币协议处理。元币已被用于实现某些类别的金融合约、名称注册和去中心化交易所。
因此,总的来说,构建共识协议有两种方法:构建独立的网络,以及在比特币之上构建协议。前一种方法虽然在 Namecoin 等应用的情况下相当成功,但很难实现;每个单独的实现都需要引导一个独立的区块链,以及构建和测试所有必要的状态转换和网络代码。此外,我们预测去中心化共识技术的应用集将遵循幂律分布,其中绝大多数应用将太小而无法保证拥有自己的区块链,并且我们注意到存在大量需要相互交互的去中心化应用,特别是去中心化自治组织 (DAO)。
另一方面,基于比特币的方法存在一个缺陷,即它没有继承比特币的简化支付验证功能。SPV 适用于比特币,因为它可以使用区块链深度作为有效性的代理;在某个时候,一旦交易的祖先追溯得足够远,就可以安全地说它们合法地是状态的一部分。另一方面,基于区块链的元协议无法强制区块链不包含在其自身协议上下文中无效的交易。因此,一个完全安全的 SPV 元协议实现需要一直向后扫描到比特币区块链的开头,以确定某些交易是否有效。目前,基于比特币的元协议的所有“轻量级”实现都依赖于可信服务器来提供数据,这可以说是一个非常次优的结果,特别是当加密货币的主要目的之一是消除对信任的需求时。
脚本
即使没有任何扩展,比特币协议实际上也促进了“智能合约”概念的弱版本。比特币中的 UTXO 不仅可以由公钥拥有,还可以由用简单的基于堆栈的编程语言表达的更复杂的脚本拥有。在这种范式中,花费该 UTXO 的交易必须提供满足脚本的数据。实际上,即使是基本的公钥所有权机制也是通过脚本实现的:该脚本将椭圆曲线签名作为输入,根据交易和拥有 UTXO 的地址对其进行验证,如果验证成功则返回 1,否则返回 0。对于各种其他用例,存在其他更复杂的脚本。例如,可以构建一个脚本,要求给定的三个私钥中的两个签名才能验证(“多重签名”),这种设置对于公司账户、安全储蓄账户和某些商家托管情况很有用。脚本还可以用于为计算问题的解决方案支付赏金,甚至可以构建一个脚本,内容类似于“如果你能提供 SPV 证明,证明你向我发送了这种面额的狗狗币交易,那么这个比特币 UTXO 就是你的”,这本质上允许去中心化的跨加密货币兑换。
然而,在比特币中实现的脚本语言有几个重要的局限性:
- 缺乏图灵完备性 - 也就是说,虽然比特币脚本语言支持很大一部分计算,但它几乎不支持所有计算。缺少的主要类别是循环。这样做是为了避免在交易验证期间出现无限循环;理论上,对于脚本程序员来说,这是一个可以克服的障碍,因为任何循环都可以通过使用 if 语句简单地多次重复底层代码来模拟,但这确实会导致脚本的空间效率非常低。例如,实现替代的椭圆曲线签名算法可能需要 256 次重复的乘法轮次,所有这些轮次都单独包含在代码中。
- 价值盲目性 - UTXO 脚本无法对可提取的金额提供细粒度的控制。例如,预言机合约的一个强大用例是套期保值合约,其中 A 和 B 投入价值 1000 美元的 BTC,30 天后,脚本将价值 1000 美元的 BTC 发送给 A,其余的发送给 B。这将需要一个预言机来确定 1 BTC 的美元价值,但即便如此,在信任和基础设施要求方面,与目前可用的完全中心化解决方案相比,这也是一个巨大的改进。然而,因为 UTXO 是全有或全无的,实现这一目标的唯一方法是通过非常低效的黑客手段,即拥有许多不同面额的 UTXO(例如,对于高达 30 的每个 k,都有一个 2k 的 UTXO),并让预言机选择将哪些 UTXO 发送给 A,哪些发送给 B。
- 缺乏状态 - UTXO 只能是已花费或未花费;没有机会进行多阶段合约或脚本来保持除此之外的任何其他内部状态。这使得很难制定多阶段期权合约、去中心化交易所报价或两阶段密码学承诺协议(对于安全的计算赏金是必要的)。这也意味着 UTXO 只能用于构建简单的一次性合约,而不能构建更复杂的“有状态”合约(如去中心化组织),并使元协议难以实现。二进制状态与价值盲目性相结合也意味着另一个重要的应用——提款限制——是不可能的。
- 区块链盲目性 - UTXO 对区块链数据(如随机数、时间戳和前一个区块哈希)是盲目的。这剥夺了脚本语言潜在的有价值的随机性来源,从而严重限制了在赌博和其他几个类别中的应用。
因此,我们看到了在加密货币之上构建高级应用的三种方法:构建新的区块链、在比特币之上使用脚本以及在比特币之上构建元协议。构建新的区块链允许在构建功能集方面拥有无限的自由,但代价是开发时间、引导工作和安全性。使用脚本很容易实现和标准化,但其功能非常有限,而元协议虽然简单,但在可扩展性方面存在缺陷。借助以太坊,我们打算构建一个替代框架,在易于开发以及更强大的轻客户端属性方面提供更大的收益,同时允许应用共享经济环境和区块链安全性。
以太坊
以太坊的意图是创建一个用于构建去中心化应用 (dapp) 的替代协议,提供一组不同的权衡,我们认为这对于一大类去中心化应用将非常有用,特别强调快速开发时间、小型和极少使用的应用的安全性以及不同应用之间非常高效交互的能力。以太坊通过构建本质上是终极抽象基础层来实现这一点:一个内置图灵完备编程语言的区块链,允许任何人编写智能合约和去中心化应用,在其中他们可以创建自己任意的所有权规则、交易格式和状态转换函数。一个准系统版本的 Namecoin 可以用两行代码编写,而其他协议(如货币和声誉系统)可以在不到二十行代码内构建。智能合约,即包含价值且仅在满足特定条件时才解锁的密码学“盒子”,也可以在该平台之上构建,由于增加了图灵完备性、价值感知、区块链感知和状态的能力,其功能比比特币脚本提供的功能强大得多。
以太坊账户
在以太坊中,状态由称为“账户”的对象组成,每个账户都有一个 20 字节的地址,状态转换是账户之间价值和信息的直接转移。一个以太坊账户包含四个字段:
- 随机数 (nonce),一个用于确保每笔交易只能被处理一次的计数器
- 账户当前的以太币余额
- 账户的合约代码(如果存在)
- 账户的存储(默认情况下为空)
“以太币”是以太坊主要的内部加密燃料,用于支付交易费。通常,有两种类型的账户:由私钥控制的外部拥有账户,以及由其合约代码控制的合约账户。外部拥有账户没有代码,人们可以通过创建和签名交易从外部拥有账户发送消息;在合约账户中,每次合约账户收到消息时,其代码就会激活,允许它读取和写入内部存储,并依次发送其他消息或创建合约。
请注意,以太坊中的“合约”不应被视为应该被“履行”或“遵守”的东西;相反,它们更像是生活在以太坊执行环境中的“自治代理”,当被消息或交易“戳中”时,总是执行一段特定的代码,并直接控制它们自己的以太币余额和它们自己的键/值存储,以跟踪持久变量。
消息和交易
在以太坊中,“交易”一词用于指代存储要从外部拥有账户发送的消息的签名数据包。交易包含:
- 消息的接收者
- 标识发送者的签名
- 从发送者转账给接收者的以太币数量
- 一个可选的数据字段
- 一个
STARTGAS值,表示允许交易执行采取的最大计算步骤数 - 一个
GASPRICE值,表示发送者为每个计算步骤支付的费用
前三个是任何加密货币中预期的标准字段。数据字段默认情况下没有功能,但虚拟机有一个操作码,合约可以使用它来访问数据;作为一个示例用例,如果一个合约充当链上域名注册服务,那么它可能希望将传递给它的数据解释为包含两个“字段”,第一个字段是要注册的域名,第二个字段是要将其注册到的 IP 地址。合约将从消息数据中读取这些值,并将它们适当地放入存储中。
STARTGAS 和 GASPRICE 字段对于以太坊的反拒绝服务模型至关重要。为了防止代码中出现意外或恶意的无限循环或其他计算浪费,每笔交易都被要求设置一个限制,规定它可以使用的代码执行计算步骤的数量。计算的基本单位是“Gas”;通常,一个计算步骤花费 1 Gas,但某些操作会花费更高数量的 Gas,因为它们的计算成本更高,或者增加了必须作为状态一部分存储的数据量。交易数据中的每个字节还需要支付 5 Gas 的费用。费用系统的意图是要求攻击者为他们消耗的每种资源(包括计算、带宽和存储)按比例付费;因此,任何导致网络消耗更多这些资源的交易,都必须具有与增量大致成比例的 gas 费。
消息
合约有能力向其他合约发送“消息”。消息是永远不会被序列化的虚拟对象,仅存在于以太坊执行环境中。一条消息包含:
- 消息的发送者(隐式)
- 消息的接收者
- 随消息一起转账的以太币数量
- 一个可选的数据字段
- 一个
STARTGAS值
本质上,消息就像一笔交易,只不过它是由合约而不是外部参与者产生的。当当前正在执行代码的合约执行 CALL 操作码时,就会产生一条消息,该操作码会产生并执行一条消息。像交易一样,消息会导致接收账户运行其代码。因此,合约可以以与外部参与者完全相同的方式与其他合约建立关系。
请注意,由交易或合约分配的 Gas 授权额度适用于该交易和所有子执行消耗的总 Gas。例如,如果外部参与者 A 向 B 发送了一笔带有 1000 Gas 的交易,并且 B 在向 C 发送消息之前消耗了 600 Gas,而 C 的内部执行在返回之前消耗了 300 Gas,那么 B 在耗尽 Gas 之前还可以再花费 100 Gas。
以太坊状态转换函数
以太坊状态转换函数 APPLY(S,TX) -> S' 可以定义如下:
- 检查交易格式是否正确(即具有正确数量的值),签名是否有效,以及随机数是否与发送者账户中的随机数匹配。如果不匹配,则返回错误。
- 将交易费计算为
STARTGAS * GASPRICE,并从签名中确定发送地址。从发送者的账户余额中减去该费用,并增加发送者的随机数。如果没有足够的余额来支付,则返回错误。 - 初始化
GAS = STARTGAS,并按每个字节扣除一定数量的 Gas,以支付交易中的字节费用。 - 将交易金额从发送者账户转账到接收账户。如果接收账户尚不存在,则创建它。如果接收账户是合约,则运行合约的代码,直到完成或执行耗尽 Gas。
- 如果由于发送者没有足够的资金导致价值转账失败,或者代码执行耗尽了 Gas,则回退除支付费用之外的所有状态更改,并将费用添加到矿工的账户中。
- 否则,将所有剩余 Gas 的费用退还给发送者,并将消耗的 Gas 所支付的费用发送给矿工。
例如,假设合约的代码是:
if !self.storage[calldataload(0)]:
self.storage[calldataload(0)] = calldataload(32)
请注意,在现实中,合约代码是用低级 EVM 代码编写的;为了清晰起见,此示例是用我们的一种高级语言 Serpent 编写的,并且可以编译为 EVM 代码。假设合约的存储最初为空,并且发送了一笔交易,其包含 10 以太币的价值、2000 Gas、0.001 以太币的 gasprice(Gas 价格)以及 64 字节的数据,其中字节 0-31 表示数字 2,字节 32-63 表示字符串 CHARLIEfn3。在这种情况下,状态转换函数的过程如下:
- 检查交易是否有效且格式正确。
- 检查交易发送者是否至少有 2000 * 0.001 = 2 个以太币。如果是,则从发送者的账户中减去 2 个以太币。
- 初始化 Gas = 2000;假设交易长度为 170 字节,字节费为 5,减去 850,剩下 1150 Gas。
- 从发送者的账户中再减去 10 个以太币,并将其添加到合约的账户中。
- 运行代码。在这种情况下,这很简单:它检查合约在索引
2处的存储是否被使用,注意到它没有被使用,因此它将索引2处的存储设置为值CHARLIE。假设这花费了 187 Gas,那么剩余的 Gas 量为 1150 - 187 = 963 - 将 963 * 0.001 = 0.963 个以太币加回到发送者的账户中,并返回结果状态。
如果交易的接收端没有合约,那么总交易费将简单地等于提供的 GASPRICE 乘以交易的字节长度,并且随交易一起发送的数据将是不相关的。
请注意,在回退方面,消息的工作方式与交易等效:如果消息执行耗尽了 Gas,那么该消息的执行以及由该执行触发的所有其他执行都会回退,但父执行不需要回退。这意味着合约调用另一个合约是“安全的”,因为如果 A 用 G Gas 调用 B,那么 A 的执行保证最多损失 G Gas。最后,请注意有一个操作码 CREATE 用于创建合约;它的执行机制通常类似于 CALL,例外之处在于执行的输出决定了新创建合约的代码。
代码执行
以太坊合约中的代码是用一种低级的、基于堆栈的字节码语言编写的,被称为“以太坊虚拟机 (EVM) 代码”或“EVM 代码”。该代码由一系列字节组成,其中每个字节代表一个操作。通常,代码执行是一个无限循环,包括在当前程序计数器(从零开始)处重复执行操作,然后将程序计数器加一,直到到达代码末尾,或者检测到错误、STOP 或 RETURN 指令。这些操作可以访问三种类型的空间来存储数据:
- 堆栈,一个后进先出的容器,可以将值推入和弹出
- 内存,一个可无限扩展的字节数组
- 合约的长期存储,一个键/值存储。与计算结束后重置的堆栈和内存不同,存储会长期持久化。
代码还可以访问传入消息的价值、发送者和数据,以及区块头数据,并且代码还可以返回一个字节数组数据作为输出。
EVM 代码的正式执行模型出奇地简单。当以太坊虚拟机运行时,其完整的计算状态可以由元组 (block_state, transaction, message, code, memory, stack, pc, gas) 定义,其中 block_state 是包含所有账户并包括余额和存储的全局状态。在每一轮执行开始时,通过获取 code 的第 pc 个字节(如果 pc >= len(code) 则为 0)来找到当前指令,并且每条指令在如何影响该元组方面都有自己的定义。例如,ADD 从堆栈中弹出两个项目并推入它们的总和,将 gas 减 1 并将 pc 加 1,而 SSTORE 从堆栈中弹出顶部两个项目,并将第二个项目插入到合约存储中由第一个项目指定的索引处。尽管有许多方法可以通过即时编译来优化以太坊虚拟机的执行,但以太坊的基本实现可以用几百行代码完成。
区块链和挖矿
以太坊区块链在许多方面类似于比特币区块链,尽管它确实有一些不同之处。在区块链架构方面,以太坊和比特币之间的主要区别在于,与比特币不同,以太坊区块包含交易列表和最新状态的副本。除此之外,区块中还存储了另外两个值:区块号和难度。以太坊中的基本区块验证算法如下:
- 检查引用的前一个区块是否存在且有效。
- 检查区块的时间戳是否大于引用的前一个区块的时间戳,并且小于未来 15 分钟
- 检查区块号、难度、交易根、叔块根和 gas 上限(各种低级以太坊特定概念)是否有效。
- 检查区块上的工作量证明 (PoW) 是否有效。
- 设
S[0]为前一个区块末尾的状态。 - 设
TX为区块的交易列表,包含n笔交易。对于0...n-1中的所有i,设置S[i+1] = APPLY(S[i],TX[i])。如果任何应用返回错误,或者如果到目前为止区块中消耗的总 Gas 超过了GASLIMIT,则返回错误。 - 设
S_FINAL为S[n],但加上支付给矿工的区块奖励。 - 检查状态
S_FINAL的默克尔树根是否等于区块头中提供的最终状态根。如果是,则区块有效;否则,无效。
乍一看,这种方法似乎效率极低,因为它需要将整个状态与每个区块一起存储,但实际上效率应该与比特币相当。原因是状态存储在树结构中,并且在每个区块之后,只需要更改树的一小部分。因此,通常在两个相邻区块之间,树的绝大部分应该是相同的,因此数据可以存储一次,并使用指针(即子树的哈希)引用两次。一种被称为“帕特里夏树 (Patricia tree)”的特殊树被用来实现这一点,包括对默克尔树概念的修改,允许高效地插入和删除节点,而不仅仅是更改节点。此外,由于所有状态信息都是最后一个区块的一部分,因此无需存储整个区块链历史记录——如果将这种策略应用于比特币,经计算可以节省 5-20 倍的空间。
一个常见的问题是,就物理硬件而言,合约代码在“哪里”执行。这有一个简单的答案:执行合约代码的过程是状态转换函数定义的一部分,而状态转换函数是区块验证算法的一部分,因此,如果一笔交易被添加到区块 B 中,由该交易产生的代码执行将由现在和将来下载并验证区块 B 的所有节点执行。
应用
总的来说,以太坊之上有三种类型的应用。第一类是金融应用,为用户提供更强大的方式来管理和使用他们的资金参与合约。这包括子货币、金融衍生品、对冲合约、储蓄钱包、遗嘱,甚至最终包括一些类别的全面雇佣合约。第二类是半金融应用,这里涉及资金,但所做的事情也有很重的非金钱方面;一个完美的例子是为解决计算问题而设立的自动执行悬赏。最后,还有一些完全非金融的应用,例如在线投票和去中心化治理。
代币系统
链上代币系统有许多应用,从代表美元或黄金等资产的子货币到公司股票、代表智能财产的个人代币、安全的不可伪造优惠券,甚至完全与传统价值无关、用作激励积分系统的代币系统。在以太坊中实现代币系统出奇地容易。需要理解的关键点是,所有的货币或代币系统,从根本上说,就是一个只有一种操作的数据库:从 A 减去 X 个单位并给 B 增加 X 个单位,条件是 (1) A 在交易前至少有 X 个单位,以及 (2) 该交易得到 A 的批准。实现一个代币系统所需要做的就是将这种逻辑实现到一个合约中。
在 Serpent 中实现代币系统的基本代码如下所示:
def send(to, value):
if self.storage[msg.sender] >= value:
self.storage[msg.sender] = self.storage[msg.sender] - value
self.storage[to] = self.storage[to] + value
这本质上是本文档上文进一步描述的“银行系统”状态转换函数的字面实现。需要添加几行额外的代码来提供最初分配货币单位的初始步骤以及其他一些边缘情况,理想情况下,还会添加一个函数以让其他合约查询某个地址的余额。但仅此而已。理论上,作为子货币的基于以太坊的代币系统可能包含基于比特币的链上元货币所缺乏的另一个重要功能:直接用该货币支付交易费的能力。实现这一点的方法是,合约将维持一个以太币余额,用它向发送者退还用于支付费用的以太币,并且它将通过收集其作为费用收取的内部货币单位并在持续运行的拍卖中转售它们来补充这个余额。因此,用户需要用以太币“激活”他们的账户,但一旦有了以太币,它就可以重复使用,因为合约每次都会退还它。
金融衍生品和稳定价值货币
金融衍生品是“智能合约”最常见的应用,也是在代码中最容易实现的之一。实现金融合约的主要挑战在于,它们中的大多数需要参考外部价格行情;例如,一个非常理想的应用是一个智能合约,它可以对冲以太币(或其他加密货币)相对于美元的波动性,但要做到这一点,需要合约知道 ETH/USD 的价值是多少。最简单的方法是通过由特定方(例如纳斯达克)维护的“数据源”合约,其设计使得该方有能力根据需要更新合约,并提供一个接口,允许其他合约向该合约发送消息并获得提供价格的响应。
有了这个关键要素,对冲合约将如下所示:
- 等待 A 方输入 1000 个以太币。
- 等待 B 方输入 1000 个以太币。
- 将通过查询数据源合约计算出的 1000 个以太币的美元价值记录在存储中,假设为 $x。
- 30 天后,允许 A 或 B“重新激活”合约,以便将价值 $x 的以太币(通过再次查询数据源合约以获取新价格来计算)发送给 A,其余的发送给 B。
这样的合约在加密商业中将具有巨大的潜力。关于加密货币被提及的主要问题之一是其波动性;尽管许多用户和商家可能希望获得处理加密资产的安全性和便利性,但他们可能不希望面临在一天内损失其资金 23% 价值的前景。到目前为止,最常被提出的解决方案是发行方支持的资产;其理念是,发行方创建一种子货币,他们有权发行和撤销单位,并向任何(在链下)向他们提供一个单位指定标的资产(例如黄金、美元)的人提供一个单位的该货币。然后,发行方承诺向任何退回一个单位加密资产的人提供一个单位的标的资产。这种机制允许任何非加密资产被“提升”为加密资产,前提是发行方是可信的。
然而,在实践中,发行方并不总是值得信赖的,而且在某些情况下,银行基础设施太薄弱或太不友好,导致此类服务无法存在。金融衍生品提供了一种替代方案。在这里,不是由单一发行方提供资金来支持资产,而是由一个去中心化的投机者市场来扮演这个角色,他们押注加密参考资产(例如 ETH)的价格会上涨。与发行方不同,投机者无法选择在交易中违约,因为对冲合约将他们的资金托管在其中。请注意,这种方法并非完全去中心化的,因为仍然需要一个可信的来源来提供价格行情,尽管可以说这在降低基础设施要求(与作为发行方不同,发布喂价不需要许可证,并且很可能被归类为言论自由)和减少欺诈可能性方面仍然是一个巨大的进步。
身份和声誉系统
最早的替代加密货币 Namecoin (在新标签页中打开) 试图使用类似比特币的区块链来提供名称注册系统,用户可以在公共数据库中注册他们的名称以及其他数据。被引用最多的主要用例是 DNS (在新标签页中打开) 系统,将诸如“bitcoin.org”(或者在 Namecoin 的情况下是“bitcoin.bit”)之类的域名映射到 IP 地址。其他用例包括电子邮件身份验证和可能更高级的声誉系统。以下是在以太坊上提供类似 Namecoin 的名称注册系统的基本合约:
def register(name, value):
if !self.storage[name]:
self.storage[name] = value
该合约非常简单;它只是以太坊网络内部的一个数据库,可以向其中添加内容,但不能修改或删除。任何人都可以注册一个带有某个值的名称,然后该注册将永远保留。更复杂的名称注册合约还将具有允许其他合约查询它的“函数子句”,以及允许名称的“所有者”(即第一个注册者)更改数据或转移所有权的机制。人们甚至可以在此基础上添加声誉和信任网功能。
去中心化文件存储
在过去几年中,出现了许多受欢迎的在线文件存储初创公司,最著名的是 Dropbox,它们试图允许用户上传其硬盘的备份,并由服务存储该备份并允许用户访问,以换取月费。然而,在目前,文件存储市场有时相对低效;粗略地看一下各种现有的解决方案就会发现,特别是在 20-200 GB 的“恐怖谷”级别,既没有免费配额,也没有企业级折扣,主流文件存储成本的月度价格使得你在单个月内支付的费用超过了整个硬盘的成本。以太坊合约可以允许开发去中心化文件存储生态系统,个人用户可以通过出租自己的硬盘来赚取少量资金,未使用的空间可用于进一步降低文件存储的成本。
这种设备的关键基础部分将是我们所称的“去中心化 Dropbox 合约”。该合约的工作原理如下。首先,将所需数据分成多个区块,为了隐私对每个区块进行加密,并从中构建一棵默克尔树。然后制定一个合约,规则是:每 N 个区块,合约将在默克尔树中选择一个随机索引(使用可从合约代码访问的前一个区块哈希作为随机性来源),并将 X 个以太币给第一个提供交易的实体,该交易带有类似简化支付验证的证明,证明其拥有树中该特定索引处的区块。当用户想要重新下载他们的文件时,他们可以使用微支付通道协议(例如,每 32 KB 支付 1 萨博)来恢复文件;最节省费用的方法是付款人直到最后才发布交易,而是在每 32 KB 之后用具有相同随机数但稍微更丰厚奖励的交易替换该交易。
该协议的一个重要特征是,尽管看起来像是在信任许多随机节点不会决定忘记该文件,但可以通过秘密共享将文件分成许多片段,并观察合约以查看每个片段是否仍在某个节点的占有中,从而将该风险降低到接近于零。如果合约仍在支付资金,这就提供了一个密码学证明,证明外面仍然有人在存储该文件。
去中心化自治组织
“去中心化自治组织 (DAO)”的一般概念是一个虚拟实体,它拥有一定数量的成员或股东,他们(也许以 67% 的多数票)有权花费实体的资金并修改其代码。成员将集体决定组织应如何分配其资金。分配 DAO 资金的方法可以从悬赏、工资到甚至更奇特的机制,例如用于奖励工作的内部货币。这本质上复制了传统公司或非营利组织的法律框架,但仅使用密码学区块链技术来执行。到目前为止,围绕 DAO 的许多讨论都集中在“去中心化自治公司 (DAC)”的“资本主义”模型上,该模型具有收取股息的股东和可交易的股票;另一种选择,也许可以描述为“去中心化自治社区”,将使所有成员在决策中拥有平等的份额,并要求 67% 的现有成员同意添加或删除成员。一个人只能拥有一个成员资格的要求将需要由该群体集体执行。
如何编写 DAO 代码的一般大纲如下。最简单的设计就是一段自我修改的代码,如果三分之二的成员同意更改,它就会改变。尽管代码在理论上是不可变的,但人们可以轻松绕过这一点,通过将代码块放在单独的合约中,并将要调用的合约地址存储在可修改的存储中,从而实现事实上的可变性。在这样一个 DAO 合约的简单实现中,将有三种交易类型,通过交易中提供的数据来区分:
[0,i,K,V]注册一个索引为i的提案,以将存储索引K处的地址更改为值V[1,i]登记对提案i的赞成投票[2,i]如果已获得足够投票,则最终确定提案i
然后,合约将为其中每一个提供子句。它将维护所有未决存储更改的记录,以及投票支持这些更改的人员列表。它还将拥有所有成员的列表。当任何存储更改获得三分之二成员的投票支持时,最终确定交易就可以执行该更改。更复杂的骨架还将具有内置的投票能力,用于发送交易、添加成员和删除成员等功能,甚至可能提供 流动民主 (Liquid Democracy) (在新标签页中打开) 风格的投票委托(即,任何人都可以指定某人代其投票,并且指定是传递的,因此如果 A 指定 B,B 指定 C,那么 C 决定 A 的投票)。这种设计将允许 DAO 作为一个去中心化社区有机地发展,允许人们最终将筛选谁是成员的任务委托给专家,尽管与“当前系统”不同,随着各个社区成员改变他们的立场,专家可以随着时间的推移轻松地出现和消失。
另一种模型是去中心化公司,其中任何账户都可以拥有零股或多股,并且需要三分之二的股份才能做出决定。一个完整的骨架将涉及资产管理功能、提出买卖股票报价的能力以及接受报价的能力(最好在合约内部具有订单匹配机制)。委托也将以流动民主的风格存在,从而泛化“董事会”的概念。
更多应用
1. 储蓄钱包。假设 Alice 想要保证她的资金安全,但担心她会丢失私钥或有人会黑入她的私钥。她将以太币放入与银行 Bob 签订的合约中,如下所示:
- 仅 Alice 一人每天最多可以提取 1% 的资金。
- 仅 Bob 一人每天最多可以提取 1% 的资金,但 Alice 有能力用她的密钥进行交易以关闭此能力。
- Alice 和 Bob 一起可以提取任何金额。
通常,每天 1% 对 Alice 来说就足够了,如果 Alice 想提取更多,她可以联系 Bob 寻求帮助。如果 Alice 的密钥被黑客入侵,她会跑去找 Bob 将资金转移到一个新合约中。如果她丢失了密钥,Bob 最终会将资金取出。如果 Bob 证明是恶意的,那么她可以关闭他提取资金的能力。
2. 农作物保险。人们可以轻松制定金融衍生品合约,但使用天气数据源而不是任何价格指数。如果爱荷华州的一位农民购买了一种根据爱荷华州降水量反向支付的衍生品,那么如果发生干旱,该农民将自动收到钱,如果有足够的降雨,该农民会很高兴,因为他们的农作物会长得很好。这可以普遍扩展到自然灾害保险。
3. 去中心化数据源。对于金融差价合约,实际上可能通过一种名为“SchellingCoin (在新标签页中打开)”的协议来去中心化数据源。SchellingCoin 的基本工作原理如下:N 个参与方都将给定数据(例如 ETH/USD 价格)的值输入系统,对这些值进行排序,处于第 25 到第 75 百分位数之间的每个人都会获得一个代币作为奖励。每个人都有动机提供其他人将提供的答案,而大量参与者实际上能够达成一致的唯一值就是显而易见的默认值:真相。这创建了一个去中心化协议,理论上可以提供任何数量的值,包括 ETH/USD 价格、柏林的温度,甚至特定困难计算的结果。
4. 智能多重签名托管。比特币允许使用多重签名交易合约,例如,给定五个密钥中的三个可以花费资金。以太坊允许更细的粒度;例如,五分之四可以花费所有资金,五分之三每天最多可以花费 10%,五分之二每天最多可以花费 0.5%。此外,以太坊多重签名是异步的——两方可以在不同时间在区块链上注册他们的签名,最后一个签名将自动发送交易。
5. 云计算。EVM 技术还可用于创建可验证的计算环境,允许用户要求他人执行计算,然后可选择要求提供证明,证明在某些随机选择的检查点处的计算已正确完成。这允许创建一个云计算市场,任何用户都可以使用他们的台式机、笔记本电脑或专用服务器参与其中,并且抽查连同保证金可用于确保系统是可信的(即,节点无法通过作弊获利)。尽管这样的系统可能并不适合所有任务;例如,需要高水平进程间通信的任务无法轻松地在大型节点云上完成。然而,其他任务更容易并行化;像 SETI@home、folding@home 和遗传算法这样的项目可以很容易地在这样的平台上实现。
6. 点对点赌博。任何数量的点对点赌博协议,例如 Frank Stajano 和 Richard Clayton 的 Cyberdice (在新标签页中打开),都可以在以太坊区块链上实现。最简单的赌博协议实际上只是关于下一个区块哈希的差价合约,并且可以从那里建立更高级的协议,创建费用接近于零且无法作弊的赌博服务。
7. 预测市场。如果提供预言机或 SchellingCoin,预测市场也很容易实现,并且预测市场与 SchellingCoin 一起可能被证明是 futarchy(未来政体) (在新标签页中打开) 作为去中心化组织治理协议的第一个主流应用。
8. 链上去中心化市场,以身份和声誉系统为基础。
杂项与关注点
改进的 GHOST 实现
“贪婪最重观察子树”(Greedy Heaviest Observed Subtree,简称 GHOST)协议是由 Yonatan Sompolinsky 和 Aviv Zohar 在2013 年 12 月 (在新标签页中打开)首次引入的一项创新。GHOST 背后的动机是,具有快速确认时间的区块链目前由于高废块率而面临安全性降低的问题——因为区块在网络中传播需要一定的时间,如果矿工 A 挖出一个区块,然后矿工 B 在 A 的区块传播到 B 之前碰巧挖出了另一个区块,那么矿工 B 的区块最终将被浪费,并且不会对网络安全做出贡献。此外,还存在中心化问题:如果矿工 A 是一个拥有 30% 算力的矿池,而 B 拥有 10% 的算力,A 将有 70% 的时间面临产生废块的风险(因为另外 30% 的时间 A 产生了上一个区块,因此会立即获得挖矿数据),而 B 将有 90% 的时间面临产生废块的风险。因此,如果区块间隔足够短以至于废块率很高,A 仅仅凭借其规模就会大幅提高效率。结合这两种效应,快速产生区块的区块链极有可能导致一个矿池拥有足够大比例的网络算力,从而对挖矿过程拥有事实上的控制权。
正如 Sompolinsky 和 Zohar 所描述的,GHOST 通过在计算哪条链是“最长”链时包含废块,解决了网络安全损失的第一个问题;也就是说,不仅是区块的父区块和更早的祖先区块,该区块祖先的废弃后代区块(在以太坊术语中称为“叔块”)也被添加到计算哪个区块拥有最大总工作量证明 (PoW) 支持的过程中。为了解决中心化偏差的第二个问题,我们超越了 Sompolinsky 和 Zohar 描述的协议,还向废块提供区块奖励:一个废块获得其基础奖励的 87.5%,而包含该废块的侄子区块获得剩余的 12.5%。然而,交易费不会奖励给叔块。
以太坊实现了一个简化的 GHOST 版本,该版本只向下追溯七层。具体定义如下:
- 一个区块必须指定一个父区块,并且必须指定 0 个或多个叔块
- 包含在区块 B 中的叔块必须具有以下属性:
- 它必须是 B 的第 k 代祖先的直接子区块,其中
2 <= k <= 7。 - 它不能是 B 的祖先区块
- 叔块必须是一个有效的区块头,但不需要是先前已验证甚至有效的区块
- 叔块必须不同于先前区块中包含的所有叔块,以及同一区块中包含的所有其他叔块(非重复包含)
- 它必须是 B 的第 k 代祖先的直接子区块,其中
- 对于区块 B 中的每个叔块 U,B 的矿工将在其 coinbase 奖励中额外获得 3.125%,而 U 的矿工将获得标准 coinbase 奖励的 93.75%。
使用这种仅允许包含最多 7 代叔块的受限版 GHOST 有两个原因。首先,无限制的 GHOST 会在计算给定区块的哪些叔块有效时引入太多复杂性。其次,以太坊中使用的带有补偿的无限制 GHOST 会消除矿工在主链上而不是在公共攻击者的链上挖矿的动机。
费用
因为发布到区块链的每笔交易都会给网络带来需要下载和验证它的成本,所以需要某种监管机制(通常涉及交易费)来防止滥用。比特币中使用的默认方法是采用纯自愿的费用,依靠矿工作为守门人并设置动态最小值。这种方法在比特币社区非常受欢迎,特别是因为它是“基于市场的”,允许矿工和交易发送者之间的供需关系决定价格。然而,这种推理的问题在于,交易处理并不是一个市场;尽管将交易处理解释为矿工向发送者提供的服务在直觉上很有吸引力,但实际上,矿工包含的每笔交易都需要由网络中的每个节点进行处理,因此交易处理的绝大部分成本由第三方承担,而不是由决定是否包含该交易的矿工承担。因此,极有可能发生公地悲剧问题。
然而,事实证明,当给定一个特定的不准确的简化假设时,基于市场机制的这个缺陷会神奇地自我抵消。论证如下。假设:
- 一笔交易导致
k个操作,向任何包含它的矿工提供kR的奖励,其中R由发送者设置,并且k和R事先对矿工(大致)可见。 - 一个操作对任何节点的处理成本为
C(即所有节点具有相同的效率) - 有
N个挖矿节点,每个节点具有完全相等的处理能力(即占总量的1/N) - 不存在非挖矿的全节点。
如果预期奖励大于成本,矿工将愿意处理一笔交易。因此,预期奖励为 kR/N,因为矿工有 1/N 的机会处理下一个区块,而矿工的处理成本仅为 kC。因此,矿工将包含满足 kR/N > kC 或 R > NC 的交易。请注意,R 是发送者提供的每个操作的费用,因此是发送者从交易中获得收益的下限,而 NC 是整个网络共同处理一个操作的成本。因此,矿工有动机仅包含那些总效用收益超过成本的交易。
然而,在现实中,与这些假设存在几个重要的偏差:
- 矿工处理交易支付的成本确实高于其他验证节点,因为额外的验证时间会延迟区块传播,从而增加区块成为废块的机会。
- 确实存在非挖矿的全节点。
- 在实践中,算力分布最终可能会变得极其不平等。
- 确实存在投机者、政治敌人和疯子,他们的效用函数包括对网络造成损害,并且他们可以巧妙地设置合约,使其成本远低于其他验证节点支付的成本。
(1) 使矿工倾向于包含更少的交易,而 (2) 增加了 NC;因此,这两种效应至少部分地相互抵消了。如何抵消? (在新标签页中打开)
(3) 和 (4) 是主要问题;为了解决它们,我们只需制定一个浮动上限:任何区块的操作数都不能超过长期指数移动平均值的 BLK_LIMIT_FACTOR 倍。具体来说:
blk.oplimit = floor((blk.parent.oplimit \* (EMAFACTOR - 1) +
floor(parent.opcount \* BLK\_LIMIT\_FACTOR)) / EMA\_FACTOR)
BLK_LIMIT_FACTOR 和 EMA_FACTOR 是常量,目前将设置为 65536 和 1.5,但在进一步分析后可能会更改。
在比特币中,还有另一个因素会抑制大区块大小:大区块需要更长的时间来传播,因此成为废块的可能性更高。在以太坊中,消耗大量 Gas 的区块也可能需要更长的时间来传播,这既因为它们在物理上更大,也因为它们需要更长的时间来处理交易状态转换以进行验证。这种延迟抑制因素在比特币中是一个重要的考虑因素,但在以太坊中由于 GHOST 协议的存在,其重要性较低;因此,依赖受监管的区块限制提供了一个更稳定的基准。
计算与图灵完备性
需要注意的重要一点是,以太坊虚拟机 (EVM) 是图灵完备的;这意味着 EVM 代码可以编码任何可以想象到的计算,包括无限循环。EVM 代码允许以两种方式进行循环。首先,有一个 JUMP 指令允许程序跳回到代码中先前的位置,以及一个 JUMPI 指令进行条件跳转,从而允许像 while x < 27: x = x * 2 这样的语句。其次,合约可以调用其他合约,从而可能允许通过递归进行循环。这自然引出了一个问题:恶意用户能否通过迫使矿工和全节点进入无限循环来实质上关闭它们?出现这个问题是因为计算机科学中一个被称为停机问题的难题:在一般情况下,无法判断给定的程序是否会停止。
正如状态转换部分所述,我们的解决方案是通过要求交易设置其允许执行的最大计算步数来工作的,如果执行时间过长,计算将被回退,但仍需支付费用。消息的工作方式相同。为了展示我们解决方案背后的动机,请考虑以下示例:
- 攻击者创建一个运行无限循环的合约,然后向矿工发送一笔激活该循环的交易。矿工将处理该交易,运行无限循环,并等待其耗尽 Gas。即使执行耗尽了 Gas 并在中途停止,该交易仍然有效,并且矿工仍然会向攻击者收取每个计算步骤的费用。
- 攻击者创建一个非常长的无限循环,意图迫使矿工持续计算很长时间,以至于在计算完成时,已经产出了更多区块,矿工将无法包含该交易以收取费用。然而,攻击者将被要求提交一个
STARTGAS值来限制执行可以采取的计算步数,因此矿工会提前知道计算将花费过多的步数。 - 攻击者看到一个包含类似
send(A,contract.storage[A]); contract.storage[A] = 0形式代码的合约,并发送一笔带有刚好足够运行第一步但不足以运行第二步的 Gas 的交易(即进行提款但不让余额减少)。合约作者不需要担心防范此类攻击,因为如果执行在中途停止,更改将被回退。 - 一个金融合约通过获取九个专有数据源的中间值来工作,以最小化风险。攻击者接管了其中一个数据源(该数据源被设计为可通过去中心化自治组织 (DAO) 部分描述的变量地址调用机制进行修改),并将其转换为运行无限循环,从而试图迫使任何从金融合约中申领资金的尝试耗尽 Gas。然而,金融合约可以在消息上设置 gas 上限来防止这个问题。
图灵完备性的替代方案是图灵不完备性,其中不存在 JUMP 和 JUMPI,并且在任何给定时间,调用栈中只允许存在每个合约的一个副本。在这种系统下,所描述的费用系统以及围绕我们解决方案有效性的不确定性可能就不必要了,因为执行合约的成本将受其大小的上限限制。此外,图灵不完备性甚至不是一个很大的限制;在我们内部构思的所有合约示例中,到目前为止只有一个需要循环,甚至那个循环也可以通过将一行代码重复 26 次来消除。鉴于图灵完备性的严重影响以及有限的好处,为什么不干脆使用一种图灵不完备的语言呢?然而,在现实中,图灵不完备性远非解决该问题的完美方案。要了解原因,请考虑以下合约:
C0: call(C1); call(C1);
C1: call(C2); call(C2);
C2: call(C3); call(C3);
...
C49: call(C50); call(C50);
C50: (run one step of a program and record the change in storage)
现在,向 A 发送一笔交易。因此,在 51 笔交易中,我们拥有了一个占用 250 个计算步骤的合约。矿工可以尝试提前检测此类逻辑炸弹,方法是在每个合约旁边维护一个值,指定它可以采取的最大计算步数,并为递归调用其他合约的合约计算此值,但这将要求矿工禁止创建其他合约的合约(因为上述所有 26 个合约的创建和执行可以很容易地合并到一个合约中)。另一个有问题的地方是,消息的地址字段是一个变量,因此通常甚至可能无法提前判断给定合约将调用哪些其他合约。因此,总而言之,我们得出了一个令人惊讶的结论:图灵完备性出人意料地易于管理,而缺乏图灵完备性同样出人意料地难以管理,除非实施完全相同的控制措施——但在那种情况下,为什么不直接让协议成为图灵完备的呢?
货币与发行
以太坊网络包含其内置货币——以太币,它具有双重目的:提供主要的流动性层以允许在各种类型的数字资产之间进行高效兑换,更重要的是,提供一种支付交易费的机制。为了方便起见并避免未来的争论(参见目前比特币中关于 mBTC/uBTC/satoshi 的争论),面额将被预先标记:
- 1: Wei
- 1012: 萨博
- 1015: 芬尼
- 1018: 以太币
这应该被视为“美元”和“美分”或“BTC”和“satoshi”概念的扩展版本。在不久的将来,我们预计“以太币”将用于普通交易,“芬尼”用于微交易,而“萨博”和“Wei”用于围绕费用和协议实现的技术讨论;其余的面额稍后可能会变得有用,目前不应包含在客户端中。
发行模型将如下所示:
- 以太币将以每 BTC 兑换 1000-2000 个以太币的价格在货币发售中发布,该机制旨在为以太坊组织提供资金并支付开发费用,并且已被 Mastercoin 和 NXT 等其他平台成功使用。早期买家将受益于更大的折扣。从发售中收到的 BTC 将全部用于支付开发人员的工资和赏金,并投资于以太坊和加密货币生态系统中的各种营利性和非营利性项目。
- 售出总量的 0.099 倍(60102216 ETH)将分配给该组织,以补偿早期贡献者并支付创世区块之前以 ETH 计价的费用。
- 售出总量的 0.099 倍将作为长期储备保留。
- 在此之后,每年将售出总量的 0.26 倍永久分配给矿工。
| 群体 | 启动时 | 1 年后 | 5 年后 |
|---|---|---|---|
| 货币单位 | 1.198X | 1.458X | 2.498X |
| 购买者 | 83.5% | 68.6% | 40.0% |
| 预售消耗的储备 | 8.26% | 6.79% | 3.96% |
| 售后使用的储备 | 8.26% | 6.79% | 3.96% |
| 矿工 | 0% | 17.8% | 52.0% |
长期供应增长率(百分比)
尽管货币发行是线性的,但就像比特币一样,随着时间的推移,供应增长率仍然趋于零。
上述模型中的两个主要选择是 (1) 捐赠池的存在和规模,以及 (2) 存在永久增长的线性供应,而不是像比特币那样的上限供应。捐赠池的理由如下。如果捐赠池不存在,并且线性发行减少到 0.217 倍以提供相同的通货膨胀率,那么以太币的总量将减少 16.5%,因此每个单位的价值将增加 19.8%。因此,在均衡状态下,发售中将多购买 19.8% 的以太币,因此每个单位的价值将再次与以前完全一样。那么该组织也将拥有 1.198 倍的 BTC,这可以被认为分为两部分:原始的 BTC 和额外的 0.198 倍。因此,这种情况与捐赠_完全等效_,但有一个重要的区别:该组织纯粹持有 BTC,因此没有动机去支持以太币单位的价值。
永久的线性供应增长模型降低了某些人认为的比特币中财富过度集中的风险,并为生活在现在和未来时代的人们提供了获取货币单位的公平机会,同时保留了获取和持有以太币的强烈动机,因为作为百分比的“供应增长率”随着时间的推移仍然趋于零。我们还推论,由于随着时间的推移,代币总是会因为粗心、死亡等原因而丢失,并且代币丢失可以建模为每年总供应量的百分比,因此流通中的总货币供应量实际上最终将稳定在一个等于年发行量除以丢失率的值(例如,在 1% 的丢失率下,一旦供应量达到 26X,那么每年将挖出 0.26X 并丢失 0.26X,从而形成均衡)。
请注意,在未来,以太坊很可能会为了安全性而切换到权益证明 (PoS) 模型,从而将发行需求降低到每年 0 到 0.05X 之间。如果以太坊组织失去资金或因任何其他原因消失,我们留下了一份开放的“社会契约”:任何人都有权创建以太坊的未来候选版本,唯一的条件是以太币的数量最多必须等于 60102216 * (1.198 + 0.26 * n),其中 n 是创世区块之后的年数。创建者可以自由地众筹或以其他方式分配 PoS 驱动的供应扩张与最大允许供应扩张之间差额的部分或全部,以支付开发费用。不符合社会契约的候选升级可能会被合理地分叉为合规版本。
挖矿中心化
比特币挖矿算法的工作原理是让矿工对稍微修改过的区块头版本一遍又一遍地计算数百万次 SHA256,直到最终一个节点得出一个哈希小于目标值(目前约为 2192)的版本。然而,这种挖矿算法容易受到两种形式的中心化影响。首先,挖矿生态系统已经由 ASIC(专用集成电路)主导,这是一种专为比特币挖矿特定任务设计的计算机芯片,因此效率高出数千倍。这意味着比特币挖矿不再是一项高度去中心化的和平等主义的追求,而是需要数百万美元的资本才能有效参与。其次,大多数比特币矿工实际上并不在本地执行区块验证;相反,他们依赖中心化的矿池来提供区块头。这个问题可以说更糟:在撰写本文时,排名前三的矿池间接控制了比特币网络中大约 50% 的处理能力,尽管由于如果一个矿池或联盟尝试进行 51%攻击,矿工可以切换到其他矿池,这一事实在一定程度上缓解了这个问题。
以太坊目前的意图是使用一种挖矿算法,要求矿工从状态中获取随机数据,计算区块链中最后 N 个区块中随机选择的一些交易,并返回结果的哈希。这有两个重要的好处。首先,以太坊合约可以包含任何类型的计算,因此以太坊 ASIC 本质上将是用于通用计算的 ASIC——即更好的 CPU。其次,挖矿需要访问整个区块链,迫使矿工存储整个区块链,并且至少能够验证每笔交易。这消除了对中心化矿池的需求;尽管矿池仍然可以发挥平衡奖励分配随机性的合法作用,但没有中央控制的点对点矿池同样可以很好地发挥这一功能。
这种模型尚未经过测试,在使用合约执行作为挖矿算法时,在避免某些巧妙的优化方面可能会遇到困难。然而,该算法一个特别有趣的特征是,它允许任何人“在井里投毒”,通过在区块链中引入大量专门设计用于阻碍某些 ASIC 的合约。ASIC 制造商存在使用这种技巧互相攻击的经济动机。因此,我们正在开发的解决方案最终是一种适应性的经济人类解决方案,而不仅仅是纯粹的技术解决方案。
可扩展性
关于以太坊的一个普遍担忧是可扩展性问题。像比特币一样,以太坊也存在每笔交易都需要由网络中的每个节点处理的缺陷。对于比特币,当前区块链的大小约为 15 GB,以每小时约 1 MB 的速度增长。如果比特币网络要处理 Visa 每秒 2000 笔的交易,它将以每三秒 1 MB 的速度增长(每小时 1 GB,每年 8 TB)。以太坊可能会经历类似的增长模式,更糟糕的是,以太坊区块链之上将有许多应用程序,而不仅仅是像比特币那样的货币,但由于以太坊全节点只需要存储状态而不是整个区块链历史,这一点得到了改善。
如此庞大的区块链大小带来的问题是中心化风险。如果区块链大小增加到(比如说)100 TB,那么可能的情况是只有极少数大型企业会运行全节点,而所有普通用户都使用轻量级 SPV 节点。在这种情况下,就会产生潜在的担忧:全节点可能会联合起来,并一致同意以某种有利可图的方式作弊(例如,更改区块奖励,给自己发放 BTC)。轻节点将无法立即检测到这一点。当然,至少可能存在一个诚实的全节点,几个小时后,关于欺诈的信息会通过 Reddit 等渠道泄露出来,但到那时就太晚了:这将取决于普通用户组织起来将给定的区块列入黑名单,这是一个与成功发起 51%攻击规模相似的巨大且可能不可行的协调问题。在比特币的情况下,这目前是一个问题,但存在一个由 Peter Todd 建议的 (在新标签页中打开)区块链修改方案,它将缓解这个问题。
在短期内,以太坊将使用两种额外的策略来应对这个问题。首先,由于基于区块链的挖矿算法,至少每个矿工都将被迫成为全节点,从而为全节点的数量设定了下限。然而,其次也是更重要的一点,我们将在处理每笔交易后在区块链中包含一个中间状态树根。即使区块验证是中心化的,只要存在一个诚实的验证节点,就可以通过验证协议规避中心化问题。如果矿工发布了一个无效区块,那么该区块要么格式错误,要么状态 S[n] 不正确。由于已知 S[0] 是正确的,因此必然存在某个第一个不正确的状态 S[i],而 S[i-1] 是正确的。验证节点将提供索引 i,以及由需要处理 APPLY(S[i-1],TX[i]) -> S[i] 的帕特里夏树节点子集组成的“无效性证明”。节点将能够使用这些节点来运行该部分计算,并发现生成的 S[i] 与提供的 S[i] 不匹配。
另一种更复杂的攻击涉及恶意矿工发布不完整的区块,因此甚至不存在完整的信息来确定区块是否有效。对此的解决方案是质询-响应协议:验证节点以目标交易索引的形式发出“质询”,轻节点在收到节点后将该区块视为不可信,直到另一个节点(无论是矿工还是其他验证者)提供帕特里夏节点的子集作为有效性证明。
结论
以太坊协议最初被构想为一种升级版的加密货币,通过一种高度通用的编程语言提供链上托管、提款限制、金融合约、博彩市场等高级功能。以太坊协议不会直接“支持”任何应用,但图灵完备编程语言的存在意味着理论上可以为任何交易类型或应用创建任意合约。然而,以太坊更有趣的地方在于,以太坊协议远远超越了单纯的货币范畴。围绕去中心化文件存储、去中心化计算和去中心化预测市场等数十种概念的协议,有潜力大幅提高计算行业的效率,并通过首次添加经济层,为其他点对点协议提供巨大的推动力。最后,还有大量与金钱完全无关的应用。
以太坊协议所实现的任意状态转换函数的概念,提供了一个具有独特潜力的平台;以太坊在设计上是开放式的,而不是一个旨在用于数据存储、博彩或金融领域特定应用的封闭式、单一用途的协议。我们相信,在未来的岁月里,它非常适合作为大量金融和非金融协议的基础层。
注释与延伸阅读
注释
- 细心的读者可能会注意到,实际上比特币地址是椭圆曲线公钥的哈希,而不是公钥本身。然而,在密码学中,将公钥哈希称为公钥本身是完全合法的术语。这是因为比特币的密码学可以被视为一种自定义的数字签名算法,其中公钥由 ECC 公钥的哈希组成,签名由 ECC 公钥与 ECC 签名拼接而成,而验证算法包括将签名中的 ECC 公钥与作为公钥提供的 ECC 公钥哈希进行比对,然后使用 ECC 公钥验证 ECC 签名。
- 从技术上讲,是前 11 个区块的中位数。
- 在内部,2 和 "CHARLIE" 都是数字,后者采用大端序的 256 进制表示。数字最小为 0,最大为 2256-1。
延伸阅读
- 内在价值 (在新标签页中打开)
- 智能财产 (在新标签页中打开)
- 智能合约 (在新标签页中打开)
- B-money (在新标签页中打开)
- 可重用的工作量证明 (在新标签页中打开)
- 具有所有者权限的安全产权 (在新标签页中打开)
- 比特币白皮书 (在新标签页中打开)
- Namecoin (在新标签页中打开)
- Zooko 三角 (在新标签页中打开)
- 彩色币白皮书 (在新标签页中打开)
- Mastercoin 白皮书 (在新标签页中打开)
- 去中心化自治公司,比特币杂志 (在新标签页中打开)
- 简易支付验证 (在新标签页中打开)
- 默克尔树 (在新标签页中打开)
- 帕特里夏树 (在新标签页中打开)
- GHOST (在新标签页中打开)
- StorJ 与自治代理,Jeff Garzik (在新标签页中打开)
- Mike Hearn 在图灵节上关于智能财产的演讲 (在新标签页中打开)
- 以太坊 RLP
- 以太坊默克尔帕特里夏树
- Peter Todd 关于默克尔求和树的探讨 (在新标签页中打开)
有关白皮书的历史,请参阅此 wiki (在新标签页中打开)。
与许多社区驱动的开源软件项目一样,以太坊自最初成立以来也在不断发展。要了解以太坊的最新发展以及协议是如何更改的,我们推荐阅读本指南。





