Skip to main content

revive_strategy/tracing/
mod.rs

1use alloy_primitives::{Address, Bytes, U256 as RU256};
2use call_tracer::ExpectedCallTracer;
3use expect_create::CreateTracer;
4use foundry_cheatcodes::{Ecx, ExpectedCallTracker, ExpectedCreate};
5
6use polkadot_sdk::pallet_revive::{
7    AccountInfo, Pallet, U256,
8    evm::{
9        CallTrace, CallTracer, PrestateTrace, PrestateTraceInfo, PrestateTracer,
10        PrestateTracerConfig, Tracer as ReviveTracer, TracerType,
11    },
12    tracing::{Tracing, trace as trace_revive},
13};
14use revert_tracer::RevertTracer;
15use revive_env::Runtime;
16use revm::{context::JournalTr, database::states::StorageSlot, state::Bytecode};
17use storage_tracer::{AccountAccess, StorageTracer};
18mod call_tracer;
19mod expect_create;
20mod revert_tracer;
21pub mod storage_tracer;
22pub struct Tracer {
23    pub call_tracer: CallTracer,
24    pub prestate_tracer: PrestateTracer<Runtime>,
25    pub storage_accesses: StorageTracer,
26    pub revert_tracer: RevertTracer,
27    pub expect_call_tracer: ExpectedCallTracer,
28    pub create_tracer: CreateTracer,
29}
30
31impl Tracer {
32    pub fn new(data: ExpectedCallTracker, create_data: Vec<ExpectedCreate>) -> Self {
33        let call_tracer =
34            match Pallet::<revive_env::Runtime>::evm_tracer(TracerType::CallTracer(None)) {
35                ReviveTracer::CallTracer(tracer) => tracer,
36                _ => unreachable!("Expected CallTracer variant"),
37            };
38
39        let prestate_tracer: PrestateTracer<revive_env::Runtime> =
40            PrestateTracer::new(PrestateTracerConfig {
41                diff_mode: true,
42                disable_storage: false,
43                disable_code: false,
44            });
45
46        Self {
47            call_tracer,
48            prestate_tracer,
49            storage_accesses: Default::default(),
50            revert_tracer: RevertTracer::new(),
51            expect_call_tracer: ExpectedCallTracer::new(data),
52            create_tracer: CreateTracer::new(create_data),
53        }
54    }
55
56    pub fn trace<R, F: FnOnce() -> R>(&mut self, f: F) -> R {
57        trace_revive(self, f)
58    }
59
60    /// Collects call traces
61    pub fn collect_call_traces(&mut self) -> Option<CallTrace> {
62        self.call_tracer.clone().collect_trace()
63    }
64
65    /// Collects prestate traces
66    fn collect_prestate_traces(&mut self) -> PrestateTrace {
67        self.prestate_tracer.clone().collect_trace()
68    }
69
70    /// Collects recorded accesses
71    pub fn get_recorded_accesses(&mut self) -> Vec<AccountAccess> {
72        self.storage_accesses.get_records()
73    }
74
75    /// Applies `PrestateTrace` diffs to the revm state
76    pub fn apply_prestate_trace(&mut self, ecx: Ecx<'_, '_, '_>) {
77        let prestate_trace = self.collect_prestate_traces();
78        match prestate_trace {
79            polkadot_sdk::pallet_revive::evm::PrestateTrace::DiffMode { pre: _digests, post } => {
80                for (key, PrestateTraceInfo { balance, nonce, code, storage }) in post {
81                    let address = Address::from_slice(key.as_bytes());
82                    let is_create = !ecx.journaled_state.state.contains_key(&address);
83
84                    ecx.journaled_state.load_account(address).expect("account could not be loaded");
85
86                    ecx.journaled_state.touch(address);
87                    let account = ecx.journaled_state.state.get_mut(&address).unwrap();
88                    if let Some(balance) = balance {
89                        account.info.balance = RU256::from_limbs(balance.0);
90                    };
91
92                    if let Some(nonce) = nonce {
93                        account.info.nonce = nonce.into();
94                    };
95
96                    if is_create
97                        && let Some(ref code) = code
98                        && let Some(info) = AccountInfo::<Runtime>::load_contract(&key)
99                    {
100                        let code = code.clone();
101                        let account =
102                            ecx.journaled_state.state.get_mut(&address).expect("account is loaded");
103                        let bytecode = Bytecode::new_raw(Bytes::from(code.0));
104                        account.info.code_hash = info.code_hash.0.into();
105                        account.info.code = Some(bytecode);
106                    }
107
108                    for (slot, entry) in storage {
109                        let key = RU256::from_be_slice(&slot.0);
110                        let previous = ecx.journaled_state.sload(address, key).expect("to load");
111
112                        if let Some(e_entry) = entry {
113                            let entry = RU256::from_be_slice(&e_entry.0);
114                            let new_slot = StorageSlot::new_changed(previous.data, entry);
115                            ecx.journaled_state
116                                .sstore(address, key, new_slot.present_value)
117                                .expect("to succeed");
118                        }
119                    }
120                }
121            }
122            _ => panic!("Can't happen"),
123        };
124    }
125}
126
127impl Tracing for Tracer {
128    fn watch_address(&mut self, addr: &polkadot_sdk::sp_core::H160) {
129        self.prestate_tracer.watch_address(addr);
130        self.call_tracer.watch_address(addr);
131        self.storage_accesses.watch_address(addr);
132        self.revert_tracer.watch_address(addr);
133        self.create_tracer.watch_address(addr);
134    }
135
136    fn terminate(
137        &mut self,
138        contract_address: polkadot_sdk::sp_core::H160,
139        beneficiary_address: polkadot_sdk::sp_core::H160,
140        gas_left: U256,
141        value: U256,
142    ) {
143        self.prestate_tracer.terminate(contract_address, beneficiary_address, gas_left, value);
144        self.call_tracer.terminate(contract_address, beneficiary_address, gas_left, value);
145        self.storage_accesses.terminate(contract_address, beneficiary_address, gas_left, value);
146        self.revert_tracer.terminate(contract_address, beneficiary_address, gas_left, value);
147        self.create_tracer.terminate(contract_address, beneficiary_address, gas_left, value);
148    }
149
150    fn enter_child_span(
151        &mut self,
152        from: polkadot_sdk::sp_core::H160,
153        to: polkadot_sdk::sp_core::H160,
154        is_delegate_call: Option<polkadot_sdk::sp_core::H160>,
155        is_read_only: bool,
156        value: U256,
157        input: &[u8],
158        gas: U256,
159    ) {
160        self.prestate_tracer.enter_child_span(
161            from,
162            to,
163            is_delegate_call,
164            is_read_only,
165            value,
166            input,
167            gas,
168        );
169        self.call_tracer.enter_child_span(
170            from,
171            to,
172            is_delegate_call,
173            is_read_only,
174            value,
175            input,
176            gas,
177        );
178        self.storage_accesses.enter_child_span(
179            from,
180            to,
181            is_delegate_call,
182            is_read_only,
183            value,
184            input,
185            gas,
186        );
187        self.revert_tracer.enter_child_span(
188            from,
189            to,
190            is_delegate_call,
191            is_read_only,
192            value,
193            input,
194            gas,
195        );
196        self.expect_call_tracer.enter_child_span(
197            from,
198            to,
199            is_delegate_call,
200            is_read_only,
201            value,
202            input,
203            gas,
204        );
205        self.create_tracer.enter_child_span(
206            from,
207            to,
208            is_delegate_call,
209            is_read_only,
210            value,
211            input,
212            gas,
213        )
214    }
215
216    fn instantiate_code(
217        &mut self,
218        code: &polkadot_sdk::pallet_revive::Code,
219        salt: Option<&[u8; 32]>,
220    ) {
221        self.prestate_tracer.instantiate_code(code, salt);
222        self.call_tracer.instantiate_code(code, salt);
223        self.storage_accesses.instantiate_code(code, salt);
224        self.revert_tracer.instantiate_code(code, salt);
225        self.expect_call_tracer.instantiate_code(code, salt);
226        self.create_tracer.instantiate_code(code, salt);
227    }
228
229    fn balance_read(&mut self, addr: &polkadot_sdk::sp_core::H160, value: U256) {
230        self.prestate_tracer.balance_read(addr, value);
231        self.call_tracer.balance_read(addr, value);
232        self.storage_accesses.balance_read(addr, value);
233        self.revert_tracer.balance_read(addr, value);
234        self.create_tracer.balance_read(addr, value);
235    }
236
237    fn storage_read(&mut self, key: &polkadot_sdk::pallet_revive::Key, value: Option<&[u8]>) {
238        self.prestate_tracer.storage_read(key, value);
239        self.call_tracer.storage_read(key, value);
240        self.storage_accesses.storage_read(key, value);
241        self.revert_tracer.storage_read(key, value);
242        self.create_tracer.storage_read(key, value);
243    }
244
245    fn storage_write(
246        &mut self,
247        key: &polkadot_sdk::pallet_revive::Key,
248        old_value: Option<Vec<u8>>,
249        new_value: Option<&[u8]>,
250    ) {
251        self.prestate_tracer.storage_write(key, old_value.clone(), new_value);
252        self.call_tracer.storage_write(key, old_value.clone(), new_value);
253        self.storage_accesses.storage_write(key, old_value.clone(), new_value);
254        self.revert_tracer.storage_write(key, old_value.clone(), new_value);
255        self.create_tracer.storage_write(key, old_value, new_value);
256    }
257
258    fn log_event(
259        &mut self,
260        event: polkadot_sdk::sp_core::H160,
261        topics: &[polkadot_sdk::sp_core::H256],
262        data: &[u8],
263    ) {
264        self.prestate_tracer.log_event(event, topics, data);
265        self.call_tracer.log_event(event, topics, data);
266        self.storage_accesses.log_event(event, topics, data);
267        self.revert_tracer.log_event(event, topics, data);
268        self.create_tracer.log_event(event, topics, data);
269    }
270
271    fn exit_child_span(
272        &mut self,
273        output: &polkadot_sdk::pallet_revive::ExecReturnValue,
274        gas_left: U256,
275    ) {
276        self.prestate_tracer.exit_child_span(output, gas_left);
277        self.call_tracer.exit_child_span(output, gas_left);
278        self.storage_accesses.exit_child_span(output, gas_left);
279        self.revert_tracer.exit_child_span(output, gas_left);
280        self.expect_call_tracer.exit_child_span(output, gas_left);
281        self.create_tracer.exit_child_span(output, gas_left);
282    }
283
284    fn exit_child_span_with_error(
285        &mut self,
286        error: polkadot_sdk::sp_runtime::DispatchError,
287        gas_left: U256,
288    ) {
289        self.prestate_tracer.exit_child_span_with_error(error, gas_left);
290        self.call_tracer.exit_child_span_with_error(error, gas_left);
291        self.storage_accesses.exit_child_span_with_error(error, gas_left);
292        self.revert_tracer.exit_child_span_with_error(error, gas_left);
293        self.expect_call_tracer.exit_child_span_with_error(error, gas_left);
294        self.create_tracer.exit_child_span_with_error(error, gas_left);
295    }
296}