mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-04-08 03:01:12 -04:00
feat: add 4844 header fields and consensus checks (#3972)
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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<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::*;
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
@@ -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 };
|
||||
|
||||
12
crates/primitives/src/blobfee.rs
Normal file
12
crates/primitives/src/blobfee.rs
Normal file
@@ -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)
|
||||
}
|
||||
@@ -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<u64>,
|
||||
/// The total amount of blob gas consumed by the transactions within the block, added in
|
||||
/// EIP-4844.
|
||||
pub blob_gas_used: Option<u64>,
|
||||
/// 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<u64>,
|
||||
/// 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<u64> {
|
||||
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::<H256>() + // mix hash
|
||||
mem::size_of::<u64>() + // nonce
|
||||
mem::size_of::<Option<u64>>() + // base fee per gas
|
||||
mem::size_of::<Option<u64>>() + // blob gas used
|
||||
mem::size_of::<Option<u64>>() + // 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::<u64>());
|
||||
}
|
||||
}
|
||||
|
||||
// Withdrawals root for post-shanghai headers
|
||||
if started_len - buf.len() < rlp_head.payload_length {
|
||||
this.withdrawals_root = Some(Decodable::decode(buf)?);
|
||||
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::<u64>());
|
||||
}
|
||||
}
|
||||
|
||||
// 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.excess_blob_gas = Some(U256::decode(buf)?.to::<u64>());
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -23,6 +23,7 @@ pub mod abi;
|
||||
mod account;
|
||||
pub mod basefee;
|
||||
mod bits;
|
||||
pub mod blobfee;
|
||||
mod block;
|
||||
pub mod bloom;
|
||||
mod chain;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -172,6 +172,9 @@ impl TryFrom<ExecutionPayload> 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();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -100,6 +100,8 @@ impl From<Header> 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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user