diff --git a/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts b/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts index d435ee9551be..84628ad02b3e 100644 --- a/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts +++ b/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts @@ -29,9 +29,9 @@ import { deriveSigningKey } from '@aztec/stdlib/keys'; import { TestWallet } from '@aztec/test-wallet/server'; import { MNEMONIC } from '../../fixtures/fixtures.js'; -import { type SubsystemsContext, deployAccounts, setupFromFresh, teardown } from '../../fixtures/snapshot_manager.js'; +import { type EndToEndContext, type SetupOptions, deployAccounts, setup, teardown } from '../../fixtures/setup.js'; import { mintTokensToPrivate } from '../../fixtures/token_utils.js'; -import { type SetupOptions, setupSponsoredFPC } from '../../fixtures/utils.js'; +import { setupSponsoredFPC } from '../../fixtures/utils.js'; import { CrossChainTestHarness } from '../../shared/cross_chain_test_harness.js'; import { FeeJuicePortalTestingHarnessFactory, @@ -50,7 +50,7 @@ export class ClientFlowsBenchmark { public logger: Logger; public aztecNode!: AztecNode; public cheatCodes!: CheatCodes; - public context!: SubsystemsContext; + public context!: EndToEndContext; public chainMonitor!: ChainMonitor; public feeJuiceBridgeTestHarness!: GasBridgingTestHarness; public adminWallet!: TestWallet; @@ -130,13 +130,18 @@ export class ClientFlowsBenchmark { async setup() { this.logger.info('Setting up subsystems from fresh'); - this.context = await setupFromFresh(this.logger, this.setupOptions, this.setupOptions); + this.context = await setup(0, { + ...this.setupOptions, + fundSponsoredFPC: true, + skipAccountDeployment: true, + l1ContractsArgs: this.setupOptions, + }); await this.applyBaseSetup(); - await this.context.aztecNode.setConfig({ feeRecipient: this.sequencerAddress, coinbase: this.coinbase }); + await this.context.aztecNodeService!.setConfig({ feeRecipient: this.sequencerAddress, coinbase: this.coinbase }); - const rollupContract = RollupContract.getFromConfig(this.context.aztecNodeConfig); - this.chainMonitor = new ChainMonitor(rollupContract, this.context.dateProvider, this.logger, 200).start(); + const rollupContract = RollupContract.getFromConfig(this.context.config); + this.chainMonitor = new ChainMonitor(rollupContract, this.context.dateProvider!, this.logger, 200).start(); return this; } @@ -203,7 +208,7 @@ export class ClientFlowsBenchmark { const [{ address: adminAddress }, { address: sequencerAddress }] = deployedAccounts; this.adminWallet = this.context.wallet; - this.aztecNode = this.context.aztecNode; + this.aztecNode = this.context.aztecNodeService!; this.cheatCodes = this.context.cheatCodes; this.adminAddress = adminAddress; @@ -231,8 +236,8 @@ export class ClientFlowsBenchmark { this.feeJuiceContract = FeeJuiceContract.at(ProtocolContractAddress.FeeJuice, this.adminWallet); this.feeJuiceBridgeTestHarness = await FeeJuicePortalTestingHarnessFactory.create({ - aztecNode: this.context.aztecNode, - aztecNodeAdmin: this.context.aztecNode, + aztecNode: this.context.aztecNodeService!, + aztecNodeAdmin: this.context.aztecNodeService!, l1Client: this.context.deployL1ContractsValues.l1Client, wallet: this.adminWallet, logger: this.logger, @@ -302,7 +307,7 @@ export class ClientFlowsBenchmark { } public async createCrossChainTestHarness(owner: AztecAddress) { - const l1Client = createExtendedL1Client(this.context.aztecNodeConfig.l1RpcUrls, MNEMONIC); + const l1Client = createExtendedL1Client(this.context.config.l1RpcUrls, MNEMONIC); const underlyingERC20Address = await deployL1Contract(l1Client, TestERC20Abi, TestERC20Bytecode, [ 'Underlying', diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts index d8118644f48e..39b2e03f0586 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts @@ -13,13 +13,7 @@ import type { TestWallet } from '@aztec/test-wallet/server'; import { jest } from '@jest/globals'; -import { - type SubsystemsContext, - deployAccounts, - publicDeployAccounts, - setupFromFresh, - teardown, -} from '../fixtures/snapshot_manager.js'; +import { type EndToEndContext, deployAccounts, publicDeployAccounts, setup, teardown } from '../fixtures/setup.js'; import { TokenSimulator } from '../simulators/token_simulator.js'; export class Role { @@ -53,7 +47,7 @@ export class BlacklistTokenContractTest { // This value MUST match the same value that we have in the contract static CHANGE_ROLES_DELAY = 86400; - context!: SubsystemsContext; + context!: EndToEndContext; logger: Logger; wallet!: TestWallet; asset!: TokenBlacklistContract; @@ -98,8 +92,8 @@ export class BlacklistTokenContractTest { }); this.cheatCodes = this.context.cheatCodes; - this.aztecNode = this.context.aztecNode; - this.sequencer = this.context.sequencer; + this.aztecNode = this.context.aztecNodeService!; + this.sequencer = this.context.sequencer!; this.wallet = this.context.wallet; this.adminAddress = deployedAccounts[0].address; this.otherAddress = deployedAccounts[1].address; @@ -137,7 +131,10 @@ export class BlacklistTokenContractTest { async setup() { this.logger.info('Setting up fresh context'); - this.context = await setupFromFresh(this.logger); + this.context = await setup(0, { + fundSponsoredFPC: true, + skipAccountDeployment: true, + }); await this.applyBaseSetup(); } diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts index 983589ea8ee0..ffd55390d3d7 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts @@ -23,13 +23,13 @@ import type { TestWallet } from '@aztec/test-wallet/server'; import { MNEMONIC } from '../fixtures/fixtures.js'; import { - type SubsystemsContext, + type EndToEndContext, + type SetupOptions, deployAccounts, publicDeployAccounts, - setupFromFresh, + setup, teardown, -} from '../fixtures/snapshot_manager.js'; -import type { SetupOptions } from '../fixtures/utils.js'; +} from '../fixtures/setup.js'; import { CrossChainTestHarness } from '../shared/cross_chain_test_harness.js'; export class CrossChainMessagingTest { @@ -37,7 +37,7 @@ export class CrossChainMessagingTest { private setupOptions: SetupOptions; private deployL1ContractsArgs: Partial; logger: Logger; - context!: SubsystemsContext; + context!: EndToEndContext; aztecNode!: AztecNode; aztecNodeConfig!: AztecNodeConfig; aztecNodeAdmin!: AztecNodeAdmin; @@ -76,7 +76,12 @@ export class CrossChainMessagingTest { async setup() { this.logger.info('Setting up cross chain messaging test'); - this.context = await setupFromFresh(this.logger, this.setupOptions, this.deployL1ContractsArgs); + this.context = await setup(0, { + ...this.setupOptions, + fundSponsoredFPC: true, + skipAccountDeployment: true, + l1ContractsArgs: this.deployL1ContractsArgs, + }); await this.applyBaseSetup(); } @@ -105,16 +110,16 @@ export class CrossChainMessagingTest { async applyBaseSetup() { // Set up base context fields - this.aztecNode = this.context.aztecNode; + this.aztecNode = this.context.aztecNodeService!; this.wallet = this.context.wallet; - this.aztecNodeConfig = this.context.aztecNodeConfig; + this.aztecNodeConfig = this.context.config; this.cheatCodes = this.context.cheatCodes; this.deployL1ContractsValues = this.context.deployL1ContractsValues; - this.aztecNodeAdmin = this.context.aztecNode; + this.aztecNodeAdmin = this.context.aztecNodeService!; if (this.requireEpochProven) { // Turn off the watcher to prevent it from keep marking blocks as proven. - this.context.watcher.setIsMarkingAsProven(false); + this.context.watcher!.setIsMarkingAsProven(false); } // Deploy 3 accounts diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts index 3e43414192d7..8c9a022eaa8d 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts @@ -168,7 +168,7 @@ describe('e2e_cross_chain_messaging l1_to_l2', () => { // Stop proving const lastProven = await aztecNode.getBlockNumber(); log.warn(`Stopping proof submission at block ${lastProven} to allow drift`); - t.context.watcher.setIsMarkingAsProven(false); + t.context.watcher!.setIsMarkingAsProven(false); // Mine several blocks to ensure drift log.warn(`Mining blocks to allow drift`); @@ -210,14 +210,14 @@ describe('e2e_cross_chain_messaging l1_to_l2', () => { // On private, we simulate the tx locally and check that we get a missing message error, then we advance to the next block await expect(() => consume().simulate({ from: user1Address })).rejects.toThrow(/No L1 to L2 message found/); await tryAdvanceBlock(); - await t.context.watcher.markAsProven(); + await t.context.watcher!.markAsProven(); } else { // On public, we actually send the tx and check that it reverts due to the missing message. // This advances the block too as a side-effect. Note that we do not rely on a simulation since the cross chain messages // do not get added at the beginning of the block during node_simulatePublicCalls (maybe they should?). const { status } = await consume().send({ from: user1Address }).wait({ dontThrowOnRevert: true }); expect(status).toEqual(TxStatus.APP_LOGIC_REVERTED); - await t.context.watcher.markAsProven(); + await t.context.watcher!.markAsProven(); } }); diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_test.ts index 4d84fe1da419..6cc58dff15ba 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_test.ts @@ -10,10 +10,10 @@ import type { StatefulTestContract } from '@aztec/noir-test-contracts.js/Statefu import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client'; import type { TestWallet } from '@aztec/test-wallet/server'; -import { type SubsystemsContext, deployAccounts, setupFromFresh, teardown } from '../fixtures/snapshot_manager.js'; +import { type EndToEndContext, deployAccounts, setup, teardown } from '../fixtures/setup.js'; export class DeployTest { - public context!: SubsystemsContext; + public context!: EndToEndContext; public logger: Logger; public wallet!: TestWallet; public defaultAccountAddress!: AztecAddress; @@ -26,10 +26,13 @@ export class DeployTest { async setup() { this.logger.info('Setting up test environment'); - this.context = await setupFromFresh(this.logger); - this.aztecNode = this.context.aztecNode; + this.context = await setup(0, { + fundSponsoredFPC: true, + skipAccountDeployment: true, + }); + this.aztecNode = this.context.aztecNodeService!; this.wallet = this.context.wallet; - this.aztecNodeAdmin = this.context.aztecNode; + this.aztecNodeAdmin = this.context.aztecNodeService!; await this.applyInitialAccount(); return this; } diff --git a/yarn-project/end-to-end/src/e2e_epochs/epochs_test.ts b/yarn-project/end-to-end/src/e2e_epochs/epochs_test.ts index 11003ad31d60..94febedddcdc 100644 --- a/yarn-project/end-to-end/src/e2e_epochs/epochs_test.ts +++ b/yarn-project/end-to-end/src/e2e_epochs/epochs_test.ts @@ -211,7 +211,7 @@ export class EpochsTestContext { ...opts, }, this.context.aztecNode, - undefined, + this.context.prefilledPublicData ?? [], { dateProvider: this.context.dateProvider }, ), ); diff --git a/yarn-project/end-to-end/src/e2e_fees/failures.test.ts b/yarn-project/end-to-end/src/e2e_fees/failures.test.ts index 7b528577c2d3..1bfb9bedad65 100644 --- a/yarn-project/end-to-end/src/e2e_fees/failures.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/failures.test.ts @@ -34,7 +34,7 @@ describe('e2e_fees failures', () => { // Prove up until the current state by just marking it as proven. // Then turn off the watcher to prevent it from keep proving - await t.context.watcher.trigger(); + await t.context.watcher!.trigger(); await t.cheatCodes.rollup.advanceToNextEpoch(); await t.catchUpProvenChain(); t.setIsMarkingAsProven(false); @@ -78,7 +78,7 @@ describe('e2e_fees failures', () => { await expectMapping(t.getGasBalanceFn, [aliceAddress, bananaFPC.address], [initialAliceGas, initialFPCGas]); // We wait until the proven chain is caught up so all previous fees are paid out. - await t.context.watcher.trigger(); + await t.context.watcher!.trigger(); await t.cheatCodes.rollup.advanceToNextEpoch(); await t.catchUpProvenChain(); @@ -100,7 +100,7 @@ describe('e2e_fees failures', () => { // @note There is a potential race condition here if other tests send transactions that get into the same // epoch and thereby pays out fees at the same time (when proven). - await t.context.watcher.trigger(); + await t.context.watcher!.trigger(); await t.cheatCodes.rollup.advanceToNextEpoch(); await t.catchUpProvenChain(); diff --git a/yarn-project/end-to-end/src/e2e_fees/fees_test.ts b/yarn-project/end-to-end/src/e2e_fees/fees_test.ts index 7896f2dc4bd1..2a4e0d21d4be 100644 --- a/yarn-project/end-to-end/src/e2e_fees/fees_test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/fees_test.ts @@ -26,14 +26,15 @@ import { getContract } from 'viem'; import { MNEMONIC } from '../fixtures/fixtures.js'; import { - type SubsystemsContext, + type EndToEndContext, + type SetupOptions, deployAccounts, publicDeployAccounts, - setupFromFresh, + setup, teardown, -} from '../fixtures/snapshot_manager.js'; +} from '../fixtures/setup.js'; import { mintTokensToPrivate } from '../fixtures/token_utils.js'; -import { type BalancesFn, type SetupOptions, getBalancesFn, setupSponsoredFPC } from '../fixtures/utils.js'; +import { type BalancesFn, getBalancesFn, setupSponsoredFPC } from '../fixtures/utils.js'; import { FeeJuicePortalTestingHarnessFactory, type GasBridgingTestHarness } from '../shared/gas_portal_test_harness.js'; /** @@ -48,7 +49,7 @@ import { FeeJuicePortalTestingHarnessFactory, type GasBridgingTestHarness } from */ export class FeesTest { private accounts: AztecAddress[] = []; - public context!: SubsystemsContext; + public context!: EndToEndContext; public logger: Logger; public aztecNode!: AztecNode; @@ -103,14 +104,16 @@ export class FeesTest { async setup() { this.logger.verbose('Setting up fresh context...'); - this.context = await setupFromFresh( - this.logger, - { startProverNode: true, ...this.setupOptions }, - { ...this.setupOptions }, - ); + this.context = await setup(0, { + startProverNode: true, + ...this.setupOptions, + fundSponsoredFPC: true, + skipAccountDeployment: true, + l1ContractsArgs: { ...this.setupOptions }, + }); - this.rollupContract = RollupContract.getFromConfig(this.context.aztecNodeConfig); - this.chainMonitor = new ChainMonitor(this.rollupContract, this.context.dateProvider, this.logger, 200).start(); + this.rollupContract = RollupContract.getFromConfig(this.context.config); + this.chainMonitor = new ChainMonitor(this.rollupContract, this.context.dateProvider!, this.logger, 200).start(); await this.applyBaseSetup(); @@ -123,7 +126,7 @@ export class FeesTest { } setIsMarkingAsProven(b: boolean) { - this.context.watcher.setIsMarkingAsProven(b); + this.context.watcher!.setIsMarkingAsProven(b); } async catchUpProvenChain() { @@ -188,8 +191,8 @@ export class FeesTest { }); this.wallet = this.context.wallet; - this.aztecNode = this.context.aztecNode; - this.aztecNodeAdmin = this.context.aztecNode; + this.aztecNode = this.context.aztecNodeService!; + this.aztecNodeAdmin = this.context.aztecNodeService!; this.gasSettings = GasSettings.default({ maxFeesPerGas: (await this.aztecNode.getCurrentMinFees()).mul(2) }); this.cheatCodes = this.context.cheatCodes; this.accounts = deployedAccounts.map(a => a.address); @@ -221,8 +224,8 @@ export class FeesTest { ); this.feeJuiceBridgeTestHarness = await FeeJuicePortalTestingHarnessFactory.create({ - aztecNode: this.context.aztecNode, - aztecNodeAdmin: this.context.aztecNode, + aztecNode: this.context.aztecNodeService!, + aztecNodeAdmin: this.context.aztecNodeService!, l1Client: this.context.deployL1ContractsValues.l1Client, wallet: this.wallet, logger: this.logger, @@ -272,7 +275,7 @@ export class FeesTest { const l1FeeJuiceAddress = this.feeJuiceBridgeTestHarness.l1FeeJuiceAddress; this.getCoinbaseBalance = async () => { - const l1Client = createExtendedL1Client(this.context.aztecNodeConfig.l1RpcUrls, MNEMONIC); + const l1Client = createExtendedL1Client(this.context.config.l1RpcUrls, MNEMONIC); const gasL1 = getContract({ address: l1FeeJuiceAddress.toString(), abi: TestERC20Abi, 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 index 3bf2d51101aa..625cea10cc55 100644 --- 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 @@ -6,15 +6,15 @@ import { ChildContract } from '@aztec/noir-test-contracts.js/Child'; import { ParentContract } from '@aztec/noir-test-contracts.js/Parent'; import { - type SubsystemsContext, + type EndToEndContext, deployAccounts, publicDeployAccounts, - setupFromFresh, + setup, teardown as teardownSubsystems, -} from '../fixtures/snapshot_manager.js'; +} from '../fixtures/setup.js'; export class NestedContractTest { - context!: SubsystemsContext; + context!: EndToEndContext; logger: Logger; wallet!: Wallet; defaultAccountAddress!: AztecAddress; @@ -44,7 +44,7 @@ export class NestedContractTest { }); this.wallet = this.context.wallet; [{ address: this.defaultAccountAddress }] = deployedAccounts; - this.aztecNode = this.context.aztecNode; + this.aztecNode = this.context.aztecNodeService!; this.logger.info('Public deploy accounts'); await publicDeployAccounts(this.wallet, [this.defaultAccountAddress]); @@ -52,7 +52,10 @@ export class NestedContractTest { async setup() { this.logger.info('Setting up fresh subsystems'); - this.context = await setupFromFresh(this.logger); + this.context = await setup(0, { + fundSponsoredFPC: true, + skipAccountDeployment: true, + }); await this.applyBaseSetup(); } diff --git a/yarn-project/end-to-end/src/e2e_p2p/add_rollup.test.ts b/yarn-project/end-to-end/src/e2e_p2p/add_rollup.test.ts index bbd9263dc515..62fe39810db0 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/add_rollup.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/add_rollup.test.ts @@ -90,7 +90,7 @@ describe('e2e_p2p_add_rollup', () => { l1TxUtils = createL1TxUtilsFromViemWallet(t.ctx.deployL1ContractsValues.l1Client); - t.ctx.watcher.setIsMarkingAsProven(false); + t.ctx.watcher!.setIsMarkingAsProven(false); }); afterAll(async () => { @@ -233,7 +233,7 @@ describe('e2e_p2p_add_rollup', () => { t.logger.info('Creating nodes'); nodes = await createNodes( { ...t.ctx.aztecNodeConfig, governanceProposerPayload: newPayloadAddress }, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, NUM_VALIDATORS, BOOT_NODE_UDP_PORT, @@ -249,7 +249,7 @@ describe('e2e_p2p_add_rollup', () => { BOOT_NODE_UDP_PORT + NUM_VALIDATORS + 1, t.bootstrapNodeEnr, ATTESTER_PRIVATE_KEYS_START_INDEX + NUM_VALIDATORS + 1, - { dateProvider: t.ctx.dateProvider }, + { dateProvider: t.ctx.dateProvider! }, t.prefilledPublicData, `${DATA_DIR}-prover`, shouldCollectMetrics(), @@ -357,7 +357,7 @@ describe('e2e_p2p_add_rollup', () => { const leafId = getL2ToL1MessageLeafId(l2ToL1MessageResult); // We need to advance to the next epoch so that the out hash will be set to outbox when the epoch is proven. - const cheatcodes = RollupCheatCodes.create(l1RpcUrls, l1ContractAddresses, t.ctx.dateProvider); + const cheatcodes = RollupCheatCodes.create(l1RpcUrls, l1ContractAddresses, t.ctx.dateProvider!); await cheatcodes.advanceToEpoch(EpochNumber(epoch + 1)); await waitForProven(node, l2OutgoingReceipt, { provenTimeout: 300 }); @@ -551,7 +551,7 @@ describe('e2e_p2p_add_rollup', () => { nodes = await createNodes( newConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, NUM_VALIDATORS, BOOT_NODE_UDP_PORT, @@ -566,7 +566,7 @@ describe('e2e_p2p_add_rollup', () => { BOOT_NODE_UDP_PORT + NUM_VALIDATORS + 1, t.bootstrapNodeEnr, ATTESTER_PRIVATE_KEYS_START_INDEX + NUM_VALIDATORS + 1, - { dateProvider: t.ctx.dateProvider }, + { dateProvider: t.ctx.dateProvider! }, prefilledPublicData, `${DATA_DIR_NEW}-prover`, shouldCollectMetrics(), diff --git a/yarn-project/end-to-end/src/e2e_p2p/broadcasted_invalid_block_proposal_slash.test.ts b/yarn-project/end-to-end/src/e2e_p2p/broadcasted_invalid_block_proposal_slash.test.ts index afb44df6ac78..359bacbbfe88 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/broadcasted_invalid_block_proposal_slash.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/broadcasted_invalid_block_proposal_slash.test.ts @@ -115,7 +115,7 @@ describe('e2e_p2p_broadcasted_invalid_block_proposal_slash', () => { }; const invalidProposerNodes = await createNodes( invalidProposerConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, 1, BOOT_NODE_UDP_PORT, @@ -131,7 +131,7 @@ describe('e2e_p2p_broadcasted_invalid_block_proposal_slash', () => { // Create remaining honest nodes const honestNodes = await createNodes( t.ctx.aztecNodeConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, NUM_VALIDATORS - 1, BOOT_NODE_UDP_PORT, diff --git a/yarn-project/end-to-end/src/e2e_p2p/data_withholding_slash.test.ts b/yarn-project/end-to-end/src/e2e_p2p/data_withholding_slash.test.ts index 917dba22df30..371067f8d77d 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/data_withholding_slash.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/data_withholding_slash.test.ts @@ -120,7 +120,7 @@ describe('e2e_p2p_data_withholding_slash', () => { t.logger.warn('Creating nodes'); nodes = await createNodes( t.ctx.aztecNodeConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, NUM_VALIDATORS, BOOT_NODE_UDP_PORT, @@ -165,7 +165,7 @@ describe('e2e_p2p_data_withholding_slash', () => { t.logger.warn('Re-creating nodes'); nodes = await createNodes( t.ctx.aztecNodeConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, NUM_VALIDATORS, BOOT_NODE_UDP_PORT, diff --git a/yarn-project/end-to-end/src/e2e_p2p/gossip_network.test.ts b/yarn-project/end-to-end/src/e2e_p2p/gossip_network.test.ts index 29f7048f8d4e..e2ab6c9b86b6 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/gossip_network.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/gossip_network.test.ts @@ -106,7 +106,7 @@ describe('e2e_p2p_network', () => { t.logger.info('Creating validator nodes'); nodes = await createNodes( t.ctx.aztecNodeConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, NUM_VALIDATORS, BOOT_NODE_UDP_PORT, @@ -123,7 +123,7 @@ describe('e2e_p2p_network', () => { BOOT_NODE_UDP_PORT + NUM_VALIDATORS + 1, t.bootstrapNodeEnr, ATTESTER_PRIVATE_KEYS_START_INDEX + NUM_VALIDATORS + 1, - { dateProvider: t.ctx.dateProvider }, + { dateProvider: t.ctx.dateProvider! }, t.prefilledPublicData, `${DATA_DIR}-prover`, shouldCollectMetrics(), @@ -134,7 +134,7 @@ describe('e2e_p2p_network', () => { const monitoringNodeConfig: AztecNodeConfig = { ...t.ctx.aztecNodeConfig, alwaysReexecuteBlockProposals: true }; monitoringNode = await createNonValidatorNode( monitoringNodeConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, BOOT_NODE_UDP_PORT + NUM_VALIDATORS + 2, t.bootstrapNodeEnr, t.prefilledPublicData, diff --git a/yarn-project/end-to-end/src/e2e_p2p/gossip_network_no_cheat.test.ts b/yarn-project/end-to-end/src/e2e_p2p/gossip_network_no_cheat.test.ts index 30c075dbdc02..8569363d69ba 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/gossip_network_no_cheat.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/gossip_network_no_cheat.test.ts @@ -178,7 +178,7 @@ describe('e2e_p2p_network', () => { // Set the system time in the node, only after we have warped the time and waited for a block // Time is only set in the NEXT block - t.ctx.dateProvider.setTime(Number(timestamp) * 1000); + t.ctx.dateProvider!.setTime(Number(timestamp) * 1000); // create our network of nodes and submit txs into each of them // the number of txs per node and the number of txs per rollup @@ -188,7 +188,7 @@ describe('e2e_p2p_network', () => { t.logger.info('Creating nodes'); nodes = await createNodes( t.ctx.aztecNodeConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, NUM_VALIDATORS, BOOT_NODE_UDP_PORT, diff --git a/yarn-project/end-to-end/src/e2e_p2p/inactivity_slash_test.ts b/yarn-project/end-to-end/src/e2e_p2p/inactivity_slash_test.ts index 59903e4daf20..b5b2da8438e8 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/inactivity_slash_test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/inactivity_slash_test.ts @@ -98,13 +98,13 @@ export class P2PInactivityTest { this.rollup = rollup; if (!this.keepInitialNode) { - await this.test.ctx.aztecNode.stop(); + await this.test.ctx.aztecNodeService!.stop(); } // Create all active nodes this.activeNodes = await createNodes( this.test.ctx.aztecNodeConfig, - this.test.ctx.dateProvider, + this.test.ctx.dateProvider!, this.test.bootstrapNodeEnr, NUM_NODES - this.inactiveNodeCount - Number(this.keepInitialNode), BOOT_NODE_UDP_PORT, @@ -118,7 +118,7 @@ export class P2PInactivityTest { const inactiveConfig = { ...this.test.ctx.aztecNodeConfig, dontStartSequencer: true }; this.inactiveNodes = await createNodes( inactiveConfig, - this.test.ctx.dateProvider, + this.test.ctx.dateProvider!, this.test.bootstrapNodeEnr, this.inactiveNodeCount, BOOT_NODE_UDP_PORT, @@ -129,7 +129,7 @@ export class P2PInactivityTest { ); this.nodes = [ - ...(this.keepInitialNode ? [this.test.ctx.aztecNode] : []), + ...(this.keepInitialNode ? [this.test.ctx.aztecNodeService!] : []), ...this.activeNodes, ...this.inactiveNodes, ]; diff --git a/yarn-project/end-to-end/src/e2e_p2p/mbps_checkpoint_consensus.test.ts b/yarn-project/end-to-end/src/e2e_p2p/mbps_checkpoint_consensus.test.ts index f9f1de9faacf..b3003cf7f447 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/mbps_checkpoint_consensus.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/mbps_checkpoint_consensus.test.ts @@ -98,7 +98,7 @@ describe.skip('e2e_p2p_mbps_checkpoint_consensus', () => { t.logger.info('Creating validator nodes for MBPS consensus'); nodes = await createNodes( t.ctx.aztecNodeConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, NUM_VALIDATORS, BOOT_NODE_UDP_PORT, @@ -206,7 +206,7 @@ describe.skip('e2e_p2p_mbps_checkpoint_consensus', () => { t.logger.info('Creating validator nodes'); nodes = await createNodes( t.ctx.aztecNodeConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, NUM_VALIDATORS, BOOT_NODE_UDP_PORT, @@ -283,7 +283,7 @@ describe.skip('e2e_p2p_mbps_checkpoint_consensus', () => { t.logger.info('Creating validator nodes'); nodes = await createNodes( t.ctx.aztecNodeConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, NUM_VALIDATORS, BOOT_NODE_UDP_PORT, diff --git a/yarn-project/end-to-end/src/e2e_p2p/multiple_validators_sentinel.parallel.test.ts b/yarn-project/end-to-end/src/e2e_p2p/multiple_validators_sentinel.parallel.test.ts index 1d47f45d237f..bdfe87816ae8 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/multiple_validators_sentinel.parallel.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/multiple_validators_sentinel.parallel.test.ts @@ -63,7 +63,7 @@ describe('e2e_p2p_multiple_validators_sentinel', () => { nodes = await createNodes( t.ctx.aztecNodeConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, NUM_NODES, BOOT_NODE_UDP_PORT, @@ -76,7 +76,7 @@ describe('e2e_p2p_multiple_validators_sentinel', () => { sentinel = await createNonValidatorNode( t.ctx.aztecNodeConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, BOOT_NODE_UDP_PORT + 1 + NUM_NODES, t.bootstrapNodeEnr, t.prefilledPublicData, diff --git a/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts b/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts index 31a4629aff4a..bb4a40629e56 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts @@ -34,13 +34,20 @@ import getPort from 'get-port'; import { type GetContractReturnType, getAddress, getContract } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; +import { + type EndToEndContext, + type SetupOptions, + deployAccounts, + getPrivateKeyFromIndex, + getSponsoredFPCAddress, + setup, + teardown, +} from '../fixtures/setup.js'; import { ATTESTER_PRIVATE_KEYS_START_INDEX, createValidatorConfig, generatePrivateKeys, } from '../fixtures/setup_p2p_test.js'; -import { type SubsystemsContext, deployAccounts, setupFromFresh, teardown } from '../fixtures/snapshot_manager.js'; -import { type SetupOptions, getPrivateKeyFromIndex, getSponsoredFPCAddress } from '../fixtures/utils.js'; import { getEndToEndTestTelemetryClient } from '../fixtures/with_telemetry_utils.js'; // Use a fixed bootstrap node private key so that we can re-use the same snapshot and the nodes can find each other @@ -55,14 +62,14 @@ export const SHORTENED_BLOCK_TIME_CONFIG_NO_PRUNES = { }; export class P2PNetworkTest { - public context!: SubsystemsContext; + public context!: EndToEndContext; public baseAccountPrivateKey: `0x${string}`; public baseAccount; public logger: Logger; public monitor!: ChainMonitor; - public ctx!: SubsystemsContext; + public ctx!: EndToEndContext; public attesterPrivateKeys: `0x${string}`[] = []; public attesterPublicKeys: string[] = []; public peerIdPrivateKeys: string[] = []; @@ -196,7 +203,7 @@ export class P2PNetworkTest { BOOTSTRAP_NODE_PRIVATE_KEY, this.bootNodePort, telemetry, - this.context.aztecNodeConfig, + this.context.config, ); // Overwrite enr with updated info this.bootstrapNodeEnr = this.bootstrapNode.getENR().encodeTxt(); @@ -325,9 +332,9 @@ export class P2PNetworkTest { const block = await this.context.deployL1ContractsValues.l1Client.getBlock({ blockNumber: receipt.blockNumber, }); - this.context.dateProvider.setTime(Number(block.timestamp) * 1000); + this.context.dateProvider!.setTime(Number(block.timestamp) * 1000); - await this.context.aztecNode.stop(); + await this.context.aztecNodeService!.stop(); } async sendDummyTx() { @@ -344,7 +351,14 @@ export class P2PNetworkTest { async setup() { this.logger.info('Setting up subsystems from fresh'); - this.context = await setupFromFresh(this.logger, this.setupOptions, this.deployL1ContractsArgs); + this.context = await setup(0, { + ...this.setupOptions, + fundSponsoredFPC: true, + skipAccountDeployment: true, + slasherFlavor: this.setupOptions.slasherFlavor ?? this.deployL1ContractsArgs.slasherFlavor ?? 'none', + aztecTargetCommitteeSize: 0, + l1ContractsArgs: this.deployL1ContractsArgs, + }); this.ctx = this.context; const sponsoredFPCAddress = await getSponsoredFPCAddress(); @@ -354,8 +368,8 @@ export class P2PNetworkTest { this.prefilledPublicData = prefilledPublicData; const rollupContract = RollupContract.getFromL1ContractsValues(this.context.deployL1ContractsValues); - this.monitor = new ChainMonitor(rollupContract, this.context.dateProvider).start(); - this.monitor.on('l1-block', ({ timestamp }) => this.context.dateProvider.setTime(Number(timestamp) * 1000)); + this.monitor = new ChainMonitor(rollupContract, this.context.dateProvider!).start(); + this.monitor.on('l1-block', ({ timestamp }) => this.context.dateProvider!.setTime(Number(timestamp) * 1000)); } async stopNodes(nodes: AztecNodeService[]) { diff --git a/yarn-project/end-to-end/src/e2e_p2p/preferred_gossip_network.test.ts b/yarn-project/end-to-end/src/e2e_p2p/preferred_gossip_network.test.ts index a97f58400f49..98ce0107a7c3 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/preferred_gossip_network.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/preferred_gossip_network.test.ts @@ -146,7 +146,7 @@ describe('e2e_p2p_preferred_network', () => { }); afterEach(async () => { - await t.stopNodes([t.ctx.aztecNode].concat(nodes).concat(validators).concat(preferredNodes)); + await t.stopNodes([t.ctx.aztecNodeService!].concat(nodes).concat(validators).concat(preferredNodes)); await t.teardown(); for (let i = 0; i < NUM_NODES + NUM_VALIDATORS + NUM_PREFERRED_NODES; i++) { fs.rmSync(`${DATA_DIR}-${i}`, { recursive: true, force: true, maxRetries: 3 }); @@ -187,7 +187,7 @@ describe('e2e_p2p_preferred_network', () => { preferredNodes = await createNodes( preferredNodeConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, NUM_PREFERRED_NODES, BOOT_NODE_UDP_PORT, @@ -221,7 +221,7 @@ describe('e2e_p2p_preferred_network', () => { t.logger.info('Creating nodes'); nodes = await createNodes( nodeConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, NUM_NODES, BOOT_NODE_UDP_PORT, @@ -244,7 +244,7 @@ describe('e2e_p2p_preferred_network', () => { validators = await createNodes( validatorConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, NUM_VALIDATORS - 1, BOOT_NODE_UDP_PORT, @@ -268,7 +268,7 @@ describe('e2e_p2p_preferred_network', () => { const noDiscoveryValidators = await createNodes( lastValidatorConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, 1, BOOT_NODE_UDP_PORT, @@ -279,7 +279,7 @@ describe('e2e_p2p_preferred_network', () => { indexOffset, ); - const allNodes = [...nodes, ...preferredNodes, ...validators, ...noDiscoveryValidators, t.ctx.aztecNode]; + const allNodes = [...nodes, ...preferredNodes, ...validators, ...noDiscoveryValidators, t.ctx.aztecNodeService!]; const identifiers = nodes .map((_, i) => `Node ${i + 1}`) .concat(preferredNodes.map((_, i) => `Preferred Node ${i + 1}`)) diff --git a/yarn-project/end-to-end/src/e2e_p2p/rediscovery.test.ts b/yarn-project/end-to-end/src/e2e_p2p/rediscovery.test.ts index 967074be35af..916c4f331253 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/rediscovery.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/rediscovery.test.ts @@ -52,7 +52,7 @@ describe('e2e_p2p_rediscovery', () => { const txsSentViaDifferentNodes: SentTx[][] = []; nodes = await createNodes( t.ctx.aztecNodeConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, NUM_VALIDATORS, BOOT_NODE_UDP_PORT, @@ -85,7 +85,7 @@ describe('e2e_p2p_rediscovery', () => { const newNode = await createNode( t.ctx.aztecNodeConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, i + 1 + BOOT_NODE_UDP_PORT, undefined, i, diff --git a/yarn-project/end-to-end/src/e2e_p2p/reex.test.ts b/yarn-project/end-to-end/src/e2e_p2p/reex.test.ts index 181c0a8fd36b..9277e9b53265 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/reex.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/reex.test.ts @@ -57,7 +57,7 @@ describe('e2e_p2p_reex', () => { await t.applyBaseSetup(); t.logger.info('Stopping main node sequencer'); - await t.ctx.aztecNode.getSequencer()?.stop(); + await t.ctx.aztecNodeService!.getSequencer()?.stop(); if (!t.bootstrapNodeEnr) { throw new Error('Bootstrap node ENR is not available'); @@ -71,7 +71,7 @@ describe('e2e_p2p_reex', () => { minTxsPerBlock: 1, maxTxsPerBlock: NUM_TXS_PER_NODE, }, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, NUM_VALIDATORS, BASE_BOOT_NODE_UDP_PORT, @@ -233,7 +233,7 @@ describe('e2e_p2p_reex', () => { // Start a fresh slot and resume proposals const [ts] = await t.ctx.cheatCodes.rollup.advanceToNextSlot(); - t.ctx.dateProvider.setTime(Number(ts) * 1000); + t.ctx.dateProvider!.setTime(Number(ts) * 1000); await resumeProposals(); diff --git a/yarn-project/end-to-end/src/e2e_p2p/reqresp.test.ts b/yarn-project/end-to-end/src/e2e_p2p/reqresp.test.ts index ee28774e6992..bb7a311effff 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/reqresp.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/reqresp.test.ts @@ -76,7 +76,7 @@ describe('e2e_p2p_reqresp_tx', () => { t.logger.info('Creating nodes'); nodes = await createNodes( t.ctx.aztecNodeConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, NUM_VALIDATORS, BOOT_NODE_UDP_PORT, @@ -92,7 +92,7 @@ describe('e2e_p2p_reqresp_tx', () => { t.logger.info('Preparing transactions to send'); const txss = await timesAsync(2, () => - prepareTransactions(t.logger, t.ctx.aztecNode, NUM_TXS_PER_NODE, t.fundedAccount), + prepareTransactions(t.logger, t.ctx.aztecNodeService!, NUM_TXS_PER_NODE, t.fundedAccount), ); t.logger.info('Removing initial node'); @@ -100,7 +100,7 @@ describe('e2e_p2p_reqresp_tx', () => { t.logger.info('Starting fresh slot'); const [timestamp] = await t.ctx.cheatCodes.rollup.advanceToNextSlot(); - t.ctx.dateProvider.setTime(Number(timestamp) * 1000); + t.ctx.dateProvider!.setTime(Number(timestamp) * 1000); const { proposerIndexes, nodesToTurnOffTxGossip } = await getProposerIndexes(); t.logger.info(`Turning off tx gossip for nodes: ${nodesToTurnOffTxGossip.map(getNodePort)}`); diff --git a/yarn-project/end-to-end/src/e2e_p2p/reqresp_no_handshake.test.ts b/yarn-project/end-to-end/src/e2e_p2p/reqresp_no_handshake.test.ts index 701ad44054b7..187172490d06 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/reqresp_no_handshake.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/reqresp_no_handshake.test.ts @@ -77,7 +77,7 @@ describe('e2e_p2p_reqresp_tx_no_handshake', () => { t.logger.info('Creating nodes'); nodes = await createNodes( { ...t.ctx.aztecNodeConfig, p2pDisableStatusHandshake: true }, // DIFFERENCE FROM reqresp.test.ts - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, NUM_VALIDATORS, BOOT_NODE_UDP_PORT, @@ -97,7 +97,7 @@ describe('e2e_p2p_reqresp_tx_no_handshake', () => { t.logger.info('Preparing transactions to send'); const txss = await timesAsync(2, () => - prepareTransactions(t.logger, t.ctx.aztecNode, NUM_TXS_PER_NODE, t.fundedAccount), + prepareTransactions(t.logger, t.ctx.aztecNodeService!, NUM_TXS_PER_NODE, t.fundedAccount), ); t.logger.info('Removing initial node'); @@ -105,7 +105,7 @@ describe('e2e_p2p_reqresp_tx_no_handshake', () => { t.logger.info('Starting fresh slot'); const [timestamp] = await t.ctx.cheatCodes.rollup.advanceToNextSlot(); - t.ctx.dateProvider.setTime(Number(timestamp) * 1000); + t.ctx.dateProvider!.setTime(Number(timestamp) * 1000); const { proposerIndexes, nodesToTurnOffTxGossip } = await getProposerIndexes(); t.logger.info(`Turning off tx gossip for nodes: ${nodesToTurnOffTxGossip.map(getNodePort)}`); diff --git a/yarn-project/end-to-end/src/e2e_p2p/slash_veto_demo.test.ts b/yarn-project/end-to-end/src/e2e_p2p/slash_veto_demo.test.ts index f7cd63e5bfc0..452d20d10610 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/slash_veto_demo.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/slash_veto_demo.test.ts @@ -102,7 +102,7 @@ describe('veto slash', () => { nodes = await createNodes( t.ctx.aztecNodeConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, NUM_NODES, // Note we do not create the last validator yet, so it shows as offline BOOT_NODE_UDP_PORT, @@ -117,7 +117,7 @@ describe('veto slash', () => { ); vetoerL1TxUtils = createL1TxUtilsFromViemWallet(vetoerL1Client, { logger: t.logger, - dateProvider: t.ctx.dateProvider, + dateProvider: t.ctx.dateProvider!, }); ({ rollup } = await t.getContracts()); @@ -201,7 +201,7 @@ describe('veto slash', () => { debugLogger.info(`\n\ninitializing slasher with proposer: ${proposer}\n\n`); const txUtils = createL1TxUtilsFromViemWallet(deployerClient, { logger: t.logger, - dateProvider: t.ctx.dateProvider, + dateProvider: t.ctx.dateProvider!, }); await txUtils.sendAndMonitorTransaction({ to: slasher.toString(), diff --git a/yarn-project/end-to-end/src/e2e_p2p/upgrade_governance_proposer.test.ts b/yarn-project/end-to-end/src/e2e_p2p/upgrade_governance_proposer.test.ts index 0eed2f499a95..505de78b8068 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/upgrade_governance_proposer.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/upgrade_governance_proposer.test.ts @@ -148,7 +148,7 @@ describe('e2e_p2p_governance_proposer', () => { t.logger.info('Creating nodes'); nodes = await createNodes( { ...t.ctx.aztecNodeConfig, governanceProposerPayload: newPayloadAddress }, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, NUM_VALIDATORS, BOOT_NODE_UDP_PORT, diff --git a/yarn-project/end-to-end/src/e2e_p2p/valid_epoch_pruned_slash.test.ts b/yarn-project/end-to-end/src/e2e_p2p/valid_epoch_pruned_slash.test.ts index a7ec2a05cd18..b4c9f345df09 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/valid_epoch_pruned_slash.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/valid_epoch_pruned_slash.test.ts @@ -110,7 +110,7 @@ describe('e2e_p2p_valid_epoch_pruned_slash', () => { t.logger.warn(`Creating ${NUM_VALIDATORS} new nodes`); nodes = await createNodes( t.ctx.aztecNodeConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, NUM_VALIDATORS, BOOT_NODE_UDP_PORT, diff --git a/yarn-project/end-to-end/src/e2e_p2p/validators_sentinel.test.ts b/yarn-project/end-to-end/src/e2e_p2p/validators_sentinel.test.ts index 0b62ad197753..0d1a43d0564b 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/validators_sentinel.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/validators_sentinel.test.ts @@ -58,7 +58,7 @@ describe('e2e_p2p_validators_sentinel', () => { nodes = await createNodes( t.ctx.aztecNodeConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, t.bootstrapNodeEnr, NUM_NODES, // Note we do not create the last validator yet, so it shows as offline BOOT_NODE_UDP_PORT, @@ -158,7 +158,7 @@ describe('e2e_p2p_validators_sentinel', () => { const nodeIndex = NUM_NODES + 1; const newNode = await createNode( t.ctx.aztecNodeConfig, - t.ctx.dateProvider, + t.ctx.dateProvider!, BOOT_NODE_UDP_PORT + nodeIndex + 1, t.bootstrapNodeEnr!, nodeIndex, diff --git a/yarn-project/end-to-end/src/e2e_snapshot_sync.test.ts b/yarn-project/end-to-end/src/e2e_snapshot_sync.test.ts index cfc17ffbdefe..42c51208b617 100644 --- a/yarn-project/end-to-end/src/e2e_snapshot_sync.test.ts +++ b/yarn-project/end-to-end/src/e2e_snapshot_sync.test.ts @@ -75,6 +75,7 @@ describe('e2e_snapshot_sync', () => { context.config, { ...config, realProofs: false, dataDirectory }, context.aztecNode, + context.prefilledPublicData ?? [], ); }; diff --git a/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts b/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts index 63573c95baf4..cc8c3d544ec6 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts @@ -7,13 +7,7 @@ import type { TestWallet } from '@aztec/test-wallet/server'; import { jest } from '@jest/globals'; -import { - type SubsystemsContext, - deployAccounts, - publicDeployAccounts, - setupFromFresh, - teardown, -} from '../fixtures/snapshot_manager.js'; +import { type EndToEndContext, deployAccounts, publicDeployAccounts, setup, teardown } from '../fixtures/setup.js'; import { mintTokensToPrivate } from '../fixtures/token_utils.js'; import { TokenSimulator } from '../simulators/token_simulator.js'; @@ -23,7 +17,7 @@ export class TokenContractTest { static TOKEN_NAME = 'USDC'; static TOKEN_SYMBOL = 'USD'; static TOKEN_DECIMALS = 18n; - context!: SubsystemsContext; + context!: EndToEndContext; logger: Logger; metricsPort?: number; asset!: TokenContract; @@ -76,7 +70,7 @@ export class TokenContractTest { initialFundedAccounts: this.context.initialFundedAccounts, }); - this.node = this.context.aztecNode; + this.node = this.context.aztecNodeService!; this.wallet = this.context.wallet; [this.adminAddress, this.account1Address, this.account2Address] = deployedAccounts.map(acc => acc.address); @@ -111,8 +105,10 @@ export class TokenContractTest { } async setup() { - this.context = await setupFromFresh(this.logger, { + this.context = await setup(0, { metricsPort: this.metricsPort, + fundSponsoredFPC: true, + skipAccountDeployment: true, }); if (this.shouldApplyBaseSetup) { diff --git a/yarn-project/end-to-end/src/fixtures/e2e_prover_test.ts b/yarn-project/end-to-end/src/fixtures/e2e_prover_test.ts index 853f05f59677..a314df4421d9 100644 --- a/yarn-project/end-to-end/src/fixtures/e2e_prover_test.ts +++ b/yarn-project/end-to-end/src/fixtures/e2e_prover_test.ts @@ -30,13 +30,15 @@ import { TokenSimulator } from '../simulators/token_simulator.js'; import { getACVMConfig } from './get_acvm_config.js'; import { getBBConfig } from './get_bb_config.js'; import { - type SubsystemsContext, + type EndToEndContext, deployAccounts, + getPrivateKeyFromIndex, + getSponsoredFPCAddress, publicDeployAccounts, - setupFromFresh, + setup, + setupPXEAndGetWallet, teardown, -} from './snapshot_manager.js'; -import { getPrivateKeyFromIndex, getSponsoredFPCAddress, setupPXEAndGetWallet } from './utils.js'; +} from './setup.js'; type ProvenSetup = { wallet: TestWallet; @@ -70,7 +72,7 @@ export class FullProverTest { private acvmConfigCleanup?: () => Promise; circuitProofVerifier?: ClientProtocolCircuitVerifier; provenAsset!: TokenContract; - context!: SubsystemsContext; + context!: EndToEndContext; private proverNode!: ProverNode; private simulatedProverNode!: ProverNode; public l1Contracts!: DeployAztecL1ContractsReturnType; @@ -130,11 +132,13 @@ export class FullProverTest { async setup() { this.logger.info('Setting up subsystems from fresh'); - this.context = await setupFromFresh( - this.logger, - { startProverNode: true, coinbase: this.coinbase }, - { realVerifier: this.realProofs }, - ); + this.context = await setup(0, { + startProverNode: true, + coinbase: this.coinbase, + fundSponsoredFPC: true, + skipAccountDeployment: true, + l1ContractsArgs: { realVerifier: this.realProofs }, + }); await this.applyBaseSetup(); await this.applyMint(); @@ -142,7 +146,7 @@ export class FullProverTest { this.logger.info(`Enabling proving`, { realProofs: this.realProofs }); // We don't wish to mark as proven automatically, so we set the flag to false - this.context.watcher.setIsMarkingAsProven(false); + this.context.watcher!.setIsMarkingAsProven(false); this.simulatedProverNode = this.context.proverNode!; ({ @@ -150,7 +154,7 @@ export class FullProverTest { deployL1ContractsValues: this.l1Contracts, cheatCodes: this.cheatCodes, } = this.context); - this.aztecNodeAdmin = this.context.aztecNode; + this.aztecNodeAdmin = this.context.aztecNodeService!; const config = this.context.aztecNodeConfig; const blobClient = await createBlobClientWithFileStores(config, this.logger); @@ -223,7 +227,7 @@ export class FullProverTest { this.logger.verbose('Starting archiver for new prover node'); const archiver = await createArchiver( { ...this.context.aztecNodeConfig, dataDirectory: undefined }, - { blobClient, dateProvider: this.context.dateProvider }, + { blobClient, dateProvider: this.context.dateProvider! }, { blockUntilSync: true }, ); diff --git a/yarn-project/end-to-end/src/fixtures/setup.ts b/yarn-project/end-to-end/src/fixtures/setup.ts new file mode 100644 index 000000000000..35faf15453ca --- /dev/null +++ b/yarn-project/end-to-end/src/fixtures/setup.ts @@ -0,0 +1,1009 @@ +import { SchnorrAccountContractArtifact } from '@aztec/accounts/schnorr'; +import { type InitialAccountData, generateSchnorrAccounts, getInitialTestAccountsData } from '@aztec/accounts/testing'; +import { type Archiver, createArchiver } from '@aztec/archiver'; +import { type AztecNodeConfig, AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node'; +import { AztecAddress, EthAddress } from '@aztec/aztec.js/addresses'; +import { + BatchCall, + type ContractFunctionInteraction, + type ContractMethod, + getContractClassFromArtifact, + waitForProven, +} from '@aztec/aztec.js/contracts'; +import { publishContractClass, publishInstance } from '@aztec/aztec.js/deployment'; +import { Fr } from '@aztec/aztec.js/fields'; +import { type Logger, createLogger } from '@aztec/aztec.js/log'; +import { type AztecNode, createAztecNodeClient, waitForNode } from '@aztec/aztec.js/node'; +import type { Wallet } from '@aztec/aztec.js/wallet'; +import { AnvilTestWatcher, CheatCodes } from '@aztec/aztec/testing'; +import { createBlobClientWithFileStores } from '@aztec/blob-client/client'; +import { SPONSORED_FPC_SALT } from '@aztec/constants'; +import { isAnvilTestChain } from '@aztec/ethereum/chain'; +import { createExtendedL1Client } from '@aztec/ethereum/client'; +import { getL1ContractsConfigEnvVars } from '@aztec/ethereum/config'; +import { NULL_KEY } from '@aztec/ethereum/constants'; +import { deployMulticall3 } from '@aztec/ethereum/contracts'; +import { + type DeployAztecL1ContractsArgs, + type DeployAztecL1ContractsReturnType, + type Operator, + type ZKPassportArgs, + deployAztecL1Contracts, +} from '@aztec/ethereum/deploy-aztec-l1-contracts'; +import { + DelayedTxUtils, + EthCheatCodes, + EthCheatCodesWithState, + createDelayedL1TxUtilsFromViemWallet, + startAnvil, +} from '@aztec/ethereum/test'; +import { BlockNumber, EpochNumber } from '@aztec/foundation/branded-types'; +import { SecretValue } from '@aztec/foundation/config'; +import { randomBytes } from '@aztec/foundation/crypto/random'; +import { tryRmDir } from '@aztec/foundation/fs'; +import { withLogNameSuffix } from '@aztec/foundation/log'; +import { retryUntil } from '@aztec/foundation/retry'; +import { sleep } from '@aztec/foundation/sleep'; +import { DateProvider, TestDateProvider } from '@aztec/foundation/timer'; +import type { DataStoreConfig } from '@aztec/kv-store/config'; +import { SponsoredFPCContract } from '@aztec/noir-contracts.js/SponsoredFPC'; +import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree'; +import type { P2PClientDeps } from '@aztec/p2p'; +import { MockGossipSubNetwork, getMockPubSubP2PServiceFactory } from '@aztec/p2p/test-helpers'; +import { protocolContractsHash } from '@aztec/protocol-contracts'; +import { type ProverNode, type ProverNodeConfig, type ProverNodeDeps, createProverNode } from '@aztec/prover-node'; +import { type PXEConfig, getPXEConfig } from '@aztec/pxe/server'; +import type { SequencerClient } from '@aztec/sequencer-client'; +import type { TestSequencerClient } from '@aztec/sequencer-client/test'; +import { type ContractInstanceWithAddress, getContractInstanceFromInstantiationParams } from '@aztec/stdlib/contract'; +import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client'; +import { tryStop } from '@aztec/stdlib/interfaces/server'; +import type { P2PClientType } from '@aztec/stdlib/p2p'; +import type { PublicDataTreeLeaf } from '@aztec/stdlib/trees'; +import { + type TelemetryClient, + type TelemetryClientConfig, + getConfigEnvVars as getTelemetryConfig, + initTelemetryClient, +} from '@aztec/telemetry-client'; +import { BenchmarkTelemetryClient } from '@aztec/telemetry-client/bench'; +import { TestWallet, deployFundedSchnorrAccounts } from '@aztec/test-wallet/server'; +import { getGenesisValues } from '@aztec/world-state/testing'; + +import type { Anvil } from '@viem/anvil'; +import fs from 'fs/promises'; +import { tmpdir } from 'os'; +import path from 'path'; +import type { Hex } from 'viem'; +import { + type HDAccount, + type PrivateKeyAccount, + generatePrivateKey, + mnemonicToAccount, + privateKeyToAccount, +} from 'viem/accounts'; +import { type Chain, foundry } from 'viem/chains'; + +import { MNEMONIC, TEST_MAX_PENDING_TX_POOL_COUNT, TEST_PEER_CHECK_INTERVAL_MS } from './fixtures.js'; +import { getACVMConfig } from './get_acvm_config.js'; +import { getBBConfig } from './get_bb_config.js'; +import { isMetricsLoggingRequested, setupMetricsLogger } from './logging.js'; +import { getEndToEndTestTelemetryClient } from './with_telemetry_utils.js'; + +export { startAnvil }; + +const { AZTEC_NODE_URL = '' } = process.env; +const getAztecUrl = () => AZTEC_NODE_URL; + +let telemetry: TelemetryClient | undefined = undefined; +async function getTelemetryClient(partialConfig: Partial & { benchmark?: boolean } = {}) { + if (!telemetry) { + const config = { ...getTelemetryConfig(), ...partialConfig }; + telemetry = config.benchmark ? new BenchmarkTelemetryClient() : await initTelemetryClient(config); + } + return telemetry; +} +if (typeof afterAll === 'function') { + afterAll(async () => { + await telemetry?.stop(); + }); +} + +export const getPrivateKeyFromIndex = (index: number): Buffer | null => { + const hdAccount = mnemonicToAccount(MNEMONIC, { addressIndex: index }); + const privKeyRaw = hdAccount.getHdKey().privateKey; + return privKeyRaw === null ? null : Buffer.from(privKeyRaw); +}; + +/** + * Sets up shared blob storage using FileStore in the data directory. + */ +export async function setupSharedBlobStorage(config: { dataDirectory?: string } & Record): Promise { + const sharedBlobPath = path.join(config.dataDirectory!, 'shared-blobs'); + await fs.mkdir(sharedBlobPath, { recursive: true }); + config.blobFileStoreUrls = [`file://${sharedBlobPath}`]; + config.blobFileStoreUploadUrl = `file://${sharedBlobPath}`; +} + +/** + * Sets up Private eXecution Environment (PXE) and returns the corresponding test wallet. + * @param aztecNode - An instance of Aztec Node. + * @param opts - Partial configuration for the PXE. + * @param logger - The logger to be used. + * @param useLogSuffix - Whether to add a randomly generated suffix to the PXE debug logs. + * @returns A test wallet, logger and teardown function. + */ +export async function setupPXEAndGetWallet( + aztecNode: AztecNode, + opts: Partial = {}, + logger = getLogger(), + useLogSuffix = false, +): Promise<{ + wallet: TestWallet; + logger: Logger; + teardown: () => Promise; +}> { + const PXEConfig = { ...getPXEConfig(), ...opts }; + // For tests we only want proving enabled if specifically requested + PXEConfig.proverEnabled = !!opts.proverEnabled; + + // If no data directory provided, create a temp directory and clean up afterwards + const configuredDataDirectory = PXEConfig.dataDirectory; + if (!configuredDataDirectory) { + PXEConfig.dataDirectory = path.join(tmpdir(), randomBytes(8).toString('hex')); + } + + const teardown = configuredDataDirectory ? () => Promise.resolve() : () => tryRmDir(PXEConfig.dataDirectory!); + + const wallet = await TestWallet.create(aztecNode, PXEConfig, { + useLogSuffix, + }); + + return { + wallet, + logger, + teardown, + }; +} + +/** Options for the e2e tests setup */ +export type SetupOptions = { + /** State load */ + stateLoad?: string; + /** Whether to enable metrics collection, if undefined, metrics collection is disabled */ + metricsPort?: number | undefined; + /** Previously deployed contracts on L1 */ + deployL1ContractsValues?: DeployAztecL1ContractsReturnType; + /** Initial fee juice for default accounts */ + initialAccountFeeJuice?: Fr; + /** Number of initial accounts funded with fee juice */ + numberOfInitialFundedAccounts?: number; + /** Data of the initial funded accounts */ + initialFundedAccounts?: InitialAccountData[]; + /** An initial set of validators */ + initialValidators?: (Operator & { privateKey: `0x${string}` })[]; + /** Anvil Start time */ + l1StartTime?: number; + /** The anvil time where we should at the earliest be seeing L2 blocks */ + l2StartTime?: number; + /** Whether to start a prover node */ + startProverNode?: boolean; + /** Manual config for the telemetry client */ + telemetryConfig?: Partial & { benchmark?: boolean }; + /** Public data that will be inserted in the tree in genesis */ + genesisPublicData?: PublicDataTreeLeaf[]; + /** Specific config for the prover node, if set. */ + proverNodeConfig?: Partial; + /** Whether to use a mock gossip sub network for p2p clients. */ + mockGossipSubNetwork?: boolean; + /** Whether to disable the anvil test watcher (can still be manually started) */ + disableAnvilTestWatcher?: boolean; + /** Whether to enable anvil automine during deployment of L1 contracts (consider defaulting this to true). */ + automineL1Setup?: boolean; + /** How many accounts to seed and unlock in anvil. */ + anvilAccounts?: number; + /** Port to start anvil (defaults to 8545) */ + anvilPort?: number; + /** Key to use for publishing L1 contracts */ + l1PublisherKey?: SecretValue<`0x${string}`>; + /** ZkPassport configuration (domain, scope, mock verifier) */ + zkPassportArgs?: ZKPassportArgs; + /** Whether to fund the sponsored FPC in genesis (defaults to false). */ + fundSponsoredFPC?: boolean; + /** Whether to skip deploying accounts during setup (legacy behavior for tests using deployAccounts helper). */ + skipAccountDeployment?: boolean; + /** L1 contracts deployment arguments. */ + l1ContractsArgs?: Partial; +} & Partial; + +/** Context for an end-to-end test as returned by the `setup` function */ +export type EndToEndContext = { + /** The Anvil instance (only set if anvil was started locally). */ + anvil: Anvil | undefined; + /** The Aztec Node service or client a connected to it. */ + aztecNode: AztecNode; + /** The Aztec Node as a service (only set if running locally). */ + aztecNodeService: AztecNodeService | undefined; + /** Client to the Aztec Node admin interface (undefined if connected to remote environment) */ + aztecNodeAdmin: AztecNodeAdmin | undefined; + /** The prover node service (only set if startProverNode is true) */ + proverNode: ProverNode | undefined; + /** A client to the sequencer service (undefined if connected to remote environment) */ + sequencer: SequencerClient | undefined; + /** Return values from deployAztecL1Contracts function. */ + deployL1ContractsValues: DeployAztecL1ContractsReturnType; + /** The Aztec Node configuration. */ + config: AztecNodeConfig; + /** The Aztec Node configuration (alias for config for backward compatibility). */ + aztecNodeConfig: AztecNodeConfig; + /** The data for the initial funded accounts. */ + initialFundedAccounts: InitialAccountData[]; + /** The wallet to be used. */ + wallet: TestWallet; + /** The wallets to be used. */ + accounts: AztecAddress[]; + /** Logger instance named as the current test. */ + logger: Logger; + /** The cheat codes. */ + cheatCodes: CheatCodes; + /** The cheat codes for L1 */ + ethCheatCodes: EthCheatCodes; + /** The anvil test watcher (undefined if connected to remote environment) */ + watcher: AnvilTestWatcher | undefined; + /** Allows tweaking current system time, used by the epoch cache only (undefined if connected to remote environment) */ + dateProvider: TestDateProvider | undefined; + /** Telemetry client */ + telemetryClient: TelemetryClient | undefined; + /** Mock gossip sub network used for gossipping messages (only if mockGossipSubNetwork was set to true in opts) */ + mockGossipSubNetwork: MockGossipSubNetwork | undefined; + /** Prefilled public data used for setting up nodes. */ + prefilledPublicData: PublicDataTreeLeaf[] | undefined; + /** ACVM config (only set if running locally). */ + acvmConfig: Awaited>; + /** BB config (only set if running locally). */ + bbConfig: Awaited>; + /** Directory to cleanup on teardown. */ + directoryToCleanup: string | undefined; + /** Function to stop the started services. */ + teardown: () => Promise; +}; + +/** + * Function to setup the test against a remote deployment. It is assumed that L1 contract are already deployed + */ +async function setupWithRemoteEnvironment( + account: HDAccount | PrivateKeyAccount, + config: AztecNodeConfig, + logger: Logger, + numberOfAccounts: number, +): Promise { + const aztecNodeUrl = getAztecUrl(); + logger.verbose(`Creating Aztec Node client to remote host ${aztecNodeUrl}`); + const aztecNode = createAztecNodeClient(aztecNodeUrl); + await waitForNode(aztecNode, logger); + logger.verbose('JSON RPC client connected to Aztec Node'); + logger.verbose(`Retrieving contract addresses from ${aztecNodeUrl}`); + const { l1ContractAddresses, rollupVersion } = await aztecNode.getNodeInfo(); + + const l1Client = createExtendedL1Client(config.l1RpcUrls, account, foundry); + + const deployL1ContractsValues: DeployAztecL1ContractsReturnType = { + l1ContractAddresses, + l1Client, + rollupVersion, + }; + const ethCheatCodes = new EthCheatCodes(config.l1RpcUrls, new DateProvider()); + const wallet = await TestWallet.create(aztecNode); + const cheatCodes = await CheatCodes.create(config.l1RpcUrls, aztecNode, new DateProvider()); + const teardown = () => Promise.resolve(); + + logger.verbose('Populating wallet from already registered accounts...'); + const initialFundedAccounts = await getInitialTestAccountsData(); + + if (initialFundedAccounts.length < numberOfAccounts) { + throw new Error(`Required ${numberOfAccounts} accounts. Found ${initialFundedAccounts.length}.`); + } + + const testAccounts = await Promise.all( + initialFundedAccounts.slice(0, numberOfAccounts).map(async account => { + const accountManager = await wallet.createSchnorrAccount(account.secret, account.salt, account.signingKey); + return accountManager.address; + }), + ); + + return { + anvil: undefined, + aztecNode, + aztecNodeService: undefined, + aztecNodeAdmin: undefined, + sequencer: undefined, + proverNode: undefined, + deployL1ContractsValues, + config, + aztecNodeConfig: config, + initialFundedAccounts, + wallet, + accounts: testAccounts, + logger, + cheatCodes, + ethCheatCodes, + prefilledPublicData: undefined, + mockGossipSubNetwork: undefined, + watcher: undefined, + dateProvider: undefined, + telemetryClient: undefined, + acvmConfig: undefined, + bbConfig: undefined, + directoryToCleanup: undefined, + teardown, + }; +} + +/** + * Sets up the environment for the end-to-end tests. + * @param numberOfAccounts - The number of new accounts to be created once the PXE is initiated. + * @param opts - Options to pass to the node initialization and to the setup script. + * @param pxeOpts - Options to pass to the PXE initialization. + */ +export async function setup( + numberOfAccounts = 1, + opts: SetupOptions = {}, + pxeOpts: Partial = {}, + chain: Chain = foundry, +): Promise { + let anvil: Anvil | undefined; + try { + opts.aztecTargetCommitteeSize ??= 0; + opts.slasherFlavor ??= 'none'; + + const config: AztecNodeConfig & SetupOptions = { ...getConfigEnvVars(), ...opts }; + // use initialValidators for the node config + config.validatorPrivateKeys = new SecretValue(opts.initialValidators?.map(v => v.privateKey) ?? []); + + config.peerCheckIntervalMS = TEST_PEER_CHECK_INTERVAL_MS; + config.maxPendingTxCount = opts.maxPendingTxCount ?? TEST_MAX_PENDING_TX_POOL_COUNT; + // For tests we only want proving enabled if specifically requested + config.realProofs = !!opts.realProofs; + // Only enforce the time table if requested + config.enforceTimeTable = !!opts.enforceTimeTable; + config.listenAddress = '127.0.0.1'; + + const logger = getLogger(); + + // Create a temp directory for any services that need it and cleanup later + const directoryToCleanup = path.join(tmpdir(), randomBytes(8).toString('hex')); + await fs.mkdir(directoryToCleanup, { recursive: true }); + if (!config.dataDirectory) { + config.dataDirectory = directoryToCleanup; + } + + if (!config.l1RpcUrls?.length) { + if (!isAnvilTestChain(chain.id)) { + throw new Error(`No ETHEREUM_HOSTS set but non anvil chain requested`); + } + if (AZTEC_NODE_URL) { + throw new Error( + `AZTEC_NODE_URL provided but no ETHEREUM_HOSTS set. Refusing to run, please set both variables so tests can deploy L1 contracts to the same Anvil instance`, + ); + } + + const res = await startAnvil({ + l1BlockTime: opts.ethereumSlotDuration, + accounts: opts.anvilAccounts, + port: opts.anvilPort, + }); + anvil = res.anvil; + config.l1RpcUrls = [res.rpcUrl]; + } + + // Enable logging metrics to a local file named after the test suite + if (isMetricsLoggingRequested()) { + const filename = path.join('log', getJobName() + '.jsonl'); + logger.info(`Logging metrics to ${filename}`); + setupMetricsLogger(filename); + } + + const dateProvider = new TestDateProvider(); + const ethCheatCodes = new EthCheatCodesWithState(config.l1RpcUrls, dateProvider); + + if (opts.stateLoad) { + await ethCheatCodes.loadChainState(opts.stateLoad); + } + + if (opts.l1StartTime) { + await ethCheatCodes.warp(opts.l1StartTime, { resetBlockInterval: true }); + } + + let publisherPrivKeyHex: `0x${string}` | undefined = undefined; + let publisherHdAccount: HDAccount | PrivateKeyAccount | undefined = undefined; + + if (opts.l1PublisherKey && opts.l1PublisherKey.getValue() && opts.l1PublisherKey.getValue() != NULL_KEY) { + publisherPrivKeyHex = opts.l1PublisherKey.getValue(); + publisherHdAccount = privateKeyToAccount(publisherPrivKeyHex); + } else if ( + config.publisherPrivateKeys && + config.publisherPrivateKeys.length > 0 && + config.publisherPrivateKeys[0].getValue() != NULL_KEY + ) { + publisherPrivKeyHex = config.publisherPrivateKeys[0].getValue(); + publisherHdAccount = privateKeyToAccount(publisherPrivKeyHex); + } else if (!MNEMONIC) { + throw new Error(`Mnemonic not provided and no publisher private key`); + } else { + publisherHdAccount = mnemonicToAccount(MNEMONIC, { addressIndex: 0 }); + const publisherPrivKeyRaw = publisherHdAccount.getHdKey().privateKey; + const publisherPrivKey = publisherPrivKeyRaw === null ? null : Buffer.from(publisherPrivKeyRaw); + publisherPrivKeyHex = `0x${publisherPrivKey!.toString('hex')}` as const; + config.publisherPrivateKeys = [new SecretValue(publisherPrivKeyHex)]; + } + + if (config.coinbase === undefined) { + config.coinbase = EthAddress.fromString(publisherHdAccount.address); + } + + if (AZTEC_NODE_URL) { + // we are setting up against a remote environment, l1 contracts are assumed to already be deployed + return await setupWithRemoteEnvironment(publisherHdAccount!, config, logger, numberOfAccounts); + } + + // Determine which addresses to fund in genesis + const initialFundedAccounts = + opts.initialFundedAccounts ?? + (await generateSchnorrAccounts(opts.numberOfInitialFundedAccounts ?? Math.max(numberOfAccounts, 10))); + const addressesToFund = initialFundedAccounts.map(a => a.address); + + // Optionally fund the sponsored FPC + if (opts.fundSponsoredFPC) { + const sponsoredFPCAddress = await getSponsoredFPCAddress(); + addressesToFund.push(sponsoredFPCAddress); + } + + const { genesisArchiveRoot, prefilledPublicData, fundingNeeded } = await getGenesisValues( + addressesToFund, + opts.initialAccountFeeJuice, + opts.genesisPublicData, + ); + + const wasAutomining = await ethCheatCodes.isAutoMining(); + const enableAutomine = opts.automineL1Setup && !wasAutomining && isAnvilTestChain(chain.id); + if (enableAutomine) { + await ethCheatCodes.setAutomine(true); + } + + const l1Client = createExtendedL1Client(config.l1RpcUrls, publisherHdAccount!, chain); + + // Deploy Multicall3 if running locally + await deployMulticall3(l1Client, logger); + + // Force viem to refresh its nonce cache to avoid "nonce too low" errors in subsequent transactions + // This is necessary because deployMulticall3 sends multiple transactions and viem may cache a stale nonce + await l1Client.getTransactionCount({ address: l1Client.account.address }); + + const deployL1ContractsValues: DeployAztecL1ContractsReturnType = await deployAztecL1Contracts( + config.l1RpcUrls[0], + publisherPrivKeyHex!, + chain.id, + { + ...getL1ContractsConfigEnvVars(), + ...opts, + ...opts.l1ContractsArgs, + vkTreeRoot: getVKTreeRoot(), + protocolContractsHash, + genesisArchiveRoot, + initialValidators: opts.initialValidators, + feeJuicePortalInitialBalance: fundingNeeded, + realVerifier: false, + }, + ); + + config.l1Contracts = deployL1ContractsValues.l1ContractAddresses; + config.rollupVersion = deployL1ContractsValues.rollupVersion; + + if (enableAutomine) { + await ethCheatCodes.setAutomine(false); + await ethCheatCodes.setIntervalMining(config.ethereumSlotDuration); + dateProvider.setTime((await ethCheatCodes.timestamp()) * 1000); + } + + if (opts.l2StartTime) { + await ethCheatCodes.warp(opts.l2StartTime, { resetBlockInterval: true }); + } + + const watcher = new AnvilTestWatcher( + new EthCheatCodesWithState(config.l1RpcUrls, dateProvider), + deployL1ContractsValues.l1ContractAddresses.rollupAddress, + deployL1ContractsValues.l1Client, + dateProvider, + ); + if (!opts.disableAnvilTestWatcher) { + await watcher.start(); + } + + // Use metricsPort-based telemetry if provided, otherwise use the regular telemetry client + const telemetryClient = opts.metricsPort + ? await getEndToEndTestTelemetryClient(opts.metricsPort) + : await getTelemetryClient(opts.telemetryConfig); + + await setupSharedBlobStorage(config); + + logger.verbose('Creating and synching an aztec node', config); + + const acvmConfig = await getACVMConfig(logger); + if (acvmConfig) { + config.acvmWorkingDirectory = acvmConfig.acvmWorkingDirectory; + config.acvmBinaryPath = acvmConfig.acvmBinaryPath; + } + + const bbConfig = await getBBConfig(logger); + if (bbConfig) { + config.bbBinaryPath = bbConfig.bbBinaryPath; + config.bbWorkingDirectory = bbConfig.bbWorkingDirectory; + } + + let mockGossipSubNetwork: MockGossipSubNetwork | undefined; + let p2pClientDeps: P2PClientDeps | undefined = undefined; + + if (opts.mockGossipSubNetwork) { + mockGossipSubNetwork = new MockGossipSubNetwork(); + p2pClientDeps = { p2pServiceFactory: getMockPubSubP2PServiceFactory(mockGossipSubNetwork) }; + } + + // Transactions built against the genesis state must be included in block 1, otherwise they are dropped. + // To avoid test failures from dropped transactions, we ensure progression beyond genesis before proceeding. + // For account deployments, we set minTxsPerBlock=1 and deploy accounts sequentially for guaranteed success. + // If no accounts need deployment, we await an empty block to confirm network progression. + const originalMinTxsPerBlock = config.minTxsPerBlock; + if (originalMinTxsPerBlock === undefined) { + throw new Error('minTxsPerBlock is undefined in e2e test setup'); + } + + // Only set minTxsPerBlock=1 if we're going to deploy accounts and need reliable block inclusion + const shouldDeployAccounts = numberOfAccounts > 0 && !opts.skipAccountDeployment; + // Only set minTxsPerBlock=0 if we need an empty block (no accounts at all, not skipped deployment) + const needsEmptyBlock = numberOfAccounts === 0 && !opts.skipAccountDeployment; + config.minTxsPerBlock = shouldDeployAccounts ? 1 : needsEmptyBlock ? 0 : originalMinTxsPerBlock; + + config.p2pEnabled = opts.mockGossipSubNetwork || config.p2pEnabled; + config.p2pIp = opts.p2pIp ?? config.p2pIp ?? '127.0.0.1'; + + if (!config.disableValidator) { + if ((config.validatorPrivateKeys?.getValue().length ?? 0) === 0) { + config.validatorPrivateKeys = new SecretValue([generatePrivateKey()]); + } + } + + const aztecNodeService = await AztecNodeService.createAndSync( + config, + { dateProvider, telemetry: telemetryClient, p2pClientDeps, logger: createLogger('node:MAIN-aztec-node') }, + { prefilledPublicData }, + ); + const sequencerClient = aztecNodeService.getSequencer(); + + if (sequencerClient) { + const publisher = (sequencerClient as TestSequencerClient).sequencer.publisher; + publisher.l1TxUtils = DelayedTxUtils.fromL1TxUtils(publisher.l1TxUtils, config.ethereumSlotDuration, l1Client); + } + + let proverNode: ProverNode | undefined = undefined; + if (opts.startProverNode) { + logger.verbose('Creating and syncing a simulated prover node...'); + const proverNodePrivateKey = getPrivateKeyFromIndex(2); + const proverNodePrivateKeyHex: Hex = `0x${proverNodePrivateKey!.toString('hex')}`; + const proverNodeDataDirectory = path.join(directoryToCleanup, randomBytes(8).toString('hex')); + const proverNodeConfig = { + ...config.proverNodeConfig, + dataDirectory: proverNodeDataDirectory, + p2pEnabled: false, + }; + proverNode = await createAndSyncProverNode( + proverNodePrivateKeyHex, + config, + proverNodeConfig, + aztecNodeService, + prefilledPublicData, + ); + } + + logger.verbose('Creating a pxe...'); + const pxeConfig = { ...getPXEConfig(), ...pxeOpts }; + pxeConfig.dataDirectory = path.join(directoryToCleanup, randomBytes(8).toString('hex')); + // For tests we only want proving enabled if specifically requested + pxeConfig.proverEnabled = !!pxeOpts.proverEnabled; + const wallet = await TestWallet.create(aztecNodeService, pxeConfig); + + const cheatCodes = await CheatCodes.create(config.l1RpcUrls, aztecNodeService, dateProvider); + + if ( + (opts.aztecTargetCommitteeSize && opts.aztecTargetCommitteeSize > 0) || + (opts.initialValidators && opts.initialValidators.length > 0) + ) { + // We need to advance such that the committee is set up. + await cheatCodes.rollup.advanceToEpoch( + EpochNumber.fromBigInt( + BigInt(await cheatCodes.rollup.getEpoch()) + BigInt(config.lagInEpochsForValidatorSet + 1), + ), + ); + await cheatCodes.rollup.setupEpoch(); + await cheatCodes.rollup.debugRollup(); + } + + let accounts: AztecAddress[] = []; + + if (shouldDeployAccounts) { + logger.info( + `${numberOfAccounts} accounts are being deployed. Reliably progressing past genesis by setting minTxsPerBlock to 1 and waiting for the accounts to be deployed`, + ); + const accountsData = initialFundedAccounts.slice(0, numberOfAccounts); + const accountManagers = await deployFundedSchnorrAccounts(wallet, aztecNodeService, accountsData); + accounts = accountManagers.map(accountManager => accountManager.address); + } else if (needsEmptyBlock) { + logger.info('No accounts are being deployed, waiting for an empty block 1 to be mined'); + while ((await aztecNodeService.getBlockNumber()) === 0) { + await sleep(2000); + } + } + // If skipAccountDeployment is true, we don't deploy or wait - tests will handle account deployment later + + // Now we restore the original minTxsPerBlock setting if we changed it. + if (sequencerClient && config.minTxsPerBlock !== originalMinTxsPerBlock) { + sequencerClient.getSequencer().updateConfig({ minTxsPerBlock: originalMinTxsPerBlock }); + } + + if (initialFundedAccounts.length < numberOfAccounts) { + throw new Error( + `Unable to deploy ${numberOfAccounts} accounts. Only ${initialFundedAccounts.length} accounts were funded.`, + ); + } + + const teardown = async () => { + try { + await tryStop(wallet, logger); + await tryStop(aztecNodeService, logger); + await tryStop(proverNode, logger); + + if (acvmConfig?.cleanup) { + await acvmConfig.cleanup(); + } + + if (bbConfig?.cleanup) { + await bbConfig.cleanup(); + } + + await tryStop(watcher, logger); + await tryStop(anvil, logger); + + await tryRmDir(directoryToCleanup, logger); + } catch (err) { + logger.error(`Error during e2e test teardown`, err); + } + }; + + return { + anvil, + aztecNode: aztecNodeService, + aztecNodeService, + aztecNodeAdmin: aztecNodeService, + cheatCodes, + ethCheatCodes, + config, + aztecNodeConfig: config, + dateProvider, + deployL1ContractsValues, + initialFundedAccounts, + logger, + mockGossipSubNetwork, + prefilledPublicData, + proverNode, + sequencer: sequencerClient, + teardown, + telemetryClient, + wallet, + accounts, + watcher, + acvmConfig, + bbConfig, + directoryToCleanup, + }; + } catch (err) { + await anvil?.stop(); + throw err; + } +} + +/** Returns the job name for the current test. */ +function getJobName() { + return process.env.JOB_NAME ?? expect.getState().currentTestName?.split(' ')[0].replaceAll('/', '_') ?? 'unknown'; +} + +/** + * Returns a logger instance for the current test. + */ +export function getLogger() { + const describeBlockName = expect.getState().currentTestName?.split(' ')[0].replaceAll('/', ':'); + if (!describeBlockName) { + const name = expect.getState().testPath?.split('/').pop()?.split('.')[0] ?? 'unknown'; + return createLogger('e2e:' + name); + } + return createLogger('e2e:' + describeBlockName); +} + +/** + * Computes the address of the "canonical" SponsoredFPCContract. + */ +export function getSponsoredFPCInstance(): Promise { + return Promise.resolve( + getContractInstanceFromInstantiationParams(SponsoredFPCContract.artifact, { + salt: new Fr(SPONSORED_FPC_SALT), + }), + ); +} + +/** + * Computes the address of the "canonical" SponsoredFPCContract. + */ +export async function getSponsoredFPCAddress() { + const sponsoredFPCInstance = await getSponsoredFPCInstance(); + return sponsoredFPCInstance.address; +} + +/** + * Deploy a sponsored FPC contract to a running instance. + */ +export async function setupSponsoredFPC(wallet: Wallet) { + const instance = await getContractInstanceFromInstantiationParams(SponsoredFPCContract.artifact, { + salt: new Fr(SPONSORED_FPC_SALT), + }); + + await wallet.registerContract(instance, SponsoredFPCContract.artifact); + getLogger().info(`SponsoredFPC: ${instance.address}`); + return instance; +} + +/** + * Registers the SponsoredFPC in this PXE instance. + */ +export async function registerSponsoredFPC(wallet: Wallet): Promise { + await wallet.registerContract(await getSponsoredFPCInstance(), SponsoredFPCContract.artifact); +} + +export async function waitForProvenChain(node: AztecNode, targetBlock?: BlockNumber, timeoutSec = 60, intervalSec = 1) { + targetBlock ??= await node.getBlockNumber(); + + await retryUntil( + async () => (await node.getProvenBlockNumber()) >= targetBlock, + 'proven chain status', + timeoutSec, + intervalSec, + ); +} + +export function createAndSyncProverNode( + proverNodePrivateKey: `0x${string}`, + aztecNodeConfig: AztecNodeConfig, + proverNodeConfig: Partial & Pick & { dontStart?: boolean }, + aztecNode: AztecNode | undefined, + prefilledPublicData: PublicDataTreeLeaf[] = [], + proverNodeDeps: ProverNodeDeps = {}, +) { + return withLogNameSuffix('prover-node', async () => { + const aztecNodeTxProvider = aztecNode && { + getTxByHash: aztecNode.getTxByHash.bind(aztecNode), + getTxsByHash: aztecNode.getTxsByHash.bind(aztecNode), + stop: () => Promise.resolve(), + }; + + const blobClient = await createBlobClientWithFileStores(aztecNodeConfig, createLogger('blob-client:prover-node')); + + const archiverConfig = { ...aztecNodeConfig, dataDirectory: proverNodeConfig.dataDirectory }; + const archiver = await createArchiver( + archiverConfig, + { blobClient, dateProvider: proverNodeDeps.dateProvider }, + { blockUntilSync: true }, + ); + + const proverConfig: ProverNodeConfig = { + ...aztecNodeConfig, + txCollectionNodeRpcUrls: [], + realProofs: false, + proverAgentCount: 2, + publisherPrivateKeys: [new SecretValue(proverNodePrivateKey)], + proverNodeMaxPendingJobs: 10, + proverNodeMaxParallelBlocksPerEpoch: 32, + proverNodePollingIntervalMs: 200, + txGatheringIntervalMs: 1000, + txGatheringBatchSize: 10, + txGatheringMaxParallelRequestsPerNode: 10, + txGatheringTimeoutMs: 24_000, + proverNodeFailedEpochStore: undefined, + proverId: EthAddress.fromNumber(1), + proverNodeEpochProvingDelayMs: undefined, + ...proverNodeConfig, + }; + + const l1TxUtils = createDelayedL1TxUtils( + aztecNodeConfig, + proverNodePrivateKey, + 'prover-node', + proverNodeDeps.dateProvider, + ); + + const proverNode = await createProverNode( + proverConfig, + { ...proverNodeDeps, aztecNodeTxProvider, archiver: archiver as Archiver, l1TxUtils }, + { prefilledPublicData }, + ); + getLogger().info(`Created and synced prover node`, { publisherAddress: l1TxUtils.client.account!.address }); + if (!proverNodeConfig.dontStart) { + await proverNode.start(); + } + return proverNode; + }); +} + +function createDelayedL1TxUtils( + aztecNodeConfig: AztecNodeConfig, + privateKey: `0x${string}`, + logName: string, + dateProvider?: DateProvider, +) { + const l1Client = createExtendedL1Client(aztecNodeConfig.l1RpcUrls, privateKey, foundry); + + const log = createLogger(logName); + const l1TxUtils = createDelayedL1TxUtilsFromViemWallet(l1Client, log, dateProvider, aztecNodeConfig); + l1TxUtils.enableDelayer(aztecNodeConfig.ethereumSlotDuration); + return l1TxUtils; +} + +export type BalancesFn = ReturnType; +export function getBalancesFn( + symbol: string, + method: ContractMethod, + from: AztecAddress, + logger: any, +): (...addresses: (AztecAddress | { address: AztecAddress })[]) => Promise { + const balances = async (...addressLikes: (AztecAddress | { address: AztecAddress })[]) => { + const addresses = addressLikes.map(addressLike => ('address' in addressLike ? addressLike.address : addressLike)); + const b = await Promise.all(addresses.map(address => method(address).simulate({ from }))); + const debugString = `${symbol} balances: ${addresses.map((address, i) => `${address}: ${b[i]}`).join(', ')}`; + logger.verbose(debugString); + return b; + }; + + return balances; +} + +export async function expectMapping( + fn: (...k: K[]) => Promise, + inputs: K[], + expectedOutputs: V[], +): Promise { + expect(inputs.length).toBe(expectedOutputs.length); + + const outputs = await fn(...inputs); + + expect(outputs).toEqual(expectedOutputs); +} + +export async function expectMappingDelta( + initialValues: V[], + fn: (...k: K[]) => Promise, + inputs: K[], + expectedDiffs: V[], +): Promise { + expect(inputs.length).toBe(expectedDiffs.length); + + const outputs = await fn(...inputs); + const diffs = outputs.map((output, i) => output - initialValues[i]); + + expect(diffs).toEqual(expectedDiffs); +} + +/** + * Registers the contract class used for test accounts and publicly deploys the instances requested. + * Use this when you need to make a public call to an account contract, such as for requesting a public authwit. + */ +export async function ensureAccountContractsPublished(wallet: Wallet, accountsToDeploy: AztecAddress[]) { + const accountsAndAddresses = await Promise.all( + accountsToDeploy.map(async address => { + return { + address, + deployed: (await wallet.getContractMetadata(address)).isContractPublished, + }; + }), + ); + const instances = ( + await Promise.all( + accountsAndAddresses + .filter(({ deployed }) => !deployed) + .map(({ address }) => wallet.getContractMetadata(address)), + ) + ).map(contractMetadata => contractMetadata.instance); + const contractClass = await getContractClassFromArtifact(SchnorrAccountContractArtifact); + if (!(await wallet.getContractClassMetadata(contractClass.id)).isContractClassPubliclyRegistered) { + await (await publishContractClass(wallet, SchnorrAccountContractArtifact)) + .send({ from: accountsToDeploy[0] }) + .wait(); + } + const requests = instances.map(instance => publishInstance(wallet, instance!)); + const batch = new BatchCall(wallet, requests); + await batch.send({ from: accountsToDeploy[0] }).wait(); +} + +/** + * Helper function to deploy accounts. + * Returns deployed account data that can be used by tests. + */ +export const deployAccounts = + (numberOfAccounts: number, logger: Logger) => + async ({ wallet, initialFundedAccounts }: { wallet: TestWallet; initialFundedAccounts: InitialAccountData[] }) => { + if (initialFundedAccounts.length < numberOfAccounts) { + throw new Error(`Cannot deploy more than ${initialFundedAccounts.length} initial accounts.`); + } + + logger.verbose('Deploying accounts funded with fee juice...'); + const deployedAccounts = initialFundedAccounts.slice(0, numberOfAccounts); + // Serial due to https://github.com/AztecProtocol/aztec-packages/issues/12045 + for (let i = 0; i < deployedAccounts.length; i++) { + const accountManager = await wallet.createSchnorrAccount( + deployedAccounts[i].secret, + deployedAccounts[i].salt, + deployedAccounts[i].signingKey, + ); + const deployMethod = await accountManager.getDeployMethod(); + await deployMethod + .send({ + from: AztecAddress.ZERO, + skipClassPublication: i !== 0, // Publish the contract class at most once. + }) + .wait(); + } + + return { deployedAccounts }; + }; + +/** + * Registers the contract class used for test accounts and publicly deploys the instances requested. + * Use this when you need to make a public call to an account contract, such as for requesting a public authwit. + */ +export async function publicDeployAccounts( + wallet: Wallet, + accountsToDeploy: AztecAddress[], + waitUntilProven = false, + node?: AztecNode, +) { + const instances = (await Promise.all(accountsToDeploy.map(account => wallet.getContractMetadata(account)))).map( + metadata => metadata.instance, + ); + + const contractClass = await getContractClassFromArtifact(SchnorrAccountContractArtifact); + const alreadyRegistered = (await wallet.getContractClassMetadata(contractClass.id)).isContractClassPubliclyRegistered; + + const calls: ContractFunctionInteraction[] = await Promise.all([ + ...(!alreadyRegistered ? [publishContractClass(wallet, SchnorrAccountContractArtifact)] : []), + ...instances.map(instance => publishInstance(wallet, instance!)), + ]); + + const batch = new BatchCall(wallet, calls); + + const txReceipt = await batch.send({ from: accountsToDeploy[0] }).wait(); + if (waitUntilProven) { + if (!node) { + throw new Error('Need to provide an AztecNode to wait for proven.'); + } else { + await waitForProven(node, txReceipt); + } + } +} + +/** + * Destroys the current context. + */ +export async function teardown(context: EndToEndContext | undefined) { + if (!context) { + return; + } + await context.teardown(); +} + +// Re-export for backward compatibility +export { deployAndInitializeTokenAndBridgeContracts } from '../shared/cross_chain_test_harness.js'; diff --git a/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts b/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts deleted file mode 100644 index 94358347b073..000000000000 --- a/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts +++ /dev/null @@ -1,333 +0,0 @@ -import { SchnorrAccountContractArtifact } from '@aztec/accounts/schnorr'; -import { type InitialAccountData, generateSchnorrAccounts } from '@aztec/accounts/testing'; -import { type AztecNodeConfig, AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node'; -import { AztecAddress, EthAddress } from '@aztec/aztec.js/addresses'; -import { BatchCall, type ContractFunctionInteraction, waitForProven } from '@aztec/aztec.js/contracts'; -import { publishContractClass, publishInstance } from '@aztec/aztec.js/deployment'; -import type { Logger } from '@aztec/aztec.js/log'; -import type { AztecNode } from '@aztec/aztec.js/node'; -import type { Wallet } from '@aztec/aztec.js/wallet'; -import { AnvilTestWatcher, CheatCodes } from '@aztec/aztec/testing'; -import { createExtendedL1Client } from '@aztec/ethereum/client'; -import { getL1ContractsConfigEnvVars } from '@aztec/ethereum/config'; -import { deployMulticall3 } from '@aztec/ethereum/contracts'; -import { - type DeployAztecL1ContractsArgs, - type DeployAztecL1ContractsReturnType, - deployAztecL1Contracts, -} from '@aztec/ethereum/deploy-aztec-l1-contracts'; -import { EthCheatCodesWithState, startAnvil } from '@aztec/ethereum/test'; -import { SecretValue } from '@aztec/foundation/config'; -import { randomBytes } from '@aztec/foundation/crypto/random'; -import { tryRmDir } from '@aztec/foundation/fs'; -import { TestDateProvider } from '@aztec/foundation/timer'; -import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree'; -import { protocolContractsHash } from '@aztec/protocol-contracts'; -import type { ProverNode } from '@aztec/prover-node'; -import { getPXEConfig } from '@aztec/pxe/server'; -import type { SequencerClient } from '@aztec/sequencer-client'; -import { tryStop } from '@aztec/stdlib/interfaces/server'; -import { TestWallet } from '@aztec/test-wallet/server'; -import { getGenesisValues } from '@aztec/world-state/testing'; - -import type { Anvil } from '@viem/anvil'; -import fs from 'fs/promises'; -import { tmpdir } from 'os'; -import path from 'path'; -import { mnemonicToAccount } from 'viem/accounts'; -import { foundry } from 'viem/chains'; - -import { MNEMONIC, TEST_MAX_PENDING_TX_POOL_COUNT, TEST_PEER_CHECK_INTERVAL_MS } from './fixtures.js'; -import { getACVMConfig } from './get_acvm_config.js'; -import { getBBConfig } from './get_bb_config.js'; -import { - type SetupOptions, - createAndSyncProverNode, - getLogger, - getPrivateKeyFromIndex, - getSponsoredFPCAddress, - setupSharedBlobStorage, -} from './utils.js'; -import { getEndToEndTestTelemetryClient } from './with_telemetry_utils.js'; - -export type SubsystemsContext = { - anvil: Anvil; - acvmConfig: any; - bbConfig: any; - aztecNode: AztecNodeService; - aztecNodeConfig: AztecNodeConfig; - wallet: TestWallet; - deployL1ContractsValues: DeployAztecL1ContractsReturnType; - proverNode?: ProverNode; - watcher: AnvilTestWatcher; - cheatCodes: CheatCodes; - sequencer: SequencerClient; - dateProvider: TestDateProvider; - initialFundedAccounts: InitialAccountData[]; - directoryToCleanup?: string; -}; - -/** - * Destroys the current subsystem context. - */ -export async function teardown(context: SubsystemsContext | undefined) { - if (!context) { - return; - } - const logger = getLogger(); - try { - logger.info('Tearing down subsystems'); - await tryStop(context.proverNode); - await tryStop(context.aztecNode); - await context.acvmConfig?.cleanup(); - await context.bbConfig?.cleanup(); - await tryStop(context.anvil); - await tryStop(context.watcher); - await tryRmDir(context.directoryToCleanup, logger); - } catch (err) { - logger.error('Error during teardown', err); - } -} - -/** - * Initializes a fresh set of subsystems. - * State is stored in temporary in-memory locations. - */ -export async function setupFromFresh( - logger: Logger, - { numberOfInitialFundedAccounts = 10, ...opts }: SetupOptions = {}, - deployL1ContractsArgs: Partial = { - initialValidators: [], - }, -): Promise { - logger.verbose(`Initializing state...`); - - // Default to no slashing - opts.slasherFlavor ??= 'none'; - deployL1ContractsArgs.slasherFlavor ??= opts.slasherFlavor; - - // Fetch the AztecNode config. - // TODO: For some reason this is currently the union of a bunch of subsystems. That needs fixing. - const aztecNodeConfig: AztecNodeConfig & SetupOptions = { ...getConfigEnvVars(), ...opts }; - aztecNodeConfig.peerCheckIntervalMS = TEST_PEER_CHECK_INTERVAL_MS; - aztecNodeConfig.maxPendingTxCount = opts.maxPendingTxCount ?? TEST_MAX_PENDING_TX_POOL_COUNT; - // Only enable proving if specifically requested. - aztecNodeConfig.realProofs = !!opts.realProofs; - // Only enforce the time table if requested - aztecNodeConfig.enforceTimeTable = !!opts.enforceTimeTable; - // Only set the target committee size if it is explicitly set - aztecNodeConfig.aztecTargetCommitteeSize = opts.aztecTargetCommitteeSize ?? 0; - aztecNodeConfig.listenAddress = '127.0.0.1'; - - deployL1ContractsArgs.aztecTargetCommitteeSize ??= aztecNodeConfig.aztecTargetCommitteeSize; - - // Create a temp directory for all ephemeral state and cleanup afterwards - const directoryToCleanup = path.join(tmpdir(), randomBytes(8).toString('hex')); - await fs.mkdir(directoryToCleanup, { recursive: true }); - aztecNodeConfig.dataDirectory = directoryToCleanup; - - await setupSharedBlobStorage(aztecNodeConfig); - - const hdAccount = mnemonicToAccount(MNEMONIC, { addressIndex: 0 }); - const publisherPrivKeyRaw = hdAccount.getHdKey().privateKey; - const publisherPrivKey = publisherPrivKeyRaw === null ? null : Buffer.from(publisherPrivKeyRaw); - const publisherPrivKeyHex = `0x${publisherPrivKey!.toString('hex')}` satisfies `0x${string}`; - - const l1Client = createExtendedL1Client([aztecNodeConfig.l1RpcUrls[0]], hdAccount, foundry); - - const validatorPrivKey = getPrivateKeyFromIndex(0); - const proverNodePrivateKey = getPrivateKeyFromIndex(0); - - aztecNodeConfig.publisherPrivateKeys = [new SecretValue(publisherPrivKeyHex)]; - aztecNodeConfig.validatorPrivateKeys = new SecretValue([`0x${validatorPrivKey!.toString('hex')}`]); - aztecNodeConfig.coinbase = opts.coinbase ?? EthAddress.fromString(`${hdAccount.address}`); - - logger.info(`Setting up environment with config`, aztecNodeConfig); - - // Start anvil. We go via a wrapper script to ensure if the parent dies, anvil dies. - logger.verbose('Starting anvil...'); - const res = await startAnvil({ l1BlockTime: opts.ethereumSlotDuration }); - const anvil = res.anvil; - aztecNodeConfig.l1RpcUrls = [res.rpcUrl]; - - const dateProvider = new TestDateProvider(); - const ethCheatCodes = new EthCheatCodesWithState(aztecNodeConfig.l1RpcUrls, dateProvider); - - // Deploy our L1 contracts. - logger.verbose('Deploying Aztec L1 contracts...'); - if (opts.l1StartTime) { - await ethCheatCodes.warp(opts.l1StartTime, { resetBlockInterval: true }); - } - - const initialFundedAccounts = await generateSchnorrAccounts(numberOfInitialFundedAccounts); - const sponsoredFPCAddress = await getSponsoredFPCAddress(); - const { genesisArchiveRoot, prefilledPublicData, fundingNeeded } = await getGenesisValues( - initialFundedAccounts.map(a => a.address).concat(sponsoredFPCAddress), - opts.initialAccountFeeJuice, - ); - - const vkTreeRoot = getVKTreeRoot(); - await deployMulticall3(l1Client, logger); - - // Define args, defaulted to our environment variables. - const args: DeployAztecL1ContractsArgs = { - ...getL1ContractsConfigEnvVars(), - ...deployL1ContractsArgs, - vkTreeRoot, - genesisArchiveRoot, - protocolContractsHash, - initialValidators: opts.initialValidators, - feeJuicePortalInitialBalance: fundingNeeded, - realVerifier: false, - }; - - const deployL1ContractsValues = await deployAztecL1Contracts( - aztecNodeConfig.l1RpcUrls[0], - publisherPrivKeyHex, - foundry.id, - args, - ); - - aztecNodeConfig.l1Contracts = deployL1ContractsValues.l1ContractAddresses; - aztecNodeConfig.rollupVersion = deployL1ContractsValues.rollupVersion; - - const watcher = new AnvilTestWatcher( - new EthCheatCodesWithState(aztecNodeConfig.l1RpcUrls, dateProvider), - deployL1ContractsValues.l1ContractAddresses.rollupAddress, - deployL1ContractsValues.l1Client, - dateProvider, - ); - await watcher.start(); - - const acvmConfig = await getACVMConfig(logger); - if (acvmConfig) { - aztecNodeConfig.acvmWorkingDirectory = acvmConfig.acvmWorkingDirectory; - aztecNodeConfig.acvmBinaryPath = acvmConfig.acvmBinaryPath; - } - - const bbConfig = await getBBConfig(logger); - if (bbConfig) { - aztecNodeConfig.bbBinaryPath = bbConfig.bbBinaryPath; - aztecNodeConfig.bbWorkingDirectory = bbConfig.bbWorkingDirectory; - } - - const telemetry = await getEndToEndTestTelemetryClient(opts.metricsPort); - - logger.info('Creating and synching an aztec node...'); - const aztecNode = await AztecNodeService.createAndSync( - aztecNodeConfig, - { telemetry, dateProvider }, - { prefilledPublicData }, - ); - - let proverNode: ProverNode | undefined = undefined; - if (opts.startProverNode) { - logger.verbose('Creating and syncing a simulated prover node with p2p disabled...'); - proverNode = await createAndSyncProverNode( - `0x${proverNodePrivateKey!.toString('hex')}`, - aztecNodeConfig, - { - ...aztecNodeConfig.proverNodeConfig, - dataDirectory: path.join(directoryToCleanup, randomBytes(8).toString('hex')), - p2pEnabled: false, - }, - aztecNode, - prefilledPublicData, - ); - } - - logger.verbose('Creating pxe...'); - const pxeConfig = getPXEConfig(); - pxeConfig.dataDirectory = path.join(directoryToCleanup, randomBytes(8).toString('hex')); - // Only enable proving if specifically requested. - pxeConfig.proverEnabled = !!opts.realProofs; - const wallet = await TestWallet.create(aztecNode, pxeConfig); - const cheatCodes = await CheatCodes.create(aztecNodeConfig.l1RpcUrls, aztecNode, dateProvider); - - return { - aztecNodeConfig, - anvil, - aztecNode, - wallet, - sequencer: aztecNode.getSequencer()!, - acvmConfig, - bbConfig, - deployL1ContractsValues, - proverNode, - watcher, - cheatCodes, - dateProvider, - initialFundedAccounts, - directoryToCleanup, - }; -} - -/** - * Helper function to deploy accounts. - * Returns deployed account data that can be used by tests. - */ -export const deployAccounts = - (numberOfAccounts: number, logger: Logger) => - async ({ wallet, initialFundedAccounts }: { wallet: TestWallet; initialFundedAccounts: InitialAccountData[] }) => { - if (initialFundedAccounts.length < numberOfAccounts) { - throw new Error(`Cannot deploy more than ${initialFundedAccounts.length} initial accounts.`); - } - - logger.verbose('Deploying accounts funded with fee juice...'); - const deployedAccounts = initialFundedAccounts.slice(0, numberOfAccounts); - // Serial due to https://github.com/AztecProtocol/aztec-packages/issues/12045 - for (let i = 0; i < deployedAccounts.length; i++) { - const accountManager = await wallet.createSchnorrAccount( - deployedAccounts[i].secret, - deployedAccounts[i].salt, - deployedAccounts[i].signingKey, - ); - const deployMethod = await accountManager.getDeployMethod(); - await deployMethod - .send({ - from: AztecAddress.ZERO, - skipClassPublication: i !== 0, // Publish the contract class at most once. - }) - .wait(); - } - - return { deployedAccounts }; - }; - -/** - * Registers the contract class used for test accounts and publicly deploys the instances requested. - * Use this when you need to make a public call to an account contract, such as for requesting a public authwit. - * @param sender - Wallet to send the deployment tx. - * @param accountsToDeploy - Which accounts to publicly deploy. - * @param waitUntilProven - Whether to wait for the tx to be proven. - * @param node - AztecNode used to wait for proven tx. - */ -export async function publicDeployAccounts( - wallet: Wallet, - accountsToDeploy: AztecAddress[], - waitUntilProven = false, - node?: AztecNode, -) { - const instances = (await Promise.all(accountsToDeploy.map(account => wallet.getContractMetadata(account)))).map( - metadata => metadata.instance, - ); - - const { instance } = await wallet.getContractMetadata(accountsToDeploy[0]); - const { isContractClassPubliclyRegistered } = await wallet.getContractClassMetadata(instance!.currentContractClassId); - - const calls: ContractFunctionInteraction[] = await Promise.all([ - ...(!isContractClassPubliclyRegistered ? [publishContractClass(wallet, SchnorrAccountContractArtifact)] : []), - ...instances.map(instance => publishInstance(wallet, instance!)), - ]); - - const batch = new BatchCall(wallet, calls); - - const txReceipt = await batch.send({ from: accountsToDeploy[0] }).wait(); - if (waitUntilProven) { - if (!node) { - throw new Error('Need to provide an AztecNode to wait for proven.'); - } else { - await waitForProven(node, txReceipt); - } - } -} diff --git a/yarn-project/end-to-end/src/fixtures/utils.ts b/yarn-project/end-to-end/src/fixtures/utils.ts index 49d528437de1..1af275f3e3c7 100644 --- a/yarn-project/end-to-end/src/fixtures/utils.ts +++ b/yarn-project/end-to-end/src/fixtures/utils.ts @@ -1,904 +1,30 @@ -import { SchnorrAccountContractArtifact } from '@aztec/accounts/schnorr'; -import { type InitialAccountData, generateSchnorrAccounts, getInitialTestAccountsData } from '@aztec/accounts/testing'; -import { type Archiver, createArchiver } from '@aztec/archiver'; -import { type AztecNodeConfig, AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node'; -import { AztecAddress } from '@aztec/aztec.js/addresses'; -import { BatchCall, type ContractMethod } from '@aztec/aztec.js/contracts'; -import { publishContractClass, publishInstance } from '@aztec/aztec.js/deployment'; -import { Fr } from '@aztec/aztec.js/fields'; -import { type Logger, createLogger } from '@aztec/aztec.js/log'; -import { type AztecNode, createAztecNodeClient, waitForNode } from '@aztec/aztec.js/node'; -import type { Wallet } from '@aztec/aztec.js/wallet'; -import { AnvilTestWatcher, CheatCodes } from '@aztec/aztec/testing'; -import { createBlobClientWithFileStores } from '@aztec/blob-client/client'; -import { SPONSORED_FPC_SALT } from '@aztec/constants'; -import { isAnvilTestChain } from '@aztec/ethereum/chain'; -import { createExtendedL1Client } from '@aztec/ethereum/client'; -import { getL1ContractsConfigEnvVars } from '@aztec/ethereum/config'; -import { NULL_KEY } from '@aztec/ethereum/constants'; -import { - type DeployAztecL1ContractsReturnType, - type Operator, - type ZKPassportArgs, - deployAztecL1Contracts, -} from '@aztec/ethereum/deploy-aztec-l1-contracts'; -import { - DelayedTxUtils, - EthCheatCodes, - EthCheatCodesWithState, - createDelayedL1TxUtilsFromViemWallet, +/** + * Re-exports from the unified setup module for backward compatibility. + * Tests that previously used utils.ts should continue to work. + */ +export { + type BalancesFn, + type EndToEndContext, + type SetupOptions, + createAndSyncProverNode, + deployAccounts, + ensureAccountContractsPublished, + expectMapping, + expectMappingDelta, + getBalancesFn, + getLogger, + getPrivateKeyFromIndex, + getSponsoredFPCAddress, + getSponsoredFPCInstance, + publicDeployAccounts, + registerSponsoredFPC, + setup, + setupPXEAndGetWallet, + setupSharedBlobStorage, + setupSponsoredFPC, startAnvil, -} from '@aztec/ethereum/test'; -import { BlockNumber, EpochNumber } from '@aztec/foundation/branded-types'; -import { SecretValue } from '@aztec/foundation/config'; -import { EthAddress } from '@aztec/foundation/eth-address'; -import { tryRmDir } from '@aztec/foundation/fs'; -import { withLogNameSuffix } from '@aztec/foundation/log'; -import { retryUntil } from '@aztec/foundation/retry'; -import { sleep } from '@aztec/foundation/sleep'; -import { DateProvider, TestDateProvider } from '@aztec/foundation/timer'; -import type { DataStoreConfig } from '@aztec/kv-store/config'; -import { SponsoredFPCContract } from '@aztec/noir-contracts.js/SponsoredFPC'; -import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree'; -import type { P2PClientDeps } from '@aztec/p2p'; -import { MockGossipSubNetwork, getMockPubSubP2PServiceFactory } from '@aztec/p2p/test-helpers'; -import { protocolContractsHash } from '@aztec/protocol-contracts'; -import { type ProverNode, type ProverNodeConfig, type ProverNodeDeps, createProverNode } from '@aztec/prover-node'; -import { type PXEConfig, getPXEConfig } from '@aztec/pxe/server'; -import type { SequencerClient } from '@aztec/sequencer-client'; -import type { TestSequencerClient } from '@aztec/sequencer-client/test'; -import { - type ContractInstanceWithAddress, - getContractClassFromArtifact, - getContractInstanceFromInstantiationParams, -} from '@aztec/stdlib/contract'; -import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client'; -import { tryStop } from '@aztec/stdlib/interfaces/server'; -import type { P2PClientType } from '@aztec/stdlib/p2p'; -import type { PublicDataTreeLeaf } from '@aztec/stdlib/trees'; -import { - type TelemetryClient, - type TelemetryClientConfig, - getConfigEnvVars as getTelemetryConfig, - initTelemetryClient, -} from '@aztec/telemetry-client'; -import { BenchmarkTelemetryClient } from '@aztec/telemetry-client/bench'; -import { TestWallet, deployFundedSchnorrAccounts } from '@aztec/test-wallet/server'; -import { getGenesisValues } from '@aztec/world-state/testing'; - -import type { Anvil } from '@viem/anvil'; -import { randomBytes } from 'crypto'; -import fs from 'fs/promises'; -import { tmpdir } from 'os'; -import * as path from 'path'; -import type { Hex } from 'viem'; -import { - type HDAccount, - type PrivateKeyAccount, - generatePrivateKey, - mnemonicToAccount, - privateKeyToAccount, -} from 'viem/accounts'; -import { type Chain, foundry } from 'viem/chains'; - -import { MNEMONIC, TEST_PEER_CHECK_INTERVAL_MS } from './fixtures.js'; -import { getACVMConfig } from './get_acvm_config.js'; -import { getBBConfig } from './get_bb_config.js'; -import { isMetricsLoggingRequested, setupMetricsLogger } from './logging.js'; + teardown, + waitForProvenChain, +} from './setup.js'; export { deployAndInitializeTokenAndBridgeContracts } from '../shared/cross_chain_test_harness.js'; -export { startAnvil }; - -/** - * Sets up shared blob storage using FileStore in the data directory. - */ -export async function setupSharedBlobStorage(config: { dataDirectory?: string } & Record): Promise { - const sharedBlobPath = path.join(config.dataDirectory!, 'shared-blobs'); - await fs.mkdir(sharedBlobPath, { recursive: true }); - config.blobFileStoreUrls = [`file://${sharedBlobPath}`]; - config.blobFileStoreUploadUrl = `file://${sharedBlobPath}`; -} - -const { AZTEC_NODE_URL = '' } = process.env; -const getAztecUrl = () => AZTEC_NODE_URL; - -let telemetry: TelemetryClient | undefined = undefined; -async function getTelemetryClient(partialConfig: Partial & { benchmark?: boolean } = {}) { - if (!telemetry) { - const config = { ...getTelemetryConfig(), ...partialConfig }; - telemetry = config.benchmark ? new BenchmarkTelemetryClient() : await initTelemetryClient(config); - } - return telemetry; -} -if (typeof afterAll === 'function') { - afterAll(async () => { - await telemetry?.stop(); - }); -} - -export const getPrivateKeyFromIndex = (index: number): Buffer | null => { - const hdAccount = mnemonicToAccount(MNEMONIC, { addressIndex: index }); - const privKeyRaw = hdAccount.getHdKey().privateKey; - return privKeyRaw === null ? null : Buffer.from(privKeyRaw); -}; - -/** - * Sets up Private eXecution Environment (PXE) and returns the corresponding test wallet. - * @param aztecNode - An instance of Aztec Node. - * @param opts - Partial configuration for the PXE. - * @param logger - The logger to be used. - * @param useLogSuffix - Whether to add a randomly generated suffix to the PXE debug logs. - * @returns A test wallet, logger and teardown function. - */ -export async function setupPXEAndGetWallet( - aztecNode: AztecNode, - opts: Partial = {}, - logger = getLogger(), - useLogSuffix = false, -): Promise<{ - /** - * The wallet instance. - */ - wallet: TestWallet; - /** - * Logger instance named as the current test. - */ - logger: Logger; - /** - * Teardown function - */ - teardown: () => Promise; -}> { - const PXEConfig = { ...getPXEConfig(), ...opts }; - // For tests we only want proving enabled if specifically requested - PXEConfig.proverEnabled = !!opts.proverEnabled; - - // If no data directory provided, create a temp directory and clean up afterwards - const configuredDataDirectory = PXEConfig.dataDirectory; - if (!configuredDataDirectory) { - PXEConfig.dataDirectory = path.join(tmpdir(), randomBytes(8).toString('hex')); - } - - const teardown = configuredDataDirectory ? () => Promise.resolve() : () => tryRmDir(PXEConfig.dataDirectory!); - - const wallet = await TestWallet.create(aztecNode, PXEConfig, { - useLogSuffix, - }); - - return { - wallet, - logger, - teardown, - }; -} - -/** - * Function to setup the test against a remote deployment. It is assumed that L1 contract are already deployed - * @param account - The account for use in create viem wallets. - * @param config - The aztec Node Configuration - * @param logger - The logger to be used - * @param numberOfAccounts - The number of new accounts to be created once the PXE is initiated. - * (will create extra accounts if the environment doesn't already have enough accounts) - * @returns Private eXecution Environment (PXE) client, viem wallets, contract addresses etc. - */ -async function setupWithRemoteEnvironment( - account: HDAccount | PrivateKeyAccount, - config: AztecNodeConfig, - logger: Logger, - numberOfAccounts: number, -): Promise { - // we are setting up against a remote environment, l1 contracts are already deployed - const aztecNodeUrl = getAztecUrl(); - logger.verbose(`Creating Aztec Node client to remote host ${aztecNodeUrl}`); - const aztecNode = createAztecNodeClient(aztecNodeUrl); - await waitForNode(aztecNode, logger); - logger.verbose('JSON RPC client connected to Aztec Node'); - logger.verbose(`Retrieving contract addresses from ${aztecNodeUrl}`); - const { l1ContractAddresses, rollupVersion } = await aztecNode.getNodeInfo(); - - const l1Client = createExtendedL1Client(config.l1RpcUrls, account, foundry); - - const deployL1ContractsValues: DeployAztecL1ContractsReturnType = { - l1ContractAddresses, - l1Client, - rollupVersion, - }; - const ethCheatCodes = new EthCheatCodes(config.l1RpcUrls, new DateProvider()); - const wallet = await TestWallet.create(aztecNode); - const cheatCodes = await CheatCodes.create(config.l1RpcUrls, aztecNode, new DateProvider()); - const teardown = () => Promise.resolve(); - - logger.verbose('Populating wallet from already registered accounts...'); - const initialFundedAccounts = await getInitialTestAccountsData(); - - if (initialFundedAccounts.length < numberOfAccounts) { - throw new Error(`Required ${numberOfAccounts} accounts. Found ${initialFundedAccounts.length}.`); - // Deploy new accounts if there's a test that requires more funded accounts in the remote environment. - } - - const testAccounts = await Promise.all( - initialFundedAccounts.slice(0, numberOfAccounts).map(async account => { - const accountManager = await wallet.createSchnorrAccount(account.secret, account.salt, account.signingKey); - return accountManager.address; - }), - ); - - return { - aztecNode, - aztecNodeAdmin: undefined, - sequencer: undefined, - proverNode: undefined, - deployL1ContractsValues, - config, - initialFundedAccounts, - wallet, - accounts: testAccounts, - logger, - cheatCodes, - ethCheatCodes, - prefilledPublicData: undefined, - mockGossipSubNetwork: undefined, - watcher: undefined, - dateProvider: undefined, - telemetryClient: undefined, - teardown, - }; -} - -/** Options for the e2e tests setup */ -export type SetupOptions = { - /** State load */ - stateLoad?: string; - /** Whether to enable metrics collection, if undefined, metrics collection is disabled */ - metricsPort?: number | undefined; - /** Previously deployed contracts on L1 */ - deployL1ContractsValues?: DeployAztecL1ContractsReturnType; - /** Initial fee juice for default accounts */ - initialAccountFeeJuice?: Fr; - /** Number of initial accounts funded with fee juice */ - numberOfInitialFundedAccounts?: number; - /** Data of the initial funded accounts */ - initialFundedAccounts?: InitialAccountData[]; - /** An initial set of validators */ - initialValidators?: (Operator & { privateKey: `0x${string}` })[]; - /** Anvil Start time */ - l1StartTime?: number; - /** The anvil time where we should at the earliest be seeing L2 blocks */ - l2StartTime?: number; - /** Whether to start a prover node */ - startProverNode?: boolean; - /** Manual config for the telemetry client */ - telemetryConfig?: Partial & { benchmark?: boolean }; - /** Public data that will be inserted in the tree in genesis */ - genesisPublicData?: PublicDataTreeLeaf[]; - /** Specific config for the prover node, if set. */ - proverNodeConfig?: Partial; - /** Whether to use a mock gossip sub network for p2p clients. */ - mockGossipSubNetwork?: boolean; - /** Whether to disable the anvil test watcher (can still be manually started) */ - disableAnvilTestWatcher?: boolean; - /** Whether to enable anvil automine during deployment of L1 contracts (consider defaulting this to true). */ - automineL1Setup?: boolean; - /** How many accounts to seed and unlock in anvil. */ - anvilAccounts?: number; - /** Port to start anvil (defaults to 8545) */ - anvilPort?: number; - /** Key to use for publishing L1 contracts */ - l1PublisherKey?: SecretValue<`0x${string}`>; - /** ZkPassport configuration (domain, scope, mock verifier) */ - zkPassportArgs?: ZKPassportArgs; -} & Partial; - -/** Context for an end-to-end test as returned by the `setup` function */ -export type EndToEndContext = { - /** The Aztec Node service or client a connected to it. */ - aztecNode: AztecNode; - /** Client to the Aztec Node admin interface (undefined if connected to remote environment) */ - aztecNodeAdmin?: AztecNodeAdmin; - /** The prover node service (only set if startProverNode is true) */ - proverNode: ProverNode | undefined; - /** A client to the sequencer service (undefined if connected to remote environment) */ - sequencer: SequencerClient | undefined; - /** Return values from deployAztecL1Contracts function. */ - deployL1ContractsValues: DeployAztecL1ContractsReturnType; - /** The Aztec Node configuration. */ - config: AztecNodeConfig; - /** The data for the initial funded accounts. */ - initialFundedAccounts: InitialAccountData[]; - /** The wallet to be used. */ - wallet: TestWallet; - /** The wallets to be used. */ - accounts: AztecAddress[]; - /** Logger instance named as the current test. */ - logger: Logger; - /** The cheat codes. */ - cheatCodes: CheatCodes; - /** The cheat codes for L1 */ - ethCheatCodes: EthCheatCodes; - /** The anvil test watcher (undefined if connected to remote environment) */ - watcher: AnvilTestWatcher | undefined; - /** Allows tweaking current system time, used by the epoch cache only (undefined if connected to remote environment) */ - dateProvider: TestDateProvider | undefined; - /** Telemetry client */ - telemetryClient: TelemetryClient | undefined; - /** Mock gossip sub network used for gossipping messages (only if mockGossipSubNetwork was set to true in opts) */ - mockGossipSubNetwork: MockGossipSubNetwork | undefined; - /** Prefilled public data used for setting up nodes. */ - prefilledPublicData: PublicDataTreeLeaf[] | undefined; - /** Function to stop the started services. */ - teardown: () => Promise; -}; - -/** - * Sets up the environment for the end-to-end tests. - * @param numberOfAccounts - The number of new accounts to be created once the PXE is initiated. - * @param opts - Options to pass to the node initialization and to the setup script. - * @param pxeOpts - Options to pass to the PXE initialization. - */ -export async function setup( - numberOfAccounts = 1, - opts: SetupOptions = {}, - pxeOpts: Partial = {}, - chain: Chain = foundry, -): Promise { - let anvil: Anvil | undefined; - try { - opts.aztecTargetCommitteeSize ??= 0; - opts.slasherFlavor ??= 'none'; - - const config: AztecNodeConfig & SetupOptions = { ...getConfigEnvVars(), ...opts }; - // use initialValidators for the node config - config.validatorPrivateKeys = new SecretValue(opts.initialValidators?.map(v => v.privateKey) ?? []); - - config.peerCheckIntervalMS = TEST_PEER_CHECK_INTERVAL_MS; - // For tests we only want proving enabled if specifically requested - config.realProofs = !!opts.realProofs; - // Only enforce the time table if requested - config.enforceTimeTable = !!opts.enforceTimeTable; - - const logger = getLogger(); - - // Create a temp directory for any services that need it and cleanup later - const directoryToCleanup = path.join(tmpdir(), randomBytes(8).toString('hex')); - await fs.mkdir(directoryToCleanup, { recursive: true }); - if (!config.dataDirectory) { - config.dataDirectory = directoryToCleanup; - } - - if (!config.l1RpcUrls?.length) { - if (!isAnvilTestChain(chain.id)) { - throw new Error(`No ETHEREUM_HOSTS set but non anvil chain requested`); - } - if (AZTEC_NODE_URL) { - throw new Error( - `AZTEC_NODE_URL provided but no ETHEREUM_HOSTS set. Refusing to run, please set both variables so tests can deploy L1 contracts to the same Anvil instance`, - ); - } - - const res = await startAnvil({ - l1BlockTime: opts.ethereumSlotDuration, - accounts: opts.anvilAccounts, - port: opts.anvilPort, - }); - anvil = res.anvil; - config.l1RpcUrls = [res.rpcUrl]; - } - - // Enable logging metrics to a local file named after the test suite - if (isMetricsLoggingRequested()) { - const filename = path.join('log', getJobName() + '.jsonl'); - logger.info(`Logging metrics to ${filename}`); - setupMetricsLogger(filename); - } - - const dateProvider = new TestDateProvider(); - const ethCheatCodes = new EthCheatCodesWithState(config.l1RpcUrls, dateProvider); - - if (opts.stateLoad) { - await ethCheatCodes.loadChainState(opts.stateLoad); - } - - if (opts.l1StartTime) { - await ethCheatCodes.warp(opts.l1StartTime, { resetBlockInterval: true }); - } - - let publisherPrivKeyHex: `0x${string}` | undefined = undefined; - let publisherHdAccount: HDAccount | PrivateKeyAccount | undefined = undefined; - - if (opts.l1PublisherKey && opts.l1PublisherKey.getValue() && opts.l1PublisherKey.getValue() != NULL_KEY) { - publisherPrivKeyHex = opts.l1PublisherKey.getValue(); - publisherHdAccount = privateKeyToAccount(publisherPrivKeyHex); - } else if ( - config.publisherPrivateKeys && - config.publisherPrivateKeys.length > 0 && - config.publisherPrivateKeys[0].getValue() != NULL_KEY - ) { - publisherPrivKeyHex = config.publisherPrivateKeys[0].getValue(); - publisherHdAccount = privateKeyToAccount(publisherPrivKeyHex); - } else if (!MNEMONIC) { - throw new Error(`Mnemonic not provided and no publisher private key`); - } else { - publisherHdAccount = mnemonicToAccount(MNEMONIC, { addressIndex: 0 }); - const publisherPrivKeyRaw = publisherHdAccount.getHdKey().privateKey; - const publisherPrivKey = publisherPrivKeyRaw === null ? null : Buffer.from(publisherPrivKeyRaw); - publisherPrivKeyHex = `0x${publisherPrivKey!.toString('hex')}` as const; - config.publisherPrivateKeys = [new SecretValue(publisherPrivKeyHex)]; - } - - if (config.coinbase === undefined) { - config.coinbase = EthAddress.fromString(publisherHdAccount.address); - } - - if (AZTEC_NODE_URL) { - // we are setting up against a remote environment, l1 contracts are assumed to already be deployed - return await setupWithRemoteEnvironment(publisherHdAccount!, config, logger, numberOfAccounts); - } - - const initialFundedAccounts = - opts.initialFundedAccounts ?? - (await generateSchnorrAccounts(opts.numberOfInitialFundedAccounts ?? numberOfAccounts)); - const { genesisArchiveRoot, prefilledPublicData, fundingNeeded } = await getGenesisValues( - initialFundedAccounts.map(a => a.address), - opts.initialAccountFeeJuice, - opts.genesisPublicData, - ); - - const wasAutomining = await ethCheatCodes.isAutoMining(); - const enableAutomine = opts.automineL1Setup && !wasAutomining && isAnvilTestChain(chain.id); - if (enableAutomine) { - await ethCheatCodes.setAutomine(true); - } - - const l1Client = createExtendedL1Client(config.l1RpcUrls, publisherHdAccount!, chain); - - const deployL1ContractsValues: DeployAztecL1ContractsReturnType = await deployAztecL1Contracts( - config.l1RpcUrls[0], - publisherPrivKeyHex!, - chain.id, - { - ...getL1ContractsConfigEnvVars(), - ...opts, - vkTreeRoot: getVKTreeRoot(), - protocolContractsHash, - genesisArchiveRoot, - initialValidators: opts.initialValidators, - feeJuicePortalInitialBalance: fundingNeeded, - realVerifier: false, - }, - ); - - config.l1Contracts = deployL1ContractsValues.l1ContractAddresses; - config.rollupVersion = deployL1ContractsValues.rollupVersion; - - if (enableAutomine) { - await ethCheatCodes.setAutomine(false); - await ethCheatCodes.setIntervalMining(config.ethereumSlotDuration); - dateProvider.setTime((await ethCheatCodes.timestamp()) * 1000); - } - - if (opts.l2StartTime) { - // This should only be used in synching test or when you need to have a stable - // timestamp for the first l2 block. - await ethCheatCodes.warp(opts.l2StartTime, { resetBlockInterval: true }); - } - - const watcher = new AnvilTestWatcher( - new EthCheatCodesWithState(config.l1RpcUrls, dateProvider), - deployL1ContractsValues.l1ContractAddresses.rollupAddress, - deployL1ContractsValues.l1Client, - dateProvider, - ); - if (!opts.disableAnvilTestWatcher) { - await watcher.start(); - } - - const telemetry = await getTelemetryClient(opts.telemetryConfig); - - await setupSharedBlobStorage(config); - - logger.verbose('Creating and synching an aztec node', config); - - const acvmConfig = await getACVMConfig(logger); - if (acvmConfig) { - config.acvmWorkingDirectory = acvmConfig.acvmWorkingDirectory; - config.acvmBinaryPath = acvmConfig.acvmBinaryPath; - } - - const bbConfig = await getBBConfig(logger); - if (bbConfig) { - config.bbBinaryPath = bbConfig.bbBinaryPath; - config.bbWorkingDirectory = bbConfig.bbWorkingDirectory; - } - - let mockGossipSubNetwork: MockGossipSubNetwork | undefined; - let p2pClientDeps: P2PClientDeps | undefined = undefined; - - if (opts.mockGossipSubNetwork) { - mockGossipSubNetwork = new MockGossipSubNetwork(); - p2pClientDeps = { p2pServiceFactory: getMockPubSubP2PServiceFactory(mockGossipSubNetwork) }; - } - - // Transactions built against the genesis state must be included in block 1, otherwise they are dropped. - // To avoid test failures from dropped transactions, we ensure progression beyond genesis before proceeding. - // For account deployments, we set minTxsPerBlock=1 and deploy accounts sequentially for guaranteed success. - // If no accounts need deployment, we await an empty block to confirm network progression. After either path - // completes, we restore the original minTxsPerBlock setting. The deployment and waiting for empty block is - // handled by the if-else branches on line 632. - // For more details on why the tx would be dropped see `validate_include_by_timestamp` function in - // `noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/validation_requests.nr`. - // - // Note: If the following seems too convoluted or if it starts making problems, we could drop the "progressing - // past genesis via an account contract deployment" optimization and just call flush() on the sequencer and wait - // for an empty block to be mined. This would simplify it all quite a bit but the setup would be slower for tests - // deploying accounts. - const originalMinTxsPerBlock = config.minTxsPerBlock; - if (originalMinTxsPerBlock === undefined) { - throw new Error('minTxsPerBlock is undefined in e2e test setup'); - } - config.minTxsPerBlock = numberOfAccounts === 0 ? 0 : 1; - - config.p2pEnabled = opts.mockGossipSubNetwork || config.p2pEnabled; - config.p2pIp = opts.p2pIp ?? config.p2pIp ?? '127.0.0.1'; - - if (!config.disableValidator) { - if ((config.validatorPrivateKeys?.getValue().length ?? 0) === 0) { - config.validatorPrivateKeys = new SecretValue([generatePrivateKey()]); - } - } - - const aztecNode = await AztecNodeService.createAndSync( - config, // REFACTOR: createAndSync mutates this config - { dateProvider, telemetry, p2pClientDeps, logger: createLogger('node:MAIN-aztec-node') }, - { prefilledPublicData }, - ); - const sequencerClient = aztecNode.getSequencer(); - - if (sequencerClient) { - const publisher = (sequencerClient as TestSequencerClient).sequencer.publisher; - publisher.l1TxUtils = DelayedTxUtils.fromL1TxUtils(publisher.l1TxUtils, config.ethereumSlotDuration, l1Client); - } - - let proverNode: ProverNode | undefined = undefined; - if (opts.startProverNode) { - logger.verbose('Creating and syncing a simulated prover node...'); - const proverNodePrivateKey = getPrivateKeyFromIndex(2); - const proverNodePrivateKeyHex: Hex = `0x${proverNodePrivateKey!.toString('hex')}`; - const proverNodeDataDirectory = path.join(directoryToCleanup, randomBytes(8).toString('hex')); - const proverNodeConfig = { ...config.proverNodeConfig, dataDirectory: proverNodeDataDirectory }; - proverNode = await createAndSyncProverNode( - proverNodePrivateKeyHex, - config, - proverNodeConfig, - aztecNode, - prefilledPublicData, - ); - } - - logger.verbose('Creating a pxe...'); - const { wallet, teardown: pxeTeardown } = await setupPXEAndGetWallet(aztecNode!, pxeOpts, logger); - - const cheatCodes = await CheatCodes.create(config.l1RpcUrls, aztecNode, dateProvider); - - if ( - (opts.aztecTargetCommitteeSize && opts.aztecTargetCommitteeSize > 0) || - (opts.initialValidators && opts.initialValidators.length > 0) - ) { - // We need to advance such that the committee is set up. - await cheatCodes.rollup.advanceToEpoch( - EpochNumber.fromBigInt( - BigInt(await cheatCodes.rollup.getEpoch()) + BigInt(config.lagInEpochsForValidatorSet + 1), - ), - ); - await cheatCodes.rollup.setupEpoch(); - await cheatCodes.rollup.debugRollup(); - } - let accounts: AztecAddress[] = []; - // Below we continue with what we described in the long comment on line 571. - if (numberOfAccounts === 0) { - logger.info('No accounts are being deployed, waiting for an empty block 1 to be mined'); - while ((await aztecNode.getBlockNumber()) === 0) { - await sleep(2000); - } - } else { - logger.info( - `${numberOfAccounts} accounts are being deployed. Reliably progressing past genesis by setting minTxsPerBlock to 1 and waiting for the accounts to be deployed`, - ); - const accountsData = initialFundedAccounts.slice(0, numberOfAccounts); - const accountManagers = await deployFundedSchnorrAccounts(wallet, aztecNode, accountsData); - accounts = accountManagers.map(accountManager => accountManager.address); - } - - // Now we restore the original minTxsPerBlock setting. - sequencerClient!.getSequencer().updateConfig({ minTxsPerBlock: originalMinTxsPerBlock }); - - if (initialFundedAccounts.length < numberOfAccounts) { - // TODO: Create (numberOfAccounts - initialFundedAccounts.length) wallets without funds. - throw new Error( - `Unable to deploy ${numberOfAccounts} accounts. Only ${initialFundedAccounts.length} accounts were funded.`, - ); - } - - const teardown = async () => { - try { - await pxeTeardown(); - - await tryStop(aztecNode, logger); - await tryStop(proverNode, logger); - - if (acvmConfig?.cleanup) { - await acvmConfig.cleanup(); - } - - if (bbConfig?.cleanup) { - await bbConfig.cleanup(); - } - - await tryStop(watcher, logger); - await tryStop(anvil, logger); - - await tryRmDir(directoryToCleanup, logger); - } catch (err) { - logger.error(`Error during e2e test teardown`, err); - } - }; - - return { - aztecNode, - aztecNodeAdmin: aztecNode, - cheatCodes, - ethCheatCodes, - config, - dateProvider, - deployL1ContractsValues, - initialFundedAccounts, - logger, - mockGossipSubNetwork, - prefilledPublicData, - proverNode, - sequencer: sequencerClient, - teardown, - telemetryClient: telemetry, - wallet, - accounts, - watcher, - }; - } catch (err) { - // TODO: Just hoisted anvil for now to ensure cleanup. Prob need to hoist the rest. - await anvil?.stop(); - throw err; - } -} - -/** - * Registers the contract class used for test accounts and publicly deploys the instances requested. - * Use this when you need to make a public call to an account contract, such as for requesting a public authwit. - * @param sender - Wallet to send the deployment tx. - * @param accountsToDeploy - Which accounts to publicly deploy. - */ - -export async function ensureAccountContractsPublished(wallet: Wallet, accountsToDeploy: AztecAddress[]) { - // We have to check whether the accounts are already deployed. This can happen if the test runs against - // the local network and the test accounts exist - const accountsAndAddresses = await Promise.all( - accountsToDeploy.map(async address => { - return { - address, - deployed: (await wallet.getContractMetadata(address)).isContractPublished, - }; - }), - ); - const instances = ( - await Promise.all( - accountsAndAddresses - .filter(({ deployed }) => !deployed) - .map(({ address }) => wallet.getContractMetadata(address)), - ) - ).map(contractMetadata => contractMetadata.instance); - const contractClass = await getContractClassFromArtifact(SchnorrAccountContractArtifact); - if (!(await wallet.getContractClassMetadata(contractClass.id)).isContractClassPubliclyRegistered) { - await (await publishContractClass(wallet, SchnorrAccountContractArtifact)) - .send({ from: accountsToDeploy[0] }) - .wait(); - } - const requests = instances.map(instance => publishInstance(wallet, instance!)); - const batch = new BatchCall(wallet, requests); - await batch.send({ from: accountsToDeploy[0] }).wait(); -} - -/** Returns the job name for the current test. */ -function getJobName() { - return process.env.JOB_NAME ?? expect.getState().currentTestName?.split(' ')[0].replaceAll('/', '_') ?? 'unknown'; -} - -/** - * Returns a logger instance for the current test. - * @returns a logger instance for the current test. - */ -export function getLogger() { - const describeBlockName = expect.getState().currentTestName?.split(' ')[0].replaceAll('/', ':'); - if (!describeBlockName) { - const name = expect.getState().testPath?.split('/').pop()?.split('.')[0] ?? 'unknown'; - return createLogger('e2e:' + name); - } - return createLogger('e2e:' + describeBlockName); -} - -export type BalancesFn = ReturnType; -export function getBalancesFn( - symbol: string, - method: ContractMethod, - from: AztecAddress, - logger: any, -): (...addresses: (AztecAddress | { address: AztecAddress })[]) => Promise { - const balances = async (...addressLikes: (AztecAddress | { address: AztecAddress })[]) => { - const addresses = addressLikes.map(addressLike => ('address' in addressLike ? addressLike.address : addressLike)); - const b = await Promise.all(addresses.map(address => method(address).simulate({ from }))); - const debugString = `${symbol} balances: ${addresses.map((address, i) => `${address}: ${b[i]}`).join(', ')}`; - logger.verbose(debugString); - return b; - }; - - return balances; -} - -export async function expectMapping( - fn: (...k: K[]) => Promise, - inputs: K[], - expectedOutputs: V[], -): Promise { - expect(inputs.length).toBe(expectedOutputs.length); - - const outputs = await fn(...inputs); - - expect(outputs).toEqual(expectedOutputs); -} - -export async function expectMappingDelta( - initialValues: V[], - fn: (...k: K[]) => Promise, - inputs: K[], - expectedDiffs: V[], -): Promise { - expect(inputs.length).toBe(expectedDiffs.length); - - const outputs = await fn(...inputs); - const diffs = outputs.map((output, i) => output - initialValues[i]); - - expect(diffs).toEqual(expectedDiffs); -} - -/** - * Computes the address of the "canonical" SponsoredFPCContract. This is not a protocol contract - * but by conventions its address is computed with a salt of 0. - * @returns The address of the sponsored FPC contract - */ -export function getSponsoredFPCInstance(): Promise { - return Promise.resolve( - getContractInstanceFromInstantiationParams(SponsoredFPCContract.artifact, { - salt: new Fr(SPONSORED_FPC_SALT), - }), - ); -} - -/** - * Computes the address of the "canonical" SponsoredFPCContract. This is not a protocol contract - * but by conventions its address is computed with a salt of 0. - * @returns The address of the sponsored FPC contract - */ -export async function getSponsoredFPCAddress() { - const sponsoredFPCInstance = await getSponsoredFPCInstance(); - return sponsoredFPCInstance.address; -} - -/** - * Deploy a sponsored FPC contract to a running instance. - */ -export async function setupSponsoredFPC(wallet: Wallet) { - const instance = await getContractInstanceFromInstantiationParams(SponsoredFPCContract.artifact, { - salt: new Fr(SPONSORED_FPC_SALT), - }); - - await wallet.registerContract(instance, SponsoredFPCContract.artifact); - getLogger().info(`SponsoredFPC: ${instance.address}`); - return instance; -} - -/** - * Registers the SponsoredFPC in this PXE instance - * @param wallet - The wallet - */ -export async function registerSponsoredFPC(wallet: Wallet): Promise { - await wallet.registerContract(await getSponsoredFPCInstance(), SponsoredFPCContract.artifact); -} - -export async function waitForProvenChain(node: AztecNode, targetBlock?: BlockNumber, timeoutSec = 60, intervalSec = 1) { - targetBlock ??= await node.getBlockNumber(); - - await retryUntil( - async () => (await node.getProvenBlockNumber()) >= targetBlock, - 'proven chain status', - timeoutSec, - intervalSec, - ); -} - -export function createAndSyncProverNode( - proverNodePrivateKey: `0x${string}`, - aztecNodeConfig: AztecNodeConfig, - proverNodeConfig: Partial & Pick & { dontStart?: boolean }, - aztecNode: AztecNode | undefined, - prefilledPublicData: PublicDataTreeLeaf[] = [], - proverNodeDeps: ProverNodeDeps = {}, -) { - return withLogNameSuffix('prover-node', async () => { - // Disable stopping the aztec node as the prover coordination test will kill it otherwise - // This is only required when stopping the prover node for testing - const aztecNodeTxProvider = aztecNode && { - getTxByHash: aztecNode.getTxByHash.bind(aztecNode), - getTxsByHash: aztecNode.getTxsByHash.bind(aztecNode), - stop: () => Promise.resolve(), - }; - - const blobClient = await createBlobClientWithFileStores(aztecNodeConfig, createLogger('blob-client:prover-node')); - - // Creating temp store and archiver for simulated prover node - const archiverConfig = { ...aztecNodeConfig, dataDirectory: proverNodeConfig.dataDirectory }; - const archiver = await createArchiver( - archiverConfig, - { blobClient, dateProvider: proverNodeDeps.dateProvider }, - { blockUntilSync: true }, - ); - - // Prover node config is for simulated proofs - const proverConfig: ProverNodeConfig = { - ...aztecNodeConfig, - txCollectionNodeRpcUrls: [], - realProofs: false, - proverAgentCount: 2, - publisherPrivateKeys: [new SecretValue(proverNodePrivateKey)], - proverNodeMaxPendingJobs: 10, - proverNodeMaxParallelBlocksPerEpoch: 32, - proverNodePollingIntervalMs: 200, - txGatheringIntervalMs: 1000, - txGatheringBatchSize: 10, - txGatheringMaxParallelRequestsPerNode: 10, - txGatheringTimeoutMs: 24_000, - proverNodeFailedEpochStore: undefined, - proverId: EthAddress.fromNumber(1), - proverNodeEpochProvingDelayMs: undefined, - ...proverNodeConfig, - }; - - const l1TxUtils = createDelayedL1TxUtils( - aztecNodeConfig, - proverNodePrivateKey, - 'prover-node', - proverNodeDeps.dateProvider, - ); - - const proverNode = await createProverNode( - proverConfig, - { ...proverNodeDeps, aztecNodeTxProvider, archiver: archiver as Archiver, l1TxUtils }, - { prefilledPublicData }, - ); - getLogger().info(`Created and synced prover node`, { publisherAddress: l1TxUtils.client.account!.address }); - if (!proverNodeConfig.dontStart) { - await proverNode.start(); - } - return proverNode; - }); -} - -function createDelayedL1TxUtils( - aztecNodeConfig: AztecNodeConfig, - privateKey: `0x${string}`, - logName: string, - dateProvider?: DateProvider, -) { - const l1Client = createExtendedL1Client(aztecNodeConfig.l1RpcUrls, privateKey, foundry); - - const log = createLogger(logName); - const l1TxUtils = createDelayedL1TxUtilsFromViemWallet(l1Client, log, dateProvider, aztecNodeConfig); - l1TxUtils.enableDelayer(aztecNodeConfig.ethereumSlotDuration); - return l1TxUtils; -}