Skip to content
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

bulletproofs: add closure to allow increases to prover generator capacity #266

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 20 additions & 9 deletions benches/r1cs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ impl KShuffleGadget {

pub fn prove<'a, 'b>(
pc_gens: &'b PedersenGens,
bp_gens: &'b BulletproofGens,
bp_gens: &'b mut BulletproofGens,
transcript: &'a mut Transcript,
input: &[Scalar],
output: &[Scalar],
Expand Down Expand Up @@ -156,7 +156,7 @@ impl KShuffleGadget {
.unzip();

Self::fill_cs(&mut prover, input_vars, output_vars)?;
let proof = prover.prove(&bp_gens)?;
let proof = prover.prove(bp_gens, |capacity, gens| gens.increase_capacity(capacity))?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe instead of sending generators into prove, and then getting them into closure, instead simply provide the reference from the closure (not sure if borrowck would love this):

let gens = BulletproofGens::new(128, 1);
...
let proof = prover.prove(|capacity| gens.increase_capacity(capacity); &gens )?;

?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the closure must return Result<&BulletproofGens, R1CSError> because the prover may decide to return Err if capacity is too high instead of blinding increasing it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having the closure provide the reference makes the borrow checker unhappy 😢 says it can't infer the appropriate lifetime?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The prover can provide a closure that doesn't resize the generators (which would result in a failure from insufficient capacity). Do we want to also have the closure return Result<(), R1CSError>? It feels maybe unnecessary to me since a no-op closure will have the same overall effect


Ok((proof, input_commitments, output_commitments))
}
Expand Down Expand Up @@ -206,11 +206,17 @@ fn bench_kshuffle_prove(c: &mut Criterion) {

// Make kshuffle proof
let pc_gens = PedersenGens::default();
let bp_gens = BulletproofGens::new(128, 1);
let mut bp_gens = BulletproofGens::new(128, 1);
b.iter(|| {
let mut prover_transcript = Transcript::new(b"ShuffleTest");
KShuffleGadget::prove(&pc_gens, &bp_gens, &mut prover_transcript, &input, &output)
.unwrap();
KShuffleGadget::prove(
&pc_gens,
&mut bp_gens,
&mut prover_transcript,
&input,
&output,
)
.unwrap();
})
},
vec![8, 16, 32, 64, 17],
Expand Down Expand Up @@ -239,11 +245,16 @@ fn bench_kshuffle_verify(c: &mut Criterion) {

// Make kshuffle proof
let pc_gens = PedersenGens::default();
let bp_gens = BulletproofGens::new(128, 1);
let mut bp_gens = BulletproofGens::new(128, 1);
let mut prover_transcript = Transcript::new(b"ShuffleTest");
let (proof, in_commitments, out_commitments) =
KShuffleGadget::prove(&pc_gens, &bp_gens, &mut prover_transcript, &input, &output)
.unwrap();
let (proof, in_commitments, out_commitments) = KShuffleGadget::prove(
&pc_gens,
&mut bp_gens,
&mut prover_transcript,
&input,
&output,
)
.unwrap();

// Verify kshuffle proof
b.iter(|| {
Expand Down
16 changes: 8 additions & 8 deletions docs/r1cs-docs-example.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ impl ShuffleProof {
/// Returns a tuple `(proof, input_commitments || output_commitments)`.
pub fn prove<'a, 'b>(
pc_gens: &'b PedersenGens,
bp_gens: &'b BulletproofGens,
bp_gens: &'b mut BulletproofGens,
transcript: &'a mut Transcript,
input: &[Scalar],
output: &[Scalar],
Expand Down Expand Up @@ -232,7 +232,7 @@ impl ShuffleProof {

ShuffleProof::gadget(&mut prover, input_vars, output_vars)?;

let proof = prover.prove(&bp_gens)?;
let proof = prover.prove(bp_gens, |capacity, gens| gens.increase_capacity(capacity))?;

Ok((ShuffleProof(proof), input_commitments, output_commitments))
}
Expand Down Expand Up @@ -309,7 +309,7 @@ The verifier receives a proof, and a list of committed inputs and outputs, from
# /// Returns a tuple `(proof, input_commitments || output_commitments)`.
# pub fn prove<'a, 'b>(
# pc_gens: &'b PedersenGens,
# bp_gens: &'b BulletproofGens,
# bp_gens: &'b mut BulletproofGens,
# transcript: &'a mut Transcript,
# input: &[Scalar],
# output: &[Scalar],
Expand Down Expand Up @@ -339,7 +339,7 @@ The verifier receives a proof, and a list of committed inputs and outputs, from
#
# ShuffleProof::gadget(&mut prover, input_vars, output_vars)?;
#
# let proof = prover.prove(&bp_gens)?;
# let proof = prover.prove(bp_gens, |capacity, gens| gens.increase_capacity(capacity))?;
#
# Ok((ShuffleProof(proof), input_commitments, output_commitments))
# }
Expand Down Expand Up @@ -449,7 +449,7 @@ Because only the prover knows the scalar values of the inputs and outputs, and t
# /// Returns a tuple `(proof, input_commitments || output_commitments)`.
# pub fn prove<'a, 'b>(
# pc_gens: &'b PedersenGens,
# bp_gens: &'b BulletproofGens,
# bp_gens: &'b mut BulletproofGens,
# transcript: &'a mut Transcript,
# input: &[Scalar],
# output: &[Scalar],
Expand Down Expand Up @@ -479,7 +479,7 @@ Because only the prover knows the scalar values of the inputs and outputs, and t
#
# ShuffleProof::gadget(&mut prover, input_vars, output_vars)?;
#
# let proof = prover.prove(&bp_gens)?;
# let proof = prover.prove(bp_gens, |capacity, gens| gens.increase_capacity(capacity))?;
#
# Ok((ShuffleProof(proof), input_commitments, output_commitments))
# }
Expand Down Expand Up @@ -517,7 +517,7 @@ Because only the prover knows the scalar values of the inputs and outputs, and t
# fn main() {
// Construct generators. 1024 Bulletproofs generators is enough for 512-size shuffles.
let pc_gens = PedersenGens::default();
let bp_gens = BulletproofGens::new(1024, 1);
let mut bp_gens = BulletproofGens::new(1024, 1);

// Putting the prover code in its own scope means we can't
// accidentally reuse prover data in the test.
Expand All @@ -538,7 +538,7 @@ let (proof, in_commitments, out_commitments) = {
let mut prover_transcript = Transcript::new(b"ShuffleProofTest");
ShuffleProof::prove(
&pc_gens,
&bp_gens,
&mut bp_gens,
&mut prover_transcript,
&inputs,
&outputs,
Expand Down
14 changes: 13 additions & 1 deletion src/r1cs/prover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,14 @@ impl<'t, 'g> Prover<'t, 'g> {
}

/// Consume this `ConstraintSystem` to produce a proof.
pub fn prove(mut self, bp_gens: &BulletproofGens) -> Result<R1CSProof, R1CSError> {
pub fn prove<F>(
mut self,
bp_gens: &mut BulletproofGens,
mut resize_fn: F,
) -> Result<R1CSProof, R1CSError>
where
F: FnMut(usize, &mut BulletproofGens),
{
use std::iter;
use util;

Expand Down Expand Up @@ -397,6 +404,8 @@ impl<'t, 'g> Prover<'t, 'g> {
// Commit to the first-phase low-level witness variables.
let n1 = self.a_L.len();

/// Get resized generators and check sufficient capacity
resize_fn(n1, bp_gens);
if bp_gens.gens_capacity < n1 {
return Err(R1CSError::InvalidGeneratorsLength);
}
Expand Down Expand Up @@ -455,9 +464,12 @@ impl<'t, 'g> Prover<'t, 'g> {
let padded_n = self.a_L.len().next_power_of_two();
let pad = padded_n - n;

// Resize from phase 2 and check capacity
resize_fn(padded_n, bp_gens);
if bp_gens.gens_capacity < padded_n {
return Err(R1CSError::InvalidGeneratorsLength);
}
let gens = bp_gens.share(0);

// Commit to the second-phase low-level witness variables

Expand Down
91 changes: 78 additions & 13 deletions tests/r1cs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ impl ShuffleProof {
/// Returns a tuple `(proof, input_commitments || output_commitments)`.
pub fn prove<'a, 'b>(
pc_gens: &'b PedersenGens,
bp_gens: &'b BulletproofGens,
bp_gens: &'b mut BulletproofGens,
transcript: &'a mut Transcript,
input: &[Scalar],
output: &[Scalar],
Expand Down Expand Up @@ -100,7 +100,9 @@ impl ShuffleProof {

ShuffleProof::gadget(&mut prover, input_vars, output_vars)?;

let proof = prover.prove(&bp_gens)?;
let proof = prover.prove(bp_gens, |capacity, gens| {
gens.increase_capacity(capacity);
})?;

Ok((ShuffleProof(proof), input_commitments, output_commitments))
}
Expand Down Expand Up @@ -144,7 +146,7 @@ fn kshuffle_helper(k: usize) {

// Common code
let pc_gens = PedersenGens::default();
let bp_gens = BulletproofGens::new((2 * k).next_power_of_two(), 1);
let mut bp_gens = BulletproofGens::new((2 * k).next_power_of_two(), 1);

let (proof, input_commitments, output_commitments) = {
// Randomly generate inputs and outputs to kshuffle
Expand All @@ -157,7 +159,14 @@ fn kshuffle_helper(k: usize) {
rand::thread_rng().shuffle(&mut output);

let mut prover_transcript = Transcript::new(b"ShuffleProofTest");
ShuffleProof::prove(&pc_gens, &bp_gens, &mut prover_transcript, &input, &output).unwrap()
ShuffleProof::prove(
&pc_gens,
&mut bp_gens,
&mut prover_transcript,
&input,
&output,
)
.unwrap()
};

{
Expand Down Expand Up @@ -236,7 +245,7 @@ fn example_gadget<CS: ConstraintSystem>(
// Prover's scope
fn example_gadget_proof(
pc_gens: &PedersenGens,
bp_gens: &BulletproofGens,
bp_gens: &mut BulletproofGens,
a1: u64,
a2: u64,
b1: u64,
Expand Down Expand Up @@ -267,7 +276,9 @@ fn example_gadget_proof(
);

// 4. Make a proof
let proof = prover.prove(bp_gens)?;
let proof = prover.prove(bp_gens, |capacity, gens| {
gens.increase_capacity(capacity);
})?;

Ok((proof, commitments))
}
Expand Down Expand Up @@ -315,9 +326,10 @@ fn example_gadget_roundtrip_helper(
) -> Result<(), R1CSError> {
// Common
let pc_gens = PedersenGens::default();
let bp_gens = BulletproofGens::new(128, 1);
let mut bp_gens = BulletproofGens::new(128, 1);

let (proof, commitments) = example_gadget_proof(&pc_gens, &bp_gens, a1, a2, b1, b2, c1, c2)?;
let (proof, commitments) =
example_gadget_proof(&pc_gens, &mut bp_gens, a1, a2, b1, b2, c1, c2)?;

example_gadget_verify(&pc_gens, &bp_gens, c2, proof, commitments)
}
Expand All @@ -332,9 +344,10 @@ fn example_gadget_roundtrip_serialization_helper(
) -> Result<(), R1CSError> {
// Common
let pc_gens = PedersenGens::default();
let bp_gens = BulletproofGens::new(128, 1);
let mut bp_gens = BulletproofGens::new(128, 1);

let (proof, commitments) = example_gadget_proof(&pc_gens, &bp_gens, a1, a2, b1, b2, c1, c2)?;
let (proof, commitments) =
example_gadget_proof(&pc_gens, &mut bp_gens, a1, a2, b1, b2, c1, c2)?;

let proof = proof.to_bytes();

Expand Down Expand Up @@ -417,7 +430,7 @@ fn range_proof_gadget() {
fn range_proof_helper(v_val: u64, n: usize) -> Result<(), R1CSError> {
// Common
let pc_gens = PedersenGens::default();
let bp_gens = BulletproofGens::new(128, 1);
let mut bp_gens = BulletproofGens::new(128, 1);

// Prover's scope
let (proof, commitment) = {
Expand All @@ -430,8 +443,7 @@ fn range_proof_helper(v_val: u64, n: usize) -> Result<(), R1CSError> {
let (com, var) = prover.commit(v_val.into(), Scalar::random(&mut rng));
assert!(range_proof(&mut prover, var.into(), Some(v_val), n).is_ok());

let proof = prover.prove(&bp_gens)?;

let proof = prover.prove(&mut bp_gens, |capacity, gens| ())?;
(proof, com)
};

Expand All @@ -447,3 +459,56 @@ fn range_proof_helper(v_val: u64, n: usize) -> Result<(), R1CSError> {
// Verifier verifies proof
Ok(verifier.verify(&proof, &pc_gens, &bp_gens)?)
}

fn prover_capacity_resize_helper<F>(
capacity: usize,
resize_fn: F,
) -> Result<(R1CSProof, CompressedRistretto), R1CSError>
where
F: FnMut(usize, &mut BulletproofGens),
{
// Common
let pc_gens = PedersenGens::default();
let mut bp_gens = BulletproofGens::new(capacity, 1);

// Prover makes a `ConstraintSystem` instance representing a range proof gadget
let mut prover_transcript = Transcript::new(b"RangeProofTest");
let mut rng = rand::thread_rng();

let mut prover = Prover::new(&pc_gens, &mut prover_transcript);

let (com, var) = prover.commit(10u64.into(), Scalar::random(&mut rng));
assert!(range_proof(&mut prover, var.into(), Some(10u64), 32).is_ok());

let proof = prover.prove(&mut bp_gens, resize_fn)?;
Ok((proof, com))
}

#[test]
fn prover_capacity_resize() {
assert!(prover_capacity_resize_helper(128, |_, _| ()).is_ok());
assert!(prover_capacity_resize_helper(0, |_, _| ()).is_err());
assert!(
prover_capacity_resize_helper(0, |capacity, gens| gens.increase_capacity(capacity)).is_ok()
);

let (proof, com) =
prover_capacity_resize_helper(64, |capacity, gens| gens.increase_capacity(capacity))
.unwrap();

// Construct verifier generators
let pc_gens = PedersenGens::default();
let mut bp_gens = BulletproofGens::new(128, 1);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be an interface for the verifier to get the needed capacity for a proof (an added field in R1CSProof)?


// Verifier makes a `ConstraintSystem` instance representing a merge gadget
let mut verifier_transcript = Transcript::new(b"RangeProofTest");
let mut verifier = Verifier::new(&mut verifier_transcript);

let var = verifier.commit(com);

// Verifier adds constraints to the constraint system
assert!(range_proof(&mut verifier, var.into(), None, 32).is_ok());

// Verifier verifies proof
assert!(verifier.verify(&proof, &pc_gens, &bp_gens).is_ok());
}