Skip to content
Open
1 change: 1 addition & 0 deletions rust/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 rust/kona/bin/host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ kona-preimage = { workspace = true, features = ["std"] }
# Protocol
kona-driver.workspace = true
kona-derive.workspace = true
kona-interop = { workspace = true, features = ["serde"] }
kona-registry.workspace = true
kona-protocol = { workspace = true, features = ["std", "serde"] }
kona-genesis = { workspace = true, features = ["std", "serde"] }
Expand Down
16 changes: 16 additions & 0 deletions rust/kona/bin/host/src/interop/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use alloy_provider::{Provider, RootProvider};
use clap::Parser;
use kona_cli::cli_styles;
use kona_genesis::{L1ChainConfig, RollupConfig};
use kona_interop::DependencySet;
use kona_preimage::{
BidirectionalChannel, Channel, HintReader, HintWriter, OracleReader, OracleServer,
};
Expand Down Expand Up @@ -96,6 +97,10 @@ pub struct InteropHost {
/// The l1 config should be stored as serde-JSON serialized files.
#[arg(long, alias = "l1-cfg")]
pub l1_config_path: Option<PathBuf>,
/// Path to the dependency set config. If provided, the host will use this config for the
/// dependency set. The config should be stored as a serde-JSON serialized file.
#[arg(long, alias = "depset-cfg", env)]
pub dependency_set_path: Option<PathBuf>,
/// Optionally enables the use of `debug_executePayload` to collect the execution witness from
/// the execution layer.
#[arg(long, env)]
Expand Down Expand Up @@ -251,6 +256,17 @@ impl InteropHost {
.map_err(|_| InteropHostError::Other("failed to parse L1 config"))
}

/// Reads the [`DependencySet`] from the file system.
pub fn read_dependency_set(&self) -> Option<Result<DependencySet, InteropHostError>> {
let path = self.dependency_set_path.as_ref()?;

Some((|| {
let ser_config = std::fs::read_to_string(path)?;
let dep_set: DependencySet = serde_json::from_str(&ser_config)?;
Ok(dep_set)
})())
}

/// Creates the key-value store for the host backend.
fn create_key_value_store(&self) -> Result<SharedKeyValueStore, InteropHostError> {
let local_kv_store = InteropLocalInputs::new(self.clone());
Expand Down
15 changes: 13 additions & 2 deletions rust/kona/bin/host/src/interop/local_kv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
use super::InteropHost;
use crate::{KeyValueStore, Result};
use alloy_primitives::{B256, keccak256};
use kona_interop::DependencySet;
use kona_preimage::PreimageKey;
use kona_proof_interop::boot::{
L1_CONFIG_KEY, L1_HEAD_KEY, L2_AGREED_PRE_STATE_KEY, L2_CLAIMED_POST_STATE_KEY,
L2_CLAIMED_TIMESTAMP_KEY, L2_ROLLUP_CONFIG_KEY,
DEPENDENCY_SET_KEY, L1_CONFIG_KEY, L1_HEAD_KEY, L2_AGREED_PRE_STATE_KEY,
L2_CLAIMED_POST_STATE_KEY, L2_CLAIMED_TIMESTAMP_KEY, L2_ROLLUP_CONFIG_KEY,
};

/// A simple, synchronous key-value store that returns data from a [`InteropHost`] config.
Expand Down Expand Up @@ -41,6 +42,16 @@ impl KeyValueStore for InteropLocalInputs {
let l1_config = self.cfg.read_l1_config().ok()?;
serde_json::to_vec(&l1_config).ok()
}
DEPENDENCY_SET_KEY => {
let dependency_set =
self.cfg.read_dependency_set().and_then(|r| r.ok()).unwrap_or_else(|| {
DependencySet {
dependencies: Default::default(),
override_message_expiry_window: None,
}
});
serde_json::to_vec(&dependency_set).ok()
}
_ => None,
}
}
Expand Down
16 changes: 16 additions & 0 deletions rust/kona/crates/proof/proof-interop/src/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use alloc::{string::ToString, vec::Vec};
use alloy_primitives::{B256, Bytes, U256};
use alloy_rlp::Decodable;
use kona_genesis::{L1ChainConfig, RollupConfig};
use kona_interop::DependencySet;
use kona_preimage::{
CommsClient, HintWriterClient, PreimageKey, PreimageKeyType, PreimageOracleClient,
errors::PreimageOracleError,
Expand Down Expand Up @@ -34,6 +35,9 @@ pub const L2_ROLLUP_CONFIG_KEY: U256 = U256::from_be_slice(&[6]);
/// The local key ident for the l1 config.
pub const L1_CONFIG_KEY: U256 = U256::from_be_slice(&[7]);

/// The local key ident for the dependency set.
pub const DEPENDENCY_SET_KEY: U256 = U256::from_be_slice(&[8]);

/// The boot information for the interop client program.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BootInfo {
Expand All @@ -49,6 +53,8 @@ pub struct BootInfo {
pub claimed_l2_timestamp: u64,
/// The rollup config for the L2 chain.
pub rollup_configs: HashMap<u64, RollupConfig>,
/// The dependency set configuration for the interop cluster.
pub dependency_set: DependencySet,
/// The L1 config for the L2 chain.
pub l1_config: L1ChainConfig,
}
Expand Down Expand Up @@ -139,6 +145,15 @@ impl BootInfo {
serde_json::from_slice(&ser_cfg).map_err(OracleProviderError::Serde)?
};

// Load the dependency set configuration from the preimage oracle.
let dependency_set: DependencySet = {
let ser_cfg = oracle
.get(PreimageKey::new_local(DEPENDENCY_SET_KEY.to()))
.await
.map_err(OracleProviderError::Preimage)?;
serde_json::from_slice(&ser_cfg).map_err(OracleProviderError::Serde)?
};
Comment on lines +148 to +155
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, there's a definite chance that kona-host doesn't support this yet which is why we're hitting a 2 hour timeout on tests (loading from the preimage oracle just retries forever because there is no good way out if you can't get a preimage you need).


// Attempt to load the l1 config from the chain ID. If there is no config for the chain,
// fall back to loading the config from the preimage oracle.

Expand Down Expand Up @@ -170,6 +185,7 @@ impl BootInfo {
l1_head,
l1_config,
rollup_configs,
dependency_set,
agreed_pre_state_commitment: l2_pre,
agreed_pre_state,
claimed_post_state: l2_post,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ where
self.interop_provider.local_safe_heads(),
&self.interop_provider,
&self.boot_info.rollup_configs,
self.boot_info.dependency_set.get_message_expiry_window(),
)
.await?;

Expand Down
6 changes: 5 additions & 1 deletion rust/kona/crates/proof/proof-interop/src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ mod tests {
use alloy_rlp::Decodable;
use async_trait::async_trait;
use kona_genesis::RollupConfig;
use kona_interop::SuperRoot;
use kona_interop::{DependencySet, SuperRoot};
use kona_preimage::{
HintWriterClient, PreimageKey, PreimageKeyType, PreimageOracleClient,
errors::PreimageOracleResult,
Expand Down Expand Up @@ -430,6 +430,10 @@ mod tests {
claimed_post_state: B256::ZERO,
claimed_l2_timestamp: 0,
rollup_configs,
dependency_set: DependencySet {
dependencies: Default::default(),
override_message_expiry_window: None,
},
l1_config: Default::default(),
};

Expand Down
108 changes: 92 additions & 16 deletions rust/kona/crates/protocol/interop/src/graph.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Interop [`MessageGraph`].

use crate::{
MESSAGE_EXPIRY_WINDOW, RawMessagePayload,
RawMessagePayload,
errors::{MessageGraphError, MessageGraphResult},
message::{EnrichedExecutingMessage, extract_executing_messages},
traits::InteropProvider,
Expand Down Expand Up @@ -36,6 +36,8 @@ pub struct MessageGraph<'a, P> {
provider: &'a P,
/// Backup rollup configs for each chain.
rollup_configs: &'a HashMap<u64, RollupConfig>,
/// The message expiry window (in seconds) for validating initiating message timestamps.
message_expiry_window: u64,
}

impl<'a, P> MessageGraph<'a, P>
Expand All @@ -50,6 +52,7 @@ where
blocks: &HashMap<u64, Sealed<Header>>,
provider: &'a P,
rollup_configs: &'a HashMap<u64, RollupConfig>,
message_expiry_window: u64,
) -> MessageGraphResult<Self, P> {
info!(
target: "message_graph",
Expand All @@ -73,7 +76,7 @@ where
num_messages = messages.len(),
"Derived message graph successfully",
);
Ok(Self { messages, provider, rollup_configs })
Ok(Self { messages, provider, rollup_configs, message_expiry_window })
}

/// Checks the validity of all messages within the graph.
Expand Down Expand Up @@ -177,7 +180,8 @@ where
// Message expiry invariant: The timestamp of the initiating message must be no more than
// `MESSAGE_EXPIRY_WINDOW` seconds in the past, relative to the timestamp of the executing
// message.
if initiating_timestamp < message.executing_timestamp.saturating_sub(MESSAGE_EXPIRY_WINDOW)
if initiating_timestamp <
message.executing_timestamp.saturating_sub(self.message_expiry_window)
{
return Err(MessageGraphError::MessageExpired {
initiating_timestamp,
Expand Down Expand Up @@ -245,9 +249,9 @@ where

#[cfg(test)]
mod test {
use super::{MESSAGE_EXPIRY_WINDOW, MessageGraph};
use super::MessageGraph;
use crate::{
MessageGraphError,
MESSAGE_EXPIRY_WINDOW, MessageGraphError,
test_util::{ExecutingMessageBuilder, SuperchainBuilder},
};
use alloy_primitives::{Address, hex, keccak256};
Expand Down Expand Up @@ -291,7 +295,8 @@ mod test {

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

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

Expand Down Expand Up @@ -323,7 +328,8 @@ mod test {

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

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

Expand All @@ -343,7 +349,8 @@ mod test {

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

let graph = MessageGraph::derive(&headers, &provider, &cfgs).await.unwrap();
let graph =
MessageGraph::derive(&headers, &provider, &cfgs, MESSAGE_EXPIRY_WINDOW).await.unwrap();
let MessageGraphError::InvalidMessages(invalid_messages) =
graph.resolve().await.unwrap_err()
else {
Expand Down Expand Up @@ -376,7 +383,8 @@ mod test {

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

let graph = MessageGraph::derive(&headers, &provider, &cfgs).await.unwrap();
let graph =
MessageGraph::derive(&headers, &provider, &cfgs, MESSAGE_EXPIRY_WINDOW).await.unwrap();
let MessageGraphError::InvalidMessages(invalid_messages) =
graph.resolve().await.unwrap_err()
else {
Expand Down Expand Up @@ -414,7 +422,8 @@ mod test {

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

let graph = MessageGraph::derive(&headers, &provider, &cfgs).await.unwrap();
let graph =
MessageGraph::derive(&headers, &provider, &cfgs, MESSAGE_EXPIRY_WINDOW).await.unwrap();
let MessageGraphError::InvalidMessages(invalid_messages) =
graph.resolve().await.unwrap_err()
else {
Expand Down Expand Up @@ -450,7 +459,8 @@ mod test {

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

let graph = MessageGraph::derive(&headers, &provider, &cfgs).await.unwrap();
let graph =
MessageGraph::derive(&headers, &provider, &cfgs, MESSAGE_EXPIRY_WINDOW).await.unwrap();
let MessageGraphError::InvalidMessages(invalid_messages) =
graph.resolve().await.unwrap_err()
else {
Expand Down Expand Up @@ -483,7 +493,8 @@ mod test {

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

let graph = MessageGraph::derive(&headers, &provider, &cfgs).await.unwrap();
let graph =
MessageGraph::derive(&headers, &provider, &cfgs, MESSAGE_EXPIRY_WINDOW).await.unwrap();
let MessageGraphError::InvalidMessages(invalid_messages) =
graph.resolve().await.unwrap_err()
else {
Expand Down Expand Up @@ -515,7 +526,8 @@ mod test {

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

let graph = MessageGraph::derive(&headers, &provider, &cfgs).await.unwrap();
let graph =
MessageGraph::derive(&headers, &provider, &cfgs, MESSAGE_EXPIRY_WINDOW).await.unwrap();
let MessageGraphError::InvalidMessages(invalid_messages) =
graph.resolve().await.unwrap_err()
else {
Expand Down Expand Up @@ -550,7 +562,8 @@ mod test {

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

let graph = MessageGraph::derive(&headers, &provider, &cfgs).await.unwrap();
let graph =
MessageGraph::derive(&headers, &provider, &cfgs, MESSAGE_EXPIRY_WINDOW).await.unwrap();
let MessageGraphError::InvalidMessages(invalid_messages) =
graph.resolve().await.unwrap_err()
else {
Expand Down Expand Up @@ -584,7 +597,8 @@ mod test {

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

let graph = MessageGraph::derive(&headers, &provider, &cfgs).await.unwrap();
let graph =
MessageGraph::derive(&headers, &provider, &cfgs, MESSAGE_EXPIRY_WINDOW).await.unwrap();
let MessageGraphError::InvalidMessages(invalid_messages) =
graph.resolve().await.unwrap_err()
else {
Expand Down Expand Up @@ -617,7 +631,8 @@ mod test {

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

let graph = MessageGraph::derive(&headers, &provider, &cfgs).await.unwrap();
let graph =
MessageGraph::derive(&headers, &provider, &cfgs, MESSAGE_EXPIRY_WINDOW).await.unwrap();
let MessageGraphError::InvalidMessages(invalid_messages) =
graph.resolve().await.unwrap_err()
else {
Expand All @@ -633,4 +648,65 @@ mod test {
}
);
}

#[tokio::test]
async fn test_derive_and_resolve_graph_message_expired_custom_window() {
let mut superchain = default_superchain();
const CUSTOM_EXPIRY: u64 = 10;

let chain_a_time = superchain.chain(CHAIN_A_ID).header.timestamp;

superchain.chain(CHAIN_A_ID).add_initiating_message(MOCK_MESSAGE.into());
superchain
.chain(CHAIN_B_ID)
.with_timestamp(chain_a_time + CUSTOM_EXPIRY + 1)
.add_executing_message(
ExecutingMessageBuilder::default()
.with_message_hash(keccak256(MOCK_MESSAGE))
.with_origin_chain_id(CHAIN_A_ID)
.with_origin_timestamp(chain_a_time),
);

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

let graph = MessageGraph::derive(&headers, &provider, &cfgs, CUSTOM_EXPIRY).await.unwrap();
let MessageGraphError::InvalidMessages(invalid_messages) =
graph.resolve().await.unwrap_err()
else {
panic!("Expected invalid messages")
};

assert_eq!(invalid_messages.len(), 1);
assert_eq!(
*invalid_messages.get(&CHAIN_B_ID).unwrap(),
MessageGraphError::MessageExpired {
initiating_timestamp: chain_a_time,
executing_timestamp: chain_a_time + CUSTOM_EXPIRY + 1
}
);
}

#[tokio::test]
async fn test_derive_and_resolve_graph_message_not_expired_within_custom_window() {
let mut superchain = default_superchain();
const CUSTOM_EXPIRY: u64 = 10;

let chain_a_time = superchain.chain(CHAIN_A_ID).header.timestamp;

superchain.chain(CHAIN_A_ID).add_initiating_message(MOCK_MESSAGE.into());
superchain
.chain(CHAIN_B_ID)
.with_timestamp(chain_a_time + CUSTOM_EXPIRY - 1)
.add_executing_message(
ExecutingMessageBuilder::default()
.with_message_hash(keccak256(MOCK_MESSAGE))
.with_origin_chain_id(CHAIN_A_ID)
.with_origin_timestamp(chain_a_time),
);

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

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