-
Notifications
You must be signed in to change notification settings - Fork 2
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
[wip]: BlockExecutor
#1
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,3 +15,116 @@ | |
// complex unless we operate on something that yields `Tx`, and not blocks, this iterator style | ||
// type must be aware of things like `gas_used`, invalid tx, etc... so that we can properly build | ||
// a block. | ||
|
||
use crate::evm::Evm; | ||
|
||
/// Abstraction over block execution input. | ||
/// | ||
/// This type must contain all of the context required to configure EVM and a way to obtain | ||
/// transactions for the block. | ||
pub trait BlockExecutionInput { | ||
/// Transaction type. | ||
type Transaction; | ||
} | ||
|
||
/// Abstraction over block execution outcome. | ||
pub trait BlockExecutionOutcome { | ||
/// Receipt type. | ||
type Receipt; | ||
|
||
fn receipts(&self) -> &[Self::Receipt]; | ||
} | ||
|
||
/// Abstraction over type that is capable of executing a block. | ||
/// | ||
/// This type knows how to configure an underlying EVM and execute transactions on top of it along | ||
/// with any additional pre/post execution actions. | ||
pub trait BlockExecutor { | ||
/// Input for the block execution. | ||
type Input: BlockExecutionInput; | ||
|
||
/// Outcome of the block execution. | ||
type Output: BlockExecutionOutcome; | ||
|
||
/// Errors that can occur during block execution. | ||
type Error; | ||
|
||
fn execute(&mut self, input: Self::Input) -> Result<Self::Output, Self::Error>; | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use crate::evm::{self, EvmFactory}; | ||
|
||
use super::*; | ||
|
||
trait ReceiptBuilder<Tx, EvmRes> { | ||
type Receipt: Default; | ||
|
||
fn build_receipt<'a>( | ||
&self, | ||
ctx: &'a BlockExecutorContext, | ||
tx: &'a Tx, | ||
evm_res: &'a EvmRes, | ||
) -> Self::Receipt; | ||
} | ||
|
||
#[derive(Default)] | ||
struct BlockExecutorContext { | ||
gas_used: u64, | ||
blob_gas_used: u64, | ||
} | ||
|
||
struct BlockExecutorOutput<T> { | ||
receipts: Vec<T>, | ||
} | ||
|
||
struct EthBlockExecutor<EvmF, ReceiptB, T> { | ||
evm_factory: EvmF, | ||
receipt_builder: ReceiptB, | ||
_pd: core::marker::PhantomData<T>, | ||
} | ||
|
||
impl<EvmF, ReceiptB, T> BlockExecutor for EthBlockExecutor<EvmF, ReceiptB, T> | ||
where | ||
EvmF: EvmFactory<Evm: Evm<Tx: From<T>>>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this could be problematic where tx is like |
||
ReceiptB: ReceiptBuilder<T, <EvmF::Evm as Evm>::Outcome>, | ||
{ | ||
type Input = alloy_consensus::Block<T>; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should perhaps consider adding some convenience type here so that we can accept any block variant, e.g. with recovered senders. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Think API wise you want the block to be either type, and if it's not recovered it should do the recovery for you -- so probably a T: Into RecoveredBlock? |
||
type Output = BlockExecutorOutput<ReceiptB::Receipt>; | ||
type Error = <EvmF::Evm as Evm>::Error; | ||
|
||
fn execute(&mut self, input: Self::Input) -> Result<Self::Output, Self::Error> { | ||
// This should use block header and additional context (chainspec?) to create the evm | ||
// config. | ||
let evm_input: EvmF::Input = todo!(); | ||
let mut evm = self.evm_factory.create_evm(evm_input); | ||
|
||
let mut ctx = BlockExecutorContext::default(); | ||
let mut receipts = Vec::new(); | ||
|
||
for tx in input.body.transactions { | ||
let result = evm.transact(tx.into())?; | ||
// ctx.gas_used += result.gas_used(); | ||
// ctx.blob_gas_used += result.blob_gas_used(); | ||
|
||
let receipt = self.receipt_builder.build_receipt(&ctx, &tx, &result); | ||
receipts.push(receipt); | ||
} | ||
|
||
Ok(BlockExecutorOutput { receipts }) | ||
} | ||
} | ||
|
||
impl<T> BlockExecutionInput for alloy_consensus::Block<T> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: this should still support somebody adding a custom block executor, say, enforcing inclusion lists from the header |
||
type Transaction = T; | ||
} | ||
|
||
impl<R> BlockExecutionOutcome for BlockExecutorOutput<R> { | ||
type Receipt = R; | ||
|
||
fn receipts(&self) -> &[Self::Receipt] { | ||
&self.receipts | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,3 +9,4 @@ | |
|
||
pub mod block; | ||
pub mod evm; | ||
pub use evm::{Evm, EvmFactory}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cool, yep I think this is it.
pretty simple and should allow for any kind of restrictions that may be necessary