Skip to content

fix(l2): limit block to blob size #2292

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 52 commits into from
Apr 16, 2025
Merged

fix(l2): limit block to blob size #2292

merged 52 commits into from
Apr 16, 2025

Conversation

avilagaston9
Copy link
Contributor

@avilagaston9 avilagaston9 commented Mar 21, 2025

Motivation

With our current implementation, if a block state diff exceeds the blob size, we are unable to commit that block.

Description

This PR provides an initial solution by calculating the state diff size after including each transaction in the block. If the size exceeds the blob limit, the previous state is restored and transactions continue to be added from the remaining senders in the mempool.

Observations

  • blockchain::build_payload was "replicated" in block_producer/payload_builder.rs with two key differences:
    • It doesn't call blockchain::apply_system_operations.
    • It uses a custom L2 implementation of fill_transactions which adds the logic described above.
  • Some functions in blockchain are now public to allow access from payload_builder.
  • PayloadBuildContext now contains am owned Block instead of a mutable reference of it, as we need to be able to clone the PayloadBuildContext to restore a previous state.
  • PayloadBuildContext is cloned before each transaction execution, which may impact performance.
  • After each transaction, vm.get_state_transitions() is called to compute the state diff size.
  • Since REVM::vm.get_state_transitions() mutates the PayloadBuildContext, we need to clone it to avoid unintended changes.
  • An accounts_info_cache is used to prevent calling get_account_info on every tx execution.

Warning

  • REVM: Payload building is 10x slower due to frequent clone() calls.
  • LEVM: Payload building is 100x slower because LEVM::get_state_transitions internally calls get_account_info.

These issues will be addressed in future PRs.

Copy link

github-actions bot commented Mar 21, 2025

Lines of code report

Total lines added: 349
Total lines removed: 25
Total lines changed: 374

Detailed view
+--------------------------------------------------------------+-------+------+
| File                                                         | Lines | Diff |
+--------------------------------------------------------------+-------+------+
| ethrex/crates/blockchain/payload.rs                          | 548   | -3   |
+--------------------------------------------------------------+-------+------+
| ethrex/crates/l2/sequencer/block_producer.rs                 | 111   | +5   |
+--------------------------------------------------------------+-------+------+
| ethrex/crates/l2/sequencer/block_producer/payload_builder.rs | 225   | +225 |
+--------------------------------------------------------------+-------+------+
| ethrex/crates/l2/sequencer/errors.rs                         | 181   | +11  |
+--------------------------------------------------------------+-------+------+
| ethrex/crates/l2/sequencer/l1_committer.rs                   | 383   | -22  |
+--------------------------------------------------------------+-------+------+
| ethrex/crates/l2/sequencer/state_diff.rs                     | 466   | +56  |
+--------------------------------------------------------------+-------+------+
| ethrex/crates/l2/utils/error.rs                              | 22    | +5   |
+--------------------------------------------------------------+-------+------+
| ethrex/crates/l2/utils/helpers.rs                            | 24    | +24  |
+--------------------------------------------------------------+-------+------+
| ethrex/crates/l2/utils/mod.rs                                | 6     | +1   |
+--------------------------------------------------------------+-------+------+
| ethrex/crates/networking/rpc/engine/payload.rs               | 684   | +1   |
+--------------------------------------------------------------+-------+------+
| ethrex/crates/vm/backends/mod.rs                             | 242   | +1   |
+--------------------------------------------------------------+-------+------+
| ethrex/crates/vm/backends/revm/db.rs                         | 225   | +19  |
+--------------------------------------------------------------+-------+------+
| ethrex/crates/vm/levm/src/vm.rs                              | 440   | +1   |
+--------------------------------------------------------------+-------+------+

@@ -23,6 +23,26 @@ pub enum EvmState {
Execution(Box<revm::db::CacheDB<ExecutionDB>>),
}

impl Clone for EvmState {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was needed to restore the previous EVM state after executing a transaction whose resulting state diff doesn't fit in a blob.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That merits a code comment I believe. My first instinct in a few months after seeing this would be to try and convert it into a derive, breaking the logic. Can't be the only one.

Also, where this differs from the derive(Clone) should also be commented. I guess it's the cache clone at the end?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem was that revm::db::State doesn't implement the Clone trait. I added a comment here: dc9d5d8!

@avilagaston9 avilagaston9 self-assigned this Mar 21, 2025
@avilagaston9 avilagaston9 marked this pull request as ready for review March 21, 2025 22:27
@avilagaston9 avilagaston9 requested a review from a team as a code owner March 21, 2025 22:27
@@ -540,6 +567,132 @@ impl Blockchain {
context.account_updates = account_updates;
Ok(())
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the idea is not to pollute l1 with a lot of l2 code, can't this go inside l2 crate?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved tol2/sequencer/block_producer/payload_builder.rs!

@avilagaston9 avilagaston9 marked this pull request as draft March 25, 2025 13:17
&mut accounts_info_cache,
)? {
debug!(
"Skipping transaction: {}, doesn't feet in blob_size",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"doesn't feet" -> "doesn't fit"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 621592b!

@jrchatruc
Copy link
Collaborator

Let's add a similar "short circuit" if the current state diff size is too close to the limit for almost any meaningful transaction to get through, similar to this check for gas

// Check if we have enough gas to run more transactions
if context.remaining_gas < TX_GAS_COST {
  debug!("No more gas to run transactions");
  break;
};

we could use the state diff cost for a regular transfer transaction that touches new storage slots as a baseline.

@avilagaston9
Copy link
Contributor Author

Let's add a similar "short circuit" if the current state diff size is too close to the limit for almost any meaningful transaction to get through, similar to this check for gas
@jrchatruc

Added in 3169adb!

Copy link

github-actions bot commented Apr 3, 2025

Benchmark Results Comparison

PR Results

Benchmark Results: Factorial

Command Mean [ms] Min [ms] Max [ms] Relative
revm_Factorial 240.3 ± 2.2 239.1 246.4 1.00
levm_Factorial 803.6 ± 3.0 799.3 808.8 3.34 ± 0.03

Benchmark Results: Factorial - Recursive

Command Mean [s] Min [s] Max [s] Relative
revm_FactorialRecursive 1.427 ± 0.095 1.319 1.572 1.00
levm_FactorialRecursive 13.893 ± 0.020 13.866 13.916 9.73 ± 0.65

Benchmark Results: Fibonacci

Command Mean [ms] Min [ms] Max [ms] Relative
revm_Fibonacci 215.2 ± 0.7 214.3 216.4 1.00
levm_Fibonacci 792.0 ± 6.3 783.3 801.1 3.68 ± 0.03

Benchmark Results: ManyHashes

Command Mean [ms] Min [ms] Max [ms] Relative
revm_ManyHashes 9.0 ± 0.3 8.6 9.7 1.00
levm_ManyHashes 16.7 ± 0.4 16.3 17.6 1.86 ± 0.07

Benchmark Results: BubbleSort

Command Mean [s] Min [s] Max [s] Relative
revm_BubbleSort 3.220 ± 0.046 3.187 3.346 1.00
levm_BubbleSort 5.707 ± 0.040 5.619 5.756 1.77 ± 0.03

Benchmark Results: ERC20 - Transfer

Command Mean [ms] Min [ms] Max [ms] Relative
revm_ERC20Transfer 248.2 ± 7.4 244.0 268.2 1.00
levm_ERC20Transfer 487.9 ± 4.1 482.5 494.7 1.97 ± 0.06

Benchmark Results: ERC20 - Mint

Command Mean [ms] Min [ms] Max [ms] Relative
revm_ERC20Mint 139.1 ± 0.6 137.8 139.9 1.00
levm_ERC20Mint 318.5 ± 5.3 313.5 328.4 2.29 ± 0.04

Benchmark Results: ERC20 - Approval

Command Mean [s] Min [s] Max [s] Relative
revm_ERC20Approval 1.034 ± 0.010 1.025 1.059 1.00
levm_ERC20Approval 1.866 ± 0.014 1.850 1.893 1.80 ± 0.02

Main Results

Benchmark Results: Factorial

Command Mean [ms] Min [ms] Max [ms] Relative
revm_Factorial 239.9 ± 1.0 239.1 241.7 1.00
levm_Factorial 804.7 ± 6.7 797.6 820.8 3.35 ± 0.03

Benchmark Results: Factorial - Recursive

Command Mean [s] Min [s] Max [s] Relative
revm_FactorialRecursive 1.415 ± 0.085 1.312 1.527 1.00
levm_FactorialRecursive 13.922 ± 0.050 13.870 14.001 9.84 ± 0.59

Benchmark Results: Fibonacci

Command Mean [ms] Min [ms] Max [ms] Relative
revm_Fibonacci 219.8 ± 6.9 214.7 237.5 1.00
levm_Fibonacci 798.4 ± 10.6 782.3 823.4 3.63 ± 0.12

Benchmark Results: ManyHashes

Command Mean [ms] Min [ms] Max [ms] Relative
revm_ManyHashes 8.6 ± 0.1 8.5 8.7 1.00
levm_ManyHashes 16.5 ± 0.2 16.2 16.8 1.93 ± 0.03

Benchmark Results: BubbleSort

Command Mean [s] Min [s] Max [s] Relative
revm_BubbleSort 3.202 ± 0.030 3.175 3.279 1.00
levm_BubbleSort 5.692 ± 0.154 5.569 6.012 1.78 ± 0.05

Benchmark Results: ERC20 - Transfer

Command Mean [ms] Min [ms] Max [ms] Relative
revm_ERC20Transfer 246.7 ± 3.3 243.8 252.7 1.00
levm_ERC20Transfer 488.5 ± 4.1 481.8 495.0 1.98 ± 0.03

Benchmark Results: ERC20 - Mint

Command Mean [ms] Min [ms] Max [ms] Relative
revm_ERC20Mint 139.9 ± 0.9 138.9 141.4 1.00
levm_ERC20Mint 314.2 ± 2.1 310.7 317.0 2.25 ± 0.02

Benchmark Results: ERC20 - Approval

Command Mean [s] Min [s] Max [s] Relative
revm_ERC20Approval 1.026 ± 0.009 1.020 1.049 1.00
levm_ERC20Approval 1.859 ± 0.022 1.833 1.892 1.81 ± 0.03

Comment on lines +115 to +120
if !blob_txs.is_empty() && context.blobs_bundle.blobs.len() >= max_blob_number_per_block {
debug!("No more blob gas to run blob transactions");
blob_txs.clear();
}
// Fetch the next transactions
let (head_tx, is_blob) = match (plain_txs.peek(), blob_txs.peek()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we won't support blob txs in the L2, so maybe this is unnecessary

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we almost certainly won't support them, but I would tackle this in a separate PR where we remove all blob txs support on the L2

Comment on lines 230 to 238
async fn check_state_diff_size(
acc_withdrawals_size: &mut usize,
acc_deposits_size: &mut usize,
acc_state_diff_size: &mut usize,
tx: Transaction,
receipt: &Receipt,
context: &mut PayloadBuildContext,
accounts_info_cache: &mut HashMap<Address, Option<AccountInfo>>,
) -> Result<bool, BlockProducerError> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd add a doc comment saying what changes it'll do to the mut variables. Also maybe consider rename the function to something like update_state_diff_size

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 0a1050a!

@jrchatruc jrchatruc enabled auto-merge April 16, 2025 14:55
@jrchatruc jrchatruc added this pull request to the merge queue Apr 16, 2025
Merged via the queue into main with commit febb4dd Apr 16, 2025
49 checks passed
@jrchatruc jrchatruc deleted the l2/limit_block_to_blob_size branch April 16, 2025 15:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants