@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 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=config.d.ts.map
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=index.d.ts.map
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 first attester address)
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): string;
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=keystore_manager.d.ts.map
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,iBAAiB,CAAC;AACjD,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;AAKjE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAIhD,OAAO,KAAK,EAEV,WAAW,EAKX,qBAAqB,EACrB,QAAQ,EACR,cAAc,EACd,iBAAiB,IAAI,uBAAuB,EAC7C,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,qBAAa,aAAc,SAAQ,KAAK;IAGpB,KAAK,CAAC,EAAE,KAAK;gBAD7B,OAAO,EAAE,MAAM,EACC,KAAK,CAAC,EAAE,KAAK,YAAA;CAKhC;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IAEpC;;;;OAIG;gBACS,QAAQ,EAAE,QAAQ;IAK9B;;;;;OAKG;IACH,OAAO,CAAC,+BAA+B;IAkBvC;;;OAGG;IACH,OAAO,CAAC,0CAA0C;IAiElD;;OAEG;IACH,qBAAqB,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,EAAE;IAK1D;;OAEG;IACH,sBAAsB,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,EAAE;IAc3D,kCAAkC,IAAI,SAAS,EAAE;IAWjD;;OAEG;IACH,oBAAoB,IAAI,SAAS,EAAE;IAQnC;;OAEG;IACH,mBAAmB,IAAI;QAAE,EAAE,EAAE,UAAU,GAAG,SAAS,CAAC;QAAC,OAAO,EAAE,SAAS,EAAE,CAAA;KAAE,GAAG,SAAS;IAwBvF;;OAEG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,uBAAuB;IAOpD;;OAEG;IACH,iBAAiB,IAAI,MAAM;IAI3B;;OAEG;IACH,kBAAkB,CAAC,cAAc,EAAE,MAAM,GAAG,UAAU;IAgBtD;;OAEG;IACH,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM;IAK/C;;;OAGG;IACH,kBAAkB,IAAI,WAAW,GAAG,SAAS;IAI7C;;;OAGG;IACH,eAAe,IAAI,cAAc,GAAG,SAAS;IAI7C;;;OAGG;IACH,uCAAuC,IAAI,IAAI;IAqB/C;;OAEG;IACH,OAAO,CAAC,4BAA4B;IA+BpC;;OAEG;IACH,OAAO,CAAC,0BAA0B;IA+ClC;;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;IAI3E;;OAEG;IACG,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC;IAI1F;;;OAGG;IACH,8BAA8B,CAC5B,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,UAAU,GAC1B,qBAAqB,GAAG,SAAS;CAiIrC"}
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"}
@@ -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
- // Ignore invalid private key at construction time
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
- const address = typeof remoteSigner === 'string' ? remoteSigner : remoteSigner.address;
94
- if (address) {
95
- try {
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
- const subResults = this.extractAddressesWithoutSensitiveOperations(account);
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
- return this.createSignersFromEthAccounts(validator.attester, validator.remoteSigner || this.keystore.remoteSigner);
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 simple prover case (just a private key)
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
- const id = EthAddress.fromString(this.keystore.prover.id);
159
- const signers = this.createSignersFromEthAccounts(this.keystore.prover.publisher, this.keystore.remoteSigner);
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 first attester address)
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 EthAddress.fromString(validator.coinbase);
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
- return attesterSigners[0].address;
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
- // Remote signer address only - use default remote signer config
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 (typeof remoteSigner === 'string') {
281
- // Just an address - use default config
282
- if (!defaultRemoteSigner) {
283
- throw new KeystoreError(`No remote signer configuration found for address ${remoteSigner}`);
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(EthAddress.fromString(remoteSigner), defaultRemoteSigner);
345
+ return new RemoteSigner(remoteSigner.address, config);
286
346
  }
287
- // Remote signer with config
288
- const config = remoteSigner.remoteSignerUrl ? {
289
- remoteSignerUrl: remoteSigner.remoteSignerUrl,
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(EthAddress.fromString(remoteSigner.address), config);
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 null;
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 null;
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 null;
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
- const address = typeof remoteSigner === 'string' ? remoteSigner : remoteSigner.address;
440
- try {
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
- if (account.startsWith('0x') && account.length === 66) {
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 (typeof remoteSigner === 'string') {
476
- // Just an address - use defaults
477
- return validator.remoteSigner || this.keystore.remoteSigner;
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
- // Has inline config
480
- if (remoteSigner.remoteSignerUrl) {
481
- return {
482
- remoteSignerUrl: remoteSigner.remoteSignerUrl,
483
- certPath: remoteSigner.certPath,
484
- certPass: remoteSigner.certPass
485
- };
486
- } else {
487
- // No URL specified, use defaults
488
- return validator.remoteSigner || this.keystore.remoteSigner;
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
- // Check the attester configuration
492
- const { attester } = validator;
552
+ return findInEthAccounts(normalized);
553
+ }
554
+ /** Extract ETH accounts from AttesterAccounts */ extractEthAccountsFromAttester(attester) {
493
555
  if (typeof attester === 'string') {
494
- const result = checkAccount(attester);
495
- return result === undefined ? undefined : result;
556
+ return attester;
496
557
  }
497
558
  if (Array.isArray(attester)) {
498
- for (const account of attester){
499
- const result = checkAccount(account);
500
- if (result !== undefined) {
501
- return result;
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 undefined;
569
+ return out;
505
570
  }
506
- // Mnemonic configuration
507
571
  if ('mnemonic' in attester) {
508
- try {
509
- const signers = this.createSignersFromMnemonic(attester);
510
- const matches = signers.some((s)=>s.address.equals(attesterAddress));
511
- // Mnemonic-derived keys are local signers
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
- // Single account object
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=loader.d.ts.map
62
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9hZGVyLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvbG9hZGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQWNBLE9BQU8sS0FBSyxFQUFlLFFBQVEsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUl4RDs7R0FFRztBQUNILHFCQUFhLGlCQUFrQixTQUFRLEtBQUs7SUFHakMsUUFBUSxFQUFFLE1BQU07SUFDUCxLQUFLLENBQUM7SUFIeEIsWUFDRSxPQUFPLEVBQUUsTUFBTSxFQUNSLFFBQVEsRUFBRSxNQUFNLEVBQ1AsS0FBSyxDQUFDLG1CQUFPLEVBSTlCO0NBQ0Y7QUFFRDs7Ozs7O0dBTUc7QUFDSCx3QkFBZ0IsZ0JBQWdCLENBQUMsUUFBUSxFQUFFLE1BQU0sR0FBRyxRQUFRLENBdUIzRDtBQUVEOzs7Ozs7R0FNRztBQUNILHdCQUFnQixxQkFBcUIsQ0FBQyxPQUFPLEVBQUUsTUFBTSxHQUFHLFFBQVEsRUFBRSxDQW1DakU7QUFFRDs7Ozs7Ozs7O0dBU0c7QUFDSCx3QkFBZ0IsYUFBYSxDQUFDLElBQUksRUFBRSxNQUFNLEdBQUcsUUFBUSxFQUFFLENBdUJ0RDtBQUVEOzs7Ozs7R0FNRztBQUNILHdCQUFnQixxQkFBcUIsQ0FBQyxLQUFLLEVBQUUsTUFBTSxHQUFHLE1BQU0sRUFBRSxHQUFHLFFBQVEsRUFBRSxDQThCMUU7QUFFRDs7Ozs7Ozs7Ozs7Ozs7O0dBZUc7QUFDSCx3QkFBZ0IsY0FBYyxDQUFDLFNBQVMsRUFBRSxRQUFRLEVBQUUsR0FBRyxRQUFRLENBZ0Y5RCJ9
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAe,QAAQ,EAAE,MAAM,YAAY,CAAC;AAIxD;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAGjC,QAAQ,EAAE,MAAM;IACP,KAAK,CAAC,EAAE,KAAK;gBAF7B,OAAO,EAAE,MAAM,EACR,QAAQ,EAAE,MAAM,EACP,KAAK,CAAC,EAAE,KAAK,YAAA;CAKhC;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,CA+E9D"}
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 { createLogger } from '@aztec/foundation/log';
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 = extractAttesterKeys(validator.attester);
183
- for (const key of attesterKeys){
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 extractAttesterKeys(attester) {
241
+ */ function extractAttesterAddresses(attester) {
242
+ // String forms (private key or other) - return as-is for coarse uniqueness
239
243
  if (typeof attester === 'string') {
240
- return [
241
- attester
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
- return attester.map((a)=>typeof a === 'string' ? a : JSON.stringify(a));
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' && 'address' in attester) {
248
- return [
249
- attester.address
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
- return [
253
- JSON.stringify(attester)
254
- ];
280
+ // mnemonic, encrypted file just disable early duplicates checking
281
+ return [];
255
282
  }