@aztec/validator-ha-signer 0.0.1-commit.96dac018d → 0.0.1-commit.993d240
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 +0 -2
- package/dest/db/index.d.ts +2 -1
- package/dest/db/index.d.ts.map +1 -1
- package/dest/db/index.js +1 -0
- package/dest/db/lmdb.d.ts +70 -0
- package/dest/db/lmdb.d.ts.map +1 -0
- package/dest/db/lmdb.js +223 -0
- package/dest/db/migrations/1_initial-schema.d.ts +4 -2
- package/dest/db/migrations/1_initial-schema.d.ts.map +1 -1
- package/dest/db/migrations/1_initial-schema.js +34 -4
- package/dest/db/migrations/2_add-checkpoint-number.d.ts +7 -0
- package/dest/db/migrations/2_add-checkpoint-number.d.ts.map +1 -0
- package/dest/db/migrations/2_add-checkpoint-number.js +17 -0
- package/dest/db/postgres.d.ts +4 -2
- package/dest/db/postgres.d.ts.map +1 -1
- package/dest/db/postgres.js +15 -13
- package/dest/db/schema.d.ts +6 -6
- package/dest/db/schema.d.ts.map +1 -1
- package/dest/db/schema.js +9 -4
- package/dest/db/types.d.ts +44 -7
- package/dest/db/types.d.ts.map +1 -1
- package/dest/db/types.js +26 -0
- package/dest/factory.d.ts +39 -4
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +71 -7
- package/dest/slashing_protection_service.d.ts +3 -3
- package/dest/slashing_protection_service.d.ts.map +1 -1
- package/dest/slashing_protection_service.js +4 -4
- package/dest/types.d.ts +8 -3
- package/dest/types.d.ts.map +1 -1
- package/dest/types.js +2 -1
- package/dest/validator_ha_signer.d.ts +3 -3
- package/dest/validator_ha_signer.d.ts.map +1 -1
- package/dest/validator_ha_signer.js +4 -6
- package/package.json +9 -7
- package/src/db/index.ts +1 -0
- package/src/db/lmdb.ts +308 -0
- package/src/db/migrations/1_initial-schema.ts +35 -4
- package/src/db/migrations/2_add-checkpoint-number.ts +19 -0
- package/src/db/postgres.ts +15 -11
- package/src/db/schema.ts +9 -4
- package/src/db/types.ts +63 -6
- package/src/factory.ts +100 -6
- package/src/slashing_protection_service.ts +6 -6
- package/src/types.ts +11 -0
- package/src/validator_ha_signer.ts +6 -8
package/src/db/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { BlockNumber, CheckpointNumber, type IndexWithinCheckpoint, SlotNumber } from '@aztec/foundation/branded-types';
|
|
2
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
3
3
|
import type { Signature } from '@aztec/foundation/eth-signature';
|
|
4
4
|
import { DutyType } from '@aztec/stdlib/ha-signing';
|
|
5
5
|
|
|
@@ -11,6 +11,7 @@ export interface DutyRow {
|
|
|
11
11
|
validator_address: string;
|
|
12
12
|
slot: string;
|
|
13
13
|
block_number: string;
|
|
14
|
+
checkpoint_number: string;
|
|
14
15
|
block_index_within_checkpoint: number;
|
|
15
16
|
duty_type: DutyType;
|
|
16
17
|
status: DutyStatus;
|
|
@@ -23,6 +24,31 @@ export interface DutyRow {
|
|
|
23
24
|
error_message: string | null;
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Plain-primitive representation of a duty record suitable for serialization
|
|
29
|
+
* (e.g. msgpackr for LMDB). All domain types are stored as their string/number
|
|
30
|
+
* equivalents. Timestamps are Unix milliseconds.
|
|
31
|
+
*/
|
|
32
|
+
export interface StoredDutyRecord {
|
|
33
|
+
rollupAddress: string;
|
|
34
|
+
validatorAddress: string;
|
|
35
|
+
slot: string;
|
|
36
|
+
blockNumber: string;
|
|
37
|
+
checkpointNumber: string;
|
|
38
|
+
blockIndexWithinCheckpoint: number;
|
|
39
|
+
dutyType: DutyType;
|
|
40
|
+
status: DutyStatus;
|
|
41
|
+
messageHash: string;
|
|
42
|
+
signature?: string;
|
|
43
|
+
nodeId: string;
|
|
44
|
+
lockToken: string;
|
|
45
|
+
/** Unix timestamp in milliseconds when signing started */
|
|
46
|
+
startedAtMs: number;
|
|
47
|
+
/** Unix timestamp in milliseconds when signing completed */
|
|
48
|
+
completedAtMs?: number;
|
|
49
|
+
errorMessage?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
26
52
|
/**
|
|
27
53
|
* Row type from INSERT_OR_GET_DUTY query (includes is_new flag)
|
|
28
54
|
*/
|
|
@@ -42,7 +68,8 @@ export enum DutyStatus {
|
|
|
42
68
|
export { DutyType };
|
|
43
69
|
|
|
44
70
|
/**
|
|
45
|
-
*
|
|
71
|
+
* Rich representation of a validator duty, with branded types and Date objects.
|
|
72
|
+
* This is the common output type returned by all SlashingProtectionDatabase implementations.
|
|
46
73
|
*/
|
|
47
74
|
export interface ValidatorDutyRecord {
|
|
48
75
|
/** Ethereum address of the rollup contract */
|
|
@@ -51,8 +78,10 @@ export interface ValidatorDutyRecord {
|
|
|
51
78
|
validatorAddress: EthAddress;
|
|
52
79
|
/** Slot number for this duty */
|
|
53
80
|
slot: SlotNumber;
|
|
54
|
-
/** Block number for this duty */
|
|
81
|
+
/** Block number for this duty (0 for non-block-proposal duties) */
|
|
55
82
|
blockNumber: BlockNumber;
|
|
83
|
+
/** Checkpoint number for this duty (0 for attestation and vote duties) */
|
|
84
|
+
checkpointNumber: CheckpointNumber;
|
|
56
85
|
/** Block index within checkpoint (0, 1, 2... for block proposals, -1 for other duty types) */
|
|
57
86
|
blockIndexWithinCheckpoint: number;
|
|
58
87
|
/** Type of duty being performed */
|
|
@@ -75,6 +104,32 @@ export interface ValidatorDutyRecord {
|
|
|
75
104
|
errorMessage?: string;
|
|
76
105
|
}
|
|
77
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Convert a {@link StoredDutyRecord} (plain-primitive wire format) to a
|
|
109
|
+
* {@link ValidatorDutyRecord} (rich domain type).
|
|
110
|
+
*
|
|
111
|
+
* Shared by LMDB and any future non-Postgres backend implementations.
|
|
112
|
+
*/
|
|
113
|
+
export function recordFromFields(stored: StoredDutyRecord): ValidatorDutyRecord {
|
|
114
|
+
return {
|
|
115
|
+
rollupAddress: EthAddress.fromString(stored.rollupAddress),
|
|
116
|
+
validatorAddress: EthAddress.fromString(stored.validatorAddress),
|
|
117
|
+
slot: SlotNumber.fromString(stored.slot),
|
|
118
|
+
blockNumber: BlockNumber.fromString(stored.blockNumber),
|
|
119
|
+
checkpointNumber: CheckpointNumber.fromString(stored.checkpointNumber),
|
|
120
|
+
blockIndexWithinCheckpoint: stored.blockIndexWithinCheckpoint,
|
|
121
|
+
dutyType: stored.dutyType,
|
|
122
|
+
status: stored.status,
|
|
123
|
+
messageHash: stored.messageHash,
|
|
124
|
+
signature: stored.signature,
|
|
125
|
+
nodeId: stored.nodeId,
|
|
126
|
+
lockToken: stored.lockToken,
|
|
127
|
+
startedAt: new Date(stored.startedAtMs),
|
|
128
|
+
completedAt: stored.completedAtMs !== undefined ? new Date(stored.completedAtMs) : undefined,
|
|
129
|
+
errorMessage: stored.errorMessage,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
78
133
|
/**
|
|
79
134
|
* Duty identifier for block proposals.
|
|
80
135
|
* blockIndexWithinCheckpoint is REQUIRED and must be >= 0.
|
|
@@ -153,8 +208,10 @@ export function getBlockIndexFromDutyIdentifier(duty: DutyIdentifier): number {
|
|
|
153
208
|
* Additional parameters for checking and recording a new duty
|
|
154
209
|
*/
|
|
155
210
|
interface CheckAndRecordExtra {
|
|
156
|
-
/** Block number for this duty */
|
|
157
|
-
blockNumber: BlockNumber
|
|
211
|
+
/** Block number for this duty (0 for non-block-proposal duties) */
|
|
212
|
+
blockNumber: BlockNumber;
|
|
213
|
+
/** Checkpoint number for this duty (0 for attestation and vote duties) */
|
|
214
|
+
checkpointNumber: CheckpointNumber;
|
|
158
215
|
/** The signing root (hash) for this duty */
|
|
159
216
|
messageHash: string;
|
|
160
217
|
/** Identifier for the node that acquired the lock */
|
package/src/factory.ts
CHANGED
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
* Factory functions for creating validator HA signers
|
|
3
3
|
*/
|
|
4
4
|
import { DateProvider } from '@aztec/foundation/timer';
|
|
5
|
-
import
|
|
5
|
+
import { createStore } from '@aztec/kv-store/lmdb-v2';
|
|
6
|
+
import type { LocalSignerConfig, ValidatorHASignerConfig } from '@aztec/stdlib/ha-signing';
|
|
6
7
|
import { getTelemetryClient } from '@aztec/telemetry-client';
|
|
7
8
|
|
|
8
9
|
import { Pool } from 'pg';
|
|
9
10
|
|
|
11
|
+
import { LmdbSlashingProtectionDatabase, migrateLmdbSlashingProtectionDatabase } from './db/lmdb.js';
|
|
10
12
|
import { PostgresSlashingProtectionDatabase } from './db/postgres.js';
|
|
11
13
|
import { HASignerMetrics } from './metrics.js';
|
|
12
|
-
import type { CreateHASignerDeps, SlashingProtectionDatabase } from './types.js';
|
|
14
|
+
import type { CreateHASignerDeps, CreateLocalSignerWithProtectionDeps, SlashingProtectionDatabase } from './types.js';
|
|
13
15
|
import { ValidatorHASigner } from './validator_ha_signer.js';
|
|
14
16
|
|
|
15
17
|
/**
|
|
@@ -27,7 +29,6 @@ import { ValidatorHASigner } from './validator_ha_signer.js';
|
|
|
27
29
|
* ```typescript
|
|
28
30
|
* const { signer, db } = await createHASigner({
|
|
29
31
|
* databaseUrl: process.env.DATABASE_URL,
|
|
30
|
-
* haSigningEnabled: true,
|
|
31
32
|
* nodeId: 'validator-node-1',
|
|
32
33
|
* pollingIntervalMs: 100,
|
|
33
34
|
* signingTimeoutMs: 3000,
|
|
@@ -56,7 +57,8 @@ export async function createHASigner(
|
|
|
56
57
|
const { databaseUrl, poolMaxCount, poolMinCount, poolIdleTimeoutMs, poolConnectionTimeoutMs, ...signerConfig } =
|
|
57
58
|
config;
|
|
58
59
|
|
|
59
|
-
|
|
60
|
+
const databaseUrlValue = databaseUrl?.getValue();
|
|
61
|
+
if (!databaseUrlValue) {
|
|
60
62
|
throw new Error('databaseUrl is required for createHASigner');
|
|
61
63
|
}
|
|
62
64
|
|
|
@@ -67,7 +69,7 @@ export async function createHASigner(
|
|
|
67
69
|
let pool: Pool;
|
|
68
70
|
if (!deps?.pool) {
|
|
69
71
|
pool = new Pool({
|
|
70
|
-
connectionString:
|
|
72
|
+
connectionString: databaseUrlValue,
|
|
71
73
|
max: poolMaxCount ?? 10,
|
|
72
74
|
min: poolMinCount ?? 0,
|
|
73
75
|
idleTimeoutMillis: poolIdleTimeoutMs ?? 10_000,
|
|
@@ -87,7 +89,99 @@ export async function createHASigner(
|
|
|
87
89
|
const metrics = new HASignerMetrics(telemetryClient, signerConfig.nodeId);
|
|
88
90
|
|
|
89
91
|
// Create signer
|
|
90
|
-
const signer = new ValidatorHASigner(db,
|
|
92
|
+
const signer = new ValidatorHASigner(db, signerConfig, { metrics, dateProvider });
|
|
91
93
|
|
|
92
94
|
return { signer, db };
|
|
93
95
|
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Create a local (single-node) signing protection signer backed by LMDB.
|
|
99
|
+
*
|
|
100
|
+
* This provides double-signing protection for nodes that are NOT running in a
|
|
101
|
+
* high-availability (multi-node) setup. It prevents a proposer from sending two
|
|
102
|
+
* proposals for the same slot if the node crashes and restarts mid-proposal.
|
|
103
|
+
*
|
|
104
|
+
* When `config.dataDirectory` is set, the protection database is persisted to disk
|
|
105
|
+
* and survives crashes/restarts. When unset, an ephemeral in-memory store is
|
|
106
|
+
* used which protects within a single run but not across restarts.
|
|
107
|
+
*
|
|
108
|
+
* @param config - Local signer config
|
|
109
|
+
* @param deps - Optional dependencies (telemetry, date provider).
|
|
110
|
+
* @returns An object containing the signer and database instances.
|
|
111
|
+
*/
|
|
112
|
+
export async function createLocalSignerWithProtection(
|
|
113
|
+
config: LocalSignerConfig,
|
|
114
|
+
deps?: CreateLocalSignerWithProtectionDeps,
|
|
115
|
+
): Promise<{
|
|
116
|
+
signer: ValidatorHASigner;
|
|
117
|
+
db: SlashingProtectionDatabase;
|
|
118
|
+
}> {
|
|
119
|
+
const telemetryClient = deps?.telemetryClient ?? getTelemetryClient();
|
|
120
|
+
const dateProvider = deps?.dateProvider ?? new DateProvider();
|
|
121
|
+
|
|
122
|
+
const kvStore = await createStore(
|
|
123
|
+
'signing-protection',
|
|
124
|
+
LmdbSlashingProtectionDatabase.SCHEMA_VERSION,
|
|
125
|
+
{
|
|
126
|
+
dataDirectory: config.dataDirectory,
|
|
127
|
+
dataStoreMapSizeKb: config.signingProtectionMapSizeKb ?? config.dataStoreMapSizeKb,
|
|
128
|
+
rollupAddress: config.rollupAddress,
|
|
129
|
+
},
|
|
130
|
+
undefined,
|
|
131
|
+
{
|
|
132
|
+
onUpgrade: (dataDirectory, currentVersion, latestVersion) =>
|
|
133
|
+
migrateLmdbSlashingProtectionDatabase(
|
|
134
|
+
dataDirectory,
|
|
135
|
+
currentVersion,
|
|
136
|
+
latestVersion,
|
|
137
|
+
config.signingProtectionMapSizeKb ?? config.dataStoreMapSizeKb,
|
|
138
|
+
),
|
|
139
|
+
schemaVersionMismatchPolicy: 'throw',
|
|
140
|
+
},
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const db = new LmdbSlashingProtectionDatabase(kvStore, dateProvider);
|
|
144
|
+
|
|
145
|
+
const signerConfig = {
|
|
146
|
+
...config,
|
|
147
|
+
nodeId: config.nodeId || 'local',
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const metrics = new HASignerMetrics(telemetryClient, signerConfig.nodeId, 'LocalSigningProtectionMetrics');
|
|
151
|
+
|
|
152
|
+
const signer = new ValidatorHASigner(db, signerConfig, { metrics, dateProvider });
|
|
153
|
+
|
|
154
|
+
return { signer, db };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Create an in-memory LMDB-backed SlashingProtectionDatabase that can be shared across
|
|
159
|
+
* multiple validator nodes in the same process. Used for testing HA setups.
|
|
160
|
+
*/
|
|
161
|
+
export async function createSharedSlashingProtectionDb(
|
|
162
|
+
dateProvider: DateProvider = new DateProvider(),
|
|
163
|
+
): Promise<SlashingProtectionDatabase> {
|
|
164
|
+
const kvStore = await createStore('shared-signing-protection', LmdbSlashingProtectionDatabase.SCHEMA_VERSION, {
|
|
165
|
+
dataStoreMapSizeKb: 1024 * 1024,
|
|
166
|
+
});
|
|
167
|
+
return new LmdbSlashingProtectionDatabase(kvStore, dateProvider);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Create a ValidatorHASigner backed by a pre-existing SlashingProtectionDatabase.
|
|
172
|
+
* Used for testing HA setups where multiple nodes share the same protection database.
|
|
173
|
+
*/
|
|
174
|
+
export function createSignerFromSharedDb(
|
|
175
|
+
db: SlashingProtectionDatabase,
|
|
176
|
+
config: Pick<
|
|
177
|
+
ValidatorHASignerConfig,
|
|
178
|
+
'nodeId' | 'pollingIntervalMs' | 'signingTimeoutMs' | 'maxStuckDutiesAgeMs' | 'rollupAddress'
|
|
179
|
+
>,
|
|
180
|
+
deps?: CreateLocalSignerWithProtectionDeps,
|
|
181
|
+
): { signer: ValidatorHASigner; db: SlashingProtectionDatabase } {
|
|
182
|
+
const telemetryClient = deps?.telemetryClient ?? getTelemetryClient();
|
|
183
|
+
const dateProvider = deps?.dateProvider ?? new DateProvider();
|
|
184
|
+
const metrics = new HASignerMetrics(telemetryClient, config.nodeId, 'SharedSigningProtectionMetrics');
|
|
185
|
+
const signer = new ValidatorHASigner(db, config, { metrics, dateProvider });
|
|
186
|
+
return { signer, db };
|
|
187
|
+
}
|
|
@@ -8,7 +8,7 @@ import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
|
8
8
|
import { RunningPromise } from '@aztec/foundation/promise';
|
|
9
9
|
import { sleep } from '@aztec/foundation/sleep';
|
|
10
10
|
import type { DateProvider } from '@aztec/foundation/timer';
|
|
11
|
-
import type {
|
|
11
|
+
import type { BaseSignerConfig } from '@aztec/stdlib/ha-signing';
|
|
12
12
|
|
|
13
13
|
import {
|
|
14
14
|
type CheckAndRecordParams,
|
|
@@ -55,7 +55,7 @@ export class SlashingProtectionService {
|
|
|
55
55
|
|
|
56
56
|
constructor(
|
|
57
57
|
private readonly db: SlashingProtectionDatabase,
|
|
58
|
-
private readonly config:
|
|
58
|
+
private readonly config: BaseSignerConfig,
|
|
59
59
|
deps: SlashingProtectionServiceDeps,
|
|
60
60
|
) {
|
|
61
61
|
this.log = createLogger('slashing-protection');
|
|
@@ -99,7 +99,7 @@ export class SlashingProtectionService {
|
|
|
99
99
|
|
|
100
100
|
if (isNew) {
|
|
101
101
|
// We successfully acquired the lock
|
|
102
|
-
this.log.
|
|
102
|
+
this.log.verbose(`Acquired lock for duty ${dutyType} at slot ${slot}`, {
|
|
103
103
|
validatorAddress: validatorAddress.toString(),
|
|
104
104
|
nodeId,
|
|
105
105
|
});
|
|
@@ -177,7 +177,7 @@ export class SlashingProtectionService {
|
|
|
177
177
|
);
|
|
178
178
|
|
|
179
179
|
if (success) {
|
|
180
|
-
this.log.
|
|
180
|
+
this.log.verbose(`Recorded successful signing for duty ${dutyType} at slot ${slot}`, {
|
|
181
181
|
validatorAddress: validatorAddress.toString(),
|
|
182
182
|
nodeId,
|
|
183
183
|
});
|
|
@@ -241,10 +241,10 @@ export class SlashingProtectionService {
|
|
|
241
241
|
*/
|
|
242
242
|
async start() {
|
|
243
243
|
// One-time cleanup at startup: remove duties from previous rollup versions
|
|
244
|
-
const numOutdatedRollupDuties = await this.db.cleanupOutdatedRollupDuties(this.config.
|
|
244
|
+
const numOutdatedRollupDuties = await this.db.cleanupOutdatedRollupDuties(this.config.rollupAddress);
|
|
245
245
|
if (numOutdatedRollupDuties > 0) {
|
|
246
246
|
this.log.info(`Cleaned up ${numOutdatedRollupDuties} duties with outdated rollup address at startup`, {
|
|
247
|
-
currentRollupAddress: this.config.
|
|
247
|
+
currentRollupAddress: this.config.rollupAddress.toString(),
|
|
248
248
|
});
|
|
249
249
|
this.metrics.recordCleanup('outdated_rollup', numOutdatedRollupDuties);
|
|
250
250
|
}
|
package/src/types.ts
CHANGED
|
@@ -2,11 +2,14 @@ import { SlotNumber } from '@aztec/foundation/branded-types';
|
|
|
2
2
|
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
3
3
|
import { DateProvider } from '@aztec/foundation/timer';
|
|
4
4
|
import {
|
|
5
|
+
type AttestationSigningContext,
|
|
6
|
+
type CheckpointProposalSigningContext,
|
|
5
7
|
DutyType,
|
|
6
8
|
type HAProtectedSigningContext,
|
|
7
9
|
type SigningContext,
|
|
8
10
|
type ValidatorHASignerConfig,
|
|
9
11
|
getBlockNumberFromSigningContext as getBlockNumberFromSigningContextFromStdlib,
|
|
12
|
+
getCheckpointNumberFromSigningContext as getCheckpointNumberFromSigningContextFromStdlib,
|
|
10
13
|
isHAProtectedContext,
|
|
11
14
|
} from '@aztec/stdlib/ha-signing';
|
|
12
15
|
import type { TelemetryClient } from '@aztec/telemetry-client';
|
|
@@ -25,8 +28,10 @@ import type {
|
|
|
25
28
|
} from './db/types.js';
|
|
26
29
|
|
|
27
30
|
export type {
|
|
31
|
+
AttestationSigningContext,
|
|
28
32
|
BlockProposalDutyIdentifier,
|
|
29
33
|
CheckAndRecordParams,
|
|
34
|
+
CheckpointProposalSigningContext,
|
|
30
35
|
DeleteDutyParams,
|
|
31
36
|
DutyIdentifier,
|
|
32
37
|
DutyRow,
|
|
@@ -40,6 +45,7 @@ export type {
|
|
|
40
45
|
export { DutyStatus, DutyType, getBlockIndexFromDutyIdentifier, normalizeBlockIndex } from './db/types.js';
|
|
41
46
|
export { isHAProtectedContext };
|
|
42
47
|
export { getBlockNumberFromSigningContextFromStdlib as getBlockNumberFromSigningContext };
|
|
48
|
+
export { getCheckpointNumberFromSigningContextFromStdlib as getCheckpointNumberFromSigningContext };
|
|
43
49
|
|
|
44
50
|
/**
|
|
45
51
|
* Result of tryInsertOrGetExisting operation
|
|
@@ -70,6 +76,11 @@ export interface CreateHASignerDeps {
|
|
|
70
76
|
dateProvider?: DateProvider;
|
|
71
77
|
}
|
|
72
78
|
|
|
79
|
+
/**
|
|
80
|
+
* deps for creating a local signing protection signer
|
|
81
|
+
*/
|
|
82
|
+
export type CreateLocalSignerWithProtectionDeps = Omit<CreateHASignerDeps, 'pool'>;
|
|
83
|
+
|
|
73
84
|
/**
|
|
74
85
|
* Database interface for slashing protection operations
|
|
75
86
|
* This abstraction allows for different database implementations (PostgreSQL, SQLite, etc.)
|
|
@@ -11,10 +11,11 @@ import type { Signature } from '@aztec/foundation/eth-signature';
|
|
|
11
11
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
12
12
|
import type { DateProvider } from '@aztec/foundation/timer';
|
|
13
13
|
import {
|
|
14
|
+
type BaseSignerConfig,
|
|
14
15
|
DutyType,
|
|
15
16
|
type HAProtectedSigningContext,
|
|
16
|
-
type ValidatorHASignerConfig,
|
|
17
17
|
getBlockNumberFromSigningContext,
|
|
18
|
+
getCheckpointNumberFromSigningContext,
|
|
18
19
|
} from '@aztec/stdlib/ha-signing';
|
|
19
20
|
|
|
20
21
|
import type { DutyIdentifier } from './db/types.js';
|
|
@@ -56,7 +57,7 @@ export class ValidatorHASigner {
|
|
|
56
57
|
|
|
57
58
|
constructor(
|
|
58
59
|
db: SlashingProtectionDatabase,
|
|
59
|
-
private readonly config:
|
|
60
|
+
private readonly config: BaseSignerConfig,
|
|
60
61
|
deps: ValidatorHASignerDeps,
|
|
61
62
|
) {
|
|
62
63
|
this.log = createLogger('validator-ha-signer');
|
|
@@ -64,15 +65,10 @@ export class ValidatorHASigner {
|
|
|
64
65
|
this.metrics = deps.metrics;
|
|
65
66
|
this.dateProvider = deps.dateProvider;
|
|
66
67
|
|
|
67
|
-
if (!config.haSigningEnabled) {
|
|
68
|
-
// this shouldn't happen, the validator should use different signer for non-HA setups
|
|
69
|
-
throw new Error('Validator HA Signer is not enabled in config');
|
|
70
|
-
}
|
|
71
|
-
|
|
72
68
|
if (!config.nodeId || config.nodeId === '') {
|
|
73
69
|
throw new Error('NODE_ID is required for high-availability setups');
|
|
74
70
|
}
|
|
75
|
-
this.rollupAddress = config.
|
|
71
|
+
this.rollupAddress = config.rollupAddress;
|
|
76
72
|
this.slashingProtection = new SlashingProtectionService(db, config, {
|
|
77
73
|
metrics: deps.metrics,
|
|
78
74
|
dateProvider: deps.dateProvider,
|
|
@@ -130,9 +126,11 @@ export class ValidatorHASigner {
|
|
|
130
126
|
// Acquire lock and get the token for ownership verification
|
|
131
127
|
// DutyAlreadySignedError and SlashingProtectionError may be thrown here and are recorded in the service
|
|
132
128
|
const blockNumber = getBlockNumberFromSigningContext(context);
|
|
129
|
+
const checkpointNumber = getCheckpointNumberFromSigningContext(context);
|
|
133
130
|
const lockToken = await this.slashingProtection.checkAndRecord({
|
|
134
131
|
...dutyIdentifier,
|
|
135
132
|
blockNumber,
|
|
133
|
+
checkpointNumber,
|
|
136
134
|
messageHash: messageHash.toString(),
|
|
137
135
|
nodeId: this.config.nodeId,
|
|
138
136
|
});
|