Skip to content

Commit

Permalink
feat: add example for using a database by reference (bluealloy#1150)
Browse files Browse the repository at this point in the history
* issue: cannot use databases by reference when using handlers

* Make db_by_ref generic

* Cleanup and cleaner debug error trait

* misc: minimise changes

* doc: add explanatory comment

---------

Co-authored-by: rakita <dragan0rakita@gmail.com>
  • Loading branch information
2 people authored and fubuloubu committed Apr 11, 2024
1 parent 02907a7 commit 863dd72
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 17 deletions.
5 changes: 5 additions & 0 deletions crates/revm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ name = "generate_block_traces"
path = "../../examples/generate_block_traces.rs"
required-features = ["std", "serde-json", "ethersdb"]

[[example]]
name = "db_by_ref"
path = "../../examples/db_by_ref.rs"
required-features = ["std", "serde-json"]

[[bench]]
name = "bench"
path = "benches/bench.rs"
Expand Down
8 changes: 4 additions & 4 deletions crates/revm/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,8 +311,8 @@ impl<'a, BuilderStage, EXT, DB: Database> EvmBuilder<'a, BuilderStage, EXT, DB>
/// When called, EvmBuilder will transition from SetGenericStage to HandlerStage.
pub fn append_handler_register(
mut self,
handle_register: register::HandleRegister<'a, EXT, DB>,
) -> EvmBuilder<'_, HandlerStage, EXT, DB> {
handle_register: register::HandleRegister<EXT, DB>,
) -> EvmBuilder<'a, HandlerStage, EXT, DB> {
self.handler
.append_handler_register(register::HandleRegisters::Plain(handle_register));
EvmBuilder {
Expand All @@ -329,8 +329,8 @@ impl<'a, BuilderStage, EXT, DB: Database> EvmBuilder<'a, BuilderStage, EXT, DB>
/// When called, EvmBuilder will transition from SetGenericStage to HandlerStage.
pub fn append_handler_register_box(
mut self,
handle_register: register::HandleRegisterBox<'a, EXT, DB>,
) -> EvmBuilder<'_, HandlerStage, EXT, DB> {
handle_register: register::HandleRegisterBox<EXT, DB>,
) -> EvmBuilder<'a, HandlerStage, EXT, DB> {
self.handler
.append_handler_register(register::HandleRegisters::Box(handle_register));
EvmBuilder {
Expand Down
12 changes: 6 additions & 6 deletions crates/revm/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub struct Handler<'a, H: Host + 'a, EXT, DB: Database> {
/// Instruction table type.
pub instruction_table: Option<InstructionTables<'a, H>>,
/// Registers that will be called on initialization.
pub registers: Vec<HandleRegisters<'a, EXT, DB>>,
pub registers: Vec<HandleRegisters<EXT, DB>>,
/// Validity handles.
pub validation: ValidationHandler<'a, EXT, DB>,
/// Pre execution handle
Expand Down Expand Up @@ -133,25 +133,25 @@ impl<'a, EXT, DB: Database> EvmHandler<'a, EXT, DB> {
}

/// Append handle register.
pub fn append_handler_register(&mut self, register: HandleRegisters<'a, EXT, DB>) {
pub fn append_handler_register(&mut self, register: HandleRegisters<EXT, DB>) {
register.register(self);
self.registers.push(register);
}

/// Append plain handle register.
pub fn append_handler_register_plain(&mut self, register: HandleRegister<'a, EXT, DB>) {
pub fn append_handler_register_plain(&mut self, register: HandleRegister<EXT, DB>) {
register(self);
self.registers.push(HandleRegisters::Plain(register));
}

/// Append boxed handle register.
pub fn append_handler_register_box(&mut self, register: HandleRegisterBox<'a, EXT, DB>) {
pub fn append_handler_register_box(&mut self, register: HandleRegisterBox<EXT, DB>) {
register(self);
self.registers.push(HandleRegisters::Box(register));
}

/// Pop last handle register and reapply all registers that are left.
pub fn pop_handle_register(&mut self) -> Option<HandleRegisters<'a, EXT, DB>> {
pub fn pop_handle_register(&mut self) -> Option<HandleRegisters<EXT, DB>> {
let out = self.registers.pop();
if out.is_some() {
let registers = core::mem::take(&mut self.registers);
Expand Down Expand Up @@ -206,7 +206,7 @@ mod test {

#[test]
fn test_handler_register_pop() {
let register = |inner: &Rc<RefCell<i32>>| -> HandleRegisterBox<'_, (), EmptyDB> {
let register = |inner: &Rc<RefCell<i32>>| -> HandleRegisterBox<(), EmptyDB> {
let inner = inner.clone();
Box::new(move |h| {
*inner.borrow_mut() += 1;
Expand Down
14 changes: 7 additions & 7 deletions crates/revm/src/handler/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@ use std::boxed::Box;
pub type EvmHandler<'a, EXT, DB> = Handler<'a, Evm<'a, EXT, DB>, EXT, DB>;

// Handle register
pub type HandleRegister<'a, EXT, DB> = fn(&mut EvmHandler<'a, EXT, DB>);
pub type HandleRegister<EXT, DB> = for<'a> fn(&mut EvmHandler<'a, EXT, DB>);

// Boxed handle register
pub type HandleRegisterBox<'a, EXT, DB> = Box<dyn Fn(&mut EvmHandler<'a, EXT, DB>)>;
pub type HandleRegisterBox<EXT, DB> = Box<dyn for<'a> Fn(&mut EvmHandler<'a, EXT, DB>)>;

pub enum HandleRegisters<'a, EXT, DB: Database> {
pub enum HandleRegisters<EXT, DB: Database> {
/// Plain function register
Plain(HandleRegister<'a, EXT, DB>),
Plain(HandleRegister<EXT, DB>),
/// Boxed function register.
Box(HandleRegisterBox<'a, EXT, DB>),
Box(HandleRegisterBox<EXT, DB>),
}

impl<'a, EXT, DB: Database> HandleRegisters<'a, EXT, DB> {
impl<EXT, DB: Database> HandleRegisters<EXT, DB> {
/// Call register function to modify EvmHandler.
pub fn register(&self, handler: &mut EvmHandler<'a, EXT, DB>) {
pub fn register(&self, handler: &mut EvmHandler<'_, EXT, DB>) {
match self {
HandleRegisters::Plain(f) => f(handler),
HandleRegisters::Box(f) => f(handler),
Expand Down
81 changes: 81 additions & 0 deletions examples/db_by_ref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use revm::{
db::{CacheDB, EmptyDB, WrapDatabaseRef},
handler::register::HandleRegister,
inspector_handle_register,
inspectors::{NoOpInspector, TracerEip3155},
primitives::ResultAndState,
DatabaseCommit, DatabaseRef, Evm,
};
use std::error::Error;

trait DatabaseRefDebugError: DatabaseRef<Error = Self::DBError> {
type DBError: std::fmt::Debug + Error + Send + Sync + 'static;
}

impl<DBError, DB> DatabaseRefDebugError for DB
where
DB: DatabaseRef<Error = DBError>,
DBError: std::fmt::Debug + Error + Send + Sync + 'static,
{
type DBError = DBError;
}

fn run_transaction<EXT, DB: DatabaseRefDebugError>(
db: DB,
ext: EXT,
register_handles_fn: HandleRegister<EXT, WrapDatabaseRef<DB>>,
) -> anyhow::Result<(ResultAndState, DB)> {
let mut evm = Evm::builder()
.with_ref_db(db)
.with_external_context(ext)
.append_handler_register(register_handles_fn)
.build();

let result = evm.transact()?;
Ok((result, evm.into_context().evm.db.0))
}

fn run_transaction_and_commit_with_ext<EXT, DB: DatabaseRefDebugError + DatabaseCommit>(
db: DB,
ext: EXT,
register_handles_fn: HandleRegister<EXT, WrapDatabaseRef<DB>>,
) -> anyhow::Result<()> {
// To circumvent borrow checker issues, we need to move the database into the
// transaction and return it after the transaction is done.
let (ResultAndState { state: changes, .. }, mut db) =
{ run_transaction(db, ext, register_handles_fn)? };

db.commit(changes);

Ok(())
}

fn run_transaction_and_commit(db: &mut CacheDB<EmptyDB>) -> anyhow::Result<()> {
let ResultAndState { state: changes, .. } = {
let rdb = &*db;

let mut evm = Evm::builder()
.with_ref_db(rdb)
.with_external_context(NoOpInspector)
.append_handler_register(inspector_handle_register)
.build();

evm.transact()?
};

// No compiler error because there is no lifetime parameter for the `HandleRegister` function
db.commit(changes);

Ok(())
}

fn main() -> anyhow::Result<()> {
let mut cache_db = CacheDB::new(EmptyDB::default());

let mut tracer = TracerEip3155::new(Box::new(std::io::stdout()), true, true);

run_transaction_and_commit_with_ext(&mut cache_db, &mut tracer, inspector_handle_register)?;
run_transaction_and_commit(&mut cache_db)?;

Ok(())
}

0 comments on commit 863dd72

Please sign in to comment.