Skip to content

Commit

Permalink
feat: implement EIP-2935 (bluealloy#1354)
Browse files Browse the repository at this point in the history
* feat: add eip-2935 constants

* feat: impl EIP-2935 `BLOCKHASH`

* fix: early return in prague

* chore: fmt

* refactor: `sload!` macro

* chore: rm unused import

* chore: rename consts

* fix: typo from merge
  • Loading branch information
onbjerg authored May 2, 2024
1 parent aceb093 commit 3e089f3
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 23 deletions.
52 changes: 36 additions & 16 deletions crates/interpreter/src/instructions/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
Host, InstructionResult, SStoreResult,
};
use core::cmp::min;
use revm_primitives::BLOCK_HASH_HISTORY;
use revm_primitives::{BLOCKHASH_SERVE_WINDOW, BLOCKHASH_STORAGE_ADDRESS, BLOCK_HASH_HISTORY};
use std::vec::Vec;

pub fn balance<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter, host: &mut H) {
Expand Down Expand Up @@ -103,32 +103,52 @@ pub fn extcodecopy<H: Host + ?Sized, SPEC: Spec>(interpreter: &mut Interpreter,
.set_data(memory_offset, code_offset, len, &code.original_bytes());
}

pub fn blockhash<H: Host + ?Sized>(interpreter: &mut Interpreter, host: &mut H) {
pub fn blockhash<H: Host + ?Sized, SPEC: Spec>(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 diff = as_usize_saturated!(diff);
// blockhash should push zero if number is same as current block number.
if diff <= BLOCK_HASH_HISTORY && diff != 0 {
let Some(hash) = host.block_hash(*number) else {
interpreter.instruction_result = InstructionResult::FatalExternalError;
let block_number = host.env().block.number;

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 <= BLOCKHASH_SERVE_WINDOW {
let value = sload!(
interpreter,
host,
BLOCKHASH_STORAGE_ADDRESS,
number.wrapping_rem(U256::from(BLOCKHASH_SERVE_WINDOW))
);
*number = value;
return;
};
*number = U256::from_be_bytes(hash.0);
return;
} 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;
}
}
_ => {
// If blockhash is requested for the current block, the hash should be 0, so we fall
// through.
}
}

*number = U256::ZERO;
}

pub fn sload<H: Host + ?Sized, SPEC: Spec>(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;
}

Expand Down
24 changes: 24 additions & 0 deletions crates/interpreter/src/instructions/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion crates/interpreter/src/opcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@ opcodes! {
0x3D => RETURNDATASIZE => system::returndatasize::<H, SPEC> => stack_io(0, 1);
0x3E => RETURNDATACOPY => system::returndatacopy::<H, SPEC> => stack_io(3, 0);
0x3F => EXTCODEHASH => host::extcodehash::<H, SPEC> => stack_io(1, 1), not_eof;
0x40 => BLOCKHASH => host::blockhash => stack_io(1, 1);
0x40 => BLOCKHASH => host::blockhash::<H, SPEC> => 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);
Expand Down
40 changes: 34 additions & 6 deletions crates/primitives/src/constants.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,62 @@
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
/// 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).
///
/// # 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.
///
/// # Note
///
/// This is named `HISTORY_STORAGE_ADDRESS` in the EIP.
pub const BLOCKHASH_STORAGE_ADDRESS: Address = address!("25a219378dad9b3503c8268c9ca836a52427a4fb");

/// 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;

0 comments on commit 3e089f3

Please sign in to comment.