@aztec-labs/cli 6.0.0-rc.3 → 6.0.0
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.
- package/dest/cmds/infrastructure/index.d.ts +1 -1
- package/dest/cmds/infrastructure/index.d.ts.map +1 -1
- package/dest/cmds/infrastructure/index.js +3 -2
- package/dest/cmds/infrastructure/sequencers.d.ts +4 -1
- package/dest/cmds/infrastructure/sequencers.d.ts.map +1 -1
- package/dest/cmds/infrastructure/sequencers.js +10 -3
- package/dest/cmds/l1/deploy_l1_contracts_cmd.d.ts +1 -1
- package/dest/cmds/l1/deploy_l1_contracts_cmd.d.ts.map +1 -1
- package/dest/cmds/l1/deploy_l1_contracts_cmd.js +1 -0
- package/dest/cmds/l1/index.d.ts +1 -1
- package/dest/cmds/l1/index.d.ts.map +1 -1
- package/dest/cmds/l1/index.js +2 -1
- package/dest/cmds/l1/l1_rpc_test_utils.d.ts +10 -2
- package/dest/cmds/l1/l1_rpc_test_utils.d.ts.map +1 -1
- package/dest/cmds/l1/l1_rpc_test_utils.js +35 -1
- package/dest/cmds/l1/prover_stats.d.ts +1 -1
- package/dest/cmds/l1/prover_stats.d.ts.map +1 -1
- package/dest/cmds/l1/prover_stats.js +3 -4
- package/dest/cmds/l1/update_l1_validators.d.ts +3 -2
- package/dest/cmds/l1/update_l1_validators.d.ts.map +1 -1
- package/dest/cmds/l1/update_l1_validators.js +6 -4
- package/dest/cmds/misc/generate_p2p_private_key.d.ts +1 -1
- package/dest/cmds/misc/generate_p2p_private_key.d.ts.map +1 -1
- package/dest/cmds/misc/generate_p2p_private_key.js +1 -0
- package/dest/cmds/validator_keys/add.d.ts +1 -1
- package/dest/cmds/validator_keys/add.d.ts.map +1 -1
- package/dest/cmds/validator_keys/add.js +14 -4
- package/dest/cmds/validator_keys/bls_key_derivation.d.ts +89 -0
- package/dest/cmds/validator_keys/bls_key_derivation.d.ts.map +1 -0
- package/dest/cmds/validator_keys/bls_key_derivation.js +122 -0
- package/dest/cmds/validator_keys/generate_bls_keypair.d.ts +3 -2
- package/dest/cmds/validator_keys/generate_bls_keypair.d.ts.map +1 -1
- package/dest/cmds/validator_keys/generate_bls_keypair.js +16 -3
- package/dest/cmds/validator_keys/index.d.ts +1 -1
- package/dest/cmds/validator_keys/index.d.ts.map +1 -1
- package/dest/cmds/validator_keys/index.js +14 -3
- package/dest/cmds/validator_keys/new.d.ts +3 -2
- package/dest/cmds/validator_keys/new.d.ts.map +1 -1
- package/dest/cmds/validator_keys/new.js +47 -33
- package/dest/cmds/validator_keys/shared.d.ts +28 -3
- package/dest/cmds/validator_keys/shared.d.ts.map +1 -1
- package/dest/cmds/validator_keys/shared.js +48 -7
- package/dest/cmds/validator_keys/staker.d.ts +6 -1
- package/dest/cmds/validator_keys/staker.d.ts.map +1 -1
- package/dest/cmds/validator_keys/staker.js +32 -0
- package/dest/config/chain_l2_config.d.ts +1 -1
- package/dest/config/chain_l2_config.d.ts.map +1 -1
- package/dest/config/chain_l2_config.js +1 -1
- package/dest/config/enrich_env.d.ts +1 -1
- package/dest/config/enrich_env.d.ts.map +1 -1
- package/dest/config/enrich_env.js +1 -1
- package/dest/config/get_l1_config.d.ts +1 -1
- package/dest/config/get_l1_config.d.ts.map +1 -1
- package/dest/config/get_l1_config.js +2 -1
- package/dest/config/network_config.d.ts +1 -1
- package/dest/config/network_config.d.ts.map +1 -1
- package/dest/config/network_config.js +4 -2
- package/dest/utils/aztec.d.ts +1 -1
- package/dest/utils/aztec.d.ts.map +1 -1
- package/dest/utils/aztec.js +1 -0
- package/dest/utils/commands.d.ts +1 -1
- package/dest/utils/commands.d.ts.map +1 -1
- package/dest/utils/commands.js +3 -1
- package/dest/utils/encoding.d.ts +1 -1
- package/dest/utils/encoding.d.ts.map +1 -1
- package/dest/utils/encoding.js +1 -0
- package/package.json +25 -25
- package/src/cmds/infrastructure/index.ts +13 -1
- package/src/cmds/infrastructure/sequencers.ts +14 -4
- package/src/cmds/l1/deploy_l1_contracts_cmd.ts +1 -0
- package/src/cmds/l1/index.ts +6 -0
- package/src/cmds/l1/l1_rpc_test_utils.ts +25 -2
- package/src/cmds/l1/prover_stats.ts +3 -2
- package/src/cmds/l1/update_l1_validators.ts +10 -4
- package/src/cmds/misc/generate_p2p_private_key.ts +1 -0
- package/src/cmds/validator_keys/add.ts +15 -2
- package/src/cmds/validator_keys/bls_key_derivation.ts +209 -0
- package/src/cmds/validator_keys/generate_bls_keypair.ts +26 -5
- package/src/cmds/validator_keys/index.ts +32 -0
- package/src/cmds/validator_keys/new.ts +50 -32
- package/src/cmds/validator_keys/shared.ts +70 -8
- package/src/cmds/validator_keys/staker.ts +56 -0
- package/src/config/chain_l2_config.ts +1 -0
- package/src/config/enrich_env.ts +1 -0
- package/src/config/get_l1_config.ts +2 -1
- package/src/config/network_config.ts +4 -2
- package/src/utils/aztec.ts +1 -0
- package/src/utils/commands.ts +3 -1
- package/src/utils/encoding.ts +1 -0
|
@@ -10,9 +10,12 @@ import { basename, dirname, join } from 'path';
|
|
|
10
10
|
import { createPublicClient, fallback, http } from 'viem';
|
|
11
11
|
import { generateMnemonic, mnemonicToAccount, privateKeyToAccount } from 'viem/accounts';
|
|
12
12
|
|
|
13
|
+
import { type BlsKeyDerivationOptions, resolveBlsKeyDerivationPolicy } from './bls_key_derivation.js';
|
|
13
14
|
import {
|
|
14
15
|
buildValidatorEntries,
|
|
15
16
|
encryptFundingAccountToFile,
|
|
17
|
+
getBlsDerivationPaths,
|
|
18
|
+
getBlsKeyCandidates,
|
|
16
19
|
logValidatorSummaries,
|
|
17
20
|
maybePrintJson,
|
|
18
21
|
resolveFundingAccount,
|
|
@@ -21,7 +24,7 @@ import {
|
|
|
21
24
|
writeEthJsonV3ToFile,
|
|
22
25
|
writeKeystoreFile,
|
|
23
26
|
} from './shared.js';
|
|
24
|
-
import { processAttesterAccounts } from './staker.js';
|
|
27
|
+
import { type StakerOutput, assertWithinProofOfPossessionGasLimit, processAttesterAccounts } from './staker.js';
|
|
25
28
|
import {
|
|
26
29
|
validateBlsPathOptions,
|
|
27
30
|
validateFundingAccountOptions,
|
|
@@ -59,7 +62,7 @@ export type NewValidatorKeystoreOptions = {
|
|
|
59
62
|
gseAddress?: EthAddress;
|
|
60
63
|
l1RpcUrls?: string[];
|
|
61
64
|
l1ChainId?: number;
|
|
62
|
-
};
|
|
65
|
+
} & BlsKeyDerivationOptions;
|
|
63
66
|
|
|
64
67
|
type PasswordSourceOptions = Pick<
|
|
65
68
|
NewValidatorKeystoreOptions,
|
|
@@ -152,6 +155,7 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
|
|
|
152
155
|
validateRemoteSignerOptions(options);
|
|
153
156
|
// validate funding account option
|
|
154
157
|
validateFundingAccountOptions(options);
|
|
158
|
+
const blsKeyDerivation = resolveBlsKeyDerivationPolicy(options);
|
|
155
159
|
|
|
156
160
|
const {
|
|
157
161
|
dataDir,
|
|
@@ -192,8 +196,9 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
|
|
|
192
196
|
});
|
|
193
197
|
const shouldEncryptKeystores = ethPassword !== undefined && blsPassword !== undefined;
|
|
194
198
|
const mnemonic = _mnemonic ?? generateMnemonic(wordlist);
|
|
199
|
+
const showGeneratedMnemonic = !_mnemonic && !json && !shouldEncryptKeystores;
|
|
195
200
|
|
|
196
|
-
if (
|
|
201
|
+
if (showGeneratedMnemonic) {
|
|
197
202
|
log('No mnemonic provided, generating new one...');
|
|
198
203
|
log(`Using new mnemonic:`);
|
|
199
204
|
log('');
|
|
@@ -217,8 +222,35 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
|
|
|
217
222
|
feeRecipient,
|
|
218
223
|
coinbase,
|
|
219
224
|
remoteSigner,
|
|
225
|
+
blsKeyDerivation,
|
|
226
|
+
generatedMnemonic: _mnemonic ? undefined : showGeneratedMnemonic ? 'shown' : 'hidden',
|
|
220
227
|
});
|
|
221
228
|
|
|
229
|
+
// Generate staker outputs if requested, from the plaintext keys and before writing anything, so that keys the GSE
|
|
230
|
+
// would reject leave no files behind.
|
|
231
|
+
const allStakerOutputs: StakerOutput[] = [];
|
|
232
|
+
if (stakerOutput && gseAddress && l1RpcUrls && l1ChainId !== undefined) {
|
|
233
|
+
const chain = createEthereumChain(l1RpcUrls, l1ChainId);
|
|
234
|
+
const publicClient = createPublicClient({
|
|
235
|
+
chain: chain.chainInfo,
|
|
236
|
+
transport: fallback(l1RpcUrls.map(url => http(url, { batch: false }))),
|
|
237
|
+
});
|
|
238
|
+
const gse = new GSEContract(publicClient, gseAddress);
|
|
239
|
+
|
|
240
|
+
// Process each validator
|
|
241
|
+
for (let i = 0; i < validators.length; i++) {
|
|
242
|
+
const validator = validators[i];
|
|
243
|
+
const outputs = await processAttesterAccounts(validator.attester, gse);
|
|
244
|
+
|
|
245
|
+
// Collect all staker outputs
|
|
246
|
+
for (let j = 0; j < outputs.length; j++) {
|
|
247
|
+
allStakerOutputs.push(outputs[j]);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
await assertWithinProofOfPossessionGasLimit(gse, allStakerOutputs);
|
|
252
|
+
}
|
|
253
|
+
|
|
222
254
|
let resolvedFundingAccount = fundingAccount ? resolveFundingAccount(fundingAccount, remoteSigner) : undefined;
|
|
223
255
|
|
|
224
256
|
// If password provided, write ETH JSON V3 and BLS BN254 keystores and replace plaintext
|
|
@@ -226,7 +258,11 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
|
|
|
226
258
|
const encryptedKeystoreOutDir =
|
|
227
259
|
encryptedKeystoreDir && encryptedKeystoreDir.length > 0 ? encryptedKeystoreDir : keystoreOutDir;
|
|
228
260
|
await writeEthJsonV3ToFile(validators, { outDir: encryptedKeystoreOutDir, password: ethPassword });
|
|
229
|
-
await writeBlsBn254ToFile(validators, {
|
|
261
|
+
await writeBlsBn254ToFile(validators, {
|
|
262
|
+
outDir: encryptedKeystoreOutDir,
|
|
263
|
+
password: blsPassword,
|
|
264
|
+
derivationPaths: getBlsDerivationPaths(summaries),
|
|
265
|
+
});
|
|
230
266
|
if (resolvedFundingAccount) {
|
|
231
267
|
resolvedFundingAccount = await encryptFundingAccountToFile(resolvedFundingAccount, {
|
|
232
268
|
outDir: encryptedKeystoreOutDir,
|
|
@@ -243,38 +279,20 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
|
|
|
243
279
|
|
|
244
280
|
await writeKeystoreFile(outputPath, keystore);
|
|
245
281
|
|
|
246
|
-
//
|
|
247
|
-
|
|
248
|
-
if (stakerOutput && gseAddress && l1RpcUrls && l1ChainId !== undefined) {
|
|
249
|
-
const chain = createEthereumChain(l1RpcUrls, l1ChainId);
|
|
250
|
-
const publicClient = createPublicClient({
|
|
251
|
-
chain: chain.chainInfo,
|
|
252
|
-
transport: fallback(l1RpcUrls.map(url => http(url, { batch: false }))),
|
|
253
|
-
});
|
|
254
|
-
const gse = new GSEContract(publicClient, gseAddress);
|
|
255
|
-
|
|
282
|
+
// Write a single JSON file with all staker outputs
|
|
283
|
+
if (allStakerOutputs.length > 0) {
|
|
256
284
|
// Extract keystore base name without extension for unique staker output filenames
|
|
257
285
|
const keystoreBaseName = basename(outputPath, '.json');
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
for (let i = 0; i < validators.length; i++) {
|
|
261
|
-
const validator = validators[i];
|
|
262
|
-
const outputs = await processAttesterAccounts(validator.attester, gse);
|
|
263
|
-
|
|
264
|
-
// Collect all staker outputs
|
|
265
|
-
for (let j = 0; j < outputs.length; j++) {
|
|
266
|
-
allStakerOutputs.push(outputs[j]);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Write a single JSON file with all staker outputs
|
|
271
|
-
if (allStakerOutputs.length > 0) {
|
|
272
|
-
const stakerOutputPath = join(keystoreOutDir, `${keystoreBaseName}_staker_output.json`);
|
|
273
|
-
await writeFile(stakerOutputPath, prettyPrintJSON(allStakerOutputs), 'utf-8');
|
|
274
|
-
}
|
|
286
|
+
const stakerOutputPath = join(keystoreOutDir, `${keystoreBaseName}_staker_output.json`);
|
|
287
|
+
await writeFile(stakerOutputPath, prettyPrintJSON(allStakerOutputs), 'utf-8');
|
|
275
288
|
}
|
|
276
289
|
|
|
277
|
-
const
|
|
290
|
+
const blsKeyCandidates = getBlsKeyCandidates(summaries);
|
|
291
|
+
const outputData = {
|
|
292
|
+
...keystore,
|
|
293
|
+
...(!_mnemonic && !shouldEncryptKeystores ? { generatedMnemonic: mnemonic } : {}),
|
|
294
|
+
...(blsKeyCandidates.length > 0 ? { blsKeyCandidates } : {}),
|
|
295
|
+
};
|
|
278
296
|
|
|
279
297
|
// Handle JSON output
|
|
280
298
|
if (json) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { prettyPrintJSON } from '@aztec-labs/cli/utils';
|
|
2
2
|
import { asyncPool } from '@aztec-labs/foundation/async-pool';
|
|
3
|
-
import {
|
|
3
|
+
import { getEnv } from '@aztec-labs/foundation/config/env';
|
|
4
4
|
import { createBn254Keystore } from '@aztec-labs/foundation/crypto/bls/bn254_keystore';
|
|
5
5
|
import { computeBn254G1PublicKeyCompressed } from '@aztec-labs/foundation/crypto/bn254';
|
|
6
6
|
import { EthAddress } from '@aztec-labs/foundation/eth-address';
|
|
@@ -8,6 +8,7 @@ import type { LogFn } from '@aztec-labs/foundation/log';
|
|
|
8
8
|
import type { EthAccount, EthPrivateKey, ValidatorKeyStore } from '@aztec-labs/node-keystore/types';
|
|
9
9
|
import type { AztecAddress } from '@aztec-labs/stdlib/aztec-address';
|
|
10
10
|
import { Wallet } from '@ethersproject/wallet';
|
|
11
|
+
import { Buffer } from 'buffer';
|
|
11
12
|
import { constants as fsConstants, mkdirSync } from 'fs';
|
|
12
13
|
import { access, writeFile } from 'fs/promises';
|
|
13
14
|
import { availableParallelism, homedir } from 'os';
|
|
@@ -15,6 +16,12 @@ import { dirname, isAbsolute, join } from 'path';
|
|
|
15
16
|
import { mnemonicToAccount } from 'viem/accounts';
|
|
16
17
|
import { Worker } from 'worker_threads';
|
|
17
18
|
|
|
19
|
+
import {
|
|
20
|
+
type BlsKeyDerivationPolicy,
|
|
21
|
+
type GeneratedMnemonic,
|
|
22
|
+
resolveBlsKeyDerivationPolicy,
|
|
23
|
+
selectBlsKey,
|
|
24
|
+
} from './bls_key_derivation.js';
|
|
18
25
|
import { defaultBlsPath } from './utils.js';
|
|
19
26
|
|
|
20
27
|
type EthJsonV3WorkerResult = { json: string } | { error: { message: string; name?: string; stack?: string } };
|
|
@@ -59,13 +66,23 @@ function encryptEthJsonV3InWorker(privateKeyHex: string, password: string): Prom
|
|
|
59
66
|
}
|
|
60
67
|
|
|
61
68
|
async function encryptEthJsonV3(privateKeyHex: string, password: string): Promise<string> {
|
|
62
|
-
if (
|
|
69
|
+
if (getEnv().JEST_WORKER_ID !== undefined) {
|
|
63
70
|
return await new Wallet(privateKeyHex).encrypt(password);
|
|
64
71
|
}
|
|
65
72
|
return await encryptEthJsonV3InWorker(privateKeyHex, password);
|
|
66
73
|
}
|
|
67
74
|
|
|
68
|
-
export type ValidatorSummary = {
|
|
75
|
+
export type ValidatorSummary = {
|
|
76
|
+
attesterEth?: string;
|
|
77
|
+
attesterBls?: string;
|
|
78
|
+
/** Derivation path of the BLS key, including the candidate component when the candidate is not 0. */
|
|
79
|
+
blsPath?: string;
|
|
80
|
+
/** Candidate selected by the BLS key derivation policy; 0 unless a retry picked another one. */
|
|
81
|
+
blsCandidate?: number;
|
|
82
|
+
/** The `--bls-key-derivation-max-gas` budget, when it took part in selecting a candidate other than 0. */
|
|
83
|
+
blsMaxGas?: number;
|
|
84
|
+
publisherEth?: string[];
|
|
85
|
+
};
|
|
69
86
|
|
|
70
87
|
export type BuildValidatorsInput = {
|
|
71
88
|
validatorCount: number;
|
|
@@ -79,12 +96,16 @@ export type BuildValidatorsInput = {
|
|
|
79
96
|
feeRecipient: AztecAddress;
|
|
80
97
|
coinbase?: EthAddress;
|
|
81
98
|
remoteSigner?: string;
|
|
99
|
+
/** Defaults to the policy used when no BLS key derivation flag is given. */
|
|
100
|
+
blsKeyDerivation?: BlsKeyDerivationPolicy;
|
|
101
|
+
/** Set when the CLI generated `mnemonic` in this run. */
|
|
102
|
+
generatedMnemonic?: GeneratedMnemonic;
|
|
82
103
|
};
|
|
83
104
|
|
|
84
105
|
export function withValidatorIndex(path: string, accountIndex: number = 0, addressIndex: number = 0) {
|
|
85
106
|
// 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
107
|
// 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(
|
|
108
|
+
const useLegacyBlsCli = ['true', '1', 'yes', 'y'].includes(getEnv().LEGACY_BLS_CLI ?? '');
|
|
88
109
|
|
|
89
110
|
const defaultBlsPathParts = defaultBlsPath.split('/');
|
|
90
111
|
|
|
@@ -153,6 +174,8 @@ export async function buildValidatorEntries(input: BuildValidatorsInput) {
|
|
|
153
174
|
feeRecipient,
|
|
154
175
|
coinbase,
|
|
155
176
|
remoteSigner,
|
|
177
|
+
blsKeyDerivation = resolveBlsKeyDerivationPolicy({}),
|
|
178
|
+
generatedMnemonic,
|
|
156
179
|
} = input;
|
|
157
180
|
|
|
158
181
|
const summaries: ValidatorSummary[] = [];
|
|
@@ -163,7 +186,11 @@ export async function buildValidatorEntries(input: BuildValidatorsInput) {
|
|
|
163
186
|
const basePath = blsPath ?? defaultBlsPath;
|
|
164
187
|
const perValidatorPath = withValidatorIndex(basePath, accountIndex, addressIndex);
|
|
165
188
|
|
|
166
|
-
const
|
|
189
|
+
const blsKey =
|
|
190
|
+
ikm || mnemonic
|
|
191
|
+
? selectBlsKey(blsKeyDerivation, { mnemonic, ikm, perValidatorPath, generatedMnemonic })
|
|
192
|
+
: undefined;
|
|
193
|
+
const blsPrivKey = blsKey?.privateKey;
|
|
167
194
|
const blsPubCompressed = blsPrivKey ? await computeBlsPublicKeyCompressed(blsPrivKey) : undefined;
|
|
168
195
|
|
|
169
196
|
const ethAttester = deriveEthAttester(mnemonic, accountIndex, addressIndex, remoteSigner);
|
|
@@ -198,6 +225,9 @@ export async function buildValidatorEntries(input: BuildValidatorsInput) {
|
|
|
198
225
|
summaries.push({
|
|
199
226
|
attesterEth: attesterEthAddress,
|
|
200
227
|
attesterBls: blsPubCompressed,
|
|
228
|
+
blsPath: blsKey?.path,
|
|
229
|
+
blsCandidate: blsKey?.candidate,
|
|
230
|
+
blsMaxGas: blsKey?.maxGas,
|
|
201
231
|
publisherEth: publisherAddresses.length > 0 ? publisherAddresses : undefined,
|
|
202
232
|
});
|
|
203
233
|
|
|
@@ -252,6 +282,10 @@ export function logValidatorSummaries(log: LogFn, summaries: ValidatorSummary[])
|
|
|
252
282
|
if (v.attesterBls) {
|
|
253
283
|
lines.push(` bls: ${v.attesterBls}`);
|
|
254
284
|
}
|
|
285
|
+
if (v.blsCandidate) {
|
|
286
|
+
const maxGas = v.blsMaxGas !== undefined ? `, max gas ${v.blsMaxGas}` : '';
|
|
287
|
+
lines.push(` bls derivation path: ${v.blsPath} (candidate ${v.blsCandidate}${maxGas})`);
|
|
288
|
+
}
|
|
255
289
|
if (v.publisherEth && v.publisherEth.length > 0) {
|
|
256
290
|
lines.push(` publisher:`);
|
|
257
291
|
for (const addr of v.publisherEth) {
|
|
@@ -259,11 +293,35 @@ export function logValidatorSummaries(log: LogFn, summaries: ValidatorSummary[])
|
|
|
259
293
|
}
|
|
260
294
|
}
|
|
261
295
|
}
|
|
296
|
+
if (summaries.some(v => v.blsCandidate)) {
|
|
297
|
+
lines.push(
|
|
298
|
+
'Some BLS keys were derived from a retry candidate (see "bls derivation path" above).',
|
|
299
|
+
'To regenerate them from the mnemonic, re-run with --bls-key-derivation-v6 and the same --bls-key-derivation-max-gas, if any.',
|
|
300
|
+
);
|
|
301
|
+
}
|
|
262
302
|
if (lines.length > 0) {
|
|
263
303
|
log(lines.join('\n'));
|
|
264
304
|
}
|
|
265
305
|
}
|
|
266
306
|
|
|
307
|
+
/** BLS keys in `summaries` that came from a retry candidate, for reporting alongside JSON output. */
|
|
308
|
+
export function getBlsKeyCandidates(summaries: ValidatorSummary[]) {
|
|
309
|
+
return summaries
|
|
310
|
+
.filter(v => v.blsCandidate)
|
|
311
|
+
.map(v => ({
|
|
312
|
+
attesterEth: v.attesterEth,
|
|
313
|
+
bls: v.attesterBls,
|
|
314
|
+
path: v.blsPath,
|
|
315
|
+
candidate: v.blsCandidate,
|
|
316
|
+
...(v.blsMaxGas !== undefined ? { maxGas: v.blsMaxGas } : {}),
|
|
317
|
+
}));
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** Derivation path of each BLS key in `summaries`, keyed by its compressed public key. */
|
|
321
|
+
export function getBlsDerivationPaths(summaries: ValidatorSummary[]): Map<string, string> {
|
|
322
|
+
return new Map(summaries.flatMap(v => (v.attesterBls && v.blsPath ? [[v.attesterBls, v.blsPath] as const] : [])));
|
|
323
|
+
}
|
|
324
|
+
|
|
267
325
|
export function maybePrintJson(log: LogFn, jsonFlag: boolean | undefined, obj: unknown) {
|
|
268
326
|
if (jsonFlag) {
|
|
269
327
|
log(prettyPrintJSON(obj as Record<string, any>));
|
|
@@ -300,10 +358,14 @@ export async function writeBn254BlsKeystore(
|
|
|
300
358
|
return outPath;
|
|
301
359
|
}
|
|
302
360
|
|
|
303
|
-
/**
|
|
361
|
+
/**
|
|
362
|
+
* Replace plaintext BLS keys in validators with { path, password } pointing to BN254 keystore files. Each file records
|
|
363
|
+
* the key's derivation path from `derivationPaths` (keyed by compressed public key), or an empty path for keys missing
|
|
364
|
+
* from it, such as keys that were already in a keystore file, whose origin is unknown.
|
|
365
|
+
*/
|
|
304
366
|
export async function writeBlsBn254ToFile(
|
|
305
367
|
validators: ValidatorKeyStore[],
|
|
306
|
-
options: { outDir: string; password: string;
|
|
368
|
+
options: { outDir: string; password: string; derivationPaths?: Map<string, string> },
|
|
307
369
|
): Promise<void> {
|
|
308
370
|
await Promise.all(
|
|
309
371
|
validators.map(async (v, i) => {
|
|
@@ -319,7 +381,7 @@ export async function writeBlsBn254ToFile(
|
|
|
319
381
|
}
|
|
320
382
|
|
|
321
383
|
const pub = await computeBlsPublicKeyCompressed(blsKey);
|
|
322
|
-
const path = options.
|
|
384
|
+
const path = options.derivationPaths?.get(pub) ?? '';
|
|
323
385
|
const fileBase = `${String(i + 1)}_${pub.slice(2, 18)}`;
|
|
324
386
|
const keystorePath = await writeBn254BlsKeystore(options.outDir, fileBase, options.password, blsKey, pub, path);
|
|
325
387
|
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
+
import { GSEAbi } from '@aztec-foundation/l1-artifacts/GSEAbi';
|
|
2
|
+
|
|
1
3
|
import { prettyPrintJSON } from '@aztec-labs/cli/utils';
|
|
4
|
+
import {
|
|
5
|
+
AMSTERDAM_PROOF_OF_POSSESSION_GAS_MODEL,
|
|
6
|
+
computeBn254RegistrationDigest,
|
|
7
|
+
estimateProofOfPossessionGas,
|
|
8
|
+
} from '@aztec-labs/ethereum/bn254';
|
|
2
9
|
import { createEthereumChain } from '@aztec-labs/ethereum/chain';
|
|
3
10
|
import { GSEContract } from '@aztec-labs/ethereum/contracts';
|
|
4
11
|
import { decryptBn254Keystore } from '@aztec-labs/foundation/crypto/bls/bn254_keystore';
|
|
@@ -21,6 +28,8 @@ import { basename, dirname, join } from 'path';
|
|
|
21
28
|
import { createPublicClient, fallback, http } from 'viem';
|
|
22
29
|
import { privateKeyToAddress } from 'viem/accounts';
|
|
23
30
|
|
|
31
|
+
import { LATEST_BLS_KEY_DERIVATION } from './bls_key_derivation.js';
|
|
32
|
+
|
|
24
33
|
export type StakerOptions = {
|
|
25
34
|
from: string;
|
|
26
35
|
password?: string;
|
|
@@ -247,6 +256,51 @@ export async function processAttesterAccounts(
|
|
|
247
256
|
return result ? [result] : [];
|
|
248
257
|
}
|
|
249
258
|
|
|
259
|
+
/**
|
|
260
|
+
* Throws when registering any of `outputs` could fail because the proof-of-possession check of its BLS key is estimated
|
|
261
|
+
* to need more gas than the GSE gives it. Governance sets that limit and can change it, so it is read from L1.
|
|
262
|
+
*/
|
|
263
|
+
export async function assertWithinProofOfPossessionGasLimit(gse: GSEContract, outputs: StakerOutput[]): Promise<void> {
|
|
264
|
+
const gasLimit = Number(
|
|
265
|
+
await gse.client.readContract({
|
|
266
|
+
address: gse.address.toString(),
|
|
267
|
+
abi: GSEAbi,
|
|
268
|
+
functionName: 'proofOfPossessionGasLimit',
|
|
269
|
+
}),
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
const overLimit = outputs
|
|
273
|
+
.map(output => {
|
|
274
|
+
const { attempts, sqrtCalls } = computeBn254RegistrationDigest({
|
|
275
|
+
x: BigInt(output.publicKeyG1.x),
|
|
276
|
+
y: BigInt(output.publicKeyG1.y),
|
|
277
|
+
});
|
|
278
|
+
return {
|
|
279
|
+
attester: output.attester,
|
|
280
|
+
estimatedGas: estimateProofOfPossessionGas(AMSTERDAM_PROOF_OF_POSSESSION_GAS_MODEL, attempts, sqrtCalls),
|
|
281
|
+
};
|
|
282
|
+
})
|
|
283
|
+
.filter(({ estimatedGas }) => estimatedGas > gasLimit);
|
|
284
|
+
if (overLimit.length === 0) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Leave the same 10% margin below the limit that the derivation version budgets leave below the cap they target.
|
|
289
|
+
const { version, budget } = LATEST_BLS_KEY_DERIVATION;
|
|
290
|
+
const maxGas = Math.floor(gasLimit * 0.9);
|
|
291
|
+
const flags =
|
|
292
|
+
maxGas < budget
|
|
293
|
+
? `--bls-key-derivation-v${version} --bls-key-derivation-max-gas ${maxGas}`
|
|
294
|
+
: `--bls-key-derivation-v${version}`;
|
|
295
|
+
throw new Error(
|
|
296
|
+
`Registering these validators could fail: the GSE at ${gse.address} gives each proof-of-possession check ` +
|
|
297
|
+
`${gasLimit} gas, and the BLS keys of these attesters are estimated to need more: ` +
|
|
298
|
+
`${overLimit.map(({ attester, estimatedGas }) => `${attester} (estimated at ${estimatedGas} gas)`).join(', ')}. ` +
|
|
299
|
+
`To replace their BLS keys and keep their ETH keys, regenerate them with validator-keys new, the same mnemonic ` +
|
|
300
|
+
`or IKM and indices, and ${flags}.`,
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
250
304
|
/**
|
|
251
305
|
* Main staker command function
|
|
252
306
|
*/
|
|
@@ -293,6 +347,8 @@ export async function generateStakerJson(options: StakerOptions, log: LogFn): Pr
|
|
|
293
347
|
return;
|
|
294
348
|
}
|
|
295
349
|
|
|
350
|
+
await assertWithinProofOfPossessionGasLimit(gse, allOutputs);
|
|
351
|
+
|
|
296
352
|
// Write a single JSON file with all staker outputs
|
|
297
353
|
const stakerOutputPath = join(outputDir, `${keystoreBaseName}_staker_output.json`);
|
|
298
354
|
writeFileSync(stakerOutputPath, prettyPrintJSON(allOutputs), 'utf-8');
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable no-restricted-properties -- this module sets variables of the Node.js process */
|
|
1
2
|
import type { NetworkNames } from '@aztec-labs/foundation/config';
|
|
2
3
|
import { createLogger } from '@aztec-labs/foundation/log';
|
|
3
4
|
import { type ConsensusEnvVar, checkConsensusEnvOverrides } from '@aztec-labs/stdlib/config';
|
package/src/config/enrich_env.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { getPublicClient } from '@aztec-labs/ethereum/client';
|
|
|
2
2
|
import { RegistryContract } from '@aztec-labs/ethereum/contracts';
|
|
3
3
|
import type { L1ContractAddresses } from '@aztec-labs/ethereum/l1-contract-addresses';
|
|
4
4
|
import { getL1ContractsConfig } from '@aztec-labs/ethereum/queries';
|
|
5
|
+
import { getEnv } from '@aztec-labs/foundation/config/env';
|
|
5
6
|
import { EthAddress } from '@aztec-labs/foundation/eth-address';
|
|
6
7
|
|
|
7
8
|
export async function getL1Config(
|
|
@@ -22,7 +23,7 @@ export async function getL1Config(
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export async function getL1RollupAddressFromEnv(l1RpcUrls: string[], l1ChainId: number) {
|
|
25
|
-
const registryAddress =
|
|
26
|
+
const registryAddress = getEnv().REGISTRY_CONTRACT_ADDRESS;
|
|
26
27
|
if (!registryAddress || !EthAddress.isAddress(registryAddress)) {
|
|
27
28
|
throw new Error(`Failed to extract registry address`);
|
|
28
29
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type NetworkConfig, NetworkConfigMapSchema, type NetworkNames } from '@aztec-labs/foundation/config';
|
|
2
|
+
import { getEnv } from '@aztec-labs/foundation/config/env';
|
|
2
3
|
import { readFile } from 'fs/promises';
|
|
3
4
|
import { join } from 'path';
|
|
4
5
|
|
|
@@ -23,7 +24,7 @@ export async function getNetworkConfig(
|
|
|
23
24
|
cacheDir?: string,
|
|
24
25
|
): Promise<NetworkConfig | undefined> {
|
|
25
26
|
// Try with the primary URL (env var or default)
|
|
26
|
-
const configLocation =
|
|
27
|
+
const configLocation = getEnv().NETWORK_CONFIG_LOCATION || DEFAULT_CONFIG_URL;
|
|
27
28
|
|
|
28
29
|
let primaryError: Error | undefined;
|
|
29
30
|
let config: NetworkConfig | undefined;
|
|
@@ -119,7 +120,8 @@ export async function enrichEnvironmentWithNetworkConfig(networkName: NetworkNam
|
|
|
119
120
|
return; // No remote config for local development
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
const
|
|
123
|
+
const { DATA_DIRECTORY } = getEnv();
|
|
124
|
+
const cacheDir = DATA_DIRECTORY ? join(DATA_DIRECTORY, 'cache') : undefined;
|
|
123
125
|
const networkConfig = await getNetworkConfig(networkName, cacheDir);
|
|
124
126
|
|
|
125
127
|
if (!networkConfig) {
|
package/src/utils/aztec.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { type LogFn, createLogger } from '@aztec-labs/foundation/log';
|
|
|
15
15
|
import type { NoirPackageConfig } from '@aztec-labs/foundation/noir';
|
|
16
16
|
import { protocolContractsHash } from '@aztec-labs/protocol-contracts';
|
|
17
17
|
import TOML from '@iarna/toml';
|
|
18
|
+
import { Buffer } from 'buffer';
|
|
18
19
|
import { readFile } from 'fs/promises';
|
|
19
20
|
import type { HDAccount, Hex, PrivateKeyAccount } from 'viem';
|
|
20
21
|
|
package/src/utils/commands.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getEnv } from '@aztec-labs/foundation/config/env';
|
|
1
2
|
import { Fr } from '@aztec-labs/foundation/curves/bn254';
|
|
2
3
|
import { GrumpkinScalar } from '@aztec-labs/foundation/curves/grumpkin';
|
|
3
4
|
import { EthAddress } from '@aztec-labs/foundation/eth-address';
|
|
@@ -8,6 +9,7 @@ import { AztecAddress } from '@aztec-labs/stdlib/aztec-address';
|
|
|
8
9
|
import { PublicKeys } from '@aztec-labs/stdlib/keys';
|
|
9
10
|
import { LogCursor, Tag } from '@aztec-labs/stdlib/logs';
|
|
10
11
|
import { TxHash } from '@aztec-labs/stdlib/tx/tx-hash';
|
|
12
|
+
import { Buffer } from 'buffer';
|
|
11
13
|
import { type Command, CommanderError, InvalidArgumentError, Option } from 'commander';
|
|
12
14
|
import { lookup } from 'dns/promises';
|
|
13
15
|
import { rename, writeFile } from 'fs/promises';
|
|
@@ -24,7 +26,7 @@ export const getLocalhost = () =>
|
|
|
24
26
|
.catch(() => 'localhost');
|
|
25
27
|
|
|
26
28
|
export const LOCALHOST = await getLocalhost();
|
|
27
|
-
export const { ETHEREUM_HOSTS = `http://${LOCALHOST}:8545`, PRIVATE_KEY, MNEMONIC, API_KEY, CLI_VERSION } =
|
|
29
|
+
export const { ETHEREUM_HOSTS = `http://${LOCALHOST}:8545`, PRIVATE_KEY, MNEMONIC, API_KEY, CLI_VERSION } = getEnv();
|
|
28
30
|
|
|
29
31
|
export function addOptions(program: Command, options: Option[]) {
|
|
30
32
|
options.forEach(option => program.addOption(option));
|
package/src/utils/encoding.ts
CHANGED