# Aurora 通过 Engine 2.4.0 版本继续推动 EVM 扩展

By [zeck](https://paragraph.com/@qgstudio) · 2022-03-01

---

![](https://storage.googleapis.com/papyrus_images/af81c005a6405f9e908b1907c8d1126eef5b08fc32cb032cef2a6bd722481578.png)

我们很乐意宣布极光的EVM引擎2.4.0版本的最新发布。尽管它是没有什么危害的版本号，但此版本对我们的几个合作伙伴产生了很大的影响，从而使极光的使命扩大了以太坊的生态系统。

让我们来吧！

总结
--

1.  简短的提醒
    
2.  为什么这次更新对于用户和开发人员来说很重要
    
3.  Aurigami 的案例
    
4.  技术细节
    

### 1) 简短而有用的提醒

Aurora是在NEAR上构建的，跟Ethereum 1.0 相比有很多不同的地方。这里面最重要的一点就是NEAR协议限制了每一个事务(transaction)中能做多少事情，而以太坊协议则是在区块上做了限制。（虽然用户可以为自己的独立的事务(transaction)做限制）

因此，在理论上极光必须将整个以太坊的区块几乎融入到到一个事务(transaction)中。这是非常具有挑战性的，但随着发布的2.4.0版本的引擎，我们完成了一些性能优化，使我们更接近伟大的目标。

### 3）为什么这对用户和开发人员来说都很重要？

很多defi的项目都会遇到这个错误\*\*“Exceeded the maximum amount of gas allowed to burn per contract”\*\*，这会让人很困惑，因为“gas” 和 “contract“ 是以太坊中的术语。

然而，这个错误实际上来自于NEAR的事务(transaction)；“gas”指的NEAR的gas（不是EVM的gas），“contract”（合约）具体指的是极光在NEAR的行引擎的智能合约。在这种情况下，错误信息非常清楚；在一次交易中，合约允许燃烧的gas有一定的上限，而我们的引擎试图运行这些defi交易时超过了上限。

你猜怎么着？随着2.4.0引擎的发布，这个问题正在慢慢被解决 — — 我们将引擎的gas的使用量减少了2倍，为开发人员创造了一些非常重要的用例，并使达到极限的难度增加了两倍，这样用户就更轻松了。

特别的，现在可以在单个事务(transaction)中进行的EVM的计算足以解锁一些以前在Aurora上根本不可能实现的defi用例。

### 3) 来自Aurigami的案例

我们想向我们defi的合作伙伴之一Aurigami大声疾呼，感谢他们投入开发资源，帮助我们发现一些优化机会，从而推出新版本。

多亏了Engine 2.4.0的发布，Aurigami得以在Aurora的testnet上启动。由于他们是一个借贷平台，此次更新符合他们尽可能平稳运行平台的核心要求。这在全球范围内更为重要，因为这种平台对于极光生态系统的分布式金融基础设施至关重要。

我们很高兴看到我们的生态系统继续增长，并期待着未来的更新，这样可以支持更多从以太坊了解和喜爱的defi用例！

### 4) 技术细节

如果你还在这里，那太好了！你会得到真正的“款待”。让我们来了解一下技术。

让我们先简要回顾一下极光引擎(Aurora [Engine](https://github.com/aurora-is-near/aurora-engine))是什么，以及它是如何工作的。极光引擎是一个在NEAR区块链上用Rust写的智能合约。它包含一个完整的EVM解释器，能够执行与以太坊完全相同的事务(transaction)，以及执行transaction前的所有辅助性逻辑（包括检查签名、nonce、账户余额与gas价格等）。

当您向极光的RPC 发送事务(transaction)时，我们的基础设施会将您签名的以太坊事务(transaction)打包为适合引擎的NEAR事务(transaction)。这意味着每一个极光事务(transaction)都将成为一个NEAR事务(transaction)，因此每个事务(transaction)都必须遵循NEAR协议的规则。正如总结中提到的，这就是NEAR gas问题产生的地方（而不是以太坊）。

自然，这就提出了一个问题：“如何提高引擎的效率，使其能够在相同的NEAR gas下进行更多EVM工作？”

这不是一个容易回答的问题（我们确实在继续优化工作），但对极光的成功至关重要。幸运的是，**NEAR的核心开发人员和受影响的defi项目之一Aurigami的开发人员愿意帮助我们找到这个问题的答案。**

结果发现，有几个”低垂的果实”(几个关键因素)一起产生了巨大的影响。

*   **更新到Rust的最新版本.** Rust编译器擅长优化代码的性能，而且总是越来越好。简单地升级到更高版本的rust，基本上可以免费获得约1%的改进。
    
*   **在EVM堆栈上使用数字的小端编码来表示**. EVM在技术上被限制为使用大端编码，然而大多数现代架构使用小端编码。与在堆栈上使用小端且仅在必要时切换到大端相比，在算术运算中不断切换堆栈上和堆栈下的数字字节顺序的成本相当高。(比如，写入存储或返回结果）。（译者注：大小端是数字在内存的存储格式）。
    
*   **账号的短路空检查**。如果一个账号的nonce和余额均为0，且没有部署任何代码，则认为这是一个空的Ethereum帐户。当然，一旦没有遇到任何一个条件，我们无需检查其他条件。
    
*   **通过引擎合约 (**[**#438**](https://github.com/aurora-is-near/aurora-engine/pull/438)**) (**[**#446**](https://github.com/aurora-is-near/aurora-engine/pull/446)**) 读取NEAR的状态机的缓存值**。这种改进对引擎的gas使用产生了最大的影响。下面给出的完整细节。
    

所有这些变化加在一起使引擎消耗的gas约为之前执行某些defi事务(transaction)时的一半。这2倍的改进足以解除Aurigami的阻碍，并允许他们在Aurora的testnet上启动。

为了理解改进缓存为何有如此的影响力，我们需要了解一点“NEAR“是怎么表示状态，以及它如何决定gas成本。trie树(字典树)是允许链上创建存储相当紧凑的内容的证明，和许多区块链一样，NEAR使用trie树(字典树)来存储状态。NEAR上的每个合约都有自己的key-value状态存储，合约的key-value会嵌入到完整的NEAR状态字典树中。

NEAR在设定gas价格时非常谨慎。其目标是确保他们能够保持1秒的阻塞时间，这使得类似web2的用户体验能够几乎即时进行交易确认。为了实现这一目标，任何块的处理时间都不能超过1秒，尤其是块中的所有事务也必须匹配这一时间。由于gas是计算工作量的度量，其思想很简单：确保仔细预测gas成本，以便交易完成的1秒工作量对应于固定的gas，然后将限制设置为远低于该值。

为了在gas成本和到达1秒时间之间建立如此严格的联系，对于一个事务(transaction)可能导致节点执行的所有操作，必须有一个单独的成本。例如，read\_base，它是执行任何合约状态读取的成本，也有read\_byte，它是每字节读取的基础成本之上的成本。回到状态详细信息，还有touch\_trie\_node cost，它是状态字典树的每个节点在从存储器读取或写入值时必须被触发的成本。

事实证明，这些IO成本占Engine总燃气消耗量的很大一部分；在某些情况下超过50%。因此，通过引入缓存来减少必要的状态读取次数会是一个大大的胜利。尽管缓存逻辑本身有成本（更不用说它的内存占用也有成本），但这低于通过执行更少的读取而节省的数量。

更细微的差别在于，某些类型的值需要不同于其他类型的缓存逻辑。一个称为“generation”(生成值)的特定值是引擎的实现逻辑。每个地址都有一个与之关联的“generation”。这个值允许我们\*\*“删除”\*\*整个地址，而不会实际产生太多IO成本；我们只是简单增加generation，忽略与generation较低的其他相关的任何状态。

这意味着，从EVM 合约的存储中读取一个值（在开发人员使用Solidity的层面来说看起来是读了一次）实际上是多次读取，因为引擎每次都需要先读取generation。但是，generation不会在事务(transaction)过程中发生变化。（如果在同一事务中删除并重新创建帐户，这些更改实际上会由EVM解释器缓存在内存中，因此在NEAR的状态机下写入的generation在事务期间不会更改). 此外，一笔交易通常不会涉及许多不同的地址（即使是复杂的defi交易也很少调用超过10个不同的EVM 合约）。因此，我们可以在每次读取时缓存完整的地址到generation的映射，这就是基本的实现。

另一方面，EVM合约存储无法在没有大量开销的情况下通过缓存查找本身，这是因为合约内容可能远大于generation的32位字节，也是因为密钥可能多得多；如果一个事务调用10个不同的合约，每个合约都必须从存储器中读取10个值，那么就需要缓存100个不同的键及其值。

此外，目前还不清楚所有这些缓存是否值得，因为合约存储中的给定值可能不会被读取多次，然而，我们基本上可以保证多次需要一个地址的generation。因此，我们用于generation的同一种缓存策略不适用于更一般的缓存。然而，它确实受益于另一种缓存。由于EVM解释器的效率低下（我们将在将来适当地解决这个问题），结果表明，看起来像是在Solidity层面读取一次值的东西，在引擎层面被转换为连续、重复地读取该值。例如，同一个值可以连续读取3到4次！幸运的是，通过一个大小为1的极其简单的LRU缓存，可以解决连续重复读取的问题，这就是实现的方法。

哟，恭喜你坚持到底！我希望你喜欢这次深入极光引擎的体验。我们继续进行这种优化工作，以推进我们的任务，将极光扩展到包括越来越多的用例。

---

*Originally published on [zeck](https://paragraph.com/@qgstudio/aurora-engine-2-4-0-evm)*
