diff --git a/docs/docs/migration_notes.md b/docs/docs/migration_notes.md index f46468a31a1f..ed282364f1c2 100644 --- a/docs/docs/migration_notes.md +++ b/docs/docs/migration_notes.md @@ -12,6 +12,26 @@ Aztec is in full-speed development. Literally every version breaks compatibility The `limit` parameter in `NoteGetterOptions` and `NoteViewerOptions` is now required to be a compile-time constant. This allows performing loops over this value, which leads to reduced circuit gate counts when setting a `limit` value. +### [Aztec.nr] canonical public authwit registry + +The introduction of an auth registry where all the authwit responses sits were made to make the job easier for sequences in relation to avoid being dossed. Essentially, with the registry, it is not required to execute code on the account contract itself in most public cases! + +Notable, this means that consuming a public authwit will no longer emit a nullifier in the account contract but instead update STORAGE in the public domain. This means that there is a larger difference between private and public again. However, it also means that if contracts need to approve, and use the approval in the same tx, it is transient and don't need to go to DA (saving 96 bytes). + +For the typescript wallets this is handled so the API's don't change, but account contracts should get rid of their current setup with `approved_actions`. + +```diff +- let actions = AccountActions::init(&mut context, ACCOUNT_ACTIONS_STORAGE_SLOT, is_valid_impl); ++ let actions = AccountActions::init(&mut context, is_valid_impl); +``` + +For contracts we have added a `set_authorized` function in the auth library that can be used to set values in the registry. + +```diff +- storage.approved_action.at(message_hash).write(true); ++ set_authorized(&mut context, message_hash, true); +``` + ### [Aztec.nr] emit encrypted logs Emitting or broadcasting encrypted notes are no longer done as part of the note creation, but must explicitly be either emitted or discarded instead. diff --git a/noir-projects/aztec-nr/authwit/src/account.nr b/noir-projects/aztec-nr/authwit/src/account.nr index 9a5504e0117a..c00592520a3d 100644 --- a/noir-projects/aztec-nr/authwit/src/account.nr +++ b/noir-projects/aztec-nr/authwit/src/account.nr @@ -1,34 +1,17 @@ use dep::aztec::context::{PrivateContext, PublicContext}; -use dep::aztec::state_vars::{Map, PublicMutable}; use dep::aztec::protocol_types::{address::AztecAddress, abis::function_selector::FunctionSelector, hash::pedersen_hash}; use crate::entrypoint::{app::AppPayload, fee::FeePayload}; use crate::auth::{IS_VALID_SELECTOR, compute_outer_authwit_hash}; -// @todo Remove the `approved_action` struct AccountActions { context: Context, is_valid_impl: fn(&mut PrivateContext, Field) -> bool, - approved_action: Map, Context>, } impl AccountActions { - pub fn init( - context: Context, - approved_action_storage_slot: Field, - is_valid_impl: fn(&mut PrivateContext, Field) -> bool - ) -> Self { - AccountActions { - context, - is_valid_impl, - approved_action: Map::new( - context, - approved_action_storage_slot, - |context, slot| { - PublicMutable::new(context, slot) - } - ) - } + pub fn init(context: Context, is_valid_impl: fn(&mut PrivateContext, Field) -> bool) -> Self { + AccountActions { context, is_valid_impl } } } @@ -66,31 +49,3 @@ impl AccountActions<&mut PrivateContext> { } // docs:end:spend_private_authwit } - - -// @todo We can entirely remove this one when the other is working. -impl AccountActions<&mut PublicContext> { - // docs:start:spend_public_authwit - pub fn spend_public_authwit(self, inner_hash: Field) -> Field { - // The `inner_hash` is "siloed" with the `msg_sender` to ensure that only it can - // consume the message. - // This ensures that contracts cannot consume messages that are not intended for them. - let message_hash = compute_outer_authwit_hash( - self.context.msg_sender(), - self.context.chain_id(), - self.context.version(), - inner_hash - ); - let is_valid = self.approved_action.at(message_hash).read(); - assert(is_valid == true, "Message not authorized by account"); - self.context.push_new_nullifier(message_hash, 0); - IS_VALID_SELECTOR - } - // docs:end:spend_public_authwit - - // docs:start:approve_public_authwit - pub fn approve_public_authwit(self, message_hash: Field) { - self.approved_action.at(message_hash).write(true); - } - // docs:end:approve_public_authwit -} diff --git a/noir-projects/aztec-nr/authwit/src/auth.nr b/noir-projects/aztec-nr/authwit/src/auth.nr index 68892872d87f..b24bad76c171 100644 --- a/noir-projects/aztec-nr/authwit/src/auth.nr +++ b/noir-projects/aztec-nr/authwit/src/auth.nr @@ -70,3 +70,32 @@ pub fn compute_outer_authwit_hash( GENERATOR_INDEX__AUTHWIT_OUTER ) } + +/** + * Helper function to set the authorization status of a message hash + * + * @param message_hash The hash of the message to authorize + * @param authorize True if the message should be authorized, false if it should be revoked + */ +pub fn set_authorized(context: &mut PublicContext, message_hash: Field, authorize: bool) { + context.call_public_function( + AztecAddress::from_field(CANONICAL_AUTH_REGISTRY_ADDRESS), + FunctionSelector::from_signature("set_authorized(Field,bool)"), + [message_hash, authorize as Field].as_slice(), + GasOpts::default() + ).assert_empty(); +} + +/** + * Helper function to reject all authwits + * + * @param reject True if all authwits should be rejected, false otherwise + */ +pub fn set_reject_all(context: &mut PublicContext, reject: bool) { + context.call_public_function( + AztecAddress::from_field(CANONICAL_AUTH_REGISTRY_ADDRESS), + FunctionSelector::from_signature("set_reject_all(bool)"), + [context.this_address().to_field(), reject as Field].as_slice(), + GasOpts::default() + ).assert_empty(); +} diff --git a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr index 6bfaa81c58f7..df46548453e0 100644 --- a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr @@ -12,7 +12,7 @@ contract AppSubscription { encrypted_logs::encrypted_note_emission::encode_and_encrypt, protocol_types::{traits::is_empty, grumpkin_point::GrumpkinPoint} }, - authwit::{account::AccountActions, auth_witness::get_auth_witness, auth::assert_current_call_valid_authwit}, + authwit::{auth_witness::get_auth_witness, auth::assert_current_call_valid_authwit}, gas_token::GasToken, token::Token }; diff --git a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr index bcf0cc8906cb..0bb6c9c00764 100644 --- a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr @@ -21,8 +21,6 @@ contract EcdsaAccount { public_key: PrivateImmutable, } - global ACCOUNT_ACTIONS_STORAGE_SLOT = 2; - // Creates a new account out of an ECDSA public key to use for signature verification #[aztec(private)] #[aztec(initializer)] @@ -41,14 +39,14 @@ contract EcdsaAccount { // Note: If you globally change the entrypoint signature don't forget to update default_entrypoint.ts #[aztec(private)] fn entrypoint(app_payload: AppPayload, fee_payload: FeePayload) { - let actions = AccountActions::init(&mut context, ACCOUNT_ACTIONS_STORAGE_SLOT, is_valid_impl); + let actions = AccountActions::init(&mut context, is_valid_impl); actions.entrypoint(app_payload, fee_payload); } #[aztec(private)] #[aztec(noinitcheck)] fn spend_private_authwit(inner_hash: Field) -> Field { - let actions = AccountActions::init(&mut context, ACCOUNT_ACTIONS_STORAGE_SLOT, is_valid_impl); + let actions = AccountActions::init(&mut context, is_valid_impl); actions.spend_private_authwit(inner_hash) } diff --git a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr index aaee6cb29ea0..d87fa10382b8 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr @@ -6,7 +6,6 @@ contract SchnorrAccount { use dep::std; use dep::aztec::prelude::{AztecAddress, FunctionSelector, NoteHeader, PrivateContext, PrivateImmutable}; - use dep::aztec::state_vars::{Map, PublicMutable}; use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt; use dep::authwit::{ entrypoint::{app::AppPayload, fee::FeePayload}, account::AccountActions, @@ -22,7 +21,6 @@ contract SchnorrAccount { // docs:start:storage signing_public_key: PrivateImmutable, // docs:end:storage - approved_actions: Map>, } // Constructs the contract @@ -46,22 +44,14 @@ contract SchnorrAccount { #[aztec(private)] #[aztec(noinitcheck)] fn entrypoint(app_payload: AppPayload, fee_payload: FeePayload) { - let actions = AccountActions::init( - &mut context, - storage.approved_actions.storage_slot, - is_valid_impl - ); + let actions = AccountActions::init(&mut context, is_valid_impl); actions.entrypoint(app_payload, fee_payload); } #[aztec(private)] #[aztec(noinitcheck)] fn spend_private_authwit(inner_hash: Field) -> Field { - let actions = AccountActions::init( - &mut context, - storage.approved_actions.storage_slot, - is_valid_impl - ); + let actions = AccountActions::init(&mut context, is_valid_impl); actions.spend_private_authwit(inner_hash) } diff --git a/noir-projects/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr index 32f28a5b207a..3441779536bd 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr @@ -12,18 +12,16 @@ contract SchnorrHardcodedAccount { global public_key_x: Field = 0x0ede3d33c920df8fdf43f3e39ed38b0882c25b056620ef52fd016fe811aa2443; global public_key_y: Field = 0x29155934ffaa105323695b5f91faadd84acc21f4a8bda2fad760f992d692bc7f; - global ACCOUNT_ACTIONS_STORAGE_SLOT = 1; - // Note: If you globally change the entrypoint signature don't forget to update default_entrypoint.ts #[aztec(private)] fn entrypoint(app_payload: AppPayload, fee_payload: FeePayload) { - let actions = AccountActions::init(&mut context, ACCOUNT_ACTIONS_STORAGE_SLOT, is_valid_impl); + let actions = AccountActions::init(&mut context, is_valid_impl); actions.entrypoint(app_payload, fee_payload); } #[aztec(private)] fn spend_private_authwit(inner_hash: Field) -> Field { - let actions = AccountActions::init(&mut context, ACCOUNT_ACTIONS_STORAGE_SLOT, is_valid_impl); + let actions = AccountActions::init(&mut context, is_valid_impl); actions.spend_private_authwit(inner_hash) } diff --git a/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/main.nr index 45f4b779ad09..ca795fca2526 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/main.nr @@ -8,18 +8,16 @@ contract SchnorrSingleKeyAccount { use crate::{util::recover_address, auth_oracle::get_auth_witness}; - global ACCOUNT_ACTIONS_STORAGE_SLOT = 1; - // Note: If you globally change the entrypoint signature don't forget to update default_entrypoint.ts #[aztec(private)] fn entrypoint(app_payload: AppPayload, fee_payload: FeePayload) { - let actions = AccountActions::init(&mut context, ACCOUNT_ACTIONS_STORAGE_SLOT, is_valid_impl); + let actions = AccountActions::init(&mut context, is_valid_impl); actions.entrypoint(app_payload, fee_payload); } #[aztec(private)] fn spend_private_authwit(inner_hash: Field) -> Field { - let actions = AccountActions::init(&mut context, ACCOUNT_ACTIONS_STORAGE_SLOT, is_valid_impl); + let actions = AccountActions::init(&mut context, is_valid_impl); actions.spend_private_authwit(inner_hash) } diff --git a/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr b/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr index 0ccd4b5a74b4..e844edd3d87e 100644 --- a/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/uniswap_contract/src/main.nr @@ -10,7 +10,7 @@ contract Uniswap { use dep::authwit::auth::{ IS_VALID_SELECTOR, assert_current_call_valid_authwit_public, compute_call_authwit_hash, - compute_outer_authwit_hash + compute_outer_authwit_hash, set_authorized }; use dep::token::Token; @@ -19,11 +19,6 @@ contract Uniswap { #[aztec(storage)] struct Storage { - // like with account contracts, stores the approval message on a slot and tracks if they are active - approved_action: Map>, - // tracks the nonce used to create the approval message for burning funds - // gets incremented each time after use to prevent replay attacks - nonce_for_burn_approval: PublicMutable, portal_address: SharedImmutable, } // docs:end:uniswap_setup @@ -153,28 +148,6 @@ contract Uniswap { } // docs:end:swap_private - // docs:start:authwit_uniswap_get - // Since the token bridge burns funds on behalf of this contract, this contract has to tell the token contract if the signature is valid - // implementation is similar to how account contracts validate public approvals. - // if valid, it returns the IS_VALID selector which is expected by token contract - #[aztec(public)] - fn spend_public_authwit(inner_hash: Field) -> Field { - let message_hash = compute_outer_authwit_hash( - context.msg_sender(), - context.chain_id(), - context.version(), - inner_hash - ); - let value = storage.approved_action.at(message_hash).read(); - if (value) { - context.push_new_nullifier(message_hash, 0); - IS_VALID_SELECTOR - } else { - 0 - } - } - // docs:end:authwit_uniswap_get - // docs:start:authwit_uniswap_set // This helper method approves the bridge to burn this contract's funds and exits the input asset to L1 // Assumes contract already has funds. @@ -188,8 +161,10 @@ contract Uniswap { token_bridge: AztecAddress, amount: Field ) { - // approve bridge to burn this contract's funds (required when exiting on L1, as it burns funds on L2): - let nonce_for_burn_approval = storage.nonce_for_burn_approval.read(); + // Since we will authorize and instantly spend the funds, all in public, we can use the same nonce + // every interaction. In practice, the authwit should be squashed, so this is also cheap! + let nonce = 0xdeadbeef; + let selector = FunctionSelector::from_signature("burn_public((Field),Field,Field)"); let message_hash = compute_call_authwit_hash( token_bridge, @@ -197,21 +172,15 @@ contract Uniswap { context.chain_id(), context.version(), selector, - [context.this_address().to_field(), amount, nonce_for_burn_approval] + [context.this_address().to_field(), amount, nonce] ); - storage.approved_action.at(message_hash).write(true); - // increment nonce_for_burn_approval so it won't be used again - storage.nonce_for_burn_approval.write(nonce_for_burn_approval + 1); + // We need to make a call to update it. + set_authorized(&mut context, message_hash, true); let this_portal_address = storage.portal_address.read_public(); // Exit to L1 Uniswap Portal ! - TokenBridge::at(token_bridge).exit_to_l1_public( - this_portal_address, - amount, - this_portal_address, - nonce_for_burn_approval - ).call(&mut context) + TokenBridge::at(token_bridge).exit_to_l1_public(this_portal_address, amount, this_portal_address, nonce).call(&mut context) } // docs:end:authwit_uniswap_set @@ -224,12 +193,5 @@ contract Uniswap { token.eq(TokenBridge::at(token_bridge).get_token().view(&mut context)), "input_asset address is not the same as seen in the bridge contract" ); } - - // /// Unconstrained /// - - // this method exists solely for e2e tests to test that nonce gets incremented each time. - unconstrained fn nonce_for_burn_approval() -> pub Field { - storage.nonce_for_burn_approval.read() - } // docs:end:assert_token_is_same } diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/burn.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/burn.test.ts index 05ca22f844fa..b20e61968a44 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/burn.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/burn.test.ts @@ -48,9 +48,9 @@ describe('e2e_blacklist_token_contract burn', () => { tokenSim.burnPublic(wallets[0].getAddress(), amount); - // Check that the message hash is no longer valid. Need to try to send since nullifiers are handled by sequencer. - const txReplay = asset.withWallet(wallets[1]).methods.burn_public(wallets[0].getAddress(), amount, nonce).send(); - await expect(txReplay.wait()).rejects.toThrow(DUPLICATE_NULLIFIER_ERROR); + await expect( + asset.withWallet(wallets[1]).methods.burn_public(wallets[0].getAddress(), amount, nonce).simulate(), + ).rejects.toThrow(/unauthorized/); }); describe('failure cases', () => { @@ -68,7 +68,7 @@ describe('e2e_blacklist_token_contract burn', () => { const amount = balance0 - 1n; expect(amount).toBeGreaterThan(0n); const nonce = 1; - await expect(asset.methods.burn_public(wallets[0].getAddress(), amount, nonce).prove()).rejects.toThrow( + await expect(asset.methods.burn_public(wallets[0].getAddress(), amount, nonce).simulate()).rejects.toThrow( 'Assertion failed: invalid nonce', ); }); @@ -78,8 +78,8 @@ describe('e2e_blacklist_token_contract burn', () => { const amount = balance0 + 1n; const nonce = Fr.random(); await expect( - asset.withWallet(wallets[1]).methods.burn_public(wallets[0].getAddress(), amount, nonce).prove(), - ).rejects.toThrow('Assertion failed: Message not authorized by account'); + asset.withWallet(wallets[1]).methods.burn_public(wallets[0].getAddress(), amount, nonce).simulate(), + ).rejects.toThrow(/unauthorized/); }); it('burn more than balance on behalf of other', async () => { @@ -106,8 +106,8 @@ describe('e2e_blacklist_token_contract burn', () => { await wallets[0].setPublicAuthWit({ caller: wallets[0].getAddress(), action }, true).send().wait(); await expect( - asset.withWallet(wallets[1]).methods.burn_public(wallets[0].getAddress(), amount, nonce).prove(), - ).rejects.toThrow('Assertion failed: Message not authorized by account'); + asset.withWallet(wallets[1]).methods.burn_public(wallets[0].getAddress(), amount, nonce).simulate(), + ).rejects.toThrow(/unauthorized/); }); it('burn from blacklisted account', async () => { 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 22701ac0cbf7..cfec5b15b1cc 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 @@ -63,11 +63,9 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { await t.tokenSim.check(); // Check that replaying the shield should fail! - const txReplay = asset - .withWallet(wallets[1]) - .methods.shield(wallets[0].getAddress(), amount, secretHash, nonce) - .send(); - await expect(txReplay.wait()).rejects.toThrow(DUPLICATE_NULLIFIER_ERROR); + await expect( + asset.withWallet(wallets[1]).methods.shield(wallets[0].getAddress(), amount, secretHash, nonce).simulate(), + ).rejects.toThrow(/unauthorized/); // Redeem it await t.addPendingShieldNoteToPXE(0, amount, secretHash, receipt.txHash); @@ -120,7 +118,7 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { const action = asset.withWallet(wallets[2]).methods.shield(wallets[0].getAddress(), amount, secretHash, nonce); await wallets[0].setPublicAuthWit({ caller: wallets[1].getAddress(), action }, true).send().wait(); - await expect(action.prove()).rejects.toThrow('Assertion failed: Message not authorized by account'); + await expect(action.prove()).rejects.toThrow(/unauthorized/); }); it('on behalf of other (without approval)', async () => { @@ -130,8 +128,8 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { expect(amount).toBeGreaterThan(0n); await expect( - asset.withWallet(wallets[1]).methods.shield(wallets[0].getAddress(), amount, secretHash, nonce).prove(), - ).rejects.toThrow(`Assertion failed: Message not authorized by account`); + asset.withWallet(wallets[1]).methods.shield(wallets[0].getAddress(), amount, secretHash, nonce).simulate(), + ).rejects.toThrow(/unauthorized/); }); it('shielding from blacklisted account', async () => { diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_public.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_public.test.ts index 0b9fd8463764..6b7413d7ce54 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_public.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_public.test.ts @@ -1,6 +1,6 @@ import { Fr } from '@aztec/aztec.js'; -import { DUPLICATE_NULLIFIER_ERROR, U128_UNDERFLOW_ERROR } from '../fixtures/index.js'; +import { U128_UNDERFLOW_ERROR } from '../fixtures/index.js'; import { BlacklistTokenContractTest } from './blacklist_token_contract_test.js'; describe('e2e_blacklist_token_contract transfer public', () => { @@ -61,12 +61,12 @@ describe('e2e_blacklist_token_contract transfer public', () => { tokenSim.transferPublic(wallets[0].getAddress(), wallets[1].getAddress(), amount); - // Check that the message hash is no longer valid. Need to try to send since nullifiers are handled by sequencer. - const txReplay = asset - .withWallet(wallets[1]) - .methods.transfer_public(wallets[0].getAddress(), wallets[1].getAddress(), amount, nonce) - .send(); - await expect(txReplay.wait()).rejects.toThrow(DUPLICATE_NULLIFIER_ERROR); + await expect( + asset + .withWallet(wallets[1]) + .methods.transfer_public(wallets[0].getAddress(), wallets[1].getAddress(), amount, nonce) + .simulate(), + ).rejects.toThrow(/unauthorized/); }); describe('failure cases', () => { @@ -96,8 +96,8 @@ describe('e2e_blacklist_token_contract transfer public', () => { asset .withWallet(wallets[1]) .methods.transfer_public(wallets[0].getAddress(), wallets[1].getAddress(), amount, nonce) - .prove(), - ).rejects.toThrow('Assertion failed: Message not authorized by account'); + .simulate(), + ).rejects.toThrow(/unauthorized/); }); it('transfer more than balance on behalf of other', async () => { @@ -137,26 +137,7 @@ describe('e2e_blacklist_token_contract transfer public', () => { await wallets[0].setPublicAuthWit({ caller: wallets[0].getAddress(), action }, true).send().wait(); // Perform the transfer - await expect(action.prove()).rejects.toThrow('Assertion failed: Message not authorized by account'); - - expect(await asset.methods.balance_of_public(wallets[0].getAddress()).simulate()).toEqual(balance0); - expect(await asset.methods.balance_of_public(wallets[1].getAddress()).simulate()).toEqual(balance1); - }); - - it('transfer on behalf of other, wrong designated caller', async () => { - const balance0 = await asset.methods.balance_of_public(wallets[0].getAddress()).simulate(); - const balance1 = await asset.methods.balance_of_public(wallets[1].getAddress()).simulate(); - const amount = balance0 + 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.transfer_public(wallets[0].getAddress(), wallets[1].getAddress(), amount, nonce); - await wallets[0].setPublicAuthWit({ caller: wallets[0].getAddress(), action }, true).send().wait(); - // Perform the transfer - await expect(action.prove()).rejects.toThrow('Assertion failed: Message not authorized by account'); + await expect(action.simulate()).rejects.toThrow(/unauthorized/); expect(await asset.methods.balance_of_public(wallets[0].getAddress()).simulate()).toEqual(balance0); expect(await asset.methods.balance_of_public(wallets[1].getAddress()).simulate()).toEqual(balance1); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/burn.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/burn.test.ts index bfe3406329c6..f736b47a64c0 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/burn.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/burn.test.ts @@ -47,9 +47,9 @@ describe('e2e_token_contract burn', () => { tokenSim.burnPublic(accounts[0].address, amount); - // Check that the message hash is no longer valid. Need to try to send since nullifiers are handled by sequencer. - const txReplay = asset.withWallet(wallets[1]).methods.burn_public(accounts[0].address, amount, nonce).send(); - await expect(txReplay.wait()).rejects.toThrow(DUPLICATE_NULLIFIER_ERROR); + await expect( + asset.withWallet(wallets[1]).methods.burn_public(accounts[0].address, amount, nonce).simulate(), + ).rejects.toThrow(/unauthorized/); }); describe('failure cases', () => { @@ -78,7 +78,7 @@ describe('e2e_token_contract burn', () => { const nonce = Fr.random(); await expect( asset.withWallet(wallets[1]).methods.burn_public(accounts[0].address, amount, nonce).simulate(), - ).rejects.toThrow('Assertion failed: Message not authorized by account'); + ).rejects.toThrow(/unauthorized/); }); it('burn more than balance on behalf of other', async () => { @@ -106,7 +106,7 @@ describe('e2e_token_contract burn', () => { await expect( asset.withWallet(wallets[1]).methods.burn_public(accounts[0].address, amount, nonce).simulate(), - ).rejects.toThrow('Assertion failed: Message not authorized by account'); + ).rejects.toThrow(/unauthorized/); }); }); }); 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 index 93ab4e448708..d7a576e09b59 100644 --- 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 @@ -1,6 +1,6 @@ import { Fr, computeSecretHash } from '@aztec/aztec.js'; -import { DUPLICATE_NULLIFIER_ERROR, U128_UNDERFLOW_ERROR } from '../fixtures/fixtures.js'; +import { U128_UNDERFLOW_ERROR } from '../fixtures/fixtures.js'; import { TokenContractTest } from './token_contract_test.js'; describe('e2e_token_contract shield + redeem shield', () => { @@ -59,8 +59,9 @@ describe('e2e_token_contract shield + redeem shield', () => { await tokenSim.check(); // Check that replaying the shield should fail! - const txReplay = asset.withWallet(wallets[1]).methods.shield(accounts[0].address, amount, secretHash, nonce).send(); - await expect(txReplay.wait()).rejects.toThrow(DUPLICATE_NULLIFIER_ERROR); + 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); @@ -113,7 +114,7 @@ describe('e2e_token_contract shield + redeem shield', () => { 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('Assertion failed: Message not authorized by account'); + await expect(action.simulate()).rejects.toThrow(/unauthorized/); }); it('on behalf of other (without approval)', async () => { @@ -124,7 +125,7 @@ describe('e2e_token_contract shield + redeem shield', () => { await expect( asset.withWallet(wallets[1]).methods.shield(accounts[0].address, amount, secretHash, nonce).simulate(), - ).rejects.toThrow(`Assertion failed: Message not authorized by account`); + ).rejects.toThrow(/unauthorized/); }); }); }); diff --git a/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts b/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts index b56ed6e5ae5b..f62fa9bc3c56 100644 --- a/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts +++ b/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts @@ -206,10 +206,6 @@ export const uniswapL1L2TestSuite = ( const wethL2BalanceBeforeSwap = await wethCrossChainHarness.getL2PrivateBalanceOf(ownerAddress); const daiL2BalanceBeforeSwap = await daiCrossChainHarness.getL2PrivateBalanceOf(ownerAddress); - // before swap - check nonce_for_burn_approval stored on uniswap - // (which is used by uniswap to approve the bridge to burn funds on its behalf to exit to L1) - const nonceForBurnApprovalBeforeSwap = await uniswapL2Contract.methods.nonce_for_burn_approval().simulate(); - // 3. Owner gives uniswap approval to unshield funds to self on its behalf logger.info('Approving uniswap to unshield funds to self on my behalf'); const nonceForWETHUnshieldApproval = new Fr(1n); @@ -289,9 +285,6 @@ export const uniswapL1L2TestSuite = ( await wethCrossChainHarness.expectPrivateBalanceOnL2(ownerAddress, wethL2BalanceBeforeSwap - wethAmountToBridge); // ensure that uniswap contract didn't eat the funds. await wethCrossChainHarness.expectPublicBalanceOnL2(uniswapL2Contract.address, 0n); - // check burn approval nonce incremented: - const nonceForBurnApprovalAfterSwap = await uniswapL2Contract.methods.nonce_for_burn_approval().simulate(); - expect(nonceForBurnApprovalAfterSwap).toBe(nonceForBurnApprovalBeforeSwap + 1n); // 5. Consume L2 to L1 message by calling uniswapPortal.swap_private() logger.info('Execute withdraw and swap on the uniswapPortal!'); @@ -442,10 +435,6 @@ export const uniswapL1L2TestSuite = ( ); await ownerWallet.setPublicAuthWit(transferMessageHash, true).send().wait(); - // before swap - check nonce_for_burn_approval stored on uniswap - // (which is used by uniswap to approve the bridge to burn funds on its behalf to exit to L1) - const nonceForBurnApprovalBeforeSwap = await uniswapL2Contract.methods.nonce_for_burn_approval().simulate(); - // 4. Swap on L1 - sends L2 to L1 message to withdraw WETH to L1 and another message to swap assets. const [secretForDepositingSwappedDai, secretHashForDepositingSwappedDai] = daiCrossChainHarness.generateClaimSecret(); @@ -521,10 +510,6 @@ export const uniswapL1L2TestSuite = ( // check weth balance of owner on L2 (we first bridged `wethAmountToBridge` into L2 and now withdrew it!) await wethCrossChainHarness.expectPublicBalanceOnL2(ownerAddress, wethL2BalanceBeforeSwap - wethAmountToBridge); - // check burn approval nonce incremented: - const nonceForBurnApprovalAfterSwap = await uniswapL2Contract.methods.nonce_for_burn_approval().simulate(); - expect(nonceForBurnApprovalAfterSwap).toBe(nonceForBurnApprovalBeforeSwap + 1n); - // 5. Perform the swap on L1 with the `uniswapPortal.swap_private()` (consuming L2 to L1 messages) logger.info('Execute withdraw and swap on the uniswapPortal!'); const daiL1BalanceOfPortalBeforeSwap = await daiCrossChainHarness.getL1BalanceOf( @@ -773,10 +758,7 @@ export const uniswapL1L2TestSuite = ( ); await ownerWallet.setPublicAuthWit(swapMessageHash, true).send().wait(); - // Swap! - await expect(action.prove()).rejects.toThrow( - "Assertion failed: Message not authorized by account 'is_valid == true'", - ); + await expect(action.simulate()).rejects.toThrow(/unauthorized/); }); it("uniswap can't pull funds without transfer approval", async () => { @@ -808,8 +790,8 @@ export const uniswapL1L2TestSuite = ( ownerEthAddress, Fr.ZERO, ) - .prove(), - ).rejects.toThrow(`Assertion failed: Message not authorized by account 'is_valid == true'`); + .simulate(), + ).rejects.toThrow(/unauthorized/); }); // tests when trying to mix private and public flows: