@aztec/validator-ha-signer 0.0.1-commit.7d4e6cd → 0.0.1-commit.7ffbba4

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 (58) hide show
  1. package/README.md +50 -37
  2. package/dest/db/index.d.ts +2 -1
  3. package/dest/db/index.d.ts.map +1 -1
  4. package/dest/db/index.js +1 -0
  5. package/dest/db/lmdb.d.ts +66 -0
  6. package/dest/db/lmdb.d.ts.map +1 -0
  7. package/dest/db/lmdb.js +188 -0
  8. package/dest/db/postgres.d.ts +37 -6
  9. package/dest/db/postgres.d.ts.map +1 -1
  10. package/dest/db/postgres.js +86 -28
  11. package/dest/db/schema.d.ts +21 -10
  12. package/dest/db/schema.d.ts.map +1 -1
  13. package/dest/db/schema.js +49 -20
  14. package/dest/db/types.d.ts +109 -33
  15. package/dest/db/types.d.ts.map +1 -1
  16. package/dest/db/types.js +57 -8
  17. package/dest/errors.d.ts +9 -5
  18. package/dest/errors.d.ts.map +1 -1
  19. package/dest/errors.js +7 -4
  20. package/dest/factory.d.ts +25 -15
  21. package/dest/factory.d.ts.map +1 -1
  22. package/dest/factory.js +55 -15
  23. package/dest/metrics.d.ts +51 -0
  24. package/dest/metrics.d.ts.map +1 -0
  25. package/dest/metrics.js +103 -0
  26. package/dest/migrations.d.ts +1 -1
  27. package/dest/migrations.d.ts.map +1 -1
  28. package/dest/migrations.js +13 -2
  29. package/dest/slashing_protection_service.d.ts +25 -6
  30. package/dest/slashing_protection_service.d.ts.map +1 -1
  31. package/dest/slashing_protection_service.js +72 -20
  32. package/dest/test/pglite_pool.d.ts +92 -0
  33. package/dest/test/pglite_pool.d.ts.map +1 -0
  34. package/dest/test/pglite_pool.js +210 -0
  35. package/dest/types.d.ts +40 -16
  36. package/dest/types.d.ts.map +1 -1
  37. package/dest/types.js +4 -1
  38. package/dest/validator_ha_signer.d.ts +18 -13
  39. package/dest/validator_ha_signer.d.ts.map +1 -1
  40. package/dest/validator_ha_signer.js +45 -36
  41. package/package.json +15 -10
  42. package/src/db/index.ts +1 -0
  43. package/src/db/lmdb.ts +264 -0
  44. package/src/db/postgres.ts +109 -27
  45. package/src/db/schema.ts +51 -20
  46. package/src/db/types.ts +166 -32
  47. package/src/errors.ts +7 -2
  48. package/src/factory.ts +67 -15
  49. package/src/metrics.ts +138 -0
  50. package/src/migrations.ts +17 -1
  51. package/src/slashing_protection_service.ts +117 -25
  52. package/src/test/pglite_pool.ts +256 -0
  53. package/src/types.ts +65 -16
  54. package/src/validator_ha_signer.ts +64 -45
  55. package/dest/config.d.ts +0 -47
  56. package/dest/config.d.ts.map +0 -1
  57. package/dest/config.js +0 -64
  58. package/src/config.ts +0 -116
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Vendored pg-compatible Pool/Client wrapper for PGlite.
3
+ *
4
+ * Copied from @middle-management/pglite-pg-adapter v0.0.3
5
+ * https://www.npmjs.com/package/@middle-management/pglite-pg-adapter
6
+ *
7
+ * Modifications:
8
+ * - Converted to ESM and TypeScript
9
+ * - Uses PGliteInterface instead of PGlite class to avoid TypeScript
10
+ * type mismatches from ESM/CJS dual package resolution with private fields
11
+ * - Simplified rowCount calculation to handle CTEs properly
12
+ */ import { EventEmitter } from 'events';
13
+ import { Readable, Writable } from 'stream';
14
+ export class Client extends EventEmitter {
15
+ pglite;
16
+ _connected = false;
17
+ host;
18
+ port;
19
+ ssl;
20
+ connection;
21
+ // Stub implementations for pg compatibility
22
+ copyFrom = ()=>new Writable();
23
+ copyTo = ()=>new Readable();
24
+ pauseDrain = ()=>{};
25
+ resumeDrain = ()=>{};
26
+ escapeLiteral = (str)=>`'${str.replace(/'/g, "''")}'`;
27
+ escapeIdentifier = (str)=>`"${str.replace(/"/g, '""')}"`;
28
+ setTypeParser = ()=>{};
29
+ getTypeParser = ()=>(value)=>value;
30
+ constructor(config){
31
+ super();
32
+ this.pglite = config.pglite;
33
+ this.host = config.host || 'localhost';
34
+ this.port = config.port || 5432;
35
+ this.ssl = typeof config.ssl === 'boolean' ? config.ssl : !!config.ssl;
36
+ this.connection = {};
37
+ }
38
+ connect() {
39
+ if (this._connected) {
40
+ return Promise.resolve();
41
+ }
42
+ this._connected = true;
43
+ this.emit('connect');
44
+ return Promise.resolve();
45
+ }
46
+ end() {
47
+ if (!this._connected) {
48
+ return Promise.resolve();
49
+ }
50
+ this._connected = false;
51
+ this.emit('end');
52
+ return Promise.resolve();
53
+ }
54
+ async query(text, values) {
55
+ if (!this._connected) {
56
+ throw new Error('Client is not connected');
57
+ }
58
+ const result = await this.pglite.query(text, values);
59
+ return this.convertPGliteResult(result);
60
+ }
61
+ convertPGliteResult(result) {
62
+ return {
63
+ command: '',
64
+ rowCount: 'affectedRows' in result ? result.affectedRows ?? 0 : result.rows.length,
65
+ oid: 0,
66
+ fields: result.fields.map((field)=>({
67
+ name: field.name,
68
+ tableID: 0,
69
+ columnID: 0,
70
+ dataTypeID: field.dataTypeID,
71
+ dataTypeSize: -1,
72
+ dataTypeModifier: -1,
73
+ format: 'text'
74
+ })),
75
+ rows: result.rows
76
+ };
77
+ }
78
+ get connected() {
79
+ return this._connected;
80
+ }
81
+ }
82
+ export class Pool extends EventEmitter {
83
+ clients = [];
84
+ availableClients = [];
85
+ waitingQueue = [];
86
+ _ended = false;
87
+ pglite;
88
+ _config;
89
+ expiredCount = 0;
90
+ options;
91
+ constructor(config){
92
+ super();
93
+ this._config = {
94
+ max: 10,
95
+ min: 0,
96
+ ...config
97
+ };
98
+ this.pglite = config.pglite;
99
+ this.options = config;
100
+ }
101
+ get totalCount() {
102
+ return this.clients.length;
103
+ }
104
+ get idleCount() {
105
+ return this.availableClients.length;
106
+ }
107
+ get waitingCount() {
108
+ return this.waitingQueue.length;
109
+ }
110
+ get ending() {
111
+ return this._ended;
112
+ }
113
+ get ended() {
114
+ return this._ended;
115
+ }
116
+ connect() {
117
+ if (this._ended) {
118
+ return Promise.reject(new Error('Pool is ended'));
119
+ }
120
+ if (this.availableClients.length > 0) {
121
+ const client = this.availableClients.pop();
122
+ client._markInUse();
123
+ return Promise.resolve(client);
124
+ }
125
+ if (this.clients.length < (this._config.max || 10)) {
126
+ const client = new PoolClient(this.pglite, this);
127
+ this.clients.push(client);
128
+ return Promise.resolve(client);
129
+ }
130
+ return new Promise((resolve)=>{
131
+ this.waitingQueue.push(resolve);
132
+ });
133
+ }
134
+ async query(text, values) {
135
+ const client = await this.connect();
136
+ try {
137
+ return await client.query(text, values);
138
+ } finally{
139
+ client.release();
140
+ }
141
+ }
142
+ releaseClient(client) {
143
+ const index = this.clients.indexOf(client);
144
+ if (index !== -1) {
145
+ client._markAvailable();
146
+ if (this.waitingQueue.length > 0) {
147
+ const resolve = this.waitingQueue.shift();
148
+ client._markInUse();
149
+ resolve(client);
150
+ } else {
151
+ this.availableClients.push(client);
152
+ }
153
+ }
154
+ }
155
+ end() {
156
+ this._ended = true;
157
+ this.clients.forEach((client)=>client._markReleased());
158
+ this.clients = [];
159
+ this.availableClients = [];
160
+ this.emit('end');
161
+ return Promise.resolve();
162
+ }
163
+ }
164
+ export class PoolClient extends Client {
165
+ pool;
166
+ _released = false;
167
+ _inUse = true;
168
+ _userReleased = false;
169
+ constructor(pglite, pool){
170
+ super({
171
+ pglite
172
+ });
173
+ this.pool = pool;
174
+ this._connected = true;
175
+ }
176
+ async query(text, values) {
177
+ if (this._userReleased && !this._inUse) {
178
+ throw new Error('Client has been released back to the pool');
179
+ }
180
+ const result = await this.pglite.query(text, values);
181
+ return this.convertPGliteResult(result);
182
+ }
183
+ release() {
184
+ if (this._released || this._userReleased) {
185
+ return;
186
+ }
187
+ this._userReleased = true;
188
+ this.pool.releaseClient(this);
189
+ }
190
+ end() {
191
+ this.release();
192
+ return Promise.resolve();
193
+ }
194
+ _markInUse() {
195
+ this._inUse = true;
196
+ this._userReleased = false;
197
+ }
198
+ _markAvailable() {
199
+ this._inUse = false;
200
+ this._userReleased = false;
201
+ }
202
+ _markReleased() {
203
+ this._released = true;
204
+ this._inUse = false;
205
+ this._userReleased = true;
206
+ }
207
+ get connected() {
208
+ return this._connected && !this._released;
209
+ }
210
+ }
package/dest/types.d.ts CHANGED
@@ -1,9 +1,14 @@
1
+ import { SlotNumber } from '@aztec/foundation/branded-types';
1
2
  import type { EthAddress } from '@aztec/foundation/eth-address';
3
+ import { DateProvider } from '@aztec/foundation/timer';
4
+ import { DutyType, type HAProtectedSigningContext, type SigningContext, type ValidatorHASignerConfig, getBlockNumberFromSigningContext as getBlockNumberFromSigningContextFromStdlib, isHAProtectedContext } from '@aztec/stdlib/ha-signing';
5
+ import type { TelemetryClient } from '@aztec/telemetry-client';
2
6
  import type { Pool } from 'pg';
3
- import type { CreateHASignerConfig, SlashingProtectionConfig } from './config.js';
4
- import type { CheckAndRecordParams, DeleteDutyParams, DutyIdentifier, DutyType, RecordSuccessParams, ValidatorDutyRecord } from './db/types.js';
5
- export type { CheckAndRecordParams, CreateHASignerConfig, DeleteDutyParams, DutyIdentifier, RecordSuccessParams, SlashingProtectionConfig, ValidatorDutyRecord, };
6
- export { DutyStatus, DutyType } from './db/types.js';
7
+ import type { BlockProposalDutyIdentifier, CheckAndRecordParams, DeleteDutyParams, DutyIdentifier, DutyRow, OtherDutyIdentifier, RecordSuccessParams, ValidatorDutyRecord } from './db/types.js';
8
+ export type { BlockProposalDutyIdentifier, CheckAndRecordParams, DeleteDutyParams, DutyIdentifier, DutyRow, HAProtectedSigningContext, OtherDutyIdentifier, RecordSuccessParams, SigningContext, ValidatorDutyRecord, ValidatorHASignerConfig, };
9
+ export { DutyStatus, DutyType, getBlockIndexFromDutyIdentifier, normalizeBlockIndex } from './db/types.js';
10
+ export { isHAProtectedContext };
11
+ export { getBlockNumberFromSigningContextFromStdlib as getBlockNumberFromSigningContext };
7
12
  /**
8
13
  * Result of tryInsertOrGetExisting operation
9
14
  */
@@ -22,18 +27,19 @@ export interface CreateHASignerDeps {
22
27
  * If provided, databaseUrl and poolConfig are ignored
23
28
  */
24
29
  pool?: Pool;
30
+ /**
31
+ * Optional telemetry client for metrics
32
+ */
33
+ telemetryClient?: TelemetryClient;
34
+ /**
35
+ * Optional date provider for timestamps
36
+ */
37
+ dateProvider?: DateProvider;
25
38
  }
26
39
  /**
27
- * Context required for slashing protection during signing operations
40
+ * deps for creating a local signing protection signer
28
41
  */
29
- export interface SigningContext {
30
- /** Slot number for this duty */
31
- slot: bigint;
32
- /** Block number for this duty */
33
- blockNumber: bigint;
34
- /** Type of duty being performed */
35
- dutyType: DutyType;
36
- }
42
+ export type CreateLocalSignerWithProtectionDeps = Omit<CreateHASignerDeps, 'pool'>;
37
43
  /**
38
44
  * Database interface for slashing protection operations
39
45
  * This abstraction allows for different database implementations (PostgreSQL, SQLite, etc.)
@@ -57,7 +63,7 @@ export interface SlashingProtectionDatabase {
57
63
  *
58
64
  * @returns true if the update succeeded, false if token didn't match or duty not found
59
65
  */
60
- updateDutySigned(validatorAddress: EthAddress, slot: bigint, dutyType: DutyType, signature: string, lockToken: string): Promise<boolean>;
66
+ updateDutySigned(rollupAddress: EthAddress, validatorAddress: EthAddress, slot: SlotNumber, dutyType: DutyType, signature: string, lockToken: string, blockIndexWithinCheckpoint: number): Promise<boolean>;
61
67
  /**
62
68
  * Delete a duty record.
63
69
  * Only succeeds if the lockToken matches (caller must be the one who created the duty).
@@ -65,11 +71,29 @@ export interface SlashingProtectionDatabase {
65
71
  *
66
72
  * @returns true if the delete succeeded, false if token didn't match or duty not found
67
73
  */
68
- deleteDuty(validatorAddress: EthAddress, slot: bigint, dutyType: DutyType, lockToken: string): Promise<boolean>;
74
+ deleteDuty(rollupAddress: EthAddress, validatorAddress: EthAddress, slot: SlotNumber, dutyType: DutyType, lockToken: string, blockIndexWithinCheckpoint: number): Promise<boolean>;
69
75
  /**
70
76
  * Cleanup own stuck duties
71
77
  * @returns the number of duties cleaned up
72
78
  */
73
79
  cleanupOwnStuckDuties(nodeId: string, maxAgeMs: number): Promise<number>;
80
+ /**
81
+ * Cleanup duties with outdated rollup address.
82
+ * Removes all duties where the rollup address doesn't match the current one.
83
+ * Used after a rollup upgrade to clean up duties for the old rollup.
84
+ * @returns the number of duties cleaned up
85
+ */
86
+ cleanupOutdatedRollupDuties(currentRollupAddress: EthAddress): Promise<number>;
87
+ /**
88
+ * Cleanup old signed duties.
89
+ * Removes only signed duties older than the specified age.
90
+ * @returns the number of duties cleaned up
91
+ */
92
+ cleanupOldDuties(maxAgeMs: number): Promise<number>;
93
+ /**
94
+ * Close the database connection.
95
+ * Should be called during graceful shutdown.
96
+ */
97
+ close(): Promise<void>;
74
98
  }
75
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy90eXBlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxVQUFVLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUVoRSxPQUFPLEtBQUssRUFBRSxJQUFJLEVBQUUsTUFBTSxJQUFJLENBQUM7QUFFL0IsT0FBTyxLQUFLLEVBQUUsb0JBQW9CLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDbEYsT0FBTyxLQUFLLEVBQ1Ysb0JBQW9CLEVBQ3BCLGdCQUFnQixFQUNoQixjQUFjLEVBQ2QsUUFBUSxFQUNSLG1CQUFtQixFQUNuQixtQkFBbUIsRUFDcEIsTUFBTSxlQUFlLENBQUM7QUFFdkIsWUFBWSxFQUNWLG9CQUFvQixFQUNwQixvQkFBb0IsRUFDcEIsZ0JBQWdCLEVBQ2hCLGNBQWMsRUFDZCxtQkFBbUIsRUFDbkIsd0JBQXdCLEVBQ3hCLG1CQUFtQixHQUNwQixDQUFDO0FBQ0YsT0FBTyxFQUFFLFVBQVUsRUFBRSxRQUFRLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFFckQ7O0dBRUc7QUFDSCxNQUFNLFdBQVcsb0JBQW9CO0lBQ25DLDJFQUEyRTtJQUMzRSxLQUFLLEVBQUUsT0FBTyxDQUFDO0lBQ2YscURBQXFEO0lBQ3JELE1BQU0sRUFBRSxtQkFBbUIsQ0FBQztDQUM3QjtBQUVEOztHQUVHO0FBQ0gsTUFBTSxXQUFXLGtCQUFrQjtJQUNqQzs7O09BR0c7SUFDSCxJQUFJLENBQUMsRUFBRSxJQUFJLENBQUM7Q0FDYjtBQUVEOztHQUVHO0FBQ0gsTUFBTSxXQUFXLGNBQWM7SUFDN0IsZ0NBQWdDO0lBQ2hDLElBQUksRUFBRSxNQUFNLENBQUM7SUFDYixpQ0FBaUM7SUFDakMsV0FBVyxFQUFFLE1BQU0sQ0FBQztJQUNwQixtQ0FBbUM7SUFDbkMsUUFBUSxFQUFFLFFBQVEsQ0FBQztDQUNwQjtBQUVEOzs7Ozs7OztHQVFHO0FBQ0gsTUFBTSxXQUFXLDBCQUEwQjtJQUN6Qzs7Ozs7T0FLRztJQUNILHNCQUFzQixDQUFDLE1BQU0sRUFBRSxvQkFBb0IsR0FBRyxPQUFPLENBQUMsb0JBQW9CLENBQUMsQ0FBQztJQUVwRjs7Ozs7T0FLRztJQUNILGdCQUFnQixDQUNkLGdCQUFnQixFQUFFLFVBQVUsRUFDNUIsSUFBSSxFQUFFLE1BQU0sRUFDWixRQUFRLEVBQUUsUUFBUSxFQUNsQixTQUFTLEVBQUUsTUFBTSxFQUNqQixTQUFTLEVBQUUsTUFBTSxHQUNoQixPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7SUFFcEI7Ozs7OztPQU1HO0lBQ0gsVUFBVSxDQUFDLGdCQUFnQixFQUFFLFVBQVUsRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLE1BQU0sR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7SUFFaEg7OztPQUdHO0lBQ0gscUJBQXFCLENBQUMsTUFBTSxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsTUFBTSxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQztDQUMxRSJ9
99
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy90eXBlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDN0QsT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDaEUsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ3ZELE9BQU8sRUFDTCxRQUFRLEVBQ1IsS0FBSyx5QkFBeUIsRUFDOUIsS0FBSyxjQUFjLEVBQ25CLEtBQUssdUJBQXVCLEVBQzVCLGdDQUFnQyxJQUFJLDBDQUEwQyxFQUM5RSxvQkFBb0IsRUFDckIsTUFBTSwwQkFBMEIsQ0FBQztBQUNsQyxPQUFPLEtBQUssRUFBRSxlQUFlLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUUvRCxPQUFPLEtBQUssRUFBRSxJQUFJLEVBQUUsTUFBTSxJQUFJLENBQUM7QUFFL0IsT0FBTyxLQUFLLEVBQ1YsMkJBQTJCLEVBQzNCLG9CQUFvQixFQUNwQixnQkFBZ0IsRUFDaEIsY0FBYyxFQUNkLE9BQU8sRUFDUCxtQkFBbUIsRUFDbkIsbUJBQW1CLEVBQ25CLG1CQUFtQixFQUNwQixNQUFNLGVBQWUsQ0FBQztBQUV2QixZQUFZLEVBQ1YsMkJBQTJCLEVBQzNCLG9CQUFvQixFQUNwQixnQkFBZ0IsRUFDaEIsY0FBYyxFQUNkLE9BQU8sRUFDUCx5QkFBeUIsRUFDekIsbUJBQW1CLEVBQ25CLG1CQUFtQixFQUNuQixjQUFjLEVBQ2QsbUJBQW1CLEVBQ25CLHVCQUF1QixHQUN4QixDQUFDO0FBQ0YsT0FBTyxFQUFFLFVBQVUsRUFBRSxRQUFRLEVBQUUsK0JBQStCLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDM0csT0FBTyxFQUFFLG9CQUFvQixFQUFFLENBQUM7QUFDaEMsT0FBTyxFQUFFLDBDQUEwQyxJQUFJLGdDQUFnQyxFQUFFLENBQUM7QUFFMUY7O0dBRUc7QUFDSCxNQUFNLFdBQVcsb0JBQW9CO0lBQ25DLDJFQUEyRTtJQUMzRSxLQUFLLEVBQUUsT0FBTyxDQUFDO0lBQ2YscURBQXFEO0lBQ3JELE1BQU0sRUFBRSxtQkFBbUIsQ0FBQztDQUM3QjtBQUVEOztHQUVHO0FBQ0gsTUFBTSxXQUFXLGtCQUFrQjtJQUNqQzs7O09BR0c7SUFDSCxJQUFJLENBQUMsRUFBRSxJQUFJLENBQUM7SUFDWjs7T0FFRztJQUNILGVBQWUsQ0FBQyxFQUFFLGVBQWUsQ0FBQztJQUNsQzs7T0FFRztJQUNILFlBQVksQ0FBQyxFQUFFLFlBQVksQ0FBQztDQUM3QjtBQUVEOztHQUVHO0FBQ0gsTUFBTSxNQUFNLG1DQUFtQyxHQUFHLElBQUksQ0FBQyxrQkFBa0IsRUFBRSxNQUFNLENBQUMsQ0FBQztBQUVuRjs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sV0FBVywwQkFBMEI7SUFDekM7Ozs7O09BS0c7SUFDSCxzQkFBc0IsQ0FBQyxNQUFNLEVBQUUsb0JBQW9CLEdBQUcsT0FBTyxDQUFDLG9CQUFvQixDQUFDLENBQUM7SUFFcEY7Ozs7O09BS0c7SUFDSCxnQkFBZ0IsQ0FDZCxhQUFhLEVBQUUsVUFBVSxFQUN6QixnQkFBZ0IsRUFBRSxVQUFVLEVBQzVCLElBQUksRUFBRSxVQUFVLEVBQ2hCLFFBQVEsRUFBRSxRQUFRLEVBQ2xCLFNBQVMsRUFBRSxNQUFNLEVBQ2pCLFNBQVMsRUFBRSxNQUFNLEVBQ2pCLDBCQUEwQixFQUFFLE1BQU0sR0FDakMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBRXBCOzs7Ozs7T0FNRztJQUNILFVBQVUsQ0FDUixhQUFhLEVBQUUsVUFBVSxFQUN6QixnQkFBZ0IsRUFBRSxVQUFVLEVBQzVCLElBQUksRUFBRSxVQUFVLEVBQ2hCLFFBQVEsRUFBRSxRQUFRLEVBQ2xCLFNBQVMsRUFBRSxNQUFNLEVBQ2pCLDBCQUEwQixFQUFFLE1BQU0sR0FDakMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBRXBCOzs7T0FHRztJQUNILHFCQUFxQixDQUFDLE1BQU0sRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUFFLE1BQU0sR0FBRyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUM7SUFFekU7Ozs7O09BS0c7SUFDSCwyQkFBMkIsQ0FBQyxvQkFBb0IsRUFBRSxVQUFVLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBRS9FOzs7O09BSUc7SUFDSCxnQkFBZ0IsQ0FBQyxRQUFRLEVBQUUsTUFBTSxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUVwRDs7O09BR0c7SUFDSCxLQUFLLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO0NBQ3hCIn0=
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAE/B,OAAO,KAAK,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAClF,OAAO,KAAK,EACV,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,QAAQ,EACR,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,eAAe,CAAC;AAEvB,YAAY,EACV,oBAAoB,EACpB,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,mBAAmB,EACnB,wBAAwB,EACxB,mBAAmB,GACpB,CAAC;AACF,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,2EAA2E;IAC3E,KAAK,EAAE,OAAO,CAAC;IACf,qDAAqD;IACrD,MAAM,EAAE,mBAAmB,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,IAAI,CAAC,EAAE,IAAI,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,mCAAmC;IACnC,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,0BAA0B;IACzC;;;;;OAKG;IACH,sBAAsB,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAEpF;;;;;OAKG;IACH,gBAAgB,CACd,gBAAgB,EAAE,UAAU,EAC5B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpB;;;;;;OAMG;IACH,UAAU,CAAC,gBAAgB,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEhH;;;OAGG;IACH,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1E"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EACL,QAAQ,EACR,KAAK,yBAAyB,EAC9B,KAAK,cAAc,EACnB,KAAK,uBAAuB,EAC5B,gCAAgC,IAAI,0CAA0C,EAC9E,oBAAoB,EACrB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAE/B,OAAO,KAAK,EACV,2BAA2B,EAC3B,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,OAAO,EACP,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,eAAe,CAAC;AAEvB,YAAY,EACV,2BAA2B,EAC3B,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,OAAO,EACP,yBAAyB,EACzB,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,uBAAuB,GACxB,CAAC;AACF,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,+BAA+B,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAC3G,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAChC,OAAO,EAAE,0CAA0C,IAAI,gCAAgC,EAAE,CAAC;AAE1F;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,2EAA2E;IAC3E,KAAK,EAAE,OAAO,CAAC;IACf,qDAAqD;IACrD,MAAM,EAAE,mBAAmB,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ;;OAEG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC;;OAEG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,MAAM,mCAAmC,GAAG,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;AAEnF;;;;;;;;GAQG;AACH,MAAM,WAAW,0BAA0B;IACzC;;;;;OAKG;IACH,sBAAsB,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAEpF;;;;;OAKG;IACH,gBAAgB,CACd,aAAa,EAAE,UAAU,EACzB,gBAAgB,EAAE,UAAU,EAC5B,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,0BAA0B,EAAE,MAAM,GACjC,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpB;;;;;;OAMG;IACH,UAAU,CACR,aAAa,EAAE,UAAU,EACzB,gBAAgB,EAAE,UAAU,EAC5B,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,MAAM,EACjB,0BAA0B,EAAE,MAAM,GACjC,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpB;;;OAGG;IACH,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEzE;;;;;OAKG;IACH,2BAA2B,CAAC,oBAAoB,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE/E;;;;OAIG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEpD;;;OAGG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB"}
package/dest/types.js CHANGED
@@ -1 +1,4 @@
1
- export { DutyStatus, DutyType } from './db/types.js';
1
+ import { getBlockNumberFromSigningContext as getBlockNumberFromSigningContextFromStdlib, isHAProtectedContext } from '@aztec/stdlib/ha-signing';
2
+ export { DutyStatus, DutyType, getBlockIndexFromDutyIdentifier, normalizeBlockIndex } from './db/types.js';
3
+ export { isHAProtectedContext };
4
+ export { getBlockNumberFromSigningContextFromStdlib as getBlockNumberFromSigningContext };
@@ -6,10 +6,16 @@
6
6
  * node will sign for a given duty (slot + duty type).
7
7
  */
8
8
  import type { Buffer32 } from '@aztec/foundation/buffer';
9
- import type { EthAddress } from '@aztec/foundation/eth-address';
9
+ import { EthAddress } from '@aztec/foundation/eth-address';
10
10
  import type { Signature } from '@aztec/foundation/eth-signature';
11
- import type { CreateHASignerConfig } from './config.js';
12
- import type { SigningContext, SlashingProtectionDatabase } from './types.js';
11
+ import type { DateProvider } from '@aztec/foundation/timer';
12
+ import { type BaseSignerConfig, type HAProtectedSigningContext } from '@aztec/stdlib/ha-signing';
13
+ import type { HASignerMetrics } from './metrics.js';
14
+ import type { SlashingProtectionDatabase } from './types.js';
15
+ export interface ValidatorHASignerDeps {
16
+ metrics: HASignerMetrics;
17
+ dateProvider: DateProvider;
18
+ }
13
19
  /**
14
20
  * Validator High Availability Signer
15
21
  *
@@ -33,7 +39,10 @@ export declare class ValidatorHASigner {
33
39
  private readonly config;
34
40
  private readonly log;
35
41
  private readonly slashingProtection;
36
- constructor(db: SlashingProtectionDatabase, config: CreateHASignerConfig);
42
+ private readonly rollupAddress;
43
+ private readonly dateProvider;
44
+ private readonly metrics;
45
+ constructor(db: SlashingProtectionDatabase, config: BaseSignerConfig, deps: ValidatorHASignerDeps);
37
46
  /**
38
47
  * Sign a message with slashing protection.
39
48
  *
@@ -44,18 +53,14 @@ export declare class ValidatorHASigner {
44
53
  *
45
54
  * @param validatorAddress - The validator's Ethereum address
46
55
  * @param messageHash - The hash to be signed
47
- * @param context - The signing context (slot, block number, duty type)
56
+ * @param context - The signing context (HA-protected duty types only)
48
57
  * @param signFn - Function that performs the actual signing
49
58
  * @returns The signature
50
59
  *
51
60
  * @throws DutyAlreadySignedError if the duty was already signed (expected in HA)
52
61
  * @throws SlashingProtectionError if attempting to sign different data for same slot (expected in HA)
53
62
  */
54
- signWithProtection(validatorAddress: EthAddress, messageHash: Buffer32, context: SigningContext, signFn: (messageHash: Buffer32) => Promise<Signature>): Promise<Signature>;
55
- /**
56
- * Check if slashing protection is enabled
57
- */
58
- get isEnabled(): boolean;
63
+ signWithProtection(validatorAddress: EthAddress, messageHash: Buffer32, context: HAProtectedSigningContext, signFn: (messageHash: Buffer32) => Promise<Signature>): Promise<Signature>;
59
64
  /**
60
65
  * Get the node ID for this signer
61
66
  */
@@ -64,11 +69,11 @@ export declare class ValidatorHASigner {
64
69
  * Start the HA signer background tasks (cleanup of stuck duties).
65
70
  * Should be called after construction and before signing operations.
66
71
  */
67
- start(): void;
72
+ start(): Promise<void>;
68
73
  /**
69
- * Stop the HA signer background tasks.
74
+ * Stop the HA signer background tasks and close database connection.
70
75
  * Should be called during graceful shutdown.
71
76
  */
72
77
  stop(): Promise<void>;
73
78
  }
74
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdG9yX2hhX3NpZ25lci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3ZhbGlkYXRvcl9oYV9zaWduZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7OztHQU1HO0FBQ0gsT0FBTyxLQUFLLEVBQUUsUUFBUSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDekQsT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDaEUsT0FBTyxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFHakUsT0FBTyxLQUFLLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFFeEQsT0FBTyxLQUFLLEVBQUUsY0FBYyxFQUFFLDBCQUEwQixFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRTdFOzs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FrQkc7QUFDSCxxQkFBYSxpQkFBaUI7SUFNMUIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxNQUFNO0lBTHpCLE9BQU8sQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFTO0lBQzdCLE9BQU8sQ0FBQyxRQUFRLENBQUMsa0JBQWtCLENBQXdDO0lBRTNFLFlBQ0UsRUFBRSxFQUFFLDBCQUEwQixFQUNiLE1BQU0sRUFBRSxvQkFBb0IsRUFnQjlDO0lBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7T0FnQkc7SUFDRyxrQkFBa0IsQ0FDdEIsZ0JBQWdCLEVBQUUsVUFBVSxFQUM1QixXQUFXLEVBQUUsUUFBUSxFQUNyQixPQUFPLEVBQUUsY0FBYyxFQUN2QixNQUFNLEVBQUUsQ0FBQyxXQUFXLEVBQUUsUUFBUSxLQUFLLE9BQU8sQ0FBQyxTQUFTLENBQUMsR0FDcEQsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQW1EcEI7SUFFRDs7T0FFRztJQUNILElBQUksU0FBUyxJQUFJLE9BQU8sQ0FFdkI7SUFFRDs7T0FFRztJQUNILElBQUksTUFBTSxJQUFJLE1BQU0sQ0FFbkI7SUFFRDs7O09BR0c7SUFDSCxLQUFLLFNBRUo7SUFFRDs7O09BR0c7SUFDRyxJQUFJLGtCQUVUO0NBQ0YifQ==
79
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdG9yX2hhX3NpZ25lci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3ZhbGlkYXRvcl9oYV9zaWduZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7OztHQU1HO0FBQ0gsT0FBTyxLQUFLLEVBQUUsUUFBUSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDekQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQzNELE9BQU8sS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRWpFLE9BQU8sS0FBSyxFQUFFLFlBQVksRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQzVELE9BQU8sRUFDTCxLQUFLLGdCQUFnQixFQUVyQixLQUFLLHlCQUF5QixFQUUvQixNQUFNLDBCQUEwQixDQUFDO0FBR2xDLE9BQU8sS0FBSyxFQUFFLGVBQWUsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUVwRCxPQUFPLEtBQUssRUFBRSwwQkFBMEIsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUU3RCxNQUFNLFdBQVcscUJBQXFCO0lBQ3BDLE9BQU8sRUFBRSxlQUFlLENBQUM7SUFDekIsWUFBWSxFQUFFLFlBQVksQ0FBQztDQUM1QjtBQUVEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FrQkc7QUFDSCxxQkFBYSxpQkFBaUI7SUFVMUIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxNQUFNO0lBVHpCLE9BQU8sQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFTO0lBQzdCLE9BQU8sQ0FBQyxRQUFRLENBQUMsa0JBQWtCLENBQTRCO0lBQy9ELE9BQU8sQ0FBQyxRQUFRLENBQUMsYUFBYSxDQUFhO0lBRTNDLE9BQU8sQ0FBQyxRQUFRLENBQUMsWUFBWSxDQUFlO0lBQzVDLE9BQU8sQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFrQjtJQUUxQyxZQUNFLEVBQUUsRUFBRSwwQkFBMEIsRUFDYixNQUFNLEVBQUUsZ0JBQWdCLEVBQ3pDLElBQUksRUFBRSxxQkFBcUIsRUFtQjVCO0lBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7T0FnQkc7SUFDRyxrQkFBa0IsQ0FDdEIsZ0JBQWdCLEVBQUUsVUFBVSxFQUM1QixXQUFXLEVBQUUsUUFBUSxFQUNyQixPQUFPLEVBQUUseUJBQXlCLEVBQ2xDLE1BQU0sRUFBRSxDQUFDLFdBQVcsRUFBRSxRQUFRLEtBQUssT0FBTyxDQUFDLFNBQVMsQ0FBQyxHQUNwRCxPQUFPLENBQUMsU0FBUyxDQUFDLENBdURwQjtJQUVEOztPQUVHO0lBQ0gsSUFBSSxNQUFNLElBQUksTUFBTSxDQUVuQjtJQUVEOzs7T0FHRztJQUNHLEtBQUssa0JBRVY7SUFFRDs7O09BR0c7SUFDRyxJQUFJLGtCQUdUO0NBQ0YifQ==
@@ -1 +1 @@
1
- {"version":3,"file":"validator_ha_signer.d.ts","sourceRoot":"","sources":["../src/validator_ha_signer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAGjE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,KAAK,EAAE,cAAc,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAE7E;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,iBAAiB;IAM1B,OAAO,CAAC,QAAQ,CAAC,MAAM;IALzB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAwC;IAE3E,YACE,EAAE,EAAE,0BAA0B,EACb,MAAM,EAAE,oBAAoB,EAgB9C;IAED;;;;;;;;;;;;;;;;OAgBG;IACG,kBAAkB,CACtB,gBAAgB,EAAE,UAAU,EAC5B,WAAW,EAAE,QAAQ,EACrB,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,CAAC,WAAW,EAAE,QAAQ,KAAK,OAAO,CAAC,SAAS,CAAC,GACpD,OAAO,CAAC,SAAS,CAAC,CAmDpB;IAED;;OAEG;IACH,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED;;OAEG;IACH,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED;;;OAGG;IACH,KAAK,SAEJ;IAED;;;OAGG;IACG,IAAI,kBAET;CACF"}
1
+ {"version":3,"file":"validator_ha_signer.d.ts","sourceRoot":"","sources":["../src/validator_ha_signer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAEjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EACL,KAAK,gBAAgB,EAErB,KAAK,yBAAyB,EAE/B,MAAM,0BAA0B,CAAC;AAGlC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAE7D,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,eAAe,CAAC;IACzB,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,iBAAiB;IAU1B,OAAO,CAAC,QAAQ,CAAC,MAAM;IATzB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA4B;IAC/D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAa;IAE3C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkB;IAE1C,YACE,EAAE,EAAE,0BAA0B,EACb,MAAM,EAAE,gBAAgB,EACzC,IAAI,EAAE,qBAAqB,EAmB5B;IAED;;;;;;;;;;;;;;;;OAgBG;IACG,kBAAkB,CACtB,gBAAgB,EAAE,UAAU,EAC5B,WAAW,EAAE,QAAQ,EACrB,OAAO,EAAE,yBAAyB,EAClC,MAAM,EAAE,CAAC,WAAW,EAAE,QAAQ,KAAK,OAAO,CAAC,SAAS,CAAC,GACpD,OAAO,CAAC,SAAS,CAAC,CAuDpB;IAED;;OAEG;IACH,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED;;;OAGG;IACG,KAAK,kBAEV;IAED;;;OAGG;IACG,IAAI,kBAGT;CACF"}
@@ -5,6 +5,7 @@
5
5
  * This ensures that even with multiple validator nodes running, only one
6
6
  * node will sign for a given duty (slot + duty type).
7
7
  */ import { createLogger } from '@aztec/foundation/log';
8
+ import { DutyType, getBlockNumberFromSigningContext } from '@aztec/stdlib/ha-signing';
8
9
  import { SlashingProtectionService } from './slashing_protection_service.js';
9
10
  /**
10
11
  * Validator High Availability Signer
@@ -28,19 +29,25 @@ import { SlashingProtectionService } from './slashing_protection_service.js';
28
29
  config;
29
30
  log;
30
31
  slashingProtection;
31
- constructor(db, config){
32
+ rollupAddress;
33
+ dateProvider;
34
+ metrics;
35
+ constructor(db, config, deps){
32
36
  this.config = config;
33
37
  this.log = createLogger('validator-ha-signer');
34
- if (!config.enabled) {
35
- // this shouldn't happen, the validator should use different signer for non-HA setups
36
- throw new Error('Validator HA Signer is not enabled in config');
37
- }
38
+ this.metrics = deps.metrics;
39
+ this.dateProvider = deps.dateProvider;
38
40
  if (!config.nodeId || config.nodeId === '') {
39
41
  throw new Error('NODE_ID is required for high-availability setups');
40
42
  }
41
- this.slashingProtection = new SlashingProtectionService(db, config);
43
+ this.rollupAddress = config.l1Contracts.rollupAddress;
44
+ this.slashingProtection = new SlashingProtectionService(db, config, {
45
+ metrics: deps.metrics,
46
+ dateProvider: deps.dateProvider
47
+ });
42
48
  this.log.info('Validator HA Signer initialized with slashing protection', {
43
- nodeId: config.nodeId
49
+ nodeId: config.nodeId,
50
+ rollupAddress: this.rollupAddress.toString()
44
51
  });
45
52
  }
46
53
  /**
@@ -53,31 +60,38 @@ import { SlashingProtectionService } from './slashing_protection_service.js';
53
60
  *
54
61
  * @param validatorAddress - The validator's Ethereum address
55
62
  * @param messageHash - The hash to be signed
56
- * @param context - The signing context (slot, block number, duty type)
63
+ * @param context - The signing context (HA-protected duty types only)
57
64
  * @param signFn - Function that performs the actual signing
58
65
  * @returns The signature
59
66
  *
60
67
  * @throws DutyAlreadySignedError if the duty was already signed (expected in HA)
61
68
  * @throws SlashingProtectionError if attempting to sign different data for same slot (expected in HA)
62
69
  */ async signWithProtection(validatorAddress, messageHash, context, signFn) {
63
- // If slashing protection is disabled, just sign directly
64
- if (!this.slashingProtection) {
65
- this.log.info('Signing without slashing protection enabled', {
66
- validatorAddress: validatorAddress.toString(),
67
- nodeId: this.config.nodeId,
68
- dutyType: context.dutyType,
70
+ const startTime = this.dateProvider.now();
71
+ const dutyType = context.dutyType;
72
+ let dutyIdentifier;
73
+ if (context.dutyType === DutyType.BLOCK_PROPOSAL) {
74
+ dutyIdentifier = {
75
+ rollupAddress: this.rollupAddress,
76
+ validatorAddress,
69
77
  slot: context.slot,
70
- blockNumber: context.blockNumber
71
- });
72
- return await signFn(messageHash);
78
+ blockIndexWithinCheckpoint: context.blockIndexWithinCheckpoint,
79
+ dutyType: context.dutyType
80
+ };
81
+ } else {
82
+ dutyIdentifier = {
83
+ rollupAddress: this.rollupAddress,
84
+ validatorAddress,
85
+ slot: context.slot,
86
+ dutyType: context.dutyType
87
+ };
73
88
  }
74
- const { slot, blockNumber, dutyType } = context;
75
89
  // Acquire lock and get the token for ownership verification
90
+ // DutyAlreadySignedError and SlashingProtectionError may be thrown here and are recorded in the service
91
+ const blockNumber = getBlockNumberFromSigningContext(context);
76
92
  const lockToken = await this.slashingProtection.checkAndRecord({
77
- validatorAddress,
78
- slot,
93
+ ...dutyIdentifier,
79
94
  blockNumber,
80
- dutyType,
81
95
  messageHash: messageHash.toString(),
82
96
  nodeId: this.config.nodeId
83
97
  });
@@ -88,30 +102,24 @@ import { SlashingProtectionService } from './slashing_protection_service.js';
88
102
  } catch (error) {
89
103
  // Delete duty to allow retry (only succeeds if we own the lock)
90
104
  await this.slashingProtection.deleteDuty({
91
- validatorAddress,
92
- slot,
93
- dutyType,
105
+ ...dutyIdentifier,
94
106
  lockToken
95
107
  });
108
+ this.metrics.recordSigningError(dutyType);
96
109
  throw error;
97
110
  }
98
111
  // Record success (only succeeds if we own the lock)
99
112
  await this.slashingProtection.recordSuccess({
100
- validatorAddress,
101
- slot,
102
- dutyType,
113
+ ...dutyIdentifier,
103
114
  signature,
104
115
  nodeId: this.config.nodeId,
105
116
  lockToken
106
117
  });
118
+ const duration = this.dateProvider.now() - startTime;
119
+ this.metrics.recordSigningSuccess(dutyType, duration);
107
120
  return signature;
108
121
  }
109
122
  /**
110
- * Check if slashing protection is enabled
111
- */ get isEnabled() {
112
- return this.slashingProtection !== undefined;
113
- }
114
- /**
115
123
  * Get the node ID for this signer
116
124
  */ get nodeId() {
117
125
  return this.config.nodeId;
@@ -119,13 +127,14 @@ import { SlashingProtectionService } from './slashing_protection_service.js';
119
127
  /**
120
128
  * Start the HA signer background tasks (cleanup of stuck duties).
121
129
  * Should be called after construction and before signing operations.
122
- */ start() {
123
- this.slashingProtection?.start();
130
+ */ async start() {
131
+ await this.slashingProtection.start();
124
132
  }
125
133
  /**
126
- * Stop the HA signer background tasks.
134
+ * Stop the HA signer background tasks and close database connection.
127
135
  * Should be called during graceful shutdown.
128
136
  */ async stop() {
129
- await this.slashingProtection?.stop();
137
+ await this.slashingProtection.stop();
138
+ await this.slashingProtection.close();
130
139
  }
131
140
  }
package/package.json CHANGED
@@ -1,23 +1,25 @@
1
1
  {
2
2
  "name": "@aztec/validator-ha-signer",
3
- "version": "0.0.1-commit.7d4e6cd",
3
+ "version": "0.0.1-commit.7ffbba4",
4
4
  "type": "module",
5
5
  "exports": {
6
- "./config": "./dest/config.js",
7
6
  "./db": "./dest/db/index.js",
8
7
  "./errors": "./dest/errors.js",
9
8
  "./factory": "./dest/factory.js",
9
+ "./metrics": "./dest/metrics.js",
10
10
  "./migrations": "./dest/migrations.js",
11
11
  "./slashing-protection-service": "./dest/slashing_protection_service.js",
12
12
  "./types": "./dest/types.js",
13
- "./validator-ha-signer": "./dest/validator_ha_signer.js"
13
+ "./validator-ha-signer": "./dest/validator_ha_signer.js",
14
+ "./test": "./dest/test/pglite_pool.js",
15
+ "./db/lmdb": "./dest/db/lmdb.js"
14
16
  },
15
17
  "typedocOptions": {
16
18
  "entryPoints": [
17
- "./src/config.ts",
18
19
  "./src/db/index.ts",
19
20
  "./src/errors.ts",
20
21
  "./src/factory.ts",
22
+ "./src/metrics.ts",
21
23
  "./src/migrations.ts",
22
24
  "./src/slashing_protection_service.ts",
23
25
  "./src/types.ts",
@@ -73,21 +75,24 @@
73
75
  ]
74
76
  },
75
77
  "dependencies": {
76
- "@aztec/foundation": "0.0.1-commit.7d4e6cd",
77
- "@aztec/node-keystore": "0.0.1-commit.7d4e6cd",
78
+ "@aztec/ethereum": "0.0.1-commit.7ffbba4",
79
+ "@aztec/foundation": "0.0.1-commit.7ffbba4",
80
+ "@aztec/kv-store": "0.0.1-commit.7ffbba4",
81
+ "@aztec/stdlib": "0.0.1-commit.7ffbba4",
82
+ "@aztec/telemetry-client": "0.0.1-commit.7ffbba4",
78
83
  "node-pg-migrate": "^8.0.4",
79
84
  "pg": "^8.11.3",
80
- "tslib": "^2.4.0"
85
+ "tslib": "^2.4.0",
86
+ "zod": "^3.23.8"
81
87
  },
82
88
  "devDependencies": {
83
- "@electric-sql/pglite": "^0.2.17",
89
+ "@electric-sql/pglite": "^0.3.14",
84
90
  "@jest/globals": "^30.0.0",
85
- "@middle-management/pglite-pg-adapter": "^0.0.3",
86
91
  "@types/jest": "^30.0.0",
87
92
  "@types/node": "^22.15.17",
88
93
  "@types/node-pg-migrate": "^2.3.1",
89
94
  "@types/pg": "^8.10.9",
90
- "@typescript/native-preview": "7.0.0-dev.20251126.1",
95
+ "@typescript/native-preview": "7.0.0-dev.20260113.1",
91
96
  "jest": "^30.0.0",
92
97
  "jest-mock-extended": "^4.0.0",
93
98
  "ts-node": "^10.9.1",
package/src/db/index.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './types.js';
2
2
  export * from './schema.js';
3
3
  export * from './postgres.js';
4
+ export * from './lmdb.js';