diff --git a/circuits/CHANGELOG.md b/circuits/CHANGELOG.md index 59d67c0..62ec516 100644 --- a/circuits/CHANGELOG.md +++ b/circuits/CHANGELOG.md @@ -10,10 +10,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add Recipient gadget [#197] -- Add `RecipientParameter::new` constructor [#201] ### Changed +- Rename `recipient` module to `sender_enc` [#214] +- Rename `blinding_factor` to `value_blinder` [#214] +- Add `sender_enc` field to `TxOutputNote` [#214] +- Add `note_pk` field to `TxOutputNote` [#214] +- Add `sender_pk`, `signatures`, `output_npk` and `sender_blinder` fields to `TxCircuit` [#214] - Remove `ViewKey` from `TxOutputNote::new()` parameters [#191] - Make `rng` the first param in `TxInputNote::new` [#189] - Rename `crossover` to `deposit` [#190] @@ -23,7 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed -- Remove `RecipientParameters` +- Remove `WitnessTxOutputNote` struct [#214] +- Remove `RecipientParameters` struct [#214] - Remove `elgamal::encrypt` and `elgamal::decrypt` ## [0.1.0] - 2024-05-22 @@ -42,6 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Update `poseidon-merkle` to v0.6 [#179] +[#214]: https://github.com/dusk-network/phoenix/issues/214 [#201]: https://github.com/dusk-network/phoenix/issues/201 [#197]: https://github.com/dusk-network/phoenix/issues/197 [#188]: https://github.com/dusk-network/phoenix/issues/188 diff --git a/circuits/src/lib.rs b/circuits/src/lib.rs index 45859db..3a662e7 100644 --- a/circuits/src/lib.rs +++ b/circuits/src/lib.rs @@ -11,7 +11,7 @@ #![no_std] mod encryption; -mod recipient; +mod sender_enc; /// Transaction structs, and circuit pub mod transaction; diff --git a/circuits/src/recipient.rs b/circuits/src/recipient.rs deleted file mode 100644 index 8a04726..0000000 --- a/circuits/src/recipient.rs +++ /dev/null @@ -1,65 +0,0 @@ -// 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. - -#![allow(non_snake_case)] - -use dusk_plonk::prelude::*; -use jubjub_schnorr::gadgets; -use phoenix_core::RecipientParameters; - -use crate::elgamal; - -/// Gadget to prove a valid origin for a given transaction. -pub(crate) fn gadget( - composer: &mut Composer, - rp: &RecipientParameters, - payload_hash: Witness, -) -> Result<(), Error> { - // VERIFY A SIGNATURE FOR EACH KEY 'A' AND 'B' - let pk_A = composer.append_point(rp.sender_pk.A()); - let pk_B = composer.append_point(rp.sender_pk.B()); - - let sig_A_u = composer.append_witness(*rp.sig[0].u()); - let sig_A_R = composer.append_point(rp.sig[0].R()); - - let sig_B_u = composer.append_witness(*rp.sig[1].u()); - let sig_B_R = composer.append_point(rp.sig[1].R()); - - gadgets::verify_signature(composer, sig_A_u, sig_A_R, pk_A, payload_hash)?; - gadgets::verify_signature(composer, sig_B_u, sig_B_R, pk_B, payload_hash)?; - - // ENCRYPT EACH KEY 'A' and 'B' USING EACH OUTPUT 'NPK' - let note_pk_1 = composer.append_public_point(rp.output_npk[0]); - let note_pk_2 = composer.append_public_point(rp.output_npk[1]); - - let r_A_1 = composer.append_witness(rp.r_A[0]); - let r_A_2 = composer.append_witness(rp.r_A[1]); - - let r_B_1 = composer.append_witness(rp.r_B[0]); - let r_B_2 = composer.append_witness(rp.r_B[1]); - - let (enc_A_1_c1, enc_A_1_c2) = - elgamal::encrypt_gadget(composer, note_pk_1, pk_A, r_A_1)?; - let (enc_A_2_c1, enc_A_2_c2) = - elgamal::encrypt_gadget(composer, note_pk_2, pk_A, r_A_2)?; - - let (enc_B_1_c1, enc_B_1_c2) = - elgamal::encrypt_gadget(composer, note_pk_1, pk_B, r_B_1)?; - let (enc_B_2_c1, enc_B_2_c2) = - elgamal::encrypt_gadget(composer, note_pk_2, pk_B, r_B_2)?; - - composer.assert_equal_public_point(enc_A_1_c1, rp.enc_A[0].0); - composer.assert_equal_public_point(enc_A_1_c2, rp.enc_A[0].1); - composer.assert_equal_public_point(enc_A_2_c1, rp.enc_A[1].0); - composer.assert_equal_public_point(enc_A_2_c2, rp.enc_A[1].1); - - composer.assert_equal_public_point(enc_B_1_c1, rp.enc_B[0].0); - composer.assert_equal_public_point(enc_B_1_c2, rp.enc_B[0].1); - composer.assert_equal_public_point(enc_B_2_c1, rp.enc_B[1].0); - composer.assert_equal_public_point(enc_B_2_c2, rp.enc_B[1].1); - - Ok(()) -} diff --git a/circuits/src/sender_enc.rs b/circuits/src/sender_enc.rs new file mode 100644 index 0000000..2987752 --- /dev/null +++ b/circuits/src/sender_enc.rs @@ -0,0 +1,115 @@ +// 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. + +#![allow(non_snake_case)] + +use dusk_jubjub::JubJubAffine; +use dusk_plonk::prelude::*; +use jubjub_schnorr::{gadgets, Signature as SchnorrSignature}; +use phoenix_core::{PublicKey, OUTPUT_NOTES}; + +use crate::elgamal; + +/// Gadget to prove a valid origin for a given transaction. +pub(crate) fn gadget( + composer: &mut Composer, + sender_pk: PublicKey, + signatures: (SchnorrSignature, SchnorrSignature), + output_npk: [JubJubAffine; OUTPUT_NOTES], + sender_blinder: [(JubJubScalar, JubJubScalar); OUTPUT_NOTES], + // [enc_A, enc_B] for note 0 + sender_enc_out0: [(JubJubAffine, JubJubAffine); 2], + // [enc_A, enc_B] for note 1 + sender_enc_out1: [(JubJubAffine, JubJubAffine); 2], + payload_hash: Witness, +) -> Result<(), Error> { + // VERIFY A SIGNATURE FOR EACH KEY 'A' AND 'B' + let sender_pk_A = composer.append_point(sender_pk.A()); + let sender_pk_B = composer.append_point(sender_pk.B()); + + let sig_A_u = composer.append_witness(*signatures.0.u()); + let sig_A_R = composer.append_point(signatures.0.R()); + + let sig_B_u = composer.append_witness(*signatures.1.u()); + let sig_B_R = composer.append_point(signatures.1.R()); + + gadgets::verify_signature( + composer, + sig_A_u, + sig_A_R, + sender_pk_A, + payload_hash, + )?; + gadgets::verify_signature( + composer, + sig_B_u, + sig_B_R, + sender_pk_B, + payload_hash, + )?; + + // ENCRYPT EACH KEY 'A' and 'B' USING EACH OUTPUT 'NPK' + let note_pk_0 = composer.append_public_point(output_npk[0]); + let note_pk_1 = composer.append_public_point(output_npk[1]); + + let blinder_A_0 = composer.append_witness(sender_blinder[0].0); + let blinder_B_0 = composer.append_witness(sender_blinder[0].1); + + let blinder_A_1 = composer.append_witness(sender_blinder[1].0); + let blinder_B_1 = composer.append_witness(sender_blinder[1].1); + + // assert that the sender encryption of the first note is correct + // appends the values of sender_enc_out0 as public input + assert_sender_enc( + composer, + sender_pk_A, + sender_pk_B, + note_pk_0, + (blinder_A_0, blinder_B_0), + sender_enc_out0, + )?; + + // assert that the sender encryption of the second note is correct + // appends the values of sender_enc_out1 as public input + assert_sender_enc( + composer, + sender_pk_A, + sender_pk_B, + note_pk_1, + (blinder_A_1, blinder_B_1), + sender_enc_out1, + )?; + + Ok(()) +} + +fn assert_sender_enc( + composer: &mut Composer, + sender_pk_A: WitnessPoint, + sender_pk_B: WitnessPoint, + note_pk: WitnessPoint, + blinder: (Witness, Witness), + sender_enc: [(JubJubAffine, JubJubAffine); 2], +) -> Result<(), Error> { + let blinder_A = blinder.0; + let (enc_A_c1, enc_A_c2) = + elgamal::encrypt_gadget(composer, note_pk, sender_pk_A, blinder_A)?; + + let blinder_B = blinder.1; + let (enc_B_c1, enc_B_c2) = + elgamal::encrypt_gadget(composer, note_pk, sender_pk_B, blinder_B)?; + + let sender_enc_A = sender_enc[0]; + let sender_enc_B = sender_enc[1]; + + composer.assert_equal_public_point(enc_A_c1, sender_enc_A.0); + composer.assert_equal_public_point(enc_A_c2, sender_enc_A.1); + + composer.assert_equal_public_point(enc_B_c1, sender_enc_B.0); + composer.assert_equal_public_point(enc_B_c2, sender_enc_B.1); + + Ok(()) +} diff --git a/circuits/src/transaction.rs b/circuits/src/transaction.rs index b4b40f6..854ba75 100644 --- a/circuits/src/transaction.rs +++ b/circuits/src/transaction.rs @@ -10,7 +10,7 @@ use dusk_jubjub::{ }; use dusk_plonk::prelude::*; use dusk_poseidon::{Domain, Hash, HashGadget}; -use jubjub_schnorr::{gadgets, SignatureDouble}; +use jubjub_schnorr::{gadgets, Signature as SchnorrSignature, SignatureDouble}; use poseidon_merkle::{zk::opening_gadget, Item, Opening, Tree}; use rand::rngs::StdRng; @@ -20,12 +20,10 @@ extern crate alloc; use alloc::vec::Vec; use phoenix_core::{ - Error as PhoenixError, Note, RecipientParameters, SecretKey, ViewKey, + Error as PhoenixError, Note, PublicKey, SecretKey, ViewKey, OUTPUT_NOTES, }; -use crate::recipient; - -const TX_OUTPUT_NOTES: usize = 2; +use crate::sender_enc; /// Struct representing a note willing to be spent, in a way /// suitable for being introduced in the transfer circuit @@ -35,7 +33,7 @@ pub struct TxInputNote { pub(crate) note: Note, pub(crate) note_pk_p: JubJubAffine, pub(crate) value: u64, - pub(crate) blinding_factor: JubJubScalar, + pub(crate) value_blinder: JubJubScalar, pub(crate) nullifier: BlsScalar, pub(crate) signature: SignatureDouble, } @@ -47,7 +45,7 @@ struct WitnessTxInputNote { note_type: Witness, pos: Witness, value: Witness, - blinding_factor: Witness, + value_blinder: Witness, nullifier: Witness, signature_u: Witness, signature_r: WitnessPoint, @@ -69,7 +67,7 @@ impl TxInputNote { let vk = ViewKey::from(sk); let value = note.value(Some(&vk))?; - let blinding_factor = note.blinding_factor(Some(&vk))?; + let value_blinder = note.value_blinder(Some(&vk))?; let nullifier = Hash::digest( Domain::Other, @@ -83,7 +81,7 @@ impl TxInputNote { note: note.clone(), note_pk_p, value, - blinding_factor, + value_blinder, nullifier, signature, }) @@ -101,7 +99,7 @@ impl TxInputNote { let pos = composer.append_witness(BlsScalar::from(*self.note.pos())); let value = composer.append_witness(self.value); - let blinding_factor = composer.append_witness(self.blinding_factor); + let value_blinder = composer.append_witness(self.value_blinder); let signature_u = composer.append_witness(*self.signature.u()); let signature_r = composer.append_point(self.signature.R()); @@ -114,7 +112,7 @@ impl TxInputNote { note_type, pos, value, - blinding_factor, + value_blinder, nullifier, @@ -131,14 +129,9 @@ impl TxInputNote { pub struct TxOutputNote { value: u64, value_commitment: JubJubAffine, - blinding_factor: JubJubScalar, -} - -#[derive(Debug, Clone)] -struct WitnessTxOutputNote { - value: Witness, - value_commitment: WitnessPoint, - blinding_factor: Witness, + value_blinder: JubJubScalar, + note_pk: JubJubAffine, + sender_enc: [(JubJubAffine, JubJubAffine); 2], } impl TxOutputNote { @@ -146,34 +139,22 @@ impl TxOutputNote { pub fn new( value: u64, value_commitment: JubJubAffine, - blinding_factor: JubJubScalar, + value_blinder: JubJubScalar, + note_pk: JubJubAffine, + sender_enc: [(JubJubAffine, JubJubAffine); 2], ) -> Self { Self { value, value_commitment, - blinding_factor, - } - } - - fn append_to_circuit( - &self, - composer: &mut Composer, - ) -> WitnessTxOutputNote { - let value = composer.append_witness(self.value); - let value_commitment = - composer.append_public_point(self.value_commitment); - let blinding_factor = composer.append_witness(self.blinding_factor); - - WitnessTxOutputNote { - value, - value_commitment, - blinding_factor, + value_blinder, + note_pk, + sender_enc, } } } /// Transaction gadget proving the following properties in ZK for a generic -/// `I` [`TxInputNote`] and [`TX_OUTPUT_NOTES`] (2) [`TxOutputNote`]: +/// `I` [`TxInputNote`] and [`OUTPUT_NOTES`] (2) [`TxOutputNote`]: /// /// 1. Membership: every [`TxInputNote`] is included in the Merkle tree of /// notes. @@ -197,7 +178,7 @@ fn nullify_gadget( payload_hash: &Witness, root: &BlsScalar, tx_input_notes: &[TxInputNote; I], - tx_output_notes: &[TxOutputNote; TX_OUTPUT_NOTES], + tx_output_notes: &[TxOutputNote; OUTPUT_NOTES], max_fee: u64, deposit: u64, ) -> Result<(), Error> { @@ -248,7 +229,7 @@ fn nullify_gadget( let pc_1 = composer .component_mul_generator(w_tx_input_note.value, GENERATOR)?; let pc_2 = composer.component_mul_generator( - w_tx_input_note.blinding_factor, + w_tx_input_note.value_blinder, GENERATOR_NUMS, )?; let value_commitment = composer.component_add_point(pc_1, pc_2); @@ -278,31 +259,30 @@ fn nullify_gadget( // COMMIT TO ALL TX OUTPUT NOTES for tx_output_note in tx_output_notes { // APPEND THE WITNESSES TO THE CIRCUIT - let w_tx_output_note = tx_output_note.append_to_circuit(composer); + let value = composer.append_witness(tx_output_note.value); + let expected_value_commitment = + composer.append_public_point(tx_output_note.value_commitment); + let value_blinder = + composer.append_witness(tx_output_note.value_blinder); // PERFORM A RANGE CHECK ([0, 2^64 - 1]) ON THE VALUE OF THE NOTE - composer.component_range::<32>(w_tx_output_note.value); + composer.component_range::<32>(value); // SUM UP ALL THE TX OUTPUT NOTE VALUES - let constraint = Constraint::new() - .left(1) - .a(tx_output_sum) - .right(1) - .b(w_tx_output_note.value); + let constraint = + Constraint::new().left(1).a(tx_output_sum).right(1).b(value); tx_output_sum = composer.gate_add(constraint); // COMMIT TO THE VALUE OF THE NOTE - let pc_1 = composer - .component_mul_generator(w_tx_output_note.value, GENERATOR)?; - let pc_2 = composer.component_mul_generator( - w_tx_output_note.blinding_factor, - GENERATOR_NUMS, - )?; - let value_commitment = composer.component_add_point(pc_1, pc_2); + let pc_1 = composer.component_mul_generator(value, GENERATOR)?; + let pc_2 = + composer.component_mul_generator(value_blinder, GENERATOR_NUMS)?; + let computed_value_commitment = + composer.component_add_point(pc_1, pc_2); composer.assert_equal_point( - w_tx_output_note.value_commitment, - value_commitment, + expected_value_commitment, + computed_value_commitment, ); } @@ -329,12 +309,14 @@ fn nullify_gadget( #[derive(Debug)] pub struct TxCircuit { tx_input_notes: [TxInputNote; I], - tx_output_notes: [TxOutputNote; TX_OUTPUT_NOTES], + tx_output_notes: [TxOutputNote; OUTPUT_NOTES], payload_hash: BlsScalar, root: BlsScalar, deposit: u64, max_fee: u64, - rp: RecipientParameters, + sender_pk: PublicKey, + signatures: (SchnorrSignature, SchnorrSignature), + sender_blinder: [(JubJubScalar, JubJubScalar); OUTPUT_NOTES], } impl Default for TxCircuit { @@ -370,7 +352,9 @@ impl Default for TxCircuit { let tx_output_note_1 = TxOutputNote { value: 0, value_commitment: JubJubAffine::default(), - blinding_factor: JubJubScalar::default(), + value_blinder: JubJubScalar::default(), + note_pk: JubJubAffine::default(), + sender_enc: [(JubJubAffine::default(), JubJubAffine::default()); 2], }; let tx_output_note_2 = tx_output_note_1.clone(); @@ -380,7 +364,10 @@ impl Default for TxCircuit { let deposit = u64::default(); let max_fee = u64::default(); - let rp = RecipientParameters::default(); + let signatures = + (SchnorrSignature::default(), SchnorrSignature::default()); + let sender_blinder = + [(JubJubScalar::default(), JubJubScalar::default()); OUTPUT_NOTES]; Self { tx_input_notes: tx_input_notes.try_into().unwrap(), @@ -389,7 +376,9 @@ impl Default for TxCircuit { root, deposit, max_fee, - rp, + sender_pk: PublicKey::from(&sk), + signatures, + sender_blinder, } } } @@ -398,12 +387,14 @@ impl TxCircuit { /// Create a new transfer circuit pub fn new( tx_input_notes: [TxInputNote; I], - tx_output_notes: [TxOutputNote; TX_OUTPUT_NOTES], + tx_output_notes: [TxOutputNote; OUTPUT_NOTES], payload_hash: BlsScalar, root: BlsScalar, deposit: u64, max_fee: u64, - rp: RecipientParameters, + sender_pk: PublicKey, + signatures: (SchnorrSignature, SchnorrSignature), + sender_blinder: [(JubJubScalar, JubJubScalar); OUTPUT_NOTES], ) -> Self { Self { tx_input_notes, @@ -412,7 +403,9 @@ impl TxCircuit { root, deposit, max_fee, - rp, + sender_pk, + signatures, + sender_blinder, } } } @@ -425,9 +418,9 @@ impl Circuit for TxCircuit { /// - `[output_value_commitment; 2]` /// - `max_fee` /// - `deposit` - /// - `(npk_1, npk_2)` - /// - `(enc_A_npk_1, enc_A_npk_2)` - /// - `(enc_B_npk_1, enc_B_npk_2)` + /// - `(npk_0, npk_1)` + /// - `(enc_A_npk_0, enc_B_npk_0)` + /// - `(enc_A_npk_1, enc_B_npk_1)` fn circuit(&self, composer: &mut Composer) -> Result<(), Error> { // Make the payload hash a public input of the circuit let payload_hash = composer.append_public(self.payload_hash); @@ -444,7 +437,19 @@ impl Circuit for TxCircuit { )?; // Prove correctness of the sender keys encryption - recipient::gadget(composer, &self.rp, payload_hash)?; + sender_enc::gadget( + composer, + self.sender_pk, + self.signatures, + [ + self.tx_output_notes[0].note_pk, + self.tx_output_notes[1].note_pk, + ], + self.sender_blinder, + self.tx_output_notes[0].sender_enc, + self.tx_output_notes[1].sender_enc, + payload_hash, + )?; Ok(()) } diff --git a/circuits/tests/elgamal.rs b/circuits/tests/elgamal.rs index ca3350b..d08eb0e 100644 --- a/circuits/tests/elgamal.rs +++ b/circuits/tests/elgamal.rs @@ -12,28 +12,6 @@ use phoenix_core::{elgamal, PublicKey, SecretKey}; use rand::rngs::StdRng; use rand::SeedableRng; -#[test] -fn test_elgamal_encrypt_and_decrypt() { - let mut rng = StdRng::seed_from_u64(0xc0b); - - let sk = SecretKey::random(&mut rng); - let pk = PublicKey::from(&sk); - - let message = GENERATOR_EXTENDED * JubJubScalar::from(1234u64); - - // Encrypt using a fresh random value 'r' - let r = JubJubScalar::random(&mut rng); - let (c1, c2) = elgamal::encrypt(pk.A(), &message, &r); - - // Assert decryption - let dec_message = elgamal::decrypt(sk.a(), &c1, &c2); - assert_eq!(message, dec_message); - - // Assert decryption using an incorrect key - let dec_message_wrong = elgamal::decrypt(sk.b(), &c1, &c2); - assert_ne!(message, dec_message_wrong); -} - static LABEL: &[u8; 12] = b"dusk-network"; const CAPACITY: usize = 13; // capacity required for the setup @@ -113,7 +91,14 @@ fn test_elgamal_gadgets() { let (proof, public_inputs) = prover .prove( &mut rng, - &ElGamalCircuit::new(&pk.A(), &sk.a(), &message, &r, &c1, &c2), + &ElGamalCircuit::new( + &pk.A(), + &sk.a(), + &message, + &r, + &c1.into(), + &c2.into(), + ), ) .expect("failed to prove"); diff --git a/circuits/tests/transaction.rs b/circuits/tests/transaction.rs index 07e1e5a..f86617b 100644 --- a/circuits/tests/transaction.rs +++ b/circuits/tests/transaction.rs @@ -11,11 +11,14 @@ use rand::{CryptoRng, RngCore}; use dusk_jubjub::JubJubScalar; use dusk_plonk::prelude::*; use ff::Field; +use jubjub_schnorr::{ + SecretKey as SchnorrSecretKey, Signature as SchnorrSignature, +}; use poseidon_merkle::{Item, Tree}; use phoenix_circuits::transaction::{TxCircuit, TxInputNote, TxOutputNote}; use phoenix_core::{ - value_commitment, Note, PublicKey, RecipientParameters, SecretKey, + elgamal, value_commitment, Note, PublicKey, SecretKey, OUTPUT_NOTES, }; #[macro_use] @@ -32,7 +35,10 @@ struct TestingParameters { root: BlsScalar, deposit: u64, max_fee: u64, - rp: RecipientParameters, + sender_pk: PublicKey, + output_npk: [JubJubAffine; OUTPUT_NOTES], + signatures: (SchnorrSignature, SchnorrSignature), + sender_blinder: [(JubJubScalar, JubJubScalar); OUTPUT_NOTES], } lazy_static! { @@ -48,8 +54,12 @@ lazy_static! { let payload_hash = BlsScalar::from(1234u64); // create and insert into the tree 4 testing tx input notes - let tx_input_notes = - create_test_tx_input_notes::<4>(&mut rng, &mut tree, &sender_sk, payload_hash); + let tx_input_notes = create_test_tx_input_notes::<4>( + &mut rng, + &mut tree, + &sender_sk, + payload_hash + ); // retrieve the root from the tree after inserting the notes let root = tree.root().hash; @@ -57,24 +67,48 @@ lazy_static! { let deposit = 5; let max_fee = 5; - // We use the same 'sk' just for testing. let sender_pk = PublicKey::from(&sender_sk); - let recipient_pk = PublicKey::from(&SecretKey::random(&mut rng)); + let receiver_pk = PublicKey::from(&SecretKey::random(&mut rng)); - let recipient_npk = *recipient_pk.gen_stealth_address( + // generate both ouput note public keys + let receiver_npk = *receiver_pk.gen_stealth_address( &JubJubScalar::random(&mut rng) ).note_pk().as_ref(); let sender_npk = *sender_pk.gen_stealth_address( &JubJubScalar::random(&mut rng) ).note_pk().as_ref(); let output_npk = [ - JubJubAffine::from(recipient_npk), + JubJubAffine::from(receiver_npk), JubJubAffine::from(sender_npk), ]; - let rp = RecipientParameters::new(&mut rng, &sender_sk, output_npk, payload_hash); - - TestingParameters { pp, tx_input_notes, payload_hash, root, deposit, max_fee, rp } + // Sign the payload hash using both 'a' and 'b' of the sender_sk + let schnorr_sk_a = SchnorrSecretKey::from(sender_sk.a()); + let sig_a = schnorr_sk_a.sign(&mut rng, payload_hash); + let schnorr_sk_b = SchnorrSecretKey::from(sender_sk.b()); + let sig_b = schnorr_sk_b.sign(&mut rng, payload_hash); + + let sender_blinder_0 = ( + JubJubScalar::random(&mut rng), + JubJubScalar::random(&mut rng), + ); + let sender_blinder_1 = ( + JubJubScalar::random(&mut rng), + JubJubScalar::random(&mut rng), + ); + + TestingParameters { + pp, + tx_input_notes, + payload_hash, + root, + deposit, + max_fee, + sender_pk, + output_npk, + signatures: (sig_a, sig_b), + sender_blinder: [sender_blinder_0, sender_blinder_1] + } }; } @@ -85,7 +119,11 @@ fn create_and_insert_test_note( pos: u64, value: u64, ) -> Note { - let mut note = Note::transparent(rng, pk, value); + let sender_blinder = [ + JubJubScalar::random(&mut *rng), + JubJubScalar::random(&mut *rng), + ]; + let mut note = Note::transparent(rng, pk, value, sender_blinder); note.set_pos(pos); let item = Item { @@ -129,6 +167,36 @@ fn create_test_tx_input_notes( input_notes.try_into().unwrap() } +fn create_tx_output_note( + rng: &mut (impl RngCore + CryptoRng), + value: u64, + note_pk: JubJubAffine, + // (blinder_A, blinder_B) + sender_blinder: (JubJubScalar, JubJubScalar), +) -> TxOutputNote { + let value_blinder = JubJubScalar::random(&mut *rng); + let value_commitment = value_commitment(value, value_blinder); + + let sender_blinder_a = sender_blinder.0; + let sender_enc_a = + elgamal::encrypt(¬e_pk.into(), TP.sender_pk.A(), &sender_blinder_a); + + let sender_blinder_b = sender_blinder.1; + let sender_enc_b = + elgamal::encrypt(¬e_pk.into(), TP.sender_pk.B(), &sender_blinder_b); + + let sender_enc_a = (sender_enc_a.0.into(), sender_enc_a.1.into()); + let sender_enc_b = (sender_enc_b.0.into(), sender_enc_b.1.into()); + + TxOutputNote::new( + value, + value_commitment, + value_blinder, + note_pk, + [sender_enc_a, sender_enc_b], + ) +} + #[test] fn test_transfer_circuit_1_2() { let mut rng = StdRng::seed_from_u64(0xc0b); @@ -140,15 +208,19 @@ fn test_transfer_circuit_1_2() { let input_notes = [TP.tx_input_notes[0].clone()]; // create 2 testing tx output notes - let value1 = 10; - let blinder1 = JubJubScalar::random(&mut rng); - let commitment1 = value_commitment(value1, blinder1); - let value2 = 5; - let blinder2 = JubJubScalar::random(&mut rng); - let commitment2 = value_commitment(value2, blinder2); let tx_output_notes = [ - TxOutputNote::new(value1, commitment1, blinder1), - TxOutputNote::new(value2, commitment2, blinder2), + create_tx_output_note( + &mut rng, + 10, + TP.output_npk[0], + TP.sender_blinder[0], + ), + create_tx_output_note( + &mut rng, + 5, + TP.output_npk[1], + TP.sender_blinder[1], + ), ]; let (proof, public_inputs) = prover @@ -161,7 +233,9 @@ fn test_transfer_circuit_1_2() { TP.root, TP.deposit, TP.max_fee, - TP.rp, + TP.sender_pk, + TP.signatures, + [TP.sender_blinder[0], TP.sender_blinder[1]], ), ) .expect("failed to prove"); @@ -183,15 +257,19 @@ fn test_transfer_circuit_2_2() { [TP.tx_input_notes[0].clone(), TP.tx_input_notes[1].clone()]; // create 2 testing tx output notes - let value1 = 35; - let blinder1 = JubJubScalar::random(&mut rng); - let commitment1 = value_commitment(value1, blinder1); - let value2 = 5; - let blinder2 = JubJubScalar::random(&mut rng); - let commitment2 = value_commitment(value2, blinder2); let tx_output_notes = [ - TxOutputNote::new(value1, commitment1, blinder1), - TxOutputNote::new(value2, commitment2, blinder2), + create_tx_output_note( + &mut rng, + 35, + TP.output_npk[0], + TP.sender_blinder[0], + ), + create_tx_output_note( + &mut rng, + 5, + TP.output_npk[1], + TP.sender_blinder[1], + ), ]; let (proof, public_inputs) = prover @@ -204,7 +282,9 @@ fn test_transfer_circuit_2_2() { TP.root, TP.deposit, TP.max_fee, - TP.rp, + TP.sender_pk, + TP.signatures, + [TP.sender_blinder[0], TP.sender_blinder[1]], ), ) .expect("failed to prove"); @@ -229,15 +309,19 @@ fn test_transfer_circuit_3_2() { ]; // create 2 testing tx output notes - let value1 = 35; - let blinder1 = JubJubScalar::random(&mut rng); - let commitment1 = value_commitment(value1, blinder1); - let value2 = 30; - let blinder2 = JubJubScalar::random(&mut rng); - let commitment2 = value_commitment(value2, blinder2); let tx_output_notes = [ - TxOutputNote::new(value1, commitment1, blinder1), - TxOutputNote::new(value2, commitment2, blinder2), + create_tx_output_note( + &mut rng, + 35, + TP.output_npk[0], + TP.sender_blinder[0], + ), + create_tx_output_note( + &mut rng, + 30, + TP.output_npk[1], + TP.sender_blinder[1], + ), ]; let (proof, public_inputs) = prover @@ -250,7 +334,9 @@ fn test_transfer_circuit_3_2() { TP.root, TP.deposit, TP.max_fee, - TP.rp, + TP.sender_pk, + TP.signatures, + [TP.sender_blinder[0], TP.sender_blinder[1]], ), ) .expect("failed to prove"); @@ -269,15 +355,19 @@ fn test_transfer_circuit_4_2() { .expect("failed to compile circuit"); // create 2 testing tx output notes - let value1 = 60; - let blinder1 = JubJubScalar::random(&mut rng); - let commitment1 = value_commitment(value1, blinder1); - let value2 = 30; - let blinder2 = JubJubScalar::random(&mut rng); - let commitment2 = value_commitment(value2, blinder2); let tx_output_notes = [ - TxOutputNote::new(value1, commitment1, blinder1), - TxOutputNote::new(value2, commitment2, blinder2), + create_tx_output_note( + &mut rng, + 60, + TP.output_npk[0], + TP.sender_blinder[0], + ), + create_tx_output_note( + &mut rng, + 30, + TP.output_npk[1], + TP.sender_blinder[1], + ), ]; let (proof, public_inputs) = prover @@ -290,7 +380,9 @@ fn test_transfer_circuit_4_2() { TP.root, TP.deposit, TP.max_fee, - TP.rp, + TP.sender_pk, + TP.signatures, + [TP.sender_blinder[0], TP.sender_blinder[1]], ), ) .expect("failed to prove");