diff --git a/docs/docs/reference/developer_references/smart_contract_reference/storage/private_state.md b/docs/docs/reference/developer_references/smart_contract_reference/storage/private_state.md index dfd7d5530ef..cfe794984f6 100644 --- a/docs/docs/reference/developer_references/smart_contract_reference/storage/private_state.md +++ b/docs/docs/reference/developer_references/smart_contract_reference/storage/private_state.md @@ -210,8 +210,6 @@ The `insert_from_public` allow public function to insert notes into private stor The usage is similar to using the `insert` method with the difference that this one is called in public functions. -#include_code insert_from_public /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust - ### `pop_notes` This function pops (gets, removes and returns) the notes the account has access to based on the provided filter. diff --git a/docs/docs/tutorials/codealong/contract_tutorials/token_contract.md b/docs/docs/tutorials/codealong/contract_tutorials/token_contract.md index c9b9410d416..5461f33d380 100644 --- a/docs/docs/tutorials/codealong/contract_tutorials/token_contract.md +++ b/docs/docs/tutorials/codealong/contract_tutorials/token_contract.md @@ -69,7 +69,7 @@ These are functions that have transparent logic, will execute in a publicly veri - `set_minter` enables accounts to be added / removed from the approved minter list - `mint_public` enables tokens to be minted to the public balance of an account - `mint_private` enables tokens to be minted to the private balance of an account (with some caveats we will dig into) -- `shield` enables tokens to be moved from a public balance to a private balance, not necessarily the same account (step 1 of a 2 step process) +- `transfer_to_public` enables tokens to be moved from a public balance to a private balance, not necessarily the same account (step 1 of a 2 step process) - `transfer_public` enables users to transfer tokens from one account's public balance to another account's public balance - `burn_public` enables users to burn tokens @@ -77,8 +77,6 @@ These are functions that have transparent logic, will execute in a publicly veri These are functions that have private logic and will be executed on user devices to maintain privacy. The only data that is submitted to the network is a proof of correct execution, new data commitments and nullifiers, so users will not reveal which contract they are interacting with or which function they are executing. The only information that will be revealed publicly is that someone executed a private transaction on Aztec. -- `redeem_shield` enables accounts to claim tokens that have been made private via `mint_private` or `shield` by providing the secret -- `transfer_to_public` enables an account to send tokens from their private balance to any other account's public balance - `transfer` enables an account to send tokens from their private balance to another account's private balance - `transfer_from` enables an account to send tokens from another account's private balance to another account's private balance - `cancel_authwit` enables an account to cancel an authorization to spend tokens @@ -160,7 +158,6 @@ Reading through the storage variables: - `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 `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. - `symbol`, `name`, and `decimals` are similar in meaning to ERC20 tokens on Ethereum. @@ -178,7 +175,7 @@ This function sets the creator of the contract (passed as `msg_sender` from the Public functions are declared with the `#[public]` macro above the function name. -As described in the [execution contexts section above](#execution-contexts), public function logic and transaction information is transparent to the world. Public functions update public state, but can be used to prepare data to be used in a private context, as we will go over below (e.g. see the [shield](#shield) function). +As described in the [execution contexts section above](#execution-contexts), public function logic and transaction information is transparent to the world. Public functions update public state, but can be used to finalize prepared in a private context (partial notes flow). Storage is referenced as `storage.variable`. @@ -204,17 +201,23 @@ First, storage is initialized. Then the function checks that the `msg_sender` is #### `mint_private` -This public function allows an account approved in the public `minters` mapping to create new private tokens that can be claimed by anyone that has the pre-image to the `secret_hash`. +This public function allows an account approved in the public `minters` mapping to create new private tokens. -First, public storage is initialized. Then it checks that the `msg_sender` is an approved minter. Then a new `TransparentNote` is created with the specified `amount` and `secret_hash`. You can read the details of the `TransparentNote` in the `types.nr` file [here (GitHub link)](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/noir-projects/noir-contracts/contracts/token_contract/src/types.nr#L61). The `amount` is added to the existing public `total_supply` and the storage value is updated. Then the new `TransparentNote` is added to the `pending_shields` using the `insert_from_public` function, which is accessible on the `PrivateSet` type. Then it's ready to be claimed by anyone with the `secret_hash` pre-image using the `redeem_shield` function. +First, partial note is prepared by the call to `_prepare_transfer_to_private` for the minted tokens recipient. Then a public call to `_finalize_mint_to_private_unsafe` is enqueued while `msg_sender`, `amount` and the `hiding_point_slot` are passed in via arguments. Since we set `from` to `msg_sender` here the usage of the unsafe function is safe. The enqueued call then checks the minter permissions of `from` and it finalizes the partial note for `to`. -#include_code mint_private /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust +#include_code mint_to_private /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust -#### `shield` +#### `transfer_to_private` -This public function enables an account to stage tokens from it's `public_balance` to be claimed as a private `TransparentNote` by any account that has the pre-image to the `secret_hash`. +This public function enables an account to send tokens from its `public_balance` to a private balance of an arbitrary recipient. -First, storage is initialized. Then it checks whether the calling contract (`context.msg_sender`) matches the account that the funds will be debited from. +First a partial note is prepared then a call to `_finalize_transfer_to_private_unsafe` is enqueued. The enqueued public call subtracts the `amount` from public balance of `msg_sender` and finalizes the partial note with the `amount`. + +#include_code transfer_to_private /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust + +#### `transfer_public` + +This public function enables public transfers between Aztec accounts. The sender's public balance will be debited the specified `amount` and the recipient's public balances will be credited with that amount. ##### Authorizing token spends @@ -224,14 +227,6 @@ If the `msg_sender` is the same as the account to debit tokens from, the authori It returns `1` to indicate successful execution. -#include_code shield /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust - -#### `transfer_public` - -This public function enables public transfers between Aztec accounts. The sender's public balance will be debited the specified `amount` and the recipient's public balances will be credited with that amount. - -After storage is initialized, the [authorization flow specified above](#authorizing-token-spends) is checked. Then the sender and recipient's balances are updated and saved to storage. - #include_code transfer_public /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust #### `burn_public` @@ -248,26 +243,16 @@ Private functions are declared with the `#[private]` macro above the function na ```rust #[private] - fn redeem_shield( + fn transfer_to_public( ``` As described in the [execution contexts section above](#execution-contexts), private function logic and transaction information is hidden from the world and is executed on user devices. Private functions update private state, but can pass data to the public execution context (e.g. see the [`transfer_to_public`](#transfer_to_public) function). 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 `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). - -The function returns `1` to indicate successful execution. - -#include_code redeem_shield /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust - #### `transfer_to_public` -This private function enables un-shielding of private `UintNote`s stored in `balances` to any Aztec account's `public_balance`. +This private function enables transferring of private balance (`UintNote` 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. @@ -303,7 +288,7 @@ Internal functions are functions that can only be called by this contract. The f #### `_increase_public_balance` -This function is called from [`transfer_to_public`](#transfer_to_public). The account's private balance is decremented in `shield` and the public balance is increased in this function. +This function is called from [`transfer_to_public`](#transfer_to_public). The account's private balance is decremented in `transfer_to_public` and the public balance is increased in this function. #include_code increase_public_balance /noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/private_set.nr b/noir-projects/aztec-nr/aztec/src/state_vars/private_set.nr index 003a6d49613..471ede3d9d5 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/private_set.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/private_set.nr @@ -41,6 +41,8 @@ impl PrivateSet where Note: NoteInterface + NullifiableNote, { + // TODO: This function is still around because of a stale blacklist token. It should most likely be nuked. If you + // need this functionality use partial notes instead. // docs:start:insert_from_public pub fn insert_from_public(self, note: &mut Note) { create_note_hash_from_public(self.context, self.storage_slot, note); 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 d7d33b87c64..d22ae39e5cf 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr @@ -22,11 +22,8 @@ contract Token { context::{PrivateCallInterface, PrivateContext}, encrypted_logs::{ encrypted_event_emission::encode_and_encrypt_event_unconstrained, - encrypted_note_emission::{ - encode_and_encrypt_note, encode_and_encrypt_note_unconstrained, - }, + encrypted_note_emission::encode_and_encrypt_note_unconstrained, }, - hash::compute_secret_hash, keys::getters::get_public_keys, macros::{ events::event, @@ -35,11 +32,9 @@ contract Token { }, oracle::random::random, prelude::{ - AztecAddress, FunctionSelector, Map, NoteGetterOptions, PrivateSet, PublicContext, - PublicMutable, SharedImmutable, + AztecAddress, FunctionSelector, Map, PublicContext, PublicMutable, SharedImmutable, }, protocol_types::{point::Point, traits::Serialize}, - utils::comparison::Comparator, }; use dep::uint_note::uint_note::UintNote; @@ -51,7 +46,7 @@ contract Token { }; // docs:end:import_authwit - use crate::types::{balance_set::BalanceSet, transparent_note::TransparentNote}; + use crate::types::balance_set::BalanceSet; // docs:end::imports @@ -85,9 +80,6 @@ contract Token { balances: Map, Context>, // docs:end:storage_balances total_supply: PublicMutable, - // docs:start:storage_pending_shields - pending_shields: PrivateSet, - // docs:end:storage_pending_shields public_balances: Map, Context>, symbol: SharedImmutable, name: SharedImmutable, @@ -111,6 +103,7 @@ contract Token { // docs:end:initialize_decimals } // docs:end:constructor + // docs:start:set_admin #[public] fn set_admin(new_admin: AztecAddress) { @@ -120,6 +113,7 @@ contract Token { // docs:end:write_admin } // docs:end:set_admin + #[public] #[view] fn public_get_name() -> FieldCompressedString { @@ -131,16 +125,19 @@ contract Token { fn private_get_name() -> FieldCompressedString { storage.name.read_private() } + #[public] #[view] fn public_get_symbol() -> pub FieldCompressedString { storage.symbol.read_public() } + #[private] #[view] fn private_get_symbol() -> pub FieldCompressedString { storage.symbol.read_private() } + #[public] #[view] fn public_get_decimals() -> pub u8 { @@ -155,6 +152,7 @@ contract Token { storage.decimals.read_private() // docs:end:read_decimals_private } + // docs:start:admin #[public] #[view] @@ -162,6 +160,7 @@ contract Token { storage.admin.read().to_field() } // docs:end:admin + // docs:start:is_minter #[public] #[view] @@ -169,6 +168,7 @@ contract Token { storage.minters.at(minter).read() } // docs:end:is_minter + // docs:start:total_supply #[public] #[view] @@ -176,6 +176,7 @@ contract Token { storage.total_supply.read().to_integer() } // docs:end:total_supply + // docs:start:balance_of_public #[public] #[view] @@ -183,6 +184,7 @@ contract Token { storage.public_balances.at(owner).read().to_integer() } // docs:end:balance_of_public + // docs:start:set_minter #[public] fn set_minter(minter: AztecAddress, approve: bool) { @@ -194,6 +196,7 @@ contract Token { // docs:end:write_minter } // docs:end:set_minter + // docs:start:mint_public #[public] fn mint_public(to: AztecAddress, amount: Field) { @@ -207,37 +210,7 @@ contract Token { storage.total_supply.write(supply); } // docs:end:mint_public - // docs:start:mint_private - // TODO(benesjan): To be nuked in a followup PR. - #[public] - fn mint_private_old(amount: Field, secret_hash: Field) { - assert(storage.minters.at(context.msg_sender()).read(), "caller is not minter"); - let pending_shields = storage.pending_shields; - let mut note = TransparentNote::new(amount, secret_hash); - let supply = storage.total_supply.read().add(U128::from_integer(amount)); - storage.total_supply.write(supply); - // docs:start:insert_from_public - pending_shields.insert_from_public(&mut note); - // docs:end:insert_from_public - } - // docs:end:mint_private - // docs:start:shield - #[public] - fn shield(from: AztecAddress, amount: Field, secret_hash: Field, nonce: Field) { - if (!from.eq(context.msg_sender())) { - // The redeem is only spendable once, so we need to ensure that you cannot insert multiple shields from the same message. - assert_current_call_valid_authwit_public(&mut context, from); - } else { - assert(nonce == 0, "invalid nonce"); - } - let amount = U128::from_integer(amount); - let from_balance = storage.public_balances.at(from).read().sub(amount); - let pending_shields = storage.pending_shields; - let mut note = TransparentNote::new(amount.to_field(), secret_hash); - storage.public_balances.at(from).write(from_balance); - pending_shields.insert_from_public(&mut note); - } - // docs:end:shield + // docs:start:transfer_public #[public] fn transfer_public(from: AztecAddress, to: AztecAddress, amount: Field, nonce: Field) { @@ -253,6 +226,7 @@ contract Token { storage.public_balances.at(to).write(to_balance); } // docs:end:transfer_public + // docs:start:burn_public #[public] fn burn_public(from: AztecAddress, amount: Field, nonce: Field) { @@ -270,32 +244,7 @@ contract Token { storage.total_supply.write(new_supply); } // docs:end:burn_public - // docs:start:redeem_shield - #[private] - fn redeem_shield(to: AztecAddress, amount: Field, secret: Field) { - let secret_hash = compute_secret_hash(secret); - // Pop 1 note (set_limit(1)) which has an amount stored in a field with index 0 (select(0, amount)) and - // a secret_hash stored in a field with index 1 (select(1, secret_hash)). - let mut options = NoteGetterOptions::new(); - options = options - .select(TransparentNote::properties().amount, Comparator.EQ, amount) - .select(TransparentNote::properties().secret_hash, Comparator.EQ, secret_hash) - .set_limit(1); - let notes = storage.pending_shields.pop_notes(options); - assert(notes.len() == 1, "note not popped"); - // Add the token note to user's balances set - // Note: Using context.msg_sender() as a sender below makes this incompatible with escrows because we send - // outgoing logs to that address and to send outgoing logs you need to get a hold of ovsk_m. - let from = context.msg_sender(); - let from_ovpk_m = get_public_keys(from).ovpk_m; - storage.balances.at(to).add(to, U128::from_integer(amount)).emit(encode_and_encrypt_note( - &mut context, - from_ovpk_m, - to, - context.msg_sender(), - )); - } - // docs:end:redeem_shield + // docs:start:transfer_to_public #[private] fn transfer_to_public(from: AztecAddress, to: AztecAddress, amount: Field, nonce: Field) { @@ -314,6 +263,7 @@ contract Token { Token::at(context.this_address())._increase_public_balance(to, amount).enqueue(&mut context); } // docs:end:transfer_to_public + // docs:start:transfer #[private] fn transfer(to: AztecAddress, amount: Field) { @@ -356,6 +306,7 @@ contract Token { ); } // docs:end:transfer + #[contract_library_method] fn subtract_balance( context: &mut PrivateContext, @@ -380,6 +331,7 @@ contract Token { compute_recurse_subtract_balance_call(*context, account, remaining).call(context) } } + // TODO(#7729): apply no_predicates to the contract interface method directly instead of having to use a wrapper // like we do here. #[no_predicates] @@ -391,6 +343,7 @@ contract Token { ) -> PrivateCallInterface<25, U128> { Token::at(context.this_address())._recurse_subtract_balance(account, remaining.to_field()) } + // TODO(#7728): even though the amount should be a U128, we can't have that type in a contract interface due to // serialization issues. #[internal] @@ -404,6 +357,7 @@ contract Token { RECURSIVE_TRANSFER_CALL_MAX_NOTES, ) } + /** * Cancel a private authentication witness. * @param inner_hash The inner hash of the authwit to cancel. @@ -416,6 +370,7 @@ contract Token { context.push_nullifier(nullifier); } // docs:end:cancel_authwit + // docs:start:transfer_from #[private] fn transfer_from(from: AztecAddress, to: AztecAddress, amount: Field, nonce: Field) { @@ -451,6 +406,7 @@ contract Token { )); } // docs:end:transfer_from + // docs:start:burn #[private] fn burn(from: AztecAddress, amount: Field, nonce: Field) { @@ -469,11 +425,11 @@ contract Token { } // docs:end:burn + // docs:start:transfer_to_private // Transfers token `amount` from public balance of message sender to a private balance of `to`. #[private] fn transfer_to_private(to: AztecAddress, amount: Field) { - // We check the minter permissions in the enqueued call as that allows us to avoid the need for `SharedMutable` - // which is less efficient. + // `from` is the owner of the public balance from which we'll subtract the `amount`. let from = context.msg_sender(); let token = Token::at(context.this_address()); @@ -486,6 +442,7 @@ contract Token { &mut context, ); } + // docs:end:transfer_to_private /// Prepares a transfer to a private balance of `to`. The transfer then needs to be /// finalized by calling `finalize_transfer_to_private`. Returns a hiding point slot. @@ -588,6 +545,7 @@ contract Token { finalization_payload.emit(); } + // docs:start:mint_to_private /// Mints token `amount` to a private balance of `to`. Message sender has to have minter permissions (checked /// in the enqueud call). #[private] @@ -608,6 +566,7 @@ contract Token { ._finalize_mint_to_private_unsafe(context.msg_sender(), amount, hiding_point_slot) .enqueue(&mut context); } + // docs:end:mint_to_private /// Finalizes a mint of token `amount` to a private balance of `to`. The mint must be prepared by calling /// `prepare_transfer_to_private` first and the resulting @@ -828,6 +787,7 @@ contract Token { storage.public_balances.at(to).write(new_balance); } // docs:end:increase_public_balance + // docs:start:reduce_total_supply #[public] #[internal] @@ -837,6 +797,7 @@ contract Token { storage.total_supply.write(new_supply); } // docs:end:reduce_total_supply + /// Unconstrained /// // docs:start:balance_of_private pub(crate) unconstrained fn balance_of_private(owner: AztecAddress) -> pub Field { diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test.nr index 35a4c1f1aff..b746bbe1f6c 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/test.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test.nr @@ -8,4 +8,3 @@ mod transfer_to_public; mod refunds; mod minting; mod reading_constants; -mod shielding; diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/minting.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/minting.nr index bc4f877d2a3..1b572b872ff 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/test/minting.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/minting.nr @@ -1,6 +1,4 @@ -use crate::test::utils; -use crate::{Token, types::transparent_note::TransparentNote}; -use dep::aztec::{oracle::random::random, test::helpers::cheatcodes}; +use crate::{test::utils, Token}; #[test] unconstrained fn mint_public_success() { diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/shielding.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/shielding.nr deleted file mode 100644 index aef1d98f9cc..00000000000 --- a/noir-projects/noir-contracts/contracts/token_contract/src/test/shielding.nr +++ /dev/null @@ -1,179 +0,0 @@ -use crate::test::utils; -use crate::{Token, types::transparent_note::TransparentNote}; -use dep::authwit::cheatcodes as authwit_cheatcodes; -use dep::aztec::{hash::compute_secret_hash, oracle::random::random}; - -#[test] -unconstrained fn shielding_on_behalf_of_self() { - // Setup without account contracts. We are not using authwits here, so dummy accounts are enough - let (env, token_contract_address, owner, _, mint_amount) = - utils::setup_and_mint_public(/* with_account_contracts */ false); - let secret = random(); - let secret_hash = compute_secret_hash(secret); - // Shield tokens - let shield_amount = mint_amount / 10; - Token::at(token_contract_address).shield(owner, shield_amount, secret_hash, 0).call( - &mut env.public(), - ); - - // We need to manually add the note to TXE because `TransparentNote` does not support automatic note log delivery. - env.add_note( - &mut TransparentNote::new(shield_amount, secret_hash), - Token::storage_layout().pending_shields.slot, - token_contract_address, - ); - - // Redeem our shielded tokens - Token::at(token_contract_address).redeem_shield(owner, shield_amount, secret).call( - &mut env.private(), - ); - - // Check balances - utils::check_public_balance(token_contract_address, owner, mint_amount - shield_amount); - utils::check_private_balance(token_contract_address, owner, shield_amount); -} - -#[test] -unconstrained fn shielding_on_behalf_of_other() { - let (env, token_contract_address, owner, recipient, mint_amount) = - utils::setup_and_mint_public(/* with_account_contracts */ true); - let secret = random(); - let secret_hash = compute_secret_hash(secret); - - // Shield tokens on behalf of owner - let shield_amount = 1000; - let shield_call_interface = - Token::at(token_contract_address).shield(owner, shield_amount, secret_hash, 0); - authwit_cheatcodes::add_public_authwit_from_call_interface( - owner, - recipient, - shield_call_interface, - ); - // Impersonate recipient to perform the call - env.impersonate(recipient); - // Shield tokens - shield_call_interface.call(&mut env.public()); - - // Become owner again - env.impersonate(owner); - // We need to manually add the note to TXE because `TransparentNote` does not support automatic note log delivery. - env.add_note( - &mut TransparentNote::new(shield_amount, secret_hash), - Token::storage_layout().pending_shields.slot, - token_contract_address, - ); - - // Redeem our shielded tokens - Token::at(token_contract_address).redeem_shield(owner, shield_amount, secret).call( - &mut env.private(), - ); - - // Check balances - utils::check_public_balance(token_contract_address, owner, mint_amount - shield_amount); - utils::check_private_balance(token_contract_address, owner, shield_amount); -} - -#[test] -unconstrained fn shielding_failure_on_behalf_of_self_more_than_balance() { - // Setup without account contracts. We are not using authwits here, so dummy accounts are enough - let (env, token_contract_address, owner, _, mint_amount) = - utils::setup_and_mint_public(/* with_account_contracts */ true); - let secret = random(); - let secret_hash = compute_secret_hash(secret); - // Shield tokens - let shield_amount = mint_amount + 1; - let shield_call_interface = - Token::at(token_contract_address).shield(owner, shield_amount, secret_hash, 0); - env.assert_public_call_fails(shield_call_interface); - - // Check balances - utils::check_public_balance(token_contract_address, owner, mint_amount); - utils::check_private_balance(token_contract_address, owner, 0); -} - -#[test] -unconstrained fn shielding_failure_on_behalf_of_self_invalid_nonce() { - // Setup without account contracts. We are not using authwits here, so dummy accounts are enough - let (env, token_contract_address, owner, _, mint_amount) = - utils::setup_and_mint_public(/* with_account_contracts */ true); - let secret = random(); - let secret_hash = compute_secret_hash(secret); - // Shield tokens - let shield_amount = mint_amount / 10; - let shield_call_interface = - Token::at(token_contract_address).shield(owner, shield_amount, secret_hash, random()); - env.assert_public_call_fails(shield_call_interface); - - // Check balances - utils::check_public_balance(token_contract_address, owner, mint_amount); - utils::check_private_balance(token_contract_address, owner, 0); -} - -#[test] -unconstrained fn shielding_failure_on_behalf_of_other_more_than_balance() { - // Setup without account contracts. We are not using authwits here, so dummy accounts are enough - let (env, token_contract_address, owner, recipient, mint_amount) = - utils::setup_and_mint_public(/* with_account_contracts */ true); - let secret = random(); - let secret_hash = compute_secret_hash(secret); - // Shield tokens on behalf of owner - let shield_amount = mint_amount + 1; - let shield_call_interface = - Token::at(token_contract_address).shield(owner, shield_amount, secret_hash, 0); - authwit_cheatcodes::add_public_authwit_from_call_interface( - owner, - recipient, - shield_call_interface, - ); - // Impersonate recipient to perform the call - env.impersonate(recipient); - // Shield tokens - env.assert_public_call_fails(shield_call_interface); - - // Check balances - utils::check_public_balance(token_contract_address, owner, mint_amount); - utils::check_private_balance(token_contract_address, owner, 0); -} - -#[test] -unconstrained fn shielding_failure_on_behalf_of_other_wrong_caller() { - // Setup without account contracts. We are not using authwits here, so dummy accounts are enough - let (env, token_contract_address, owner, recipient, mint_amount) = - utils::setup_and_mint_public(/* with_account_contracts */ true); - let secret = random(); - let secret_hash = compute_secret_hash(secret); - // Shield tokens on behalf of owner - let shield_amount = mint_amount + 1; - let shield_call_interface = - Token::at(token_contract_address).shield(owner, shield_amount, secret_hash, 0); - authwit_cheatcodes::add_public_authwit_from_call_interface(owner, owner, shield_call_interface); - // Impersonate recipient to perform the call - env.impersonate(recipient); - // Shield tokens - env.assert_public_call_fails(shield_call_interface); - - // Check balances - utils::check_public_balance(token_contract_address, owner, mint_amount); - utils::check_private_balance(token_contract_address, owner, 0); -} - -#[test] -unconstrained fn shielding_failure_on_behalf_of_other_without_approval() { - // Setup without account contracts. We are not using authwits here, so dummy accounts are enough - let (env, token_contract_address, owner, recipient, mint_amount) = - utils::setup_and_mint_public(/* with_account_contracts */ true); - let secret = random(); - let secret_hash = compute_secret_hash(secret); - // Shield tokens on behalf of owner - let shield_amount = mint_amount + 1; - let shield_call_interface = - Token::at(token_contract_address).shield(owner, shield_amount, secret_hash, 0); - // Impersonate recipient to perform the call - env.impersonate(recipient); - // Shield tokens - env.assert_public_call_fails(shield_call_interface); - - // Check balances - utils::check_public_balance(token_contract_address, owner, mint_amount); - utils::check_private_balance(token_contract_address, owner, 0); -} 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 cad3fda646e..1bc5f644e4f 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/types.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/types.nr @@ -1,2 +1 @@ -pub(crate) mod transparent_note; pub(crate) mod balance_set; diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/types/transparent_note.nr b/noir-projects/noir-contracts/contracts/token_contract/src/types/transparent_note.nr deleted file mode 100644 index ed1f08e0d74..00000000000 --- a/noir-projects/noir-contracts/contracts/token_contract/src/types/transparent_note.nr +++ /dev/null @@ -1,63 +0,0 @@ -// docs:start:token_types_all -use dep::aztec::{ - macros::notes::note, - note::utils::compute_note_hash_for_nullify, - prelude::{NoteHeader, NullifiableNote, PrivateContext}, - protocol_types::{ - constants::GENERATOR_INDEX__NOTE_NULLIFIER, hash::poseidon2_hash_with_separator, - }, -}; - -use dep::std::mem::zeroed; - -// Transparent note represents a note that is created in the clear (public execution), but can only be spent by those -// that know the preimage of the "secret_hash" (the secret). This is typically used when shielding a token balance. -// Owner of the tokens provides a "secret_hash" as an argument to the public "shield" function and then the tokens -// can be redeemed in private by presenting the preimage of the "secret_hash" (the secret). -#[note] -pub struct TransparentNote { - amount: Field, - secret_hash: Field, -} - -impl NullifiableNote for TransparentNote { - // Computing a nullifier in a transparent note is not guarded by making secret a part of the nullifier preimage (as - // is common in other cases) and instead is guarded by the functionality of "redeem_shield" function. There we do - // the following: - // 1) We pass the secret as an argument to the function and use it to compute a secret hash, - // 2) we fetch a note via the "get_notes" oracle which accepts the secret hash as an argument, - // 3) the "get_notes" oracle constrains that the secret hash in the returned note matches the one computed in - // circuit. - // This achieves that the note can only be spent by the party that knows the secret. - fn compute_nullifier( - self, - _context: &mut PrivateContext, - _note_hash_for_nullify: Field, - ) -> Field { - let note_hash_for_nullify = compute_note_hash_for_nullify(self); - poseidon2_hash_with_separator( - [note_hash_for_nullify], - GENERATOR_INDEX__NOTE_NULLIFIER as Field, - ) - } - - unconstrained fn compute_nullifier_without_context(self) -> Field { - // compute_nullifier ignores both of its parameters so we can reuse it here - self.compute_nullifier(zeroed(), zeroed()) - } -} - -impl TransparentNote { - // CONSTRUCTORS - pub fn new(amount: Field, secret_hash: Field) -> Self { - TransparentNote { amount, secret_hash, header: NoteHeader::empty() } - } -} - -impl Eq for TransparentNote { - fn eq(self, other: Self) -> bool { - (self.amount == other.amount) & (self.secret_hash == other.secret_hash) - } -} - -// docs:end:token_types_all diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts index 7623818516f..5f5d8a12522 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts @@ -222,8 +222,7 @@ export class BlacklistTokenContractTest { this.logger.verbose(`Public balance of wallet 0: ${publicBalance}`); expect(publicBalance).toEqual(this.tokenSim.balanceOfPublic(address)); - tokenSim.mintPrivate(amount); - tokenSim.redeemShield(address, amount); + tokenSim.mintPrivate(address, amount); const privateBalance = await asset.methods.balance_of_private(address).simulate(); this.logger.verbose(`Private balance of wallet 0: ${privateBalance}`); expect(privateBalance).toEqual(tokenSim.balanceOfPrivate(address)); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/minting.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/minting.test.ts index 19dd05a9aad..62a1d0af5ca 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/minting.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/minting.test.ts @@ -83,13 +83,10 @@ describe('e2e_blacklist_token_contract mint', () => { }); describe('Mint flow', () => { - it('mint_private as minter', async () => { + it('mint_private as minter and redeem as recipient', async () => { const receipt = await asset.methods.mint_private(amount, secretHash).send().wait(); - tokenSim.mintPrivate(amount); txHash = receipt.txHash; - }); - it('redeem as recipient', async () => { await t.addPendingShieldNoteToPXE(0, amount, secretHash, txHash); const receiptClaim = await asset.methods @@ -97,7 +94,7 @@ describe('e2e_blacklist_token_contract mint', () => { .send() .wait({ debug: true }); - tokenSim.redeemShield(wallets[0].getAddress(), amount); + tokenSim.mintPrivate(wallets[0].getAddress(), amount); // 1 note should be created containing `amount` of tokens const { visibleIncomingNotes } = receiptClaim.debugInfo!; expect(visibleIncomingNotes.length).toBe(1); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/shielding.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/shielding.test.ts index de9a3e1529d..c2955daf770 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/shielding.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/shielding.test.ts @@ -37,14 +37,13 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { const receipt = await asset.methods.shield(wallets[0].getAddress(), amount, secretHash, 0).send().wait(); - tokenSim.shield(wallets[0].getAddress(), amount); - await t.tokenSim.check(); - // Redeem it await t.addPendingShieldNoteToPXE(0, amount, secretHash, receipt.txHash); await asset.methods.redeem_shield(wallets[0].getAddress(), amount, secret).send().wait(); - tokenSim.redeemShield(wallets[0].getAddress(), amount); + // Check that the result matches token sim + tokenSim.transferToPrivate(wallets[0].getAddress(), wallets[0].getAddress(), amount); + await t.tokenSim.check(); }); it('on behalf of other', async () => { @@ -59,9 +58,6 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { const receipt = await action.send().wait(); - tokenSim.shield(wallets[0].getAddress(), amount); - await t.tokenSim.check(); - // Check that replaying the shield should fail! await expect( asset.withWallet(wallets[1]).methods.shield(wallets[0].getAddress(), amount, secretHash, nonce).simulate(), @@ -71,7 +67,9 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { await t.addPendingShieldNoteToPXE(0, amount, secretHash, receipt.txHash); await asset.methods.redeem_shield(wallets[0].getAddress(), amount, secret).send().wait(); - tokenSim.redeemShield(wallets[0].getAddress(), amount); + // Check that the result matches token sim + tokenSim.transferToPrivate(wallets[0].getAddress(), wallets[0].getAddress(), amount); + await t.tokenSim.check(); }); describe('failure cases', () => { diff --git a/yarn-project/end-to-end/src/e2e_fees/fees_test.ts b/yarn-project/end-to-end/src/e2e_fees/fees_test.ts index aa930a1a56e..fad11c79369 100644 --- a/yarn-project/end-to-end/src/e2e_fees/fees_test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/fees_test.ts @@ -4,12 +4,8 @@ import { type AztecAddress, type AztecNode, type DebugLogger, - ExtendedNote, - Fr, - Note, type PXE, SignerlessWallet, - type TxHash, createDebugLogger, sleep, } from '@aztec/aztec.js'; @@ -126,22 +122,6 @@ export class FeesTest { expect(balanceAfter).toEqual(balanceBefore + amount); } - /** Adds a pending shield transparent node for the banana coin token contract to the pxe. */ - // TODO(benesjan): nuke this - async addPendingShieldNoteToPXE(owner: AztecAddress | AccountWallet, amount: bigint, secretHash: Fr, txHash: TxHash) { - const note = new Note([new Fr(amount), secretHash]); - const ownerAddress = 'getAddress' in owner ? owner.getAddress() : owner; - const extendedNote = new ExtendedNote( - note, - ownerAddress, - this.bananaCoin.address, - BananaCoin.storage.pending_shields.slot, - BananaCoin.notes.TransparentNote.id, - txHash, - ); - await this.pxe.addNote(extendedNote, ownerAddress); - } - public async applyBaseSnapshots() { await this.applyInitialAccountsSnapshot(); await this.applyPublicDeployAccountsSnapshot(); diff --git a/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts b/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts index bf9fdb65ba8..870f40707c9 100644 --- a/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts @@ -1,12 +1,4 @@ -import { - type AccountWallet, - type AztecAddress, - BatchCall, - Fr, - PrivateFeePaymentMethod, - computeSecretHash, - sleep, -} from '@aztec/aztec.js'; +import { type AccountWallet, type AztecAddress, BatchCall, PrivateFeePaymentMethod, sleep } from '@aztec/aztec.js'; import { type GasSettings } from '@aztec/circuits.js'; import { type TokenContract as BananaCoin, FPCContract } from '@aztec/noir-contracts.js'; @@ -179,8 +171,8 @@ describe('e2e_fees private_payment', () => { * BC increase total supply * * PUBLIC TEARDOWN - * increase sequencer/fee recipient/FPC admin private banana balance by feeAmount by finalizing partial note - * increase Alice's private banana balance by feeAmount by finalizing partial note + * increase sequencer/fee recipient/FPC admin private banana balance by feeAmount by finalizing partial note + * increase Alice's private banana balance by feeAmount by finalizing partial note */ const newlyMintedBananas = 10n; const from = aliceAddress; // we are setting from to Alice here because of TODO(#9887) @@ -221,23 +213,20 @@ describe('e2e_fees private_payment', () => { * setup fee and refund partial notes * setup public teardown call * - * * PRIVATE APP LOGIC - * N/A + * a partial note is prepared * * PUBLIC APP LOGIC * BC decrease Alice public balance by shieldedBananas - * BC create transparent note of shieldedBananas + * BC finalizes the partial note with an amount --> this is where the note is created in public * * PUBLIC TEARDOWN - * increase sequencer/fee recipient/FPC admin private banana balance by feeAmount by finalizing partial note - * increase Alice's private banana balance by feeAmount by finalizing partial note + * increase sequencer/fee recipient/FPC admin private banana balance by feeAmount by finalizing partial note + * increase Alice's private banana balance by feeAmount by finalizing partial note */ - const shieldedBananas = 1n; - const shieldSecret = Fr.random(); - const shieldSecretHash = computeSecretHash(shieldSecret); + const amountTransferredToPrivate = 1n; const tx = await bananaCoin.methods - .shield(aliceAddress, shieldedBananas, shieldSecretHash, 0n) + .transfer_to_private(aliceAddress, amountTransferredToPrivate) .send({ fee: { gasSettings, @@ -256,30 +245,27 @@ describe('e2e_fees private_payment', () => { await expectMapping( t.getBananaPrivateBalanceFn, [aliceAddress, bananaFPC.address, sequencerAddress], - [initialAlicePrivateBananas - feeAmount, 0n, initialSequencerPrivateBananas + feeAmount], + [ + initialAlicePrivateBananas - feeAmount + amountTransferredToPrivate, + 0n, + initialSequencerPrivateBananas + feeAmount, + ], ); await expectMapping( t.getBananaPublicBalanceFn, [aliceAddress, bananaFPC.address, sequencerAddress], - [initialAlicePublicBananas - shieldedBananas, initialFPCPublicBananas, 0n], + [initialAlicePublicBananas - amountTransferredToPrivate, initialFPCPublicBananas, 0n], ); await expectMapping( t.getGasBalanceFn, [aliceAddress, bananaFPC.address, sequencerAddress], [initialAliceGas, initialFPCGas - feeAmount, initialSequencerGas], ); - - await expect( - t.addPendingShieldNoteToPXE(t.aliceWallet, shieldedBananas, shieldSecretHash, tx.txHash), - ).resolves.toBeUndefined(); }); it('pays fees for tx that creates notes in both private and public', async () => { - const privateTransfer = 1n; - const shieldedBananas = 1n; - const shieldSecret = Fr.random(); - const shieldSecretHash = computeSecretHash(shieldSecret); - + const amountTransferredInPrivate = 1n; + const amountTransferredToPrivate = 2n; /** * PRIVATE SETUP * check authwit @@ -290,18 +276,19 @@ describe('e2e_fees private_payment', () => { * PRIVATE APP LOGIC * reduce Alice's private balance by privateTransfer * create note for Bob with privateTransfer amount of private BC + * prepare partial note (in the transfer to private) * * PUBLIC APP LOGIC - * BC decrease Alice public balance by shieldedBananas - * BC create transparent note of shieldedBananas + * BC decrease Alice public balance by amountTransferredToPrivate + * BC finalize partial note with amountTransferredToPrivate (this is where the note is created in public) * * PUBLIC TEARDOWN - * increase sequencer/fee recipient/FPC admin private banana balance by feeAmount by finalizing partial note - * increase Alice's private banana balance by feeAmount by finalizing partial note + * increase sequencer/fee recipient/FPC admin private banana balance by feeAmount by finalizing partial note + * increase Alice's private banana balance by feeAmount by finalizing partial note */ const tx = await new BatchCall(aliceWallet, [ - bananaCoin.methods.transfer(bobAddress, privateTransfer).request(), - bananaCoin.methods.shield(aliceAddress, shieldedBananas, shieldSecretHash, 0n).request(), + bananaCoin.methods.transfer(bobAddress, amountTransferredInPrivate).request(), + bananaCoin.methods.transfer_to_private(aliceAddress, amountTransferredToPrivate).request(), ]) .send({ fee: { @@ -322,8 +309,8 @@ describe('e2e_fees private_payment', () => { t.getBananaPrivateBalanceFn, [aliceAddress, bobAddress, bananaFPC.address, sequencerAddress], [ - initialAlicePrivateBananas - feeAmount - privateTransfer, - initialBobPrivateBananas + privateTransfer, + initialAlicePrivateBananas - feeAmount - amountTransferredInPrivate + amountTransferredToPrivate, + initialBobPrivateBananas + amountTransferredInPrivate, 0n, initialSequencerPrivateBananas + feeAmount, ], @@ -331,17 +318,13 @@ describe('e2e_fees private_payment', () => { await expectMapping( t.getBananaPublicBalanceFn, [aliceAddress, bananaFPC.address, sequencerAddress], - [initialAlicePublicBananas - shieldedBananas, initialFPCPublicBananas, 0n], + [initialAlicePublicBananas - amountTransferredToPrivate, initialFPCPublicBananas, 0n], ); await expectMapping( t.getGasBalanceFn, [aliceAddress, bananaFPC.address, sequencerAddress], [initialAliceGas, initialFPCGas - feeAmount, initialSequencerGas], ); - - await expect( - t.addPendingShieldNoteToPXE(t.aliceWallet, shieldedBananas, shieldSecretHash, tx.txHash), - ).resolves.toBeUndefined(); }); it('rejects txs that dont have enough balance to cover gas costs', async () => { diff --git a/yarn-project/end-to-end/src/e2e_lending_contract.test.ts b/yarn-project/end-to-end/src/e2e_lending_contract.test.ts index cb356d6ebda..a80b6264203 100644 --- a/yarn-project/end-to-end/src/e2e_lending_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_lending_contract.test.ts @@ -107,11 +107,9 @@ describe('e2e_lending_contract', () => { } lendingSim.mintStableCoinOutsideLoan(lendingAccount.address, 10000n, true); - lendingSim.stableCoin.redeemShield(lendingAccount.address, 10000n); lendingSim.mintStableCoinOutsideLoan(lendingAccount.address, 10000n, false); - lendingSim.collateralAsset.mintPrivate(10000n); - lendingSim.collateralAsset.redeemShield(lendingAccount.address, 10000n); + lendingSim.collateralAsset.mintPrivate(lendingAccount.address, 10000n); lendingSim.collateralAsset.mintPublic(lendingAccount.address, 10000n); }); diff --git a/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts b/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts index 737575c3597..2df25d15ab6 100644 --- a/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts +++ b/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts @@ -8,12 +8,9 @@ import { type DebugLogger, type DeployL1Contracts, EthAddress, - ExtendedNote, type Fq, Fr, - Note, type PXE, - type TxHash, createDebugLogger, deployL1Contract, } from '@aztec/aztec.js'; @@ -327,19 +324,6 @@ export class FullProverTest { await this.acvmConfigCleanup?.(); } - async addPendingShieldNoteToPXE(accountIndex: number, amount: bigint, secretHash: Fr, txHash: TxHash) { - const note = new Note([new Fr(amount), secretHash]); - const extendedNote = new ExtendedNote( - note, - this.accounts[accountIndex].address, - this.fakeProofsAsset.address, - TokenContract.storage.pending_shields.slot, - TokenContract.notes.TransparentNote.id, - txHash, - ); - await this.wallets[accountIndex].addNote(extendedNote); - } - async applyMintSnapshot() { await this.snapshotManager.snapshot( 'mint', @@ -375,8 +359,7 @@ export class FullProverTest { this.logger.verbose(`Public balance of wallet 0: ${publicBalance}`); expect(publicBalance).toEqual(this.tokenSim.balanceOfPublic(address)); - tokenSim.mintPrivate(amount); - tokenSim.redeemShield(address, amount); + tokenSim.mintPrivate(address, amount); const privateBalance = await asset.methods.balance_of_private(address).simulate(); this.logger.verbose(`Private balance of wallet 0: ${privateBalance}`); expect(privateBalance).toEqual(tokenSim.balanceOfPrivate(address)); diff --git a/yarn-project/end-to-end/src/e2e_synching.test.ts b/yarn-project/end-to-end/src/e2e_synching.test.ts index 448b36e69c6..af1963d24b9 100644 --- a/yarn-project/end-to-end/src/e2e_synching.test.ts +++ b/yarn-project/end-to-end/src/e2e_synching.test.ts @@ -36,7 +36,6 @@ import { getSchnorrAccount } from '@aztec/accounts/schnorr'; import { createArchiver } from '@aztec/archiver'; import { AztecNodeService } from '@aztec/aztec-node'; import { - type AccountWallet, type AccountWalletWithSecretKey, AnvilTestWatcher, BatchCall, @@ -48,7 +47,7 @@ import { sleep, } from '@aztec/aztec.js'; // eslint-disable-next-line no-restricted-imports -import { ExtendedNote, L2Block, LogType, Note, type TxHash, tryStop } from '@aztec/circuit-types'; +import { L2Block, LogType, tryStop } from '@aztec/circuit-types'; import { type AztecAddress } from '@aztec/circuits.js'; import { getL1ContractsConfigEnvVars } from '@aztec/ethereum'; import { Timer } from '@aztec/foundation/timer'; @@ -184,27 +183,6 @@ class TestVariant { } } - private async addPendingShieldNoteToPXE(args: { - amount: bigint; - secretHash: Fr; - txHash: TxHash; - accountAddress: AztecAddress; - assetAddress: AztecAddress; - wallet: AccountWallet; - }) { - const { accountAddress, assetAddress, amount, secretHash, txHash, wallet } = args; - const note = new Note([new Fr(amount), secretHash]); - const extendedNote = new ExtendedNote( - note, - accountAddress, - assetAddress, - TokenContract.storage.pending_shields.slot, - TokenContract.notes.TransparentNote.id, - txHash, - ); - await wallet.addNote(extendedNote); - } - async createAndSendTxs() { if (!this.pxe) { throw new Error('Undefined PXE'); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/shielding.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/shielding.test.ts deleted file mode 100644 index d7a576e09b5..00000000000 --- a/yarn-project/end-to-end/src/e2e_token_contract/shielding.test.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { Fr, computeSecretHash } from '@aztec/aztec.js'; - -import { U128_UNDERFLOW_ERROR } from '../fixtures/fixtures.js'; -import { TokenContractTest } from './token_contract_test.js'; - -describe('e2e_token_contract shield + redeem shield', () => { - const t = new TokenContractTest('shielding'); - let { asset, accounts, tokenSim, wallets } = t; - const secret = Fr.random(); - let secretHash: Fr; - - beforeAll(async () => { - await t.applyBaseSnapshots(); - await t.applyMintSnapshot(); - await t.setup(); - // Have to destructure again to ensure we have latest refs. - ({ asset, accounts, tokenSim, wallets } = t); - secretHash = computeSecretHash(secret); - }); - - afterAll(async () => { - await t.teardown(); - }); - - afterEach(async () => { - await t.tokenSim.check(); - }); - - it('on behalf of self', async () => { - const balancePub = await asset.methods.balance_of_public(accounts[0].address).simulate(); - const amount = balancePub / 2n; - expect(amount).toBeGreaterThan(0n); - - const receipt = await asset.methods.shield(accounts[0].address, amount, secretHash, 0).send().wait(); - - tokenSim.shield(accounts[0].address, amount); - await tokenSim.check(); - - // Redeem it - await t.addPendingShieldNoteToPXE(0, amount, secretHash, receipt.txHash); - await asset.methods.redeem_shield(accounts[0].address, amount, secret).send().wait(); - - tokenSim.redeemShield(accounts[0].address, amount); - }); - - it('on behalf of other', async () => { - const balancePub = await asset.methods.balance_of_public(accounts[0].address).simulate(); - const amount = balancePub / 2n; - const nonce = Fr.random(); - expect(amount).toBeGreaterThan(0n); - - // We need to compute the message we want to sign and add it to the wallet as approved - const action = asset.withWallet(wallets[1]).methods.shield(accounts[0].address, amount, secretHash, nonce); - await wallets[0].setPublicAuthWit({ caller: accounts[1].address, action }, true).send().wait(); - - const receipt = await action.send().wait(); - - tokenSim.shield(accounts[0].address, amount); - await tokenSim.check(); - - // Check that replaying the shield should fail! - await expect( - asset.withWallet(wallets[1]).methods.shield(accounts[0].address, amount, secretHash, nonce).simulate(), - ).rejects.toThrow(/unauthorized/); - - // Redeem it - await t.addPendingShieldNoteToPXE(0, amount, secretHash, receipt.txHash); - await asset.methods.redeem_shield(accounts[0].address, amount, secret).send().wait(); - - tokenSim.redeemShield(accounts[0].address, amount); - }); - - describe('failure cases', () => { - it('on behalf of self (more than balance)', async () => { - const balancePub = await asset.methods.balance_of_public(accounts[0].address).simulate(); - const amount = balancePub + 1n; - expect(amount).toBeGreaterThan(0n); - - await expect(asset.methods.shield(accounts[0].address, amount, secretHash, 0).simulate()).rejects.toThrow( - U128_UNDERFLOW_ERROR, - ); - }); - - it('on behalf of self (invalid nonce)', async () => { - const balancePub = await asset.methods.balance_of_public(accounts[0].address).simulate(); - const amount = balancePub + 1n; - expect(amount).toBeGreaterThan(0n); - - await expect(asset.methods.shield(accounts[0].address, amount, secretHash, 1).simulate()).rejects.toThrow( - 'Assertion failed: invalid nonce', - ); - }); - - it('on behalf of other (more than balance)', async () => { - const balancePub = await asset.methods.balance_of_public(accounts[0].address).simulate(); - const amount = balancePub + 1n; - const nonce = Fr.random(); - expect(amount).toBeGreaterThan(0n); - - // We need to compute the message we want to sign and add it to the wallet as approved - const action = asset.withWallet(wallets[1]).methods.shield(accounts[0].address, amount, secretHash, nonce); - await wallets[0].setPublicAuthWit({ caller: accounts[1].address, action }, true).send().wait(); - - await expect(action.simulate()).rejects.toThrow(U128_UNDERFLOW_ERROR); - }); - - it('on behalf of other (wrong designated caller)', async () => { - const balancePub = await asset.methods.balance_of_public(accounts[0].address).simulate(); - const amount = balancePub + 1n; - const nonce = Fr.random(); - expect(amount).toBeGreaterThan(0n); - - // We need to compute the message we want to sign and add it to the wallet as approved - const action = asset.withWallet(wallets[2]).methods.shield(accounts[0].address, amount, secretHash, nonce); - await wallets[0].setPublicAuthWit({ caller: accounts[1].address, action }, true).send().wait(); - - await expect(action.simulate()).rejects.toThrow(/unauthorized/); - }); - - it('on behalf of other (without approval)', async () => { - const balance = await asset.methods.balance_of_public(accounts[0].address).simulate(); - const amount = balance / 2n; - const nonce = Fr.random(); - expect(amount).toBeGreaterThan(0n); - - await expect( - asset.withWallet(wallets[1]).methods.shield(accounts[0].address, amount, secretHash, nonce).simulate(), - ).rejects.toThrow(/unauthorized/); - }); - }); -}); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts b/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts index 6ede484f6d2..9d1d76123bc 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts @@ -1,14 +1,5 @@ import { getSchnorrAccount } from '@aztec/accounts/schnorr'; -import { - type AccountWallet, - type CompleteAddress, - type DebugLogger, - ExtendedNote, - Fr, - Note, - type TxHash, - createDebugLogger, -} from '@aztec/aztec.js'; +import { type AccountWallet, type CompleteAddress, type DebugLogger, createDebugLogger } from '@aztec/aztec.js'; import { DocsExampleContract, TokenContract } from '@aztec/noir-contracts.js'; import { jest } from '@jest/globals'; @@ -123,19 +114,6 @@ export class TokenContractTest { await this.snapshotManager.teardown(); } - async addPendingShieldNoteToPXE(accountIndex: number, amount: bigint, secretHash: Fr, txHash: TxHash) { - const note = new Note([new Fr(amount), secretHash]); - const extendedNote = new ExtendedNote( - note, - this.accounts[accountIndex].address, - this.asset.address, - TokenContract.storage.pending_shields.slot, - TokenContract.notes.TransparentNote.id, - txHash, - ); - await this.wallets[accountIndex].addNote(extendedNote); - } - async applyMintSnapshot() { await this.snapshotManager.snapshot( 'mint', @@ -164,8 +142,7 @@ export class TokenContractTest { this.logger.verbose(`Public balance of wallet 0: ${publicBalance}`); expect(publicBalance).toEqual(this.tokenSim.balanceOfPublic(address)); - tokenSim.mintPrivate(amount); - tokenSim.redeemShield(address, amount); + tokenSim.mintPrivate(address, amount); const privateBalance = await asset.methods.balance_of_private(address).simulate(); this.logger.verbose(`Private balance of wallet 0: ${privateBalance}`); expect(privateBalance).toEqual(tokenSim.balanceOfPrivate(address)); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_private.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_private.test.ts new file mode 100644 index 00000000000..c0f6c1368b2 --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_private.test.ts @@ -0,0 +1,59 @@ +import { U128_UNDERFLOW_ERROR } from '../fixtures/fixtures.js'; +import { TokenContractTest } from './token_contract_test.js'; + +describe('e2e_token_contract transfer_to_private', () => { + const t = new TokenContractTest('transfer_to_private'); + let { asset, accounts, tokenSim } = t; + + beforeAll(async () => { + await t.applyBaseSnapshots(); + await t.applyMintSnapshot(); + await t.setup(); + // Have to destructure again to ensure we have latest refs. + ({ asset, accounts, tokenSim } = t); + }); + + afterAll(async () => { + await t.teardown(); + }); + + afterEach(async () => { + await t.tokenSim.check(); + }); + + it('to self', async () => { + const balancePub = await asset.methods.balance_of_public(accounts[0].address).simulate(); + const amount = balancePub / 2n; + expect(amount).toBeGreaterThan(0n); + + await asset.methods.transfer_to_private(accounts[0].address, amount).send().wait(); + + // Check that the result matches token sim + tokenSim.transferToPrivate(accounts[0].address, accounts[0].address, amount); + await tokenSim.check(); + }); + + it('to someone else', async () => { + const balancePub = await asset.methods.balance_of_public(accounts[0].address).simulate(); + const amount = balancePub / 2n; + expect(amount).toBeGreaterThan(0n); + + await asset.methods.transfer_to_private(accounts[1].address, amount).send().wait(); + + // Check that the result matches token sim + tokenSim.transferToPrivate(accounts[0].address, accounts[1].address, amount); + await tokenSim.check(); + }); + + describe('failure cases', () => { + it('to self (more than balance)', async () => { + const balancePub = await asset.methods.balance_of_public(accounts[0].address).simulate(); + const amount = balancePub + 1n; + expect(amount).toBeGreaterThan(0n); + + await expect(asset.methods.transfer_to_private(accounts[0].address, amount).simulate()).rejects.toThrow( + U128_UNDERFLOW_ERROR, + ); + }); + }); +}); diff --git a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts index 1fea3491017..ffe17819f62 100644 --- a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts +++ b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts @@ -121,7 +121,10 @@ describe('guides/dapp/testing', () => { it('checks public storage', async () => { // docs:start:public-storage await token.methods.mint_public(owner.getAddress(), 100n).send().wait(); - const ownerPublicBalanceSlot = cheats.aztec.computeSlotInMap(6n, owner.getAddress()); + const ownerPublicBalanceSlot = cheats.aztec.computeSlotInMap( + TokenContract.storage.public_balances.slot, + owner.getAddress(), + ); const balance = await pxe.getPublicStorageAt(token.address, ownerPublicBalanceSlot); expect(balance.value).toEqual(100n); // docs:end:public-storage @@ -179,7 +182,10 @@ describe('guides/dapp/testing', () => { const call = token.methods.transfer_public(owner.getAddress(), recipient.getAddress(), 1000n, 0); const receipt = await call.send({ skipPublicSimulation: true }).wait({ dontThrowOnRevert: true }); expect(receipt.status).toEqual(TxStatus.APP_LOGIC_REVERTED); - const ownerPublicBalanceSlot = cheats.aztec.computeSlotInMap(6n, owner.getAddress()); + const ownerPublicBalanceSlot = cheats.aztec.computeSlotInMap( + TokenContract.storage.public_balances.slot, + owner.getAddress(), + ); const balance = await pxe.getPublicStorageAt(token.address, ownerPublicBalanceSlot); expect(balance.value).toEqual(100n); // docs:end:pub-reverted diff --git a/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts b/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts index b561aa03488..687fe8148c7 100644 --- a/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts +++ b/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts @@ -4,17 +4,14 @@ import { type AztecNode, type DebugLogger, EthAddress, - ExtendedNote, type FieldsOf, Fr, type L1TokenManager, L1TokenPortalManager, type L2AmountClaim, type L2AmountClaimWithRecipient, - Note, type PXE, type SiblingPath, - type TxHash, type TxReceipt, type Wallet, deployL1Contract, @@ -343,20 +340,6 @@ export class CrossChainTestHarness { await this.l2Token.methods.transfer_to_private(this.ownerAddress, shieldAmount).send().wait(); } - async addPendingShieldNoteToPXE(shieldAmount: bigint, secretHash: Fr, txHash: TxHash) { - this.logger.info('Adding note to PXE'); - const note = new Note([new Fr(shieldAmount), secretHash]); - const extendedNote = new ExtendedNote( - note, - this.ownerAddress, - this.l2Token.address, - TokenContract.storage.pending_shields.slot, - TokenContract.notes.TransparentNote.id, - txHash, - ); - await this.ownerWallet.addNote(extendedNote); - } - async transferToPublicOnL2(amount: bigint, nonce = Fr.ZERO) { this.logger.info('Transferring tokens to public'); await this.l2Token.methods.transfer_to_public(this.ownerAddress, this.ownerAddress, amount, nonce).send().wait(); diff --git a/yarn-project/end-to-end/src/simulators/lending_simulator.ts b/yarn-project/end-to-end/src/simulators/lending_simulator.ts index 288cac32eaf..d09741b637e 100644 --- a/yarn-project/end-to-end/src/simulators/lending_simulator.ts +++ b/yarn-project/end-to-end/src/simulators/lending_simulator.ts @@ -166,7 +166,7 @@ export class LendingSimulator { mintStableCoinOutsideLoan(recipient: AztecAddress, amount: bigint, priv = false) { if (priv) { - this.stableCoin.mintPrivate(amount); + this.stableCoin.mintPrivate(recipient, amount); } else { this.stableCoin.mintPublic(recipient, amount); } diff --git a/yarn-project/end-to-end/src/simulators/token_simulator.ts b/yarn-project/end-to-end/src/simulators/token_simulator.ts index 03caf19ab16..53a4c5d6e2c 100644 --- a/yarn-project/end-to-end/src/simulators/token_simulator.ts +++ b/yarn-project/end-to-end/src/simulators/token_simulator.ts @@ -26,8 +26,9 @@ export class TokenSimulator { this.lookupProvider.set(account.toString(), wallet); } - public mintPrivate(amount: bigint) { + public mintPrivate(to: AztecAddress, amount: bigint) { this.totalSupply += amount; + this.balancesPrivate.set(to.toString(), (this.balancesPrivate.get(to.toString()) || 0n) + amount); } public mintPublic(to: AztecAddress, amount: bigint) { @@ -54,13 +55,10 @@ export class TokenSimulator { this.balancesPrivate.set(to.toString(), toBalance + amount); } - public shield(from: AztecAddress, amount: bigint) { + public transferToPrivate(from: AztecAddress, to: AztecAddress, amount: bigint) { const fromBalance = this.balancePublic.get(from.toString()) || 0n; expect(fromBalance).toBeGreaterThanOrEqual(amount); this.balancePublic.set(from.toString(), fromBalance - amount); - } - - public redeemShield(to: AztecAddress, amount: bigint) { const toBalance = this.balancesPrivate.get(to.toString()) || 0n; this.balancesPrivate.set(to.toString(), toBalance + amount); }