1
1
//! Support for "cheat codes" / bypass functions
2
2
3
- use alloy_primitives:: { Address , map:: AddressHashSet } ;
3
+ use alloy_evm:: precompiles:: { Precompile , PrecompileInput } ;
4
+ use alloy_primitives:: {
5
+ Address , Bytes ,
6
+ map:: { AddressHashSet , foldhash:: HashMap } ,
7
+ } ;
4
8
use parking_lot:: RwLock ;
9
+ use revm:: precompile:: {
10
+ PrecompileError , PrecompileOutput , PrecompileResult , secp256k1:: ec_recover_run,
11
+ utilities:: right_pad,
12
+ } ;
5
13
use std:: sync:: Arc ;
6
14
7
15
/// Manages user modifications that may affect the node's behavior
@@ -61,6 +69,21 @@ impl CheatsManager {
61
69
pub fn impersonated_accounts ( & self ) -> AddressHashSet {
62
70
self . state . read ( ) . impersonated_accounts . clone ( )
63
71
}
72
+
73
+ /// Registers an override so that `ecrecover(signature)` returns `addr`.
74
+ pub fn add_recover_override ( & self , sig : Bytes , addr : Address ) {
75
+ self . state . write ( ) . signature_overrides . insert ( sig, addr) ;
76
+ }
77
+
78
+ /// If an override exists for `sig`, returns the address; otherwise `None`.
79
+ pub fn get_recover_override ( & self , sig : & Bytes ) -> Option < Address > {
80
+ self . state . read ( ) . signature_overrides . get ( sig) . copied ( )
81
+ }
82
+
83
+ /// Returns true if any ecrecover overrides have been registered.
84
+ pub fn has_recover_overrides ( & self ) -> bool {
85
+ !self . state . read ( ) . signature_overrides . is_empty ( )
86
+ }
64
87
}
65
88
66
89
/// Container type for all the state variables
@@ -70,4 +93,47 @@ pub struct CheatsState {
70
93
pub impersonated_accounts : AddressHashSet ,
71
94
/// If set to true will make the `is_impersonated` function always return true
72
95
pub auto_impersonate_accounts : bool ,
96
+ /// Overrides for ecrecover: Signature => Address
97
+ pub signature_overrides : HashMap < Bytes , Address > ,
98
+ }
99
+
100
+ impl CheatEcrecover {
101
+ pub fn new ( cheats : Arc < CheatsManager > ) -> Self {
102
+ Self { cheats }
103
+ }
104
+ }
105
+
106
+ impl Precompile for CheatEcrecover {
107
+ fn call ( & self , input : PrecompileInput < ' _ > ) -> PrecompileResult {
108
+ if !self . cheats . has_recover_overrides ( ) {
109
+ return ec_recover_run ( input. data , input. gas ) ;
110
+ }
111
+
112
+ const ECRECOVER_BASE : u64 = 3_000 ;
113
+ if input. gas < ECRECOVER_BASE {
114
+ return Err ( PrecompileError :: OutOfGas ) ;
115
+ }
116
+ let padded = right_pad :: < 128 > ( input. data ) ;
117
+ let v = padded[ 63 ] ;
118
+ let mut sig_bytes = [ 0u8 ; 65 ] ;
119
+ sig_bytes[ ..64 ] . copy_from_slice ( & padded[ 64 ..128 ] ) ;
120
+ sig_bytes[ 64 ] = v;
121
+ let sig_bytes_wrapped = Bytes :: copy_from_slice ( & sig_bytes) ;
122
+ if let Some ( addr) = self . cheats . get_recover_override ( & sig_bytes_wrapped) {
123
+ let mut out = [ 0u8 ; 32 ] ;
124
+ out[ 12 ..] . copy_from_slice ( addr. as_slice ( ) ) ;
125
+ return Ok ( PrecompileOutput :: new ( ECRECOVER_BASE , Bytes :: copy_from_slice ( & out) ) ) ;
126
+ }
127
+ ec_recover_run ( input. data , input. gas )
128
+ }
129
+
130
+ fn is_pure ( & self ) -> bool {
131
+ false
132
+ }
133
+ }
134
+
135
+ /// A custom ecrecover precompile that supports cheat-based signature overrides.
136
+ #[ derive( Clone , Debug ) ]
137
+ pub struct CheatEcrecover {
138
+ cheats : Arc < CheatsManager > ,
73
139
}
0 commit comments