diff --git a/Cargo.toml b/Cargo.toml index 0b0f2e96..60a85000 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" } \ No newline at end of file +[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" } \ No newline at end of file diff --git a/crates/evm/src/eth/mod.rs b/crates/evm/src/eth/mod.rs index b238e69d..c51d8970 100644 --- a/crates/evm/src/eth/mod.rs +++ b/crates/evm/src/eth/mod.rs @@ -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:?}" ); @@ -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:?}" ); } diff --git a/crates/evm/src/precompiles.rs b/crates/evm/src/precompiles.rs index 73780b61..9557b60a 100644 --- a/crates/evm/src/precompiles.rs +++ b/crates/evm/src/precompiles.rs @@ -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, }; @@ -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); } } @@ -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 { @@ -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. @@ -146,7 +146,7 @@ impl PrecompilesMap { F: FnOnce(Option) -> Option, { 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); @@ -154,12 +154,12 @@ impl PrecompilesMap { 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); } } @@ -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); } @@ -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 { + pub fn get_stateless(&self, address: &Address) -> Option { // 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) } }; @@ -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 for PrecompilesMap { @@ -365,13 +378,14 @@ where context: &mut Context, Chain>, address: &Address, inputs: &InputsImpl, - _is_static: bool, + is_static: bool, gas_limit: u64, ) -> Result, 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, @@ -379,35 +393,41 @@ where 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); @@ -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) } } @@ -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, + /// Stateless Precompiles + stateless: HashMap, + /// Stateful Precompiles + stateful: HashSet
, /// Addresses of precompile addresses: HashSet
, } @@ -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 { - self.inner.into_iter() + self.stateless.into_iter() } } @@ -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"), }; @@ -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"), }; @@ -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 @@ -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] @@ -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 @@ -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"