-
Notifications
You must be signed in to change notification settings - Fork 172
BulletProofs++ #928
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Daeinar
wants to merge
17
commits into
main
Choose a base branch
from
bp3
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+303
−0
Open
BulletProofs++ #928
Changes from 3 commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
3304253
feat: Adds Weight Norm Linear Argument from BulletProofs++
Daeinar de0e441
fix: small comment tweak
Daeinar 63db81f
fix: remove dead code
Daeinar 5dde36e
fix: generic group support for WNLA
Daeinar d861163
fix: make license checker and clippy happy
Daeinar 28a8ace
fix: license checker still complaining...
Daeinar 1f683c8
fix: license checker still complaining...
Daeinar 9215c64
fix: clean up traits, remove extend function and panic instead if vec…
Daeinar 54ef01f
fix: removing some more unneeded trait bounds
Daeinar 410af6d
Simplify util functions
jonas-lj da05203
Use GroupElement trait in wnla
jonas-lj 50fa255
Use default generator
jonas-lj 59888ad
TODO + fmt
jonas-lj fa57d66
Use Fiat-Shamir trait
jonas-lj 909f140
Consume proof on recursion + error handling
jonas-lj 35e2922
refactor
jonas-lj 8ae8746
deafult not needed
jonas-lj File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| # BulletProofs++ (alias BP3) | ||
|
|
||
| Implementation of the [Bulletproofs++](https://eprint.iacr.org/2022/510.pdf) protocol. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| pub mod wnla; | ||
| mod util; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| use std::cmp::max; | ||
| use std::ops::{Add, Mul}; | ||
| use fastcrypto::groups::{GroupElement, ristretto255::RistrettoScalar}; | ||
|
|
||
| // Splits vector `v` into two vectors `v_even` and `v_odd` where `v_even` contains the elements at even indices and | ||
| // `v_odd` contains the elements at odd indices. | ||
| pub fn reduce<T>(v: &[T]) -> (Vec<T>, Vec<T>) | ||
| where | ||
| T: Copy | ||
| { | ||
| let mut v_even = Vec::with_capacity((v.len() + 1) / 2); | ||
| let mut v_odd = Vec::with_capacity(v.len() / 2); | ||
| for (i, val) in v.iter().enumerate() { | ||
| if i % 2 == 0 { v_even.push(*val); } else { v_odd.push(*val); } | ||
| } | ||
| (v_even, v_odd) | ||
| } | ||
|
|
||
| // Extends vector `v` to length `n` by appending default values of type `T` if `v` is shorter than `n`. | ||
| pub fn extend<T>(v: &[T], n: usize) -> Vec<T> | ||
| where | ||
| T: Copy + Default | ||
| { | ||
| let mut v_ext = Vec::with_capacity(n); | ||
| v_ext.extend_from_slice(&v[..v.len().min(n)]); | ||
| v_ext.resize(n, T::default()); | ||
| v_ext | ||
| } | ||
|
|
||
| // Computes the inner product of two vectors `v` and `w`, i.e., result = sum_i (v_i * w_i). | ||
| pub fn inner_product<T>(v: &[T], w: &[RistrettoScalar]) -> T | ||
| where | ||
| T: Copy + Mul<RistrettoScalar, Output = T> + Add<Output = T> + Default, | ||
| { | ||
| let mut result = T::default(); // ZERO | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In case T is |
||
| let v_ext = extend(v, max(v.len(), w.len())); | ||
| let w_ext = &extend(w, max(v.len(), w.len())); | ||
|
jonas-lj marked this conversation as resolved.
Outdated
|
||
| v_ext.iter().zip(w_ext).for_each(|(v_val, w_val)| { | ||
| result = result.add(v_val.mul(*w_val)); | ||
| }); | ||
| result | ||
| } | ||
|
|
||
| // Computes the weighted inner product of two vectors `v` and `w` with a given `weight`, i.e., result = sum_i (v_i * w_i * weight^i). | ||
| pub fn weighted_inner_product<T>(v: &[T], w: &[RistrettoScalar], weight: &RistrettoScalar) -> T | ||
| where | ||
| T: Copy + Mul<RistrettoScalar, Output = T> + Add<Output = T> + Default, | ||
| { | ||
| let mut exp = RistrettoScalar::generator(); // ONE | ||
| let mut result = T::default(); // ZERO | ||
| let v_ext = extend(v, max(v.len(), w.len())); | ||
|
jonas-lj marked this conversation as resolved.
Outdated
|
||
| let w_ext = &extend(w, max(v.len(), w.len())); | ||
| v_ext.iter().zip(w_ext).for_each(|(v_val, w_val)| { | ||
| exp = exp.mul(weight); | ||
|
jonas-lj marked this conversation as resolved.
Outdated
|
||
| result = result.add(v_val.mul(w_val.mul(&exp))); | ||
| }); | ||
| result | ||
| } | ||
|
|
||
| // Scales each element of vector `v` by a given `scalar`, i.e., result[i] = v[i] * scalar. | ||
| pub fn scale<'a, T>(v: &[T], scalar: &'a RistrettoScalar) -> Vec<T> | ||
| where | ||
| T: Copy + Mul<&'a RistrettoScalar, Output = T> | ||
| { | ||
| v.iter().map(|x| x.mul(scalar)).collect() | ||
| } | ||
|
|
||
| // Adds two vectors `v` and `w` element-wise, i.e., result[i] = v[i] + w[i]. | ||
| pub fn add<T>(v: &[T], w: &[T]) -> Vec<T> | ||
| where | ||
| T: Copy + Add<Output = T> + Default | ||
| { | ||
| // let mut result = Vec::with_capacity(max(v.len(), w.len())); | ||
| let v_ext = extend(v, max(v.len(), w.len())); | ||
| let w_ext = &extend(w, max(v.len(), w.len())); | ||
| v_ext.iter().zip(w_ext).map(|(v_val, w_val)| v_val.add(*w_val)).collect::<Vec<T>>() | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,217 @@ | ||
| #![allow(non_snake_case)] | ||
|
|
||
| use std::ops::{Add, Mul, Sub}; | ||
| use fastcrypto::groups::ristretto255::*; | ||
| use fastcrypto::groups::{GroupElement, Scalar}; | ||
| use fastcrypto::serde_helpers::ToFromByteArray; | ||
| use merlin::Transcript; | ||
| use crate::bp3::util::*; | ||
|
|
||
| pub struct WeightNormLinearArgument { | ||
|
jonas-lj marked this conversation as resolved.
Outdated
|
||
| pub Gen: RistrettoPoint, | ||
| pub G: Vec<RistrettoPoint>, | ||
| pub H: Vec<RistrettoPoint>, | ||
| pub c: Vec<RistrettoScalar>, | ||
| pub rho: RistrettoScalar, | ||
| pub mu: RistrettoScalar, | ||
| } | ||
|
|
||
| #[derive(Clone, Debug)] | ||
| pub struct Proof { | ||
| pub R: Vec<RistrettoPoint>, | ||
| pub X: Vec<RistrettoPoint>, | ||
| pub l: Vec<RistrettoScalar>, | ||
| pub n: Vec<RistrettoScalar>, | ||
| } | ||
|
|
||
| impl WeightNormLinearArgument { | ||
|
|
||
| /// Computes a weight norm linear argument commitment `C` for vectors `l` and `n`: | ||
| /// `C = v * Gen + <H, l> + <G, n> with v = <c, l> + <n, n>_mu`. | ||
| pub fn commit(&self, l: &[RistrettoScalar], n: &[RistrettoScalar]) -> RistrettoPoint { | ||
| let v = inner_product(&self.c, l). | ||
| add(weighted_inner_product(n, n, &self.mu)); | ||
| self.Gen.mul(v). | ||
| add(&inner_product(&self.H, l)). | ||
| add(&inner_product(&self.G, n)) | ||
| } | ||
|
|
||
|
|
||
| /// Verifies a weight norm linear argument proof. | ||
| /// TODO: This is the "naive" (recursive) verification. We can optimize it as described on page 14 of the paper. | ||
| pub fn verify(&self, C: &RistrettoPoint, t: &mut Transcript, proof: &Proof) -> bool { | ||
| if proof.X.len() != proof.R.len() { | ||
| return false; | ||
| } | ||
|
|
||
| if proof.X.is_empty() { | ||
| return C.eq(&self.commit(&proof.l, &proof.n)); | ||
| } | ||
|
|
||
| let (c0, c1) = reduce(&self.c); | ||
| let (G0, G1) = reduce(&self.G); | ||
| let (H0, H1) = reduce(&self.H); | ||
|
|
||
| // Add messages to Fiat Shamir transcript | ||
| t.append_message(b"wnla:C", &C.to_byte_array()); | ||
| t.append_message(b"wnla:X", &proof.X.last().unwrap().to_byte_array()); | ||
| t.append_message(b"wnla:R", &proof.R.last().unwrap().to_byte_array()); | ||
| t.append_u64(b"wlna:l.len", self.H.len() as u64); | ||
| t.append_u64(b"wlna:n.len", self.G.len() as u64); | ||
|
|
||
| // Compute Fiat Shamir challenge gamma | ||
| let mut buf = [0u8; 64]; | ||
| t.challenge_bytes(b"wnla:gamma", &mut buf); | ||
| let gamma = RistrettoScalar::from_bytes_mod_order_wide(&buf); | ||
|
|
||
| // G' = rho * G0 + gamma * G1 | ||
| let Gp = add(&scale(&G0, &self.rho), &scale(&G1, &gamma)); | ||
| // H' = H0 + gamma * H1 | ||
| let Hp = add(&H0, &scale(&H1, &gamma)); | ||
| // c' = c0 + gamma * c1 | ||
| let cp = add(&c0, &scale(&c1, &gamma)); | ||
| // C' = C + gamma * X + (gamma^2 - 1) * R | ||
| let Cp = C. | ||
| add(&proof.X.last().unwrap().mul(&gamma)). | ||
| add(&proof.R.last().unwrap().mul(&gamma.mul(&gamma).sub(&RistrettoScalar::generator()))); | ||
|
|
||
| let wnla = WeightNormLinearArgument { | ||
| Gen: self.Gen, | ||
| G: Gp, | ||
| H: Hp, | ||
| c: cp, | ||
| rho: self.mu, | ||
| mu: self.mu.mul(&self.mu) | ||
| }; | ||
|
|
||
| let proofp = Proof { | ||
| R: proof.R[..proof.R.len() - 1].to_vec(), | ||
| X: proof.X[..proof.X.len() - 1].to_vec(), | ||
| l: proof.l.clone(), | ||
| n: proof.n.clone(), | ||
| }; | ||
|
|
||
| wnla.verify(&Cp, t, &proofp) | ||
| } | ||
|
|
||
| /// Creates a weight norm linear argument proof. | ||
| pub fn prove(&self, C: &RistrettoPoint, t: &mut Transcript, l: Vec<RistrettoScalar>, n: Vec<RistrettoScalar>) -> Proof { | ||
| if l.len() + n.len() < 6 { | ||
| return Proof { | ||
| R: vec![], | ||
| X: vec![], | ||
| l: l, | ||
| n: n, | ||
| }; | ||
| } | ||
|
|
||
| let rho_inv = self.rho.inverse().unwrap(); | ||
|
|
||
| let (c0, c1) = reduce(&self.c); | ||
| let (l0, l1) = reduce(&l); | ||
| let (n0, n1) = reduce(&n); | ||
| let (G0, G1) = reduce(&self.G); | ||
| let (H0, H1) = reduce(&self.H); | ||
|
|
||
| let mu_squared = self.mu.mul(&self.mu); | ||
|
|
||
| // v_x = 2 * rho_inv * <n0, n1>_{mu_squared} + <c0, l1> + <c1, l0> | ||
| let vx = weighted_inner_product(&n0, &n1, &mu_squared). | ||
| mul(&rho_inv.mul(&RistrettoScalar::from(2u64))). | ||
| add(&inner_product(&c0, &l1)). | ||
| add(&inner_product(&c1, &l0)); | ||
|
|
||
| // v_r = <n1, n1>_{mu_squared} + <c1, l1> | ||
| let vr = weighted_inner_product(&n1, &n1, &mu_squared). | ||
| add(&inner_product(&c1, &l1)); | ||
|
|
||
| // X = v_x * Gen + <H0, l1> + <H1, l0> + <G0, rho * n1> + <G1, rho_inv * n0> | ||
| let X = self.Gen.mul(vx). | ||
| add(&inner_product(&H0, &l1)). | ||
| add(&inner_product(&H1, &l0)). | ||
| add(&inner_product(&G0, &scale(&n1, &self.rho))). | ||
| add(&inner_product(&G1, &scale(&n0, &rho_inv))); | ||
|
jonas-lj marked this conversation as resolved.
Outdated
|
||
|
|
||
| // R = v_r * Gen + <H1, l1> + <G1, n1> | ||
| let R = self.Gen.mul(vr). | ||
| add(&inner_product(&H1, &l1)). | ||
| add(&inner_product(&G1, &n1)); | ||
|
jonas-lj marked this conversation as resolved.
Outdated
|
||
|
|
||
| // Add messages to Fiat Shamir transcript | ||
| t.append_message(b"wnla:C", &C.to_byte_array()); | ||
| t.append_message(b"wnla:X", &X.to_byte_array()); | ||
| t.append_message(b"wnla:R", &R.to_byte_array()); | ||
| t.append_u64(b"wlna:l.len", l.len() as u64); | ||
| t.append_u64(b"wlna:n.len", n.len() as u64); | ||
|
|
||
| // Compute Fiat Shamir challenge gamma | ||
| let mut buf = [0u8; 64]; | ||
| t.challenge_bytes(b"wnla:gamma", &mut buf); | ||
| let gamma = RistrettoScalar::from_bytes_mod_order_wide(&buf); | ||
|
|
||
| // H' = H0 + gamma * H1 | ||
| let Hp = add(&H0, &scale(&H1, &gamma)); | ||
| // G' = rho * G0 + gamma * G1 | ||
| let Gp = add(&scale(&G0, &self.rho), &scale(&G1, &gamma)); | ||
| // c' = c0 + gamma * c1 | ||
| let cp = add(&c0, &scale(&c1, &gamma)); | ||
| // l' = l0 + gamma * l1 | ||
| let lp = add(&l0, &scale(&l1, &gamma)); | ||
| // n' = rho_inv * n0 + gamma * n1 | ||
| let np = add(&scale(&n0, &rho_inv), &scale(&n1, &gamma)); | ||
|
|
||
| let wnla = WeightNormLinearArgument { | ||
| Gen: self.Gen, | ||
| G: Gp, | ||
| H: Hp, | ||
| c: cp, | ||
| rho: self.mu, | ||
| mu: mu_squared | ||
| }; | ||
|
|
||
| let mut proof = wnla.prove(&wnla.commit(&lp, &np), t, lp, np); | ||
| proof.R.push(R); | ||
| proof.X.push(X); | ||
| proof | ||
| } | ||
|
|
||
| } | ||
|
|
||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use crate::bp3::wnla::*; | ||
| use ark_std::rand::thread_rng; | ||
|
|
||
| #[test] | ||
| fn test_weight_norm_linear_argument() { | ||
| const N: usize = 4; | ||
| let mut rand = thread_rng(); | ||
|
|
||
| let Gen = RistrettoPoint::generator(); | ||
| let G = (0..N).map(|_| RistrettoPoint::random(&mut rand)).collect::<Vec<_>>(); | ||
| let H = (0..N).map(|_| RistrettoPoint::random(&mut rand)).collect::<Vec<_>>(); | ||
| let c = (0..N).map(|_| RistrettoScalar::rand(&mut rand)).collect::<Vec<_>>(); | ||
| let rho = RistrettoScalar::rand(&mut rand); | ||
|
|
||
| let wnla = WeightNormLinearArgument{ | ||
| Gen: Gen, | ||
| G: G, | ||
| H: H, | ||
| c: c, | ||
| rho: rho, | ||
| mu: rho.mul(&rho) | ||
| }; | ||
|
|
||
| let l = (0..N).map(|_| RistrettoScalar::rand(&mut rand)).collect::<Vec<_>>(); | ||
| let n = (0..N).map(|_| RistrettoScalar::rand(&mut rand)).collect::<Vec<_>>(); | ||
|
|
||
| let C = wnla.commit(&l, &n); | ||
|
|
||
| let mut pt = merlin::Transcript::new(b"wnla test"); | ||
| let proof = wnla.prove(&C, &mut pt, l, n); | ||
| let mut vt = merlin::Transcript::new(b"wnla test"); | ||
| assert!(wnla.verify(&C, &mut vt, &proof)); | ||
|
|
||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.