Skip to content

Commit

Permalink
feat(client): Superchain consolidation
Browse files Browse the repository at this point in the history
  • Loading branch information
clabby committed Jan 30, 2025
1 parent ebdc804 commit 6344c66
Show file tree
Hide file tree
Showing 9 changed files with 271 additions and 33 deletions.
4 changes: 4 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions bin/client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ kona-std-fpvm-proc.workspace = true
# Maili
maili-protocol.workspace = true
maili-genesis = { workspace = true, features = ["serde"] }
maili-registry.workspace = true

# Alloy
alloy-rlp.workspace = true
Expand Down
59 changes: 43 additions & 16 deletions bin/client/src/interop/consolidate.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
//! Consolidation phase of the interop proof program.
use super::FaultProofProgramError;
use crate::interop::util::fetch_output_block_hash;
use alloc::{sync::Arc, vec::Vec};
use core::fmt::Debug;
use kona_interop::MessageGraph;
use kona_preimage::{HintWriterClient, PreimageOracleClient};
use kona_proof::CachingOracle;
use kona_proof_interop::{BootInfo, OracleInteropProvider, PreState};
use revm::primitives::HashMap;
use kona_proof::{errors::OracleProviderError, l2::OracleL2ChainProvider, CachingOracle};
use kona_proof_interop::{
BootInfo, HintType, OracleInteropProvider, PreState, SuperchainConsolidator,
};
use maili_registry::{HashMap, ROLLUP_CONFIGS};
use tracing::info;

/// Executes the consolidation phase of the interop proof with the given [PreimageOracleClient] and
Expand All @@ -20,13 +22,13 @@ use tracing::info;
pub(crate) async fn consolidate_dependencies<P, H>(
oracle: Arc<CachingOracle<P, H>>,
boot: BootInfo,
pre: PreState,
mut pre: PreState,
) -> Result<(), FaultProofProgramError>
where
P: PreimageOracleClient + Send + Sync + Debug + Clone,
H: HintWriterClient + Send + Sync + Debug + Clone,
{
let provider = OracleInteropProvider::new(oracle, pre.clone());
let interop_provider = OracleInteropProvider::new(oracle.clone(), pre.clone());

info!(target: "client_interop", "Deriving local-safe headers from prestate");

Expand All @@ -39,25 +41,50 @@ where
.pending_progress
.iter()
.zip(transition_state.pre_state.output_roots.iter())
.map(|(optimistic_block, pre_state)| (pre_state.chain_id, optimistic_block.block_hash))
.map(|(optimistic_block, pre_state)| (pre_state, optimistic_block.block_hash))
.collect::<HashMap<_, _>>();

let mut headers = Vec::with_capacity(block_hashes.len());
for (chain_id, block_hash) in block_hashes {
let header = provider.header_by_hash(chain_id, block_hash).await?;
headers.push((chain_id, header.seal(block_hash)));
let mut l2_providers = HashMap::default();
for (pre, block_hash) in block_hashes {
// Fetch the safe head's block hash for the given L2 chain ID.
let safe_head_hash =
fetch_output_block_hash(oracle.as_ref(), pre.output_root, pre.chain_id).await?;

// Send hints for the L2 block data in the pending progress. This is an important step,
// because non-canonical blocks within the pending progress will not be able to be fetched
// by the host through the traditional means. If the block is determined to not be canonical
// by the host, it will re-execute it and store the required preimages to complete
// deposit-only re-execution. If the block is determined to be canonical, the host will
// no-op, and fetch preimages through the traditional route as needed.
oracle
.write(&HintType::L2BlockData.encode_with(&[
safe_head_hash.as_slice(),
block_hash.as_slice(),
pre.chain_id.to_be_bytes().as_slice(),
]))
.await
.map_err(OracleProviderError::Preimage)?;

let header = interop_provider.header_by_hash(pre.chain_id, block_hash).await?;
headers.push((pre.chain_id, header.seal(block_hash)));

let rollup_config =
ROLLUP_CONFIGS.get(&pre.chain_id).cloned().expect("TODO: Handle gracefully");

let provider = OracleL2ChainProvider::new(safe_head_hash, rollup_config, oracle.clone());
l2_providers.insert(pre.chain_id, provider);
}

info!(target: "client_interop", "Loaded {} local-safe headers", headers.len());

// TODO: Re-execution w/ bad blocks. Not complete, we just panic if any deps are invalid atm.
let graph = MessageGraph::derive(headers.as_slice(), provider).await.unwrap();
graph.resolve().await.unwrap();
// Consolidate the superchain
SuperchainConsolidator::new(&mut pre, interop_provider, l2_providers, headers)
.consolidate()
.await
.expect("TODO: Handle gracefully");

// Transition to the Super Root at the next timestamp.
//
// TODO: This won't work if we replace blocks, `transition` doesn't allow replacement of pending
// progress just yet.
let post = pre.transition(None).ok_or(FaultProofProgramError::StateTransitionFailed)?;
let post_commitment = post.hash();

Expand Down
22 changes: 17 additions & 5 deletions bin/client/src/interop/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,27 @@ where
}
};

fetch_output_block_hash(caching_oracle, rich_output.output_root, rich_output.chain_id).await
}

/// Fetches the block hash that the passed output root commits to.
pub(crate) async fn fetch_output_block_hash<O>(
caching_oracle: &O,
output_root: B256,
chain_id: u64,
) -> Result<B256, OracleProviderError>
where
O: CommsClient,
{
caching_oracle
.write(&HintType::L2OutputRoot.encode_with(&[
rich_output.output_root.as_slice(),
rich_output.chain_id.to_be_bytes().as_slice(),
]))
.write(
&HintType::L2OutputRoot
.encode_with(&[output_root.as_slice(), chain_id.to_be_bytes().as_slice()]),
)
.await
.map_err(OracleProviderError::Preimage)?;
let output_preimage = caching_oracle
.get(PreimageKey::new(*rich_output.output_root, PreimageKeyType::Keccak256))
.get(PreimageKey::new(*output_root, PreimageKeyType::Keccak256))
.await
.map_err(OracleProviderError::Preimage)?;

Expand Down
22 changes: 11 additions & 11 deletions crates/interop/src/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use tracing::{info, warn};
///
/// [MessageIdentifier]: crate::MessageIdentifier
#[derive(Debug)]
pub struct MessageGraph<P> {
pub struct MessageGraph<'a, P> {
/// The horizon timestamp is the highest timestamp of all blocks containing [ExecutingMessage]s
/// within the graph.
///
Expand All @@ -36,10 +36,10 @@ pub struct MessageGraph<P> {
messages: Vec<EnrichedExecutingMessage>,
/// The data provider for the graph. Required for fetching headers, receipts and remote
/// messages within history during resolution.
provider: P,
provider: &'a P,
}

impl<P> MessageGraph<P>
impl<'a, P> MessageGraph<'a, P>
where
P: InteropProvider,
{
Expand All @@ -49,7 +49,7 @@ where
/// [ExecutingMessage]: crate::ExecutingMessage
pub async fn derive(
blocks: &[(u64, Sealed<Header>)],
provider: P,
provider: &'a P,
) -> MessageGraphResult<Self, P> {
info!(
target: "message-graph",
Expand Down Expand Up @@ -249,7 +249,7 @@ mod test {

let (headers, provider) = superchain.build();

let graph = MessageGraph::derive(headers.as_slice(), provider).await.unwrap();
let graph = MessageGraph::derive(headers.as_slice(), &provider).await.unwrap();
graph.resolve().await.unwrap();
}

Expand All @@ -270,7 +270,7 @@ mod test {

let (headers, provider) = superchain.build();

let graph = MessageGraph::derive(headers.as_slice(), provider).await.unwrap();
let graph = MessageGraph::derive(headers.as_slice(), &provider).await.unwrap();
graph.resolve().await.unwrap();
}

Expand All @@ -283,7 +283,7 @@ mod test {

let (headers, provider) = superchain.build();

let graph = MessageGraph::derive(headers.as_slice(), provider).await.unwrap();
let graph = MessageGraph::derive(headers.as_slice(), &provider).await.unwrap();
assert_eq!(graph.resolve().await.unwrap_err(), MessageGraphError::InvalidMessages(vec![2]));
}

Expand All @@ -296,7 +296,7 @@ mod test {

let (headers, provider) = superchain.build();

let graph = MessageGraph::derive(headers.as_slice(), provider).await.unwrap();
let graph = MessageGraph::derive(headers.as_slice(), &provider).await.unwrap();
assert_eq!(graph.resolve().await.unwrap_err(), MessageGraphError::InvalidMessages(vec![2]));
}

Expand All @@ -309,7 +309,7 @@ mod test {

let (headers, provider) = superchain.build();

let graph = MessageGraph::derive(headers.as_slice(), provider).await.unwrap();
let graph = MessageGraph::derive(headers.as_slice(), &provider).await.unwrap();
assert_eq!(graph.resolve().await.unwrap_err(), MessageGraphError::InvalidMessages(vec![2]));
}

Expand All @@ -322,7 +322,7 @@ mod test {

let (headers, provider) = superchain.build();

let graph = MessageGraph::derive(headers.as_slice(), provider).await.unwrap();
let graph = MessageGraph::derive(headers.as_slice(), &provider).await.unwrap();
assert_eq!(graph.resolve().await.unwrap_err(), MessageGraphError::InvalidMessages(vec![2]));
}

Expand All @@ -341,7 +341,7 @@ mod test {

let (headers, provider) = superchain.build();

let graph = MessageGraph::derive(headers.as_slice(), provider).await.unwrap();
let graph = MessageGraph::derive(headers.as_slice(), &provider).await.unwrap();
assert_eq!(graph.resolve().await.unwrap_err(), MessageGraphError::InvalidMessages(vec![2]));
}
}
2 changes: 1 addition & 1 deletion crates/interop/src/super_root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl SuperRoot {
}

/// A wrapper around an output root hash with the chain ID it belongs to.
#[derive(Debug, Clone, Eq, PartialEq)]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(any(feature = "arbitrary", test), derive(arbitrary::Arbitrary))]
pub struct OutputRootWithChain {
/// The chain ID of the output root.
Expand Down
3 changes: 3 additions & 0 deletions crates/proof-sdk/proof-interop/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ kona-preimage.workspace = true
kona-interop.workspace = true
kona-proof.workspace = true
kona-mpt.workspace = true
kona-executor.workspace = true

# Maili
maili-registry.workspace = true
Expand All @@ -27,9 +28,11 @@ alloy-rlp.workspace = true
alloy-primitives.workspace = true
alloy-consensus.workspace = true
alloy-eips.workspace = true
alloy-rpc-types-engine.workspace = true

# OP Alloy
op-alloy-consensus.workspace = true
op-alloy-rpc-types-engine.workspace = true

# General
serde.workspace = true
Expand Down
Loading

0 comments on commit 6344c66

Please sign in to comment.