本文档是Arbitrum虚拟机(AVM)架构设计理念的梗概。
AVM设计的起点是以太坊虚拟机(EVM)。由于Arbitrum致力于让为EVM编写或编译的程序能够高效地执行,所以在很多方面保留了EVM的设计。例如,AVM使用了EVM基础的整形(256位大端序无符号整形),以及EVM上对整形操作的指令。
而AVM与EVM之间的区别则由Arbitrum L2协议以及Arbitrum为解决争议使用的多轮挑战协议而产生。
Arbitrum不像EVM和其类似架构,Arbitrum需要同时支持执行(通过本地运算推进运算状态)和证明(说服L1合约和其他可信赖方某个执行是正确的)。基于EVM的系统通过重新执行有争议的代码来解决争议,Arbitrum则依赖挑战协议输出的最终证明。
我们希望能够在本地、可信赖的环境对执行的速度进行优化,因为本地执行是常见场景。另一方面,证明,没有那么常用但也需要在链上甚至是拥堵的L1链上能够足够高效。本系统应该很少需要证明,但也必须时刻为证明做好准备。在逻辑上分离执行与证明,使得在常见情况下执行速度能够得到大幅优化,而在这些场景中不需要证明。
另一个不同是Arbitrum使用了ArbOS,一个在L2上运行的『操作系统』。ArbOS对不同的合约进行隔离并追踪其资源使用情况。
在L2上可信赖的软件中支持这些功能,而非像以太坊那样将其构建于L1监管的合约中,在成本方面提供了巨大优势。因为相比于在L1的EthBridge合约上进行资源管理,在L2上进行运算和存储的开销都会低很多。在L2上有一套可信赖的操作系统也提供了显著的灵活性,因为L2的代码相比L1的VM架构更容易演进或者为某条链进行自定义。
在L2上使用可信赖的操作系统确实需要在架构上有一定支持,例如允许该OS限制和追踪合约使用的资源。
任何基于断言和争议解决的L2协议(至少包括所有的rollup协议)都必须定义一套能够对整个VM状态进行梅克尔哈希的规则。该规则必须是系统架构的一部分,因为系统需要基于它来解决争议。
同时它也必须足够高效地维持梅克尔哈希,并在有需要时对其进行重新计算。例如,这会影响到系统架构如何组织其内存。对梅克尔化而言,任何大的且可变的数据结构都是相对昂贵的,而梅克尔算法也必须是系统特性的一部分。
AVM架构解决这一问题的方法是,使用了大小有限、不可变的内存对象,元组。元组不能就地更改,但又一个指令可以复制后对元组修改。这一措施使得树结构的创建可以像大平面内存结构一样。应用可以通过内部调用包含元组的库,来使用诸如大扁平数组,键值对存储等功能。
元组的特性使得创建循环结构的元组是不可能的,所以AVM可以通过有引用计数的、不可变的数据结构来安全地管理元组。每个元组的哈希只需要计算一次,因为其内容是不可变的。
传统的代码组织结构是,存储一个线性的指令数组,将程序计数器(PC)指向下一个要执行的指令。使用该方法证明一条指令需要对数时间和空间,因为需要用梅克尔证明来证实此时PC指向了哪条指令。
AVM使用了该传统的方法,但增加了一个特性,使证明和证明核查只需要常数时间和空间。位于某PC值下的指令其码点是(instruction at PC, Hash(CodePoint at PC+1))。若在PC+1下没有码点,则使用0代替。
为了证明,『程序计数器』被『当前码点哈希』所代替,它是状态机的一部分。该哈希的原像包含了当前指令,以及下一个码点的哈希,若指令不是Jump,则这些就已经是用以验证指令以及当前码点哈希后指令的证据的全集。
所有的Jump指令的目的地都是码点。所以,我们可以快速地得到一个Jump指令的证明,不仅包含要跳转到的PC,还包括jump执行后『当前码点哈希』的内容。在所有情况下,证明和验证仅需要常数时间和空间。
在正常情况下(不需要证明只需要执行),只会使用传统架构中的PC值。不过,当需要证明时,证明者可查表,来获取任何相关PC对应的码点哈希。
ArbOS,运行于L2,分离各个不受信的应用程序,追踪并限制其资源使用,并管理着验证者向用户收费的经济模型。
为了支持ArbOS,AVM指令集支持保存和恢复虚拟机的栈,管理寄存器并追踪资源使用,接受外部调用者的信息。这些指令仅能被ArbOS自身使用,它会确保在不受信的代码中永远不会出现这些指令。