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

Refactor/core #858

Merged
merged 12 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading