How to generate traces for an existing block
Is there a method to generate traces for an existing block? I'm using EthersDB to set the state to one block prior to my target block, and then I essentially pass all the transactions from my target block to the EVM. However, the issue with EthersDB is that it's read-only. Consequently, I can't commit state changes after each transaction. Here's my full example:
macro_rules! local_fill {
($left:expr, $right:expr, $fun:expr) => {
if let Some(right) = $right {
$left = $fun(right.0)
}
};
($left:expr, $right:expr) => {
if let Some(right) = $right {
$left = B160::from(right)
}
};
}
#[tokio::main]
async fn main() -> Result<()> {
// create ethers client and wrap it in Arc<M>
let client = Provider::<Http>::try_from(
"https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27",
)?;
let chain_id: u64 = 1; //mainnet is 1
let client = Arc::new(client);
let block_number = 108889447;
let block = client
.get_block_with_txs(block_number)
.await?
.expect("must not fail");
let previous_block_number = block_number - 1;
//get the state of the previous block
let mut state_db =
EthersDB::new(client.clone(), Some(previous_block_number.into())).expect("cannot fail");
let mut cache_db: CacheDB<EmptyDB> = CacheDB::new(EmptyDB::default());
//TODO: copy state_db into cache_db
let mut evm = EVM::new();
evm.database(&mut state_db);
let mut env = Env::default();
if let Some(number) = block.number {
let nn = number.0[0];
env.block.number = U256::from(nn);
}
local_fill!(env.block.coinbase, Some(block.coinbase));
local_fill!(env.block.timestamp, Some(block.timestamp), U256::from_limbs);
local_fill!(
env.block.difficulty,
Some(block.difficulty),
U256::from_limbs
);
local_fill!(env.block.gas_limit, Some(block.gas_limit), U256::from_limbs);
if let Some(base_fee) = block.base_fee_per_gas {
local_fill!(env.block.basefee, Some(base_fee), U256::from_limbs);
}
println!("Found {} transactions.", block.transactions.len());
// Fill in CfgEnv
env.cfg.chain_id = U256::from(chain_id);
for tx in block.transactions {
env.tx.caller = B160::from(tx.from);
env.tx.gas_limit = tx.gas.as_u64();
local_fill!(env.tx.gas_price, tx.gas_price, U256::from_limbs);
local_fill!(env.tx.value, Some(tx.value), U256::from_limbs);
env.tx.data = tx.input.0;
let mut gas_priority_fee = U256::ZERO;
local_fill!(
gas_priority_fee,
tx.max_priority_fee_per_gas,
U256::from_limbs
);
env.tx.gas_priority_fee = Some(gas_priority_fee);
env.tx.chain_id = Some(chain_id);
env.tx.nonce = Some(tx.nonce.as_u64());
if let Some(access_list) = tx.access_list {
env.tx.access_list = access_list
.0
.into_iter()
.map(|item| {
let new_keys: Vec<U256> = item
.storage_keys
.into_iter()
.map(|h256| {
U256::from_le_bytes(h256.0) // TODO Check Endianess
})
.collect();
(B160::from(item.address), new_keys)
})
.collect();
}
env.tx.transact_to = match tx.to {
Some(to_address) => TransactTo::Call(to_address.into()),
None => TransactTo::create(),
};
evm.env = env.clone();
// up the transaction in the EVM
let inspector = TracerEip3155::new(Box::new(stdout()), true, true);
if let Err(error) = evm.inspect_commit(inspector) {
println!("Got error: {:?}", error);
}
}
Ok(())
}
You can add the EthersDB as the underlying database for the CacheDB with these changes - https://github.com/bluealloy/revm/pull/774
I think the generate_block_example is just what you need
Done in https://github.com/bluealloy/revm/tree/main/examples/block_traces