@lodestar/beacon-node 1.50.0-dev.f2d839a25c → 1.50.0-dev.fd4f342a40
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/pool/index.js +4 -4
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts +1 -0
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +6 -2
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts +4 -1
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.js +4 -3
- package/lib/chain/archiveStore/utils/pruneHistory.js.map +1 -1
- 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/chain.js +3 -3
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/gossipValidation.d.ts +3 -1
- package/lib/chain/errors/gossipValidation.d.ts.map +1 -1
- package/lib/chain/errors/gossipValidation.js +2 -0
- package/lib/chain/errors/gossipValidation.js.map +1 -1
- package/lib/chain/initState.d.ts +2 -9
- package/lib/chain/initState.d.ts.map +1 -1
- package/lib/chain/initState.js +5 -38
- package/lib/chain/initState.js.map +1 -1
- package/lib/chain/opPools/opPool.d.ts +5 -4
- package/lib/chain/opPools/opPool.d.ts.map +1 -1
- package/lib/chain/opPools/opPool.js +62 -18
- package/lib/chain/opPools/opPool.js.map +1 -1
- package/lib/chain/opPools/utils.d.ts +4 -1
- package/lib/chain/opPools/utils.d.ts.map +1 -1
- package/lib/chain/opPools/utils.js +11 -1
- package/lib/chain/opPools/utils.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/chain/validation/attesterSlashing.d.ts +4 -4
- package/lib/chain/validation/attesterSlashing.d.ts.map +1 -1
- package/lib/chain/validation/attesterSlashing.js +3 -0
- package/lib/chain/validation/attesterSlashing.js.map +1 -1
- package/lib/chain/validation/proposerSlashing.d.ts +3 -3
- package/lib/chain/validation/proposerSlashing.d.ts.map +1 -1
- package/lib/chain/validation/proposerSlashing.js +3 -0
- package/lib/chain/validation/proposerSlashing.js.map +1 -1
- package/lib/db/beacon.d.ts.map +1 -1
- package/lib/db/beacon.js +2 -0
- package/lib/db/beacon.js.map +1 -1
- package/lib/db/repositories/proposerSlashing.d.ts +1 -0
- package/lib/db/repositories/proposerSlashing.d.ts.map +1 -1
- package/lib/db/repositories/proposerSlashing.js +5 -1
- package/lib/db/repositories/proposerSlashing.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/network/metadata.d.ts +1 -1
- package/lib/network/metadata.d.ts.map +1 -1
- package/lib/network/metadata.js +17 -17
- package/lib/network/metadata.js.map +1 -1
- package/lib/network/peers/datastore.d.ts +9 -9
- package/lib/network/peers/datastore.d.ts.map +1 -1
- package/lib/network/peers/datastore.js +36 -36
- package/lib/network/peers/datastore.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +7 -4
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/gossipValidatorFn.d.ts +7 -3
- package/lib/network/processor/gossipValidatorFn.d.ts.map +1 -1
- package/lib/network/processor/gossipValidatorFn.js +7 -3
- package/lib/network/processor/gossipValidatorFn.js.map +1 -1
- package/lib/network/processor/index.d.ts +9 -0
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +77 -10
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +41 -26
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.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 +9 -5
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +88 -46
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/util/execution.d.ts +2 -0
- package/lib/util/execution.d.ts.map +1 -1
- package/lib/util/execution.js +1 -1
- package/lib/util/execution.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/beacon/pool/index.ts +4 -4
- package/src/chain/archiveStore/archiveStore.ts +9 -3
- package/src/chain/archiveStore/utils/pruneHistory.ts +6 -4
- package/src/chain/blocks/importBlock.ts +4 -0
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +2 -1
- package/src/chain/chain.ts +3 -3
- package/src/chain/errors/gossipValidation.ts +2 -0
- package/src/chain/initState.ts +8 -60
- package/src/chain/opPools/opPool.ts +91 -18
- package/src/chain/opPools/utils.ts +31 -3
- 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/chain/validation/attesterSlashing.ts +7 -4
- package/src/chain/validation/proposerSlashing.ts +7 -4
- package/src/db/beacon.ts +2 -0
- package/src/db/repositories/proposerSlashing.ts +6 -1
- package/src/index.ts +1 -1
- package/src/network/metadata.ts +17 -17
- package/src/network/peers/datastore.ts +36 -36
- package/src/network/processor/gossipHandlers.ts +7 -4
- package/src/network/processor/gossipValidatorFn.ts +13 -3
- package/src/network/processor/index.ts +89 -10
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +42 -28
- package/src/sync/range/range.ts +50 -18
- package/src/sync/unknownBlock.ts +99 -50
- package/src/util/execution.ts +1 -1
|
@@ -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";
|
|
@@ -102,6 +108,12 @@ type SearchedRootEntry = {
|
|
|
102
108
|
*/
|
|
103
109
|
const DEFAULT_EARLIEST_PERMISSIBLE_SLOT_DISTANCE = 32;
|
|
104
110
|
|
|
111
|
+
/**
|
|
112
|
+
* The network worker bans a Fatal peer only on its next PeerManager heartbeat (30s). Until then the main thread
|
|
113
|
+
* would keep validating that peer's messages, so drop them here. 3 slots (36s) outlasts one heartbeat.
|
|
114
|
+
*/
|
|
115
|
+
export const FATAL_PEER_EXPIRY_SLOTS = 3;
|
|
116
|
+
|
|
105
117
|
type WorkOpts = {
|
|
106
118
|
bypassQueue?: boolean;
|
|
107
119
|
};
|
|
@@ -232,6 +244,7 @@ type SearchUnknownRootTarget =
|
|
|
232
244
|
*/
|
|
233
245
|
export class NetworkProcessor {
|
|
234
246
|
private readonly chain: IBeaconChain;
|
|
247
|
+
private readonly core: INetworkCore;
|
|
235
248
|
private readonly events: NetworkEventBus;
|
|
236
249
|
private readonly logger: Logger;
|
|
237
250
|
private readonly metrics: Metrics | null;
|
|
@@ -256,23 +269,31 @@ export class NetworkProcessor {
|
|
|
256
269
|
() => new MapDef<RootHex, SearchedRootEntry>(() => ({}))
|
|
257
270
|
);
|
|
258
271
|
private bufferedRootsBySlot = new MapDef<Slot, Set<RootHex>>(() => new Set());
|
|
272
|
+
/** peer -> clock slot when it was Fatal'ed */
|
|
273
|
+
private readonly fatalPeers = new Map<PeerIdStr, Slot>();
|
|
259
274
|
|
|
260
275
|
constructor(
|
|
261
276
|
modules: NetworkProcessorModules,
|
|
262
277
|
private readonly opts: NetworkProcessorOpts
|
|
263
278
|
) {
|
|
264
|
-
const {chain, events, logger, metrics} = modules;
|
|
279
|
+
const {chain, core, events, logger, metrics} = modules;
|
|
265
280
|
this.chain = chain;
|
|
281
|
+
this.core = core;
|
|
266
282
|
this.events = events;
|
|
267
283
|
this.metrics = metrics;
|
|
268
284
|
this.logger = logger;
|
|
269
285
|
this.events = events;
|
|
270
286
|
this.gossipQueues = createGossipQueues();
|
|
271
287
|
this.gossipTopicConcurrency = mapValues(this.gossipQueues, () => 0);
|
|
272
|
-
this.gossipValidatorFn = getGossipValidatorFn(
|
|
288
|
+
this.gossipValidatorFn = getGossipValidatorFn(
|
|
289
|
+
modules.gossipHandlers ?? getGossipHandlers(modules, opts),
|
|
290
|
+
modules,
|
|
291
|
+
this.onFatalPeer
|
|
292
|
+
);
|
|
273
293
|
this.gossipValidatorBatchFn = getGossipValidatorBatchFn(
|
|
274
294
|
modules.gossipHandlers ?? getGossipHandlers(modules, opts),
|
|
275
|
-
modules
|
|
295
|
+
modules,
|
|
296
|
+
this.onFatalPeer
|
|
276
297
|
);
|
|
277
298
|
|
|
278
299
|
events.on(NetworkEvent.pendingGossipsubMessage, this.onPendingGossipsubMessage);
|
|
@@ -406,19 +427,47 @@ export class NetworkProcessor {
|
|
|
406
427
|
}
|
|
407
428
|
}
|
|
408
429
|
|
|
430
|
+
private onFatalPeer = (peer: PeerIdStr): void => {
|
|
431
|
+
this.fatalPeers.set(peer, this.chain.clock.currentSlot);
|
|
432
|
+
};
|
|
433
|
+
|
|
409
434
|
private onPendingGossipsubMessage = (message: PendingGossipsubMessage): void => {
|
|
410
435
|
const topicType = message.topic.type;
|
|
436
|
+
if (this.fatalPeers.has(message.propagationSource)) {
|
|
437
|
+
this.metrics?.networkProcessor.gossipValidationError.inc({topic: topicType, error: GossipErrorCode.FATAL_PEER});
|
|
438
|
+
// No need to report the dropped job to gossip. It will be eventually pruned from the mcache
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
|
|
411
442
|
const extractBlockSlotRootFn = this.extractBlockSlotRootFns[topicType];
|
|
443
|
+
if (extractBlockSlotRootFn === undefined) {
|
|
444
|
+
// some messages don't have slot and root
|
|
445
|
+
this.pushPendingGossipsubMessageToQueue(message);
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
412
448
|
|
|
413
449
|
// 1st extract round: make sure slot is in range and if block root is not available
|
|
414
450
|
// proactively search for it + queue the message
|
|
415
|
-
const slotRoot = extractBlockSlotRootFn
|
|
416
|
-
? extractBlockSlotRootFn(message.msg.data, message.topic.boundary.fork)
|
|
417
|
-
: null;
|
|
451
|
+
const slotRoot = extractBlockSlotRootFn(message.msg.data, message.topic.boundary.fork);
|
|
418
452
|
if (slotRoot === null) {
|
|
419
|
-
//
|
|
420
|
-
|
|
421
|
-
|
|
453
|
+
// DOS protection: null means malformed bytes or slot >= 2^32, neither is forwarded by honest peers.
|
|
454
|
+
const {propagationSource, clientAgent, clientVersion} = message;
|
|
455
|
+
const code = GossipErrorCode.INVALID_SLOT;
|
|
456
|
+
const peerAction = rejectPeerAction(topicType, code);
|
|
457
|
+
this.metrics?.networkProcessor.gossipValidationReject.inc({topic: topicType});
|
|
458
|
+
this.core.reportPeer(propagationSource, peerAction, code);
|
|
459
|
+
this.logger.debug(`Gossip validation ${topicType} rejected due to null slot`, {
|
|
460
|
+
peer: propagationSource,
|
|
461
|
+
clientAgent,
|
|
462
|
+
clientVersion,
|
|
463
|
+
peerAction,
|
|
464
|
+
code,
|
|
465
|
+
});
|
|
466
|
+
this.events.emit(NetworkEvent.gossipMessageValidationResult, {
|
|
467
|
+
msgId: message.msgId,
|
|
468
|
+
propagationSource,
|
|
469
|
+
acceptance: TopicValidatorResult.Reject,
|
|
470
|
+
});
|
|
422
471
|
return;
|
|
423
472
|
}
|
|
424
473
|
|
|
@@ -872,6 +921,10 @@ export class NetworkProcessor {
|
|
|
872
921
|
for (const slot of this.bufferedRootsBySlot.keys()) {
|
|
873
922
|
if (slot <= minSlot) this.bufferedRootsBySlot.delete(slot);
|
|
874
923
|
}
|
|
924
|
+
|
|
925
|
+
for (const [peer, slot] of this.fatalPeers) {
|
|
926
|
+
if (clockSlot - slot > FATAL_PEER_EXPIRY_SLOTS) this.fatalPeers.delete(peer);
|
|
927
|
+
}
|
|
875
928
|
};
|
|
876
929
|
|
|
877
930
|
private executeWork(): void {
|
|
@@ -927,6 +980,32 @@ export class NetworkProcessor {
|
|
|
927
980
|
private async processPendingGossipsubMessage(
|
|
928
981
|
messageOrArray: PendingGossipsubMessage | PendingGossipsubMessage[]
|
|
929
982
|
): Promise<void> {
|
|
983
|
+
// drop messages from peers Fatal'ed after these were queued or started awaiting a block/envelope
|
|
984
|
+
// No need to report the dropped jobs to gossip. They will be eventually pruned from the mcache
|
|
985
|
+
if (this.fatalPeers.size > 0) {
|
|
986
|
+
if (Array.isArray(messageOrArray)) {
|
|
987
|
+
const allowed: PendingGossipsubMessage[] = [];
|
|
988
|
+
for (const msg of messageOrArray) {
|
|
989
|
+
if (this.fatalPeers.has(msg.propagationSource)) {
|
|
990
|
+
this.metrics?.networkProcessor.gossipValidationError.inc({
|
|
991
|
+
topic: msg.topic.type,
|
|
992
|
+
error: GossipErrorCode.FATAL_PEER,
|
|
993
|
+
});
|
|
994
|
+
} else {
|
|
995
|
+
allowed.push(msg);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
if (allowed.length === 0) return;
|
|
999
|
+
messageOrArray = allowed;
|
|
1000
|
+
} else if (this.fatalPeers.has(messageOrArray.propagationSource)) {
|
|
1001
|
+
this.metrics?.networkProcessor.gossipValidationError.inc({
|
|
1002
|
+
topic: messageOrArray.topic.type,
|
|
1003
|
+
error: GossipErrorCode.FATAL_PEER,
|
|
1004
|
+
});
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
|
|
930
1009
|
const nowSec = Date.now() / 1000;
|
|
931
1010
|
if (Array.isArray(messageOrArray)) {
|
|
932
1011
|
for (const msg of messageOrArray) {
|
|
@@ -6,6 +6,7 @@ import {toRootHex} from "@lodestar/utils";
|
|
|
6
6
|
import {EnvelopeReconstructionError} from "../../../chain/errors/index.js";
|
|
7
7
|
import {IBeaconChain} from "../../../chain/index.js";
|
|
8
8
|
import {IBeaconDb} from "../../../db/index.js";
|
|
9
|
+
import {MAX_BODIES_PER_REQUEST} from "../../../util/execution.js";
|
|
9
10
|
import {ExecutionPayloadEnvelopesByRootRequest} from "../../../util/types.js";
|
|
10
11
|
import {prettyPrintPeerId} from "../../util.js";
|
|
11
12
|
|
|
@@ -19,10 +20,15 @@ export async function* onExecutionPayloadEnvelopesByRoot(
|
|
|
19
20
|
// The gloas req/resp spec uses MIN_EPOCHS_FOR_BLOCK_REQUESTS to define the minimum range peers MUST serve.
|
|
20
21
|
// Archival nodes may still serve older retained payloads to allow genesis sync.
|
|
21
22
|
|
|
22
|
-
// Resolve slots first so archived
|
|
23
|
+
// Resolve slots first so archived envelopes can be reconstructed in EL batches.
|
|
24
|
+
// Duplicate roots are served once, since each archived envelope costs an EL fetch and a rebuild.
|
|
23
25
|
const requests: {blockSlot: Slot; blockRootHex: RootHex}[] = [];
|
|
26
|
+
const seenRoots = new Set<RootHex>();
|
|
24
27
|
for (const root of requestBody) {
|
|
25
28
|
const rootHex = toRootHex(root);
|
|
29
|
+
if (seenRoots.has(rootHex)) continue;
|
|
30
|
+
seenRoots.add(rootHex);
|
|
31
|
+
|
|
26
32
|
const block = chain.forkChoice.getBlockHexDefaultStatus(rootHex);
|
|
27
33
|
// If the block is not in fork choice, it may be finalized. Attempt to find its slot in block archive
|
|
28
34
|
const slot = block ? block.slot : await db.blockArchive.getSlotByRoot(root);
|
|
@@ -41,35 +47,43 @@ export async function* onExecutionPayloadEnvelopesByRoot(
|
|
|
41
47
|
requests.push({blockSlot: slot, blockRootHex: rootHex});
|
|
42
48
|
}
|
|
43
49
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
)
|
|
50
|
+
// Yield between EL batches so the first response does not wait for the entire request.
|
|
51
|
+
let yielded = 0;
|
|
52
|
+
for (let i = 0; i < requests.length; i += MAX_BODIES_PER_REQUEST) {
|
|
53
|
+
const batch = requests.slice(i, i + MAX_BODIES_PER_REQUEST);
|
|
54
|
+
let envelopesBytes: (Uint8Array | null)[];
|
|
55
|
+
try {
|
|
56
|
+
// By-root allows omission, so a mismatched envelope is left out rather than failing the response.
|
|
57
|
+
envelopesBytes = await chain.getSerializedExecutionPayloadEnvelopes(batch, "omit");
|
|
58
|
+
} catch (e) {
|
|
59
|
+
if (e instanceof EnvelopeReconstructionError) {
|
|
60
|
+
// By-root permits partial responses, so an EL failure can end the response here.
|
|
61
|
+
if (yielded > 0) return;
|
|
62
|
+
throw new ResponseError(
|
|
63
|
+
RespStatus.RESOURCE_UNAVAILABLE,
|
|
64
|
+
`Failed to reconstruct archived envelopes: ${e.message}`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
throw e;
|
|
54
68
|
}
|
|
55
|
-
throw e;
|
|
56
|
-
}
|
|
57
69
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
for (let j = 0; j < batch.length; j++) {
|
|
71
|
+
const {blockSlot, blockRootHex} = batch[j];
|
|
72
|
+
const envelopeBytes = envelopesBytes[j];
|
|
73
|
+
if (envelopeBytes) {
|
|
74
|
+
yielded++;
|
|
75
|
+
yield {
|
|
76
|
+
data: envelopeBytes,
|
|
77
|
+
boundary: chain.config.getForkBoundaryAtEpoch(computeEpochAtSlot(blockSlot)),
|
|
78
|
+
};
|
|
79
|
+
} else {
|
|
80
|
+
chain.logger.debug("Cannot serve ExecutionPayloadEnvelopesByRoot: envelope not found", {
|
|
81
|
+
slot: blockSlot,
|
|
82
|
+
root: blockRootHex,
|
|
83
|
+
peer: prettyPrintPeerId(peerId),
|
|
84
|
+
client: peerClient,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
73
87
|
}
|
|
74
88
|
}
|
|
75
89
|
}
|
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
|
@@ -15,7 +15,7 @@ import {ChainEvent, ChainEventData, IBeaconChain} from "../chain/index.js";
|
|
|
15
15
|
import {validateGloasBlockDataColumnSidecars} from "../chain/validation/dataColumnSidecar.js";
|
|
16
16
|
import {validateGossipExecutionPayloadEnvelope} from "../chain/validation/executionPayloadEnvelope.js";
|
|
17
17
|
import {Metrics} from "../metrics/index.js";
|
|
18
|
-
import {INetwork, NetworkEvent, NetworkEventData, prettyPrintPeerIdStr} from "../network/index.js";
|
|
18
|
+
import {INetwork, NetworkEvent, NetworkEventData, PeerAction, prettyPrintPeerIdStr} from "../network/index.js";
|
|
19
19
|
import {PeerSyncMeta} from "../network/peers/peersData.js";
|
|
20
20
|
import {MAX_PEERS_PER_ROOT} from "../network/processor/constants.js";
|
|
21
21
|
import {ClockEvent} from "../util/clock.js";
|
|
@@ -442,6 +442,11 @@ export class BlockInputSync {
|
|
|
442
442
|
};
|
|
443
443
|
|
|
444
444
|
private addByRootHex = (rootHex: RootHex, peerIdStr?: PeerIdStr): boolean => {
|
|
445
|
+
if (this.knownBadBlocks.has(rootHex)) {
|
|
446
|
+
this.logger.debug("Ignoring known bad block root", {root: rootHex, peerIdStr: peerIdStr ?? "unknown peer"});
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
|
|
445
450
|
let pendingBlock = this.pendingBlocks.get(rootHex);
|
|
446
451
|
let added = false;
|
|
447
452
|
if (!pendingBlock) {
|
|
@@ -477,6 +482,11 @@ export class BlockInputSync {
|
|
|
477
482
|
};
|
|
478
483
|
|
|
479
484
|
private addByBlockInput = (blockInput: IBlockInput, peerIdStr?: string): void => {
|
|
485
|
+
if (this.knownBadBlocks.has(blockInput.blockRootHex)) {
|
|
486
|
+
this.logger.debug("Ignoring known bad block", {slot: blockInput.slot, root: blockInput.blockRootHex, peerIdStr});
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
|
|
480
490
|
let pendingBlock = this.pendingBlocks.get(blockInput.blockRootHex);
|
|
481
491
|
// if entry is missing or was added via rootHex and now we have more complete information overwrite
|
|
482
492
|
// the existing information with the more complete cache entry
|
|
@@ -513,6 +523,15 @@ export class BlockInputSync {
|
|
|
513
523
|
};
|
|
514
524
|
|
|
515
525
|
private addByPayloadRootHex = (rootHex: RootHex, peerIdStr?: PeerIdStr, slot?: Slot): boolean => {
|
|
526
|
+
if (this.knownBadBlocks.has(rootHex)) {
|
|
527
|
+
this.logger.debug("Ignoring payload for known bad block root", {
|
|
528
|
+
slot: slot ?? "unknown",
|
|
529
|
+
root: rootHex,
|
|
530
|
+
peerIdStr,
|
|
531
|
+
});
|
|
532
|
+
return false;
|
|
533
|
+
}
|
|
534
|
+
|
|
516
535
|
let pendingPayload = this.pendingPayloads.get(rootHex);
|
|
517
536
|
let added = false;
|
|
518
537
|
if (!pendingPayload) {
|
|
@@ -581,6 +600,15 @@ export class BlockInputSync {
|
|
|
581
600
|
peerIdStr?: PeerIdStr,
|
|
582
601
|
envelope?: gloas.SignedExecutionPayloadEnvelope
|
|
583
602
|
): void => {
|
|
603
|
+
if (this.knownBadBlocks.has(payloadInput.blockRootHex)) {
|
|
604
|
+
this.logger.debug("Ignoring payload input for known bad block root", {
|
|
605
|
+
slot: payloadInput.slot,
|
|
606
|
+
root: payloadInput.blockRootHex,
|
|
607
|
+
peerIdStr,
|
|
608
|
+
});
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
|
|
584
612
|
const pendingPayload = this.toPendingPayloadInput(
|
|
585
613
|
payloadInput,
|
|
586
614
|
this.pendingPayloads.get(payloadInput.blockRootHex),
|
|
@@ -654,6 +682,7 @@ export class BlockInputSync {
|
|
|
654
682
|
return {kind: "ready"};
|
|
655
683
|
}
|
|
656
684
|
|
|
685
|
+
// The parent's payload is known but does not match `parentBlockHashHex`
|
|
657
686
|
if (this.chain.forkChoice.hasPayloadHexUnsafe(parentRootHex)) {
|
|
658
687
|
return {kind: "invalidParentPayload", parentRootHex, parentBlockHashHex};
|
|
659
688
|
}
|
|
@@ -1032,7 +1061,8 @@ export class BlockInputSync {
|
|
|
1032
1061
|
}
|
|
1033
1062
|
} else if (blockSlot <= finalizedSlot) {
|
|
1034
1063
|
// the common ancestor of the downloading chain and canonical chain should be at least the finalized slot and
|
|
1035
|
-
// we should found it through forkchoice. If not,
|
|
1064
|
+
// we should found it through forkchoice. If not, drop the chain without penalizing peers because pending entries are
|
|
1065
|
+
// block retained on failed download, so finalization can pass the block while it is still pending
|
|
1036
1066
|
// 0 - 1 - ... - n - finalizedSlot
|
|
1037
1067
|
// \
|
|
1038
1068
|
// parent 1 - parent 2 - ... - unknownParent block
|
|
@@ -1040,7 +1070,7 @@ export class BlockInputSync {
|
|
|
1040
1070
|
...logCtx2,
|
|
1041
1071
|
finalizedSlot,
|
|
1042
1072
|
});
|
|
1043
|
-
this.
|
|
1073
|
+
this.removeAllDescendants(block, DroppedItemReason.belowFinalized);
|
|
1044
1074
|
} else {
|
|
1045
1075
|
this.onUnknownBlockRoot({rootHex: pending.blockInput.parentRootHex, source: BlockInputSource.byRoot});
|
|
1046
1076
|
}
|
|
@@ -1172,15 +1202,18 @@ export class BlockInputSync {
|
|
|
1172
1202
|
break;
|
|
1173
1203
|
|
|
1174
1204
|
case BlockErrorCode.EXECUTION_ENGINE_INVALID:
|
|
1175
|
-
//
|
|
1205
|
+
// Ban but do not downscore: optimistic-sync peers may forward and serve blocks whose payload is later
|
|
1206
|
+
// rejected by the execution engine. Per the spec, an INVALID execution payload SHOULD NOT cause a peer
|
|
1207
|
+
// to be down-scored or disconnected, only consensus-layer invalidity MAY.
|
|
1208
|
+
// https://github.com/ethereum/consensus-specs/blob/master/specs/bellatrix/p2p-interface.md#the-reqresp-domain
|
|
1176
1209
|
this.logger.debug("Execution engine rejected block from unknown parent sync", errorData, res.err);
|
|
1177
|
-
this.
|
|
1210
|
+
this.removeAndBanAllDescendants(pendingBlock, DroppedItemReason.elInvalid);
|
|
1178
1211
|
break;
|
|
1179
1212
|
|
|
1180
1213
|
default:
|
|
1181
1214
|
// Block is not correct with respect to our chain. Log error loudly
|
|
1182
1215
|
this.logger.debug("Error processing block from unknown parent sync", errorData, res.err);
|
|
1183
|
-
this.
|
|
1216
|
+
this.removeAllDescendants(pendingBlock, DroppedItemReason.invalidBlock);
|
|
1184
1217
|
}
|
|
1185
1218
|
}
|
|
1186
1219
|
|
|
@@ -1393,20 +1426,23 @@ export class BlockInputSync {
|
|
|
1393
1426
|
pendingPayload.status = PendingPayloadInputStatus.downloaded;
|
|
1394
1427
|
break;
|
|
1395
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
|
|
1396
1431
|
case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
|
|
1397
1432
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1433
|
+
this.chain.seenPayloadEnvelopeInputCache.removeInvalid(pendingPayload.payloadInput);
|
|
1398
1434
|
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.elInvalid);
|
|
1399
1435
|
break;
|
|
1400
1436
|
|
|
1401
1437
|
case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
|
|
1402
1438
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1439
|
+
this.chain.seenPayloadEnvelopeInputCache.removeInvalid(pendingPayload.payloadInput);
|
|
1403
1440
|
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidEnvelope);
|
|
1404
1441
|
break;
|
|
1405
1442
|
|
|
1406
1443
|
case PayloadErrorCode.INVALID_SIGNATURE:
|
|
1407
|
-
// TODO GLOAS: Decide how invalid payload inputs should eventually leave memory without
|
|
1408
|
-
// reintroducing envelope replacement / recreation flows.
|
|
1409
1444
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1445
|
+
this.chain.seenPayloadEnvelopeInputCache.removeInvalid(pendingPayload.payloadInput);
|
|
1410
1446
|
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidSignature);
|
|
1411
1447
|
break;
|
|
1412
1448
|
|
|
@@ -1478,11 +1514,11 @@ export class BlockInputSync {
|
|
|
1478
1514
|
}
|
|
1479
1515
|
|
|
1480
1516
|
const {peerId, client: peerClient} = peerMeta;
|
|
1481
|
-
cacheItem.peerIdStrings.add(peerId);
|
|
1482
1517
|
|
|
1483
1518
|
try {
|
|
1484
1519
|
if (!envelope) {
|
|
1485
1520
|
envelope = await this.fetchExecutionPayloadEnvelope(peerId, blockRoot, rootHex);
|
|
1521
|
+
cacheItem.peerIdStrings.add(peerId);
|
|
1486
1522
|
const slotWasUnknown = typeof slot !== "number";
|
|
1487
1523
|
slot = envelope.message.payload.slotNumber;
|
|
1488
1524
|
if (slotWasUnknown) {
|
|
@@ -1524,6 +1560,8 @@ export class BlockInputSync {
|
|
|
1524
1560
|
const missing = pendingPayload.payloadInput.getMissingSampledColumnMeta().missing;
|
|
1525
1561
|
if (missing.length > 0) {
|
|
1526
1562
|
const columnSidecars = await this.fetchPayloadColumns(peerMeta, pendingPayload.payloadInput, missing);
|
|
1563
|
+
// toPendingPayloadInput copied cacheItem's set, so add to the object that is carried forward
|
|
1564
|
+
pendingPayload.peerIdStrings.add(peerId);
|
|
1527
1565
|
const seenTimestampSec = Date.now() / 1000;
|
|
1528
1566
|
for (const columnSidecar of columnSidecars) {
|
|
1529
1567
|
if (pendingPayload.payloadInput.hasColumn(columnSidecar.index)) {
|
|
@@ -1716,8 +1754,6 @@ export class BlockInputSync {
|
|
|
1716
1754
|
}
|
|
1717
1755
|
const {peerId, client: peerClient} = peerMeta;
|
|
1718
1756
|
|
|
1719
|
-
cacheItem.peerIdStrings.add(peerId);
|
|
1720
|
-
|
|
1721
1757
|
try {
|
|
1722
1758
|
const downloadResult = await downloadByRoot({
|
|
1723
1759
|
config: this.config,
|
|
@@ -1728,6 +1764,7 @@ export class BlockInputSync {
|
|
|
1728
1764
|
cacheItem,
|
|
1729
1765
|
});
|
|
1730
1766
|
cacheItem = downloadResult.result;
|
|
1767
|
+
cacheItem.peerIdStrings.add(peerId);
|
|
1731
1768
|
if (slot === undefined) {
|
|
1732
1769
|
slot = cacheItem.blockInput.slot;
|
|
1733
1770
|
// we were not able to observe the time into slot when starting the fetch, do it now
|
|
@@ -1843,44 +1880,8 @@ export class BlockInputSync {
|
|
|
1843
1880
|
throw Error(message);
|
|
1844
1881
|
}
|
|
1845
1882
|
|
|
1846
|
-
/**
|
|
1847
|
-
* Gets all descendant blocks of `block` recursively from `pendingBlocks`.
|
|
1848
|
-
* Assumes that if a parent block does not exist or is not processable, all descendant blocks are bad too.
|
|
1849
|
-
* Downscore all peers that have referenced any of this bad blocks. May report peers multiple times if they have
|
|
1850
|
-
* referenced more than one bad block.
|
|
1851
|
-
*/
|
|
1852
|
-
private removeAndDownScoreAllDescendants(block: BlockInputSyncCacheItem, headReason: DroppedItemReason): void {
|
|
1853
|
-
// Get all blocks that are a descendant of this one
|
|
1854
|
-
const badPendingBlocks = this.removeAllDescendants(block, headReason, DroppedItemReason.invalidParent);
|
|
1855
|
-
// just console log and do not penalize on pending/bad blocks for debugging
|
|
1856
|
-
// console.log("removeAndDownscoreAllDescendants", {block});
|
|
1857
|
-
|
|
1858
|
-
for (const block of badPendingBlocks) {
|
|
1859
|
-
//
|
|
1860
|
-
// TODO(fulu): why is this commented out here?
|
|
1861
|
-
//
|
|
1862
|
-
// this.knownBadBlocks.add(block.blockRootHex);
|
|
1863
|
-
// for (const peerIdStr of block.peerIdStrings) {
|
|
1864
|
-
// // TODO: Refactor peerRpcScores to work with peerIdStr only
|
|
1865
|
-
// this.network.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadBlockByRoot");
|
|
1866
|
-
// }
|
|
1867
|
-
this.logger.debug("ignored Banning unknown block", {
|
|
1868
|
-
slot: getBlockInputSyncCacheItemSlot(block),
|
|
1869
|
-
root: getBlockInputSyncCacheItemRootHex(block),
|
|
1870
|
-
peerIdStrings: Array.from(block.peerIdStrings)
|
|
1871
|
-
.map((id) => prettyPrintPeerIdStr(id))
|
|
1872
|
-
.join(","),
|
|
1873
|
-
});
|
|
1874
|
-
}
|
|
1875
|
-
|
|
1876
|
-
// Prune knownBadBlocks
|
|
1877
|
-
pruneSetToMax(this.knownBadBlocks, MAX_KNOWN_BAD_BLOCKS);
|
|
1878
|
-
}
|
|
1879
|
-
|
|
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
|
}
|
|
@@ -1903,13 +1904,61 @@ export class BlockInputSync {
|
|
|
1903
1904
|
}
|
|
1904
1905
|
}
|
|
1905
1906
|
|
|
1907
|
+
/**
|
|
1908
|
+
* Same as `removeAndBanAllDescendants`, and downscores every peer that referenced or served a banned item, once per
|
|
1909
|
+
* peer. Only call for consensus-layer invalidity, never for an INVALID execution payload:
|
|
1910
|
+
* https://github.com/ethereum/consensus-specs/blob/master/specs/bellatrix/p2p-interface.md#the-reqresp-domain
|
|
1911
|
+
*/
|
|
1912
|
+
private removeAndDownScoreAllDescendants(block: BlockInputSyncCacheItem, headReason: DroppedItemReason): void {
|
|
1913
|
+
const peersToReport = new Set<PeerIdStr>();
|
|
1914
|
+
for (const bannedBlock of this.removeAndBanAllDescendants(block, headReason)) {
|
|
1915
|
+
for (const peerIdStr of bannedBlock.peerIdStrings) {
|
|
1916
|
+
peersToReport.add(peerIdStr);
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
for (const peerIdStr of peersToReport) {
|
|
1921
|
+
this.network.reportPeer(peerIdStr, PeerAction.LowToleranceError, headReason);
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
/**
|
|
1926
|
+
* Removes `block` and all its pending descendants and bans their roots in `knownBadBlocks`. Only call when the chain
|
|
1927
|
+
* is provably bad: a banned root is never searched for again. Returns the banned items.
|
|
1928
|
+
*/
|
|
1929
|
+
private removeAndBanAllDescendants(
|
|
1930
|
+
block: BlockInputSyncCacheItem,
|
|
1931
|
+
headReason: DroppedItemReason
|
|
1932
|
+
): BlockInputSyncCacheItem[] {
|
|
1933
|
+
const bannedBlocks: BlockInputSyncCacheItem[] = [];
|
|
1934
|
+
for (const badBlock of this.removeAllDescendants(block, headReason, DroppedItemReason.invalidParent)) {
|
|
1935
|
+
const rootHex = getBlockInputSyncCacheItemRootHex(badBlock);
|
|
1936
|
+
// a gossip import may race a failing sync path, never ban a root fork choice has imported
|
|
1937
|
+
if (this.chain.forkChoice.hasBlockHex(rootHex)) {
|
|
1938
|
+
continue;
|
|
1939
|
+
}
|
|
1940
|
+
this.knownBadBlocks.add(rootHex);
|
|
1941
|
+
bannedBlocks.push(badBlock);
|
|
1942
|
+
this.logger.debug("Banning unknown block", {
|
|
1943
|
+
slot: getBlockInputSyncCacheItemSlot(badBlock),
|
|
1944
|
+
root: rootHex,
|
|
1945
|
+
reason: headReason,
|
|
1946
|
+
peerIdStrings: Array.from(badBlock.peerIdStrings)
|
|
1947
|
+
.map((id) => prettyPrintPeerIdStr(id))
|
|
1948
|
+
.join(","),
|
|
1949
|
+
});
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
pruneSetToMax(this.knownBadBlocks, MAX_KNOWN_BAD_BLOCKS);
|
|
1953
|
+
return bannedBlocks;
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1906
1956
|
private removeAllDescendants(
|
|
1907
1957
|
block: BlockInputSyncCacheItem,
|
|
1908
1958
|
headReason: DroppedItemReason,
|
|
1909
1959
|
descendantReason: DroppedItemReason = DroppedItemReason.invalidParent
|
|
1910
1960
|
): BlockInputSyncCacheItem[] {
|
|
1911
1961
|
const rootHex = getBlockInputSyncCacheItemRootHex(block);
|
|
1912
|
-
const slot = getBlockInputSyncCacheItemSlot(block);
|
|
1913
1962
|
// Get all blocks that are a descendant of this one (index 0 is the head item itself)
|
|
1914
1963
|
const badPendingBlocks = [block, ...getAllDescendantBlocks(rootHex, this.pendingBlocks)];
|
|
1915
1964
|
|
|
@@ -1926,8 +1975,8 @@ export class BlockInputSync {
|
|
|
1926
1975
|
// Keep PayloadEnvelopeInput resident in the seen cache for consistency with the
|
|
1927
1976
|
// importBlock()-owned lifecycle.
|
|
1928
1977
|
this.logger.debug("Removing bad/unknown/incomplete BlockInputSyncCacheItem", {
|
|
1929
|
-
slot,
|
|
1930
|
-
|
|
1978
|
+
slot: getBlockInputSyncCacheItemSlot(block),
|
|
1979
|
+
root: rootHex,
|
|
1931
1980
|
});
|
|
1932
1981
|
}
|
|
1933
1982
|
|
package/src/util/execution.ts
CHANGED
|
@@ -260,7 +260,7 @@ export async function getDataColumnSidecarsFromExecution(
|
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
/** engine_getPayloadBodiesByHashV2: every EL must accept requests of up to 32 hashes, larger ones may fail with -38004 */
|
|
263
|
-
const MAX_BODIES_PER_REQUEST = 32;
|
|
263
|
+
export const MAX_BODIES_PER_REQUEST = 32;
|
|
264
264
|
|
|
265
265
|
type SlotEnvelopeBytes = {slot: Slot; envelopeBytes: Uint8Array};
|
|
266
266
|
|