@aztec/cli 3.0.0-nightly.20251118 → 3.0.0-nightly.20251120
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 +19 -3
- package/dest/cmds/validator_keys/generate_bls_keypair.d.ts.map +1 -1
- package/dest/cmds/validator_keys/generate_bls_keypair.js +2 -1
- 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 +2 -1
- package/dest/cmds/validator_keys/new.d.ts.map +1 -1
- package/dest/cmds/validator_keys/new.js +24 -26
- package/dest/cmds/validator_keys/shared.d.ts +2 -0
- package/dest/cmds/validator_keys/shared.d.ts.map +1 -1
- package/dest/cmds/validator_keys/shared.js +26 -8
- package/dest/cmds/validator_keys/staker.d.ts.map +1 -1
- package/dest/cmds/validator_keys/staker.js +12 -11
- package/dest/cmds/validator_keys/utils.d.ts +25 -0
- package/dest/cmds/validator_keys/utils.d.ts.map +1 -0
- package/dest/cmds/validator_keys/utils.js +52 -0
- 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 +18 -4
- package/package.json +30 -29
- package/public_include_metric_prefixes.json +1 -0
- package/src/cmds/validator_keys/add.ts +20 -4
- package/src/cmds/validator_keys/generate_bls_keypair.ts +2 -1
- package/src/cmds/validator_keys/index.ts +33 -17
- package/src/cmds/validator_keys/new.ts +35 -34
- package/src/cmds/validator_keys/shared.ts +27 -8
- package/src/cmds/validator_keys/staker.ts +14 -12
- package/src/cmds/validator_keys/utils.ts +80 -0
- package/src/config/network_config.ts +25 -4
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.20251120",
|
|
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.20251120",
|
|
75
|
+
"@aztec/archiver": "3.0.0-nightly.20251120",
|
|
76
|
+
"@aztec/aztec.js": "3.0.0-nightly.20251120",
|
|
77
|
+
"@aztec/constants": "3.0.0-nightly.20251120",
|
|
78
|
+
"@aztec/entrypoints": "3.0.0-nightly.20251120",
|
|
79
|
+
"@aztec/ethereum": "3.0.0-nightly.20251120",
|
|
80
|
+
"@aztec/foundation": "3.0.0-nightly.20251120",
|
|
81
|
+
"@aztec/l1-artifacts": "3.0.0-nightly.20251120",
|
|
82
|
+
"@aztec/node-keystore": "3.0.0-nightly.20251120",
|
|
83
|
+
"@aztec/node-lib": "3.0.0-nightly.20251120",
|
|
84
|
+
"@aztec/p2p": "3.0.0-nightly.20251120",
|
|
85
|
+
"@aztec/protocol-contracts": "3.0.0-nightly.20251120",
|
|
86
|
+
"@aztec/stdlib": "3.0.0-nightly.20251120",
|
|
87
|
+
"@aztec/test-wallet": "3.0.0-nightly.20251120",
|
|
88
|
+
"@aztec/world-state": "3.0.0-nightly.20251120",
|
|
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.20251120",
|
|
103
|
+
"@aztec/kv-store": "3.0.0-nightly.20251120",
|
|
104
|
+
"@aztec/telemetry-client": "3.0.0-nightly.20251120",
|
|
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.20251120",
|
|
121
|
+
"@aztec/bb-prover": "3.0.0-nightly.20251120",
|
|
122
|
+
"@aztec/ethereum": "3.0.0-nightly.20251120",
|
|
123
|
+
"@aztec/l1-artifacts": "3.0.0-nightly.20251120",
|
|
124
|
+
"@aztec/noir-contracts.js": "3.0.0-nightly.20251120",
|
|
125
|
+
"@aztec/noir-protocol-circuits-types": "3.0.0-nightly.20251120",
|
|
126
|
+
"@aztec/noir-test-contracts.js": "3.0.0-nightly.20251120",
|
|
127
|
+
"@aztec/protocol-contracts": "3.0.0-nightly.20251120",
|
|
128
|
+
"@aztec/stdlib": "3.0.0-nightly.20251120"
|
|
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_"]
|
|
@@ -16,15 +16,24 @@ import {
|
|
|
16
16
|
writeEthJsonV3ToFile,
|
|
17
17
|
writeKeystoreFile,
|
|
18
18
|
} from './shared.js';
|
|
19
|
+
import { validateBlsPathOptions, validatePublisherOptions, validateRemoteSignerOptions } from './utils.js';
|
|
19
20
|
|
|
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
|
+
// validate publisher options
|
|
27
|
+
validatePublisherOptions(options);
|
|
28
|
+
// validate remote signer options
|
|
29
|
+
validateRemoteSignerOptions(options);
|
|
30
|
+
|
|
23
31
|
const {
|
|
24
32
|
dataDir,
|
|
25
33
|
file,
|
|
26
34
|
count,
|
|
27
35
|
publisherCount = 0,
|
|
36
|
+
publishers,
|
|
28
37
|
mnemonic,
|
|
29
38
|
accountIndex = 0,
|
|
30
39
|
addressIndex,
|
|
@@ -36,7 +45,7 @@ export async function addValidatorKeys(existing: string, options: AddValidatorKe
|
|
|
36
45
|
fundingAccount: fundingAccountOpt,
|
|
37
46
|
remoteSigner: remoteSignerOpt,
|
|
38
47
|
password,
|
|
39
|
-
|
|
48
|
+
encryptedKeystoreDir,
|
|
40
49
|
} = options;
|
|
41
50
|
|
|
42
51
|
const validatorCount = typeof count === 'number' && Number.isFinite(count) && count > 0 ? Math.floor(count) : 1;
|
|
@@ -69,6 +78,7 @@ export async function addValidatorKeys(existing: string, options: AddValidatorKe
|
|
|
69
78
|
const { validators, summaries } = await buildValidatorEntries({
|
|
70
79
|
validatorCount,
|
|
71
80
|
publisherCount,
|
|
81
|
+
publishers,
|
|
72
82
|
accountIndex,
|
|
73
83
|
baseAddressIndex: effectiveBaseAddressIndex,
|
|
74
84
|
mnemonic: mnemonicToUse,
|
|
@@ -84,10 +94,16 @@ export async function addValidatorKeys(existing: string, options: AddValidatorKe
|
|
|
84
94
|
|
|
85
95
|
// If password provided, write ETH JSON V3 and BLS BN254 keystores and replace plaintext
|
|
86
96
|
if (password !== undefined) {
|
|
87
|
-
|
|
88
|
-
|
|
97
|
+
let targetDir: string;
|
|
98
|
+
if (encryptedKeystoreDir && encryptedKeystoreDir.length > 0) {
|
|
99
|
+
targetDir = encryptedKeystoreDir;
|
|
100
|
+
} else if (dataDir && dataDir.length > 0) {
|
|
101
|
+
targetDir = dataDir;
|
|
102
|
+
} else {
|
|
103
|
+
targetDir = dirname(existing);
|
|
104
|
+
}
|
|
89
105
|
await writeEthJsonV3ToFile(keystore.validators, { outDir: targetDir, password });
|
|
90
|
-
await writeBlsBn254ToFile(keystore.validators, { outDir: targetDir, password });
|
|
106
|
+
await writeBlsBn254ToFile(keystore.validators, { outDir: targetDir, password, blsPath });
|
|
91
107
|
}
|
|
92
108
|
|
|
93
109
|
let outputPath = existing;
|
|
@@ -4,6 +4,7 @@ import type { LogFn } from '@aztec/foundation/log';
|
|
|
4
4
|
import { writeFile } from 'fs/promises';
|
|
5
5
|
|
|
6
6
|
import { computeBlsPublicKeyCompressed, withValidatorIndex } from './shared.js';
|
|
7
|
+
import { defaultBlsPath } from './utils.js';
|
|
7
8
|
|
|
8
9
|
export type GenerateBlsKeypairOptions = {
|
|
9
10
|
mnemonic?: string;
|
|
@@ -17,7 +18,7 @@ export type GenerateBlsKeypairOptions = {
|
|
|
17
18
|
|
|
18
19
|
export async function generateBlsKeypair(options: GenerateBlsKeypairOptions, log: LogFn) {
|
|
19
20
|
const { mnemonic, ikm, blsPath, compressed = true, json, out } = options;
|
|
20
|
-
const path = withValidatorIndex(blsPath ??
|
|
21
|
+
const path = withValidatorIndex(blsPath ?? defaultBlsPath, 0);
|
|
21
22
|
const priv = deriveBlsPrivateKey(mnemonic, ikm, path);
|
|
22
23
|
const pub = await computeBlsPublicKeyCompressed(priv);
|
|
23
24
|
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 './utils.js';
|
|
6
7
|
|
|
7
8
|
export function injectCommands(program: Command, log: LogFn) {
|
|
8
9
|
const group = program
|
|
@@ -17,25 +18,32 @@ export function injectCommands(program: Command, log: LogFn) {
|
|
|
17
18
|
.option('--data-dir <path>', 'Directory to store keystore(s). Defaults to ~/.aztec/keystore')
|
|
18
19
|
.option('--file <name>', 'Keystore file name. Defaults to key1.json (or keyN.json if key1.json exists)')
|
|
19
20
|
.option('--count <N>', 'Number of validators to generate', parseOptionalInteger)
|
|
20
|
-
.option('--publisher-count <N>', 'Number of publisher accounts per validator (default
|
|
21
|
+
.option('--publisher-count <N>', 'Number of publisher accounts per validator (default 0)', value =>
|
|
21
22
|
parseOptionalInteger(value, 0),
|
|
22
23
|
)
|
|
24
|
+
.option('--publishers <privateKeys>', 'Comma-separated list of publisher private keys for all validators.', value =>
|
|
25
|
+
value.split(',').map((key: string) => key.trim()),
|
|
26
|
+
)
|
|
23
27
|
.option('--mnemonic <mnemonic>', 'Mnemonic for ETH/BLS derivation')
|
|
24
28
|
.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)
|
|
27
|
-
.option(
|
|
29
|
+
.option('--account-index <N>', 'Base account index for ETH/BLS derivation', parseOptionalInteger)
|
|
30
|
+
.option('--address-index <N>', 'Base address index for ETH/BLS derivation', parseOptionalInteger)
|
|
31
|
+
.option(
|
|
32
|
+
'--coinbase <address>',
|
|
33
|
+
'Coinbase ETH address to use when proposing. Defaults to attester address.',
|
|
34
|
+
parseEthereumAddress,
|
|
35
|
+
)
|
|
28
36
|
.option('--funding-account <address>', 'ETH account to fund publishers', parseEthereumAddress)
|
|
29
37
|
.option('--remote-signer <url>', 'Default remote signer URL for accounts in this file')
|
|
30
38
|
.option('--ikm <hex>', 'Initial keying material for BLS (alternative to mnemonic)', value => parseHex(value, 32))
|
|
31
|
-
.option('--bls-path <path>',
|
|
39
|
+
.option('--bls-path <path>', `EIP-2334 path (default ${defaultBlsPath})`)
|
|
32
40
|
.option(
|
|
33
41
|
'--password <str>',
|
|
34
42
|
'Password for writing keystore files (ETH JSON V3 and BLS EIP-2335). Empty string allowed',
|
|
35
43
|
)
|
|
36
|
-
.option('--
|
|
44
|
+
.option('--encrypted-keystore-dir <dir>', 'Output directory for encrypted keystore file(s)')
|
|
37
45
|
.option('--json', 'Echo resulting JSON to stdout')
|
|
38
|
-
.option('--staker-output', 'Generate staker output JSON
|
|
46
|
+
.option('--staker-output', 'Generate a single staker output JSON file with an array of validator entries')
|
|
39
47
|
.option('--gse-address <address>', 'GSE contract address (required with --staker-output)', parseEthereumAddress)
|
|
40
48
|
.option('--l1-rpc-urls <urls>', 'L1 RPC URLs (comma-separated, required with --staker-output)', value =>
|
|
41
49
|
value.split(','),
|
|
@@ -49,6 +57,7 @@ export function injectCommands(program: Command, log: LogFn) {
|
|
|
49
57
|
.requiredOption('--fee-recipient <address>', 'Aztec address that will receive fees', parseAztecAddress)
|
|
50
58
|
.action(async options => {
|
|
51
59
|
const { newValidatorKeystore } = await import('./new.js');
|
|
60
|
+
|
|
52
61
|
await newValidatorKeystore(options, log);
|
|
53
62
|
});
|
|
54
63
|
|
|
@@ -57,27 +66,34 @@ export function injectCommands(program: Command, log: LogFn) {
|
|
|
57
66
|
.summary('Augment an existing validator keystore JSON')
|
|
58
67
|
.description('Adds attester/publisher/BLS entries to an existing keystore using the same flags as new')
|
|
59
68
|
.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)
|
|
63
|
-
.option('--publisher-count <N>', 'Number of publisher accounts per validator (default
|
|
69
|
+
.option('--data-dir <path>', 'Directory where keystore(s) live. (default: ~/.aztec/keystore)')
|
|
70
|
+
.option('--file <name>', 'Override output file name. (default: key<N>.json)')
|
|
71
|
+
.option('--count <N>', 'Number of validators to add. (default: 1)', parseOptionalInteger)
|
|
72
|
+
.option('--publisher-count <N>', 'Number of publisher accounts per validator (default 0)', value =>
|
|
64
73
|
parseOptionalInteger(value, 0),
|
|
65
74
|
)
|
|
75
|
+
.option('--publishers <privateKeys>', 'Comma-separated list of publisher private keys for all validators.', value =>
|
|
76
|
+
value.split(',').map((key: string) => key.trim()),
|
|
77
|
+
)
|
|
66
78
|
.option('--mnemonic <mnemonic>', 'Mnemonic for ETH/BLS derivation')
|
|
67
79
|
.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(
|
|
80
|
+
.option('--account-index <N>', 'Base account index for ETH/BLS derivation', parseOptionalInteger)
|
|
81
|
+
.option('--address-index <N>', 'Base address index for ETH/BLS derivation', parseOptionalInteger)
|
|
82
|
+
.option(
|
|
83
|
+
'--coinbase <address>',
|
|
84
|
+
'Coinbase ETH address to use when proposing. Defaults to attester address.',
|
|
85
|
+
parseEthereumAddress,
|
|
86
|
+
)
|
|
71
87
|
.option('--funding-account <address>', 'ETH account to fund publishers', parseEthereumAddress)
|
|
72
88
|
.option('--remote-signer <url>', 'Default remote signer URL for accounts in this file')
|
|
73
89
|
.option('--ikm <hex>', 'Initial keying material for BLS (alternative to mnemonic)', value => parseHex(value, 32))
|
|
74
|
-
.option('--bls-path <path>',
|
|
90
|
+
.option('--bls-path <path>', `EIP-2334 path (default ${defaultBlsPath})`)
|
|
75
91
|
.option('--empty', 'Generate an empty skeleton without keys')
|
|
76
92
|
.option(
|
|
77
93
|
'--password <str>',
|
|
78
94
|
'Password for writing keystore files (ETH JSON V3 and BLS EIP-2335). Empty string allowed',
|
|
79
95
|
)
|
|
80
|
-
.option('--
|
|
96
|
+
.option('--encrypted-keystore-dir <dir>', 'Output directory for encrypted keystore file(s)')
|
|
81
97
|
.option('--json', 'Echo resulting JSON to stdout')
|
|
82
98
|
.requiredOption('--fee-recipient <address>', 'Aztec address that will receive fees', parseAztecAddress)
|
|
83
99
|
.action(async (existing: string, options) => {
|
|
@@ -110,7 +126,7 @@ export function injectCommands(program: Command, log: LogFn) {
|
|
|
110
126
|
.description('Generate a BLS keypair with convenience flags')
|
|
111
127
|
.option('--mnemonic <mnemonic>', 'Mnemonic for BLS derivation')
|
|
112
128
|
.option('--ikm <hex>', 'Initial keying material for BLS (alternative to mnemonic)', value => parseHex(value, 32))
|
|
113
|
-
.option('--bls-path <path>',
|
|
129
|
+
.option('--bls-path <path>', `EIP-2334 path (default ${defaultBlsPath})`)
|
|
114
130
|
.option('--g2', 'Derive on G2 subgroup')
|
|
115
131
|
.option('--compressed', 'Output compressed public key')
|
|
116
132
|
.option('--json', 'Print JSON output to stdout')
|
|
@@ -20,12 +20,19 @@ import {
|
|
|
20
20
|
writeKeystoreFile,
|
|
21
21
|
} from './shared.js';
|
|
22
22
|
import { processAttesterAccounts } from './staker.js';
|
|
23
|
+
import {
|
|
24
|
+
validateBlsPathOptions,
|
|
25
|
+
validatePublisherOptions,
|
|
26
|
+
validateRemoteSignerOptions,
|
|
27
|
+
validateStakerOutputOptions,
|
|
28
|
+
} from './utils.js';
|
|
23
29
|
|
|
24
30
|
export type NewValidatorKeystoreOptions = {
|
|
25
31
|
dataDir?: string;
|
|
26
32
|
file?: string;
|
|
27
33
|
count?: number;
|
|
28
34
|
publisherCount?: number;
|
|
35
|
+
publishers?: string[];
|
|
29
36
|
mnemonic?: string;
|
|
30
37
|
passphrase?: string;
|
|
31
38
|
accountIndex?: number;
|
|
@@ -34,7 +41,7 @@ export type NewValidatorKeystoreOptions = {
|
|
|
34
41
|
ikm?: string;
|
|
35
42
|
blsPath?: string;
|
|
36
43
|
password?: string;
|
|
37
|
-
|
|
44
|
+
encryptedKeystoreDir?: string;
|
|
38
45
|
json?: boolean;
|
|
39
46
|
feeRecipient: AztecAddress;
|
|
40
47
|
coinbase?: EthAddress;
|
|
@@ -47,11 +54,21 @@ export type NewValidatorKeystoreOptions = {
|
|
|
47
54
|
};
|
|
48
55
|
|
|
49
56
|
export async function newValidatorKeystore(options: NewValidatorKeystoreOptions, log: LogFn) {
|
|
57
|
+
// validate bls-path inputs before proceeding with key generation
|
|
58
|
+
validateBlsPathOptions(options);
|
|
59
|
+
// validate staker output options before proceeding with key generation
|
|
60
|
+
validateStakerOutputOptions(options);
|
|
61
|
+
// validate publisher options
|
|
62
|
+
validatePublisherOptions(options);
|
|
63
|
+
// validate remote signer options
|
|
64
|
+
validateRemoteSignerOptions(options);
|
|
65
|
+
|
|
50
66
|
const {
|
|
51
67
|
dataDir,
|
|
52
68
|
file,
|
|
53
69
|
count,
|
|
54
70
|
publisherCount = 0,
|
|
71
|
+
publishers,
|
|
55
72
|
json,
|
|
56
73
|
coinbase,
|
|
57
74
|
accountIndex = 0,
|
|
@@ -63,32 +80,13 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
|
|
|
63
80
|
ikm,
|
|
64
81
|
mnemonic: _mnemonic,
|
|
65
82
|
password,
|
|
66
|
-
|
|
83
|
+
encryptedKeystoreDir,
|
|
67
84
|
stakerOutput,
|
|
68
85
|
gseAddress,
|
|
69
86
|
l1RpcUrls,
|
|
70
87
|
l1ChainId,
|
|
71
88
|
} = options;
|
|
72
89
|
|
|
73
|
-
// Validate staker output requirements
|
|
74
|
-
if (stakerOutput) {
|
|
75
|
-
if (!gseAddress) {
|
|
76
|
-
throw new Error('--gse-address is required when using --staker-output');
|
|
77
|
-
}
|
|
78
|
-
if (!l1RpcUrls || l1RpcUrls.length === 0) {
|
|
79
|
-
throw new Error('--l1-rpc-urls is required when using --staker-output');
|
|
80
|
-
}
|
|
81
|
-
if (l1ChainId === undefined) {
|
|
82
|
-
throw new Error('--l1-chain-id is required when using --staker-output');
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (remoteSigner && !_mnemonic) {
|
|
87
|
-
throw new Error(
|
|
88
|
-
'Using --remote-signer requires a deterministic key source. Provide --mnemonic to derive keys, or omit --remote-signer to write new private keys to keystore.',
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
90
|
const mnemonic = _mnemonic ?? generateMnemonic(wordlist);
|
|
93
91
|
|
|
94
92
|
if (!_mnemonic && !json) {
|
|
@@ -101,10 +99,12 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
|
|
|
101
99
|
|
|
102
100
|
const validatorCount = typeof count === 'number' && Number.isFinite(count) && count > 0 ? Math.floor(count) : 1;
|
|
103
101
|
const { outputPath } = await resolveKeystoreOutputPath(dataDir, file);
|
|
102
|
+
const keystoreOutDir = dirname(outputPath);
|
|
104
103
|
|
|
105
104
|
const { validators, summaries } = await buildValidatorEntries({
|
|
106
105
|
validatorCount,
|
|
107
106
|
publisherCount,
|
|
107
|
+
publishers,
|
|
108
108
|
accountIndex,
|
|
109
109
|
baseAddressIndex: addressIndex,
|
|
110
110
|
mnemonic,
|
|
@@ -118,9 +118,10 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
|
|
|
118
118
|
|
|
119
119
|
// If password provided, write ETH JSON V3 and BLS BN254 keystores and replace plaintext
|
|
120
120
|
if (password !== undefined) {
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
await
|
|
121
|
+
const encryptedKeystoreOutDir =
|
|
122
|
+
encryptedKeystoreDir && encryptedKeystoreDir.length > 0 ? encryptedKeystoreDir : keystoreOutDir;
|
|
123
|
+
await writeEthJsonV3ToFile(validators, { outDir: encryptedKeystoreOutDir, password });
|
|
124
|
+
await writeBlsBn254ToFile(validators, { outDir: encryptedKeystoreOutDir, password });
|
|
124
125
|
}
|
|
125
126
|
|
|
126
127
|
const keystore = {
|
|
@@ -140,7 +141,6 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
|
|
|
140
141
|
});
|
|
141
142
|
const gse = new GSEContract(publicClient, gseAddress);
|
|
142
143
|
|
|
143
|
-
const keystoreOutDir = outDir && outDir.length > 0 ? outDir : dirname(outputPath);
|
|
144
144
|
// Extract keystore base name without extension for unique staker output filenames
|
|
145
145
|
const keystoreBaseName = basename(outputPath, '.json');
|
|
146
146
|
|
|
@@ -149,17 +149,17 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
|
|
|
149
149
|
const validator = validators[i];
|
|
150
150
|
const outputs = await processAttesterAccounts(validator.attester, gse, password);
|
|
151
151
|
|
|
152
|
-
//
|
|
152
|
+
// Collect all staker outputs
|
|
153
153
|
for (let j = 0; j < outputs.length; j++) {
|
|
154
|
-
const attesterIndex = i + 1;
|
|
155
|
-
const stakerOutputPath = join(
|
|
156
|
-
keystoreOutDir,
|
|
157
|
-
`${keystoreBaseName}_attester${attesterIndex}_staker_output.json`,
|
|
158
|
-
);
|
|
159
|
-
await writeFile(stakerOutputPath, prettyPrintJSON(outputs[j]), 'utf-8');
|
|
160
154
|
allStakerOutputs.push(outputs[j]);
|
|
161
155
|
}
|
|
162
156
|
}
|
|
157
|
+
|
|
158
|
+
// Write a single JSON file with all staker outputs
|
|
159
|
+
if (allStakerOutputs.length > 0) {
|
|
160
|
+
const stakerOutputPath = join(keystoreOutDir, `${keystoreBaseName}_staker_output.json`);
|
|
161
|
+
await writeFile(stakerOutputPath, prettyPrintJSON(allStakerOutputs), 'utf-8');
|
|
162
|
+
}
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
const outputData = !_mnemonic ? { ...keystore, generatedMnemonic: mnemonic } : keystore;
|
|
@@ -178,8 +178,9 @@ export async function newValidatorKeystore(options: NewValidatorKeystoreOptions,
|
|
|
178
178
|
} else {
|
|
179
179
|
log(`Wrote validator keystore to ${outputPath}`);
|
|
180
180
|
if (stakerOutput && allStakerOutputs.length > 0) {
|
|
181
|
-
const
|
|
182
|
-
|
|
181
|
+
const keystoreBaseName = basename(outputPath, '.json');
|
|
182
|
+
const stakerOutputPath = join(keystoreOutDir, `${keystoreBaseName}_staker_output.json`);
|
|
183
|
+
log(`Wrote staker output for ${allStakerOutputs.length} validator(s) to ${stakerOutputPath}`);
|
|
183
184
|
log('');
|
|
184
185
|
}
|
|
185
186
|
}
|
|
@@ -13,11 +13,14 @@ import { homedir } from 'os';
|
|
|
13
13
|
import { dirname, isAbsolute, join } from 'path';
|
|
14
14
|
import { mnemonicToAccount } from 'viem/accounts';
|
|
15
15
|
|
|
16
|
+
import { defaultBlsPath } from './utils.js';
|
|
17
|
+
|
|
16
18
|
export type ValidatorSummary = { attesterEth?: string; attesterBls?: string; publisherEth?: string[] };
|
|
17
19
|
|
|
18
20
|
export type BuildValidatorsInput = {
|
|
19
21
|
validatorCount: number;
|
|
20
22
|
publisherCount?: number;
|
|
23
|
+
publishers?: string[];
|
|
21
24
|
accountIndex: number;
|
|
22
25
|
baseAddressIndex: number;
|
|
23
26
|
mnemonic: string;
|
|
@@ -30,10 +33,23 @@ export type BuildValidatorsInput = {
|
|
|
30
33
|
};
|
|
31
34
|
|
|
32
35
|
export function withValidatorIndex(path: string, accountIndex: number = 0, addressIndex: number = 0) {
|
|
36
|
+
// NOTE: The legacy BLS CLI is to allow users who generated keys in 2.1.4 to be able to use the same command
|
|
37
|
+
// 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.
|
|
38
|
+
const useLegacyBlsCli = ['true', '1', 'yes', 'y'].includes(process.env.LEGACY_BLS_CLI ?? '');
|
|
39
|
+
|
|
40
|
+
const defaultBlsPathParts = defaultBlsPath.split('/');
|
|
41
|
+
|
|
33
42
|
const parts = path.split('/');
|
|
34
|
-
if (parts.length ==
|
|
35
|
-
|
|
36
|
-
|
|
43
|
+
if (parts.length == defaultBlsPathParts.length && parts.every((part, index) => part === defaultBlsPathParts[index])) {
|
|
44
|
+
if (useLegacyBlsCli) {
|
|
45
|
+
// In 2.1.4, we were using address-index in parts[3] and did NOT use account-index, check lines 32 & 84
|
|
46
|
+
// https://github.com/AztecProtocol/aztec-packages/blob/v2.1.4/yarn-project/cli/src/cmds/validator_keys/shared.ts
|
|
47
|
+
|
|
48
|
+
parts[3] = String(addressIndex);
|
|
49
|
+
} else {
|
|
50
|
+
parts[3] = String(accountIndex);
|
|
51
|
+
parts[5] = String(addressIndex);
|
|
52
|
+
}
|
|
37
53
|
return parts.join('/');
|
|
38
54
|
}
|
|
39
55
|
return path;
|
|
@@ -64,6 +80,7 @@ export async function buildValidatorEntries(input: BuildValidatorsInput) {
|
|
|
64
80
|
const {
|
|
65
81
|
validatorCount,
|
|
66
82
|
publisherCount = 0,
|
|
83
|
+
publishers,
|
|
67
84
|
accountIndex,
|
|
68
85
|
baseAddressIndex,
|
|
69
86
|
mnemonic,
|
|
@@ -75,7 +92,6 @@ export async function buildValidatorEntries(input: BuildValidatorsInput) {
|
|
|
75
92
|
fundingAccount,
|
|
76
93
|
} = input;
|
|
77
94
|
|
|
78
|
-
const defaultBlsPath = 'm/12381/3600/0/0/0';
|
|
79
95
|
const summaries: ValidatorSummary[] = [];
|
|
80
96
|
|
|
81
97
|
const validators = await Promise.all(
|
|
@@ -92,7 +108,10 @@ export async function buildValidatorEntries(input: BuildValidatorsInput) {
|
|
|
92
108
|
|
|
93
109
|
let publisherField: EthAccount | EthPrivateKey | (EthAccount | EthPrivateKey)[] | undefined;
|
|
94
110
|
const publisherAddresses: string[] = [];
|
|
95
|
-
if (
|
|
111
|
+
if (publishers && publishers.length > 0) {
|
|
112
|
+
publisherAddresses.push(...publishers);
|
|
113
|
+
publisherField = publishers.length === 1 ? (publishers[0] as EthPrivateKey) : (publishers as EthPrivateKey[]);
|
|
114
|
+
} else if (publisherCount > 0) {
|
|
96
115
|
const publishersBaseIndex = baseAddressIndex + validatorCount + i * publisherCount;
|
|
97
116
|
const publisherAccounts = Array.from({ length: publisherCount }, (_unused2, j) => {
|
|
98
117
|
const publisherAddressIndex = publishersBaseIndex + j;
|
|
@@ -123,7 +142,7 @@ export async function buildValidatorEntries(input: BuildValidatorsInput) {
|
|
|
123
142
|
attester,
|
|
124
143
|
...(publisherField !== undefined ? { publisher: publisherField } : {}),
|
|
125
144
|
feeRecipient,
|
|
126
|
-
coinbase,
|
|
145
|
+
coinbase: coinbase ?? attesterEthAddress,
|
|
127
146
|
fundingAccount,
|
|
128
147
|
} as ValidatorKeyStore;
|
|
129
148
|
}),
|
|
@@ -222,7 +241,7 @@ export async function writeBn254BlsKeystore(
|
|
|
222
241
|
/** Replace plaintext BLS keys in validators with { path, password } pointing to BN254 keystore files. */
|
|
223
242
|
export async function writeBlsBn254ToFile(
|
|
224
243
|
validators: ValidatorKeyStore[],
|
|
225
|
-
options: { outDir: string; password: string },
|
|
244
|
+
options: { outDir: string; password: string; blsPath?: string },
|
|
226
245
|
): Promise<void> {
|
|
227
246
|
for (let i = 0; i < validators.length; i++) {
|
|
228
247
|
const v = validators[i];
|
|
@@ -238,7 +257,7 @@ export async function writeBlsBn254ToFile(
|
|
|
238
257
|
}
|
|
239
258
|
|
|
240
259
|
const pub = await computeBlsPublicKeyCompressed(blsKey);
|
|
241
|
-
const path =
|
|
260
|
+
const path = options.blsPath ?? defaultBlsPath;
|
|
242
261
|
const fileBase = `${String(i + 1)}_${pub.slice(2, 18)}`;
|
|
243
262
|
const keystorePath = await writeBn254BlsKeystore(options.outDir, fileBase, options.password, blsKey, pub, path);
|
|
244
263
|
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
2
|
+
import { type EthPrivateKey, ethPrivateKeySchema } from '@aztec/node-keystore';
|
|
3
|
+
|
|
4
|
+
export const defaultBlsPath = 'm/12381/3600/0/0/0';
|
|
5
|
+
|
|
6
|
+
export function validateBlsPathOptions(options: {
|
|
7
|
+
count?: number;
|
|
8
|
+
publisherCount?: number;
|
|
9
|
+
accountIndex?: number;
|
|
10
|
+
addressIndex?: number;
|
|
11
|
+
blsPath?: string;
|
|
12
|
+
ikm?: string;
|
|
13
|
+
}) {
|
|
14
|
+
if (options.blsPath && options.blsPath !== defaultBlsPath) {
|
|
15
|
+
if (
|
|
16
|
+
(options.count && options.count !== 1) ||
|
|
17
|
+
(options.publisherCount && options.publisherCount > 0) ||
|
|
18
|
+
(options.accountIndex && options.accountIndex !== 0) ||
|
|
19
|
+
(options.addressIndex && options.addressIndex !== 0)
|
|
20
|
+
) {
|
|
21
|
+
throw new Error('--bls-path cannot be used with --count, --publisher-count, --account-index, or --address-index');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function validateStakerOutputOptions(options: {
|
|
27
|
+
stakerOutput?: boolean;
|
|
28
|
+
gseAddress?: EthAddress;
|
|
29
|
+
l1RpcUrls?: string[];
|
|
30
|
+
l1ChainId?: number;
|
|
31
|
+
}) {
|
|
32
|
+
if (!options.stakerOutput) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
// Required options for staker output
|
|
36
|
+
if (!options.gseAddress) {
|
|
37
|
+
throw new Error('--gse-address is required when using --staker-output');
|
|
38
|
+
}
|
|
39
|
+
if (!options.l1RpcUrls || options.l1RpcUrls.length === 0) {
|
|
40
|
+
throw new Error('--l1-rpc-urls is required when using --staker-output');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (options.l1ChainId === undefined) {
|
|
44
|
+
throw new Error('--l1-chain-id is required when using --staker-output');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function validateRemoteSignerOptions(options: { remoteSigner?: string; mnemonic?: string }) {
|
|
49
|
+
if (options.remoteSigner && !options.mnemonic) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
'Using --remote-signer requires a deterministic key source. Provide --mnemonic to derive keys, or omit --remote-signer to write new private keys to keystore.',
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function validatePublisherOptions(options: { publishers?: string[]; publisherCount?: number }) {
|
|
57
|
+
if (options.publisherCount && options.publisherCount > 0 && options.publishers && options.publishers.length > 0) {
|
|
58
|
+
throw new Error('--publishers and --publisher-count cannot be used together');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (options.publishers && options.publishers.length > 0) {
|
|
62
|
+
// Normalize each private key by adding 0x prefix if missing
|
|
63
|
+
const normalizedKeys: string[] = [];
|
|
64
|
+
for (const key of options.publishers) {
|
|
65
|
+
let privateKey = key.trim();
|
|
66
|
+
if (!privateKey.startsWith('0x')) {
|
|
67
|
+
privateKey = '0x' + privateKey;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
ethPrivateKeySchema.parse(privateKey);
|
|
72
|
+
normalizedKeys.push(privateKey);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
throw new Error(`Invalid publisher private key: ${error instanceof Error ? error.message : String(error)}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Update the options with the normalized keys
|
|
78
|
+
options.publishers = normalizedKeys as EthPrivateKey[];
|
|
79
|
+
}
|
|
80
|
+
}
|