Skip to main content

reth_evm_ethereum/
factory.rs

1#[cfg(feature = "jit")]
2use alloc::string::String;
3use alloc::sync::Arc;
4use alloy_consensus::Header;
5use reth_chainspec::{ChainSpec, EthChainSpec, EthereumHardforks};
6#[cfg(feature = "std")]
7use reth_evm::precompile_cache::{
8    CachedPrecompileProvider, PrecompileCacheMap, PrecompileCachePolicy,
9};
10
11#[cfg(feature = "jit")]
12pub use evm2_jit::{
13    runtime::{
14        maybe_run_jit_helper, CompilationEvent, CompilationKind, JitBackend, JitMode,
15        RuntimeConfig, RuntimeStatsSnapshot, RuntimeTuning,
16    },
17    CompileTimings,
18};
19
20use crate::{
21    executor::{EthBigBlockExecutor, EthBigBlockPlan},
22    EthBlockExecutionCtx, EthBlockExecutor, EthEvmEnv, RethReceiptBuilder,
23};
24use reth_ethereum_primitives::{Receipt, TransactionSigned};
25use reth_evm::ReceiptBuilder;
26
27/// Factory used to construct an evm2-backed EVM.
28///
29/// Custom-node integrations can select a different evm2 transaction type through [`Self::Types`].
30pub trait EvmFactory: Clone + core::fmt::Debug + Send + Sync + Unpin + 'static
31where
32    <Self::Types as evm2::EvmTypesHost>::BlockEnvExt: Send + Sync,
33    <Self::Types as evm2::EvmTypesHost>::EvmExt: Default,
34    <Self::Types as evm2::EvmTypesHost>::TxResultExt: Send + Sync,
35{
36    /// Runtime evm2 type family used by the EVM.
37    type Types: evm2::EvmTypes + evm2::EvmTypesHost<SpecId = Self::SpecId>;
38
39    /// Runtime specification identifier used by the EVM type family.
40    type SpecId: Copy
41        + Into<evm2::SpecId>
42        + Eq
43        + core::hash::Hash
44        + core::fmt::Debug
45        + Send
46        + Sync
47        + Clone
48        + 'static;
49
50    /// Maps an Ethereum hardfork to the runtime specification selected by this factory.
51    fn spec_id(&self, spec: evm2::SpecId) -> Self::SpecId;
52
53    /// Converts the standard Ethereum block environment into the factory's block environment.
54    fn block_env(&self, block: evm2::env::BlockEnv) -> evm2::env::BlockEnv<Self::Types> {
55        block.with_ext(Default::default())
56    }
57
58    /// Returns the runtime version for an Ethereum hardfork.
59    fn version(&self, spec: evm2::SpecId, chain_id: u64) -> evm2::Version {
60        let mut version = evm2::Version::new(spec);
61        version.chain_id = chain_id;
62        version
63    }
64
65    /// Builds the execution configuration for a runtime specification.
66    fn execution_config(
67        &self,
68        spec: Self::SpecId,
69        version: evm2::Version,
70    ) -> evm2::ExecutionConfig<Self::Types> {
71        evm2::ExecutionConfig::for_spec_and_version(spec, version)
72    }
73
74    /// Builds the transaction registry for a runtime specification.
75    fn tx_registry(
76        &self,
77        spec: Self::SpecId,
78    ) -> evm2::registry::TxRegistry<Self::Types, evm2::TxResult<Self::Types>>;
79
80    /// Builds the precompile table for a runtime specification.
81    fn precompiles(&self, spec: Self::SpecId) -> evm2::Precompiles<Self::Types> {
82        evm2::Precompiles::base(spec.into())
83    }
84
85    /// Applies factory-specific configuration to a newly created EVM.
86    fn configure_evm(&self, _evm: &mut evm2::Evm<'_, Self::Types>) {}
87
88    /// Selects pure precompiles eligible for shared caching. Custom factories default to uncached.
89    #[cfg(feature = "std")]
90    fn precompile_cache_policy(&self, _spec: Self::SpecId) -> PrecompileCachePolicy {
91        PrecompileCachePolicy::Disabled
92    }
93}
94
95/// Ethereum block executor factory.
96#[derive(Debug)]
97pub struct EthBlockExecutorFactory<R = RethReceiptBuilder, C = ChainSpec, F = RethEvmFactory>
98where
99    F: EvmFactory,
100{
101    /// Receipt builder.
102    receipt_builder: R,
103    /// Chain specification.
104    chain_spec: Arc<C>,
105    /// EVM factory configuration.
106    evm_factory: F,
107}
108
109/// Executor factory for merged payloads that switch block context at segment boundaries.
110#[derive(Debug, Clone)]
111pub struct EthBigBlockExecutorFactory<C = ChainSpec, F = RethEvmFactory>
112where
113    F: EvmFactory,
114{
115    inner: EthBlockExecutorFactory<RethReceiptBuilder, C, F>,
116}
117
118impl<C, F: EvmFactory> EthBigBlockExecutorFactory<C, F> {
119    /// Creates a big-block executor factory from the standard Ethereum factory.
120    pub const fn new(inner: EthBlockExecutorFactory<RethReceiptBuilder, C, F>) -> Self {
121        Self { inner }
122    }
123
124    /// Returns the wrapped Ethereum executor factory.
125    pub const fn inner(&self) -> &EthBlockExecutorFactory<RethReceiptBuilder, C, F> {
126        &self.inner
127    }
128}
129
130impl<C, F> reth_evm::BlockExecutorFactory for EthBigBlockExecutorFactory<C, F>
131where
132    C: EthChainSpec<Header = Header> + EthereumHardforks,
133    F: EvmFactory,
134    F::Types: evm2::EvmTypes<Tx = evm2::ethereum::TxEnvelope>,
135{
136    type EvmFactory = F;
137    type EvmTypes = F::Types;
138    type Transaction = TransactionSigned;
139    type Receipt = Receipt;
140    type Evm<'a> = evm2::Evm<'a, F::Types>;
141    type EvmEnv = EthEvmEnv<F::Types>;
142    type ExecutionCtx<'a>
143        = EthBigBlockPlan<'a, F::Types>
144    where
145        Self: 'a;
146    type Executor<'a>
147        = EthBigBlockExecutor<'a, C, F>
148    where
149        Self: 'a;
150
151    fn create_executor<'a>(
152        &'a self,
153        evm: Self::Evm<'a>,
154        ctx: Self::ExecutionCtx<'a>,
155    ) -> Self::Executor<'a>
156    where
157        Self: 'a,
158    {
159        let executor = EthBlockExecutor::new(
160            evm,
161            ctx.segments[0].ctx.clone(),
162            self.inner.chain_spec().as_ref(),
163            self.inner.receipt_builder(),
164        );
165        EthBigBlockExecutor::new(executor, &self.inner, self.inner.chain_spec().clone(), ctx)
166    }
167
168    fn evm_factory(&self) -> &Self::EvmFactory {
169        self.inner.evm_factory()
170    }
171
172    fn evm_with_env<'a, DB>(&self, db: DB, evm_env: Self::EvmEnv) -> Self::Evm<'a>
173    where
174        DB: evm2::evm::DynDatabase + 'a,
175    {
176        self.inner.evm_with_env(db, evm_env)
177    }
178}
179
180impl<R: Clone, C, F: EvmFactory> Clone for EthBlockExecutorFactory<R, C, F> {
181    fn clone(&self) -> Self {
182        Self {
183            receipt_builder: self.receipt_builder.clone(),
184            chain_spec: self.chain_spec.clone(),
185            evm_factory: self.evm_factory.clone(),
186        }
187    }
188}
189
190impl<C> EthBlockExecutorFactory<RethReceiptBuilder, C> {
191    /// Creates a new Ethereum block executor factory.
192    pub fn new(chain_spec: Arc<C>) -> Self {
193        Self::new_with_evm_factory(chain_spec, RethEvmFactory::default())
194    }
195}
196
197impl<C, F: EvmFactory> EthBlockExecutorFactory<RethReceiptBuilder, C, F> {
198    /// Creates a new Ethereum block executor factory with the given EVM factory configuration.
199    pub const fn new_with_evm_factory(chain_spec: Arc<C>, evm_factory: F) -> Self {
200        Self::new_with_receipt_builder(RethReceiptBuilder, chain_spec, evm_factory)
201    }
202}
203
204impl<R, C, F: EvmFactory> EthBlockExecutorFactory<R, C, F> {
205    /// Creates a new Ethereum block executor factory with the given receipt and EVM factories.
206    pub const fn new_with_receipt_builder(
207        receipt_builder: R,
208        chain_spec: Arc<C>,
209        evm_factory: F,
210    ) -> Self {
211        Self { receipt_builder, chain_spec, evm_factory }
212    }
213
214    /// Returns the chain spec associated with this factory.
215    pub const fn chain_spec(&self) -> &Arc<C> {
216        &self.chain_spec
217    }
218
219    /// Returns the configured receipt builder.
220    pub const fn receipt_builder(&self) -> &R {
221        &self.receipt_builder
222    }
223
224    /// Returns the configured EVM factory state.
225    pub const fn evm_factory(&self) -> &F {
226        &self.evm_factory
227    }
228
229    /// Returns mutable access to the configured EVM factory state.
230    #[cfg(feature = "jit")]
231    pub(crate) const fn evm_factory_mut(&mut self) -> &mut F {
232        &mut self.evm_factory
233    }
234
235    /// Creates an EVM instance with the configured Ethereum execution environment.
236    pub(crate) fn build_evm_with_env<'a, DB>(
237        &self,
238        db: DB,
239        env: EthEvmEnv<F::Types>,
240    ) -> evm2::Evm<'a, F::Types>
241    where
242        C: EthChainSpec<Header = Header>,
243        DB: evm2::evm::DynDatabase + 'a,
244        F: 'static,
245    {
246        let precompiles = self.evm_factory.precompiles(env.spec);
247        self.build_evm(db, env, precompiles)
248    }
249
250    /// Creates an EVM instance whose precompile results are cached in `cache_map`.
251    ///
252    /// The cache wraps the factory's precompiles before [`EvmFactory::configure_evm`] runs, so
253    /// factory customizations apply to the cached provider.
254    #[cfg(feature = "std")]
255    pub(crate) fn build_evm_with_precompile_cache<'a, DB>(
256        &self,
257        db: DB,
258        env: EthEvmEnv<F::Types>,
259        cache_map: PrecompileCacheMap<F::SpecId>,
260        metrics_enabled: bool,
261    ) -> evm2::Evm<'a, F::Types>
262    where
263        C: EthChainSpec<Header = Header>,
264        DB: evm2::evm::DynDatabase + 'a,
265        F: 'static,
266    {
267        let spec = env.spec;
268        let precompiles = CachedPrecompileProvider::new(
269            self.evm_factory.precompiles(spec),
270            cache_map,
271            spec,
272            self.evm_factory.precompile_cache_policy(spec),
273            metrics_enabled,
274        );
275        self.build_evm(db, env, precompiles)
276    }
277
278    fn build_evm<'a, DB>(
279        &self,
280        db: DB,
281        env: EthEvmEnv<F::Types>,
282        precompiles: impl evm2::evm::precompile::PrecompileProvider<F::Types> + 'a,
283    ) -> evm2::Evm<'a, F::Types>
284    where
285        DB: evm2::evm::DynDatabase + 'a,
286    {
287        let spec = env.spec;
288        let config = self.evm_factory.execution_config(spec, env.version);
289        let registry = self.evm_factory.tx_registry(spec);
290
291        let mut evm = evm2::Evm::<F::Types>::new_with_execution_config(
292            config,
293            spec,
294            env.block,
295            registry,
296            db,
297            precompiles,
298        );
299        self.evm_factory.configure_evm(&mut evm);
300        evm
301    }
302
303    /// Replaces the execution configuration, transaction registry, and precompiles at a segment
304    /// boundary while preserving the EVM state and database.
305    ///
306    /// Within one spec the registry and precompiles are kept, so a precompile cache installed by
307    /// [`Self::build_evm_with_precompile_cache`] survives.
308    pub(crate) fn reconfigure_evm<'a>(
309        &'a self,
310        evm: &mut evm2::Evm<'a, F::Types>,
311        env: &EthEvmEnv<F::Types>,
312    ) {
313        let spec = env.spec;
314        let config = self.evm_factory.execution_config(spec, env.version);
315        if evm.config_spec_id() == spec {
316            evm.set_block_and_execution_config_within_spec(env.block, config);
317            return
318        }
319
320        let registry = self.evm_factory.tx_registry(spec);
321        let precompiles = self.evm_factory.precompiles(spec);
322
323        evm.set_block_and_execution_config(env.block, config, spec, registry, precompiles);
324        self.evm_factory.configure_evm(evm);
325    }
326}
327
328impl<R, C, F> reth_evm::BlockExecutorFactory for EthBlockExecutorFactory<R, C, F>
329where
330    C: EthChainSpec<Header = Header> + EthereumHardforks,
331    F: EvmFactory,
332    R: ReceiptBuilder,
333
334    <F::Types as evm2::EvmTypesHost>::Tx: alloy_eips::eip2718::Typed2718,
335{
336    type EvmFactory = F;
337    type EvmTypes = F::Types;
338    type Transaction = R::Transaction;
339    type Receipt = R::Receipt;
340    type Evm<'a> = evm2::Evm<'a, F::Types>;
341    type EvmEnv = EthEvmEnv<F::Types>;
342    type ExecutionCtx<'a>
343        = EthBlockExecutionCtx<'a>
344    where
345        Self: 'a;
346    type Executor<'a>
347        = EthBlockExecutor<'a, F::Types, &'a R>
348    where
349        Self: 'a;
350
351    fn create_executor<'a>(
352        &'a self,
353        evm: Self::Evm<'a>,
354        ctx: Self::ExecutionCtx<'a>,
355    ) -> Self::Executor<'a>
356    where
357        Self: 'a,
358    {
359        EthBlockExecutor::new(evm, ctx, self.chain_spec.as_ref(), &self.receipt_builder)
360    }
361
362    fn evm_factory(&self) -> &Self::EvmFactory {
363        Self::evm_factory(self)
364    }
365
366    fn evm_with_env<'a, DB>(&self, db: DB, evm_env: Self::EvmEnv) -> Self::Evm<'a>
367    where
368        DB: evm2::evm::DynDatabase + 'a,
369    {
370        self.build_evm_with_env(db, evm_env)
371    }
372}
373
374/// Reth EVM factory configuration.
375///
376/// With the `jit` feature, this owns the shared evm2 JIT backend. EVMs only install the JIT
377/// interpreter runner when local JIT support was selected through
378/// [`ConfigureEvm::with_jit_support`](reth_evm::ConfigureEvm::with_jit_support).
379#[derive(Debug, Clone)]
380pub struct RethEvmFactory {
381    #[cfg(feature = "jit")]
382    backend: JitBackend,
383    #[cfg(feature = "jit")]
384    metrics: JitMetrics,
385    #[cfg(feature = "jit")]
386    jit_support: bool,
387}
388
389impl Default for RethEvmFactory {
390    fn default() -> Self {
391        Self::disabled()
392    }
393}
394
395impl RethEvmFactory {
396    /// Creates a factory configuration with JIT compilation disabled.
397    #[cfg_attr(not(feature = "jit"), allow(clippy::missing_const_for_fn))]
398    pub fn disabled() -> Self {
399        #[cfg(feature = "jit")]
400        {
401            Self::new(JitBackend::disabled())
402        }
403        #[cfg(not(feature = "jit"))]
404        {
405            Self {}
406        }
407    }
408}
409
410impl EvmFactory for RethEvmFactory {
411    type Types = evm2::BaseEvmTypes;
412    type SpecId = evm2::SpecId;
413
414    fn spec_id(&self, spec: evm2::SpecId) -> evm2::SpecId {
415        spec
416    }
417
418    #[cfg(feature = "std")]
419    fn precompile_cache_policy(&self, _spec: Self::SpecId) -> PrecompileCachePolicy {
420        PrecompileCachePolicy::All
421    }
422
423    fn tx_registry(
424        &self,
425        spec: Self::SpecId,
426    ) -> evm2::registry::TxRegistry<Self::Types, evm2::TxResult<Self::Types>> {
427        evm2::ethereum::ethereum_tx_registry(spec)
428    }
429
430    fn configure_evm(&self, evm: &mut evm2::Evm<'_, Self::Types>) {
431        #[cfg(feature = "jit")]
432        self.configure_evm(evm);
433
434        #[cfg(not(feature = "jit"))]
435        let _ = evm;
436    }
437}
438
439#[cfg(feature = "jit")]
440impl RethEvmFactory {
441    /// Creates a new factory configuration that owns the backend.
442    pub fn new(backend: JitBackend) -> Self {
443        Self::new_with_metrics(backend, JitMetrics::default())
444    }
445
446    /// Creates a new factory configuration that owns the backend and records metrics.
447    pub const fn new_with_metrics(backend: JitBackend, metrics: JitMetrics) -> Self {
448        Self { backend, metrics, jit_support: false }
449    }
450
451    /// Returns a reference to the JIT backend.
452    pub const fn backend(&self) -> &JitBackend {
453        &self.backend
454    }
455
456    /// Enables or disables local JIT support for subsequently created EVMs.
457    pub const fn set_jit_support(&mut self, enabled: bool) {
458        self.jit_support = enabled;
459    }
460
461    /// Returns whether subsequently created EVMs install the JIT interpreter runner.
462    pub const fn jit_support_enabled(&self) -> bool {
463        self.jit_support
464    }
465
466    /// Installs the evm2 JIT interpreter runner on a configured EVM if locally enabled.
467    fn configure_evm(&self, evm: &mut evm2::Evm<'_, evm2::BaseEvmTypes>) {
468        if self.jit_support_enabled() {
469            evm.set_interpreter_runner(evm2_jit::evm2_evm::JitInterpreterRunner::new(
470                self.backend.clone(),
471            ));
472        }
473    }
474
475    /// Pauses JIT helper execution while keeping queueing and resident lookups enabled.
476    fn pause_jit(&self) {
477        let was_paused = self.backend.is_paused();
478        self.backend.pause();
479        let is_paused = self.backend.is_paused();
480        if !was_paused && is_paused {
481            self.metrics.pauses_total.increment(1);
482        }
483        self.metrics.paused.set(is_paused as u8 as f64);
484    }
485
486    /// Resumes background JIT promotion.
487    fn resume_jit(&self) {
488        let was_paused = self.backend.is_paused();
489        self.backend.resume();
490        let is_paused = self.backend.is_paused();
491        if was_paused && !is_paused {
492            self.metrics.resumes_total.increment(1);
493        }
494        self.metrics.paused.set(is_paused as u8 as f64);
495    }
496}
497
498#[cfg(feature = "jit")]
499impl reth_evm::JitBackend for RethEvmFactory {
500    fn set_enabled(&self, enabled: bool) -> Result<(), String> {
501        self.backend.set_enabled(enabled).map_err(|err| err.to_string())
502    }
503
504    fn pause(&self) {
505        self.pause_jit();
506    }
507
508    fn resume(&self) {
509        self.resume_jit();
510    }
511
512    fn clear(&self) {
513        self.backend.clear_all();
514    }
515}
516
517/// Prometheus metrics for evm2 JIT runtime stats.
518#[cfg(feature = "jit")]
519#[derive(reth_metrics::Metrics, Clone)]
520#[metrics(scope = "evm2.jit")]
521pub struct JitMetrics {
522    /// Total lookups that returned a compiled function.
523    pub lookup_hits: metrics::Gauge,
524    /// Total lookups that returned interpret (not ready).
525    pub lookup_misses: metrics::Gauge,
526    /// Lookup-observed events currently queued.
527    pub events_queued: metrics::Gauge,
528    /// Lookup-observed events dropped due to event queue overflow.
529    pub events_dropped: metrics::Gauge,
530    /// Control commands dropped because the command channel was full.
531    pub commands_dropped: metrics::Gauge,
532    /// Number of entries in the resident compiled map.
533    pub resident_entries: metrics::Gauge,
534    /// Approximate total bytes of compiled machine code in the resident map.
535    pub jit_code_bytes: metrics::Gauge,
536    /// Approximate total bytes of JIT-related data.
537    pub jit_data_bytes: metrics::Gauge,
538    /// Number of pending control commands queued for the backend.
539    pub command_queue_len: metrics::Gauge,
540    /// Number of compilation jobs dispatched but not completed yet.
541    pub pending_jobs: metrics::Gauge,
542    /// Total number of entries evicted.
543    pub evictions: metrics::Gauge,
544    /// Total number of compilations dispatched.
545    pub compilations_dispatched: metrics::Gauge,
546    /// Total number of successful compilations.
547    pub compilations_succeeded: metrics::Gauge,
548    /// Total number of failed compilations.
549    pub compilations_failed: metrics::Gauge,
550    /// Total compilation results discarded because the resident cache was cleared while they ran.
551    pub compilations_discarded: metrics::Gauge,
552    /// Total number of frames or hosts that fell back to the interpreter.
553    pub interpreter_fallbacks: metrics::Gauge,
554    /// Total number of JIT helper processes spawned.
555    pub jit_helper_spawns: metrics::Gauge,
556    /// Total number of JIT helper process spawn failures.
557    pub jit_helper_spawn_failures: metrics::Gauge,
558    /// Total number of JIT helper process restarts.
559    pub jit_helper_restarts: metrics::Gauge,
560    /// Total number of JIT helper job timeouts.
561    pub jit_helper_timeouts: metrics::Gauge,
562    /// Total number of JIT helper process disconnects.
563    pub jit_helper_disconnects: metrics::Gauge,
564    /// Total number of JIT helper pause requests.
565    pub jit_helper_pause_requests: metrics::Gauge,
566    /// Total number of JIT helper pause acknowledgements.
567    pub jit_helper_pause_acknowledgements: metrics::Gauge,
568    /// Total number of JIT helper pause failures.
569    pub jit_helper_pause_failures: metrics::Gauge,
570    /// Total number of JIT helper pause acknowledgement timeouts.
571    pub jit_helper_pause_timeouts: metrics::Gauge,
572    /// Total number of JIT helper resume requests.
573    pub jit_helper_resume_requests: metrics::Gauge,
574    /// Total number of JIT helper resume failures.
575    pub jit_helper_resume_failures: metrics::Gauge,
576    /// Total number of transitions into paused JIT helper execution.
577    pub pauses_total: metrics::Counter,
578    /// Total number of transitions out of paused JIT helper execution.
579    pub resumes_total: metrics::Counter,
580    /// Whether JIT helper execution is currently paused.
581    pub paused: metrics::Gauge,
582    /// Histogram of total JIT compilation durations in seconds.
583    pub jit_compilation_duration: metrics::Histogram,
584    /// Duration of the last JIT compilation in seconds.
585    pub jit_compilation_duration_last: metrics::Gauge,
586    /// Histogram of parse phase durations in seconds.
587    pub jit_parse_duration: metrics::Histogram,
588    /// Histogram of translate phase durations in seconds.
589    pub jit_translate_duration: metrics::Histogram,
590    /// Histogram of optimize phase durations in seconds.
591    pub jit_optimize_duration: metrics::Histogram,
592    /// Histogram of codegen phase durations in seconds.
593    pub jit_codegen_duration: metrics::Histogram,
594}
595
596#[cfg(feature = "jit")]
597impl JitMetrics {
598    /// Records a [`RuntimeStatsSnapshot`] into the metrics.
599    pub fn record(&self, stats: &RuntimeStatsSnapshot) {
600        let RuntimeStatsSnapshot {
601            lookup_hits,
602            lookup_misses,
603            events_dropped,
604            commands_dropped,
605            resident_entries,
606            events_queued,
607            command_queue_len,
608            pending_jobs,
609            jit_code_bytes,
610            jit_data_bytes,
611            evictions,
612            compilations_dispatched,
613            compilations_succeeded,
614            compilations_failed,
615            compilations_discarded,
616            interpreter_fallbacks,
617            jit_helper_spawns,
618            jit_helper_spawn_failures,
619            jit_helper_restarts,
620            jit_helper_timeouts,
621            jit_helper_disconnects,
622            jit_helper_pause_requests,
623            jit_helper_pause_acknowledgements,
624            jit_helper_pause_failures,
625            jit_helper_pause_timeouts,
626            jit_helper_resume_requests,
627            jit_helper_resume_failures,
628        } = *stats;
629        self.lookup_hits.set(lookup_hits as f64);
630        self.lookup_misses.set(lookup_misses as f64);
631        self.events_queued.set(events_queued as f64);
632        self.events_dropped.set(events_dropped as f64);
633        self.commands_dropped.set(commands_dropped as f64);
634        self.resident_entries.set(resident_entries as f64);
635        self.jit_code_bytes.set(jit_code_bytes as f64);
636        self.jit_data_bytes.set(jit_data_bytes as f64);
637        self.command_queue_len.set(command_queue_len as f64);
638        self.pending_jobs.set(pending_jobs as f64);
639        self.evictions.set(evictions as f64);
640        self.compilations_dispatched.set(compilations_dispatched as f64);
641        self.compilations_succeeded.set(compilations_succeeded as f64);
642        self.compilations_failed.set(compilations_failed as f64);
643        self.compilations_discarded.set(compilations_discarded as f64);
644        self.interpreter_fallbacks.set(interpreter_fallbacks as f64);
645        self.jit_helper_spawns.set(jit_helper_spawns as f64);
646        self.jit_helper_spawn_failures.set(jit_helper_spawn_failures as f64);
647        self.jit_helper_restarts.set(jit_helper_restarts as f64);
648        self.jit_helper_timeouts.set(jit_helper_timeouts as f64);
649        self.jit_helper_disconnects.set(jit_helper_disconnects as f64);
650        self.jit_helper_pause_requests.set(jit_helper_pause_requests as f64);
651        self.jit_helper_pause_acknowledgements.set(jit_helper_pause_acknowledgements as f64);
652        self.jit_helper_pause_failures.set(jit_helper_pause_failures as f64);
653        self.jit_helper_pause_timeouts.set(jit_helper_pause_timeouts as f64);
654        self.jit_helper_resume_requests.set(jit_helper_resume_requests as f64);
655        self.jit_helper_resume_failures.set(jit_helper_resume_failures as f64);
656    }
657
658    /// Records a [`CompilationEvent`] into the histogram metrics.
659    pub fn record_compilation(&self, event: &CompilationEvent) {
660        let duration_secs = event.duration.as_secs_f64();
661        self.jit_compilation_duration.record(duration_secs);
662        self.jit_compilation_duration_last.set(duration_secs);
663        self.jit_parse_duration.record(event.timings.parse.as_secs_f64());
664        self.jit_translate_duration.record(event.timings.translate.as_secs_f64());
665        self.jit_optimize_duration.record(event.timings.optimize.as_secs_f64());
666        self.jit_codegen_duration.record(event.timings.codegen.as_secs_f64());
667    }
668}
669
670#[cfg(all(test, feature = "std"))]
671mod tests {
672    use super::*;
673    use alloy_primitives::U256;
674    use core::any::TypeId;
675    use evm2::{
676        evm::{EmptyDB, NonStaticAny},
677        BaseEvmTypes, SpecId,
678    };
679
680    #[test]
681    fn reconfigure_keeps_precompile_cache_within_spec() {
682        let factory = EthBlockExecutorFactory::new(Arc::new(ChainSpec::default()));
683        let has_cache = |evm: &evm2::Evm<'_, BaseEvmTypes>| {
684            let precompiles: &dyn NonStaticAny = &**evm.precompiles();
685            precompiles.type_id() == TypeId::of::<CachedPrecompileProvider<BaseEvmTypes, SpecId>>()
686        };
687        let env = |spec, number: u64| {
688            let mut env = EthEvmEnv::<BaseEvmTypes>::new(spec, Default::default(), 1);
689            env.block.number = U256::from(number);
690            env
691        };
692
693        let mut evm = factory.build_evm_with_precompile_cache(
694            EmptyDB::default(),
695            env(SpecId::OSAKA, 1),
696            PrecompileCacheMap::default(),
697            false,
698        );
699        factory.reconfigure_evm(&mut evm, &env(SpecId::OSAKA, 2));
700        assert!(has_cache(&evm));
701        assert_eq!(evm.block().number, U256::from(2));
702
703        factory.reconfigure_evm(&mut evm, &env(SpecId::AMSTERDAM, 3));
704        assert!(!has_cache(&evm));
705        assert_eq!(evm.config_spec_id(), SpecId::AMSTERDAM);
706    }
707}