Skip to content

Commit

Permalink
feat(workspace): kona-proof-interop crate (#902)
Browse files Browse the repository at this point in the history
  • Loading branch information
clabby authored Jan 14, 2025
1 parent e7bfe4d commit 0543084
Show file tree
Hide file tree
Showing 9 changed files with 554 additions and 0 deletions.
18 changes: 18 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ kona-client = { path = "bin/client", version = "0.1.0", default-features = false
kona-derive = { path = "crates/derive", version = "0.2.2", default-features = false }
kona-driver = { path = "crates/driver", version = "0.2.2", default-features = false }
kona-executor = { path = "crates/executor", version = "0.2.2", default-features = false }
kona-interop = { path = "crates/interop", version = "0.1.0", default-features = false }
kona-proof = { path = "crates/proof-sdk/proof", version = "0.2.2", default-features = false }
kona-proof-interop = { path = "crates/proof-sdk/proof-interop", version = "0.1.0", default-features = false }
kona-std-fpvm = { path = "crates/proof-sdk/std-fpvm", version = "0.1.2", default-features = false }
kona-preimage = { path = "crates/proof-sdk/preimage", version = "0.2.1", default-features = false }
kona-std-fpvm-proc = { path = "crates/proof-sdk/std-fpvm-proc", version = "0.1.2", default-features = false }
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ see the [SDK section of the book](https://op-rs.github.io/kona/sdk/intro.html).
**Proof SDK**

- [`kona-proof`](./crates/proof-sdk/proof): High level OP Stack state transition proof SDK.
- [`kona-proof-interop`](./crates/proof-sdk/proof-interop): Extension of `kona-proof` with interop support.
- [`preimage`](./crates/proof-sdk/preimage): High level interfaces to the [`PreimageOracle`][fpp-specs] ABI.
- [`std-fpvm`](./crates/proof-sdk/std-fpvm): Platform specific [Fault Proof VM][g-fault-proof-vm] kernel APIs.
- [`std-fpvm-proc`](./crates/proof-sdk/std-fpvm-proc): Proc macro for [Fault Proof Program][fpp-specs] entrypoints.
Expand Down
45 changes: 45 additions & 0 deletions crates/proof-sdk/proof-interop/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
[package]
name = "kona-proof-interop"
description = "OP Stack Proof SDK with Interop support"
version = "0.1.0"
edition.workspace = true
authors.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true

[lints]
workspace = true

[dependencies]
# Workspace
kona-preimage.workspace = true
kona-interop.workspace = true
kona-proof.workspace = true

# Maili
maili-registry.workspace = true

# Alloy
alloy-rlp.workspace = true
alloy-primitives.workspace = true

# Op Alloy
op-alloy-genesis = { workspace = true, features = ["serde"] }

# General
serde.workspace = true
tracing.workspace = true
serde_json.workspace = true

# Arbitrary
arbitrary = { version = "1.4", features = ["derive"], optional = true }

[dev-dependencies]
alloy-primitives = { workspace = true, features = ["rlp", "arbitrary"] }
kona-interop = { workspace = true, features = ["arbitrary"] }
arbitrary = { version = "1.4", features = ["derive"] }
rand.workspace = true

[features]
arbitrary = ["dep:arbitrary", "alloy-primitives/arbitrary", "kona-interop/arbitrary"]
8 changes: 8 additions & 0 deletions crates/proof-sdk/proof-interop/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# `kona-proof-interop`

<a href="https://github.com/op-rs/kona/actions/workflows/rust_ci.yaml"><img src="https://github.com/op-rs/kona/actions/workflows/rust_ci.yaml/badge.svg?label=ci" alt="CI"></a>
<a href="https://crates.io/crates/kona-proof-interop"><img src="https://img.shields.io/crates/v/kona-proof-interop.svg?label=kona-proof-interop&labelColor=2a2f35" alt="Kona Proof SDK"></a>
<a href="https://github.com/op-rs/kona/blob/main/LICENSE.md"><img src="https://img.shields.io/badge/License-MIT-d1d1f6.svg?label=license&labelColor=2a2f35" alt="License"></a>
<a href="https://img.shields.io/codecov/c/github/op-rs/kona"><img src="https://img.shields.io/codecov/c/github/op-rs/kona" alt="Codecov"></a>

`kona-proof-interop` is an OP Stack state transition proof SDK, with interop support, built on top of [`kona-proof`](../proof/)
123 changes: 123 additions & 0 deletions crates/proof-sdk/proof-interop/src/boot.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
//! This module contains the prologue phase of the client program, pulling in the boot information
//! through the `PreimageOracle` ABI as local keys.
use alloy_primitives::{B256, U256};
use kona_preimage::{PreimageKey, PreimageOracleClient};
use kona_proof::errors::OracleProviderError;
use maili_registry::ROLLUP_CONFIGS;
use op_alloy_genesis::RollupConfig;
use serde::{Deserialize, Serialize};
use tracing::warn;

/// The local key ident for the L1 head hash.
pub const L1_HEAD_KEY: U256 = U256::from_be_slice(&[1]);

/// The local key ident for the agreed upon L2 pre-state claim.
pub const L2_AGREED_PRE_STATE_KEY: U256 = U256::from_be_slice(&[2]);

/// The local key ident for the L2 post-state claim.
pub const L2_CLAIMED_POST_STATE_KEY: U256 = U256::from_be_slice(&[3]);

/// The local key ident for the L2 claim timestamp.
pub const L2_CLAIM_TIMESTAMP_KEY: U256 = U256::from_be_slice(&[4]);

/// The local key ident for the L2 chain ID.
pub const L2_CHAIN_ID_KEY: U256 = U256::from_be_slice(&[5]);

/// The local key ident for the L2 rollup config.
pub const L2_ROLLUP_CONFIG_KEY: U256 = U256::from_be_slice(&[6]);

/// The boot information for the interop client program.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BootInfo {
/// The L1 head hash containing the safe L2 chain data that may reproduce the post-state claim.
pub l1_head: B256,
/// The agreed upon superchain pre-state commitment.
pub agreed_pre_state: B256,
/// The claimed (disputed) superchain post-state commitment.
pub claimed_post_state: B256,
/// The L2 claim timestamp.
pub claimed_l2_timestamp: u64,
/// The L2 chain ID.
pub chain_id: u64,
/// The rollup config for the L2 chain.
pub rollup_config: RollupConfig,
}

impl BootInfo {
/// Load the boot information from the preimage oracle.
///
/// ## Takes
/// - `oracle`: The preimage oracle reader.
///
/// ## Returns
/// - `Ok(BootInfo)`: The boot information.
/// - `Err(_)`: Failed to load the boot information.
pub async fn load<O>(oracle: &O) -> Result<Self, OracleProviderError>
where
O: PreimageOracleClient + Send,
{
let mut l1_head: B256 = B256::ZERO;
oracle
.get_exact(PreimageKey::new_local(L1_HEAD_KEY.to()), l1_head.as_mut())
.await
.map_err(OracleProviderError::Preimage)?;

let mut l2_pre: B256 = B256::ZERO;
oracle
.get_exact(PreimageKey::new_local(L2_AGREED_PRE_STATE_KEY.to()), l2_pre.as_mut())
.await
.map_err(OracleProviderError::Preimage)?;

let mut l2_post: B256 = B256::ZERO;
oracle
.get_exact(PreimageKey::new_local(L2_CLAIMED_POST_STATE_KEY.to()), l2_post.as_mut())
.await
.map_err(OracleProviderError::Preimage)?;

let l2_claim_block = u64::from_be_bytes(
oracle
.get(PreimageKey::new_local(L2_CLAIM_TIMESTAMP_KEY.to()))
.await
.map_err(OracleProviderError::Preimage)?
.as_slice()
.try_into()
.map_err(OracleProviderError::SliceConversion)?,
);
let chain_id = u64::from_be_bytes(
oracle
.get(PreimageKey::new_local(L2_CHAIN_ID_KEY.to()))
.await
.map_err(OracleProviderError::Preimage)?
.as_slice()
.try_into()
.map_err(OracleProviderError::SliceConversion)?,
);

// Attempt to load the rollup config from the chain ID. If there is no config for the chain,
// fall back to loading the config from the preimage oracle.
let rollup_config = if let Some(config) = ROLLUP_CONFIGS.get(&chain_id) {
config.clone()
} else {
warn!(
target: "boot-loader",
"No rollup config found for chain ID {}, falling back to preimage oracle. This is insecure in production without additional validation!",
chain_id
);
let ser_cfg = oracle
.get(PreimageKey::new_local(L2_ROLLUP_CONFIG_KEY.to()))
.await
.map_err(OracleProviderError::Preimage)?;
serde_json::from_slice(&ser_cfg).map_err(OracleProviderError::Serde)?
};

Ok(Self {
l1_head,
agreed_pre_state: l2_pre,
claimed_post_state: l2_post,
claimed_l2_timestamp: l2_claim_block,
chain_id,
rollup_config,
})
}
}
138 changes: 138 additions & 0 deletions crates/proof-sdk/proof-interop/src/hint.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//! This module contains the [HintType] enum.
use alloc::{
string::{String, ToString},
vec::Vec,
};
use alloy_primitives::{hex, Bytes};
use core::fmt::Display;
use kona_proof::errors::HintParsingError;

/// A [Hint] is parsed in the format `<hint_type> <hint_data>`, where `<hint_type>` is a string that
/// represents the type of hint, and `<hint_data>` is the data associated with the hint (bytes
/// encoded as hex UTF-8).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Hint {
/// The type of hint.
pub hint_type: HintType,
/// The data associated with the hint.
pub hint_data: Bytes,
}

impl Hint {
/// Parses a hint from a string.
pub fn parse(s: &str) -> Result<Self, HintParsingError> {
let mut parts = s.split(' ').collect::<Vec<_>>();

if parts.len() != 2 {
return Err(HintParsingError(alloc::format!("Invalid hint format: {}", s)));
}

let hint_type = HintType::try_from(parts.remove(0))?;
let hint_data =
hex::decode(parts.remove(0)).map_err(|e| HintParsingError(e.to_string()))?.into();

Ok(Self { hint_type, hint_data })
}

/// Splits the [Hint] into its components.
pub fn split(self) -> (HintType, Bytes) {
(self.hint_type, self.hint_data)
}
}

/// The [HintType] enum is used to specify the type of hint that was received.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum HintType {
/// A hint that specifies the block header of a layer 1 block.
L1BlockHeader,
/// A hint that specifies the transactions of a layer 1 block.
L1Transactions,
/// A hint that specifies the state node of a layer 1 block.
L1Receipts,
/// A hint that specifies a blob in the layer 1 beacon chain.
L1Blob,
/// A hint that specifies a precompile call on layer 1.
L1Precompile,
/// A hint that specifies the block header of a layer 2 block.
L2BlockHeader,
/// A hint that specifies the transactions of a layer 2 block.
L2Transactions,
/// A hint that specifies the code of a contract on layer 2.
L2Code,
/// A hint that specifies the preimage of the agreed upon pre-state claim.
AgreedPreState,
/// A hint that specifies the preimage of an L2 output root within the agreed upon pre-state,
/// by chain ID.
L2OutputRoot,
/// A hint that specifies the state node in the L2 state trie.
L2StateNode,
/// A hint that specifies the proof on the path to an account in the L2 state trie.
L2AccountProof,
/// A hint that specifies the proof on the path to a storage slot in an account within in the
/// L2 state trie.
L2AccountStorageProof,
/// A hint that specifies bulk storage of all the code, state and keys generated by an
/// execution witness.
L2PayloadWitness,
}

impl HintType {
/// Encodes the hint type as a string.
pub fn encode_with(&self, data: &[&[u8]]) -> String {
let concatenated = hex::encode(data.iter().copied().flatten().copied().collect::<Vec<_>>());
alloc::format!("{} {}", self, concatenated)
}
}

impl TryFrom<&str> for HintType {
type Error = HintParsingError;

fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
"l1-block-header" => Ok(Self::L1BlockHeader),
"l1-transactions" => Ok(Self::L1Transactions),
"l1-receipts" => Ok(Self::L1Receipts),
"l1-blob" => Ok(Self::L1Blob),
"l1-precompile" => Ok(Self::L1Precompile),
"l2-block-header" => Ok(Self::L2BlockHeader),
"l2-transactions" => Ok(Self::L2Transactions),
"l2-code" => Ok(Self::L2Code),
"agreed-pre-state" => Ok(Self::AgreedPreState),
"l2-output-root" => Ok(Self::L2OutputRoot),
"l2-state-node" => Ok(Self::L2StateNode),
"l2-account-proof" => Ok(Self::L2AccountProof),
"l2-account-storage-proof" => Ok(Self::L2AccountStorageProof),
"l2-payload-witness" => Ok(Self::L2PayloadWitness),
_ => Err(HintParsingError(value.to_string())),
}
}
}

impl From<HintType> for &str {
fn from(value: HintType) -> Self {
match value {
HintType::L1BlockHeader => "l1-block-header",
HintType::L1Transactions => "l1-transactions",
HintType::L1Receipts => "l1-receipts",
HintType::L1Blob => "l1-blob",
HintType::L1Precompile => "l1-precompile",
HintType::L2BlockHeader => "l2-block-header",
HintType::L2Transactions => "l2-transactions",
HintType::L2Code => "l2-code",
HintType::AgreedPreState => "agreed-pre-state",
HintType::L2OutputRoot => "l2-output-root",
HintType::L2StateNode => "l2-state-node",
HintType::L2AccountProof => "l2-account-proof",
HintType::L2AccountStorageProof => "l2-account-storage-proof",
HintType::L2PayloadWitness => "l2-payload-witness",
}
}
}

impl Display for HintType {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let s: &str = (*self).into();
write!(f, "{}", s)
}
}
16 changes: 16 additions & 0 deletions crates/proof-sdk/proof-interop/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#![doc = include_str!("../README.md")]
#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)]
#![deny(unused_must_use, rust_2018_idioms)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![cfg_attr(not(any(test, feature = "arbitrary")), no_std)]

extern crate alloc;

pub mod pre_state;

mod hint;
pub use hint::{Hint, HintType};

pub mod boot;
pub use boot::BootInfo;
Loading

0 comments on commit 0543084

Please sign in to comment.