anvil/eth/backend/
cheats.rs1use 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};
14
15static PRECOMPILE_ID_CHEAT_ECRECOVER: PrecompileId =
17 PrecompileId::Custom(Cow::Borrowed("cheat_ecrecover"));
18
19#[derive(Clone, Debug, Default)]
23pub struct CheatsManager {
24 state: Arc<RwLock<CheatsState>>,
26}
27
28impl CheatsManager {
29 pub fn impersonate(&self, addr: Address) -> bool {
33 trace!(target: "cheats", %addr, "start impersonating");
34 !self.state.write().impersonated_accounts.insert(addr)
38 }
39
40 pub fn stop_impersonating(&self, addr: &Address) {
42 trace!(target: "cheats", %addr, "stop impersonating");
43 self.state.write().impersonated_accounts.remove(addr);
44 }
45
46 pub fn is_impersonated(&self, addr: Address) -> bool {
48 if self.auto_impersonate_accounts() {
49 true
50 } else {
51 self.state.read().impersonated_accounts.contains(&addr)
52 }
53 }
54
55 pub fn auto_impersonate_accounts(&self) -> bool {
57 self.state.read().auto_impersonate_accounts
58 }
59
60 pub fn set_auto_impersonate_account(&self, enabled: bool) {
63 trace!(target: "cheats", "Auto impersonation set to {:?}", enabled);
64 self.state.write().auto_impersonate_accounts = enabled
65 }
66
67 pub fn impersonated_accounts(&self) -> AddressHashSet {
69 self.state.read().impersonated_accounts.clone()
70 }
71
72 pub fn add_recover_override(&self, sig: Bytes, addr: Address) {
74 self.state.write().signature_overrides.insert(sig, addr);
75 }
76
77 pub fn get_recover_override(&self, sig: &Bytes) -> Option<Address> {
79 self.state.read().signature_overrides.get(sig).copied()
80 }
81
82 pub fn has_recover_overrides(&self) -> bool {
84 !self.state.read().signature_overrides.is_empty()
85 }
86}
87
88#[derive(Clone, Debug, Default)]
90pub struct CheatsState {
91 pub impersonated_accounts: AddressHashSet,
93 pub auto_impersonate_accounts: bool,
95 pub signature_overrides: HashMap<Bytes, Address>,
97}
98
99impl CheatEcrecover {
100 pub fn new(cheats: Arc<CheatsManager>) -> Self {
101 Self { cheats }
102 }
103}
104
105impl Precompile for CheatEcrecover {
106 fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
107 if !self.cheats.has_recover_overrides() {
108 return ec_recover_run(input.data, input.gas);
109 }
110
111 const ECRECOVER_BASE: u64 = 3_000;
112 if input.gas < ECRECOVER_BASE {
113 return Err(PrecompileError::OutOfGas);
114 }
115 let padded = right_pad::<128>(input.data);
116 let v = padded[63];
117 let mut sig_bytes = [0u8; 65];
118 sig_bytes[..64].copy_from_slice(&padded[64..128]);
119 sig_bytes[64] = v;
120 let sig_bytes_wrapped = Bytes::copy_from_slice(&sig_bytes);
121 if let Some(addr) = self.cheats.get_recover_override(&sig_bytes_wrapped) {
122 let mut out = [0u8; 32];
123 out[12..].copy_from_slice(addr.as_slice());
124 return Ok(PrecompileOutput::new(ECRECOVER_BASE, Bytes::copy_from_slice(&out)));
125 }
126 ec_recover_run(input.data, input.gas)
127 }
128
129 fn precompile_id(&self) -> &PrecompileId {
130 &PRECOMPILE_ID_CHEAT_ECRECOVER
131 }
132
133 fn is_pure(&self) -> bool {
134 false
135 }
136}
137
138#[derive(Clone, Debug)]
140pub struct CheatEcrecover {
141 cheats: Arc<CheatsManager>,
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn impersonate_returns_false_then_true() {
150 let mgr = CheatsManager::default();
151 let addr = Address::from([1u8; 20]);
152 assert!(!mgr.impersonate(addr));
153 assert!(mgr.impersonate(addr));
154 }
155}