cfx_rpc_eth_types/
trace.rs

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