diff --git a/.gitignore b/.gitignore index c51275b..b11f553 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,6 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk -caulk_multi_lookup/target -caulk_single_opening/target \ No newline at end of file +srs/ +polys/ +tmp/ diff --git a/Cargo.toml b/Cargo.toml index 998ce05..8137e38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,41 @@ -[workspace] +[package] +name = "caulk" +version = "0.1.0" +authors = ["mmaller "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ark-ff = { version = "^0.3.0", default-features = false } +ark-ec = { version = "^0.3.0", default-features = false } +ark-serialize = { version = "^0.3.0", default-features = false, features = [ "derive" ] } +ark-poly = { version = "^0.3.0", default-features = false } +ark-std = { version = "^0.3.0", default-features = false } +ark-r1cs-std = { version = "^0.3.0", default-features = false, optional = true } +ark-bls12-381 = { version = "^0.3.0", features = [ "std" ] } +ark-bls12-377 = { version = "^0.3.0", features = [ "std" ] } +ark-poly-commit = { version = "^0.3.0", default-features = false } + +tracing = { version = "0.1", default-features = false, features = [ "attributes" ], optional = true } +derivative = { version = "2.0", features = ["use_core"], optional = true} +rand = "0.8.5" +rand_chacha = { version = "0.3.1" } +thiserror = "1.0.19" +blake2s_simd = "1.0.0" + +rayon = { version = "1.5.2", default-features = false, optional = true } +merlin = { version = "3.0.0" } + +[features] +asm = [ "ark-ff/asm" ] +parallel = [ + "rayon", + "ark-std/parallel", + "ark-ff/parallel", + "ark-poly/parallel" + ] +print-trace = [ + "ark-std/print-trace" +] -members = [ - "caulk_multi_lookup", - "caulk_single_opening" -] \ No newline at end of file diff --git a/caulk_multi_lookup/Cargo.toml b/caulk_multi_lookup/Cargo.toml deleted file mode 100644 index 3542dca..0000000 --- a/caulk_multi_lookup/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "caulk_multi_lookup" -authors = ["mmaller ", "khovratovich "] -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -ark-ff = { version = "^0.3.0", default-features = false } -ark-ec = { version = "^0.3.0", default-features = false } -ark-serialize = { version = "^0.3.0", default-features = false, features = [ "derive" ] } -ark-poly = { version = "^0.3.0", default-features = false } -ark-std = { version = "^0.3.0", default-features = false } -ark-relations = { version = "^0.3.0", default-features = false } -ark-crypto-primitives = { version = "^0.3.0", default-features = false } -ark-r1cs-std = { version = "^0.3.0", default-features = false, optional = true } -ark-bls12-381 = { version = "^0.3.0", features = [ "std" ] } -ark-poly-commit = { version = "^0.3.0", default-features = false } -ark-marlin = { version = "^0.3.0", default-features = false } - -tracing = { version = "0.1", default-features = false, features = [ "attributes" ], optional = true } -derivative = { version = "2.0", features = ["use_core"], optional = true} -rand = "0.7.3" -rand_chacha = { version = "0.2.1" } -thiserror = "1.0.19" -blake2s_simd = "0.5.10" - - -[features] -asm = [ "ark-ff/asm" ] diff --git a/caulk_multi_lookup/src/caulk_multi_setup.rs b/caulk_multi_lookup/src/caulk_multi_setup.rs deleted file mode 100644 index cd5888b..0000000 --- a/caulk_multi_lookup/src/caulk_multi_setup.rs +++ /dev/null @@ -1,382 +0,0 @@ -/* -This file includes the setup algorithm for Caulk with multi openings. -A full description of the setup is not formally given in the paper. -*/ - -use ark_bls12_381::{Bls12_381, Fr, FrParameters, G1Affine, G2Affine}; -use ark_ec::{bls12::Bls12, AffineCurve, PairingEngine, ProjectiveCurve}; -use ark_ff::{Fp256, UniformRand}; -use ark_poly::{ - EvaluationDomain, Evaluations as EvaluationsOnDomain, GeneralEvaluationDomain, UVPolynomial, -}; -use ark_poly_commit::kzg10::*; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; - -use crate::tools::{KzgBls12_381, UniPoly381}; -use ark_std::{cfg_into_iter, One, Zero}; -use std::{fs::File, io::Read, time::Instant}; - -// structure of public parameters -#[allow(non_snake_case)] -pub struct PublicParameters { - pub poly_ck: Powers<'static, Bls12>, - pub poly_vk: VerifierKey>, - pub domain_m: GeneralEvaluationDomain, - pub domain_n: GeneralEvaluationDomain, - pub domain_N: GeneralEvaluationDomain, - pub verifier_pp: VerifierPublicParameters, - pub lagrange_polynomials_n: Vec, - pub lagrange_polynomials_m: Vec, - pub id_poly: UniPoly381, - pub N: usize, - pub m: usize, - pub n: usize, - pub g2_powers: Vec, -} - -pub struct LookupParameters { - m: usize, - lagrange_polynomials_m: Vec, - domain_m: GeneralEvaluationDomain, - id_poly: UniPoly381, -} - -impl PublicParameters { - pub fn regenerate_lookup_params(&mut self, m: usize) { - let lp = generate_lookup_params(m); - self.m = lp.m; - self.lagrange_polynomials_m = lp.lagrange_polynomials_m; - self.domain_m = lp.domain_m; - self.id_poly = lp.id_poly; - } - - //store powers of g in a file - pub fn store(&self, path: &str) { - use std::io::Write; - - //1. Powers of g - let mut g_bytes = vec![]; - let mut f = File::create(path).expect("Unable to create file"); - let deg: u32 = self.poly_ck.powers_of_g.len().try_into().unwrap(); - let deg_bytes = deg.to_be_bytes(); - f.write_all(°_bytes).expect("Unable to write data"); - let deg32: usize = deg.try_into().unwrap(); - for i in 0..deg32 { - self.poly_ck.powers_of_g[i] - .into_projective() - .into_affine() - .serialize_uncompressed(&mut g_bytes) - .ok(); - } - f.write_all(&g_bytes).expect("Unable to write data"); - - //2. Powers of gammag - let deg_gamma: u32 = self.poly_ck.powers_of_gamma_g.len().try_into().unwrap(); - let mut gg_bytes = vec![]; - let deg_bytes = deg_gamma.to_be_bytes(); - f.write_all(°_bytes).expect("Unable to write data"); - let deg32: usize = deg.try_into().unwrap(); - for i in 0..deg32 { - self.poly_ck.powers_of_gamma_g[i] - .into_projective() - .into_affine() - .serialize_uncompressed(&mut gg_bytes) - .ok(); - } - f.write_all(&gg_bytes).expect("Unable to write data"); - - //3. Verifier key - let mut h_bytes = vec![]; - self.poly_vk.h.serialize_uncompressed(&mut h_bytes).ok(); - self.poly_vk - .beta_h - .serialize_uncompressed(&mut h_bytes) - .ok(); - f.write_all(&h_bytes).expect("Unable to write data"); - - //4. g2 powers - let mut g2_bytes = vec![]; - let deg2: u32 = self.g2_powers.len().try_into().unwrap(); - let deg2_bytes = deg2.to_be_bytes(); - f.write_all(°2_bytes).expect("Unable to write data"); - let deg2_32: usize = deg2.try_into().unwrap(); - for i in 0..deg2_32 { - self.g2_powers[i] - .into_projective() - .into_affine() - .serialize_uncompressed(&mut g2_bytes) - .ok(); - } - f.write_all(&g2_bytes).expect("Unable to write data"); - } - - //load powers of g from a file - pub fn load( - path: &str, - ) -> ( - Powers<'static, Bls12>, - VerifierKey>, - Vec, - ) { - const G1_UNCOMPR_SIZE: usize = 96; - const G2_UNCOMPR_SIZE: usize = 192; - let mut data = Vec::new(); - let mut f = File::open(path).expect("Unable to open file"); - f.read_to_end(&mut data).expect("Unable to read data"); - - //1. reading g powers - let mut cur_counter: usize = 0; - let deg_bytes: [u8; 4] = (&data[0..4]).try_into().unwrap(); - let deg: u32 = u32::from_be_bytes(deg_bytes); - let mut powers_of_g = vec![]; - let deg32: usize = deg.try_into().unwrap(); - cur_counter += 4; - for i in 0..deg32 { - let buf_bytes = - &data[cur_counter + i * G1_UNCOMPR_SIZE..cur_counter + (i + 1) * G1_UNCOMPR_SIZE]; - let tmp = G1Affine::deserialize_unchecked(buf_bytes).unwrap(); - powers_of_g.push(tmp); - } - cur_counter += deg32 * G1_UNCOMPR_SIZE; - - //2. reading gamma g powers - let deg_bytes: [u8; 4] = (&data[cur_counter..cur_counter + 4]).try_into().unwrap(); - let deg: u32 = u32::from_be_bytes(deg_bytes); - let mut powers_of_gamma_g = vec![]; - let deg32: usize = deg.try_into().unwrap(); - cur_counter += 4; - for i in 0..deg32 { - let buf_bytes = - &data[cur_counter + i * G1_UNCOMPR_SIZE..cur_counter + (i + 1) * G1_UNCOMPR_SIZE]; - let tmp = G1Affine::deserialize_unchecked(buf_bytes).unwrap(); - powers_of_gamma_g.push(tmp); - } - cur_counter += deg32 * G1_UNCOMPR_SIZE; - - //3. reading verifier key - let buf_bytes = &data[cur_counter..cur_counter + G2_UNCOMPR_SIZE]; - let h = G2Affine::deserialize_unchecked(buf_bytes).unwrap(); - cur_counter += G2_UNCOMPR_SIZE; - let buf_bytes = &data[cur_counter..cur_counter + G2_UNCOMPR_SIZE]; - let beta_h = G2Affine::deserialize_unchecked(buf_bytes).unwrap(); - cur_counter += G2_UNCOMPR_SIZE; - - //4. reading G2 powers - let deg2_bytes: [u8; 4] = (&data[cur_counter..cur_counter + 4]).try_into().unwrap(); - let deg2: u32 = u32::from_be_bytes(deg2_bytes); - let mut g2_powers = vec![]; - let deg2_32: usize = deg2.try_into().unwrap(); - cur_counter += 4; - for _ in 0..deg2_32 { - let buf_bytes = &data[cur_counter..cur_counter + G2_UNCOMPR_SIZE]; - let tmp = G2Affine::deserialize_unchecked(buf_bytes).unwrap(); - g2_powers.push(tmp); - cur_counter += G2_UNCOMPR_SIZE; - } - - let vk = VerifierKey { - g: powers_of_g[0].clone(), - gamma_g: powers_of_gamma_g[0].clone(), - h: h, - beta_h: beta_h, - prepared_h: h.into(), - prepared_beta_h: beta_h.into(), - }; - - let powers = Powers { - powers_of_g: ark_std::borrow::Cow::Owned(powers_of_g), - powers_of_gamma_g: ark_std::borrow::Cow::Owned(powers_of_gamma_g), - }; - - (powers, vk, g2_powers) - } -} - -// smaller set of public parameters used by verifier -pub struct VerifierPublicParameters { - pub poly_vk: VerifierKey>, - pub domain_m_size: usize, -} - -fn generate_lookup_params(m: usize) -> LookupParameters { - let domain_m: GeneralEvaluationDomain = GeneralEvaluationDomain::new(m.clone()).unwrap(); - - // id_poly(X) = 1 for omega_m in range and 0 for omega_m not in range. - let mut id_vec = Vec::new(); - for _ in 0..m.clone() { - id_vec.push(Fr::one()); - } - for _ in m.clone()..domain_m.size() { - id_vec.push(Fr::zero()); - } - let id_poly = EvaluationsOnDomain::from_vec_and_domain(id_vec, domain_m).interpolate(); - let mut lagrange_polynomials_m: Vec = Vec::new(); - - for i in 0..domain_m.size() { - let evals: Vec> = cfg_into_iter!(0..domain_m.size()) - .map(|k| if k == i { Fr::one() } else { Fr::zero() }) - .collect(); - lagrange_polynomials_m - .push(EvaluationsOnDomain::from_vec_and_domain(evals, domain_m).interpolate()); - } - - return LookupParameters { - m: m, - lagrange_polynomials_m: lagrange_polynomials_m, - domain_m: domain_m, - id_poly: id_poly, - }; -} - -// Reduces full srs down to smaller srs for smaller polynomials -// Copied from arkworks library (where same function is private) -fn trim>( - srs: UniversalParams, - mut supported_degree: usize, -) -> (Powers<'static, E>, VerifierKey) { - if supported_degree == 1 { - supported_degree += 1; - } - let pp = srs.clone(); - let powers_of_g = pp.powers_of_g[..=supported_degree].to_vec(); - let powers_of_gamma_g = (0..=supported_degree) - .map(|i| pp.powers_of_gamma_g[&i]) - .collect(); - - let powers = Powers { - powers_of_g: ark_std::borrow::Cow::Owned(powers_of_g), - powers_of_gamma_g: ark_std::borrow::Cow::Owned(powers_of_gamma_g), - }; - let vk = VerifierKey { - g: pp.powers_of_g[0], - gamma_g: pp.powers_of_gamma_g[&0], - h: pp.h, - beta_h: pp.beta_h, - prepared_h: pp.prepared_h.clone(), - prepared_beta_h: pp.prepared_beta_h.clone(), - }; - (powers, vk) -} - -// setup algorithm for index_hiding_polycommit -// also includes a bunch of precomputation. -// @max_degree max degree of table polynomial C(X), also the size of the trusted setup -// @N domain size on which proofs are constructed. Should not be smaller than max_degree -// @m lookup size. Can be changed later -// @n suppl domain for the unity proofs. Should be at least 6+log N -#[allow(non_snake_case)] -pub fn setup_multi_lookup(max_degree: &usize, N: &usize, m: &usize, n: &usize) -> PublicParameters { - let rng = &mut ark_std::test_rng(); - - // Setup algorithm. To be replaced by output of a universal setup before being production ready. - - //let mut srs = KzgBls12_381::setup(4, true, rng).unwrap(); - let poly_ck: Powers<'static, Bls12>; - let poly_vk: VerifierKey>; - let mut g2_powers: Vec = Vec::new(); - - //try opening the file. If it exists load the setup from there, otherwise generate - let path = format!("srs/srs_{}.setup", max_degree); - let res = File::open(path.clone()); - let store_to_file: bool; - match res { - Ok(_) => { - let now = Instant::now(); - let (_poly_ck, _poly_vk, _g2_powers) = PublicParameters::load(&path); - println!("time to load powers = {:?}", now.elapsed()); - store_to_file = false; - g2_powers = _g2_powers; - poly_ck = _poly_ck; - poly_vk = _poly_vk; - } - Err(_) => { - let now = Instant::now(); - let srs = KzgBls12_381::setup(max_degree.clone(), true, rng).unwrap(); - println!("time to setup powers = {:?}", now.elapsed()); - - // trim down to size - let (poly_ck2, poly_vk2) = trim::(srs, max_degree.clone()); - poly_ck = Powers { - powers_of_g: ark_std::borrow::Cow::Owned(poly_ck2.powers_of_g.into()), - powers_of_gamma_g: ark_std::borrow::Cow::Owned(poly_ck2.powers_of_gamma_g.into()), - }; - poly_vk = poly_vk2; - - // need some powers of g2 - // arkworks setup doesn't give these powers but the setup does use a fixed randomness to generate them. - // so we can generate powers of g2 directly. - let rng = &mut ark_std::test_rng(); - let beta: Fp256 = Fr::rand(rng); - let mut temp = poly_vk.h.clone(); - - for _ in 0..poly_ck.powers_of_g.len() { - g2_powers.push(temp.clone()); - temp = temp.mul(beta).into_affine(); - } - - store_to_file = true; - } - } - - // domain where openings {w_i}_{i in I} are embedded - let domain_n: GeneralEvaluationDomain = GeneralEvaluationDomain::new(n.clone()).unwrap(); - let domain_N: GeneralEvaluationDomain = GeneralEvaluationDomain::new(N.clone()).unwrap(); - - // precomputation to speed up prover - // lagrange_polynomials[i] = polynomial equal to 0 at w^j for j!= i and 1 at w^i - let mut lagrange_polynomials_n: Vec = Vec::new(); - - for i in 0..domain_n.size() { - let evals: Vec> = cfg_into_iter!(0..domain_n.size()) - .map(|k| if k == i { Fr::one() } else { Fr::zero() }) - .collect(); - lagrange_polynomials_n - .push(EvaluationsOnDomain::from_vec_and_domain(evals, domain_n).interpolate()); - } - - let lp = generate_lookup_params(m.clone()); - - let verifier_pp = VerifierPublicParameters { - poly_vk: poly_vk.clone(), - domain_m_size: lp.domain_m.size(), - }; - - let pp = PublicParameters { - poly_ck: poly_ck, - domain_m: lp.domain_m, - domain_n: domain_n, - lagrange_polynomials_n: lagrange_polynomials_n, - lagrange_polynomials_m: lp.lagrange_polynomials_m, - id_poly: lp.id_poly, - domain_N: domain_N, - poly_vk: poly_vk, - verifier_pp: verifier_pp, - N: N.clone(), - n: n.clone(), - m: lp.m.clone(), - g2_powers: g2_powers.clone(), - }; - if store_to_file { - pp.store(&path); - } - return pp; -} - -#[test] -#[allow(non_snake_case)] -pub fn test_load() { - let n: usize = 4; - let N: usize = 1 << n; - let powers_size: usize = 4 * N; //SRS SIZE - let temp_m = n; //dummy - let pp = setup_multi_lookup(&powers_size, &N, &temp_m, &n); - let path = "powers.log"; - pp.store(path); - let loaded = PublicParameters::load(path); - assert_eq!(pp.poly_ck.powers_of_g, loaded.0.powers_of_g); - assert_eq!(pp.poly_ck.powers_of_gamma_g, loaded.0.powers_of_gamma_g); - assert_eq!(pp.poly_vk.h, loaded.1.h); - assert_eq!(pp.poly_vk.beta_h, loaded.1.beta_h); - assert_eq!(pp.g2_powers, loaded.2); - std::fs::remove_file(&path).expect("File can not be deleted"); -} diff --git a/caulk_multi_lookup/src/multiopen.rs b/caulk_multi_lookup/src/multiopen.rs deleted file mode 100644 index 555fdb2..0000000 --- a/caulk_multi_lookup/src/multiopen.rs +++ /dev/null @@ -1,394 +0,0 @@ -/* -This file includes an algorithm for calculating n openings of a KZG vector commitment of size n in n log(n) time. -The algorithm is by Feist and khovratovich. -It is useful for preprocessing. -The full algorithm is described here https://github.com/khovratovich/Kate/blob/master/Kate_amortized.pdf -*/ - -use std::str::FromStr; -//use std::time::{Instant}; -use std::vec::Vec; - -use ark_bls12_381::{Fr, FrParameters, G2Affine, G2Projective}; -use ark_ec::{AffineCurve, ProjectiveCurve}; -use ark_ff::{Field, Fp256, PrimeField}; -use ark_poly::{ - univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain, UVPolynomial, -}; - -pub fn compute_h_opt_g2( - c_poly: &DensePolynomial>, //c(X) degree up to d<2^p , i.e. c_poly has at most d+1 coeffs non-zero - g2powers: &Vec, //SRS - p: usize, -) -> Vec { - let mut coeffs = c_poly.coeffs().to_vec(); - let dom_size = 1 << p; - let fpzero = Fp256::from_str("0").unwrap(); - coeffs.resize(dom_size, fpzero); - - //let now = Instant::now(); - //1. x_ext = [[x^(d-1)], [x^{d-2},...,[x],[1], d+2 [0]'s] - let mut x_ext = vec![]; - for i in 0..=dom_size - 2 { - x_ext.push(g2powers[dom_size - 2 - i].into_projective()); - } - let g1inf = g2powers[0].mul(fpzero); - x_ext.resize(2 * dom_size, g1inf); //filling 2d+2 neutral elements - - let y = dft_g2_opt(&x_ext, p + 1); - //println!("Step 1 computed in {:?}", now.elapsed()); - - //2. c_ext = [c_d, d zeroes, c_d,c_{0},c_1,...,c_{d-2},c_{d-1}] - //let now = Instant::now(); - let mut c_ext = vec![]; - c_ext.push(coeffs[coeffs.len() - 1]); - c_ext.resize(dom_size, fpzero); - c_ext.push(coeffs[coeffs.len() - 1]); - for i in 0..coeffs.len() - 1 { - c_ext.push(coeffs[i]); - } - assert_eq!(c_ext.len(), 2 * dom_size); - let v = dft_opt(&c_ext, p + 1); - //println!("Step 2 computed in {:?}", now.elapsed()); - - //3. u = y o v - - //let now = Instant::now(); - let u = y - .into_iter() - .zip(v.into_iter()) - .map(|(a, b)| a.mul(b.into_repr())) - .collect(); - // println!("Step 3 computed in {:?}", now.elapsed()); - - //4. h_ext = idft_{2d+2}(u) - //let now = Instant::now(); - let h_ext = idft_g2_opt(&u, p + 1); - //println!("Step 4 computed in {:?}", now.elapsed()); - - return h_ext[0..dom_size].to_vec(); -} - -//compute dft of size @dom_size over vector of G1 elements -//q_i = h_0 + h_1w^i + h_2w^{2i}+\cdots + h_{dom_size-1}w^{(dom_size-1)i} for 0<= i< dom_size=2^p -pub fn dft_g2_opt(h: &Vec, p: usize) -> Vec { - let dom_size = 1 << p; - assert_eq!(h.len(), dom_size); //we do not support inputs of size not power of 2 - let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(dom_size).unwrap(); - let mut l = dom_size / 2; - let mut m: usize = 1; - //Stockham FFT - let mut xprev = h.to_vec(); - for _ in 1..=p { - let mut xnext = vec![]; - xnext.resize(xprev.len(), h[0]); - for j in 0..l { - for k in 0..m { - let c0 = xprev[k + j * m].clone(); - let c1 = &xprev[k + j * m + l * m]; - xnext[k + 2 * j * m] = c0 + c1; - let wj_2l = input_domain.element((j * dom_size / (2 * l)) % dom_size); - xnext[k + 2 * j * m + m] = (c0 - c1).mul(wj_2l.into_repr()); - } - } - l = l / 2; - m = m * 2; - xprev = xnext; - } - return xprev; -} - -//compute dft of size @dom_size over vector of Fr elements -//q_i = h_0 + h_1w^i + h_2w^{2i}+\cdots + h_{dom_size-1}w^{(dom_size-1)i} for 0<= i< dom_size=2^p -pub fn dft_opt(h: &Vec, p: usize) -> Vec { - let dom_size = 1 << p; - assert_eq!(h.len(), dom_size); //we do not support inputs of size not power of 2 - let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(dom_size).unwrap(); - let mut l = dom_size / 2; - let mut m: usize = 1; - //Stockham FFT - let mut xprev = h.to_vec(); - for _ in 1..=p { - let mut xnext = vec![]; - xnext.resize(xprev.len(), h[0]); - for j in 0..l { - for k in 0..m { - let c0 = xprev[k + j * m].clone(); - let c1 = &xprev[k + j * m + l * m]; - xnext[k + 2 * j * m] = c0 + c1; - let wj_2l = input_domain.element((j * dom_size / (2 * l)) % dom_size); - xnext[k + 2 * j * m + m] = (c0 - c1) * (wj_2l); - } - } - l = l / 2; - m = m * 2; - xprev = xnext; - } - return xprev; -} - -//compute idft of size @dom_size over vector of G1 elements -//q_i = (h_0 + h_1w^-i + h_2w^{-2i}+\cdots + h_{dom_size-1}w^{-(dom_size-1)i})/dom_size for 0<= i< dom_size=2^p -pub fn idft_g2_opt(h: &Vec, p: usize) -> Vec { - let dom_size = 1 << p; - assert_eq!(h.len(), dom_size); //we do not support inputs of size not power of 2 - let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(dom_size).unwrap(); - let mut l = dom_size / 2; - let mut m: usize = 1; - let mut dom_fr = Fr::from_str("1").unwrap(); - //Stockham FFT - let mut xprev = h.to_vec(); - for _ in 1..=p { - let mut xnext = vec![]; - xnext.resize(xprev.len(), h[0]); - for j in 0..l { - for k in 0..m { - let c0 = xprev[k + j * m].clone(); - let c1 = &xprev[k + j * m + l * m]; - xnext[k + 2 * j * m] = c0 + c1; - let wj_2l = input_domain - .element((dom_size - (j * dom_size / (2 * l)) % dom_size) % dom_size); - xnext[k + 2 * j * m + m] = (c0 - c1).mul(wj_2l.into_repr()); //Difference #1 to forward dft - } - } - l = l / 2; - m = m * 2; - dom_fr = dom_fr + dom_fr; - xprev = xnext; - } - let res = xprev - .iter() - .map(|x| x.mul(dom_fr.inverse().unwrap().into_repr())) - .collect(); - return res; -} - -//compute all openings to c_poly using a smart formula -pub fn multiple_open_g2( - g2powers: &Vec, //SRS - c_poly: &DensePolynomial>, //c(X) - p: usize, -) -> Vec { - let degree = c_poly.coeffs.len() - 1; - let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(degree).unwrap(); - - //let now = Instant::now(); - let h2 = compute_h_opt_g2(c_poly, g2powers, p); - //println!("H2 computed in {:?}", now.elapsed()); - //assert_eq!(h,h2); - - let dom_size = input_domain.size(); - assert_eq!(1 << p, dom_size); - assert_eq!(degree + 1, dom_size); - /*let now = Instant::now(); - let q = dftG1(&h,p); - println!("Q computed in {:?}", now.elapsed());*/ - - //let now = Instant::now(); - let q2 = dft_g2_opt(&h2, p); - //println!("Q2 computed in {:?}", now.elapsed()); - //assert_eq!(q,q2); - - let mut res: Vec = vec![]; - for i in 0..dom_size { - res.push(q2[i].into_affine()); - } - return res; -} - -#[cfg(test)] -pub mod tests { - - use std::time::Instant; - - use ark_bls12_381::{Bls12_381, G1Affine, G1Projective}; - use ark_ec::{AffineCurve, ProjectiveCurve}; - use ark_ff::Fp256; - use ark_poly::univariate::DensePolynomial; - use ark_poly::EvaluationDomain; - use ark_poly_commit::kzg10::*; - use ark_poly_commit::UVPolynomial; - use ark_std::One; - - use crate::caulk_multi_setup::{setup_multi_lookup, PublicParameters}; - use crate::multiopen::*; - use crate::tools::{kzg_commit_g2, kzg_open_g1, KzgBls12_381, UniPoly381}; - - pub fn commit_direct( - c_poly: &DensePolynomial>, //c(X) - poly_ck: &Powers, //SRS - ) -> G1Affine { - assert!(c_poly.coeffs.len() <= poly_ck.powers_of_g.len()); - let mut com = poly_ck.powers_of_g[0].mul(c_poly.coeffs[0]); - for i in 1..c_poly.coeffs.len() { - com = com + poly_ck.powers_of_g[i].mul(c_poly.coeffs[i]); - } - return com.into_affine(); - } - - //compute dft of size @dom_size over vector of G1 elements - //q_i = h_0 + h_1w^i + h_2w^{2i}+\cdots + h_{dom_size-1}w^{(dom_size-1)i} for 0<= i< dom_size=2^p - #[allow(dead_code)] - pub fn dft_g1_opt(h: &Vec, p: usize) -> Vec { - let dom_size = 1 << p; - assert_eq!(h.len(), dom_size); //we do not support inputs of size not power of 2 - let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(dom_size).unwrap(); - let mut l = dom_size / 2; - let mut m: usize = 1; - //Stockham FFT - let mut xprev = h.to_vec(); - for _ in 1..=p { - let mut xnext = vec![]; - xnext.resize(xprev.len(), h[0]); - for j in 0..l { - for k in 0..m { - let c0 = xprev[k + j * m].clone(); - let c1 = &xprev[k + j * m + l * m]; - xnext[k + 2 * j * m] = c0 + c1; - let wj_2l = input_domain.element((j * dom_size / (2 * l)) % dom_size); - xnext[k + 2 * j * m + m] = (c0 - c1).mul(wj_2l.into_repr()); - } - } - l = l / 2; - m = m * 2; - xprev = xnext; - } - return xprev; - } - - //compute idft of size @dom_size over vector of G1 elements - //q_i = (h_0 + h_1w^-i + h_2w^{-2i}+\cdots + h_{dom_size-1}w^{-(dom_size-1)i})/dom_size for 0<= i< dom_size=2^p - #[allow(dead_code)] - pub fn idft_g1_opt(h: &Vec, p: usize) -> Vec { - let dom_size = 1 << p; - assert_eq!(h.len(), dom_size); //we do not support inputs of size not power of 2 - let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(dom_size).unwrap(); - let mut l = dom_size / 2; - let mut m: usize = 1; - let mut dom_fr = Fr::from_str("1").unwrap(); - //Stockham FFT - let mut xprev = h.to_vec(); - for _ in 1..=p { - let mut xnext = vec![]; - xnext.resize(xprev.len(), h[0]); - for j in 0..l { - for k in 0..m { - let c0 = xprev[k + j * m].clone(); - let c1 = &xprev[k + j * m + l * m]; - xnext[k + 2 * j * m] = c0 + c1; - let wj_2l = input_domain - .element((dom_size - (j * dom_size / (2 * l)) % dom_size) % dom_size); - xnext[k + 2 * j * m + m] = (c0 - c1).mul(wj_2l.into_repr()); - //Difference #1 to forward dft - } - } - l = l / 2; - m = m * 2; - dom_fr = dom_fr + dom_fr; - xprev = xnext; - } - let res = xprev - .iter() - .map(|x| x.mul(dom_fr.inverse().unwrap().into_repr())) - .collect(); - return res; - } - - //compute all openings to c_poly by mere calling `open` N times - #[allow(dead_code)] - pub fn multiple_open_naive( - c_poly: &DensePolynomial>, - c_com_open: &Randomness, DensePolynomial>>, - poly_ck: &Powers, - degree: usize, - ) -> Vec { - let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(degree).unwrap(); - let mut res: Vec = vec![]; - for i in 0..input_domain.size() { - let omega_i = input_domain.element(i); - res.push(kzg_open_g1(&c_poly, &omega_i, &c_com_open, &poly_ck).w); - } - return res; - } - - //compute all openings to c_poly by mere calling `open` N times - pub fn multiple_open_naive_g2( - c_poly: &DensePolynomial>, - srs: &PublicParameters, - degree: usize, - ) -> Vec { - let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(degree).unwrap(); - let mut res: Vec = vec![]; - for i in 0..input_domain.size() { - let omega_i = input_domain.element(i); - res.push(kzg_open_g2(&c_poly, &omega_i, srs)); - } - return res; - } - - pub fn kzg_open_g2( - p: &DensePolynomial>, - x: &Fp256, //point - srs: &PublicParameters, - ) -> G2Affine { - let tmp = DensePolynomial::from_coefficients_slice(&[Fr::one()]); - let (_tmp_com, tmp_com_open) = - KzgBls12_381::commit(&srs.poly_ck, &tmp, None, None).unwrap(); - let (witness_polynomial, _random_witness_polynomial) = - KzgBls12_381::compute_witness_polynomial(p, x.clone(), &tmp_com_open).unwrap(); - - return kzg_commit_g2(&witness_polynomial, srs); - } - - #[allow(non_snake_case)] - #[test] - pub fn test_commit() { - // current kzg setup should be changed with output from a setup ceremony - let p: usize = 8; //bitlength of poly degree - let max_degree: usize = (1 << p) + 2; - let actual_degree: usize = (1 << p) - 1; - let m: usize = 1 << (p / 2); - let N: usize = 1 << p; - let pp = setup_multi_lookup(&max_degree, &N, &m, &p); - - // Setting up test instance to run evaluate on. - // test randomness for c_poly is same everytime. - // g_c = g^(c(x)) - let rng = &mut ark_std::test_rng(); - let c_poly = UniPoly381::rand(actual_degree, rng); - let (c_com, _) = KzgBls12_381::commit(&pp.poly_ck, &c_poly, None, None).unwrap(); - let g_c1 = c_com.0; - - let g_c2 = commit_direct(&c_poly, &pp.poly_ck); - assert_eq!(g_c1, g_c2); - println!("commit test passed") - } - - #[allow(non_snake_case)] - #[test] - pub fn test_multi_g2() { - // current kzg setup should be changed with output from a setup ceremony - let p: usize = 6; //bitlength of poly degree - let max_degree: usize = (1 << p) + 2; - let actual_degree: usize = (1 << p) - 1; - let m: usize = 1 << (p / 2); - let N: usize = 1 << p; - let pp = setup_multi_lookup(&max_degree, &N, &m, &p); - - // Setting up test instance to run evaluate on. - // test randomness for c_poly is same everytime. - // test index equals 5 everytime - // g_c = g^(c(x)) - let rng = &mut ark_std::test_rng(); - let c_poly = UniPoly381::rand(actual_degree, rng); - - let now = Instant::now(); - let q = multiple_open_naive_g2(&c_poly, &pp, actual_degree); - println!("Multi naive computed in {:?}", now.elapsed()); - - let now = Instant::now(); - let q2 = multiple_open_g2(&pp.g2_powers, &c_poly, p); - println!("Multi advanced computed in {:?}", now.elapsed()); - assert_eq!(q, q2); - } -} diff --git a/caulk_multi_lookup/src/tools.rs b/caulk_multi_lookup/src/tools.rs deleted file mode 100644 index 2f77c57..0000000 --- a/caulk_multi_lookup/src/tools.rs +++ /dev/null @@ -1,634 +0,0 @@ -/* -This file includes backend tools: -(1) read_line() is for taking inputs from the user -(2) bipoly_commit commits to bivariate polynomials -(3) hash_caulk_single is for hashing group and field elements into a field element -(4) random_field is for generating random field elements -(5) convert_to_bigints is for formatting inputs into multiscalar operations -(6) kzg_open_g1_native is for opening KZG commitments -(7) kzg_partial_open_g1_native is for partially opening bivariate commitments to univariate commitments -(8) kzg_verify_g1_native is for verifying KZG commitments -(9) kzg_partial_open_g1_native is for partially verifying bivariate commitments to univariate commitments -(10) generate_lagrange_polynomials_subset is for generating lagrange polynomials over a subset that is not roots of unity. -(11) aggregate_kzg_proofs_g2 is for aggregating KZG proofs -*/ - -use ark_bls12_381::{Bls12_381, Fr, FrParameters, G1Affine, G1Projective, G2Affine, G2Projective}; -use ark_ec::{msm::VariableBaseMSM, AffineCurve, PairingEngine, ProjectiveCurve}; -use ark_ff::{Field, Fp256, PrimeField}; -use ark_poly::{ - univariate::DensePolynomial as DensePoly, EvaluationDomain, GeneralEvaluationDomain, - Polynomial, UVPolynomial, -}; -use ark_poly_commit::kzg10::*; -use ark_serialize::CanonicalSerialize; -use ark_std::One; -use ark_std::Zero; - -use blake2s_simd::Params; -use rand::{thread_rng, Rng, SeedableRng}; -use rand_chacha::ChaChaRng; -use std::{error::Error, io, str::FromStr}; - -use crate::caulk_multi_setup::PublicParameters; - -pub type UniPoly381 = DensePoly<::Fr>; -pub type KzgBls12_381 = KZG10; - -// Function for reading inputs from the command line. -pub fn read_line() -> T -where - ::Err: Error + 'static, -{ - let mut input = String::new(); - io::stdin() - .read_line(&mut input) - .expect("Failed to get console input."); - let output: T = input.trim().parse().expect("Console input is invalid."); - output -} - -/* -Function to commit to f(X,Y) -here f = [ [a0, a1, a2], [b1, b2, b3] ] represents (a0 + a1 Y + a2 Y^2 ) + X (b1 + b2 Y + b3 Y^2) - -First we unwrap to get a vector of form [a0, a1, a2, b0, b1, b2] -Then we commit to f as a commitment to f'(X) = a0 + a1 X + a2 X^2 + b0 X^3 + b1 X^4 + b2 X^5 - -We also need to know the maximum degree of (a0 + a1 Y + a2 Y^2 ) to prevent overflow errors. - -This is described in Section 4.6.2 -*/ -pub fn bipoly_commit( - pp: &PublicParameters, - poly: &Vec>>, - deg_x: usize, -) -> G1Affine { - let mut poly_formatted = Vec::new(); - - for i in 0..poly.len() { - let temp = convert_to_bigints(&poly[i].coeffs); - for j in 0..poly[i].len() { - poly_formatted.push(temp[j]); - } - let temp = convert_to_bigints(&[Fr::zero()].to_vec())[0]; - for _ in poly[i].len()..deg_x { - poly_formatted.push(temp); - } - } - - assert!(pp.poly_ck.powers_of_g.len() >= poly_formatted.len()); - let g1_poly = - VariableBaseMSM::multi_scalar_mul(&pp.poly_ck.powers_of_g, poly_formatted.as_slice()) - .into_affine(); - - return g1_poly; -} - -///////////////////////////////////////////////////////////////////// -// Hashing -///////////////////////////////////////////////////////////////////// - -// hashing to field copied from -// https://github.com/kobigurk/aggregatable-dkg/blob/main/src/signature/utils/hash.rs -fn rng_from_message(personalization: &[u8], message: &[u8]) -> ChaChaRng { - let hash = Params::new() - .hash_length(32) - .personal(personalization) - .to_state() - .update(message) - .finalize(); - let mut seed = [0u8; 32]; - seed.copy_from_slice(hash.as_bytes()); - let rng = ChaChaRng::from_seed(seed); - rng -} - -pub fn hash_to_field(personalization: &[u8], message: &[u8]) -> F { - let mut rng = rng_from_message(personalization, message); - loop { - let bytes: Vec = (0..F::zero().serialized_size()) - .map(|_| rng.gen()) - .collect(); - if let Some(p) = F::from_random_bytes(&bytes) { - return p; - } - } -} - -/* hash function that takes as input: - (1) some state (either equal to the last hash output or zero) - (2) a vector of g1 elements - (3) a vector of g2 elements - (4) a vector of field elements - -It returns a field element. -*/ -pub fn hash_caulk_multi( - state: Fr, - g1_elements: Option<&Vec<&G1Affine>>, - g2_elements: Option<&Vec<&G2Affine>>, - field_elements: Option<&Vec<&Fr>>, -) -> Fr { - // PERSONALIZATION distinguishes this hash from other hashes that may be in the system - const PERSONALIZATION: &[u8] = b"CAULK2"; - - /////////////////////////////////////////////////////////// - // Handling cases where no g1_elements or no g1_elements or no field elements are input - /////////////////////////////////////////////////////////// - let g1_elements_len: usize; - let g2_elements_len: usize; - let field_elements_len: usize; - - if g1_elements == None { - g1_elements_len = 0; - } else { - g1_elements_len = g1_elements.unwrap().len(); - } - - if g2_elements == None { - g2_elements_len = 0; - } else { - g2_elements_len = g2_elements.unwrap().len(); - } - - if field_elements == None { - field_elements_len = 0; - } else { - field_elements_len = field_elements.unwrap().len(); - } - - /////////////////////////////////////////////////////////// - // Transform inputs into bytes - /////////////////////////////////////////////////////////// - let mut state_bytes = vec![]; - state.serialize(&mut state_bytes).ok(); - - let mut g1_elements_bytes = Vec::new(); - for i in 0..g1_elements_len { - let mut temp = vec![]; - g1_elements.unwrap()[i].clone().serialize(&mut temp).ok(); - g1_elements_bytes.append(&mut temp.clone()); - } - - let mut g2_elements_bytes = Vec::new(); - for i in 0..g2_elements_len { - let mut temp = vec![]; - g2_elements.unwrap()[i].clone().serialize(&mut temp).ok(); - g2_elements_bytes.append(&mut temp.clone()); - } - - let mut field_elements_bytes = Vec::new(); - for i in 0..field_elements_len { - let mut temp = vec![]; - field_elements.unwrap()[i].clone().serialize(&mut temp).ok(); - field_elements_bytes.append(&mut temp.clone()); - } - - // Transform bytes into vector of bytes of the form expected by hash_to_field - let mut hash_input: Vec = state_bytes.clone(); - for i in 0..g1_elements_bytes.len() { - hash_input = [&hash_input as &[_], &[g1_elements_bytes[i]]].concat(); - } - - for i in 0..g2_elements_bytes.len() { - hash_input = [&hash_input as &[_], &[g2_elements_bytes[i]]].concat(); - } - - for i in 0..field_elements_bytes.len() { - hash_input = [&hash_input as &[_], &[field_elements_bytes[i]]].concat(); - } - - // hash_to_field - return hash_to_field::(PERSONALIZATION, &hash_input); -} - -////////////////////////////////////////////////// - -pub fn random_field() -> F { - let mut rng = thread_rng(); - loop { - let bytes: Vec = (0..F::zero().serialized_size()) - .map(|_| rng.gen()) - .collect(); - if let Some(p) = F::from_random_bytes(&bytes) { - return p; - } - } -} - -//copied from arkworks -pub fn convert_to_bigints(p: &Vec) -> Vec { - let coeffs = ark_std::cfg_iter!(p) - .map(|s| s.into_repr()) - .collect::>(); - coeffs -} - -//////////////////////////////////////////////// -// -/* -KZG.Open( srs_KZG, f(X), deg, (alpha1, alpha2, ..., alphan) ) -returns ([f(alpha1), ..., f(alphan)], pi) -Algorithm described in Section 4.6.1, Multiple Openings -*/ -pub fn kzg_open_g1_native( - poly_ck: &Powers, - poly: &DensePoly, - max_deg: Option<&usize>, - points: Vec<&Fr>, -) -> (Vec, G1Affine) { - let mut evals = Vec::new(); - let mut proofs = Vec::new(); - for i in 0..points.len() { - let (eval, pi) = kzg_open_g1_native_single(poly_ck, poly, max_deg, points[i]); - evals.push(eval); - proofs.push(pi); - } - - let mut res: G1Projective = G1Projective::zero(); //default value - - for j in 0..points.len() { - let w_j = points[j].clone(); - //1. Computing coefficient [1/prod] - let mut prod = Fr::one(); - for k in 0..points.len() { - let w_k = points[k]; - if k != j { - prod = prod * (w_j - w_k); - } - } - //2. Summation - let q_add = proofs[j].mul(prod.inverse().unwrap()); //[1/prod]Q_{j} - res = res + q_add; - } - - return (evals, res.into_affine()); -} - -fn kzg_open_g1_native_single( - poly_ck: &Powers, - poly: &DensePoly, - max_deg: Option<&usize>, - point: &Fr, -) -> (Fr, G1Affine) { - let eval = poly.evaluate(&point); - - let global_max_deg = poly_ck.powers_of_g.len(); - - let mut d: usize = 0; - if max_deg == None { - d += global_max_deg; - } else { - d += max_deg.unwrap(); - } - let divisor = DensePoly::from_coefficients_vec(vec![-point.clone(), Fr::one()]); - let witness_polynomial = poly / &divisor; - - assert!(poly_ck.powers_of_g[(global_max_deg - d)..].len() >= witness_polynomial.len()); - let proof = VariableBaseMSM::multi_scalar_mul( - &poly_ck.powers_of_g[(global_max_deg - d)..], - &convert_to_bigints(&witness_polynomial.coeffs).as_slice(), - ) - .into_affine(); - return (eval, proof); -} - -//////////////////////////////////////////////// -// -/* -KZG.Open( srs_KZG, f(X, Y), deg, alpha ) -returns ([f(alpha, x)]_1, pi) -Algorithm described in Section 4.6.2, KZG for Bivariate Polynomials -*/ -pub fn kzg_partial_open_g1_native( - pp: &PublicParameters, - poly: &Vec>, - deg_x: usize, - point: &Fr, -) -> (G1Affine, G1Affine, DensePoly) { - let mut poly_partial_eval = DensePoly::from_coefficients_vec(vec![Fr::zero()]); - let mut alpha = Fr::one(); - for i in 0..poly.len() { - let pow_alpha = DensePoly::from_coefficients_vec(vec![alpha.clone()]); - poly_partial_eval = poly_partial_eval + &pow_alpha * &poly[i]; - alpha = alpha * point; - } - - let eval = VariableBaseMSM::multi_scalar_mul( - &pp.poly_ck.powers_of_g, - convert_to_bigints(&poly_partial_eval.coeffs).as_slice(), - ) - .into_affine(); - - let mut witness_bipolynomial = Vec::new(); - let poly_reverse: Vec<_> = poly.into_iter().rev().collect(); - witness_bipolynomial.push(poly_reverse[0].clone()); - - let alpha = DensePoly::from_coefficients_vec(vec![point.clone()]); - for i in 1..(poly_reverse.len() - 1) { - witness_bipolynomial.push(poly_reverse[i].clone() + &alpha * &witness_bipolynomial[i - 1]); - } - - witness_bipolynomial.reverse(); - - let proof = bipoly_commit(pp, &witness_bipolynomial, deg_x); - - return (eval, proof, poly_partial_eval); -} - -/* -// KZG.Verify( srs_KZG, F, deg, (alpha1, alpha2, ..., alphan), (v1, ..., vn), pi ) -Algorithm described in Section 4.6.1, Multiple Openings -*/ -pub fn kzg_verify_g1_native( - //Verify that @c_com is a commitment to C(X) such that C(x)=z - srs: &PublicParameters, - c_com: G1Affine, //commitment - max_deg: Option<&usize>, // max degree - points: Vec, // x such that eval = C(x) - evals: Vec, //evaluation - pi: G1Affine, //proof -) -> bool { - // Interpolation set - // tau_i(X) = lagrange_tau[i] = polynomial equal to 0 at point[j] for j!= i and 1 at points[i] - - let mut lagrange_tau = DensePoly::from_coefficients_slice(&[Fr::zero()]); - for i in 0..points.len() { - let mut temp: UniPoly381 = DensePoly::from_coefficients_slice(&[Fr::one()]); - for j in 0..points.len() { - if i != j { - temp = &temp * (&DensePoly::from_coefficients_slice(&[-points[j], Fr::one()])); - } - } - let lagrange_scalar = temp.evaluate(&points[i]).inverse().unwrap() * &evals[i]; - lagrange_tau = - lagrange_tau + &temp * (&DensePoly::from_coefficients_slice(&[lagrange_scalar])); - } - - // commit to sum evals[i] tau_i(X) - - // println!( "lagrange_tau = {:?}", lagrange_tau.evaluate(&points[0]) == evals[0] ); - assert!( - srs.poly_ck.powers_of_g.len() >= lagrange_tau.len(), - "not enough powers of g in kzg_verify_g1_native" - ); - let g1_tau = VariableBaseMSM::multi_scalar_mul( - &srs.poly_ck.powers_of_g[..lagrange_tau.len()], - convert_to_bigints(&lagrange_tau.coeffs).as_slice(), - ); - - // vanishing polynomial - // z_tau[i] = polynomial equal to 0 at point[j] - - let mut z_tau = DensePoly::from_coefficients_slice(&[Fr::one()]); - for i in 0..points.len() { - z_tau = &z_tau * (&DensePoly::from_coefficients_slice(&[-points[i], Fr::one()])); - } - - // commit to z_tau(X) in g2 - assert!(srs.g2_powers.len() >= z_tau.len()); - let g2_z_tau = VariableBaseMSM::multi_scalar_mul( - &srs.g2_powers[..z_tau.len()], - convert_to_bigints(&z_tau.coeffs).as_slice(), - ); - - let global_max_deg = srs.poly_ck.powers_of_g.len(); - - let mut d: usize = 0; - if max_deg == None { - d += global_max_deg; - } else { - d += max_deg.unwrap(); - } - - let pairing1 = Bls12_381::pairing( - c_com.into_projective() - g1_tau, - srs.g2_powers[global_max_deg - d], - ); - - let pairing2 = Bls12_381::pairing(pi, g2_z_tau); - - return pairing1 == pairing2; -} - -/* -KZG.Verify( srs_KZG, F, deg, alpha, F_alpha, pi ) -Algorithm described in Section 4.6.2, KZG for Bivariate Polynomials -Be very careful here. Verification is only valid if it is paired with a degree check. -*/ -pub fn kzg_partial_verify_g1_native( - srs: &PublicParameters, - c_com: G1Affine, //commitment - deg_x: usize, - point: Fr, - partial_eval: G1Affine, - pi: G1Affine, //proof -) -> bool { - let pairing1 = Bls12_381::pairing( - c_com.into_projective() - partial_eval.into_projective(), - srs.g2_powers[0], - ); - let pairing2 = Bls12_381::pairing( - pi, - srs.g2_powers[deg_x].into_projective() - srs.g2_powers[0].mul(point), - ); - - return pairing1 == pairing2; -} - -pub fn kzg_commit_g2(poly: &DensePoly>, srs: &PublicParameters) -> G2Affine { - let mut res = srs.g2_powers[0].mul(poly[0]); - for i in 1..poly.len() { - res = res + srs.g2_powers[i].mul(poly[i]) - } - return res.into_affine(); -} - -////////////////////////////////////////////////////// - -pub fn generate_lagrange_polynomials_subset( - positions: &Vec, - srs: &PublicParameters, -) -> Vec>> { - let mut tau_polys = vec![]; - let m = positions.len(); - for j in 0..m { - let mut tau_j = DensePoly::from_coefficients_slice(&[Fr::one()]); //start from tau_j =1 - for k in 0..m { - if k != j { - //tau_j = prod_{k\neq j} (X-w^(i_k))/(w^(i_j)-w^(i_k)) - let denum = srs.domain_N.element(positions[j]) - srs.domain_N.element(positions[k]); - tau_j = &tau_j - * &DensePoly::from_coefficients_slice(&[ - -srs.domain_N.element(positions[k]) / denum, //-w^(i_k))/(w^(i_j)-w^(i_k) - Fr::one() / denum, //1//(w^(i_j)-w^(i_k)) - ]); - } - } - tau_polys.push(tau_j.clone()); - } - tau_polys -} - -/* -Algorithm for aggregating KZG proofs into a single proof -Described in Section 4.6.3 Subset openings -compute Q =\sum_{j=1}^m \frac{Q_{i_j}}}{\prod_{1\leq k\leq m,\; k\neq j}(\omega^{i_j}-\omega^{i_k})} -*/ -pub fn aggregate_kzg_proofs_g2( - openings: &Vec, //Q_i - positions: &Vec, //i_j - input_domain: &GeneralEvaluationDomain, -) -> G2Affine { - let m = positions.len(); - let mut res: G2Projective = openings[0].into_projective(); //default value - - for j in 0..m { - let i_j = positions[j]; - let w_ij = input_domain.element(i_j); - //1. Computing coefficient [1/prod] - let mut prod = Fr::one(); - for k in 0..m { - let i_k = positions[k]; - let w_ik = input_domain.element(i_k); - if k != j { - prod = prod * (w_ij - w_ik); - } - } - //2. Summation - let q_add = openings[i_j].mul(prod.inverse().unwrap()); //[1/prod]Q_{j} - if j == 0 { - res = q_add; - } else { - res = res + q_add; - } - } - return res.into_affine(); -} - -////////////////////////////////////////////////////// - -#[cfg(test)] -pub mod tests { - - use crate::caulk_multi_setup::setup_multi_lookup; - - use crate::multiopen::multiple_open_g2; - use crate::tools::{ - aggregate_kzg_proofs_g2, generate_lagrange_polynomials_subset, KzgBls12_381, UniPoly381, - }; - - use ark_poly::{ - univariate::DensePolynomial as DensePoly, EvaluationDomain, Polynomial, UVPolynomial, - }; - - use ark_bls12_381::{Bls12_381, Fr, G2Affine}; - use ark_ec::{AffineCurve, PairingEngine, ProjectiveCurve}; - use ark_std::{One, Zero}; - use std::time::Instant; - - #[allow(non_snake_case)] - #[test] - pub fn test_lagrange() { - let p: usize = 8; //bitlength of poly degree - let max_degree: usize = (1 << p) + 2; - let m: usize = 8; - let N: usize = 1 << p; - let pp = setup_multi_lookup(&max_degree, &N, &m, &p); - let now = Instant::now(); - println!("time to setup {:?}", now.elapsed()); - - let mut positions: Vec = vec![]; - for i in 0..m { - //generate positions evenly distributed in the set - let i_j: usize = i * (max_degree / m); - positions.push(i_j); - } - - let tau_polys = generate_lagrange_polynomials_subset(&positions, &pp); - for j in 0..m { - for k in 0..m { - if k == j { - assert_eq!( - tau_polys[j].evaluate(&pp.domain_N.element(positions[k])), - Fr::one() - ) - } else { - assert_eq!( - tau_polys[j].evaluate(&pp.domain_N.element(positions[k])), - Fr::zero() - ) - } - } - } - } - - #[allow(non_snake_case)] - #[test] - pub fn test_Q_g2() { - // current kzg setup should be changed with output from a setup ceremony - let p: usize = 6; //bitlength of poly degree - let max_degree: usize = (1 << p) + 2; - let actual_degree: usize = (1 << p) - 1; - let m: usize = 1 << (p / 2); - let N: usize = 1 << p; - let pp = setup_multi_lookup(&max_degree, &N, &m, &p); - - // Setting up test instance to run evaluate on. - // test randomness for c_poly is same everytime. - // test index equals 5 everytime - // g_c = g^(c(x)) - let rng = &mut ark_std::test_rng(); - let c_poly = UniPoly381::rand(actual_degree, rng); - let (c_com, _) = KzgBls12_381::commit(&pp.poly_ck, &c_poly, None, None).unwrap(); - - let now = Instant::now(); - let openings = multiple_open_g2(&pp.g2_powers, &c_poly, p); - println!("Multi advanced computed in {:?}", now.elapsed()); - - let mut positions: Vec = vec![]; - for i in 0..m { - let i_j: usize = i * (max_degree / m); - positions.push(i_j); - } - - let now = Instant::now(); - - //Compute proof - let Q: G2Affine = aggregate_kzg_proofs_g2(&openings, &positions, &pp.domain_N); - println!( - "Full proof for {:?} positions computed in {:?}", - m, - now.elapsed() - ); - - //Compute commitment to C_I - let mut C_I = DensePoly::from_coefficients_slice(&[Fr::zero()]); //C_I = sum_j c_j*tau_j - let tau_polys = generate_lagrange_polynomials_subset(&positions, &pp); - for j in 0..m { - C_I = &C_I + &(&tau_polys[j] * c_poly.evaluate(&pp.domain_N.element(positions[j]))); - //sum_j c_j*tau_j - } - let (c_I_com, _c_I_com_open) = KzgBls12_381::commit(&pp.poly_ck, &C_I, None, None).unwrap(); - - //Compute commitment to z_I - let mut z_I = DensePoly::from_coefficients_slice(&[Fr::one()]); - for j in 0..m { - z_I = &z_I - * &DensePoly::from_coefficients_slice(&[ - -pp.domain_N.element(positions[j]), - Fr::one(), - ]); - } - let (z_I_com, _) = KzgBls12_381::commit(&pp.poly_ck, &z_I, None, None).unwrap(); - - //pairing check - let pairing1 = Bls12_381::pairing( - (c_com.0.into_projective() - c_I_com.0.into_projective()).into_affine(), - pp.g2_powers[0], - ); - let pairing2 = Bls12_381::pairing(z_I_com.0, Q); - assert_eq!(pairing1, pairing2); - } -} diff --git a/caulk_single_opening/Cargo.toml b/caulk_single_opening/Cargo.toml deleted file mode 100644 index 92acfd3..0000000 --- a/caulk_single_opening/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -[package] -name = "caulk_single_opening" -version = "0.1.0" -authors = ["mmaller "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -ark-ff = { version = "^0.3.0", default-features = false } -ark-ec = { version = "^0.3.0", default-features = false } -ark-serialize = { version = "^0.3.0", default-features = false, features = [ "derive" ] } -ark-poly = { version = "^0.3.0", default-features = false } -ark-std = { version = "^0.3.0", default-features = false } -ark-r1cs-std = { version = "^0.3.0", default-features = false, optional = true } -ark-bls12-381 = { version = "^0.3.0", features = [ "std" ] } -ark-bls12-377 = { version = "^0.3.0", features = [ "std" ] } -ark-poly-commit = { version = "^0.3.0", default-features = false } - -tracing = { version = "0.1", default-features = false, features = [ "attributes" ], optional = true } -derivative = { version = "2.0", features = ["use_core"], optional = true} -rand = "0.8.5" -rand_chacha = { version = "0.3.1" } -thiserror = "1.0.19" -blake2s_simd = "1.0.0" - -rayon = { version = "1.5.2", default-features = false, optional = true } -merlin = { version = "3.0.0" } - -[features] -asm = [ "ark-ff/asm" ] -parallel = [ - "rayon", - "ark-std/parallel", - "ark-ff/parallel", - "ark-poly/parallel" - ] -print-trace = [ - "ark-std/print-trace" -] - diff --git a/caulk_single_opening/src/dft.rs b/caulk_single_opening/src/dft.rs deleted file mode 100644 index b222ca3..0000000 --- a/caulk_single_opening/src/dft.rs +++ /dev/null @@ -1,371 +0,0 @@ -/* -This file includes an algorithm for calculating n openings of a KZG vector commitment of size n in n log(n) time. -The algorithm is by Feist and khovratovich. -It is useful for preprocessing. -The full algorithm is described here https://github.com/khovratovich/Kate/blob/master/Kate_amortized.pdf -*/ - -use ark_ec::{AffineCurve, PairingEngine, ProjectiveCurve}; -use ark_ff::{Field, PrimeField}; -use ark_poly::univariate::DensePolynomial; -use ark_poly::{EvaluationDomain, GeneralEvaluationDomain, UVPolynomial}; -use ark_poly_commit::kzg10::*; -use ark_std::One; -use ark_std::Zero; -use std::vec::Vec; - -//compute all pre-proofs using DFT -// h_i= c_d[x^{d-i-1}]+c_{d-1}[x^{d-i-2}]+c_{d-2}[x^{d-i-3}]+\cdots + c_{i+2}[x]+c_{i+1}[1] -pub fn compute_h( - c_poly: &DensePolynomial, //c(X) degree up to d<2^p , i.e. c_poly has at most d+1 coeffs non-zero - poly_ck: &Powers, //SRS - p: usize, -) -> Vec { - let mut coeffs = c_poly.coeffs().to_vec(); - let dom_size = 1 << p; - let fpzero = E::Fr::zero(); - coeffs.resize(dom_size, fpzero); - - //let now = Instant::now(); - //1. x_ext = [[x^(d-1)], [x^{d-2},...,[x],[1], d+2 [0]'s] - let mut x_ext: Vec = poly_ck - .powers_of_g - .iter() - .take(dom_size - 1) - .rev() - .map(|x| x.into_projective()) - .collect(); - x_ext.resize(2 * dom_size, E::G1Projective::zero()); //filling 2d+2 neutral elements - - let y = dft_g1::(&x_ext, p + 1); - //println!("Step 1 computed in {:?}", now.elapsed()); - - //2. c_ext = [c_d, d zeroes, c_d,c_{0},c_1,...,c_{d-2},c_{d-1}] - //let now = Instant::now(); - let mut c_ext = vec![coeffs[coeffs.len() - 1]]; - c_ext.resize(dom_size, fpzero); - c_ext.push(coeffs[coeffs.len() - 1]); - for &e in coeffs.iter().take(coeffs.len() - 1) { - c_ext.push(e); - } - assert_eq!(c_ext.len(), 2 * dom_size); - let v = dft_opt::(&c_ext, p + 1); - //println!("Step 2 computed in {:?}", now.elapsed()); - - //3. u = y o v - - //let now = Instant::now(); - let u: Vec<_> = y - .into_iter() - .zip(v.into_iter()) - .map(|(a, b)| a.mul(b.into_repr())) - .collect(); - // println!("Step 3 computed in {:?}", now.elapsed()); - - //4. h_ext = idft_{2d+2}(u) - //let now = Instant::now(); - let h_ext = idft_g1::(&u, p + 1); - //println!("Step 4 computed in {:?}", now.elapsed()); - - h_ext[0..dom_size].to_vec() -} - -//compute DFT of size @dom_size over vector of G1 elements -//q_i = h_0 + h_1w^i + h_2w^{2i}+\cdots + h_{dom_size-1}w^{(dom_size-1)i} for 0<= i< dom_size=2^p -pub fn dft_g1(h: &[E::G1Projective], p: usize) -> Vec { - let dom_size = 1 << p; - assert_eq!(h.len(), dom_size); //we do not support inputs of size not power of 2 - let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(dom_size).unwrap(); - let mut l = dom_size / 2; - let mut m: usize = 1; - //Stockham FFT - let mut xvec = h.to_vec(); - for _ in 0..p { - let mut xt = xvec.clone(); - for j in 0..l { - for k in 0..m { - let c0 = xvec[k + j * m]; - let c1 = xvec[k + j * m + l * m]; - xt[k + 2 * j * m] = c0 + c1; - let wj_2l = input_domain.element((j * dom_size / (2 * l)) % dom_size); - xt[k + 2 * j * m + m] = (c0 - c1).mul(wj_2l.into_repr()); - } - } - l /= 2; - m *= 2; - xvec = xt; - } - xvec -} - -//compute DFT of size @dom_size over vector of Fr elements -//q_i = h_0 + h_1w^i + h_2w^{2i}+\cdots + h_{dom_size-1}w^{(dom_size-1)i} for 0<= i< dom_size=2^p -pub fn dft_opt(h: &[E::Fr], p: usize) -> Vec { - let dom_size = 1 << p; - assert_eq!(h.len(), dom_size); //we do not support inputs of size not power of 2 - let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(dom_size).unwrap(); - let mut l = dom_size / 2; - let mut m: usize = 1; - //Stockham FFT - let mut xvec = h.to_vec(); - for _ in 0..p { - let mut xt = xvec.clone(); - for j in 0..l { - for k in 0..m { - let c0 = xvec[k + j * m]; - let c1 = xvec[k + j * m + l * m]; - xt[k + 2 * j * m] = c0 + c1; - let wj_2l = input_domain.element((j * dom_size / (2 * l)) % dom_size); - xt[k + 2 * j * m + m] = (c0 - c1) * (wj_2l); - } - } - l /= 2; - m *= 2; - xvec = xt; - } - xvec -} - -//compute idft of size @dom_size over vector of G1 elements -//q_i = (h_0 + h_1w^-i + h_2w^{-2i}+\cdots + h_{dom_size-1}w^{-(dom_size-1)i})/dom_size for 0<= i< dom_size=2^p -pub fn idft_g1(h: &[E::G1Projective], p: usize) -> Vec { - let dom_size = 1 << p; - assert_eq!(h.len(), dom_size); //we do not support inputs of size not power of 2 - let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(dom_size).unwrap(); - let mut l = dom_size / 2; - let mut m: usize = 1; - let mut dom_fr = E::Fr::one(); - //Stockham FFT - let mut xvec = h.to_vec(); - for _ in 0..p { - let mut xt = xvec.clone(); - for j in 0..l { - for k in 0..m { - let c0 = xvec[k + j * m]; - let c1 = xvec[k + j * m + l * m]; - xt[k + 2 * j * m] = c0 + c1; - let wj_2l = input_domain - .element((dom_size - (j * dom_size / (2 * l)) % dom_size) % dom_size); - xt[k + 2 * j * m + m] = (c0 - c1).mul(wj_2l.into_repr()); //Difference #1 to forward DFT - } - } - l /= 2; - m *= 2; - dom_fr = dom_fr + dom_fr; - xvec = xt; - } - - let domain_inverse = dom_fr.inverse().unwrap().into_repr(); - - xvec.iter().map(|x| x.mul(domain_inverse)).collect() -} - -#[cfg(test)] -pub mod tests { - use super::*; - use crate::caulk_single_setup::caulk_single_setup; - use crate::KZGCommit; - use ark_bls12_377::Bls12_377; - use ark_bls12_381::Bls12_381; - use ark_ec::PairingEngine; - use ark_poly::univariate::DensePolynomial; - use ark_std::test_rng; - use ark_std::time::Instant; - use ark_std::UniformRand; - - /// Various functions that are used for testing - - fn commit_direct( - c_poly: &DensePolynomial, //c(X) - poly_ck: &Powers, //SRS - ) -> E::G1Affine { - assert!(c_poly.coeffs.len() <= poly_ck.powers_of_g.len()); - let mut com = poly_ck.powers_of_g[0].mul(c_poly.coeffs[0]); - for i in 1..c_poly.coeffs.len() { - com = com + poly_ck.powers_of_g[i].mul(c_poly.coeffs[i]); - } - com.into_affine() - } - - //compute all openings to c_poly by mere calling `open` N times - fn multiple_open_naive( - c_poly: &DensePolynomial, - c_com_open: &Randomness>, - poly_ck: &Powers, - degree: usize, - ) -> Vec { - let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(degree).unwrap(); - let mut res: Vec = vec![]; - for i in 0..input_domain.size() { - let omega_i = input_domain.element(i); - res.push(kzg_open_g1_test::(&c_poly, &omega_i, &c_com_open, &poly_ck).w); - } - res - } - - //////////////////////////////////////////////// - fn kzg_open_g1_test( - p: &DensePolynomial, - omega_5: &E::Fr, - polycom_open: &Randomness>, - poly_ck: &Powers, - ) -> Proof { - let rng = &mut ark_std::test_rng(); - - let (witness_polynomial, _random_witness_polynomial) = - KZG10::::compute_witness_polynomial(p, omega_5.clone(), polycom_open).unwrap(); - - let (temp0, _temp1) = KZG10::commit(poly_ck, &witness_polynomial, None, Some(rng)).unwrap(); - Proof { - w: temp0.0, - random_v: None, - } - } - - //compute KZG proof Q = g1_q = g^( (c(x) - c(w^i)) / (x - w^i) ) where x is secret, w^i is the point where we open, and c(X) is the committed polynomial - fn single_open_default( - c_poly: &DensePolynomial, //c(X) - c_com_open: &Randomness>, // - poly_ck: &Powers, - i: usize, // - degree: usize, - ) -> E::G1Affine { - let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(degree).unwrap(); - let omega_i = input_domain.element(i); - let c_poly_open = kzg_open_g1_test(&c_poly, &omega_i, &c_com_open, &poly_ck); - c_poly_open.w - } - - //KZG proof/opening at point y for c(X) = sum_i c_i X^i - //(1)T_y(X) = sum_i t_i X^i - //(2) t_{deg-1} = c_deg - //(3) t_j = c_{j+1} + y*t_{j+1} - fn single_open_fast( - c_poly: &DensePolynomial, //c(X) - poly_ck: &Powers, //SRS - i: usize, //y=w^i - degree: usize, //degree of c(X) - ) -> E::G1Affine { - //computing opening point - let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(degree).unwrap(); - let y = input_domain.element(i); - - //compute quotient - let mut t_poly = c_poly.clone(); - t_poly.coeffs.remove(0); //shifting indices - for j in (0..t_poly.len() - 1).rev() { - t_poly.coeffs[j] = c_poly.coeffs[j + 1] + y * t_poly.coeffs[j + 1] - } - - //commit - let (t_com, _) = KZG10::commit(&poly_ck, &t_poly, None, None).unwrap(); - t_com.0 - } - - #[test] - fn test_single() { - test_single_helper::(); - test_single_helper::(); - } - - fn test_single_helper() { - let mut rng = test_rng(); - - // setting public parameters - // current kzg setup should be changed with output from a setup ceremony - let max_degree: usize = 100; - let actual_degree: usize = 63; - let pp = caulk_single_setup(max_degree, actual_degree, &mut rng); - - // Setting up test instance to run evaluate on. - // test randomness for c_poly is same everytime. - // test index equals 5 everytime - // g_c = g^(c(x)) - let rng = &mut test_rng(); - let c_poly = DensePolynomial::::rand(actual_degree, rng); - let (_c_com, c_com_open) = KZG10::::commit(&pp.poly_ck, &c_poly, None, None).unwrap(); - - let i: usize = 6; - let q = single_open_default(&c_poly, &c_com_open, &pp.poly_ck, i, actual_degree); - let q2 = single_open_fast(&c_poly, &pp.poly_ck, i, actual_degree); - assert_eq!(q, q2); - } - - #[test] - fn test_dft() { - test_dft_helper::(); - test_dft_helper::(); - } - - fn test_dft_helper() { - let mut rng = test_rng(); - for i in 2..6 { - let size = 1 << i; - let h: Vec = - (0..size).map(|_| E::G1Projective::rand(&mut rng)).collect(); - - let c_dft = dft_g1::(&h, i); - let c_back = idft_g1::(&c_dft, i); - assert_eq!(h, c_back); - } - } - - #[test] - fn test_commit() { - test_commit_helper::(); - test_commit_helper::(); - } - - pub fn test_commit_helper() { - let mut rng = test_rng(); - - // current kzg setup should be changed with output from a setup ceremony - let max_degree: usize = 100; - let actual_degree: usize = 63; - let pp = caulk_single_setup(max_degree, actual_degree, &mut rng); - - // Setting up test instance to run evaluate on. - // test randomness for c_poly is same everytime. - // g_c = g^(c(x)) - let c_poly = DensePolynomial::::rand(actual_degree, &mut rng); - let (c_com, _c_com_open) = KZG10::::commit(&pp.poly_ck, &c_poly, None, None).unwrap(); - let g_c1 = c_com.0; - - let g_c2 = commit_direct(&c_poly, &pp.poly_ck); - assert_eq!(g_c1, g_c2); - println!("commit test passed") - } - - #[test] - pub fn test_multi() { - test_multi_helper::(); - test_multi_helper::(); - } - - pub fn test_multi_helper() { - let mut rng = test_rng(); - - // current kzg setup should be changed with output from a setup ceremony - let p: usize = 9; - let max_degree: usize = 1 << p + 1; - let actual_degree: usize = (1 << p) - 1; - let pp = caulk_single_setup(max_degree, actual_degree, &mut rng); - - // Setting up test instance to run evaluate on. - // test randomness for c_poly is same everytime. - // test index equals 5 everytime - // g_c = g^(c(x)) - let c_poly = DensePolynomial::::rand(actual_degree, &mut rng); - let (c_com, c_com_open) = KZG10::::commit(&pp.poly_ck, &c_poly, None, None).unwrap(); - let _g_c = c_com.0; - - let now = Instant::now(); - let q = multiple_open_naive(&c_poly, &c_com_open, &pp.poly_ck, actual_degree); - println!("Multi naive computed in {:?}", now.elapsed()); - - let now = Instant::now(); - let q2 = KZGCommit::multiple_open(&c_poly, &pp.poly_ck, p); - println!("Multi advanced computed in {:?}", now.elapsed()); - assert_eq!(q, q2); - } -} diff --git a/caulk_single_opening/src/kzg.rs b/caulk_single_opening/src/kzg.rs deleted file mode 100644 index 522e25d..0000000 --- a/caulk_single_opening/src/kzg.rs +++ /dev/null @@ -1,224 +0,0 @@ -/* -This file includes backend tools: -(1) read_line() is for taking inputs from the user -(2) kzg_open_g1 is for opening KZG commitments -(3) kzg_verify_g1 is for verifying KZG commitments -(4) hash_caulk_single is for hashing group and field elements into a field element -(5) random_field is for generating random field elements -*/ - -use crate::{compute_h, dft_g1}; -use ark_ec::{msm::VariableBaseMSM, AffineCurve, PairingEngine, ProjectiveCurve}; -use ark_ff::{Field, PrimeField}; -use ark_poly::{univariate::DensePolynomial, Polynomial, UVPolynomial}; -use ark_poly::{EvaluationDomain, GeneralEvaluationDomain}; -use ark_poly_commit::kzg10::*; -use ark_std::{end_timer, start_timer}; -use ark_std::{One, Zero}; -#[cfg(feature = "parallel")] -use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; -use std::marker::PhantomData; - -//////////////////////////////////////////////// -// - -//copied from arkworks -fn convert_to_bigints(p: &[F]) -> Vec { - ark_std::cfg_iter!(p) - .map(|s| s.into_repr()) - .collect::>() -} - -///////////////////////////////////////////////////////////////////// -// KZG opening and verifying -///////////////////////////////////////////////////////////////////// - -pub struct KZGCommit { - phantom: PhantomData, -} - -impl KZGCommit { - pub fn commit(powers: &Powers, polynomial: &DensePolynomial) -> E::G1Affine { - let (com, _randomness) = KZG10::::commit(powers, polynomial, None, None).unwrap(); - com.0 - } - - // compute all openings to c_poly using a smart formula - // This Code implements an algorithm for calculating n openings of a KZG vector commitment of size n in n log(n) time. - // The algorithm is by Feist and Khovratovich. - // It is useful for preprocessing. - // The full algorithm is described here https://github.com/khovratovich/Kate/blob/master/Kate_amortized.pdf - pub fn multiple_open( - c_poly: &DensePolynomial, //c(X) - poly_ck: &Powers, //SRS - p: usize, - ) -> Vec { - let timer = start_timer!(|| "multiple open"); - - let degree = c_poly.coeffs.len() - 1; - let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(degree).unwrap(); - - let h_timer = start_timer!(|| "compute h"); - let h2 = compute_h(c_poly, poly_ck, p); - end_timer!(h_timer); - - let dom_size = input_domain.size(); - assert_eq!(1 << p, dom_size); - assert_eq!(degree + 1, dom_size); - - let dft_timer = start_timer!(|| "G1 dft"); - let q2 = dft_g1::(&h2, p); - end_timer!(dft_timer); - - let res = E::G1Projective::batch_normalization_into_affine(q2.as_ref()); - - end_timer!(timer); - res - } - - /* - KZG.Open( srs_KZG, f(X), deg, (alpha1, alpha2, ..., alphan) ) - returns ([f(alpha1), ..., f(alphan)], pi) - Algorithm described in Section 4.6.1, Multiple Openings - */ - pub fn open_g1_batch( - poly_ck: &Powers, - poly: &DensePolynomial, - max_deg: Option<&usize>, - points: &[E::Fr], - ) -> (Vec, E::G1Affine) { - let mut evals = Vec::new(); - let mut proofs = Vec::new(); - for p in points.iter() { - let (eval, pi) = Self::open_g1_single(poly_ck, poly, max_deg, p); - evals.push(eval); - proofs.push(pi); - } - - let mut res = E::G1Projective::zero(); //default value - - for j in 0..points.len() { - let w_j = points[j]; - //1. Computing coefficient [1/prod] - let mut prod = E::Fr::one(); - for (k, p) in points.iter().enumerate() { - if k != j { - prod *= w_j - p; - } - } - //2. Summation - let q_add = proofs[j].mul(prod.inverse().unwrap()); //[1/prod]Q_{j} - res += q_add; - } - - (evals, res.into_affine()) - } - - //KZG.Open( srs_KZG, f(X), deg, alpha ) returns (f(alpha), pi) - fn open_g1_single( - poly_ck: &Powers, - poly: &DensePolynomial, - max_deg: Option<&usize>, - point: &E::Fr, - ) -> (E::Fr, E::G1Affine) { - let eval = poly.evaluate(point); - - let global_max_deg = poly_ck.powers_of_g.len(); - - let mut d: usize = 0; - if max_deg == None { - d += global_max_deg; - } else { - d += max_deg.unwrap(); - } - let divisor = DensePolynomial::from_coefficients_vec(vec![-*point, E::Fr::one()]); - let witness_polynomial = poly / &divisor; - - assert!(poly_ck.powers_of_g[(global_max_deg - d)..].len() >= witness_polynomial.len()); - let proof = VariableBaseMSM::multi_scalar_mul( - &poly_ck.powers_of_g[(global_max_deg - d)..], - convert_to_bigints(&witness_polynomial.coeffs).as_slice(), - ) - .into_affine(); - (eval, proof) - } - - /* - // KZG.Verify( srs_KZG, F, deg, (alpha1, alpha2, ..., alphan), (v1, ..., vn), pi ) - Algorithm described in Section 4.6.1, Multiple Openings - */ - pub fn verify_g1( - // Verify that @c_com is a commitment to C(X) such that C(x)=z - powers_of_g1: &[E::G1Affine], // generator of G1 - powers_of_g2: &[E::G2Affine], // [1]_2, [x]_2, [x^2]_2, ... - c_com: &E::G1Affine, //commitment - max_deg: Option<&usize>, // max degree - points: &[E::Fr], // x such that eval = C(x) - evals: &[E::Fr], //evaluation - pi: &E::G1Affine, //proof - ) -> bool { - // Interpolation set - // tau_i(X) = lagrange_tau[i] = polynomial equal to 0 at point[j] for j!= i and 1 at points[i] - - let mut lagrange_tau = DensePolynomial::from_coefficients_slice(&[E::Fr::zero()]); - let mut prod = DensePolynomial::from_coefficients_slice(&[E::Fr::one()]); - let mut components = vec![]; - for &p in points.iter() { - let poly = DensePolynomial::from_coefficients_slice(&[-p, E::Fr::one()]); - prod = &prod * (&poly); - components.push(poly); - } - - for i in 0..points.len() { - let mut temp = &prod / &components[i]; - let lagrange_scalar = temp.evaluate(&points[i]).inverse().unwrap() * evals[i]; - temp.coeffs.iter_mut().for_each(|x| *x *= lagrange_scalar); - lagrange_tau = lagrange_tau + temp; - } - - // commit to sum evals[i] tau_i(X) - assert!( - powers_of_g1.len() >= lagrange_tau.len(), - "KZG verifier doesn't have enough g1 powers" - ); - let g1_tau = VariableBaseMSM::multi_scalar_mul( - &powers_of_g1[..lagrange_tau.len()], - convert_to_bigints(&lagrange_tau.coeffs).as_slice(), - ); - - // vanishing polynomial - let z_tau = prod; - - // commit to z_tau(X) in g2 - assert!( - powers_of_g2.len() >= z_tau.len(), - "KZG verifier doesn't have enough g2 powers" - ); - let g2_z_tau = VariableBaseMSM::multi_scalar_mul( - &powers_of_g2[..z_tau.len()], - convert_to_bigints(&z_tau.coeffs).as_slice(), - ); - - let global_max_deg = powers_of_g1.len(); - - let mut d: usize = 0; - if max_deg == None { - d += global_max_deg; - } else { - d += max_deg.unwrap(); - } - - let pairing_inputs = vec![ - ( - E::G1Prepared::from((g1_tau - c_com.into_projective()).into_affine()), - E::G2Prepared::from(powers_of_g2[global_max_deg - d]), - ), - ( - E::G1Prepared::from(*pi), - E::G2Prepared::from(g2_z_tau.into_affine()), - ), - ]; - - E::product_of_pairings(pairing_inputs.iter()).is_one() - } -} diff --git a/caulk_multi_lookup/src/main.rs b/examples/multi_lookup.rs similarity index 61% rename from caulk_multi_lookup/src/main.rs rename to examples/multi_lookup.rs index c901204..552bf44 100644 --- a/caulk_multi_lookup/src/main.rs +++ b/examples/multi_lookup.rs @@ -1,28 +1,35 @@ -mod caulk_multi_lookup; -mod caulk_multi_setup; -mod caulk_multi_unity; -mod multiopen; -mod tools; - -use crate::caulk_multi_lookup::{ - compute_lookup_proof, get_poly_and_g2_openings, verify_lookup_proof, LookupInstance, - LookupProverInput, -}; -use crate::caulk_multi_setup::setup_multi_lookup; -use crate::tools::{random_field, read_line, KzgBls12_381}; - -use ark_bls12_381::{Fr, FrParameters}; -use ark_ff::Fp256; +use ark_bls12_381::{Bls12_381, Fr}; use ark_poly::{univariate::DensePolynomial, EvaluationDomain}; use ark_poly_commit::{Polynomial, UVPolynomial}; -use ark_std::time::Instant; - +use ark_std::{test_rng, time::Instant, UniformRand}; +use caulk::{ + multi::{ + compute_lookup_proof, get_poly_and_g2_openings, verify_lookup_proof, LookupInstance, + LookupProverInput, + }, + KZGCommit, PublicParameters, +}; use rand::Rng; -use std::cmp::max; +use std::{cmp::max, error::Error, io, str::FromStr}; + +// Function for reading inputs from the command line. +fn read_line() -> T +where + ::Err: Error + 'static, +{ + let mut input = String::new(); + io::stdin() + .read_line(&mut input) + .expect("Failed to get console input."); + let output: T = input.trim().parse().expect("Console input is invalid."); + output +} #[allow(non_snake_case)] fn main() { - //1. Setup + let mut rng = test_rng(); + + // 1. Setup // setting public parameters // current kzg setup should be changed with output from a setup ceremony println!("What is the bitsize of the degree of the polynomial inside the commitment? "); @@ -33,39 +40,39 @@ fn main() { let N: usize = 1 << n; let powers_size: usize = max(N + 2, 1024); let actual_degree = N - 1; - let temp_m = n; //dummy + let temp_m = n; // dummy let now = Instant::now(); - let mut pp = setup_multi_lookup(&powers_size, &N, &temp_m, &n); + let mut pp = PublicParameters::::setup(&powers_size, &N, &temp_m, &n); println!( "Time to setup multi openings of table size {:?} = {:?}", actual_degree + 1, now.elapsed() ); - //2. Poly and openings + // 2. Poly and openings let now = Instant::now(); let table = get_poly_and_g2_openings(&pp, actual_degree); println!("Time to generate commitment table = {:?}", now.elapsed()); - //3. Setup + // 3. Setup pp.regenerate_lookup_params(m); - //4. Positions - let mut rng = rand::thread_rng(); + // 4. Positions + // let mut rng = rand::thread_rng(); let mut positions: Vec = vec![]; for _ in 0..m { - //generate positions randomly in the set - //let i_j: usize = j*(actual_degree/m); - let i_j: usize = rng.gen_range(0, actual_degree); + // generate positions randomly in the set + // let i_j: usize = j*(actual_degree/m); + let i_j: usize = rng.gen_range(0..actual_degree); positions.push(i_j); } println!("positions = {:?}", positions); - //5. generating phi - let blinder: Fp256 = random_field::(); + // 5. generating phi + let blinder = Fr::rand(&mut rng); let a_m = DensePolynomial::from_coefficients_slice(&[blinder]); let mut phi_poly = a_m.mul_by_vanishing_poly(pp.domain_m); let c_poly_local = table.c_poly.clone(); @@ -73,39 +80,36 @@ fn main() { for j in 0..m { phi_poly = &phi_poly + &(&pp.lagrange_polynomials_m[j] - * c_poly_local.evaluate(&pp.domain_N.element(positions[j]))); //adding c(w^{i_j})*mu_j(X) + * c_poly_local.evaluate(&pp.domain_N.element(positions[j]))); // adding c(w^{i_j})*mu_j(X) } for j in m..pp.domain_m.size() { phi_poly = &phi_poly + &(&pp.lagrange_polynomials_m[j] * c_poly_local.evaluate(&pp.domain_N.element(0))); - //adding c(w^{i_j})*mu_j(X) + // adding c(w^{i_j})*mu_j(X) } - //6. Running proofs + // 6. Running proofs let now = Instant::now(); - let (c_com, _) = KzgBls12_381::commit(&pp.poly_ck, &table.c_poly, None, None).unwrap(); - let (phi_com, _) = KzgBls12_381::commit(&pp.poly_ck, &phi_poly, None, None).unwrap(); + let c_com = KZGCommit::::commit_g1(&pp.poly_ck, &table.c_poly); + let phi_com = KZGCommit::::commit_g1(&pp.poly_ck, &phi_poly); println!("Time to generate inputs = {:?}", now.elapsed()); - let lookup_instance = LookupInstance { - c_com: c_com.0.clone(), - phi_com: phi_com.0.clone(), - }; + let lookup_instance = LookupInstance { c_com, phi_com }; let prover_input = LookupProverInput { c_poly: table.c_poly.clone(), - phi_poly: phi_poly, - positions: positions, + phi_poly, + positions, openings: table.openings.clone(), }; println!("We are now ready to run the prover. How many times should we run it?"); let number_of_openings: usize = read_line(); let now = Instant::now(); - let (proof, unity_proof) = compute_lookup_proof(&lookup_instance, &prover_input, &pp); + let (proof, unity_proof) = compute_lookup_proof(&lookup_instance, &prover_input, &pp, &mut rng); for _ in 1..number_of_openings { - _ = compute_lookup_proof(&lookup_instance, &prover_input, &pp); + _ = compute_lookup_proof(&lookup_instance, &prover_input, &pp, &mut rng); } println!( "Time to evaluate {} times {} multi-openings of table size {:?} = {:?} ", @@ -117,7 +121,7 @@ fn main() { let now = Instant::now(); for _ in 0..number_of_openings { - verify_lookup_proof(table.c_com, phi_com.0, &proof, &unity_proof, &pp); + verify_lookup_proof(&table.c_com, &phi_com, &proof, &unity_proof, &pp); } println!( "Time to verify {} times {} multi-openings of table size {:?} = {:?} ", @@ -128,7 +132,7 @@ fn main() { ); assert!( - verify_lookup_proof(table.c_com, phi_com.0, &proof, &unity_proof, &pp), + verify_lookup_proof(&table.c_com, &phi_com, &proof, &unity_proof, &pp), "Result does not verify" ); } diff --git a/caulk_single_opening/examples/single_opening.rs b/examples/single_opening.rs similarity index 87% rename from caulk_single_opening/examples/single_opening.rs rename to examples/single_opening.rs index 4fc6a19..7450449 100644 --- a/caulk_single_opening/examples/single_opening.rs +++ b/examples/single_opening.rs @@ -1,16 +1,15 @@ use ark_bls12_381::{Bls12_381, Fr, G1Affine}; use ark_ec::{AffineCurve, ProjectiveCurve}; -use ark_poly::univariate::DensePolynomial; -use ark_poly::{EvaluationDomain, GeneralEvaluationDomain, Polynomial, UVPolynomial}; +use ark_poly::{ + univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain, Polynomial, + UVPolynomial, +}; use ark_poly_commit::kzg10::KZG10; -use ark_std::test_rng; -use ark_std::UniformRand; -use caulk_single_opening::caulk_single_setup; -use caulk_single_opening::CaulkTranscript; -use caulk_single_opening::KZGCommit; -use caulk_single_opening::{caulk_single_prove, caulk_single_verify}; -use std::time::Instant; -use std::{error::Error, io, str::FromStr}; +use ark_std::{test_rng, UniformRand}; +use caulk::{ + caulk_single_prove, caulk_single_setup, caulk_single_verify, CaulkTranscript, KZGCommit, +}; +use std::{error::Error, io, str::FromStr, time::Instant}; type UniPoly381 = DensePolynomial; type KzgBls12_381 = KZG10; @@ -48,7 +47,7 @@ fn main() { now.elapsed() ); - //polynomial and commitment + // polynomial and commitment let now = Instant::now(); // deterministic randomness. Should never be used in practice. let c_poly = UniPoly381::rand(actual_degree, &mut rng); @@ -60,7 +59,7 @@ fn main() { now.elapsed() ); - //point at which we will open c_com + // point at which we will open c_com let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(actual_degree).unwrap(); println!("Which position in the vector should we open at? "); let position: usize = read_line(); @@ -71,7 +70,7 @@ fn main() { ); let omega_i = input_domain.element(position); - //Deciding whether to open all positions or just the one position. + // Deciding whether to open all positions or just the one position. println!("Should we open all possible positions? Opening all possible positions is slow. Please input either YES or NO" ); let open_all: String = read_line(); @@ -92,9 +91,10 @@ fn main() { "Console input is invalid" ); - //compute all openings + // compute all openings let now = Instant::now(); - let g1_qs = KZGCommit::multiple_open(&c_poly, &pp.poly_ck, p); + let g1_qs = + KZGCommit::::multiple_open::(&c_poly, &pp.poly_ck.powers_of_g, p); g1_q = g1_qs[position]; println!("Time to compute all KZG openings {:?}", now.elapsed()); } diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..028d640 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,9 @@ +reorder_imports = true +wrap_comments = true +normalize_comments = true +use_try_shorthand = true +match_block_trailing_comma = true +use_field_init_shorthand = true +edition = "2018" +condense_wildcard_suffixes = true +imports_granularity = "Crate" \ No newline at end of file diff --git a/src/dft.rs b/src/dft.rs new file mode 100644 index 0000000..dae8e2b --- /dev/null +++ b/src/dft.rs @@ -0,0 +1,246 @@ +// This file includes an algorithm for calculating n openings of a KZG vector +// commitment of size n in n log(n) time. The algorithm is by Feist and +// khovratovich. It is useful for preprocessing. +// The full algorithm is described here https://github.com/khovratovich/Kate/blob/master/Kate_amortized.pdf + +use ark_ec::ProjectiveCurve; +use ark_ff::PrimeField; +use ark_poly::{ + univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain, UVPolynomial, +}; +use std::vec::Vec; + +// compute all pre-proofs using DFT +// h_i= c_d[x^{d-i-1}]+c_{d-1}[x^{d-i-2}]+c_{d-2}[x^{d-i-3}]+\cdots + +// c_{i+2}[x]+c_{i+1}[1] +pub fn compute_h( + c_poly: &DensePolynomial, /* c(X) degree up to d<2^p , i.e. c_poly has at most d+1 coeffs + * non-zero */ + powers: &[G], // SRS + p: usize, +) -> Vec +where + F: PrimeField, + G: ProjectiveCurve, +{ + let mut coeffs = c_poly.coeffs().to_vec(); + let dom_size = 1 << p; + let fpzero = F::zero(); + coeffs.resize(dom_size, fpzero); + + // let now = Instant::now(); + // 1. x_ext = [[x^(d-1)], [x^{d-2},...,[x],[1], d+2 [0]'s] + let mut x_ext: Vec = powers.iter().take(dom_size - 1).rev().map(|&x| x).collect(); + x_ext.resize(2 * dom_size, G::zero()); // filling 2d+2 neutral elements + + let y = group_dft::(&x_ext, p + 1); + // println!("Step 1 computed in {:?}", now.elapsed()); + + // 2. c_ext = [c_d, d zeroes, c_d,c_{0},c_1,...,c_{d-2},c_{d-1}] + // let now = Instant::now(); + let mut c_ext = vec![coeffs[coeffs.len() - 1]]; + c_ext.resize(dom_size, fpzero); + c_ext.push(coeffs[coeffs.len() - 1]); + for &e in coeffs.iter().take(coeffs.len() - 1) { + c_ext.push(e); + } + assert_eq!(c_ext.len(), 2 * dom_size); + let v = field_dft::(&c_ext, p + 1); + // println!("Step 2 computed in {:?}", now.elapsed()); + + // 3. u = y o v + + // let now = Instant::now(); + let u: Vec<_> = y + .into_iter() + .zip(v.into_iter()) + .map(|(a, b)| a.mul(b.into_repr())) + .collect(); + // println!("Step 3 computed in {:?}", now.elapsed()); + + // 4. h_ext = idft_{2d+2}(u) + // let now = Instant::now(); + let h_ext = group_inv_dft::(&u, p + 1); + // println!("Step 4 computed in {:?}", now.elapsed()); + + h_ext[0..dom_size].to_vec() +} + +// compute DFT of size @dom_size over vector of Fr elements +// q_i = h_0 + h_1w^i + h_2w^{2i}+\cdots + h_{dom_size-1}w^{(dom_size-1)i} for +// 0<= i< dom_size=2^p +pub fn group_dft(h: &[G], p: usize) -> Vec +where + F: PrimeField, + G: ProjectiveCurve, +{ + let dom_size = 1 << p; + assert_eq!(h.len(), dom_size); // we do not support inputs of size not power of 2 + let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(dom_size).unwrap(); + let mut l = dom_size / 2; + let mut m: usize = 1; + // Stockham FFT + let mut xvec = h.to_vec(); + for _ in 0..p { + let mut xt = xvec.clone(); + for j in 0..l { + for k in 0..m { + let c0 = xvec[k + j * m]; + let c1 = xvec[k + j * m + l * m]; + xt[k + 2 * j * m] = c0 + c1; + let wj_2l = input_domain.element((j * dom_size / (2 * l)) % dom_size); + xt[k + 2 * j * m + m] = (c0 - c1).mul(wj_2l.into_repr()); + } + } + l /= 2; + m *= 2; + xvec = xt; + } + xvec +} + +// compute DFT of size @dom_size over vector of Fr elements +// q_i = h_0 + h_1w^i + h_2w^{2i}+\cdots + h_{dom_size-1}w^{(dom_size-1)i} for +// 0<= i< dom_size=2^p +pub fn field_dft(h: &[F], p: usize) -> Vec { + let dom_size = 1 << p; + assert_eq!(h.len(), dom_size); // we do not support inputs of size not power of 2 + let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(dom_size).unwrap(); + let mut l = dom_size / 2; + let mut m: usize = 1; + // Stockham FFT + let mut xvec = h.to_vec(); + for _ in 0..p { + let mut xt = xvec.clone(); + for j in 0..l { + for k in 0..m { + let c0 = xvec[k + j * m]; + let c1 = xvec[k + j * m + l * m]; + xt[k + 2 * j * m] = c0 + c1; + let wj_2l = input_domain.element((j * dom_size / (2 * l)) % dom_size); + xt[k + 2 * j * m + m] = (c0 - c1) * (wj_2l); + } + } + l /= 2; + m *= 2; + xvec = xt; + } + xvec +} + +// compute idft of size @dom_size over vector of G1 elements +// q_i = (h_0 + h_1w^-i + h_2w^{-2i}+\cdots + +// h_{dom_size-1}w^{-(dom_size-1)i})/dom_size for 0<= i< dom_size=2^p +pub fn group_inv_dft(h: &[G], p: usize) -> Vec +where + F: PrimeField, + G: ProjectiveCurve, +{ + let dom_size = 1 << p; + assert_eq!(h.len(), dom_size); // we do not support inputs of size not power of 2 + let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(dom_size).unwrap(); + let mut l = dom_size / 2; + let mut m: usize = 1; + let mut dom_fr = F::one(); + // Stockham FFT + let mut xvec = h.to_vec(); + for _ in 0..p { + let mut xt = xvec.clone(); + for j in 0..l { + for k in 0..m { + let c0 = xvec[k + j * m]; + let c1 = xvec[k + j * m + l * m]; + xt[k + 2 * j * m] = c0 + c1; + let wj_2l = input_domain + .element((dom_size - (j * dom_size / (2 * l)) % dom_size) % dom_size); + xt[k + 2 * j * m + m] = (c0 - c1).mul(wj_2l.into_repr()); // Difference #1 to forward DFT + } + } + l /= 2; + m *= 2; + dom_fr = dom_fr + dom_fr; + xvec = xt; + } + + let domain_inverse = dom_fr.inverse().unwrap().into_repr(); + + xvec.iter().map(|x| x.mul(domain_inverse)).collect() +} + +// compute idft of size @dom_size over vector of G1 elements +// q_i = (h_0 + h_1w^-i + h_2w^{-2i}+\cdots + +// h_{dom_size-1}w^{-(dom_size-1)i})/dom_size for 0<= i< dom_size=2^p +pub fn field_inv_dft(h: &[F], p: usize) -> Vec { + let dom_size = 1 << p; + assert_eq!(h.len(), dom_size); // we do not support inputs of size not power of 2 + let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(dom_size).unwrap(); + let mut l = dom_size / 2; + let mut m: usize = 1; + let mut dom_fr = F::one(); + // Stockham FFT + let mut xvec = h.to_vec(); + for _ in 0..p { + let mut xt = xvec.clone(); + for j in 0..l { + for k in 0..m { + let c0 = xvec[k + j * m]; + let c1 = xvec[k + j * m + l * m]; + xt[k + 2 * j * m] = c0 + c1; + let wj_2l = input_domain + .element((dom_size - (j * dom_size / (2 * l)) % dom_size) % dom_size); + xt[k + 2 * j * m + m] = (c0 - c1) * wj_2l; // Difference #1 to + // forward DFT + } + } + l /= 2; + m *= 2; + dom_fr = dom_fr + dom_fr; + xvec = xt; + } + + let domain_inverse = dom_fr.inverse().unwrap(); + + xvec.iter().map(|&x| x * domain_inverse).collect() +} + +#[cfg(test)] +pub mod tests { + use super::*; + use ark_bls12_377::Bls12_377; + use ark_bls12_381::Bls12_381; + use ark_ec::PairingEngine; + use ark_std::{test_rng, UniformRand}; + + #[test] + fn test_dft() { + test_dft_helper::(); + test_dft_helper::(); + } + + fn test_dft_helper() { + let mut rng = test_rng(); + for i in 2..6 { + let size = 1 << i; + + let h: Vec = (0..size).map(|_| E::Fr::rand(&mut rng)).collect(); + + let c_dft = field_dft::(&h, i); + let c_back = field_inv_dft::(&c_dft, i); + assert_eq!(h, c_back); + + let h: Vec = + (0..size).map(|_| E::G1Projective::rand(&mut rng)).collect(); + + let c_dft = group_dft::(&h, i); + let c_back = group_inv_dft::(&c_dft, i); + assert_eq!(h, c_back); + + let h: Vec = + (0..size).map(|_| E::G2Projective::rand(&mut rng)).collect(); + + let c_dft = group_dft::(&h, i); + let c_back = group_inv_dft::(&c_dft, i); + assert_eq!(h, c_back); + } + } +} diff --git a/src/kzg.rs b/src/kzg.rs new file mode 100644 index 0000000..4187a29 --- /dev/null +++ b/src/kzg.rs @@ -0,0 +1,698 @@ +// This file includes backend tools: +// (1) read_line() is for taking inputs from the user +// (2) kzg_open_g1 is for opening KZG commitments +// (3) kzg_verify_g1 is for verifying KZG commitments +// (4) hash_caulk_single is for hashing group and field elements into a field +// element (5) random_field is for generating random field elements + +use crate::{compute_h, group_dft, util::convert_to_bigints}; +use ark_ec::{msm::VariableBaseMSM, AffineCurve, PairingEngine, ProjectiveCurve}; +use ark_ff::Field; +use ark_poly::{ + univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain, Polynomial, + UVPolynomial, +}; +use ark_poly_commit::kzg10::*; +use ark_std::{end_timer, start_timer, One, Zero}; +#[cfg(feature = "parallel")] +use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; +use std::marker::PhantomData; + +///////////////////////////////////////////////////////////////////// +// KZG opening and verifying +///////////////////////////////////////////////////////////////////// + +pub struct KZGCommit { + phantom: PhantomData, +} + +impl KZGCommit { + pub fn commit_g1(powers: &Powers, polynomial: &DensePolynomial) -> E::G1Affine { + let (com, _randomness) = KZG10::::commit(powers, polynomial, None, None).unwrap(); + com.0 + } + + pub fn commit_g2(g2_powers: &[E::G2Affine], poly: &DensePolynomial) -> E::G2Affine { + // todo:MSM? + let mut res = g2_powers[0].mul(poly[0]); + for i in 1..poly.len() { + res = res + g2_powers[i].mul(poly[i]) + } + res.into_affine() + } + + // Function to commit to f(X,Y) + // here f = [ [a0, a1, a2], [b1, b2, b3] ] represents (a0 + a1 Y + a2 Y^2 ) + X + // (b1 + b2 Y + b3 Y^2) + // + // First we unwrap to get a vector of form [a0, a1, a2, b0, b1, b2] + // Then we commit to f as a commitment to f'(X) = a0 + a1 X + a2 X^2 + b0 X^3 + + // b1 X^4 + b2 X^5 + // + // We also need to know the maximum degree of (a0 + a1 Y + a2 Y^2 ) to prevent + // overflow errors. + // + // This is described in Section 4.6.2 + pub fn bipoly_commit( + pp: &crate::multi::PublicParameters, + poly: &Vec>, + deg_x: usize, + ) -> E::G1Affine { + let mut poly_formatted = Vec::new(); + + for i in 0..poly.len() { + let temp = convert_to_bigints(&poly[i].coeffs); + for j in 0..poly[i].len() { + poly_formatted.push(temp[j]); + } + let temp = convert_to_bigints(&[E::Fr::zero()].to_vec())[0]; + for _ in poly[i].len()..deg_x { + poly_formatted.push(temp); + } + } + + assert!(pp.poly_ck.powers_of_g.len() >= poly_formatted.len()); + let g1_poly = + VariableBaseMSM::multi_scalar_mul(&pp.poly_ck.powers_of_g, poly_formatted.as_slice()) + .into_affine(); + + g1_poly + } + + // compute all openings to c_poly using a smart formula + // This Code implements an algorithm for calculating n openings of a KZG vector + // commitment of size n in n log(n) time. The algorithm is by Feist and + // Khovratovich. It is useful for preprocessing. + // The full algorithm is described here https://github.com/khovratovich/Kate/blob/master/Kate_amortized.pdf + pub fn multiple_open( + c_poly: &DensePolynomial, // c(X) + powers: &[G], // SRS + p: usize, + ) -> Vec + where + G: AffineCurve + Sized, + { + let timer = start_timer!(|| "multiple open"); + + let degree = c_poly.coeffs.len() - 1; + let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(degree).unwrap(); + + let h_timer = start_timer!(|| "compute h"); + let powers: Vec = powers.iter().map(|x| x.into_projective()).collect(); + let h2 = compute_h(c_poly, &powers, p); + end_timer!(h_timer); + + let dom_size = input_domain.size(); + assert_eq!(1 << p, dom_size); + assert_eq!(degree + 1, dom_size); + + let dft_timer = start_timer!(|| "G1 dft"); + let q2 = group_dft::(&h2, p); + end_timer!(dft_timer); + + let res = G::Projective::batch_normalization_into_affine(q2.as_ref()); + + end_timer!(timer); + res + } + + //////////////////////////////////////////////// + // KZG.Open( srs_KZG, f(X, Y), deg, alpha ) + // returns ([f(alpha, x)]_1, pi) + // Algorithm described in Section 4.6.2, KZG for Bivariate Polynomials + pub fn partial_open_g1( + pp: &crate::multi::PublicParameters, + poly: &[DensePolynomial], + deg_x: usize, + point: &E::Fr, + ) -> (E::G1Affine, E::G1Affine, DensePolynomial) { + let mut poly_partial_eval = DensePolynomial::from_coefficients_vec(vec![E::Fr::zero()]); + let mut alpha = E::Fr::one(); + for i in 0..poly.len() { + let pow_alpha = DensePolynomial::from_coefficients_vec(vec![alpha.clone()]); + poly_partial_eval = poly_partial_eval + &pow_alpha * &poly[i]; + alpha = alpha * point; + } + + let eval = VariableBaseMSM::multi_scalar_mul( + &pp.poly_ck.powers_of_g, + convert_to_bigints(&poly_partial_eval.coeffs).as_slice(), + ) + .into_affine(); + + let mut witness_bipolynomial = Vec::new(); + let poly_reverse: Vec<_> = poly.into_iter().rev().collect(); + witness_bipolynomial.push(poly_reverse[0].clone()); + + let alpha = DensePolynomial::from_coefficients_vec(vec![point.clone()]); + for i in 1..(poly_reverse.len() - 1) { + witness_bipolynomial + .push(poly_reverse[i].clone() + &alpha * &witness_bipolynomial[i - 1]); + } + + witness_bipolynomial.reverse(); + + let proof = Self::bipoly_commit(pp, &witness_bipolynomial, deg_x); + + (eval, proof, poly_partial_eval) + } + + // KZG.Open( srs_KZG, f(X), deg, (alpha1, alpha2, ..., alphan) ) + // returns ([f(alpha1), ..., f(alphan)], pi) + // Algorithm described in Section 4.6.1, Multiple Openings + pub fn open_g1_batch( + poly_ck: &Powers, + poly: &DensePolynomial, + max_deg: Option<&usize>, + points: &[E::Fr], + ) -> (Vec, E::G1Affine) { + let mut evals = Vec::new(); + let mut proofs = Vec::new(); + for p in points.iter() { + let (eval, pi) = Self::open_g1_single(poly_ck, poly, max_deg, p); + evals.push(eval); + proofs.push(pi); + } + + let mut res = E::G1Projective::zero(); // default value + + for j in 0..points.len() { + let w_j = points[j]; + // 1. Computing coefficient [1/prod] + let mut prod = E::Fr::one(); + for (k, p) in points.iter().enumerate() { + if k != j { + prod *= w_j - p; + } + } + // 2. Summation + let q_add = proofs[j].mul(prod.inverse().unwrap()); //[1/prod]Q_{j} + res += q_add; + } + + (evals, res.into_affine()) + } + + // KZG.Open( srs_KZG, f(X), deg, alpha ) returns (f(alpha), pi) + fn open_g1_single( + poly_ck: &Powers, + poly: &DensePolynomial, + max_deg: Option<&usize>, + point: &E::Fr, + ) -> (E::Fr, E::G1Affine) { + let eval = poly.evaluate(point); + + let global_max_deg = poly_ck.powers_of_g.len(); + + let mut d: usize = 0; + if max_deg == None { + d += global_max_deg; + } else { + d += max_deg.unwrap(); + } + let divisor = DensePolynomial::from_coefficients_vec(vec![-*point, E::Fr::one()]); + let witness_polynomial = poly / &divisor; + + assert!(poly_ck.powers_of_g[(global_max_deg - d)..].len() >= witness_polynomial.len()); + let proof = VariableBaseMSM::multi_scalar_mul( + &poly_ck.powers_of_g[(global_max_deg - d)..], + convert_to_bigints(&witness_polynomial.coeffs).as_slice(), + ) + .into_affine(); + (eval, proof) + } + + // KZG.Verify( srs_KZG, F, deg, (alpha1, alpha2, ..., alphan), (v1, ..., vn), pi + // ) Algorithm described in Section 4.6.1, Multiple Openings + pub fn verify_g1( + // TODO: parameters struct + // Verify that @c_com is a commitment to C(X) such that C(x)=z + powers_of_g1: &[E::G1Affine], // generator of G1 + powers_of_g2: &[E::G2Affine], // [1]_2, [x]_2, [x^2]_2, ... + c_com: &E::G1Affine, // commitment + max_deg: Option<&usize>, // max degree + points: &[E::Fr], // x such that eval = C(x) + evals: &[E::Fr], // evaluation + pi: &E::G1Affine, // proof + ) -> bool { + // Interpolation set + // tau_i(X) = lagrange_tau[i] = polynomial equal to 0 at point[j] for j!= i and + // 1 at points[i] + + let mut lagrange_tau = DensePolynomial::from_coefficients_slice(&[E::Fr::zero()]); + let mut prod = DensePolynomial::from_coefficients_slice(&[E::Fr::one()]); + let mut components = vec![]; + for &p in points.iter() { + let poly = DensePolynomial::from_coefficients_slice(&[-p, E::Fr::one()]); + prod = &prod * (&poly); + components.push(poly); + } + + for i in 0..points.len() { + let mut temp = &prod / &components[i]; + let lagrange_scalar = temp.evaluate(&points[i]).inverse().unwrap() * evals[i]; + temp.coeffs.iter_mut().for_each(|x| *x *= lagrange_scalar); + lagrange_tau = lagrange_tau + temp; + } + + // commit to sum evals[i] tau_i(X) + assert!( + powers_of_g1.len() >= lagrange_tau.len(), + "KZG verifier doesn't have enough g1 powers" + ); + let g1_tau = VariableBaseMSM::multi_scalar_mul( + &powers_of_g1[..lagrange_tau.len()], + convert_to_bigints(&lagrange_tau.coeffs).as_slice(), + ); + + // vanishing polynomial + let z_tau = prod; + + // commit to z_tau(X) in g2 + assert!( + powers_of_g2.len() >= z_tau.len(), + "KZG verifier doesn't have enough g2 powers" + ); + let g2_z_tau = VariableBaseMSM::multi_scalar_mul( + &powers_of_g2[..z_tau.len()], + convert_to_bigints(&z_tau.coeffs).as_slice(), + ); + + let global_max_deg = powers_of_g1.len(); + + let mut d: usize = 0; + if max_deg == None { + d += global_max_deg; + } else { + d += max_deg.unwrap(); + } + + let pairing_inputs = vec![ + ( + E::G1Prepared::from((g1_tau - c_com.into_projective()).into_affine()), + E::G2Prepared::from(powers_of_g2[global_max_deg - d]), + ), + ( + E::G1Prepared::from(*pi), + E::G2Prepared::from(g2_z_tau.into_affine()), + ), + ]; + + E::product_of_pairings(pairing_inputs.iter()).is_one() + } + + // KZG.Verify( srs_KZG, F, deg, alpha, F_alpha, pi ) + // Algorithm described in Section 4.6.2, KZG for Bivariate Polynomials + // Be very careful here. Verification is only valid if it is paired with a + // degree check. + pub fn partial_verify_g1( + srs: &crate::multi::PublicParameters, + c_com: &E::G1Affine, // commitment + deg_x: usize, + point: &E::Fr, + partial_eval: &E::G1Affine, + pi: &E::G1Affine, // proof + ) -> bool { + // todo: pairing product + let pairing1 = E::pairing( + c_com.into_projective() - partial_eval.into_projective(), + srs.g2_powers[0], + ); + let pairing2 = E::pairing( + *pi, + srs.g2_powers[deg_x].into_projective() - srs.g2_powers[0].mul(*point), + ); + + pairing1 == pairing2 + } + + // Algorithm for aggregating KZG proofs into a single proof + // Described in Section 4.6.3 Subset openings + // compute Q =\sum_{j=1}^m \frac{Q_{i_j}}}{\prod_{1\leq k\leq m,\; k\neq + // j}(\omega^{i_j}-\omega^{i_k})} + pub fn aggregate_proof_g2( + openings: &[E::G2Affine], // Q_i + positions: &[usize], // i_j + input_domain: &GeneralEvaluationDomain, + ) -> E::G2Affine { + let m = positions.len(); + let mut res = openings[0].into_projective(); // default value + + for j in 0..m { + let i_j = positions[j]; + let w_ij = input_domain.element(i_j); + // 1. Computing coefficient [1/prod] + let mut prod = E::Fr::one(); + for k in 0..m { + let i_k = positions[k]; + let w_ik = input_domain.element(i_k); + if k != j { + prod = prod * (w_ij - w_ik); + } + } + // 2. Summation + let q_add = openings[i_j].mul(prod.inverse().unwrap()); //[1/prod]Q_{j} + if j == 0 { + res = q_add; + } else { + res = res + q_add; + } + } + res.into_affine() + } +} + +pub fn generate_lagrange_polynomials_subset( + positions: &[usize], + srs: &crate::multi::PublicParameters, +) -> Vec> { + let mut tau_polys = vec![]; + let m = positions.len(); + for j in 0..m { + let mut tau_j = DensePolynomial::from_coefficients_slice(&[E::Fr::one()]); // start from tau_j =1 + for k in 0..m { + if k != j { + // tau_j = prod_{k\neq j} (X-w^(i_k))/(w^(i_j)-w^(i_k)) + let denum = srs.domain_N.element(positions[j]) - srs.domain_N.element(positions[k]); + tau_j = &tau_j + * &DensePolynomial::from_coefficients_slice(&[ + -srs.domain_N.element(positions[k]) / denum, //-w^(i_k))/(w^(i_j)-w^(i_k) + E::Fr::one() / denum, // 1//(w^(i_j)-w^(i_k)) + ]); + } + } + tau_polys.push(tau_j.clone()); + } + tau_polys +} + +#[cfg(test)] +pub mod tests { + + use super::{generate_lagrange_polynomials_subset, KZGCommit, *}; + use crate::caulk_single_setup; + use ark_bls12_377::Bls12_377; + use ark_bls12_381::Bls12_381; + use ark_ec::{AffineCurve, PairingEngine, ProjectiveCurve}; + use ark_poly::{univariate::DensePolynomial, EvaluationDomain, Polynomial, UVPolynomial}; + use ark_poly_commit::kzg10::KZG10; + use ark_std::{test_rng, One, Zero}; + use std::time::Instant; + + #[test] + fn test_lagrange() { + test_lagrange_helper::(); + test_lagrange_helper::(); + } + + #[allow(non_snake_case)] + fn test_lagrange_helper() { + let p: usize = 8; // bitlength of poly degree + let max_degree: usize = (1 << p) + 2; + let m: usize = 8; + let N: usize = 1 << p; + + let now = Instant::now(); + let pp = crate::multi::PublicParameters::::setup(&max_degree, &N, &m, &p); + println!("time to setup {:?}", now.elapsed()); + + let mut positions: Vec = vec![]; + for i in 0..m { + // generate positions evenly distributed in the set + let i_j: usize = i * (max_degree / m); + positions.push(i_j); + } + + let tau_polys = generate_lagrange_polynomials_subset(&positions, &pp); + for j in 0..m { + for k in 0..m { + if k == j { + assert_eq!( + tau_polys[j].evaluate(&pp.domain_N.element(positions[k])), + E::Fr::one() + ) + } else { + assert_eq!( + tau_polys[j].evaluate(&pp.domain_N.element(positions[k])), + E::Fr::zero() + ) + } + } + } + } + + #[test] + #[allow(non_snake_case)] + pub fn test_Q_g2() { + test_Q_g2_helper::(); + test_Q_g2_helper::(); + } + + #[allow(non_snake_case)] + pub fn test_Q_g2_helper() { + let rng = &mut ark_std::test_rng(); + + // current kzg setup should be changed with output from a setup ceremony + let p: usize = 6; // bitlength of poly degree + let max_degree: usize = (1 << p) + 2; + let actual_degree: usize = (1 << p) - 1; + let m: usize = 1 << (p / 2); + let N: usize = 1 << p; + let pp = crate::multi::PublicParameters::setup(&max_degree, &N, &m, &p); + + // Setting up test instance to run evaluate on. + // test randomness for c_poly is same everytime. + // test index equals 5 everytime + // g_c = g^(c(x)) + + let c_poly = DensePolynomial::::rand(actual_degree, rng); + let c_com = KZGCommit::::commit_g1(&pp.poly_ck, &c_poly); + + let now = Instant::now(); + let openings = KZGCommit::::multiple_open::(&c_poly, &pp.g2_powers, p); + println!("Multi advanced computed in {:?}", now.elapsed()); + + let mut positions: Vec = vec![]; + for i in 0..m { + let i_j: usize = i * (max_degree / m); + positions.push(i_j); + } + + let now = Instant::now(); + + // Compute proof + let Q: E::G2Affine = + KZGCommit::::aggregate_proof_g2(&openings, &positions, &pp.domain_N); + println!( + "Full proof for {:?} positions computed in {:?}", + m, + now.elapsed() + ); + + // Compute commitment to C_I + let mut C_I = DensePolynomial::from_coefficients_slice(&[E::Fr::zero()]); // C_I = sum_j c_j*tau_j + let tau_polys = generate_lagrange_polynomials_subset(&positions, &pp); + for j in 0..m { + C_I = &C_I + &(&tau_polys[j] * c_poly.evaluate(&pp.domain_N.element(positions[j]))); + // sum_j c_j*tau_j + } + let c_I_com = KZGCommit::::commit_g1(&pp.poly_ck, &C_I); + + // Compute commitment to z_I + let mut z_I = DensePolynomial::from_coefficients_slice(&[E::Fr::one()]); + for j in 0..m { + z_I = &z_I + * &DensePolynomial::from_coefficients_slice(&[ + -pp.domain_N.element(positions[j]), + E::Fr::one(), + ]); + } + let z_I_com = KZGCommit::::commit_g1(&pp.poly_ck, &z_I); + + // pairing check + let pairing1 = E::pairing( + (c_com.into_projective() - c_I_com.into_projective()).into_affine(), + pp.g2_powers[0], + ); + let pairing2 = E::pairing(z_I_com, Q); + assert_eq!(pairing1, pairing2); + } + + #[test] + fn test_single() { + test_single_helper::(); + test_single_helper::(); + } + + fn test_single_helper() { + let mut rng = test_rng(); + + // setting public parameters + // current kzg setup should be changed with output from a setup ceremony + let max_degree: usize = 100; + let actual_degree: usize = 63; + let pp = caulk_single_setup(max_degree, actual_degree, &mut rng); + + // Setting up test instance to run evaluate on. + // test randomness for c_poly is same everytime. + // test index equals 5 everytime + // g_c = g^(c(x)) + let rng = &mut test_rng(); + let c_poly = DensePolynomial::::rand(actual_degree, rng); + let (_c_com, c_com_open) = KZG10::::commit(&pp.poly_ck, &c_poly, None, None).unwrap(); + + let i: usize = 6; + let q = single_open_default(&c_poly, &c_com_open, &pp.poly_ck, i, actual_degree); + let q2 = single_open_fast(&c_poly, &pp.poly_ck, i, actual_degree); + assert_eq!(q, q2); + } + + #[test] + pub fn test_multi() { + test_multi_helper::(); + test_multi_helper::(); + } + + pub fn test_multi_helper() { + let mut rng = test_rng(); + + // current kzg setup should be changed with output from a setup ceremony + let p: usize = 9; + let max_degree: usize = 1 << p + 1; + let actual_degree: usize = (1 << p) - 1; + let pp = caulk_single_setup(max_degree, actual_degree, &mut rng); + + // Setting up test instance to run evaluate on. + // test randomness for c_poly is same everytime. + // test index equals 5 everytime + // g_c = g^(c(x)) + let c_poly = DensePolynomial::::rand(actual_degree, &mut rng); + let (c_com, c_com_open) = KZG10::::commit(&pp.poly_ck, &c_poly, None, None).unwrap(); + let _g_c = c_com.0; + + let now = Instant::now(); + let q = multiple_open_naive(&c_poly, &c_com_open, &pp.poly_ck, actual_degree); + println!("Multi naive computed in {:?}", now.elapsed()); + + let now = Instant::now(); + let q2 = KZGCommit::::multiple_open::(&c_poly, &pp.poly_ck.powers_of_g, p); + println!("Multi advanced computed in {:?}", now.elapsed()); + assert_eq!(q, q2); + } + + #[test] + fn test_commit() { + test_commit_helper::(); + test_commit_helper::(); + } + + pub fn test_commit_helper() { + let mut rng = test_rng(); + + // current kzg setup should be changed with output from a setup ceremony + let max_degree: usize = 100; + let actual_degree: usize = 63; + let pp = caulk_single_setup(max_degree, actual_degree, &mut rng); + + // Setting up test instance to run evaluate on. + // test randomness for c_poly is same everytime. + // g_c = g^(c(x)) + let c_poly = DensePolynomial::::rand(actual_degree, &mut rng); + let (c_com, _c_com_open) = KZG10::::commit(&pp.poly_ck, &c_poly, None, None).unwrap(); + let g_c1 = c_com.0; + + let g_c2 = commit_direct(&c_poly, &pp.poly_ck); + assert_eq!(g_c1, g_c2); + println!("commit test passed") + } + + /// Various functions that are used for testing + + fn commit_direct( + c_poly: &DensePolynomial, // c(X) + poly_ck: &Powers, // SRS + ) -> E::G1Affine { + assert!(c_poly.coeffs.len() <= poly_ck.powers_of_g.len()); + let mut com = poly_ck.powers_of_g[0].mul(c_poly.coeffs[0]); + for i in 1..c_poly.coeffs.len() { + com = com + poly_ck.powers_of_g[i].mul(c_poly.coeffs[i]); + } + com.into_affine() + } + + // compute all openings to c_poly by mere calling `open` N times + fn multiple_open_naive( + c_poly: &DensePolynomial, + c_com_open: &Randomness>, + poly_ck: &Powers, + degree: usize, + ) -> Vec { + let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(degree).unwrap(); + let mut res: Vec = vec![]; + for i in 0..input_domain.size() { + let omega_i = input_domain.element(i); + res.push(kzg_open_g1_test::(&c_poly, &omega_i, &c_com_open, &poly_ck).w); + } + res + } + + //////////////////////////////////////////////// + fn kzg_open_g1_test( + p: &DensePolynomial, + omega_5: &E::Fr, + polycom_open: &Randomness>, + poly_ck: &Powers, + ) -> Proof { + let rng = &mut ark_std::test_rng(); + + let (witness_polynomial, _random_witness_polynomial) = + KZG10::::compute_witness_polynomial(p, omega_5.clone(), polycom_open).unwrap(); + + let (temp0, _temp1) = KZG10::commit(poly_ck, &witness_polynomial, None, Some(rng)).unwrap(); + Proof { + w: temp0.0, + random_v: None, + } + } + + // compute KZG proof Q = g1_q = g^( (c(x) - c(w^i)) / (x - w^i) ) where x is + // secret, w^i is the point where we open, and c(X) is the committed polynomial + fn single_open_default( + c_poly: &DensePolynomial, // c(X) + c_com_open: &Randomness>, // + poly_ck: &Powers, + i: usize, // + degree: usize, + ) -> E::G1Affine { + let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(degree).unwrap(); + let omega_i = input_domain.element(i); + let c_poly_open = kzg_open_g1_test(&c_poly, &omega_i, &c_com_open, &poly_ck); + c_poly_open.w + } + + // KZG proof/opening at point y for c(X) = sum_i c_i X^i + //(1)T_y(X) = sum_i t_i X^i + //(2) t_{deg-1} = c_deg + //(3) t_j = c_{j+1} + y*t_{j+1} + fn single_open_fast( + c_poly: &DensePolynomial, // c(X) + poly_ck: &Powers, // SRS + i: usize, // y=w^i + degree: usize, // degree of c(X) + ) -> E::G1Affine { + // computing opening point + let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(degree).unwrap(); + let y = input_domain.element(i); + + // compute quotient + let mut t_poly = c_poly.clone(); + t_poly.coeffs.remove(0); // shifting indices + for j in (0..t_poly.len() - 1).rev() { + t_poly.coeffs[j] = c_poly.coeffs[j + 1] + y * t_poly.coeffs[j + 1] + } + + // commit + let (t_com, _) = KZG10::commit(&poly_ck, &t_poly, None, None).unwrap(); + t_com.0 + } +} diff --git a/caulk_single_opening/src/lib.rs b/src/lib.rs similarity index 81% rename from caulk_single_opening/src/lib.rs rename to src/lib.rs index c2ad329..ff5d44e 100644 --- a/caulk_single_opening/src/lib.rs +++ b/src/lib.rs @@ -1,32 +1,32 @@ -mod caulk_single; -mod caulk_single_setup; -mod caulk_single_unity; mod dft; mod kzg; +pub mod multi; mod pedersen; +mod single; mod transcript; +pub(crate) mod util; -pub use caulk_single::{caulk_single_prove, caulk_single_verify}; -pub use caulk_single_setup::caulk_single_setup; pub use dft::*; pub use kzg::KZGCommit; +pub use multi::PublicParameters; pub use pedersen::PedersenParam; +pub use single::{caulk_single_prove, caulk_single_verify, setup::caulk_single_setup}; pub use transcript::CaulkTranscript; #[cfg(test)] mod tests { - use crate::caulk_single_setup; - use crate::CaulkTranscript; - use crate::KZGCommit; - use crate::{caulk_single_prove, caulk_single_verify}; - use ark_bls12_381::{Bls12_381, Fr}; + use crate::{ + caulk_single_prove, caulk_single_setup, caulk_single_verify, CaulkTranscript, KZGCommit, + }; + use ark_bls12_381::{Bls12_381, Fr, G1Affine}; use ark_ec::{AffineCurve, ProjectiveCurve}; - use ark_poly::univariate::DensePolynomial; - use ark_poly::{EvaluationDomain, GeneralEvaluationDomain, Polynomial, UVPolynomial}; + use ark_poly::{ + univariate::DensePolynomial, EvaluationDomain, GeneralEvaluationDomain, Polynomial, + UVPolynomial, + }; use ark_poly_commit::kzg10::KZG10; - use ark_std::test_rng; - use ark_std::UniformRand; + use ark_std::{test_rng, UniformRand}; type UniPoly381 = DensePolynomial; type KzgBls12_381 = KZG10; @@ -50,7 +50,7 @@ mod tests { let (g1_C, _) = KzgBls12_381::commit(&pp.poly_ck, &c_poly, None, None).unwrap(); let g1_C = g1_C.0; - //point at which we will open c_com + // point at which we will open c_com let input_domain: GeneralEvaluationDomain = EvaluationDomain::new(actual_degree).unwrap(); @@ -96,7 +96,11 @@ mod tests { } // compute all openings { - let g1_qs = KZGCommit::multiple_open(&c_poly, &pp.poly_ck, p); + let g1_qs = KZGCommit::::multiple_open::( + &c_poly, + &pp.poly_ck.powers_of_g, + p, + ); let g1_q = g1_qs[position]; // run the prover diff --git a/caulk_multi_lookup/src/caulk_multi_lookup.rs b/src/multi/mod.rs similarity index 54% rename from caulk_multi_lookup/src/caulk_multi_lookup.rs rename to src/multi/mod.rs index 3da4999..13a6140 100644 --- a/caulk_multi_lookup/src/caulk_multi_lookup.rs +++ b/src/multi/mod.rs @@ -1,71 +1,67 @@ -/* -This file includes the Caulk prover and verifier for single openings. -The protocol is described in Figure 3. -*/ +// This file includes the Caulk prover and verifier for single openings. +// The protocol is described in Figure 3. -use ark_bls12_381::{Bls12_381, Fr, FrParameters, G1Affine, G2Affine}; -use ark_ff::{Field, Fp256}; -use ark_poly::{univariate::DensePolynomial, Evaluations as EvaluationsOnDomain}; +pub mod setup; +mod unity; +use crate::{kzg::generate_lagrange_polynomials_subset, CaulkTranscript, KZGCommit}; use ark_ec::{AffineCurve, PairingEngine, ProjectiveCurve}; -use ark_poly::{EvaluationDomain, Evaluations, GeneralEvaluationDomain, Polynomial, UVPolynomial}; -use ark_serialize::CanonicalSerialize; - -use ark_std::{cfg_into_iter, One, Zero}; - -use std::time::Instant; -use std::vec::Vec; - -use crate::caulk_multi_setup::{setup_multi_lookup, PublicParameters}; -use crate::caulk_multi_unity::{prove_multiunity, verify_multiunity, ProofMultiUnity}; -use crate::tools::{ - aggregate_kzg_proofs_g2, generate_lagrange_polynomials_subset, hash_caulk_multi, kzg_commit_g2, - kzg_open_g1_native, kzg_verify_g1_native, random_field, KzgBls12_381, UniPoly381, +use ark_ff::{Field, PrimeField}; +use ark_poly::{ + univariate::DensePolynomial, EvaluationDomain, Evaluations as EvaluationsOnDomain, Evaluations, + GeneralEvaluationDomain, Polynomial, UVPolynomial, }; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{cfg_into_iter, rand::RngCore, One, UniformRand, Zero}; +pub use setup::PublicParameters; +use std::{ + convert::TryInto, + fs::File, + io::{Read, Write}, + time::Instant, + vec::Vec, +}; +use unity::{prove_multiunity, verify_multiunity, ProofMultiUnity}; -use crate::multiopen::multiple_open_g2; - -pub struct LookupInstance { - pub c_com: G1Affine, //polynomial C(X) that represents a table - pub phi_com: G1Affine, //polynomial phi(X) that represents the values to look up +pub struct LookupInstance { + pub c_com: C, // polynomial C(X) that represents a table + pub phi_com: C, // polynomial phi(X) that represents the values to look up } -pub struct LookupProverInput { - pub c_poly: DensePolynomial>, //polynomial C(X) that represents a table - pub phi_poly: DensePolynomial>, //polynomial phi(X) that represents the values to look up - pub positions: Vec, // - pub openings: Vec, +pub struct LookupProverInput { + pub c_poly: DensePolynomial, // polynomial C(X) that represents a table + pub phi_poly: DensePolynomial, // polynomial phi(X) that represents the values to look up + pub positions: Vec, // + pub openings: Vec, } #[derive(Debug, PartialEq)] -//Data structure to be stored in a file: polynomial, its commitment, and its openings (for certain SRS) -pub struct TableInput { - pub c_poly: DensePolynomial>, - pub c_com: G1Affine, - pub openings: Vec, +// Data structure to be stored in a file: polynomial, its commitment, and its +// openings (for certain SRS) +pub struct TableInput { + pub c_poly: DensePolynomial, + pub c_com: E::G1Affine, + pub openings: Vec, } -//Lookup proof data structure +// Lookup proof data structure #[allow(non_snake_case)] -pub struct LookupProof { - pub C_I_com: G1Affine, //Commitment to C_I(X) - pub H1_com: G2Affine, //Commmitment to H_1(X) - pub H2_com: G1Affine, //Commmitment to H_2(X) - pub u_com: G1Affine, //Commmitment to u(X) - pub z_I_com: G1Affine, //Commitment to z_I(X) - pub v1: Fr, - pub v2: Fr, - pub pi1: G1Affine, - pub pi2: G1Affine, - pub pi3: G1Affine, +pub struct LookupProof { + pub C_I_com: E::G1Affine, // Commitment to C_I(X) + pub H1_com: E::G2Affine, // Commitment to H_1(X) + pub H2_com: E::G1Affine, // Commitment to H_2(X) + pub u_com: E::G1Affine, // Commitment to u(X) + pub z_I_com: E::G1Affine, // Commitment to z_I(X) + pub v1: E::Fr, + pub v2: E::Fr, + pub pi1: E::G1Affine, + pub pi2: E::G1Affine, + pub pi3: E::G1Affine, } -impl TableInput { +impl TableInput { fn store(&self, path: &str) { - use std::fs::File; - use std::io::Write; - - //1. Polynomial + // 1. Polynomial let mut o_bytes = vec![]; let mut f = File::create(path).expect("Unable to create file"); let len: u32 = self.c_poly.len().try_into().unwrap(); @@ -79,12 +75,12 @@ impl TableInput { } f.write_all(&o_bytes).expect("Unable to write data"); - //2. Commitment + // 2. Commitment o_bytes.clear(); self.c_com.serialize_uncompressed(&mut o_bytes).ok(); f.write_all(&o_bytes).expect("Unable to write data"); - //3. Openings + // 3. Openings o_bytes.clear(); let len: u32 = self.openings.len().try_into().unwrap(); let len_bytes = len.to_be_bytes(); @@ -96,10 +92,7 @@ impl TableInput { f.write_all(&o_bytes).expect("Unable to write data"); } - fn load(path: &str) -> TableInput { - use ark_serialize::CanonicalDeserialize; - use std::fs::File; - use std::io::Read; + fn load(path: &str) -> TableInput { const FR_UNCOMPR_SIZE: usize = 32; const G1_UNCOMPR_SIZE: usize = 96; const G2_UNCOMPR_SIZE: usize = 192; @@ -107,7 +100,7 @@ impl TableInput { let mut f = File::open(path).expect("Unable to open file"); f.read_to_end(&mut data).expect("Unable to read data"); - //1. reading c_poly + // 1. reading c_poly let mut cur_counter: usize = 0; let len_bytes: [u8; 4] = (&data[0..4]).try_into().unwrap(); let len: u32 = u32::from_be_bytes(len_bytes); @@ -117,17 +110,17 @@ impl TableInput { for i in 0..len32 { let buf_bytes = &data[cur_counter + i * FR_UNCOMPR_SIZE..cur_counter + (i + 1) * FR_UNCOMPR_SIZE]; - let tmp = Fr::deserialize_unchecked(buf_bytes).unwrap(); + let tmp = E::Fr::deserialize_unchecked(buf_bytes).unwrap(); coeffs.push(tmp); } cur_counter += len32 * FR_UNCOMPR_SIZE; - //2. c_com + // 2. c_com let buf_bytes = &data[cur_counter..cur_counter + G1_UNCOMPR_SIZE]; - let c_com = G1Affine::deserialize_unchecked(buf_bytes).unwrap(); + let c_com = E::G1Affine::deserialize_unchecked(buf_bytes).unwrap(); cur_counter += G1_UNCOMPR_SIZE; - //3 openings + // 3 openings let len_bytes: [u8; 4] = (&data[cur_counter..cur_counter + 4]).try_into().unwrap(); let len: u32 = u32::from_be_bytes(len_bytes); let mut openings = vec![]; @@ -135,42 +128,43 @@ impl TableInput { cur_counter += 4; for _ in 0..len32 { let buf_bytes = &data[cur_counter..cur_counter + G2_UNCOMPR_SIZE]; - let tmp = G2Affine::deserialize_unchecked(buf_bytes).unwrap(); + let tmp = E::G2Affine::deserialize_unchecked(buf_bytes).unwrap(); openings.push(tmp); cur_counter += G2_UNCOMPR_SIZE; } return TableInput { c_poly: DensePolynomial { coeffs }, - c_com: c_com, - openings: openings, + c_com, + openings, }; } } #[allow(non_snake_case)] -pub fn compute_lookup_proof( - instance: &LookupInstance, - input: &LookupProverInput, - srs: &PublicParameters, -) -> (LookupProof, ProofMultiUnity) { +pub fn compute_lookup_proof( + instance: &LookupInstance, + input: &LookupProverInput, + srs: &PublicParameters, + rng: &mut R, +) -> (LookupProof, ProofMultiUnity) { let m = input.positions.len(); /////////////////////////////////////////////////////////////////// - //1. Blinders + // 1. Blinders /////////////////////////////////////////////////////////////////// // provers blinders for zero-knowledge - let r1: Fp256 = random_field::(); - let r2: Fp256 = random_field::(); - let r3: Fp256 = random_field::(); - let r4: Fp256 = random_field::(); - let r5: Fp256 = random_field::(); - let r6: Fp256 = random_field::(); - let r7: Fp256 = random_field::(); + let r1 = E::Fr::rand(rng); + let r2 = E::Fr::rand(rng); + let r3 = E::Fr::rand(rng); + let r4 = E::Fr::rand(rng); + let r5 = E::Fr::rand(rng); + let r6 = E::Fr::rand(rng); + let r7 = E::Fr::rand(rng); /////////////////////////////////////////////////////////////////// - //2. Compute z_I(X) = r1 prod_{i in I} (X - w^i) + // 2. Compute z_I(X) = r1 prod_{i in I} (X - w^i) /////////////////////////////////////////////////////////////////// // z_I includes each position only once. @@ -194,29 +188,30 @@ pub fn compute_lookup_proof( z_I = &z_I * &DensePolynomial::from_coefficients_slice(&[ -srs.domain_N.element(positions_no_repeats[j]), - Fr::one(), + E::Fr::one(), ]); } /////////////////////////////////////////////////////////////////// - //2. Compute C_I(X) = (r_2+r_3X + r4X^2)*Z_I(X) + sum_j c_j*tau_j(X) + // 2. Compute C_I(X) = (r_2+r_3X + r4X^2)*Z_I(X) + sum_j c_j*tau_j(X) /////////////////////////////////////////////////////////////////// - let mut c_I_poly = DensePolynomial::from_coefficients_slice(&[Fr::zero()]); + let mut c_I_poly = DensePolynomial::from_coefficients_slice(&[E::Fr::zero()]); // tau_polys[i] = 1 at positions_no_repeats[i] and 0 at positions_no_repeats[j] - // Takes m^2 time, or 36ms when m = 32. Can be done in m log^2(m) time if this ever becomes a bottleneck. - // See https://people.csail.mit.edu/devadas/pubs/scalable_thresh.pdf + // Takes m^2 time, or 36ms when m = 32. Can be done in m log^2(m) time if this + // ever becomes a bottleneck. See https://people.csail.mit.edu/devadas/pubs/scalable_thresh.pdf let tau_polys = generate_lagrange_polynomials_subset(&positions_no_repeats, srs); // C_I(X) = sum_j c_j*tau_j(X) - // Takes m^2 time, or 38ms when m = 32. Can be done faster if we store c_poly evaluations. + // Takes m^2 time, or 38ms when m = 32. Can be done faster if we store c_poly + // evaluations. for j in 0..positions_no_repeats.len() { c_I_poly = &c_I_poly + &(&tau_polys[j] * input .c_poly - .evaluate(&srs.domain_N.element(positions_no_repeats[j]))); //sum_j c_j*tau_j + .evaluate(&srs.domain_N.element(positions_no_repeats[j]))); // sum_j c_j*tau_j } // extra_blinder = r2 + r3 X + r4 X^2 @@ -226,21 +221,23 @@ pub fn compute_lookup_proof( c_I_poly = &c_I_poly + &(&z_I * &extra_blinder); /////////////////////////////////////////////////////////////////// - //4. Compute H1 + // 4. Compute H1 /////////////////////////////////////////////////////////////////// // Compute [Q(x)]_2 by aggregating kzg proofs such that - // Q(X) = ( C(X) - sum_{i in I} c_{i+1} tau_i(X) ) / ( prod_{i in I} (X - w^i) ) - let g2_Q = aggregate_kzg_proofs_g2(&input.openings, &positions_no_repeats, &srs.domain_N); + // Q(X) = ( C(X) - sum_{i in I} c_{i+1} tau_i(X) ) / ( prod_{i in I} (X - + // w^i) ) + let g2_Q = + KZGCommit::::aggregate_proof_g2(&input.openings, &positions_no_repeats, &srs.domain_N); // blind_com = [ r2 + r3 x + r4 x^2 ]_2 - let blind_com = kzg_commit_g2(&extra_blinder, srs); + let blind_com = KZGCommit::::commit_g2(&srs.g2_powers, &extra_blinder); // H1_com = [ r1^{-1} Q(x) ]_2 - blind_com let H1_com = (g2_Q.mul(r1.inverse().unwrap()) - blind_com.into_projective()).into_affine(); /////////////////////////////////////////////////////////////////// - //5. Compute u(X) = sum_j w^{i_j} mu_j(X) + (r5 + r6 X + r7 X^2) z_{Vm}(X) + // 5. Compute u(X) = sum_j w^{i_j} mu_j(X) + (r5 + r6 X + r7 X^2) z_{Vm}(X) /////////////////////////////////////////////////////////////////// // u(X) = sum_j w^{i_j} mu_j(X) @@ -256,81 +253,76 @@ pub fn compute_lookup_proof( + &(extra_blinder2.mul_by_vanishing_poly(srs.domain_m)); /////////////////////////////////////////////////////////////////// - //6. Commitments + // 6. Commitments /////////////////////////////////////////////////////////////////// - let (z_I_com, _) = KzgBls12_381::commit(&srs.poly_ck, &z_I, None, None).unwrap(); - let (C_I_com, _) = KzgBls12_381::commit(&srs.poly_ck, &c_I_poly, None, None).unwrap(); - let (u_com, _) = KzgBls12_381::commit(&srs.poly_ck, &u_poly, None, None).unwrap(); + let z_I_com = KZGCommit::::commit_g1(&srs.poly_ck, &z_I); + let C_I_com = KZGCommit::::commit_g1(&srs.poly_ck, &c_I_poly); + let u_com = KZGCommit::::commit_g1(&srs.poly_ck, &u_poly); /////////////////////////////////////////////////////////////////// - //7 Prepare unity proof + // 7 Prepare unity proof /////////////////////////////////////////////////////////////////// - // hash_input initialised to zero - let mut hash_input = Fr::zero(); + // transcript initialised to zero + let mut transcript = CaulkTranscript::new(); - //let now = Instant::now(); + // let now = Instant::now(); let unity_proof = prove_multiunity( &srs, - &mut hash_input, - &u_com.0, + &mut transcript, + &u_com, u_vals.clone(), extra_blinder2, ); - //println!("Time to prove unity {:?}", now.elapsed()); + // println!("Time to prove unity {:?}", now.elapsed()); // quick test can be uncommented to check if unity proof verifies - // let unity_check = verify_multiunity( &srs, &mut Fr::zero(), u_com.0.clone(), &unity_proof ); - // println!("unity_check = {}", unity_check); + // let unity_check = verify_multiunity( &srs, &mut Fr::zero(), u_com.0.clone(), + // &unity_proof ); println!("unity_check = {}", unity_check); /////////////////////////////////////////////////////////////////// - //8. Hash outputs to get chi + // 8. Hash outputs to get chi /////////////////////////////////////////////////////////////////// - let chi = hash_caulk_multi::( - hash_input, - Some( - &[ - &instance.c_com, - &instance.phi_com, - // hash last round of unity proof for good practice - &unity_proof.g1_u_bar_alpha, - &unity_proof.g1_h_2_alpha, - &unity_proof.pi_1, - &unity_proof.pi_2, - &unity_proof.pi_3, - &unity_proof.pi_4, - &unity_proof.pi_5, - // lookup inputs - &C_I_com.0, - &z_I_com.0, - &u_com.0, - ] - .to_vec(), - ), - Some(&[&H1_com.clone()].to_vec()), - Some(&[&unity_proof.v1, &unity_proof.v2, &unity_proof.v3].to_vec()), - ); + transcript.append_element(b"c_com", &instance.c_com); + transcript.append_element(b"phi_com", &instance.phi_com); + transcript.append_element(b"u_bar_alpha", &unity_proof.g1_u_bar_alpha); + transcript.append_element(b"h2_alpha", &unity_proof.g1_h_2_alpha); + transcript.append_element(b"pi_1", &unity_proof.pi_1); + transcript.append_element(b"pi_2", &unity_proof.pi_2); + transcript.append_element(b"pi_3", &unity_proof.pi_3); + transcript.append_element(b"pi_4", &unity_proof.pi_4); + transcript.append_element(b"pi_5", &unity_proof.pi_5); + transcript.append_element(b"C_I_com", &C_I_com); + transcript.append_element(b"z_I_com", &z_I_com); + transcript.append_element(b"u_com", &u_com); - hash_input = chi.clone(); + transcript.append_element(b"h1_com", &H1_com); + + transcript.append_element(b"v1", &unity_proof.v1); + transcript.append_element(b"v2", &unity_proof.v2); + transcript.append_element(b"v3", &unity_proof.v3); + + let chi = transcript.get_and_append_challenge(b"chi"); /////////////////////////////////////////////////////////////////// - //9. Compute z_I( u(X) ) + // 9. Compute z_I( u(X) ) /////////////////////////////////////////////////////////////////// // Need a bigger domain to compute z_I(u(X)) over. // Has size O(m^2) - let domain_m_sq: GeneralEvaluationDomain = + let domain_m_sq: GeneralEvaluationDomain = GeneralEvaluationDomain::new(z_I.len() * u_poly.len() + 2).unwrap(); // id_poly(X) = 0 for sigma_i < m and 1 for sigma_i > m // used for when m is not a power of 2 - let mut id_poly = DensePolynomial::from_coefficients_slice(&[Fr::one()]); + let mut id_poly = DensePolynomial::from_coefficients_slice(&[E::Fr::one()]); id_poly = &id_poly - &srs.id_poly; // Compute z_I( u(X) + w^0 id(X) ) - // Computing z_I(u(X)) is done naively and could be faster. Currently this is not a bottleneck - let evals: Vec> = cfg_into_iter!(0..domain_m_sq.size()) + // Computing z_I(u(X)) is done naively and could be faster. Currently this is + // not a bottleneck + let evals: Vec = cfg_into_iter!(0..domain_m_sq.size()) .map(|k| { z_I.evaluate( &(u_poly.evaluate(&domain_m_sq.element(k)) @@ -341,14 +333,15 @@ pub fn compute_lookup_proof( let z_I_u_poly = Evaluations::from_vec_and_domain(evals, domain_m_sq).interpolate(); /////////////////////////////////////////////////////////////////// - //10. Compute C_I(u(X))-phi(X) + // 10. Compute C_I(u(X))-phi(X) /////////////////////////////////////////////////////////////////// // Compute C_I( u(X) ) - // Computing C_I(u(X)) is done naively and could be faster. Currently this is not a bottleneck + // Computing C_I(u(X)) is done naively and could be faster. Currently this is + // not a bottleneck - //Actually compute c_I( u(X) + id(X) ) in case m is not a power of 2 - let evals: Vec> = cfg_into_iter!(0..domain_m_sq.size()) + // Actually compute c_I( u(X) + id(X) ) in case m is not a power of 2 + let evals: Vec = cfg_into_iter!(0..domain_m_sq.size()) .map(|k| { c_I_poly.evaluate( &(u_poly.evaluate(&domain_m_sq.element(k)) @@ -362,42 +355,43 @@ pub fn compute_lookup_proof( &Evaluations::from_vec_and_domain(evals, domain_m_sq).interpolate() - &input.phi_poly; /////////////////////////////////////////////////////////////////// - //11. Compute H2 + // 11. Compute H2 /////////////////////////////////////////////////////////////////// // temp_poly(X) = z_I(u(X)) + chi [ C_I(u(X)) - phi(X) ] let temp_poly = &z_I_u_poly + &(&c_I_u_poly * chi); - //H2(X) = temp_poly / z_Vm(X) + // H2(X) = temp_poly / z_Vm(X) let (H2_poly, rem) = temp_poly.divide_by_vanishing_poly(srs.domain_m).unwrap(); assert!( - rem == DensePolynomial::from_coefficients_slice(&[Fr::zero()]), + rem == DensePolynomial::from_coefficients_slice(&[E::Fr::zero()]), "H_2(X) doesn't divide" ); /////////////////////////////////////////////////////////////////// - //12. Compute commitments to H2 + // 12. Compute commitments to H2 /////////////////////////////////////////////////////////////////// - //let now = Instant::now(); - let (H2_com, _) = KzgBls12_381::commit(&srs.poly_ck, &H2_poly, None, None).unwrap(); - //println!("Time to commit to H2 {:?}", now.elapsed()); + // let now = Instant::now(); + let H2_com = KZGCommit::::commit_g1(&srs.poly_ck, &H2_poly); + // println!("Time to commit to H2 {:?}", now.elapsed()); /////////////////////////////////////////////////////////////////// - //13. Hash outputs to get alpha + // 13. Hash outputs to get alpha /////////////////////////////////////////////////////////////////// - let alpha = hash_caulk_multi::(hash_input, Some(&[&H2_com.0.clone()].to_vec()), None, None); + transcript.append_element(b"h2", &H2_com); + let alpha = transcript.get_and_append_challenge(b"alpha"); // last hash so don't need to update hash_input // hash_input = alpha.clone(); /////////////////////////////////////////////////////////////////// - //14. Open u at alpha, get v1 + // 14. Open u at alpha, get v1 /////////////////////////////////////////////////////////////////// - let (evals1, pi1) = kzg_open_g1_native(&srs.poly_ck, &u_poly, None, [&alpha].to_vec()); + let (evals1, pi1) = KZGCommit::::open_g1_batch(&srs.poly_ck, &u_poly, None, &[alpha]); let v1 = evals1[0]; /////////////////////////////////////////////////////////////////// - //15. Compute p1(X) and open at v1 + // 15. Compute p1(X) and open at v1 /////////////////////////////////////////////////////////////////// // v1_id = u(alpha) + id(alpha) for when m is not a power of 2 @@ -406,10 +400,10 @@ pub fn compute_lookup_proof( // p1(X) = z_IX() + chi cI(X) let p1_poly = &z_I + &(&c_I_poly * chi); - let (evals2, pi2) = kzg_open_g1_native(&srs.poly_ck, &p1_poly, None, [&v1_id].to_vec()); + let (evals2, pi2) = KZGCommit::::open_g1_batch(&srs.poly_ck, &p1_poly, None, &[v1_id]); /////////////////////////////////////////////////////////////////// - //16. Compute p2(X) and open p2 at alpha + // 16. Compute p2(X) and open p2 at alpha /////////////////////////////////////////////////////////////////// // p2(X) = zI(u(alpha)) + chi C_I( u(alpha) ) @@ -421,135 +415,132 @@ pub fn compute_lookup_proof( p2_poly = &p2_poly - &(&input.phi_poly * chi); // p2(X) = p2(X) - zVm(alpha) H2(X) - let zVm: UniPoly381 = srs.domain_m.vanishing_polynomial().into(); + let zVm: DensePolynomial = srs.domain_m.vanishing_polynomial().into(); p2_poly = &p2_poly - &(&H2_poly * zVm.evaluate(&alpha)); // Open p2(X) at alpha - let (evals3, pi3) = kzg_open_g1_native(&srs.poly_ck, &p2_poly, None, [&alpha].to_vec()); + let (evals3, pi3) = KZGCommit::::open_g1_batch(&srs.poly_ck, &p2_poly, None, &[alpha]); // check that p2_poly(alpha) = 0 - assert!(evals3[0] == Fr::zero(), "p2(alpha) does not equal 0"); + assert!(evals3[0] == E::Fr::zero(), "p2(alpha) does not equal 0"); /////////////////////////////////////////////////////////////////// - //17. Compose proof + // 17. Compose proof /////////////////////////////////////////////////////////////////// let proof = LookupProof { - C_I_com: C_I_com.0, - H1_com: H1_com, - H2_com: H2_com.0, - z_I_com: z_I_com.0, - u_com: u_com.0, - v1: v1, + C_I_com, + H1_com, + H2_com, + z_I_com, + u_com, + v1, v2: evals2[0], - pi1: pi1, - pi2: pi2, - pi3: pi3, + pi1, + pi2, + pi3, }; - return (proof, unity_proof); + (proof, unity_proof) } #[allow(non_snake_case)] -pub fn verify_lookup_proof( - c_com: G1Affine, - phi_com: G1Affine, - proof: &LookupProof, - unity_proof: &ProofMultiUnity, - srs: &PublicParameters, +pub fn verify_lookup_proof( + c_com: &E::G1Affine, + phi_com: &E::G1Affine, + proof: &LookupProof, + unity_proof: &ProofMultiUnity, + srs: &PublicParameters, ) -> bool { /////////////////////////////////////////////////////////////////// - //1. check unity + // 1. check unity /////////////////////////////////////////////////////////////////// // hash_input initialised to zero - let mut hash_input = Fr::zero(); + let mut transcript = CaulkTranscript::new(); - let unity_check = verify_multiunity(srs, &mut hash_input, proof.u_com, unity_proof); + let unity_check = verify_multiunity(srs, &mut transcript, &proof.u_com, unity_proof); assert!(unity_check, "failure on unity"); /////////////////////////////////////////////////////////////////// - //2. Hash outputs to get chi + // 2. Hash outputs to get chi /////////////////////////////////////////////////////////////////// - let chi = hash_caulk_multi::( - hash_input.clone(), - Some( - &[ - &c_com, - &phi_com, - // include last round of unity proof outputs for good practice - &unity_proof.g1_u_bar_alpha, - &unity_proof.g1_h_2_alpha, - &unity_proof.pi_1, - &unity_proof.pi_2, - &unity_proof.pi_3, - &unity_proof.pi_4, - &unity_proof.pi_5, - // outputs from multi-lookup - &proof.C_I_com, - &proof.z_I_com, - &proof.u_com, - ] - .to_vec(), - ), - Some(&[&proof.H1_com].to_vec()), - Some(&[&unity_proof.v1, &unity_proof.v2, &unity_proof.v3].to_vec()), - ); + transcript.append_element(b"c_com", c_com); + transcript.append_element(b"phi_com", phi_com); + transcript.append_element(b"u_bar_alpha", &unity_proof.g1_u_bar_alpha); + transcript.append_element(b"h2_alpha", &unity_proof.g1_h_2_alpha); + transcript.append_element(b"pi_1", &unity_proof.pi_1); + transcript.append_element(b"pi_2", &unity_proof.pi_2); + transcript.append_element(b"pi_3", &unity_proof.pi_3); + transcript.append_element(b"pi_4", &unity_proof.pi_4); + transcript.append_element(b"pi_5", &unity_proof.pi_5); + transcript.append_element(b"C_I_com", &proof.C_I_com); + transcript.append_element(b"z_I_com", &proof.z_I_com); + transcript.append_element(b"u_com", &proof.u_com); - hash_input = chi.clone(); + transcript.append_element(b"h1_com", &proof.H1_com); + + transcript.append_element(b"v1", &unity_proof.v1); + transcript.append_element(b"v2", &unity_proof.v2); + transcript.append_element(b"v3", &unity_proof.v3); + + let chi = transcript.get_and_append_challenge(b"chi"); /////////////////////////////////////////////////////////////////// - //3. Hash outputs to get alpha + // 3. Hash outputs to get alpha /////////////////////////////////////////////////////////////////// - let alpha = hash_caulk_multi::(hash_input, Some(&[&proof.H2_com].to_vec()), None, None); + transcript.append_element(b"h2", &proof.H2_com); + let alpha = transcript.get_and_append_challenge(b"alpha"); // last hash so don't need to update hash_input // hash_input = alpha.clone(); /////////////////////////////////////////////////////////////////// - //4. Check pi_1 + // 4. Check pi_1 /////////////////////////////////////////////////////////////////// // KZG.Verify(srs_KZG, [u]_1, deg = bot, alpha, v1, pi1) - let check1 = kzg_verify_g1_native( - &srs, - proof.u_com.clone(), + let check1 = KZGCommit::::verify_g1( + &srs.poly_ck.powers_of_g, + &srs.g2_powers, + &proof.u_com, None, - [alpha].to_vec(), - [proof.v1].to_vec(), - proof.pi1, + &[alpha], + &[proof.v1], + &proof.pi1, ); assert!(check1, "failure on pi_1 check"); /////////////////////////////////////////////////////////////////// - //5. Check pi_2 + // 5. Check pi_2 /////////////////////////////////////////////////////////////////// // v1_id = u(alpha)+ id(alpha) for when m is not a power of 2 - let v1_id = proof.v1 + (Fr::one() - srs.id_poly.evaluate(&alpha)); + let v1_id = proof.v1 + (E::Fr::one() - srs.id_poly.evaluate(&alpha)); // [P1]_1 = [z_I]_1 + chi [c_I]_1 let p1_com = (proof.z_I_com.into_projective() + proof.C_I_com.mul(chi)).into_affine(); // KZG.Verify(srs_KZG, [P1]_1, deg = bot, v1_id, v2, pi2) - let check2 = kzg_verify_g1_native( - &srs, - p1_com, + let check2 = KZGCommit::::verify_g1( + &srs.poly_ck.powers_of_g, + &srs.g2_powers, + &p1_com, None, - [v1_id].to_vec(), - [proof.v2].to_vec(), - proof.pi2, + &[v1_id], + &[proof.v2], + &proof.pi2, ); assert!(check2, "failure on pi_2 check"); /////////////////////////////////////////////////////////////////// - //6. Check pi_3 + // 6. Check pi_3 /////////////////////////////////////////////////////////////////// // z_Vm(X) - let zVm: UniPoly381 = srs.domain_m.vanishing_polynomial().into(); //z_V_m(alpah) + let zVm: DensePolynomial = srs.domain_m.vanishing_polynomial().into(); // z_V_m(alpah) // [P2]_1 = [v2]_1 - chi cm - zVm(alpha) [H_2]_1 let p2_com = ( @@ -561,28 +552,29 @@ pub fn verify_lookup_proof( .into_affine(); // KZG.Verify(srs_KZG, [P2]_1, deg = bot, alpha, 0, pi3) - let check3 = kzg_verify_g1_native( - &srs, - p2_com, + let check3 = KZGCommit::::verify_g1( + &srs.poly_ck.powers_of_g, + &srs.g2_powers, + &p2_com, None, - [alpha].to_vec(), - [Fr::zero()].to_vec(), - proof.pi3, + &[alpha], + &[E::Fr::zero()], + &proof.pi3, ); assert!(check3, "failure on check 3"); /////////////////////////////////////////////////////////////////// - //7. Check final pairing + // 7. Check final pairing /////////////////////////////////////////////////////////////////// // pairing1 = e([C]_1 - [C_I]_1, [1]_2) - let pairing1 = Bls12_381::pairing( + let pairing1 = E::pairing( (c_com.into_projective() - proof.C_I_com.into_projective()).into_affine(), srs.g2_powers[0], ); // pairing2 = e([z_I]_1, [H_1]_2) - let pairing2 = Bls12_381::pairing(proof.z_I_com, proof.H1_com); + let pairing2 = E::pairing(proof.z_I_com, proof.H1_com); assert!(pairing1 == pairing2, "failure on pairing check"); @@ -591,38 +583,39 @@ pub fn verify_lookup_proof( #[allow(non_snake_case)] #[allow(dead_code)] -pub fn generate_lookup_input() -> ( - LookupProverInput, - PublicParameters, //SRS +pub fn generate_lookup_input( + rng: &mut R, +) -> ( + LookupProverInput, + PublicParameters, // SRS ) { - let n: usize = 8; //bitlength of poly degree + let n: usize = 8; // bitlength of poly degree let m: usize = 4; - //let m: usize = (1<<(n/2-1)); //should be power of 2 + // let m: usize = (1<<(n/2-1)); //should be power of 2 let N: usize = 1 << n; let max_degree: usize = if N > 2 * m * m { N - 1 } else { 2 * m * m }; let actual_degree = N - 1; let now = Instant::now(); - let pp = setup_multi_lookup(&max_degree, &N, &m, &n); + let pp = PublicParameters::::setup(&max_degree, &N, &m, &n); println!("Time to setup {:?}", now.elapsed()); - let rng = &mut ark_std::test_rng(); - let c_poly = UniPoly381::rand(actual_degree, rng); + let c_poly = DensePolynomial::::rand(actual_degree, rng); let mut positions: Vec = vec![]; for j in 0..m { - //generate positions evenly distributed in the set + // generate positions evenly distributed in the set let i_j: usize = j * (N / m); positions.push(i_j); } - //generating phi - let blinder: Fp256 = random_field::(); + // generating phi + let blinder = E::Fr::rand(rng); let a_m = DensePolynomial::from_coefficients_slice(&[blinder]); let mut phi_poly = a_m.mul_by_vanishing_poly(pp.domain_m); for j in 0..m { phi_poly = &phi_poly + &(&pp.lagrange_polynomials_m[j] - * c_poly.evaluate(&pp.domain_N.element(positions[j]))); //adding c(w^{i_j})*mu_j(X) + * c_poly.evaluate(&pp.domain_N.element(positions[j]))); // adding c(w^{i_j})*mu_j(X) } for j in m..pp.domain_m.size() { @@ -631,69 +624,32 @@ pub fn generate_lookup_input() -> ( } let now = Instant::now(); - let openings = multiple_open_g2(&pp.g2_powers, &c_poly, n); + let openings = KZGCommit::::multiple_open::(&c_poly, &pp.g2_powers, n); println!("Time to generate openings {:?}", now.elapsed()); return ( LookupProverInput { - c_poly: c_poly, - phi_poly: phi_poly, - positions: positions, - openings: openings, + c_poly, + phi_poly, + positions, + openings, }, pp, ); } -#[allow(non_snake_case)] -#[test] -pub fn test_lookup() { - _do_lookup(); -} - -#[allow(non_snake_case)] -#[test] -pub fn test_store() { - //1. Setup - let n: usize = 6; - let N: usize = 1 << n; - let powers_size: usize = N + 2; //SRS SIZE - let temp_m = n; //dummy - let pp = setup_multi_lookup(&powers_size, &N, &temp_m, &n); - let actual_degree = N - 1; - let path = format!("tmp/poly_openings.log"); - - //2. Store - let rng = &mut ark_std::test_rng(); - let c_poly = UniPoly381::rand(actual_degree, rng); - let (c_comx, _) = KzgBls12_381::commit(&pp.poly_ck, &c_poly, None, None).unwrap(); - let openings = multiple_open_g2(&pp.g2_powers, &c_poly, pp.n); - let table = TableInput { - c_poly: c_poly, - c_com: c_comx.0, - openings: openings, - }; - table.store(&path); - - //3. Load - let table_loaded = TableInput::load(&path); - - //4. Test - assert_eq!(table, table_loaded); - std::fs::remove_file(&path).expect("File can not be deleted"); -} - -#[allow(non_snake_case)] -#[test] -pub fn test_multiple_lookups() { - do_multiple_lookups() -} - -pub fn get_poly_and_g2_openings(pp: &PublicParameters, actual_degree: usize) -> TableInput { - use std::fs::File; - - //try opening the file. If it exists load the setup from there, otherwise generate - let path = format!("polys/poly_{}_openings_{}.setup", actual_degree, pp.N); +pub fn get_poly_and_g2_openings( + pp: &PublicParameters, + actual_degree: usize, +) -> TableInput { + // try opening the file. If it exists load the setup from there, otherwise + // generate + let path = format!( + "polys/poly_{}_openings_{}_{}.setup", + actual_degree, + pp.N, + E::Fq::size_in_bits() + ); let res = File::open(path.clone()); match res { Ok(_) => { @@ -701,29 +657,81 @@ pub fn get_poly_and_g2_openings(pp: &PublicParameters, actual_degree: usize) -> let table = TableInput::load(&path); println!("Time to load openings = {:?}", now.elapsed()); return table; - } + }, Err(_) => { let rng = &mut ark_std::test_rng(); - let c_poly = UniPoly381::rand(actual_degree, rng); - let (c_comx, _) = KzgBls12_381::commit(&pp.poly_ck, &c_poly, None, None).unwrap(); + let c_poly = DensePolynomial::::rand(actual_degree, rng); + let c_comx = KZGCommit::::commit_g1(&pp.poly_ck, &c_poly); let now = Instant::now(); - let openings = multiple_open_g2(&pp.g2_powers, &c_poly, pp.n); + let openings = + KZGCommit::::multiple_open::(&c_poly, &pp.g2_powers, pp.n); println!("Time to generate openings = {:?}", now.elapsed()); let table = TableInput { - c_poly: c_poly, - c_com: c_comx.0, - openings: openings, + c_poly, + c_com: c_comx, + openings, }; table.store(&path); return table; - } + }, } } #[cfg(test)] -pub mod tests { +mod tests { + use super::*; + use ark_bls12_377::Bls12_377; + use ark_bls12_381::Bls12_381; + use ark_ff::PrimeField; + + #[test] + fn test_store() { + test_store_helper::(); + test_store_helper::(); + } + #[allow(non_snake_case)] - pub fn do_multiple_lookups() { + pub fn test_store_helper() { + // 1. Setup + let n: usize = 6; + let N: usize = 1 << n; + let powers_size: usize = N + 2; // SRS SIZE + let temp_m = n; // dummy + let pp = PublicParameters::::setup(&powers_size, &N, &temp_m, &n); + let actual_degree = N - 1; + let path = format!("tmp/poly_openings_{}.log", E::Fq::size_in_bits()); + + // 2. Store + let rng = &mut ark_std::test_rng(); + let c_poly = DensePolynomial::::rand(actual_degree, rng); + let c_com = KZGCommit::::commit_g1(&pp.poly_ck, &c_poly); + let openings = KZGCommit::::multiple_open::(&c_poly, &pp.g2_powers, pp.n); + let table = TableInput:: { + c_poly, + c_com, + openings, + }; + table.store(&path); + + // 3. Load + let table_loaded = TableInput::load(&path); + + // 4. Test + assert_eq!(table, table_loaded); + std::fs::remove_file(&path).expect("File can not be deleted"); + } + + #[allow(non_snake_case)] + #[test] + fn test_multiple_lookups() { + do_multiple_lookups::(); + do_multiple_lookups::(); + } + + #[allow(non_snake_case)] + fn do_multiple_lookups() { + let mut rng = ark_std::test_rng(); + const MIN_LOG_N: usize = 7; const MAX_LOG_N: usize = 15; const EPS: usize = 1; @@ -731,20 +739,20 @@ pub mod tests { const MAX_LOG_M: usize = 5; for n in MIN_LOG_N..=MAX_LOG_N { - //1. Setup + // 1. Setup let N: usize = 1 << n; - let powers_size: usize = N + 2; //SRS SIZE + let powers_size: usize = N + 2; // SRS SIZE println!("N={}", N); - let temp_m = n; //dummy - let mut pp = setup_multi_lookup(&powers_size, &N, &temp_m, &n); + let temp_m = n; // dummy + let mut pp = PublicParameters::::setup(&powers_size, &N, &temp_m, &n); let actual_degree = N - EPS; - //println!("time for powers of tau {:?} for N={:?}", now.elapsed(),N); + // println!("time for powers of tau {:?} for N={:?}", now.elapsed(),N); - //2. Poly and openings + // 2. Poly and openings let table = get_poly_and_g2_openings(&pp, actual_degree); for logm in MIN_LOG_M..=std::cmp::min(MAX_LOG_M, n / 2 - 1) { - //3. Setup + // 3. Setup let now = Instant::now(); let mut m = 1 << logm; m = m + 1; @@ -753,16 +761,16 @@ pub mod tests { pp.regenerate_lookup_params(m); println!("Time to generate aux domain {:?}", now.elapsed()); - //4. Positions + // 4. Positions let mut positions: Vec = vec![]; for j in 0..m { - //generate positions evenly distributed in the set + // generate positions evenly distributed in the set let i_j: usize = j * (actual_degree / m); positions.push(i_j); } - //5. generating phi - let blinder: Fp256 = random_field::(); + // 5. generating phi + let blinder = E::Fr::rand(&mut rng); let a_m = DensePolynomial::from_coefficients_slice(&[blinder]); let mut phi_poly = a_m.mul_by_vanishing_poly(pp.domain_m); let c_poly_local = table.c_poly.clone(); @@ -770,41 +778,37 @@ pub mod tests { phi_poly = &phi_poly + &(&pp.lagrange_polynomials_m[j] * c_poly_local.evaluate(&pp.domain_N.element(positions[j]))); - //adding c(w^{i_j})*mu_j(X) + // adding c(w^{i_j})*mu_j(X) } for j in m..pp.domain_m.size() { phi_poly = &phi_poly + &(&pp.lagrange_polynomials_m[j] - * c_poly_local.evaluate(&pp.domain_N.element(0))); //adding c(w^{i_j})*mu_j(X) + * c_poly_local.evaluate(&pp.domain_N.element(0))); + // adding c(w^{i_j})*mu_j(X) } - //6. Running proofs + // 6. Running proofs let now = Instant::now(); - let (c_com, _) = - KzgBls12_381::commit(&pp.poly_ck, &table.c_poly, None, None).unwrap(); - let (phi_com, _) = - KzgBls12_381::commit(&pp.poly_ck, &phi_poly, None, None).unwrap(); + let c_com = KZGCommit::::commit_g1(&pp.poly_ck, &table.c_poly); + let phi_com = KZGCommit::::commit_g1(&pp.poly_ck, &phi_poly); println!("Time to generate inputs = {:?}", now.elapsed()); - let lookup_instance = LookupInstance { - c_com: c_com.0.clone(), - phi_com: phi_com.0.clone(), - }; + let lookup_instance = LookupInstance { c_com, phi_com }; let prover_input = LookupProverInput { c_poly: table.c_poly.clone(), - phi_poly: phi_poly, - positions: positions, + phi_poly, + positions, openings: table.openings.clone(), }; let now = Instant::now(); let (proof, unity_proof) = - compute_lookup_proof(&lookup_instance, &prover_input, &pp); + compute_lookup_proof::(&lookup_instance, &prover_input, &pp, &mut rng); println!("Time to generate proof for = {:?}", now.elapsed()); let now = Instant::now(); - let res = verify_lookup_proof(table.c_com, phi_com.0, &proof, &unity_proof, &pp); + let res = verify_lookup_proof(&table.c_com, &phi_com, &proof, &unity_proof, &pp); println!("Time to verify proof for = {:?}", now.elapsed()); assert!(res); println!("Lookup test succeeded"); @@ -812,34 +816,39 @@ pub mod tests { } } - pub fn _do_lookup() { + #[allow(non_snake_case)] + #[test] + fn test_lookup() { + do_lookup::(); + do_lookup::(); + } + + fn do_lookup() { + let mut rng = ark_std::test_rng(); + let now = Instant::now(); - let (prover_input, srs) = generate_lookup_input(); + let (prover_input, srs) = generate_lookup_input(&mut rng); println!( "Time to generate parameters for n={:?} = {:?}", srs.n, now.elapsed() ); - //kzg_test(&srs); - let (c_com, _) = - KzgBls12_381::commit(&srs.poly_ck, &prover_input.c_poly, None, None).unwrap(); - let (phi_com, _) = - KzgBls12_381::commit(&srs.poly_ck, &prover_input.phi_poly, None, None).unwrap(); + // kzg_test(&srs); + let c_com = KZGCommit::::commit_g1(&srs.poly_ck, &prover_input.c_poly); + let phi_com = KZGCommit::::commit_g1(&srs.poly_ck, &prover_input.phi_poly); - let lookup_instance = LookupInstance { - c_com: c_com.0.clone(), - phi_com: phi_com.0.clone(), - }; + let lookup_instance = LookupInstance { c_com, phi_com }; let now = Instant::now(); - let (proof, unity_proof) = compute_lookup_proof(&lookup_instance, &prover_input, &srs); + let (proof, unity_proof) = + compute_lookup_proof(&lookup_instance, &prover_input, &srs, &mut rng); println!( "Time to generate proof for m={:?} = {:?}", srs.m, now.elapsed() ); let now = Instant::now(); - let res = verify_lookup_proof(c_com.0, phi_com.0, &proof, &unity_proof, &srs); + let res = verify_lookup_proof(&c_com, &phi_com, &proof, &unity_proof, &srs); println!( "Time to verify proof for n={:?} = {:?}", srs.n, diff --git a/src/multi/setup.rs b/src/multi/setup.rs new file mode 100644 index 0000000..48c04df --- /dev/null +++ b/src/multi/setup.rs @@ -0,0 +1,356 @@ +use crate::util::trim; +use ark_ec::{AffineCurve, PairingEngine, ProjectiveCurve}; +use ark_ff::{PrimeField, UniformRand}; +use ark_poly::{ + univariate::DensePolynomial, EvaluationDomain, Evaluations as EvaluationsOnDomain, + GeneralEvaluationDomain, +}; +use ark_poly_commit::kzg10::*; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use std::{convert::TryInto, io::Write}; +// use crate::tools::{KzgBls12_381, UniPoly381}; +use ark_std::{cfg_into_iter, One, Zero}; +use std::{fs::File, io::Read, time::Instant}; + +// structure of public parameters +#[allow(non_snake_case)] +pub struct PublicParameters { + pub poly_ck: Powers<'static, E>, + pub domain_m: GeneralEvaluationDomain, + pub domain_n: GeneralEvaluationDomain, + pub domain_N: GeneralEvaluationDomain, + pub verifier_pp: VerifierPublicParameters, + pub lagrange_polynomials_n: Vec>, + pub lagrange_polynomials_m: Vec>, + pub id_poly: DensePolynomial, + pub N: usize, + pub m: usize, + pub n: usize, + pub g2_powers: Vec, +} + +pub struct LookupParameters { + m: usize, + lagrange_polynomials_m: Vec>, + domain_m: GeneralEvaluationDomain, + id_poly: DensePolynomial, +} + +impl LookupParameters { + fn new(m: usize) -> Self { + let domain_m: GeneralEvaluationDomain = GeneralEvaluationDomain::new(m.clone()).unwrap(); + + // id_poly(X) = 1 for omega_m in range and 0 for omega_m not in range. + let mut id_vec = Vec::new(); + for _ in 0..m.clone() { + id_vec.push(F::one()); + } + for _ in m.clone()..domain_m.size() { + id_vec.push(F::zero()); + } + let id_poly = EvaluationsOnDomain::from_vec_and_domain(id_vec, domain_m).interpolate(); + let mut lagrange_polynomials_m: Vec> = Vec::new(); + + for i in 0..domain_m.size() { + let evals: Vec = cfg_into_iter!(0..domain_m.size()) + .map(|k| if k == i { F::one() } else { F::zero() }) + .collect(); + lagrange_polynomials_m + .push(EvaluationsOnDomain::from_vec_and_domain(evals, domain_m).interpolate()); + } + + Self { + m, + lagrange_polynomials_m, + domain_m, + id_poly, + } + } +} + +// smaller set of public parameters used by verifier +pub struct VerifierPublicParameters { + pub poly_vk: VerifierKey, + pub domain_m_size: usize, +} + +impl PublicParameters { + pub fn regenerate_lookup_params(&mut self, m: usize) { + let lp = LookupParameters::new(m); + self.m = lp.m; + self.lagrange_polynomials_m = lp.lagrange_polynomials_m; + self.domain_m = lp.domain_m; + self.id_poly = lp.id_poly; + } + + // store powers of g in a file + pub fn store(&self, path: &str) { + // 1. Powers of g + let mut g_bytes = vec![]; + let mut f = File::create(path).expect("Unable to create file"); + let deg: u32 = self.poly_ck.powers_of_g.len().try_into().unwrap(); + let deg_bytes = deg.to_be_bytes(); + f.write_all(°_bytes).expect("Unable to write data"); + let deg32: usize = deg.try_into().unwrap(); + for i in 0..deg32 { + self.poly_ck.powers_of_g[i] + .into_projective() + .into_affine() + .serialize_uncompressed(&mut g_bytes) + .ok(); + } + f.write_all(&g_bytes).expect("Unable to write data"); + + // 2. Powers of gammag + let deg_gamma: u32 = self.poly_ck.powers_of_gamma_g.len().try_into().unwrap(); + let mut gg_bytes = vec![]; + let deg_bytes = deg_gamma.to_be_bytes(); + f.write_all(°_bytes).expect("Unable to write data"); + let deg32: usize = deg.try_into().unwrap(); + for i in 0..deg32 { + self.poly_ck.powers_of_gamma_g[i] + .into_projective() + .into_affine() + .serialize_uncompressed(&mut gg_bytes) + .ok(); + } + f.write_all(&gg_bytes).expect("Unable to write data"); + + // 3. Verifier key + let mut h_bytes = vec![]; + self.verifier_pp + .poly_vk + .h + .serialize_uncompressed(&mut h_bytes) + .ok(); + self.verifier_pp + .poly_vk + .beta_h + .serialize_uncompressed(&mut h_bytes) + .ok(); + f.write_all(&h_bytes).expect("Unable to write data"); + + // 4. g2 powers + let mut g2_bytes = vec![]; + let deg2: u32 = self.g2_powers.len().try_into().unwrap(); + let deg2_bytes = deg2.to_be_bytes(); + f.write_all(°2_bytes).expect("Unable to write data"); + let deg2_32: usize = deg2.try_into().unwrap(); + for i in 0..deg2_32 { + self.g2_powers[i] + .into_projective() + .into_affine() + .serialize_uncompressed(&mut g2_bytes) + .ok(); + } + f.write_all(&g2_bytes).expect("Unable to write data"); + } + + // load powers of g from a file + pub fn load(path: &str) -> (Powers<'static, E>, VerifierKey, Vec) { + const G1_UNCOMPR_SIZE: usize = 96; + const G2_UNCOMPR_SIZE: usize = 192; + let mut data = Vec::new(); + let mut f = File::open(path).expect("Unable to open file"); + f.read_to_end(&mut data).expect("Unable to read data"); + + // 1. reading g powers + let mut cur_counter: usize = 0; + let deg_bytes: [u8; 4] = (&data[0..4]).try_into().unwrap(); + let deg: u32 = u32::from_be_bytes(deg_bytes); + let mut powers_of_g = vec![]; + let deg32: usize = deg.try_into().unwrap(); + cur_counter += 4; + for i in 0..deg32 { + let buf_bytes = + &data[cur_counter + i * G1_UNCOMPR_SIZE..cur_counter + (i + 1) * G1_UNCOMPR_SIZE]; + let tmp = E::G1Affine::deserialize_unchecked(buf_bytes).unwrap(); + powers_of_g.push(tmp); + } + cur_counter += deg32 * G1_UNCOMPR_SIZE; + + // 2. reading gamma g powers + let deg_bytes: [u8; 4] = (&data[cur_counter..cur_counter + 4]).try_into().unwrap(); + let deg: u32 = u32::from_be_bytes(deg_bytes); + let mut powers_of_gamma_g = vec![]; + let deg32: usize = deg.try_into().unwrap(); + cur_counter += 4; + for i in 0..deg32 { + let buf_bytes = + &data[cur_counter + i * G1_UNCOMPR_SIZE..cur_counter + (i + 1) * G1_UNCOMPR_SIZE]; + let tmp = E::G1Affine::deserialize_unchecked(buf_bytes).unwrap(); + powers_of_gamma_g.push(tmp); + } + cur_counter += deg32 * G1_UNCOMPR_SIZE; + + // 3. reading verifier key + let buf_bytes = &data[cur_counter..cur_counter + G2_UNCOMPR_SIZE]; + let h = E::G2Affine::deserialize_unchecked(buf_bytes).unwrap(); + cur_counter += G2_UNCOMPR_SIZE; + let buf_bytes = &data[cur_counter..cur_counter + G2_UNCOMPR_SIZE]; + let beta_h = E::G2Affine::deserialize_unchecked(buf_bytes).unwrap(); + cur_counter += G2_UNCOMPR_SIZE; + + // 4. reading G2 powers + let deg2_bytes: [u8; 4] = (&data[cur_counter..cur_counter + 4]).try_into().unwrap(); + let deg2: u32 = u32::from_be_bytes(deg2_bytes); + let mut g2_powers = vec![]; + let deg2_32: usize = deg2.try_into().unwrap(); + cur_counter += 4; + for _ in 0..deg2_32 { + let buf_bytes = &data[cur_counter..cur_counter + G2_UNCOMPR_SIZE]; + let tmp = E::G2Affine::deserialize_unchecked(buf_bytes).unwrap(); + g2_powers.push(tmp); + cur_counter += G2_UNCOMPR_SIZE; + } + + let vk = VerifierKey { + g: powers_of_g[0].clone(), + gamma_g: powers_of_gamma_g[0].clone(), + h, + beta_h, + prepared_h: h.into(), + prepared_beta_h: beta_h.into(), + }; + + let powers = Powers { + powers_of_g: ark_std::borrow::Cow::Owned(powers_of_g), + powers_of_gamma_g: ark_std::borrow::Cow::Owned(powers_of_gamma_g), + }; + + (powers, vk, g2_powers) + } + + // setup algorithm for index_hiding_polycommit + // also includes a bunch of precomputation. + // @max_degree max degree of table polynomial C(X), also the size of the trusted + // setup @N domain size on which proofs are constructed. Should not be + // smaller than max_degree @m lookup size. Can be changed later + // @n suppl domain for the unity proofs. Should be at least 6+log N + #[allow(non_snake_case)] + pub fn setup(max_degree: &usize, N: &usize, m: &usize, n: &usize) -> PublicParameters { + // Setup algorithm. To be replaced by output of a universal setup before being + // production ready. + + // let mut srs = KzgBls12_381::setup(4, true, rng).unwrap(); + let poly_ck: Powers<'static, E>; + let poly_vk: VerifierKey; + let mut g2_powers: Vec = Vec::new(); + + // try opening the file. If it exists load the setup from there, otherwise + // generate + let path = format!("srs/srs_{}_{}.setup", max_degree, E::Fq::size_in_bits()); + let res = File::open(path.clone()); + let store_to_file: bool; + match res { + Ok(_) => { + let now = Instant::now(); + let (_poly_ck, _poly_vk, _g2_powers) = PublicParameters::load(&path); + println!("time to load powers = {:?}", now.elapsed()); + store_to_file = false; + g2_powers = _g2_powers; + poly_ck = _poly_ck; + poly_vk = _poly_vk; + }, + Err(_) => { + let rng = &mut ark_std::test_rng(); + let now = Instant::now(); + let srs = KZG10::>::setup(max_degree.clone(), true, rng) + .unwrap(); + println!("time to setup powers = {:?}", now.elapsed()); + + // trim down to size + let (poly_ck2, poly_vk2) = + trim::>(&srs, max_degree.clone()); + poly_ck = Powers { + powers_of_g: ark_std::borrow::Cow::Owned(poly_ck2.powers_of_g.into()), + powers_of_gamma_g: ark_std::borrow::Cow::Owned( + poly_ck2.powers_of_gamma_g.into(), + ), + }; + poly_vk = poly_vk2; + + // need some powers of g2 + // arkworks setup doesn't give these powers but the setup does use a fixed + // randomness to generate them. so we can generate powers of g2 + // directly. + let rng = &mut ark_std::test_rng(); + let beta = E::Fr::rand(rng); + let mut temp = poly_vk.h.clone(); + + for _ in 0..poly_ck.powers_of_g.len() { + g2_powers.push(temp.clone()); + temp = temp.mul(beta).into_affine(); + } + + store_to_file = true; + }, + } + + // domain where openings {w_i}_{i in I} are embedded + let domain_n: GeneralEvaluationDomain = + GeneralEvaluationDomain::new(n.clone()).unwrap(); + let domain_N: GeneralEvaluationDomain = + GeneralEvaluationDomain::new(N.clone()).unwrap(); + + // precomputation to speed up prover + // lagrange_polynomials[i] = polynomial equal to 0 at w^j for j!= i and 1 at + // w^i + let mut lagrange_polynomials_n: Vec> = Vec::new(); + + for i in 0..domain_n.size() { + let evals: Vec = cfg_into_iter!(0..domain_n.size()) + .map(|k| if k == i { E::Fr::one() } else { E::Fr::zero() }) + .collect(); + lagrange_polynomials_n + .push(EvaluationsOnDomain::from_vec_and_domain(evals, domain_n).interpolate()); + } + + let lp = LookupParameters::new(*m); + + let verifier_pp = VerifierPublicParameters { + poly_vk, + domain_m_size: lp.domain_m.size(), + }; + + let pp = PublicParameters { + poly_ck, + domain_m: lp.domain_m, + domain_n, + lagrange_polynomials_n, + lagrange_polynomials_m: lp.lagrange_polynomials_m, + id_poly: lp.id_poly, + domain_N, + verifier_pp, + N: N.clone(), + n: n.clone(), + m: lp.m.clone(), + g2_powers: g2_powers.clone(), + }; + if store_to_file { + pp.store(&path); + } + pp + } +} + +#[test] +#[allow(non_snake_case)] +pub fn test_load() { + use ark_bls12_381::Bls12_381; + + let n: usize = 4; + let N: usize = 1 << n; + let powers_size: usize = 4 * N; // SRS SIZE + let temp_m = n; // dummy + let pp = PublicParameters::::setup(&powers_size, &N, &temp_m, &n); + let path = "powers.log"; + pp.store(path); + let loaded = PublicParameters::::load(path); + assert_eq!(pp.poly_ck.powers_of_g, loaded.0.powers_of_g); + assert_eq!(pp.poly_ck.powers_of_gamma_g, loaded.0.powers_of_gamma_g); + assert_eq!(pp.verifier_pp.poly_vk.h, loaded.1.h); + assert_eq!(pp.verifier_pp.poly_vk.beta_h, loaded.1.beta_h); + assert_eq!(pp.g2_powers, loaded.2); + std::fs::remove_file(&path).expect("File can not be deleted"); +} diff --git a/caulk_multi_lookup/src/caulk_multi_unity.rs b/src/multi/unity.rs similarity index 67% rename from caulk_multi_lookup/src/caulk_multi_unity.rs rename to src/multi/unity.rs index afdd4e4..91cb27d 100644 --- a/caulk_multi_lookup/src/caulk_multi_unity.rs +++ b/src/multi/unity.rs @@ -1,61 +1,56 @@ -/* -This file includes the Caulk's unity prover and verifier for multi openings. -The protocol is described in Figure 4. -*/ +// This file includes the Caulk's unity prover and verifier for multi openings. +// The protocol is described in Figure 4. -use ark_bls12_381::{Fr, FrParameters, G1Affine}; -use ark_ec::{msm::VariableBaseMSM, AffineCurve, ProjectiveCurve}; -use ark_ff::Fp256; +use super::setup::PublicParameters; +use crate::{util::convert_to_bigints, CaulkTranscript, KZGCommit}; +use ark_ec::{msm::VariableBaseMSM, AffineCurve, PairingEngine, ProjectiveCurve}; use ark_poly::{ univariate::DensePolynomial, EvaluationDomain, Evaluations as EvaluationsOnDomain, Polynomial, UVPolynomial, }; use ark_std::{One, Zero}; -use crate::caulk_multi_setup::PublicParameters; -use crate::tools::{ - bipoly_commit, convert_to_bigints, hash_caulk_multi, kzg_open_g1_native, - kzg_partial_open_g1_native, kzg_partial_verify_g1_native, kzg_verify_g1_native, UniPoly381, -}; - // output structure of prove_unity -pub struct ProofMultiUnity { - pub g1_u_bar: G1Affine, - pub g1_h_1: G1Affine, - pub g1_h_2: G1Affine, - pub g1_u_bar_alpha: G1Affine, - pub g1_h_2_alpha: G1Affine, - pub v1: Fr, - pub v2: Fr, - pub v3: Fr, - pub pi_1: G1Affine, - pub pi_2: G1Affine, - pub pi_3: G1Affine, - pub pi_4: G1Affine, - pub pi_5: G1Affine, +pub struct ProofMultiUnity { + pub g1_u_bar: E::G1Affine, + pub g1_h_1: E::G1Affine, + pub g1_h_2: E::G1Affine, + pub g1_u_bar_alpha: E::G1Affine, + pub g1_h_2_alpha: E::G1Affine, + pub v1: E::Fr, + pub v2: E::Fr, + pub v3: E::Fr, + pub pi_1: E::G1Affine, + pub pi_2: E::G1Affine, + pub pi_3: E::G1Affine, + pub pi_4: E::G1Affine, + pub pi_5: E::G1Affine, } -// Prove knowledge of vec_u_evals such that g1_u = g1^(sum_j u_j mu_j(x)) and u_j^N = 1 +// Prove knowledge of vec_u_evals such that g1_u = g1^(sum_j u_j mu_j(x)) and +// u_j^N = 1 #[allow(non_snake_case)] -pub fn prove_multiunity( - pp: &PublicParameters, - hash_input: &mut Fr, - g1_u: &G1Affine, - mut vec_u_evals: Vec>, - u_poly_quotient: UniPoly381, -) -> ProofMultiUnity { - // The test_rng is deterministic. Should be replaced with actual random generator. +pub fn prove_multiunity( + pp: &PublicParameters, + transcript: &mut CaulkTranscript, + g1_u: &E::G1Affine, + mut vec_u_evals: Vec, + u_poly_quotient: DensePolynomial, +) -> ProofMultiUnity { + // The test_rng is deterministic. Should be replaced with actual random + // generator. let rng_arkworks = &mut ark_std::test_rng(); // let rng_arkworks = &mut ark_std::test_rng(); let n = pp.n; let deg_blinders = 11 / n; - let z_Vm: UniPoly381 = pp.domain_m.vanishing_polynomial().into(); + let z_Vm: DensePolynomial = pp.domain_m.vanishing_polynomial().into(); ////////////////////////////////////////////////////////////////////////////////////////////////////////// - // 1. Compute polynomials u_s(X) = vec_u_polys[s] such that u_s( nu_i ) = w_i^{2^s} + // 1. Compute polynomials u_s(X) = vec_u_polys[s] such that u_s( nu_i ) = + // w_i^{2^s} ////////////////////////////////////////////////////////////////////////////////////////////////////////// - let mut vec_u_polys: Vec = Vec::new(); + let mut vec_u_polys: Vec> = Vec::new(); vec_u_polys.push( EvaluationsOnDomain::from_vec_and_domain(vec_u_evals.clone(), pp.domain_m).interpolate() @@ -70,7 +65,7 @@ pub fn prove_multiunity( vec_u_polys.push( EvaluationsOnDomain::from_vec_and_domain(vec_u_evals.clone(), pp.domain_m) .interpolate() - + (&z_Vm * &UniPoly381::rand(deg_blinders, rng_arkworks)), + + (&z_Vm * &DensePolynomial::::rand(deg_blinders, rng_arkworks)), ); } @@ -78,16 +73,18 @@ pub fn prove_multiunity( // 2. Compute U_bar(X,Y) = sum_{s= 1}^n u_{s-1} rho_s(Y) ////////////////////////////////////////////////////////////////////////////////////////////////////////// - // bivariate polynomials such that bipoly_U_bar[j] = a_j(Y) where U_bar(X,Y) = sum_j X^j a_j(Y) + // bivariate polynomials such that bipoly_U_bar[j] = a_j(Y) where U_bar(X,Y) = + // sum_j X^j a_j(Y) let mut bipoly_U_bar = Vec::new(); - // vec_u_polys[0] has an extended degree because it is blinded so use vec_u_polys[1] for the length + // vec_u_polys[0] has an extended degree because it is blinded so use + // vec_u_polys[1] for the length for j in 0..vec_u_polys[1].len() { /* Denoting u_{s-1}(X) = sum_j u_{s-1, j} X^j then temp is a_j(Y) = sum_{s=1}^n u_{s-1, j} * rho_s(Y) */ - let mut temp = DensePolynomial::from_coefficients_slice(&[Fr::zero()]); + let mut temp = DensePolynomial::from_coefficients_slice(&[E::Fr::zero()]); for s in 1..n { let u_s_j = DensePolynomial::from_coefficients_slice(&[vec_u_polys[s][j]]); @@ -106,7 +103,7 @@ pub fn prove_multiunity( let id_poly = pp.id_poly.clone(); // Hs(X) = (u_{s-1}^2(X) - u_s(X)) / zVm(X). Abort if doesn't divide. - let mut vec_H_s_polys: Vec> = Vec::new(); + let mut vec_H_s_polys: Vec> = Vec::new(); for s in 1..n { let (poly_H_s, remainder) = (&(&vec_u_polys[s - 1] * &vec_u_polys[s - 1]) - &vec_u_polys[s]) @@ -138,7 +135,7 @@ pub fn prove_multiunity( // In case length of H_1(X) and H_2(X) is different pad with zeros. for _ in vec_H_s_polys[0].len()..vec_H_s_polys[1].len() { - let h_0_j = DensePolynomial::from_coefficients_slice(&[Fr::zero()]); + let h_0_j = DensePolynomial::from_coefficients_slice(&[E::Fr::zero()]); bipoly_h_2.push(h_0_j); } @@ -156,31 +153,26 @@ pub fn prove_multiunity( ////////////////////////////////////////////////////////////////////////////////////////////////////////// // 5. Commit to U_bar(X^n, X) and h_2(X^n, X) ////////////////////////////////////////////////////////////////////////////////////////////////////////// - let g1_u_bar = bipoly_commit(pp, &bipoly_U_bar, pp.domain_n.size()); - let g1_h_2 = bipoly_commit(pp, &bipoly_h_2, pp.domain_n.size()); + let g1_u_bar = KZGCommit::::bipoly_commit(pp, &bipoly_U_bar, pp.domain_n.size()); + let g1_h_2 = KZGCommit::::bipoly_commit(pp, &bipoly_h_2, pp.domain_n.size()); //////////////////////////// // 6. alpha = Hash(g1_u, g1_u_bar, g1_h_2) //////////////////////////// - - let alpha = hash_caulk_multi::( - hash_input.clone(), - Some(&[&g1_u, &g1_u_bar, &g1_h_2].to_vec()), - None, - None, - ); - - *hash_input = alpha.clone(); + transcript.append_element(b"u", g1_u); + transcript.append_element(b"u_bar", &g1_u_bar); + transcript.append_element(b"h2", &g1_h_2); + let alpha = transcript.get_and_append_challenge(b"alpha"); ////////////////////////////////////////////////////////////////////////////////////////////////////////// // 7. Compute h_1(Y) ////////////////////////////////////////////////////////////////////////////////////////////////////////// // poly_U_alpha = sum_{s=1}^n u_{s-1}(alpha) rho_s(Y) - let mut poly_U_alpha = DensePolynomial::from_coefficients_slice(&[Fr::zero()]); + let mut poly_U_alpha = DensePolynomial::from_coefficients_slice(&[E::Fr::zero()]); // poly_Usq_alpha = sum_{s=1}^n u_{s-1}^2(alpha) rho_s(Y) - let mut poly_Usq_alpha = DensePolynomial::from_coefficients_slice(&[Fr::zero()]); + let mut poly_Usq_alpha = DensePolynomial::from_coefficients_slice(&[E::Fr::zero()]); for s in 0..n { let u_s_alpha = vec_u_polys[s].evaluate(&alpha); @@ -191,8 +183,8 @@ pub fn prove_multiunity( poly_Usq_alpha = &poly_Usq_alpha + &(&temp * &pp.lagrange_polynomials_n[s]); } - // divide h1(Y) = [ U^2(alpha,Y) - sum_{s=1}^n u_{s-1}^2(alpha) rho_s(Y) ) ] / zVn(Y) - // return an error if division fails + // divide h1(Y) = [ U^2(alpha,Y) - sum_{s=1}^n u_{s-1}^2(alpha) rho_s(Y) ) ] / + // zVn(Y) return an error if division fails let (poly_h_1, remainder) = (&(&poly_U_alpha * &poly_U_alpha) - &poly_Usq_alpha) .divide_by_vanishing_poly(pp.domain_n) .unwrap(); @@ -209,15 +201,15 @@ pub fn prove_multiunity( .into_affine(); //////////////////////////// - //9. beta = Hash( g1_h_1 ) + // 9. beta = Hash( g1_h_1 ) //////////////////////////// - let beta = hash_caulk_multi::(hash_input.clone(), Some(&[&g1_h_1].to_vec()), None, None); - - *hash_input = beta.clone(); + transcript.append_element(b"h1", &g1_h_1); + let beta = transcript.get_and_append_challenge(b"beta"); ////////////////////////////////////////////////////////////////////////////////////////////////////////// - // 10. Compute p(Y) = (U^2(alpha, beta) - h1(Y) zVn(beta) ) - (u_bar(alpha, beta sigma^(-1)) + id(alpha) rho_n(Y)) - zVm(alpha )h2(alpha,Y) + // 10. Compute p(Y) = (U^2(alpha, beta) - h1(Y) zVn(beta) ) - (u_bar(alpha, beta + // sigma^(-1)) + id(alpha) rho_n(Y)) - zVm(alpha )h2(alpha,Y) ////////////////////////////////////////////////////////////////////////////////////////////////////////// // p(Y) = U^2(alpha, beta) @@ -229,7 +221,7 @@ pub fn prove_multiunity( // p(Y) = p(Y) - ( u_bar(alpha, beta sigma) + id(alpha) rho_n(beta)) // u_bar_alpha_shiftbeta = u_bar(alpha, beta sigma) - let mut u_bar_alpha_shiftbeta = Fr::zero(); + let mut u_bar_alpha_shiftbeta = E::Fr::zero(); let beta_shift = beta * &pp.domain_n.element(1); for s in 1..n { let u_s_alpha = vec_u_polys[s].evaluate(&alpha); @@ -246,7 +238,7 @@ pub fn prove_multiunity( //////////////////////////// // p(Y) = p(Y) - h1(Y) zVn(beta) - let z_Vn: UniPoly381 = pp.domain_n.vanishing_polynomial().into(); + let z_Vn: DensePolynomial = pp.domain_n.vanishing_polynomial().into(); let temp = &DensePolynomial::from_coefficients_slice(&[z_Vn.evaluate(&beta)]) * &poly_h_1; poly_p = &poly_p - &temp; @@ -254,7 +246,7 @@ pub fn prove_multiunity( // p(Y) = p(Y) - z_Vm(alpha) h_2(alpha, Y) // poly_h_2_alpha = h_2(alpha, Y) - let mut poly_h_2_alpha = DensePolynomial::from_coefficients_slice(&[Fr::zero()]); + let mut poly_h_2_alpha = DensePolynomial::from_coefficients_slice(&[E::Fr::zero()]); for s in 0..vec_H_s_polys.len() { let h_s_j = DensePolynomial::from_coefficients_slice(&[vec_H_s_polys[s].evaluate(&alpha)]); poly_h_2_alpha = &poly_h_2_alpha + &(&h_s_j * &pp.lagrange_polynomials_n[s]); @@ -265,92 +257,83 @@ pub fn prove_multiunity( poly_p = &poly_p - &temp; // check p(beta) = 0 - assert!(poly_p.evaluate(&beta) == Fr::zero()); + assert!(poly_p.evaluate(&beta) == E::Fr::zero()); ////////////////////////////////////////////////////////////////////////////////////////////////////////// // 11. Open KZG commitments ////////////////////////////////////////////////////////////////////////////////////////////////////////// // KZG.Open( srs, u(X), deg = bot, X = alpha ) - let (evals_1, pi_1) = kzg_open_g1_native(&pp.poly_ck, &vec_u_polys[0], None, [&alpha].to_vec()); + let (evals_1, pi_1) = KZGCommit::open_g1_batch(&pp.poly_ck, &vec_u_polys[0], None, &[alpha]); // KZG.Open( srs, U_bar(X,Y), deg = bot, X = alpha ) let (g1_u_bar_alpha, pi_2, poly_u_bar_alpha) = - kzg_partial_open_g1_native(&pp, &bipoly_U_bar, pp.domain_n.size(), &alpha); + KZGCommit::partial_open_g1(&pp, &bipoly_U_bar, pp.domain_n.size(), &alpha); // KZG.Open( srs, h_2(X,Y), deg = bot, X = alpha ) let (g1_h_2_alpha, pi_3, _) = - kzg_partial_open_g1_native(&pp, &bipoly_h_2, pp.domain_n.size(), &alpha); + KZGCommit::partial_open_g1(&pp, &bipoly_h_2, pp.domain_n.size(), &alpha); - // KZG.Open( srs, U_bar(alpha,Y), deg = bot, Y = [1, beta, beta * sigma] ) should evaluate to (0, v2, v3) - let (evals_2, pi_4) = kzg_open_g1_native( + // KZG.Open( srs, U_bar(alpha,Y), deg = bot, Y = [1, beta, beta * sigma] ) + // should evaluate to (0, v2, v3) + let (evals_2, pi_4) = KZGCommit::open_g1_batch( &pp.poly_ck, &poly_u_bar_alpha, Some(&(pp.domain_n.size() - 1)), - [&Fr::one(), &beta, &(beta * &pp.domain_n.element(1))].to_vec(), + &[E::Fr::one(), beta, (beta * &pp.domain_n.element(1))], ); - assert!(evals_2[0] == Fr::zero()); + assert!(evals_2[0] == E::Fr::zero()); // KZG.Open(srs, p(Y), deg = n-1, Y = beta) - let (evals_3, pi_5) = kzg_open_g1_native( + let (evals_3, pi_5) = KZGCommit::open_g1_batch( &pp.poly_ck, &poly_p, Some(&(pp.domain_n.size() - 1)), - [&beta].to_vec(), + &[beta], ); - assert!(evals_3[0] == Fr::zero()); + assert!(evals_3[0] == E::Fr::zero()); let proof = ProofMultiUnity { - g1_u_bar: g1_u_bar, - g1_h_1: g1_h_1, - g1_h_2: g1_h_2, - g1_u_bar_alpha: g1_u_bar_alpha, - g1_h_2_alpha: g1_h_2_alpha, + g1_u_bar, + g1_h_1, + g1_h_2, + g1_u_bar_alpha, + g1_h_2_alpha, v1: evals_1[0], v2: evals_2[1], v3: evals_2[2], - pi_1: pi_1, - pi_2: pi_2, - pi_3: pi_3, - pi_4: pi_4, - pi_5: pi_5, + pi_1, + pi_2, + pi_3, + pi_4, + pi_5, }; proof } -// Verify that the prover knows vec_u_evals such that g1_u = g1^(sum_j u_j mu_j(x)) and u_j^N = 1 +// Verify that the prover knows vec_u_evals such that g1_u = g1^(sum_j u_j +// mu_j(x)) and u_j^N = 1 #[allow(non_snake_case)] -pub fn verify_multiunity( - pp: &PublicParameters, - hash_input: &mut Fr, - g1_u: G1Affine, - pi_unity: &ProofMultiUnity, +pub fn verify_multiunity( + pp: &PublicParameters, + transcript: &mut CaulkTranscript, + g1_u: &E::G1Affine, + pi_unity: &ProofMultiUnity, ) -> bool { //////////////////////////// // alpha = Hash(g1_u, g1_u_bar, g1_h_2) //////////////////////////// - - let alpha = hash_caulk_multi::( - hash_input.clone(), - Some(&[&g1_u, &pi_unity.g1_u_bar, &pi_unity.g1_h_2].to_vec()), - None, - None, - ); - - *hash_input = alpha.clone(); + transcript.append_element(b"u", g1_u); + transcript.append_element(b"u_bar", &pi_unity.g1_u_bar); + transcript.append_element(b"h2", &pi_unity.g1_h_2); + let alpha = transcript.get_and_append_challenge(b"alpha"); //////////////////////////// // beta = Hash( g1_h_1 ) //////////////////////////// - let beta = hash_caulk_multi::( - hash_input.clone(), - Some(&[&pi_unity.g1_h_1].to_vec()), - None, - None, - ); - - *hash_input = beta.clone(); + transcript.append_element(b"h1", &pi_unity.g1_h_1); + let beta = transcript.get_and_append_challenge(b"beta"); ///////////////////////////// // Compute [P]_1 @@ -378,45 +361,48 @@ pub fn verify_multiunity( // Check the KZG openings //////////////////////////// - let check1 = kzg_verify_g1_native( - &pp, - g1_u.clone(), + let check1 = KZGCommit::::verify_g1( + &pp.poly_ck.powers_of_g, + &pp.g2_powers, + &g1_u, None, - [alpha].to_vec(), - [pi_unity.v1].to_vec(), - pi_unity.pi_1, + &[alpha], + &[pi_unity.v1], + &pi_unity.pi_1, ); - let check2 = kzg_partial_verify_g1_native( + let check2 = KZGCommit::partial_verify_g1( &pp, - pi_unity.g1_u_bar, + &pi_unity.g1_u_bar, pp.domain_n.size(), - alpha, - pi_unity.g1_u_bar_alpha, - pi_unity.pi_2, + &alpha, + &pi_unity.g1_u_bar_alpha, + &pi_unity.pi_2, ); - let check3 = kzg_partial_verify_g1_native( + let check3 = KZGCommit::partial_verify_g1( &pp, - pi_unity.g1_h_2, + &pi_unity.g1_h_2, pp.domain_n.size(), - alpha, - pi_unity.g1_h_2_alpha, - pi_unity.pi_3, + &alpha, + &pi_unity.g1_h_2_alpha, + &pi_unity.pi_3, ); - let check4 = kzg_verify_g1_native( - &pp, - pi_unity.g1_u_bar_alpha, + let check4 = KZGCommit::::verify_g1( + &pp.poly_ck.powers_of_g, + &pp.g2_powers, + &pi_unity.g1_u_bar_alpha, Some(&(pp.domain_n.size() - 1)), - [Fr::one(), beta, beta * &pp.domain_n.element(1)].to_vec(), - [Fr::zero(), pi_unity.v2, pi_unity.v3].to_vec(), - pi_unity.pi_4, + &[E::Fr::one(), beta, beta * &pp.domain_n.element(1)], + &[E::Fr::zero(), pi_unity.v2, pi_unity.v3], + &pi_unity.pi_4, ); - let check5 = kzg_verify_g1_native( - &pp, - g1_P.into_affine(), + let check5 = KZGCommit::::verify_g1( + &pp.poly_ck.powers_of_g, + &pp.g2_powers, + &g1_P.into_affine(), Some(&(pp.domain_n.size() - 1)), - [beta].to_vec(), - [Fr::zero()].to_vec(), - pi_unity.pi_5, + &[beta], + &[E::Fr::zero()], + &pi_unity.pi_5, ); return check1 && check2 && check3 && check4 && check5; @@ -424,24 +410,30 @@ pub fn verify_multiunity( #[cfg(test)] pub mod tests { - use crate::caulk_multi_setup::setup_multi_lookup; - use crate::caulk_multi_unity::{prove_multiunity, verify_multiunity}; - use crate::tools::{convert_to_bigints, UniPoly381}; + use super::{prove_multiunity, verify_multiunity}; + use crate::{util::convert_to_bigints, CaulkTranscript}; + use ark_bls12_377::Bls12_377; + use ark_bls12_381::Bls12_381; + use ark_ec::{msm::VariableBaseMSM, PairingEngine, ProjectiveCurve}; + use ark_poly::{ + univariate::DensePolynomial, EvaluationDomain, Evaluations as EvaluationsOnDomain, + UVPolynomial, + }; + use ark_std::test_rng; use rand::Rng; use std::time::Instant; - use ark_bls12_381::FrParameters; - use ark_ec::{msm::VariableBaseMSM, ProjectiveCurve}; - use ark_ff::Fp256; - use ark_poly::{EvaluationDomain, Evaluations as EvaluationsOnDomain, UVPolynomial}; - - //#[test] - #[allow(non_snake_case)] #[test] - pub fn test_unity() { - let mut rng = rand::thread_rng(); + fn test_unity() { + test_unity_helper::(); + test_unity_helper::(); + } - let n: usize = 8; //bitlength of poly degree + #[allow(non_snake_case)] + fn test_unity_helper() { + let mut rng = test_rng(); + + let n: usize = 8; // bitlength of poly degree let max_degree: usize = (1 << n) + 2; let N: usize = (1 << n) - 1; @@ -450,7 +442,7 @@ pub mod tests { // run the setup let now = Instant::now(); - let pp = setup_multi_lookup(&max_degree, &N, &m, &n); + let pp = crate::multi::PublicParameters::::setup(&max_degree, &N, &m, &n); println!( "time to setup single openings of table size {:?} = {:?}", N + 1, @@ -462,20 +454,19 @@ pub mod tests { //////////////////////////////////////////////////////////////////////////////////// // choose [u1, ..., um] such that uj**N = 1 - let mut vec_u_evals: Vec> = Vec::new(); + let mut vec_u_evals: Vec = Vec::new(); for _ in 0..m { - let j = rng.gen_range(0, pp.domain_N.size()); + let j = rng.gen_range(0..pp.domain_N.size()); vec_u_evals.push(pp.domain_N.element(j)); } // choose random quotient polynomial of degree 1. - let rng_arkworks = &mut ark_std::test_rng(); - let u_poly_quotient = UniPoly381::rand(5, rng_arkworks); + let u_poly_quotient = DensePolynomial::::rand(5, &mut rng); // X^m - 1 - let z_Vm: UniPoly381 = pp.domain_m.vanishing_polynomial().into(); + let z_Vm: DensePolynomial = pp.domain_m.vanishing_polynomial().into(); - //commit to polynomial u(X) = sum_j uj muj(X) + u_quotient(X) z_Vm(X) + // commit to polynomial u(X) = sum_j uj muj(X) + u_quotient(X) z_Vm(X) let u_poly = &EvaluationsOnDomain::from_vec_and_domain(vec_u_evals.clone(), pp.domain_m) .interpolate() + &(&u_poly_quotient * &z_Vm); @@ -490,14 +481,22 @@ pub mod tests { //////////////////////////////////////////////////////////////////////////////////// // run the prover //////////////////////////////////////////////////////////////////////////////////// - let pi_unity = prove_multiunity(&pp, &g1_u, vec_u_evals.clone(), u_poly_quotient); + let mut prover_transcript = CaulkTranscript::new(); + let pi_unity = prove_multiunity::( + &pp, + &mut prover_transcript, + &g1_u, + vec_u_evals.clone(), + u_poly_quotient, + ); //////////////////////////////////////////////////////////////////////////////////// // run the verifier //////////////////////////////////////////////////////////////////////////////////// + let mut verifier_transcript = CaulkTranscript::new(); println!( "unity proof verifies {:?}", - verify_multiunity(&pp, g1_u, pi_unity) + verify_multiunity::(&pp, &mut verifier_transcript, &g1_u, &pi_unity) ); } } diff --git a/caulk_single_opening/src/pedersen.rs b/src/pedersen.rs similarity index 90% rename from caulk_single_opening/src/pedersen.rs rename to src/pedersen.rs index 1ef45d9..d65220a 100644 --- a/caulk_single_opening/src/pedersen.rs +++ b/src/pedersen.rs @@ -1,7 +1,6 @@ -/* -This file includes a prover and verifier for demonstrating knowledge of an opening of a Pedersen commitment. -The protocol is informally described in Appendix A.2, Proof of Opening of a Pedersen Commitment -*/ +// This file includes a prover and verifier for demonstrating knowledge of an +// opening of a Pedersen commitment. The protocol is informally described in +// Appendix A.2, Proof of Opening of a Pedersen Commitment use crate::CaulkTranscript; use ark_ec::{AffineCurve, ProjectiveCurve}; diff --git a/caulk_single_opening/src/caulk_single.rs b/src/single/mod.rs similarity index 87% rename from caulk_single_opening/src/caulk_single.rs rename to src/single/mod.rs index 57b6d6a..1f066ce 100644 --- a/caulk_single_opening/src/caulk_single.rs +++ b/src/single/mod.rs @@ -1,22 +1,23 @@ -/* -This file includes the Caulk prover and verifier for single openings. -The protocol is described in Figure 1. -*/ +// This file includes the Caulk prover and verifier for single openings. +// The protocol is described in Figure 1. -use crate::caulk_single_setup::{PublicParameters, VerifierPublicParameters}; -use crate::caulk_single_unity::{ - caulk_single_unity_prove, caulk_single_unity_verify, CaulkProofUnity, PublicParametersUnity, - VerifierPublicParametersUnity, +pub mod setup; +pub mod unity; + +use crate::{ + pedersen::{PedersenCommit, PedersenProof}, + CaulkTranscript, }; -use crate::pedersen::{PedersenCommit, PedersenProof}; -use crate::CaulkTranscript; use ark_ec::{AffineCurve, PairingEngine, ProjectiveCurve}; use ark_ff::{Field, PrimeField}; use ark_poly::{EvaluationDomain, GeneralEvaluationDomain}; -use ark_std::rand::RngCore; -use ark_std::UniformRand; -use ark_std::{One, Zero}; +use ark_std::{rand::RngCore, One, UniformRand, Zero}; +use setup::{PublicParameters, VerifierPublicParameters}; use std::ops::Neg; +use unity::{ + caulk_single_unity_prove, caulk_single_unity_verify, CaulkProofUnity, PublicParametersUnity, + VerifierPublicParametersUnity, +}; // Structure of opening proofs output by prove. #[allow(non_snake_case)] @@ -28,11 +29,12 @@ pub struct CaulkProof { pub pi_unity: CaulkProofUnity, } -//Proves knowledge of (i, Q, z, r) such that +// Proves knowledge of (i, Q, z, r) such that // 1) Q is a KZG opening proof that g1_C opens to z at i // 2) cm = g^z h^r -//Takes as input opening proof Q. Does not need knowledge of contents of C = g1_C. +// Takes as input opening proof Q. Does not need knowledge of contents of C = +// g1_C. #[allow(non_snake_case)] #[allow(clippy::too_many_arguments)] pub fn caulk_single_prove( @@ -74,7 +76,8 @@ pub fn caulk_single_prove( // Pedersen prove /////////////////////////////// - // hash the instance and the proof elements to determine hash inputs for Pedersen prover + // hash the instance and the proof elements to determine hash inputs for + // Pedersen prover transcript.append_element(b"0", &E::Fr::zero()); transcript.append_element(b"C", g1_C); @@ -89,7 +92,8 @@ pub fn caulk_single_prove( // Unity prove /////////////////////////////// - // hash the last round of the pedersen proof to determine hash input to the unity prover + // hash the last round of the pedersen proof to determine hash input to the + // unity prover transcript.append_element(b"t1", &pi_ped.t1); transcript.append_element(b"t2", &pi_ped.t2); @@ -115,7 +119,7 @@ pub fn caulk_single_prove( } } -//Verifies that the prover knows of (i, Q, z, r) such that +// Verifies that the prover knows of (i, Q, z, r) such that // 1) Q is a KZG opening proof that g1_C opens to z at i // 2) cm = g^z h^r #[allow(non_snake_case)] @@ -143,7 +147,8 @@ pub fn caulk_single_verify( // Pedersen check /////////////////////////////// - // hash the instance and the proof elements to determine hash inputs for Pedersen prover + // hash the instance and the proof elements to determine hash inputs for + // Pedersen prover transcript.append_element(b"0", &E::Fr::zero()); transcript.append_element(b"C", g1_C); transcript.append_element(b"T", &proof.g1_T); @@ -157,7 +162,8 @@ pub fn caulk_single_verify( // Unity check /////////////////////////////// - // hash the last round of the pedersen proof to determine hash input to the unity prover + // hash the last round of the pedersen proof to determine hash input to the + // unity prover transcript.append_element(b"t1", &proof.pi_ped.t1); transcript.append_element(b"t2", &proof.pi_ped.t2); diff --git a/caulk_single_opening/src/caulk_single_setup.rs b/src/single/setup.rs similarity index 79% rename from caulk_single_opening/src/caulk_single_setup.rs rename to src/single/setup.rs index 336f606..a61d668 100644 --- a/caulk_single_opening/src/caulk_single_setup.rs +++ b/src/single/setup.rs @@ -1,9 +1,7 @@ -/* -This file includes the setup algorithm for Caulk with single openings. -A full description of the setup is not formally given in the paper. -*/ +// This file includes the setup algorithm for Caulk with single openings. +// A full description of the setup is not formally given in the paper. -use crate::PedersenParam; +use crate::{util::trim, PedersenParam}; use ark_ec::{AffineCurve, PairingEngine, ProjectiveCurve}; use ark_ff::{Field, UniformRand}; use ark_poly::{ @@ -11,8 +9,7 @@ use ark_poly::{ GeneralEvaluationDomain, UVPolynomial, }; use ark_poly_commit::kzg10::*; -use ark_std::{cfg_into_iter, rand::RngCore, One, Zero}; -use ark_std::{end_timer, start_timer}; +use ark_std::{cfg_into_iter, end_timer, rand::RngCore, start_timer, One, Zero}; #[cfg(feature = "parallel")] use rayon::iter::{IntoParallelIterator, ParallelIterator}; use std::cmp::max; @@ -41,36 +38,6 @@ pub struct VerifierPublicParameters { pub powers_of_g2: Vec, } -// Reduces full srs down to smaller srs for smaller polynomials -// Copied from arkworks library (where same function is private) -fn trim>( - srs: &UniversalParams, - mut supported_degree: usize, -) -> (Powers<'static, E>, VerifierKey) { - if supported_degree == 1 { - supported_degree += 1; - } - - let powers_of_g = srs.powers_of_g[..=supported_degree].to_vec(); - let powers_of_gamma_g = (0..=supported_degree) - .map(|i| srs.powers_of_gamma_g[&i]) - .collect(); - - let powers = Powers { - powers_of_g: ark_std::borrow::Cow::Owned(powers_of_g), - powers_of_gamma_g: ark_std::borrow::Cow::Owned(powers_of_gamma_g), - }; - let vk = VerifierKey { - g: srs.powers_of_g[0], - gamma_g: srs.powers_of_gamma_g[&0], - h: srs.h, - beta_h: srs.beta_h, - prepared_h: srs.prepared_h.clone(), - prepared_beta_h: srs.prepared_beta_h.clone(), - }; - (powers, vk) -} - // setup algorithm for Caulk with single openings // also includes a bunch of precomputation. #[allow(non_snake_case)] @@ -96,7 +63,8 @@ pub fn caulk_single_setup( // We take the larger of the two. let poly_ck_size = max(actual_degree, 2 * domain_Vn.size() + 3); - // Setup algorithm. To be replaced by output of a universal setup before being production ready. + // Setup algorithm. To be replaced by output of a universal setup before being + // production ready. let powers_time = start_timer!(|| "setup powers"); let srs = KZG10::>::setup(max(max_degree, poly_ck_size), true, rng) .unwrap(); @@ -115,7 +83,8 @@ pub fn caulk_single_setup( let ped_h: E::G1Affine = E::G1Projective::rand(rng).into_affine(); // precomputation to speed up prover - // lagrange_polynomials_Vn[i] = polynomial equal to 0 at w^j for j!= i and 1 at w^i + // lagrange_polynomials_Vn[i] = polynomial equal to 0 at w^j for j!= i and 1 at + // w^i let mut lagrange_polynomials_Vn: Vec> = Vec::new(); // precomputation to speed up verifier. @@ -149,9 +118,9 @@ pub fn caulk_single_setup( } lagrange_scalars_Vn.push(temp.inverse().unwrap()); - // poly_prod = (X - 1) (X - w) (X - w^2) (X - w^3) (X - w^4) (X - w^(5 + logN)) (X - w^(6 + logN)) - // for efficiency not including (X - w^i) for i > 6 + logN - // prover sets these evaluations to 0 anyway. + // poly_prod = (X - 1) (X - w) (X - w^2) (X - w^3) (X - w^4) (X - w^(5 + logN)) + // (X - w^(6 + logN)) for efficiency not including (X - w^i) for i > 6 + + // logN prover sets these evaluations to 0 anyway. let mut poly_prod = DensePolynomial::from_coefficients_slice(&[E::Fr::one()]); for i in 0..domain_Vn.size() { if i < 5 { @@ -181,8 +150,8 @@ pub fn caulk_single_setup( let ped_g = poly_ck.powers_of_g[0]; // need some powers of g2 - // arkworks setup doesn't give these powers but the setup does use a fixed randomness to generate them. - // so we can generate powers of g2 directly. + // arkworks setup doesn't give these powers but the setup does use a fixed + // randomness to generate them. so we can generate powers of g2 directly. let rng = &mut ark_std::test_rng(); let beta = E::Fr::rand(rng); let mut temp = poly_vk.h; diff --git a/caulk_single_opening/src/caulk_single_unity.rs b/src/single/unity.rs similarity index 92% rename from caulk_single_opening/src/caulk_single_unity.rs rename to src/single/unity.rs index 114ce87..d316daa 100644 --- a/caulk_single_opening/src/caulk_single_unity.rs +++ b/src/single/unity.rs @@ -1,11 +1,8 @@ -/* -This file includes the Caulk's unity prover and verifier for single openings. -The protocol is described in Figure 2. -*/ +// This file includes the Caulk's unity prover and verifier for single openings. +// The protocol is described in Figure 2. -use crate::caulk_single_setup::{PublicParameters, VerifierPublicParameters}; -use crate::kzg::KZGCommit; -use crate::CaulkTranscript; +use super::setup::{PublicParameters, VerifierPublicParameters}; +use crate::{kzg::KZGCommit, CaulkTranscript}; use ark_ec::{AffineCurve, PairingEngine, ProjectiveCurve}; use ark_ff::Field; use ark_poly::{ @@ -13,8 +10,7 @@ use ark_poly::{ GeneralEvaluationDomain, Polynomial, UVPolynomial, }; use ark_poly_commit::kzg10::*; -use ark_std::{cfg_into_iter, One, Zero}; -use ark_std::{rand::RngCore, UniformRand}; +use ark_std::{cfg_into_iter, rand::RngCore, One, UniformRand, Zero}; #[cfg(feature = "parallel")] use rayon::iter::{IntoParallelIterator, ParallelIterator}; @@ -167,7 +163,8 @@ pub fn caulk_single_unity_prove( let mut p_poly = &(&f_poly - &a_poly) * &(&pp.lagrange_polynomials_Vn[0] + &pp.lagrange_polynomials_Vn[1]); - // p(X) = p(X) + ( (1 - sigma) f(X) - f(sigma^(-2)X) + f(sigma^(-1) X) ) rho_3(X) + // p(X) = p(X) + ( (1 - sigma) f(X) - f(sigma^(-2)X) + f(sigma^(-1) X) ) + // rho_3(X) p_poly = &p_poly + &(&(&(&(&DensePolynomial::from_coefficients_slice(&[(E::Fr::one() - sigma)]) * &f_poly) @@ -186,7 +183,8 @@ pub fn caulk_single_unity_prove( p_poly = &p_poly + &(&(&(&f_poly * &f_poly_shift_1) - &f_poly_shift_2) * &pp.lagrange_polynomials_Vn[4]); - // p(X) = p(X) + ( f(X) - f(sigma^(-1) X) * f(sigma^(-1)X) ) prod_(i not in [5, .. , logN + 4]) (X - sigma^i) + // p(X) = p(X) + ( f(X) - f(sigma^(-1) X) * f(sigma^(-1)X) ) prod_(i not in + // [5, .. , logN + 4]) (X - sigma^i) p_poly = &p_poly + &(&(&f_poly - &(&f_poly_shift_1 * &f_poly_shift_1)) * &pp.poly_prod); // p(X) = p(X) + ( f(sigma^(-1) X) - 1 ) rho_(logN + 6)(X) @@ -201,8 +199,8 @@ pub fn caulk_single_unity_prove( //////////////////////////// // Commit to f(X) and h(X) //////////////////////////// - let g1_F = KZGCommit::::commit(&pp.poly_ck, &f_poly); - let h_hat_com = KZGCommit::::commit(&pp.poly_ck, &h_hat_poly); + let g1_F = KZGCommit::::commit_g1(&pp.poly_ck, &f_poly); + let h_hat_com = KZGCommit::::commit_g1(&pp.poly_ck, &h_hat_poly); // g1_H is a commitment to h_hat_poly + X^(d-1) z(X) let g1_H = (h_hat_com.into_projective() + pp.gxd.mul(-*a) + pp.gxpen.mul(*b)).into_affine(); @@ -267,14 +265,14 @@ pub fn caulk_single_unity_prove( // p_alpha(X) = p_alpha(X) + ( v1 f(X) - v2 ) rho5(alpha) p_alpha_poly = &p_alpha_poly + &(&(&(&f_poly * &pv1) - &pv2) * &prho5); - // p_alpha(X) = p_alpha(X) + ( f(X) - v1^2 ) prod_(i not in [5, .. , logN + 4]) (alpha - sigma^i) + // p_alpha(X) = p_alpha(X) + ( f(X) - v1^2 ) prod_(i not in [5, .. , logN + + // 4]) (alpha - sigma^i) p_alpha_poly = &p_alpha_poly + &(&(&f_poly - &(&pv1 * &pv1)) * &ppolyprod); - /* - Differing slightly from paper - Paper uses p_alpha(X) = p_alpha(X) + ( v1 - 1 ) rho_(n)(alpha) assuming that logN = n - 6 - We use p_alpha(X) = p_alpha(X) + ( v1 - 1 ) rho_(logN + 6)(alpha) to allow for any value of logN - */ + // Differing slightly from paper + // Paper uses p_alpha(X) = p_alpha(X) + ( v1 - 1 ) rho_(n)(alpha) assuming that + // logN = n - 6 We use p_alpha(X) = p_alpha(X) + ( v1 - 1 ) rho_(logN + + // 6)(alpha) to allow for any value of logN p_alpha_poly = &p_alpha_poly + &(&(&pv1 - &(DensePolynomial::from_coefficients_slice(&[E::Fr::one()]))) * &prhologN6); @@ -363,9 +361,11 @@ pub fn caulk_single_unity_verify( // pprod = prod_(i not in [5,..,logN+4]) (alpha - w^i) let pprod = vk.poly_prod.evaluate(&alpha); - // P = H^(-z(alpha)) * F^(rho0(alpha) + L_1(alpha) + (1 - w)L_2(alpha) + L_3(alpha) + v1 L_4(alpha) - // + prod_(i not in [5,..,logN+4]) (alpha - w^i)) - // * g^( (v1 -v2)L_2(alpha) + (v2 - w v1)L_3(alpha) - v2 L_4(alpha) + (v1 - 1)L_(logN+5)(alpha) + // P = H^(-z(alpha)) * F^(rho0(alpha) + L_1(alpha) + (1 - w)L_2(alpha) + + // L_3(alpha) + v1 L_4(alpha) + prod_(i not in + // [5,..,logN+4]) (alpha - w^i)) + // * g^( (v1 -v2)L_2(alpha) + (v2 - w v1)L_3(alpha) - v2 + // L_4(alpha) + (v1 - 1)L_(logN+5)(alpha) // - v1^2 * prod_(i not in [5,..,logN+4]) (alpha - w^i) ) let g1_p = proof.g1_H.mul(-zalpha) + proof @@ -397,9 +397,9 @@ pub fn caulk_single_unity_verify( let g1_q = proof.pi2; - // check that e(P Q3^(-alpha), g2)e( g^(-(rho0 + rho1) - zH(alpha) x^(d-1)), A ) e( Q3, g2^x ) = 1 - // Had to move A from affine to projective and back to affine to get it to compile. - // No idea what difference this makes. + // check that e(P Q3^(-alpha), g2)e( g^(-(rho0 + rho1) - zH(alpha) x^(d-1)), A ) + // e( Q3, g2^x ) = 1 Had to move A from affine to projective and back to + // affine to get it to compile. No idea what difference this makes. let eq1 = vec![ ( (g1_p + g1_q.mul(alpha)).into_affine().into(), diff --git a/caulk_single_opening/src/transcript.rs b/src/transcript.rs similarity index 100% rename from caulk_single_opening/src/transcript.rs rename to src/transcript.rs diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..d0bb1c7 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,44 @@ +use ark_ec::PairingEngine; +use ark_ff::PrimeField; +use ark_poly::UVPolynomial; +use ark_poly_commit::kzg10::*; + +// Reduces full srs down to smaller srs for smaller polynomials +// Copied from arkworks library (where same function is private) +pub(crate) fn trim>( + srs: &UniversalParams, + mut supported_degree: usize, +) -> (Powers<'static, E>, VerifierKey) { + if supported_degree == 1 { + supported_degree += 1; + } + + let powers_of_g = srs.powers_of_g[..=supported_degree].to_vec(); + let powers_of_gamma_g = (0..=supported_degree) + .map(|i| srs.powers_of_gamma_g[&i]) + .collect(); + + let powers = Powers { + powers_of_g: ark_std::borrow::Cow::Owned(powers_of_g), + powers_of_gamma_g: ark_std::borrow::Cow::Owned(powers_of_gamma_g), + }; + let vk = VerifierKey { + g: srs.powers_of_g[0], + gamma_g: srs.powers_of_gamma_g[&0], + h: srs.h, + beta_h: srs.beta_h, + prepared_h: srs.prepared_h.clone(), + prepared_beta_h: srs.prepared_beta_h.clone(), + }; + (powers, vk) +} + +//////////////////////////////////////////////// +// + +// copied from arkworks +pub(crate) fn convert_to_bigints(p: &[F]) -> Vec { + ark_std::cfg_iter!(p) + .map(|s| s.into_repr()) + .collect::>() +}