Vitalik's New Quick Read: Multi-dimensional Gas Pricing

CN
链捕手
Follow
1 year ago

Author: Vitalik Buterin

Compiled by: Karen, Foresight News

In Ethereum, resources have been limited until recently and are priced using a single resource called "Gas." Gas is a unit of measurement for the "computational effort" required to process specific transactions or blocks. Gas combines various types of "computational effort," including:

  1. Raw computation (e.g., ADD, MULTIPLY);
  2. Reading and writing to Ethereum storage (e.g., SSTORE, SLOAD, ETH transfers);
  3. Data bandwidth;
  4. Cost of generating ZK-SNARK proofs for block production.

For example, the transaction I sent consumed a total of 47,085 Gas, including: (i) a base cost of 21,000 Gas, (ii) calldata bytes included as part of the transaction consuming 1556 Gas, (iii) reading and writing to storage consuming 16,500 Gas, (iv) generating logs consuming 2149 Gas, with the remainder used for EVM execution. The transaction fee that users must pay is directly proportional to the gas consumed by the transaction. A block can contain a maximum of 30 million Gas, and the gas price is continuously adjusted through the EIP-1559 targeting mechanism to ensure an average of 15 million Gas per block.

This approach has a major advantage: because all content is merged into a virtual resource, market design is very simple. Optimizing transactions to minimize costs is easy, optimizing blocks to collect as high fees as possible is relatively easy (excluding MEV), and there are no strange incentive mechanisms encouraging some transactions to be bundled with others to save costs.

However, this approach also has inefficiencies: it treats different resources as interchangeable, while the actual underlying constraints are not the same. To understand this issue, you can first look at the following chart:

Gas limits impose a constraint:

The actual underlying security constraints are usually closer to:

This difference means that one-dimensional Gas may reduce throughput by up to n times if there are n types of resources with different security constraints. Therefore, people have long been interested in the concept of multidimensional Gas, and through EIP-4844, we have actually implemented multidimensional Gas on Ethereum. This article explores the advantages of this approach and the prospects for further enhancement.

Blob: Multidimensional Gas in Dencun

At the beginning of this year, the average block size was 150 kB. A large part of this was Rollup data: Layer2 protocols store data on-chain. This data is very expensive: although the cost of transactions on Rollup is only 5-10 times that of corresponding transactions on Ethereum L1, this cost is still too high for many use cases.

So why not reduce the Gas cost of calldata (currently 16 Gas for non-zero bytes, 4 Gas for zero bytes) to make Rollup cheaper? We have done this before, and we can do it again now. But the answer here is: the maximum block size is 30,000,000/16=1,875,000 non-zero bytes, and the network can barely or almost cannot handle blocks of this size. Reducing the cost by 4 times would increase the maximum value to 7.5 MB, which would pose a huge risk to security.

This problem was ultimately solved by introducing a separate, Rollup-friendly data space (called blob) in each block.

These two resources have different prices and constraints: after the Dencun hard fork, an Ethereum block can contain (i) 30 million Gas and (ii) 6 blobs, each of which can contain approximately 125 kB of calldata. Both resources have separate pricing and are adjusted through a pricing mechanism similar to EIP-1559, with the goal of averaging 15 million Gas and 3 blobs per block.

As a result, the cost of Rollup has been reduced by 100 times, the transaction volume on Rollup has increased by more than 3 times, and the theoretical maximum block size has only slightly increased: from about 1.9 MB to about 2.6 MB.

Note: The transaction fees for Rollup are provided by Growthepie.xyz. The Dencun fork occurred on March 13, 2024, introducing multidimensional pricing for blobs.

Multidimensional Gas and Stateless Clients

In the near future, storage proofs for stateless clients will also encounter similar issues. Stateless clients are a new type of client that will be able to verify the chain without needing to store a large amount of data or any data locally. Stateless clients achieve this by accepting proofs of the specific parts of Ethereum state that transactions in that block need to access.

The above figure shows a stateless client receiving a block and proofs of the current values of the specific parts of the state (e.g., account balances, code, storage) touched by the execution of that block, enabling nodes to verify a block without any storage.

A storage read costs 2100-2600 Gas, depending on the type of read, while storage write costs are higher. On average, a block will execute approximately 1000 storage read/write operations (including ETH balance checks, SSTORE and SLOAD calls, contract code reads, and other operations). However, the theoretical maximum is 30,000,000/2,100=14,285 reads. The bandwidth load of stateless clients is proportional to this number.

The current plan is to support stateless clients by transitioning Ethereum's state tree design from Merkle Patricia trees to Verkle trees. However, Verkle trees do not have post-quantum security and are not the optimal choice for newer STARK proof systems. Therefore, many are interested in supporting stateless clients through binary Merkle trees and STARKs, either completely skipping Verkle or upgrading to them after a few years of Verkle transition, once STARKs become more mature.

Based on the binary hash tree branch, STARK proofs have many advantages, but their key weakness is the long time it takes to generate proofs: Verkle trees can prove over 100,000 values per second, while hash-based STARKs typically can only prove a few thousand hashes per second, and proving each value requires including many "branches" of hashes.

Considering the numbers predicted today from highly optimized proof systems like Binius and Plonky3, as well as specialized hashes like Vision-Mark-32, it seems that proving 1000 values per second is feasible for a while, but proving 14,285 values is not. It would be fine for the average block, but potential worst-case scenario blocks (published by attackers) would disrupt the network.

Our default method for handling such situations is to reprice: increase the cost of storage reads to reduce the maximum value per block to a more secure level. However, we have done this many times already, and doing it again would make too many applications too expensive. A better approach is multidimensional Gas: imposing separate limits and charges for storage access, maintaining an average usage of 1000 storage accesses per block, but setting an upper limit for each block, for example, 2000 accesses.

Universality of Multidimensional Gas

Another resource worth considering is the growth of state size: the operations that increase the Ethereum state size, which subsequently need to be saved by full nodes. The uniqueness of state size growth is that the reasons for limiting it come entirely from long-term sustained usage, rather than peak usage.

Therefore, adding a separate Gas dimension for operations that increase state size (e.g., zero-to-non-zero SSTORE, contract creation) might be valuable, but with a different goal: we can set a floating price to target a specific average usage, but not set a limit for each block.

This demonstrates a powerful property of multidimensional Gas: it allows us to inquire separately for each resource, (i) what is the ideal average usage? (ii) what is the secure maximum usage per block? Unlike setting Gas prices based on the maximum value per block and letting the average usage follow, we have 2n degrees of freedom to set 2n parameters, adjusting each parameter based on considerations for network security.

More complex situations, such as when security considerations for two resources partially overlap, can be handled by making an opcode or resource consumption use multiple types of Gas in some quantity (e.g., a zero-to-non-zero SSTORE might consume 5000 stateless client proof Gas and 20000 storage expansion Gas).

Max per transaction (pick whichever is higher, data or computation cost)

Let 𝑥1 be the Gas cost for data, and 𝑥2 be the Gas cost for computation, so in a one-dimensional Gas system, we can write the Gas cost for a transaction as:

In this scheme, we define the Gas cost for a transaction as:

In other words, transactions are charged based on which of the two resources they consume more of, rather than the sum of data and computation. This can easily be extended to cover more dimensions (e.g., 𝑚𝑎𝑥(…,𝑥3∗𝑠𝑡𝑜𝑟𝑎𝑔𝑒_𝑎𝑐𝑐𝑒𝑠𝑠)).

It should be easy to see how this increases throughput while ensuring security. The theoretical maximum data size in a block is still Gas LIMIT /𝑥1, the same as in the one-dimensional Gas scheme. Similarly, the theoretical maximum computation size is Gas LIMIT /𝑥2, also the same as in the one-dimensional Gas scheme. However, the Gas cost for any transaction consuming data and computation is reduced.

This is probably the approach adopted in the proposed EIP-7623, to reduce the maximum block size while further increasing the blob count. The precise mechanism in EIP-7623 is slightly more complex: it keeps the current calldata price at 16 Gas per byte but adds a floor price of 48 Gas per byte; transactions pay the higher of (16 * bytes + execution_Gas) and (48 * bytes). Therefore, EIP-7623 reduces the theoretical maximum transaction call data in a block from about 1.9 MB to about 0.6 MB, while keeping costs for most applications unchanged. The benefit of this approach is that it requires very minimal changes compared to the current one-dimensional Gas scheme, making it very easy to implement.

However, this approach has two drawbacks:

  1. Even if all other transactions in a block use very little of that resource, transactions that heavily consume one resource will still unnecessarily incur a large cost;
  2. It incentivizes bundling data-intensive and computation-intensive transactions together to save costs.

I believe that rules like those in EIP-7623, whether for transaction calldata or other resources, can bring significant benefits, even with these drawbacks.

However, if we are willing to invest (significantly higher) development effort, a more ideal approach will emerge.

Multidimensional EIP-1559: A More Challenging but Ideal Strategy

Let's first review how regular EIP-1559 works. We will focus on the version introduced for blobs in EIP-4844, as it is more mathematically elegant.

We track a parameter excess_blobs. During each block, we set:

excessblobs = max(excessblobs + len(block.blobs) - TARGET, 0)

where TARGET = 3. This means that if a block has more blobs than the target, excessblobs increases, and if a block has fewer blobs than the target, excessblobs decreases. Then we set blobbasefee = exp(excessblobs / 25.47), where exp is the approximate value of the exponential function 𝑒𝑥𝑝(𝑥)=2.71828^𝑥.

In other words, every time excessblobs increases by about 25, the blob base fee increases by about 2.7 times. If blobs become too expensive, the average usage decreases, and excessblobs starts to decrease, automatically lowering the price again. The price of blobs is continuously adjusted to ensure that, on average, blocks are half full, meaning each block contains an average of 3 blobs.

If there is a short-term peak in usage, there will be a constraint: each block can contain a maximum of 6 blobs, in which case transactions can compete with each other by increasing the priority fee. However, under normal circumstances, each blob only needs to pay the blob_basefee plus a small additional priority fee as an incentive for inclusion.

This type of Gas pricing has been in Ethereum for many years: as early as 2020, EIP-1559 introduced a very similar mechanism. Through EIP-4844, we have set two independent floating prices for Gas and Blobs.

Note: Gas base fees within one hour on May 8, 2024, in gwei. Source: ultrasound.money

In principle, we can add more independent floating fees for storage reads and other types of operations, but I will detail an issue to be aware of in the next section.

For users, this experience is very similar to today: you no longer pay a base fee, but instead pay two base fees, which your wallet can abstract away from you, showing you only the expected fee and the maximum fee you can expect to pay.

For block builders, the optimal strategy is mostly the same as today: include any valid content. Most blocks are not full—whether in Gas or Blobs. A challenging situation arises when there is enough Gas or enough Blobs to exceed the block limit, and builders potentially need to solve a multidimensional knapsack problem to maximize their profit. However, even with fairly good approximation algorithms, the gains from optimizing profit through proprietary algorithms in this case are much smaller than the gains from using MEV for the same operations.

For developers, the main challenge is the need to redesign the functionality of the EVM and its related infrastructure, which is currently designed based on a single price and a single limit, to accommodate multiple prices and multiple limits.

One issue faced by application developers is that optimization becomes slightly more difficult: in some cases, you can no longer explicitly say that A is more efficient than B, because if A uses more calldata and B uses more execution, A may be cheaper when calldata is cheap and more expensive when calldata is expensive.

However, developers can still achieve fairly good results by optimizing based on long-term historical average prices.

Multidimensional Pricing, EVM, and Sub-calls

There is an issue that did not arise in blobs, and will not arise in EIP-7623 or even in a complete multidimensional pricing implementation for calldata, but it will arise if we attempt to separately price state access or any other resource: the Gas limit in sub-calls.

Gas limits in the EVM exist in two places. First, each transaction sets a Gas limit, restricting the total amount of Gas that can be used in that transaction. Second, when a contract calls another contract, that call can set its own Gas limit. This allows contracts to call other contracts they do not trust and still ensure they have remaining Gas to perform other computations after the call.

Note: Trace of an abstract transaction, where one account calls another account and provides a limited amount of Gas to the callee to ensure that even if the callee consumes all the Gas allocated to it, the external call can continue running.

The challenge is that achieving multidimensional Gas between different types of execution seems to require sub-calls to provide multiple limits for each type of Gas, which would require very deep changes to the EVM and be incompatible with existing applications.

This is one of the reasons why multidimensional Gas proposals typically stay at two dimensions: data and execution. Data (whether transaction calldata or blob) is allocated externally to the EVM, so no changes are needed internally to the EVM to separately price calldata or blob.

We can come up with an "EIP-7623-style solution" to address this issue. Here's a simple implementation: charge 4 times the fee for storage operations during execution; for simplicity, assume each storage operation costs 10,000 Gas. At the end of the transaction, refund min(7500 * storageoperations, executionGas). As a result, after deducting the refund, the user needs to pay the following fee:

executionGas + 10,000 * storageoperations - min(7500 * storageoperations, executionGas)

This is equal to:

max(executionGas + 2500 * storageoperations, 10,000 * storage_operations)

This reflects the structure of EIP-7623. Another approach is to track storageoperations and executionGas in real-time and charge 2500 or 10,000 based on how much max(executionGas + 2500 * storageoperations, 10,000 * storage_operations) increases at the time of the CALL opcode. This avoids transactions needing to over-allocate Gas, which is mainly recovered through refunds.

We do not get fine-grained allowances for sub-calls: sub-calls may consume all the allowance of a transaction for cheap storage operations.

But we do get something fairly good, which is that contracts making sub-calls can set a limit and ensure that once the sub-call is done, the caller still has enough Gas for the necessary post-processing.

The simplest "complete multidimensional pricing solution" I can think of is this: we treat the Gas limit for sub-calls as proportional. That is, suppose there are 𝑘 different types of execution, and each transaction sets multidimensional limits 𝐿1…𝐿𝑘. Suppose at the current execution point, the remaining Gas is 𝑔1…𝑔𝑘. Suppose a CALL opcode is invoked, and the sub-call Gas limit is 𝑆. Let 𝑠1=𝑆, then 𝑠2=𝑠1/𝑔1∗𝑔2, 𝑠3=𝑠1/𝑔1∗𝑔3, and so on.

In other words, we treat the Gas for the first type of execution (actually VM execution) as a special "unit of account," and then allocate the other types of Gas so that the sub-call gets the same percentage of available Gas in each type of Gas. This approach is a bit ugly, but maximizes backward compatibility.

If we want to make this scheme more "neutral" between different types of Gas without sacrificing backward compatibility, we can simply represent the Gas limit parameter for sub-calls as a fraction of the remaining Gas in the current context (e.g., [1…63]/64).

However, in either case, it is worth emphasizing that once we start introducing multidimensional execution Gas, inherent complexity (ugliness) will increase, which seems difficult to avoid.

Therefore, our task is to make a complex trade-off: do we accept a certain degree of complexity (ugliness) increase at the EVM level to safely unlock significant L1 scalability gains, and if so, which specific proposal is most effective for the protocol economy and application developers? It is quite likely that neither of the two approaches I mentioned above is the best, but there is still room for more elegant and better solutions to be proposed.

Special thanks to Ansgar Dietrichs, Barnabe Monnot, and Davide Crapis for their feedback and review.

免责声明:本文章仅代表作者个人观点,不代表本平台的立场和观点。本文章仅供信息分享,不构成对任何人的任何投资建议。用户与作者之间的任何争议,与本平台无关。如网页中刊载的文章或图片涉及侵权,请提供相关的权利证明和身份证明发送邮件到support@aicoin.com,本平台相关工作人员将会进行核查。

Share To
APP

X

Telegram

Facebook

Reddit

CopyLink