Skip to content

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

Merged
iovoid merged 14 commits into
mainfrom
refactor/separate-state-transitions
Apr 23, 2025
Merged

refactor(levm,l1,l2): split block execution and update generation#2519
iovoid merged 14 commits into
mainfrom
refactor/separate-state-transitions

Conversation

@iovoid

@iovoid iovoid commented Apr 22, 2025

Copy link
Copy Markdown
Contributor

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
@github-actions

github-actions Bot commented Apr 22, 2025

Copy link
Copy Markdown

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 Block execution throughput and performance in general 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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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.

Comment thread crates/blockchain/blockchain.rs Outdated
Ok(v) => v,
Err(err) => return Err((ChainError::EvmError(err), None)),
};
for account_update in account_updates {

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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.

@github-actions

github-actions Bot commented Apr 22, 2025

Copy link
Copy Markdown

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 thread crates/blockchain/blockchain.rs Outdated
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
Copy Markdown
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
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

panic!("invalid database")
};

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

Copy link
Copy Markdown
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
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed

@JereSalo JereSalo left a comment

Copy link
Copy Markdown
Contributor

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!

@iovoid iovoid left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated

@JulianVentura JulianVentura left a comment

Copy link
Copy Markdown
Contributor

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
@iovoid iovoid deleted the refactor/separate-state-transitions branch April 23, 2025 19:29
pedrobergamini pushed a commit to pedrobergamini/ethrex that referenced this pull request Aug 24, 2025
…mbdaclass#2519)

**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.

<!-- Link to issues: Resolves lambdaclass#111, Resolves lambdaclass#222 -->

Closes lambdaclass#2504
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 Block execution throughput and performance in general

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LEVM: Separate get_state_transitions from execute_block

5 participants