Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(workspace): kona-proof-interop crate #902

Merged
merged 3 commits into from
Jan 14, 2025
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
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
Loading