1use 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
19const fn precompile_id_supports_caching(id: &PrecompileId) -> bool {
25 !matches!(id, PrecompileId::Identity)
26}
27
28#[derive(Clone)]
33pub struct PrecompilesMap {
34 precompiles: PrecompilesKind,
36 lookup: Option<Arc<dyn PrecompileLookup>>,
38}
39
40impl PrecompilesMap {
41 pub fn from_static(precompiles: &'static Precompiles) -> Self {
43 Self::new(Cow::Borrowed(precompiles))
44 }
45
46 pub fn new(precompiles: Cow<'static, Precompiles>) -> Self {
48 Self { precompiles: PrecompilesKind::Builtin(precompiles), lookup: None }
49 }
50
51 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 if let Some(dyn_precompile) = dyn_precompiles.inner.remove(address) {
60 let transformed = f(dyn_precompile);
62
63 dyn_precompiles.inner.insert(*address, transformed);
65 }
66 }
67
68 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 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 #[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 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 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 let result = f(current);
162
163 match result {
164 Some(transformed) => {
165 dyn_precompiles.inner.insert(*address, transformed);
167 dyn_precompiles.addresses.insert(*address);
168 }
169 None => {
170 dyn_precompiles.inner.remove(address);
172 dyn_precompiles.addresses.remove(address);
173 }
174 }
175 }
176
177 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 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 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 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 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 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 for (source, _dest) in &moves {
304 if self.get(source).is_none() {
305 return Err(MovePrecompileError::NotAPrecompile(*source));
306 }
307 }
308
309 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 for (dest, precompile) in extracted {
326 self.apply_precompile(&dest, |_| Some(precompile));
327 }
328
329 Ok(())
330 }
331
332 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 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 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 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 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 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 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 pub fn get(&self, address: &Address) -> Option<impl Precompile + '_> {
482 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 let Some(precompile) = static_result {
492 return Some(Either::Left(precompile));
493 }
494
495 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 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 let r;
553 let input_bytes = match &inputs.input {
554 CallInput::SharedBuffer(range) => {
555 #[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#[derive(Clone)]
622enum PrecompilesKind {
623 Builtin(Cow<'static, Precompiles>),
625 Dynamic(DynPrecompiles),
627}
628
629#[derive(Clone)]
631pub struct DynPrecompile(pub(crate) Arc<dyn Precompile + Send + Sync>);
632
633impl DynPrecompile {
634 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 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 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#[derive(Clone, Default)]
668pub struct DynPrecompiles {
669 inner: AddressMap<DynPrecompile>,
671 addresses: AddressSet,
673}
674
675impl DynPrecompiles {
676 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#[derive(Debug)]
691pub struct PrecompileInput<'a> {
692 pub data: &'a [u8],
694 pub gas: u64,
696 pub caller: Address,
698 pub value: U256,
700 pub target_address: Address,
703 pub is_static: bool,
705 pub bytecode_address: Address,
707 pub internals: EvmInternals<'a>,
709}
710
711impl<'a> PrecompileInput<'a> {
712 pub const fn data(&self) -> &[u8] {
714 self.data
715 }
716
717 pub const fn caller(&self) -> &Address {
719 &self.caller
720 }
721
722 pub const fn gas(&self) -> u64 {
724 self.gas
725 }
726
727 pub const fn value(&self) -> &U256 {
729 &self.value
730 }
731
732 pub const fn target_address(&self) -> &Address {
734 &self.target_address
735 }
736
737 pub const fn bytecode_address(&self) -> &Address {
739 &self.bytecode_address
740 }
741
742 pub fn is_direct_call(&self) -> bool {
745 self.target_address == self.bytecode_address
746 }
747
748 pub const fn is_static_call(&self) -> bool {
750 self.is_static
751 }
752
753 pub const fn internals(&self) -> &EvmInternals<'_> {
755 &self.internals
756 }
757
758 pub const fn internals_mut(&mut self) -> &mut EvmInternals<'a> {
760 &mut self.internals
761 }
762}
763
764#[auto_impl::auto_impl(&, Arc)]
766pub trait Precompile {
767 fn precompile_id(&self) -> &PrecompileId;
769
770 fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult;
772
773 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
941pub trait PrecompileLookup: Send + Sync {
946 fn lookup(&self, address: &Address) -> Option<DynPrecompile>;
951}
952
953impl<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#[derive(Clone, Debug, PartialEq, Eq)]
965pub enum MovePrecompileError {
966 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 let identity_address = address!("0x0000000000000000000000000000000000000004");
1003 let test_input = Bytes::from_static(b"test data");
1004 let gas_limit = 1000;
1005
1006 spec_precompiles.ensure_dynamic_precompiles();
1008
1009 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 let constant_bytes = Bytes::from_static(b"constant value");
1034
1035 spec_precompiles.map_precompile(&identity_address, move |_original_dyn| {
1037 (|_input: PrecompileInput<'_>| -> PrecompileResult {
1039 Ok(PrecompileOutput::new(10, Bytes::from_static(b"constant value")))
1040 })
1041 .into()
1042 });
1043
1044 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 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 let identity = revm::precompile::identity::FUN;
1131 assert!(!identity.supports_caching(), "identity precompile should not support caching");
1132
1133 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 let dynamic_prefix = [0xDE, 0xAD];
1147
1148 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 let identity_address = address!("0x0000000000000000000000000000000000000004");
1166 assert!(spec_precompiles.get(&identity_address).is_some());
1167
1168 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 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 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 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 assert!(spec_precompiles.get(&identity_address).is_some());
1273 assert!(spec_precompiles.get(&new_address).is_none());
1274
1275 spec_precompiles.move_precompiles([(identity_address, new_address)]).unwrap();
1277
1278 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 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 spec_precompiles.move_precompiles([(identity_address, identity_address)]).unwrap();
1327
1328 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 assert!(spec_precompiles.get(&ecrecover).is_none());
1348 assert!(spec_precompiles.get(&sha256).is_none());
1349
1350 assert!(spec_precompiles.get(&new_ecrecover).is_some());
1352 assert!(spec_precompiles.get(&new_sha256).is_some());
1353 }
1354}