anvil/eth/backend/
cheats.rs1use alloy_evm::precompiles::{Precompile, PrecompileInput};
4use alloy_primitives::{
5 Address, B256, Bytes,
6 map::{AddressHashSet, foldhash::HashMap},
7};
8use parking_lot::RwLock;
9use revm::precompile::{
10 PrecompileHalt, PrecompileId, PrecompileOutput, PrecompileResult, call_eth_precompile,
11 secp256k1::ec_recover_run, 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 pub fn set_next_block_prevrandao(&self, prevrandao: B256) {
92 trace!(target: "cheats", %prevrandao, "set next block prevrandao");
93 let mut state = self.state.write();
94 state.prevrandao_generation = state.prevrandao_generation.wrapping_add(1);
95 state.next_block_prevrandao = Some(prevrandao);
96 }
97
98 pub(crate) fn prepare_next_block_prevrandao(&self) -> Option<PendingPrevrandao> {
100 let state = self.state.read();
101 state
102 .next_block_prevrandao
103 .map(|value| PendingPrevrandao { value, generation: state.prevrandao_generation })
104 }
105
106 pub fn take_next_block_prevrandao(&self) -> Option<B256> {
108 let mut state = self.state.write();
109 state.prevrandao_generation = state.prevrandao_generation.wrapping_add(1);
110 state.next_block_prevrandao.take()
111 }
112
113 pub(crate) fn consume_next_block_prevrandao(&self, pending: PendingPrevrandao) {
115 let mut state = self.state.write();
116 if state.prevrandao_generation == pending.generation {
117 state.next_block_prevrandao.take();
118 }
119 }
120
121 pub fn clear_next_block_prevrandao(&self) {
126 let mut state = self.state.write();
127 state.prevrandao_generation = state.prevrandao_generation.wrapping_add(1);
128 state.next_block_prevrandao.take();
129 }
130}
131
132#[derive(Clone, Debug, Default)]
134pub struct CheatsState {
135 pub impersonated_accounts: AddressHashSet,
137 pub auto_impersonate_accounts: bool,
139 pub signature_overrides: HashMap<Bytes, Address>,
141 pub next_block_prevrandao: Option<B256>,
144 prevrandao_generation: u64,
146}
147
148#[derive(Clone, Copy, Debug)]
150pub(crate) struct PendingPrevrandao {
151 pub(crate) value: B256,
152 generation: u64,
153}
154
155impl CheatEcrecover {
156 pub const fn new(cheats: Arc<CheatsManager>) -> Self {
157 Self { cheats }
158 }
159}
160
161impl Precompile for CheatEcrecover {
162 fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
163 if !self.cheats.has_recover_overrides() {
164 return Ok(call_eth_precompile(ec_recover_run, input.data, input.gas, input.reservoir));
165 }
166
167 const ECRECOVER_BASE: u64 = 3_000;
168 if input.gas < ECRECOVER_BASE {
169 return Ok(PrecompileOutput::halt(PrecompileHalt::OutOfGas, input.reservoir));
170 }
171 let padded = right_pad::<128>(input.data);
172 let v = padded[63];
173 let mut sig_bytes = [0u8; 65];
174 sig_bytes[..64].copy_from_slice(&padded[64..128]);
175 sig_bytes[64] = v;
176 let sig_bytes_wrapped = Bytes::copy_from_slice(&sig_bytes);
177 if let Some(addr) = self.cheats.get_recover_override(&sig_bytes_wrapped) {
178 let mut out = [0u8; 32];
179 out[12..].copy_from_slice(addr.as_slice());
180 return Ok(PrecompileOutput::new(
181 ECRECOVER_BASE,
182 Bytes::copy_from_slice(&out),
183 input.reservoir,
184 ));
185 }
186 Ok(call_eth_precompile(ec_recover_run, input.data, input.gas, input.reservoir))
187 }
188
189 fn precompile_id(&self) -> &PrecompileId {
190 &PRECOMPILE_ID_CHEAT_ECRECOVER
191 }
192
193 fn supports_caching(&self) -> bool {
194 false
195 }
196}
197
198#[derive(Clone, Debug)]
200pub struct CheatEcrecover {
201 cheats: Arc<CheatsManager>,
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207
208 #[test]
209 fn candidate_consumes_only_its_prevrandao_override() {
210 let cheats = CheatsManager::default();
211 let value = B256::with_last_byte(1);
212 cheats.set_next_block_prevrandao(value);
213 let pending = cheats.prepare_next_block_prevrandao().unwrap();
214
215 cheats.set_next_block_prevrandao(value);
216 cheats.consume_next_block_prevrandao(pending);
217
218 assert_eq!(cheats.prepare_next_block_prevrandao().unwrap().value, value);
219 }
220
221 #[test]
222 fn impersonate_returns_false_then_true() {
223 let mgr = CheatsManager::default();
224 let addr = Address::from([1u8; 20]);
225 assert!(!mgr.impersonate(addr));
226 assert!(mgr.impersonate(addr));
227 }
228}