diff --git a/crates/consensus/auto-seal/src/lib.rs b/crates/consensus/auto-seal/src/lib.rs index 668a2117f6b..29cb6f49177 100644 --- a/crates/consensus/auto-seal/src/lib.rs +++ b/crates/consensus/auto-seal/src/lib.rs @@ -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() { diff --git a/crates/consensus/common/src/validation.rs b/crates/consensus/common/src/validation.rs index 485d6146737..fd7c6140d16 100644 --- a/crates/consensus/common/src/validation.rs +++ b/crates/consensus/common/src/validation.rs @@ -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}; @@ -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(()) } @@ -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(()) } @@ -376,6 +395,72 @@ pub fn full_validation 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::*; @@ -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 diff --git a/crates/interfaces/src/consensus.rs b/crates/interfaces/src/consensus.rs index 16f4d6c07ad..76c2175c9af 100644 --- a/crates/interfaces/src/consensus.rs +++ b/crates/interfaces/src/consensus.rs @@ -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), diff --git a/crates/net/eth-wire/src/types/blocks.rs b/crates/net/eth-wire/src/types/blocks.rs index 47777cd71b1..808c8f4a460 100644 --- a/crates/net/eth-wire/src/types/blocks.rs +++ b/crates/net/eth-wire/src/types/blocks.rs @@ -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); @@ -289,6 +291,8 @@ mod test { nonce: 0x0000000000000000u64, base_fee_per_gas: None, withdrawals_root: None, + blob_gas_used: None, + excess_blob_gas: None, }, ]), }; @@ -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, @@ -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, diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index 84da180060d..3e15444d15d 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -714,6 +714,8 @@ fn build_payload( difficulty: U256::ZERO, gas_used: cumulative_gas_used, extra_data: extra_data.into(), + blob_gas_used: None, + excess_blob_gas: None, }; // seal the block @@ -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 }; diff --git a/crates/primitives/src/blobfee.rs b/crates/primitives/src/blobfee.rs new file mode 100644 index 00000000000..e82b5d2f8c6 --- /dev/null +++ b/crates/primitives/src/blobfee.rs @@ -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) +} diff --git a/crates/primitives/src/header.rs b/crates/primitives/src/header.rs index ba8e8f31deb..e39c6a8287a 100644 --- a/crates/primitives/src/header.rs +++ b/crates/primitives/src/header.rs @@ -1,5 +1,6 @@ use crate::{ basefee::calculate_next_block_base_fee, + blobfee::calculate_excess_blob_gas, keccak256, proofs::{EMPTY_LIST_HASH, EMPTY_ROOT}, BlockBodyRoots, BlockHash, BlockNumHash, BlockNumber, Bloom, Bytes, H160, H256, H64, U256, @@ -7,7 +8,7 @@ use crate::{ use bytes::{Buf, BufMut, BytesMut}; use reth_codecs::{add_arbitrary_tests, derive_arbitrary, main_codec, Compact}; -use reth_rlp::{length_of_length, Decodable, Encodable, EMPTY_STRING_CODE}; +use reth_rlp::{length_of_length, Decodable, Encodable, EMPTY_LIST_CODE, EMPTY_STRING_CODE}; use serde::{Deserialize, Serialize}; use std::{ mem, @@ -91,6 +92,13 @@ pub struct Header { /// above the gas target, and decreasing when blocks are below the gas target. The base fee per /// gas is burned. pub base_fee_per_gas: Option, + /// The total amount of blob gas consumed by the transactions within the block, added in + /// EIP-4844. + pub blob_gas_used: Option, + /// A running total of blob gas consumed in excess of the target, prior to the block. Blocks + /// with above-target blob gas consumption increase this value, blocks with below-target blob + /// gas consumption decrease it (bounded at 0). This was added in EIP-4844. + pub excess_blob_gas: Option, /// An arbitrary byte array containing data relevant to this block. This must be 32 bytes or /// fewer; formally Hx. pub extra_data: Bytes, @@ -116,6 +124,8 @@ impl Default for Header { nonce: 0, base_fee_per_gas: None, withdrawals_root: None, + blob_gas_used: None, + excess_blob_gas: None, } } } @@ -170,6 +180,13 @@ impl Header { Some(calculate_next_block_base_fee(self.gas_used, self.gas_limit, self.base_fee_per_gas?)) } + /// Calculate excess blob gas for the next block according to the EIP-4844 spec. + /// + /// Returns a `None` if no excess blob gas is set, no EIP-4844 support + pub fn next_block_excess_blob_gas(&self) -> Option { + Some(calculate_excess_blob_gas(self.excess_blob_gas?, self.blob_gas_used?)) + } + /// Seal the header with a known hash. /// /// WARNING: This method does not perform validation whether the hash is correct. @@ -202,6 +219,8 @@ impl Header { mem::size_of::() + // mix hash mem::size_of::() + // nonce mem::size_of::>() + // base fee per gas + mem::size_of::>() + // blob gas used + mem::size_of::>() + // excess blob gas self.extra_data.len() // extra data } @@ -225,11 +244,34 @@ impl Header { if let Some(base_fee) = self.base_fee_per_gas { length += U256::from(base_fee).length(); - } else if self.withdrawals_root.is_some() { - length += 1; // EMTY STRING CODE + } else if self.withdrawals_root.is_some() || + self.blob_gas_used.is_some() || + self.excess_blob_gas.is_some() + { + length += 1; // EMPTY STRING CODE } + if let Some(root) = self.withdrawals_root { length += root.length(); + } else if self.blob_gas_used.is_some() || self.excess_blob_gas.is_some() { + length += 1; // EMPTY STRING CODE + } + + if let Some(blob_gas_used) = self.blob_gas_used { + length += U256::from(blob_gas_used).length(); + } else if self.excess_blob_gas.is_some() { + length += 1; // EMPTY STRING CODE + } + + // Encode excess blob gas length. If new fields are added, the above pattern will need to + // be repeated and placeholder length added. Otherwise, it's impossible to tell _which_ + // fields are missing. This is mainly relevant for contrived cases where a header is + // created at random, for example: + // * A header is created with a withdrawals root, but no base fee. Shanghai blocks are + // post-London, so this is technically not valid. However, a tool like proptest would + // generate a block like this. + if let Some(excess_blob_gas) = self.excess_blob_gas { + length += U256::from(excess_blob_gas).length(); } length @@ -261,12 +303,38 @@ impl Encodable for Header { // but withdrawals root is present. if let Some(ref base_fee) = self.base_fee_per_gas { U256::from(*base_fee).encode(out); - } else if self.withdrawals_root.is_some() { + } else if self.withdrawals_root.is_some() || + self.blob_gas_used.is_some() || + self.excess_blob_gas.is_some() + { out.put_u8(EMPTY_STRING_CODE); } + // Encode withdrawals root. Put empty string if withdrawals root is missing, + // but blob gas used is present. if let Some(ref root) = self.withdrawals_root { root.encode(out); + } else if self.blob_gas_used.is_some() || self.excess_blob_gas.is_some() { + out.put_u8(EMPTY_STRING_CODE); + } + + // Encode blob gas used. Put empty string if blob gas used is missing, + // but excess blob gas is present. + if let Some(ref blob_gas_used) = self.blob_gas_used { + U256::from(*blob_gas_used).encode(out); + } else if self.excess_blob_gas.is_some() { + out.put_u8(EMPTY_LIST_CODE); + } + + // Encode excess blob gas. If new fields are added, the above pattern will need to be + // repeated and placeholders added. Otherwise, it's impossible to tell _which_ fields + // are missing. This is mainly relevant for contrived cases where a header is created + // at random, for example: + // * A header is created with a withdrawals root, but no base fee. Shanghai blocks are + // post-London, so this is technically not valid. However, a tool like proptest would + // generate a block like this. + if let Some(ref excess_blob_gas) = self.excess_blob_gas { + U256::from(*excess_blob_gas).encode(out); } } @@ -303,7 +371,10 @@ impl Decodable for Header { nonce: H64::decode(buf)?.to_low_u64_be(), base_fee_per_gas: None, withdrawals_root: None, + blob_gas_used: None, + excess_blob_gas: None, }; + if started_len - buf.len() < rlp_head.payload_length { if buf.first().map(|b| *b == EMPTY_STRING_CODE).unwrap_or_default() { buf.advance(1) @@ -311,9 +382,36 @@ impl Decodable for Header { this.base_fee_per_gas = Some(U256::decode(buf)?.to::()); } } + + // Withdrawals root for post-shanghai headers + if started_len - buf.len() < rlp_head.payload_length { + if buf.first().map(|b| *b == EMPTY_STRING_CODE).unwrap_or_default() { + buf.advance(1) + } else { + this.withdrawals_root = Some(Decodable::decode(buf)?); + } + } + + // Blob gas used and excess blob gas for post-cancun headers + if started_len - buf.len() < rlp_head.payload_length { + if buf.first().map(|b| *b == EMPTY_LIST_CODE).unwrap_or_default() { + buf.advance(1) + } else { + this.blob_gas_used = Some(U256::decode(buf)?.to::()); + } + } + + // Decode excess blob gas. If new fields are added, the above pattern will need to be + // repeated and placeholders decoded. Otherwise, it's impossible to tell _which_ fields are + // missing. This is mainly relevant for contrived cases where a header is created at + // random, for example: + // * A header is created with a withdrawals root, but no base fee. Shanghai blocks are + // post-London, so this is technically not valid. However, a tool like proptest would + // generate a block like this. if started_len - buf.len() < rlp_head.payload_length { - this.withdrawals_root = Some(Decodable::decode(buf)?); + this.excess_blob_gas = Some(U256::decode(buf)?.to::()); } + let consumed = started_len - buf.len(); if consumed != rlp_head.payload_length { return Err(reth_rlp::DecodeError::ListLengthMismatch { @@ -536,6 +634,8 @@ mod ethers_compat { gas_used: block.gas_used.as_u64(), withdrawals_root: None, logs_bloom: block.logs_bloom.unwrap_or_default().0.into(), + blob_gas_used: None, + excess_blob_gas: None, } } } @@ -605,6 +705,8 @@ mod tests { nonce: 0, base_fee_per_gas: Some(0x036b_u64), withdrawals_root: None, + blob_gas_used: None, + excess_blob_gas: None, }; assert_eq!(header.hash_slow(), expected_hash); } @@ -683,6 +785,120 @@ mod tests { assert_eq!(header.hash_slow(), expected_hash); } + // Test vector from: https://github.com/ethereum/tests/blob/7e9e0940c0fcdbead8af3078ede70f969109bd85/BlockchainTests/ValidBlocks/bcExample/cancunExample.json + #[test] + fn test_decode_block_header_with_blob_fields_ef_tests() { + let data = hex::decode("f90221a03a9b485972e7353edd9152712492f0c58d89ef80623686b6bf947a4a6dce6cb6a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa03c837fc158e3e93eafcaf2e658a02f5d8f99abc9f1c4c66cdea96c0ca26406aea04409cc4b699384ba5f8248d92b784713610c5ff9c1de51e9239da0dac76de9cea046cab26abf1047b5b119ecc2dda1296b071766c8b1307e1381fcecc90d513d86b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008001887fffffffffffffff8302a86582079e42a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b42188000000000000000009a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218302000080").unwrap(); + let expected = Header { + parent_hash: H256::from_str( + "3a9b485972e7353edd9152712492f0c58d89ef80623686b6bf947a4a6dce6cb6", + ) + .unwrap(), + ommers_hash: H256::from_str( + "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + ) + .unwrap(), + beneficiary: Address::from_str("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba").unwrap(), + state_root: H256::from_str( + "3c837fc158e3e93eafcaf2e658a02f5d8f99abc9f1c4c66cdea96c0ca26406ae", + ) + .unwrap(), + transactions_root: H256::from_str( + "4409cc4b699384ba5f8248d92b784713610c5ff9c1de51e9239da0dac76de9ce", + ) + .unwrap(), + receipts_root: H256::from_str( + "46cab26abf1047b5b119ecc2dda1296b071766c8b1307e1381fcecc90d513d86", + ) + .unwrap(), + logs_bloom: Default::default(), + difficulty: U256::from(0), + number: 0x1, + gas_limit: 0x7fffffffffffffff, + gas_used: 0x02a865, + timestamp: 0x079e, + extra_data: Bytes::from(vec![0x42]), + mix_hash: H256::from_str( + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + ) + .unwrap(), + nonce: 0, + base_fee_per_gas: Some(9), + withdrawals_root: Some( + H256::from_str("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + .unwrap(), + ), + blob_gas_used: Some(0x020000), + excess_blob_gas: Some(0), + }; + + let header = Header::decode(&mut data.as_slice()).unwrap(); + assert_eq!(header, expected); + + let expected_hash = + H256::from_str("0x10aca3ebb4cf6ddd9e945a5db19385f9c105ede7374380c50d56384c3d233785") + .unwrap(); + assert_eq!(header.hash_slow(), expected_hash); + } + + #[test] + fn test_decode_block_header_with_blob_fields() { + // Block from devnet-7 + let data = hex::decode("f90239a013a7ec98912f917b3e804654e37c9866092043c13eb8eab94eb64818e886cff5a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794f97e180c050e5ab072211ad2c213eb5aee4df134a0ec229dbe85b0d3643ad0f471e6ec1a36bbc87deffbbd970762d22a53b35d068aa056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080830305988401c9c380808464c40d5499d883010c01846765746888676f312e32302e35856c696e7578a070ccadc40b16e2094954b1064749cc6fbac783c1712f1b271a8aac3eda2f232588000000000000000007a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421808401600000").unwrap(); + let expected = Header { + parent_hash: H256::from_str( + "13a7ec98912f917b3e804654e37c9866092043c13eb8eab94eb64818e886cff5", + ) + .unwrap(), + ommers_hash: H256::from_str( + "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + ) + .unwrap(), + beneficiary: Address::from_str("f97e180c050e5ab072211ad2c213eb5aee4df134").unwrap(), + state_root: H256::from_str( + "ec229dbe85b0d3643ad0f471e6ec1a36bbc87deffbbd970762d22a53b35d068a", + ) + .unwrap(), + transactions_root: H256::from_str( + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + ) + .unwrap(), + receipts_root: H256::from_str( + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + ) + .unwrap(), + logs_bloom: Default::default(), + difficulty: U256::from(0), + number: 0x30598, + gas_limit: 0x1c9c380, + gas_used: 0, + timestamp: 0x64c40d54, + extra_data: Bytes::from( + hex::decode("d883010c01846765746888676f312e32302e35856c696e7578").unwrap(), + ), + mix_hash: H256::from_str( + "70ccadc40b16e2094954b1064749cc6fbac783c1712f1b271a8aac3eda2f2325", + ) + .unwrap(), + nonce: 0, + base_fee_per_gas: Some(7), + withdrawals_root: Some( + H256::from_str("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + .unwrap(), + ), + blob_gas_used: Some(0), + excess_blob_gas: Some(0x1600000), + }; + + let header = Header::decode(&mut data.as_slice()).unwrap(); + assert_eq!(header, expected); + + let expected_hash = + H256::from_str("0x539c9ea0a3ca49808799d3964b8b6607037227de26bc51073c6926963127087b") + .unwrap(); + assert_eq!(header.hash_slow(), expected_hash); + } + #[test] fn sanity_direction() { let reverse = true; diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 64faa1e03b1..8e82bdd7073 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -23,6 +23,7 @@ pub mod abi; mod account; pub mod basefee; mod bits; +pub mod blobfee; mod block; pub mod bloom; mod chain; diff --git a/crates/rpc/rpc-types/src/eth/block.rs b/crates/rpc/rpc-types/src/eth/block.rs index ff47937af4e..a246baf20a7 100644 --- a/crates/rpc/rpc-types/src/eth/block.rs +++ b/crates/rpc/rpc-types/src/eth/block.rs @@ -267,6 +267,9 @@ impl Header { base_fee_per_gas, extra_data, withdrawals_root, + // TODO: add header fields to the rpc header + blob_gas_used: _, + excess_blob_gas: _, }, hash, } = primitive_header; diff --git a/crates/rpc/rpc-types/src/eth/engine/payload.rs b/crates/rpc/rpc-types/src/eth/engine/payload.rs index f187ec662a9..4cfb9bdd621 100644 --- a/crates/rpc/rpc-types/src/eth/engine/payload.rs +++ b/crates/rpc/rpc-types/src/eth/engine/payload.rs @@ -172,6 +172,9 @@ impl TryFrom for SealedBlock { ommers_hash: EMPTY_LIST_HASH, difficulty: Default::default(), nonce: Default::default(), + // TODO: add conversion once ExecutionPayload has 4844 fields + blob_gas_used: None, + excess_blob_gas: None, } .seal_slow(); diff --git a/crates/rpc/rpc/src/eth/api/pending_block.rs b/crates/rpc/rpc/src/eth/api/pending_block.rs index b5b8bc89c41..c9b74a3de7d 100644 --- a/crates/rpc/rpc/src/eth/api/pending_block.rs +++ b/crates/rpc/rpc/src/eth/api/pending_block.rs @@ -143,6 +143,8 @@ impl PendingBlockEnv { difficulty: U256::ZERO, gas_used: cumulative_gas_used, extra_data: Default::default(), + blob_gas_used: None, + excess_blob_gas: None, }; // seal the block diff --git a/testing/ef-tests/src/models.rs b/testing/ef-tests/src/models.rs index bca586bd866..989a0dd9e7f 100644 --- a/testing/ef-tests/src/models.rs +++ b/testing/ef-tests/src/models.rs @@ -100,6 +100,8 @@ impl From
for SealedHeader { parent_hash: value.parent_hash, logs_bloom: value.bloom, withdrawals_root: value.withdrawals_root, + blob_gas_used: None, + excess_blob_gas: None, }; header.seal(value.hash) }