Skip to content

Commit

Permalink
stark2snark
Browse files Browse the repository at this point in the history
  • Loading branch information
hashcashier committed Feb 1, 2024
1 parent 68b3b74 commit 01bce1d
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 49 deletions.
3 changes: 3 additions & 0 deletions host/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ pub struct ProveArgs {
#[clap(short, long, default_value_t = false)]
/// Prove remotely using Bonsai.
pub submit_to_bonsai: bool,
/// Convert the resulting STARK receipt into a Groth-16 SNARK using Bonsai
#[clap(short, long, default_value_t = false)]
pub snark: bool,
}

#[derive(clap::Args, Debug, Clone)]
Expand Down
63 changes: 47 additions & 16 deletions host/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ extern crate core;
use anyhow::Result;
use clap::Parser;
use log::info;
use risc0_zkvm::sha::Digest;
use zeth::{
cli::Cli,
operations::{chains, info::op_info, rollups},
operations::{chains, info::op_info, rollups, stark2snark},
};
use zeth_guests::*;
use zeth_lib::{
Expand Down Expand Up @@ -55,36 +56,66 @@ async fn main() -> Result<()> {
return op_info(cli).await;
}

// Prepare to snarkify resulting stark
let (should_snarkify, can_snarkify) = if let Cli::Prove(prove_args) = &cli {
(prove_args.snark, prove_args.submit_to_bonsai)
} else {
(false, false)
};

// Execute other commands
let core_args = cli.core_args();

match core_args.network {
let (image_id, stark) = match core_args.network {
Network::Ethereum => {
let rpc_url = core_args.eth_rpc_url.clone();
chains::build_chain_blocks::<EthereumStrategy>(
cli,
rpc_url,
ETH_MAINNET_CHAIN_SPEC.clone(),
ETH_BLOCK_ELF,
(
ETH_BLOCK_ID,
chains::build_chain_blocks::<EthereumStrategy>(
cli,
rpc_url,
ETH_MAINNET_CHAIN_SPEC.clone(),
ETH_BLOCK_ELF,
)
.await?,
)
.await
}
Network::Optimism => {
let rpc_url = core_args.op_rpc_url.clone();
chains::build_chain_blocks::<OptimismStrategy>(
cli,
rpc_url,
OP_MAINNET_CHAIN_SPEC.clone(),
OP_BLOCK_ELF,
(
OP_BLOCK_ID,
chains::build_chain_blocks::<OptimismStrategy>(
cli,
rpc_url,
OP_MAINNET_CHAIN_SPEC.clone(),
OP_BLOCK_ELF,
)
.await?,
)
.await
}
Network::OptimismDerived => {
if let Some(composition_size) = cli.composition() {
rollups::compose_derived_rollup_blocks(cli, composition_size).await
(
OP_COMPOSE_ID,
rollups::compose_derived_rollup_blocks(cli, composition_size).await?,
)
} else {
rollups::derive_rollup_blocks(cli).await
(OP_DERIVE_ID, rollups::derive_rollup_blocks(cli).await?)
}
}
};

if should_snarkify {
let Some((stark_uuid, stark_receipt)) = stark else {
panic!("No STARK data to snarkify!");
};

if !can_snarkify {
panic!("Bonsai submission flag required to create a SNARK!");
}

stark2snark(Digest::from(image_id), stark_uuid, stark_receipt).await?;
}

Ok(())
}
21 changes: 11 additions & 10 deletions host/src/operations/chains.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use std::fmt::Debug;
use anyhow::Context;
use ethers_core::types::Transaction as EthersTransaction;
use log::{info, warn};
use risc0_zkvm::compute_image_id;
use risc0_zkvm::{compute_image_id, Receipt};
use serde::{Deserialize, Serialize};
use zeth_lib::{
builder::BlockBuilderStrategy,
Expand All @@ -37,7 +37,7 @@ pub async fn build_chain_blocks<N: BlockBuilderStrategy>(
rpc_url: Option<String>,
chain_spec: ChainSpec,
guest_elf: &[u8],
) -> anyhow::Result<()>
) -> anyhow::Result<Option<(String, Receipt)>>
where
N::TxEssence: 'static + Send + TryFrom<EthersTransaction> + Serialize + Deserialize<'static>,
<N::TxEssence as TryFrom<EthersTransaction>>::Error: Debug,
Expand Down Expand Up @@ -87,8 +87,8 @@ where
}

let compressed_output = output.with_state_compressed();
match &cli {
Cli::Build(..) => {}
let result = match &cli {
Cli::Build(..) => None,
Cli::Run(run_args) => {
execute(
&input,
Expand All @@ -98,6 +98,7 @@ where
&compressed_output,
&cli.execution_label(),
);
None
}
Cli::Prove(..) => {
maybe_prove(
Expand All @@ -107,21 +108,21 @@ where
&compressed_output,
Default::default(),
)
.await;
.await
}
Cli::Verify(verify_args) => {
Cli::Verify(verify_args) => Some(
verify_bonsai_receipt(
compute_image_id(guest_elf)?,
&compressed_output,
verify_args.bonsai_receipt_uuid.clone(),
4,
)
.await?;
}
.await?,
),
Cli::OpInfo(..) => {
unreachable!()
}
}
};

Ok(())
Ok(result)
}
81 changes: 79 additions & 2 deletions host/src/operations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,92 @@ use std::fmt::Debug;

use log::{debug, error, info, warn};
use risc0_zkvm::{
compute_image_id, default_prover, serde::to_vec, sha::Digest, Assumption, ExecutorEnv,
ExecutorImpl, FileSegmentRef, Receipt, Session,
compute_image_id, default_prover,
serde::to_vec,
sha::{Digest, Digestible},
Assumption, ExecutorEnv, ExecutorImpl, FileSegmentRef, Groth16Receipt, Groth16Seal,
InnerReceipt, Receipt, Session,
};
use serde::{de::DeserializeOwned, Serialize};
use tempfile::tempdir;
use zeth_primitives::keccak::keccak;

use crate::{cli::Cli, load_receipt, save_receipt};

pub async fn stark2snark(
image_id: Digest,
stark_uuid: String,
stark_receipt: Receipt,
) -> anyhow::Result<(String, Receipt)> {
// Label snark output as journal digest
let receipt_label = format!(
"{}-{}",
hex::encode(image_id),
hex::encode(keccak(stark_receipt.journal.bytes.digest()))
);
// Load cached receipt if found
if let Ok(Some(cached_data)) = load_receipt(&receipt_label) {
info!("Loaded locally cached receipt");
return Ok(cached_data);
}
// Otherwise compute on Bonsai
let stark_uuid = if stark_uuid.is_empty() {
upload_receipt(&stark_receipt).await?
} else {
stark_uuid
};

let claim = stark_receipt.get_claim()?;

let client = bonsai_sdk::alpha_async::get_client_from_env(risc0_zkvm::VERSION).await?;
let snark_uuid = client.create_snark(stark_uuid)?;

let snark_receipt = loop {
let res = snark_uuid.status(&client)?;

if res.status == "RUNNING" {
info!("Current status: {} - continue polling...", res.status,);
std::thread::sleep(std::time::Duration::from_secs(15));
} else if res.status == "SUCCEEDED" {
let snark = res
.output
.expect("Bonsai response is missing SnarkReceipt.")
.snark;
// Convert Bonsai SnarkReceipt to legacy receipt
let seal = Groth16Seal {
a: snark.a,
b: snark.b,
c: snark.c,
};

let receipt = Receipt::new(
InnerReceipt::Groth16(Groth16Receipt {
seal: seal.to_vec(),
claim,
}),
stark_receipt.journal.bytes,
);

// verify validity of snark receipt
receipt.verify(image_id)?;

break receipt;
} else {
panic!(
"Workflow exited: {} - | err: {}",
res.status,
res.error_msg.unwrap_or_default()
);
}
};

let snark_data = (snark_uuid.uuid, snark_receipt);

save_receipt(&receipt_label, &snark_data);

Ok(snark_data)
}

pub async fn verify_bonsai_receipt<O: Eq + Debug + DeserializeOwned>(
image_id: Digest,
expected_output: &O,
Expand Down
47 changes: 26 additions & 21 deletions host/src/operations/rollups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use std::collections::VecDeque;

use anyhow::Context;
use log::{info, trace};
use risc0_zkvm::Assumption;
use risc0_zkvm::{Assumption, Receipt};
use zeth_guests::*;
use zeth_lib::{
builder::{BlockBuilderStrategy, OptimismStrategy},
Expand All @@ -42,7 +42,7 @@ use crate::{
operations::{maybe_prove, verify_bonsai_receipt},
};

pub async fn derive_rollup_blocks(cli: Cli) -> anyhow::Result<()> {
pub async fn derive_rollup_blocks(cli: Cli) -> anyhow::Result<Option<(String, Receipt)>> {
info!("Fetching data ...");
let core_args = cli.core_args().clone();
let op_builder_provider_factory = ProviderFactory::new(
Expand Down Expand Up @@ -117,9 +117,7 @@ pub async fn derive_rollup_blocks(cli: Cli) -> anyhow::Result<()> {
println!("Derived: {} {}", derived_block.0, derived_block.1);
}

match &cli {
Cli::Build(..) => {}
Cli::Run(..) => {}
let final_result = match &cli {
Cli::Prove(..) => {
maybe_prove(
&cli,
Expand All @@ -128,26 +126,30 @@ pub async fn derive_rollup_blocks(cli: Cli) -> anyhow::Result<()> {
&derive_output,
(assumptions, bonsai_receipt_uuids),
)
.await;
.await
}
Cli::Verify(verify_args) => {
Cli::Verify(verify_args) => Some(
verify_bonsai_receipt(
OP_DERIVE_ID.into(),
&derive_output,
verify_args.bonsai_receipt_uuid.clone(),
4,
)
.await?;
}
.await?,
),
Cli::OpInfo(..) => {
unreachable!()
}
}
_ => None,
};

Ok(())
Ok(final_result)
}

pub async fn compose_derived_rollup_blocks(cli: Cli, composition_size: u64) -> anyhow::Result<()> {
pub async fn compose_derived_rollup_blocks(
cli: Cli,
composition_size: u64,
) -> anyhow::Result<Option<(String, Receipt)>> {
let core_args = cli.core_args().clone();
// OP Composition
info!("Fetching data ...");
Expand Down Expand Up @@ -431,7 +433,7 @@ pub async fn compose_derived_rollup_blocks(cli: Cli, composition_size: u64) -> a
.process()
.expect("Finish composition failed.");

if let (
let final_result = if let (
Some((prep_receipt_uuid, prep_receipt)),
Some((aggregate_receipt_uuid, aggregate_receipt)),
) = (prep_compose_receipt, aggregate_receipt)
Expand All @@ -446,22 +448,25 @@ pub async fn compose_derived_rollup_blocks(cli: Cli, composition_size: u64) -> a
vec![prep_receipt_uuid, aggregate_receipt_uuid],
),
)
.await;
.await
} else if let Cli::Verify(verify_args) = cli {
verify_bonsai_receipt(
OP_COMPOSE_ID.into(),
&finish_compose_output,
verify_args.bonsai_receipt_uuid.clone(),
4,
Some(
verify_bonsai_receipt(
OP_COMPOSE_ID.into(),
&finish_compose_output,
verify_args.bonsai_receipt_uuid.clone(),
4,
)
.await?,
)
.await?;
} else {
info!("Preflight successful!");
None
};

trace!("Final composition output: {:?}", &finish_compose_output);

Ok(())
Ok(final_result)
}

async fn build_op_blocks(
Expand Down

0 comments on commit 01bce1d

Please sign in to comment.