diff --git a/.gitignore b/.gitignore index 4f653b47..54acb129 100644 --- a/.gitignore +++ b/.gitignore @@ -12,8 +12,7 @@ Cargo.lock **/proptest-regressions /output.txt flamegraph.svg -**/*.trace -**/*.memory +dhat-heap.json **/.DS_Store **/*.swp diff --git a/Cargo.toml b/Cargo.toml index 84bc3f20..8acbd135 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ rayon = { version = "1.7.0", optional = true } giza-core = { git = "https://github.com/lambdaclass/giza", branch = "remove_prints", optional = true } giza-prover = { git = "https://github.com/lambdaclass/giza", branch = "remove_prints", optional = true } giza-runner = { git = "https://github.com/lambdaclass/giza", branch = "remove_prints", optional = true } +dhat = { version = "0.3.2", optional = true } [dev-dependencies] proptest = "1.2.0" @@ -39,21 +40,23 @@ instruments = [] # This enables timing prints in prover and ve metal = ["lambdaworks-math/metal"] parallel = ["dep:rayon"] giza = ["dep:giza-core", "dep:giza-prover", "dep:giza-runner"] +heap_profiling = ["dep:dhat"] [[bench]] name = "criterion_prover" harness = false -metal = ["lambdaworks-math/metal"] [[bench]] name = "criterion_prover_70k" harness = false -metal = ["lambdaworks-math/metal"] + +[[bench]] +name = "criterion_prover_security" +harness = false [[bench]] name = "criterion_verifier" harness = false -metal = ["lambdaworks-math/metal"] [[bench]] name = "criterion_verifier_70k" @@ -63,11 +66,24 @@ harness = false name = "criterion_giza" harness = false +[[bench]] +name = "criterion_verifier_security" +harness = false + +[[bench]] +name = "criterion_table" +harness = false + +[[bench]] +name = "dhat_prover" +harness = false +required-features = ["heap_profiling"] [profile.release] lto = true opt-level = 3 codegen-units = 1 +debug = 1 [profile.test] lto = "thin" diff --git a/Makefile b/Makefile index c60c63f9..981b783d 100644 --- a/Makefile +++ b/Makefile @@ -19,10 +19,10 @@ build: cargo build --release prove: build - cargo run --release prove $(PROGRAM_PATH) $(PROOF_PATH) + cargo run --release prove $(PROGRAM_PATH) $(PROOF_PATH) -s verify: build - cargo run --release verify $(PROOF_PATH) + cargo run --release verify $(PROOF_PATH) -s run_all: build cargo run --release prove_and_verify $(PROGRAM_PATH) @@ -43,12 +43,21 @@ clippy: cargo clippy --workspace --all-targets -- -D warnings benchmarks_sequential: $(COMPILED_CAIRO0_PROGRAMS) - cargo bench + cargo bench --bench criterion_prover + cargo bench --bench criterion_verifier benchmarks_parallel: $(COMPILED_CAIRO0_PROGRAMS) cargo bench -F parallel --bench criterion_prover cargo bench -F parallel --bench criterion_verifier +benchmarks_security: $(COMPILED_CAIRO0_PROGRAMS) + cargo bench -F parallel --bench criterion_prover_security + cargo bench -F parallel --bench criterion_verifier_security + +benchmarks_table: $(COMPILED_CAIRO0_PROGRAMS) + cargo bench -F parallel --bench criterion_table + cargo bench --bench dhat_prover + benchmarks_parallel_all: $(COMPILED_CAIRO0_PROGRAMS) cargo bench -F parallel diff --git a/README.md b/README.md index c4038daa..8b70f391 100644 --- a/README.md +++ b/README.md @@ -140,3 +140,138 @@ if you donĀ“t have the tools for fuzzing installed use make fuzzer_tools ``` +## Benchmarks + +To get the results of the table below, run + +``` +make benchmarks_table +``` + +The results shown are from the execution of a Fibonacci program. + +Bits of security are used to ensure that the proof satisfies a certain conjuecturable security level. + +First table has the results that are independent of the hardware used. + +| n | Trace length | Prover RAM | Proof size 80 bit security | Proof size 128 bit security | +|-----|--------------|------------|----------------------------|-----------------------------| +| 100 | 2^10 | 18.5 MB | 270 KB | 476 KB | +| 500 | 2^12 | 75.5 MB | 335 KB | 591 KB | +| 2k | 2^14 | 302.1 MB | 407 KB | 719 KB | +| 5k | 2^16 | 1.2 GB | 488 KB | 862 KB | +| 20k | 2^18 | 4.7 GB | 576 KB | 1 MB | + +Second table has the results of the execution on an Apple M1 with 4 E and 4 P cores and 16 GB of RAM: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Trace lengthTrace time80 bit security128 bit security
Prover timeVerifier timeProver timeVerifier time
2^100.9 ms1.1 s3.1 ms1.1 s4.7 ms
2^125.3 ms335.5 ms7.6 ms336.4 ms9.5 ms
2^1424.7 ms1.41 s26.4 ms1.42 s29 ms
2^1677.2 ms5.8 s108.8 ms5.8 s113.9 ms
2^18312 ms24.3 s477.4 ms24.3 s481.7 ms
+ +Third table has the results of the execution on an Intel Xeon Platinum with 4 cores and 16 GB of RAM: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Trace lengthTrace time80 bit security128 bit security
Prover timeVerifier timeProver timeVerifier time
2^101.4 ms2.5 s6.3 ms2.5 s9.8 ms
2^128 ms709 ms13.3 ms710.5 ms17.7 ms
2^1444.9 ms3 s41.1 ms3 s46.5 ms
2^16140.8 s12.2 s160.6 ms12.2 s168 ms
2^18700 ms50.5 s692.9 ms50.5 s702.6 ms
diff --git a/benches/criterion_giza.rs b/benches/criterion_giza.rs index 9160623a..307b4447 100644 --- a/benches/criterion_giza.rs +++ b/benches/criterion_giza.rs @@ -10,6 +10,8 @@ use lambdaworks_stark::{ starks::proof::options::{self, SecurityLevel}, }; #[cfg(feature = "giza")] +pub mod criterion_utils; +#[cfg(feature = "giza")] pub mod functions; #[cfg(not(feature = "giza"))] diff --git a/benches/criterion_prover.rs b/benches/criterion_prover.rs index 5842ea02..8b251909 100644 --- a/benches/criterion_prover.rs +++ b/benches/criterion_prover.rs @@ -1,14 +1,11 @@ use criterion::{ - black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, -}; -use lambdaworks_stark::{ - cairo::{ - air::generate_cairo_proof, - runner::run::{generate_prover_args, CairoVersion}, - }, - starks::proof::options::{ProofOptions, SecurityLevel}, + criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, }; +use criterion_utils::utils::run_cairo_bench_with_security_level; +use functions::path::cairo0_program_path; +use lambdaworks_stark::starks::proof::options::SecurityLevel; +pub mod criterion_utils; pub mod functions; fn cairo_benches(c: &mut Criterion) { @@ -39,25 +36,13 @@ fn cairo_benches(c: &mut Criterion) { ); } -fn cairo0_program_path(program_name: &str) -> String { - const CARGO_DIR: &str = env!("CARGO_MANIFEST_DIR"); - const PROGRAM_BASE_REL_PATH: &str = "/cairo_programs/cairo0/"; - let program_base_path = CARGO_DIR.to_string() + PROGRAM_BASE_REL_PATH; - program_base_path + program_name -} - fn run_cairo_bench(group: &mut BenchmarkGroup<'_, WallTime>, benchname: &str, program_path: &str) { - let program_content = std::fs::read(program_path).unwrap(); - let proof_options = ProofOptions::new_secure(SecurityLevel::Provable80Bits, 3); - let (main_trace, pub_inputs) = - generate_prover_args(&program_content, &CairoVersion::V0, &None).unwrap(); - println!("Generated main trace with {} rows", main_trace.n_rows()); - - group.bench_function(benchname, |bench| { - bench.iter(|| { - black_box(generate_cairo_proof(&main_trace, &pub_inputs, &proof_options).unwrap()) - }); - }); + run_cairo_bench_with_security_level( + group, + benchname, + program_path, + SecurityLevel::Conjecturable80Bits, + ); } criterion_group!(benches, cairo_benches); diff --git a/benches/criterion_prover_70k.rs b/benches/criterion_prover_70k.rs index 63a8044f..839f46af 100644 --- a/benches/criterion_prover_70k.rs +++ b/benches/criterion_prover_70k.rs @@ -1,15 +1,11 @@ use criterion::{ - black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, - SamplingMode, -}; -use lambdaworks_stark::{ - cairo::{ - air::generate_cairo_proof, - runner::run::{generate_prover_args, CairoVersion}, - }, - starks::proof::options::{ProofOptions, SecurityLevel}, + criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, SamplingMode, }; +use criterion_utils::utils::run_cairo_bench_with_security_level; +use functions::path::cairo0_program_path; +use lambdaworks_stark::starks::proof::options::SecurityLevel; +pub mod criterion_utils; pub mod functions; fn fibo_70k_bench(c: &mut Criterion) { @@ -36,24 +32,13 @@ fn fibo_70k_bench(c: &mut Criterion) { ); } -fn cairo0_program_path(program_name: &str) -> String { - const CARGO_DIR: &str = env!("CARGO_MANIFEST_DIR"); - const PROGRAM_BASE_REL_PATH: &str = "/cairo_programs/cairo0/"; - let program_base_path = CARGO_DIR.to_string() + PROGRAM_BASE_REL_PATH; - program_base_path + program_name -} - fn run_cairo_bench(group: &mut BenchmarkGroup<'_, WallTime>, benchname: &str, program_path: &str) { - let program_content = std::fs::read(program_path).unwrap(); - let proof_options = ProofOptions::new_secure(SecurityLevel::Provable80Bits, 3); - let (main_trace, pub_inputs) = - generate_prover_args(&program_content, &CairoVersion::V0, &None).unwrap(); - - group.bench_function(benchname, |bench| { - bench.iter(|| { - black_box(generate_cairo_proof(&main_trace, &pub_inputs, &proof_options).unwrap()) - }); - }); + run_cairo_bench_with_security_level( + group, + benchname, + program_path, + SecurityLevel::Provable128Bits, + ); } criterion_group!(benches, fibo_70k_bench); diff --git a/benches/criterion_prover_security.rs b/benches/criterion_prover_security.rs new file mode 100644 index 00000000..d04ac152 --- /dev/null +++ b/benches/criterion_prover_security.rs @@ -0,0 +1,55 @@ +use criterion::{ + criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, +}; +use criterion_utils::utils::run_cairo_bench_with_security_level; +use functions::path::cairo0_program_path; +use lambdaworks_stark::starks::proof::options::SecurityLevel; + +pub mod criterion_utils; +pub mod functions; + +fn cairo_benches(c: &mut Criterion) { + #[cfg(feature = "parallel")] + { + let num_threads: usize = std::env::var("NUM_THREADS") + .unwrap_or("8".to_string()) + .parse() + .unwrap(); + println!("Running benchmarks using {} threads", num_threads); + rayon::ThreadPoolBuilder::new() + .num_threads(num_threads) + .build_global() + .unwrap(); + }; + + let mut group = c.benchmark_group("CAIRO"); + group.sample_size(10); + run_cairo_bench( + &mut group, + "fibonacci/500", + &cairo0_program_path("fibonacci_500.json"), + ); + run_cairo_bench( + &mut group, + "fibonacci/1000", + &cairo0_program_path("fibonacci_1000.json"), + ); +} + +fn run_cairo_bench(group: &mut BenchmarkGroup<'_, WallTime>, benchname: &str, program_path: &str) { + run_cairo_bench_with_security_level( + group, + &format!("80_bits/{benchname}"), + program_path, + SecurityLevel::Conjecturable80Bits, + ); + run_cairo_bench_with_security_level( + group, + &format!("128_bits/{benchname}"), + program_path, + SecurityLevel::Conjecturable128Bits, + ); +} + +criterion_group!(benches, cairo_benches); +criterion_main!(benches); diff --git a/benches/criterion_table.rs b/benches/criterion_table.rs new file mode 100644 index 00000000..f01d92d0 --- /dev/null +++ b/benches/criterion_table.rs @@ -0,0 +1,101 @@ +use criterion::{ + criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, +}; +use criterion_utils::utils::{ + run_cairo_bench_and_measure_proof, run_trace_bench, run_verifier_bench_with_security_level, +}; +use functions::path::{cairo0_program_path, cairo0_proof_path}; +use lambdaworks_stark::starks::proof::options::SecurityLevel; + +pub mod criterion_utils; +pub mod functions; + +fn table_benches(c: &mut Criterion) { + #[cfg(feature = "parallel")] + { + let num_threads: usize = std::env::var("NUM_THREADS") + .unwrap_or("8".to_string()) + .parse() + .unwrap(); + println!("Running benchmarks using {} threads", num_threads); + rayon::ThreadPoolBuilder::new() + .num_threads(num_threads) + .build_global() + .unwrap(); + }; + + let mut group = c.benchmark_group("TABLE"); + group.sample_size(10); + run_table_bench( + &mut group, + "fibonacci/100", + &cairo0_program_path("fibonacci_100.json"), + &cairo0_proof_path("fibonacci_100.proof"), + &cairo0_proof_path("fibonacci_100_sec.proof"), + ); + run_table_bench( + &mut group, + "fibonacci/500", + &cairo0_program_path("fibonacci_500.json"), + &cairo0_proof_path("fibonacci_500.proof"), + &cairo0_proof_path("fibonacci_500_sec.proof"), + ); + run_table_bench( + &mut group, + "fibonacci/2000", + &cairo0_program_path("fibonacci_2000.json"), + &cairo0_proof_path("fibonacci_2000.proof"), + &cairo0_proof_path("fibonacci_2000_sec.proof"), + ); + run_table_bench( + &mut group, + "fibonacci/5000", + &cairo0_program_path("fibonacci_5000.json"), + &cairo0_proof_path("fibonacci_5000.proof"), + &cairo0_proof_path("fibonacci_5000_sec.proof"), + ); + run_table_bench( + &mut group, + "fibonacci/20000", + &cairo0_program_path("fibonacci_20000.json"), + &cairo0_proof_path("fibonacci_20000.proof"), + &cairo0_proof_path("fibonacci_20000_sec.proof"), + ); +} + +fn run_table_bench( + group: &mut BenchmarkGroup<'_, WallTime>, + benchname: &str, + program_path: &str, + proof_path: &str, + sec_proof_path: &str, +) { + run_trace_bench(group, &format!("trace/{benchname}"), program_path); + run_cairo_bench_and_measure_proof( + group, + &format!("prover/80_bits/{benchname}"), + program_path, + SecurityLevel::Conjecturable80Bits, + ); + run_verifier_bench_with_security_level( + group, + &format!("verifier/80_bits/{benchname}"), + proof_path, + SecurityLevel::Conjecturable80Bits, + ); + run_cairo_bench_and_measure_proof( + group, + &format!("prover/128_bits/{benchname}"), + program_path, + SecurityLevel::Conjecturable128Bits, + ); + run_verifier_bench_with_security_level( + group, + &format!("verifier/128_bits/{benchname}"), + sec_proof_path, + SecurityLevel::Conjecturable128Bits, + ); +} + +criterion_group!(benches, table_benches); +criterion_main!(benches); diff --git a/benches/criterion_utils/mod.rs b/benches/criterion_utils/mod.rs new file mode 100644 index 00000000..b5614dd8 --- /dev/null +++ b/benches/criterion_utils/mod.rs @@ -0,0 +1 @@ +pub mod utils; diff --git a/benches/criterion_utils/utils.rs b/benches/criterion_utils/utils.rs new file mode 100644 index 00000000..ffdea9f2 --- /dev/null +++ b/benches/criterion_utils/utils.rs @@ -0,0 +1,118 @@ +use criterion::{black_box, measurement::WallTime, BenchmarkGroup}; +use lambdaworks_math::{ + field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, + traits::{Deserializable, Serializable}, +}; +use lambdaworks_stark::{ + cairo::{ + air::{generate_cairo_proof, verify_cairo_proof, PublicInputs}, + cairo_layout::CairoLayout, + execution_trace::build_main_trace, + runner::run::{build_pub_inputs, run_program, CairoVersion}, + }, + starks::proof::{ + options::{ProofOptions, SecurityLevel}, + stark::StarkProof, + }, +}; + +use crate::functions::stark::generate_prover_args_with_options; + +pub fn run_cairo_bench_with_security_level( + group: &mut BenchmarkGroup<'_, WallTime>, + benchname: &str, + program_path: &str, + security_level: SecurityLevel, +) { + let (proof_options, main_trace, pub_inputs) = + generate_prover_args_with_options(program_path, security_level); + + group.bench_function(benchname, |bench| { + bench.iter(|| { + black_box(generate_cairo_proof(&main_trace, &pub_inputs, &proof_options).unwrap()) + }); + }); +} + +pub fn run_cairo_bench_and_measure_proof( + group: &mut BenchmarkGroup<'_, WallTime>, + benchname: &str, + program_path: &str, + security_level: SecurityLevel, +) { + let (proof_options, main_trace, pub_inputs) = + generate_prover_args_with_options(program_path, security_level); + + let proof_size = generate_cairo_proof(&main_trace, &pub_inputs, &proof_options) + .unwrap() + .serialize() + .len(); + println!("Proof size: {} bytes", proof_size); + println!("Trace length: {} rows", main_trace.n_rows()); + + group.bench_function(benchname, |bench| { + bench.iter(|| { + black_box(generate_cairo_proof(&main_trace, &pub_inputs, &proof_options).unwrap()) + }); + }); +} + +pub fn run_verifier_bench_with_security_level( + group: &mut BenchmarkGroup<'_, WallTime>, + benchname: &str, + program_path: &str, + security_level: SecurityLevel, +) { + let (proof, pub_inputs) = load_proof_and_pub_inputs(program_path); + let proof_options = ProofOptions::new_secure(security_level, 3); + group.bench_function(benchname, |bench| { + bench.iter(|| { + black_box(assert!(verify_cairo_proof( + &proof, + &pub_inputs, + &proof_options + ))) + }); + }); +} + +fn load_proof_and_pub_inputs(input_path: &str) -> (StarkProof, PublicInputs) { + let program_content = std::fs::read(input_path).unwrap(); + let mut bytes = program_content.as_slice(); + let proof_len = usize::from_be_bytes(bytes[0..8].try_into().unwrap()); + bytes = &bytes[8..]; + let proof = StarkProof::::deserialize(&bytes[0..proof_len]).unwrap(); + bytes = &bytes[proof_len..]; + + let public_inputs = PublicInputs::deserialize(bytes).unwrap(); + + (proof, public_inputs) +} + +pub fn run_trace_bench( + group: &mut BenchmarkGroup<'_, WallTime>, + benchname: &str, + program_path: &str, +) { + let program_content = std::fs::read(program_path).unwrap(); + + let (register_states, memory, program_size, range_check_builtin_range) = run_program( + None, + CairoLayout::Small, + &program_content, + &CairoVersion::V0, + ) + .unwrap(); + + let mut pub_inputs = build_pub_inputs( + range_check_builtin_range, + &None, + ®ister_states, + &memory, + program_size, + ); + + group.bench_function(benchname, |bench| { + bench.iter(|| black_box(build_main_trace(®ister_states, &memory, &mut pub_inputs))); + }); +} diff --git a/benches/criterion_verifier.rs b/benches/criterion_verifier.rs index bb283a04..51d17a72 100644 --- a/benches/criterion_verifier.rs +++ b/benches/criterion_verifier.rs @@ -1,31 +1,13 @@ use criterion::{ - black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, + criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, }; -use lambdaworks_math::{ - field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, traits::Deserializable, -}; -use lambdaworks_stark::{ - cairo::air::{verify_cairo_proof, PublicInputs}, - starks::proof::{ - options::{ProofOptions, SecurityLevel}, - stark::StarkProof, - }, -}; - -pub mod functions; - -fn load_proof_and_pub_inputs(input_path: &str) -> (StarkProof, PublicInputs) { - let program_content = std::fs::read(input_path).unwrap(); - let mut bytes = program_content.as_slice(); - let proof_len = usize::from_be_bytes(bytes[0..8].try_into().unwrap()); - bytes = &bytes[8..]; - let proof = StarkProof::::deserialize(&bytes[0..proof_len]).unwrap(); - bytes = &bytes[proof_len..]; +use criterion_utils::utils::run_verifier_bench_with_security_level; +use functions::path::cairo0_proof_path; - let public_inputs = PublicInputs::deserialize(bytes).unwrap(); +use lambdaworks_stark::starks::proof::options::SecurityLevel; - (proof, public_inputs) -} +pub mod criterion_utils; +pub mod functions; fn verifier_benches(c: &mut Criterion) { #[cfg(feature = "parallel")] @@ -54,23 +36,17 @@ fn verifier_benches(c: &mut Criterion) { ); } -fn cairo0_proof_path(program_name: &str) -> String { - const CARGO_DIR: &str = env!("CARGO_MANIFEST_DIR"); - const PROGRAM_BASE_REL_PATH: &str = "/benches/proofs/"; - let program_base_path = CARGO_DIR.to_string() + PROGRAM_BASE_REL_PATH; - program_base_path + program_name -} - fn run_verifier_bench( group: &mut BenchmarkGroup<'_, WallTime>, benchname: &str, program_path: &str, ) { - let (proof, pub_inputs) = load_proof_and_pub_inputs(program_path); - let proof_options = ProofOptions::new_secure(SecurityLevel::Provable80Bits, 3); - group.bench_function(benchname, |bench| { - bench.iter(|| black_box(verify_cairo_proof(&proof, &pub_inputs, &proof_options))); - }); + run_verifier_bench_with_security_level( + group, + benchname, + program_path, + SecurityLevel::Conjecturable80Bits, + ); } criterion_group!(benches, verifier_benches); diff --git a/benches/criterion_verifier_70k.rs b/benches/criterion_verifier_70k.rs index 12f3ccd5..63ad4196 100644 --- a/benches/criterion_verifier_70k.rs +++ b/benches/criterion_verifier_70k.rs @@ -1,32 +1,13 @@ use criterion::{ - black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, - SamplingMode, + criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, SamplingMode, }; -use lambdaworks_math::{ - field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, traits::Deserializable, -}; -use lambdaworks_stark::{ - cairo::air::{verify_cairo_proof, PublicInputs}, - starks::proof::{ - options::{ProofOptions, SecurityLevel}, - stark::StarkProof, - }, -}; - -pub mod functions; - -fn load_proof_and_pub_inputs(input_path: &str) -> (StarkProof, PublicInputs) { - let program_content = std::fs::read(input_path).unwrap(); - let mut bytes = program_content.as_slice(); - let proof_len = usize::from_be_bytes(bytes[0..8].try_into().unwrap()); - bytes = &bytes[8..]; - let proof = StarkProof::::deserialize(&bytes[0..proof_len]).unwrap(); - bytes = &bytes[proof_len..]; +use criterion_utils::utils::run_verifier_bench_with_security_level; +use functions::path::cairo0_proof_path; - let public_inputs = PublicInputs::deserialize(bytes).unwrap(); +use lambdaworks_stark::starks::proof::options::SecurityLevel; - (proof, public_inputs) -} +pub mod criterion_utils; +pub mod functions; fn verifier_benches(c: &mut Criterion) { #[cfg(feature = "parallel")] @@ -48,27 +29,21 @@ fn verifier_benches(c: &mut Criterion) { run_verifier_bench( &mut group, "fibonacci/70000", - &cairo0_proof_path("fibonacci_70000.proof"), + &cairo0_proof_path("fibonacci_70000_sec.proof"), ); } -fn cairo0_proof_path(program_name: &str) -> String { - const CARGO_DIR: &str = env!("CARGO_MANIFEST_DIR"); - const PROGRAM_BASE_REL_PATH: &str = "/benches/proofs/"; - let program_base_path = CARGO_DIR.to_string() + PROGRAM_BASE_REL_PATH; - program_base_path + program_name -} - fn run_verifier_bench( group: &mut BenchmarkGroup<'_, WallTime>, benchname: &str, program_path: &str, ) { - let (proof, pub_inputs) = load_proof_and_pub_inputs(program_path); - let proof_options = ProofOptions::new_secure(SecurityLevel::Provable80Bits, 3); - group.bench_function(benchname, |bench| { - bench.iter(|| black_box(verify_cairo_proof(&proof, &pub_inputs, &proof_options))); - }); + run_verifier_bench_with_security_level( + group, + benchname, + program_path, + SecurityLevel::Conjecturable128Bits, + ); } criterion_group!(benches, verifier_benches); diff --git a/benches/criterion_verifier_security.rs b/benches/criterion_verifier_security.rs new file mode 100644 index 00000000..7a6b327f --- /dev/null +++ b/benches/criterion_verifier_security.rs @@ -0,0 +1,68 @@ +use criterion::{ + criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, +}; + +use criterion_utils::utils::run_verifier_bench_with_security_level; +use lambdaworks_stark::starks::proof::options::SecurityLevel; + +pub mod criterion_utils; +pub mod functions; + +fn verifier_benches(c: &mut Criterion) { + #[cfg(feature = "parallel")] + { + let num_threads: usize = std::env::var("NUM_THREADS") + .unwrap_or("8".to_string()) + .parse() + .unwrap(); + println!("Running benchmarks using {} threads", num_threads); + rayon::ThreadPoolBuilder::new() + .num_threads(num_threads) + .build_global() + .unwrap(); + }; + + let mut group = c.benchmark_group("VERIFIER"); + run_verifier_bench( + &mut group, + "fibonacci/500", + &cairo0_proof_path("fibonacci_500.proof"), + &cairo0_proof_path("fibonacci_500_sec.proof"), + ); + run_verifier_bench( + &mut group, + "fibonacci/1000", + &cairo0_proof_path("fibonacci_1000.proof"), + &cairo0_proof_path("fibonacci_1000_sec.proof"), + ); +} + +fn cairo0_proof_path(program_name: &str) -> String { + const CARGO_DIR: &str = env!("CARGO_MANIFEST_DIR"); + const PROGRAM_BASE_REL_PATH: &str = "/benches/proofs/"; + let program_base_path = CARGO_DIR.to_string() + PROGRAM_BASE_REL_PATH; + program_base_path + program_name +} + +fn run_verifier_bench( + group: &mut BenchmarkGroup<'_, WallTime>, + benchname: &str, + proof_path: &str, + sec_proof_path: &str, +) { + run_verifier_bench_with_security_level( + group, + &format!("80_bits/{benchname}"), + proof_path, + SecurityLevel::Conjecturable80Bits, + ); + run_verifier_bench_with_security_level( + group, + &format!("128_bits/{benchname}"), + sec_proof_path, + SecurityLevel::Conjecturable128Bits, + ); +} + +criterion_group!(benches, verifier_benches); +criterion_main!(benches); diff --git a/benches/dhat_prover.rs b/benches/dhat_prover.rs new file mode 100644 index 00000000..8ef5b1be --- /dev/null +++ b/benches/dhat_prover.rs @@ -0,0 +1,48 @@ +use functions::path::cairo0_program_path; +use lambdaworks_stark::{cairo::air::generate_cairo_proof, starks::proof::options::SecurityLevel}; + +use crate::functions::stark::generate_prover_args_with_options; + +pub mod functions; + +#[global_allocator] +static ALLOC: dhat::Alloc = dhat::Alloc; + +pub fn main() { + let _profiler = dhat::Profiler::new_heap(); + println!("\nMeasuring prover RAM usage\n"); + + run_prover( + &cairo0_program_path("fibonacci_100.json"), + SecurityLevel::Conjecturable80Bits, + ); + run_prover( + &cairo0_program_path("fibonacci_500.json"), + SecurityLevel::Conjecturable80Bits, + ); + run_prover( + &cairo0_program_path("fibonacci_2000.json"), + SecurityLevel::Conjecturable80Bits, + ); + run_prover( + &cairo0_program_path("fibonacci_5000.json"), + SecurityLevel::Conjecturable80Bits, + ); + run_prover( + &cairo0_program_path("fibonacci_20000.json"), + SecurityLevel::Conjecturable80Bits, + ); +} + +fn run_prover(program_path: &str, security_level: SecurityLevel) { + println!("Generating proof for {}", program_path); + + let (proof_options, main_trace, pub_inputs) = + generate_prover_args_with_options(program_path, security_level); + + let _proof_size = generate_cairo_proof(&main_trace, &pub_inputs, &proof_options); + println!( + "Prover RAM usage: {} bytes", + dhat::HeapStats::get().max_bytes + ); +} diff --git a/benches/functions/mod.rs b/benches/functions/mod.rs index c3514ea0..323c9dae 100644 --- a/benches/functions/mod.rs +++ b/benches/functions/mod.rs @@ -1,2 +1,2 @@ -pub mod cairo; +pub mod path; pub mod stark; diff --git a/benches/functions/cairo.rs b/benches/functions/path.rs similarity index 50% rename from benches/functions/cairo.rs rename to benches/functions/path.rs index fe7e4f1e..4d570d43 100644 --- a/benches/functions/cairo.rs +++ b/benches/functions/path.rs @@ -4,3 +4,10 @@ pub fn cairo0_program_path(program_name: &str) -> String { let program_base_path = CARGO_DIR.to_string() + PROGRAM_BASE_REL_PATH; program_base_path + program_name } + +pub fn cairo0_proof_path(program_name: &str) -> String { + const CARGO_DIR: &str = env!("CARGO_MANIFEST_DIR"); + const PROGRAM_BASE_REL_PATH: &str = "/benches/proofs/"; + let program_base_path = CARGO_DIR.to_string() + PROGRAM_BASE_REL_PATH; + program_base_path + program_name +} diff --git a/benches/functions/stark.rs b/benches/functions/stark.rs index 0f475018..44437366 100644 --- a/benches/functions/stark.rs +++ b/benches/functions/stark.rs @@ -1,4 +1,16 @@ -use lambdaworks_stark::cairo::{cairo_mem::CairoMemory, register_states::RegisterStates}; +use lambdaworks_math::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField; +use lambdaworks_stark::{ + cairo::{ + air::PublicInputs, + cairo_mem::CairoMemory, + register_states::RegisterStates, + runner::run::{generate_prover_args, CairoVersion}, + }, + starks::{ + proof::options::{ProofOptions, SecurityLevel}, + trace::TraceTable, + }, +}; pub fn generate_cairo_trace(filename: &str) -> (RegisterStates, CairoMemory) { let base_dir = env!("CARGO_MANIFEST_DIR").to_string() + "/src/cairo_vm/test_data/"; @@ -12,3 +24,14 @@ pub fn generate_cairo_trace(filename: &str) -> (RegisterStates, CairoMemory) { (register_states, memory) } + +pub fn generate_prover_args_with_options( + program_path: &str, + security_level: SecurityLevel, +) -> (ProofOptions, TraceTable, PublicInputs) { + let program_content = std::fs::read(program_path).unwrap(); + let proof_options = ProofOptions::new_secure(security_level, 3); + let (main_trace, pub_inputs) = + generate_prover_args(&program_content, &CairoVersion::V0, &None).unwrap(); + (proof_options, main_trace, pub_inputs) +} diff --git a/benches/proofs/fibonacci_100.proof b/benches/proofs/fibonacci_100.proof new file mode 100644 index 00000000..53426696 Binary files /dev/null and b/benches/proofs/fibonacci_100.proof differ diff --git a/benches/proofs/fibonacci_1000.proof b/benches/proofs/fibonacci_1000.proof index f2696069..94d7f60c 100644 Binary files a/benches/proofs/fibonacci_1000.proof and b/benches/proofs/fibonacci_1000.proof differ diff --git a/benches/proofs/fibonacci_10000.proof b/benches/proofs/fibonacci_10000.proof new file mode 100644 index 00000000..1b6d821d Binary files /dev/null and b/benches/proofs/fibonacci_10000.proof differ diff --git a/benches/proofs/fibonacci_1000_sec.proof b/benches/proofs/fibonacci_1000_sec.proof new file mode 100644 index 00000000..59ea2961 Binary files /dev/null and b/benches/proofs/fibonacci_1000_sec.proof differ diff --git a/benches/proofs/fibonacci_100_sec.proof b/benches/proofs/fibonacci_100_sec.proof new file mode 100644 index 00000000..d5b15bc7 Binary files /dev/null and b/benches/proofs/fibonacci_100_sec.proof differ diff --git a/benches/proofs/fibonacci_2000.proof b/benches/proofs/fibonacci_2000.proof new file mode 100644 index 00000000..776cc0df Binary files /dev/null and b/benches/proofs/fibonacci_2000.proof differ diff --git a/benches/proofs/fibonacci_20000.proof b/benches/proofs/fibonacci_20000.proof new file mode 100644 index 00000000..a625aa0f Binary files /dev/null and b/benches/proofs/fibonacci_20000.proof differ diff --git a/benches/proofs/fibonacci_20000_sec.proof b/benches/proofs/fibonacci_20000_sec.proof new file mode 100644 index 00000000..0cf38bf7 Binary files /dev/null and b/benches/proofs/fibonacci_20000_sec.proof differ diff --git a/benches/proofs/fibonacci_2000_sec.proof b/benches/proofs/fibonacci_2000_sec.proof new file mode 100644 index 00000000..f4ce0de7 Binary files /dev/null and b/benches/proofs/fibonacci_2000_sec.proof differ diff --git a/benches/proofs/fibonacci_500.proof b/benches/proofs/fibonacci_500.proof index e934ef66..79ef3792 100644 Binary files a/benches/proofs/fibonacci_500.proof and b/benches/proofs/fibonacci_500.proof differ diff --git a/benches/proofs/fibonacci_5000.proof b/benches/proofs/fibonacci_5000.proof new file mode 100644 index 00000000..5a4b0799 Binary files /dev/null and b/benches/proofs/fibonacci_5000.proof differ diff --git a/benches/proofs/fibonacci_5000_sec.proof b/benches/proofs/fibonacci_5000_sec.proof new file mode 100644 index 00000000..293bf1db Binary files /dev/null and b/benches/proofs/fibonacci_5000_sec.proof differ diff --git a/benches/proofs/fibonacci_500_sec.proof b/benches/proofs/fibonacci_500_sec.proof new file mode 100644 index 00000000..e1f7eca9 Binary files /dev/null and b/benches/proofs/fibonacci_500_sec.proof differ diff --git a/benches/proofs/fibonacci_70000.proof b/benches/proofs/fibonacci_70000.proof deleted file mode 100644 index 99813bb8..00000000 Binary files a/benches/proofs/fibonacci_70000.proof and /dev/null differ diff --git a/benches/proofs/fibonacci_70000_sec.proof b/benches/proofs/fibonacci_70000_sec.proof new file mode 100644 index 00000000..f2e71f6e Binary files /dev/null and b/benches/proofs/fibonacci_70000_sec.proof differ diff --git a/cairo_programs/cairo0/.gitignore b/cairo_programs/cairo0/.gitignore index a6c57f5f..790706e7 100644 --- a/cairo_programs/cairo0/.gitignore +++ b/cairo_programs/cairo0/.gitignore @@ -1 +1,3 @@ *.json +*.trace +*.memory diff --git a/cairo_programs/cairo0/fibonacci_2000.cairo b/cairo_programs/cairo0/fibonacci_2000.cairo new file mode 100644 index 00000000..73e1c207 --- /dev/null +++ b/cairo_programs/cairo0/fibonacci_2000.cairo @@ -0,0 +1,19 @@ +func main() { + // Call fib(1, 1, 2000). + let result: felt = fib(1, 1, 2000); + + // Make sure the 2000th Fibonacci number is 2488789449880543025993160600549605444069072274860741457936217360575764513035. + assert result = 2488789449880543025993160600549605444069072274860741457936217360575764513035; + ret; +} + +func fib(first_element, second_element, n) -> (res: felt) { + jmp fib_body if n != 0; + tempvar result = second_element; + return (second_element,); + + fib_body: + tempvar y = first_element + second_element; + return fib(second_element, y, n - 1); +} + diff --git a/cairo_programs/cairo0/fibonacci_20000.cairo b/cairo_programs/cairo0/fibonacci_20000.cairo new file mode 100644 index 00000000..cf10f9c9 --- /dev/null +++ b/cairo_programs/cairo0/fibonacci_20000.cairo @@ -0,0 +1,19 @@ +func main() { + // Call fib(1, 1, 20000). + let result: felt = fib(1, 1, 20000); + + // Make sure the 20000th Fibonacci number is 2466807170979865163394825981097502788813037637400933247258687309819819983611. + assert result = 2466807170979865163394825981097502788813037637400933247258687309819819983611; + ret; +} + +func fib(first_element, second_element, n) -> (res: felt) { + jmp fib_body if n != 0; + tempvar result = second_element; + return (second_element,); + + fib_body: + tempvar y = first_element + second_element; + return fib(second_element, y, n - 1); +} + diff --git a/cairo_programs/cairo0/fibonacci_5000.cairo b/cairo_programs/cairo0/fibonacci_5000.cairo new file mode 100644 index 00000000..a36e4db8 --- /dev/null +++ b/cairo_programs/cairo0/fibonacci_5000.cairo @@ -0,0 +1,19 @@ +func main() { + // Call fib(1, 1, 5000). + let result: felt = fib(1, 1, 5000); + + // Make sure the 5000th Fibonacci number is 529451847554265076639542422726571337094129671408026182306040934618673389596. + assert result = 529451847554265076639542422726571337094129671408026182306040934618673389596; + ret; +} + +func fib(first_element, second_element, n) -> (res: felt) { + jmp fib_body if n != 0; + tempvar result = second_element; + return (second_element,); + + fib_body: + tempvar y = first_element + second_element; + return fib(second_element, y, n - 1); +} + diff --git a/cairo_programs/cairo1/.gitignore b/cairo_programs/cairo1/.gitignore new file mode 100644 index 00000000..992dfe5a --- /dev/null +++ b/cairo_programs/cairo1/.gitignore @@ -0,0 +1,2 @@ +*.json +*.sierra diff --git a/src/cairo/runner/run.rs b/src/cairo/runner/run.rs index 22bfc5b0..a6e8e90a 100644 --- a/src/cairo/runner/run.rs +++ b/src/cairo/runner/run.rs @@ -252,16 +252,31 @@ pub fn generate_prover_args( let (register_states, memory, program_size, range_check_builtin_range) = run_program(None, cairo_layout, program_content, cairo_version)?; - let memory_segments = create_memory_segment_map(range_check_builtin_range, output_range); - - let mut pub_inputs = - PublicInputs::from_regs_and_mem(®ister_states, &memory, program_size, &memory_segments); + let mut pub_inputs = build_pub_inputs( + range_check_builtin_range, + output_range, + ®ister_states, + &memory, + program_size, + ); let main_trace = build_main_trace(®ister_states, &memory, &mut pub_inputs); Ok((main_trace, pub_inputs)) } +pub fn build_pub_inputs( + range_check_builtin_range: Option>, + output_range: &Option>, + register_states: &RegisterStates, + memory: &CairoMemory, + program_size: usize, +) -> PublicInputs { + let memory_segments = create_memory_segment_map(range_check_builtin_range, output_range); + + PublicInputs::from_regs_and_mem(register_states, memory, program_size, &memory_segments) +} + fn create_memory_segment_map( range_check_builtin_range: Option>, output_range: &Option>, diff --git a/src/main.rs b/src/main.rs index 1f952024..87f92337 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use lambdaworks_math::field::fields::fft_friendly::stark_252_prime_field::Stark2 use lambdaworks_math::traits::{Deserializable, Serializable}; use lambdaworks_stark::cairo::air::{generate_cairo_proof, verify_cairo_proof, PublicInputs}; use lambdaworks_stark::cairo::runner::run::{generate_prover_args, CairoVersion}; -use lambdaworks_stark::starks::proof::options::ProofOptions; +use lambdaworks_stark::starks::proof::options::{ProofOptions, SecurityLevel}; use lambdaworks_stark::starks::proof::stark::StarkProof; use std::env; use std::time::Instant; @@ -32,7 +32,8 @@ fn generate_proof( return None; }; - println!(" Time spent: {:?} \n", timer.elapsed()); + println!(" Time spent: {:?}", timer.elapsed()); + println!(" Generated main trace with {} rows\n", main_trace.n_rows()); let timer = Instant::now(); println!("Making proof ..."); @@ -44,7 +45,10 @@ fn generate_proof( } }; - println!("Time spent in proving: {:?} \n", timer.elapsed()); + println!("Time spent in proving: {:?}", timer.elapsed()); + + let proof_size = proof.serialize().len(); + println!("Proof size: {} bytes\n", proof_size); Some((proof, pub_inputs)) } @@ -69,9 +73,29 @@ fn verify_proof( proof_verified } -fn main() { - let proof_options = ProofOptions::default_test_options(); +fn get_proof_options(arg: Option<&String>) -> Option { + let default_coset_offset = ProofOptions::default_test_options().coset_offset; + + match arg { + Some(arg) => { + if arg == "-s" { + println!("Using secure proof options"); + Some(ProofOptions::new_secure( + SecurityLevel::Conjecturable128Bits, + default_coset_offset, + )) + } else { + None + } + } + None => Some(ProofOptions::new_secure( + SecurityLevel::Conjecturable80Bits, + default_coset_offset, + )), + } +} +fn main() { let args: Vec = env::args().collect(); if args.len() < 2 { @@ -84,12 +108,13 @@ fn main() { match command.as_str() { "prove" => { if args.len() < 4 { - println!("Usage: cargo run prove "); + println!("Usage: cargo run prove [-s]"); return; } let input_path = &args[2]; let output_path = &args[3]; + let Some(proof_options) = get_proof_options(args.get(4)) else { return; }; let Some((proof, pub_inputs)) = generate_proof(input_path, &proof_options) else { return; @@ -109,11 +134,13 @@ fn main() { } "verify" => { if args.len() < 3 { - println!("Usage: cargo run verify "); + println!("Usage: cargo run verify [-s]"); return; } let input_path = &args[2]; + let Some(proof_options) = get_proof_options(args.get(3)) else { return; }; + let Ok(program_content) = std::fs::read(input_path) else { println!("Error opening {input_path} file"); return; @@ -144,11 +171,13 @@ fn main() { } "prove_and_verify" => { if args.len() < 3 { - println!("Usage: cargo run prove_and_verify "); + println!("Usage: cargo run prove_and_verify [-s]"); return; } let input_path = &args[2]; + let Some(proof_options) = get_proof_options(args.get(3)) else { return; }; + let Some((proof, pub_inputs)) = generate_proof(input_path, &proof_options) else { return; };