cfx_rpc_cfx_types/
trace.rs

1// Copyright 2020 Conflux Foundation. All rights reserved.
2// Conflux is free software and distributed under GNU General Public License.
3// See http://www.gnu.org/licenses/
4
5use crate::RpcAddress;
6use cfx_addr::Network;
7use cfx_parity_trace_types::{
8    Action as VmAction, ActionType as VmActionType, BlockExecTraces,
9    Call as VmCall, CallResult as VmCallResult, Create as VmCreate,
10    CreateResult as VmCreateResult, ExecTrace,
11    InternalTransferAction as VmInternalTransferAction,
12    LocalizedTrace as PrimitiveLocalizedTrace, Outcome,
13    SelfDestructAction as VmSelfDestruction, SetAuth as VmSetAuth,
14    SetAuthOutcome, TransactionExecTraces,
15};
16use cfx_rpc_primitives::Bytes;
17use cfx_types::{Space, H256, U256, U64};
18use cfx_vm_types::{CallType, CreateType};
19use primitives::SignedTransaction;
20use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer};
21use std::sync::Arc;
22use strum_macros::EnumDiscriminants;
23
24#[derive(Debug, Clone, PartialEq, EnumDiscriminants)]
25#[strum_discriminants(name(ActionType))]
26#[strum_discriminants(derive(Hash, Serialize, Deserialize))]
27#[strum_discriminants(serde(rename_all = "snake_case", deny_unknown_fields))]
28pub enum Action {
29    Call(Call),
30    Create(Create),
31    CallResult(CallResult),
32    CreateResult(CreateResult),
33    InternalTransferAction(InternalTransferAction),
34    SetAuth(SetAuth),
35    SelfDestruct(SelfDestructAction),
36}
37
38impl Action {
39    pub fn try_from(
40        action: VmAction, network: Network,
41    ) -> Result<Self, String> {
42        Ok(match action {
43            VmAction::Call(x) => Action::Call(Call::try_from(x, network)?),
44            VmAction::Create(x) => {
45                Action::Create(Create::try_from(x, network)?)
46            }
47            VmAction::CallResult(x) => Action::CallResult(x.into()),
48            VmAction::CreateResult(x) => {
49                Action::CreateResult(CreateResult::try_from(x, network)?)
50            }
51            VmAction::InternalTransferAction(x) => {
52                Action::InternalTransferAction(
53                    InternalTransferAction::try_from(x, network)?,
54                )
55            }
56            VmAction::SetAuth(action) => {
57                Action::SetAuth(SetAuth::try_from(action, network)?)
58            }
59            VmAction::SelfDestruct(selfdestruct) => Action::SelfDestruct(
60                SelfDestructAction::try_from(selfdestruct, network)?,
61            ),
62        })
63    }
64}
65
66impl Into<VmActionType> for ActionType {
67    fn into(self) -> VmActionType {
68        match self {
69            Self::Call => VmActionType::Call,
70            Self::Create => VmActionType::Create,
71            Self::CallResult => VmActionType::CallResult,
72            Self::CreateResult => VmActionType::CreateResult,
73            Self::InternalTransferAction => {
74                VmActionType::InternalTransferAction
75            }
76            Self::SetAuth => VmActionType::SetAuth,
77            Self::SelfDestruct => VmActionType::SelfDestruct,
78        }
79    }
80}
81
82#[derive(Debug, Clone, PartialEq, Serialize)]
83#[serde(rename_all = "camelCase")]
84pub struct Call {
85    pub space: Space,
86    pub from: RpcAddress,
87    pub to: RpcAddress,
88    pub value: U256,
89    pub gas: U256,
90    pub input: Bytes,
91    pub call_type: CallType,
92}
93
94impl Call {
95    fn try_from(call: VmCall, network: Network) -> Result<Self, String> {
96        Ok(Self {
97            space: call.space,
98            from: RpcAddress::try_from_h160(call.from, network)?,
99            to: RpcAddress::try_from_h160(call.to, network)?,
100            value: call.value,
101            gas: call.gas,
102            input: call.input.into(),
103            call_type: call.call_type,
104        })
105    }
106}
107
108#[derive(Debug, Clone, PartialEq, Serialize)]
109#[serde(rename_all = "camelCase")]
110pub struct CallResult {
111    pub outcome: Outcome,
112    pub gas_left: U256,
113    pub return_data: Bytes,
114}
115
116impl From<VmCallResult> for CallResult {
117    fn from(result: VmCallResult) -> Self {
118        Self {
119            outcome: result.outcome,
120            gas_left: result.gas_left,
121            return_data: result.return_data.into(),
122        }
123    }
124}
125
126#[derive(Debug, Clone, PartialEq, Serialize)]
127#[serde(rename_all = "camelCase")]
128pub struct Create {
129    pub space: Space,
130    pub from: RpcAddress,
131    pub value: U256,
132    pub gas: U256,
133    pub init: Bytes,
134    pub create_type: CreateType,
135}
136
137impl Create {
138    fn try_from(create: VmCreate, network: Network) -> Result<Self, String> {
139        Ok(Self {
140            space: create.space,
141            from: RpcAddress::try_from_h160(create.from, network)?,
142            value: create.value,
143            gas: create.gas,
144            init: create.init.into(),
145            create_type: create.create_type,
146        })
147    }
148}
149
150#[derive(Debug, Clone, PartialEq, Serialize)]
151#[serde(rename_all = "camelCase")]
152pub struct CreateResult {
153    pub outcome: Outcome,
154    pub addr: RpcAddress,
155    pub gas_left: U256,
156    pub return_data: Bytes,
157}
158
159impl CreateResult {
160    fn try_from(
161        result: VmCreateResult, network: Network,
162    ) -> Result<Self, String> {
163        Ok(Self {
164            outcome: result.outcome,
165            addr: RpcAddress::try_from_h160(result.addr, network)?,
166            gas_left: result.gas_left,
167            return_data: result.return_data.into(),
168        })
169    }
170}
171
172#[derive(Debug, Clone, PartialEq, Serialize)]
173#[serde(rename_all = "camelCase")]
174pub struct InternalTransferAction {
175    pub from: RpcAddress,
176    pub from_pocket: String,
177    pub from_space: String,
178    pub to: RpcAddress,
179    pub to_pocket: String,
180    pub to_space: String,
181    pub value: U256,
182}
183
184impl InternalTransferAction {
185    fn try_from(
186        action: VmInternalTransferAction, network: Network,
187    ) -> Result<Self, String> {
188        Ok(Self {
189            from: RpcAddress::try_from_h160(
190                action.from.inner_address_or_default(),
191                network,
192            )?,
193            from_pocket: action.from.pocket().into(),
194            from_space: action.from.space().into(),
195            to: RpcAddress::try_from_h160(
196                action.to.inner_address_or_default(),
197                network,
198            )?,
199            to_pocket: action.to.pocket().into(),
200            to_space: action.to.space().into(),
201            value: action.value,
202        })
203    }
204}
205
206#[derive(Debug, Clone, PartialEq, Serialize)]
207#[serde(rename_all = "camelCase")]
208pub struct SetAuth {
209    pub space: Space,
210    /// The address of the impl.
211    pub address: RpcAddress,
212    pub chain_id: U256,
213    pub nonce: U256,
214    /// The outcome of the create
215    pub outcome: SetAuthOutcome,
216    /// The address of the author.
217    pub author: Option<RpcAddress>,
218}
219
220impl SetAuth {
221    fn try_from(action: VmSetAuth, network: Network) -> Result<Self, String> {
222        let VmSetAuth {
223            space,
224            address,
225            chain_id,
226            nonce,
227            outcome,
228            author,
229        } = action;
230        Ok(Self {
231            space,
232            address: RpcAddress::try_from_h160(address, network)?,
233            chain_id,
234            nonce,
235            outcome,
236            author: match author {
237                Some(a) => Some(RpcAddress::try_from_h160(a, network)?),
238                None => None,
239            },
240        })
241    }
242}
243
244/// Represents a _selfdestruct_ action fka `suicide`.
245#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
246#[serde(rename_all = "camelCase")]
247pub struct SelfDestructAction {
248    // / The space of the contract.
249    pub space: Space,
250    /// destroyed/suicided address.
251    pub address: RpcAddress,
252    /// Balance of the contract just before it was destroyed.
253    pub balance: U256,
254    /// destroyed contract heir.
255    pub refund_address: RpcAddress,
256}
257
258impl SelfDestructAction {
259    fn try_from(
260        action: VmSelfDestruction, network: Network,
261    ) -> Result<Self, String> {
262        let VmSelfDestruction {
263            space,
264            address,
265            balance,
266            refund_address,
267        } = action;
268        Ok(Self {
269            space,
270            address: RpcAddress::try_from_h160(address, network)?,
271            refund_address: RpcAddress::try_from_h160(refund_address, network)?,
272            balance,
273        })
274    }
275}
276
277#[derive(Debug, Serialize)]
278#[serde(rename_all = "camelCase")]
279pub struct LocalizedBlockTrace {
280    pub transaction_traces: Vec<LocalizedTransactionTrace>,
281    /// Epoch hash.
282    pub epoch_hash: H256,
283    /// Epoch number.
284    pub epoch_number: U256,
285    /// Block hash.
286    pub block_hash: H256,
287}
288
289#[derive(Debug, Serialize)]
290#[serde(rename_all = "camelCase")]
291pub struct LocalizedTransactionTrace {
292    pub traces: Vec<Trace>,
293    /// Transaction position.
294    pub transaction_position: U64,
295    /// Signed transaction hash.
296    pub transaction_hash: H256,
297}
298
299#[derive(Debug)]
300pub struct Trace {
301    pub action: Action,
302    pub valid: bool,
303}
304
305impl Serialize for Trace {
306    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
307    where S: Serializer {
308        let mut struc = serializer.serialize_struct("LocalizedTrace", 8)?;
309
310        match self.action {
311            Action::Call(ref call) => {
312                struc.serialize_field("type", "call")?;
313                struc.serialize_field("action", call)?;
314            }
315            Action::Create(ref create) => {
316                struc.serialize_field("type", "create")?;
317                struc.serialize_field("action", create)?;
318            }
319            Action::CallResult(ref call_result) => {
320                struc.serialize_field("type", "call_result")?;
321                struc.serialize_field("action", call_result)?;
322            }
323            Action::CreateResult(ref create_result) => {
324                struc.serialize_field("type", "create_result")?;
325                struc.serialize_field("action", create_result)?;
326            }
327            Action::InternalTransferAction(ref internal_action) => {
328                struc.serialize_field("type", "internal_transfer_action")?;
329                struc.serialize_field("action", internal_action)?;
330            }
331            Action::SetAuth(ref set_auth) => {
332                struc.serialize_field("type", "set_auth")?;
333                struc.serialize_field("action", set_auth)?;
334            }
335            Action::SelfDestruct(ref selfdestruct) => {
336                struc.serialize_field("type", "suicide")?;
337                struc.serialize_field("action", selfdestruct)?;
338            }
339        }
340
341        struc.serialize_field("valid", &self.valid)?;
342        struc.end()
343    }
344}
345
346#[derive(Debug, Serialize)]
347#[serde(rename_all = "camelCase")]
348pub struct LocalizedTrace {
349    #[serde(flatten)]
350    pub trace: Trace,
351    /// Epoch hash.
352    pub epoch_hash: H256,
353    /// Epoch number.
354    pub epoch_number: U256,
355    /// Block hash.
356    pub block_hash: H256,
357    /// Transaction position.
358    pub transaction_position: U64,
359    /// Signed transaction hash.
360    pub transaction_hash: H256,
361}
362
363impl LocalizedTrace {
364    pub fn from(
365        trace: PrimitiveLocalizedTrace, network: Network,
366    ) -> Result<Self, String> {
367        Ok(LocalizedTrace {
368            trace: Trace {
369                action: Action::try_from(trace.action, network)?,
370                valid: trace.valid,
371            },
372            epoch_number: trace.epoch_number,
373            epoch_hash: trace.epoch_hash,
374            block_hash: trace.block_hash,
375            transaction_position: trace.transaction_position,
376            transaction_hash: trace.transaction_hash,
377        })
378    }
379}
380
381impl LocalizedTransactionTrace {
382    pub fn from(
383        traces: TransactionExecTraces, transaction_hash: H256,
384        transaction_position: usize, network: Network,
385    ) -> Result<Self, String> {
386        let traces: Vec<ExecTrace> = traces.into();
387
388        Ok(LocalizedTransactionTrace {
389            traces: traces
390                .into_iter()
391                .map(|t| {
392                    let valid = t.valid;
393                    Action::try_from(t.action, network)
394                        .map(|action| Trace { action, valid })
395                })
396                .collect::<Result<_, _>>()?,
397            transaction_position: transaction_position.into(),
398            transaction_hash,
399        })
400    }
401}
402
403impl LocalizedBlockTrace {
404    pub fn from(
405        traces: BlockExecTraces, block_hash: H256, epoch_hash: H256,
406        epoch_number: u64, transactions: &Vec<Arc<SignedTransaction>>,
407        network: Network,
408    ) -> Result<Self, String> {
409        let traces: Vec<TransactionExecTraces> = traces.into();
410        if traces.len() != transactions.len() {
411            cfx_util_macros::bail!("trace and tx hash list length unmatch!");
412        }
413        let transaction_traces = traces
414            .into_iter()
415            .enumerate()
416            .filter_map(|(tx_pos, t)| match transactions[tx_pos].space() {
417                Space::Native => Some((transactions[tx_pos].hash(), t)),
418                Space::Ethereum => None,
419            })
420            .enumerate()
421            .map(|(rpc_index, (tx_hash, t))| {
422                LocalizedTransactionTrace::from(t, tx_hash, rpc_index, network)
423            })
424            .collect::<Result<_, _>>()?;
425
426        Ok(LocalizedBlockTrace {
427            transaction_traces,
428            epoch_hash,
429            epoch_number: epoch_number.into(),
430            block_hash,
431        })
432    }
433}
434
435#[cfg(test)]
436mod tests {
437    use super::*;
438    use cfx_addr::Network;
439
440    #[test]
441    fn test_localized_trace_serialization() {
442        let localized_trace = LocalizedTrace {
443            trace: Trace {
444                action: Action::Call(Call {
445                    space: Space::Native,
446                    from: RpcAddress::null(Network::Main).unwrap(),
447                    to: RpcAddress::null(Network::Main).unwrap(),
448                    value: U256::from(1000u64),
449                    gas: U256::from(21000u64),
450                    input: Bytes::from(vec![0x60, 0x60, 0x60, 0x40]),
451                    call_type: CallType::Call,
452                }),
453                valid: true,
454            },
455            epoch_hash: Default::default(),
456            epoch_number: Default::default(),
457            block_hash: Default::default(),
458            transaction_position: U64::from(0),
459            transaction_hash: Default::default(),
460        };
461
462        let serialized =
463            serde_json::to_string_pretty(&localized_trace).unwrap();
464
465        let expected = r#"{
466  "type": "call",
467  "action": {
468    "space": "native",
469    "from": "CFX:TYPE.NULL:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0SFBNJM2",
470    "to": "CFX:TYPE.NULL:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0SFBNJM2",
471    "value": "0x3e8",
472    "gas": "0x5208",
473    "input": "0x60606040",
474    "callType": "call"
475  },
476  "valid": true,
477  "epochHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
478  "epochNumber": "0x0",
479  "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
480  "transactionPosition": "0x0",
481  "transactionHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
482}"#;
483        assert_eq!(serialized, expected,);
484    }
485}