diff --git a/Cargo.toml b/Cargo.toml index 7b9a54fa..0f2269ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,15 @@ [package] name = "plonky2" +description = "Recursive SNARKs based on Plonk and FRI" version = "0.1.0" -authors = ["Daniel Lubarov "] +authors = ["Daniel Lubarov "] +readme = "README.md" +license = "MIT OR Apache-2.0" +repository = "https://github.com/mir-protocol/plonky2" +keywords = ["cryptography", "SNARK"] +categories = ["cryptography"] edition = "2018" +default-run = "bench_recursion" [dependencies] env_logger = "0.8.3" diff --git a/README.md b/README.md new file mode 100644 index 00000000..f33850e4 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# plonky2 + +TODO: Write a readme... diff --git a/src/bin/bench_field_mul.rs b/src/bin/bench_field_mul.rs new file mode 100644 index 00000000..4a1b7e04 --- /dev/null +++ b/src/bin/bench_field_mul.rs @@ -0,0 +1,21 @@ +use std::time::Instant; + +use plonky2::field::crandall_field::CrandallField; +use plonky2::field::field::Field; + +type F = CrandallField; + +fn main() { + let m = F::from_canonical_u64(12345678901234567890); + let mut x = F::ONE; + let start = Instant::now(); + let num_muls = 2000000000; + for _ in 0..num_muls { + x *= m; + } + let duration = start.elapsed(); + + println!("result {:?}", x); + println!("took {:?}", duration); + println!("avg {:?}ns", duration.as_secs_f64() * 1e9 / (num_muls as f64)); +} diff --git a/src/bin/bench_gmimc.rs b/src/bin/bench_gmimc.rs new file mode 100644 index 00000000..5f7140d7 --- /dev/null +++ b/src/bin/bench_gmimc.rs @@ -0,0 +1,42 @@ +use std::thread; +use std::time::Instant; + +use plonky2::field::crandall_field::CrandallField; +use plonky2::field::field::Field; +use plonky2::gmimc::gmimc_permute_array; +use plonky2::hash::{GMIMC_CONSTANTS, GMIMC_ROUNDS}; + +type F = CrandallField; + +// 113 wire polys, 3 Z polys, 4 parts of quotient poly. +const PROVER_POLYS: usize = 113 + 3 + 4; + +fn main() { + const THREADS: usize = 12; + const LDE_BITS: i32 = 3; + const W: usize = 13; + const HASHES_PER_POLY: usize = 1 << (13 + LDE_BITS); + + let threads = (0..THREADS).map(|_i| { + thread::spawn(move || { + let mut x = [F::ZERO; W]; + for i in 0..W { + x[i] = F::from_canonical_u64((i as u64) * 123456 + 789); + } + + let hashes_per_thread = HASHES_PER_POLY * PROVER_POLYS / THREADS; + let start = Instant::now(); + for _ in 0..hashes_per_thread { + x = gmimc_permute_array::<_, W, GMIMC_ROUNDS>(x, GMIMC_CONSTANTS); + } + let duration = start.elapsed(); + println!("took {:?}", duration); + println!("avg {:?}us", duration.as_secs_f64() * 1e6 / (hashes_per_thread as f64)); + println!("result {:?}", x); + }) + }).collect::>(); + + for t in threads { + t.join().expect("oops"); + } +} diff --git a/src/bin/bench_ldes.rs b/src/bin/bench_ldes.rs new file mode 100644 index 00000000..ecdcd4fb --- /dev/null +++ b/src/bin/bench_ldes.rs @@ -0,0 +1,33 @@ +use std::time::Instant; + +use rayon::prelude::*; + +use plonky2::field::crandall_field::CrandallField; +use plonky2::field::fft; +use plonky2::field::field::Field; +use plonky2::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; + +type F = CrandallField; + +// 113 wire polys, 3 Z polys, 4 parts of quotient poly. +const PROVER_POLYS: usize = 113 + 3 + 4; + +fn main() { + const DEGREE: usize = 1 << 13; + const RATE_BITS: usize = 3; + + let start = Instant::now(); + (0usize..PROVER_POLYS).into_par_iter().for_each(|i| { + let mut values = vec![CrandallField::ZERO; DEGREE]; + for j in 0usize..DEGREE { + values[j] = CrandallField((i * j) as u64); + } + let poly_values = PolynomialValues::new(values); + let start = Instant::now(); + let result = poly_values.lde(RATE_BITS); + let duration = start.elapsed(); + println!("LDE took {:?}", duration); + println!("LDE result: {:?}", result.values[0]); + }); + println!("FFT overall took {:?}", start.elapsed()); +} diff --git a/src/bin/bench_recursion.rs b/src/bin/bench_recursion.rs new file mode 100644 index 00000000..9047d15a --- /dev/null +++ b/src/bin/bench_recursion.rs @@ -0,0 +1,66 @@ +use std::thread; +use std::time::Instant; + +use env_logger::Env; +use rayon::prelude::*; + +use plonky2::circuit_builder::CircuitBuilder; +use plonky2::circuit_data::CircuitConfig; +use plonky2::field::crandall_field::CrandallField; +use plonky2::field::fft; +use plonky2::field::field::Field; +use plonky2::gates::constant::ConstantGate; +use plonky2::gates::gmimc::GMiMCGate; +use plonky2::gmimc::gmimc_permute_array; +use plonky2::hash::{GMIMC_CONSTANTS, GMIMC_ROUNDS}; +use plonky2::polynomial::polynomial::PolynomialCoeffs; +use plonky2::witness::PartialWitness; + +// 113 wire polys, 3 Z polys, 4 parts of quotient poly. +const PROVER_POLYS: usize = 113 + 3 + 4; + +fn main() { + // Set the default log filter. This can be overridden using the `RUST_LOG` environment variable, + // e.g. `RUST_LOG=debug`. + // We default to debug for now, since there aren't many logs anyway, but we should probably + // change this to info or warn later. + env_logger::Builder::from_env(Env::default().default_filter_or("debug")).init(); + + bench_prove::(); + + // bench_field_mul::(); + + // bench_fft(); + println!(); + // bench_gmimc::(); +} + +fn bench_prove() { + let gmimc_gate = GMiMCGate::::with_automatic_constants(); + + let config = CircuitConfig { + num_wires: 120, + num_routed_wires: 12, + security_bits: 128, + rate_bits: 3, + num_checks: 3, + }; + + let mut builder = CircuitBuilder::::new(config); + + for _ in 0..5000 { + builder.add_gate_no_constants(gmimc_gate.clone()); + } + + builder.add_gate(ConstantGate::get(), vec![F::NEG_ONE]); + + // for _ in 0..(40 * 5) { + // builder.add_gate( + // FriConsistencyGate::new(2, 3, 13), + // vec![F::primitive_root_of_unity(13)]); + // } + + let prover = builder.build_prover(); + let inputs = PartialWitness::new(); + prover.prove(inputs); +} diff --git a/src/field/field_search.rs b/src/bin/field_search.rs similarity index 98% rename from src/field/field_search.rs rename to src/bin/field_search.rs index d73c99e6..e0fdb7e6 100644 --- a/src/field/field_search.rs +++ b/src/bin/field_search.rs @@ -1,4 +1,4 @@ -fn field_search() { +fn main() { for deg in (61..=64).rev() { for adic in (28..=32).rev() { for i in 1u128..100000 { diff --git a/src/field/crandall_field.rs b/src/field/crandall_field.rs index 2267e128..55e529a8 100644 --- a/src/field/crandall_field.rs +++ b/src/field/crandall_field.rs @@ -13,7 +13,7 @@ const EPSILON: u64 = 2415919103; /// A field designed for use with the Crandall reduction algorithm. /// /// Its order is -/// ``` +/// ```ignore /// P = 2**64 - EPSILON /// = 2**64 - 9 * 2**28 + 1 /// = 2**28 * (2**36 - 9) + 1 @@ -158,6 +158,7 @@ impl Neg for CrandallField { if self.is_zero() { Self::ZERO } else { + // TODO: This could underflow if we're not canonical. Self(Self::ORDER - self.0) } } @@ -226,7 +227,8 @@ impl DivAssign for CrandallField { } } -/// no final reduction +/// Reduces to a 64-bit value. The result might not be in canonical form; it could be in between the +/// field order and `2^64`. #[inline] fn reduce128(x: u128) -> CrandallField { // This is Crandall's algorithm. When we have some high-order bits (i.e. with a weight of 2^64), @@ -250,5 +252,5 @@ fn split(x: u128) -> (u64, u64) { mod tests { use crate::test_arithmetic; - test_arithmetic!(crate::CrandallField); + test_arithmetic!(crate::field::crandall_field::CrandallField); } diff --git a/src/field/fft.rs b/src/field/fft.rs index d3c98766..05102374 100644 --- a/src/field/fft.rs +++ b/src/field/fft.rs @@ -35,7 +35,7 @@ impl FftPrecomputation { } } -pub(crate) fn fft(poly: PolynomialCoeffs) -> PolynomialValues { +pub fn fft(poly: PolynomialCoeffs) -> PolynomialValues { let precomputation = fft_precompute(poly.len()); fft_with_precomputation_power_of_2(poly, &precomputation) } diff --git a/src/field/field_testing.rs b/src/field/field_testing.rs index ca42a4c2..1a852b73 100644 --- a/src/field/field_testing.rs +++ b/src/field/field_testing.rs @@ -136,7 +136,7 @@ pub fn run_binaryop_test_cases( macro_rules! test_arithmetic { ($field:ty) => { mod arithmetic { - use crate::{Field}; + use crate::field::field::Field; use std::ops::{Add, Mul, Neg, Sub}; // Can be 32 or 64; doesn't have to be computer's actual word diff --git a/src/field/mod.rs b/src/field/mod.rs index f377301d..04db57a2 100644 --- a/src/field/mod.rs +++ b/src/field/mod.rs @@ -1,7 +1,6 @@ -pub(crate) mod crandall_field; -pub(crate) mod field; -pub(crate) mod field_search; -pub(crate) mod fft; +pub mod crandall_field; +pub mod field; +pub mod fft; pub(crate) mod cosets; #[cfg(test)] diff --git a/src/gates/mod.rs b/src/gates/mod.rs index 5e3a7724..f222549d 100644 --- a/src/gates/mod.rs +++ b/src/gates/mod.rs @@ -1,7 +1,7 @@ pub(crate) mod arithmetic; -pub(crate) mod constant; +pub mod constant; pub(crate) mod fri_consistency_gate; pub(crate) mod gate; -pub(crate) mod gmimc; +pub mod gmimc; pub(crate) mod gmimc_eval; pub(crate) mod noop; diff --git a/src/hash.rs b/src/hash.rs index 2f083bd1..9ac7b120 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -13,9 +13,9 @@ pub(crate) const SPONGE_RATE: usize = 8; pub(crate) const SPONGE_CAPACITY: usize = 4; pub(crate) const SPONGE_WIDTH: usize = SPONGE_RATE + SPONGE_CAPACITY; -pub(crate) const GMIMC_ROUNDS: usize = 101; +pub const GMIMC_ROUNDS: usize = 101; /// This is the result of `gmimc_automatic_constants`; i.e. it's from ChaCha20 seeded with 0. -pub(crate) const GMIMC_CONSTANTS: [u64; GMIMC_ROUNDS] = [13080132715619999810, 8594738768332784433, 12896916466795114362, 1109962092924985887, 16216730424513838303, 10137062674532189451, 15292064468290167604, 17255573296743700660, 14827154243383347999, 2846171648262623971, 16246264665335217464, 14214208089399786945, 9667108688411000080, 6470857421371427314, 14103331941574951088, 11854816474757864855, 3498097497657653643, 7947235693333396721, 11110078702363612411, 16384314114341783099, 15404405914224921002, 14077880832148466479, 9555554663682579629, 13859595359622389547, 16859897326779206643, 17685474422023725021, 17858764736437889563, 9410011023624402450, 12495243630852222748, 12416945299436348089, 5776666812952701944, 6314421663507268983, 7402742472177291738, 982536713292517255, 17321168867539521172, 2934354895304883596, 10567510599683852824, 8135543734546633309, 116353493093565855, 8029688164312877009, 9003846638141970076, 7052445133185619935, 9645665433271393194, 5446430061585660707, 16770910636054378912, 17708360573237778662, 4661556288797079635, 11977051900536351292, 4378616569536950472, 3334807503157233344, 8019184736760206441, 2395043909056213726, 6558421058999795722, 11735894061922784518, 8143540539718733269, 5991753490174091591, 12235918792748480378, 2880312033996085535, 18224748117164817283, 18070411014966027790, 8156487614951798795, 10615269511128318233, 12489426406026437595, 5055279340584943685, 7231927320516917417, 2602078848371820415, 12445944370602567717, 3978905924297801117, 16711272946032085229, 10439032362290464320, 15110119873264383151, 821141790739535246, 11073536381779174375, 4866839313593360589, 13118391690850240703, 14527674975242150843, 7612751960041028847, 6808090908507673494, 6899703780195472329, 3664666286710282218, 783179505504239941, 8990689242729919931, 9646603556395461579, 7351246026916028004, 16970959815450893036, 15735726859844361172, 10347018222946250943, 12195545879691602738, 7423314197870213963, 14908016118492485461, 5840340123122280205, 17740311464247702688, 815306422036794512, 17456357369997417977, 6982651077270605698, 11970987325834369417, 8167785009370061651, 9483259820363401119, 954550221761525285, 10339565172077536587, 8651171085167737860]; +pub const GMIMC_CONSTANTS: [u64; GMIMC_ROUNDS] = [13080132715619999810, 8594738768332784433, 12896916466795114362, 1109962092924985887, 16216730424513838303, 10137062674532189451, 15292064468290167604, 17255573296743700660, 14827154243383347999, 2846171648262623971, 16246264665335217464, 14214208089399786945, 9667108688411000080, 6470857421371427314, 14103331941574951088, 11854816474757864855, 3498097497657653643, 7947235693333396721, 11110078702363612411, 16384314114341783099, 15404405914224921002, 14077880832148466479, 9555554663682579629, 13859595359622389547, 16859897326779206643, 17685474422023725021, 17858764736437889563, 9410011023624402450, 12495243630852222748, 12416945299436348089, 5776666812952701944, 6314421663507268983, 7402742472177291738, 982536713292517255, 17321168867539521172, 2934354895304883596, 10567510599683852824, 8135543734546633309, 116353493093565855, 8029688164312877009, 9003846638141970076, 7052445133185619935, 9645665433271393194, 5446430061585660707, 16770910636054378912, 17708360573237778662, 4661556288797079635, 11977051900536351292, 4378616569536950472, 3334807503157233344, 8019184736760206441, 2395043909056213726, 6558421058999795722, 11735894061922784518, 8143540539718733269, 5991753490174091591, 12235918792748480378, 2880312033996085535, 18224748117164817283, 18070411014966027790, 8156487614951798795, 10615269511128318233, 12489426406026437595, 5055279340584943685, 7231927320516917417, 2602078848371820415, 12445944370602567717, 3978905924297801117, 16711272946032085229, 10439032362290464320, 15110119873264383151, 821141790739535246, 11073536381779174375, 4866839313593360589, 13118391690850240703, 14527674975242150843, 7612751960041028847, 6808090908507673494, 6899703780195472329, 3664666286710282218, 783179505504239941, 8990689242729919931, 9646603556395461579, 7351246026916028004, 16970959815450893036, 15735726859844361172, 10347018222946250943, 12195545879691602738, 7423314197870213963, 14908016118492485461, 5840340123122280205, 17740311464247702688, 815306422036794512, 17456357369997417977, 6982651077270605698, 11970987325834369417, 8167785009370061651, 9483259820363401119, 954550221761525285, 10339565172077536587, 8651171085167737860]; /// Controls the granularity of parallelization when building Merkle trees. I.e., we will try to /// split up the task into units of work, such that each unit involves hashing roughly this many diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 00000000..5f840e78 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,22 @@ +pub mod circuit_builder; +pub mod circuit_data; +pub mod vars; +pub mod field; +pub mod fri; +pub mod gadgets; +pub mod gates; +pub mod generator; +pub mod gmimc; +pub mod hash; +pub mod plonk_challenger; +pub mod plonk_common; +pub mod polynomial; +pub mod proof; +pub mod prover; +pub mod recursive_verifier; +pub mod rescue; +pub mod target; +pub mod util; +pub mod verifier; +pub mod wire; +pub mod witness; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 32ae538f..00000000 --- a/src/main.rs +++ /dev/null @@ -1,156 +0,0 @@ -use std::thread; -use std::time::Instant; - -use env_logger::Env; -use rayon::prelude::*; - -use field::crandall_field::CrandallField; -use field::fft; - -use crate::circuit_builder::CircuitBuilder; -use crate::circuit_data::CircuitConfig; -use crate::field::field::Field; -use crate::gates::constant::ConstantGate; -use crate::gates::gmimc::GMiMCGate; -use crate::hash::{GMIMC_CONSTANTS, GMIMC_ROUNDS}; -use crate::polynomial::polynomial::PolynomialCoeffs; -use crate::witness::PartialWitness; - -mod circuit_builder; -mod circuit_data; -mod vars; -mod field; -mod fri; -mod gadgets; -mod gates; -mod generator; -mod gmimc; -mod hash; -mod plonk_challenger; -mod plonk_common; -mod polynomial; -mod proof; -mod prover; -mod recursive_verifier; -mod rescue; -mod target; -mod util; -mod verifier; -mod wire; -mod witness; - -// 112 wire polys, 3 Z polys, 4 parts of quotient poly. -const PROVER_POLYS: usize = 113 + 3 + 4; - -fn main() { - // Set the default log filter. This can be overridden using the `RUST_LOG` environment variable, - // e.g. `RUST_LOG=debug`. - // We default to debug for now, since there aren't many logs anyway, but we should probably - // change this to info or warn later. - env_logger::Builder::from_env(Env::default().default_filter_or("debug")).init(); - - bench_prove::(); - - // bench_field_mul::(); - - // bench_fft(); - println!(); - // bench_gmimc::(); - - // field_search() -} - -fn bench_field_mul() { - let m = F::from_canonical_u64(12345678901234567890); - let mut x = F::ONE; - let start = Instant::now(); - let num_muls = 2000000000; - for _ in 0..num_muls { - x *= m; - } - let duration = start.elapsed(); - println!("result {:?}", x); - println!("took {:?}", duration); - println!("avg {:?}ns", duration.as_secs_f64() * 1e9 / (num_muls as f64)); -} - -fn bench_prove() { - let gmimc_gate = GMiMCGate::::with_automatic_constants(); - - let config = CircuitConfig { - num_wires: 120, - num_routed_wires: 12, - security_bits: 128, - rate_bits: 3, - num_checks: 3, - }; - - let mut builder = CircuitBuilder::::new(config); - - for _ in 0..5000 { - builder.add_gate_no_constants(gmimc_gate.clone()); - } - - builder.add_gate(ConstantGate::get(), vec![F::NEG_ONE]); - - // for _ in 0..(40 * 5) { - // builder.add_gate( - // FriConsistencyGate::new(2, 3, 13), - // vec![F::primitive_root_of_unity(13)]); - // } - - let prover = builder.build_prover(); - let inputs = PartialWitness::new(); - prover.prove(inputs); -} - -fn bench_gmimc() { - const THREADS: usize = 12; - const LDE_BITS: i32 = 3; - const W: usize = 13; - let hashes_per_poly = 1 << (13 + LDE_BITS); - let threads = (0..THREADS).map(|_i| { - thread::spawn(move || { - let mut x = [F::ZERO; W]; - for i in 0..W { - x[i] = F::from_canonical_u64((i as u64) * 123456 + 789); - } - - let hashes_per_thread = hashes_per_poly * PROVER_POLYS / THREADS; - let start = Instant::now(); - for _ in 0..hashes_per_thread { - x = gmimc::gmimc_permute_array::<_, W, GMIMC_ROUNDS>(x, GMIMC_CONSTANTS); - } - let duration = start.elapsed(); - println!("took {:?}", duration); - println!("avg {:?}us", duration.as_secs_f64() * 1e6 / (hashes_per_thread as f64)); - println!("result {:?}", x); - }) - }).collect::>(); - - for t in threads { - t.join().expect("oops"); - } -} - -fn bench_fft() { - let degree = 1 << 13; - let lde_bits = 3; - let lde_size = degree << lde_bits; - println!("{} << {} = {}", degree, lde_bits, lde_size); - - let start = Instant::now(); - (0usize..PROVER_POLYS).into_par_iter().for_each(|i| { - let mut coeffs = vec![CrandallField::ZERO; lde_size]; - for j in 0usize..lde_size { - coeffs[j] = CrandallField((i * j) as u64); - } - - let start = Instant::now(); - let result = fft::fft(PolynomialCoeffs { coeffs }); - let duration = start.elapsed(); - println!("FFT took {:?}", duration); - println!("FFT result: {:?}", result.values[0]); - }); - println!("FFT overall took {:?}", start.elapsed()); -} diff --git a/src/polynomial/mod.rs b/src/polynomial/mod.rs index 1e8fe6ed..2c7f7076 100644 --- a/src/polynomial/mod.rs +++ b/src/polynomial/mod.rs @@ -1,2 +1,2 @@ pub(crate) mod division; -pub(crate) mod polynomial; +pub mod polynomial; diff --git a/src/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index 6a96b072..cc9a4852 100644 --- a/src/polynomial/polynomial.rs +++ b/src/polynomial/polynomial.rs @@ -7,12 +7,12 @@ use crate::util::log2_strict; /// The points are implicitly `g^i`, where `g` generates the subgroup whose size equals the number /// of points. #[derive(Clone, Debug, Eq, PartialEq)] -pub(crate) struct PolynomialValues { - pub(crate) values: Vec, +pub struct PolynomialValues { + pub values: Vec, } impl PolynomialValues { - pub(crate) fn new(values: Vec) -> Self { + pub fn new(values: Vec) -> Self { assert!(values.len().is_power_of_two()); PolynomialValues { values } } @@ -35,7 +35,7 @@ impl PolynomialValues { .collect() } - pub(crate) fn lde(self, rate_bits: usize) -> Self { + pub fn lde(self, rate_bits: usize) -> Self { let mut coeffs = ifft(self).lde(rate_bits); fft(coeffs) } @@ -43,12 +43,12 @@ impl PolynomialValues { /// A polynomial in coefficient form. The number of coefficients must be a power of two. #[derive(Clone, Debug, Eq, PartialEq)] -pub(crate) struct PolynomialCoeffs { +pub struct PolynomialCoeffs { pub(crate) coeffs: Vec, } impl PolynomialCoeffs { - pub(crate) fn new(coeffs: Vec) -> Self { + pub fn new(coeffs: Vec) -> Self { assert!(coeffs.len().is_power_of_two()); PolynomialCoeffs { coeffs } }