主页 > imtoken钱包下载教程 > 以太坊设计原则

以太坊设计原则

imtoken钱包下载教程 2023-06-12 06:12:05

尽管以太坊的许多想法已经在早期的加密货币(如比特币)中使用和测试了 5 年,但在处理一些不同的协议功能方面,以太坊仍然有一种通用的做事方式。很多时候,以太坊被用来构建全新的经济方法,因为它具有其他系统所没有的许多功能。本文将详细描述以太坊的所有潜在优势以及构建以太坊协议过程中的一些争议点。此外,我们的解决方案和替代解决方案的潜在风险也会被指出。

原则

以太坊协议的设计遵循以下原则:

1.三明治复杂模型

我们认为以太坊的底层协议应该尽可能简单,界面设计应该通俗易懂,那些不可避免的复杂部分应该放在中间层。中间层不是核心共识的一部分,对最终用户是不可见的。它包括:高级语言编译器、参数序列化和反序列化脚本、存储数据结构模型、leveldb 存储接口和有线协议。当然,这个设置也不是绝对的。

2.自由

不应限制用户使用以太坊协议,也不应试图支持或排除某些以太坊合约或交易。这类似于“网络中立”概念背后的指导原则。比特币交易协议不遵循这个原则。在比特币交易协议中,不鼓励使用区块链进行“去标签”(例如,数据存储、元协议)。在某些情况下,对齐协议的重大更改可能导致未经授权使用区块链来攻击应用程序。因此,在以太坊中,我们强烈建议设置交易费用,用户使用区块链的步骤越多,交易费用就越高(类似于庇古税)。这样,交易费就可以作为对合法矿工的奖励,犯罪分子可以付出代价,一石二鸟。

3.泛化

以太坊协议的特性和操作码应最大限度地体现低级概念(如基本粒子),以便它们可以任意组合。因此,通过剥离那些不需要的功能来使低级概念更有效。遵循这一原则的一个例子是我们选择 LOG 操作码作为向 dapps 提供信息的一种方式以太坊挖矿原理,而不是像以前那样记录所有事务和消息信息。早期,“消息”的概念完全是各种概念的集合,其中包括“函数调用”和“外部观察者感兴趣的事件”,两者是完全可分离的。

4.没有特征是最大的特征点

为了遵循泛化原则,我们拒绝将那些高级用例作为协议的一部分嵌入,即使它们是经常使用的用例,也从不这样做。如果真的想实现这些用例,可以在合约中创建子协议(例如,基于以太坊的子货币、比特币/莱特币/狗狗币的侧链等)。例如,以太坊缺乏类似于比特币的“时间锁定”功能。但是这个函数可以通过如下协议来模拟:用户将签名的数据包发送给特定的合约进行处理,如果数据包在特定的合约中有效,则执行相应的函数。

5.无风险规避机制

如果风险的增加带来可观的收益(例如,广义状态转换、区块时间增加 50 倍、共识效率),我们愿意承担更高的风险。

这些原则指导着以太坊的发展,但也不是绝对的;在某些情况下,为了减少开发时间或者不想一次做太多的改动,我们也可以延迟做某些改动,留待以后修改。

区块链层协议

本节介绍以太坊中区块链层协议的变化,包括区块和交易如何工作,数据如何序列化以及存储和账户背后的机制。

账户,不是UTXO①

比特币及其许多衍生品将用户的余额信息存储在 UTXO 结构中,系统的整个状态由一系列“有效”输出组成(将这些“有效输出”视为硬币)。每个 UTXO 都有一个所有者和自己的价值属性。一笔交易会消耗几个 UTXO,也会产生几个新的 UTXO。“有效输出”必须满足以下约束:

1.每个引用的输入都必须有效且未使用;

2.交易的签名必须与每个输入的所有者的签名匹配;

3.输入的总值必须等于或大于输出的总值。

因此,在比特币系统中,用户的“余额”是用户私钥能够有效签名的所有UTXO的总和。下图展示了比特币系统中的交易输入输出过程:

不过,以太坊放弃了 UTXO 方案,使用了一种更简单的方法:使用状态的概念来存储一系列账户,每个账户都有自己的余额,以及以太坊特有的数据(代码或内部存储器)。如果交易发起人的账户余额足以支付交易费用,则交易有效,将从发起人账户中扣除相应金额,并计入收款账户。在某些情况下,如果承兑账户中有代码需要执行,交易会触发代码的执行,那么账户的内存可能会发生变化,甚至可能会创建额外的信息并发送给其他账户,导致新的事务发生。

虽然以太坊没有采用UTXO的概念,但UTXO也有一些优势:

1.更高程度的隐私保护

如果用户为每笔交易使用新地址,则难以关联账户。这对于具有高安全性要求的货币系统很好,但不适用于任何 dapp 应用程序。因为 dapp 经常需要跟踪用户复杂的绑定状态,所以 dapp 的状态不能像货币系统中的状态那样简单地划分。

2.潜在的可扩展性

UTXO 理论上更具可扩展性。因为我们只能依靠那些金融币拥有者来维护能够证明货币所有权的默克尔树,即使每个人(包括数据拥有者)都忘记了某个数据,真正受损的只有数据拥有者,其他人不受影响。

在以太坊账户系统中,如果 Merkle 树中某个账户对应的信息被所有人丢失,那么该账户将无法处理任何可能影响它的消息,包括发送给它的消息。消息,它也无法处理。

但是,不仅UTXO可以扩展,还有不依赖UTXO的方式进行扩展(这里没有扩展,译者注)。

帐户的好处如下:

1.节省大量空间

如果一个账户有 5 个 UTXO,从 UTXO 模式切换账户模式所需的空间将从 300 字节减少到 30 字节。具体计算如下:

300 = (20+32+8)* 5 (20是地址字节数,32是TX的id字节数,8是交易金额值的字节数)) ;

30 = 20 + 8 + 2(20为地址字节数,8为交易量字节数,2为nonce字节数②)

但实际节省的资金并没有那么大,因为帐户需要存储在 Patricia 树中。此外,以太坊中的交易比比特币中的交易小(以太坊中为 100 字节,比特币中为 200-250 字节),因为每笔交易 A 交易只需要生成引用、签名和输出。

2.更高的可替代性

在UTXO结构中,“有效输出”的源码实现中没有区块链层的概念,所以无论从技术上还是法律上,创建一个红名单/黑名单并通过来源来区分它们都不是很实用这些“有效输出”。

3.简单

以太坊编码更简单、更容易理解,尤其是在涉及复杂脚本时。尽管任何去中心化应用程序都可以以 UTXO 方式实现,但这种方式本质上是通过给脚本一种方式来限制给定 UTXO 可以使用的 UTXO 类型以及请求的 UTXO 类型,包括更改的 Merkle 树证明脚本评估的应用程序的根状态。帐户要复杂得多。

4.轻客户端

轻客户端可以通过在指定方向扫描状态树随时访问与账户相关的所有数据。在UTXO方式中,引用随着每笔交易而变化,这对于使用长期运行的dapp来说无疑是繁重的。 UTXO 根状态传播机制如前所述。

我们相信帐户的好处远远超过其他方法,尤其是对于我们正在处理的包含任意状态和代码的 dapp。另外,本着“没有特征就是最好的特征”的指导原则,我们相信如果用户真正关心隐私,可以通过合约中的签名包协议建立一个加密“混合器”进行加密。

account 方法的一个弱点是,为了防止重放攻击,每个 A 交易都必须有一个 nonce,这使得帐户需要跟踪 nonce 的使用情况,并且只在 nonce 有效时才接受交易last used 并且值为 1。这意味着即使不再使用的帐户也无法从帐户状态中检索。解决这个问题的一个简单方法是让交易包含一个块号,这样它们就不会在几个时间段内重复,并在每个时间段重置随机数。由于完全扫描账户成本太高,所以为了删除未使用的账户,矿工或用户需要“Ping”操作来实现。我们在1.0 上没有实现这个机制,1.1 及以上可以使用这个机制。

默克尔帕特里夏树

Merkle Patricia tree/trie,由 Alan Reiner 构思并在 Ripple 协议实现中获得,是以太坊的主要数据结构,用于存储所有账户状态,以及每个区块中的交易和收据数据。 MPT 是 Merkle 树和 Patricia 树的组合首字母缩写词,将这两种树组合在一起创建具有以下属性的结构:

1.每个唯一键值对唯一映射到根的哈希值;在 MPT 中,不可能只用一个键值对欺骗成员(除非攻击者有 ~2^128 的计算能力);

2.添加、删除、修改键值对的时间复杂度是对数的。

MPT 为我们提供了一个高效、易于更新、代表整个状态树的“指纹”。

关于 MPT 的更多详情:

MPT的具体设计决策如下:

1.有两种类型的节点

KV 节点和离散节点。 KV 节点的存在提高了效率,因为如果树在特定区域是稀疏的,KV 节点可以作为“捷径”而不是深度为 64 的树。

2.离散节点是十六进制的,不是二进制的

这使得搜索更高效,我们现在意识到这个选项并不理想,因为 hex 可以通过批量存储节点来模拟二进制树的查找效率。 MPT 树的结构实现非常容易出错,最终导致至少状态根不匹配。

3.空值和非会员没有区别

这样做是为了简化逻辑,在以太坊中没有设置值默认为0,空字符串也用0表示。不过需要强调的是,这样做牺牲了一些通用性,因此并不是最优的。

4.终端节点和非终端节点的区别

技术上,不需要“是否是终端节点”来标识一个节点,因为所有的树都是用来存储静态密钥长度的,但是为了增加通用性,我们会添加这个标志,希望以太坊的 MPT其他加密货币将按原样采用实施。

5.使用SHA3(k)作为安全树中的key(用于状态树和账户存储树)

通过设置离散节点链的深度为 64 来使用 SHA3(k),并且对 SLOAD 和 SSTORE 指令的重复调用使得对树进行 DoS 的尝试非常困难。但是,这也使得详尽的树节点变得困难,如果您希望您的客户端是详尽的,最简单的方法是维护一个数据库映射:sha3(k) -> k

RLP

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

RLP 编码是以太坊中的主要序列化格式,它无处不在:区块、交易、账户状态和有线协议消息。详见RLP​​官方说明:

RLP 旨在成为一种高度简化的序列化格式,其唯一目的是存储嵌套字节数组③。与 protobuf 和 BSON 等现有解决方案不同,RLP 没有定义任何特定的数据类型,例如 Boolean、floa、double 或 integer。它只是将结构存储为嵌套数组,并将其留给协议来确定数组的含义。 RLP 也没有明确支持地图集。半官方的建议是使用[[k1, v1], [k2, v2], ...]的嵌套数组来表示键值对的集合,k1, k2 ...按照字符标准排序字符串。

RLP 的替代方案是 protobuf 或 BSON,它们是一直在使用的算法。但是,在以太坊中,我们更喜欢使用 RLP,因为:

1.很容易实现;

2.绝对保证字节一致性。

很多语言没有明确的 Map 集合排序,浮点格式有很多特殊情况,会导致相同数据的编码和散列不同。通过内部开发协议,我们可以确保在设计时考虑到这些目标(这是一个通用原则,也适用于代码的其他部分,例如 VM)。 BitTorrent 使用的编码方法 bencode 可能是 RLP 的替代方案。但它采用十进制编码方式,比二进制RLP略逊一筹。

压缩算法

线路协议和数据库都使用自定义压缩算法来存储数据。该算法可以描述为:游程编码④值为0,其他值保留(除了一些特殊情况,如sha3('')),例如:

>>> compress('horse')
'horse'
>>> compress('donkey dragon 1231231243')
'donkey dragon 1231231243'
>>> compress('\xf8\xaf\xf8\xab\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbe{b\xd5\xcd\x8d\x87\x97')
'\xf8\xaf\xf8\xab\xa0\xfe\x9e\xbe{b\xd5\xcd\x8d\x87\x97'
>>> compress("\xc5\xd2F\x01\x86\xf7#<\x92~}\xb2\xdc\xc7\x03\xc0\xe5\x00\xb6S\xca\x82';{\xfa\xd8\x04]\x85\xa4p")
‘\xfe\x01'

在压缩算法出现之前,以太坊协议很多地方都有一些特殊情况,比如sha3经常被覆盖,所以sha3('')='',不需要把代码存放在帐户,可节省 64 个字节。然而,最近,所有这些使以太坊数据结构臃肿的特殊情况都已被删除,而是在区块链协议之外的层中添加了数据保存功能,也就是将其放入有线协议并将其放入插入用户数据库实现中这增加了模块化,简化了共识层,并允许压缩算法不断更新,从而相对容易部署。

树的使用(trie)

以太坊区块链中的每个区块头都包含指向三棵树的指针:状态树、交易树和收据树。

状态树表示访问块后的整个状态;

交易树代表区块中的所有交易,由索引索引键控; (例如,k0:第一个执行的事务,k1:第二个执行的事务)

收据树代表每笔交易对应的收据。交易收据是一个 RLP 编码的数据结构:

[ medstate,gas_used,logbloom,日志]

地点:

•medstate:事务处理后,树根的状态;

•gas_used:交易处理后使用的gas量;

•logs:是表[地址,[topic1,topic2.。 .], data] 元素列表。该表由事务执行期间(包括主调用和子调用)调用的操作码LOG0...LOG4生成; address 是生成日志主题的合约地址,最多 4 个 32 字节的值; data 是任意字一个节大小的数组;

• logbloom:布隆过滤器⑤由事务中所有日志的地址和主题组成。

区块头中还有一个布隆过滤器,它是区块中所有交易的布隆过滤器的逻辑或组合。这种结构使得以太坊协议轻客户端的使用尽可能的友好。

叔块奖励(废弃块)

GHOST 协议于 2013 年 10 月由 Jonsner 和特拉维夫首次提出,是一项了不起的创新。这是加快区块时间的第一次认真尝试。因为一个区块在网络中传播需要一定的时间,如果矿工 A 挖了一个区块并广播到全网,而 B 也在广播的途中挖了一个区块,那么 B 的区块就已经过时了,并且B 的块已过期。这种挖掘对网络的安全性没有贡献。 GHOST的目的是解决过时挖矿导致安全性降低的问题。

另外还有一个中心化问题:如果A是一个矿池,它有30%的算力,B有10%的算力。 A 70% 的时间产生陈旧的区块(因为其他 30% 的时间产生最新的区块,所以它立即挖掘数据),而 B 90% 的时间产生陈旧的区块。如果出块间隔很短,那么陈旧率就会很高,A 会凭借更大的算力让挖矿更高效。因此,如果出块速度过快,很容易导致网络算力大的矿池控制挖矿过程。

根据乔纳森和特拉维夫的说法,GHOST 解决了在计算哪条链是最长链的过程中产生陈旧块导致的网络安全性下降问题。即不仅父区块和更早的区块,还加上叔块⑥来计算哪个区块的工作量证明最大。

为了解决第二个问题:中心化,我们采取了不同的策略:同样为过时区块提供区块奖励:挖掘过时区块的奖励是区块8的基本奖励的7/;挖掘包含过时区块的侄子区块将获得基础奖励的 1/32 作为赏金。但是,交易费用不会奖励给叔块或侄子块。

在以太坊中,只有在过时分叉的 7 代内的相关区块才能被称为过时区块。这种限制的原因是,首先,如果 GHOST 协议不限制过期区块的数量,那么在计算过期区块的有效性时会花费大量开销;对链上挖矿的热情;最后,计算表明,7 层内过时的块奖励政策限制提供了大部分预期效果而没有负面影响。

块时间算法的设计决策包括:

•阻塞时间12s:选择12s是因为它已经是最快的时间了,基本上比网络延迟要长。在 2013 年的一篇关于测量比特币网络延迟的论文中,确定 12.6 秒是新生成的块传播到 95% 的节点的时间;但是,该论文还指出,传播时间与块大小成正比,因此在更快的货币中,我们可以预期传播时间会大大减少。传播间隔时间是恒定的,大约 2 秒。但是,为了安全起见,在我们的分析中,我们假设区块传播需要 12 秒。

• 7 个祖先块的限制:这种设计的目的是只保留少量的块,并清除较早的块。已显示 7 个块可提供大部分所需的效果。

• 1 个后代块限制:例如 c(c(p(p(p(p(head))))) c=child, p = parent,这是不合法的,因为它有两个后代块。这是设计的为简单起见,上面的模拟结果表明它不会带来很大的中心化风险。

• **叔块需要有效性:**叔块必须是有效的标头,而不是有效的块。这也是为了简单起见,将区块链模型保持为线性数据结构。但是,要求叔块是有效块也是合法的。

•奖励分配:7/8的挖矿基础奖励分配给叔块,1/32分配给侄子块,两者的交易费用均为0%。如果费用占多数,从中心化的角度来看,这将导致叔块激励无效;然而,这就是为什么只要我们继续使用 PoW,以太坊就会继续发行以太币。

难度更新算法

目前以太坊通过以下规则更新难度:

diff(genesis) = 2^32
diff(block) = diff.block.parent + floor(diff.block.parent / 1024) *
    1 if block.timestamp - block.parent.timestamp < 9 else
    -1 if block.timestamp - block.parent.timestamp >= 9

难度更新规则设计目标如下:

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

•低波动性:如果算力恒定,则难度不会大幅波动;

• 简单:算法的实现应该比较简单;

• 低内存:算法不应依赖过多的历史块,可能较少使用“内存变量”。假设有最新的十个block,加上这十个block的headers中存储的内存变量,这些block可以用于算法的计算;

•非开发:算法不应赋予矿工篡改时间戳或矿池的能力,并反复添加或移除算力以最大化他们的利润。

我们目前的算法对于低波动性和非开发性来说并不理想,至少我们计划切换时间戳来比较父块和祖块,所以矿工只连续挖了2个块,只有修改时间戳的动机。另一个更强大的模拟公式:

汽油和费用

比特币中的所有交易大致相同,因此可以对其网络成本进行建模。以太坊中的交易比较复杂,所以交易费用需要考虑到账户的很多方面,包括宽带费、存储费、计算费等。尤其重要的是,以太坊编程语言是图灵完备的,因此交易使用任意数量的带宽、存储和计算成本。这可能会导致在计算成本的过程中突然断电,计算被迫中止。

以太坊交易手续费的基本机制如下:

• 每笔交易必须指定一定数量的gas(即指定startgas的值),以及支付每单位gas所需的费用(即gasprice),在交易执行开始时,该值startgas * gasprice 将从发送者账户中扣除;

• 事务执行过程中的所有操作,包括读写数据库、发送消息,每一步计算都会消耗一定的gas;

• 如果交易完成且gas消耗小于指定的限制值,交易将正常执行,剩余的gas值将赋值给变量gas_rem;交易完成后 交易完成后,发送方将收到以太币gas_rem * gasprice,矿工奖励为(startgas - gas_rem) * gasprice 以太币价值;

• 如果交易执行,gas 消耗耗尽,所有执行都恢复到原来的状态,但交易仍然有效,但交易的唯一结果是向矿工支付 startgas * gasprice in ether 的值,其他不变;

• 当一个合约发送消息时对于另一个合约,可以设置由该消息引起的子执行的gas限制。如果子执行的gas用完了,子执行会继续,但是gas还是会被消耗。

必须满足以上几点,例如:

• 如果交易没有指定gas限制,那么恶意用户将发送一个包含数十亿步交易的循环。没有人能够处理这样的交易,因为提前处理网络上的矿工可能需要很长时间,这会导致拒绝服务漏洞。

• 严格气体计数、时间限制等机制的替代方案不起作用,因为它们过于主观

• startgas * gasprice 的整个值,应该在开头设置好,这样在交易执行过程中不会因为gas不足而终止交易。请注意,仅检查帐户余额是不够的,因为帐户可以将余额发送到其他地方。

• 如果在gas不足的情况下交易执行没有恢复(回滚),合约必须采取强有力的安全措施来防止合约发生变化。

• 如果子限制不存在,恶意账户可以通过与其他账户签订协议对其他账户进行拒绝服务攻击。在计算开始时插入一个大循环,然后向受害者合约发送消息,或任何补救措施,都会使整个交易陷入僵局。

• 需要交易发送者而不是合约来支付gas,大大提高了开发者的可操作性。以太坊的早期版本使用合约来支付gas,这导致了一个相当严重的问题:每个合约都必须实现“监护人”代码,以确保每条传入的消息都有足够的以太币来消耗。

耗气量计算具有以下特点:

• 对于任何交易,将收取 21000 gas 的基本费用。这些费用可用于支付运行椭圆曲线算法所需的成本。该算法旨在从签名中恢复发件人的地址以及用于存储交易的硬盘驱动器和带宽空间。

• 一笔交易可以包含无限量的“数据”。虚拟机中的某些操作码允许合约允许交易访问这些数据。数据的固定消耗计算为:每个零字节4个gas,非零字节68个gas。之所以出现这个公式,是因为合约中的大部分交易数据都由一系列 32 字节的参数组成,其中大部分参数都有许多前导零字节。这种结构可能看起来效率低下,但由于压缩算法,它实际上非常有效。我们希望这种结构能够取代其他更复杂的机制,这些机制严格按照预期的字节数打包参数,从而在编译阶段造成很大的复杂性。这是三明治复杂模型的一个例外,但由于成本效益比,它也是一个合理的模型。

• 用于设置账户内存的操作码SSTORE的消耗为: 1.零值变为非零值时,消耗20000gas; 2.将零值变为零值,或者将非零值变为非零值,消耗5000gas; 3. 将非零值转换为零值,消耗 5000 gas,加上交易执行成功后返回的 20000 gas。最高退款金额为交易消耗的总gas的50%。这样的设置会激励人们清除记忆。我们注意到,由于缺乏这样的激励,很多合约导致存储空间没有得到有效利用,导致存储快速膨胀;收取存储费用提供了许多好处,而不会丢失一旦建立保证就可以永久存在的合同。延迟退款机制对于防止拒绝服务攻击是必要的。攻击者发送一笔带有少量gas的交易,循环清空大量存储,直到耗尽gas,消耗大量验证算力,但实际上并没有清空存储或消耗大量gas。 50%的上限是为了保证已经获得一定交易gas的旷工仍然可以确定执行交易的计算时间上限。

• 合同为消息提供的数据是免费的。由于在消息调用期间不需要大量复制数据,因此调用数据可以简单地视为指向父合约内存的指针,在子进程执行时不会更改。

• 内存是一个可无限扩展的数组,但是每扩展32字节的内存将消耗1个gas成本,小于32字节的将被计为32字节。

• 一些操作码的计算时间非常依赖于参数,并且gas成本计算动态变化。例如,EXP 的开销是指数级的;复制操作码(例如 CALLDATACOPY、CODECOPY、EXTCODECOPY)的开销为 1+1(每次复制 32 个字节)。这里不包括内存扩展的开销,因为它会触发二次攻击。

• 如果该值非零,CALL 操作码会消耗额外的 9000 gas。这是因为任何价值转移都会导致归档节点的历史存储显着增长。请注意,实际消耗为 6700。基于此,我们强制增加自动给接收器的 gas 值。此值至少为 2300。这样做是为了让接受交易的钱包至少有足够的气体来记录交易。

gas 机制的另一个重要部分是 gas 价格本身的经济学。在比特币中,默认的做法是采取纯粹自愿的收费方式,由矿工充当看门人并动态设置最低费用。 Ethereum allows transaction senders to set any amount of gas. This approach is very popular in the Bitcoin community because it is the embodiment of a "market economy": allowing miners and traders to determine prices based on supply and demand. The problem with this approach, however, is that transaction processing does not follow market principles. Although transaction processing can be thought of as a service that miners provide to senders (which sounds intuitive), in reality every transaction processed by a miner must be processed by every node in the network, so most of the transaction processing The costs are all borne by third-party institutions, not the miners who decide whether to process it or not.

Currently, due to the lack of clear information on the behavior of miners in practice, we will adopt a very simple and fair method: a voting system to set the gas limit. Miners have the right to set the gas limit of the current block within 0.0975% (1/1024) of the gas limit of the last block. So the final gas limit should be the miners The intermediate value of the setting. We hope to use a soft fork method in the future to use a more accurate algorithm.

Virtual machine

Ethereum virtual machine is the engine that executes transaction code, and also The core difference between Ethereum and other systems. Note that the virtual machine should be considered separately from the "contract and message model". For example, the SIGNEXTEND opcode is a function of the virtual machine, but in fact "a contract calls other contracts and specifies the child The "gas limit for calls" is part of the "Contract and Message Model". The EVM is designed with the following goals:

Simple: as few opcodes as possible and low-level; as few data types as possible; Structure as little as possible;

• Unambiguous results: There is no room for ambiguity in the VM specification statement, and results should be completely deterministic. Also, the computational steps should be precise so that gas can be measured

• Space saving: EVM components should be as compact as possible;

• Expected applications should be specialized: applications built on VM should be able to handle 20 bytes address, and a 32-bit custom encryption value, with the ability to use modulus operations for custom encryption, read blocks, and interact with transaction data and state;

•Simple and secure: In order to allow VM Not being exploited, it should be easy to make operations that build a gas cost model;

Optimization friendly: should be easy to optimize so that just-in-time compilation (JIT) and accelerated versions of the VM can be built.

EVM also has the following special designs:

• The difference between temporary/permanent storage:

Let's take a look at what temporary storage and permanent storage are.

p>

Temporary storage: exists in each instance of the VM and disappears after the VM execution ends;

Permanent storage: exists in the blockchain state layer.

Suppose the following tree is executed (S for permanent storage, M for temporary storage):

1.A calls B;

2.B sets B.S[0 ]=5, B.M[0]=9 ;

3.B calls C;

4.C calls B.

At this point, if B tries to read B.S[0], it will get the data stored before B, which is 5; but if B tries to read B.M[0], it will get 0, because B.M is temporary storage, read When fetching it is a new instance of the virtual machine.

In an internal call, if you set B.M[0] = 13 and B.S[0] = 17, then the internal call Both the external call and the call of C are terminated, and then the external call of B is executed. At this time, when M is read, you will see that B.M[0] = 9 (this value was set in the last instance of the same VM execution), B.S[0 ] = 17. If B's ​​external call ends, and then A calls B again, you'll see B.M[0] = 0 and B.S[0] = 17. The purpose of this distinction is: 1.Each execution instance is allocated memory space that is not detracted by loop calls, which makes safe programming easier. 2.Provide a form of memory that can operate quickly: because the tree needs to be modified, storing updates is necessarily slow.

• Stack/Memory Mode

In the early days, there were three computing states: stack (stack, a 32-byte standard LIFO), memory (memory, infinitely expandable temporary bytes) array), storage (storage, permanent storage). On the temporary storage side, the alternatives to stack and memory are the memory-only paradigm, or a mix of registers and memory (there is little difference between the two, and registers are essentially a type of memory). In this case, each instruction has three parameters, eg: ADD R1 R2 R3: M[R1] = M[R2] + M[R3] . The stack paradigm was chosen for obvious reasons, it made the code 4x smaller.

• Word size 32 bytes

In most structures, such as Bitcoin, the word size is 4 or 8 bytes. 4 or 8 bytes are too limiting for storage addresses or cryptographic calculations.而太大的值又很难建立相应安全的gas模型。 32字节是一个理想大小,因为它足够存储下许多加密算法的实现以及地址,又不会因为太大而导致效率低下。

• 我们有自己的虚拟机

我们的虚拟机使用java、Lisp或Lua等语言开发。我们认为开发一款专业的虚拟机是值得的,因为:

1. 我们的VM规范比其他许多虚拟机简单的多,因为其他虚拟机为复杂性付出的代价更小,也就是说它们更容易变得复杂;然而,在我们的方案中每额外增加一点复杂性,都会给集约化发展带来障碍,以及潜在的安全缺陷,比如造成共识失败,这就让我们的复杂性成本很高,因而不容易造成复杂;

2. 我们的VM更加专业化,如支持32字节;

3. 我们不会有复杂的外部依赖,复杂的外部依赖会导致我们安装失败;

4. 完善的审查机制,可以具体到特殊的安全需求;对外部VM而言,这一点无论如何都是必要的。

• 使用了可变、可扩展的内存大小

固定内存的大小是不必要的限制,太小或太大都不合适。如果内存大小是固定的,每次访问内存都需要检查访问是否超出边界,显然这样的效率并不高。

• 栈大小没有限制

没什么特别理由!许多情况下,该设计不是绝对必要的;因为,gas的开销和区块层gas的限制总是会充当每种资源消耗的上限。

• 1024调用深度限制

许多编程语言在栈的深度过大时触发中断比在内存过载时触发中断的策略要快的多。所以区块中gas限制所隐含的限制是不够的。

• 无类型

为了简单起见,可以使用DIV, SDIV, MOD, SMOD的有符号或无符号的操作码代替(事实证明,对于操作码ADD和MUL,有符号和无符号是对等的);转换成定点运算在所有情况下都很简单,例如,在32位深度下,a * b -> (a * b) / 2^32, a / b -> a * 2^32 / b ,+, - 和 * 在整数下不变。

VM中某些操作码的函数和作用很容易理解,但也有一些不太好理解,以下是一些特殊的原因:

*• ADDMOD, MULMOD *

大多数情况下,addmod(a, b, c) = a * b % c,但在椭圆曲线算法中,使用的是32字节模数运算,直接执行a * b % c 实际上是在执行((a * b) % 2^256) % c会得到完全不同的结果。计算公式a * b % c 使用32字节空间的32字节值是非常不常见且繁琐。

• SIGNEXTEND

SIGNEXTEND操作码的作用是为了方便从大的有符号整数到小的有符号整数的类型转换。小的有符号整数是很有用的,因为未来的即时编译虚拟机可能会检测长时间运行的代码块,小的有符号整数能加快处理。

• SHA3

在以太坊代码中SHA3作为安全的高强度的hash集合应用非常广泛,通常在使用存储器时需要使用Hash函数来确保安全,以防止恶意冲突,在验证默克尔树和类似以太坊的数据结构时也需要使用到Hash函数。重要的是,与SHA3的相似的hash函数,如SHA256,ECRECVOR,RIPEM160不是以操作码的形式包含在里面,而是以伪合约的形式。这样做的目的是将它们放在一个单独的类别中,如果当我们以后提出适当的“本地扩展”系统时,可以添加更多这样的合约,而不需要扩展操作码。

• ORIGIN

ORIGIN操作码由交易的发送者提供,主要的作用是允许合约退回支付的gas。

• COINBASE

COINBASE的主要作用是:1.如果使用COINBASE操作码,则允许子货币对网络安全作出贡献;2.开放使用矿工作为一个去中心化的经济体,来设置基于子共识的应用,如Schellingcoin。

• PREVHASH

PREVHASH作为一个半安全的随机来源,允许合约评估上一个区块的默克尔树状态证明,而不需要高度复杂的递归结构“以太坊轻客户端”。

• EXTCODESIZE, EXTCODECOPY

主要的作用是让合约依据模板检查其他合约的代码,甚至是在与其他合约交互前,模拟它们。

• JUMP DEST

当跳转(jump)目的地限制在几个索引时(尤其是,动态目的跳转的计算复杂度是O(log(有效挑战目的数量)),而静态跳转总是恒定的),JIT虚拟机实现起来更简单。于是,我们需要:1.对有效变量跳转目的地做限制;2.使用静态而不是动态跳转的激励方式。为了达到这两个目标,我们定下了以下规则:1.紧接着push后的跳转可以跳到任何地方,而不仅是另一个jump;2.其他的jump只能跳转到JUMPDEST。对跳转的限制是必须的,你可以通过查看代码中的先前操作来决定是静态还是动态的跳转。缺乏对静态跳转的需求是激励使用它们的原因。禁止跳转进入push数据也会加快JIT虚拟机的编译和执行。

*• LOG *

LOG是事件的日志。

• CALLCODE

该操作码允许合约使用自己的存储器,在单独的栈空间和内存中调用其他合约的“函数”。这样可以在区块链上灵活实现标准库代码。

• SELFDESTRUCT

允许合约删除它自己,前提是它已经不需要存在了。 SELFDESTRUCT并非立即执行,而是在交易执行完之后执行。这是因为这样做可以恢复那些执行后大大增加了缓存复杂度的SELFDESTRUCT操作码。

• PC

尽管理论上不需要PC操作码,因为所有的PC操作码都可以根据将push操作的索引加入实际程序计数器来代替实现,但使用PC可以创建独立代码的位置(可复制粘贴到其他合约的编译函数,如果它们以不同索引结束,不要打断)。

注释:

① 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:A挖出区块后广播途中,B也挖出了区块(过时区块),此时区块链会出现分叉。过时分叉上的区块就叫uncle区块。它不是这个块的父区块,父区块的兄弟区块(平级关系)。

原文: