@aztec/aztec 0.0.1-commit.5daedc8 → 0.0.1-commit.6201a7b05

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 (157) hide show
  1. package/dest/bin/index.js +12 -5
  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 +77 -22
  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 +52 -34
  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 +7 -52
  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/migrate_ha_db.d.ts +3 -0
  18. package/dest/cli/cmds/migrate_ha_db.d.ts.map +1 -0
  19. package/dest/cli/cmds/migrate_ha_db.js +27 -0
  20. package/dest/cli/cmds/profile.d.ts +4 -0
  21. package/dest/cli/cmds/profile.d.ts.map +1 -0
  22. package/dest/cli/cmds/profile.js +8 -0
  23. package/dest/cli/cmds/profile_flamegraph.d.ts +4 -0
  24. package/dest/cli/cmds/profile_flamegraph.d.ts.map +1 -0
  25. package/dest/cli/cmds/profile_flamegraph.js +52 -0
  26. package/dest/cli/cmds/profile_gates.d.ts +4 -0
  27. package/dest/cli/cmds/profile_gates.d.ts.map +1 -0
  28. package/dest/cli/cmds/profile_gates.js +58 -0
  29. package/dest/cli/cmds/profile_utils.d.ts +18 -0
  30. package/dest/cli/cmds/profile_utils.d.ts.map +1 -0
  31. package/dest/cli/cmds/profile_utils.js +50 -0
  32. package/dest/cli/cmds/standby.d.ts +32 -0
  33. package/dest/cli/cmds/standby.d.ts.map +1 -0
  34. package/dest/cli/cmds/standby.js +97 -0
  35. package/dest/cli/cmds/start_archiver.d.ts +2 -2
  36. package/dest/cli/cmds/start_archiver.d.ts.map +1 -1
  37. package/dest/cli/cmds/start_archiver.js +15 -15
  38. package/dest/cli/cmds/start_bot.d.ts +3 -3
  39. package/dest/cli/cmds/start_bot.d.ts.map +1 -1
  40. package/dest/cli/cmds/start_bot.js +10 -6
  41. package/dest/cli/cmds/start_node.d.ts +3 -2
  42. package/dest/cli/cmds/start_node.d.ts.map +1 -1
  43. package/dest/cli/cmds/start_node.js +75 -33
  44. package/dest/cli/cmds/start_p2p_bootstrap.d.ts +2 -2
  45. package/dest/cli/cmds/start_p2p_bootstrap.d.ts.map +1 -1
  46. package/dest/cli/cmds/start_p2p_bootstrap.js +2 -3
  47. package/dest/cli/cmds/start_prover_agent.d.ts +1 -1
  48. package/dest/cli/cmds/start_prover_agent.d.ts.map +1 -1
  49. package/dest/cli/cmds/start_prover_agent.js +6 -18
  50. package/dest/cli/cmds/start_prover_broker.d.ts +1 -1
  51. package/dest/cli/cmds/start_prover_broker.d.ts.map +1 -1
  52. package/dest/cli/cmds/start_prover_broker.js +15 -9
  53. package/dest/cli/cmds/start_txe.d.ts +2 -2
  54. package/dest/cli/cmds/start_txe.d.ts.map +1 -1
  55. package/dest/cli/cmds/start_txe.js +4 -3
  56. package/dest/cli/cmds/utils/artifacts.d.ts +21 -0
  57. package/dest/cli/cmds/utils/artifacts.d.ts.map +1 -0
  58. package/dest/cli/cmds/utils/artifacts.js +24 -0
  59. package/dest/cli/cmds/utils/collect_crate_dirs.d.ts +21 -0
  60. package/dest/cli/cmds/utils/collect_crate_dirs.d.ts.map +1 -0
  61. package/dest/cli/cmds/utils/collect_crate_dirs.js +114 -0
  62. package/dest/cli/cmds/utils/needs_recompile.d.ts +10 -0
  63. package/dest/cli/cmds/utils/needs_recompile.d.ts.map +1 -0
  64. package/dest/cli/cmds/utils/needs_recompile.js +90 -0
  65. package/dest/cli/cmds/utils/spawn.d.ts +3 -0
  66. package/dest/cli/cmds/utils/spawn.d.ts.map +1 -0
  67. package/dest/cli/cmds/utils/spawn.js +16 -0
  68. package/dest/cli/cmds/utils/warn_if_aztec_version_mismatch.d.ts +4 -0
  69. package/dest/cli/cmds/utils/warn_if_aztec_version_mismatch.d.ts.map +1 -0
  70. package/dest/cli/cmds/utils/warn_if_aztec_version_mismatch.js +62 -0
  71. package/dest/cli/util.d.ts +9 -20
  72. package/dest/cli/util.d.ts.map +1 -1
  73. package/dest/cli/util.js +51 -87
  74. package/dest/examples/token.js +9 -9
  75. package/dest/examples/util.d.ts +2 -2
  76. package/dest/examples/util.d.ts.map +1 -1
  77. package/dest/local-network/banana_fpc.d.ts +1 -1
  78. package/dest/local-network/banana_fpc.d.ts.map +1 -1
  79. package/dest/local-network/banana_fpc.js +4 -4
  80. package/dest/local-network/local-network.d.ts +15 -17
  81. package/dest/local-network/local-network.d.ts.map +1 -1
  82. package/dest/local-network/local-network.js +89 -46
  83. package/dest/testing/anvil_test_watcher.d.ts +16 -4
  84. package/dest/testing/anvil_test_watcher.d.ts.map +1 -1
  85. package/dest/testing/anvil_test_watcher.js +86 -23
  86. package/dest/testing/cheat_codes.d.ts +13 -15
  87. package/dest/testing/cheat_codes.d.ts.map +1 -1
  88. package/dest/testing/cheat_codes.js +34 -32
  89. package/dest/testing/epoch_test_settler.d.ts +19 -0
  90. package/dest/testing/epoch_test_settler.d.ts.map +1 -0
  91. package/dest/testing/epoch_test_settler.js +61 -0
  92. package/dest/testing/index.d.ts +4 -2
  93. package/dest/testing/index.d.ts.map +1 -1
  94. package/dest/testing/index.js +2 -0
  95. package/dest/testing/token_allowed_setup.d.ts +7 -0
  96. package/dest/testing/token_allowed_setup.d.ts.map +1 -0
  97. package/dest/testing/token_allowed_setup.js +20 -0
  98. package/package.json +39 -36
  99. package/scripts/add_crate.sh +52 -0
  100. package/scripts/aztec.sh +73 -0
  101. package/scripts/init.sh +39 -0
  102. package/scripts/new.sh +83 -0
  103. package/scripts/setup_workspace.sh +69 -0
  104. package/scripts/templates/blank/contract/Nargo.toml +6 -0
  105. package/scripts/templates/blank/contract/src/main.nr +10 -0
  106. package/scripts/templates/blank/test/Nargo.toml +7 -0
  107. package/scripts/templates/blank/test/src/lib.nr +11 -0
  108. package/scripts/templates/counter/contract/Nargo.toml +7 -0
  109. package/scripts/templates/counter/contract/src/main.nr +48 -0
  110. package/scripts/templates/counter/test/Nargo.toml +7 -0
  111. package/scripts/templates/counter/test/src/lib.nr +32 -0
  112. package/src/bin/index.ts +13 -5
  113. package/src/cli/admin_api_key_store.ts +128 -0
  114. package/src/cli/aztec_start_action.ts +69 -17
  115. package/src/cli/aztec_start_options.ts +59 -35
  116. package/src/cli/cli.ts +11 -56
  117. package/src/cli/cmds/compile.ts +178 -0
  118. package/src/cli/cmds/migrate_ha_db.ts +43 -0
  119. package/src/cli/cmds/profile.ts +25 -0
  120. package/src/cli/cmds/profile_flamegraph.ts +64 -0
  121. package/src/cli/cmds/profile_gates.ts +68 -0
  122. package/src/cli/cmds/profile_utils.ts +58 -0
  123. package/src/cli/cmds/standby.ts +132 -0
  124. package/src/cli/cmds/start_archiver.ts +14 -19
  125. package/src/cli/cmds/start_bot.ts +9 -6
  126. package/src/cli/cmds/start_node.ts +85 -37
  127. package/src/cli/cmds/start_p2p_bootstrap.ts +3 -3
  128. package/src/cli/cmds/start_prover_agent.ts +7 -18
  129. package/src/cli/cmds/start_prover_broker.ts +20 -16
  130. package/src/cli/cmds/start_txe.ts +5 -3
  131. package/src/cli/cmds/utils/artifacts.ts +44 -0
  132. package/src/cli/cmds/utils/collect_crate_dirs.ts +118 -0
  133. package/src/cli/cmds/utils/needs_recompile.ts +98 -0
  134. package/src/cli/cmds/utils/spawn.ts +16 -0
  135. package/src/cli/cmds/utils/warn_if_aztec_version_mismatch.ts +77 -0
  136. package/src/cli/util.ts +60 -98
  137. package/src/examples/token.ts +10 -10
  138. package/src/examples/util.ts +1 -1
  139. package/src/local-network/banana_fpc.ts +12 -8
  140. package/src/local-network/local-network.ts +123 -88
  141. package/src/testing/anvil_test_watcher.ts +97 -22
  142. package/src/testing/cheat_codes.ts +42 -35
  143. package/src/testing/epoch_test_settler.ts +70 -0
  144. package/src/testing/index.ts +3 -1
  145. package/src/testing/token_allowed_setup.ts +19 -0
  146. package/dest/cli/cmds/start_blob_sink.d.ts +0 -3
  147. package/dest/cli/cmds/start_blob_sink.d.ts.map +0 -1
  148. package/dest/cli/cmds/start_blob_sink.js +0 -33
  149. package/dest/cli/cmds/start_prover_node.d.ts +0 -7
  150. package/dest/cli/cmds/start_prover_node.d.ts.map +0 -1
  151. package/dest/cli/cmds/start_prover_node.js +0 -108
  152. package/dest/cli/release_version.d.ts +0 -2
  153. package/dest/cli/release_version.d.ts.map +0 -1
  154. package/dest/cli/release_version.js +0 -14
  155. package/src/cli/cmds/start_blob_sink.ts +0 -57
  156. package/src/cli/cmds/start_prover_node.ts +0 -124
  157. package/src/cli/release_version.ts +0 -21
@@ -1,6 +1,6 @@
1
1
  import { type InitialAccountData, getInitialTestAccountsData } from '@aztec/accounts/testing';
2
2
  import type { Wallet } from '@aztec/aztec.js/wallet';
3
- import { Fr } from '@aztec/foundation/fields';
3
+ import { Fr } from '@aztec/foundation/curves/bn254';
4
4
  import type { LogFn } from '@aztec/foundation/log';
5
5
  import { FPCContract } from '@aztec/noir-contracts.js/FPC';
6
6
  import { TokenContract } from '@aztec/noir-contracts.js/Token';
@@ -48,13 +48,17 @@ 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([
52
- TokenContract.deploy(wallet, admin, bananaCoinArgs.name, bananaCoinArgs.symbol, bananaCoinArgs.decimal)
53
- .send({ from: admin, contractAddressSalt: BANANA_COIN_SALT, universalDeploy: true })
54
- .deployed(),
55
- FPCContract.deploy(wallet, bananaCoinAddress, admin)
56
- .send({ from: admin, contractAddressSalt: BANANA_FPC_SALT, universalDeploy: true })
57
- .deployed(),
51
+ const [{ contract: bananaCoin }, { contract: fpc }] = await Promise.all([
52
+ TokenContract.deploy(wallet, admin, bananaCoinArgs.name, bananaCoinArgs.symbol, bananaCoinArgs.decimal).send({
53
+ from: admin,
54
+ contractAddressSalt: BANANA_COIN_SALT,
55
+ universalDeploy: true,
56
+ }),
57
+ FPCContract.deploy(wallet, bananaCoinAddress, admin).send({
58
+ from: admin,
59
+ contractAddressSalt: BANANA_FPC_SALT,
60
+ universalDeploy: true,
61
+ }),
58
62
  ]);
59
63
 
60
64
  log(`BananaCoin: ${bananaCoin.address}`);
@@ -1,40 +1,45 @@
1
1
  #!/usr/bin/env -S node --no-warnings
2
2
  import { getInitialTestAccountsData } from '@aztec/accounts/testing';
3
- import { type AztecNodeConfig, AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node';
4
- import { EthAddress } from '@aztec/aztec.js/addresses';
5
- import { type BlobSinkClientInterface, createBlobSinkClient } from '@aztec/blob-sink/client';
3
+ import { AztecNodeService } from '@aztec/aztec-node';
4
+ import { type AztecNodeConfig, getConfigEnvVars } from '@aztec/aztec-node/config';
5
+ import { Fr } from '@aztec/aztec.js/fields';
6
+ import { createLogger } from '@aztec/aztec.js/log';
7
+ import { type BlobClientInterface, createBlobClient } from '@aztec/blob-client/client';
6
8
  import { GENESIS_ARCHIVE_ROOT } from '@aztec/constants';
7
- import {
8
- NULL_KEY,
9
- createEthereumChain,
10
- deployL1Contracts,
11
- deployMulticall3,
12
- getL1ContractsConfigEnvVars,
13
- waitForPublicClient,
14
- } from '@aztec/ethereum';
9
+ import { createEthereumChain } from '@aztec/ethereum/chain';
10
+ import { waitForPublicClient } from '@aztec/ethereum/client';
11
+ import { getL1ContractsConfigEnvVars } from '@aztec/ethereum/config';
12
+ import { NULL_KEY } from '@aztec/ethereum/constants';
13
+ import { deployAztecL1Contracts } from '@aztec/ethereum/deploy-aztec-l1-contracts';
15
14
  import { EthCheatCodes } from '@aztec/ethereum/test';
16
15
  import { SecretValue } from '@aztec/foundation/config';
17
- import { Fr } from '@aztec/foundation/fields';
18
- import { type LogFn, createLogger } from '@aztec/foundation/log';
16
+ import { EthAddress } from '@aztec/foundation/eth-address';
17
+ import type { LogFn } from '@aztec/foundation/log';
19
18
  import { DateProvider, TestDateProvider } from '@aztec/foundation/timer';
20
19
  import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
21
20
  import { protocolContractsHash } from '@aztec/protocol-contracts';
22
- 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';
23
25
  import {
24
26
  type TelemetryClient,
25
27
  getConfigEnvVars as getTelemetryClientConfig,
26
28
  initTelemetryClient,
27
29
  } from '@aztec/telemetry-client';
28
- import { TestWallet, deployFundedSchnorrAccounts } from '@aztec/test-wallet/server';
30
+ import { EmbeddedWallet } from '@aztec/wallets/embedded';
31
+ import { deployFundedSchnorrAccounts } from '@aztec/wallets/testing';
29
32
  import { getGenesisValues } from '@aztec/world-state/testing';
30
33
 
31
- import { type HDAccount, type PrivateKeyAccount, createPublicClient, fallback, http as httpViemTransport } from 'viem';
34
+ import { type Hex, createPublicClient, fallback, http as httpViemTransport } from 'viem';
32
35
  import { mnemonicToAccount, privateKeyToAddress } from 'viem/accounts';
33
36
  import { foundry } from 'viem/chains';
34
37
 
35
38
  import { createAccountLogs } from '../cli/util.js';
36
39
  import { DefaultMnemonic } from '../mnemonic.js';
37
40
  import { AnvilTestWatcher } from '../testing/anvil_test_watcher.js';
41
+ import { EpochTestSettler } from '../testing/epoch_test_settler.js';
42
+ import { getTokenAllowedSetupFunctions } from '../testing/token_allowed_setup.js';
38
43
  import { getBananaFPCAddress, setupBananaFPC } from './banana_fpc.js';
39
44
  import { getSponsoredFPCAddress } from './sponsored_fpc.js';
40
45
 
@@ -49,42 +54,25 @@ const localAnvil = foundry;
49
54
  */
50
55
  export async function deployContractsToL1(
51
56
  aztecNodeConfig: AztecNodeConfig,
52
- hdAccount: HDAccount | PrivateKeyAccount,
53
- contractDeployLogger = logger,
57
+ privateKey: Hex,
54
58
  opts: {
55
- assumeProvenThroughBlockNumber?: number;
56
- salt?: number;
57
59
  genesisArchiveRoot?: Fr;
58
60
  feeJuicePortalInitialBalance?: bigint;
59
61
  } = {},
60
62
  ) {
61
- const chain =
62
- aztecNodeConfig.l1RpcUrls.length > 0
63
- ? createEthereumChain(aztecNodeConfig.l1RpcUrls, aztecNodeConfig.l1ChainId)
64
- : { chainInfo: localAnvil };
65
-
66
63
  await waitForPublicClient(aztecNodeConfig);
67
64
 
68
- const l1Contracts = await deployL1Contracts(
69
- aztecNodeConfig.l1RpcUrls,
70
- hdAccount,
71
- chain.chainInfo,
72
- contractDeployLogger,
73
- {
74
- ...getL1ContractsConfigEnvVars(), // TODO: We should not need to be loading config from env again, caller should handle this
75
- ...aztecNodeConfig,
76
- vkTreeRoot: getVKTreeRoot(),
77
- protocolContractsHash,
78
- genesisArchiveRoot: opts.genesisArchiveRoot ?? new Fr(GENESIS_ARCHIVE_ROOT),
79
- salt: opts.salt,
80
- feeJuicePortalInitialBalance: opts.feeJuicePortalInitialBalance,
81
- aztecTargetCommitteeSize: 0, // no committee in local network
82
- slasherFlavor: 'none', // no slashing in local network
83
- realVerifier: false,
84
- },
85
- );
86
-
87
- await deployMulticall3(l1Contracts.l1Client, logger);
65
+ const l1Contracts = await deployAztecL1Contracts(aztecNodeConfig.l1RpcUrls[0], privateKey, foundry.id, {
66
+ ...getL1ContractsConfigEnvVars(), // TODO: We should not need to be loading config from env again, caller should handle this
67
+ ...aztecNodeConfig,
68
+ vkTreeRoot: getVKTreeRoot(),
69
+ protocolContractsHash,
70
+ genesisArchiveRoot: opts.genesisArchiveRoot ?? new Fr(GENESIS_ARCHIVE_ROOT),
71
+ feeJuicePortalInitialBalance: opts.feeJuicePortalInitialBalance,
72
+ aztecTargetCommitteeSize: 0, // no committee in local network
73
+ slasherEnabled: false, // no slashing in local network
74
+ realVerifier: false,
75
+ });
88
76
 
89
77
  aztecNodeConfig.l1Contracts = l1Contracts.l1ContractAddresses;
90
78
  aztecNodeConfig.rollupVersion = l1Contracts.rollupVersion;
@@ -96,8 +84,6 @@ export async function deployContractsToL1(
96
84
  export type LocalNetworkConfig = AztecNodeConfig & {
97
85
  /** Mnemonic used to derive the L1 deployer private key.*/
98
86
  l1Mnemonic: string;
99
- /** Salt used to deploy L1 contracts.*/
100
- deployAztecContractsSalt: string;
101
87
  /** Whether to deploy test accounts on local network start.*/
102
88
  testAccounts: boolean;
103
89
  };
@@ -116,15 +102,26 @@ export async function createLocalNetwork(config: Partial<LocalNetworkConfig> = {
116
102
  if ((config.l1RpcUrls?.length || 0) > 1) {
117
103
  logger.warn(`Multiple L1 RPC URLs provided. Local networks will only use the first one: ${l1RpcUrl}`);
118
104
  }
119
- const aztecNodeConfig: AztecNodeConfig = { ...getConfigEnvVars(), ...config };
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
+
110
+ const aztecNodeConfig: AztecNodeConfig = {
111
+ ...getConfigEnvVars(),
112
+ ...config,
113
+ txPublicSetupAllowListExtend: [...tokenAllowList, ...(config.txPublicSetupAllowListExtend ?? [])],
114
+ };
120
115
  const hdAccount = mnemonicToAccount(config.l1Mnemonic || DefaultMnemonic);
121
116
  if (
122
- aztecNodeConfig.publisherPrivateKeys == undefined ||
123
- !aztecNodeConfig.publisherPrivateKeys.length ||
124
- aztecNodeConfig.publisherPrivateKeys[0].getValue() === NULL_KEY
117
+ aztecNodeConfig.sequencerPublisherPrivateKeys == undefined ||
118
+ !aztecNodeConfig.sequencerPublisherPrivateKeys.length ||
119
+ aztecNodeConfig.sequencerPublisherPrivateKeys[0].getValue() === NULL_KEY
125
120
  ) {
126
121
  const privKey = hdAccount.getHdKey().privateKey;
127
- 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
+ ];
128
125
  }
129
126
  if (!aztecNodeConfig.validatorPrivateKeys?.getValue().length) {
130
127
  const privKey = hdAccount.getHdKey().privateKey;
@@ -148,20 +145,28 @@ export async function createLocalNetwork(config: Partial<LocalNetworkConfig> = {
148
145
 
149
146
  const bananaFPC = await getBananaFPCAddress(initialAccounts);
150
147
  const sponsoredFPC = await getSponsoredFPCAddress();
151
- const fundedAddresses = initialAccounts.length
152
- ? [...initialAccounts.map(a => a.address), bananaFPC, sponsoredFPC]
153
- : [];
154
- 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);
155
155
 
156
- let watcher: AnvilTestWatcher | undefined = undefined;
157
156
  const dateProvider = new TestDateProvider();
157
+
158
+ let cheatcodes: EthCheatCodes | undefined;
159
+ let rollupAddress: EthAddress | undefined;
160
+ let watcher: AnvilTestWatcher | undefined;
158
161
  if (!aztecNodeConfig.p2pEnabled) {
159
- const l1ContractAddresses = await deployContractsToL1(aztecNodeConfig, hdAccount, undefined, {
160
- assumeProvenThroughBlockNumber: Number.MAX_SAFE_INTEGER,
161
- genesisArchiveRoot,
162
- salt: config.deployAztecContractsSalt ? parseInt(config.deployAztecContractsSalt) : undefined,
163
- feeJuicePortalInitialBalance: fundingNeeded,
164
- });
162
+ ({ rollupAddress } = await deployContractsToL1(
163
+ aztecNodeConfig,
164
+ aztecNodeConfig.validatorPrivateKeys.getValue()[0],
165
+ {
166
+ genesisArchiveRoot,
167
+ feeJuicePortalInitialBalance: fundingNeeded,
168
+ },
169
+ ));
165
170
 
166
171
  const chain =
167
172
  aztecNodeConfig.l1RpcUrls.length > 0
@@ -173,36 +178,56 @@ export async function createLocalNetwork(config: Partial<LocalNetworkConfig> = {
173
178
  transport: fallback([httpViemTransport(l1RpcUrl)]) as any,
174
179
  });
175
180
 
176
- watcher = new AnvilTestWatcher(
177
- new EthCheatCodes([l1RpcUrl], dateProvider),
178
- l1ContractAddresses.rollupAddress,
179
- publicClient,
180
- dateProvider,
181
- );
181
+ cheatcodes = new EthCheatCodes([l1RpcUrl], dateProvider);
182
+
183
+ watcher = new AnvilTestWatcher(cheatcodes, rollupAddress, publicClient, dateProvider);
182
184
  watcher.setisLocalNetwork(true);
185
+ watcher.setIsMarkingAsProven(false); // Do not mark as proven in the watcher. It's marked in the epochTestSettler after the out hash is set.
186
+
183
187
  await watcher.start();
184
188
  }
185
189
 
186
- const telemetry = initTelemetryClient(getTelemetryClientConfig());
187
- // Create a local blob sink client inside the local network, no http connectivity
188
- const blobSinkClient = createBlobSinkClient();
189
- const node = await createAztecNode(
190
- aztecNodeConfig,
191
- { telemetry, blobSinkClient, dateProvider },
192
- { prefilledPublicData },
193
- );
190
+ const telemetry = await initTelemetryClient(getTelemetryClientConfig());
191
+ // Create a local blob client client inside the local network, no http connectivity
192
+ const blobClient = createBlobClient();
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
+ }
209
+
210
+ let epochTestSettler: EpochTestSettler | undefined;
211
+ if (!aztecNodeConfig.p2pEnabled) {
212
+ epochTestSettler = new EpochTestSettler(
213
+ cheatcodes!,
214
+ rollupAddress!,
215
+ node.getBlockSource(),
216
+ logger.createChild('epoch-settler'),
217
+ { pollingIntervalMs: 200 },
218
+ );
219
+ await epochTestSettler.start();
220
+ }
194
221
 
195
222
  if (initialAccounts.length) {
196
- const PXEConfig = { proverEnabled: aztecNodeConfig.realProofs };
197
- const wallet = await TestWallet.create(node, PXEConfig);
223
+ const wallet = await EmbeddedWallet.create(node, {
224
+ pxeConfig: { proverEnabled: aztecNodeConfig.realProofs },
225
+ ephemeral: true,
226
+ });
198
227
 
199
228
  userLog('Setting up funded test accounts...');
200
- const accountManagers = await deployFundedSchnorrAccounts(wallet, node, initialAccounts);
201
- const accountsWithSecrets = accountManagers.map((manager, i) => ({
202
- account: manager,
203
- secretKey: initialAccounts[i].secret,
204
- }));
205
- const accLogs = await createAccountLogs(accountsWithSecrets, wallet);
229
+ const accountManagers = await deployFundedSchnorrAccounts(wallet, initialAccounts);
230
+ const accLogs = await createAccountLogs(accountManagers, wallet);
206
231
  userLog(accLogs.join(''));
207
232
 
208
233
  await setupBananaFPC(initialAccounts, wallet, userLog);
@@ -216,6 +241,7 @@ export async function createLocalNetwork(config: Partial<LocalNetworkConfig> = {
216
241
  const stop = async () => {
217
242
  await node.stop();
218
243
  await watcher?.stop();
244
+ await epochTestSettler?.stop();
219
245
  };
220
246
 
221
247
  return { node, stop };
@@ -227,8 +253,13 @@ export async function createLocalNetwork(config: Partial<LocalNetworkConfig> = {
227
253
  */
228
254
  export async function createAztecNode(
229
255
  config: Partial<AztecNodeConfig> = {},
230
- deps: { telemetry?: TelemetryClient; blobSinkClient?: BlobSinkClientInterface; dateProvider?: DateProvider } = {},
231
- options: { prefilledPublicData?: PublicDataTreeLeaf[] } = {},
256
+ deps: {
257
+ telemetry?: TelemetryClient;
258
+ blobClient?: BlobClientInterface;
259
+ dateProvider?: DateProvider;
260
+ proverBroker?: ProvingJobBroker;
261
+ } = {},
262
+ options: { genesis?: GenesisData } = {},
232
263
  ) {
233
264
  // TODO(#12272): will clean this up. This is criminal.
234
265
  const { l1Contracts, ...rest } = getConfigEnvVars();
@@ -237,6 +268,10 @@ export async function createAztecNode(
237
268
  ...config,
238
269
  l1Contracts: { ...l1Contracts, ...config.l1Contracts },
239
270
  };
240
- 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
+ );
241
276
  return node;
242
277
  }
@@ -1,5 +1,5 @@
1
- import type { ViemClient } from '@aztec/ethereum';
2
1
  import { EthCheatCodes, RollupCheatCodes } from '@aztec/ethereum/test';
2
+ import type { ViemClient } from '@aztec/ethereum/types';
3
3
  import { SlotNumber } from '@aztec/foundation/branded-types';
4
4
  import type { EthAddress } from '@aztec/foundation/eth-address';
5
5
  import { type Logger, createLogger } from '@aztec/foundation/log';
@@ -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,36 +138,37 @@ 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
 
125
159
  async warpTimeIfNeeded() {
126
160
  try {
127
161
  const currentSlot = SlotNumber.fromBigInt(await this.rollup.read.getCurrentSlot());
128
- const pendingCheckpointNumber = BigInt(await this.rollup.read.getPendingCheckpointNumber());
162
+ const pendingCheckpointNumber = await this.rollup.read.getPendingCheckpointNumber();
129
163
  const checkpointLog = await this.rollup.read.getCheckpoint([pendingCheckpointNumber]);
130
164
  const nextSlot = SlotNumber(currentSlot + 1);
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,8 +1,8 @@
1
1
  import { EthCheatCodes, RollupCheatCodes } from '@aztec/ethereum/test';
2
- import { retryUntil } from '@aztec/foundation/retry';
2
+ import { SlotNumber } from '@aztec/foundation/branded-types';
3
+ import { createLogger } from '@aztec/foundation/log';
3
4
  import type { DateProvider } from '@aztec/foundation/timer';
4
- import type { SequencerClient } from '@aztec/sequencer-client';
5
- import type { AztecNode } from '@aztec/stdlib/interfaces/client';
5
+ import type { AztecNode, AztecNodeDebug } from '@aztec/stdlib/interfaces/client';
6
6
 
7
7
  /**
8
8
  * A class that provides utility functions for interacting with the chain.
@@ -11,6 +11,8 @@ import type { AztecNode } from '@aztec/stdlib/interfaces/client';
11
11
  * codes, please consider whether it makes sense to just introduce new utils in your tests instead.
12
12
  */
13
13
  export class CheatCodes {
14
+ private logger = createLogger('aztecjs:cheat_codes');
15
+
14
16
  constructor(
15
17
  /** Cheat codes for L1.*/
16
18
  public eth: EthCheatCodes,
@@ -29,50 +31,55 @@ export class CheatCodes {
29
31
 
30
32
  /**
31
33
  * Warps the L1 timestamp to a target timestamp and mines an L2 block that advances the L2 timestamp to at least
32
- * the target timestamp. L2 timestamp is not advanced exactly to the target timestamp because it is determined
33
- * by the slot number, which advances in fixed intervals.
34
- * This is useful for testing time-dependent contract behavior.
35
- * @param sequencerClient - The sequencer client to use to force an empty block to be mined.
36
- * @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.
37
37
  * @param targetTimestamp - The target timestamp to warp to (in seconds)
38
38
  */
39
- async warpL2TimeAtLeastTo(sequencerClient: SequencerClient, node: AztecNode, targetTimestamp: bigint | number) {
40
- const currentL2BlockNumber = await node.getBlockNumber();
39
+ async warpL2TimeAtLeastTo(node: AztecNodeDebug, targetTimestamp: bigint | number) {
40
+ const targetBigInt = BigInt(targetTimestamp);
41
+ const currentTimestamp = BigInt(await this.eth.lastBlockTimestamp());
41
42
 
42
- // We warp the L1 timestamp
43
- 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
+ }
44
48
 
45
- // Wait until an L2 block is mined
46
- const sequencer = sequencerClient.getSequencer();
47
- const minTxsPerBlock = sequencer.getConfig().minTxsPerBlock;
48
- sequencer.updateConfig({ minTxsPerBlock: 0 });
49
+ const currentSlot = await this.rollup.getSlot();
50
+ const targetSlot = await this.rollup.getSlotAt(targetBigInt);
49
51
 
50
- await retryUntil(
51
- async () => {
52
- const newL2BlockNumber = await node.getBlockNumber();
53
- return newL2BlockNumber > currentL2BlockNumber;
54
- },
55
- 'new block after warping L2 time',
56
- 36,
57
- 1,
58
- );
52
+ let effectiveTimestamp = targetBigInt;
59
53
 
60
- // Restore original minTxsPerBlock
61
- 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();
62
67
  }
63
68
 
64
69
  /**
65
70
  * Warps the L1 timestamp forward by a specified duration and mines an L2 block that advances the L2 timestamp at
66
- * least by the duration. L2 timestamp is not advanced exactly by the duration because it is determined by the slot
67
- * number, which advances in fixed intervals.
68
- * This is useful for testing time-dependent contract behavior.
69
- * @param sequencerClient - The sequencer client to use to force an empty block to be mined.
70
- * @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.
71
74
  * @param duration - The duration to advance time by (in seconds)
72
75
  */
73
- async warpL2TimeAtLeastBy(sequencerClient: SequencerClient, node: AztecNode, duration: bigint | number) {
74
- 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();
75
82
  const targetTimestamp = BigInt(currentTimestamp) + BigInt(duration);
76
- await this.warpL2TimeAtLeastTo(sequencerClient, node, targetTimestamp);
83
+ await this.warpL2TimeAtLeastTo(node, targetTimestamp);
77
84
  }
78
85
  }