cfx_rpc_eth_types/
trace.rs

1pub use alloy_rpc_types_trace::parity::TraceType;
2use alloy_rpc_types_trace::parity::{StateDiff, VmTrace};
3use cfx_parameters::internal_contract_addresses::CROSS_SPACE_CONTRACT_ADDRESS;
4use cfx_parity_trace_types::{
5    Action as VmAction, Outcome, SetAuth as VmSetAuth,
6    SetAuthOutcome as VmSetAuthOutcome,
7};
8use cfx_rpc_cfx_types::{
9    trace::{Action as CfxRpcAction, LocalizedTrace as CfxLocalizedTrace},
10    RpcAddress,
11};
12use cfx_rpc_primitives::Bytes;
13use cfx_rpc_utils::error::jsonrpsee_error_helpers::internal_error;
14use cfx_types::{address_util::AddressUtil, Address, H256, U256};
15use cfx_util_macros::bail;
16use cfx_vm_types::{CallType, CreateType};
17use jsonrpsee::types::ErrorObjectOwned;
18use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer};
19use std::{collections::HashMap, convert::TryFrom, fmt};
20
21/// Create response
22#[derive(Debug, Serialize, Clone, PartialEq, Eq, Default)]
23#[serde(rename_all = "camelCase")]
24pub struct Create {
25    /// Sender
26    from: Address,
27    /// Value
28    value: U256,
29    /// Gas
30    gas: U256,
31    /// Initialization code
32    init: Bytes,
33    /// The create type `CREATE` or `CREATE2`
34    create_type: CreateType,
35}
36
37/// Call response
38#[derive(Debug, Serialize, Clone, PartialEq, Eq, Default)]
39#[serde(rename_all = "camelCase")]
40pub struct Call {
41    /// Sender
42    from: Address,
43    /// Recipient
44    to: Address,
45    /// Transferred Value
46    value: U256,
47    /// Gas
48    gas: U256,
49    /// Input data
50    input: Bytes,
51    /// The type of the call.
52    call_type: CallType,
53}
54
55/// Represents a _selfdestruct_ action fka `suicide`.
56#[derive(
57    Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Default,
58)]
59#[serde(rename_all = "camelCase")]
60pub struct SelfDestructAction {
61    /// destroyed/suicided address.
62    pub address: Address,
63    /// Balance of the contract just before it was destroyed.
64    pub balance: U256,
65    /// destroyed contract heir.
66    pub refund_address: Address,
67}
68
69/// Action
70#[derive(Debug, Clone, PartialEq, Eq)]
71pub enum Action {
72    /// Call
73    Call(Call),
74    /// Create
75    Create(Create),
76    /// SelfDestruct
77    SelfDestruct(SelfDestructAction),
78    /* TODO: Support Reward */
79}
80
81impl Default for Action {
82    fn default() -> Self { Action::Call(Default::default()) }
83}
84
85impl Action {
86    pub fn gas(&self) -> Option<U256> {
87        match self {
88            Action::Call(ref call) => Some(call.gas),
89            Action::Create(ref create) => Some(create.gas),
90            Action::SelfDestruct(_) => None,
91        }
92    }
93}
94
95impl TryFrom<VmAction> for Action {
96    type Error = String;
97
98    fn try_from(cfx_action: VmAction) -> Result<Self, String> {
99        match cfx_action {
100            VmAction::Call(call) => Ok(Action::Call(Call {
101                from: call.from,
102                to: call.to,
103                value: call.value,
104                gas: call.gas,
105                input: call.input.into(),
106                call_type: call.call_type,
107            })),
108            VmAction::Create(create) => Ok(Action::Create(Create {
109                from: create.from,
110                value: create.value,
111                gas: create.gas,
112                init: create.init.into(),
113                create_type: create.create_type,
114            })),
115            VmAction::SelfDestruct(selfdestruct) => {
116                Ok(Action::SelfDestruct(SelfDestructAction {
117                    address: selfdestruct.address,
118                    balance: selfdestruct.balance,
119                    refund_address: selfdestruct.refund_address,
120                }))
121            }
122            action => {
123                bail!("unsupported action in eth space: {:?}", action);
124            }
125        }
126    }
127}
128
129/// Call Result
130#[derive(Debug, Serialize, Clone, PartialEq, Eq, Default)]
131#[serde(rename_all = "camelCase")]
132pub struct CallResult {
133    /// Gas used
134    gas_used: U256,
135    /// Output bytes
136    output: Bytes,
137}
138
139/// Craete Result
140#[derive(Debug, Serialize, Clone, PartialEq, Eq, Default)]
141#[serde(rename_all = "camelCase")]
142pub struct CreateResult {
143    /// Gas used
144    gas_used: U256,
145    /// Code
146    code: Bytes,
147    /// Assigned address
148    address: Address,
149}
150
151/// Response
152#[derive(Debug, Clone, PartialEq, Eq, Default)]
153pub enum ActionResult {
154    /// Call
155    Call(CallResult),
156    /// Create
157    Create(CreateResult),
158    /// None
159    #[default]
160    None,
161}
162
163/// Trace
164#[derive(Debug, Clone)]
165pub struct LocalizedTrace {
166    /// Action
167    pub action: Action,
168    /// Result
169    pub result: ActionResult,
170    /// The error message if the transaction failed.
171    pub error: Option<String>,
172    /// Trace address
173    pub trace_address: Vec<usize>,
174    /// Subtraces
175    pub subtraces: usize,
176    /// Transaction position
177    pub transaction_position: usize,
178    /// Transaction hash
179    pub transaction_hash: H256,
180    /// Block Number
181    pub block_number: u64,
182    /// Block Hash
183    pub block_hash: H256,
184    /// Valid
185    pub valid: bool,
186}
187
188impl Serialize for LocalizedTrace {
189    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
190    where S: Serializer {
191        let mut struc = serializer.serialize_struct("LocalizedTrace", 9)?;
192        match self.action {
193            Action::Call(ref call) => {
194                struc.serialize_field("type", "call")?;
195                struc.serialize_field("action", call)?;
196            }
197            Action::Create(ref create) => {
198                struc.serialize_field("type", "create")?;
199                struc.serialize_field("action", create)?;
200            }
201            Action::SelfDestruct(ref selfdestruct) => {
202                struc.serialize_field("type", "suicide")?;
203                struc.serialize_field("action", selfdestruct)?;
204            }
205        }
206
207        match self.result {
208            ActionResult::Call(ref call) => {
209                struc.serialize_field("result", call)?
210            }
211            ActionResult::Create(ref create) => {
212                struc.serialize_field("result", create)?
213            }
214            ActionResult::None => {
215                struc.serialize_field("result", &None as &Option<u8>)?
216            }
217        }
218
219        if let Some(error) = &self.error {
220            struc.serialize_field("error", error)?;
221        }
222
223        struc.serialize_field("traceAddress", &self.trace_address)?;
224        struc.serialize_field("subtraces", &self.subtraces)?;
225        struc.serialize_field(
226            "transactionPosition",
227            &self.transaction_position,
228        )?;
229        struc.serialize_field("transactionHash", &self.transaction_hash)?;
230        struc.serialize_field("blockNumber", &self.block_number)?;
231        struc.serialize_field("blockHash", &self.block_hash)?;
232        struc.serialize_field("valid", &self.valid)?;
233
234        struc.end()
235    }
236}
237
238impl LocalizedTrace {
239    pub fn set_result(
240        &mut self, result: Option<VmAction>,
241    ) -> Result<(), ErrorObjectOwned> {
242        if !matches!(self.result, ActionResult::None) {
243            // One action matches exactly one result.
244            bail!(internal_error());
245        }
246        if result.is_none() {
247            // If the result is None, it means the action has no result.
248            self.result = ActionResult::None;
249            return Ok(());
250        }
251        let result = result.unwrap();
252        match result {
253            VmAction::CallResult(call_result) => {
254                if !matches!(self.action, Action::Call(_)) {
255                    bail!(internal_error());
256                }
257                let gas =
258                    self.action.gas().expect("call action should have gas");
259                let gas_used = gas - call_result.gas_left;
260                self.result = ActionResult::Call(CallResult {
261                    gas_used,
262                    output: call_result.return_data.clone().into(),
263                });
264                match call_result.outcome {
265                    Outcome::Reverted => {
266                        self.error = Some(TraceError::Reverted.to_string());
267                    }
268                    Outcome::Fail => {
269                        self.error = Some(
270                            TraceError::Error(call_result.return_data.into())
271                                .to_string(),
272                        );
273                    }
274                    _ => {}
275                }
276            }
277            VmAction::CreateResult(create_result) => {
278                if !matches!(self.action, Action::Create(_)) {
279                    bail!(internal_error());
280                }
281                // FIXME(lpl): Check if `return_data` is `code`.
282                let gas =
283                    self.action.gas().expect("call action should have gas");
284                let gas_used = gas - create_result.gas_left;
285                self.result = ActionResult::Create(CreateResult {
286                    gas_used,
287                    code: create_result.return_data.clone().into(),
288                    address: create_result.addr,
289                });
290                match create_result.outcome {
291                    Outcome::Reverted => {
292                        self.error = Some(TraceError::Reverted.to_string());
293                    }
294                    Outcome::Fail => {
295                        self.error = Some(
296                            TraceError::Error(create_result.return_data.into())
297                                .to_string(),
298                        );
299                    }
300                    _ => {}
301                }
302            }
303            _ => bail!(internal_error()),
304        }
305        Ok(())
306    }
307}
308
309/// Trace
310#[derive(Debug, Clone, PartialEq, Eq, Default)]
311pub struct TransactionTrace {
312    /// Trace address
313    trace_address: Vec<usize>,
314    /// Subtraces
315    subtraces: usize,
316    /// Action
317    action: Action,
318    /// Result
319    result: ActionResult,
320    /// Error
321    error: Option<String>,
322}
323
324impl From<LocalizedTrace> for TransactionTrace {
325    fn from(local_trace: LocalizedTrace) -> Self {
326        let LocalizedTrace {
327            action,
328            result,
329            error,
330            trace_address,
331            subtraces,
332            transaction_position: _,
333            transaction_hash: _,
334            block_number: _,
335            block_hash: _,
336            valid: _,
337        } = local_trace;
338        TransactionTrace {
339            action,
340            result,
341            error,
342            subtraces,
343            trace_address,
344        }
345    }
346}
347
348impl Serialize for TransactionTrace {
349    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
350    where S: Serializer {
351        let mut struc = serializer.serialize_struct("Trace", 4)?;
352        match self.action {
353            Action::Call(ref call) => {
354                struc.serialize_field("type", "call")?;
355                struc.serialize_field("action", call)?;
356            }
357            Action::Create(ref create) => {
358                struc.serialize_field("type", "create")?;
359                struc.serialize_field("action", create)?;
360            }
361            Action::SelfDestruct(ref selfdestruct) => {
362                struc.serialize_field("type", "suicide")?;
363                struc.serialize_field("action", selfdestruct)?;
364            }
365        }
366
367        match self.result {
368            ActionResult::Call(ref call) => {
369                struc.serialize_field("result", call)?
370            }
371            ActionResult::Create(ref create) => {
372                struc.serialize_field("result", create)?
373            }
374            ActionResult::None => {
375                struc.serialize_field("result", &None as &Option<u8>)?
376            }
377        }
378
379        if let Some(error) = &self.error {
380            struc.serialize_field("error", error)?;
381        }
382
383        struc.serialize_field("traceAddress", &self.trace_address)?;
384        struc.serialize_field("subtraces", &self.subtraces)?;
385
386        struc.end()
387    }
388}
389
390/// The Outcome of a traced transaction with optional settings
391#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
392#[serde(rename_all = "camelCase")]
393pub struct TraceResults {
394    /// Output of the trace
395    pub output: Bytes,
396    /// Enabled if [TraceType::StateDiff] is provided
397    pub state_diff: Option<StateDiff>,
398    /// Enabled if [TraceType::Trace] is provided, otherwise an empty vec
399    #[serde(default)]
400    pub trace: Vec<TransactionTrace>,
401    /// Enabled if [TraceType::VmTrace] is provided
402    pub vm_trace: Option<VmTrace>,
403}
404
405#[derive(Debug, Clone)]
406pub enum TraceError {
407    /// Execution has been reverted with REVERT instruction.
408    Reverted,
409    /// Other errors with error message encoded.
410    Error(Bytes),
411}
412
413impl fmt::Display for TraceError {
414    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
415        let message = match &self {
416            TraceError::Reverted => "Reverted",
417            // error bytes are constructed from `format`, so this should
418            // succeed.
419            TraceError::Error(b) => {
420                std::str::from_utf8(&b.0).map_err(|_| fmt::Error)?
421            }
422        };
423        message.fmt(f)
424    }
425}
426
427#[derive(Debug, Clone, PartialEq, Serialize)]
428#[serde(rename_all = "camelCase")]
429pub struct SetAuth {
430    /// The address of the impl.
431    pub address: Address,
432    pub chain_id: U256,
433    pub nonce: U256,
434    pub author: Option<Address>,
435}
436
437/// Trace
438#[derive(Debug, Clone, PartialEq, Serialize)]
439#[serde(rename_all = "camelCase")]
440pub struct LocalizedSetAuthTrace {
441    /// Action
442    pub action: SetAuth,
443    /// Result
444    pub result: VmSetAuthOutcome,
445    /// Transaction position
446    pub transaction_position: usize,
447    /// Transaction hash
448    pub transaction_hash: H256,
449    /// Block Number
450    pub block_number: u64,
451    /// Block Hash
452    pub block_hash: H256,
453}
454
455impl LocalizedSetAuthTrace {
456    pub fn new(
457        vm_action: &VmSetAuth, transaction_position: usize,
458        transaction_hash: H256, block_number: u64, block_hash: H256,
459    ) -> Self {
460        let action = SetAuth {
461            address: vm_action.address,
462            chain_id: vm_action.chain_id,
463            nonce: vm_action.nonce,
464            author: vm_action.author,
465        };
466        Self {
467            action,
468            result: vm_action.outcome,
469            transaction_position,
470            transaction_hash,
471            block_number,
472            block_hash,
473        }
474    }
475}
476
477#[derive(Debug, Serialize, Clone)]
478#[serde(rename_all = "camelCase")]
479pub struct EpochTrace {
480    cfx_traces: Vec<CfxLocalizedTrace>,
481    eth_traces: Vec<LocalizedTrace>,
482    mirror_address_map: HashMap<Address, RpcAddress>,
483}
484
485impl EpochTrace {
486    pub fn new(
487        cfx_traces: Vec<CfxLocalizedTrace>, eth_traces: Vec<LocalizedTrace>,
488    ) -> Self {
489        let mut mirror_address_map = HashMap::new();
490        for t in &cfx_traces {
491            if let CfxRpcAction::Call(action) = &t.trace.action {
492                if action.to.hex_address == CROSS_SPACE_CONTRACT_ADDRESS {
493                    mirror_address_map.insert(
494                        action.from.hex_address.evm_map().address,
495                        action.from.clone(),
496                    );
497                }
498            }
499        }
500        Self {
501            cfx_traces,
502            eth_traces,
503            mirror_address_map,
504        }
505    }
506}