Skip to content
This repository was archived by the owner on Jan 16, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 12 additions & 16 deletions bin/client/src/interop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use kona_executor::{ExecutorError, KonaHandleRegister};
use kona_preimage::{HintWriterClient, PreimageOracleClient};
use kona_proof::{errors::OracleProviderError, l2::OracleL2ChainProvider, CachingOracle};
use kona_proof_interop::{
BootInfo, ConsolidationError, PreState, INVALID_TRANSITION_HASH, TRANSITION_STATE_MAX_STEPS,
boot::BootstrapError, BootInfo, ConsolidationError, PreState, TRANSITION_STATE_MAX_STEPS,
};
use thiserror::Error;
use tracing::{error, info};
Expand All @@ -31,18 +31,18 @@ pub enum FaultProofProgramError {
/// An error occurred in the driver.
#[error(transparent)]
Driver(#[from] DriverError<ExecutorError>),
/// An error occurred during RLP decoding.
#[error("RLP decoding error: {0}")]
Rlp(alloy_rlp::Error),
/// Consolidation error.
#[error(transparent)]
Consolidation(#[from] ConsolidationError),
/// Bootstrap error
#[error(transparent)]
Bootstrap(#[from] BootstrapError),
/// State transition failed.
#[error("Critical state transition failure")]
StateTransitionFailed,
/// Missing a rollup configuration.
#[error("Missing rollup configuration for chain ID {0}")]
MissingRollupConfig(u64),
/// Consolidation error.
#[error(transparent)]
Consolidation(#[from] ConsolidationError),
}

/// Executes the interop fault proof program with the given [PreimageOracleClient] and
Expand All @@ -68,20 +68,16 @@ 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) => boot,
Err(BootstrapError::NoOpTransition) => {
info!(target: "client_interop", "No-op transition, short-circuiting.");
return Ok(());
}
Err(e) => {
error!(target: "client_interop", "Failed to load boot info: {:?}", e);
error!(target: "client_interop", "Failed to load boot info: {}", e);
return Err(e.into());
}
};

// If the pre state is invalid, short-circuit and check if the post-state claim is also invalid.
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.");
return Ok(());
}

// Load in the agreed pre-state from the preimage oracle in order to determine the active
// sub-problem.
match boot.agreed_pre_state {
Expand Down
2 changes: 1 addition & 1 deletion bin/host/src/interop/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ impl InteropHost {
let preimage = BidirectionalChannel::new()?;

let server_task = self.start_server(hint.host, preimage.host).await?;
let client_task = task::spawn(kona_client::single::run(
let client_task = task::spawn(kona_client::interop::run(
OracleReader::new(preimage.client),
HintWriter::new(hint.client),
None,
Expand Down
2 changes: 1 addition & 1 deletion bin/host/src/interop/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ impl HintHandler for InteropHintHandler {

let block_number = u64::from_be_bytes(hint.data.as_ref()[..8].try_into()?);
let address = Address::from_slice(&hint.data.as_ref()[8..28]);
let slot = B256::from_slice(&hint.data.as_ref()[28..]);
let slot = B256::from_slice(&hint.data.as_ref()[28..60]);
let chain_id = u64::from_be_bytes(hint.data[60..].try_into()?);

let mut proof_response = providers
Expand Down
36 changes: 32 additions & 4 deletions crates/proof-sdk/proof-interop/src/boot.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! This module contains the prologue phase of the client program, pulling in the boot information
//! through the `PreimageOracle` ABI as local keys.

use crate::{HintType, PreState};
use crate::{HintType, PreState, INVALID_TRANSITION, INVALID_TRANSITION_HASH};
use alloc::{string::ToString, vec::Vec};
use alloy_primitives::{Bytes, B256, U256};
use alloy_rlp::Decodable;
Expand All @@ -13,6 +13,7 @@ use kona_proof::errors::OracleProviderError;
use maili_genesis::RollupConfig;
use maili_registry::{HashMap, ROLLUP_CONFIGS};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use tracing::warn;

/// The local key ident for the L1 head hash.
Expand Down Expand Up @@ -56,7 +57,7 @@ impl BootInfo {
/// ## Returns
/// - `Ok(BootInfo)`: The boot information.
/// - `Err(_)`: Failed to load the boot information.
pub async fn load<O>(oracle: &O) -> Result<Self, OracleProviderError>
pub async fn load<O>(oracle: &O) -> Result<Self, BootstrapError>
where
O: PreimageOracleClient + HintWriterClient + Clone + Send,
{
Expand Down Expand Up @@ -88,9 +89,22 @@ impl BootInfo {
.map_err(OracleProviderError::SliceConversion)?,
);

let raw_pre_state = read_raw_pre_state(oracle, l2_pre).await?;
if raw_pre_state == INVALID_TRANSITION {
warn!(
target: "boot-loader",
"Invalid pre-state, short-circuiting to check post-state claim."
);

if l2_post == INVALID_TRANSITION_HASH {
return Err(BootstrapError::NoOpTransition);
} else {
return Err(BootstrapError::InvalidPostState(l2_post));
}
}

let agreed_pre_state =
PreState::decode(&mut read_raw_pre_state(oracle, l2_pre).await?.as_ref())
.map_err(OracleProviderError::Rlp)?;
PreState::decode(&mut raw_pre_state.as_ref()).map_err(OracleProviderError::Rlp)?;

let chain_ids: Vec<_> = match agreed_pre_state {
PreState::SuperRoot(ref super_root) => {
Expand Down Expand Up @@ -135,6 +149,20 @@ impl BootInfo {
}
}

/// An error that occurred during the bootstrapping phase.
#[derive(Debug, Error)]
pub enum BootstrapError {
/// An error occurred while reading from the preimage oracle.
#[error(transparent)]
Oracle(#[from] OracleProviderError),
/// The pre-state is invalid and the post-state claim is not invalid.
#[error("`INVALID` pre-state claim; Post-state {0} unexpected.")]
InvalidPostState(B256),
/// The pre-state is invalid and the post-state claim is also invalid.
#[error("No-op state transition detected; both pre and post states are `INVALID`.")]
NoOpTransition,
}

/// Reads the raw pre-state from the preimage oracle.
pub(crate) async fn read_raw_pre_state<O>(
caching_oracle: &O,
Expand Down
3 changes: 2 additions & 1 deletion crates/proof-sdk/proof-interop/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ extern crate alloc;

mod pre_state;
pub use pre_state::{
OptimisticBlock, PreState, TransitionState, INVALID_TRANSITION_HASH, TRANSITION_STATE_MAX_STEPS,
OptimisticBlock, PreState, TransitionState, INVALID_TRANSITION, INVALID_TRANSITION_HASH,
TRANSITION_STATE_MAX_STEPS,
};

mod hint;
Expand Down
3 changes: 3 additions & 0 deletions crates/proof-sdk/proof-interop/src/pre_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ pub(crate) const TRANSITION_STATE_VERSION: u8 = 255;
/// The maximum number of steps allowed in a [TransitionState].
pub const TRANSITION_STATE_MAX_STEPS: u64 = 2u64.pow(10) - 1;

/// The [Bytes] representation of the string "invalid".
pub const INVALID_TRANSITION: Bytes = Bytes::from_static(b"invalid");

/// `keccak256("invalid")`
pub const INVALID_TRANSITION_HASH: B256 =
b256!("ffd7db0f9d5cdeb49c4c9eba649d4dc6d852d64671e65488e57f58584992ac68");
Expand Down
4 changes: 2 additions & 2 deletions crates/proof-sdk/proof/src/hint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ where
}

let mut hint_data = Vec::with_capacity(self.data.len() + data.as_ref().len());
hint_data[..self.data.len()].copy_from_slice(self.data.as_ref());
hint_data[self.data.len()..].copy_from_slice(data.as_ref());
hint_data.extend_from_slice(self.data.as_ref());
hint_data.extend_from_slice(data.as_ref());

Self { data: hint_data.into(), ..self }
}
Expand Down