@lodestar/beacon-node 1.50.0-dev.2e668c516f → 1.50.0-dev.3bd495295e
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/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +4 -0
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +2 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/errors/gossipValidation.d.ts +2 -1
- package/lib/chain/errors/gossipValidation.d.ts.map +1 -1
- package/lib/chain/errors/gossipValidation.js +1 -0
- package/lib/chain/errors/gossipValidation.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +5 -2
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +6 -2
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +6 -0
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +10 -0
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +3 -0
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/gossipValidatorFn.d.ts +3 -1
- package/lib/network/processor/gossipValidatorFn.d.ts.map +1 -1
- package/lib/network/processor/gossipValidatorFn.js +1 -1
- package/lib/network/processor/gossipValidatorFn.js.map +1 -1
- package/lib/network/processor/index.d.ts +1 -0
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +28 -8
- package/lib/network/processor/index.js.map +1 -1
- package/lib/sync/range/range.d.ts.map +1 -1
- package/lib/sync/range/range.js +50 -18
- package/lib/sync/range/range.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +5 -4
- package/lib/sync/unknownBlock.js.map +1 -1
- package/package.json +15 -15
- package/src/chain/blocks/importBlock.ts +4 -0
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +2 -1
- package/src/chain/errors/gossipValidation.ts +1 -0
- package/src/chain/prepareNextSlot.ts +6 -3
- package/src/chain/produceBlock/produceBlockBody.ts +8 -1
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +11 -0
- package/src/network/processor/gossipHandlers.ts +3 -0
- package/src/network/processor/gossipValidatorFn.ts +1 -1
- package/src/network/processor/index.ts +34 -8
- package/src/sync/range/range.ts +50 -18
- package/src/sync/unknownBlock.ts +5 -4
|
@@ -22,8 +22,9 @@ export enum PayloadEnvelopeInputSource {
|
|
|
22
22
|
* - finalized: below the finalized slot
|
|
23
23
|
* - prune: explicit prune by root
|
|
24
24
|
* - cap: insertion-order backstop cap (MAX_PAYLOAD_ENVELOPE_INPUT_CACHE_SIZE)
|
|
25
|
+
* - invalid: the cached envelope failed import with a definitive invalid verdict
|
|
25
26
|
*/
|
|
26
|
-
export type PayloadEnvelopeInputPruneReason = "belowParent" | "finalized" | "remove" | "cap";
|
|
27
|
+
export type PayloadEnvelopeInputPruneReason = "belowParent" | "finalized" | "remove" | "cap" | "invalid";
|
|
27
28
|
|
|
28
29
|
export type SourceMeta = {
|
|
29
30
|
source: PayloadEnvelopeInputSource;
|
|
@@ -9,6 +9,7 @@ export enum GossipErrorCode {
|
|
|
9
9
|
INVALID_SERIALIZED_BYTES_ERROR_CODE = "GOSSIP_ERROR_INVALID_SERIALIZED_BYTES",
|
|
10
10
|
PAST_SLOT = "GOSSIP_ERROR_PAST_SLOT",
|
|
11
11
|
FUTURE_SLOT = "GOSSIP_ERROR_FUTURE_SLOT",
|
|
12
|
+
INVALID_SLOT = "GOSSIP_ERROR_INVALID_SLOT",
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
export class GossipActionError<T extends {code: string}> extends LodestarError<T> {
|
|
@@ -244,6 +244,10 @@ export class PrepareNextSlotScheduler {
|
|
|
244
244
|
parentBlockHash = preparedState.latestExecutionPayloadHeader.blockHash;
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
+
// The payload_attributes event must carry the same hashes we send to the EL
|
|
248
|
+
const safeBlockHash = getSafeExecutionBlockHash(this.chain.forkChoice, this.logger);
|
|
249
|
+
const finalizedBlockHash = getFinalizedExecutionBlockHash(this.chain.forkChoice);
|
|
250
|
+
|
|
247
251
|
let payloadAttributes: PayloadAttributes | undefined;
|
|
248
252
|
// If emitPayloadAttributes is true emit a SSE payloadAttributes event for
|
|
249
253
|
// every slot. Without the flag, only emit the event if we are proposing in the next slot.
|
|
@@ -256,6 +260,8 @@ export class PrepareNextSlotScheduler {
|
|
|
256
260
|
prepareSlot,
|
|
257
261
|
parentBlockRoot: fromHex(updatedHead.blockRoot),
|
|
258
262
|
parentBlockHash,
|
|
263
|
+
safeBlockHash,
|
|
264
|
+
finalizedBlockHash,
|
|
259
265
|
feeRecipient: feeRecipient ?? "0x0000000000000000000000000000000000000000",
|
|
260
266
|
});
|
|
261
267
|
this.chain.emitter.emit(routes.events.EventType.payloadAttributes, {data, version: fork});
|
|
@@ -267,9 +273,6 @@ export class PrepareNextSlotScheduler {
|
|
|
267
273
|
computeTimeAtSlot(this.config, prepareSlot, this.chain.genesisTime) - Date.now() / 1000;
|
|
268
274
|
this.metrics?.blockPayload.payloadAdvancePrepTime.observe(preparationTime);
|
|
269
275
|
|
|
270
|
-
const safeBlockHash = getSafeExecutionBlockHash(this.chain.forkChoice, this.logger);
|
|
271
|
-
const finalizedBlockHash = getFinalizedExecutionBlockHash(this.chain.forkChoice);
|
|
272
|
-
|
|
273
276
|
// awaiting here instead of throwing an async call because there is no other task
|
|
274
277
|
// left for scheduler and this gives nice semantics to catch and log errors in the
|
|
275
278
|
// try/catch wrapper here.
|
|
@@ -845,6 +845,8 @@ export function getPayloadAttributesForSSE(
|
|
|
845
845
|
prepareSlot,
|
|
846
846
|
parentBlockRoot,
|
|
847
847
|
parentBlockHash,
|
|
848
|
+
safeBlockHash,
|
|
849
|
+
finalizedBlockHash,
|
|
848
850
|
feeRecipient,
|
|
849
851
|
}: {
|
|
850
852
|
/**
|
|
@@ -855,6 +857,8 @@ export function getPayloadAttributesForSSE(
|
|
|
855
857
|
prepareSlot: Slot;
|
|
856
858
|
parentBlockRoot: Root;
|
|
857
859
|
parentBlockHash: Bytes32;
|
|
860
|
+
safeBlockHash: RootHex;
|
|
861
|
+
finalizedBlockHash: RootHex;
|
|
858
862
|
feeRecipient: string;
|
|
859
863
|
}
|
|
860
864
|
): SSEPayloadAttributes {
|
|
@@ -874,7 +878,10 @@ export function getPayloadAttributesForSSE(
|
|
|
874
878
|
payloadAttributes,
|
|
875
879
|
} as SSEPayloadAttributes;
|
|
876
880
|
|
|
877
|
-
if (
|
|
881
|
+
if (isForkPostGloas(fork)) {
|
|
882
|
+
(ssePayloadAttributes as gloas.SSEPayloadAttributes).safeBlockHash = fromHex(safeBlockHash);
|
|
883
|
+
(ssePayloadAttributes as gloas.SSEPayloadAttributes).finalizedBlockHash = fromHex(finalizedBlockHash);
|
|
884
|
+
} else {
|
|
878
885
|
// Removed in Gloas, builders can get the block number from the EL via the block hash if required
|
|
879
886
|
(ssePayloadAttributes as bellatrix.SSEPayloadAttributes).parentBlockNumber = prepareState.payloadBlockNumber;
|
|
880
887
|
}
|
|
@@ -264,6 +264,17 @@ export class SeenPayloadEnvelopeInput {
|
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
+
/**
|
|
268
|
+
* Removes a PayloadEnvelopeInput whose envelope failed import with a definitive invalid verdict. The envelope
|
|
269
|
+
* cannot be replaced in place, sync recreates the entry via `getOrReload()` or `add()` to fetch another one.
|
|
270
|
+
* No-op if the cached entry is a different object, it may already be the recreated one.
|
|
271
|
+
*/
|
|
272
|
+
removeInvalid(payloadInput: PayloadEnvelopeInput): void {
|
|
273
|
+
if (this.payloadInputs.get(payloadInput.blockRootHex) === payloadInput) {
|
|
274
|
+
this.evictPayloadInput(payloadInput, "invalid");
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
267
278
|
pruneBelowParent(parentBlock: ProtoBlock): void {
|
|
268
279
|
for (const block of this.forkChoice.getAllAncestorBlocks(parentBlock.blockRoot, parentBlock.payloadStatus)) {
|
|
269
280
|
// Only evict once the payload is FULL (revealed/imported) — on an EMPTY/PENDING branch we may
|
|
@@ -1341,6 +1341,9 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1341
1341
|
case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
|
|
1342
1342
|
case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
|
|
1343
1343
|
core.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadGossipPayload");
|
|
1344
|
+
// The builder may have signed another envelope that is valid, keeping this one would make
|
|
1345
|
+
// by-root and range sync reuse it and never import the payload
|
|
1346
|
+
chain.seenPayloadEnvelopeInputCache.removeInvalid(payloadInput);
|
|
1344
1347
|
// Misbehaving peer, but could highlight an issue in another client
|
|
1345
1348
|
logLevel = LogLevel.warn;
|
|
1346
1349
|
break;
|
|
@@ -112,7 +112,7 @@ const gossipRejectPeerAction: Record<GossipType, RejectPeerActionRule> = {
|
|
|
112
112
|
[GossipType.light_client_optimistic_update]: {default: PeerAction.HighToleranceError},
|
|
113
113
|
};
|
|
114
114
|
|
|
115
|
-
function rejectPeerAction(type: GossipType, code: string): PeerAction {
|
|
115
|
+
export function rejectPeerAction(type: GossipType, code: string): PeerAction {
|
|
116
116
|
const rule = gossipRejectPeerAction[type];
|
|
117
117
|
return rule.byCode?.[code] ?? rule.default;
|
|
118
118
|
}
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
getParentRootFromSignedBeaconBlockSerialized,
|
|
23
23
|
getPayloadPresentFromPayloadAttestationMessageSerialized,
|
|
24
24
|
} from "../../util/sszBytes.js";
|
|
25
|
+
import {INetworkCore} from "../core/index.js";
|
|
25
26
|
import {NetworkEvent, NetworkEventBus} from "../events.js";
|
|
26
27
|
import {
|
|
27
28
|
GossipHandlers,
|
|
@@ -34,7 +35,12 @@ import {MAX_PEERS_PER_ROOT} from "./constants.js";
|
|
|
34
35
|
import {createExtractBlockSlotRootFns} from "./extractSlotRootFns.js";
|
|
35
36
|
import {GossipHandlerOpts, ValidatorFnsModules, getGossipHandlers} from "./gossipHandlers.js";
|
|
36
37
|
import {createGossipQueues} from "./gossipQueues/index.js";
|
|
37
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
ValidatorFnModules,
|
|
40
|
+
getGossipValidatorBatchFn,
|
|
41
|
+
getGossipValidatorFn,
|
|
42
|
+
rejectPeerAction,
|
|
43
|
+
} from "./gossipValidatorFn.js";
|
|
38
44
|
import {PendingGossipsubMessage} from "./types.js";
|
|
39
45
|
|
|
40
46
|
export * from "./types.js";
|
|
@@ -232,6 +238,7 @@ type SearchUnknownRootTarget =
|
|
|
232
238
|
*/
|
|
233
239
|
export class NetworkProcessor {
|
|
234
240
|
private readonly chain: IBeaconChain;
|
|
241
|
+
private readonly core: INetworkCore;
|
|
235
242
|
private readonly events: NetworkEventBus;
|
|
236
243
|
private readonly logger: Logger;
|
|
237
244
|
private readonly metrics: Metrics | null;
|
|
@@ -261,8 +268,9 @@ export class NetworkProcessor {
|
|
|
261
268
|
modules: NetworkProcessorModules,
|
|
262
269
|
private readonly opts: NetworkProcessorOpts
|
|
263
270
|
) {
|
|
264
|
-
const {chain, events, logger, metrics} = modules;
|
|
271
|
+
const {chain, core, events, logger, metrics} = modules;
|
|
265
272
|
this.chain = chain;
|
|
273
|
+
this.core = core;
|
|
266
274
|
this.events = events;
|
|
267
275
|
this.metrics = metrics;
|
|
268
276
|
this.logger = logger;
|
|
@@ -409,16 +417,34 @@ export class NetworkProcessor {
|
|
|
409
417
|
private onPendingGossipsubMessage = (message: PendingGossipsubMessage): void => {
|
|
410
418
|
const topicType = message.topic.type;
|
|
411
419
|
const extractBlockSlotRootFn = this.extractBlockSlotRootFns[topicType];
|
|
420
|
+
if (extractBlockSlotRootFn === undefined) {
|
|
421
|
+
// some messages don't have slot and root
|
|
422
|
+
this.pushPendingGossipsubMessageToQueue(message);
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
412
425
|
|
|
413
426
|
// 1st extract round: make sure slot is in range and if block root is not available
|
|
414
427
|
// proactively search for it + queue the message
|
|
415
|
-
const slotRoot = extractBlockSlotRootFn
|
|
416
|
-
? extractBlockSlotRootFn(message.msg.data, message.topic.boundary.fork)
|
|
417
|
-
: null;
|
|
428
|
+
const slotRoot = extractBlockSlotRootFn(message.msg.data, message.topic.boundary.fork);
|
|
418
429
|
if (slotRoot === null) {
|
|
419
|
-
//
|
|
420
|
-
|
|
421
|
-
|
|
430
|
+
// DOS protection: null means malformed bytes or slot >= 2^32, neither is forwarded by honest peers.
|
|
431
|
+
const {propagationSource, clientAgent, clientVersion} = message;
|
|
432
|
+
const code = GossipErrorCode.INVALID_SLOT;
|
|
433
|
+
const peerAction = rejectPeerAction(topicType, code);
|
|
434
|
+
this.metrics?.networkProcessor.gossipValidationReject.inc({topic: topicType});
|
|
435
|
+
this.core.reportPeer(propagationSource, peerAction, code);
|
|
436
|
+
this.logger.debug(`Gossip validation ${topicType} rejected due to null slot`, {
|
|
437
|
+
peer: propagationSource,
|
|
438
|
+
clientAgent,
|
|
439
|
+
clientVersion,
|
|
440
|
+
peerAction,
|
|
441
|
+
code,
|
|
442
|
+
});
|
|
443
|
+
this.events.emit(NetworkEvent.gossipMessageValidationResult, {
|
|
444
|
+
msgId: message.msgId,
|
|
445
|
+
propagationSource,
|
|
446
|
+
acceptance: TopicValidatorResult.Reject,
|
|
447
|
+
});
|
|
422
448
|
return;
|
|
423
449
|
}
|
|
424
450
|
|
package/src/sync/range/range.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {IBeaconStateViewGloas, computeStartSlotAtEpoch, isStatePostGloas} from "
|
|
|
5
5
|
import {Epoch, Status, fulu} from "@lodestar/types";
|
|
6
6
|
import {Logger, prettyPrintIndices, toRootHex} from "@lodestar/utils";
|
|
7
7
|
import {IBlockInput} from "../../chain/blocks/blockInput/types.js";
|
|
8
|
+
import {PayloadError, PayloadErrorCode} from "../../chain/blocks/importExecutionPayload.js";
|
|
8
9
|
import {AttestationImportOpt, ImportBlockOpts} from "../../chain/blocks/index.js";
|
|
9
10
|
import {assertLinearChainSegment} from "../../chain/blocks/utils/chainSegment.js";
|
|
10
11
|
import {BlockError} from "../../chain/errors/index.js";
|
|
@@ -207,22 +208,50 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
|
|
|
207
208
|
blsVerifyOnMainThread: false,
|
|
208
209
|
};
|
|
209
210
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
211
|
+
try {
|
|
212
|
+
if (this.opts?.disableProcessAsChainSegment) {
|
|
213
|
+
// Should only be used for debugging or testing
|
|
214
|
+
for (const block of blocks) {
|
|
215
|
+
await this.chain.processBlock(block, flags);
|
|
216
|
+
const payloadEnvelope = payloadEnvelopes?.get(block.slot);
|
|
217
|
+
if (payloadEnvelope?.hasPayloadEnvelope()) {
|
|
218
|
+
await this.chain.processExecutionPayload(payloadEnvelope);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
} else {
|
|
222
|
+
const {orphaned, skipped} = await this.chain.processChainSegment(blocks, payloadEnvelopes, flags);
|
|
223
|
+
// We log orphaned payloads to work with different clients
|
|
224
|
+
// in the future, consider applying penalties in certain conditions
|
|
225
|
+
// make sure the payload source was from range sync in that case
|
|
226
|
+
for (const {slot, payloadEnvelopeInput} of orphaned) {
|
|
227
|
+
const {peerIdStr, source} = payloadEnvelopeInput.getPayloadEnvelopeSource();
|
|
228
|
+
let client = "unknown";
|
|
229
|
+
if (peerIdStr !== undefined) {
|
|
230
|
+
try {
|
|
231
|
+
client = this.getConnectedPeerSyncMeta(peerIdStr).client;
|
|
232
|
+
} catch {
|
|
233
|
+
// peer disconnected since serving the envelope, keep "unknown"
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
this.logger.debug("Orphaned payload envelope in range sync batch", {
|
|
237
|
+
slot,
|
|
238
|
+
root: payloadEnvelopeInput.blockRootHex,
|
|
239
|
+
peer: peerIdStr ?? "unknown",
|
|
240
|
+
source,
|
|
241
|
+
client,
|
|
242
|
+
skipped,
|
|
243
|
+
});
|
|
217
244
|
}
|
|
218
245
|
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
246
|
+
} catch (e) {
|
|
247
|
+
if (
|
|
248
|
+
e instanceof PayloadError &&
|
|
249
|
+
(e.type.code === PayloadErrorCode.INVALID_SIGNATURE ||
|
|
250
|
+
e.type.code === PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR ||
|
|
251
|
+
e.type.code === PayloadErrorCode.EXECUTION_ENGINE_INVALID)
|
|
252
|
+
) {
|
|
253
|
+
const {payloadInput} = e;
|
|
254
|
+
const {peerIdStr, source} = payloadInput.getPayloadEnvelopeSource();
|
|
226
255
|
let client = "unknown";
|
|
227
256
|
if (peerIdStr !== undefined) {
|
|
228
257
|
try {
|
|
@@ -231,15 +260,18 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
|
|
|
231
260
|
// peer disconnected since serving the envelope, keep "unknown"
|
|
232
261
|
}
|
|
233
262
|
}
|
|
234
|
-
this.logger.debug("
|
|
235
|
-
slot,
|
|
236
|
-
root:
|
|
263
|
+
this.logger.debug("Removing invalid payload envelope in range sync batch", {
|
|
264
|
+
slot: payloadInput.slot,
|
|
265
|
+
root: payloadInput.blockRootHex,
|
|
237
266
|
peer: peerIdStr ?? "unknown",
|
|
238
267
|
source,
|
|
239
268
|
client,
|
|
240
|
-
|
|
269
|
+
code: e.type.code,
|
|
241
270
|
});
|
|
271
|
+
// The batch is downloaded again, it would otherwise reuse the cached envelope and fail the same way
|
|
272
|
+
this.chain.seenPayloadEnvelopeInputCache.removeInvalid(payloadInput);
|
|
242
273
|
}
|
|
274
|
+
throw e;
|
|
243
275
|
}
|
|
244
276
|
};
|
|
245
277
|
|
package/src/sync/unknownBlock.ts
CHANGED
|
@@ -1426,20 +1426,23 @@ export class BlockInputSync {
|
|
|
1426
1426
|
pendingPayload.status = PendingPayloadInputStatus.downloaded;
|
|
1427
1427
|
break;
|
|
1428
1428
|
|
|
1429
|
+
// The invalid envelope is evicted from the seen cache in the cases below, otherwise the next
|
|
1430
|
+
// attempt would reuse it instead of fetching another envelope from peers
|
|
1429
1431
|
case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
|
|
1430
1432
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1433
|
+
this.chain.seenPayloadEnvelopeInputCache.removeInvalid(pendingPayload.payloadInput);
|
|
1431
1434
|
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.elInvalid);
|
|
1432
1435
|
break;
|
|
1433
1436
|
|
|
1434
1437
|
case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
|
|
1435
1438
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1439
|
+
this.chain.seenPayloadEnvelopeInputCache.removeInvalid(pendingPayload.payloadInput);
|
|
1436
1440
|
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidEnvelope);
|
|
1437
1441
|
break;
|
|
1438
1442
|
|
|
1439
1443
|
case PayloadErrorCode.INVALID_SIGNATURE:
|
|
1440
|
-
// TODO GLOAS: Decide how invalid payload inputs should eventually leave memory without
|
|
1441
|
-
// reintroducing envelope replacement / recreation flows.
|
|
1442
1444
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1445
|
+
this.chain.seenPayloadEnvelopeInputCache.removeInvalid(pendingPayload.payloadInput);
|
|
1443
1446
|
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidSignature);
|
|
1444
1447
|
break;
|
|
1445
1448
|
|
|
@@ -1879,8 +1882,6 @@ export class BlockInputSync {
|
|
|
1879
1882
|
|
|
1880
1883
|
// Once a parent payload is invalid, every descendant waiting on that payload lineage becomes unrecoverable too.
|
|
1881
1884
|
private removePendingPayloadAndDescendants(rootHex: RootHex, headReason: DroppedItemReason): void {
|
|
1882
|
-
// Keep PayloadEnvelopeInput resident in the seen cache. importBlock() owns that object and
|
|
1883
|
-
// later validation/finalization logic decides when it can leave memory.
|
|
1884
1885
|
if (this.pendingPayloads.delete(rootHex)) {
|
|
1885
1886
|
this.metrics?.blockInputSync.removedPayloads.inc({reason: headReason}, 1);
|
|
1886
1887
|
}
|