Skip to content

Commit 9d61e15

Browse files
authored
feat(l1): implement osaka EIPs 7918 & 7892 + use BlobSchedule from EFTests (#4284)
**Motivation** Implement changes defined in [EIP-7918](https://eips.ethereum.org/EIPS/eip-7918) & [EIP-7892](https://eips.ethereum.org/EIPS/eip-7892) Fix EFTests not using the `BlobSchedule` defined in the test files <!-- Why does this pull request exist? What are its goals? --> **Description** * Update `calc_excess_blob_gas` according to EIP * Add `ForkBlobSchedule`s for Osaka & BPO1-BPO5 forks * Enable EIP-7918 & EIP-7892 ef tests * Use the `BlobSchedule` defined in each test file when running EF Tests <!-- A clear and concise general description of the changes this PR introduces --> <!-- Link to issues: Resolves #111, Resolves #222 --> Closes #4157 & Closes #4156
1 parent c211d3a commit 9d61e15

File tree

13 files changed

+290
-60
lines changed

13 files changed

+290
-60
lines changed

crates/blockchain/payload.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,16 +121,11 @@ pub fn create_payload(args: &BuildPayloadArgs, storage: &Store) -> Result<Block,
121121
.get_block_header_by_hash(args.parent)?
122122
.ok_or_else(|| ChainError::ParentNotFound)?;
123123
let chain_config = storage.get_chain_config()?;
124+
let fork = chain_config.fork(args.timestamp);
124125
let gas_limit = calc_gas_limit(parent_block.gas_limit);
125126
let excess_blob_gas = chain_config
126127
.get_fork_blob_schedule(args.timestamp)
127-
.map(|schedule| {
128-
calc_excess_blob_gas(
129-
parent_block.excess_blob_gas.unwrap_or_default(),
130-
parent_block.blob_gas_used.unwrap_or_default(),
131-
schedule.target,
132-
)
133-
});
128+
.map(|schedule| calc_excess_blob_gas(&parent_block, schedule, fork));
134129

135130
let header = BlockHeader {
136131
parent_hash: args.parent,

crates/common/constants.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,6 @@ pub const GAS_PER_BLOB: u32 = 1 << 17;
5757

5858
// Minimum base fee per blob
5959
pub const MIN_BASE_FEE_PER_BLOB_GAS: u64 = 1;
60+
61+
// Blob base cost defined in EIP-7918
62+
pub const BLOB_BASE_COST: u64 = 8192;

crates/common/serde_utils.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,30 @@ pub mod u256 {
139139
}
140140
}
141141

142+
pub mod u32 {
143+
use super::*;
144+
145+
pub mod hex_str {
146+
use super::*;
147+
148+
pub fn deserialize<'de, D>(d: D) -> Result<u32, D::Error>
149+
where
150+
D: Deserializer<'de>,
151+
{
152+
let value = String::deserialize(d)?;
153+
u32::from_str_radix(value.trim_start_matches("0x"), 16)
154+
.map_err(|_| D::Error::custom("Failed to deserialize u32 value"))
155+
}
156+
157+
pub fn serialize<S>(value: &u32, serializer: S) -> Result<S::Ok, S::Error>
158+
where
159+
S: Serializer,
160+
{
161+
serializer.serialize_str(&format!("{value:#x}"))
162+
}
163+
}
164+
}
165+
142166
pub mod u64 {
143167
use serde::de::IntoDeserializer;
144168

crates/common/types/block.rs

Lines changed: 85 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
use super::{
2-
BASE_FEE_MAX_CHANGE_DENOMINATOR, ChainConfig, GAS_LIMIT_ADJUSTMENT_FACTOR, GAS_LIMIT_MINIMUM,
3-
INITIAL_BASE_FEE,
2+
BASE_FEE_MAX_CHANGE_DENOMINATOR, ChainConfig, Fork, ForkBlobSchedule,
3+
GAS_LIMIT_ADJUSTMENT_FACTOR, GAS_LIMIT_MINIMUM, INITIAL_BASE_FEE,
44
};
55
use crate::{
66
Address, H256, U256,
77
constants::{
8-
DEFAULT_OMMERS_HASH, EMPTY_WITHDRAWALS_HASH, GAS_PER_BLOB, MIN_BASE_FEE_PER_BLOB_GAS,
8+
BLOB_BASE_COST, DEFAULT_OMMERS_HASH, EMPTY_WITHDRAWALS_HASH, GAS_PER_BLOB,
9+
MIN_BASE_FEE_PER_BLOB_GAS,
910
},
1011
types::{Receipt, Transaction},
1112
};
@@ -719,11 +720,7 @@ fn validate_excess_blob_gas(
719720
let expected_excess_blob_gas = chain_config
720721
.get_fork_blob_schedule(header.timestamp)
721722
.map(|schedule| {
722-
calc_excess_blob_gas(
723-
parent_header.excess_blob_gas.unwrap_or_default(),
724-
parent_header.blob_gas_used.unwrap_or_default(),
725-
schedule.target,
726-
)
723+
calc_excess_blob_gas(parent_header, schedule, chain_config.fork(header.timestamp))
727724
})
728725
.unwrap_or_default();
729726
if header
@@ -735,14 +732,31 @@ fn validate_excess_blob_gas(
735732
Ok(())
736733
}
737734

738-
pub fn calc_excess_blob_gas(
739-
parent_excess_blob_gas: u64,
740-
parent_blob_gas_used: u64,
741-
target: u32,
742-
) -> u64 {
735+
pub fn calc_excess_blob_gas(parent: &BlockHeader, schedule: ForkBlobSchedule, fork: Fork) -> u64 {
736+
let parent_blob_gas_used = parent.blob_gas_used.unwrap_or_default();
737+
let parent_base_fee_per_gas = parent.base_fee_per_gas.unwrap_or_default();
738+
let parent_excess_blob_gas = parent.excess_blob_gas.unwrap_or_default();
739+
743740
let excess_blob_gas = parent_excess_blob_gas + parent_blob_gas_used;
744-
let target_blob_gas_per_block = target * GAS_PER_BLOB;
745-
excess_blob_gas.saturating_sub(target_blob_gas_per_block.into())
741+
let target_blob_gas_per_block = (schedule.target * GAS_PER_BLOB) as u64;
742+
if excess_blob_gas < target_blob_gas_per_block {
743+
return 0;
744+
}
745+
746+
if fork >= Fork::Osaka
747+
&& BLOB_BASE_COST * parent_base_fee_per_gas
748+
> (GAS_PER_BLOB as u64)
749+
* calculate_base_fee_per_blob_gas(
750+
parent_excess_blob_gas,
751+
schedule.base_fee_update_fraction,
752+
)
753+
{
754+
return parent_excess_blob_gas
755+
+ parent_blob_gas_used * (schedule.max as u64 - schedule.target as u64)
756+
/ schedule.max as u64;
757+
}
758+
759+
excess_blob_gas - target_blob_gas_per_block
746760
}
747761

748762
#[cfg(test)]
@@ -913,4 +927,60 @@ mod test {
913927
);
914928
assert_eq!(calc_base_fee, expected_base_fee)
915929
}
930+
931+
#[test]
932+
fn test_calc_blob_fee_post_osaka_bpo1() {
933+
let parent = BlockHeader {
934+
excess_blob_gas: Some(5149252),
935+
blob_gas_used: Some(1310720),
936+
base_fee_per_gas: Some(30),
937+
..Default::default()
938+
};
939+
let schedule = ForkBlobSchedule {
940+
target: 9,
941+
max: 14,
942+
base_fee_update_fraction: 8832827,
943+
};
944+
let fork = Fork::Osaka;
945+
946+
let res = calc_excess_blob_gas(&parent, schedule, fork);
947+
assert_eq!(res, 5617366)
948+
}
949+
950+
#[test]
951+
fn test_calc_blob_fee_post_osaka_bpo3() {
952+
let parent = BlockHeader {
953+
excess_blob_gas: Some(19251039),
954+
blob_gas_used: Some(2490368),
955+
base_fee_per_gas: Some(50),
956+
..Default::default()
957+
};
958+
let schedule = ForkBlobSchedule {
959+
target: 21,
960+
max: 32,
961+
base_fee_update_fraction: 20609697,
962+
};
963+
let fork = Fork::Osaka;
964+
let res = calc_excess_blob_gas(&parent, schedule, fork);
965+
assert_eq!(res, 20107103)
966+
}
967+
968+
#[test]
969+
fn test_calc_blob_fee_post_osaka_bpo1_ef() {
970+
let parent = BlockHeader {
971+
excess_blob_gas: Some(0x360000),
972+
blob_gas_used: Some(0),
973+
base_fee_per_gas: Some(0x11),
974+
..Default::default()
975+
};
976+
let schedule = ForkBlobSchedule {
977+
target: 9,
978+
max: 14,
979+
base_fee_update_fraction: 0x86c73b,
980+
};
981+
let fork = Fork::Osaka;
982+
983+
let res = calc_excess_blob_gas(&parent, schedule, fork);
984+
assert_eq!(res, 3538944)
985+
}
916986
}

crates/common/types/genesis.rs

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,31 @@ impl TryFrom<&Path> for Genesis {
8585
warn!("Invalid fork, only post-merge networks are supported.");
8686
}
8787

88+
if genesis.config.bpo1_time.is_some() && genesis.config.blob_schedule.bpo1.is_none()
89+
|| genesis.config.bpo2_time.is_some() && genesis.config.blob_schedule.bpo2.is_none()
90+
|| genesis.config.bpo3_time.is_some() && genesis.config.blob_schedule.bpo3.is_none()
91+
|| genesis.config.bpo4_time.is_some() && genesis.config.blob_schedule.bpo4.is_none()
92+
|| genesis.config.bpo5_time.is_some() && genesis.config.blob_schedule.bpo5.is_none()
93+
{
94+
warn!("BPO time set but no BPO BlobSchedule found in ChainConfig")
95+
}
96+
8897
Ok(genesis)
8998
}
9099
}
91100

92101
#[allow(unused)]
93102
#[derive(
94-
Clone, Copy, Debug, Serialize, Deserialize, PartialEq, RSerialize, RDeserialize, Archive,
103+
Clone,
104+
Copy,
105+
Debug,
106+
Serialize,
107+
Deserialize,
108+
PartialEq,
109+
RSerialize,
110+
RDeserialize,
111+
Archive,
112+
Default,
95113
)]
96114
#[serde(rename_all = "camelCase")]
97115
pub struct ForkBlobSchedule {
@@ -110,13 +128,31 @@ pub struct BlobSchedule {
110128
pub cancun: ForkBlobSchedule,
111129
#[serde(default = "default_prague_schedule")]
112130
pub prague: ForkBlobSchedule,
131+
#[serde(default = "default_osaka_schedule")]
132+
pub osaka: ForkBlobSchedule,
133+
#[serde(default, skip_serializing_if = "Option::is_none")]
134+
pub bpo1: Option<ForkBlobSchedule>,
135+
#[serde(default, skip_serializing_if = "Option::is_none")]
136+
pub bpo2: Option<ForkBlobSchedule>,
137+
#[serde(default, skip_serializing_if = "Option::is_none")]
138+
pub bpo3: Option<ForkBlobSchedule>,
139+
#[serde(default, skip_serializing_if = "Option::is_none")]
140+
pub bpo4: Option<ForkBlobSchedule>,
141+
#[serde(default, skip_serializing_if = "Option::is_none")]
142+
pub bpo5: Option<ForkBlobSchedule>,
113143
}
114144

115145
impl Default for BlobSchedule {
116146
fn default() -> Self {
117147
BlobSchedule {
118148
cancun: default_cancun_schedule(),
119149
prague: default_prague_schedule(),
150+
osaka: default_osaka_schedule(),
151+
bpo1: None,
152+
bpo2: None,
153+
bpo3: None,
154+
bpo4: None,
155+
bpo5: None,
120156
}
121157
}
122158
}
@@ -137,6 +173,14 @@ fn default_prague_schedule() -> ForkBlobSchedule {
137173
}
138174
}
139175

176+
fn default_osaka_schedule() -> ForkBlobSchedule {
177+
ForkBlobSchedule {
178+
target: 6,
179+
max: 9,
180+
base_fee_update_fraction: 5007716,
181+
}
182+
}
183+
140184
/// Blockchain settings defined per block
141185
#[allow(unused)]
142186
#[derive(
@@ -276,6 +320,26 @@ impl From<Fork> for &str {
276320
}
277321

278322
impl ChainConfig {
323+
pub fn is_bpo1_activated(&self, block_timestamp: u64) -> bool {
324+
self.bpo1_time.is_some_and(|time| time <= block_timestamp)
325+
}
326+
327+
pub fn is_bpo2_activated(&self, block_timestamp: u64) -> bool {
328+
self.bpo2_time.is_some_and(|time| time <= block_timestamp)
329+
}
330+
331+
pub fn is_bpo3_activated(&self, block_timestamp: u64) -> bool {
332+
self.bpo3_time.is_some_and(|time| time <= block_timestamp)
333+
}
334+
335+
pub fn is_bpo4_activated(&self, block_timestamp: u64) -> bool {
336+
self.bpo4_time.is_some_and(|time| time <= block_timestamp)
337+
}
338+
339+
pub fn is_bpo5_activated(&self, block_timestamp: u64) -> bool {
340+
self.bpo5_time.is_some_and(|time| time <= block_timestamp)
341+
}
342+
279343
pub fn is_osaka_activated(&self, block_timestamp: u64) -> bool {
280344
self.osaka_time.is_some_and(|time| time <= block_timestamp)
281345
}
@@ -348,7 +412,19 @@ impl ChainConfig {
348412
}
349413

350414
pub fn get_fork_blob_schedule(&self, block_timestamp: u64) -> Option<ForkBlobSchedule> {
351-
if self.is_prague_activated(block_timestamp) {
415+
if self.is_bpo5_activated(block_timestamp) {
416+
Some(self.blob_schedule.bpo5.unwrap_or_default())
417+
} else if self.is_bpo4_activated(block_timestamp) {
418+
Some(self.blob_schedule.bpo4.unwrap_or_default())
419+
} else if self.is_bpo3_activated(block_timestamp) {
420+
Some(self.blob_schedule.bpo3.unwrap_or_default())
421+
} else if self.is_bpo2_activated(block_timestamp) {
422+
Some(self.blob_schedule.bpo2.unwrap_or_default())
423+
} else if self.is_bpo1_activated(block_timestamp) {
424+
Some(self.blob_schedule.bpo1.unwrap_or_default())
425+
} else if self.is_osaka_activated(block_timestamp) {
426+
Some(self.blob_schedule.osaka)
427+
} else if self.is_prague_activated(block_timestamp) {
352428
Some(self.blob_schedule.prague)
353429
} else if self.is_cancun_activated(block_timestamp) {
354430
Some(self.blob_schedule.cancun)
@@ -559,6 +635,7 @@ mod tests {
559635
max: 4,
560636
base_fee_update_fraction: 13353908,
561637
},
638+
..Default::default()
562639
},
563640
..Default::default()
564641
};
@@ -734,6 +811,7 @@ mod tests {
734811
max: 4,
735812
base_fee_update_fraction: 20000,
736813
},
814+
..Default::default()
737815
},
738816
deposit_contract_address: H160::from_str("0x4242424242424242424242424242424242424242")
739817
.unwrap(),
@@ -766,6 +844,7 @@ mod tests {
766844
max: 9,
767845
base_fee_update_fraction: 5007716,
768846
},
847+
..Default::default()
769848
},
770849
deposit_contract_address: H160::from_str("0x4242424242424242424242424242424242424242")
771850
.unwrap(),
@@ -805,6 +884,7 @@ mod tests {
805884
max: 4,
806885
base_fee_update_fraction: 20000,
807886
},
887+
..Default::default()
808888
},
809889
deposit_contract_address: H160::from_str("0x4242424242424242424242424242424242424242")
810890
.unwrap(),
@@ -844,6 +924,7 @@ mod tests {
844924
max: 9,
845925
base_fee_update_fraction: 5007716,
846926
},
927+
..Default::default()
847928
},
848929
deposit_contract_address: H160::from_str("0x4242424242424242424242424242424242424242")
849930
.unwrap(),

0 commit comments

Comments
 (0)