Skip to content

Commit 8094c45

Browse files
authored
feat(eip7702): Impl newest version of EIP (bluealloy#1695)
* latest eip7702 wip * add code loading handler * WIP adding is_delegate_cold flag * feat: add StateLoad and Eip7702CodeLoad * feat: add gas accounting among other things * clippy,fmt, op test * path to latest alloy-eips * comment eip7702 decode tests * Eip7702 format starts with 0xEF0100 * typo * fix(eip7702): fix empty or eip7702 code check * Type Eip7702s to Eip7702 * Corrent comments * switch new and new_raw Eip7702Bytecode * propagate last commit * nit: rename fn * fix(eip7702): set delegated code on call (bluealloy#1706) * type change, return eip7702 raw on Bytecode::bytecode * eip7702 delegation test * Cleanup, refactor sstore gas calc * doc * chore: add AuthList json format * fix initial eip7702 gas, fix eip7702 refund on revert * small refactor * fix refund cnt * error handling, EIP-3607 fix, wip on auth validity * add auth validity check, fix EIP-3607 fix * switch tests * missing comment * fix tests * rm println * remove skip of required fields * docs, test meta dat
1 parent 0ae01d5 commit 8094c45

File tree

127 files changed

+448801
-3837
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

127 files changed

+448801
-3837
lines changed

Cargo.lock

Lines changed: 18 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,6 @@ debug = true
2222
[profile.ethtests]
2323
inherits = "test"
2424
opt-level = 3
25+
26+
[patch.crates-io]
27+
alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "fd159f6" }

bins/revme/src/cmd/evmrunner.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use revm::{
22
db::BenchmarkDB,
33
inspector_handle_register,
44
inspectors::TracerEip3155,
5-
primitives::{eof::EofDecodeError, Address, Bytecode, TxKind},
5+
primitives::{Address, Bytecode, BytecodeDecodeError, TxKind},
66
Evm,
77
};
88
use std::io::Error as IoError;
@@ -26,7 +26,7 @@ pub enum Errors {
2626
#[error(transparent)]
2727
Io(#[from] IoError),
2828
#[error(transparent)]
29-
EofError(#[from] EofDecodeError),
29+
BytecodeDecodeError(#[from] BytecodeDecodeError),
3030
}
3131

3232
/// Evm runner command allows running arbitrary evm bytecode.

bins/revme/src/cmd/statetest/models/eip7702.rs

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use alloy_rlp::{Decodable, Error as RlpError, Header};
22
use revm::primitives::{AccessList, Bytes, Signature, SignedAuthorization, TxKind, U256};
33
use std::vec::Vec;
44

5+
/// TODO remove it when new tests are generated that has a Authorization json field.
56
/// [EIP-7702 Set Code Transaction](https://eips.ethereum.org/EIPS/eip-7702)
67
///
78
/// Set EOA account code for one transaction
@@ -112,22 +113,3 @@ impl TxEip7702 {
112113
Ok(tx)
113114
}
114115
}
115-
116-
#[cfg(test)]
117-
mod tests {
118-
use super::*;
119-
120-
#[test]
121-
fn decode_eip7702_tx() {
122-
let tx_bytes = hex::decode("f8c2018080078398968094a94f5374fce5edbc8e2a8697c15331677e6ebf0b8080c0f85df85b01940000000000000000000000000000000000001000c18001a08171c0ded912d4f458b8115618c18f3f430f414919c73b4daa693c47fd325414a0787741e1621bcb9cb58ece039ad73f41d9422aa259ed53c2b0bd30dc7ff09be780a00e6c8f4d73b175887e1f21cc00bf0f8243af18aed208ec0a4562ee60e7f85736a03f8e8f1b01fcd6d3a988877e80dc17fad16274447f4211ed74b41e8789ae70cd").unwrap();
123-
let tx = TxEip7702::decode(&mut tx_bytes.as_slice()).unwrap();
124-
assert_eq!(tx.authorization_list.len(), 1);
125-
}
126-
127-
#[test]
128-
fn test_eip7702_tx() {
129-
let tx_bytes = hex::decode("f8c2018080078398968094a94f5374fce5edbc8e2a8697c15331677e6ebf0b8080c0f85df85b80940000000000000000000000000000000000001000c10180a09e833a19cf7ac609d713ffeb8d5cd327237ef5cb4ac9524c53195423e348629fa0632893e4b18b32faf56972dc3568c3a3869dcf9eb9c282a637173475d19e8d2f01a05d6eea7691335a6bb066613d5c33a27bd1cbc89feb472b6dd437aca6aec73282a013c492943ea0fce77a20b1d554eac087fee37fa27b0f8294b13fb3162a0fb175").unwrap();
130-
let tx = TxEip7702::decode(&mut tx_bytes.as_slice()).unwrap();
131-
assert_eq!(tx.authorization_list.len(), 1);
132-
}
133-
}

bins/revme/src/cmd/statetest/models/mod.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,15 +127,25 @@ pub struct TransactionParts {
127127

128128
#[serde(default)]
129129
pub access_lists: Vec<Option<AccessList>>,
130-
131-
//#[serde(default)]
132-
// TODO EIP-7702 when added enable serde `deny_unknown_fields`.
133-
//pub authorization_list: Vec<Option<Vec<TestAuthorization>>>,
130+
#[serde(default)]
131+
pub authorization_list: Vec<Authorization>,
134132
#[serde(default)]
135133
pub blob_versioned_hashes: Vec<B256>,
136134
pub max_fee_per_blob_gas: Option<U256>,
137135
}
138136

137+
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
138+
#[serde(rename_all = "camelCase", deny_unknown_fields)]
139+
pub struct Authorization {
140+
chain_id: U256,
141+
address: Address,
142+
nonce: U256,
143+
v: U256,
144+
r: U256,
145+
s: U256,
146+
signer: Option<Address>,
147+
}
148+
139149
#[cfg(test)]
140150
mod tests {
141151

crates/interpreter/src/gas/calc.rs

Lines changed: 56 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
use revm_primitives::eip7702;
2+
13
use super::constants::*;
24
use crate::{
35
num_words,
46
primitives::{AccessListItem, SpecId, U256},
5-
SelfDestructResult,
7+
AccountLoad, Eip7702CodeLoad, SStoreResult, SelfDestructResult, StateLoad,
68
};
79

810
/// `const` Option `?`.
@@ -18,37 +20,37 @@ macro_rules! tri {
1820
/// `SSTORE` opcode refund calculation.
1921
#[allow(clippy::collapsible_else_if)]
2022
#[inline]
21-
pub fn sstore_refund(spec_id: SpecId, original: U256, current: U256, new: U256) -> i64 {
23+
pub fn sstore_refund(spec_id: SpecId, vals: &SStoreResult) -> i64 {
2224
if spec_id.is_enabled_in(SpecId::ISTANBUL) {
2325
// EIP-3529: Reduction in refunds
2426
let sstore_clears_schedule = if spec_id.is_enabled_in(SpecId::LONDON) {
2527
(SSTORE_RESET - COLD_SLOAD_COST + ACCESS_LIST_STORAGE_KEY) as i64
2628
} else {
2729
REFUND_SSTORE_CLEARS
2830
};
29-
if current == new {
31+
if vals.is_new_eq_present() {
3032
0
3133
} else {
32-
if original == current && new.is_zero() {
34+
if vals.is_original_eq_present() && vals.is_new_zero() {
3335
sstore_clears_schedule
3436
} else {
3537
let mut refund = 0;
3638

37-
if !original.is_zero() {
38-
if current.is_zero() {
39+
if !vals.is_original_zero() {
40+
if vals.is_present_zero() {
3941
refund -= sstore_clears_schedule;
40-
} else if new.is_zero() {
42+
} else if vals.is_new_zero() {
4143
refund += sstore_clears_schedule;
4244
}
4345
}
4446

45-
if original == new {
47+
if vals.is_original_eq_new() {
4648
let (gas_sstore_reset, gas_sload) = if spec_id.is_enabled_in(SpecId::BERLIN) {
4749
(SSTORE_RESET - COLD_SLOAD_COST, WARM_STORAGE_READ_COST)
4850
} else {
4951
(SSTORE_RESET, sload_cost(spec_id, false))
5052
};
51-
if original.is_zero() {
53+
if vals.is_original_zero() {
5254
refund += (SSTORE_SET - gas_sload) as i64;
5355
} else {
5456
refund += (gas_sstore_reset - gas_sload) as i64;
@@ -59,7 +61,7 @@ pub fn sstore_refund(spec_id: SpecId, original: U256, current: U256, new: U256)
5961
}
6062
}
6163
} else {
62-
if !current.is_zero() && new.is_zero() {
64+
if !vals.is_present_zero() && vals.is_new_zero() {
6365
REFUND_SSTORE_CLEARS
6466
} else {
6567
0
@@ -123,9 +125,9 @@ pub const fn verylowcopy_cost(len: u64) -> Option<u64> {
123125

124126
/// `EXTCODECOPY` opcode cost calculation.
125127
#[inline]
126-
pub const fn extcodecopy_cost(spec_id: SpecId, len: u64, is_cold: bool) -> Option<u64> {
128+
pub const fn extcodecopy_cost(spec_id: SpecId, len: u64, load: Eip7702CodeLoad<()>) -> Option<u64> {
127129
let base_gas = if spec_id.is_enabled_in(SpecId::BERLIN) {
128-
warm_cold_cost(is_cold)
130+
warm_cold_cost_with_delegation(load)
129131
} else if spec_id.is_enabled_in(SpecId::TANGERINE) {
130132
700
131133
} else {
@@ -187,24 +189,15 @@ pub const fn sload_cost(spec_id: SpecId, is_cold: bool) -> u64 {
187189

188190
/// `SSTORE` opcode cost calculation.
189191
#[inline]
190-
pub fn sstore_cost(
191-
spec_id: SpecId,
192-
original: U256,
193-
current: U256,
194-
new: U256,
195-
gas: u64,
196-
is_cold: bool,
197-
) -> Option<u64> {
192+
pub fn sstore_cost(spec_id: SpecId, vals: &SStoreResult, gas: u64, is_cold: bool) -> Option<u64> {
198193
// EIP-1706 Disable SSTORE with gasleft lower than call stipend
199194
if spec_id.is_enabled_in(SpecId::ISTANBUL) && gas <= CALL_STIPEND {
200195
return None;
201196
}
202197

203198
if spec_id.is_enabled_in(SpecId::BERLIN) {
204199
// Berlin specification logic
205-
let mut gas_cost = istanbul_sstore_cost::<WARM_STORAGE_READ_COST, WARM_SSTORE_RESET>(
206-
original, current, new,
207-
);
200+
let mut gas_cost = istanbul_sstore_cost::<WARM_STORAGE_READ_COST, WARM_SSTORE_RESET>(vals);
208201

209202
if is_cold {
210203
gas_cost += COLD_SLOAD_COST;
@@ -213,26 +206,24 @@ pub fn sstore_cost(
213206
} else if spec_id.is_enabled_in(SpecId::ISTANBUL) {
214207
// Istanbul logic
215208
Some(istanbul_sstore_cost::<INSTANBUL_SLOAD_GAS, SSTORE_RESET>(
216-
original, current, new,
209+
vals,
217210
))
218211
} else {
219212
// Frontier logic
220-
Some(frontier_sstore_cost(current, new))
213+
Some(frontier_sstore_cost(vals))
221214
}
222215
}
223216

224217
/// EIP-2200: Structured Definitions for Net Gas Metering
225218
#[inline]
226219
fn istanbul_sstore_cost<const SLOAD_GAS: u64, const SSTORE_RESET_GAS: u64>(
227-
original: U256,
228-
current: U256,
229-
new: U256,
220+
vals: &SStoreResult,
230221
) -> u64 {
231-
if new == current {
222+
if vals.is_new_eq_present() {
232223
SLOAD_GAS
233-
} else if original == current && original.is_zero() {
224+
} else if vals.is_original_eq_present() && vals.is_original_zero() {
234225
SSTORE_SET
235-
} else if original == current {
226+
} else if vals.is_original_eq_present() {
236227
SSTORE_RESET_GAS
237228
} else {
238229
SLOAD_GAS
@@ -241,8 +232,8 @@ fn istanbul_sstore_cost<const SLOAD_GAS: u64, const SSTORE_RESET_GAS: u64>(
241232

242233
/// Frontier sstore cost just had two cases set and reset values.
243234
#[inline]
244-
fn frontier_sstore_cost(current: U256, new: U256) -> u64 {
245-
if current.is_zero() && !new.is_zero() {
235+
fn frontier_sstore_cost(vals: &SStoreResult) -> u64 {
236+
if vals.is_present_zero() && !vals.is_new_zero() {
246237
SSTORE_SET
247238
} else {
248239
SSTORE_RESET
@@ -251,12 +242,12 @@ fn frontier_sstore_cost(current: U256, new: U256) -> u64 {
251242

252243
/// `SELFDESTRUCT` opcode cost calculation.
253244
#[inline]
254-
pub const fn selfdestruct_cost(spec_id: SpecId, res: SelfDestructResult) -> u64 {
245+
pub const fn selfdestruct_cost(spec_id: SpecId, res: StateLoad<SelfDestructResult>) -> u64 {
255246
// EIP-161: State trie clearing (invariant-preserving alternative)
256247
let should_charge_topup = if spec_id.is_enabled_in(SpecId::SPURIOUS_DRAGON) {
257-
res.had_value && !res.target_exists
248+
res.data.had_value && !res.data.target_exists
258249
} else {
259-
!res.target_exists
250+
!res.data.target_exists
260251
};
261252

262253
// EIP-150: Gas cost changes for IO-heavy operations
@@ -288,16 +279,24 @@ pub const fn selfdestruct_cost(spec_id: SpecId, res: SelfDestructResult) -> u64
288279
/// * Transfer value gas. If value is transferred and balance of target account is updated.
289280
/// * If account is not existing and needs to be created. After Spurious dragon
290281
/// this is only accounted if value is transferred.
282+
///
283+
/// account_load.is_empty will be accounted only if hardfork is SPURIOUS_DRAGON and
284+
/// there is transfer value.
285+
///
286+
/// This means that [`crate::OpCode::EXTSTATICCALL`],
287+
/// [`crate::OpCode::EXTDELEGATECALL`] that dont transfer value will not be
288+
/// effected by this field.
289+
///
290+
/// [`crate::OpCode::CALL`], [`crate::OpCode::EXTCALL`] use this field.
291+
///
292+
/// While [`crate::OpCode::STATICCALL`], [`crate::OpCode::DELEGATECALL`],
293+
/// [`crate::OpCode::CALLCODE`] need to have this field hardcoded to false
294+
/// as they were present before SPURIOUS_DRAGON hardfork.
291295
#[inline]
292-
pub const fn call_cost(
293-
spec_id: SpecId,
294-
transfers_value: bool,
295-
is_cold: bool,
296-
new_account_accounting: bool,
297-
) -> u64 {
296+
pub const fn call_cost(spec_id: SpecId, transfers_value: bool, account_load: AccountLoad) -> u64 {
298297
// Account access.
299298
let mut gas = if spec_id.is_enabled_in(SpecId::BERLIN) {
300-
warm_cold_cost(is_cold)
299+
warm_cold_cost_with_delegation(account_load.load)
301300
} else if spec_id.is_enabled_in(SpecId::TANGERINE) {
302301
// EIP-150: Gas cost changes for IO-heavy operations
303302
700
@@ -311,7 +310,7 @@ pub const fn call_cost(
311310
}
312311

313312
// new account cost
314-
if new_account_accounting {
313+
if account_load.is_empty {
315314
// EIP-161: State trie clearing (invariant-preserving alternative)
316315
if spec_id.is_enabled_in(SpecId::SPURIOUS_DRAGON) {
317316
// account only if there is value transferred.
@@ -336,6 +335,18 @@ pub const fn warm_cold_cost(is_cold: bool) -> u64 {
336335
}
337336
}
338337

338+
/// Berlin warm and cold storage access cost for account access.
339+
///
340+
/// If delegation is Some, add additional cost for delegation account load.
341+
#[inline]
342+
pub const fn warm_cold_cost_with_delegation(load: Eip7702CodeLoad<()>) -> u64 {
343+
let mut gas = warm_cold_cost(load.state_load.is_cold);
344+
if let Some(is_cold) = load.is_delegate_account_cold {
345+
gas += warm_cold_cost(is_cold);
346+
}
347+
gas
348+
}
349+
339350
/// Memory expansion cost calculation for a given memory length.
340351
#[inline]
341352
pub const fn memory_gas_for_len(len: usize) -> u64 {
@@ -400,7 +411,7 @@ pub fn validate_initial_tx_gas(
400411

401412
// EIP-7702
402413
if spec_id.is_enabled_in(SpecId::PRAGUE) {
403-
initial_gas += authorization_list_num * PER_AUTH_BASE_COST;
414+
initial_gas += authorization_list_num * eip7702::PER_EMPTY_ACCOUNT_COST;
404415
}
405416

406417
initial_gas

0 commit comments

Comments
 (0)