Skip to content

Commit

Permalink
feat!: use transcript composition (#115)
Browse files Browse the repository at this point in the history
The library uses [Merlin](https://merlin.cool/) transcripts internally
for handling Fiat-Shamir operations. When generating and verifying a
proof, the caller provides a label that is used to instantiate the
transcript.

This is not particularly idiomatic, because it requires a `&'static`
lifetime for the label, it does not follow Merlin's design
recommendations, and it does not support transcript composition.
Composition allows a single transcript to be used for multiple
sub-protocols safely and flexibly.

This PR makes a breaking change in two ways to support this.

First, it changes the public API to replace transcript labels with
mutable references to Merlin transcripts. This means in particular that
the caller is responsible for the transcript: it either instantiates a
new transcript with a label of its choice, or passes along an existing
transcript for composition.

Second, it changes how domain separation is applied to the transcript.
The Merlin
[documentation](https://merlin.cool/transcript/ops.html#initialization)
requires the use of a fixed domain separation message label `dom-sep`,
and recommends its use in composition. The library currently uses a
[different
design](https://github.com/tari-project/bulletproofs-plus/blob/da71f7872f02a0e9d3000c316bb083181daa9942/src/transcripts.rs#L72)
that, while safe if transcripts are strictly internal, could cause
issues during composition.

If it's desirable for existing proofs to verify, the domain separation
change can be reverted, but the documentation should be modified to
indicate this nonstandard behavior.

Closes #114.

BREAKING CHANGE: Changes the prover and verifier APIs to replace
transcript labels with Merlin transcripts. Changes how domain separation
is applied internally.
  • Loading branch information
AaronFeickert authored Mar 5, 2024
1 parent ded62ca commit 6be2bda
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 77 deletions.
28 changes: 18 additions & 10 deletions benches/range_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ extern crate criterion;

use criterion::{Criterion, SamplingMode};
use curve25519_dalek::scalar::Scalar;
use merlin::Transcript;
use rand_chacha::ChaCha12Rng;
use rand_core::{CryptoRngCore, SeedableRng};
use tari_bulletproofs_plus::{
Expand Down Expand Up @@ -93,7 +94,12 @@ fn create_aggregated_rangeproof_helper(bit_length: usize, extension_degree: Exte
// Benchmark this code
b.iter(|| {
// 4. Create the aggregated proof
let _proof = RistrettoRangeProof::prove_with_rng(transcript_label, &statement, &witness, &mut rng);
let _proof = RistrettoRangeProof::prove_with_rng(
&mut Transcript::new(transcript_label.as_bytes()),
&statement,
&witness,
&mut rng,
);
})
});
}
Expand Down Expand Up @@ -133,7 +139,7 @@ fn verify_aggregated_rangeproof_helper(bit_length: usize, extension_degree: Exte
// 0. Batch data
let mut statements = vec![];
let mut proofs = vec![];
let mut transcript_labels = vec![];
let mut transcripts = vec![];

// 1. Generators
let generators = RangeParameters::init(bit_length, aggregation_factor, pederson_gens.clone()).unwrap();
Expand Down Expand Up @@ -165,17 +171,18 @@ fn verify_aggregated_rangeproof_helper(bit_length: usize, extension_degree: Exte
let statement =
RangeStatement::init(generators, commitments.clone(), minimum_values.clone(), seed_nonce).unwrap();
statements.push(statement.clone());
transcript_labels.push(transcript_label);
let mut transcript = Transcript::new(transcript_label.as_bytes());
transcripts.push(transcript.clone());

// 4. Create the proof
let proof = RistrettoRangeProof::prove_with_rng(transcript_label, &statement, &witness, &mut rng).unwrap();
let proof = RistrettoRangeProof::prove_with_rng(&mut transcript, &statement, &witness, &mut rng).unwrap();
proofs.push(proof);

// Benchmark this code
b.iter(|| {
// 5. Verify the aggregated proof
let _masks =
RangeProof::verify_batch(&transcript_labels, &statements, &proofs, VerifyAction::VerifyOnly)
RangeProof::verify_batch(&mut transcripts.clone(), &statements, &proofs, VerifyAction::VerifyOnly)
.unwrap();
});
});
Expand Down Expand Up @@ -221,7 +228,7 @@ fn verify_batched_rangeproofs_helper(bit_length: usize, extension_degree: Extens
// Batch data
let mut statements = vec![];
let mut proofs = vec![];
let mut transcript_labels = vec![];
let mut transcripts = vec![];

for _ in 0..number_of_range_proofs {
// Witness data
Expand All @@ -244,11 +251,12 @@ fn verify_batched_rangeproofs_helper(bit_length: usize, extension_degree: Extens
)
.unwrap();
statements.push(statement.clone());
transcript_labels.push(transcript_label);
let mut transcript = Transcript::new(transcript_label.as_bytes());
transcripts.push(transcript.clone());

// Proof
let proof =
RistrettoRangeProof::prove_with_rng(transcript_label, &statement, &witness, &mut rng).unwrap();
RistrettoRangeProof::prove_with_rng(&mut transcript, &statement, &witness, &mut rng).unwrap();
proofs.push(proof);
}

Expand All @@ -258,7 +266,7 @@ fn verify_batched_rangeproofs_helper(bit_length: usize, extension_degree: Extens
match extract_masks {
VerifyAction::VerifyOnly => {
let _masks = RangeProof::verify_batch(
&transcript_labels,
&mut transcripts.clone(),
&statements,
&proofs,
VerifyAction::VerifyOnly,
Expand All @@ -267,7 +275,7 @@ fn verify_batched_rangeproofs_helper(bit_length: usize, extension_degree: Extens
},
VerifyAction::RecoverOnly => {
let _masks = RangeProof::verify_batch(
&transcript_labels,
&mut transcripts.clone(),
&statements,
&proofs,
VerifyAction::RecoverOnly,
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,7 @@ mod utils;
pub use generators::bulletproof_gens::BulletproofGens;
/// Bulletproofs+ generators and base points needed for a batch of range proofs
pub use generators::pedersen_gens::PedersenGens;
/// Merlin transcripts
pub use merlin::Transcript;

pub mod ristretto;
8 changes: 4 additions & 4 deletions src/protocols/transcript_protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use crate::{errors::ProofError, traits::FixedBytesRepr};

/// Defines a `TranscriptProtocol` trait for using a Merlin transcript.
pub trait TranscriptProtocol {
/// Append a domain separator for the range proof with the given `label` and `message`.
fn domain_separator(&mut self, label: &'static [u8], message: &[u8]);
/// Append a domain separator for the range proof.
fn append_domain_separator(&mut self);

/// Append a `point` with the given `label`.
fn append_point<P: FixedBytesRepr>(&mut self, label: &'static [u8], point: &P);
Expand All @@ -37,8 +37,8 @@ pub trait TranscriptProtocol {
}

impl TranscriptProtocol for Transcript {
fn domain_separator(&mut self, label: &'static [u8], message: &[u8]) {
self.append_message(label, message);
fn append_domain_separator(&mut self) {
self.append_message(b"dom-sep", b"Bulletproofs+ Range Proof");
}

fn append_point<P: FixedBytesRepr>(&mut self, label: &'static [u8], point: &P) {
Expand Down
Loading

0 comments on commit 6be2bda

Please sign in to comment.