@aztec/cli 3.0.0-nightly.20251118 → 3.0.0-nightly.20251119
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/validator_keys/add.d.ts.map +1 -1
- package/dest/cmds/validator_keys/add.js +14 -4
- package/dest/cmds/validator_keys/generate_bls_keypair.js +2 -2
- package/dest/cmds/validator_keys/index.d.ts.map +1 -1
- package/dest/cmds/validator_keys/index.js +4 -3
- package/dest/cmds/validator_keys/new.d.ts +1 -1
- package/dest/cmds/validator_keys/new.d.ts.map +1 -1
- package/dest/cmds/validator_keys/new.js +17 -12
- package/dest/cmds/validator_keys/shared.d.ts +10 -0
- package/dest/cmds/validator_keys/shared.d.ts.map +1 -1
- package/dest/cmds/validator_keys/shared.js +27 -5
- package/dest/cmds/validator_keys/staker.d.ts.map +1 -1
- package/dest/cmds/validator_keys/staker.js +12 -11
- package/package.json +30 -29
- package/public_include_metric_prefixes.json +1 -0
- package/src/cmds/validator_keys/add.ts +14 -4
- package/src/cmds/validator_keys/generate_bls_keypair.ts +2 -2
- package/src/cmds/validator_keys/index.ts +15 -13
- package/src/cmds/validator_keys/new.ts +21 -15
- package/src/cmds/validator_keys/shared.ts +40 -6
- package/src/cmds/validator_keys/staker.ts +14 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../../src/cmds/validator_keys/add.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAQnD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../../src/cmds/validator_keys/add.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAQnD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAW5D,MAAM,MAAM,uBAAuB,GAAG,2BAA2B,CAAC;AAElE,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,uBAAuB,EAAE,GAAG,EAAE,KAAK,iBAkGpG"}
|
|
@@ -2,9 +2,11 @@ import { loadKeystoreFile } from '@aztec/node-keystore/loader';
|
|
|
2
2
|
import { wordlist } from '@scure/bip39/wordlists/english.js';
|
|
3
3
|
import { dirname, isAbsolute, join } from 'path';
|
|
4
4
|
import { generateMnemonic } from 'viem/accounts';
|
|
5
|
-
import { buildValidatorEntries, logValidatorSummaries, maybePrintJson, writeBlsBn254ToFile, writeEthJsonV3ToFile, writeKeystoreFile } from './shared.js';
|
|
5
|
+
import { buildValidatorEntries, logValidatorSummaries, maybePrintJson, validateBlsPathOptions, writeBlsBn254ToFile, writeEthJsonV3ToFile, writeKeystoreFile } from './shared.js';
|
|
6
6
|
export async function addValidatorKeys(existing, options, log) {
|
|
7
|
-
|
|
7
|
+
// validate bls-path inputs before proceeding with key generation
|
|
8
|
+
validateBlsPathOptions(options);
|
|
9
|
+
const { dataDir, file, count, publisherCount = 0, mnemonic, accountIndex = 0, addressIndex, ikm, blsPath, json, feeRecipient: feeRecipientOpt, coinbase: coinbaseOpt, fundingAccount: fundingAccountOpt, remoteSigner: remoteSignerOpt, password, encryptedKeystoreDir } = options;
|
|
8
10
|
const validatorCount = typeof count === 'number' && Number.isFinite(count) && count > 0 ? Math.floor(count) : 1;
|
|
9
11
|
const baseAddressIndex = addressIndex ?? 0;
|
|
10
12
|
const keystore = loadKeystoreFile(existing);
|
|
@@ -40,14 +42,22 @@ export async function addValidatorKeys(existing, options, log) {
|
|
|
40
42
|
keystore.validators.push(...validators);
|
|
41
43
|
// If password provided, write ETH JSON V3 and BLS BN254 keystores and replace plaintext
|
|
42
44
|
if (password !== undefined) {
|
|
43
|
-
|
|
45
|
+
let targetDir;
|
|
46
|
+
if (encryptedKeystoreDir && encryptedKeystoreDir.length > 0) {
|
|
47
|
+
targetDir = encryptedKeystoreDir;
|
|
48
|
+
} else if (dataDir && dataDir.length > 0) {
|
|
49
|
+
targetDir = dataDir;
|
|
50
|
+
} else {
|
|
51
|
+
targetDir = dirname(existing);
|
|
52
|
+
}
|
|
44
53
|
await writeEthJsonV3ToFile(keystore.validators, {
|
|
45
54
|
outDir: targetDir,
|
|
46
55
|
password
|
|
47
56
|
});
|
|
48
57
|
await writeBlsBn254ToFile(keystore.validators, {
|
|
49
58
|
outDir: targetDir,
|
|
50
|
-
password
|
|
59
|
+
password,
|
|
60
|
+
blsPath
|
|
51
61
|
});
|
|
52
62
|
}
|
|
53
63
|
let outputPath = existing;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { deriveBlsPrivateKey } from '@aztec/foundation/crypto';
|
|
2
2
|
import { writeFile } from 'fs/promises';
|
|
3
|
-
import { computeBlsPublicKeyCompressed, withValidatorIndex } from './shared.js';
|
|
3
|
+
import { computeBlsPublicKeyCompressed, defaultBlsPath, withValidatorIndex } from './shared.js';
|
|
4
4
|
export async function generateBlsKeypair(options, log) {
|
|
5
5
|
const { mnemonic, ikm, blsPath, compressed = true, json, out } = options;
|
|
6
|
-
const path = withValidatorIndex(blsPath ??
|
|
6
|
+
const path = withValidatorIndex(blsPath ?? defaultBlsPath, 0);
|
|
7
7
|
const priv = deriveBlsPrivateKey(mnemonic, ikm, path);
|
|
8
8
|
const pub = await computeBlsPublicKeyCompressed(priv);
|
|
9
9
|
const result = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cmds/validator_keys/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAEnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cmds/validator_keys/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAEnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,WAsH1D"}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { parseAztecAddress, parseEthereumAddress, parseHex, parseOptionalInteger } from '../../utils/commands.js';
|
|
2
|
+
import { defaultBlsPath } from './shared.js';
|
|
2
3
|
export function injectCommands(program, log) {
|
|
3
4
|
const group = program.command('validator-keys').aliases([
|
|
4
5
|
'valKeys',
|
|
5
6
|
'valkeys'
|
|
6
7
|
]).description('Manage validator keystores for node operators');
|
|
7
|
-
group.command('new').summary('Generate a new validator keystore JSON').description('Generates a new validator keystore with ETH secp256k1 accounts and optional BLS accounts').option('--data-dir <path>', 'Directory to store keystore(s). Defaults to ~/.aztec/keystore').option('--file <name>', 'Keystore file name. Defaults to key1.json (or keyN.json if key1.json exists)').option('--count <N>', 'Number of validators to generate', parseOptionalInteger).option('--publisher-count <N>', 'Number of publisher accounts per validator (default 1)', (value)=>parseOptionalInteger(value, 0)).option('--mnemonic <mnemonic>', 'Mnemonic for ETH/BLS derivation').option('--passphrase <str>', 'Optional passphrase for mnemonic').option('--account-index <N>', 'Base account index for ETH derivation', parseOptionalInteger).option('--address-index <N>', 'Base address index for ETH derivation', parseOptionalInteger).option('--coinbase <address>', 'Coinbase ETH address to use when proposing', parseEthereumAddress).option('--funding-account <address>', 'ETH account to fund publishers', parseEthereumAddress).option('--remote-signer <url>', 'Default remote signer URL for accounts in this file').option('--ikm <hex>', 'Initial keying material for BLS (alternative to mnemonic)', (value)=>parseHex(value, 32)).option('--bls-path <path>',
|
|
8
|
+
group.command('new').summary('Generate a new validator keystore JSON').description('Generates a new validator keystore with ETH secp256k1 accounts and optional BLS accounts').option('--data-dir <path>', 'Directory to store keystore(s). Defaults to ~/.aztec/keystore').option('--file <name>', 'Keystore file name. Defaults to key1.json (or keyN.json if key1.json exists)').option('--count <N>', 'Number of validators to generate', parseOptionalInteger).option('--publisher-count <N>', 'Number of publisher accounts per validator (default 1)', (value)=>parseOptionalInteger(value, 0)).option('--mnemonic <mnemonic>', 'Mnemonic for ETH/BLS derivation').option('--passphrase <str>', 'Optional passphrase for mnemonic').option('--account-index <N>', 'Base account index for ETH/BLS derivation', parseOptionalInteger).option('--address-index <N>', 'Base address index for ETH/BLS derivation', parseOptionalInteger).option('--coinbase <address>', 'Coinbase ETH address to use when proposing', parseEthereumAddress).option('--funding-account <address>', 'ETH account to fund publishers', parseEthereumAddress).option('--remote-signer <url>', 'Default remote signer URL for accounts in this file').option('--ikm <hex>', 'Initial keying material for BLS (alternative to mnemonic)', (value)=>parseHex(value, 32)).option('--bls-path <path>', `EIP-2334 path (default ${defaultBlsPath})`).option('--password <str>', 'Password for writing keystore files (ETH JSON V3 and BLS EIP-2335). Empty string allowed').option('--encrypted-keystore-dir <dir>', 'Output directory for encrypted keystore file(s)').option('--json', 'Echo resulting JSON to stdout').option('--staker-output', 'Generate a single staker output JSON file with an array of validator entries').option('--gse-address <address>', 'GSE contract address (required with --staker-output)', parseEthereumAddress).option('--l1-rpc-urls <urls>', 'L1 RPC URLs (comma-separated, required with --staker-output)', (value)=>value.split(',')).option('-c, --l1-chain-id <number>', 'L1 chain ID (required with --staker-output)', (value)=>parseInt(value), 31337).requiredOption('--fee-recipient <address>', 'Aztec address that will receive fees', parseAztecAddress).action(async (options)=>{
|
|
8
9
|
const { newValidatorKeystore } = await import('./new.js');
|
|
9
10
|
await newValidatorKeystore(options, log);
|
|
10
11
|
});
|
|
11
|
-
group.command('add').summary('Augment an existing validator keystore JSON').description('Adds attester/publisher/BLS entries to an existing keystore using the same flags as new').argument('<existing>', 'Path to existing keystore JSON').option('--data-dir <path>', 'Directory where keystore(s) live').option('--file <name>', 'Override output file name').option('--count <N>', 'Number of validators to add', parseOptionalInteger).option('--publisher-count <N>', 'Number of publisher accounts per validator (default 1)', (value)=>parseOptionalInteger(value, 0)).option('--mnemonic <mnemonic>', 'Mnemonic for ETH/BLS derivation').option('--passphrase <str>', 'Optional passphrase for mnemonic').option('--account-index <N>', 'Base account index for ETH derivation', parseOptionalInteger).option('--address-index <N>', 'Base address index for ETH derivation', parseOptionalInteger).option('--coinbase <address>', 'Coinbase ETH address to use when proposing', parseEthereumAddress).option('--funding-account <address>', 'ETH account to fund publishers', parseEthereumAddress).option('--remote-signer <url>', 'Default remote signer URL for accounts in this file').option('--ikm <hex>', 'Initial keying material for BLS (alternative to mnemonic)', (value)=>parseHex(value, 32)).option('--bls-path <path>',
|
|
12
|
+
group.command('add').summary('Augment an existing validator keystore JSON').description('Adds attester/publisher/BLS entries to an existing keystore using the same flags as new').argument('<existing>', 'Path to existing keystore JSON').option('--data-dir <path>', 'Directory where keystore(s) live. (default: ~/.aztec/keystore)').option('--file <name>', 'Override output file name. (default: key<N>.json)').option('--count <N>', 'Number of validators to add. (default: 1)', parseOptionalInteger).option('--publisher-count <N>', 'Number of publisher accounts per validator (default 1)', (value)=>parseOptionalInteger(value, 0)).option('--mnemonic <mnemonic>', 'Mnemonic for ETH/BLS derivation').option('--passphrase <str>', 'Optional passphrase for mnemonic').option('--account-index <N>', 'Base account index for ETH/BLS derivation', parseOptionalInteger).option('--address-index <N>', 'Base address index for ETH/BLS derivation', parseOptionalInteger).option('--coinbase <address>', 'Coinbase ETH address to use when proposing', parseEthereumAddress).option('--funding-account <address>', 'ETH account to fund publishers', parseEthereumAddress).option('--remote-signer <url>', 'Default remote signer URL for accounts in this file').option('--ikm <hex>', 'Initial keying material for BLS (alternative to mnemonic)', (value)=>parseHex(value, 32)).option('--bls-path <path>', `EIP-2334 path (default ${defaultBlsPath})`).option('--empty', 'Generate an empty skeleton without keys').option('--password <str>', 'Password for writing keystore files (ETH JSON V3 and BLS EIP-2335). Empty string allowed').option('--encrypted-keystore-dir <dir>', 'Output directory for encrypted keystore file(s)').option('--json', 'Echo resulting JSON to stdout').requiredOption('--fee-recipient <address>', 'Aztec address that will receive fees', parseAztecAddress).action(async (existing, options)=>{
|
|
12
13
|
const { addValidatorKeys } = await import('./add.js');
|
|
13
14
|
await addValidatorKeys(existing, options, log);
|
|
14
15
|
});
|
|
@@ -19,7 +20,7 @@ export function injectCommands(program, log) {
|
|
|
19
20
|
await generateStakerJson(options, log);
|
|
20
21
|
});
|
|
21
22
|
// top-level convenience: aztec generate-bls-keypair
|
|
22
|
-
program.command('generate-bls-keypair').description('Generate a BLS keypair with convenience flags').option('--mnemonic <mnemonic>', 'Mnemonic for BLS derivation').option('--ikm <hex>', 'Initial keying material for BLS (alternative to mnemonic)', (value)=>parseHex(value, 32)).option('--bls-path <path>',
|
|
23
|
+
program.command('generate-bls-keypair').description('Generate a BLS keypair with convenience flags').option('--mnemonic <mnemonic>', 'Mnemonic for BLS derivation').option('--ikm <hex>', 'Initial keying material for BLS (alternative to mnemonic)', (value)=>parseHex(value, 32)).option('--bls-path <path>', `EIP-2334 path (default ${defaultBlsPath})`).option('--g2', 'Derive on G2 subgroup').option('--compressed', 'Output compressed public key').option('--json', 'Print JSON output to stdout').option('--out <file>', 'Write output to file').action(async (options)=>{
|
|
23
24
|
const { generateBlsKeypair } = await import('./generate_bls_keypair.js');
|
|
24
25
|
await generateBlsKeypair(options, log);
|
|
25
26
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../../src/cmds/validator_keys/new.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../../src/cmds/validator_keys/new.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAoBhE,MAAM,MAAM,2BAA2B,GAAG;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,EAAE,YAAY,CAAC;IAC3B,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,UAAU,CAAC;IAC5B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,2BAA2B,EAAE,GAAG,EAAE,KAAK,iBAoK1F"}
|
|
@@ -5,10 +5,12 @@ import { writeFile } from 'fs/promises';
|
|
|
5
5
|
import { basename, dirname, join } from 'path';
|
|
6
6
|
import { createPublicClient, fallback, http } from 'viem';
|
|
7
7
|
import { generateMnemonic, mnemonicToAccount } from 'viem/accounts';
|
|
8
|
-
import { buildValidatorEntries, logValidatorSummaries, maybePrintJson, resolveKeystoreOutputPath, writeBlsBn254ToFile, writeEthJsonV3ToFile, writeKeystoreFile } from './shared.js';
|
|
8
|
+
import { buildValidatorEntries, logValidatorSummaries, maybePrintJson, resolveKeystoreOutputPath, validateBlsPathOptions, writeBlsBn254ToFile, writeEthJsonV3ToFile, writeKeystoreFile } from './shared.js';
|
|
9
9
|
import { processAttesterAccounts } from './staker.js';
|
|
10
10
|
export async function newValidatorKeystore(options, log) {
|
|
11
|
-
|
|
11
|
+
// validate bls-path inputs before proceeding with key generation
|
|
12
|
+
validateBlsPathOptions(options);
|
|
13
|
+
const { dataDir, file, count, publisherCount = 0, json, coinbase, accountIndex = 0, addressIndex = 0, feeRecipient, remoteSigner, fundingAccount, blsPath, ikm, mnemonic: _mnemonic, password, encryptedKeystoreDir, stakerOutput, gseAddress, l1RpcUrls, l1ChainId } = options;
|
|
12
14
|
// Validate staker output requirements
|
|
13
15
|
if (stakerOutput) {
|
|
14
16
|
if (!gseAddress) {
|
|
@@ -34,6 +36,7 @@ export async function newValidatorKeystore(options, log) {
|
|
|
34
36
|
}
|
|
35
37
|
const validatorCount = typeof count === 'number' && Number.isFinite(count) && count > 0 ? Math.floor(count) : 1;
|
|
36
38
|
const { outputPath } = await resolveKeystoreOutputPath(dataDir, file);
|
|
39
|
+
const keystoreOutDir = dirname(outputPath);
|
|
37
40
|
const { validators, summaries } = await buildValidatorEntries({
|
|
38
41
|
validatorCount,
|
|
39
42
|
publisherCount,
|
|
@@ -49,13 +52,13 @@ export async function newValidatorKeystore(options, log) {
|
|
|
49
52
|
});
|
|
50
53
|
// If password provided, write ETH JSON V3 and BLS BN254 keystores and replace plaintext
|
|
51
54
|
if (password !== undefined) {
|
|
52
|
-
const
|
|
55
|
+
const encryptedKeystoreOutDir = encryptedKeystoreDir && encryptedKeystoreDir.length > 0 ? encryptedKeystoreDir : keystoreOutDir;
|
|
53
56
|
await writeEthJsonV3ToFile(validators, {
|
|
54
|
-
outDir:
|
|
57
|
+
outDir: encryptedKeystoreOutDir,
|
|
55
58
|
password
|
|
56
59
|
});
|
|
57
60
|
await writeBlsBn254ToFile(validators, {
|
|
58
|
-
outDir:
|
|
61
|
+
outDir: encryptedKeystoreOutDir,
|
|
59
62
|
password
|
|
60
63
|
});
|
|
61
64
|
}
|
|
@@ -73,21 +76,22 @@ export async function newValidatorKeystore(options, log) {
|
|
|
73
76
|
transport: fallback(l1RpcUrls.map((url)=>http(url)))
|
|
74
77
|
});
|
|
75
78
|
const gse = new GSEContract(publicClient, gseAddress);
|
|
76
|
-
const keystoreOutDir = outDir && outDir.length > 0 ? outDir : dirname(outputPath);
|
|
77
79
|
// Extract keystore base name without extension for unique staker output filenames
|
|
78
80
|
const keystoreBaseName = basename(outputPath, '.json');
|
|
79
81
|
// Process each validator
|
|
80
82
|
for(let i = 0; i < validators.length; i++){
|
|
81
83
|
const validator = validators[i];
|
|
82
84
|
const outputs = await processAttesterAccounts(validator.attester, gse, password);
|
|
83
|
-
//
|
|
85
|
+
// Collect all staker outputs
|
|
84
86
|
for(let j = 0; j < outputs.length; j++){
|
|
85
|
-
const attesterIndex = i + 1;
|
|
86
|
-
const stakerOutputPath = join(keystoreOutDir, `${keystoreBaseName}_attester${attesterIndex}_staker_output.json`);
|
|
87
|
-
await writeFile(stakerOutputPath, prettyPrintJSON(outputs[j]), 'utf-8');
|
|
88
87
|
allStakerOutputs.push(outputs[j]);
|
|
89
88
|
}
|
|
90
89
|
}
|
|
90
|
+
// Write a single JSON file with all staker outputs
|
|
91
|
+
if (allStakerOutputs.length > 0) {
|
|
92
|
+
const stakerOutputPath = join(keystoreOutDir, `${keystoreBaseName}_staker_output.json`);
|
|
93
|
+
await writeFile(stakerOutputPath, prettyPrintJSON(allStakerOutputs), 'utf-8');
|
|
94
|
+
}
|
|
91
95
|
}
|
|
92
96
|
const outputData = !_mnemonic ? {
|
|
93
97
|
...keystore,
|
|
@@ -107,8 +111,9 @@ export async function newValidatorKeystore(options, log) {
|
|
|
107
111
|
} else {
|
|
108
112
|
log(`Wrote validator keystore to ${outputPath}`);
|
|
109
113
|
if (stakerOutput && allStakerOutputs.length > 0) {
|
|
110
|
-
const
|
|
111
|
-
|
|
114
|
+
const keystoreBaseName = basename(outputPath, '.json');
|
|
115
|
+
const stakerOutputPath = join(keystoreOutDir, `${keystoreBaseName}_staker_output.json`);
|
|
116
|
+
log(`Wrote staker output for ${allStakerOutputs.length} validator(s) to ${stakerOutputPath}`);
|
|
112
117
|
log('');
|
|
113
118
|
}
|
|
114
119
|
}
|
|
@@ -20,6 +20,15 @@ export type BuildValidatorsInput = {
|
|
|
20
20
|
remoteSigner?: string;
|
|
21
21
|
fundingAccount?: EthAddress;
|
|
22
22
|
};
|
|
23
|
+
export declare const defaultBlsPath = "m/12381/3600/0/0/0";
|
|
24
|
+
export declare function validateBlsPathOptions(options: {
|
|
25
|
+
count?: number;
|
|
26
|
+
publisherCount?: number;
|
|
27
|
+
accountIndex?: number;
|
|
28
|
+
addressIndex?: number;
|
|
29
|
+
blsPath?: string;
|
|
30
|
+
ikm?: string;
|
|
31
|
+
}): void;
|
|
23
32
|
export declare function withValidatorIndex(path: string, accountIndex?: number, addressIndex?: number): string;
|
|
24
33
|
/**
|
|
25
34
|
* Compute a compressed BN254 G1 public key from a private key.
|
|
@@ -56,6 +65,7 @@ export declare function writeBn254BlsKeystore(outDir: string, fileNameBase: stri
|
|
|
56
65
|
export declare function writeBlsBn254ToFile(validators: ValidatorKeyStore[], options: {
|
|
57
66
|
outDir: string;
|
|
58
67
|
password: string;
|
|
68
|
+
blsPath?: string;
|
|
59
69
|
}): Promise<void>;
|
|
60
70
|
/** Writes an Ethereum JSON V3 keystore using ethers, returns absolute path */
|
|
61
71
|
export declare function writeEthJsonV3Keystore(outDir: string, fileNameBase: string, password: string, privateKeyHex: string): Promise<string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/cmds/validator_keys/shared.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAShE,MAAM,MAAM,gBAAgB,GAAG;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAEvG,MAAM,MAAM,oBAAoB,GAAG;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,YAAY,CAAC;IAC3B,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,UAAU,CAAC;CAC7B,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,GAAE,MAAU,EAAE,YAAY,GAAE,MAAU,
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/cmds/validator_keys/shared.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,11 +7,34 @@ import { access, writeFile } from 'fs/promises';
|
|
|
7
7
|
import { homedir } from 'os';
|
|
8
8
|
import { dirname, isAbsolute, join } from 'path';
|
|
9
9
|
import { mnemonicToAccount } from 'viem/accounts';
|
|
10
|
+
export const defaultBlsPath = 'm/12381/3600/0/0/0';
|
|
11
|
+
export function validateBlsPathOptions(options) {
|
|
12
|
+
if (options.blsPath && options.blsPath !== defaultBlsPath) {
|
|
13
|
+
if (options.count && options.count !== 1 || options.publisherCount && options.publisherCount > 0 || options.accountIndex && options.accountIndex !== 0 || options.addressIndex && options.addressIndex !== 0) {
|
|
14
|
+
throw new Error('--bls-path cannot be used with --count, --publisher-count, --account-index, or --address-index');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
10
18
|
export function withValidatorIndex(path, accountIndex = 0, addressIndex = 0) {
|
|
19
|
+
// NOTE: The legacy BLS CLI is to allow users who generated keys in 2.1.4 to be able to use the same command
|
|
20
|
+
// 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.
|
|
21
|
+
const useLegacyBlsCli = [
|
|
22
|
+
'true',
|
|
23
|
+
'1',
|
|
24
|
+
'yes',
|
|
25
|
+
'y'
|
|
26
|
+
].includes(process.env.LEGACY_BLS_CLI ?? '');
|
|
27
|
+
const defaultBlsPathParts = defaultBlsPath.split('/');
|
|
11
28
|
const parts = path.split('/');
|
|
12
|
-
if (parts.length ==
|
|
13
|
-
|
|
14
|
-
|
|
29
|
+
if (parts.length == defaultBlsPathParts.length && parts.every((part, index)=>part === defaultBlsPathParts[index])) {
|
|
30
|
+
if (useLegacyBlsCli) {
|
|
31
|
+
// In 2.1.4, we were using address-index in parts[3] and did NOT use account-index, check lines 32 & 84
|
|
32
|
+
// https://github.com/AztecProtocol/aztec-packages/blob/v2.1.4/yarn-project/cli/src/cmds/validator_keys/shared.ts
|
|
33
|
+
parts[3] = String(addressIndex);
|
|
34
|
+
} else {
|
|
35
|
+
parts[3] = String(accountIndex);
|
|
36
|
+
parts[5] = String(addressIndex);
|
|
37
|
+
}
|
|
15
38
|
return parts.join('/');
|
|
16
39
|
}
|
|
17
40
|
return path;
|
|
@@ -35,7 +58,6 @@ export function deriveEthAttester(mnemonic, baseAccountIndex, addressIndex, remo
|
|
|
35
58
|
}
|
|
36
59
|
export async function buildValidatorEntries(input) {
|
|
37
60
|
const { validatorCount, publisherCount = 0, accountIndex, baseAddressIndex, mnemonic, ikm, blsPath, feeRecipient, coinbase, remoteSigner, fundingAccount } = input;
|
|
38
|
-
const defaultBlsPath = 'm/12381/3600/0/0/0';
|
|
39
61
|
const summaries = [];
|
|
40
62
|
const validators = await Promise.all(Array.from({
|
|
41
63
|
length: validatorCount
|
|
@@ -191,7 +213,7 @@ export function maybePrintJson(log, jsonFlag, obj) {
|
|
|
191
213
|
continue;
|
|
192
214
|
}
|
|
193
215
|
const pub = await computeBlsPublicKeyCompressed(blsKey);
|
|
194
|
-
const path =
|
|
216
|
+
const path = options.blsPath ?? defaultBlsPath;
|
|
195
217
|
const fileBase = `${String(i + 1)}_${pub.slice(2, 18)}`;
|
|
196
218
|
const keystorePath = await writeBn254BlsKeystore(options.outDir, fileBase, options.password, blsKey, pub, path);
|
|
197
219
|
if (typeof att === 'object') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"staker.d.ts","sourceRoot":"","sources":["../../../src/cmds/validator_keys/staker.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAuB,MAAM,iBAAiB,CAAC;AAGnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAEnD,OAAO,KAAK,EAEV,gBAAgB,EAKjB,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"staker.d.ts","sourceRoot":"","sources":["../../../src/cmds/validator_keys/staker.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAuB,MAAM,iBAAiB,CAAC;AAGnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAEnD,OAAO,KAAK,EAEV,gBAAgB,EAKjB,MAAM,4BAA4B,CAAC;AAQpC,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,UAAU,CAAC;IACvB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE;QACX,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;KACX,CAAC;IACF,WAAW,EAAE;QACX,EAAE,EAAE,MAAM,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;KACZ,CAAC;IACF,iBAAiB,EAAE;QACjB,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;KACX,CAAC;CACH,CAAC;AA2KF;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,gBAAgB,EAAE,gBAAgB,EAClC,GAAG,EAAE,WAAW,EAChB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,YAAY,EAAE,CAAC,CAqBzB;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CA+C1F"}
|
|
@@ -6,6 +6,7 @@ import { Fr } from '@aztec/foundation/fields';
|
|
|
6
6
|
import { loadKeystoreFile } from '@aztec/node-keystore/loader';
|
|
7
7
|
import { Wallet } from '@ethersproject/wallet';
|
|
8
8
|
import { readFileSync, writeFileSync } from 'fs';
|
|
9
|
+
import { basename, dirname, join } from 'path';
|
|
9
10
|
import { createPublicClient, fallback, http } from 'viem';
|
|
10
11
|
import { privateKeyToAddress } from 'viem/accounts';
|
|
11
12
|
/**
|
|
@@ -186,21 +187,21 @@ import { privateKeyToAddress } from 'viem/accounts';
|
|
|
186
187
|
transport: fallback(l1RpcUrls.map((url)=>http(url)))
|
|
187
188
|
});
|
|
188
189
|
const gse = new GSEContract(publicClient, gseAddress);
|
|
189
|
-
|
|
190
|
-
|
|
190
|
+
const keystoreBaseName = basename(from, '.json');
|
|
191
|
+
const outputDir = output ? output : dirname(from);
|
|
192
|
+
for(let i = 0; i < keystore.validators.length; i++){
|
|
193
|
+
const validator = keystore.validators[i];
|
|
191
194
|
const outputs = await processAttesterAccounts(validator.attester, gse, password);
|
|
192
|
-
|
|
195
|
+
for(let j = 0; j < outputs.length; j++){
|
|
196
|
+
allOutputs.push(outputs[j]);
|
|
197
|
+
}
|
|
193
198
|
}
|
|
194
199
|
if (allOutputs.length === 0) {
|
|
195
200
|
log('No attesters with BLS keys found (skipping mnemonics and encrypted keystores without password)');
|
|
196
201
|
return;
|
|
197
202
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
log(`Wrote staking data to ${output}`);
|
|
203
|
-
} else {
|
|
204
|
-
log(jsonOutput);
|
|
205
|
-
}
|
|
203
|
+
// Write a single JSON file with all staker outputs
|
|
204
|
+
const stakerOutputPath = join(outputDir, `${keystoreBaseName}_staker_output.json`);
|
|
205
|
+
writeFileSync(stakerOutputPath, prettyPrintJSON(allOutputs), 'utf-8');
|
|
206
|
+
log(`Wrote staker output for ${allOutputs.length} validator(s) to ${stakerOutputPath}`);
|
|
206
207
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/cli",
|
|
3
|
-
"version": "3.0.0-nightly.
|
|
3
|
+
"version": "3.0.0-nightly.20251119",
|
|
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.
|
|
75
|
-
"@aztec/archiver": "3.0.0-nightly.
|
|
76
|
-
"@aztec/aztec.js": "3.0.0-nightly.
|
|
77
|
-
"@aztec/constants": "3.0.0-nightly.
|
|
78
|
-
"@aztec/entrypoints": "3.0.0-nightly.
|
|
79
|
-
"@aztec/ethereum": "3.0.0-nightly.
|
|
80
|
-
"@aztec/foundation": "3.0.0-nightly.
|
|
81
|
-
"@aztec/l1-artifacts": "3.0.0-nightly.
|
|
82
|
-
"@aztec/node-keystore": "3.0.0-nightly.
|
|
83
|
-
"@aztec/node-lib": "3.0.0-nightly.
|
|
84
|
-
"@aztec/p2p": "3.0.0-nightly.
|
|
85
|
-
"@aztec/protocol-contracts": "3.0.0-nightly.
|
|
86
|
-
"@aztec/stdlib": "3.0.0-nightly.
|
|
87
|
-
"@aztec/test-wallet": "3.0.0-nightly.
|
|
88
|
-
"@aztec/world-state": "3.0.0-nightly.
|
|
74
|
+
"@aztec/accounts": "3.0.0-nightly.20251119",
|
|
75
|
+
"@aztec/archiver": "3.0.0-nightly.20251119",
|
|
76
|
+
"@aztec/aztec.js": "3.0.0-nightly.20251119",
|
|
77
|
+
"@aztec/constants": "3.0.0-nightly.20251119",
|
|
78
|
+
"@aztec/entrypoints": "3.0.0-nightly.20251119",
|
|
79
|
+
"@aztec/ethereum": "3.0.0-nightly.20251119",
|
|
80
|
+
"@aztec/foundation": "3.0.0-nightly.20251119",
|
|
81
|
+
"@aztec/l1-artifacts": "3.0.0-nightly.20251119",
|
|
82
|
+
"@aztec/node-keystore": "3.0.0-nightly.20251119",
|
|
83
|
+
"@aztec/node-lib": "3.0.0-nightly.20251119",
|
|
84
|
+
"@aztec/p2p": "3.0.0-nightly.20251119",
|
|
85
|
+
"@aztec/protocol-contracts": "3.0.0-nightly.20251119",
|
|
86
|
+
"@aztec/stdlib": "3.0.0-nightly.20251119",
|
|
87
|
+
"@aztec/test-wallet": "3.0.0-nightly.20251119",
|
|
88
|
+
"@aztec/world-state": "3.0.0-nightly.20251119",
|
|
89
89
|
"@ethersproject/wallet": "^5.8.0",
|
|
90
90
|
"@iarna/toml": "^2.2.5",
|
|
91
91
|
"@libp2p/peer-id-factory": "^3.0.4",
|
|
@@ -99,9 +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.
|
|
103
|
-
"@aztec/kv-store": "3.0.0-nightly.
|
|
104
|
-
"@aztec/telemetry-client": "3.0.0-nightly.
|
|
102
|
+
"@aztec/aztec-node": "3.0.0-nightly.20251119",
|
|
103
|
+
"@aztec/kv-store": "3.0.0-nightly.20251119",
|
|
104
|
+
"@aztec/telemetry-client": "3.0.0-nightly.20251119",
|
|
105
105
|
"@jest/globals": "^30.0.0",
|
|
106
106
|
"@types/jest": "^30.0.0",
|
|
107
107
|
"@types/lodash.chunk": "^4.2.9",
|
|
@@ -117,20 +117,21 @@
|
|
|
117
117
|
"typescript": "^5.3.3"
|
|
118
118
|
},
|
|
119
119
|
"peerDependencies": {
|
|
120
|
-
"@aztec/accounts": "3.0.0-nightly.
|
|
121
|
-
"@aztec/bb-prover": "3.0.0-nightly.
|
|
122
|
-
"@aztec/ethereum": "3.0.0-nightly.
|
|
123
|
-
"@aztec/l1-artifacts": "3.0.0-nightly.
|
|
124
|
-
"@aztec/noir-contracts.js": "3.0.0-nightly.
|
|
125
|
-
"@aztec/noir-protocol-circuits-types": "3.0.0-nightly.
|
|
126
|
-
"@aztec/noir-test-contracts.js": "3.0.0-nightly.
|
|
127
|
-
"@aztec/protocol-contracts": "3.0.0-nightly.
|
|
128
|
-
"@aztec/stdlib": "3.0.0-nightly.
|
|
120
|
+
"@aztec/accounts": "3.0.0-nightly.20251119",
|
|
121
|
+
"@aztec/bb-prover": "3.0.0-nightly.20251119",
|
|
122
|
+
"@aztec/ethereum": "3.0.0-nightly.20251119",
|
|
123
|
+
"@aztec/l1-artifacts": "3.0.0-nightly.20251119",
|
|
124
|
+
"@aztec/noir-contracts.js": "3.0.0-nightly.20251119",
|
|
125
|
+
"@aztec/noir-protocol-circuits-types": "3.0.0-nightly.20251119",
|
|
126
|
+
"@aztec/noir-test-contracts.js": "3.0.0-nightly.20251119",
|
|
127
|
+
"@aztec/protocol-contracts": "3.0.0-nightly.20251119",
|
|
128
|
+
"@aztec/stdlib": "3.0.0-nightly.20251119"
|
|
129
129
|
},
|
|
130
130
|
"files": [
|
|
131
131
|
"dest",
|
|
132
132
|
"src",
|
|
133
|
-
"!*.test.*"
|
|
133
|
+
"!*.test.*",
|
|
134
|
+
"public_include_metric_prefixes.json"
|
|
134
135
|
],
|
|
135
136
|
"types": "./dest/index.d.ts",
|
|
136
137
|
"engines": {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
["aztec.validator", "aztec.tx_collector", "aztec.mempool", "aztec.p2p.gossip.agg_", "aztec.ivc_verifier.agg_"]
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
buildValidatorEntries,
|
|
13
13
|
logValidatorSummaries,
|
|
14
14
|
maybePrintJson,
|
|
15
|
+
validateBlsPathOptions,
|
|
15
16
|
writeBlsBn254ToFile,
|
|
16
17
|
writeEthJsonV3ToFile,
|
|
17
18
|
writeKeystoreFile,
|
|
@@ -20,6 +21,9 @@ import {
|
|
|
20
21
|
export type AddValidatorKeysOptions = NewValidatorKeystoreOptions;
|
|
21
22
|
|
|
22
23
|
export async function addValidatorKeys(existing: string, options: AddValidatorKeysOptions, log: LogFn) {
|
|
24
|
+
// validate bls-path inputs before proceeding with key generation
|
|
25
|
+
validateBlsPathOptions(options);
|
|
26
|
+
|
|
23
27
|
const {
|
|
24
28
|
dataDir,
|
|
25
29
|
file,
|
|
@@ -36,7 +40,7 @@ export async function addValidatorKeys(existing: string, options: AddValidatorKe
|
|
|
36
40
|
fundingAccount: fundingAccountOpt,
|
|
37
41
|
remoteSigner: remoteSignerOpt,
|
|
38
42
|
password,
|
|
39
|
-
|
|
43
|
+
encryptedKeystoreDir,
|
|
40
44
|
} = options;
|
|
41
45
|
|
|
42
46
|
const validatorCount = typeof count === 'number' && Number.isFinite(count) && count > 0 ? Math.floor(count) : 1;
|
|
@@ -84,10 +88,16 @@ export async function addValidatorKeys(existing: string, options: AddValidatorKe
|
|
|
84
88
|
|
|
85
89
|
// If password provided, write ETH JSON V3 and BLS BN254 keystores and replace plaintext
|
|
86
90
|
if (password !== undefined) {
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
let targetDir: string;
|
|
92
|
+
if (encryptedKeystoreDir && encryptedKeystoreDir.length > 0) {
|
|
93
|
+
targetDir = encryptedKeystoreDir;
|
|
94
|
+
} else if (dataDir && dataDir.length > 0) {
|
|
95
|
+
targetDir = dataDir;
|
|
96
|
+
} else {
|
|
97
|
+
targetDir = dirname(existing);
|
|
98
|
+
}
|
|
89
99
|
await writeEthJsonV3ToFile(keystore.validators, { outDir: targetDir, password });
|
|
90
|
-
await writeBlsBn254ToFile(keystore.validators, { outDir: targetDir, password });
|
|
100
|
+
await writeBlsBn254ToFile(keystore.validators, { outDir: targetDir, password, blsPath });
|
|
91
101
|
}
|
|
92
102
|
|
|
93
103
|
let outputPath = existing;
|
|
@@ -3,7 +3,7 @@ import type { LogFn } from '@aztec/foundation/log';
|
|
|
3
3
|
|
|
4
4
|
import { writeFile } from 'fs/promises';
|
|
5
5
|
|
|
6
|
-
import { computeBlsPublicKeyCompressed, withValidatorIndex } from './shared.js';
|
|
6
|
+
import { computeBlsPublicKeyCompressed, defaultBlsPath, withValidatorIndex } from './shared.js';
|
|
7
7
|
|
|
8
8
|
export type GenerateBlsKeypairOptions = {
|
|
9
9
|
mnemonic?: string;
|
|
@@ -17,7 +17,7 @@ export type GenerateBlsKeypairOptions = {
|
|
|
17
17
|
|
|
18
18
|
export async function generateBlsKeypair(options: GenerateBlsKeypairOptions, log: LogFn) {
|
|
19
19
|
const { mnemonic, ikm, blsPath, compressed = true, json, out } = options;
|
|
20
|
-
const path = withValidatorIndex(blsPath ??
|
|
20
|
+
const path = withValidatorIndex(blsPath ?? defaultBlsPath, 0);
|
|
21
21
|
const priv = deriveBlsPrivateKey(mnemonic, ikm, path);
|
|
22
22
|
const pub = await computeBlsPublicKeyCompressed(priv);
|
|
23
23
|
const result = { path, privateKey: priv, publicKey: pub, format: compressed ? 'compressed' : 'uncompressed' };
|
|
@@ -3,6 +3,7 @@ import type { LogFn } from '@aztec/foundation/log';
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
|
|
5
5
|
import { parseAztecAddress, parseEthereumAddress, parseHex, parseOptionalInteger } from '../../utils/commands.js';
|
|
6
|
+
import { defaultBlsPath } from './shared.js';
|
|
6
7
|
|
|
7
8
|
export function injectCommands(program: Command, log: LogFn) {
|
|
8
9
|
const group = program
|
|
@@ -22,20 +23,20 @@ export function injectCommands(program: Command, log: LogFn) {
|
|
|
22
23
|
)
|
|
23
24
|
.option('--mnemonic <mnemonic>', 'Mnemonic for ETH/BLS derivation')
|
|
24
25
|
.option('--passphrase <str>', 'Optional passphrase for mnemonic')
|
|
25
|
-
.option('--account-index <N>', 'Base account index for ETH derivation', parseOptionalInteger)
|
|
26
|
-
.option('--address-index <N>', 'Base address index for ETH derivation', parseOptionalInteger)
|
|
26
|
+
.option('--account-index <N>', 'Base account index for ETH/BLS derivation', parseOptionalInteger)
|
|
27
|
+
.option('--address-index <N>', 'Base address index for ETH/BLS derivation', parseOptionalInteger)
|
|
27
28
|
.option('--coinbase <address>', 'Coinbase ETH address to use when proposing', parseEthereumAddress)
|
|
28
29
|
.option('--funding-account <address>', 'ETH account to fund publishers', parseEthereumAddress)
|
|
29
30
|
.option('--remote-signer <url>', 'Default remote signer URL for accounts in this file')
|
|
30
31
|
.option('--ikm <hex>', 'Initial keying material for BLS (alternative to mnemonic)', value => parseHex(value, 32))
|
|
31
|
-
.option('--bls-path <path>',
|
|
32
|
+
.option('--bls-path <path>', `EIP-2334 path (default ${defaultBlsPath})`)
|
|
32
33
|
.option(
|
|
33
34
|
'--password <str>',
|
|
34
35
|
'Password for writing keystore files (ETH JSON V3 and BLS EIP-2335). Empty string allowed',
|
|
35
36
|
)
|
|
36
|
-
.option('--
|
|
37
|
+
.option('--encrypted-keystore-dir <dir>', 'Output directory for encrypted keystore file(s)')
|
|
37
38
|
.option('--json', 'Echo resulting JSON to stdout')
|
|
38
|
-
.option('--staker-output', 'Generate staker output JSON
|
|
39
|
+
.option('--staker-output', 'Generate a single staker output JSON file with an array of validator entries')
|
|
39
40
|
.option('--gse-address <address>', 'GSE contract address (required with --staker-output)', parseEthereumAddress)
|
|
40
41
|
.option('--l1-rpc-urls <urls>', 'L1 RPC URLs (comma-separated, required with --staker-output)', value =>
|
|
41
42
|
value.split(','),
|
|
@@ -49,6 +50,7 @@ export function injectCommands(program: Command, log: LogFn) {
|
|
|
49
50
|
.requiredOption('--fee-recipient <address>', 'Aztec address that will receive fees', parseAztecAddress)
|
|
50
51
|
.action(async options => {
|
|
51
52
|
const { newValidatorKeystore } = await import('./new.js');
|
|
53
|
+
|
|
52
54
|
await newValidatorKeystore(options, log);
|
|
53
55
|
});
|
|
54
56
|
|
|
@@ -57,27 +59,27 @@ export function injectCommands(program: Command, log: LogFn) {
|
|
|
57
59
|
.summary('Augment an existing validator keystore JSON')
|
|
58
60
|
.description('Adds attester/publisher/BLS entries to an existing keystore using the same flags as new')
|
|
59
61
|
.argument('<existing>', 'Path to existing keystore JSON')
|
|
60
|
-
.option('--data-dir <path>', 'Directory where keystore(s) live')
|
|
61
|
-
.option('--file <name>', 'Override output file name')
|
|
62
|
-
.option('--count <N>', 'Number of validators to add', parseOptionalInteger)
|
|
62
|
+
.option('--data-dir <path>', 'Directory where keystore(s) live. (default: ~/.aztec/keystore)')
|
|
63
|
+
.option('--file <name>', 'Override output file name. (default: key<N>.json)')
|
|
64
|
+
.option('--count <N>', 'Number of validators to add. (default: 1)', parseOptionalInteger)
|
|
63
65
|
.option('--publisher-count <N>', 'Number of publisher accounts per validator (default 1)', value =>
|
|
64
66
|
parseOptionalInteger(value, 0),
|
|
65
67
|
)
|
|
66
68
|
.option('--mnemonic <mnemonic>', 'Mnemonic for ETH/BLS derivation')
|
|
67
69
|
.option('--passphrase <str>', 'Optional passphrase for mnemonic')
|
|
68
|
-
.option('--account-index <N>', 'Base account index for ETH derivation', parseOptionalInteger)
|
|
69
|
-
.option('--address-index <N>', 'Base address index for ETH derivation', parseOptionalInteger)
|
|
70
|
+
.option('--account-index <N>', 'Base account index for ETH/BLS derivation', parseOptionalInteger)
|
|
71
|
+
.option('--address-index <N>', 'Base address index for ETH/BLS derivation', parseOptionalInteger)
|
|
70
72
|
.option('--coinbase <address>', 'Coinbase ETH address to use when proposing', parseEthereumAddress)
|
|
71
73
|
.option('--funding-account <address>', 'ETH account to fund publishers', parseEthereumAddress)
|
|
72
74
|
.option('--remote-signer <url>', 'Default remote signer URL for accounts in this file')
|
|
73
75
|
.option('--ikm <hex>', 'Initial keying material for BLS (alternative to mnemonic)', value => parseHex(value, 32))
|
|
74
|
-
.option('--bls-path <path>',
|
|
76
|
+
.option('--bls-path <path>', `EIP-2334 path (default ${defaultBlsPath})`)
|
|
75
77
|
.option('--empty', 'Generate an empty skeleton without keys')
|
|
76
78
|
.option(
|
|
77
79
|
'--password <str>',
|
|
78
80
|
'Password for writing keystore files (ETH JSON V3 and BLS EIP-2335). Empty string allowed',
|
|
79
81
|
)
|
|
80
|
-
.option('--
|
|
82
|
+
.option('--encrypted-keystore-dir <dir>', 'Output directory for encrypted keystore file(s)')
|
|
81
83
|
.option('--json', 'Echo resulting JSON to stdout')
|
|
82
84
|
.requiredOption('--fee-recipient <address>', 'Aztec address that will receive fees', parseAztecAddress)
|
|
83
85
|
.action(async (existing: string, options) => {
|
|
@@ -110,7 +112,7 @@ export function injectCommands(program: Command, log: LogFn) {
|
|
|
110
112
|
.description('Generate a BLS keypair with convenience flags')
|
|
111
113
|
.option('--mnemonic <mnemonic>', 'Mnemonic for BLS derivation')
|
|
112
114
|
.option('--ikm <hex>', 'Initial keying material for BLS (alternative to mnemonic)', value => parseHex(value, 32))
|
|
113
|
-
.option('--bls-path <path>',
|
|
115
|
+
.option('--bls-path <path>', `EIP-2334 path (default ${defaultBlsPath})`)
|
|
114
116
|
.option('--g2', 'Derive on G2 subgroup')
|
|
115
117
|
.option('--compressed', 'Output compressed public key')
|
|
116
118
|
.option('--json', 'Print JSON output to stdout')
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
logValidatorSummaries,
|
|
16
16
|
maybePrintJson,
|
|
17
17
|
resolveKeystoreOutputPath,
|
|
18
|
+
validateBlsPathOptions,
|
|
18
19
|
writeBlsBn254ToFile,
|
|
19
20
|
writeEthJsonV3ToFile,
|
|
20
21
|
writeKeystoreFile,
|
|
@@ -34,7 +35,7 @@ export type NewValidatorKeystoreOptions = {
|
|
|
34
35
|
ikm?: string;
|
|
35
36
|
blsPath?: string;
|
|
36
37
|
password?: string;
|
|
37
|
-
|
|
38
|
+
encryptedKeystoreDir?: string;
|
|
38
39
|
json?: boolean;
|
|
39
40
|
feeRecipient: AztecAddress;
|
|
40
41
|
coinbase?: EthAddress;
|
|
@@ -47,6 +48,9 @@ export type NewValidatorKeystoreOptions = {
|
|
|
47
48
|
};
|
|
48
49
|
|
|
49
50
|
export async function newValidatorKeystore(options: NewValidatorKeystoreOptions, log: LogFn) {
|
|
51
|
+
// validate bls-path inputs before proceeding with key generation
|
|
52
|
+
validateBlsPathOptions(options);
|
|
53
|
+
|
|
50
54
|
const {
|
|
51
55
|
dataDir,
|
|
52
56
|
file,
|
|
@@ -63,7 +67,7 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
|
|
|
63
67
|
ikm,
|
|
64
68
|
mnemonic: _mnemonic,
|
|
65
69
|
password,
|
|
66
|
-
|
|
70
|
+
encryptedKeystoreDir,
|
|
67
71
|
stakerOutput,
|
|
68
72
|
gseAddress,
|
|
69
73
|
l1RpcUrls,
|
|
@@ -101,6 +105,7 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
|
|
|
101
105
|
|
|
102
106
|
const validatorCount = typeof count === 'number' && Number.isFinite(count) && count > 0 ? Math.floor(count) : 1;
|
|
103
107
|
const { outputPath } = await resolveKeystoreOutputPath(dataDir, file);
|
|
108
|
+
const keystoreOutDir = dirname(outputPath);
|
|
104
109
|
|
|
105
110
|
const { validators, summaries } = await buildValidatorEntries({
|
|
106
111
|
validatorCount,
|
|
@@ -118,9 +123,10 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
|
|
|
118
123
|
|
|
119
124
|
// If password provided, write ETH JSON V3 and BLS BN254 keystores and replace plaintext
|
|
120
125
|
if (password !== undefined) {
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
await
|
|
126
|
+
const encryptedKeystoreOutDir =
|
|
127
|
+
encryptedKeystoreDir && encryptedKeystoreDir.length > 0 ? encryptedKeystoreDir : keystoreOutDir;
|
|
128
|
+
await writeEthJsonV3ToFile(validators, { outDir: encryptedKeystoreOutDir, password });
|
|
129
|
+
await writeBlsBn254ToFile(validators, { outDir: encryptedKeystoreOutDir, password });
|
|
124
130
|
}
|
|
125
131
|
|
|
126
132
|
const keystore = {
|
|
@@ -140,7 +146,6 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
|
|
|
140
146
|
});
|
|
141
147
|
const gse = new GSEContract(publicClient, gseAddress);
|
|
142
148
|
|
|
143
|
-
const keystoreOutDir = outDir && outDir.length > 0 ? outDir : dirname(outputPath);
|
|
144
149
|
// Extract keystore base name without extension for unique staker output filenames
|
|
145
150
|
const keystoreBaseName = basename(outputPath, '.json');
|
|
146
151
|
|
|
@@ -149,17 +154,17 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
|
|
|
149
154
|
const validator = validators[i];
|
|
150
155
|
const outputs = await processAttesterAccounts(validator.attester, gse, password);
|
|
151
156
|
|
|
152
|
-
//
|
|
157
|
+
// Collect all staker outputs
|
|
153
158
|
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
159
|
allStakerOutputs.push(outputs[j]);
|
|
161
160
|
}
|
|
162
161
|
}
|
|
162
|
+
|
|
163
|
+
// Write a single JSON file with all staker outputs
|
|
164
|
+
if (allStakerOutputs.length > 0) {
|
|
165
|
+
const stakerOutputPath = join(keystoreOutDir, `${keystoreBaseName}_staker_output.json`);
|
|
166
|
+
await writeFile(stakerOutputPath, prettyPrintJSON(allStakerOutputs), 'utf-8');
|
|
167
|
+
}
|
|
163
168
|
}
|
|
164
169
|
|
|
165
170
|
const outputData = !_mnemonic ? { ...keystore, generatedMnemonic: mnemonic } : keystore;
|
|
@@ -178,8 +183,9 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
|
|
|
178
183
|
} else {
|
|
179
184
|
log(`Wrote validator keystore to ${outputPath}`);
|
|
180
185
|
if (stakerOutput && allStakerOutputs.length > 0) {
|
|
181
|
-
const
|
|
182
|
-
|
|
186
|
+
const keystoreBaseName = basename(outputPath, '.json');
|
|
187
|
+
const stakerOutputPath = join(keystoreOutDir, `${keystoreBaseName}_staker_output.json`);
|
|
188
|
+
log(`Wrote staker output for ${allStakerOutputs.length} validator(s) to ${stakerOutputPath}`);
|
|
183
189
|
log('');
|
|
184
190
|
}
|
|
185
191
|
}
|
|
@@ -29,11 +29,46 @@ export type BuildValidatorsInput = {
|
|
|
29
29
|
fundingAccount?: EthAddress;
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
+
export const defaultBlsPath = 'm/12381/3600/0/0/0';
|
|
33
|
+
|
|
34
|
+
export function validateBlsPathOptions(options: {
|
|
35
|
+
count?: number;
|
|
36
|
+
publisherCount?: number;
|
|
37
|
+
accountIndex?: number;
|
|
38
|
+
addressIndex?: number;
|
|
39
|
+
blsPath?: string;
|
|
40
|
+
ikm?: string;
|
|
41
|
+
}) {
|
|
42
|
+
if (options.blsPath && options.blsPath !== defaultBlsPath) {
|
|
43
|
+
if (
|
|
44
|
+
(options.count && options.count !== 1) ||
|
|
45
|
+
(options.publisherCount && options.publisherCount > 0) ||
|
|
46
|
+
(options.accountIndex && options.accountIndex !== 0) ||
|
|
47
|
+
(options.addressIndex && options.addressIndex !== 0)
|
|
48
|
+
) {
|
|
49
|
+
throw new Error('--bls-path cannot be used with --count, --publisher-count, --account-index, or --address-index');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
32
54
|
export function withValidatorIndex(path: string, accountIndex: number = 0, addressIndex: number = 0) {
|
|
55
|
+
// NOTE: The legacy BLS CLI is to allow users who generated keys in 2.1.4 to be able to use the same command
|
|
56
|
+
// 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.
|
|
57
|
+
const useLegacyBlsCli = ['true', '1', 'yes', 'y'].includes(process.env.LEGACY_BLS_CLI ?? '');
|
|
58
|
+
|
|
59
|
+
const defaultBlsPathParts = defaultBlsPath.split('/');
|
|
60
|
+
|
|
33
61
|
const parts = path.split('/');
|
|
34
|
-
if (parts.length ==
|
|
35
|
-
|
|
36
|
-
|
|
62
|
+
if (parts.length == defaultBlsPathParts.length && parts.every((part, index) => part === defaultBlsPathParts[index])) {
|
|
63
|
+
if (useLegacyBlsCli) {
|
|
64
|
+
// In 2.1.4, we were using address-index in parts[3] and did NOT use account-index, check lines 32 & 84
|
|
65
|
+
// https://github.com/AztecProtocol/aztec-packages/blob/v2.1.4/yarn-project/cli/src/cmds/validator_keys/shared.ts
|
|
66
|
+
|
|
67
|
+
parts[3] = String(addressIndex);
|
|
68
|
+
} else {
|
|
69
|
+
parts[3] = String(accountIndex);
|
|
70
|
+
parts[5] = String(addressIndex);
|
|
71
|
+
}
|
|
37
72
|
return parts.join('/');
|
|
38
73
|
}
|
|
39
74
|
return path;
|
|
@@ -75,7 +110,6 @@ export async function buildValidatorEntries(input: BuildValidatorsInput) {
|
|
|
75
110
|
fundingAccount,
|
|
76
111
|
} = input;
|
|
77
112
|
|
|
78
|
-
const defaultBlsPath = 'm/12381/3600/0/0/0';
|
|
79
113
|
const summaries: ValidatorSummary[] = [];
|
|
80
114
|
|
|
81
115
|
const validators = await Promise.all(
|
|
@@ -222,7 +256,7 @@ export async function writeBn254BlsKeystore(
|
|
|
222
256
|
/** Replace plaintext BLS keys in validators with { path, password } pointing to BN254 keystore files. */
|
|
223
257
|
export async function writeBlsBn254ToFile(
|
|
224
258
|
validators: ValidatorKeyStore[],
|
|
225
|
-
options: { outDir: string; password: string },
|
|
259
|
+
options: { outDir: string; password: string; blsPath?: string },
|
|
226
260
|
): Promise<void> {
|
|
227
261
|
for (let i = 0; i < validators.length; i++) {
|
|
228
262
|
const v = validators[i];
|
|
@@ -238,7 +272,7 @@ export async function writeBlsBn254ToFile(
|
|
|
238
272
|
}
|
|
239
273
|
|
|
240
274
|
const pub = await computeBlsPublicKeyCompressed(blsKey);
|
|
241
|
-
const path =
|
|
275
|
+
const path = options.blsPath ?? defaultBlsPath;
|
|
242
276
|
const fileBase = `${String(i + 1)}_${pub.slice(2, 18)}`;
|
|
243
277
|
const keystorePath = await writeBn254BlsKeystore(options.outDir, fileBase, options.password, blsKey, pub, path);
|
|
244
278
|
|
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
|
|
18
18
|
import { Wallet } from '@ethersproject/wallet';
|
|
19
19
|
import { readFileSync, writeFileSync } from 'fs';
|
|
20
|
+
import { basename, dirname, join } from 'path';
|
|
20
21
|
import { createPublicClient, fallback, http } from 'viem';
|
|
21
22
|
import { privateKeyToAddress } from 'viem/accounts';
|
|
22
23
|
|
|
@@ -275,10 +276,16 @@ export async function generateStakerJson(options: StakerOptions, log: LogFn): Pr
|
|
|
275
276
|
});
|
|
276
277
|
const gse = new GSEContract(publicClient, gseAddress);
|
|
277
278
|
|
|
278
|
-
|
|
279
|
-
|
|
279
|
+
const keystoreBaseName = basename(from, '.json');
|
|
280
|
+
const outputDir = output ? output : dirname(from);
|
|
281
|
+
|
|
282
|
+
for (let i = 0; i < keystore.validators.length; i++) {
|
|
283
|
+
const validator = keystore.validators[i];
|
|
280
284
|
const outputs = await processAttesterAccounts(validator.attester, gse, password);
|
|
281
|
-
|
|
285
|
+
|
|
286
|
+
for (let j = 0; j < outputs.length; j++) {
|
|
287
|
+
allOutputs.push(outputs[j]);
|
|
288
|
+
}
|
|
282
289
|
}
|
|
283
290
|
|
|
284
291
|
if (allOutputs.length === 0) {
|
|
@@ -286,13 +293,8 @@ export async function generateStakerJson(options: StakerOptions, log: LogFn): Pr
|
|
|
286
293
|
return;
|
|
287
294
|
}
|
|
288
295
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
writeFileSync(output, jsonOutput, 'utf-8');
|
|
294
|
-
log(`Wrote staking data to ${output}`);
|
|
295
|
-
} else {
|
|
296
|
-
log(jsonOutput);
|
|
297
|
-
}
|
|
296
|
+
// Write a single JSON file with all staker outputs
|
|
297
|
+
const stakerOutputPath = join(outputDir, `${keystoreBaseName}_staker_output.json`);
|
|
298
|
+
writeFileSync(stakerOutputPath, prettyPrintJSON(allOutputs), 'utf-8');
|
|
299
|
+
log(`Wrote staker output for ${allOutputs.length} validator(s) to ${stakerOutputPath}`);
|
|
298
300
|
}
|