From 493a3f3ff25725026800f8fa116b41ea4b5760ed Mon Sep 17 00:00:00 2001 From: esau <152162806+sklppy88@users.noreply.github.com> Date: Sun, 27 Oct 2024 17:42:06 +0000 Subject: [PATCH] refactor: replace token note with uint note (#8143) In this PR, we are replacing the token note with our previously added U128 note and making BalanceSet hold a UintNote instead of being generic. The token note is now redundant as we have a UintNote in aztec nr now. Resolves #8107. --- .../aztec/concepts/storage/partial_notes.md | 6 +- .../how_to_compile_contract.md | 8 +- .../storage/storage_slots.md | 6 +- .../contract_tutorials/token_contract.md | 8 +- .../aztec-nr/uint-note/src/uint_note.nr | 28 ++++++- .../token_blacklist_contract/src/main.nr | 4 +- .../contracts/token_contract/Nargo.toml | 1 + .../contracts/token_contract/src/main.nr | 16 ++-- .../token_contract/src/test/refunds.nr | 11 +-- .../contracts/token_contract/src/types.nr | 1 - .../token_contract/src/types/balance_set.nr | 76 +++++-------------- .../token_contract/src/types/token_note.nr | 75 ------------------ 12 files changed, 74 insertions(+), 166 deletions(-) delete mode 100644 noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr diff --git a/docs/docs/aztec/concepts/storage/partial_notes.md b/docs/docs/aztec/concepts/storage/partial_notes.md index 82489fcfdd6..fb597632d0b 100644 --- a/docs/docs/aztec/concepts/storage/partial_notes.md +++ b/docs/docs/aztec/concepts/storage/partial_notes.md @@ -68,8 +68,8 @@ Now let's do the same for partial notes. ## Partial notes life cycle -1. Create a partial/unfinished note in a private function of your contract --> partial here means that the values within the note are not yet considered finalized (e.g. `amount` in a `TokenNote`), -2. compute a note hiding point of the partial note using a multi scalar multiplication on an elliptic curve. For `TokenNote` this would be done as `G_amt * amount0 + G_npk * npk_m_hash + G_rnd * randomness + G_slot * slot`, where each `G_` is a generator point for a specific field in the note, +1. Create a partial/unfinished note in a private function of your contract --> partial here means that the values within the note are not yet considered finalized (e.g. `amount` in a `UintNote`), +2. compute a note hiding point of the partial note using a multi scalar multiplication on an elliptic curve. For `UintNote` this would be done as `G_amt * amount0 + G_npk * npk_m_hash + G_rnd * randomness + G_slot * slot`, where each `G_` is a generator point for a specific field in the note, 3. emit partial note log, 4. pass the note hiding point to a public function, 5. in a public function determine the value you want to add to the note (e.g. adding a value to an amount) and add it to the note hiding point (e.g. `NOTE_HIDING_POINT + G_amt * amount`), @@ -136,7 +136,7 @@ Then we just emit `P_a.x` and `P_b.x` as a note hashes, and we're done! This is implemented by applying the `partial_note` attribute: -#include_code TokenNote noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr rust +#include_code UintNote noir-projects/aztec-nr/uint-note/src/uint_note.nr rust Those `G_x` are generators that generated [here](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/noir-projects/noir-projects/aztec-nr/aztec/src/generators.nr). Anyone can use them for separating different fields in a "partial note". diff --git a/docs/docs/guides/developer_guides/smart_contracts/how_to_compile_contract.md b/docs/docs/guides/developer_guides/smart_contracts/how_to_compile_contract.md index b66308412d0..414fc543bf4 100644 --- a/docs/docs/guides/developer_guides/smart_contracts/how_to_compile_contract.md +++ b/docs/docs/guides/developer_guides/smart_contracts/how_to_compile_contract.md @@ -146,7 +146,7 @@ export class TokenContract extends ContractBase { }, balances: { slot: new Fr(3n), - typ: 'BalancesMap', + typ: 'BalancesMap', }, total_supply: { slot: new Fr(4n), @@ -185,16 +185,16 @@ export class TokenContract extends ContractBase { >; } - public static get notes(): ContractNotes<'TransparentNote' | 'TokenNote'> { + public static get notes(): ContractNotes<'TransparentNote' | 'UintNote'> { const notes = this.artifact.outputs.globals.notes ? (this.artifact.outputs.globals.notes as any) : []; return { TransparentNote: { id: new Fr(84114971101151129711410111011678111116101n), }, - TokenNote: { + UintNote: { id: new Fr(8411110710111078111116101n), }, - } as ContractNotes<'TransparentNote' | 'TokenNote'>; + } as ContractNotes<'TransparentNote' | 'UintNote'>; } /** Type-safe wrappers for the public methods exposed by the contract. */ diff --git a/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/storage/storage_slots.md b/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/storage/storage_slots.md index ba3c45fe4c6..e19ac04d856 100644 --- a/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/storage/storage_slots.md +++ b/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/storage/storage_slots.md @@ -27,12 +27,12 @@ sequenceDiagram BalanceMap->>BalanceMap: derived_slot = H(map_slot, to) BalanceMap->>BalanceSet: BalanceSet::new(to, derived_slot) Token->>BalanceSet: to_bal.add(amount) - BalanceSet->>BalanceSet: note = TokenNote::new(amount, to) + BalanceSet->>BalanceSet: note = UintNote::new(amount, to) BalanceSet->>Set: insert(note) Set->>LifeCycle: create_note(derived_slot, note) LifeCycle->>LifeCycle: note.header = NoteHeader { contract_address,
storage_slot: derived_slot, nonce: 0, note_hash_counter } - Utils->>TokenNote: note_hiding_point = note.compute_note_hiding_point() - TokenNote->>Utils: note_hash = note_hiding_point.x + Utils->>UintNote: note_hiding_point = note.compute_note_hiding_point() + UintNote->>Utils: note_hash = note_hiding_point.x LifeCycle->>Context: push_note_hash(note_hash) end Context->>Kernel: unique_note_hash = H(nonce, note_hash) diff --git a/docs/docs/tutorials/codealong/contract_tutorials/token_contract.md b/docs/docs/tutorials/codealong/contract_tutorials/token_contract.md index 66dece82714..9638102a0d6 100644 --- a/docs/docs/tutorials/codealong/contract_tutorials/token_contract.md +++ b/docs/docs/tutorials/codealong/contract_tutorials/token_contract.md @@ -52,7 +52,7 @@ compressed_string = {git="https://github.com/AztecProtocol/aztec-packages/", tag We will be working within `main.nr` for the rest of the tutorial. -## How this will work +## How this will work Before writing the functions, let's go through them to see how this contract will work: @@ -158,7 +158,7 @@ Reading through the storage variables: - `admin` an Aztec address stored in public state. - `minters` is a mapping of Aztec addresses in public state. This will store whether an account is an approved minter on the contract. -- `balances` is a mapping of private balances. Private balances are stored in a `PrivateSet` of `TokenNote`s. The balance is the sum of all of an account's `TokenNote`s. +- `balances` is a mapping of private balances. Private balances are stored in a `PrivateSet` of `UintNote`s. The balance is the sum of all of an account's `UintNote`s. - `total_supply` is an unsigned integer (max 128 bit value) stored in public state and represents the total number of tokens minted. - `pending_shields` is a `PrivateSet` of `TransparentNote`s stored in private state. What is stored publicly is a set of commitments to `TransparentNote`s. - `public_balances` is a mapping of Aztec addresses in public state and represents the publicly viewable balances of accounts. @@ -257,7 +257,7 @@ Storage is referenced as `storage.variable`. #### `redeem_shield` -This private function enables an account to move tokens from a `TransparentNote` in the `pending_shields` mapping to a `TokenNote` in private `balances`. The `TokenNote` will be associated with a nullifier key, so any account that knows this key can spend this note. +This private function enables an account to move tokens from a `TransparentNote` in the `pending_shields` mapping to a `UintNote` in private `balances`. The `UintNote` will be associated with a nullifier key, so any account that knows this key can spend this note. Going through the function logic, first the `secret_hash` is generated from the given secret. This ensures that only the entity possessing the secret can use it to redeem the note. Following this, a `TransparentNote` is retrieved from the set, using the provided amount and secret. The note is subsequently removed from the set, allowing it to be redeemed only once. The recipient's private balance is then increased using the `increment` helper function from the `value_note` [library (GitHub link)](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/noir-projects/aztec-nr/value-note/src/utils.nr). @@ -267,7 +267,7 @@ The function returns `1` to indicate successful execution. #### `unshield` -This private function enables un-shielding of private `TokenNote`s stored in `balances` to any Aztec account's `public_balance`. +This private function enables un-shielding of private `UintNote`s stored in `balances` to any Aztec account's `public_balance`. After initializing storage, the function checks that the `msg_sender` is authorized to spend tokens. See [the Authorizing token spends section](#authorizing-token-spends) above for more detail--the only difference being that `assert_valid_message_for` is modified to work specifically in the private context. After the authorization check, the sender's private balance is decreased using the `decrement` helper function for the `value_note` library. Then it stages a public function call on this contract ([`_increase_public_balance`](#_increase_public_balance)) to be executed in the [public execution phase](#execution-contexts) of transaction execution. `_increase_public_balance` is marked as an `internal` function, so can only be called by this token contract. diff --git a/noir-projects/aztec-nr/uint-note/src/uint_note.nr b/noir-projects/aztec-nr/uint-note/src/uint_note.nr index 44ab33c7201..3bc3840d30e 100644 --- a/noir-projects/aztec-nr/uint-note/src/uint_note.nr +++ b/noir-projects/aztec-nr/uint-note/src/uint_note.nr @@ -1,24 +1,28 @@ use dep::aztec::{ keys::getters::get_nsk_app, - macros::notes::note, + macros::notes::partial_note, note::utils::compute_note_hash_for_nullify, - prelude::{NullifiableNote, PrivateContext}, + oracle::random::random, + prelude::{NoteHeader, NullifiableNote, PrivateContext}, protocol_types::{ constants::GENERATOR_INDEX__NOTE_NULLIFIER, hash::poseidon2_hash_with_separator, }, }; -#[note] +// docs:start:UintNote +#[partial_note(quote {value})] pub struct UintNote { - // The integer stored by the note + // The amount of tokens in the note value: U128, // The nullifying public key hash is used with the nsk_app to ensure that the note can be privately spent. npk_m_hash: Field, // Randomness of the note to hide its contents randomness: Field, } +// docs:end:UintNote impl NullifiableNote for UintNote { + // docs:start:nullifier fn compute_nullifier( self, context: &mut PrivateContext, @@ -30,6 +34,7 @@ impl NullifiableNote for UintNote { GENERATOR_INDEX__NOTE_NULLIFIER as Field, ) } + // docs:end:nullifier unconstrained fn compute_nullifier_without_context(self) -> Field { let note_hash_for_nullify = compute_note_hash_for_nullify(self); @@ -48,3 +53,18 @@ impl Eq for UintNote { & (self.randomness == other.randomness) } } + +impl UintNote { + pub fn new(value: U128, owner_npk_m_hash: Field) -> Self { + // We use the randomness to preserve the privacy of the note recipient by preventing brute-forcing, so a + // malicious sender could use non-random values to make the note less private. But they already know the full + // note pre-image anyway, and so the recipient already trusts them to not disclose this information. We can + // therefore assume that the sender will cooperate in the random value generation. + let randomness = unsafe { random() }; + Self { value, npk_m_hash: owner_npk_m_hash, randomness, header: NoteHeader::empty() } + } + + pub fn get_value(self) -> U128 { + self.value + } +} diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr index c55589d0d65..ef4b0249e17 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr @@ -14,9 +14,7 @@ use dep::aztec::macros::aztec; contract TokenBlacklist { // Libs use dep::aztec::{ - encrypted_logs::encrypted_note_emission::{ - encode_and_encrypt_note, encode_and_encrypt_note_unconstrained, - }, + encrypted_logs::encrypted_note_emission::encode_and_encrypt_note_unconstrained, hash::compute_secret_hash, keys::getters::get_public_keys, macros::{functions::{initializer, internal, private, public, view}, storage::storage}, diff --git a/noir-projects/noir-contracts/contracts/token_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/token_contract/Nargo.toml index 6814c6d8df1..447317ae013 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/Nargo.toml +++ b/noir-projects/noir-contracts/contracts/token_contract/Nargo.toml @@ -6,5 +6,6 @@ type = "contract" [dependencies] aztec = { path = "../../../aztec-nr/aztec" } +uint_note = { path = "../../../aztec-nr/uint-note" } compressed_string = { path = "../../../aztec-nr/compressed-string" } authwit = { path = "../../../aztec-nr/authwit" } diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr index 2a444749fd6..2fc3f7f06b9 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr @@ -42,6 +42,8 @@ contract Token { utils::comparison::Comparator, }; + use dep::uint_note::uint_note::UintNote; + // docs:start:import_authwit use dep::authwit::auth::{ assert_current_call_valid_authwit, assert_current_call_valid_authwit_public, @@ -49,9 +51,7 @@ contract Token { }; // docs:end:import_authwit - use crate::types::{ - balance_set::BalanceSet, token_note::TokenNote, transparent_note::TransparentNote, - }; + use crate::types::{balance_set::BalanceSet, transparent_note::TransparentNote}; // docs:end::imports @@ -82,7 +82,7 @@ contract Token { minters: Map, Context>, // docs:end:storage_minters // docs:start:storage_balances - balances: Map, Context>, + balances: Map, Context>, // docs:end:storage_balances total_supply: PublicMutable, // docs:start:storage_pending_shields @@ -571,13 +571,13 @@ contract Token { let fee_payer_randomness = unsafe { random() }; let user_randomness = unsafe { random() }; - let fee_payer_setup_payload = TokenNote::setup_payload().new( + let fee_payer_setup_payload = UintNote::setup_payload().new( fee_payer_npk_m_hash, fee_payer_randomness, storage.balances.at(fee_payer).set.storage_slot, ); - let user_setup_payload = TokenNote::setup_payload().new( + let user_setup_payload = UintNote::setup_payload().new( user_npk_m_hash, user_randomness, storage.balances.at(user).set.storage_slot, @@ -665,9 +665,9 @@ contract Token { // 3. We construct the note finalization payloads with the correct amounts and hiding points to get the note // hashes and unencrypted logs. let fee_payer_finalization_payload = - TokenNote::finalization_payload().new(&mut context, fee_payer_slot, tx_fee); + UintNote::finalization_payload().new(&mut context, fee_payer_slot, tx_fee); let user_finalization_payload = - TokenNote::finalization_payload().new(&mut context, user_slot, refund_amount); + UintNote::finalization_payload().new(&mut context, user_slot, refund_amount); // 4. At last we emit the note hashes and the final note logs. fee_payer_finalization_payload.emit(); diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/refunds.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/refunds.nr index 93c57bde666..2163f0d3e9a 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/test/refunds.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/refunds.nr @@ -1,10 +1,11 @@ -use crate::{test::utils, Token, types::token_note::TokenNote}; +use crate::{test::utils, Token}; use dep::authwit::cheatcodes as authwit_cheatcodes; use dep::aztec::{ keys::getters::get_public_keys, prelude::NoteHeader, protocol_types::storage::map::derive_storage_slot_in_map, }; +use dep::uint_note::uint_note::UintNote; use std::test::OracleMock; #[test] @@ -48,8 +49,8 @@ unconstrained fn setup_refund_success() { // worth `funded_amount - transaction_fee`. We "know" the transaction fee was 1 (it is hardcoded in // `executePublicFunction` TXE oracle) but we need to notify TXE of the note (preimage). env.add_note( - &mut TokenNote { - amount: U128::from_integer(funded_amount - 1), + &mut UintNote { + value: U128::from_integer(funded_amount - 1), npk_m_hash: user_npk_m_hash, randomness: user_randomness, header: NoteHeader::empty(), @@ -58,8 +59,8 @@ unconstrained fn setup_refund_success() { token_contract_address, ); env.add_note( - &mut TokenNote { - amount: U128::from_integer(1), + &mut UintNote { + value: U128::from_integer(1), npk_m_hash: fee_payer_npk_m_hash, randomness: fee_payer_randomness, header: NoteHeader::empty(), diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/types.nr b/noir-projects/noir-contracts/contracts/token_contract/src/types.nr index 0c4b216e189..59c9dc6f1c3 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/types.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/types.nr @@ -1,3 +1,2 @@ mod transparent_note; mod balance_set; -mod token_note; diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/types/balance_set.nr b/noir-projects/noir-contracts/contracts/token_contract/src/types/balance_set.nr index 9f650a94291..0c854f2eaf9 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/types/balance_set.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/types/balance_set.nr @@ -1,40 +1,28 @@ -use crate::types::token_note::OwnedNote; use dep::aztec::{ context::{PrivateContext, UnconstrainedContext}, - keys::getters::get_public_keys, - note::{ - note_emission::OuterNoteEmission, note_getter::view_notes, note_interface::NullifiableNote, - }, + note::note_emission::OuterNoteEmission, protocol_types::{constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, public_keys::NpkM}, }; -use dep::aztec::prelude::{NoteGetterOptions, NoteInterface, NoteViewerOptions, PrivateSet}; +use dep::aztec::prelude::{NoteGetterOptions, NoteViewerOptions, PrivateSet}; +use dep::uint_note::uint_note::UintNote; -pub struct BalanceSet { - set: PrivateSet, +pub struct BalanceSet { + set: PrivateSet, } -impl BalanceSet { +impl BalanceSet { pub fn new(context: Context, storage_slot: Field) -> Self { assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); Self { set: PrivateSet::new(context, storage_slot) } } } -impl BalanceSet { - pub unconstrained fn balance_of(self: Self) -> U128 - where - T: NoteInterface + NullifiableNote + OwnedNote, - { +impl BalanceSet { + pub unconstrained fn balance_of(self: Self) -> U128 { self.balance_of_with_offset(0) } - pub unconstrained fn balance_of_with_offset( - self: Self, - offset: u32, - ) -> U128 - where - T: NoteInterface + NullifiableNote + OwnedNote, - { + pub unconstrained fn balance_of_with_offset(self: Self, offset: u32) -> U128 { let mut balance = U128::from_integer(0); // docs:start:view_notes let mut options = NoteViewerOptions::new(); @@ -42,7 +30,7 @@ impl BalanceSet { // docs:end:view_notes for i in 0..options.limit { if i < notes.len() { - balance = balance + notes.get_unchecked(i).get_amount(); + balance = balance + notes.get_unchecked(i).get_value(); } } if (notes.len() == options.limit) { @@ -53,20 +41,13 @@ impl BalanceSet { } } -impl BalanceSet { - pub fn add( - self: Self, - owner_npk_m: NpkM, - addend: U128, - ) -> OuterNoteEmission - where - T: NoteInterface + NullifiableNote + OwnedNote + Eq, - { +impl BalanceSet<&mut PrivateContext> { + pub fn add(self: Self, owner_npk_m: NpkM, addend: U128) -> OuterNoteEmission { if addend == U128::from_integer(0) { OuterNoteEmission::new(Option::none()) } else { // We fetch the nullifier public key hash from the registry / from our PXE - let mut addend_note = T::new(addend, owner_npk_m.hash()); + let mut addend_note = UintNote::new(addend, owner_npk_m.hash()); // docs:start:insert OuterNoteEmission::new(Option::some(self.set.insert(&mut addend_note))) @@ -74,14 +55,7 @@ impl BalanceSet { } } - pub fn sub( - self: Self, - owner_npk_m: NpkM, - amount: U128, - ) -> OuterNoteEmission - where - T: NoteInterface + NullifiableNote + OwnedNote + Eq, - { + pub fn sub(self: Self, owner_npk_m: NpkM, amount: U128) -> OuterNoteEmission { let subtracted = self.try_sub(amount, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL); // try_sub may have substracted more or less than amount. We must ensure that we subtracted at least as much as @@ -99,14 +73,7 @@ impl BalanceSet { // The `max_notes` parameter is used to fine-tune the number of constraints created by this function. The gate count // scales relatively linearly with `max_notes`, but a lower `max_notes` parameter increases the likelihood of // `try_sub` subtracting an amount smaller than `target_amount`. - pub fn try_sub( - self: Self, - target_amount: U128, - max_notes: u32, - ) -> U128 - where - T: NoteInterface + NullifiableNote + OwnedNote + Eq, - { + pub fn try_sub(self: Self, target_amount: U128, max_notes: u32) -> U128 { // We are using a preprocessor here (filter applied in an unconstrained context) instead of a filter because // we do not need to prove correct execution of the preprocessor. // Because the `min_sum` notes is not constrained, users could choose to e.g. not call it. However, all this @@ -120,7 +87,7 @@ impl BalanceSet { for i in 0..options.limit { if i < notes.len() { let note = notes.get_unchecked(i); - subtracted = subtracted + note.get_amount(); + subtracted = subtracted + note.get_value(); } } @@ -133,13 +100,10 @@ impl BalanceSet { // The preprocessor (a filter applied in an unconstrained context) does not check if total sum is larger or equal to // 'min_sum' - all it does is remove extra notes if it does reach that value. // Note that proper usage of this preprocessor requires for notes to be sorted in descending order. -pub fn preprocess_notes_min_sum( - notes: [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL], +pub fn preprocess_notes_min_sum( + notes: [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL], min_sum: U128, -) -> [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] -where - T: NoteInterface + NullifiableNote + OwnedNote, -{ +) -> [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] { let mut selected = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; let mut sum = U128::from_integer(0); for i in 0..notes.len() { @@ -150,7 +114,7 @@ where if notes[i].is_some() & sum < min_sum { let note = notes[i].unwrap_unchecked(); selected[i] = Option::some(note); - sum = sum.add(note.get_amount()); + sum = sum.add(note.get_value()); } } selected diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr deleted file mode 100644 index 677dd918033..00000000000 --- a/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr +++ /dev/null @@ -1,75 +0,0 @@ -use dep::aztec::{ - keys::getters::get_nsk_app, - macros::notes::partial_note, - note::utils::compute_note_hash_for_nullify, - oracle::random::random, - prelude::{NoteHeader, NullifiableNote, PrivateContext}, - protocol_types::{ - constants::GENERATOR_INDEX__NOTE_NULLIFIER, hash::poseidon2_hash_with_separator, - }, -}; - -trait OwnedNote { - fn new(amount: U128, owner_npk_m_hash: Field) -> Self; - fn get_amount(self) -> U128; -} - -// docs:start:TokenNote -#[partial_note(quote {amount})] -pub struct TokenNote { - // The amount of tokens in the note - amount: U128, - // The nullifying public key hash is used with the nsk_app to ensure that the note can be privately spent. - npk_m_hash: Field, - // Randomness of the note to hide its contents - randomness: Field, -} -// docs:end:TokenNote - -impl NullifiableNote for TokenNote { - // docs:start:nullifier - fn compute_nullifier( - self, - context: &mut PrivateContext, - note_hash_for_nullify: Field, - ) -> Field { - let secret = context.request_nsk_app(self.npk_m_hash); - poseidon2_hash_with_separator( - [note_hash_for_nullify, secret], - GENERATOR_INDEX__NOTE_NULLIFIER as Field, - ) - } - // docs:end:nullifier - - unconstrained fn compute_nullifier_without_context(self) -> Field { - let note_hash_for_nullify = compute_note_hash_for_nullify(self); - let secret = get_nsk_app(self.npk_m_hash); - poseidon2_hash_with_separator( - [note_hash_for_nullify, secret], - GENERATOR_INDEX__NOTE_NULLIFIER, - ) - } -} - -impl Eq for TokenNote { - fn eq(self, other: Self) -> bool { - (self.amount == other.amount) - & (self.npk_m_hash == other.npk_m_hash) - & (self.randomness == other.randomness) - } -} - -impl OwnedNote for TokenNote { - fn new(amount: U128, owner_npk_m_hash: Field) -> Self { - // We use the randomness to preserve the privacy of the note recipient by preventing brute-forcing, so a - // malicious sender could use non-random values to make the note less private. But they already know the full - // note pre-image anyway, and so the recipient already trusts them to not disclose this information. We can - // therefore assume that the sender will cooperate in the random value generation. - let randomness = unsafe { random() }; - Self { amount, npk_m_hash: owner_npk_m_hash, randomness, header: NoteHeader::empty() } - } - - fn get_amount(self) -> U128 { - self.amount - } -}