Skip to content
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

Implement Filecoin.EthTraceTransaction #5386

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@

- [#5375](https://github.com/ChainSafe/forest/issues/5375) Add an RNG wrapper that that can be overriden by a reproducible seeded RNG.

- [#5386](https://github.com/ChainSafe/forest/pull/5386) Add support for the `Filecoin.EthTraceTransaction` RPC method.

### Changed

### Removed
Expand Down
134 changes: 81 additions & 53 deletions src/rpc/methods/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2233,7 +2233,7 @@ impl RpcMethod<1> for EthGetTransactionByHash {
ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
(tx_hash,): Self::Params,
) -> Result<Self::Ok, ServerError> {
get_eth_transaction_by_hash(ctx, tx_hash, None).await
get_eth_transaction_by_hash(&ctx, &tx_hash, None).await
}
}

Expand All @@ -2252,16 +2252,16 @@ impl RpcMethod<2> for EthGetTransactionByHashLimited {
ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
(tx_hash, limit): Self::Params,
) -> Result<Self::Ok, ServerError> {
get_eth_transaction_by_hash(ctx, tx_hash, Some(limit)).await
get_eth_transaction_by_hash(&ctx, &tx_hash, Some(limit)).await
}
}

async fn get_eth_transaction_by_hash(
ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
tx_hash: EthHash,
ctx: &Ctx<impl Blockstore + Send + Sync + 'static>,
tx_hash: &EthHash,
limit: Option<ChainEpoch>,
) -> Result<Option<ApiEthTx>, ServerError> {
let message_cid = ctx.chain_store().get_mapping(&tx_hash)?.unwrap_or_else(|| {
let message_cid = ctx.chain_store().get_mapping(tx_hash)?.unwrap_or_else(|| {
tracing::debug!(
"could not find transaction hash {} in Ethereum mapping",
tx_hash
Expand All @@ -2285,7 +2285,7 @@ async fn get_eth_transaction_by_hash(
return_dec: ipld,
};

if let Ok(tx) = new_eth_tx_from_message_lookup(&ctx, &message_lookup, None) {
if let Ok(tx) = new_eth_tx_from_message_lookup(ctx, &message_lookup, None) {
return Ok(Some(tx));
}
}
Expand Down Expand Up @@ -2861,58 +2861,86 @@ impl RpcMethod<1> for EthTraceBlock {
ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
(block_param,): Self::Params,
) -> Result<Self::Ok, ServerError> {
let ts = tipset_by_ext_block_number_or_hash(ctx.chain_store(), block_param)?;

let (state_root, trace) = ctx.state_manager.execution_trace(&ts)?;

let state = StateTree::new_from_root(ctx.store_owned(), &state_root)?;

let cid = ts.key().cid()?;

let block_hash: EthHash = cid.into();
trace_block(ctx, block_param).await
}
}

let mut all_traces = vec![];
let mut msg_idx = 0;
for ir in trace.into_iter() {
// ignore messages from system actor
if ir.msg.from == system::ADDRESS.into() {
continue;
async fn trace_block<B: Blockstore + Send + Sync + 'static>(
ctx: Ctx<B>,
block_param: ExtBlockNumberOrHash,
) -> Result<Vec<EthBlockTrace>, ServerError> {
let ts = tipset_by_ext_block_number_or_hash(ctx.chain_store(), block_param)?;
let (state_root, trace) = ctx.state_manager.execution_trace(&ts)?;
let state = StateTree::new_from_root(ctx.store_owned(), &state_root)?;
let cid = ts.key().cid()?;
let block_hash: EthHash = cid.into();
let mut all_traces = vec![];
let mut msg_idx = 0;
for ir in trace.into_iter() {
// ignore messages from system actor
if ir.msg.from == system::ADDRESS.into() {
continue;
}
msg_idx += 1;
let tx_hash = EthGetTransactionHashByCid::handle(ctx.clone(), (ir.msg_cid,)).await?;
let tx_hash = tx_hash
.with_context(|| format!("cannot find transaction hash for cid {}", ir.msg_cid))?;
let mut env = trace::base_environment(&state, &ir.msg.from)
.map_err(|e| format!("when processing message {}: {}", ir.msg_cid, e))?;
if let Some(execution_trace) = ir.execution_trace {
trace::build_traces(&mut env, &[], execution_trace)?;
for trace in env.traces {
all_traces.push(EthBlockTrace {
trace: EthTrace {
r#type: trace.r#type,
subtraces: trace.subtraces,
trace_address: trace.trace_address,
action: trace.action,
result: trace.result,
error: trace.error,
},
block_hash: block_hash.clone(),
block_number: ts.epoch(),
transaction_hash: tx_hash.clone(),
transaction_position: msg_idx as i64,
});
}
}
}
Ok(all_traces)
}

msg_idx += 1;

let tx_hash = EthGetTransactionHashByCid::handle(ctx.clone(), (ir.msg_cid,)).await?;

let tx_hash = tx_hash
.with_context(|| format!("cannot find transaction hash for cid {}", ir.msg_cid))?;

let mut env = trace::base_environment(&state, &ir.msg.from)
.map_err(|e| format!("when processing message {}: {}", ir.msg_cid, e))?;

if let Some(execution_trace) = ir.execution_trace {
trace::build_traces(&mut env, &[], execution_trace)?;
pub enum EthTraceTransaction {}
impl RpcMethod<1> for EthTraceTransaction {
const NAME: &'static str = "Filecoin.EthTraceTransaction";
const NAME_ALIAS: Option<&'static str> = Some("trace_transaction");
const N_REQUIRED_PARAMS: usize = 1;
const PARAM_NAMES: [&'static str; 1] = ["txhash"];
const API_PATHS: ApiPaths = ApiPaths::V1;
const PERMISSION: Permission = Permission::Read;
const DESCRIPTION: Option<&'static str> =
Some("Returns the traces for a specific transaction.");

for trace in env.traces {
all_traces.push(EthBlockTrace {
trace: EthTrace {
r#type: trace.r#type,
subtraces: trace.subtraces,
trace_address: trace.trace_address,
action: trace.action,
result: trace.result,
error: trace.error,
},

block_hash: block_hash.clone(),
block_number: ts.epoch(),
transaction_hash: tx_hash.clone(),
transaction_position: msg_idx as i64,
});
}
}
}
type Params = (String,);
type Ok = Vec<EthBlockTrace>;
async fn handle(
ctx: Ctx<impl Blockstore + Send + Sync + 'static>,
(tx_hash,): Self::Params,
) -> Result<Self::Ok, ServerError> {
let eth_hash = EthHash::from_str(&tx_hash)?;
let eth_txn = get_eth_transaction_by_hash(&ctx, &eth_hash, None)
.await?
.ok_or(ServerError::internal_error("transaction not found", None))?;

Ok(all_traces)
let traces = trace_block(
ctx,
ExtBlockNumberOrHash::from_block_number(eth_txn.block_number.0 as i64),
)
.await?
.into_iter()
.filter(|trace| trace.transaction_hash == eth_hash)
.collect();
Ok(traces)
}
}

Expand Down
1 change: 1 addition & 0 deletions src/rpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ macro_rules! for_each_rpc_method {
$callback!($crate::rpc::eth::EthUninstallFilter);
$callback!($crate::rpc::eth::EthSyncing);
$callback!($crate::rpc::eth::EthTraceBlock);
$callback!($crate::rpc::eth::EthTraceTransaction);
$callback!($crate::rpc::eth::EthTraceReplayBlockTransactions);
$callback!($crate::rpc::eth::Web3ClientVersion);
$callback!($crate::rpc::eth::EthSendRawTransaction);
Expand Down
3 changes: 3 additions & 0 deletions src/tool/subcommands/api_cmd/api_compare_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1733,6 +1733,9 @@ fn eth_state_tests_with_tipset<DB: Blockstore>(
tests.push(RpcTest::identity(EthGetTransactionByHashLimited::request(
(tx.hash.clone(), shared_tipset.epoch()),
)?));
tests.push(RpcTest::identity(
EthTraceTransaction::request((tx.hash.to_string(),)).unwrap(),
));
if smsg.message.from.protocol() == Protocol::Delegated
&& smsg.message.to.protocol() == Protocol::Delegated
{
Expand Down
Loading