cfx_execute_helper/observer/exec_tracer/
phantom_traces.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 super::{
6    action_types::{
7        Action, Call, CallResult, Create, CreateResult, InternalTransferAction,
8        Outcome,
9    },
10    trace_types::{ExecTrace, TransactionExecTraces},
11};
12
13use cfx_executor::{
14    executive_observer::AddressPocket,
15    internal_contract::{is_call_create_sig, is_withdraw_sig},
16};
17use cfx_parameters::internal_contract_addresses::CROSS_SPACE_CONTRACT_ADDRESS;
18use cfx_types::{Address, AddressWithSpace, Space, H256, U256};
19use cfx_vm_types::CallType;
20use solidity_abi::ABIEncodable;
21
22pub fn recover_phantom_trace_for_withdraw(
23    mut tx_traces: impl Iterator<Item = ExecTrace>,
24) -> Result<Vec<TransactionExecTraces>, String> {
25    let trace = match tx_traces.next() {
26        Some(t) => t,
27        None => {
28            error!("Unable to recover phantom trace: no more traces (expected withdraw)");
29            return Err("Unable to recover phantom trace: no more traces (expected withdraw)".into());
30        }
31    };
32
33    match trace.action {
34        Action::InternalTransferAction(InternalTransferAction {
35            from:
36                AddressPocket::Balance(AddressWithSpace {
37                    address: from,
38                    space: Space::Ethereum,
39                }),
40            to:
41                AddressPocket::Balance(AddressWithSpace {
42                    address: _,
43                    space: Space::Native,
44                }),
45            value,
46        }) => {
47            return Ok(vec![TransactionExecTraces(vec![
48                ExecTrace {
49                    action: Action::Call(Call {
50                        space: Space::Ethereum,
51                        from,
52                        to: Address::zero(),
53                        value,
54                        gas: 0.into(),
55                        input: Default::default(),
56                        call_type: CallType::Call,
57                    }),
58                    valid: true,
59                },
60                ExecTrace {
61                    action: Action::CallResult(CallResult {
62                        outcome: Outcome::Success,
63                        gas_left: 0.into(),
64                        return_data: Default::default(),
65                    }),
66                    valid: true,
67                },
68            ])]);
69        }
70
71        _ => {
72            error!("Unable to recover phantom trace: unexpected trace type while processing withdraw: {:?}", trace);
73            return Err("Unable to recover phantom trace: unexpected trace type while processing withdraw".into());
74        }
75    }
76}
77
78pub fn recover_phantom_trace_for_call(
79    tx_traces: &mut impl Iterator<Item = ExecTrace>, original_tx_hash: H256,
80    cross_space_nonce: u32,
81) -> Result<Vec<TransactionExecTraces>, String> {
82    let mut traces = vec![];
83
84    let trace = match tx_traces.next() {
85        Some(t) => t,
86        None => {
87            error!("Unable to recover phantom trace: no more traces (expected balance transfer) hash={:?}, nonce={:?}", original_tx_hash, cross_space_nonce);
88            return Err("Unable to recover phantom trace: no more traces (expected balance transfer)".into());
89        }
90    };
91
92    match trace.action {
93        Action::InternalTransferAction(InternalTransferAction {
94            from: _,
95            to:
96                AddressPocket::Balance(AddressWithSpace {
97                    address,
98                    space: Space::Ethereum,
99                }),
100            value,
101        }) => {
102            let input =
103                (original_tx_hash, U256::from(cross_space_nonce)).abi_encode();
104
105            traces.push(TransactionExecTraces(vec![
106                ExecTrace {
107                    action: Action::Call(Call {
108                        space: Space::Ethereum,
109                        from: Address::zero(),
110                        to: address,
111                        value,
112                        gas: 0.into(),
113                        input,
114                        call_type: CallType::Call,
115                    }),
116                    valid: true,
117                },
118                ExecTrace {
119                    action: Action::CallResult(CallResult {
120                        outcome: Outcome::Success,
121                        gas_left: 0.into(),
122                        return_data: Default::default(),
123                    }),
124                    valid: true,
125                },
126            ]));
127        }
128
129        _ => {
130            error!("Unable to recover phantom trace: unexpected trace type while processing call (hash={:?}, nonce={:?}): {:?}", original_tx_hash, cross_space_nonce, trace);
131            return Err("Unable to recover phantom trace: unexpected trace type while processing call".into());
132        }
133    }
134
135    let mut stack_depth = 0;
136    let mut phantom_traces = vec![];
137
138    loop {
139        let mut trace = match tx_traces.next() {
140            Some(t) => t,
141            None => {
142                error!("Unable to recover phantom trace: no more traces (expected eSpace trace entry) hash={:?}, nonce={:?}", original_tx_hash, cross_space_nonce);
143                return Err("Unable to recover phantom trace: no more traces (expected eSpace trace entry)".into());
144            }
145        };
146
147        // phantom traces have 0 gas
148        match trace.action {
149            Action::Call(Call { ref mut gas, .. }) => {
150                *gas = 0.into();
151            }
152            Action::Create(Create { ref mut gas, .. }) => {
153                *gas = 0.into();
154            }
155            Action::CallResult(CallResult {
156                ref mut gas_left, ..
157            }) => {
158                *gas_left = 0.into();
159            }
160            Action::CreateResult(CreateResult {
161                ref mut gas_left, ..
162            }) => {
163                *gas_left = 0.into();
164            }
165            Action::InternalTransferAction(InternalTransferAction {
166                ..
167            }) => {}
168            Action::SelfDestruct(_) => {}
169            // phantom tx should not have these actions
170            Action::SetAuth(_) => {}
171        }
172
173        phantom_traces.push(trace);
174
175        match phantom_traces.last().as_ref().unwrap().action {
176            Action::Call(_) | Action::Create(_) => {
177                stack_depth += 1;
178            }
179            Action::CallResult(_) | Action::CreateResult(_) => {
180                stack_depth -= 1;
181
182                if stack_depth == 0 {
183                    break;
184                }
185            }
186            _ => {}
187        }
188    }
189
190    traces.push(TransactionExecTraces(phantom_traces));
191    Ok(traces)
192}
193
194pub fn recover_phantom_traces(
195    tx_traces: TransactionExecTraces, original_tx_hash: H256,
196) -> Result<Vec<TransactionExecTraces>, String> {
197    let mut traces: Vec<TransactionExecTraces> = vec![];
198    let mut traces_iter = tx_traces.0.into_iter();
199    let mut cross_space_nonce = 0u32;
200
201    loop {
202        let trace = match traces_iter.next() {
203            Some(t) => t,
204            None => break,
205        };
206
207        match trace.action {
208            Action::Call(Call {
209                space: Space::Native,
210                to,
211                call_type: CallType::Call,
212                input,
213                ..
214            }) if to == CROSS_SPACE_CONTRACT_ADDRESS
215                && trace.valid
216                && is_call_create_sig(&input[0..4]) =>
217            {
218                let phantom_traces = recover_phantom_trace_for_call(
219                    &mut traces_iter,
220                    original_tx_hash,
221                    cross_space_nonce,
222                )?;
223
224                traces.extend(phantom_traces);
225                cross_space_nonce += 1;
226            }
227            Action::Call(Call {
228                space: Space::Native,
229                to,
230                call_type: CallType::Call,
231                input,
232                ..
233            }) if to == CROSS_SPACE_CONTRACT_ADDRESS
234                && trace.valid
235                && is_withdraw_sig(&input[0..4]) =>
236            {
237                let phantom_traces =
238                    recover_phantom_trace_for_withdraw(&mut traces_iter)?;
239
240                traces.extend(phantom_traces);
241            }
242            _ => {}
243        }
244    }
245
246    Ok(traces)
247}