@lodestar/beacon-node 1.44.0-dev.86fc005b68 → 1.44.0-dev.8d5a6a4fc1
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/api/impl/beacon/blocks/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/blocks/index.js +13 -5
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/config/constants.d.ts +1 -0
- package/lib/api/impl/config/constants.d.ts.map +1 -1
- package/lib/api/impl/config/constants.js +2 -1
- package/lib/api/impl/config/constants.js.map +1 -1
- package/lib/api/impl/lodestar/index.d.ts.map +1 -1
- package/lib/api/impl/lodestar/index.js +28 -0
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts +0 -1
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +0 -4
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +8 -1
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/blockError.d.ts +0 -7
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +0 -3
- package/lib/chain/errors/blockError.js.map +1 -1
- package/lib/chain/forkChoice/index.d.ts +4 -4
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +10 -7
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/options.js +1 -0
- package/lib/chain/options.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +0 -1
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/queued.d.ts +0 -1
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +0 -4
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts +0 -5
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.js +0 -5
- package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +1 -4
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js +5 -2
- package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
- package/lib/chain/stateCache/types.d.ts +0 -2
- package/lib/chain/stateCache/types.d.ts.map +1 -1
- package/lib/chain/stateCache/types.js.map +1 -1
- package/lib/chain/validation/block.d.ts +5 -1
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +4 -14
- package/lib/chain/validation/block.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +0 -2
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +1 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +5 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +749 -2
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +9 -2
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/index.d.ts +2 -2
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +24 -22
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js +9 -5
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +8 -3
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts +2 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +16 -6
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/index.js +2 -2
- package/lib/network/reqresp/handlers/index.js.map +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts.map +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js +14 -3
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +24 -19
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/util/dataColumns.d.ts.map +1 -1
- package/lib/util/dataColumns.js +16 -11
- package/lib/util/dataColumns.js.map +1 -1
- package/package.json +15 -17
- package/src/api/impl/beacon/blocks/index.ts +13 -5
- package/src/api/impl/config/constants.ts +2 -0
- package/src/api/impl/lodestar/index.ts +30 -0
- package/src/chain/archiveStore/archiveStore.ts +0 -5
- package/src/chain/chain.ts +10 -1
- package/src/chain/errors/blockError.ts +0 -4
- package/src/chain/forkChoice/index.ts +13 -0
- package/src/chain/options.ts +1 -0
- package/src/chain/regen/interface.ts +0 -1
- package/src/chain/regen/queued.ts +0 -5
- package/src/chain/stateCache/fifoBlockStateCache.ts +0 -6
- package/src/chain/stateCache/persistentCheckpointsCache.ts +6 -2
- package/src/chain/stateCache/types.ts +0 -2
- package/src/chain/validation/block.ts +12 -16
- package/src/chain/validation/executionPayloadEnvelope.ts +0 -2
- package/src/metrics/metrics/lodestar.ts +6 -0
- package/src/network/processor/gossipHandlers.ts +9 -2
- package/src/network/processor/index.ts +26 -26
- package/src/network/reqresp/handlers/beaconBlocksByRange.ts +12 -5
- package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +11 -3
- package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +1 -1
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +22 -6
- package/src/network/reqresp/handlers/index.ts +2 -2
- package/src/network/reqresp/utils/dataColumnResponseValidation.ts +13 -3
- package/src/sync/unknownBlock.ts +27 -19
- package/src/util/dataColumns.ts +17 -12
|
@@ -186,9 +186,11 @@ export class NetworkProcessor {
|
|
|
186
186
|
// we may not receive the block for messages like Attestation and SignedAggregateAndProof messages, in that case PendingGossipsubMessage needs
|
|
187
187
|
// to be stored in this Map and reprocessed once the block comes
|
|
188
188
|
private readonly awaitingMessagesByBlockRoot: MapDef<RootHex, Set<PendingGossipsubMessage>>;
|
|
189
|
+
private awaitingBlockMessageCount = 0;
|
|
189
190
|
// we may not receive the payload for messages that require the FULL payload variant to be processed,
|
|
190
191
|
// in that case PendingGossipsubMessage needs to be stored in this Map and reprocessed once the payload comes
|
|
191
192
|
private readonly awaitingMessagesByPayloadBlockRoot: MapDef<RootHex, Set<PendingGossipsubMessage>>;
|
|
193
|
+
private awaitingPayloadMessageCount = 0;
|
|
192
194
|
private unknownBlocksBySlot = new MapDef<Slot, Set<RootHex>>(() => new Set());
|
|
193
195
|
private unknownEnvelopesBySlot = new MapDef<Slot, Set<RootHex>>(() => new Set());
|
|
194
196
|
|
|
@@ -228,8 +230,8 @@ export class NetworkProcessor {
|
|
|
228
230
|
metrics.gossipValidationQueue.keySize.set({topic}, this.gossipQueues[topic].keySize);
|
|
229
231
|
metrics.gossipValidationQueue.concurrency.set({topic}, this.gossipTopicConcurrency[topic]);
|
|
230
232
|
}
|
|
231
|
-
metrics.awaitingBlockGossipMessages.countPerSlot.set(this.
|
|
232
|
-
metrics.awaitingPayloadGossipMessages.countPerSlot.set(this.
|
|
233
|
+
metrics.awaitingBlockGossipMessages.countPerSlot.set(this.awaitingBlockMessageCount);
|
|
234
|
+
metrics.awaitingPayloadGossipMessages.countPerSlot.set(this.awaitingPayloadMessageCount);
|
|
233
235
|
// specific metric for beacon_attestation topic
|
|
234
236
|
metrics.gossipValidationQueue.keyAge.reset();
|
|
235
237
|
for (const ageMs of this.gossipQueues.beacon_attestation.getDataAgeMs()) {
|
|
@@ -497,7 +499,7 @@ export class NetworkProcessor {
|
|
|
497
499
|
this.pushPendingGossipsubMessageToQueue(message);
|
|
498
500
|
break;
|
|
499
501
|
case PreprocessAction.AwaitBlock: {
|
|
500
|
-
if (this.
|
|
502
|
+
if (this.awaitingBlockMessageCount > MAX_QUEUED_UNKNOWN_BLOCK_GOSSIP_OBJECTS) {
|
|
501
503
|
// No need to report the dropped job to gossip. It will be eventually pruned from the mcache
|
|
502
504
|
this.metrics?.awaitingBlockGossipMessages.reject.inc({
|
|
503
505
|
reason: ReprocessRejectReason.reached_limit,
|
|
@@ -509,10 +511,11 @@ export class NetworkProcessor {
|
|
|
509
511
|
this.metrics?.awaitingBlockGossipMessages.queue.inc({topic: topicType});
|
|
510
512
|
const awaitingGossipsubMessages = this.awaitingMessagesByBlockRoot.getOrDefault(preprocessResult.root);
|
|
511
513
|
awaitingGossipsubMessages.add(message);
|
|
514
|
+
this.awaitingBlockMessageCount++;
|
|
512
515
|
break;
|
|
513
516
|
}
|
|
514
517
|
case PreprocessAction.AwaitEnvelope: {
|
|
515
|
-
if (this.
|
|
518
|
+
if (this.awaitingPayloadMessageCount > MAX_QUEUED_UNKNOWN_PAYLOAD_GOSSIP_OBJECTS) {
|
|
516
519
|
this.metrics?.awaitingPayloadGossipMessages.reject.inc({
|
|
517
520
|
reason: ReprocessRejectReason.reached_limit,
|
|
518
521
|
topic: topicType,
|
|
@@ -525,6 +528,7 @@ export class NetworkProcessor {
|
|
|
525
528
|
preprocessResult.root
|
|
526
529
|
);
|
|
527
530
|
awaitingPayloadGossipsubMessages.add(message);
|
|
531
|
+
this.awaitingPayloadMessageCount++;
|
|
528
532
|
break;
|
|
529
533
|
}
|
|
530
534
|
}
|
|
@@ -548,6 +552,12 @@ export class NetworkProcessor {
|
|
|
548
552
|
return;
|
|
549
553
|
}
|
|
550
554
|
|
|
555
|
+
// Atomically remove from map and update counter before async iteration to
|
|
556
|
+
// prevent double-decrement race with onClockSlot during yield points below
|
|
557
|
+
if (this.awaitingMessagesByBlockRoot.delete(rootHex)) {
|
|
558
|
+
this.awaitingBlockMessageCount -= waitingGossipsubMessages.size;
|
|
559
|
+
}
|
|
560
|
+
|
|
551
561
|
const nowSec = Date.now() / 1000;
|
|
552
562
|
let count = 0;
|
|
553
563
|
// TODO: we can group attestations to process in batches but since we have the SeenAttestationDatas
|
|
@@ -567,8 +577,6 @@ export class NetworkProcessor {
|
|
|
567
577
|
await sleep(AWAITING_GOSSIP_OBJECTS_YIELD_EVERY_MS);
|
|
568
578
|
}
|
|
569
579
|
}
|
|
570
|
-
|
|
571
|
-
this.awaitingMessagesByBlockRoot.delete(rootHex);
|
|
572
580
|
};
|
|
573
581
|
|
|
574
582
|
private onPayloadEnvelopeProcessed = async ({blockRoot: rootHex}: {blockRoot: RootHex}): Promise<void> => {
|
|
@@ -577,6 +585,12 @@ export class NetworkProcessor {
|
|
|
577
585
|
return;
|
|
578
586
|
}
|
|
579
587
|
|
|
588
|
+
// Atomically remove from map and update counter before async iteration to
|
|
589
|
+
// prevent double-decrement race with onClockSlot during yield points below
|
|
590
|
+
if (this.awaitingMessagesByPayloadBlockRoot.delete(rootHex)) {
|
|
591
|
+
this.awaitingPayloadMessageCount -= waitingGossipsubMessages.size;
|
|
592
|
+
}
|
|
593
|
+
|
|
580
594
|
const nowSec = Date.now() / 1000;
|
|
581
595
|
let count = 0;
|
|
582
596
|
for (const message of waitingGossipsubMessages) {
|
|
@@ -593,8 +607,6 @@ export class NetworkProcessor {
|
|
|
593
607
|
await sleep(AWAITING_GOSSIP_OBJECTS_YIELD_EVERY_MS);
|
|
594
608
|
}
|
|
595
609
|
}
|
|
596
|
-
|
|
597
|
-
this.awaitingMessagesByPayloadBlockRoot.delete(rootHex);
|
|
598
610
|
};
|
|
599
611
|
|
|
600
612
|
private onClockSlot = (clockSlot: Slot): void => {
|
|
@@ -618,7 +630,9 @@ export class NetworkProcessor {
|
|
|
618
630
|
);
|
|
619
631
|
// No need to report the dropped job to gossip. It will be eventually pruned from the mcache
|
|
620
632
|
}
|
|
621
|
-
this.awaitingMessagesByBlockRoot.delete(rootHex)
|
|
633
|
+
if (this.awaitingMessagesByBlockRoot.delete(rootHex)) {
|
|
634
|
+
this.awaitingBlockMessageCount -= gossipMessages.size;
|
|
635
|
+
}
|
|
622
636
|
}
|
|
623
637
|
}
|
|
624
638
|
this.unknownBlocksBySlot.delete(slot);
|
|
@@ -641,7 +655,9 @@ export class NetworkProcessor {
|
|
|
641
655
|
);
|
|
642
656
|
// No need to report the dropped job to gossip. It will be eventually pruned from the mcache
|
|
643
657
|
}
|
|
644
|
-
this.awaitingMessagesByPayloadBlockRoot.delete(rootHex)
|
|
658
|
+
if (this.awaitingMessagesByPayloadBlockRoot.delete(rootHex)) {
|
|
659
|
+
this.awaitingPayloadMessageCount -= gossipMessages.size;
|
|
660
|
+
}
|
|
645
661
|
}
|
|
646
662
|
}
|
|
647
663
|
this.unknownEnvelopesBySlot.delete(slot);
|
|
@@ -784,20 +800,4 @@ export class NetworkProcessor {
|
|
|
784
800
|
|
|
785
801
|
return null;
|
|
786
802
|
}
|
|
787
|
-
|
|
788
|
-
private get unknownBlockGossipsubMessagesCount(): number {
|
|
789
|
-
let count = 0;
|
|
790
|
-
for (const messages of this.awaitingMessagesByBlockRoot.values()) {
|
|
791
|
-
count += messages.size;
|
|
792
|
-
}
|
|
793
|
-
return count;
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
private get unknownPayloadGossipsubMessagesCount(): number {
|
|
797
|
-
let count = 0;
|
|
798
|
-
for (const messages of this.awaitingMessagesByPayloadBlockRoot.values()) {
|
|
799
|
-
count += messages.size;
|
|
800
|
-
}
|
|
801
|
-
return count;
|
|
802
|
-
}
|
|
803
803
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {PeerId} from "@libp2p/interface";
|
|
2
2
|
import {BeaconConfig} from "@lodestar/config";
|
|
3
|
-
import {GENESIS_SLOT, isForkPostDeneb
|
|
3
|
+
import {GENESIS_SLOT, isForkPostDeneb} from "@lodestar/params";
|
|
4
4
|
import {RespStatus, ResponseError, ResponseOutgoing} from "@lodestar/reqresp";
|
|
5
5
|
import {computeEpochAtSlot} from "@lodestar/state-transition";
|
|
6
6
|
import {deneb, phase0} from "@lodestar/types";
|
|
@@ -29,13 +29,20 @@ export async function* onBeaconBlocksByRange(
|
|
|
29
29
|
// starts above it to avoid duplicate yields. See archiveBlocks.ts for the migration logic.
|
|
30
30
|
const archiveMaxSlot = finalizedSlot;
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
// endSlot is exclusive, so highest served slot is endSlot - 1.
|
|
33
|
+
// Throw only when the entire requested range is below earliestAvailableSlot.
|
|
34
|
+
if (endSlot - 1 < chain.earliestAvailableSlot) {
|
|
35
|
+
chain.logger.verbose("Peer requested range before earliestAvailableSlot for BeaconBlocksByRange", {
|
|
35
36
|
peer: prettyPrintPeerId(peerId),
|
|
36
37
|
client: peerClient,
|
|
38
|
+
startSlot,
|
|
39
|
+
count,
|
|
40
|
+
earliestAvailableSlot: chain.earliestAvailableSlot,
|
|
37
41
|
});
|
|
38
|
-
|
|
42
|
+
throw new ResponseError(
|
|
43
|
+
RespStatus.RESOURCE_UNAVAILABLE,
|
|
44
|
+
`Requested range is before earliestAvailableSlot startSlot=${startSlot} count=${count} earliestAvailableSlot=${chain.earliestAvailableSlot}`
|
|
45
|
+
);
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
// Finalized range of blocks
|
|
@@ -34,12 +34,20 @@ export async function* onDataColumnSidecarsByRange(
|
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
// endSlot is exclusive, so highest served slot is endSlot - 1.
|
|
38
|
+
// Throw only when the entire requested range is below earliestAvailableSlot.
|
|
39
|
+
if (endSlot - 1 < chain.earliestAvailableSlot) {
|
|
40
|
+
chain.logger.verbose("Peer requested range before earliestAvailableSlot for DataColumnSidecarsByRange", {
|
|
39
41
|
peer: prettyPrintPeerId(peerId),
|
|
40
42
|
client: peerClient,
|
|
43
|
+
startSlot,
|
|
44
|
+
count,
|
|
45
|
+
earliestAvailableSlot: chain.earliestAvailableSlot,
|
|
41
46
|
});
|
|
42
|
-
|
|
47
|
+
throw new ResponseError(
|
|
48
|
+
RespStatus.RESOURCE_UNAVAILABLE,
|
|
49
|
+
`Requested range is before earliestAvailableSlot startSlot=${startSlot} count=${count} earliestAvailableSlot=${chain.earliestAvailableSlot}`
|
|
50
|
+
);
|
|
43
51
|
}
|
|
44
52
|
|
|
45
53
|
const finalized = db.dataColumnSidecarArchive;
|
|
@@ -30,7 +30,7 @@ export async function* onDataColumnSidecarsByRoot(
|
|
|
30
30
|
const {blockRoot, columns: requestedColumns} = dataColumnsByRootIdentifier;
|
|
31
31
|
const availableColumns = validateRequestedDataColumns(chain, requestedColumns);
|
|
32
32
|
if (availableColumns.length === 0) {
|
|
33
|
-
|
|
33
|
+
continue;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
const blockRootHex = toRootHex(blockRoot);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {PeerId} from "@libp2p/interface";
|
|
1
2
|
import {ChainConfig} from "@lodestar/config";
|
|
2
3
|
import {PayloadStatus} from "@lodestar/fork-choice";
|
|
3
4
|
import {GENESIS_SLOT} from "@lodestar/params";
|
|
@@ -6,23 +7,38 @@ import {computeEpochAtSlot} from "@lodestar/state-transition";
|
|
|
6
7
|
import {gloas} from "@lodestar/types";
|
|
7
8
|
import {IBeaconChain} from "../../../chain/index.js";
|
|
8
9
|
import {IBeaconDb} from "../../../db/index.js";
|
|
10
|
+
import {prettyPrintPeerId} from "../../util.js";
|
|
9
11
|
|
|
10
12
|
export async function* onExecutionPayloadEnvelopesByRange(
|
|
11
13
|
request: gloas.ExecutionPayloadEnvelopesByRangeRequest,
|
|
12
14
|
chain: IBeaconChain,
|
|
13
|
-
db: IBeaconDb
|
|
15
|
+
db: IBeaconDb,
|
|
16
|
+
peerId: PeerId,
|
|
17
|
+
peerClient: string
|
|
14
18
|
): AsyncIterable<ResponseOutgoing> {
|
|
15
19
|
const {startSlot, count} = validateExecutionPayloadEnvelopesByRangeRequest(chain.config, request);
|
|
16
20
|
const endSlot = startSlot + count;
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
// endSlot is exclusive, so highest served slot is endSlot - 1.
|
|
23
|
+
// Throw only when the entire requested range is below earliestAvailableSlot.
|
|
24
|
+
if (endSlot - 1 < chain.earliestAvailableSlot) {
|
|
25
|
+
chain.logger.verbose("Peer requested range before earliestAvailableSlot for ExecutionPayloadEnvelopesByRange", {
|
|
26
|
+
peer: prettyPrintPeerId(peerId),
|
|
27
|
+
client: peerClient,
|
|
28
|
+
startSlot,
|
|
29
|
+
count,
|
|
30
|
+
earliestAvailableSlot: chain.earliestAvailableSlot,
|
|
31
|
+
});
|
|
32
|
+
throw new ResponseError(
|
|
33
|
+
RespStatus.RESOURCE_UNAVAILABLE,
|
|
34
|
+
`Requested range is before earliestAvailableSlot startSlot=${startSlot} count=${count} earliestAvailableSlot=${chain.earliestAvailableSlot}`
|
|
35
|
+
);
|
|
20
36
|
}
|
|
21
37
|
|
|
22
38
|
const finalized = db.executionPayloadEnvelopeArchive;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// in the next finalization run
|
|
39
|
+
// Use the finalized block's actual slot as the checkpoint epoch-boundary slot may be skipped
|
|
40
|
+
const finalizedSlot = chain.forkChoice.getFinalizedBlock().slot;
|
|
41
|
+
// The finalized block's envelope stays in the hot db until the next finalization run
|
|
26
42
|
const archiveMaxSlot = finalizedSlot - 1;
|
|
27
43
|
|
|
28
44
|
// Finalized range of envelopes
|
|
@@ -74,9 +74,9 @@ export function getReqRespHandlers({db, chain}: {db: IBeaconDb; chain: IBeaconCh
|
|
|
74
74
|
const body = ExecutionPayloadEnvelopesByRootRequestType(chain.config).deserialize(req.data);
|
|
75
75
|
return onExecutionPayloadEnvelopesByRoot(body, chain, db, peerId, peerClient);
|
|
76
76
|
},
|
|
77
|
-
[ReqRespMethod.ExecutionPayloadEnvelopesByRange]: (req) => {
|
|
77
|
+
[ReqRespMethod.ExecutionPayloadEnvelopesByRange]: (req, peerId, peerClient) => {
|
|
78
78
|
const body = ssz.gloas.ExecutionPayloadEnvelopesByRangeRequest.deserialize(req.data);
|
|
79
|
-
return onExecutionPayloadEnvelopesByRange(body, chain, db);
|
|
79
|
+
return onExecutionPayloadEnvelopesByRange(body, chain, db, peerId, peerClient);
|
|
80
80
|
},
|
|
81
81
|
|
|
82
82
|
[ReqRespMethod.LightClientBootstrap]: (req) => {
|
|
@@ -79,9 +79,19 @@ export function validateRequestedDataColumns(chain: IBeaconChain, requestedColum
|
|
|
79
79
|
throw new ResponseError(RespStatus.INVALID_REQUEST, "dataColumnSidecar requested without column indices");
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
const custodyColumns = chain.custodyConfig
|
|
83
|
-
const availableColumns =
|
|
84
|
-
const missingColumns =
|
|
82
|
+
const {custodyColumns, custodyColumnsIndex} = chain.custodyConfig;
|
|
83
|
+
const availableColumns: ColumnIndex[] = [];
|
|
84
|
+
const missingColumns: ColumnIndex[] = [];
|
|
85
|
+
for (const c of requestedColumns) {
|
|
86
|
+
// `c` is peer-controlled and SSZ-deserialized as `uint64`, so it may exceed
|
|
87
|
+
// `NUMBER_OF_COLUMNS - 1`; `Uint8Array` returns `undefined` for OOB reads,
|
|
88
|
+
// and `undefined !== 0` would silently classify OOB indices as custodied.
|
|
89
|
+
if ((custodyColumnsIndex[c] ?? 0) !== 0) {
|
|
90
|
+
availableColumns.push(c);
|
|
91
|
+
} else {
|
|
92
|
+
missingColumns.push(c);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
85
95
|
|
|
86
96
|
if (missingColumns.length > 0) {
|
|
87
97
|
chain.logger.verbose("Requested dataColumnSidecar for non-custody columns", {
|
package/src/sync/unknownBlock.ts
CHANGED
|
@@ -850,25 +850,28 @@ export class BlockInputSync {
|
|
|
850
850
|
}
|
|
851
851
|
|
|
852
852
|
const payloadInput = this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
|
|
853
|
-
if (!
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
853
|
+
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
854
|
+
// Block not in fork choice yet. payloadInput may be seeded from the block body during download, so a
|
|
855
|
+
// non-null payloadInput does not imply the block is imported; defer regardless and pull the block first.
|
|
856
|
+
// onBlockImported re-triggers the search to resume this envelope.
|
|
857
|
+
if (!this.pendingBlocks.has(rootHex)) {
|
|
858
|
+
this.addByRootHex(rootHex);
|
|
859
|
+
}
|
|
859
860
|
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
}
|
|
864
|
-
} else {
|
|
865
|
-
this.logger.debug("Missing PayloadEnvelopeInput for known block while reconciling payload envelope", {
|
|
866
|
-
root: rootHex,
|
|
867
|
-
});
|
|
861
|
+
const pendingBlock = this.pendingBlocks.get(rootHex);
|
|
862
|
+
if (pendingBlock && this.network.getConnectedPeers().length > 0) {
|
|
863
|
+
await this.downloadBlock(pendingBlock);
|
|
868
864
|
}
|
|
869
865
|
return;
|
|
870
866
|
}
|
|
871
867
|
|
|
868
|
+
if (!payloadInput) {
|
|
869
|
+
this.logger.debug("Missing PayloadEnvelopeInput for known block while reconciling payload envelope", {
|
|
870
|
+
root: rootHex,
|
|
871
|
+
});
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
|
|
872
875
|
if (!payloadInput.hasPayloadEnvelope()) {
|
|
873
876
|
const validationResult = await wrapError(
|
|
874
877
|
validateGossipExecutionPayloadEnvelope(this.chain, pendingPayload.envelope)
|
|
@@ -1073,11 +1076,11 @@ export class BlockInputSync {
|
|
|
1073
1076
|
}
|
|
1074
1077
|
|
|
1075
1078
|
payloadInput ??= this.chain.seenPayloadEnvelopeInputCache.get(rootHex);
|
|
1076
|
-
if (!
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
//
|
|
1079
|
+
if (!this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
1080
|
+
// Block not in fork choice yet. Validating now would throw BLOCK_ROOT_UNKNOWN, so keep the downloaded
|
|
1081
|
+
// envelope and wait for the block body; reconcilePayloadEnvelope validates once the block lands.
|
|
1082
|
+
// payloadInput may be seeded from the block body during download, so a non-null payloadInput does not
|
|
1083
|
+
// imply the block is imported.
|
|
1081
1084
|
return {
|
|
1082
1085
|
status: PendingPayloadInputStatus.waitingForBlock,
|
|
1083
1086
|
envelope,
|
|
@@ -1086,6 +1089,11 @@ export class BlockInputSync {
|
|
|
1086
1089
|
};
|
|
1087
1090
|
}
|
|
1088
1091
|
|
|
1092
|
+
if (!payloadInput) {
|
|
1093
|
+
// Block is in fork choice but no PayloadEnvelopeInput exists, should have been created during block import.
|
|
1094
|
+
throw new Error(`Missing PayloadEnvelopeInput for known block ${rootHex}`);
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1089
1097
|
if (!payloadInput.hasPayloadEnvelope()) {
|
|
1090
1098
|
await validateGossipExecutionPayloadEnvelope(this.chain, envelope);
|
|
1091
1099
|
}
|
package/src/util/dataColumns.ts
CHANGED
|
@@ -470,15 +470,17 @@ export async function recoverDataColumnSidecars(
|
|
|
470
470
|
return DataColumnReconstructionCode.SuccessLate;
|
|
471
471
|
}
|
|
472
472
|
|
|
473
|
-
//
|
|
474
|
-
//
|
|
475
|
-
//
|
|
476
|
-
//
|
|
477
|
-
//
|
|
478
|
-
//
|
|
479
|
-
//
|
|
480
|
-
//
|
|
481
|
-
const
|
|
473
|
+
// Per consensus-specs PR #4657, only publish reconstructed columns the node is
|
|
474
|
+
// subscribed to (custody + sampling). Eagerly cross-seeding non-subscribed
|
|
475
|
+
// columns floods the network with duplicates because the sender has no
|
|
476
|
+
// visibility into which peers already saw the message via the topic mesh.
|
|
477
|
+
// This matches the getBlobsV2 path in `getDataColumnSidecarsFromExecution` and
|
|
478
|
+
// aligns with Lighthouse/Prysm. Capture missing sampled indices before adding
|
|
479
|
+
// any reconstructed columns so they are not filtered out by the subsequent
|
|
480
|
+
// `addColumn` calls.
|
|
481
|
+
const missingSampledColumns = new Set(input.getMissingSampledColumnMeta().missing);
|
|
482
|
+
const sidecarsReconstructed: DataColumnSidecar[] = [];
|
|
483
|
+
const sidecarsToPublish: DataColumnSidecar[] = [];
|
|
482
484
|
for (const columnSidecar of fullSidecars) {
|
|
483
485
|
if (!input.hasColumn(columnSidecar.index)) {
|
|
484
486
|
if (input instanceof PayloadEnvelopeInput) {
|
|
@@ -501,11 +503,14 @@ export async function recoverDataColumnSidecars(
|
|
|
501
503
|
source: BlockInputSource.recovery,
|
|
502
504
|
});
|
|
503
505
|
}
|
|
504
|
-
|
|
506
|
+
sidecarsReconstructed.push(columnSidecar);
|
|
507
|
+
if (missingSampledColumns.has(columnSidecar.index)) {
|
|
508
|
+
sidecarsToPublish.push(columnSidecar);
|
|
509
|
+
}
|
|
505
510
|
}
|
|
506
511
|
}
|
|
507
|
-
metrics?.peerDas.reconstructedColumns.inc(
|
|
508
|
-
metrics?.dataColumns.bySource.inc({source: BlockInputSource.recovery},
|
|
512
|
+
metrics?.peerDas.reconstructedColumns.inc(sidecarsReconstructed.length);
|
|
513
|
+
metrics?.dataColumns.bySource.inc({source: BlockInputSource.recovery}, sidecarsReconstructed.length);
|
|
509
514
|
emitter.emit(ChainEvent.publishDataColumns, sidecarsToPublish);
|
|
510
515
|
// TODO: Can we record dataColumns.sentPeersPerSubnet metric somehow
|
|
511
516
|
return DataColumnReconstructionCode.SuccessResolved;
|