@lodestar/beacon-node 1.51.0-dev.8d5cdd33be → 1.51.0-dev.8fdbd61e7e
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/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +3 -4
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts +9 -2
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.js +12 -4
- package/lib/chain/archiveStore/utils/pruneHistory.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksSignatures.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksSignatures.js +3 -0
- package/lib/chain/blocks/verifyBlocksSignatures.js.map +1 -1
- package/lib/chain/bls/multithread/index.d.ts +1 -1
- package/lib/chain/bls/multithread/index.d.ts.map +1 -1
- package/lib/chain/bls/multithread/index.js +37 -11
- package/lib/chain/bls/multithread/index.js.map +1 -1
- package/lib/chain/validation/aggregateAndProof.js +1 -1
- package/lib/chain/validation/aggregateAndProof.js.map +1 -1
- package/lib/chain/validation/attestation.js +1 -1
- package/lib/chain/validation/attestation.js.map +1 -1
- package/lib/chain/validation/syncCommittee.d.ts.map +1 -1
- package/lib/chain/validation/syncCommittee.js +16 -10
- package/lib/chain/validation/syncCommittee.js.map +1 -1
- package/lib/chain/validation/syncCommitteeContributionAndProof.d.ts.map +1 -1
- package/lib/chain/validation/syncCommitteeContributionAndProof.js +11 -0
- package/lib/chain/validation/syncCommitteeContributionAndProof.js.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByHead.js +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByHead.js.map +1 -1
- package/lib/sync/sync.d.ts +8 -0
- package/lib/sync/sync.d.ts.map +1 -1
- package/lib/sync/sync.js +42 -22
- package/lib/sync/sync.js.map +1 -1
- package/package.json +16 -16
- package/src/chain/archiveStore/archiveStore.ts +5 -4
- package/src/chain/archiveStore/utils/pruneHistory.ts +16 -6
- package/src/chain/blocks/verifyBlocksSignatures.ts +3 -0
- package/src/chain/bls/multithread/index.ts +34 -14
- package/src/chain/validation/aggregateAndProof.ts +1 -1
- package/src/chain/validation/attestation.ts +1 -1
- package/src/chain/validation/syncCommittee.ts +31 -15
- package/src/chain/validation/syncCommitteeContributionAndProof.ts +13 -0
- package/src/network/reqresp/handlers/beaconBlocksByHead.ts +1 -1
- package/src/sync/sync.ts +43 -22
|
@@ -72,7 +72,7 @@ async function validateAggregateAndProof(
|
|
|
72
72
|
const attSlot = attData.slot;
|
|
73
73
|
|
|
74
74
|
let committeeIndex: number | null;
|
|
75
|
-
if (ForkSeq[fork] >= ForkSeq.gloas) {
|
|
75
|
+
if (ForkSeq[fork] >= ForkSeq.gloas && chain.config.getForkSeq(attSlot) >= ForkSeq.gloas) {
|
|
76
76
|
// [REJECT] `aggregate.data.index < 2`.
|
|
77
77
|
if (attData.index >= 2) {
|
|
78
78
|
throw new AttestationError(GossipAction.REJECT, {
|
|
@@ -291,7 +291,7 @@ async function validateAttestationNoSignatureCheck(
|
|
|
291
291
|
// api or first time validation of a gossip attestation
|
|
292
292
|
committeeIndex = attestationOrCache.attestation.committeeIndex;
|
|
293
293
|
|
|
294
|
-
if (isForkPostGloas(fork)) {
|
|
294
|
+
if (isForkPostGloas(fork) && isForkPostGloas(chain.config.getForkName(attSlot))) {
|
|
295
295
|
// [REJECT] `attestation.data.index < 2`.
|
|
296
296
|
if (attData.index >= 2) {
|
|
297
297
|
throw new AttestationError(GossipAction.REJECT, {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {SYNC_COMMITTEE_SUBNET_COUNT, SYNC_COMMITTEE_SUBNET_SIZE} from "@lodestar/params";
|
|
2
2
|
import {IBeaconStateView, isStatePostAltair} from "@lodestar/state-transition";
|
|
3
|
-
import {SubnetID, altair} from "@lodestar/types";
|
|
3
|
+
import {RootHex, Slot, SubnetID, ValidatorIndex, altair} from "@lodestar/types";
|
|
4
4
|
import {toRootHex} from "@lodestar/utils";
|
|
5
|
+
import {Metrics} from "../../metrics/metrics.js";
|
|
5
6
|
import {GossipAction, SyncCommitteeError, SyncCommitteeErrorCode} from "../errors/index.js";
|
|
6
7
|
import {IBeaconChain} from "../interface.js";
|
|
7
8
|
import {getSyncCommitteeSignatureSet} from "./signatureSets/index.js";
|
|
@@ -32,6 +33,33 @@ export async function validateGossipSyncCommittee(
|
|
|
32
33
|
|
|
33
34
|
// [IGNORE] There has been no other valid sync committee signature for the declared slot for the validator referenced
|
|
34
35
|
// by sync_committee_signature.validator_index.
|
|
36
|
+
assertSyncCommitteeMessageNotKnown(chain, slot, subnet, validatorIndex, messageRoot, chain.metrics);
|
|
37
|
+
|
|
38
|
+
// [REJECT] The subnet_id is valid for the given validator, i.e. subnet_id in compute_subnets_for_sync_committee(state, sync_committee_signature.validator_index).
|
|
39
|
+
// Note this validation implies the validator is part of the broader current sync committee along with the correct subcommittee.
|
|
40
|
+
// > Checked in validateGossipSyncCommitteeExceptSig()
|
|
41
|
+
|
|
42
|
+
// [REJECT] The signature is valid for the message beacon_block_root for the validator referenced by validator_index.
|
|
43
|
+
await validateSyncCommitteeSigOnly(chain, headState, syncCommittee);
|
|
44
|
+
|
|
45
|
+
// Re-check after the await: concurrent messages from the same validator all pass the check above.
|
|
46
|
+
// Metrics were already counted by the first check.
|
|
47
|
+
assertSyncCommitteeMessageNotKnown(chain, slot, subnet, validatorIndex, messageRoot, null);
|
|
48
|
+
|
|
49
|
+
// Register this valid item as seen
|
|
50
|
+
chain.seenSyncCommitteeMessages.add(slot, subnet, validatorIndex, messageRoot);
|
|
51
|
+
|
|
52
|
+
return {indicesInSubcommittee};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function assertSyncCommitteeMessageNotKnown(
|
|
56
|
+
chain: IBeaconChain,
|
|
57
|
+
slot: Slot,
|
|
58
|
+
subnet: SubnetID,
|
|
59
|
+
validatorIndex: ValidatorIndex,
|
|
60
|
+
messageRoot: RootHex,
|
|
61
|
+
metrics: Metrics | null
|
|
62
|
+
): void {
|
|
35
63
|
const prevRoot = chain.seenSyncCommitteeMessages.get(slot, subnet, validatorIndex);
|
|
36
64
|
if (prevRoot) {
|
|
37
65
|
let shouldIgnore = false;
|
|
@@ -39,9 +67,9 @@ export async function validateGossipSyncCommittee(
|
|
|
39
67
|
shouldIgnore = true;
|
|
40
68
|
} else {
|
|
41
69
|
const headRoot = chain.forkChoice.getHeadRoot();
|
|
42
|
-
|
|
70
|
+
metrics?.gossipSyncCommittee.equivocationCount.inc();
|
|
43
71
|
if (messageRoot === headRoot) {
|
|
44
|
-
|
|
72
|
+
metrics?.gossipSyncCommittee.equivocationToHeadCount.inc();
|
|
45
73
|
} else {
|
|
46
74
|
shouldIgnore = true;
|
|
47
75
|
}
|
|
@@ -57,18 +85,6 @@ export async function validateGossipSyncCommittee(
|
|
|
57
85
|
});
|
|
58
86
|
}
|
|
59
87
|
}
|
|
60
|
-
|
|
61
|
-
// [REJECT] The subnet_id is valid for the given validator, i.e. subnet_id in compute_subnets_for_sync_committee(state, sync_committee_signature.validator_index).
|
|
62
|
-
// Note this validation implies the validator is part of the broader current sync committee along with the correct subcommittee.
|
|
63
|
-
// > Checked in validateGossipSyncCommitteeExceptSig()
|
|
64
|
-
|
|
65
|
-
// [REJECT] The signature is valid for the message beacon_block_root for the validator referenced by validator_index.
|
|
66
|
-
await validateSyncCommitteeSigOnly(chain, headState, syncCommittee);
|
|
67
|
-
|
|
68
|
-
// Register this valid item as seen
|
|
69
|
-
chain.seenSyncCommitteeMessages.add(slot, subnet, validatorIndex, messageRoot);
|
|
70
|
-
|
|
71
|
-
return {indicesInSubcommittee};
|
|
72
88
|
}
|
|
73
89
|
|
|
74
90
|
export async function validateApiSyncCommittee(
|
|
@@ -92,6 +92,19 @@ export async function validateSyncCommitteeGossipContributionAndProof(
|
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
// Re-check after the await: concurrent contributions from the same aggregator all pass the checks above
|
|
96
|
+
if (chain.seenContributionAndProof.isAggregatorKnown(slot, subcommitteeIndex, aggregatorIndex)) {
|
|
97
|
+
throw new SyncCommitteeError(GossipAction.IGNORE, {
|
|
98
|
+
code: SyncCommitteeErrorCode.SYNC_COMMITTEE_AGGREGATOR_ALREADY_KNOWN,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!skipValidationKnownParticipants && chain.seenContributionAndProof.participantsKnown(contribution)) {
|
|
103
|
+
throw new SyncCommitteeError(GossipAction.IGNORE, {
|
|
104
|
+
code: SyncCommitteeErrorCode.SYNC_COMMITTEE_PARTICIPANTS_ALREADY_KNOWN,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
95
108
|
// no need to add to seenSyncCommittteeContributionCache here, gossip handler will do that
|
|
96
109
|
chain.seenContributionAndProof.add(contributionAndProof, syncCommitteeParticipantIndices.length);
|
|
97
110
|
|
|
@@ -23,7 +23,7 @@ export async function* onBeaconBlocksByHead(
|
|
|
23
23
|
let blockRootHex = requestedRootHex;
|
|
24
24
|
const minimumRequestEpoch = Math.max(
|
|
25
25
|
GENESIS_EPOCH,
|
|
26
|
-
chain.clock.currentEpoch - chain.config.
|
|
26
|
+
chain.clock.currentEpoch - chain.config.getMinEpochsForBlockRequests(chain.clock.currentEpoch)
|
|
27
27
|
);
|
|
28
28
|
const minimumRequestSlot = computeStartSlotAtEpoch(minimumRequestEpoch);
|
|
29
29
|
|
package/src/sync/sync.ts
CHANGED
|
@@ -33,6 +33,8 @@ export class BeaconSync implements IBeaconSync {
|
|
|
33
33
|
/** For metrics only */
|
|
34
34
|
private readonly peerSyncType = new Map<string, PeerSyncType>();
|
|
35
35
|
private readonly slotImportTolerance: Slot;
|
|
36
|
+
/** The requested subscription state, set while a change is being applied */
|
|
37
|
+
private pendingGossipSubscribed: boolean | undefined;
|
|
36
38
|
|
|
37
39
|
constructor(opts: SyncOptions, modules: SyncModules) {
|
|
38
40
|
const {config, chain, db, metrics, network, logger} = modules;
|
|
@@ -89,6 +91,7 @@ export class BeaconSync implements IBeaconSync {
|
|
|
89
91
|
}
|
|
90
92
|
|
|
91
93
|
close(): void {
|
|
94
|
+
this.pendingGossipSubscribed = undefined;
|
|
92
95
|
this.network.events.off(NetworkEvent.peerConnected, this.addPeer);
|
|
93
96
|
this.network.events.off(NetworkEvent.peerDisconnected, this.removePeer);
|
|
94
97
|
this.chain.clock.off(ClockEvent.epoch, this.onClockEpoch);
|
|
@@ -296,17 +299,7 @@ export class BeaconSync implements IBeaconSync {
|
|
|
296
299
|
if (state === SyncState.Synced && this.chain.clock.currentEpoch >= MIN_EPOCH_TO_START_GOSSIP) {
|
|
297
300
|
this.chain.forkChoice.resumeFastConfirmation();
|
|
298
301
|
|
|
299
|
-
|
|
300
|
-
this.network
|
|
301
|
-
.subscribeGossipCoreTopics()
|
|
302
|
-
.then(() => {
|
|
303
|
-
this.metrics?.syncSwitchGossipSubscriptions.inc({action: "subscribed"});
|
|
304
|
-
this.logger.info("Subscribed gossip core topics");
|
|
305
|
-
})
|
|
306
|
-
.catch((e) => {
|
|
307
|
-
this.logger.error("Error subscribing to gossip core topics", {}, e);
|
|
308
|
-
});
|
|
309
|
-
}
|
|
302
|
+
this.setGossipSubscription(true);
|
|
310
303
|
|
|
311
304
|
// also start searching for unknown blocks
|
|
312
305
|
if (!this.unknownBlockSync.isSubscribedToNetwork()) {
|
|
@@ -322,17 +315,8 @@ export class BeaconSync implements IBeaconSync {
|
|
|
322
315
|
// Same debounce as gossip: transient blips keep the rule running, only a real gap pauses it
|
|
323
316
|
this.chain.forkChoice.pauseFastConfirmation();
|
|
324
317
|
|
|
325
|
-
if (this.
|
|
326
|
-
this.logger.warn(
|
|
327
|
-
this.network
|
|
328
|
-
.unsubscribeGossipCoreTopics()
|
|
329
|
-
.then(() => {
|
|
330
|
-
this.metrics?.syncSwitchGossipSubscriptions.inc({action: "unsubscribed"});
|
|
331
|
-
this.logger.info("Un-subscribed gossip core topics");
|
|
332
|
-
})
|
|
333
|
-
.catch((e) => {
|
|
334
|
-
this.logger.error("Error unsubscribing to gossip core topics", {}, e);
|
|
335
|
-
});
|
|
318
|
+
if (this.setGossipSubscription(false)) {
|
|
319
|
+
this.logger.warn("Node sync has fallen behind", {slots: syncDiff});
|
|
336
320
|
}
|
|
337
321
|
|
|
338
322
|
// also stop searching for unknown blocks
|
|
@@ -344,6 +328,43 @@ export class BeaconSync implements IBeaconSync {
|
|
|
344
328
|
}
|
|
345
329
|
};
|
|
346
330
|
|
|
331
|
+
private setGossipSubscription(subscribe: boolean): boolean {
|
|
332
|
+
if ((this.pendingGossipSubscribed ?? this.network.isSubscribedToGossipCoreTopics()) === subscribe) return false;
|
|
333
|
+
const applying = this.pendingGossipSubscribed !== undefined;
|
|
334
|
+
this.pendingGossipSubscribed = subscribe;
|
|
335
|
+
if (!applying) void this.applyGossipSubscription();
|
|
336
|
+
return true;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Applies the latest requested state one request at a time. Network skips a subscribe issued while an unsubscribe is
|
|
341
|
+
* in flight, so opposite requests must not overlap.
|
|
342
|
+
*/
|
|
343
|
+
private async applyGossipSubscription(): Promise<void> {
|
|
344
|
+
for (;;) {
|
|
345
|
+
const subscribe = this.pendingGossipSubscribed;
|
|
346
|
+
if (subscribe === undefined || subscribe === this.network.isSubscribedToGossipCoreTopics()) break;
|
|
347
|
+
try {
|
|
348
|
+
await (subscribe ? this.network.subscribeGossipCoreTopics() : this.network.unsubscribeGossipCoreTopics());
|
|
349
|
+
} catch (e) {
|
|
350
|
+
this.logger.error(
|
|
351
|
+
subscribe ? "Error subscribing to gossip core topics" : "Error unsubscribing to gossip core topics",
|
|
352
|
+
{},
|
|
353
|
+
e as Error
|
|
354
|
+
);
|
|
355
|
+
// Leave a retry of the same request to the next sync state update
|
|
356
|
+
if (this.pendingGossipSubscribed === subscribe) break;
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
if (this.pendingGossipSubscribed === undefined || this.network.isSubscribedToGossipCoreTopics() !== subscribe) {
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
this.metrics?.syncSwitchGossipSubscriptions.inc({action: subscribe ? "subscribed" : "unsubscribed"});
|
|
363
|
+
this.logger.info(subscribe ? "Subscribed gossip core topics" : "Un-subscribed gossip core topics");
|
|
364
|
+
}
|
|
365
|
+
this.pendingGossipSubscribed = undefined;
|
|
366
|
+
}
|
|
367
|
+
|
|
347
368
|
private onClockEpoch = (): void => {
|
|
348
369
|
// If a node witness the genesis event consider starting gossip
|
|
349
370
|
// Also, ensure that updateSyncState is run at least once per epoch.
|