@aztec/cli 2.0.3 → 2.1.0-rc.2

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.
@@ -5,10 +5,16 @@ import type { SharedNodeConfig } from '@aztec/node-lib/config';
5
5
  import type { P2PConfig } from '@aztec/p2p/config';
6
6
  import type { SlasherConfig } from '@aztec/stdlib/interfaces/server';
7
7
 
8
- import { mkdir, readFile, stat, writeFile } from 'fs/promises';
9
- import path, { dirname, join } from 'path';
8
+ import path, { join } from 'path';
10
9
 
11
10
  import publicIncludeMetrics from '../../public_include_metric_prefixes.json' with { type: 'json' };
11
+ import { cachedFetch } from './cached_fetch.js';
12
+ import { enrichEthAddressVar, enrichVar } from './enrich_env.js';
13
+
14
+ const SNAPSHOTS_URL = 'https://aztec-labs-snapshots.com';
15
+
16
+ const defaultDBMapSizeKb = 128 * 1_024 * 1_024; // 128 GB
17
+ const tbMapSizeKb = 1_024 * 1_024 * 1_024; // 1 TB
12
18
 
13
19
  export type L2ChainConfig = L1ContractsConfig &
14
20
  Pick<P2PConfig, 'txPoolDeleteTxsAfterReorg'> &
@@ -18,13 +24,10 @@ export type L2ChainConfig = L1ContractsConfig &
18
24
  sponsoredFPC: boolean;
19
25
  p2pEnabled: boolean;
20
26
  p2pBootstrapNodes: string[];
21
- registryAddress: string;
22
- slashFactoryAddress: string;
23
- feeAssetHandlerAddress: string;
24
27
  seqMinTxsPerBlock: number;
25
28
  seqMaxTxsPerBlock: number;
26
29
  realProofs: boolean;
27
- snapshotsUrl: string;
30
+ snapshotsUrls: string[];
28
31
  autoUpdate: SharedNodeConfig['autoUpdate'];
29
32
  autoUpdateUrl?: string;
30
33
  maxTxPoolSize: number;
@@ -32,6 +35,14 @@ export type L2ChainConfig = L1ContractsConfig &
32
35
  publicMetricsCollectorUrl?: string;
33
36
  publicMetricsCollectFrom?: string[];
34
37
 
38
+ // Setting the dbMapSize provides the default for every DB in the node.
39
+ // Then we explicitly override the sizes for the archiver and the larger trees.
40
+ dbMapSizeKb: number;
41
+ archiverStoreMapSizeKb: number;
42
+ noteHashTreeMapSizeKb: number;
43
+ nullifierTreeMapSizeKb: number;
44
+ publicDataTreeMapSizeKb: number;
45
+
35
46
  // Control whether sentinel is enabled or not. Needed for slashing
36
47
  sentinelEnabled: boolean;
37
48
  };
@@ -47,6 +58,8 @@ const DefaultSlashConfig = {
47
58
  slashingOffsetInRounds: 2,
48
59
  /** No slash vetoer */
49
60
  slashingVetoer: EthAddress.ZERO,
61
+ /** Use default disable duration */
62
+ slashingDisableDuration: DefaultL1ContractsConfig.slashingDisableDuration,
50
63
  /** Use default slash amounts */
51
64
  slashAmountSmall: DefaultL1ContractsConfig.slashAmountSmall,
52
65
  slashAmountMedium: DefaultL1ContractsConfig.slashAmountMedium,
@@ -63,11 +76,20 @@ const DefaultSlashConfig = {
63
76
  slashProposeInvalidAttestationsPenalty: DefaultL1ContractsConfig.slashAmountLarge,
64
77
  slashAttestDescendantOfInvalidPenalty: DefaultL1ContractsConfig.slashAmountLarge,
65
78
  slashUnknownPenalty: DefaultL1ContractsConfig.slashAmountSmall,
66
- slashBroadcastedInvalidBlockPenalty: DefaultL1ContractsConfig.slashAmountMedium,
79
+ slashBroadcastedInvalidBlockPenalty: 0n, // DefaultL1ContractsConfig.slashAmountSmall // Disabled until further testing
67
80
  slashMaxPayloadSize: 50,
68
81
  slashGracePeriodL2Slots: 32 * 2, // Two epochs from genesis
69
82
  slashOffenseExpirationRounds: 8,
70
83
  sentinelEnabled: true,
84
+ slashExecuteRoundsLookBack: 4,
85
+ } satisfies Partial<L2ChainConfig>;
86
+
87
+ const DefaultNetworkDBMapSizeConfig = {
88
+ dbMapSizeKb: defaultDBMapSizeKb,
89
+ archiverStoreMapSizeKb: tbMapSizeKb,
90
+ noteHashTreeMapSizeKb: tbMapSizeKb,
91
+ nullifierTreeMapSizeKb: tbMapSizeKb,
92
+ publicDataTreeMapSizeKb: tbMapSizeKb,
71
93
  } satisfies Partial<L2ChainConfig>;
72
94
 
73
95
  export const stagingIgnitionL2ChainConfig: L2ChainConfig = {
@@ -76,13 +98,10 @@ export const stagingIgnitionL2ChainConfig: L2ChainConfig = {
76
98
  sponsoredFPC: false,
77
99
  p2pEnabled: true,
78
100
  p2pBootstrapNodes: [],
79
- registryAddress: '0x6c04b1c116ec1ea0f918e3cc91e87b0af3e23b73',
80
- slashFactoryAddress: '',
81
- feeAssetHandlerAddress: '',
82
101
  seqMinTxsPerBlock: 0,
83
102
  seqMaxTxsPerBlock: 0,
84
103
  realProofs: true,
85
- snapshotsUrl: 'https://storage.googleapis.com/aztec-testnet/snapshots/staging-ignition/',
104
+ snapshotsUrls: [`${SNAPSHOTS_URL}/staging-ignition/`],
86
105
  autoUpdate: 'config-and-version',
87
106
  autoUpdateUrl: 'https://storage.googleapis.com/aztec-testnet/auto-update/staging-ignition.json',
88
107
  maxTxPoolSize: 100_000_000, // 100MB
@@ -99,6 +118,8 @@ export const stagingIgnitionL2ChainConfig: L2ChainConfig = {
99
118
  aztecEpochDuration: 32,
100
119
  /** The target validator committee size. */
101
120
  aztecTargetCommitteeSize: 24,
121
+ /** The number of epochs to lag behind the current epoch for validator selection. */
122
+ lagInEpochs: 2,
102
123
  /** The number of epochs after an epoch ends that proofs are still accepted. */
103
124
  aztecProofSubmissionEpochs: 1,
104
125
  /** How many sequencers must agree with a slash for it to be executed. */
@@ -107,6 +128,7 @@ export const stagingIgnitionL2ChainConfig: L2ChainConfig = {
107
128
  slashingRoundSizeInEpochs: 4,
108
129
  slashingLifetimeInRounds: 40,
109
130
  slashingExecutionDelayInRounds: 28,
131
+ slashingDisableDuration: 5 * 24 * 60 * 60, // 5 days in seconds
110
132
  slashAmountSmall: 2_000n * 10n ** 18n,
111
133
  slashAmountMedium: 10_000n * 10n ** 18n,
112
134
  slashAmountLarge: 50_000n * 10n ** 18n,
@@ -124,6 +146,7 @@ export const stagingIgnitionL2ChainConfig: L2ChainConfig = {
124
146
 
125
147
  ejectionThreshold: 100_000n * 10n ** 18n,
126
148
  activationThreshold: 200_000n * 10n ** 18n,
149
+ localEjectionThreshold: 196_000n * 10n ** 18n,
127
150
 
128
151
  governanceProposerRoundSize: 300, // TODO TMNT-322
129
152
  governanceProposerQuorum: 151, // TODO TMNT-322
@@ -140,11 +163,14 @@ export const stagingIgnitionL2ChainConfig: L2ChainConfig = {
140
163
  slashProposeInvalidAttestationsPenalty: 50_000n * 10n ** 18n,
141
164
  slashAttestDescendantOfInvalidPenalty: 50_000n * 10n ** 18n,
142
165
  slashUnknownPenalty: 2_000n * 10n ** 18n,
143
- slashBroadcastedInvalidBlockPenalty: 10_000n * 10n ** 18n,
166
+ slashBroadcastedInvalidBlockPenalty: 0n, // 10_000n * 10n ** 18n, Disabled for now until further testing
144
167
  slashMaxPayloadSize: 50,
145
168
  slashGracePeriodL2Slots: 32 * 4, // One round from genesis
146
169
  slashOffenseExpirationRounds: 8,
147
170
  sentinelEnabled: true,
171
+ slashExecuteRoundsLookBack: 4,
172
+
173
+ ...DefaultNetworkDBMapSizeConfig,
148
174
  };
149
175
 
150
176
  export const stagingPublicL2ChainConfig: L2ChainConfig = {
@@ -153,13 +179,10 @@ export const stagingPublicL2ChainConfig: L2ChainConfig = {
153
179
  sponsoredFPC: true,
154
180
  p2pEnabled: true,
155
181
  p2pBootstrapNodes: [],
156
- registryAddress: '0x2e48addca360da61e4d6c21ff2b1961af56eb83b',
157
- slashFactoryAddress: '0xe19410632fd00695bc5a08dd82044b7b26317742',
158
- feeAssetHandlerAddress: '0xb46dc3d91f849999330b6dd93473fa29fc45b076',
159
182
  seqMinTxsPerBlock: 0,
160
183
  seqMaxTxsPerBlock: 20,
161
184
  realProofs: true,
162
- snapshotsUrl: 'https://storage.googleapis.com/aztec-testnet/snapshots/staging-public/',
185
+ snapshotsUrls: [`${SNAPSHOTS_URL}/staging-public/`],
163
186
  autoUpdate: 'config-and-version',
164
187
  autoUpdateUrl: 'https://storage.googleapis.com/aztec-testnet/auto-update/staging-public.json',
165
188
  publicIncludeMetrics,
@@ -177,6 +200,10 @@ export const stagingPublicL2ChainConfig: L2ChainConfig = {
177
200
  aztecEpochDuration: 32,
178
201
  /** The target validator committee size. */
179
202
  aztecTargetCommitteeSize: 48,
203
+ /** The number of epochs to lag behind the current epoch for validator selection. */
204
+ lagInEpochs: DefaultL1ContractsConfig.lagInEpochs,
205
+ /** The local ejection threshold for a validator. Stricter than ejectionThreshold but local to a specific rollup */
206
+ localEjectionThreshold: DefaultL1ContractsConfig.localEjectionThreshold,
180
207
  /** The number of epochs after an epoch ends that proofs are still accepted. */
181
208
  aztecProofSubmissionEpochs: 1,
182
209
  /** The deposit amount for a validator */
@@ -195,6 +222,8 @@ export const stagingPublicL2ChainConfig: L2ChainConfig = {
195
222
  exitDelaySeconds: DefaultL1ContractsConfig.exitDelaySeconds,
196
223
 
197
224
  ...DefaultSlashConfig,
225
+
226
+ ...DefaultNetworkDBMapSizeConfig,
198
227
  };
199
228
 
200
229
  export const testnetL2ChainConfig: L2ChainConfig = {
@@ -203,13 +232,10 @@ export const testnetL2ChainConfig: L2ChainConfig = {
203
232
  sponsoredFPC: true,
204
233
  p2pEnabled: true,
205
234
  p2pBootstrapNodes: [],
206
- registryAddress: '0xc2f24280f5c7f4897370dfdeb30f79ded14f1c81',
207
- slashFactoryAddress: '0x76291684ae928d6e5bcff348e36917f4cc532db8',
208
- feeAssetHandlerAddress: '0x50513c3713ffd33301e85f30d86ab764df421fe9',
209
235
  seqMinTxsPerBlock: 0,
210
236
  seqMaxTxsPerBlock: 20,
211
237
  realProofs: true,
212
- snapshotsUrl: 'https://storage.googleapis.com/aztec-testnet/snapshots/testnet/',
238
+ snapshotsUrls: [`${SNAPSHOTS_URL}/testnet/`],
213
239
  autoUpdate: 'config-and-version',
214
240
  autoUpdateUrl: 'https://storage.googleapis.com/aztec-testnet/auto-update/testnet.json',
215
241
  maxTxPoolSize: 100_000_000, // 100MB
@@ -227,12 +253,16 @@ export const testnetL2ChainConfig: L2ChainConfig = {
227
253
  aztecEpochDuration: 32,
228
254
  /** The target validator committee size. */
229
255
  aztecTargetCommitteeSize: 48,
256
+ /** The number of epochs to lag behind the current epoch for validator selection. */
257
+ lagInEpochs: 2,
230
258
  /** The number of epochs after an epoch ends that proofs are still accepted. */
231
259
  aztecProofSubmissionEpochs: 1,
232
260
  /** The deposit amount for a validator */
233
261
  activationThreshold: DefaultL1ContractsConfig.activationThreshold,
234
262
  /** The minimum stake for a validator. */
235
263
  ejectionThreshold: DefaultL1ContractsConfig.ejectionThreshold,
264
+ /** The local ejection threshold for a validator. Stricter than ejectionThreshold but local to a specific rollup */
265
+ localEjectionThreshold: DefaultL1ContractsConfig.localEjectionThreshold,
236
266
  /** The slashing round size */
237
267
  slashingRoundSizeInEpochs: DefaultL1ContractsConfig.slashingRoundSizeInEpochs,
238
268
  /** Governance proposing round size */
@@ -248,42 +278,101 @@ export const testnetL2ChainConfig: L2ChainConfig = {
248
278
  slashPrunePenalty: 0n,
249
279
  slashDataWithholdingPenalty: 0n,
250
280
  slashInactivityPenalty: DefaultL1ContractsConfig.slashAmountMedium,
281
+
282
+ ...DefaultNetworkDBMapSizeConfig,
283
+ };
284
+
285
+ export const ignitionL2ChainConfig: L2ChainConfig = {
286
+ l1ChainId: 1,
287
+ testAccounts: false,
288
+ sponsoredFPC: false,
289
+ p2pEnabled: true,
290
+ p2pBootstrapNodes: [],
291
+ seqMinTxsPerBlock: 0,
292
+ seqMaxTxsPerBlock: 0,
293
+ realProofs: true,
294
+ snapshotsUrls: [`${SNAPSHOTS_URL}/ignition/`],
295
+ autoUpdate: 'notify',
296
+ autoUpdateUrl: 'https://storage.googleapis.com/aztec-testnet/auto-update/ignition.json',
297
+ maxTxPoolSize: 100_000_000, // 100MB
298
+ publicIncludeMetrics,
299
+ publicMetricsCollectorUrl: 'https://telemetry.alpha-testnet.aztec-labs.com/v1/metrics',
300
+ publicMetricsCollectFrom: ['sequencer'],
301
+ txPoolDeleteTxsAfterReorg: false,
302
+
303
+ /** How many seconds an L1 slot lasts. */
304
+ ethereumSlotDuration: 12,
305
+ /** How many seconds an L2 slots lasts (must be multiple of ethereum slot duration). */
306
+ aztecSlotDuration: 72,
307
+ /** How many L2 slots an epoch lasts. */
308
+ aztecEpochDuration: 32,
309
+ /** The target validator committee size. */
310
+ aztecTargetCommitteeSize: 24,
311
+ /** The number of epochs to lag behind the current epoch for validator selection. */
312
+ lagInEpochs: 2,
313
+ /** The number of epochs after an epoch ends that proofs are still accepted. */
314
+ aztecProofSubmissionEpochs: 1,
315
+ /** How many sequencers must agree with a slash for it to be executed. */
316
+ slashingQuorum: 65,
317
+
318
+ slashingRoundSizeInEpochs: 4,
319
+ slashingLifetimeInRounds: 40,
320
+ slashingExecutionDelayInRounds: 28,
321
+ slashingDisableDuration: 5 * 24 * 60 * 60, // 5 days in seconds
322
+ slashAmountSmall: 2_000n * 10n ** 18n,
323
+ slashAmountMedium: 10_000n * 10n ** 18n,
324
+ slashAmountLarge: 50_000n * 10n ** 18n,
325
+ slashingOffsetInRounds: 2,
326
+ slasherFlavor: 'tally',
327
+ slashingVetoer: EthAddress.ZERO, // TODO TMNT-329
328
+
329
+ /** The mana target for the rollup */
330
+ manaTarget: 0n,
331
+
332
+ exitDelaySeconds: 5 * 24 * 60 * 60,
333
+
334
+ /** The proving cost per mana */
335
+ provingCostPerMana: 0n,
336
+
337
+ ejectionThreshold: 100_000n * 10n ** 18n,
338
+ activationThreshold: 200_000n * 10n ** 18n,
339
+ localEjectionThreshold: 196_000n * 10n ** 18n,
340
+
341
+ governanceProposerRoundSize: 300, // TODO TMNT-322
342
+ governanceProposerQuorum: 151, // TODO TMNT-322
343
+
344
+ // Node slashing config
345
+ // TODO TMNT-330
346
+ slashMinPenaltyPercentage: 0.5,
347
+ slashMaxPenaltyPercentage: 2.0,
348
+ slashInactivityTargetPercentage: 0.7,
349
+ slashInactivityConsecutiveEpochThreshold: 2,
350
+ slashInactivityPenalty: 2_000n * 10n ** 18n,
351
+ slashPrunePenalty: 0n, // 2_000n * 10n ** 18n, We disable slashing for prune offenses right now
352
+ slashDataWithholdingPenalty: 0n, // 2_000n * 10n ** 18n, We disable slashing for data withholding offenses right now
353
+ slashProposeInvalidAttestationsPenalty: 50_000n * 10n ** 18n,
354
+ slashAttestDescendantOfInvalidPenalty: 50_000n * 10n ** 18n,
355
+ slashUnknownPenalty: 2_000n * 10n ** 18n,
356
+ slashBroadcastedInvalidBlockPenalty: 0n, // 10_000n * 10n ** 18n, Disabled for now until further testing
357
+ slashMaxPayloadSize: 50,
358
+ slashGracePeriodL2Slots: 32 * 4, // One round from genesis
359
+ slashOffenseExpirationRounds: 8,
360
+ sentinelEnabled: true,
361
+ slashExecuteRoundsLookBack: 4,
362
+
363
+ ...DefaultNetworkDBMapSizeConfig,
251
364
  };
252
365
 
253
366
  const BOOTNODE_CACHE_DURATION_MS = 60 * 60 * 1000; // 1 hour;
254
367
 
255
368
  export async function getBootnodes(networkName: NetworkNames, cacheDir?: string) {
256
- const cacheFile = cacheDir ? join(cacheDir, networkName, 'bootnodes.json') : undefined;
257
- try {
258
- if (cacheFile) {
259
- const info = await stat(cacheFile);
260
- if (info.mtimeMs + BOOTNODE_CACHE_DURATION_MS > Date.now()) {
261
- return JSON.parse(await readFile(cacheFile, 'utf-8'))['bootnodes'];
262
- }
263
- }
264
- } catch {
265
- // no-op. Get the remote-file
266
- }
267
-
268
369
  const url = `http://static.aztec.network/${networkName}/bootnodes.json`;
269
- const response = await fetch(url);
270
- if (!response.ok) {
271
- throw new Error(
272
- `Failed to fetch basic contract addresses from ${url}. Check you are using a correct network name.`,
273
- );
274
- }
275
- const json = await response.json();
276
-
277
- try {
278
- if (cacheFile) {
279
- await mkdir(dirname(cacheFile), { recursive: true });
280
- await writeFile(cacheFile, JSON.stringify(json), 'utf-8');
281
- }
282
- } catch {
283
- // no-op
284
- }
370
+ const data = await cachedFetch(url, {
371
+ cacheDurationMs: BOOTNODE_CACHE_DURATION_MS,
372
+ cacheFile: cacheDir ? join(cacheDir, networkName, 'bootnodes.json') : undefined,
373
+ });
285
374
 
286
- return json['bootnodes'];
375
+ return data?.bootnodes;
287
376
  }
288
377
 
289
378
  export async function getL2ChainConfig(
@@ -297,6 +386,8 @@ export async function getL2ChainConfig(
297
386
  config = { ...testnetL2ChainConfig };
298
387
  } else if (networkName === 'staging-ignition') {
299
388
  config = { ...stagingIgnitionL2ChainConfig };
389
+ } else if (networkName === 'ignition') {
390
+ config = { ...ignitionL2ChainConfig };
300
391
  }
301
392
  if (!config) {
302
393
  return undefined;
@@ -309,23 +400,6 @@ export async function getL2ChainConfig(
309
400
  return config;
310
401
  }
311
402
 
312
- function enrichVar(envVar: EnvVar, value: string | undefined) {
313
- // Don't override
314
- if (process.env[envVar] || value === undefined) {
315
- return;
316
- }
317
- process.env[envVar] = value;
318
- }
319
-
320
- function enrichEthAddressVar(envVar: EnvVar, value: string) {
321
- // EthAddress doesn't like being given empty strings
322
- if (value === '') {
323
- enrichVar(envVar, EthAddress.ZERO.toString());
324
- return;
325
- }
326
- enrichVar(envVar, value);
327
- }
328
-
329
403
  function getDefaultDataDir(networkName: NetworkNames): string {
330
404
  return path.join(process.env.HOME || '~', '.aztec', networkName, 'data');
331
405
  }
@@ -352,9 +426,15 @@ export async function enrichEnvironmentWithChainConfig(networkName: NetworkNames
352
426
  enrichVar('SEQ_MAX_TX_PER_BLOCK', config.seqMaxTxsPerBlock.toString());
353
427
  enrichVar('PROVER_REAL_PROOFS', config.realProofs.toString());
354
428
  enrichVar('PXE_PROVER_ENABLED', config.realProofs.toString());
355
- enrichVar('SYNC_SNAPSHOTS_URL', config.snapshotsUrl);
429
+ enrichVar('SYNC_SNAPSHOTS_URLS', config.snapshotsUrls.join(','));
356
430
  enrichVar('P2P_MAX_TX_POOL_SIZE', config.maxTxPoolSize.toString());
357
431
 
432
+ enrichVar('DATA_STORE_MAP_SIZE_KB', config.dbMapSizeKb.toString());
433
+ enrichVar('ARCHIVER_STORE_MAP_SIZE_KB', config.archiverStoreMapSizeKb.toString());
434
+ enrichVar('NOTE_HASH_TREE_MAP_SIZE_KB', config.noteHashTreeMapSizeKb.toString());
435
+ enrichVar('NULLIFIER_TREE_MAP_SIZE_KB', config.nullifierTreeMapSizeKb.toString());
436
+ enrichVar('PUBLIC_DATA_TREE_MAP_SIZE_KB', config.publicDataTreeMapSizeKb.toString());
437
+
358
438
  if (config.autoUpdate) {
359
439
  enrichVar('AUTO_UPDATE', config.autoUpdate?.toString());
360
440
  }
@@ -375,10 +455,6 @@ export async function enrichEnvironmentWithChainConfig(networkName: NetworkNames
375
455
  enrichVar('PUBLIC_OTEL_COLLECT_FROM', config.publicMetricsCollectFrom.join(','));
376
456
  }
377
457
 
378
- enrichEthAddressVar('REGISTRY_CONTRACT_ADDRESS', config.registryAddress);
379
- enrichEthAddressVar('SLASH_FACTORY_CONTRACT_ADDRESS', config.slashFactoryAddress);
380
- enrichEthAddressVar('FEE_ASSET_HANDLER_CONTRACT_ADDRESS', config.feeAssetHandlerAddress);
381
-
382
458
  // Deployment stuff
383
459
  enrichVar('ETHEREUM_SLOT_DURATION', config.ethereumSlotDuration.toString());
384
460
  enrichVar('AZTEC_SLOT_DURATION', config.aztecSlotDuration.toString());
@@ -387,6 +463,7 @@ export async function enrichEnvironmentWithChainConfig(networkName: NetworkNames
387
463
  enrichVar('AZTEC_PROOF_SUBMISSION_EPOCHS', config.aztecProofSubmissionEpochs.toString());
388
464
  enrichVar('AZTEC_ACTIVATION_THRESHOLD', config.activationThreshold.toString());
389
465
  enrichVar('AZTEC_EJECTION_THRESHOLD', config.ejectionThreshold.toString());
466
+ enrichVar('AZTEC_LOCAL_EJECTION_THRESHOLD', config.localEjectionThreshold.toString());
390
467
  enrichVar('AZTEC_SLASHING_QUORUM', config.slashingQuorum?.toString());
391
468
  enrichVar('AZTEC_SLASHING_ROUND_SIZE_IN_EPOCHS', config.slashingRoundSizeInEpochs.toString());
392
469
  enrichVar('AZTEC_GOVERNANCE_PROPOSER_QUORUM', config.governanceProposerQuorum?.toString());
@@ -0,0 +1,15 @@
1
+ import { EthAddress } from '@aztec/aztec.js';
2
+ import type { EnvVar } from '@aztec/foundation/config';
3
+
4
+ export function enrichVar(envVar: EnvVar, value: string | undefined) {
5
+ // Don't override
6
+ if (process.env[envVar] || value === undefined) {
7
+ return;
8
+ }
9
+ process.env[envVar] = value;
10
+ }
11
+
12
+ export function enrichEthAddressVar(envVar: EnvVar, value: string) {
13
+ // EthAddress doesn't like being given empty strings
14
+ enrichVar(envVar, value || EthAddress.ZERO.toString());
15
+ }
@@ -1,2 +1,4 @@
1
+ export * from './cached_fetch.js';
1
2
  export * from './chain_l2_config.js';
2
3
  export * from './get_l1_config.js';
4
+ export * from './network_config.js';
@@ -0,0 +1,102 @@
1
+ import { type NetworkConfig, NetworkConfigMapSchema, type NetworkNames } from '@aztec/foundation/config';
2
+
3
+ import { readFile } from 'fs/promises';
4
+ import { join } from 'path';
5
+
6
+ import { cachedFetch } from './cached_fetch.js';
7
+ import { enrichEthAddressVar, enrichVar } from './enrich_env.js';
8
+
9
+ const DEFAULT_CONFIG_URL =
10
+ 'https://raw.githubusercontent.com/AztecProtocol/networks/refs/heads/main/network_config.json';
11
+ const NETWORK_CONFIG_CACHE_DURATION_MS = 60 * 60 * 1000; // 1 hour
12
+
13
+ /**
14
+ * Fetches remote network configuration from GitHub with caching support.
15
+ * Uses the reusable cachedFetch utility.
16
+ *
17
+ * @param networkName - The network name to fetch config for
18
+ * @param cacheDir - Optional cache directory for storing fetched config
19
+ * @returns Remote configuration for the specified network, or undefined if not found/error
20
+ */
21
+ export async function getNetworkConfig(
22
+ networkName: NetworkNames,
23
+ cacheDir?: string,
24
+ ): Promise<NetworkConfig | undefined> {
25
+ let url: URL | undefined;
26
+ const configLocation = process.env.NETWORK_CONFIG_LOCATION || DEFAULT_CONFIG_URL;
27
+
28
+ if (!configLocation) {
29
+ return undefined;
30
+ }
31
+
32
+ try {
33
+ if (configLocation.includes('://')) {
34
+ url = new URL(configLocation);
35
+ } else {
36
+ url = new URL(`file://${configLocation}`);
37
+ }
38
+ } catch {
39
+ /* no-op */
40
+ }
41
+
42
+ if (!url) {
43
+ return undefined;
44
+ }
45
+
46
+ try {
47
+ let rawConfig: any;
48
+
49
+ if (url.protocol === 'http:' || url.protocol === 'https:') {
50
+ rawConfig = await cachedFetch(url.href, {
51
+ cacheDurationMs: NETWORK_CONFIG_CACHE_DURATION_MS,
52
+ cacheFile: cacheDir ? join(cacheDir, networkName, 'network_config.json') : undefined,
53
+ });
54
+ } else if (url.protocol === 'file:') {
55
+ rawConfig = JSON.parse(await readFile(url.pathname, 'utf-8'));
56
+ } else {
57
+ throw new Error('Unsupported Aztec network config protocol: ' + url.href);
58
+ }
59
+
60
+ if (!rawConfig) {
61
+ return undefined;
62
+ }
63
+
64
+ const networkConfigMap = NetworkConfigMapSchema.parse(rawConfig);
65
+ if (networkName in networkConfigMap) {
66
+ return networkConfigMap[networkName];
67
+ } else {
68
+ return undefined;
69
+ }
70
+ } catch {
71
+ return undefined;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Enriches environment variables with remote network configuration.
77
+ * This function is called before node config initialization to set env vars
78
+ * from the remote config, following the same pattern as enrichEnvironmentWithChainConfig().
79
+ *
80
+ * @param networkName - The network name to fetch remote config for
81
+ */
82
+ export async function enrichEnvironmentWithNetworkConfig(networkName: NetworkNames) {
83
+ if (networkName === 'local') {
84
+ return; // No remote config for local development
85
+ }
86
+
87
+ const cacheDir = process.env.DATA_DIRECTORY ? join(process.env.DATA_DIRECTORY, 'cache') : undefined;
88
+ const networkConfig = await getNetworkConfig(networkName, cacheDir);
89
+
90
+ if (!networkConfig) {
91
+ return;
92
+ }
93
+
94
+ enrichVar('BOOTSTRAP_NODES', networkConfig.bootnodes.join(','));
95
+ enrichVar('L1_CHAIN_ID', String(networkConfig.l1ChainId));
96
+ enrichVar('SYNC_SNAPSHOTS_URLS', networkConfig.snapshots.join(','));
97
+
98
+ enrichEthAddressVar('REGISTRY_CONTRACT_ADDRESS', networkConfig.registryAddress.toString());
99
+ if (networkConfig.feeAssetHandlerAddress) {
100
+ enrichEthAddressVar('FEE_ASSET_HANDLER_CONTRACT_ADDRESS', networkConfig.feeAssetHandlerAddress.toString());
101
+ }
102
+ }
@@ -57,6 +57,7 @@ export async function deployAztecContracts(
57
57
  feeJuicePortalInitialBalance: bigint,
58
58
  acceleratedTestDeployments: boolean,
59
59
  config: L1ContractsConfig,
60
+ existingToken: EthAddress | undefined,
60
61
  realVerifier: boolean,
61
62
  createVerificationJson: string | false,
62
63
  debugLogger: Logger,
@@ -85,6 +86,7 @@ export async function deployAztecContracts(
85
86
  acceleratedTestDeployments,
86
87
  feeJuicePortalInitialBalance,
87
88
  realVerifier,
89
+ existingTokenAddress: existingToken,
88
90
  ...config,
89
91
  },
90
92
  config,