Skip to content

Commit

Permalink
WIP: Add circuit 3
Browse files Browse the repository at this point in the history
  • Loading branch information
xevisalle committed May 15, 2024
1 parent b6001b5 commit 0c7c9c3
Show file tree
Hide file tree
Showing 5 changed files with 349 additions and 165 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ aes-gcm = "0.10"
zeroize = { version = "1", default-features = false, features = ["derive"] }
rkyv = { version = "0.7", optional = true, default-features = false }
bytecheck = { version = "0.6", optional = true, default-features = false }
rand = "0.8"

[dev-dependencies]
assert_matches = "1.3"
rand = "0.8"
rkyv = { version = "0.7", default-features = false, features = ["size_32"] }
lazy_static = "1.4"

[features]
default = [] # "alloc" is suggested as default feature but would be breaking change
Expand Down
31 changes: 15 additions & 16 deletions src/note.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,50 +326,49 @@ impl Note {
}

#[cfg(feature = "zk")]
/// TBC
/// Create a circuit input note
pub fn create_input_note<const H: usize, const A: usize>(
&self,
merkle_opening: poseidon_merkle::Opening<(), H, A>,
sk: &SecretKey,
skeleteon_hash: BlsScalar,
rng: &mut (impl RngCore + CryptoRng),
) -> crate::transfer::InputNote<H, A> {
) -> Result<crate::transfer::InputNote<H, A>, Error> {
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 value = self.value(Some(&vk))?;
let blinding_factor = self.blinding_factor(Some(&vk))?;

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 {
Ok(crate::transfer::InputNote {
merkle_opening,
note: self.clone(),
note_pk_p: note_pk_p.into(),
note_pk_p,
value,
blinding_factor,
nullifier,
signature,
}
})
}

#[cfg(feature = "zk")]
/// TBC
/// TODO: return result
/// Create a circuit output note
pub fn create_output_note(
&self,
vk: &ViewKey,
) -> crate::transfer::OutputNote {
crate::transfer::OutputNote {
value: self.value(Some(&vk)).unwrap(),
) -> Result<crate::transfer::OutputNote, Error> {
Ok(crate::transfer::OutputNote {
value: self.value(Some(vk))?,
value_commitment: self.value_commitment.into(),
blinding_factor: self.blinding_factor(Some(&vk)).unwrap(),
}
blinding_factor: self.blinding_factor(Some(vk))?,
})
}
}

Expand Down
219 changes: 164 additions & 55 deletions src/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,24 @@ use dusk_jubjub::{JubJubScalar, GENERATOR, GENERATOR_NUMS};
use dusk_plonk::prelude::*;
use dusk_poseidon::sponge;
use jubjub_schnorr::{gadgets, SignatureDouble};
use poseidon_merkle::{zk::opening_gadget, Tree};
use poseidon_merkle::{zk::opening_gadget, Item, Opening, Tree};

use rand::rngs::StdRng;
use rand_core::SeedableRng;

extern crate alloc;
use alloc::vec::Vec;

use crate::Note;
use crate::{SecretKey, ViewKey};

const OUTPUT: usize = 2;

/// TBC
/// Struct representing a note willing to be spent, in a way
/// suitable for being introduced in the transfer circuit
#[derive(Debug, Clone)]
pub struct InputNote<const H: usize, const A: usize> {
pub(crate) merkle_opening: Opening<(), H, A>,
pub(crate) note: Note,
pub(crate) note_pk_p: JubJubAffine,
pub(crate) value: u64,
Expand All @@ -23,9 +34,8 @@ pub struct InputNote<const H: usize, const A: usize> {
pub(crate) signature: SignatureDouble,
}

/// TBC
#[derive(Debug, Clone)]
pub struct WitnessInputNote {
struct WitnessInputNote {
note_pk: WitnessPoint,
note_pk_p: WitnessPoint,
note_type: Witness,
Expand All @@ -39,11 +49,7 @@ pub struct WitnessInputNote {
}

impl<const H: usize, const A: usize> InputNote<H, A> {
/// TBC
pub fn append_to_circuit(
&self,
composer: &mut Composer,
) -> WitnessInputNote {
fn append_to_circuit(&self, composer: &mut Composer) -> WitnessInputNote {
let nullifier = composer.append_public(self.nullifier);

let note_pk = composer
Expand Down Expand Up @@ -79,28 +85,24 @@ impl<const H: usize, const A: usize> InputNote<H, A> {
}
}

/// TBC
/// Struct representing a note willing to be created, in a way
/// suitable for being introduced in the transfer circuit
#[derive(Debug, Clone)]
pub struct OutputNote {
pub(crate) value: u64,
pub(crate) value_commitment: JubJubAffine,
pub(crate) blinding_factor: JubJubScalar,
}

/// TBC
#[derive(Debug, Clone)]
pub struct WitnessOutputNote {
struct WitnessOutputNote {
value: Witness,
value_commitment: WitnessPoint,
blinding_factor: Witness,
}

impl OutputNote {
/// TBC
pub fn append_to_circuit(
&self,
composer: &mut Composer,
) -> WitnessOutputNote {
fn append_to_circuit(&self, composer: &mut Composer) -> WitnessOutputNote {
let value = composer.append_witness(self.value);
let value_commitment =
composer.append_public_point(self.value_commitment);
Expand All @@ -114,19 +116,19 @@ impl OutputNote {
}
}

const OUTPUT: usize = 2;

/// Transfer
// TODO: return result
/// Transfer gadget expecting I input notes to be spent and O output
/// notes to be created.
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],
skeleton_hash: BlsScalar,
tree: &Tree<(), H, A>,
) {
let skeleton_hash_pi = composer.append_public(skeleton_hash);
let root_pi = composer.append_public(tree.root().hash);
skeleton_hash: &BlsScalar,
root: &BlsScalar,
crossover: u64,
max_fee: u64,
) -> Result<(), Error> {
let skeleton_hash_pi = composer.append_public(*skeleton_hash);
let root_pi = composer.append_public(*root);

let mut input_notes_sum = Composer::ZERO;

Expand All @@ -144,8 +146,7 @@ pub fn gadget<const H: usize, const A: usize, const I: usize>(
w_input_note.note_pk,
w_input_note.note_pk_p,
skeleton_hash_pi,
)
.unwrap();
)?;

// COMPUTE AND ASSERT THE NULLIFIER
let nullifier = sponge::gadget(
Expand All @@ -170,15 +171,12 @@ pub fn gadget<const H: usize, const A: usize, const I: usize>(
input_notes_sum = composer.gate_add(constraint);

// COMMIT TO THE VALUE OF THE NOTE
let pc_1 = composer
.component_mul_generator(w_input_note.value, GENERATOR)
.unwrap();
let pc_2 = composer
.component_mul_generator(
w_input_note.blinding_factor,
GENERATOR_NUMS,
)
.unwrap();
let pc_1 =
composer.component_mul_generator(w_input_note.value, GENERATOR)?;
let pc_2 = composer.component_mul_generator(
w_input_note.blinding_factor,
GENERATOR_NUMS,
)?;
let value_commitment = composer.component_add_point(pc_1, pc_2);

// COMPUTE THE NOTE HASH
Expand All @@ -195,14 +193,12 @@ pub fn gadget<const H: usize, const A: usize, const I: usize>(
);

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

let mut output_notes_sum = Composer::ZERO;
let mut output_sum = Composer::ZERO;

// COMMIT TO ALL OUTPUT NOTES
for output_note in output_notes {
Expand All @@ -215,21 +211,18 @@ pub fn gadget<const H: usize, const A: usize, const I: usize>(
// SUM UP ALL THE CREATED NOTE VALUES
let constraint = Constraint::new()
.left(1)
.a(output_notes_sum)
.a(output_sum)
.right(1)
.b(w_output_note.value);
output_notes_sum = composer.gate_add(constraint);
output_sum = composer.gate_add(constraint);

// COMMIT TO THE VALUE OF THE NOTE
let pc_1 = composer
.component_mul_generator(w_output_note.value, GENERATOR)
.unwrap();
let pc_2 = composer
.component_mul_generator(
w_output_note.blinding_factor,
GENERATOR_NUMS,
)
.unwrap();
let pc_1 =
composer.component_mul_generator(w_output_note.value, GENERATOR)?;
let pc_2 = composer.component_mul_generator(
w_output_note.blinding_factor,
GENERATOR_NUMS,
)?;
let value_commitment = composer.component_add_point(pc_1, pc_2);

composer.assert_equal_point(
Expand All @@ -238,7 +231,123 @@ pub fn gadget<const H: usize, const A: usize, const I: usize>(
);
}

let max_fee = composer.append_public(max_fee);
let crossover = composer.append_public(crossover);

// SUM UP THE CROSSOVER AND THE MAX FEE
let constraint = Constraint::new()
.left(1)
.a(output_sum)
.right(1)
.b(max_fee)
.fourth(1)
.d(crossover);
output_sum = composer.gate_add(constraint);

// VERIFY BALANCE
// TODO: insert crossover and fee
composer.assert_equal(input_notes_sum, output_notes_sum);
composer.assert_equal(input_notes_sum, output_sum);

Ok(())
}

/// Declaration of the transfer circuit
#[derive(Debug)]
pub struct TransferCircuit<const H: usize, const A: usize, const I: usize> {
input_notes: [InputNote<H, A>; I],
output_notes: [OutputNote; OUTPUT],
skeleton_hash: BlsScalar,
root: BlsScalar,
crossover: u64,
max_fee: u64,
}

impl<const H: usize, const A: usize, const I: usize> Default
for TransferCircuit<H, A, I>
{
fn default() -> Self {
let mut rng = StdRng::seed_from_u64(0xbeef);

let sk = SecretKey::random(&mut rng);
let vk = ViewKey::from(&sk);

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

let mut input_notes = Vec::new();
let note = Note::empty();
let item = Item {
hash: note.hash(),
data: (),
};
tree.insert(*note.pos(), item);

for _ in 0..I {
let merkle_opening = tree.opening(*note.pos()).expect("Tree read.");
let input_note = note
.create_input_note(merkle_opening, &sk, skeleton_hash, &mut rng)
.expect("Note created properly.");

input_notes.push(input_note);
}

let output_note_1 = note
.create_output_note(&vk)
.expect("Note created properly.");
let output_note_2 = note
.create_output_note(&vk)
.expect("Note created properly.");

let output_notes = [output_note_1, output_note_2];

let root = BlsScalar::default();
let crossover = u64::default();
let max_fee = u64::default();

Self {
input_notes: input_notes.try_into().unwrap(),
output_notes,
skeleton_hash,
root,
crossover,
max_fee,
}
}
}

impl<const H: usize, const A: usize, const I: usize> TransferCircuit<H, A, I> {
/// Create a new transfer circuit
pub fn new(
input_notes: [InputNote<H, A>; I],
output_notes: [OutputNote; OUTPUT],
skeleton_hash: BlsScalar,
root: BlsScalar,
crossover: u64,
max_fee: u64,
) -> Self {
Self {
input_notes,
output_notes,
skeleton_hash,
root,
crossover,
max_fee,
}
}
}

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

0 comments on commit 0c7c9c3

Please sign in to comment.