Skip to content

Commit

Permalink
feat: EIP-7742 (#1600)
Browse files Browse the repository at this point in the history
* basic utils and header update

* payload attributes

* sidecar

* rename

* fix

* fmt

* apply latest calc blob changes

* doc
  • Loading branch information
klkvr authored Nov 27, 2024
1 parent f837698 commit 0ca76e8
Show file tree
Hide file tree
Showing 12 changed files with 320 additions and 19 deletions.
16 changes: 16 additions & 0 deletions crates/consensus-any/src/block/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@ pub struct AnyHeader {
/// EIP-7685 requests hash.
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
pub requests_hash: Option<B256>,
/// EIP-7744 target blob count.
#[cfg_attr(
feature = "serde",
serde(
default,
with = "alloy_serde::quantity::opt",
skip_serializing_if = "Option::is_none"
)
)]
pub target_blobs_per_block: Option<u64>,
}

impl BlockHeader for AnyHeader {
Expand Down Expand Up @@ -178,6 +188,10 @@ impl BlockHeader for AnyHeader {
self.requests_hash
}

fn target_blobs_per_block(&self) -> Option<u64> {
self.target_blobs_per_block
}

fn extra_data(&self) -> &Bytes {
&self.extra_data
}
Expand Down Expand Up @@ -207,6 +221,7 @@ impl From<Header> for AnyHeader {
excess_blob_gas,
parent_beacon_block_root,
requests_hash,
target_blobs_per_block,
} = value;

Self {
Expand All @@ -231,6 +246,7 @@ impl From<Header> for AnyHeader {
excess_blob_gas,
parent_beacon_block_root,
requests_hash,
target_blobs_per_block,
}
}
}
111 changes: 105 additions & 6 deletions crates/consensus/src/block/header.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use crate::constants::{EMPTY_OMMER_ROOT_HASH, EMPTY_ROOT_HASH};
use alloc::vec::Vec;
use alloy_eips::{
calc_blob_gasprice,
eip1559::{calc_next_block_base_fee, BaseFeeParams},
eip1898::BlockWithParent,
eip4844::{calc_blob_gasprice, calc_excess_blob_gas},
eip4844::{self},
eip7742,
merge::ALLOWED_FUTURE_BLOCK_TIME_SECONDS,
BlockNumHash,
};
Expand Down Expand Up @@ -125,6 +127,18 @@ pub struct Header {
/// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
pub requests_hash: Option<B256>,
/// The target number of blobs in the block, introduced in [EIP-7742].
///
/// [EIP-7742]: https://eips.ethereum.org/EIPS/eip-7742
#[cfg_attr(
feature = "serde",
serde(
default,
with = "alloy_serde::quantity::opt",
skip_serializing_if = "Option::is_none"
)
)]
pub target_blobs_per_block: Option<u64>,
}

impl AsRef<Self> for Header {
Expand Down Expand Up @@ -157,6 +171,7 @@ impl Default for Header {
excess_blob_gas: None,
parent_beacon_block_root: None,
requests_hash: None,
target_blobs_per_block: None,
}
}
}
Expand Down Expand Up @@ -206,9 +221,18 @@ impl Header {
///
/// Returns `None` if `excess_blob_gas` is None.
///
/// If [`Self::target_blobs_per_block`] is [`Some`], uses EIP-7742 formula for calculating
/// the blob gas price, otherwise uses EIP-4844 formula.
///
/// See also [Self::next_block_excess_blob_gas]
pub fn next_block_blob_fee(&self) -> Option<u128> {
self.next_block_excess_blob_gas().map(calc_blob_gasprice)
let next_block_excess_blob_gas = self.next_block_excess_blob_gas()?;

if self.target_blobs_per_block().is_none() {
Some(eip4844::calc_blob_gasprice(next_block_excess_blob_gas))
} else {
Some(eip7742::calc_blob_gasprice(next_block_excess_blob_gas))
}
}

/// Calculate base fee for next block according to the EIP-1559 spec.
Expand All @@ -226,9 +250,27 @@ impl Header {
/// Calculate excess blob gas for the next block according to the EIP-4844
/// spec.
///
/// If [`Self::target_blobs_per_block`] is [`Some`], uses EIP-7742 formula for calculating
/// the excess blob gas, otherwise uses EIP-4844 formula.
///
/// Note: this function will return incorrect (unnormalized, lower) value at EIP-7742 activation
/// block. If this is undesired, consider using [`eip7742::calc_excess_blob_gas_at_transition`].
///
/// Returns a `None` if no excess blob gas is set, no EIP-4844 support
pub fn next_block_excess_blob_gas(&self) -> Option<u64> {
Some(calc_excess_blob_gas(self.excess_blob_gas?, self.blob_gas_used?))
let excess_blob_gas = self.excess_blob_gas?;
let blob_gas_used = self.blob_gas_used?;

Some(self.target_blobs_per_block.map_or_else(
|| eip4844::calc_excess_blob_gas(excess_blob_gas, blob_gas_used),
|target_blobs_per_block| {
eip7742::calc_excess_blob_gas(
excess_blob_gas,
blob_gas_used,
target_blobs_per_block,
)
},
))
}

/// Calculate a heuristic for the in-memory size of the [Header].
Expand Down Expand Up @@ -303,6 +345,10 @@ impl Header {
length += requests_hash.length();
}

if let Some(target_blobs_per_block) = self.target_blobs_per_block {
length += target_blobs_per_block.length();
}

length
}

Expand Down Expand Up @@ -403,6 +449,10 @@ impl Encodable for Header {
if let Some(ref requests_hash) = self.requests_hash {
requests_hash.encode(out);
}

if let Some(ref target_blobs_per_block) = self.target_blobs_per_block {
target_blobs_per_block.encode(out);
}
}

fn length(&self) -> usize {
Expand Down Expand Up @@ -442,6 +492,7 @@ impl Decodable for Header {
excess_blob_gas: None,
parent_beacon_block_root: None,
requests_hash: None,
target_blobs_per_block: None,
};
if started_len - buf.len() < rlp_head.payload_length {
this.base_fee_per_gas = Some(u64::decode(buf)?);
Expand Down Expand Up @@ -471,6 +522,11 @@ impl Decodable for Header {
this.requests_hash = Some(B256::decode(buf)?);
}

// Decode target blob count.
if started_len - buf.len() < rlp_head.payload_length {
this.target_blobs_per_block = Some(u64::decode(buf)?);
}

let consumed = started_len - buf.len();
if consumed != rlp_head.payload_length {
return Err(alloy_rlp::Error::ListLengthMismatch {
Expand Down Expand Up @@ -547,6 +603,7 @@ impl<'a> arbitrary::Arbitrary<'a> for Header {
parent_beacon_block_root: u.arbitrary()?,
requests_hash: u.arbitrary()?,
withdrawals_root: u.arbitrary()?,
target_blobs_per_block: u.arbitrary()?,
};

Ok(generate_valid_header(
Expand Down Expand Up @@ -622,24 +679,54 @@ pub trait BlockHeader {
/// Retrieves the requests hash of the block, if available
fn requests_hash(&self) -> Option<B256>;

/// Retrieves the target blob count of the block, if available
fn target_blobs_per_block(&self) -> Option<u64>;

/// Retrieves the block's extra data field
fn extra_data(&self) -> &Bytes;

/// Calculate excess blob gas for the next block according to the EIP-4844
/// spec.
///
/// If [`BlockHeader::target_blobs_per_block`] is [`Some`], uses EIP-7742 formula for
/// calculating the excess blob gas, otherwise uses EIP-4844 formula.
///
/// Note: this function will return incorrect (unnormalized, lower) value at EIP-7742 activation
/// block. If this is undesired, consider using [`eip7742::calc_excess_blob_gas_at_transition`].
///
/// Returns a `None` if no excess blob gas is set, no EIP-4844 support
fn next_block_excess_blob_gas(&self) -> Option<u64> {
Some(calc_excess_blob_gas(self.excess_blob_gas()?, self.blob_gas_used()?))
let excess_blob_gas = self.excess_blob_gas()?;
let blob_gas_used = self.blob_gas_used()?;

Some(self.target_blobs_per_block().map_or_else(
|| eip4844::calc_excess_blob_gas(excess_blob_gas, blob_gas_used),
|target_blobs_per_block| {
eip7742::calc_excess_blob_gas(
excess_blob_gas,
blob_gas_used,
target_blobs_per_block,
)
},
))
}

/// Returns the blob fee for the next block according to the EIP-4844 spec.
///
/// Returns `None` if `excess_blob_gas` is None.
///
/// See also [Self::next_block_excess_blob_gas]
/// If this header has `target_blobs_per_block` set, uses EIP-7742 formula for calculating
/// the blob gas price, otherwise uses EIP-4844 formula.
///
/// See also [BlockHeader::next_block_excess_blob_gas]
fn next_block_blob_fee(&self) -> Option<u128> {
self.next_block_excess_blob_gas().map(calc_blob_gasprice)
let next_block_excess_blob_gas = self.next_block_excess_blob_gas()?;

if self.target_blobs_per_block().is_none() {
Some(eip4844::calc_blob_gasprice(next_block_excess_blob_gas))
} else {
Some(eip7742::calc_blob_gasprice(next_block_excess_blob_gas))
}
}

/// Calculate base fee for next block according to the EIP-1559 spec.
Expand Down Expand Up @@ -743,6 +830,10 @@ impl BlockHeader for Header {
self.requests_hash
}

fn target_blobs_per_block(&self) -> Option<u64> {
self.target_blobs_per_block
}

fn extra_data(&self) -> &Bytes {
&self.extra_data
}
Expand Down Expand Up @@ -833,6 +924,10 @@ impl<T: BlockHeader> BlockHeader for alloy_serde::WithOtherFields<T> {
fn requests_hash(&self) -> Option<B256> {
self.inner.requests_hash()
}

fn target_blobs_per_block(&self) -> Option<u64> {
self.inner.target_blobs_per_block()
}
}

/// Bincode-compatibl [`Header`] serde implementation.
Expand Down Expand Up @@ -886,6 +981,8 @@ pub(crate) mod serde_bincode_compat {
parent_beacon_block_root: Option<B256>,
#[serde(default)]
requests_hash: Option<B256>,
#[serde(default)]
target_blobs_per_block: Option<u64>,
extra_data: Cow<'a, Bytes>,
}

Expand Down Expand Up @@ -913,6 +1010,7 @@ pub(crate) mod serde_bincode_compat {
parent_beacon_block_root: value.parent_beacon_block_root,
requests_hash: value.requests_hash,
extra_data: Cow::Borrowed(&value.extra_data),
target_blobs_per_block: value.target_blobs_per_block,
}
}
}
Expand Down Expand Up @@ -941,6 +1039,7 @@ pub(crate) mod serde_bincode_compat {
parent_beacon_block_root: value.parent_beacon_block_root,
requests_hash: value.requests_hash,
extra_data: value.extra_data.into_owned(),
target_blobs_per_block: value.target_blobs_per_block,
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/eips/src/eip4844/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ pub fn calc_blob_gasprice(excess_blob_gas: u64) -> u128 {
///
/// This function panics if `denominator` is zero.
#[inline]
fn fake_exponential(factor: u128, numerator: u128, denominator: u128) -> u128 {
pub fn fake_exponential(factor: u128, numerator: u128, denominator: u128) -> u128 {
assert_ne!(denominator, 0, "attempt to divide by zero");

let mut i = 1;
Expand Down
65 changes: 65 additions & 0 deletions crates/eips/src/eip7742.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//! Contains constants and utility functions for [EIP-7742](https://eips.ethereum.org/EIPS/eip-7742)
use crate::eip4844::{self, fake_exponential, BLOB_TX_MIN_BLOB_GASPRICE, DATA_GAS_PER_BLOB};

/// Controls the update rate of the blob base fee based on `target_blobs_per_block`.
pub const BLOB_BASE_FEE_UPDATE_FRACTION_PER_TARGET_BLOB: u128 = 1112825;

/// Controls the update rate of the blob base fee based on `target_blobs_per_block`.
pub const EXCESS_BLOB_GAS_NORMALIZATION_TARGET: u64 = 128;

/// Same as [`eip4844::BLOB_GASPRICE_UPDATE_FRACTION`], but normalized for the target of 128
/// blobs.
pub const BLOB_BASE_FEE_UPDATE_FRACTION_NORMALIZED: u128 =
BLOB_BASE_FEE_UPDATE_FRACTION_PER_TARGET_BLOB * EXCESS_BLOB_GAS_NORMALIZATION_TARGET as u128;

/// Calculates the `excess_blob_gas` for the header of the block enabling EIP-7742.
///
/// Normalizes the parent's excess blob gas as per EIP-7742.
#[inline]
pub const fn calc_excess_blob_gas_at_transition(
parent_excess_blob_gas: u64,
parent_blob_gas_used: u64,
parent_target_blobs_per_block: u64,
) -> u64 {
let normalized_parent_excess_blob_gas = parent_excess_blob_gas
* EXCESS_BLOB_GAS_NORMALIZATION_TARGET
/ eip4844::TARGET_BLOBS_PER_BLOCK;

calc_excess_blob_gas(
normalized_parent_excess_blob_gas,
parent_blob_gas_used,
parent_target_blobs_per_block,
)
}

/// Calculates the `excess_blob_gas` from the parent header's `blob_gas_used`, `excess_blob_gas` and
/// `target_blobs_per_block`.
///
/// Note: this function assumes that the parent block's excess blob gas is normalized as per
/// EIP-7742.
#[inline]
pub const fn calc_excess_blob_gas(
parent_excess_blob_gas: u64,
parent_blob_gas_used: u64,
parent_target_blobs_per_block: u64,
) -> u64 {
let normalized_blob_gas_used =
parent_blob_gas_used * EXCESS_BLOB_GAS_NORMALIZATION_TARGET / parent_target_blobs_per_block;
let normalized_target_blob_gas = DATA_GAS_PER_BLOB * EXCESS_BLOB_GAS_NORMALIZATION_TARGET;

(parent_excess_blob_gas + normalized_blob_gas_used).saturating_sub(normalized_target_blob_gas)
}

/// Calculates the blob gas price from the header's excess blob gas field.
///
/// Similar to [crate::eip4844::calc_blob_gasprice], but adjusts the update rate based on
/// `target_blobs_per_block`.
#[inline]
pub fn calc_blob_gasprice(excess_blob_gas: u64) -> u128 {
fake_exponential(
BLOB_TX_MIN_BLOB_GASPRICE,
excess_blob_gas as u128,
BLOB_BASE_FEE_UPDATE_FRACTION_NORMALIZED,
)
}
2 changes: 2 additions & 0 deletions crates/eips/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,5 @@ pub mod eip7251;
pub mod eip7685;

pub mod eip7702;

pub mod eip7742;
10 changes: 6 additions & 4 deletions crates/provider/src/fillers/gas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,12 +226,14 @@ where
}
}

provider
let latest_block = provider
.get_block_by_number(BlockNumberOrTag::Latest, BlockTransactionsKind::Hashes)
.await?
.ok_or(RpcError::NullResp)?
.header()
.as_ref()
.ok_or(RpcError::NullResp)?;

let latest_header = latest_block.header().as_ref();

latest_header
.next_block_blob_fee()
.map(Into::into)
.ok_or(RpcError::UnsupportedFeature("eip4844"))
Expand Down
Loading

0 comments on commit 0ca76e8

Please sign in to comment.