Skip to content

refactor(levm,l1,l2): split block execution and update generation #2519

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 14 commits into from
Apr 23, 2025

Conversation

iovoid
Copy link
Contributor

@iovoid iovoid commented Apr 22, 2025

Motivation

Currently during batch processing, the state transitions are calculated for every block and then merged, when it would be more performant to calculate them once at the end.

Description

This PR removes the account updates from the execution result and makes every consumer manually request them.

Closes #2504

@iovoid iovoid requested a review from a team as a code owner April 22, 2025 12:35
Copy link

github-actions bot commented Apr 22, 2025

Lines of code report

Total lines added: 15
Total lines removed: 11
Total lines changed: 26

Detailed view
+----------------------------------------------------------+-------+------+
| File                                                     | Lines | Diff |
+----------------------------------------------------------+-------+------+
| ethrex/crates/blockchain/blockchain.rs                   | 494   | +9   |
+----------------------------------------------------------+-------+------+
| ethrex/crates/l2/prover/zkvm/interface/risc0/src/main.rs | 46    | +1   |
+----------------------------------------------------------+-------+------+
| ethrex/crates/l2/prover/zkvm/interface/sp1/src/main.rs   | 43    | +1   |
+----------------------------------------------------------+-------+------+
| ethrex/crates/l2/sequencer/l1_committer.rs               | 387   | +4   |
+----------------------------------------------------------+-------+------+
| ethrex/crates/vm/backends/levm/mod.rs                    | 637   | -5   |
+----------------------------------------------------------+-------+------+
| ethrex/crates/vm/backends/mod.rs                         | 241   | -1   |
+----------------------------------------------------------+-------+------+
| ethrex/crates/vm/backends/revm/mod.rs                    | 607   | -5   |
+----------------------------------------------------------+-------+------+

@iovoid iovoid added performance levm Lambda EVM implementation L2 Rollup client L1 Ethereum client labels Apr 22, 2025
if chain_config.fork(block.header.timestamp) != fork {
return Err((
ChainError::Custom("Crossing fork boundary in bulk mode".into()),
Some(BatchBlockProcessingFailure {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think is a bit confusing, since the problem is not that the block n is invalid, but that the caller made a mistake by calling this functions with blocks from different forks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe a new error variant?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Sounds good.

Copy link
Contributor

Choose a reason for hiding this comment

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

We should handle this (if we cannot avoid it) in caller code (aka full sync) as crossing fork boundaries in a batch is something that will eventually happen when syncing networks. This probably exceeds the scope of this PR but we should create an issue for it

Copy link
Collaborator

Choose a reason for hiding this comment

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

agreed with @fmoletta, we should create a ticket and handle this from the caller. But I think it's better to also check this from inside and return an error.

Copy link
Collaborator

Choose a reason for hiding this comment

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

See conversation in slack. This shouldn't be a problem since we're only dealing with post merge networks and we should be able to do batch block processing across fork boundaries.

Ok(v) => v,
Err(err) => return Err((ChainError::EvmError(err), None)),
};
for account_update in account_updates {
Copy link
Collaborator

Choose a reason for hiding this comment

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

not sure what this does, but it seems weird do do account updates processing in the blockchain crate. From a design perspective, it seems this should be done inside the vm crate and that blockchain crate should just get results and update the db?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually now that a single get_state_transition call is made, this merge isn't needed. Fixed.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yep, we also don't need to use all_account_updates nor the for loop. We can just send account_updates directly to the apply_account_updates method.

let account_updates = result.account_updates;
let _result = vm.execute_block(&block)?;
// let receipts = result.receipts;
let account_updates = vm.get_state_transitions(fork)?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

hmm, weird to pass the current fork to the get_state_transitions function. From the outside, it doesn't make much sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's needed because because SpuriousDragon changed the rules on remotion from the trie

Copy link
Collaborator

Choose a reason for hiding this comment

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

But we don't support SpuriousDragon, so maybe we can avoid this?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, I wanted to remove that in the past but the thing is that the State EF Tests test for Spurious Dragon and we use this same method. As per my understanding LEVM will ideally support all forks, but if we change opinion on that then it's ok to remove the fork.

Copy link
Collaborator

Choose a reason for hiding this comment

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

The vm should know which fork the block was executed from, so when calling get_state_transitions it should have that information internally. It should not be passed explicitly. Otherwise I could execute a Cacun block and then ask for the state transitions for SpuriousDragon

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes! The VM knows the fork but the thing is that get_state_transitions is not a method from the actual VM. It is just a function of the LEVM module in the intersection between L1 and the VM. Maybe it should be a VM method though.

Copy link
Contributor

Choose a reason for hiding this comment

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

Adding to this. Our VM just exists in the context of a transaction, after executing one we discard the VM and the only thing that's left is the modified state, so we lose that VM instance that knew which fork it was executing. I agree that it's not nice to pass the fork, for now we can just disable SpuriousDragon checks

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 LEVM DB doesn't currently keep track of which blocks it executed.

Copy link

github-actions bot commented Apr 22, 2025

Benchmark Block Execution Results Comparison Against Main

Command Mean [s] Min [s] Max [s] Relative
base 180.069 ± 1.017 178.719 181.440 1.00
head 180.115 ± 0.956 178.538 181.982 1.00 ± 0.01

Comment on lines 272 to 275
let account_updates = match vm.get_state_transitions(fork) {
Ok(v) => v,
Err(err) => return Err((ChainError::EvmError(err), None)),
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
let account_updates = match vm.get_state_transitions(fork) {
Ok(v) => v,
Err(err) => return Err((ChainError::EvmError(err), None)),
};
let account_updates = vm.get_state_transitions(fork).map_err(|err| (ChainError::EvmError(err), None))?;

nit

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@@ -39,9 +39,10 @@ pub fn main() {
panic!("invalid database")
};

let fork = db.chain_config.fork(block.header.timestamp);
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not necessary when running with REVM

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Contributor

@JereSalo JereSalo left a comment

Choose a reason for hiding this comment

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

I think it's good!

Copy link
Contributor Author

@iovoid iovoid left a comment

Choose a reason for hiding this comment

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

Updated

Copy link
Contributor

@JulianVentura JulianVentura left a comment

Choose a reason for hiding this comment

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

LGTM

@iovoid iovoid added this pull request to the merge queue Apr 23, 2025
Merged via the queue into main with commit 5a7d759 Apr 23, 2025
31 checks passed
@iovoid iovoid deleted the refactor/separate-state-transitions branch April 23, 2025 19:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
L1 Ethereum client L2 Rollup client levm Lambda EVM implementation performance
Projects
None yet
Development

Successfully merging this pull request may close these issues.

LEVM: Separate get_state_transitions from execute_block
5 participants