Skip to content

Commit cd5a659

Browse files
authored
feat(EIP-7840): Add blob schedule to execution client cfg (#1980)
* feat(EIP-7840): Add blob schedule to execution client configuration files * fix test * no_std include vec * doc
1 parent 67200ca commit cd5a659

File tree

9 files changed

+157
-46
lines changed

9 files changed

+157
-46
lines changed

bins/revme/src/cmd/statetest/runner.rs

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use revm::{
1919
database_interface::EmptyDB,
2020
handler::EthHandler,
2121
primitives::{keccak256, Bytes, TxKind, B256},
22-
specification::hardfork::SpecId,
22+
specification::{eip4844::TARGET_BLOB_GAS_PER_BLOCK_CANCUN, hardfork::SpecId},
2323
Context, DatabaseCommit, EvmCommit, MainEvm,
2424
};
2525
use serde_json::json;
@@ -291,18 +291,6 @@ pub fn execute_test_suite(
291291
block.difficulty = unit.env.current_difficulty;
292292
// After the Merge prevrandao replaces mix_hash field in block and replaced difficulty opcode in EVM.
293293
block.prevrandao = unit.env.current_random;
294-
// EIP-4844
295-
if let Some(current_excess_blob_gas) = unit.env.current_excess_blob_gas {
296-
block.set_blob_excess_gas_and_price(current_excess_blob_gas.to());
297-
} else if let (Some(parent_blob_gas_used), Some(parent_excess_blob_gas)) = (
298-
unit.env.parent_blob_gas_used,
299-
unit.env.parent_excess_blob_gas,
300-
) {
301-
block.set_blob_excess_gas_and_price(calc_excess_blob_gas(
302-
parent_blob_gas_used.to(),
303-
parent_excess_blob_gas.to(),
304-
));
305-
}
306294

307295
// Tx env
308296
tx.caller = if let Some(address) = unit.transaction.sender {
@@ -344,6 +332,29 @@ pub fn execute_test_suite(
344332

345333
cfg.spec = spec_name.to_spec_id();
346334

335+
// EIP-4844
336+
if let Some(current_excess_blob_gas) = unit.env.current_excess_blob_gas {
337+
block.set_blob_excess_gas_and_price(
338+
current_excess_blob_gas.to(),
339+
cfg.spec.is_enabled_in(SpecId::PRAGUE),
340+
);
341+
} else if let (Some(parent_blob_gas_used), Some(parent_excess_blob_gas)) = (
342+
unit.env.parent_blob_gas_used,
343+
unit.env.parent_excess_blob_gas,
344+
) {
345+
block.set_blob_excess_gas_and_price(
346+
calc_excess_blob_gas(
347+
parent_blob_gas_used.to(),
348+
parent_excess_blob_gas.to(),
349+
unit.env
350+
.parent_target_blobs_per_block
351+
.map(|i| i.to())
352+
.unwrap_or(TARGET_BLOB_GAS_PER_BLOCK_CANCUN),
353+
),
354+
cfg.spec.is_enabled_in(SpecId::PRAGUE),
355+
);
356+
}
357+
347358
if cfg.spec.is_enabled_in(SpecId::MERGE) && block.prevrandao.is_none() {
348359
// If spec is merge and prevrandao is not set, set it to default
349360
block.prevrandao = Some(B256::default());

crates/context/interface/src/block/blob.rs

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
use specification::eip4844::{
2-
BLOB_GASPRICE_UPDATE_FRACTION, MIN_BLOB_GASPRICE, TARGET_BLOB_GAS_PER_BLOCK,
3-
};
1+
use specification::eip4844::{self, MIN_BLOB_GASPRICE};
42

53
/// Structure holding block blob excess gas and it calculates blob fee
64
///
@@ -20,34 +18,62 @@ pub struct BlobExcessGasAndPrice {
2018

2119
impl BlobExcessGasAndPrice {
2220
/// Creates a new instance by calculating the blob gas price with [`calc_blob_gasprice`].
23-
pub fn new(excess_blob_gas: u64) -> Self {
24-
let blob_gasprice = calc_blob_gasprice(excess_blob_gas);
21+
pub fn new(excess_blob_gas: u64, is_prague: bool) -> Self {
22+
let blob_gasprice = calc_blob_gasprice(excess_blob_gas, is_prague);
2523
Self {
2624
excess_blob_gas,
2725
blob_gasprice,
2826
}
2927
}
28+
29+
/// Calculate this block excess gas and price from the parent excess gas and gas used
30+
/// and the target blob gas per block.
31+
///
32+
/// This fields will be used to calculate `excess_blob_gas` with [`calc_excess_blob_gas`] func.
33+
pub fn from_parent_and_target(
34+
parent_excess_blob_gas: u64,
35+
parent_blob_gas_used: u64,
36+
parent_target_blob_gas_per_block: u64,
37+
is_prague: bool,
38+
) -> Self {
39+
Self::new(
40+
calc_excess_blob_gas(
41+
parent_excess_blob_gas,
42+
parent_blob_gas_used,
43+
parent_target_blob_gas_per_block,
44+
),
45+
is_prague,
46+
)
47+
}
3048
}
3149

3250
/// Calculates the `excess_blob_gas` from the parent header's `blob_gas_used` and `excess_blob_gas`.
3351
///
3452
/// See also [the EIP-4844 helpers]<https://eips.ethereum.org/EIPS/eip-4844#helpers>
3553
/// (`calc_excess_blob_gas`).
3654
#[inline]
37-
pub fn calc_excess_blob_gas(parent_excess_blob_gas: u64, parent_blob_gas_used: u64) -> u64 {
38-
(parent_excess_blob_gas + parent_blob_gas_used).saturating_sub(TARGET_BLOB_GAS_PER_BLOCK)
55+
pub fn calc_excess_blob_gas(
56+
parent_excess_blob_gas: u64,
57+
parent_blob_gas_used: u64,
58+
parent_target_blob_gas_per_block: u64,
59+
) -> u64 {
60+
(parent_excess_blob_gas + parent_blob_gas_used).saturating_sub(parent_target_blob_gas_per_block)
3961
}
4062

4163
/// Calculates the blob gas price from the header's excess blob gas field.
4264
///
4365
/// See also [the EIP-4844 helpers](https://eips.ethereum.org/EIPS/eip-4844#helpers)
4466
/// (`get_blob_gasprice`).
4567
#[inline]
46-
pub fn calc_blob_gasprice(excess_blob_gas: u64) -> u128 {
68+
pub fn calc_blob_gasprice(excess_blob_gas: u64, is_prague: bool) -> u128 {
4769
fake_exponential(
4870
MIN_BLOB_GASPRICE,
4971
excess_blob_gas,
50-
BLOB_GASPRICE_UPDATE_FRACTION,
72+
if is_prague {
73+
eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE
74+
} else {
75+
eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN
76+
},
5177
)
5278
}
5379

@@ -84,7 +110,10 @@ pub fn fake_exponential(factor: u64, numerator: u64, denominator: u64) -> u128 {
84110
#[cfg(test)]
85111
mod tests {
86112
use super::*;
87-
use specification::eip4844::GAS_PER_BLOB;
113+
use specification::eip4844::{
114+
BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN, GAS_PER_BLOB,
115+
TARGET_BLOB_GAS_PER_BLOCK_CANCUN as TARGET_BLOB_GAS_PER_BLOCK,
116+
};
88117

89118
// https://github.com/ethereum/go-ethereum/blob/28857080d732857030eda80c69b9ba2c8926f221/consensus/misc/eip4844/eip4844_test.go#L27
90119
#[test]
@@ -135,7 +164,11 @@ mod tests {
135164
0,
136165
),
137166
] {
138-
let actual = calc_excess_blob_gas(excess, blobs * GAS_PER_BLOB);
167+
let actual = calc_excess_blob_gas(
168+
excess,
169+
blobs * GAS_PER_BLOB,
170+
eip4844::TARGET_BLOB_GAS_PER_BLOCK_CANCUN,
171+
);
139172
assert_eq!(actual, expected, "test: {t:?}");
140173
}
141174
}
@@ -148,18 +181,18 @@ mod tests {
148181
(2314057, 1),
149182
(2314058, 2),
150183
(10 * 1024 * 1024, 23),
151-
// `calc_blob_gasprice` approximates `e ** (excess_blob_gas / BLOB_GASPRICE_UPDATE_FRACTION)` using Taylor expansion
184+
// `calc_blob_gasprice` approximates `e ** (excess_blob_gas / BLOB_BASE_FEE_UPDATE_FRACTION)` using Taylor expansion
152185
//
153186
// to roughly find where boundaries will be hit:
154-
// 2 ** bits = e ** (excess_blob_gas / BLOB_GASPRICE_UPDATE_FRACTION)
155-
// excess_blob_gas = ln(2 ** bits) * BLOB_GASPRICE_UPDATE_FRACTION
187+
// 2 ** bits = e ** (excess_blob_gas / BLOB_BASE_FEE_UPDATE_FRACTION)
188+
// excess_blob_gas = ln(2 ** bits) * BLOB_BASE_FEE_UPDATE_FRACTION
156189
(148099578, 18446739238971471609), // output is just below the overflow
157190
(148099579, 18446744762204311910), // output is just after the overflow
158191
(161087488, 902580055246494526580),
159192
];
160193

161194
for &(excess, expected) in blob_fee_vectors {
162-
let actual = calc_blob_gasprice(excess);
195+
let actual = calc_blob_gasprice(excess, false);
163196
assert_eq!(actual, expected, "test: {excess}");
164197
}
165198
}
@@ -183,7 +216,7 @@ mod tests {
183216
(1, 5, 2, 11), // approximate 12.18
184217
(2, 5, 2, 23), // approximate 24.36
185218
(1, 50000000, 2225652, 5709098764),
186-
(1, 380928, BLOB_GASPRICE_UPDATE_FRACTION, 1),
219+
(1, 380928, BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN, 1),
187220
] {
188221
let actual = fake_exponential(factor, numerator, denominator);
189222
assert_eq!(actual, expected, "test: {t:?}");

crates/context/interface/src/cfg.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,14 @@ pub trait Cfg {
1010

1111
fn chain_id(&self) -> u64;
1212

13-
// TODO : Make SpecId a associated type but for faster development we use impl Into.
13+
// Specification id that is set.
1414
fn spec(&self) -> Self::Spec;
1515

16+
/// Returns the blob target and max count for the given spec id.
17+
///
18+
/// EIP-7840: Add blob schedule to execution client configuration files
19+
fn blob_max_count(&self, spec_id: SpecId) -> u8;
20+
1621
fn max_code_size(&self) -> usize;
1722

1823
fn is_eip3607_disabled(&self) -> bool;

crates/context/interface/src/result.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ pub enum InvalidTransaction {
307307
///
308308
/// `to` must be present
309309
BlobCreateTransaction,
310-
/// Transaction has more then [`specification::eip4844::MAX_BLOB_NUMBER_PER_BLOCK`] blobs
310+
/// Transaction has more then `max` blobs
311311
TooManyBlobs {
312312
max: usize,
313313
have: usize,

crates/context/src/block.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ pub struct BlockEnv {
4646
impl BlockEnv {
4747
/// Takes `blob_excess_gas` saves it inside env
4848
/// and calculates `blob_fee` with [`BlobExcessGasAndPrice`].
49-
pub fn set_blob_excess_gas_and_price(&mut self, excess_blob_gas: u64) {
50-
self.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new(excess_blob_gas));
49+
pub fn set_blob_excess_gas_and_price(&mut self, excess_blob_gas: u64, is_prague: bool) {
50+
self.blob_excess_gas_and_price =
51+
Some(BlobExcessGasAndPrice::new(excess_blob_gas, is_prague));
5152
}
5253
}
5354

@@ -103,7 +104,7 @@ impl Default for BlockEnv {
103104
basefee: 0,
104105
difficulty: U256::ZERO,
105106
prevrandao: Some(B256::ZERO),
106-
blob_excess_gas_and_price: Some(BlobExcessGasAndPrice::new(0)),
107+
blob_excess_gas_and_price: Some(BlobExcessGasAndPrice::new(0, false)),
107108
}
108109
}
109110
}

crates/context/src/cfg.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub use context_interface::Cfg;
22

33
use interpreter::MAX_CODE_SIZE;
44
use specification::hardfork::SpecId;
5+
use std::{vec, vec::Vec};
56

67
/// EVM configuration
78
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
@@ -24,6 +25,10 @@ pub struct CfgEnv<SPEC: Into<SpecId> = SpecId> {
2425
pub limit_contract_code_size: Option<usize>,
2526
/// Skips the nonce validation against the account's nonce
2627
pub disable_nonce_check: bool,
28+
/// Blob target count. EIP-7840 Add blob schedule to EL config files.
29+
///
30+
/// Note : Items must be sorted by `SpecId`.
31+
pub blob_target_and_max_count: Vec<(SpecId, u8, u8)>,
2732
/// A hard memory limit in bytes beyond which
2833
/// [OutOfGasError::Memory][context_interface::result::OutOfGasError::Memory] cannot be resized.
2934
///
@@ -77,6 +82,12 @@ impl CfgEnv {
7782
self.chain_id = chain_id;
7883
self
7984
}
85+
86+
/// Sets the blob target and max count over hardforks.
87+
pub fn set_blob_max_and_target_count(&mut self, mut vec: Vec<(SpecId, u8, u8)>) {
88+
vec.sort_by_key(|(id, _, _)| *id);
89+
self.blob_target_and_max_count = vec;
90+
}
8091
}
8192

8293
impl<SPEC: Into<SpecId> + Copy> Cfg for CfgEnv<SPEC> {
@@ -90,6 +101,20 @@ impl<SPEC: Into<SpecId> + Copy> Cfg for CfgEnv<SPEC> {
90101
self.spec
91102
}
92103

104+
#[inline]
105+
fn blob_max_count(&self, spec_id: SpecId) -> u8 {
106+
self.blob_target_and_max_count
107+
.iter()
108+
.rev()
109+
.find_map(|(id, _, max)| {
110+
if spec_id as u8 >= *id as u8 {
111+
return Some(*max);
112+
}
113+
None
114+
})
115+
.unwrap_or(6)
116+
}
117+
93118
fn max_code_size(&self) -> usize {
94119
self.limit_contract_code_size.unwrap_or(MAX_CODE_SIZE)
95120
}
@@ -156,6 +181,7 @@ impl Default for CfgEnv {
156181
limit_contract_code_size: None,
157182
spec: SpecId::PRAGUE,
158183
disable_nonce_check: false,
184+
blob_target_and_max_count: vec![(SpecId::CANCUN, 3, 6), (SpecId::PRAGUE, 6, 9)],
159185
#[cfg(feature = "memory_limit")]
160186
memory_limit: (1 << 32) - 1,
161187
#[cfg(feature = "optional_balance_check")]
@@ -171,3 +197,17 @@ impl Default for CfgEnv {
171197
}
172198
}
173199
}
200+
201+
#[cfg(test)]
202+
mod test {
203+
use super::*;
204+
205+
#[test]
206+
fn blob_max_and_target_count() {
207+
let cfg = CfgEnv::default();
208+
assert_eq!(cfg.blob_max_count(SpecId::BERLIN), (6));
209+
assert_eq!(cfg.blob_max_count(SpecId::CANCUN), (6));
210+
assert_eq!(cfg.blob_max_count(SpecId::PRAGUE), (9));
211+
assert_eq!(cfg.blob_max_count(SpecId::OSAKA), (9));
212+
}
213+
}

crates/handler/src/validation.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ pub fn validate_eip4844_tx(
106106
blobs: &[B256],
107107
max_blob_fee: u128,
108108
block_blob_gas_price: u128,
109+
max_blobs: u8,
109110
) -> Result<(), InvalidTransaction> {
110111
// Ensure that the user was willing to at least pay the current blob gasprice
111112
if block_blob_gas_price > max_blob_fee {
@@ -126,10 +127,10 @@ pub fn validate_eip4844_tx(
126127

127128
// Ensure the total blob gas spent is at most equal to the limit
128129
// assert blob_gas_used <= MAX_BLOB_GAS_PER_BLOCK
129-
if blobs.len() > eip4844::MAX_BLOB_NUMBER_PER_BLOCK as usize {
130+
if blobs.len() > max_blobs as usize {
130131
return Err(InvalidTransaction::TooManyBlobs {
131132
have: blobs.len(),
132-
max: eip4844::MAX_BLOB_NUMBER_PER_BLOCK as usize,
133+
max: max_blobs as usize,
133134
});
134135
}
135136
Ok(())
@@ -220,6 +221,7 @@ where
220221
tx.blob_versioned_hashes(),
221222
tx.max_fee_per_blob_gas(),
222223
context.block().blob_gasprice().unwrap_or_default(),
224+
context.cfg().blob_max_count(spec_id),
223225
)?;
224226
}
225227
TransactionType::Eip7702 => {
Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,43 @@
11
//! EIP-4844 constants
2+
//!
3+
4+
/// First version of the blob
5+
pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01;
26

37
/// Gas consumption of a single data blob (== blob byte size)
48
pub const GAS_PER_BLOB: u64 = 1 << 17;
59

10+
/// Min blob gas price
11+
pub const MIN_BLOB_GASPRICE: u64 = 1;
12+
613
/// Target number of the blob per block
7-
pub const TARGET_BLOB_NUMBER_PER_BLOCK: u64 = 3;
14+
pub const TARGET_BLOB_NUMBER_PER_BLOCK_CANCUN: u64 = 3;
815

916
/// Max number of blobs per block
10-
pub const MAX_BLOB_NUMBER_PER_BLOCK: u64 = 2 * TARGET_BLOB_NUMBER_PER_BLOCK;
17+
pub const MAX_BLOB_NUMBER_PER_BLOCK_CANCUN: u64 = 2 * TARGET_BLOB_NUMBER_PER_BLOCK_CANCUN;
1118

1219
/// Maximum consumable blob gas for data blobs per block
13-
pub const MAX_BLOB_GAS_PER_BLOCK: u64 = MAX_BLOB_NUMBER_PER_BLOCK * GAS_PER_BLOB;
20+
pub const MAX_BLOB_GAS_PER_BLOCK_CANCUN: u64 = MAX_BLOB_NUMBER_PER_BLOCK_CANCUN * GAS_PER_BLOB;
1421

1522
/// Target consumable blob gas for data blobs per block (for 1559-like pricing)
16-
pub const TARGET_BLOB_GAS_PER_BLOCK: u64 = TARGET_BLOB_NUMBER_PER_BLOCK * GAS_PER_BLOB;
17-
18-
/// Minimum gas price for data blobs
19-
pub const MIN_BLOB_GASPRICE: u64 = 1;
23+
pub const TARGET_BLOB_GAS_PER_BLOCK_CANCUN: u64 =
24+
TARGET_BLOB_NUMBER_PER_BLOCK_CANCUN * GAS_PER_BLOB;
2025

2126
/// Controls the maximum rate of change for blob gas price
22-
pub const BLOB_GASPRICE_UPDATE_FRACTION: u64 = 3338477;
27+
pub const BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN: u64 = 3338477;
2328

24-
/// First version of the blob
25-
pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01;
29+
/// Target number of the blob per block
30+
pub const TARGET_BLOB_NUMBER_PER_BLOCK_PRAGUE: u64 = 6;
31+
32+
/// Max number of blobs per block
33+
pub const MAX_BLOB_NUMBER_PER_BLOCK_PRAGUE: u64 = 9;
34+
35+
/// Maximum consumable blob gas for data blobs per block
36+
pub const MAX_BLOB_GAS_PER_BLOCK_PRAGUE: u64 = MAX_BLOB_NUMBER_PER_BLOCK_PRAGUE * GAS_PER_BLOB;
37+
38+
/// Target consumable blob gas for data blobs per block (for 1559-like pricing)
39+
pub const TARGET_BLOB_GAS_PER_BLOCK_PRAGUE: u64 =
40+
TARGET_BLOB_NUMBER_PER_BLOCK_PRAGUE * GAS_PER_BLOB;
41+
42+
/// Controls the maximum rate of change for blob gas price
43+
pub const BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE: u64 = 5007716;

0 commit comments

Comments
 (0)