From 83342181deee47dba55b885f78691d423fb7d7d5 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Mon, 22 Apr 2024 15:51:52 +0200 Subject: [PATCH 1/8] feat: add eip-2935 constants --- crates/primitives/src/constants.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/crates/primitives/src/constants.rs b/crates/primitives/src/constants.rs index 07745dd3cc..047a8ae3d5 100644 --- a/crates/primitives/src/constants.rs +++ b/crates/primitives/src/constants.rs @@ -1,12 +1,24 @@ +use alloy_primitives::address; + use crate::Address; /// EIP-170: Contract code size limit /// By default limit is 0x6000 (~25kb) pub const MAX_CODE_SIZE: usize = 0x6000; -/// Number of block hashes that EVM can access in the past +/// Number of block hashes that EVM can access in the past (pre-Prague). pub const BLOCK_HASH_HISTORY: usize = 256; +/// EIP-2935: Serve historical block hashes from state +/// +/// Number of block hashes the EVM can access in the past (Prague). +pub const HISTORY_SERVE_WINDOW: usize = 8192; + +/// EIP-2935: Serve historical block hashes from state +/// +/// The address where historical blockhashes are available. +pub const HISTORY_STORAGE_ADDRESS: Address = address!("25a219378dad9b3503c8268c9ca836a52427a4fb"); + /// EIP-3860: Limit and meter initcode /// /// Limit of maximum initcode size is 2 * MAX_CODE_SIZE From b95dbd339fe7b024a7bd10b78d04b358cda065a0 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Mon, 22 Apr 2024 15:52:08 +0200 Subject: [PATCH 2/8] feat: impl EIP-2935 `BLOCKHASH` --- crates/interpreter/src/instructions/host.rs | 22 +++++++++++++++++---- crates/interpreter/src/opcode.rs | 2 +- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index bad571de35..9374200806 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -5,7 +5,7 @@ use crate::{ Host, InstructionResult, SStoreResult, }; use core::cmp::min; -use revm_primitives::BLOCK_HASH_HISTORY; +use revm_primitives::{BLOCK_HASH_HISTORY, HISTORY_SERVE_WINDOW, HISTORY_STORAGE_ADDRESS}; use std::vec::Vec; pub fn balance(interpreter: &mut Interpreter, host: &mut H) { @@ -103,14 +103,27 @@ pub fn extcodecopy(interpreter: &mut Interpreter, .set_data(memory_offset, code_offset, len, &code.original_bytes()); } -pub fn blockhash(interpreter: &mut Interpreter, host: &mut H) { +pub fn blockhash(interpreter: &mut Interpreter, host: &mut H) { gas!(interpreter, gas::BLOCKHASH); pop_top!(interpreter, number); - if let Some(diff) = host.env().block.number.checked_sub(*number) { + let block_number = host.env().block.number; + + if let Some(diff) = block_number.checked_sub(*number) { let diff = as_usize_saturated!(diff); + // blockhash should push zero if number is same as current block number. - if diff <= BLOCK_HASH_HISTORY && diff != 0 { + if SPEC::enabled(PRAGUE) && diff <= HISTORY_SERVE_WINDOW && diff != 0 { + let Some((value, is_cold)) = host.sload( + HISTORY_STORAGE_ADDRESS, + number.wrapping_rem(U256::from(HISTORY_SERVE_WINDOW)), + ) else { + interpreter.instruction_result = InstructionResult::FatalExternalError; + return; + }; + gas!(interpreter, gas::sload_cost(SPEC::SPEC_ID, is_cold)); + *number = value; + } else if diff <= BLOCK_HASH_HISTORY && diff != 0 { let Some(hash) = host.block_hash(*number) else { interpreter.instruction_result = InstructionResult::FatalExternalError; return; @@ -119,6 +132,7 @@ pub fn blockhash(interpreter: &mut Interpreter, host: &mut H) return; } } + *number = U256::ZERO; } diff --git a/crates/interpreter/src/opcode.rs b/crates/interpreter/src/opcode.rs index 883ceed7ce..589aa5404f 100644 --- a/crates/interpreter/src/opcode.rs +++ b/crates/interpreter/src/opcode.rs @@ -443,7 +443,7 @@ opcodes! { 0x3D => RETURNDATASIZE => system::returndatasize:: => stack_io<0, 1>; 0x3E => RETURNDATACOPY => system::returndatacopy:: => stack_io<3, 0>; 0x3F => EXTCODEHASH => host::extcodehash:: => stack_io<1, 1>, not_eof; - 0x40 => BLOCKHASH => host::blockhash => stack_io<1, 1>; + 0x40 => BLOCKHASH => host::blockhash:: => stack_io<1, 1>; 0x41 => COINBASE => host_env::coinbase => stack_io<0, 1>; 0x42 => TIMESTAMP => host_env::timestamp => stack_io<0, 1>; 0x43 => NUMBER => host_env::block_number => stack_io<0, 1>; From 214706d947fead19aaef94c4dc5d77bed4169b9d Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Mon, 22 Apr 2024 16:12:50 +0200 Subject: [PATCH 3/8] fix: early return in prague --- crates/interpreter/src/instructions/host.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index 9374200806..f239f4bb4b 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -123,6 +123,7 @@ pub fn blockhash(interpreter: &mut Interpreter, ho }; gas!(interpreter, gas::sload_cost(SPEC::SPEC_ID, is_cold)); *number = value; + return; } else if diff <= BLOCK_HASH_HISTORY && diff != 0 { let Some(hash) = host.block_hash(*number) else { interpreter.instruction_result = InstructionResult::FatalExternalError; From 379d951127c7b32c553088ffc7b9276a04283aba Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Wed, 24 Apr 2024 13:30:53 +0200 Subject: [PATCH 4/8] chore: fmt --- crates/primitives/src/constants.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/crates/primitives/src/constants.rs b/crates/primitives/src/constants.rs index 047a8ae3d5..cd2fd83e64 100644 --- a/crates/primitives/src/constants.rs +++ b/crates/primitives/src/constants.rs @@ -1,9 +1,8 @@ -use alloy_primitives::address; - -use crate::Address; +use alloy_primitives::{address, Address}; /// EIP-170: Contract code size limit -/// By default limit is 0x6000 (~25kb) +/// +/// By default the limit is `0x6000` (~25kb) pub const MAX_CODE_SIZE: usize = 0x6000; /// Number of block hashes that EVM can access in the past (pre-Prague). @@ -21,26 +20,35 @@ pub const HISTORY_STORAGE_ADDRESS: Address = address!("25a219378dad9b3503c8268c9 /// EIP-3860: Limit and meter initcode /// -/// Limit of maximum initcode size is 2 * MAX_CODE_SIZE +/// Limit of maximum initcode size is `2 * MAX_CODE_SIZE`. pub const MAX_INITCODE_SIZE: usize = 2 * MAX_CODE_SIZE; -/// Precompile 3 is special in few places +/// The address of precompile 3, which is handled specially in a few places. pub const PRECOMPILE3: Address = Address::new([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3]); -// EIP-4844 constants + +// === EIP-4844 constants === + /// Gas consumption of a single data blob (== blob byte size). pub const GAS_PER_BLOB: u64 = 1 << 17; + /// Target number of the blob per block. pub const TARGET_BLOB_NUMBER_PER_BLOCK: u64 = 3; + /// Max number of blobs per block pub const MAX_BLOB_NUMBER_PER_BLOCK: u64 = 2 * TARGET_BLOB_NUMBER_PER_BLOCK; + /// Maximum consumable blob gas for data blobs per block. pub const MAX_BLOB_GAS_PER_BLOCK: u64 = MAX_BLOB_NUMBER_PER_BLOCK * GAS_PER_BLOB; + /// Target consumable blob gas for data blobs per block (for 1559-like pricing). pub const TARGET_BLOB_GAS_PER_BLOCK: u64 = TARGET_BLOB_NUMBER_PER_BLOCK * GAS_PER_BLOB; + /// Minimum gas price for data blobs. pub const MIN_BLOB_GASPRICE: u64 = 1; + /// Controls the maximum rate of change for blob gas price. pub const BLOB_GASPRICE_UPDATE_FRACTION: u64 = 3338477; + /// First version of the blob. pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01; From 74cea763ec57ca3e0dbfc7773bcea1ffcef8c957 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Wed, 24 Apr 2024 13:52:15 +0200 Subject: [PATCH 5/8] refactor: `sload!` macro --- crates/interpreter/src/instructions/host.rs | 57 ++++++++++--------- crates/interpreter/src/instructions/macros.rs | 24 ++++++++ 2 files changed, 55 insertions(+), 26 deletions(-) diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index f239f4bb4b..9ccde49b2a 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -5,7 +5,7 @@ use crate::{ Host, InstructionResult, SStoreResult, }; use core::cmp::min; -use revm_primitives::{BLOCK_HASH_HISTORY, HISTORY_SERVE_WINDOW, HISTORY_STORAGE_ADDRESS}; +use revm_primitives::{Address, BLOCK_HASH_HISTORY, HISTORY_SERVE_WINDOW, HISTORY_STORAGE_ADDRESS}; use std::vec::Vec; pub fn balance(interpreter: &mut Interpreter, host: &mut H) { @@ -109,28 +109,32 @@ pub fn blockhash(interpreter: &mut Interpreter, ho let block_number = host.env().block.number; - if let Some(diff) = block_number.checked_sub(*number) { - let diff = as_usize_saturated!(diff); - - // blockhash should push zero if number is same as current block number. - if SPEC::enabled(PRAGUE) && diff <= HISTORY_SERVE_WINDOW && diff != 0 { - let Some((value, is_cold)) = host.sload( - HISTORY_STORAGE_ADDRESS, - number.wrapping_rem(U256::from(HISTORY_SERVE_WINDOW)), - ) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; + match block_number.checked_sub(*number) { + Some(diff) if !diff.is_zero() => { + let diff = as_usize_saturated!(diff); + + // blockhash should push zero if number is same as current block number. + if SPEC::enabled(PRAGUE) && diff <= HISTORY_SERVE_WINDOW { + let value = sload!( + interpreter, + host, + HISTORY_STORAGE_ADDRESS, + number.wrapping_rem(U256::from(HISTORY_SERVE_WINDOW)) + ); + *number = value; return; - }; - gas!(interpreter, gas::sload_cost(SPEC::SPEC_ID, is_cold)); - *number = value; - return; - } else if diff <= BLOCK_HASH_HISTORY && diff != 0 { - let Some(hash) = host.block_hash(*number) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; + } else if diff <= BLOCK_HASH_HISTORY { + let Some(hash) = host.block_hash(*number) else { + interpreter.instruction_result = InstructionResult::FatalExternalError; + return; + }; + *number = U256::from_be_bytes(hash.0); return; - }; - *number = U256::from_be_bytes(hash.0); - return; + } + } + _ => { + // If blockhash is requested for the current block, the hash should be 0, so we fall + // through. } } @@ -139,11 +143,12 @@ pub fn blockhash(interpreter: &mut Interpreter, ho pub fn sload(interpreter: &mut Interpreter, host: &mut H) { pop_top!(interpreter, index); - let Some((value, is_cold)) = host.sload(interpreter.contract.target_address, *index) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - gas!(interpreter, gas::sload_cost(SPEC::SPEC_ID, is_cold)); + let value = sload!( + interpreter, + host, + interpreter.contract.target_address, + *index + ); *index = value; } diff --git a/crates/interpreter/src/instructions/macros.rs b/crates/interpreter/src/instructions/macros.rs index 214a6c79b1..c9fbe04a2f 100644 --- a/crates/interpreter/src/instructions/macros.rs +++ b/crates/interpreter/src/instructions/macros.rs @@ -45,6 +45,30 @@ macro_rules! check { }; } +/// Performs an `SLOAD` on the target account and storage index. +/// +/// If the slot could not be loaded, or if the gas cost could not be charged, the expanded code +/// sets the instruction result and returns accordingly. +/// +/// # Note +/// +/// This macro charges gas. +/// +/// # Returns +/// +/// Expands to the value of the storage slot. +#[macro_export] +macro_rules! sload { + ($interp:expr, $host:expr, $address:expr, $index:expr) => {{ + let Some((value, is_cold)) = $host.sload($address, $index) else { + $interp.instruction_result = $crate::InstructionResult::FatalExternalError; + return; + }; + $crate::gas!($interp, $crate::gas::sload_cost(SPEC::SPEC_ID, is_cold)); + value + }}; +} + /// Records a `gas` cost and fails the instruction if it would exceed the available gas. #[macro_export] macro_rules! gas { From 7758c8a789acf7e696803303062d55abf51d605f Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Wed, 24 Apr 2024 13:53:15 +0200 Subject: [PATCH 6/8] chore: rm unused import --- crates/interpreter/src/instructions/host.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index 9ccde49b2a..4a4d200f1f 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -5,7 +5,7 @@ use crate::{ Host, InstructionResult, SStoreResult, }; use core::cmp::min; -use revm_primitives::{Address, BLOCK_HASH_HISTORY, HISTORY_SERVE_WINDOW, HISTORY_STORAGE_ADDRESS}; +use revm_primitives::{BLOCK_HASH_HISTORY, HISTORY_SERVE_WINDOW, HISTORY_STORAGE_ADDRESS}; use std::vec::Vec; pub fn balance(interpreter: &mut Interpreter, host: &mut H) { From 37ac22d6887550220e84adbb0e38ea5b2b868b4c Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Thu, 2 May 2024 00:49:15 +0200 Subject: [PATCH 7/8] chore: rename consts --- crates/interpreter/src/instructions/host.rs | 8 ++++---- crates/primitives/src/constants.rs | 12 ++++++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index 4a4d200f1f..43cba873b0 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -5,7 +5,7 @@ use crate::{ Host, InstructionResult, SStoreResult, }; use core::cmp::min; -use revm_primitives::{BLOCK_HASH_HISTORY, HISTORY_SERVE_WINDOW, HISTORY_STORAGE_ADDRESS}; +use revm_primitives::{BLOCKHASH_SERVE_WINDOW, BLOCKHASH_STORAGE_ADDRESS, BLOCK_HASH_HISTORY}; use std::vec::Vec; pub fn balance(interpreter: &mut Interpreter, host: &mut H) { @@ -114,12 +114,12 @@ pub fn blockhash(interpreter: &mut Interpreter, ho let diff = as_usize_saturated!(diff); // blockhash should push zero if number is same as current block number. - if SPEC::enabled(PRAGUE) && diff <= HISTORY_SERVE_WINDOW { + if SPEC::enabled(PRAGUE) && diff <= BLOCKHASH_SERVE_WINDOW { let value = sload!( interpreter, host, - HISTORY_STORAGE_ADDRESS, - number.wrapping_rem(U256::from(HISTORY_SERVE_WINDOW)) + BLOCKHASH_STORAGE_ADDRESS, + number.wrapping_rem(U256::from(BLOCKHASH_SERVE_WINDOW)) ); *number = value; return; diff --git a/crates/primitives/src/constants.rs b/crates/primitives/src/constants.rs index cd2fd83e64..c8223300bf 100644 --- a/crates/primitives/src/constants.rs +++ b/crates/primitives/src/constants.rs @@ -11,12 +11,20 @@ pub const BLOCK_HASH_HISTORY: usize = 256; /// EIP-2935: Serve historical block hashes from state /// /// Number of block hashes the EVM can access in the past (Prague). -pub const HISTORY_SERVE_WINDOW: usize = 8192; +/// +/// # Note +/// +/// This is named `HISTORY_SERVE_WINDOW` in the EIP. +pub const BLOCKHASH_SERVE_WINDOW: usize = 8192; /// EIP-2935: Serve historical block hashes from state /// /// The address where historical blockhashes are available. -pub const HISTORY_STORAGE_ADDRESS: Address = address!("25a219378dad9b3503c8268c9ca836a52427a4fb"); +/// +/// # Note +/// +/// This is named `HISTORY_STORAGE_ADDRESS` in the EIP. +pub const BLOCKHASH_STORAGE_ADDRESS: Address = address!("25a219378dad9b3503c8268c9ca836a52427a4fb"); /// EIP-3860: Limit and meter initcode /// From 59eed4d9b0c1db0a4f66915002639115077a7f37 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Thu, 2 May 2024 00:52:03 +0200 Subject: [PATCH 8/8] fix: typo from merge --- crates/interpreter/src/opcode.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/interpreter/src/opcode.rs b/crates/interpreter/src/opcode.rs index 0d790ffb1e..5114c2ae19 100644 --- a/crates/interpreter/src/opcode.rs +++ b/crates/interpreter/src/opcode.rs @@ -582,7 +582,7 @@ opcodes! { 0x3D => RETURNDATASIZE => system::returndatasize:: => stack_io(0, 1); 0x3E => RETURNDATACOPY => system::returndatacopy:: => stack_io(3, 0); 0x3F => EXTCODEHASH => host::extcodehash:: => stack_io(1, 1), not_eof; - 0x40 => BLOCKHASH => host::blockhash => stack_io(1, 1); + 0x40 => BLOCKHASH => host::blockhash:: => stack_io(1, 1); 0x41 => COINBASE => host_env::coinbase => stack_io(0, 1); 0x42 => TIMESTAMP => host_env::timestamp => stack_io(0, 1); 0x43 => NUMBER => host_env::block_number => stack_io(0, 1);