Skip to content
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

feat: block executor provider and ethereum + op impl #7594

Merged
merged 59 commits into from Apr 19, 2024
Merged

Conversation

mattsse
Copy link
Collaborator

@mattsse mattsse commented Apr 12, 2024

the current BlockExecutor trait is intended for batch execution

we need one for single blocks, the batch executor can then simply use this.

This introduces a few new traits for execution

  • ExecutorProvider: responsible for configuring the executors given a DB as input

Batch and single block execution are now two separate parts

The OP and ETH currently has some duplicated code, but we want keep this because a lot will change wrt primitive types so unifying them at this point would introduce issues in future.

This only adds the types
replacing the EVMProcessor with this should be done in a separate pr

This will unlock a ton of cleanups wrt optimism types and project layout

Note: this does use GATs for the provider

@mattsse mattsse changed the title wip!feat: block executor trait feat: block executor trait and evm impl Apr 17, 2024
@mattsse mattsse marked this pull request as ready for review April 17, 2024 14:39
mattsse and others added 9 commits April 18, 2024 14:42
Co-authored-by: Oliver Nordbjerg <onbjerg@users.noreply.github.com>
Co-authored-by: Oliver Nordbjerg <onbjerg@users.noreply.github.com>
Co-authored-by: Oliver Nordbjerg <onbjerg@users.noreply.github.com>
Co-authored-by: Oliver Nordbjerg <onbjerg@users.noreply.github.com>
Co-authored-by: Oliver Nordbjerg <onbjerg@users.noreply.github.com>
Co-authored-by: Oliver Nordbjerg <onbjerg@users.noreply.github.com>
Co-authored-by: Oliver Nordbjerg <onbjerg@users.noreply.github.com>
Co-authored-by: Oliver Nordbjerg <onbjerg@users.noreply.github.com>
Co-authored-by: Oliver Nordbjerg <onbjerg@users.noreply.github.com>
@mattsse
Copy link
Collaborator Author

mattsse commented Apr 18, 2024

is this something we can fit in? if not, is this deliberate?

I think we can eventually make this work

I deliberately abstracted the output type so we can change the behaviour more easily

Copy link
Member

@emhane emhane left a comment

Choose a reason for hiding this comment

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

this is super nice. docs and comments are very helpful. code is clean, so even without reading docs it's easy to follow.

would be nice to group some of the code inside of the op execute function into smaller functions, for example group the checks related to gas into one function, although I'm aware the different checks are probably in this specific order for a reason, hence may not be able to group in that way.

crates/evm-ethereum/src/execute.rs Show resolved Hide resolved
crates/evm-ethereum/src/execute.rs Show resolved Hide resolved
crates/evm-ethereum/src/execute.rs Show resolved Hide resolved
crates/evm-ethereum/src/execute.rs Show resolved Hide resolved
crates/node-optimism/src/evm/execute.rs Show resolved Hide resolved
@mattsse
Copy link
Collaborator Author

mattsse commented Apr 18, 2024

would be nice to group some of the code inside of the op execute function into smaller functions

yes ideally, we can come up with a reusable design that separates this into

  • pre
  • txs
  • post

but we're not there yet, so I want to keep it like this for now

Copy link
Member

@onbjerg onbjerg left a comment

Choose a reason for hiding this comment

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

godspeed 🫡

Copy link
Member

@gakonst gakonst left a comment

Choose a reason for hiding this comment

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

As I understand this you need this refactor to continue being productive in the executor-related code and retire the previous code, so I am supportive of it from that sense.

It feels like we need to get to an Executor abstraction over the Runtime & its config that allows:

  1. Instantiate the Executor with an Ethereum-like header payload for the runtime
  2. Streaming inputs on a Transaction-level granularity into it over a channel (instead of over block level), so that we can do IO while we're executing
  3. Allow pre-execution and post-execution hooks, at the block-level granularity (for 4788 etc.), and perhaps transaction-level granularity for OP-style deposit transaction nonce caching thing?
  4. On executor-defined intervals, stream outputs to a cache and/or DB writer

Comment on lines +147 to +154
// apply pre execution changes
apply_beacon_root_contract_call(
&self.chain_spec,
block.timestamp,
block.number,
block.parent_beacon_block_root,
&mut evm,
)?;
Copy link
Member

Choose a reason for hiding this comment

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

This should be generalized to a hook exposed to the Node Builder for both pre and post exec cc @Rjected re: Electra EIPs

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this is the idea, but I just want to get rid of the EVMprocessor asap because this is blocking everything else

fn execute_pre_and_transactions<Ext, DB>(
&mut self,
block: &BlockWithSenders,
mut evm: Evm<'_, Ext, &mut State<DB>>,
Copy link
Member

Choose a reason for hiding this comment

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

Is the &mut an issue for parallelism down the line?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

a paralle impl needs its own implementation

error: err.into(),
}
})?;
evm.db_mut().commit(state);
Copy link
Member

Choose a reason for hiding this comment

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

Confirming this does no IO right, just inmem?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

just in mem

tbh I find the entire bundle state thing super confusing and actually not sure if this is need for the single block executor

Copy link
Member

Choose a reason for hiding this comment

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

It's so that we can cache effectively across blocks while also keeping all the created/destroyed information in one place (vs clone'ing it / recalculating it like we did before with PostState)

crates/evm-ethereum/src/execute.rs Outdated Show resolved Hide resolved
/// It does __not__ apply post-execution changes.
fn execute_pre_and_transactions<Ext, DB>(
&mut self,
block: &BlockWithSenders,
Copy link
Member

Choose a reason for hiding this comment

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

I hope that we can get to a world where we can stream reads from a TxReader service into an Executor one-by-one over a channel, vs having to load them all in mem and then pass on the entire batch to the executor, can we accommodate that?

Comment on lines +41 to +45
/// The output of an executed block in a batch.
#[derive(Debug, Clone, Copy)]
pub struct BatchBlockOutput {
/// The size hint of the batch's tracked state.
pub size_hint: Option<usize>,
Copy link
Member

Choose a reason for hiding this comment

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

a bit confusing type, I'd think BatchBlockOutput includes a Vec<Self::Output> above

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

it does not because all changes are committed to the same bundlestate which then gets consumed and written to db

Comment on lines +63 to +70
/// A helper type for ethereum block inputs that consists of a block and the total difficulty.
#[derive(Debug)]
pub struct EthBlockExecutionInput<'a, Block> {
/// The block to execute.
pub block: &'a Block,
/// The total difficulty of the block.
pub total_difficulty: U256,
}
Copy link
Member

Choose a reason for hiding this comment

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

I like the Input being generic over the block, I'd think that we can make the total_difficulty an External generic such that we get rid of it as a term as it's no longer relevant Post-merge

Comment on lines +59 to +60
/// The total gas used by the block.
pub gas_used: u64,
Copy link
Member

Choose a reason for hiding this comment

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

I'm good with this being concrete, would encourage us to proactively think about what will multi dimensional pricing look like in the future, e.g. thinking about "local fee markets" in parevm

crates/evm/src/execute.rs Outdated Show resolved Hide resolved
Comment on lines +195 to +211
// Cache the depositor account prior to the state transition for the deposit nonce.
//
// Note that this *only* needs to be done post-regolith hardfork, as deposit nonces
// were not introduced in Bedrock. In addition, regular transactions don't have deposit
// nonces, so we don't need to touch the DB for those.
let depositor = (is_regolith && transaction.is_deposit())
.then(|| {
evm.db_mut()
.load_cache_account(*sender)
.map(|acc| acc.account_info().unwrap_or_default())
})
.transpose()
.map_err(|_| {
BlockExecutionError::OptimismBlockExecution(
OptimismBlockExecutionError::AccountLoadFailed(*sender),
)
})?;
Copy link
Member

Choose a reason for hiding this comment

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

this feels hacky not sure how to pull out nicely

Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
@mattsse
Copy link
Collaborator Author

mattsse commented Apr 18, 2024

As I understand this you need this refactor to continue being productive in the executor-related code and retire the previous code, so I am supportive of it from that sense.

It feels like we need to get to an Executor abstraction over the Runtime & its config that allows:

  1. Instantiate the Executor with an Ethereum-like header payload for the runtime
  2. Streaming inputs on a Transaction-level granularity into it over a channel (instead of over block level), so that we can do IO while we're executing
  3. Allow pre-execution and post-execution hooks, at the block-level granularity (for 4788 etc.), and perhaps transaction-level granularity for OP-style deposit transaction nonce caching thing?
  4. On executor-defined intervals, stream outputs to a cache and/or DB writer

yes the EVMProcessor leaks into everything and is very bad because it is mutually exclusive with optimism/ethereum this needs to go asap.

all of these suggestions are future work, but we need this separation now, then we can make changes more easily.

@mattsse mattsse added this pull request to the merge queue Apr 19, 2024
Merged via the queue into main with commit 1c46e5a Apr 19, 2024
28 checks passed
@mattsse mattsse deleted the matt/executor-trait branch April 19, 2024 09:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-execution Related to the Execution and EVM
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants