1use crate::{Database, EvmInternals};
4use alloc::{borrow::Cow, boxed::Box, string::String, sync::Arc};
5use alloy_consensus::transaction::Either;
6use alloy_primitives::{
7 map::{HashMap, HashSet},
8 Address, Bytes, U256,
9};
10use core::fmt::Debug;
11use revm::{
12 context::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#[derive(Clone)]
24pub struct PrecompilesMap {
25 precompiles: PrecompilesKind,
27 lookup: Option<Arc<dyn PrecompileLookup>>,
29}
30
31impl PrecompilesMap {
32 pub fn from_static(precompiles: &'static Precompiles) -> Self {
34 Self::new(Cow::Borrowed(precompiles))
35 }
36
37 pub fn new(precompiles: Cow<'static, Precompiles>) -> Self {
39 Self { precompiles: PrecompilesKind::Builtin(precompiles), lookup: None }
40 }
41
42 pub fn map_precompile<F>(&mut self, address: &Address, f: F)
44 where
45 F: FnOnce(DynPrecompile) -> DynPrecompile + Send + Sync + 'static,
46 {
47 let dyn_precompiles = self.ensure_dynamic_precompiles();
48
49 if let Some(dyn_precompile) = dyn_precompiles.inner.remove(address) {
51 let transformed = f(dyn_precompile);
53
54 dyn_precompiles.inner.insert(*address, transformed);
56 }
57 }
58
59 pub fn map_precompiles<F>(&mut self, f: F)
61 where
62 F: FnMut(&Address, DynPrecompile) -> DynPrecompile,
63 {
64 self.map_precompiles_filtered(f, |_, _| true);
65 }
66
67 pub fn map_pure_precompiles<F>(&mut self, f: F)
72 where
73 F: FnMut(&Address, DynPrecompile) -> DynPrecompile,
74 {
75 self.map_precompiles_filtered(f, |_, precompile| precompile.is_pure());
76 }
77
78 #[inline]
83 fn map_precompiles_filtered<F, P>(&mut self, mut f: F, mut filter: P)
84 where
85 F: FnMut(&Address, DynPrecompile) -> DynPrecompile,
86 P: FnMut(&Address, &DynPrecompile) -> bool,
87 {
88 let dyn_precompiles = self.ensure_dynamic_precompiles();
89
90 let entries = dyn_precompiles.inner.drain();
92 let mut new_map =
93 HashMap::with_capacity_and_hasher(entries.size_hint().0, Default::default());
94 for (addr, precompile) in entries {
95 if filter(&addr, &precompile) {
96 let transformed = f(&addr, precompile);
97 new_map.insert(addr, transformed);
98 } else {
99 new_map.insert(addr, precompile);
100 }
101 }
102
103 dyn_precompiles.inner = new_map;
104 }
105
106 pub fn apply_precompile<F>(&mut self, address: &Address, f: F)
145 where
146 F: FnOnce(Option<DynPrecompile>) -> Option<DynPrecompile>,
147 {
148 let dyn_precompiles = self.ensure_dynamic_precompiles();
149 let current = dyn_precompiles.inner.get(address).cloned();
150
151 let result = f(current);
153
154 match result {
155 Some(transformed) => {
156 dyn_precompiles.inner.insert(*address, transformed);
158 dyn_precompiles.addresses.insert(*address);
159 }
160 None => {
161 dyn_precompiles.inner.remove(address);
163 dyn_precompiles.addresses.remove(address);
164 }
165 }
166 }
167
168 pub fn with_mapped_precompile<F>(mut self, address: &Address, f: F) -> Self
173 where
174 F: FnOnce(DynPrecompile) -> DynPrecompile + Send + Sync + 'static,
175 {
176 self.map_precompile(address, f);
177 self
178 }
179
180 pub fn with_mapped_precompiles<F>(mut self, f: F) -> Self
185 where
186 F: FnMut(&Address, DynPrecompile) -> DynPrecompile,
187 {
188 self.map_precompiles(f);
189 self
190 }
191
192 pub fn with_applied_precompile<F>(mut self, address: &Address, f: F) -> Self
198 where
199 F: FnOnce(Option<DynPrecompile>) -> Option<DynPrecompile>,
200 {
201 self.apply_precompile(address, f);
202 self
203 }
204
205 pub fn extend_precompiles<I>(&mut self, precompiles: I)
224 where
225 I: IntoIterator<Item = (Address, DynPrecompile)>,
226 {
227 for (addr, precompile) in precompiles {
228 self.apply_precompile(&addr, |_| Some(precompile));
229 }
230 }
231
232 pub fn with_extended_precompiles<I>(mut self, precompiles: I) -> Self
253 where
254 I: IntoIterator<Item = (Address, DynPrecompile)>,
255 {
256 self.extend_precompiles(precompiles);
257 self
258 }
259
260 pub fn set_precompile_lookup<L>(&mut self, lookup: L)
296 where
297 L: PrecompileLookup + 'static,
298 {
299 self.lookup = Some(Arc::new(lookup));
300 }
301
302 pub fn with_precompile_lookup<L>(mut self, lookup: L) -> Self
310 where
311 L: PrecompileLookup + 'static,
312 {
313 self.set_precompile_lookup(lookup);
314 self
315 }
316
317 pub fn into_dyn_precompiles(mut self) -> DynPrecompiles {
319 self.ensure_dynamic_precompiles();
320 match self.precompiles {
321 PrecompilesKind::Dynamic(dynamic) => dynamic,
322 _ => unreachable!("We just ensured that this is a Dynamic variant"),
323 }
324 }
325
326 pub fn ensure_dynamic_precompiles(&mut self) -> &mut DynPrecompiles {
330 if let PrecompilesKind::Builtin(ref precompiles_cow) = self.precompiles {
331 let mut dynamic = DynPrecompiles::default();
332
333 let static_precompiles = match precompiles_cow {
334 Cow::Borrowed(static_ref) => static_ref,
335 Cow::Owned(owned) => owned,
336 };
337
338 for (&addr, pc) in static_precompiles.inner().iter() {
339 dynamic.inner.insert(addr, DynPrecompile::from(*pc.precompile()));
340 dynamic.addresses.insert(addr);
341 }
342
343 self.precompiles = PrecompilesKind::Dynamic(dynamic);
344 }
345
346 match &mut self.precompiles {
347 PrecompilesKind::Dynamic(dynamic) => dynamic,
348 _ => unreachable!("We just ensured that this is a Dynamic variant"),
349 }
350 }
351
352 pub fn identifiers(&self) -> impl Iterator<Item = &PrecompileId> {
354 match &self.precompiles {
355 PrecompilesKind::Builtin(precompiles) => {
356 Either::Left(precompiles.inner().values().map(|p| p.precompile_id()))
357 }
358 PrecompilesKind::Dynamic(dyn_precompiles) => {
359 Either::Right(dyn_precompiles.inner.values().map(|p| p.precompile_id()))
360 }
361 }
362 }
363
364 pub fn addresses(&self) -> impl Iterator<Item = &Address> {
366 match &self.precompiles {
367 PrecompilesKind::Builtin(precompiles) => Either::Left(precompiles.addresses()),
368 PrecompilesKind::Dynamic(dyn_precompiles) => {
369 Either::Right(dyn_precompiles.addresses.iter())
370 }
371 }
372 }
373
374 pub fn get(&self, address: &Address) -> Option<impl Precompile + '_> {
379 let static_result = match &self.precompiles {
381 PrecompilesKind::Builtin(precompiles) => precompiles.get(address).map(Either::Left),
382 PrecompilesKind::Dynamic(dyn_precompiles) => {
383 dyn_precompiles.inner.get(address).map(Either::Right)
384 }
385 };
386
387 if let Some(precompile) = static_result {
389 return Some(Either::Left(precompile));
390 }
391
392 let lookup = self.lookup.as_ref()?;
394 lookup.lookup(address).map(Either::Right)
395 }
396}
397
398impl From<EthPrecompiles> for PrecompilesMap {
399 fn from(value: EthPrecompiles) -> Self {
400 Self::from_static(value.precompiles)
401 }
402}
403
404impl core::fmt::Debug for PrecompilesMap {
405 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
406 match &self.precompiles {
407 PrecompilesKind::Builtin(_) => f.debug_struct("PrecompilesMap::Builtin").finish(),
408 PrecompilesKind::Dynamic(precompiles) => f
409 .debug_struct("PrecompilesMap::Dynamic")
410 .field("addresses", &precompiles.addresses)
411 .finish(),
412 }
413 }
414}
415
416impl<BlockEnv, TxEnv, CfgEnv, DB, Chain>
417 PrecompileProvider<Context<BlockEnv, TxEnv, CfgEnv, DB, Journal<DB>, Chain>> for PrecompilesMap
418where
419 BlockEnv: revm::context::Block,
420 TxEnv: revm::context::Transaction,
421 CfgEnv: revm::context::Cfg,
422 DB: Database,
423{
424 type Output = InterpreterResult;
425
426 fn set_spec(&mut self, _spec: CfgEnv::Spec) -> bool {
427 false
428 }
429
430 fn run(
431 &mut self,
432 context: &mut Context<BlockEnv, TxEnv, CfgEnv, DB, Journal<DB>, Chain>,
433 inputs: &CallInputs,
434 ) -> Result<Option<InterpreterResult>, String> {
435 let Some(precompile) = self.get(&inputs.bytecode_address) else {
437 return Ok(None);
438 };
439
440 let mut result = InterpreterResult {
441 result: InstructionResult::Return,
442 gas: Gas::new(inputs.gas_limit),
443 output: Bytes::new(),
444 };
445
446 let (local, journal) = (&context.local, &mut context.journaled_state);
447
448 let r;
450 let input_bytes = match &inputs.input {
451 CallInput::SharedBuffer(range) => {
452 #[allow(clippy::option_if_let_else)]
455 if let Some(slice) = local.shared_memory_buffer_slice(range.clone()) {
456 r = slice;
457 &*r
458 } else {
459 &[]
460 }
461 }
462 CallInput::Bytes(bytes) => bytes.as_ref(),
463 };
464
465 let precompile_result = precompile.call(PrecompileInput {
466 data: input_bytes,
467 gas: inputs.gas_limit,
468 caller: inputs.caller,
469 value: inputs.call_value(),
470 internals: EvmInternals::new(journal, &context.block),
471 target_address: inputs.target_address,
472 bytecode_address: inputs.bytecode_address,
473 });
474
475 match precompile_result {
476 Ok(output) => {
477 let underflow = result.gas.record_cost(output.gas_used);
478 assert!(underflow, "Gas underflow is not possible");
479 result.result = if output.reverted {
480 InstructionResult::Revert
481 } else {
482 InstructionResult::Return
483 };
484 result.output = output.bytes;
485 }
486 Err(PrecompileError::Fatal(e)) => return Err(e),
487 Err(e) => {
488 result.result = if e.is_oog() {
489 InstructionResult::PrecompileOOG
490 } else {
491 InstructionResult::PrecompileError
492 };
493 }
494 };
495
496 Ok(Some(result))
497 }
498
499 fn warm_addresses(&self) -> Box<impl Iterator<Item = Address>> {
500 Box::new(self.addresses().copied())
501 }
502
503 fn contains(&self, address: &Address) -> bool {
504 self.get(address).is_some()
505 }
506}
507
508#[derive(Clone)]
513enum PrecompilesKind {
514 Builtin(Cow<'static, Precompiles>),
516 Dynamic(DynPrecompiles),
518}
519
520#[derive(Clone)]
522pub struct DynPrecompile(pub(crate) Arc<dyn Precompile + Send + Sync>);
523
524impl DynPrecompile {
525 pub fn new<F>(id: PrecompileId, f: F) -> Self
527 where
528 F: Fn(PrecompileInput<'_>) -> PrecompileResult + Send + Sync + 'static,
529 {
530 Self(Arc::new((id, f)))
531 }
532
533 pub fn new_stateful<F>(id: PrecompileId, f: F) -> Self
536 where
537 F: Fn(PrecompileInput<'_>) -> PrecompileResult + Send + Sync + 'static,
538 {
539 Self(Arc::new(StatefulPrecompile((id, f))))
540 }
541
542 pub fn stateful(self) -> Self {
544 Self(Arc::new(StatefulPrecompile(self.0)))
545 }
546}
547
548impl core::fmt::Debug for DynPrecompile {
549 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
550 f.debug_struct("DynPrecompile").finish()
551 }
552}
553
554#[derive(Clone, Default)]
559pub struct DynPrecompiles {
560 inner: HashMap<Address, DynPrecompile>,
562 addresses: HashSet<Address>,
564}
565
566impl DynPrecompiles {
567 pub fn into_precompiles(self) -> impl Iterator<Item = (Address, DynPrecompile)> {
570 self.inner.into_iter()
571 }
572}
573
574impl core::fmt::Debug for DynPrecompiles {
575 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
576 f.debug_struct("DynPrecompiles").field("addresses", &self.addresses).finish()
577 }
578}
579
580#[derive(Debug)]
582pub struct PrecompileInput<'a> {
583 pub data: &'a [u8],
585 pub gas: u64,
587 pub caller: Address,
589 pub value: U256,
591 pub target_address: Address,
594 pub bytecode_address: Address,
596 pub internals: EvmInternals<'a>,
598}
599
600impl<'a> PrecompileInput<'a> {
601 pub const fn data(&self) -> &[u8] {
603 self.data
604 }
605
606 pub const fn caller(&self) -> &Address {
608 &self.caller
609 }
610
611 pub const fn gas(&self) -> u64 {
613 self.gas
614 }
615
616 pub const fn value(&self) -> &U256 {
618 &self.value
619 }
620
621 pub const fn target_address(&self) -> &Address {
623 &self.target_address
624 }
625
626 pub const fn bytecode_address(&self) -> &Address {
628 &self.bytecode_address
629 }
630
631 pub fn is_direct_call(&self) -> bool {
634 self.target_address == self.bytecode_address
635 }
636
637 pub const fn internals(&self) -> &EvmInternals<'_> {
639 &self.internals
640 }
641
642 pub const fn internals_mut(&mut self) -> &mut EvmInternals<'a> {
644 &mut self.internals
645 }
646}
647
648#[auto_impl::auto_impl(&, Arc)]
650pub trait Precompile {
651 fn precompile_id(&self) -> &PrecompileId;
653
654 fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult;
656
657 fn is_pure(&self) -> bool {
684 true
685 }
686}
687
688impl<F> Precompile for (PrecompileId, F)
689where
690 F: Fn(PrecompileInput<'_>) -> PrecompileResult + Send + Sync,
691{
692 fn precompile_id(&self) -> &PrecompileId {
693 &self.0
694 }
695
696 fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
697 self.1(input)
698 }
699}
700
701impl<F> Precompile for (&PrecompileId, F)
702where
703 F: Fn(PrecompileInput<'_>) -> PrecompileResult + Send + Sync,
704{
705 fn precompile_id(&self) -> &PrecompileId {
706 self.0
707 }
708
709 fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
710 self.1(input)
711 }
712}
713
714impl Precompile for revm::precompile::Precompile {
715 fn precompile_id(&self) -> &PrecompileId {
716 self.id()
717 }
718
719 fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
720 self.precompile()(input.data, input.gas)
721 }
722}
723
724impl<F> From<F> for DynPrecompile
725where
726 F: Fn(PrecompileInput<'_>) -> PrecompileResult + Send + Sync + 'static,
727{
728 fn from(f: F) -> Self {
729 Self::new(PrecompileId::Custom("closure".into()), f)
730 }
731}
732
733impl From<PrecompileFn> for DynPrecompile {
734 fn from(f: PrecompileFn) -> Self {
735 let p = move |input: PrecompileInput<'_>| f(input.data, input.gas);
736 p.into()
737 }
738}
739
740impl<F> From<(PrecompileId, F)> for DynPrecompile
741where
742 F: Fn(PrecompileInput<'_>) -> PrecompileResult + Send + Sync + 'static,
743{
744 fn from((id, f): (PrecompileId, F)) -> Self {
745 Self(Arc::new((id, f)))
746 }
747}
748
749impl From<(PrecompileId, PrecompileFn)> for DynPrecompile {
750 fn from((id, f): (PrecompileId, PrecompileFn)) -> Self {
751 let p = move |input: PrecompileInput<'_>| f(input.data, input.gas);
752 (id, p).into()
753 }
754}
755
756impl Precompile for DynPrecompile {
757 fn precompile_id(&self) -> &PrecompileId {
758 self.0.precompile_id()
759 }
760
761 fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
762 self.0.call(input)
763 }
764
765 fn is_pure(&self) -> bool {
766 self.0.is_pure()
767 }
768}
769
770impl<A: Precompile, B: Precompile> Precompile for Either<A, B> {
771 fn precompile_id(&self) -> &PrecompileId {
772 match self {
773 Self::Left(p) => p.precompile_id(),
774 Self::Right(p) => p.precompile_id(),
775 }
776 }
777
778 fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
779 match self {
780 Self::Left(p) => p.call(input),
781 Self::Right(p) => p.call(input),
782 }
783 }
784
785 fn is_pure(&self) -> bool {
786 match self {
787 Self::Left(p) => p.is_pure(),
788 Self::Right(p) => p.is_pure(),
789 }
790 }
791}
792
793struct StatefulPrecompile<P>(P);
794
795impl<P: Precompile> Precompile for StatefulPrecompile<P> {
796 fn precompile_id(&self) -> &PrecompileId {
797 self.0.precompile_id()
798 }
799
800 fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
801 self.0.call(input)
802 }
803
804 fn is_pure(&self) -> bool {
805 false
806 }
807}
808
809pub trait PrecompileLookup: Send + Sync {
814 fn lookup(&self, address: &Address) -> Option<DynPrecompile>;
819}
820
821impl<F> PrecompileLookup for F
823where
824 F: Fn(&Address) -> Option<DynPrecompile> + Send + Sync,
825{
826 fn lookup(&self, address: &Address) -> Option<DynPrecompile> {
827 self(address)
828 }
829}
830
831#[cfg(test)]
832mod tests {
833 use super::*;
834 use crate::eth::EthEvmContext;
835 use alloy_primitives::{address, Bytes};
836 use revm::{
837 context::Block,
838 database::EmptyDB,
839 precompile::{PrecompileId, PrecompileOutput},
840 };
841
842 #[test]
843 fn test_map_precompile() {
844 let eth_precompiles = EthPrecompiles::default();
845 let mut spec_precompiles = PrecompilesMap::from(eth_precompiles);
846
847 let mut ctx = EthEvmContext::new(EmptyDB::default(), Default::default());
848
849 let identity_address = address!("0x0000000000000000000000000000000000000004");
851 let test_input = Bytes::from_static(b"test data");
852 let gas_limit = 1000;
853
854 spec_precompiles.ensure_dynamic_precompiles();
856
857 let dyn_precompile = match &spec_precompiles.precompiles {
859 PrecompilesKind::Dynamic(dyn_precompiles) => {
860 dyn_precompiles.inner.get(&identity_address).unwrap()
861 }
862 _ => panic!("Expected dynamic precompiles"),
863 };
864
865 let result = dyn_precompile
866 .call(PrecompileInput {
867 data: &test_input,
868 gas: gas_limit,
869 caller: Address::ZERO,
870 value: U256::ZERO,
871 internals: EvmInternals::new(&mut ctx.journaled_state, &ctx.block),
872 target_address: identity_address,
873 bytecode_address: identity_address,
874 })
875 .unwrap();
876 assert_eq!(result.bytes, test_input, "Identity precompile should return the input data");
877
878 let constant_bytes = Bytes::from_static(b"constant value");
881
882 spec_precompiles.map_precompile(&identity_address, move |_original_dyn| {
884 (|_input: PrecompileInput<'_>| -> PrecompileResult {
886 Ok(PrecompileOutput::new(10, Bytes::from_static(b"constant value")))
887 })
888 .into()
889 });
890
891 let dyn_precompile = match &spec_precompiles.precompiles {
893 PrecompilesKind::Dynamic(dyn_precompiles) => {
894 dyn_precompiles.inner.get(&identity_address).unwrap()
895 }
896 _ => panic!("Expected dynamic precompiles"),
897 };
898
899 let result = dyn_precompile
900 .call(PrecompileInput {
901 data: &test_input,
902 gas: gas_limit,
903 caller: Address::ZERO,
904 value: U256::ZERO,
905 internals: EvmInternals::new(&mut ctx.journaled_state, &ctx.block),
906 target_address: identity_address,
907 bytecode_address: identity_address,
908 })
909 .unwrap();
910 assert_eq!(
911 result.bytes, constant_bytes,
912 "Modified precompile should return the constant value"
913 );
914 }
915
916 #[test]
917 fn test_closure_precompile() {
918 let test_input = Bytes::from_static(b"test data");
919 let expected_output = Bytes::from_static(b"processed: test data");
920 let gas_limit = 1000;
921
922 let mut ctx = EthEvmContext::new(EmptyDB::default(), Default::default());
923
924 let closure_precompile = |input: PrecompileInput<'_>| -> PrecompileResult {
926 let _timestamp = input.internals.block_env().timestamp();
927 let mut output = b"processed: ".to_vec();
928 output.extend_from_slice(input.data.as_ref());
929 Ok(PrecompileOutput::new(15, Bytes::from(output)))
930 };
931
932 let dyn_precompile: DynPrecompile = closure_precompile.into();
933
934 let result = dyn_precompile
935 .call(PrecompileInput {
936 data: &test_input,
937 gas: gas_limit,
938 caller: Address::ZERO,
939 value: U256::ZERO,
940 internals: EvmInternals::new(&mut ctx.journaled_state, &ctx.block),
941 target_address: Address::ZERO,
942 bytecode_address: Address::ZERO,
943 })
944 .unwrap();
945 assert_eq!(result.gas_used, 15);
946 assert_eq!(result.bytes, expected_output);
947 }
948
949 #[test]
950 fn test_is_pure() {
951 let closure_precompile = |_input: PrecompileInput<'_>| -> PrecompileResult {
953 Ok(PrecompileOutput::new(10, Bytes::from_static(b"output")))
954 };
955
956 let dyn_precompile: DynPrecompile = closure_precompile.into();
957 assert!(dyn_precompile.is_pure(), "should be pure by default");
958
959 let stateful_precompile =
961 DynPrecompile::new_stateful(PrecompileId::Custom("closure".into()), closure_precompile);
962 assert!(!stateful_precompile.is_pure(), "PurePrecompile should return true for is_pure");
963
964 let either_left = Either::<DynPrecompile, DynPrecompile>::Left(stateful_precompile);
965 assert!(!either_left.is_pure(), "Either::Left with non-pure should return false");
966
967 let either_right = Either::<DynPrecompile, DynPrecompile>::Right(dyn_precompile);
968 assert!(either_right.is_pure(), "Either::Right with pure should return true");
969 }
970
971 #[test]
972 fn test_precompile_lookup() {
973 let eth_precompiles = EthPrecompiles::default();
974 let mut spec_precompiles = PrecompilesMap::from(eth_precompiles);
975
976 let mut ctx = EthEvmContext::new(EmptyDB::default(), Default::default());
977
978 let dynamic_prefix = [0xDE, 0xAD];
980
981 spec_precompiles.set_precompile_lookup(move |address: &Address| {
983 if address.as_slice().starts_with(&dynamic_prefix) {
984 Some(DynPrecompile::new(PrecompileId::Custom("dynamic".into()), |_input| {
985 Ok(PrecompileOutput {
986 gas_used: 100,
987 gas_refunded: 0,
988 bytes: Bytes::from("dynamic precompile response"),
989 reverted: false,
990 })
991 }))
992 } else {
993 None
994 }
995 });
996
997 let identity_address = address!("0x0000000000000000000000000000000000000004");
999 assert!(spec_precompiles.get(&identity_address).is_some());
1000
1001 let dynamic_address = address!("0xDEAD000000000000000000000000000000000001");
1003 let dynamic_precompile = spec_precompiles.get(&dynamic_address);
1004 assert!(dynamic_precompile.is_some(), "Dynamic precompile should be found");
1005
1006 let result = dynamic_precompile
1008 .unwrap()
1009 .call(PrecompileInput {
1010 data: &[],
1011 gas: 1000,
1012 caller: Address::ZERO,
1013 value: U256::ZERO,
1014 internals: EvmInternals::new(&mut ctx.journaled_state, &ctx.block),
1015 target_address: dynamic_address,
1016 bytecode_address: dynamic_address,
1017 })
1018 .unwrap();
1019 assert_eq!(result.gas_used, 100);
1020 assert_eq!(result.bytes, Bytes::from("dynamic precompile response"));
1021
1022 let non_matching_address = address!("0x1234000000000000000000000000000000000001");
1024 assert!(spec_precompiles.get(&non_matching_address).is_none());
1025 }
1026
1027 #[test]
1028 fn test_get_precompile() {
1029 let eth_precompiles = EthPrecompiles::default();
1030 let spec_precompiles = PrecompilesMap::from(eth_precompiles);
1031
1032 let mut ctx = EthEvmContext::new(EmptyDB::default(), Default::default());
1033
1034 let identity_address = address!("0x0000000000000000000000000000000000000004");
1035 let test_input = Bytes::from_static(b"test data");
1036 let gas_limit = 1000;
1037
1038 let precompile = spec_precompiles.get(&identity_address);
1039 assert!(precompile.is_some(), "Identity precompile should exist");
1040
1041 let result = precompile
1042 .unwrap()
1043 .call(PrecompileInput {
1044 data: &test_input,
1045 gas: gas_limit,
1046 caller: Address::ZERO,
1047 value: U256::ZERO,
1048 target_address: identity_address,
1049 bytecode_address: identity_address,
1050 internals: EvmInternals::new(&mut ctx.journaled_state, &ctx.block),
1051 })
1052 .unwrap();
1053 assert_eq!(result.bytes, test_input, "Identity precompile should return the input data");
1054
1055 let nonexistent_address = address!("0x0000000000000000000000000000000000000099");
1056 assert!(
1057 spec_precompiles.get(&nonexistent_address).is_none(),
1058 "Non-existent precompile should not be found"
1059 );
1060
1061 let mut dynamic_precompiles = spec_precompiles;
1062 dynamic_precompiles.ensure_dynamic_precompiles();
1063
1064 let dyn_precompile = dynamic_precompiles.get(&identity_address);
1065 assert!(
1066 dyn_precompile.is_some(),
1067 "Identity precompile should exist after conversion to dynamic"
1068 );
1069
1070 let result = dyn_precompile
1071 .unwrap()
1072 .call(PrecompileInput {
1073 data: &test_input,
1074 gas: gas_limit,
1075 caller: Address::ZERO,
1076 value: U256::ZERO,
1077 internals: EvmInternals::new(&mut ctx.journaled_state, &ctx.block),
1078 target_address: identity_address,
1079 bytecode_address: identity_address,
1080 })
1081 .unwrap();
1082 assert_eq!(
1083 result.bytes, test_input,
1084 "Identity precompile should return the input data after conversion to dynamic"
1085 );
1086 }
1087}