@lodestar/beacon-node 1.50.0-dev.70314c9caa → 1.50.0-dev.7df32d9c25
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 +0 -4
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +1 -2
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/errors/gossipValidation.d.ts +1 -3
- package/lib/chain/errors/gossipValidation.d.ts.map +1 -1
- package/lib/chain/errors/gossipValidation.js +0 -2
- package/lib/chain/errors/gossipValidation.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +2 -5
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +1 -3
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +2 -6
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +0 -6
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +0 -10
- 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 +0 -3
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/gossipValidatorFn.d.ts +3 -7
- package/lib/network/processor/gossipValidatorFn.d.ts.map +1 -1
- package/lib/network/processor/gossipValidatorFn.js +3 -7
- package/lib/network/processor/gossipValidatorFn.js.map +1 -1
- package/lib/network/processor/index.d.ts +0 -9
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +10 -77
- 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 +18 -50
- package/lib/sync/range/range.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +4 -5
- package/lib/sync/unknownBlock.js.map +1 -1
- package/package.json +15 -15
- package/src/chain/blocks/importBlock.ts +0 -4
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +1 -2
- package/src/chain/errors/gossipValidation.ts +0 -2
- package/src/chain/prepareNextSlot.ts +3 -6
- package/src/chain/produceBlock/produceBlockBody.ts +1 -8
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +0 -11
- package/src/network/processor/gossipHandlers.ts +0 -3
- package/src/network/processor/gossipValidatorFn.ts +3 -13
- package/src/network/processor/index.ts +10 -89
- package/src/sync/range/range.ts +18 -50
- package/src/sync/unknownBlock.ts +4 -5
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
getParentRootFromSignedBeaconBlockSerialized,
|
|
23
23
|
getPayloadPresentFromPayloadAttestationMessageSerialized,
|
|
24
24
|
} from "../../util/sszBytes.js";
|
|
25
|
-
import {INetworkCore} from "../core/index.js";
|
|
26
25
|
import {NetworkEvent, NetworkEventBus} from "../events.js";
|
|
27
26
|
import {
|
|
28
27
|
GossipHandlers,
|
|
@@ -35,12 +34,7 @@ import {MAX_PEERS_PER_ROOT} from "./constants.js";
|
|
|
35
34
|
import {createExtractBlockSlotRootFns} from "./extractSlotRootFns.js";
|
|
36
35
|
import {GossipHandlerOpts, ValidatorFnsModules, getGossipHandlers} from "./gossipHandlers.js";
|
|
37
36
|
import {createGossipQueues} from "./gossipQueues/index.js";
|
|
38
|
-
import {
|
|
39
|
-
ValidatorFnModules,
|
|
40
|
-
getGossipValidatorBatchFn,
|
|
41
|
-
getGossipValidatorFn,
|
|
42
|
-
rejectPeerAction,
|
|
43
|
-
} from "./gossipValidatorFn.js";
|
|
37
|
+
import {ValidatorFnModules, getGossipValidatorBatchFn, getGossipValidatorFn} from "./gossipValidatorFn.js";
|
|
44
38
|
import {PendingGossipsubMessage} from "./types.js";
|
|
45
39
|
|
|
46
40
|
export * from "./types.js";
|
|
@@ -108,12 +102,6 @@ type SearchedRootEntry = {
|
|
|
108
102
|
*/
|
|
109
103
|
const DEFAULT_EARLIEST_PERMISSIBLE_SLOT_DISTANCE = 32;
|
|
110
104
|
|
|
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
|
-
|
|
117
105
|
type WorkOpts = {
|
|
118
106
|
bypassQueue?: boolean;
|
|
119
107
|
};
|
|
@@ -244,7 +232,6 @@ type SearchUnknownRootTarget =
|
|
|
244
232
|
*/
|
|
245
233
|
export class NetworkProcessor {
|
|
246
234
|
private readonly chain: IBeaconChain;
|
|
247
|
-
private readonly core: INetworkCore;
|
|
248
235
|
private readonly events: NetworkEventBus;
|
|
249
236
|
private readonly logger: Logger;
|
|
250
237
|
private readonly metrics: Metrics | null;
|
|
@@ -269,31 +256,23 @@ export class NetworkProcessor {
|
|
|
269
256
|
() => new MapDef<RootHex, SearchedRootEntry>(() => ({}))
|
|
270
257
|
);
|
|
271
258
|
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>();
|
|
274
259
|
|
|
275
260
|
constructor(
|
|
276
261
|
modules: NetworkProcessorModules,
|
|
277
262
|
private readonly opts: NetworkProcessorOpts
|
|
278
263
|
) {
|
|
279
|
-
const {chain,
|
|
264
|
+
const {chain, events, logger, metrics} = modules;
|
|
280
265
|
this.chain = chain;
|
|
281
|
-
this.core = core;
|
|
282
266
|
this.events = events;
|
|
283
267
|
this.metrics = metrics;
|
|
284
268
|
this.logger = logger;
|
|
285
269
|
this.events = events;
|
|
286
270
|
this.gossipQueues = createGossipQueues();
|
|
287
271
|
this.gossipTopicConcurrency = mapValues(this.gossipQueues, () => 0);
|
|
288
|
-
this.gossipValidatorFn = getGossipValidatorFn(
|
|
289
|
-
modules.gossipHandlers ?? getGossipHandlers(modules, opts),
|
|
290
|
-
modules,
|
|
291
|
-
this.onFatalPeer
|
|
292
|
-
);
|
|
272
|
+
this.gossipValidatorFn = getGossipValidatorFn(modules.gossipHandlers ?? getGossipHandlers(modules, opts), modules);
|
|
293
273
|
this.gossipValidatorBatchFn = getGossipValidatorBatchFn(
|
|
294
274
|
modules.gossipHandlers ?? getGossipHandlers(modules, opts),
|
|
295
|
-
modules
|
|
296
|
-
this.onFatalPeer
|
|
275
|
+
modules
|
|
297
276
|
);
|
|
298
277
|
|
|
299
278
|
events.on(NetworkEvent.pendingGossipsubMessage, this.onPendingGossipsubMessage);
|
|
@@ -427,47 +406,19 @@ export class NetworkProcessor {
|
|
|
427
406
|
}
|
|
428
407
|
}
|
|
429
408
|
|
|
430
|
-
private onFatalPeer = (peer: PeerIdStr): void => {
|
|
431
|
-
this.fatalPeers.set(peer, this.chain.clock.currentSlot);
|
|
432
|
-
};
|
|
433
|
-
|
|
434
409
|
private onPendingGossipsubMessage = (message: PendingGossipsubMessage): void => {
|
|
435
410
|
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
|
-
|
|
442
411
|
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
|
-
}
|
|
448
412
|
|
|
449
413
|
// 1st extract round: make sure slot is in range and if block root is not available
|
|
450
414
|
// proactively search for it + queue the message
|
|
451
|
-
const slotRoot = extractBlockSlotRootFn
|
|
415
|
+
const slotRoot = extractBlockSlotRootFn
|
|
416
|
+
? extractBlockSlotRootFn(message.msg.data, message.topic.boundary.fork)
|
|
417
|
+
: null;
|
|
452
418
|
if (slotRoot === null) {
|
|
453
|
-
//
|
|
454
|
-
|
|
455
|
-
|
|
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
|
-
});
|
|
419
|
+
// some messages don't have slot and root
|
|
420
|
+
// if the msg.data is invalid, message will be rejected when deserializing data in later phase (gossipValidatorFn)
|
|
421
|
+
this.pushPendingGossipsubMessageToQueue(message);
|
|
471
422
|
return;
|
|
472
423
|
}
|
|
473
424
|
|
|
@@ -921,10 +872,6 @@ export class NetworkProcessor {
|
|
|
921
872
|
for (const slot of this.bufferedRootsBySlot.keys()) {
|
|
922
873
|
if (slot <= minSlot) this.bufferedRootsBySlot.delete(slot);
|
|
923
874
|
}
|
|
924
|
-
|
|
925
|
-
for (const [peer, slot] of this.fatalPeers) {
|
|
926
|
-
if (clockSlot - slot > FATAL_PEER_EXPIRY_SLOTS) this.fatalPeers.delete(peer);
|
|
927
|
-
}
|
|
928
875
|
};
|
|
929
876
|
|
|
930
877
|
private executeWork(): void {
|
|
@@ -980,32 +927,6 @@ export class NetworkProcessor {
|
|
|
980
927
|
private async processPendingGossipsubMessage(
|
|
981
928
|
messageOrArray: PendingGossipsubMessage | PendingGossipsubMessage[]
|
|
982
929
|
): 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
|
-
|
|
1009
930
|
const nowSec = Date.now() / 1000;
|
|
1010
931
|
if (Array.isArray(messageOrArray)) {
|
|
1011
932
|
for (const msg of messageOrArray) {
|
package/src/sync/range/range.ts
CHANGED
|
@@ -5,7 +5,6 @@ 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";
|
|
9
8
|
import {AttestationImportOpt, ImportBlockOpts} from "../../chain/blocks/index.js";
|
|
10
9
|
import {assertLinearChainSegment} from "../../chain/blocks/utils/chainSegment.js";
|
|
11
10
|
import {BlockError} from "../../chain/errors/index.js";
|
|
@@ -208,50 +207,22 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
|
|
|
208
207
|
blsVerifyOnMainThread: false,
|
|
209
208
|
};
|
|
210
209
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
});
|
|
210
|
+
if (this.opts?.disableProcessAsChainSegment) {
|
|
211
|
+
// Should only be used for debugging or testing
|
|
212
|
+
for (const block of blocks) {
|
|
213
|
+
await this.chain.processBlock(block, flags);
|
|
214
|
+
const payloadEnvelope = payloadEnvelopes?.get(block.slot);
|
|
215
|
+
if (payloadEnvelope?.hasPayloadEnvelope()) {
|
|
216
|
+
await this.chain.processExecutionPayload(payloadEnvelope);
|
|
244
217
|
}
|
|
245
218
|
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const {payloadInput} = e;
|
|
254
|
-
const {peerIdStr, source} = payloadInput.getPayloadEnvelopeSource();
|
|
219
|
+
} else {
|
|
220
|
+
const {orphaned, skipped} = await this.chain.processChainSegment(blocks, payloadEnvelopes, flags);
|
|
221
|
+
// We log orphaned payloads to work with different clients
|
|
222
|
+
// in the future, consider applying penalties in certain conditions
|
|
223
|
+
// make sure the payload source was from range sync in that case
|
|
224
|
+
for (const {slot, payloadEnvelopeInput} of orphaned) {
|
|
225
|
+
const {peerIdStr, source} = payloadEnvelopeInput.getPayloadEnvelopeSource();
|
|
255
226
|
let client = "unknown";
|
|
256
227
|
if (peerIdStr !== undefined) {
|
|
257
228
|
try {
|
|
@@ -260,18 +231,15 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
|
|
|
260
231
|
// peer disconnected since serving the envelope, keep "unknown"
|
|
261
232
|
}
|
|
262
233
|
}
|
|
263
|
-
this.logger.debug("
|
|
264
|
-
slot
|
|
265
|
-
root:
|
|
234
|
+
this.logger.debug("Orphaned payload envelope in range sync batch", {
|
|
235
|
+
slot,
|
|
236
|
+
root: payloadEnvelopeInput.blockRootHex,
|
|
266
237
|
peer: peerIdStr ?? "unknown",
|
|
267
238
|
source,
|
|
268
239
|
client,
|
|
269
|
-
|
|
240
|
+
skipped,
|
|
270
241
|
});
|
|
271
|
-
// The batch is downloaded again, it would otherwise reuse the cached envelope and fail the same way
|
|
272
|
-
this.chain.seenPayloadEnvelopeInputCache.removeInvalid(payloadInput);
|
|
273
242
|
}
|
|
274
|
-
throw e;
|
|
275
243
|
}
|
|
276
244
|
};
|
|
277
245
|
|
package/src/sync/unknownBlock.ts
CHANGED
|
@@ -1426,23 +1426,20 @@ 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
|
|
1431
1429
|
case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
|
|
1432
1430
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1433
|
-
this.chain.seenPayloadEnvelopeInputCache.removeInvalid(pendingPayload.payloadInput);
|
|
1434
1431
|
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.elInvalid);
|
|
1435
1432
|
break;
|
|
1436
1433
|
|
|
1437
1434
|
case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
|
|
1438
1435
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1439
|
-
this.chain.seenPayloadEnvelopeInputCache.removeInvalid(pendingPayload.payloadInput);
|
|
1440
1436
|
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidEnvelope);
|
|
1441
1437
|
break;
|
|
1442
1438
|
|
|
1443
1439
|
case PayloadErrorCode.INVALID_SIGNATURE:
|
|
1440
|
+
// TODO GLOAS: Decide how invalid payload inputs should eventually leave memory without
|
|
1441
|
+
// reintroducing envelope replacement / recreation flows.
|
|
1444
1442
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1445
|
-
this.chain.seenPayloadEnvelopeInputCache.removeInvalid(pendingPayload.payloadInput);
|
|
1446
1443
|
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidSignature);
|
|
1447
1444
|
break;
|
|
1448
1445
|
|
|
@@ -1882,6 +1879,8 @@ export class BlockInputSync {
|
|
|
1882
1879
|
|
|
1883
1880
|
// Once a parent payload is invalid, every descendant waiting on that payload lineage becomes unrecoverable too.
|
|
1884
1881
|
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.
|
|
1885
1884
|
if (this.pendingPayloads.delete(rootHex)) {
|
|
1886
1885
|
this.metrics?.blockInputSync.removedPayloads.inc({reason: headReason}, 1);
|
|
1887
1886
|
}
|