From c1ced5cfa7ebe4d0e81ee7c18b4181385ccd167c Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 23 Mar 2022 14:05:00 +0100 Subject: [PATCH 01/12] feat(cache): proper json cache --- evm/Cargo.toml | 3 +- evm/src/executor/fork/backend.rs | 9 +- evm/src/executor/fork/cache.rs | 214 +++++++++++++++++++++++++++++++ evm/src/executor/fork/mod.rs | 2 + 4 files changed, 223 insertions(+), 5 deletions(-) create mode 100644 evm/src/executor/fork/cache.rs diff --git a/evm/Cargo.toml b/evm/Cargo.toml index 6edae15b7c92..145d8d0ae64d 100644 --- a/evm/Cargo.toml +++ b/evm/Cargo.toml @@ -20,6 +20,7 @@ thiserror = "1.0.29" # Logging tracing = "0.1.26" tracing-subscriber = "0.2.20" +tracing-error = "0.2.0" # Threading/futures tokio = { version = "1.10.1" } @@ -30,7 +31,7 @@ once_cell = "1.9.0" # EVM bytes = "1.1.0" hashbrown = "0.12" -revm = { package = "revm", git = "https://github.com/bluealloy/revm", default-features = false, features = ["std", "k256"] } +revm = { package = "revm", git = "https://github.com/bluealloy/revm", default-features = false, features = ["std", "k256", "with-serde"] } # Fuzzer proptest = "1.0.0" diff --git a/evm/src/executor/fork/backend.rs b/evm/src/executor/fork/backend.rs index 887c6f5e6908..a17103d5f7fa 100644 --- a/evm/src/executor/fork/backend.rs +++ b/evm/src/executor/fork/backend.rs @@ -1,7 +1,7 @@ //! Smart caching and deduplication of requests when using a forking provider use revm::{db::DatabaseRef, AccountInfo, KECCAK_EMPTY}; -use crate::storage::StorageMap; +use crate::{executor::fork::cache::StorageInfo, storage::StorageMap}; use ethers::{ core::abi::ethereum_types::BigEndianHash, providers::Middleware, @@ -26,14 +26,15 @@ use std::{ }; use tracing::trace; -type StorageInfo = BTreeMap; - /// In Memory cache containing all fetched accounts and storage slots /// and their values from RPC -#[derive(Clone, Debug, Default)] +#[derive(Debug, Default)] pub struct SharedMemCache { + /// Account related data pub accounts: Arc>>, + /// Storage related data pub storage: Arc>>, + /// All retrieved block hashes pub block_hashes: Arc>>, } diff --git a/evm/src/executor/fork/cache.rs b/evm/src/executor/fork/cache.rs new file mode 100644 index 000000000000..ba44b999f562 --- /dev/null +++ b/evm/src/executor/fork/cache.rs @@ -0,0 +1,214 @@ +//! Cache related abstraction +use ethers::types::{Address, Bytes, H256, U256}; +use parking_lot::RwLock; +use revm::AccountInfo; +use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer}; +use std::{collections::BTreeMap, fs, io::BufWriter, path::PathBuf, sync::Arc}; +use tracing::{trace, trace_span, warn, Level}; +use tracing_error::InstrumentResult; + +pub type StorageInfo = BTreeMap; + +/// A shareable Block database +#[derive(Clone)] +pub struct BlockchainDb { + /// Contains all the data + db: Arc, + /// metadata of the current config + meta: Arc>, + /// the cache to + cache: Arc, +} + +impl BlockchainDb { + /// Creates a new instance of the [BlockchainDb] + /// + /// if a `cache_path` is provided it attempts to load a previously stored [JsonBlockCacheData] + /// and will try to use the cached entries it holds. + /// + /// This will return a new and empty [MemDb] if + /// - `cache_path` is `None` + /// - the file the `cache_path` points to, does not exist + /// - the file contains malformed data, or if it couldn't be read + /// - the provided `meta` differs from [BlockchainDbMeta] that's stored on disk + pub fn new(meta: BlockchainDbMeta, cache_path: Option) -> Self { + // read cache and check if metadata matches + let cache = cache_path + .and_then(|p| { + JsonBlockCacheDB::load(p).ok().filter(|cache| { + if meta != *cache.meta().read() { + warn!(target:"cache", "non-matching block metadata"); + false + } else { + true + } + }) + }) + .unwrap_or_else(|| JsonBlockCacheDB::transient(Arc::new(RwLock::new(meta)))); + + Self { db: Arc::clone(cache.db()), meta: Arc::clone(cache.meta()), cache: Arc::new(()) } + } + + /// Delegate for [BlockCacheDB::flush()] + pub fn flush_cache(&self) { + self.cache.flush() + } +} + +/// relevant identifying markers in the context of [BlockchainDb] +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +pub struct BlockchainDbMeta { + pub cfg_env: revm::CfgEnv, + pub block_env: revm::BlockEnv, + pub endpoint: String, +} + +/// In Memory cache containing all fetched accounts and storage slots +/// and their values from RPC +#[derive(Debug, Default)] +pub struct MemDb { + /// Account related data + pub accounts: RwLock>, + /// Storage related data + pub storage: RwLock>, + /// All retrieved block hashes + pub block_hashes: RwLock>, +} + +/// Helper trait to abstract disk caching for [SharedMemCache] +pub trait BlockCacheDB: Send + Sync { + /// Expected to flush the cache contents to disk + fn flush(&self) {} +} + +// A [BlockCacheDB] that does nothing +impl BlockCacheDB for () {} + +/// A [BlockCacheDB] that stores the cached content in a json file +pub struct JsonBlockCacheDB { + /// Where this cache file is stored. + /// + /// If this is a [None] then caching is disabled + cache_path: Option, + /// Object that's stored in a json file + data: JsonBlockCacheData, +} + +impl JsonBlockCacheDB { + /// Marks the cache as transient. + /// + /// A transient cache will never be written to disk + fn transient(meta: Arc>) -> Self { + Self { + cache_path: None, + data: JsonBlockCacheData { meta, data: Arc::new(Default::default()) }, + } + } + + /// Loads the contents of the diskmap file and returns the read object + /// + /// # Errors + /// This will fail if + /// - the `path` does not exist + /// - the format does not match [JsonBlockCacheData] + pub fn load(path: impl Into) -> eyre::Result { + let path = path.into(); + let span = trace_span!("cache", "path={:?}", &path); + let _enter = span.enter(); + trace!("reading json cache path={:?}", path); + let file = std::fs::File::open(&path).in_current_span()?; + let file = std::io::BufReader::new(file); + let data = serde_json::from_reader(file).in_current_span()?; + Ok(Self { cache_path: Some(path), data }) + } + + /// Returns the [MemDb] it holds access to + pub fn db(&self) -> &Arc { + &self.data.data + } + + /// Metadata stored alongside the data + pub fn meta(&self) -> &Arc> { + &self.data.meta + } +} + +impl BlockCacheDB for JsonBlockCacheDB { + fn flush(&self) { + // writes the data to a json file + if let Some(ref path) = self.cache_path { + trace!(target: "cache", "saving json cache path={:?}", path); + if let Some(parent) = path.parent() { + let _ = fs::create_dir_all(parent); + } + let _ = fs::File::create(path) + .map_err(|e| warn!(target: "cache", "Failed to open json cache for writing: {}", e)) + .and_then(|f| { + serde_json::to_writer(BufWriter::new(f), &self.data) + .map_err(|e| warn!(target: "cache" ,"Failed to write to json cache: {}", e)) + }); + } + } +} + +/// The Data the [JsonBlockCacheDB] can read and flush +/// +/// This will be deserialized in a JSON object with the keys: +/// `["meta", "accounts", "storage", "block_hashes"]` +pub struct JsonBlockCacheData { + pub meta: Arc>, + pub data: Arc, +} + +impl Serialize for JsonBlockCacheData { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut map = serializer.serialize_map(Some(4))?; + + let meta = self.meta.read(); + map.serialize_entry("meta", &*meta)?; + drop(meta); + + let accounts = self.data.accounts.read(); + map.serialize_entry("accounts", &*accounts)?; + drop(accounts); + + let storage = self.data.storage.read(); + map.serialize_entry("storage", &*storage)?; + drop(storage); + + let block_hashes = self.data.block_hashes.read(); + map.serialize_entry("accounts", &*block_hashes)?; + drop(block_hashes); + + map.end() + } +} + +impl<'de> Deserialize<'de> for JsonBlockCacheData { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + struct Data { + meta: BlockchainDbMeta, + accounts: BTreeMap, + storage: BTreeMap, + block_hashes: BTreeMap, + } + + let Data { meta, accounts, storage, block_hashes } = Data::deserialize(deserializer)?; + + Ok(JsonBlockCacheData { + meta: Arc::new(RwLock::new(meta)), + data: Arc::new(MemDb { + accounts: RwLock::new(accounts), + storage: RwLock::new(storage), + block_hashes: RwLock::new(block_hashes), + }), + }) + } +} diff --git a/evm/src/executor/fork/mod.rs b/evm/src/executor/fork/mod.rs index c7ba153c78f6..35557ff1420f 100644 --- a/evm/src/executor/fork/mod.rs +++ b/evm/src/executor/fork/mod.rs @@ -3,3 +3,5 @@ pub use backend::{SharedBackend, SharedMemCache}; mod init; pub use init::environment; + +mod cache; From 933694dfe733ec1716d0aa69d9402555e3637871 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 23 Mar 2022 14:30:51 +0100 Subject: [PATCH 02/12] refactor: use new db types --- evm/Cargo.toml | 1 + evm/src/executor/builder.rs | 42 +++++++-------- evm/src/executor/fork/backend.rs | 93 +++++++++++--------------------- evm/src/executor/fork/cache.rs | 17 +++++- evm/src/executor/fork/mod.rs | 1 + 5 files changed, 71 insertions(+), 83 deletions(-) diff --git a/evm/Cargo.toml b/evm/Cargo.toml index 145d8d0ae64d..a84c6798df99 100644 --- a/evm/Cargo.toml +++ b/evm/Cargo.toml @@ -38,6 +38,7 @@ proptest = "1.0.0" # Display ansi_term = "0.12.1" +url = "2.2.2" [dev-dependencies] tempfile = "3.3.0" diff --git a/evm/src/executor/builder.rs b/evm/src/executor/builder.rs index 76461b0e6b9c..b2802f17088f 100644 --- a/evm/src/executor/builder.rs +++ b/evm/src/executor/builder.rs @@ -14,8 +14,10 @@ use super::{ use crate::storage::StorageMap; use ethers::types::{H160, H256, U256}; +use crate::executor::fork::{BlockchainDb, BlockchainDbMeta}; use parking_lot::lock_api::RwLock; use revm::AccountInfo; +use url::Url; #[derive(Default, Debug)] pub struct ExecutorBuilder { @@ -29,7 +31,7 @@ pub struct ExecutorBuilder { #[derive(Clone, Debug)] pub struct Fork { /// Where to read the cached storage from - pub cache_storage: Option, + pub cache_path: Option, /// The URL to a node for fetching remote state pub url: String, /// The block to fork against @@ -40,25 +42,23 @@ impl Fork { /// Initialises the Storage Backend /// /// If configured, then this will initialise the backend with the storage cahce - fn into_backend(self) -> SharedBackend { - let Fork { cache_storage, url, pin_block } = self; + fn into_backend(self, env: &Env) -> SharedBackend { + let Fork { cache_path, url, pin_block } = self; + + let host = Url::parse(&url) + .ok() + .and_then(|url| url.host().map(|host| host.to_string())) + .unwrap_or_else(|| url.clone()); + let provider = Provider::try_from(url).expect("Failed to establish provider"); - let mut storage_map = if let Some(cached_storage) = cache_storage { - StorageMap::read(cached_storage) - } else { - StorageMap::transient() - }; - - SharedBackend::new( - provider, - SharedMemCache { - storage: Arc::new(RwLock::new(storage_map.take_storage())), - ..Default::default() - }, - pin_block.map(Into::into), - storage_map, - ) + + let meta = + BlockchainDbMeta { cfg_env: env.cfg.clone(), block_env: env.block.clone(), host }; + + let db = BlockchainDb::new(meta, cache_path); + + SharedBackend::new(provider, db, pin_block.map(Into::into)) } } @@ -69,9 +69,9 @@ pub enum Backend { impl Backend { /// Instantiates a new backend union based on whether there was or not a fork url specified - fn new(fork: Option) -> Self { + fn new(fork: Option, env: &Env) -> Self { if let Some(fork) = fork { - Backend::Forked(fork.into_backend()) + Backend::Forked(fork.into_backend(env)) } else { Backend::Simple(EmptyDB()) } @@ -160,7 +160,7 @@ impl ExecutorBuilder { /// Builds the executor as configured. pub fn build(self) -> Executor { - let db = Backend::new(self.fork); + let db = Backend::new(self.fork, &self.env); Executor::new(db, self.env, self.inspector_config) } } diff --git a/evm/src/executor/fork/backend.rs b/evm/src/executor/fork/backend.rs index a17103d5f7fa..c29d432c0f3c 100644 --- a/evm/src/executor/fork/backend.rs +++ b/evm/src/executor/fork/backend.rs @@ -1,7 +1,10 @@ //! Smart caching and deduplication of requests when using a forking provider use revm::{db::DatabaseRef, AccountInfo, KECCAK_EMPTY}; -use crate::{executor::fork::cache::StorageInfo, storage::StorageMap}; +use crate::{ + executor::fork::{cache::StorageInfo, BlockchainDb}, + storage::StorageMap, +}; use ethers::{ core::abi::ethereum_types::BigEndianHash, providers::Middleware, @@ -38,28 +41,6 @@ pub struct SharedMemCache { pub block_hashes: Arc>>, } -/// A type that's used to write the storage to the path once dropped -struct FlushStorageCacheOnDrop { - /// Where to write the data on drop - storage_map: StorageMap, - /// access to the storage to write when this type is dropped - storage: Arc>>, -} - -impl Drop for FlushStorageCacheOnDrop { - fn drop(&mut self) { - // only flush if map is not transient - if !self.storage_map.is_transient() { - let lock = self.storage.read(); - let data = lock.clone(); - drop(lock); - self.storage_map.set_storage(data); - self.storage_map.save(); - trace!(target: "diskmap", "flushed storage map {:?}", self.storage_map.path()); - } - } -} - type AccountFuture = Pin, Address)> + Send>>; type StorageFuture = Pin, Address, U256)> + Send>>; @@ -87,8 +68,8 @@ enum BackendRequest { #[must_use = "BackendHandler does nothing unless polled."] struct BackendHandler { provider: M, - /// Stores the state. - cache: SharedMemCache, + /// Stores all the data. + db: BlockchainDb, /// Requests currently in progress pending_requests: Vec>, /// Listeners that wait for a `get_account` related response @@ -104,9 +85,6 @@ struct BackendHandler { /// The block to fetch data from. // This is an `Option` so that we can have less code churn in the functions below block_id: Option, - /// If storage caching is enabled, this will flush the data to the configured file once the - /// handler is dropped - flush_storage: Option, } impl BackendHandler @@ -115,19 +93,14 @@ where { fn new( provider: M, - cache: SharedMemCache, + db: BlockchainDb, rx: Receiver, shutdown: Receiver>, block_id: Option, - storage_map: StorageMap, ) -> Self { Self { - flush_storage: Some(FlushStorageCacheOnDrop { - storage_map, - storage: Arc::clone(&cache.storage), - }), provider, - cache, + db, pending_requests: Default::default(), account_requests: Default::default(), storage_requests: Default::default(), @@ -147,7 +120,7 @@ where fn on_request(&mut self, req: BackendRequest) { match req { BackendRequest::Basic(addr, sender) => { - let lock = self.cache.accounts.read(); + let lock = self.db.accounts().read(); let basic = lock.get(&addr).cloned(); // release the lock drop(lock); @@ -158,7 +131,7 @@ where } } BackendRequest::BlockHash(number, sender) => { - let lock = self.cache.block_hashes.read(); + let lock = self.db.block_hashes().read(); let hash = lock.get(&number).cloned(); // release the lock drop(lock); @@ -169,7 +142,7 @@ where } } BackendRequest::Storage(addr, idx, sender) => { - let lock = self.cache.storage.read(); + let lock = self.db.storage().read(); let acc = lock.get(&addr); let value = acc.and_then(|acc| acc.get(&idx).copied()); // release the lock @@ -302,7 +275,7 @@ where // update the cache let acc = AccountInfo { nonce: nonce.as_u64(), balance, code, code_hash }; - pin.cache.accounts.write().insert(addr, acc.clone()); + pin.db.accounts().write().insert(addr, acc.clone()); // notify all listeners if let Some(listeners) = pin.account_requests.remove(&addr) { @@ -321,7 +294,7 @@ where }); // update the cache - pin.cache.storage.write().entry(addr).or_default().insert(idx, value); + pin.db.storage().write().entry(addr).or_default().insert(idx, value); // notify all listeners if let Some(listeners) = pin.storage_requests.remove(&(addr, idx)) { @@ -340,7 +313,7 @@ where }); // update the cache - pin.cache.block_hashes.write().insert(number, value); + pin.db.block_hashes().write().insert(number, value); // notify all listeners if let Some(listeners) = pin.block_requests.remove(&number) { @@ -360,7 +333,7 @@ where if pin.incoming.is_done() && pin.pending_requests.is_empty() { if let Poll::Ready(Some(ack)) = Pin::new(&mut pin.shutdown).poll_next(cx) { // effectively flushing the cached storage if any - let _ = pin.flush_storage.take(); + let _ = pin.db.flush_cache(); // signaling back let _ = ack.send(()); } @@ -426,19 +399,13 @@ impl SharedBackend { /// /// /// NOTE: this should be called with `Arc` - pub fn new( - provider: M, - cache: SharedMemCache, - pin_block: Option, - storage_map: StorageMap, - ) -> Self + pub fn new(provider: M, db: BlockchainDb, pin_block: Option) -> Self where M: Middleware + Unpin + 'static + Clone, { let (backend, backend_rx) = channel(1); let (shutdown, shutdown_rx) = channel(1); - let handler = - BackendHandler::new(provider, cache, backend_rx, shutdown_rx, pin_block, storage_map); + let handler = BackendHandler::new(provider, db, backend_rx, shutdown_rx, pin_block); // spawn the provider handler to background let rt = RuntimeOrHandle::new(); std::thread::spawn(move || match rt { @@ -504,6 +471,7 @@ impl DatabaseRef for SharedBackend { #[cfg(test)] mod tests { + use crate::executor::fork::BlockchainDbMeta; use ethers::{ providers::{Http, Provider}, types::Address, @@ -514,31 +482,34 @@ mod tests { #[test] fn shared_backend() { - let provider = Provider::::try_from( - "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27", - ) - .unwrap(); + let url = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27"; + let provider = Provider::::try_from(url).unwrap(); // some rng contract from etherscan let address: Address = "63091244180ae240c87d1f528f5f269134cb07b3".parse().unwrap(); - let cache = SharedMemCache::default(); - let backend = - SharedBackend::new(Arc::new(provider), cache.clone(), None, StorageMap::transient()); + let meta = BlockchainDbMeta { + cfg_env: Default::default(), + block_env: Default::default(), + host: url.to_string(), + }; + + let db = BlockchainDb::new(meta, None); + let backend = SharedBackend::new(Arc::new(provider), db.clone(), None); let idx = U256::from(0u64); let value = backend.storage(address, idx); let account = backend.basic(address); - let mem_acc = cache.accounts.read().get(&address).unwrap().clone(); + let mem_acc = db.accounts().read().get(&address).unwrap().clone(); assert_eq!(account.balance, mem_acc.balance); assert_eq!(account.nonce, mem_acc.nonce); - let slots = cache.storage.read().get(&address).unwrap().clone(); + let slots = db.storage().read().get(&address).unwrap().clone(); assert_eq!(slots.len(), 1); assert_eq!(slots.get(&idx).copied().unwrap(), value); let num = U256::from(10u64); let hash = backend.block_hash(num); - let mem_hash = *cache.block_hashes.read().get(&num.as_u64()).unwrap(); + let mem_hash = *db.block_hashes().read().get(&num.as_u64()).unwrap(); assert_eq!(hash, mem_hash); let max_slots = 5; @@ -549,7 +520,7 @@ mod tests { } }); handle.join().unwrap(); - let slots = cache.storage.read().get(&address).unwrap().clone(); + let slots = db.storage().read().get(&address).unwrap().clone(); assert_eq!(slots.len() as u64, max_slots); } } diff --git a/evm/src/executor/fork/cache.rs b/evm/src/executor/fork/cache.rs index ba44b999f562..3e2be9359fe3 100644 --- a/evm/src/executor/fork/cache.rs +++ b/evm/src/executor/fork/cache.rs @@ -49,6 +49,21 @@ impl BlockchainDb { Self { db: Arc::clone(cache.db()), meta: Arc::clone(cache.meta()), cache: Arc::new(()) } } + /// Returns the map that holds the account related info + pub fn accounts(&self) -> &RwLock> { + &self.db.accounts + } + + /// Returns the map that holds the storage related info + pub fn storage(&self) -> &RwLock> { + &self.db.storage + } + + /// Returns the map that holds all the block hashes + pub fn block_hashes(&self) -> &RwLock> { + &self.db.block_hashes + } + /// Delegate for [BlockCacheDB::flush()] pub fn flush_cache(&self) { self.cache.flush() @@ -60,7 +75,7 @@ impl BlockchainDb { pub struct BlockchainDbMeta { pub cfg_env: revm::CfgEnv, pub block_env: revm::BlockEnv, - pub endpoint: String, + pub host: String, } /// In Memory cache containing all fetched accounts and storage slots diff --git a/evm/src/executor/fork/mod.rs b/evm/src/executor/fork/mod.rs index 35557ff1420f..2f3b9c2583ef 100644 --- a/evm/src/executor/fork/mod.rs +++ b/evm/src/executor/fork/mod.rs @@ -5,3 +5,4 @@ mod init; pub use init::environment; mod cache; +pub use cache::{BlockCacheDB, BlockchainDb, BlockchainDbMeta, JsonBlockCacheDB}; From 2977cd79da69d45b5c7842003367100622d052a3 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 23 Mar 2022 14:34:23 +0100 Subject: [PATCH 03/12] chore(clippy): make clippy happy --- evm/src/executor/builder.rs | 12 +++--------- evm/src/executor/fork/backend.rs | 5 +---- evm/src/executor/fork/cache.rs | 9 +++++++-- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/evm/src/executor/builder.rs b/evm/src/executor/builder.rs index b2802f17088f..1ca8abacdafd 100644 --- a/evm/src/executor/builder.rs +++ b/evm/src/executor/builder.rs @@ -3,19 +3,14 @@ use revm::{ db::{DatabaseRef, EmptyDB}, Env, SpecId, }; -use std::{path::PathBuf, sync::Arc}; +use std::path::PathBuf; -use super::{ - fork::{SharedBackend, SharedMemCache}, - inspector::InspectorStackConfig, - Executor, -}; +use super::{fork::SharedBackend, inspector::InspectorStackConfig, Executor}; -use crate::storage::StorageMap; use ethers::types::{H160, H256, U256}; use crate::executor::fork::{BlockchainDb, BlockchainDbMeta}; -use parking_lot::lock_api::RwLock; + use revm::AccountInfo; use url::Url; @@ -52,7 +47,6 @@ impl Fork { let provider = Provider::try_from(url).expect("Failed to establish provider"); - let meta = BlockchainDbMeta { cfg_env: env.cfg.clone(), block_env: env.block.clone(), host }; diff --git a/evm/src/executor/fork/backend.rs b/evm/src/executor/fork/backend.rs index c29d432c0f3c..d4f183b1f801 100644 --- a/evm/src/executor/fork/backend.rs +++ b/evm/src/executor/fork/backend.rs @@ -1,10 +1,7 @@ //! Smart caching and deduplication of requests when using a forking provider use revm::{db::DatabaseRef, AccountInfo, KECCAK_EMPTY}; -use crate::{ - executor::fork::{cache::StorageInfo, BlockchainDb}, - storage::StorageMap, -}; +use crate::executor::fork::{cache::StorageInfo, BlockchainDb}; use ethers::{ core::abi::ethereum_types::BigEndianHash, providers::Middleware, diff --git a/evm/src/executor/fork/cache.rs b/evm/src/executor/fork/cache.rs index 3e2be9359fe3..bf6bd002aa73 100644 --- a/evm/src/executor/fork/cache.rs +++ b/evm/src/executor/fork/cache.rs @@ -1,10 +1,10 @@ //! Cache related abstraction -use ethers::types::{Address, Bytes, H256, U256}; +use ethers::types::{Address, H256, U256}; use parking_lot::RwLock; use revm::AccountInfo; use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer}; use std::{collections::BTreeMap, fs, io::BufWriter, path::PathBuf, sync::Arc}; -use tracing::{trace, trace_span, warn, Level}; +use tracing::{trace, trace_span, warn}; use tracing_error::InstrumentResult; pub type StorageInfo = BTreeMap; @@ -64,6 +64,11 @@ impl BlockchainDb { &self.db.block_hashes } + /// Returns the [revm::Env] related metadata + pub fn meta(&self) -> &Arc> { + &self.meta + } + /// Delegate for [BlockCacheDB::flush()] pub fn flush_cache(&self) { self.cache.flush() From 0ef8917016b4652b97252a3d3cdaf3e125328c51 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 23 Mar 2022 14:36:01 +0100 Subject: [PATCH 04/12] bump revm --- Cargo.lock | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4b9172ea2e2e..eed87df2ab59 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -688,7 +688,7 @@ dependencies = [ "indenter", "once_cell", "owo-colors", - "tracing-error", + "tracing-error 0.1.2", ] [[package]] @@ -700,7 +700,7 @@ dependencies = [ "once_cell", "owo-colors", "tracing-core", - "tracing-error", + "tracing-error 0.1.2", ] [[package]] @@ -1714,7 +1714,9 @@ dependencies = [ "thiserror", "tokio", "tracing", + "tracing-error 0.2.0", "tracing-subscriber 0.2.25", + "url", ] [[package]] @@ -2021,6 +2023,9 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] [[package]] name = "hidapi-rusb" @@ -3592,23 +3597,25 @@ dependencies = [ [[package]] name = "revm" version = "1.2.0" -source = "git+https://github.com/bluealloy/revm#cb5fbb9adc36c8ee127dce92259356cecbb829b8" +source = "git+https://github.com/bluealloy/revm#0437dcdc658f8a950e4c652d587d266a4bec35ff" dependencies = [ "arrayref", "auto_impl", "bytes", "hashbrown 0.12.0", + "hex", "num_enum", "primitive-types", "revm_precompiles", "rlp", + "serde", "sha3 0.10.1", ] [[package]] name = "revm_precompiles" version = "0.4.0" -source = "git+https://github.com/bluealloy/revm#cb5fbb9adc36c8ee127dce92259356cecbb829b8" +source = "git+https://github.com/bluealloy/revm#0437dcdc658f8a950e4c652d587d266a4bec35ff" dependencies = [ "bytes", "k256", @@ -4530,6 +4537,16 @@ dependencies = [ "tracing-subscriber 0.2.25", ] +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber 0.3.9", +] + [[package]] name = "tracing-futures" version = "0.2.5" From a6e02d87dd12f4bb3009f8560ffeb3761d7f8003 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 23 Mar 2022 14:36:34 +0100 Subject: [PATCH 05/12] docs: some docs --- evm/src/executor/fork/cache.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/src/executor/fork/cache.rs b/evm/src/executor/fork/cache.rs index bf6bd002aa73..0060724f1051 100644 --- a/evm/src/executor/fork/cache.rs +++ b/evm/src/executor/fork/cache.rs @@ -16,7 +16,7 @@ pub struct BlockchainDb { db: Arc, /// metadata of the current config meta: Arc>, - /// the cache to + /// the cache that can be flushed cache: Arc, } From bb2deaf498a5631cbff51ea2aa7b223c15b72456 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 23 Mar 2022 14:57:38 +0100 Subject: [PATCH 06/12] refactor: extend Fork type --- cli/src/utils.rs | 27 ++++++++++++++++++++------- config/src/lib.rs | 5 +++++ evm/src/executor/builder.rs | 12 ++++++++++-- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/cli/src/utils.rs b/cli/src/utils.rs index 5a4ead1c4b46..ad4f4723bcb2 100644 --- a/cli/src/utils.rs +++ b/cli/src/utils.rs @@ -159,15 +159,17 @@ pub fn block_on(future: F) -> F::Output { /// - storage is allowed (`no_storage_caching = false`) /// /// If all these criteria are met, then storage caching is enabled and storage info will be written -/// to [Config::data_dir()]//block/storage.json +/// to [Config::foundry_cache_dir()]//block/storage.json /// /// for `mainnet` and `--fork-block-number 14435000` on mac the corresponding storage cache will be -/// at `$HOME`/Library/Application Support/foundry/mainnet/14435000/storage.json` +/// at `~/.foundry/cache/mainnet/14435000/storage.json` pub fn get_fork(evm_opts: &EvmOpts, config: &StorageCachingConfig) -> Option { - fn get_cache_storage_path( + /// Returns the chain id of the endpoint url, if available, otherwise mainnet == 1 + /// and the path where the cache file should be + fn get_chain_and_cache_path( evm_opts: &EvmOpts, config: &StorageCachingConfig, - ) -> Option { + ) -> Option<(Option, u64)> { if evm_opts.no_storage_caching { // storage caching explicitly opted out of return None @@ -187,7 +189,12 @@ pub fn get_fork(evm_opts: &EvmOpts, config: &StorageCachingConfig) -> Option { @@ -200,8 +207,14 @@ pub fn get_fork(evm_opts: &EvmOpts, config: &StorageCachingConfig) -> Option Option { + Self::foundry_dir().map(|p|p.join("cache")) + } + #[doc = r#"Returns the path to `foundry`'s data directory inside the user's data directory |Platform | Value | Example | | ------- | ------------------------------------- | -------------------------------- | diff --git a/evm/src/executor/builder.rs b/evm/src/executor/builder.rs index 1ca8abacdafd..87b605cfeb85 100644 --- a/evm/src/executor/builder.rs +++ b/evm/src/executor/builder.rs @@ -31,6 +31,8 @@ pub struct Fork { pub url: String, /// The block to fork against pub pin_block: Option, + /// chain id retrieved from the endpoint + pub chain_id: u64, } impl Fork { @@ -38,7 +40,7 @@ impl Fork { /// /// If configured, then this will initialise the backend with the storage cahce fn into_backend(self, env: &Env) -> SharedBackend { - let Fork { cache_path, url, pin_block } = self; + let Fork { cache_path, url, pin_block , chain_id,} = self; let host = Url::parse(&url) .ok() @@ -47,9 +49,15 @@ impl Fork { let provider = Provider::try_from(url).expect("Failed to establish provider"); - let meta = + let mut meta = BlockchainDbMeta { cfg_env: env.cfg.clone(), block_env: env.block.clone(), host }; + // update the meta to match the forked config + meta.cfg_env.chain_id = chain_id.into(); + if let Some(pin) = pin_block { + meta.block_env.number = pin.into(); + } + let db = BlockchainDb::new(meta, cache_path); SharedBackend::new(provider, db, pin_block.map(Into::into)) From 7f9ec38448a8c27acf73398d9354192ae1314c36 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 23 Mar 2022 14:59:15 +0100 Subject: [PATCH 07/12] remove diskmap types --- evm/src/executor/builder.rs | 2 +- evm/src/lib.rs | 3 - evm/src/storage/diskmap.rs | 137 ------------------------------------ evm/src/storage/mod.rs | 130 ---------------------------------- 4 files changed, 1 insertion(+), 271 deletions(-) delete mode 100644 evm/src/storage/diskmap.rs delete mode 100644 evm/src/storage/mod.rs diff --git a/evm/src/executor/builder.rs b/evm/src/executor/builder.rs index 87b605cfeb85..0b66d5fccccc 100644 --- a/evm/src/executor/builder.rs +++ b/evm/src/executor/builder.rs @@ -40,7 +40,7 @@ impl Fork { /// /// If configured, then this will initialise the backend with the storage cahce fn into_backend(self, env: &Env) -> SharedBackend { - let Fork { cache_path, url, pin_block , chain_id,} = self; + let Fork { cache_path, url, pin_block, chain_id } = self; let host = Url::parse(&url) .ok() diff --git a/evm/src/lib.rs b/evm/src/lib.rs index 7b837c52194a..22a9cd53d04c 100644 --- a/evm/src/lib.rs +++ b/evm/src/lib.rs @@ -14,9 +14,6 @@ pub use executor::abi; /// Fuzzing wrapper for executors pub mod fuzz; -/// Support for storing things and disk. -pub mod storage; - // Re-exports pub use ethers::types::Address; pub use hashbrown::HashMap; diff --git a/evm/src/storage/diskmap.rs b/evm/src/storage/diskmap.rs deleted file mode 100644 index dd899173563f..000000000000 --- a/evm/src/storage/diskmap.rs +++ /dev/null @@ -1,137 +0,0 @@ -use std::{ - collections::BTreeMap, - fmt, fs, hash, - io::{BufReader, BufWriter}, - ops, - path::PathBuf, -}; - -use tracing::{trace, warn}; - -/// Disk-serializable BTreeMap -/// -/// How to read and save the contents of the type will be up to the user, -/// See [DiskMap::save()], [DiskMap::reload()] -#[derive(Debug)] -pub(crate) struct DiskMap { - /// Where this file is stored - file_path: PathBuf, - /// Data it holds - cache: BTreeMap, - /// Whether to write to disk - transient: bool, -} - -impl ops::Deref for DiskMap { - type Target = BTreeMap; - fn deref(&self) -> &Self::Target { - &self.cache - } -} - -impl ops::DerefMut for DiskMap { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.cache - } -} - -impl DiskMap { - /// Creates a new, empty `DiskMap` - pub fn new(file_path: impl Into) -> Self { - Self::with_data(file_path, Default::default()) - } - - /// Creates a new, `DiskMap` - pub fn with_data(file_path: impl Into, cache: BTreeMap) -> Self { - let path = file_path.into(); - DiskMap { file_path: path, cache, transient: false } - } - - /// Reads the contents of the diskmap file and returns the read cache - pub fn read(path: impl Into, read: F) -> Self - where - F: Fn(BufReader) -> Result, E>, - E: fmt::Display, - { - let mut map = Self::new(path); - trace!(target: "diskmap" ,"reading diskmap path={:?}", map.file_path); - map.reload(read); - map - } - - /// The path where the diskmap is (will be) stored - pub fn path(&self) -> &PathBuf { - &self.file_path - } - - /// Whether this is a transient disk map - pub fn is_transient(&self) -> bool { - self.transient - } - - /// Consumes the type and returns the underlying map - pub fn into_inner(self) -> BTreeMap { - self.cache - } - - /// Sets the `transient` to the given bool value - pub fn set_transient(mut self, transient: bool) -> Self { - self.transient = transient; - self - } - - /// Marks the cache as transient. - /// - /// A transient cache will never be written to disk - pub fn transient(self) -> Self { - self.set_transient(true) - } - - /// Reloads the cache. - /// - /// This will replace the current cache with the contents of the file the diskmap points to, - /// overwriting all changes - fn reload(&mut self, read: F) - where - F: Fn(BufReader) -> Result, E>, - E: fmt::Display, - { - if self.transient { - return - } - trace!("reloading diskmap {:?}", self.file_path); - let _ = fs::File::open(self.file_path.clone()) - .map_err(|e| trace!(target: "diskmap" , "Failed to open disk map: {}", e)) - .and_then(|f| { - read(BufReader::new(f)) - .map_err(|e| warn!(target: "diskmap" ,"Failed to read disk map: {}", e)) - }) - .map(|m| { - self.cache = m; - }); - } - - /// Saves the diskmap to the file it points to - /// - /// The closure is expected to do the actual writing - pub fn save(&self, write: F) - where - F: Fn(&BTreeMap, &mut BufWriter) -> Result<(), E>, - E: fmt::Display, - { - if self.transient { - return - } - trace!(target: "diskmap", "saving diskmap {:?}", self.file_path); - if let Some(parent) = self.file_path.parent() { - let _ = fs::create_dir_all(parent); - } - let _ = fs::File::create(&self.file_path) - .map_err(|e| warn!(target: "diskmap", "Failed to open disk map for writing: {}", e)) - .and_then(|f| { - let mut f = BufWriter::new(f); - write(&self.cache, &mut f) - .map_err(|e| warn!(target: "diskmap" ,"Failed to write to disk map: {}", e)) - }); - } -} diff --git a/evm/src/storage/mod.rs b/evm/src/storage/mod.rs deleted file mode 100644 index 17afc2ba0c50..000000000000 --- a/evm/src/storage/mod.rs +++ /dev/null @@ -1,130 +0,0 @@ -//! disk storage support and helpers - -use ethers::types::{Address, U256}; -use std::{ - collections::BTreeMap, - path::{Path, PathBuf}, -}; - -pub mod diskmap; - -/// Maps the storage of an account -pub type AccountStorage = BTreeMap>; - -/// Disk-backed map for storage slots, uses json. -pub struct StorageMap { - cache: diskmap::DiskMap>, -} - -impl StorageMap { - /// Creates new storage map at given _directory_. - /// - /// # Example - /// - /// ```no_run - /// use std::collections::BTreeMap; - /// use foundry_evm::Address; - /// use foundry_evm::storage::StorageMap; - /// let mut map = StorageMap::read("data dir"); - /// map.insert(Address::random(), BTreeMap::from([(100u64.into(), 200u64.into())])); - /// map.save(); - /// ``` - pub fn read(path: impl AsRef) -> Self { - StorageMap { cache: diskmap::DiskMap::read(path.as_ref().join("storage.json"), read_u256) } - } - - /// Creates a new, `DiskMap` filled with the cache data - pub fn with_data(path: impl AsRef, cache: AccountStorage) -> Self { - StorageMap { cache: diskmap::DiskMap::with_data(path.as_ref().join("storage.json"), cache) } - } - - /// Creates transient storage map (no changes are saved to disk). - pub fn transient() -> Self { - StorageMap { cache: diskmap::DiskMap::new("storage.json").transient() } - } - - /// Whether this cache is transient - pub fn is_transient(&self) -> bool { - self.cache.is_transient() - } - - /// Sets the given data as the content of this cache - pub fn set_storage(&mut self, data: AccountStorage) { - *self.cache = data; - } - - /// Returns the storage data and replaces it with an empty map - pub fn take_storage(&mut self) -> AccountStorage { - std::mem::take(&mut *self.cache) - } - - /// The path where the diskmap is (will be) stored - pub fn path(&self) -> &PathBuf { - self.cache.path() - } - - /// Get the cache map. - pub fn inner(&self) -> &AccountStorage { - &self.cache - } - - /// Consumes the type and returns the underlying map - pub fn into_inner(self) -> AccountStorage { - self.cache.into_inner() - } - - pub fn save(&self) { - self.cache.save(write_u256) - } - - /// Sets new value for given address. - pub fn insert( - &mut self, - key: Address, - value: BTreeMap, - ) -> Option> { - self.cache.insert(key, value) - } - - /// Removes an entry - pub fn remove(&mut self, key: Address) -> Option> { - self.cache.remove(&key) - } -} - -/// Read a hash map of U256 -> U256 -fn read_u256(reader: R) -> Result -where - R: ::std::io::Read, -{ - serde_json::from_reader(reader) -} - -/// Write a hash map of U256 -> U256 -fn write_u256(m: &AccountStorage, writer: &mut W) -> Result<(), serde_json::Error> -where - W: ::std::io::Write, -{ - serde_json::to_writer(writer, m) -} - -#[cfg(test)] -mod tests { - use super::*; - use tempfile::tempdir; - - #[test] - fn can_save_and_reload_storage_map() { - let tempdir = tempdir().unwrap(); - let mut map = StorageMap::read(tempdir.path()); - let addr = Address::random(); - map.insert(addr, BTreeMap::from([(100u64.into(), 200u64.into())])); - map.save(); - - let map = StorageMap::read(tempdir.path()); - assert_eq!( - map.into_inner(), - BTreeMap::from([(addr, BTreeMap::from([(100u64.into(), 200u64.into()),]))]) - ); - } -} From 5df8a347b14e50ec99e238c9069d2d47d7cba611 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 23 Mar 2022 15:02:01 +0100 Subject: [PATCH 08/12] test: refactor tests --- evm/src/executor/fork/backend.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/evm/src/executor/fork/backend.rs b/evm/src/executor/fork/backend.rs index d4f183b1f801..fbd5304e0615 100644 --- a/evm/src/executor/fork/backend.rs +++ b/evm/src/executor/fork/backend.rs @@ -473,16 +473,13 @@ mod tests { providers::{Http, Provider}, types::Address, }; - use std::convert::TryFrom; + use std::{convert::TryFrom, path::PathBuf}; use super::*; - #[test] - fn shared_backend() { + fn new_db(cache_path: Option) -> BlockchainDb { let url = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27"; let provider = Provider::::try_from(url).unwrap(); - // some rng contract from etherscan - let address: Address = "63091244180ae240c87d1f528f5f269134cb07b3".parse().unwrap(); let meta = BlockchainDbMeta { cfg_env: Default::default(), @@ -490,9 +487,17 @@ mod tests { host: url.to_string(), }; - let db = BlockchainDb::new(meta, None); + BlockchainDb::new(meta, cache_path) + } + + #[test] + fn shared_backend() { + let db = new_db(None); let backend = SharedBackend::new(Arc::new(provider), db.clone(), None); + // some rng contract from etherscan + let address: Address = "63091244180ae240c87d1f528f5f269134cb07b3".parse().unwrap(); + let idx = U256::from(0u64); let value = backend.storage(address, idx); let account = backend.basic(address); @@ -520,4 +525,7 @@ mod tests { let slots = db.storage().read().get(&address).unwrap().clone(); assert_eq!(slots.len() as u64, max_slots); } + + #[test] + fn can_cache() {} } From c70d6e1442acd613bee737c1a840f268ae81169f Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 23 Mar 2022 15:09:09 +0100 Subject: [PATCH 09/12] remove sharedmemcache --- evm/src/executor/fork/backend.rs | 12 ------------ evm/src/executor/fork/mod.rs | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/evm/src/executor/fork/backend.rs b/evm/src/executor/fork/backend.rs index fbd5304e0615..d1f1a18ad4a2 100644 --- a/evm/src/executor/fork/backend.rs +++ b/evm/src/executor/fork/backend.rs @@ -26,18 +26,6 @@ use std::{ }; use tracing::trace; -/// In Memory cache containing all fetched accounts and storage slots -/// and their values from RPC -#[derive(Debug, Default)] -pub struct SharedMemCache { - /// Account related data - pub accounts: Arc>>, - /// Storage related data - pub storage: Arc>>, - /// All retrieved block hashes - pub block_hashes: Arc>>, -} - type AccountFuture = Pin, Address)> + Send>>; type StorageFuture = Pin, Address, U256)> + Send>>; diff --git a/evm/src/executor/fork/mod.rs b/evm/src/executor/fork/mod.rs index 2f3b9c2583ef..a9ed736c2047 100644 --- a/evm/src/executor/fork/mod.rs +++ b/evm/src/executor/fork/mod.rs @@ -1,5 +1,5 @@ mod backend; -pub use backend::{SharedBackend, SharedMemCache}; +pub use backend::SharedBackend; mod init; pub use init::environment; From 04eee7c673f865f8de9b2dce0c8edff9b20cb0c0 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 23 Mar 2022 16:02:35 +0100 Subject: [PATCH 10/12] add tests --- evm/src/executor/builder.rs | 4 +- evm/src/executor/fork/backend.rs | 81 +++++++++++++++++++++++++------- evm/src/executor/fork/cache.rs | 49 +++++++++---------- evm/src/executor/fork/mod.rs | 2 +- evm/test-data/storage.json | 41 ++++++++++++++++ 5 files changed, 128 insertions(+), 49 deletions(-) create mode 100644 evm/test-data/storage.json diff --git a/evm/src/executor/builder.rs b/evm/src/executor/builder.rs index 0b66d5fccccc..3bb2714398e9 100644 --- a/evm/src/executor/builder.rs +++ b/evm/src/executor/builder.rs @@ -38,8 +38,8 @@ pub struct Fork { impl Fork { /// Initialises the Storage Backend /// - /// If configured, then this will initialise the backend with the storage cahce - fn into_backend(self, env: &Env) -> SharedBackend { + /// If configured, then this will initialise the backend with the storage cache + pub fn into_backend(self, env: &Env) -> SharedBackend { let Fork { cache_path, url, pin_block, chain_id } = self; let host = Url::parse(&url) diff --git a/evm/src/executor/fork/backend.rs b/evm/src/executor/fork/backend.rs index d1f1a18ad4a2..947211dffe4f 100644 --- a/evm/src/executor/fork/backend.rs +++ b/evm/src/executor/fork/backend.rs @@ -1,7 +1,7 @@ //! Smart caching and deduplication of requests when using a forking provider use revm::{db::DatabaseRef, AccountInfo, KECCAK_EMPTY}; -use crate::executor::fork::{cache::StorageInfo, BlockchainDb}; +use crate::executor::fork::{BlockchainDb}; use ethers::{ core::abi::ethereum_types::BigEndianHash, providers::Middleware, @@ -15,13 +15,12 @@ use futures::{ task::{Context, Poll}, Future, FutureExt, }; -use parking_lot::RwLock; + use std::{ - collections::{hash_map::Entry, BTreeMap, HashMap}, + collections::{hash_map::Entry, HashMap}, pin::Pin, sync::{ mpsc::{channel as oneshot_channel, Sender as OneshotSender}, - Arc, }, }; use tracing::trace; @@ -318,7 +317,7 @@ where if pin.incoming.is_done() && pin.pending_requests.is_empty() { if let Poll::Ready(Some(ack)) = Pin::new(&mut pin.shutdown).poll_next(cx) { // effectively flushing the cached storage if any - let _ = pin.db.flush_cache(); + pin.db.cache().flush(); // signaling back let _ = ack.send(()); } @@ -456,31 +455,30 @@ impl DatabaseRef for SharedBackend { #[cfg(test)] mod tests { - use crate::executor::fork::BlockchainDbMeta; + use crate::executor::{ + fork::{BlockchainDbMeta, JsonBlockCacheDB}, + Fork, + }; use ethers::{ providers::{Http, Provider}, types::Address, }; + use std::{convert::TryFrom, path::PathBuf}; use super::*; + const ENDPOINT: &str = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27"; - fn new_db(cache_path: Option) -> BlockchainDb { - let url = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27"; - let provider = Provider::::try_from(url).unwrap(); - + #[test] + fn shared_backend() { + let provider = Provider::::try_from(ENDPOINT).unwrap(); let meta = BlockchainDbMeta { cfg_env: Default::default(), block_env: Default::default(), - host: url.to_string(), + host: ENDPOINT.to_string(), }; - BlockchainDb::new(meta, cache_path) - } - - #[test] - fn shared_backend() { - let db = new_db(None); + let db = BlockchainDb::new(meta, None); let backend = SharedBackend::new(Arc::new(provider), db.clone(), None); // some rng contract from etherscan @@ -515,5 +513,52 @@ mod tests { } #[test] - fn can_cache() {} + fn can_read_cache() { + let cache_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/storage.json"); + let json = JsonBlockCacheDB::load(cache_path).unwrap(); + assert!(!json.db().accounts.read().is_empty()); + } + + #[test] + fn can_read_write_cache() { + let tmpdir = tempfile::tempdir().unwrap(); + let cache_path = tmpdir.path().join("storage.json"); + + let block_num = 14435000; + let env = revm::Env::default(); + + let fork = Fork { + cache_path: Some(cache_path.clone()), + url: ENDPOINT.to_string(), + pin_block: Some(block_num), + chain_id: 1, + }; + + let backend = fork.into_backend(&env); + + // some rng contract from etherscan + let address: Address = "63091244180ae240c87d1f528f5f269134cb07b3".parse().unwrap(); + + let idx = U256::from(0u64); + let _value = backend.storage(address, idx); + let _account = backend.basic(address); + + // fill some slots + let num_slots = 10u64; + for idx in 1..num_slots { + let _ = backend.storage(address, idx.into()); + } + drop(backend); + + let meta = BlockchainDbMeta { + cfg_env: Default::default(), + block_env: revm::BlockEnv { number: block_num.into(), ..Default::default() }, + host: "mainnet.infura.io".to_string(), + }; + + let db = BlockchainDb::new(meta, Some(cache_path)); + assert!(db.accounts().read().contains_key(&address)); + assert!(db.storage().read().contains_key(&address)); + assert_eq!(db.storage().read().get(&address).unwrap().len(), num_slots as usize); + } } diff --git a/evm/src/executor/fork/cache.rs b/evm/src/executor/fork/cache.rs index 0060724f1051..417b542c4b2e 100644 --- a/evm/src/executor/fork/cache.rs +++ b/evm/src/executor/fork/cache.rs @@ -10,14 +10,14 @@ use tracing_error::InstrumentResult; pub type StorageInfo = BTreeMap; /// A shareable Block database -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct BlockchainDb { /// Contains all the data db: Arc, /// metadata of the current config meta: Arc>, /// the cache that can be flushed - cache: Arc, + cache: Arc, } impl BlockchainDb { @@ -34,6 +34,7 @@ impl BlockchainDb { pub fn new(meta: BlockchainDbMeta, cache_path: Option) -> Self { // read cache and check if metadata matches let cache = cache_path + .as_ref() .and_then(|p| { JsonBlockCacheDB::load(p).ok().filter(|cache| { if meta != *cache.meta().read() { @@ -44,9 +45,9 @@ impl BlockchainDb { } }) }) - .unwrap_or_else(|| JsonBlockCacheDB::transient(Arc::new(RwLock::new(meta)))); + .unwrap_or_else(|| JsonBlockCacheDB::new(Arc::new(RwLock::new(meta)), cache_path)); - Self { db: Arc::clone(cache.db()), meta: Arc::clone(cache.meta()), cache: Arc::new(()) } + Self { db: Arc::clone(cache.db()), meta: Arc::clone(cache.meta()), cache: Arc::new(cache) } } /// Returns the map that holds the account related info @@ -69,9 +70,9 @@ impl BlockchainDb { &self.meta } - /// Delegate for [BlockCacheDB::flush()] - pub fn flush_cache(&self) { - self.cache.flush() + /// Returns the inner cache + pub fn cache(&self) -> &Arc { + &self.cache } } @@ -95,16 +96,8 @@ pub struct MemDb { pub block_hashes: RwLock>, } -/// Helper trait to abstract disk caching for [SharedMemCache] -pub trait BlockCacheDB: Send + Sync { - /// Expected to flush the cache contents to disk - fn flush(&self) {} -} - -// A [BlockCacheDB] that does nothing -impl BlockCacheDB for () {} - /// A [BlockCacheDB] that stores the cached content in a json file +#[derive(Debug)] pub struct JsonBlockCacheDB { /// Where this cache file is stored. /// @@ -115,14 +108,9 @@ pub struct JsonBlockCacheDB { } impl JsonBlockCacheDB { - /// Marks the cache as transient. - /// - /// A transient cache will never be written to disk - fn transient(meta: Arc>) -> Self { - Self { - cache_path: None, - data: JsonBlockCacheData { meta, data: Arc::new(Default::default()) }, - } + /// Creates a new instance. + fn new(meta: Arc>, cache_path: Option) -> Self { + Self { cache_path, data: JsonBlockCacheData { meta, data: Arc::new(Default::default()) } } } /// Loads the contents of the diskmap file and returns the read object @@ -151,10 +139,14 @@ impl JsonBlockCacheDB { pub fn meta(&self) -> &Arc> { &self.data.meta } -} -impl BlockCacheDB for JsonBlockCacheDB { - fn flush(&self) { + /// Returns `true` if this is a transient cache and nothing will be flushed + pub fn is_transient(&self) -> bool { + self.cache_path.is_none() + } + + /// Flushes the DB to disk if caching is enabled + pub fn flush(&self) { // writes the data to a json file if let Some(ref path) = self.cache_path { trace!(target: "cache", "saving json cache path={:?}", path); @@ -175,6 +167,7 @@ impl BlockCacheDB for JsonBlockCacheDB { /// /// This will be deserialized in a JSON object with the keys: /// `["meta", "accounts", "storage", "block_hashes"]` +#[derive(Debug)] pub struct JsonBlockCacheData { pub meta: Arc>, pub data: Arc, @@ -200,7 +193,7 @@ impl Serialize for JsonBlockCacheData { drop(storage); let block_hashes = self.data.block_hashes.read(); - map.serialize_entry("accounts", &*block_hashes)?; + map.serialize_entry("block_hashes", &*block_hashes)?; drop(block_hashes); map.end() diff --git a/evm/src/executor/fork/mod.rs b/evm/src/executor/fork/mod.rs index a9ed736c2047..aaa8398477de 100644 --- a/evm/src/executor/fork/mod.rs +++ b/evm/src/executor/fork/mod.rs @@ -5,4 +5,4 @@ mod init; pub use init::environment; mod cache; -pub use cache::{BlockCacheDB, BlockchainDb, BlockchainDbMeta, JsonBlockCacheDB}; +pub use cache::{BlockchainDb, BlockchainDbMeta, JsonBlockCacheDB}; diff --git a/evm/test-data/storage.json b/evm/test-data/storage.json new file mode 100644 index 000000000000..ba12e7de0e21 --- /dev/null +++ b/evm/test-data/storage.json @@ -0,0 +1,41 @@ +{ + "meta": { + "cfg_env": { + "chain_id": "0x1", + "spec_id": "LATEST", + "perf_all_precompiles_have_balance": false + }, + "block_env": { + "number": "0xdc42b8", + "coinbase": "0x0000000000000000000000000000000000000000", + "timestamp": "0x1", + "difficulty": "0x0", + "basefee": "0x0", + "gas_limit": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + }, + "host": "mainnet.infura.io" + }, + "accounts": { + "0x63091244180ae240c87d1f528f5f269134cb07b3": { + "balance": "0x0", + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "code": null, + "nonce": 0 + } + }, + "storage": { + "0x63091244180ae240c87d1f528f5f269134cb07b3": { + "0x0": "0x0", + "0x1": "0x0", + "0x2": "0x0", + "0x3": "0x0", + "0x4": "0x0", + "0x5": "0x0", + "0x6": "0x0", + "0x7": "0x0", + "0x8": "0x0", + "0x9": "0x0" + } + }, + "block_hashes": {} +} \ No newline at end of file From fe5adccf75e29a196de4ec67f6d76fb08f4c46ea Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 23 Mar 2022 17:58:28 +0100 Subject: [PATCH 11/12] more tracing --- Cargo.lock | 42 +++------------ cli/Cargo.toml | 3 +- cli/src/utils.rs | 63 ++++++++-------------- config/src/lib.rs | 61 +++++++++++++++++---- evm/Cargo.toml | 2 +- evm/src/executor/fork/backend.rs | 15 +++--- evm/src/executor/fork/cache.rs | 2 +- evm/src/executor/opts.rs | 93 ++++++++++++++++++++------------ forge/Cargo.toml | 2 +- 9 files changed, 154 insertions(+), 129 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eed87df2ab59..6892a44e50cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1594,7 +1594,7 @@ dependencies = [ "serde_json", "tokio", "tracing", - "tracing-subscriber 0.2.25", + "tracing-subscriber 0.3.9", ] [[package]] @@ -1655,7 +1655,8 @@ dependencies = [ "tokio", "toml", "tracing", - "tracing-subscriber 0.2.25", + "tracing-error 0.2.0", + "tracing-subscriber 0.3.9", "ui", "vergen", "walkdir", @@ -1715,7 +1716,7 @@ dependencies = [ "tokio", "tracing", "tracing-error 0.2.0", - "tracing-subscriber 0.2.25", + "tracing-subscriber 0.3.9", "url", ] @@ -2485,15 +2486,6 @@ dependencies = [ "cfg-if 1.0.0", ] -[[package]] -name = "matchers" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" -dependencies = [ - "regex-automata", -] - [[package]] name = "matchers" version = "0.1.0" @@ -4568,36 +4560,15 @@ dependencies = [ "tracing-core", ] -[[package]] -name = "tracing-serde" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" -dependencies = [ - "serde", - "tracing-core", -] - [[package]] name = "tracing-subscriber" version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" dependencies = [ - "ansi_term", - "chrono 0.4.19", - "lazy_static", - "matchers 0.0.1", - "regex", - "serde", - "serde_json", "sharded-slab", - "smallvec", "thread_local", - "tracing", "tracing-core", - "tracing-log", - "tracing-serde", ] [[package]] @@ -4606,13 +4577,16 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e0ab7bdc962035a87fba73f3acca9b8a8d0034c2e6f60b84aeaaddddc155dce" dependencies = [ + "ansi_term", "lazy_static", - "matchers 0.1.0", + "matchers", "regex", "sharded-slab", + "smallvec", "thread_local", "tracing", "tracing-core", + "tracing-log", ] [[package]] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index e571003c8eae..8652afb9e171 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -38,7 +38,8 @@ tokio = { version = "1.11.0", features = ["macros"] } regex = { version = "1.5.4", default-features = false } ansi_term = "0.12.1" rpassword = "5.0.1" -tracing-subscriber = "0.2.20" +tracing-error = "0.2.0" +tracing-subscriber = { version = "0.3", features = ["registry", "env-filter", "fmt"] } tracing = "0.1.26" hex = "0.4.3" rayon = "1.5.1" diff --git a/cli/src/utils.rs b/cli/src/utils.rs index ad4f4723bcb2..818d7c134e4f 100644 --- a/cli/src/utils.rs +++ b/cli/src/utils.rs @@ -5,13 +5,12 @@ use std::{ time::Duration, }; -use ethers::{ - providers::{Middleware, Provider}, - solc::EvmVersion, - types::U256, -}; +use ethers::{solc::EvmVersion, types::U256}; use forge::executor::{opts::EvmOpts, Fork, SpecId}; use foundry_config::{caching::StorageCachingConfig, Config}; +use tracing_error::ErrorLayer; +use tracing_subscriber::prelude::*; + // reexport all `foundry_config::utils` #[doc(hidden)] pub use foundry_config::utils::*; @@ -60,10 +59,11 @@ impl> FoundryPathExt for T { /// Initializes a tracing Subscriber for logging #[allow(dead_code)] pub fn subscriber() { - tracing_subscriber::FmtSubscriber::builder() - // .with_timer(tracing_subscriber::fmt::time::uptime()) - .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) - .init(); + tracing_subscriber::Registry::default() + .with(tracing_subscriber::EnvFilter::from_default_env()) + .with(ErrorLayer::default()) + .with(tracing_subscriber::fmt::layer()) + .init() } pub fn evm_spec(evm: &EvmVersion) -> SpecId { @@ -159,17 +159,21 @@ pub fn block_on(future: F) -> F::Output { /// - storage is allowed (`no_storage_caching = false`) /// /// If all these criteria are met, then storage caching is enabled and storage info will be written -/// to [Config::foundry_cache_dir()]//block/storage.json +/// to [Config::foundry_cache_dir()]///storage.json /// /// for `mainnet` and `--fork-block-number 14435000` on mac the corresponding storage cache will be /// at `~/.foundry/cache/mainnet/14435000/storage.json` pub fn get_fork(evm_opts: &EvmOpts, config: &StorageCachingConfig) -> Option { - /// Returns the chain id of the endpoint url, if available, otherwise mainnet == 1 - /// and the path where the cache file should be - fn get_chain_and_cache_path( + /// Returns the path where the cache file should be stored + /// + /// or `None` if caching should not be enabled + /// + /// See also [ Config::foundry_block_cache_file()] + fn get_block_storage_path( evm_opts: &EvmOpts, config: &StorageCachingConfig, - ) -> Option<(Option, u64)> { + chain_id: u64, + ) -> Option { if evm_opts.no_storage_caching { // storage caching explicitly opted out of return None @@ -177,38 +181,17 @@ pub fn get_fork(evm_opts: &EvmOpts, config: &StorageCachingConfig) -> Option { - let chain_id: u64 = chain_id.try_into().ok()?; - if config.enable_for_chain_id(chain_id) { - let chain = if let Ok(chain) = ethers::types::Chain::try_from(chain_id) { - chain.to_string() - } else { - format!("{}", chain_id) - }; - return Some(( - Some( - Config::foundry_cache_dir()?.join(chain).join(format!("{}", block)), - ), - chain_id, - )) - } - } - Err(err) => { - tracing::warn!("Failed to get chain id for {}: {:?}", url, err); - } - } + + if config.enable_for_endpoint(url) && config.enable_for_chain_id(chain_id) { + return Config::foundry_block_cache_file(chain_id, block) } None } if let Some(ref url) = evm_opts.fork_url { - let (cache_storage, chain_id) = get_chain_and_cache_path(evm_opts, config) - .unwrap_or_else(|| (None, ethers::types::Chain::Mainnet as u64)); + let chain_id = evm_opts.get_chain_id(); + let cache_storage = get_block_storage_path(evm_opts, config, chain_id); let fork = Fork { url: url.clone(), pin_block: evm_opts.fork_block_number, diff --git a/config/src/lib.rs b/config/src/lib.rs index 2ef463f02616..f6ecaa7ebfbf 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -3,6 +3,7 @@ extern crate core; use std::{ borrow::Cow, + fmt, path::{Path, PathBuf}, str::FromStr, }; @@ -709,9 +710,20 @@ impl Config { dirs_next::home_dir().map(|p| p.join(Config::FOUNDRY_DIR_NAME)) } - /// Returns the path to foundry's config dir `~/.foundry/cache` + /// Returns the path to foundry's cache dir `~/.foundry/cache` pub fn foundry_cache_dir() -> Option { - Self::foundry_dir().map(|p|p.join("cache")) + Self::foundry_dir().map(|p| p.join("cache")) + } + + /// Returns the path to the cache file of the `block` on the `chain` + /// `~/.foundry/cache///storage.json` + pub fn foundry_block_cache_file(chain_id: impl Into, block: u64) -> Option { + Some( + Config::foundry_cache_dir()? + .join(chain_id.into().to_string()) + .join(format!("{}", block)) + .join("storage.json"), + ) } #[doc = r#"Returns the path to `foundry`'s data directory inside the user's data directory @@ -1283,6 +1295,42 @@ impl Chain { } } +impl fmt::Display for Chain { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Chain::Named(chain) => chain.fmt(f), + Chain::Id(id) => { + if let Ok(chain) = ethers_core::types::Chain::try_from(*id) { + chain.fmt(f) + } else { + id.fmt(f) + } + } + } + } +} + +impl From for Chain { + fn from(id: ethers_core::types::Chain) -> Self { + Chain::Named(id) + } +} + +impl From for Chain { + fn from(id: u64) -> Self { + Chain::Id(id) + } +} + +impl From for u64 { + fn from(c: Chain) -> Self { + match c { + Chain::Named(c) => c as u64, + Chain::Id(id) => id, + } + } +} + impl<'de> Deserialize<'de> for Chain { fn deserialize(deserializer: D) -> Result where @@ -1329,15 +1377,6 @@ mod from_str_lowercase { } } -impl From for u64 { - fn from(c: Chain) -> Self { - match c { - Chain::Named(c) => c as u64, - Chain::Id(id) => id, - } - } -} - fn canonic(path: impl Into) -> PathBuf { let path = path.into(); ethers_solc::utils::canonicalize(&path).unwrap_or(path) diff --git a/evm/Cargo.toml b/evm/Cargo.toml index a84c6798df99..253e1dc26019 100644 --- a/evm/Cargo.toml +++ b/evm/Cargo.toml @@ -19,7 +19,7 @@ thiserror = "1.0.29" # Logging tracing = "0.1.26" -tracing-subscriber = "0.2.20" +tracing-subscriber = "0.3" tracing-error = "0.2.0" # Threading/futures diff --git a/evm/src/executor/fork/backend.rs b/evm/src/executor/fork/backend.rs index 947211dffe4f..bb7c7c80b9f3 100644 --- a/evm/src/executor/fork/backend.rs +++ b/evm/src/executor/fork/backend.rs @@ -1,7 +1,7 @@ //! Smart caching and deduplication of requests when using a forking provider use revm::{db::DatabaseRef, AccountInfo, KECCAK_EMPTY}; -use crate::executor::fork::{BlockchainDb}; +use crate::executor::fork::BlockchainDb; use ethers::{ core::abi::ethereum_types::BigEndianHash, providers::Middleware, @@ -19,9 +19,7 @@ use futures::{ use std::{ collections::{hash_map::Entry, HashMap}, pin::Pin, - sync::{ - mpsc::{channel as oneshot_channel, Sender as OneshotSender}, - }, + sync::mpsc::{channel as oneshot_channel, Sender as OneshotSender}, }; use tracing::trace; @@ -104,6 +102,7 @@ where fn on_request(&mut self, req: BackendRequest) { match req { BackendRequest::Basic(addr, sender) => { + trace!(target: "backendhandler", "received request basic address={:?}", addr); let lock = self.db.accounts().read(); let basic = lock.get(&addr).cloned(); // release the lock @@ -155,6 +154,7 @@ where entry.get_mut().push(listener); } Entry::Vacant(entry) => { + trace!(target: "backendhandler", "preparing storage request, address={:?}, idx={}", address, idx); entry.insert(vec![listener]); let provider = self.provider.clone(); let block_id = self.block_id; @@ -173,6 +173,7 @@ where /// returns the future that fetches the account data fn get_account_req(&self, address: Address) -> ProviderRequest { + trace!(target: "backendhandler", "preparing account request, address={:?}", address); let provider = self.provider.clone(); let block_id = self.block_id; let fut = Box::pin(async move { @@ -205,6 +206,7 @@ where entry.get_mut().push(listener); } Entry::Vacant(entry) => { + trace!(target: "backendhandler", "preparing block hash request, number={}", number); entry.insert(vec![listener]); let provider = self.provider.clone(); let fut = Box::pin(async move { @@ -392,6 +394,7 @@ impl SharedBackend { let handler = BackendHandler::new(provider, db, backend_rx, shutdown_rx, pin_block); // spawn the provider handler to background let rt = RuntimeOrHandle::new(); + trace!(target: "backendhandler", "spawning Backendhandler"); std::thread::spawn(move || match rt { RuntimeOrHandle::Runtime(runtime) => runtime.block_on(handler), RuntimeOrHandle::Handle(handle) => handle.block_on(handler), @@ -463,8 +466,8 @@ mod tests { providers::{Http, Provider}, types::Address, }; - - use std::{convert::TryFrom, path::PathBuf}; + + use std::{convert::TryFrom, path::PathBuf, sync::Arc}; use super::*; const ENDPOINT: &str = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27"; diff --git a/evm/src/executor/fork/cache.rs b/evm/src/executor/fork/cache.rs index 417b542c4b2e..918644fc567f 100644 --- a/evm/src/executor/fork/cache.rs +++ b/evm/src/executor/fork/cache.rs @@ -121,9 +121,9 @@ impl JsonBlockCacheDB { /// - the format does not match [JsonBlockCacheData] pub fn load(path: impl Into) -> eyre::Result { let path = path.into(); + trace!(target: "cache", "reading json cache path={:?}", path); let span = trace_span!("cache", "path={:?}", &path); let _enter = span.enter(); - trace!("reading json cache path={:?}", path); let file = std::fs::File::open(&path).in_current_span()?; let file = std::io::BufReader::new(file); let data = serde_json::from_reader(file).in_current_span()?; diff --git a/evm/src/executor/opts.rs b/evm/src/executor/opts.rs index 393728792f30..f2e5dfdeae54 100644 --- a/evm/src/executor/opts.rs +++ b/evm/src/executor/opts.rs @@ -1,5 +1,5 @@ use ethers::{ - providers::Provider, + providers::{Middleware, Provider}, types::{Address, U256}, }; use foundry_utils::RuntimeOrHandle; @@ -36,39 +36,6 @@ pub struct EvmOpts { pub verbosity: u8, } -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct Env { - /// the block gas limit - pub gas_limit: u64, - - /// the chainid opcode value - pub chain_id: Option, - - /// the tx.gasprice value during EVM execution - pub gas_price: u64, - - /// the base fee in a block - pub block_base_fee_per_gas: u64, - - /// the tx.origin value during EVM execution - pub tx_origin: Address, - - /// the block.coinbase value during EVM execution - pub block_coinbase: Address, - - /// the block.timestamp value during EVM execution - pub block_timestamp: u64, - - /// the block.number value during EVM execution" - pub block_number: u64, - - /// the block.difficulty value during EVM execution - pub block_difficulty: u64, - - /// the block.gaslimit value during EVM execution - pub block_gas_limit: Option, -} - impl EvmOpts { pub fn evm_env(&self) -> revm::Env { if let Some(ref fork_url) = self.fork_url { @@ -106,4 +73,62 @@ impl EvmOpts { } } } + + /// Returns the configured chain id, which will be + /// - the value of `chain_id` if set + /// - mainnet if `fork_url` contains "mainnet" + /// - the chain if `fork_url` is set and the endpoints returned its chain id successfully + /// - mainnet otherwise + pub fn get_chain_id(&self) -> u64 { + use ethers::types::Chain; + if let Some(id) = self.env.chain_id { + return id + } + if let Some(ref url) = self.fork_url { + if url.contains("mainnet") { + tracing::trace!("auto detected mainnet chain from url {}", url); + return Chain::Mainnet as u64 + } + let provider = Provider::try_from(url.as_str()) + .expect(&format!("Failed to establish provider to {}", url)); + + if let Ok(id) = foundry_utils::RuntimeOrHandle::new().block_on(provider.get_chainid()) { + return id.as_u64() + } + } + Chain::Mainnet as u64 + } +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct Env { + /// the block gas limit + pub gas_limit: u64, + + /// the chainid opcode value + pub chain_id: Option, + + /// the tx.gasprice value during EVM execution + pub gas_price: u64, + + /// the base fee in a block + pub block_base_fee_per_gas: u64, + + /// the tx.origin value during EVM execution + pub tx_origin: Address, + + /// the block.coinbase value during EVM execution + pub block_coinbase: Address, + + /// the block.timestamp value during EVM execution + pub block_timestamp: u64, + + /// the block.number value during EVM execution" + pub block_number: u64, + + /// the block.difficulty value during EVM execution + pub block_difficulty: u64, + + /// the block.gaslimit value during EVM execution + pub block_gas_limit: Option, } diff --git a/forge/Cargo.toml b/forge/Cargo.toml index 6df46babb863..20b1cc7fc6ac 100644 --- a/forge/Cargo.toml +++ b/forge/Cargo.toml @@ -19,7 +19,7 @@ glob = "0.3.0" # TODO: Trim down tokio = { version = "1.10.1" } tracing = "0.1.26" -tracing-subscriber = "0.2.20" +tracing-subscriber = "0.3" proptest = "1.0.0" rayon = "1.5" rlp = "0.5.1" From c9a2a853be46ffb698e79eb358080a8c13d87464 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 23 Mar 2022 18:27:10 +0100 Subject: [PATCH 12/12] chore(clippy): make clippy happy --- evm/src/executor/opts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/src/executor/opts.rs b/evm/src/executor/opts.rs index f2e5dfdeae54..67d5ea9cddbc 100644 --- a/evm/src/executor/opts.rs +++ b/evm/src/executor/opts.rs @@ -90,7 +90,7 @@ impl EvmOpts { return Chain::Mainnet as u64 } let provider = Provider::try_from(url.as_str()) - .expect(&format!("Failed to establish provider to {}", url)); + .unwrap_or_else(|_| panic!("Failed to establish provider to {}", url)); if let Ok(id) = foundry_utils::RuntimeOrHandle::new().block_on(provider.get_chainid()) { return id.as_u64()