cfx_execute_helper/phantom_tx/
recover.rs

1use cfx_executor::internal_contract::{
2    cross_space_events::*, SolidityEventTrait,
3};
4use cfx_parameters::internal_contract_addresses::CROSS_SPACE_CONTRACT_ADDRESS;
5use cfx_types::{Address, Bloom, Space, H256, U256};
6use primitives::{log_entry::build_bloom, Action, LogEntry, TransactionStatus};
7use solidity_abi::{ABIDecodable, ABIEncodable};
8
9use super::PhantomTransaction;
10
11type Bytes20 = [u8; 20];
12
13pub fn build_bloom_and_recover_phantom(
14    logs: &[LogEntry], tx_hash: H256,
15) -> (Vec<PhantomTransaction>, Bloom) {
16    (recover_phantom(logs, tx_hash), build_bloom(logs))
17}
18
19pub fn recover_phantom(
20    logs: &[LogEntry], tx_hash: H256,
21) -> Vec<PhantomTransaction> {
22    let mut phantom_txs: Vec<PhantomTransaction> = Default::default();
23    let mut maybe_working_tx: Option<PhantomTransaction> = None;
24    let mut cross_space_nonce = 0u32;
25    for log in logs.iter() {
26        if log.address == CROSS_SPACE_CONTRACT_ADDRESS {
27            let event_sig = log.topics.first().unwrap();
28            if event_sig == &CallEvent::EVENT_SIG
29                || event_sig == &CreateEvent::EVENT_SIG
30            {
31                assert!(maybe_working_tx.is_none());
32
33                let from = Address::from(
34                    Bytes20::abi_decode(&log.topics[1].as_ref()).unwrap(),
35                );
36                let to = Address::from(
37                    Bytes20::abi_decode(&log.topics[2].as_ref()).unwrap(),
38                );
39                let (value, nonce, data): (_, _, Vec<u8>) =
40                    ABIDecodable::abi_decode(&log.data).unwrap();
41
42                let is_create = event_sig == &CreateEvent::EVENT_SIG;
43                let action = if is_create {
44                    Action::Create
45                } else {
46                    Action::Call(to)
47                };
48                // The first phantom transaction for cross-space call, transfer
49                // balance and gas fee from the zero address to the mapped
50                // sender
51                phantom_txs.push(PhantomTransaction::simple_transfer(
52                    /* from */ Address::zero(),
53                    /* to */ from,
54                    U256::zero(), // Zero address always has nonce 0.
55                    value,
56                    /* data */
57                    (tx_hash, U256::from(cross_space_nonce)).abi_encode(),
58                ));
59                cross_space_nonce += 1;
60                // The second phantom transaction for cross-space call, transfer
61                // balance and gas fee from the zero address to the mapped
62                // sender
63                maybe_working_tx = Some(PhantomTransaction {
64                    from,
65                    nonce,
66                    action,
67                    value,
68                    data,
69                    ..Default::default()
70                });
71            } else if event_sig == &WithdrawEvent::EVENT_SIG {
72                let from = Address::from(
73                    Bytes20::abi_decode(&log.topics[1].as_ref()).unwrap(),
74                );
75                let (value, nonce) =
76                    ABIDecodable::abi_decode(&log.data).unwrap();
77                // The only one transaction for the withdraw
78                phantom_txs.push(PhantomTransaction::simple_transfer(
79                    from,
80                    Address::zero(),
81                    nonce,
82                    value,
83                    /* data */ vec![],
84                ));
85            } else if event_sig == &ReturnEvent::EVENT_SIG {
86                let success: bool =
87                    ABIDecodable::abi_decode(&log.data).unwrap();
88
89                let mut working_tx =
90                    std::mem::take(&mut maybe_working_tx).unwrap();
91
92                working_tx.outcome_status = if success {
93                    TransactionStatus::Success
94                } else {
95                    TransactionStatus::Failure
96                };
97
98                // Complete the second transaction for cross-space call.
99                phantom_txs.push(working_tx);
100            }
101        } else if log.space == Space::Ethereum {
102            if let Some(ref mut working_tx) = maybe_working_tx {
103                // The receipt is generated in cross-space call
104                working_tx.logs.push(log.clone());
105                working_tx.log_bloom.accrue_bloom(&log.bloom());
106            } else {
107                // The receipt is generated in evm-space transaction. Does
108                // nothing.
109            }
110        }
111    }
112    return phantom_txs;
113}