@aztec/node-keystore 2.1.0-rc.1 → 2.1.0-rc.3
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/keystore_manager.d.ts +8 -2
- package/dest/keystore_manager.d.ts.map +1 -1
- package/dest/keystore_manager.js +58 -42
- package/dest/loader.js +22 -3
- package/dest/schemas.d.ts +391 -94
- package/dest/schemas.d.ts.map +1 -1
- package/dest/schemas.js +22 -2
- package/dest/types.d.ts +17 -6
- package/dest/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/keystore_manager.ts +71 -54
- package/src/loader.ts +23 -3
- package/src/schemas.ts +22 -3
- package/src/types.ts +18 -6
package/dest/schemas.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAE3D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAE3D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAG/D,eAAO,MAAM,mBAAmB,kDAGK,CAAC;AACtC,eAAO,MAAM,mBAAmB,kDAGK,CAAC;AAgFtC,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYvB,CAAC"}
|
package/dest/schemas.js
CHANGED
|
@@ -5,6 +5,7 @@ import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
// Use Aztec's validation functions but return string types to match our TypeScript interfaces
|
|
7
7
|
export const ethPrivateKeySchema = z.string().regex(/^0x[0-9a-fA-F]{64}$/, 'Invalid private key (must be 32 bytes with 0x prefix)').transform((s)=>s);
|
|
8
|
+
export const blsPrivateKeySchema = z.string().regex(/^0x[0-9a-fA-F]{64}$/, 'Invalid BLS private key (must be 32 bytes with 0x prefix)').transform((s)=>s);
|
|
8
9
|
const urlSchema = z.string().url('Invalid URL');
|
|
9
10
|
// Remote signer config schema
|
|
10
11
|
const remoteSignerConfigSchema = z.union([
|
|
@@ -20,7 +21,7 @@ const remoteSignerAccountSchema = z.union([
|
|
|
20
21
|
schemas.EthAddress,
|
|
21
22
|
z.object({
|
|
22
23
|
address: schemas.EthAddress,
|
|
23
|
-
remoteSignerUrl:
|
|
24
|
+
remoteSignerUrl: urlSchema,
|
|
24
25
|
certPath: optional(z.string()),
|
|
25
26
|
certPass: optional(z.string())
|
|
26
27
|
})
|
|
@@ -50,6 +51,25 @@ const ethAccountsSchema = z.union([
|
|
|
50
51
|
z.array(ethAccountSchema),
|
|
51
52
|
mnemonicConfigSchema
|
|
52
53
|
]);
|
|
54
|
+
// BLSAccount schema
|
|
55
|
+
const blsAccountSchema = z.union([
|
|
56
|
+
blsPrivateKeySchema,
|
|
57
|
+
jsonKeyFileV3Schema
|
|
58
|
+
]);
|
|
59
|
+
// AttesterAccount schema: either EthAccount or { eth: EthAccount, bls?: BLSAccount }
|
|
60
|
+
const attesterAccountSchema = z.union([
|
|
61
|
+
ethAccountSchema,
|
|
62
|
+
z.object({
|
|
63
|
+
eth: ethAccountSchema,
|
|
64
|
+
bls: optional(blsAccountSchema)
|
|
65
|
+
})
|
|
66
|
+
]);
|
|
67
|
+
// AttesterAccounts schema: AttesterAccount | AttesterAccount[] | MnemonicConfig
|
|
68
|
+
const attesterAccountsSchema = z.union([
|
|
69
|
+
attesterAccountSchema,
|
|
70
|
+
z.array(attesterAccountSchema),
|
|
71
|
+
mnemonicConfigSchema
|
|
72
|
+
]);
|
|
53
73
|
// Prover keystore schema
|
|
54
74
|
const proverKeyStoreSchema = z.union([
|
|
55
75
|
ethAccountSchema,
|
|
@@ -60,7 +80,7 @@ const proverKeyStoreSchema = z.union([
|
|
|
60
80
|
]);
|
|
61
81
|
// Validator keystore schema
|
|
62
82
|
const validatorKeyStoreSchema = z.object({
|
|
63
|
-
attester:
|
|
83
|
+
attester: attesterAccountsSchema,
|
|
64
84
|
coinbase: optional(schemas.EthAddress),
|
|
65
85
|
publisher: optional(ethAccountsSchema),
|
|
66
86
|
feeRecipient: AztecAddress.schema,
|
package/dest/types.d.ts
CHANGED
|
@@ -12,12 +12,14 @@ export type Hex<TByteLength extends number> = `0x${string}` & {
|
|
|
12
12
|
readonly _length: TByteLength;
|
|
13
13
|
};
|
|
14
14
|
/** A json keystore config points to a local file with the encrypted private key, and may require a password for decrypting it */
|
|
15
|
-
export type
|
|
15
|
+
export type JsonKeyFileV3Config = {
|
|
16
16
|
path: string;
|
|
17
17
|
password?: string;
|
|
18
18
|
};
|
|
19
19
|
/** A private key is a 32-byte 0x-prefixed hex */
|
|
20
20
|
export type EthPrivateKey = Hex<32>;
|
|
21
|
+
/** A BLS private key is a 32-byte 0x-prefixed hex */
|
|
22
|
+
export type BLSPrivateKey = Hex<32>;
|
|
21
23
|
/** URL type for remote signers */
|
|
22
24
|
export type Url = string;
|
|
23
25
|
/**
|
|
@@ -34,14 +36,14 @@ export type EthRemoteSignerConfig = Url | {
|
|
|
34
36
|
*/
|
|
35
37
|
export type EthRemoteSignerAccount = EthAddress | {
|
|
36
38
|
address: EthAddress;
|
|
37
|
-
remoteSignerUrl
|
|
39
|
+
remoteSignerUrl: Url;
|
|
38
40
|
certPath?: string;
|
|
39
41
|
certPass?: string;
|
|
40
42
|
};
|
|
41
43
|
/** An L1 account is a private key, a remote signer configuration, or a standard json key store file */
|
|
42
|
-
export type EthAccount = EthPrivateKey | EthRemoteSignerAccount |
|
|
44
|
+
export type EthAccount = EthPrivateKey | EthRemoteSignerAccount | JsonKeyFileV3Config;
|
|
43
45
|
/** A mnemonic can be used to define a set of accounts */
|
|
44
|
-
export type
|
|
46
|
+
export type MnemonicConfig = {
|
|
45
47
|
mnemonic: string;
|
|
46
48
|
addressIndex?: number;
|
|
47
49
|
accountIndex?: number;
|
|
@@ -49,7 +51,7 @@ export type EthMnemonicConfig = {
|
|
|
49
51
|
accountCount?: number;
|
|
50
52
|
};
|
|
51
53
|
/** One or more L1 accounts */
|
|
52
|
-
export type EthAccounts = EthAccount | EthAccount[] |
|
|
54
|
+
export type EthAccounts = EthAccount | EthAccount[] | MnemonicConfig;
|
|
53
55
|
export type ProverKeyStoreWithId = {
|
|
54
56
|
/** Address that identifies the prover. This address will receive the rewards. */
|
|
55
57
|
id: EthAddress;
|
|
@@ -57,12 +59,21 @@ export type ProverKeyStoreWithId = {
|
|
|
57
59
|
publisher: EthAccounts;
|
|
58
60
|
};
|
|
59
61
|
export type ProverKeyStore = ProverKeyStoreWithId | EthAccount;
|
|
62
|
+
/** A BLS account is either a private key, or a standard json key store file */
|
|
63
|
+
export type BLSAccount = BLSPrivateKey | JsonKeyFileV3Config;
|
|
64
|
+
/** An AttesterAccount is a combined EthAccount and optional BLSAccount */
|
|
65
|
+
export type AttesterAccount = {
|
|
66
|
+
eth: EthAccount;
|
|
67
|
+
bls?: BLSAccount;
|
|
68
|
+
} | EthAccount;
|
|
69
|
+
/** One or more attester accounts combining ETH and BLS keys */
|
|
70
|
+
export type AttesterAccounts = AttesterAccount | AttesterAccount[] | MnemonicConfig;
|
|
60
71
|
export type ValidatorKeyStore = {
|
|
61
72
|
/**
|
|
62
73
|
* One or more validator attester keys to handle in this configuration block.
|
|
63
74
|
* An attester address may only appear once across all configuration blocks across all keystore files.
|
|
64
75
|
*/
|
|
65
|
-
attester:
|
|
76
|
+
attester: AttesterAccounts;
|
|
66
77
|
/**
|
|
67
78
|
* Coinbase address to use when proposing an L2 block as any of the validators in this configuration block.
|
|
68
79
|
* Falls back to the attester address if not set.
|
package/dest/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEhE,8DAA8D;AAC9D,MAAM,MAAM,GAAG,CAAC,WAAW,SAAS,MAAM,IAAI,KAAK,MAAM,EAAE,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;CAAE,CAAC;AAEhG,iIAAiI;AACjI,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/node-keystore",
|
|
3
|
-
"version": "2.1.0-rc.
|
|
3
|
+
"version": "2.1.0-rc.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/index.js",
|
|
@@ -62,9 +62,9 @@
|
|
|
62
62
|
]
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@aztec/ethereum": "2.1.0-rc.
|
|
66
|
-
"@aztec/foundation": "2.1.0-rc.
|
|
67
|
-
"@aztec/stdlib": "2.1.0-rc.
|
|
65
|
+
"@aztec/ethereum": "2.1.0-rc.3",
|
|
66
|
+
"@aztec/foundation": "2.1.0-rc.3",
|
|
67
|
+
"@aztec/stdlib": "2.1.0-rc.3",
|
|
68
68
|
"@ethersproject/wallet": "^5.7.0",
|
|
69
69
|
"tslib": "^2.4.0",
|
|
70
70
|
"viem": "2.23.7",
|
package/src/keystore_manager.ts
CHANGED
|
@@ -18,13 +18,14 @@ import { mnemonicToAccount } from 'viem/accounts';
|
|
|
18
18
|
import { ethPrivateKeySchema } from './schemas.js';
|
|
19
19
|
import { LocalSigner, RemoteSigner } from './signer.js';
|
|
20
20
|
import type {
|
|
21
|
+
AttesterAccounts,
|
|
21
22
|
EthAccount,
|
|
22
23
|
EthAccounts,
|
|
23
|
-
EthJsonKeyFileV3Config,
|
|
24
|
-
EthMnemonicConfig,
|
|
25
24
|
EthRemoteSignerAccount,
|
|
26
25
|
EthRemoteSignerConfig,
|
|
26
|
+
JsonKeyFileV3Config,
|
|
27
27
|
KeyStore,
|
|
28
|
+
MnemonicConfig,
|
|
28
29
|
ProverKeyStore,
|
|
29
30
|
ValidatorKeyStore as ValidatorKeystoreConfig,
|
|
30
31
|
} from './types.js';
|
|
@@ -110,7 +111,7 @@ export class KeystoreManager {
|
|
|
110
111
|
const validator = this.getValidator(i);
|
|
111
112
|
const remoteSigner = validator.remoteSigner || this.keystore.remoteSigner;
|
|
112
113
|
|
|
113
|
-
collectRemoteSigners(validator.attester, remoteSigner);
|
|
114
|
+
collectRemoteSigners(this.extractEthAccountsFromAttester(validator.attester), remoteSigner);
|
|
114
115
|
if (validator.publisher) {
|
|
115
116
|
collectRemoteSigners(validator.publisher, remoteSigner);
|
|
116
117
|
}
|
|
@@ -162,38 +163,38 @@ export class KeystoreManager {
|
|
|
162
163
|
* Best-effort address extraction that avoids decryption/derivation (no JSON-V3 or mnemonic processing).
|
|
163
164
|
* This is used at construction time to check for obvious duplicates without throwing for invalid inputs.
|
|
164
165
|
*/
|
|
165
|
-
private extractAddressesWithoutSensitiveOperations(accounts:
|
|
166
|
+
private extractAddressesWithoutSensitiveOperations(accounts: AttesterAccounts): EthAddress[] {
|
|
167
|
+
const ethAccounts = this.extractEthAccountsFromAttester(accounts);
|
|
168
|
+
return this.extractAddressesFromEthAccountsNonSensitive(ethAccounts);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Extract addresses from EthAccounts without sensitive operations (no decryption/derivation).
|
|
173
|
+
*/
|
|
174
|
+
private extractAddressesFromEthAccountsNonSensitive(accounts: EthAccounts): EthAddress[] {
|
|
166
175
|
const results: EthAddress[] = [];
|
|
167
176
|
|
|
168
177
|
const handleAccount = (account: EthAccount): void => {
|
|
169
|
-
// String cases: private key or address or remote signer address
|
|
170
178
|
if (typeof account === 'string') {
|
|
171
179
|
if (account.startsWith('0x') && account.length === 66) {
|
|
172
|
-
// Private key -> derive address locally without external deps
|
|
173
180
|
try {
|
|
174
181
|
const signer = new LocalSigner(Buffer32.fromString(ethPrivateKeySchema.parse(account)));
|
|
175
182
|
results.push(signer.address);
|
|
176
183
|
} catch {
|
|
177
|
-
//
|
|
184
|
+
// ignore invalid private key at construction time
|
|
178
185
|
}
|
|
179
|
-
return;
|
|
180
186
|
}
|
|
181
|
-
|
|
182
|
-
// Any other string cannot be confidently resolved here
|
|
183
187
|
return;
|
|
184
188
|
}
|
|
185
189
|
|
|
186
|
-
// JSON V3 keystore: skip (requires decryption)
|
|
187
190
|
if ('path' in account) {
|
|
188
191
|
return;
|
|
189
192
|
}
|
|
190
193
|
|
|
191
|
-
// Mnemonic: skip (requires derivation and may throw on invalid mnemonics)
|
|
192
194
|
if ('mnemonic' in (account as any)) {
|
|
193
195
|
return;
|
|
194
196
|
}
|
|
195
197
|
|
|
196
|
-
// Remote signer account. If it contains 'address' then extract, otherwise it IS the address
|
|
197
198
|
const remoteSigner: EthRemoteSignerAccount = account;
|
|
198
199
|
if ('address' in remoteSigner) {
|
|
199
200
|
results.push(remoteSigner.address);
|
|
@@ -204,12 +205,15 @@ export class KeystoreManager {
|
|
|
204
205
|
|
|
205
206
|
if (Array.isArray(accounts)) {
|
|
206
207
|
for (const account of accounts) {
|
|
207
|
-
|
|
208
|
-
results.push(...subResults);
|
|
208
|
+
handleAccount(account as EthAccount);
|
|
209
209
|
}
|
|
210
210
|
return results;
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
+
if (typeof accounts === 'object' && accounts !== null && 'mnemonic' in (accounts as any)) {
|
|
214
|
+
return results;
|
|
215
|
+
}
|
|
216
|
+
|
|
213
217
|
handleAccount(accounts as EthAccount);
|
|
214
218
|
return results;
|
|
215
219
|
}
|
|
@@ -219,7 +223,8 @@ export class KeystoreManager {
|
|
|
219
223
|
*/
|
|
220
224
|
createAttesterSigners(validatorIndex: number): EthSigner[] {
|
|
221
225
|
const validator = this.getValidator(validatorIndex);
|
|
222
|
-
|
|
226
|
+
const ethAccounts = this.extractEthAccountsFromAttester(validator.attester);
|
|
227
|
+
return this.createSignersFromEthAccounts(ethAccounts, validator.remoteSigner || this.keystore.remoteSigner);
|
|
223
228
|
}
|
|
224
229
|
|
|
225
230
|
/**
|
|
@@ -286,7 +291,6 @@ export class KeystoreManager {
|
|
|
286
291
|
if ('id' in this.keystore.prover) {
|
|
287
292
|
const id = this.keystore.prover.id;
|
|
288
293
|
const signers = this.createSignersFromEthAccounts(this.keystore.prover.publisher, this.keystore.remoteSigner);
|
|
289
|
-
|
|
290
294
|
return { id, signers };
|
|
291
295
|
}
|
|
292
296
|
|
|
@@ -316,22 +320,17 @@ export class KeystoreManager {
|
|
|
316
320
|
}
|
|
317
321
|
|
|
318
322
|
/**
|
|
319
|
-
* Get coinbase address for validator (falls back to
|
|
323
|
+
* Get coinbase address for validator (falls back to the specific attester address)
|
|
320
324
|
*/
|
|
321
|
-
getCoinbaseAddress(validatorIndex: number): EthAddress {
|
|
325
|
+
getCoinbaseAddress(validatorIndex: number, attesterAddress: EthAddress): EthAddress {
|
|
322
326
|
const validator = this.getValidator(validatorIndex);
|
|
323
327
|
|
|
324
328
|
if (validator.coinbase) {
|
|
325
329
|
return validator.coinbase;
|
|
326
330
|
}
|
|
327
331
|
|
|
328
|
-
// Fall back to
|
|
329
|
-
|
|
330
|
-
if (attesterSigners.length === 0) {
|
|
331
|
-
throw new KeystoreError(`No attester signers found for validator ${validatorIndex}`);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
return attesterSigners[0].address;
|
|
332
|
+
// Fall back to the specific attester address
|
|
333
|
+
return attesterAddress;
|
|
335
334
|
}
|
|
336
335
|
|
|
337
336
|
/**
|
|
@@ -368,7 +367,7 @@ export class KeystoreManager {
|
|
|
368
367
|
for (let validatorIndex = 0; validatorIndex < validatorCount; validatorIndex++) {
|
|
369
368
|
const validator = this.getValidator(validatorIndex);
|
|
370
369
|
const signers = this.createSignersFromEthAccounts(
|
|
371
|
-
validator.attester,
|
|
370
|
+
this.extractEthAccountsFromAttester(validator.attester),
|
|
372
371
|
validator.remoteSigner || this.keystore.remoteSigner,
|
|
373
372
|
);
|
|
374
373
|
for (const signer of signers) {
|
|
@@ -466,7 +465,7 @@ export class KeystoreManager {
|
|
|
466
465
|
/**
|
|
467
466
|
* Create signer from JSON V3 keystore file or directory
|
|
468
467
|
*/
|
|
469
|
-
private createSignerFromJsonV3(config:
|
|
468
|
+
private createSignerFromJsonV3(config: JsonKeyFileV3Config): EthSigner[] {
|
|
470
469
|
try {
|
|
471
470
|
const stats = statSync(config.path);
|
|
472
471
|
|
|
@@ -546,7 +545,7 @@ export class KeystoreManager {
|
|
|
546
545
|
/**
|
|
547
546
|
* Create signers from mnemonic configuration using BIP44 derivation
|
|
548
547
|
*/
|
|
549
|
-
private createSignersFromMnemonic(config:
|
|
548
|
+
private createSignersFromMnemonic(config: MnemonicConfig): EthSigner[] {
|
|
550
549
|
const { mnemonic, addressIndex = 0, accountIndex = 0, addressCount = 1, accountCount = 1 } = config;
|
|
551
550
|
const signers: EthSigner[] = [];
|
|
552
551
|
|
|
@@ -677,38 +676,56 @@ export class KeystoreManager {
|
|
|
677
676
|
return validator.remoteSigner || this.keystore.remoteSigner;
|
|
678
677
|
};
|
|
679
678
|
|
|
680
|
-
//
|
|
681
|
-
const
|
|
679
|
+
// Normalize attester to EthAccounts and search
|
|
680
|
+
const normalized = this.extractEthAccountsFromAttester(validator.attester);
|
|
681
|
+
|
|
682
|
+
const findInEthAccounts = (accs: EthAccounts): EthRemoteSignerConfig | undefined => {
|
|
683
|
+
if (typeof accs === 'string') {
|
|
684
|
+
return checkAccount(accs);
|
|
685
|
+
}
|
|
686
|
+
if (Array.isArray(accs)) {
|
|
687
|
+
for (const a of accs as EthAccount[]) {
|
|
688
|
+
const res = checkAccount(a);
|
|
689
|
+
if (res !== undefined) {
|
|
690
|
+
return res;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
return undefined;
|
|
694
|
+
}
|
|
695
|
+
if (typeof accs === 'object' && accs !== null && 'mnemonic' in accs) {
|
|
696
|
+
// mnemonic-derived keys are local signers; no remote signer config
|
|
697
|
+
return undefined;
|
|
698
|
+
}
|
|
699
|
+
return checkAccount(accs as EthAccount);
|
|
700
|
+
};
|
|
701
|
+
|
|
702
|
+
return findInEthAccounts(normalized);
|
|
703
|
+
}
|
|
682
704
|
|
|
705
|
+
/** Extract ETH accounts from AttesterAccounts */
|
|
706
|
+
private extractEthAccountsFromAttester(attester: AttesterAccounts): EthAccounts {
|
|
683
707
|
if (typeof attester === 'string') {
|
|
684
|
-
|
|
685
|
-
return result === undefined ? undefined : result;
|
|
708
|
+
return attester;
|
|
686
709
|
}
|
|
687
|
-
|
|
688
710
|
if (Array.isArray(attester)) {
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
if (
|
|
692
|
-
|
|
711
|
+
const out: EthAccount[] = [];
|
|
712
|
+
for (const item of attester) {
|
|
713
|
+
if (typeof item === 'string') {
|
|
714
|
+
out.push(item);
|
|
715
|
+
} else if ('eth' in (item as any)) {
|
|
716
|
+
out.push((item as any).eth as EthAccount);
|
|
717
|
+
} else if (!('mnemonic' in (item as any))) {
|
|
718
|
+
out.push(item as EthAccount);
|
|
693
719
|
}
|
|
694
720
|
}
|
|
695
|
-
return
|
|
721
|
+
return out;
|
|
696
722
|
}
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
if ('mnemonic' in attester) {
|
|
700
|
-
try {
|
|
701
|
-
const signers = this.createSignersFromMnemonic(attester);
|
|
702
|
-
const matches = signers.some(s => s.address.equals(attesterAddress));
|
|
703
|
-
// Mnemonic-derived keys are local signers
|
|
704
|
-
return matches ? undefined : undefined;
|
|
705
|
-
} catch {
|
|
706
|
-
return undefined;
|
|
707
|
-
}
|
|
723
|
+
if ('mnemonic' in (attester as any)) {
|
|
724
|
+
return attester as any;
|
|
708
725
|
}
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
return
|
|
726
|
+
if ('eth' in (attester as any)) {
|
|
727
|
+
return (attester as any).eth as EthAccount;
|
|
728
|
+
}
|
|
729
|
+
return attester as any;
|
|
713
730
|
}
|
|
714
731
|
}
|
package/src/loader.ts
CHANGED
|
@@ -285,17 +285,37 @@ export function mergeKeystores(keystores: KeyStore[]): KeyStore {
|
|
|
285
285
|
* @returns Array of string keys used to detect duplicates.
|
|
286
286
|
*/
|
|
287
287
|
function extractAttesterKeys(attester: unknown): string[] {
|
|
288
|
+
// String forms (private key or other) - return as-is for coarse uniqueness
|
|
288
289
|
if (typeof attester === 'string') {
|
|
289
290
|
return [attester];
|
|
290
291
|
}
|
|
291
292
|
|
|
293
|
+
// Arrays of attester items
|
|
292
294
|
if (Array.isArray(attester)) {
|
|
293
|
-
|
|
295
|
+
const keys: string[] = [];
|
|
296
|
+
for (const item of attester) {
|
|
297
|
+
keys.push(...extractAttesterKeys(item));
|
|
298
|
+
}
|
|
299
|
+
return keys;
|
|
294
300
|
}
|
|
295
301
|
|
|
296
|
-
if (attester && typeof attester === 'object'
|
|
297
|
-
|
|
302
|
+
if (attester && typeof attester === 'object') {
|
|
303
|
+
const obj = attester as Record<string, unknown>;
|
|
304
|
+
|
|
305
|
+
// New shape: { eth: EthAccount, bls?: BLSAccount }
|
|
306
|
+
if ('eth' in obj) {
|
|
307
|
+
return extractAttesterKeys(obj.eth);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Remote signer account object shape: { address, remoteSignerUrl?, ... }
|
|
311
|
+
if ('address' in obj) {
|
|
312
|
+
return [String((obj as any).address)];
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Mnemonic or other object shapes: stringify
|
|
316
|
+
return [JSON.stringify(attester)];
|
|
298
317
|
}
|
|
299
318
|
|
|
319
|
+
// Fallback stringify for anything else (null/undefined)
|
|
300
320
|
return [JSON.stringify(attester)];
|
|
301
321
|
}
|
package/src/schemas.ts
CHANGED
|
@@ -6,13 +6,17 @@ import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
|
6
6
|
|
|
7
7
|
import { z } from 'zod';
|
|
8
8
|
|
|
9
|
-
import type { EthPrivateKey } from './types.js';
|
|
9
|
+
import type { BLSPrivateKey, EthPrivateKey } from './types.js';
|
|
10
10
|
|
|
11
11
|
// Use Aztec's validation functions but return string types to match our TypeScript interfaces
|
|
12
12
|
export const ethPrivateKeySchema = z
|
|
13
13
|
.string()
|
|
14
14
|
.regex(/^0x[0-9a-fA-F]{64}$/, 'Invalid private key (must be 32 bytes with 0x prefix)')
|
|
15
15
|
.transform(s => s as EthPrivateKey);
|
|
16
|
+
export const blsPrivateKeySchema = z
|
|
17
|
+
.string()
|
|
18
|
+
.regex(/^0x[0-9a-fA-F]{64}$/, 'Invalid BLS private key (must be 32 bytes with 0x prefix)')
|
|
19
|
+
.transform(s => s as BLSPrivateKey);
|
|
16
20
|
const urlSchema = z.string().url('Invalid URL');
|
|
17
21
|
|
|
18
22
|
// Remote signer config schema
|
|
@@ -30,7 +34,7 @@ const remoteSignerAccountSchema = z.union([
|
|
|
30
34
|
schemas.EthAddress,
|
|
31
35
|
z.object({
|
|
32
36
|
address: schemas.EthAddress,
|
|
33
|
-
remoteSignerUrl:
|
|
37
|
+
remoteSignerUrl: urlSchema,
|
|
34
38
|
certPath: optional(z.string()),
|
|
35
39
|
certPass: optional(z.string()),
|
|
36
40
|
}),
|
|
@@ -57,6 +61,21 @@ const ethAccountSchema = z.union([ethPrivateKeySchema, remoteSignerAccountSchema
|
|
|
57
61
|
// EthAccounts schema
|
|
58
62
|
const ethAccountsSchema = z.union([ethAccountSchema, z.array(ethAccountSchema), mnemonicConfigSchema]);
|
|
59
63
|
|
|
64
|
+
// BLSAccount schema
|
|
65
|
+
const blsAccountSchema = z.union([blsPrivateKeySchema, jsonKeyFileV3Schema]);
|
|
66
|
+
|
|
67
|
+
// AttesterAccount schema: either EthAccount or { eth: EthAccount, bls?: BLSAccount }
|
|
68
|
+
const attesterAccountSchema = z.union([
|
|
69
|
+
ethAccountSchema,
|
|
70
|
+
z.object({
|
|
71
|
+
eth: ethAccountSchema,
|
|
72
|
+
bls: optional(blsAccountSchema),
|
|
73
|
+
}),
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
// AttesterAccounts schema: AttesterAccount | AttesterAccount[] | MnemonicConfig
|
|
77
|
+
const attesterAccountsSchema = z.union([attesterAccountSchema, z.array(attesterAccountSchema), mnemonicConfigSchema]);
|
|
78
|
+
|
|
60
79
|
// Prover keystore schema
|
|
61
80
|
const proverKeyStoreSchema = z.union([
|
|
62
81
|
ethAccountSchema,
|
|
@@ -68,7 +87,7 @@ const proverKeyStoreSchema = z.union([
|
|
|
68
87
|
|
|
69
88
|
// Validator keystore schema
|
|
70
89
|
const validatorKeyStoreSchema = z.object({
|
|
71
|
-
attester:
|
|
90
|
+
attester: attesterAccountsSchema,
|
|
72
91
|
coinbase: optional(schemas.EthAddress),
|
|
73
92
|
publisher: optional(ethAccountsSchema),
|
|
74
93
|
feeRecipient: AztecAddress.schema,
|
package/src/types.ts
CHANGED
|
@@ -12,11 +12,14 @@ import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
|
12
12
|
export type Hex<TByteLength extends number> = `0x${string}` & { readonly _length: TByteLength };
|
|
13
13
|
|
|
14
14
|
/** A json keystore config points to a local file with the encrypted private key, and may require a password for decrypting it */
|
|
15
|
-
export type
|
|
15
|
+
export type JsonKeyFileV3Config = { path: string; password?: string };
|
|
16
16
|
|
|
17
17
|
/** A private key is a 32-byte 0x-prefixed hex */
|
|
18
18
|
export type EthPrivateKey = Hex<32>;
|
|
19
19
|
|
|
20
|
+
/** A BLS private key is a 32-byte 0x-prefixed hex */
|
|
21
|
+
export type BLSPrivateKey = Hex<32>;
|
|
22
|
+
|
|
20
23
|
/** URL type for remote signers */
|
|
21
24
|
export type Url = string;
|
|
22
25
|
|
|
@@ -39,16 +42,16 @@ export type EthRemoteSignerAccount =
|
|
|
39
42
|
| EthAddress
|
|
40
43
|
| {
|
|
41
44
|
address: EthAddress;
|
|
42
|
-
remoteSignerUrl
|
|
45
|
+
remoteSignerUrl: Url;
|
|
43
46
|
certPath?: string;
|
|
44
47
|
certPass?: string;
|
|
45
48
|
};
|
|
46
49
|
|
|
47
50
|
/** An L1 account is a private key, a remote signer configuration, or a standard json key store file */
|
|
48
|
-
export type EthAccount = EthPrivateKey | EthRemoteSignerAccount |
|
|
51
|
+
export type EthAccount = EthPrivateKey | EthRemoteSignerAccount | JsonKeyFileV3Config;
|
|
49
52
|
|
|
50
53
|
/** A mnemonic can be used to define a set of accounts */
|
|
51
|
-
export type
|
|
54
|
+
export type MnemonicConfig = {
|
|
52
55
|
mnemonic: string;
|
|
53
56
|
addressIndex?: number;
|
|
54
57
|
accountIndex?: number;
|
|
@@ -57,7 +60,7 @@ export type EthMnemonicConfig = {
|
|
|
57
60
|
};
|
|
58
61
|
|
|
59
62
|
/** One or more L1 accounts */
|
|
60
|
-
export type EthAccounts = EthAccount | EthAccount[] |
|
|
63
|
+
export type EthAccounts = EthAccount | EthAccount[] | MnemonicConfig;
|
|
61
64
|
|
|
62
65
|
export type ProverKeyStoreWithId = {
|
|
63
66
|
/** Address that identifies the prover. This address will receive the rewards. */
|
|
@@ -68,12 +71,21 @@ export type ProverKeyStoreWithId = {
|
|
|
68
71
|
|
|
69
72
|
export type ProverKeyStore = ProverKeyStoreWithId | EthAccount;
|
|
70
73
|
|
|
74
|
+
/** A BLS account is either a private key, or a standard json key store file */
|
|
75
|
+
export type BLSAccount = BLSPrivateKey | JsonKeyFileV3Config;
|
|
76
|
+
|
|
77
|
+
/** An AttesterAccount is a combined EthAccount and optional BLSAccount */
|
|
78
|
+
export type AttesterAccount = { eth: EthAccount; bls?: BLSAccount } | EthAccount;
|
|
79
|
+
|
|
80
|
+
/** One or more attester accounts combining ETH and BLS keys */
|
|
81
|
+
export type AttesterAccounts = AttesterAccount | AttesterAccount[] | MnemonicConfig;
|
|
82
|
+
|
|
71
83
|
export type ValidatorKeyStore = {
|
|
72
84
|
/**
|
|
73
85
|
* One or more validator attester keys to handle in this configuration block.
|
|
74
86
|
* An attester address may only appear once across all configuration blocks across all keystore files.
|
|
75
87
|
*/
|
|
76
|
-
attester:
|
|
88
|
+
attester: AttesterAccounts;
|
|
77
89
|
/**
|
|
78
90
|
* Coinbase address to use when proposing an L2 block as any of the validators in this configuration block.
|
|
79
91
|
* Falls back to the attester address if not set.
|