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(l2-withdrawals): consensus rules #14308

Merged
merged 30 commits into from
Feb 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d055ac6
Checkout consensus rules from emhane/exec-verif-storage-root
emhane Feb 7, 2025
661368f
Remove lib dependency of reth-optimism-consensus on reth-trie
emhane Feb 7, 2025
223a3e8
Merge branch 'main' into emhane/l2-withdrawals-consensus-rules
emhane Feb 8, 2025
a151617
Fix merge conflicts
emhane Feb 8, 2025
345ea1d
Trigger compilation for windows to use locked crunchy version
emhane Feb 9, 2025
2f8f14c
Fix feature propagation
emhane Feb 9, 2025
fb30484
Format import prelude
emhane Feb 9, 2025
67d595d
Merge branch 'main' into emhane/l2-withdrawals-consensus-rules
emhane Feb 12, 2025
f2162d6
Update gate for shanghai check
emhane Feb 12, 2025
825e02b
Use thiserror in favour of derive_more
emhane Feb 12, 2025
45bb1fe
Remove crunchy dep
emhane Feb 12, 2025
7fc673a
Merge branch 'emhane/l2-withdrawals-consensus-rules' of github.com:pa…
emhane Feb 12, 2025
ca4b259
Replace dep reth-trie with reth-trie-common
emhane Feb 12, 2025
e1e93c8
fixup! Use thiserror in favour of derive_more
emhane Feb 12, 2025
21d8bf5
Fix no_std deps
emhane Feb 12, 2025
60053eb
Fix re-exports
emhane Feb 12, 2025
0ffa925
fixup! Fix re-exports
emhane Feb 12, 2025
bc735f9
Fix docs
emhane Feb 12, 2025
632b646
Merge branch 'main' into emhane/l2-withdrawals-consensus-rules
emhane Feb 13, 2025
5913ba1
Merge branch 'main' into emhane/l2-withdrawals-consensus-rules
emhane Feb 13, 2025
22cd267
Move shanghai checks to new file
emhane Feb 13, 2025
3eaef74
Remove redundant trait bound
emhane Feb 13, 2025
a4c2bb2
Fix manifest
emhane Feb 13, 2025
a8c91af
Merge branch 'main' into emhane/l2-withdrawals-consensus-rules
emhane Feb 14, 2025
e5c0faf
Fix deps
emhane Feb 14, 2025
54e5cff
Merge branch 'main' into emhane/l2-withdrawals-consensus-rules
emhane Feb 14, 2025
a5c9e10
Add String error message to ConsensusError::Other
emhane Feb 14, 2025
bc9370f
Fix no_std lib
emhane Feb 14, 2025
bdfa88c
Fix no_std deps
emhane Feb 14, 2025
e47bd7d
Fix no_std deps
emhane Feb 14, 2025
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
24 changes: 18 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion crates/consensus/consensus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

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_primitives::{BlockHash, BlockNumber, Bloom, B256, U256};
use reth_execution_types::BlockExecutionResult;
Expand Down Expand Up @@ -433,6 +433,9 @@ pub enum ConsensusError {
/// The block's timestamp.
timestamp: u64,
},
/// Other, likely an injected L2 error.
#[error("{0}")]
Other(String),
}

impl ConsensusError {
Expand Down
24 changes: 24 additions & 0 deletions crates/optimism/consensus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ reth-consensus-common.workspace = true
reth-consensus.workspace = true
reth-primitives.workspace = true
reth-primitives-traits.workspace = true
reth-storage-api.workspace = true
reth-storage-errors.workspace = true
reth-trie-common.workspace = true

# op-reth
reth-optimism-forks.workspace = true
Expand All @@ -31,13 +34,24 @@ alloy-eips.workspace = true
alloy-primitives.workspace = true
alloy-consensus.workspace = true
alloy-trie.workspace = true
revm.workspace = true
op-alloy-consensus.workspace = true

# misc
tracing.workspace = true
thiserror.workspace = true

[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
reth-optimism-chainspec.workspace = true
reth-trie.workspace = true

[features]
default = ["std"]
Expand All @@ -50,14 +64,24 @@ std = [
"reth-optimism-forks/std",
"reth-optimism-chainspec/std",
"reth-optimism-primitives/std",
"reth-storage-api/std",
"reth-storage-errors/std",
"reth-trie-common/std",
"alloy-chains/std",
"alloy-eips/std",
"alloy-primitives/std",
"alloy-consensus/std",
"alloy-trie/std",
"op-alloy-consensus/std",
"reth-revm/std",
"revm/std",
"tracing/std",
"thiserror/std",
"reth-execution-types/std",
]
optimism = [
"reth-optimism-primitives/optimism",
"revm/optimism",
"reth-execution-types/optimism",
"reth-optimism-node/optimism",
]
30 changes: 30 additions & 0 deletions crates/optimism/consensus/src/error.rs
Original file line number Diff line number Diff line change
@@ -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),
}
38 changes: 29 additions & 9 deletions crates/optimism/consensus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,36 @@

extern crate alloc;

use core::fmt::Debug;

use alloc::sync::Arc;
use alloc::{format, sync::Arc};
use alloy_consensus::{BlockHeader as _, EMPTY_OMMER_ROOT_HASH};
use alloy_primitives::{B64, U256};
use core::fmt::Debug;
use reth_chainspec::{EthChainSpec, EthereumHardforks};
use reth_consensus::{Consensus, ConsensusError, FullConsensus, HeaderValidator};
use reth_consensus_common::validation::{
validate_against_parent_4844, validate_against_parent_eip1559_base_fee,
validate_against_parent_hash_number, validate_against_parent_timestamp,
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_optimism_forks::OpHardforks;
use reth_optimism_primitives::DepositReceipt;
use reth_primitives::{GotExpected, NodePrimitives, RecoveredBlock, SealedHeader};
use reth_primitives_traits::{Block, BlockBody, BlockHeader, SealedBlock};

mod proof;
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::{
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.
///
/// Provides basic checks as outlined in the execution specs.
Expand Down Expand Up @@ -98,13 +101,30 @@ impl<ChainSpec: EthChainSpec + OpHardforks, B: Block> Consensus<B>
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()) {
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()) {
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(())
Expand Down
24 changes: 24 additions & 0 deletions crates/optimism/consensus/src/validation/canyon.rs
Original file line number Diff line number Diff line change
@@ -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(())
}
Loading
Loading