From 999a86a84ac9bd9d54f6c045c4d1afb0be188ab5 Mon Sep 17 00:00:00 2001 From: moana Date: Tue, 11 Jun 2024 18:41:16 +0200 Subject: [PATCH 1/2] circuits: Add `RecipientParameters::new` constructor --- circuits/CHANGELOG.md | 2 + circuits/Cargo.toml | 4 +- circuits/src/recipient.rs | 149 +++++++++++++++++++++++---------- circuits/src/transaction.rs | 9 +- circuits/tests/transaction.rs | 152 +++++++++++++--------------------- 5 files changed, 171 insertions(+), 145 deletions(-) diff --git a/circuits/CHANGELOG.md b/circuits/CHANGELOG.md index 4f0e6b0..f3fc5c5 100644 --- a/circuits/CHANGELOG.md +++ b/circuits/CHANGELOG.md @@ -10,6 +10,7 @@ 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 @@ -36,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Update `poseidon-merkle` to v0.6 [#179] +[#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 [#191]: https://github.com/dusk-network/phoenix/issues/191 diff --git a/circuits/Cargo.toml b/circuits/Cargo.toml index 71587f0..b83d846 100644 --- a/circuits/Cargo.toml +++ b/circuits/Cargo.toml @@ -10,14 +10,14 @@ exclude = [".github/workflows/dusk-ci.yml", ".gitignore"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -phoenix-core = { version = "0.28.0", path = "../core" } +phoenix-core = { version = "0.28.0", default-features = false, path = "../core" } dusk-plonk = { version = "0.19", default-features = false } dusk-jubjub = { version = "0.14", default-features = false } poseidon-merkle = { version = "0.6", features = ["rkyv-impl", "zk", "size_32"] } dusk-poseidon = { version = "0.39", features = ["zk"] } jubjub-schnorr = { version = "0.4", features = ["zk"] } rand = { version = "0.8", default-features = false, features = ["std_rng"] } +ff = { version = "0.13", default-features = false } [dev-dependencies] -ff = { version = "0.13", default-features = false } lazy_static = "1.4" diff --git a/circuits/src/recipient.rs b/circuits/src/recipient.rs index 1dda19f..36e895e 100644 --- a/circuits/src/recipient.rs +++ b/circuits/src/recipient.rs @@ -4,17 +4,16 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. +#![allow(non_snake_case)] + use dusk_jubjub::JubJubScalar; use dusk_plonk::prelude::*; -use jubjub_schnorr::{gadgets, Signature}; +use ff::Field; +use jubjub_schnorr::{gadgets, SecretKey as SchnorrSecretKey, Signature}; +use rand::{CryptoRng, RngCore}; use crate::elgamal; -use phoenix_core::PublicKey; - -use rand::rngs::StdRng; -use rand::SeedableRng; - -use phoenix_core::SecretKey; +use phoenix_core::{PublicKey, SecretKey}; const TX_OUTPUT_NOTES: usize = 2; @@ -24,39 +23,103 @@ pub struct RecipientParameters { /// Public key of the transaction sender pub sender_pk: PublicKey, /// Note public keys of each note recipient - pub recipient_npk_vec: [JubJubAffine; TX_OUTPUT_NOTES], + pub output_npk: [JubJubAffine; TX_OUTPUT_NOTES], /// Signatures of 'payload_hash' verifiable using 'pk_A' and 'pk_B' - pub sig_vec: [Signature; TX_OUTPUT_NOTES], + pub sig: [Signature; TX_OUTPUT_NOTES], /// Asymmetric encryption of 'pk_A' using both recipients 'npk' - pub enc_A_vec: [(JubJubExtended, JubJubExtended); TX_OUTPUT_NOTES], + pub enc_A: [(JubJubExtended, JubJubExtended); TX_OUTPUT_NOTES], /// Asymmetric encryption of 'pk_B' using both recipients 'npk' - pub enc_B_vec: [(JubJubExtended, JubJubExtended); TX_OUTPUT_NOTES], + pub enc_B: [(JubJubExtended, JubJubExtended); TX_OUTPUT_NOTES], /// Randomness needed to encrypt/decrypt 'pk_A' - pub r_A_vec: [JubJubScalar; TX_OUTPUT_NOTES], + pub r_A: [JubJubScalar; TX_OUTPUT_NOTES], /// Randomness needed to encrypt/decrypt 'pk_B' - pub r_B_vec: [JubJubScalar; TX_OUTPUT_NOTES], + pub r_B: [JubJubScalar; TX_OUTPUT_NOTES], } impl Default for RecipientParameters { fn default() -> Self { - let mut rng = StdRng::seed_from_u64(0xbeef); - - let sk = SecretKey::random(&mut rng); + let sk = + SecretKey::new(JubJubScalar::default(), JubJubScalar::default()); let sender_pk = PublicKey::from(&sk); Self { sender_pk, - recipient_npk_vec: [ - JubJubAffine::default(), - JubJubAffine::default(), - ], - sig_vec: [Signature::default(), Signature::default()], - enc_A_vec: [(JubJubExtended::default(), JubJubExtended::default()); + output_npk: [JubJubAffine::default(), JubJubAffine::default()], + sig: [Signature::default(), Signature::default()], + enc_A: [(JubJubExtended::default(), JubJubExtended::default()); TX_OUTPUT_NOTES], - enc_B_vec: [(JubJubExtended::default(), JubJubExtended::default()); + enc_B: [(JubJubExtended::default(), JubJubExtended::default()); TX_OUTPUT_NOTES], - r_A_vec: [JubJubScalar::default(); TX_OUTPUT_NOTES], - r_B_vec: [JubJubScalar::default(); TX_OUTPUT_NOTES], + r_A: [JubJubScalar::default(); TX_OUTPUT_NOTES], + r_B: [JubJubScalar::default(); TX_OUTPUT_NOTES], + } + } +} + +impl RecipientParameters { + /// Create the recipient parameter + pub fn new( + rng: &mut (impl RngCore + CryptoRng), + sender_sk: &SecretKey, + output_npk: [JubJubAffine; TX_OUTPUT_NOTES], + payload_hash: BlsScalar, + ) -> Self { + // Encrypt the public key of the sender. We need to encrypt + // both 'A' and 'B', using both tx output note public keys. + let sender_pk = PublicKey::from(sender_sk); + + let r_A = [ + JubJubScalar::random(&mut *rng), + JubJubScalar::random(&mut *rng), + ]; + let r_B = [ + JubJubScalar::random(&mut *rng), + JubJubScalar::random(&mut *rng), + ]; + + let (A_enc_1_c1, A_enc_1_c2) = elgamal::encrypt( + &output_npk[0].into(), // note_pk_1.as_ref(), + sender_pk.A(), + &r_A[0], + ); + + let (B_enc_1_c1, B_enc_1_c2) = elgamal::encrypt( + &output_npk[0].into(), // note_pk_1.as_ref(), + sender_pk.B(), + &r_B[0], + ); + let (A_enc_2_c1, A_enc_2_c2) = elgamal::encrypt( + &output_npk[1].into(), // note_pk_2.as_ref(), + sender_pk.A(), + &r_A[1], + ); + + let (B_enc_2_c1, B_enc_2_c2) = elgamal::encrypt( + &output_npk[1].into(), // note_pk_2.as_ref(), + sender_pk.B(), + &r_B[1], + ); + + let enc_A = [(A_enc_1_c1, A_enc_1_c2), (A_enc_2_c1, A_enc_2_c2)]; + let enc_B = [(B_enc_1_c1, B_enc_1_c2), (B_enc_2_c1, B_enc_2_c2)]; + + // Sign the payload hash using both 'a' and 'b' + let schnorr_sk_a = SchnorrSecretKey::from(sender_sk.a()); + let sig_A = schnorr_sk_a.sign(rng, payload_hash); + + let schnorr_sk_b = SchnorrSecretKey::from(sender_sk.b()); + let sig_B = schnorr_sk_b.sign(rng, payload_hash); + + let sig = [sig_A, sig_B]; + + RecipientParameters { + sender_pk, + output_npk, + sig, + enc_A, + enc_B, + r_A, + r_B, } } } @@ -71,24 +134,24 @@ pub(crate) fn gadget( 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_vec[0].u()); - let sig_A_R = composer.append_point(rp.sig_vec[0].R()); + 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_vec[1].u()); - let sig_B_R = composer.append_point(rp.sig_vec[1].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.recipient_npk_vec[0]); - let note_pk_2 = composer.append_public_point(rp.recipient_npk_vec[1]); + 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_vec[0]); - let r_A_2 = composer.append_witness(rp.r_A_vec[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_vec[0]); - let r_B_2 = composer.append_witness(rp.r_B_vec[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)?; @@ -100,15 +163,15 @@ pub(crate) fn gadget( 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_vec[0].0); - composer.assert_equal_public_point(enc_A_1_c2, rp.enc_A_vec[0].1); - composer.assert_equal_public_point(enc_A_2_c1, rp.enc_A_vec[1].0); - composer.assert_equal_public_point(enc_A_2_c2, rp.enc_A_vec[1].1); + 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_vec[0].0); - composer.assert_equal_public_point(enc_B_1_c2, rp.enc_B_vec[0].1); - composer.assert_equal_public_point(enc_B_2_c1, rp.enc_B_vec[1].0); - composer.assert_equal_public_point(enc_B_2_c2, rp.enc_B_vec[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/transaction.rs b/circuits/src/transaction.rs index 6b6591b..a4766bd 100644 --- a/circuits/src/transaction.rs +++ b/circuits/src/transaction.rs @@ -140,7 +140,7 @@ struct WitnessTxOutputNote { } impl TxOutputNote { - /// Crate a new `TxOutputNote`. + /// Create a new `TxOutputNote`. pub fn new( value: u64, value_commitment: JubJubAffine, @@ -337,9 +337,8 @@ pub struct TxCircuit { impl Default for TxCircuit { fn default() -> Self { - let mut rng = StdRng::seed_from_u64(0xbeef); - - let sk = SecretKey::random(&mut rng); + let sk = + SecretKey::new(JubJubScalar::default(), JubJubScalar::default()); let mut tree = Tree::<(), H>::new(); let payload_hash = BlsScalar::default(); @@ -355,7 +354,7 @@ impl Default for TxCircuit { for _ in 0..I { let merkle_opening = tree.opening(*note.pos()).expect("Tree read."); let tx_input_note = TxInputNote::new( - &mut rng, + &mut StdRng::seed_from_u64(0xb001), ¬e, merkle_opening, &sk, diff --git a/circuits/tests/transaction.rs b/circuits/tests/transaction.rs index 703fc2b..ccc6fdd 100644 --- a/circuits/tests/transaction.rs +++ b/circuits/tests/transaction.rs @@ -8,26 +8,21 @@ use rand::rngs::StdRng; use rand::SeedableRng; use rand::{CryptoRng, RngCore}; -use dusk_jubjub::{JubJubScalar, GENERATOR_EXTENDED, GENERATOR_NUMS_EXTENDED}; - +use dusk_jubjub::JubJubScalar; use dusk_plonk::prelude::*; use ff::Field; -use jubjub_schnorr::SecretKey as SchnorrSecretKey; +use poseidon_merkle::{Item, Tree}; use phoenix_circuits::{ - elgamal, transaction::{TxCircuit, TxInputNote, TxOutputNote}, RecipientParameters, }; -use phoenix_core::{Note, PublicKey, SecretKey}; - -use poseidon_merkle::{Item, Tree}; +use phoenix_core::{value_commitment, Note, PublicKey, SecretKey}; #[macro_use] extern crate lazy_static; static LABEL: &[u8; 12] = b"dusk-network"; -const CAPACITY: usize = 17; // capacity required for the setup const HEIGHT: usize = 17; @@ -43,17 +38,19 @@ struct TestingParameters { lazy_static! { static ref TP: TestingParameters = { + const CAPACITY: usize = 17; + let mut rng = StdRng::seed_from_u64(0xc0b); let pp = PublicParameters::setup(1 << CAPACITY, &mut rng).unwrap(); - let sk = SecretKey::random(&mut rng); + let sender_sk = SecretKey::random(&mut rng); let mut tree = Tree::<(), HEIGHT>::new(); 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, &sk, payload_hash); + 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; @@ -61,73 +58,22 @@ lazy_static! { let deposit = 5; let max_fee = 5; - // Compute the tx output note public keys using - // the recipient public key - let recipient_pk = PublicKey::from(&sk); - - let r = JubJubScalar::random(&mut rng); - let sa = recipient_pk.gen_stealth_address(&r); - let note_pk_1 = sa.note_pk(); - - let r = JubJubScalar::random(&mut rng); - let sa = recipient_pk.gen_stealth_address(&r); - let note_pk_2 = sa.note_pk(); - - let recipient_npk_vec = [ - JubJubAffine::from(note_pk_1.as_ref()), - JubJubAffine::from(note_pk_2.as_ref()), + // 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 recipient_npk = *recipient_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(sender_npk), ]; - // Encrypt the public key of the sender. We need to encrypt - // both 'A' and 'B', using both tx output note public keys. - // We use the same 'sk' just for testing. - let sender_pk = PublicKey::from(&sk); - - #[allow(non_snake_case)] - let r_A_1 = JubJubScalar::random(&mut rng); - #[allow(non_snake_case)] - let (A_enc_1_c1, A_enc_1_c2) = - elgamal::encrypt(note_pk_1.as_ref(), &sender_pk.A(), &r_A_1); - - #[allow(non_snake_case)] - let r_B_1 = JubJubScalar::random(&mut rng); - #[allow(non_snake_case)] - let (B_enc_1_c1, B_enc_1_c2) = - elgamal::encrypt(note_pk_1.as_ref(), &sender_pk.B(), &r_B_1); - #[allow(non_snake_case)] - let r_A_2 = JubJubScalar::random(&mut rng); - #[allow(non_snake_case)] - let (A_enc_2_c1, A_enc_2_c2) = - elgamal::encrypt(note_pk_2.as_ref(), &sender_pk.A(), &r_A_2); - - #[allow(non_snake_case)] - let r_B_2 = JubJubScalar::random(&mut rng); - #[allow(non_snake_case)] - let (B_enc_2_c1, B_enc_2_c2) = - elgamal::encrypt(note_pk_2.as_ref(), &sender_pk.B(), &r_B_2); - - #[allow(non_snake_case)] - let enc_A_vec = [(A_enc_1_c1, A_enc_1_c2), (A_enc_2_c1, A_enc_2_c2)]; - #[allow(non_snake_case)] - let enc_B_vec = [(B_enc_1_c1, B_enc_1_c2), (B_enc_2_c1, B_enc_2_c2)]; - - #[allow(non_snake_case)] - let r_A_vec = [r_A_1, r_A_2]; - #[allow(non_snake_case)] - let r_B_vec = [r_B_1, r_B_2]; - - // Sign the payload hash using both 'a' and 'b' - let schnorr_sk_a = SchnorrSecretKey::from(sk.a()); - #[allow(non_snake_case)] - let sig_A = schnorr_sk_a.sign(&mut rng, payload_hash); - - let schnorr_sk_b = SchnorrSecretKey::from(sk.b()); - #[allow(non_snake_case)] - let sig_B = schnorr_sk_b.sign(&mut rng, payload_hash); - - let sig_vec = [sig_A, sig_B]; - - let rp = RecipientParameters { sender_pk, recipient_npk_vec, sig_vec, enc_A_vec, enc_B_vec, r_A_vec, r_B_vec }; + let rp = RecipientParameters::new(&mut rng, &sender_sk, output_npk, payload_hash); TestingParameters { pp, tx_input_notes, payload_hash, root, deposit, max_fee, rp } }; @@ -184,18 +130,6 @@ fn create_test_tx_input_notes( input_notes.try_into().unwrap() } -// we don't care if the test output notes are spendable -fn create_test_tx_output_note(value: u64) -> TxOutputNote { - let blinding_factor = JubJubScalar::from(42u64); - - let value_commitment = JubJubAffine::from( - (GENERATOR_EXTENDED * JubJubScalar::from(value)) - + (GENERATOR_NUMS_EXTENDED * blinding_factor), - ); - - TxOutputNote::new(value, value_commitment, blinding_factor) -} - #[test] fn test_transfer_circuit_1_2() { let mut rng = StdRng::seed_from_u64(0xc0b); @@ -207,10 +141,20 @@ 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 = [ - create_test_tx_output_note(10), - create_test_tx_output_note(5), + TxOutputNote::new(value1, commitment1, blinder1), + TxOutputNote::new(value2, commitment2, blinder2), ]; + // let tx_output_notes = [ + // TxOutputNote::new(10, JubJubScalar::random(&mut rng)), + // TxOutputNote::new(5, JubJubScalar::random(&mut rng)), + // ]; let (proof, public_inputs) = prover .prove( @@ -244,9 +188,15 @@ 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 = [ - create_test_tx_output_note(35), - create_test_tx_output_note(5), + TxOutputNote::new(value1, commitment1, blinder1), + TxOutputNote::new(value2, commitment2, blinder2), ]; let (proof, public_inputs) = prover @@ -284,9 +234,15 @@ 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 = [ - create_test_tx_output_note(35), - create_test_tx_output_note(30), + TxOutputNote::new(value1, commitment1, blinder1), + TxOutputNote::new(value2, commitment2, blinder2), ]; let (proof, public_inputs) = prover @@ -318,9 +274,15 @@ 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 = [ - create_test_tx_output_note(60), - create_test_tx_output_note(30), + TxOutputNote::new(value1, commitment1, blinder1), + TxOutputNote::new(value2, commitment2, blinder2), ]; let (proof, public_inputs) = prover From 9445b6bbc498a60c23b88aebc51e098ff4f535fd Mon Sep 17 00:00:00 2001 From: moana Date: Tue, 11 Jun 2024 18:33:52 +0200 Subject: [PATCH 2/2] core: Introduce API changes These include: - Add function `value_commitment` - Add function `transparent_value_commitment` - Turn the value-commitment an `JubJubAffine` point - Expose `NOTE_ENCRYPTION_SIZE` - Make `alloc` a `default` feature --- core/CHANGELOG.md | 6 ++++ core/Cargo.toml | 2 +- core/src/addresses.rs | 23 +++++++++++++++ core/src/{keys => addresses}/stealth.rs | 2 +- core/src/{keys => addresses}/sync.rs | 2 +- core/src/keys.rs | 11 ------- core/src/lib.rs | 31 +++++++++++++++++--- core/src/note.rs | 39 +++++++++---------------- core/tests/note_test.rs | 25 +++++++--------- 9 files changed, 84 insertions(+), 57 deletions(-) create mode 100644 core/src/addresses.rs rename core/src/{keys => addresses}/stealth.rs (98%) rename core/src/{keys => addresses}/sync.rs (98%) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 15644c3..21ca397 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -10,10 +10,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add a light sync method in the `ViewKey` [#199] +- Add function `value_commitment` [#201] +- Add function `transparent_value_commitment` [#201] ### Changed - Rename `crossover` to `deposit` [#190] +- Turn the value-commitment an `JubJubAffine` point [#201] +- Expose `NOTE_ENCRYPTION_SIZE` [#201] +- Make `alloc` a `default` feature [#201] ### Removed @@ -329,6 +334,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Canonical implementation shielded by feature. +[#201]: https://github.com/dusk-network/phoenix/issues/201 [#199]: https://github.com/dusk-network/phoenix/issues/199 [#195]: https://github.com/dusk-network/phoenix/issues/195 [#190]: https://github.com/dusk-network/phoenix/issues/190 diff --git a/core/Cargo.toml b/core/Cargo.toml index c922a04..dad4253 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -28,7 +28,7 @@ rand = "0.8" rkyv = { version = "0.7", default-features = false, features = ["size_32"] } [features] -default = [] # "alloc" is suggested as default feature but would be breaking change +default = ["alloc"] alloc = [] rkyv-impl = [ "dusk-jubjub/rkyv-impl", diff --git a/core/src/addresses.rs b/core/src/addresses.rs new file mode 100644 index 0000000..3c9c44b --- /dev/null +++ b/core/src/addresses.rs @@ -0,0 +1,23 @@ +// 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. + +pub mod stealth; +pub mod sync; + +// /// Hashes a JubJub's ExtendedPoint into a JubJub's Scalar using the JubJub's +// /// hash to scalar function +// pub fn hash(p: &JubJubExtended) -> JubJubScalar { +// JubJubScalar::hash_to_scalar(&JubJubAffine::from(p).to_bytes()) +// } + +/// The trait `Ownable` is required by any type that wants to prove its +/// ownership. +pub trait Ownable { + /// Returns the associated `SyncAddress` + fn sync_address(&self) -> sync::SyncAddress; + /// Returns the associated `StealthAddress` + fn stealth_address(&self) -> stealth::StealthAddress; +} diff --git a/core/src/keys/stealth.rs b/core/src/addresses/stealth.rs similarity index 98% rename from core/src/keys/stealth.rs rename to core/src/addresses/stealth.rs index b3d895e..daf8217 100644 --- a/core/src/keys/stealth.rs +++ b/core/src/addresses/stealth.rs @@ -4,7 +4,7 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -use crate::keys::{sync::SyncAddress, Ownable}; +use crate::{Ownable, SyncAddress}; use dusk_jubjub::{JubJubAffine, JubJubExtended}; use jubjub_schnorr::PublicKey as NotePublicKey; diff --git a/core/src/keys/sync.rs b/core/src/addresses/sync.rs similarity index 98% rename from core/src/keys/sync.rs rename to core/src/addresses/sync.rs index 2d197a0..b2cbfa6 100644 --- a/core/src/keys/sync.rs +++ b/core/src/addresses/sync.rs @@ -4,7 +4,7 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -use crate::keys::{stealth::StealthAddress, Ownable}; +use crate::{Ownable, StealthAddress}; use dusk_jubjub::{JubJubAffine, JubJubExtended}; use jubjub_schnorr::PublicKey as NotePublicKey; diff --git a/core/src/keys.rs b/core/src/keys.rs index c073f48..4aef965 100644 --- a/core/src/keys.rs +++ b/core/src/keys.rs @@ -8,8 +8,6 @@ use dusk_jubjub::{JubJubAffine, JubJubExtended, JubJubScalar}; pub mod public; pub mod secret; -pub mod stealth; -pub mod sync; pub mod view; /// Hashes a JubJub's ExtendedPoint into a JubJub's Scalar using the JubJub's @@ -17,12 +15,3 @@ pub mod view; pub fn hash(p: &JubJubExtended) -> JubJubScalar { JubJubScalar::hash_to_scalar(&JubJubAffine::from(p).to_bytes()) } - -/// The trait `Ownable` is required by any type that wants to prove its -/// ownership. -pub trait Ownable { - /// Returns the associated `SyncAddress` - fn sync_address(&self) -> sync::SyncAddress; - /// Returns the associated `StealthAddress` - fn stealth_address(&self) -> stealth::StealthAddress; -} diff --git a/core/src/lib.rs b/core/src/lib.rs index fd434a9..a7352f0 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -10,6 +10,7 @@ #![deny(missing_docs)] #![no_std] +mod addresses; mod encryption; mod error; mod keys; @@ -18,17 +19,39 @@ mod note; #[cfg(feature = "alloc")] mod transaction; +pub use addresses::stealth::StealthAddress; +pub use addresses::sync::SyncAddress; +pub use addresses::Ownable; pub use encryption::aes; pub use error::Error; pub use keys::hash; pub use keys::public::PublicKey; pub use keys::secret::SecretKey; -pub use keys::stealth::StealthAddress; -pub use keys::sync::SyncAddress; pub use keys::view::ViewKey; -pub use keys::Ownable; -pub use note::{Note, NoteType}; +pub use note::{Note, NoteType, ENCRYPTION_SIZE as NOTE_ENCRYPTION_SIZE}; #[cfg(feature = "alloc")] /// Transaction Skeleton used by the phoenix transaction model pub use transaction::TxSkeleton; + +use dusk_jubjub::{ + JubJubAffine, JubJubScalar, GENERATOR_EXTENDED, GENERATOR_NUMS_EXTENDED, +}; + +/// Use the pedersen commitment scheme to compute a transparent value +/// commitment. +pub fn transparent_value_commitment(value: u64) -> JubJubAffine { + JubJubAffine::from(GENERATOR_EXTENDED * JubJubScalar::from(value)) +} + +/// Use the pedersen commitment scheme to compute a value commitment using a +/// blinding-factor. +pub fn value_commitment( + value: u64, + blinding_factor: JubJubScalar, +) -> JubJubAffine { + JubJubAffine::from( + (GENERATOR_EXTENDED * JubJubScalar::from(value)) + + (GENERATOR_NUMS_EXTENDED * blinding_factor), + ) +} diff --git a/core/src/note.rs b/core/src/note.rs index a3414f1..e666d33 100644 --- a/core/src/note.rs +++ b/core/src/note.rs @@ -7,14 +7,12 @@ use core::convert::{TryFrom, TryInto}; use crate::{ - Error, Ownable, PublicKey, SecretKey, StealthAddress, SyncAddress, ViewKey, + transparent_value_commitment, value_commitment, Error, Ownable, PublicKey, + SecretKey, StealthAddress, SyncAddress, ViewKey, }; use dusk_bls12_381::BlsScalar; use dusk_bytes::{DeserializableSlice, Error as BytesError, Serializable}; -use dusk_jubjub::{ - dhke, JubJubAffine, JubJubExtended, JubJubScalar, GENERATOR_EXTENDED, - GENERATOR_NUMS_EXTENDED, -}; +use dusk_jubjub::{dhke, JubJubAffine, JubJubScalar, GENERATOR_NUMS_EXTENDED}; use crate::aes; @@ -25,15 +23,14 @@ use rand::{CryptoRng, RngCore}; #[cfg(feature = "rkyv-impl")] use rkyv::{Archive, Deserialize, Serialize}; -/// Blinder used for transparent +/// Blinder used for transparent notes. pub(crate) const TRANSPARENT_BLINDER: JubJubScalar = JubJubScalar::zero(); /// Size of the Phoenix notes plaintext: value (8 bytes) + blinder (32 bytes) pub(crate) const PLAINTEXT_SIZE: usize = 40; /// Size of the Phoenix notes encryption -pub(crate) const ENCRYPTION_SIZE: usize = - PLAINTEXT_SIZE + aes::ENCRYPTION_EXTRA_SIZE; +pub const ENCRYPTION_SIZE: usize = PLAINTEXT_SIZE + aes::ENCRYPTION_EXTRA_SIZE; /// The types of a Note #[derive(Debug, Clone, Copy, Eq, PartialEq)] @@ -78,7 +75,7 @@ impl TryFrom for NoteType { )] pub struct Note { pub(crate) note_type: NoteType, - pub(crate) value_commitment: JubJubExtended, + pub(crate) value_commitment: JubJubAffine, pub(crate) stealth_address: StealthAddress, pub(crate) sync_address: SyncAddress, pub(crate) pos: u64, @@ -108,9 +105,7 @@ impl Note { let r_sync = JubJubScalar::random(&mut *rng); let sync_address = pk.gen_sync_address(&r_sync); - let value_commitment = JubJubScalar::from(value); - let value_commitment = (GENERATOR_EXTENDED * value_commitment) - + (GENERATOR_NUMS_EXTENDED * blinding_factor); + let value_commitment = value_commitment(value, blinding_factor); // Output notes have undefined position, equals to u64's MAX value let pos = u64::MAX; @@ -167,9 +162,7 @@ impl Note { sync_address: SyncAddress, value: u64, ) -> Self { - let value_commitment = JubJubScalar::from(value); - let value_commitment = (GENERATOR_EXTENDED * value_commitment) - + (GENERATOR_NUMS_EXTENDED * TRANSPARENT_BLINDER); + let value_commitment = transparent_value_commitment(value); let pos = u64::MAX; @@ -205,7 +198,7 @@ impl Note { pub fn empty() -> Self { Self { note_type: NoteType::Transparent, - value_commitment: JubJubExtended::default(), + value_commitment: JubJubAffine::default(), stealth_address: StealthAddress::default(), sync_address: SyncAddress::default(), pos: 0, @@ -253,14 +246,13 @@ impl Note { /// Return the internal representation of scalars to be hashed pub fn hash_inputs(&self) -> [BlsScalar; 6] { - let value_commitment = self.value_commitment().to_hash_inputs(); let note_pk = self.stealth_address().note_pk().as_ref().to_hash_inputs(); [ BlsScalar::from(self.note_type as u64), - value_commitment[0], - value_commitment[1], + self.value_commitment.get_u(), + self.value_commitment.get_v(), note_pk[0], note_pk[1], BlsScalar::from(self.pos), @@ -290,7 +282,7 @@ impl Note { } /// Return the value commitment `H(value, blinding_factor)` - pub const fn value_commitment(&self) -> &JubJubExtended { + pub const fn value_commitment(&self) -> &JubJubAffine { &self.value_commitment } @@ -367,9 +359,7 @@ impl Serializable<{ 169 + ENCRYPTION_SIZE }> for Note { buf[0] = self.note_type as u8; - buf[1..33].copy_from_slice( - &JubJubAffine::from(&self.value_commitment).to_bytes(), - ); + buf[1..33].copy_from_slice(&self.value_commitment.to_bytes()); buf[33..97].copy_from_slice(&self.stealth_address.to_bytes()); buf[97..161].copy_from_slice(&self.sync_address.to_bytes()); buf[161..169].copy_from_slice(&self.pos.to_le_bytes()); @@ -384,8 +374,7 @@ impl Serializable<{ 169 + ENCRYPTION_SIZE }> for Note { let note_type = bytes[0].try_into().map_err(|_| BytesError::InvalidData)?; - let value_commitment = - JubJubExtended::from(JubJubAffine::from_slice(&bytes[1..33])?); + let value_commitment = JubJubAffine::from_slice(&bytes[1..33])?; let stealth_address = StealthAddress::from_slice(&bytes[33..97])?; let sync_address = SyncAddress::from_slice(&bytes[97..161])?; diff --git a/core/tests/note_test.rs b/core/tests/note_test.rs index 6ae5d2a..d283038 100644 --- a/core/tests/note_test.rs +++ b/core/tests/note_test.rs @@ -4,10 +4,11 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -use dusk_jubjub::{JubJubScalar, GENERATOR_EXTENDED, GENERATOR_NUMS_EXTENDED}; +use dusk_jubjub::JubJubScalar; use ff::Field; use phoenix_core::{ - Error, Note, NoteType, Ownable, PublicKey, SecretKey, ViewKey, + value_commitment, Error, Note, NoteType, Ownable, PublicKey, SecretKey, + ViewKey, }; use rand::rngs::StdRng; use rand::SeedableRng; @@ -103,18 +104,16 @@ fn value_commitment_transparent() { let value = note .value(Some(&vk)) - .expect("Value not returned with the correct view key"); - let value = JubJubScalar::from(value); + .expect("The note should be owned by the provided vk"); let blinding_factor = note .blinding_factor(Some(&vk)) - .expect("Blinding factor not returned with the correct view key"); + .expect("The note should be owned by the provided vk"); let commitment = note.value_commitment(); - let commitment_p = (GENERATOR_EXTENDED * value) - + (GENERATOR_NUMS_EXTENDED * blinding_factor); + let commitment_p = value_commitment(value, blinding_factor); - assert_eq!(commitment, &commitment_p); + assert_eq!(commitment, &commitment_p.into()); } #[test] @@ -131,18 +130,16 @@ fn value_commitment_obfuscated() { let value = note .value(Some(&vk)) - .expect("Value not returned with the correct view key"); - let value = JubJubScalar::from(value); + .expect("The note should be owned by the provided vk"); let blinding_factor = note .blinding_factor(Some(&vk)) - .expect("Blinding factor not returned with the correct view key"); + .expect("The note should be owned by the provided vk"); let commitment = note.value_commitment(); - let commitment_p = (GENERATOR_EXTENDED * value) - + (GENERATOR_NUMS_EXTENDED * blinding_factor); + let commitment_p = value_commitment(value, blinding_factor); - assert_eq!(commitment, &commitment_p); + assert_eq!(commitment, &commitment_p.into()); } #[test]