@lodestar/beacon-node 1.43.0-dev.a6d8600051 → 1.43.0-dev.ac258a8def
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 +10 -0
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/debug/index.d.ts.map +1 -1
- package/lib/api/impl/debug/index.js +0 -1
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +2 -1
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/blocks/blockInput/blockInput.d.ts +3 -0
- package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
- package/lib/chain/blocks/blockInput/blockInput.js +4 -1
- package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +16 -29
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +4 -0
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +15 -6
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +29 -17
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +11 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +24 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +16 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/types.d.ts +2 -1
- package/lib/chain/blocks/types.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.d.ts +1 -3
- package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.js +34 -23
- 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 -6
- package/lib/chain/blocks/verifyBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts +0 -4
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +5 -2
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts +2 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.js +16 -7
- package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +2 -2
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +10 -6
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +25 -10
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +0 -11
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/emitter.js +0 -4
- package/lib/chain/emitter.js.map +1 -1
- package/lib/chain/errors/proposerPreferences.d.ts +8 -1
- package/lib/chain/errors/proposerPreferences.d.ts.map +1 -1
- package/lib/chain/errors/proposerPreferences.js +1 -0
- package/lib/chain/errors/proposerPreferences.js.map +1 -1
- package/lib/chain/initState.d.ts.map +1 -1
- package/lib/chain/initState.js +6 -1
- package/lib/chain/initState.js.map +1 -1
- package/lib/chain/prepareNextSlot.js +1 -1
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +1 -0
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +2 -7
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +1 -4
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/regen/regen.d.ts.map +1 -1
- package/lib/chain/regen/regen.js +1 -4
- package/lib/chain/regen/regen.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +14 -10
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +63 -19
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/seenCache/seenProposerPreferences.d.ts +8 -7
- package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +1 -1
- package/lib/chain/seenCache/seenProposerPreferences.js +11 -10
- package/lib/chain/seenCache/seenProposerPreferences.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +11 -8
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
- package/lib/chain/validation/proposerPreferences.js +39 -17
- package/lib/chain/validation/proposerPreferences.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +2 -0
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/interface.d.ts +1 -0
- package/lib/network/interface.d.ts.map +1 -1
- package/lib/network/network.d.ts +1 -0
- package/lib/network/network.d.ts.map +1 -1
- package/lib/network/network.js +3 -0
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +24 -7
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/index.js +5 -5
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/ReqRespBeaconNode.d.ts.map +1 -1
- package/lib/network/reqresp/ReqRespBeaconNode.js +1 -1
- package/lib/network/reqresp/ReqRespBeaconNode.js.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByHead.d.ts +9 -0
- package/lib/network/reqresp/handlers/beaconBlocksByHead.d.ts.map +1 -0
- package/lib/network/reqresp/handlers/beaconBlocksByHead.js +61 -0
- package/lib/network/reqresp/handlers/beaconBlocksByHead.js.map +1 -0
- package/lib/network/reqresp/handlers/index.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/index.js +5 -0
- package/lib/network/reqresp/handlers/index.js.map +1 -1
- package/lib/network/reqresp/interface.d.ts +1 -1
- package/lib/network/reqresp/interface.js +1 -1
- package/lib/network/reqresp/protocols.d.ts +1 -0
- package/lib/network/reqresp/protocols.d.ts.map +1 -1
- package/lib/network/reqresp/protocols.js +5 -0
- package/lib/network/reqresp/protocols.js.map +1 -1
- package/lib/network/reqresp/rateLimit.d.ts.map +1 -1
- package/lib/network/reqresp/rateLimit.js +4 -0
- package/lib/network/reqresp/rateLimit.js.map +1 -1
- package/lib/network/reqresp/score.d.ts.map +1 -1
- package/lib/network/reqresp/score.js +1 -0
- package/lib/network/reqresp/score.js.map +1 -1
- package/lib/network/reqresp/types.d.ts +3 -0
- package/lib/network/reqresp/types.d.ts.map +1 -1
- package/lib/network/reqresp/types.js +3 -0
- package/lib/network/reqresp/types.js.map +1 -1
- package/lib/node/nodejs.js +2 -2
- package/lib/node/nodejs.js.map +1 -1
- package/lib/node/notifier.js +1 -7
- package/lib/node/notifier.js.map +1 -1
- package/lib/sync/constants.d.ts +7 -1
- package/lib/sync/constants.d.ts.map +1 -1
- package/lib/sync/constants.js +7 -4
- package/lib/sync/constants.js.map +1 -1
- package/lib/sync/range/batch.d.ts +23 -3
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +191 -36
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts +16 -2
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +99 -8
- 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 +54 -5
- package/lib/sync/range/range.js.map +1 -1
- package/lib/sync/sync.d.ts.map +1 -1
- package/lib/sync/sync.js +13 -0
- package/lib/sync/sync.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +7 -2
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +127 -57
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts +36 -9
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +106 -42
- package/lib/sync/utils/downloadByRange.js.map +1 -1
- package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRoot.js +10 -0
- package/lib/sync/utils/downloadByRoot.js.map +1 -1
- package/lib/sync/utils/rateLimit.d.ts +2 -0
- package/lib/sync/utils/rateLimit.d.ts.map +1 -0
- package/lib/sync/utils/rateLimit.js +15 -0
- package/lib/sync/utils/rateLimit.js.map +1 -0
- package/lib/util/sszBytes.d.ts.map +1 -1
- package/lib/util/sszBytes.js +8 -6
- package/lib/util/sszBytes.js.map +1 -1
- package/package.json +16 -16
- package/src/api/impl/beacon/blocks/index.ts +13 -0
- package/src/api/impl/debug/index.ts +0 -1
- package/src/api/impl/validator/index.ts +2 -1
- package/src/chain/blocks/blockInput/blockInput.ts +4 -1
- package/src/chain/blocks/importBlock.ts +16 -49
- package/src/chain/blocks/importExecutionPayload.ts +21 -11
- package/src/chain/blocks/index.ts +18 -9
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +32 -2
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +17 -0
- package/src/chain/blocks/types.ts +2 -1
- package/src/chain/blocks/utils/chainSegment.ts +35 -24
- package/src/chain/blocks/verifyBlock.ts +7 -5
- package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +6 -4
- package/src/chain/blocks/verifyBlocksSanityChecks.ts +16 -6
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +14 -6
- package/src/chain/chain.ts +29 -9
- package/src/chain/emitter.ts +0 -11
- package/src/chain/errors/proposerPreferences.ts +9 -1
- package/src/chain/initState.ts +9 -1
- package/src/chain/prepareNextSlot.ts +1 -1
- package/src/chain/produceBlock/produceBlockBody.ts +3 -7
- package/src/chain/regen/queued.ts +2 -7
- package/src/chain/regen/regen.ts +2 -7
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +78 -22
- package/src/chain/seenCache/seenProposerPreferences.ts +14 -11
- package/src/chain/validation/executionPayloadBid.ts +11 -8
- package/src/chain/validation/proposerPreferences.ts +37 -18
- package/src/network/interface.ts +1 -0
- package/src/network/network.ts +12 -0
- package/src/network/processor/gossipHandlers.ts +30 -7
- package/src/network/processor/index.ts +5 -5
- package/src/network/reqresp/ReqRespBeaconNode.ts +1 -0
- package/src/network/reqresp/handlers/beaconBlocksByHead.ts +91 -0
- package/src/network/reqresp/handlers/index.ts +5 -0
- package/src/network/reqresp/interface.ts +1 -1
- package/src/network/reqresp/protocols.ts +6 -0
- package/src/network/reqresp/rateLimit.ts +4 -0
- package/src/network/reqresp/score.ts +1 -0
- package/src/network/reqresp/types.ts +5 -0
- package/src/node/nodejs.ts +2 -2
- package/src/node/notifier.ts +1 -8
- package/src/sync/constants.ts +8 -4
- package/src/sync/range/batch.ts +240 -42
- package/src/sync/range/chain.ts +118 -9
- package/src/sync/range/range.ts +68 -5
- package/src/sync/sync.ts +13 -1
- package/src/sync/unknownBlock.ts +157 -60
- package/src/sync/utils/downloadByRange.ts +174 -44
- package/src/sync/utils/downloadByRoot.ts +12 -0
- package/src/sync/utils/rateLimit.ts +16 -0
- package/src/util/sszBytes.ts +8 -6
|
@@ -42,6 +42,7 @@ export enum ReqRespMethod {
|
|
|
42
42
|
Metadata = "metadata",
|
|
43
43
|
BeaconBlocksByRange = "beacon_blocks_by_range",
|
|
44
44
|
BeaconBlocksByRoot = "beacon_blocks_by_root",
|
|
45
|
+
BeaconBlocksByHead = "beacon_blocks_by_head",
|
|
45
46
|
BlobSidecarsByRange = "blob_sidecars_by_range",
|
|
46
47
|
BlobSidecarsByRoot = "blob_sidecars_by_root",
|
|
47
48
|
DataColumnSidecarsByRange = "data_column_sidecars_by_range",
|
|
@@ -62,6 +63,7 @@ export type RequestBodyByMethod = {
|
|
|
62
63
|
[ReqRespMethod.Metadata]: null;
|
|
63
64
|
[ReqRespMethod.BeaconBlocksByRange]: phase0.BeaconBlocksByRangeRequest;
|
|
64
65
|
[ReqRespMethod.BeaconBlocksByRoot]: BeaconBlocksByRootRequest;
|
|
66
|
+
[ReqRespMethod.BeaconBlocksByHead]: fulu.BeaconBlocksByHeadRequest;
|
|
65
67
|
[ReqRespMethod.BlobSidecarsByRange]: deneb.BlobSidecarsByRangeRequest;
|
|
66
68
|
[ReqRespMethod.BlobSidecarsByRoot]: BlobSidecarsByRootRequest;
|
|
67
69
|
[ReqRespMethod.DataColumnSidecarsByRange]: fulu.DataColumnSidecarsByRangeRequest;
|
|
@@ -82,6 +84,7 @@ type ResponseBodyByMethod = {
|
|
|
82
84
|
// Do not matter
|
|
83
85
|
[ReqRespMethod.BeaconBlocksByRange]: SignedBeaconBlock;
|
|
84
86
|
[ReqRespMethod.BeaconBlocksByRoot]: SignedBeaconBlock;
|
|
87
|
+
[ReqRespMethod.BeaconBlocksByHead]: SignedBeaconBlock;
|
|
85
88
|
[ReqRespMethod.BlobSidecarsByRange]: deneb.BlobSidecar;
|
|
86
89
|
[ReqRespMethod.BlobSidecarsByRoot]: deneb.BlobSidecar;
|
|
87
90
|
[ReqRespMethod.DataColumnSidecarsByRange]: DataColumnSidecar;
|
|
@@ -111,6 +114,7 @@ export const requestSszTypeByMethod: (
|
|
|
111
114
|
|
|
112
115
|
[ReqRespMethod.BeaconBlocksByRange]: ssz.phase0.BeaconBlocksByRangeRequest,
|
|
113
116
|
[ReqRespMethod.BeaconBlocksByRoot]: BeaconBlocksByRootRequestType(fork, config),
|
|
117
|
+
[ReqRespMethod.BeaconBlocksByHead]: ssz.fulu.BeaconBlocksByHeadRequest,
|
|
114
118
|
[ReqRespMethod.BlobSidecarsByRange]: ssz.deneb.BlobSidecarsByRangeRequest,
|
|
115
119
|
[ReqRespMethod.BlobSidecarsByRoot]: BlobSidecarsByRootRequestType(fork, config),
|
|
116
120
|
[ReqRespMethod.DataColumnSidecarsByRange]: ssz.fulu.DataColumnSidecarsByRangeRequest,
|
|
@@ -142,6 +146,7 @@ export const responseSszTypeByMethod: {[K in ReqRespMethod]: ResponseTypeGetter<
|
|
|
142
146
|
version === Version.V1 ? ssz.phase0.Metadata : version === Version.V2 ? ssz.altair.Metadata : ssz.fulu.Metadata,
|
|
143
147
|
[ReqRespMethod.BeaconBlocksByRange]: blocksResponseType,
|
|
144
148
|
[ReqRespMethod.BeaconBlocksByRoot]: blocksResponseType,
|
|
149
|
+
[ReqRespMethod.BeaconBlocksByHead]: (fork) => ssz[fork].SignedBeaconBlock,
|
|
145
150
|
[ReqRespMethod.BlobSidecarsByRange]: () => ssz.deneb.BlobSidecar,
|
|
146
151
|
[ReqRespMethod.BlobSidecarsByRoot]: () => ssz.deneb.BlobSidecar,
|
|
147
152
|
[ReqRespMethod.LightClientBootstrap]: (fork) => sszTypesFor(onlyPostAltairFork(fork)).LightClientBootstrap,
|
package/src/node/nodejs.ts
CHANGED
|
@@ -221,7 +221,7 @@ export class BeaconNode {
|
|
|
221
221
|
|
|
222
222
|
let executionEngineOpts = opts.executionEngine;
|
|
223
223
|
if (opts.executionEngine.mode === "mock") {
|
|
224
|
-
const
|
|
224
|
+
const latestEth1BlockHash =
|
|
225
225
|
isStatePostBellatrix(anchorState) && anchorState.isExecutionStateType
|
|
226
226
|
? isStatePostGloas(anchorState)
|
|
227
227
|
? toRootHex(anchorState.latestBlockHash)
|
|
@@ -230,7 +230,7 @@ export class BeaconNode {
|
|
|
230
230
|
executionEngineOpts = {
|
|
231
231
|
...opts.executionEngine,
|
|
232
232
|
genesisBlockHash: ZERO_HASH_HEX,
|
|
233
|
-
eth1BlockHash,
|
|
233
|
+
eth1BlockHash: opts.executionEngine.eth1BlockHash ?? latestEth1BlockHash,
|
|
234
234
|
genesisTime: anchorState.genesisTime,
|
|
235
235
|
config,
|
|
236
236
|
};
|
package/src/node/notifier.ts
CHANGED
|
@@ -167,14 +167,7 @@ function getHeadExecutionInfo(
|
|
|
167
167
|
return [];
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
|
|
171
|
-
// arrives, in that case the exec-block row surfaces the inherited parent anchor (from the
|
|
172
|
-
// bid), which is already validated. Normalize to "valid" to avoid leaking internal
|
|
173
|
-
// fork-choice bookkeeping into the log. Once the payload envelope arrives and the FULL
|
|
174
|
-
// variant becomes head, executionStatus is Valid/Syncing naturally.
|
|
175
|
-
// TODO GLOAS: revisit once optimistic sync is implemented
|
|
176
|
-
const executionStatusStr =
|
|
177
|
-
headInfo.executionStatus === ExecutionStatus.PayloadSeparated ? "valid" : headInfo.executionStatus.toLowerCase();
|
|
170
|
+
const executionStatusStr = headInfo.executionStatus.toLowerCase();
|
|
178
171
|
|
|
179
172
|
// Add execution status to notifier only if head is on/post bellatrix
|
|
180
173
|
if (isStatePostBellatrix(headState) && headState.isExecutionStateType) {
|
package/src/sync/constants.ts
CHANGED
|
@@ -5,10 +5,14 @@ export const PARALLEL_HEAD_CHAINS = 2;
|
|
|
5
5
|
export const MIN_FINALIZED_CHAIN_VALIDATED_EPOCHS = 10;
|
|
6
6
|
|
|
7
7
|
/** The number of times to retry a batch before it is considered failed. */
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
export const MAX_BATCH_DOWNLOAD_ATTEMPTS = 5;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Backoff before assigning more range-sync batches to a peer that rate-limited us.
|
|
12
|
+
*
|
|
13
|
+
* Note: this is used when rate limited due to MAX_CONCURRENT_REQUESTS
|
|
14
|
+
*/
|
|
15
|
+
export const RATE_LIMITED_PEER_BACKOFF_MS = 5_000;
|
|
12
16
|
|
|
13
17
|
/**
|
|
14
18
|
* Consider batch faulty after downloading and processing this number of times
|
package/src/sync/range/batch.ts
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
2
|
import {ForkName, isForkPostDeneb, isForkPostFulu, isForkPostGloas} from "@lodestar/params";
|
|
3
|
-
import {Epoch, RootHex, Slot, phase0} from "@lodestar/types";
|
|
4
|
-
import {LodestarError} from "@lodestar/utils";
|
|
3
|
+
import {Epoch, RootHex, SignedBeaconBlock, Slot, gloas, phase0} from "@lodestar/types";
|
|
4
|
+
import {LodestarError, byteArrayEquals, prettyPrintIndices, toRootHex} from "@lodestar/utils";
|
|
5
5
|
import {isBlockInputColumns} from "../../chain/blocks/blockInput/blockInput.js";
|
|
6
6
|
import {IBlockInput} from "../../chain/blocks/blockInput/types.js";
|
|
7
7
|
import {isDaOutOfRange} from "../../chain/blocks/blockInput/utils.js";
|
|
8
8
|
import {PayloadEnvelopeInput} from "../../chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js";
|
|
9
9
|
import {BlockError, BlockErrorCode} from "../../chain/errors/index.js";
|
|
10
|
+
import {ZERO_HASH} from "../../constants/constants.js";
|
|
10
11
|
import {PeerSyncMeta} from "../../network/peers/peersData.js";
|
|
11
12
|
import {IClock} from "../../util/clock.js";
|
|
12
13
|
import {CustodyConfig} from "../../util/dataColumns.js";
|
|
13
14
|
import {PeerIdStr} from "../../util/peerId.js";
|
|
14
15
|
import {MAX_BATCH_DOWNLOAD_ATTEMPTS, MAX_BATCH_PROCESSING_ATTEMPTS} from "../constants.js";
|
|
15
|
-
import {DownloadByRangeRequests} from "../utils/downloadByRange.js";
|
|
16
|
+
import {DownloadByRangeRequests, ParentPayloadCommitments} from "../utils/downloadByRange.js";
|
|
16
17
|
import {getBatchSlotRange, hashBlocks} from "./utils/index.js";
|
|
17
18
|
|
|
18
19
|
/**
|
|
@@ -79,10 +80,36 @@ export type BatchState =
|
|
|
79
80
|
};
|
|
80
81
|
|
|
81
82
|
export type BatchMetadata = {
|
|
83
|
+
// Batch-level slot window (always present)
|
|
82
84
|
startEpoch: Epoch;
|
|
85
|
+
startSlot: Slot;
|
|
86
|
+
count: number;
|
|
83
87
|
status: BatchStatus;
|
|
88
|
+
|
|
89
|
+
// Per-type outstanding request shapes; only present when that sub-request exists.
|
|
90
|
+
// Format: "startSlot=<n>,count=<n>" (plus ",cols=<indices>" for columns).
|
|
91
|
+
blocksReq?: string;
|
|
92
|
+
blobsReq?: string;
|
|
93
|
+
columnsReq?: string;
|
|
94
|
+
envelopesReq?: string;
|
|
95
|
+
|
|
96
|
+
// Retry counters
|
|
97
|
+
downloadAttempts: number;
|
|
98
|
+
processingAttempts: number;
|
|
99
|
+
|
|
100
|
+
// Cumulative peer attribution for failed attempts (only present when non-empty)
|
|
101
|
+
failedDownloadPeers?: string;
|
|
102
|
+
failedProcessingPeers?: string;
|
|
84
103
|
};
|
|
85
104
|
|
|
105
|
+
function formatRangeReq(req: {startSlot: Slot; count: number}): string {
|
|
106
|
+
return `startSlot=${req.startSlot},count=${req.count}`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function formatColumnsReq(req: {startSlot: Slot; count: number; columns: number[]}): string {
|
|
110
|
+
return `startSlot=${req.startSlot},count=${req.count},cols=${prettyPrintIndices(req.columns)}`;
|
|
111
|
+
}
|
|
112
|
+
|
|
86
113
|
/**
|
|
87
114
|
* Batches are downloaded at the first block of the epoch.
|
|
88
115
|
*
|
|
@@ -115,8 +142,18 @@ export class Batch {
|
|
|
115
142
|
private readonly config: ChainForkConfig;
|
|
116
143
|
private readonly clock: IClock;
|
|
117
144
|
private readonly custodyConfig: CustodyConfig;
|
|
118
|
-
|
|
119
|
-
|
|
145
|
+
private readonly isFirstBatchInChain: boolean;
|
|
146
|
+
private readonly latestBid: gloas.ExecutionPayloadBid | undefined;
|
|
147
|
+
|
|
148
|
+
constructor(
|
|
149
|
+
startEpoch: Epoch,
|
|
150
|
+
config: ChainForkConfig,
|
|
151
|
+
clock: IClock,
|
|
152
|
+
custodyConfig: CustodyConfig,
|
|
153
|
+
isFirstBatchInChain: boolean,
|
|
154
|
+
latestBid: gloas.ExecutionPayloadBid | undefined,
|
|
155
|
+
targetSlot: Slot
|
|
156
|
+
) {
|
|
120
157
|
this.config = config;
|
|
121
158
|
this.clock = clock;
|
|
122
159
|
this.custodyConfig = custodyConfig;
|
|
@@ -125,10 +162,40 @@ export class Batch {
|
|
|
125
162
|
this.forkName = this.config.getForkName(startSlot);
|
|
126
163
|
this.startEpoch = startEpoch;
|
|
127
164
|
this.startSlot = startSlot;
|
|
128
|
-
this.count = count;
|
|
165
|
+
this.count = Math.min(count, targetSlot - startSlot + 1);
|
|
166
|
+
this.isFirstBatchInChain = isFirstBatchInChain;
|
|
167
|
+
this.latestBid = latestBid;
|
|
129
168
|
this.requests = this.getRequests([]);
|
|
130
169
|
}
|
|
131
170
|
|
|
171
|
+
private shouldDownloadParentEnvelope(firstBlock?: SignedBeaconBlock): boolean {
|
|
172
|
+
if (!this.isFirstBatchInChain) return false;
|
|
173
|
+
|
|
174
|
+
if (this.startSlot === 0 || !isForkPostGloas(this.config.getForkName(this.startSlot - 1))) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// we only know if we should download parent envelope if firstBlock is downloaded
|
|
179
|
+
if (firstBlock === undefined) return false;
|
|
180
|
+
if (this.latestBid === undefined) return false;
|
|
181
|
+
const firstBlockBidParentHash = (firstBlock.message.body as gloas.BeaconBlockBody).signedExecutionPayloadBid.message
|
|
182
|
+
.parentBlockHash;
|
|
183
|
+
return byteArrayEquals(firstBlockBidParentHash, this.latestBid.blockHash);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
getParentPayloadCommitments(parentBlockRoot: Uint8Array): ParentPayloadCommitments {
|
|
187
|
+
if (this.latestBid === undefined) {
|
|
188
|
+
throw new Error(
|
|
189
|
+
`Coding error: getParentPayloadCommitments called without latestBid for parentBlockRoot=${toRootHex(parentBlockRoot)}`
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
blockRoot: parentBlockRoot,
|
|
194
|
+
blockRootHex: toRootHex(parentBlockRoot),
|
|
195
|
+
kzgCommitments: this.latestBid.blobKzgCommitments,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
132
199
|
/**
|
|
133
200
|
* Builds ByRange requests for block, blobs and columns
|
|
134
201
|
*/
|
|
@@ -176,6 +243,7 @@ export class Batch {
|
|
|
176
243
|
const envelopesBySlot = this.state.payloadEnvelopes ?? new Map<Slot, PayloadEnvelopeInput>();
|
|
177
244
|
|
|
178
245
|
// ensure blocks are in slot-wise order
|
|
246
|
+
const isPostGloas = isForkPostGloas(this.forkName);
|
|
179
247
|
for (const blockInput of blocks) {
|
|
180
248
|
const blockSlot = blockInput.slot;
|
|
181
249
|
// check if block/data is present (hasBlock/hasAllData). If present then check if startSlot is the same as
|
|
@@ -191,21 +259,36 @@ export class Batch {
|
|
|
191
259
|
if (blockInput.hasBlock() && blockStartSlot === blockSlot) {
|
|
192
260
|
blockStartSlot = blockSlot + 1;
|
|
193
261
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
262
|
+
|
|
263
|
+
// Range sync uses hasComputedAllData (all sampled columns physically present), not hasAllData
|
|
264
|
+
// which flips at the reconstruction threshold. Sync never triggers reconstruction, so accepting
|
|
265
|
+
// a half-downloaded block here makes writeBlockInputToDb later block on waitForComputedAllData.
|
|
266
|
+
if (isPostGloas) {
|
|
267
|
+
// Post-Gloas: column data lives on PayloadEnvelopeInput, not on BlockInputNoData.
|
|
268
|
+
const payloadInput = envelopesBySlot.get(blockSlot);
|
|
269
|
+
if (blockInput.hasBlock() && envelopeStartSlot === blockSlot && payloadInput?.hasPayloadEnvelope()) {
|
|
270
|
+
envelopeStartSlot = blockSlot + 1;
|
|
271
|
+
}
|
|
272
|
+
if (payloadInput && !payloadInput.hasComputedAllData()) {
|
|
273
|
+
for (const index of payloadInput.getMissingSampledColumnMeta().missing) {
|
|
204
274
|
neededColumns.add(index);
|
|
205
275
|
}
|
|
276
|
+
} else if (payloadInput?.hasComputedAllData() && dataStartSlot === blockSlot) {
|
|
277
|
+
// Only advance dataStartSlot when we know columns for this slot are complete. If
|
|
278
|
+
// payloadInput is missing entirely we cannot tell, so stop here so the next round
|
|
279
|
+
// re-requests columns (and envelopes) starting at this slot.
|
|
280
|
+
dataStartSlot = blockSlot + 1;
|
|
281
|
+
}
|
|
282
|
+
} else {
|
|
283
|
+
if (isBlockInputColumns(blockInput) ? !blockInput.hasComputedAllData() : !blockInput.hasAllData()) {
|
|
284
|
+
if (isBlockInputColumns(blockInput)) {
|
|
285
|
+
for (const index of blockInput.getMissingSampledColumnMeta().missing) {
|
|
286
|
+
neededColumns.add(index);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
} else if (dataStartSlot === blockSlot) {
|
|
290
|
+
dataStartSlot = blockSlot + 1;
|
|
206
291
|
}
|
|
207
|
-
} else if (dataStartSlot === blockSlot) {
|
|
208
|
-
dataStartSlot = blockSlot + 1;
|
|
209
292
|
}
|
|
210
293
|
}
|
|
211
294
|
|
|
@@ -225,11 +308,15 @@ export class Batch {
|
|
|
225
308
|
// range of 40 - 63, startSlot will be inclusive but subtraction will exclusive so need to + 1
|
|
226
309
|
const count = endSlot - dataStartSlot + 1;
|
|
227
310
|
if (isForkPostFulu(this.forkName) && withinValidRequestWindow) {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
311
|
+
// Skip the column re-request when we have no specific column indices outstanding.
|
|
312
|
+
// Peer rejects an empty `columns` list
|
|
313
|
+
if (neededColumns.size > 0) {
|
|
314
|
+
requests.columnsRequest = {
|
|
315
|
+
count,
|
|
316
|
+
startSlot: dataStartSlot,
|
|
317
|
+
columns: Array.from(neededColumns),
|
|
318
|
+
};
|
|
319
|
+
}
|
|
233
320
|
} else if (isForkPostDeneb(this.forkName) && withinValidRequestWindow) {
|
|
234
321
|
requests.blobsRequest = {
|
|
235
322
|
count,
|
|
@@ -246,6 +333,36 @@ export class Batch {
|
|
|
246
333
|
};
|
|
247
334
|
}
|
|
248
335
|
|
|
336
|
+
// Only the first batch of a SyncChain may need the dangling-parent payload by-root.
|
|
337
|
+
if (blocks.length > 0 && this.shouldDownloadParentEnvelope(blocks[0].getBlock())) {
|
|
338
|
+
// shouldDownloadParentEnvelope() = true means there are at least 1 block
|
|
339
|
+
const parentRoot = blocks[0].getBlock().message.parentRoot;
|
|
340
|
+
if (!byteArrayEquals(parentRoot, ZERO_HASH)) {
|
|
341
|
+
const parentRootHex = toRootHex(parentRoot);
|
|
342
|
+
let parentPayloadInput: PayloadEnvelopeInput | undefined;
|
|
343
|
+
if (this.state.payloadEnvelopes) {
|
|
344
|
+
for (const pi of this.state.payloadEnvelopes.values()) {
|
|
345
|
+
if (pi.blockRootHex === parentRootHex) {
|
|
346
|
+
parentPayloadInput = pi;
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const needsEnvelope = !parentPayloadInput?.hasPayloadEnvelope();
|
|
353
|
+
const missingColumns = parentPayloadInput
|
|
354
|
+
? parentPayloadInput.getMissingSampledColumnMeta().missing
|
|
355
|
+
: this.custodyConfig.sampledColumns;
|
|
356
|
+
|
|
357
|
+
if (needsEnvelope || missingColumns.length > 0) {
|
|
358
|
+
requests.parentPayloadRequest = {
|
|
359
|
+
...(needsEnvelope ? {envelopeBlockRoot: parentRoot} : {}),
|
|
360
|
+
...(missingColumns.length > 0 ? {blockRoot: parentRoot, columns: missingColumns} : {}),
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
249
366
|
return requests;
|
|
250
367
|
}
|
|
251
368
|
|
|
@@ -257,24 +374,28 @@ export class Batch {
|
|
|
257
374
|
return this.requests;
|
|
258
375
|
}
|
|
259
376
|
|
|
260
|
-
// post-fulu we need to ensure that we only request columns that the peer has advertised
|
|
261
|
-
const {columnsRequest} = this.requests;
|
|
262
|
-
if (columnsRequest == null) {
|
|
263
|
-
return this.requests;
|
|
264
|
-
}
|
|
377
|
+
// post-fulu we need to ensure that we only request columns that the peer has advertised.
|
|
378
|
+
const {columnsRequest, parentPayloadRequest} = this.requests;
|
|
265
379
|
|
|
266
380
|
const peerColumns = new Set(peer.custodyColumns ?? []);
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
381
|
+
const filteredColumnsRequest =
|
|
382
|
+
columnsRequest != null ? columnsRequest.columns.filter((c) => peerColumns.has(c)) : null;
|
|
383
|
+
const parentColumns = parentPayloadRequest?.columns;
|
|
384
|
+
const filteredParentColumns = parentColumns != null ? parentColumns.filter((c) => peerColumns.has(c)) : null;
|
|
385
|
+
|
|
386
|
+
const updatedColumnRequest =
|
|
387
|
+
columnsRequest != null && filteredColumnsRequest != null
|
|
388
|
+
? {columnsRequest: {...columnsRequest, columns: filteredColumnsRequest}}
|
|
389
|
+
: {};
|
|
390
|
+
const updatedParentPayloadRequest =
|
|
391
|
+
parentPayloadRequest != null && filteredParentColumns != null
|
|
392
|
+
? {parentPayloadRequest: {...parentPayloadRequest, columns: filteredParentColumns}}
|
|
393
|
+
: {};
|
|
271
394
|
|
|
272
395
|
return {
|
|
273
396
|
...this.requests,
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
columns: requestedColumns,
|
|
277
|
-
},
|
|
397
|
+
...updatedColumnRequest,
|
|
398
|
+
...updatedParentPayloadRequest,
|
|
278
399
|
};
|
|
279
400
|
}
|
|
280
401
|
|
|
@@ -286,7 +407,26 @@ export class Batch {
|
|
|
286
407
|
}
|
|
287
408
|
|
|
288
409
|
getMetadata(): BatchMetadata {
|
|
289
|
-
|
|
410
|
+
const {blocksRequest, blobsRequest, columnsRequest, envelopesRequest} = this.requests;
|
|
411
|
+
const failedProcessingPeerList = this.failedProcessingAttempts.flatMap((a) => a.peers);
|
|
412
|
+
return {
|
|
413
|
+
startEpoch: this.startEpoch,
|
|
414
|
+
startSlot: this.startSlot,
|
|
415
|
+
count: this.count,
|
|
416
|
+
status: this.state.status,
|
|
417
|
+
...(blocksRequest && {blocksReq: formatRangeReq(blocksRequest)}),
|
|
418
|
+
...(blobsRequest && {blobsReq: formatRangeReq(blobsRequest)}),
|
|
419
|
+
...(columnsRequest && {columnsReq: formatColumnsReq(columnsRequest)}),
|
|
420
|
+
...(envelopesRequest && {envelopesReq: formatRangeReq(envelopesRequest)}),
|
|
421
|
+
downloadAttempts: this.failedDownloadAttempts.length,
|
|
422
|
+
processingAttempts: this.failedProcessingAttempts.length,
|
|
423
|
+
...(this.failedDownloadAttempts.length > 0 && {
|
|
424
|
+
failedDownloadPeers: this.failedDownloadAttempts.join(","),
|
|
425
|
+
}),
|
|
426
|
+
...(failedProcessingPeerList.length > 0 && {
|
|
427
|
+
failedProcessingPeers: failedProcessingPeerList.join(","),
|
|
428
|
+
}),
|
|
429
|
+
};
|
|
290
430
|
}
|
|
291
431
|
|
|
292
432
|
getBlocks(): IBlockInput[] {
|
|
@@ -334,7 +474,11 @@ export class Batch {
|
|
|
334
474
|
const slots = new Set<number>();
|
|
335
475
|
for (const block of blocks) {
|
|
336
476
|
slots.add(block.slot);
|
|
337
|
-
|
|
477
|
+
const dataComplete = isBlockInputColumns(block)
|
|
478
|
+
? // by_range needs to download all columns
|
|
479
|
+
block.hasBlock() && block.hasComputedAllData()
|
|
480
|
+
: block.hasBlockAndAllData();
|
|
481
|
+
if (!dataComplete) {
|
|
338
482
|
allComplete = false;
|
|
339
483
|
}
|
|
340
484
|
}
|
|
@@ -350,11 +494,45 @@ export class Batch {
|
|
|
350
494
|
}
|
|
351
495
|
const newPayloadEnvelopes = payloadEnvelopes ?? this.state.payloadEnvelopes;
|
|
352
496
|
|
|
497
|
+
if (allComplete && isForkPostGloas(this.forkName)) {
|
|
498
|
+
for (const block of blocks) {
|
|
499
|
+
const payloadInput = newPayloadEnvelopes?.get(block.slot);
|
|
500
|
+
// by_range needs every block's envelope and all sampled columns.
|
|
501
|
+
if (!payloadInput?.hasPayloadEnvelope() || !payloadInput.hasComputedAllData()) {
|
|
502
|
+
allComplete = false;
|
|
503
|
+
break;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// First batch of a sync chain must additionally have the dangling-parent payload fully
|
|
509
|
+
// present, otherwise `processBlocks` will throw PARENT_PAYLOAD_UNKNOWN. The parent's
|
|
510
|
+
// `PayloadEnvelopeInput` is identified by `blockRootHex` matching `blocks[0].parentRoot`.
|
|
511
|
+
if (allComplete && blocks.length > 0 && this.shouldDownloadParentEnvelope(blocks[0].getBlock())) {
|
|
512
|
+
const parentRoot = blocks[0].getBlock().message.parentRoot;
|
|
513
|
+
// Genesis has no parent payload — nothing to wait for.
|
|
514
|
+
if (!byteArrayEquals(parentRoot, ZERO_HASH)) {
|
|
515
|
+
const parentRootHex = toRootHex(parentRoot);
|
|
516
|
+
let parentPayloadComplete = false;
|
|
517
|
+
if (newPayloadEnvelopes) {
|
|
518
|
+
for (const payloadInput of newPayloadEnvelopes.values()) {
|
|
519
|
+
if (payloadInput.blockRootHex === parentRootHex) {
|
|
520
|
+
parentPayloadComplete = payloadInput.hasPayloadEnvelope() && payloadInput.hasComputedAllData();
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
if (!parentPayloadComplete) {
|
|
526
|
+
allComplete = false;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
353
531
|
if (allComplete) {
|
|
354
532
|
this.state = {status: BatchStatus.AwaitingProcessing, blocks, payloadEnvelopes: newPayloadEnvelopes};
|
|
355
533
|
} else {
|
|
356
|
-
this.requests = this.getRequests(blocks);
|
|
357
534
|
this.state = {status: BatchStatus.AwaitingDownload, blocks, payloadEnvelopes: newPayloadEnvelopes};
|
|
535
|
+
this.requests = this.getRequests(blocks);
|
|
358
536
|
}
|
|
359
537
|
|
|
360
538
|
return this.state as DownloadSuccessState;
|
|
@@ -380,10 +558,30 @@ export class Batch {
|
|
|
380
558
|
};
|
|
381
559
|
}
|
|
382
560
|
|
|
561
|
+
/**
|
|
562
|
+
* Downloading -> AwaitingDownload (without counting as a failed attempt).
|
|
563
|
+
* Used when the peer rate-limited us — the request was never actually served.
|
|
564
|
+
*/
|
|
565
|
+
downloadingRateLimited(): void {
|
|
566
|
+
if (this.state.status !== BatchStatus.Downloading) {
|
|
567
|
+
throw new BatchError(this.wrongStatusErrorType(BatchStatus.Downloading));
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
this.state = {
|
|
571
|
+
status: BatchStatus.AwaitingDownload,
|
|
572
|
+
blocks: this.state.blocks,
|
|
573
|
+
payloadEnvelopes: this.state.payloadEnvelopes,
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
|
|
383
577
|
/**
|
|
384
578
|
* AwaitingProcessing -> Processing
|
|
385
579
|
*/
|
|
386
|
-
startProcessing(): {
|
|
580
|
+
startProcessing(): {
|
|
581
|
+
blocks: IBlockInput[];
|
|
582
|
+
payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null;
|
|
583
|
+
peers: PeerIdStr[];
|
|
584
|
+
} {
|
|
387
585
|
if (this.state.status !== BatchStatus.AwaitingProcessing) {
|
|
388
586
|
throw new BatchError(this.wrongStatusErrorType(BatchStatus.AwaitingProcessing));
|
|
389
587
|
}
|
|
@@ -396,7 +594,7 @@ export class Batch {
|
|
|
396
594
|
const peers = this.goodPeers;
|
|
397
595
|
this.goodPeers = [];
|
|
398
596
|
this.state = {status: BatchStatus.Processing, blocks, payloadEnvelopes, attempt: {peers, hash}};
|
|
399
|
-
return {blocks, payloadEnvelopes};
|
|
597
|
+
return {blocks, payloadEnvelopes, peers};
|
|
400
598
|
}
|
|
401
599
|
|
|
402
600
|
/**
|
|
@@ -479,7 +677,7 @@ export class Batch {
|
|
|
479
677
|
|
|
480
678
|
/** Helper to construct typed BatchError. Stack traces are correct as the error is thrown above */
|
|
481
679
|
private errorType(type: BatchErrorType): BatchErrorType & BatchErrorMetadata {
|
|
482
|
-
return {...type,
|
|
680
|
+
return {...type, startEpoch: this.startEpoch, status: this.state.status};
|
|
483
681
|
}
|
|
484
682
|
|
|
485
683
|
private wrongStatusErrorType(expectedStatus: BatchStatus): BatchErrorType & BatchErrorMetadata {
|