revm icon indicating copy to clipboard operation
revm copied to clipboard

How to generate traces for an existing block

Open mr-ma opened this issue 2 years ago • 2 comments

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(())
}

mr-ma avatar Aug 31 '23 16:08 mr-ma

You can add the EthersDB as the underlying database for the CacheDB with these changes - https://github.com/bluealloy/revm/pull/774

0xkr8os avatar Oct 06 '23 15:10 0xkr8os

I think the generate_block_example is just what you need

Pana avatar Mar 26 '24 07:03 Pana

Done in https://github.com/bluealloy/revm/tree/main/examples/block_traces

rakita avatar Mar 13 '25 17:03 rakita