@lodestar/beacon-node 1.47.0-rc.0 → 1.48.0-dev.02b355c4db
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 +8 -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 +44 -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 +89 -23
- 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/verifyBlock.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.js +5 -2
- package/lib/chain/blocks/verifyBlock.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 +12 -6
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +131 -76
- 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 +19 -7
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +2 -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 +9 -0
- package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadBid.js +2 -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 +6 -6
- 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 +5 -0
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +37 -14
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.js +3 -25
- package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +46 -64
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +1 -0
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/interface.js +1 -0
- package/lib/chain/regen/interface.js.map +1 -1
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +74 -63
- 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 +15 -3
- package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +199 -28
- 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/apiClient.d.ts +6 -0
- package/lib/execution/builder/apiClient.d.ts.map +1 -1
- package/lib/execution/builder/apiClient.js +19 -1
- package/lib/execution/builder/apiClient.js.map +1 -1
- package/lib/execution/builder/validateBid.d.ts +1 -0
- package/lib/execution/builder/validateBid.d.ts.map +1 -1
- package/lib/execution/builder/validateBid.js +17 -6
- 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 +32 -4
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +78 -8
- 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 -6
- 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/network.d.ts.map +1 -1
- package/lib/network/network.js +7 -6
- 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/gossipQueues/indexed.d.ts.map +1 -1
- package/lib/network/processor/gossipQueues/indexed.js +0 -1
- package/lib/network/processor/gossipQueues/indexed.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 +270 -122
- 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/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +9 -2
- package/lib/sync/range/chain.js.map +1 -1
- package/lib/sync/range/range.d.ts.map +1 -1
- package/lib/sync/range/range.js +25 -2
- 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 +18 -18
- package/src/api/impl/beacon/blocks/index.ts +12 -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 +49 -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 +126 -29
- package/src/chain/blocks/types.ts +9 -0
- package/src/chain/blocks/utils/chainSegment.ts +26 -12
- package/src/chain/blocks/verifyBlock.ts +5 -2
- package/src/chain/blocks/writeBlockInputToDb.ts +10 -17
- package/src/chain/chain.ts +155 -85
- package/src/chain/emitter.ts +1 -1
- package/src/chain/errors/blockError.ts +22 -10
- package/src/chain/errors/dataColumnSidecarError.ts +24 -8
- package/src/chain/errors/executionPayloadBid.ts +8 -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 +6 -6
- 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 +42 -17
- package/src/chain/produceBlock/computeNewStateRoot.ts +3 -38
- package/src/chain/produceBlock/produceBlockBody.ts +50 -71
- package/src/chain/regen/interface.ts +1 -0
- package/src/chain/validation/block.ts +77 -79
- package/src/chain/validation/dataColumnSidecar.ts +47 -14
- package/src/chain/validation/executionPayloadBid.ts +230 -32
- 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/apiClient.ts +21 -1
- package/src/execution/builder/validateBid.ts +26 -7
- package/src/execution/engine/mock.ts +4 -1
- package/src/metrics/metrics/lodestar.ts +83 -9
- package/src/monitoring/clientStats.ts +1 -1
- package/src/network/gossip/encoding.ts +0 -6
- package/src/network/gossip/topic.ts +3 -25
- package/src/network/network.ts +9 -6
- package/src/network/processor/gossipHandlers.ts +500 -355
- package/src/network/processor/gossipQueues/indexed.ts +0 -1
- package/src/network/processor/index.ts +312 -176
- 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/chain.ts +9 -2
- package/src/sync/range/range.ts +24 -2
- package/src/util/sszBytes.ts +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import {TopicValidatorResult} from "@libp2p/gossipsub";
|
|
1
2
|
import {routes} from "@lodestar/api";
|
|
2
|
-
import {ForkSeq} from "@lodestar/params";
|
|
3
|
+
import {ForkSeq, NUMBER_OF_COLUMNS} from "@lodestar/params";
|
|
3
4
|
import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
|
4
5
|
import {RootHex, Slot, SlotRootHex} from "@lodestar/types";
|
|
5
6
|
import {Logger, MapDef, mapValues, sleep} from "@lodestar/utils";
|
|
@@ -10,7 +11,6 @@ import {IBeaconChain} from "../../chain/interface.js";
|
|
|
10
11
|
import {IBeaconDb} from "../../db/interface.js";
|
|
11
12
|
import {Metrics} from "../../metrics/metrics.js";
|
|
12
13
|
import {ClockEvent} from "../../util/clock.js";
|
|
13
|
-
import {callInNextEventLoop} from "../../util/eventLoop.js";
|
|
14
14
|
import {PeerIdStr} from "../../util/peerId.js";
|
|
15
15
|
import {
|
|
16
16
|
getBeaconBlockRootFromExecutionPayloadEnvelopeSerialized,
|
|
@@ -59,6 +59,36 @@ export type NetworkProcessorOpts = GossipHandlerOpts & {
|
|
|
59
59
|
*/
|
|
60
60
|
const MAX_UNKNOWN_ROOTS_SLOT_CACHE_SIZE = 3;
|
|
61
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
|
+
|
|
62
92
|
/**
|
|
63
93
|
* This is respective to gossipsub seenTTL (which is 550 * 0.7 = 385s), also it's respective
|
|
64
94
|
* to beacon_attestation ATTESTATION_PROPAGATION_SLOT_RANGE (32 slots).
|
|
@@ -120,6 +150,14 @@ export enum ReprocessRejectReason {
|
|
|
120
150
|
* There are too many gossip messages that have unknown block root.
|
|
121
151
|
*/
|
|
122
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",
|
|
123
161
|
/**
|
|
124
162
|
* The awaiting gossip message is pruned per clock slot.
|
|
125
163
|
*/
|
|
@@ -185,19 +223,21 @@ export class NetworkProcessor {
|
|
|
185
223
|
private readonly gossipTopicConcurrency: {[K in GossipType]: number};
|
|
186
224
|
private readonly extractBlockSlotRootFns = createExtractBlockSlotRootFns();
|
|
187
225
|
// we may not receive the block for messages like Attestation and SignedAggregateAndProof messages, in that case PendingGossipsubMessage needs
|
|
188
|
-
// to be stored in this Map and reprocessed once the block comes
|
|
189
|
-
|
|
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>>>;
|
|
190
229
|
private awaitingBlockMessageCount = 0;
|
|
191
230
|
// we may not receive the payload for messages that require the FULL payload variant to be processed,
|
|
192
231
|
// in that case PendingGossipsubMessage needs to be stored in this Map and reprocessed once the payload comes
|
|
193
|
-
private readonly awaitingMessagesByPayloadBlockRoot: MapDef<
|
|
232
|
+
private readonly awaitingMessagesByPayloadBlockRoot: MapDef<
|
|
233
|
+
RootHex,
|
|
234
|
+
MapDef<GossipType, Set<PendingGossipsubMessage>>
|
|
235
|
+
>;
|
|
194
236
|
private awaitingPayloadMessageCount = 0;
|
|
195
|
-
private
|
|
196
|
-
() => new MapDef<RootHex,
|
|
197
|
-
);
|
|
198
|
-
private unknownEnvelopesBySlot = new MapDef<Slot, MapDef<RootHex, Set<PeerIdStr>>>(
|
|
199
|
-
() => new MapDef<RootHex, Set<PeerIdStr>>(() => new Set())
|
|
237
|
+
private searchedRootsBySlot = new MapDef<Slot, MapDef<RootHex, SearchedRootEntry>>(
|
|
238
|
+
() => new MapDef<RootHex, SearchedRootEntry>(() => ({}))
|
|
200
239
|
);
|
|
240
|
+
private bufferedRootsBySlot = new MapDef<Slot, Set<RootHex>>(() => new Set());
|
|
201
241
|
|
|
202
242
|
constructor(
|
|
203
243
|
modules: NetworkProcessorModules,
|
|
@@ -222,8 +262,12 @@ export class NetworkProcessor {
|
|
|
222
262
|
this.chain.emitter.on(routes.events.EventType.executionPayload, this.onPayloadEnvelopeProcessed);
|
|
223
263
|
this.chain.clock.on(ClockEvent.slot, this.onClockSlot);
|
|
224
264
|
|
|
225
|
-
this.awaitingMessagesByBlockRoot = new MapDef<RootHex, Set<PendingGossipsubMessage
|
|
226
|
-
|
|
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
|
+
);
|
|
227
271
|
|
|
228
272
|
// TODO: Implement queues and priorization for ReqResp incoming requests
|
|
229
273
|
// Listens to NetworkEvent.reqRespIncomingRequest event
|
|
@@ -283,9 +327,12 @@ export class NetworkProcessor {
|
|
|
283
327
|
if (this.chain.seenBlock(root)) {
|
|
284
328
|
return;
|
|
285
329
|
}
|
|
286
|
-
const
|
|
287
|
-
const alreadySearching =
|
|
288
|
-
|
|
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;
|
|
289
336
|
|
|
290
337
|
// brand-new search always emits (peer may be undefined)
|
|
291
338
|
let shouldEmit = !alreadySearching;
|
|
@@ -309,10 +356,12 @@ export class NetworkProcessor {
|
|
|
309
356
|
if (this.chain.seenPayloadEnvelope(root)) {
|
|
310
357
|
return;
|
|
311
358
|
}
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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;
|
|
316
365
|
|
|
317
366
|
let shouldEmit = !alreadySearching; // brand-new search always emits (peer may be undefined)
|
|
318
367
|
if (peer !== undefined && !forwardedPeers.has(peer) && forwardedPeers.size < MAX_PEERS_PER_ROOT) {
|
|
@@ -360,15 +409,29 @@ export class NetworkProcessor {
|
|
|
360
409
|
return;
|
|
361
410
|
}
|
|
362
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
|
+
|
|
363
425
|
message.msgSlot = slot;
|
|
426
|
+
const peerId = message.propagationSource.toString();
|
|
364
427
|
|
|
365
428
|
// this determines whether this message needs to wait for a Block or Envelope
|
|
366
|
-
// 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.
|
|
367
430
|
let preprocessResult: PreprocessResult = {action: PreprocessAction.PushToQueue};
|
|
368
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
|
|
369
432
|
if (root && !this.chain.forkChoice.hasBlockHexUnsafe(root)) {
|
|
370
433
|
// starting from GLOAS, unknown root from data_column_sidecar also falls into this case
|
|
371
|
-
this.
|
|
434
|
+
this.searchUnknownRoot({slot, root}, true, false, peerId);
|
|
372
435
|
// for beacon_attestation and beacon_aggregate_and_proof messages, this is only temporary.
|
|
373
436
|
// if "index = 1" we need to await for the Envelope instead
|
|
374
437
|
preprocessResult = {action: PreprocessAction.AwaitBlock, root};
|
|
@@ -378,40 +441,32 @@ export class NetworkProcessor {
|
|
|
378
441
|
// we separate the search action from the await action
|
|
379
442
|
|
|
380
443
|
// beacon_block: proactively search for parent block/envelope across all forks, but never queue.
|
|
381
|
-
// 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).
|
|
382
446
|
if (topicType === GossipType.beacon_block) {
|
|
383
447
|
const parentRoot = getParentRootFromSignedBeaconBlockSerialized(message.msg.data);
|
|
384
448
|
if (parentRoot) {
|
|
449
|
+
let searchParentBlock = false;
|
|
450
|
+
let searchParentEnvelope = false;
|
|
385
451
|
if (ForkSeq[fork] >= ForkSeq.gloas) {
|
|
386
452
|
// GLOAS: also check parent envelope, same logic as execution_payload_bid
|
|
387
453
|
const parentBlockHash = getParentBlockHashFromGloasSignedBeaconBlockSerialized(message.msg.data);
|
|
388
454
|
if (parentBlockHash && !this.chain.forkChoice.getBlockHexAndBlockHash(parentRoot, parentBlockHash)) {
|
|
389
455
|
const protoBlock = this.chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
|
|
390
456
|
if (protoBlock === null) {
|
|
391
|
-
|
|
392
|
-
{slot, root: parentRoot},
|
|
393
|
-
BlockInputSource.network_processor,
|
|
394
|
-
message.propagationSource.toString()
|
|
395
|
-
);
|
|
457
|
+
searchParentBlock = true;
|
|
396
458
|
} else if (
|
|
397
459
|
protoBlock.executionPayloadBlockHash &&
|
|
398
460
|
protoBlock.executionPayloadBlockHash !== parentBlockHash
|
|
399
461
|
) {
|
|
400
462
|
// only search for the envelope by block root if we're sure there is one. Otherwise UnknownBlockSync will penalize the peer.
|
|
401
|
-
|
|
402
|
-
{slot, root: parentRoot},
|
|
403
|
-
BlockInputSource.network_processor,
|
|
404
|
-
message.propagationSource.toString()
|
|
405
|
-
);
|
|
463
|
+
searchParentEnvelope = true;
|
|
406
464
|
}
|
|
407
465
|
}
|
|
408
466
|
} else if (!this.chain.forkChoice.hasBlockHexUnsafe(parentRoot)) {
|
|
409
|
-
|
|
410
|
-
{slot, root: parentRoot},
|
|
411
|
-
BlockInputSource.network_processor,
|
|
412
|
-
message.propagationSource.toString()
|
|
413
|
-
);
|
|
467
|
+
searchParentBlock = true;
|
|
414
468
|
}
|
|
469
|
+
this.searchUnknownRoot({slot, root: parentRoot}, searchParentBlock, searchParentEnvelope, peerId);
|
|
415
470
|
}
|
|
416
471
|
preprocessResult = {action: PreprocessAction.PushToQueue};
|
|
417
472
|
}
|
|
@@ -430,11 +485,7 @@ export class NetworkProcessor {
|
|
|
430
485
|
: getDataIndexFromSignedAggregateAndProofSerialized(message.msg.data);
|
|
431
486
|
if (attIndex === 1 && !this.chain.forkChoice.hasPayloadHexUnsafe(root)) {
|
|
432
487
|
// attestation votes that the payload is available but it is not yet known
|
|
433
|
-
this.
|
|
434
|
-
{slot, root},
|
|
435
|
-
BlockInputSource.network_processor,
|
|
436
|
-
message.propagationSource.toString()
|
|
437
|
-
);
|
|
488
|
+
this.searchUnknownRoot({slot, root}, false, true, peerId);
|
|
438
489
|
preprocessResult = {action: PreprocessAction.AwaitEnvelope, root};
|
|
439
490
|
}
|
|
440
491
|
break;
|
|
@@ -444,11 +495,7 @@ export class NetworkProcessor {
|
|
|
444
495
|
const payloadPresent = getPayloadPresentFromPayloadAttestationMessageSerialized(message.msg.data);
|
|
445
496
|
if (payloadPresent && !this.chain.forkChoice.hasPayloadHexUnsafe(root)) {
|
|
446
497
|
// payload attestation votes that the payload is available but it is not yet known
|
|
447
|
-
this.
|
|
448
|
-
{slot, root},
|
|
449
|
-
BlockInputSource.network_processor,
|
|
450
|
-
message.propagationSource.toString()
|
|
451
|
-
);
|
|
498
|
+
this.searchUnknownRoot({slot, root}, false, true, peerId);
|
|
452
499
|
// do not await the envelope, payload attestation processing only requires that the block is known
|
|
453
500
|
// also do not reset preprocessResult, we may already await for the block
|
|
454
501
|
}
|
|
@@ -457,11 +504,7 @@ export class NetworkProcessor {
|
|
|
457
504
|
case GossipType.data_column_sidecar: {
|
|
458
505
|
if (root == null) break;
|
|
459
506
|
if (!this.chain.forkChoice.hasPayloadHexUnsafe(root)) {
|
|
460
|
-
this.
|
|
461
|
-
{slot, root},
|
|
462
|
-
BlockInputSource.network_processor,
|
|
463
|
-
message.propagationSource.toString()
|
|
464
|
-
);
|
|
507
|
+
this.searchUnknownRoot({slot, root}, false, true, peerId);
|
|
465
508
|
// do not await the envelope, we can do gossip validation
|
|
466
509
|
// also do not reset preprocessResult, we may already await for the block
|
|
467
510
|
}
|
|
@@ -472,11 +515,7 @@ export class NetworkProcessor {
|
|
|
472
515
|
// Extract beacon_block_root directly
|
|
473
516
|
const blockRoot = getBeaconBlockRootFromExecutionPayloadEnvelopeSerialized(message.msg.data);
|
|
474
517
|
if (blockRoot && !this.chain.forkChoice.hasBlockHexUnsafe(blockRoot)) {
|
|
475
|
-
this.
|
|
476
|
-
{slot, root: blockRoot},
|
|
477
|
-
BlockInputSource.network_processor,
|
|
478
|
-
message.propagationSource.toString()
|
|
479
|
-
);
|
|
518
|
+
this.searchUnknownRoot({slot, root: blockRoot}, true, false, peerId);
|
|
480
519
|
// We always want to await the block
|
|
481
520
|
// This allows us to properly forward the payload envelope
|
|
482
521
|
preprocessResult = {action: PreprocessAction.AwaitBlock, root: blockRoot};
|
|
@@ -495,21 +534,13 @@ export class NetworkProcessor {
|
|
|
495
534
|
) {
|
|
496
535
|
const protoBlock = this.chain.forkChoice.getBlockHexDefaultStatus(parentBlockRoot);
|
|
497
536
|
if (protoBlock === null) {
|
|
498
|
-
this.
|
|
499
|
-
{slot, root: parentBlockRoot},
|
|
500
|
-
BlockInputSource.network_processor,
|
|
501
|
-
message.propagationSource.toString()
|
|
502
|
-
);
|
|
537
|
+
this.searchUnknownRoot({slot, root: parentBlockRoot}, true, false, peerId);
|
|
503
538
|
preprocessResult = {action: PreprocessAction.AwaitBlock, root: parentBlockRoot};
|
|
504
539
|
} else if (
|
|
505
540
|
protoBlock.executionPayloadBlockHash &&
|
|
506
541
|
protoBlock.executionPayloadBlockHash !== parentBlockHash
|
|
507
542
|
) {
|
|
508
|
-
this.
|
|
509
|
-
{slot, root: parentBlockRoot},
|
|
510
|
-
BlockInputSource.network_processor,
|
|
511
|
-
message.propagationSource.toString()
|
|
512
|
-
);
|
|
543
|
+
this.searchUnknownRoot({slot, root: parentBlockRoot}, false, true, peerId);
|
|
513
544
|
preprocessResult = {action: PreprocessAction.AwaitEnvelope, root: parentBlockRoot};
|
|
514
545
|
}
|
|
515
546
|
}
|
|
@@ -522,44 +553,109 @@ export class NetworkProcessor {
|
|
|
522
553
|
case PreprocessAction.PushToQueue:
|
|
523
554
|
this.pushPendingGossipsubMessageToQueue(message);
|
|
524
555
|
break;
|
|
525
|
-
case PreprocessAction.AwaitBlock:
|
|
526
|
-
|
|
527
|
-
// No need to report the dropped job to gossip. It will be eventually pruned from the mcache
|
|
528
|
-
this.metrics?.awaitingBlockGossipMessages.reject.inc({
|
|
529
|
-
reason: ReprocessRejectReason.reached_limit,
|
|
530
|
-
topic: topicType,
|
|
531
|
-
});
|
|
532
|
-
return;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
this.metrics?.awaitingBlockGossipMessages.queue.inc({topic: topicType});
|
|
536
|
-
const awaitingGossipsubMessages = this.awaitingMessagesByBlockRoot.getOrDefault(preprocessResult.root);
|
|
537
|
-
awaitingGossipsubMessages.add(message);
|
|
538
|
-
this.awaitingBlockMessageCount++;
|
|
556
|
+
case PreprocessAction.AwaitBlock:
|
|
557
|
+
this.maybeAwaitBlock(preprocessResult.root, topicType, slot, message);
|
|
539
558
|
break;
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
if (this.awaitingPayloadMessageCount > MAX_QUEUED_UNKNOWN_PAYLOAD_GOSSIP_OBJECTS) {
|
|
543
|
-
this.metrics?.awaitingPayloadGossipMessages.reject.inc({
|
|
544
|
-
reason: ReprocessRejectReason.reached_limit,
|
|
545
|
-
topic: topicType,
|
|
546
|
-
});
|
|
547
|
-
return;
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
this.metrics?.awaitingPayloadGossipMessages.queue.inc({topic: topicType});
|
|
551
|
-
const awaitingPayloadGossipsubMessages = this.awaitingMessagesByPayloadBlockRoot.getOrDefault(
|
|
552
|
-
preprocessResult.root
|
|
553
|
-
);
|
|
554
|
-
awaitingPayloadGossipsubMessages.add(message);
|
|
555
|
-
this.awaitingPayloadMessageCount++;
|
|
559
|
+
case PreprocessAction.AwaitEnvelope:
|
|
560
|
+
this.maybeAwaitPayload(preprocessResult.root, topicType, slot, message);
|
|
556
561
|
break;
|
|
557
|
-
}
|
|
558
562
|
}
|
|
559
563
|
};
|
|
560
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
|
+
|
|
561
656
|
private pushPendingGossipsubMessageToQueue(message: PendingGossipsubMessage): void {
|
|
562
657
|
const topicType = message.topic.type;
|
|
658
|
+
message.queueAddedMs = Date.now();
|
|
563
659
|
const droppedCount = this.gossipQueues[topicType].add(message);
|
|
564
660
|
if (droppedCount) {
|
|
565
661
|
// No need to report the dropped job to gossip. It will be eventually pruned from the mcache
|
|
@@ -571,64 +667,82 @@ export class NetworkProcessor {
|
|
|
571
667
|
}
|
|
572
668
|
|
|
573
669
|
private onBlockProcessed = async ({block: rootHex}: {block: string; executionOptimistic: boolean}): Promise<void> => {
|
|
574
|
-
const
|
|
575
|
-
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) {
|
|
576
679
|
return;
|
|
577
680
|
}
|
|
578
681
|
|
|
579
682
|
// Atomically remove from map and update counter before async iteration to
|
|
580
683
|
// prevent double-decrement race with onClockSlot during yield points below
|
|
581
684
|
if (this.awaitingMessagesByBlockRoot.delete(rootHex)) {
|
|
582
|
-
this.awaitingBlockMessageCount -=
|
|
685
|
+
this.awaitingBlockMessageCount -= total;
|
|
583
686
|
}
|
|
584
687
|
|
|
585
688
|
const nowSec = Date.now() / 1000;
|
|
586
689
|
let count = 0;
|
|
587
690
|
// TODO: we can group attestations to process in batches but since we have the SeenAttestationDatas
|
|
588
691
|
// cache, it may not be necessary at this time
|
|
589
|
-
for (const
|
|
590
|
-
const
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
count
|
|
601
|
-
|
|
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
|
+
}
|
|
602
707
|
}
|
|
603
708
|
}
|
|
604
709
|
};
|
|
605
710
|
|
|
606
711
|
private onPayloadEnvelopeProcessed = async ({blockRoot: rootHex}: {blockRoot: RootHex}): Promise<void> => {
|
|
607
|
-
const
|
|
608
|
-
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) {
|
|
609
721
|
return;
|
|
610
722
|
}
|
|
611
723
|
|
|
612
724
|
// Atomically remove from map and update counter before async iteration to
|
|
613
725
|
// prevent double-decrement race with onClockSlot during yield points below
|
|
614
726
|
if (this.awaitingMessagesByPayloadBlockRoot.delete(rootHex)) {
|
|
615
|
-
this.awaitingPayloadMessageCount -=
|
|
727
|
+
this.awaitingPayloadMessageCount -= total;
|
|
616
728
|
}
|
|
617
729
|
|
|
618
730
|
const nowSec = Date.now() / 1000;
|
|
619
731
|
let count = 0;
|
|
620
|
-
for (const
|
|
621
|
-
const
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
count
|
|
631
|
-
|
|
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
|
+
}
|
|
632
746
|
}
|
|
633
747
|
}
|
|
634
748
|
};
|
|
@@ -637,12 +751,19 @@ export class NetworkProcessor {
|
|
|
637
751
|
const nowSec = Date.now() / 1000;
|
|
638
752
|
const minSlot = clockSlot - MAX_UNKNOWN_ROOTS_SLOT_CACHE_SIZE;
|
|
639
753
|
|
|
640
|
-
for (const [slot,
|
|
754
|
+
for (const [slot, searchedRoots] of this.searchedRootsBySlot) {
|
|
641
755
|
if (slot > minSlot) continue;
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
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) {
|
|
646
767
|
const topicType = message.topic.type;
|
|
647
768
|
this.metrics?.awaitingBlockGossipMessages.reject.inc({
|
|
648
769
|
topic: topicType,
|
|
@@ -654,20 +775,22 @@ export class NetworkProcessor {
|
|
|
654
775
|
);
|
|
655
776
|
// No need to report the dropped job to gossip. It will be eventually pruned from the mcache
|
|
656
777
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
778
|
+
removed += messages.size;
|
|
779
|
+
}
|
|
780
|
+
if (this.awaitingMessagesByBlockRoot.delete(rootHex)) {
|
|
781
|
+
this.awaitingBlockMessageCount -= removed;
|
|
660
782
|
}
|
|
661
783
|
}
|
|
662
|
-
this.unknownBlocksBySlot.delete(slot);
|
|
663
|
-
}
|
|
664
784
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
for (const rootHex of
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
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) {
|
|
671
794
|
const topicType = message.topic.type;
|
|
672
795
|
this.metrics?.awaitingPayloadGossipMessages.reject.inc({
|
|
673
796
|
topic: topicType,
|
|
@@ -679,12 +802,18 @@ export class NetworkProcessor {
|
|
|
679
802
|
);
|
|
680
803
|
// No need to report the dropped job to gossip. It will be eventually pruned from the mcache
|
|
681
804
|
}
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
805
|
+
removed += messages.size;
|
|
806
|
+
}
|
|
807
|
+
if (this.awaitingMessagesByPayloadBlockRoot.delete(rootHex)) {
|
|
808
|
+
this.awaitingPayloadMessageCount -= removed;
|
|
685
809
|
}
|
|
686
810
|
}
|
|
687
|
-
|
|
811
|
+
|
|
812
|
+
this.searchedRootsBySlot.delete(slot);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
for (const slot of this.bufferedRootsBySlot.keys()) {
|
|
816
|
+
if (slot <= minSlot) this.bufferedRootsBySlot.delete(slot);
|
|
688
817
|
}
|
|
689
818
|
};
|
|
690
819
|
|
|
@@ -746,12 +875,20 @@ export class NetworkProcessor {
|
|
|
746
875
|
for (const msg of messageOrArray) {
|
|
747
876
|
msg.startProcessUnixSec = nowSec;
|
|
748
877
|
if (msg.queueAddedMs !== undefined) {
|
|
749
|
-
this.metrics?.gossipValidationQueue.queueTime.observe(
|
|
878
|
+
this.metrics?.gossipValidationQueue.queueTime.observe(
|
|
879
|
+
{topic: msg.topic.type},
|
|
880
|
+
nowSec - msg.queueAddedMs / 1000
|
|
881
|
+
);
|
|
750
882
|
}
|
|
751
883
|
}
|
|
752
884
|
} else {
|
|
753
|
-
// indexed queue is not used here
|
|
754
885
|
messageOrArray.startProcessUnixSec = nowSec;
|
|
886
|
+
if (messageOrArray.queueAddedMs !== undefined) {
|
|
887
|
+
this.metrics?.gossipValidationQueue.queueTime.observe(
|
|
888
|
+
{topic: messageOrArray.topic.type},
|
|
889
|
+
nowSec - messageOrArray.queueAddedMs / 1000
|
|
890
|
+
);
|
|
891
|
+
}
|
|
755
892
|
}
|
|
756
893
|
|
|
757
894
|
const acceptanceArr = Array.isArray(messageOrArray)
|
|
@@ -764,49 +901,48 @@ export class NetworkProcessor {
|
|
|
764
901
|
];
|
|
765
902
|
|
|
766
903
|
if (Array.isArray(messageOrArray)) {
|
|
767
|
-
for (const msg of messageOrArray) {
|
|
768
|
-
this.trackJobTime(msg, messageOrArray.length);
|
|
904
|
+
for (const [i, msg] of messageOrArray.entries()) {
|
|
905
|
+
this.trackJobTime(msg, messageOrArray.length, acceptanceArr[i]);
|
|
769
906
|
}
|
|
770
907
|
} else {
|
|
771
|
-
this.trackJobTime(messageOrArray, 1);
|
|
908
|
+
this.trackJobTime(messageOrArray, 1, acceptanceArr[0]);
|
|
772
909
|
}
|
|
773
910
|
|
|
774
|
-
//
|
|
775
|
-
//
|
|
776
|
-
//
|
|
777
|
-
|
|
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
|
|
778
914
|
if (Array.isArray(messageOrArray)) {
|
|
779
915
|
for (const [i, msg] of messageOrArray.entries()) {
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
acceptance: acceptanceArr[i],
|
|
785
|
-
});
|
|
916
|
+
this.events.emit(NetworkEvent.gossipMessageValidationResult, {
|
|
917
|
+
msgId: msg.msgId,
|
|
918
|
+
propagationSource: msg.propagationSource,
|
|
919
|
+
acceptance: acceptanceArr[i],
|
|
786
920
|
});
|
|
787
921
|
}
|
|
788
922
|
} else {
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
acceptance: acceptanceArr[0],
|
|
794
|
-
});
|
|
923
|
+
this.events.emit(NetworkEvent.gossipMessageValidationResult, {
|
|
924
|
+
msgId: messageOrArray.msgId,
|
|
925
|
+
propagationSource: messageOrArray.propagationSource,
|
|
926
|
+
acceptance: acceptanceArr[0],
|
|
795
927
|
});
|
|
796
928
|
}
|
|
797
929
|
}
|
|
798
930
|
|
|
799
|
-
private trackJobTime(message: PendingGossipsubMessage, numJob: number): void {
|
|
931
|
+
private trackJobTime(message: PendingGossipsubMessage, numJob: number, acceptance: TopicValidatorResult): void {
|
|
800
932
|
if (message.startProcessUnixSec !== null) {
|
|
933
|
+
// record job wait time for all messages
|
|
801
934
|
this.metrics?.gossipValidationQueue.jobWaitTime.observe(
|
|
802
935
|
{topic: message.topic.type},
|
|
803
936
|
message.startProcessUnixSec - message.seenTimestampSec
|
|
804
937
|
);
|
|
805
|
-
//
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
938
|
+
// but only record job time for ACCEPT messages
|
|
939
|
+
if (acceptance === TopicValidatorResult.Accept) {
|
|
940
|
+
// if it takes 64ms to process 64 jobs, the average job time is 1ms
|
|
941
|
+
this.metrics?.gossipValidationQueue.jobTime.observe(
|
|
942
|
+
{topic: message.topic.type},
|
|
943
|
+
(Date.now() / 1000 - message.startProcessUnixSec) / numJob
|
|
944
|
+
);
|
|
945
|
+
}
|
|
810
946
|
}
|
|
811
947
|
}
|
|
812
948
|
|