在以太坊中,资源直到最近都被限制并通过一种名为“Gas”的单一资源进行定价。Gas是衡量处理特定交易或区块所需“计算努力”的量度。Gas将多种类型的“努力”合并在一起,主要包括:
例如,我发送的这笔交易总共消耗了47,085Gas。这分为:(i)21,000Gas的“基础成本”,(ii)交易中包含的字节数据需要的1556Gas,(iii)读写存储所需的16,500Gas,(iv) 生成日志所需的2149Gas,其余用于EVM执行。用户必须支付的交易费用与交易消耗的Gas成正比。一个区块最多可以包含30百万Gas,通过EIP-1559的目标机制不断调整Gas价格,确保区块平均包含15百万Gas。
这种方法有一个主要的优点:因为所有东西都合并成一个虚拟资源,所以它导致了非常简单的市场设计。优化交易以最小化成本很容易,优化区块以收集尽可能高的费用也相对容易(不包括MEV),并且没有奇怪的激励机制鼓励某些交易与其他交易捆绑以节省费用。
但这种方法也有一个主要的缺点:它将不同的资源视为可以相互转换,而实际上网络可以处理的潜在限制并不是这样的。理解这个问题的一种方法是查看下面的图表:
Gas 限制强制执行以下约束:
𝑥1𝑑𝑎𝑡𝑎+𝑥2𝑐𝑜𝑚𝑝𝑢𝑡𝑎𝑡𝑖𝑜𝑛<𝑁
。实际的底层安全约束通常更接近
𝑚𝑎𝑥(𝑥1𝑑𝑎𝑡𝑎,𝑥2𝑐𝑜𝑚𝑝𝑢𝑡𝑎𝑡𝑖𝑜𝑛)<𝑁
。这种差异导致气体限制不必要地排除实际安全的区块,或接受实际不安全的区块,或两者的某种混合。
如果有
𝑛
具有不同安全限制的资源,那么一维气体可能会降低吞吐量高达一个因子
𝑛
。因此,长期以来,人们对“ 多维气体, 与 EIP-4844 今天,我们实际上在以太坊上运行了多维气体。这篇文章探讨了这种方法的好处以及进一步增强这种方法的前景。
在今年年初,平均区块大小为150 kB。这个大小中的很大一部分是滚动数据:第二层协议在链上存储数据以提高安全性。这些数据很昂贵:即使在滚动上的交易成本比以太坊L1上对应的交易便宜约5-10倍,但即使这个成本对于许多用例来说也太高了。
为什么不降低calldataGas成本(当前为每个非零字节16Gas,每个零字节4Gas),以使滚动更便宜?我们以前做过这样的事情,我们可以再次做到。这里的答案是:一个区块的最坏情况大小是30,000,000/16=1,875,000个非零字节,而网络已经勉强能处理那么大的区块。再次将成本降低4倍将将最大值提高到7.5 MB,这将是一个巨大的安全风险。
这个问题最终通过在每个区块中引入一个单独的滚动友好数据空间“blobs”来处理。这两个资源有不同的价格和不同的限制:在Dencun硬分叉后,一个以太坊区块最多可以包含(i)30百万Gas,和(ii)6个blob,每个blob可以包含约125 kB的calldata。这两种资源有不同的价格,由分别调整的类似于EIP-1559的价格机制调整,以实现每个区块平均使用15百万Gas和3个blob。
结果,滚动变得便宜了100倍,滚动上的交易量增加了3倍多,而理论上的最大区块大小只增加了一点点:从约1.9 MB增加到约2.6 MB。
Rollups上的交易费用,由growthepie.xyz提供。引入带有多维度定价的blobs的Dencun硬分叉发生在2024年3月13日。
在不久的将来,关于无状态客户端的存储证明将出现类似的问题。无状态客户端是一种新型客户端,能够在本地不存储或几乎不存储数据的情况下验证链。无状态客户端通过接受区块中交易所需接触的特定以太坊状态的证明来实现这一点。
无状态客户端接收到一个区块,以及证明该区块执行所接触的特定状态部分(例如账户余额、代码、存储)当前值的证明。这允许节点在没有任何自身存储的情况下验证区块。
根据读取类型的不同,存储读取成本为2100-2600Gas,存储写入成本更高。平均而言,一个区块执行大约1000次存储读取和写入(包括ETH余额检查、SSTORE和SLOAD调用、合约代码读取和其他操作)。然而,理论最大值为30,000,000 / 2,100 = 14,285次读取。无状态客户端的带宽负载与此数字成正比。
目前,计划通过将以太坊的状态树设计从Merkle Patricia树迁移到Verkle树来支持无状态客户端。然而,Verkle树并不具备抗量子能力,并且对于新一波的STARK证明系统来说并不理想。因此,许多人对通过二进制Merkle树和STARKs来支持无状态客户端更感兴趣——要么完全跳过Verkle,要么在Verkle过渡后几年内升级到更成熟的STARKs。
基于哈希的STARK证明二进制哈希树分支有许多优点,但它们的主要缺点是生成证明需要很长时间:虽然Verkle树每秒可以证明超过十万个值,但基于哈希的STARKs通常每秒只能证明几千个哈希,并且证明每个值需要包含许多哈希的“分支”。
根据今天由超优化的证明系统(如Binius和Plonky3)和专门的哈希(如Vision-Mark-32)投射的数据,似乎在一段时间内实用的证明每秒不超过1000个值,但不是14,285个值。平均区块是可以接受的,但最坏情况下的区块,可能由攻击者发布,将会破坏网络。
我们处理这种情况的“默认”方法是重新定价:使存储读取变得更昂贵,以将每个区块的最大值减少到更安全的水平。然而,我们已经多次这样做了,再次这样做会使许多应用程序变得过于昂贵。更好的方法是多维度Gas:分别限制和收费存储访问,每个区块的平均使用量保持在1000次存储访问,但设置一个每区块例如2000次的限制。
另一个值得考虑的资源是状态大小增长:增加以太坊状态大小的操作,从那时起全节点将需要持有这些数据。状态大小增长的独特属性在于,限制其增长的理由完全来自长期的持续使用,而不是突发性增长。因此,对于增加状态大小的操作(例如零到非零的SSTORE,合约创建),引入一个单独的Gas维度可能会有价值,但目标不同:我们可以设置一个浮动价格来针对特定的平均使用量,但不设置每区块的限制。
这展示了多维度Gas的一个强大特性:它让我们可以分别考虑每种资源的(i)理想平均使用量和(ii)安全的每区块最大使用量的问题。与其基于每区块最大值设置Gas价格,并让平均使用量随之变化,我们有2𝑛个自由度来设置2𝑛个参数,根据对网络安全的考虑来调整每一个。
更复杂的情况,比如两个资源的安全性考量部分叠加,可以通过使一个操作码或资源消耗多种类型的Gas来处理(例如,零到非零的SSTORE可能花费5000无状态客户端证明Gas和20000存储扩展Gas)。
设( x_1 )为数据的Gas成本,( x_2 )为计算的Gas成本,那么在单维度Gas系统中,我们可以将交易的Gas成本写成:
[ \text{gas} = x_1 \times \text{data} + x_2 \times \text{computation} ]
在这种方案中,我们定义交易的Gas成本为:
[ \text{gas} = \max(x_1 \times \text{data}, x_2 \times \text{computation}) ]
即交易的收费基于其消耗的资源中较多的一项,而不是数据加计算的总和。这可以很容易地扩展到更多维度(例如,[ \max(…, x_3 \times \text{storage_access}) ])。
不难看出,这种方法在保持安全性的同时提高了吞吐量。区块中的数据理论最大量仍然是[ \frac{\text{GASLIMIT}}{x_1} ],与单维度Gas方案完全相同。同样,计算的理论最大量是[ \frac{\text{GASLIMIT}}{x_2} ],这也与单维度Gas方案相同。然而,任何同时消耗数据和计算的交易的Gas成本都减少了。
这大致是拟议的EIP-7623中采用的方案,用于在减少最大区块大小的同时进一步增加blob数量。EIP-7623中的具体机制稍微复杂一些:它保持当前的calldata价格为每字节16Gas,但增加了每字节48Gas的“底价”;交易支付的费用是[ 16 \times \text{bytes} + \text{execution_gas} ]和[ 48 \times \text{bytes} ]中的较高者。结果是,EIP-7623将区块中理论上的最大交易calldata从约1.9 MB减少到约0.6 MB,同时保持大多数应用的成本不变。该方法的优点在于,它与当前的单维度Gas方案相比变化很小,因此很容易实施。
有两个缺点:
对于重资源的交易,即使区块中的所有其他交易对该资源的使用很少,仍然需要支付大量费用。
它会激励数据密集型和计算密集型的交易合并到一个捆绑中以节省成本。
我认为,尽管存在这些缺点,但EIP-7623风格的规则,无论是对于交易calldata还是其他资源,都能带来足够大的好处,使其值得实施。然而,如果我们愿意投入(显著更高的)开发努力,有一种更理想的方法。
首先让我们回顾一下“常规”EIP-1559是如何工作的。我们将重点关注在EIP-4844中为blobs引入的版本,因为它在数学上更优雅。
我们跟踪一个参数,excess_blobs。在每个区块期间,我们设置:
[ \text{excess_blobs} \leftarrow \max(\text{excess_blobs} + \text{len(block.blobs)} - \text{TARGET}, 0) ]
其中TARGET = 3。也就是说,如果一个区块的blobs数量超过目标值,excess_blobs增加;如果少于目标值,excess_blobs减少。然后我们设置blob_basefee = exp(excess_blobs / 25.47),其中exp是指数函数的近似值[ \exp(x) = 2.71828^x ]。
也就是说,每当excess_blobs增加约25,blob basefee就增加约2.7倍。如果blobs变得过于昂贵,平均使用量下降,excess_blobs开始减少,价格自动再次下降。blob的价格不断调整,以确保区块平均填充一半,即每个区块平均包含3个blobs。
如果使用量在短期内激增,则限制生效:每个区块最多只能包含6个blobs,在这种情况下,交易可以通过提高优先费用来竞争。然而,在正常情况下,每个blob只需要支付blob_basefee加上一点点的优先费用作为包含激励。
这种定价方式在以太坊的Gas中已经存在多年:2020年引入的EIP-1559采用了非常类似的机制。通过EIP-4844,我们现在对Gas和blobs有两个独立浮动的价格。
多维度EIP-1559的实现
为了将多维度EIP-1559应用到其他资源(例如存储访问、计算、数据传输),我们可以为每个资源引入类似的机制:
定义目标值和参数:为每个资源(例如存储访问、计算)定义一个目标值(TARGET)和一个调整参数(类似于25.47)。
跟踪多维度参数:跟踪每个资源的过量使用量(excess_resources),例如excess_storage, excess_computation。
调整基础费用:根据过量使用量调整每个资源的基础费用(basefee),例如:
[ \text{excess_storage} \leftarrow \max(\text{excess_storage} + \text{len(block.storage_accesses)} - \text{TARGET}, 0) ]
[ \text{storage_basefee} = \exp(\text{excess_storage} / \text{ADJUSTMENT_PARAMETER}) ]
自动调整价格:资源的价格会不断调整,以确保每个区块对该资源的平均使用量达到目标值。
处理短期峰值:如果某个资源在短期内使用量激增,则设置每个区块的最大值,交易可以通过提高优先费用来竞争。
实施的挑战与优势
实施多维度EIP-1559比简单的每交易最大值方案复杂得多,但其优势在于能够更精确地控制每个资源的使用量和价格,确保网络的安全性和效率。
挑战:
需要更复杂的开发和实现工作。
必须精确设置每个资源的目标值和调整参数。
增加了网络和矿工的计算和处理负担。
优势:
更准确的资源使用控制。
通过动态调整基础费用,确保网络稳定和安全。
减少对个别资源的过度依赖,平衡网络负载。
总之,多维度EIP-1559虽然实现起来更难,但其带来的控制和优化能力可以大大提升以太坊网络的长期稳定性和可扩展性。
原则上,我们可以为存储读取和其他类型的操作添加更多独立浮动的费用,不过有一个问题,我将在下一节中详细说明。
对于用户来说,体验与今天非常相似:你支付的不再是一个基础费用,而是两个基础费用,但你的钱包可以将其抽象掉,只显示你预期的费用和最大可能支付的费用。
对于区块构建者来说,大多数时候最优策略与今天相同:包含任何有效的交易。大多数区块并不满——无论是在Gas还是在blobs上。唯一具有挑战性的情况是,当有足够的Gas或足够的blobs超过区块限制时,构建者需要解决一个多维度背包问题来最大化其利润。然而,即使在这种情况下,也存在相当好的近似算法,与MEV(最大可提取价值)相比,使用专有算法优化利润的收益要小得多。
对于开发者来说,主要的挑战在于需要重新设计EVM(以太坊虚拟机)及其周边基础设施的特性,因为这些特性目前是围绕一个价格和一个限制设计的,现在需要适应多个价格和多个限制的设计。对于应用开发者来说,一个问题是优化变得稍微困难了:在某些情况下,你不能再明确地说A比B更高效,因为如果A使用更多的calldata而B使用更多的执行Gas,那么当calldata便宜时A可能更便宜,而当calldata昂贵时A可能更贵。然而,开发者仍然可以通过基于长期历史平均价格进行优化来获得相当好的结果。
多维度EIP-1559的实施将使以太坊网络在资源使用和价格控制方面更加精确和灵活,尽管这需要更复杂的开发和实现工作。对于用户来说,体验变化不大;对于区块构建者来说,挑战主要在于处理资源使用超过区块限制的情况;对于开发者来说,需要适应新的多价格和多限制的设计,并在优化过程中考虑更多因素。尽管有这些挑战,多维度EIP-1559带来的控制和优化能力可以大大提升以太坊网络的长期稳定性和可扩展性。
有一个问题并未出现在 blobs 中,也不会出现在 EIP-7623 或针对 calldata 的“全”多维度定价实现中,但如果我们尝试单独定价状态访问或任何其他资源,则会出现:EVM 中子调用的Gas限制。
EVM 中的Gas限制存在于两个地方。首先,每个交易设置一个Gas限制,这限制了该交易中可以使用的Gas总量。其次,当一个合约调用另一个合约时,可以设置自己的Gas限制。这允许合约调用它们不信任的其他合约,并且仍然保证在调用后它们会有剩余的Gas用于执行其他计算。
例如,在一个账户抽象交易中,一个账户调用另一个账户,并只给被调用方一个有限的Gas量,以确保即使被调用方消耗了分配给它的所有Gas,外部调用仍然可以继续运行。
在不同类型的执行之间使Gas多维度化,似乎要求子调用为每种类型的Gas提供多个限制,这需要对 EVM 进行深度变更,并且不与现有应用程序兼容。这就是为什么多维度Gas提案通常停留在两个维度:数据和执行。数据(无论是交易 calldata 还是 blobs)仅在 EVM 外部分配,因此为了使 calldata 或 blobs 单独定价,不需要对 EVM 内部进行任何更改。
我们可以考虑一种“EIP-7623 风格的解决方案”。这里有一个简单的实现:在执行期间,对存储操作收费提高4倍;为了简化分析,假设每次存储操作收费10000Gas。在交易结束时,退还最小值(7500 * 存储操作次数,执行Gas)。结果是,在减去退款后,用户被收费:
[ \text{execution_gas} + 10000 \times \text{storage_operations} - \min(7500 \times \text{storage_operations}, \text{execution_gas}) ]
这等于:
[ \text{execution_gas} + 2500 \times \text{storage_operations} ]
等式结果
[ \max(\text{execution_gas} + 2500 \times \text{storage_operations}, 10000 \times \text{storage_operations}) ]
这反映了 EIP-7623 的结构。另一种方法是实时跟踪 storage_operations 和 execution_gas,并根据在调用操作码时 [ \max(\text{execution_gas} + 2500 \times \text{storage_operations}, 10000 \times \text{storage_operations}) ] 的增加情况来收取费用,选择2500或10000。这避免了交易过度分配Gas,并通过退款大部分收回。
尽管无法为子调用获得细粒度的权限控制:子调用可能消耗交易的所有“廉价”存储操作配额,但我们得到的解决方案足够好,使合约在进行子调用时可以设置限制,并确保子调用执行完成后,主调用仍有足够的Gas进行所需的后处理。
我能想到的最简单的“全多维度定价解决方案”是:我们将子调用Gas限制视为比例性的。假设有 ( k ) 种不同类型的执行,每个交易设置一个多维度限制 ( L_1, \ldots, L_k )。假设在当前执行点,剩余Gas为 ( g_1, \ldots, g_k )。假设调用 CALL 操作码,子调用Gas限制为 ( S )。则 ( s_1 = S ),然后 ( s_2 = s_1 \cdot \frac{g_2}{g_1} ),( s_3 = s_1 \cdot \frac{g_3}{g_1} ) 等等。
我们将第一种Gas类型(实际上是 VM 执行)视为一种特权的“计量单位”,然后分配其他类型的Gas,使子调用在每种类型的可用Gas中获得相同的百分比。这有些丑陋,但最大化了向后兼容性。如果我们想在不同类型的Gas之间使方案更“中立”,以牺牲向后兼容性为代价,我们可以让子调用Gas限制参数表示当前上下文中剩余Gas的一部分(例如, [ \frac{1, \ldots, 63}{64} ])。
无论哪种情况,都需要强调的是,一旦引入多维度执行Gas,本质上的复杂性就会增加,这似乎难以避免。因此,我们的任务是进行复杂的权衡:我们是否接受 EVM 层面上的更多复杂性,以便安全地解锁显著的 L1 可扩展性增益,如果是,哪个具体提案最适合协议经济学和应用开发者?很可能上述提案都不是最佳方案,仍有空间提出更优雅和更好的解决方案。
在以太坊中,资源直到最近都被限制并通过一种名为“Gas”的单一资源进行定价。Gas是衡量处理特定交易或区块所需“计算努力”的量度。Gas将多种类型的“努力”合并在一起,主要包括:
例如,我发送的这笔交易总共消耗了47,085Gas。这分为:(i)21,000Gas的“基础成本”,(ii)交易中包含的字节数据需要的1556Gas,(iii)读写存储所需的16,500Gas,(iv) 生成日志所需的2149Gas,其余用于EVM执行。用户必须支付的交易费用与交易消耗的Gas成正比。一个区块最多可以包含30百万Gas,通过EIP-1559的目标机制不断调整Gas价格,确保区块平均包含15百万Gas。
这种方法有一个主要的优点:因为所有东西都合并成一个虚拟资源,所以它导致了非常简单的市场设计。优化交易以最小化成本很容易,优化区块以收集尽可能高的费用也相对容易(不包括MEV),并且没有奇怪的激励机制鼓励某些交易与其他交易捆绑以节省费用。
但这种方法也有一个主要的缺点:它将不同的资源视为可以相互转换,而实际上网络可以处理的潜在限制并不是这样的。理解这个问题的一种方法是查看下面的图表:
Gas 限制强制执行以下约束:
𝑥1𝑑𝑎𝑡𝑎+𝑥2𝑐𝑜𝑚𝑝𝑢𝑡𝑎𝑡𝑖𝑜𝑛<𝑁
。实际的底层安全约束通常更接近
𝑚𝑎𝑥(𝑥1𝑑𝑎𝑡𝑎,𝑥2𝑐𝑜𝑚𝑝𝑢𝑡𝑎𝑡𝑖𝑜𝑛)<𝑁
。这种差异导致气体限制不必要地排除实际安全的区块,或接受实际不安全的区块,或两者的某种混合。
如果有
𝑛
具有不同安全限制的资源,那么一维气体可能会降低吞吐量高达一个因子
𝑛
。因此,长期以来,人们对“ 多维气体, 与 EIP-4844 今天,我们实际上在以太坊上运行了多维气体。这篇文章探讨了这种方法的好处以及进一步增强这种方法的前景。
在今年年初,平均区块大小为150 kB。这个大小中的很大一部分是滚动数据:第二层协议在链上存储数据以提高安全性。这些数据很昂贵:即使在滚动上的交易成本比以太坊L1上对应的交易便宜约5-10倍,但即使这个成本对于许多用例来说也太高了。
为什么不降低calldataGas成本(当前为每个非零字节16Gas,每个零字节4Gas),以使滚动更便宜?我们以前做过这样的事情,我们可以再次做到。这里的答案是:一个区块的最坏情况大小是30,000,000/16=1,875,000个非零字节,而网络已经勉强能处理那么大的区块。再次将成本降低4倍将将最大值提高到7.5 MB,这将是一个巨大的安全风险。
这个问题最终通过在每个区块中引入一个单独的滚动友好数据空间“blobs”来处理。这两个资源有不同的价格和不同的限制:在Dencun硬分叉后,一个以太坊区块最多可以包含(i)30百万Gas,和(ii)6个blob,每个blob可以包含约125 kB的calldata。这两种资源有不同的价格,由分别调整的类似于EIP-1559的价格机制调整,以实现每个区块平均使用15百万Gas和3个blob。
结果,滚动变得便宜了100倍,滚动上的交易量增加了3倍多,而理论上的最大区块大小只增加了一点点:从约1.9 MB增加到约2.6 MB。
Rollups上的交易费用,由growthepie.xyz提供。引入带有多维度定价的blobs的Dencun硬分叉发生在2024年3月13日。
在不久的将来,关于无状态客户端的存储证明将出现类似的问题。无状态客户端是一种新型客户端,能够在本地不存储或几乎不存储数据的情况下验证链。无状态客户端通过接受区块中交易所需接触的特定以太坊状态的证明来实现这一点。
无状态客户端接收到一个区块,以及证明该区块执行所接触的特定状态部分(例如账户余额、代码、存储)当前值的证明。这允许节点在没有任何自身存储的情况下验证区块。
根据读取类型的不同,存储读取成本为2100-2600Gas,存储写入成本更高。平均而言,一个区块执行大约1000次存储读取和写入(包括ETH余额检查、SSTORE和SLOAD调用、合约代码读取和其他操作)。然而,理论最大值为30,000,000 / 2,100 = 14,285次读取。无状态客户端的带宽负载与此数字成正比。
目前,计划通过将以太坊的状态树设计从Merkle Patricia树迁移到Verkle树来支持无状态客户端。然而,Verkle树并不具备抗量子能力,并且对于新一波的STARK证明系统来说并不理想。因此,许多人对通过二进制Merkle树和STARKs来支持无状态客户端更感兴趣——要么完全跳过Verkle,要么在Verkle过渡后几年内升级到更成熟的STARKs。
基于哈希的STARK证明二进制哈希树分支有许多优点,但它们的主要缺点是生成证明需要很长时间:虽然Verkle树每秒可以证明超过十万个值,但基于哈希的STARKs通常每秒只能证明几千个哈希,并且证明每个值需要包含许多哈希的“分支”。
根据今天由超优化的证明系统(如Binius和Plonky3)和专门的哈希(如Vision-Mark-32)投射的数据,似乎在一段时间内实用的证明每秒不超过1000个值,但不是14,285个值。平均区块是可以接受的,但最坏情况下的区块,可能由攻击者发布,将会破坏网络。
我们处理这种情况的“默认”方法是重新定价:使存储读取变得更昂贵,以将每个区块的最大值减少到更安全的水平。然而,我们已经多次这样做了,再次这样做会使许多应用程序变得过于昂贵。更好的方法是多维度Gas:分别限制和收费存储访问,每个区块的平均使用量保持在1000次存储访问,但设置一个每区块例如2000次的限制。
另一个值得考虑的资源是状态大小增长:增加以太坊状态大小的操作,从那时起全节点将需要持有这些数据。状态大小增长的独特属性在于,限制其增长的理由完全来自长期的持续使用,而不是突发性增长。因此,对于增加状态大小的操作(例如零到非零的SSTORE,合约创建),引入一个单独的Gas维度可能会有价值,但目标不同:我们可以设置一个浮动价格来针对特定的平均使用量,但不设置每区块的限制。
这展示了多维度Gas的一个强大特性:它让我们可以分别考虑每种资源的(i)理想平均使用量和(ii)安全的每区块最大使用量的问题。与其基于每区块最大值设置Gas价格,并让平均使用量随之变化,我们有2𝑛个自由度来设置2𝑛个参数,根据对网络安全的考虑来调整每一个。
更复杂的情况,比如两个资源的安全性考量部分叠加,可以通过使一个操作码或资源消耗多种类型的Gas来处理(例如,零到非零的SSTORE可能花费5000无状态客户端证明Gas和20000存储扩展Gas)。
设( x_1 )为数据的Gas成本,( x_2 )为计算的Gas成本,那么在单维度Gas系统中,我们可以将交易的Gas成本写成:
[ \text{gas} = x_1 \times \text{data} + x_2 \times \text{computation} ]
在这种方案中,我们定义交易的Gas成本为:
[ \text{gas} = \max(x_1 \times \text{data}, x_2 \times \text{computation}) ]
即交易的收费基于其消耗的资源中较多的一项,而不是数据加计算的总和。这可以很容易地扩展到更多维度(例如,[ \max(…, x_3 \times \text{storage_access}) ])。
不难看出,这种方法在保持安全性的同时提高了吞吐量。区块中的数据理论最大量仍然是[ \frac{\text{GASLIMIT}}{x_1} ],与单维度Gas方案完全相同。同样,计算的理论最大量是[ \frac{\text{GASLIMIT}}{x_2} ],这也与单维度Gas方案相同。然而,任何同时消耗数据和计算的交易的Gas成本都减少了。
这大致是拟议的EIP-7623中采用的方案,用于在减少最大区块大小的同时进一步增加blob数量。EIP-7623中的具体机制稍微复杂一些:它保持当前的calldata价格为每字节16Gas,但增加了每字节48Gas的“底价”;交易支付的费用是[ 16 \times \text{bytes} + \text{execution_gas} ]和[ 48 \times \text{bytes} ]中的较高者。结果是,EIP-7623将区块中理论上的最大交易calldata从约1.9 MB减少到约0.6 MB,同时保持大多数应用的成本不变。该方法的优点在于,它与当前的单维度Gas方案相比变化很小,因此很容易实施。
有两个缺点:
对于重资源的交易,即使区块中的所有其他交易对该资源的使用很少,仍然需要支付大量费用。
它会激励数据密集型和计算密集型的交易合并到一个捆绑中以节省成本。
我认为,尽管存在这些缺点,但EIP-7623风格的规则,无论是对于交易calldata还是其他资源,都能带来足够大的好处,使其值得实施。然而,如果我们愿意投入(显著更高的)开发努力,有一种更理想的方法。
首先让我们回顾一下“常规”EIP-1559是如何工作的。我们将重点关注在EIP-4844中为blobs引入的版本,因为它在数学上更优雅。
我们跟踪一个参数,excess_blobs。在每个区块期间,我们设置:
[ \text{excess_blobs} \leftarrow \max(\text{excess_blobs} + \text{len(block.blobs)} - \text{TARGET}, 0) ]
其中TARGET = 3。也就是说,如果一个区块的blobs数量超过目标值,excess_blobs增加;如果少于目标值,excess_blobs减少。然后我们设置blob_basefee = exp(excess_blobs / 25.47),其中exp是指数函数的近似值[ \exp(x) = 2.71828^x ]。
也就是说,每当excess_blobs增加约25,blob basefee就增加约2.7倍。如果blobs变得过于昂贵,平均使用量下降,excess_blobs开始减少,价格自动再次下降。blob的价格不断调整,以确保区块平均填充一半,即每个区块平均包含3个blobs。
如果使用量在短期内激增,则限制生效:每个区块最多只能包含6个blobs,在这种情况下,交易可以通过提高优先费用来竞争。然而,在正常情况下,每个blob只需要支付blob_basefee加上一点点的优先费用作为包含激励。
这种定价方式在以太坊的Gas中已经存在多年:2020年引入的EIP-1559采用了非常类似的机制。通过EIP-4844,我们现在对Gas和blobs有两个独立浮动的价格。
多维度EIP-1559的实现
为了将多维度EIP-1559应用到其他资源(例如存储访问、计算、数据传输),我们可以为每个资源引入类似的机制:
定义目标值和参数:为每个资源(例如存储访问、计算)定义一个目标值(TARGET)和一个调整参数(类似于25.47)。
跟踪多维度参数:跟踪每个资源的过量使用量(excess_resources),例如excess_storage, excess_computation。
调整基础费用:根据过量使用量调整每个资源的基础费用(basefee),例如:
[ \text{excess_storage} \leftarrow \max(\text{excess_storage} + \text{len(block.storage_accesses)} - \text{TARGET}, 0) ]
[ \text{storage_basefee} = \exp(\text{excess_storage} / \text{ADJUSTMENT_PARAMETER}) ]
自动调整价格:资源的价格会不断调整,以确保每个区块对该资源的平均使用量达到目标值。
处理短期峰值:如果某个资源在短期内使用量激增,则设置每个区块的最大值,交易可以通过提高优先费用来竞争。
实施的挑战与优势
实施多维度EIP-1559比简单的每交易最大值方案复杂得多,但其优势在于能够更精确地控制每个资源的使用量和价格,确保网络的安全性和效率。
挑战:
需要更复杂的开发和实现工作。
必须精确设置每个资源的目标值和调整参数。
增加了网络和矿工的计算和处理负担。
优势:
更准确的资源使用控制。
通过动态调整基础费用,确保网络稳定和安全。
减少对个别资源的过度依赖,平衡网络负载。
总之,多维度EIP-1559虽然实现起来更难,但其带来的控制和优化能力可以大大提升以太坊网络的长期稳定性和可扩展性。
原则上,我们可以为存储读取和其他类型的操作添加更多独立浮动的费用,不过有一个问题,我将在下一节中详细说明。
对于用户来说,体验与今天非常相似:你支付的不再是一个基础费用,而是两个基础费用,但你的钱包可以将其抽象掉,只显示你预期的费用和最大可能支付的费用。
对于区块构建者来说,大多数时候最优策略与今天相同:包含任何有效的交易。大多数区块并不满——无论是在Gas还是在blobs上。唯一具有挑战性的情况是,当有足够的Gas或足够的blobs超过区块限制时,构建者需要解决一个多维度背包问题来最大化其利润。然而,即使在这种情况下,也存在相当好的近似算法,与MEV(最大可提取价值)相比,使用专有算法优化利润的收益要小得多。
对于开发者来说,主要的挑战在于需要重新设计EVM(以太坊虚拟机)及其周边基础设施的特性,因为这些特性目前是围绕一个价格和一个限制设计的,现在需要适应多个价格和多个限制的设计。对于应用开发者来说,一个问题是优化变得稍微困难了:在某些情况下,你不能再明确地说A比B更高效,因为如果A使用更多的calldata而B使用更多的执行Gas,那么当calldata便宜时A可能更便宜,而当calldata昂贵时A可能更贵。然而,开发者仍然可以通过基于长期历史平均价格进行优化来获得相当好的结果。
多维度EIP-1559的实施将使以太坊网络在资源使用和价格控制方面更加精确和灵活,尽管这需要更复杂的开发和实现工作。对于用户来说,体验变化不大;对于区块构建者来说,挑战主要在于处理资源使用超过区块限制的情况;对于开发者来说,需要适应新的多价格和多限制的设计,并在优化过程中考虑更多因素。尽管有这些挑战,多维度EIP-1559带来的控制和优化能力可以大大提升以太坊网络的长期稳定性和可扩展性。
有一个问题并未出现在 blobs 中,也不会出现在 EIP-7623 或针对 calldata 的“全”多维度定价实现中,但如果我们尝试单独定价状态访问或任何其他资源,则会出现:EVM 中子调用的Gas限制。
EVM 中的Gas限制存在于两个地方。首先,每个交易设置一个Gas限制,这限制了该交易中可以使用的Gas总量。其次,当一个合约调用另一个合约时,可以设置自己的Gas限制。这允许合约调用它们不信任的其他合约,并且仍然保证在调用后它们会有剩余的Gas用于执行其他计算。
例如,在一个账户抽象交易中,一个账户调用另一个账户,并只给被调用方一个有限的Gas量,以确保即使被调用方消耗了分配给它的所有Gas,外部调用仍然可以继续运行。
在不同类型的执行之间使Gas多维度化,似乎要求子调用为每种类型的Gas提供多个限制,这需要对 EVM 进行深度变更,并且不与现有应用程序兼容。这就是为什么多维度Gas提案通常停留在两个维度:数据和执行。数据(无论是交易 calldata 还是 blobs)仅在 EVM 外部分配,因此为了使 calldata 或 blobs 单独定价,不需要对 EVM 内部进行任何更改。
我们可以考虑一种“EIP-7623 风格的解决方案”。这里有一个简单的实现:在执行期间,对存储操作收费提高4倍;为了简化分析,假设每次存储操作收费10000Gas。在交易结束时,退还最小值(7500 * 存储操作次数,执行Gas)。结果是,在减去退款后,用户被收费:
[ \text{execution_gas} + 10000 \times \text{storage_operations} - \min(7500 \times \text{storage_operations}, \text{execution_gas}) ]
这等于:
[ \text{execution_gas} + 2500 \times \text{storage_operations} ]
等式结果
[ \max(\text{execution_gas} + 2500 \times \text{storage_operations}, 10000 \times \text{storage_operations}) ]
这反映了 EIP-7623 的结构。另一种方法是实时跟踪 storage_operations 和 execution_gas,并根据在调用操作码时 [ \max(\text{execution_gas} + 2500 \times \text{storage_operations}, 10000 \times \text{storage_operations}) ] 的增加情况来收取费用,选择2500或10000。这避免了交易过度分配Gas,并通过退款大部分收回。
尽管无法为子调用获得细粒度的权限控制:子调用可能消耗交易的所有“廉价”存储操作配额,但我们得到的解决方案足够好,使合约在进行子调用时可以设置限制,并确保子调用执行完成后,主调用仍有足够的Gas进行所需的后处理。
我能想到的最简单的“全多维度定价解决方案”是:我们将子调用Gas限制视为比例性的。假设有 ( k ) 种不同类型的执行,每个交易设置一个多维度限制 ( L_1, \ldots, L_k )。假设在当前执行点,剩余Gas为 ( g_1, \ldots, g_k )。假设调用 CALL 操作码,子调用Gas限制为 ( S )。则 ( s_1 = S ),然后 ( s_2 = s_1 \cdot \frac{g_2}{g_1} ),( s_3 = s_1 \cdot \frac{g_3}{g_1} ) 等等。
我们将第一种Gas类型(实际上是 VM 执行)视为一种特权的“计量单位”,然后分配其他类型的Gas,使子调用在每种类型的可用Gas中获得相同的百分比。这有些丑陋,但最大化了向后兼容性。如果我们想在不同类型的Gas之间使方案更“中立”,以牺牲向后兼容性为代价,我们可以让子调用Gas限制参数表示当前上下文中剩余Gas的一部分(例如, [ \frac{1, \ldots, 63}{64} ])。
无论哪种情况,都需要强调的是,一旦引入多维度执行Gas,本质上的复杂性就会增加,这似乎难以避免。因此,我们的任务是进行复杂的权衡:我们是否接受 EVM 层面上的更多复杂性,以便安全地解锁显著的 L1 可扩展性增益,如果是,哪个具体提案最适合协议经济学和应用开发者?很可能上述提案都不是最佳方案,仍有空间提出更优雅和更好的解决方案。