Skip to content

Commit

Permalink
addressing feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
sklppy88 committed Oct 27, 2024
1 parent 42b0443 commit b42f95b
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 130 deletions.
6 changes: 3 additions & 3 deletions docs/docs/aztec/concepts/storage/partial_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`),
Expand Down Expand Up @@ -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".

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export class TokenContract extends ContractBase {
},
balances: {
slot: new Fr(3n),
typ: 'BalancesMap<TokenNote>',
typ: 'BalancesMap',
},
total_supply: {
slot: new Fr(4n),
Expand Down Expand Up @@ -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. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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, <br> 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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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).

Expand All @@ -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.

Expand Down
31 changes: 13 additions & 18 deletions noir-projects/aztec-nr/uint-note/src/uint_note.nr
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,19 @@ use dep::aztec::{
},
};

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 {
// docs:start:UintNote
#[partial_note(quote {value})]
pub struct UintNote {
// The amount of tokens in the note
amount: U128,
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:TokenNote
// docs:end:UintNote

impl NullifiableNote for TokenNote {
impl NullifiableNote for UintNote {
// docs:start:nullifier
fn compute_nullifier(
self,
Expand All @@ -51,25 +46,25 @@ impl NullifiableNote for TokenNote {
}
}

impl Eq for TokenNote {
impl Eq for UintNote {
fn eq(self, other: Self) -> bool {
(self.amount == other.amount)
(self.value == other.value)
& (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 {
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 { amount, npk_m_hash: owner_npk_m_hash, randomness, header: NoteHeader::empty() }
Self { value, npk_m_hash: owner_npk_m_hash, randomness, header: NoteHeader::empty() }
}

fn get_amount(self) -> U128 {
self.amount
pub fn get_value(self) -> U128 {
self.value
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -191,12 +189,16 @@ contract TokenBlacklist {
// Add the token note to user's balances set
let msg_sender_keys = get_public_keys(context.msg_sender());
let to_keys = get_public_keys(to);
storage.balances.add(to, U128::from_integer(amount)).emit(encode_and_encrypt_note(
&mut context,
msg_sender_keys.ovpk_m,
to_keys.ivpk_m,
to,
));
// TODO: constrain encryption below - we are using unconstrained here only becuase of the following Noir issue
// https://github.com/noir-lang/noir/issues/5771
storage.balances.add(to, U128::from_integer(amount)).emit(
encode_and_encrypt_note_unconstrained(
&mut context,
msg_sender_keys.ovpk_m,
to_keys.ivpk_m,
to,
),
);
}

#[private]
Expand All @@ -213,12 +215,16 @@ contract TokenBlacklist {
}

let from_keys = get_public_keys(from);
storage.balances.sub(from, U128::from_integer(amount)).emit(encode_and_encrypt_note(
&mut context,
from_keys.ovpk_m,
from_keys.ivpk_m,
from,
));
// TODO: constrain encryption below - we are using unconstrained here only becuase of the following Noir issue
// https://github.com/noir-lang/noir/issues/5771
storage.balances.sub(from, U128::from_integer(amount)).emit(
encode_and_encrypt_note_unconstrained(
&mut context,
from_keys.ovpk_m,
from_keys.ivpk_m,
from,
),
);

TokenBlacklist::at(context.this_address())._increase_public_balance(to, amount).enqueue(
&mut context,
Expand Down Expand Up @@ -269,12 +275,16 @@ contract TokenBlacklist {
}

let from_keys = get_public_keys(from);
storage.balances.sub(from, U128::from_integer(amount)).emit(encode_and_encrypt_note(
&mut context,
from_keys.ovpk_m,
from_keys.ivpk_m,
from,
));
// TODO: constrain encryption below - we are using unconstrained here only becuase of the following Noir issue
// https://github.com/noir-lang/noir/issues/5771
storage.balances.sub(from, U128::from_integer(amount)).emit(
encode_and_encrypt_note_unconstrained(
&mut context,
from_keys.ovpk_m,
from_keys.ivpk_m,
from,
),
);

TokenBlacklist::at(context.this_address())._reduce_total_supply(amount).enqueue(&mut context);
}
Expand Down
71 changes: 53 additions & 18 deletions noir-projects/noir-contracts/contracts/token_contract/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ contract Token {
// Libs
use std::meta::derive;

use dep::uint_note::uint_note::{UintNote, UintNoteHidingPoint};
use dep::compressed_string::FieldCompressedString;

use dep::aztec::{
Expand All @@ -43,14 +42,17 @@ 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,
compute_authwit_nullifier,
};
// docs:end:import_authwit

use crate::types::{transparent_note::TransparentNote, balance_set::BalanceSet};
use crate::types::{balance_set::BalanceSet, transparent_note::TransparentNote};

// docs:end::imports

// In the first transfer iteration we are computing a lot of additional information (validating inputs, retrieving
Expand Down Expand Up @@ -80,7 +82,7 @@ contract Token {
minters: Map<AztecAddress, PublicMutable<bool, Context>, Context>,
// docs:end:storage_minters
// docs:start:storage_balances
balances: Map<AztecAddress, BalanceSet<UintNote, Context>, Context>,
balances: Map<AztecAddress, BalanceSet<Context>, Context>,
// docs:end:storage_balances
total_supply: PublicMutable<U128, Context>,
// docs:start:storage_pending_shields
Expand Down Expand Up @@ -461,20 +463,28 @@ contract Token {
let amount = U128::from_integer(amount);
// docs:start:increase_private_balance
// docs:start:encrypted
storage.balances.at(from).sub(from_keys.npk_m, amount).emit(encode_and_encrypt_note(
&mut context,
from_keys.ovpk_m,
from_keys.ivpk_m,
from,
));
// TODO: constrain encryption below - we are using unconstrained here only becuase of the following Noir issue
// https://github.com/noir-lang/noir/issues/5771
storage.balances.at(from).sub(from_keys.npk_m, amount).emit(
encode_and_encrypt_note_unconstrained(
&mut context,
from_keys.ovpk_m,
from_keys.ivpk_m,
from,
),
);
// docs:end:encrypted
// docs:end:increase_private_balance
storage.balances.at(to).add(to_keys.npk_m, amount).emit(encode_and_encrypt_note(
&mut context,
from_keys.ovpk_m,
to_keys.ivpk_m,
to,
));
// TODO: constrain encryption below - we are using unconstrained here only becuase of the following Noir issue
// https://github.com/noir-lang/noir/issues/5771
storage.balances.at(to).add(to_keys.npk_m, amount).emit(
encode_and_encrypt_note_unconstrained(
&mut context,
from_keys.ovpk_m,
to_keys.ivpk_m,
to,
),
);
}
// docs:end:transfer_from
// docs:start:burn
Expand All @@ -486,8 +496,15 @@ contract Token {
assert(nonce == 0, "invalid nonce");
}
let from_keys = get_public_keys(from);
// TODO: constrain encryption below - we are using unconstrained here only becuase of the following Noir issue
// https://github.com/noir-lang/noir/issues/5771
storage.balances.at(from).sub(from_keys.npk_m, U128::from_integer(amount)).emit(
encode_and_encrypt_note(&mut context, from_keys.ovpk_m, from_keys.ivpk_m, from),
encode_and_encrypt_note_unconstrained(
&mut context,
from_keys.ovpk_m,
from_keys.ivpk_m,
from,
),
);
Token::at(context.this_address())._reduce_total_supply(amount).enqueue(&mut context);
}
Expand Down Expand Up @@ -647,8 +664,10 @@ 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 = UintNote::finalization_payload().new(fee_payer_point, tx_fee);
let user_finalization_payload = UintNote::finalization_payload().new(user_point, refund_amount);
let fee_payer_finalization_payload =
UintNote::finalization_payload().new(&mut context, fee_payer_slot, tx_fee);
let user_finalization_payload =
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();
Expand All @@ -658,6 +677,22 @@ contract Token {
// docs:end:complete_refund

/// Internal ///
// docs:start:increase_public_balance
#[public]
#[internal]
fn _increase_public_balance(to: AztecAddress, amount: Field) {
let new_balance = storage.public_balances.at(to).read().add(U128::from_integer(amount));
storage.public_balances.at(to).write(new_balance);
}
// docs:end:increase_public_balance
// docs:start:reduce_total_supply
#[public]
#[internal]
fn _reduce_total_supply(amount: Field) {
// Only to be called from burn.
let new_supply = storage.total_supply.read().sub(U128::from_integer(amount));
storage.total_supply.write(new_supply);
}
// docs:end:reduce_total_supply
/// Unconstrained ///
// docs:start:balance_of_private
Expand Down
Loading

0 comments on commit b42f95b

Please sign in to comment.