@lodestar/beacon-node 1.48.0-dev.f22c5ce63e → 1.48.0-dev.f63ac74039
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/blocks/index.js +4 -11
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/utils.js +14 -0
- package/lib/api/impl/beacon/state/utils.js.map +1 -1
- package/lib/api/impl/config/index.d.ts.map +1 -1
- package/lib/api/impl/config/index.js +4 -1
- package/lib/api/impl/config/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +39 -39
- package/lib/api/impl/validator/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 +5 -1
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.js +1 -0
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/metrics.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/metrics.js +12 -1
- package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/types.d.ts +2 -0
- package/lib/chain/archiveStore/historicalState/types.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/types.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/worker.js +4 -1
- package/lib/chain/archiveStore/historicalState/worker.js.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts +9 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.js +68 -38
- package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts +5 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +12 -18
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/index.d.ts +8 -4
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +83 -17
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/types.d.ts +8 -0
- package/lib/chain/blocks/types.d.ts.map +1 -1
- package/lib/chain/blocks/types.js.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.d.ts +1 -1
- package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.js +26 -11
- package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
- package/lib/chain/blocks/writeBlockInputToDb.d.ts.map +1 -1
- package/lib/chain/blocks/writeBlockInputToDb.js +9 -16
- package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
- package/lib/chain/chain.d.ts +11 -5
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +129 -75
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +1 -1
- package/lib/chain/emitter.js +1 -1
- package/lib/chain/errors/blockError.d.ts +14 -9
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +0 -2
- package/lib/chain/errors/blockError.js.map +1 -1
- package/lib/chain/errors/dataColumnSidecarError.d.ts +15 -2
- package/lib/chain/errors/dataColumnSidecarError.d.ts.map +1 -1
- package/lib/chain/errors/dataColumnSidecarError.js.map +1 -1
- package/lib/chain/errors/executionPayloadBid.d.ts +4 -0
- package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadBid.js +1 -0
- package/lib/chain/errors/executionPayloadBid.js.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.d.ts +10 -11
- package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.js +0 -2
- package/lib/chain/errors/executionPayloadEnvelope.js.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/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +2 -0
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/interface.d.ts +4 -4
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/lightClient/index.d.ts +3 -3
- package/lib/chain/lightClient/index.d.ts.map +1 -1
- package/lib/chain/lightClient/index.js +21 -23
- package/lib/chain/lightClient/index.js.map +1 -1
- package/lib/chain/lightClient/proofs.d.ts +2 -1
- package/lib/chain/lightClient/proofs.d.ts.map +1 -1
- package/lib/chain/lightClient/proofs.js +5 -1
- package/lib/chain/lightClient/proofs.js.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +16 -14
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +4 -2
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +57 -72
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +27 -55
- package/lib/chain/validation/block.js.map +1 -1
- package/lib/chain/validation/dataColumnSidecar.d.ts.map +1 -1
- package/lib/chain/validation/dataColumnSidecar.js +38 -13
- package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +56 -40
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +10 -29
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.js +5 -6
- package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
- package/lib/chain/validatorMonitor.d.ts +7 -1
- package/lib/chain/validatorMonitor.d.ts.map +1 -1
- package/lib/chain/validatorMonitor.js +22 -5
- package/lib/chain/validatorMonitor.js.map +1 -1
- package/lib/db/beacon.d.ts +14 -5
- package/lib/db/beacon.d.ts.map +1 -1
- package/lib/db/beacon.js +17 -3
- package/lib/db/beacon.js.map +1 -1
- package/lib/db/dataColumnStore.d.ts +39 -0
- package/lib/db/dataColumnStore.d.ts.map +1 -0
- package/lib/db/dataColumnStore.js +81 -0
- package/lib/db/dataColumnStore.js.map +1 -0
- package/lib/db/flatFileStore/atomicWrite.d.ts +8 -0
- package/lib/db/flatFileStore/atomicWrite.d.ts.map +1 -0
- package/lib/db/flatFileStore/atomicWrite.js +61 -0
- package/lib/db/flatFileStore/atomicWrite.js.map +1 -0
- package/lib/db/flatFileStore/dcolFormat.d.ts +90 -0
- package/lib/db/flatFileStore/dcolFormat.d.ts.map +1 -0
- package/lib/db/flatFileStore/dcolFormat.js +286 -0
- package/lib/db/flatFileStore/dcolFormat.js.map +1 -0
- package/lib/db/flatFileStore/errors.d.ts +82 -0
- package/lib/db/flatFileStore/errors.d.ts.map +1 -0
- package/lib/db/flatFileStore/errors.js +32 -0
- package/lib/db/flatFileStore/errors.js.map +1 -0
- package/lib/db/flatFileStore/flatFileStore.d.ts +41 -0
- package/lib/db/flatFileStore/flatFileStore.d.ts.map +1 -0
- package/lib/db/flatFileStore/flatFileStore.js +251 -0
- package/lib/db/flatFileStore/flatFileStore.js.map +1 -0
- package/lib/db/flatFileStore/index.d.ts +4 -0
- package/lib/db/flatFileStore/index.d.ts.map +1 -0
- package/lib/db/flatFileStore/index.js +2 -0
- package/lib/db/flatFileStore/index.js.map +1 -0
- package/lib/db/flatFileStore/interface.d.ts +25 -0
- package/lib/db/flatFileStore/interface.d.ts.map +1 -0
- package/lib/db/flatFileStore/interface.js +2 -0
- package/lib/db/flatFileStore/interface.js.map +1 -0
- package/lib/db/flatFileStore/metrics.d.ts +24 -0
- package/lib/db/flatFileStore/metrics.d.ts.map +1 -0
- package/lib/db/flatFileStore/metrics.js +25 -0
- package/lib/db/flatFileStore/metrics.js.map +1 -0
- package/lib/db/flatFileStore/path.d.ts +9 -0
- package/lib/db/flatFileStore/path.d.ts.map +1 -0
- package/lib/db/flatFileStore/path.js +21 -0
- package/lib/db/flatFileStore/path.js.map +1 -0
- package/lib/db/flatFileStore/slotIndex.d.ts +14 -0
- package/lib/db/flatFileStore/slotIndex.d.ts.map +1 -0
- package/lib/db/flatFileStore/slotIndex.js +46 -0
- package/lib/db/flatFileStore/slotIndex.js.map +1 -0
- package/lib/db/flatFileStore/snappy.d.ts +3 -0
- package/lib/db/flatFileStore/snappy.d.ts.map +1 -0
- package/lib/db/flatFileStore/snappy.js +12 -0
- package/lib/db/flatFileStore/snappy.js.map +1 -0
- package/lib/db/index.d.ts +1 -0
- package/lib/db/index.d.ts.map +1 -1
- package/lib/db/interface.d.ts +8 -1
- package/lib/db/interface.d.ts.map +1 -1
- package/lib/db/repositories/blockArchive.d.ts +8 -1
- package/lib/db/repositories/blockArchive.d.ts.map +1 -1
- package/lib/db/repositories/blockArchive.js +65 -28
- package/lib/db/repositories/blockArchive.js.map +1 -1
- package/lib/db/repositories/blockArchiveIndex.d.ts +1 -2
- package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
- package/lib/db/repositories/blockArchiveIndex.js +3 -7
- package/lib/db/repositories/blockArchiveIndex.js.map +1 -1
- package/lib/execution/builder/validateBid.d.ts.map +1 -1
- package/lib/execution/builder/validateBid.js +8 -5
- package/lib/execution/builder/validateBid.js.map +1 -1
- package/lib/execution/engine/mock.d.ts.map +1 -1
- package/lib/execution/engine/mock.js +4 -1
- package/lib/execution/engine/mock.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +22 -3
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +60 -6
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/monitoring/clientStats.js +1 -1
- package/lib/monitoring/clientStats.js.map +1 -1
- package/lib/network/gossip/encoding.d.ts.map +1 -1
- package/lib/network/gossip/encoding.js +0 -7
- package/lib/network/gossip/encoding.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +2 -1
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +4 -20
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/libp2p/index.d.ts +9 -1
- package/lib/network/libp2p/index.d.ts.map +1 -1
- package/lib/network/libp2p/index.js +23 -0
- package/lib/network/libp2p/index.js.map +1 -1
- package/lib/network/network.js +2 -2
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +388 -285
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/index.d.ts +49 -3
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +254 -114
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +112 -91
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +2 -0
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
- package/lib/network/reqresp/protocols.d.ts.map +1 -1
- package/lib/network/reqresp/protocols.js +6 -22
- package/lib/network/reqresp/protocols.js.map +1 -1
- package/lib/network/reqresp/rateLimit.js +2 -1
- package/lib/network/reqresp/rateLimit.js.map +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts +2 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts.map +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js +14 -9
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
- package/lib/node/nodejs.d.ts +2 -1
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +4 -2
- package/lib/node/nodejs.js.map +1 -1
- package/lib/sync/range/range.d.ts.map +1 -1
- package/lib/sync/range/range.js +24 -1
- package/lib/sync/range/range.js.map +1 -1
- package/lib/util/sszBytes.d.ts +6 -0
- package/lib/util/sszBytes.d.ts.map +1 -1
- package/lib/util/sszBytes.js +1 -1
- package/lib/util/sszBytes.js.map +1 -1
- package/package.json +19 -20
- package/src/api/impl/beacon/blocks/index.ts +8 -14
- package/src/api/impl/beacon/state/utils.ts +15 -0
- package/src/api/impl/config/index.ts +4 -1
- package/src/api/impl/validator/index.ts +44 -43
- package/src/chain/archiveStore/archiveStore.ts +12 -1
- package/src/chain/archiveStore/historicalState/historicalStateRegen.ts +1 -0
- package/src/chain/archiveStore/historicalState/metrics.ts +14 -1
- package/src/chain/archiveStore/historicalState/types.ts +2 -0
- package/src/chain/archiveStore/historicalState/worker.ts +4 -1
- package/src/chain/archiveStore/utils/archiveBlocks.ts +73 -42
- package/src/chain/blocks/importBlock.ts +25 -20
- package/src/chain/blocks/index.ts +120 -23
- package/src/chain/blocks/types.ts +9 -0
- package/src/chain/blocks/utils/chainSegment.ts +26 -12
- package/src/chain/blocks/writeBlockInputToDb.ts +10 -17
- package/src/chain/chain.ts +153 -84
- package/src/chain/emitter.ts +1 -1
- package/src/chain/errors/blockError.ts +19 -10
- package/src/chain/errors/dataColumnSidecarError.ts +24 -8
- package/src/chain/errors/executionPayloadBid.ts +2 -0
- package/src/chain/errors/executionPayloadEnvelope.ts +14 -11
- package/src/chain/errors/gossipValidation.ts +1 -0
- package/src/chain/forkChoice/index.ts +2 -0
- package/src/chain/interface.ts +4 -4
- package/src/chain/lightClient/index.ts +32 -36
- package/src/chain/lightClient/proofs.ts +10 -0
- package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
- package/src/chain/prepareNextSlot.ts +21 -17
- package/src/chain/produceBlock/produceBlockBody.ts +62 -79
- package/src/chain/validation/block.ts +27 -72
- package/src/chain/validation/dataColumnSidecar.ts +47 -14
- package/src/chain/validation/executionPayloadBid.ts +62 -44
- package/src/chain/validation/executionPayloadEnvelope.ts +10 -37
- package/src/chain/validation/payloadAttestationMessage.ts +8 -9
- package/src/chain/validatorMonitor.ts +24 -7
- package/src/db/beacon.ts +33 -6
- package/src/db/dataColumnStore.ts +113 -0
- package/src/db/flatFileStore/atomicWrite.ts +65 -0
- package/src/db/flatFileStore/dcolFormat.ts +389 -0
- package/src/db/flatFileStore/errors.ts +53 -0
- package/src/db/flatFileStore/flatFileStore.ts +310 -0
- package/src/db/flatFileStore/index.ts +3 -0
- package/src/db/flatFileStore/interface.ts +21 -0
- package/src/db/flatFileStore/metrics.ts +48 -0
- package/src/db/flatFileStore/path.ts +32 -0
- package/src/db/flatFileStore/slotIndex.ts +56 -0
- package/src/db/flatFileStore/snappy.ts +14 -0
- package/src/db/index.ts +1 -0
- package/src/db/interface.ts +9 -1
- package/src/db/repositories/blockArchive.ts +89 -30
- package/src/db/repositories/blockArchiveIndex.ts +4 -9
- package/src/execution/builder/validateBid.ts +10 -6
- package/src/execution/engine/mock.ts +4 -1
- package/src/metrics/metrics/lodestar.ts +63 -6
- package/src/monitoring/clientStats.ts +1 -1
- package/src/network/gossip/encoding.ts +0 -7
- package/src/network/gossip/topic.ts +3 -25
- package/src/network/libp2p/index.ts +28 -1
- package/src/network/network.ts +2 -2
- package/src/network/processor/gossipHandlers.ts +500 -355
- package/src/network/processor/index.ts +287 -165
- package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +139 -105
- package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +2 -0
- package/src/network/reqresp/protocols.ts +7 -24
- package/src/network/reqresp/rateLimit.ts +2 -1
- package/src/network/reqresp/utils/dataColumnResponseValidation.ts +15 -7
- package/src/node/nodejs.ts +6 -1
- package/src/sync/range/range.ts +23 -1
- package/src/util/sszBytes.ts +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {TopicValidatorResult} from "@libp2p/gossipsub";
|
|
2
2
|
import {routes} from "@lodestar/api";
|
|
3
|
-
import {ForkSeq} from "@lodestar/params";
|
|
3
|
+
import {ForkSeq, NUMBER_OF_COLUMNS} from "@lodestar/params";
|
|
4
4
|
import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
|
5
5
|
import {RootHex, Slot, SlotRootHex} from "@lodestar/types";
|
|
6
6
|
import {Logger, MapDef, mapValues, sleep} from "@lodestar/utils";
|
|
@@ -11,7 +11,6 @@ import {IBeaconChain} from "../../chain/interface.js";
|
|
|
11
11
|
import {IBeaconDb} from "../../db/interface.js";
|
|
12
12
|
import {Metrics} from "../../metrics/metrics.js";
|
|
13
13
|
import {ClockEvent} from "../../util/clock.js";
|
|
14
|
-
import {callInNextEventLoop} from "../../util/eventLoop.js";
|
|
15
14
|
import {PeerIdStr} from "../../util/peerId.js";
|
|
16
15
|
import {
|
|
17
16
|
getBeaconBlockRootFromExecutionPayloadEnvelopeSerialized,
|
|
@@ -60,6 +59,36 @@ export type NetworkProcessorOpts = GossipHandlerOpts & {
|
|
|
60
59
|
*/
|
|
61
60
|
const MAX_UNKNOWN_ROOTS_SLOT_CACHE_SIZE = 3;
|
|
62
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Max distinct roots we BUFFER messages for, per slot. Kept tight to avoid the OOM
|
|
64
|
+
* risk. We don't support super forky condition where there are more than this many roots per slot via gossip.
|
|
65
|
+
*/
|
|
66
|
+
export const MAX_BUFFERED_ROOTS_PER_SLOT = 5;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Max distinct roots we emit an unknown-root search for, per slot. Higher than
|
|
70
|
+
* the buffer budget because:
|
|
71
|
+
* - in the attack scenario, the genuine root may comes after the first MAX_BUFFERED_ROOTS_PER_SLOT roots
|
|
72
|
+
* - if we cannot search, we'll penalize peers in UnknownBlockInput, not in NetworkProcessor
|
|
73
|
+
*/
|
|
74
|
+
export const MAX_SEARCHED_ROOTS_PER_SLOT = 32;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Given the same root and topic, we'll ignore messages after the below cap. Each cap is the number of
|
|
78
|
+
* legitimate messages we expect for one block, plus headroom for some malformed messages that may arrives first
|
|
79
|
+
* they will be penalized by the gossip handler
|
|
80
|
+
*/
|
|
81
|
+
export const MAX_AWAITING_MESSAGES_PER_ROOT: Partial<Record<GossipType, number>> = {
|
|
82
|
+
[GossipType.data_column_sidecar]: 2 * NUMBER_OF_COLUMNS,
|
|
83
|
+
[GossipType.execution_payload]: 2,
|
|
84
|
+
[GossipType.execution_payload_bid]: 8,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Track the forwarded peers we already emit to UnknownBlockInput sync.
|
|
89
|
+
*/
|
|
90
|
+
type SearchedRootEntry = {blockPeerIds?: Set<PeerIdStr>; envelopePeerIds?: Set<PeerIdStr>};
|
|
91
|
+
|
|
63
92
|
/**
|
|
64
93
|
* This is respective to gossipsub seenTTL (which is 550 * 0.7 = 385s), also it's respective
|
|
65
94
|
* to beacon_attestation ATTESTATION_PROPAGATION_SLOT_RANGE (32 slots).
|
|
@@ -121,6 +150,14 @@ export enum ReprocessRejectReason {
|
|
|
121
150
|
* There are too many gossip messages that have unknown block root.
|
|
122
151
|
*/
|
|
123
152
|
reached_limit = "reached_limit",
|
|
153
|
+
/**
|
|
154
|
+
* Too many distinct unknown roots are already tracked for the message's slot (MAX_UNKNOWN_ROOTS_PER_SLOT).
|
|
155
|
+
*/
|
|
156
|
+
reached_root_limit = "reached_root_limit",
|
|
157
|
+
/**
|
|
158
|
+
* Too many gossip messages are already buffered for this (root, topic) (MAX_AWAITING_MESSAGES_PER_ROOT).
|
|
159
|
+
*/
|
|
160
|
+
reached_topic_limit = "reached_topic_limit",
|
|
124
161
|
/**
|
|
125
162
|
* The awaiting gossip message is pruned per clock slot.
|
|
126
163
|
*/
|
|
@@ -186,19 +223,21 @@ export class NetworkProcessor {
|
|
|
186
223
|
private readonly gossipTopicConcurrency: {[K in GossipType]: number};
|
|
187
224
|
private readonly extractBlockSlotRootFns = createExtractBlockSlotRootFns();
|
|
188
225
|
// we may not receive the block for messages like Attestation and SignedAggregateAndProof messages, in that case PendingGossipsubMessage needs
|
|
189
|
-
// to be stored in this Map and reprocessed once the block comes
|
|
190
|
-
|
|
226
|
+
// to be stored in this Map and reprocessed once the block comes. Keyed by topic per root so we can cap
|
|
227
|
+
// the number of buffered messages per (root, topic) - see MAX_AWAITING_MESSAGES_PER_ROOT.
|
|
228
|
+
private readonly awaitingMessagesByBlockRoot: MapDef<RootHex, MapDef<GossipType, Set<PendingGossipsubMessage>>>;
|
|
191
229
|
private awaitingBlockMessageCount = 0;
|
|
192
230
|
// we may not receive the payload for messages that require the FULL payload variant to be processed,
|
|
193
231
|
// in that case PendingGossipsubMessage needs to be stored in this Map and reprocessed once the payload comes
|
|
194
|
-
private readonly awaitingMessagesByPayloadBlockRoot: MapDef<
|
|
232
|
+
private readonly awaitingMessagesByPayloadBlockRoot: MapDef<
|
|
233
|
+
RootHex,
|
|
234
|
+
MapDef<GossipType, Set<PendingGossipsubMessage>>
|
|
235
|
+
>;
|
|
195
236
|
private awaitingPayloadMessageCount = 0;
|
|
196
|
-
private
|
|
197
|
-
() => new MapDef<RootHex,
|
|
198
|
-
);
|
|
199
|
-
private unknownEnvelopesBySlot = new MapDef<Slot, MapDef<RootHex, Set<PeerIdStr>>>(
|
|
200
|
-
() => new MapDef<RootHex, Set<PeerIdStr>>(() => new Set())
|
|
237
|
+
private searchedRootsBySlot = new MapDef<Slot, MapDef<RootHex, SearchedRootEntry>>(
|
|
238
|
+
() => new MapDef<RootHex, SearchedRootEntry>(() => ({}))
|
|
201
239
|
);
|
|
240
|
+
private bufferedRootsBySlot = new MapDef<Slot, Set<RootHex>>(() => new Set());
|
|
202
241
|
|
|
203
242
|
constructor(
|
|
204
243
|
modules: NetworkProcessorModules,
|
|
@@ -223,8 +262,12 @@ export class NetworkProcessor {
|
|
|
223
262
|
this.chain.emitter.on(routes.events.EventType.executionPayload, this.onPayloadEnvelopeProcessed);
|
|
224
263
|
this.chain.clock.on(ClockEvent.slot, this.onClockSlot);
|
|
225
264
|
|
|
226
|
-
this.awaitingMessagesByBlockRoot = new MapDef<RootHex, Set<PendingGossipsubMessage
|
|
227
|
-
|
|
265
|
+
this.awaitingMessagesByBlockRoot = new MapDef<RootHex, MapDef<GossipType, Set<PendingGossipsubMessage>>>(
|
|
266
|
+
() => new MapDef<GossipType, Set<PendingGossipsubMessage>>(() => new Set())
|
|
267
|
+
);
|
|
268
|
+
this.awaitingMessagesByPayloadBlockRoot = new MapDef<RootHex, MapDef<GossipType, Set<PendingGossipsubMessage>>>(
|
|
269
|
+
() => new MapDef<GossipType, Set<PendingGossipsubMessage>>(() => new Set())
|
|
270
|
+
);
|
|
228
271
|
|
|
229
272
|
// TODO: Implement queues and priorization for ReqResp incoming requests
|
|
230
273
|
// Listens to NetworkEvent.reqRespIncomingRequest event
|
|
@@ -284,9 +327,12 @@ export class NetworkProcessor {
|
|
|
284
327
|
if (this.chain.seenBlock(root)) {
|
|
285
328
|
return;
|
|
286
329
|
}
|
|
287
|
-
const
|
|
288
|
-
const alreadySearching =
|
|
289
|
-
|
|
330
|
+
const entry = this.searchedRootsBySlot.getOrDefault(slot).getOrDefault(root);
|
|
331
|
+
const alreadySearching = entry.blockPeerIds !== undefined;
|
|
332
|
+
if (entry.blockPeerIds === undefined) {
|
|
333
|
+
entry.blockPeerIds = new Set();
|
|
334
|
+
}
|
|
335
|
+
const forwardedPeers = entry.blockPeerIds;
|
|
290
336
|
|
|
291
337
|
// brand-new search always emits (peer may be undefined)
|
|
292
338
|
let shouldEmit = !alreadySearching;
|
|
@@ -310,10 +356,12 @@ export class NetworkProcessor {
|
|
|
310
356
|
if (this.chain.seenPayloadEnvelope(root)) {
|
|
311
357
|
return;
|
|
312
358
|
}
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
359
|
+
const entry = this.searchedRootsBySlot.getOrDefault(slot).getOrDefault(root);
|
|
360
|
+
const alreadySearching = entry.envelopePeerIds !== undefined;
|
|
361
|
+
if (entry.envelopePeerIds === undefined) {
|
|
362
|
+
entry.envelopePeerIds = new Set();
|
|
363
|
+
}
|
|
364
|
+
const forwardedPeers = entry.envelopePeerIds;
|
|
317
365
|
|
|
318
366
|
let shouldEmit = !alreadySearching; // brand-new search always emits (peer may be undefined)
|
|
319
367
|
if (peer !== undefined && !forwardedPeers.has(peer) && forwardedPeers.size < MAX_PEERS_PER_ROOT) {
|
|
@@ -361,15 +409,29 @@ export class NetworkProcessor {
|
|
|
361
409
|
return;
|
|
362
410
|
}
|
|
363
411
|
|
|
412
|
+
// DOS protection: drop far-future messages before allocating any unknown-root tracking entry.
|
|
413
|
+
// Bound each topic to what its gossip validator accepts; only execution_payload_bid accepts the next slot.
|
|
414
|
+
const latestPermissableSlot =
|
|
415
|
+
this.chain.clock.currentSlotWithGossipDisparity + (topicType === GossipType.execution_payload_bid ? 1 : 0);
|
|
416
|
+
if (slot > latestPermissableSlot) {
|
|
417
|
+
// No need to report the dropped job to gossip. It will be eventually pruned from the mcache
|
|
418
|
+
this.metrics?.networkProcessor.gossipValidationError.inc({
|
|
419
|
+
topic: topicType,
|
|
420
|
+
error: GossipErrorCode.FUTURE_SLOT,
|
|
421
|
+
});
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
|
|
364
425
|
message.msgSlot = slot;
|
|
426
|
+
const peerId = message.propagationSource.toString();
|
|
365
427
|
|
|
366
428
|
// this determines whether this message needs to wait for a Block or Envelope
|
|
367
|
-
// a message should only wait for what it voted for, hence we don't want to put it on both queues
|
|
429
|
+
// a message should only wait for what it voted for, hence we don't want to put it on both queues.
|
|
368
430
|
let preprocessResult: PreprocessResult = {action: PreprocessAction.PushToQueue};
|
|
369
431
|
// no need to check if root is a descendant of the current finalized block, it will be checked once we validate the message if needed
|
|
370
432
|
if (root && !this.chain.forkChoice.hasBlockHexUnsafe(root)) {
|
|
371
433
|
// starting from GLOAS, unknown root from data_column_sidecar also falls into this case
|
|
372
|
-
this.
|
|
434
|
+
this.searchUnknownRoot({slot, root}, true, false, peerId);
|
|
373
435
|
// for beacon_attestation and beacon_aggregate_and_proof messages, this is only temporary.
|
|
374
436
|
// if "index = 1" we need to await for the Envelope instead
|
|
375
437
|
preprocessResult = {action: PreprocessAction.AwaitBlock, root};
|
|
@@ -379,40 +441,32 @@ export class NetworkProcessor {
|
|
|
379
441
|
// we separate the search action from the await action
|
|
380
442
|
|
|
381
443
|
// beacon_block: proactively search for parent block/envelope across all forks, but never queue.
|
|
382
|
-
// BlockInputSync handles cascading recovery if the gossip handler throws.
|
|
444
|
+
// BlockInputSync handles cascading recovery if the gossip handler throws. The parent root differs from
|
|
445
|
+
// the message root and this message never awaits, so its search flushes inline here (gated by the budget).
|
|
383
446
|
if (topicType === GossipType.beacon_block) {
|
|
384
447
|
const parentRoot = getParentRootFromSignedBeaconBlockSerialized(message.msg.data);
|
|
385
448
|
if (parentRoot) {
|
|
449
|
+
let searchParentBlock = false;
|
|
450
|
+
let searchParentEnvelope = false;
|
|
386
451
|
if (ForkSeq[fork] >= ForkSeq.gloas) {
|
|
387
452
|
// GLOAS: also check parent envelope, same logic as execution_payload_bid
|
|
388
453
|
const parentBlockHash = getParentBlockHashFromGloasSignedBeaconBlockSerialized(message.msg.data);
|
|
389
454
|
if (parentBlockHash && !this.chain.forkChoice.getBlockHexAndBlockHash(parentRoot, parentBlockHash)) {
|
|
390
455
|
const protoBlock = this.chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
|
|
391
456
|
if (protoBlock === null) {
|
|
392
|
-
|
|
393
|
-
{slot, root: parentRoot},
|
|
394
|
-
BlockInputSource.network_processor,
|
|
395
|
-
message.propagationSource.toString()
|
|
396
|
-
);
|
|
457
|
+
searchParentBlock = true;
|
|
397
458
|
} else if (
|
|
398
459
|
protoBlock.executionPayloadBlockHash &&
|
|
399
460
|
protoBlock.executionPayloadBlockHash !== parentBlockHash
|
|
400
461
|
) {
|
|
401
462
|
// only search for the envelope by block root if we're sure there is one. Otherwise UnknownBlockSync will penalize the peer.
|
|
402
|
-
|
|
403
|
-
{slot, root: parentRoot},
|
|
404
|
-
BlockInputSource.network_processor,
|
|
405
|
-
message.propagationSource.toString()
|
|
406
|
-
);
|
|
463
|
+
searchParentEnvelope = true;
|
|
407
464
|
}
|
|
408
465
|
}
|
|
409
466
|
} else if (!this.chain.forkChoice.hasBlockHexUnsafe(parentRoot)) {
|
|
410
|
-
|
|
411
|
-
{slot, root: parentRoot},
|
|
412
|
-
BlockInputSource.network_processor,
|
|
413
|
-
message.propagationSource.toString()
|
|
414
|
-
);
|
|
467
|
+
searchParentBlock = true;
|
|
415
468
|
}
|
|
469
|
+
this.searchUnknownRoot({slot, root: parentRoot}, searchParentBlock, searchParentEnvelope, peerId);
|
|
416
470
|
}
|
|
417
471
|
preprocessResult = {action: PreprocessAction.PushToQueue};
|
|
418
472
|
}
|
|
@@ -431,11 +485,7 @@ export class NetworkProcessor {
|
|
|
431
485
|
: getDataIndexFromSignedAggregateAndProofSerialized(message.msg.data);
|
|
432
486
|
if (attIndex === 1 && !this.chain.forkChoice.hasPayloadHexUnsafe(root)) {
|
|
433
487
|
// attestation votes that the payload is available but it is not yet known
|
|
434
|
-
this.
|
|
435
|
-
{slot, root},
|
|
436
|
-
BlockInputSource.network_processor,
|
|
437
|
-
message.propagationSource.toString()
|
|
438
|
-
);
|
|
488
|
+
this.searchUnknownRoot({slot, root}, false, true, peerId);
|
|
439
489
|
preprocessResult = {action: PreprocessAction.AwaitEnvelope, root};
|
|
440
490
|
}
|
|
441
491
|
break;
|
|
@@ -445,11 +495,7 @@ export class NetworkProcessor {
|
|
|
445
495
|
const payloadPresent = getPayloadPresentFromPayloadAttestationMessageSerialized(message.msg.data);
|
|
446
496
|
if (payloadPresent && !this.chain.forkChoice.hasPayloadHexUnsafe(root)) {
|
|
447
497
|
// payload attestation votes that the payload is available but it is not yet known
|
|
448
|
-
this.
|
|
449
|
-
{slot, root},
|
|
450
|
-
BlockInputSource.network_processor,
|
|
451
|
-
message.propagationSource.toString()
|
|
452
|
-
);
|
|
498
|
+
this.searchUnknownRoot({slot, root}, false, true, peerId);
|
|
453
499
|
// do not await the envelope, payload attestation processing only requires that the block is known
|
|
454
500
|
// also do not reset preprocessResult, we may already await for the block
|
|
455
501
|
}
|
|
@@ -458,11 +504,7 @@ export class NetworkProcessor {
|
|
|
458
504
|
case GossipType.data_column_sidecar: {
|
|
459
505
|
if (root == null) break;
|
|
460
506
|
if (!this.chain.forkChoice.hasPayloadHexUnsafe(root)) {
|
|
461
|
-
this.
|
|
462
|
-
{slot, root},
|
|
463
|
-
BlockInputSource.network_processor,
|
|
464
|
-
message.propagationSource.toString()
|
|
465
|
-
);
|
|
507
|
+
this.searchUnknownRoot({slot, root}, false, true, peerId);
|
|
466
508
|
// do not await the envelope, we can do gossip validation
|
|
467
509
|
// also do not reset preprocessResult, we may already await for the block
|
|
468
510
|
}
|
|
@@ -473,11 +515,7 @@ export class NetworkProcessor {
|
|
|
473
515
|
// Extract beacon_block_root directly
|
|
474
516
|
const blockRoot = getBeaconBlockRootFromExecutionPayloadEnvelopeSerialized(message.msg.data);
|
|
475
517
|
if (blockRoot && !this.chain.forkChoice.hasBlockHexUnsafe(blockRoot)) {
|
|
476
|
-
this.
|
|
477
|
-
{slot, root: blockRoot},
|
|
478
|
-
BlockInputSource.network_processor,
|
|
479
|
-
message.propagationSource.toString()
|
|
480
|
-
);
|
|
518
|
+
this.searchUnknownRoot({slot, root: blockRoot}, true, false, peerId);
|
|
481
519
|
// We always want to await the block
|
|
482
520
|
// This allows us to properly forward the payload envelope
|
|
483
521
|
preprocessResult = {action: PreprocessAction.AwaitBlock, root: blockRoot};
|
|
@@ -496,21 +534,13 @@ export class NetworkProcessor {
|
|
|
496
534
|
) {
|
|
497
535
|
const protoBlock = this.chain.forkChoice.getBlockHexDefaultStatus(parentBlockRoot);
|
|
498
536
|
if (protoBlock === null) {
|
|
499
|
-
this.
|
|
500
|
-
{slot, root: parentBlockRoot},
|
|
501
|
-
BlockInputSource.network_processor,
|
|
502
|
-
message.propagationSource.toString()
|
|
503
|
-
);
|
|
537
|
+
this.searchUnknownRoot({slot, root: parentBlockRoot}, true, false, peerId);
|
|
504
538
|
preprocessResult = {action: PreprocessAction.AwaitBlock, root: parentBlockRoot};
|
|
505
539
|
} else if (
|
|
506
540
|
protoBlock.executionPayloadBlockHash &&
|
|
507
541
|
protoBlock.executionPayloadBlockHash !== parentBlockHash
|
|
508
542
|
) {
|
|
509
|
-
this.
|
|
510
|
-
{slot, root: parentBlockRoot},
|
|
511
|
-
BlockInputSource.network_processor,
|
|
512
|
-
message.propagationSource.toString()
|
|
513
|
-
);
|
|
543
|
+
this.searchUnknownRoot({slot, root: parentBlockRoot}, false, true, peerId);
|
|
514
544
|
preprocessResult = {action: PreprocessAction.AwaitEnvelope, root: parentBlockRoot};
|
|
515
545
|
}
|
|
516
546
|
}
|
|
@@ -523,42 +553,106 @@ export class NetworkProcessor {
|
|
|
523
553
|
case PreprocessAction.PushToQueue:
|
|
524
554
|
this.pushPendingGossipsubMessageToQueue(message);
|
|
525
555
|
break;
|
|
526
|
-
case PreprocessAction.AwaitBlock:
|
|
527
|
-
|
|
528
|
-
// No need to report the dropped job to gossip. It will be eventually pruned from the mcache
|
|
529
|
-
this.metrics?.awaitingBlockGossipMessages.reject.inc({
|
|
530
|
-
reason: ReprocessRejectReason.reached_limit,
|
|
531
|
-
topic: topicType,
|
|
532
|
-
});
|
|
533
|
-
return;
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
this.metrics?.awaitingBlockGossipMessages.queue.inc({topic: topicType});
|
|
537
|
-
const awaitingGossipsubMessages = this.awaitingMessagesByBlockRoot.getOrDefault(preprocessResult.root);
|
|
538
|
-
awaitingGossipsubMessages.add(message);
|
|
539
|
-
this.awaitingBlockMessageCount++;
|
|
556
|
+
case PreprocessAction.AwaitBlock:
|
|
557
|
+
this.maybeAwaitBlock(preprocessResult.root, topicType, slot, message);
|
|
540
558
|
break;
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
if (this.awaitingPayloadMessageCount > MAX_QUEUED_UNKNOWN_PAYLOAD_GOSSIP_OBJECTS) {
|
|
544
|
-
this.metrics?.awaitingPayloadGossipMessages.reject.inc({
|
|
545
|
-
reason: ReprocessRejectReason.reached_limit,
|
|
546
|
-
topic: topicType,
|
|
547
|
-
});
|
|
548
|
-
return;
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
this.metrics?.awaitingPayloadGossipMessages.queue.inc({topic: topicType});
|
|
552
|
-
const awaitingPayloadGossipsubMessages = this.awaitingMessagesByPayloadBlockRoot.getOrDefault(
|
|
553
|
-
preprocessResult.root
|
|
554
|
-
);
|
|
555
|
-
awaitingPayloadGossipsubMessages.add(message);
|
|
556
|
-
this.awaitingPayloadMessageCount++;
|
|
559
|
+
case PreprocessAction.AwaitEnvelope:
|
|
560
|
+
this.maybeAwaitPayload(preprocessResult.root, topicType, slot, message);
|
|
557
561
|
break;
|
|
558
|
-
}
|
|
559
562
|
}
|
|
560
563
|
};
|
|
561
564
|
|
|
565
|
+
/**
|
|
566
|
+
* Buffer a gossip message that must wait for an unknown block before it can be validated.
|
|
567
|
+
*
|
|
568
|
+
* For DOS protection, the message is ignored (reject metric incremented) when any cap is hit:
|
|
569
|
+
* - MAX_QUEUED_UNKNOWN_BLOCK_GOSSIP_OBJECTS: global awaiting-block message count
|
|
570
|
+
* - MAX_BUFFERED_ROOTS_PER_SLOT: distinct roots we buffer messages for, per slot
|
|
571
|
+
* - MAX_AWAITING_MESSAGES_PER_ROOT[topic]: messages buffered per (root, topic)
|
|
572
|
+
*/
|
|
573
|
+
private maybeAwaitBlock(root: RootHex, topicType: GossipType, slot: Slot, message: PendingGossipsubMessage): void {
|
|
574
|
+
const metric = this.metrics?.awaitingBlockGossipMessages;
|
|
575
|
+
// global message-count cap - cheapest check first
|
|
576
|
+
if (this.awaitingBlockMessageCount > MAX_QUEUED_UNKNOWN_BLOCK_GOSSIP_OBJECTS) {
|
|
577
|
+
metric?.reject.inc({reason: ReprocessRejectReason.reached_limit, topic: topicType});
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
// per-(root, topic) message cap: e.g. <= NUMBER_OF_COLUMNS columns per root
|
|
581
|
+
const perRootCap = MAX_AWAITING_MESSAGES_PER_ROOT[topicType];
|
|
582
|
+
if (
|
|
583
|
+
perRootCap !== undefined &&
|
|
584
|
+
(this.awaitingMessagesByBlockRoot.get(root)?.get(topicType)?.size ?? 0) >= perRootCap
|
|
585
|
+
) {
|
|
586
|
+
metric?.reject.inc({reason: ReprocessRejectReason.reached_topic_limit, topic: topicType});
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
if (!this.tryReserveBufferedRoot(slot, root)) {
|
|
590
|
+
metric?.reject.inc({reason: ReprocessRejectReason.reached_root_limit, topic: topicType});
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
metric?.queue.inc({topic: topicType});
|
|
595
|
+
this.awaitingMessagesByBlockRoot.getOrDefault(root).getOrDefault(topicType).add(message);
|
|
596
|
+
this.awaitingBlockMessageCount++;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/** Payload counterpart of maybeAwaitBlock */
|
|
600
|
+
private maybeAwaitPayload(root: RootHex, topicType: GossipType, slot: Slot, message: PendingGossipsubMessage): void {
|
|
601
|
+
const metric = this.metrics?.awaitingPayloadGossipMessages;
|
|
602
|
+
if (this.awaitingPayloadMessageCount > MAX_QUEUED_UNKNOWN_PAYLOAD_GOSSIP_OBJECTS) {
|
|
603
|
+
metric?.reject.inc({reason: ReprocessRejectReason.reached_limit, topic: topicType});
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
const perRootCap = MAX_AWAITING_MESSAGES_PER_ROOT[topicType];
|
|
607
|
+
if (
|
|
608
|
+
perRootCap !== undefined &&
|
|
609
|
+
(this.awaitingMessagesByPayloadBlockRoot.get(root)?.get(topicType)?.size ?? 0) >= perRootCap
|
|
610
|
+
) {
|
|
611
|
+
metric?.reject.inc({reason: ReprocessRejectReason.reached_topic_limit, topic: topicType});
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
if (!this.tryReserveBufferedRoot(slot, root)) {
|
|
615
|
+
metric?.reject.inc({reason: ReprocessRejectReason.reached_root_limit, topic: topicType});
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
metric?.queue.inc({topic: topicType});
|
|
620
|
+
this.awaitingMessagesByPayloadBlockRoot.getOrDefault(root).getOrDefault(topicType).add(message);
|
|
621
|
+
this.awaitingPayloadMessageCount++;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* Cap distinct roots we emit an unknown-root search for, an already-tracked root (dedup) never counts as "too many".
|
|
626
|
+
*/
|
|
627
|
+
private tooManySearchedRoots(slot: Slot, root: RootHex): boolean {
|
|
628
|
+
const roots = this.searchedRootsBySlot.get(slot);
|
|
629
|
+
if (roots === undefined) return false;
|
|
630
|
+
return !roots.has(root) && roots.size >= MAX_SEARCHED_ROOTS_PER_SLOT;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Cap distinct roots we buffer per slot.
|
|
635
|
+
*/
|
|
636
|
+
tryReserveBufferedRoot(slot: Slot, root: RootHex): boolean {
|
|
637
|
+
const roots = this.bufferedRootsBySlot.get(slot);
|
|
638
|
+
if (roots?.has(root)) return true;
|
|
639
|
+
if ((roots?.size ?? 0) >= MAX_BUFFERED_ROOTS_PER_SLOT) return false;
|
|
640
|
+
this.bufferedRootsBySlot.getOrDefault(slot).add(root);
|
|
641
|
+
return true;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
private searchUnknownRoot(
|
|
645
|
+
slotRoot: SlotRootHex,
|
|
646
|
+
searchBlock: boolean,
|
|
647
|
+
searchEnvelope: boolean,
|
|
648
|
+
peerId: PeerIdStr
|
|
649
|
+
): void {
|
|
650
|
+
if (!searchBlock && !searchEnvelope) return;
|
|
651
|
+
if (this.tooManySearchedRoots(slotRoot.slot, slotRoot.root)) return;
|
|
652
|
+
if (searchBlock) this.searchUnknownBlock(slotRoot, BlockInputSource.network_processor, peerId);
|
|
653
|
+
if (searchEnvelope) this.searchUnknownEnvelope(slotRoot, BlockInputSource.network_processor, peerId);
|
|
654
|
+
}
|
|
655
|
+
|
|
562
656
|
private pushPendingGossipsubMessageToQueue(message: PendingGossipsubMessage): void {
|
|
563
657
|
const topicType = message.topic.type;
|
|
564
658
|
message.queueAddedMs = Date.now();
|
|
@@ -573,64 +667,82 @@ export class NetworkProcessor {
|
|
|
573
667
|
}
|
|
574
668
|
|
|
575
669
|
private onBlockProcessed = async ({block: rootHex}: {block: string; executionOptimistic: boolean}): Promise<void> => {
|
|
576
|
-
const
|
|
577
|
-
if (
|
|
670
|
+
const messagesByTopic = this.awaitingMessagesByBlockRoot.get(rootHex);
|
|
671
|
+
if (messagesByTopic === undefined) {
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
let total = 0;
|
|
675
|
+
for (const messages of messagesByTopic.values()) {
|
|
676
|
+
total += messages.size;
|
|
677
|
+
}
|
|
678
|
+
if (total === 0) {
|
|
578
679
|
return;
|
|
579
680
|
}
|
|
580
681
|
|
|
581
682
|
// Atomically remove from map and update counter before async iteration to
|
|
582
683
|
// prevent double-decrement race with onClockSlot during yield points below
|
|
583
684
|
if (this.awaitingMessagesByBlockRoot.delete(rootHex)) {
|
|
584
|
-
this.awaitingBlockMessageCount -=
|
|
685
|
+
this.awaitingBlockMessageCount -= total;
|
|
585
686
|
}
|
|
586
687
|
|
|
587
688
|
const nowSec = Date.now() / 1000;
|
|
588
689
|
let count = 0;
|
|
589
690
|
// TODO: we can group attestations to process in batches but since we have the SeenAttestationDatas
|
|
590
691
|
// cache, it may not be necessary at this time
|
|
591
|
-
for (const
|
|
592
|
-
const
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
count
|
|
603
|
-
|
|
692
|
+
for (const messages of messagesByTopic.values()) {
|
|
693
|
+
for (const message of messages) {
|
|
694
|
+
const topicType = message.topic.type;
|
|
695
|
+
this.metrics?.awaitingBlockGossipMessages.waitSecBeforeResolve.set(
|
|
696
|
+
{topic: topicType},
|
|
697
|
+
nowSec - message.seenTimestampSec
|
|
698
|
+
);
|
|
699
|
+
this.metrics?.awaitingBlockGossipMessages.resolve.inc({topic: topicType});
|
|
700
|
+
this.pushPendingGossipsubMessageToQueue(message);
|
|
701
|
+
count++;
|
|
702
|
+
// don't want to block the event loop, worse case it'd wait for 16_084 / 1024 * 50ms = 800ms which is not a big deal
|
|
703
|
+
if (count === MAX_AWAITING_GOSSIP_OBJECTS_PER_TICK) {
|
|
704
|
+
count = 0;
|
|
705
|
+
await sleep(AWAITING_GOSSIP_OBJECTS_YIELD_EVERY_MS);
|
|
706
|
+
}
|
|
604
707
|
}
|
|
605
708
|
}
|
|
606
709
|
};
|
|
607
710
|
|
|
608
711
|
private onPayloadEnvelopeProcessed = async ({blockRoot: rootHex}: {blockRoot: RootHex}): Promise<void> => {
|
|
609
|
-
const
|
|
610
|
-
if (
|
|
712
|
+
const messagesByTopic = this.awaitingMessagesByPayloadBlockRoot.get(rootHex);
|
|
713
|
+
if (messagesByTopic === undefined) {
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
let total = 0;
|
|
717
|
+
for (const messages of messagesByTopic.values()) {
|
|
718
|
+
total += messages.size;
|
|
719
|
+
}
|
|
720
|
+
if (total === 0) {
|
|
611
721
|
return;
|
|
612
722
|
}
|
|
613
723
|
|
|
614
724
|
// Atomically remove from map and update counter before async iteration to
|
|
615
725
|
// prevent double-decrement race with onClockSlot during yield points below
|
|
616
726
|
if (this.awaitingMessagesByPayloadBlockRoot.delete(rootHex)) {
|
|
617
|
-
this.awaitingPayloadMessageCount -=
|
|
727
|
+
this.awaitingPayloadMessageCount -= total;
|
|
618
728
|
}
|
|
619
729
|
|
|
620
730
|
const nowSec = Date.now() / 1000;
|
|
621
731
|
let count = 0;
|
|
622
|
-
for (const
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
count
|
|
633
|
-
|
|
732
|
+
for (const messages of messagesByTopic.values()) {
|
|
733
|
+
for (const message of messages) {
|
|
734
|
+
const topicType = message.topic.type;
|
|
735
|
+
this.metrics?.awaitingPayloadGossipMessages.waitSecBeforeResolve.set(
|
|
736
|
+
{topic: topicType},
|
|
737
|
+
nowSec - message.seenTimestampSec
|
|
738
|
+
);
|
|
739
|
+
this.metrics?.awaitingPayloadGossipMessages.resolve.inc({topic: topicType});
|
|
740
|
+
this.pushPendingGossipsubMessageToQueue(message);
|
|
741
|
+
count++;
|
|
742
|
+
if (count === MAX_AWAITING_GOSSIP_OBJECTS_PER_TICK) {
|
|
743
|
+
count = 0;
|
|
744
|
+
await sleep(AWAITING_GOSSIP_OBJECTS_YIELD_EVERY_MS);
|
|
745
|
+
}
|
|
634
746
|
}
|
|
635
747
|
}
|
|
636
748
|
};
|
|
@@ -639,12 +751,19 @@ export class NetworkProcessor {
|
|
|
639
751
|
const nowSec = Date.now() / 1000;
|
|
640
752
|
const minSlot = clockSlot - MAX_UNKNOWN_ROOTS_SLOT_CACHE_SIZE;
|
|
641
753
|
|
|
642
|
-
for (const [slot,
|
|
754
|
+
for (const [slot, searchedRoots] of this.searchedRootsBySlot) {
|
|
643
755
|
if (slot > minSlot) continue;
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
756
|
+
|
|
757
|
+
// expire messages awaiting an unknown block for these roots. Only roots this slot did a BLOCK search
|
|
758
|
+
// for (blockPeerIds set) have block waits keyed here; skipping the rest avoids deleting a payload wait
|
|
759
|
+
// that a newer slot created for the same root (block/envelope searches share searchedRootsBySlot).
|
|
760
|
+
for (const [rootHex, entry] of searchedRoots) {
|
|
761
|
+
if (entry.blockPeerIds === undefined) continue;
|
|
762
|
+
const messagesByTopic = this.awaitingMessagesByBlockRoot.get(rootHex);
|
|
763
|
+
if (messagesByTopic === undefined) continue;
|
|
764
|
+
let removed = 0;
|
|
765
|
+
for (const messages of messagesByTopic.values()) {
|
|
766
|
+
for (const message of messages) {
|
|
648
767
|
const topicType = message.topic.type;
|
|
649
768
|
this.metrics?.awaitingBlockGossipMessages.reject.inc({
|
|
650
769
|
topic: topicType,
|
|
@@ -656,20 +775,22 @@ export class NetworkProcessor {
|
|
|
656
775
|
);
|
|
657
776
|
// No need to report the dropped job to gossip. It will be eventually pruned from the mcache
|
|
658
777
|
}
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
778
|
+
removed += messages.size;
|
|
779
|
+
}
|
|
780
|
+
if (this.awaitingMessagesByBlockRoot.delete(rootHex)) {
|
|
781
|
+
this.awaitingBlockMessageCount -= removed;
|
|
662
782
|
}
|
|
663
783
|
}
|
|
664
|
-
this.unknownBlocksBySlot.delete(slot);
|
|
665
|
-
}
|
|
666
784
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
for (const rootHex of
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
785
|
+
// expire messages awaiting an unknown payload envelope for these roots. Symmetric to the block pass:
|
|
786
|
+
// only roots this slot did an ENVELOPE search for (envelopePeerIds set) have payload waits keyed here.
|
|
787
|
+
for (const [rootHex, entry] of searchedRoots) {
|
|
788
|
+
if (entry.envelopePeerIds === undefined) continue;
|
|
789
|
+
const messagesByTopic = this.awaitingMessagesByPayloadBlockRoot.get(rootHex);
|
|
790
|
+
if (messagesByTopic === undefined) continue;
|
|
791
|
+
let removed = 0;
|
|
792
|
+
for (const messages of messagesByTopic.values()) {
|
|
793
|
+
for (const message of messages) {
|
|
673
794
|
const topicType = message.topic.type;
|
|
674
795
|
this.metrics?.awaitingPayloadGossipMessages.reject.inc({
|
|
675
796
|
topic: topicType,
|
|
@@ -681,12 +802,18 @@ export class NetworkProcessor {
|
|
|
681
802
|
);
|
|
682
803
|
// No need to report the dropped job to gossip. It will be eventually pruned from the mcache
|
|
683
804
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
805
|
+
removed += messages.size;
|
|
806
|
+
}
|
|
807
|
+
if (this.awaitingMessagesByPayloadBlockRoot.delete(rootHex)) {
|
|
808
|
+
this.awaitingPayloadMessageCount -= removed;
|
|
687
809
|
}
|
|
688
810
|
}
|
|
689
|
-
|
|
811
|
+
|
|
812
|
+
this.searchedRootsBySlot.delete(slot);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
for (const slot of this.bufferedRootsBySlot.keys()) {
|
|
816
|
+
if (slot <= minSlot) this.bufferedRootsBySlot.delete(slot);
|
|
690
817
|
}
|
|
691
818
|
};
|
|
692
819
|
|
|
@@ -781,27 +908,22 @@ export class NetworkProcessor {
|
|
|
781
908
|
this.trackJobTime(messageOrArray, 1, acceptanceArr[0]);
|
|
782
909
|
}
|
|
783
910
|
|
|
784
|
-
//
|
|
785
|
-
//
|
|
786
|
-
//
|
|
787
|
-
|
|
911
|
+
// Report the validation verdict synchronously so it reaches the network worker before any deferred
|
|
912
|
+
// handler (e.g. block import) runs on the next event loop. At network thread side, gossipsub onValidationResult()
|
|
913
|
+
// has its own callInNextEventLoop
|
|
788
914
|
if (Array.isArray(messageOrArray)) {
|
|
789
915
|
for (const [i, msg] of messageOrArray.entries()) {
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
acceptance: acceptanceArr[i],
|
|
795
|
-
});
|
|
916
|
+
this.events.emit(NetworkEvent.gossipMessageValidationResult, {
|
|
917
|
+
msgId: msg.msgId,
|
|
918
|
+
propagationSource: msg.propagationSource,
|
|
919
|
+
acceptance: acceptanceArr[i],
|
|
796
920
|
});
|
|
797
921
|
}
|
|
798
922
|
} else {
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
acceptance: acceptanceArr[0],
|
|
804
|
-
});
|
|
923
|
+
this.events.emit(NetworkEvent.gossipMessageValidationResult, {
|
|
924
|
+
msgId: messageOrArray.msgId,
|
|
925
|
+
propagationSource: messageOrArray.propagationSource,
|
|
926
|
+
acceptance: acceptanceArr[0],
|
|
805
927
|
});
|
|
806
928
|
}
|
|
807
929
|
}
|