1
// Copyright 2019-2025 PureStake Inc.
2
// This file is part of Moonbeam.
3

            
4
// Moonbeam is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Moonbeam is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
use ethereum_types::{H160, H256};
18
use std::{collections::btree_map::BTreeMap, vec, vec::Vec};
19

            
20
use crate::types::{convert_memory, single::RawStepLog, ContextType};
21
use evm_tracing_events::{
22
	runtime::{Capture, ExitReason},
23
	Event, GasometerEvent, Listener as ListenerT, RuntimeEvent, StepEventFilter,
24
};
25

            
26
#[derive(Debug)]
27
pub struct Listener {
28
	disable_storage: bool,
29
	disable_memory: bool,
30
	disable_stack: bool,
31

            
32
	new_context: bool,
33
	context_stack: Vec<Context>,
34

            
35
	pub struct_logs: Vec<RawStepLog>,
36
	pub return_value: Vec<u8>,
37
	pub final_gas: u64,
38
	pub remaining_memory_usage: Option<usize>,
39
}
40

            
41
#[derive(Debug)]
42
struct Context {
43
	storage_cache: BTreeMap<H256, H256>,
44
	address: H160,
45
	current_step: Option<Step>,
46
	global_storage_changes: BTreeMap<H160, BTreeMap<H256, H256>>,
47
}
48

            
49
#[derive(Debug)]
50
struct Step {
51
	/// Current opcode.
52
	opcode: Vec<u8>,
53
	/// Depth of the context.
54
	depth: usize,
55
	/// Remaining gas.
56
	gas: u64,
57
	/// Gas cost of the following opcode.
58
	gas_cost: u64,
59
	/// Program counter position.
60
	position: usize,
61
	/// EVM memory copy (if not disabled).
62
	memory: Option<Vec<u8>>,
63
	/// EVM stack copy (if not disabled).
64
	stack: Option<Vec<H256>>,
65
}
66

            
67
impl Listener {
68
	pub fn new(
69
		disable_storage: bool,
70
		disable_memory: bool,
71
		disable_stack: bool,
72
		raw_max_memory_usage: usize,
73
	) -> Self {
74
		Self {
75
			disable_storage,
76
			disable_memory,
77
			disable_stack,
78
			remaining_memory_usage: Some(raw_max_memory_usage),
79

            
80
			struct_logs: vec![],
81
			return_value: vec![],
82
			final_gas: 0,
83

            
84
			new_context: false,
85
			context_stack: vec![],
86
		}
87
	}
88

            
89
	pub fn using<R, F: FnOnce() -> R>(&mut self, f: F) -> R {
90
		evm_tracing_events::using(self, f)
91
	}
92

            
93
	pub fn gasometer_event(&mut self, event: GasometerEvent) {
94
		match event {
95
			GasometerEvent::RecordTransaction { cost, .. } => {
96
				// First event of a transaction.
97
				// Next step will be the first context.
98
				self.new_context = true;
99
				self.final_gas = cost;
100
			}
101
			GasometerEvent::RecordCost { cost, snapshot } => {
102
				if let Some(context) = self.context_stack.last_mut() {
103
					// Register opcode cost. (ignore costs not between Step and StepResult)
104
					if let Some(step) = &mut context.current_step {
105
						step.gas = snapshot.gas();
106
						step.gas_cost = cost;
107
					}
108

            
109
					self.final_gas = snapshot.used_gas;
110
				}
111
			}
112
			GasometerEvent::RecordDynamicCost {
113
				gas_cost, snapshot, ..
114
			} => {
115
				if let Some(context) = self.context_stack.last_mut() {
116
					// Register opcode cost. (ignore costs not between Step and StepResult)
117
					if let Some(step) = &mut context.current_step {
118
						step.gas = snapshot.gas();
119
						step.gas_cost = gas_cost;
120
					}
121

            
122
					self.final_gas = snapshot.used_gas;
123
				}
124
			}
125
			// We ignore other kinds of message if any (new ones may be added in the future).
126
			#[allow(unreachable_patterns)]
127
			_ => (),
128
		}
129
	}
130

            
131
	pub fn runtime_event(&mut self, event: RuntimeEvent) {
132
		match event {
133
			RuntimeEvent::Step {
134
				context,
135
				opcode,
136
				position,
137
				stack,
138
				memory,
139
			} => {
140
				// Create a context if needed.
141
				if self.new_context {
142
					self.new_context = false;
143

            
144
					self.context_stack.push(Context {
145
						storage_cache: BTreeMap::new(),
146
						address: context.address,
147
						current_step: None,
148
						global_storage_changes: BTreeMap::new(),
149
					});
150
				}
151

            
152
				let depth = self.context_stack.len();
153

            
154
				// Ignore steps outside of any context (shouldn't even be possible).
155
				if let Some(context) = self.context_stack.last_mut() {
156
					context.current_step = Some(Step {
157
						opcode,
158
						depth,
159
						gas: 0,      // 0 for now, will add with gas events
160
						gas_cost: 0, // 0 for now, will add with gas events
161
						position: *position.as_ref().unwrap_or(&0) as usize,
162
						memory: if self.disable_memory {
163
							None
164
						} else {
165
							let memory = memory.expect("memory data to not be filtered out");
166

            
167
							self.remaining_memory_usage = self
168
								.remaining_memory_usage
169
								.and_then(|inner| inner.checked_sub(memory.data.len()));
170

            
171
							if self.remaining_memory_usage.is_none() {
172
								return;
173
							}
174

            
175
							Some(memory.data.clone())
176
						},
177
						stack: if self.disable_stack {
178
							None
179
						} else {
180
							let stack = stack.expect("stack data to not be filtered out");
181

            
182
							self.remaining_memory_usage = self
183
								.remaining_memory_usage
184
								.and_then(|inner| inner.checked_sub(stack.data.len()));
185

            
186
							if self.remaining_memory_usage.is_none() {
187
								return;
188
							}
189

            
190
							Some(stack.data.clone())
191
						},
192
					});
193
				}
194
			}
195
			RuntimeEvent::StepResult {
196
				result,
197
				return_value,
198
			} => {
199
				// StepResult is expected to be emitted after a step (in a context).
200
				// Only case StepResult will occur without a Step before is in a transfer
201
				// transaction to a non-contract address. However it will not contain any
202
				// steps and return an empty trace, so we can ignore this edge case.
203
				if let Some(context) = self.context_stack.last_mut() {
204
					if let Some(current_step) = context.current_step.take() {
205
						let Step {
206
							opcode,
207
							depth,
208
							gas,
209
							gas_cost,
210
							position,
211
							memory,
212
							stack,
213
						} = current_step;
214

            
215
						let memory = memory.map(convert_memory);
216

            
217
						let storage = if self.disable_storage {
218
							None
219
						} else {
220
							self.remaining_memory_usage =
221
								self.remaining_memory_usage.and_then(|inner| {
222
									inner.checked_sub(context.storage_cache.len() * 64)
223
								});
224

            
225
							if self.remaining_memory_usage.is_none() {
226
								return;
227
							}
228

            
229
							Some(context.storage_cache.clone())
230
						};
231

            
232
						self.struct_logs.push(RawStepLog {
233
							depth: depth.into(),
234
							gas: gas.into(),
235
							gas_cost: gas_cost.into(),
236
							memory,
237
							op: opcode,
238
							pc: position.into(),
239
							stack,
240
							storage,
241
						});
242
					}
243
				}
244

            
245
				// We match on the capture to handle traps/exits.
246
				match result {
247
					Err(Capture::Exit(reason)) => {
248
						// Exit = we exit the context (should always be some)
249
						if let Some(mut context) = self.context_stack.pop() {
250
							// If final context is exited, we store gas and return value.
251
							if self.context_stack.is_empty() {
252
								self.return_value = return_value.to_vec();
253
							}
254

            
255
							// If the context exited without revert we must keep track of the
256
							// updated storage keys.
257
							if !self.disable_storage && matches!(reason, ExitReason::Succeed(_)) {
258
								if let Some(parent_context) = self.context_stack.last_mut() {
259
									// Add cache to storage changes.
260
									context
261
										.global_storage_changes
262
										.insert(context.address, context.storage_cache);
263

            
264
									// Apply storage changes to parent, either updating its cache or map of changes.
265
									for (address, mut storage) in
266
										context.global_storage_changes.into_iter()
267
									{
268
										// Same address => We update its cache (only tracked keys)
269
										if parent_context.address == address {
270
											for (cached_key, cached_value) in
271
												parent_context.storage_cache.iter_mut()
272
											{
273
												if let Some(value) = storage.remove(cached_key) {
274
													*cached_value = value;
275
												}
276
											}
277
										}
278
										// Otherwise, update the storage changes.
279
										else {
280
											parent_context
281
												.global_storage_changes
282
												.entry(address)
283
												.or_insert_with(BTreeMap::new)
284
												.append(&mut storage);
285
										}
286
									}
287
								}
288
							}
289
						}
290
					}
291
					Err(Capture::Trap(opcode)) if ContextType::from(opcode.clone()).is_some() => {
292
						self.new_context = true;
293
					}
294
					_ => (),
295
				}
296
			}
297
			RuntimeEvent::SLoad {
298
				address: _,
299
				index,
300
				value,
301
			}
302
			| RuntimeEvent::SStore {
303
				address: _,
304
				index,
305
				value,
306
			} => {
307
				if let Some(context) = self.context_stack.last_mut() {
308
					if !self.disable_storage {
309
						context.storage_cache.insert(index, value);
310
					}
311
				}
312
			}
313
			// We ignore other kinds of messages if any (new ones may be added in the future).
314
			#[allow(unreachable_patterns)]
315
			_ => (),
316
		}
317
	}
318
}
319

            
320
impl ListenerT for Listener {
321
	fn event(&mut self, event: Event) {
322
		if self.remaining_memory_usage.is_none() {
323
			return;
324
		}
325

            
326
		match event {
327
			Event::Gasometer(e) => self.gasometer_event(e),
328
			Event::Runtime(e) => self.runtime_event(e),
329
			_ => {}
330
		};
331
	}
332

            
333
	fn step_event_filter(&self) -> StepEventFilter {
334
		StepEventFilter {
335
			enable_memory: !self.disable_memory,
336
			enable_stack: !self.disable_stack,
337
		}
338
	}
339
}