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 8bbe59e00fd0..9ff3d7cc7af0 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 @@ -140,9 +140,28 @@ describe('e2e_deploy_contract deploy method', () => { await new BatchCall(wallet, [...deploy.calls, init]).send().wait(); }, 300_000); - 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. - }); + it.skip('publicly deploys a contract in one tx and calls a public function on it later in the same block', async () => { + await t.aztecNode.setConfig({ minTxsPerBlock: 2 }); + + const owner = wallet.getAddress(); + logger.debug('Initializing deploy method'); + const deployMethod = StatefulTestContract.deploy(wallet, owner, owner, 42); + logger.debug('Creating request/calls to register and deploy contract'); + const deploy = await deployMethod.request(); + const deployTx = new BatchCall(wallet, deploy.calls); + logger.debug('Getting an instance of the not-yet-deployed contract to batch calls to'); + const contract = await StatefulTestContract.at((await deployMethod.getInstance()).address, wallet); + + logger.debug('Creating public call to run in same block as deployment'); + const publicCall = contract.methods.increment_public_value(owner, 84); + + logger.debug('Deploying a contract and calling a public function in the same block'); + const [deployTxReceipt, publicCallTxReceipt] = await Promise.all([ + deployTx.send({ skipPublicSimulation: true }).wait({ timeout: 600 }), + publicCall.send({ skipPublicSimulation: true }).wait({ timeout: 600 }), + ]); + expect(deployTxReceipt.blockNumber).toEqual(publicCallTxReceipt.blockNumber); + }, 300_000); describe('regressions', () => { it('fails properly when trying to deploy a contract with a failing constructor with a pxe client with retries', async () => { diff --git a/yarn-project/simulator/src/public/avm/fixtures/simple_contract_data_source.ts b/yarn-project/simulator/src/public/avm/fixtures/simple_contract_data_source.ts index e02c306c9547..3fc0adc6fc6d 100644 --- a/yarn-project/simulator/src/public/avm/fixtures/simple_contract_data_source.ts +++ b/yarn-project/simulator/src/public/avm/fixtures/simple_contract_data_source.ts @@ -2,12 +2,11 @@ import type { Fr } from '@aztec/foundation/fields'; import { createLogger } from '@aztec/foundation/log'; import { type ContractArtifact, FunctionSelector } from '@aztec/stdlib/abi'; import type { AztecAddress } from '@aztec/stdlib/aztec-address'; -import { - type ContractClassPublic, - type ContractDataSource, - type ContractInstanceWithAddress, - type PublicFunction, - computePublicBytecodeCommitment, +import type { + ContractClassPublic, + ContractDataSource, + ContractInstanceWithAddress, + PublicFunction, } from '@aztec/stdlib/contract'; import { PUBLIC_DISPATCH_FN_NAME } from './index.js'; @@ -63,9 +62,8 @@ export class SimpleContractDataSource implements ContractDataSource { return Promise.resolve(this.contractClasses.get(id.toString())); } - async getBytecodeCommitment(id: Fr): Promise { - const contractClass = await this.getContractClass(id); - return Promise.resolve(computePublicBytecodeCommitment(contractClass!.packedBytecode)); + getBytecodeCommitment(_id: Fr): Promise { + return Promise.resolve(undefined); } getContract(address: AztecAddress): Promise { diff --git a/yarn-project/simulator/src/public/avm/journal/journal.ts b/yarn-project/simulator/src/public/avm/journal/journal.ts index 74742b2d0e59..a15b651a6b48 100644 --- a/yarn-project/simulator/src/public/avm/journal/journal.ts +++ b/yarn-project/simulator/src/public/avm/journal/journal.ts @@ -193,17 +193,15 @@ export class AvmPersistableStateManager { `Value mismatch when performing public data write (got value: ${value}, value in tree: ${newLeafPreimage.value})`, ); } else { - this.log.debug(`insertion witness data length: ${result.insertionWitnessData.length}`); // The new leaf preimage should have the new value and slot newLeafPreimage.slot = leafSlot; newLeafPreimage.value = value; // TODO: is this necessary?! Why doesn't sequentialInsert return the newLeafPreimage via // result.insertionWitnessData[0].leafPreimage? - this.log.debug( - `newLeafPreimage.slot: ${newLeafPreimage.slot}, newLeafPreimage.value: ${newLeafPreimage.value}`, + this.log.trace( + `newLeafPreimage.slot: ${newLeafPreimage.slot}, newLeafPreimage.value: ${newLeafPreimage.value}, insertionIndex: ${result.insertionWitnessData[0].index}`, ); - this.log.debug(`insertion index: ${result.insertionWitnessData[0].index}`); insertionPath = result.insertionWitnessData[0].siblingPath.toFields(); } diff --git a/yarn-project/simulator/src/public/fixtures/public_tx_simulation_tester.ts b/yarn-project/simulator/src/public/fixtures/public_tx_simulation_tester.ts index aa356207e9c4..2785a4536d8b 100644 --- a/yarn-project/simulator/src/public/fixtures/public_tx_simulation_tester.ts +++ b/yarn-project/simulator/src/public/fixtures/public_tx_simulation_tester.ts @@ -12,7 +12,7 @@ import { BaseAvmSimulationTester } from '../avm/fixtures/base_avm_simulation_tes import { getContractFunctionArtifact, getFunctionSelector } from '../avm/fixtures/index.js'; import { SimpleContractDataSource } from '../avm/fixtures/simple_contract_data_source.js'; import { WorldStateDB } from '../public_db_sources.js'; -import { type PublicTxResult, PublicTxSimulator } from '../public_tx_simulator.js'; +import { type PublicTxResult, PublicTxSimulator } from '../public_tx_simulator/public_tx_simulator.js'; import { createTxForPublicCalls } from './index.js'; const TIMESTAMP = new Fr(99833); diff --git a/yarn-project/simulator/src/public/fixtures/utils.ts b/yarn-project/simulator/src/public/fixtures/utils.ts index 53c20f3875e7..7e4a28e04985 100644 --- a/yarn-project/simulator/src/public/fixtures/utils.ts +++ b/yarn-project/simulator/src/public/fixtures/utils.ts @@ -17,6 +17,7 @@ import { Gas, GasFees, GasSettings } from '@aztec/stdlib/gas'; import { siloNullifier } from '@aztec/stdlib/hash'; import { PartialPrivateTailPublicInputsForPublic, + PartialPrivateTailPublicInputsForRollup, PrivateKernelTailCircuitPublicInputs, RollupValidationRequests, ScopedLogHash, @@ -90,6 +91,28 @@ export async function createTxForPublicCalls( return tx; } +export function createTxForPrivateOnly(feePayer = AztecAddress.zero(), gasUsedByPrivate: Gas = new Gas(10, 10)): Tx { + // use max limits + const gasLimits = new Gas(DEFAULT_GAS_LIMIT, MAX_L2_GAS_PER_TX_PUBLIC_PORTION); + + const forRollup = PartialPrivateTailPublicInputsForRollup.empty(); + + const maxFeesPerGas = feePayer.isZero() ? GasFees.empty() : new GasFees(10, 10); + const gasSettings = new GasSettings(gasLimits, Gas.empty(), maxFeesPerGas, GasFees.empty()); + const txContext = new TxContext(Fr.zero(), Fr.zero(), gasSettings); + const constantData = new TxConstantData(BlockHeader.empty(), txContext, Fr.zero(), Fr.zero()); + + const txData = new PrivateKernelTailCircuitPublicInputs( + constantData, + RollupValidationRequests.empty(), + /*gasUsed=*/ gasUsedByPrivate, + feePayer, + /*forPublic=*/ undefined, + forRollup, + ); + return Tx.newWithTxData(txData); +} + export async function addNewContractClassToTx( tx: Tx, contractClass: ContractClassPublic, @@ -116,13 +139,14 @@ export async function addNewContractClassToTx( new Fr(REGISTERER_CONTRACT_ADDRESS), ]); + const accumulatedData = tx.data.forPublic ? tx.data.forPublic!.revertibleAccumulatedData : tx.data.forRollup!.end; if (!skipNullifierInsertion) { - const nextNullifierIndex = countAccumulatedItems(tx.data.forPublic!.revertibleAccumulatedData.nullifiers); - tx.data.forPublic!.revertibleAccumulatedData.nullifiers[nextNullifierIndex] = contractClass.id; + const nextNullifierIndex = countAccumulatedItems(accumulatedData.nullifiers); + accumulatedData.nullifiers[nextNullifierIndex] = contractClass.id; } - const nextLogIndex = countAccumulatedItems(tx.data.forPublic!.revertibleAccumulatedData.contractClassLogsHashes); - tx.data.forPublic!.revertibleAccumulatedData.contractClassLogsHashes[nextLogIndex] = contractClassLogHash; + const nextLogIndex = countAccumulatedItems(accumulatedData.contractClassLogsHashes); + accumulatedData.contractClassLogsHashes[nextLogIndex] = contractClassLogHash; tx.contractClassLogs.push(contractClassLog); } @@ -164,11 +188,12 @@ export async function addNewContractInstanceToTx( contractInstance.address.toField(), ); + const accumulatedData = tx.data.forPublic ? tx.data.forPublic!.revertibleAccumulatedData : tx.data.forRollup!.end; if (!skipNullifierInsertion) { - const nextNullifierIndex = countAccumulatedItems(tx.data.forPublic!.revertibleAccumulatedData.nullifiers); - tx.data.forPublic!.revertibleAccumulatedData.nullifiers[nextNullifierIndex] = contractAddressNullifier; + const nextNullifierIndex = countAccumulatedItems(accumulatedData.nullifiers); + accumulatedData.nullifiers[nextNullifierIndex] = contractAddressNullifier; } - const nextLogIndex = countAccumulatedItems(tx.data.forPublic!.revertibleAccumulatedData.privateLogs); - tx.data.forPublic!.revertibleAccumulatedData.privateLogs[nextLogIndex] = contractInstanceLog; + const nextLogIndex = countAccumulatedItems(accumulatedData.privateLogs); + accumulatedData.privateLogs[nextLogIndex] = contractInstanceLog; } diff --git a/yarn-project/simulator/src/public/index.ts b/yarn-project/simulator/src/public/index.ts index 2fab51c1dc86..bf389ec528a6 100644 --- a/yarn-project/simulator/src/public/index.ts +++ b/yarn-project/simulator/src/public/index.ts @@ -1,8 +1,8 @@ export * from '../common/db_interfaces.js'; -export * from './public_tx_simulator.js'; +export * from './public_tx_simulator/public_tx_simulator.js'; export { type EnqueuedPublicCallExecutionResult, type PublicFunctionCallResult } from './execution.js'; export * from './public_db_sources.js'; -export { PublicProcessor, PublicProcessorFactory } from './public_processor.js'; +export { PublicProcessor, PublicProcessorFactory } from './public_processor/public_processor.js'; export { SideEffectTrace } from './side_effect_trace.js'; export { getExecutionRequestsByPhase } from './utils.js'; export { PublicTxSimulationTester } from './fixtures/index.js'; diff --git a/yarn-project/simulator/src/public/public_db_sources.ts b/yarn-project/simulator/src/public/public_db_sources.ts index 97326988cf57..ddc57236c67f 100644 --- a/yarn-project/simulator/src/public/public_db_sources.ts +++ b/yarn-project/simulator/src/public/public_db_sources.ts @@ -189,11 +189,9 @@ export class ContractsDataSourcePublicDB implements PublicContractsDB { // Try and retrieve from cache const key = contractClassId.toString(); const result = this.bytecodeCommitmentCache.get(key); - if (result !== undefined) { return result; } - // Now try from the store const fromStore = await this.dataSource.getBytecodeCommitment(contractClassId); if (fromStore !== undefined) { @@ -201,11 +199,12 @@ export class ContractsDataSourcePublicDB implements PublicContractsDB { return fromStore; } - // Not in either the store or the caches, build it here and cache + // Not in either the store or the cache, build it here and cache const contractClass = await this.getContractClass(contractClassId); if (contractClass === undefined) { return undefined; } + const value = await computePublicBytecodeCommitment(contractClass.packedBytecode); this.bytecodeCommitmentCache.set(key, value); return value; diff --git a/yarn-project/simulator/src/public/apps_tests/token_public_processor.test.ts b/yarn-project/simulator/src/public/public_processor/apps_tests/deployments.test.ts similarity index 63% rename from yarn-project/simulator/src/public/apps_tests/token_public_processor.test.ts rename to yarn-project/simulator/src/public/public_processor/apps_tests/deployments.test.ts index 1402170a6f50..e66962e94a13 100644 --- a/yarn-project/simulator/src/public/apps_tests/token_public_processor.test.ts +++ b/yarn-project/simulator/src/public/public_processor/apps_tests/deployments.test.ts @@ -1,30 +1,27 @@ import { Fr } from '@aztec/foundation/fields'; -import { createLogger } from '@aztec/foundation/log'; import { TestDateProvider } from '@aztec/foundation/timer'; +import { AvmTestContractArtifact } from '@aztec/noir-contracts.js/AvmTest'; import { TokenContractArtifact } from '@aztec/noir-contracts.js/Token'; import { RevertCode } from '@aztec/stdlib/avm'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; -import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract'; import { GasFees } from '@aztec/stdlib/gas'; import { GlobalVariables } from '@aztec/stdlib/tx'; import { getTelemetryClient } from '@aztec/telemetry-client'; import { NativeWorldStateService } from '@aztec/world-state'; -import { PublicTxSimulationTester, SimpleContractDataSource } from '../../server.js'; -import { createContractClassAndInstance } from '../avm/fixtures/index.js'; -import { addNewContractClassToTx, addNewContractInstanceToTx } from '../fixtures/utils.js'; -import { WorldStateDB } from '../public_db_sources.js'; +import { PublicTxSimulationTester, SimpleContractDataSource } from '../../../server.js'; +import { createContractClassAndInstance } from '../../avm/fixtures/index.js'; +import { addNewContractClassToTx, addNewContractInstanceToTx, createTxForPrivateOnly } from '../../fixtures/utils.js'; +import { WorldStateDB } from '../../public_db_sources.js'; +import { PublicTxSimulator } from '../../public_tx_simulator/public_tx_simulator.js'; import { PublicProcessor } from '../public_processor.js'; -import { PublicTxSimulator } from '../public_tx_simulator.js'; -describe('Public Processor app tests: TokenContract', () => { - const logger = createLogger('public-processor-apps-tests-token'); +describe('Public processor contract registration/deployment tests', () => { + //const logger = createLogger('public-processor-apps-tests-deployments'); - const NUM_TRANSFERS = 10; const admin = AztecAddress.fromNumber(42); const sender = AztecAddress.fromNumber(111); - let token: ContractInstanceWithAddress; let worldStateDB: WorldStateDB; let tester: PublicTxSimulationTester; let processor: PublicProcessor; @@ -55,69 +52,96 @@ describe('Public Processor app tests: TokenContract', () => { await tester.setFeePayerBalance(sender); }); - it('token constructor, mint, many transfers', async () => { - const startTime = performance.now(); + it('can deploy in a private-only tx and call a public function later in the block', async () => { + const { contractClass, contractInstance } = await createContractClassAndInstance( + /*constructorArgs=*/ [], + admin, + AvmTestContractArtifact, + ); - const mintAmount = 1_000_000n; - const transferAmount = 10n; - const nonce = new Fr(0); + // First transaction - deploys and initializes first token contract + const deployTx = createTxForPrivateOnly(/*feePayer=*/ admin); + await addNewContractClassToTx(deployTx, contractClass); + await addNewContractInstanceToTx(deployTx, contractInstance); - const constructorArgs = [admin, /*name=*/ 'Token', /*symbol=*/ 'TOK', /*decimals=*/ new Fr(18)]; + // NOTE: we need to include the contract artifact for each enqueued call, otherwise the tester + // will not know how to construct the TX since we are intentionally not adding the contract to + // the contract data source. - token = await tester.registerAndDeployContract(constructorArgs, /*deployer=*/ admin, TokenContractArtifact); - const constructorTx = await tester.createTx( + // Second transaction - makes a simple public call on the deployed contract + const simplePublicTx = await tester.createTx( /*sender=*/ admin, /*setupCalls=*/ [], /*appCalls=*/ [ { - address: token.address, - fnName: 'constructor', - args: constructorArgs, + address: contractInstance.address, + fnName: 'read_storage_single', + args: [], + contractArtifact: AvmTestContractArtifact, }, ], ); - const mintTx = await tester.createTx( + const results = await processor.process([deployTx, simplePublicTx]); + const processedTxs = results[0]; + const failedTxs = results[1]; + expect(processedTxs.length).toBe(2); + expect(failedTxs.length).toBe(0); + + // First tx should succeed (constructor) + expect(processedTxs[0].revertCode).toEqual(RevertCode.OK); + + // Second tx should succeed (public call) + expect(processedTxs[1].revertCode).toEqual(RevertCode.OK); + }); + + it('can deploy a contract and call its public function in same tx', async () => { + const mintAmount = 1_000_000n; + const constructorArgs = [admin, /*name=*/ 'Token', /*symbol=*/ 'TOK', /*decimals=*/ new Fr(18)]; + const { contractClass, contractInstance } = await createContractClassAndInstance( + constructorArgs, + admin, + TokenContractArtifact, + ); + const token = contractInstance; + + // NOTE: we need to include the contract artifact for each enqueued call, otherwise the tester + // will not know how to construct the TX since we are intentionally not adding the contract to + // the contract data source. + + // Deploys a contract and calls its public constructor and another public call in same tx + const deployAndCallTx = await tester.createTx( /*sender=*/ admin, /*setupCalls=*/ [], /*appCalls=*/ [ + { + address: token.address, + fnName: 'constructor', + args: constructorArgs, + contractArtifact: TokenContractArtifact, + }, { address: token.address, fnName: 'mint_to_public', args: [/*to=*/ sender, mintAmount], + contractArtifact: TokenContractArtifact, }, ], ); + await addNewContractClassToTx(deployAndCallTx, contractClass); + await addNewContractInstanceToTx(deployAndCallTx, contractInstance); - const transferTxs = []; - for (let i = 0; i < NUM_TRANSFERS; i++) { - const receiver = AztecAddress.fromNumber(200 + i); // different receiver each time - transferTxs.push( - await tester.createTx( - /*sender=*/ sender, - /*setupCalls=*/ [], - /*appCalls=*/ [ - { - address: token.address, - fnName: 'transfer_in_public', - args: [/*from=*/ sender, /*to=*/ receiver, transferAmount, nonce], - }, - ], - ), - ); - } - - const results = await processor.process([constructorTx, mintTx, ...transferTxs]); + const results = await processor.process([deployAndCallTx]); const processedTxs = results[0]; const failedTxs = results[1]; - expect(processedTxs.length).toBe(NUM_TRANSFERS + 2); // constructor, mint, transfers + expect(processedTxs.length).toBe(1); expect(failedTxs.length).toBe(0); - const endTime = performance.now(); - logger.verbose(`TokenContract public processor test took ${endTime - startTime}ms\n`); + // First tx should succeed (constructor) + expect(processedTxs[0].revertCode).toEqual(RevertCode.OK); }); - it('new contract cannot get removed from ContractDataSource by a later failing transaction', async () => { + it('new contract cannot get removed from block-level cache by a later failing transaction', async () => { const mintAmount = 1_000_000n; const constructorArgs = [admin, /*name=*/ 'Token', /*symbol=*/ 'TOK', /*decimals=*/ new Fr(18)]; @@ -128,14 +152,6 @@ describe('Public Processor app tests: TokenContract', () => { ); const token = contractInstance; - // another token instance, same contract class - const otherAdmin = AztecAddress.fromNumber(43); - const anotherToken = await tester.registerAndDeployContract( - constructorArgs, - /*deployer=*/ otherAdmin, - TokenContractArtifact, - ); - // First transaction - deploys and initializes first token contract const passingConstructorTx = await tester.createTx( /*sender=*/ admin, @@ -165,14 +181,14 @@ describe('Public Processor app tests: TokenContract', () => { /*setupCalls=*/ [], /*appCalls=*/ [ { - address: anotherToken.address, + address: token.address, fnName: 'constructor', args: constructorArgs, contractArtifact: TokenContractArtifact, }, // The next enqueued call will fail because sender has no tokens to transfer { - address: anotherToken.address, + address: token.address, fnName: 'transfer_in_public', args: [/*from=*/ sender, /*to=*/ receiver, transferAmount, nonce], contractArtifact: TokenContractArtifact, @@ -200,7 +216,6 @@ describe('Public Processor app tests: TokenContract', () => { ); const results = await processor.process([passingConstructorTx, failingConstructorTx, mintTx]); - //const results = await processor.process([passingConstructorTx]); const processedTxs = results[0]; const failedTxs = results[1]; expect(processedTxs.length).toBe(3); diff --git a/yarn-project/simulator/src/public/public_processor/apps_tests/token.test.ts b/yarn-project/simulator/src/public/public_processor/apps_tests/token.test.ts new file mode 100644 index 000000000000..ad971d355c2b --- /dev/null +++ b/yarn-project/simulator/src/public/public_processor/apps_tests/token.test.ts @@ -0,0 +1,116 @@ +import { Fr } from '@aztec/foundation/fields'; +import { createLogger } from '@aztec/foundation/log'; +import { TestDateProvider } from '@aztec/foundation/timer'; +import { TokenContractArtifact } from '@aztec/noir-contracts.js/Token'; +import { AztecAddress } from '@aztec/stdlib/aztec-address'; +import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract'; +import { GasFees } from '@aztec/stdlib/gas'; +import { GlobalVariables } from '@aztec/stdlib/tx'; +import { getTelemetryClient } from '@aztec/telemetry-client'; +import { NativeWorldStateService } from '@aztec/world-state'; + +import { PublicTxSimulationTester, SimpleContractDataSource } from '../../../server.js'; +import { WorldStateDB } from '../../public_db_sources.js'; +import { PublicTxSimulator } from '../../public_tx_simulator/public_tx_simulator.js'; +import { PublicProcessor } from '../public_processor.js'; + +describe('Public Processor app tests: TokenContract', () => { + const logger = createLogger('public-processor-apps-tests-token'); + + const NUM_TRANSFERS = 10; + const admin = AztecAddress.fromNumber(42); + const sender = AztecAddress.fromNumber(111); + + let token: ContractInstanceWithAddress; + let worldStateDB: WorldStateDB; + let tester: PublicTxSimulationTester; + let processor: PublicProcessor; + + beforeEach(async () => { + const globals = GlobalVariables.empty(); + // apply some nonzero default gas fees + globals.gasFees = new GasFees(2, 3); + + const contractDataSource = new SimpleContractDataSource(); + const merkleTrees = await (await NativeWorldStateService.tmp()).fork(); + worldStateDB = new WorldStateDB(merkleTrees, contractDataSource); + const simulator = new PublicTxSimulator(merkleTrees, worldStateDB, globals, /*doMerkleOperations=*/ true); + + processor = new PublicProcessor( + merkleTrees, + globals, + worldStateDB, + simulator, + new TestDateProvider(), + getTelemetryClient(), + ); + + tester = new PublicTxSimulationTester(worldStateDB, contractDataSource, merkleTrees); + + // make sure tx senders have fee balance + await tester.setFeePayerBalance(admin); + await tester.setFeePayerBalance(sender); + }); + + it('token constructor, mint, many transfers', async () => { + const startTime = performance.now(); + + const mintAmount = 1_000_000n; + const transferAmount = 10n; + const nonce = new Fr(0); + + const constructorArgs = [admin, /*name=*/ 'Token', /*symbol=*/ 'TOK', /*decimals=*/ new Fr(18)]; + + token = await tester.registerAndDeployContract(constructorArgs, /*deployer=*/ admin, TokenContractArtifact); + const constructorTx = await tester.createTx( + /*sender=*/ admin, + /*setupCalls=*/ [], + /*appCalls=*/ [ + { + address: token.address, + fnName: 'constructor', + args: constructorArgs, + }, + ], + ); + + const mintTx = await tester.createTx( + /*sender=*/ admin, + /*setupCalls=*/ [], + /*appCalls=*/ [ + { + address: token.address, + fnName: 'mint_to_public', + args: [/*to=*/ sender, mintAmount], + }, + ], + ); + + const transferTxs = []; + for (let i = 0; i < NUM_TRANSFERS; i++) { + const receiver = AztecAddress.fromNumber(200 + i); // different receiver each time + transferTxs.push( + await tester.createTx( + /*sender=*/ sender, + /*setupCalls=*/ [], + /*appCalls=*/ [ + { + address: token.address, + fnName: 'transfer_in_public', + args: [/*from=*/ sender, /*to=*/ receiver, transferAmount, nonce], + }, + ], + ), + ); + } + + const results = await processor.process([constructorTx, mintTx, ...transferTxs]); + const processedTxs = results[0]; + const failedTxs = results[1]; + expect(processedTxs.length).toBe(NUM_TRANSFERS + 2); // constructor, mint, transfers + expect(failedTxs.length).toBe(0); + + const endTime = performance.now(); + logger.verbose(`TokenContract public processor test took ${endTime - startTime}ms\n`); + }); +}); diff --git a/yarn-project/simulator/src/public/public_processor.test.ts b/yarn-project/simulator/src/public/public_processor/public_processor.test.ts similarity index 98% rename from yarn-project/simulator/src/public/public_processor.test.ts rename to yarn-project/simulator/src/public/public_processor/public_processor.test.ts index d443c533d97f..fa5d2f30b37b 100644 --- a/yarn-project/simulator/src/public/public_processor.test.ts +++ b/yarn-project/simulator/src/public/public_processor/public_processor.test.ts @@ -15,9 +15,9 @@ import { getTelemetryClient } from '@aztec/telemetry-client'; import { type MockProxy, mock } from 'jest-mock-extended'; -import type { WorldStateDB } from './public_db_sources.js'; +import { WorldStateDB } from '../public_db_sources.js'; +import type { PublicTxResult, PublicTxSimulator } from '../public_tx_simulator/public_tx_simulator.js'; import { PublicProcessor } from './public_processor.js'; -import type { PublicTxResult, PublicTxSimulator } from './public_tx_simulator.js'; describe('public_processor', () => { let db: MockProxy; diff --git a/yarn-project/simulator/src/public/public_processor.ts b/yarn-project/simulator/src/public/public_processor/public_processor.ts similarity index 97% rename from yarn-project/simulator/src/public/public_processor.ts rename to yarn-project/simulator/src/public/public_processor/public_processor.ts index f259e114583a..b2a805c685de 100644 --- a/yarn-project/simulator/src/public/public_processor.ts +++ b/yarn-project/simulator/src/public/public_processor/public_processor.ts @@ -33,9 +33,9 @@ import { } from '@aztec/telemetry-client'; import { ForkCheckpoint } from '@aztec/world-state/native'; -import { WorldStateDB } from './public_db_sources.js'; +import { WorldStateDB } from '../public_db_sources.js'; +import { PublicTxSimulator } from '../public_tx_simulator/public_tx_simulator.js'; import { PublicProcessorMetrics } from './public_processor_metrics.js'; -import { PublicTxSimulator } from './public_tx_simulator.js'; /** * Creates new instances of PublicProcessor given the provided merkle tree db and contract data source. @@ -265,6 +265,10 @@ export class PublicProcessor implements Traceable { // If there are no public calls, perform all tree insertions for side effects from private // When there are public calls, the PublicTxSimulator & AVM handle tree insertions. await this.doTreeInsertionsForPrivateOnlyTx(processedTx); + // Add any contracts registered/deployed in this private-only tx to the block-level cache + // (add to tx-level cache and then commit to block-level cache) + await this.worldStateDB.addNewContracts(tx); + this.worldStateDB.commitContractsForTx(); } nullifierCache?.addNullifiers(processedTx.txEffect.nullifiers.map(n => n.toBuffer())); @@ -289,6 +293,8 @@ export class PublicProcessor implements Traceable { } finally { // Base case is we always commit the checkpoint. Using the ForkCheckpoint means this has no effect if the tx was reverted await checkpoint.commit(); + // The tx-level contracts cache should not live on to the next tx + this.worldStateDB.clearContractsForTx(); } } diff --git a/yarn-project/simulator/src/public/public_processor_metrics.ts b/yarn-project/simulator/src/public/public_processor/public_processor_metrics.ts similarity index 100% rename from yarn-project/simulator/src/public/public_processor_metrics.ts rename to yarn-project/simulator/src/public/public_processor/public_processor_metrics.ts diff --git a/yarn-project/simulator/src/public/apps_tests/avm_test.test.ts b/yarn-project/simulator/src/public/public_tx_simulator/apps_tests/avm_test.test.ts similarity index 95% rename from yarn-project/simulator/src/public/apps_tests/avm_test.test.ts rename to yarn-project/simulator/src/public/public_tx_simulator/apps_tests/avm_test.test.ts index a313e476bec0..f425b34fdb51 100644 --- a/yarn-project/simulator/src/public/apps_tests/avm_test.test.ts +++ b/yarn-project/simulator/src/public/public_tx_simulator/apps_tests/avm_test.test.ts @@ -3,7 +3,7 @@ import { AvmTestContractArtifact } from '@aztec/noir-contracts.js/AvmTest'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract'; -import { PublicTxSimulationTester } from '../fixtures/public_tx_simulation_tester.js'; +import { PublicTxSimulationTester } from '../../fixtures/public_tx_simulation_tester.js'; describe('Public TX simulator apps tests: AvmTestContract', () => { const deployer = AztecAddress.fromNumber(42); diff --git a/yarn-project/simulator/src/public/apps_tests/token.test.ts b/yarn-project/simulator/src/public/public_tx_simulator/apps_tests/token.test.ts similarity index 97% rename from yarn-project/simulator/src/public/apps_tests/token.test.ts rename to yarn-project/simulator/src/public/public_tx_simulator/apps_tests/token.test.ts index bb0bc15a1abd..861aaadfd36f 100644 --- a/yarn-project/simulator/src/public/apps_tests/token.test.ts +++ b/yarn-project/simulator/src/public/public_tx_simulator/apps_tests/token.test.ts @@ -4,7 +4,7 @@ import { TokenContractArtifact } from '@aztec/noir-contracts.js/Token'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract'; -import { PublicTxSimulationTester } from '../fixtures/public_tx_simulation_tester.js'; +import { PublicTxSimulationTester } from '../../fixtures/public_tx_simulation_tester.js'; import type { PublicTxResult } from '../public_tx_simulator.js'; describe('Public TX simulator apps tests: TokenContract', () => { diff --git a/yarn-project/simulator/src/public/public_tx_context.ts b/yarn-project/simulator/src/public/public_tx_simulator/public_tx_context.ts similarity index 98% rename from yarn-project/simulator/src/public/public_tx_context.ts rename to yarn-project/simulator/src/public/public_tx_simulator/public_tx_context.ts index 2b8c38f38916..04bcc0b367ab 100644 --- a/yarn-project/simulator/src/public/public_tx_context.ts +++ b/yarn-project/simulator/src/public/public_tx_simulator/public_tx_context.ts @@ -45,10 +45,10 @@ import { import { strict as assert } from 'assert'; import { inspect } from 'util'; -import { AvmPersistableStateManager } from './avm/index.js'; -import type { WorldStateDB } from './public_db_sources.js'; -import { SideEffectArrayLengths, SideEffectTrace } from './side_effect_trace.js'; -import { getCallRequestsByPhase, getExecutionRequestsByPhase } from './utils.js'; +import { AvmPersistableStateManager } from '../avm/index.js'; +import type { WorldStateDB } from '../public_db_sources.js'; +import { SideEffectArrayLengths, SideEffectTrace } from '../side_effect_trace.js'; +import { getCallRequestsByPhase, getExecutionRequestsByPhase } from '../utils.js'; /** * The transaction-level context for public execution. diff --git a/yarn-project/simulator/src/public/public_tx_simulator.test.ts b/yarn-project/simulator/src/public/public_tx_simulator/public_tx_simulator.test.ts similarity index 99% rename from yarn-project/simulator/src/public/public_tx_simulator.test.ts rename to yarn-project/simulator/src/public/public_tx_simulator/public_tx_simulator.test.ts index 936063c51cba..940b10f3d877 100644 --- a/yarn-project/simulator/src/public/public_tx_simulator.test.ts +++ b/yarn-project/simulator/src/public/public_tx_simulator/public_tx_simulator.test.ts @@ -36,10 +36,10 @@ import { NativeWorldStateService } from '@aztec/world-state'; import { jest } from '@jest/globals'; import { mock } from 'jest-mock-extended'; -import { AvmFinalizedCallResult } from './avm/avm_contract_call_result.js'; -import type { AvmPersistableStateManager } from './avm/journal/journal.js'; -import type { InstructionSet } from './avm/serialization/bytecode_serialization.js'; -import { WorldStateDB } from './public_db_sources.js'; +import { AvmFinalizedCallResult } from '../avm/avm_contract_call_result.js'; +import type { AvmPersistableStateManager } from '../avm/journal/journal.js'; +import type { InstructionSet } from '../avm/serialization/bytecode_serialization.js'; +import { WorldStateDB } from '../public_db_sources.js'; import { type PublicTxResult, PublicTxSimulator } from './public_tx_simulator.js'; describe('public_tx_simulator', () => { diff --git a/yarn-project/simulator/src/public/public_tx_simulator.ts b/yarn-project/simulator/src/public/public_tx_simulator/public_tx_simulator.ts similarity index 97% rename from yarn-project/simulator/src/public/public_tx_simulator.ts rename to yarn-project/simulator/src/public/public_tx_simulator/public_tx_simulator.ts index 52c1a0a73b98..d9651affee60 100644 --- a/yarn-project/simulator/src/public/public_tx_simulator.ts +++ b/yarn-project/simulator/src/public/public_tx_simulator/public_tx_simulator.ts @@ -20,12 +20,12 @@ import { Attributes, type TelemetryClient, type Tracer, getTelemetryClient, trac import { strict as assert } from 'assert'; -import { getPublicFunctionDebugName } from '../common/debug_fn_name.js'; -import type { AvmFinalizedCallResult } from './avm/avm_contract_call_result.js'; -import { type AvmPersistableStateManager, AvmSimulator } from './avm/index.js'; -import { NullifierCollisionError } from './avm/journal/nullifiers.js'; -import { ExecutorMetrics } from './executor_metrics.js'; -import type { WorldStateDB } from './public_db_sources.js'; +import { getPublicFunctionDebugName } from '../../common/debug_fn_name.js'; +import type { AvmFinalizedCallResult } from '../avm/avm_contract_call_result.js'; +import { type AvmPersistableStateManager, AvmSimulator } from '../avm/index.js'; +import { NullifierCollisionError } from '../avm/journal/nullifiers.js'; +import { ExecutorMetrics } from '../executor_metrics.js'; +import type { WorldStateDB } from '../public_db_sources.js'; import { PublicTxContext } from './public_tx_context.js'; export type ProcessedPhase = {