Skip to content
Closed
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
3 changes: 2 additions & 1 deletion bins/revm-test/src/bin/analysis.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::time::Instant;

use bytes::Bytes;
use revm::{db::BenchmarkDB, Bytecode, TransactTo};
use revm::{blockchain::EmptyBlockchain, db::BenchmarkDB, Bytecode, TransactTo};
extern crate alloc;

fn main() {
Expand All @@ -28,6 +28,7 @@ fn main() {
let bytecode_analysed = Bytecode::new_raw(contract_data).to_analysed::<revm::LondonSpec>();

evm.database(BenchmarkDB::new_bytecode(bytecode_raw));
evm.set_blockchain(EmptyBlockchain);

// just to spead up processor.
for _ in 0..10000 {
Expand Down
3 changes: 2 additions & 1 deletion bins/revm-test/src/bin/snailtracer.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::time::Duration;

use bytes::Bytes;
use revm::{db::BenchmarkDB, Bytecode, TransactTo};
use revm::{blockchain::EmptyBlockchain, db::BenchmarkDB, Bytecode, TransactTo};

extern crate alloc;

Expand All @@ -11,6 +11,7 @@ pub fn simple_example() {
// BenchmarkDB is dummy state that implements Database trait.
let mut evm = revm::new();
evm.database(BenchmarkDB::new_bytecode(Bytecode::new_raw(contract_data)));
evm.set_blockchain(EmptyBlockchain);

// execution globals block hash/gas_limit/coinbase/timestamp..
evm.env.tx.caller = "0x1000000000000000000000000000000000000000"
Expand Down
1 change: 1 addition & 0 deletions bins/revme/src/statetest/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ pub fn execute_test_suit(path: &Path, elapsed: &Arc<Mutex<Duration>>) -> Result<
let mut database_cloned = database.clone();
let mut evm = revm::new();
evm.database(&mut database_cloned);
evm.set_blockchain(revm::blockchain::InMemoryBlockchain::default());
evm.env = env.clone();
// do the deed

Expand Down
39 changes: 39 additions & 0 deletions crates/revm/src/blockchain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#[cfg(feature = "ethersdb")]
mod ethers_blockchain;
mod in_memory_blockchain;

#[cfg(feature = "ethersdb")]
pub use ethers_blockchain::EthersBlockchain;

pub use in_memory_blockchain::{CachedBlockchain, EmptyBlockchain, InMemoryBlockchain};

use auto_impl::auto_impl;

use crate::{B256, U256};

#[auto_impl(& mut, Box)]
pub trait Blockchain {
type Error;

// Get block hash by block number
fn block_hash(&mut self, number: U256) -> Result<B256, Self::Error>;
}

#[auto_impl(&, Box, Arc)]
pub trait BlockchainRef {
type Error;

// Get block hash by block number
fn block_hash(&self, number: U256) -> Result<B256, Self::Error>;
}

impl<T> Blockchain for &T
where
T: BlockchainRef,
{
type Error = <T as BlockchainRef>::Error;

fn block_hash(&mut self, number: U256) -> Result<B256, Self::Error> {
BlockchainRef::block_hash(*self, number)
}
}
112 changes: 112 additions & 0 deletions crates/revm/src/blockchain/ethers_blockchain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use std::sync::Arc;

use crate::{B256, KECCAK_EMPTY, U256};

use ethers_core::types::{BlockId, U64 as eU64};
use ethers_providers::Middleware;
use tokio::runtime::{Handle, Runtime};

use super::Blockchain;

pub struct EthersBlockchain<M>
where
M: Middleware,
{
client: Arc<M>,
runtime: Option<Runtime>,
block_number: Option<BlockId>,
}

impl<M> EthersBlockchain<M>
where
M: Middleware,
{
/// create ethers db connector inputs are url and block on what we are basing our database (None for latest)
pub fn new(client: Arc<M>, block_number: Option<u64>) -> Option<Self> {
let runtime = Handle::try_current()
.is_err()
.then(|| Runtime::new().unwrap());

let client = client;

let mut out = Self {
client,
runtime,
block_number: None,
};
let bnum = if let Some(block_number) = block_number {
block_number.into()
} else {
out.block_on(out.client.get_block_number()).ok()?
};

out.block_number = Some(BlockId::from(bnum));
Some(out)
}

/// internal utility function to call tokio feature and wait for output
fn block_on<F: core::future::Future>(&self, f: F) -> F::Output {
match &self.runtime {
Some(runtime) => runtime.block_on(f),
None => futures::executor::block_on(f),
}
}
}

impl<M> Blockchain for EthersBlockchain<M>
where
M: Middleware,
{
type Error = ();

fn block_hash(&mut self, number: U256) -> Result<B256, Self::Error> {
// saturate usize
if number > U256::from(u64::MAX) {
return Ok(KECCAK_EMPTY);
}
let number = eU64::from(u64::try_from(number).unwrap());
let f = async {
self.client
.get_block(BlockId::from(number))
.await
.ok()
.flatten()
};
Ok(B256(self.block_on(f).unwrap().hash.unwrap().0))
}
}

/// Run tests with `cargo test -- --nocapture` to see print statements
#[cfg(test)]
mod tests {
use std::str::FromStr;

use super::*;
use ethers_providers::{Http, Provider};

#[test]
fn can_get_block_hash() {
let client = Provider::<Http>::try_from(
"https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27",
)
.unwrap();
let client = Arc::new(client);

let mut ethers_blockchain = EthersBlockchain::new(
Arc::clone(&client), // public infura mainnet
None,
)
.unwrap();

// block number to test
let block_num = U256::from(16148323);
let block_hash = ethers_blockchain.block_hash(block_num).unwrap();

// https://etherscan.io/block/16148323
let actual =
B256::from_str("0xc133a5a4ceef2a6b5cd6fc682e49ca0f8fce3f18da85098c6a15f8e0f6f4c2cf")
.unwrap();

assert_eq!(block_hash, actual);
}
}
75 changes: 75 additions & 0 deletions crates/revm/src/blockchain/in_memory_blockchain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use crate::blockchain::{Blockchain, BlockchainRef};
use crate::common::keccak256;
use crate::{B256, U256};
use core::convert::Infallible;
use hashbrown::{hash_map::Entry, HashMap as Map};

pub type InMemoryBlockchain = CachedBlockchain<EmptyBlockchain>;

impl Default for InMemoryBlockchain {
fn default() -> Self {
CachedBlockchain::new(EmptyBlockchain)
}
}

/// Memory backend, storing all state values in a `Map` in memory.
#[derive(Debug, Clone)]
pub struct CachedBlockchain<ExtBC: BlockchainRef> {
pub block_hashes: Map<U256, B256>,
pub blockchain: ExtBC,
}

impl<ExtBC: BlockchainRef> CachedBlockchain<ExtBC> {
pub fn new(blockchain: ExtBC) -> Self {
Self {
block_hashes: Map::new(),
blockchain,
}
}
}

impl<ExtBC: BlockchainRef> Blockchain for CachedBlockchain<ExtBC> {
type Error = ExtBC::Error;

fn block_hash(&mut self, number: U256) -> Result<B256, Self::Error> {
match self.block_hashes.entry(number) {
Entry::Occupied(entry) => Ok(*entry.get()),
Entry::Vacant(entry) => {
let hash = self.blockchain.block_hash(number)?;
entry.insert(hash);
Ok(hash)
}
}
}
}

impl<ExtBC: BlockchainRef> BlockchainRef for CachedBlockchain<ExtBC> {
type Error = ExtBC::Error;

fn block_hash(&self, number: U256) -> Result<B256, Self::Error> {
match self.block_hashes.get(&number) {
Some(entry) => Ok(*entry),
None => self.blockchain.block_hash(number),
}
}
}

/// An empty database that always returns default values when queried.
#[derive(Debug, Default, Clone)]
pub struct EmptyBlockchain;

impl Blockchain for EmptyBlockchain {
type Error = Infallible;

fn block_hash(&mut self, number: U256) -> Result<B256, Self::Error> {
Ok(keccak256(&number.to_be_bytes::<{ U256::BYTES }>()))
}
}

impl BlockchainRef for EmptyBlockchain {
type Error = Infallible;

fn block_hash(&self, number: U256) -> Result<B256, Self::Error> {
Ok(keccak256(&number.to_be_bytes::<{ U256::BYTES }>()))
}
}
11 changes: 0 additions & 11 deletions crates/revm/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ pub trait Database {
fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error>;
/// Get storage value of address at index.
fn storage(&mut self, address: B160, index: U256) -> Result<U256, Self::Error>;

// History related
fn block_hash(&mut self, number: U256) -> Result<B256, Self::Error>;
}

#[auto_impl(& mut, Box)]
Expand All @@ -48,9 +45,6 @@ pub trait DatabaseRef {
fn code_by_hash(&self, code_hash: B256) -> Result<Bytecode, Self::Error>;
/// Get storage value of address at index.
fn storage(&self, address: B160, index: U256) -> Result<U256, Self::Error>;

// History related
fn block_hash(&self, number: U256) -> Result<B256, Self::Error>;
}

pub struct RefDBWrapper<'a, Error> {
Expand All @@ -77,9 +71,4 @@ impl<'a, Error> Database for RefDBWrapper<'a, Error> {
fn storage(&mut self, address: B160, index: U256) -> Result<U256, Self::Error> {
self.db.storage(address, index)
}

// History related
fn block_hash(&mut self, number: U256) -> Result<B256, Self::Error> {
self.db.block_hash(number)
}
}
50 changes: 2 additions & 48 deletions crates/revm/src/db/ethersdb.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
use std::sync::Arc;

use crate::{
interpreter::bytecode::Bytecode, AccountInfo, Database, B160, B256, KECCAK_EMPTY, U256,
};
use crate::{interpreter::bytecode::Bytecode, AccountInfo, Database, B160, B256, U256};

use ethers_core::types::{BlockId, H160 as eH160, H256, U64 as eU64};
use ethers_core::types::{BlockId, H160 as eH160, H256};
use ethers_providers::Middleware;
use tokio::runtime::{Handle, Runtime};

Expand Down Expand Up @@ -104,29 +102,11 @@ where
};
Ok(self.block_on(f))
}

fn block_hash(&mut self, number: U256) -> Result<B256, Self::Error> {
// saturate usize
if number > U256::from(u64::MAX) {
return Ok(KECCAK_EMPTY);
}
let number = eU64::from(u64::try_from(number).unwrap());
let f = async {
self.client
.get_block(BlockId::from(number))
.await
.ok()
.flatten()
};
Ok(B256(self.block_on(f).unwrap().hash.unwrap().0))
}
}

/// Run tests with `cargo test -- --nocapture` to see print statements
#[cfg(test)]
mod tests {
use std::str::FromStr;

use super::*;
use ethers_core::types::U256 as eU256;
use ethers_providers::{Http, Provider};
Expand Down Expand Up @@ -187,30 +167,4 @@ mod tests {

assert_eq!(storage, actual);
}

#[test]
fn can_get_block_hash() {
let client = Provider::<Http>::try_from(
"https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27",
)
.unwrap();
let client = Arc::new(client);

let mut ethersdb = EthersDB::new(
Arc::clone(&client), // public infura mainnet
None,
)
.unwrap();

// block number to test
let block_num = U256::from(16148323);
let block_hash = ethersdb.block_hash(block_num).unwrap();

// https://etherscan.io/block/16148323
let actual =
B256::from_str("0xc133a5a4ceef2a6b5cd6fc682e49ca0f8fce3f18da85098c6a15f8e0f6f4c2cf")
.unwrap();

assert_eq!(block_hash, actual);
}
}
Loading