Skip to content

Commit

Permalink
Add a RingCT example (tari-project#92)
Browse files Browse the repository at this point in the history
This PR adds a basic example of how to use Triptych for a RingCT-style linkable ring signature.

Closes tari-project#90.
  • Loading branch information
AaronFeickert authored Jul 2, 2024
1 parent b83d1df commit 8d47c28
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 0 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,8 @@ harness = false
[[bench]]
name = "parallel"
harness = false

[[example]]
name = "ringct"
crate-type = ["staticlib"]
test = true
86 changes: 86 additions & 0 deletions examples/ringct.rs
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(&params, 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(&params, &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());
}
}

0 comments on commit 8d47c28

Please sign in to comment.