From f887d895050494e1e8830586e462cf9fc0b2f3d0 Mon Sep 17 00:00:00 2001 From: Gustavo Date: Fri, 7 Feb 2025 15:15:50 +0100 Subject: [PATCH] feat: add dory commitment scheme Signed-off-by: Gustavo --- Cargo.lock | 116 ++---- jolt-core/Cargo.toml | 2 +- jolt-core/src/poly/commitment/dory.rs | 142 ++++++++ jolt-core/src/poly/commitment/dory/error.rs | 30 ++ jolt-core/src/poly/commitment/dory/params.rs | 163 +++++++++ jolt-core/src/poly/commitment/dory/reduce.rs | 332 ++++++++++++++++++ jolt-core/src/poly/commitment/dory/scalar.rs | 101 ++++++ jolt-core/src/poly/commitment/dory/tests.rs | 52 +++ .../poly/commitment/dory/vec_operations.rs | 255 ++++++++++++++ jolt-core/src/poly/commitment/mod.rs | 1 + jolt-core/src/utils/errors.rs | 2 + 11 files changed, 1108 insertions(+), 88 deletions(-) create mode 100644 jolt-core/src/poly/commitment/dory.rs create mode 100644 jolt-core/src/poly/commitment/dory/error.rs create mode 100644 jolt-core/src/poly/commitment/dory/params.rs create mode 100644 jolt-core/src/poly/commitment/dory/reduce.rs create mode 100644 jolt-core/src/poly/commitment/dory/scalar.rs create mode 100644 jolt-core/src/poly/commitment/dory/tests.rs create mode 100644 jolt-core/src/poly/commitment/dory/vec_operations.rs diff --git a/Cargo.lock b/Cargo.lock index aefc84ab4..bb67d95c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,7 +69,7 @@ dependencies = [ "k256", "keccak-asm", "proptest", - "rand 0.8.5", + "rand", "ruint", "serde", "tiny-keccak", @@ -358,7 +358,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" dependencies = [ "num-traits", - "rand 0.8.5", + "rand", ] [[package]] @@ -368,7 +368,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ "num-traits", - "rand 0.8.5", + "rand", "rayon", ] @@ -477,7 +477,7 @@ dependencies = [ "cfg-if", "derive_more", "p3-util", - "rand 0.8.5", + "rand", "rayon", "seq-macro", "subtle", @@ -890,7 +890,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", - "rand_core 0.6.4", + "rand_core", "subtle", "zeroize", ] @@ -1031,7 +1031,7 @@ dependencies = [ "generic-array", "group", "pkcs8", - "rand_core 0.6.4", + "rand_core", "sec1", "subtle", "zeroize", @@ -1125,7 +1125,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ - "rand_core 0.6.4", + "rand_core", "subtle", ] @@ -1151,7 +1151,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "byteorder", - "rand 0.8.5", + "rand", "rustc-hex", "static_assertions", ] @@ -1269,17 +1269,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.15" @@ -1289,7 +1278,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] @@ -1312,7 +1301,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", - "rand_core 0.6.4", + "rand_core", "subtle", ] @@ -1574,7 +1563,7 @@ dependencies = [ "hex", "icicle-runtime", "once_cell", - "rand 0.8.5", + "rand", "rayon", ] @@ -1586,7 +1575,7 @@ dependencies = [ "cmake", "icicle-core", "icicle-runtime", - "rand 0.8.5", + "rand", ] [[package]] @@ -1851,7 +1840,7 @@ dependencies = [ "eyre", "jolt-core", "jolt-sdk", - "rand 0.8.5", + "rand", "rmp-serde", "serde", "syn 1.0.109", @@ -1880,7 +1869,7 @@ dependencies = [ "enum_dispatch", "eyre", "fixedbitset", - "getrandom 0.2.15", + "getrandom", "iai-callgrind", "icicle-bn254", "icicle-core", @@ -1891,9 +1880,9 @@ dependencies = [ "num-integer", "once_cell", "postcard", - "rand 0.7.3", - "rand_chacha 0.3.1", - "rand_core 0.6.4", + "rand", + "rand_chacha", + "rand_core", "rayon", "reqwest", "serde", @@ -2119,7 +2108,7 @@ checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi 0.3.9", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.52.0", ] @@ -2575,8 +2564,8 @@ dependencies = [ "bitflags", "lazy_static", "num-traits", - "rand 0.8.5", - "rand_chacha 0.3.1", + "rand", + "rand_chacha", "rand_xorshift", "regex-syntax 0.8.5", "rusty-fork", @@ -2605,19 +2594,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - [[package]] name = "rand" version = "0.8.5" @@ -2625,18 +2601,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "rand_chacha", + "rand_core", ] [[package]] @@ -2646,16 +2612,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", + "rand_core", ] [[package]] @@ -2664,16 +2621,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", + "getrandom", ] [[package]] @@ -2682,7 +2630,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ - "rand_core 0.6.4", + "rand_core", ] [[package]] @@ -2720,7 +2668,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.15", + "getrandom", "libredox", "thiserror", ] @@ -2831,7 +2779,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom", "libc", "spin", "untrusted", @@ -2886,7 +2834,7 @@ dependencies = [ "parity-scale-codec", "primitive-types", "proptest", - "rand 0.8.5", + "rand", "rlp", "ruint-macro", "serde", @@ -3279,7 +3227,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest 0.10.7", - "rand_core 0.6.4", + "rand_core", ] [[package]] @@ -3914,12 +3862,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/jolt-core/Cargo.toml b/jolt-core/Cargo.toml index 7df78f525..d78f02a21 100644 --- a/jolt-core/Cargo.toml +++ b/jolt-core/Cargo.toml @@ -53,7 +53,7 @@ num-integer = "0.1.45" postcard = { version = "1.0.8", default-features = false, features = [ "use-std", ] } -rand = "0.7.3" +rand = "0.8.0" rand_chacha = { version = "0.3.0", default-features = false } rand_core = { version = "0.6.4", default-features = false } rayon = { version = "^1.8.0", optional = true } diff --git a/jolt-core/src/poly/commitment/dory.rs b/jolt-core/src/poly/commitment/dory.rs new file mode 100644 index 000000000..d92bc0f71 --- /dev/null +++ b/jolt-core/src/poly/commitment/dory.rs @@ -0,0 +1,142 @@ +use std::{marker::PhantomData, ops::Mul}; + +use ark_ec::pairing::{Pairing, PairingOutput}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use error::Error; +use params::PublicParams; +use rayon::iter::IntoParallelIterator; +use rayon::iter::ParallelIterator; +use scalar::ScalarProof; +use scalar::{commit, Commitment, Witness}; +use vec_operations::{G1Vec, G2Vec}; + +use crate::utils::errors::ProofVerifyError; +use crate::{ + field::JoltField, + poly::multilinear_polynomial::MultilinearPolynomial, + utils::{ + math::Math, + transcript::{AppendToTranscript, Transcript}, + }, +}; + +use super::commitment_scheme::{CommitShape, CommitmentScheme}; + +mod error; +mod params; +mod reduce; +mod scalar; +mod vec_operations; + +#[cfg(test)] +mod tests; + +/// G1 +pub type G1 = ::G1; + +/// G2 +pub type G2 = ::G2; + +/// Cyclic group of integers modulo prime number r +/// +/// This is the Domain Set Z +pub type Zr = ::ScalarField; + +pub type Gt = PairingOutput; + +#[derive(Clone, Default)] +pub struct DoryScheme { + _data: PhantomData<(P, ProofTranscript)>, +} + +impl AppendToTranscript for Commitment

{ + fn append_to_transcript(&self, _transcript: &mut ProofTranscript) { + todo!() + } +} + +#[derive(CanonicalDeserialize, CanonicalSerialize)] +pub struct DoryBatchedProof; + +impl CommitmentScheme for DoryScheme +where + P: Pairing + Default, + ProofTranscript: Transcript, + G1

: Mul, Output = G1

>, + G2

: Mul, Output = G2

>, + P::ScalarField: JoltField + Default, +{ + type Field = Zr

; + + type Setup = PublicParams

; + + type Commitment = Commitment

; + + type Proof = ScalarProof

; + + type BatchedProof = DoryBatchedProof; + + fn setup(shapes: &[CommitShape]) -> Self::Setup { + // Dory's setup procedure initializes + let mut max_len: usize = 0; + for shape in shapes { + let len = shape.input_length.log_2(); + if len > max_len { + max_len = len; + } + } + let mut rng = ark_std::rand::thread_rng(); + PublicParams::new(&mut rng, max_len).expect("Length must be greater than 0") + } + + fn commit(poly: &MultilinearPolynomial, setup: &Self::Setup) -> Self::Commitment { + let MultilinearPolynomial::LargeScalars(poly) = poly else { + panic!("Expected LargeScalars polynomial"); + }; + let witness = Witness::new(setup, poly.evals_ref()); + commit(witness, setup).unwrap() + } + + fn batch_commit( + polys: &[&MultilinearPolynomial], + setup: &Self::Setup, + _batch_type: super::commitment_scheme::BatchType, + ) -> Vec { + polys + .into_par_iter() + .map(|poly| Self::commit(poly, setup)) + .collect() + } + + fn prove( + setup: &Self::Setup, + poly: &MultilinearPolynomial, + _opening_point: &[Self::Field], // point at which the polynomial is evaluated + _transcript: &mut ProofTranscript, + ) -> Self::Proof { + let MultilinearPolynomial::LargeScalars(poly) = poly else { + panic!("Expected LargeScalars polynomial"); + }; + let witness = Witness::new(setup, poly.evals_ref()); + ScalarProof::new(witness) + } + + fn verify( + proof: &Self::Proof, + setup: &Self::Setup, + _transcript: &mut ProofTranscript, + _opening_point: &[Self::Field], // point at which the polynomial is evaluated + _opening: &Self::Field, // evaluation \widetilde{Z}(r) + commitment: &Self::Commitment, + ) -> Result<(), ProofVerifyError> { + if proof.verify(setup, commitment).unwrap() { + Ok(()) + } else { + Err(ProofVerifyError::VerificationFailed) + } + } + + fn protocol_name() -> &'static [u8] { + b"dory" + } +} diff --git a/jolt-core/src/poly/commitment/dory/error.rs b/jolt-core/src/poly/commitment/dory/error.rs new file mode 100644 index 000000000..f9c300354 --- /dev/null +++ b/jolt-core/src/poly/commitment/dory/error.rs @@ -0,0 +1,30 @@ +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("length mismatch")] + LengthMismatch, + #[error("empty vectors: {0:?}")] + EmptyVector(GType), + #[error("could not invert d")] + CouldntInvertD, + + #[error("serialization error {0}")] + Serialization(#[from] ark_serialize::SerializationError), + + #[error( + "recursive public parameters should be twice as the public parameters it is derived from" + )] + LengthNotTwice, + #[error("reduce params not initialized")] + ReduceParamsNotInitialized, + #[error("public params is empty")] + EmptyPublicParams, + + #[error("zr zero")] + ZrZero, +} + +#[derive(Debug)] +pub enum GType { + G1, + G2, +} diff --git a/jolt-core/src/poly/commitment/dory/params.rs b/jolt-core/src/poly/commitment/dory/params.rs new file mode 100644 index 000000000..b8f7123ff --- /dev/null +++ b/jolt-core/src/poly/commitment/dory/params.rs @@ -0,0 +1,163 @@ +use ark_ec::pairing::Pairing; +use ark_ff::UniformRand; +use ark_serialize::CanonicalSerialize; +use ark_std::rand::Rng; +use sha3::{Digest, Sha3_256}; + +use super::{Error, G1Vec, G2Vec, Gt, G1, G2}; + +#[derive(Clone)] +pub struct PublicParams { + pub g1v: G1Vec

, + pub g2v: G2Vec

, + + pub x: Gt

, + + pub reduce_pp: Option>, +} + +#[derive(Clone)] +pub struct ReducePublicParams { + pub gamma_1_prime: G1Vec

, + pub gamma_2_prime: G2Vec

, + + pub delta_1r: Gt

, + pub delta_1l: Gt

, + pub delta_2r: Gt

, + pub delta_2l: Gt

, +} + +impl PublicParams { + pub fn new(rng: &mut impl Rng, n: usize) -> Result + where + G1: UniformRand, + G2: UniformRand, + { + let g1v = G1Vec::random(rng, n); + let g2v = G2Vec::random(rng, n); + let x = g1v.inner_prod(&g2v)?; + let reduce_pp = ReducePublicParams::new(rng, &g1v, &g2v)?; + let value = Self { + g1v, + g2v, + reduce_pp, + x, + }; + Ok(value) + } + + pub fn new_derived(&self, rng: &mut impl Rng, n: usize) -> Result + where + G1: UniformRand, + G2: UniformRand, + { + if self.g1v.len() != 2 * n || self.g2v.len() != 2 * n { + return Err(Error::LengthNotTwice); + } + let Some(reduce_pp) = &self.reduce_pp else { + return Err(Error::ReduceParamsNotInitialized); + }; + let g1v = reduce_pp.gamma_1_prime.clone(); + let g2v = reduce_pp.gamma_2_prime.clone(); + + let reduce_pp = ReducePublicParams::new(rng, &g1v, &g2v)?; + let x = g1v.inner_prod(&g2v)?; + + let value = Self { + g1v, + g2v, + reduce_pp, + x, + }; + Ok(value) + } + + pub fn digest(&self, prev: Option<&[u8]>) -> Result, Error> { + let mut hasher = Sha3_256::new(); + if let Some(prev) = prev { + hasher.update(prev); + } + + if let Some(reduce_pp) = &self.reduce_pp { + hasher.update(reduce_pp.digest()?); + } + self.x + .serialize_uncompressed(&mut hasher) + .expect("Serialization failed"); + + self.g1v.serialize_uncompressed(&mut hasher)?; + self.g2v.serialize_uncompressed(&mut hasher)?; + + Ok(hasher.finalize().to_vec()) + } + + pub fn generate_public_params(rng: &mut impl Rng, mut n: usize) -> Result, Error> + where + G1: UniformRand, + G2: UniformRand, + { + let mut res = Vec::new(); + let mut params = Self::new(rng, n)?; + while n > 0 { + res.push(params); + if n / 2 == 0 { + break; + } + n /= 2; + params = res.last().expect("just pushed").new_derived(rng, n)?; + } + Ok(res) + } +} + +impl ReducePublicParams { + pub fn new( + rng: &mut impl Rng, + g1v: &[G1], + g2v: &[G2], + ) -> Result, Error> + where + G1: UniformRand, + G2: UniformRand, + { + assert_eq!(g1v.len(), g2v.len()); + if g1v.len() == 1 { + return Ok(None); + } + let m = g1v.len() / 2; + let gamma_1l: G1Vec = (&g1v[..m]).into(); + let gamma_1r: G1Vec = (&g1v[m..]).into(); + + let gamma_2l = (&g2v[..m]).into(); + let gamma_2r = (&g2v[m..]).into(); + + let gamma_1_prime = G1Vec::random(rng, m); + let gamma_2_prime = G2Vec::random(rng, m); + + let delta_1l = gamma_1l.inner_prod(&gamma_2_prime)?; + let delta_1r = gamma_1r.inner_prod(&gamma_2_prime)?; + let delta_2l = gamma_1_prime.inner_prod(&gamma_2l)?; + let delta_2r = gamma_1_prime.inner_prod(&gamma_2r)?; + Ok(Some(Self { + gamma_1_prime, + gamma_2_prime, + delta_1r, + delta_1l, + delta_2r, + delta_2l, + })) + } + + pub fn digest(&self) -> Result, Error> { + let mut hasher = Sha3_256::new(); + + self.gamma_1_prime.serialize_uncompressed(&mut hasher)?; + self.gamma_2_prime.serialize_uncompressed(&mut hasher)?; + self.delta_1r.serialize_uncompressed(&mut hasher)?; + self.delta_1l.serialize_uncompressed(&mut hasher)?; + self.delta_2r.serialize_uncompressed(&mut hasher)?; + self.delta_2l.serialize_uncompressed(&mut hasher)?; + + Ok(hasher.finalize().to_vec()) + } +} diff --git a/jolt-core/src/poly/commitment/dory/reduce.rs b/jolt-core/src/poly/commitment/dory/reduce.rs new file mode 100644 index 000000000..57234e38f --- /dev/null +++ b/jolt-core/src/poly/commitment/dory/reduce.rs @@ -0,0 +1,332 @@ +#![allow(dead_code)] + +use std::ops::{Add, Mul}; + +use ark_ec::pairing::Pairing; +use ark_ff::{Field, PrimeField}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use sha3::{Digest, Sha3_256}; + +use super::{ + params::ReducePublicParams, vec_operations::mul_gt, Commitment, Error, G1Vec, G2Vec, Gt, + PublicParams, ScalarProof, Witness, Zr, G1, G2, +}; + +/// Proof +#[derive(Clone, CanonicalDeserialize, CanonicalSerialize)] +pub struct DoryProof { + pub from_prover_1: Vec>, + pub from_prover_2: Vec>, + pub final_proof: ScalarProof, +} + +impl DoryProof { + fn verify_recursive( + public_params: &[PublicParams], + commitment: Commitment, + from_prover_1: &[ReduceProverStep1Elements], + from_prover_2: &[ReduceProverStep2Elements], + final_proof: &ScalarProof, + ) -> Result { + match public_params { + [] => Err(Error::EmptyPublicParams), + [params] => final_proof.verify(params, &commitment), + [param1, public_params_rest @ ..] => { + let digest = param1.digest(None)?.to_vec(); + + let PublicParams { x, reduce_pp, .. } = param1; + let ReducePublicParams { + delta_1r, + delta_1l, + delta_2r, + delta_2l, + .. + } = reduce_pp.as_ref().expect("gv1 is greater than 1"); + + match (from_prover_1, from_prover_2) { + ( + [ReduceProverStep1Elements { + d1l, d1r, d2l, d2r, .. + }, from_prover_1_rest @ ..], + [ReduceProverStep2Elements { + c_plus, c_minus, .. + }, from_prover_2_rest @ ..], + ) => { + let Commitment { c, d1, d2 } = commitment; + + let step_1_element = ReduceProverStep1Elements { + pp_digest: digest, + d1l: *d1l, + d1r: *d1r, + d2l: *d2l, + d2r: *d2r, + c, + d1, + d2, + }; + + let (betha, step_1_digest) = step_1_element.ro()?; + + let step_2_element = ReduceProverStep2Elements { + step_1_digest, + c_plus: *c_plus, + c_minus: *c_minus, + }; + + let alpha = step_2_element.ro()?; + let inverse_alpha = alpha.inverse().ok_or(Error::ZrZero)?; + let inverse_betha = betha.inverse().ok_or(Error::ZrZero)?; + + let c_prime = mul_gt(&[ + c, + *x, + d2 * betha, + d1 * inverse_betha, + *c_plus * alpha, + *c_minus * inverse_alpha, + ]) + .expect("slice is not empty"); + + let d1_prime = mul_gt(&[ + *d1l * alpha, + *d1r, + *delta_1l * alpha * betha, + *delta_1r * betha, + ]) + .expect("slice is not empty"); + + let d2_prime = mul_gt(&[ + *d2l * inverse_alpha, + *d2r, + *delta_2l * inverse_alpha * inverse_betha, + *delta_2r * inverse_betha, + ]) + .expect("slice is not empty"); + + let next_commitment = Commitment { + c: c_prime, + d1: d1_prime, + d2: d2_prime, + }; + + Self::verify_recursive( + public_params_rest, + next_commitment, + from_prover_1_rest, + from_prover_2_rest, + final_proof, + ) + } + _ => todo!(), + } + } + } + } + + pub fn verify( + &self, + public_params: &[PublicParams], + commitment: Commitment, + ) -> Result + where + Gt: Mul, Output = Gt>, + G1: Mul, Output = G1>, + G2: Mul, Output = G2>, + { + Self::verify_recursive( + public_params, + commitment, + &self.from_prover_1, + &self.from_prover_2, + &self.final_proof, + ) + } +} + +#[derive(Clone, CanonicalDeserialize, CanonicalSerialize)] +pub struct ReduceProverStep1Elements { + pp_digest: Vec, + d1l: Gt, + d1r: Gt, + d2l: Gt, + d2r: Gt, + c: Gt, + d1: Gt, + d2: Gt, +} + +impl ReduceProverStep1Elements { + pub fn ro(&self) -> Result<(Zr, Vec), Error> { + let mut hasher = Sha3_256::new(); + self.serialize_uncompressed(&mut hasher)?; + let digest = hasher.finalize(); + Ok(( + Zr::::from_be_bytes_mod_order(&digest), + digest.to_vec(), + )) + } +} + +#[derive(Clone, CanonicalDeserialize, CanonicalSerialize)] +pub struct ReduceProverStep2Elements { + step_1_digest: Vec, + c_plus: Gt, + c_minus: Gt, +} + +impl ReduceProverStep2Elements { + pub fn ro(&self) -> Result, Error> { + let mut hasher = Sha3_256::new(); + self.serialize_uncompressed(&mut hasher)?; + let digest = hasher.finalize(); + Ok(Zr::::from_be_bytes_mod_order(&digest)) + } +} + +pub fn reduce( + params: &[PublicParams], + witness: Witness, + Commitment { c, d1, d2 }: Commitment, +) -> Result, Error> +where + G1Vec: Add, Output = G1Vec>, + G2Vec: Add, Output = G2Vec>, +{ + match params { + [] => unimplemented!(), + [param1, rest_param @ ..] => { + let digest = param1.digest(None)?; + + let PublicParams { + g1v, + g2v, + x, + reduce_pp, + .. + } = param1; + + let ReducePublicParams { + delta_1r, + delta_1l, + delta_2r, + delta_2l, + gamma_1_prime, + gamma_2_prime, + } = reduce_pp.as_ref().unwrap(); + + let m = g1v.len() / 2; + + // P: + let v1l: G1Vec = (&witness.v1[..m]).into(); + let v1r: G1Vec = (&witness.v1[m..]).into(); + let v2l = (&witness.v2[..m]).into(); + let v2r = (&witness.v2[m..]).into(); + + // P --> V: + let d1l = v1l.inner_prod(gamma_2_prime)?; + let d1r = v1r.inner_prod(gamma_2_prime)?; + let d2l = gamma_1_prime.inner_prod(&v2l)?; + let d2r = gamma_1_prime.inner_prod(&v2r)?; + + let step_1_element = ReduceProverStep1Elements { + pp_digest: digest, + d1l, + d1r, + d2l, + d2r, + c, + d1, + d2, + }; + + let (betha, step_1_digest) = step_1_element.ro()?; + let inverse_betha = betha.inverse().unwrap(); + + // P: + let v1 = witness.v1 + (g1v * betha); + let v2 = witness.v2 + (g2v * inverse_betha); + + let v1l: G1Vec = v1[..m].to_vec().into(); + let v1r: G1Vec = v1[m..].to_vec().into(); + let v2l = v2[..m].to_vec().into(); + let v2r = v2[m..].to_vec().into(); + + // P --> V: + let c_plus = v1l.inner_prod(&v2r)?; + let c_minus = v1r.inner_prod(&v2l)?; + + let step_2_element = ReduceProverStep2Elements { + step_1_digest, + c_plus, + c_minus, + }; + let alpha = step_2_element.ro()?; + let inverse_alpha = alpha.inverse().unwrap(); + + let v1_prime = v1l * alpha + v1r; + let v2_prime = v2l * inverse_alpha + v2r; + + let next_witness = Witness { + v1: v1_prime, + v2: v2_prime, + }; + + if m == 1 { + return Ok(DoryProof { + from_prover_1: vec![step_1_element], + from_prover_2: vec![step_2_element], + final_proof: ScalarProof::new(next_witness), + }); + } + + let c_prime = mul_gt(&[ + c, + *x, + d2 * betha, + d1 * inverse_betha, + c_plus * alpha, + c_minus * inverse_alpha, + ]) + .unwrap(); + + let d1_prime = mul_gt(&[ + d1l * alpha, + d1r, + *delta_1l * alpha * betha, + *delta_1r * betha, + ]) + .unwrap(); + + let d2_prime = mul_gt(&[ + d2l * inverse_alpha, + d2r, + *delta_2l * inverse_alpha * inverse_betha, + *delta_2r * inverse_betha, + ]) + .unwrap(); + + let next_commitment = Commitment { + c: c_prime, + d1: d1_prime, + d2: d2_prime, + }; + + let DoryProof { + from_prover_1: step_1_elements, + from_prover_2: step_2_elements, + final_proof: scalar_product_proof, + } = reduce(rest_param, next_witness, next_commitment)?; + + let mut from_prover_1 = vec![step_1_element]; + from_prover_1.extend(step_1_elements); + let mut from_prover_2 = vec![step_2_element]; + from_prover_2.extend(step_2_elements); + + Ok(DoryProof { + from_prover_1, + from_prover_2, + final_proof: scalar_product_proof, + }) + } + } +} diff --git a/jolt-core/src/poly/commitment/dory/scalar.rs b/jolt-core/src/poly/commitment/dory/scalar.rs new file mode 100644 index 000000000..2687fdaa2 --- /dev/null +++ b/jolt-core/src/poly/commitment/dory/scalar.rs @@ -0,0 +1,101 @@ +use std::ops::Mul; + +use ark_ec::pairing::Pairing; +use ark_ff::{Field, UniformRand}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use rand::thread_rng; + +use super::{ + vec_operations::{e, mul_gt}, + Error, G1Vec, G2Vec, Gt, PublicParams, Zr, G1, G2, +}; + +/// Witness over set Zr +#[derive(Clone)] +pub struct Witness { + pub v1: G1Vec, + pub v2: G2Vec, +} + +impl Witness

{ + pub fn new(params: &PublicParams

, poly: &[Zr

]) -> Self { + let v1 = params + .g1v + .iter() + .zip(poly.iter()) + .map(|(a, b)| *a * *b) + .collect::>>(); + + let v2 = params + .g2v + .iter() + .zip(poly.iter()) + .map(|(a, b)| *a * *b) + .collect::>>(); + let v1 = v1.into(); + let v2 = v2.into(); + + Self { v1, v2 } + } +} + +#[derive(Clone, Copy, CanonicalSerialize, CanonicalDeserialize, Debug, Default, PartialEq, Eq)] +pub struct Commitment { + pub c: Gt, + pub d1: Gt, + pub d2: Gt, +} + +pub fn commit( + Witness { v1, v2 }: Witness, + public_params: &PublicParams, +) -> Result, Error> { + let d1 = v1.inner_prod(&public_params.g2v)?; + let d2 = public_params.g1v.inner_prod(&v2)?; + let c = v1.inner_prod(&v2)?; + + let commitment = Commitment { d1, d2, c }; + Ok(commitment) +} + +#[derive(Clone, CanonicalDeserialize, CanonicalSerialize)] +pub struct ScalarProof { + //pp: &'a PublicParams, + e1: G1Vec, + e2: G2Vec, +} + +impl ScalarProof { + pub fn new(witness: Witness) -> Self { + Self { + //pp: public_params, + e1: witness.v1, + e2: witness.v2, + } + } + + pub fn verify( + &self, + pp: &PublicParams, + Commitment { c, d1, d2 }: &Commitment, + ) -> Result + where + for<'c> &'c G1Vec: Mul, Output = G1Vec>, + G1: Mul, Output = G1>, + G2: Mul, Output = G2>, + Gt: Mul, Output = Gt>, + { + let mut rng = thread_rng(); + let d: Zr = Zr::::rand(&mut rng); + let d_inv = d.inverse().ok_or(Error::CouldntInvertD)?; + + let g1 = G1Vec::::from(&[self.e1[0], pp.g1v[0] * d]).sum(); + + let g2 = G2Vec::::from(&[self.e2[0], pp.g2v[0] * d_inv]).sum(); + let left_eq = e(g1, g2); + + let right_eq = mul_gt(&[pp.x, *c, *d2 * d, *d1 * d_inv]).expect("has more than one item"); + + Ok(left_eq == right_eq) + } +} diff --git a/jolt-core/src/poly/commitment/dory/tests.rs b/jolt-core/src/poly/commitment/dory/tests.rs new file mode 100644 index 000000000..48d37267b --- /dev/null +++ b/jolt-core/src/poly/commitment/dory/tests.rs @@ -0,0 +1,52 @@ +use ark_bn254::Bn254; +use ark_std::UniformRand; + +use crate::poly::commitment::dory::scalar::Witness; + +use super::{commit, reduce, G1Vec, G2Vec, PublicParams, ScalarProof, G1, G2}; + +#[test] +fn test_scalar_product_proof() { + let mut rng = ark_std::test_rng(); + let public_params = PublicParams::::new(&mut rng, 1).unwrap(); + + let g1v = vec![G1::::rand(&mut rng)]; + let g2v = vec![G2::::rand(&mut rng)]; + let witness = Witness { + v1: g1v.into(), + v2: g2v.into(), + }; + let commitment = commit(witness.clone(), &public_params).unwrap(); + + let proof = ScalarProof::new(witness); + assert!(proof.verify(&public_params, &commitment).unwrap()); +} + +#[test] +fn test_dory_reduce() { + let mut rng = ark_std::test_rng(); + let n = 8; + let g1v = G1Vec::::random(&mut rng, n); + let g2v = G2Vec::random(&mut rng, n); + + let params = PublicParams::generate_public_params(&mut rng, n).unwrap(); + + let witness = Witness { v1: g1v, v2: g2v }; + let commitment = commit(witness.clone(), ¶ms[0]).unwrap(); + + let proof = reduce::reduce(¶ms, witness, commitment).unwrap(); + + assert_eq!(proof.from_prover_1.len(), 3); + assert_eq!(proof.from_prover_2.len(), 3); + + assert_eq!(params[0].g1v.len(), 8); + assert_eq!(params[1].g1v.len(), 4); + assert_eq!(params[2].g1v.len(), 2); + assert_eq!(params[3].g1v.len(), 1); + + assert_eq!(params[0].reduce_pp.as_ref().unwrap().gamma_1_prime.len(), 4); + assert_eq!(params[1].reduce_pp.as_ref().unwrap().gamma_1_prime.len(), 2); + assert_eq!(params[2].reduce_pp.as_ref().unwrap().gamma_1_prime.len(), 1); + assert!(params[3].reduce_pp.is_none()); + assert!(proof.verify(¶ms, commitment).unwrap()); +} diff --git a/jolt-core/src/poly/commitment/dory/vec_operations.rs b/jolt-core/src/poly/commitment/dory/vec_operations.rs new file mode 100644 index 000000000..fc8966064 --- /dev/null +++ b/jolt-core/src/poly/commitment/dory/vec_operations.rs @@ -0,0 +1,255 @@ +use std::ops::{Add, Deref, Mul}; + +use ark_ec::{pairing::Pairing, Group}; +use ark_ff::UniformRand; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::rand::Rng; + +use super::{ + error::{Error, GType}, + Gt, Zr, G1, G2, +}; + +pub fn e(g1: G1, g2: G2) -> Gt { + Curve::pairing(g1, g2) +} + +pub fn mul_gt(gts: &[Gt]) -> Option> { + gts.iter().fold(None, |prev, curr| match prev { + Some(prev) => Some(curr + prev), + None => Some(*curr), + }) +} + +// G1 +#[derive(Clone, CanonicalDeserialize, CanonicalSerialize)] +pub struct G1Vec(Vec>); + +impl G1Vec { + pub fn inner_prod(&self, g2v: &G2Vec) -> Result, Error> + where + G1: From>, + G2: From>, + { + match (self.as_ref(), g2v.as_ref()) { + ([], _) => Err(Error::EmptyVector(GType::G1)), + (_, []) => Err(Error::EmptyVector(GType::G2)), + (a, b) if a.len() != b.len() => Err(Error::LengthMismatch), + ([g1], [g2]) => Ok(e(*g1, *g2)), + (a, b) => Ok(Curve::multi_pairing(a, b)), + } + } + + pub fn sum(&self) -> G1 { + self.iter().sum() + } + + pub fn random(rng: &mut impl Rng, n: usize) -> Self + where + G1: UniformRand, + { + Self( + (0..n) + .map(|_| { + let random_scalar = Zr::::rand(rng); + G1::::generator() * random_scalar + }) + .collect(), + ) + } +} + +impl Deref for G1Vec { + type Target = [G1]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Add for G1Vec +where + Curve: Pairing, + for<'b> &'b G1: Add<&'b G1, Output = G1>, +{ + type Output = G1Vec; + + fn add(self, rhs: Self) -> Self::Output { + Self( + self.0 + .iter() + .zip(rhs.0.iter()) + .map(|(val1, val2)| val1 + val2) + .collect(), + ) + } +} + +impl Mul> for G1Vec +where + G1: Copy, + G1: Mul, Output = G1>, +{ + type Output = G1Vec; + + fn mul(self, rhs: Zr) -> Self::Output { + G1Vec(self.0.iter().map(|val| *val * rhs).collect()) + } +} + +impl Mul> for &G1Vec +where + G1: Copy, + G1: Mul, Output = G1>, +{ + type Output = G1Vec; + + fn mul(self, rhs: Zr) -> Self::Output { + G1Vec(self.iter().map(|val| *val * rhs).collect()) + } +} + +impl From<&[G1]> for G1Vec { + fn from(value: &[G1]) -> Self { + Self(value.into()) + } +} + +impl From<&[G1; N]> for G1Vec { + fn from(value: &[G1; N]) -> Self { + Self((*value).into()) + } +} + +impl From>> for G1Vec { + fn from(value: Vec>) -> Self { + Self(value) + } +} + +// G2 + +#[derive(Clone, CanonicalDeserialize, CanonicalSerialize)] +pub struct G2Vec(Vec>); + +impl G2Vec { + pub fn sum(&self) -> G2 { + self.iter().sum() + } + + pub fn random(rng: &mut impl Rng, n: usize) -> Self + where + G2: UniformRand, + { + Self( + (0..n) + .map(|_| { + let random_scalar = Zr::::rand(rng); + G2::::generator() * random_scalar + }) + .collect(), + ) + } +} + +impl Deref for G2Vec { + type Target = [G2]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From<&[G2]> for G2Vec { + fn from(value: &[G2]) -> Self { + Self(value.into()) + } +} + +impl From<&[G2; N]> for G2Vec { + fn from(value: &[G2; N]) -> Self { + Self((*value).into()) + } +} + +impl From>> for G2Vec { + fn from(value: Vec>) -> Self { + Self(value) + } +} + +impl Add for G2Vec +where + Curve: Pairing, +{ + type Output = G2Vec; + + fn add(self, rhs: Self) -> Self::Output { + G2Vec( + self.0 + .iter() + .zip(rhs.0.iter()) + .map(|(val1, val2)| *val1 + *val2) + .collect(), + ) + } +} + +impl Mul> for G2Vec +where + Curve: Pairing, + G2: Copy + Mul, Output = G2>, +{ + type Output = G2Vec; + + fn mul(self, rhs: Zr) -> Self::Output { + G2Vec(self.0.iter().map(|val| *val * rhs).collect()) + } +} + +impl Mul> for &G2Vec +where + Curve: Pairing, + G2: Copy, + G2: Mul, Output = G2>, +{ + type Output = G2Vec; + + fn mul(self, rhs: Zr) -> Self::Output { + G2Vec(self.0.iter().map(|val| *val * rhs).collect()) + } +} + +#[cfg(test)] +mod tests { + use ark_bn254::Bn254; + use ark_ff::UniformRand; + + use super::{ + super::{G1Vec, G1, G2}, + e, mul_gt, + }; + + #[test] + fn test_inner_prod() { + let mut rng = ark_std::test_rng(); + let g1a = G1::::rand(&mut rng); + let g1b = G1::::rand(&mut rng); + let g1c = G1::::rand(&mut rng); + + let g2a = G2::::rand(&mut rng); + let g2b = G2::::rand(&mut rng); + let g2c = G2::::rand(&mut rng); + + let expected = mul_gt(&[e(g1a, g2a), e(g1b, g2b), e(g1c, g2c)]).unwrap(); + + let g1v = &[g1a, g1b, g1c]; + let g1v: G1Vec = g1v.into(); + let g2v = &[g2a, g2b, g2c]; + let g2v = g2v.into(); + + let actual = g1v.inner_prod(&g2v).unwrap(); + + assert_eq!(expected, actual); + } +} diff --git a/jolt-core/src/poly/commitment/mod.rs b/jolt-core/src/poly/commitment/mod.rs index 370368ab2..aa4c37cdb 100644 --- a/jolt-core/src/poly/commitment/mod.rs +++ b/jolt-core/src/poly/commitment/mod.rs @@ -1,5 +1,6 @@ pub mod binius; pub mod commitment_scheme; +pub mod dory; pub mod hyperkzg; pub mod kzg; pub mod zeromorph; diff --git a/jolt-core/src/utils/errors.rs b/jolt-core/src/utils/errors.rs index 0521dcd7b..713bb48cf 100644 --- a/jolt-core/src/utils/errors.rs +++ b/jolt-core/src/utils/errors.rs @@ -18,4 +18,6 @@ pub enum ProofVerifyError { KeyLengthError(usize, usize), #[error("Invalid key length: {0}, expected power of 2")] InvalidKeyLength(usize), + #[error("Verification was incorrect")] + VerificationFailed, }