diff --git a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts deleted file mode 100644 index 9e878db7895..00000000000 --- a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { type AztecAddress, BatchCall, type DebugLogger, Fr, type PXE, type Wallet, toBigIntBE } from '@aztec/aztec.js'; -import { ChildContract, ImportTestContract, ParentContract, TestContract } from '@aztec/noir-contracts.js'; - -import { setup } from './fixtures/utils.js'; - -describe('e2e_nested_contract', () => { - let pxe: PXE; - let wallet: Wallet; - let logger: DebugLogger; - let teardown: () => Promise; - - beforeEach(async () => { - ({ teardown, pxe, wallet, logger } = await setup()); - }); - - afterEach(() => teardown()); - - describe('parent manually calls child', () => { - let parentContract: ParentContract; - let childContract: ChildContract; - - beforeEach(async () => { - parentContract = await ParentContract.deploy(wallet).send().deployed(); - childContract = await ChildContract.deploy(wallet).send().deployed(); - }); - - const getChildStoredValue = (child: { address: AztecAddress }) => pxe.getPublicStorageAt(child.address, new Fr(1)); - - it('performs nested calls', async () => { - await parentContract.methods - .entry_point(childContract.address, childContract.methods.value.selector) - .send() - .wait(); - }); - - it('fails simulation if calling a function not allowed to be called externally', async () => { - await expect( - parentContract.methods - .entry_point(childContract.address, (childContract.methods as any).value_internal.selector) - .prove(), - ).rejects.toThrow(/Assertion failed: Function value_internal can only be called internally/); - }); - - it('performs public nested calls', async () => { - await parentContract.methods - .pub_entry_point(childContract.address, childContract.methods.pub_get_value.selector, 42n) - .send() - .wait(); - }); - - it('enqueues a single public call', async () => { - await parentContract.methods - .enqueue_call_to_child(childContract.address, childContract.methods.pub_inc_value.selector, 42n) - .send() - .wait(); - expect(await getChildStoredValue(childContract)).toEqual(new Fr(42n)); - }); - - it('fails simulation if calling a public function not allowed to be called externally', async () => { - await expect( - parentContract.methods - .enqueue_call_to_child( - childContract.address, - (childContract.methods as any).pub_inc_value_internal.selector, - 42n, - ) - .prove(), - ).rejects.toThrow(/Assertion failed: Function pub_inc_value_internal can only be called internally/); - }); - - it('enqueues multiple public calls', async () => { - await parentContract.methods - .enqueue_call_to_child_twice(childContract.address, childContract.methods.pub_inc_value.selector, 42n) - .send() - .wait(); - expect(await getChildStoredValue(childContract)).toEqual(new Fr(85n)); - }); - - it('enqueues a public call with nested public calls', async () => { - await parentContract.methods - .enqueue_call_to_pub_entry_point(childContract.address, childContract.methods.pub_inc_value.selector, 42n) - .send() - .wait(); - expect(await getChildStoredValue(childContract)).toEqual(new Fr(42n)); - }); - - it('enqueues multiple public calls with nested public calls', async () => { - await parentContract.methods - .enqueue_calls_to_pub_entry_point(childContract.address, childContract.methods.pub_inc_value.selector, 42n) - .send() - .wait(); - expect(await getChildStoredValue(childContract)).toEqual(new Fr(85n)); - }); - - // Regression for https://github.com/AztecProtocol/aztec-packages/issues/640 - it('reads fresh value after write within the same tx', async () => { - await parentContract.methods - .pub_entry_point_twice(childContract.address, childContract.methods.pub_inc_value.selector, 42n) - .send() - .wait(); - expect(await getChildStoredValue(childContract)).toEqual(new Fr(84n)); - }); - - // Regression for https://github.com/AztecProtocol/aztec-packages/issues/1645 - // Executes a public call first and then a private call (which enqueues another public call) - // through the account contract, if the account entrypoint behaves properly, it will honor - // this order and not run the private call first which results in the public calls being inverted. - it('executes public calls in expected order', async () => { - const pubSetValueSelector = childContract.methods.pub_set_value.selector; - const actions = [ - childContract.methods.pub_set_value(20n).request(), - parentContract.methods.enqueue_call_to_child(childContract.address, pubSetValueSelector, 40n).request(), - ]; - - const tx = await new BatchCall(wallet, actions).send().wait(); - const extendedLogs = ( - await wallet.getUnencryptedLogs({ - fromBlock: tx.blockNumber!, - }) - ).logs; - const processedLogs = extendedLogs.map(extendedLog => toBigIntBE(extendedLog.log.data)); - expect(processedLogs).toEqual([20n, 40n]); - expect(await getChildStoredValue(childContract)).toEqual(new Fr(40n)); - }); - }); - - describe('importer uses autogenerated test contract interface', () => { - let importerContract: ImportTestContract; - let testContract: TestContract; - - beforeEach(async () => { - logger.info(`Deploying importer test contract`); - importerContract = await ImportTestContract.deploy(wallet).send().deployed(); - logger.info(`Deploying test contract`); - testContract = await TestContract.deploy(wallet).send().deployed(); - }); - - it('calls a method with multiple arguments', async () => { - logger.info(`Calling main on importer contract`); - await importerContract.methods.main_contract(testContract.address).send().wait(); - }); - - it('calls a method no arguments', async () => { - logger.info(`Calling noargs on importer contract`); - await importerContract.methods.call_no_args(testContract.address).send().wait(); - }); - - it('calls an open function', async () => { - logger.info(`Calling openfn on importer contract`); - await importerContract.methods.call_open_fn(testContract.address).send().wait(); - }); - - it('calls an open function from an open function', async () => { - logger.info(`Calling pub openfn on importer contract`); - await importerContract.methods.pub_call_open_fn(testContract.address).send().wait(); - }); - }); -}); diff --git a/yarn-project/end-to-end/src/e2e_nested_contract/importer.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract/importer.test.ts new file mode 100644 index 00000000000..f74f92dcde8 --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_nested_contract/importer.test.ts @@ -0,0 +1,45 @@ +import { ImportTestContract, TestContract } from '@aztec/noir-contracts.js'; + +import { NestedContractTest } from './nested_contract_test.js'; + +describe('e2e_nested_contract manual', () => { + const t = new NestedContractTest('manual'); + let testContract: TestContract; + let importerContract: ImportTestContract; + let { wallets, logger } = t; + + beforeAll(async () => { + await t.applyBaseSnapshots(); + await t.setup(); + ({ wallets, logger } = t); + }); + + beforeEach(async () => { + importerContract = await ImportTestContract.deploy(wallets[0]).send().deployed(); + testContract = await TestContract.deploy(wallets[0]).send().deployed(); + }); + + afterAll(async () => { + await t.teardown(); + }); + + it('calls a method with multiple arguments', async () => { + logger.info(`Calling main on importer contract`); + await importerContract.methods.main_contract(testContract.address).send().wait(); + }); + + it('calls a method no arguments', async () => { + logger.info(`Calling noargs on importer contract`); + await importerContract.methods.call_no_args(testContract.address).send().wait(); + }); + + it('calls an open function', async () => { + logger.info(`Calling openfn on importer contract`); + await importerContract.methods.call_open_fn(testContract.address).send().wait(); + }); + + it('calls an open function from an open function', async () => { + logger.info(`Calling pub openfn on importer contract`); + await importerContract.methods.pub_call_open_fn(testContract.address).send().wait(); + }); +}); diff --git a/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_call.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_call.test.ts new file mode 100644 index 00000000000..640345447d0 --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_call.test.ts @@ -0,0 +1,29 @@ +import { NestedContractTest } from './nested_contract_test.js'; + +describe('e2e_nested_contract manual', () => { + const t = new NestedContractTest('manual'); + let { parentContract, childContract } = t; + + beforeAll(async () => { + await t.applyBaseSnapshots(); + await t.applyManualSnapshots(); + await t.setup(); + ({ parentContract, childContract } = t); + }); + + afterAll(async () => { + await t.teardown(); + }); + + it('performs nested calls', async () => { + await parentContract.methods.entry_point(childContract.address, childContract.methods.value.selector).send().wait(); + }); + + it('fails simulation if calling a function not allowed to be called externally', async () => { + await expect( + parentContract.methods + .entry_point(childContract.address, (childContract.methods as any).value_internal.selector) + .prove(), + ).rejects.toThrow(/Assertion failed: Function value_internal can only be called internally/); + }); +}); diff --git a/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts new file mode 100644 index 00000000000..a0ba5c148c0 --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts @@ -0,0 +1,71 @@ +import { type AztecAddress, Fr } from '@aztec/aztec.js'; +import { ChildContract, ParentContract } from '@aztec/noir-contracts.js'; + +import { NestedContractTest } from './nested_contract_test.js'; + +describe('e2e_nested_contract manual_enqueue', () => { + const t = new NestedContractTest('manual_enqueue'); + let { wallets, pxe, parentContract, childContract } = t; + + const getChildStoredValue = (child: { address: AztecAddress }) => pxe.getPublicStorageAt(child.address, new Fr(1)); + + beforeAll(async () => { + await t.applyBaseSnapshots(); + // We don't have the manual snapshot because every test requires a fresh setup and teardown + await t.setup(); + ({ wallets, pxe } = t); + }); + + beforeEach(async () => { + parentContract = await ParentContract.deploy(wallets[0]).send().deployed(); + childContract = await ChildContract.deploy(wallets[0]).send().deployed(); + }); + + afterAll(async () => { + await t.teardown(); + }); + + it('enqueues a single public call', async () => { + await parentContract.methods + .enqueue_call_to_child(childContract.address, childContract.methods.pub_inc_value.selector, 42n) + .send() + .wait(); + expect(await getChildStoredValue(childContract)).toEqual(new Fr(42n)); + }); + + it('fails simulation if calling a public function not allowed to be called externally', async () => { + await expect( + parentContract.methods + .enqueue_call_to_child( + childContract.address, + (childContract.methods as any).pub_inc_value_internal.selector, + 42n, + ) + .prove(), + ).rejects.toThrow(/Assertion failed: Function pub_inc_value_internal can only be called internally/); + }); + + it('enqueues multiple public calls', async () => { + await parentContract.methods + .enqueue_call_to_child_twice(childContract.address, childContract.methods.pub_inc_value.selector, 42n) + .send() + .wait(); + expect(await getChildStoredValue(childContract)).toEqual(new Fr(85n)); + }); + + it('enqueues a public call with nested public calls', async () => { + await parentContract.methods + .enqueue_call_to_pub_entry_point(childContract.address, childContract.methods.pub_inc_value.selector, 42n) + .send() + .wait(); + expect(await getChildStoredValue(childContract)).toEqual(new Fr(42n)); + }); + + it('enqueues multiple public calls with nested public calls', async () => { + await parentContract.methods + .enqueue_calls_to_pub_entry_point(childContract.address, childContract.methods.pub_inc_value.selector, 42n) + .send() + .wait(); + expect(await getChildStoredValue(childContract)).toEqual(new Fr(85n)); + }); +}); diff --git a/yarn-project/end-to-end/src/e2e_nested_contract/manual_public.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract/manual_public.test.ts new file mode 100644 index 00000000000..0db8df6f558 --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_nested_contract/manual_public.test.ts @@ -0,0 +1,61 @@ +import { type AztecAddress, BatchCall, Fr, type Wallet, toBigIntBE } from '@aztec/aztec.js'; + +import { NestedContractTest } from './nested_contract_test.js'; + +describe('e2e_nested_contract manual', () => { + const t = new NestedContractTest('manual'); + let wallet: Wallet; + let { wallets, pxe, parentContract, childContract } = t; + + const getChildStoredValue = (child: { address: AztecAddress }) => pxe.getPublicStorageAt(child.address, new Fr(1)); + + beforeAll(async () => { + await t.applyBaseSnapshots(); + await t.applyManualSnapshots(); + await t.setup(); + ({ wallets, pxe, parentContract, childContract } = t); + wallet = wallets[0]; + }); + + afterAll(async () => { + await t.teardown(); + }); + + it('performs public nested calls', async () => { + await parentContract.methods + .pub_entry_point(childContract.address, childContract.methods.pub_get_value.selector, 42n) + .send() + .wait(); + }); + + // Regression for https://github.com/AztecProtocol/aztec-packages/issues/640 + it('reads fresh value after write within the same tx', async () => { + await parentContract.methods + .pub_entry_point_twice(childContract.address, childContract.methods.pub_inc_value.selector, 42n) + .send() + .wait(); + expect(await getChildStoredValue(childContract)).toEqual(new Fr(84n)); + }); + + // Regression for https://github.com/AztecProtocol/aztec-packages/issues/1645 + // Executes a public call first and then a private call (which enqueues another public call) + // through the account contract, if the account entrypoint behaves properly, it will honor + // this order and not run the private call first which results in the public calls being inverted. + it('executes public calls in expected order', async () => { + const pubSetValueSelector = childContract.methods.pub_set_value.selector; + const actions = [ + childContract.methods.pub_set_value(20n).request(), + parentContract.methods.enqueue_call_to_child(childContract.address, pubSetValueSelector, 40n).request(), + ]; + + const tx = await new BatchCall(wallet, actions).send().wait(); + const extendedLogs = ( + await wallet.getUnencryptedLogs({ + fromBlock: tx.blockNumber!, + }) + ).logs; + const processedLogs = extendedLogs.map(extendedLog => toBigIntBE(extendedLog.log.data)); + expect(processedLogs).toEqual([20n, 40n]); + expect(await getChildStoredValue(childContract)).toEqual(new Fr(40n)); + }); +}); diff --git a/yarn-project/end-to-end/src/e2e_nested_contract/nested_contract_test.ts b/yarn-project/end-to-end/src/e2e_nested_contract/nested_contract_test.ts new file mode 100644 index 00000000000..512320b7fbd --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_nested_contract/nested_contract_test.ts @@ -0,0 +1,88 @@ +import { getSchnorrAccount } from '@aztec/accounts/schnorr'; +import { + type AccountWallet, + type CompleteAddress, + type DebugLogger, + type PXE, + createDebugLogger, +} from '@aztec/aztec.js'; +import { ChildContract, ParentContract } from '@aztec/noir-contracts.js'; + +import { + SnapshotManager, + type SubsystemsContext, + addAccounts, + publicDeployAccounts, +} from '../fixtures/snapshot_manager.js'; + +const { E2E_DATA_PATH: dataPath } = process.env; + +export class NestedContractTest { + private snapshotManager: SnapshotManager; + logger: DebugLogger; + wallets: AccountWallet[] = []; + accounts: CompleteAddress[] = []; + pxe!: PXE; + + parentContract!: ParentContract; + childContract!: ChildContract; + + constructor(testName: string) { + this.logger = createDebugLogger(`aztec:e2e_nested_contract:${testName}`); + this.snapshotManager = new SnapshotManager(`e2e_nested_contract/${testName}`, dataPath); + } + + /** + * Adds two state shifts to snapshot manager. + * 1. Add 3 accounts. + * 2. Publicly deploy accounts + */ + async applyBaseSnapshots() { + await this.snapshotManager.snapshot('3_accounts', addAccounts(3, this.logger), async ({ accountKeys }, { pxe }) => { + const accountManagers = accountKeys.map(ak => getSchnorrAccount(pxe, ak[0], ak[1], 1)); + this.wallets = await Promise.all(accountManagers.map(a => a.getWallet())); + this.accounts = await pxe.getRegisteredAccounts(); + this.wallets.forEach((w, i) => this.logger.verbose(`Wallet ${i} address: ${w.getAddress()}`)); + + this.pxe = pxe; + }); + + await this.snapshotManager.snapshot( + 'public_deploy', + async () => {}, + async () => { + this.logger.verbose(`Public deploy accounts...`); + await publicDeployAccounts(this.wallets[0], this.accounts.slice(0, 2)); + }, + ); + } + + async setup() { + await this.snapshotManager.setup(); + } + + async teardown() { + await this.snapshotManager.teardown(); + } + + snapshot = ( + name: string, + apply: (context: SubsystemsContext) => Promise, + restore: (snapshotData: T, context: SubsystemsContext) => Promise = () => Promise.resolve(), + ): Promise => this.snapshotManager.snapshot(name, apply, restore); + + async applyManualSnapshots() { + await this.snapshotManager.snapshot( + 'manual', + async () => { + const parentContract = await ParentContract.deploy(this.wallets[0]).send().deployed(); + const childContract = await ChildContract.deploy(this.wallets[0]).send().deployed(); + return { parentContractAddress: parentContract.address, childContractAddress: childContract.address }; + }, + async ({ parentContractAddress, childContractAddress }) => { + this.parentContract = await ParentContract.at(parentContractAddress, this.wallets[0]); + this.childContract = await ChildContract.at(childContractAddress, this.wallets[0]); + }, + ); + } +} diff --git a/yarn-project/pxe/src/note_processor/note_processor.test.ts b/yarn-project/pxe/src/note_processor/note_processor.test.ts index 1ddd5bd1ce6..f334c25162e 100644 --- a/yarn-project/pxe/src/note_processor/note_processor.test.ts +++ b/yarn-project/pxe/src/note_processor/note_processor.test.ts @@ -168,7 +168,7 @@ describe('Note Processor', () => { index: BigInt(firstBlockDataStartIndex + 2), }), ]); - }); + }, 25_000); it('should store multiple notes that belong to us', async () => { const prependedBlocks = 2;