Skip to content
Merged
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
6 changes: 4 additions & 2 deletions src/daemon/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,8 +407,10 @@ async fn prefill_rpc_caches_for_tipset(state_manager: StateManager, tsk: TipsetK
}
}
{
if let Err(e) = state_manager.execution_trace(&ts).await {
warn!("failed to call `StateManager::execution_trace` for cache warmup: {e:#}");
// Warms both the FVM-replay cache and the parity-trace cache,
// since `eth_trace_block` calls `execution_trace` internally.
if let Err(e) = crate::rpc::eth::eth_trace_block(&state_manager, &ts).await {
warn!("failed to call `eth_trace_block` for cache warmup: {e:#}");
}
}
{
Expand Down
70 changes: 45 additions & 25 deletions src/rpc/methods/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3475,26 +3475,26 @@ impl RpcMethod<1> for EthTraceBlock {
let ts = resolver
.tipset_by_block_number_or_hash(block_param, ResolveNullTipset::TakeOlder)
.await?;
eth_trace_block(&ctx, &ts).await
eth_trace_block(&ctx.state_manager, &ts).await
}
}

/// Replays a tipset and resolves every non-system transaction into a [`trace::TipsetTraceEntry`].
async fn execute_tipset_traces(
ctx: &Ctx,
state_manager: &StateManager,
ts: &Tipset,
) -> Result<(StateTree<DbImpl>, Vec<trace::TipsetTraceEntry>), ServerError> {
let (state_root, raw_traces) = ctx.state_manager.execution_trace(ts).await?;
let state = ctx.state_manager.get_state_tree(&state_root)?;
let (state_root, raw_traces) = state_manager.execution_trace(ts).await?;
let state = state_manager.get_state_tree(&state_root)?;

// Resolve every non-system message's tx hash in parallel. Each lookup is
// an independent DB read; running them sequentially adds O(N) IO
// latency to every trace_block response.
let raw = non_system_traces_with_positions(raw_traces).collect_vec();
let mut entries: Vec<trace::TipsetTraceEntry> = Vec::with_capacity(raw.len());
let mut join_set = tokio::task::JoinSet::new();
let db = ctx.db();
let eth_chain_id = ctx.chain_config().eth_chain_id;
let db = state_manager.db();
let eth_chain_id = state_manager.chain_config().eth_chain_id;
for (msg_position, invoc_result) in raw {
let db = db.shallow_clone();
join_set.spawn_blocking(move || {
Expand Down Expand Up @@ -3533,23 +3533,43 @@ fn non_system_traces_with_positions(
.map(|(idx, ir)| (idx as i64, ir))
}

async fn eth_trace_block(ctx: &Ctx, ts: &Tipset) -> Result<Vec<EthBlockTrace>, ServerError> {
let (state, entries) = execute_tipset_traces(ctx, ts).await?;
let block_hash: EthHash = ts.key().cid()?.into();
let mut all_traces = vec![];
/// Builds the Parity-style block traces for `ts`, caching the result by tipset.
/// Unlike [`StateManager::execution_trace`], this also caches the tx-hash
/// lookups and parity-trace construction.
pub(crate) async fn eth_trace_block(
state_manager: &StateManager,
ts: &Tipset,
) -> Result<Vec<EthBlockTrace>, ServerError> {
// 64 most-recent blocks; bounded by count, not bytes (a few MiB on mainnet,
// see the `cache_eth_trace_block_size` metric).
const ETH_TRACE_BLOCK_CACHE_SIZE: NonZeroUsize = nonzero!(64usize);
static ETH_TRACE_BLOCK_CACHE: LazyLock<SizeTrackingCache<CidWrapper, Arc<Vec<EthBlockTrace>>>> =
LazyLock::new(|| {
SizeTrackingCache::new_with_metrics("eth_trace_block", ETH_TRACE_BLOCK_CACHE_SIZE)
});

for entry in entries {
for trace in entry.build_parity_traces(&state)? {
all_traces.push(EthBlockTrace {
trace,
block_hash,
block_number: ts.epoch(),
transaction_hash: entry.tx_hash,
transaction_position: entry.msg_position,
});
}
}
Ok(all_traces)
let block_cid = ts.key().cid()?;
let traces = ETH_TRACE_BLOCK_CACHE
.get_or_insert_async(&CidWrapper::from(block_cid), async move {
let (state, entries) = execute_tipset_traces(state_manager, ts).await?;
let block_hash: EthHash = block_cid.into();
let mut all_traces = vec![];

for entry in entries {
for trace in entry.build_parity_traces(&state)? {
all_traces.push(EthBlockTrace {
trace,
block_hash,
block_number: ts.epoch(),
transaction_hash: entry.tx_hash,
transaction_position: entry.msg_position,
});
}
}
anyhow::Ok(Arc::new(all_traces))
})
.await?;
Ok(Arc::unwrap_or_clone(traces))
}

pub enum EthDebugTraceTransaction {}
Expand Down Expand Up @@ -3657,7 +3677,7 @@ async fn debug_trace_transaction(
return Ok(GethTrace::PreState(frame));
}

let (state, entries) = execute_tipset_traces(&ctx, &ts).await?;
let (state, entries) = execute_tipset_traces(&ctx.state_manager, &ts).await?;
let entry = entries
.into_iter()
.find(|e| e.tx_hash == eth_hash)
Expand Down Expand Up @@ -3860,7 +3880,7 @@ impl RpcMethod<1> for EthTraceTransaction {
.tipset_by_block_number_or_hash(eth_txn.block_number, ResolveNullTipset::TakeOlder)
.await?;

let traces = eth_trace_block(&ctx, &ts)
let traces = eth_trace_block(&ctx.state_manager, &ts)
.await?
.into_iter()
.filter(|trace| trace.transaction_hash == eth_hash)
Expand Down Expand Up @@ -3909,7 +3929,7 @@ async fn eth_trace_replay_block_transactions(
ctx: &Ctx,
ts: &Tipset,
) -> Result<Vec<EthReplayBlockTransactionTrace>, ServerError> {
let (state, entries) = execute_tipset_traces(ctx, ts).await?;
let (state, entries) = execute_tipset_traces(&ctx.state_manager, ts).await?;

let mut all_traces = vec![];
for entry in entries {
Expand Down
31 changes: 22 additions & 9 deletions src/rpc/methods/eth/trace/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ use crate::rpc::eth::trace::GETH_TRACE_REVERT_ERROR;
use crate::rpc::eth::trace::utils::extract_revert_reason;
use crate::shim::error::ExitCode;
use anyhow::{Context as _, Result, bail};
use get_size2::GetSize;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;

/// Typed error for Parity-style EVM trace entries.
#[derive(Debug, Hash, Clone, PartialEq, Eq, thiserror::Error)]
#[derive(Debug, Hash, Clone, PartialEq, Eq, thiserror::Error, GetSize)]
pub enum TraceError {
#[error("Reverted")]
Reverted,
Expand Down Expand Up @@ -102,7 +103,9 @@ fn parse_exit_code_display(s: &str) -> u32 {
.unwrap_or(0)
}

#[derive(Eq, Hash, PartialEq, Default, Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[derive(
Eq, Hash, PartialEq, Default, Serialize, Deserialize, Debug, Clone, JsonSchema, GetSize,
)]
#[serde(rename_all = "camelCase")]
pub struct EthCallTraceAction {
pub call_type: String,
Expand All @@ -113,7 +116,9 @@ pub struct EthCallTraceAction {
pub input: EthBytes,
}

#[derive(Eq, Hash, PartialEq, Default, Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[derive(
Eq, Hash, PartialEq, Default, Serialize, Deserialize, Debug, Clone, JsonSchema, GetSize,
)]
#[serde(rename_all = "camelCase")]
pub struct EthCreateTraceAction {
pub from: EthAddress,
Expand All @@ -122,7 +127,7 @@ pub struct EthCreateTraceAction {
pub init: EthBytes,
}

#[derive(Eq, Hash, PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[derive(Eq, Hash, PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema, GetSize)]
#[serde(untagged)]
pub enum TraceAction {
Call(EthCallTraceAction),
Expand All @@ -135,14 +140,18 @@ impl Default for TraceAction {
}
}

#[derive(Eq, Hash, PartialEq, Default, Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[derive(
Eq, Hash, PartialEq, Default, Serialize, Deserialize, Debug, Clone, JsonSchema, GetSize,
)]
#[serde(rename_all = "camelCase")]
pub struct EthCallTraceResult {
pub gas_used: EthUint64,
pub output: EthBytes,
}

#[derive(Eq, Hash, PartialEq, Default, Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[derive(
Eq, Hash, PartialEq, Default, Serialize, Deserialize, Debug, Clone, JsonSchema, GetSize,
)]
#[serde(rename_all = "camelCase")]
pub struct EthCreateTraceResult {
#[serde(skip_serializing_if = "Option::is_none")]
Expand All @@ -151,7 +160,7 @@ pub struct EthCreateTraceResult {
pub code: EthBytes,
}

#[derive(Eq, Hash, PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[derive(Eq, Hash, PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema, GetSize)]
#[serde(untagged)]
pub enum TraceResult {
Call(EthCallTraceResult),
Expand Down Expand Up @@ -496,7 +505,9 @@ impl EthTraceResults {
}
}

#[derive(Eq, Hash, PartialEq, Default, Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[derive(
Eq, Hash, PartialEq, Default, Serialize, Deserialize, Debug, Clone, JsonSchema, GetSize,
)]
#[serde(rename_all = "camelCase")]
pub struct EthTrace {
pub r#type: String,
Expand Down Expand Up @@ -595,7 +606,9 @@ impl EthTrace {
}
}

#[derive(Eq, Hash, PartialEq, Default, Serialize, Deserialize, Debug, Clone, JsonSchema)]
#[derive(
Eq, Hash, PartialEq, Default, Serialize, Deserialize, Debug, Clone, JsonSchema, GetSize,
)]
#[serde(rename_all = "camelCase")]
pub struct EthBlockTrace {
#[serde(flatten)]
Expand Down
Loading