Skip to content

Commit 00daecc

Browse files
committed
Add range proof protocols
Signed-off-by: lovesh <lovesh.bond@gmail.com>
1 parent cb6ba81 commit 00daecc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+9346
-459
lines changed

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ members = [
1515
"secret_sharing_and_dkg",
1616
"legogroth16",
1717
"oblivious_transfer",
18+
"kvac",
1819
"merlin",
19-
"kvac"
20+
"bulletproofs_plus_plus",
21+
"smc_range_proof"
2022
]
2123
resolver = "2"
2224

bbs_plus/Cargo.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "bbs_plus"
3-
version = "0.16.0"
3+
version = "0.17.0"
44
edition.workspace = true
55
authors.workspace = true
66
license.workspace = true
@@ -19,10 +19,10 @@ ark-std.workspace = true
1919
digest.workspace = true
2020
rayon = {workspace = true, optional = true}
2121
itertools.workspace = true
22-
schnorr_pok = { version = "0.14.0", default-features = false, path = "../schnorr_pok" }
23-
dock_crypto_utils = { version = "0.15.0", default-features = false, path = "../utils" }
24-
oblivious_transfer_protocols = { version = "0.3.0", default-features = false, path = "../oblivious_transfer" }
25-
secret_sharing_and_dkg = { version = "0.7.0", default-features = false, path = "../secret_sharing_and_dkg" }
22+
schnorr_pok = { version = "0.15.0", default-features = false, path = "../schnorr_pok" }
23+
dock_crypto_utils = { version = "0.16.0", default-features = false, path = "../utils" }
24+
oblivious_transfer_protocols = { version = "0.4.0", default-features = false, path = "../oblivious_transfer" }
25+
secret_sharing_and_dkg = { version = "0.8.0", default-features = false, path = "../secret_sharing_and_dkg" }
2626
sha3 = { version = "0.10.6", default-features = false }
2727
serde.workspace = true
2828
serde_with.workspace = true

benches/Cargo.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ authors.workspace = true
66
license.workspace = true
77

88
[dependencies]
9-
bbs_plus = { version = "0.16.0", default-features = false, path = "../bbs_plus" }
10-
schnorr_pok = { version = "0.14.0", default-features = false, path = "../schnorr_pok" }
11-
vb_accumulator = { version = "0.17.0", default-features = false, path = "../vb_accumulator" }
9+
bbs_plus = { version = "0.17.0", default-features = false, path = "../bbs_plus" }
10+
schnorr_pok = { version = "0.15.0", default-features = false, path = "../schnorr_pok" }
11+
vb_accumulator = { version = "0.18.0", default-features = false, path = "../vb_accumulator" }
1212
test_utils = { default-features = false, path = "../test_utils" }
1313
ark-ff.workspace = true
1414
ark-ec.workspace = true
@@ -18,8 +18,8 @@ serde.workspace = true
1818
serde_with.workspace = true
1919
blake2 = { version = "0.10", default-features = false }
2020
itertools.workspace = true
21-
coconut-crypto = { version = "0.5.0", default-features = false, path = "../coconut" }
22-
oblivious_transfer_protocols = { version = "0.3.0", default-features = false, path = "../oblivious_transfer" }
21+
coconut-crypto = { version = "0.6.0", default-features = false, path = "../coconut" }
22+
oblivious_transfer_protocols = { version = "0.4.0", default-features = false, path = "../oblivious_transfer" }
2323
dock_crypto_utils = { default-features = false, path = "../utils" }
2424
zeroize.workspace = true
2525

benches/benches/dkls19_batch_mul_2p.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ fn batch_multiplication(c: &mut Criterion) {
8282
)
8383
.unwrap();
8484

85-
let (party2, U, kos_rlc, gamma_b) = Party2::new(
85+
let (party2, _, kos_rlc, gamma_b) = Party2::new(
8686
&mut rng,
8787
beta.clone(),
8888
base_ot_sender_keys.clone(),

bulletproofs_plus_plus/Cargo.toml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
[package]
2+
name = "bulletproofs_plus_plus"
3+
version = "0.1.0"
4+
edition.workspace = true
5+
authors.workspace = true
6+
license.workspace = true
7+
repository.workspace = true
8+
description = "Bulletproofs++"
9+
10+
[dependencies]
11+
ark-ff.workspace = true
12+
ark-ec.workspace = true
13+
ark-std.workspace = true
14+
ark-serialize.workspace = true
15+
ark-poly.workspace = true
16+
digest.workspace = true
17+
serde.workspace = true
18+
serde_with.workspace = true
19+
zeroize.workspace = true
20+
rayon = { workspace = true, optional = true }
21+
dock_crypto_utils = { version = "0.16.0", default-features = false, path = "../utils" }
22+
23+
[dev-dependencies]
24+
blake2.workspace = true
25+
ark-bls12-381.workspace = true
26+
ark-ed25519 = { version = "^0.4.0", default-features = false }
27+
ark-curve25519 = { version = "^0.4.0", default-features = false }
28+
ark-secp256k1 = { version = "^0.4.0", default-features = false }
29+
30+
[features]
31+
default = [ "parallel"]
32+
std = [ "ark-ff/std", "ark-ec/std", "ark-std/std", "ark-serialize/std", "serde/std", "ark-poly/std", "dock_crypto_utils/std"]
33+
parallel = [ "std", "ark-ff/parallel", "ark-ec/parallel", "ark-std/parallel", "rayon", "ark-poly/parallel", "dock_crypto_utils/parallel"]
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
use ark_std::string::String;
2+
use serde::Serialize;
3+
4+
#[derive(Debug, Serialize)]
5+
pub enum BulletproofsPlusPlusError {
6+
UnexpectedLengthOfVectors(String),
7+
WeightedNormLinearArgumentVerificationFailed,
8+
ExpectedPowerOfTwo(String),
9+
ValueIncompatibleWithBase(String),
10+
IncorrectBounds(String),
11+
IncorrectNumberOfCommitments(usize, usize),
12+
}

bulletproofs_plus_plus/src/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#![cfg_attr(not(feature = "std"), no_std)]
2+
#![allow(non_snake_case)]
3+
4+
pub mod error;
5+
pub mod range_proof_arbitrary_range;
6+
pub mod rangeproof;
7+
pub mod setup;
8+
pub mod util;
9+
pub mod weighted_norm_linear_argument;
10+
11+
pub mod prelude {
12+
pub use crate::{
13+
error::BulletproofsPlusPlusError,
14+
range_proof_arbitrary_range::ProofArbitraryRange,
15+
rangeproof::{Proof, Prover},
16+
setup::SetupParams,
17+
};
18+
}
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
use ark_ec::{AffineRepr, CurveGroup};
2+
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
3+
4+
use ark_std::{format, rand::RngCore, vec::Vec};
5+
6+
use crate::rangeproof::{Proof, Prover};
7+
8+
use crate::error::BulletproofsPlusPlusError;
9+
use dock_crypto_utils::transcript::Transcript;
10+
11+
use crate::setup::SetupParams;
12+
use dock_crypto_utils::msm::WindowTable;
13+
14+
/// Range proof for values in arbitrary ranges where each value `v_i` belongs to interval `[min_i, max_i)`
15+
/// Uses the range proof for perfect ranges of form `[0, base^l)` where upper bound is a power of the base.
16+
/// It splits a single range check of the form `min_i <= v_i < max_i` into 2 as `0 <= v_i - min_i` and `0 <= max_i - 1 - v_i`
17+
/// and creates proofs both both checks. Along the proofs, it outputs commitments to `v_i - min_i` and `max_i - 1 - v_i` as
18+
/// `g * (v_i - min_i) + h * {r_i}_1` and `g * (max_i - 1 - v_i) + h * {r_i}_2` respectively and both which can be
19+
/// transformed to `g * v_i + h * {r_i}_1`, `g * v_i + h * {r_i}_2` by the verifier and the prover proves that
20+
/// `v_i` in `g * v_i + h * r_i` in is same as `v_i` in `g * v_i + h * {r_i}_1`, `g * v_i + h * {r_i}_2`
21+
#[derive(Clone, PartialEq, Eq, Debug, CanonicalSerialize, CanonicalDeserialize)]
22+
pub struct ProofArbitraryRange<G: AffineRepr> {
23+
pub V: Vec<G>,
24+
pub proof: Proof<G>,
25+
}
26+
27+
impl<G: AffineRepr> ProofArbitraryRange<G> {
28+
pub fn new<R: RngCore>(
29+
rng: &mut R,
30+
num_bits: u16,
31+
values_and_bounds: Vec<(u64, u64, u64)>,
32+
randomness: Vec<G::ScalarField>,
33+
setup_params: SetupParams<G>,
34+
transcript: &mut impl Transcript,
35+
) -> Result<Self, BulletproofsPlusPlusError> {
36+
// TODO: Fx base
37+
let base = 2;
38+
Self::new_with_given_base(
39+
rng,
40+
base,
41+
num_bits,
42+
values_and_bounds,
43+
randomness,
44+
setup_params,
45+
transcript,
46+
)
47+
}
48+
49+
pub fn new_with_given_base<R: RngCore>(
50+
rng: &mut R,
51+
base: u16,
52+
num_bits: u16,
53+
values_and_bounds: Vec<(u64, u64, u64)>,
54+
randomness: Vec<G::ScalarField>,
55+
setup_params: SetupParams<G>,
56+
transcript: &mut impl Transcript,
57+
) -> Result<Self, BulletproofsPlusPlusError> {
58+
if values_and_bounds.len() * 2 != randomness.len() {
59+
return Err(BulletproofsPlusPlusError::UnexpectedLengthOfVectors(
60+
format!(
61+
"length of randomness={} should be double of length of values_and_bounds={}",
62+
values_and_bounds.len(),
63+
randomness.len()
64+
),
65+
));
66+
}
67+
let mut V = Vec::<G>::with_capacity(randomness.len());
68+
let mut v = Vec::<u64>::with_capacity(randomness.len());
69+
for (i, (v_i, min, max)) in values_and_bounds.iter().enumerate() {
70+
if min > v_i {
71+
return Err(BulletproofsPlusPlusError::IncorrectBounds(format!(
72+
"value={} should be >= min={}",
73+
v_i, min
74+
)));
75+
}
76+
if v_i >= max {
77+
return Err(BulletproofsPlusPlusError::IncorrectBounds(format!(
78+
"value={} should be < max={}",
79+
v_i, max
80+
)));
81+
}
82+
// Commit to `v_i - min` as `g * (v_i - min) + h * randomness[2 * i]`
83+
V.push(setup_params.compute_pedersen_commitment(v_i - min, &randomness[2 * i]));
84+
// Commit to `max - 1 - v_i` as `g * (max -1 - v_i) + h * randomness[2 * i]`
85+
V.push(setup_params.compute_pedersen_commitment(max - 1 - v_i, &randomness[2 * i + 1]));
86+
v.push(v_i - min);
87+
v.push(max - 1 - v_i);
88+
}
89+
let prover = Prover::new_with_given_base(base, num_bits, V.clone(), v, randomness)?;
90+
let proof = prover.prove(rng, setup_params, transcript)?;
91+
Ok(Self { V, proof })
92+
}
93+
94+
pub fn verify(
95+
&self,
96+
num_bits: u16,
97+
setup_params: &SetupParams<G>,
98+
transcript: &mut impl Transcript,
99+
) -> Result<(), BulletproofsPlusPlusError> {
100+
self.proof
101+
.verify(num_bits, &self.V, setup_params, transcript)
102+
}
103+
104+
pub fn num_proofs(&self) -> u32 {
105+
self.V.len() as u32 / 2
106+
}
107+
108+
/// Returns a vector of tuples where each tuple is a pair of commitments as (`(v_i - min_i)`, `(max_i - v_i)`)
109+
pub fn get_split_commitments(&self) -> Vec<(G, G)> {
110+
let mut comms = Vec::with_capacity(self.num_proofs() as usize);
111+
for i in (0..self.V.len()).step_by(2) {
112+
comms.push((self.V[i], self.V[i + 1]));
113+
}
114+
comms
115+
}
116+
117+
/// Returns a vector of tuples where each tuple is a pair of commitments to the `v_i` but with different randomnesses
118+
pub fn get_commitments_to_values(
119+
&self,
120+
bounds: Vec<(u64, u64)>,
121+
setup_params: &SetupParams<G>,
122+
) -> Result<Vec<(G, G)>, BulletproofsPlusPlusError> {
123+
self.get_commitments_to_values_given_g(bounds, &setup_params.G)
124+
}
125+
126+
/// Same as `Self::get_commitments_to_values` but accepts the generator `g` from the setup params
127+
pub fn get_commitments_to_values_given_g(
128+
&self,
129+
bounds: Vec<(u64, u64)>,
130+
g: &G,
131+
) -> Result<Vec<(G, G)>, BulletproofsPlusPlusError> {
132+
if bounds.len() != self.num_proofs() as usize {
133+
return Err(BulletproofsPlusPlusError::IncorrectNumberOfCommitments(
134+
bounds.len(),
135+
self.num_proofs() as usize,
136+
));
137+
}
138+
let table = WindowTable::new(self.num_proofs() as usize * 2, g.into_group());
139+
let mut comms = Vec::with_capacity(self.num_proofs() as usize);
140+
for i in (0..self.V.len()).step_by(2) {
141+
let (min, max) = (bounds[i / 2].0, bounds[i / 2].1);
142+
if max <= min {
143+
return Err(BulletproofsPlusPlusError::IncorrectBounds(format!(
144+
"max={} should be > min={}",
145+
max, min
146+
)));
147+
}
148+
// `V[i]` is a commitment to `value - min` and `V[i+1]` is a commitment to `max - 1 - value`. Generate commitments
149+
// to value by `V[i] + g * min` and `g * (max - 1) - V[i+1]`
150+
comms.push((
151+
(self.V[i] + table.multiply(&G::ScalarField::from(min))).into_affine(),
152+
(table.multiply(&G::ScalarField::from(max - 1)) - self.V[i + 1]).into_affine(),
153+
));
154+
}
155+
Ok(comms)
156+
}
157+
}
158+
159+
#[cfg(test)]
160+
mod tests {
161+
use super::*;
162+
use ark_std::{
163+
rand::{prelude::StdRng, SeedableRng},
164+
UniformRand,
165+
};
166+
use blake2::Blake2b512;
167+
use dock_crypto_utils::transcript::new_merlin_transcript;
168+
use std::time::{Duration, Instant};
169+
170+
fn test_rangeproof_for_arbitrary_range<G: AffineRepr>(
171+
base: u16,
172+
num_bits: u16,
173+
values_and_bounds: Vec<(u64, u64, u64)>,
174+
) -> (Duration, Duration) {
175+
let mut rng = StdRng::seed_from_u64(0u64);
176+
177+
let mut gamma = vec![];
178+
for _ in 0..values_and_bounds.len() * 2 {
179+
gamma.push(G::ScalarField::rand(&mut rng));
180+
}
181+
182+
let setup_params = SetupParams::<G>::new_for_arbitrary_range_proof::<Blake2b512>(
183+
b"test",
184+
base,
185+
num_bits,
186+
values_and_bounds.len() as u32,
187+
);
188+
189+
let start = Instant::now();
190+
let mut transcript = new_merlin_transcript(b"BPP/tests");
191+
let proof = ProofArbitraryRange::new_with_given_base(
192+
&mut rng,
193+
base,
194+
num_bits,
195+
values_and_bounds.clone(),
196+
gamma.clone(),
197+
setup_params.clone(),
198+
&mut transcript,
199+
)
200+
.unwrap();
201+
let proving_time = start.elapsed();
202+
203+
let bounds = values_and_bounds
204+
.clone()
205+
.into_iter()
206+
.map(|(_, min, max)| (min, max))
207+
.collect();
208+
let split_comms = proof.get_split_commitments();
209+
let comms = proof
210+
.get_commitments_to_values(bounds, &setup_params)
211+
.unwrap();
212+
213+
let start = Instant::now();
214+
let mut transcript = new_merlin_transcript(b"BPP/tests");
215+
proof
216+
.verify(num_bits, &setup_params, &mut transcript)
217+
.unwrap();
218+
let verifying_time = start.elapsed();
219+
220+
for (i, (v, min, max)) in values_and_bounds.into_iter().enumerate() {
221+
let (comm_min, comm_max) = split_comms[i];
222+
assert_eq!(
223+
comm_min + setup_params.compute_pedersen_commitment(min, &-gamma[2 * i]),
224+
setup_params.G * G::ScalarField::from(v)
225+
);
226+
assert_eq!(
227+
setup_params
228+
.compute_pedersen_commitment(max - 1, &gamma[2 * i + 1])
229+
.into_group()
230+
- comm_max,
231+
setup_params.G * G::ScalarField::from(v)
232+
);
233+
assert_eq!(
234+
comms[i].0 + setup_params.H_vec[0].mul(-gamma[2 * i]),
235+
setup_params.G * G::ScalarField::from(v)
236+
);
237+
assert_eq!(
238+
comms[i].1 + setup_params.H_vec[0].mul(gamma[2 * i + 1]),
239+
setup_params.G * G::ScalarField::from(v)
240+
);
241+
}
242+
243+
(proving_time, verifying_time)
244+
}
245+
246+
fn check_for_arbitrary_range<G: AffineRepr>() {
247+
for (base, num_bits, val_bounds) in [
248+
(2, 4, vec![(7, 3, 10)]),
249+
(2, 4, vec![(0, 0, 15), (14, 0, 15)]),
250+
(16, 8, vec![(60, 40, 80), (15, 10, 20)]),
251+
(16, 8, vec![(100, 50, 150)]),
252+
] {
253+
let size = val_bounds.len();
254+
let (p, v) = test_rangeproof_for_arbitrary_range::<G>(base, num_bits, val_bounds);
255+
println!("For base={}, max value bits={} for {} checks, proving time = {:?} and verifying time = {:?}", base, num_bits, size, p, v);
256+
}
257+
}
258+
259+
#[test]
260+
fn rangeproof_bls12381() {
261+
check_for_arbitrary_range::<ark_bls12_381::G1Affine>()
262+
}
263+
}

0 commit comments

Comments
 (0)