11//! Support for "cheat codes" / bypass functions
22
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+ } ;
48use parking_lot:: RwLock ;
9+ use revm:: precompile:: {
10+ PrecompileError , PrecompileOutput , PrecompileResult , secp256k1:: ec_recover_run,
11+ utilities:: right_pad,
12+ } ;
513use std:: sync:: Arc ;
614
715/// Manages user modifications that may affect the node's behavior
@@ -61,6 +69,21 @@ impl CheatsManager {
6169 pub fn impersonated_accounts ( & self ) -> AddressHashSet {
6270 self . state . read ( ) . impersonated_accounts . clone ( )
6371 }
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+ }
6487}
6588
6689/// Container type for all the state variables
@@ -70,4 +93,47 @@ pub struct CheatsState {
7093 pub impersonated_accounts : AddressHashSet ,
7194 /// If set to true will make the `is_impersonated` function always return true
7295 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 > ,
73139}
0 commit comments