Skip to content
Draft
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
2 changes: 2 additions & 0 deletions Cargo.lock

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

12 changes: 6 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ resolver = "2"
members = [
"bin/*",
"crates/*",
"example/preloader",
"example/preloader",
"canoe/bindings",
"canoe/provider",
"canoe/steel/apps",
"canoe/steel/methods",
"canoe/steel/verifier",
"canoe/steel/verifier",
"canoe/sp1-cc/host",
"canoe/sp1-cc/client",
"canoe/sp1-cc/vkey-bin",
Expand Down Expand Up @@ -55,12 +55,12 @@ hokulea-zkvm-verification = { path = "crates/zkvm-verification", version = "0.1.
eigenda-cert = { path = "crates/eigenda-cert" }

# Alloy (Network)
alloy-rlp = { version = "0.3.12", default-features = false }
alloy-provider = { version = "1.0.38", default-features = false }
alloy-consensus = { version = "1.0.38", default-features = false }
alloy-rpc-types = { version = "1.0.38", default-features = false }
alloy-genesis = { version = "1.0.38", default-features = false }
alloy-primitives = { version = "1.3.1", default-features = false }
alloy-provider = { version = "1.0.38", default-features = false }
alloy-rlp = { version = "0.3.12", default-features = false }
alloy-rpc-types = { version = "1.0.38", default-features = false }
alloy-sol-types = { version = "1.3.1", default-features = false }

# Execution
Expand Down Expand Up @@ -137,7 +137,7 @@ sp1-core-executor = "5.2.1"
sp1-prover = "5.2.1"
sp1-cc-client-executor = { git = "https://github.com/succinctlabs/sp1-contract-call.git", tag = "reth-1.9.1" }
sp1-cc-host-executor = { git = "https://github.com/succinctlabs/sp1-contract-call.git", tag = "reth-1.9.1" }
rsp-primitives = { git = "https://github.com/succinctlabs/rsp", tag = "reth-1.9.1" }
rsp-primitives = { git = "https://github.com/succinctlabs/rsp", tag = "reth-1.9.1" }

# ZKVM deps
hex = "0.4"
Expand Down
5 changes: 3 additions & 2 deletions canoe/sp1-cc/host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ sp1-cc-host-executor = { workspace = true }
sp1-cc-client-executor = { workspace = true }
rsp-primitives = { workspace = true }

alloy-genesis.workspace = true
alloy-primitives.workspace = true
alloy-sol-types = { workspace = true }
alloy-sol-types.workspace = true
bincode.workspace = true

anyhow = { workspace = true }
Expand All @@ -25,4 +26,4 @@ tracing.workspace = true
# sp1
sp1-sdk.workspace = true
sp1-core-executor.workspace = true
sp1-prover.workspace = true
sp1-prover.workspace = true
87 changes: 53 additions & 34 deletions canoe/sp1-cc/host/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use alloy_genesis::ChainConfig;
use alloy_primitives::Address;
use alloy_rpc_types::BlockNumberOrTag;
use alloy_sol_types::SolType;
use anyhow::Result;
use async_trait::async_trait;
use canoe_bindings::{Journal, StatusCode};
use canoe_provider::{CanoeInput, CanoeProvider, CertVerifierCall};
use rsp_primitives::genesis::genesis_from_json;
use sp1_cc_client_executor::ContractInput;
use sp1_cc_host_executor::{EvmSketch, Genesis};
use sp1_sdk::{
Expand All @@ -19,9 +21,7 @@ use std::{
use tracing::{debug, info, warn};
use url::Url;

use rsp_primitives::genesis::genesis_from_json;

/// The ELF we want to execute inside the zkVM.
/// The default ELF we want to execute inside the zkVM.
pub const ELF: &[u8] = include_bytes!("../../elf/canoe-sp1-cc-client");

const DEFAULT_NETWORK_PRIVATE_KEY: &str =
Expand Down Expand Up @@ -62,6 +62,10 @@ pub struct CanoeSp1CCProvider {
pub eth_rpc_url: String,
/// if true, execute and return a mock proof
pub mock_mode: bool,
/// optional custom chain configuration for genesis block
pub custom_chain_config: Option<ChainConfig>,
/// optional custom ELF bytes for the SP1 zkVM client. If None, uses default ELF
pub custom_canoe_client_elf: Option<Vec<u8>>,
}

#[async_trait]
Expand All @@ -77,7 +81,16 @@ impl CanoeProvider for CanoeSp1CCProvider {
return None;
}

Some(get_sp1_cc_proof(canoe_inputs, &self.eth_rpc_url, self.mock_mode).await)
Some(
get_sp1_cc_proof(
canoe_inputs,
&self.eth_rpc_url,
self.mock_mode,
self.custom_chain_config.clone(),
self.custom_canoe_client_elf.as_deref(),
)
.await,
)
}
}

Expand All @@ -92,6 +105,10 @@ pub struct CanoeSp1CCReducedProofProvider {
pub eth_rpc_url: String,
/// if true, execute and return a mock proof
pub mock_mode: bool,
/// optional custom chain configuration for genesis block
pub custom_chain_config: Option<ChainConfig>,
/// optional custom ELF bytes for the SP1 zkVM client. If None, uses default ELF
pub custom_canoe_client_elf: Option<Vec<u8>>,
}

#[async_trait]
Expand All @@ -107,7 +124,15 @@ impl CanoeProvider for CanoeSp1CCReducedProofProvider {
return None;
}

match get_sp1_cc_proof(canoe_inputs, &self.eth_rpc_url, self.mock_mode).await {
match get_sp1_cc_proof(
canoe_inputs,
&self.eth_rpc_url,
self.mock_mode,
self.custom_chain_config.clone(),
self.custom_canoe_client_elf.as_deref(),
)
.await
{
Ok(proof) => {
let SP1Proof::Compressed(proof) = proof.proof else {
panic!("cannot get Sp1ReducedProof")
Expand All @@ -123,6 +148,8 @@ async fn get_sp1_cc_proof(
canoe_inputs: Vec<CanoeInput>,
eth_rpc_url: &str,
mock_mode: bool,
custom_chain_config: Option<ChainConfig>,
custom_canoe_client_elf: Option<&[u8]>,
) -> Result<sp1_sdk::SP1ProofWithPublicValues> {
// ensure chain id and l1 block number across all DAcerts are identical
let l1_chain_id = canoe_inputs[0].l1_chain_id;
Expand All @@ -147,35 +174,26 @@ async fn get_sp1_cc_proof(

let rpc_url = Url::from_str(eth_rpc_url).unwrap();

let sketch = match Genesis::try_from(l1_chain_id) {
Ok(genesis) => {
EvmSketch::builder()
.at_block(block_number)
.with_genesis(genesis)
.el_rpc_url(rpc_url)
.build()
.await?
}
// if genesis is not available in the sp1-cc library, the code uses custom genesis config
Err(_) => {
let chain_config = match l1_chain_id {
17000 => genesis_from_json(HOLESKY_GENESIS).expect("genesis from json"),
3151908 => genesis_from_json(KURTOSIS_DEVNET_GENESIS).expect("genesis from json"),
_ => panic!("chain id {l1_chain_id} is not supported by canoe sp1 cc"),
};

let genesis = Genesis::Custom(chain_config.config);

EvmSketch::builder()
.at_block(block_number)
.with_genesis(genesis)
.el_rpc_url(rpc_url)
.build()
.await
.expect("evm sketch builder")
}
let genesis = if let Some(chain_config) = custom_chain_config {
Genesis::Custom(chain_config)
} else if let Ok(genesis) = Genesis::try_from(l1_chain_id) {
genesis
} else {
let chain_config = match l1_chain_id {
17000 => genesis_from_json(HOLESKY_GENESIS).expect("genesis from json"),
3151908 => genesis_from_json(KURTOSIS_DEVNET_GENESIS).expect("genesis from json"),
_ => panic!("chain id {l1_chain_id} is not supported by canoe sp1 cc"),
};
Genesis::Custom(chain_config.config)
};

let sketch = EvmSketch::builder()
.at_block(block_number)
.with_genesis(genesis)
.el_rpc_url(rpc_url)
.build()
.await?;

let derived_l1_header_hash = sketch.anchor.header().hash_slow();
assert!(l1_head_block_hash == derived_l1_header_hash);

Expand Down Expand Up @@ -222,12 +240,13 @@ async fn get_sp1_cc_proof(
.network()
.private_key(&network_private_key)
.build();
let (pk, _vk) = client.setup(ELF);
let elf_bytes = custom_canoe_client_elf.unwrap_or(ELF);
let (pk, _vk) = client.setup(elf_bytes);

let proof = if mock_mode {
// Execute the program using the `ProverClient.execute` method, without generating a proof.
let (public_values, report) = client
.execute(ELF, &stdin)
.execute(elf_bytes, &stdin)
.run()
.expect("sp1-cc should have executed the ELF");
info!(
Expand Down
26 changes: 23 additions & 3 deletions canoe/sp1-cc/verifier/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#![no_std]
extern crate alloc;

use core::str::FromStr;

use alloc::{
string::{String, ToString},
vec::Vec,
Expand Down Expand Up @@ -39,7 +41,21 @@ pub const V_KEY: [u32; 8] = [
];

#[derive(Clone)]
pub struct CanoeSp1CCVerifier {}
pub struct CanoeSp1CCVerifier {
v_key: [u32; 8],
}

impl CanoeSp1CCVerifier {
pub fn new(v_key: [u32; 8]) -> Self {
Self { v_key }
}
}

impl Default for CanoeSp1CCVerifier {
fn default() -> Self {
Self::new(V_KEY)
}
}

impl CanoeVerifier for CanoeSp1CCVerifier {
// some variable is unused, because when sp1-cc verifier is not configured in zkVM mode, all tests
Expand All @@ -50,7 +66,7 @@ impl CanoeVerifier for CanoeSp1CCVerifier {
cert_validity_pair: Vec<(AltDACommitment, CertValidity)>,
canoe_proof_bytes: Option<Vec<u8>>,
) -> Result<(), HokuleaCanoeVerificationError> {
info!("using CanoeSp1CCVerifier with v_key {:?}", V_KEY);
info!("using CanoeSp1CCVerifier with v_key {:?}", &self.v_key);

assert!(!cert_validity_pair.is_empty());

Expand All @@ -72,7 +88,7 @@ impl CanoeVerifier for CanoeSp1CCVerifier {
let public_values_digest = Sha256::digest(journals_bytes);
// the function will panic if the proof is incorrect
// https://github.com/succinctlabs/sp1/blob/011d2c64808301878e6f0375c3596b3e22e53949/crates/zkvm/lib/src/verify.rs#L3
verify_sp1_proof(&V_KEY, &public_values_digest.into());
verify_sp1_proof(&self.v_key, &public_values_digest.into());
Ok(())
} else {
panic!("CanoeSp1CCVerifier should only be used for secure integration whose validation happens in zkVM");
Expand Down Expand Up @@ -154,6 +170,10 @@ fn hash_chain_config(chain_id: u64, active_fork_name: String) -> B256 {
// Resulting different genesis hash.
// https://github.com/succinctlabs/rsp/blob/c14b4005ea9257e4d434a080b6900411c17f781b/crates/primitives/src/genesis.rs#L19
fn rsp_genesis_hash(chain_id: u64) -> B256 {
if let Some(s) = option_env!("CUSTOM_RSP_GENESIS_HASH") {
return B256::from_str(s).expect("CUSTOM_RSP_GENESIS_HASH should be a valid hex string");
}

match Genesis::try_from(chain_id) {
Ok(genesis) => {
let rsp_genesis_bytes =
Expand Down
10 changes: 10 additions & 0 deletions canoe/verifier-address-fetcher/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,13 @@ fn cert_verifier_address_abi_encode_interface(
}
}
}

impl CanoeVerifierAddressFetcher for Address {
fn fetch_address(
&self,
_chain_id: u64,
_versioned_cert: &EigenDAVersionedCert,
) -> Result<Address, CanoeVerifierAddressFetcherError> {
Ok(*self)
}
}
6 changes: 6 additions & 0 deletions canoe/verifier/src/chain_spec.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use core::str::FromStr;

use alloy_genesis::Genesis;
use alloy_primitives::B256;
use reth_chainspec::{Chain, ChainSpec, ChainSpecBuilder, HOLESKY, MAINNET, SEPOLIA};
Expand All @@ -10,6 +12,10 @@ pub fn derive_chain_spec_id(
l1_head_block_timestamp: u64,
l1_head_block_number: u64,
) -> SpecId {
if let Some(s) = option_env!("CUSTOM_CHAIN_SPEC") {
return SpecId::from_str(s).expect("CUSTOM_CHAIN_SPEC should be a valid hard fork name");
}

match l1_chain_id {
// mainnet
1 => spec_by_timestamp_and_block_number(
Expand Down
7 changes: 4 additions & 3 deletions example/preloader/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ hokulea-client.workspace = true
hokulea-compute-proof.workspace = true

# General
alloy-genesis.workspace = true
anyhow.workspace = true
tokio = { workspace = true, features = ["full"] }
clap = { workspace = true, features = ["derive", "env"] }
cfg-if = { workspace = true }
tracing = { workspace = true }
clap = { workspace = true, features = ["derive", "env"] }
serde_json.workspace = true
tokio = { workspace = true, features = ["full"] }
tracing = { workspace = true }

kona-client.workspace = true
kona-preimage.workspace = true
Expand Down
13 changes: 12 additions & 1 deletion example/preloader/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,20 @@ async fn main() -> anyhow::Result<()> {
.and_then(|v| v.parse::<bool>().ok())
.unwrap_or(false);

let canoe_provider = CanoeSp1CCReducedProofProvider{
let custom_chain_config = if let Some(path) = cfg.kona_cfg.l1_config_path {
let json = std::fs::read_to_string(&path)
.with_context(|| format!("failed to read genesis file at {}", path.display()))?;
let genesis = serde_json::from_str::<alloy_genesis::Genesis>(&json)
.with_context(|| format!("failed to parse L1 genesis from {}", path.display()))?;
Some(genesis.config)
} else {
None
};
let canoe_provider = CanoeSp1CCReducedProofProvider {
eth_rpc_url: cfg.kona_cfg.l1_node_address.clone().unwrap(),
mock_mode,
custom_chain_config,
custom_canoe_client_elf: None,
};
let canoe_verifier = CanoeNoOpVerifier{};
} else {
Expand Down
Loading