moonbeam_evm_tracer/
lib.rs

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//! Substrate EVM tracing.
18//!
19//! The purpose of this crate is enable tracing the EVM opcode execution and will be used by
20//! both Dapp developers - to get a granular view on their transactions - and indexers to access
21//! the EVM callstack (internal transactions).
22//!
23//! Proxies EVM messages to the host functions.
24
25#![cfg_attr(not(feature = "std"), no_std)]
26
27pub mod tracer {
28	use ethereum_types::H256;
29	use evm_tracing_events::{EvmEvent, GasometerEvent, RuntimeEvent, StepEventFilter};
30	use parity_scale_codec::{Decode, Encode};
31
32	use evm::tracing::{using as evm_using, EventListener as EvmListener};
33	use evm_gasometer::tracing::{using as gasometer_using, EventListener as GasometerListener};
34	use evm_runtime::tracing::{using as runtime_using, EventListener as RuntimeListener};
35	use sp_runtime::DispatchError;
36	use sp_std::{cell::RefCell, rc::Rc};
37
38	/// The current EthereumXcmTransaction trace status.
39	#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq)]
40	pub enum EthereumTracingStatus {
41		/// A full block trace.
42		Block,
43		/// A single transaction.
44		Transaction(H256),
45		/// Exit signal.
46		TransactionExited,
47	}
48
49	environmental::environmental!(ETHEREUM_TRACING_STATUS: EthereumTracingStatus);
50
51	struct ListenerProxy<T>(pub Rc<RefCell<T>>);
52	impl<T: GasometerListener> GasometerListener for ListenerProxy<T> {
53		fn event(&mut self, event: evm_gasometer::tracing::Event) {
54			self.0.borrow_mut().event(event);
55		}
56	}
57
58	impl<T: RuntimeListener> RuntimeListener for ListenerProxy<T> {
59		fn event(&mut self, event: evm_runtime::tracing::Event) {
60			self.0.borrow_mut().event(event);
61		}
62	}
63
64	impl<T: EvmListener> EvmListener for ListenerProxy<T> {
65		fn event(&mut self, event: evm::tracing::Event) {
66			self.0.borrow_mut().event(event);
67		}
68	}
69
70	pub struct EthereumTracer;
71
72	impl EthereumTracer {
73		pub fn transaction(
74			tx_hash: H256,
75			func: impl FnOnce() -> Result<(), DispatchError>,
76		) -> Result<(), DispatchError> {
77			ETHEREUM_TRACING_STATUS::using(&mut EthereumTracingStatus::Transaction(tx_hash), func)
78		}
79
80		pub fn block(
81			func: impl FnOnce() -> Result<(), DispatchError>,
82		) -> Result<(), DispatchError> {
83			ETHEREUM_TRACING_STATUS::using(&mut EthereumTracingStatus::Block, func)
84		}
85
86		pub fn transaction_exited() {
87			ETHEREUM_TRACING_STATUS::with(|state| {
88				*state = EthereumTracingStatus::TransactionExited
89			});
90		}
91
92		pub fn status() -> Option<EthereumTracingStatus> {
93			ETHEREUM_TRACING_STATUS::with(|state| state.clone())
94		}
95	}
96
97	pub struct EvmTracer {
98		step_event_filter: StepEventFilter,
99	}
100
101	impl EvmTracer {
102		pub fn new() -> Self {
103			Self {
104				step_event_filter: moonbeam_primitives_ext::moonbeam_ext::step_event_filter(),
105			}
106		}
107
108		/// Setup event listeners and execute provided closure.
109		///
110		/// Consume the tracer and return it alongside the return value of
111		/// the closure.
112		pub fn trace<R, F: FnOnce() -> R>(self, f: F) {
113			let wrapped = Rc::new(RefCell::new(self));
114
115			let mut gasometer = ListenerProxy(Rc::clone(&wrapped));
116			let mut runtime = ListenerProxy(Rc::clone(&wrapped));
117			let mut evm = ListenerProxy(Rc::clone(&wrapped));
118
119			// Each line wraps the previous `f` into a `using` call.
120			// Listening to new events results in adding one new line.
121			// Order is irrelevant when registering listeners.
122			let f = || runtime_using(&mut runtime, f);
123			let f = || gasometer_using(&mut gasometer, f);
124			let f = || evm_using(&mut evm, f);
125			f();
126		}
127
128		pub fn emit_new() {
129			moonbeam_primitives_ext::moonbeam_ext::call_list_new();
130		}
131	}
132
133	impl EvmListener for EvmTracer {
134		/// Proxies `evm::tracing::Event` to the host.
135		fn event(&mut self, event: evm::tracing::Event) {
136			let event: EvmEvent = event.into();
137			let message = event.encode();
138			moonbeam_primitives_ext::moonbeam_ext::evm_event(message);
139		}
140	}
141
142	impl GasometerListener for EvmTracer {
143		/// Proxies `evm_gasometer::tracing::Event` to the host.
144		fn event(&mut self, event: evm_gasometer::tracing::Event) {
145			let event: GasometerEvent = event.into();
146			let message = event.encode();
147			moonbeam_primitives_ext::moonbeam_ext::gasometer_event(message);
148		}
149	}
150
151	impl RuntimeListener for EvmTracer {
152		/// Proxies `evm_runtime::tracing::Event` to the host.
153		fn event(&mut self, event: evm_runtime::tracing::Event) {
154			let event = RuntimeEvent::from_evm_event(event, self.step_event_filter);
155			let message = event.encode();
156			moonbeam_primitives_ext::moonbeam_ext::runtime_event(message);
157		}
158	}
159}