@aztec/cli 3.0.0-nightly.20251111 → 3.0.0-nightly.20251113

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.
@@ -37,7 +37,7 @@ const DefaultSlashConfig = {
37
37
  slashExecuteRoundsLookBack: 4
38
38
  };
39
39
  const DefaultNetworkDBMapSizeConfig = {
40
- dbMapSizeKb: defaultDBMapSizeKb,
40
+ dataStoreMapSizeKb: defaultDBMapSizeKb,
41
41
  archiverStoreMapSizeKb: tbMapSizeKb,
42
42
  noteHashTreeMapSizeKb: tbMapSizeKb,
43
43
  nullifierTreeMapSizeKb: tbMapSizeKb,
@@ -49,9 +49,9 @@ export const stagingIgnitionL2ChainConfig = {
49
49
  sponsoredFPC: false,
50
50
  disableTransactions: true,
51
51
  p2pEnabled: true,
52
- p2pBootstrapNodes: [],
53
- seqMinTxsPerBlock: 0,
54
- seqMaxTxsPerBlock: 0,
52
+ bootstrapNodes: [],
53
+ minTxsPerBlock: 0,
54
+ maxTxsPerBlock: 0,
55
55
  realProofs: true,
56
56
  snapshotsUrls: [
57
57
  `${SNAPSHOTS_URL}/staging-ignition/`
@@ -65,6 +65,8 @@ export const stagingIgnitionL2ChainConfig = {
65
65
  publicMetricsCollectFrom: [
66
66
  'sequencer'
67
67
  ],
68
+ txPoolDeleteTxsAfterReorg: false,
69
+ blobAllowEmptySources: true,
68
70
  /** How many seconds an L1 slot lasts. */ ethereumSlotDuration: 12,
69
71
  /** How many seconds an L2 slots lasts (must be multiple of ethereum slot duration). */ aztecSlotDuration: 72,
70
72
  /** How many L2 slots an epoch lasts. */ aztecEpochDuration: 32,
@@ -115,9 +117,9 @@ export const stagingPublicL2ChainConfig = {
115
117
  sponsoredFPC: true,
116
118
  disableTransactions: false,
117
119
  p2pEnabled: true,
118
- p2pBootstrapNodes: [],
119
- seqMinTxsPerBlock: 0,
120
- seqMaxTxsPerBlock: 20,
120
+ bootstrapNodes: [],
121
+ minTxsPerBlock: 0,
122
+ maxTxsPerBlock: 20,
121
123
  realProofs: true,
122
124
  snapshotsUrls: [
123
125
  `${SNAPSHOTS_URL}/staging-public/`
@@ -131,6 +133,7 @@ export const stagingPublicL2ChainConfig = {
131
133
  'sequencer'
132
134
  ],
133
135
  maxTxPoolSize: 100_000_000,
136
+ txPoolDeleteTxsAfterReorg: true,
134
137
  // Deployment stuff
135
138
  /** How many seconds an L1 slot lasts. */ ethereumSlotDuration: 12,
136
139
  /** How many seconds an L2 slots lasts (must be multiple of ethereum slot duration). */ aztecSlotDuration: 36,
@@ -155,9 +158,9 @@ export const nextNetL2ChainConfig = {
155
158
  sponsoredFPC: true,
156
159
  p2pEnabled: true,
157
160
  disableTransactions: false,
158
- p2pBootstrapNodes: [],
159
- seqMinTxsPerBlock: 0,
160
- seqMaxTxsPerBlock: 8,
161
+ bootstrapNodes: [],
162
+ minTxsPerBlock: 0,
163
+ maxTxsPerBlock: 8,
161
164
  realProofs: true,
162
165
  snapshotsUrls: [],
163
166
  autoUpdate: 'config-and-version',
@@ -169,6 +172,7 @@ export const nextNetL2ChainConfig = {
169
172
  ''
170
173
  ],
171
174
  maxTxPoolSize: 100_000_000,
175
+ txPoolDeleteTxsAfterReorg: false,
172
176
  // Deployment stuff
173
177
  /** How many seconds an L1 slot lasts. */ ethereumSlotDuration: 12,
174
178
  /** How many seconds an L2 slots lasts (must be multiple of ethereum slot duration). */ aztecSlotDuration: 36,
@@ -193,9 +197,9 @@ export const testnetL2ChainConfig = {
193
197
  sponsoredFPC: true,
194
198
  p2pEnabled: true,
195
199
  disableTransactions: true,
196
- p2pBootstrapNodes: [],
197
- seqMinTxsPerBlock: 0,
198
- seqMaxTxsPerBlock: 0,
200
+ bootstrapNodes: [],
201
+ minTxsPerBlock: 0,
202
+ maxTxsPerBlock: 0,
199
203
  realProofs: true,
200
204
  snapshotsUrls: [
201
205
  `${SNAPSHOTS_URL}/testnet/`
@@ -209,6 +213,7 @@ export const testnetL2ChainConfig = {
209
213
  publicMetricsCollectFrom: [
210
214
  'sequencer'
211
215
  ],
216
+ txPoolDeleteTxsAfterReorg: true,
212
217
  skipArchiverInitialSync: true,
213
218
  blobAllowEmptySources: true,
214
219
  /** How many seconds an L1 slot lasts. */ ethereumSlotDuration: 12,
@@ -257,14 +262,15 @@ export const testnetL2ChainConfig = {
257
262
  ...DefaultNetworkDBMapSizeConfig
258
263
  };
259
264
  export const mainnetL2ChainConfig = {
265
+ txPoolDeleteTxsAfterReorg: true,
266
+ disableTransactions: true,
260
267
  l1ChainId: 1,
261
268
  testAccounts: false,
262
269
  sponsoredFPC: false,
263
270
  p2pEnabled: true,
264
- disableTransactions: true,
265
- p2pBootstrapNodes: [],
266
- seqMinTxsPerBlock: 0,
267
- seqMaxTxsPerBlock: 0,
271
+ bootstrapNodes: [],
272
+ minTxsPerBlock: 0,
273
+ maxTxsPerBlock: 0,
268
274
  realProofs: true,
269
275
  snapshotsUrls: [
270
276
  `${SNAPSHOTS_URL}/mainnet/`
@@ -329,9 +335,9 @@ export const devnetL2ChainConfig = {
329
335
  sponsoredFPC: true,
330
336
  p2pEnabled: true,
331
337
  disableTransactions: false,
332
- p2pBootstrapNodes: [],
333
- seqMinTxsPerBlock: 0,
334
- seqMaxTxsPerBlock: 8,
338
+ bootstrapNodes: [],
339
+ minTxsPerBlock: 0,
340
+ maxTxsPerBlock: 8,
335
341
  realProofs: false,
336
342
  snapshotsUrls: [],
337
343
  autoUpdate: 'config-and-version',
@@ -343,6 +349,7 @@ export const devnetL2ChainConfig = {
343
349
  ''
344
350
  ],
345
351
  maxTxPoolSize: 100_000_000,
352
+ txPoolDeleteTxsAfterReorg: true,
346
353
  // Deployment stuff
347
354
  /** How many seconds an L1 slot lasts. */ ethereumSlotDuration: 12,
348
355
  /** How many seconds an L2 slots lasts (must be multiple of ethereum slot duration). */ aztecSlotDuration: 36,
@@ -393,7 +400,7 @@ export function getL2ChainConfig(networkName) {
393
400
  function getDefaultDataDir(networkName) {
394
401
  return path.join(process.env.HOME || '~', '.aztec', networkName, 'data');
395
402
  }
396
- export function enrichEnvironmentWithChainConfig(networkName) {
403
+ export function enrichEnvironmentWithChainName(networkName) {
397
404
  if (networkName === 'local') {
398
405
  return;
399
406
  }
@@ -402,18 +409,22 @@ export function enrichEnvironmentWithChainConfig(networkName) {
402
409
  if (!config) {
403
410
  throw new Error(`Unknown network name: ${networkName}`);
404
411
  }
405
- enrichVar('BOOTSTRAP_NODES', config.p2pBootstrapNodes.join(','));
412
+ enrichEnvironmentWithChainConfig(config);
413
+ }
414
+ export function enrichEnvironmentWithChainConfig(config) {
415
+ enrichVar('BOOTSTRAP_NODES', config.bootstrapNodes.join(','));
406
416
  enrichVar('TEST_ACCOUNTS', config.testAccounts.toString());
407
417
  enrichVar('SPONSORED_FPC', config.sponsoredFPC.toString());
408
418
  enrichVar('P2P_ENABLED', config.p2pEnabled.toString());
409
419
  enrichVar('L1_CHAIN_ID', config.l1ChainId.toString());
410
- enrichVar('SEQ_MIN_TX_PER_BLOCK', config.seqMinTxsPerBlock.toString());
411
- enrichVar('SEQ_MAX_TX_PER_BLOCK', config.seqMaxTxsPerBlock.toString());
420
+ enrichVar('SEQ_MIN_TX_PER_BLOCK', config.minTxsPerBlock.toString());
421
+ enrichVar('SEQ_MAX_TX_PER_BLOCK', config.maxTxsPerBlock.toString());
412
422
  enrichVar('PROVER_REAL_PROOFS', config.realProofs.toString());
413
423
  enrichVar('PXE_PROVER_ENABLED', config.realProofs.toString());
414
424
  enrichVar('SYNC_SNAPSHOTS_URLS', config.snapshotsUrls.join(','));
415
425
  enrichVar('P2P_MAX_TX_POOL_SIZE', config.maxTxPoolSize.toString());
416
- enrichVar('DATA_STORE_MAP_SIZE_KB', config.dbMapSizeKb.toString());
426
+ enrichVar('P2P_TX_POOL_DELETE_TXS_AFTER_REORG', config.txPoolDeleteTxsAfterReorg.toString());
427
+ enrichVar('DATA_STORE_MAP_SIZE_KB', config.dataStoreMapSizeKb.toString());
417
428
  enrichVar('ARCHIVER_STORE_MAP_SIZE_KB', config.archiverStoreMapSizeKb.toString());
418
429
  enrichVar('NOTE_HASH_TREE_MAP_SIZE_KB', config.noteHashTreeMapSizeKb.toString());
419
430
  enrichVar('NULLIFIER_TREE_MAP_SIZE_KB', config.nullifierTreeMapSizeKb.toString());
@@ -445,6 +456,7 @@ export function enrichEnvironmentWithChainConfig(networkName) {
445
456
  enrichVar('AZTEC_SLOT_DURATION', config.aztecSlotDuration.toString());
446
457
  enrichVar('AZTEC_EPOCH_DURATION', config.aztecEpochDuration.toString());
447
458
  enrichVar('AZTEC_TARGET_COMMITTEE_SIZE', config.aztecTargetCommitteeSize.toString());
459
+ enrichVar('AZTEC_LAG_IN_EPOCHS', config.lagInEpochs.toString());
448
460
  enrichVar('AZTEC_PROOF_SUBMISSION_EPOCHS', config.aztecProofSubmissionEpochs.toString());
449
461
  enrichVar('AZTEC_ACTIVATION_THRESHOLD', config.activationThreshold.toString());
450
462
  enrichVar('AZTEC_EJECTION_THRESHOLD', config.ejectionThreshold.toString());
@@ -462,6 +474,7 @@ export function enrichEnvironmentWithChainConfig(networkName) {
462
474
  enrichVar('AZTEC_SLASHING_EXECUTION_DELAY_IN_ROUNDS', config.slashingExecutionDelayInRounds.toString());
463
475
  enrichVar('AZTEC_SLASHING_OFFSET_IN_ROUNDS', config.slashingOffsetInRounds.toString());
464
476
  enrichVar('AZTEC_SLASHER_FLAVOR', config.slasherFlavor);
477
+ enrichVar('AZTEC_SLASHING_DISABLE_DURATION', config.slashingDisableDuration.toString());
465
478
  enrichVar('AZTEC_EXIT_DELAY_SECONDS', config.exitDelaySeconds.toString());
466
479
  enrichEthAddressVar('AZTEC_SLASHING_VETOER', config.slashingVetoer.toString());
467
480
  // Slashing
@@ -478,6 +491,8 @@ export function enrichEnvironmentWithChainConfig(networkName) {
478
491
  enrichVar('SLASH_INVALID_BLOCK_PENALTY', config.slashBroadcastedInvalidBlockPenalty.toString());
479
492
  enrichVar('SLASH_OFFENSE_EXPIRATION_ROUNDS', config.slashOffenseExpirationRounds.toString());
480
493
  enrichVar('SLASH_MAX_PAYLOAD_SIZE', config.slashMaxPayloadSize.toString());
494
+ enrichVar('SLASH_GRACE_PERIOD_L2_SLOTS', config.slashGracePeriodL2Slots.toString());
495
+ enrichVar('SLASH_EXECUTE_ROUNDS_LOOK_BACK', config.slashExecuteRoundsLookBack.toString());
481
496
  enrichVar('SENTINEL_ENABLED', config.sentinelEnabled.toString());
482
497
  enrichVar('TRANSACTIONS_DISABLED', config.disableTransactions.toString());
483
498
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/cli",
3
- "version": "3.0.0-nightly.20251111",
3
+ "version": "3.0.0-nightly.20251113",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./contracts": "./dest/cmds/contracts/index.js",
@@ -71,21 +71,21 @@
71
71
  ]
72
72
  },
73
73
  "dependencies": {
74
- "@aztec/accounts": "3.0.0-nightly.20251111",
75
- "@aztec/archiver": "3.0.0-nightly.20251111",
76
- "@aztec/aztec.js": "3.0.0-nightly.20251111",
77
- "@aztec/constants": "3.0.0-nightly.20251111",
78
- "@aztec/entrypoints": "3.0.0-nightly.20251111",
79
- "@aztec/ethereum": "3.0.0-nightly.20251111",
80
- "@aztec/foundation": "3.0.0-nightly.20251111",
81
- "@aztec/l1-artifacts": "3.0.0-nightly.20251111",
82
- "@aztec/node-keystore": "3.0.0-nightly.20251111",
83
- "@aztec/node-lib": "3.0.0-nightly.20251111",
84
- "@aztec/p2p": "3.0.0-nightly.20251111",
85
- "@aztec/protocol-contracts": "3.0.0-nightly.20251111",
86
- "@aztec/stdlib": "3.0.0-nightly.20251111",
87
- "@aztec/test-wallet": "3.0.0-nightly.20251111",
88
- "@aztec/world-state": "3.0.0-nightly.20251111",
74
+ "@aztec/accounts": "3.0.0-nightly.20251113",
75
+ "@aztec/archiver": "3.0.0-nightly.20251113",
76
+ "@aztec/aztec.js": "3.0.0-nightly.20251113",
77
+ "@aztec/constants": "3.0.0-nightly.20251113",
78
+ "@aztec/entrypoints": "3.0.0-nightly.20251113",
79
+ "@aztec/ethereum": "3.0.0-nightly.20251113",
80
+ "@aztec/foundation": "3.0.0-nightly.20251113",
81
+ "@aztec/l1-artifacts": "3.0.0-nightly.20251113",
82
+ "@aztec/node-keystore": "3.0.0-nightly.20251113",
83
+ "@aztec/node-lib": "3.0.0-nightly.20251113",
84
+ "@aztec/p2p": "3.0.0-nightly.20251113",
85
+ "@aztec/protocol-contracts": "3.0.0-nightly.20251113",
86
+ "@aztec/stdlib": "3.0.0-nightly.20251113",
87
+ "@aztec/test-wallet": "3.0.0-nightly.20251113",
88
+ "@aztec/world-state": "3.0.0-nightly.20251113",
89
89
  "@ethersproject/wallet": "^5.8.0",
90
90
  "@iarna/toml": "^2.2.5",
91
91
  "@libp2p/peer-id-factory": "^3.0.4",
@@ -99,6 +99,9 @@
99
99
  "viem": "npm:@spalladino/viem@2.38.2-eip7594.0"
100
100
  },
101
101
  "devDependencies": {
102
+ "@aztec/aztec-node": "3.0.0-nightly.20251113",
103
+ "@aztec/kv-store": "3.0.0-nightly.20251113",
104
+ "@aztec/telemetry-client": "3.0.0-nightly.20251113",
102
105
  "@jest/globals": "^30.0.0",
103
106
  "@types/jest": "^30.0.0",
104
107
  "@types/lodash.chunk": "^4.2.9",
@@ -114,15 +117,15 @@
114
117
  "typescript": "^5.3.3"
115
118
  },
116
119
  "peerDependencies": {
117
- "@aztec/accounts": "3.0.0-nightly.20251111",
118
- "@aztec/bb-prover": "3.0.0-nightly.20251111",
119
- "@aztec/ethereum": "3.0.0-nightly.20251111",
120
- "@aztec/l1-artifacts": "3.0.0-nightly.20251111",
121
- "@aztec/noir-contracts.js": "3.0.0-nightly.20251111",
122
- "@aztec/noir-protocol-circuits-types": "3.0.0-nightly.20251111",
123
- "@aztec/noir-test-contracts.js": "3.0.0-nightly.20251111",
124
- "@aztec/protocol-contracts": "3.0.0-nightly.20251111",
125
- "@aztec/stdlib": "3.0.0-nightly.20251111"
120
+ "@aztec/accounts": "3.0.0-nightly.20251113",
121
+ "@aztec/bb-prover": "3.0.0-nightly.20251113",
122
+ "@aztec/ethereum": "3.0.0-nightly.20251113",
123
+ "@aztec/l1-artifacts": "3.0.0-nightly.20251113",
124
+ "@aztec/noir-contracts.js": "3.0.0-nightly.20251113",
125
+ "@aztec/noir-protocol-circuits-types": "3.0.0-nightly.20251113",
126
+ "@aztec/noir-test-contracts.js": "3.0.0-nightly.20251113",
127
+ "@aztec/protocol-contracts": "3.0.0-nightly.20251113",
128
+ "@aztec/stdlib": "3.0.0-nightly.20251113"
126
129
  },
127
130
  "files": [
128
131
  "dest",
@@ -29,16 +29,9 @@ export async function inspectContract(contractArtifactFile: string, debugLogger:
29
29
  log(`\tpublic bytecode commitment: ${contractClass.publicBytecodeCommitment.toString()}`);
30
30
  log(`\tpublic bytecode length: ${contractClass.packedBytecode.length} bytes (${bytecodeLengthInFields} fields)`);
31
31
 
32
- const externalFunctions = contractFns.filter(f => !f.isInternal);
33
- if (externalFunctions.length > 0) {
34
- log(`\nExternal functions:`);
35
- await Promise.all(externalFunctions.map(f => logFunction(f, log)));
36
- }
37
-
38
- const internalFunctions = contractFns.filter(f => f.isInternal);
39
- if (internalFunctions.length > 0) {
40
- log(`\nInternal functions:`);
41
- await Promise.all(internalFunctions.map(f => logFunction(f, log)));
32
+ if (contractFns.length > 0) {
33
+ log(`\nExternal contract functions:`);
34
+ await Promise.all(contractFns.map(f => logFunction(f, log)));
42
35
  }
43
36
  }
44
37
 
@@ -9,7 +9,7 @@ import { type SemVer, parse } from 'semver';
9
9
  import type { DependencyChanges } from './common.js';
10
10
  import { atomicUpdateFile } from './utils.js';
11
11
 
12
- const deprecatedNpmPackages = new Set<string>(['@aztec/cli', '@aztec/aztec-sandbox']);
12
+ const deprecatedNpmPackages = new Set<string>(['@aztec/cli', '@aztec/aztec-local-network']);
13
13
  const npmDeprecationMessage = `
14
14
  The following packages have been deprecated and will no longer be updated on the npm registry:
15
15
  ${Array.from(deprecatedNpmPackages)
@@ -30,7 +30,6 @@ export async function addValidatorKeys(existing: string, options: AddValidatorKe
30
30
  addressIndex,
31
31
  ikm,
32
32
  blsPath,
33
- blsOnly,
34
33
  json,
35
34
  feeRecipient: feeRecipientOpt,
36
35
  coinbase: coinbaseOpt,
@@ -75,7 +74,6 @@ export async function addValidatorKeys(existing: string, options: AddValidatorKe
75
74
  mnemonic: mnemonicToUse,
76
75
  ikm,
77
76
  blsPath,
78
- blsOnly,
79
77
  feeRecipient,
80
78
  coinbase,
81
79
  remoteSigner,
@@ -7,7 +7,7 @@ import { parseAztecAddress, parseEthereumAddress, parseHex, parseOptionalInteger
7
7
  export function injectCommands(program: Command, log: LogFn) {
8
8
  const group = program
9
9
  .command('validator-keys')
10
- .aliases(['valKeys'])
10
+ .aliases(['valKeys', 'valkeys'])
11
11
  .description('Manage validator keystores for node operators');
12
12
 
13
13
  group
@@ -29,13 +29,23 @@ export function injectCommands(program: Command, log: LogFn) {
29
29
  .option('--remote-signer <url>', 'Default remote signer URL for accounts in this file')
30
30
  .option('--ikm <hex>', 'Initial keying material for BLS (alternative to mnemonic)', value => parseHex(value, 32))
31
31
  .option('--bls-path <path>', 'EIP-2334 path (default m/12381/3600/0/0/0)')
32
- .option('--bls-only', 'Generate only BLS keys')
33
32
  .option(
34
33
  '--password <str>',
35
34
  'Password for writing keystore files (ETH JSON V3 and BLS EIP-2335). Empty string allowed',
36
35
  )
37
36
  .option('--out-dir <dir>', 'Output directory for generated keystore file(s)')
38
37
  .option('--json', 'Echo resulting JSON to stdout')
38
+ .option('--staker-output', 'Generate staker output JSON files for each attester')
39
+ .option('--gse-address <address>', 'GSE contract address (required with --staker-output)', parseEthereumAddress)
40
+ .option('--l1-rpc-urls <urls>', 'L1 RPC URLs (comma-separated, required with --staker-output)', value =>
41
+ value.split(','),
42
+ )
43
+ .option(
44
+ '-c, --l1-chain-id <number>',
45
+ 'L1 chain ID (required with --staker-output)',
46
+ value => parseInt(value),
47
+ 31337,
48
+ )
39
49
  .requiredOption('--fee-recipient <address>', 'Aztec address that will receive fees', parseAztecAddress)
40
50
  .action(async options => {
41
51
  const { newValidatorKeystore } = await import('./new.js');
@@ -62,7 +72,6 @@ export function injectCommands(program: Command, log: LogFn) {
62
72
  .option('--remote-signer <url>', 'Default remote signer URL for accounts in this file')
63
73
  .option('--ikm <hex>', 'Initial keying material for BLS (alternative to mnemonic)', value => parseHex(value, 32))
64
74
  .option('--bls-path <path>', 'EIP-2334 path (default m/12381/3600/0/0/0)')
65
- .option('--bls-only', 'Generate only BLS keys')
66
75
  .option('--empty', 'Generate an empty skeleton without keys')
67
76
  .option(
68
77
  '--password <str>',
@@ -76,6 +85,25 @@ export function injectCommands(program: Command, log: LogFn) {
76
85
  await addValidatorKeys(existing, options, log);
77
86
  });
78
87
 
88
+ group
89
+ .command('staker')
90
+ .summary('Generate staking JSON from keystore')
91
+ .description(
92
+ 'Reads a validator keystore and outputs staking data with BLS public keys for each attester (skips mnemonics)',
93
+ )
94
+ .requiredOption('--from <keystore>', 'Path to keystore JSON file')
95
+ .option('--password <password>', 'Password for decrypting encrypted keystores (if not specified in keystore file)')
96
+ .requiredOption('--gse-address <address>', 'GSE contract address', parseEthereumAddress)
97
+ .option('--l1-rpc-urls <urls>', 'L1 RPC URLs (comma-separated)', value => value.split(','), [
98
+ 'http://localhost:8545',
99
+ ])
100
+ .option('-c, --l1-chain-id <number>', 'L1 chain ID', value => parseInt(value), 31337)
101
+ .option('--output <file>', 'Output file path (if not specified, JSON is written to stdout)')
102
+ .action(async options => {
103
+ const { generateStakerJson } = await import('./staker.js');
104
+ await generateStakerJson(options, log);
105
+ });
106
+
79
107
  // top-level convenience: aztec generate-bls-keypair
80
108
  program
81
109
  .command('generate-bls-keypair')
@@ -1,9 +1,13 @@
1
+ import { prettyPrintJSON } from '@aztec/cli/utils';
2
+ import { GSEContract, createEthereumChain } from '@aztec/ethereum';
1
3
  import type { EthAddress } from '@aztec/foundation/eth-address';
2
4
  import type { LogFn } from '@aztec/foundation/log';
3
5
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
4
6
 
5
7
  import { wordlist } from '@scure/bip39/wordlists/english.js';
6
- import { dirname } from 'path';
8
+ import { writeFile } from 'fs/promises';
9
+ import { basename, dirname, join } from 'path';
10
+ import { createPublicClient, fallback, http } from 'viem';
7
11
  import { generateMnemonic, mnemonicToAccount } from 'viem/accounts';
8
12
 
9
13
  import {
@@ -15,6 +19,7 @@ import {
15
19
  writeEthJsonV3ToFile,
16
20
  writeKeystoreFile,
17
21
  } from './shared.js';
22
+ import { processAttesterAccounts } from './staker.js';
18
23
 
19
24
  export type NewValidatorKeystoreOptions = {
20
25
  dataDir?: string;
@@ -28,7 +33,6 @@ export type NewValidatorKeystoreOptions = {
28
33
  separatePublisher?: boolean;
29
34
  ikm?: string;
30
35
  blsPath?: string;
31
- blsOnly?: boolean;
32
36
  password?: string;
33
37
  outDir?: string;
34
38
  json?: boolean;
@@ -36,6 +40,10 @@ export type NewValidatorKeystoreOptions = {
36
40
  coinbase?: EthAddress;
37
41
  remoteSigner?: string;
38
42
  fundingAccount?: EthAddress;
43
+ stakerOutput?: boolean;
44
+ gseAddress?: EthAddress;
45
+ l1RpcUrls?: string[];
46
+ l1ChainId?: number;
39
47
  };
40
48
 
41
49
  export async function newValidatorKeystore(options: NewValidatorKeystoreOptions, log: LogFn) {
@@ -51,14 +59,30 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
51
59
  feeRecipient,
52
60
  remoteSigner,
53
61
  fundingAccount,
54
- blsOnly,
55
62
  blsPath,
56
63
  ikm,
57
64
  mnemonic: _mnemonic,
58
65
  password,
59
66
  outDir,
67
+ stakerOutput,
68
+ gseAddress,
69
+ l1RpcUrls,
70
+ l1ChainId,
60
71
  } = options;
61
72
 
73
+ // Validate staker output requirements
74
+ if (stakerOutput) {
75
+ if (!gseAddress) {
76
+ throw new Error('--gse-address is required when using --staker-output');
77
+ }
78
+ if (!l1RpcUrls || l1RpcUrls.length === 0) {
79
+ throw new Error('--l1-rpc-urls is required when using --staker-output');
80
+ }
81
+ if (l1ChainId === undefined) {
82
+ throw new Error('--l1-chain-id is required when using --staker-output');
83
+ }
84
+ }
85
+
62
86
  if (remoteSigner && !_mnemonic) {
63
87
  throw new Error(
64
88
  'Using --remote-signer requires a deterministic key source. Provide --mnemonic to derive keys, or omit --remote-signer to write new private keys to keystore.',
@@ -67,6 +91,14 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
67
91
 
68
92
  const mnemonic = _mnemonic ?? generateMnemonic(wordlist);
69
93
 
94
+ if (!_mnemonic && !json) {
95
+ log('No mnemonic provided, generating new one...');
96
+ log(`Using new mnemonic:`);
97
+ log('');
98
+ log(mnemonic);
99
+ log('');
100
+ }
101
+
70
102
  const validatorCount = typeof count === 'number' && Number.isFinite(count) && count > 0 ? Math.floor(count) : 1;
71
103
  const { outputPath } = await resolveKeystoreOutputPath(dataDir, file);
72
104
 
@@ -78,7 +110,6 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
78
110
  mnemonic,
79
111
  ikm,
80
112
  blsPath,
81
- blsOnly,
82
113
  feeRecipient,
83
114
  coinbase,
84
115
  remoteSigner,
@@ -99,15 +130,66 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
99
130
 
100
131
  await writeKeystoreFile(outputPath, keystore);
101
132
 
102
- maybePrintJson(log, json, keystore as unknown as Record<string, any>);
103
- if (!json) {
133
+ // Generate staker outputs if requested
134
+ const allStakerOutputs: any[] = [];
135
+ if (stakerOutput && gseAddress && l1RpcUrls && l1ChainId !== undefined) {
136
+ const chain = createEthereumChain(l1RpcUrls, l1ChainId);
137
+ const publicClient = createPublicClient({
138
+ chain: chain.chainInfo,
139
+ transport: fallback(l1RpcUrls.map(url => http(url))),
140
+ });
141
+ const gse = new GSEContract(publicClient, gseAddress);
142
+
143
+ const keystoreOutDir = outDir && outDir.length > 0 ? outDir : dirname(outputPath);
144
+ // Extract keystore base name without extension for unique staker output filenames
145
+ const keystoreBaseName = basename(outputPath, '.json');
146
+
147
+ // Process each validator
148
+ for (let i = 0; i < validators.length; i++) {
149
+ const validator = validators[i];
150
+ const outputs = await processAttesterAccounts(validator.attester, gse, password);
151
+
152
+ // Save each attester's staker output
153
+ for (let j = 0; j < outputs.length; j++) {
154
+ const attesterIndex = i + 1;
155
+ const stakerOutputPath = join(
156
+ keystoreOutDir,
157
+ `${keystoreBaseName}_attester${attesterIndex}_staker_output.json`,
158
+ );
159
+ await writeFile(stakerOutputPath, prettyPrintJSON(outputs[j]), 'utf-8');
160
+ allStakerOutputs.push(outputs[j]);
161
+ }
162
+ }
163
+ }
164
+
165
+ const outputData = !_mnemonic ? { ...keystore, generatedMnemonic: mnemonic } : keystore;
166
+
167
+ // Handle JSON output
168
+ if (json) {
169
+ if (stakerOutput && allStakerOutputs.length > 0) {
170
+ const combinedOutput = {
171
+ keystore: outputData,
172
+ staker: allStakerOutputs,
173
+ };
174
+ maybePrintJson(log, json, combinedOutput as unknown as Record<string, any>);
175
+ } else {
176
+ maybePrintJson(log, json, outputData as unknown as Record<string, any>);
177
+ }
178
+ } else {
104
179
  log(`Wrote validator keystore to ${outputPath}`);
180
+ if (stakerOutput && allStakerOutputs.length > 0) {
181
+ const keystoreOutDir = outDir && outDir.length > 0 ? outDir : dirname(outputPath);
182
+ log(`Wrote ${allStakerOutputs.length} staker output file(s) to ${keystoreOutDir}`);
183
+ log('');
184
+ }
105
185
  }
106
186
 
107
- // Always print a concise summary of public keys (addresses and BLS pubkeys)
108
- logValidatorSummaries(log, summaries);
187
+ // print a concise summary of public keys (addresses and BLS pubkeys) if no --json options was selected
188
+ if (!json) {
189
+ logValidatorSummaries(log, summaries);
190
+ }
109
191
 
110
- if (!blsOnly && mnemonic && remoteSigner) {
192
+ if (mnemonic && remoteSigner && !json) {
111
193
  for (let i = 0; i < validatorCount; i++) {
112
194
  const addrIdx = addressIndex + i;
113
195
  const acct = mnemonicToAccount(mnemonic, {
@@ -117,4 +199,10 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
117
199
  log(`attester address: ${acct.address} remoteSignerUrl: ${remoteSigner}`);
118
200
  }
119
201
  }
202
+
203
+ // Log staker outputs if not in JSON mode
204
+ if (!json && stakerOutput && allStakerOutputs.length > 0) {
205
+ log('\nStaker outputs:');
206
+ log(prettyPrintJSON(allStakerOutputs));
207
+ }
120
208
  }
@@ -23,7 +23,6 @@ export type BuildValidatorsInput = {
23
23
  mnemonic: string;
24
24
  ikm?: string;
25
25
  blsPath?: string;
26
- blsOnly?: boolean;
27
26
  feeRecipient: AztecAddress;
28
27
  coinbase?: EthAddress;
29
28
  remoteSigner?: string;
@@ -69,7 +68,6 @@ export async function buildValidatorEntries(input: BuildValidatorsInput) {
69
68
  mnemonic,
70
69
  ikm,
71
70
  blsPath,
72
- blsOnly,
73
71
  feeRecipient,
74
72
  coinbase,
75
73
  remoteSigner,
@@ -85,15 +83,9 @@ export async function buildValidatorEntries(input: BuildValidatorsInput) {
85
83
  const basePath = blsPath ?? defaultBlsPath;
86
84
  const perValidatorPath = withValidatorIndex(basePath, addressIndex);
87
85
 
88
- const blsPrivKey = blsOnly || ikm || mnemonic ? deriveBlsPrivateKey(mnemonic, ikm, perValidatorPath) : undefined;
86
+ const blsPrivKey = ikm || mnemonic ? deriveBlsPrivateKey(mnemonic, ikm, perValidatorPath) : undefined;
89
87
  const blsPubCompressed = blsPrivKey ? await computeBlsPublicKeyCompressed(blsPrivKey) : undefined;
90
88
 
91
- if (blsOnly) {
92
- const attester = { bls: blsPrivKey! };
93
- summaries.push({ attesterBls: blsPubCompressed });
94
- return { attester, feeRecipient } as ValidatorKeyStore;
95
- }
96
-
97
89
  const ethAttester = deriveEthAttester(mnemonic, accountIndex, addressIndex, remoteSigner);
98
90
  const attester = blsPrivKey ? { eth: ethAttester, bls: blsPrivKey } : ethAttester;
99
91