Skip to content
Open
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
24 changes: 19 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ members = [
"crates/cairo-program-runner",
"crates/cairo-program-runner-lib",
"crates/stwo_run_and_prove",
"crates/proving_service",
"crates/vm_runner",
]
resolver = "2"

[profile.release]
debug = true

[workspace.package]
version = "1.1.0"
edition = "2024"
Expand All @@ -16,7 +20,7 @@ repository = "https://github.com/starkware-libs/proving-utils"
[workspace.dependencies]
anyhow = "1.0.98"
bincode = { version = "2.0.1", features = ["serde"] }
cairo-air = "1.1.0"
cairo-air = { git = "https://github.com/starkware-libs/stwo-cairo", rev = "319e2e96" }
cairo-lang-executable = "2.16.0"
cairo-lang-runner = "2.16.0"
cairo-lang-casm = "2.16.0"
Expand All @@ -40,13 +44,23 @@ sonic-rs = "0.3.17"
starknet-crypto = "=0.8.1"
starknet-ff = "0.3.7"
starknet-types-core = "=0.2.4"
stwo-cairo-utils = "1.1.0"
stwo-cairo-adapter = "1.1.0"
stwo-cairo-prover = "1.1.0"
stwo-cairo-serialize = "1.1.0"
stwo = { git = "https://github.com/starkware-libs/stwo", rev = "3428a95e", features = [
"parallel",
], default-features = false }
stwo-cairo-utils = { package = "stwo-cairo-utils", git = "https://github.com/starkware-libs/stwo-cairo", rev = "319e2e96" }
stwo-cairo-adapter = { package = "stwo-cairo-adapter", git = "https://github.com/starkware-libs/stwo-cairo", rev = "319e2e96" }
stwo-cairo-prover = { package = "stwo-cairo-prover", git = "https://github.com/starkware-libs/stwo-cairo", rev = "319e2e96" }
stwo-cairo-serialize = { package = "stwo-cairo-serialize", git = "https://github.com/starkware-libs/stwo-cairo", rev = "319e2e96" }
circuit-air = { git = "https://github.com/starkware-libs/stwo-circuits", rev = "c6aea41" }
circuit-cairo-air = { git = "https://github.com/starkware-libs/stwo-circuits", rev = "c6aea41" }
circuit-prover = { git = "https://github.com/starkware-libs/stwo-circuits", rev = "c6aea41" }
circuits = { git = "https://github.com/starkware-libs/stwo-circuits", rev = "c6aea41" }
circuits-stark-verifier = { git = "https://github.com/starkware-libs/stwo-circuits", rev = "c6aea41" }
tempfile = "3.10.1"
thiserror = "1.0.61"
thiserror-no-std = "2.0.2"

tracing = "0.1.40"
mockall = "0.13.1"
rstest = "0.21"

40 changes: 40 additions & 0 deletions crates/proving_service/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
[package]
name = "proving_service"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
description = "Proving service for Cairo programs"

[dependencies]
anyhow.workspace = true
cairo-air.workspace = true
cairo-program-runner-lib.workspace = true
cairo-vm.workspace = true
clap.workspace = true
env_logger.workspace = true
log.workspace = true
serde.workspace = true
serde_json.workspace = true
sonic-rs.workspace = true
starknet-ff.workspace = true
stwo-cairo-prover.workspace = true
stwo-cairo-utils.workspace = true
stwo-cairo-adapter.workspace = true
stwo-cairo-serialize.workspace = true
circuit-air.workspace = true
circuit-cairo-air.workspace = true
circuit-prover.workspace = true
circuits.workspace = true
circuits-stark-verifier.workspace = true
itertools = "0.12"
stwo.workspace = true
stwo-run-and-prove = { path = "../stwo_run_and_prove" }
thiserror.workspace = true
tracing.workspace = true

[dev-dependencies]
ctor.workspace = true
mockall.workspace = true
rstest.workspace = true
tempfile.workspace = true
129 changes: 129 additions & 0 deletions crates/proving_service/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use cairo_air::utils::ProofFormat;
use circuit_air::statement::all_circuit_components;
use circuit_cairo_air::statement::MEMORY_VALUES_LIMBS;
use circuit_cairo_air::verify::{
CairoVerifierConfig, build_fixed_cairo_circuit, prepare_cairo_proof_for_circuit_verifier,
};
use circuit_prover::finalize::finalize_context;
use circuit_prover::prover::BaseColumnPool;
use circuit_prover::prover::preprare_circuit_proof_for_circuit_verifier;
use circuit_prover::prover::{SimdBackend, prove_circuit_with_precompute};
use circuit_prover::witness::preprocessed::PreprocessedCircuit;
use circuits_stark_verifier::proof::ProofConfig;
use itertools::Itertools;
use std::array;
use std::sync::Arc;
use std::{fs::read_to_string, path::PathBuf};
use stwo::core::fields::m31::M31;
use stwo::core::fields::qm31::QM31;
use stwo::core::pcs::PcsConfig;
use stwo::core::utils::MaybeOwned;
use stwo::prover::CommitmentTreeProver;
use stwo::prover::poly::twiddles::TwiddleTree;
use stwo_cairo_adapter::ProverInput;
use stwo_cairo_prover::prover::{ProverParameters, prove_cairo_with_precompute};
use stwo_cairo_prover::stwo::core::vcs_lifted::blake2_merkle::Blake2sM31MerkleChannel;
use stwo_cairo_prover::witness::prelude::PreProcessedTrace;

use tracing::{Level, span};

pub use stwo_run_and_prove::{
ProveConfig, ProverTrait, RunConfig, StwoProverEntryPoint, StwoRunAndProveError,
stwo_run_and_prove,
};

pub struct ProvingServiceEntryPoint {
pub base_column_pool: BaseColumnPool<SimdBackend>,
pub preprocessed_circuit: PreprocessedCircuit,
pub privacy_verifier_config: CairoVerifierConfig,
pub twiddles: TwiddleTree<SimdBackend>,
pub cairo_preprocessed_trace: Arc<PreProcessedTrace>,
pub cairo_preprocessed_tree: CommitmentTreeProver<SimdBackend, Blake2sM31MerkleChannel>,
pub circuit_preprocessed_tree: CommitmentTreeProver<SimdBackend, Blake2sM31MerkleChannel>,
}

impl ProverTrait for ProvingServiceEntryPoint {
fn create_and_serialize_proof(
&self,
prover_input: ProverInput,
_verify: bool,
_proof_path: PathBuf,
_proof_format: ProofFormat,
proof_params_json: Option<PathBuf>,
) -> Result<(), StwoRunAndProveError> {
let proof_params: ProverParameters = if let Some(proof_params_json) = proof_params_json {
let s = read_to_string(&proof_params_json)
.map_err(|e| StwoRunAndProveError::PathIO(e, proof_params_json.clone()))?;
serde_json::from_str(&s)
.map_err(|e| StwoRunAndProveError::Anyhow(anyhow::Error::from(e)))?
} else {
panic!("Proof parameters JSON file is required");
};

let span = span!(Level::INFO, "proving cairo").entered();
let cairo_proof = prove_cairo_with_precompute::<Blake2sM31MerkleChannel>(
&self.base_column_pool,
&self.twiddles,
self.cairo_preprocessed_trace.clone(),
MaybeOwned::Borrowed(&self.cairo_preprocessed_tree),
prover_input,
proof_params,
)
.map_err(|e| StwoRunAndProveError::Anyhow(anyhow::Error::from(e)))?;
span.exit();

let (proof, public_data) = prepare_cairo_proof_for_circuit_verifier(
&cairo_proof,
&self.privacy_verifier_config.proof_config,
);

let (public_claim, outputs, _program) = public_data.pack_into_u32s();
let outputs: Vec<[M31; 28]> = outputs
.chunks_exact(MEMORY_VALUES_LIMBS)
.map(|chunk| array::from_fn(|i| M31::from_u32_unchecked(chunk[i])))
.collect_vec();

let span = span!(Level::INFO, "building verification context").entered();

let mut context =
build_fixed_cairo_circuit(&self.privacy_verifier_config, proof, public_claim, outputs);
span.exit();

finalize_context(&mut context);
let context_values = context.values();

let span = span!(Level::INFO, "proving verification circuit").entered();

let mut pcs_config = PcsConfig::default();
let lifting_log_size = self.preprocessed_circuit.params.trace_log_size
+ pcs_config.fri_config.log_blowup_factor;
pcs_config.lifting_log_size = Some(lifting_log_size);

let circuit_proof = prove_circuit_with_precompute(
&self.base_column_pool,
&self.twiddles,
&self.preprocessed_circuit,
MaybeOwned::Borrowed(&self.circuit_preprocessed_tree),
context_values,
pcs_config,
);
span.exit();

let preprocessed_column_ids = self.preprocessed_circuit.preprocessed_trace.ids();
let proof_config = ProofConfig::from_components(
&all_circuit_components::<QM31>(),
preprocessed_column_ids.len(),
&circuit_proof.pcs_config,
circuit_air::statement::INTERACTION_POW_BITS,
);
let (_proof, _public_data) =
preprare_circuit_proof_for_circuit_verifier(circuit_proof, proof_config);

// Sleep for 1 second to create a seperation between the proving and clean up.
std::thread::sleep(std::time::Duration::from_secs(1));

// TODO: Serialize the proof to a file.

Ok(())
}
}
159 changes: 159 additions & 0 deletions crates/proving_service/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
use cairo_air::{PreProcessedTraceVariant, utils::ProofFormat};
use cairo_program_runner_lib::utils::get_program_input_from_path;
use circuit_cairo_air::privacy::privacy_cairo_verifier_config;
use circuit_cairo_air::verify::build_cairo_verifier_circuit;
use circuit_prover::prover::BaseColumnPool;
use circuit_prover::witness::preprocessed::PreprocessedCircuit;
use clap::Parser;
use proving_service::ProvingServiceEntryPoint;
use std::process::ExitCode;
use std::{cmp::max, path::PathBuf, sync::Arc};
use stwo::{
core::vcs_lifted::blake2_merkle::Blake2sM31MerkleChannel,
prover::{CommitmentTreeProver, poly::circle::PolyOps},
};
use stwo_cairo_prover::witness::{
prelude::{CanonicCoset, SimdBackend},
preprocessed_trace::gen_trace,
};
use stwo_cairo_utils::binary_utils::run_binary;
use stwo_run_and_prove::{ProveConfig, RunConfig, StwoRunAndProveError, stwo_run_and_prove};
use tracing::{Level, span};

/// This binary runs a cairo program and generates a Stwo proof for it.
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Args {
#[clap(long = "program", help = "Absolute path to the compiled program.")]
program: PathBuf,
#[clap(
long = "program_input",
help = "Absolute path to the program input file."
)]
program_input: Option<PathBuf>,
#[clap(
long = "prover_params_json",
help = "Absolute path to the JSON file containing the prover parameters."
)]
prover_params_json: Option<PathBuf>,
#[clap(
long = "proof_path",
help = "Absolute path where the generated proof will be saved."
)]
proof_path: PathBuf,
#[clap(long, value_enum, default_value_t = ProofFormat::CairoSerde, help = "Json or cairo-serde.")]
proof_format: ProofFormat,
#[clap(long = "verify", help = "Should verify the generated proof.")]
verify: bool,
#[clap(
long = "program_output",
help = "Optional absolute path where the program's output will be saved."
)]
program_output: Option<PathBuf>,
#[clap(
long = "save_debug_data",
help = "Should save the ProverInput to a file in `debug_data_dir` for both success and failure."
)]
save_debug_data: bool,
#[clap(
long = "debug_data_dir",
help = "Absolute path to the output directory where the ProverInput will be saved in the
case of a proving error, or when the save_debug_data flag is enabled."
)]
debug_data_dir: Option<PathBuf>,
}

fn main() -> ExitCode {
run_binary(run, "proving_service")
}

fn run() -> Result<(), StwoRunAndProveError> {
let _span = span!(Level::INFO, "run").entered();
let args = Args::parse();
let prove_config = ProveConfig {
verify: args.verify,
proof_path: args.proof_path,
proof_format: args.proof_format,
prover_params_json: args.prover_params_json,
};

let privacy_verifier_config = privacy_cairo_verifier_config();
let proof_config = &privacy_verifier_config.proof_config;
let mut novalue_context = build_cairo_verifier_circuit(&privacy_verifier_config);
let preprocessed_circuit = PreprocessedCircuit::preprocess_circuit(&mut novalue_context);

let cairo_evaluation_domain_log_size = proof_config
.log_evaluation_domain_size()
.try_into()
.unwrap();

let circuit_proof_log_blowup_factor = 1;
let max_domain_size = max(
preprocessed_circuit.params.trace_log_size + circuit_proof_log_blowup_factor,
cairo_evaluation_domain_log_size,
);

// Precompute twiddles.
// Account for blowup factor and for composition polynomial calculation (taking the max since
// the composition polynomial is split prior to LDE).
let twiddles = SimdBackend::precompute_twiddles(
CanonicCoset::new(max_domain_size)
.circle_domain()
.half_coset,
);

let preprocessed_trace =
Arc::new(PreProcessedTraceVariant::CanonicalSmall.to_preprocessed_trace());
let preprocessed_trace_polys =
SimdBackend::interpolate_columns(gen_trace(preprocessed_trace.clone()), &twiddles);

let store_polynomials_coefficients = true;

let base_column_pool = BaseColumnPool::<SimdBackend>::new();
let cairo_preprocessed_tree = CommitmentTreeProver::<SimdBackend, Blake2sM31MerkleChannel>::new(
preprocessed_trace_polys,
proof_config.fri.log_blowup_factor.try_into().unwrap(),
&twiddles,
store_polynomials_coefficients,
Some(cairo_evaluation_domain_log_size),
&base_column_pool,
);

let circuit_preprocessed_trace = preprocessed_circuit
.preprocessed_trace
.get_trace::<SimdBackend>();
let circuit_preprocessed_trace_polys =
SimdBackend::interpolate_columns(circuit_preprocessed_trace, &twiddles);

let circuit_preprocessed_tree =
CommitmentTreeProver::<SimdBackend, Blake2sM31MerkleChannel>::new(
circuit_preprocessed_trace_polys,
circuit_proof_log_blowup_factor,
&twiddles,
store_polynomials_coefficients,
None,
&base_column_pool,
);

let prover = Box::new(ProvingServiceEntryPoint {
base_column_pool,
preprocessed_circuit,
privacy_verifier_config,
twiddles,
cairo_preprocessed_trace: preprocessed_trace,
cairo_preprocessed_tree,
circuit_preprocessed_tree,
});
let run_config = RunConfig {
program_path: args.program,
program_input: get_program_input_from_path(&args.program_input)?,
program_output: args.program_output,
debug_data_dir: args.debug_data_dir,
save_debug_data: args.save_debug_data,
extra_hint_processor: None,
};
// Sleep for 1 second to create a seperation between the preproccessing and the proving.
std::thread::sleep(std::time::Duration::from_secs(1));
stwo_run_and_prove(run_config, prove_config, prover)?;
Ok(())
}