Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ thiserror = { version = "2.0.0", default-features = false }
serde_json = "1"
test-case = "3"

#[patch.crates-io]
#revm = { git = "https://github.com/bluealloy/revm", rev = "11b16259" }
#op-revm = { git = "https://github.com/bluealloy/revm", rev = "11b16259" }
[patch.crates-io]
revm = { git = "https://github.com/0gfoundation/revm", rev = "71cbb675f562a6c1ac1fd4eda8618b8a921426b6" }
op-revm = { git = "https://github.com/0gfoundation/revm", rev = "71cbb675f562a6c1ac1fd4eda8618b8a921426b6", package = "op-revm" }

# revm = { path = "../revm/crates/revm" }
# op-revm = { path = "../revm/crates/op-revm" }
4 changes: 2 additions & 2 deletions crates/evm/src/eth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ mod tests {

// precompile should NOT be available in early spec
assert!(
early_evm.precompiles_mut().get(&precompile_addr).is_none(),
early_evm.precompiles_mut().get_stateless(&precompile_addr).is_none(),
"{name} precompile at {precompile_addr:?} should NOT be available for early spec {early_spec:?}"
);

Expand All @@ -330,7 +330,7 @@ mod tests {

// precompile should be available in later spec
assert!(
later_evm.precompiles_mut().get(&precompile_addr).is_some(),
later_evm.precompiles_mut().get_stateless(&precompile_addr).is_some(),
"{name} precompile at {precompile_addr:?} should be available for later spec {later_spec:?}"
);
}
Expand Down
114 changes: 68 additions & 46 deletions crates/evm/src/precompiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use revm::{
context::LocalContextTr,
handler::{EthPrecompiles, PrecompileProvider},
interpreter::{CallInput, Gas, InputsImpl, InstructionResult, InterpreterResult},
precompile::{PrecompileError, PrecompileFn, PrecompileId, PrecompileResult, Precompiles},
precompile::{stateful_precompiles::run_stateful_precompile, PrecompileError, PrecompileFn, PrecompileId, PrecompileResult, Precompiles},
Context, Journal,
};

Expand Down Expand Up @@ -46,13 +46,13 @@ impl PrecompilesMap {
{
let dyn_precompiles = self.ensure_dynamic_precompiles();

// get the current precompile at the address
if let Some(dyn_precompile) = dyn_precompiles.inner.remove(address) {
// get the current stateless precompile at the address
if let Some(dyn_precompile) = dyn_precompiles.stateless.remove(address) {
// apply the transformation function
let transformed = f(dyn_precompile);

// update the precompile at the address
dyn_precompiles.inner.insert(*address, transformed);
dyn_precompiles.stateless.insert(*address, transformed);
}
}

Expand Down Expand Up @@ -87,8 +87,8 @@ impl PrecompilesMap {
{
let dyn_precompiles = self.ensure_dynamic_precompiles();

// apply the transformation to each precompile
let entries = dyn_precompiles.inner.drain();
// apply the transformation to each stateless precompile
let entries = dyn_precompiles.stateless.drain();
let mut new_map =
HashMap::with_capacity_and_hasher(entries.size_hint().0, Default::default());
for (addr, precompile) in entries {
Expand All @@ -100,7 +100,7 @@ impl PrecompilesMap {
}
}

dyn_precompiles.inner = new_map;
dyn_precompiles.stateless = new_map;
}

/// Applies a transformation to the precompile at the given address.
Expand Down Expand Up @@ -146,20 +146,20 @@ impl PrecompilesMap {
F: FnOnce(Option<DynPrecompile>) -> Option<DynPrecompile>,
{
let dyn_precompiles = self.ensure_dynamic_precompiles();
let current = dyn_precompiles.inner.get(address).cloned();
let current = dyn_precompiles.stateless.get(address).cloned();

// apply the transformation function
let result = f(current);

match result {
Some(transformed) => {
// insert the transformed precompile
dyn_precompiles.inner.insert(*address, transformed);
dyn_precompiles.stateless.insert(*address, transformed);
dyn_precompiles.addresses.insert(*address);
}
None => {
// remove the precompile if the transformation returned None
dyn_precompiles.inner.remove(address);
dyn_precompiles.stateless.remove(address);
dyn_precompiles.addresses.remove(address);
}
}
Expand Down Expand Up @@ -280,11 +280,16 @@ impl PrecompilesMap {
Cow::Owned(owned) => owned,
};

for (&addr, pc) in static_precompiles.inner().iter() {
dynamic.inner.insert(addr, DynPrecompile::from(*pc.precompile()));
for (&addr, pc) in static_precompiles.stateless().iter() {
dynamic.stateless.insert(addr, DynPrecompile::from(*pc.precompile()));
dynamic.addresses.insert(addr);
}

for addr in static_precompiles.stateful().iter() {
dynamic.stateful.insert(*addr);
dynamic.addresses.insert(*addr);
}

self.precompiles = PrecompilesKind::Dynamic(dynamic);
}

Expand All @@ -308,12 +313,12 @@ impl PrecompilesMap {
///
/// This method first checks the static precompile map, and if not found,
/// falls back to the dynamic lookup function (if set).
pub fn get(&self, address: &Address) -> Option<impl Precompile + '_> {
pub fn get_stateless(&self, address: &Address) -> Option<impl Precompile + '_> {
// First check static precompiles
let static_result = match &self.precompiles {
PrecompilesKind::Builtin(precompiles) => precompiles.get(address).map(Either::Left),
PrecompilesKind::Builtin(precompiles) => precompiles.get_stateless(address).map(Either::Left),
PrecompilesKind::Dynamic(dyn_precompiles) => {
dyn_precompiles.inner.get(address).map(Either::Right)
dyn_precompiles.stateless.get(address).map(Either::Right)
}
};

Expand All @@ -326,6 +331,14 @@ impl PrecompilesMap {
let lookup = self.lookup.as_ref()?;
lookup.lookup(address).map(Either::Right)
}

/// Check if the given address is stateful precompile.
pub fn is_stateful(&self, address: &Address) -> bool {
match &self.precompiles {
PrecompilesKind::Builtin(precompiles) => precompiles.is_stateful(address),
PrecompilesKind::Dynamic(dyn_precompiles) => dyn_precompiles.stateful.contains(address),
}
}
}

impl From<EthPrecompiles> for PrecompilesMap {
Expand Down Expand Up @@ -365,49 +378,56 @@ where
context: &mut Context<BlockEnv, TxEnv, CfgEnv, DB, Journal<DB>, Chain>,
address: &Address,
inputs: &InputsImpl,
_is_static: bool,
is_static: bool,
gas_limit: u64,
) -> Result<Option<InterpreterResult>, String> {
// Get the precompile at the address
let Some(precompile) = self.get(address) else {
let maybe_stateless = self.get_stateless(address);
if maybe_stateless.is_none() && !self.is_stateful(address) {
return Ok(None);
};
}

let mut result = InterpreterResult {
result: InstructionResult::Return,
gas: Gas::new(gas_limit),
output: Bytes::new(),
};

let (local, journal) = (&context.local, &mut context.journaled_state);

// Execute the precompile
let r;
let input_bytes = match &inputs.input {
CallInput::SharedBuffer(range) => {
// `map_or` does not work here as we use `r` to extend lifetime of the slice
// and return it.
#[allow(clippy::option_if_let_else)]
if let Some(slice) = local.shared_memory_buffer_slice(range.clone()) {
r = slice;
&*r
if let Some(slice) = context.local.shared_memory_buffer_slice(range.clone()) {
slice.to_vec()
} else {
&[]
vec![]
}
}
CallInput::Bytes(bytes) => bytes.as_ref(),
CallInput::Bytes(bytes) => bytes.0.to_vec(),
};

let precompile_result = precompile.call(PrecompileInput {
data: input_bytes,
gas: gas_limit,
caller: inputs.caller_address,
value: inputs.call_value,
internals: EvmInternals::new(journal, &context.block),
let precompile_result = match maybe_stateless {
Some(precompile) => precompile.call(PrecompileInput {
data: &input_bytes,
gas: gas_limit,
caller: inputs.caller_address,
value: inputs.call_value,
internals: EvmInternals::new(&mut context.journaled_state, &context.block),
target_address: inputs.target_address,
bytecode_address: inputs.bytecode_address.expect("always set for precompile calls"),
});

}),
None => run_stateful_precompile(
*address,
&input_bytes,
gas_limit,
inputs.caller_address,
inputs.call_value,
is_static,
context,
),
};
match precompile_result {
Ok(output) => {
let underflow = result.gas.record_cost(output.gas_used);
Expand Down Expand Up @@ -437,7 +457,7 @@ where
}

fn contains(&self, address: &Address) -> bool {
self.get(address).is_some()
self.get_stateless(address).is_some() || self.is_stateful(address)
}
}

Expand Down Expand Up @@ -493,8 +513,10 @@ impl core::fmt::Debug for DynPrecompile {
/// unlike the static `Precompiles` struct from revm.
#[derive(Clone, Default)]
pub struct DynPrecompiles {
/// Precompiles
inner: HashMap<Address, DynPrecompile>,
/// Stateless Precompiles
stateless: HashMap<Address, DynPrecompile>,
/// Stateful Precompiles
stateful: HashSet<Address>,
/// Addresses of precompile
addresses: HashSet<Address>,
}
Expand All @@ -503,7 +525,7 @@ impl DynPrecompiles {
/// Consumes the type and returns an iterator over the addresses and the corresponding
/// precompile.
pub fn into_precompiles(self) -> impl Iterator<Item = (Address, DynPrecompile)> {
self.inner.into_iter()
self.stateless.into_iter()
}
}

Expand Down Expand Up @@ -793,7 +815,7 @@ mod tests {
// using the dynamic precompiles interface
let dyn_precompile = match &spec_precompiles.precompiles {
PrecompilesKind::Dynamic(dyn_precompiles) => {
dyn_precompiles.inner.get(&identity_address).unwrap()
dyn_precompiles.stateless.get(&identity_address).unwrap()
}
_ => panic!("Expected dynamic precompiles"),
};
Expand Down Expand Up @@ -827,7 +849,7 @@ mod tests {
// get the modified precompile and check it
let dyn_precompile = match &spec_precompiles.precompiles {
PrecompilesKind::Dynamic(dyn_precompiles) => {
dyn_precompiles.inner.get(&identity_address).unwrap()
dyn_precompiles.stateless.get(&identity_address).unwrap()
}
_ => panic!("Expected dynamic precompiles"),
};
Expand Down Expand Up @@ -931,11 +953,11 @@ mod tests {

// Test that static precompiles still work
let identity_address = address!("0x0000000000000000000000000000000000000004");
assert!(spec_precompiles.get(&identity_address).is_some());
assert!(spec_precompiles.get_stateless(&identity_address).is_some());

// Test dynamic lookup for matching address
let dynamic_address = address!("0xDEAD000000000000000000000000000000000001");
let dynamic_precompile = spec_precompiles.get(&dynamic_address);
let dynamic_precompile = spec_precompiles.get_stateless(&dynamic_address);
assert!(dynamic_precompile.is_some(), "Dynamic precompile should be found");

// Execute the dynamic precompile
Expand All @@ -956,7 +978,7 @@ mod tests {

// Test non-matching address returns None
let non_matching_address = address!("0x1234000000000000000000000000000000000001");
assert!(spec_precompiles.get(&non_matching_address).is_none());
assert!(spec_precompiles.get_stateless(&non_matching_address).is_none());
}

#[test]
Expand All @@ -970,7 +992,7 @@ mod tests {
let test_input = Bytes::from_static(b"test data");
let gas_limit = 1000;

let precompile = spec_precompiles.get(&identity_address);
let precompile = spec_precompiles.get_stateless(&identity_address);
assert!(precompile.is_some(), "Identity precompile should exist");

let result = precompile
Expand All @@ -989,14 +1011,14 @@ mod tests {

let nonexistent_address = address!("0x0000000000000000000000000000000000000099");
assert!(
spec_precompiles.get(&nonexistent_address).is_none(),
spec_precompiles.get_stateless(&nonexistent_address).is_none(),
"Non-existent precompile should not be found"
);

let mut dynamic_precompiles = spec_precompiles;
dynamic_precompiles.ensure_dynamic_precompiles();

let dyn_precompile = dynamic_precompiles.get(&identity_address);
let dyn_precompile = dynamic_precompiles.get_stateless(&identity_address);
assert!(
dyn_precompile.is_some(),
"Identity precompile should exist after conversion to dynamic"
Expand Down
Loading