@aztec/validator-client 0.0.0-test.1 → 0.0.1-commit.017a351

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.
Files changed (66) hide show
  1. package/README.md +325 -0
  2. package/dest/checkpoint_builder.d.ts +79 -0
  3. package/dest/checkpoint_builder.d.ts.map +1 -0
  4. package/dest/checkpoint_builder.js +251 -0
  5. package/dest/config.d.ts +3 -14
  6. package/dest/config.d.ts.map +1 -1
  7. package/dest/config.js +75 -12
  8. package/dest/duties/validation_service.d.ts +49 -13
  9. package/dest/duties/validation_service.d.ts.map +1 -1
  10. package/dest/duties/validation_service.js +112 -18
  11. package/dest/factory.d.ts +34 -6
  12. package/dest/factory.d.ts.map +1 -1
  13. package/dest/factory.js +19 -6
  14. package/dest/index.d.ts +5 -2
  15. package/dest/index.d.ts.map +1 -1
  16. package/dest/index.js +4 -1
  17. package/dest/key_store/ha_key_store.d.ts +99 -0
  18. package/dest/key_store/ha_key_store.d.ts.map +1 -0
  19. package/dest/key_store/ha_key_store.js +208 -0
  20. package/dest/key_store/index.d.ts +4 -1
  21. package/dest/key_store/index.d.ts.map +1 -1
  22. package/dest/key_store/index.js +3 -0
  23. package/dest/key_store/interface.d.ts +85 -6
  24. package/dest/key_store/interface.d.ts.map +1 -1
  25. package/dest/key_store/interface.js +3 -3
  26. package/dest/key_store/local_key_store.d.ts +46 -11
  27. package/dest/key_store/local_key_store.d.ts.map +1 -1
  28. package/dest/key_store/local_key_store.js +68 -17
  29. package/dest/key_store/node_keystore_adapter.d.ts +151 -0
  30. package/dest/key_store/node_keystore_adapter.d.ts.map +1 -0
  31. package/dest/key_store/node_keystore_adapter.js +330 -0
  32. package/dest/key_store/web3signer_key_store.d.ts +66 -0
  33. package/dest/key_store/web3signer_key_store.d.ts.map +1 -0
  34. package/dest/key_store/web3signer_key_store.js +156 -0
  35. package/dest/metrics.d.ts +25 -5
  36. package/dest/metrics.d.ts.map +1 -1
  37. package/dest/metrics.js +86 -21
  38. package/dest/proposal_handler.d.ts +134 -0
  39. package/dest/proposal_handler.d.ts.map +1 -0
  40. package/dest/proposal_handler.js +1072 -0
  41. package/dest/validator.d.ts +109 -59
  42. package/dest/validator.d.ts.map +1 -1
  43. package/dest/validator.js +698 -171
  44. package/package.json +37 -21
  45. package/src/checkpoint_builder.ts +417 -0
  46. package/src/config.ts +87 -26
  47. package/src/duties/validation_service.ts +200 -21
  48. package/src/factory.ts +82 -11
  49. package/src/index.ts +4 -1
  50. package/src/key_store/ha_key_store.ts +269 -0
  51. package/src/key_store/index.ts +3 -0
  52. package/src/key_store/interface.ts +100 -5
  53. package/src/key_store/local_key_store.ts +77 -18
  54. package/src/key_store/node_keystore_adapter.ts +398 -0
  55. package/src/key_store/web3signer_key_store.ts +205 -0
  56. package/src/metrics.ts +121 -22
  57. package/src/proposal_handler.ts +1161 -0
  58. package/src/validator.ts +975 -222
  59. package/dest/errors/index.d.ts +0 -2
  60. package/dest/errors/index.d.ts.map +0 -1
  61. package/dest/errors/index.js +0 -1
  62. package/dest/errors/validator.error.d.ts +0 -29
  63. package/dest/errors/validator.error.d.ts.map +0 -1
  64. package/dest/errors/validator.error.js +0 -45
  65. package/src/errors/index.ts +0 -1
  66. package/src/errors/validator.error.ts +0 -55
@@ -0,0 +1,330 @@
1
+ import { KeystoreManager, loadKeystoreFile } from '@aztec/node-keystore';
2
+ import { InvalidValidatorPrivateKeyError } from '@aztec/stdlib/validators';
3
+ import { privateKeyToAccount } from 'viem/accounts';
4
+ export class NodeKeystoreAdapter {
5
+ keystoreManager;
6
+ // Per-validator cache (lazy)
7
+ validators = new Map();
8
+ addressIndex = new Map();
9
+ constructor(keystoreManager){
10
+ this.keystoreManager = keystoreManager;
11
+ }
12
+ /**
13
+ * Create an adapter from a keystore JSON file on disk.
14
+ * @param keystoreFilePath Absolute or relative path to a keystore JSON file
15
+ * @returns A configured NodeKeystoreAdapter instance
16
+ * @throws Error when the file fails schema validation or cannot be read
17
+ */ static fromKeystoreFile(keystoreFilePath) {
18
+ const keystoreConfig = loadKeystoreFile(keystoreFilePath);
19
+ return NodeKeystoreAdapter.fromKeystoreConfig(keystoreConfig);
20
+ }
21
+ /**
22
+ * Create an adapter from an in-memory keystore-like object.
23
+ * Validates resolved duplicate attester addresses across validators and sources.
24
+ * @param keystoreConfig Parsed config object (typically result of loadKeystoreFile)
25
+ * @returns A configured NodeKeystoreAdapter instance
26
+ * @throws Error when resolved duplicate attester addresses are detected
27
+ */ static fromKeystoreConfig(keystoreConfig) {
28
+ const keystoreManager = new KeystoreManager(keystoreConfig);
29
+ // Validate resolved attester addresses (covers JSON V3 and mnemonic duplicates across validators)
30
+ keystoreManager.validateResolvedUniqueAttesterAddresses();
31
+ return new NodeKeystoreAdapter(keystoreManager);
32
+ }
33
+ /**
34
+ * Build an adapter directly from a list of validator private keys.
35
+ * Each key becomes a separate validator using the same key for attester and publisher,
36
+ * coinbase defaults to the derived EOA address, and feeRecipient is a 32-byte padded address.
37
+ * Note: Fee recipient is a temporary placeholder, replace with actual fee recipient when implemented.
38
+ */ static fromPrivateKeys(privateKeys) {
39
+ // Minimal validation: 0x + 64 hex
40
+ const isPk = (s)=>/^0x[0-9a-fA-F]{64}$/.test(s);
41
+ for (const pk of privateKeys){
42
+ if (!isPk(pk)) {
43
+ throw new InvalidValidatorPrivateKeyError();
44
+ }
45
+ }
46
+ const validators = privateKeys.map((pk)=>{
47
+ const account = privateKeyToAccount(pk);
48
+ const ethAddress = account.address;
49
+ // TODO: Temporary fee recipient, replace with actual fee recipient when implemented
50
+ const feeRecipient = `0x${ethAddress.slice(2).padStart(64, '0')}`;
51
+ return {
52
+ attester: pk,
53
+ publisher: pk,
54
+ coinbase: ethAddress,
55
+ feeRecipient
56
+ };
57
+ });
58
+ const cfg = {
59
+ schemaVersion: 1,
60
+ validators
61
+ };
62
+ return NodeKeystoreAdapter.fromKeystoreConfig(cfg);
63
+ }
64
+ /**
65
+ * Build an adapter for a Web3Signer setup by providing the signer URL and the EOA addresses.
66
+ * Each address becomes a separate validator; attester and publisher point to the same remote signer entry.
67
+ * Note: Fee recipient is a temporary placeholder, replace with actual fee recipient when implemented.
68
+ */ static fromWeb3Signer(web3SignerUrl, addresses) {
69
+ const validators = addresses.map((address)=>{
70
+ const ethAddress = address.toString();
71
+ // TODO: Temporary fee recipient, replace with actual fee recipient when implemented
72
+ const feeRecipient = `0x${ethAddress.slice(2).padStart(64, '0')}`;
73
+ return {
74
+ attester: {
75
+ address: ethAddress,
76
+ remoteSignerUrl: web3SignerUrl
77
+ },
78
+ publisher: {
79
+ address: ethAddress,
80
+ remoteSignerUrl: web3SignerUrl
81
+ },
82
+ coinbase: ethAddress,
83
+ feeRecipient
84
+ };
85
+ });
86
+ const cfg = {
87
+ schemaVersion: 1,
88
+ validators
89
+ };
90
+ return NodeKeystoreAdapter.fromKeystoreConfig(cfg);
91
+ }
92
+ static fromKeyStoreManager(manager) {
93
+ return new NodeKeystoreAdapter(manager);
94
+ }
95
+ /**
96
+ * Normalize address keys to lowercase hex strings for map/set usage.
97
+ */ static key(addr) {
98
+ return typeof addr === 'string' ? addr.toLowerCase() : addr.toString().toLowerCase();
99
+ }
100
+ /**
101
+ * Ensure per-validator signer cache exists; build it by creating
102
+ * attester/publisher signers and populating indices when missing.
103
+ * @param validatorIndex Index of the validator in the keystore
104
+ * @returns The cached validator entry
105
+ */ ensureValidator(validatorIndex) {
106
+ const cached = this.validators.get(validatorIndex);
107
+ if (cached) {
108
+ return cached;
109
+ }
110
+ const attesters = this.keystoreManager.createAttesterSigners(validatorIndex);
111
+ const publishers = this.keystoreManager.createPublisherSigners(validatorIndex);
112
+ // Build 'all' + indices
113
+ const byAddress = new Map();
114
+ const attesterSet = new Set();
115
+ for (const s of attesters){
116
+ const k = NodeKeystoreAdapter.key(s.address);
117
+ byAddress.set(k, s);
118
+ attesterSet.add(k);
119
+ }
120
+ for (const s of publishers){
121
+ const k = NodeKeystoreAdapter.key(s.address);
122
+ if (!byAddress.has(k)) {
123
+ byAddress.set(k, s);
124
+ }
125
+ }
126
+ const all = Array.from(byAddress.values());
127
+ // Populate global index
128
+ for (const [k, signer] of byAddress.entries()){
129
+ this.addressIndex.set(k, {
130
+ signer,
131
+ validatorIndex
132
+ });
133
+ }
134
+ const built = {
135
+ attesters,
136
+ publishers,
137
+ all,
138
+ byAddress,
139
+ attesterSet
140
+ };
141
+ this.validators.set(validatorIndex, built);
142
+ return built;
143
+ }
144
+ /**
145
+ * Iterate all validator indices in the keystore manager.
146
+ */ *validatorIndices() {
147
+ const n = this.keystoreManager.getValidatorCount();
148
+ for(let i = 0; i < n; i++){
149
+ yield i;
150
+ }
151
+ }
152
+ /**
153
+ * Find the validator index that contains the given attester address.
154
+ * @param attesterAddress Address to locate
155
+ * @returns Validator index
156
+ * @throws Error when no validator contains the attester
157
+ */ findValidatorIndexForAttester(attesterAddress) {
158
+ const key = NodeKeystoreAdapter.key(attesterAddress);
159
+ // Fast path: if we’ve already cached any validator that includes this as attester
160
+ for (const i of this.validatorIndices()){
161
+ const v = this.ensureValidator(i);
162
+ if (v.attesterSet.has(key)) {
163
+ return i;
164
+ }
165
+ }
166
+ throw new Error(`Attester address ${attesterAddress.toString()} not found in any validator configuration`);
167
+ }
168
+ /**
169
+ * Get attester address by flat index across all validators' attester sets.
170
+ * @param index Zero-based flat index across all attesters
171
+ * @returns EthAddress for the indexed attester
172
+ * @throws Error when index is out of bounds
173
+ */ getAddress(index) {
174
+ const all = this.getAddresses();
175
+ if (index < 0 || index >= all.length) {
176
+ throw new Error(`Index ${index} is out of bounds (0..${all.length - 1}).`);
177
+ }
178
+ return all[index];
179
+ }
180
+ /**
181
+ * Get all attester addresses across validators (legacy-compatible view).
182
+ */ getAddresses() {
183
+ const out = [];
184
+ for (const i of this.validatorIndices()){
185
+ const v = this.ensureValidator(i);
186
+ // attester addresses only for backward compatibility
187
+ for (const s of v.attesters){
188
+ out.push(s.address);
189
+ }
190
+ }
191
+ return out;
192
+ }
193
+ /**
194
+ * Sign typed data with all attester signers across validators.
195
+ * @param typedData EIP-712 typed data
196
+ * @param _context Signing context (ignored by NodeKeystoreAdapter, used for HA protection)
197
+ * @returns Array of signatures in validator order, flattened
198
+ */ async signTypedData(typedData, _context) {
199
+ const jobs = [];
200
+ for (const i of this.validatorIndices()){
201
+ const v = this.ensureValidator(i);
202
+ for (const s of v.attesters){
203
+ jobs.push(this.keystoreManager.signTypedData(s, typedData));
204
+ }
205
+ }
206
+ return await Promise.all(jobs);
207
+ }
208
+ /**
209
+ * Sign a message with all attester signers across validators.
210
+ * @param message 32-byte message (already hashed/padded as needed)
211
+ * @param _context Signing context (ignored by NodeKeystoreAdapter, used for HA protection)
212
+ * @returns Array of signatures in validator order, flattened
213
+ */ async signMessage(message, _context) {
214
+ const jobs = [];
215
+ for (const i of this.validatorIndices()){
216
+ const v = this.ensureValidator(i);
217
+ for (const s of v.attesters){
218
+ jobs.push(this.keystoreManager.signMessage(s, message));
219
+ }
220
+ }
221
+ return await Promise.all(jobs);
222
+ }
223
+ /**
224
+ * Sign typed data with a signer identified by address (any role).
225
+ * Hydrates caches on-demand when the address is first seen.
226
+ * @param address Address to sign with
227
+ * @param typedData EIP-712 typed data
228
+ * @param _context Signing context (ignored by NodeKeystoreAdapter, used for HA protection)
229
+ * @returns Signature from the signer matching the address
230
+ * @throws Error when no signer exists for the address
231
+ */ async signTypedDataWithAddress(address, typedData, _context) {
232
+ const entry = this.addressIndex.get(NodeKeystoreAdapter.key(address));
233
+ if (entry) {
234
+ return await this.keystoreManager.signTypedData(entry.signer, typedData);
235
+ }
236
+ // If not in global index yet, lazily hydrate all validators once and retry
237
+ for (const i of this.validatorIndices()){
238
+ this.ensureValidator(i);
239
+ }
240
+ const second = this.addressIndex.get(NodeKeystoreAdapter.key(address));
241
+ if (second) {
242
+ return await this.keystoreManager.signTypedData(second.signer, typedData);
243
+ }
244
+ throw new Error(`No signer found for address ${address.toString()}`);
245
+ }
246
+ /**
247
+ * Sign a message with a signer identified by address (any role).
248
+ * Hydrates caches on-demand when the address is first seen.
249
+ * @param address Address to sign with
250
+ * @param message 32-byte message
251
+ * @param _context Signing context (ignored by NodeKeystoreAdapter, used for HA protection)
252
+ * @returns Signature from the signer matching the address
253
+ * @throws Error when no signer exists for the address
254
+ */ async signMessageWithAddress(address, message, _context) {
255
+ const entry = this.addressIndex.get(NodeKeystoreAdapter.key(address));
256
+ if (entry) {
257
+ return await this.keystoreManager.signMessage(entry.signer, message);
258
+ }
259
+ for (const i of this.validatorIndices()){
260
+ this.ensureValidator(i);
261
+ }
262
+ const second = this.addressIndex.get(NodeKeystoreAdapter.key(address));
263
+ if (second) {
264
+ return await this.keystoreManager.signMessage(second.signer, message);
265
+ }
266
+ throw new Error(`No signer found for address ${address.toString()}`);
267
+ }
268
+ /**
269
+ * Get all attester addresses across validators (alias of getAddresses).
270
+ */ getAttesterAddresses() {
271
+ return this.getAddresses();
272
+ }
273
+ /**
274
+ * Get the effective coinbase address for the validator that contains the given attester.
275
+ * @param attesterAddress Address of an attester belonging to the validator
276
+ * @returns Coinbase EthAddress
277
+ */ getCoinbaseAddress(attesterAddress) {
278
+ const validatorIndex = this.findValidatorIndexForAttester(attesterAddress);
279
+ return this.keystoreManager.getCoinbaseAddress(validatorIndex, attesterAddress);
280
+ }
281
+ /**
282
+ * Get the publisher addresses for the validator that contains the given attester.
283
+ * @param attesterAddress Address of an attester belonging to the validator
284
+ * @returns Array of publisher addresses
285
+ */ getPublisherAddresses(attesterAddress) {
286
+ const validatorIndex = this.findValidatorIndexForAttester(attesterAddress);
287
+ const v = this.ensureValidator(validatorIndex);
288
+ return v.publishers.map((s)=>s.address);
289
+ }
290
+ getAttestorForPublisher(publisherAddress) {
291
+ const attestorAddresses = this.getAttesterAddresses();
292
+ for (const attestor of attestorAddresses){
293
+ const publishers = this.getPublisherAddresses(attestor);
294
+ const found = publishers.some((publisher)=>publisher.equals(publisherAddress));
295
+ if (found) {
296
+ return attestor;
297
+ }
298
+ }
299
+ // Could not find an attestor for this publisher
300
+ throw new Error(`Failed to find attestor for publisher ${publisherAddress.toString()}`);
301
+ }
302
+ /**
303
+ * Get the fee recipient for the validator that contains the given attester.
304
+ * @param attesterAddress Address of an attester belonging to the validator
305
+ * @returns Fee recipient as AztecAddress
306
+ */ getFeeRecipient(attesterAddress) {
307
+ const validatorIndex = this.findValidatorIndexForAttester(attesterAddress);
308
+ return this.keystoreManager.getFeeRecipient(validatorIndex);
309
+ }
310
+ /**
311
+ * Get the effective remote signer configuration for the attester.
312
+ * Precedence: account-level override > validator-level override > file-level default.
313
+ * Returns undefined for local signers (private key / JSON-V3 / mnemonic).
314
+ * @param attesterAddress Address of an attester belonging to the validator
315
+ * @returns Effective remote signer configuration or undefined
316
+ */ getRemoteSignerConfig(attesterAddress) {
317
+ const validatorIndex = this.findValidatorIndexForAttester(attesterAddress);
318
+ return this.keystoreManager.getEffectiveRemoteSignerConfig(validatorIndex, attesterAddress);
319
+ }
320
+ /**
321
+ * Start the key store - no-op
322
+ */ start() {
323
+ return Promise.resolve();
324
+ }
325
+ /**
326
+ * Stop the key store - no-op
327
+ */ stop() {
328
+ return Promise.resolve();
329
+ }
330
+ }
@@ -0,0 +1,66 @@
1
+ import type { Buffer32 } from '@aztec/foundation/buffer';
2
+ import { EthAddress } from '@aztec/foundation/eth-address';
3
+ import { Signature } from '@aztec/foundation/eth-signature';
4
+ import type { SigningContext } from '@aztec/validator-ha-signer/types';
5
+ import type { TypedDataDefinition } from 'viem';
6
+ import type { ValidatorKeyStore } from './interface.js';
7
+ /**
8
+ * Web3Signer Key Store
9
+ *
10
+ * An implementation of the Key store using Web3Signer remote signing service.
11
+ * This implementation uses the Web3Signer JSON-RPC API for secp256k1 signatures.
12
+ */
13
+ export declare class Web3SignerKeyStore implements ValidatorKeyStore {
14
+ private addresses;
15
+ private baseUrl;
16
+ constructor(addresses: EthAddress[], baseUrl: string);
17
+ /**
18
+ * Get the address of a signer by index
19
+ *
20
+ * @param index - The index of the signer
21
+ * @returns the address
22
+ */
23
+ getAddress(index: number): EthAddress;
24
+ /**
25
+ * Get all addresses
26
+ *
27
+ * @returns all addresses
28
+ */
29
+ getAddresses(): EthAddress[];
30
+ /**
31
+ * Sign EIP-712 typed data with all keystore addresses
32
+ * @param typedData - The complete EIP-712 typed data structure (domain, types, primaryType, message)
33
+ * @param _context - Signing context (ignored by Web3SignerKeyStore, used for HA protection)
34
+ * @return signatures
35
+ */
36
+ signTypedData(typedData: TypedDataDefinition, _context: SigningContext): Promise<Signature[]>;
37
+ /**
38
+ * Sign EIP-712 typed data with a specific address
39
+ * @param address - The address of the signer to use
40
+ * @param typedData - The complete EIP-712 typed data structure (domain, types, primaryType, message)
41
+ * @param _context - Signing context (ignored by Web3SignerKeyStore, used for HA protection)
42
+ * @returns signature for the specified address
43
+ * @throws Error if the address is not found in the keystore or signing fails
44
+ */
45
+ signTypedDataWithAddress(address: EthAddress, typedData: TypedDataDefinition, _context: SigningContext): Promise<Signature>;
46
+ /**
47
+ * Sign a message with all keystore addresses using EIP-191 prefix
48
+ *
49
+ * @param message - The message to sign
50
+ * @param _context - Signing context (ignored by Web3SignerKeyStore, used for HA protection)
51
+ * @return signatures
52
+ */
53
+ signMessage(message: Buffer32, _context: SigningContext): Promise<Signature[]>;
54
+ /**
55
+ * Sign a message with a specific address using EIP-191 prefix
56
+ * @param address - The address of the signer to use
57
+ * @param message - The message to sign
58
+ * @param _context - Signing context (ignored by Web3SignerKeyStore, used for HA protection)
59
+ * @returns signature for the specified address
60
+ * @throws Error if the address is not found in the keystore or signing fails
61
+ */
62
+ signMessageWithAddress(address: EthAddress, message: Buffer32, _context: SigningContext): Promise<Signature>;
63
+ private makeJsonRpcSignRequest;
64
+ private makeJsonRpcSignTypedDataRequest;
65
+ }
66
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2ViM3NpZ25lcl9rZXlfc3RvcmUuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9rZXlfc3RvcmUvd2ViM3NpZ25lcl9rZXlfc3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsUUFBUSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFFekQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQzNELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUM1RCxPQUFPLEtBQUssRUFBRSxjQUFjLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQUV2RSxPQUFPLEtBQUssRUFBRSxtQkFBbUIsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUVoRCxPQUFPLEtBQUssRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRXhEOzs7OztHQUtHO0FBQ0gscUJBQWEsa0JBQW1CLFlBQVcsaUJBQWlCO0lBRXhELE9BQU8sQ0FBQyxTQUFTO0lBQ2pCLE9BQU8sQ0FBQyxPQUFPO0lBRmpCLFlBQ1UsU0FBUyxFQUFFLFVBQVUsRUFBRSxFQUN2QixPQUFPLEVBQUUsTUFBTSxFQUNyQjtJQUVKOzs7OztPQUtHO0lBQ0ksVUFBVSxDQUFDLEtBQUssRUFBRSxNQUFNLEdBQUcsVUFBVSxDQUszQztJQUVEOzs7O09BSUc7SUFDSSxZQUFZLElBQUksVUFBVSxFQUFFLENBRWxDO0lBRUQ7Ozs7O09BS0c7SUFDSSxhQUFhLENBQUMsU0FBUyxFQUFFLG1CQUFtQixFQUFFLFFBQVEsRUFBRSxjQUFjLEdBQUcsT0FBTyxDQUFDLFNBQVMsRUFBRSxDQUFDLENBRW5HO0lBRUQ7Ozs7Ozs7T0FPRztJQUNVLHdCQUF3QixDQUNuQyxPQUFPLEVBQUUsVUFBVSxFQUNuQixTQUFTLEVBQUUsbUJBQW1CLEVBQzlCLFFBQVEsRUFBRSxjQUFjLEdBQ3ZCLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FNcEI7SUFFRDs7Ozs7O09BTUc7SUFDSSxXQUFXLENBQUMsT0FBTyxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsY0FBYyxHQUFHLE9BQU8sQ0FBQyxTQUFTLEVBQUUsQ0FBQyxDQUVwRjtJQUVEOzs7Ozs7O09BT0c7SUFDVSxzQkFBc0IsQ0FDakMsT0FBTyxFQUFFLFVBQVUsRUFDbkIsT0FBTyxFQUFFLFFBQVEsRUFDakIsUUFBUSxFQUFFLGNBQWMsR0FDdkIsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUtwQjtZQVFhLHNCQUFzQjtZQWlEdEIsK0JBQStCO0NBNkM5QyJ9
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web3signer_key_store.d.ts","sourceRoot":"","sources":["../../src/key_store/web3signer_key_store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAEvE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAEhD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD;;;;;GAKG;AACH,qBAAa,kBAAmB,YAAW,iBAAiB;IAExD,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,OAAO;IAFjB,YACU,SAAS,EAAE,UAAU,EAAE,EACvB,OAAO,EAAE,MAAM,EACrB;IAEJ;;;;;OAKG;IACI,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAK3C;IAED;;;;OAIG;IACI,YAAY,IAAI,UAAU,EAAE,CAElC;IAED;;;;;OAKG;IACI,aAAa,CAAC,SAAS,EAAE,mBAAmB,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAEnG;IAED;;;;;;;OAOG;IACU,wBAAwB,CACnC,OAAO,EAAE,UAAU,EACnB,SAAS,EAAE,mBAAmB,EAC9B,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,SAAS,CAAC,CAMpB;IAED;;;;;;OAMG;IACI,WAAW,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAEpF;IAED;;;;;;;OAOG;IACU,sBAAsB,CACjC,OAAO,EAAE,UAAU,EACnB,OAAO,EAAE,QAAQ,EACjB,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,SAAS,CAAC,CAKpB;YAQa,sBAAsB;YAiDtB,+BAA+B;CA6C9C"}
@@ -0,0 +1,156 @@
1
+ import { normalizeSignature } from '@aztec/foundation/crypto/secp256k1-signer';
2
+ import { Signature } from '@aztec/foundation/eth-signature';
3
+ /**
4
+ * Web3Signer Key Store
5
+ *
6
+ * An implementation of the Key store using Web3Signer remote signing service.
7
+ * This implementation uses the Web3Signer JSON-RPC API for secp256k1 signatures.
8
+ */ export class Web3SignerKeyStore {
9
+ addresses;
10
+ baseUrl;
11
+ constructor(addresses, baseUrl){
12
+ this.addresses = addresses;
13
+ this.baseUrl = baseUrl;
14
+ }
15
+ /**
16
+ * Get the address of a signer by index
17
+ *
18
+ * @param index - The index of the signer
19
+ * @returns the address
20
+ */ getAddress(index) {
21
+ if (index >= this.addresses.length) {
22
+ throw new Error(`Index ${index} is out of bounds.`);
23
+ }
24
+ return this.addresses[index];
25
+ }
26
+ /**
27
+ * Get all addresses
28
+ *
29
+ * @returns all addresses
30
+ */ getAddresses() {
31
+ return this.addresses;
32
+ }
33
+ /**
34
+ * Sign EIP-712 typed data with all keystore addresses
35
+ * @param typedData - The complete EIP-712 typed data structure (domain, types, primaryType, message)
36
+ * @param _context - Signing context (ignored by Web3SignerKeyStore, used for HA protection)
37
+ * @return signatures
38
+ */ signTypedData(typedData, _context) {
39
+ return Promise.all(this.addresses.map((address)=>this.makeJsonRpcSignTypedDataRequest(address, typedData)));
40
+ }
41
+ /**
42
+ * Sign EIP-712 typed data with a specific address
43
+ * @param address - The address of the signer to use
44
+ * @param typedData - The complete EIP-712 typed data structure (domain, types, primaryType, message)
45
+ * @param _context - Signing context (ignored by Web3SignerKeyStore, used for HA protection)
46
+ * @returns signature for the specified address
47
+ * @throws Error if the address is not found in the keystore or signing fails
48
+ */ async signTypedDataWithAddress(address, typedData, _context) {
49
+ if (!this.addresses.some((addr)=>addr.equals(address))) {
50
+ throw new Error(`Address ${address.toString()} not found in keystore`);
51
+ }
52
+ return await this.makeJsonRpcSignTypedDataRequest(address, typedData);
53
+ }
54
+ /**
55
+ * Sign a message with all keystore addresses using EIP-191 prefix
56
+ *
57
+ * @param message - The message to sign
58
+ * @param _context - Signing context (ignored by Web3SignerKeyStore, used for HA protection)
59
+ * @return signatures
60
+ */ signMessage(message, _context) {
61
+ return Promise.all(this.addresses.map((address)=>this.makeJsonRpcSignRequest(address, message)));
62
+ }
63
+ /**
64
+ * Sign a message with a specific address using EIP-191 prefix
65
+ * @param address - The address of the signer to use
66
+ * @param message - The message to sign
67
+ * @param _context - Signing context (ignored by Web3SignerKeyStore, used for HA protection)
68
+ * @returns signature for the specified address
69
+ * @throws Error if the address is not found in the keystore or signing fails
70
+ */ async signMessageWithAddress(address, message, _context) {
71
+ if (!this.addresses.some((addr)=>addr.equals(address))) {
72
+ throw new Error(`Address ${address.toString()} not found in keystore`);
73
+ }
74
+ return await this.makeJsonRpcSignRequest(address, message);
75
+ }
76
+ /**
77
+ * Make a JSON-RPC sign request to Web3Signer using eth_sign
78
+ * @param address - The Ethereum address to sign with
79
+ * @param data - The data to sign
80
+ * @returns The signature
81
+ */ async makeJsonRpcSignRequest(address, data) {
82
+ const url = this.baseUrl;
83
+ // Use JSON-RPC eth_sign method which automatically applies Ethereum message prefixing
84
+ const body = {
85
+ jsonrpc: '2.0',
86
+ method: 'eth_sign',
87
+ params: [
88
+ address.toString(),
89
+ data.toString()
90
+ ],
91
+ id: 1
92
+ };
93
+ const response = await fetch(url, {
94
+ method: 'POST',
95
+ headers: {
96
+ 'Content-Type': 'application/json'
97
+ },
98
+ body: JSON.stringify(body)
99
+ });
100
+ if (!response.ok) {
101
+ const errorText = await response.text();
102
+ throw new Error(`Web3Signer request failed: ${response.status} ${response.statusText} - ${errorText}`);
103
+ }
104
+ const result = await response.json();
105
+ // Handle JSON-RPC response format
106
+ if (result.error) {
107
+ throw new Error(`Web3Signer JSON-RPC error: ${result.error.code} - ${result.error.message}`);
108
+ }
109
+ if (!result.result) {
110
+ throw new Error('Invalid response from Web3Signer: no result found');
111
+ }
112
+ let signatureHex = result.result;
113
+ // Ensure the signature has the 0x prefix
114
+ if (!signatureHex.startsWith('0x')) {
115
+ signatureHex = '0x' + signatureHex;
116
+ }
117
+ // Parse the signature from the hex string
118
+ return normalizeSignature(Signature.fromString(signatureHex));
119
+ }
120
+ async makeJsonRpcSignTypedDataRequest(address, typedData) {
121
+ const url = this.baseUrl;
122
+ const body = {
123
+ jsonrpc: '2.0',
124
+ method: 'eth_signTypedData',
125
+ params: [
126
+ address.toString(),
127
+ JSON.stringify(typedData)
128
+ ],
129
+ id: 1
130
+ };
131
+ const response = await fetch(url, {
132
+ method: 'POST',
133
+ headers: {
134
+ 'Content-Type': 'application/json'
135
+ },
136
+ body: JSON.stringify(body)
137
+ });
138
+ if (!response.ok) {
139
+ const errorText = await response.text();
140
+ throw new Error(`Web3Signer request failed: ${response.status} ${response.statusText} - ${errorText}`);
141
+ }
142
+ const result = await response.json();
143
+ if (result.error) {
144
+ throw new Error(`Web3Signer JSON-RPC error: ${result.error.code} - ${result.error.message}`);
145
+ }
146
+ if (!result.result) {
147
+ throw new Error('Invalid response from Web3Signer: no result found');
148
+ }
149
+ let signatureHex = result.result;
150
+ // Ensure the signature has the 0x prefix
151
+ if (!signatureHex.startsWith('0x')) {
152
+ signatureHex = '0x' + signatureHex;
153
+ }
154
+ return normalizeSignature(Signature.fromString(signatureHex));
155
+ }
156
+ }
package/dest/metrics.d.ts CHANGED
@@ -1,11 +1,31 @@
1
+ import type { EpochNumber } from '@aztec/foundation/branded-types';
2
+ import type { EthAddress } from '@aztec/foundation/eth-address';
1
3
  import type { BlockProposal } from '@aztec/stdlib/p2p';
2
4
  import { type TelemetryClient } from '@aztec/telemetry-client';
5
+ import type { BlockProposalValidationFailureReason } from './proposal_handler.js';
3
6
  export declare class ValidatorMetrics {
4
- private reExecutionTime;
5
7
  private failedReexecutionCounter;
8
+ private successfulAttestationsCount;
9
+ private failedAttestationsBadProposalCount;
10
+ private failedAttestationsNodeIssueCount;
11
+ private currentEpoch;
12
+ private attestedEpochCount;
13
+ private reexMana;
14
+ private reexTx;
15
+ private reexDuration;
16
+ private checkpointProposalToPipelinedStateDuration;
17
+ private checkpointProposalReceiveOffsetFromNextSlotBoundary;
6
18
  constructor(telemetryClient: TelemetryClient);
7
- reExecutionTimer(): () => void;
8
- recordReExecutionTime(time: number): void;
9
- recordFailedReexecution(proposal: BlockProposal): Promise<void>;
19
+ recordReex(time: number, txs: number, mManaTotal: number): void;
20
+ recordCheckpointProposalToPipelinedStateDuration(durationMs: number): void;
21
+ recordCheckpointProposalReceiveOffsetFromNextSlotBoundary(offsetMs: number): void;
22
+ recordFailedReexecution(proposal: BlockProposal): void;
23
+ incSuccessfulAttestations(num: number): void;
24
+ incFailedAttestationsBadProposal(num: number, reason: BlockProposalValidationFailureReason, inCommittee: boolean): void;
25
+ incFailedAttestationsNodeIssue(num: number, reason: BlockProposalValidationFailureReason, inCommittee: boolean): void;
26
+ /** Update the gauge tracking the current epoch number (proxy for total epochs elapsed). */
27
+ setCurrentEpoch(epoch: EpochNumber): void;
28
+ /** Increment the count of epochs in which the given attester submitted at least one attestation. */
29
+ incAttestedEpochCount(attester: EthAddress): void;
10
30
  }
11
- //# sourceMappingURL=metrics.d.ts.map
31
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWV0cmljcy5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL21ldHJpY3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsV0FBVyxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDbkUsT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDaEUsT0FBTyxLQUFLLEVBQUUsYUFBYSxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDdkQsT0FBTyxFQUtMLEtBQUssZUFBZSxFQUdyQixNQUFNLHlCQUF5QixDQUFDO0FBRWpDLE9BQU8sS0FBSyxFQUFFLG9DQUFvQyxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFFbEYscUJBQWEsZ0JBQWdCO0lBQzNCLE9BQU8sQ0FBQyx3QkFBd0IsQ0FBZ0I7SUFDaEQsT0FBTyxDQUFDLDJCQUEyQixDQUFnQjtJQUNuRCxPQUFPLENBQUMsa0NBQWtDLENBQWdCO0lBQzFELE9BQU8sQ0FBQyxnQ0FBZ0MsQ0FBZ0I7SUFDeEQsT0FBTyxDQUFDLFlBQVksQ0FBUTtJQUM1QixPQUFPLENBQUMsa0JBQWtCLENBQWdCO0lBRTFDLE9BQU8sQ0FBQyxRQUFRLENBQVk7SUFDNUIsT0FBTyxDQUFDLE1BQU0sQ0FBWTtJQUMxQixPQUFPLENBQUMsWUFBWSxDQUFRO0lBQzVCLE9BQU8sQ0FBQywwQ0FBMEMsQ0FBWTtJQUM5RCxPQUFPLENBQUMsbURBQW1ELENBQVk7SUFFdkUsWUFBWSxlQUFlLEVBQUUsZUFBZSxFQTBEM0M7SUFFTSxVQUFVLENBQUMsSUFBSSxFQUFFLE1BQU0sRUFBRSxHQUFHLEVBQUUsTUFBTSxFQUFFLFVBQVUsRUFBRSxNQUFNLFFBSTlEO0lBRU0sZ0RBQWdELENBQUMsVUFBVSxFQUFFLE1BQU0sUUFFekU7SUFFTSx5REFBeUQsQ0FBQyxRQUFRLEVBQUUsTUFBTSxRQUloRjtJQUVNLHVCQUF1QixDQUFDLFFBQVEsRUFBRSxhQUFhLFFBTXJEO0lBRU0seUJBQXlCLENBQUMsR0FBRyxFQUFFLE1BQU0sUUFFM0M7SUFFTSxnQ0FBZ0MsQ0FDckMsR0FBRyxFQUFFLE1BQU0sRUFDWCxNQUFNLEVBQUUsb0NBQW9DLEVBQzVDLFdBQVcsRUFBRSxPQUFPLFFBTXJCO0lBRU0sOEJBQThCLENBQ25DLEdBQUcsRUFBRSxNQUFNLEVBQ1gsTUFBTSxFQUFFLG9DQUFvQyxFQUM1QyxXQUFXLEVBQUUsT0FBTyxRQU1yQjtJQUVELDJGQUEyRjtJQUNwRixlQUFlLENBQUMsS0FBSyxFQUFFLFdBQVcsUUFFeEM7SUFFRCxvR0FBb0c7SUFDN0YscUJBQXFCLENBQUMsUUFBUSxFQUFFLFVBQVUsUUFFaEQ7Q0FDRiJ9
@@ -1 +1 @@
1
- {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAIL,KAAK,eAAe,EAGrB,MAAM,yBAAyB,CAAC;AAEjC,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,wBAAwB,CAAgB;gBAEpC,eAAe,EAAE,eAAe;IAgBrC,gBAAgB,IAAI,MAAM,IAAI;IAQ9B,qBAAqB,CAAC,IAAI,EAAE,MAAM;IAI5B,uBAAuB,CAAC,QAAQ,EAAE,aAAa;CAO7D"}
1
+ {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAKL,KAAK,eAAe,EAGrB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,uBAAuB,CAAC;AAElF,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,wBAAwB,CAAgB;IAChD,OAAO,CAAC,2BAA2B,CAAgB;IACnD,OAAO,CAAC,kCAAkC,CAAgB;IAC1D,OAAO,CAAC,gCAAgC,CAAgB;IACxD,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,kBAAkB,CAAgB;IAE1C,OAAO,CAAC,QAAQ,CAAY;IAC5B,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,0CAA0C,CAAY;IAC9D,OAAO,CAAC,mDAAmD,CAAY;IAEvE,YAAY,eAAe,EAAE,eAAe,EA0D3C;IAEM,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAI9D;IAEM,gDAAgD,CAAC,UAAU,EAAE,MAAM,QAEzE;IAEM,yDAAyD,CAAC,QAAQ,EAAE,MAAM,QAIhF;IAEM,uBAAuB,CAAC,QAAQ,EAAE,aAAa,QAMrD;IAEM,yBAAyB,CAAC,GAAG,EAAE,MAAM,QAE3C;IAEM,gCAAgC,CACrC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,oCAAoC,EAC5C,WAAW,EAAE,OAAO,QAMrB;IAEM,8BAA8B,CACnC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,oCAAoC,EAC5C,WAAW,EAAE,OAAO,QAMrB;IAED,2FAA2F;IACpF,eAAe,CAAC,KAAK,EAAE,WAAW,QAExC;IAED,oGAAoG;IAC7F,qBAAqB,CAAC,QAAQ,EAAE,UAAU,QAEhD;CACF"}