Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: EIP-7742 #1600

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions crates/consensus/src/block/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,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 @@ -179,6 +189,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 @@ -208,6 +222,7 @@ impl From<super::Header> for AnyHeader {
excess_blob_gas,
parent_beacon_block_root,
requests_hash,
target_blobs_per_block,
} = value;

Self {
Expand All @@ -232,6 +247,7 @@ impl From<super::Header> for AnyHeader {
excess_blob_gas,
parent_beacon_block_root,
requests_hash,
target_blobs_per_block,
}
}
}
105 changes: 97 additions & 8 deletions crates/consensus/src/block/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use crate::constants::{EMPTY_OMMER_ROOT_HASH, EMPTY_ROOT_HASH};
use alloc::vec::Vec;
use alloy_eips::{
eip1559::{calc_next_block_base_fee, BaseFeeParams},
eip4844::{calc_blob_gasprice, calc_excess_blob_gas},
eip4844::{self, calc_blob_gasprice},
eip7742,
merge::ALLOWED_FUTURE_BLOCK_TIME_SECONDS,
BlockNumHash,
};
Expand Down Expand Up @@ -124,6 +125,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 @@ -156,6 +169,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 @@ -205,9 +219,21 @@ impl Header {
///
/// Returns `None` if `excess_blob_gas` is None.
///
/// If `next_block_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)
pub fn next_block_blob_fee(
&self,
next_block_target_blobs_per_block: Option<u64>,
) -> Option<u128> {
let next_block_excess_blob_gas = self.next_block_excess_blob_gas()?;
Some(next_block_target_blobs_per_block.map_or_else(
|| eip4844::calc_blob_gasprice(next_block_excess_blob_gas),
|target_blobs_per_block| {
eip7742::calc_blob_gasprice(next_block_excess_blob_gas, target_blobs_per_block)
},
))
}

/// Calculate base fee for next block according to the EIP-1559 spec.
Expand All @@ -227,7 +253,19 @@ impl Header {
///
/// 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 @@ -302,6 +340,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 @@ -395,6 +437,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 @@ -434,6 +480,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 @@ -463,6 +510,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 @@ -539,6 +591,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 @@ -613,6 +666,9 @@ 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;

Expand All @@ -621,16 +677,37 @@ pub trait BlockHeader {
///
/// 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]
fn next_block_blob_fee(&self) -> Option<u128> {
self.next_block_excess_blob_gas().map(calc_blob_gasprice)
/// If `next_block_target_blobs_per_block` is [`Some`], 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, next_block_target_blobs_per_block: Option<u64>) -> Option<u128> {
let next_block_excess_blob_gas = self.next_block_excess_blob_gas()?;
Some(next_block_target_blobs_per_block.map_or_else(
|| eip4844::calc_blob_gasprice(next_block_excess_blob_gas),
|target_blobs_per_block| {
eip7742::calc_blob_gasprice(next_block_excess_blob_gas, target_blobs_per_block)
},
))
}
}

Expand Down Expand Up @@ -715,6 +792,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 @@ -805,6 +886,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 @@ -858,6 +943,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 @@ -885,6 +972,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 @@ -913,6 +1001,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
31 changes: 31 additions & 0 deletions crates/eips/src/eip7742.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//! Contains constants and utility functions for [EIP-7742](https://eips.ethereum.org/EIPS/eip-7742)

use crate::eip4844::{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: u64 = 1112825;

/// Calculates the `excess_blob_gas` from the parent header's `blob_gas_used`, `excess_blob_gas` and
/// `target_blobs_per_block`.
///
/// Similar to [crate::eip4844::calc_excess_blob_gas], but derives the target blob gas from
/// `parent_target_blobs_per_block`.
#[inline]
pub const fn calc_excess_blob_gas(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit concerned that this will be easy to misuse if the functions have the same name, but since this takes an additional param this is okay imo

parent_excess_blob_gas: u64,
parent_blob_gas_used: u64,
parent_target_blobs_per_block: u64,
) -> u64 {
(parent_excess_blob_gas + parent_blob_gas_used)
.saturating_sub(DATA_GAS_PER_BLOB * parent_target_blobs_per_block)
}

/// 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, target_blobs_per_block: u64) -> u128 {
let update_fraction = BLOB_BASE_FEE_UPDATE_FRACTION_PER_TARGET_BLOB * target_blobs_per_block;
fake_exponential(BLOB_TX_MIN_BLOB_GASPRICE, excess_blob_gas as u128, update_fraction as u128)
}
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;
16 changes: 11 additions & 5 deletions crates/provider/src/fillers/gas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,13 +226,19 @@ where
}
}

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

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

latest_header
// We assume next block blob count to be the same as the latest block
//
// Blob target increases are expected to be rare, thus this should be correct most of
// the time
.next_block_blob_fee(latest_header.target_blobs_per_block())
.map(Into::into)
.ok_or(RpcError::UnsupportedFeature("eip4844"))
}
Expand Down
10 changes: 10 additions & 0 deletions crates/rpc-types-beacon/src/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ struct BeaconPayloadAttributes {
withdrawals: Option<Vec<Withdrawal>>,
#[serde(skip_serializing_if = "Option::is_none")]
parent_beacon_block_root: Option<B256>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde_as(as = "Option<DisplayFromStr>")]
target_blobs_per_block: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde_as(as = "Option<DisplayFromStr>")]
max_blobs_per_block: Option<u64>,
}

/// Optimism Payload Attributes
Expand Down Expand Up @@ -149,6 +155,8 @@ pub mod beacon_api_payload_attributes {
suggested_fee_recipient: payload_attributes.suggested_fee_recipient,
withdrawals: payload_attributes.withdrawals.clone(),
parent_beacon_block_root: payload_attributes.parent_beacon_block_root,
target_blobs_per_block: payload_attributes.target_blobs_per_block,
max_blobs_per_block: payload_attributes.max_blobs_per_block,
};
beacon_api_payload_attributes.serialize(serializer)
}
Expand All @@ -165,6 +173,8 @@ pub mod beacon_api_payload_attributes {
suggested_fee_recipient: beacon_api_payload_attributes.suggested_fee_recipient,
withdrawals: beacon_api_payload_attributes.withdrawals,
parent_beacon_block_root: beacon_api_payload_attributes.parent_beacon_block_root,
target_blobs_per_block: beacon_api_payload_attributes.target_blobs_per_block,
max_blobs_per_block: beacon_api_payload_attributes.max_blobs_per_block,
})
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/rpc-types-engine/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ extern crate alloc;

mod cancun;
pub use cancun::*;
mod prague;
pub use prague::*;
mod sidecar;
pub use sidecar::*;

Expand Down
Loading