Skip to content
Merged
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
2 changes: 2 additions & 0 deletions crates/consensus/auto-seal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,8 @@ impl StorageInner {
nonce: 0,
base_fee_per_gas,
extra_data: Default::default(),
blob_gas_used: None,
excess_blob_gas: None,
};

header.transactions_root = if transactions.is_empty() {
Expand Down
91 changes: 89 additions & 2 deletions crates/consensus/common/src/validation.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
//! Collection of methods for block validation.
use reth_interfaces::{consensus::ConsensusError, Result as RethResult};
use reth_primitives::{
constants, BlockNumber, ChainSpec, Hardfork, Header, InvalidTransactionError, SealedBlock,
SealedHeader, Transaction, TransactionSignedEcRecovered, TxEip1559, TxEip2930, TxLegacy,
blobfee::calculate_excess_blob_gas,
constants::{
self,
eip4844::{DATA_GAS_PER_BLOB, MAX_DATA_GAS_PER_BLOCK},
},
BlockNumber, ChainSpec, Hardfork, Header, InvalidTransactionError, SealedBlock, SealedHeader,
Transaction, TransactionSignedEcRecovered, TxEip1559, TxEip2930, TxLegacy,
};
use reth_provider::{AccountReader, HeaderProvider, WithdrawalsProvider};
use std::collections::{hash_map::Entry, HashMap};
Expand Down Expand Up @@ -38,6 +43,15 @@ pub fn validate_header_standalone(
return Err(ConsensusError::WithdrawalsRootUnexpected)
}

// Ensures that EIP-4844 fields are valid once cancun is active.
if chain_spec.fork(Hardfork::Cancun).active_at_timestamp(header.timestamp) {
validate_4844_header_standalone(header)?;
} else if header.blob_gas_used.is_some() {
return Err(ConsensusError::BlobGasUsedUnexpected)
} else if header.excess_blob_gas.is_some() {
return Err(ConsensusError::ExcessBlobGasUnexpected)
}

Ok(())
}

Expand Down Expand Up @@ -291,6 +305,11 @@ pub fn validate_header_regarding_parent(
}
}

// ensure that the blob gas fields for this block
if chain_spec.fork(Hardfork::Cancun).active_at_timestamp(child.timestamp) {
validate_4844_header_with_parent(parent, child)?;
}

Ok(())
}

Expand Down Expand Up @@ -376,6 +395,72 @@ pub fn full_validation<Provider: HeaderProvider + AccountReader + WithdrawalsPro
Ok(())
}

/// Validates that the EIP-4844 header fields are correct with respect to the parent block. This
/// ensures that the `blob_gas_used` and `excess_blob_gas` fields exist in the child header, and
/// that the `excess_blob_gas` field matches the expected `excess_blob_gas` calculated from the
/// parent header fields.
pub fn validate_4844_header_with_parent(
parent: &SealedHeader,
child: &SealedHeader,
) -> Result<(), ConsensusError> {
// From [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844#header-extension):
//
// > For the first post-fork block, both parent.blob_gas_used and parent.excess_blob_gas
// > are evaluated as 0.
//
// This means in the first post-fork block, calculate_excess_blob_gas will return 0.
let parent_blob_gas_used = parent.blob_gas_used.unwrap_or(0);
let parent_excess_blob_gas = parent.excess_blob_gas.unwrap_or(0);

if child.blob_gas_used.is_none() {
return Err(ConsensusError::BlobGasUsedMissing)
}
let excess_blob_gas = child.excess_blob_gas.ok_or(ConsensusError::ExcessBlobGasMissing)?;

let expected_excess_blob_gas =
calculate_excess_blob_gas(parent_excess_blob_gas, parent_blob_gas_used);
if expected_excess_blob_gas != excess_blob_gas {
return Err(ConsensusError::ExcessBlobGasDiff {
expected: expected_excess_blob_gas,
got: excess_blob_gas,
parent_excess_blob_gas,
parent_blob_gas_used,
})
}

Ok(())
}

/// Validates that the EIP-4844 header fields exist and conform to the spec. This ensures that:
///
/// * `blob_gas_used` exists as a header field
/// * `excess_blob_gas` exists as a header field
/// * `blob_gas_used` is less than or equal to `MAX_DATA_GAS_PER_BLOCK`
/// * `blob_gas_used` is a multiple of `DATA_GAS_PER_BLOB`
pub fn validate_4844_header_standalone(header: &SealedHeader) -> Result<(), ConsensusError> {
let blob_gas_used = header.blob_gas_used.ok_or(ConsensusError::BlobGasUsedMissing)?;

if header.excess_blob_gas.is_none() {
return Err(ConsensusError::ExcessBlobGasMissing)
}

if blob_gas_used > MAX_DATA_GAS_PER_BLOCK {
return Err(ConsensusError::BlobGasUsedExceedsMaxBlobGasPerBlock {
blob_gas_used,
max_blob_gas_per_block: MAX_DATA_GAS_PER_BLOCK,
})
}

if blob_gas_used % DATA_GAS_PER_BLOB != 0 {
return Err(ConsensusError::BlobGasUsedNotMultipleOfBlobGasPerBlob {
blob_gas_used,
blob_gas_per_blob: DATA_GAS_PER_BLOB,
})
}

Ok(())
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -530,6 +615,8 @@ mod tests {
nonce: 0x0000000000000000,
base_fee_per_gas: 0x28f0001df.into(),
withdrawals_root: None,
blob_gas_used: None,
excess_blob_gas: None,
};
// size: 0x9b5

Expand Down
21 changes: 21 additions & 0 deletions crates/interfaces/src/consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,27 @@ pub enum ConsensusError {
WithdrawalIndexInvalid { got: u64, expected: u64 },
#[error("Missing withdrawals")]
BodyWithdrawalsMissing,
#[error("Missing blob gas used")]
BlobGasUsedMissing,
#[error("Unexpected blob gas used")]
BlobGasUsedUnexpected,
#[error("Missing excess blob gas")]
ExcessBlobGasMissing,
#[error("Unexpected excess blob gas")]
ExcessBlobGasUnexpected,
#[error("Blob gas used {blob_gas_used} exceeds maximum allowance {max_blob_gas_per_block}")]
BlobGasUsedExceedsMaxBlobGasPerBlock { blob_gas_used: u64, max_blob_gas_per_block: u64 },
#[error(
"Blob gas used {blob_gas_used} is not a multiple of blob gas per blob {blob_gas_per_blob}"
)]
BlobGasUsedNotMultipleOfBlobGasPerBlob { blob_gas_used: u64, blob_gas_per_blob: u64 },
#[error("Invalid excess blob gas. Expected: {expected}, got: {got}. Parent excess blob gas: {parent_excess_blob_gas}, parent blob gas used: {parent_blob_gas_used}.")]
ExcessBlobGasDiff {
expected: u64,
got: u64,
parent_excess_blob_gas: u64,
parent_blob_gas_used: u64,
},
/// Error for a transaction that violates consensus.
#[error(transparent)]
InvalidTransaction(#[from] InvalidTransactionError),
Expand Down
8 changes: 8 additions & 0 deletions crates/net/eth-wire/src/types/blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ mod test {
nonce: 0x0000000000000000u64,
base_fee_per_gas: None,
withdrawals_root: None,
blob_gas_used: None,
excess_blob_gas: None,
},
]),
}.encode(&mut data);
Expand Down Expand Up @@ -289,6 +291,8 @@ mod test {
nonce: 0x0000000000000000u64,
base_fee_per_gas: None,
withdrawals_root: None,
blob_gas_used: None,
excess_blob_gas: None,
},
]),
};
Expand Down Expand Up @@ -401,6 +405,8 @@ mod test {
nonce: 0x0000000000000000u64,
base_fee_per_gas: None,
withdrawals_root: None,
blob_gas_used: None,
excess_blob_gas: None,
},
],
withdrawals: None,
Expand Down Expand Up @@ -485,6 +491,8 @@ mod test {
nonce: 0x0000000000000000u64,
base_fee_per_gas: None,
withdrawals_root: None,
blob_gas_used: None,
excess_blob_gas: None,
},
],
withdrawals: None,
Expand Down
4 changes: 4 additions & 0 deletions crates/payload/basic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,8 @@ fn build_payload<Pool, Client>(
difficulty: U256::ZERO,
gas_used: cumulative_gas_used,
extra_data: extra_data.into(),
blob_gas_used: None,
excess_blob_gas: None,
};

// seal the block
Expand Down Expand Up @@ -785,6 +787,8 @@ where
difficulty: U256::ZERO,
gas_used: 0,
extra_data: extra_data.into(),
blob_gas_used: None,
excess_blob_gas: None,
};

let block = Block { header, body: vec![], ommers: vec![], withdrawals };
Expand Down
12 changes: 12 additions & 0 deletions crates/primitives/src/blobfee.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//! Helpers for working with EIP-4844 blob fee

use crate::constants::eip4844::TARGET_DATA_GAS_PER_BLOCK;

/// Calculates the excess data gas for the next block, after applying the current set of blobs on
/// top of the excess data gas.
///
/// Specified in [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844#header-extension)
pub fn calculate_excess_blob_gas(parent_excess_blob_gas: u64, parent_blob_gas_used: u64) -> u64 {
let excess_blob_gas = parent_excess_blob_gas + parent_blob_gas_used;
excess_blob_gas.saturating_sub(TARGET_DATA_GAS_PER_BLOCK)
}
Loading