@aztec/cli 0.0.1-commit.b655e406 → 0.0.1-commit.b8a057fa

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 (191) hide show
  1. package/README.md +1 -1
  2. package/dest/cmds/aztec_node/block_number.d.ts +1 -1
  3. package/dest/cmds/aztec_node/block_number.js +1 -1
  4. package/dest/cmds/aztec_node/get_block.d.ts +1 -1
  5. package/dest/cmds/aztec_node/get_block.d.ts.map +1 -1
  6. package/dest/cmds/aztec_node/get_block.js +2 -1
  7. package/dest/cmds/aztec_node/get_current_min_fee.d.ts +3 -0
  8. package/dest/cmds/aztec_node/get_current_min_fee.d.ts.map +1 -0
  9. package/dest/cmds/aztec_node/{get_current_base_fee.js → get_current_min_fee.js} +2 -2
  10. package/dest/cmds/aztec_node/get_l1_to_l2_message_witness.d.ts +1 -1
  11. package/dest/cmds/aztec_node/get_logs.d.ts +30 -3
  12. package/dest/cmds/aztec_node/get_logs.d.ts.map +1 -1
  13. package/dest/cmds/aztec_node/get_logs.js +39 -29
  14. package/dest/cmds/aztec_node/get_node_info.d.ts +1 -1
  15. package/dest/cmds/aztec_node/get_node_info.d.ts.map +1 -1
  16. package/dest/cmds/aztec_node/get_node_info.js +0 -2
  17. package/dest/cmds/aztec_node/index.d.ts +1 -1
  18. package/dest/cmds/aztec_node/index.d.ts.map +1 -1
  19. package/dest/cmds/aztec_node/index.js +16 -6
  20. package/dest/cmds/contracts/index.d.ts +1 -1
  21. package/dest/cmds/contracts/inspect_contract.d.ts +1 -1
  22. package/dest/cmds/contracts/inspect_contract.d.ts.map +1 -1
  23. package/dest/cmds/contracts/inspect_contract.js +4 -10
  24. package/dest/cmds/contracts/parse_parameter_struct.d.ts +1 -1
  25. package/dest/cmds/infrastructure/index.d.ts +1 -1
  26. package/dest/cmds/infrastructure/index.d.ts.map +1 -1
  27. package/dest/cmds/infrastructure/index.js +0 -1
  28. package/dest/cmds/infrastructure/sequencers.d.ts +1 -2
  29. package/dest/cmds/infrastructure/sequencers.d.ts.map +1 -1
  30. package/dest/cmds/infrastructure/sequencers.js +11 -4
  31. package/dest/cmds/infrastructure/setup_l2_contract.d.ts +1 -1
  32. package/dest/cmds/infrastructure/setup_l2_contract.d.ts.map +1 -1
  33. package/dest/cmds/infrastructure/setup_l2_contract.js +12 -7
  34. package/dest/cmds/l1/advance_epoch.d.ts +1 -1
  35. package/dest/cmds/l1/assume_proven_through.d.ts +3 -2
  36. package/dest/cmds/l1/assume_proven_through.d.ts.map +1 -1
  37. package/dest/cmds/l1/assume_proven_through.js +3 -4
  38. package/dest/cmds/l1/bridge_erc20.d.ts +1 -1
  39. package/dest/cmds/l1/bridge_erc20.d.ts.map +1 -1
  40. package/dest/cmds/l1/bridge_erc20.js +2 -1
  41. package/dest/cmds/l1/compute_genesis_values.d.ts +4 -0
  42. package/dest/cmds/l1/compute_genesis_values.d.ts.map +1 -0
  43. package/dest/cmds/l1/compute_genesis_values.js +17 -0
  44. package/dest/cmds/l1/create_l1_account.d.ts +1 -1
  45. package/dest/cmds/l1/deploy_l1_contracts_cmd.d.ts +4 -0
  46. package/dest/cmds/l1/deploy_l1_contracts_cmd.d.ts.map +1 -0
  47. package/dest/cmds/l1/{deploy_l1_contracts.js → deploy_l1_contracts_cmd.js} +39 -5
  48. package/dest/cmds/l1/deploy_new_rollup.d.ts +4 -4
  49. package/dest/cmds/l1/deploy_new_rollup.d.ts.map +1 -1
  50. package/dest/cmds/l1/deploy_new_rollup.js +4 -6
  51. package/dest/cmds/l1/get_l1_addresses.d.ts +1 -1
  52. package/dest/cmds/l1/get_l1_addresses.d.ts.map +1 -1
  53. package/dest/cmds/l1/get_l1_addresses.js +5 -2
  54. package/dest/cmds/l1/get_l1_balance.d.ts +1 -1
  55. package/dest/cmds/l1/get_l1_balance.js +4 -2
  56. package/dest/cmds/l1/governance_utils.d.ts +5 -5
  57. package/dest/cmds/l1/governance_utils.d.ts.map +1 -1
  58. package/dest/cmds/l1/governance_utils.js +3 -1
  59. package/dest/cmds/l1/index.d.ts +1 -2
  60. package/dest/cmds/l1/index.d.ts.map +1 -1
  61. package/dest/cmds/l1/index.js +17 -10
  62. package/dest/cmds/l1/prover_stats.d.ts +1 -1
  63. package/dest/cmds/l1/prover_stats.d.ts.map +1 -1
  64. package/dest/cmds/l1/prover_stats.js +32 -30
  65. package/dest/cmds/l1/trigger_seed_snapshot.d.ts +2 -2
  66. package/dest/cmds/l1/trigger_seed_snapshot.d.ts.map +1 -1
  67. package/dest/cmds/l1/trigger_seed_snapshot.js +2 -1
  68. package/dest/cmds/l1/update_l1_validators.d.ts +7 -7
  69. package/dest/cmds/l1/update_l1_validators.d.ts.map +1 -1
  70. package/dest/cmds/l1/update_l1_validators.js +58 -20
  71. package/dest/cmds/misc/compute_selector.d.ts +1 -1
  72. package/dest/cmds/misc/example_contracts.d.ts +1 -1
  73. package/dest/cmds/misc/generate_bootnode_enr.d.ts +1 -1
  74. package/dest/cmds/misc/generate_p2p_private_key.d.ts +1 -1
  75. package/dest/cmds/misc/generate_secret_and_hash.d.ts +1 -1
  76. package/dest/cmds/misc/generate_secret_and_hash.js +1 -1
  77. package/dest/cmds/misc/generate_secret_key.d.ts +1 -1
  78. package/dest/cmds/misc/get_canonical_sponsored_fpc_address.d.ts +1 -1
  79. package/dest/cmds/misc/index.d.ts +1 -1
  80. package/dest/cmds/misc/index.d.ts.map +1 -1
  81. package/dest/cmds/misc/index.js +1 -1
  82. package/dest/cmds/misc/update/common.d.ts +1 -1
  83. package/dest/cmds/misc/update/github.d.ts +1 -1
  84. package/dest/cmds/misc/update/noir.d.ts +1 -1
  85. package/dest/cmds/misc/update/npm.d.ts +1 -1
  86. package/dest/cmds/misc/update/npm.js +1 -1
  87. package/dest/cmds/misc/update/utils.d.ts +1 -1
  88. package/dest/cmds/misc/update.d.ts +1 -1
  89. package/dest/cmds/validator_keys/add.d.ts +1 -1
  90. package/dest/cmds/validator_keys/add.d.ts.map +1 -1
  91. package/dest/cmds/validator_keys/add.js +20 -7
  92. package/dest/cmds/validator_keys/eth_json_v3_worker.d.ts +2 -0
  93. package/dest/cmds/validator_keys/eth_json_v3_worker.d.ts.map +1 -0
  94. package/dest/cmds/validator_keys/eth_json_v3_worker.js +27 -0
  95. package/dest/cmds/validator_keys/generate_bls_keypair.d.ts +1 -1
  96. package/dest/cmds/validator_keys/generate_bls_keypair.d.ts.map +1 -1
  97. package/dest/cmds/validator_keys/generate_bls_keypair.js +3 -2
  98. package/dest/cmds/validator_keys/index.d.ts +1 -1
  99. package/dest/cmds/validator_keys/index.d.ts.map +1 -1
  100. package/dest/cmds/validator_keys/index.js +17 -4
  101. package/dest/cmds/validator_keys/new.d.ts +12 -4
  102. package/dest/cmds/validator_keys/new.d.ts.map +1 -1
  103. package/dest/cmds/validator_keys/new.js +163 -19
  104. package/dest/cmds/validator_keys/shared.d.ts +4 -4
  105. package/dest/cmds/validator_keys/shared.d.ts.map +1 -1
  106. package/dest/cmds/validator_keys/shared.js +97 -51
  107. package/dest/cmds/validator_keys/staker.d.ts +38 -0
  108. package/dest/cmds/validator_keys/staker.d.ts.map +1 -0
  109. package/dest/cmds/validator_keys/staker.js +210 -0
  110. package/dest/cmds/validator_keys/utils.d.ts +25 -0
  111. package/dest/cmds/validator_keys/utils.d.ts.map +1 -0
  112. package/dest/cmds/validator_keys/utils.js +52 -0
  113. package/dest/config/cached_fetch.d.ts +19 -10
  114. package/dest/config/cached_fetch.d.ts.map +1 -1
  115. package/dest/config/cached_fetch.js +110 -32
  116. package/dest/config/chain_l2_config.d.ts +18 -38
  117. package/dest/config/chain_l2_config.d.ts.map +1 -1
  118. package/dest/config/chain_l2_config.js +48 -463
  119. package/dest/config/enrich_env.d.ts +1 -1
  120. package/dest/config/generated/networks.d.ts +239 -0
  121. package/dest/config/generated/networks.d.ts.map +1 -0
  122. package/dest/config/generated/networks.js +240 -0
  123. package/dest/config/get_l1_config.d.ts +3 -2
  124. package/dest/config/get_l1_config.d.ts.map +1 -1
  125. package/dest/config/get_l1_config.js +3 -1
  126. package/dest/config/index.d.ts +1 -1
  127. package/dest/config/network_config.d.ts +7 -4
  128. package/dest/config/network_config.d.ts.map +1 -1
  129. package/dest/config/network_config.js +70 -30
  130. package/dest/utils/aztec.d.ts +7 -14
  131. package/dest/utils/aztec.d.ts.map +1 -1
  132. package/dest/utils/aztec.js +19 -41
  133. package/dest/utils/commands.d.ts +20 -11
  134. package/dest/utils/commands.d.ts.map +1 -1
  135. package/dest/utils/commands.js +26 -15
  136. package/dest/utils/encoding.d.ts +1 -1
  137. package/dest/utils/encoding.js +1 -1
  138. package/dest/utils/github.d.ts +1 -1
  139. package/dest/utils/index.d.ts +1 -1
  140. package/dest/utils/inspect.d.ts +3 -2
  141. package/dest/utils/inspect.d.ts.map +1 -1
  142. package/dest/utils/inspect.js +15 -12
  143. package/dest/utils/setup_contracts.d.ts +1 -1
  144. package/package.json +44 -31
  145. package/public_include_metric_prefixes.json +1 -0
  146. package/src/cmds/aztec_node/block_number.ts +1 -1
  147. package/src/cmds/aztec_node/get_block.ts +2 -1
  148. package/src/cmds/aztec_node/{get_current_base_fee.ts → get_current_min_fee.ts} +2 -2
  149. package/src/cmds/aztec_node/get_logs.ts +70 -37
  150. package/src/cmds/aztec_node/get_node_info.ts +0 -2
  151. package/src/cmds/aztec_node/index.ts +16 -11
  152. package/src/cmds/contracts/inspect_contract.ts +4 -11
  153. package/src/cmds/infrastructure/index.ts +0 -1
  154. package/src/cmds/infrastructure/sequencers.ts +7 -5
  155. package/src/cmds/infrastructure/setup_l2_contract.ts +12 -7
  156. package/src/cmds/l1/assume_proven_through.ts +4 -4
  157. package/src/cmds/l1/bridge_erc20.ts +2 -1
  158. package/src/cmds/l1/compute_genesis_values.ts +29 -0
  159. package/src/cmds/l1/{deploy_l1_contracts.ts → deploy_l1_contracts_cmd.ts} +39 -23
  160. package/src/cmds/l1/deploy_new_rollup.ts +5 -12
  161. package/src/cmds/l1/get_l1_addresses.ts +4 -2
  162. package/src/cmds/l1/get_l1_balance.ts +2 -2
  163. package/src/cmds/l1/governance_utils.ts +3 -8
  164. package/src/cmds/l1/index.ts +30 -21
  165. package/src/cmds/l1/prover_stats.ts +42 -31
  166. package/src/cmds/l1/trigger_seed_snapshot.ts +2 -1
  167. package/src/cmds/l1/update_l1_validators.ts +52 -32
  168. package/src/cmds/misc/generate_secret_and_hash.ts +1 -1
  169. package/src/cmds/misc/index.ts +1 -1
  170. package/src/cmds/misc/update/npm.ts +1 -1
  171. package/src/cmds/validator_keys/add.ts +20 -10
  172. package/src/cmds/validator_keys/eth_json_v3_worker.ts +30 -0
  173. package/src/cmds/validator_keys/generate_bls_keypair.ts +3 -2
  174. package/src/cmds/validator_keys/index.ts +85 -25
  175. package/src/cmds/validator_keys/new.ts +211 -25
  176. package/src/cmds/validator_keys/shared.ts +117 -60
  177. package/src/cmds/validator_keys/staker.ts +301 -0
  178. package/src/cmds/validator_keys/utils.ts +81 -0
  179. package/src/config/cached_fetch.ts +119 -31
  180. package/src/config/chain_l2_config.ts +47 -602
  181. package/src/config/generated/networks.ts +244 -0
  182. package/src/config/get_l1_config.ts +4 -1
  183. package/src/config/network_config.ts +80 -31
  184. package/src/utils/aztec.ts +30 -95
  185. package/src/utils/commands.ts +29 -15
  186. package/src/utils/encoding.ts +1 -1
  187. package/src/utils/inspect.ts +13 -10
  188. package/dest/cmds/aztec_node/get_current_base_fee.d.ts +0 -3
  189. package/dest/cmds/aztec_node/get_current_base_fee.d.ts.map +0 -1
  190. package/dest/cmds/l1/deploy_l1_contracts.d.ts +0 -4
  191. package/dest/cmds/l1/deploy_l1_contracts.d.ts.map +0 -1
@@ -1,9 +1,14 @@
1
+ import { prettyPrintJSON } from '@aztec/cli/utils';
2
+ import { createEthereumChain } from '@aztec/ethereum/chain';
3
+ import { GSEContract } from '@aztec/ethereum/contracts';
1
4
  import type { EthAddress } from '@aztec/foundation/eth-address';
2
5
  import type { LogFn } from '@aztec/foundation/log';
3
6
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
4
7
 
5
8
  import { wordlist } from '@scure/bip39/wordlists/english.js';
6
- import { dirname } from 'path';
9
+ import { readFile, writeFile } from 'fs/promises';
10
+ import { basename, dirname, join } from 'path';
11
+ import { createPublicClient, fallback, http } from 'viem';
7
12
  import { generateMnemonic, mnemonicToAccount } from 'viem/accounts';
8
13
 
9
14
  import {
@@ -15,12 +20,20 @@ import {
15
20
  writeEthJsonV3ToFile,
16
21
  writeKeystoreFile,
17
22
  } from './shared.js';
23
+ import { processAttesterAccounts } from './staker.js';
24
+ import {
25
+ validateBlsPathOptions,
26
+ validatePublisherOptions,
27
+ validateRemoteSignerOptions,
28
+ validateStakerOutputOptions,
29
+ } from './utils.js';
18
30
 
19
31
  export type NewValidatorKeystoreOptions = {
20
32
  dataDir?: string;
21
33
  file?: string;
22
34
  count?: number;
23
35
  publisherCount?: number;
36
+ publishers?: string[];
24
37
  mnemonic?: string;
25
38
  passphrase?: string;
26
39
  accountIndex?: number;
@@ -28,68 +41,184 @@ export type NewValidatorKeystoreOptions = {
28
41
  separatePublisher?: boolean;
29
42
  ikm?: string;
30
43
  blsPath?: string;
31
- blsOnly?: boolean;
32
44
  password?: string;
33
- outDir?: string;
45
+ passwordFile?: string;
46
+ ethPassword?: string;
47
+ ethPasswordFile?: string;
48
+ blsPassword?: string;
49
+ blsPasswordFile?: string;
50
+ encryptedKeystoreDir?: string;
34
51
  json?: boolean;
35
52
  feeRecipient: AztecAddress;
36
53
  coinbase?: EthAddress;
37
54
  remoteSigner?: string;
38
- fundingAccount?: EthAddress;
55
+ stakerOutput?: boolean;
56
+ gseAddress?: EthAddress;
57
+ l1RpcUrls?: string[];
58
+ l1ChainId?: number;
39
59
  };
40
60
 
61
+ type PasswordSourceOptions = Pick<
62
+ NewValidatorKeystoreOptions,
63
+ 'password' | 'passwordFile' | 'ethPassword' | 'ethPasswordFile' | 'blsPassword' | 'blsPasswordFile'
64
+ >;
65
+
66
+ type PasswordSource = {
67
+ password?: string;
68
+ passwordFile?: string;
69
+ passwordOption: string;
70
+ passwordFileOption: string;
71
+ };
72
+
73
+ function validatePassword(password: string, source: string): string {
74
+ if (password.length === 0) {
75
+ throw new Error(`${source} cannot be empty`);
76
+ }
77
+ return password;
78
+ }
79
+
80
+ function stripOneTrailingNewline(password: string): string {
81
+ if (password.endsWith('\n')) {
82
+ password = password.slice(0, -1);
83
+ }
84
+ if (password.endsWith('\r')) {
85
+ password = password.slice(0, -1);
86
+ }
87
+ return password;
88
+ }
89
+
90
+ async function resolvePasswordSource(source: PasswordSource): Promise<string | undefined> {
91
+ if (source.password !== undefined && source.passwordFile !== undefined) {
92
+ throw new Error(`${source.passwordOption} and ${source.passwordFileOption} cannot be used together`);
93
+ }
94
+ if (source.password !== undefined) {
95
+ return validatePassword(source.password, source.passwordOption);
96
+ }
97
+ if (source.passwordFile !== undefined) {
98
+ if (source.passwordFile.length === 0) {
99
+ throw new Error(`${source.passwordFileOption} cannot be empty`);
100
+ }
101
+ const password = stripOneTrailingNewline(await readFile(source.passwordFile, 'utf-8'));
102
+ return validatePassword(password, source.passwordFileOption);
103
+ }
104
+
105
+ return undefined;
106
+ }
107
+
108
+ async function resolvePasswords(options: PasswordSourceOptions) {
109
+ const sharedPassword = await resolvePasswordSource({
110
+ password: options.password,
111
+ passwordFile: options.passwordFile,
112
+ passwordOption: '--password',
113
+ passwordFileOption: '--password-file',
114
+ });
115
+ const ethPassword =
116
+ (await resolvePasswordSource({
117
+ password: options.ethPassword,
118
+ passwordFile: options.ethPasswordFile,
119
+ passwordOption: '--eth-password',
120
+ passwordFileOption: '--eth-password-file',
121
+ })) ?? sharedPassword;
122
+ const blsPassword =
123
+ (await resolvePasswordSource({
124
+ password: options.blsPassword,
125
+ passwordFile: options.blsPasswordFile,
126
+ passwordOption: '--bls-password',
127
+ passwordFileOption: '--bls-password-file',
128
+ })) ?? sharedPassword;
129
+
130
+ if (ethPassword === undefined && blsPassword === undefined) {
131
+ return {};
132
+ }
133
+ if (ethPassword === undefined || blsPassword === undefined) {
134
+ throw new Error(
135
+ 'Both ETH and BLS passwords are required when writing encrypted keystores. Provide --password to use one password for both, or provide both --eth-password and --bls-password.',
136
+ );
137
+ }
138
+ return { ethPassword, blsPassword };
139
+ }
140
+
41
141
  export async function newValidatorKeystore(options: NewValidatorKeystoreOptions, log: LogFn) {
142
+ // validate bls-path inputs before proceeding with key generation
143
+ validateBlsPathOptions(options);
144
+ // validate staker output options before proceeding with key generation
145
+ validateStakerOutputOptions(options);
146
+ // validate publisher options
147
+ validatePublisherOptions(options);
148
+ // validate remote signer options
149
+ validateRemoteSignerOptions(options);
150
+
42
151
  const {
43
152
  dataDir,
44
153
  file,
45
154
  count,
46
155
  publisherCount = 0,
156
+ publishers,
47
157
  json,
48
158
  coinbase,
49
159
  accountIndex = 0,
50
160
  addressIndex = 0,
51
161
  feeRecipient,
52
162
  remoteSigner,
53
- fundingAccount,
54
- blsOnly,
55
163
  blsPath,
56
164
  ikm,
57
165
  mnemonic: _mnemonic,
58
- password,
59
- outDir,
166
+ password: passwordOption,
167
+ passwordFile,
168
+ ethPassword: ethPasswordOption,
169
+ ethPasswordFile,
170
+ blsPassword: blsPasswordOption,
171
+ blsPasswordFile,
172
+ encryptedKeystoreDir,
173
+ stakerOutput,
174
+ gseAddress,
175
+ l1RpcUrls,
176
+ l1ChainId,
60
177
  } = options;
61
178
 
62
- if (remoteSigner && !_mnemonic) {
63
- throw new Error(
64
- 'Using --remote-signer requires a deterministic key source. Provide --mnemonic to derive keys, or omit --remote-signer to write new private keys to keystore.',
65
- );
66
- }
67
-
179
+ const { ethPassword, blsPassword } = await resolvePasswords({
180
+ password: passwordOption,
181
+ passwordFile,
182
+ ethPassword: ethPasswordOption,
183
+ ethPasswordFile,
184
+ blsPassword: blsPasswordOption,
185
+ blsPasswordFile,
186
+ });
187
+ const shouldEncryptKeystores = ethPassword !== undefined && blsPassword !== undefined;
68
188
  const mnemonic = _mnemonic ?? generateMnemonic(wordlist);
69
189
 
190
+ if (!_mnemonic && !json && !shouldEncryptKeystores) {
191
+ log('No mnemonic provided, generating new one...');
192
+ log(`Using new mnemonic:`);
193
+ log('');
194
+ log(mnemonic);
195
+ log('');
196
+ }
197
+
70
198
  const validatorCount = typeof count === 'number' && Number.isFinite(count) && count > 0 ? Math.floor(count) : 1;
71
199
  const { outputPath } = await resolveKeystoreOutputPath(dataDir, file);
200
+ const keystoreOutDir = dirname(outputPath);
72
201
 
73
202
  const { validators, summaries } = await buildValidatorEntries({
74
203
  validatorCount,
75
204
  publisherCount,
205
+ publishers,
76
206
  accountIndex,
77
207
  baseAddressIndex: addressIndex,
78
208
  mnemonic,
79
209
  ikm,
80
210
  blsPath,
81
- blsOnly,
82
211
  feeRecipient,
83
212
  coinbase,
84
213
  remoteSigner,
85
- fundingAccount,
86
214
  });
87
215
 
88
216
  // If password provided, write ETH JSON V3 and BLS BN254 keystores and replace plaintext
89
- if (password !== undefined) {
90
- const keystoreOutDir = outDir && outDir.length > 0 ? outDir : dirname(outputPath);
91
- await writeEthJsonV3ToFile(validators, { outDir: keystoreOutDir, password });
92
- await writeBlsBn254ToFile(validators, { outDir: keystoreOutDir, password });
217
+ if (shouldEncryptKeystores) {
218
+ const encryptedKeystoreOutDir =
219
+ encryptedKeystoreDir && encryptedKeystoreDir.length > 0 ? encryptedKeystoreDir : keystoreOutDir;
220
+ await writeEthJsonV3ToFile(validators, { outDir: encryptedKeystoreOutDir, password: ethPassword });
221
+ await writeBlsBn254ToFile(validators, { outDir: encryptedKeystoreOutDir, password: blsPassword });
93
222
  }
94
223
 
95
224
  const keystore = {
@@ -99,15 +228,66 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
99
228
 
100
229
  await writeKeystoreFile(outputPath, keystore);
101
230
 
102
- maybePrintJson(log, json, keystore as unknown as Record<string, any>);
103
- if (!json) {
231
+ // Generate staker outputs if requested
232
+ const allStakerOutputs: any[] = [];
233
+ if (stakerOutput && gseAddress && l1RpcUrls && l1ChainId !== undefined) {
234
+ const chain = createEthereumChain(l1RpcUrls, l1ChainId);
235
+ const publicClient = createPublicClient({
236
+ chain: chain.chainInfo,
237
+ transport: fallback(l1RpcUrls.map(url => http(url, { batch: false }))),
238
+ });
239
+ const gse = new GSEContract(publicClient, gseAddress);
240
+
241
+ // Extract keystore base name without extension for unique staker output filenames
242
+ const keystoreBaseName = basename(outputPath, '.json');
243
+
244
+ // Process each validator
245
+ for (let i = 0; i < validators.length; i++) {
246
+ const validator = validators[i];
247
+ const outputs = await processAttesterAccounts(validator.attester, gse);
248
+
249
+ // Collect all staker outputs
250
+ for (let j = 0; j < outputs.length; j++) {
251
+ allStakerOutputs.push(outputs[j]);
252
+ }
253
+ }
254
+
255
+ // Write a single JSON file with all staker outputs
256
+ if (allStakerOutputs.length > 0) {
257
+ const stakerOutputPath = join(keystoreOutDir, `${keystoreBaseName}_staker_output.json`);
258
+ await writeFile(stakerOutputPath, prettyPrintJSON(allStakerOutputs), 'utf-8');
259
+ }
260
+ }
261
+
262
+ const outputData = !_mnemonic && !shouldEncryptKeystores ? { ...keystore, generatedMnemonic: mnemonic } : keystore;
263
+
264
+ // Handle JSON output
265
+ if (json) {
266
+ if (stakerOutput && allStakerOutputs.length > 0) {
267
+ const combinedOutput = {
268
+ keystore: outputData,
269
+ staker: allStakerOutputs,
270
+ };
271
+ maybePrintJson(log, json, combinedOutput as unknown as Record<string, any>);
272
+ } else {
273
+ maybePrintJson(log, json, outputData as unknown as Record<string, any>);
274
+ }
275
+ } else {
104
276
  log(`Wrote validator keystore to ${outputPath}`);
277
+ if (stakerOutput && allStakerOutputs.length > 0) {
278
+ const keystoreBaseName = basename(outputPath, '.json');
279
+ const stakerOutputPath = join(keystoreOutDir, `${keystoreBaseName}_staker_output.json`);
280
+ log(`Wrote staker output for ${allStakerOutputs.length} validator(s) to ${stakerOutputPath}`);
281
+ log('');
282
+ }
105
283
  }
106
284
 
107
- // Always print a concise summary of public keys (addresses and BLS pubkeys)
108
- logValidatorSummaries(log, summaries);
285
+ // print a concise summary of public keys (addresses and BLS pubkeys) if no --json options was selected
286
+ if (!json) {
287
+ logValidatorSummaries(log, summaries);
288
+ }
109
289
 
110
- if (!blsOnly && mnemonic && remoteSigner) {
290
+ if (mnemonic && remoteSigner && !json) {
111
291
  for (let i = 0; i < validatorCount; i++) {
112
292
  const addrIdx = addressIndex + i;
113
293
  const acct = mnemonicToAccount(mnemonic, {
@@ -117,4 +297,10 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
117
297
  log(`attester address: ${acct.address} remoteSignerUrl: ${remoteSigner}`);
118
298
  }
119
299
  }
300
+
301
+ // Log staker outputs if not in JSON mode
302
+ if (!json && stakerOutput && allStakerOutputs.length > 0) {
303
+ log('\nStaker outputs:');
304
+ log(prettyPrintJSON(allStakerOutputs));
305
+ }
120
306
  }
@@ -1,6 +1,8 @@
1
1
  import { prettyPrintJSON } from '@aztec/cli/utils';
2
- import { computeBn254G1PublicKeyCompressed, deriveBlsPrivateKey } from '@aztec/foundation/crypto';
2
+ import { asyncPool } from '@aztec/foundation/async-pool';
3
+ import { deriveBlsPrivateKey } from '@aztec/foundation/crypto/bls';
3
4
  import { createBn254Keystore } from '@aztec/foundation/crypto/bls/bn254_keystore';
5
+ import { computeBn254G1PublicKeyCompressed } from '@aztec/foundation/crypto/bn254';
4
6
  import type { EthAddress } from '@aztec/foundation/eth-address';
5
7
  import type { LogFn } from '@aztec/foundation/log';
6
8
  import type { EthAccount, EthPrivateKey, ValidatorKeyStore } from '@aztec/node-keystore/types';
@@ -9,31 +11,94 @@ import type { AztecAddress } from '@aztec/stdlib/aztec-address';
9
11
  import { Wallet } from '@ethersproject/wallet';
10
12
  import { constants as fsConstants, mkdirSync } from 'fs';
11
13
  import { access, writeFile } from 'fs/promises';
12
- import { homedir } from 'os';
14
+ import { availableParallelism, homedir } from 'os';
13
15
  import { dirname, isAbsolute, join } from 'path';
14
16
  import { mnemonicToAccount } from 'viem/accounts';
17
+ import { Worker } from 'worker_threads';
18
+
19
+ import { defaultBlsPath } from './utils.js';
20
+
21
+ type EthJsonV3WorkerResult = { json: string } | { error: { message: string; name?: string; stack?: string } };
22
+
23
+ // ethers' default scrypt parameters use substantial memory, so keep worker fan-out bounded.
24
+ const maxEthKeystoreWorkers = Math.max(1, Math.min(4, availableParallelism()));
25
+
26
+ function deserializeWorkerError(error: { message: string; name?: string; stack?: string }) {
27
+ const result = new Error(error.message);
28
+ result.name = error.name ?? result.name;
29
+ result.stack = error.stack;
30
+ return result;
31
+ }
32
+
33
+ function encryptEthJsonV3InWorker(privateKeyHex: string, password: string): Promise<string> {
34
+ return new Promise<string>((resolve, reject) => {
35
+ const worker = new Worker(new URL('./eth_json_v3_worker.js', import.meta.url), {
36
+ workerData: { privateKeyHex, password },
37
+ });
38
+ let settled = false;
39
+
40
+ worker.once('message', (result: EthJsonV3WorkerResult) => {
41
+ settled = true;
42
+
43
+ if ('json' in result) {
44
+ resolve(result.json);
45
+ } else {
46
+ reject(deserializeWorkerError(result.error));
47
+ }
48
+ });
49
+ worker.once('error', error => {
50
+ settled = true;
51
+ reject(error);
52
+ });
53
+ worker.once('exit', code => {
54
+ if (!settled) {
55
+ reject(new Error(`ETH JSON V3 worker stopped with exit code ${code}`));
56
+ }
57
+ });
58
+ });
59
+ }
60
+
61
+ async function encryptEthJsonV3(privateKeyHex: string, password: string): Promise<string> {
62
+ if (process.env.JEST_WORKER_ID !== undefined) {
63
+ return await new Wallet(privateKeyHex).encrypt(password);
64
+ }
65
+ return await encryptEthJsonV3InWorker(privateKeyHex, password);
66
+ }
15
67
 
16
68
  export type ValidatorSummary = { attesterEth?: string; attesterBls?: string; publisherEth?: string[] };
17
69
 
18
70
  export type BuildValidatorsInput = {
19
71
  validatorCount: number;
20
72
  publisherCount?: number;
73
+ publishers?: string[];
21
74
  accountIndex: number;
22
75
  baseAddressIndex: number;
23
76
  mnemonic: string;
24
77
  ikm?: string;
25
78
  blsPath?: string;
26
- blsOnly?: boolean;
27
79
  feeRecipient: AztecAddress;
28
80
  coinbase?: EthAddress;
29
81
  remoteSigner?: string;
30
- fundingAccount?: EthAddress;
31
82
  };
32
83
 
33
- export function withValidatorIndex(path: string, index: number) {
84
+ export function withValidatorIndex(path: string, accountIndex: number = 0, addressIndex: number = 0) {
85
+ // NOTE: The legacy BLS CLI is to allow users who generated keys in 2.1.4 to be able to use the same command
86
+ // to re-generate their keys. In 2.1.5 we switched how we append addresses to the path so this is to maintain backwards compatibility.
87
+ const useLegacyBlsCli = ['true', '1', 'yes', 'y'].includes(process.env.LEGACY_BLS_CLI ?? '');
88
+
89
+ const defaultBlsPathParts = defaultBlsPath.split('/');
90
+
34
91
  const parts = path.split('/');
35
- if (parts.length >= 4 && parts[0] === 'm' && parts[1] === '12381' && parts[2] === '3600') {
36
- parts[3] = String(index);
92
+ if (parts.length == defaultBlsPathParts.length && parts.every((part, index) => part === defaultBlsPathParts[index])) {
93
+ if (useLegacyBlsCli) {
94
+ // In 2.1.4, we were using address-index in parts[3] and did NOT use account-index, check lines 32 & 84
95
+ // https://github.com/AztecProtocol/aztec-packages/blob/v2.1.4/yarn-project/cli/src/cmds/validator_keys/shared.ts
96
+
97
+ parts[3] = String(addressIndex);
98
+ } else {
99
+ parts[3] = String(accountIndex);
100
+ parts[5] = String(addressIndex);
101
+ }
37
102
  return parts.join('/');
38
103
  }
39
104
  return path;
@@ -64,42 +129,37 @@ export async function buildValidatorEntries(input: BuildValidatorsInput) {
64
129
  const {
65
130
  validatorCount,
66
131
  publisherCount = 0,
132
+ publishers,
67
133
  accountIndex,
68
134
  baseAddressIndex,
69
135
  mnemonic,
70
136
  ikm,
71
137
  blsPath,
72
- blsOnly,
73
138
  feeRecipient,
74
139
  coinbase,
75
140
  remoteSigner,
76
- fundingAccount,
77
141
  } = input;
78
142
 
79
- const defaultBlsPath = 'm/12381/3600/0/0/0';
80
143
  const summaries: ValidatorSummary[] = [];
81
144
 
82
145
  const validators = await Promise.all(
83
146
  Array.from({ length: validatorCount }, async (_unused, i) => {
84
147
  const addressIndex = baseAddressIndex + i;
85
148
  const basePath = blsPath ?? defaultBlsPath;
86
- const perValidatorPath = withValidatorIndex(basePath, addressIndex);
149
+ const perValidatorPath = withValidatorIndex(basePath, accountIndex, addressIndex);
87
150
 
88
- const blsPrivKey = blsOnly || ikm || mnemonic ? deriveBlsPrivateKey(mnemonic, ikm, perValidatorPath) : undefined;
151
+ const blsPrivKey = ikm || mnemonic ? deriveBlsPrivateKey(mnemonic, ikm, perValidatorPath) : undefined;
89
152
  const blsPubCompressed = blsPrivKey ? await computeBlsPublicKeyCompressed(blsPrivKey) : undefined;
90
153
 
91
- if (blsOnly) {
92
- const attester = { bls: blsPrivKey! };
93
- summaries.push({ attesterBls: blsPubCompressed });
94
- return { attester, feeRecipient } as ValidatorKeyStore;
95
- }
96
-
97
154
  const ethAttester = deriveEthAttester(mnemonic, accountIndex, addressIndex, remoteSigner);
98
155
  const attester = blsPrivKey ? { eth: ethAttester, bls: blsPrivKey } : ethAttester;
99
156
 
100
157
  let publisherField: EthAccount | EthPrivateKey | (EthAccount | EthPrivateKey)[] | undefined;
101
158
  const publisherAddresses: string[] = [];
102
- if (publisherCount > 0) {
159
+ if (publishers && publishers.length > 0) {
160
+ publisherAddresses.push(...publishers);
161
+ publisherField = publishers.length === 1 ? (publishers[0] as EthPrivateKey) : (publishers as EthPrivateKey[]);
162
+ } else if (publisherCount > 0) {
103
163
  const publishersBaseIndex = baseAddressIndex + validatorCount + i * publisherCount;
104
164
  const publisherAccounts = Array.from({ length: publisherCount }, (_unused2, j) => {
105
165
  const publisherAddressIndex = publishersBaseIndex + j;
@@ -130,8 +190,7 @@ export async function buildValidatorEntries(input: BuildValidatorsInput) {
130
190
  attester,
131
191
  ...(publisherField !== undefined ? { publisher: publisherField } : {}),
132
192
  feeRecipient,
133
- coinbase,
134
- fundingAccount,
193
+ coinbase: coinbase ?? attesterEthAddress,
135
194
  } as ValidatorKeyStore;
136
195
  }),
137
196
  );
@@ -218,7 +277,7 @@ export async function writeBn254BlsKeystore(
218
277
  ): Promise<string> {
219
278
  mkdirSync(outDir, { recursive: true });
220
279
 
221
- const keystore = createBn254Keystore(password, privateKeyHex, pubkeyHex, derivationPath);
280
+ const keystore = await createBn254Keystore(password, privateKeyHex, pubkeyHex, derivationPath);
222
281
 
223
282
  const safeBase = fileNameBase.replace(/[^a-zA-Z0-9_-]/g, '_');
224
283
  const outPath = join(outDir, `keystore-${safeBase}.json`);
@@ -229,30 +288,31 @@ export async function writeBn254BlsKeystore(
229
288
  /** Replace plaintext BLS keys in validators with { path, password } pointing to BN254 keystore files. */
230
289
  export async function writeBlsBn254ToFile(
231
290
  validators: ValidatorKeyStore[],
232
- options: { outDir: string; password: string },
291
+ options: { outDir: string; password: string; blsPath?: string },
233
292
  ): Promise<void> {
234
- for (let i = 0; i < validators.length; i++) {
235
- const v = validators[i];
236
- if (!v || typeof v !== 'object' || !('attester' in v)) {
237
- continue;
238
- }
239
- const att = (v as any).attester;
293
+ await Promise.all(
294
+ validators.map(async (v, i) => {
295
+ if (!v || typeof v !== 'object' || !('attester' in v)) {
296
+ return;
297
+ }
298
+ const att = (v as any).attester;
240
299
 
241
- // Shapes: { bls: <hex> } or { eth: <ethAccount>, bls?: <hex> } or plain EthAccount
242
- const blsKey: string | undefined = typeof att === 'object' && 'bls' in att ? (att as any).bls : undefined;
243
- if (!blsKey || typeof blsKey !== 'string') {
244
- continue;
245
- }
300
+ // Shapes: { bls: <hex> } or { eth: <ethAccount>, bls?: <hex> } or plain EthAccount
301
+ const blsKey: string | undefined = typeof att === 'object' && 'bls' in att ? (att as any).bls : undefined;
302
+ if (!blsKey || typeof blsKey !== 'string') {
303
+ return;
304
+ }
246
305
 
247
- const pub = await computeBlsPublicKeyCompressed(blsKey);
248
- const path = 'm/12381/3600/0/0/0';
249
- const fileBase = `${String(i + 1)}_${pub.slice(2, 18)}`;
250
- const keystorePath = await writeBn254BlsKeystore(options.outDir, fileBase, options.password, blsKey, pub, path);
306
+ const pub = await computeBlsPublicKeyCompressed(blsKey);
307
+ const path = options.blsPath ?? defaultBlsPath;
308
+ const fileBase = `${String(i + 1)}_${pub.slice(2, 18)}`;
309
+ const keystorePath = await writeBn254BlsKeystore(options.outDir, fileBase, options.password, blsKey, pub, path);
251
310
 
252
- if (typeof att === 'object') {
253
- (att as any).bls = { path: keystorePath, password: options.password };
254
- }
255
- }
311
+ if (typeof att === 'object') {
312
+ (att as any).bls = { path: keystorePath, password: options.password };
313
+ }
314
+ }),
315
+ );
256
316
  }
257
317
 
258
318
  /** Writes an Ethereum JSON V3 keystore using ethers, returns absolute path */
@@ -264,8 +324,7 @@ export async function writeEthJsonV3Keystore(
264
324
  ): Promise<string> {
265
325
  const safeBase = fileNameBase.replace(/[^a-zA-Z0-9_-]/g, '_');
266
326
  mkdirSync(outDir, { recursive: true });
267
- const wallet = new Wallet(privateKeyHex);
268
- const json = await wallet.encrypt(password);
327
+ const json = await encryptEthJsonV3(privateKeyHex, password);
269
328
  const outPath = join(outDir, `keystore-eth-${safeBase}.json`);
270
329
  await writeFile(outPath, json, { encoding: 'utf-8' });
271
330
  return outPath;
@@ -276,13 +335,16 @@ export async function writeEthJsonV3ToFile(
276
335
  validators: ValidatorKeyStore[],
277
336
  options: { outDir: string; password: string },
278
337
  ): Promise<void> {
279
- const maybeEncryptEth = async (account: any, label: string) => {
338
+ const tasks: (() => Promise<void>)[] = [];
339
+
340
+ const maybeQueueEncryptEth = (account: any, label: string, setEncryptedAccount: (account: any) => void) => {
280
341
  if (typeof account === 'string' && account.startsWith('0x') && account.length === 66) {
281
- const fileBase = `${label}_${account.slice(2, 10)}`;
282
- const p = await writeEthJsonV3Keystore(options.outDir, fileBase, options.password, account);
283
- return { path: p, password: options.password };
342
+ tasks.push(async () => {
343
+ const fileBase = `${label}_${account.slice(2, 10)}`;
344
+ const path = await writeEthJsonV3Keystore(options.outDir, fileBase, options.password, account);
345
+ setEncryptedAccount({ path, password: options.password });
346
+ });
284
347
  }
285
- return account;
286
348
  };
287
349
 
288
350
  for (let i = 0; i < validators.length; i++) {
@@ -294,28 +356,23 @@ export async function writeEthJsonV3ToFile(
294
356
  // attester may be string (eth), object with eth, or remote signer
295
357
  const att = (v as any).attester;
296
358
  if (typeof att === 'string') {
297
- (v as any).attester = await maybeEncryptEth(att, `attester_${i + 1}`);
359
+ maybeQueueEncryptEth(att, `attester_${i + 1}`, account => ((v as any).attester = account));
298
360
  } else if (att && typeof att === 'object' && 'eth' in att) {
299
- (att as any).eth = await maybeEncryptEth((att as any).eth, `attester_${i + 1}`);
361
+ maybeQueueEncryptEth((att as any).eth, `attester_${i + 1}`, account => ((att as any).eth = account));
300
362
  }
301
363
 
302
364
  // publisher can be single or array
303
365
  if ('publisher' in v) {
304
366
  const pub = (v as any).publisher;
305
367
  if (Array.isArray(pub)) {
306
- const out: any[] = [];
307
368
  for (let j = 0; j < pub.length; j++) {
308
- out.push(await maybeEncryptEth(pub[j], `publisher_${i + 1}_${j + 1}`));
369
+ maybeQueueEncryptEth(pub[j], `publisher_${i + 1}_${j + 1}`, account => (pub[j] = account));
309
370
  }
310
- (v as any).publisher = out;
311
371
  } else if (pub !== undefined) {
312
- (v as any).publisher = await maybeEncryptEth(pub, `publisher_${i + 1}`);
372
+ maybeQueueEncryptEth(pub, `publisher_${i + 1}`, account => ((v as any).publisher = account));
313
373
  }
314
374
  }
315
-
316
- // Optional fundingAccount within validator
317
- if ('fundingAccount' in v) {
318
- (v as any).fundingAccount = await maybeEncryptEth((v as any).fundingAccount, `funding_${i + 1}`);
319
- }
320
375
  }
376
+
377
+ await asyncPool(maxEthKeystoreWorkers, tasks, task => task());
321
378
  }