Skip to content

Commit 4b975de

Browse files
committed
Merge remote-tracking branch 'origin/main' into rakita/state-gas
2 parents 7e01885 + 99c74f2 commit 4b975de

14 files changed

Lines changed: 147 additions & 60 deletions

File tree

bins/revme/src/cmd/blockchaintest.rs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,7 @@ fn execute_blockchain_test(
657657
| ForkSpec::CancunToPragueAtTime15k
658658
| ForkSpec::PragueToOsakaAtTime15k
659659
| ForkSpec::BPO1ToBPO2AtTime15k
660+
| ForkSpec::BPO2ToAmsterdamAtTime15k
660661
) {
661662
eprintln!("⚠️ Skipping transition fork: {:?}", test_case.network);
662663
return Ok(());
@@ -760,8 +761,11 @@ fn execute_blockchain_test(
760761
error: format!("{e:?}"),
761762
})?;
762763

763-
// Track cumulative gas used across all transactions in this block
764-
let mut cumulative_gas_used: u64 = 0;
764+
// Track cumulative gas used across all transactions in this block.
765+
// EIP-8037: Split gas accounting into regular (execution) and state gas.
766+
let mut cumulative_tx_gas_used: u64 = 0;
767+
let mut block_regular_gas_used: u64 = 0;
768+
let mut block_state_gas_used: u64 = 0;
765769
let mut block_completed = true;
766770

767771
// Execute each transaction in the block
@@ -896,15 +900,11 @@ fn execute_blockchain_test(
896900
block_completed = false;
897901
break; // Skip to next block
898902
}
899-
// EIP-7778: Block gas accounting without refunds.
900-
// For Amsterdam+, block gas = max(spent, floor_gas).
901-
// For pre-Amsterdam, block gas = used() = max(spent - refunded, floor_gas).
903+
// EIP-8037: Split gas accounting.
902904
let gas = result.result.gas();
903-
cumulative_gas_used += if spec_id.is_enabled_in(SpecId::AMSTERDAM) {
904-
gas.total_gas_spent().max(gas.floor_gas())
905-
} else {
906-
gas.tx_gas_used()
907-
};
905+
cumulative_tx_gas_used += gas.tx_gas_used();
906+
block_regular_gas_used += gas.block_regular_gas_used();
907+
block_state_gas_used += gas.block_state_gas_used();
908908
evm.commit(result.state);
909909
}
910910
Err(e) => {
@@ -962,20 +962,27 @@ fn execute_blockchain_test(
962962
}
963963
}
964964

965-
// Validate block gas used against header
965+
// Validate block gas used against header.
966+
// EIP-8037 (Amsterdam+): block gas_used = max(regular_gas, state_gas).
967+
// Pre-Amsterdam: block gas_used = cumulative tx_gas_used (includes refunds).
966968
if block_completed && !should_fail {
967969
if let Some(block_header) = block.block_header.as_ref() {
968970
let expected_gas_used = block_header.gas_used.to::<u64>();
969-
if cumulative_gas_used != expected_gas_used {
971+
let actual_block_gas_used = if spec_id.is_enabled_in(SpecId::AMSTERDAM) {
972+
block_regular_gas_used.max(block_state_gas_used)
973+
} else {
974+
cumulative_tx_gas_used
975+
};
976+
if actual_block_gas_used != expected_gas_used {
970977
if print_env_on_error {
971978
eprintln!(
972-
"Block gas used mismatch at block {block_idx}: expected {expected_gas_used}, got {cumulative_gas_used}"
979+
"Block gas used mismatch at block {block_idx}: expected {expected_gas_used}, got {actual_block_gas_used} (regular: {block_regular_gas_used}, state: {block_state_gas_used}, tx: {cumulative_tx_gas_used})"
973980
);
974981
}
975982
return Err(TestExecutionError::BlockGasUsedMismatch {
976983
block_idx,
977984
expected: expected_gas_used,
978-
actual: cumulative_gas_used,
985+
actual: actual_block_gas_used,
979986
});
980987
}
981988
}

crates/context/interface/src/local.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,28 +18,28 @@ impl<T> Default for FrameStack<T> {
1818
}
1919
}
2020

21-
impl<T: Default> FrameStack<T> {
22-
/// Creates a new stack with preallocated items by calling `T::default()` `len` times.
23-
/// Index will still be `None` until `end_init` is called.
24-
pub fn new_prealloc(len: usize) -> Self {
25-
let mut stack = Vec::with_capacity(len);
26-
for _ in 0..len {
27-
stack.push(T::default());
28-
}
29-
Self { stack, index: None }
30-
}
31-
}
32-
3321
impl<T> FrameStack<T> {
3422
/// Creates a new, empty stack. It must be initialized with init before use.
3523
pub fn new() -> Self {
36-
// Init N amount of frames to allocate the stack.
24+
// p99.9 of call frame depth is 8,
25+
// per: https://ethresear.ch/t/evm-stack-and-memory-usage-statistics-report/24209
3726
Self {
3827
stack: Vec::with_capacity(8),
3928
index: None,
4029
}
4130
}
4231

32+
/// Creates a new stack with preallocated items by calling `T::default()` `len` times.
33+
/// Index will still be `None` until `end_init` is called.
34+
pub fn new_prealloc(len: usize) -> Self
35+
where
36+
T: Default,
37+
{
38+
let mut stack = Vec::with_capacity(len);
39+
stack.resize_with(len, T::default);
40+
Self { stack, index: None }
41+
}
42+
4343
/// Initializes the stack with a single item.
4444
#[inline]
4545
pub fn start_init(&mut self) -> OutFrame<'_, T> {

crates/handler/src/handler.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ pub trait Handler {
133133
.and_then(|exec_result| {
134134
// System calls have no intrinsic gas; build ResultGas from frame result.
135135
let gas = exec_result.gas();
136-
let result_gas = build_result_gas(&gas, init_and_floor_gas);
136+
let result_gas = build_result_gas(gas, init_and_floor_gas);
137137
self.execution_result(evm, exec_result, result_gas)
138138
}) {
139139
out @ Ok(_) => out,

crates/inspector/src/handler.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,14 @@ where
173173
/// Handles the start of a frame by calling the appropriate inspector method.
174174
pub fn frame_start<CTX, INTR: InterpreterTypes>(
175175
context: &mut CTX,
176-
inspector: &mut impl Inspector<CTX, INTR>,
176+
inspector: &mut impl Inspector<CTX, INTR, FrameInput, FrameResult>,
177177
frame_input: &mut FrameInput,
178178
) -> Option<FrameResult> {
179+
// Generic hook before variant dispatch
180+
if let Some(result) = inspector.frame_start(context, frame_input) {
181+
return Some(result);
182+
}
183+
// Variant-specific dispatch
179184
match frame_input {
180185
FrameInput::Call(i) => {
181186
if let Some(output) = inspector.call(context, i) {
@@ -195,10 +200,11 @@ pub fn frame_start<CTX, INTR: InterpreterTypes>(
195200
/// Handles the end of a frame by calling the appropriate inspector method.
196201
pub fn frame_end<CTX, INTR: InterpreterTypes>(
197202
context: &mut CTX,
198-
inspector: &mut impl Inspector<CTX, INTR>,
203+
inspector: &mut impl Inspector<CTX, INTR, FrameInput, FrameResult>,
199204
frame_input: &FrameInput,
200205
frame_output: &mut FrameResult,
201206
) {
207+
// Variant-specific dispatch first
202208
match frame_output {
203209
FrameResult::Call(outcome) => {
204210
let FrameInput::Call(i) = frame_input else {
@@ -213,6 +219,8 @@ pub fn frame_end<CTX, INTR: InterpreterTypes>(
213219
inspector.create_end(context, i, outcome);
214220
}
215221
}
222+
// Generic hook after variant dispatch
223+
inspector.frame_end(context, frame_input, frame_output);
216224
}
217225

218226
/// Run Interpreter loop with inspection support.

crates/inspector/src/inspector.rs

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use auto_impl::auto_impl;
22
use context::{Database, Journal, JournalEntry};
3+
use handler::FrameResult;
34
use interpreter::{
4-
interpreter::EthInterpreter, CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter,
5-
InterpreterTypes,
5+
interpreter::EthInterpreter, CallInputs, CallOutcome, CreateInputs, CreateOutcome, FrameInput,
6+
Interpreter, InterpreterTypes,
67
};
78
use primitives::{Address, Log, U256};
89
use state::EvmState;
@@ -14,7 +15,8 @@ use state::EvmState;
1415
/// Object that is implemented this trait is used in `InspectorHandler` to trace the EVM execution.
1516
/// And API that allow calling the inspector can be found in [`crate::InspectEvm`] and [`crate::InspectCommitEvm`].
1617
#[auto_impl(&mut, Box)]
17-
pub trait Inspector<CTX, INTR: InterpreterTypes = EthInterpreter> {
18+
pub trait Inspector<CTX, INTR: InterpreterTypes = EthInterpreter, FI = FrameInput, FR = FrameResult>
19+
{
1820
/// Called before the interpreter is initialized.
1921
///
2022
/// If `interp.bytecode.set_action` is set the execution of the interpreter is skipped.
@@ -64,6 +66,29 @@ pub trait Inspector<CTX, INTR: InterpreterTypes = EthInterpreter> {
6466
self.log(context, log);
6567
}
6668

69+
/// Called before call/create dispatch. Called with a mutable reference to
70+
/// the frame input, allowing mutations of the input before the variant-specific
71+
/// hooks (`call`/`create`) are called.
72+
///
73+
/// Returning `Some(FrameResult)` will skip execution of the frame entirely,
74+
/// and also skips calling `call()`/`create()`. `frame_end` will still be called.
75+
#[inline]
76+
fn frame_start(&mut self, context: &mut CTX, frame_input: &mut FI) -> Option<FR> {
77+
let _ = context;
78+
let _ = frame_input;
79+
None
80+
}
81+
82+
/// Called after `call_end()`/`create_end()` variant-specific hooks complete.
83+
///
84+
/// Allows transformation of the final result regardless of frame kind.
85+
#[inline]
86+
fn frame_end(&mut self, context: &mut CTX, frame_input: &FI, frame_result: &mut FR) {
87+
let _ = context;
88+
let _ = frame_input;
89+
let _ = frame_result;
90+
}
91+
6792
/// Called whenever a call to a contract is about to start.
6893
///
6994
/// Returning `CallOutcome` will override the result of the call.
@@ -122,10 +147,10 @@ pub trait Inspector<CTX, INTR: InterpreterTypes = EthInterpreter> {
122147
}
123148
}
124149

125-
impl<CTX, INTR: InterpreterTypes, L, R> Inspector<CTX, INTR> for (L, R)
150+
impl<CTX, INTR: InterpreterTypes, FI, FR, L, R> Inspector<CTX, INTR, FI, FR> for (L, R)
126151
where
127-
L: Inspector<CTX, INTR>,
128-
R: Inspector<CTX, INTR>,
152+
L: Inspector<CTX, INTR, FI, FR>,
153+
R: Inspector<CTX, INTR, FI, FR>,
129154
{
130155
fn initialize_interp(&mut self, interp: &mut Interpreter<INTR>, context: &mut CTX) {
131156
self.0.initialize_interp(interp, context);
@@ -152,6 +177,17 @@ where
152177
self.1.log_full(interp, context, log);
153178
}
154179

180+
fn frame_start(&mut self, context: &mut CTX, frame_input: &mut FI) -> Option<FR> {
181+
let first = self.0.frame_start(context, frame_input);
182+
let second = self.1.frame_start(context, frame_input);
183+
first.or(second)
184+
}
185+
186+
fn frame_end(&mut self, context: &mut CTX, frame_input: &FI, frame_result: &mut FR) {
187+
self.0.frame_end(context, frame_input, frame_result);
188+
self.1.frame_end(context, frame_input, frame_result);
189+
}
190+
155191
fn call(&mut self, context: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> {
156192
let first = self.0.call(context, inputs);
157193
let second = self.1.call(context, inputs);

crates/inspector/src/traits.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use handler::{
55
EthFrame, EvmTr, FrameInitOrResult, FrameResult, ItemOrResult,
66
};
77
use interpreter::{
8-
interpreter::EthInterpreter, interpreter_action::FrameInit, CallOutcome, InterpreterTypes,
8+
interpreter::EthInterpreter, interpreter_action::FrameInit, CallOutcome, FrameInput,
9+
InterpreterTypes,
910
};
1011

1112
use crate::{
@@ -26,7 +27,7 @@ pub trait InspectorEvmTr:
2627
>
2728
{
2829
/// The inspector type used for EVM execution inspection.
29-
type Inspector: Inspector<Self::Context, EthInterpreter>;
30+
type Inspector: Inspector<Self::Context, EthInterpreter, FrameInput, FrameResult>;
3031

3132
/// Returns a tuple of mutable references to the context, the inspector, the frame and the instructions.
3233
///
@@ -118,7 +119,7 @@ pub trait InspectorEvmTr:
118119
{
119120
if *was_precompile_called {
120121
let logs = ctx.journal_mut().logs()[logs_i..].to_vec();
121-
for log in logs.iter().chain(precompile_call_logs.iter()).cloned() {
122+
for log in logs.into_iter().chain(precompile_call_logs.iter().cloned()) {
122123
inspector.log(ctx, log);
123124
}
124125
}

crates/precompile/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ criterion.workspace = true
7474
rand = { workspace = true, features = ["std"] }
7575
ark-std = { workspace = true }
7676
rstest.workspace = true
77+
p256 = { workspace = true, features = ["ecdsa"] }
7778

7879
[features]
7980
default = ["std", "secp256k1", "blst", "c-kzg", "portable"]

crates/precompile/bench/blake2.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ pub fn add_benches(group: &mut BenchmarkGroup<'_, criterion::measurement::WallTi
107107
blake2::algo::compress(
108108
black_box(12),
109109
&mut h_copy,
110-
black_box(m),
111-
black_box(t),
110+
&black_box(m),
111+
&black_box(t),
112112
black_box(false),
113113
);
114114
});

crates/precompile/bench/main.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub mod ecrecover;
66
pub mod eip1962;
77
pub mod eip2537;
88
pub mod eip4844;
9+
pub mod secp256r1;
910

1011
use criterion::{criterion_group, criterion_main, Criterion};
1112

@@ -35,6 +36,9 @@ pub fn benchmark_crypto_precompiles(c: &mut Criterion) {
3536

3637
// Run Blake2 benchmarks
3738
blake2::add_benches(&mut group);
39+
40+
// Run secp256r1 benchmarks
41+
secp256r1::add_benches(&mut group);
3842
}
3943

4044
criterion_group! {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//! Benchmarks for the secp256r1 (P256) precompile
2+
use criterion::{measurement::Measurement, BenchmarkGroup};
3+
use p256::ecdsa::{signature::hazmat::PrehashSigner, SigningKey};
4+
use primitives::Bytes;
5+
use revm_precompile::secp256r1::p256_verify;
6+
7+
/// Add benches for the secp256r1 precompile
8+
pub fn add_benches<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
9+
// Generate a valid P256 signature using p256's own OsRng (rand_core 0.6)
10+
let signing_key = SigningKey::random(&mut p256::elliptic_curve::rand_core::OsRng);
11+
let verifying_key = signing_key.verifying_key();
12+
13+
let msg_hash = [0xabu8; 32];
14+
15+
let (signature, _) = signing_key.sign_prehash(&msg_hash).unwrap();
16+
let sig_bytes = signature.to_bytes();
17+
18+
let pk_encoded = verifying_key.to_encoded_point(false);
19+
// Uncompressed point is 0x04 || x || y, skip the 0x04 prefix
20+
let pk_bytes = &pk_encoded.as_bytes()[1..];
21+
22+
// Input layout: msg_hash (32) || r (32) || s (32) || pubkey_x (32) || pubkey_y (32)
23+
let mut input = Vec::with_capacity(160);
24+
input.extend_from_slice(&msg_hash);
25+
input.extend_from_slice(&sig_bytes);
26+
input.extend_from_slice(pk_bytes);
27+
28+
let input = Bytes::from(input);
29+
30+
group.bench_function("p256verify precompile", |b| {
31+
b.iter(|| p256_verify(&input, u64::MAX).unwrap())
32+
});
33+
}

0 commit comments

Comments
 (0)