1//! Support for "cheat codes" / bypass functions
23use alloy_evm::precompiles::{Precompile, PrecompileInput};
4use alloy_primitives::{
5 Address, Bytes,
6 map::{AddressHashSet, foldhash::HashMap},
7};
8use parking_lot::RwLock;
9use revm::precompile::{
10 PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult, secp256k1::ec_recover_run,
11 utilities::right_pad,
12};
13use std::{borrow::Cow, sync::Arc};
1415/// ID for the [`CheatEcrecover::precompile_id`] precompile.
16static PRECOMPILE_ID_CHEAT_ECRECOVER: PrecompileId =
17 PrecompileId::Custom(Cow::Borrowed("cheat_ecrecover"));
1819/// Manages user modifications that may affect the node's behavior
20///
21/// Contains the state of executed, non-eth standard cheat code RPC
22#[derive(Clone, Debug, Default)]
23pub struct CheatsManager {
24/// shareable state
25state: Arc<RwLock<CheatsState>>,
26}
2728impl CheatsManager {
29/// Sets the account to impersonate
30 ///
31 /// Returns `true` if the account is already impersonated
32pub fn impersonate(&self, addr: Address) -> bool {
33trace!(target: "cheats", "Start impersonating {:?}", addr);
34let mut state = self.state.write();
35// When somebody **explicitly** impersonates an account we need to store it so we are able
36 // to return it from `eth_accounts`. That's why we do not simply call `is_impersonated()`
37 // which does not check that list when auto impersonation is enabled.
38if state.impersonated_accounts.contains(&addr) {
39// need to check if already impersonated, so we don't overwrite the code
40return true;
41 }
42state.impersonated_accounts.insert(addr)
43 }
4445/// Removes the account that from the impersonated set
46pub fn stop_impersonating(&self, addr: &Address) {
47trace!(target: "cheats", "Stop impersonating {:?}", addr);
48self.state.write().impersonated_accounts.remove(addr);
49 }
5051/// Returns true if the `addr` is currently impersonated
52pub fn is_impersonated(&self, addr: Address) -> bool {
53if self.auto_impersonate_accounts() {
54true
55} else {
56self.state.read().impersonated_accounts.contains(&addr)
57 }
58 }
5960/// Returns true is auto impersonation is enabled
61pub fn auto_impersonate_accounts(&self) -> bool {
62self.state.read().auto_impersonate_accounts
63 }
6465/// Sets the auto impersonation flag which if set to true will make the `is_impersonated`
66 /// function always return true
67pub fn set_auto_impersonate_account(&self, enabled: bool) {
68trace!(target: "cheats", "Auto impersonation set to {:?}", enabled);
69self.state.write().auto_impersonate_accounts = enabled70 }
7172/// Returns all accounts that are currently being impersonated.
73pub fn impersonated_accounts(&self) -> AddressHashSet {
74self.state.read().impersonated_accounts.clone()
75 }
7677/// Registers an override so that `ecrecover(signature)` returns `addr`.
78pub fn add_recover_override(&self, sig: Bytes, addr: Address) {
79self.state.write().signature_overrides.insert(sig, addr);
80 }
8182/// If an override exists for `sig`, returns the address; otherwise `None`.
83pub fn get_recover_override(&self, sig: &Bytes) -> Option<Address> {
84self.state.read().signature_overrides.get(sig).copied()
85 }
8687/// Returns true if any ecrecover overrides have been registered.
88pub fn has_recover_overrides(&self) -> bool {
89 !self.state.read().signature_overrides.is_empty()
90 }
91}
9293/// Container type for all the state variables
94#[derive(Clone, Debug, Default)]
95pub struct CheatsState {
96/// All accounts that are currently impersonated
97pub impersonated_accounts: AddressHashSet,
98/// If set to true will make the `is_impersonated` function always return true
99pub auto_impersonate_accounts: bool,
100/// Overrides for ecrecover: Signature => Address
101pub signature_overrides: HashMap<Bytes, Address>,
102}
103104impl CheatEcrecover {
105pub fn new(cheats: Arc<CheatsManager>) -> Self {
106Self { cheats }
107 }
108}
109110impl Precompile for CheatEcrecover {
111fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
112if !self.cheats.has_recover_overrides() {
113return ec_recover_run(input.data, input.gas);
114 }
115116const ECRECOVER_BASE: u64 = 3_000;
117if input.gas < ECRECOVER_BASE {
118return Err(PrecompileError::OutOfGas);
119 }
120let padded = right_pad::<128>(input.data);
121let v = padded[63];
122let mut sig_bytes = [0u8; 65];
123sig_bytes[..64].copy_from_slice(&padded[64..128]);
124sig_bytes[64] = v;
125let sig_bytes_wrapped = Bytes::copy_from_slice(&sig_bytes);
126if let Some(addr) = self.cheats.get_recover_override(&sig_bytes_wrapped) {
127let mut out = [0u8; 32];
128out[12..].copy_from_slice(addr.as_slice());
129return Ok(PrecompileOutput::new(ECRECOVER_BASE, Bytes::copy_from_slice(&out)));
130 }
131 ec_recover_run(input.data, input.gas)
132 }
133134fn precompile_id(&self) -> &PrecompileId {
135&PRECOMPILE_ID_CHEAT_ECRECOVER136 }
137138fn is_pure(&self) -> bool {
139false
140}
141}
142143/// A custom ecrecover precompile that supports cheat-based signature overrides.
144#[derive(Clone, Debug)]
145pub struct CheatEcrecover {
146 cheats: Arc<CheatsManager>,
147}