@lodestar/validator 1.51.0-dev.32db66b396 → 1.51.0-dev.38410b466e
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/lib/slashingProtection/attestation/index.d.ts +1 -0
- package/lib/slashingProtection/attestation/index.d.ts.map +1 -1
- package/lib/slashingProtection/attestation/index.js +77 -44
- package/lib/slashingProtection/attestation/index.js.map +1 -1
- package/lib/slashingProtection/index.d.ts +2 -0
- package/lib/slashingProtection/index.d.ts.map +1 -1
- package/lib/slashingProtection/index.js +48 -5
- package/lib/slashingProtection/index.js.map +1 -1
- package/package.json +12 -12
- package/src/slashingProtection/attestation/index.ts +81 -46
- package/src/slashingProtection/index.ts +50 -6
|
@@ -13,6 +13,7 @@ export declare class SlashingProtectionAttestationService {
|
|
|
13
13
|
private attestationByTarget;
|
|
14
14
|
private attestationLowerBound;
|
|
15
15
|
private minMaxSurround;
|
|
16
|
+
private readonly pendingAttestations;
|
|
16
17
|
constructor(signedAttestationDb: AttestationByTargetRepository, attestationLowerBound: AttestationLowerBoundRepository, minMaxSurround: MinMaxSurround);
|
|
17
18
|
/**
|
|
18
19
|
* Check an attestation for slash safety, and if it is safe, record it in the database
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/slashingProtection/attestation/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAE,KAAK,EAAC,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/slashingProtection/attestation/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAE,KAAK,EAAC,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAAC,cAAc,EAAyD,MAAM,4BAA4B,CAAC;AAClH,OAAO,EAAC,6BAA6B,EAAC,MAAM,aAAa,CAAC;AAE1D,OAAO,EAAC,6BAA6B,EAAC,MAAM,oCAAoC,CAAC;AACjF,OAAO,EAAC,+BAA+B,EAAC,MAAM,sCAAsC,CAAC;AACrF,OAAO,EAAC,uBAAuB,EAAE,2BAA2B,EAAC,MAAM,aAAa,CAAC;AACjF,OAAO,EACL,6BAA6B,EAC7B,+BAA+B,EAC/B,uBAAuB,EACvB,2BAA2B,GAC5B,CAAC;AAEF,aAAK,UAAU;IACb,SAAS,0BAA0B;IACnC,EAAE,mBAAmB;CACtB;AAED,qBAAa,oCAAoC;IAC/C,OAAO,CAAC,mBAAmB,CAAgC;IAC3D,OAAO,CAAC,qBAAqB,CAAkC;IAC/D,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAoC;IAExE,YACE,mBAAmB,EAAE,6BAA6B,EAClD,qBAAqB,EAAE,+BAA+B,EACtD,cAAc,EAAE,cAAc,EAK/B;IAED;;;OAGG;IACG,yBAAyB,CAAC,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,6BAA6B,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB5G;IAED;;OAEG;IACG,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,6BAA6B,GAAG,OAAO,CAAC,UAAU,CAAC,CA4FzG;IAED;;;;OAIG;IACG,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,6BAA6B,GAAG,OAAO,CAAC,IAAI,CAAC,CAGpG;IAED;;OAEG;IACG,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,6BAA6B,GAAG,IAAI,CAAC,CAE3G;IAED;;OAEG;IACG,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,6BAA6B,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqExG;IAED;;OAEG;IACG,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,6BAA6B,EAAE,CAAC,CAEpF;IAEK,WAAW,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,CAExC;CACF"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { defer, toPubkeyHex } from "@lodestar/utils";
|
|
1
2
|
import { SurroundAttestationError, SurroundAttestationErrorCode } from "../minMaxSurround/index.js";
|
|
2
3
|
import { ZERO_ROOT, isEqualNonZeroRoot, isEqualRoot, minEpoch } from "../utils.js";
|
|
3
4
|
import { AttestationByTargetRepository } from "./attestationByTargetRepository.js";
|
|
@@ -13,6 +14,7 @@ export class SlashingProtectionAttestationService {
|
|
|
13
14
|
attestationByTarget;
|
|
14
15
|
attestationLowerBound;
|
|
15
16
|
minMaxSurround;
|
|
17
|
+
pendingAttestations = new Map();
|
|
16
18
|
constructor(signedAttestationDb, attestationLowerBound, minMaxSurround) {
|
|
17
19
|
this.attestationByTarget = signedAttestationDb;
|
|
18
20
|
this.attestationLowerBound = attestationLowerBound;
|
|
@@ -23,9 +25,24 @@ export class SlashingProtectionAttestationService {
|
|
|
23
25
|
* This is the safe, externally-callable interface for checking attestations
|
|
24
26
|
*/
|
|
25
27
|
async checkAndInsertAttestation(pubKey, attestation) {
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
const pubkeyHex = toPubkeyHex(pubKey);
|
|
29
|
+
const previous = this.pendingAttestations.get(pubkeyHex);
|
|
30
|
+
const { promise, resolve } = defer();
|
|
31
|
+
this.pendingAttestations.set(pubkeyHex, promise);
|
|
32
|
+
try {
|
|
33
|
+
if (previous !== undefined) {
|
|
34
|
+
await previous;
|
|
35
|
+
}
|
|
36
|
+
const safeStatus = await this.checkAttestation(pubKey, attestation);
|
|
37
|
+
if (safeStatus !== SafeStatus.SAME_DATA) {
|
|
38
|
+
await this.insertAttestation(pubKey, attestation);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
resolve();
|
|
43
|
+
if (this.pendingAttestations.get(pubkeyHex) === promise) {
|
|
44
|
+
this.pendingAttestations.delete(pubkeyHex);
|
|
45
|
+
}
|
|
29
46
|
}
|
|
30
47
|
// TODO: Implement safe clean-up of stored attestations
|
|
31
48
|
}
|
|
@@ -139,52 +156,68 @@ export class SlashingProtectionAttestationService {
|
|
|
139
156
|
* Interchange import / export functionality
|
|
140
157
|
*/
|
|
141
158
|
async importAttestations(pubkey, attestations) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
if (maxSourceAtt === null || attestation.sourceEpoch > maxSourceAtt.sourceEpoch) {
|
|
151
|
-
maxSourceAtt = attestation;
|
|
159
|
+
const pubkeyHex = toPubkeyHex(pubkey);
|
|
160
|
+
const previous = this.pendingAttestations.get(pubkeyHex);
|
|
161
|
+
const { promise, resolve } = defer();
|
|
162
|
+
this.pendingAttestations.set(pubkeyHex, promise);
|
|
163
|
+
try {
|
|
164
|
+
if (previous !== undefined) {
|
|
165
|
+
await previous;
|
|
152
166
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
for (const attestation of attestations) {
|
|
165
|
-
const { sourceEpoch, targetEpoch, signingRoot } = attestation;
|
|
166
|
-
const prevAtt = attestationsByTarget.get(targetEpoch) ?? (await this.attestationByTarget.get(pubkey, targetEpoch));
|
|
167
|
-
if (prevAtt === null || (prevAtt.sourceEpoch === sourceEpoch && isEqualRoot(prevAtt.signingRoot, signingRoot))) {
|
|
168
|
-
attestationsByTarget.set(targetEpoch, attestation);
|
|
167
|
+
// Min-max surround misses a surround vote beyond its lookback, require the highest target attestation to
|
|
168
|
+
// have the highest source epoch as `checkAttestation` relies on it
|
|
169
|
+
let latestAtt = await this.attestationByTarget.getLatest(pubkey);
|
|
170
|
+
let maxSourceAtt = latestAtt;
|
|
171
|
+
for (const attestation of attestations) {
|
|
172
|
+
if (latestAtt === null || attestation.targetEpoch >= latestAtt.targetEpoch) {
|
|
173
|
+
latestAtt = attestation;
|
|
174
|
+
}
|
|
175
|
+
if (maxSourceAtt === null || attestation.sourceEpoch > maxSourceAtt.sourceEpoch) {
|
|
176
|
+
maxSourceAtt = attestation;
|
|
177
|
+
}
|
|
169
178
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
179
|
+
if (latestAtt && maxSourceAtt && latestAtt.sourceEpoch < maxSourceAtt.sourceEpoch) {
|
|
180
|
+
throw new InvalidAttestationError({
|
|
181
|
+
code: InvalidAttestationErrorCode.NEW_SURROUNDS_PREV,
|
|
182
|
+
attestation: latestAtt,
|
|
183
|
+
prev: maxSourceAtt,
|
|
175
184
|
});
|
|
176
185
|
}
|
|
186
|
+
// Never replace a recorded attestation with a different source or signing root, a zero root refuses any attestation
|
|
187
|
+
// with that target. The highest source epoch is kept as the latest attestation must have it, see above.
|
|
188
|
+
const attestationsByTarget = new Map();
|
|
189
|
+
for (const attestation of attestations) {
|
|
190
|
+
const { sourceEpoch, targetEpoch, signingRoot } = attestation;
|
|
191
|
+
const prevAtt = attestationsByTarget.get(targetEpoch) ?? (await this.attestationByTarget.get(pubkey, targetEpoch));
|
|
192
|
+
if (prevAtt === null ||
|
|
193
|
+
(prevAtt.sourceEpoch === sourceEpoch && isEqualRoot(prevAtt.signingRoot, signingRoot))) {
|
|
194
|
+
attestationsByTarget.set(targetEpoch, attestation);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
attestationsByTarget.set(targetEpoch, {
|
|
198
|
+
sourceEpoch: Math.max(prevAtt.sourceEpoch, sourceEpoch),
|
|
199
|
+
targetEpoch,
|
|
200
|
+
signingRoot: ZERO_ROOT,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
await this.attestationByTarget.set(pubkey, Array.from(attestationsByTarget.values()));
|
|
205
|
+
// Pre-compute spans for all attestations
|
|
206
|
+
for (const attestation of attestations) {
|
|
207
|
+
await this.minMaxSurround.insertAttestation(pubkey, attestation);
|
|
208
|
+
}
|
|
209
|
+
// Pre-compute and store lower-bound
|
|
210
|
+
const minSourceEpoch = minEpoch(attestations.map((attestation) => attestation.sourceEpoch));
|
|
211
|
+
const minTargetEpoch = minEpoch(attestations.map((attestation) => attestation.targetEpoch));
|
|
212
|
+
if (minSourceEpoch != null && minTargetEpoch != null) {
|
|
213
|
+
await this.attestationLowerBound.set(pubkey, { minSourceEpoch, minTargetEpoch });
|
|
214
|
+
}
|
|
177
215
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
// Pre-compute and store lower-bound
|
|
184
|
-
const minSourceEpoch = minEpoch(attestations.map((attestation) => attestation.sourceEpoch));
|
|
185
|
-
const minTargetEpoch = minEpoch(attestations.map((attestation) => attestation.targetEpoch));
|
|
186
|
-
if (minSourceEpoch != null && minTargetEpoch != null) {
|
|
187
|
-
await this.attestationLowerBound.set(pubkey, { minSourceEpoch, minTargetEpoch });
|
|
216
|
+
finally {
|
|
217
|
+
resolve();
|
|
218
|
+
if (this.pendingAttestations.get(pubkeyHex) === promise) {
|
|
219
|
+
this.pendingAttestations.delete(pubkeyHex);
|
|
220
|
+
}
|
|
188
221
|
}
|
|
189
222
|
}
|
|
190
223
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/slashingProtection/attestation/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,wBAAwB,EAAE,4BAA4B,EAAC,MAAM,4BAA4B,CAAC;AAElH,OAAO,EAAC,SAAS,EAAE,kBAAkB,EAAE,WAAW,EAAE,QAAQ,EAAC,MAAM,aAAa,CAAC;AACjF,OAAO,EAAC,6BAA6B,EAAC,MAAM,oCAAoC,CAAC;AACjF,OAAO,EAAC,+BAA+B,EAAC,MAAM,sCAAsC,CAAC;AACrF,OAAO,EAAC,uBAAuB,EAAE,2BAA2B,EAAC,MAAM,aAAa,CAAC;AACjF,OAAO,EACL,6BAA6B,EAC7B,+BAA+B,EAC/B,uBAAuB,EACvB,2BAA2B,GAC5B,CAAC;AAEF,IAAK,UAGJ;AAHD,WAAK,UAAU;IACb,iDAAmC,CAAA;IACnC,mCAAqB,CAAA;AACvB,CAAC,EAHI,UAAU,KAAV,UAAU,QAGd;AAED,MAAM,OAAO,oCAAoC;IACvC,mBAAmB,CAAgC;IACnD,qBAAqB,CAAkC;IACvD,cAAc,CAAiB;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/slashingProtection/attestation/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,6 +17,8 @@ export declare class SlashingProtection implements ISlashingProtection {
|
|
|
17
17
|
protected db: LodestarValidatorDatabaseController;
|
|
18
18
|
private blockService;
|
|
19
19
|
private attestationService;
|
|
20
|
+
private readonly importing;
|
|
21
|
+
private lastImport;
|
|
20
22
|
constructor(db: LodestarValidatorDatabaseController);
|
|
21
23
|
checkAndInsertBlockProposal(pubKey: BLSPubkey, block: SlashingProtectionBlock): Promise<void>;
|
|
22
24
|
checkAndInsertAttestation(pubKey: BLSPubkey, attestation: SlashingProtectionAttestation): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/slashingProtection/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,SAAS,EAAE,KAAK,EAAE,IAAI,EAAC,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/slashingProtection/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,SAAS,EAAE,KAAK,EAAE,IAAI,EAAC,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAC,MAAM,EAAqB,MAAM,iBAAiB,CAAC;AAE3D,OAAO,EAAC,mCAAmC,EAAY,MAAM,aAAa,CAAC;AAO3E,OAAO,EACL,WAAW,EAGX,wBAAwB,EAIzB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAC,mBAAmB,EAAC,MAAM,gBAAgB,CAAC;AAEnD,OAAO,EAAC,6BAA6B,EAAE,uBAAuB,EAAC,MAAM,YAAY,CAAC;AAElF,OAAO,EAAC,uBAAuB,EAAE,2BAA2B,EAAC,MAAM,wBAAwB,CAAC;AAC5F,OAAO,EAAC,iBAAiB,EAAE,qBAAqB,EAAC,MAAM,kBAAkB,CAAC;AAC1E,YAAY,EAAC,WAAW,EAAE,iBAAiB,EAAC,MAAM,wBAAwB,CAAC;AAC3E,OAAO,EAAC,gBAAgB,EAAE,yBAAyB,EAAC,MAAM,wBAAwB,CAAC;AACnF,YAAY,EAAC,mBAAmB,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,6BAA6B,EAAC,CAAC;AACpH;;;GAGG;AACH,qBAAa,kBAAmB,YAAW,mBAAmB;IAMhD,SAAS,CAAC,EAAE,EAAE,mCAAmC;IAL7D,OAAO,CAAC,YAAY,CAAiC;IACrD,OAAO,CAAC,kBAAkB,CAAuC;IACjE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAuC;IACjE,OAAO,CAAC,UAAU,CAA8B;IAEhD,YAAsB,EAAE,EAAE,mCAAmC,EAa5D;IAEK,2BAA2B,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAUlG;IAEK,yBAAyB,CAAC,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,6BAA6B,GAAG,OAAO,CAAC,IAAI,CAAC,CAU5G;IAEK,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAE1E;IAEK,iBAAiB,CACrB,WAAW,EAAE,WAAW,EACxB,qBAAqB,EAAE,IAAI,EAC3B,MAAM,CAAC,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,KAAK,GACnB,OAAO,CAAC,IAAI,CAAC,CAgDf;IAEK,iBAAiB,CACrB,qBAAqB,EAAE,IAAI,EAC3B,OAAO,EAAE,SAAS,EAAE,EACpB,aAAa,EAAE,wBAAwB,EACvC,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,CAAC,CAYtB;IAEK,WAAW,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,CAIxC;CACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GENESIS_EPOCH } from "@lodestar/params";
|
|
2
|
-
import { toPubkeyHex } from "@lodestar/utils";
|
|
2
|
+
import { defer, toPubkeyHex } from "@lodestar/utils";
|
|
3
3
|
import { uniqueVectorArr } from "../slashingProtection/utils.js";
|
|
4
4
|
import { AttestationByTargetRepository, AttestationLowerBoundRepository, SlashingProtectionAttestationService, } from "./attestation/index.js";
|
|
5
5
|
import { BlockBySlotRepository, SlashingProtectionBlockService } from "./block/index.js";
|
|
@@ -16,6 +16,8 @@ export class SlashingProtection {
|
|
|
16
16
|
db;
|
|
17
17
|
blockService;
|
|
18
18
|
attestationService;
|
|
19
|
+
importing = new Map();
|
|
20
|
+
lastImport = null;
|
|
19
21
|
constructor(db) {
|
|
20
22
|
this.db = db;
|
|
21
23
|
const blockBySlotRepository = new BlockBySlotRepository(db);
|
|
@@ -27,9 +29,25 @@ export class SlashingProtection {
|
|
|
27
29
|
this.attestationService = new SlashingProtectionAttestationService(attestationByTargetRepository, attestationLowerBoundRepository, minMaxSurround);
|
|
28
30
|
}
|
|
29
31
|
async checkAndInsertBlockProposal(pubKey, block) {
|
|
32
|
+
if (this.importing.size !== 0) {
|
|
33
|
+
const pubkeyHex = toPubkeyHex(pubKey);
|
|
34
|
+
let importing = this.importing.get(pubkeyHex);
|
|
35
|
+
while (importing !== undefined) {
|
|
36
|
+
await importing;
|
|
37
|
+
importing = this.importing.get(pubkeyHex);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
30
40
|
await this.blockService.checkAndInsertBlockProposal(pubKey, block);
|
|
31
41
|
}
|
|
32
42
|
async checkAndInsertAttestation(pubKey, attestation) {
|
|
43
|
+
if (this.importing.size !== 0) {
|
|
44
|
+
const pubkeyHex = toPubkeyHex(pubKey);
|
|
45
|
+
let importing = this.importing.get(pubkeyHex);
|
|
46
|
+
while (importing !== undefined) {
|
|
47
|
+
await importing;
|
|
48
|
+
importing = this.importing.get(pubkeyHex);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
33
51
|
await this.attestationService.checkAndInsertAttestation(pubKey, attestation);
|
|
34
52
|
}
|
|
35
53
|
async hasAttestedInEpoch(pubKey, epoch) {
|
|
@@ -52,10 +70,35 @@ export class SlashingProtection {
|
|
|
52
70
|
}
|
|
53
71
|
}
|
|
54
72
|
}
|
|
55
|
-
for
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
73
|
+
// Checks for keys in the file wait for the whole import, a key that is already signing must not sign against
|
|
74
|
+
// a history the import is still extending. Imports run one after another so a later one cannot release a key early.
|
|
75
|
+
const pubkeyHexes = data.map((validator) => toPubkeyHex(validator.pubkey));
|
|
76
|
+
const previous = this.lastImport;
|
|
77
|
+
const { promise, resolve } = defer();
|
|
78
|
+
this.lastImport = promise;
|
|
79
|
+
for (const pubkeyHex of pubkeyHexes) {
|
|
80
|
+
this.importing.set(pubkeyHex, promise);
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
if (previous !== null) {
|
|
84
|
+
await previous;
|
|
85
|
+
}
|
|
86
|
+
for (const validator of data) {
|
|
87
|
+
logger?.info("Importing slashing protection", { pubkey: toPubkeyHex(validator.pubkey) });
|
|
88
|
+
await this.blockService.importBlocks(validator.pubkey, validator.signedBlocks);
|
|
89
|
+
await this.attestationService.importAttestations(validator.pubkey, validator.signedAttestations);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
resolve();
|
|
94
|
+
if (this.lastImport === promise) {
|
|
95
|
+
this.lastImport = null;
|
|
96
|
+
}
|
|
97
|
+
for (const pubkeyHex of pubkeyHexes) {
|
|
98
|
+
if (this.importing.get(pubkeyHex) === promise) {
|
|
99
|
+
this.importing.delete(pubkeyHex);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
59
102
|
}
|
|
60
103
|
}
|
|
61
104
|
async exportInterchange(genesisValidatorsRoot, pubkeys, formatVersion, logger) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/slashingProtection/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAC,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAS,WAAW,EAAC,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/slashingProtection/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lodestar/validator",
|
|
3
|
-
"version": "1.51.0-dev.
|
|
3
|
+
"version": "1.51.0-dev.38410b466e",
|
|
4
4
|
"description": "A Typescript implementation of the validator client",
|
|
5
5
|
"author": "ChainSafe Systems",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -50,21 +50,21 @@
|
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@chainsafe/lodestar-z": "catalog:",
|
|
52
52
|
"@chainsafe/ssz": "^1.8.1",
|
|
53
|
-
"@lodestar/api": "^1.51.0-dev.
|
|
54
|
-
"@lodestar/config": "^1.51.0-dev.
|
|
55
|
-
"@lodestar/db": "^1.51.0-dev.
|
|
56
|
-
"@lodestar/params": "^1.51.0-dev.
|
|
57
|
-
"@lodestar/state-transition": "^1.51.0-dev.
|
|
58
|
-
"@lodestar/types": "^1.51.0-dev.
|
|
59
|
-
"@lodestar/utils": "^1.51.0-dev.
|
|
53
|
+
"@lodestar/api": "^1.51.0-dev.38410b466e",
|
|
54
|
+
"@lodestar/config": "^1.51.0-dev.38410b466e",
|
|
55
|
+
"@lodestar/db": "^1.51.0-dev.38410b466e",
|
|
56
|
+
"@lodestar/params": "^1.51.0-dev.38410b466e",
|
|
57
|
+
"@lodestar/state-transition": "^1.51.0-dev.38410b466e",
|
|
58
|
+
"@lodestar/types": "^1.51.0-dev.38410b466e",
|
|
59
|
+
"@lodestar/utils": "^1.51.0-dev.38410b466e",
|
|
60
60
|
"strict-event-emitter-types": "^2.0.0"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
|
-
"@lodestar/logger": "^1.51.0-dev.
|
|
64
|
-
"@lodestar/spec-test-util": "^1.51.0-dev.
|
|
65
|
-
"@lodestar/test-utils": "^1.51.0-dev.
|
|
63
|
+
"@lodestar/logger": "^1.51.0-dev.38410b466e",
|
|
64
|
+
"@lodestar/spec-test-util": "^1.51.0-dev.38410b466e",
|
|
65
|
+
"@lodestar/test-utils": "^1.51.0-dev.38410b466e",
|
|
66
66
|
"@vekexasia/bigint-buffer2": "^1.1.1",
|
|
67
67
|
"rimraf": "^4.4.1"
|
|
68
68
|
},
|
|
69
|
-
"gitHead": "
|
|
69
|
+
"gitHead": "27c04066e245afb8b5d71eb3127f64bcc32f764a"
|
|
70
70
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {BLSPubkey, Epoch} from "@lodestar/types";
|
|
2
|
+
import {defer, toPubkeyHex} from "@lodestar/utils";
|
|
2
3
|
import {MinMaxSurround, SurroundAttestationError, SurroundAttestationErrorCode} from "../minMaxSurround/index.js";
|
|
3
4
|
import {SlashingProtectionAttestation} from "../types.js";
|
|
4
5
|
import {ZERO_ROOT, isEqualNonZeroRoot, isEqualRoot, minEpoch} from "../utils.js";
|
|
@@ -21,6 +22,7 @@ export class SlashingProtectionAttestationService {
|
|
|
21
22
|
private attestationByTarget: AttestationByTargetRepository;
|
|
22
23
|
private attestationLowerBound: AttestationLowerBoundRepository;
|
|
23
24
|
private minMaxSurround: MinMaxSurround;
|
|
25
|
+
private readonly pendingAttestations = new Map<string, Promise<void>>();
|
|
24
26
|
|
|
25
27
|
constructor(
|
|
26
28
|
signedAttestationDb: AttestationByTargetRepository,
|
|
@@ -37,10 +39,25 @@ export class SlashingProtectionAttestationService {
|
|
|
37
39
|
* This is the safe, externally-callable interface for checking attestations
|
|
38
40
|
*/
|
|
39
41
|
async checkAndInsertAttestation(pubKey: BLSPubkey, attestation: SlashingProtectionAttestation): Promise<void> {
|
|
40
|
-
const
|
|
42
|
+
const pubkeyHex = toPubkeyHex(pubKey);
|
|
43
|
+
const previous = this.pendingAttestations.get(pubkeyHex);
|
|
44
|
+
const {promise, resolve} = defer<void>();
|
|
45
|
+
this.pendingAttestations.set(pubkeyHex, promise);
|
|
41
46
|
|
|
42
|
-
|
|
43
|
-
|
|
47
|
+
try {
|
|
48
|
+
if (previous !== undefined) {
|
|
49
|
+
await previous;
|
|
50
|
+
}
|
|
51
|
+
const safeStatus = await this.checkAttestation(pubKey, attestation);
|
|
52
|
+
|
|
53
|
+
if (safeStatus !== SafeStatus.SAME_DATA) {
|
|
54
|
+
await this.insertAttestation(pubKey, attestation);
|
|
55
|
+
}
|
|
56
|
+
} finally {
|
|
57
|
+
resolve();
|
|
58
|
+
if (this.pendingAttestations.get(pubkeyHex) === promise) {
|
|
59
|
+
this.pendingAttestations.delete(pubkeyHex);
|
|
60
|
+
}
|
|
44
61
|
}
|
|
45
62
|
|
|
46
63
|
// TODO: Implement safe clean-up of stored attestations
|
|
@@ -164,55 +181,73 @@ export class SlashingProtectionAttestationService {
|
|
|
164
181
|
* Interchange import / export functionality
|
|
165
182
|
*/
|
|
166
183
|
async importAttestations(pubkey: BLSPubkey, attestations: SlashingProtectionAttestation[]): Promise<void> {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
184
|
+
const pubkeyHex = toPubkeyHex(pubkey);
|
|
185
|
+
const previous = this.pendingAttestations.get(pubkeyHex);
|
|
186
|
+
const {promise, resolve} = defer<void>();
|
|
187
|
+
this.pendingAttestations.set(pubkeyHex, promise);
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
if (previous !== undefined) {
|
|
191
|
+
await previous;
|
|
174
192
|
}
|
|
175
|
-
|
|
176
|
-
|
|
193
|
+
// Min-max surround misses a surround vote beyond its lookback, require the highest target attestation to
|
|
194
|
+
// have the highest source epoch as `checkAttestation` relies on it
|
|
195
|
+
let latestAtt = await this.attestationByTarget.getLatest(pubkey);
|
|
196
|
+
let maxSourceAtt = latestAtt;
|
|
197
|
+
for (const attestation of attestations) {
|
|
198
|
+
if (latestAtt === null || attestation.targetEpoch >= latestAtt.targetEpoch) {
|
|
199
|
+
latestAtt = attestation;
|
|
200
|
+
}
|
|
201
|
+
if (maxSourceAtt === null || attestation.sourceEpoch > maxSourceAtt.sourceEpoch) {
|
|
202
|
+
maxSourceAtt = attestation;
|
|
203
|
+
}
|
|
177
204
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
prev: maxSourceAtt,
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Never replace a recorded attestation with a different source or signing root, a zero root refuses any attestation
|
|
188
|
-
// with that target. The highest source epoch is kept as the latest attestation must have it, see above.
|
|
189
|
-
const attestationsByTarget = new Map<Epoch, SlashingProtectionAttestation>();
|
|
190
|
-
for (const attestation of attestations) {
|
|
191
|
-
const {sourceEpoch, targetEpoch, signingRoot} = attestation;
|
|
192
|
-
const prevAtt =
|
|
193
|
-
attestationsByTarget.get(targetEpoch) ?? (await this.attestationByTarget.get(pubkey, targetEpoch));
|
|
194
|
-
if (prevAtt === null || (prevAtt.sourceEpoch === sourceEpoch && isEqualRoot(prevAtt.signingRoot, signingRoot))) {
|
|
195
|
-
attestationsByTarget.set(targetEpoch, attestation);
|
|
196
|
-
} else {
|
|
197
|
-
attestationsByTarget.set(targetEpoch, {
|
|
198
|
-
sourceEpoch: Math.max(prevAtt.sourceEpoch, sourceEpoch),
|
|
199
|
-
targetEpoch,
|
|
200
|
-
signingRoot: ZERO_ROOT,
|
|
205
|
+
if (latestAtt && maxSourceAtt && latestAtt.sourceEpoch < maxSourceAtt.sourceEpoch) {
|
|
206
|
+
throw new InvalidAttestationError({
|
|
207
|
+
code: InvalidAttestationErrorCode.NEW_SURROUNDS_PREV,
|
|
208
|
+
attestation: latestAtt,
|
|
209
|
+
prev: maxSourceAtt,
|
|
201
210
|
});
|
|
202
211
|
}
|
|
203
|
-
}
|
|
204
|
-
await this.attestationByTarget.set(pubkey, Array.from(attestationsByTarget.values()));
|
|
205
212
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
213
|
+
// Never replace a recorded attestation with a different source or signing root, a zero root refuses any attestation
|
|
214
|
+
// with that target. The highest source epoch is kept as the latest attestation must have it, see above.
|
|
215
|
+
const attestationsByTarget = new Map<Epoch, SlashingProtectionAttestation>();
|
|
216
|
+
for (const attestation of attestations) {
|
|
217
|
+
const {sourceEpoch, targetEpoch, signingRoot} = attestation;
|
|
218
|
+
const prevAtt =
|
|
219
|
+
attestationsByTarget.get(targetEpoch) ?? (await this.attestationByTarget.get(pubkey, targetEpoch));
|
|
220
|
+
if (
|
|
221
|
+
prevAtt === null ||
|
|
222
|
+
(prevAtt.sourceEpoch === sourceEpoch && isEqualRoot(prevAtt.signingRoot, signingRoot))
|
|
223
|
+
) {
|
|
224
|
+
attestationsByTarget.set(targetEpoch, attestation);
|
|
225
|
+
} else {
|
|
226
|
+
attestationsByTarget.set(targetEpoch, {
|
|
227
|
+
sourceEpoch: Math.max(prevAtt.sourceEpoch, sourceEpoch),
|
|
228
|
+
targetEpoch,
|
|
229
|
+
signingRoot: ZERO_ROOT,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
await this.attestationByTarget.set(pubkey, Array.from(attestationsByTarget.values()));
|
|
234
|
+
|
|
235
|
+
// Pre-compute spans for all attestations
|
|
236
|
+
for (const attestation of attestations) {
|
|
237
|
+
await this.minMaxSurround.insertAttestation(pubkey, attestation);
|
|
238
|
+
}
|
|
210
239
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
240
|
+
// Pre-compute and store lower-bound
|
|
241
|
+
const minSourceEpoch = minEpoch(attestations.map((attestation) => attestation.sourceEpoch));
|
|
242
|
+
const minTargetEpoch = minEpoch(attestations.map((attestation) => attestation.targetEpoch));
|
|
243
|
+
if (minSourceEpoch != null && minTargetEpoch != null) {
|
|
244
|
+
await this.attestationLowerBound.set(pubkey, {minSourceEpoch, minTargetEpoch});
|
|
245
|
+
}
|
|
246
|
+
} finally {
|
|
247
|
+
resolve();
|
|
248
|
+
if (this.pendingAttestations.get(pubkeyHex) === promise) {
|
|
249
|
+
this.pendingAttestations.delete(pubkeyHex);
|
|
250
|
+
}
|
|
216
251
|
}
|
|
217
252
|
}
|
|
218
253
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {GENESIS_EPOCH} from "@lodestar/params";
|
|
2
2
|
import {BLSPubkey, Epoch, Root} from "@lodestar/types";
|
|
3
|
-
import {Logger, toPubkeyHex} from "@lodestar/utils";
|
|
3
|
+
import {Logger, defer, toPubkeyHex} from "@lodestar/utils";
|
|
4
4
|
import {uniqueVectorArr} from "../slashingProtection/utils.js";
|
|
5
|
-
import {LodestarValidatorDatabaseController} from "../types.js";
|
|
5
|
+
import {LodestarValidatorDatabaseController, PubkeyHex} from "../types.js";
|
|
6
6
|
import {
|
|
7
7
|
AttestationByTargetRepository,
|
|
8
8
|
AttestationLowerBoundRepository,
|
|
@@ -34,6 +34,8 @@ export type {ISlashingProtection, InterchangeFormatVersion, SlashingProtectionBl
|
|
|
34
34
|
export class SlashingProtection implements ISlashingProtection {
|
|
35
35
|
private blockService: SlashingProtectionBlockService;
|
|
36
36
|
private attestationService: SlashingProtectionAttestationService;
|
|
37
|
+
private readonly importing = new Map<PubkeyHex, Promise<void>>();
|
|
38
|
+
private lastImport: Promise<void> | null = null;
|
|
37
39
|
|
|
38
40
|
constructor(protected db: LodestarValidatorDatabaseController) {
|
|
39
41
|
const blockBySlotRepository = new BlockBySlotRepository(db);
|
|
@@ -51,10 +53,26 @@ export class SlashingProtection implements ISlashingProtection {
|
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
async checkAndInsertBlockProposal(pubKey: BLSPubkey, block: SlashingProtectionBlock): Promise<void> {
|
|
56
|
+
if (this.importing.size !== 0) {
|
|
57
|
+
const pubkeyHex = toPubkeyHex(pubKey);
|
|
58
|
+
let importing = this.importing.get(pubkeyHex);
|
|
59
|
+
while (importing !== undefined) {
|
|
60
|
+
await importing;
|
|
61
|
+
importing = this.importing.get(pubkeyHex);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
54
64
|
await this.blockService.checkAndInsertBlockProposal(pubKey, block);
|
|
55
65
|
}
|
|
56
66
|
|
|
57
67
|
async checkAndInsertAttestation(pubKey: BLSPubkey, attestation: SlashingProtectionAttestation): Promise<void> {
|
|
68
|
+
if (this.importing.size !== 0) {
|
|
69
|
+
const pubkeyHex = toPubkeyHex(pubKey);
|
|
70
|
+
let importing = this.importing.get(pubkeyHex);
|
|
71
|
+
while (importing !== undefined) {
|
|
72
|
+
await importing;
|
|
73
|
+
importing = this.importing.get(pubkeyHex);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
58
76
|
await this.attestationService.checkAndInsertAttestation(pubKey, attestation);
|
|
59
77
|
}
|
|
60
78
|
|
|
@@ -84,10 +102,36 @@ export class SlashingProtection implements ISlashingProtection {
|
|
|
84
102
|
}
|
|
85
103
|
}
|
|
86
104
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
105
|
+
|
|
106
|
+
// Checks for keys in the file wait for the whole import, a key that is already signing must not sign against
|
|
107
|
+
// a history the import is still extending. Imports run one after another so a later one cannot release a key early.
|
|
108
|
+
const pubkeyHexes = data.map((validator) => toPubkeyHex(validator.pubkey));
|
|
109
|
+
const previous = this.lastImport;
|
|
110
|
+
const {promise, resolve} = defer<void>();
|
|
111
|
+
this.lastImport = promise;
|
|
112
|
+
for (const pubkeyHex of pubkeyHexes) {
|
|
113
|
+
this.importing.set(pubkeyHex, promise);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
if (previous !== null) {
|
|
118
|
+
await previous;
|
|
119
|
+
}
|
|
120
|
+
for (const validator of data) {
|
|
121
|
+
logger?.info("Importing slashing protection", {pubkey: toPubkeyHex(validator.pubkey)});
|
|
122
|
+
await this.blockService.importBlocks(validator.pubkey, validator.signedBlocks);
|
|
123
|
+
await this.attestationService.importAttestations(validator.pubkey, validator.signedAttestations);
|
|
124
|
+
}
|
|
125
|
+
} finally {
|
|
126
|
+
resolve();
|
|
127
|
+
if (this.lastImport === promise) {
|
|
128
|
+
this.lastImport = null;
|
|
129
|
+
}
|
|
130
|
+
for (const pubkeyHex of pubkeyHexes) {
|
|
131
|
+
if (this.importing.get(pubkeyHex) === promise) {
|
|
132
|
+
this.importing.delete(pubkeyHex);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
91
135
|
}
|
|
92
136
|
}
|
|
93
137
|
|