diff --git a/docs/docs/aztec/glossary/call_types.md b/docs/docs/aztec/glossary/call_types.md index efd977d48cb..0e080b38335 100644 --- a/docs/docs/aztec/glossary/call_types.md +++ b/docs/docs/aztec/glossary/call_types.md @@ -178,7 +178,7 @@ This is used to get a result out of an execution, either private or public. It c #include_code public_getter /noir-projects/noir-contracts/contracts/auth_contract/src/main.nr rust -#include_code simulate_public_getter yarn-project/end-to-end/src/e2e_auth_contract.test.ts typescript +#include_code simulate_function yarn-project/end-to-end/src/composed/docs_examples.test.ts typescript :::warning No correctness is guaranteed on the result of `simulate`! Correct execution is entirely optional and left up to the client that handles this request. diff --git a/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/common_patterns/index.md b/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/common_patterns/index.md index fbf1f9e26f8..f99deb83cbd 100644 --- a/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/common_patterns/index.md +++ b/docs/docs/guides/developer_guides/smart_contracts/writing_contracts/common_patterns/index.md @@ -16,7 +16,7 @@ Similarly we have discovered some anti-patterns too (like privacy leakage) that We call this the "authentication witness" pattern or authwit for short. - Approve someone in private domain: - #include_code authwit_to_another_sc /yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts typescript + #include_code authwit_to_another_sc /yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_private.test.ts typescript Here you approve a contract to burn funds on your behalf. diff --git a/docs/docs/tutorials/codealong/contract_tutorials/advanced/token_bridge/4_typescript_glue_code.md b/docs/docs/tutorials/codealong/contract_tutorials/advanced/token_bridge/4_typescript_glue_code.md index 3a349ae6d26..8392aee7049 100644 --- a/docs/docs/tutorials/codealong/contract_tutorials/advanced/token_bridge/4_typescript_glue_code.md +++ b/docs/docs/tutorials/codealong/contract_tutorials/advanced/token_bridge/4_typescript_glue_code.md @@ -141,14 +141,14 @@ This fetches the wallets from the sandbox and deploys our cross chain harness on Paste the private flow test below the setup: -#include_code e2e_private_cross_chain /yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts typescript +#include_code e2e_private_cross_chain /yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_private.test.ts typescript ## Public flow test Paste the public flow below the private flow: ```ts -#include_code e2e_public_cross_chain /yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/deposits.test.ts raw +#include_code e2e_public_cross_chain /yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public.test.ts raw }) ``` @@ -165,7 +165,7 @@ Note - you might have a jest error at the end of each test saying "expected 1-2 ## Next Steps -### Follow a more detailed Aztec.js tutorial +### Follow a more detailed Aztec.js tutorial Follow the tutorial [here](../../../aztecjs-getting-started.md). diff --git a/noir-projects/noir-contracts/contracts/auth_contract/src/main.nr b/noir-projects/noir-contracts/contracts/auth_contract/src/main.nr index 885fbdbd162..a971c8b109b 100644 --- a/noir-projects/noir-contracts/contracts/auth_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/auth_contract/src/main.nr @@ -1,3 +1,5 @@ +mod test; + // Test contract showing basic public access control that can be used in private. It uses a SharedMutable state variable to // publicly store the address of an authorized account that can call private functions. contract Auth { @@ -12,7 +14,7 @@ contract Auth { // Admin can change the value of the authorized address via set_authorized() admin: PublicImmutable, // docs:start:shared_mutable_storage - authorized: SharedMutable, + authorized: SharedMutable, // docs:end:shared_mutable_storage } diff --git a/noir-projects/noir-contracts/contracts/auth_contract/src/test.nr b/noir-projects/noir-contracts/contracts/auth_contract/src/test.nr new file mode 100644 index 00000000000..23d3896f7b7 --- /dev/null +++ b/noir-projects/noir-contracts/contracts/auth_contract/src/test.nr @@ -0,0 +1,2 @@ +mod main; +mod utils; diff --git a/noir-projects/noir-contracts/contracts/auth_contract/src/test/main.nr b/noir-projects/noir-contracts/contracts/auth_contract/src/test/main.nr new file mode 100644 index 00000000000..f0a7775b16c --- /dev/null +++ b/noir-projects/noir-contracts/contracts/auth_contract/src/test/main.nr @@ -0,0 +1,123 @@ +use crate::test::utils; +use crate::Auth; + +use dep::aztec::prelude::AztecAddress; + +global CHANGE_AUTHORIZED_DELAY_BLOCKS = 5; + +// TODO (#8588): These were ported over directly from e2e tests. Refactor these in the correct TXe style. +#[test] +unconstrained fn main() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, auth_contract_address, admin, to_authorize, other) = utils::setup(); + + let authorized_is_unset_initially = || { + env.impersonate(admin); + let authorized = env.call_public(Auth::at(auth_contract_address).get_authorized()); + assert_eq(authorized, AztecAddress::from_field(0)); + }; + authorized_is_unset_initially(); + + let non_admin_cannot_set_unauthorized = || { + env.impersonate(other); + env.assert_public_call_fails(Auth::at(auth_contract_address).set_authorized(to_authorize)); + }; + non_admin_cannot_set_unauthorized(); + + let admin_sets_authorized = || { + env.impersonate(admin); + env.call_public(Auth::at(auth_contract_address).set_authorized(to_authorize)); + env.advance_block_by(1); + + let scheduled_authorized = env.call_public(Auth::at(auth_contract_address).get_scheduled_authorized()); + assert_eq(scheduled_authorized, to_authorize); + }; + admin_sets_authorized(); + + let authorized_is_not_yet_effective = || { + env.impersonate(to_authorize); + let authorized: AztecAddress = env.call_public(Auth::at(auth_contract_address).get_authorized()); + assert_eq(authorized, AztecAddress::zero()); + + env.assert_private_call_fails(Auth::at(auth_contract_address).do_private_authorized_thing()); + }; + authorized_is_not_yet_effective(); + + let authorized_becomes_effective_after_delay = || { + env.impersonate(to_authorize); + + // We advance block by 4, because the delay is 5, and we initially advanced block by one after setting the value. See below comment for explanation. + env.advance_block_by(CHANGE_AUTHORIZED_DELAY_BLOCKS - 1); + let authorized: AztecAddress = env.call_public(Auth::at(auth_contract_address).get_authorized()); + assert_eq(authorized, to_authorize); + + let authorized_in_private: AztecAddress = env.call_private(Auth::at(auth_contract_address).get_authorized_in_private()); + assert_eq(authorized_in_private, AztecAddress::zero()); + + // We need to always advance the block one more time to get the current value in private, compared to the value in public. + // To see why let's see this diagram. + // When we schedule a change in public, lets say we are at block 2 (building a tx to be included in block 2), which means the latest committed block is block 1. + // Thus, the value change will be set to block 7 (2 + 5). + // If we now advance our env by 5 blocks, we will be at block 7 (building a tx to be included in block 7), which means the latest committed block is block 6. + // Reading the value in public will work, because it will use the current block (7), and the current block is the block of change; but + // if we try to create a historical proof, we do not have access to block 7 yet, and have to build the proof off of block 6, but at this time, the value change will not have + // taken place yet, therefore we need to be at block 8 (building a tx to be included in block 8), for the historical proof to work, as it will have access to the full block 7 + // where the value change takes effect. + // Note: We do not see this behavior in the e2e tests because setting the value inplicitly advances the block number by 1. + // 1 2 3 4 5 6 7 8 9 + // | | | | | | | | | + // ^ + // value change scheduled here + // ^ + // get_authorized() (public) called here with block_number = 7 + // ^ + // get_authorized() (private) called here with block_number = 8 + env.advance_block_by(1); + let authorized_in_private_again: AztecAddress = env.call_private(Auth::at(auth_contract_address).get_authorized_in_private()); + assert_eq(authorized_in_private_again, to_authorize); + + env.call_private_void(Auth::at(auth_contract_address).do_private_authorized_thing()); + }; + authorized_becomes_effective_after_delay(); + + let authorize_other = || { + env.impersonate(admin); + env.call_public(Auth::at(auth_contract_address).set_authorized(other)); + env.advance_block_by(1); + + let scheduled_authorized = env.call_public(Auth::at(auth_contract_address).get_scheduled_authorized()); + assert_eq(scheduled_authorized, other); + + let authorized: AztecAddress = env.call_public(Auth::at(auth_contract_address).get_authorized()); + assert_eq(authorized, to_authorize); + + env.impersonate(to_authorize); + env.call_private_void(Auth::at(auth_contract_address).do_private_authorized_thing()); + + env.impersonate(other); + env.assert_private_call_fails(Auth::at(auth_contract_address).do_private_authorized_thing()); + }; + authorize_other(); + + let authorized_becomes_effective_after_delay_again = || { + env.impersonate(to_authorize); + + // We advance the block by 4 again like above + env.advance_block_by(CHANGE_AUTHORIZED_DELAY_BLOCKS - 1); + let authorized: AztecAddress = env.call_public(Auth::at(auth_contract_address).get_authorized()); + assert_eq(authorized, other); + + let authorized_in_private: AztecAddress = env.call_private(Auth::at(auth_contract_address).get_authorized_in_private()); + assert_eq(authorized_in_private, to_authorize); + + env.advance_block_by(1); + let authorized_in_private_again: AztecAddress = env.call_private(Auth::at(auth_contract_address).get_authorized_in_private()); + assert_eq(authorized_in_private_again, other); + + env.assert_private_call_fails(Auth::at(auth_contract_address).do_private_authorized_thing()); + + env.impersonate(other); + env.call_private_void(Auth::at(auth_contract_address).do_private_authorized_thing()); + }; + authorized_becomes_effective_after_delay_again(); +} diff --git a/noir-projects/noir-contracts/contracts/auth_contract/src/test/utils.nr b/noir-projects/noir-contracts/contracts/auth_contract/src/test/utils.nr new file mode 100644 index 00000000000..f2b87dbdac7 --- /dev/null +++ b/noir-projects/noir-contracts/contracts/auth_contract/src/test/utils.nr @@ -0,0 +1,18 @@ +use dep::aztec::{prelude::AztecAddress, test::helpers::test_environment::TestEnvironment}; + +use crate::Auth; + +pub fn setup() -> (&mut TestEnvironment, AztecAddress, AztecAddress, AztecAddress, AztecAddress) { + let mut env = TestEnvironment::new(); + + let admin = env.create_account(); + let to_authorize = env.create_account(); + let other = env.create_account(); + + let initializer_call_interface = Auth::interface().constructor(admin); + + let auth_contract = env.deploy_self("Auth").with_public_initializer(initializer_call_interface); + let auth_contract_address = auth_contract.to_address(); + env.advance_block_by(1); + (&mut env, auth_contract_address, admin, to_authorize, other) +} diff --git a/yarn-project/end-to-end/Earthfile b/yarn-project/end-to-end/Earthfile index 8e8c05c9377..820c0d45b8f 100644 --- a/yarn-project/end-to-end/Earthfile +++ b/yarn-project/end-to-end/Earthfile @@ -152,9 +152,6 @@ e2e-cheat-codes: e2e-counter-contract: DO +E2E_TEST --test=./src/e2e_counter_contract.test.ts -e2e-cross-chain-messaging: - DO +E2E_TEST --test=./src/e2e_cross_chain_messaging.test.ts - e2e-crowdfunding-and-claim: DO +E2E_TEST --test=./src/e2e_crowdfunding_and_claim.test.ts @@ -230,11 +227,8 @@ e2e-fees-fee-juice-payments: e2e-fees-account-init: DO +E2E_TEST --test=./src/e2e_fees/account_init.test.ts -e2e-public-cross-chain-messaging: - DO +E2E_TEST --test=./src/e2e_public_cross_chain_messaging - -e2e-public-to-private-messaging: - DO +E2E_TEST --test=./src/e2e_public_to_private_messaging.test.ts +e2e-cross-chain-messaging: + DO +E2E_TEST --test=./src/e2e_cross_chain_messaging e2e-state-vars: DO +E2E_TEST --test=./src/e2e_state_vars.test.ts diff --git a/yarn-project/end-to-end/src/e2e_auth_contract.test.ts b/yarn-project/end-to-end/src/e2e_auth_contract.test.ts deleted file mode 100644 index 262590cc81b..00000000000 --- a/yarn-project/end-to-end/src/e2e_auth_contract.test.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { type AccountWallet, AztecAddress, Fr, type PXE } from '@aztec/aztec.js'; -import { AuthContract } from '@aztec/noir-contracts.js'; - -import { jest } from '@jest/globals'; - -import { publicDeployAccounts, setup } from './fixtures/utils.js'; - -describe('e2e_auth_contract', () => { - const TIMEOUT = 120_000; - jest.setTimeout(TIMEOUT); - - let teardown: () => Promise; - - let admin: AccountWallet; - let authorized: AccountWallet; - let other: AccountWallet; - - let pxe: PXE; - - let contract: AuthContract; - - const DELAY = 5; - - beforeAll(async () => { - ({ - teardown, - wallets: [admin, authorized, other], - pxe, - } = await setup(3)); - - await publicDeployAccounts(admin, [admin, authorized, other]); - - const deployTx = AuthContract.deploy(admin, admin.getAddress()).send({}); - const receipt = await deployTx.wait(); - contract = receipt.contract; - }); - - afterAll(() => teardown()); - - async function mineBlock() { - await contract.methods.get_authorized().send().wait(); - } - - async function mineBlocks(amount: number) { - for (let i = 0; i < amount; ++i) { - await mineBlock(); - } - } - - it('authorized is unset initially', async () => { - expect(await contract.methods.get_authorized().simulate()).toEqual(AztecAddress.ZERO); - }); - - it('non-admin cannot set authorized', async () => { - await expect(contract.withWallet(other).methods.set_authorized(authorized.getAddress()).prove()).rejects.toThrow( - 'caller is not admin', - ); - }); - - it('admin sets authorized', async () => { - await contract.withWallet(admin).methods.set_authorized(authorized.getAddress()).send().wait(); - - expect(await contract.methods.get_scheduled_authorized().simulate()).toEqual(authorized.getAddress()); - }); - - it('authorized is not yet set, cannot use permission', async () => { - expect(await contract.methods.get_authorized().simulate()).toEqual(AztecAddress.ZERO); - - await expect(contract.withWallet(authorized).methods.do_private_authorized_thing().prove()).rejects.toThrow( - 'caller is not authorized', - ); - }); - - it('after a while the scheduled change is effective and can be used with max block restriction', async () => { - await mineBlocks(DELAY); // This gets us past the block of change - - // docs:start:simulate_public_getter - expect(await contract.methods.get_authorized().simulate()).toEqual(authorized.getAddress()); - // docs:end:simulate_public_getter - - const interaction = contract.withWallet(authorized).methods.do_private_authorized_thing(); - - const tx = await interaction.prove(); - - const lastBlockNumber = await pxe.getBlockNumber(); - // In the last block there was no scheduled value change, so the earliest one could be scheduled is in the next - // block. Because of the delay, the block of change would be lastBlockNumber + 1 + DELAY. Therefore the block - // horizon should be the block preceding that one. - const expectedMaxBlockNumber = lastBlockNumber + DELAY; - - expect(tx.data.forRollup!.rollupValidationRequests.maxBlockNumber.isSome).toEqual(true); - expect(tx.data.forRollup!.rollupValidationRequests.maxBlockNumber.value).toEqual(new Fr(expectedMaxBlockNumber)); - - expect((await interaction.send().wait()).status).toEqual('success'); - }); - - it('a new authorized address is set but not immediately effective, the previous one retains permissions', async () => { - await contract.withWallet(admin).methods.set_authorized(other.getAddress()).send().wait(); - - expect(await contract.methods.get_authorized().simulate()).toEqual(authorized.getAddress()); - - expect(await contract.methods.get_scheduled_authorized().simulate()).toEqual(other.getAddress()); - - await expect(contract.withWallet(other).methods.do_private_authorized_thing().prove()).rejects.toThrow( - 'caller is not authorized', - ); - - expect((await contract.withWallet(authorized).methods.do_private_authorized_thing().send().wait()).status).toEqual( - 'success', - ); - }); - - it('after some time the scheduled change is made effective', async () => { - await mineBlocks(DELAY); // This gets us past the block of change - - expect(await contract.methods.get_authorized().simulate()).toEqual(other.getAddress()); - - await expect(contract.withWallet(authorized).methods.do_private_authorized_thing().prove()).rejects.toThrow( - 'caller is not authorized', - ); - - expect((await contract.withWallet(other).methods.do_private_authorized_thing().send().wait()).status).toEqual( - 'success', - ); - }); -}); diff --git a/yarn-project/end-to-end/src/e2e_counter_contract.test.ts b/yarn-project/end-to-end/src/e2e_counter_contract.test.ts deleted file mode 100644 index c885a74bb8c..00000000000 --- a/yarn-project/end-to-end/src/e2e_counter_contract.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { type AccountWallet, type AztecAddress, type DebugLogger } from '@aztec/aztec.js'; -import { CounterContract } from '@aztec/noir-contracts.js/Counter'; - -import { setup } from './fixtures/utils.js'; - -describe('e2e_counter_contract', () => { - let wallet: AccountWallet; - let logger: DebugLogger; - let teardown: () => Promise; - - let counterContract: CounterContract; - let owner: AztecAddress; - let outgoingViewer: AztecAddress; - - beforeAll(async () => { - // Setup environment - ({ teardown, wallet, logger } = await setup(1)); - owner = wallet.getAddress(); - // Setting the outgoing viewer to owner to not have to bother with setting up another account. - outgoingViewer = owner; - - counterContract = await CounterContract.deploy(wallet, 0, owner, outgoingViewer).send().deployed(); - - logger.info(`Counter contract deployed at ${counterContract.address}`); - }); - - afterAll(() => teardown()); - - describe('increments', () => { - it('counts', async () => { - const receipt = await counterContract.methods.increment(owner, outgoingViewer).send().wait(); - expect(await counterContract.methods.get_counter(owner).simulate()).toBe(1n); - expect(receipt.transactionFee).toBeGreaterThan(0n); - }); - }); -}); diff --git a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/public_cross_chain_messaging_contract_test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts similarity index 96% rename from yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/public_cross_chain_messaging_contract_test.ts rename to yarn-project/end-to-end/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts index 9771c8403cd..c9fa3c27255 100644 --- a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/public_cross_chain_messaging_contract_test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts @@ -28,7 +28,7 @@ import { CrossChainTestHarness } from '../shared/cross_chain_test_harness.js'; const { E2E_DATA_PATH: dataPath } = process.env; -export class PublicCrossChainMessagingContractTest { +export class CrossChainMessagingTest { private snapshotManager: ISnapshotManager; logger: DebugLogger; wallets: AccountWallet[] = []; @@ -52,8 +52,8 @@ export class PublicCrossChainMessagingContractTest { outbox!: any; // GetContractReturnType | undefined; constructor(testName: string) { - this.logger = createDebugLogger(`aztec:e2e_public_cross_chain_messaging:${testName}`); - this.snapshotManager = createSnapshotManager(`e2e_public_cross_chain_messaging/${testName}`, dataPath); + this.logger = createDebugLogger(`aztec:e2e_cross_chain_messaging:${testName}`); + this.snapshotManager = createSnapshotManager(`e2e_cross_chain_messaging/${testName}`, dataPath); } async assumeProven() { @@ -106,7 +106,7 @@ export class PublicCrossChainMessagingContractTest { ); await this.snapshotManager.snapshot( - 'e2e_public_cross_chain_messaging', + 'e2e_cross_chain_messaging', async () => { // Create the token contract state. // Move this account thing to addAccounts above? diff --git a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/l1_to_l2.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts similarity index 93% rename from yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/l1_to_l2.test.ts rename to yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts index 615ea99c97b..e285e94b43f 100644 --- a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/l1_to_l2.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts @@ -10,10 +10,10 @@ import { import { TestContract } from '@aztec/noir-contracts.js'; import { sendL1ToL2Message } from '../fixtures/l1_to_l2_messaging.js'; -import { PublicCrossChainMessagingContractTest } from './public_cross_chain_messaging_contract_test.js'; +import { CrossChainMessagingTest } from './cross_chain_messaging_test.js'; -describe('e2e_public_cross_chain_messaging l1_to_l2', () => { - const t = new PublicCrossChainMessagingContractTest('l1_to_l2'); +describe('e2e_cross_chain_messaging l1_to_l2', () => { + const t = new CrossChainMessagingTest('l1_to_l2'); let { crossChainTestHarness, aztecNode, user1Wallet } = t; diff --git a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/l2_to_l1.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts similarity index 94% rename from yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/l2_to_l1.test.ts rename to yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts index 73d8b435ac3..782df6f0e9c 100644 --- a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/l2_to_l1.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts @@ -5,10 +5,10 @@ import { TestContract } from '@aztec/noir-contracts.js'; import { type Hex, decodeEventLog } from 'viem'; -import { PublicCrossChainMessagingContractTest } from './public_cross_chain_messaging_contract_test.js'; +import { CrossChainMessagingTest } from './cross_chain_messaging_test.js'; -describe('e2e_public_cross_chain_messaging l2_to_l1', () => { - const t = new PublicCrossChainMessagingContractTest('l2_to_l1'); +describe('e2e_cross_chain_messaging l2_to_l1', () => { + const t = new CrossChainMessagingTest('l2_to_l1'); let { crossChainTestHarness, aztecNode, user1Wallet, outbox } = t; diff --git a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/failure_cases.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_failure_cases.test.ts similarity index 55% rename from yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/failure_cases.test.ts rename to yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_failure_cases.test.ts index 7ec54101628..afc868a2086 100644 --- a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/failure_cases.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_failure_cases.test.ts @@ -3,12 +3,13 @@ import { sha256ToField } from '@aztec/foundation/crypto'; import { toFunctionSelector } from 'viem'; -import { PublicCrossChainMessagingContractTest } from './public_cross_chain_messaging_contract_test.js'; +import { NO_L1_TO_L2_MSG_ERROR } from '../fixtures/fixtures.js'; +import { CrossChainMessagingTest } from './cross_chain_messaging_test.js'; -describe('e2e_public_cross_chain_messaging failures', () => { - const t = new PublicCrossChainMessagingContractTest('failures'); +describe('e2e_cross_chain_messaging token_bridge_failure_cases', () => { + const t = new CrossChainMessagingTest('token_bridge_failure_cases'); - let { crossChainTestHarness, ethAccount, l2Bridge, user1Wallet, user2Wallet } = t; + let { crossChainTestHarness, ethAccount, l2Bridge, user1Wallet, user2Wallet, aztecNode, ownerAddress } = t; beforeAll(async () => { await t.applyBaseSnapshots(); @@ -17,6 +18,8 @@ describe('e2e_public_cross_chain_messaging failures', () => { ({ crossChainTestHarness, user1Wallet, user2Wallet } = t); ethAccount = crossChainTestHarness.ethAccount; l2Bridge = crossChainTestHarness.l2Bridge; + aztecNode = crossChainTestHarness.aztecNode; + ownerAddress = crossChainTestHarness.ownerAddress; }, 300_000); afterAll(async () => { @@ -38,7 +41,7 @@ describe('e2e_public_cross_chain_messaging failures', () => { ).rejects.toThrow(/unauthorized/); }, 60_000); - it("can't claim funds privately which were intended for public deposit from the token portal", async () => { + it("Can't claim funds privately which were intended for public deposit from the token portal", async () => { const bridgeAmount = 100n; const [secret, secretHash] = crossChainTestHarness.generateClaimSecret(); @@ -65,4 +68,37 @@ describe('e2e_public_cross_chain_messaging failures', () => { l2Bridge.withWallet(user2Wallet).methods.claim_private(secretHash, bridgeAmount, secret).prove(), ).rejects.toThrow(`No non-nullified L1 to L2 message found for message hash ${wrongMessage.hash().toString()}`); }, 60_000); + + it("Can't claim funds publicly which were intended for private deposit from the token portal", async () => { + // 1. Mint tokens on L1 + const bridgeAmount = 100n; + await crossChainTestHarness.mintTokensOnL1(bridgeAmount); + + // 2. Deposit tokens to the TokenPortal privately + const [secretForL2MessageConsumption, secretHashForL2MessageConsumption] = + crossChainTestHarness.generateClaimSecret(); + + const msgHash = await crossChainTestHarness.sendTokensToPortalPrivate( + Fr.random(), + bridgeAmount, + secretHashForL2MessageConsumption, + ); + expect(await crossChainTestHarness.getL1BalanceOf(ethAccount)).toBe(0n); + + // Wait for the message to be available for consumption + await crossChainTestHarness.makeMessageConsumable(msgHash); + + // get message leaf index, needed for claiming in public + const maybeIndexAndPath = await aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash, 0n); + expect(maybeIndexAndPath).toBeDefined(); + const messageLeafIndex = maybeIndexAndPath![0]; + + // 3. Consume L1 -> L2 message and try to mint publicly on L2 - should fail + await expect( + l2Bridge + .withWallet(user2Wallet) + .methods.claim_public(ownerAddress, bridgeAmount, secretForL2MessageConsumption, messageLeafIndex) + .prove(), + ).rejects.toThrow(NO_L1_TO_L2_MSG_ERROR); + }); }); diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_private.test.ts similarity index 60% rename from yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts rename to yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_private.test.ts index 8ff1dca5857..6edd41c5599 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_private.test.ts @@ -1,81 +1,51 @@ -import { - type AccountWallet, - type AztecAddress, - type AztecNode, - type DebugLogger, - EthAddress, - Fr, - L1Actor, - L1ToL2Message, - L2Actor, - computeAuthWitMessageHash, -} from '@aztec/aztec.js'; +import { Fr, L1Actor, L1ToL2Message, L2Actor } from '@aztec/aztec.js'; import { sha256ToField } from '@aztec/foundation/crypto'; import { RollupAbi } from '@aztec/l1-artifacts'; -import { type TokenBridgeContract, type TokenContract } from '@aztec/noir-contracts.js'; import { getContract } from 'viem'; import { toFunctionSelector } from 'viem/utils'; -import { NO_L1_TO_L2_MSG_ERROR } from './fixtures/fixtures.js'; -import { setup } from './fixtures/utils.js'; -import { CrossChainTestHarness } from './shared/cross_chain_test_harness.js'; +import { CrossChainMessagingTest } from './cross_chain_messaging_test.js'; -describe('e2e_cross_chain_messaging', () => { - let aztecNode: AztecNode; - let logger: DebugLogger; - let teardown: () => Promise; +describe('e2e_cross_chain_messaging token_bridge_private', () => { + const t = new CrossChainMessagingTest('token_bridge_private'); - let user1Wallet: AccountWallet; - let user2Wallet: AccountWallet; - let ethAccount: EthAddress; - let ownerAddress: AztecAddress; - - let crossChainTestHarness: CrossChainTestHarness; - let l2Token: TokenContract; - let l2Bridge: TokenBridgeContract; - let rollup: any; + let { + crossChainTestHarness, + ethAccount, + aztecNode, + logger, + ownerAddress, + l2Bridge, + l2Token, + user1Wallet, + user2Wallet, + rollup, + } = t; beforeEach(async () => { - const { - aztecNode: aztecNode_, - pxe, - deployL1ContractsValues, - wallets, - logger: logger_, - teardown: teardown_, - } = await setup(2); - - crossChainTestHarness = await CrossChainTestHarness.new( - aztecNode_, - pxe, - deployL1ContractsValues.publicClient, - deployL1ContractsValues.walletClient, - wallets[0], - logger_, - ); + await t.applyBaseSnapshots(); + await t.setup(); + // Have to destructure again to ensure we have latest refs. + ({ crossChainTestHarness, user1Wallet, user2Wallet } = t); + ethAccount = crossChainTestHarness.ethAccount; + aztecNode = crossChainTestHarness.aztecNode; + logger = crossChainTestHarness.logger; + ownerAddress = crossChainTestHarness.ownerAddress; + l2Bridge = crossChainTestHarness.l2Bridge; + l2Token = crossChainTestHarness.l2Token; rollup = getContract({ - address: deployL1ContractsValues.l1ContractAddresses.rollupAddress.toString(), + address: crossChainTestHarness.l1ContractAddresses.rollupAddress.toString(), abi: RollupAbi, - client: deployL1ContractsValues.walletClient, + client: crossChainTestHarness.walletClient, }); - - l2Token = crossChainTestHarness.l2Token; - l2Bridge = crossChainTestHarness.l2Bridge; - ethAccount = crossChainTestHarness.ethAccount; - ownerAddress = crossChainTestHarness.ownerAddress; - user1Wallet = wallets[0]; - user2Wallet = wallets[1]; - logger = logger_; - aztecNode = aztecNode_; - teardown = teardown_; - logger.info('Successfully deployed contracts and initialized portal'); - }); + }, 300_000); afterEach(async () => { - await teardown(); + await t.teardown(); }); + // docs:start:e2e_private_cross_chain it('Privately deposit funds from L1 -> L2 and withdraw back to L1', async () => { // Generate a claim secret using pedersen @@ -148,7 +118,6 @@ describe('e2e_cross_chain_messaging', () => { }); // docs:end:e2e_private_cross_chain - // Unit tests for TokenBridge's private methods. it('Someone else can mint funds to me on my behalf (privately)', async () => { const l1TokenBalance = 1000000n; const bridgeAmount = 100n; @@ -204,60 +173,6 @@ describe('e2e_cross_chain_messaging', () => { ); await crossChainTestHarness.redeemShieldPrivatelyOnL2(bridgeAmount, secretForRedeemingMintedNotes); await crossChainTestHarness.expectPrivateBalanceOnL2(ownerAddress, bridgeAmount); - }); - - it("Bridge can't withdraw my funds if I don't give approval", async () => { - const mintAmountToUser1 = 100n; - await crossChainTestHarness.mintTokensPublicOnL2(mintAmountToUser1); - - const withdrawAmount = 9n; - const nonce = Fr.random(); - const expectedBurnMessageHash = computeAuthWitMessageHash( - { - caller: l2Bridge.address, - action: l2Token.methods.burn(user1Wallet.getAddress(), withdrawAmount, nonce).request(), - }, - { chainId: user1Wallet.getChainId(), version: user1Wallet.getVersion() }, - ); - // Should fail as owner has not given approval to bridge burn their funds. - await expect( - l2Bridge - .withWallet(user1Wallet) - .methods.exit_to_l1_private(l2Token.address, ethAccount, withdrawAmount, EthAddress.ZERO, nonce) - .prove(), - ).rejects.toThrow(`Unknown auth witness for message hash ${expectedBurnMessageHash.toString()}`); - }); - - it("Can't claim funds publicly if they were deposited privately", async () => { - // 1. Mint tokens on L1 - const bridgeAmount = 100n; - await crossChainTestHarness.mintTokensOnL1(bridgeAmount); - - // 2. Deposit tokens to the TokenPortal privately - const [secretForL2MessageConsumption, secretHashForL2MessageConsumption] = - crossChainTestHarness.generateClaimSecret(); - - const msgHash = await crossChainTestHarness.sendTokensToPortalPrivate( - Fr.random(), - bridgeAmount, - secretHashForL2MessageConsumption, - ); - expect(await crossChainTestHarness.getL1BalanceOf(ethAccount)).toBe(0n); - - // Wait for the message to be available for consumption - await crossChainTestHarness.makeMessageConsumable(msgHash); - - // get message leaf index, needed for claiming in public - const maybeIndexAndPath = await aztecNode.getL1ToL2MessageMembershipWitness('latest', msgHash, 0n); - expect(maybeIndexAndPath).toBeDefined(); - const messageLeafIndex = maybeIndexAndPath![0]; - - // 3. Consume L1 -> L2 message and try to mint publicly on L2 - should fail - await expect( - l2Bridge - .withWallet(user2Wallet) - .methods.claim_public(ownerAddress, bridgeAmount, secretForL2MessageConsumption, messageLeafIndex) - .prove(), - ).rejects.toThrow(NO_L1_TO_L2_MSG_ERROR); - }); + }), + 90_000; }); diff --git a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/deposits.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public.test.ts similarity index 96% rename from yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/deposits.test.ts rename to yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public.test.ts index c3ca23206f4..22d44a573e9 100644 --- a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/deposits.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public.test.ts @@ -1,10 +1,10 @@ import { Fr } from '@aztec/aztec.js'; import { NO_L1_TO_L2_MSG_ERROR } from '../fixtures/fixtures.js'; -import { PublicCrossChainMessagingContractTest } from './public_cross_chain_messaging_contract_test.js'; +import { CrossChainMessagingTest } from './cross_chain_messaging_test.js'; -describe('e2e_public_cross_chain_messaging deposits', () => { - const t = new PublicCrossChainMessagingContractTest('deposits'); +describe('e2e_cross_chain_messaging token_bridge_public', () => { + const t = new CrossChainMessagingTest('token_bridge_public'); let { crossChainTestHarness, diff --git a/yarn-project/end-to-end/src/e2e_public_to_private_messaging.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public_to_private.test.ts similarity index 68% rename from yarn-project/end-to-end/src/e2e_public_to_private_messaging.test.ts rename to yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public_to_private.test.ts index 6b7ac9451ae..5b1086c095c 100644 --- a/yarn-project/end-to-end/src/e2e_public_to_private_messaging.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public_to_private.test.ts @@ -1,50 +1,28 @@ -import { type AztecAddress, type AztecNode, type DebugLogger, type EthAddress } from '@aztec/aztec.js'; +import { CrossChainMessagingTest } from './cross_chain_messaging_test.js'; -import { setup } from './fixtures/utils.js'; -import { CrossChainTestHarness } from './shared/cross_chain_test_harness.js'; +describe('e2e_cross_chain_messaging token_bridge_public_to_private', () => { + const t = new CrossChainMessagingTest('token_bridge_public_to_private'); -describe('e2e_public_to_private_messaging', () => { - let logger: DebugLogger; - let teardown: () => Promise; - - let aztecNode: AztecNode; - let ethAccount: EthAddress; + let { crossChainTestHarness, ethAccount, aztecNode, ownerAddress } = t; let underlyingERC20: any; - let ownerAddress: AztecAddress; - let crossChainTestHarness: CrossChainTestHarness; beforeEach(async () => { - const { - aztecNode: aztecNode_, - pxe, - deployL1ContractsValues, - wallet, - logger: logger_, - teardown: teardown_, - } = await setup(2); - crossChainTestHarness = await CrossChainTestHarness.new( - aztecNode_, - pxe, - deployL1ContractsValues.publicClient, - deployL1ContractsValues.walletClient, - wallet, - logger_, - ); + await t.applyBaseSnapshots(); + await t.setup(); + // Have to destructure again to ensure we have latest refs. + ({ crossChainTestHarness } = t); - aztecNode = crossChainTestHarness.aztecNode; ethAccount = crossChainTestHarness.ethAccount; + aztecNode = crossChainTestHarness.aztecNode; ownerAddress = crossChainTestHarness.ownerAddress; underlyingERC20 = crossChainTestHarness.underlyingERC20; - - teardown = teardown_; - logger = logger_; - logger.info('Successfully deployed contracts and initialized portal'); - }); + }, 300_000); afterEach(async () => { - await teardown(); + await t.teardown(); }); + // Moved from e2e_public_to_private_messaging.test.ts it('Milestone 5.4: Should be able to create a commitment in a public function and spend in a private function', async () => { // Generate a claim secret using pedersen const l1TokenBalance = 1000000n; diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts index 5daa7a72611..cebc961785b 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts @@ -1,4 +1,5 @@ -import { AztecAddress, type DebugLogger, type PXE, type Wallet } from '@aztec/aztec.js'; +import { getDeployedTestAccountsWallets } from '@aztec/accounts/testing'; +import { AztecAddress, type DebugLogger, type PXE, type Wallet, createPXEClient, makeFetch } from '@aztec/aztec.js'; import { CounterContract, StatefulTestContract } from '@aztec/noir-contracts.js'; import { TestContract } from '@aztec/noir-contracts.js/Test'; import { TokenContract } from '@aztec/noir-contracts.js/Token'; @@ -98,4 +99,18 @@ describe('e2e_deploy_contract deploy method', () => { it.skip('publicly deploys and calls a public function in a tx in the same block', async () => { // TODO(@spalladino): Requires being able to read a nullifier on the same block it was emitted. }); + + describe('regressions', () => { + it('fails properly when trying to deploy a contract with a failing constructor with a pxe client with retries', async () => { + const { PXE_URL } = process.env; + if (!PXE_URL) { + return; + } + const pxeClient = createPXEClient(PXE_URL, makeFetch([1, 2, 3], false)); + const [wallet] = await getDeployedTestAccountsWallets(pxeClient); + await expect( + StatefulTestContract.deployWithOpts({ wallet, method: 'wrong_constructor' }).send().deployed(), + ).rejects.toThrow(/Unknown function/); + }); + }); }); diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/regressions.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/regressions.test.ts deleted file mode 100644 index 729ed976450..00000000000 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/regressions.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { getDeployedTestAccountsWallets } from '@aztec/accounts/testing'; -import { createPXEClient, makeFetch } from '@aztec/aztec.js'; -import { StatefulTestContract } from '@aztec/noir-contracts.js'; - -import { DeployTest } from './deploy_test.js'; - -describe('e2e_deploy_contract regressions', () => { - const t = new DeployTest('regressions'); - - beforeAll(async () => { - await t.setup(); - }); - - afterAll(() => t.teardown()); - - it('fails properly when trying to deploy a contract with a failing constructor with a pxe client with retries', async () => { - const { PXE_URL } = process.env; - if (!PXE_URL) { - return; - } - const pxeClient = createPXEClient(PXE_URL, makeFetch([1, 2, 3], false)); - const [wallet] = await getDeployedTestAccountsWallets(pxeClient); - await expect( - StatefulTestContract.deployWithOpts({ wallet, method: 'wrong_constructor' }).send().deployed(), - ).rejects.toThrow(/Unknown function/); - }); -});