Skip to content

Commit

Permalink
feat!: split batches for verification (#22)
Browse files Browse the repository at this point in the history
* Split batches for verification

* Fail on an empty batch

* Remove unnecessary call

* Add a comment about the batch limit
  • Loading branch information
AaronFeickert committed Dec 14, 2022
1 parent b7f00f2 commit 531bae6
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 45 deletions.
20 changes: 16 additions & 4 deletions benches/range_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ fn verify_aggregated_rangeproof_helper(bit_length: usize, extension_degree: Exte
// Benchmark this code
b.iter(|| {
// 5. Verify the aggregated proof
let _masks = RangeProof::verify_do_not_recover_masks(transcript_label, &statements, &proofs).unwrap();
let _masks =
RangeProof::verify_batch(transcript_label, &statements, &proofs, VerifyAction::VerifyOnly).unwrap();
});
});
}
Expand Down Expand Up @@ -251,11 +252,22 @@ fn verify_batched_rangeproofs_helper(bit_length: usize, extension_degree: Extens
// 5. Verify the entire batch of single proofs
match extract_masks {
VerifyAction::VerifyOnly => {
let _masks =
RangeProof::verify_do_not_recover_masks(transcript_label, statements, proofs).unwrap();
let _masks = RangeProof::verify_batch(
transcript_label,
statements,
proofs,
VerifyAction::VerifyOnly,
)
.unwrap();
},
VerifyAction::RecoverOnly => {
let _masks = RangeProof::recover_masks_ony(transcript_label, statements, proofs).unwrap();
let _masks = RangeProof::verify_batch(
transcript_label,
statements,
proofs,
VerifyAction::RecoverOnly,
)
.unwrap();
},
_ => {},
}
Expand Down
73 changes: 44 additions & 29 deletions src/range_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ pub struct RangeProof<P: Compressable> {
/// The maximum bit length for which proofs can be generated
pub const MAX_RANGE_PROOF_BIT_LENGTH: usize = 64;

/// Maximum number of proofs in a batch
/// This is only for performance reasons, where a very large batch can see diminishing returns
/// There is no theoretical limit imposed by the algorithms!
const MAX_RANGE_PROOF_BATCH_SIZE: usize = 256;

/// # Example
/// ```
/// use curve25519_dalek::scalar::Scalar;
Expand Down Expand Up @@ -160,13 +165,18 @@ pub const MAX_RANGE_PROOF_BIT_LENGTH: usize = 64;
/// }
///
/// // 5. Verify the entire batch as the commitment owner, i.e. the prover self
/// let recovered_private_masks =
/// RangeProof::verify_and_recover_masks(transcript_label, &statements_private, &proofs).unwrap();
/// let recovered_private_masks = RangeProof::verify_batch(
/// transcript_label,
/// &statements_private,
/// &proofs,
/// VerifyAction::RecoverAndVerify,
/// )
/// .unwrap();
/// assert_eq!(private_masks, recovered_private_masks);
///
/// // 6. Verify the entire batch as public entity
/// let recovered_public_masks =
/// RangeProof::verify_do_not_recover_masks(transcript_label, &statements_public, &proofs).unwrap();
/// RangeProof::verify_batch(transcript_label, &statements_public, &proofs, VerifyAction::VerifyOnly).unwrap();
/// assert_eq!(public_masks, recovered_public_masks);
///
/// # }
Expand Down Expand Up @@ -433,37 +443,42 @@ where
Ok((max_mn, max_index))
}

/// Verify a batch of single and/or aggregated range proofs as the commitment owner and recover the masks for single
/// range proofs by supplying the optional seed nonces
pub fn verify_and_recover_masks(
/// Wrapper function for batch verification in different modes: mask recovery, verification, or both
pub fn verify_batch(
transcript_label: &'static str,
statements: &[RangeStatement<P>],
range_proofs: &[RangeProof<P>],
proofs: &[RangeProof<P>],
action: VerifyAction,
) -> Result<Vec<Option<ExtendedMask>>, ProofError> {
RangeProof::verify(
transcript_label,
statements,
range_proofs,
VerifyAction::RecoverAndVerify,
)
}
// By definition, an empty batch fails
if statements.is_empty() || proofs.is_empty() {
return Err(ProofError::InvalidArgument(
"Range statements or proofs length empty".to_string(),
));
}
// We need to check for size consistency here, even though it's also done later
if statements.len() != proofs.len() {
return Err(ProofError::InvalidArgument(
"Range statements and proofs length mismatch".to_string(),
));
}

/// Verify a batch of single and/or aggregated range proofs as a public entity
pub fn verify_do_not_recover_masks(
transcript_label: &'static str,
statements: &[RangeStatement<P>],
range_proofs: &[RangeProof<P>],
) -> Result<Vec<Option<ExtendedMask>>, ProofError> {
RangeProof::verify(transcript_label, statements, range_proofs, VerifyAction::VerifyOnly)
}
// Store masks from all results
let mut masks = Vec::<Option<ExtendedMask>>::with_capacity(proofs.len());

/// Recover the masks for single range proofs by supplying the optional seed nonces
pub fn recover_masks_ony(
transcript_label: &'static str,
statements: &[RangeStatement<P>],
range_proofs: &[RangeProof<P>],
) -> Result<Vec<Option<ExtendedMask>>, ProofError> {
RangeProof::verify(transcript_label, statements, range_proofs, VerifyAction::RecoverOnly)
// Get chunks of both the statements and proofs
let mut chunks = statements
.chunks(MAX_RANGE_PROOF_BATCH_SIZE)
.zip(proofs.chunks(MAX_RANGE_PROOF_BATCH_SIZE));

// If the batch fails, propagate the error; otherwise, store the masks and keep going
if let Some((batch_statements, batch_proofs)) = chunks.next() {
let mut result = RangeProof::verify(transcript_label, batch_statements, batch_proofs, action)?;

masks.append(&mut result);
}

Ok(masks)
}

// Verify a batch of single and/or aggregated range proofs as a public entity, or recover the masks for single
Expand Down
44 changes: 32 additions & 12 deletions tests/ristretto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use tari_bulletproofs_plus::{
generators::pedersen_gens::ExtensionDegree,
protocols::scalar_protocol::ScalarProtocol,
range_parameters::RangeParameters,
range_proof::RangeProof,
range_proof::{RangeProof, VerifyAction},
range_statement::RangeStatement,
range_witness::RangeWitness,
ristretto,
Expand Down Expand Up @@ -244,23 +244,37 @@ fn prove_and_verify(
if !proofs.is_empty() {
// 5. Verify the entire batch as the commitment owner, i.e. the prover self
// --- Only recover the masks
let recovered_private_masks =
RangeProof::recover_masks_ony(transcript_label, &statements_private.clone(), &proofs.clone()).unwrap();
let recovered_private_masks = RangeProof::verify_batch(
transcript_label,
&statements_private.clone(),
&proofs.clone(),
VerifyAction::RecoverOnly,
)
.unwrap();
assert_eq!(private_masks, recovered_private_masks);
// --- Recover the masks and verify the proofs
let recovered_private_masks =
RangeProof::verify_and_recover_masks(transcript_label, &statements_private.clone(), &proofs.clone())
.unwrap();
let recovered_private_masks = RangeProof::verify_batch(
transcript_label,
&statements_private.clone(),
&proofs.clone(),
VerifyAction::RecoverAndVerify,
)
.unwrap();
assert_eq!(private_masks, recovered_private_masks);
// --- Verify the proofs but do not recover the masks
let recovered_private_masks =
RangeProof::verify_do_not_recover_masks(transcript_label, &statements_private.clone(), &proofs.clone())
.unwrap();
let recovered_private_masks = RangeProof::verify_batch(
transcript_label,
&statements_private.clone(),
&proofs.clone(),
VerifyAction::VerifyOnly,
)
.unwrap();
assert_eq!(public_masks, recovered_private_masks);

// 6. Verify the entire batch as public entity
let recovered_public_masks =
RangeProof::verify_do_not_recover_masks(transcript_label, &statements_public, &proofs).unwrap();
RangeProof::verify_batch(transcript_label, &statements_public, &proofs, VerifyAction::VerifyOnly)
.unwrap();
assert_eq!(public_masks, recovered_public_masks);

// 7. Try to recover the masks with incorrect seed_nonce values
Expand All @@ -282,10 +296,11 @@ fn prove_and_verify(
seed_nonce: statement.seed_nonce.map(|seed_nonce| seed_nonce + Scalar::one()),
});
}
let recovered_private_masks_changed = RistrettoRangeProof::verify_and_recover_masks(
let recovered_private_masks_changed = RistrettoRangeProof::verify_batch(
transcript_label,
&statements_private_changed,
&proofs.clone(),
VerifyAction::RecoverAndVerify,
)
.unwrap();
assert_ne!(private_masks, recovered_private_masks_changed);
Expand All @@ -312,7 +327,12 @@ fn prove_and_verify(
seed_nonce: statement.seed_nonce,
});
}
match RangeProof::verify_do_not_recover_masks(transcript_label, &statements_public_changed, &proofs) {
match RangeProof::verify_batch(
transcript_label,
&statements_public_changed,
&proofs,
VerifyAction::VerifyOnly,
) {
Ok(_) => {
panic!("Range proof should not verify")
},
Expand Down

0 comments on commit 531bae6

Please sign in to comment.