@aztec/node-keystore 2.0.3 → 2.1.0-rc.2
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 +11 -0
- package/dest/keystore_manager.d.ts.map +1 -1
- package/dest/keystore_manager.js +117 -33
- 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/signer.d.ts +8 -0
- package/dest/signer.d.ts.map +1 -1
- package/dest/signer.js +51 -0
- 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 +142 -44
- package/src/loader.ts +23 -3
- package/src/schemas.ts +22 -3
- package/src/signer.ts +76 -0
- 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/signer.d.ts
CHANGED
|
@@ -39,6 +39,14 @@ export declare class RemoteSigner implements EthSigner {
|
|
|
39
39
|
private readonly config;
|
|
40
40
|
private fetch;
|
|
41
41
|
constructor(address: EthAddress, config: EthRemoteSignerConfig, fetch?: typeof globalThis.fetch);
|
|
42
|
+
/**
|
|
43
|
+
* Validates that a web3signer is accessible and that the given addresses are available.
|
|
44
|
+
* @param remoteSignerUrl - The URL of the web3signer (can be string or EthRemoteSignerConfig)
|
|
45
|
+
* @param addresses - The addresses to check for availability
|
|
46
|
+
* @param fetch - Optional fetch implementation for testing
|
|
47
|
+
* @throws Error if the web3signer is not accessible or if any address is not available
|
|
48
|
+
*/
|
|
49
|
+
static validateAccess(remoteSignerUrl: EthRemoteSignerConfig, addresses: string[], fetch?: typeof globalThis.fetch): Promise<void>;
|
|
42
50
|
/**
|
|
43
51
|
* Sign a message using eth_sign via remote JSON-RPC.
|
|
44
52
|
*/
|
package/dest/signer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signer.d.ts","sourceRoot":"","sources":["../src/signer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,SAAS,EAAiC,MAAM,iCAAiC,CAAC;AAI3F,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EAKzB,MAAM,MAAM,CAAC;AAEd,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD;;GAEG;AACH,qBAAa,WAAY,SAAQ,KAAK;IAG3B,MAAM,EAAE,MAAM;IACd,GAAG,EAAE,MAAM;IACX,UAAU,CAAC,EAAE,MAAM;IACnB,SAAS,CAAC,EAAE,MAAM;gBAJzB,OAAO,EAAE,MAAM,EACR,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,UAAU,CAAC,EAAE,MAAM,YAAA,EACnB,SAAS,CAAC,EAAE,MAAM,YAAA;CAK5B;AAED;;GAEG;AACH,qBAAa,WAAY,YAAW,SAAS;IAG/B,OAAO,CAAC,UAAU;IAF9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;gBAErB,UAAU,EAAE,QAAQ;IAIxC,IAAI,OAAO,IAAI,UAAU,CAExB;IAED,WAAW,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IAIlD,aAAa,CAAC,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC;IAKjE,eAAe,CAAC,WAAW,EAAE,uBAAuB,GAAG,OAAO,CAAC,SAAS,CAAC;CAc1E;AAmBD;;GAEG;AACH,qBAAa,YAAa,YAAW,SAAS;aAE1B,OAAO,EAAE,UAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,KAAK;gBAFG,OAAO,EAAE,UAAU,EAClB,MAAM,EAAE,qBAAqB,EACtC,KAAK,GAAE,OAAO,UAAU,CAAC,KAAwB;IAG3D;;OAEG;IACG,WAAW,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IAIxD;;OAEG;IACG,aAAa,CAAC,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC;IAIvE,eAAe,CAAC,WAAW,EAAE,uBAAuB,GAAG,OAAO,CAAC,SAAS,CAAC;IAIzE;;OAEG;IACH;;OAEG;YACW,sBAAsB;IAcpC;;OAEG;YACW,+BAA+B;IAkB7C;;OAEG;YACW,iCAAiC;IA+C/C;;OAEG;YACW,kBAAkB;IAyChC;;OAEG;IACH,OAAO,CAAC,YAAY;IAOpB;;OAEG;IACH,OAAO,CAAC,UAAU;CAGnB"}
|
|
1
|
+
{"version":3,"file":"signer.d.ts","sourceRoot":"","sources":["../src/signer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,SAAS,EAAiC,MAAM,iCAAiC,CAAC;AAI3F,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EAKzB,MAAM,MAAM,CAAC;AAEd,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD;;GAEG;AACH,qBAAa,WAAY,SAAQ,KAAK;IAG3B,MAAM,EAAE,MAAM;IACd,GAAG,EAAE,MAAM;IACX,UAAU,CAAC,EAAE,MAAM;IACnB,SAAS,CAAC,EAAE,MAAM;gBAJzB,OAAO,EAAE,MAAM,EACR,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,UAAU,CAAC,EAAE,MAAM,YAAA,EACnB,SAAS,CAAC,EAAE,MAAM,YAAA;CAK5B;AAED;;GAEG;AACH,qBAAa,WAAY,YAAW,SAAS;IAG/B,OAAO,CAAC,UAAU;IAF9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;gBAErB,UAAU,EAAE,QAAQ;IAIxC,IAAI,OAAO,IAAI,UAAU,CAExB;IAED,WAAW,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IAIlD,aAAa,CAAC,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC;IAKjE,eAAe,CAAC,WAAW,EAAE,uBAAuB,GAAG,OAAO,CAAC,SAAS,CAAC;CAc1E;AAmBD;;GAEG;AACH,qBAAa,YAAa,YAAW,SAAS;aAE1B,OAAO,EAAE,UAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,KAAK;gBAFG,OAAO,EAAE,UAAU,EAClB,MAAM,EAAE,qBAAqB,EACtC,KAAK,GAAE,OAAO,UAAU,CAAC,KAAwB;IAG3D;;;;;;OAMG;WACU,cAAc,CACzB,eAAe,EAAE,qBAAqB,EACtC,SAAS,EAAE,MAAM,EAAE,EACnB,KAAK,GAAE,OAAO,UAAU,CAAC,KAAwB,GAChD,OAAO,CAAC,IAAI,CAAC;IAiEhB;;OAEG;IACG,WAAW,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC;IAIxD;;OAEG;IACG,aAAa,CAAC,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC;IAIvE,eAAe,CAAC,WAAW,EAAE,uBAAuB,GAAG,OAAO,CAAC,SAAS,CAAC;IAIzE;;OAEG;IACH;;OAEG;YACW,sBAAsB;IAcpC;;OAEG;YACW,+BAA+B;IAkB7C;;OAEG;YACW,iCAAiC;IA+C/C;;OAEG;YACW,kBAAkB;IAyChC;;OAEG;IACH,OAAO,CAAC,YAAY;IAOpB;;OAEG;IACH,OAAO,CAAC,UAAU;CAGnB"}
|
package/dest/signer.js
CHANGED
|
@@ -63,6 +63,57 @@ import { hashTypedData, keccak256, parseTransaction, serializeTransaction } from
|
|
|
63
63
|
this.fetch = fetch;
|
|
64
64
|
}
|
|
65
65
|
/**
|
|
66
|
+
* Validates that a web3signer is accessible and that the given addresses are available.
|
|
67
|
+
* @param remoteSignerUrl - The URL of the web3signer (can be string or EthRemoteSignerConfig)
|
|
68
|
+
* @param addresses - The addresses to check for availability
|
|
69
|
+
* @param fetch - Optional fetch implementation for testing
|
|
70
|
+
* @throws Error if the web3signer is not accessible or if any address is not available
|
|
71
|
+
*/ static async validateAccess(remoteSignerUrl, addresses, fetch = globalThis.fetch) {
|
|
72
|
+
const url = typeof remoteSignerUrl === 'string' ? remoteSignerUrl : remoteSignerUrl.remoteSignerUrl;
|
|
73
|
+
try {
|
|
74
|
+
// Check if the web3signer is reachable by calling eth_accounts
|
|
75
|
+
const response = await fetch(url, {
|
|
76
|
+
method: 'POST',
|
|
77
|
+
headers: {
|
|
78
|
+
'Content-Type': 'application/json'
|
|
79
|
+
},
|
|
80
|
+
body: JSON.stringify({
|
|
81
|
+
jsonrpc: '2.0',
|
|
82
|
+
method: 'eth_accounts',
|
|
83
|
+
params: [],
|
|
84
|
+
id: 1
|
|
85
|
+
})
|
|
86
|
+
});
|
|
87
|
+
if (!response.ok) {
|
|
88
|
+
const errorText = await response.text();
|
|
89
|
+
throw new SignerError(`Web3Signer validation failed: ${response.status} ${response.statusText} - ${errorText}`, 'eth_accounts', url, response.status);
|
|
90
|
+
}
|
|
91
|
+
const result = await response.json();
|
|
92
|
+
if (result.error) {
|
|
93
|
+
throw new SignerError(`Web3Signer JSON-RPC error during validation: ${result.error.code} - ${result.error.message}`, 'eth_accounts', url, 200, result.error.code);
|
|
94
|
+
}
|
|
95
|
+
if (!result.result || !Array.isArray(result.result)) {
|
|
96
|
+
throw new Error('Invalid response from Web3Signer: expected array of accounts');
|
|
97
|
+
}
|
|
98
|
+
// Normalize addresses to lowercase for comparison
|
|
99
|
+
const availableAccounts = result.result.map((addr)=>addr.toLowerCase());
|
|
100
|
+
const requestedAddresses = addresses.map((addr)=>addr.toLowerCase());
|
|
101
|
+
// Check if all requested addresses are available
|
|
102
|
+
const missingAddresses = requestedAddresses.filter((addr)=>!availableAccounts.includes(addr));
|
|
103
|
+
if (missingAddresses.length > 0) {
|
|
104
|
+
throw new Error(`The following addresses are not available in the web3signer: ${missingAddresses.join(', ')}`);
|
|
105
|
+
}
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (error instanceof SignerError) {
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
if (error.code === 'ECONNREFUSED' || error.cause?.code === 'ECONNREFUSED') {
|
|
111
|
+
throw new Error(`Unable to connect to web3signer at ${url}. Please ensure it is running and accessible.`);
|
|
112
|
+
}
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
66
117
|
* Sign a message using eth_sign via remote JSON-RPC.
|
|
67
118
|
*/ async signMessage(message) {
|
|
68
119
|
return await this.makeJsonRpcSignRequest(message);
|
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.0.
|
|
3
|
+
"version": "2.1.0-rc.2",
|
|
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.0.
|
|
66
|
-
"@aztec/foundation": "2.0.
|
|
67
|
-
"@aztec/stdlib": "2.0.
|
|
65
|
+
"@aztec/ethereum": "2.1.0-rc.2",
|
|
66
|
+
"@aztec/foundation": "2.1.0-rc.2",
|
|
67
|
+
"@aztec/stdlib": "2.1.0-rc.2",
|
|
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';
|
|
@@ -58,6 +59,82 @@ export class KeystoreManager {
|
|
|
58
59
|
this.validateUniqueAttesterAddresses();
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Validates all remote signers in the keystore are accessible and have the required addresses.
|
|
64
|
+
* Should be called after construction if validation is needed.
|
|
65
|
+
*/
|
|
66
|
+
async validateSigners(): Promise<void> {
|
|
67
|
+
// Collect all remote signers with their addresses grouped by URL
|
|
68
|
+
const remoteSignersByUrl = new Map<string, Set<string>>();
|
|
69
|
+
|
|
70
|
+
// Helper to extract remote signer URL from config
|
|
71
|
+
const getUrl = (config: EthRemoteSignerConfig): string => {
|
|
72
|
+
return typeof config === 'string' ? config : config.remoteSignerUrl;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Helper to collect remote signers from accounts
|
|
76
|
+
const collectRemoteSigners = (accounts: EthAccounts, defaultRemoteSigner?: EthRemoteSignerConfig): void => {
|
|
77
|
+
const processAccount = (account: EthAccount): void => {
|
|
78
|
+
if (typeof account === 'object' && !('path' in account) && !('mnemonic' in (account as any))) {
|
|
79
|
+
// This is a remote signer account
|
|
80
|
+
const remoteSigner = account as EthRemoteSignerAccount;
|
|
81
|
+
const address = 'address' in remoteSigner ? remoteSigner.address : remoteSigner;
|
|
82
|
+
|
|
83
|
+
let url: string;
|
|
84
|
+
if ('remoteSignerUrl' in remoteSigner && remoteSigner.remoteSignerUrl) {
|
|
85
|
+
url = remoteSigner.remoteSignerUrl;
|
|
86
|
+
} else if (defaultRemoteSigner) {
|
|
87
|
+
url = getUrl(defaultRemoteSigner);
|
|
88
|
+
} else {
|
|
89
|
+
return; // No remote signer URL available
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!remoteSignersByUrl.has(url)) {
|
|
93
|
+
remoteSignersByUrl.set(url, new Set());
|
|
94
|
+
}
|
|
95
|
+
remoteSignersByUrl.get(url)!.add(address.toString());
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
if (Array.isArray(accounts)) {
|
|
100
|
+
accounts.forEach(account => collectRemoteSigners(account, defaultRemoteSigner));
|
|
101
|
+
} else if (typeof accounts === 'object' && 'mnemonic' in accounts) {
|
|
102
|
+
// Skip mnemonic configs
|
|
103
|
+
} else {
|
|
104
|
+
processAccount(accounts as EthAccount);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// Collect from validators
|
|
109
|
+
const validatorCount = this.getValidatorCount();
|
|
110
|
+
for (let i = 0; i < validatorCount; i++) {
|
|
111
|
+
const validator = this.getValidator(i);
|
|
112
|
+
const remoteSigner = validator.remoteSigner || this.keystore.remoteSigner;
|
|
113
|
+
|
|
114
|
+
collectRemoteSigners(this.extractEthAccountsFromAttester(validator.attester), remoteSigner);
|
|
115
|
+
if (validator.publisher) {
|
|
116
|
+
collectRemoteSigners(validator.publisher, remoteSigner);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Collect from slasher
|
|
121
|
+
if (this.keystore.slasher) {
|
|
122
|
+
collectRemoteSigners(this.keystore.slasher, this.keystore.remoteSigner);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Collect from prover
|
|
126
|
+
if (this.keystore.prover && typeof this.keystore.prover === 'object' && 'publisher' in this.keystore.prover) {
|
|
127
|
+
collectRemoteSigners(this.keystore.prover.publisher, this.keystore.remoteSigner);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Validate each remote signer URL with all its addresses
|
|
131
|
+
for (const [url, addresses] of remoteSignersByUrl.entries()) {
|
|
132
|
+
if (addresses.size > 0) {
|
|
133
|
+
await RemoteSigner.validateAccess(url, Array.from(addresses));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
61
138
|
/**
|
|
62
139
|
* Validates that attester addresses are unique across all validators
|
|
63
140
|
* Only checks simple private key attesters, not JSON-V3 or mnemonic attesters,
|
|
@@ -86,38 +163,38 @@ export class KeystoreManager {
|
|
|
86
163
|
* Best-effort address extraction that avoids decryption/derivation (no JSON-V3 or mnemonic processing).
|
|
87
164
|
* This is used at construction time to check for obvious duplicates without throwing for invalid inputs.
|
|
88
165
|
*/
|
|
89
|
-
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[] {
|
|
90
175
|
const results: EthAddress[] = [];
|
|
91
176
|
|
|
92
177
|
const handleAccount = (account: EthAccount): void => {
|
|
93
|
-
// String cases: private key or address or remote signer address
|
|
94
178
|
if (typeof account === 'string') {
|
|
95
179
|
if (account.startsWith('0x') && account.length === 66) {
|
|
96
|
-
// Private key -> derive address locally without external deps
|
|
97
180
|
try {
|
|
98
181
|
const signer = new LocalSigner(Buffer32.fromString(ethPrivateKeySchema.parse(account)));
|
|
99
182
|
results.push(signer.address);
|
|
100
183
|
} catch {
|
|
101
|
-
//
|
|
184
|
+
// ignore invalid private key at construction time
|
|
102
185
|
}
|
|
103
|
-
return;
|
|
104
186
|
}
|
|
105
|
-
|
|
106
|
-
// Any other string cannot be confidently resolved here
|
|
107
187
|
return;
|
|
108
188
|
}
|
|
109
189
|
|
|
110
|
-
// JSON V3 keystore: skip (requires decryption)
|
|
111
190
|
if ('path' in account) {
|
|
112
191
|
return;
|
|
113
192
|
}
|
|
114
193
|
|
|
115
|
-
// Mnemonic: skip (requires derivation and may throw on invalid mnemonics)
|
|
116
194
|
if ('mnemonic' in (account as any)) {
|
|
117
195
|
return;
|
|
118
196
|
}
|
|
119
197
|
|
|
120
|
-
// Remote signer account. If it contains 'address' then extract, otherwise it IS the address
|
|
121
198
|
const remoteSigner: EthRemoteSignerAccount = account;
|
|
122
199
|
if ('address' in remoteSigner) {
|
|
123
200
|
results.push(remoteSigner.address);
|
|
@@ -128,12 +205,15 @@ export class KeystoreManager {
|
|
|
128
205
|
|
|
129
206
|
if (Array.isArray(accounts)) {
|
|
130
207
|
for (const account of accounts) {
|
|
131
|
-
|
|
132
|
-
results.push(...subResults);
|
|
208
|
+
handleAccount(account as EthAccount);
|
|
133
209
|
}
|
|
134
210
|
return results;
|
|
135
211
|
}
|
|
136
212
|
|
|
213
|
+
if (typeof accounts === 'object' && accounts !== null && 'mnemonic' in (accounts as any)) {
|
|
214
|
+
return results;
|
|
215
|
+
}
|
|
216
|
+
|
|
137
217
|
handleAccount(accounts as EthAccount);
|
|
138
218
|
return results;
|
|
139
219
|
}
|
|
@@ -143,7 +223,8 @@ export class KeystoreManager {
|
|
|
143
223
|
*/
|
|
144
224
|
createAttesterSigners(validatorIndex: number): EthSigner[] {
|
|
145
225
|
const validator = this.getValidator(validatorIndex);
|
|
146
|
-
|
|
226
|
+
const ethAccounts = this.extractEthAccountsFromAttester(validator.attester);
|
|
227
|
+
return this.createSignersFromEthAccounts(ethAccounts, validator.remoteSigner || this.keystore.remoteSigner);
|
|
147
228
|
}
|
|
148
229
|
|
|
149
230
|
/**
|
|
@@ -210,7 +291,6 @@ export class KeystoreManager {
|
|
|
210
291
|
if ('id' in this.keystore.prover) {
|
|
211
292
|
const id = this.keystore.prover.id;
|
|
212
293
|
const signers = this.createSignersFromEthAccounts(this.keystore.prover.publisher, this.keystore.remoteSigner);
|
|
213
|
-
|
|
214
294
|
return { id, signers };
|
|
215
295
|
}
|
|
216
296
|
|
|
@@ -292,7 +372,7 @@ export class KeystoreManager {
|
|
|
292
372
|
for (let validatorIndex = 0; validatorIndex < validatorCount; validatorIndex++) {
|
|
293
373
|
const validator = this.getValidator(validatorIndex);
|
|
294
374
|
const signers = this.createSignersFromEthAccounts(
|
|
295
|
-
validator.attester,
|
|
375
|
+
this.extractEthAccountsFromAttester(validator.attester),
|
|
296
376
|
validator.remoteSigner || this.keystore.remoteSigner,
|
|
297
377
|
);
|
|
298
378
|
for (const signer of signers) {
|
|
@@ -390,7 +470,7 @@ export class KeystoreManager {
|
|
|
390
470
|
/**
|
|
391
471
|
* Create signer from JSON V3 keystore file or directory
|
|
392
472
|
*/
|
|
393
|
-
private createSignerFromJsonV3(config:
|
|
473
|
+
private createSignerFromJsonV3(config: JsonKeyFileV3Config): EthSigner[] {
|
|
394
474
|
try {
|
|
395
475
|
const stats = statSync(config.path);
|
|
396
476
|
|
|
@@ -470,7 +550,7 @@ export class KeystoreManager {
|
|
|
470
550
|
/**
|
|
471
551
|
* Create signers from mnemonic configuration using BIP44 derivation
|
|
472
552
|
*/
|
|
473
|
-
private createSignersFromMnemonic(config:
|
|
553
|
+
private createSignersFromMnemonic(config: MnemonicConfig): EthSigner[] {
|
|
474
554
|
const { mnemonic, addressIndex = 0, accountIndex = 0, addressCount = 1, accountCount = 1 } = config;
|
|
475
555
|
const signers: EthSigner[] = [];
|
|
476
556
|
|
|
@@ -601,38 +681,56 @@ export class KeystoreManager {
|
|
|
601
681
|
return validator.remoteSigner || this.keystore.remoteSigner;
|
|
602
682
|
};
|
|
603
683
|
|
|
604
|
-
//
|
|
605
|
-
const
|
|
684
|
+
// Normalize attester to EthAccounts and search
|
|
685
|
+
const normalized = this.extractEthAccountsFromAttester(validator.attester);
|
|
686
|
+
|
|
687
|
+
const findInEthAccounts = (accs: EthAccounts): EthRemoteSignerConfig | undefined => {
|
|
688
|
+
if (typeof accs === 'string') {
|
|
689
|
+
return checkAccount(accs);
|
|
690
|
+
}
|
|
691
|
+
if (Array.isArray(accs)) {
|
|
692
|
+
for (const a of accs as EthAccount[]) {
|
|
693
|
+
const res = checkAccount(a);
|
|
694
|
+
if (res !== undefined) {
|
|
695
|
+
return res;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
return undefined;
|
|
699
|
+
}
|
|
700
|
+
if (typeof accs === 'object' && accs !== null && 'mnemonic' in accs) {
|
|
701
|
+
// mnemonic-derived keys are local signers; no remote signer config
|
|
702
|
+
return undefined;
|
|
703
|
+
}
|
|
704
|
+
return checkAccount(accs as EthAccount);
|
|
705
|
+
};
|
|
706
|
+
|
|
707
|
+
return findInEthAccounts(normalized);
|
|
708
|
+
}
|
|
606
709
|
|
|
710
|
+
/** Extract ETH accounts from AttesterAccounts */
|
|
711
|
+
private extractEthAccountsFromAttester(attester: AttesterAccounts): EthAccounts {
|
|
607
712
|
if (typeof attester === 'string') {
|
|
608
|
-
|
|
609
|
-
return result === undefined ? undefined : result;
|
|
713
|
+
return attester;
|
|
610
714
|
}
|
|
611
|
-
|
|
612
715
|
if (Array.isArray(attester)) {
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
if (
|
|
616
|
-
|
|
716
|
+
const out: EthAccount[] = [];
|
|
717
|
+
for (const item of attester) {
|
|
718
|
+
if (typeof item === 'string') {
|
|
719
|
+
out.push(item);
|
|
720
|
+
} else if ('eth' in (item as any)) {
|
|
721
|
+
out.push((item as any).eth as EthAccount);
|
|
722
|
+
} else if (!('mnemonic' in (item as any))) {
|
|
723
|
+
out.push(item as EthAccount);
|
|
617
724
|
}
|
|
618
725
|
}
|
|
619
|
-
return
|
|
726
|
+
return out;
|
|
620
727
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
if ('mnemonic' in attester) {
|
|
624
|
-
try {
|
|
625
|
-
const signers = this.createSignersFromMnemonic(attester);
|
|
626
|
-
const matches = signers.some(s => s.address.equals(attesterAddress));
|
|
627
|
-
// Mnemonic-derived keys are local signers
|
|
628
|
-
return matches ? undefined : undefined;
|
|
629
|
-
} catch {
|
|
630
|
-
return undefined;
|
|
631
|
-
}
|
|
728
|
+
if ('mnemonic' in (attester as any)) {
|
|
729
|
+
return attester as any;
|
|
632
730
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
return
|
|
731
|
+
if ('eth' in (attester as any)) {
|
|
732
|
+
return (attester as any).eth as EthAccount;
|
|
733
|
+
}
|
|
734
|
+
return attester as any;
|
|
637
735
|
}
|
|
638
736
|
}
|
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,
|