@aztec/validator-ha-signer 0.0.1-commit.23b0eb0 → 0.0.1-commit.2448fdb
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dest/config.d.ts +6 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +6 -0
- package/dest/db/in_memory.d.ts +20 -0
- package/dest/db/in_memory.d.ts.map +1 -0
- package/dest/db/in_memory.js +73 -0
- package/dest/db/postgres.d.ts +15 -1
- package/dest/db/postgres.d.ts.map +1 -1
- package/dest/db/postgres.js +24 -3
- package/dest/db/schema.d.ts +13 -6
- package/dest/db/schema.d.ts.map +1 -1
- package/dest/db/schema.js +14 -5
- package/dest/db/types.d.ts +2 -2
- package/dest/db/types.d.ts.map +1 -1
- package/dest/factory.d.ts +14 -1
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +25 -0
- package/dest/slashing_protection_service.d.ts +8 -4
- package/dest/slashing_protection_service.d.ts.map +1 -1
- package/dest/slashing_protection_service.js +39 -10
- package/dest/types.d.ts +14 -1
- package/dest/types.d.ts.map +1 -1
- package/dest/validator_ha_signer.d.ts +2 -2
- package/dest/validator_ha_signer.d.ts.map +1 -1
- package/dest/validator_ha_signer.js +2 -2
- package/package.json +3 -3
- package/src/config.ts +8 -0
- package/src/db/in_memory.ts +107 -0
- package/src/db/postgres.ts +25 -2
- package/src/db/schema.ts +16 -5
- package/src/db/types.ts +1 -1
- package/src/factory.ts +32 -0
- package/src/slashing_protection_service.ts +43 -14
- package/src/types.ts +15 -0
- package/src/validator_ha_signer.ts +2 -2
package/README.md
CHANGED
|
@@ -178,6 +178,16 @@ All signing operations require a `SigningContext` that includes:
|
|
|
178
178
|
|
|
179
179
|
Note: `AUTH_REQUEST` duties bypass HA protection since signing multiple times is safe for authentication requests.
|
|
180
180
|
|
|
181
|
+
## Important Limitations
|
|
182
|
+
|
|
183
|
+
### Database Isolation Per Rollup Version
|
|
184
|
+
|
|
185
|
+
**You cannot use the same database to provide slashing protection for validator nodes running on different rollup versions** (e.g., current rollup and old rollup simultaneously).
|
|
186
|
+
|
|
187
|
+
When the HA signer performs background cleanup via `cleanupOutdatedRollupDuties()`, it removes all duties where the rollup address doesn't match the current rollup address. If two validators running on different rollup versions share the same database, they will delete each other's duties during cleanup.
|
|
188
|
+
|
|
189
|
+
**Solution**: Use separate databases for validators running on different rollup versions. Each rollup version requires its own isolated slashing protection database.
|
|
190
|
+
|
|
181
191
|
## Development
|
|
182
192
|
|
|
183
193
|
```bash
|
package/dest/config.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ export interface ValidatorHASignerConfig {
|
|
|
21
21
|
signingTimeoutMs: number;
|
|
22
22
|
/** Maximum age of a stuck duty in ms (defaults to 2x hardcoded Aztec slot duration if not set) */
|
|
23
23
|
maxStuckDutiesAgeMs?: number;
|
|
24
|
+
/** Optional: clean up old duties after this many hours (disabled if not set) */
|
|
25
|
+
cleanupOldDutiesAfterHours?: number;
|
|
24
26
|
/**
|
|
25
27
|
* PostgreSQL connection string
|
|
26
28
|
* Format: postgresql://user:password@host:port/database
|
|
@@ -59,6 +61,7 @@ export declare const ValidatorHASignerConfigSchema: z.ZodObject<{
|
|
|
59
61
|
pollingIntervalMs: z.ZodNumber;
|
|
60
62
|
signingTimeoutMs: z.ZodNumber;
|
|
61
63
|
maxStuckDutiesAgeMs: z.ZodOptional<z.ZodNumber>;
|
|
64
|
+
cleanupOldDutiesAfterHours: z.ZodOptional<z.ZodNumber>;
|
|
62
65
|
databaseUrl: z.ZodOptional<z.ZodString>;
|
|
63
66
|
poolMaxCount: z.ZodOptional<z.ZodNumber>;
|
|
64
67
|
poolMinCount: z.ZodOptional<z.ZodNumber>;
|
|
@@ -73,6 +76,7 @@ export declare const ValidatorHASignerConfigSchema: z.ZodObject<{
|
|
|
73
76
|
pollingIntervalMs: number;
|
|
74
77
|
signingTimeoutMs: number;
|
|
75
78
|
maxStuckDutiesAgeMs?: number | undefined;
|
|
79
|
+
cleanupOldDutiesAfterHours?: number | undefined;
|
|
76
80
|
databaseUrl?: string | undefined;
|
|
77
81
|
poolMaxCount?: number | undefined;
|
|
78
82
|
poolMinCount?: number | undefined;
|
|
@@ -87,10 +91,11 @@ export declare const ValidatorHASignerConfigSchema: z.ZodObject<{
|
|
|
87
91
|
pollingIntervalMs: number;
|
|
88
92
|
signingTimeoutMs: number;
|
|
89
93
|
maxStuckDutiesAgeMs?: number | undefined;
|
|
94
|
+
cleanupOldDutiesAfterHours?: number | undefined;
|
|
90
95
|
databaseUrl?: string | undefined;
|
|
91
96
|
poolMaxCount?: number | undefined;
|
|
92
97
|
poolMinCount?: number | undefined;
|
|
93
98
|
poolIdleTimeoutMs?: number | undefined;
|
|
94
99
|
poolConnectionTimeoutMs?: number | undefined;
|
|
95
100
|
}>;
|
|
96
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
101
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvY29uZmlnLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sdUNBQXVDLENBQUM7QUFDakYsT0FBTyxFQUNMLEtBQUssa0JBQWtCLEVBTXhCLE1BQU0sMEJBQTBCLENBQUM7QUFDbEMsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBRzNELE9BQU8sRUFBRSxDQUFDLEVBQUUsTUFBTSxLQUFLLENBQUM7QUFFeEI7Ozs7O0dBS0c7QUFDSCxNQUFNLFdBQVcsdUJBQXVCO0lBQ3RDLDBEQUEwRDtJQUMxRCxnQkFBZ0IsRUFBRSxPQUFPLENBQUM7SUFDMUIsc0RBQXNEO0lBQ3RELFdBQVcsRUFBRSxJQUFJLENBQUMsbUJBQW1CLEVBQUUsZUFBZSxDQUFDLENBQUM7SUFDeEQsc0NBQXNDO0lBQ3RDLE1BQU0sRUFBRSxNQUFNLENBQUM7SUFDZixzRUFBc0U7SUFDdEUsaUJBQWlCLEVBQUUsTUFBTSxDQUFDO0lBQzFCLG9FQUFvRTtJQUNwRSxnQkFBZ0IsRUFBRSxNQUFNLENBQUM7SUFDekIsa0dBQWtHO0lBQ2xHLG1CQUFtQixDQUFDLEVBQUUsTUFBTSxDQUFDO0lBQzdCLGdGQUFnRjtJQUNoRiwwQkFBMEIsQ0FBQyxFQUFFLE1BQU0sQ0FBQztJQUNwQzs7O09BR0c7SUFDSCxXQUFXLENBQUMsRUFBRSxNQUFNLENBQUM7SUFDckI7O09BRUc7SUFDSCwwREFBMEQ7SUFDMUQsWUFBWSxDQUFDLEVBQUUsTUFBTSxDQUFDO0lBQ3RCLHlEQUF5RDtJQUN6RCxZQUFZLENBQUMsRUFBRSxNQUFNLENBQUM7SUFDdEIsb0RBQW9EO0lBQ3BELGlCQUFpQixDQUFDLEVBQUUsTUFBTSxDQUFDO0lBQzNCLGtFQUFrRTtJQUNsRSx1QkFBdUIsQ0FBQyxFQUFFLE1BQU0sQ0FBQztDQUNsQztBQUVELGVBQU8sTUFBTSwrQkFBK0IsRUFBRSxrQkFBa0IsQ0FBQyx1QkFBdUIsQ0FpRXZGLENBQUM7QUFFRixlQUFPLE1BQU0sOEJBQThCLEVBQUUsdUJBRTVDLENBQUM7QUFFRjs7OztHQUlHO0FBQ0gsd0JBQWdCLGdCQUFnQixJQUFJLHVCQUF1QixDQUUxRDtBQUVELGVBQU8sTUFBTSw2QkFBNkI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7RUFlRSxDQUFDIn0=
|
package/dest/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EACL,KAAK,kBAAkB,EAMxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAG3D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACtC,0DAA0D;IAC1D,gBAAgB,EAAE,OAAO,CAAC;IAC1B,sDAAsD;IACtD,WAAW,EAAE,IAAI,CAAC,mBAAmB,EAAE,eAAe,CAAC,CAAC;IACxD,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oEAAoE;IACpE,gBAAgB,EAAE,MAAM,CAAC;IACzB,kGAAkG;IAClG,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kEAAkE;IAClE,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,eAAO,MAAM,+BAA+B,EAAE,kBAAkB,CAAC,uBAAuB,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EACL,KAAK,kBAAkB,EAMxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAG3D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACtC,0DAA0D;IAC1D,gBAAgB,EAAE,OAAO,CAAC;IAC1B,sDAAsD;IACtD,WAAW,EAAE,IAAI,CAAC,mBAAmB,EAAE,eAAe,CAAC,CAAC;IACxD,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oEAAoE;IACpE,gBAAgB,EAAE,MAAM,CAAC;IACzB,kGAAkG;IAClG,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,gFAAgF;IAChF,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kEAAkE;IAClE,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,eAAO,MAAM,+BAA+B,EAAE,kBAAkB,CAAC,uBAAuB,CAiEvF,CAAC;AAEF,eAAO,MAAM,8BAA8B,EAAE,uBAE5C,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,uBAAuB,CAE1D;AAED,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAeE,CAAC"}
|
package/dest/config.js
CHANGED
|
@@ -36,6 +36,11 @@ export const validatorHASignerConfigMappings = {
|
|
|
36
36
|
description: 'The maximum age of a stuck duty in ms (defaults to 2x Aztec slot duration)',
|
|
37
37
|
...optionalNumberConfigHelper()
|
|
38
38
|
},
|
|
39
|
+
cleanupOldDutiesAfterHours: {
|
|
40
|
+
env: 'VALIDATOR_HA_OLD_DUTIES_MAX_AGE_H',
|
|
41
|
+
description: 'Optional: clean up old duties after this many hours (disabled if not set)',
|
|
42
|
+
...optionalNumberConfigHelper()
|
|
43
|
+
},
|
|
39
44
|
databaseUrl: {
|
|
40
45
|
env: 'VALIDATOR_HA_DATABASE_URL',
|
|
41
46
|
description: 'PostgreSQL connection string for validator HA signer (format: postgresql://user:password@host:port/database)'
|
|
@@ -78,6 +83,7 @@ export const ValidatorHASignerConfigSchema = z.object({
|
|
|
78
83
|
pollingIntervalMs: z.number().min(0),
|
|
79
84
|
signingTimeoutMs: z.number().min(0),
|
|
80
85
|
maxStuckDutiesAgeMs: z.number().min(0).optional(),
|
|
86
|
+
cleanupOldDutiesAfterHours: z.number().min(0).optional(),
|
|
81
87
|
databaseUrl: z.string().optional(),
|
|
82
88
|
poolMaxCount: z.number().min(0).optional(),
|
|
83
89
|
poolMinCount: z.number().min(0).optional(),
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory implementation of SlashingProtectionDatabase for testing.
|
|
3
|
+
* Used to simulate shared slashing protection in HA test setups without requiring PostgreSQL.
|
|
4
|
+
*/
|
|
5
|
+
import type { SlotNumber } from '@aztec/foundation/branded-types';
|
|
6
|
+
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
7
|
+
import type { SlashingProtectionDatabase, TryInsertOrGetResult } from '../types.js';
|
|
8
|
+
import type { CheckAndRecordParams, DutyType } from './types.js';
|
|
9
|
+
/** In-memory slashing protection database for testing HA setups. */
|
|
10
|
+
export declare class InMemorySlashingProtectionDatabase implements SlashingProtectionDatabase {
|
|
11
|
+
private duties;
|
|
12
|
+
tryInsertOrGetExisting(params: CheckAndRecordParams): Promise<TryInsertOrGetResult>;
|
|
13
|
+
updateDutySigned(rollupAddress: EthAddress, validatorAddress: EthAddress, slot: SlotNumber, dutyType: DutyType, signature: string, lockToken: string, blockIndexWithinCheckpoint: number): Promise<boolean>;
|
|
14
|
+
deleteDuty(rollupAddress: EthAddress, validatorAddress: EthAddress, slot: SlotNumber, dutyType: DutyType, lockToken: string, blockIndexWithinCheckpoint: number): Promise<boolean>;
|
|
15
|
+
cleanupOwnStuckDuties(_nodeId: string, _maxAgeMs: number): Promise<number>;
|
|
16
|
+
cleanupOutdatedRollupDuties(_currentRollupAddress: EthAddress): Promise<number>;
|
|
17
|
+
cleanupOldDuties(_maxAgeMs: number): Promise<number>;
|
|
18
|
+
close(): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5fbWVtb3J5LmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZGIvaW5fbWVtb3J5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUNILE9BQU8sS0FBSyxFQUFlLFVBQVUsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQy9FLE9BQU8sS0FBSyxFQUFFLFVBQVUsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBRWhFLE9BQU8sS0FBSyxFQUFFLDBCQUEwQixFQUFFLG9CQUFvQixFQUF1QixNQUFNLGFBQWEsQ0FBQztBQUN6RyxPQUFPLEtBQUssRUFBRSxvQkFBb0IsRUFBRSxRQUFRLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFjakUsb0VBQW9FO0FBQ3BFLHFCQUFhLGtDQUFtQyxZQUFXLDBCQUEwQjtJQUNuRixPQUFPLENBQUMsTUFBTSxDQUEwQztJQUV4RCxzQkFBc0IsQ0FBQyxNQUFNLEVBQUUsb0JBQW9CLEdBQUcsT0FBTyxDQUFDLG9CQUFvQixDQUFDLENBeUJsRjtJQUVELGdCQUFnQixDQUNkLGFBQWEsRUFBRSxVQUFVLEVBQ3pCLGdCQUFnQixFQUFFLFVBQVUsRUFDNUIsSUFBSSxFQUFFLFVBQVUsRUFDaEIsUUFBUSxFQUFFLFFBQVEsRUFDbEIsU0FBUyxFQUFFLE1BQU0sRUFDakIsU0FBUyxFQUFFLE1BQU0sRUFDakIsMEJBQTBCLEVBQUUsTUFBTSxHQUNqQyxPQUFPLENBQUMsT0FBTyxDQUFDLENBVWxCO0lBRUQsVUFBVSxDQUNSLGFBQWEsRUFBRSxVQUFVLEVBQ3pCLGdCQUFnQixFQUFFLFVBQVUsRUFDNUIsSUFBSSxFQUFFLFVBQVUsRUFDaEIsUUFBUSxFQUFFLFFBQVEsRUFDbEIsU0FBUyxFQUFFLE1BQU0sRUFDakIsMEJBQTBCLEVBQUUsTUFBTSxHQUNqQyxPQUFPLENBQUMsT0FBTyxDQUFDLENBUWxCO0lBRUQscUJBQXFCLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUUsTUFBTSxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FFekU7SUFFRCwyQkFBMkIsQ0FBQyxxQkFBcUIsRUFBRSxVQUFVLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUU5RTtJQUVELGdCQUFnQixDQUFDLFNBQVMsRUFBRSxNQUFNLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUVuRDtJQUVELEtBQUssSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLENBR3JCO0NBQ0YifQ==
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"in_memory.d.ts","sourceRoot":"","sources":["../../src/db/in_memory.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAe,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAC/E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,KAAK,EAAE,0BAA0B,EAAE,oBAAoB,EAAuB,MAAM,aAAa,CAAC;AACzG,OAAO,KAAK,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAcjE,oEAAoE;AACpE,qBAAa,kCAAmC,YAAW,0BAA0B;IACnF,OAAO,CAAC,MAAM,CAA0C;IAExD,sBAAsB,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAyBlF;IAED,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,CAUlB;IAED,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,CAQlB;IAED,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEzE;IAED,2BAA2B,CAAC,qBAAqB,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAE9E;IAED,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEnD;IAED,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAGrB;CACF"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory implementation of SlashingProtectionDatabase for testing.
|
|
3
|
+
* Used to simulate shared slashing protection in HA test setups without requiring PostgreSQL.
|
|
4
|
+
*/ import { DutyStatus, getBlockIndexFromDutyIdentifier } from './types.js';
|
|
5
|
+
/** Creates a unique key for a duty based on its identifying fields. */ function dutyKey(rollupAddress, validatorAddress, slot, dutyType, blockIndexWithinCheckpoint) {
|
|
6
|
+
return `${rollupAddress}:${validatorAddress}:${slot}:${dutyType}:${blockIndexWithinCheckpoint}`;
|
|
7
|
+
}
|
|
8
|
+
/** In-memory slashing protection database for testing HA setups. */ export class InMemorySlashingProtectionDatabase {
|
|
9
|
+
duties = new Map();
|
|
10
|
+
tryInsertOrGetExisting(params) {
|
|
11
|
+
const blockIndex = getBlockIndexFromDutyIdentifier(params);
|
|
12
|
+
const key = dutyKey(params.rollupAddress, params.validatorAddress, params.slot, params.dutyType, blockIndex);
|
|
13
|
+
const existing = this.duties.get(key);
|
|
14
|
+
if (existing) {
|
|
15
|
+
return Promise.resolve({
|
|
16
|
+
isNew: false,
|
|
17
|
+
record: existing
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
const lockToken = `lock-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
21
|
+
const record = {
|
|
22
|
+
rollupAddress: params.rollupAddress,
|
|
23
|
+
validatorAddress: params.validatorAddress,
|
|
24
|
+
slot: params.slot,
|
|
25
|
+
blockNumber: params.blockNumber,
|
|
26
|
+
blockIndexWithinCheckpoint: blockIndex,
|
|
27
|
+
dutyType: params.dutyType,
|
|
28
|
+
status: DutyStatus.SIGNING,
|
|
29
|
+
messageHash: params.messageHash,
|
|
30
|
+
nodeId: params.nodeId,
|
|
31
|
+
lockToken,
|
|
32
|
+
startedAt: new Date()
|
|
33
|
+
};
|
|
34
|
+
this.duties.set(key, record);
|
|
35
|
+
return Promise.resolve({
|
|
36
|
+
isNew: true,
|
|
37
|
+
record
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
updateDutySigned(rollupAddress, validatorAddress, slot, dutyType, signature, lockToken, blockIndexWithinCheckpoint) {
|
|
41
|
+
const key = dutyKey(rollupAddress, validatorAddress, slot, dutyType, blockIndexWithinCheckpoint);
|
|
42
|
+
const record = this.duties.get(key);
|
|
43
|
+
if (!record || record.lockToken !== lockToken) {
|
|
44
|
+
return Promise.resolve(false);
|
|
45
|
+
}
|
|
46
|
+
record.status = DutyStatus.SIGNED;
|
|
47
|
+
record.signature = signature;
|
|
48
|
+
record.completedAt = new Date();
|
|
49
|
+
return Promise.resolve(true);
|
|
50
|
+
}
|
|
51
|
+
deleteDuty(rollupAddress, validatorAddress, slot, dutyType, lockToken, blockIndexWithinCheckpoint) {
|
|
52
|
+
const key = dutyKey(rollupAddress, validatorAddress, slot, dutyType, blockIndexWithinCheckpoint);
|
|
53
|
+
const record = this.duties.get(key);
|
|
54
|
+
if (!record || record.lockToken !== lockToken) {
|
|
55
|
+
return Promise.resolve(false);
|
|
56
|
+
}
|
|
57
|
+
this.duties.delete(key);
|
|
58
|
+
return Promise.resolve(true);
|
|
59
|
+
}
|
|
60
|
+
cleanupOwnStuckDuties(_nodeId, _maxAgeMs) {
|
|
61
|
+
return Promise.resolve(0);
|
|
62
|
+
}
|
|
63
|
+
cleanupOutdatedRollupDuties(_currentRollupAddress) {
|
|
64
|
+
return Promise.resolve(0);
|
|
65
|
+
}
|
|
66
|
+
cleanupOldDuties(_maxAgeMs) {
|
|
67
|
+
return Promise.resolve(0);
|
|
68
|
+
}
|
|
69
|
+
close() {
|
|
70
|
+
this.duties.clear();
|
|
71
|
+
return Promise.resolve();
|
|
72
|
+
}
|
|
73
|
+
}
|
package/dest/db/postgres.d.ts
CHANGED
|
@@ -66,5 +66,19 @@ export declare class PostgresSlashingProtectionDatabase implements SlashingProte
|
|
|
66
66
|
* @returns the number of duties cleaned up
|
|
67
67
|
*/
|
|
68
68
|
cleanupOwnStuckDuties(nodeId: string, maxAgeMs: number): Promise<number>;
|
|
69
|
+
/**
|
|
70
|
+
* Cleanup duties with outdated rollup address.
|
|
71
|
+
* Removes all duties where the rollup address doesn't match the current one.
|
|
72
|
+
* Used after a rollup upgrade to clean up duties for the old rollup.
|
|
73
|
+
* @returns the number of duties cleaned up
|
|
74
|
+
*/
|
|
75
|
+
cleanupOutdatedRollupDuties(currentRollupAddress: EthAddress): Promise<number>;
|
|
76
|
+
/**
|
|
77
|
+
* Cleanup old signed duties.
|
|
78
|
+
* Removes only signed duties older than the specified age.
|
|
79
|
+
* Does not remove 'signing' duties as they may be in progress.
|
|
80
|
+
* @returns the number of duties cleaned up
|
|
81
|
+
*/
|
|
82
|
+
cleanupOldDuties(maxAgeMs: number): Promise<number>;
|
|
69
83
|
}
|
|
70
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
84
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicG9zdGdyZXMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kYi9wb3N0Z3Jlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUNILE9BQU8sRUFBZSxVQUFVLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUUxRSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFJM0QsT0FBTyxLQUFLLEVBQUUsV0FBVyxFQUFFLGNBQWMsRUFBRSxNQUFNLElBQUksQ0FBQztBQUV0RCxPQUFPLEtBQUssRUFBRSwwQkFBMEIsRUFBRSxvQkFBb0IsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQVVwRixPQUFPLEtBQUssRUFBRSxvQkFBb0IsRUFBVyxRQUFRLEVBQXVDLE1BQU0sWUFBWSxDQUFDO0FBRy9HOzs7R0FHRztBQUNILE1BQU0sV0FBVyxhQUFhO0lBQzVCLEtBQUssQ0FBQyxDQUFDLFNBQVMsY0FBYyxHQUFHLEdBQUcsRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLE1BQU0sQ0FBQyxFQUFFLEdBQUcsRUFBRSxHQUFHLE9BQU8sQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUM3RixHQUFHLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO0NBQ3RCO0FBRUQ7O0dBRUc7QUFDSCxxQkFBYSxrQ0FBbUMsWUFBVywwQkFBMEI7SUFHdkUsT0FBTyxDQUFDLFFBQVEsQ0FBQyxJQUFJO0lBRmpDLE9BQU8sQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFTO0lBRTdCLFlBQTZCLElBQUksRUFBRSxhQUFhLEVBRS9DO0lBRUQ7Ozs7O09BS0c7SUFDRyxVQUFVLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQWdDaEM7SUFFRDs7Ozs7Ozs7T0FRRztJQUNHLHNCQUFzQixDQUFDLE1BQU0sRUFBRSxvQkFBb0IsR0FBRyxPQUFPLENBQUMsb0JBQW9CLENBQUMsQ0FvRHhGO0lBRUQ7Ozs7O09BS0c7SUFDRyxnQkFBZ0IsQ0FDcEIsYUFBYSxFQUFFLFVBQVUsRUFDekIsZ0JBQWdCLEVBQUUsVUFBVSxFQUM1QixJQUFJLEVBQUUsVUFBVSxFQUNoQixRQUFRLEVBQUUsUUFBUSxFQUNsQixTQUFTLEVBQUUsTUFBTSxFQUNqQixTQUFTLEVBQUUsTUFBTSxFQUNqQiwwQkFBMEIsRUFBRSxNQUFNLEdBQ2pDLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FzQmxCO0lBRUQ7Ozs7OztPQU1HO0lBQ0csVUFBVSxDQUNkLGFBQWEsRUFBRSxVQUFVLEVBQ3pCLGdCQUFnQixFQUFFLFVBQVUsRUFDNUIsSUFBSSxFQUFFLFVBQVUsRUFDaEIsUUFBUSxFQUFFLFFBQVEsRUFDbEIsU0FBUyxFQUFFLE1BQU0sRUFDakIsMEJBQTBCLEVBQUUsTUFBTSxHQUNqQyxPQUFPLENBQUMsT0FBTyxDQUFDLENBcUJsQjtJQUVEOztPQUVHO0lBQ0gsT0FBTyxDQUFDLFdBQVc7SUFtQm5COztPQUVHO0lBQ0csS0FBSyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FHM0I7SUFFRDs7O09BR0c7SUFDRyxxQkFBcUIsQ0FBQyxNQUFNLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxNQUFNLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUc3RTtJQUVEOzs7OztPQUtHO0lBQ0csMkJBQTJCLENBQUMsb0JBQW9CLEVBQUUsVUFBVSxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FHbkY7SUFFRDs7Ozs7T0FLRztJQUNHLGdCQUFnQixDQUFDLFFBQVEsRUFBRSxNQUFNLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUd4RDtDQUNGIn0=
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/db/postgres.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAe,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAE1E,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAI3D,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,IAAI,CAAC;AAEtD,OAAO,KAAK,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/db/postgres.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAe,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAE1E,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAI3D,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,IAAI,CAAC;AAEtD,OAAO,KAAK,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAUpF,OAAO,KAAK,EAAE,oBAAoB,EAAW,QAAQ,EAAuC,MAAM,YAAY,CAAC;AAG/G;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,CAAC,SAAS,cAAc,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7F,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtB;AAED;;GAEG;AACH,qBAAa,kCAAmC,YAAW,0BAA0B;IAGvE,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFjC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAE7B,YAA6B,IAAI,EAAE,aAAa,EAE/C;IAED;;;;;OAKG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAgChC;IAED;;;;;;;;OAQG;IACG,sBAAsB,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAoDxF;IAED;;;;;OAKG;IACG,gBAAgB,CACpB,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,CAsBlB;IAED;;;;;;OAMG;IACG,UAAU,CACd,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,CAqBlB;IAED;;OAEG;IACH,OAAO,CAAC,WAAW;IAmBnB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAG3B;IAED;;;OAGG;IACG,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAG7E;IAED;;;;;OAKG;IACG,2BAA2B,CAAC,oBAAoB,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAGnF;IAED;;;;;OAKG;IACG,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGxD;CACF"}
|
package/dest/db/postgres.js
CHANGED
|
@@ -5,7 +5,7 @@ import { randomBytes } from '@aztec/foundation/crypto/random';
|
|
|
5
5
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
6
6
|
import { createLogger } from '@aztec/foundation/log';
|
|
7
7
|
import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
8
|
-
import { CLEANUP_OWN_STUCK_DUTIES, DELETE_DUTY, INSERT_OR_GET_DUTY, SCHEMA_VERSION, UPDATE_DUTY_SIGNED } from './schema.js';
|
|
8
|
+
import { CLEANUP_OLD_DUTIES, CLEANUP_OUTDATED_ROLLUP_DUTIES, CLEANUP_OWN_STUCK_DUTIES, DELETE_DUTY, INSERT_OR_GET_DUTY, SCHEMA_VERSION, UPDATE_DUTY_SIGNED } from './schema.js';
|
|
9
9
|
import { getBlockIndexFromDutyIdentifier } from './types.js';
|
|
10
10
|
/**
|
|
11
11
|
* PostgreSQL implementation of the slashing protection database
|
|
@@ -177,10 +177,31 @@ import { getBlockIndexFromDutyIdentifier } from './types.js';
|
|
|
177
177
|
* Cleanup own stuck duties
|
|
178
178
|
* @returns the number of duties cleaned up
|
|
179
179
|
*/ async cleanupOwnStuckDuties(nodeId, maxAgeMs) {
|
|
180
|
-
const cutoff = new Date(Date.now() - maxAgeMs);
|
|
181
180
|
const result = await this.pool.query(CLEANUP_OWN_STUCK_DUTIES, [
|
|
182
181
|
nodeId,
|
|
183
|
-
|
|
182
|
+
maxAgeMs
|
|
183
|
+
]);
|
|
184
|
+
return result.rowCount ?? 0;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Cleanup duties with outdated rollup address.
|
|
188
|
+
* Removes all duties where the rollup address doesn't match the current one.
|
|
189
|
+
* Used after a rollup upgrade to clean up duties for the old rollup.
|
|
190
|
+
* @returns the number of duties cleaned up
|
|
191
|
+
*/ async cleanupOutdatedRollupDuties(currentRollupAddress) {
|
|
192
|
+
const result = await this.pool.query(CLEANUP_OUTDATED_ROLLUP_DUTIES, [
|
|
193
|
+
currentRollupAddress.toString()
|
|
194
|
+
]);
|
|
195
|
+
return result.rowCount ?? 0;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Cleanup old signed duties.
|
|
199
|
+
* Removes only signed duties older than the specified age.
|
|
200
|
+
* Does not remove 'signing' duties as they may be in progress.
|
|
201
|
+
* @returns the number of duties cleaned up
|
|
202
|
+
*/ async cleanupOldDuties(maxAgeMs) {
|
|
203
|
+
const result = await this.pool.query(CLEANUP_OLD_DUTIES, [
|
|
204
|
+
maxAgeMs
|
|
184
205
|
]);
|
|
185
206
|
return result.rowCount ?? 0;
|
|
186
207
|
}
|
package/dest/db/schema.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export declare const SCHEMA_VERSION = 1;
|
|
|
12
12
|
/**
|
|
13
13
|
* SQL to create the validator_duties table
|
|
14
14
|
*/
|
|
15
|
-
export declare const CREATE_VALIDATOR_DUTIES_TABLE = "\nCREATE TABLE IF NOT EXISTS validator_duties (\n rollup_address VARCHAR(42) NOT NULL,\n validator_address VARCHAR(42) NOT NULL,\n slot BIGINT NOT NULL,\n block_number BIGINT NOT NULL,\n block_index_within_checkpoint INTEGER NOT NULL DEFAULT 0,\n duty_type VARCHAR(30) NOT NULL CHECK (duty_type IN ('BLOCK_PROPOSAL', 'CHECKPOINT_PROPOSAL', 'ATTESTATION', 'ATTESTATIONS_AND_SIGNERS', 'GOVERNANCE_VOTE', 'SLASHING_VOTE')),\n status VARCHAR(20) NOT NULL CHECK (status IN ('signing', 'signed'
|
|
15
|
+
export declare const CREATE_VALIDATOR_DUTIES_TABLE = "\nCREATE TABLE IF NOT EXISTS validator_duties (\n rollup_address VARCHAR(42) NOT NULL,\n validator_address VARCHAR(42) NOT NULL,\n slot BIGINT NOT NULL,\n block_number BIGINT NOT NULL,\n block_index_within_checkpoint INTEGER NOT NULL DEFAULT 0,\n duty_type VARCHAR(30) NOT NULL CHECK (duty_type IN ('BLOCK_PROPOSAL', 'CHECKPOINT_PROPOSAL', 'ATTESTATION', 'ATTESTATIONS_AND_SIGNERS', 'GOVERNANCE_VOTE', 'SLASHING_VOTE')),\n status VARCHAR(20) NOT NULL CHECK (status IN ('signing', 'signed')),\n message_hash VARCHAR(66) NOT NULL,\n signature VARCHAR(132),\n node_id VARCHAR(255) NOT NULL,\n lock_token VARCHAR(64) NOT NULL,\n started_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n completed_at TIMESTAMP,\n error_message TEXT,\n\n PRIMARY KEY (rollup_address, validator_address, slot, duty_type, block_index_within_checkpoint),\n CHECK (completed_at IS NULL OR completed_at >= started_at)\n);\n";
|
|
16
16
|
/**
|
|
17
17
|
* SQL to create index on status and started_at for cleanup queries
|
|
18
18
|
*/
|
|
@@ -32,7 +32,7 @@ export declare const INSERT_SCHEMA_VERSION = "\nINSERT INTO schema_version (vers
|
|
|
32
32
|
/**
|
|
33
33
|
* Complete schema setup - all statements in order
|
|
34
34
|
*/
|
|
35
|
-
export declare const SCHEMA_SETUP: readonly ["\nCREATE TABLE IF NOT EXISTS schema_version (\n version INTEGER PRIMARY KEY,\n applied_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP\n);\n", "\nCREATE TABLE IF NOT EXISTS validator_duties (\n rollup_address VARCHAR(42) NOT NULL,\n validator_address VARCHAR(42) NOT NULL,\n slot BIGINT NOT NULL,\n block_number BIGINT NOT NULL,\n block_index_within_checkpoint INTEGER NOT NULL DEFAULT 0,\n duty_type VARCHAR(30) NOT NULL CHECK (duty_type IN ('BLOCK_PROPOSAL', 'CHECKPOINT_PROPOSAL', 'ATTESTATION', 'ATTESTATIONS_AND_SIGNERS', 'GOVERNANCE_VOTE', 'SLASHING_VOTE')),\n status VARCHAR(20) NOT NULL CHECK (status IN ('signing', 'signed'
|
|
35
|
+
export declare const SCHEMA_SETUP: readonly ["\nCREATE TABLE IF NOT EXISTS schema_version (\n version INTEGER PRIMARY KEY,\n applied_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP\n);\n", "\nCREATE TABLE IF NOT EXISTS validator_duties (\n rollup_address VARCHAR(42) NOT NULL,\n validator_address VARCHAR(42) NOT NULL,\n slot BIGINT NOT NULL,\n block_number BIGINT NOT NULL,\n block_index_within_checkpoint INTEGER NOT NULL DEFAULT 0,\n duty_type VARCHAR(30) NOT NULL CHECK (duty_type IN ('BLOCK_PROPOSAL', 'CHECKPOINT_PROPOSAL', 'ATTESTATION', 'ATTESTATIONS_AND_SIGNERS', 'GOVERNANCE_VOTE', 'SLASHING_VOTE')),\n status VARCHAR(20) NOT NULL CHECK (status IN ('signing', 'signed')),\n message_hash VARCHAR(66) NOT NULL,\n signature VARCHAR(132),\n node_id VARCHAR(255) NOT NULL,\n lock_token VARCHAR(64) NOT NULL,\n started_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n completed_at TIMESTAMP,\n error_message TEXT,\n\n PRIMARY KEY (rollup_address, validator_address, slot, duty_type, block_index_within_checkpoint),\n CHECK (completed_at IS NULL OR completed_at >= started_at)\n);\n", "\nCREATE INDEX IF NOT EXISTS idx_validator_duties_status\nON validator_duties(status, started_at);\n", "\nCREATE INDEX IF NOT EXISTS idx_validator_duties_node\nON validator_duties(node_id, started_at);\n"];
|
|
36
36
|
/**
|
|
37
37
|
* Query to get current schema version
|
|
38
38
|
*/
|
|
@@ -65,14 +65,21 @@ export declare const DELETE_DUTY = "\nDELETE FROM validator_duties\nWHERE rollup
|
|
|
65
65
|
export declare const CLEANUP_OLD_SIGNED_DUTIES = "\nDELETE FROM validator_duties\nWHERE status = 'signed'\n AND completed_at < $1;\n";
|
|
66
66
|
/**
|
|
67
67
|
* Query to clean up old duties (for maintenance)
|
|
68
|
-
* Removes duties older than a specified
|
|
68
|
+
* Removes SIGNED duties older than a specified age (in milliseconds)
|
|
69
69
|
*/
|
|
70
|
-
export declare const CLEANUP_OLD_DUTIES = "\nDELETE FROM validator_duties\nWHERE status
|
|
70
|
+
export declare const CLEANUP_OLD_DUTIES = "\nDELETE FROM validator_duties\nWHERE status = 'signed'\n AND started_at < CURRENT_TIMESTAMP - ($1 || ' milliseconds')::INTERVAL;\n";
|
|
71
71
|
/**
|
|
72
72
|
* Query to cleanup own stuck duties
|
|
73
73
|
* Removes duties in 'signing' status for a specific node that are older than maxAgeMs
|
|
74
|
+
* Uses DB's CURRENT_TIMESTAMP to avoid clock skew issues between nodes
|
|
74
75
|
*/
|
|
75
|
-
export declare const CLEANUP_OWN_STUCK_DUTIES = "\nDELETE FROM validator_duties\nWHERE node_id = $1\n AND status = 'signing'\n AND started_at < $2;\n";
|
|
76
|
+
export declare const CLEANUP_OWN_STUCK_DUTIES = "\nDELETE FROM validator_duties\nWHERE node_id = $1\n AND status = 'signing'\n AND started_at < CURRENT_TIMESTAMP - ($2 || ' milliseconds')::INTERVAL;\n";
|
|
77
|
+
/**
|
|
78
|
+
* Query to cleanup duties with outdated rollup address
|
|
79
|
+
* Removes all duties where the rollup address doesn't match the current one
|
|
80
|
+
* Used after a rollup upgrade to clean up duties for the old rollup
|
|
81
|
+
*/
|
|
82
|
+
export declare const CLEANUP_OUTDATED_ROLLUP_DUTIES = "\nDELETE FROM validator_duties\nWHERE rollup_address != $1;\n";
|
|
76
83
|
/**
|
|
77
84
|
* SQL to drop the validator_duties table
|
|
78
85
|
*/
|
|
@@ -86,4 +93,4 @@ export declare const DROP_SCHEMA_VERSION_TABLE = "DROP TABLE IF EXISTS schema_ve
|
|
|
86
93
|
* Returns duties in 'signing' status that have been stuck for too long
|
|
87
94
|
*/
|
|
88
95
|
export declare const GET_STUCK_DUTIES = "\nSELECT\n rollup_address,\n validator_address,\n slot,\n block_number,\n block_index_within_checkpoint,\n duty_type,\n status,\n message_hash,\n node_id,\n started_at,\n EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - started_at)) as age_seconds\nFROM validator_duties\nWHERE status = 'signing'\n AND started_at < $1\nORDER BY started_at ASC;\n";
|
|
89
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
96
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NoZW1hLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZGIvc2NoZW1hLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7R0FNRztBQUVIOztHQUVHO0FBQ0gsZUFBTyxNQUFNLGNBQWMsSUFBSSxDQUFDO0FBRWhDOztHQUVHO0FBQ0gsZUFBTyxNQUFNLDZCQUE2QixzNUJBb0J6QyxDQUFDO0FBRUY7O0dBRUc7QUFDSCxlQUFPLE1BQU0sbUJBQW1CLHlHQUcvQixDQUFDO0FBRUY7O0dBRUc7QUFDSCxlQUFPLE1BQU0saUJBQWlCLHdHQUc3QixDQUFDO0FBRUY7O0dBRUc7QUFDSCxlQUFPLE1BQU0sMkJBQTJCLG1KQUt2QyxDQUFDO0FBRUY7O0dBRUc7QUFDSCxlQUFPLE1BQU0scUJBQXFCLDZGQUlqQyxDQUFDO0FBRUY7O0dBRUc7QUFDSCxlQUFPLE1BQU0sWUFBWSxpd0NBS2YsQ0FBQztBQUVYOztHQUVHO0FBQ0gsZUFBTyxNQUFNLGtCQUFrQiwwRUFFOUIsQ0FBQztBQUVGOzs7Ozs7Ozs7O0dBVUc7QUFDSCxlQUFPLE1BQU0sa0JBQWtCLDZ1Q0EwRDlCLENBQUM7QUFFRjs7R0FFRztBQUNILGVBQU8sTUFBTSxrQkFBa0IsK1NBWTlCLENBQUM7QUFFRjs7O0dBR0c7QUFDSCxlQUFPLE1BQU0sV0FBVyxpT0FTdkIsQ0FBQztBQUVGOzs7R0FHRztBQUNILGVBQU8sTUFBTSx5QkFBeUIsd0ZBSXJDLENBQUM7QUFFRjs7O0dBR0c7QUFDSCxlQUFPLE1BQU0sa0JBQWtCLHlJQUk5QixDQUFDO0FBRUY7Ozs7R0FJRztBQUNILGVBQU8sTUFBTSx3QkFBd0IsOEpBS3BDLENBQUM7QUFFRjs7OztHQUlHO0FBQ0gsZUFBTyxNQUFNLDhCQUE4QixrRUFHMUMsQ0FBQztBQUVGOztHQUVHO0FBQ0gsZUFBTyxNQUFNLDJCQUEyQiwyQ0FBMkMsQ0FBQztBQUVwRjs7R0FFRztBQUNILGVBQU8sTUFBTSx5QkFBeUIseUNBQXlDLENBQUM7QUFFaEY7OztHQUdHO0FBQ0gsZUFBTyxNQUFNLGdCQUFnQixrV0FpQjVCLENBQUMifQ==
|
package/dest/db/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;GAEG;AACH,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC;;GAEG;AACH,eAAO,MAAM,6BAA6B,
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;GAEG;AACH,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC;;GAEG;AACH,eAAO,MAAM,6BAA6B,s5BAoBzC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,yGAG/B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,wGAG7B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,2BAA2B,mJAKvC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,6FAIjC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,iwCAKf,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,kBAAkB,0EAE9B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,6uCA0D9B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,+SAY9B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,WAAW,iOASvB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,yBAAyB,wFAIrC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,yIAI9B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,8JAKpC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,kEAG1C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,2BAA2B,2CAA2C,CAAC;AAEpF;;GAEG;AACH,eAAO,MAAM,yBAAyB,yCAAyC,CAAC;AAEhF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,kWAiB5B,CAAC"}
|
package/dest/db/schema.js
CHANGED
|
@@ -17,7 +17,7 @@ CREATE TABLE IF NOT EXISTS validator_duties (
|
|
|
17
17
|
block_number BIGINT NOT NULL,
|
|
18
18
|
block_index_within_checkpoint INTEGER NOT NULL DEFAULT 0,
|
|
19
19
|
duty_type VARCHAR(30) NOT NULL CHECK (duty_type IN ('BLOCK_PROPOSAL', 'CHECKPOINT_PROPOSAL', 'ATTESTATION', 'ATTESTATIONS_AND_SIGNERS', 'GOVERNANCE_VOTE', 'SLASHING_VOTE')),
|
|
20
|
-
status VARCHAR(20) NOT NULL CHECK (status IN ('signing', 'signed'
|
|
20
|
+
status VARCHAR(20) NOT NULL CHECK (status IN ('signing', 'signed')),
|
|
21
21
|
message_hash VARCHAR(66) NOT NULL,
|
|
22
22
|
signature VARCHAR(132),
|
|
23
23
|
node_id VARCHAR(255) NOT NULL,
|
|
@@ -177,20 +177,29 @@ WHERE status = 'signed'
|
|
|
177
177
|
`;
|
|
178
178
|
/**
|
|
179
179
|
* Query to clean up old duties (for maintenance)
|
|
180
|
-
* Removes duties older than a specified
|
|
180
|
+
* Removes SIGNED duties older than a specified age (in milliseconds)
|
|
181
181
|
*/ export const CLEANUP_OLD_DUTIES = `
|
|
182
182
|
DELETE FROM validator_duties
|
|
183
|
-
WHERE status
|
|
184
|
-
AND started_at < $1;
|
|
183
|
+
WHERE status = 'signed'
|
|
184
|
+
AND started_at < CURRENT_TIMESTAMP - ($1 || ' milliseconds')::INTERVAL;
|
|
185
185
|
`;
|
|
186
186
|
/**
|
|
187
187
|
* Query to cleanup own stuck duties
|
|
188
188
|
* Removes duties in 'signing' status for a specific node that are older than maxAgeMs
|
|
189
|
+
* Uses DB's CURRENT_TIMESTAMP to avoid clock skew issues between nodes
|
|
189
190
|
*/ export const CLEANUP_OWN_STUCK_DUTIES = `
|
|
190
191
|
DELETE FROM validator_duties
|
|
191
192
|
WHERE node_id = $1
|
|
192
193
|
AND status = 'signing'
|
|
193
|
-
AND started_at < $2;
|
|
194
|
+
AND started_at < CURRENT_TIMESTAMP - ($2 || ' milliseconds')::INTERVAL;
|
|
195
|
+
`;
|
|
196
|
+
/**
|
|
197
|
+
* Query to cleanup duties with outdated rollup address
|
|
198
|
+
* Removes all duties where the rollup address doesn't match the current one
|
|
199
|
+
* Used after a rollup upgrade to clean up duties for the old rollup
|
|
200
|
+
*/ export const CLEANUP_OUTDATED_ROLLUP_DUTIES = `
|
|
201
|
+
DELETE FROM validator_duties
|
|
202
|
+
WHERE rollup_address != $1;
|
|
194
203
|
`;
|
|
195
204
|
/**
|
|
196
205
|
* SQL to drop the validator_duties table
|
package/dest/db/types.d.ts
CHANGED
|
@@ -76,7 +76,7 @@ export interface ValidatorDutyRecord {
|
|
|
76
76
|
startedAt: Date;
|
|
77
77
|
/** When the duty signing was completed (success or failure) */
|
|
78
78
|
completedAt?: Date;
|
|
79
|
-
/** Error message
|
|
79
|
+
/** Error message (currently unused) */
|
|
80
80
|
errorMessage?: string;
|
|
81
81
|
}
|
|
82
82
|
/**
|
|
@@ -163,4 +163,4 @@ interface DeleteDutyExtra {
|
|
|
163
163
|
*/
|
|
164
164
|
export type DeleteDutyParams = DutyIdentifier & DeleteDutyExtra;
|
|
165
165
|
export {};
|
|
166
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
166
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kYi90eXBlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxXQUFXLEVBQUUsZ0JBQWdCLEVBQUUscUJBQXFCLEVBQUUsVUFBVSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDeEgsT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDaEUsT0FBTyxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFFakU7O0dBRUc7QUFDSCxNQUFNLFdBQVcsT0FBTztJQUN0QixjQUFjLEVBQUUsTUFBTSxDQUFDO0lBQ3ZCLGlCQUFpQixFQUFFLE1BQU0sQ0FBQztJQUMxQixJQUFJLEVBQUUsTUFBTSxDQUFDO0lBQ2IsWUFBWSxFQUFFLE1BQU0sQ0FBQztJQUNyQiw2QkFBNkIsRUFBRSxNQUFNLENBQUM7SUFDdEMsU0FBUyxFQUFFLFFBQVEsQ0FBQztJQUNwQixNQUFNLEVBQUUsVUFBVSxDQUFDO0lBQ25CLFlBQVksRUFBRSxNQUFNLENBQUM7SUFDckIsU0FBUyxFQUFFLE1BQU0sR0FBRyxJQUFJLENBQUM7SUFDekIsT0FBTyxFQUFFLE1BQU0sQ0FBQztJQUNoQixVQUFVLEVBQUUsTUFBTSxDQUFDO0lBQ25CLFVBQVUsRUFBRSxJQUFJLENBQUM7SUFDakIsWUFBWSxFQUFFLElBQUksR0FBRyxJQUFJLENBQUM7SUFDMUIsYUFBYSxFQUFFLE1BQU0sR0FBRyxJQUFJLENBQUM7Q0FDOUI7QUFFRDs7R0FFRztBQUNILE1BQU0sV0FBVyxjQUFlLFNBQVEsT0FBTztJQUM3QyxNQUFNLEVBQUUsT0FBTyxDQUFDO0NBQ2pCO0FBRUQ7O0dBRUc7QUFDSCxvQkFBWSxRQUFRO0lBQ2xCLGNBQWMsbUJBQW1CO0lBQ2pDLG1CQUFtQix3QkFBd0I7SUFDM0MsV0FBVyxnQkFBZ0I7SUFDM0Isd0JBQXdCLDZCQUE2QjtJQUNyRCxlQUFlLG9CQUFvQjtJQUNuQyxhQUFhLGtCQUFrQjtJQUMvQixZQUFZLGlCQUFpQjtJQUM3QixHQUFHLFFBQVE7Q0FDWjtBQUVEOztHQUVHO0FBQ0gsb0JBQVksVUFBVTtJQUNwQixPQUFPLFlBQVk7SUFDbkIsTUFBTSxXQUFXO0NBQ2xCO0FBRUQ7O0dBRUc7QUFDSCxNQUFNLFdBQVcsbUJBQW1CO0lBQ2xDLDhDQUE4QztJQUM5QyxhQUFhLEVBQUUsVUFBVSxDQUFDO0lBQzFCLHdDQUF3QztJQUN4QyxnQkFBZ0IsRUFBRSxVQUFVLENBQUM7SUFDN0IsZ0NBQWdDO0lBQ2hDLElBQUksRUFBRSxVQUFVLENBQUM7SUFDakIsaUNBQWlDO0lBQ2pDLFdBQVcsRUFBRSxXQUFXLENBQUM7SUFDekIsOEZBQThGO0lBQzlGLDBCQUEwQixFQUFFLE1BQU0sQ0FBQztJQUNuQyxtQ0FBbUM7SUFDbkMsUUFBUSxFQUFFLFFBQVEsQ0FBQztJQUNuQixpQ0FBaUM7SUFDakMsTUFBTSxFQUFFLFVBQVUsQ0FBQztJQUNuQiw0Q0FBNEM7SUFDNUMsV0FBVyxFQUFFLE1BQU0sQ0FBQztJQUNwQix5REFBeUQ7SUFDekQsU0FBUyxDQUFDLEVBQUUsTUFBTSxDQUFDO0lBQ25CLDREQUE0RDtJQUM1RCxNQUFNLEVBQUUsTUFBTSxDQUFDO0lBQ2YsNERBQTREO0lBQzVELFNBQVMsRUFBRSxNQUFNLENBQUM7SUFDbEIsd0NBQXdDO0lBQ3hDLFNBQVMsRUFBRSxJQUFJLENBQUM7SUFDaEIsK0RBQStEO0lBQy9ELFdBQVcsQ0FBQyxFQUFFLElBQUksQ0FBQztJQUNuQix1Q0FBdUM7SUFDdkMsWUFBWSxDQUFDLEVBQUUsTUFBTSxDQUFDO0NBQ3ZCO0FBRUQ7OztHQUdHO0FBQ0gsTUFBTSxXQUFXLDJCQUEyQjtJQUMxQyxhQUFhLEVBQUUsVUFBVSxDQUFDO0lBQzFCLGdCQUFnQixFQUFFLFVBQVUsQ0FBQztJQUM3QixJQUFJLEVBQUUsVUFBVSxDQUFDO0lBQ2pCLGdGQUFnRjtJQUNoRiwwQkFBMEIsRUFBRSxxQkFBcUIsQ0FBQztJQUNsRCxRQUFRLEVBQUUsUUFBUSxDQUFDLGNBQWMsQ0FBQztDQUNuQztBQUVEOzs7R0FHRztBQUNILE1BQU0sV0FBVyxtQkFBbUI7SUFDbEMsYUFBYSxFQUFFLFVBQVUsQ0FBQztJQUMxQixnQkFBZ0IsRUFBRSxVQUFVLENBQUM7SUFDN0IsSUFBSSxFQUFFLFVBQVUsQ0FBQztJQUNqQixRQUFRLEVBQ0osUUFBUSxDQUFDLG1CQUFtQixHQUM1QixRQUFRLENBQUMsV0FBVyxHQUNwQixRQUFRLENBQUMsd0JBQXdCLEdBQ2pDLFFBQVEsQ0FBQyxlQUFlLEdBQ3hCLFFBQVEsQ0FBQyxhQUFhLEdBQ3RCLFFBQVEsQ0FBQyxZQUFZLEdBQ3JCLFFBQVEsQ0FBQyxHQUFHLENBQUM7Q0FDbEI7QUFFRDs7Ozs7R0FLRztBQUNILE1BQU0sTUFBTSxjQUFjLEdBQUcsMkJBQTJCLEdBQUcsbUJBQW1CLENBQUM7QUFFL0U7Ozs7OztHQU1HO0FBQ0gsd0JBQWdCLG1CQUFtQixDQUFDLFFBQVEsRUFBRSxRQUFRLEVBQUUsMEJBQTBCLEVBQUUsTUFBTSxHQUFHLFNBQVMsR0FBRyxNQUFNLENBYzlHO0FBRUQ7Ozs7R0FJRztBQUNILHdCQUFnQiwrQkFBK0IsQ0FBQyxJQUFJLEVBQUUsY0FBYyxHQUFHLE1BQU0sQ0FLNUU7QUFFRDs7R0FFRztBQUNILFVBQVUsbUJBQW1CO0lBQzNCLGlDQUFpQztJQUNqQyxXQUFXLEVBQUUsV0FBVyxHQUFHLGdCQUFnQixDQUFDO0lBQzVDLDRDQUE0QztJQUM1QyxXQUFXLEVBQUUsTUFBTSxDQUFDO0lBQ3BCLHFEQUFxRDtJQUNyRCxNQUFNLEVBQUUsTUFBTSxDQUFDO0NBQ2hCO0FBRUQ7OztHQUdHO0FBQ0gsTUFBTSxNQUFNLG9CQUFvQixHQUFHLGNBQWMsR0FBRyxtQkFBbUIsQ0FBQztBQUV4RTs7R0FFRztBQUNILFVBQVUsa0JBQWtCO0lBQzFCLFNBQVMsRUFBRSxTQUFTLENBQUM7SUFDckIsTUFBTSxFQUFFLE1BQU0sQ0FBQztJQUNmLFNBQVMsRUFBRSxNQUFNLENBQUM7Q0FDbkI7QUFFRDs7O0dBR0c7QUFDSCxNQUFNLE1BQU0sbUJBQW1CLEdBQUcsY0FBYyxHQUFHLGtCQUFrQixDQUFDO0FBRXRFOztHQUVHO0FBQ0gsVUFBVSxlQUFlO0lBQ3ZCLFNBQVMsRUFBRSxNQUFNLENBQUM7Q0FDbkI7QUFFRDs7O0dBR0c7QUFDSCxNQUFNLE1BQU0sZ0JBQWdCLEdBQUcsY0FBYyxHQUFHLGVBQWUsQ0FBQyJ9
|
package/dest/db/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/db/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AACxH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAEjE;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,6BAA6B,EAAE,MAAM,CAAC;IACtC,SAAS,EAAE,QAAQ,CAAC;IACpB,MAAM,EAAE,UAAU,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,IAAI,CAAC;IACjB,YAAY,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,cAAe,SAAQ,OAAO;IAC7C,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,oBAAY,QAAQ;IAClB,cAAc,mBAAmB;IACjC,mBAAmB,wBAAwB;IAC3C,WAAW,gBAAgB;IAC3B,wBAAwB,6BAA6B;IACrD,eAAe,oBAAoB;IACnC,aAAa,kBAAkB;IAC/B,YAAY,iBAAiB;IAC7B,GAAG,QAAQ;CACZ;AAED;;GAEG;AACH,oBAAY,UAAU;IACpB,OAAO,YAAY;IACnB,MAAM,WAAW;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,8CAA8C;IAC9C,aAAa,EAAE,UAAU,CAAC;IAC1B,wCAAwC;IACxC,gBAAgB,EAAE,UAAU,CAAC;IAC7B,gCAAgC;IAChC,IAAI,EAAE,UAAU,CAAC;IACjB,iCAAiC;IACjC,WAAW,EAAE,WAAW,CAAC;IACzB,8FAA8F;IAC9F,0BAA0B,EAAE,MAAM,CAAC;IACnC,mCAAmC;IACnC,QAAQ,EAAE,QAAQ,CAAC;IACnB,iCAAiC;IACjC,MAAM,EAAE,UAAU,CAAC;IACnB,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,SAAS,EAAE,IAAI,CAAC;IAChB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,IAAI,CAAC;IACnB,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/db/types.ts"],"names":[],"mappings":"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"}
|
package/dest/factory.d.ts
CHANGED
|
@@ -39,4 +39,17 @@ export declare function createHASigner(config: ValidatorHASignerConfig, deps?: C
|
|
|
39
39
|
signer: ValidatorHASigner;
|
|
40
40
|
db: SlashingProtectionDatabase;
|
|
41
41
|
}>;
|
|
42
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Create an in-memory SlashingProtectionDatabase that can be shared across
|
|
44
|
+
* multiple validator nodes in the same process. Used for testing HA setups.
|
|
45
|
+
*/
|
|
46
|
+
export declare function createSharedSlashingProtectionDb(): SlashingProtectionDatabase;
|
|
47
|
+
/**
|
|
48
|
+
* Create a ValidatorHASigner backed by a pre-existing SlashingProtectionDatabase.
|
|
49
|
+
* Used for testing HA setups where multiple nodes share the same protection database.
|
|
50
|
+
*/
|
|
51
|
+
export declare function createSignerFromSharedDb(db: SlashingProtectionDatabase, config: Pick<ValidatorHASignerConfig, 'nodeId' | 'pollingIntervalMs' | 'signingTimeoutMs' | 'maxStuckDutiesAgeMs' | 'l1Contracts'>): {
|
|
52
|
+
signer: ValidatorHASigner;
|
|
53
|
+
db: SlashingProtectionDatabase;
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdG9yeS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2ZhY3RvcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBS0EsT0FBTyxLQUFLLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFHM0QsT0FBTyxLQUFLLEVBQUUsa0JBQWtCLEVBQUUsMEJBQTBCLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDakYsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFFN0Q7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWlDRztBQUNILHdCQUFzQixjQUFjLENBQ2xDLE1BQU0sRUFBRSx1QkFBdUIsRUFDL0IsSUFBSSxDQUFDLEVBQUUsa0JBQWtCLEdBQ3hCLE9BQU8sQ0FBQztJQUNULE1BQU0sRUFBRSxpQkFBaUIsQ0FBQztJQUMxQixFQUFFLEVBQUUsMEJBQTBCLENBQUM7Q0FDaEMsQ0FBQyxDQStCRDtBQUVEOzs7R0FHRztBQUNILHdCQUFnQixnQ0FBZ0MsSUFBSSwwQkFBMEIsQ0FFN0U7QUFFRDs7O0dBR0c7QUFDSCx3QkFBZ0Isd0JBQXdCLENBQ3RDLEVBQUUsRUFBRSwwQkFBMEIsRUFDOUIsTUFBTSxFQUFFLElBQUksQ0FDVix1QkFBdUIsRUFDdkIsUUFBUSxHQUFHLG1CQUFtQixHQUFHLGtCQUFrQixHQUFHLHFCQUFxQixHQUFHLGFBQWEsQ0FDNUYsR0FDQTtJQUFFLE1BQU0sRUFBRSxpQkFBaUIsQ0FBQztJQUFDLEVBQUUsRUFBRSwwQkFBMEIsQ0FBQTtDQUFFLENBVy9EIn0=
|
package/dest/factory.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAG3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,uBAAuB,EAC/B,IAAI,CAAC,EAAE,kBAAkB,GACxB,OAAO,CAAC;IACT,MAAM,EAAE,iBAAiB,CAAC;IAC1B,EAAE,EAAE,0BAA0B,CAAC;CAChC,CAAC,CA+BD;AAED;;;GAGG;AACH,wBAAgB,gCAAgC,IAAI,0BAA0B,CAE7E;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,0BAA0B,EAC9B,MAAM,EAAE,IAAI,CACV,uBAAuB,EACvB,QAAQ,GAAG,mBAAmB,GAAG,kBAAkB,GAAG,qBAAqB,GAAG,aAAa,CAC5F,GACA;IAAE,MAAM,EAAE,iBAAiB,CAAC;IAAC,EAAE,EAAE,0BAA0B,CAAA;CAAE,CAW/D"}
|
package/dest/factory.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Factory functions for creating validator HA signers
|
|
3
3
|
*/ import { Pool } from 'pg';
|
|
4
|
+
import { InMemorySlashingProtectionDatabase } from './db/in_memory.js';
|
|
4
5
|
import { PostgresSlashingProtectionDatabase } from './db/postgres.js';
|
|
5
6
|
import { ValidatorHASigner } from './validator_ha_signer.js';
|
|
6
7
|
/**
|
|
@@ -68,3 +69,27 @@ import { ValidatorHASigner } from './validator_ha_signer.js';
|
|
|
68
69
|
db
|
|
69
70
|
};
|
|
70
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Create an in-memory SlashingProtectionDatabase that can be shared across
|
|
74
|
+
* multiple validator nodes in the same process. Used for testing HA setups.
|
|
75
|
+
*/ export function createSharedSlashingProtectionDb() {
|
|
76
|
+
return new InMemorySlashingProtectionDatabase();
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Create a ValidatorHASigner backed by a pre-existing SlashingProtectionDatabase.
|
|
80
|
+
* Used for testing HA setups where multiple nodes share the same protection database.
|
|
81
|
+
*/ export function createSignerFromSharedDb(db, config) {
|
|
82
|
+
const signerConfig = {
|
|
83
|
+
haSigningEnabled: true,
|
|
84
|
+
l1Contracts: config.l1Contracts,
|
|
85
|
+
nodeId: config.nodeId || `shared-${Date.now()}`,
|
|
86
|
+
pollingIntervalMs: config.pollingIntervalMs ?? 100,
|
|
87
|
+
signingTimeoutMs: config.signingTimeoutMs ?? 3000,
|
|
88
|
+
maxStuckDutiesAgeMs: config.maxStuckDutiesAgeMs
|
|
89
|
+
};
|
|
90
|
+
const signer = new ValidatorHASigner(db, signerConfig);
|
|
91
|
+
return {
|
|
92
|
+
signer,
|
|
93
|
+
db
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -23,6 +23,7 @@ export declare class SlashingProtectionService {
|
|
|
23
23
|
private readonly signingTimeoutMs;
|
|
24
24
|
private readonly maxStuckDutiesAgeMs;
|
|
25
25
|
private cleanupRunningPromise;
|
|
26
|
+
private lastOldDutiesCleanupAtMs?;
|
|
26
27
|
constructor(db: SlashingProtectionDatabase, config: ValidatorHASignerConfig);
|
|
27
28
|
/**
|
|
28
29
|
* Check if a duty can be performed and acquire the lock if so.
|
|
@@ -33,7 +34,6 @@ export declare class SlashingProtectionService {
|
|
|
33
34
|
* 2. If insert succeeds, we acquired the lock - return the lockToken
|
|
34
35
|
* 3. If a record exists, handle based on status:
|
|
35
36
|
* - SIGNED: Throw appropriate error (already signed or slashing protection)
|
|
36
|
-
* - FAILED: Delete the failed record
|
|
37
37
|
* - SIGNING: Wait and poll until status changes, then handle result
|
|
38
38
|
*
|
|
39
39
|
* @returns The lockToken that must be used for recordSuccess/deleteDuty
|
|
@@ -65,7 +65,11 @@ export declare class SlashingProtectionService {
|
|
|
65
65
|
* Start running tasks.
|
|
66
66
|
* Cleanup runs immediately on start to recover from any previous crashes.
|
|
67
67
|
*/
|
|
68
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Start the background cleanup task.
|
|
70
|
+
* Also performs one-time cleanup of duties with outdated rollup addresses.
|
|
71
|
+
*/
|
|
72
|
+
start(): Promise<void>;
|
|
69
73
|
/**
|
|
70
74
|
* Stop the background cleanup task.
|
|
71
75
|
*/
|
|
@@ -75,6 +79,6 @@ export declare class SlashingProtectionService {
|
|
|
75
79
|
* Should be called after stop() during graceful shutdown.
|
|
76
80
|
*/
|
|
77
81
|
close(): Promise<void>;
|
|
78
|
-
private
|
|
82
|
+
private cleanup;
|
|
79
83
|
}
|
|
80
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
84
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2xhc2hpbmdfcHJvdGVjdGlvbl9zZXJ2aWNlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvc2xhc2hpbmdfcHJvdGVjdGlvbl9zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQVVBLE9BQU8sRUFDTCxLQUFLLG9CQUFvQixFQUN6QixLQUFLLGdCQUFnQixFQUVyQixLQUFLLG1CQUFtQixFQUV6QixNQUFNLGVBQWUsQ0FBQztBQUV2QixPQUFPLEtBQUssRUFBRSwwQkFBMEIsRUFBRSx1QkFBdUIsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUV0Rjs7Ozs7Ozs7Ozs7Ozs7R0FjRztBQUNILHFCQUFhLHlCQUF5QjtJQVVsQyxPQUFPLENBQUMsUUFBUSxDQUFDLEVBQUU7SUFDbkIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxNQUFNO0lBVnpCLE9BQU8sQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFTO0lBQzdCLE9BQU8sQ0FBQyxRQUFRLENBQUMsaUJBQWlCLENBQVM7SUFDM0MsT0FBTyxDQUFDLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBUztJQUMxQyxPQUFPLENBQUMsUUFBUSxDQUFDLG1CQUFtQixDQUFTO0lBRTdDLE9BQU8sQ0FBQyxxQkFBcUIsQ0FBaUI7SUFDOUMsT0FBTyxDQUFDLHdCQUF3QixDQUFDLENBQVM7SUFFMUMsWUFDbUIsRUFBRSxFQUFFLDBCQUEwQixFQUM5QixNQUFNLEVBQUUsdUJBQXVCLEVBU2pEO0lBRUQ7Ozs7Ozs7Ozs7Ozs7O09BY0c7SUFDRyxjQUFjLENBQUMsTUFBTSxFQUFFLG9CQUFvQixHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FpRWxFO0lBRUQ7Ozs7OztPQU1HO0lBQ0csYUFBYSxDQUFDLE1BQU0sRUFBRSxtQkFBbUIsR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBMkJqRTtJQUVEOzs7Ozs7T0FNRztJQUNHLFVBQVUsQ0FBQyxNQUFNLEVBQUUsZ0JBQWdCLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQXdCM0Q7SUFFRDs7T0FFRztJQUNILElBQUksTUFBTSxJQUFJLE1BQU0sQ0FFbkI7SUFFRDs7O09BR0c7SUFDSDs7O09BR0c7SUFDRyxLQUFLLGtCQVdWO0lBRUQ7O09BRUc7SUFDRyxJQUFJLGtCQUdUO0lBRUQ7OztPQUdHO0lBQ0csS0FBSyxrQkFHVjtZQU1hLE9BQU87Q0E2QnRCIn0=
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slashing_protection_service.d.ts","sourceRoot":"","sources":["../src/slashing_protection_service.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EAErB,KAAK,mBAAmB,EAEzB,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAEtF;;;;;;;;;;;;;;GAcG;AACH,qBAAa,yBAAyB;
|
|
1
|
+
{"version":3,"file":"slashing_protection_service.d.ts","sourceRoot":"","sources":["../src/slashing_protection_service.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EAErB,KAAK,mBAAmB,EAEzB,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAEtF;;;;;;;;;;;;;;GAcG;AACH,qBAAa,yBAAyB;IAUlC,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAVzB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAE7C,OAAO,CAAC,qBAAqB,CAAiB;IAC9C,OAAO,CAAC,wBAAwB,CAAC,CAAS;IAE1C,YACmB,EAAE,EAAE,0BAA0B,EAC9B,MAAM,EAAE,uBAAuB,EASjD;IAED;;;;;;;;;;;;;;OAcG;IACG,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAiElE;IAED;;;;;;OAMG;IACG,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,CA2BjE;IAED;;;;;;OAMG;IACG,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAwB3D;IAED;;OAEG;IACH,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED;;;OAGG;IACH;;;OAGG;IACG,KAAK,kBAWV;IAED;;OAEG;IACG,IAAI,kBAGT;IAED;;;OAGG;IACG,KAAK,kBAGV;YAMa,OAAO;CA6BtB"}
|
|
@@ -30,6 +30,7 @@ import { DutyAlreadySignedError, SlashingProtectionError } from './errors.js';
|
|
|
30
30
|
signingTimeoutMs;
|
|
31
31
|
maxStuckDutiesAgeMs;
|
|
32
32
|
cleanupRunningPromise;
|
|
33
|
+
lastOldDutiesCleanupAtMs;
|
|
33
34
|
constructor(db, config){
|
|
34
35
|
this.db = db;
|
|
35
36
|
this.config = config;
|
|
@@ -38,7 +39,7 @@ import { DutyAlreadySignedError, SlashingProtectionError } from './errors.js';
|
|
|
38
39
|
this.signingTimeoutMs = config.signingTimeoutMs;
|
|
39
40
|
// Default to 144s (2x 72s Aztec slot duration) if not explicitly configured
|
|
40
41
|
this.maxStuckDutiesAgeMs = config.maxStuckDutiesAgeMs ?? 144_000;
|
|
41
|
-
this.cleanupRunningPromise = new RunningPromise(this.
|
|
42
|
+
this.cleanupRunningPromise = new RunningPromise(this.cleanup.bind(this), this.log, this.maxStuckDutiesAgeMs);
|
|
42
43
|
}
|
|
43
44
|
/**
|
|
44
45
|
* Check if a duty can be performed and acquire the lock if so.
|
|
@@ -49,7 +50,6 @@ import { DutyAlreadySignedError, SlashingProtectionError } from './errors.js';
|
|
|
49
50
|
* 2. If insert succeeds, we acquired the lock - return the lockToken
|
|
50
51
|
* 3. If a record exists, handle based on status:
|
|
51
52
|
* - SIGNED: Throw appropriate error (already signed or slashing protection)
|
|
52
|
-
* - FAILED: Delete the failed record
|
|
53
53
|
* - SIGNING: Wait and poll until status changes, then handle result
|
|
54
54
|
*
|
|
55
55
|
* @returns The lockToken that must be used for recordSuccess/deleteDuty
|
|
@@ -67,7 +67,7 @@ import { DutyAlreadySignedError, SlashingProtectionError } from './errors.js';
|
|
|
67
67
|
const { isNew, record } = await this.db.tryInsertOrGetExisting(params);
|
|
68
68
|
if (isNew) {
|
|
69
69
|
// We successfully acquired the lock
|
|
70
|
-
this.log.
|
|
70
|
+
this.log.verbose(`Acquired lock for duty ${dutyType} at slot ${slot}`, {
|
|
71
71
|
validatorAddress: validatorAddress.toString(),
|
|
72
72
|
nodeId
|
|
73
73
|
});
|
|
@@ -120,7 +120,7 @@ import { DutyAlreadySignedError, SlashingProtectionError } from './errors.js';
|
|
|
120
120
|
const blockIndexWithinCheckpoint = getBlockIndexFromDutyIdentifier(params);
|
|
121
121
|
const success = await this.db.updateDutySigned(rollupAddress, validatorAddress, slot, dutyType, signature.toString(), lockToken, blockIndexWithinCheckpoint);
|
|
122
122
|
if (success) {
|
|
123
|
-
this.log.
|
|
123
|
+
this.log.verbose(`Recorded successful signing for duty ${dutyType} at slot ${slot}`, {
|
|
124
124
|
validatorAddress: validatorAddress.toString(),
|
|
125
125
|
nodeId
|
|
126
126
|
});
|
|
@@ -161,7 +161,17 @@ import { DutyAlreadySignedError, SlashingProtectionError } from './errors.js';
|
|
|
161
161
|
/**
|
|
162
162
|
* Start running tasks.
|
|
163
163
|
* Cleanup runs immediately on start to recover from any previous crashes.
|
|
164
|
-
*/
|
|
164
|
+
*/ /**
|
|
165
|
+
* Start the background cleanup task.
|
|
166
|
+
* Also performs one-time cleanup of duties with outdated rollup addresses.
|
|
167
|
+
*/ async start() {
|
|
168
|
+
// One-time cleanup at startup: remove duties from previous rollup versions
|
|
169
|
+
const numOutdatedRollupDuties = await this.db.cleanupOutdatedRollupDuties(this.config.l1Contracts.rollupAddress);
|
|
170
|
+
if (numOutdatedRollupDuties > 0) {
|
|
171
|
+
this.log.info(`Cleaned up ${numOutdatedRollupDuties} duties with outdated rollup address at startup`, {
|
|
172
|
+
currentRollupAddress: this.config.l1Contracts.rollupAddress.toString()
|
|
173
|
+
});
|
|
174
|
+
}
|
|
165
175
|
this.cleanupRunningPromise.start();
|
|
166
176
|
this.log.info('Slashing protection service started', {
|
|
167
177
|
nodeId: this.config.nodeId
|
|
@@ -183,14 +193,33 @@ import { DutyAlreadySignedError, SlashingProtectionError } from './errors.js';
|
|
|
183
193
|
this.log.info('Slashing protection database connection closed');
|
|
184
194
|
}
|
|
185
195
|
/**
|
|
186
|
-
*
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
196
|
+
* Periodic cleanup of stuck duties and optionally old signed duties.
|
|
197
|
+
* Runs in the background via RunningPromise.
|
|
198
|
+
*/ async cleanup() {
|
|
199
|
+
// 1. Clean up stuck duties (our own node's duties that got stuck in 'signing' status)
|
|
200
|
+
const numStuckDuties = await this.db.cleanupOwnStuckDuties(this.config.nodeId, this.maxStuckDutiesAgeMs);
|
|
201
|
+
if (numStuckDuties > 0) {
|
|
202
|
+
this.log.verbose(`Cleaned up ${numStuckDuties} stuck duties`, {
|
|
191
203
|
nodeId: this.config.nodeId,
|
|
192
204
|
maxStuckDutiesAgeMs: this.maxStuckDutiesAgeMs
|
|
193
205
|
});
|
|
194
206
|
}
|
|
207
|
+
// 2. Clean up old signed duties if configured
|
|
208
|
+
// we shouldn't run this as often as stuck duty cleanup.
|
|
209
|
+
if (this.config.cleanupOldDutiesAfterHours !== undefined) {
|
|
210
|
+
const maxAgeMs = this.config.cleanupOldDutiesAfterHours * 60 * 60 * 1000;
|
|
211
|
+
const nowMs = Date.now();
|
|
212
|
+
const shouldRun = this.lastOldDutiesCleanupAtMs === undefined || nowMs - this.lastOldDutiesCleanupAtMs >= maxAgeMs;
|
|
213
|
+
if (shouldRun) {
|
|
214
|
+
const numOldDuties = await this.db.cleanupOldDuties(maxAgeMs);
|
|
215
|
+
this.lastOldDutiesCleanupAtMs = nowMs;
|
|
216
|
+
if (numOldDuties > 0) {
|
|
217
|
+
this.log.verbose(`Cleaned up ${numOldDuties} old signed duties`, {
|
|
218
|
+
cleanupOldDutiesAfterHours: this.config.cleanupOldDutiesAfterHours,
|
|
219
|
+
maxAgeMs
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
195
224
|
}
|
|
196
225
|
}
|
package/dest/types.d.ts
CHANGED
|
@@ -130,10 +130,23 @@ export interface SlashingProtectionDatabase {
|
|
|
130
130
|
* @returns the number of duties cleaned up
|
|
131
131
|
*/
|
|
132
132
|
cleanupOwnStuckDuties(nodeId: string, maxAgeMs: number): Promise<number>;
|
|
133
|
+
/**
|
|
134
|
+
* Cleanup duties with outdated rollup address.
|
|
135
|
+
* Removes all duties where the rollup address doesn't match the current one.
|
|
136
|
+
* Used after a rollup upgrade to clean up duties for the old rollup.
|
|
137
|
+
* @returns the number of duties cleaned up
|
|
138
|
+
*/
|
|
139
|
+
cleanupOutdatedRollupDuties(currentRollupAddress: EthAddress): Promise<number>;
|
|
140
|
+
/**
|
|
141
|
+
* Cleanup old signed duties.
|
|
142
|
+
* Removes only signed duties older than the specified age.
|
|
143
|
+
* @returns the number of duties cleaned up
|
|
144
|
+
*/
|
|
145
|
+
cleanupOldDuties(maxAgeMs: number): Promise<number>;
|
|
133
146
|
/**
|
|
134
147
|
* Close the database connection.
|
|
135
148
|
* Should be called during graceful shutdown.
|
|
136
149
|
*/
|
|
137
150
|
close(): Promise<void>;
|
|
138
151
|
}
|
|
139
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
152
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy90eXBlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsV0FBVyxFQUNYLEtBQUssZ0JBQWdCLEVBQ3JCLEtBQUsscUJBQXFCLEVBQzFCLEtBQUssVUFBVSxFQUNoQixNQUFNLGlDQUFpQyxDQUFDO0FBQ3pDLE9BQU8sS0FBSyxFQUFFLFVBQVUsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBRWhFLE9BQU8sS0FBSyxFQUFFLElBQUksRUFBRSxNQUFNLElBQUksQ0FBQztBQUUvQixPQUFPLEtBQUssRUFBRSx1QkFBdUIsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUMzRCxPQUFPLEVBQ0wsS0FBSywyQkFBMkIsRUFDaEMsS0FBSyxvQkFBb0IsRUFDekIsS0FBSyxnQkFBZ0IsRUFDckIsS0FBSyxjQUFjLEVBQ25CLEtBQUssT0FBTyxFQUNaLFFBQVEsRUFDUixLQUFLLG1CQUFtQixFQUN4QixLQUFLLG1CQUFtQixFQUN4QixLQUFLLG1CQUFtQixFQUN6QixNQUFNLGVBQWUsQ0FBQztBQUV2QixZQUFZLEVBQ1YsMkJBQTJCLEVBQzNCLG9CQUFvQixFQUNwQixnQkFBZ0IsRUFDaEIsY0FBYyxFQUNkLE9BQU8sRUFDUCxtQkFBbUIsRUFDbkIsbUJBQW1CLEVBQ25CLG1CQUFtQixFQUNuQix1QkFBdUIsR0FDeEIsQ0FBQztBQUNGLE9BQU8sRUFBRSxVQUFVLEVBQUUsUUFBUSxFQUFFLCtCQUErQixFQUFFLG1CQUFtQixFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRTNHOztHQUVHO0FBQ0gsTUFBTSxXQUFXLG9CQUFvQjtJQUNuQywyRUFBMkU7SUFDM0UsS0FBSyxFQUFFLE9BQU8sQ0FBQztJQUNmLHFEQUFxRDtJQUNyRCxNQUFNLEVBQUUsbUJBQW1CLENBQUM7Q0FDN0I7QUFFRDs7R0FFRztBQUNILE1BQU0sV0FBVyxrQkFBa0I7SUFDakM7OztPQUdHO0lBQ0gsSUFBSSxD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
|
package/dest/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -61,11 +61,11 @@ export declare class ValidatorHASigner {
|
|
|
61
61
|
* Start the HA signer background tasks (cleanup of stuck duties).
|
|
62
62
|
* Should be called after construction and before signing operations.
|
|
63
63
|
*/
|
|
64
|
-
start(): void
|
|
64
|
+
start(): Promise<void>;
|
|
65
65
|
/**
|
|
66
66
|
* Stop the HA signer background tasks and close database connection.
|
|
67
67
|
* Should be called during graceful shutdown.
|
|
68
68
|
*/
|
|
69
69
|
stop(): Promise<void>;
|
|
70
70
|
}
|
|
71
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
71
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdG9yX2hhX3NpZ25lci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3ZhbGlkYXRvcl9oYV9zaWduZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7OztHQU1HO0FBQ0gsT0FBTyxLQUFLLEVBQUUsUUFBUSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDekQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQzNELE9BQU8sS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBR2pFLE9BQU8sS0FBSyxFQUFFLHVCQUF1QixFQUFFLE1BQU0sYUFBYSxDQUFDO0FBRzNELE9BQU8sRUFDTCxLQUFLLHlCQUF5QixFQUM5QixLQUFLLDBCQUEwQixFQUVoQyxNQUFNLFlBQVksQ0FBQztBQUVwQjs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBa0JHO0FBQ0gscUJBQWEsaUJBQWlCO0lBTzFCLE9BQU8sQ0FBQyxRQUFRLENBQUMsTUFBTTtJQU56QixPQUFPLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBUztJQUM3QixPQUFPLENBQUMsUUFBUSxDQUFDLGtCQUFrQixDQUE0QjtJQUMvRCxPQUFPLENBQUMsUUFBUSxDQUFDLGFBQWEsQ0FBYTtJQUUzQyxZQUNFLEVBQUUsRUFBRSwwQkFBMEIsRUFDYixNQUFNLEVBQUUsdUJBQXVCLEVBa0JqRDtJQUVEOzs7Ozs7Ozs7Ozs7Ozs7O09BZ0JHO0lBQ0csa0JBQWtCLENBQ3RCLGdCQUFnQixFQUFFLFVBQVUsRUFDNUIsV0FBVyxFQUFFLFFBQVEsRUFDckIsT0FBTyxFQUFFLHlCQUF5QixFQUNsQyxNQUFNLEVBQUUsQ0FBQyxXQUFXLEVBQUUsUUFBUSxLQUFLLE9BQU8sQ0FBQyxTQUFTLENBQUMsR0FDcEQsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQStDcEI7SUFFRDs7T0FFRztJQUNILElBQUksTUFBTSxJQUFJLE1BQU0sQ0FFbkI7SUFFRDs7O09BR0c7SUFDRyxLQUFLLGtCQUVWO0lBRUQ7OztPQUdHO0lBQ0csSUFBSSxrQkFHVDtDQUNGIn0=
|
|
@@ -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,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAGjE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAG3D,OAAO,EACL,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAEhC,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,iBAAiB;IAO1B,OAAO,CAAC,QAAQ,CAAC,MAAM;IANzB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA4B;IAC/D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAa;IAE3C,YACE,EAAE,EAAE,0BAA0B,EACb,MAAM,EAAE,uBAAuB,EAkBjD;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,CA+CpB;IAED;;OAEG;IACH,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED;;;OAGG;
|
|
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;AAGjE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAG3D,OAAO,EACL,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAEhC,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,iBAAiB;IAO1B,OAAO,CAAC,QAAQ,CAAC,MAAM;IANzB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA4B;IAC/D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAa;IAE3C,YACE,EAAE,EAAE,0BAA0B,EACb,MAAM,EAAE,uBAAuB,EAkBjD;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,CA+CpB;IAED;;OAEG;IACH,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED;;;OAGG;IACG,KAAK,kBAEV;IAED;;;OAGG;IACG,IAAI,kBAGT;CACF"}
|
|
@@ -119,8 +119,8 @@ import { getBlockNumberFromSigningContext } from './types.js';
|
|
|
119
119
|
/**
|
|
120
120
|
* Start the HA signer background tasks (cleanup of stuck duties).
|
|
121
121
|
* Should be called after construction and before signing operations.
|
|
122
|
-
*/ start() {
|
|
123
|
-
this.slashingProtection.start();
|
|
122
|
+
*/ async start() {
|
|
123
|
+
await this.slashingProtection.start();
|
|
124
124
|
}
|
|
125
125
|
/**
|
|
126
126
|
* Stop the HA signer background tasks and close database connection.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/validator-ha-signer",
|
|
3
|
-
"version": "0.0.1-commit.
|
|
3
|
+
"version": "0.0.1-commit.2448fdb",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./config": "./dest/config.js",
|
|
@@ -74,8 +74,8 @@
|
|
|
74
74
|
]
|
|
75
75
|
},
|
|
76
76
|
"dependencies": {
|
|
77
|
-
"@aztec/ethereum": "0.0.1-commit.
|
|
78
|
-
"@aztec/foundation": "0.0.1-commit.
|
|
77
|
+
"@aztec/ethereum": "0.0.1-commit.2448fdb",
|
|
78
|
+
"@aztec/foundation": "0.0.1-commit.2448fdb",
|
|
79
79
|
"node-pg-migrate": "^8.0.4",
|
|
80
80
|
"pg": "^8.11.3",
|
|
81
81
|
"tslib": "^2.4.0",
|
package/src/config.ts
CHANGED
|
@@ -31,6 +31,8 @@ export interface ValidatorHASignerConfig {
|
|
|
31
31
|
signingTimeoutMs: number;
|
|
32
32
|
/** Maximum age of a stuck duty in ms (defaults to 2x hardcoded Aztec slot duration if not set) */
|
|
33
33
|
maxStuckDutiesAgeMs?: number;
|
|
34
|
+
/** Optional: clean up old duties after this many hours (disabled if not set) */
|
|
35
|
+
cleanupOldDutiesAfterHours?: number;
|
|
34
36
|
/**
|
|
35
37
|
* PostgreSQL connection string
|
|
36
38
|
* Format: postgresql://user:password@host:port/database
|
|
@@ -84,6 +86,11 @@ export const validatorHASignerConfigMappings: ConfigMappingsType<ValidatorHASign
|
|
|
84
86
|
description: 'The maximum age of a stuck duty in ms (defaults to 2x Aztec slot duration)',
|
|
85
87
|
...optionalNumberConfigHelper(),
|
|
86
88
|
},
|
|
89
|
+
cleanupOldDutiesAfterHours: {
|
|
90
|
+
env: 'VALIDATOR_HA_OLD_DUTIES_MAX_AGE_H',
|
|
91
|
+
description: 'Optional: clean up old duties after this many hours (disabled if not set)',
|
|
92
|
+
...optionalNumberConfigHelper(),
|
|
93
|
+
},
|
|
87
94
|
databaseUrl: {
|
|
88
95
|
env: 'VALIDATOR_HA_DATABASE_URL',
|
|
89
96
|
description:
|
|
@@ -133,6 +140,7 @@ export const ValidatorHASignerConfigSchema = z.object({
|
|
|
133
140
|
pollingIntervalMs: z.number().min(0),
|
|
134
141
|
signingTimeoutMs: z.number().min(0),
|
|
135
142
|
maxStuckDutiesAgeMs: z.number().min(0).optional(),
|
|
143
|
+
cleanupOldDutiesAfterHours: z.number().min(0).optional(),
|
|
136
144
|
databaseUrl: z.string().optional(),
|
|
137
145
|
poolMaxCount: z.number().min(0).optional(),
|
|
138
146
|
poolMinCount: z.number().min(0).optional(),
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory implementation of SlashingProtectionDatabase for testing.
|
|
3
|
+
* Used to simulate shared slashing protection in HA test setups without requiring PostgreSQL.
|
|
4
|
+
*/
|
|
5
|
+
import type { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
6
|
+
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
7
|
+
|
|
8
|
+
import type { SlashingProtectionDatabase, TryInsertOrGetResult, ValidatorDutyRecord } from '../types.js';
|
|
9
|
+
import type { CheckAndRecordParams, DutyType } from './types.js';
|
|
10
|
+
import { DutyStatus, getBlockIndexFromDutyIdentifier } from './types.js';
|
|
11
|
+
|
|
12
|
+
/** Creates a unique key for a duty based on its identifying fields. */
|
|
13
|
+
function dutyKey(
|
|
14
|
+
rollupAddress: EthAddress,
|
|
15
|
+
validatorAddress: EthAddress,
|
|
16
|
+
slot: SlotNumber,
|
|
17
|
+
dutyType: DutyType,
|
|
18
|
+
blockIndexWithinCheckpoint: number,
|
|
19
|
+
): string {
|
|
20
|
+
return `${rollupAddress}:${validatorAddress}:${slot}:${dutyType}:${blockIndexWithinCheckpoint}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** In-memory slashing protection database for testing HA setups. */
|
|
24
|
+
export class InMemorySlashingProtectionDatabase implements SlashingProtectionDatabase {
|
|
25
|
+
private duties = new Map<string, ValidatorDutyRecord>();
|
|
26
|
+
|
|
27
|
+
tryInsertOrGetExisting(params: CheckAndRecordParams): Promise<TryInsertOrGetResult> {
|
|
28
|
+
const blockIndex = getBlockIndexFromDutyIdentifier(params);
|
|
29
|
+
const key = dutyKey(params.rollupAddress, params.validatorAddress, params.slot, params.dutyType, blockIndex);
|
|
30
|
+
|
|
31
|
+
const existing = this.duties.get(key);
|
|
32
|
+
if (existing) {
|
|
33
|
+
return Promise.resolve({ isNew: false, record: existing });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const lockToken = `lock-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
37
|
+
const record: ValidatorDutyRecord = {
|
|
38
|
+
rollupAddress: params.rollupAddress,
|
|
39
|
+
validatorAddress: params.validatorAddress,
|
|
40
|
+
slot: params.slot,
|
|
41
|
+
blockNumber: params.blockNumber as BlockNumber,
|
|
42
|
+
blockIndexWithinCheckpoint: blockIndex,
|
|
43
|
+
dutyType: params.dutyType,
|
|
44
|
+
status: DutyStatus.SIGNING,
|
|
45
|
+
messageHash: params.messageHash,
|
|
46
|
+
nodeId: params.nodeId,
|
|
47
|
+
lockToken,
|
|
48
|
+
startedAt: new Date(),
|
|
49
|
+
};
|
|
50
|
+
this.duties.set(key, record);
|
|
51
|
+
return Promise.resolve({ isNew: true, record });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
updateDutySigned(
|
|
55
|
+
rollupAddress: EthAddress,
|
|
56
|
+
validatorAddress: EthAddress,
|
|
57
|
+
slot: SlotNumber,
|
|
58
|
+
dutyType: DutyType,
|
|
59
|
+
signature: string,
|
|
60
|
+
lockToken: string,
|
|
61
|
+
blockIndexWithinCheckpoint: number,
|
|
62
|
+
): Promise<boolean> {
|
|
63
|
+
const key = dutyKey(rollupAddress, validatorAddress, slot, dutyType, blockIndexWithinCheckpoint);
|
|
64
|
+
const record = this.duties.get(key);
|
|
65
|
+
if (!record || record.lockToken !== lockToken) {
|
|
66
|
+
return Promise.resolve(false);
|
|
67
|
+
}
|
|
68
|
+
record.status = DutyStatus.SIGNED;
|
|
69
|
+
record.signature = signature;
|
|
70
|
+
record.completedAt = new Date();
|
|
71
|
+
return Promise.resolve(true);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
deleteDuty(
|
|
75
|
+
rollupAddress: EthAddress,
|
|
76
|
+
validatorAddress: EthAddress,
|
|
77
|
+
slot: SlotNumber,
|
|
78
|
+
dutyType: DutyType,
|
|
79
|
+
lockToken: string,
|
|
80
|
+
blockIndexWithinCheckpoint: number,
|
|
81
|
+
): Promise<boolean> {
|
|
82
|
+
const key = dutyKey(rollupAddress, validatorAddress, slot, dutyType, blockIndexWithinCheckpoint);
|
|
83
|
+
const record = this.duties.get(key);
|
|
84
|
+
if (!record || record.lockToken !== lockToken) {
|
|
85
|
+
return Promise.resolve(false);
|
|
86
|
+
}
|
|
87
|
+
this.duties.delete(key);
|
|
88
|
+
return Promise.resolve(true);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
cleanupOwnStuckDuties(_nodeId: string, _maxAgeMs: number): Promise<number> {
|
|
92
|
+
return Promise.resolve(0);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
cleanupOutdatedRollupDuties(_currentRollupAddress: EthAddress): Promise<number> {
|
|
96
|
+
return Promise.resolve(0);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
cleanupOldDuties(_maxAgeMs: number): Promise<number> {
|
|
100
|
+
return Promise.resolve(0);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
close(): Promise<void> {
|
|
104
|
+
this.duties.clear();
|
|
105
|
+
return Promise.resolve();
|
|
106
|
+
}
|
|
107
|
+
}
|
package/src/db/postgres.ts
CHANGED
|
@@ -11,6 +11,8 @@ import type { QueryResult, QueryResultRow } from 'pg';
|
|
|
11
11
|
|
|
12
12
|
import type { SlashingProtectionDatabase, TryInsertOrGetResult } from '../types.js';
|
|
13
13
|
import {
|
|
14
|
+
CLEANUP_OLD_DUTIES,
|
|
15
|
+
CLEANUP_OUTDATED_ROLLUP_DUTIES,
|
|
14
16
|
CLEANUP_OWN_STUCK_DUTIES,
|
|
15
17
|
DELETE_DUTY,
|
|
16
18
|
INSERT_OR_GET_DUTY,
|
|
@@ -252,8 +254,29 @@ export class PostgresSlashingProtectionDatabase implements SlashingProtectionDat
|
|
|
252
254
|
* @returns the number of duties cleaned up
|
|
253
255
|
*/
|
|
254
256
|
async cleanupOwnStuckDuties(nodeId: string, maxAgeMs: number): Promise<number> {
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
+
const result = await this.pool.query(CLEANUP_OWN_STUCK_DUTIES, [nodeId, maxAgeMs]);
|
|
258
|
+
return result.rowCount ?? 0;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Cleanup duties with outdated rollup address.
|
|
263
|
+
* Removes all duties where the rollup address doesn't match the current one.
|
|
264
|
+
* Used after a rollup upgrade to clean up duties for the old rollup.
|
|
265
|
+
* @returns the number of duties cleaned up
|
|
266
|
+
*/
|
|
267
|
+
async cleanupOutdatedRollupDuties(currentRollupAddress: EthAddress): Promise<number> {
|
|
268
|
+
const result = await this.pool.query(CLEANUP_OUTDATED_ROLLUP_DUTIES, [currentRollupAddress.toString()]);
|
|
269
|
+
return result.rowCount ?? 0;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Cleanup old signed duties.
|
|
274
|
+
* Removes only signed duties older than the specified age.
|
|
275
|
+
* Does not remove 'signing' duties as they may be in progress.
|
|
276
|
+
* @returns the number of duties cleaned up
|
|
277
|
+
*/
|
|
278
|
+
async cleanupOldDuties(maxAgeMs: number): Promise<number> {
|
|
279
|
+
const result = await this.pool.query(CLEANUP_OLD_DUTIES, [maxAgeMs]);
|
|
257
280
|
return result.rowCount ?? 0;
|
|
258
281
|
}
|
|
259
282
|
}
|
package/src/db/schema.ts
CHANGED
|
@@ -22,7 +22,7 @@ CREATE TABLE IF NOT EXISTS validator_duties (
|
|
|
22
22
|
block_number BIGINT NOT NULL,
|
|
23
23
|
block_index_within_checkpoint INTEGER NOT NULL DEFAULT 0,
|
|
24
24
|
duty_type VARCHAR(30) NOT NULL CHECK (duty_type IN ('BLOCK_PROPOSAL', 'CHECKPOINT_PROPOSAL', 'ATTESTATION', 'ATTESTATIONS_AND_SIGNERS', 'GOVERNANCE_VOTE', 'SLASHING_VOTE')),
|
|
25
|
-
status VARCHAR(20) NOT NULL CHECK (status IN ('signing', 'signed'
|
|
25
|
+
status VARCHAR(20) NOT NULL CHECK (status IN ('signing', 'signed')),
|
|
26
26
|
message_hash VARCHAR(66) NOT NULL,
|
|
27
27
|
signature VARCHAR(132),
|
|
28
28
|
node_id VARCHAR(255) NOT NULL,
|
|
@@ -203,23 +203,34 @@ WHERE status = 'signed'
|
|
|
203
203
|
|
|
204
204
|
/**
|
|
205
205
|
* Query to clean up old duties (for maintenance)
|
|
206
|
-
* Removes duties older than a specified
|
|
206
|
+
* Removes SIGNED duties older than a specified age (in milliseconds)
|
|
207
207
|
*/
|
|
208
208
|
export const CLEANUP_OLD_DUTIES = `
|
|
209
209
|
DELETE FROM validator_duties
|
|
210
|
-
WHERE status
|
|
211
|
-
AND started_at < $1;
|
|
210
|
+
WHERE status = 'signed'
|
|
211
|
+
AND started_at < CURRENT_TIMESTAMP - ($1 || ' milliseconds')::INTERVAL;
|
|
212
212
|
`;
|
|
213
213
|
|
|
214
214
|
/**
|
|
215
215
|
* Query to cleanup own stuck duties
|
|
216
216
|
* Removes duties in 'signing' status for a specific node that are older than maxAgeMs
|
|
217
|
+
* Uses DB's CURRENT_TIMESTAMP to avoid clock skew issues between nodes
|
|
217
218
|
*/
|
|
218
219
|
export const CLEANUP_OWN_STUCK_DUTIES = `
|
|
219
220
|
DELETE FROM validator_duties
|
|
220
221
|
WHERE node_id = $1
|
|
221
222
|
AND status = 'signing'
|
|
222
|
-
AND started_at < $2;
|
|
223
|
+
AND started_at < CURRENT_TIMESTAMP - ($2 || ' milliseconds')::INTERVAL;
|
|
224
|
+
`;
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Query to cleanup duties with outdated rollup address
|
|
228
|
+
* Removes all duties where the rollup address doesn't match the current one
|
|
229
|
+
* Used after a rollup upgrade to clean up duties for the old rollup
|
|
230
|
+
*/
|
|
231
|
+
export const CLEANUP_OUTDATED_ROLLUP_DUTIES = `
|
|
232
|
+
DELETE FROM validator_duties
|
|
233
|
+
WHERE rollup_address != $1;
|
|
223
234
|
`;
|
|
224
235
|
|
|
225
236
|
/**
|
package/src/db/types.ts
CHANGED
|
@@ -81,7 +81,7 @@ export interface ValidatorDutyRecord {
|
|
|
81
81
|
startedAt: Date;
|
|
82
82
|
/** When the duty signing was completed (success or failure) */
|
|
83
83
|
completedAt?: Date;
|
|
84
|
-
/** Error message
|
|
84
|
+
/** Error message (currently unused) */
|
|
85
85
|
errorMessage?: string;
|
|
86
86
|
}
|
|
87
87
|
|
package/src/factory.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { Pool } from 'pg';
|
|
5
5
|
|
|
6
6
|
import type { ValidatorHASignerConfig } from './config.js';
|
|
7
|
+
import { InMemorySlashingProtectionDatabase } from './db/in_memory.js';
|
|
7
8
|
import { PostgresSlashingProtectionDatabase } from './db/postgres.js';
|
|
8
9
|
import type { CreateHASignerDeps, SlashingProtectionDatabase } from './types.js';
|
|
9
10
|
import { ValidatorHASigner } from './validator_ha_signer.js';
|
|
@@ -80,3 +81,34 @@ export async function createHASigner(
|
|
|
80
81
|
|
|
81
82
|
return { signer, db };
|
|
82
83
|
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Create an in-memory SlashingProtectionDatabase that can be shared across
|
|
87
|
+
* multiple validator nodes in the same process. Used for testing HA setups.
|
|
88
|
+
*/
|
|
89
|
+
export function createSharedSlashingProtectionDb(): SlashingProtectionDatabase {
|
|
90
|
+
return new InMemorySlashingProtectionDatabase();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Create a ValidatorHASigner backed by a pre-existing SlashingProtectionDatabase.
|
|
95
|
+
* Used for testing HA setups where multiple nodes share the same protection database.
|
|
96
|
+
*/
|
|
97
|
+
export function createSignerFromSharedDb(
|
|
98
|
+
db: SlashingProtectionDatabase,
|
|
99
|
+
config: Pick<
|
|
100
|
+
ValidatorHASignerConfig,
|
|
101
|
+
'nodeId' | 'pollingIntervalMs' | 'signingTimeoutMs' | 'maxStuckDutiesAgeMs' | 'l1Contracts'
|
|
102
|
+
>,
|
|
103
|
+
): { signer: ValidatorHASigner; db: SlashingProtectionDatabase } {
|
|
104
|
+
const signerConfig: ValidatorHASignerConfig = {
|
|
105
|
+
haSigningEnabled: true,
|
|
106
|
+
l1Contracts: config.l1Contracts,
|
|
107
|
+
nodeId: config.nodeId || `shared-${Date.now()}`,
|
|
108
|
+
pollingIntervalMs: config.pollingIntervalMs ?? 100,
|
|
109
|
+
signingTimeoutMs: config.signingTimeoutMs ?? 3000,
|
|
110
|
+
maxStuckDutiesAgeMs: config.maxStuckDutiesAgeMs,
|
|
111
|
+
};
|
|
112
|
+
const signer = new ValidatorHASigner(db, signerConfig);
|
|
113
|
+
return { signer, db };
|
|
114
|
+
}
|
|
@@ -40,6 +40,7 @@ export class SlashingProtectionService {
|
|
|
40
40
|
private readonly maxStuckDutiesAgeMs: number;
|
|
41
41
|
|
|
42
42
|
private cleanupRunningPromise: RunningPromise;
|
|
43
|
+
private lastOldDutiesCleanupAtMs?: number;
|
|
43
44
|
|
|
44
45
|
constructor(
|
|
45
46
|
private readonly db: SlashingProtectionDatabase,
|
|
@@ -51,11 +52,7 @@ export class SlashingProtectionService {
|
|
|
51
52
|
// Default to 144s (2x 72s Aztec slot duration) if not explicitly configured
|
|
52
53
|
this.maxStuckDutiesAgeMs = config.maxStuckDutiesAgeMs ?? 144_000;
|
|
53
54
|
|
|
54
|
-
this.cleanupRunningPromise = new RunningPromise(
|
|
55
|
-
this.cleanupStuckDuties.bind(this),
|
|
56
|
-
this.log,
|
|
57
|
-
this.maxStuckDutiesAgeMs,
|
|
58
|
-
);
|
|
55
|
+
this.cleanupRunningPromise = new RunningPromise(this.cleanup.bind(this), this.log, this.maxStuckDutiesAgeMs);
|
|
59
56
|
}
|
|
60
57
|
|
|
61
58
|
/**
|
|
@@ -67,7 +64,6 @@ export class SlashingProtectionService {
|
|
|
67
64
|
* 2. If insert succeeds, we acquired the lock - return the lockToken
|
|
68
65
|
* 3. If a record exists, handle based on status:
|
|
69
66
|
* - SIGNED: Throw appropriate error (already signed or slashing protection)
|
|
70
|
-
* - FAILED: Delete the failed record
|
|
71
67
|
* - SIGNING: Wait and poll until status changes, then handle result
|
|
72
68
|
*
|
|
73
69
|
* @returns The lockToken that must be used for recordSuccess/deleteDuty
|
|
@@ -89,7 +85,7 @@ export class SlashingProtectionService {
|
|
|
89
85
|
|
|
90
86
|
if (isNew) {
|
|
91
87
|
// We successfully acquired the lock
|
|
92
|
-
this.log.
|
|
88
|
+
this.log.verbose(`Acquired lock for duty ${dutyType} at slot ${slot}`, {
|
|
93
89
|
validatorAddress: validatorAddress.toString(),
|
|
94
90
|
nodeId,
|
|
95
91
|
});
|
|
@@ -163,7 +159,7 @@ export class SlashingProtectionService {
|
|
|
163
159
|
);
|
|
164
160
|
|
|
165
161
|
if (success) {
|
|
166
|
-
this.log.
|
|
162
|
+
this.log.verbose(`Recorded successful signing for duty ${dutyType} at slot ${slot}`, {
|
|
167
163
|
validatorAddress: validatorAddress.toString(),
|
|
168
164
|
nodeId,
|
|
169
165
|
});
|
|
@@ -221,7 +217,19 @@ export class SlashingProtectionService {
|
|
|
221
217
|
* Start running tasks.
|
|
222
218
|
* Cleanup runs immediately on start to recover from any previous crashes.
|
|
223
219
|
*/
|
|
224
|
-
|
|
220
|
+
/**
|
|
221
|
+
* Start the background cleanup task.
|
|
222
|
+
* Also performs one-time cleanup of duties with outdated rollup addresses.
|
|
223
|
+
*/
|
|
224
|
+
async start() {
|
|
225
|
+
// One-time cleanup at startup: remove duties from previous rollup versions
|
|
226
|
+
const numOutdatedRollupDuties = await this.db.cleanupOutdatedRollupDuties(this.config.l1Contracts.rollupAddress);
|
|
227
|
+
if (numOutdatedRollupDuties > 0) {
|
|
228
|
+
this.log.info(`Cleaned up ${numOutdatedRollupDuties} duties with outdated rollup address at startup`, {
|
|
229
|
+
currentRollupAddress: this.config.l1Contracts.rollupAddress.toString(),
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
225
233
|
this.cleanupRunningPromise.start();
|
|
226
234
|
this.log.info('Slashing protection service started', { nodeId: this.config.nodeId });
|
|
227
235
|
}
|
|
@@ -244,15 +252,36 @@ export class SlashingProtectionService {
|
|
|
244
252
|
}
|
|
245
253
|
|
|
246
254
|
/**
|
|
247
|
-
*
|
|
255
|
+
* Periodic cleanup of stuck duties and optionally old signed duties.
|
|
256
|
+
* Runs in the background via RunningPromise.
|
|
248
257
|
*/
|
|
249
|
-
private async
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
258
|
+
private async cleanup() {
|
|
259
|
+
// 1. Clean up stuck duties (our own node's duties that got stuck in 'signing' status)
|
|
260
|
+
const numStuckDuties = await this.db.cleanupOwnStuckDuties(this.config.nodeId, this.maxStuckDutiesAgeMs);
|
|
261
|
+
if (numStuckDuties > 0) {
|
|
262
|
+
this.log.verbose(`Cleaned up ${numStuckDuties} stuck duties`, {
|
|
253
263
|
nodeId: this.config.nodeId,
|
|
254
264
|
maxStuckDutiesAgeMs: this.maxStuckDutiesAgeMs,
|
|
255
265
|
});
|
|
256
266
|
}
|
|
267
|
+
|
|
268
|
+
// 2. Clean up old signed duties if configured
|
|
269
|
+
// we shouldn't run this as often as stuck duty cleanup.
|
|
270
|
+
if (this.config.cleanupOldDutiesAfterHours !== undefined) {
|
|
271
|
+
const maxAgeMs = this.config.cleanupOldDutiesAfterHours * 60 * 60 * 1000;
|
|
272
|
+
const nowMs = Date.now();
|
|
273
|
+
const shouldRun =
|
|
274
|
+
this.lastOldDutiesCleanupAtMs === undefined || nowMs - this.lastOldDutiesCleanupAtMs >= maxAgeMs;
|
|
275
|
+
if (shouldRun) {
|
|
276
|
+
const numOldDuties = await this.db.cleanupOldDuties(maxAgeMs);
|
|
277
|
+
this.lastOldDutiesCleanupAtMs = nowMs;
|
|
278
|
+
if (numOldDuties > 0) {
|
|
279
|
+
this.log.verbose(`Cleaned up ${numOldDuties} old signed duties`, {
|
|
280
|
+
cleanupOldDutiesAfterHours: this.config.cleanupOldDutiesAfterHours,
|
|
281
|
+
maxAgeMs,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
257
286
|
}
|
|
258
287
|
}
|
package/src/types.ts
CHANGED
|
@@ -203,6 +203,21 @@ export interface SlashingProtectionDatabase {
|
|
|
203
203
|
*/
|
|
204
204
|
cleanupOwnStuckDuties(nodeId: string, maxAgeMs: number): Promise<number>;
|
|
205
205
|
|
|
206
|
+
/**
|
|
207
|
+
* Cleanup duties with outdated rollup address.
|
|
208
|
+
* Removes all duties where the rollup address doesn't match the current one.
|
|
209
|
+
* Used after a rollup upgrade to clean up duties for the old rollup.
|
|
210
|
+
* @returns the number of duties cleaned up
|
|
211
|
+
*/
|
|
212
|
+
cleanupOutdatedRollupDuties(currentRollupAddress: EthAddress): Promise<number>;
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Cleanup old signed duties.
|
|
216
|
+
* Removes only signed duties older than the specified age.
|
|
217
|
+
* @returns the number of duties cleaned up
|
|
218
|
+
*/
|
|
219
|
+
cleanupOldDuties(maxAgeMs: number): Promise<number>;
|
|
220
|
+
|
|
206
221
|
/**
|
|
207
222
|
* Close the database connection.
|
|
208
223
|
* Should be called during graceful shutdown.
|
|
@@ -147,8 +147,8 @@ export class ValidatorHASigner {
|
|
|
147
147
|
* Start the HA signer background tasks (cleanup of stuck duties).
|
|
148
148
|
* Should be called after construction and before signing operations.
|
|
149
149
|
*/
|
|
150
|
-
start() {
|
|
151
|
-
this.slashingProtection.start();
|
|
150
|
+
async start() {
|
|
151
|
+
await this.slashingProtection.start();
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
/**
|