作者: Vitalik;翻译&校正: kim & 阿剑

从某些协议功用的处理办法上来说,以太坊与常见密码学钱银运转办法仍有许多不同。

编者注:本译文的首个版别见于此处,本次再版现已过校正。在此对原译者 kim 标明感谢。
原文的写作时刻不承认,但可将其视为一个原点,反思以太坊的规划理念以及以太坊在这几年间的演化。既是反思其立异,也是反思其有欠考虑的当地。

虽然以太坊的许多理念在新近的密码学钱银(如比特币)上现已运用并测试了5年之久,但从某些协议功用的处理办法上来说,以太坊与常见办法仍有许多不同。并且,以太坊可用于开发全新的经济工具,由于它具有其他体系不具有的许多功用。本文会详细描述以太坊一切的潜在优点,以及在构建以太坊协议进程中某些有争议的当地。别的,也会指出咱们的计划及替代计划的潜在危险。

准则

以太坊协议的规划遵从以下几点准则:

  • 三明治杂乱模型(亦可译为 “杂乱度分层模型” ):咱们以为以太坊的底层协议应尽或许的简略,接口规划应易于了解(不论是面向开发者的高档编程言语接口,仍是面向用户的运用接口)。那些不行避免的杂乱部分应放入中间层。中间层不作文中心一致的一部分,且对终究用户不行见,它包括:高档言语编译器、参数序列化和反序列化脚本、存储数据结构模型、leveldb 存储接口以及联网协议等。当然,差异的界线不是肯定清晰的,有时分需求酌情调整。

  • 自在:不该约束用户运用以太坊协议,也不该企图优先支撑或不支撑某些以太坊合约或买卖。这一点与 “网络中立” 概念背面的辅导准则相似。比特币买卖协议就 没有 遵从这一准则:比特币买卖协议并不鼓舞区块链的 “十分规用处(off-labal purpose)” (如,数据存储,元协议)(校正注:off-labal 的原意为将药物用在其经过批准的适应症之外的症状上,例如运用止咳药来治疗头痛。此处意译为 “十分规用处” );并且,有时分还有人用 准-协议层 的变更(例如将 OP_RETURN 字段的长度约束在 40 字节)来进犯以 “未经授权” 的办法运用区块链的运用(校正注:此处是在挖苦比特币的社区有查看比特币区块链用法的倾向)。因而,在以太坊,咱们坚决支撑仅运用买卖手续费来到达大体鼓励相容的办法 —— 用户耗费整个网络越多资源,需求付出的价值就越高,也即使其自己承当本钱(即庇古税)。

  • 泛化:以太坊协议的特性和操作码应最大限度地表现低层次的概念(就像根本粒子相同),以便它们能够随意组合,包括组合出今日看来没什么用、但未来或许有用的东西。并且,经过剥离那些不需求的功用,低层次的概念能够愈加高效。遵从这一准则的例子是,咱们挑选 LOG 操作码作为向 dapp 供给信息的办法,而不是像之前那样记录下一切买卖和音讯。在新近,“音讯(message)” 的概念完完全全是多种概念的调集,它包括 “函数调用(function call)” 和 “外在观察者感兴趣的事情信息(event)” ,而两者是完全能够别离开来的。

  • 没有特色便是最大的特色:为了遵从泛化准则,咱们拒绝将那些高档用例内嵌为协议的一部分,哪怕是经常运用的用例,也绝不这么做。假如人们真的想完成这些用例,能够在合约内创立子协议(如,根据以太坊的子钱银,比特币/莱特币/狗币的侧链等)。比方,在以太坊中就缺少相似比特币中的 “时刻锁” 功用。可是,经过以下协议能够模仿出这个功用:用户发送签名数据包到特定的合约中处理,假如数据包在特定合约中有用,则履行相应的函数。

  • 不厌恶危险:假如危险的添加带来了可观的优点,咱们乐意承当更高的危险(例如,通用的状况转化,出块时刻减低 50 倍,一致功率,等等)。

这些准则辅导着以太坊的开发,但它们并不是肯定的;某些情况下,为了削减开发时刻或许不期望一次作出过多改动,也会使咱们推延作出某些修正,把它留到将来的版别中去修正。

区块链层协议

本节对以太坊中区块链层协议的改动进行了描述,包括区块和买卖是怎么工作的、数据怎么序列化及存储、账户背面的机制。

账户 ,而非 UTXO 1

比特币及其许多变种,都将用户的余额信息存储在 UTXO 结构中,体系的整个状况由一系列的 “未花费的输出” 组成(能够将这些 “未花费的输出” 想象成钱币)(校正注:更好的一个比方或许是 “支票”。)。每个 UTXO 都有具有者和自身的价值特色。一笔买卖在消费若干个 UTXO 一起也会生成若干个新的 UTXO;而买卖遭到下列有用性要求的约束:

1.每个被引证的输入有必要有用,且未被运用过;2.买卖的签名有必要与每笔输入的一切者签名匹配;3.输入的总值有必要等于或大于输出的总值。
因而,比特币体系中,用户的 “余额” 是该用户的私钥能够有用签名的一切 UTXO 的总和。下图展现了比特币体系中买卖输入输出进程:

V神设计理念公布,细数以太坊潜在的优缺点

比特币所用的三式记账法

可是,以太坊抛弃了 UTXO 的计划,转而运用更简略的办法:选用状况(state)的概念存储一系列账户,每个账户都有自己的余额,以及以太坊特有的数据(代码和内部存储器)。假如买卖建议方的账户余额满足付出买卖费用,则买卖有用,那么建议方账户会扣除相应金额,而接纳账户则计入该金额。某些情况下,接纳账户内有需求履行的代码,则买卖会触发该代码的履行,那么账户的内部存储器或许会发生改变,乃至或许会创立额定的音讯发送给其他账户,从而导致新的买卖发生。

虽然以太坊没有选用 UTXO 的概念,但 UTXO 也不乏有一些优点:

  • 较高程度的隐私保护:假如用户每次买卖都运用一个新的地址,那么账户之间的彼此关联就很困难。这样做适用于对安全性要求高的钱银体系,但不是对任何 dapp 都合适。由于 dapp 一般需求跟踪用户杂乱的绑定状况,而 dapp 的状况并不能像钱银体系中的状况那样简略地区分。

  • 潜在的可扩展性:理论上来说,UTXO 与某些类型的可扩展性计划(scalability paradigm)更符合,由于只需持币者具有能够证明自己钱银一切权的默克尔证明即可,即使一切的人(包括 TA 本人)都遗忘了这一数据,真实受损也这个人,其他人不受影响。在以太坊账户体系中,假如一切人都丢失了某个账户对应的默克尔树部分,那么该账户将无法处理任何能够影响它的音讯,包括发送给它的音讯,它也无法处理。不过,并非只有 UTXO 能够可扩展,也存在不依靠 UTXO 就能扩展的办法(此处没有扩展开来讲,译者注)。

账户的优点有以下几点:

  • 节约很多空间:假如一个账户有 5 个 UTXO,则从 UTXO 办法转成账户办法,所需空间会从 300 字节降到 30 字节。详细核算如下:300 = (20+32+8)* 5 (20 是地址字节数,32 是 TX 的 id 字节数,8 是面额占用的字节数); 30 = 20 + 8 + 2 (20 是地址字节数,8 是账户余额值字节数,2 是 nonce 2 字节数);但实践节约并没有这么大,由于账户需求被存储在帕特里夏树中。别的以太坊中买卖也比比特币中的更小(以太坊中 100 字节,比特币中 200-250 字节),由于每次买卖只需求生成一次引证,一次签名,以及一个输出。

  • 可互换性更强:UTXO 结构并没有区块链层的概念,所以不管是在技能仍是法律上,经过树立一个红名单/黑名单,并根据的这些 “有用输出” 的来历差异它们并不是很实践。

  • 简略:以太坊编码更简略、更易于了解,尤其是在涉及到杂乱脚本时。虽然任何去中心化运用都能够用 UTXO 办法来(勉强)完成,但这种办法实质上是赋予脚本约束给定的 UTXO 所能输出的 UTXO 的种类及其运用条件(比方需求包括默克尔树证明来帮助脚本所对应的运用更改状况根)的才能。因而,UTXO 完成办法比以太坊运用账户的办法要杂乱的多。

  • 轻客户端:轻客户端能够随时经过沿指定方向扫描状况树来拜访与账户相关的一切数据。在 UTXO 范式中,每笔买卖需求用到的引证都不同,这关于长时刻运转并运用了上文说到的 UTXO 根状况传达机制的 dapp 运用来说,无疑是深重的。

咱们以为,账户的优点大大超过了其他办法,尤其是关于咱们想要支撑的、可包括恣意状况和代码的 dapp 运用而言。别的,本着 “没有特色便是最大的特色” 的辅导准则,咱们以为假如用户真的关心私密性,则能够经过合约中的签名数据包协议来树立一个加密 “混币器(mixer and coinjoin)” 混杂付出途径。

账户办法的一个弱点是:为了阻挠重放进犯(replay attack,指让同一笔买卖重复履行),每笔买卖有必要有一个 “nonce”(流水号)。因而,每个账户都要有一个实时更新的 nonce 值,每一笔新买卖都在账户 nonce 值上递加 1 作为自己的 nonce(并在买卖处理之后按此值更新账户的 nonce 值)(校正注:在账户办法下,假如买卖不附带这种耗费性的标识符,买卖就可被重复处理,这样接纳账户能够一遍又一遍地收账且不必付出任何价值,而发账的账户会被吸干;以太坊账户的 nonce 随所建议的买卖得到处理而递加,就处理了这个问题)。这就意味着,即使不再运用的账户,也不能从账户状况中移除。处理这个问题的一个简略办法是让买卖包括一个区块号,使它们在一段时刻后就无法再被重放,并且每隔一段时刻段重置 nonce。

若要在状况中删去某个账户(比方长期不运用的账户),就有必要先 “ping” 出它们来,而完好扫描区块链协议的开支是十分大的。在1.0上咱们没有完成这个机制,1.1及以上版别或许会运用这个机制。

校正注:这便是以太坊日后面临的 “状况爆炸” 问题的技能原因:一切状况数据有必要完好保存,无法合理地删去账户。作为一种区块链协议,以太坊的节点不仅要对事务(买卖)的次序到达一致,还要对大局状况到达一致(表现办法便是区块头里需求包括状况根。因而,若要删去状况,也需求全网的一致,否则会堕入割裂。
校正注:这种以 nonce 来标记账户买卖次序的做法,也使得用户的买卖有必要次序履行,假如一笔买卖无法得到处理,运用后续 nonce 的买卖也无法得到处理。

默克尔帕特里夏树(MPT)

默克尔帕特里夏树(Merkle Patricia tree/trie),由 Alan Reiner 提出设想,并在瑞波协议中得到完成,是以太坊的首要数据结构,用于存储一切账户状况,以及每个区块中的买卖和收据数据。MPT 是默克尔树和帕特里夏树的结合,结合这两种树创立的结构具有以下特色:

  • 任一组 键-值对 所对应的根哈希值都是仅有的,想要谎报某个 键值对 存在于某棵树上是必定会被识破的(除非进犯者具有约 2^128 的算力)。

  • 增、删、改 一个键值对的时刻杂乱度是对数等级。

MPT为咱们供给了一个高效、易更新、且代表整个状况树的 “指纹” 。关于MPT更详细描述:https://github.com/ethereum/wiki/wiki/Patricia-Tree。

MPT的详细规划决议计划如下:

  • 有两类节点:KV 节点和离散节点。KV节点的存在提高了功率,由于假如在特定区域树是稀少的,KV节点可作为一个 “捷径” 来紧缩树的高度(阅览 MPT 的胪陈可了解更多细节)。

  • 离散节点是十六进制,不是二进制:这样让查找更有用率,咱们现在认识到这种挑选并不抱负,由于十六进制树的查找功率在二进制中能够经过批次存储节点来模仿。可是,MPT 树结构的完成是十分简略犯错的,终究至少会形成状况根不匹配,所以咱们决定放置变更,等到 1.1 版别再说。

  • 空值(empty value)与非成员(non-membership)之间没有差异:这样做是为了简化逻辑,以太坊中未启用的账户的值(余额)默以为 0,空字符串也用 0 标明。可是,需求着重的是,这样做献身了一些通用性,因而也不是最优的。

  • 终节点(terminating)和非终节点的差异:技能上,标识一个节点 “是否是终节点” 是没必要的,由于以太坊中一切的树都被用于存储固定长度(即键的长度)的数据,但为了添加通用性,咱们仍是会添加这个标识,以期望以太坊的 MPT 的完成办法能够被其他密码学钱银原样采纳。

  • 在 “安全树”(状况树和账户存储树)中选用 SHA3(k) 作为键:运用 SHA3(k),想要经过生成许多的账户(账户最多可让状况树高达 64 层!)偏重复调用 SLOAD 和 SSTORE 操作码来 DoS 进犯的难度会大大提高。留意,这也让枚举树变得更困难;假如要使你的客户端具有枚举的功用,最简略的办法便是保护一个映射 sha3(k) -> k 的数据库。

校正注:这儿的意思是,假如运用 k 作为默克尔树存储数据的键,其散布或许很稀少,而进犯者能够简略地规划出需求很深的树途径来存储的账户,并对这些账户重复调用状况拜访操作,以此形成网络中的节点超负荷运转,可是,哈希函数的成果是随机散布的,以 sha3(k) 作为键能够使键的散布较为均匀,树高也会较矮)。
这种特性也是有得有失,这一方面意味着 DoS 进犯会变得更困难,另一方面,也使得一个区块中的买卖的状况树拜访途径,很少有重合的,因而每次查找都是杂乱度最差的景象。
此外,这也使得 MPT 不宜完成 “无状况性”(区块自身带着验证所需的数据、验证者无需具有大局状况),由于状况拜访的途径不重合,证据的空间功率也是最差景象。当然,也能够说,默克尔树证据的空间功率自身也不行高

RLP

RLP(recursive length prefix):递归长度前缀。

RLP 编码是以太坊中首要的序列化格局,它的运用无处不在:区块、买卖、账户状况以及网络协议音讯。详见 RLP 正式描述: https://github.com/ethereum/wiki/wiki/RLP

RLP 旨在成为高度简化的序列化格局,它仅有的意图是存储嵌套的字节数组 3。不同于 protobuf、BSON 等现有的处理计划,RLP并不定义任何指定的数据类型,如 Boolean(布尔值)、float(浮点数)、double 或许 integer(整数)。它仅仅是以嵌套数组的办法存储结构体,由协议来承认数组的意义。RLP 也没有显式支撑 map 调集,半官方的建议是选用 [[k1, v1], [k2, v2], ...] 的嵌套数组来标明键值对调集,k1,k2 ... 按照字符串的标准排序。

与 RLP 具有相同功用的计划是 protobuf 或 BSON,它们是一直被运用的算法。可是,以太坊中,咱们更偏向于运用 RLP,由于:(1)它易于完成;(2)肯定确保字节的一致性。

许多言语的键值对调集没有清晰的排序,并且浮点格局有许多特别情况,这或许形成相同数据却发生不同编码和不同哈希值。经过内部开发协议,咱们能确保它是带着这些方针规划的(这是一般准则,也适用于代码的其他部分,如虚拟机)。BitTorrent 运用的编码办法 bencode 或许能够替代 RLP。不过它选用的是十进制的编码办法,与选用二进制的 RLP 比较,略微差劲了点。

紧缩算法

网络协议和数据库都选用了一个自定义的紧缩算法来存储数据。该算法可描述为:对 0 运用行程编码 4 并一起保存其他值(除了一些特别情况如 sha3(' ') ),举例如下:

紧缩算法存在之前,以太坊协议的许多当地都有一些特别情况,例如,sha3 经常被重定义使得 sha3(' ')=' ',这样不需求在账户中存储代码,能够节约 64 字节。可是,最近一切这些使得以太坊数据结构变得臃肿的特别情况都被删去了,取而代之的是将数据保存函数添加到区块链协议之外的层,也便是将其放入网络协议以及将其刺进用户数据库完成。这样添加了模块化才能,简化了一致层,使得对紧缩算法的持续更新布置起来相对简略(例如:可经过网络协议的版别号来差异、布置)。

树(trie)的运用

提醒:了解这部分的常识需求读者了解布隆过滤器 5 的原理。简介可见:http://en.wikipedia.org/wiki/Bloom_filter

以太坊区块链中每个区块头都包括指向三个树的指针:状况树、买卖树、收据树。

  • 状况树代表处理完该区块后的整个状况;

  • 买卖树代表区块中一切买卖,这些买卖由 index 索引作为key;(例如,k0:第一个履行的买卖,k1:第二个履行的买卖)

  • 收据树代表每笔买卖相应的收据。

买卖的收据是一个 RLP 编码的数据结构:

  • 其间:

  • medstate:买卖处理后,状况树的根;

  • gas_used:买卖处理后,gas 的运用量;

  • logs:是许多 [address, [topic1, topic2...], data] 元素的列表。这些元素由买卖履行期间调用的操作码 LOG0 ... LOG4 生成(包括主调用和子调用);address 是生成日志的合约的地址;topics 是最多 4 个 32 字节的值;data 是恣意巨细的字节数组;

  • logbloom:买卖中一切 logs 的 address 和 topics 组成的布隆过滤器。

区块头中也存在一个布隆过滤器,它是区块中买卖的一切布隆过滤器的或运算(OR)成果。这样的结构使得以太坊协议对轻客户端友好得无以复加。

注释:

  • UTXO:unspent transaction outputs,字面了解是:未花费的买卖输出,也即未被任何买卖引证为输入的买卖输出。它是比特币协议中用于存储价值(一切权)信息的数据结构。—— 校正注

  • Nonce,Number used once 或 Number once 的缩写,在密码学中 Nonce 是一个只被运用一次的恣意或非重复的随机数值,在加密技能中的初始向量和加密哈希函数都发挥着重要效果,在各类验证协议的通信运用中确保验证信息不被重复运用以对抗重放进犯(Replay Attack)。—— 译者注

  • 嵌套数组:创立一个数组,并运用其他数组填充该数组。如数组 pets:
    var cats : String[] = ["Cat","Beansprout", "Pumpkin", "Max"];
    var dogs : String[] = ["Dog","Oly","Sib"];
    var pets : String = [cats, dogs];

    —— 译者注

  • 行程编码(run-length-encoding):一种统计编码。首要技能是检测重复的比特或字符序列,并用它们的呈现次数取而代之。(百度百科)—— 译者注

  • 布隆过滤器:由 Howard Bloom 在 1970 年提出的二进制向量数据结构,它具有很好的空间和时刻功率,被用来检测一个元素是不是调集中的一个成员。(百度百科)—— 译者注

叔块(uncle blocks)奖赏

GHOST 协议是一项不起的立异,由 Yonatan Sompolinsky 和 Aviv Zohar 在 2013 年 10 月初次提出的。它是处理快速出块伴生问题的第一个仔细测验。

GHOST 的意图是处理这样一个难题:更短的出块时刻(因而承认速度会更快)会导致有更多区块 “过期” 因而安全性会下降 —— 由于区块在网络中传达需求必定时刻,假如矿工 A 挖到一个区块并向全网广播,在广播的路上,B 也挖出了区块,那么 B 的区块是过期的,且 B 的本次挖矿对网络的安全没有贡献。

此外,还有一个中心化问题:假如 A 是一个矿池,有 30% 的算力,B 有 10% 的算力。A有 70% 的时刻发生过期的区块(由于别的的 30% 时刻会发生最新区块,能够为 TA “立即” 得到了最新块的数据而无需等待区块传达),而 B 有 90% 的时刻发生过期区块。假如区块的产出时刻距离很短,那么过期率就会变高,则 A 凭借其更大的算力使挖矿功率也更高。所以,区块生成过快,简略导致网络算力大的矿池在事实上垄断挖矿进程。

根据 Sompolinsky 和 Zohar的描述,GHOST 处理了在核算哪个链是最长的链的进程中,因发生过期区块而形成的网络安全性下降的问题。也便是说,不仅是父区块和更早的区块,一起过期的旁支区块(在以太坊中,咱们称之为 “叔块”)也被添加到核算哪个块具有最大的总工作量证明中去。

为了处理第二个问题:中心化问题,咱们选用了另一种策略:对过期区块也供给区块奖赏:挖到过期区块的奖赏是该区块根底奖赏的 7/8;而包括过期区块的侄子区块将收到 1/32 的根底奖赏作为赏金。可是,买卖费不会奖赏给叔块和侄块。

在以太坊中,过期区块只能被其兄弟区块的 7 代以内的直系后代区块包括为叔块。之所以这样约束是由于,首先,GHOST 协议若不约束过期区块的代际距离,将会花费很多开支在核算过期区块的有用性上;其次,无约束的过期区块鼓励方针会让矿工失掉在主链上挖矿的热情;最后,核算标明,过期区块奖赏方针约束在 7 层内供给了大部分所需的效果,并且不会带来负面效应。

  • 度量中心化危险的一个模仿器可见此处:https://github.com/ethereum/economic-modeling/blob/master/ghost.py

  • 一个更高层次的讨论可见此处:https://blog.ethereum.org/2014/07/11/toward-a-12-second-block-time/

校正注:此处的 “包括” 在技能上的办法是:侄块在区块头中引证叔块的区块哈希值,然后把叔块的区块头包括在区块体内。

区块时刻算法的规划决议计划包括:

  • 区块时刻 12s:挑选 12 秒是由于这现已是长于网络推迟的最短时刻距离了。在 2013 年的一份关于丈量比特币网络推迟的论文中,承认了 12.6 秒是新发生的区块传到达 95% 节点的时刻;可是,该论文还指出传达时刻与区块巨细成比例,因而在更快的钱银中,咱们能够等待传达时刻大大削减。传达距离时刻是稳定的,约为 2 秒。可是,为了安全起见,在咱们的分析中,咱们假定区块的传达需求 12 秒

  • 7 代先人以内的约束:这样规划的意图是期望只保存少数区块,而将更早之前的区块铲除。现已证明 7 代的可引证规模就能够供给大部分所需的效果。

  • 1 代后嗣的约束:(例如,设 c = child 且 p = parent,则 c(c(p(p(p(head))))) 是无效的):这也是出于简练性的规划方针,并且上述的模仿器显示这不会带来很大的中心化危险。(校正注:此句难解;一种或许的意思是:叔块的后代不能作为叔块,即只有主链的一代旁支能作为叔块。)

  • 叔块有必要是有用的 :叔块有必要是有用的 header,而不是有用的区块。这样做也是为了简化,将区块链模型坚持为线性数据结构(而不会变成 DAG)。不过,要求叔块是有用的区块也是有用的办法。

  • 奖金分配:7/8 的挖矿根底奖赏分配给叔块,1/32 分给侄块,它们买卖费用都是 0%。假如费用占大都,从中心化的视点看,这会使叔块鼓励机制无效;可是,这也是为什么只需咱们持续运用 PoW,以太坊就会不断发行以太币的原因。

难度更新算法

现在以太坊经过以下规矩进行难度更新:

难度更新规矩的规划方针如下:

  • 快速更新:区块间的时刻应该跟着 hash 算力的增减而快速调整;

  • 低波动性:假如挖矿算力稳定,那么难度不该剧烈波动;

  • 简略:算法的完成应相对简略;

  • 低内存:算法不该依靠于过多的前史区块,要尽或许少的运用 “内存变量”。假定有最新的十个区块,将存储在这十个区块头部的内存变量相加,这些区块都可用于算法的核算;

  • 不行爆炸:算法不该让矿工有过多篡改时刻戳或许矿池反复添加或删去算力的鼓励

咱们当时的算法在低波动性和抗爆炸性上并不抱负。最近,咱们计划把时刻戳参数改为与父区块和祖父区块比较,所以矿工只有在接连挖 2 个区块时,才有动力去修正时刻戳。另一个更强大的模仿公式:https://github.com/ethereum/economic-modeling/blob/master/diffadjust/blkdiff.py

Gas 和费用

比特币中一切买卖大体相同,因而它们的网络本钱用单一一种单位来模仿。以太坊中的买卖要更杂乱,所以买卖费用需求考虑到账户的许多方面,包括网络带宽费用、存储费用和核算费用。尤其重要的是,以太坊编程言语是图灵完备的,所以买卖会运用恣意数量的宽带、存储和核算本钱;而终究会运用多少数量是无法牢靠预测的(由于所谓的 “图灵停机问题”)(校正注:即不存在一个牢靠的办法,能够断言恣意可在图灵机上履行的程序会不会在有限步内停止)。避免有人运用无限循环来施行拒绝服务式进犯是咱们的一个关键方针。

以太坊买卖费用的根本机制如下:

  • 每笔买卖有必要指明自身乐意耗费的 gas 数量(即指定 startgas 的值),以及乐意为每单元 gas 付出的费用(即 gasprice ),在买卖履行开始时,startgas * gasprice 价值的以太币会从发送者账户中扣除;(校正注:此处的 startgas 便是咱们现在惯用的 gaslimit 。)

  • 买卖履行期间的一切操作,包括读写数据库、发送音讯以及每一步的核算都会耗费必定数量的 gas;

  • 假如买卖履行完毕,耗费的 gas 值小于指定的约束值,则买卖履行正常,并将剩余的 gas 值赋予变量 gas_rem ; 在买卖完成后,发送者会收到回来的 gas_rem * gasprice 价值的以太币,而给矿工的奖赏是(startgas - gas_rem)* gasprice 价值的以太币;

  • 假如买卖履行中,gas耗费殆尽,则一切的履行康复原样,但买卖依然有用,仅仅买卖的仅有成果是将 startgas * gasprice 价值的以太币付出给矿工,其他不变;

  • 当一个合约发送音讯给另一个合约,能够对这个音讯引起的子履行设置一个 gas 约束。假如子履行耗尽了 gas,则子履行康复原样,但 gas 依然耗费。(校正注:截至本文校正之时(2021 年 7 月 9 日),这一点还未改动,但它在未来有或许会改动。见《值得考虑删去的 EVM 功用》)

上述说到的几点都是有必要满足的,例如:

  • 假如买卖不需求指定 gas 约束,那么歹意用户就会发送一个有数十亿步循环的买卖。没有人能够处理这样的买卖,由于处理这样的买卖花的时刻或许很长很长;可是谁也无法预先奉告网络上的矿工,这就会导致拒绝服务的缝隙发生。

  • 一种替代严厉 gas 计数的办法是时刻约束,但它不行能有用,由于它们太片面了(某些核算机比他人的更快,即使咱们的核算机都相同也依然有或许呈现差池)。

  • startgas * gasprice 的整个值,在开始时就应该设置好,这样不至于在买卖履行中形成该账户 “破产”、无力持续付出 gas 费用。一边履行一边查看余额也不行,由于账户能够把余额放到别的当地。

  • 假如在 gas 不行的情况下,买卖履行不会完全复原(回滚),合约就有必要选用强有力的安全措施来避免合约发生改变。

  • 假如子约束不存在,则歹意账户能够对其他合约施行拒绝服务进犯。进犯者能够先与受害合约到达一致意见,然后在核算进程开始时刺进一个无限循环,那么发送音讯给受害合约或许受害合约的任何弥补测验,都会使整个买卖死锁。(校正注:此句亦难解。)

  • 要求买卖发送者而不是合约来付出 gas,这样大大添加了开发人员的可操作性。以太坊早期的版别是由合约来付出gas的,这导致了一个相当严重的问题:每个合约有必要完成 “门卫” 代码,确保每个传入的音讯为合约供给了满足的以太币供其耗费。

gas 耗费核算有以下特色:

  • 关于任何买卖,都将收取 21000 gas 的根本费用。这些费用可用于付出运转椭圆曲线算法所需的费用(该算法旨在从签名中康复发送者的地址)以及存储买卖所花费的硬盘和带宽空间。

  • 买卖能够包括无限量的 “数据” 。虚拟机中的某些操作码,能够让收到这样买卖的合约拜访这些数据。数据的 “固定耗费量” 规矩是:每个零字节 4 gas,非零字节 68 gas。这个公式的发生是由于用户向合约发送的买卖中,大部分的买卖数据由一系列的 32 字节的参数组成,其间大都参数具有许多前导零字节。该结构看起来好像功率不高,但由于紧缩算法的存在,实践上仍是很有用率的。咱们期望此结构能够替代其他更杂乱的机制:这些机制根据预期字节数严厉包装参数,从而导致编译阶段杂乱性大增。这是三明治杂乱模型的一个例外,但由于本钱效益比,这也是合理的模型。

  • 用于设置账户存储项的操作码 SSTORE 的耗费是:1)将零值改为非零值时,耗费 20000 gas;2)将零值变成零值,或非零值变非零值,耗费 5000 gas;3)将非零值变成零值,耗费 5000 gas;此外,买卖履行成功(即未耗尽 gas 买卖就履行完了)后会退回 15000 gas。退款金额上限是买卖耗费 gas 总额的 50%。这给了人们小小鼓励去铲除存储项。咱们留意到,正由于缺少这样的鼓励,许多合约的存储空间没有被有用运用,从而导致了存储数据的快速胀大。这一规划既能供给 “为存储项持续收取租金” 办法的大部分优点,又不会失掉合约一旦确立就能够永久存在的确保。推迟退款机制是必要的,由于能够阻挠拒绝服务进犯:进犯者能够发送一笔含有少数 gas 的买卖,循环铲除很多的存储项,直到用光 gas,这样耗费了很多的验证算力,但实践并没有真实铲除存储,也不需求付出许多 gas。50% 的上限的是为了确保打包买卖的矿工依然能够承认履行买卖的核算时刻的上限。

  • (校正注:首先,SSTORE 等状况拜访操作码的 gas 耗费量现已跟着以太坊的硬分叉而屡次更改。截至 2021 年 7 月,最新的数值可见《柏林晋级内容概览》;在可预见的未来,这个操作码的数值还会持续改变;其次,这儿的 gas refund 机制,过后证明并没有启动缓解状况数据的胀大问题,反而恶化了该问题,由于人们能够在 gas price 较低时写入很多废物数据,在 gas price 较高时铲除这些数据来获得 gas,这便是 “GasToken” 的原理。当时已承认,在 “伦敦” 分叉中会改动 gas refund 机制,见《以太坊 “伦敦” 晋级预览》。)

  • 合约供给的音讯数据是没有本钱的。由于在音讯调用期间不需求实质仿制任何数据,调用数据(call data)能够简略地视为指向父合约 memory 的指针,该指针在子进程履行时不会改动。

  • Memory 是一个能够无限扩展的数组,可是,每扩展 32 字节的 memory 就会耗费 1 gas 的本钱,不足 32 字节以 32 字节计。(校正注:memory 一般译为内存,但在以太坊的语境下,它是 EVM 由于存储数据的三种类型之一,因而都不译,以示其特别性。)

  • 某些操作码的核算时刻极度依靠参数,gas 开支核算是动态改变的。例如,EXP 的的开支是指数等级的(10 gas + 10 gas/字节,即,x^0 = 1 gas、x^1 … x^255 = 2 gas、x^256 … x^65535 = 3 gas,等等)。仿制操作码(如:CALLDATACOPY, CODECOPY, EXTCODECOPY)的开支是 1 gas + 1 gas/32 字节(四舍五入;LOG 操作码的规矩也相似)。Memory 扩展的开支不包括在这儿。如若包括,会变成一个平方进犯向量(50000 次的 CALLDATACOPY,每次耗费 50000 gas,则其核算量应是 50000^2,但假如不运用动态收费规矩,就只需付出 ~50000 gas)。

  • 假如值不是零,操作码 CALL(以及 CALLCODE)会额定耗费 9000 gas。这是由于任何值传输都会引起归档节点的前史存储显著增大。请留意,操作的 实践耗费 是 6700;可是此根底上,咱们强制添加了一个主动给予接纳者的 gas 值,这个值最小 2300。这样做是为了让接受买卖的钱包至少有满足的 gas 来生成 log。(校正注:见《值得考虑删去的 EVM 功用》)

Gas 机制的另一个重要部分是 gas 价格自身表现出的经济学原理。比特币中,默许的办法是采纳纯粹自愿的收费办法,矿工扮演守门人的人物并且动态设置收费的最小值。以太坊中答应买卖发送者设置恣意数意图 gas。这种办法在比特币社区十分受欢迎,由于它是 “市场经济” 的表现:答应矿工和买卖者之间根据供需关系来决定价格。可是,这种办法的问题是,买卖处理并不遵从市场准则。虽然能够将买卖处理看作是矿工向发送者供给的服务(这听起来很直观),但实践上矿工所处理的每个买卖都有必要由网络中的每个节点处理,所以买卖处理的大部分本钱都由第三方机构承当,而不是决定是否处理它的矿工。因而,“公地悲惨剧” 问题很有或许发生。

当时,由于缺少矿工在实践中的行为的清晰信息,所以咱们将采纳一个十分简略公平的办法:投票体系,来设定单个区块可耗费的 gas 总额。矿工有权将在最新区块的 gas 上限根底上变更 0.0975% (1/1024),作为当时区块的 gas 上限。所以终究的 gas 上限应该是矿工们设置的中间值。咱们期望将来能够选用软分叉的办法来运用愈加准确的算法。

虚拟机

以太坊虚拟机是履行买卖代码的引擎,也是以太坊与其他体系的中心差异。请留意,虚拟机应该同 “合约与音讯模型” 分隔考虑。例如,SIGNEXTEND 操作码是虚拟机的一个功用,但实践上 “某个合约能够调用其他合约并指定子调用的 gas 限定值” 是 “合约与音讯模型” 的一部分。

EVM的规划方针如下:

  • 简略:操作码尽或许的少并且初级;数据类型尽或许少;虚拟机的结构尽或许少;

  • 成果清晰:在 VM 标准中,没有任何或许发生歧义的空间,成果应该是完全承认的。此外,核算步骤应该是准确的,以便能够丈量 gas 的耗费量;

  • 节约空间:EVM 组件应尽或许紧凑;

  • 为预期用处而特化:在 VM 上构建的运用应能处理 20 字节的地址,以及 32 位的自定义加密值,具有用于自定义加密的模数运算、读取区块和买卖数据与状况交互等才能;

  • 简略安全:为了让 VM 不被运用,应该能够简略地让树立一套 gas 耗费本钱模型的操作;

  • 优化友好:应该易于优化,以便即时编译(JIT)和 VM 的加速版别能够构建出来。

一起 EVM 也有如下特别规划:

  • 暂时/永久存储的差异:咱们先来看看什么是暂时存储和永久存储。暂时存储:存在于 VM 的每个实例中,并在 VM 履行完毕后消失。永久存储:存在于区块链状况层。假定履行下面的树(S 代表永久存储,M 代表暂时存储):

  • A调用 B;

  • B 设置 B.S[0]=5,B.M[0]=9 ;

  • B 调用 C;

  • C 调用 B。
    此刻,假如B企图读取 B.S[0] ,它将得到B前面存入的数据,也便是 5;但假如 B 企图读取 B.M[0] ,它将得到 0,由于 B.M 是暂时存储,读取它的时分是虚拟机的一个新的实例。在一个内部调用(inner call)中,假如设置 B.M[0] = 13 和 B.S[0] = 17 ,然后内部调用和 C 的调用都停止、回到了 B 的外部调用(outer call),此刻读取 M,将会看到 B.M[0] = 9 (此值是在上一次同一 VM 履行实例中设置的), B.S[0] = 17 。假如 B 的外部调用完毕,然后 A 再次调用 B,将看到 B.M[0] = 0,B.S[0] = 17 。这个差异的意图是:1.每个履行实例都分配有内存空间,不会由于循环调用而减损,这让安全编程愈加简略。2.供给一个能够快速操作的内存办法:由于需求修正树,所以存储更新必然很慢。

  • 栈/memory 办法:早期,核算状况(除了指向下一个指令的程序计数器)有三种:栈(stack,一个 32 字节标准的 LIFO 栈),内存(memory,可无限延长的暂时字节数组),存储项(storage,永久存储)。在暂时存储端,栈和内存的替代计划是 memory-only 范式,或许是寄存器和内存的混合体(两者差异不大,寄存器本质上也是一种内存)。在这种情况下,每个指令都有三个参数,例如: ADD R1 R2 R3: M[R1] = M[R2] + M[R3] 。挑选栈范式的原因很明显,它使代码缩小了 4 倍。

  • 单词巨细 32 字节:在大大都结构中,如比特币,单词巨细是 4 或 8 字节。4 或 8 字节对存储地址和加密核算来说局限性太大了。而不对巨细作约束又很难树立相应安全的 gas 模型。32 字节是一个抱负巨细,由于它满足存储下许多密码算法所需求的大数值以及地址,又不会由于太大而导致功率低下。
    咱们有自己的虚拟机:咱们的虚拟机运用 java、Lisp 和 Lua 等言语开发。咱们以为开发一款专业的虚拟机是值得的,由于:1)咱们的 VM 标准比其他许多虚拟机简略的多,由于其他虚拟机为杂乱性付出的价值更小,也便是说它们更简略变得杂乱;可是,在咱们的计划中每额定添加一点杂乱性,都会给集约化发展带来妨碍,并带来潜在的安全缺陷,比方一致错误,这就让咱们的杂乱性本钱很高;2)咱们的 VM 愈加专业化,如支撑 32 字节;3)咱们不会有杂乱的外部依靠,杂乱的外部依靠会导致咱们装置失利;4)完善的查看机制,能够详细到特别的安全需求;即使运用外部 VM,也无法节约太多工作量。
    运用了可变、可扩展的 memory 巨细:固定 memory 的巨细是不必要的约束,太小或太大都不合适。假如内存巨细是固定的,每次拜访内存都需求查看拜访是否超出边界,显然这样的功率并不高。
    1024 调用深度约束:许多编程言语在内存还没有溢出时,就由于调用深度太深而溃散了。所以仅运用区块 gas 上限一种约束是不行的。
    无类型:仅仅为了简练。不过,DIV、SDIV、MOD、SMOD 会运用有符号(signed)或无符号的操作码(事实证明,关于操作码 ADD 和 MUL,有符号和无符号是对等的);转化成定点运算在一切情况下都很简略,例如,在 32 位长度下,a * b -> (a * b) / 2^32, a / b -> a * 2^32 / b ,+、- 和 * 在整数下不变。
    校正注:在原译本中还有如下一段,但其对应阶段在当时版别的原文中现已删去了: 栈巨细没有约束:没什么特别理由!许多情况下,该规划不是肯定必要的;由于,gas 的开支和区块 gas 上限总是会充当每种资源耗费的上限。

这个 VM 中某些操作码的功用和意图很简略了解,但也有一些不太好了解,以下是一些特别的原因:

  • ADDMOD, MULMOD:大大都情况下, mulmod(a, b, c) = a * b % c ,但在椭圆曲线算法中,运用的是 32 字节模数运算,直接履行 a * b % c 实践上是在履行 ((a * b) % 2^256) % c ,会得到完全不同的成果。在 32 字节的空间中履行 32 字节数值的 a * b % c 核算的一致十分困难且繁琐。

  • SIGNEXTEND:SIGNEXTEND操作码的效果是为了便利从大的有符号整数到小的有符号整数的类型转化。小的有符号整数是很有用的,由于未来的即时编译虚拟机或许有才能检测首要处理 32 字节整数又长时刻运转的代码块,小的有符号整数能加速处理。

  • SHA3:在以太坊代码中,SHA3 作为安全的、高强度的、不定长数据哈希映射办法,运用十分广泛。一般,在运用存储器时,需求运用 Hash 函数来避免歹意冲突,在验证默克尔树和相似的以太坊数据结构时也需求运用到 Hash 函数。重要的是,与 SHA3 的相似的哈希函数,如 SHA256、ECRECVOR、RIPEM160,不是以操作码的办法包括在里面,而是以伪合约的办法。这样做的意图是将它们放在一个独自的类别中,假如当咱们以后提出恰当的 “原生插件” 体系时,能够添加更多这样的合约,而不需求扩展操作码。

  • ORIGIN:ORIGIN 操作码由买卖的发送者供给,首要的效果是答应合约退回付出的 gas。

  • COINBASE:COINBASE 的首要效果是:1)答应子钱银对网络安全作出贡献;2)使矿工能够作为一个去中心化的经济体,来设置根据子一致的运用,如 Schellingcoin。

  • PREVHASH:PREVHASH 可用作一个半安全的随机来历。此外,答应合约求值(evalute)上一个区块的默克尔树状况证明,而不需求高度杂乱的 “以太坊轻客户端” 递归结构 。

  • EXTCODESIZE, EXTCODECOPY:首要的效果是让合约根据模板查看其他合约的代码,乃至是在与其他合约交互前,模仿它们。见:https://lesswrong.com/lw/aq9/decision_theories_a_less_wrong_primer/

  • JUMPDEST:当跳转(jump)意图地约束在几个索引时(尤其是,动态意图跳转的核算杂乱度是 O(log(有用挑战意图数量)),而静态跳转总是稳定的),JIT 虚拟机完成起来更简略。所以,咱们需求:1)对有用变量跳转意图地做约束;2)鼓励运用静态而不是动态跳转。为了到达这两个方针,咱们定下了以下规矩:1)紧接着 push 后的跳转能够跳到任何当地,而不仅是另一个 jump;2)其他的 jump 只能跳转到 JUMPDEST。对跳转的约束是有必要的,这样就可经过查看代码中的前一个操作来承认当时是一个静态跳转仍是动态跳转。缺少对静态跳转的需求是鼓励运用它们的原因。禁止跳转进入 push 数据也会加速 JIT 虚拟机的编译和履行。

  • LOG:LOG是事情的日志。

  • CALLCODE:该操作码答应合约运用自己的存储项,在独自的栈空间和 memory 中调用其他合约的 “函数” 。这样能够在区块链上灵活完成标准库代码。

  • SELFDESTRUCT:答应合约删去它自己,前提是它现已不需求存在了。SELFDESTRUCT 并非立即履行,而是在买卖履行完之后履行。这是由于假如答应 SELFDESTRUCT 在履行之后回滚,将会极大地提高缓存的杂乱度,不利于高效的 VM 完成。

  • PC:虽然理论上不需求 PC 操作码,由于一切 PC 操作码的实例都能够根据将 push 操作的索引加入实践程序计数器来替代完成,但运用 PC 能够创立独立代码的方位(可仿制粘贴到其他合约的编译函数,假如它们以不同索引完毕,不会被打断)。

视野开拓

要考虑具有普遍性的商业变动,那么我们首先想到的是,这些变动都是通过普遍的交易媒介—货币金星船舶的。货币把所有的经济活动串联在一起。如果一种价格升高,而另一种价格走低,我们可以认为需求从一种行业转向了另一种行业;但是,如果所有的价格都同时升高或者同时降低,那么货币领域准是发生了一些变化。只有货币的需求,和/或者货币的供给,这些因素发生变化,才会引起物价变化。-《美国大萧条》

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注