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 pub fn collect_call_traces(&mut self) -> Option<CallTrace> {
62 self.call_tracer.clone().collect_trace()
63 }
64
65 fn collect_prestate_traces(&mut self) -> PrestateTrace {
67 self.prestate_tracer.clone().collect_trace()
68 }
69
70 pub fn get_recorded_accesses(&mut self) -> Vec<AccountAccess> {
72 self.storage_accesses.get_records()
73 }
74
75 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}