Skip to main content

alloy_evm/
precompiles.rs

1//! Helpers for dealing with Precompiles.
2
3use crate::{Database, EvmInternals};
4use alloc::{borrow::Cow, boxed::Box, string::String, sync::Arc, vec::Vec};
5use alloy_consensus::transaction::Either;
6use alloy_primitives::{
7    map::{AddressMap, AddressSet},
8    Address, Bytes, U256,
9};
10use core::fmt::{self, Debug, Display};
11use revm::{
12    context::{ContextTr, LocalContextTr},
13    handler::{EthPrecompiles, PrecompileProvider},
14    interpreter::{CallInput, CallInputs, Gas, InstructionResult, InterpreterResult},
15    precompile::{PrecompileError, PrecompileFn, PrecompileId, PrecompileResult, Precompiles},
16    Context, Journal,
17};
18
19/// Returns whether the given [`PrecompileId`] supports caching.
20///
21/// This returns `false` for precompiles where the cost of computing a cache key (hashing the
22/// input) is comparable to just re-executing the precompile (e.g., the identity precompile which
23/// simply copies input to output).
24const fn precompile_id_supports_caching(id: &PrecompileId) -> bool {
25    !matches!(id, PrecompileId::Identity)
26}
27
28/// A mapping of precompile contracts that can be either static (builtin) or dynamic.
29///
30/// This is an optimization that allows us to keep using the static precompiles
31/// until we need to modify them, at which point we convert to the dynamic representation.
32#[derive(Clone)]
33pub struct PrecompilesMap {
34    /// The wrapped precompiles in their current representation.
35    precompiles: PrecompilesKind,
36    /// An optional dynamic precompile loader that can lookup precompiles dynamically.
37    lookup: Option<Arc<dyn PrecompileLookup>>,
38}
39
40impl PrecompilesMap {
41    /// Creates the [`PrecompilesMap`] from a static reference.
42    pub fn from_static(precompiles: &'static Precompiles) -> Self {
43        Self::new(Cow::Borrowed(precompiles))
44    }
45
46    /// Creates a new set of precompiles for a spec.
47    pub fn new(precompiles: Cow<'static, Precompiles>) -> Self {
48        Self { precompiles: PrecompilesKind::Builtin(precompiles), lookup: None }
49    }
50
51    /// Maps a precompile at the given address using the provided function.
52    pub fn map_precompile<F>(&mut self, address: &Address, f: F)
53    where
54        F: FnOnce(DynPrecompile) -> DynPrecompile + Send + Sync + 'static,
55    {
56        let dyn_precompiles = self.ensure_dynamic_precompiles();
57
58        // get the current precompile at the address
59        if let Some(dyn_precompile) = dyn_precompiles.inner.remove(address) {
60            // apply the transformation function
61            let transformed = f(dyn_precompile);
62
63            // update the precompile at the address
64            dyn_precompiles.inner.insert(*address, transformed);
65        }
66    }
67
68    /// Maps all precompiles using the provided function.
69    pub fn map_precompiles<F>(&mut self, f: F)
70    where
71        F: FnMut(&Address, DynPrecompile) -> DynPrecompile,
72    {
73        self.map_precompiles_filtered(f, |_, _| true);
74    }
75
76    /// Maps all cacheable precompiles using the provided function.
77    ///
78    /// This is a variant of [`Self::map_precompiles`] that only applies the transformation
79    /// to precompiles that support caching, see [`Precompile::supports_caching`].
80    pub fn map_cacheable_precompiles<F>(&mut self, f: F)
81    where
82        F: FnMut(&Address, DynPrecompile) -> DynPrecompile,
83    {
84        self.map_precompiles_filtered(f, |_, precompile| precompile.supports_caching());
85    }
86
87    /// Internal helper to map precompiles with an optional filter.
88    ///
89    /// The `filter` decides whether to apply the mapping function `f` to a given
90    /// precompile. If the filter returns `false`, the original precompile is kept.
91    #[inline]
92    fn map_precompiles_filtered<F, P>(&mut self, mut f: F, mut filter: P)
93    where
94        F: FnMut(&Address, DynPrecompile) -> DynPrecompile,
95        P: FnMut(&Address, &DynPrecompile) -> bool,
96    {
97        let dyn_precompiles = self.ensure_dynamic_precompiles();
98
99        // apply the transformation to each precompile
100        let entries = dyn_precompiles.inner.drain();
101        let mut new_map =
102            AddressMap::with_capacity_and_hasher(entries.size_hint().0, Default::default());
103        for (addr, precompile) in entries {
104            if filter(&addr, &precompile) {
105                let transformed = f(&addr, precompile);
106                new_map.insert(addr, transformed);
107            } else {
108                new_map.insert(addr, precompile);
109            }
110        }
111
112        dyn_precompiles.inner = new_map;
113    }
114
115    /// Applies a transformation to the precompile at the given address.
116    ///
117    /// This method allows you to add, update, or remove a precompile by applying a closure
118    /// to the existing precompile (if any) at the specified address.
119    ///
120    /// # Behavior
121    ///
122    /// The closure receives:
123    /// - `Some(precompile)` if a precompile exists at the address
124    /// - `None` if no precompile exists at the address
125    ///
126    /// Based on what the closure returns:
127    /// - `Some(precompile)` - Insert or replace the precompile at the address
128    /// - `None` - Remove the precompile from the address (if it exists)
129    ///
130    /// # Examples
131    ///
132    /// ```ignore
133    /// // Add a new precompile
134    /// precompiles.apply_precompile(&address, |_| Some(my_precompile));
135    ///
136    /// // Update an existing precompile
137    /// precompiles.apply_precompile(&address, |existing| {
138    ///     existing.map(|p| wrap_with_logging(p))
139    /// });
140    ///
141    /// // Remove a precompile
142    /// precompiles.apply_precompile(&address, |_| None);
143    ///
144    /// // Conditionally update
145    /// precompiles.apply_precompile(&address, |existing| {
146    ///     if let Some(p) = existing {
147    ///         Some(modify_precompile(p))
148    ///     } else {
149    ///         Some(create_default_precompile())
150    ///     }
151    /// });
152    /// ```
153    pub fn apply_precompile<F>(&mut self, address: &Address, f: F)
154    where
155        F: FnOnce(Option<DynPrecompile>) -> Option<DynPrecompile>,
156    {
157        let dyn_precompiles = self.ensure_dynamic_precompiles();
158        let current = dyn_precompiles.inner.get(address).cloned();
159
160        // apply the transformation function
161        let result = f(current);
162
163        match result {
164            Some(transformed) => {
165                // insert the transformed precompile
166                dyn_precompiles.inner.insert(*address, transformed);
167                dyn_precompiles.addresses.insert(*address);
168            }
169            None => {
170                // remove the precompile if the transformation returned None
171                dyn_precompiles.inner.remove(address);
172                dyn_precompiles.addresses.remove(address);
173            }
174        }
175    }
176
177    /// Builder-style method that maps a precompile at the given address using the provided
178    /// function.
179    ///
180    /// This is a consuming version of [`map_precompile`](Self::map_precompile) that returns `Self`.
181    pub fn with_mapped_precompile<F>(mut self, address: &Address, f: F) -> Self
182    where
183        F: FnOnce(DynPrecompile) -> DynPrecompile + Send + Sync + 'static,
184    {
185        self.map_precompile(address, f);
186        self
187    }
188
189    /// Builder-style method that maps all precompiles using the provided function.
190    ///
191    /// This is a consuming version of [`map_precompiles`](Self::map_precompiles) that returns
192    /// `Self`.
193    pub fn with_mapped_precompiles<F>(mut self, f: F) -> Self
194    where
195        F: FnMut(&Address, DynPrecompile) -> DynPrecompile,
196    {
197        self.map_precompiles(f);
198        self
199    }
200
201    /// Builder-style method that applies a transformation to the precompile at the given address.
202    ///
203    /// This is a consuming version of [`apply_precompile`](Self::apply_precompile) that returns
204    /// `Self`. See [`apply_precompile`](Self::apply_precompile) for detailed behavior and
205    /// examples.
206    pub fn with_applied_precompile<F>(mut self, address: &Address, f: F) -> Self
207    where
208        F: FnOnce(Option<DynPrecompile>) -> Option<DynPrecompile>,
209    {
210        self.apply_precompile(address, f);
211        self
212    }
213
214    /// Extends the precompile map with multiple precompiles.
215    ///
216    /// This is a convenience method for inserting or replacing multiple precompiles at once.
217    /// Each precompile in the iterator is applied to its corresponding address.
218    ///
219    /// **Note**: This method will **replace** any existing precompiles at the given addresses.
220    /// If you need to modify existing precompiles, use [`map_precompile`](Self::map_precompile)
221    /// or [`apply_precompile`](Self::apply_precompile) instead.
222    ///
223    /// # Examples
224    ///
225    /// ```ignore
226    /// let precompiles = vec![
227    ///     (address1, my_precompile1),
228    ///     (address2, my_precompile2),
229    /// ];
230    /// precompiles_map.extend_precompiles(precompiles);
231    /// ```
232    pub fn extend_precompiles<I>(&mut self, precompiles: I)
233    where
234        I: IntoIterator<Item = (Address, DynPrecompile)>,
235    {
236        for (addr, precompile) in precompiles {
237            self.apply_precompile(&addr, |_| Some(precompile));
238        }
239    }
240
241    /// Builder-style method that extends the precompile map with multiple precompiles.
242    ///
243    /// This is a consuming version of [`extend_precompiles`](Self::extend_precompiles) that returns
244    /// `Self`.
245    ///
246    /// **Note**: This method will **replace** any existing precompiles at the given addresses.
247    /// If you need to modify existing precompiles, use
248    /// [`with_mapped_precompile`](Self::with_mapped_precompile)
249    /// or [`with_applied_precompile`](Self::with_applied_precompile) instead.
250    ///
251    /// # Examples
252    ///
253    /// ```ignore
254    /// let precompiles = vec![
255    ///     (address1, my_precompile1),
256    ///     (address2, my_precompile2),
257    /// ];
258    /// let map = PrecompilesMap::new(precompiles_cow)
259    ///     .with_extended_precompiles(precompiles);
260    /// ```
261    pub fn with_extended_precompiles<I>(mut self, precompiles: I) -> Self
262    where
263        I: IntoIterator<Item = (Address, DynPrecompile)>,
264    {
265        self.extend_precompiles(precompiles);
266        self
267    }
268
269    /// Moves precompiles from source addresses to destination addresses.
270    ///
271    /// For each `(source, dest)` pair in the iterator:
272    /// - If `source == dest`, the pair is skipped (no-op).
273    /// - If the source address is not a precompile, returns an error.
274    /// - Otherwise, the precompile is removed from the source address and installed at the
275    ///   destination address.
276    ///
277    /// # Errors
278    ///
279    /// Returns [`MovePrecompileError::NotAPrecompile`] if any source address does not have a
280    /// precompile installed.
281    ///
282    /// # Examples
283    ///
284    /// ```ignore
285    /// // Move ECRECOVER from 0x01 to a custom address
286    /// let moves = [(
287    ///     address!("0x0000000000000000000000000000000000000001"),
288    ///     address!("0x0000000000000000000000000000000000000100"),
289    /// )];
290    /// precompiles.move_precompiles(moves)?;
291    /// ```
292    pub fn move_precompiles<I>(&mut self, moves: I) -> Result<(), MovePrecompileError>
293    where
294        I: IntoIterator<Item = (Address, Address)>,
295    {
296        let moves: Vec<_> = moves.into_iter().filter(|(src, dest)| src != dest).collect();
297
298        if moves.is_empty() {
299            return Ok(());
300        }
301
302        // Validate all source addresses are precompiles before making any changes
303        for (source, _dest) in &moves {
304            if self.get(source).is_none() {
305                return Err(MovePrecompileError::NotAPrecompile(*source));
306            }
307        }
308
309        // Extract precompiles from source addresses
310        let mut extracted: Vec<(Address, DynPrecompile)> = Vec::with_capacity(moves.len());
311
312        for (source, dest) in moves {
313            let mut found_precompile: Option<DynPrecompile> = None;
314            self.apply_precompile(&source, |existing| {
315                found_precompile = existing;
316                None
317            });
318
319            if let Some(precompile) = found_precompile {
320                extracted.push((dest, precompile));
321            }
322        }
323
324        // Install precompiles at destination addresses
325        for (dest, precompile) in extracted {
326            self.apply_precompile(&dest, |_| Some(precompile));
327        }
328
329        Ok(())
330    }
331
332    /// Builder-style method that moves precompiles from source addresses to destination addresses.
333    ///
334    /// This is a consuming version of [`move_precompiles`](Self::move_precompiles) that returns
335    /// `Self` on success.
336    ///
337    /// # Errors
338    ///
339    /// Returns [`MovePrecompileError::NotAPrecompile`] if any source address does not have a
340    /// precompile installed.
341    ///
342    /// # Examples
343    ///
344    /// ```ignore
345    /// let moves = [(
346    ///     address!("0x0000000000000000000000000000000000000001"),
347    ///     address!("0x0000000000000000000000000000000000000100"),
348    /// )];
349    /// let map = PrecompilesMap::new(precompiles_cow)
350    ///     .with_moved_precompiles(moves)?;
351    /// ```
352    pub fn with_moved_precompiles<I>(mut self, moves: I) -> Result<Self, MovePrecompileError>
353    where
354        I: IntoIterator<Item = (Address, Address)>,
355    {
356        self.move_precompiles(moves)?;
357        Ok(self)
358    }
359
360    /// Sets a dynamic precompile lookup function that is called for addresses not found
361    /// in the static precompile map.
362    ///
363    /// This method allows you to provide runtime-resolved precompiles that aren't known
364    /// at initialization time. The lookup function is called whenever a precompile check
365    /// is performed for an address that doesn't exist in the main precompile map.
366    ///
367    /// # Important Notes
368    ///
369    /// - **Priority**: Static precompiles take precedence. The lookup function is only called if
370    ///   the address is not found in the main precompile map.
371    /// - **Gas accounting**: Addresses resolved through this lookup are always treated as cold,
372    ///   meaning they incur cold access costs even on repeated calls within the same transaction.
373    ///   See also [`PrecompileProvider::warm_addresses`].
374    /// - **Performance**: The lookup function is called on every precompile check for
375    ///   non-registered addresses, so it should be efficient.
376    ///
377    /// # Example
378    ///
379    /// ```ignore
380    /// precompiles.set_precompile_lookup(|address| {
381    ///     // Dynamically resolve precompiles based on address pattern
382    ///     if address.as_slice().starts_with(&[0xDE, 0xAD]) {
383    ///         Some(DynPrecompile::new(|input| {
384    ///             // Custom precompile logic
385    ///             Ok(PrecompileOutput {
386    ///                 gas_used: 100,
387    ///                 bytes: Bytes::from("dynamic precompile"),
388    ///             })
389    ///         }))
390    ///     } else {
391    ///         None
392    ///     }
393    /// });
394    /// ```
395    pub fn set_precompile_lookup<L>(&mut self, lookup: L)
396    where
397        L: PrecompileLookup + 'static,
398    {
399        self.lookup = Some(Arc::new(lookup));
400    }
401
402    /// Builder-style method to set a dynamic precompile lookup function.
403    ///
404    /// This is a consuming version of [`set_precompile_lookup`](Self::set_precompile_lookup)
405    /// that returns `Self` for method chaining.
406    ///
407    /// See [`set_precompile_lookup`](Self::set_precompile_lookup) for detailed behavior,
408    /// important notes, and examples.
409    pub fn with_precompile_lookup<L>(mut self, lookup: L) -> Self
410    where
411        L: PrecompileLookup + 'static,
412    {
413        self.set_precompile_lookup(lookup);
414        self
415    }
416
417    /// Consumes the type and returns a set of [`DynPrecompile`].
418    pub fn into_dyn_precompiles(mut self) -> DynPrecompiles {
419        self.ensure_dynamic_precompiles();
420        match self.precompiles {
421            PrecompilesKind::Dynamic(dynamic) => dynamic,
422            _ => unreachable!("We just ensured that this is a Dynamic variant"),
423        }
424    }
425
426    /// Ensures that precompiles are in their dynamic representation.
427    /// If they are already dynamic, this is a no-op.
428    /// Returns a mutable reference to the dynamic precompiles.
429    pub fn ensure_dynamic_precompiles(&mut self) -> &mut DynPrecompiles {
430        if let PrecompilesKind::Builtin(ref precompiles_cow) = self.precompiles {
431            let mut dynamic = DynPrecompiles::default();
432
433            let static_precompiles = match precompiles_cow {
434                Cow::Borrowed(static_ref) => static_ref,
435                Cow::Owned(owned) => owned,
436            };
437
438            for (&addr, pc) in static_precompiles.inner().iter() {
439                dynamic.inner.insert(
440                    addr,
441                    DynPrecompile::from((pc.precompile_id().clone(), *pc.precompile())),
442                );
443                dynamic.addresses.insert(addr);
444            }
445
446            self.precompiles = PrecompilesKind::Dynamic(dynamic);
447        }
448
449        match &mut self.precompiles {
450            PrecompilesKind::Dynamic(dynamic) => dynamic,
451            _ => unreachable!("We just ensured that this is a Dynamic variant"),
452        }
453    }
454
455    /// Returns an iterator over the [`PrecompileId`]s of the installed precompiles.
456    pub fn identifiers(&self) -> impl Iterator<Item = &PrecompileId> {
457        match &self.precompiles {
458            PrecompilesKind::Builtin(precompiles) => {
459                Either::Left(precompiles.inner().values().map(|p| p.precompile_id()))
460            }
461            PrecompilesKind::Dynamic(dyn_precompiles) => {
462                Either::Right(dyn_precompiles.inner.values().map(|p| p.precompile_id()))
463            }
464        }
465    }
466
467    /// Returns an iterator over references to precompile addresses.
468    pub fn addresses(&self) -> impl Iterator<Item = &Address> {
469        match &self.precompiles {
470            PrecompilesKind::Builtin(precompiles) => Either::Left(precompiles.addresses()),
471            PrecompilesKind::Dynamic(dyn_precompiles) => {
472                Either::Right(dyn_precompiles.addresses.iter())
473            }
474        }
475    }
476
477    /// Gets a reference to the precompile at the given address.
478    ///
479    /// This method first checks the static precompile map, and if not found,
480    /// falls back to the dynamic lookup function (if set).
481    pub fn get(&self, address: &Address) -> Option<impl Precompile + '_> {
482        // First check static precompiles
483        let static_result = match &self.precompiles {
484            PrecompilesKind::Builtin(precompiles) => precompiles.get(address).map(Either::Left),
485            PrecompilesKind::Dynamic(dyn_precompiles) => {
486                dyn_precompiles.inner.get(address).map(Either::Right)
487            }
488        };
489
490        // If found in static precompiles, wrap in Left and return
491        if let Some(precompile) = static_result {
492            return Some(Either::Left(precompile));
493        }
494
495        // Otherwise, try the lookup function if available
496        let lookup = self.lookup.as_ref()?;
497        lookup.lookup(address).map(Either::Right)
498    }
499}
500
501impl From<EthPrecompiles> for PrecompilesMap {
502    fn from(value: EthPrecompiles) -> Self {
503        Self::from_static(value.precompiles)
504    }
505}
506
507impl core::fmt::Debug for PrecompilesMap {
508    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
509        match &self.precompiles {
510            PrecompilesKind::Builtin(_) => f.debug_struct("PrecompilesMap::Builtin").finish(),
511            PrecompilesKind::Dynamic(precompiles) => f
512                .debug_struct("PrecompilesMap::Dynamic")
513                .field("addresses", &precompiles.addresses)
514                .finish(),
515        }
516    }
517}
518
519impl<BlockEnv, TxEnv, CfgEnv, DB, Chain>
520    PrecompileProvider<Context<BlockEnv, TxEnv, CfgEnv, DB, Journal<DB>, Chain>> for PrecompilesMap
521where
522    BlockEnv: revm::context::Block,
523    TxEnv: revm::context::Transaction,
524    CfgEnv: revm::context::Cfg,
525    DB: Database,
526{
527    type Output = InterpreterResult;
528
529    fn set_spec(&mut self, _spec: CfgEnv::Spec) -> bool {
530        false
531    }
532
533    fn run(
534        &mut self,
535        context: &mut Context<BlockEnv, TxEnv, CfgEnv, DB, Journal<DB>, Chain>,
536        inputs: &CallInputs,
537    ) -> Result<Option<InterpreterResult>, String> {
538        // Get the precompile at the address
539        let Some(precompile) = self.get(&inputs.bytecode_address) else {
540            return Ok(None);
541        };
542
543        let mut result = InterpreterResult {
544            result: InstructionResult::Return,
545            gas: Gas::new(inputs.gas_limit),
546            output: Bytes::new(),
547        };
548
549        let (block, tx, cfg, journaled_state, _, local) = context.all_mut();
550
551        // Execute the precompile
552        let r;
553        let input_bytes = match &inputs.input {
554            CallInput::SharedBuffer(range) => {
555                // `map_or` does not work here as we use `r` to extend lifetime of the slice
556                // and return it.
557                #[allow(clippy::option_if_let_else)]
558                if let Some(slice) = local.shared_memory_buffer_slice(range.clone()) {
559                    r = slice;
560                    &*r
561                } else {
562                    &[]
563                }
564            }
565            CallInput::Bytes(bytes) => bytes.as_ref(),
566        };
567
568        let precompile_result = {
569            let _span =
570                tracing::debug_span!("precompile", name = precompile.precompile_id().name(),)
571                    .entered();
572            precompile.call(PrecompileInput {
573                data: input_bytes,
574                gas: inputs.gas_limit,
575                caller: inputs.caller,
576                value: inputs.call_value(),
577                is_static: inputs.is_static,
578                internals: EvmInternals::new(journaled_state, block, cfg, tx),
579                target_address: inputs.target_address,
580                bytecode_address: inputs.bytecode_address,
581            })
582        };
583
584        match precompile_result {
585            Ok(output) => {
586                let underflow = result.gas.record_cost(output.gas_used);
587                assert!(underflow, "Gas underflow is not possible");
588                result.result = if output.reverted {
589                    InstructionResult::Revert
590                } else {
591                    InstructionResult::Return
592                };
593                result.output = output.bytes;
594            }
595            Err(PrecompileError::Fatal(e)) => return Err(e),
596            Err(e) => {
597                result.result = if e.is_oog() {
598                    InstructionResult::PrecompileOOG
599                } else {
600                    InstructionResult::PrecompileError
601                };
602            }
603        };
604
605        Ok(Some(result))
606    }
607
608    fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>> {
609        Box::new(self.addresses().copied())
610    }
611
612    fn contains(&self, address: &Address) -> bool {
613        self.get(address).is_some()
614    }
615}
616
617/// A mapping of precompile contracts that can be either static (builtin) or dynamic.
618///
619/// This is an optimization that allows us to keep using the static precompiles
620/// until we need to modify them, at which point we convert to the dynamic representation.
621#[derive(Clone)]
622enum PrecompilesKind {
623    /// Static builtin precompiles.
624    Builtin(Cow<'static, Precompiles>),
625    /// Dynamic precompiles that can be modified at runtime.
626    Dynamic(DynPrecompiles),
627}
628
629/// A dynamic precompile implementation that can be modified at runtime.
630#[derive(Clone)]
631pub struct DynPrecompile(pub(crate) Arc<dyn Precompile + Send + Sync>);
632
633impl DynPrecompile {
634    /// Creates a new [`DynPrecompiles`] with the given closure.
635    pub fn new<F>(id: PrecompileId, f: F) -> Self
636    where
637        F: Fn(PrecompileInput<'_>) -> PrecompileResult + Send + Sync + 'static,
638    {
639        Self(Arc::new((id, f)))
640    }
641
642    /// Creates a new [`DynPrecompiles`] with the given closure and
643    /// [`Precompile::supports_caching`] returning `false`.
644    pub fn new_stateful<F>(id: PrecompileId, f: F) -> Self
645    where
646        F: Fn(PrecompileInput<'_>) -> PrecompileResult + Send + Sync + 'static,
647    {
648        Self(Arc::new(StatefulPrecompile((id, f))))
649    }
650
651    /// Flips [`Precompile::supports_caching`] to `false`.
652    pub fn stateful(self) -> Self {
653        Self(Arc::new(StatefulPrecompile(self.0)))
654    }
655}
656
657impl core::fmt::Debug for DynPrecompile {
658    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
659        f.debug_struct("DynPrecompile").finish()
660    }
661}
662
663/// A mutable representation of precompiles that allows for runtime modification.
664///
665/// This structure stores dynamic precompiles that can be modified at runtime,
666/// unlike the static `Precompiles` struct from revm.
667#[derive(Clone, Default)]
668pub struct DynPrecompiles {
669    /// Precompiles
670    inner: AddressMap<DynPrecompile>,
671    /// Addresses of precompile
672    addresses: AddressSet,
673}
674
675impl DynPrecompiles {
676    /// Consumes the type and returns an iterator over the addresses and the corresponding
677    /// precompile.
678    pub fn into_precompiles(self) -> impl Iterator<Item = (Address, DynPrecompile)> {
679        self.inner.into_iter()
680    }
681}
682
683impl core::fmt::Debug for DynPrecompiles {
684    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
685        f.debug_struct("DynPrecompiles").field("addresses", &self.addresses).finish()
686    }
687}
688
689/// Input for a precompile call.
690#[derive(Debug)]
691pub struct PrecompileInput<'a> {
692    /// Input data bytes.
693    pub data: &'a [u8],
694    /// Gas limit.
695    pub gas: u64,
696    /// Caller address.
697    pub caller: Address,
698    /// Value sent with the call.
699    pub value: U256,
700    /// Target address of the call. Would be the same as `bytecode_address` unless it's a
701    /// DELEGATECALL.
702    pub target_address: Address,
703    /// Whether this call is in a STATICCALL context.
704    pub is_static: bool,
705    /// Bytecode address of the call.
706    pub bytecode_address: Address,
707    /// Various hooks for interacting with the EVM state.
708    pub internals: EvmInternals<'a>,
709}
710
711impl<'a> PrecompileInput<'a> {
712    /// Returns the calldata of the call.
713    pub const fn data(&self) -> &[u8] {
714        self.data
715    }
716
717    /// Returns the caller address of the call.
718    pub const fn caller(&self) -> &Address {
719        &self.caller
720    }
721
722    /// Returns the gas limit of the call.
723    pub const fn gas(&self) -> u64 {
724        self.gas
725    }
726
727    /// Returns the value of the call.
728    pub const fn value(&self) -> &U256 {
729        &self.value
730    }
731
732    /// Returns the target address of the call.
733    pub const fn target_address(&self) -> &Address {
734        &self.target_address
735    }
736
737    /// Returns the bytecode address of the call.
738    pub const fn bytecode_address(&self) -> &Address {
739        &self.bytecode_address
740    }
741
742    /// Returns whether the call is a direct call, i.e when precompile was called directly and not
743    /// via a DELEGATECALL/CALLCODE.
744    pub fn is_direct_call(&self) -> bool {
745        self.target_address == self.bytecode_address
746    }
747
748    /// Returns whether this call is in a STATICCALL context.
749    pub const fn is_static_call(&self) -> bool {
750        self.is_static
751    }
752
753    /// Returns the [`EvmInternals`].
754    pub const fn internals(&self) -> &EvmInternals<'_> {
755        &self.internals
756    }
757
758    /// Returns a mutable reference to the [`EvmInternals`].
759    pub const fn internals_mut(&mut self) -> &mut EvmInternals<'a> {
760        &mut self.internals
761    }
762}
763
764/// Trait for implementing precompiled contracts.
765#[auto_impl::auto_impl(&, Arc)]
766pub trait Precompile {
767    /// Returns precompile ID.
768    fn precompile_id(&self) -> &PrecompileId;
769
770    /// Execute the precompile with the given input data, gas limit, and caller address.
771    fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult;
772
773    /// Returns whether this precompile's results should be cached.
774    ///
775    /// A cacheable precompile has deterministic output based solely on its input,
776    /// and the cost of execution is high enough that caching the result is beneficial.
777    ///
778    /// # Default
779    ///
780    /// Returns `true` by default, indicating the precompile supports caching,
781    /// as this is what most precompiles benefit from.
782    ///
783    /// # When to return `false`
784    ///
785    /// - **Non-deterministic precompiles**: If the output depends on state or external factors
786    /// - **Cheap precompiles**: If the cost of computing a cache key (hashing the input) is
787    ///   comparable to just re-executing the precompile (e.g., the identity precompile which simply
788    ///   copies input to output)
789    ///
790    /// # Examples
791    ///
792    /// ```ignore
793    /// impl Precompile for MyPrecompile {
794    ///     fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
795    ///         // ...
796    ///     }
797    ///
798    ///     fn supports_caching(&self) -> bool {
799    ///         false
800    ///     }
801    /// }
802    /// ```
803    fn supports_caching(&self) -> bool {
804        true
805    }
806}
807
808impl<F> Precompile for (PrecompileId, F)
809where
810    F: Fn(PrecompileInput<'_>) -> PrecompileResult + Send + Sync,
811{
812    fn precompile_id(&self) -> &PrecompileId {
813        &self.0
814    }
815
816    fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
817        self.1(input)
818    }
819
820    fn supports_caching(&self) -> bool {
821        precompile_id_supports_caching(&self.0)
822    }
823}
824
825impl<F> Precompile for (&PrecompileId, F)
826where
827    F: Fn(PrecompileInput<'_>) -> PrecompileResult + Send + Sync,
828{
829    fn precompile_id(&self) -> &PrecompileId {
830        self.0
831    }
832
833    fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
834        self.1(input)
835    }
836
837    fn supports_caching(&self) -> bool {
838        precompile_id_supports_caching(self.0)
839    }
840}
841
842impl Precompile for revm::precompile::Precompile {
843    fn precompile_id(&self) -> &PrecompileId {
844        self.id()
845    }
846
847    fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
848        self.precompile()(input.data, input.gas)
849    }
850
851    fn supports_caching(&self) -> bool {
852        precompile_id_supports_caching(self.id())
853    }
854}
855
856impl<F> From<F> for DynPrecompile
857where
858    F: Fn(PrecompileInput<'_>) -> PrecompileResult + Send + Sync + 'static,
859{
860    fn from(f: F) -> Self {
861        Self::new(PrecompileId::Custom("closure".into()), f)
862    }
863}
864
865impl From<PrecompileFn> for DynPrecompile {
866    fn from(f: PrecompileFn) -> Self {
867        let p = move |input: PrecompileInput<'_>| f(input.data, input.gas);
868        p.into()
869    }
870}
871
872impl<F> From<(PrecompileId, F)> for DynPrecompile
873where
874    F: Fn(PrecompileInput<'_>) -> PrecompileResult + Send + Sync + 'static,
875{
876    fn from((id, f): (PrecompileId, F)) -> Self {
877        Self(Arc::new((id, f)))
878    }
879}
880
881impl From<(PrecompileId, PrecompileFn)> for DynPrecompile {
882    fn from((id, f): (PrecompileId, PrecompileFn)) -> Self {
883        let p = move |input: PrecompileInput<'_>| f(input.data, input.gas);
884        (id, p).into()
885    }
886}
887
888impl Precompile for DynPrecompile {
889    fn precompile_id(&self) -> &PrecompileId {
890        self.0.precompile_id()
891    }
892
893    fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
894        self.0.call(input)
895    }
896
897    fn supports_caching(&self) -> bool {
898        self.0.supports_caching()
899    }
900}
901
902impl<A: Precompile, B: Precompile> Precompile for Either<A, B> {
903    fn precompile_id(&self) -> &PrecompileId {
904        match self {
905            Self::Left(p) => p.precompile_id(),
906            Self::Right(p) => p.precompile_id(),
907        }
908    }
909
910    fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
911        match self {
912            Self::Left(p) => p.call(input),
913            Self::Right(p) => p.call(input),
914        }
915    }
916
917    fn supports_caching(&self) -> bool {
918        match self {
919            Self::Left(p) => p.supports_caching(),
920            Self::Right(p) => p.supports_caching(),
921        }
922    }
923}
924
925struct StatefulPrecompile<P>(P);
926
927impl<P: Precompile> Precompile for StatefulPrecompile<P> {
928    fn precompile_id(&self) -> &PrecompileId {
929        self.0.precompile_id()
930    }
931
932    fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
933        self.0.call(input)
934    }
935
936    fn supports_caching(&self) -> bool {
937        false
938    }
939}
940
941/// Trait for dynamically resolving precompile contracts.
942///
943/// This trait allows for runtime resolution of precompiles that aren't known
944/// at initialization time.
945pub trait PrecompileLookup: Send + Sync {
946    /// Looks up a precompile at the given address.
947    ///
948    /// Returns `Some(precompile)` if a precompile exists at the address,
949    /// or `None` if no precompile is found.
950    fn lookup(&self, address: &Address) -> Option<DynPrecompile>;
951}
952
953/// Implement PrecompileLookup for closure types
954impl<F> PrecompileLookup for F
955where
956    F: Fn(&Address) -> Option<DynPrecompile> + Send + Sync,
957{
958    fn lookup(&self, address: &Address) -> Option<DynPrecompile> {
959        self(address)
960    }
961}
962
963/// Error that can occur when moving precompiles.
964#[derive(Clone, Debug, PartialEq, Eq)]
965pub enum MovePrecompileError {
966    /// The source address is not a precompile.
967    NotAPrecompile(Address),
968}
969
970impl Display for MovePrecompileError {
971    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
972        match self {
973            Self::NotAPrecompile(addr) => {
974                write!(f, "source address {addr} is not a precompile")
975            }
976        }
977    }
978}
979
980impl core::error::Error for MovePrecompileError {}
981
982#[cfg(test)]
983mod tests {
984    use super::*;
985    use crate::eth::EthEvmContext;
986    use alloy_primitives::{address, Bytes};
987    use revm::{
988        context::Block,
989        database::EmptyDB,
990        precompile::{PrecompileId, PrecompileOutput},
991        primitives::hardfork::SpecId,
992    };
993
994    #[test]
995    fn test_map_precompile() {
996        let eth_precompiles = EthPrecompiles::new(SpecId::default());
997        let mut spec_precompiles = PrecompilesMap::from(eth_precompiles);
998
999        let mut ctx = EthEvmContext::new(EmptyDB::default(), Default::default());
1000
1001        // create a test input for the precompile (identity precompile)
1002        let identity_address = address!("0x0000000000000000000000000000000000000004");
1003        let test_input = Bytes::from_static(b"test data");
1004        let gas_limit = 1000;
1005
1006        // Ensure we're using dynamic precompiles
1007        spec_precompiles.ensure_dynamic_precompiles();
1008
1009        // using the dynamic precompiles interface
1010        let dyn_precompile = match &spec_precompiles.precompiles {
1011            PrecompilesKind::Dynamic(dyn_precompiles) => {
1012                dyn_precompiles.inner.get(&identity_address).unwrap()
1013            }
1014            _ => panic!("Expected dynamic precompiles"),
1015        };
1016
1017        let result = dyn_precompile
1018            .call(PrecompileInput {
1019                data: &test_input,
1020                gas: gas_limit,
1021                caller: Address::ZERO,
1022                value: U256::ZERO,
1023                is_static: false,
1024                internals: EvmInternals::from_context(&mut ctx),
1025                target_address: identity_address,
1026                bytecode_address: identity_address,
1027            })
1028            .unwrap();
1029        assert_eq!(result.bytes, test_input, "Identity precompile should return the input data");
1030
1031        // define a function to modify the precompile
1032        // this will change the identity precompile to always return a fixed value
1033        let constant_bytes = Bytes::from_static(b"constant value");
1034
1035        // define a function to modify the precompile to always return a constant value
1036        spec_precompiles.map_precompile(&identity_address, move |_original_dyn| {
1037            // create a new DynPrecompile that always returns our constant
1038            (|_input: PrecompileInput<'_>| -> PrecompileResult {
1039                Ok(PrecompileOutput::new(10, Bytes::from_static(b"constant value")))
1040            })
1041            .into()
1042        });
1043
1044        // get the modified precompile and check it
1045        let dyn_precompile = match &spec_precompiles.precompiles {
1046            PrecompilesKind::Dynamic(dyn_precompiles) => {
1047                dyn_precompiles.inner.get(&identity_address).unwrap()
1048            }
1049            _ => panic!("Expected dynamic precompiles"),
1050        };
1051
1052        let result = dyn_precompile
1053            .call(PrecompileInput {
1054                data: &test_input,
1055                gas: gas_limit,
1056                caller: Address::ZERO,
1057                value: U256::ZERO,
1058                is_static: false,
1059                internals: EvmInternals::from_context(&mut ctx),
1060                target_address: identity_address,
1061                bytecode_address: identity_address,
1062            })
1063            .unwrap();
1064        assert_eq!(
1065            result.bytes, constant_bytes,
1066            "Modified precompile should return the constant value"
1067        );
1068    }
1069
1070    #[test]
1071    fn test_closure_precompile() {
1072        let test_input = Bytes::from_static(b"test data");
1073        let expected_output = Bytes::from_static(b"processed: test data");
1074        let gas_limit = 1000;
1075
1076        let mut ctx = EthEvmContext::new(EmptyDB::default(), Default::default());
1077
1078        // define a closure that implements the precompile functionality
1079        let closure_precompile = |input: PrecompileInput<'_>| -> PrecompileResult {
1080            let _timestamp = input.internals.block_env().timestamp();
1081            let mut output = b"processed: ".to_vec();
1082            output.extend_from_slice(input.data.as_ref());
1083            Ok(PrecompileOutput::new(15, Bytes::from(output)))
1084        };
1085
1086        let dyn_precompile: DynPrecompile = closure_precompile.into();
1087
1088        let result = dyn_precompile
1089            .call(PrecompileInput {
1090                data: &test_input,
1091                gas: gas_limit,
1092                caller: Address::ZERO,
1093                value: U256::ZERO,
1094                is_static: false,
1095                internals: EvmInternals::from_context(&mut ctx),
1096                target_address: Address::ZERO,
1097                bytecode_address: Address::ZERO,
1098            })
1099            .unwrap();
1100        assert_eq!(result.gas_used, 15);
1101        assert_eq!(result.bytes, expected_output);
1102    }
1103
1104    #[test]
1105    fn test_supports_caching() {
1106        let closure_precompile = |_input: PrecompileInput<'_>| -> PrecompileResult {
1107            Ok(PrecompileOutput::new(10, Bytes::from_static(b"output")))
1108        };
1109
1110        let dyn_precompile: DynPrecompile = closure_precompile.into();
1111        assert!(dyn_precompile.supports_caching(), "should support caching by default");
1112
1113        let stateful_precompile =
1114            DynPrecompile::new_stateful(PrecompileId::Custom("closure".into()), closure_precompile);
1115        assert!(
1116            !stateful_precompile.supports_caching(),
1117            "stateful precompile should not support caching"
1118        );
1119
1120        let either_left = Either::<DynPrecompile, DynPrecompile>::Left(stateful_precompile);
1121        assert!(
1122            !either_left.supports_caching(),
1123            "Either::Left with non-cacheable should return false"
1124        );
1125
1126        let either_right = Either::<DynPrecompile, DynPrecompile>::Right(dyn_precompile);
1127        assert!(either_right.supports_caching(), "Either::Right with cacheable should return true");
1128
1129        // Identity precompile should not support caching
1130        let identity = revm::precompile::identity::FUN;
1131        assert!(!identity.supports_caching(), "identity precompile should not support caching");
1132
1133        // Other builtin precompiles should support caching
1134        let sha256 = revm::precompile::hash::SHA256;
1135        assert!(sha256.supports_caching(), "sha256 precompile should support caching");
1136    }
1137
1138    #[test]
1139    fn test_precompile_lookup() {
1140        let eth_precompiles = EthPrecompiles::new(SpecId::default());
1141        let mut spec_precompiles = PrecompilesMap::from(eth_precompiles);
1142
1143        let mut ctx = EthEvmContext::new(EmptyDB::default(), Default::default());
1144
1145        // Define a custom address pattern for dynamic precompiles
1146        let dynamic_prefix = [0xDE, 0xAD];
1147
1148        // Set up the lookup function
1149        spec_precompiles.set_precompile_lookup(move |address: &Address| {
1150            if address.as_slice().starts_with(&dynamic_prefix) {
1151                Some(DynPrecompile::new(PrecompileId::Custom("dynamic".into()), |_input| {
1152                    Ok(PrecompileOutput {
1153                        gas_used: 100,
1154                        gas_refunded: 0,
1155                        bytes: Bytes::from("dynamic precompile response"),
1156                        reverted: false,
1157                    })
1158                }))
1159            } else {
1160                None
1161            }
1162        });
1163
1164        // Test that static precompiles still work
1165        let identity_address = address!("0x0000000000000000000000000000000000000004");
1166        assert!(spec_precompiles.get(&identity_address).is_some());
1167
1168        // Test dynamic lookup for matching address
1169        let dynamic_address = address!("0xDEAD000000000000000000000000000000000001");
1170        let dynamic_precompile = spec_precompiles.get(&dynamic_address);
1171        assert!(dynamic_precompile.is_some(), "Dynamic precompile should be found");
1172
1173        // Execute the dynamic precompile
1174        let result = dynamic_precompile
1175            .unwrap()
1176            .call(PrecompileInput {
1177                data: &[],
1178                gas: 1000,
1179                caller: Address::ZERO,
1180                value: U256::ZERO,
1181                is_static: false,
1182                internals: EvmInternals::from_context(&mut ctx),
1183                target_address: dynamic_address,
1184                bytecode_address: dynamic_address,
1185            })
1186            .unwrap();
1187        assert_eq!(result.gas_used, 100);
1188        assert_eq!(result.bytes, Bytes::from("dynamic precompile response"));
1189
1190        // Test non-matching address returns None
1191        let non_matching_address = address!("0x1234000000000000000000000000000000000001");
1192        assert!(spec_precompiles.get(&non_matching_address).is_none());
1193    }
1194
1195    #[test]
1196    fn test_get_precompile() {
1197        let eth_precompiles = EthPrecompiles::new(SpecId::default());
1198        let spec_precompiles = PrecompilesMap::from(eth_precompiles);
1199
1200        let mut ctx = EthEvmContext::new(EmptyDB::default(), Default::default());
1201
1202        let identity_address = address!("0x0000000000000000000000000000000000000004");
1203        let test_input = Bytes::from_static(b"test data");
1204        let gas_limit = 1000;
1205
1206        let precompile = spec_precompiles.get(&identity_address);
1207        assert!(precompile.is_some(), "Identity precompile should exist");
1208
1209        let result = precompile
1210            .unwrap()
1211            .call(PrecompileInput {
1212                data: &test_input,
1213                gas: gas_limit,
1214                caller: Address::ZERO,
1215                value: U256::ZERO,
1216                is_static: false,
1217                target_address: identity_address,
1218                bytecode_address: identity_address,
1219                internals: EvmInternals::from_context(&mut ctx),
1220            })
1221            .unwrap();
1222        assert_eq!(result.bytes, test_input, "Identity precompile should return the input data");
1223
1224        let nonexistent_address = address!("0x0000000000000000000000000000000000000099");
1225        assert!(
1226            spec_precompiles.get(&nonexistent_address).is_none(),
1227            "Non-existent precompile should not be found"
1228        );
1229
1230        let mut dynamic_precompiles = spec_precompiles;
1231        dynamic_precompiles.ensure_dynamic_precompiles();
1232
1233        let dyn_precompile = dynamic_precompiles.get(&identity_address);
1234        assert!(
1235            dyn_precompile.is_some(),
1236            "Identity precompile should exist after conversion to dynamic"
1237        );
1238
1239        let result = dyn_precompile
1240            .unwrap()
1241            .call(PrecompileInput {
1242                data: &test_input,
1243                gas: gas_limit,
1244                caller: Address::ZERO,
1245                value: U256::ZERO,
1246                is_static: false,
1247                internals: EvmInternals::from_context(&mut ctx),
1248                target_address: identity_address,
1249                bytecode_address: identity_address,
1250            })
1251            .unwrap();
1252        assert_eq!(
1253            result.bytes, test_input,
1254            "Identity precompile should return the input data after conversion to dynamic"
1255        );
1256    }
1257
1258    #[test]
1259    fn test_move_precompiles() {
1260        let eth_precompiles = EthPrecompiles::new(SpecId::default());
1261        let mut spec_precompiles = PrecompilesMap::from(eth_precompiles);
1262
1263        let mut ctx = EthEvmContext::new(EmptyDB::default(), Default::default());
1264
1265        // Identity precompile at address 0x04
1266        let identity_address = address!("0x0000000000000000000000000000000000000004");
1267        let new_address = address!("0x0000000000000000000000000000000000001000");
1268        let test_input = Bytes::from_static(b"test data");
1269        let gas_limit = 1000;
1270
1271        // Verify the precompile exists at original address
1272        assert!(spec_precompiles.get(&identity_address).is_some());
1273        assert!(spec_precompiles.get(&new_address).is_none());
1274
1275        // Move the precompile
1276        spec_precompiles.move_precompiles([(identity_address, new_address)]).unwrap();
1277
1278        // Verify the precompile moved
1279        assert!(
1280            spec_precompiles.get(&identity_address).is_none(),
1281            "Precompile should no longer exist at original address"
1282        );
1283        assert!(
1284            spec_precompiles.get(&new_address).is_some(),
1285            "Precompile should exist at new address"
1286        );
1287
1288        // Verify the moved precompile works correctly
1289        let result = spec_precompiles
1290            .get(&new_address)
1291            .unwrap()
1292            .call(PrecompileInput {
1293                data: &test_input,
1294                gas: gas_limit,
1295                caller: Address::ZERO,
1296                value: U256::ZERO,
1297                is_static: false,
1298                internals: EvmInternals::from_context(&mut ctx),
1299                target_address: new_address,
1300                bytecode_address: new_address,
1301            })
1302            .unwrap();
1303        assert_eq!(result.bytes, test_input, "Moved identity precompile should return input data");
1304    }
1305
1306    #[test]
1307    fn test_move_precompiles_not_a_precompile() {
1308        let eth_precompiles = EthPrecompiles::new(SpecId::default());
1309        let mut spec_precompiles = PrecompilesMap::from(eth_precompiles);
1310
1311        let non_precompile = address!("0x0000000000000000000000000000000000000099");
1312        let dest = address!("0x0000000000000000000000000000000000001000");
1313
1314        let result = spec_precompiles.move_precompiles([(non_precompile, dest)]);
1315        assert_eq!(result, Err(MovePrecompileError::NotAPrecompile(non_precompile)));
1316    }
1317
1318    #[test]
1319    fn test_move_precompiles_same_address_noop() {
1320        let eth_precompiles = EthPrecompiles::new(SpecId::default());
1321        let mut spec_precompiles = PrecompilesMap::from(eth_precompiles);
1322
1323        let identity_address = address!("0x0000000000000000000000000000000000000004");
1324
1325        // Moving to same address should be a no-op and not error
1326        spec_precompiles.move_precompiles([(identity_address, identity_address)]).unwrap();
1327
1328        // Precompile should still exist
1329        assert!(spec_precompiles.get(&identity_address).is_some());
1330    }
1331
1332    #[test]
1333    fn test_move_precompiles_multiple() {
1334        let eth_precompiles = EthPrecompiles::new(SpecId::default());
1335        let mut spec_precompiles = PrecompilesMap::from(eth_precompiles);
1336
1337        let ecrecover = address!("0x0000000000000000000000000000000000000001");
1338        let sha256 = address!("0x0000000000000000000000000000000000000002");
1339        let new_ecrecover = address!("0x0000000000000000000000000000000000001001");
1340        let new_sha256 = address!("0x0000000000000000000000000000000000001002");
1341
1342        spec_precompiles
1343            .move_precompiles([(ecrecover, new_ecrecover), (sha256, new_sha256)])
1344            .unwrap();
1345
1346        // Original addresses should be empty
1347        assert!(spec_precompiles.get(&ecrecover).is_none());
1348        assert!(spec_precompiles.get(&sha256).is_none());
1349
1350        // New addresses should have the precompiles
1351        assert!(spec_precompiles.get(&new_ecrecover).is_some());
1352        assert!(spec_precompiles.get(&new_sha256).is_some());
1353    }
1354}