-
Notifications
You must be signed in to change notification settings - Fork 606
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
How to simulate debug_traceCall
with revm
#1092
Comments
i don't think that's possible with revm, revm is just a rust implementation of evm |
for rpc tracing, ptal https://github.com/paradigmxyz/evm-inspectors |
could you add example of this? |
you can take a look at the anvil or reth codebase |
Kind of hackish, but works for me (kind of 😅) // Example Adapted From: https://github.com/bluealloy/revm/issues/672
use ethers_core::types::BlockId;
use ethers_providers::Middleware;
use ethers_providers::{Http, Provider};
use indicatif::ProgressBar;
use revm::db::{CacheDB, EthersDB, StateBuilder};
use revm::inspectors::{CustomPrintTracer, TracerEip3155};
use revm::primitives::{Address, TransactTo, U256};
use revm::{inspector_handle_register, Evm};
use revm_inspectors::tracing::TracingInspectorConfig;
use std::fs::OpenOptions;
use std::io::BufWriter;
use std::io::Write;
use std::sync::Arc;
use std::sync::Mutex;
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 = Address::from(right.as_fixed_bytes())
}
};
}
struct FlushWriter {
writer: Arc<Mutex<BufWriter<std::fs::File>>>,
}
impl FlushWriter {
fn new(writer: Arc<Mutex<BufWriter<std::fs::File>>>) -> Self {
Self { writer }
}
}
impl Write for FlushWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.writer.lock().unwrap().write(buf)
}
fn flush(&mut self) -> std::io::Result<()> {
self.writer.lock().unwrap().flush()
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Create ethers client and wrap it in Arc<M>
let client = Provider::<Http>::try_from("RPC_URL")?;
let client = Arc::new(client);
// Params
let chain_id: u64 = 0;
let block_number = 0;
// Fetch the transaction-rich block
let block = match client.get_block_with_txs(block_number).await {
Ok(Some(block)) => block,
Ok(None) => anyhow::bail!("Block not found"),
Err(error) => anyhow::bail!("Error: {:?}", error),
};
println!(
"Fetched block number: {} {}",
block.number.unwrap().0[0],
block.transactions.first().unwrap().hash
);
let previous_block_number = block_number - 1;
// Use the previous block state as the db with caching
let prev_id: BlockId = previous_block_number.into();
// SAFETY: This cannot fail since this is in the top-level tokio runtime
let state_db = EthersDB::new(Arc::clone(&client), Some(prev_id)).expect("panic");
let cache_db: CacheDB<EthersDB<Provider<Http>>> = CacheDB::new(state_db);
let mut state = StateBuilder::new_with_database(cache_db).build();
let mut evm = Evm::builder()
.with_db(&mut state)
.with_external_context(revm_inspectors::tracing::TracingInspector::new(
TracingInspectorConfig {
exclude_precompile_calls: true,
record_call_return_data: true,
record_logs: false,
record_memory_snapshots: false,
record_state_diff: false,
record_steps: false,
record_stack_snapshots: revm_inspectors::tracing::StackSnapshotType::Pushes,
},
))
.modify_block_env(|b| {
if let Some(number) = block.number {
let nn = number.0[0];
b.number = U256::from(nn);
}
local_fill!(b.coinbase, block.author);
local_fill!(b.timestamp, Some(block.timestamp), U256::from_limbs);
local_fill!(b.difficulty, Some(block.difficulty), U256::from_limbs);
local_fill!(b.gas_limit, Some(block.gas_limit), U256::from_limbs);
if let Some(base_fee) = block.base_fee_per_gas {
local_fill!(b.basefee, Some(base_fee), U256::from_limbs);
}
})
.modify_cfg_env(|c| {
c.chain_id = chain_id;
c.limit_contract_code_size = None;
})
.build();
let txs = block.transactions.len();
println!("Found {txs} transactions.");
let console_bar = Arc::new(ProgressBar::new(txs as u64));
let elapsed = std::time::Duration::ZERO;
// Create the traces directory if it doesn't exist
std::fs::create_dir_all("traces").expect("Failed to create traces directory");
evm.context.external.get_traces();
// Fill in CfgEnv
for (i, tx) in block.transactions.iter().enumerate() {
evm = evm
.modify()
.modify_tx_env(|etx| {
etx.caller = Address::from(tx.from.as_fixed_bytes());
etx.gas_limit = tx.gas.as_u64();
local_fill!(etx.gas_price, tx.gas_price, U256::from_limbs);
local_fill!(etx.value, Some(tx.value), U256::from_limbs);
etx.data = tx.clone().input.0.into();
let mut gas_priority_fee = U256::ZERO;
local_fill!(
gas_priority_fee,
tx.max_priority_fee_per_gas,
U256::from_limbs
);
etx.gas_priority_fee = Some(gas_priority_fee);
etx.chain_id = Some(chain_id);
etx.nonce = Some(tx.nonce.as_u64());
if let Some(access_list) = tx.clone().access_list {
etx.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))
.collect();
(Address::from(item.address.as_fixed_bytes()), new_keys)
})
.collect();
} else {
etx.access_list = Default::default();
}
etx.transact_to = match tx.to {
Some(to_address) => {
TransactTo::Call(Address::from(to_address.as_fixed_bytes()))
}
None => TransactTo::create(),
};
})
.build();
// Construct the file writer to write the trace to
let file_name = format!("{}.log", i);
let write = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(file_name);
let inner = Arc::new(Mutex::new(BufWriter::new(
write.expect("Failed to open file"),
)));
println!("{:?}", tx.hash);
let mut writer = FlushWriter::new(Arc::clone(&inner));
if let Err(error) = evm.transact_commit() {
println!("Got error: {:?}", error);
}
// Inspect and commit the transaction to the EVM
let traces = evm.context.external.get_traces(); // .set_writer(Box::new(writer));
writer
.write_all(format!("{:#?}", traces).as_bytes())
.unwrap();
// Flush the file writer
inner.lock().unwrap().flush().expect("Failed to flush file");
console_bar.inc(1);
}
console_bar.finish_with_message("Finished all transactions.");
println!(
"Finished execution. Total CPU time: {}s",
elapsed.as_secs_f64()
);
Ok(())
} I'll leave this issue open till we have a more better solution |
@rakita the generate_block_traces.rs blocks for me, investigating, I noticed that this condition is ALWAYS met leading to the program blocking and CPU going to 99%. I wonder what I could be doing wrong. I am running from the main branch and ran the example as is. Could I be doing something wrong? |
Hey it is probably related to this: #1117 Fatal error can happen if rpc does not return. The bug was that this was not propagated and would continue running. |
would close this |
Hello, I have been looking into the examples of revm, most especially that of block traces, any way I can modify the example to give a simplified output shown on
debug_traceCall
The text was updated successfully, but these errors were encountered: