forked from tari-project/triptych
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b83d1df
commit b906878
Showing
2 changed files
with
91 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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 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,86 @@ | ||
// Copyright (c) 2024, The Tari Project | ||
// SPDX-License-Identifier: BSD-3-Clause | ||
|
||
//! In a RingCT design, outputs have (roughly speaking) two parts that we need: | ||
//! - an output verification key, usually derived from a recipient address and nonce | ||
//! - a value commitment, where the mask is derived from a shared secret | ||
//! | ||
//! A linkable ring signature is required in such a design. | ||
//! | ||
//! This example shows how to use Triptych. | ||
#[cfg(test)] | ||
mod test { | ||
use std::sync::Arc; | ||
|
||
use curve25519_dalek::{RistrettoPoint, Scalar}; | ||
use merlin::Transcript; | ||
use rand_chacha::ChaCha12Rng; | ||
use rand_core::SeedableRng; | ||
use triptych::parallel::*; | ||
|
||
#[allow(non_snake_case)] | ||
#[test] | ||
fn ringct() { | ||
// In practice you should use an actual random number generator; this is just for easier testing | ||
let mut rng = ChaCha12Rng::seed_from_u64(8675309); | ||
|
||
// Parameters that will define the number of outputs used in the proof: 2^4 == 16 | ||
// The parameters are `Arc`-wrapped since it's likely they could be reused | ||
let n = 2; | ||
let m = 4; | ||
let params = Arc::new(TriptychParameters::new(n, m).unwrap()); | ||
let number_outputs = params.get_N(); | ||
|
||
// Value commitments use the Triptych `G` generator for masks, and need another component for values | ||
let H = RistrettoPoint::random(&mut rng); | ||
|
||
// All output verification keys and value commitments (except ours) look random to us, so just make them random! | ||
let mut output_keys = (0..number_outputs) | ||
.map(|_| RistrettoPoint::random(&mut rng)) | ||
.collect::<Vec<RistrettoPoint>>(); | ||
let mut value_commitments = (0..number_outputs) | ||
.map(|_| RistrettoPoint::random(&mut rng)) | ||
.collect::<Vec<RistrettoPoint>>(); | ||
|
||
// We'll put the output we control at some arbitrary index within the sets | ||
let index: u32 = 7; | ||
|
||
// We know the signing key corresponding to the output verification key | ||
let signing_key = Scalar::random(&mut rng); | ||
output_keys[index as usize] = signing_key * params.get_G(); | ||
|
||
// We also know the value and mask corresponding to the value commitment | ||
let commitment_value = Scalar::from(12345u32); | ||
let commitment_mask = Scalar::random(&mut rng); | ||
value_commitments[index as usize] = commitment_value * H + commitment_mask * params.get_G1(); | ||
|
||
// In RingCT, the linkable ring signature (Triptych, in this case) comes equipped with a commitment offset | ||
// This is a commitment to the same value, but with a different mask | ||
// Why? Because the difference between the value commitment and offset now looks like a verification key! | ||
// (The value components cancel) | ||
let offset_mask = Scalar::random(&mut rng); | ||
let offset = commitment_value * H + offset_mask * params.get_G1(); | ||
|
||
// We are ready to set up the Triptych witness! | ||
// This includes the signing key and the difference between the value commitment and offset masks | ||
let witness = TriptychWitness::new(¶ms, index, &signing_key, &(commitment_mask - offset_mask)).unwrap(); | ||
|
||
// We can also set up the input set and statement | ||
// The input set is `Arc`-wrapped since it's likely it could be reused | ||
// The linkable ring signature also comes equipped with a linking tag; the library can compute it for us | ||
let input_set = Arc::new(TriptychInputSet::new(&output_keys, &value_commitments).unwrap()); | ||
let statement = TriptychStatement::new(¶ms, &input_set, &offset, &witness.compute_linking_tag()).unwrap(); | ||
|
||
// The proof needs a transcript associated to it | ||
// This binds any important context we might care about | ||
// For this example, we'll keep it simple | ||
let mut transcript = Transcript::new(b"An example of RingCT with Triptych"); | ||
|
||
// At long last, build the proof! | ||
// Note that we need to clone the transcript here, since the verifier needs to use the original one | ||
let proof = TriptychProof::prove_with_rng(&witness, &statement, &mut rng, &mut transcript.clone()).unwrap(); | ||
|
||
// The proof should verify | ||
assert!(proof.verify(&statement, &mut transcript).is_ok()); | ||
} | ||
} |