Skip to content
Open
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
57 changes: 57 additions & 0 deletions crates/blockchain/tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,63 @@ impl Blockchain {
Ok(call_traces)
}

/// Re-executes the given transaction with tracing disabled (noopTracer).
/// Returns once the tx finishes; nothing is recorded. May need to re-execute ancestor
/// blocks to rebuild the parent state, up to the amount given by `reexec`.
pub async fn trace_transaction_noop(
&self,
tx_hash: H256,
reexec: u32,
timeout: Duration,
) -> Result<(), ChainError> {
let Some((_, block_hash, tx_index)) =
self.storage.get_transaction_location(tx_hash).await?
else {
return Err(ChainError::Custom("Transaction not Found".to_string()));
};
let tx_index = tx_index as usize;
let Some(block) = self.storage.get_block_by_hash(block_hash).await? else {
return Err(ChainError::Custom("Block not Found".to_string()));
};
let mut vm = self
.rebuild_parent_state(block.header.parent_hash, reexec)
.await?;
vm.rerun_block(&block, Some(tx_index))?;
timeout_trace_operation(timeout, move || vm.trace_tx_noop(&block, tx_index)).await
}

/// Re-executes every transaction in `block` with tracing disabled (noopTracer).
/// Returns one entry per tx (hash + empty unit) so callers can emit a uniform empty result per tx.
/// May need to re-execute ancestor blocks to rebuild the parent state, up to the amount given by `reexec`.
pub async fn trace_block_noop(
&self,
block: Block,
reexec: u32,
timeout: Duration,
) -> Result<Vec<(H256, ())>, ChainError> {
let mut vm = self
.rebuild_parent_state(block.header.parent_hash, reexec)
.await?;
// Run system calls but stop before tx 0
vm.rerun_block(&block, Some(0))?;
let vm = Arc::new(Mutex::new(vm));
let block = Arc::new(block);
let mut results = Vec::with_capacity(block.body.transactions.len());
for index in 0..block.body.transactions.len() {
let block = block.clone();
let vm = vm.clone();
let tx_hash = block.as_ref().body.transactions[index].hash();
timeout_trace_operation(timeout, move || {
vm.lock()
.map_err(|_| EvmError::Custom("Unexpected Runtime Error".to_string()))?
.trace_tx_noop(block.as_ref(), index)
})
.await?;
results.push((tx_hash, ()));
}
Ok(results)
}

/// Outputs the prestate trace for the given transaction.
/// If `diff_mode` is true, returns both pre and post state; otherwise returns only pre state.
/// `include_empty` keeps default-state entries in pre (only valid when `diff_mode` is false).
Expand Down
22 changes: 22 additions & 0 deletions crates/networking/rpc/tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,12 @@ struct TraceConfig {

#[derive(Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[allow(clippy::enum_variant_names)]
enum TracerType {
#[default]
CallTracer,
PrestateTracer,
NoopTracer,
}

#[derive(Deserialize, Default)]
Expand Down Expand Up @@ -171,6 +173,14 @@ impl RpcHandler for TraceTransactionRequest {
PrestateResult::Diff(diff) => Ok(serde_json::to_value(diff)?),
}
}
TracerType::NoopTracer => {
context
.blockchain
.trace_transaction_noop(self.tx_hash, reexec, timeout)
.await
.map_err(|err| RpcErr::Internal(err.to_string()))?;
Ok(serde_json::json!({}))
}
}
}
}
Expand Down Expand Up @@ -282,6 +292,18 @@ impl RpcHandler for TraceBlockByNumberRequest {
.collect::<Result<_, serde_json::Error>>()?;
Ok(serde_json::to_value(block_trace)?)
}
TracerType::NoopTracer => {
let traces = context
.blockchain
.trace_block_noop(block, reexec, timeout)
.await
.map_err(|err| RpcErr::Internal(err.to_string()))?;
let block_trace: BlockTrace<serde_json::Value> = traces
.into_iter()
.map(|(hash, ())| (hash, serde_json::json!({})).into())
.collect();
Ok(serde_json::to_value(block_trace)?)
}
}
}
}
19 changes: 19 additions & 0 deletions crates/vm/backends/levm/tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,25 @@ impl LEVM {
}
}

/// Executes `tx` with tracing disabled and discards the result. Matches geth's
/// `noopTracer`: the transaction still runs (so execution errors surface and the
/// trace harness overhead is exercised), but nothing is recorded.
pub fn trace_tx_noop(
db: &mut GeneralizedDatabase,
block_header: &BlockHeader,
tx: &Transaction,
vm_type: VMType,
crypto: &dyn Crypto,
) -> Result<(), EvmError> {
let sender = tx
.sender(crypto)
.map_err(|e| EvmError::Transaction(format!("Couldn't recover sender: {e}")))?;
let env = Self::setup_env(tx, sender, block_header, db, vm_type)?;
let mut vm = VM::new(env, db, tx, LevmCallTracer::disabled(), vm_type, crypto)?;
vm.execute()?;
Ok(())
}

/// Run transaction with callTracer activated.
pub fn trace_tx_calls(
db: &mut GeneralizedDatabase,
Expand Down
20 changes: 20 additions & 0 deletions crates/vm/tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,26 @@ impl Evm {
)
}

/// Executes a single tx with tracing disabled (noopTracer).
/// Assumes that the received state already contains changes from previous transactions.
pub fn trace_tx_noop(&mut self, block: &Block, tx_index: usize) -> Result<(), EvmError> {
let tx = block
.body
.transactions
.get(tx_index)
.ok_or(EvmError::Custom(
"Missing Transaction for Trace".to_string(),
))?;

LEVM::trace_tx_noop(
&mut self.db,
&block.header,
tx,
self.vm_type,
self.crypto.as_ref(),
)
}

/// Executes a single tx and captures the pre/post account state (prestateTracer).
/// Assumes that the received state already contains changes from previous transactions.
pub fn trace_tx_prestate(
Expand Down
Loading