1use super::types::{EthAddress, EthBytes, EthCallTraceAction, EthTrace, TraceAction, TraceResult};
5use super::utils::{decode_params, decode_return};
6use super::{
7 EthCallTraceResult, EthCreateTraceAction, EthCreateTraceResult, decode_payload,
8 encode_filecoin_params_as_abi, encode_filecoin_returns_as_abi,
9};
10use crate::eth::{EAMMethod, EVMMethod};
11use crate::rpc::methods::eth::lookup_eth_address;
12use crate::rpc::methods::state::ExecutionTrace;
13use crate::rpc::state::ActorTrace;
14use crate::shim::fvm_shared_latest::METHOD_CONSTRUCTOR;
15use crate::shim::{actors::is_evm_actor, address::Address, error::ExitCode, state_tree::StateTree};
16use fil_actor_eam_state::v12 as eam12;
17use fil_actor_evm_state::v15 as evm12;
18use fil_actor_init_state::v12::ExecReturn;
19use fil_actor_init_state::v15::Method as InitMethod;
20use fvm_ipld_blockstore::Blockstore;
21
22use anyhow::{Context, bail};
23use num::FromPrimitive;
24use tracing::debug;
25
26#[derive(Default)]
27pub struct Environment {
28 caller: EthAddress,
29 is_evm: bool,
30 subtrace_count: i64,
31 pub traces: Vec<EthTrace>,
32 last_byte_code: Option<EthAddress>,
33}
34
35pub fn base_environment<BS: Blockstore + Send + Sync>(
36 state: &StateTree<BS>,
37 from: &Address,
38) -> anyhow::Result<Environment> {
39 let sender = lookup_eth_address(from, state)?
40 .with_context(|| format!("top-level message sender {from} s could not be found"))?;
41 Ok(Environment {
42 caller: sender,
43 ..Environment::default()
44 })
45}
46
47fn trace_to_address(trace: &ActorTrace) -> EthAddress {
48 if let Some(addr) = trace.state.delegated_address
49 && let Ok(eth_addr) = EthAddress::from_filecoin_address(&addr.into())
50 {
51 return eth_addr;
52 }
53 EthAddress::from_actor_id(trace.id)
54}
55
56fn trace_is_evm_or_eam(trace: &ExecutionTrace) -> bool {
58 if let Some(invoked_actor) = &trace.invoked_actor {
59 is_evm_actor(&invoked_actor.state.code)
60 || invoked_actor.id != Address::ETHEREUM_ACCOUNT_MANAGER_ACTOR.id().unwrap()
61 } else {
62 false
63 }
64}
65
66fn trace_err_msg(trace: &ExecutionTrace) -> Option<String> {
68 let code = trace.msg_rct.exit_code;
69
70 if code.is_success() {
71 return None;
72 }
73
74 if code == ExitCode::SYS_OUT_OF_GAS {
76 return Some("out of gas".into());
77 }
78
79 if code < ExitCode::FIRST_ACTOR_ERROR_CODE.into() {
81 return Some(format!("vm error: {code}"));
82 }
83
84 if trace_is_evm_or_eam(trace) {
86 match code.into() {
87 evm12::EVM_CONTRACT_REVERTED => return Some("Reverted".into()), evm12::EVM_CONTRACT_INVALID_INSTRUCTION => return Some("invalid instruction".into()),
89 evm12::EVM_CONTRACT_UNDEFINED_INSTRUCTION => {
90 return Some("undefined instruction".into());
91 }
92 evm12::EVM_CONTRACT_STACK_UNDERFLOW => return Some("stack underflow".into()),
93 evm12::EVM_CONTRACT_STACK_OVERFLOW => return Some("stack overflow".into()),
94 evm12::EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS => {
95 return Some("illegal memory access".into());
96 }
97 evm12::EVM_CONTRACT_BAD_JUMPDEST => return Some("invalid jump destination".into()),
98 evm12::EVM_CONTRACT_SELFDESTRUCT_FAILED => return Some("self destruct failed".into()),
99 _ => (),
100 }
101 }
102 Some(format!("actor error: {code}"))
104}
105
106pub fn build_traces(
108 env: &mut Environment,
109 address: &[i64],
110 trace: ExecutionTrace,
111) -> anyhow::Result<()> {
112 let (trace, recurse_into) = build_trace(env, address, trace)?;
113
114 let last_trace_idx = if let Some(trace) = trace {
115 let len = env.traces.len();
116 env.traces.push(trace);
117 env.subtrace_count += 1;
118 Some(len)
119 } else {
120 None
121 };
122
123 let (recurse_into, invoked_actor) = if let Some(trace) = recurse_into {
125 if let Some(invoked_actor) = &trace.invoked_actor {
126 let invoked_actor = invoked_actor.clone();
127 (trace, invoked_actor)
128 } else {
129 return Ok(());
130 }
131 } else {
132 return Ok(());
133 };
134
135 let mut sub_env = Environment {
136 caller: trace_to_address(&invoked_actor),
137 is_evm: is_evm_actor(&invoked_actor.state.code),
138 traces: env.traces.clone(),
139 ..Environment::default()
140 };
141 for subcall in recurse_into.subcalls.into_iter() {
142 let mut new_address = address.to_vec();
143 new_address.push(sub_env.subtrace_count);
144 build_traces(&mut sub_env, &new_address, subcall)?;
145 }
146 env.traces = sub_env.traces;
147 if let Some(idx) = last_trace_idx {
148 env.traces.get_mut(idx).expect("Infallible").subtraces = sub_env.subtrace_count;
149 }
150
151 Ok(())
152}
153
154fn build_trace(
158 env: &mut Environment,
159 address: &[i64],
160 trace: ExecutionTrace,
161) -> anyhow::Result<(Option<EthTrace>, Option<ExecutionTrace>)> {
162 if !address.is_empty()
181 && Into::<ExitCode>::into(trace.msg_rct.exit_code) == ExitCode::SYS_INSUFFICIENT_FUNDS
182 {
183 return Ok((None, None));
184 }
185
186 if trace.invoked_actor.is_none() {
190 return Ok((None, None));
191 }
192
193 let method = EVMMethod::from_u64(trace.msg.method);
199 if let Some(EVMMethod::InvokeContract) = method {
200 let (trace, exec_trace) = trace_evm_call(env, address, trace)?;
201 return Ok((Some(trace), Some(exec_trace)));
202 }
203
204 match trace.msg.to {
206 Address::INIT_ACTOR => {
207 let method = InitMethod::from_u64(trace.msg.method);
208 match method {
209 Some(InitMethod::Exec) | Some(InitMethod::Exec4) => {
210 return trace_native_create(env, address, &trace);
211 }
212 _ => (),
213 }
214 }
215 Address::ETHEREUM_ACCOUNT_MANAGER_ACTOR => {
216 let method = EAMMethod::from_u64(trace.msg.method);
217 match method {
218 Some(EAMMethod::Create)
219 | Some(EAMMethod::Create2)
220 | Some(EAMMethod::CreateExternal) => {
221 return trace_eth_create(env, address, &trace);
222 }
223 _ => (),
224 }
225 }
226 _ => (),
227 }
228
229 if env.is_evm && trace.msg.method > 0 && trace.msg.method < 1024 {
239 return trace_evm_private(env, address, &trace);
240 }
241
242 Ok((Some(trace_native_call(env, address, &trace)?), Some(trace)))
243}
244
245fn trace_call(
247 env: &mut Environment,
248 address: &[i64],
249 trace: &ExecutionTrace,
250 input: EthBytes,
251 output: EthBytes,
252) -> anyhow::Result<EthTrace> {
253 if let Some(invoked_actor) = &trace.invoked_actor {
254 let to = trace_to_address(invoked_actor);
255 let call_type: String = if trace.msg.read_only.unwrap_or_default() {
256 "staticcall"
257 } else {
258 "call"
259 }
260 .into();
261
262 Ok(EthTrace {
263 r#type: "call".into(),
264 action: TraceAction::Call(EthCallTraceAction {
265 call_type,
266 from: env.caller.clone(),
267 to: Some(to),
268 gas: trace.msg.gas_limit.unwrap_or_default().into(),
269 value: trace.msg.value.clone().into(),
270 input,
271 }),
272 result: TraceResult::Call(EthCallTraceResult {
273 gas_used: trace.sum_gas().total_gas.into(),
274 output,
275 }),
276 trace_address: Vec::from(address),
277 error: trace_err_msg(trace),
278 ..EthTrace::default()
279 })
280 } else {
281 bail!("no invoked actor")
282 }
283}
284
285fn trace_native_call(
287 env: &mut Environment,
288 address: &[i64],
289 trace: &ExecutionTrace,
290) -> anyhow::Result<EthTrace> {
291 trace_call(
292 env,
293 address,
294 trace,
295 encode_filecoin_params_as_abi(trace.msg.method, trace.msg.params_codec, &trace.msg.params)?,
296 EthBytes(encode_filecoin_returns_as_abi(
297 trace.msg_rct.exit_code.value().into(),
298 trace.msg_rct.return_codec,
299 &trace.msg_rct.r#return,
300 )),
301 )
302}
303
304fn trace_evm_call(
307 env: &mut Environment,
308 address: &[i64],
309 trace: ExecutionTrace,
310) -> anyhow::Result<(EthTrace, ExecutionTrace)> {
311 let input = match decode_payload(&trace.msg.params, trace.msg.params_codec) {
312 Ok(value) => value,
313 Err(err) => {
314 debug!("failed to decode contract invocation payload: {err}");
315 return Ok((trace_native_call(env, address, &trace)?, trace));
316 }
317 };
318 let output = match decode_payload(&trace.msg_rct.r#return, trace.msg_rct.return_codec) {
319 Ok(value) => value,
320 Err(err) => {
321 debug!("failed to decode contract invocation return: {err}");
322 return Ok((trace_native_call(env, address, &trace)?, trace));
323 }
324 };
325 Ok((trace_call(env, address, &trace, input, output)?, trace))
326}
327
328fn trace_native_create(
332 env: &mut Environment,
333 address: &[i64],
334 trace: &ExecutionTrace,
335) -> anyhow::Result<(Option<EthTrace>, Option<ExecutionTrace>)> {
336 if trace.msg.read_only.unwrap_or_default() {
337 return Ok((None, None));
342 }
343
344 let sub_trace = trace
345 .subcalls
346 .iter()
347 .find(|c| c.msg.method == METHOD_CONSTRUCTOR);
348
349 let sub_trace = if let Some(sub_trace) = sub_trace {
350 sub_trace
351 } else {
352 if trace.msg_rct.exit_code.is_success() {
356 bail!("successful Exec/Exec4 call failed to call a constructor");
357 }
358 return Ok((None, None));
368 };
369
370 if trace.msg.method == (InitMethod::Exec4 as u64) {
375 bail!("direct call to Exec4 successfully called a constructor!");
376 }
377
378 let mut output = EthBytes::default();
379 let mut create_addr = EthAddress::default();
380 if trace.msg_rct.exit_code.is_success() {
381 output = EthBytes(vec![0xFE]);
386
387 let init_return: ExecReturn = decode_return(&trace.msg_rct)?;
389 let actor_id = init_return.id_address.id()?;
390 let eth_addr = EthAddress::from_actor_id(actor_id);
391 create_addr = eth_addr;
392 }
393
394 Ok((
395 Some(EthTrace {
396 r#type: "create".into(),
397 action: TraceAction::Create(EthCreateTraceAction {
398 from: env.caller.clone(),
399 gas: trace.msg.gas_limit.unwrap_or_default().into(),
400 value: trace.msg.value.clone().into(),
401 init: EthBytes(vec![0xFE]),
405 }),
406 result: TraceResult::Create(EthCreateTraceResult {
407 gas_used: trace.sum_gas().total_gas.into(),
408 address: Some(create_addr),
409 code: output,
410 }),
411 trace_address: Vec::from(address),
412 error: trace_err_msg(trace),
413 ..EthTrace::default()
414 }),
415 Some(sub_trace.clone()),
416 ))
417}
418
419fn decode_create_via_eam(trace: &ExecutionTrace) -> anyhow::Result<(Vec<u8>, EthAddress)> {
423 let init_code = match EAMMethod::from_u64(trace.msg.method) {
424 Some(EAMMethod::Create) => {
425 let params = decode_params::<eam12::CreateParams>(&trace.msg)?;
426 params.initcode
427 }
428 Some(EAMMethod::Create2) => {
429 let params = decode_params::<eam12::Create2Params>(&trace.msg)?;
430 params.initcode
431 }
432 Some(EAMMethod::CreateExternal) => {
433 decode_payload(&trace.msg.params, trace.msg.params_codec)?.into()
434 }
435 _ => bail!("unexpected CREATE method {}", trace.msg.method),
436 };
437 let ret = decode_return::<eam12::CreateReturn>(&trace.msg_rct)?;
438
439 Ok((init_code, ret.eth_address.0.into()))
440}
441
442fn trace_eth_create(
445 env: &mut Environment,
446 address: &[i64],
447 trace: &ExecutionTrace,
448) -> anyhow::Result<(Option<EthTrace>, Option<ExecutionTrace>)> {
449 if trace.msg.read_only.unwrap_or_default() {
451 return Ok((None, None));
452 }
453
454 let sub_trace = trace
456 .subcalls
457 .iter()
458 .filter_map(|et| {
459 if et.msg.to == Address::INIT_ACTOR {
460 et.subcalls
461 .iter()
462 .find(|et| et.msg.method == METHOD_CONSTRUCTOR)
463 } else {
464 match EVMMethod::from_u64(et.msg.method) {
465 Some(EVMMethod::Resurrect) => Some(et),
466 _ => None,
467 }
468 }
469 })
470 .next();
471
472 let sub_trace = if let Some(sub_trace) = sub_trace {
474 sub_trace
475 } else {
476 if trace.msg_rct.exit_code.is_success() {
477 bail!("successful Create/Create2 call failed to call a constructor");
478 }
479 return Ok((None, None));
480 };
481
482 let (init_code, create_addr) = decode_create_via_eam(trace)?;
484
485 let output = match trace.msg_rct.exit_code.value() {
487 0 => {
488 EthBytes(vec![0xFE])
494 }
495 33 => {
496 decode_payload(&sub_trace.msg_rct.r#return, sub_trace.msg_rct.return_codec)?
500 }
501 _ => EthBytes::default(),
502 };
503
504 Ok((
505 Some(EthTrace {
506 r#type: "create".into(),
507 action: TraceAction::Create(EthCreateTraceAction {
508 from: env.caller.clone(),
509 gas: trace.msg.gas_limit.unwrap_or_default().into(),
510 value: trace.msg.value.clone().into(),
511 init: init_code.into(),
512 }),
513 result: TraceResult::Create(EthCreateTraceResult {
514 gas_used: trace.sum_gas().total_gas.into(),
515 address: Some(create_addr),
516 code: output,
517 }),
518 trace_address: Vec::from(address),
519 error: trace_err_msg(trace),
520 ..EthTrace::default()
521 }),
522 Some(sub_trace.clone()),
523 ))
524}
525
526fn trace_evm_private(
529 env: &mut Environment,
530 address: &[i64],
531 trace: &ExecutionTrace,
532) -> anyhow::Result<(Option<EthTrace>, Option<ExecutionTrace>)> {
533 match EVMMethod::from_u64(trace.msg.method) {
548 Some(EVMMethod::GetBytecode) => {
549 env.last_byte_code = None;
553 if trace.msg_rct.exit_code.is_success()
554 && let Option::Some(actor_trace) = &trace.invoked_actor
555 {
556 let to = trace_to_address(actor_trace);
557 env.last_byte_code = Some(to);
558 }
559 Ok((None, None))
560 }
561 Some(EVMMethod::InvokeContractDelegate) => {
562 if env.last_byte_code.is_none() {
574 bail!("unknown bytecode for delegate call");
575 }
576
577 if let Option::Some(actor_trace) = &trace.invoked_actor {
578 let to = trace_to_address(actor_trace);
579 if env.caller != to {
580 bail!(
581 "delegate-call not from address to self: {:?} != {:?}",
582 env.caller,
583 to
584 );
585 }
586 }
587
588 let dp = decode_params::<evm12::DelegateCallParams>(&trace.msg)?;
589
590 let output = decode_payload(&trace.msg_rct.r#return, trace.msg_rct.return_codec)
591 .map_err(|e| anyhow::anyhow!("failed to decode delegate-call return: {}", e))?;
592
593 Ok((
594 Some(EthTrace {
595 r#type: "call".into(),
596 action: TraceAction::Call(EthCallTraceAction {
597 call_type: "delegatecall".into(),
598 from: env.caller.clone(),
599 to: env.last_byte_code.clone(),
600 gas: trace.msg.gas_limit.unwrap_or_default().into(),
601 value: trace.msg.value.clone().into(),
602 input: dp.input.into(),
603 }),
604 result: TraceResult::Call(EthCallTraceResult {
605 gas_used: trace.sum_gas().total_gas.into(),
606 output,
607 }),
608 trace_address: Vec::from(address),
609 error: trace_err_msg(trace),
610 ..EthTrace::default()
611 }),
612 Some(trace.clone()),
613 ))
614 }
615 _ => {
616 Ok((None, None))
619 }
620 }
621}