@lodestar/beacon-node 1.34.0-dev.d5a4e7a09c → 1.34.0-dev.d9fc7bb103
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.js +162 -55
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +3 -3
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/beacon/state/index.js +15 -16
- package/lib/api/impl/beacon/state/index.js.map +1 -1
- package/lib/api/impl/debug/index.d.ts +1 -1
- package/lib/api/impl/debug/index.js +24 -1
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/api/impl/validator/index.js +37 -26
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/api/impl/validator/utils.d.ts +3 -3
- package/lib/api/impl/validator/utils.js +2 -2
- package/lib/api/impl/validator/utils.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts +1 -1
- package/lib/chain/archiveStore/archiveStore.js +2 -2
- package/lib/chain/archiveStore/interface.d.ts +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.js +89 -22
- package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
- package/lib/chain/beaconProposerCache.d.ts +1 -0
- package/lib/chain/beaconProposerCache.js +3 -0
- package/lib/chain/beaconProposerCache.js.map +1 -1
- package/lib/chain/blocks/importBlock.js +3 -2
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/types.d.ts +66 -23
- package/lib/chain/blocks/types.js +39 -5
- package/lib/chain/blocks/types.js.map +1 -1
- package/lib/chain/blocks/utils/zebraBanner.d.ts +2 -0
- package/lib/chain/blocks/utils/zebraBanner.js +45 -0
- package/lib/chain/blocks/utils/zebraBanner.js.map +1 -0
- package/lib/chain/blocks/verifyBlock.js +18 -5
- package/lib/chain/blocks/verifyBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksDataAvailability.js +21 -10
- package/lib/chain/blocks/verifyBlocksDataAvailability.js.map +1 -1
- package/lib/chain/blocks/writeBlockInputToDb.js +63 -16
- package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
- package/lib/chain/chain.d.ts +18 -101
- package/lib/chain/chain.js +107 -68
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +18 -2
- package/lib/chain/emitter.js +13 -0
- package/lib/chain/emitter.js.map +1 -1
- package/lib/chain/errors/dataColumnSidecarError.d.ts +69 -0
- package/lib/chain/errors/dataColumnSidecarError.js +21 -0
- package/lib/chain/errors/dataColumnSidecarError.js.map +1 -0
- package/lib/chain/errors/index.d.ts +1 -0
- package/lib/chain/errors/index.js +1 -0
- package/lib/chain/errors/index.js.map +1 -1
- package/lib/chain/interface.d.ts +6 -7
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/options.d.ts +4 -1
- package/lib/chain/options.js +1 -0
- package/lib/chain/options.js.map +1 -1
- package/lib/chain/prepareNextSlot.js +2 -1
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +30 -16
- package/lib/chain/produceBlock/produceBlockBody.js +28 -28
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/produceBlock/validateBlobsAndKzgCommitments.d.ts +6 -3
- package/lib/chain/produceBlock/validateBlobsAndKzgCommitments.js +28 -4
- package/lib/chain/produceBlock/validateBlobsAndKzgCommitments.js.map +1 -1
- package/lib/chain/seenCache/seenGossipBlockInput.d.ts +64 -18
- package/lib/chain/seenCache/seenGossipBlockInput.js +321 -53
- package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
- package/lib/chain/validation/dataColumnSidecar.d.ts +29 -0
- package/lib/chain/validation/dataColumnSidecar.js +248 -0
- package/lib/chain/validation/dataColumnSidecar.js.map +1 -0
- package/lib/db/beacon.d.ts +3 -1
- package/lib/db/beacon.js +3 -1
- package/lib/db/beacon.js.map +1 -1
- package/lib/db/buckets.d.ts +3 -1
- package/lib/db/buckets.js +2 -0
- package/lib/db/buckets.js.map +1 -1
- package/lib/db/interface.d.ts +3 -1
- package/lib/db/repositories/dataColumnSidecar.d.ts +26 -0
- package/lib/db/repositories/dataColumnSidecar.js +39 -0
- package/lib/db/repositories/dataColumnSidecar.js.map +1 -0
- package/lib/db/repositories/dataColumnSidecarArchive.d.ts +24 -0
- package/lib/db/repositories/dataColumnSidecarArchive.js +39 -0
- package/lib/db/repositories/dataColumnSidecarArchive.js.map +1 -0
- package/lib/db/repositories/index.d.ts +2 -0
- package/lib/db/repositories/index.js +2 -0
- package/lib/db/repositories/index.js.map +1 -1
- package/lib/execution/builder/http.d.ts +20 -4
- package/lib/execution/builder/http.js +30 -11
- package/lib/execution/builder/http.js.map +1 -1
- package/lib/execution/builder/interface.d.ts +5 -4
- package/lib/execution/engine/http.d.ts +8 -5
- package/lib/execution/engine/http.js +56 -42
- package/lib/execution/engine/http.js.map +1 -1
- package/lib/execution/engine/interface.d.ts +6 -13
- package/lib/execution/engine/interface.js +1 -1
- package/lib/execution/engine/interface.js.map +1 -1
- package/lib/execution/engine/mock.d.ts +1 -0
- package/lib/execution/engine/mock.js +5 -0
- package/lib/execution/engine/mock.js.map +1 -1
- package/lib/execution/engine/types.d.ts +12 -2
- package/lib/execution/engine/types.js +8 -2
- package/lib/execution/engine/types.js.map +1 -1
- package/lib/metrics/metrics/beacon.d.ts +15 -5
- package/lib/metrics/metrics/beacon.js +61 -14
- package/lib/metrics/metrics/beacon.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +29 -1
- package/lib/metrics/metrics/lodestar.js +59 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/core/metrics.d.ts +10 -3
- package/lib/network/core/metrics.js +22 -4
- package/lib/network/core/metrics.js.map +1 -1
- package/lib/network/core/networkCore.d.ts +15 -4
- package/lib/network/core/networkCore.js +73 -23
- package/lib/network/core/networkCore.js.map +1 -1
- package/lib/network/core/networkCoreWorker.js +2 -0
- package/lib/network/core/networkCoreWorker.js.map +1 -1
- package/lib/network/core/networkCoreWorkerHandler.d.ts +5 -3
- package/lib/network/core/networkCoreWorkerHandler.js +6 -1
- package/lib/network/core/networkCoreWorkerHandler.js.map +1 -1
- package/lib/network/core/types.d.ts +7 -4
- package/lib/network/events.d.ts +4 -2
- package/lib/network/events.js.map +1 -1
- package/lib/network/gossip/gossipsub.d.ts +2 -2
- package/lib/network/gossip/gossipsub.js +8 -6
- package/lib/network/gossip/gossipsub.js.map +1 -1
- package/lib/network/gossip/interface.d.ts +8 -1
- package/lib/network/gossip/interface.js +1 -0
- package/lib/network/gossip/interface.js.map +1 -1
- package/lib/network/gossip/scoringParameters.d.ts +6 -2
- package/lib/network/gossip/scoringParameters.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +2033 -1484
- package/lib/network/gossip/topic.js +29 -1
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/interface.d.ts +11 -3
- package/lib/network/metadata.d.ts +9 -5
- package/lib/network/metadata.js +26 -5
- package/lib/network/metadata.js.map +1 -1
- package/lib/network/network.d.ts +14 -4
- package/lib/network/network.js +73 -11
- package/lib/network/network.js.map +1 -1
- package/lib/network/networkConfig.d.ts +12 -0
- package/lib/network/networkConfig.js +2 -0
- package/lib/network/networkConfig.js.map +1 -0
- package/lib/network/options.d.ts +1 -0
- package/lib/network/options.js +5 -0
- package/lib/network/options.js.map +1 -1
- package/lib/network/peers/discover.d.ts +8 -3
- package/lib/network/peers/discover.js +99 -14
- package/lib/network/peers/discover.js.map +1 -1
- package/lib/network/peers/peerManager.d.ts +10 -4
- package/lib/network/peers/peerManager.js +105 -19
- package/lib/network/peers/peerManager.js.map +1 -1
- package/lib/network/peers/peersData.d.ts +17 -3
- package/lib/network/peers/peersData.js.map +1 -1
- package/lib/network/peers/score/interface.d.ts +1 -1
- package/lib/network/peers/score/score.d.ts +2 -2
- package/lib/network/peers/score/score.js +4 -1
- package/lib/network/peers/score/score.js.map +1 -1
- package/lib/network/peers/score/store.d.ts +3 -1
- package/lib/network/peers/score/store.js +6 -2
- package/lib/network/peers/score/store.js.map +1 -1
- package/lib/network/peers/utils/assertPeerRelevance.d.ts +7 -3
- package/lib/network/peers/utils/assertPeerRelevance.js +10 -1
- package/lib/network/peers/utils/assertPeerRelevance.js.map +1 -1
- package/lib/network/peers/utils/prioritizePeers.d.ts +19 -7
- package/lib/network/peers/utils/prioritizePeers.js +42 -6
- package/lib/network/peers/utils/prioritizePeers.js.map +1 -1
- package/lib/network/processor/extractSlotRootFns.js +8 -1
- package/lib/network/processor/extractSlotRootFns.js.map +1 -1
- package/lib/network/processor/gossipHandlers.js +165 -16
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/gossipQueues/index.js +5 -0
- package/lib/network/processor/gossipQueues/index.js.map +1 -1
- package/lib/network/processor/index.js +1 -0
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/ReqRespBeaconNode.d.ts +2 -2
- package/lib/network/reqresp/ReqRespBeaconNode.js +36 -14
- package/lib/network/reqresp/ReqRespBeaconNode.js.map +1 -1
- package/lib/network/reqresp/beaconBlocksMaybeBlobsByRange.d.ts +24 -4
- package/lib/network/reqresp/beaconBlocksMaybeBlobsByRange.js +259 -20
- package/lib/network/reqresp/beaconBlocksMaybeBlobsByRange.js.map +1 -1
- package/lib/network/reqresp/beaconBlocksMaybeBlobsByRoot.d.ts +37 -6
- package/lib/network/reqresp/beaconBlocksMaybeBlobsByRoot.js +283 -28
- package/lib/network/reqresp/beaconBlocksMaybeBlobsByRoot.js.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js +3 -3
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRoot.d.ts +2 -2
- package/lib/network/reqresp/handlers/beaconBlocksByRoot.js.map +1 -1
- package/lib/network/reqresp/handlers/blobSidecarsByRange.d.ts +2 -2
- package/lib/network/reqresp/handlers/blobSidecarsByRange.js +2 -3
- package/lib/network/reqresp/handlers/blobSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts +8 -0
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +73 -0
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -0
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.d.ts +6 -0
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +36 -0
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -0
- package/lib/network/reqresp/handlers/index.js +13 -2
- package/lib/network/reqresp/handlers/index.js.map +1 -1
- package/lib/network/reqresp/protocols.d.ts +4 -0
- package/lib/network/reqresp/protocols.js +20 -0
- package/lib/network/reqresp/protocols.js.map +1 -1
- package/lib/network/reqresp/rateLimit.js +19 -3
- package/lib/network/reqresp/rateLimit.js.map +1 -1
- package/lib/network/reqresp/score.js +3 -0
- package/lib/network/reqresp/score.js.map +1 -1
- package/lib/network/reqresp/types.d.ts +13 -6
- package/lib/network/reqresp/types.js +14 -5
- package/lib/network/reqresp/types.js.map +1 -1
- package/lib/network/statusCache.d.ts +5 -5
- package/lib/network/statusCache.js.map +1 -1
- package/lib/network/subnets/interface.d.ts +3 -0
- package/lib/network/subnets/interface.js +14 -1
- package/lib/network/subnets/interface.js.map +1 -1
- package/lib/network/subnets/syncnetsService.js +4 -5
- package/lib/network/subnets/syncnetsService.js.map +1 -1
- package/lib/node/nodejs.js +1 -0
- package/lib/node/nodejs.js.map +1 -1
- package/lib/sync/constants.d.ts +18 -3
- package/lib/sync/constants.js +21 -3
- package/lib/sync/constants.js.map +1 -1
- package/lib/sync/interface.d.ts +2 -2
- package/lib/sync/interface.js +1 -1
- package/lib/sync/interface.js.map +1 -1
- package/lib/sync/range/batch.d.ts +17 -2
- package/lib/sync/range/batch.js +39 -7
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts +15 -1
- package/lib/sync/range/chain.js +124 -33
- package/lib/sync/range/chain.js.map +1 -1
- package/lib/sync/range/range.d.ts +3 -2
- package/lib/sync/range/range.js +9 -3
- package/lib/sync/range/range.js.map +1 -1
- package/lib/sync/range/utils/chainTarget.d.ts +5 -1
- package/lib/sync/range/utils/chainTarget.js +26 -1
- package/lib/sync/range/utils/chainTarget.js.map +1 -1
- package/lib/sync/range/utils/peerBalancer.d.ts +19 -5
- package/lib/sync/range/utils/peerBalancer.js +104 -10
- package/lib/sync/range/utils/peerBalancer.js.map +1 -1
- package/lib/sync/sync.js +1 -1
- package/lib/sync/sync.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +54 -5
- package/lib/sync/unknownBlock.js +321 -61
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/remoteSyncType.d.ts +4 -4
- package/lib/sync/utils/remoteSyncType.js.map +1 -1
- package/lib/util/blobs.d.ts +16 -4
- package/lib/util/blobs.js +122 -5
- package/lib/util/blobs.js.map +1 -1
- package/lib/util/dataColumns.d.ts +137 -0
- package/lib/util/dataColumns.js +358 -0
- package/lib/util/dataColumns.js.map +1 -0
- package/lib/util/metadata.d.ts +5 -0
- package/lib/util/metadata.js +10 -0
- package/lib/util/metadata.js.map +1 -1
- package/lib/util/sszBytes.d.ts +1 -0
- package/lib/util/sszBytes.js +17 -0
- package/lib/util/sszBytes.js.map +1 -1
- package/lib/util/types.d.ts +7 -0
- package/lib/util/types.js +3 -0
- package/lib/util/types.js.map +1 -1
- package/package.json +18 -18
- package/lib/network/reqresp/handlers/status.d.ts +0 -4
- package/lib/network/reqresp/handlers/status.js +0 -11
- package/lib/network/reqresp/handlers/status.js.map +0 -1
package/lib/sync/unknownBlock.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { INTERVALS_PER_SLOT } from "@lodestar/params";
|
|
1
|
+
import { ForkName, ForkSeq, INTERVALS_PER_SLOT } from "@lodestar/params";
|
|
2
2
|
import { fromHex, pruneSetToMax, toRootHex } from "@lodestar/utils";
|
|
3
3
|
import { sleep } from "@lodestar/utils";
|
|
4
4
|
import { BlockInputType } from "../chain/blocks/types.js";
|
|
5
5
|
import { BlockError, BlockErrorCode } from "../chain/errors/index.js";
|
|
6
|
-
import { NetworkEvent
|
|
6
|
+
import { NetworkEvent } from "../network/index.js";
|
|
7
7
|
import { beaconBlocksMaybeBlobsByRoot, unavailableBeaconBlobsByRoot, } from "../network/reqresp/beaconBlocksMaybeBlobsByRoot.js";
|
|
8
8
|
import { byteArrayEquals } from "../util/bytes.js";
|
|
9
9
|
import { shuffle } from "../util/shuffle.js";
|
|
10
|
+
import { sortBy } from "../util/sortBy.js";
|
|
10
11
|
import { wrapError } from "../util/wrapError.js";
|
|
12
|
+
import { MAX_CONCURRENT_REQUESTS } from "./constants.js";
|
|
11
13
|
import { PendingBlockStatus, PendingBlockType } from "./interface.js";
|
|
12
14
|
import { getAllDescendantBlocks, getDescendantBlocks, getUnknownAndAncestorBlocks } from "./utils/pendingBlocksTree.js";
|
|
13
15
|
const MAX_ATTEMPTS_PER_BLOCK = 5;
|
|
@@ -68,6 +70,21 @@ export class UnknownBlockSync {
|
|
|
68
70
|
this.logger.debug("Error handling unknownBlockParent event", {}, e);
|
|
69
71
|
}
|
|
70
72
|
};
|
|
73
|
+
this.onPeerConnected = (data) => {
|
|
74
|
+
try {
|
|
75
|
+
const peerId = data.peer;
|
|
76
|
+
const peerSyncMeta = this.network.getConnectedPeerSyncMeta(peerId);
|
|
77
|
+
this.peerBalancer.onPeerConnected(data.peer, peerSyncMeta);
|
|
78
|
+
this.triggerUnknownBlockSearch();
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
this.logger.debug("Error handling peerConnected event", {}, e);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
this.onPeerDisconnected = (data) => {
|
|
85
|
+
const peerId = data.peer;
|
|
86
|
+
this.peerBalancer.onPeerDisconnected(peerId);
|
|
87
|
+
};
|
|
71
88
|
/**
|
|
72
89
|
* Gather tip parent blocks with unknown parent and do a search for all of them
|
|
73
90
|
*/
|
|
@@ -105,16 +122,22 @@ export class UnknownBlockSync {
|
|
|
105
122
|
}
|
|
106
123
|
// most of the time there is exactly 1 unknown block
|
|
107
124
|
for (const block of unknowns) {
|
|
108
|
-
this.downloadBlock(block
|
|
125
|
+
this.downloadBlock(block).catch((e) => {
|
|
109
126
|
this.logger.debug("Unexpected error - downloadBlock", { root: block.blockRootHex }, e);
|
|
110
127
|
});
|
|
111
128
|
}
|
|
112
129
|
};
|
|
113
130
|
this.maxPendingBlocks = opts?.maxPendingBlocks ?? MAX_PENDING_BLOCKS;
|
|
114
131
|
this.proposerBoostSecWindow = this.config.SECONDS_PER_SLOT / INTERVALS_PER_SLOT;
|
|
132
|
+
this.peerBalancer = new UnknownBlockPeerBalancer(this.network.custodyConfig);
|
|
115
133
|
if (metrics) {
|
|
116
|
-
metrics.syncUnknownBlock.pendingBlocks.addCollect(() =>
|
|
117
|
-
|
|
134
|
+
metrics.syncUnknownBlock.pendingBlocks.addCollect(() => {
|
|
135
|
+
metrics.syncUnknownBlock.pendingBlocks.set(this.pendingBlocks.size);
|
|
136
|
+
metrics.syncUnknownBlock.knownBadBlocks.set(this.knownBadBlocks.size);
|
|
137
|
+
metrics.syncUnknownBlock.peerBalancer.peersMetaCount.set(this.peerBalancer.peersMeta.size);
|
|
138
|
+
metrics.syncUnknownBlock.peerBalancer.peersActiveRequestCount.set(this.peerBalancer.activeRequests.size);
|
|
139
|
+
metrics.syncUnknownBlock.peerBalancer.totalActiveRequests.set(this.peerBalancer.getTotalActiveRequests());
|
|
140
|
+
});
|
|
118
141
|
}
|
|
119
142
|
}
|
|
120
143
|
subscribeToNetwork() {
|
|
@@ -125,7 +148,8 @@ export class UnknownBlockSync {
|
|
|
125
148
|
this.network.events.on(NetworkEvent.unknownBlock, this.onUnknownBlock);
|
|
126
149
|
this.network.events.on(NetworkEvent.unknownBlockInput, this.onUnknownBlockInput);
|
|
127
150
|
this.network.events.on(NetworkEvent.unknownBlockParent, this.onUnknownParent);
|
|
128
|
-
this.network.events.on(NetworkEvent.peerConnected, this.
|
|
151
|
+
this.network.events.on(NetworkEvent.peerConnected, this.onPeerConnected);
|
|
152
|
+
this.network.events.on(NetworkEvent.peerDisconnected, this.onPeerDisconnected);
|
|
129
153
|
this.subscribedToNetworkEvents = true;
|
|
130
154
|
}
|
|
131
155
|
}
|
|
@@ -138,7 +162,8 @@ export class UnknownBlockSync {
|
|
|
138
162
|
this.network.events.off(NetworkEvent.unknownBlock, this.onUnknownBlock);
|
|
139
163
|
this.network.events.off(NetworkEvent.unknownBlockInput, this.onUnknownBlockInput);
|
|
140
164
|
this.network.events.off(NetworkEvent.unknownBlockParent, this.onUnknownParent);
|
|
141
|
-
this.network.events.off(NetworkEvent.peerConnected, this.
|
|
165
|
+
this.network.events.off(NetworkEvent.peerConnected, this.onPeerConnected);
|
|
166
|
+
this.network.events.off(NetworkEvent.peerDisconnected, this.onPeerDisconnected);
|
|
142
167
|
this.subscribedToNetworkEvents = false;
|
|
143
168
|
}
|
|
144
169
|
close() {
|
|
@@ -150,7 +175,7 @@ export class UnknownBlockSync {
|
|
|
150
175
|
}
|
|
151
176
|
/**
|
|
152
177
|
* When a blockInput comes with an unknown parent:
|
|
153
|
-
* - add the block to pendingBlocks with status downloaded
|
|
178
|
+
* - add the block to pendingBlocks with status downloaded or pending blockRootHex as key. This is similar to
|
|
154
179
|
* an `onUnknownBlock` event, but the blocks is downloaded.
|
|
155
180
|
* - add the parent root to pendingBlocks with status pending, parentBlockRootHex as key. This is
|
|
156
181
|
* the same to an `onUnknownBlock` event with parentBlockRootHex as root.
|
|
@@ -163,14 +188,26 @@ export class UnknownBlockSync {
|
|
|
163
188
|
// add 1 pending block with status downloaded
|
|
164
189
|
let pendingBlock = this.pendingBlocks.get(blockRootHex);
|
|
165
190
|
if (!pendingBlock) {
|
|
166
|
-
pendingBlock =
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
191
|
+
pendingBlock =
|
|
192
|
+
blockInput.type === BlockInputType.dataPromise
|
|
193
|
+
? {
|
|
194
|
+
unknownBlockType: PendingBlockType.UNKNOWN_DATA,
|
|
195
|
+
blockRootHex,
|
|
196
|
+
// this will be set after we download block
|
|
197
|
+
parentBlockRootHex: null,
|
|
198
|
+
blockInput,
|
|
199
|
+
peerIdStrs: new Set(),
|
|
200
|
+
status: PendingBlockStatus.pending,
|
|
201
|
+
downloadAttempts: 0,
|
|
202
|
+
}
|
|
203
|
+
: {
|
|
204
|
+
blockRootHex,
|
|
205
|
+
parentBlockRootHex,
|
|
206
|
+
blockInput,
|
|
207
|
+
peerIdStrs: new Set(),
|
|
208
|
+
status: PendingBlockStatus.downloaded,
|
|
209
|
+
downloadAttempts: 0,
|
|
210
|
+
};
|
|
174
211
|
this.pendingBlocks.set(blockRootHex, pendingBlock);
|
|
175
212
|
this.logger.verbose("Added unknown block parent to pendingBlocks", {
|
|
176
213
|
root: blockRootHex,
|
|
@@ -194,7 +231,7 @@ export class UnknownBlockSync {
|
|
|
194
231
|
if (blockInputOrRootHex.block !== null) {
|
|
195
232
|
const { block } = blockInputOrRootHex;
|
|
196
233
|
blockRootHex = toRootHex(this.config.getForkTypes(block.message.slot).BeaconBlock.hashTreeRoot(block.message));
|
|
197
|
-
unknownBlockType = PendingBlockType.
|
|
234
|
+
unknownBlockType = PendingBlockType.UNKNOWN_DATA;
|
|
198
235
|
}
|
|
199
236
|
else {
|
|
200
237
|
unknownBlockType = PendingBlockType.UNKNOWN_BLOCKINPUT;
|
|
@@ -207,6 +244,7 @@ export class UnknownBlockSync {
|
|
|
207
244
|
pendingBlock = {
|
|
208
245
|
unknownBlockType,
|
|
209
246
|
blockRootHex,
|
|
247
|
+
// this will be set after we download block
|
|
210
248
|
parentBlockRootHex: null,
|
|
211
249
|
blockInput,
|
|
212
250
|
peerIdStrs: new Set(),
|
|
@@ -230,24 +268,26 @@ export class UnknownBlockSync {
|
|
|
230
268
|
}
|
|
231
269
|
return unknownBlockType;
|
|
232
270
|
}
|
|
233
|
-
async downloadBlock(block
|
|
271
|
+
async downloadBlock(block) {
|
|
234
272
|
if (block.status !== PendingBlockStatus.pending) {
|
|
235
273
|
return;
|
|
236
274
|
}
|
|
237
275
|
const unknownBlockType = block.unknownBlockType;
|
|
238
|
-
|
|
276
|
+
const logCtx = {
|
|
239
277
|
root: block.blockRootHex,
|
|
240
278
|
pendingBlocks: this.pendingBlocks.size,
|
|
241
279
|
slot: block.blockInput?.block?.message.slot ?? "unknown",
|
|
242
280
|
unknownBlockType,
|
|
243
|
-
}
|
|
281
|
+
};
|
|
282
|
+
this.logger.verbose("Downloading unknown block", logCtx);
|
|
244
283
|
block.status = PendingBlockStatus.fetching;
|
|
245
284
|
let res;
|
|
246
285
|
if (block.blockInput === null) {
|
|
247
|
-
|
|
286
|
+
// we only have block root, and nothing else
|
|
287
|
+
res = await wrapError(this.fetchUnknownBlockRoot(fromHex(block.blockRootHex)));
|
|
248
288
|
}
|
|
249
289
|
else {
|
|
250
|
-
res = await wrapError(this.fetchUnavailableBlockInput(block.blockInput
|
|
290
|
+
res = await wrapError(this.fetchUnavailableBlockInput(block.blockInput));
|
|
251
291
|
}
|
|
252
292
|
if (res.err)
|
|
253
293
|
this.metrics?.syncUnknownBlock.downloadedBlocksError.inc();
|
|
@@ -255,6 +295,11 @@ export class UnknownBlockSync {
|
|
|
255
295
|
this.metrics?.syncUnknownBlock.downloadedBlocksSuccess.inc();
|
|
256
296
|
if (!res.err) {
|
|
257
297
|
const { blockInput, peerIdStr } = res.result;
|
|
298
|
+
// fetchUnknownBlockRoot and fetchUnavailableBlockInput should return available data BlockInput, throw error if not
|
|
299
|
+
if (blockInput.type === BlockInputType.dataPromise) {
|
|
300
|
+
// if there were any peers who would have had the missing datacolumns, it would have resulted in err
|
|
301
|
+
throw Error(`Expected BlockInput to be available, got dataPromise for ${block.blockRootHex}`);
|
|
302
|
+
}
|
|
258
303
|
block = {
|
|
259
304
|
...block,
|
|
260
305
|
status: PendingBlockStatus.downloaded,
|
|
@@ -300,7 +345,7 @@ export class UnknownBlockSync {
|
|
|
300
345
|
}
|
|
301
346
|
}
|
|
302
347
|
else {
|
|
303
|
-
// this allows to retry the download of the block
|
|
348
|
+
// block download has error, this allows to retry the download of the block
|
|
304
349
|
block.status = PendingBlockStatus.pending;
|
|
305
350
|
// parentSlot > finalizedSlot, continue downloading parent of parent
|
|
306
351
|
block.downloadAttempts++;
|
|
@@ -319,9 +364,21 @@ export class UnknownBlockSync {
|
|
|
319
364
|
/**
|
|
320
365
|
* Send block to the processor awaiting completition. If processed successfully, send all children to the processor.
|
|
321
366
|
* On error, remove and downscore all descendants.
|
|
367
|
+
* This function could run recursively for all descendant blocks
|
|
322
368
|
*/
|
|
323
369
|
async processBlock(pendingBlock) {
|
|
370
|
+
// pending block status is `downloaded` right after `downloadBlock`
|
|
371
|
+
// but could be `pending` if added by `onUnknownBlockParent` event and this function is called recursively
|
|
324
372
|
if (pendingBlock.status !== PendingBlockStatus.downloaded) {
|
|
373
|
+
if (pendingBlock.status === PendingBlockStatus.pending) {
|
|
374
|
+
const connectedPeers = this.network.getConnectedPeers();
|
|
375
|
+
if (connectedPeers.length === 0) {
|
|
376
|
+
this.logger.debug("No connected peers, skipping download block", { blockRoot: pendingBlock.blockRootHex });
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
// if the download is a success we'll call `processBlock()` for this block
|
|
380
|
+
await this.downloadBlock(pendingBlock);
|
|
381
|
+
}
|
|
325
382
|
return;
|
|
326
383
|
}
|
|
327
384
|
pendingBlock.status = PendingBlockStatus.processing;
|
|
@@ -403,83 +460,126 @@ export class UnknownBlockSync {
|
|
|
403
460
|
}
|
|
404
461
|
}
|
|
405
462
|
/**
|
|
406
|
-
*
|
|
407
|
-
*
|
|
463
|
+
* From a set of shuffled peers:
|
|
464
|
+
* - fetch the block
|
|
465
|
+
* - from deneb, fetch all missing blobs
|
|
466
|
+
* - from peerDAS, fetch sampled colmns
|
|
467
|
+
* TODO: this means we only have block root, and nothing else. Consider to reflect this in the function name
|
|
468
|
+
* prefulu, will attempt a max of `MAX_ATTEMPTS_PER_BLOCK` on different peers, postfulu we may attempt more as defined in `getMaxDownloadAttempts()` function
|
|
408
469
|
* Also verifies the received block root + returns the peer that provided the block for future downscoring.
|
|
409
470
|
*/
|
|
410
|
-
async fetchUnknownBlockRoot(blockRoot
|
|
411
|
-
const shuffledPeers = shuffle(connectedPeers);
|
|
471
|
+
async fetchUnknownBlockRoot(blockRoot) {
|
|
412
472
|
const blockRootHex = toRootHex(blockRoot);
|
|
473
|
+
const excludedPeers = new Set();
|
|
474
|
+
let partialDownload = null;
|
|
475
|
+
const defaultPendingColumns = this.config.getForkSeq(this.chain.clock.currentSlot) >= ForkSeq.fulu
|
|
476
|
+
? new Set(this.network.custodyConfig.sampleGroups)
|
|
477
|
+
: null;
|
|
413
478
|
let lastError = null;
|
|
414
|
-
|
|
415
|
-
|
|
479
|
+
let i = 0;
|
|
480
|
+
while (i++ < this.getMaxDownloadAttempts()) {
|
|
481
|
+
// pendingDataColumns is null prefulu
|
|
482
|
+
const peer = this.peerBalancer.bestPeerForPendingColumns(partialDownload ? new Set(partialDownload.pendingDataColumns) : defaultPendingColumns, excludedPeers);
|
|
483
|
+
if (peer === null) {
|
|
484
|
+
// no more peer with needed columns to try, throw error
|
|
485
|
+
throw Error(`Error fetching UnknownBlockRoot after ${i}: cannot find peer with needed columns ${partialDownload?.pendingDataColumns.join(", ")}`);
|
|
486
|
+
}
|
|
487
|
+
const { peerId, client: peerClient } = peer;
|
|
488
|
+
excludedPeers.add(peerId);
|
|
416
489
|
try {
|
|
417
|
-
const [blockInput] = await beaconBlocksMaybeBlobsByRoot(this.config, this.network,
|
|
490
|
+
const { blocks: [blockInput], pendingDataColumns, } = await beaconBlocksMaybeBlobsByRoot(this.config, this.network, peerId, [blockRoot], partialDownload, peerClient, this.metrics, this.logger);
|
|
418
491
|
// Peer does not have the block, try with next peer
|
|
419
492
|
if (blockInput === undefined) {
|
|
420
493
|
continue;
|
|
421
494
|
}
|
|
422
|
-
|
|
495
|
+
if (pendingDataColumns !== null) {
|
|
496
|
+
partialDownload = { blocks: [blockInput], pendingDataColumns };
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
// data is available, verify block root is correct
|
|
423
500
|
const block = blockInput.block.message;
|
|
424
501
|
const receivedBlockRoot = this.config.getForkTypes(block.slot).BeaconBlock.hashTreeRoot(block);
|
|
425
502
|
if (!byteArrayEquals(receivedBlockRoot, blockRoot)) {
|
|
426
503
|
throw Error(`Wrong block received by peer, got ${toRootHex(receivedBlockRoot)} expected ${blockRootHex}`);
|
|
427
504
|
}
|
|
428
|
-
return { blockInput, peerIdStr:
|
|
505
|
+
return { blockInput, peerIdStr: peerId };
|
|
429
506
|
}
|
|
430
507
|
catch (e) {
|
|
431
|
-
this.logger.debug("Error fetching UnknownBlockRoot", { attempt: i, blockRootHex, peer }, e);
|
|
508
|
+
this.logger.debug("Error fetching UnknownBlockRoot", { attempt: i, blockRootHex, peer: peerId }, e);
|
|
432
509
|
lastError = e;
|
|
433
510
|
}
|
|
511
|
+
finally {
|
|
512
|
+
this.peerBalancer.onRequestCompleted(peerId);
|
|
513
|
+
}
|
|
434
514
|
}
|
|
435
515
|
if (lastError) {
|
|
436
|
-
lastError.message = `Error fetching UnknownBlockRoot after ${
|
|
516
|
+
lastError.message = `Error fetching UnknownBlockRoot after ${i} attempts: ${lastError.message}`;
|
|
437
517
|
throw lastError;
|
|
438
518
|
}
|
|
439
|
-
throw Error(`Error fetching UnknownBlockRoot after ${
|
|
519
|
+
throw Error(`Error fetching UnknownBlockRoot after ${i}: cannot download all blobs or data columns for block ${blockRootHex}`);
|
|
440
520
|
}
|
|
441
521
|
/**
|
|
442
|
-
*
|
|
443
|
-
*
|
|
522
|
+
* We have partial block input:
|
|
523
|
+
* - we have block but not have all blobs (deneb) or needed columns (fulu)
|
|
524
|
+
* - we don't have block and have some blobs (deneb) or some columns (fulu)
|
|
525
|
+
* Fetches missing block/data columns/block for the blockinput. This function returns either preData or availableData BlockInput.
|
|
444
526
|
*/
|
|
445
|
-
async fetchUnavailableBlockInput(unavailableBlockInput
|
|
527
|
+
async fetchUnavailableBlockInput(unavailableBlockInput) {
|
|
446
528
|
if (unavailableBlockInput.block !== null && unavailableBlockInput.type !== BlockInputType.dataPromise) {
|
|
447
529
|
return { blockInput: unavailableBlockInput, peerIdStr: "" };
|
|
448
530
|
}
|
|
449
|
-
const shuffledPeers = shuffle(connectedPeers);
|
|
450
531
|
let blockRootHex;
|
|
451
|
-
let pendingBlobs;
|
|
452
532
|
let blobKzgCommitmentsLen;
|
|
453
533
|
let blockRoot;
|
|
534
|
+
const dataMeta = {};
|
|
535
|
+
let sampledColumns = [];
|
|
454
536
|
if (unavailableBlockInput.block === null) {
|
|
455
537
|
blockRootHex = unavailableBlockInput.blockRootHex;
|
|
456
538
|
blockRoot = fromHex(blockRootHex);
|
|
457
539
|
}
|
|
458
540
|
else {
|
|
459
|
-
const unavailableBlock = unavailableBlockInput
|
|
541
|
+
const { cachedData, block: unavailableBlock } = unavailableBlockInput;
|
|
460
542
|
blockRoot = this.config
|
|
461
543
|
.getForkTypes(unavailableBlock.message.slot)
|
|
462
544
|
.BeaconBlock.hashTreeRoot(unavailableBlock.message);
|
|
463
545
|
blockRootHex = toRootHex(blockRoot);
|
|
464
546
|
blobKzgCommitmentsLen = unavailableBlock.message.body.blobKzgCommitments.length;
|
|
465
|
-
|
|
547
|
+
if (cachedData.fork === ForkName.deneb || cachedData.fork === ForkName.electra) {
|
|
548
|
+
const pendingBlobs = blobKzgCommitmentsLen - cachedData.blobsCache.size;
|
|
549
|
+
Object.assign(dataMeta, { pendingBlobs });
|
|
550
|
+
}
|
|
551
|
+
else if (cachedData.fork === ForkName.fulu) {
|
|
552
|
+
sampledColumns = this.network.custodyConfig.sampledColumns;
|
|
553
|
+
const pendingColumns = sampledColumns.length - cachedData.dataColumnsCache.size;
|
|
554
|
+
Object.assign(dataMeta, { pendingColumns });
|
|
555
|
+
}
|
|
466
556
|
}
|
|
467
557
|
let lastError = null;
|
|
468
|
-
|
|
469
|
-
|
|
558
|
+
let i = 0;
|
|
559
|
+
const excludedPeers = new Set();
|
|
560
|
+
while (i++ < this.getMaxDownloadAttempts()) {
|
|
561
|
+
const bestPeer = this.peerBalancer.bestPeerForBlockInput(unavailableBlockInput, excludedPeers);
|
|
562
|
+
if (bestPeer === null) {
|
|
563
|
+
// no more peer to try, throw error
|
|
564
|
+
throw Error(`Error fetching UnavailableBlockInput after ${i}: cannot find peer with needed columns ${sampledColumns.join(", ")}`);
|
|
565
|
+
}
|
|
566
|
+
const { peerId, client: peerClient } = bestPeer;
|
|
567
|
+
excludedPeers.add(peerId);
|
|
470
568
|
try {
|
|
471
|
-
const blockInput = await unavailableBeaconBlobsByRoot(this.config, this.network,
|
|
569
|
+
const blockInput = await unavailableBeaconBlobsByRoot(this.config, this.network, peerId, peerClient, unavailableBlockInput, {
|
|
472
570
|
metrics: this.metrics,
|
|
473
|
-
|
|
571
|
+
logger: this.logger,
|
|
474
572
|
executionEngine: this.chain.executionEngine,
|
|
475
|
-
|
|
573
|
+
emitter: this.chain.emitter,
|
|
476
574
|
blockInputsRetryTrackerCache: this.blockInputsRetryTrackerCache,
|
|
575
|
+
engineGetBlobsCache: this.engineGetBlobsCache,
|
|
477
576
|
});
|
|
478
|
-
|
|
479
|
-
|
|
577
|
+
if (unavailableBlockInput.block !== null && blockInput.type === BlockInputType.dataPromise) {
|
|
578
|
+
// all datacolumns were not downloaded we can continue with other peers
|
|
579
|
+
// as unavailableBlockInput.block's dataColumnsCache would be updated
|
|
480
580
|
continue;
|
|
481
581
|
}
|
|
482
|
-
//
|
|
582
|
+
// data is available, verify block root is correct
|
|
483
583
|
const block = blockInput.block.message;
|
|
484
584
|
const receivedBlockRoot = this.config.getForkTypes(block.slot).BeaconBlock.hashTreeRoot(block);
|
|
485
585
|
if (!byteArrayEquals(receivedBlockRoot, blockRoot)) {
|
|
@@ -489,20 +589,23 @@ export class UnknownBlockSync {
|
|
|
489
589
|
this.logger.debug("Fetched NullBlockInput", { attempts: i, blockRootHex });
|
|
490
590
|
}
|
|
491
591
|
else {
|
|
492
|
-
this.logger.debug("Fetched UnavailableBlockInput", { attempts: i,
|
|
592
|
+
this.logger.debug("Fetched UnavailableBlockInput", { attempts: i, ...dataMeta, blobKzgCommitmentsLen });
|
|
493
593
|
}
|
|
494
|
-
return { blockInput, peerIdStr:
|
|
594
|
+
return { blockInput, peerIdStr: peerId };
|
|
495
595
|
}
|
|
496
596
|
catch (e) {
|
|
497
|
-
this.logger.debug("Error fetching UnavailableBlockInput", { attempt: i, blockRootHex, peer }, e);
|
|
597
|
+
this.logger.debug("Error fetching UnavailableBlockInput", { attempt: i, blockRootHex, peer: peerId }, e);
|
|
498
598
|
lastError = e;
|
|
499
599
|
}
|
|
600
|
+
finally {
|
|
601
|
+
this.peerBalancer.onRequestCompleted(peerId);
|
|
602
|
+
}
|
|
500
603
|
}
|
|
501
604
|
if (lastError) {
|
|
502
|
-
lastError.message = `Error fetching UnavailableBlockInput after ${
|
|
605
|
+
lastError.message = `Error fetching UnavailableBlockInput after ${i} attempts: ${lastError.message}`;
|
|
503
606
|
throw lastError;
|
|
504
607
|
}
|
|
505
|
-
throw Error(`Error fetching UnavailableBlockInput after ${
|
|
608
|
+
throw Error(`Error fetching UnavailableBlockInput after ${i}: unknown error`);
|
|
506
609
|
}
|
|
507
610
|
/**
|
|
508
611
|
* Gets all descendant blocks of `block` recursively from `pendingBlocks`.
|
|
@@ -513,13 +616,15 @@ export class UnknownBlockSync {
|
|
|
513
616
|
removeAndDownscoreAllDescendants(block) {
|
|
514
617
|
// Get all blocks that are a descendant of this one
|
|
515
618
|
const badPendingBlocks = this.removeAllDescendants(block);
|
|
619
|
+
// just console log and do not penalize on pending/bad blocks for debugging
|
|
620
|
+
// console.log("removeAndDownscoreAllDescendants", {block});
|
|
516
621
|
for (const block of badPendingBlocks) {
|
|
517
|
-
this.knownBadBlocks.add(block.blockRootHex);
|
|
518
|
-
for (const peerIdStr of block.peerIdStrs) {
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
}
|
|
522
|
-
this.logger.debug("Banning unknown block", {
|
|
622
|
+
// this.knownBadBlocks.add(block.blockRootHex);
|
|
623
|
+
// for (const peerIdStr of block.peerIdStrs) {
|
|
624
|
+
// // TODO: Refactor peerRpcScores to work with peerIdStr only
|
|
625
|
+
// this.network.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadBlockByRoot");
|
|
626
|
+
// }
|
|
627
|
+
this.logger.debug("ignored Banning unknown block", {
|
|
523
628
|
root: block.blockRootHex,
|
|
524
629
|
peerIdStrs: Array.from(block.peerIdStrs).join(","),
|
|
525
630
|
});
|
|
@@ -539,5 +644,160 @@ export class UnknownBlockSync {
|
|
|
539
644
|
}
|
|
540
645
|
return badPendingBlocks;
|
|
541
646
|
}
|
|
647
|
+
getMaxDownloadAttempts() {
|
|
648
|
+
if (this.config.getForkSeq(this.chain.clock.currentSlot) < ForkSeq.fulu) {
|
|
649
|
+
return MAX_ATTEMPTS_PER_BLOCK;
|
|
650
|
+
}
|
|
651
|
+
// TODO: I consider max 20 downloads per block for a supernode is enough for devnets
|
|
652
|
+
// review this computation for public testnets or mainnet
|
|
653
|
+
return Math.min(20, (MAX_ATTEMPTS_PER_BLOCK * this.network.custodyConfig.sampleGroups.length) / this.config.SAMPLES_PER_SLOT);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Class to track active byRoots requests and balance them across eligible peers.
|
|
658
|
+
*/
|
|
659
|
+
export class UnknownBlockPeerBalancer {
|
|
660
|
+
constructor(custodyConfig) {
|
|
661
|
+
this.peersMeta = new Map();
|
|
662
|
+
this.activeRequests = new Map();
|
|
663
|
+
this.custodyConfig = custodyConfig;
|
|
664
|
+
}
|
|
665
|
+
/** Trigger on each peer re-status */
|
|
666
|
+
onPeerConnected(peerId, syncMeta) {
|
|
667
|
+
this.peersMeta.set(peerId, syncMeta);
|
|
668
|
+
if (!this.activeRequests.has(peerId)) {
|
|
669
|
+
this.activeRequests.set(peerId, 0);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
onPeerDisconnected(peerId) {
|
|
673
|
+
this.peersMeta.delete(peerId);
|
|
674
|
+
this.activeRequests.delete(peerId);
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* called from fetchUnknownBlockRoot() where we only have block root and nothing else
|
|
678
|
+
* excludedPeers are the peers that we requested already so we don't want to try again
|
|
679
|
+
* pendingColumns is empty for prefulu, or the 1st time we we download a block by root
|
|
680
|
+
*/
|
|
681
|
+
bestPeerForPendingColumns(pendingColumns, excludedPeers) {
|
|
682
|
+
const eligiblePeers = this.filterPeers(pendingColumns, excludedPeers);
|
|
683
|
+
if (eligiblePeers.length === 0) {
|
|
684
|
+
return null;
|
|
685
|
+
}
|
|
686
|
+
const sortedEligiblePeers = sortBy(shuffle(eligiblePeers),
|
|
687
|
+
// prefer peers with least active req
|
|
688
|
+
(peerId) => this.activeRequests.get(peerId) ?? 0);
|
|
689
|
+
const bestPeerId = sortedEligiblePeers[0];
|
|
690
|
+
this.onRequest(bestPeerId);
|
|
691
|
+
return this.peersMeta.get(bestPeerId) ?? null;
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* called from fetchUnavailableBlockInput() where we have either BlockInput or NullBlockInput
|
|
695
|
+
* excludedPeers are the peers that we requested already so we don't want to try again
|
|
696
|
+
*/
|
|
697
|
+
bestPeerForBlockInput(unavailableBlockInput, excludedPeers) {
|
|
698
|
+
let cachedData = undefined;
|
|
699
|
+
if (unavailableBlockInput.block === null) {
|
|
700
|
+
// NullBlockInput
|
|
701
|
+
cachedData = unavailableBlockInput.cachedData;
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
// BlockInput
|
|
705
|
+
if (unavailableBlockInput.type !== BlockInputType.dataPromise) {
|
|
706
|
+
throw Error(`bestPeerForBlockInput called with BlockInput type ${unavailableBlockInput.type}, expected dataPromise`);
|
|
707
|
+
}
|
|
708
|
+
cachedData = unavailableBlockInput.cachedData;
|
|
709
|
+
}
|
|
710
|
+
const eligiblePeers = [];
|
|
711
|
+
if (cachedData.fork === ForkName.fulu) {
|
|
712
|
+
// cached data is CachedDataColumns
|
|
713
|
+
const { dataColumnsCache } = cachedData;
|
|
714
|
+
const pendingDataColumns = new Set();
|
|
715
|
+
for (const column of this.custodyConfig.sampledColumns) {
|
|
716
|
+
if (!dataColumnsCache.has(column)) {
|
|
717
|
+
pendingDataColumns.add(column);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
// there could be no pending column in case of NullBlockInput
|
|
721
|
+
eligiblePeers.push(...this.filterPeers(pendingDataColumns, excludedPeers));
|
|
722
|
+
}
|
|
723
|
+
else {
|
|
724
|
+
// prefulu
|
|
725
|
+
const pendingDataColumns = null;
|
|
726
|
+
eligiblePeers.push(...this.filterPeers(pendingDataColumns, excludedPeers));
|
|
727
|
+
}
|
|
728
|
+
if (eligiblePeers.length === 0) {
|
|
729
|
+
return null;
|
|
730
|
+
}
|
|
731
|
+
const sortedEligiblePeers = sortBy(shuffle(eligiblePeers),
|
|
732
|
+
// prefer peers with least active req
|
|
733
|
+
(peerId) => this.activeRequests.get(peerId) ?? 0);
|
|
734
|
+
const bestPeerId = sortedEligiblePeers[0];
|
|
735
|
+
this.onRequest(bestPeerId);
|
|
736
|
+
return this.peersMeta.get(bestPeerId) ?? null;
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Consumers don't need to call this method directly, it is called internally by bestPeer*() methods
|
|
740
|
+
* make this public for testing
|
|
741
|
+
*/
|
|
742
|
+
onRequest(peerId) {
|
|
743
|
+
this.activeRequests.set(peerId, (this.activeRequests.get(peerId) ?? 0) + 1);
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Consumers should call this method when a request is completed for a peer.
|
|
747
|
+
*/
|
|
748
|
+
onRequestCompleted(peerId) {
|
|
749
|
+
this.activeRequests.set(peerId, Math.max(0, (this.activeRequests.get(peerId) ?? 1) - 1));
|
|
750
|
+
}
|
|
751
|
+
getTotalActiveRequests() {
|
|
752
|
+
let totalActiveRequests = 0;
|
|
753
|
+
for (const count of this.activeRequests.values()) {
|
|
754
|
+
totalActiveRequests += count;
|
|
755
|
+
}
|
|
756
|
+
return totalActiveRequests;
|
|
757
|
+
}
|
|
758
|
+
// pendingDataColumns could be null for prefulu
|
|
759
|
+
filterPeers(pendingDataColumns, excludedPeers) {
|
|
760
|
+
let maxColumnCount = 0;
|
|
761
|
+
const considerPeers = [];
|
|
762
|
+
for (const [peerId, syncMeta] of this.peersMeta.entries()) {
|
|
763
|
+
if (excludedPeers.has(peerId)) {
|
|
764
|
+
// made request to this peer already
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
767
|
+
const activeRequests = this.activeRequests.get(peerId) ?? 0;
|
|
768
|
+
if (activeRequests >= MAX_CONCURRENT_REQUESTS) {
|
|
769
|
+
// should return peer with no more than MAX_CONCURRENT_REQUESTS active requests
|
|
770
|
+
continue;
|
|
771
|
+
}
|
|
772
|
+
if (pendingDataColumns === null || pendingDataColumns.size === 0) {
|
|
773
|
+
// prefulu, no pending columns
|
|
774
|
+
considerPeers.push({ peerId, columnCount: 0 });
|
|
775
|
+
continue;
|
|
776
|
+
}
|
|
777
|
+
// postfulu, find peers that have custody columns that we need
|
|
778
|
+
const { custodyGroups: peerColumns } = syncMeta;
|
|
779
|
+
// check if the peer has all needed columns
|
|
780
|
+
// get match
|
|
781
|
+
const columns = peerColumns.reduce((acc, elem) => {
|
|
782
|
+
if (pendingDataColumns.has(elem)) {
|
|
783
|
+
acc.push(elem);
|
|
784
|
+
}
|
|
785
|
+
return acc;
|
|
786
|
+
}, []);
|
|
787
|
+
if (columns.length > 0) {
|
|
788
|
+
if (columns.length > maxColumnCount) {
|
|
789
|
+
maxColumnCount = columns.length;
|
|
790
|
+
}
|
|
791
|
+
considerPeers.push({ peerId, columnCount: columns.length });
|
|
792
|
+
}
|
|
793
|
+
} // end for
|
|
794
|
+
const eligiblePeers = [];
|
|
795
|
+
for (const { peerId, columnCount } of considerPeers) {
|
|
796
|
+
if (columnCount === maxColumnCount) {
|
|
797
|
+
eligiblePeers.push(peerId);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
return eligiblePeers;
|
|
801
|
+
}
|
|
542
802
|
}
|
|
543
803
|
//# sourceMappingURL=unknownBlock.js.map
|