@lodestar/beacon-node 1.47.0-dev.beabd3a65c → 1.47.0-dev.c1d9395d5e
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/metrics/metrics/lodestar.d.ts +7 -2
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +7 -1
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/sync/types.d.ts +32 -0
- package/lib/sync/types.d.ts.map +1 -1
- package/lib/sync/types.js +33 -0
- package/lib/sync/types.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +88 -38
- package/lib/sync/unknownBlock.js.map +1 -1
- package/package.json +14 -14
- package/src/metrics/metrics/lodestar.ts +9 -3
- package/src/sync/types.ts +33 -0
- package/src/sync/unknownBlock.ts +97 -40
|
@@ -24,7 +24,7 @@ import {ExecutionPayloadStatus} from "../../execution/index.js";
|
|
|
24
24
|
import {GossipType} from "../../network/index.js";
|
|
25
25
|
import {CannotAcceptWorkReason, ReprocessRejectReason} from "../../network/processor/index.js";
|
|
26
26
|
import {BackfillSyncMethod} from "../../sync/backfill/backfill.js";
|
|
27
|
-
import {PendingBlockType} from "../../sync/types.js";
|
|
27
|
+
import {DroppedItemReason, PendingBlockType} from "../../sync/types.js";
|
|
28
28
|
import {PeerSyncType, RangeSyncType} from "../../sync/utils/remoteSyncType.js";
|
|
29
29
|
import {AllocSource} from "../../util/bufferPool.js";
|
|
30
30
|
import {DataColumnReconstructionCode} from "../../util/dataColumns.js";
|
|
@@ -680,9 +680,15 @@ export function createLodestarMetrics(
|
|
|
680
680
|
name: "lodestar_sync_unknown_block_downloaded_blocks_error_total",
|
|
681
681
|
help: "Total number of downloaded blocks errors in UnknownBlockSync",
|
|
682
682
|
}),
|
|
683
|
-
removedBlocks: register.gauge({
|
|
683
|
+
removedBlocks: register.gauge<{reason: DroppedItemReason}>({
|
|
684
684
|
name: "lodestar_sync_unknown_block_removed_blocks_total",
|
|
685
|
-
help: "Total
|
|
685
|
+
help: "Total pending blocks dropped from BlockInputSync without completing, by reason",
|
|
686
|
+
labelNames: ["reason"],
|
|
687
|
+
}),
|
|
688
|
+
removedPayloads: register.gauge<{reason: DroppedItemReason}>({
|
|
689
|
+
name: "lodestar_sync_unknown_block_removed_payloads_total",
|
|
690
|
+
help: "Total pending payloads dropped from BlockInputSync without completing, by reason",
|
|
691
|
+
labelNames: ["reason"],
|
|
686
692
|
}),
|
|
687
693
|
elapsedTimeTillReceived: register.histogram({
|
|
688
694
|
name: "lodestar_sync_unknown_block_elapsed_time_till_received",
|
package/src/sync/types.ts
CHANGED
|
@@ -36,6 +36,39 @@ export enum PendingBlockInputStatus {
|
|
|
36
36
|
processing = "processing",
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Why a pending item was dropped from BlockInputSync WITHOUT completing.
|
|
41
|
+
* The happy path — a successfully imported/processed block or payload — is removed by the import
|
|
42
|
+
* flow (onPayloadImported, processReadyBlock/processPayload success) and is deliberately NOT tracked
|
|
43
|
+
* here
|
|
44
|
+
*/
|
|
45
|
+
export enum DroppedItemReason {
|
|
46
|
+
/** prune: item's own slot is below the finalized slot (routine cleanup), or a downloaded block landed at/below finality */
|
|
47
|
+
belowFinalized = "below_finalized",
|
|
48
|
+
/** prune: slot-less, unresolvable payload evicted after PRUNE_UNRESOLVED_SLOT_EPOCHS */
|
|
49
|
+
agedOut = "aged_out",
|
|
50
|
+
/** pruneSetToMax evicted the oldest entry because the cache hit its capacity (DoS guard) */
|
|
51
|
+
capacity = "capacity",
|
|
52
|
+
/** descendant removed because an ancestor was invalid/removed */
|
|
53
|
+
invalidParent = "invalid_parent",
|
|
54
|
+
/** transient execution-engine error removal (EXECUTION_ENGINE_ERROR) */
|
|
55
|
+
elError = "el_error",
|
|
56
|
+
/** the execution engine declared the item invalid (EXECUTION_ENGINE_INVALID) */
|
|
57
|
+
elInvalid = "el_invalid",
|
|
58
|
+
/** a block failed our own checks (not correct w.r.t. our chain) */
|
|
59
|
+
invalidBlock = "invalid_block",
|
|
60
|
+
/** a block declares a parent payload hash that conflicts with the parent's actual payload (gloas) */
|
|
61
|
+
invalidParentPayload = "invalid_parent_payload",
|
|
62
|
+
/** a payload envelope failed gossip verification (ENVELOPE_VERIFICATION_ERROR) */
|
|
63
|
+
invalidEnvelope = "invalid_envelope",
|
|
64
|
+
/** a payload had an invalid signature (INVALID_SIGNATURE) */
|
|
65
|
+
invalidSignature = "invalid_signature",
|
|
66
|
+
/** a block root was given up on after exhausting download attempts (data could not be fetched) */
|
|
67
|
+
unavailable = "unavailable",
|
|
68
|
+
/** an unexpected / unhandled error code led to removal */
|
|
69
|
+
unknown = "unknown",
|
|
70
|
+
}
|
|
71
|
+
|
|
39
72
|
export enum PendingPayloadInputStatus {
|
|
40
73
|
pending = "pending",
|
|
41
74
|
fetching = "fetching",
|
package/src/sync/unknownBlock.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {routes} from "@lodestar/api";
|
|
2
2
|
import {ChainForkConfig} from "@lodestar/config";
|
|
3
3
|
import {CheckpointWithHex} from "@lodestar/fork-choice";
|
|
4
|
-
import {ForkSeq, isForkPostGloas} from "@lodestar/params";
|
|
4
|
+
import {ForkSeq, SLOTS_PER_EPOCH, isForkPostGloas} from "@lodestar/params";
|
|
5
5
|
import {RequestError, RequestErrorCode} from "@lodestar/reqresp";
|
|
6
6
|
import {computeTimeAtSlot} from "@lodestar/state-transition";
|
|
7
7
|
import {RootHex, Slot, gloas} from "@lodestar/types";
|
|
@@ -25,6 +25,7 @@ import {MAX_CONCURRENT_REQUESTS} from "./constants.js";
|
|
|
25
25
|
import {SyncOptions} from "./options.js";
|
|
26
26
|
import {
|
|
27
27
|
BlockInputSyncCacheItem,
|
|
28
|
+
DroppedItemReason,
|
|
28
29
|
PayloadSyncCacheItem,
|
|
29
30
|
PendingBlockInput,
|
|
30
31
|
PendingBlockInputStatus,
|
|
@@ -48,6 +49,10 @@ import {getRateLimitedUntilMs} from "./utils/rateLimit.js";
|
|
|
48
49
|
const MAX_ATTEMPTS_PER_BLOCK = 5;
|
|
49
50
|
const MAX_KNOWN_BAD_BLOCKS = 500;
|
|
50
51
|
const MAX_PENDING_BLOCKS = 100;
|
|
52
|
+
/**
|
|
53
|
+
* A pending payload without slot can only stay at most 2 epochs.
|
|
54
|
+
*/
|
|
55
|
+
const PRUNE_UNRESOLVED_SLOT_EPOCHS = 2;
|
|
51
56
|
|
|
52
57
|
type AdvancePendingBlockResult =
|
|
53
58
|
| "ready"
|
|
@@ -255,7 +260,7 @@ export class BlockInputSync {
|
|
|
255
260
|
parentRoot: missingDependency.parentRootHex,
|
|
256
261
|
parentBlockHash: missingDependency.parentBlockHashHex,
|
|
257
262
|
});
|
|
258
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
263
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidParentPayload);
|
|
259
264
|
}
|
|
260
265
|
return;
|
|
261
266
|
}
|
|
@@ -295,28 +300,37 @@ export class BlockInputSync {
|
|
|
295
300
|
// Use the actual finalized block slot, not computeStartSlotAtEpoch(checkpoint.epoch)
|
|
296
301
|
// because the payload at the finalized block is not finalized
|
|
297
302
|
const finalizedSlot = this.chain.forkChoice.getFinalizedBlock().slot;
|
|
303
|
+
const nowSec = Date.now() / 1000;
|
|
304
|
+
const maxSecInCache = (PRUNE_UNRESOLVED_SLOT_EPOCHS * SLOTS_PER_EPOCH * this.config.SLOT_DURATION_MS) / 1000;
|
|
298
305
|
|
|
299
|
-
let
|
|
306
|
+
let belowFinalizedPayloads = 0;
|
|
307
|
+
let agedOutPayloads = 0;
|
|
300
308
|
let unresolvedSlotPayloads = 0;
|
|
309
|
+
|
|
301
310
|
for (const [rootHex, payload] of this.pendingPayloads) {
|
|
302
311
|
// Last-effort resolve for a slot-less PendingPayloadRootHex, and store it back for later passes.
|
|
303
312
|
if (!isPendingPayloadInput(payload) && !isPendingPayloadEnvelope(payload) && payload.slot === undefined) {
|
|
304
313
|
payload.slot = this.resolvePayloadSlot(rootHex);
|
|
314
|
+
if (payload.slot === undefined) {
|
|
315
|
+
// Still unresolvable: age it out so the MAX_PENDING_BLOCKS cap cannot stay saturated.
|
|
316
|
+
if (nowSec - payload.timeAddedSec > maxSecInCache) {
|
|
317
|
+
this.pendingPayloads.delete(rootHex);
|
|
318
|
+
agedOutPayloads++;
|
|
319
|
+
} else {
|
|
320
|
+
unresolvedSlotPayloads++;
|
|
321
|
+
this.logger.debug("BlockInputSync.pruneFinalized: pending payload with unresolved slot", {
|
|
322
|
+
root: rootHex,
|
|
323
|
+
finalizedSlot,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
305
328
|
}
|
|
306
329
|
|
|
307
330
|
const slot = getPayloadSyncCacheItemSlot(payload);
|
|
308
|
-
if (typeof slot
|
|
309
|
-
unresolvedSlotPayloads++;
|
|
310
|
-
this.logger.debug("BlockInputSync.pruneFinalized: pending payload with unresolved slot", {
|
|
311
|
-
root: rootHex,
|
|
312
|
-
finalizedSlot,
|
|
313
|
-
});
|
|
314
|
-
continue;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
if (slot < finalizedSlot) {
|
|
331
|
+
if (typeof slot === "number" && slot < finalizedSlot) {
|
|
318
332
|
this.pendingPayloads.delete(rootHex);
|
|
319
|
-
|
|
333
|
+
belowFinalizedPayloads++;
|
|
320
334
|
}
|
|
321
335
|
}
|
|
322
336
|
|
|
@@ -329,14 +343,24 @@ export class BlockInputSync {
|
|
|
329
343
|
}
|
|
330
344
|
}
|
|
331
345
|
|
|
332
|
-
if (
|
|
333
|
-
this.metrics?.blockInputSync.
|
|
346
|
+
if (belowFinalizedPayloads > 0) {
|
|
347
|
+
this.metrics?.blockInputSync.removedPayloads.inc(
|
|
348
|
+
{reason: DroppedItemReason.belowFinalized},
|
|
349
|
+
belowFinalizedPayloads
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
if (agedOutPayloads > 0) {
|
|
353
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.agedOut}, agedOutPayloads);
|
|
354
|
+
}
|
|
355
|
+
if (deletedBlocks > 0) {
|
|
356
|
+
this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.belowFinalized}, deletedBlocks);
|
|
334
357
|
}
|
|
335
358
|
|
|
336
359
|
this.logger.debug("BlockInputSync.pruneFinalized dropped pre-finalized pending items", {
|
|
337
360
|
finalizedSlot,
|
|
338
361
|
finalizedRoot: checkpoint.rootHex,
|
|
339
|
-
|
|
362
|
+
belowFinalizedPayloads,
|
|
363
|
+
agedOutPayloads,
|
|
340
364
|
deletedBlocks,
|
|
341
365
|
unresolvedSlotPayloads,
|
|
342
366
|
});
|
|
@@ -369,6 +393,7 @@ export class BlockInputSync {
|
|
|
369
393
|
// Limit pending blocks to prevent DOS attacks that cause OOM
|
|
370
394
|
const prunedItemCount = pruneSetToMax(this.pendingBlocks, this.maxPendingBlocks);
|
|
371
395
|
if (prunedItemCount > 0) {
|
|
396
|
+
this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
|
|
372
397
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingBlocks`);
|
|
373
398
|
}
|
|
374
399
|
return added;
|
|
@@ -403,6 +428,7 @@ export class BlockInputSync {
|
|
|
403
428
|
// Limit pending blocks to prevent DOS attacks that cause OOM
|
|
404
429
|
const prunedItemCount = pruneSetToMax(this.pendingBlocks, this.maxPendingBlocks);
|
|
405
430
|
if (prunedItemCount > 0) {
|
|
431
|
+
this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
|
|
406
432
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingBlocks`);
|
|
407
433
|
}
|
|
408
434
|
};
|
|
@@ -427,6 +453,14 @@ export class BlockInputSync {
|
|
|
427
453
|
root: rootHex,
|
|
428
454
|
peerIdStr: peerIdStr ?? "unknown peer",
|
|
429
455
|
});
|
|
456
|
+
} else if (
|
|
457
|
+
// update a previously slot-less entry once a slot becomes available
|
|
458
|
+
slot !== undefined &&
|
|
459
|
+
!isPendingPayloadInput(pendingPayload) &&
|
|
460
|
+
!isPendingPayloadEnvelope(pendingPayload) &&
|
|
461
|
+
pendingPayload.slot === undefined
|
|
462
|
+
) {
|
|
463
|
+
pendingPayload.slot = slot;
|
|
430
464
|
}
|
|
431
465
|
|
|
432
466
|
if (peerIdStr) {
|
|
@@ -435,6 +469,7 @@ export class BlockInputSync {
|
|
|
435
469
|
|
|
436
470
|
const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
|
|
437
471
|
if (prunedItemCount > 0) {
|
|
472
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
|
|
438
473
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
|
|
439
474
|
}
|
|
440
475
|
return added;
|
|
@@ -488,6 +523,7 @@ export class BlockInputSync {
|
|
|
488
523
|
|
|
489
524
|
const prunedItemCount = pruneSetToMax(this.pendingPayloads, this.maxPendingBlocks);
|
|
490
525
|
if (prunedItemCount > 0) {
|
|
526
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.capacity}, prunedItemCount);
|
|
491
527
|
this.logger.verbose(`Pruned ${prunedItemCount} items from BlockInputSync.pendingPayloads`);
|
|
492
528
|
}
|
|
493
529
|
};
|
|
@@ -582,7 +618,7 @@ export class BlockInputSync {
|
|
|
582
618
|
parentRoot: missingDependency.parentRootHex,
|
|
583
619
|
parentBlockHash: missingDependency.parentBlockHashHex,
|
|
584
620
|
});
|
|
585
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
621
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidParentPayload);
|
|
586
622
|
return "removed";
|
|
587
623
|
}
|
|
588
624
|
}
|
|
@@ -805,7 +841,7 @@ export class BlockInputSync {
|
|
|
805
841
|
...logCtx2,
|
|
806
842
|
finalizedSlot,
|
|
807
843
|
});
|
|
808
|
-
this.removeAndDownScoreAllDescendants(block);
|
|
844
|
+
this.removeAndDownScoreAllDescendants(block, DroppedItemReason.belowFinalized);
|
|
809
845
|
} else {
|
|
810
846
|
this.onUnknownBlockRoot({rootHex: pending.blockInput.parentRootHex, source: BlockInputSource.byRoot});
|
|
811
847
|
}
|
|
@@ -822,7 +858,7 @@ export class BlockInputSync {
|
|
|
822
858
|
|
|
823
859
|
this.metrics?.blockInputSync.downloadedBlocksError.inc();
|
|
824
860
|
this.logger.debug("Ignoring unknown block root after many failed downloads", logCtx, res.err);
|
|
825
|
-
this.removeAndDownScoreAllDescendants(block);
|
|
861
|
+
this.removeAndDownScoreAllDescendants(block, DroppedItemReason.unavailable);
|
|
826
862
|
}
|
|
827
863
|
}
|
|
828
864
|
|
|
@@ -930,19 +966,19 @@ export class BlockInputSync {
|
|
|
930
966
|
case BlockErrorCode.EXECUTION_ENGINE_ERROR:
|
|
931
967
|
// Removing the block(s) without penalizing the peers, hoping for EL to
|
|
932
968
|
// recover on a latter download + verify attempt.
|
|
933
|
-
this.removeAllDescendants(pendingBlock);
|
|
969
|
+
this.removeAllDescendants(pendingBlock, DroppedItemReason.elError, DroppedItemReason.elError);
|
|
934
970
|
break;
|
|
935
971
|
|
|
936
972
|
case BlockErrorCode.EXECUTION_ENGINE_INVALID:
|
|
937
973
|
// the peer served a bad block
|
|
938
974
|
this.logger.debug("Execution engine rejected block from unknown parent sync", errorData, res.err);
|
|
939
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
975
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.elInvalid);
|
|
940
976
|
break;
|
|
941
977
|
|
|
942
978
|
default:
|
|
943
979
|
// Block is not correct with respect to our chain. Log error loudly
|
|
944
980
|
this.logger.debug("Error processing block from unknown parent sync", errorData, res.err);
|
|
945
|
-
this.removeAndDownScoreAllDescendants(pendingBlock);
|
|
981
|
+
this.removeAndDownScoreAllDescendants(pendingBlock, DroppedItemReason.invalidBlock);
|
|
946
982
|
}
|
|
947
983
|
}
|
|
948
984
|
|
|
@@ -1146,17 +1182,27 @@ export class BlockInputSync {
|
|
|
1146
1182
|
break;
|
|
1147
1183
|
|
|
1148
1184
|
case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
|
|
1185
|
+
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1186
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.elInvalid);
|
|
1187
|
+
break;
|
|
1188
|
+
|
|
1149
1189
|
case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
|
|
1190
|
+
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1191
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidEnvelope);
|
|
1192
|
+
break;
|
|
1193
|
+
|
|
1150
1194
|
case PayloadErrorCode.INVALID_SIGNATURE:
|
|
1151
1195
|
// TODO GLOAS: Decide how invalid payload inputs should eventually leave memory without
|
|
1152
1196
|
// reintroducing envelope replacement / recreation flows.
|
|
1153
1197
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1154
|
-
this.removePendingPayloadAndDescendants(rootHex);
|
|
1198
|
+
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidSignature);
|
|
1155
1199
|
break;
|
|
1156
1200
|
|
|
1157
1201
|
default:
|
|
1158
1202
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1159
|
-
this.pendingPayloads.delete(rootHex)
|
|
1203
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1204
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.unknown}, 1);
|
|
1205
|
+
}
|
|
1160
1206
|
}
|
|
1161
1207
|
return;
|
|
1162
1208
|
}
|
|
@@ -1546,9 +1592,9 @@ export class BlockInputSync {
|
|
|
1546
1592
|
* Downscore all peers that have referenced any of this bad blocks. May report peers multiple times if they have
|
|
1547
1593
|
* referenced more than one bad block.
|
|
1548
1594
|
*/
|
|
1549
|
-
private removeAndDownScoreAllDescendants(block: BlockInputSyncCacheItem): void {
|
|
1595
|
+
private removeAndDownScoreAllDescendants(block: BlockInputSyncCacheItem, headReason: DroppedItemReason): void {
|
|
1550
1596
|
// Get all blocks that are a descendant of this one
|
|
1551
|
-
const badPendingBlocks = this.removeAllDescendants(block);
|
|
1597
|
+
const badPendingBlocks = this.removeAllDescendants(block, headReason, DroppedItemReason.invalidParent);
|
|
1552
1598
|
// just console log and do not penalize on pending/bad blocks for debugging
|
|
1553
1599
|
// console.log("removeAndDownscoreAllDescendants", {block});
|
|
1554
1600
|
|
|
@@ -1575,18 +1621,22 @@ export class BlockInputSync {
|
|
|
1575
1621
|
}
|
|
1576
1622
|
|
|
1577
1623
|
// Once a parent payload is invalid, every descendant waiting on that payload lineage becomes unrecoverable too.
|
|
1578
|
-
private removePendingPayloadAndDescendants(rootHex: RootHex): void {
|
|
1624
|
+
private removePendingPayloadAndDescendants(rootHex: RootHex, headReason: DroppedItemReason): void {
|
|
1579
1625
|
// Keep PayloadEnvelopeInput resident in the seen cache. importBlock() owns that object and
|
|
1580
1626
|
// later validation/finalization logic decides when it can leave memory.
|
|
1581
|
-
this.pendingPayloads.delete(rootHex)
|
|
1627
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1628
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: headReason}, 1);
|
|
1629
|
+
}
|
|
1582
1630
|
|
|
1583
1631
|
const badPendingBlocks = getAllDescendantBlocks(rootHex, this.pendingBlocks);
|
|
1584
|
-
this.metrics?.blockInputSync.removedBlocks.inc(badPendingBlocks.length);
|
|
1585
|
-
|
|
1586
1632
|
for (const block of badPendingBlocks) {
|
|
1587
1633
|
const descendantRootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1588
|
-
this.pendingBlocks.delete(descendantRootHex)
|
|
1589
|
-
|
|
1634
|
+
if (this.pendingBlocks.delete(descendantRootHex)) {
|
|
1635
|
+
this.metrics?.blockInputSync.removedBlocks.inc({reason: DroppedItemReason.invalidParent}, 1);
|
|
1636
|
+
}
|
|
1637
|
+
if (this.pendingPayloads.delete(descendantRootHex)) {
|
|
1638
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason: DroppedItemReason.invalidParent}, 1);
|
|
1639
|
+
}
|
|
1590
1640
|
this.chain.seenBlockInputCache.prune(descendantRootHex);
|
|
1591
1641
|
this.logger.debug("Removing pending descendant after invalid parent payload", {
|
|
1592
1642
|
slot: getBlockInputSyncCacheItemSlot(block),
|
|
@@ -1596,18 +1646,25 @@ export class BlockInputSync {
|
|
|
1596
1646
|
}
|
|
1597
1647
|
}
|
|
1598
1648
|
|
|
1599
|
-
private removeAllDescendants(
|
|
1649
|
+
private removeAllDescendants(
|
|
1650
|
+
block: BlockInputSyncCacheItem,
|
|
1651
|
+
headReason: DroppedItemReason,
|
|
1652
|
+
descendantReason: DroppedItemReason = DroppedItemReason.invalidParent
|
|
1653
|
+
): BlockInputSyncCacheItem[] {
|
|
1600
1654
|
const rootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1601
1655
|
const slot = getBlockInputSyncCacheItemSlot(block);
|
|
1602
|
-
// Get all blocks that are a descendant of this one
|
|
1656
|
+
// Get all blocks that are a descendant of this one (index 0 is the head item itself)
|
|
1603
1657
|
const badPendingBlocks = [block, ...getAllDescendantBlocks(rootHex, this.pendingBlocks)];
|
|
1604
1658
|
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
for (const block of badPendingBlocks) {
|
|
1659
|
+
for (const [i, block] of badPendingBlocks.entries()) {
|
|
1608
1660
|
const rootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1609
|
-
|
|
1610
|
-
this.
|
|
1661
|
+
const reason = i === 0 ? headReason : descendantReason;
|
|
1662
|
+
if (this.pendingBlocks.delete(rootHex)) {
|
|
1663
|
+
this.metrics?.blockInputSync.removedBlocks.inc({reason}, 1);
|
|
1664
|
+
}
|
|
1665
|
+
if (this.pendingPayloads.delete(rootHex)) {
|
|
1666
|
+
this.metrics?.blockInputSync.removedPayloads.inc({reason}, 1);
|
|
1667
|
+
}
|
|
1611
1668
|
this.chain.seenBlockInputCache.prune(rootHex);
|
|
1612
1669
|
// Keep PayloadEnvelopeInput resident in the seen cache for consistency with the
|
|
1613
1670
|
// importBlock()-owned lifecycle.
|