From be1fd423f3893ff119f6d0e98fe793f4db32cf2e Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Mon, 17 Jul 2023 10:16:45 +0200 Subject: [PATCH 01/28] feat: provide polynomial utils --- Cargo.toml | 9 + src/lib.rs | 2 + src/proving_schemes/bulletproof/polynomial.rs | 155 ++++++++++++++++++ 3 files changed, 166 insertions(+) create mode 100644 src/proving_schemes/bulletproof/polynomial.rs diff --git a/Cargo.toml b/Cargo.toml index e6bbd92..448114e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,12 @@ repository = "https://github.com/runtime-machines/rust-template" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +sha2 = {version = "0.9", default-features = false} +num-bigint = "0.4.3" +curve25519-dalek = {version = "3.2.0", features = ["serde"] } +merlin = { version = "3", default-features = false } + +[dependencies.bulletproofs] +git = "https://github.com/runtime-machines/bulletproofs.git" +branch = "fix-range-proof" +features = ["avx2_backend", "scalar_range_proof"] \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 0f0ecd8..5dd35f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +mod proving_schemes; + /// Hello from Magrathea pub fn answer() -> u32 { 42 diff --git a/src/proving_schemes/bulletproof/polynomial.rs b/src/proving_schemes/bulletproof/polynomial.rs new file mode 100644 index 0000000..932b4e0 --- /dev/null +++ b/src/proving_schemes/bulletproof/polynomial.rs @@ -0,0 +1,155 @@ +use curve25519_dalek::scalar::Scalar; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Polynomial(pub(crate) Vec); + +impl Polynomial { + pub fn new(coefficients: Vec) -> Self { + let mut poly = Polynomial(coefficients); + poly.normalize(); + poly + } + + pub fn from(coefficients: &[u128]) -> Self { + Polynomial::new( + coefficients + .iter() + .map(|n| Scalar::from(*n)) + .collect::>(), + ) + } + + pub fn zero() -> Self { + Polynomial(vec![Scalar::zero()]) + } + + pub fn one() -> Self { + Polynomial(vec![Scalar::one()]) + } + + pub fn degree(&self) -> usize { + self.0.len() - 1 + } + + // TODO: check correctness of numerator + // Creates a polinomial that contains a set of `p` points, by using lagrange + pub fn lagrange(p: &[(Scalar, Scalar)]) -> Self { + let k = p.len(); + let mut l = Polynomial::zero(); + for j in 0..k { + let mut l_j = Polynomial::one(); + for i in 0..k { + if i != j { + let c = (p[j].0 - p[i].0).invert(); + l_j = &l_j * &Polynomial::new(vec![-(c * p[i].0), c]); + } + } + l += &(&l_j * &p[j].1); + } + l + } + + // Remove ending zeroes + pub fn normalize(&mut self) { + if self.0.len() > 1 && self.0[self.0.len() - 1] == Scalar::zero() { + let zero = Scalar::zero(); + let first_non_zero = self.0.iter().rev().position(|p| p != &zero); + if let Some(first_non_zero) = first_non_zero { + self.0.resize(self.0.len() - first_non_zero, Scalar::zero()); + } else { + self.0.resize(1, Scalar::zero()); + } + } + } + + pub fn is_zero(&self) -> bool { + self.0.len() == 1 && self.0[0] == Scalar::zero() + } + + pub fn set(&mut self, i: usize, p: Scalar) { + if self.0.len() < i + 1 { + self.0.resize(i + 1, Scalar::zero()); + } + self.0[i] = p; + self.normalize(); + } + + pub fn get(&mut self, i: usize) -> Option<&Scalar> { + self.0.get(i) + } +} + +impl std::ops::AddAssign<&Polynomial> for Polynomial { + fn add_assign(&mut self, rhs: &Polynomial) { + for n in 0..std::cmp::max(self.0.len(), rhs.0.len()) { + if n >= self.0.len() { + self.0.push(rhs.0[n]); + } else if n < self.0.len() && n < rhs.0.len() { + self.0[n] += rhs.0[n]; + } + } + self.normalize(); + } +} + +impl std::ops::AddAssign<&Scalar> for Polynomial { + fn add_assign(&mut self, rhs: &Scalar) { + self.0[0] += rhs; + } +} + +impl std::ops::SubAssign<&Polynomial> for Polynomial { + fn sub_assign(&mut self, rhs: &Polynomial) { + for n in 0..std::cmp::max(self.0.len(), rhs.0.len()) { + if n >= self.0.len() { + self.0.push(rhs.0[n]); + } else if n < self.0.len() && n < rhs.0.len() { + self.0[n] -= rhs.0[n]; + } + } + self.normalize(); + } +} + +impl std::ops::Mul<&Polynomial> for &Polynomial { + type Output = Polynomial; + fn mul(self, rhs: &Polynomial) -> Self::Output { + let mut mul: Vec = std::iter::repeat(Scalar::zero()) + .take(self.0.len() + rhs.0.len() - 1) + .collect(); + for n in 0..self.0.len() { + for m in 0..rhs.0.len() { + mul[n + m] += self.0[n] * rhs.0[m]; + } + } + Polynomial(mul) + } +} + +impl std::ops::Mul<&Scalar> for &Polynomial { + type Output = Polynomial; + fn mul(self, rhs: &Scalar) -> Self::Output { + if rhs == &Scalar::zero() { + Polynomial::zero() + } else { + Polynomial(self.0.iter().map(|v| v * rhs).collect::>()) + } + } +} + +impl std::ops::Div for Polynomial { + type Output = (Polynomial, Polynomial); + + fn div(self, rhs: Polynomial) -> Self::Output { + let (mut q, mut r) = (Polynomial::zero(), self); + while !r.is_zero() && r.degree() >= rhs.degree() { + let lead_r = r.0[r.0.len() - 1]; + let lead_d = rhs.0[rhs.0.len() - 1]; + let mut t = Polynomial::zero(); + t.set(r.0.len() - rhs.0.len(), lead_r * lead_d.invert()); + q += &t; + r -= &(&rhs * &t); + } + (q, r) + } +} From 1aa86a7b1b6c34dd4066ef05d1bfede2d168acef Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Mon, 17 Jul 2023 10:19:13 +0200 Subject: [PATCH 02/28] feat: skeleton of Bulletproof proving scheme --- src/proving_schemes/bulletproof/mod.rs | 83 ++++++++++++++++++++++ src/proving_schemes/mod.rs | 14 ++++ src/proving_schemes/transcript_protocol.rs | 60 ++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 src/proving_schemes/bulletproof/mod.rs create mode 100644 src/proving_schemes/mod.rs create mode 100644 src/proving_schemes/transcript_protocol.rs diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs new file mode 100644 index 0000000..077feee --- /dev/null +++ b/src/proving_schemes/bulletproof/mod.rs @@ -0,0 +1,83 @@ +mod polynomial; + +use bulletproofs::{BulletproofGens, InnerProductProof}; +use curve25519_dalek::{ + constants::RISTRETTO_BASEPOINT_POINT, + ristretto::{CompressedRistretto, RistrettoPoint}, + scalar::Scalar, + traits::MultiscalarMul, +}; +use merlin::Transcript; +use sha2::Sha512; + +use self::polynomial::Polynomial; + +use super::{transcript_protocol::TranscriptProtocol, ProvingScheme}; + +struct BulletproofPS { + gens: Vec, + polynomial: Polynomial, + commitment: RistrettoPoint, +} + +impl ProvingScheme for BulletproofPS { + fn prove( + &self, + transcript: &mut Transcript, + ) -> InnerProductProof { + let vec_one = vec![Scalar::one(); self.polynomial.0.len()]; + let q = (transcript.challenge_scalar(b"w")) * RISTRETTO_BASEPOINT_POINT; + // TODO: split in the middle + let (g_vec, h_vec) = self.gens.split_at(self.gens.len() / 2); + + InnerProductProof::create( + transcript, + &q, + &vec_one, + &vec_one, + g_vec.to_vec(), + h_vec.to_vec(), + self.polynomial.0, + vec_one, + ) + } + + // TODO: ErrorHandling + fn verify( + &self, + gens: &[RistrettoPoint], + transcript: &mut Transcript, + proof: &InnerProductProof, + ) -> bool { + let number_of_children = todo!(); + let vec_one = vec![Scalar::one(); self.polynomial.0.len()]; + let q = (transcript.challenge_scalar(b"w")) * RISTRETTO_BASEPOINT_POINT; + let p = todo!(); + // TODO: split in the middle + let (g_vec, h_vec) = self.gens.split_at(self.gens.len() / 2); + proof + .verify( + number_of_children, + transcript, + &vec_one, + &vec_one, + p, + &q, + g_vec, + h_vec, + ) + .is_ok() + } +} + +fn compute_gens(children_count: usize) -> Vec { + let bp_gens = BulletproofGens::new(1, children_count); + (0..children_count) + .flat_map(|share| bp_gens.share(share).G(1)) + .copied() + .collect() +} + +fn hash_com_to_scalar(com: &CompressedRistretto) -> Scalar { + Scalar::hash_from_bytes::(com.as_bytes()) +} diff --git a/src/proving_schemes/mod.rs b/src/proving_schemes/mod.rs new file mode 100644 index 0000000..2038c66 --- /dev/null +++ b/src/proving_schemes/mod.rs @@ -0,0 +1,14 @@ +use merlin::Transcript; + +mod bulletproof; +mod transcript_protocol; + +pub trait ProvingScheme { + fn prove(&self, transcript: &mut Transcript) -> Proof; + fn verify( + &self, + gens: &[Point], + transcript: &mut Transcript, + proof: &Proof, + ) -> bool; +} diff --git a/src/proving_schemes/transcript_protocol.rs b/src/proving_schemes/transcript_protocol.rs new file mode 100644 index 0000000..72a3e88 --- /dev/null +++ b/src/proving_schemes/transcript_protocol.rs @@ -0,0 +1,60 @@ +use std::borrow::Borrow; + +use curve25519_dalek::{ristretto::CompressedRistretto, scalar::Scalar}; +use merlin::Transcript; + +/// Trait providing methods to append different types to a transcript +pub trait TranscriptProtocol { + fn append_usize(&mut self, label: &'static [u8], value: usize); + fn append_points_vector(&mut self, label: &'static [u8], vec: I) + where + I: IntoIterator, + I::Item: Borrow; + fn append_point( + &mut self, + label: &'static [u8], + point: &CompressedRistretto, + ); + fn append_scalar(&mut self, label: &'static [u8], scalar: &Scalar); + fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar; +} + +impl TranscriptProtocol for Transcript { + fn append_usize(&mut self, label: &'static [u8], value: usize) { + let bytes = value.to_be_bytes(); + // Strip leading zero to obtain the same number of bytes regardless of the size of usize + let cut_position = bytes + .iter() + .position(|byte| *byte != 0) + .unwrap_or(bytes.len() - 1); + self.append_message(label, &bytes[cut_position..]); + } + + fn append_points_vector(&mut self, label: &'static [u8], vec: I) + where + I: IntoIterator, + I::Item: Borrow, + { + for point in vec { + self.append_point(label, point.borrow()); + } + } + + fn append_point( + &mut self, + label: &'static [u8], + point: &CompressedRistretto, + ) { + self.append_message(label, point.as_bytes()); + } + + fn append_scalar(&mut self, label: &'static [u8], scalar: &Scalar) { + self.append_message(label, scalar.as_bytes()) + } + + fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar { + let mut buf = [0; 64]; + self.challenge_bytes(label, &mut buf); + Scalar::from_bytes_mod_order_wide(&buf) + } +} From c4207534fb04af08e697ab511d7f8c6c068fc3ec Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Mon, 17 Jul 2023 11:01:44 +0200 Subject: [PATCH 03/28] fix: solved todos for lagrange formula and middle splitting --- src/proving_schemes/bulletproof/mod.rs | 44 ++++++++++++++----- src/proving_schemes/bulletproof/polynomial.rs | 1 - 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index 077feee..4f79304 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -25,20 +25,20 @@ impl ProvingScheme for BulletproofPS { &self, transcript: &mut Transcript, ) -> InnerProductProof { - let vec_one = vec![Scalar::one(); self.polynomial.0.len()]; + let n = self.polynomial.0.len(); + let vec_one = vec![Scalar::one(); n]; let q = (transcript.challenge_scalar(b"w")) * RISTRETTO_BASEPOINT_POINT; - // TODO: split in the middle - let (g_vec, h_vec) = self.gens.split_at(self.gens.len() / 2); + let (g_vec, h_vec) = split_gens(&self.gens); InnerProductProof::create( transcript, &q, &vec_one, &vec_one, - g_vec.to_vec(), - h_vec.to_vec(), - self.polynomial.0, - vec_one, + g_vec[..n].to_vec(), + h_vec[..n].to_vec(), + self.polynomial.0.clone(), + vec_one.to_vec(), ) } @@ -53,8 +53,7 @@ impl ProvingScheme for BulletproofPS { let vec_one = vec![Scalar::one(); self.polynomial.0.len()]; let q = (transcript.challenge_scalar(b"w")) * RISTRETTO_BASEPOINT_POINT; let p = todo!(); - // TODO: split in the middle - let (g_vec, h_vec) = self.gens.split_at(self.gens.len() / 2); + let (g_vec, h_vec) = split_gens(gens); proof .verify( number_of_children, @@ -63,14 +62,15 @@ impl ProvingScheme for BulletproofPS { &vec_one, p, &q, - g_vec, - h_vec, + &g_vec, + &h_vec, ) .is_ok() } } fn compute_gens(children_count: usize) -> Vec { + let children_count = ((children_count + 1) / 2) * 2; let bp_gens = BulletproofGens::new(1, children_count); (0..children_count) .flat_map(|share| bp_gens.share(share).G(1)) @@ -81,3 +81,25 @@ fn compute_gens(children_count: usize) -> Vec { fn hash_com_to_scalar(com: &CompressedRistretto) -> Scalar { Scalar::hash_from_bytes::(com.as_bytes()) } + +fn split_gens( + gens: &[RistrettoPoint], +) -> (Vec, Vec) { + let (g_vec, h_vec) = gens.split_at((gens.len() + 1) / 2); + (g_vec.to_vec(), h_vec.to_vec()) +} + +#[test] +fn correct_compute_gens() { + // Even gens length + let gens = compute_gens(6); + let (g_vec, h_vec) = split_gens(&gens); + assert_eq!(g_vec.len(), 3); + assert_eq!(h_vec.len(), 3); + + // Odd gens length + let gens = compute_gens(5); + let (g_vec, h_vec) = split_gens(&gens); + assert_eq!(g_vec.len(), 3); + assert_eq!(h_vec.len(), 3); +} diff --git a/src/proving_schemes/bulletproof/polynomial.rs b/src/proving_schemes/bulletproof/polynomial.rs index 932b4e0..2d9feca 100644 --- a/src/proving_schemes/bulletproof/polynomial.rs +++ b/src/proving_schemes/bulletproof/polynomial.rs @@ -31,7 +31,6 @@ impl Polynomial { self.0.len() - 1 } - // TODO: check correctness of numerator // Creates a polinomial that contains a set of `p` points, by using lagrange pub fn lagrange(p: &[(Scalar, Scalar)]) -> Self { let k = p.len(); From 96b3fe80427466388c1ef742648740c5c4a5b5e5 Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Mon, 17 Jul 2023 15:28:47 +0200 Subject: [PATCH 04/28] feat: extend `ProvingScheme` with utility methods and prove, verify implementation --- src/proving_schemes/bulletproof/mod.rs | 98 +++++++++++++++++++------- src/proving_schemes/mod.rs | 25 ++++--- 2 files changed, 88 insertions(+), 35 deletions(-) diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index 4f79304..edcd76d 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -1,6 +1,6 @@ mod polynomial; -use bulletproofs::{BulletproofGens, InnerProductProof}; +use bulletproofs::{inner_product, BulletproofGens, InnerProductProof}; use curve25519_dalek::{ constants::RISTRETTO_BASEPOINT_POINT, ristretto::{CompressedRistretto, RistrettoPoint}, @@ -8,7 +8,6 @@ use curve25519_dalek::{ traits::MultiscalarMul, }; use merlin::Transcript; -use sha2::Sha512; use self::polynomial::Polynomial; @@ -17,21 +16,57 @@ use super::{transcript_protocol::TranscriptProtocol, ProvingScheme}; struct BulletproofPS { gens: Vec, polynomial: Polynomial, - commitment: RistrettoPoint, + commitment: CompressedRistretto, } -impl ProvingScheme for BulletproofPS { - fn prove( - &self, - transcript: &mut Transcript, - ) -> InnerProductProof { +struct InnerProduct { + proof: InnerProductProof, + com: RistrettoPoint, + result: Scalar, +} + +impl ProvingScheme + for BulletproofPS +{ + fn compute_commitment(&self, bytes: &[[u8; 32]]) -> BulletproofPS { + // Compute generators + let gens = compute_gens(bytes.len()); + + let points: Vec<_> = bytes + .iter() + .enumerate() + .map(|(index, &byte)| { + ( + Scalar::from_bytes_mod_order(byte), + Scalar::from(index as u64), + ) + }) + .collect(); + + // Lagrange interpolation + let polynomial = Polynomial::lagrange(&points); + + // Commit + let commitment = + RistrettoPoint::multiscalar_mul(&self.polynomial.0, &gens) + .compress(); + + BulletproofPS { + gens, + polynomial, + commitment, + } + } + + fn prove(&self) -> InnerProduct { + let mut transcript = Transcript::new(b"InnerProductNode"); let n = self.polynomial.0.len(); let vec_one = vec![Scalar::one(); n]; let q = (transcript.challenge_scalar(b"w")) * RISTRETTO_BASEPOINT_POINT; let (g_vec, h_vec) = split_gens(&self.gens); - InnerProductProof::create( - transcript, + let proof = InnerProductProof::create( + &mut transcript, &q, &vec_one, &vec_one, @@ -39,34 +74,51 @@ impl ProvingScheme for BulletproofPS { h_vec[..n].to_vec(), self.polynomial.0.clone(), vec_one.to_vec(), - ) + ); + + let result = inner_product(&self.polynomial.0, &vec_one); + + let com = RistrettoPoint::multiscalar_mul( + self.polynomial.0.iter().chain(vec_one.iter()), + g_vec.iter().chain(h_vec.iter()), + ); + + InnerProduct { proof, result, com } } // TODO: ErrorHandling fn verify( - &self, - gens: &[RistrettoPoint], - transcript: &mut Transcript, - proof: &InnerProductProof, + InnerProduct { proof, result, com }: &InnerProduct, + children: &[CompressedRistretto], ) -> bool { - let number_of_children = todo!(); - let vec_one = vec![Scalar::one(); self.polynomial.0.len()]; + let mut transcript = Transcript::new(b"InnerProductNode"); + let number_of_children = children.len(); + let vec_one = vec![Scalar::one(); number_of_children]; let q = (transcript.challenge_scalar(b"w")) * RISTRETTO_BASEPOINT_POINT; - let p = todo!(); - let (g_vec, h_vec) = split_gens(gens); + let p = com + q * result; + let (g_vec, h_vec) = split_gens(&compute_gens(number_of_children)); + proof .verify( number_of_children, - transcript, + &mut transcript, &vec_one, &vec_one, - p, + &p, &q, &g_vec, &h_vec, ) .is_ok() } + + fn get_commitment(&self) -> CompressedRistretto { + self.commitment + } + + fn commitment_to_bytes(com: CompressedRistretto) -> [u8; 32] { + com.to_bytes() + } } fn compute_gens(children_count: usize) -> Vec { @@ -78,10 +130,6 @@ fn compute_gens(children_count: usize) -> Vec { .collect() } -fn hash_com_to_scalar(com: &CompressedRistretto) -> Scalar { - Scalar::hash_from_bytes::(com.as_bytes()) -} - fn split_gens( gens: &[RistrettoPoint], ) -> (Vec, Vec) { diff --git a/src/proving_schemes/mod.rs b/src/proving_schemes/mod.rs index 2038c66..1e29659 100644 --- a/src/proving_schemes/mod.rs +++ b/src/proving_schemes/mod.rs @@ -1,14 +1,19 @@ -use merlin::Transcript; - mod bulletproof; mod transcript_protocol; -pub trait ProvingScheme { - fn prove(&self, transcript: &mut Transcript) -> Proof; - fn verify( - &self, - gens: &[Point], - transcript: &mut Transcript, - proof: &Proof, - ) -> bool; +pub trait ProvingScheme { + /// Compute the commitment of a slice of bytes interpreted as little endian Scalars + fn compute_commitment(&self, bytes: &[[u8; 32]]) -> Self; + + /// Return the commpressed commitment of the node + fn get_commitment(&self) -> CompressedPoint; + + /// Convert a compressed commitment in a byte array + fn commitment_to_bytes(com: CompressedPoint) -> [u8; 32]; + + /// Compute the proof of a node + fn prove(&self) -> Proof; + + /// Verify the proof of a node + fn verify(proof: &Proof, children: &[CompressedPoint]) -> bool; } From c83a65082aa62a40d0ac2c91ad208a6dd529ef99 Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Tue, 18 Jul 2023 12:16:18 +0200 Subject: [PATCH 05/28] feat & fix: make generators global and correct logic relationship between commit-prove-verify --- src/proving_schemes/bulletproof/mod.rs | 174 +++++++++++++++---------- src/proving_schemes/mod.rs | 28 ++-- 2 files changed, 124 insertions(+), 78 deletions(-) diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index edcd76d..64567ba 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -1,6 +1,6 @@ mod polynomial; -use bulletproofs::{inner_product, BulletproofGens, InnerProductProof}; +use bulletproofs::{BulletproofGens, InnerProductProof}; use curve25519_dalek::{ constants::RISTRETTO_BASEPOINT_POINT, ristretto::{CompressedRistretto, RistrettoPoint}, @@ -11,96 +11,111 @@ use merlin::Transcript; use self::polynomial::Polynomial; -use super::{transcript_protocol::TranscriptProtocol, ProvingScheme}; +use super::{ + transcript_protocol::TranscriptProtocol, ProvingScheme, MAX_GENERATORS, +}; struct BulletproofPS { gens: Vec, - polynomial: Polynomial, - commitment: CompressedRistretto, } -struct InnerProduct { +type Node = (Polynomial, CompressedRistretto); + +struct InnerProductProofCom { proof: InnerProductProof, - com: RistrettoPoint, - result: Scalar, + com: CompressedRistretto, } -impl ProvingScheme +// TODO: manage generators more efficiently + +impl ProvingScheme for BulletproofPS { - fn compute_commitment(&self, bytes: &[[u8; 32]]) -> BulletproofPS { - // Compute generators - let gens = compute_gens(bytes.len()); - - let points: Vec<_> = bytes - .iter() - .enumerate() - .map(|(index, &byte)| { - ( - Scalar::from_bytes_mod_order(byte), - Scalar::from(index as u64), - ) - }) - .collect(); + fn instantiate_generators() -> BulletproofPS { + let padded_length = (MAX_GENERATORS * 2).next_power_of_two(); + let bp_gens = BulletproofGens::new(padded_length, 1); + BulletproofPS { + gens: bp_gens.share(0).G(padded_length).cloned().collect(), + } + } + fn compute_commitment(&self, bytes: &[[u8; 32]]) -> Node { + let points = compute_points(bytes); // Lagrange interpolation let polynomial = Polynomial::lagrange(&points); // Commit - let commitment = - RistrettoPoint::multiscalar_mul(&self.polynomial.0, &gens) - .compress(); + let commitment = RistrettoPoint::multiscalar_mul( + &polynomial.0, + &self.gens[..points.len()], + ) + .compress(); - BulletproofPS { - gens, - polynomial, - commitment, - } + (polynomial, commitment) } - fn prove(&self) -> InnerProduct { + fn prove( + &self, + (polynomial, _): &Node, + point: &(u64, [u8; 32]), + ) -> InnerProductProofCom { let mut transcript = Transcript::new(b"InnerProductNode"); - let n = self.polynomial.0.len(); - let vec_one = vec![Scalar::one(); n]; let q = (transcript.challenge_scalar(b"w")) * RISTRETTO_BASEPOINT_POINT; - let (g_vec, h_vec) = split_gens(&self.gens); + + let n = polynomial.0.len(); + + let g_h_factors = vec![Scalar::one(); n]; + + let (g_vec, h_vec) = split_gens(&self.gens[..(n * 2)]); + + let a_vec = &polynomial.0; + let b_vec = compute_b_vec(n, point.0); + + let com = RistrettoPoint::multiscalar_mul( + a_vec.iter().chain(&b_vec), + g_vec[..n].iter().chain(&h_vec[..n]), + ) + .compress(); let proof = InnerProductProof::create( &mut transcript, &q, - &vec_one, - &vec_one, + &g_h_factors, + &g_h_factors, g_vec[..n].to_vec(), h_vec[..n].to_vec(), - self.polynomial.0.clone(), - vec_one.to_vec(), + a_vec.to_vec(), + b_vec, ); - let result = inner_product(&self.polynomial.0, &vec_one); - - let com = RistrettoPoint::multiscalar_mul( - self.polynomial.0.iter().chain(vec_one.iter()), - g_vec.iter().chain(h_vec.iter()), - ); - - InnerProduct { proof, result, com } + InnerProductProofCom { proof, com } } // TODO: ErrorHandling + // TODO: correct to share generators? fn verify( - InnerProduct { proof, result, com }: &InnerProduct, - children: &[CompressedRistretto], + &self, + InnerProductProofCom { proof, com }: &InnerProductProofCom, + children_count: usize, + _point: &(u64, [u8; 32]), ) -> bool { let mut transcript = Transcript::new(b"InnerProductNode"); - let number_of_children = children.len(); - let vec_one = vec![Scalar::one(); number_of_children]; let q = (transcript.challenge_scalar(b"w")) * RISTRETTO_BASEPOINT_POINT; - let p = com + q * result; - let (g_vec, h_vec) = split_gens(&compute_gens(number_of_children)); + + let n = children_count.next_power_of_two(); + + let vec_one = vec![Scalar::one(); n]; + + // TODO: handle error + let com = com.decompress().unwrap(); + + let p = com + q; + + let (g_vec, h_vec) = split_gens(&self.gens[..(n * 2)]); proof .verify( - number_of_children, + n, &mut transcript, &vec_one, &vec_one, @@ -112,21 +127,42 @@ impl ProvingScheme .is_ok() } - fn get_commitment(&self) -> CompressedRistretto { - self.commitment - } - fn commitment_to_bytes(com: CompressedRistretto) -> [u8; 32] { com.to_bytes() } + + fn add_new_generator(&self) { + todo!() + } } -fn compute_gens(children_count: usize) -> Vec { - let children_count = ((children_count + 1) / 2) * 2; - let bp_gens = BulletproofGens::new(1, children_count); - (0..children_count) - .flat_map(|share| bp_gens.share(share).G(1)) +fn compute_points(bytes: &[[u8; 32]]) -> Vec<(Scalar, Scalar)> { + let points: Vec<_> = bytes + .iter() + .enumerate() + .map(|(index, &byte)| point_into_scalar_point(&(index as u64, byte))) + .collect(); + + padding_points(&points) +} + +fn point_into_scalar_point( + &(position, evaluation): &(u64, [u8; 32]), +) -> (Scalar, Scalar) { + ( + Scalar::from(position), + Scalar::from_bytes_mod_order(evaluation), + ) +} + +fn padding_points(points: &[(Scalar, Scalar)]) -> Vec<(Scalar, Scalar)> { + points + .iter() .copied() + .chain( + (points.len()..points.len().next_power_of_two()) + .map(|index| (Scalar::from(index as u64), Scalar::zero())), + ) .collect() } @@ -137,13 +173,13 @@ fn split_gens( (g_vec.to_vec(), h_vec.to_vec()) } -#[test] -fn correct_compute_gens() { - // Even gens length - let gens = compute_gens(6); - let (g_vec, h_vec) = split_gens(&gens); - assert_eq!(g_vec.len(), 3); - assert_eq!(h_vec.len(), 3); +fn compute_b_vec(length: usize, position: u64) -> Vec { + // TODO: handle types error + let length: u32 = length.try_into().unwrap(); + (0..length) + .map(|index| Scalar::from(position.pow(index))) + .collect() +} // Odd gens length let gens = compute_gens(5); diff --git a/src/proving_schemes/mod.rs b/src/proving_schemes/mod.rs index 1e29659..501d9b4 100644 --- a/src/proving_schemes/mod.rs +++ b/src/proving_schemes/mod.rs @@ -1,19 +1,29 @@ mod bulletproof; mod transcript_protocol; -pub trait ProvingScheme { - /// Compute the commitment of a slice of bytes interpreted as little endian Scalars - fn compute_commitment(&self, bytes: &[[u8; 32]]) -> Self; +pub const MAX_GENERATORS: usize = 256; - /// Return the commpressed commitment of the node - fn get_commitment(&self) -> CompressedPoint; +pub trait ProvingScheme { + // Instantiate the `ProvingScheme` instantiating a starting amount of generators + fn instantiate_generators() -> Self; + + // Increase the generators' quantity + fn add_new_generator(&self); + + /// Generate a polynomial and its commitment from slice of points + fn compute_commitment(&self, bytes: &[[u8; 32]]) -> Node; /// Convert a compressed commitment in a byte array fn commitment_to_bytes(com: CompressedPoint) -> [u8; 32]; - /// Compute the proof of a node - fn prove(&self) -> Proof; + /// Compute the proof that the point (position, evaluation) is a node's child + fn prove(&self, node: &Node, point: &(u64, [u8; 32])) -> Proof; - /// Verify the proof of a node - fn verify(proof: &Proof, children: &[CompressedPoint]) -> bool; + /// Verify that points the point (position, evaluation) is a node's child + fn verify( + &self, + proof: &Proof, + children_count: usize, + point: &(u64, [u8; 32]), + ) -> bool; } From 7126fe597e11d425aa6cd9b72e0dd5cecd58caac Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Tue, 18 Jul 2023 12:17:08 +0200 Subject: [PATCH 06/28] test: add test for evaluating the polynomial's and prove-verification flow --- src/proving_schemes/bulletproof/mod.rs | 73 +++++++++++++++++-- src/proving_schemes/bulletproof/polynomial.rs | 10 +++ 2 files changed, 78 insertions(+), 5 deletions(-) diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index 64567ba..5175656 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -181,9 +181,72 @@ fn compute_b_vec(length: usize, position: u64) -> Vec { .collect() } - // Odd gens length - let gens = compute_gens(5); - let (g_vec, h_vec) = split_gens(&gens); - assert_eq!(g_vec.len(), 3); - assert_eq!(h_vec.len(), 3); +#[cfg(test)] +mod test { + use curve25519_dalek::scalar::Scalar; + + use crate::proving_schemes::{ + bulletproof::polynomial::Polynomial, ProvingScheme, + }; + + use super::BulletproofPS; + + fn generate_bytes() -> Vec<[u8; 32]> { + vec![ + [ + 0x90, 0x76, 0x33, 0xfe, 0x1c, 0x4b, 0x66, 0xa4, 0xa2, 0x8d, + 0x2d, 0xd7, 0x67, 0x83, 0x86, 0xc3, 0x53, 0xd0, 0xde, 0x54, + 0x55, 0xd4, 0xfc, 0x9d, 0xe8, 0xef, 0x7a, 0xc3, 0x1f, 0x35, + 0xbb, 0x05, + ], + [ + 0x6c, 0x33, 0x74, 0xa1, 0x89, 0x4f, 0x62, 0x21, 0x0a, 0xaa, + 0x2f, 0xe1, 0x86, 0xa6, 0xf9, 0x2c, 0xe0, 0xaa, 0x75, 0xc2, + 0x77, 0x95, 0x81, 0xc2, 0x95, 0xfc, 0x08, 0x17, 0x9a, 0x73, + 0x94, 0x0c, + ], + [ + 0xef, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, + 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, + ], + ] + } + + fn generate_positions() -> Vec<(Scalar, Scalar)> { + generate_bytes() + .iter() + .enumerate() + .map(|(position, &evaluation)| { + ( + Scalar::from(position as u128), + Scalar::from_bytes_mod_order(evaluation), + ) + }) + .collect() + } + + #[test] + fn correct_polynomial_construction() { + let bytes = generate_positions(); + + let polynomial = Polynomial::lagrange(&bytes); + + assert_eq!(polynomial.eval(&Scalar::from(0_u128)), bytes[0].1); + assert_eq!(polynomial.eval(&Scalar::from(1_u128)), bytes[1].1); + assert_eq!(polynomial.eval(&Scalar::from(2_u128)), bytes[2].1); + } + + #[test] + fn correct_prove_verification() { + let scheme = BulletproofPS::instantiate_generators(); + let bytes: Vec<[u8; 32]> = generate_bytes(); + + let node = scheme.compute_commitment(&bytes); + + let proof = scheme.prove::(&node, &(1, bytes[1])); + + assert!(scheme.verify(&proof, bytes.len(), &(1, bytes[1]))); + } } diff --git a/src/proving_schemes/bulletproof/polynomial.rs b/src/proving_schemes/bulletproof/polynomial.rs index 2d9feca..685b36b 100644 --- a/src/proving_schemes/bulletproof/polynomial.rs +++ b/src/proving_schemes/bulletproof/polynomial.rs @@ -48,6 +48,16 @@ impl Polynomial { l } + pub fn eval(&self, x: &Scalar) -> Scalar { + let mut x_pow = Scalar::one(); + let mut y = self.0[0]; + for (i, _) in self.0.iter().enumerate().skip(1) { + x_pow *= x; + y += &(x_pow * self.0[i]); + } + y + } + // Remove ending zeroes pub fn normalize(&mut self) { if self.0.len() > 1 && self.0[self.0.len() - 1] == Scalar::zero() { From c753309dfa2468ad7fd61319bc269f85a6087ab5 Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Tue, 18 Jul 2023 14:16:33 +0200 Subject: [PATCH 07/28] refactor: move generic types of trait as internal types --- src/proving_schemes/bulletproof/mod.rs | 16 +++++++++------- src/proving_schemes/mod.rs | 15 ++++++++++----- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index 5175656..5d1756a 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -28,9 +28,11 @@ struct InnerProductProofCom { // TODO: manage generators more efficiently -impl ProvingScheme - for BulletproofPS -{ +impl ProvingScheme for BulletproofPS { + type Scalar = Scalar; + type Commit = Node; + type Proof = InnerProductProofCom; + fn instantiate_generators() -> BulletproofPS { let padded_length = (MAX_GENERATORS * 2).next_power_of_two(); let bp_gens = BulletproofGens::new(padded_length, 1); @@ -54,7 +56,7 @@ impl ProvingScheme (polynomial, commitment) } - fn prove( + fn prove( &self, (polynomial, _): &Node, point: &(u64, [u8; 32]), @@ -127,8 +129,8 @@ impl ProvingScheme .is_ok() } - fn commitment_to_bytes(com: CompressedRistretto) -> [u8; 32] { - com.to_bytes() + fn commitment_to_bytes(com: Node) -> [u8; 32] { + com.1.to_bytes() } fn add_new_generator(&self) { @@ -245,7 +247,7 @@ mod test { let node = scheme.compute_commitment(&bytes); - let proof = scheme.prove::(&node, &(1, bytes[1])); + let proof = scheme.prove(&node, &(1, bytes[1])); assert!(scheme.verify(&proof, bytes.len(), &(1, bytes[1]))); } diff --git a/src/proving_schemes/mod.rs b/src/proving_schemes/mod.rs index 501d9b4..6014bae 100644 --- a/src/proving_schemes/mod.rs +++ b/src/proving_schemes/mod.rs @@ -3,7 +3,11 @@ mod transcript_protocol; pub const MAX_GENERATORS: usize = 256; -pub trait ProvingScheme { +pub trait ProvingScheme { + type Scalar; + type Commit; + type Proof; + // Instantiate the `ProvingScheme` instantiating a starting amount of generators fn instantiate_generators() -> Self; @@ -11,18 +15,19 @@ pub trait ProvingScheme { fn add_new_generator(&self); /// Generate a polynomial and its commitment from slice of points - fn compute_commitment(&self, bytes: &[[u8; 32]]) -> Node; + fn compute_commitment(&self, bytes: &[[u8; 32]]) -> Self::Commit; /// Convert a compressed commitment in a byte array - fn commitment_to_bytes(com: CompressedPoint) -> [u8; 32]; + fn commitment_to_bytes(com: Self::Commit) -> [u8; 32]; /// Compute the proof that the point (position, evaluation) is a node's child - fn prove(&self, node: &Node, point: &(u64, [u8; 32])) -> Proof; + fn prove(&self, com: &Self::Commit, point: &(u64, [u8; 32])) + -> Self::Proof; /// Verify that points the point (position, evaluation) is a node's child fn verify( &self, - proof: &Proof, + proof: &Self::Proof, children_count: usize, point: &(u64, [u8; 32]), ) -> bool; From 37c7c9ed23a53e8946968523665c479fa0196078 Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Tue, 18 Jul 2023 14:38:52 +0200 Subject: [PATCH 08/28] fix: correct computation of Q point including the inner product --- src/proving_schemes/bulletproof/mod.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index 5d1756a..192e3a2 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -1,6 +1,6 @@ mod polynomial; -use bulletproofs::{BulletproofGens, InnerProductProof}; +use bulletproofs::{inner_product, BulletproofGens, InnerProductProof}; use curve25519_dalek::{ constants::RISTRETTO_BASEPOINT_POINT, ristretto::{CompressedRistretto, RistrettoPoint}, @@ -21,9 +21,10 @@ struct BulletproofPS { type Node = (Polynomial, CompressedRistretto); -struct InnerProductProofCom { +struct InnerProduct { proof: InnerProductProof, com: CompressedRistretto, + value: Scalar, } // TODO: manage generators more efficiently @@ -31,7 +32,7 @@ struct InnerProductProofCom { impl ProvingScheme for BulletproofPS { type Scalar = Scalar; type Commit = Node; - type Proof = InnerProductProofCom; + type Proof = InnerProduct; fn instantiate_generators() -> BulletproofPS { let padded_length = (MAX_GENERATORS * 2).next_power_of_two(); @@ -60,7 +61,7 @@ impl ProvingScheme for BulletproofPS { &self, (polynomial, _): &Node, point: &(u64, [u8; 32]), - ) -> InnerProductProofCom { + ) -> InnerProduct { let mut transcript = Transcript::new(b"InnerProductNode"); let q = (transcript.challenge_scalar(b"w")) * RISTRETTO_BASEPOINT_POINT; @@ -79,6 +80,8 @@ impl ProvingScheme for BulletproofPS { ) .compress(); + let value = inner_product(a_vec, &b_vec); + let proof = InnerProductProof::create( &mut transcript, &q, @@ -90,14 +93,14 @@ impl ProvingScheme for BulletproofPS { b_vec, ); - InnerProductProofCom { proof, com } + InnerProduct { proof, com, value } } // TODO: ErrorHandling // TODO: correct to share generators? fn verify( &self, - InnerProductProofCom { proof, com }: &InnerProductProofCom, + InnerProduct { proof, com, value }: &InnerProduct, children_count: usize, _point: &(u64, [u8; 32]), ) -> bool { @@ -111,7 +114,7 @@ impl ProvingScheme for BulletproofPS { // TODO: handle error let com = com.decompress().unwrap(); - let p = com + q; + let p = com + (q * value); let (g_vec, h_vec) = split_gens(&self.gens[..(n * 2)]); From 57c0255fb28aad39df7caf48a19647c2e58c5bd9 Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Tue, 18 Jul 2023 14:46:49 +0200 Subject: [PATCH 09/28] docs: add code and doc comments --- src/proving_schemes/bulletproof/mod.rs | 14 ++++++++------ src/proving_schemes/mod.rs | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index 192e3a2..c0c0c52 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -15,18 +15,25 @@ use super::{ transcript_protocol::TranscriptProtocol, ProvingScheme, MAX_GENERATORS, }; +/// Represent the `ProvingScheme` based on the Bulletproof protocol struct BulletproofPS { gens: Vec, } +/// Represent the commitment type Node = (Polynomial, CompressedRistretto); +/// Represent the info related to the IPA such as; +/// - the proof +/// - the vector commitment of of a and b vectors +/// - the value of the inner product struct InnerProduct { proof: InnerProductProof, com: CompressedRistretto, value: Scalar, } +// TODO: handle errors // TODO: manage generators more efficiently impl ProvingScheme for BulletproofPS { @@ -44,10 +51,9 @@ impl ProvingScheme for BulletproofPS { fn compute_commitment(&self, bytes: &[[u8; 32]]) -> Node { let points = compute_points(bytes); - // Lagrange interpolation + let polynomial = Polynomial::lagrange(&points); - // Commit let commitment = RistrettoPoint::multiscalar_mul( &polynomial.0, &self.gens[..points.len()], @@ -96,8 +102,6 @@ impl ProvingScheme for BulletproofPS { InnerProduct { proof, com, value } } - // TODO: ErrorHandling - // TODO: correct to share generators? fn verify( &self, InnerProduct { proof, com, value }: &InnerProduct, @@ -111,7 +115,6 @@ impl ProvingScheme for BulletproofPS { let vec_one = vec![Scalar::one(); n]; - // TODO: handle error let com = com.decompress().unwrap(); let p = com + (q * value); @@ -179,7 +182,6 @@ fn split_gens( } fn compute_b_vec(length: usize, position: u64) -> Vec { - // TODO: handle types error let length: u32 = length.try_into().unwrap(); (0..length) .map(|index| Scalar::from(position.pow(index))) diff --git a/src/proving_schemes/mod.rs b/src/proving_schemes/mod.rs index 6014bae..f055841 100644 --- a/src/proving_schemes/mod.rs +++ b/src/proving_schemes/mod.rs @@ -8,13 +8,13 @@ pub trait ProvingScheme { type Commit; type Proof; - // Instantiate the `ProvingScheme` instantiating a starting amount of generators + // Return a `ProvingScheme` by instantiating a starting amount of generators fn instantiate_generators() -> Self; // Increase the generators' quantity fn add_new_generator(&self); - /// Generate a polynomial and its commitment from slice of points + /// Generate a polynomial and its commitment from slice of bytes fn compute_commitment(&self, bytes: &[[u8; 32]]) -> Self::Commit; /// Convert a compressed commitment in a byte array From 83631ea4f56119fb410705e7a19bd5cd36eb2d4a Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Tue, 18 Jul 2023 14:54:07 +0200 Subject: [PATCH 10/28] feat: implement increasing of gens --- src/proving_schemes/bulletproof/mod.rs | 14 +++++++++----- src/proving_schemes/mod.rs | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index c0c0c52..355aef7 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -42,10 +42,8 @@ impl ProvingScheme for BulletproofPS { type Proof = InnerProduct; fn instantiate_generators() -> BulletproofPS { - let padded_length = (MAX_GENERATORS * 2).next_power_of_two(); - let bp_gens = BulletproofGens::new(padded_length, 1); BulletproofPS { - gens: bp_gens.share(0).G(padded_length).cloned().collect(), + gens: create_gens(MAX_GENERATORS * 2), } } @@ -139,11 +137,17 @@ impl ProvingScheme for BulletproofPS { com.1.to_bytes() } - fn add_new_generator(&self) { - todo!() + fn add_new_generator(&mut self) { + self.gens = create_gens(self.gens.len() + 1); } } +fn create_gens(gens_capacity: usize) -> Vec { + let padded_length = gens_capacity.next_power_of_two(); + let bp_gens = BulletproofGens::new(padded_length, 1); + bp_gens.share(0).G(padded_length).cloned().collect() +} + fn compute_points(bytes: &[[u8; 32]]) -> Vec<(Scalar, Scalar)> { let points: Vec<_> = bytes .iter() diff --git a/src/proving_schemes/mod.rs b/src/proving_schemes/mod.rs index f055841..2ee07bb 100644 --- a/src/proving_schemes/mod.rs +++ b/src/proving_schemes/mod.rs @@ -12,7 +12,7 @@ pub trait ProvingScheme { fn instantiate_generators() -> Self; // Increase the generators' quantity - fn add_new_generator(&self); + fn add_new_generator(&mut self); /// Generate a polynomial and its commitment from slice of bytes fn compute_commitment(&self, bytes: &[[u8; 32]]) -> Self::Commit; From af281f043489b04c436f43e0e36dff759a06fab0 Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Mon, 17 Jul 2023 10:16:45 +0200 Subject: [PATCH 11/28] feat: provide polynomial utils --- Cargo.toml | 10 +- src/lib.rs | 2 + src/proving_schemes/bulletproof/polynomial.rs | 155 ++++++++++++++++++ 3 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 src/proving_schemes/bulletproof/polynomial.rs diff --git a/Cargo.toml b/Cargo.toml index 6ffe789..a6c789c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,12 @@ repository = "https://github.com/runtime-machines/verkle-tree-rust" ark-std = { version = "^0.3.0", default-features = false } rand = { version = "0.8.3" } ark-ff = { version = "^0.3.0", default-features = false } -ark-ec = { version = "^0.3.0", default-features = false } \ No newline at end of file +ark-ec = { version = "^0.3.0", default-features = false } +num-bigint = "0.4.3" +curve25519-dalek = {version = "3.2.0", features = ["serde"] } +merlin = { version = "3", default-features = false } + +[dependencies.bulletproofs] +git = "https://github.com/runtime-machines/bulletproofs.git" +branch = "fix-range-proof" +features = ["avx2_backend", "scalar_range_proof"] \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 56e955d..4ed3916 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +mod proving_schemes; + mod committer; mod db; mod errors; diff --git a/src/proving_schemes/bulletproof/polynomial.rs b/src/proving_schemes/bulletproof/polynomial.rs new file mode 100644 index 0000000..932b4e0 --- /dev/null +++ b/src/proving_schemes/bulletproof/polynomial.rs @@ -0,0 +1,155 @@ +use curve25519_dalek::scalar::Scalar; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Polynomial(pub(crate) Vec); + +impl Polynomial { + pub fn new(coefficients: Vec) -> Self { + let mut poly = Polynomial(coefficients); + poly.normalize(); + poly + } + + pub fn from(coefficients: &[u128]) -> Self { + Polynomial::new( + coefficients + .iter() + .map(|n| Scalar::from(*n)) + .collect::>(), + ) + } + + pub fn zero() -> Self { + Polynomial(vec![Scalar::zero()]) + } + + pub fn one() -> Self { + Polynomial(vec![Scalar::one()]) + } + + pub fn degree(&self) -> usize { + self.0.len() - 1 + } + + // TODO: check correctness of numerator + // Creates a polinomial that contains a set of `p` points, by using lagrange + pub fn lagrange(p: &[(Scalar, Scalar)]) -> Self { + let k = p.len(); + let mut l = Polynomial::zero(); + for j in 0..k { + let mut l_j = Polynomial::one(); + for i in 0..k { + if i != j { + let c = (p[j].0 - p[i].0).invert(); + l_j = &l_j * &Polynomial::new(vec![-(c * p[i].0), c]); + } + } + l += &(&l_j * &p[j].1); + } + l + } + + // Remove ending zeroes + pub fn normalize(&mut self) { + if self.0.len() > 1 && self.0[self.0.len() - 1] == Scalar::zero() { + let zero = Scalar::zero(); + let first_non_zero = self.0.iter().rev().position(|p| p != &zero); + if let Some(first_non_zero) = first_non_zero { + self.0.resize(self.0.len() - first_non_zero, Scalar::zero()); + } else { + self.0.resize(1, Scalar::zero()); + } + } + } + + pub fn is_zero(&self) -> bool { + self.0.len() == 1 && self.0[0] == Scalar::zero() + } + + pub fn set(&mut self, i: usize, p: Scalar) { + if self.0.len() < i + 1 { + self.0.resize(i + 1, Scalar::zero()); + } + self.0[i] = p; + self.normalize(); + } + + pub fn get(&mut self, i: usize) -> Option<&Scalar> { + self.0.get(i) + } +} + +impl std::ops::AddAssign<&Polynomial> for Polynomial { + fn add_assign(&mut self, rhs: &Polynomial) { + for n in 0..std::cmp::max(self.0.len(), rhs.0.len()) { + if n >= self.0.len() { + self.0.push(rhs.0[n]); + } else if n < self.0.len() && n < rhs.0.len() { + self.0[n] += rhs.0[n]; + } + } + self.normalize(); + } +} + +impl std::ops::AddAssign<&Scalar> for Polynomial { + fn add_assign(&mut self, rhs: &Scalar) { + self.0[0] += rhs; + } +} + +impl std::ops::SubAssign<&Polynomial> for Polynomial { + fn sub_assign(&mut self, rhs: &Polynomial) { + for n in 0..std::cmp::max(self.0.len(), rhs.0.len()) { + if n >= self.0.len() { + self.0.push(rhs.0[n]); + } else if n < self.0.len() && n < rhs.0.len() { + self.0[n] -= rhs.0[n]; + } + } + self.normalize(); + } +} + +impl std::ops::Mul<&Polynomial> for &Polynomial { + type Output = Polynomial; + fn mul(self, rhs: &Polynomial) -> Self::Output { + let mut mul: Vec = std::iter::repeat(Scalar::zero()) + .take(self.0.len() + rhs.0.len() - 1) + .collect(); + for n in 0..self.0.len() { + for m in 0..rhs.0.len() { + mul[n + m] += self.0[n] * rhs.0[m]; + } + } + Polynomial(mul) + } +} + +impl std::ops::Mul<&Scalar> for &Polynomial { + type Output = Polynomial; + fn mul(self, rhs: &Scalar) -> Self::Output { + if rhs == &Scalar::zero() { + Polynomial::zero() + } else { + Polynomial(self.0.iter().map(|v| v * rhs).collect::>()) + } + } +} + +impl std::ops::Div for Polynomial { + type Output = (Polynomial, Polynomial); + + fn div(self, rhs: Polynomial) -> Self::Output { + let (mut q, mut r) = (Polynomial::zero(), self); + while !r.is_zero() && r.degree() >= rhs.degree() { + let lead_r = r.0[r.0.len() - 1]; + let lead_d = rhs.0[rhs.0.len() - 1]; + let mut t = Polynomial::zero(); + t.set(r.0.len() - rhs.0.len(), lead_r * lead_d.invert()); + q += &t; + r -= &(&rhs * &t); + } + (q, r) + } +} From 71c6cc946122764357c04de366c74a1f17528816 Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Mon, 17 Jul 2023 10:19:13 +0200 Subject: [PATCH 12/28] feat: skeleton of Bulletproof proving scheme --- src/proving_schemes/bulletproof/mod.rs | 83 ++++++++++++++++++++++ src/proving_schemes/mod.rs | 14 ++++ src/proving_schemes/transcript_protocol.rs | 60 ++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 src/proving_schemes/bulletproof/mod.rs create mode 100644 src/proving_schemes/mod.rs create mode 100644 src/proving_schemes/transcript_protocol.rs diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs new file mode 100644 index 0000000..077feee --- /dev/null +++ b/src/proving_schemes/bulletproof/mod.rs @@ -0,0 +1,83 @@ +mod polynomial; + +use bulletproofs::{BulletproofGens, InnerProductProof}; +use curve25519_dalek::{ + constants::RISTRETTO_BASEPOINT_POINT, + ristretto::{CompressedRistretto, RistrettoPoint}, + scalar::Scalar, + traits::MultiscalarMul, +}; +use merlin::Transcript; +use sha2::Sha512; + +use self::polynomial::Polynomial; + +use super::{transcript_protocol::TranscriptProtocol, ProvingScheme}; + +struct BulletproofPS { + gens: Vec, + polynomial: Polynomial, + commitment: RistrettoPoint, +} + +impl ProvingScheme for BulletproofPS { + fn prove( + &self, + transcript: &mut Transcript, + ) -> InnerProductProof { + let vec_one = vec![Scalar::one(); self.polynomial.0.len()]; + let q = (transcript.challenge_scalar(b"w")) * RISTRETTO_BASEPOINT_POINT; + // TODO: split in the middle + let (g_vec, h_vec) = self.gens.split_at(self.gens.len() / 2); + + InnerProductProof::create( + transcript, + &q, + &vec_one, + &vec_one, + g_vec.to_vec(), + h_vec.to_vec(), + self.polynomial.0, + vec_one, + ) + } + + // TODO: ErrorHandling + fn verify( + &self, + gens: &[RistrettoPoint], + transcript: &mut Transcript, + proof: &InnerProductProof, + ) -> bool { + let number_of_children = todo!(); + let vec_one = vec![Scalar::one(); self.polynomial.0.len()]; + let q = (transcript.challenge_scalar(b"w")) * RISTRETTO_BASEPOINT_POINT; + let p = todo!(); + // TODO: split in the middle + let (g_vec, h_vec) = self.gens.split_at(self.gens.len() / 2); + proof + .verify( + number_of_children, + transcript, + &vec_one, + &vec_one, + p, + &q, + g_vec, + h_vec, + ) + .is_ok() + } +} + +fn compute_gens(children_count: usize) -> Vec { + let bp_gens = BulletproofGens::new(1, children_count); + (0..children_count) + .flat_map(|share| bp_gens.share(share).G(1)) + .copied() + .collect() +} + +fn hash_com_to_scalar(com: &CompressedRistretto) -> Scalar { + Scalar::hash_from_bytes::(com.as_bytes()) +} diff --git a/src/proving_schemes/mod.rs b/src/proving_schemes/mod.rs new file mode 100644 index 0000000..2038c66 --- /dev/null +++ b/src/proving_schemes/mod.rs @@ -0,0 +1,14 @@ +use merlin::Transcript; + +mod bulletproof; +mod transcript_protocol; + +pub trait ProvingScheme { + fn prove(&self, transcript: &mut Transcript) -> Proof; + fn verify( + &self, + gens: &[Point], + transcript: &mut Transcript, + proof: &Proof, + ) -> bool; +} diff --git a/src/proving_schemes/transcript_protocol.rs b/src/proving_schemes/transcript_protocol.rs new file mode 100644 index 0000000..72a3e88 --- /dev/null +++ b/src/proving_schemes/transcript_protocol.rs @@ -0,0 +1,60 @@ +use std::borrow::Borrow; + +use curve25519_dalek::{ristretto::CompressedRistretto, scalar::Scalar}; +use merlin::Transcript; + +/// Trait providing methods to append different types to a transcript +pub trait TranscriptProtocol { + fn append_usize(&mut self, label: &'static [u8], value: usize); + fn append_points_vector(&mut self, label: &'static [u8], vec: I) + where + I: IntoIterator, + I::Item: Borrow; + fn append_point( + &mut self, + label: &'static [u8], + point: &CompressedRistretto, + ); + fn append_scalar(&mut self, label: &'static [u8], scalar: &Scalar); + fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar; +} + +impl TranscriptProtocol for Transcript { + fn append_usize(&mut self, label: &'static [u8], value: usize) { + let bytes = value.to_be_bytes(); + // Strip leading zero to obtain the same number of bytes regardless of the size of usize + let cut_position = bytes + .iter() + .position(|byte| *byte != 0) + .unwrap_or(bytes.len() - 1); + self.append_message(label, &bytes[cut_position..]); + } + + fn append_points_vector(&mut self, label: &'static [u8], vec: I) + where + I: IntoIterator, + I::Item: Borrow, + { + for point in vec { + self.append_point(label, point.borrow()); + } + } + + fn append_point( + &mut self, + label: &'static [u8], + point: &CompressedRistretto, + ) { + self.append_message(label, point.as_bytes()); + } + + fn append_scalar(&mut self, label: &'static [u8], scalar: &Scalar) { + self.append_message(label, scalar.as_bytes()) + } + + fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar { + let mut buf = [0; 64]; + self.challenge_bytes(label, &mut buf); + Scalar::from_bytes_mod_order_wide(&buf) + } +} From c13a8b203d54b27a6a82e5cc4f55908f28baa03d Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Mon, 17 Jul 2023 11:01:44 +0200 Subject: [PATCH 13/28] fix: solved todos for lagrange formula and middle splitting --- src/proving_schemes/bulletproof/mod.rs | 44 ++++++++++++++----- src/proving_schemes/bulletproof/polynomial.rs | 1 - 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index 077feee..4f79304 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -25,20 +25,20 @@ impl ProvingScheme for BulletproofPS { &self, transcript: &mut Transcript, ) -> InnerProductProof { - let vec_one = vec![Scalar::one(); self.polynomial.0.len()]; + let n = self.polynomial.0.len(); + let vec_one = vec![Scalar::one(); n]; let q = (transcript.challenge_scalar(b"w")) * RISTRETTO_BASEPOINT_POINT; - // TODO: split in the middle - let (g_vec, h_vec) = self.gens.split_at(self.gens.len() / 2); + let (g_vec, h_vec) = split_gens(&self.gens); InnerProductProof::create( transcript, &q, &vec_one, &vec_one, - g_vec.to_vec(), - h_vec.to_vec(), - self.polynomial.0, - vec_one, + g_vec[..n].to_vec(), + h_vec[..n].to_vec(), + self.polynomial.0.clone(), + vec_one.to_vec(), ) } @@ -53,8 +53,7 @@ impl ProvingScheme for BulletproofPS { let vec_one = vec![Scalar::one(); self.polynomial.0.len()]; let q = (transcript.challenge_scalar(b"w")) * RISTRETTO_BASEPOINT_POINT; let p = todo!(); - // TODO: split in the middle - let (g_vec, h_vec) = self.gens.split_at(self.gens.len() / 2); + let (g_vec, h_vec) = split_gens(gens); proof .verify( number_of_children, @@ -63,14 +62,15 @@ impl ProvingScheme for BulletproofPS { &vec_one, p, &q, - g_vec, - h_vec, + &g_vec, + &h_vec, ) .is_ok() } } fn compute_gens(children_count: usize) -> Vec { + let children_count = ((children_count + 1) / 2) * 2; let bp_gens = BulletproofGens::new(1, children_count); (0..children_count) .flat_map(|share| bp_gens.share(share).G(1)) @@ -81,3 +81,25 @@ fn compute_gens(children_count: usize) -> Vec { fn hash_com_to_scalar(com: &CompressedRistretto) -> Scalar { Scalar::hash_from_bytes::(com.as_bytes()) } + +fn split_gens( + gens: &[RistrettoPoint], +) -> (Vec, Vec) { + let (g_vec, h_vec) = gens.split_at((gens.len() + 1) / 2); + (g_vec.to_vec(), h_vec.to_vec()) +} + +#[test] +fn correct_compute_gens() { + // Even gens length + let gens = compute_gens(6); + let (g_vec, h_vec) = split_gens(&gens); + assert_eq!(g_vec.len(), 3); + assert_eq!(h_vec.len(), 3); + + // Odd gens length + let gens = compute_gens(5); + let (g_vec, h_vec) = split_gens(&gens); + assert_eq!(g_vec.len(), 3); + assert_eq!(h_vec.len(), 3); +} diff --git a/src/proving_schemes/bulletproof/polynomial.rs b/src/proving_schemes/bulletproof/polynomial.rs index 932b4e0..2d9feca 100644 --- a/src/proving_schemes/bulletproof/polynomial.rs +++ b/src/proving_schemes/bulletproof/polynomial.rs @@ -31,7 +31,6 @@ impl Polynomial { self.0.len() - 1 } - // TODO: check correctness of numerator // Creates a polinomial that contains a set of `p` points, by using lagrange pub fn lagrange(p: &[(Scalar, Scalar)]) -> Self { let k = p.len(); From 77b0b533785c138a3809e18520fba9441a25aa58 Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Mon, 17 Jul 2023 15:28:47 +0200 Subject: [PATCH 14/28] feat: extend `ProvingScheme` with utility methods and prove, verify implementation --- src/proving_schemes/bulletproof/mod.rs | 98 +++++++++++++++++++------- src/proving_schemes/mod.rs | 25 ++++--- 2 files changed, 88 insertions(+), 35 deletions(-) diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index 4f79304..edcd76d 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -1,6 +1,6 @@ mod polynomial; -use bulletproofs::{BulletproofGens, InnerProductProof}; +use bulletproofs::{inner_product, BulletproofGens, InnerProductProof}; use curve25519_dalek::{ constants::RISTRETTO_BASEPOINT_POINT, ristretto::{CompressedRistretto, RistrettoPoint}, @@ -8,7 +8,6 @@ use curve25519_dalek::{ traits::MultiscalarMul, }; use merlin::Transcript; -use sha2::Sha512; use self::polynomial::Polynomial; @@ -17,21 +16,57 @@ use super::{transcript_protocol::TranscriptProtocol, ProvingScheme}; struct BulletproofPS { gens: Vec, polynomial: Polynomial, - commitment: RistrettoPoint, + commitment: CompressedRistretto, } -impl ProvingScheme for BulletproofPS { - fn prove( - &self, - transcript: &mut Transcript, - ) -> InnerProductProof { +struct InnerProduct { + proof: InnerProductProof, + com: RistrettoPoint, + result: Scalar, +} + +impl ProvingScheme + for BulletproofPS +{ + fn compute_commitment(&self, bytes: &[[u8; 32]]) -> BulletproofPS { + // Compute generators + let gens = compute_gens(bytes.len()); + + let points: Vec<_> = bytes + .iter() + .enumerate() + .map(|(index, &byte)| { + ( + Scalar::from_bytes_mod_order(byte), + Scalar::from(index as u64), + ) + }) + .collect(); + + // Lagrange interpolation + let polynomial = Polynomial::lagrange(&points); + + // Commit + let commitment = + RistrettoPoint::multiscalar_mul(&self.polynomial.0, &gens) + .compress(); + + BulletproofPS { + gens, + polynomial, + commitment, + } + } + + fn prove(&self) -> InnerProduct { + let mut transcript = Transcript::new(b"InnerProductNode"); let n = self.polynomial.0.len(); let vec_one = vec![Scalar::one(); n]; let q = (transcript.challenge_scalar(b"w")) * RISTRETTO_BASEPOINT_POINT; let (g_vec, h_vec) = split_gens(&self.gens); - InnerProductProof::create( - transcript, + let proof = InnerProductProof::create( + &mut transcript, &q, &vec_one, &vec_one, @@ -39,34 +74,51 @@ impl ProvingScheme for BulletproofPS { h_vec[..n].to_vec(), self.polynomial.0.clone(), vec_one.to_vec(), - ) + ); + + let result = inner_product(&self.polynomial.0, &vec_one); + + let com = RistrettoPoint::multiscalar_mul( + self.polynomial.0.iter().chain(vec_one.iter()), + g_vec.iter().chain(h_vec.iter()), + ); + + InnerProduct { proof, result, com } } // TODO: ErrorHandling fn verify( - &self, - gens: &[RistrettoPoint], - transcript: &mut Transcript, - proof: &InnerProductProof, + InnerProduct { proof, result, com }: &InnerProduct, + children: &[CompressedRistretto], ) -> bool { - let number_of_children = todo!(); - let vec_one = vec![Scalar::one(); self.polynomial.0.len()]; + let mut transcript = Transcript::new(b"InnerProductNode"); + let number_of_children = children.len(); + let vec_one = vec![Scalar::one(); number_of_children]; let q = (transcript.challenge_scalar(b"w")) * RISTRETTO_BASEPOINT_POINT; - let p = todo!(); - let (g_vec, h_vec) = split_gens(gens); + let p = com + q * result; + let (g_vec, h_vec) = split_gens(&compute_gens(number_of_children)); + proof .verify( number_of_children, - transcript, + &mut transcript, &vec_one, &vec_one, - p, + &p, &q, &g_vec, &h_vec, ) .is_ok() } + + fn get_commitment(&self) -> CompressedRistretto { + self.commitment + } + + fn commitment_to_bytes(com: CompressedRistretto) -> [u8; 32] { + com.to_bytes() + } } fn compute_gens(children_count: usize) -> Vec { @@ -78,10 +130,6 @@ fn compute_gens(children_count: usize) -> Vec { .collect() } -fn hash_com_to_scalar(com: &CompressedRistretto) -> Scalar { - Scalar::hash_from_bytes::(com.as_bytes()) -} - fn split_gens( gens: &[RistrettoPoint], ) -> (Vec, Vec) { diff --git a/src/proving_schemes/mod.rs b/src/proving_schemes/mod.rs index 2038c66..1e29659 100644 --- a/src/proving_schemes/mod.rs +++ b/src/proving_schemes/mod.rs @@ -1,14 +1,19 @@ -use merlin::Transcript; - mod bulletproof; mod transcript_protocol; -pub trait ProvingScheme { - fn prove(&self, transcript: &mut Transcript) -> Proof; - fn verify( - &self, - gens: &[Point], - transcript: &mut Transcript, - proof: &Proof, - ) -> bool; +pub trait ProvingScheme { + /// Compute the commitment of a slice of bytes interpreted as little endian Scalars + fn compute_commitment(&self, bytes: &[[u8; 32]]) -> Self; + + /// Return the commpressed commitment of the node + fn get_commitment(&self) -> CompressedPoint; + + /// Convert a compressed commitment in a byte array + fn commitment_to_bytes(com: CompressedPoint) -> [u8; 32]; + + /// Compute the proof of a node + fn prove(&self) -> Proof; + + /// Verify the proof of a node + fn verify(proof: &Proof, children: &[CompressedPoint]) -> bool; } From 05848ee1916006cb6b09b675cac84559e35a0d27 Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Tue, 18 Jul 2023 12:16:18 +0200 Subject: [PATCH 15/28] feat & fix: make generators global and correct logic relationship between commit-prove-verify --- src/proving_schemes/bulletproof/mod.rs | 174 +++++++++++++++---------- src/proving_schemes/mod.rs | 28 ++-- 2 files changed, 124 insertions(+), 78 deletions(-) diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index edcd76d..64567ba 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -1,6 +1,6 @@ mod polynomial; -use bulletproofs::{inner_product, BulletproofGens, InnerProductProof}; +use bulletproofs::{BulletproofGens, InnerProductProof}; use curve25519_dalek::{ constants::RISTRETTO_BASEPOINT_POINT, ristretto::{CompressedRistretto, RistrettoPoint}, @@ -11,96 +11,111 @@ use merlin::Transcript; use self::polynomial::Polynomial; -use super::{transcript_protocol::TranscriptProtocol, ProvingScheme}; +use super::{ + transcript_protocol::TranscriptProtocol, ProvingScheme, MAX_GENERATORS, +}; struct BulletproofPS { gens: Vec, - polynomial: Polynomial, - commitment: CompressedRistretto, } -struct InnerProduct { +type Node = (Polynomial, CompressedRistretto); + +struct InnerProductProofCom { proof: InnerProductProof, - com: RistrettoPoint, - result: Scalar, + com: CompressedRistretto, } -impl ProvingScheme +// TODO: manage generators more efficiently + +impl ProvingScheme for BulletproofPS { - fn compute_commitment(&self, bytes: &[[u8; 32]]) -> BulletproofPS { - // Compute generators - let gens = compute_gens(bytes.len()); - - let points: Vec<_> = bytes - .iter() - .enumerate() - .map(|(index, &byte)| { - ( - Scalar::from_bytes_mod_order(byte), - Scalar::from(index as u64), - ) - }) - .collect(); + fn instantiate_generators() -> BulletproofPS { + let padded_length = (MAX_GENERATORS * 2).next_power_of_two(); + let bp_gens = BulletproofGens::new(padded_length, 1); + BulletproofPS { + gens: bp_gens.share(0).G(padded_length).cloned().collect(), + } + } + fn compute_commitment(&self, bytes: &[[u8; 32]]) -> Node { + let points = compute_points(bytes); // Lagrange interpolation let polynomial = Polynomial::lagrange(&points); // Commit - let commitment = - RistrettoPoint::multiscalar_mul(&self.polynomial.0, &gens) - .compress(); + let commitment = RistrettoPoint::multiscalar_mul( + &polynomial.0, + &self.gens[..points.len()], + ) + .compress(); - BulletproofPS { - gens, - polynomial, - commitment, - } + (polynomial, commitment) } - fn prove(&self) -> InnerProduct { + fn prove( + &self, + (polynomial, _): &Node, + point: &(u64, [u8; 32]), + ) -> InnerProductProofCom { let mut transcript = Transcript::new(b"InnerProductNode"); - let n = self.polynomial.0.len(); - let vec_one = vec![Scalar::one(); n]; let q = (transcript.challenge_scalar(b"w")) * RISTRETTO_BASEPOINT_POINT; - let (g_vec, h_vec) = split_gens(&self.gens); + + let n = polynomial.0.len(); + + let g_h_factors = vec![Scalar::one(); n]; + + let (g_vec, h_vec) = split_gens(&self.gens[..(n * 2)]); + + let a_vec = &polynomial.0; + let b_vec = compute_b_vec(n, point.0); + + let com = RistrettoPoint::multiscalar_mul( + a_vec.iter().chain(&b_vec), + g_vec[..n].iter().chain(&h_vec[..n]), + ) + .compress(); let proof = InnerProductProof::create( &mut transcript, &q, - &vec_one, - &vec_one, + &g_h_factors, + &g_h_factors, g_vec[..n].to_vec(), h_vec[..n].to_vec(), - self.polynomial.0.clone(), - vec_one.to_vec(), + a_vec.to_vec(), + b_vec, ); - let result = inner_product(&self.polynomial.0, &vec_one); - - let com = RistrettoPoint::multiscalar_mul( - self.polynomial.0.iter().chain(vec_one.iter()), - g_vec.iter().chain(h_vec.iter()), - ); - - InnerProduct { proof, result, com } + InnerProductProofCom { proof, com } } // TODO: ErrorHandling + // TODO: correct to share generators? fn verify( - InnerProduct { proof, result, com }: &InnerProduct, - children: &[CompressedRistretto], + &self, + InnerProductProofCom { proof, com }: &InnerProductProofCom, + children_count: usize, + _point: &(u64, [u8; 32]), ) -> bool { let mut transcript = Transcript::new(b"InnerProductNode"); - let number_of_children = children.len(); - let vec_one = vec![Scalar::one(); number_of_children]; let q = (transcript.challenge_scalar(b"w")) * RISTRETTO_BASEPOINT_POINT; - let p = com + q * result; - let (g_vec, h_vec) = split_gens(&compute_gens(number_of_children)); + + let n = children_count.next_power_of_two(); + + let vec_one = vec![Scalar::one(); n]; + + // TODO: handle error + let com = com.decompress().unwrap(); + + let p = com + q; + + let (g_vec, h_vec) = split_gens(&self.gens[..(n * 2)]); proof .verify( - number_of_children, + n, &mut transcript, &vec_one, &vec_one, @@ -112,21 +127,42 @@ impl ProvingScheme .is_ok() } - fn get_commitment(&self) -> CompressedRistretto { - self.commitment - } - fn commitment_to_bytes(com: CompressedRistretto) -> [u8; 32] { com.to_bytes() } + + fn add_new_generator(&self) { + todo!() + } } -fn compute_gens(children_count: usize) -> Vec { - let children_count = ((children_count + 1) / 2) * 2; - let bp_gens = BulletproofGens::new(1, children_count); - (0..children_count) - .flat_map(|share| bp_gens.share(share).G(1)) +fn compute_points(bytes: &[[u8; 32]]) -> Vec<(Scalar, Scalar)> { + let points: Vec<_> = bytes + .iter() + .enumerate() + .map(|(index, &byte)| point_into_scalar_point(&(index as u64, byte))) + .collect(); + + padding_points(&points) +} + +fn point_into_scalar_point( + &(position, evaluation): &(u64, [u8; 32]), +) -> (Scalar, Scalar) { + ( + Scalar::from(position), + Scalar::from_bytes_mod_order(evaluation), + ) +} + +fn padding_points(points: &[(Scalar, Scalar)]) -> Vec<(Scalar, Scalar)> { + points + .iter() .copied() + .chain( + (points.len()..points.len().next_power_of_two()) + .map(|index| (Scalar::from(index as u64), Scalar::zero())), + ) .collect() } @@ -137,13 +173,13 @@ fn split_gens( (g_vec.to_vec(), h_vec.to_vec()) } -#[test] -fn correct_compute_gens() { - // Even gens length - let gens = compute_gens(6); - let (g_vec, h_vec) = split_gens(&gens); - assert_eq!(g_vec.len(), 3); - assert_eq!(h_vec.len(), 3); +fn compute_b_vec(length: usize, position: u64) -> Vec { + // TODO: handle types error + let length: u32 = length.try_into().unwrap(); + (0..length) + .map(|index| Scalar::from(position.pow(index))) + .collect() +} // Odd gens length let gens = compute_gens(5); diff --git a/src/proving_schemes/mod.rs b/src/proving_schemes/mod.rs index 1e29659..501d9b4 100644 --- a/src/proving_schemes/mod.rs +++ b/src/proving_schemes/mod.rs @@ -1,19 +1,29 @@ mod bulletproof; mod transcript_protocol; -pub trait ProvingScheme { - /// Compute the commitment of a slice of bytes interpreted as little endian Scalars - fn compute_commitment(&self, bytes: &[[u8; 32]]) -> Self; +pub const MAX_GENERATORS: usize = 256; - /// Return the commpressed commitment of the node - fn get_commitment(&self) -> CompressedPoint; +pub trait ProvingScheme { + // Instantiate the `ProvingScheme` instantiating a starting amount of generators + fn instantiate_generators() -> Self; + + // Increase the generators' quantity + fn add_new_generator(&self); + + /// Generate a polynomial and its commitment from slice of points + fn compute_commitment(&self, bytes: &[[u8; 32]]) -> Node; /// Convert a compressed commitment in a byte array fn commitment_to_bytes(com: CompressedPoint) -> [u8; 32]; - /// Compute the proof of a node - fn prove(&self) -> Proof; + /// Compute the proof that the point (position, evaluation) is a node's child + fn prove(&self, node: &Node, point: &(u64, [u8; 32])) -> Proof; - /// Verify the proof of a node - fn verify(proof: &Proof, children: &[CompressedPoint]) -> bool; + /// Verify that points the point (position, evaluation) is a node's child + fn verify( + &self, + proof: &Proof, + children_count: usize, + point: &(u64, [u8; 32]), + ) -> bool; } From 97c8c3a567bc76be64eb00de2c1e4305c504b354 Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Tue, 18 Jul 2023 12:17:08 +0200 Subject: [PATCH 16/28] test: add test for evaluating the polynomial's and prove-verification flow --- src/proving_schemes/bulletproof/mod.rs | 73 +++++++++++++++++-- src/proving_schemes/bulletproof/polynomial.rs | 10 +++ 2 files changed, 78 insertions(+), 5 deletions(-) diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index 64567ba..5175656 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -181,9 +181,72 @@ fn compute_b_vec(length: usize, position: u64) -> Vec { .collect() } - // Odd gens length - let gens = compute_gens(5); - let (g_vec, h_vec) = split_gens(&gens); - assert_eq!(g_vec.len(), 3); - assert_eq!(h_vec.len(), 3); +#[cfg(test)] +mod test { + use curve25519_dalek::scalar::Scalar; + + use crate::proving_schemes::{ + bulletproof::polynomial::Polynomial, ProvingScheme, + }; + + use super::BulletproofPS; + + fn generate_bytes() -> Vec<[u8; 32]> { + vec![ + [ + 0x90, 0x76, 0x33, 0xfe, 0x1c, 0x4b, 0x66, 0xa4, 0xa2, 0x8d, + 0x2d, 0xd7, 0x67, 0x83, 0x86, 0xc3, 0x53, 0xd0, 0xde, 0x54, + 0x55, 0xd4, 0xfc, 0x9d, 0xe8, 0xef, 0x7a, 0xc3, 0x1f, 0x35, + 0xbb, 0x05, + ], + [ + 0x6c, 0x33, 0x74, 0xa1, 0x89, 0x4f, 0x62, 0x21, 0x0a, 0xaa, + 0x2f, 0xe1, 0x86, 0xa6, 0xf9, 0x2c, 0xe0, 0xaa, 0x75, 0xc2, + 0x77, 0x95, 0x81, 0xc2, 0x95, 0xfc, 0x08, 0x17, 0x9a, 0x73, + 0x94, 0x0c, + ], + [ + 0xef, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, + 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, + ], + ] + } + + fn generate_positions() -> Vec<(Scalar, Scalar)> { + generate_bytes() + .iter() + .enumerate() + .map(|(position, &evaluation)| { + ( + Scalar::from(position as u128), + Scalar::from_bytes_mod_order(evaluation), + ) + }) + .collect() + } + + #[test] + fn correct_polynomial_construction() { + let bytes = generate_positions(); + + let polynomial = Polynomial::lagrange(&bytes); + + assert_eq!(polynomial.eval(&Scalar::from(0_u128)), bytes[0].1); + assert_eq!(polynomial.eval(&Scalar::from(1_u128)), bytes[1].1); + assert_eq!(polynomial.eval(&Scalar::from(2_u128)), bytes[2].1); + } + + #[test] + fn correct_prove_verification() { + let scheme = BulletproofPS::instantiate_generators(); + let bytes: Vec<[u8; 32]> = generate_bytes(); + + let node = scheme.compute_commitment(&bytes); + + let proof = scheme.prove::(&node, &(1, bytes[1])); + + assert!(scheme.verify(&proof, bytes.len(), &(1, bytes[1]))); + } } diff --git a/src/proving_schemes/bulletproof/polynomial.rs b/src/proving_schemes/bulletproof/polynomial.rs index 2d9feca..685b36b 100644 --- a/src/proving_schemes/bulletproof/polynomial.rs +++ b/src/proving_schemes/bulletproof/polynomial.rs @@ -48,6 +48,16 @@ impl Polynomial { l } + pub fn eval(&self, x: &Scalar) -> Scalar { + let mut x_pow = Scalar::one(); + let mut y = self.0[0]; + for (i, _) in self.0.iter().enumerate().skip(1) { + x_pow *= x; + y += &(x_pow * self.0[i]); + } + y + } + // Remove ending zeroes pub fn normalize(&mut self) { if self.0.len() > 1 && self.0[self.0.len() - 1] == Scalar::zero() { From 159b70ab6a2fc6ec574ef2ba170a47ff989da2c8 Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Tue, 18 Jul 2023 14:16:33 +0200 Subject: [PATCH 17/28] refactor: move generic types of trait as internal types --- src/proving_schemes/bulletproof/mod.rs | 16 +++++++++------- src/proving_schemes/mod.rs | 15 ++++++++++----- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index 5175656..5d1756a 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -28,9 +28,11 @@ struct InnerProductProofCom { // TODO: manage generators more efficiently -impl ProvingScheme - for BulletproofPS -{ +impl ProvingScheme for BulletproofPS { + type Scalar = Scalar; + type Commit = Node; + type Proof = InnerProductProofCom; + fn instantiate_generators() -> BulletproofPS { let padded_length = (MAX_GENERATORS * 2).next_power_of_two(); let bp_gens = BulletproofGens::new(padded_length, 1); @@ -54,7 +56,7 @@ impl ProvingScheme (polynomial, commitment) } - fn prove( + fn prove( &self, (polynomial, _): &Node, point: &(u64, [u8; 32]), @@ -127,8 +129,8 @@ impl ProvingScheme .is_ok() } - fn commitment_to_bytes(com: CompressedRistretto) -> [u8; 32] { - com.to_bytes() + fn commitment_to_bytes(com: Node) -> [u8; 32] { + com.1.to_bytes() } fn add_new_generator(&self) { @@ -245,7 +247,7 @@ mod test { let node = scheme.compute_commitment(&bytes); - let proof = scheme.prove::(&node, &(1, bytes[1])); + let proof = scheme.prove(&node, &(1, bytes[1])); assert!(scheme.verify(&proof, bytes.len(), &(1, bytes[1]))); } diff --git a/src/proving_schemes/mod.rs b/src/proving_schemes/mod.rs index 501d9b4..6014bae 100644 --- a/src/proving_schemes/mod.rs +++ b/src/proving_schemes/mod.rs @@ -3,7 +3,11 @@ mod transcript_protocol; pub const MAX_GENERATORS: usize = 256; -pub trait ProvingScheme { +pub trait ProvingScheme { + type Scalar; + type Commit; + type Proof; + // Instantiate the `ProvingScheme` instantiating a starting amount of generators fn instantiate_generators() -> Self; @@ -11,18 +15,19 @@ pub trait ProvingScheme { fn add_new_generator(&self); /// Generate a polynomial and its commitment from slice of points - fn compute_commitment(&self, bytes: &[[u8; 32]]) -> Node; + fn compute_commitment(&self, bytes: &[[u8; 32]]) -> Self::Commit; /// Convert a compressed commitment in a byte array - fn commitment_to_bytes(com: CompressedPoint) -> [u8; 32]; + fn commitment_to_bytes(com: Self::Commit) -> [u8; 32]; /// Compute the proof that the point (position, evaluation) is a node's child - fn prove(&self, node: &Node, point: &(u64, [u8; 32])) -> Proof; + fn prove(&self, com: &Self::Commit, point: &(u64, [u8; 32])) + -> Self::Proof; /// Verify that points the point (position, evaluation) is a node's child fn verify( &self, - proof: &Proof, + proof: &Self::Proof, children_count: usize, point: &(u64, [u8; 32]), ) -> bool; From 01adc24b5f17fcbf5812190d3657801827a63045 Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Tue, 18 Jul 2023 14:38:52 +0200 Subject: [PATCH 18/28] fix: correct computation of Q point including the inner product --- src/proving_schemes/bulletproof/mod.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index 5d1756a..192e3a2 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -1,6 +1,6 @@ mod polynomial; -use bulletproofs::{BulletproofGens, InnerProductProof}; +use bulletproofs::{inner_product, BulletproofGens, InnerProductProof}; use curve25519_dalek::{ constants::RISTRETTO_BASEPOINT_POINT, ristretto::{CompressedRistretto, RistrettoPoint}, @@ -21,9 +21,10 @@ struct BulletproofPS { type Node = (Polynomial, CompressedRistretto); -struct InnerProductProofCom { +struct InnerProduct { proof: InnerProductProof, com: CompressedRistretto, + value: Scalar, } // TODO: manage generators more efficiently @@ -31,7 +32,7 @@ struct InnerProductProofCom { impl ProvingScheme for BulletproofPS { type Scalar = Scalar; type Commit = Node; - type Proof = InnerProductProofCom; + type Proof = InnerProduct; fn instantiate_generators() -> BulletproofPS { let padded_length = (MAX_GENERATORS * 2).next_power_of_two(); @@ -60,7 +61,7 @@ impl ProvingScheme for BulletproofPS { &self, (polynomial, _): &Node, point: &(u64, [u8; 32]), - ) -> InnerProductProofCom { + ) -> InnerProduct { let mut transcript = Transcript::new(b"InnerProductNode"); let q = (transcript.challenge_scalar(b"w")) * RISTRETTO_BASEPOINT_POINT; @@ -79,6 +80,8 @@ impl ProvingScheme for BulletproofPS { ) .compress(); + let value = inner_product(a_vec, &b_vec); + let proof = InnerProductProof::create( &mut transcript, &q, @@ -90,14 +93,14 @@ impl ProvingScheme for BulletproofPS { b_vec, ); - InnerProductProofCom { proof, com } + InnerProduct { proof, com, value } } // TODO: ErrorHandling // TODO: correct to share generators? fn verify( &self, - InnerProductProofCom { proof, com }: &InnerProductProofCom, + InnerProduct { proof, com, value }: &InnerProduct, children_count: usize, _point: &(u64, [u8; 32]), ) -> bool { @@ -111,7 +114,7 @@ impl ProvingScheme for BulletproofPS { // TODO: handle error let com = com.decompress().unwrap(); - let p = com + q; + let p = com + (q * value); let (g_vec, h_vec) = split_gens(&self.gens[..(n * 2)]); From ae3146be7d65daf55532f33662b4d9c32b739f3e Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Tue, 18 Jul 2023 14:46:49 +0200 Subject: [PATCH 19/28] docs: add code and doc comments --- src/proving_schemes/bulletproof/mod.rs | 14 ++++++++------ src/proving_schemes/mod.rs | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index 192e3a2..c0c0c52 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -15,18 +15,25 @@ use super::{ transcript_protocol::TranscriptProtocol, ProvingScheme, MAX_GENERATORS, }; +/// Represent the `ProvingScheme` based on the Bulletproof protocol struct BulletproofPS { gens: Vec, } +/// Represent the commitment type Node = (Polynomial, CompressedRistretto); +/// Represent the info related to the IPA such as; +/// - the proof +/// - the vector commitment of of a and b vectors +/// - the value of the inner product struct InnerProduct { proof: InnerProductProof, com: CompressedRistretto, value: Scalar, } +// TODO: handle errors // TODO: manage generators more efficiently impl ProvingScheme for BulletproofPS { @@ -44,10 +51,9 @@ impl ProvingScheme for BulletproofPS { fn compute_commitment(&self, bytes: &[[u8; 32]]) -> Node { let points = compute_points(bytes); - // Lagrange interpolation + let polynomial = Polynomial::lagrange(&points); - // Commit let commitment = RistrettoPoint::multiscalar_mul( &polynomial.0, &self.gens[..points.len()], @@ -96,8 +102,6 @@ impl ProvingScheme for BulletproofPS { InnerProduct { proof, com, value } } - // TODO: ErrorHandling - // TODO: correct to share generators? fn verify( &self, InnerProduct { proof, com, value }: &InnerProduct, @@ -111,7 +115,6 @@ impl ProvingScheme for BulletproofPS { let vec_one = vec![Scalar::one(); n]; - // TODO: handle error let com = com.decompress().unwrap(); let p = com + (q * value); @@ -179,7 +182,6 @@ fn split_gens( } fn compute_b_vec(length: usize, position: u64) -> Vec { - // TODO: handle types error let length: u32 = length.try_into().unwrap(); (0..length) .map(|index| Scalar::from(position.pow(index))) diff --git a/src/proving_schemes/mod.rs b/src/proving_schemes/mod.rs index 6014bae..f055841 100644 --- a/src/proving_schemes/mod.rs +++ b/src/proving_schemes/mod.rs @@ -8,13 +8,13 @@ pub trait ProvingScheme { type Commit; type Proof; - // Instantiate the `ProvingScheme` instantiating a starting amount of generators + // Return a `ProvingScheme` by instantiating a starting amount of generators fn instantiate_generators() -> Self; // Increase the generators' quantity fn add_new_generator(&self); - /// Generate a polynomial and its commitment from slice of points + /// Generate a polynomial and its commitment from slice of bytes fn compute_commitment(&self, bytes: &[[u8; 32]]) -> Self::Commit; /// Convert a compressed commitment in a byte array From a4e0227522a74377b479519cdfeabd19a9722e44 Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Tue, 18 Jul 2023 14:54:07 +0200 Subject: [PATCH 20/28] feat: implement increasing of gens --- src/proving_schemes/bulletproof/mod.rs | 14 +++++++++----- src/proving_schemes/mod.rs | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index c0c0c52..355aef7 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -42,10 +42,8 @@ impl ProvingScheme for BulletproofPS { type Proof = InnerProduct; fn instantiate_generators() -> BulletproofPS { - let padded_length = (MAX_GENERATORS * 2).next_power_of_two(); - let bp_gens = BulletproofGens::new(padded_length, 1); BulletproofPS { - gens: bp_gens.share(0).G(padded_length).cloned().collect(), + gens: create_gens(MAX_GENERATORS * 2), } } @@ -139,11 +137,17 @@ impl ProvingScheme for BulletproofPS { com.1.to_bytes() } - fn add_new_generator(&self) { - todo!() + fn add_new_generator(&mut self) { + self.gens = create_gens(self.gens.len() + 1); } } +fn create_gens(gens_capacity: usize) -> Vec { + let padded_length = gens_capacity.next_power_of_two(); + let bp_gens = BulletproofGens::new(padded_length, 1); + bp_gens.share(0).G(padded_length).cloned().collect() +} + fn compute_points(bytes: &[[u8; 32]]) -> Vec<(Scalar, Scalar)> { let points: Vec<_> = bytes .iter() diff --git a/src/proving_schemes/mod.rs b/src/proving_schemes/mod.rs index f055841..2ee07bb 100644 --- a/src/proving_schemes/mod.rs +++ b/src/proving_schemes/mod.rs @@ -12,7 +12,7 @@ pub trait ProvingScheme { fn instantiate_generators() -> Self; // Increase the generators' quantity - fn add_new_generator(&self); + fn add_new_generator(&mut self); /// Generate a polynomial and its commitment from slice of bytes fn compute_commitment(&self, bytes: &[[u8; 32]]) -> Self::Commit; From e2f812a0244242af7dda254dd7e491ec996441bb Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Tue, 18 Jul 2023 16:14:39 +0200 Subject: [PATCH 21/28] doc: add todo specifying to choose one of the proving scheme's traits --- src/committer.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/committer.rs b/src/committer.rs index 0fa0de6..70c1e17 100644 --- a/src/committer.rs +++ b/src/committer.rs @@ -1 +1,2 @@ +// TODO: keep this or the proving scheme one pub trait Committer {} From cbc7770de05572c0c9ee01c2434202c98da2b8a9 Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Tue, 18 Jul 2023 17:22:43 +0200 Subject: [PATCH 22/28] fix: split parameters and use custom type for `Poiint` --- src/proving_schemes/bulletproof/mod.rs | 45 ++++++++++++++++---------- src/proving_schemes/mod.rs | 10 ++++-- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index 355aef7..9850dae 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -23,6 +23,8 @@ struct BulletproofPS { /// Represent the commitment type Node = (Polynomial, CompressedRistretto); +type ScalarPolynomialPoint = (Scalar, Scalar); + /// Represent the info related to the IPA such as; /// - the proof /// - the vector commitment of of a and b vectors @@ -40,6 +42,7 @@ impl ProvingScheme for BulletproofPS { type Scalar = Scalar; type Commit = Node; type Proof = InnerProduct; + type PolynomialPoint = (u128, [u8; 32]); fn instantiate_generators() -> BulletproofPS { BulletproofPS { @@ -48,7 +51,7 @@ impl ProvingScheme for BulletproofPS { } fn compute_commitment(&self, bytes: &[[u8; 32]]) -> Node { - let points = compute_points(bytes); + let points = compute_scalar_polynomial_points(bytes); let polynomial = Polynomial::lagrange(&points); @@ -64,7 +67,7 @@ impl ProvingScheme for BulletproofPS { fn prove( &self, (polynomial, _): &Node, - point: &(u64, [u8; 32]), + polynomial_point: &Self::PolynomialPoint, ) -> InnerProduct { let mut transcript = Transcript::new(b"InnerProductNode"); let q = (transcript.challenge_scalar(b"w")) * RISTRETTO_BASEPOINT_POINT; @@ -76,7 +79,7 @@ impl ProvingScheme for BulletproofPS { let (g_vec, h_vec) = split_gens(&self.gens[..(n * 2)]); let a_vec = &polynomial.0; - let b_vec = compute_b_vec(n, point.0); + let b_vec = compute_b_vec(n, polynomial_point.0); let com = RistrettoPoint::multiscalar_mul( a_vec.iter().chain(&b_vec), @@ -104,7 +107,7 @@ impl ProvingScheme for BulletproofPS { &self, InnerProduct { proof, com, value }: &InnerProduct, children_count: usize, - _point: &(u64, [u8; 32]), + polynomial_point: &Self::PolynomialPoint, ) -> bool { let mut transcript = Transcript::new(b"InnerProductNode"); let q = (transcript.challenge_scalar(b"w")) * RISTRETTO_BASEPOINT_POINT; @@ -148,31 +151,39 @@ fn create_gens(gens_capacity: usize) -> Vec { bp_gens.share(0).G(padded_length).cloned().collect() } -fn compute_points(bytes: &[[u8; 32]]) -> Vec<(Scalar, Scalar)> { - let points: Vec<_> = bytes +fn compute_scalar_polynomial_points( + bytes: &[[u8; 32]], +) -> Vec { + let scalar_polynomial_points: Vec<_> = bytes .iter() .enumerate() - .map(|(index, &byte)| point_into_scalar_point(&(index as u64, byte))) + .map(|(index, &byte)| { + create_scalar_polynomial_point(index as u128, byte) + }) .collect(); - padding_points(&points) + padding_scalar_polynomial_points(&scalar_polynomial_points) } -fn point_into_scalar_point( - &(position, evaluation): &(u64, [u8; 32]), -) -> (Scalar, Scalar) { +fn create_scalar_polynomial_point( + position: u128, + evaluation: [u8; 32], +) -> ScalarPolynomialPoint { ( Scalar::from(position), Scalar::from_bytes_mod_order(evaluation), ) } -fn padding_points(points: &[(Scalar, Scalar)]) -> Vec<(Scalar, Scalar)> { - points +fn padding_scalar_polynomial_points( + scalar_polynomial_points: &[ScalarPolynomialPoint], +) -> Vec { + scalar_polynomial_points .iter() .copied() .chain( - (points.len()..points.len().next_power_of_two()) + (scalar_polynomial_points.len() + ..scalar_polynomial_points.len().next_power_of_two()) .map(|index| (Scalar::from(index as u64), Scalar::zero())), ) .collect() @@ -185,7 +196,7 @@ fn split_gens( (g_vec.to_vec(), h_vec.to_vec()) } -fn compute_b_vec(length: usize, position: u64) -> Vec { +fn compute_b_vec(length: usize, position: u128) -> Vec { let length: u32 = length.try_into().unwrap(); (0..length) .map(|index| Scalar::from(position.pow(index))) @@ -200,7 +211,7 @@ mod test { bulletproof::polynomial::Polynomial, ProvingScheme, }; - use super::BulletproofPS; + use super::{BulletproofPS, ScalarPolynomialPoint}; fn generate_bytes() -> Vec<[u8; 32]> { vec![ @@ -225,7 +236,7 @@ mod test { ] } - fn generate_positions() -> Vec<(Scalar, Scalar)> { + fn generate_positions() -> Vec { generate_bytes() .iter() .enumerate() diff --git a/src/proving_schemes/mod.rs b/src/proving_schemes/mod.rs index 2ee07bb..c435e01 100644 --- a/src/proving_schemes/mod.rs +++ b/src/proving_schemes/mod.rs @@ -7,6 +7,7 @@ pub trait ProvingScheme { type Scalar; type Commit; type Proof; + type PolynomialPoint; // Return a `ProvingScheme` by instantiating a starting amount of generators fn instantiate_generators() -> Self; @@ -21,14 +22,17 @@ pub trait ProvingScheme { fn commitment_to_bytes(com: Self::Commit) -> [u8; 32]; /// Compute the proof that the point (position, evaluation) is a node's child - fn prove(&self, com: &Self::Commit, point: &(u64, [u8; 32])) - -> Self::Proof; + fn prove( + &self, + com: &Self::Commit, + polynomial_point: &Self::PolynomialPoint, + ) -> Self::Proof; /// Verify that points the point (position, evaluation) is a node's child fn verify( &self, proof: &Self::Proof, children_count: usize, - point: &(u64, [u8; 32]), + polynomial_point: &Self::PolynomialPoint, ) -> bool; } From 5ef0f7aae5ef049b91dfba7f9c6405b857b830de Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Tue, 18 Jul 2023 17:24:18 +0200 Subject: [PATCH 23/28] fix: return couple of references to save memory --- src/proving_schemes/bulletproof/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index 9850dae..50da6fd 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -191,9 +191,9 @@ fn padding_scalar_polynomial_points( fn split_gens( gens: &[RistrettoPoint], -) -> (Vec, Vec) { +) -> (&[RistrettoPoint], &[RistrettoPoint]) { let (g_vec, h_vec) = gens.split_at((gens.len() + 1) / 2); - (g_vec.to_vec(), h_vec.to_vec()) + (g_vec, h_vec) } fn compute_b_vec(length: usize, position: u128) -> Vec { From f2802f1de9862c8297ba748a9f25b2e03dc3e8be Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Tue, 18 Jul 2023 17:30:41 +0200 Subject: [PATCH 24/28] fix: use polynomial_point in verifier --- src/proving_schemes/bulletproof/mod.rs | 28 ++++++++++++-------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index 50da6fd..388e13f 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -27,7 +27,7 @@ type ScalarPolynomialPoint = (Scalar, Scalar); /// Represent the info related to the IPA such as; /// - the proof -/// - the vector commitment of of a and b vectors +/// - the vector commitment of the vector a /// - the value of the inner product struct InnerProduct { proof: InnerProductProof, @@ -81,11 +81,8 @@ impl ProvingScheme for BulletproofPS { let a_vec = &polynomial.0; let b_vec = compute_b_vec(n, polynomial_point.0); - let com = RistrettoPoint::multiscalar_mul( - a_vec.iter().chain(&b_vec), - g_vec[..n].iter().chain(&h_vec[..n]), - ) - .compress(); + let com = + RistrettoPoint::multiscalar_mul(a_vec, &g_vec[..n]).compress(); let value = inner_product(a_vec, &b_vec); @@ -114,24 +111,25 @@ impl ProvingScheme for BulletproofPS { let n = children_count.next_power_of_two(); - let vec_one = vec![Scalar::one(); n]; - - let com = com.decompress().unwrap(); - - let p = com + (q * value); + let g_h_factors = vec![Scalar::one(); n]; let (g_vec, h_vec) = split_gens(&self.gens[..(n * 2)]); + let a_com = com.decompress().unwrap(); + let b_vec = compute_b_vec(n, polynomial_point.0); + let b_com = RistrettoPoint::multiscalar_mul(b_vec, &h_vec[..n]); + let p = a_com + b_com + (q * value); + proof .verify( n, &mut transcript, - &vec_one, - &vec_one, + &g_h_factors, + &g_h_factors, &p, &q, - &g_vec, - &h_vec, + g_vec, + h_vec, ) .is_ok() } From c78399bd56963b681b6e801fb7cfda800a36d83a Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Wed, 19 Jul 2023 09:57:35 +0200 Subject: [PATCH 25/28] refactor: change `compute_commitment` parameters intto `Scalar` --- src/proving_schemes/bulletproof/mod.rs | 44 +++++++++++--------------- src/proving_schemes/mod.rs | 2 +- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index 388e13f..1afa672 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -50,8 +50,12 @@ impl ProvingScheme for BulletproofPS { } } - fn compute_commitment(&self, bytes: &[[u8; 32]]) -> Node { - let points = compute_scalar_polynomial_points(bytes); + fn add_new_generator(&mut self) { + self.gens = create_gens(self.gens.len() + 1); + } + + fn compute_commitment(&self, children: &[Scalar]) -> Node { + let points = compute_scalar_polynomial_points(children); let polynomial = Polynomial::lagrange(&points); @@ -64,6 +68,10 @@ impl ProvingScheme for BulletproofPS { (polynomial, commitment) } + fn commitment_to_bytes(com: Node) -> [u8; 32] { + com.1.to_bytes() + } + fn prove( &self, (polynomial, _): &Node, @@ -133,14 +141,6 @@ impl ProvingScheme for BulletproofPS { ) .is_ok() } - - fn commitment_to_bytes(com: Node) -> [u8; 32] { - com.1.to_bytes() - } - - fn add_new_generator(&mut self) { - self.gens = create_gens(self.gens.len() + 1); - } } fn create_gens(gens_capacity: usize) -> Vec { @@ -150,29 +150,19 @@ fn create_gens(gens_capacity: usize) -> Vec { } fn compute_scalar_polynomial_points( - bytes: &[[u8; 32]], + polynomial_evaluations: &[Scalar], ) -> Vec { - let scalar_polynomial_points: Vec<_> = bytes + let scalar_polynomial_points: Vec<_> = polynomial_evaluations .iter() .enumerate() - .map(|(index, &byte)| { - create_scalar_polynomial_point(index as u128, byte) + .map(|(position, &polynomial_evaluation)| { + (Scalar::from(position as u64), polynomial_evaluation) }) .collect(); padding_scalar_polynomial_points(&scalar_polynomial_points) } -fn create_scalar_polynomial_point( - position: u128, - evaluation: [u8; 32], -) -> ScalarPolynomialPoint { - ( - Scalar::from(position), - Scalar::from_bytes_mod_order(evaluation), - ) -} - fn padding_scalar_polynomial_points( scalar_polynomial_points: &[ScalarPolynomialPoint], ) -> Vec { @@ -262,8 +252,12 @@ mod test { fn correct_prove_verification() { let scheme = BulletproofPS::instantiate_generators(); let bytes: Vec<[u8; 32]> = generate_bytes(); + let scalars: Vec<_> = bytes + .iter() + .map(|byte| Scalar::from_bytes_mod_order(*byte)) + .collect(); - let node = scheme.compute_commitment(&bytes); + let node = scheme.compute_commitment(&scalars); let proof = scheme.prove(&node, &(1, bytes[1])); diff --git a/src/proving_schemes/mod.rs b/src/proving_schemes/mod.rs index c435e01..1ef91bc 100644 --- a/src/proving_schemes/mod.rs +++ b/src/proving_schemes/mod.rs @@ -16,7 +16,7 @@ pub trait ProvingScheme { fn add_new_generator(&mut self); /// Generate a polynomial and its commitment from slice of bytes - fn compute_commitment(&self, bytes: &[[u8; 32]]) -> Self::Commit; + fn compute_commitment(&self, children: &[Self::Scalar]) -> Self::Commit; /// Convert a compressed commitment in a byte array fn commitment_to_bytes(com: Self::Commit) -> [u8; 32]; From 249f354d42e1933ebf4a85c63388e9d2796eaa27 Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Wed, 19 Jul 2023 10:09:09 +0200 Subject: [PATCH 26/28] feat: implement type conversion methods --- Cargo.toml | 1 + src/proving_schemes/bulletproof/mod.rs | 9 +++++++++ src/proving_schemes/mod.rs | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index a6c789c..dcd65fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ ark-ec = { version = "^0.3.0", default-features = false } num-bigint = "0.4.3" curve25519-dalek = {version = "3.2.0", features = ["serde"] } merlin = { version = "3", default-features = false } +sha2 = {version = "0.9", default-features = false} [dependencies.bulletproofs] git = "https://github.com/runtime-machines/bulletproofs.git" diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index 1afa672..b2f2182 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -8,6 +8,7 @@ use curve25519_dalek::{ traits::MultiscalarMul, }; use merlin::Transcript; +use sha2::Sha512; use self::polynomial::Polynomial; @@ -68,6 +69,14 @@ impl ProvingScheme for BulletproofPS { (polynomial, commitment) } + fn from_bytes_to_scalar(bytes: &[u8]) -> Self::Scalar { + Scalar::hash_from_bytes::(bytes) + } + + fn from_commitment_to_scalar(com: &Self::Commit) -> Self::Scalar { + Scalar::hash_from_bytes::(com.1.as_bytes()) + } + fn commitment_to_bytes(com: Node) -> [u8; 32] { com.1.to_bytes() } diff --git a/src/proving_schemes/mod.rs b/src/proving_schemes/mod.rs index 1ef91bc..94fbb3d 100644 --- a/src/proving_schemes/mod.rs +++ b/src/proving_schemes/mod.rs @@ -18,6 +18,12 @@ pub trait ProvingScheme { /// Generate a polynomial and its commitment from slice of bytes fn compute_commitment(&self, children: &[Self::Scalar]) -> Self::Commit; + /// Convert a slice of bytes into a scalar (field element) + fn from_bytes_to_scalar(input: &[u8]) -> Self::Scalar; + + /// Convert a point (group element) into a scalar (field element) + fn from_commitment_to_scalar(input: &Self::Commit) -> Self::Scalar; + /// Convert a compressed commitment in a byte array fn commitment_to_bytes(com: Self::Commit) -> [u8; 32]; From 1cf53bbbf0b97b49fd6d4f1b07a316a5d8e6af22 Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Wed, 19 Jul 2023 10:11:31 +0200 Subject: [PATCH 27/28] test: add wrong verification --- src/proving_schemes/bulletproof/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index b2f2182..3b6ac42 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -271,5 +271,7 @@ mod test { let proof = scheme.prove(&node, &(1, bytes[1])); assert!(scheme.verify(&proof, bytes.len(), &(1, bytes[1]))); + + assert!(!scheme.verify(&proof, bytes.len(), &(0, bytes[1]))); } } From 3ac0f2427f48223708e88eb77570013870e286b5 Mon Sep 17 00:00:00 2001 From: erikareale <13k.erika@gmail.com> Date: Wed, 19 Jul 2023 12:33:55 +0200 Subject: [PATCH 28/28] refactor: make directly usage of commitment and remove bytes --- src/proving_schemes/bulletproof/mod.rs | 103 ++++++++++++------------- src/proving_schemes/mod.rs | 22 +++--- 2 files changed, 62 insertions(+), 63 deletions(-) diff --git a/src/proving_schemes/bulletproof/mod.rs b/src/proving_schemes/bulletproof/mod.rs index 3b6ac42..5762fdb 100644 --- a/src/proving_schemes/bulletproof/mod.rs +++ b/src/proving_schemes/bulletproof/mod.rs @@ -1,6 +1,6 @@ mod polynomial; -use bulletproofs::{inner_product, BulletproofGens, InnerProductProof}; +use bulletproofs::{BulletproofGens, InnerProductProof}; use curve25519_dalek::{ constants::RISTRETTO_BASEPOINT_POINT, ristretto::{CompressedRistretto, RistrettoPoint}, @@ -21,29 +21,15 @@ struct BulletproofPS { gens: Vec, } -/// Represent the commitment -type Node = (Polynomial, CompressedRistretto); - type ScalarPolynomialPoint = (Scalar, Scalar); -/// Represent the info related to the IPA such as; -/// - the proof -/// - the vector commitment of the vector a -/// - the value of the inner product -struct InnerProduct { - proof: InnerProductProof, - com: CompressedRistretto, - value: Scalar, -} - // TODO: handle errors // TODO: manage generators more efficiently impl ProvingScheme for BulletproofPS { type Scalar = Scalar; - type Commit = Node; - type Proof = InnerProduct; - type PolynomialPoint = (u128, [u8; 32]); + type Commit = CompressedRistretto; + type Proof = InnerProductProof; fn instantiate_generators() -> BulletproofPS { BulletproofPS { @@ -55,18 +41,21 @@ impl ProvingScheme for BulletproofPS { self.gens = create_gens(self.gens.len() + 1); } - fn compute_commitment(&self, children: &[Scalar]) -> Node { + fn compute_commitment( + &self, + children: &[Scalar], + ) -> (Vec, CompressedRistretto) { let points = compute_scalar_polynomial_points(children); - let polynomial = Polynomial::lagrange(&points); + let polynomial_coefficients = Polynomial::lagrange(&points).0; let commitment = RistrettoPoint::multiscalar_mul( - &polynomial.0, + &polynomial_coefficients, &self.gens[..points.len()], ) .compress(); - (polynomial, commitment) + (polynomial_coefficients, commitment) } fn from_bytes_to_scalar(bytes: &[u8]) -> Self::Scalar { @@ -74,56 +63,47 @@ impl ProvingScheme for BulletproofPS { } fn from_commitment_to_scalar(com: &Self::Commit) -> Self::Scalar { - Scalar::hash_from_bytes::(com.1.as_bytes()) - } - - fn commitment_to_bytes(com: Node) -> [u8; 32] { - com.1.to_bytes() + Scalar::hash_from_bytes::(com.as_bytes()) } fn prove( &self, - (polynomial, _): &Node, - polynomial_point: &Self::PolynomialPoint, - ) -> InnerProduct { - let mut transcript = Transcript::new(b"InnerProductNode"); + polynomial_coefficients: &[Scalar], + position: u128, + _evaluation: Self::Scalar, + ) -> InnerProductProof { + let mut transcript = Transcript::new(b"InnerProductProofNode"); let q = (transcript.challenge_scalar(b"w")) * RISTRETTO_BASEPOINT_POINT; - let n = polynomial.0.len(); + let n = polynomial_coefficients.len(); let g_h_factors = vec![Scalar::one(); n]; let (g_vec, h_vec) = split_gens(&self.gens[..(n * 2)]); - let a_vec = &polynomial.0; - let b_vec = compute_b_vec(n, polynomial_point.0); - - let com = - RistrettoPoint::multiscalar_mul(a_vec, &g_vec[..n]).compress(); + let b_vec = compute_b_vec(n, position); - let value = inner_product(a_vec, &b_vec); - - let proof = InnerProductProof::create( + InnerProductProof::create( &mut transcript, &q, &g_h_factors, &g_h_factors, g_vec[..n].to_vec(), h_vec[..n].to_vec(), - a_vec.to_vec(), + polynomial_coefficients.to_vec(), b_vec, - ); - - InnerProduct { proof, com, value } + ) } fn verify( &self, - InnerProduct { proof, com, value }: &InnerProduct, + com: &CompressedRistretto, + proof: &InnerProductProof, children_count: usize, - polynomial_point: &Self::PolynomialPoint, + position: u128, + evaluation: Self::Scalar, ) -> bool { - let mut transcript = Transcript::new(b"InnerProductNode"); + let mut transcript = Transcript::new(b"InnerProductProofNode"); let q = (transcript.challenge_scalar(b"w")) * RISTRETTO_BASEPOINT_POINT; let n = children_count.next_power_of_two(); @@ -133,9 +113,9 @@ impl ProvingScheme for BulletproofPS { let (g_vec, h_vec) = split_gens(&self.gens[..(n * 2)]); let a_com = com.decompress().unwrap(); - let b_vec = compute_b_vec(n, polynomial_point.0); + let b_vec = compute_b_vec(n, position); let b_com = RistrettoPoint::multiscalar_mul(b_vec, &h_vec[..n]); - let p = a_com + b_com + (q * value); + let p = a_com + b_com + (q * evaluation); proof .verify( @@ -266,12 +246,29 @@ mod test { .map(|byte| Scalar::from_bytes_mod_order(*byte)) .collect(); - let node = scheme.compute_commitment(&scalars); - - let proof = scheme.prove(&node, &(1, bytes[1])); + let (polynomial_coefficients, com) = + scheme.compute_commitment(&scalars); - assert!(scheme.verify(&proof, bytes.len(), &(1, bytes[1]))); + let proof = scheme.prove( + &polynomial_coefficients, + 1, + Scalar::from_bytes_mod_order(bytes[1]), + ); - assert!(!scheme.verify(&proof, bytes.len(), &(0, bytes[1]))); + assert!(scheme.verify( + &com, + &proof, + bytes.len(), + 1, + Scalar::from_bytes_mod_order(bytes[1]) + )); + + assert!(!scheme.verify( + &com, + &proof, + bytes.len(), + 0, + Scalar::from_bytes_mod_order(bytes[1]) + )); } } diff --git a/src/proving_schemes/mod.rs b/src/proving_schemes/mod.rs index 94fbb3d..b745a8f 100644 --- a/src/proving_schemes/mod.rs +++ b/src/proving_schemes/mod.rs @@ -7,7 +7,6 @@ pub trait ProvingScheme { type Scalar; type Commit; type Proof; - type PolynomialPoint; // Return a `ProvingScheme` by instantiating a starting amount of generators fn instantiate_generators() -> Self; @@ -16,7 +15,10 @@ pub trait ProvingScheme { fn add_new_generator(&mut self); /// Generate a polynomial and its commitment from slice of bytes - fn compute_commitment(&self, children: &[Self::Scalar]) -> Self::Commit; + fn compute_commitment( + &self, + children: &[Self::Scalar], + ) -> (Vec, Self::Commit); /// Convert a slice of bytes into a scalar (field element) fn from_bytes_to_scalar(input: &[u8]) -> Self::Scalar; @@ -24,21 +26,21 @@ pub trait ProvingScheme { /// Convert a point (group element) into a scalar (field element) fn from_commitment_to_scalar(input: &Self::Commit) -> Self::Scalar; - /// Convert a compressed commitment in a byte array - fn commitment_to_bytes(com: Self::Commit) -> [u8; 32]; - - /// Compute the proof that the point (position, evaluation) is a node's child + /// Compute the proof that the evaluation at a given position is a node's child fn prove( &self, - com: &Self::Commit, - polynomial_point: &Self::PolynomialPoint, + polynomial_coefficients: &[Self::Scalar], + position: u128, + evaluation: Self::Scalar, ) -> Self::Proof; - /// Verify that points the point (position, evaluation) is a node's child + /// Verify that points the evaluation at a given position is a node's child fn verify( &self, + commitment: &Self::Commit, proof: &Self::Proof, children_count: usize, - polynomial_point: &Self::PolynomialPoint, + position: u128, + evaluation: Self::Scalar, ) -> bool; }