mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat(l2-withdrawals): consensus rules (#14308)
This commit is contained in:
24
Cargo.lock
generated
24
Cargo.lock
generated
@ -2460,15 +2460,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "data-encoding"
|
name = "data-encoding"
|
||||||
version = "2.7.0"
|
version = "2.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f"
|
checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "data-encoding-macro"
|
name = "data-encoding-macro"
|
||||||
version = "0.1.16"
|
version = "0.1.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b16d9d0d88a5273d830dac8b78ceb217ffc9b1d5404e5597a3542515329405b"
|
checksum = "9f9724adfcf41f45bf652b3995837669d73c4d49a1b5ac1ff82905ac7d9b5558"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"data-encoding",
|
"data-encoding",
|
||||||
"data-encoding-macro-internal",
|
"data-encoding-macro-internal",
|
||||||
@ -2476,9 +2476,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "data-encoding-macro-internal"
|
name = "data-encoding-macro-internal"
|
||||||
version = "0.1.14"
|
version = "0.1.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1145d32e826a7748b69ee8fc62d3e6355ff7f1051df53141e7048162fc90481b"
|
checksum = "18e4fdb82bd54a12e42fb58a800dcae6b9e13982238ce2296dc3570b92148e1f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"data-encoding",
|
"data-encoding",
|
||||||
"syn 2.0.98",
|
"syn 2.0.98",
|
||||||
@ -8457,6 +8457,7 @@ dependencies = [
|
|||||||
name = "reth-optimism-consensus"
|
name = "reth-optimism-consensus"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"alloy-chains",
|
||||||
"alloy-consensus",
|
"alloy-consensus",
|
||||||
"alloy-eips",
|
"alloy-eips",
|
||||||
"alloy-primitives",
|
"alloy-primitives",
|
||||||
@ -8465,12 +8466,23 @@ dependencies = [
|
|||||||
"reth-chainspec",
|
"reth-chainspec",
|
||||||
"reth-consensus",
|
"reth-consensus",
|
||||||
"reth-consensus-common",
|
"reth-consensus-common",
|
||||||
|
"reth-db-common",
|
||||||
"reth-execution-types",
|
"reth-execution-types",
|
||||||
"reth-optimism-chainspec",
|
"reth-optimism-chainspec",
|
||||||
"reth-optimism-forks",
|
"reth-optimism-forks",
|
||||||
|
"reth-optimism-node",
|
||||||
"reth-optimism-primitives",
|
"reth-optimism-primitives",
|
||||||
"reth-primitives",
|
"reth-primitives",
|
||||||
"reth-primitives-traits",
|
"reth-primitives-traits",
|
||||||
|
"reth-provider",
|
||||||
|
"reth-revm",
|
||||||
|
"reth-storage-api",
|
||||||
|
"reth-storage-errors",
|
||||||
|
"reth-trie",
|
||||||
|
"reth-trie-common",
|
||||||
|
"reth-trie-db",
|
||||||
|
"revm",
|
||||||
|
"thiserror 2.0.11",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
use alloc::{fmt::Debug, sync::Arc, vec::Vec};
|
use alloc::{fmt::Debug, string::String, sync::Arc, vec::Vec};
|
||||||
use alloy_consensus::Header;
|
use alloy_consensus::Header;
|
||||||
use alloy_primitives::{BlockHash, BlockNumber, Bloom, B256, U256};
|
use alloy_primitives::{BlockHash, BlockNumber, Bloom, B256, U256};
|
||||||
use reth_execution_types::BlockExecutionResult;
|
use reth_execution_types::BlockExecutionResult;
|
||||||
@ -433,6 +433,9 @@ pub enum ConsensusError {
|
|||||||
/// The block's timestamp.
|
/// The block's timestamp.
|
||||||
timestamp: u64,
|
timestamp: u64,
|
||||||
},
|
},
|
||||||
|
/// Other, likely an injected L2 error.
|
||||||
|
#[error("{0}")]
|
||||||
|
Other(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConsensusError {
|
impl ConsensusError {
|
||||||
|
|||||||
@ -19,6 +19,9 @@ reth-consensus-common.workspace = true
|
|||||||
reth-consensus.workspace = true
|
reth-consensus.workspace = true
|
||||||
reth-primitives.workspace = true
|
reth-primitives.workspace = true
|
||||||
reth-primitives-traits.workspace = true
|
reth-primitives-traits.workspace = true
|
||||||
|
reth-storage-api.workspace = true
|
||||||
|
reth-storage-errors.workspace = true
|
||||||
|
reth-trie-common.workspace = true
|
||||||
|
|
||||||
# op-reth
|
# op-reth
|
||||||
reth-optimism-forks.workspace = true
|
reth-optimism-forks.workspace = true
|
||||||
@ -31,13 +34,24 @@ alloy-eips.workspace = true
|
|||||||
alloy-primitives.workspace = true
|
alloy-primitives.workspace = true
|
||||||
alloy-consensus.workspace = true
|
alloy-consensus.workspace = true
|
||||||
alloy-trie.workspace = true
|
alloy-trie.workspace = true
|
||||||
|
revm.workspace = true
|
||||||
op-alloy-consensus.workspace = true
|
op-alloy-consensus.workspace = true
|
||||||
|
|
||||||
|
# misc
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
|
thiserror.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
reth-provider = { workspace = true, features = ["test-utils"] }
|
||||||
|
reth-trie-db.workspace = true
|
||||||
|
reth-db-common.workspace = true
|
||||||
|
reth-optimism-node.workspace = true
|
||||||
|
reth-revm.workspace = true
|
||||||
|
op-alloy-consensus.workspace = true
|
||||||
|
alloy-chains.workspace = true
|
||||||
alloy-primitives.workspace = true
|
alloy-primitives.workspace = true
|
||||||
reth-optimism-chainspec.workspace = true
|
reth-optimism-chainspec.workspace = true
|
||||||
|
reth-trie.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
@ -50,14 +64,24 @@ std = [
|
|||||||
"reth-optimism-forks/std",
|
"reth-optimism-forks/std",
|
||||||
"reth-optimism-chainspec/std",
|
"reth-optimism-chainspec/std",
|
||||||
"reth-optimism-primitives/std",
|
"reth-optimism-primitives/std",
|
||||||
|
"reth-storage-api/std",
|
||||||
|
"reth-storage-errors/std",
|
||||||
|
"reth-trie-common/std",
|
||||||
|
"alloy-chains/std",
|
||||||
"alloy-eips/std",
|
"alloy-eips/std",
|
||||||
"alloy-primitives/std",
|
"alloy-primitives/std",
|
||||||
"alloy-consensus/std",
|
"alloy-consensus/std",
|
||||||
"alloy-trie/std",
|
"alloy-trie/std",
|
||||||
"op-alloy-consensus/std",
|
"op-alloy-consensus/std",
|
||||||
|
"reth-revm/std",
|
||||||
|
"revm/std",
|
||||||
|
"tracing/std",
|
||||||
|
"thiserror/std",
|
||||||
"reth-execution-types/std",
|
"reth-execution-types/std",
|
||||||
]
|
]
|
||||||
optimism = [
|
optimism = [
|
||||||
"reth-optimism-primitives/optimism",
|
"reth-optimism-primitives/optimism",
|
||||||
|
"revm/optimism",
|
||||||
"reth-execution-types/optimism",
|
"reth-execution-types/optimism",
|
||||||
|
"reth-optimism-node/optimism",
|
||||||
]
|
]
|
||||||
|
|||||||
30
crates/optimism/consensus/src/error.rs
Normal file
30
crates/optimism/consensus/src/error.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
//! Optimism consensus errors
|
||||||
|
|
||||||
|
use alloy_primitives::B256;
|
||||||
|
use reth_consensus::ConsensusError;
|
||||||
|
use reth_storage_errors::provider::ProviderError;
|
||||||
|
|
||||||
|
/// Optimism consensus error.
|
||||||
|
#[derive(Debug, Clone, thiserror::Error)]
|
||||||
|
pub enum OpConsensusError {
|
||||||
|
/// Block body has non-empty withdrawals list (l1 withdrawals).
|
||||||
|
#[error("non-empty block body withdrawals list")]
|
||||||
|
WithdrawalsNonEmpty,
|
||||||
|
/// Failed to compute L2 withdrawals storage root.
|
||||||
|
#[error("compute L2 withdrawals root failed: {_0}")]
|
||||||
|
L2WithdrawalsRootCalculationFail(#[from] ProviderError),
|
||||||
|
/// L2 withdrawals root missing in block header.
|
||||||
|
#[error("L2 withdrawals root missing from block header")]
|
||||||
|
L2WithdrawalsRootMissing,
|
||||||
|
/// L2 withdrawals root in block header, doesn't match local storage root of predeploy.
|
||||||
|
#[error("L2 withdrawals root mismatch, header: {header}, exec_res: {exec_res}")]
|
||||||
|
L2WithdrawalsRootMismatch {
|
||||||
|
/// Storage root of pre-deploy in block.
|
||||||
|
header: B256,
|
||||||
|
/// Storage root of pre-deploy loaded from local state.
|
||||||
|
exec_res: B256,
|
||||||
|
},
|
||||||
|
/// L1 [`ConsensusError`], that also occurs on L2.
|
||||||
|
#[error(transparent)]
|
||||||
|
Eth(#[from] ConsensusError),
|
||||||
|
}
|
||||||
@ -12,33 +12,36 @@
|
|||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
use core::fmt::Debug;
|
use alloc::{format, sync::Arc};
|
||||||
|
|
||||||
use alloc::sync::Arc;
|
|
||||||
use alloy_consensus::{BlockHeader as _, EMPTY_OMMER_ROOT_HASH};
|
use alloy_consensus::{BlockHeader as _, EMPTY_OMMER_ROOT_HASH};
|
||||||
use alloy_primitives::{B64, U256};
|
use alloy_primitives::{B64, U256};
|
||||||
|
use core::fmt::Debug;
|
||||||
use reth_chainspec::{EthChainSpec, EthereumHardforks};
|
use reth_chainspec::{EthChainSpec, EthereumHardforks};
|
||||||
use reth_consensus::{Consensus, ConsensusError, FullConsensus, HeaderValidator};
|
use reth_consensus::{Consensus, ConsensusError, FullConsensus, HeaderValidator};
|
||||||
use reth_consensus_common::validation::{
|
use reth_consensus_common::validation::{
|
||||||
validate_against_parent_4844, validate_against_parent_eip1559_base_fee,
|
validate_against_parent_4844, validate_against_parent_eip1559_base_fee,
|
||||||
validate_against_parent_hash_number, validate_against_parent_timestamp,
|
validate_against_parent_hash_number, validate_against_parent_timestamp,
|
||||||
validate_body_against_header, validate_cancun_gas, validate_header_base_fee,
|
validate_body_against_header, validate_cancun_gas, validate_header_base_fee,
|
||||||
validate_header_extra_data, validate_header_gas, validate_shanghai_withdrawals,
|
validate_header_extra_data, validate_header_gas,
|
||||||
};
|
};
|
||||||
use reth_execution_types::BlockExecutionResult;
|
use reth_execution_types::BlockExecutionResult;
|
||||||
use reth_optimism_forks::OpHardforks;
|
use reth_optimism_forks::OpHardforks;
|
||||||
use reth_optimism_primitives::DepositReceipt;
|
use reth_optimism_primitives::DepositReceipt;
|
||||||
use reth_primitives::{GotExpected, NodePrimitives, RecoveredBlock, SealedHeader};
|
use reth_primitives::{GotExpected, NodePrimitives, RecoveredBlock, SealedHeader};
|
||||||
|
use reth_primitives_traits::{Block, BlockBody, BlockHeader, SealedBlock};
|
||||||
|
|
||||||
mod proof;
|
mod proof;
|
||||||
pub use proof::calculate_receipt_root_no_memo_optimism;
|
pub use proof::calculate_receipt_root_no_memo_optimism;
|
||||||
use reth_primitives_traits::{Block, BlockBody, BlockHeader, SealedBlock};
|
|
||||||
|
|
||||||
mod validation;
|
pub mod validation;
|
||||||
pub use validation::{
|
pub use validation::{
|
||||||
decode_holocene_base_fee, next_block_base_fee, validate_block_post_execution,
|
canyon, decode_holocene_base_fee, isthmus, next_block_base_fee, shanghai,
|
||||||
|
validate_block_post_execution,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
|
pub use error::OpConsensusError;
|
||||||
|
|
||||||
/// Optimism consensus implementation.
|
/// Optimism consensus implementation.
|
||||||
///
|
///
|
||||||
/// Provides basic checks as outlined in the execution specs.
|
/// Provides basic checks as outlined in the execution specs.
|
||||||
@ -98,13 +101,30 @@ impl<ChainSpec: EthChainSpec + OpHardforks, B: Block> Consensus<B>
|
|||||||
return Err(ConsensusError::BodyTransactionRootDiff(error.into()))
|
return Err(ConsensusError::BodyTransactionRootDiff(error.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// EIP-4895: Beacon chain push withdrawals as operations
|
// Check empty shanghai-withdrawals
|
||||||
if self.chain_spec.is_shanghai_active_at_timestamp(block.timestamp()) {
|
if self.chain_spec.is_shanghai_active_at_timestamp(block.timestamp()) {
|
||||||
validate_shanghai_withdrawals(block)?;
|
shanghai::ensure_empty_shanghai_withdrawals(block.body()).map_err(|err| {
|
||||||
|
ConsensusError::Other(format!("failed to verify block {}: {err}", block.number()))
|
||||||
|
})?
|
||||||
|
} else {
|
||||||
|
return Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.chain_spec.is_cancun_active_at_timestamp(block.timestamp()) {
|
if self.chain_spec.is_cancun_active_at_timestamp(block.timestamp()) {
|
||||||
validate_cancun_gas(block)?;
|
validate_cancun_gas(block)?;
|
||||||
|
} else {
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check withdrawals root field in header
|
||||||
|
if self.chain_spec.is_isthmus_active_at_timestamp(block.timestamp()) {
|
||||||
|
// storage root of withdrawals pre-deploy is verified post-execution
|
||||||
|
isthmus::ensure_withdrawals_storage_root_is_some(block.header()).map_err(|err| {
|
||||||
|
ConsensusError::Other(format!("failed to verify block {}: {err}", block.number()))
|
||||||
|
})?
|
||||||
|
} else {
|
||||||
|
// canyon is active, else would have returned already
|
||||||
|
canyon::ensure_empty_withdrawals_root(block.header())?
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
24
crates/optimism/consensus/src/validation/canyon.rs
Normal file
24
crates/optimism/consensus/src/validation/canyon.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//! Canyon consensus rule checks.
|
||||||
|
|
||||||
|
use alloy_consensus::BlockHeader;
|
||||||
|
use alloy_trie::EMPTY_ROOT_HASH;
|
||||||
|
use reth_consensus::ConsensusError;
|
||||||
|
use reth_primitives::GotExpected;
|
||||||
|
|
||||||
|
/// Verifies that withdrawals root in block header (Shanghai) is always [`EMPTY_ROOT_HASH`] in
|
||||||
|
/// Canyon.
|
||||||
|
#[inline]
|
||||||
|
pub fn ensure_empty_withdrawals_root<H: BlockHeader>(header: &H) -> Result<(), ConsensusError> {
|
||||||
|
// Shanghai rule
|
||||||
|
let header_withdrawals_root =
|
||||||
|
&header.withdrawals_root().ok_or(ConsensusError::WithdrawalsRootMissing)?;
|
||||||
|
|
||||||
|
// Canyon rules
|
||||||
|
if *header_withdrawals_root != EMPTY_ROOT_HASH {
|
||||||
|
return Err(ConsensusError::BodyWithdrawalsRootDiff(
|
||||||
|
GotExpected { got: *header_withdrawals_root, expected: EMPTY_ROOT_HASH }.into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
127
crates/optimism/consensus/src/validation/isthmus.rs
Normal file
127
crates/optimism/consensus/src/validation/isthmus.rs
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
//! Block verification w.r.t. consensus rules new in Isthmus hardfork.
|
||||||
|
|
||||||
|
use crate::OpConsensusError;
|
||||||
|
use alloy_consensus::BlockHeader;
|
||||||
|
use core::fmt;
|
||||||
|
use reth_optimism_primitives::predeploys::ADDRESS_L2_TO_L1_MESSAGE_PASSER;
|
||||||
|
use reth_storage_api::StorageRootProvider;
|
||||||
|
use reth_trie_common::HashedStorage;
|
||||||
|
use revm::db::BundleAccount;
|
||||||
|
|
||||||
|
/// Verifies that `withdrawals_root` (i.e. `l2tol1-msg-passer` storage root since Isthmus) field is
|
||||||
|
/// set in block header.
|
||||||
|
pub fn ensure_withdrawals_storage_root_is_some<H: BlockHeader>(
|
||||||
|
header: H,
|
||||||
|
) -> Result<(), OpConsensusError> {
|
||||||
|
header.withdrawals_root().ok_or(OpConsensusError::L2WithdrawalsRootMissing)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verifies block header field `withdrawals_root` against storage root of
|
||||||
|
/// `L2ToL1MessagePasser.sol` predeploy post block execution.
|
||||||
|
///
|
||||||
|
/// See <https://specs.optimism.io/protocol/isthmus/exec-engine.html#l2tol1messagepasser-storage-root-in-header>.
|
||||||
|
pub fn verify_withdrawals_storage_root<DB, H>(
|
||||||
|
predeploy_account_updates: Option<&BundleAccount>,
|
||||||
|
state: DB,
|
||||||
|
header: H,
|
||||||
|
) -> Result<(), OpConsensusError>
|
||||||
|
where
|
||||||
|
DB: StorageRootProvider,
|
||||||
|
H: BlockHeader + fmt::Debug,
|
||||||
|
{
|
||||||
|
let header_storage_root =
|
||||||
|
header.withdrawals_root().ok_or(OpConsensusError::L2WithdrawalsRootMissing)?;
|
||||||
|
|
||||||
|
// if block contained l2 withdrawals transactions, use predeploy storage updates from
|
||||||
|
// execution
|
||||||
|
let hashed_storage_updates = predeploy_account_updates.map(|acc| {
|
||||||
|
HashedStorage::from_plain_storage(
|
||||||
|
acc.status,
|
||||||
|
acc.storage.iter().map(|(slot, value)| (slot, &value.present_value)),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let storage_root = state
|
||||||
|
.storage_root(ADDRESS_L2_TO_L1_MESSAGE_PASSER, hashed_storage_updates.unwrap_or_default())
|
||||||
|
.map_err(OpConsensusError::L2WithdrawalsRootCalculationFail)?;
|
||||||
|
|
||||||
|
if header_storage_root != storage_root {
|
||||||
|
return Err(OpConsensusError::L2WithdrawalsRootMismatch {
|
||||||
|
header: header_storage_root,
|
||||||
|
exec_res: storage_root,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use alloc::sync::Arc;
|
||||||
|
use alloy_chains::Chain;
|
||||||
|
use alloy_consensus::Header;
|
||||||
|
use alloy_primitives::{keccak256, B256, U256};
|
||||||
|
use core::str::FromStr;
|
||||||
|
use reth_db_common::init::init_genesis;
|
||||||
|
use reth_optimism_chainspec::OpChainSpecBuilder;
|
||||||
|
use reth_optimism_node::OpNode;
|
||||||
|
use reth_provider::{
|
||||||
|
providers::BlockchainProvider, test_utils::create_test_provider_factory_with_node_types,
|
||||||
|
StateWriter,
|
||||||
|
};
|
||||||
|
use reth_storage_api::StateProviderFactory;
|
||||||
|
use reth_trie::test_utils::storage_root_prehashed;
|
||||||
|
use reth_trie_common::HashedPostState;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn l2tol1_message_passer_no_withdrawals() {
|
||||||
|
let hashed_address = keccak256(ADDRESS_L2_TO_L1_MESSAGE_PASSER);
|
||||||
|
|
||||||
|
// create account storage
|
||||||
|
let init_storage = HashedStorage::from_iter(
|
||||||
|
false,
|
||||||
|
[
|
||||||
|
"50000000000000000000000000000004253371b55351a08cb3267d4d265530b6",
|
||||||
|
"512428ed685fff57294d1a9cbb147b18ae5db9cf6ae4b312fa1946ba0561882e",
|
||||||
|
"51e6784c736ef8548f856909870b38e49ef7a4e3e77e5e945e0d5e6fcaa3037f",
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.map(|str| (B256::from_str(str).unwrap(), U256::from(1))),
|
||||||
|
);
|
||||||
|
let mut state = HashedPostState::default();
|
||||||
|
state.storages.insert(hashed_address, init_storage.clone());
|
||||||
|
|
||||||
|
// init test db
|
||||||
|
// note: must be empty (default) chain spec to ensure storage is empty after init genesis,
|
||||||
|
// otherwise can't use `storage_root_prehashed` to determine storage root later
|
||||||
|
let provider_factory = create_test_provider_factory_with_node_types::<OpNode>(Arc::new(
|
||||||
|
OpChainSpecBuilder::default().chain(Chain::dev()).genesis(Default::default()).build(),
|
||||||
|
));
|
||||||
|
let _ = init_genesis(&provider_factory).unwrap();
|
||||||
|
|
||||||
|
// write account storage to database
|
||||||
|
let provider_rw = provider_factory.provider_rw().unwrap();
|
||||||
|
provider_rw.write_hashed_state(&state.clone().into_sorted()).unwrap();
|
||||||
|
provider_rw.commit().unwrap();
|
||||||
|
|
||||||
|
// create block header with withdrawals root set to storage root of l2tol1-msg-passer
|
||||||
|
let header = Header {
|
||||||
|
withdrawals_root: Some(storage_root_prehashed(init_storage.storage)),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// create state provider factory
|
||||||
|
let state_provider_factory = BlockchainProvider::new(provider_factory).unwrap();
|
||||||
|
|
||||||
|
// validate block against existing state by passing empty state updates
|
||||||
|
verify_withdrawals_storage_root(
|
||||||
|
None,
|
||||||
|
state_provider_factory.latest().expect("load state"),
|
||||||
|
&header,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,9 @@
|
|||||||
|
//! Verification of blocks w.r.t. Optimism hardforks.
|
||||||
|
|
||||||
|
pub mod canyon;
|
||||||
|
pub mod isthmus;
|
||||||
|
pub mod shanghai;
|
||||||
|
|
||||||
use crate::proof::calculate_receipt_root_optimism;
|
use crate::proof::calculate_receipt_root_optimism;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use alloy_consensus::{BlockHeader, TxReceipt};
|
use alloy_consensus::{BlockHeader, TxReceipt};
|
||||||
20
crates/optimism/consensus/src/validation/shanghai.rs
Normal file
20
crates/optimism/consensus/src/validation/shanghai.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
//! L2 Shanghai consensus rule checks.
|
||||||
|
|
||||||
|
use crate::OpConsensusError;
|
||||||
|
use reth_consensus::ConsensusError;
|
||||||
|
use reth_primitives_traits::BlockBody;
|
||||||
|
|
||||||
|
/// Verifies that withdrawals in block body (Shanghai) is always empty in Canyon.
|
||||||
|
/// <https://specs.optimism.io/protocol/rollup-node-p2p.html#block-validation>
|
||||||
|
#[inline]
|
||||||
|
pub fn ensure_empty_shanghai_withdrawals<T: BlockBody>(body: &T) -> Result<(), OpConsensusError> {
|
||||||
|
// Shanghai rule
|
||||||
|
let withdrawals = body.withdrawals().ok_or(ConsensusError::BodyWithdrawalsMissing)?;
|
||||||
|
|
||||||
|
// Canyon rule
|
||||||
|
if !withdrawals.as_ref().is_empty() {
|
||||||
|
return Err(OpConsensusError::WithdrawalsNonEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@ -27,10 +27,13 @@ pub mod primitives {
|
|||||||
pub mod consensus {
|
pub mod consensus {
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use reth_consensus::*;
|
pub use reth_consensus::*;
|
||||||
#[doc(inline)]
|
/// Consensus rule checks.
|
||||||
pub use reth_consensus_common::*;
|
pub mod validation {
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use reth_optimism_consensus::*;
|
pub use reth_consensus_common::validation::*;
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use reth_optimism_consensus::validation::*;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Re-exported from `reth_chainspec`
|
/// Re-exported from `reth_chainspec`
|
||||||
|
|||||||
Reference in New Issue
Block a user