@aztec/node-keystore 2.0.3 → 2.1.0-rc.10

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.
@@ -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;AAGhD,eAAO,MAAM,mBAAmB,kDAGK,CAAC;AAiEtC,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYvB,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: optional(urlSchema),
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: ethAccountsSchema,
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
@@ -7,7 +7,7 @@ import type { EthSigner } from '@aztec/ethereum';
7
7
  import { Buffer32 } from '@aztec/foundation/buffer';
8
8
  import type { EthAddress } from '@aztec/foundation/eth-address';
9
9
  import { Signature } from '@aztec/foundation/eth-signature';
10
- import { type TransactionSerializable, type TypedDataDefinition } from 'viem';
10
+ import { type TransactionSerializable, type TypedDataDefinition } from '@spalladino/viem';
11
11
  import type { EthRemoteSignerConfig } from './types.js';
12
12
  /**
13
13
  * Error thrown for remote signer HTTP or JSON-RPC failures
@@ -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
  */
@@ -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,kBAAkB,CAAC;AAE1B,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
@@ -7,7 +7,7 @@ import { Secp256k1Signer, randomBytes, toRecoveryBit } from '@aztec/foundation/c
7
7
  import { Signature } from '@aztec/foundation/eth-signature';
8
8
  import { jsonStringify } from '@aztec/foundation/json-rpc';
9
9
  import { withHexPrefix } from '@aztec/foundation/string';
10
- import { hashTypedData, keccak256, parseTransaction, serializeTransaction } from 'viem';
10
+ import { hashTypedData, keccak256, parseTransaction, serializeTransaction } from '@spalladino/viem';
11
11
  /**
12
12
  * Error thrown for remote signer HTTP or JSON-RPC failures
13
13
  */ export class SignerError extends Error {
@@ -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 EthJsonKeyFileV3Config = {
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?: Url;
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 | EthJsonKeyFileV3Config;
44
+ export type EthAccount = EthPrivateKey | EthRemoteSignerAccount | JsonKeyFileV3Config;
43
45
  /** A mnemonic can be used to define a set of accounts */
44
- export type EthMnemonicConfig = {
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[] | EthMnemonicConfig;
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: EthAccounts;
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.
@@ -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,sBAAsB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzE,iDAAiD;AACjD,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;AAEpC,kCAAkC;AAClC,MAAM,MAAM,GAAG,GAAG,MAAM,CAAC;AAEzB;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAC7B,GAAG,GACH;IACE,eAAe,EAAE,GAAG,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEN;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAC9B,UAAU,GACV;IACE,OAAO,EAAE,UAAU,CAAC;IACpB,eAAe,CAAC,EAAE,GAAG,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEN,uGAAuG;AACvG,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,sBAAsB,GAAG,sBAAsB,CAAC;AAEzF,yDAAyD;AACzD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,8BAA8B;AAC9B,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,UAAU,EAAE,GAAG,iBAAiB,CAAC;AAExE,MAAM,MAAM,oBAAoB,GAAG;IACjC,iFAAiF;IACjF,EAAE,EAAE,UAAU,CAAC;IACf,sDAAsD;IACtD,SAAS,EAAE,WAAW,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,oBAAoB,GAAG,UAAU,CAAC;AAE/D,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;OAGG;IACH,QAAQ,EAAE,WAAW,CAAC;IACtB;;;OAGG;IACH,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB;;;OAGG;IACH,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB;;OAEG;IACH,YAAY,EAAE,YAAY,CAAC;IAC3B;;OAEG;IACH,YAAY,CAAC,EAAE,qBAAqB,CAAC;IACrC;;OAEG;IACH,cAAc,CAAC,EAAE,UAAU,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,0DAA0D;IAC1D,aAAa,EAAE,MAAM,CAAC;IACtB,gCAAgC;IAChC,UAAU,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACjC,8GAA8G;IAC9G,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,qBAAqB,CAAC;IACrC,sEAAsE;IACtE,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,wHAAwH;IACxH,cAAc,CAAC,EAAE,UAAU,CAAC;CAC7B,CAAC"}
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,mBAAmB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtE,iDAAiD;AACjD,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;AAEpC,qDAAqD;AACrD,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;AAEpC,kCAAkC;AAClC,MAAM,MAAM,GAAG,GAAG,MAAM,CAAC;AAEzB;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAC7B,GAAG,GACH;IACE,eAAe,EAAE,GAAG,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEN;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAC9B,UAAU,GACV;IACE,OAAO,EAAE,UAAU,CAAC;IACpB,eAAe,EAAE,GAAG,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEN,uGAAuG;AACvG,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,sBAAsB,GAAG,mBAAmB,CAAC;AAEtF,yDAAyD;AACzD,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,8BAA8B;AAC9B,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,UAAU,EAAE,GAAG,cAAc,CAAC;AAErE,MAAM,MAAM,oBAAoB,GAAG;IACjC,iFAAiF;IACjF,EAAE,EAAE,UAAU,CAAC;IACf,sDAAsD;IACtD,SAAS,EAAE,WAAW,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,oBAAoB,GAAG,UAAU,CAAC;AAE/D,+EAA+E;AAC/E,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,mBAAmB,CAAC;AAE7D,0EAA0E;AAC1E,MAAM,MAAM,eAAe,GAAG;IAAE,GAAG,EAAE,UAAU,CAAC;IAAC,GAAG,CAAC,EAAE,UAAU,CAAA;CAAE,GAAG,UAAU,CAAC;AAEjF,+DAA+D;AAC/D,MAAM,MAAM,gBAAgB,GAAG,eAAe,GAAG,eAAe,EAAE,GAAG,cAAc,CAAC;AAEpF,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;OAGG;IACH,QAAQ,EAAE,gBAAgB,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB;;;OAGG;IACH,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB;;OAEG;IACH,YAAY,EAAE,YAAY,CAAC;IAC3B;;OAEG;IACH,YAAY,CAAC,EAAE,qBAAqB,CAAC;IACrC;;OAEG;IACH,cAAc,CAAC,EAAE,UAAU,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,0DAA0D;IAC1D,aAAa,EAAE,MAAM,CAAC;IACtB,gCAAgC;IAChC,UAAU,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACjC,8GAA8G;IAC9G,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,qBAAqB,CAAC;IACrC,sEAAsE;IACtE,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,wHAAwH;IACxH,cAAc,CAAC,EAAE,UAAU,CAAC;CAC7B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/node-keystore",
3
- "version": "2.0.3",
3
+ "version": "2.1.0-rc.10",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
@@ -62,12 +62,12 @@
62
62
  ]
63
63
  },
64
64
  "dependencies": {
65
- "@aztec/ethereum": "2.0.3",
66
- "@aztec/foundation": "2.0.3",
67
- "@aztec/stdlib": "2.0.3",
65
+ "@aztec/ethereum": "2.1.0-rc.10",
66
+ "@aztec/foundation": "2.1.0-rc.10",
67
+ "@aztec/stdlib": "2.1.0-rc.10",
68
68
  "@ethersproject/wallet": "^5.7.0",
69
+ "@spalladino/viem": "2.38.2-eip7594.0",
69
70
  "tslib": "^2.4.0",
70
- "viem": "2.23.7",
71
71
  "zod": "^3.23.8"
72
72
  },
73
73
  "devDependencies": {
@@ -10,21 +10,22 @@ import type { Signature } from '@aztec/foundation/eth-signature';
10
10
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
11
11
 
12
12
  import { Wallet } from '@ethersproject/wallet';
13
+ import type { TypedDataDefinition } from '@spalladino/viem';
14
+ import { mnemonicToAccount } from '@spalladino/viem/accounts';
13
15
  import { readFileSync, readdirSync, statSync } from 'fs';
14
16
  import { extname, join } from 'path';
15
- import type { TypedDataDefinition } from 'viem';
16
- import { mnemonicToAccount } from 'viem/accounts';
17
17
 
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: EthAccounts): EthAddress[] {
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
- // Ignore invalid private key at construction time
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
- const subResults = this.extractAddressesWithoutSensitiveOperations(account);
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
- return this.createSignersFromEthAccounts(validator.attester, validator.remoteSigner || this.keystore.remoteSigner);
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
 
@@ -240,22 +320,17 @@ export class KeystoreManager {
240
320
  }
241
321
 
242
322
  /**
243
- * Get coinbase address for validator (falls back to first attester address)
323
+ * Get coinbase address for validator (falls back to the specific attester address)
244
324
  */
245
- getCoinbaseAddress(validatorIndex: number): EthAddress {
325
+ getCoinbaseAddress(validatorIndex: number, attesterAddress: EthAddress): EthAddress {
246
326
  const validator = this.getValidator(validatorIndex);
247
327
 
248
328
  if (validator.coinbase) {
249
329
  return validator.coinbase;
250
330
  }
251
331
 
252
- // Fall back to first attester address
253
- const attesterSigners = this.createAttesterSigners(validatorIndex);
254
- if (attesterSigners.length === 0) {
255
- throw new KeystoreError(`No attester signers found for validator ${validatorIndex}`);
256
- }
257
-
258
- return attesterSigners[0].address;
332
+ // Fall back to the specific attester address
333
+ return attesterAddress;
259
334
  }
260
335
 
261
336
  /**
@@ -292,7 +367,7 @@ export class KeystoreManager {
292
367
  for (let validatorIndex = 0; validatorIndex < validatorCount; validatorIndex++) {
293
368
  const validator = this.getValidator(validatorIndex);
294
369
  const signers = this.createSignersFromEthAccounts(
295
- validator.attester,
370
+ this.extractEthAccountsFromAttester(validator.attester),
296
371
  validator.remoteSigner || this.keystore.remoteSigner,
297
372
  );
298
373
  for (const signer of signers) {
@@ -390,7 +465,7 @@ export class KeystoreManager {
390
465
  /**
391
466
  * Create signer from JSON V3 keystore file or directory
392
467
  */
393
- private createSignerFromJsonV3(config: EthJsonKeyFileV3Config): EthSigner[] {
468
+ private createSignerFromJsonV3(config: JsonKeyFileV3Config): EthSigner[] {
394
469
  try {
395
470
  const stats = statSync(config.path);
396
471
 
@@ -470,7 +545,7 @@ export class KeystoreManager {
470
545
  /**
471
546
  * Create signers from mnemonic configuration using BIP44 derivation
472
547
  */
473
- private createSignersFromMnemonic(config: EthMnemonicConfig): EthSigner[] {
548
+ private createSignersFromMnemonic(config: MnemonicConfig): EthSigner[] {
474
549
  const { mnemonic, addressIndex = 0, accountIndex = 0, addressCount = 1, accountCount = 1 } = config;
475
550
  const signers: EthSigner[] = [];
476
551
 
@@ -601,38 +676,56 @@ export class KeystoreManager {
601
676
  return validator.remoteSigner || this.keystore.remoteSigner;
602
677
  };
603
678
 
604
- // Check the attester configuration
605
- const { attester } = validator;
679
+ // Normalize attester to EthAccounts and search
680
+ const normalized = this.extractEthAccountsFromAttester(validator.attester);
606
681
 
682
+ const findInEthAccounts = (accs: EthAccounts): EthRemoteSignerConfig | undefined => {
683
+ if (typeof accs === 'string') {
684
+ return checkAccount(accs);
685
+ }
686
+ if (Array.isArray(accs)) {
687
+ for (const a of accs as EthAccount[]) {
688
+ const res = checkAccount(a);
689
+ if (res !== undefined) {
690
+ return res;
691
+ }
692
+ }
693
+ return undefined;
694
+ }
695
+ if (typeof accs === 'object' && accs !== null && 'mnemonic' in accs) {
696
+ // mnemonic-derived keys are local signers; no remote signer config
697
+ return undefined;
698
+ }
699
+ return checkAccount(accs as EthAccount);
700
+ };
701
+
702
+ return findInEthAccounts(normalized);
703
+ }
704
+
705
+ /** Extract ETH accounts from AttesterAccounts */
706
+ private extractEthAccountsFromAttester(attester: AttesterAccounts): EthAccounts {
607
707
  if (typeof attester === 'string') {
608
- const result = checkAccount(attester);
609
- return result === undefined ? undefined : result;
708
+ return attester;
610
709
  }
611
-
612
710
  if (Array.isArray(attester)) {
613
- for (const account of attester) {
614
- const result = checkAccount(account);
615
- if (result !== undefined) {
616
- return result;
711
+ const out: EthAccount[] = [];
712
+ for (const item of attester) {
713
+ if (typeof item === 'string') {
714
+ out.push(item);
715
+ } else if ('eth' in (item as any)) {
716
+ out.push((item as any).eth as EthAccount);
717
+ } else if (!('mnemonic' in (item as any))) {
718
+ out.push(item as EthAccount);
617
719
  }
618
720
  }
619
- return undefined;
721
+ return out;
620
722
  }
621
-
622
- // Mnemonic configuration
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
- }
723
+ if ('mnemonic' in (attester as any)) {
724
+ return attester as any;
632
725
  }
633
-
634
- // Single account object
635
- const result = checkAccount(attester);
636
- return result === undefined ? undefined : result;
726
+ if ('eth' in (attester as any)) {
727
+ return (attester as any).eth as EthAccount;
728
+ }
729
+ return attester as any;
637
730
  }
638
731
  }
package/src/loader.ts CHANGED
@@ -285,17 +285,37 @@ export function mergeKeystores(keystores: KeyStore[]): KeyStore {
285
285
  * @returns Array of string keys used to detect duplicates.
286
286
  */
287
287
  function extractAttesterKeys(attester: unknown): string[] {
288
+ // String forms (private key or other) - return as-is for coarse uniqueness
288
289
  if (typeof attester === 'string') {
289
290
  return [attester];
290
291
  }
291
292
 
293
+ // Arrays of attester items
292
294
  if (Array.isArray(attester)) {
293
- return attester.map(a => (typeof a === 'string' ? a : JSON.stringify(a)));
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' && 'address' in attester) {
297
- return [(attester as { address: string }).address];
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
  }