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