Skip to content

Commit 084c0ad

Browse files
committed
instead of activating all precompiles by default we activate selectively based on the spec defined
1 parent ec4ffd3 commit 084c0ad

File tree

3 files changed

+168
-41
lines changed

3 files changed

+168
-41
lines changed

crates/anvil/src/eth/backend/executor.rs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use revm::{
3333
database::WrapDatabaseRef,
3434
handler::{instructions::EthInstructions, EthPrecompiles},
3535
interpreter::InstructionResult,
36+
precompile::{PrecompileSpecId, Precompiles},
3637
primitives::hardfork::SpecId,
3738
Database, DatabaseRef, Inspector, Journal,
3839
};
@@ -428,6 +429,7 @@ where
428429
I: Inspector<EthEvmContext<DB>> + Inspector<OpContext<DB>>,
429430
{
430431
if env.is_optimism {
432+
let op_cfg = env.evm_env.cfg_env.clone().with_spec(op_revm::OpSpecId::BEDROCK);
431433
let op_context = OpContext {
432434
journaled_state: {
433435
let mut journal = Journal::new(db);
@@ -436,28 +438,30 @@ where
436438
journal
437439
},
438440
block: env.evm_env.block_env.clone(),
439-
cfg: env.evm_env.cfg_env.clone().with_spec(op_revm::OpSpecId::BEDROCK),
441+
cfg: op_cfg.clone(),
440442
tx: env.tx.clone(),
441443
chain: L1BlockInfo::default(),
442444
local: LocalContext::default(),
443445
error: Ok(()),
444446
};
445447

446-
let evm = op_revm::OpEvm(RevmEvm::new_with_inspector(
448+
let op_precompiles = OpPrecompiles::new_with_spec(op_cfg.spec).precompiles();
449+
let op_evm = op_revm::OpEvm(RevmEvm::new_with_inspector(
447450
op_context,
448451
inspector,
449452
EthInstructions::default(),
450-
PrecompilesMap::from_static(OpPrecompiles::default().precompiles()),
453+
PrecompilesMap::from_static(op_precompiles),
451454
));
452455

453-
let op = OpEvm::new(evm, true);
456+
let op = OpEvm::new(op_evm, true);
454457

455458
EitherEvm::Op(op)
456459
} else {
457-
let evm_context = EthEvmContext {
460+
let spec = env.evm_env.cfg_env.spec;
461+
let eth_context = EthEvmContext {
458462
journaled_state: {
459463
let mut journal = Journal::new(db);
460-
journal.set_spec_id(env.evm_env.cfg_env.spec);
464+
journal.set_spec_id(spec);
461465
journal
462466
},
463467
block: env.evm_env.block_env.clone(),
@@ -468,14 +472,19 @@ where
468472
error: Ok(()),
469473
};
470474

471-
let evm = RevmEvm::new_with_inspector(
472-
evm_context,
475+
let eth_precompiles = EthPrecompiles {
476+
precompiles: Precompiles::new(PrecompileSpecId::from_spec_id(spec)),
477+
spec,
478+
}
479+
.precompiles;
480+
let eth_evm = RevmEvm::new_with_inspector(
481+
eth_context,
473482
inspector,
474483
EthInstructions::default(),
475-
PrecompilesMap::from_static(EthPrecompiles::default().precompiles),
484+
PrecompilesMap::from_static(eth_precompiles),
476485
);
477486

478-
let eth = EthEvm::new(evm, true);
487+
let eth = EthEvm::new(eth_evm, true);
479488

480489
EitherEvm::Eth(eth)
481490
}

crates/anvil/src/evm.rs

Lines changed: 133 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -32,24 +32,37 @@ pub fn inject_precompiles<DB, I>(
3232

3333
#[cfg(test)]
3434
mod tests {
35+
use std::convert::Infallible;
36+
3537
use alloy_evm::{eth::EthEvmContext, precompiles::PrecompilesMap, EthEvm, Evm, EvmEnv};
3638
use alloy_op_evm::OpEvm;
3739
use alloy_primitives::{address, Address, Bytes, TxKind};
3840
use foundry_evm_core::either_evm::EitherEvm;
3941
use itertools::Itertools;
40-
use op_revm::{precompiles::OpPrecompiles, L1BlockInfo, OpContext, OpTransaction};
42+
use op_revm::{precompiles::OpPrecompiles, L1BlockInfo, OpContext, OpSpecId, OpTransaction};
4143
use revm::{
42-
context::{Evm as RevmEvm, JournalTr, LocalContext, TxEnv},
43-
database::EmptyDB,
44+
context::{CfgEnv, Evm as RevmEvm, JournalTr, LocalContext, TxEnv},
45+
database::{EmptyDB, EmptyDBTyped},
4446
handler::{instructions::EthInstructions, EthPrecompiles},
4547
inspector::NoOpInspector,
4648
interpreter::interpreter::EthInterpreter,
47-
precompile::{PrecompileOutput, PrecompileResult, PrecompileWithAddress},
49+
precompile::{
50+
PrecompileOutput, PrecompileResult, PrecompileSpecId, PrecompileWithAddress,
51+
Precompiles,
52+
},
53+
primitives::hardfork::SpecId,
4854
Journal,
4955
};
5056

5157
use crate::{inject_precompiles, PrecompileFactory};
5258

59+
// A precompile activated in the `Prague` spec.
60+
const ETH_PRAGUE_PRECOMPILE: Address = address!("0x0000000000000000000000000000000000000011");
61+
62+
// A precompile activated in the `Fjord` spec.
63+
const OP_FROJD_PRECOMPILE: Address = address!("0x0000000000000000000000000000000000000100");
64+
65+
// A custom precompile address and payload for testing.
5366
const PRECOMPILE_ADDR: Address = address!("0x0000000000000000000000000000000000000071");
5467
const PAYLOAD: &[u8] = &[0xde, 0xad, 0xbe, 0xef];
5568

@@ -71,10 +84,13 @@ mod tests {
7184
Ok(PrecompileOutput { bytes: Bytes::copy_from_slice(input), gas_used: 0 })
7285
}
7386

74-
#[test]
75-
fn build_eth_evm_with_extra_precompiles() {
87+
/// Creates a new EVM instance with the custom precompile factory.
88+
fn create_eth_evm(
89+
spec: SpecId,
90+
) -> (foundry_evm::Env, EitherEvm<EmptyDBTyped<Infallible>, NoOpInspector, PrecompilesMap>)
91+
{
7692
let eth_env = foundry_evm::Env {
77-
evm_env: EvmEnv { block_env: Default::default(), cfg_env: Default::default() },
93+
evm_env: EvmEnv { block_env: Default::default(), cfg_env: CfgEnv::new_with_spec(spec) },
7894
tx: TxEnv {
7995
kind: TxKind::Call(PRECOMPILE_ADDR),
8096
data: PAYLOAD.into(),
@@ -92,35 +108,34 @@ mod tests {
92108
error: Ok(()),
93109
};
94110

95-
let mut evm = EitherEvm::Eth(EthEvm::new(
111+
let eth_precompiles = EthPrecompiles {
112+
precompiles: Precompiles::new(PrecompileSpecId::from_spec_id(spec)),
113+
spec,
114+
}
115+
.precompiles;
116+
let eth_evm = EitherEvm::Eth(EthEvm::new(
96117
RevmEvm::new_with_inspector(
97118
eth_evm_context,
98119
NoOpInspector,
99120
EthInstructions::<EthInterpreter, EthEvmContext<EmptyDB>>::default(),
100-
PrecompilesMap::from_static(EthPrecompiles::default().precompiles),
121+
PrecompilesMap::from_static(eth_precompiles),
101122
),
102123
true,
103124
));
104125

105-
assert!(!evm.precompiles_mut().addresses().contains(&PRECOMPILE_ADDR));
106-
107-
inject_precompiles(&mut evm, CustomPrecompileFactory.precompiles());
108-
109-
assert!(evm.precompiles_mut().addresses().contains(&PRECOMPILE_ADDR));
110-
111-
let result = match &mut evm {
112-
EitherEvm::Eth(eth_evm) => eth_evm.transact(eth_env.tx).unwrap(),
113-
_ => unreachable!(),
114-
};
115-
116-
assert!(result.result.is_success());
117-
assert_eq!(result.result.output(), Some(&PAYLOAD.into()));
126+
(eth_env, eth_evm)
118127
}
119128

120-
#[test]
121-
fn build_op_evm_with_extra_precompiles() {
129+
/// Creates a new OP EVM instance with the custom precompile factory.
130+
fn create_op_evm(
131+
spec: SpecId,
132+
op_spec: OpSpecId,
133+
) -> (
134+
crate::eth::backend::env::Env,
135+
EitherEvm<EmptyDBTyped<Infallible>, NoOpInspector, PrecompilesMap>,
136+
) {
122137
let op_env = crate::eth::backend::env::Env {
123-
evm_env: EvmEnv { block_env: Default::default(), cfg_env: Default::default() },
138+
evm_env: EvmEnv { block_env: Default::default(), cfg_env: CfgEnv::new_with_spec(spec) },
124139
tx: OpTransaction::<TxEnv> {
125140
base: TxEnv {
126141
kind: TxKind::Call(PRECOMPILE_ADDR),
@@ -132,6 +147,7 @@ mod tests {
132147
is_optimism: true,
133148
};
134149

150+
let op_cfg = op_env.evm_env.cfg_env.clone().with_spec(op_spec);
135151
let op_evm_context = OpContext {
136152
journaled_state: {
137153
let mut journal = Journal::new(EmptyDB::default());
@@ -140,31 +156,119 @@ mod tests {
140156
journal
141157
},
142158
block: op_env.evm_env.block_env.clone(),
143-
cfg: op_env.evm_env.cfg_env.clone().with_spec(op_revm::OpSpecId::BEDROCK),
159+
cfg: op_cfg.clone(),
144160
tx: op_env.tx.clone(),
145161
chain: L1BlockInfo::default(),
146162
local: LocalContext::default(),
147163
error: Ok(()),
148164
};
149165

150-
let mut evm = EitherEvm::Op(OpEvm::new(
166+
let op_precompiles = OpPrecompiles::new_with_spec(op_cfg.spec).precompiles();
167+
let op_evm = EitherEvm::Op(OpEvm::new(
151168
op_revm::OpEvm(RevmEvm::new_with_inspector(
152169
op_evm_context,
153170
NoOpInspector,
154171
EthInstructions::<EthInterpreter, OpContext<EmptyDB>>::default(),
155-
PrecompilesMap::from_static(OpPrecompiles::default().precompiles()),
172+
PrecompilesMap::from_static(op_precompiles),
156173
)),
157174
true,
158175
));
159176

177+
(op_env, op_evm)
178+
}
179+
180+
#[test]
181+
fn build_eth_evm_with_extra_precompiles_default_spec() {
182+
let (env, mut evm) = create_eth_evm(SpecId::default());
183+
184+
// Check that the Prague precompile IS present when using the default spec.
185+
assert!(evm.precompiles_mut().addresses().contains(&ETH_PRAGUE_PRECOMPILE));
186+
187+
assert!(!evm.precompiles_mut().addresses().contains(&PRECOMPILE_ADDR));
188+
189+
inject_precompiles(&mut evm, CustomPrecompileFactory.precompiles());
190+
191+
assert!(evm.precompiles_mut().addresses().contains(&PRECOMPILE_ADDR));
192+
193+
let result = match &mut evm {
194+
EitherEvm::Eth(eth_evm) => eth_evm.transact(env.tx).unwrap(),
195+
_ => unreachable!(),
196+
};
197+
198+
assert!(result.result.is_success());
199+
assert_eq!(result.result.output(), Some(&PAYLOAD.into()));
200+
}
201+
202+
#[test]
203+
fn build_eth_evm_with_extra_precompiles_london_spec() {
204+
let (env, mut evm) = create_eth_evm(SpecId::LONDON);
205+
206+
// Check that the Prague precompile IS NOT present when using the London spec.
207+
assert!(!evm.precompiles_mut().addresses().contains(&ETH_PRAGUE_PRECOMPILE));
208+
209+
assert!(!evm.precompiles_mut().addresses().contains(&PRECOMPILE_ADDR));
210+
211+
inject_precompiles(&mut evm, CustomPrecompileFactory.precompiles());
212+
213+
assert!(evm.precompiles_mut().addresses().contains(&PRECOMPILE_ADDR));
214+
215+
let result = match &mut evm {
216+
EitherEvm::Eth(eth_evm) => eth_evm.transact(env.tx).unwrap(),
217+
_ => unreachable!(),
218+
};
219+
220+
assert!(result.result.is_success());
221+
assert_eq!(result.result.output(), Some(&PAYLOAD.into()));
222+
}
223+
224+
#[test]
225+
fn build_op_evm_with_extra_precompiles_default_spec() {
226+
let (env, mut evm) = create_op_evm(
227+
SpecId::default(),
228+
// TODO: OpSpecId::ISTHMUS is not yet supported, fails with: `Missing operator fee
229+
// scalar for isthmus L1 Block`.
230+
OpSpecId::HOLOCENE,
231+
);
232+
233+
// Check that the Fjord precompile IS present when using the default spec.
234+
assert!(evm.precompiles_mut().addresses().contains(&OP_FROJD_PRECOMPILE));
235+
236+
// Check that the Prague precompile is NOT present when using the default spec.
237+
assert!(!evm.precompiles_mut().addresses().contains(&ETH_PRAGUE_PRECOMPILE));
238+
239+
assert!(!evm.precompiles_mut().addresses().contains(&PRECOMPILE_ADDR));
240+
241+
inject_precompiles(&mut evm, CustomPrecompileFactory.precompiles());
242+
243+
assert!(evm.precompiles_mut().addresses().contains(&PRECOMPILE_ADDR));
244+
245+
let result = match &mut evm {
246+
EitherEvm::Op(op_evm) => op_evm.transact(env.tx).unwrap(),
247+
_ => unreachable!(),
248+
};
249+
250+
assert!(result.result.is_success());
251+
assert_eq!(result.result.output(), Some(&PAYLOAD.into()));
252+
}
253+
254+
#[test]
255+
fn build_op_evm_with_extra_precompiles_bedrock_spec() {
256+
let (env, mut evm) = create_op_evm(SpecId::default(), OpSpecId::BEDROCK);
257+
258+
// Check that the Fjord precompile IS NOT present when using the `OpSpecId::BEDROCK` spec.
259+
assert!(!evm.precompiles_mut().addresses().contains(&OP_FROJD_PRECOMPILE));
260+
261+
// Check that the Prague precompile IS NOT present when using the `OpSpecId::BEDROCK` spec.
262+
assert!(!evm.precompiles_mut().addresses().contains(&ETH_PRAGUE_PRECOMPILE));
263+
160264
assert!(!evm.precompiles_mut().addresses().contains(&PRECOMPILE_ADDR));
161265

162266
inject_precompiles(&mut evm, CustomPrecompileFactory.precompiles());
163267

164268
assert!(evm.precompiles_mut().addresses().contains(&PRECOMPILE_ADDR));
165269

166270
let result = match &mut evm {
167-
EitherEvm::Op(op_evm) => op_evm.transact(op_env.tx).unwrap(),
271+
EitherEvm::Op(op_evm) => op_evm.transact(env.tx).unwrap(),
168272
_ => unreachable!(),
169273
};
170274

crates/evm/core/src/evm.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use revm::{
2222
CallValue, CreateInputs, CreateOutcome, FrameInput, Gas, InstructionResult,
2323
InterpreterResult,
2424
},
25+
precompile::{PrecompileSpecId, Precompiles},
2526
primitives::hardfork::SpecId,
2627
Context, ExecuteEvm, Journal,
2728
};
@@ -44,13 +45,19 @@ pub fn new_evm_with_inspector<'i, 'db, I: InspectorExt + ?Sized>(
4445
local: LocalContext::default(),
4546
error: Ok(()),
4647
};
48+
let spec = ctx.cfg.spec;
49+
let precompiles = EthPrecompiles {
50+
precompiles: Precompiles::new(PrecompileSpecId::from_spec_id(spec)),
51+
spec,
52+
}
53+
.precompiles;
4754

4855
FoundryEvm {
4956
inner: RevmEvm::new_with_inspector(
5057
ctx,
5158
inspector,
5259
EthInstructions::default(),
53-
PrecompilesMap::from_static(EthPrecompiles::default().precompiles),
60+
PrecompilesMap::from_static(precompiles),
5461
),
5562
}
5663
}
@@ -59,12 +66,19 @@ pub fn new_evm_with_existing_context<'a>(
5966
ctx: EthEvmContext<&'a mut dyn DatabaseExt>,
6067
inspector: &'a mut dyn InspectorExt,
6168
) -> FoundryEvm<'a, &'a mut dyn InspectorExt> {
69+
let spec = ctx.cfg.spec;
70+
let precompiles = EthPrecompiles {
71+
precompiles: Precompiles::new(PrecompileSpecId::from_spec_id(spec)),
72+
spec,
73+
}
74+
.precompiles;
75+
6276
FoundryEvm {
6377
inner: RevmEvm::new_with_inspector(
6478
ctx,
6579
inspector,
6680
EthInstructions::default(),
67-
PrecompilesMap::from_static(EthPrecompiles::default().precompiles),
81+
PrecompilesMap::from_static(precompiles),
6882
),
6983
}
7084
}

0 commit comments

Comments
 (0)