@aztec/aztec 0.0.1-commit.0c875d939 → 0.0.1-commit.0dc957cde

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/dest/bin/index.js +7 -3
  2. package/dest/cli/admin_api_key_store.d.ts +45 -0
  3. package/dest/cli/admin_api_key_store.d.ts.map +1 -0
  4. package/dest/cli/admin_api_key_store.js +98 -0
  5. package/dest/cli/aztec_start_action.d.ts +1 -1
  6. package/dest/cli/aztec_start_action.d.ts.map +1 -1
  7. package/dest/cli/aztec_start_action.js +72 -18
  8. package/dest/cli/aztec_start_options.d.ts +2 -2
  9. package/dest/cli/aztec_start_options.d.ts.map +1 -1
  10. package/dest/cli/aztec_start_options.js +38 -9
  11. package/dest/cli/cli.d.ts +1 -1
  12. package/dest/cli/cli.d.ts.map +1 -1
  13. package/dest/cli/cli.js +3 -4
  14. package/dest/cli/cmds/compile.d.ts +4 -0
  15. package/dest/cli/cmds/compile.d.ts.map +1 -0
  16. package/dest/cli/cmds/compile.js +155 -0
  17. package/dest/cli/cmds/profile.d.ts +4 -0
  18. package/dest/cli/cmds/profile.d.ts.map +1 -0
  19. package/dest/cli/cmds/profile.js +8 -0
  20. package/dest/cli/cmds/profile_flamegraph.d.ts +4 -0
  21. package/dest/cli/cmds/profile_flamegraph.d.ts.map +1 -0
  22. package/dest/cli/cmds/profile_flamegraph.js +52 -0
  23. package/dest/cli/cmds/profile_gates.d.ts +4 -0
  24. package/dest/cli/cmds/profile_gates.d.ts.map +1 -0
  25. package/dest/cli/cmds/profile_gates.js +58 -0
  26. package/dest/cli/cmds/profile_utils.d.ts +18 -0
  27. package/dest/cli/cmds/profile_utils.d.ts.map +1 -0
  28. package/dest/cli/cmds/profile_utils.js +50 -0
  29. package/dest/cli/cmds/standby.d.ts +32 -0
  30. package/dest/cli/cmds/standby.d.ts.map +1 -0
  31. package/dest/cli/cmds/standby.js +97 -0
  32. package/dest/cli/cmds/start_archiver.d.ts +2 -2
  33. package/dest/cli/cmds/start_archiver.d.ts.map +1 -1
  34. package/dest/cli/cmds/start_archiver.js +4 -2
  35. package/dest/cli/cmds/start_node.d.ts +3 -2
  36. package/dest/cli/cmds/start_node.d.ts.map +1 -1
  37. package/dest/cli/cmds/start_node.js +69 -29
  38. package/dest/cli/cmds/start_prover_agent.d.ts +1 -1
  39. package/dest/cli/cmds/start_prover_agent.d.ts.map +1 -1
  40. package/dest/cli/cmds/start_prover_agent.js +3 -15
  41. package/dest/cli/cmds/start_prover_broker.d.ts +1 -1
  42. package/dest/cli/cmds/start_prover_broker.d.ts.map +1 -1
  43. package/dest/cli/cmds/start_prover_broker.js +12 -6
  44. package/dest/cli/cmds/utils/artifacts.d.ts +21 -0
  45. package/dest/cli/cmds/utils/artifacts.d.ts.map +1 -0
  46. package/dest/cli/cmds/utils/artifacts.js +24 -0
  47. package/dest/cli/cmds/utils/collect_crate_dirs.d.ts +21 -0
  48. package/dest/cli/cmds/utils/collect_crate_dirs.d.ts.map +1 -0
  49. package/dest/cli/cmds/utils/collect_crate_dirs.js +114 -0
  50. package/dest/cli/cmds/utils/needs_recompile.d.ts +10 -0
  51. package/dest/cli/cmds/utils/needs_recompile.d.ts.map +1 -0
  52. package/dest/cli/cmds/utils/needs_recompile.js +90 -0
  53. package/dest/cli/cmds/utils/spawn.d.ts +3 -0
  54. package/dest/cli/cmds/utils/spawn.d.ts.map +1 -0
  55. package/dest/cli/cmds/utils/spawn.js +16 -0
  56. package/dest/cli/cmds/utils/warn_if_aztec_version_mismatch.d.ts +4 -0
  57. package/dest/cli/cmds/utils/warn_if_aztec_version_mismatch.d.ts.map +1 -0
  58. package/dest/cli/cmds/utils/warn_if_aztec_version_mismatch.js +62 -0
  59. package/dest/cli/util.d.ts +3 -5
  60. package/dest/cli/util.d.ts.map +1 -1
  61. package/dest/cli/util.js +40 -81
  62. package/dest/examples/token.js +4 -4
  63. package/dest/local-network/banana_fpc.js +1 -1
  64. package/dest/local-network/local-network.d.ts +5 -4
  65. package/dest/local-network/local-network.d.ts.map +1 -1
  66. package/dest/local-network/local-network.js +46 -12
  67. package/dest/testing/anvil_test_watcher.d.ts +15 -3
  68. package/dest/testing/anvil_test_watcher.d.ts.map +1 -1
  69. package/dest/testing/anvil_test_watcher.js +85 -22
  70. package/dest/testing/cheat_codes.d.ts +11 -15
  71. package/dest/testing/cheat_codes.d.ts.map +1 -1
  72. package/dest/testing/cheat_codes.js +34 -32
  73. package/dest/testing/epoch_test_settler.d.ts +1 -1
  74. package/dest/testing/epoch_test_settler.d.ts.map +1 -1
  75. package/dest/testing/epoch_test_settler.js +3 -4
  76. package/dest/testing/index.d.ts +3 -2
  77. package/dest/testing/index.d.ts.map +1 -1
  78. package/dest/testing/index.js +1 -0
  79. package/dest/testing/token_allowed_setup.d.ts +7 -0
  80. package/dest/testing/token_allowed_setup.d.ts.map +1 -0
  81. package/dest/testing/token_allowed_setup.js +20 -0
  82. package/package.json +34 -34
  83. package/scripts/add_crate.sh +102 -0
  84. package/scripts/aztec.sh +15 -5
  85. package/scripts/init.sh +23 -19
  86. package/scripts/new.sh +48 -24
  87. package/scripts/setup_workspace.sh +68 -0
  88. package/src/bin/index.ts +7 -3
  89. package/src/cli/admin_api_key_store.ts +128 -0
  90. package/src/cli/aztec_start_action.ts +65 -14
  91. package/src/cli/aztec_start_options.ts +46 -10
  92. package/src/cli/cli.ts +3 -4
  93. package/src/cli/cmds/compile.ts +178 -0
  94. package/src/cli/cmds/profile.ts +25 -0
  95. package/src/cli/cmds/profile_flamegraph.ts +64 -0
  96. package/src/cli/cmds/profile_gates.ts +68 -0
  97. package/src/cli/cmds/profile_utils.ts +58 -0
  98. package/src/cli/cmds/standby.ts +132 -0
  99. package/src/cli/cmds/start_archiver.ts +8 -2
  100. package/src/cli/cmds/start_node.ts +81 -33
  101. package/src/cli/cmds/start_prover_agent.ts +3 -6
  102. package/src/cli/cmds/start_prover_broker.ts +14 -14
  103. package/src/cli/cmds/utils/artifacts.ts +44 -0
  104. package/src/cli/cmds/utils/collect_crate_dirs.ts +118 -0
  105. package/src/cli/cmds/utils/needs_recompile.ts +98 -0
  106. package/src/cli/cmds/utils/spawn.ts +16 -0
  107. package/src/cli/cmds/utils/warn_if_aztec_version_mismatch.ts +77 -0
  108. package/src/cli/util.ts +43 -76
  109. package/src/examples/token.ts +6 -4
  110. package/src/local-network/banana_fpc.ts +1 -1
  111. package/src/local-network/local-network.ts +56 -15
  112. package/src/testing/anvil_test_watcher.ts +95 -20
  113. package/src/testing/cheat_codes.ts +42 -36
  114. package/src/testing/epoch_test_settler.ts +3 -4
  115. package/src/testing/index.ts +2 -1
  116. package/src/testing/token_allowed_setup.ts +19 -0
  117. package/dest/cli/cmds/start_prover_node.d.ts +0 -7
  118. package/dest/cli/cmds/start_prover_node.d.ts.map +0 -1
  119. package/dest/cli/cmds/start_prover_node.js +0 -108
  120. package/dest/cli/release_version.d.ts +0 -2
  121. package/dest/cli/release_version.d.ts.map +0 -1
  122. package/dest/cli/release_version.js +0 -14
  123. package/scripts/compile.sh +0 -44
  124. package/scripts/extract_function.js +0 -47
  125. package/scripts/flamegraph.sh +0 -59
  126. package/scripts/setup_project.sh +0 -31
  127. package/src/cli/cmds/start_prover_node.ts +0 -124
  128. package/src/cli/release_version.ts +0 -21
package/src/cli/util.ts CHANGED
@@ -1,17 +1,18 @@
1
1
  import type { AztecNodeConfig } from '@aztec/aztec-node';
2
2
  import type { AccountManager } from '@aztec/aztec.js/wallet';
3
+ import { getNetworkConfig } from '@aztec/cli/config';
4
+ import { RegistryContract } from '@aztec/ethereum/contracts';
3
5
  import type { ViemClient } from '@aztec/ethereum/types';
4
- import type { ConfigMappingsType } from '@aztec/foundation/config';
5
- import { EthAddress } from '@aztec/foundation/eth-address';
6
+ import type { ConfigMappingsType, NetworkNames } from '@aztec/foundation/config';
6
7
  import { jsonStringify } from '@aztec/foundation/json-rpc';
7
8
  import { type LogFn, createLogger } from '@aztec/foundation/log';
8
- import type { SharedNodeConfig } from '@aztec/node-lib/config';
9
9
  import type { ProverConfig } from '@aztec/stdlib/interfaces/server';
10
- import { getTelemetryClient } from '@aztec/telemetry-client/start';
10
+ import { type VersionCheck, getPackageVersion } from '@aztec/stdlib/update-checker';
11
11
  import type { EmbeddedWallet } from '@aztec/wallets/embedded';
12
12
 
13
13
  import chalk from 'chalk';
14
14
  import type { Command } from 'commander';
15
+ import type { Hex } from 'viem';
15
16
 
16
17
  import { type AztecStartOption, aztecStartOptions } from './aztec_start_options.js';
17
18
 
@@ -271,7 +272,7 @@ export async function preloadCrsDataForVerifying(
271
272
  ): Promise<void> {
272
273
  if (realProofs) {
273
274
  const { Crs, GrumpkinCrs } = await import('@aztec/bb.js');
274
- await Promise.all([Crs.new(2 ** 1, undefined, log), GrumpkinCrs.new(2 ** 16 + 1, undefined, log)]);
275
+ await Promise.all([Crs.new(2 ** 1, undefined, log), GrumpkinCrs.new(2 ** 16, undefined, log)]);
275
276
  }
276
277
  }
277
278
 
@@ -286,96 +287,62 @@ export async function preloadCrsDataForServerSideProving(
286
287
  ): Promise<void> {
287
288
  if (realProofs) {
288
289
  const { Crs, GrumpkinCrs } = await import('@aztec/bb.js');
289
- await Promise.all([Crs.new(2 ** 25 + 1, undefined, log), GrumpkinCrs.new(2 ** 18 + 1, undefined, log)]);
290
+ await Promise.all([Crs.new(2 ** 25, undefined, log), GrumpkinCrs.new(2 ** 18, undefined, log)]);
290
291
  }
291
292
  }
292
293
 
293
- export async function setupUpdateMonitor(
294
- autoUpdateMode: SharedNodeConfig['autoUpdate'],
295
- updatesLocation: URL,
294
+ export async function setupVersionChecker(
295
+ network: NetworkNames,
296
296
  followsCanonicalRollup: boolean,
297
297
  publicClient: ViemClient,
298
- registryContractAddress: EthAddress,
299
298
  signalHandlers: Array<() => Promise<void>>,
300
- updateNodeConfig?: (config: object) => Promise<void>,
301
- ) {
302
- const logger = createLogger('update-check');
303
- const { UpdateChecker } = await import('@aztec/stdlib/update-checker');
304
- const checker = await UpdateChecker.new({
305
- baseURL: updatesLocation,
306
- publicClient,
307
- registryContractAddress,
308
- });
299
+ cacheDir?: string,
300
+ ): Promise<void> {
301
+ const networkConfig = await getNetworkConfig(network, cacheDir);
302
+ if (!networkConfig) {
303
+ return;
304
+ }
309
305
 
310
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
311
- checker.on('newRollupVersion', async ({ latestVersion, currentVersion }) => {
312
- if (isShuttingDown()) {
313
- return;
314
- }
306
+ const { VersionChecker } = await import('@aztec/stdlib/update-checker');
315
307
 
316
- // if node follows canonical rollup then this is equivalent to a config update
317
- if (!followsCanonicalRollup) {
318
- return;
319
- }
308
+ const logger = createLogger('version_check');
309
+ const registry = new RegistryContract(publicClient, networkConfig.registryAddress as Hex);
320
310
 
321
- if (autoUpdateMode === 'config' || autoUpdateMode === 'config-and-version') {
322
- logger.info(`New rollup version detected. Please restart the node`, { latestVersion, currentVersion });
323
- await shutdown(logger.info, ExitCode.ROLLUP_UPGRADE, signalHandlers);
324
- } else if (autoUpdateMode === 'notify') {
325
- logger.warn(`New rollup detected. Please restart the node`, { latestVersion, currentVersion });
326
- }
311
+ const checks: Array<VersionCheck> = [];
312
+ checks.push({
313
+ name: 'node',
314
+ currentVersion: getPackageVersion() ?? 'unknown',
315
+ getLatestVersion: async () => {
316
+ const cfg = await getNetworkConfig(network, cacheDir);
317
+ return cfg?.nodeVersion;
318
+ },
327
319
  });
328
320
 
329
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
330
- checker.on('newNodeVersion', async ({ latestVersion, currentVersion }) => {
331
- if (isShuttingDown()) {
332
- return;
333
- }
334
- if (autoUpdateMode === 'config-and-version') {
335
- logger.info(`New node version detected. Please update and restart the node`, { latestVersion, currentVersion });
336
- await shutdown(logger.info, ExitCode.VERSION_UPGRADE, signalHandlers);
337
- } else if (autoUpdateMode === 'notify') {
338
- logger.info(`New node version detected. Please update and restart the node`, { latestVersion, currentVersion });
321
+ if (followsCanonicalRollup) {
322
+ const getLatestVersion = async () => {
323
+ const version = (await registry.getRollupVersions()).at(-1);
324
+ return version !== undefined ? String(version) : undefined;
325
+ };
326
+ const currentVersion = await getLatestVersion();
327
+ if (currentVersion !== undefined) {
328
+ checks.push({
329
+ name: 'rollup',
330
+ currentVersion,
331
+ getLatestVersion,
332
+ });
339
333
  }
340
- });
334
+ }
341
335
 
342
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
343
- checker.on('updateNodeConfig', async config => {
336
+ const checker = new VersionChecker(checks, 600_000, logger);
337
+ checker.on('newVersion', ({ name, latestVersion, currentVersion }) => {
344
338
  if (isShuttingDown()) {
345
339
  return;
346
340
  }
347
341
 
348
- if ((autoUpdateMode === 'config' || autoUpdateMode === 'config-and-version') && updateNodeConfig) {
349
- logger.warn(`Config change detected. Updating node`, config);
350
- try {
351
- await updateNodeConfig(config);
352
- } catch (err) {
353
- logger.warn('Failed to update config', { err });
354
- }
355
- }
356
- // don't notify on these config changes
357
- });
358
-
359
- checker.on('updatePublicTelemetryConfig', config => {
360
- if (autoUpdateMode === 'config' || autoUpdateMode === 'config-and-version') {
361
- logger.warn(`Public telemetry config change detected. Updating telemetry client`, config);
362
- try {
363
- const publicIncludeMetrics: unknown = (config as any).publicIncludeMetrics;
364
- if (Array.isArray(publicIncludeMetrics) && publicIncludeMetrics.every(m => typeof m === 'string')) {
365
- getTelemetryClient().setExportedPublicTelemetry(publicIncludeMetrics);
366
- }
367
- const publicMetricsCollectFrom: unknown = (config as any).publicMetricsCollectFrom;
368
- if (Array.isArray(publicMetricsCollectFrom) && publicMetricsCollectFrom.every(m => typeof m === 'string')) {
369
- getTelemetryClient().setPublicTelemetryCollectFrom(publicMetricsCollectFrom);
370
- }
371
- } catch (err) {
372
- logger.warn('Failed to update config', { err });
373
- }
374
- }
375
- // don't notify on these config changes
342
+ logger.warn(`New ${name} version available`, { latestVersion, currentVersion });
376
343
  });
377
-
378
344
  checker.start();
345
+ signalHandlers.push(() => checker.stop());
379
346
  }
380
347
 
381
348
  export function stringifyConfig(config: object): string {
@@ -32,7 +32,9 @@ async function main() {
32
32
  logger.info(`Fetched Alice and Bob accounts: ${alice.toString()}, ${bob.toString()}`);
33
33
 
34
34
  logger.info('Deploying Token...');
35
- const token = await TokenContract.deploy(wallet, alice, 'TokenName', 'TokenSymbol', 18).send({ from: alice });
35
+ const { contract: token } = await TokenContract.deploy(wallet, alice, 'TokenName', 'TokenSymbol', 18).send({
36
+ from: alice,
37
+ });
36
38
  logger.info('Token deployed');
37
39
 
38
40
  // Mint tokens to Alice
@@ -41,7 +43,7 @@ async function main() {
41
43
 
42
44
  logger.info(`${ALICE_MINT_BALANCE} tokens were successfully minted by Alice and transferred to private`);
43
45
 
44
- const balanceAfterMint = await token.methods.balance_of_private(alice).simulate({ from: alice });
46
+ const { result: balanceAfterMint } = await token.methods.balance_of_private(alice).simulate({ from: alice });
45
47
  logger.info(`Tokens successfully minted. New Alice's balance: ${balanceAfterMint}`);
46
48
 
47
49
  // We will now transfer tokens from Alice to Bob
@@ -49,10 +51,10 @@ async function main() {
49
51
  await token.methods.transfer(bob, TRANSFER_AMOUNT).send({ from: alice });
50
52
 
51
53
  // Check the new balances
52
- const aliceBalance = await token.methods.balance_of_private(alice).simulate({ from: alice });
54
+ const { result: aliceBalance } = await token.methods.balance_of_private(alice).simulate({ from: alice });
53
55
  logger.info(`Alice's balance ${aliceBalance}`);
54
56
 
55
- const bobBalance = await token.methods.balance_of_private(bob).simulate({ from: bob });
57
+ const { result: bobBalance } = await token.methods.balance_of_private(bob).simulate({ from: bob });
56
58
  logger.info(`Bob's balance ${bobBalance}`);
57
59
  }
58
60
 
@@ -48,7 +48,7 @@ export async function getBananaFPCAddress(initialAccounts: InitialAccountData[])
48
48
  export async function setupBananaFPC(initialAccounts: InitialAccountData[], wallet: Wallet, log: LogFn) {
49
49
  const bananaCoinAddress = await getBananaCoinAddress(initialAccounts);
50
50
  const admin = getBananaAdmin(initialAccounts);
51
- const [bananaCoin, fpc] = await Promise.all([
51
+ const [{ contract: bananaCoin }, { contract: fpc }] = await Promise.all([
52
52
  TokenContract.deploy(wallet, admin, bananaCoinArgs.name, bananaCoinArgs.symbol, bananaCoinArgs.decimal).send({
53
53
  from: admin,
54
54
  contractAddressSalt: BANANA_COIN_SALT,
@@ -18,7 +18,10 @@ import type { LogFn } from '@aztec/foundation/log';
18
18
  import { DateProvider, TestDateProvider } from '@aztec/foundation/timer';
19
19
  import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
20
20
  import { protocolContractsHash } from '@aztec/protocol-contracts';
21
- import type { PublicDataTreeLeaf } from '@aztec/stdlib/trees';
21
+ import { SequencerState } from '@aztec/sequencer-client';
22
+ import { AztecAddress } from '@aztec/stdlib/aztec-address';
23
+ import type { ProvingJobBroker } from '@aztec/stdlib/interfaces/server';
24
+ import type { GenesisData } from '@aztec/stdlib/world-state';
22
25
  import {
23
26
  type TelemetryClient,
24
27
  getConfigEnvVars as getTelemetryClientConfig,
@@ -36,6 +39,7 @@ import { createAccountLogs } from '../cli/util.js';
36
39
  import { DefaultMnemonic } from '../mnemonic.js';
37
40
  import { AnvilTestWatcher } from '../testing/anvil_test_watcher.js';
38
41
  import { EpochTestSettler } from '../testing/epoch_test_settler.js';
42
+ import { getTokenAllowedSetupFunctions } from '../testing/token_allowed_setup.js';
39
43
  import { getBananaFPCAddress, setupBananaFPC } from './banana_fpc.js';
40
44
  import { getSponsoredFPCAddress } from './sponsored_fpc.js';
41
45
 
@@ -66,7 +70,7 @@ export async function deployContractsToL1(
66
70
  genesisArchiveRoot: opts.genesisArchiveRoot ?? new Fr(GENESIS_ARCHIVE_ROOT),
67
71
  feeJuicePortalInitialBalance: opts.feeJuicePortalInitialBalance,
68
72
  aztecTargetCommitteeSize: 0, // no committee in local network
69
- slasherFlavor: 'none', // no slashing in local network
73
+ slasherEnabled: false, // no slashing in local network
70
74
  realVerifier: false,
71
75
  });
72
76
 
@@ -99,18 +103,25 @@ export async function createLocalNetwork(config: Partial<LocalNetworkConfig> = {
99
103
  logger.warn(`Multiple L1 RPC URLs provided. Local networks will only use the first one: ${l1RpcUrl}`);
100
104
  }
101
105
 
106
+ // The local network deploys a banana FPC with Token contracts, so include Token entries
107
+ // in the setup allowlist so FPC-based fee payments work out of the box.
108
+ const tokenAllowList = await getTokenAllowedSetupFunctions();
109
+
102
110
  const aztecNodeConfig: AztecNodeConfig = {
103
111
  ...getConfigEnvVars(),
104
112
  ...config,
113
+ txPublicSetupAllowListExtend: [...tokenAllowList, ...(config.txPublicSetupAllowListExtend ?? [])],
105
114
  };
106
115
  const hdAccount = mnemonicToAccount(config.l1Mnemonic || DefaultMnemonic);
107
116
  if (
108
- aztecNodeConfig.publisherPrivateKeys == undefined ||
109
- !aztecNodeConfig.publisherPrivateKeys.length ||
110
- aztecNodeConfig.publisherPrivateKeys[0].getValue() === NULL_KEY
117
+ aztecNodeConfig.sequencerPublisherPrivateKeys == undefined ||
118
+ !aztecNodeConfig.sequencerPublisherPrivateKeys.length ||
119
+ aztecNodeConfig.sequencerPublisherPrivateKeys[0].getValue() === NULL_KEY
111
120
  ) {
112
121
  const privKey = hdAccount.getHdKey().privateKey;
113
- aztecNodeConfig.publisherPrivateKeys = [new SecretValue(`0x${Buffer.from(privKey!).toString('hex')}` as const)];
122
+ aztecNodeConfig.sequencerPublisherPrivateKeys = [
123
+ new SecretValue(`0x${Buffer.from(privKey!).toString('hex')}` as const),
124
+ ];
114
125
  }
115
126
  if (!aztecNodeConfig.validatorPrivateKeys?.getValue().length) {
116
127
  const privKey = hdAccount.getHdKey().privateKey;
@@ -134,10 +145,13 @@ export async function createLocalNetwork(config: Partial<LocalNetworkConfig> = {
134
145
 
135
146
  const bananaFPC = await getBananaFPCAddress(initialAccounts);
136
147
  const sponsoredFPC = await getSponsoredFPCAddress();
137
- const fundedAddresses = initialAccounts.length
138
- ? [...initialAccounts.map(a => a.address), bananaFPC, sponsoredFPC]
139
- : [];
140
- const { genesisArchiveRoot, prefilledPublicData, fundingNeeded } = await getGenesisValues(fundedAddresses);
148
+ const prefundAddresses = (aztecNodeConfig.prefundAddresses ?? []).map(a => AztecAddress.fromString(a));
149
+ const fundedAddresses = [
150
+ ...initialAccounts.map(a => a.address),
151
+ ...(initialAccounts.length ? [bananaFPC, sponsoredFPC] : []),
152
+ ...prefundAddresses,
153
+ ];
154
+ const { genesisArchiveRoot, genesis, fundingNeeded } = await getGenesisValues(fundedAddresses);
141
155
 
142
156
  const dateProvider = new TestDateProvider();
143
157
 
@@ -176,7 +190,22 @@ export async function createLocalNetwork(config: Partial<LocalNetworkConfig> = {
176
190
  const telemetry = await initTelemetryClient(getTelemetryClientConfig());
177
191
  // Create a local blob client client inside the local network, no http connectivity
178
192
  const blobClient = createBlobClient();
179
- const node = await createAztecNode(aztecNodeConfig, { telemetry, blobClient, dateProvider }, { prefilledPublicData });
193
+ const node = await createAztecNode(aztecNodeConfig, { telemetry, blobClient, dateProvider }, { genesis });
194
+
195
+ // Now that the node is up, let the watcher check for pending txs so it can skip unfilled slots faster when
196
+ // transactions are waiting in the mempool. Also let it check if the sequencer is actively building, to avoid
197
+ // warping time out from under an in-progress block.
198
+ watcher?.setGetPendingTxCount(() => node.getPendingTxCount());
199
+ const sequencer = node.getSequencer()?.getSequencer();
200
+ if (sequencer) {
201
+ const idleStates: Set<string> = new Set([
202
+ SequencerState.STOPPED,
203
+ SequencerState.STOPPING,
204
+ SequencerState.IDLE,
205
+ SequencerState.SYNCHRONIZING,
206
+ ]);
207
+ watcher?.setIsSequencerBuilding(() => !idleStates.has(sequencer.getState()));
208
+ }
180
209
 
181
210
  let epochTestSettler: EpochTestSettler | undefined;
182
211
  if (!aztecNodeConfig.p2pEnabled) {
@@ -191,7 +220,10 @@ export async function createLocalNetwork(config: Partial<LocalNetworkConfig> = {
191
220
  }
192
221
 
193
222
  if (initialAccounts.length) {
194
- const wallet = await EmbeddedWallet.create(node, { pxeConfig: { proverEnabled: aztecNodeConfig.realProofs } });
223
+ const wallet = await EmbeddedWallet.create(node, {
224
+ pxeConfig: { proverEnabled: aztecNodeConfig.realProofs },
225
+ ephemeral: true,
226
+ });
195
227
 
196
228
  userLog('Setting up funded test accounts...');
197
229
  const accountManagers = await deployFundedSchnorrAccounts(wallet, initialAccounts);
@@ -221,8 +253,13 @@ export async function createLocalNetwork(config: Partial<LocalNetworkConfig> = {
221
253
  */
222
254
  export async function createAztecNode(
223
255
  config: Partial<AztecNodeConfig> = {},
224
- deps: { telemetry?: TelemetryClient; blobClient?: BlobClientInterface; dateProvider?: DateProvider } = {},
225
- options: { prefilledPublicData?: PublicDataTreeLeaf[] } = {},
256
+ deps: {
257
+ telemetry?: TelemetryClient;
258
+ blobClient?: BlobClientInterface;
259
+ dateProvider?: DateProvider;
260
+ proverBroker?: ProvingJobBroker;
261
+ } = {},
262
+ options: { genesis?: GenesisData } = {},
226
263
  ) {
227
264
  // TODO(#12272): will clean this up. This is criminal.
228
265
  const { l1Contracts, ...rest } = getConfigEnvVars();
@@ -231,6 +268,10 @@ export async function createAztecNode(
231
268
  ...config,
232
269
  l1Contracts: { ...l1Contracts, ...config.l1Contracts },
233
270
  };
234
- const node = await AztecNodeService.createAndSync(aztecNodeConfig, deps, options);
271
+ const node = await AztecNodeService.createAndSync(
272
+ aztecNodeConfig,
273
+ { ...deps, proverNodeDeps: { broker: deps.proverBroker } },
274
+ options,
275
+ );
235
276
  return node;
236
277
  }
@@ -9,6 +9,11 @@ import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
9
9
 
10
10
  import { type GetContractReturnType, getAddress, getContract } from 'viem';
11
11
 
12
+ export type AnvilTestWatcherOpts = {
13
+ isLocalNetwork?: boolean;
14
+ isMarkingAsProven?: boolean;
15
+ };
16
+
12
17
  /**
13
18
  * Represents a watcher for a rollup contract.
14
19
  *
@@ -17,7 +22,8 @@ import { type GetContractReturnType, getAddress, getContract } from 'viem';
17
22
  * block within the slot. And if so, it will time travel into the next slot.
18
23
  */
19
24
  export class AnvilTestWatcher {
20
- private isLocalNetwork: boolean = false;
25
+ private isLocalNetwork;
26
+ private isMarkingAsProven;
21
27
 
22
28
  private rollup: GetContractReturnType<typeof RollupAbi, ViemClient>;
23
29
  private rollupCheatCodes: RollupCheatCodes;
@@ -29,13 +35,21 @@ export class AnvilTestWatcher {
29
35
 
30
36
  private logger: Logger = createLogger(`aztecjs:utils:watcher`);
31
37
 
32
- private isMarkingAsProven = true;
38
+ // Optional callback to check if there are pending txs in the mempool.
39
+ private getPendingTxCount?: () => Promise<number>;
40
+
41
+ // Optional callback to check if the sequencer is actively building a block.
42
+ private isSequencerBuilding?: () => boolean;
43
+
44
+ // Tracks when we first observed the current unfilled slot with pending txs (real wall time).
45
+ private unfilledSlotFirstSeen?: { slot: number; realTime: number };
33
46
 
34
47
  constructor(
35
48
  private cheatcodes: EthCheatCodes,
36
49
  rollupAddress: EthAddress,
37
50
  l1Client: ViemClient,
38
51
  private dateProvider?: TestDateProvider,
52
+ opts: AnvilTestWatcherOpts = {},
39
53
  ) {
40
54
  this.rollup = getContract({
41
55
  address: getAddress(rollupAddress.toString()),
@@ -47,6 +61,9 @@ export class AnvilTestWatcher {
47
61
  rollupAddress,
48
62
  });
49
63
 
64
+ this.isLocalNetwork = opts.isLocalNetwork ?? false;
65
+ this.isMarkingAsProven = opts.isMarkingAsProven ?? true;
66
+
50
67
  this.logger.debug(`Watcher created for rollup at ${rollupAddress}`);
51
68
  }
52
69
 
@@ -59,6 +76,16 @@ export class AnvilTestWatcher {
59
76
  this.isLocalNetwork = isLocalNetwork;
60
77
  }
61
78
 
79
+ /** Sets a callback to check for pending txs, used to skip unfilled slots faster when txs are waiting. */
80
+ setGetPendingTxCount(fn: () => Promise<number>) {
81
+ this.getPendingTxCount = fn;
82
+ }
83
+
84
+ /** Sets a callback to check if the sequencer is actively building, to avoid warping while it works. */
85
+ setIsSequencerBuilding(fn: () => boolean) {
86
+ this.isSequencerBuilding = fn;
87
+ }
88
+
62
89
  async start() {
63
90
  if (this.filledRunningPromise) {
64
91
  throw new Error('Watcher already watching for filled slot');
@@ -111,14 +138,21 @@ export class AnvilTestWatcher {
111
138
  return;
112
139
  }
113
140
 
114
- const l1Time = (await this.cheatcodes.timestamp()) * 1000;
141
+ const l1Time = (await this.cheatcodes.lastBlockTimestamp()) * 1000;
115
142
  const wallTime = this.dateProvider.now();
116
143
  if (l1Time > wallTime) {
117
144
  this.logger.warn(`L1 is ahead of wall time. Syncing wall time to L1 time`);
118
145
  this.dateProvider.setTime(l1Time);
119
146
  } else if (l1Time + Number(this.l2SlotDuration) * 1000 < wallTime) {
120
- this.logger.warn(`L1 is more than 1 L2 slot behind wall time. Warping to wall time`);
121
- await this.cheatcodes.warp(Math.ceil(wallTime / 1000));
147
+ // Warp L1 to the slot boundary at-or-before wall time. Rounding to a slot boundary (rather than
148
+ // `ceil(wallTime / 1000)`) keeps this loop's target aligned with `warpTimeIfNeeded`'s
149
+ // `nextSlotTimestamp` target, avoiding a race where the two loops pick timestamps a fraction of
150
+ // a second apart and one of them is then rejected by anvil as non-monotonic.
151
+ const wallSec = Math.floor(wallTime / 1000);
152
+ const targetSlot = await this.rollup.read.getSlotAt([BigInt(wallSec)]);
153
+ const targetTimestamp = Number(await this.rollup.read.getTimestampForSlot([targetSlot]));
154
+ this.logger.warn(`L1 is more than 1 L2 slot behind wall time. Warping to slot ${targetSlot} boundary`);
155
+ await this.warpToTimestamp(targetTimestamp);
122
156
  }
123
157
  }
124
158
 
@@ -131,16 +165,10 @@ export class AnvilTestWatcher {
131
165
  const nextSlotTimestamp = Number(await this.rollup.read.getTimestampForSlot([BigInt(nextSlot)]));
132
166
 
133
167
  if (BigInt(currentSlot) === checkpointLog.slotNumber) {
134
- // We should jump to the next slot
135
- try {
136
- await this.cheatcodes.warp(nextSlotTimestamp, {
137
- resetBlockInterval: true,
138
- });
139
- } catch (e) {
140
- this.logger.error(`Failed to warp to timestamp ${nextSlotTimestamp}: ${e}`);
168
+ // The current slot has been filled, we should jump to the next slot.
169
+ if (await this.warpToTimestamp(nextSlotTimestamp)) {
170
+ this.logger.info(`Slot ${currentSlot} was filled, jumped to next slot`);
141
171
  }
142
-
143
- this.logger.info(`Slot ${currentSlot} was filled, jumped to next slot`);
144
172
  return;
145
173
  }
146
174
 
@@ -149,18 +177,65 @@ export class AnvilTestWatcher {
149
177
  return;
150
178
  }
151
179
 
180
+ // If there are pending txs and the sequencer missed them, warp quickly (after a 2s real-time debounce) so the
181
+ // sequencer can retry in the next slot. Without this, we'd have to wait a full real-time slot duration (~36s) for
182
+ // the dateProvider to catch up to the next slot timestamp. We skip the warp if the sequencer is actively building
183
+ // to avoid invalidating its in-progress work.
184
+ if (this.getPendingTxCount) {
185
+ const pendingTxs = await this.getPendingTxCount();
186
+ if (pendingTxs > 0) {
187
+ if (this.isSequencerBuilding?.()) {
188
+ this.unfilledSlotFirstSeen = undefined;
189
+ return;
190
+ }
191
+
192
+ const realNow = Date.now();
193
+ if (!this.unfilledSlotFirstSeen || this.unfilledSlotFirstSeen.slot !== currentSlot) {
194
+ this.unfilledSlotFirstSeen = { slot: currentSlot, realTime: realNow };
195
+ return;
196
+ }
197
+
198
+ if (realNow - this.unfilledSlotFirstSeen.realTime > 2000) {
199
+ if (await this.warpToTimestamp(nextSlotTimestamp)) {
200
+ this.logger.info(`Slot ${currentSlot} was missed with pending txs, jumped to next slot`);
201
+ }
202
+ this.unfilledSlotFirstSeen = undefined;
203
+ }
204
+
205
+ return;
206
+ }
207
+ }
208
+
209
+ // Fallback: warp when the dateProvider time has passed the next slot timestamp.
152
210
  const currentTimestamp = this.dateProvider?.now() ?? Date.now();
153
211
  if (currentTimestamp > nextSlotTimestamp * 1000) {
154
- try {
155
- await this.cheatcodes.warp(nextSlotTimestamp, { resetBlockInterval: true });
156
- } catch (e) {
157
- this.logger.error(`Failed to warp to timestamp ${nextSlotTimestamp}: ${e}`);
212
+ if (await this.warpToTimestamp(nextSlotTimestamp)) {
213
+ this.logger.info(`Slot ${currentSlot} was missed, jumped to next slot`);
158
214
  }
159
-
160
- this.logger.info(`Slot ${currentSlot} was missed, jumped to next slot`);
161
215
  }
162
216
  } catch {
163
217
  this.logger.error('mineIfSlotFilled failed');
164
218
  }
165
219
  }
220
+
221
+ /**
222
+ * Warps L1 to `timestamp`, unless L1 is already at or past it. Returns true when a warp actually
223
+ * happened, false when skipped or on error. Callers use the return value to gate success logs.
224
+ */
225
+ private async warpToTimestamp(timestamp: number): Promise<boolean> {
226
+ try {
227
+ // Anvil rejects evm_setNextBlockTimestamp values <= the current block's timestamp. The two
228
+ // watcher loops can race and pick targets a fraction of a second apart; skip here rather than
229
+ // letting the second one error out noisily.
230
+ const lastTimestamp = await this.cheatcodes.lastBlockTimestamp();
231
+ if (timestamp <= lastTimestamp) {
232
+ return false;
233
+ }
234
+ await this.cheatcodes.warp(timestamp, { resetBlockInterval: true });
235
+ return true;
236
+ } catch (e) {
237
+ this.logger.error(`Failed to warp to timestamp ${timestamp}: ${e}`);
238
+ return false;
239
+ }
240
+ }
166
241
  }
@@ -1,9 +1,8 @@
1
1
  import { EthCheatCodes, RollupCheatCodes } from '@aztec/ethereum/test';
2
- import { BlockNumber } from '@aztec/foundation/branded-types';
3
- import { retryUntil } from '@aztec/foundation/retry';
2
+ import { SlotNumber } from '@aztec/foundation/branded-types';
3
+ import { createLogger } from '@aztec/foundation/log';
4
4
  import type { DateProvider } from '@aztec/foundation/timer';
5
- import type { SequencerClient } from '@aztec/sequencer-client';
6
- import type { AztecNode } from '@aztec/stdlib/interfaces/client';
5
+ import type { AztecNode, AztecNodeDebug } from '@aztec/stdlib/interfaces/client';
7
6
 
8
7
  /**
9
8
  * A class that provides utility functions for interacting with the chain.
@@ -12,6 +11,8 @@ import type { AztecNode } from '@aztec/stdlib/interfaces/client';
12
11
  * codes, please consider whether it makes sense to just introduce new utils in your tests instead.
13
12
  */
14
13
  export class CheatCodes {
14
+ private logger = createLogger('aztecjs:cheat_codes');
15
+
15
16
  constructor(
16
17
  /** Cheat codes for L1.*/
17
18
  public eth: EthCheatCodes,
@@ -30,50 +31,55 @@ export class CheatCodes {
30
31
 
31
32
  /**
32
33
  * Warps the L1 timestamp to a target timestamp and mines an L2 block that advances the L2 timestamp to at least
33
- * the target timestamp. L2 timestamp is not advanced exactly to the target timestamp because it is determined
34
- * by the slot number, which advances in fixed intervals.
35
- * This is useful for testing time-dependent contract behavior.
36
- * @param sequencerClient - The sequencer client to use to force an empty block to be mined.
37
- * @param node - The Aztec node used to query if a new block has been mined.
34
+ * the target timestamp. If the target timestamp falls within the current L2 slot (which already has a block),
35
+ * the timestamp is automatically adjusted forward to the start of the next slot so that `mineBlock()` succeeds.
36
+ * @param node - The Aztec node used to force an empty block to be mined.
38
37
  * @param targetTimestamp - The target timestamp to warp to (in seconds)
39
38
  */
40
- async warpL2TimeAtLeastTo(sequencerClient: SequencerClient, node: AztecNode, targetTimestamp: bigint | number) {
41
- const currentL2BlockNumber: BlockNumber = await node.getBlockNumber();
39
+ async warpL2TimeAtLeastTo(node: AztecNodeDebug, targetTimestamp: bigint | number) {
40
+ const targetBigInt = BigInt(targetTimestamp);
41
+ const currentTimestamp = BigInt(await this.eth.lastBlockTimestamp());
42
42
 
43
- // We warp the L1 timestamp
44
- await this.eth.warp(targetTimestamp, { resetBlockInterval: true });
43
+ if (targetBigInt <= currentTimestamp) {
44
+ throw new Error(
45
+ `warpL2TimeAtLeastTo: target timestamp ${targetBigInt} is not in the future (current L1 timestamp is ${currentTimestamp}).`,
46
+ );
47
+ }
45
48
 
46
- // Wait until an L2 block is mined
47
- const sequencer = sequencerClient.getSequencer();
48
- const minTxsPerBlock = sequencer.getConfig().minTxsPerBlock;
49
- sequencer.updateConfig({ minTxsPerBlock: 0 });
49
+ const currentSlot = await this.rollup.getSlot();
50
+ const targetSlot = await this.rollup.getSlotAt(targetBigInt);
50
51
 
51
- await retryUntil(
52
- async () => {
53
- const newL2BlockNumber: BlockNumber = await node.getBlockNumber();
54
- return newL2BlockNumber > currentL2BlockNumber;
55
- },
56
- 'new block after warping L2 time',
57
- 36,
58
- 1,
59
- );
52
+ let effectiveTimestamp = targetBigInt;
60
53
 
61
- // Restore original minTxsPerBlock
62
- sequencer.updateConfig({ minTxsPerBlock });
54
+ if (targetSlot <= currentSlot) {
55
+ // Target lands in the same (or earlier) slot — auto-adjust to the next slot's start.
56
+ const nextSlot = SlotNumber(currentSlot + 1);
57
+ const nextSlotTimestamp = await this.rollup.getTimestampForSlot(nextSlot);
58
+ this.logger.warn(
59
+ `warpL2TimeAtLeastTo: target timestamp ${targetBigInt} falls in current slot ${currentSlot}. ` +
60
+ `Auto-adjusting to start of slot ${nextSlot} at timestamp ${nextSlotTimestamp}.`,
61
+ );
62
+ effectiveTimestamp = nextSlotTimestamp;
63
+ }
64
+
65
+ await this.eth.warp(effectiveTimestamp, { resetBlockInterval: true });
66
+ await node.mineBlock();
63
67
  }
64
68
 
65
69
  /**
66
70
  * Warps the L1 timestamp forward by a specified duration and mines an L2 block that advances the L2 timestamp at
67
- * least by the duration. L2 timestamp is not advanced exactly by the duration because it is determined by the slot
68
- * number, which advances in fixed intervals.
69
- * This is useful for testing time-dependent contract behavior.
70
- * @param sequencerClient - The sequencer client to use to force an empty block to be mined.
71
- * @param node - The Aztec node used to query if a new block has been mined.
71
+ * least by the duration. If the duration is too short to cross an L2 slot boundary, the warp is automatically
72
+ * extended to the start of the next slot so that `mineBlock()` succeeds.
73
+ * @param node - The Aztec node used to force an empty block to be mined.
72
74
  * @param duration - The duration to advance time by (in seconds)
73
75
  */
74
- async warpL2TimeAtLeastBy(sequencerClient: SequencerClient, node: AztecNode, duration: bigint | number) {
75
- const currentTimestamp = await this.eth.timestamp();
76
+ async warpL2TimeAtLeastBy(node: AztecNodeDebug, duration: bigint | number) {
77
+ if (BigInt(duration) <= 0n) {
78
+ throw new Error(`warpL2TimeAtLeastBy: duration must be positive, got ${duration} seconds.`);
79
+ }
80
+
81
+ const currentTimestamp = await this.eth.lastBlockTimestamp();
76
82
  const targetTimestamp = BigInt(currentTimestamp) + BigInt(duration);
77
- await this.warpL2TimeAtLeastTo(sequencerClient, node, targetTimestamp);
83
+ await this.warpL2TimeAtLeastTo(node, targetTimestamp);
78
84
  }
79
85
  }