Skip to content

Commit

Permalink
feat(proof-interop): Support multiple RollupConfigs in boot
Browse files Browse the repository at this point in the history
  • Loading branch information
clabby committed Jan 31, 2025
1 parent ebdc804 commit 9ca353f
Show file tree
Hide file tree
Showing 14 changed files with 165 additions and 123 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

10 changes: 6 additions & 4 deletions bin/client/src/interop/consolidate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,17 @@ use tracing::info;
pub(crate) async fn consolidate_dependencies<P, H>(
oracle: Arc<CachingOracle<P, H>>,
boot: BootInfo,
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 provider = OracleInteropProvider::new(oracle, boot.agreed_pre_state.clone());

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

// Ensure that the pre-state is a transition state.
let PreState::TransitionState(ref transition_state) = pre else {
let PreState::TransitionState(ref transition_state) = boot.agreed_pre_state else {
return Err(FaultProofProgramError::StateTransitionFailed);
};

Expand All @@ -58,7 +57,10 @@ where
//
// 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 = boot
.agreed_pre_state
.transition(None)
.ok_or(FaultProofProgramError::StateTransitionFailed)?;
let post_commitment = post.hash();

// Ensure that the post-state matches the claimed post-state.
Expand Down
14 changes: 5 additions & 9 deletions bin/client/src/interop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
use alloc::sync::Arc;
use alloy_primitives::B256;
use alloy_rlp::Decodable;
use consolidate::consolidate_dependencies;
use core::fmt::Debug;
use kona_driver::DriverError;
Expand All @@ -13,7 +12,6 @@ use kona_proof_interop::{BootInfo, PreState, INVALID_TRANSITION_HASH, TRANSITION
use thiserror::Error;
use tracing::{error, info};
use transition::sub_transition;
use util::read_raw_pre_state;

pub(crate) mod consolidate;
pub(crate) mod transition;
Expand Down Expand Up @@ -69,7 +67,7 @@ where
};

// If the pre state is invalid, short-circuit and check if the post-state claim is also invalid.
if boot.agreed_pre_state == INVALID_TRANSITION_HASH &&
if boot.agreed_pre_state_commitment == INVALID_TRANSITION_HASH &&
boot.claimed_post_state == INVALID_TRANSITION_HASH
{
info!(target: "client_interop", "Invalid pre and post state, short-circuiting.");
Expand All @@ -78,20 +76,18 @@ where

// Load in the agreed pre-state from the preimage oracle in order to determine the active
// sub-problem.
let pre = PreState::decode(&mut read_raw_pre_state(oracle.as_ref(), &boot).await?.as_ref())
.map_err(FaultProofProgramError::RLPDecodingError)?;
match pre {
match boot.agreed_pre_state {
PreState::SuperRoot(_) => {
// If the pre-state is a super root, the first sub-problem is always selected.
sub_transition(oracle, handle_register, boot, pre).await
sub_transition(oracle, handle_register, boot).await
}
PreState::TransitionState(ref transition_state) => {
// If the pre-state is a transition state, the sub-problem is selected based on the
// current step.
if transition_state.step < TRANSITION_STATE_MAX_STEPS {
sub_transition(oracle, handle_register, boot, pre).await
sub_transition(oracle, handle_register, boot).await
} else {
consolidate_dependencies(oracle, boot, pre).await
consolidate_dependencies(oracle, boot).await
}
}
}
Expand Down
55 changes: 34 additions & 21 deletions bin/client/src/interop/transition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,31 +31,42 @@ pub(crate) async fn sub_transition<P, H>(
>,
>,
boot: BootInfo,
pre: PreState,
) -> Result<(), FaultProofProgramError>
where
P: PreimageOracleClient + Send + Sync + Debug + Clone,
H: HintWriterClient + Send + Sync + Debug + Clone,
{
// Check if we can short-circuit the transition, if we are within padding.
if let PreState::TransitionState(ref transition_state) = pre {
if let PreState::TransitionState(ref transition_state) = boot.agreed_pre_state {
if transition_state.step >= transition_state.pre_state.output_roots.len() as u64 {
info!(
target: "interop_client",
"No derivation/execution required, transition state is already saturated."
);

return transition_and_check(pre, None, boot.claimed_post_state, None);
return transition_and_check(boot.agreed_pre_state, None, boot.claimed_post_state, None);
}
}

// Fetch the L2 block hash of the current safe head.
let safe_head_hash = fetch_l2_safe_head_hash(oracle.as_ref(), &pre).await?;
let safe_head_hash = fetch_l2_safe_head_hash(oracle.as_ref(), &boot.agreed_pre_state).await?;

// Determine the active L2 chain ID and the fetch rollup configuration.
let active_l2_chain_id = boot
.agreed_pre_state
.active_l2_chain_id()
.ok_or(FaultProofProgramError::StateTransitionFailed)?;
let rollup_config = boot
.rollup_configs
.get(&active_l2_chain_id)
.cloned()
.map(Arc::new)
.ok_or(FaultProofProgramError::StateTransitionFailed)?;

// Instantiate the L1 EL + CL provider and the L2 EL provider.
let mut l1_provider = OracleL1ChainProvider::new(boot.l1_head, oracle.clone());
let mut l2_provider =
OracleL2ChainProvider::new(safe_head_hash, boot.rollup_config.clone(), oracle.clone());
OracleL2ChainProvider::new(safe_head_hash, rollup_config.clone(), oracle.clone());
let beacon = OracleBlobProvider::new(oracle.clone());

// Fetch the safe head's block header.
Expand All @@ -64,9 +75,8 @@ where
.map(|header| Sealed::new_unchecked(header, safe_head_hash))?;

// Translate the claimed timestamp to an L2 block number.
let claimed_l2_block_number = boot.rollup_config.genesis.l2.number +
((boot.claimed_l2_timestamp - boot.rollup_config.genesis.l2_time) /
boot.rollup_config.block_time);
let claimed_l2_block_number = rollup_config.genesis.l2.number +
((boot.claimed_l2_timestamp - rollup_config.genesis.l2_time) / rollup_config.block_time);

// If the claimed L2 block number is less than the safe head of the L2 chain, the claim is
// invalid.
Expand All @@ -79,7 +89,7 @@ where
safe = safe_head.number
);
return Err(FaultProofProgramError::InvalidClaim(
boot.agreed_pre_state,
boot.agreed_pre_state_commitment,
boot.claimed_post_state,
));
}
Expand All @@ -95,29 +105,34 @@ where

// Create a new derivation driver with the given boot information and oracle.
let cursor =
new_pipeline_cursor(&boot.rollup_config, safe_head, &mut l1_provider, &mut l2_provider)
new_pipeline_cursor(rollup_config.as_ref(), safe_head, &mut l1_provider, &mut l2_provider)
.await?;
l2_provider.set_cursor(cursor.clone());

let cfg = Arc::new(boot.rollup_config.clone());
let pipeline = OraclePipeline::new(
cfg.clone(),
rollup_config.clone(),
cursor.clone(),
oracle.clone(),
beacon,
l1_provider.clone(),
l2_provider.clone(),
);
let executor = KonaExecutor::new(&cfg, l2_provider.clone(), l2_provider, handle_register, None);
let executor = KonaExecutor::new(
rollup_config.as_ref(),
l2_provider.clone(),
l2_provider,
handle_register,
None,
);
let mut driver = Driver::new(cursor, executor, pipeline);

// Run the derivation pipeline until we are able to produce the output root of the claimed
// L2 block.
match driver.advance_to_target(&boot.rollup_config, Some(claimed_l2_block_number)).await {
match driver.advance_to_target(rollup_config.as_ref(), Some(claimed_l2_block_number)).await {
Ok((safe_head, output_root)) => {
let optimistic_block = OptimisticBlock::new(safe_head.block_info.hash, output_root);
transition_and_check(
pre,
boot.agreed_pre_state,
Some(optimistic_block),
boot.claimed_post_state,
Some((boot.claimed_l2_timestamp, safe_head.block_info.timestamp)),
Expand All @@ -137,14 +152,12 @@ where
"Exhausted data source; Transitioning to invalid state."
);

if boot.claimed_post_state == INVALID_TRANSITION_HASH {
Ok(())
} else {
Err(FaultProofProgramError::InvalidClaim(
(boot.claimed_post_state == INVALID_TRANSITION_HASH).then_some(()).ok_or(
FaultProofProgramError::InvalidClaim(
INVALID_TRANSITION_HASH,
boot.claimed_post_state,
))
}
),
)
}
Err(e) => {
error!(
Expand Down
33 changes: 3 additions & 30 deletions bin/client/src/interop/util.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,12 @@
//! Utilities for the interop proof program
use alloc::string::ToString;
use alloy_primitives::{Bytes, B256};
use alloy_primitives::B256;
use kona_preimage::{errors::PreimageOracleError, CommsClient, PreimageKey, PreimageKeyType};
use kona_proof::errors::OracleProviderError;
use kona_proof_interop::{BootInfo, HintType, PreState};
use kona_proof_interop::{HintType, PreState};

/// Reads the raw pre-state from the preimage oracle.
pub(crate) async fn read_raw_pre_state<O>(
caching_oracle: &O,
boot_info: &BootInfo,
) -> Result<Bytes, OracleProviderError>
where
O: CommsClient,
{
caching_oracle
.write(&HintType::AgreedPreState.encode_with(&[boot_info.agreed_pre_state.as_ref()]))
.await
.map_err(OracleProviderError::Preimage)?;
let pre = caching_oracle
.get(PreimageKey::new(*boot_info.agreed_pre_state, PreimageKeyType::Keccak256))
.await
.map_err(OracleProviderError::Preimage)?;

if pre.is_empty() {
return Err(OracleProviderError::Preimage(PreimageOracleError::Other(
"Invalid pre-state preimage".to_string(),
)));
}

Ok(Bytes::from(pre))
}

/// Fetches the safe head hash of the L2 chain based on the agreed upon L2 output root in the
/// [BootInfo].
/// Fetches the safe head hash of the L2 chain, using the active L2 chain in the [PreState].
pub(crate) async fn fetch_l2_safe_head_hash<O>(
caching_oracle: &O,
pre: &PreState,
Expand Down
35 changes: 18 additions & 17 deletions bin/client/src/single.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,13 @@ where
////////////////////////////////////////////////////////////////

let oracle = Arc::new(CachingOracle::new(ORACLE_LRU_SIZE, oracle_client, hint_client));
let boot = match BootInfo::load(oracle.as_ref()).await {
Ok(boot) => Arc::new(boot),
Err(e) => {
error!(target: "client", "Failed to load boot info: {:?}", e);
return Err(e.into());
}
};
let safe_head_hash = fetch_safe_head_hash(oracle.as_ref(), boot.as_ref()).await?;
let boot = BootInfo::load(oracle.as_ref()).await?;
let rollup_config = Arc::new(boot.rollup_config);
let safe_head_hash = fetch_safe_head_hash(oracle.as_ref(), boot.agreed_l2_output_root).await?;

let mut l1_provider = OracleL1ChainProvider::new(boot.l1_head, oracle.clone());
let mut l2_provider =
OracleL2ChainProvider::new(safe_head_hash, boot.rollup_config.clone(), oracle.clone());
OracleL2ChainProvider::new(safe_head_hash, rollup_config.clone(), oracle.clone());
let beacon = OracleBlobProvider::new(oracle.clone());

// Fetch the safe head's block header.
Expand Down Expand Up @@ -105,26 +100,32 @@ where

// Create a new derivation driver with the given boot information and oracle.
let cursor =
new_pipeline_cursor(&boot.rollup_config, safe_head, &mut l1_provider, &mut l2_provider)
new_pipeline_cursor(rollup_config.as_ref(), safe_head, &mut l1_provider, &mut l2_provider)
.await?;
l2_provider.set_cursor(cursor.clone());

let cfg = Arc::new(boot.rollup_config.clone());
let pipeline = OraclePipeline::new(
cfg.clone(),
rollup_config.clone(),
cursor.clone(),
oracle.clone(),
beacon,
l1_provider.clone(),
l2_provider.clone(),
);
let executor = KonaExecutor::new(&cfg, l2_provider.clone(), l2_provider, handle_register, None);
let executor = KonaExecutor::new(
rollup_config.as_ref(),
l2_provider.clone(),
l2_provider,
handle_register,
None,
);
let mut driver = Driver::new(cursor, executor, pipeline);

// Run the derivation pipeline until we are able to produce the output root of the claimed
// L2 block.
let (safe_head, output_root) =
driver.advance_to_target(&boot.rollup_config, Some(boot.claimed_l2_block_number)).await?;
let (safe_head, output_root) = driver
.advance_to_target(rollup_config.as_ref(), Some(boot.claimed_l2_block_number))
.await?;

////////////////////////////////////////////////////////////////
// EPILOGUE //
Expand Down Expand Up @@ -154,7 +155,7 @@ where
/// [BootInfo].
pub async fn fetch_safe_head_hash<O>(
caching_oracle: &O,
boot_info: &BootInfo,
agreed_l2_output_root: B256,
) -> Result<B256, OracleProviderError>
where
O: CommsClient,
Expand All @@ -163,7 +164,7 @@ where
HintType::StartingL2Output
.get_exact_preimage(
caching_oracle,
boot_info.agreed_l2_output_root,
agreed_l2_output_root,
PreimageKeyType::Keccak256,
&mut output_preimage,
)
Expand Down
2 changes: 1 addition & 1 deletion bin/host/src/interop/fetcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,7 @@ where
let mut l1_provider = OracleL1ChainProvider::new(l1_head, oracle.clone());
let mut l2_provider = OracleL2ChainProvider::new(
agreed_block_hash,
rollup_config.as_ref().clone(),
rollup_config.clone(),
oracle.clone(),
);
let beacon = OracleBlobProvider::new(oracle.clone());
Expand Down
14 changes: 6 additions & 8 deletions bin/host/src/interop/local_kv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ use anyhow::Result;
use kona_host::KeyValueStore;
use kona_preimage::PreimageKey;
use kona_proof_interop::boot::{
L1_HEAD_KEY, L2_AGREED_PRE_STATE_KEY, L2_CHAIN_ID_KEY, L2_CLAIMED_POST_STATE_KEY,
L2_CLAIMED_TIMESTAMP_KEY, L2_ROLLUP_CONFIG_KEY,
L1_HEAD_KEY, L2_AGREED_PRE_STATE_KEY, L2_CLAIMED_POST_STATE_KEY, L2_CLAIMED_TIMESTAMP_KEY,
L2_ROLLUP_CONFIG_KEY,
};
use maili_registry::HashMap;

/// The default chain ID to use if none is provided.
pub(crate) const DEFAULT_CHAIN_ID: u64 = 0xbeef_babe;
Expand Down Expand Up @@ -37,14 +38,11 @@ impl KeyValueStore for LocalKeyValueStore {
}
L2_CLAIMED_POST_STATE_KEY => Some(self.cfg.claimed_l2_post_state.to_vec()),
L2_CLAIMED_TIMESTAMP_KEY => Some(self.cfg.claimed_l2_timestamp.to_be_bytes().to_vec()),
L2_CHAIN_ID_KEY => Some(self.cfg.active_l2_chain_id().ok()?.to_be_bytes().to_vec()),
L2_ROLLUP_CONFIG_KEY => {
let rollup_configs = self.cfg.read_rollup_configs().ok()?;
let active_rollup_config = rollup_configs
.get(&self.cfg.active_l2_chain_id().ok()?)
.cloned()
.unwrap_or_default();
let serialized = serde_json::to_vec(&active_rollup_config).ok()?;
let serialized =
serde_json::to_vec(&rollup_configs.into_iter().collect::<HashMap<_, _>>())
.ok()?;
Some(serialized)
}
_ => None,
Expand Down
4 changes: 4 additions & 0 deletions crates/interop/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ op-alloy-consensus.workspace = true
# Arbitrary
arbitrary = { version = "1.4", features = ["derive"], optional = true }

# Serde
serde = { workspace = true, optional = true }

[dev-dependencies]
tokio = { workspace = true, features = ["full"] }
alloy-primitives = { workspace = true, features = ["rlp", "arbitrary"] }
Expand All @@ -35,3 +38,4 @@ rand.workspace = true

[features]
arbitrary = ["dep:arbitrary", "alloy-primitives/arbitrary"]
serde = ["dep:serde", "alloy-primitives/serde"]
Loading

0 comments on commit 9ca353f

Please sign in to comment.