Skip to content

Commit

Permalink
feat(revm): add forking mode (#835)
Browse files Browse the repository at this point in the history
* feat: copy-paste old forking provider

* feat(fork): convert to REVM traits

* chore: remove unnecessary codehash handler

* feat: impl Database for shared backend

* chore: fix tests

* chore: fmt

* fix(fork): correctly convert H256 <> U256 for storage

* refactor: separate storage from accounts in cache

* feat(fork): fetch block hashes

* chore: remove unused DB parameter

* test: add test for block hashes

* feat: add forked backend to executor builder

* feat(cli): set fork url on the executor

* refactor: move shared backend to separate file

* feat(fork): add fn for instantiating forked env

* feat(cli): allow pinning block number

* fix(fork): install missing listeners

* feat(fork): instantiate environment with forked state

* fix: use a CALLER address with maxed out balance for calls

this is required because in forking mode otherwise the account wont have enough balance
to transact

* chore: fmt

Co-authored-by: Oliver Nordbjerg <[email protected]>
  • Loading branch information
gakonst and onbjerg committed Mar 17, 2022
1 parent 4141fc2 commit dad15a9
Show file tree
Hide file tree
Showing 12 changed files with 716 additions and 58 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions forge/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ thiserror = "1.0.29"
revm = { package = "revm", git = "https://github.com/onbjerg/revm", branch = "onbjerg/blockhashes", default-features = false, features = ["std", "k256"] }
hashbrown = "0.12"
once_cell = "1.9.0"
parking_lot = "0.12.0"
futures = "0.3.21"

[dev-dependencies]
ethers = { git = "https://github.com/gakonst/ethers-rs", default-features = false, features = ["solc-full", "solc-tests"] }
Expand Down
92 changes: 87 additions & 5 deletions forge/src/executor/builder.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,87 @@
use revm::{db::EmptyDB, Env, SpecId};
use ethers::prelude::Provider;
use revm::{
db::{DatabaseRef, EmptyDB},
Env, SpecId,
};

use super::{inspector::InspectorStackConfig, Executor};
use super::{
fork::{SharedBackend, SharedMemCache},
inspector::InspectorStackConfig,
Executor,
};

#[derive(Default)]
pub struct ExecutorBuilder {
/// The execution environment configuration.
env: Env,
/// The configuration used to build an [InspectorStack].
inspector_config: InspectorStackConfig,
fork: Option<Fork>,
}

#[derive(Clone, Debug)]
pub struct Fork {
// todo: cache path
/// The URL to a node for fetching remote state
pub url: String,
/// The block to fork against
pub pin_block: Option<u64>,
}

pub enum Backend {
Simple(EmptyDB),
Forked(SharedBackend),
}

impl Backend {
/// Instantiates a new backend union based on whether there was or not a fork url specified
fn new(fork: Option<Fork>) -> Self {
if let Some(fork) = fork {
let provider = Provider::try_from(fork.url).unwrap();
// TOOD: Add reading cache from disk
let backend = SharedBackend::new(
provider,
SharedMemCache::default(),
fork.pin_block.map(Into::into),
);
Backend::Forked(backend)
} else {
Backend::Simple(EmptyDB())
}
}
}

use ethers::types::{H160, H256, U256};
use revm::AccountInfo;

impl DatabaseRef for Backend {
fn block_hash(&self, number: U256) -> H256 {
match self {
Backend::Simple(inner) => inner.block_hash(number),
Backend::Forked(inner) => inner.block_hash(number),
}
}

fn basic(&self, address: H160) -> AccountInfo {
match self {
Backend::Simple(inner) => inner.basic(address),
Backend::Forked(inner) => inner.basic(address),
}
}

fn code_by_hash(&self, address: H256) -> bytes::Bytes {
match self {
Backend::Simple(inner) => inner.code_by_hash(address),
Backend::Forked(inner) => inner.code_by_hash(address),
}
}

fn storage(&self, address: H160, index: U256) -> U256 {
match self {
Backend::Simple(inner) => inner.storage(address, index),
Backend::Forked(inner) => inner.storage(address, index),
}
}
}

impl ExecutorBuilder {
Expand All @@ -24,6 +98,7 @@ impl ExecutorBuilder {
self
}

#[must_use]
pub fn with_spec(mut self, spec: SpecId) -> Self {
self.env.cfg.spec_id = spec;
self
Expand All @@ -36,12 +111,19 @@ impl ExecutorBuilder {
self
}

/// Configure the executor's forking mode
#[must_use]
pub fn with_fork(mut self, fork: Fork) -> Self {
self.fork = Some(fork);
self
}

/// Builds the executor as configured.
pub fn build(self) -> Executor<EmptyDB> {
Executor::new(EmptyDB(), self.env, self.inspector_config)
pub fn build(self) -> Executor<Backend> {
let db = Backend::new(self.fork);
Executor::new(db, self.env, self.inspector_config)
}

// TODO: add with_traces
// TODO: add with_debug(ger?)
// TODO: add forked
}
Loading

0 comments on commit dad15a9

Please sign in to comment.