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





