@aztec/node-keystore 3.0.0-canary.a9708bd → 3.0.0-devnet.2-patch.1
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/config.d.ts +1 -1
- package/dest/index.d.ts +1 -1
- package/dest/keystore_manager.d.ts +17 -5
- package/dest/keystore_manager.d.ts.map +1 -1
- package/dest/keystore_manager.js +179 -121
- package/dest/loader.d.ts +1 -1
- package/dest/loader.d.ts.map +1 -1
- package/dest/loader.js +42 -15
- package/dest/schemas.d.ts +976 -783
- package/dest/schemas.d.ts.map +1 -1
- package/dest/schemas.js +54 -34
- package/dest/signer.d.ts +10 -17
- package/dest/signer.d.ts.map +1 -1
- package/dest/signer.js +58 -6
- package/dest/types.d.ts +41 -23
- package/dest/types.d.ts.map +1 -1
- package/dest/types.js +1 -1
- package/package.json +8 -7
- package/src/keystore_manager.ts +216 -144
- package/src/loader.ts +38 -9
- package/src/schemas.ts +83 -53
- package/src/signer.ts +84 -11
- package/src/types.ts +45 -28
package/dest/config.d.ts
CHANGED
|
@@ -4,4 +4,4 @@ export type KeyStoreConfig = {
|
|
|
4
4
|
};
|
|
5
5
|
export declare const keyStoreConfigMappings: ConfigMappingsType<KeyStoreConfig>;
|
|
6
6
|
export declare function getKeyStoreConfigFromEnv(): KeyStoreConfig;
|
|
7
|
-
//# sourceMappingURL=
|
|
7
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvY29uZmlnLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLGtCQUFrQixFQUF5QixNQUFNLDBCQUEwQixDQUFDO0FBRTFGLE1BQU0sTUFBTSxjQUFjLEdBQUc7SUFDM0IsaUJBQWlCLEVBQUUsTUFBTSxHQUFHLFNBQVMsQ0FBQztDQUN2QyxDQUFDO0FBRUYsZUFBTyxNQUFNLHNCQUFzQixFQUFFLGtCQUFrQixDQUFDLGNBQWMsQ0FLckUsQ0FBQztBQUVGLHdCQUFnQix3QkFBd0IsSUFBSSxjQUFjLENBRXpEIn0=
|
package/dest/index.d.ts
CHANGED
|
@@ -4,4 +4,4 @@ export * from './schemas.js';
|
|
|
4
4
|
export * from './signer.js';
|
|
5
5
|
export * from './keystore_manager.js';
|
|
6
6
|
export * from './config.js';
|
|
7
|
-
//# sourceMappingURL=
|
|
7
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLFlBQVksQ0FBQztBQUMzQixjQUFjLGFBQWEsQ0FBQztBQUM1QixjQUFjLGNBQWMsQ0FBQztBQUM3QixjQUFjLGFBQWEsQ0FBQztBQUM1QixjQUFjLHVCQUF1QixDQUFDO0FBQ3RDLGNBQWMsYUFBYSxDQUFDIn0=
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Manages keystore configuration and delegates signing operations to appropriate signers.
|
|
5
5
|
*/
|
|
6
|
-
import type { EthSigner } from '@aztec/ethereum';
|
|
6
|
+
import type { EthSigner } from '@aztec/ethereum/eth-signer';
|
|
7
7
|
import { Buffer32 } from '@aztec/foundation/buffer';
|
|
8
8
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
9
9
|
import type { Signature } from '@aztec/foundation/eth-signature';
|
|
10
|
+
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
10
11
|
import type { TypedDataDefinition } from 'viem';
|
|
11
12
|
import type { EthAccounts, EthRemoteSignerConfig, KeyStore, ProverKeyStore, ValidatorKeyStore as ValidatorKeystoreConfig } from './types.js';
|
|
12
13
|
/**
|
|
@@ -27,6 +28,11 @@ export declare class KeystoreManager {
|
|
|
27
28
|
* @param keystore Parsed keystore configuration
|
|
28
29
|
*/
|
|
29
30
|
constructor(keystore: KeyStore);
|
|
31
|
+
/**
|
|
32
|
+
* Validates all remote signers in the keystore are accessible and have the required addresses.
|
|
33
|
+
* Should be called after construction if validation is needed.
|
|
34
|
+
*/
|
|
35
|
+
validateSigners(): Promise<void>;
|
|
30
36
|
/**
|
|
31
37
|
* Validates that attester addresses are unique across all validators
|
|
32
38
|
* Only checks simple private key attesters, not JSON-V3 or mnemonic attesters,
|
|
@@ -39,6 +45,10 @@ export declare class KeystoreManager {
|
|
|
39
45
|
* This is used at construction time to check for obvious duplicates without throwing for invalid inputs.
|
|
40
46
|
*/
|
|
41
47
|
private extractAddressesWithoutSensitiveOperations;
|
|
48
|
+
/**
|
|
49
|
+
* Extract addresses from EthAccounts without sensitive operations (no decryption/derivation).
|
|
50
|
+
*/
|
|
51
|
+
private extractAddressesFromEthAccountsNonSensitive;
|
|
42
52
|
/**
|
|
43
53
|
* Create signers for validator attester accounts
|
|
44
54
|
*/
|
|
@@ -68,13 +78,13 @@ export declare class KeystoreManager {
|
|
|
68
78
|
*/
|
|
69
79
|
getValidatorCount(): number;
|
|
70
80
|
/**
|
|
71
|
-
* Get coinbase address for validator (falls back to
|
|
81
|
+
* Get coinbase address for validator (falls back to the specific attester address)
|
|
72
82
|
*/
|
|
73
|
-
getCoinbaseAddress(validatorIndex: number): EthAddress;
|
|
83
|
+
getCoinbaseAddress(validatorIndex: number, attesterAddress: EthAddress): EthAddress;
|
|
74
84
|
/**
|
|
75
85
|
* Get fee recipient for validator
|
|
76
86
|
*/
|
|
77
|
-
getFeeRecipient(validatorIndex: number):
|
|
87
|
+
getFeeRecipient(validatorIndex: number): AztecAddress;
|
|
78
88
|
/**
|
|
79
89
|
* Get the raw slasher configuration as provided in the keystore file.
|
|
80
90
|
* @returns The slasher accounts configuration or undefined if not set
|
|
@@ -123,5 +133,7 @@ export declare class KeystoreManager {
|
|
|
123
133
|
* Precedence: account-level override > validator-level config > file-level default
|
|
124
134
|
*/
|
|
125
135
|
getEffectiveRemoteSignerConfig(validatorIndex: number, attesterAddress: EthAddress): EthRemoteSignerConfig | undefined;
|
|
136
|
+
/** Extract ETH accounts from AttesterAccounts */
|
|
137
|
+
private extractEthAccountsFromAttester;
|
|
126
138
|
}
|
|
127
|
-
//# sourceMappingURL=
|
|
139
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoia2V5c3RvcmVfbWFuYWdlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2tleXN0b3JlX21hbmFnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7R0FJRztBQUNILE9BQU8sS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQzVELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUNwRCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDM0QsT0FBTyxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDakUsT0FBTyxLQUFLLEVBQUUsWUFBWSxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFLaEUsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFLaEQsT0FBTyxLQUFLLEVBSVYsV0FBVyxFQUVYLHFCQUFxQixFQUNyQixRQUFRLEVBRVIsY0FBYyxFQUNkLGlCQUFpQixJQUFJLHVCQUF1QixFQUM3QyxNQUFNLFlBQVksQ0FBQztBQUVwQjs7R0FFRztBQUNILHFCQUFhLGFBQWMsU0FBUSxLQUFLO0lBR3BCLEtBQUssQ0FBQztJQUZ4QixZQUNFLE9BQU8sRUFBRSxNQUFNLEVBQ0MsS0FBSyxDQUFDLG1CQUFPLEVBSTlCO0NBQ0Y7QUFFRDs7R0FFRztBQUNILHFCQUFhLGVBQWU7SUFDMUIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQVc7SUFFcEM7Ozs7T0FJRztJQUNILFlBQVksUUFBUSxFQUFFLFFBQVEsRUFHN0I7SUFFRDs7O09BR0c7SUFDRyxlQUFlLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQXNFckM7SUFFRDs7Ozs7T0FLRztJQUNILE9BQU8sQ0FBQywrQkFBK0I7SUFrQnZDOzs7T0FHRztJQUNILE9BQU8sQ0FBQywwQ0FBMEM7SUFLbEQ7O09BRUc7SUFDSCxPQUFPLENBQUMsMkNBQTJDO0lBK0NuRDs7T0FFRztJQUNILHFCQUFxQixDQUFDLGNBQWMsRUFBRSxNQUFNLEdBQUcsU0FBUyxFQUFFLENBSXpEO0lBRUQ7O09BRUc7SUFDSCxzQkFBc0IsQ0FBQyxjQUFjLEVBQUUsTUFBTSxHQUFHLFNBQVMsRUFBRSxDQVkxRDtJQUVELGtDQUFrQyxJQUFJLFNBQVMsRUFBRSxDQVNoRDtJQUVEOztPQUVHO0lBQ0gsb0JBQW9CLElBQUksU0FBUyxFQUFFLENBTWxDO0lBRUQ7O09BRUc7SUFDSCxtQkFBbUIsSUFBSTtRQUFFLEVBQUUsRUFBRSxVQUFVLEdBQUcsU0FBUyxDQUFDO1FBQUMsT0FBTyxFQUFFLFNBQVMsRUFBRSxDQUFBO0tBQUUsR0FBRyxTQUFTLENBK0J0RjtJQUVEOztPQUVHO0lBQ0gsWUFBWSxDQUFDLEtBQUssRUFBRSxNQUFNLEdBQUcsdUJBQXVCLENBS25EO0lBRUQ7O09BRUc7SUFDSCxpQkFBaUIsSUFBSSxNQUFNLENBRTFCO0lBRUQ7O09BRUc7SUFDSCxrQkFBa0IsQ0FBQyxjQUFjLEVBQUUsTUFBTSxFQUFFLGVBQWUsRUFBRSxVQUFVLEdBQUcsVUFBVSxDQVNsRjtJQUVEOztPQUVHO0lBQ0gsZUFBZSxDQUFDLGNBQWMsRUFBRSxNQUFNLEdBQUcsWUFBWSxDQUdwRDtJQUVEOzs7T0FHRztJQUNILGtCQUFrQixJQUFJLFdBQVcsR0FBRyxTQUFTLENBRTVDO0lBRUQ7OztPQUdHO0lBQ0gsZUFBZSxJQUFJLGNBQWMsR0FBRyxTQUFTLENBRTVDO0lBRUQ7OztPQUdHO0lBQ0gsdUNBQXVDLElBQUksSUFBSSxDQW1COUM7SUFFRDs7T0FFRztJQUNILE9BQU8sQ0FBQyw0QkFBNEI7SUErQnBDOztPQUVHO0lBQ0gsT0FBTyxDQUFDLDBCQUEwQjtJQTJDbEM7O09BRUc7SUFDSCxPQUFPLENBQUMsc0JBQXNCO0lBa0Q5Qjs7T0FFRztJQUNILE9BQU8sQ0FBQyxnQ0FBZ0M7SUF3QnhDOztPQUVHO0lBQ0gsT0FBTyxDQUFDLHlCQUF5QjtJQThCakM7O09BRUc7SUFDRyxXQUFXLENBQUMsTUFBTSxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQUUsUUFBUSxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FFMUU7SUFFRDs7T0FFRztJQUNHLGFBQWEsQ0FBQyxNQUFNLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxtQkFBbUIsR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBRXpGO0lBRUQ7OztPQUdHO0lBQ0gsOEJBQThCLENBQzVCLGNBQWMsRUFBRSxNQUFNLEVBQ3RCLGVBQWUsRUFBRSxVQUFVLEdBQzFCLHFCQUFxQixHQUFHLFNBQVMsQ0F3R25DO0lBRUQsaURBQWlEO0lBQ2pELE9BQU8sQ0FBQyw4QkFBOEI7Q0F5QnZDIn0=
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keystore_manager.d.ts","sourceRoot":"","sources":["../src/keystore_manager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"keystore_manager.d.ts","sourceRoot":"","sources":["../src/keystore_manager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAKhE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAKhD,OAAO,KAAK,EAIV,WAAW,EAEX,qBAAqB,EACrB,QAAQ,EAER,cAAc,EACd,iBAAiB,IAAI,uBAAuB,EAC7C,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,qBAAa,aAAc,SAAQ,KAAK;IAGpB,KAAK,CAAC;IAFxB,YACE,OAAO,EAAE,MAAM,EACC,KAAK,CAAC,mBAAO,EAI9B;CACF;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IAEpC;;;;OAIG;IACH,YAAY,QAAQ,EAAE,QAAQ,EAG7B;IAED;;;OAGG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAsErC;IAED;;;;;OAKG;IACH,OAAO,CAAC,+BAA+B;IAkBvC;;;OAGG;IACH,OAAO,CAAC,0CAA0C;IAKlD;;OAEG;IACH,OAAO,CAAC,2CAA2C;IA+CnD;;OAEG;IACH,qBAAqB,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,EAAE,CAIzD;IAED;;OAEG;IACH,sBAAsB,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,EAAE,CAY1D;IAED,kCAAkC,IAAI,SAAS,EAAE,CAShD;IAED;;OAEG;IACH,oBAAoB,IAAI,SAAS,EAAE,CAMlC;IAED;;OAEG;IACH,mBAAmB,IAAI;QAAE,EAAE,EAAE,UAAU,GAAG,SAAS,CAAC;QAAC,OAAO,EAAE,SAAS,EAAE,CAAA;KAAE,GAAG,SAAS,CA+BtF;IAED;;OAEG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,uBAAuB,CAKnD;IAED;;OAEG;IACH,iBAAiB,IAAI,MAAM,CAE1B;IAED;;OAEG;IACH,kBAAkB,CAAC,cAAc,EAAE,MAAM,EAAE,eAAe,EAAE,UAAU,GAAG,UAAU,CASlF;IAED;;OAEG;IACH,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,YAAY,CAGpD;IAED;;;OAGG;IACH,kBAAkB,IAAI,WAAW,GAAG,SAAS,CAE5C;IAED;;;OAGG;IACH,eAAe,IAAI,cAAc,GAAG,SAAS,CAE5C;IAED;;;OAGG;IACH,uCAAuC,IAAI,IAAI,CAmB9C;IAED;;OAEG;IACH,OAAO,CAAC,4BAA4B;IA+BpC;;OAEG;IACH,OAAO,CAAC,0BAA0B;IA2ClC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAkD9B;;OAEG;IACH,OAAO,CAAC,gCAAgC;IAwBxC;;OAEG;IACH,OAAO,CAAC,yBAAyB;IA8BjC;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,CAE1E;IAED;;OAEG;IACG,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC,CAEzF;IAED;;;OAGG;IACH,8BAA8B,CAC5B,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,UAAU,GAC1B,qBAAqB,GAAG,SAAS,CAwGnC;IAED,iDAAiD;IACjD,OAAO,CAAC,8BAA8B;CAyBvC"}
|
package/dest/keystore_manager.js
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Manages keystore configuration and delegates signing operations to appropriate signers.
|
|
5
5
|
*/ import { Buffer32 } from '@aztec/foundation/buffer';
|
|
6
|
-
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
7
6
|
import { Wallet } from '@ethersproject/wallet';
|
|
8
7
|
import { readFileSync, readdirSync, statSync } from 'fs';
|
|
9
8
|
import { extname, join } from 'path';
|
|
10
9
|
import { mnemonicToAccount } from 'viem/accounts';
|
|
10
|
+
import { ethPrivateKeySchema } from './schemas.js';
|
|
11
11
|
import { LocalSigner, RemoteSigner } from './signer.js';
|
|
12
12
|
/**
|
|
13
13
|
* Error thrown when keystore operations fail
|
|
@@ -31,6 +31,70 @@ import { LocalSigner, RemoteSigner } from './signer.js';
|
|
|
31
31
|
this.validateUniqueAttesterAddresses();
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
|
+
* Validates all remote signers in the keystore are accessible and have the required addresses.
|
|
35
|
+
* Should be called after construction if validation is needed.
|
|
36
|
+
*/ async validateSigners() {
|
|
37
|
+
// Collect all remote signers with their addresses grouped by URL
|
|
38
|
+
const remoteSignersByUrl = new Map();
|
|
39
|
+
// Helper to extract remote signer URL from config
|
|
40
|
+
const getUrl = (config)=>{
|
|
41
|
+
return typeof config === 'string' ? config : config.remoteSignerUrl;
|
|
42
|
+
};
|
|
43
|
+
// Helper to collect remote signers from accounts
|
|
44
|
+
const collectRemoteSigners = (accounts, defaultRemoteSigner)=>{
|
|
45
|
+
const processAccount = (account)=>{
|
|
46
|
+
if (typeof account === 'object' && !('path' in account) && !('mnemonic' in account)) {
|
|
47
|
+
// This is a remote signer account
|
|
48
|
+
const remoteSigner = account;
|
|
49
|
+
const address = 'address' in remoteSigner ? remoteSigner.address : remoteSigner;
|
|
50
|
+
let url;
|
|
51
|
+
if ('remoteSignerUrl' in remoteSigner && remoteSigner.remoteSignerUrl) {
|
|
52
|
+
url = remoteSigner.remoteSignerUrl;
|
|
53
|
+
} else if (defaultRemoteSigner) {
|
|
54
|
+
url = getUrl(defaultRemoteSigner);
|
|
55
|
+
} else {
|
|
56
|
+
return; // No remote signer URL available
|
|
57
|
+
}
|
|
58
|
+
if (!remoteSignersByUrl.has(url)) {
|
|
59
|
+
remoteSignersByUrl.set(url, new Set());
|
|
60
|
+
}
|
|
61
|
+
remoteSignersByUrl.get(url).add(address.toString());
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
if (Array.isArray(accounts)) {
|
|
65
|
+
accounts.forEach((account)=>collectRemoteSigners(account, defaultRemoteSigner));
|
|
66
|
+
} else if (typeof accounts === 'object' && 'mnemonic' in accounts) {
|
|
67
|
+
// Skip mnemonic configs
|
|
68
|
+
} else {
|
|
69
|
+
processAccount(accounts);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
// Collect from validators
|
|
73
|
+
const validatorCount = this.getValidatorCount();
|
|
74
|
+
for(let i = 0; i < validatorCount; i++){
|
|
75
|
+
const validator = this.getValidator(i);
|
|
76
|
+
const remoteSigner = validator.remoteSigner || this.keystore.remoteSigner;
|
|
77
|
+
collectRemoteSigners(this.extractEthAccountsFromAttester(validator.attester), remoteSigner);
|
|
78
|
+
if (validator.publisher) {
|
|
79
|
+
collectRemoteSigners(validator.publisher, remoteSigner);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Collect from slasher
|
|
83
|
+
if (this.keystore.slasher) {
|
|
84
|
+
collectRemoteSigners(this.keystore.slasher, this.keystore.remoteSigner);
|
|
85
|
+
}
|
|
86
|
+
// Collect from prover
|
|
87
|
+
if (this.keystore.prover && typeof this.keystore.prover === 'object' && 'publisher' in this.keystore.prover) {
|
|
88
|
+
collectRemoteSigners(this.keystore.prover.publisher, this.keystore.remoteSigner);
|
|
89
|
+
}
|
|
90
|
+
// Validate each remote signer URL with all its addresses
|
|
91
|
+
for (const [url, addresses] of remoteSignersByUrl.entries()){
|
|
92
|
+
if (addresses.size > 0) {
|
|
93
|
+
await RemoteSigner.validateAccess(url, Array.from(addresses));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
34
98
|
* Validates that attester addresses are unique across all validators
|
|
35
99
|
* Only checks simple private key attesters, not JSON-V3 or mnemonic attesters,
|
|
36
100
|
* these are validated when decrypting the JSON-V3 keystore files
|
|
@@ -54,58 +118,47 @@ import { LocalSigner, RemoteSigner } from './signer.js';
|
|
|
54
118
|
* Best-effort address extraction that avoids decryption/derivation (no JSON-V3 or mnemonic processing).
|
|
55
119
|
* This is used at construction time to check for obvious duplicates without throwing for invalid inputs.
|
|
56
120
|
*/ extractAddressesWithoutSensitiveOperations(accounts) {
|
|
121
|
+
const ethAccounts = this.extractEthAccountsFromAttester(accounts);
|
|
122
|
+
return this.extractAddressesFromEthAccountsNonSensitive(ethAccounts);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Extract addresses from EthAccounts without sensitive operations (no decryption/derivation).
|
|
126
|
+
*/ extractAddressesFromEthAccountsNonSensitive(accounts) {
|
|
57
127
|
const results = [];
|
|
58
128
|
const handleAccount = (account)=>{
|
|
59
|
-
// String cases: private key or address or remote signer address
|
|
60
129
|
if (typeof account === 'string') {
|
|
61
130
|
if (account.startsWith('0x') && account.length === 66) {
|
|
62
|
-
// Private key -> derive address locally without external deps
|
|
63
131
|
try {
|
|
64
|
-
const signer = new LocalSigner(Buffer32.fromString(account));
|
|
132
|
+
const signer = new LocalSigner(Buffer32.fromString(ethPrivateKeySchema.parse(account)));
|
|
65
133
|
results.push(signer.address);
|
|
66
134
|
} catch {
|
|
67
|
-
//
|
|
68
|
-
}
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
if (account.startsWith('0x') && account.length === 42) {
|
|
72
|
-
// Address string
|
|
73
|
-
try {
|
|
74
|
-
results.push(EthAddress.fromString(account));
|
|
75
|
-
} catch {
|
|
76
|
-
// Ignore invalid address format at construction time
|
|
135
|
+
// ignore invalid private key at construction time
|
|
77
136
|
}
|
|
78
|
-
return;
|
|
79
137
|
}
|
|
80
|
-
// Any other string cannot be confidently resolved here
|
|
81
138
|
return;
|
|
82
139
|
}
|
|
83
|
-
// JSON V3 keystore: skip (requires decryption)
|
|
84
140
|
if ('path' in account) {
|
|
85
141
|
return;
|
|
86
142
|
}
|
|
87
|
-
// Mnemonic: skip (requires derivation and may throw on invalid mnemonics)
|
|
88
143
|
if ('mnemonic' in account) {
|
|
89
144
|
return;
|
|
90
145
|
}
|
|
91
|
-
// Remote signer account (object form)
|
|
92
146
|
const remoteSigner = account;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
results.push(EthAddress.fromString(address));
|
|
97
|
-
} catch {
|
|
98
|
-
// Ignore invalid address format at construction time
|
|
99
|
-
}
|
|
147
|
+
if ('address' in remoteSigner) {
|
|
148
|
+
results.push(remoteSigner.address);
|
|
149
|
+
return;
|
|
100
150
|
}
|
|
151
|
+
results.push(remoteSigner);
|
|
101
152
|
};
|
|
102
153
|
if (Array.isArray(accounts)) {
|
|
103
154
|
for (const account of accounts){
|
|
104
|
-
|
|
105
|
-
results.push(...subResults);
|
|
155
|
+
handleAccount(account);
|
|
106
156
|
}
|
|
107
157
|
return results;
|
|
108
158
|
}
|
|
159
|
+
if (typeof accounts === 'object' && accounts !== null && 'mnemonic' in accounts) {
|
|
160
|
+
return results;
|
|
161
|
+
}
|
|
109
162
|
handleAccount(accounts);
|
|
110
163
|
return results;
|
|
111
164
|
}
|
|
@@ -113,7 +166,8 @@ import { LocalSigner, RemoteSigner } from './signer.js';
|
|
|
113
166
|
* Create signers for validator attester accounts
|
|
114
167
|
*/ createAttesterSigners(validatorIndex) {
|
|
115
168
|
const validator = this.getValidator(validatorIndex);
|
|
116
|
-
|
|
169
|
+
const ethAccounts = this.extractEthAccountsFromAttester(validator.attester);
|
|
170
|
+
return this.createSignersFromEthAccounts(ethAccounts, validator.remoteSigner || this.keystore.remoteSigner);
|
|
117
171
|
}
|
|
118
172
|
/**
|
|
119
173
|
* Create signers for validator publisher accounts (falls back to attester if not specified)
|
|
@@ -147,7 +201,7 @@ import { LocalSigner, RemoteSigner } from './signer.js';
|
|
|
147
201
|
if (!this.keystore.prover) {
|
|
148
202
|
return undefined;
|
|
149
203
|
}
|
|
150
|
-
// Handle
|
|
204
|
+
// Handle prover being a private key, JSON key store or remote signer with nested address
|
|
151
205
|
if (typeof this.keystore.prover === 'string' || 'path' in this.keystore.prover || 'address' in this.keystore.prover) {
|
|
152
206
|
const signers = this.createSignersFromEthAccounts(this.keystore.prover, this.keystore.remoteSigner);
|
|
153
207
|
return {
|
|
@@ -155,10 +209,19 @@ import { LocalSigner, RemoteSigner } from './signer.js';
|
|
|
155
209
|
signers
|
|
156
210
|
};
|
|
157
211
|
}
|
|
158
|
-
|
|
159
|
-
|
|
212
|
+
// Handle prover as Id and specified publishers
|
|
213
|
+
if ('id' in this.keystore.prover) {
|
|
214
|
+
const id = this.keystore.prover.id;
|
|
215
|
+
const signers = this.createSignersFromEthAccounts(this.keystore.prover.publisher, this.keystore.remoteSigner);
|
|
216
|
+
return {
|
|
217
|
+
id,
|
|
218
|
+
signers
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
// Here, prover is just an EthAddress for a remote signer
|
|
222
|
+
const signers = this.createSignersFromEthAccounts(this.keystore.prover, this.keystore.remoteSigner);
|
|
160
223
|
return {
|
|
161
|
-
id,
|
|
224
|
+
id: undefined,
|
|
162
225
|
signers
|
|
163
226
|
};
|
|
164
227
|
}
|
|
@@ -176,18 +239,14 @@ import { LocalSigner, RemoteSigner } from './signer.js';
|
|
|
176
239
|
return this.keystore.validators?.length || 0;
|
|
177
240
|
}
|
|
178
241
|
/**
|
|
179
|
-
* Get coinbase address for validator (falls back to
|
|
180
|
-
*/ getCoinbaseAddress(validatorIndex) {
|
|
242
|
+
* Get coinbase address for validator (falls back to the specific attester address)
|
|
243
|
+
*/ getCoinbaseAddress(validatorIndex, attesterAddress) {
|
|
181
244
|
const validator = this.getValidator(validatorIndex);
|
|
182
245
|
if (validator.coinbase) {
|
|
183
|
-
return
|
|
184
|
-
}
|
|
185
|
-
// Fall back to first attester address
|
|
186
|
-
const attesterSigners = this.createAttesterSigners(validatorIndex);
|
|
187
|
-
if (attesterSigners.length === 0) {
|
|
188
|
-
throw new KeystoreError(`No attester signers found for validator ${validatorIndex}`);
|
|
246
|
+
return validator.coinbase;
|
|
189
247
|
}
|
|
190
|
-
|
|
248
|
+
// Fall back to the specific attester address
|
|
249
|
+
return attesterAddress;
|
|
191
250
|
}
|
|
192
251
|
/**
|
|
193
252
|
* Get fee recipient for validator
|
|
@@ -215,7 +274,7 @@ import { LocalSigner, RemoteSigner } from './signer.js';
|
|
|
215
274
|
const validatorCount = this.getValidatorCount();
|
|
216
275
|
for(let validatorIndex = 0; validatorIndex < validatorCount; validatorIndex++){
|
|
217
276
|
const validator = this.getValidator(validatorIndex);
|
|
218
|
-
const signers = this.createSignersFromEthAccounts(validator.attester, validator.remoteSigner || this.keystore.remoteSigner);
|
|
277
|
+
const signers = this.createSignersFromEthAccounts(this.extractEthAccountsFromAttester(validator.attester), validator.remoteSigner || this.keystore.remoteSigner);
|
|
219
278
|
for (const signer of signers){
|
|
220
279
|
const address = signer.address.toString().toLowerCase();
|
|
221
280
|
if (seenAddresses.has(address)) {
|
|
@@ -261,13 +320,9 @@ import { LocalSigner, RemoteSigner } from './signer.js';
|
|
|
261
320
|
if (typeof account === 'string') {
|
|
262
321
|
if (account.startsWith('0x') && account.length === 66) {
|
|
263
322
|
// Private key
|
|
264
|
-
return new LocalSigner(Buffer32.fromString(account));
|
|
323
|
+
return new LocalSigner(Buffer32.fromString(ethPrivateKeySchema.parse(account)));
|
|
265
324
|
} else {
|
|
266
|
-
|
|
267
|
-
if (!defaultRemoteSigner) {
|
|
268
|
-
throw new KeystoreError(`No remote signer configuration found for address ${account}`);
|
|
269
|
-
}
|
|
270
|
-
return new RemoteSigner(EthAddress.fromString(account), defaultRemoteSigner);
|
|
325
|
+
throw new Error(`Invalid private key`);
|
|
271
326
|
}
|
|
272
327
|
}
|
|
273
328
|
// JSON V3 keystore
|
|
@@ -277,23 +332,23 @@ import { LocalSigner, RemoteSigner } from './signer.js';
|
|
|
277
332
|
}
|
|
278
333
|
// Remote signer account
|
|
279
334
|
const remoteSigner = account;
|
|
280
|
-
if (
|
|
281
|
-
//
|
|
282
|
-
|
|
283
|
-
|
|
335
|
+
if ('address' in remoteSigner) {
|
|
336
|
+
// Remote signer with config
|
|
337
|
+
const config = remoteSigner.remoteSignerUrl ? {
|
|
338
|
+
remoteSignerUrl: remoteSigner.remoteSignerUrl,
|
|
339
|
+
certPath: remoteSigner.certPath,
|
|
340
|
+
certPass: remoteSigner.certPass
|
|
341
|
+
} : defaultRemoteSigner;
|
|
342
|
+
if (!config) {
|
|
343
|
+
throw new KeystoreError(`No remote signer configuration found for address ${remoteSigner.address}`);
|
|
284
344
|
}
|
|
285
|
-
return new RemoteSigner(
|
|
345
|
+
return new RemoteSigner(remoteSigner.address, config);
|
|
286
346
|
}
|
|
287
|
-
//
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
certPath: remoteSigner.certPath,
|
|
291
|
-
certPass: remoteSigner.certPass
|
|
292
|
-
} : defaultRemoteSigner;
|
|
293
|
-
if (!config) {
|
|
294
|
-
throw new KeystoreError(`No remote signer configuration found for address ${remoteSigner.address}`);
|
|
347
|
+
// Just an address - use default config
|
|
348
|
+
if (!defaultRemoteSigner) {
|
|
349
|
+
throw new KeystoreError(`No remote signer configuration found for address ${remoteSigner}`);
|
|
295
350
|
}
|
|
296
|
-
return new RemoteSigner(
|
|
351
|
+
return new RemoteSigner(remoteSigner, defaultRemoteSigner);
|
|
297
352
|
}
|
|
298
353
|
/**
|
|
299
354
|
* Create signer from JSON V3 keystore file or directory
|
|
@@ -410,20 +465,13 @@ import { LocalSigner, RemoteSigner } from './signer.js';
|
|
|
410
465
|
if (account.startsWith('0x') && account.length === 66) {
|
|
411
466
|
// This is a private key - derive the address
|
|
412
467
|
try {
|
|
413
|
-
const signer = new LocalSigner(Buffer32.fromString(account));
|
|
468
|
+
const signer = new LocalSigner(Buffer32.fromString(ethPrivateKeySchema.parse(account)));
|
|
414
469
|
return signer.address;
|
|
415
470
|
} catch {
|
|
416
|
-
return
|
|
417
|
-
}
|
|
418
|
-
} else if (account.startsWith('0x') && account.length === 42) {
|
|
419
|
-
// This is an address
|
|
420
|
-
try {
|
|
421
|
-
return EthAddress.fromString(account);
|
|
422
|
-
} catch {
|
|
423
|
-
return null;
|
|
471
|
+
return undefined;
|
|
424
472
|
}
|
|
425
473
|
}
|
|
426
|
-
return
|
|
474
|
+
return undefined;
|
|
427
475
|
}
|
|
428
476
|
// JSON V3 keystore
|
|
429
477
|
if ('path' in account) {
|
|
@@ -431,17 +479,15 @@ import { LocalSigner, RemoteSigner } from './signer.js';
|
|
|
431
479
|
const signers = this.createSignerFromJsonV3(account);
|
|
432
480
|
return signers.map((s)=>s.address);
|
|
433
481
|
} catch {
|
|
434
|
-
return
|
|
482
|
+
return undefined;
|
|
435
483
|
}
|
|
436
484
|
}
|
|
437
|
-
// Remote signer account
|
|
485
|
+
// Remote signer account, either it is an address or the address is nested
|
|
438
486
|
const remoteSigner = account;
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
return EthAddress.fromString(address);
|
|
442
|
-
} catch {
|
|
443
|
-
return null;
|
|
487
|
+
if ('address' in remoteSigner) {
|
|
488
|
+
return remoteSigner.address;
|
|
444
489
|
}
|
|
490
|
+
return remoteSigner;
|
|
445
491
|
};
|
|
446
492
|
// Helper to check if account matches and get its remote signer config
|
|
447
493
|
const checkAccount = (account)=>{
|
|
@@ -458,13 +504,7 @@ import { LocalSigner, RemoteSigner } from './signer.js';
|
|
|
458
504
|
}
|
|
459
505
|
// Found a match - determine the config to return
|
|
460
506
|
if (typeof account === 'string') {
|
|
461
|
-
|
|
462
|
-
// Private key - local signer, no remote config
|
|
463
|
-
return undefined;
|
|
464
|
-
} else {
|
|
465
|
-
// Address only - use defaults
|
|
466
|
-
return validator.remoteSigner || this.keystore.remoteSigner;
|
|
467
|
-
}
|
|
507
|
+
return undefined;
|
|
468
508
|
}
|
|
469
509
|
// JSON V3 - local signer, no remote config
|
|
470
510
|
if ('path' in account) {
|
|
@@ -472,50 +512,68 @@ import { LocalSigner, RemoteSigner } from './signer.js';
|
|
|
472
512
|
}
|
|
473
513
|
// Remote signer account with potential override
|
|
474
514
|
const remoteSigner = account;
|
|
475
|
-
if (
|
|
476
|
-
//
|
|
477
|
-
|
|
515
|
+
if ('address' in remoteSigner) {
|
|
516
|
+
// Has inline config
|
|
517
|
+
if (remoteSigner.remoteSignerUrl) {
|
|
518
|
+
return {
|
|
519
|
+
remoteSignerUrl: remoteSigner.remoteSignerUrl,
|
|
520
|
+
certPath: remoteSigner.certPath,
|
|
521
|
+
certPass: remoteSigner.certPass
|
|
522
|
+
};
|
|
523
|
+
} else {
|
|
524
|
+
// No URL specified, use defaults
|
|
525
|
+
return validator.remoteSigner || this.keystore.remoteSigner;
|
|
526
|
+
}
|
|
478
527
|
}
|
|
479
|
-
//
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
528
|
+
// Just an address, use defaults
|
|
529
|
+
return validator.remoteSigner || this.keystore.remoteSigner;
|
|
530
|
+
};
|
|
531
|
+
// Normalize attester to EthAccounts and search
|
|
532
|
+
const normalized = this.extractEthAccountsFromAttester(validator.attester);
|
|
533
|
+
const findInEthAccounts = (accs)=>{
|
|
534
|
+
if (typeof accs === 'string') {
|
|
535
|
+
return checkAccount(accs);
|
|
536
|
+
}
|
|
537
|
+
if (Array.isArray(accs)) {
|
|
538
|
+
for (const a of accs){
|
|
539
|
+
const res = checkAccount(a);
|
|
540
|
+
if (res !== undefined) {
|
|
541
|
+
return res;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
return undefined;
|
|
489
545
|
}
|
|
546
|
+
if (typeof accs === 'object' && accs !== null && 'mnemonic' in accs) {
|
|
547
|
+
// mnemonic-derived keys are local signers; no remote signer config
|
|
548
|
+
return undefined;
|
|
549
|
+
}
|
|
550
|
+
return checkAccount(accs);
|
|
490
551
|
};
|
|
491
|
-
|
|
492
|
-
|
|
552
|
+
return findInEthAccounts(normalized);
|
|
553
|
+
}
|
|
554
|
+
/** Extract ETH accounts from AttesterAccounts */ extractEthAccountsFromAttester(attester) {
|
|
493
555
|
if (typeof attester === 'string') {
|
|
494
|
-
|
|
495
|
-
return result === undefined ? undefined : result;
|
|
556
|
+
return attester;
|
|
496
557
|
}
|
|
497
558
|
if (Array.isArray(attester)) {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
if (
|
|
501
|
-
|
|
559
|
+
const out = [];
|
|
560
|
+
for (const item of attester){
|
|
561
|
+
if (typeof item === 'string') {
|
|
562
|
+
out.push(item);
|
|
563
|
+
} else if ('eth' in item) {
|
|
564
|
+
out.push(item.eth);
|
|
565
|
+
} else if (!('mnemonic' in item)) {
|
|
566
|
+
out.push(item);
|
|
502
567
|
}
|
|
503
568
|
}
|
|
504
|
-
return
|
|
569
|
+
return out;
|
|
505
570
|
}
|
|
506
|
-
// Mnemonic configuration
|
|
507
571
|
if ('mnemonic' in attester) {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
return matches ? undefined : undefined;
|
|
513
|
-
} catch {
|
|
514
|
-
return undefined;
|
|
515
|
-
}
|
|
572
|
+
return attester;
|
|
573
|
+
}
|
|
574
|
+
if ('eth' in attester) {
|
|
575
|
+
return attester.eth;
|
|
516
576
|
}
|
|
517
|
-
|
|
518
|
-
const result = checkAccount(attester);
|
|
519
|
-
return result === undefined ? undefined : result;
|
|
577
|
+
return attester;
|
|
520
578
|
}
|
|
521
579
|
}
|
package/dest/loader.d.ts
CHANGED
|
@@ -59,4 +59,4 @@ export declare function loadMultipleKeystores(paths: string | string[]): KeyStor
|
|
|
59
59
|
* @throws KeyStoreLoadError When duplicate attester keys are found or multiple prover configs exist.
|
|
60
60
|
*/
|
|
61
61
|
export declare function mergeKeystores(keystores: KeyStore[]): KeyStore;
|
|
62
|
-
//# sourceMappingURL=
|
|
62
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9hZGVyLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvbG9hZGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQWNBLE9BQU8sS0FBSyxFQUFlLFFBQVEsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUl4RDs7R0FFRztBQUNILHFCQUFhLGlCQUFrQixTQUFRLEtBQUs7SUFHakMsUUFBUSxFQUFFLE1BQU07SUFDUCxLQUFLLENBQUM7SUFIeEIsWUFDRSxPQUFPLEVBQUUsTUFBTSxFQUNSLFFBQVEsRUFBRSxNQUFNLEVBQ1AsS0FBSyxDQUFDLG1CQUFPLEVBSTlCO0NBQ0Y7QUFFRDs7Ozs7O0dBTUc7QUFDSCx3QkFBZ0IsZ0JBQWdCLENBQUMsUUFBUSxFQUFFLE1BQU0sR0FBRyxRQUFRLENBdUIzRDtBQUVEOzs7Ozs7R0FNRztBQUNILHdCQUFnQixxQkFBcUIsQ0FBQyxPQUFPLEVBQUUsTUFBTSxHQUFHLFFBQVEsRUFBRSxDQW1DakU7QUFFRDs7Ozs7Ozs7O0dBU0c7QUFDSCx3QkFBZ0IsYUFBYSxDQUFDLElBQUksRUFBRSxNQUFNLEdBQUcsUUFBUSxFQUFFLENBdUJ0RDtBQUVEOzs7Ozs7R0FNRztBQUNILHdCQUFnQixxQkFBcUIsQ0FBQyxLQUFLLEVBQUUsTUFBTSxHQUFHLE1BQU0sRUFBRSxHQUFHLFFBQVEsRUFBRSxDQThCMUU7QUFFRDs7Ozs7Ozs7Ozs7Ozs7O0dBZUc7QUFDSCx3QkFBZ0IsY0FBYyxDQUFDLFNBQVMsRUFBRSxRQUFRLEVBQUUsR0FBRyxRQUFRLENBZ0Y5RCJ9
|
package/dest/loader.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAe,QAAQ,EAAE,MAAM,YAAY,CAAC;AAIxD;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAGjC,QAAQ,EAAE,MAAM;IACP,KAAK,CAAC;IAHxB,YACE,OAAO,EAAE,MAAM,EACR,QAAQ,EAAE,MAAM,EACP,KAAK,CAAC,mBAAO,EAI9B;CACF;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAuB3D;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,CAmCjE;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CAuBtD;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,QAAQ,EAAE,CA8B1E;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,QAAQ,CAgF9D"}
|
package/dest/loader.js
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
* Keystore File Loader
|
|
3
3
|
*
|
|
4
4
|
* Handles loading and parsing keystore configuration files.
|
|
5
|
-
*/ import {
|
|
5
|
+
*/ import { EthAddress } from '@aztec/foundation/eth-address';
|
|
6
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
6
7
|
import { readFileSync, readdirSync, statSync } from 'fs';
|
|
7
8
|
import { extname, join } from 'path';
|
|
9
|
+
import { privateKeyToAddress } from 'viem/accounts';
|
|
8
10
|
import { keystoreSchema } from './schemas.js';
|
|
9
11
|
const logger = createLogger('node-keystore:loader');
|
|
10
12
|
/**
|
|
@@ -179,8 +181,9 @@ const logger = createLogger('node-keystore:loader');
|
|
|
179
181
|
if (keystore.validators) {
|
|
180
182
|
for (const validator of keystore.validators){
|
|
181
183
|
// Check for duplicate attester addresses
|
|
182
|
-
const attesterKeys =
|
|
183
|
-
for (
|
|
184
|
+
const attesterKeys = extractAttesterAddresses(validator.attester);
|
|
185
|
+
for (let key of attesterKeys){
|
|
186
|
+
key = key.toLowerCase();
|
|
184
187
|
if (attesterAddresses.has(key)) {
|
|
185
188
|
throw new KeyStoreLoadError(`Duplicate attester address ${key} found across keystore files`, `keystores[${i}].validators`);
|
|
186
189
|
}
|
|
@@ -235,21 +238,45 @@ const logger = createLogger('node-keystore:loader');
|
|
|
235
238
|
*
|
|
236
239
|
* @param attester The attester configuration in any supported shape.
|
|
237
240
|
* @returns Array of string keys used to detect duplicates.
|
|
238
|
-
*/ function
|
|
241
|
+
*/ function extractAttesterAddresses(attester) {
|
|
242
|
+
// String forms (private key or other) - return as-is for coarse uniqueness
|
|
239
243
|
if (typeof attester === 'string') {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
244
|
+
if (attester.length === 66) {
|
|
245
|
+
return [
|
|
246
|
+
privateKeyToAddress(attester)
|
|
247
|
+
];
|
|
248
|
+
} else {
|
|
249
|
+
return [
|
|
250
|
+
attester
|
|
251
|
+
];
|
|
252
|
+
}
|
|
243
253
|
}
|
|
254
|
+
// Arrays of attester items
|
|
244
255
|
if (Array.isArray(attester)) {
|
|
245
|
-
|
|
256
|
+
const keys = [];
|
|
257
|
+
for (const item of attester){
|
|
258
|
+
keys.push(...extractAttesterAddresses(item));
|
|
259
|
+
}
|
|
260
|
+
return keys;
|
|
246
261
|
}
|
|
247
|
-
if (attester && typeof attester === 'object'
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
262
|
+
if (attester && typeof attester === 'object') {
|
|
263
|
+
if (attester instanceof EthAddress) {
|
|
264
|
+
return [
|
|
265
|
+
attester.toString()
|
|
266
|
+
];
|
|
267
|
+
}
|
|
268
|
+
const obj = attester;
|
|
269
|
+
// New shape: { eth: EthAccount, bls?: BLSAccount }
|
|
270
|
+
if ('eth' in obj) {
|
|
271
|
+
return extractAttesterAddresses(obj.eth);
|
|
272
|
+
}
|
|
273
|
+
// Remote signer account object shape: { address, remoteSignerUrl?, ... }
|
|
274
|
+
if ('address' in obj) {
|
|
275
|
+
return [
|
|
276
|
+
String(obj.address)
|
|
277
|
+
];
|
|
278
|
+
}
|
|
251
279
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
];
|
|
280
|
+
// mnemonic, encrypted file just disable early duplicates checking
|
|
281
|
+
return [];
|
|
255
282
|
}
|