Skip to content

Commit

Permalink
WIP: Add transfer circuit 2
Browse files Browse the repository at this point in the history
  • Loading branch information
xevisalle committed May 14, 2024
1 parent 73cd8cd commit b6001b5
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 26 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,8 @@ zk = [
name = "encryption_gadgets"
path = "tests/encryption_gadgets.rs"
required-features = ["zk"]

[[test]]
name = "transfer_gadget"
path = "tests/transfer_gadget.rs"
required-features = ["zk"]
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ pub mod crossover;
pub mod error;
/// Fee
pub mod fee;
/// Transfer gadgets
#[cfg(feature = "zk")]
pub mod gadgets;
/// Transparent and Obfuscated Notes
pub mod note;
/// Transfer gadgets
#[cfg(feature = "zk")]
pub mod transfer;

/// Phoenix Core Keys & Addresses
mod keys;
Expand Down
54 changes: 54 additions & 0 deletions src/note.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use dusk_jubjub::{
use crate::aes;

use dusk_poseidon::sponge::hash;

use ff::Field;
use rand_core::{CryptoRng, RngCore};

Expand Down Expand Up @@ -323,6 +324,53 @@ impl Note {
_ => Err(Error::MissingViewKey),
}
}

#[cfg(feature = "zk")]
/// TBC
pub fn create_input_note<const H: usize, const A: usize>(
&self,
sk: &SecretKey,
skeleteon_hash: BlsScalar,
rng: &mut (impl RngCore + CryptoRng),
) -> crate::transfer::InputNote<H, A> {
let note_sk = sk.gen_note_sk(self);
let note_pk_p =
JubJubAffine::from(GENERATOR_NUMS_EXTENDED * note_sk.as_ref());

let vk = ViewKey::from(sk);
let value = self.value(Some(&vk)).expect("Decryption correct.");
let blinding_factor = self
.blinding_factor(Some(&vk))
.expect("Decryption correct.");

let nullifier =
hash(&[note_pk_p.get_u(), note_pk_p.get_v(), self.pos.into()]);

let signature = note_sk.sign_double(rng, skeleteon_hash);

crate::transfer::InputNote {
note: self.clone(),
note_pk_p: note_pk_p.into(),
value,
blinding_factor,
nullifier,
signature,
}
}

#[cfg(feature = "zk")]
/// TBC
/// TODO: return result
pub fn create_output_note(
&self,
vk: &ViewKey,
) -> crate::transfer::OutputNote {
crate::transfer::OutputNote {
value: self.value(Some(&vk)).unwrap(),
value_commitment: self.value_commitment.into(),
blinding_factor: self.blinding_factor(Some(&vk)).unwrap(),
}
}
}

impl Ownable for Note {
Expand All @@ -331,6 +379,12 @@ impl Ownable for Note {
}
}

impl Ownable for &Note {
fn stealth_address(&self) -> &StealthAddress {
&self.stealth_address
}
}

// Serialize into 105 + ENCRYPTION_SIZE bytes, where 105 is the size of all the
// note elements without the encryption. ENCRYPTION_SIZE = PLAINTEXT_SIZE +
// ENCRYPTION_EXTRA_SIZE
Expand Down
46 changes: 23 additions & 23 deletions src/gadgets.rs → src/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,22 @@
// Copyright (c) DUSK NETWORK. All rights reserved.

use dusk_jubjub::{JubJubScalar, GENERATOR, GENERATOR_NUMS};
use jubjub_schnorr::{gadgets, SignatureDouble};
use poseidon_merkle::{zk::opening_gadget, Opening};

use dusk_plonk::prelude::*;
use dusk_poseidon::sponge;
use jubjub_schnorr::{gadgets, SignatureDouble};
use poseidon_merkle::{zk::opening_gadget, Tree};

use crate::Note;

use dusk_plonk::prelude::*;

/// TBC
#[derive(Debug, Clone)]
pub struct InputNote<const H: usize, const A: usize> {
opening: Opening<(), H, A>,
note: Note,
note_pk_p: JubJubAffine,
value: u64,
blinding_factor: JubJubScalar,
nullifier: BlsScalar,
signature: SignatureDouble,
pub(crate) note: Note,
pub(crate) note_pk_p: JubJubAffine,
pub(crate) value: u64,
pub(crate) blinding_factor: JubJubScalar,
pub(crate) nullifier: BlsScalar,
pub(crate) signature: SignatureDouble,
}

/// TBC
Expand Down Expand Up @@ -85,9 +82,9 @@ impl<const H: usize, const A: usize> InputNote<H, A> {
/// TBC
#[derive(Debug, Clone)]
pub struct OutputNote {
value: u64,
value_commitment: JubJubAffine,
blinding_factor: JubJubScalar,
pub(crate) value: u64,
pub(crate) value_commitment: JubJubAffine,
pub(crate) blinding_factor: JubJubScalar,
}

/// TBC
Expand Down Expand Up @@ -121,15 +118,15 @@ const OUTPUT: usize = 2;

/// Transfer
// TODO: return result
pub fn transfer<const H: usize, const A: usize, const I: usize>(
pub fn gadget<const H: usize, const A: usize, const I: usize>(
composer: &mut Composer,
input_notes: [InputNote<H, A>; I],
output_notes: [OutputNote; OUTPUT],
input_notes: &[InputNote<H, A>; I],
output_notes: &[OutputNote; OUTPUT],
skeleton_hash: BlsScalar,
root: BlsScalar,
tree: &Tree<(), H, A>,
) {
let skeleton_hash_pi = composer.append_public(skeleton_hash);
let root_pi = composer.append_public(root);
let root_pi = composer.append_public(tree.root().hash);

let mut input_notes_sum = Composer::ZERO;

Expand Down Expand Up @@ -188,17 +185,20 @@ pub fn transfer<const H: usize, const A: usize, const I: usize>(
let note_hash = sponge::gadget(
composer,
&[
w_input_note.note_type,
*value_commitment.x(),
*value_commitment.y(),
w_input_note.note_type,
w_input_note.pos,
*w_input_note.note_pk.x(),
*w_input_note.note_pk.y(),
w_input_note.pos,
],
);

// VERIFY THE MERKLE OPENING
let root = opening_gadget(composer, &input_note.opening, note_hash);
let merkle_opening = tree
.opening(*input_note.note.pos())
.expect("Tree was read successfully");
let root = opening_gadget(composer, &merkle_opening, note_hash);
composer.assert_equal(root, root_pi);
}

Expand Down
159 changes: 159 additions & 0 deletions tests/transfer_gadget.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) DUSK NETWORK. All rights reserved.

use rand_core::{CryptoRng, OsRng, RngCore};

use phoenix_core::transfer::{gadget, InputNote, OutputNote};
use phoenix_core::{Note, PublicKey, SecretKey, ViewKey};
use poseidon_merkle::{Item, Tree};

use dusk_plonk::prelude::*;

static LABEL: &[u8; 12] = b"dusk-network";
const CAPACITY: usize = 15; // capacity required for the setup
const OUTPUT: usize = 2;
const H: usize = 2;
const A: usize = 4;
const I: usize = 2;

#[derive(Debug)]
pub struct TransferCircuit {
input_notes: [InputNote<H, A>; I],
output_notes: [OutputNote; OUTPUT],
skeleton_hash: BlsScalar,
tree: Tree<(), H, A>,
}

impl Default for TransferCircuit {
fn default() -> Self {
let mut rng = OsRng;
let sk = SecretKey::random(&mut rng);

let mut tree = Tree::<(), H, A>::new();

let input_note_1 = create_test_input_note(&mut tree, 0, &sk, &mut rng);
let input_note_2 = create_test_input_note(&mut tree, 1, &sk, &mut rng);

let input_notes = [input_note_1, input_note_2];

let output_note_1 = create_test_output_note(&sk, &mut rng);
let output_note_2 = create_test_output_note(&sk, &mut rng);

let output_notes = [output_note_1, output_note_2];

let skeleton_hash = BlsScalar::default();

Self {
input_notes,
output_notes,
skeleton_hash,
tree,
}
}
}

impl TransferCircuit {
pub fn new(
input_notes: [InputNote<H, A>; I],
output_notes: [OutputNote; OUTPUT],
skeleton_hash: BlsScalar,
tree: Tree<(), H, A>,
) -> Self {
Self {
input_notes,
output_notes,
skeleton_hash,
tree,
}
}
}

impl Circuit for TransferCircuit {
fn circuit(&self, composer: &mut Composer) -> Result<(), Error> {
gadget::<H, A, I>(
composer,
&self.input_notes,
&self.output_notes,
self.skeleton_hash,
&self.tree,
);
Ok(())
}
}

fn create_test_input_note<const H: usize, const A: usize>(
tree: &mut Tree<(), H, A>,
pos: u64,
sk: &SecretKey,
rng: &mut (impl RngCore + CryptoRng),
) -> InputNote<H, A> {
let pk = PublicKey::from(sk);
let value = 25;

let mut note = Note::transparent(rng, &pk, value);

note.set_pos(pos);

let item = Item {
hash: note.hash(),
data: (),
};

tree.insert(*note.pos(), item);

let skeleton_hash = BlsScalar::from(1234u64);

note.create_input_note(&sk, skeleton_hash, rng)
}

fn create_test_output_note(
sk: &SecretKey,
rng: &mut (impl RngCore + CryptoRng),
) -> OutputNote {
let note = Note::transparent(rng, &PublicKey::from(sk), 25);
note.create_output_note(&ViewKey::from(sk))
}

#[test]
fn test_transfer_circuit() {
let mut rng = OsRng;
let sk = SecretKey::random(&mut rng);

let mut tree = Tree::<(), H, A>::new();

let input_note_1 = create_test_input_note(&mut tree, 0, &sk, &mut rng);
let input_note_2 = create_test_input_note(&mut tree, 1, &sk, &mut rng);

let input_notes = [input_note_1, input_note_2];

let output_note_1 = create_test_output_note(&sk, &mut rng);
let output_note_2 = create_test_output_note(&sk, &mut rng);

let output_notes = [output_note_1, output_note_2];

let skeleton_hash = BlsScalar::from(1234u64);

let pp = PublicParameters::setup(1 << CAPACITY, &mut OsRng).unwrap();

let (prover, verifier) = Compiler::compile::<TransferCircuit>(&pp, LABEL)
.expect("failed to compile circuit");

let (proof, public_inputs) = prover
.prove(
&mut OsRng,
&TransferCircuit::new(
input_notes,
output_notes,
skeleton_hash,
tree,
),
)
.expect("failed to prove");

verifier
.verify(&proof, &public_inputs)
.expect("failed to verify proof");
}

0 comments on commit b6001b5

Please sign in to comment.