5_ArbGas与运行时

ArbGas是Arbitrum用来管理链上执行成本的。与以太坊gas的理念一致,每个AVM指令都会有一定数量的ArbGas消耗,而一次运算的总成本是该运算包含的指令的ArbGas的加总。

ArbGas并不能直接与以太坊gas相比。Arbitrum并没有硬性的ArbGas limit,正常情况下Arbitrum链每秒可以消耗任意数量的ArbGas,而在以太坊中则有gas limit。开发者和用户应该把ArbGas理解为是比以太坊gas更加 且便宜的。

Why ArbGas?

AVM的设计原则之一是,每个指令都应该对验证、证明和证据检验有可预测的执行时间。这就导致我们需要一种方式来计量或估算验证任何运算的时间。

有两个原因。第一,我们需要确保证据检验有可预测的成本,这样就能预测EthBridge需要多少L1 gas,以确保EthBridge不会接近L1的gas limit。

第二,精确的验证时间估算对最大化rollup链的吞吐量是很重要的,因为可以帮我们安全地确定链的速度限制。

断言中的ArbGas

每条断言都包含了该运算所使用的ArbGas。像断言中其他东西一样,该值只是由断言者提供的,如果其断言是错误仍会被拒绝。

即使断言中的ArbGas值可能是错误的,但还是可以可靠地作为用来估算该区块所需计算的上限。因为,检查该区块的验证者可以在所主张的ArbGas耗尽后就停止运算;如果所主张的ArbGas耗尽后运算还未终止,该rollup区块肯定是错的,检查者就可以安全地挑战它。因此,rollup协议就可以安全地使用rollup区块中对ArbGas的主张,减去之前区块中ArbGas的数量,作为验证该区块正确性的所需时间上限。

即使某个断言中只有ArbGas这一个数据是错的,对该断言进行挑战也是安全的。当对一个断言二分后,该断言会包含所主张的ArbGas使用情况,其和必须等于父断言的ArbGas消耗量。如果一个断言的ArbGas数量是错的,那么至少有一个子断言的ArbGas是错的。所以挑战者知道某个断言的ArbGas是错的,他一定能找出某个分段的ArbGas是错的。

最终争议会走到单条AVM指令,以及关于该指令的ArbGas断言。单步证明会检查该断言是否正确。因此,rollup区块中错误的ArbGas断言最终都能追查到错误ArbGas数量的单步指令,然后再通过EthBridge的单步验证进行检测。

AVM中的ArbGas计量

AVM也在内部处理ArbGas,它会使用ArbGasRemaining寄存器进行计量,该寄存器是一个256位的无符号整型,行为如下:

  • 该寄存器初始值为MaxUint256
  • 在执行任何指令之前,该指令的ArbGas会即刻从寄存器中扣除。如果这使寄存器的值小于0,会抛出错误并将寄存器重设为MaxUint256。(在AVM规范中说明了该错误会导致的控制权转移)
  • 有一条特殊指令可以读取该寄存器的值
  • 还有一条特殊指令可以将该寄存器的值设置为任意值

该机制确保了ArbOS能够控制并计算应用代码需要消耗的ArbGas。在调用该应用之前,ArbOS可以通过将寄存器设置至N来限制应用的调用至N个ArbGas,如果有out-of-ArbGas错误产生则接收该错误。如果该寄存器的读数接近MaxInt256,肯定是因为该应用发生了out-of-ArbGas错误。(情况可能是这样的:应用生成了其他的错误而还有一小部分ArbGas剩余,然后在错误处理器的开头出现了out-of-ArbGas错误。在这种情况下,第二个错误会将ArbGasRemaining设置为MaxInt256并将控制器返回给错误处理器的开头,导致错误处理器认为是该程序导致的out-of-ArbGas错误。我们认为这种合理的行为是正确的。)

如果程序在没有错误产生的情况下讲控制器返回给了运行时,运行时就可以读取ArbGasRemaining寄存器来确定程序调用消耗了多少ArbGas,并充值进程序的账户中。

运行时可以安全地忽略掉ArbGas计量机制。如果没有使用过特殊指令,寄存器会被设置为MaxInt256,其值会减少但实践中不可能到0,所以不会有错误产生。

需要注意的是,必须确保应用不能调取可以更改ArbGasRemaining数值的指令。在编译应用代码时编译器不应编译该指令,AVM加载器和运行时也应该在加载之前先检测一遍,来确保该AVM指令(以及其他有特权的指令)不会出现在应用代码中。

速度上限

Arbitrum链的安全依赖于一个假设,当一名验证着创建一个rollup区块时,其他验证着会对其进行检测,如果有错误则发起挑战。这需要其他验证者有时间和资源来检查每个rollup区块并及时提出挑战。Arbitrum协议把该项作为设置rollup区块挑战截止时间的考虑因素。

这就给AVM的运行设置了实际的速度上限:长远来看VM不可能运行地比验证者检验速度还要快。如果rollup区块的发布速度比速度上限还快,那截止时间在未来就会越来越长。由于rollup合约强加未来截止时间最多有多长的限制,最终会使新的rollup区块生成变慢,来确保实际的速度上限。

能够精确设置速度上限的基础是能够以一定准确度估算对AVM运算进行验证的所需时间。安全起见,任何在验证时间估算方面的不确定性都迫使我们把速度上限设置得低一些。而我们确实不想把速度上限设低,所以会尝试精确的估算。

ArbGas

ArbGas是验证者验证AVM运算所需时间的计量尺度。100百万的ArbGas约等于2020年Offchain用的开发者电脑的1秒的CPU时间。

ArbGas与以太坊gas,既有共同点又有不同点,二者有不同的使用成本与折中。(例如,在以太坊上获取存储空间是非常昂贵的,因为这要求每一位以太坊矿工都要为之买单。)

每个AVM指令的ArbGas的成本都是定义好的。

维持常量成本

让大多数指令在进行验证,证明,证据检查时维持固定成本是比较简单的,但在内存访问上要实现该目的则比较麻烦。

在这一点上,AVM使用了非常规的结构:固定大小不可变元组。元组最多有8个元素,每个元素中都包含这一个Arbitrum值。该值自己也可以是元组,所以每个元组可以代表任意大的树形结构。(实践中可以用有向无环图DAG来表示,有区别但区别并不重要,因为元组和其内容是不可变的。)

用该结构就可以将大数组或内存表示为元组树。该实现基本上沿用了类似系统中常用的梅克尔树结构,并且实现地非常显性。在渐进效率上,并没有任何损失:访问这种数据结构需要对数量级的AVM指令,但每个指令花费的时间都是常量,所以访问梅克尔化内存也需要对数时间。Arbitrum标准库提供了对该数据结构的支持。

为保证证明所花时间为常量,每个元组都包含了其每个元素中内容的梅克尔树的哈希。该哈希在元组创建时就可计算。由于元组不可修改,所以运算也只需一次。

这种哈希策略让单步证明的创建于检验都只需要常量时间和空间,因为一条包含元组的指令的单步证明,只需要检查元组的哈希与其元素中内容的哈希是否相同即可。

惰性哈希与平摊时间

为提升性能,在单个断言验证中,我们放宽了时间限制,由常量时间变为平摊时间。换言之,每个指令都有ArbGas消耗,但其中一部分ArbGas在指令验证时可以稍后再说,不必当下用掉。之后,仍然在该断言的验证过程内,之前的ArbGas可以用做其他用途。

在单个断言中使用平摊是安全的,因为截止时间和速度限制只对断言整体生效,所以在单个断言内移动时间成本并不会影响对截止时间和速度限制的响应。

目前,唯一使用了平摊的是元组的惰性哈希。创建元组时,AVM虚拟机并不会计算其哈希,而将其标注为『待哈希』。创建该元组时已经预留了计算该哈希的足够ArbGas。当需要时,如AVMhash指令执行时或该断言的一部分需要哈希时,该哈希才会被计算。

惰性哈希能够节省运算量,是因为,有些元组在需要计算哈希之前就被废弃了。这种做法在以太坊转译的程序中非常常见,因为以太坊在交易时会使用临时内存,在交易中只保留少量的存储空间。

如果在断言中元组没被丢弃,在断言终点就需要计算哈希。AVM hash是梅克尔型的哈希,能够覆盖到所有可到达的元组,因此AVM hash的运算会强制执行所有可达元组的哈希。对每个元组进行哈希所需的ArbGas在元组创建时就已经预留好了。

请注意,因为每个断言都会对所有可达元组进行哈希,所以一个断言永远不会有任何ArbGas负债需要之后的断言来偿还。每个完成的哈希的成本都由在同一断言中之前收取的ArbGas支付。

4_区块编号和时间1_本地区块链