Skip to content

Commit

Permalink
refactor arbiter-core (#858)
Browse files Browse the repository at this point in the history
* large scale refactor

* chore: error consolidation

* chore: remove unused imports

* chore: remove errors

* updating to revm 4.0.0

* error refactor

* fix tests and clean up imports

* saving progress

* cleanup

* cleanup

* Update lib.rs

---------

Co-authored-by: Waylon Jepsen <[email protected]>
Co-authored-by: Waylon Jepsen <[email protected]>
  • Loading branch information
3 people authored Feb 8, 2024
1 parent cbc87f5 commit c82d56e
Show file tree
Hide file tree
Showing 41 changed files with 1,110 additions and 1,567 deletions.
176 changes: 53 additions & 123 deletions Cargo.lock

Large diffs are not rendered by default.

14 changes: 5 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,21 @@ arbiter-bindings = { version = "*", path = "./arbiter-bindings" }
arbiter-core = { version = "*", path = "./arbiter-core" }
arbiter-macros = { path = "./arbiter-macros" }
arbiter-engine = { path = "./arbiter-engine" }
revm = { version = "=4.0.0", features = ["ethersdb", "std", "serde"] }
revm-primitives = "=2.0.0"
ethers = { version = "2.0.13" }
serde = { version = "1.0.193", features = ["derive"] }
serde_json = { version = "=1.0.108" }
revm = { git = "https://github.com/bluealloy/revm.git", features = [
"ethersdb",
"std",
"serde",
], rev = "30bbcdfe81446c9d1e9b37acc95f208943ddf858" }
revm-primitives = { git = "https://github.com/bluealloy/revm.git", rev = "30bbcdfe81446c9d1e9b37acc95f208943ddf858" }
serde_json = { version = "1.0.113" }
thiserror = { version = "1.0.55" }
syn = { version = "2.0.48", features = ["full"] }
proc-macro2 = { version = "1.0.78" }
tokio = { version = "1.36.0", features = ["macros", "full"] }
crossbeam-channel = { version = "0.5.11" }
futures-util = { version = "=0.3.30" }
futures-util = { version = "0.3.30" }
async-trait = { version = "0.1.76" }
tracing = "0.1.40"
async-stream = "0.3.5"
toml = { version = "=0.8.9" }
toml = "0.8.10"

# Dependencies for the release build
[dependencies]
Expand Down
16 changes: 10 additions & 6 deletions arbiter-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
name = "arbiter-core"
version = "0.9.1"
edition = "2021"
authors = ["Waylon Jepsen <[email protected]>", "Colin Roberts <[email protected]>"]
authors = [
"Waylon Jepsen <[email protected]>",
"Colin Roberts <[email protected]>",
]
description = "Allowing smart contract developers to do simulation driven development via an EVM emulator"
license = "Apache-2.0"
keywords = ["ethereum", "evm", "emulator", "testing", "smart-contracts"]
Expand All @@ -13,6 +16,7 @@ readme = "../README.md"
arbiter-bindings = { path = "../arbiter-bindings" }

# Ethereum and EVM
uint = "0.9.5"
ethers.workspace = true
revm.workspace = true
revm-primitives.workspace = true
Expand All @@ -34,7 +38,7 @@ futures-locks = { version = "=0.7.1" }


# Randomness
rand = { version = "=0.8.5" }
rand = { version = "=0.8.5" }
rand_distr = { version = "=0.4.3" }
statrs = { version = "=0.16.0" }

Expand All @@ -50,17 +54,17 @@ polars = { version = "0.36.2", features = ["parquet", "csv", "json"] }

# Dependencies for the test build and development
[dev-dependencies]
anyhow = { version = "=1.0.79" }
test-log = { version = "=0.2.14" }
anyhow = { version = "=1.0.79" }
test-log = { version = "=0.2.14" }
tracing-test = "0.2.4"
tracing-subscriber = "0.3.18"

polars = "0.36.2"
cargo_metadata = "0.18.1"
chrono = "0.4.33"
futures = { version = "=0.3.30" }
futures = { version = "=0.3.30" }

assert_matches = { version = "=1.5" }
assert_matches = { version = "=1.5" }

[[bench]]
name = "bench"
Expand Down
6 changes: 3 additions & 3 deletions arbiter-core/benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use arbiter_bindings::bindings::{
};
use arbiter_core::{
environment::{Environment, EnvironmentBuilder},
middleware::RevmMiddleware,
middleware::ArbiterMiddleware,
};
use ethers::{
core::{k256::ecdsa::SigningKey, utils::Anvil},
Expand Down Expand Up @@ -177,10 +177,10 @@ async fn anvil_startup() -> Result<(
Ok((client, anvil))
}

fn arbiter_startup() -> Result<(Environment, Arc<RevmMiddleware>)> {
fn arbiter_startup() -> Result<(Environment, Arc<ArbiterMiddleware>)> {
let environment = Environment::builder().build();

let client = RevmMiddleware::new(&environment, Some("name"))?;
let client = ArbiterMiddleware::new(&environment, Some("name"))?;
Ok((environment, client))
}

Expand Down
13 changes: 5 additions & 8 deletions arbiter-core/src/console/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
//! This module contains the backend for the `console2.log` Solidity function so
//! that these logs can be read in Arbiter.

use std::ops::Range;
use revm_primitives::address;

use revm::{
interpreter::{CallInputs, InterpreterResult},
Database, EvmContext, Inspector,
};
use revm_primitives::{address, Address, Bytes};
use super::*;

const CONSOLE_ADDRESS: Address = address!("000000000000000000636F6e736F6c652e6c6f67");

Expand All @@ -24,9 +20,10 @@ impl<DB: Database> Inspector<DB> for ConsoleLogs {
#[inline]
fn call(
&mut self,
_context: &mut EvmContext<'_, DB>,
_context: &mut EvmContext<DB>,
call: &mut CallInputs,
) -> Option<(InterpreterResult, Range<usize>)> {
_return_memory_offset: Range<usize>,
) -> Option<CallOutcome> {
if call.contract == CONSOLE_ADDRESS {
self.0.push(call.input.clone());
}
Expand Down
47 changes: 18 additions & 29 deletions arbiter-core/src/coprocessor.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,35 @@
//! The `Coprocessor` is used to process calls and can access read-only from the
//! `Environment`'s database. The `Coprocessor` will stay up to date with the
//! latest state of the `Environment`'s database.
//! The [`Coprocessor`] is used to process calls and can access read-only from
//! the [`Environment`]'s database while staying up to date with the
//! latest state of the [`Environment`]'s database.

use std::convert::Infallible;

use revm::EVM;
use revm_primitives::{EVMError, ResultAndState};

use crate::{database::ArbiterDB, environment::Environment};
use super::*;
use crate::environment::Environment;

// TODO: I have not tested that the coprocessor actually maintains state parity
// with the environment. At the moment, it is only able to be constructed and
// can certainly act as a read-only EVM.

/// A `Coprocessor` is used to process calls and can access read-only from the
/// `Environment`'s database. This can eventually be used for things like
/// A [`Coprocessor`] is used to process calls and can access read-only from the
/// [`Environment`]'s database. This can eventually be used for things like
/// parallelized compute for agents that are not currently sending transactions
/// that need to be processed by the `Environment`, but are instead using the
/// that need to be processed by the [`Environment`], but are instead using the
/// current state to make decisions.
pub struct Coprocessor {
evm: EVM<ArbiterDB>,
pub struct Coprocessor<'a> {
evm: Evm<'a, (), ArbiterDB>,
}

impl Coprocessor {
impl<'a> Coprocessor<'a> {
/// Create a new `Coprocessor` with the given `Environment`.
pub fn new(environment: &Environment) -> Self {
let db = ArbiterDB(
environment
.db
.as_ref()
.unwrap_or(&ArbiterDB::new())
.0
.clone(),
);
let mut evm = EVM::new();
evm.database(db);
let db = environment.db.clone();
let evm = Evm::builder().with_db(db).build();
Self { evm }
}

// TODO: Should probably take in a TxEnv or something.
/// Used as an entrypoint to process a call with the `Coprocessor`.
pub fn transact_ref(&self) -> Result<ResultAndState, EVMError<Infallible>> {
self.evm.transact_ref()
pub fn transact(&mut self) -> Result<ResultAndState, EVMError<Infallible>> {
self.evm.transact()
}
}

Expand All @@ -54,8 +43,8 @@ mod tests {
fn coprocessor() {
let environment = Environment::builder().build();
let mut coprocessor = Coprocessor::new(&environment);
coprocessor.evm.env.tx.value = U256::from(100);
let outcome = coprocessor.transact_ref();
coprocessor.evm.tx_mut().value = U256::from(100);
let outcome = coprocessor.transact();
if let Err(EVMError::Transaction(InvalidTransaction::LackOfFundForMaxFee {
fee,
balance,
Expand Down
25 changes: 7 additions & 18 deletions arbiter-core/src/data_collection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@
//! * `E` - Type that implements the `EthLogDecode`, `Debug`, `Serialize`
//! traits, and has a static lifetime.

use std::{
collections::BTreeMap, fmt::Debug, io::BufWriter, marker::PhantomData, mem::transmute,
sync::Arc,
};
use std::{io::BufWriter, marker::PhantomData, mem::transmute};

use ethers::{
abi::RawLog,
Expand All @@ -41,10 +38,7 @@ use serde_json::Value;
use tokio::{sync::broadcast::Receiver as BroadcastReceiver, task::JoinHandle};

use super::*;
use crate::{
environment::Broadcast,
middleware::{cast::revm_logs_to_ethers_logs, errors::RevmMiddlewareError, RevmMiddleware},
};
use crate::middleware::{connection::revm_logs_to_ethers_logs, ArbiterMiddleware};

pub(crate) type FilterDecoder =
BTreeMap<String, (FilteredParams, Box<dyn Fn(&RawLog) -> String + Send + Sync>)>;
Expand All @@ -65,7 +59,6 @@ pub(crate) type FilterDecoder =
pub struct EventLogger {
decoder: FilterDecoder,
receiver: Option<BroadcastReceiver<Broadcast>>,
// shutdown_sender: Option<crossbeam_channel::Sender<()>>,
output_file_type: Option<OutputFileType>,
directory: Option<String>,
file_name: Option<String>,
Expand Down Expand Up @@ -134,13 +127,13 @@ impl EventLogger {
/// The `EventLogger` instance with the added event.
pub fn add<S: Into<String>, D: EthLogDecode + Debug + Serialize + 'static>(
mut self,
event: Event<Arc<RevmMiddleware>, RevmMiddleware, D>,
event: Event<Arc<ArbiterMiddleware>, ArbiterMiddleware, D>,
name: S,
) -> Self {
let name = name.into();
// Grab the connection from the client and add a new event sender so that we
// have a distinct channel to now receive events over
let event_transmuted: EventTransmuted<Arc<RevmMiddleware>, RevmMiddleware, D> =
let event_transmuted: EventTransmuted<Arc<ArbiterMiddleware>, ArbiterMiddleware, D> =
unsafe { transmute(event) };
let middleware = event_transmuted.provider.clone();
let decoder = |x: &_| serde_json::to_string(&D::decode_log(x).unwrap()).unwrap();
Expand All @@ -162,14 +155,10 @@ impl EventLogger {
/// not stored.
pub fn add_stream<D: EthLogDecode + Debug + Serialize + 'static>(
self,
event: Event<Arc<RevmMiddleware>, RevmMiddleware, D>,
event: Event<Arc<ArbiterMiddleware>, ArbiterMiddleware, D>,
) -> Self {
let mut hasher = Sha256::new();
hasher.update(
serde_json::to_string(&event.filter)
.map_err(RevmMiddlewareError::Json)
.unwrap(),
);
hasher.update(serde_json::to_string(&event.filter).unwrap());
let hash = hasher.finalize();
let id = hex::encode(hash);
self.add(event, id)
Expand Down Expand Up @@ -258,7 +247,7 @@ impl EventLogger {
///
/// This function will return an error if there is a problem creating the
/// directories or files, or writing to the files.
pub fn run(self) -> Result<JoinHandle<()>, RevmMiddlewareError> {
pub fn run(self) -> Result<JoinHandle<()>, ArbiterCoreError> {
let mut receiver = self.receiver.unwrap();
let dir = self.directory.unwrap_or("./data".into());
let file_name = self.file_name.unwrap_or("output".into());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
//! that the [`Environment`] can be initialized with a forked database and the
//! end-user still has access to the relevant metadata.

use std::{collections::HashMap, env, fs};

use ethers::types::Address;
use std::{env, fs};

use super::*;

Expand All @@ -15,7 +13,7 @@ use super::*;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ContractMetadata {
/// The address of the contract.
pub address: Address,
pub address: eAddress,

/// The path to the contract artifacts.
pub artifacts_path: String,
Expand All @@ -27,8 +25,8 @@ pub struct ContractMetadata {
/// A [`Fork`] is used to store the data that will be loaded into an
/// [`Environment`] and be used in `arbiter-core`. It is a wrapper around a
/// [`CacheDB`] and a [`HashMap`] of [`ContractMetadata`] so that the
/// [`Environment`] can be initialized with the data and the end-user still has
/// access to the relevant metadata.
/// [`environment::Environment`] can be initialized with the data and the
/// end-user still has access to the relevant metadata.
#[derive(Clone, Debug)]
pub struct Fork {
/// The [`CacheDB`] that will be loaded into the [`Environment`].
Expand All @@ -38,12 +36,12 @@ pub struct Fork {
/// end-user.
pub contracts_meta: HashMap<String, ContractMetadata>,
/// The [`HashMap`] of [`Address`] that will be used by the end-user.
pub eoa: HashMap<String, ethers::types::H160>,
pub eoa: HashMap<String, eAddress>,
}

impl Fork {
/// Creates a new [`Fork`] from serialized [`DiskData`] stored on disk.
pub fn from_disk(path: &str) -> Result<Self, EnvironmentError> {
pub fn from_disk(path: &str) -> Result<Self, ArbiterCoreError> {
// Read the file
let mut cwd = env::current_dir().unwrap();
cwd.push(path);
Expand All @@ -66,8 +64,8 @@ impl Fork {

// Insert storage data into the DB
for (key_str, value_str) in storage_map {
let key = revm::primitives::U256::from_str_radix(&key_str, 10).unwrap();
let value = revm::primitives::U256::from_str_radix(&value_str, 10).unwrap();
let key = U256::from_str_radix(&key_str, 10).unwrap();
let value = U256::from_str_radix(&value_str, 10).unwrap();

db.insert_account_storage(address, key, value).unwrap();
}
Expand Down Expand Up @@ -98,8 +96,8 @@ pub struct DiskData {
pub meta: HashMap<String, ContractMetadata>,

/// This is the raw data that will be loaded into the [`Fork`].
pub raw: HashMap<Address, (AccountInfo, Storage)>,
pub raw: HashMap<eAddress, (AccountInfo, Storage)>,

/// This is the eoa data that will be loaded into the [`Fork`].
pub externally_owned_accounts: HashMap<String, Address>,
pub externally_owned_accounts: HashMap<String, eAddress>,
}
Loading

0 comments on commit c82d56e

Please sign in to comment.