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}