@lodestar/beacon-node 1.43.0-dev.4358217e12 → 1.43.0-dev.4451fec75a
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/beacon/pool/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +45 -2
- package/lib/api/impl/beacon/pool/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 +68 -2
- 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 -31
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +6 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +19 -10
- 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 +35 -21
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +12 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +28 -2
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +17 -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 +2 -1
- package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.js +30 -12
- 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/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -1
- package/lib/chain/blocks/verifyPayloadsDataAvailability.js +8 -3
- package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +25 -8
- 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/lightClient/index.d.ts.map +1 -1
- package/lib/chain/lightClient/index.js +1 -1
- package/lib/chain/lightClient/index.js.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.d.ts +2 -1
- package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.js +52 -10
- package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +16 -18
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +12 -3
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +33 -20
- 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 +21 -11
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +70 -20
- 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/payloadAttestationMessage.d.ts +1 -1
- package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.js +5 -3
- package/lib/chain/validation/payloadAttestationMessage.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/metrics/metrics/lodestar.d.ts +4 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +5 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +751 -2
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/interface.d.ts +2 -0
- package/lib/network/interface.d.ts.map +1 -1
- package/lib/network/network.d.ts +2 -0
- package/lib/network/network.d.ts.map +1 -1
- package/lib/network/network.js +8 -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 +61 -13
- 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 +43 -6
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +243 -44
- 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 +100 -9
- 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/range/utils/peerBalancer.d.ts +2 -1
- package/lib/sync/range/utils/peerBalancer.d.ts.map +1 -1
- package/lib/sync/range/utils/peerBalancer.js +8 -4
- package/lib/sync/range/utils/peerBalancer.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 +15 -16
- package/src/api/impl/beacon/blocks/index.ts +13 -0
- package/src/api/impl/beacon/pool/index.ts +83 -1
- package/src/api/impl/debug/index.ts +0 -1
- package/src/api/impl/validator/index.ts +82 -1
- package/src/chain/blocks/blockInput/blockInput.ts +4 -1
- package/src/chain/blocks/importBlock.ts +16 -50
- package/src/chain/blocks/importExecutionPayload.ts +32 -15
- package/src/chain/blocks/index.ts +32 -15
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +37 -3
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +18 -0
- package/src/chain/blocks/types.ts +2 -1
- package/src/chain/blocks/utils/chainSegment.ts +35 -24
- package/src/chain/blocks/verifyBlock.ts +45 -13
- 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/blocks/verifyPayloadsDataAvailability.ts +7 -4
- package/src/chain/chain.ts +29 -7
- 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/lightClient/index.ts +6 -6
- package/src/chain/opPools/payloadAttestationPool.ts +59 -10
- package/src/chain/prepareNextSlot.ts +21 -29
- package/src/chain/produceBlock/produceBlockBody.ts +41 -25
- package/src/chain/regen/queued.ts +2 -7
- package/src/chain/regen/regen.ts +2 -7
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +90 -24
- package/src/chain/seenCache/seenProposerPreferences.ts +14 -11
- package/src/chain/validation/executionPayloadBid.ts +11 -8
- package/src/chain/validation/payloadAttestationMessage.ts +6 -4
- package/src/chain/validation/proposerPreferences.ts +37 -18
- package/src/metrics/metrics/lodestar.ts +6 -0
- package/src/network/interface.ts +2 -0
- package/src/network/network.ts +23 -0
- package/src/network/processor/gossipHandlers.ts +75 -14
- 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 +309 -51
- package/src/sync/range/chain.ts +119 -10
- package/src/sync/range/range.ts +68 -5
- package/src/sync/range/utils/peerBalancer.ts +9 -3
- 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
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
|
/**
|
|
@@ -44,6 +45,15 @@ export type Attempt = {
|
|
|
44
45
|
hash: RootHex;
|
|
45
46
|
};
|
|
46
47
|
|
|
48
|
+
type TrackedRequest = {
|
|
49
|
+
/** only happen for the 1st batch in checkpoint sync */
|
|
50
|
+
parentPayload: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* we always issue by_range before parent_payload, so we don't model this as null
|
|
53
|
+
*/
|
|
54
|
+
byRangeColumns: Set<number>;
|
|
55
|
+
};
|
|
56
|
+
|
|
47
57
|
export type AwaitingDownloadState = {
|
|
48
58
|
status: BatchStatus.AwaitingDownload;
|
|
49
59
|
blocks: IBlockInput[];
|
|
@@ -61,6 +71,7 @@ export type BatchState =
|
|
|
61
71
|
| {
|
|
62
72
|
status: BatchStatus.Downloading;
|
|
63
73
|
peer: PeerIdStr;
|
|
74
|
+
request: TrackedRequest;
|
|
64
75
|
blocks: IBlockInput[];
|
|
65
76
|
payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null;
|
|
66
77
|
}
|
|
@@ -79,10 +90,43 @@ export type BatchState =
|
|
|
79
90
|
};
|
|
80
91
|
|
|
81
92
|
export type BatchMetadata = {
|
|
93
|
+
// Batch-level slot window (always present)
|
|
82
94
|
startEpoch: Epoch;
|
|
95
|
+
startSlot: Slot;
|
|
96
|
+
count: number;
|
|
83
97
|
status: BatchStatus;
|
|
98
|
+
|
|
99
|
+
// Per-type outstanding request shapes; only present when that sub-request exists.
|
|
100
|
+
// Format: "startSlot=<n>,count=<n>" (plus ",cols=<indices>" for columns).
|
|
101
|
+
blocksReq?: string;
|
|
102
|
+
blobsReq?: string;
|
|
103
|
+
columnsReq?: string;
|
|
104
|
+
envelopesReq?: string;
|
|
105
|
+
|
|
106
|
+
// Retry counters
|
|
107
|
+
downloadAttempts: number;
|
|
108
|
+
processingAttempts: number;
|
|
109
|
+
|
|
110
|
+
// Cumulative peer attribution for failed attempts (only present when non-empty)
|
|
111
|
+
failedDownloadPeers?: string;
|
|
112
|
+
failedProcessingPeers?: string;
|
|
84
113
|
};
|
|
85
114
|
|
|
115
|
+
function formatRangeReq(req: {startSlot: Slot; count: number}): string {
|
|
116
|
+
return `startSlot=${req.startSlot},count=${req.count}`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function formatColumnsReq(req: {startSlot: Slot; count: number; columns: number[]}): string {
|
|
120
|
+
return `startSlot=${req.startSlot},count=${req.count},cols=${prettyPrintIndices(req.columns)}`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function getTrackedRequest({parentPayloadRequest, columnsRequest}: DownloadByRangeRequests): TrackedRequest {
|
|
124
|
+
return {
|
|
125
|
+
parentPayload: parentPayloadRequest != null,
|
|
126
|
+
byRangeColumns: new Set(parentPayloadRequest == null ? (columnsRequest?.columns ?? []) : []),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
86
130
|
/**
|
|
87
131
|
* Batches are downloaded at the first block of the epoch.
|
|
88
132
|
*
|
|
@@ -104,8 +148,8 @@ export class Batch {
|
|
|
104
148
|
requests: DownloadByRangeRequests;
|
|
105
149
|
/** State of the batch. */
|
|
106
150
|
state: BatchState = {status: BatchStatus.AwaitingDownload, blocks: [], payloadEnvelopes: null};
|
|
107
|
-
/** Peers that provided good data */
|
|
108
|
-
|
|
151
|
+
/** Peers that provided good data, with column coverage for by_range requests */
|
|
152
|
+
private readonly successfulDownloads = new Map<PeerIdStr, TrackedRequest>();
|
|
109
153
|
/** The `Attempts` that have been made and failed to send us this batch. */
|
|
110
154
|
readonly failedProcessingAttempts: Attempt[] = [];
|
|
111
155
|
/** The `Attempts` that have been made and failed because of execution malfunction. */
|
|
@@ -115,8 +159,18 @@ export class Batch {
|
|
|
115
159
|
private readonly config: ChainForkConfig;
|
|
116
160
|
private readonly clock: IClock;
|
|
117
161
|
private readonly custodyConfig: CustodyConfig;
|
|
118
|
-
|
|
119
|
-
|
|
162
|
+
private readonly isFirstBatchInChain: boolean;
|
|
163
|
+
private readonly latestBid: gloas.ExecutionPayloadBid | undefined;
|
|
164
|
+
|
|
165
|
+
constructor(
|
|
166
|
+
startEpoch: Epoch,
|
|
167
|
+
config: ChainForkConfig,
|
|
168
|
+
clock: IClock,
|
|
169
|
+
custodyConfig: CustodyConfig,
|
|
170
|
+
isFirstBatchInChain: boolean,
|
|
171
|
+
latestBid: gloas.ExecutionPayloadBid | undefined,
|
|
172
|
+
targetSlot: Slot
|
|
173
|
+
) {
|
|
120
174
|
this.config = config;
|
|
121
175
|
this.clock = clock;
|
|
122
176
|
this.custodyConfig = custodyConfig;
|
|
@@ -125,10 +179,40 @@ export class Batch {
|
|
|
125
179
|
this.forkName = this.config.getForkName(startSlot);
|
|
126
180
|
this.startEpoch = startEpoch;
|
|
127
181
|
this.startSlot = startSlot;
|
|
128
|
-
this.count = count;
|
|
182
|
+
this.count = Math.min(count, targetSlot - startSlot + 1);
|
|
183
|
+
this.isFirstBatchInChain = isFirstBatchInChain;
|
|
184
|
+
this.latestBid = latestBid;
|
|
129
185
|
this.requests = this.getRequests([]);
|
|
130
186
|
}
|
|
131
187
|
|
|
188
|
+
private shouldDownloadParentEnvelope(firstBlock?: SignedBeaconBlock): boolean {
|
|
189
|
+
if (!this.isFirstBatchInChain) return false;
|
|
190
|
+
|
|
191
|
+
if (this.startSlot === 0 || !isForkPostGloas(this.config.getForkName(this.startSlot - 1))) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// we only know if we should download parent envelope if firstBlock is downloaded
|
|
196
|
+
if (firstBlock === undefined) return false;
|
|
197
|
+
if (this.latestBid === undefined) return false;
|
|
198
|
+
const firstBlockBidParentHash = (firstBlock.message.body as gloas.BeaconBlockBody).signedExecutionPayloadBid.message
|
|
199
|
+
.parentBlockHash;
|
|
200
|
+
return byteArrayEquals(firstBlockBidParentHash, this.latestBid.blockHash);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
getParentPayloadCommitments(parentBlockRoot: Uint8Array): ParentPayloadCommitments {
|
|
204
|
+
if (this.latestBid === undefined) {
|
|
205
|
+
throw new Error(
|
|
206
|
+
`Coding error: getParentPayloadCommitments called without latestBid for parentBlockRoot=${toRootHex(parentBlockRoot)}`
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
blockRoot: parentBlockRoot,
|
|
211
|
+
blockRootHex: toRootHex(parentBlockRoot),
|
|
212
|
+
kzgCommitments: this.latestBid.blobKzgCommitments,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
132
216
|
/**
|
|
133
217
|
* Builds ByRange requests for block, blobs and columns
|
|
134
218
|
*/
|
|
@@ -176,6 +260,7 @@ export class Batch {
|
|
|
176
260
|
const envelopesBySlot = this.state.payloadEnvelopes ?? new Map<Slot, PayloadEnvelopeInput>();
|
|
177
261
|
|
|
178
262
|
// ensure blocks are in slot-wise order
|
|
263
|
+
const isPostGloas = isForkPostGloas(this.forkName);
|
|
179
264
|
for (const blockInput of blocks) {
|
|
180
265
|
const blockSlot = blockInput.slot;
|
|
181
266
|
// check if block/data is present (hasBlock/hasAllData). If present then check if startSlot is the same as
|
|
@@ -191,21 +276,36 @@ export class Batch {
|
|
|
191
276
|
if (blockInput.hasBlock() && blockStartSlot === blockSlot) {
|
|
192
277
|
blockStartSlot = blockSlot + 1;
|
|
193
278
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
279
|
+
|
|
280
|
+
// Range sync uses hasComputedAllData (all sampled columns physically present), not hasAllData
|
|
281
|
+
// which flips at the reconstruction threshold. Sync never triggers reconstruction, so accepting
|
|
282
|
+
// a half-downloaded block here makes writeBlockInputToDb later block on waitForComputedAllData.
|
|
283
|
+
if (isPostGloas) {
|
|
284
|
+
// Post-Gloas: column data lives on PayloadEnvelopeInput, not on BlockInputNoData.
|
|
285
|
+
const payloadInput = envelopesBySlot.get(blockSlot);
|
|
286
|
+
if (blockInput.hasBlock() && envelopeStartSlot === blockSlot && payloadInput?.hasPayloadEnvelope()) {
|
|
287
|
+
envelopeStartSlot = blockSlot + 1;
|
|
288
|
+
}
|
|
289
|
+
if (payloadInput && !payloadInput.hasComputedAllData()) {
|
|
290
|
+
for (const index of payloadInput.getMissingSampledColumnMeta().missing) {
|
|
204
291
|
neededColumns.add(index);
|
|
205
292
|
}
|
|
293
|
+
} else if (payloadInput?.hasComputedAllData() && dataStartSlot === blockSlot) {
|
|
294
|
+
// Only advance dataStartSlot when we know columns for this slot are complete. If
|
|
295
|
+
// payloadInput is missing entirely we cannot tell, so stop here so the next round
|
|
296
|
+
// re-requests columns (and envelopes) starting at this slot.
|
|
297
|
+
dataStartSlot = blockSlot + 1;
|
|
298
|
+
}
|
|
299
|
+
} else {
|
|
300
|
+
if (isBlockInputColumns(blockInput) ? !blockInput.hasComputedAllData() : !blockInput.hasAllData()) {
|
|
301
|
+
if (isBlockInputColumns(blockInput)) {
|
|
302
|
+
for (const index of blockInput.getMissingSampledColumnMeta().missing) {
|
|
303
|
+
neededColumns.add(index);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
} else if (dataStartSlot === blockSlot) {
|
|
307
|
+
dataStartSlot = blockSlot + 1;
|
|
206
308
|
}
|
|
207
|
-
} else if (dataStartSlot === blockSlot) {
|
|
208
|
-
dataStartSlot = blockSlot + 1;
|
|
209
309
|
}
|
|
210
310
|
}
|
|
211
311
|
|
|
@@ -225,11 +325,15 @@ export class Batch {
|
|
|
225
325
|
// range of 40 - 63, startSlot will be inclusive but subtraction will exclusive so need to + 1
|
|
226
326
|
const count = endSlot - dataStartSlot + 1;
|
|
227
327
|
if (isForkPostFulu(this.forkName) && withinValidRequestWindow) {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
328
|
+
// Skip the column re-request when we have no specific column indices outstanding.
|
|
329
|
+
// Peer rejects an empty `columns` list
|
|
330
|
+
if (neededColumns.size > 0) {
|
|
331
|
+
requests.columnsRequest = {
|
|
332
|
+
count,
|
|
333
|
+
startSlot: dataStartSlot,
|
|
334
|
+
columns: Array.from(neededColumns),
|
|
335
|
+
};
|
|
336
|
+
}
|
|
233
337
|
} else if (isForkPostDeneb(this.forkName) && withinValidRequestWindow) {
|
|
234
338
|
requests.blobsRequest = {
|
|
235
339
|
count,
|
|
@@ -246,6 +350,36 @@ export class Batch {
|
|
|
246
350
|
};
|
|
247
351
|
}
|
|
248
352
|
|
|
353
|
+
// Only the first batch of a SyncChain may need the dangling-parent payload by-root.
|
|
354
|
+
if (blocks.length > 0 && this.shouldDownloadParentEnvelope(blocks[0].getBlock())) {
|
|
355
|
+
// shouldDownloadParentEnvelope() = true means there are at least 1 block
|
|
356
|
+
const parentRoot = blocks[0].getBlock().message.parentRoot;
|
|
357
|
+
if (!byteArrayEquals(parentRoot, ZERO_HASH)) {
|
|
358
|
+
const parentRootHex = toRootHex(parentRoot);
|
|
359
|
+
let parentPayloadInput: PayloadEnvelopeInput | undefined;
|
|
360
|
+
if (this.state.payloadEnvelopes) {
|
|
361
|
+
for (const pi of this.state.payloadEnvelopes.values()) {
|
|
362
|
+
if (pi.blockRootHex === parentRootHex) {
|
|
363
|
+
parentPayloadInput = pi;
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const needsEnvelope = !parentPayloadInput?.hasPayloadEnvelope();
|
|
370
|
+
const missingColumns = parentPayloadInput
|
|
371
|
+
? parentPayloadInput.getMissingSampledColumnMeta().missing
|
|
372
|
+
: this.custodyConfig.sampledColumns;
|
|
373
|
+
|
|
374
|
+
if (needsEnvelope || missingColumns.length > 0) {
|
|
375
|
+
requests.parentPayloadRequest = {
|
|
376
|
+
...(needsEnvelope ? {envelopeBlockRoot: parentRoot} : {}),
|
|
377
|
+
...(missingColumns.length > 0 ? {blockRoot: parentRoot, columns: missingColumns} : {}),
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
249
383
|
return requests;
|
|
250
384
|
}
|
|
251
385
|
|
|
@@ -257,24 +391,28 @@ export class Batch {
|
|
|
257
391
|
return this.requests;
|
|
258
392
|
}
|
|
259
393
|
|
|
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
|
-
}
|
|
394
|
+
// post-fulu we need to ensure that we only request columns that the peer has advertised.
|
|
395
|
+
const {columnsRequest, parentPayloadRequest} = this.requests;
|
|
265
396
|
|
|
266
397
|
const peerColumns = new Set(peer.custodyColumns ?? []);
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
398
|
+
const filteredColumnsRequest =
|
|
399
|
+
columnsRequest != null ? columnsRequest.columns.filter((c) => peerColumns.has(c)) : null;
|
|
400
|
+
const parentColumns = parentPayloadRequest?.columns;
|
|
401
|
+
const filteredParentColumns = parentColumns != null ? parentColumns.filter((c) => peerColumns.has(c)) : null;
|
|
402
|
+
|
|
403
|
+
const updatedColumnRequest =
|
|
404
|
+
columnsRequest != null && filteredColumnsRequest != null
|
|
405
|
+
? {columnsRequest: {...columnsRequest, columns: filteredColumnsRequest}}
|
|
406
|
+
: {};
|
|
407
|
+
const updatedParentPayloadRequest =
|
|
408
|
+
parentPayloadRequest != null && filteredParentColumns != null
|
|
409
|
+
? {parentPayloadRequest: {...parentPayloadRequest, columns: filteredParentColumns}}
|
|
410
|
+
: {};
|
|
271
411
|
|
|
272
412
|
return {
|
|
273
413
|
...this.requests,
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
columns: requestedColumns,
|
|
277
|
-
},
|
|
414
|
+
...updatedColumnRequest,
|
|
415
|
+
...updatedParentPayloadRequest,
|
|
278
416
|
};
|
|
279
417
|
}
|
|
280
418
|
|
|
@@ -285,8 +423,56 @@ export class Batch {
|
|
|
285
423
|
return [...this.failedDownloadAttempts, ...this.failedProcessingAttempts.flatMap((a) => a.peers)];
|
|
286
424
|
}
|
|
287
425
|
|
|
426
|
+
/**
|
|
427
|
+
* True only if the peer has already returned a successful response for the current request.
|
|
428
|
+
* A by_range success may update `this.requests` to parent_payload, and the same peer is then
|
|
429
|
+
* still eligible for the newly discovered parent payload data.
|
|
430
|
+
* For by_range, a peer that previously succeeded with a superset of requested columns is skipped.
|
|
431
|
+
*/
|
|
432
|
+
hasPeerSucceededCurrentRequest(peer: PeerSyncMeta): boolean {
|
|
433
|
+
const successfulDownload = this.successfulDownloads.get(peer.peerId);
|
|
434
|
+
if (successfulDownload == null) return false;
|
|
435
|
+
|
|
436
|
+
const request = getTrackedRequest(this.getRequestsForPeer(peer));
|
|
437
|
+
if (request.parentPayload) return successfulDownload.parentPayload;
|
|
438
|
+
|
|
439
|
+
const requestByRangeColumns = request.byRangeColumns;
|
|
440
|
+
|
|
441
|
+
if (requestByRangeColumns.size === 0) {
|
|
442
|
+
// this means a download blocks/envelops by_range only
|
|
443
|
+
// don't do that again if we already did it
|
|
444
|
+
// see https://github.com/ChainSafe/lodestar/issues/9357
|
|
445
|
+
return true;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return [...requestByRangeColumns].every((column) => successfulDownload.byRangeColumns.has(column));
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
private getSuccessfulPeers(): PeerIdStr[] {
|
|
452
|
+
return Array.from(this.successfulDownloads.keys());
|
|
453
|
+
}
|
|
454
|
+
|
|
288
455
|
getMetadata(): BatchMetadata {
|
|
289
|
-
|
|
456
|
+
const {blocksRequest, blobsRequest, columnsRequest, envelopesRequest} = this.requests;
|
|
457
|
+
const failedProcessingPeerList = this.failedProcessingAttempts.flatMap((a) => a.peers);
|
|
458
|
+
return {
|
|
459
|
+
startEpoch: this.startEpoch,
|
|
460
|
+
startSlot: this.startSlot,
|
|
461
|
+
count: this.count,
|
|
462
|
+
status: this.state.status,
|
|
463
|
+
...(blocksRequest && {blocksReq: formatRangeReq(blocksRequest)}),
|
|
464
|
+
...(blobsRequest && {blobsReq: formatRangeReq(blobsRequest)}),
|
|
465
|
+
...(columnsRequest && {columnsReq: formatColumnsReq(columnsRequest)}),
|
|
466
|
+
...(envelopesRequest && {envelopesReq: formatRangeReq(envelopesRequest)}),
|
|
467
|
+
downloadAttempts: this.failedDownloadAttempts.length,
|
|
468
|
+
processingAttempts: this.failedProcessingAttempts.length,
|
|
469
|
+
...(this.failedDownloadAttempts.length > 0 && {
|
|
470
|
+
failedDownloadPeers: this.failedDownloadAttempts.join(","),
|
|
471
|
+
}),
|
|
472
|
+
...(failedProcessingPeerList.length > 0 && {
|
|
473
|
+
failedProcessingPeers: failedProcessingPeerList.join(","),
|
|
474
|
+
}),
|
|
475
|
+
};
|
|
290
476
|
}
|
|
291
477
|
|
|
292
478
|
getBlocks(): IBlockInput[] {
|
|
@@ -300,14 +486,17 @@ export class Batch {
|
|
|
300
486
|
/**
|
|
301
487
|
* AwaitingDownload -> Downloading
|
|
302
488
|
*/
|
|
303
|
-
startDownloading(peer:
|
|
489
|
+
startDownloading(peer: PeerSyncMeta): void {
|
|
304
490
|
if (this.state.status !== BatchStatus.AwaitingDownload) {
|
|
305
491
|
throw new BatchError(this.wrongStatusErrorType(BatchStatus.AwaitingDownload));
|
|
306
492
|
}
|
|
307
493
|
|
|
494
|
+
const request = getTrackedRequest(this.getRequestsForPeer(peer));
|
|
495
|
+
|
|
308
496
|
this.state = {
|
|
309
497
|
status: BatchStatus.Downloading,
|
|
310
|
-
peer,
|
|
498
|
+
peer: peer.peerId,
|
|
499
|
+
request,
|
|
311
500
|
blocks: this.state.blocks,
|
|
312
501
|
payloadEnvelopes: this.state.payloadEnvelopes,
|
|
313
502
|
};
|
|
@@ -328,13 +517,27 @@ export class Batch {
|
|
|
328
517
|
// ensure that blocks are always sorted before getting stored on the batch.state or being used to getRequests
|
|
329
518
|
blocks.sort((a, b) => a.slot - b.slot);
|
|
330
519
|
|
|
331
|
-
this.
|
|
520
|
+
const successfulDownload = this.successfulDownloads.get(peer) ?? {
|
|
521
|
+
parentPayload: false,
|
|
522
|
+
byRangeColumns: new Set<number>(),
|
|
523
|
+
};
|
|
524
|
+
successfulDownload.parentPayload ||= this.state.request.parentPayload;
|
|
525
|
+
if (!this.state.request.parentPayload) {
|
|
526
|
+
for (const column of this.state.request.byRangeColumns) {
|
|
527
|
+
successfulDownload.byRangeColumns.add(column);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
this.successfulDownloads.set(peer, successfulDownload);
|
|
332
531
|
|
|
333
532
|
let allComplete = true;
|
|
334
533
|
const slots = new Set<number>();
|
|
335
534
|
for (const block of blocks) {
|
|
336
535
|
slots.add(block.slot);
|
|
337
|
-
|
|
536
|
+
const dataComplete = isBlockInputColumns(block)
|
|
537
|
+
? // by_range needs to download all columns
|
|
538
|
+
block.hasBlock() && block.hasComputedAllData()
|
|
539
|
+
: block.hasBlockAndAllData();
|
|
540
|
+
if (!dataComplete) {
|
|
338
541
|
allComplete = false;
|
|
339
542
|
}
|
|
340
543
|
}
|
|
@@ -350,11 +553,46 @@ export class Batch {
|
|
|
350
553
|
}
|
|
351
554
|
const newPayloadEnvelopes = payloadEnvelopes ?? this.state.payloadEnvelopes;
|
|
352
555
|
|
|
556
|
+
if (allComplete && isForkPostGloas(this.forkName)) {
|
|
557
|
+
for (const block of blocks) {
|
|
558
|
+
const payloadInput = newPayloadEnvelopes?.get(block.slot);
|
|
559
|
+
// only need to make sure envelope has all columns, not all blocks have payload
|
|
560
|
+
// assertLinearChainSegment() was called before reaching this
|
|
561
|
+
if (payloadInput?.hasPayloadEnvelope() && !payloadInput.hasComputedAllData()) {
|
|
562
|
+
allComplete = false;
|
|
563
|
+
break;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// First batch of a sync chain must additionally have the dangling-parent payload fully
|
|
569
|
+
// present, otherwise `processBlocks` will throw PARENT_PAYLOAD_UNKNOWN. The parent's
|
|
570
|
+
// `PayloadEnvelopeInput` is identified by `blockRootHex` matching `blocks[0].parentRoot`.
|
|
571
|
+
if (allComplete && blocks.length > 0 && this.shouldDownloadParentEnvelope(blocks[0].getBlock())) {
|
|
572
|
+
const parentRoot = blocks[0].getBlock().message.parentRoot;
|
|
573
|
+
// Genesis has no parent payload — nothing to wait for.
|
|
574
|
+
if (!byteArrayEquals(parentRoot, ZERO_HASH)) {
|
|
575
|
+
const parentRootHex = toRootHex(parentRoot);
|
|
576
|
+
let parentPayloadComplete = false;
|
|
577
|
+
if (newPayloadEnvelopes) {
|
|
578
|
+
for (const payloadInput of newPayloadEnvelopes.values()) {
|
|
579
|
+
if (payloadInput.blockRootHex === parentRootHex) {
|
|
580
|
+
parentPayloadComplete = payloadInput.hasPayloadEnvelope() && payloadInput.hasComputedAllData();
|
|
581
|
+
break;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
if (!parentPayloadComplete) {
|
|
586
|
+
allComplete = false;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
353
591
|
if (allComplete) {
|
|
354
592
|
this.state = {status: BatchStatus.AwaitingProcessing, blocks, payloadEnvelopes: newPayloadEnvelopes};
|
|
355
593
|
} else {
|
|
356
|
-
this.requests = this.getRequests(blocks);
|
|
357
594
|
this.state = {status: BatchStatus.AwaitingDownload, blocks, payloadEnvelopes: newPayloadEnvelopes};
|
|
595
|
+
this.requests = this.getRequests(blocks);
|
|
358
596
|
}
|
|
359
597
|
|
|
360
598
|
return this.state as DownloadSuccessState;
|
|
@@ -380,10 +618,30 @@ export class Batch {
|
|
|
380
618
|
};
|
|
381
619
|
}
|
|
382
620
|
|
|
621
|
+
/**
|
|
622
|
+
* Downloading -> AwaitingDownload (without counting as a failed attempt).
|
|
623
|
+
* Used when the peer rate-limited us — the request was never actually served.
|
|
624
|
+
*/
|
|
625
|
+
downloadingRateLimited(): void {
|
|
626
|
+
if (this.state.status !== BatchStatus.Downloading) {
|
|
627
|
+
throw new BatchError(this.wrongStatusErrorType(BatchStatus.Downloading));
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
this.state = {
|
|
631
|
+
status: BatchStatus.AwaitingDownload,
|
|
632
|
+
blocks: this.state.blocks,
|
|
633
|
+
payloadEnvelopes: this.state.payloadEnvelopes,
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
|
|
383
637
|
/**
|
|
384
638
|
* AwaitingProcessing -> Processing
|
|
385
639
|
*/
|
|
386
|
-
startProcessing(): {
|
|
640
|
+
startProcessing(): {
|
|
641
|
+
blocks: IBlockInput[];
|
|
642
|
+
payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null;
|
|
643
|
+
peers: PeerIdStr[];
|
|
644
|
+
} {
|
|
387
645
|
if (this.state.status !== BatchStatus.AwaitingProcessing) {
|
|
388
646
|
throw new BatchError(this.wrongStatusErrorType(BatchStatus.AwaitingProcessing));
|
|
389
647
|
}
|
|
@@ -391,12 +649,12 @@ export class Batch {
|
|
|
391
649
|
const blocks = this.state.blocks;
|
|
392
650
|
const payloadEnvelopes = this.state.payloadEnvelopes;
|
|
393
651
|
const hash = hashBlocks(blocks, this.config); // tracks blocks to report peer on processing error
|
|
394
|
-
// Reset
|
|
395
|
-
// that the data came from will be handled by the Attempt that goes for processing
|
|
396
|
-
const peers = this.
|
|
397
|
-
this.
|
|
652
|
+
// Reset successfulDownloads in case another download attempt needs to be made. When Attempt is successful or not
|
|
653
|
+
// the peers that the data came from will be handled by the Attempt that goes for processing.
|
|
654
|
+
const peers = this.getSuccessfulPeers();
|
|
655
|
+
this.successfulDownloads.clear();
|
|
398
656
|
this.state = {status: BatchStatus.Processing, blocks, payloadEnvelopes, attempt: {peers, hash}};
|
|
399
|
-
return {blocks, payloadEnvelopes};
|
|
657
|
+
return {blocks, payloadEnvelopes, peers};
|
|
400
658
|
}
|
|
401
659
|
|
|
402
660
|
/**
|
|
@@ -479,7 +737,7 @@ export class Batch {
|
|
|
479
737
|
|
|
480
738
|
/** Helper to construct typed BatchError. Stack traces are correct as the error is thrown above */
|
|
481
739
|
private errorType(type: BatchErrorType): BatchErrorType & BatchErrorMetadata {
|
|
482
|
-
return {...type,
|
|
740
|
+
return {...type, startEpoch: this.startEpoch, status: this.state.status};
|
|
483
741
|
}
|
|
484
742
|
|
|
485
743
|
private wrongStatusErrorType(expectedStatus: BatchStatus): BatchErrorType & BatchErrorMetadata {
|