@lodestar/beacon-node 1.50.0-dev.8ba9d1dd30 → 1.50.0-dev.9c7f3c203f
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 +6 -1
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/index.js +13 -0
- package/lib/api/impl/beacon/index.js.map +1 -1
- package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/index.js +23 -3
- package/lib/api/impl/beacon/state/index.js.map +1 -1
- package/lib/api/impl/debug/index.d.ts.map +1 -1
- package/lib/api/impl/debug/index.js +26 -18
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts +1 -0
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +23 -1
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +1 -1
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts +2 -2
- package/lib/chain/archiveStore/historicalState/getHistoricalState.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/getHistoricalState.js +5 -7
- package/lib/chain/archiveStore/historicalState/getHistoricalState.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.js +1 -1
- package/lib/chain/archiveStore/historicalState/historicalStateRegen.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/types.d.ts +2 -2
- package/lib/chain/archiveStore/historicalState/types.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/worker.js +1 -1
- package/lib/chain/archiveStore/historicalState/worker.js.map +1 -1
- package/lib/chain/archiveStore/interface.d.ts +1 -1
- package/lib/chain/archiveStore/interface.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +2 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.js +1 -1
- package/lib/chain/blocks/verifyBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts +2 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js +5 -4
- package/lib/chain/blocks/verifyBlocksStateTransitionOnly.js.map +1 -1
- package/lib/chain/chain.d.ts +7 -4
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +47 -14
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +7 -1
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/emitter.js +5 -0
- package/lib/chain/emitter.js.map +1 -1
- package/lib/chain/errors/envelopeReconstructionError.d.ts +5 -0
- package/lib/chain/errors/envelopeReconstructionError.d.ts.map +1 -1
- package/lib/chain/errors/envelopeReconstructionError.js +2 -0
- package/lib/chain/errors/envelopeReconstructionError.js.map +1 -1
- package/lib/chain/errors/gossipValidation.d.ts +1 -0
- package/lib/chain/errors/gossipValidation.d.ts.map +1 -1
- package/lib/chain/errors/gossipValidation.js +1 -0
- package/lib/chain/errors/gossipValidation.js.map +1 -1
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +2 -0
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/interface.d.ts +3 -2
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.d.ts +4 -4
- package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.js +14 -11
- package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
- package/lib/chain/options.d.ts +2 -1
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/options.js +1 -1
- package/lib/chain/options.js.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.js +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
- package/lib/chain/regen/regen.d.ts +2 -0
- package/lib/chain/regen/regen.d.ts.map +1 -1
- package/lib/chain/regen/regen.js +10 -4
- package/lib/chain/regen/regen.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +9 -2
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +23 -13
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/validatorMonitor.d.ts +5 -1
- package/lib/chain/validatorMonitor.d.ts.map +1 -1
- package/lib/chain/validatorMonitor.js +64 -54
- package/lib/chain/validatorMonitor.js.map +1 -1
- package/lib/execution/engine/disabled.d.ts +0 -2
- package/lib/execution/engine/disabled.d.ts.map +1 -1
- package/lib/execution/engine/disabled.js +0 -6
- package/lib/execution/engine/disabled.js.map +1 -1
- package/lib/execution/engine/http.d.ts +1 -3
- package/lib/execution/engine/http.d.ts.map +1 -1
- package/lib/execution/engine/http.js +2 -17
- package/lib/execution/engine/http.js.map +1 -1
- package/lib/execution/engine/interface.d.ts +2 -4
- package/lib/execution/engine/interface.d.ts.map +1 -1
- package/lib/execution/engine/mock.d.ts +0 -2
- package/lib/execution/engine/mock.d.ts.map +1 -1
- package/lib/execution/engine/mock.js +0 -8
- package/lib/execution/engine/mock.js.map +1 -1
- package/lib/execution/engine/types.d.ts +0 -8
- package/lib/execution/engine/types.d.ts.map +1 -1
- package/lib/execution/engine/types.js.map +1 -1
- package/lib/metrics/metrics.d.ts +7 -3
- package/lib/metrics/metrics.d.ts.map +1 -1
- package/lib/metrics/metrics.js +4 -4
- package/lib/metrics/metrics.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +3 -0
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/gossipValidatorFn.d.ts +4 -2
- package/lib/network/processor/gossipValidatorFn.d.ts.map +1 -1
- package/lib/network/processor/gossipValidatorFn.js +6 -2
- package/lib/network/processor/gossipValidatorFn.js.map +1 -1
- package/lib/network/processor/index.d.ts +8 -0
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +49 -2
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +25 -3
- package/lib/node/nodejs.js.map +1 -1
- package/lib/sync/range/batch.d.ts +6 -0
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +10 -0
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +5 -1
- 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 +50 -18
- package/lib/sync/range/range.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +5 -4
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/util/execution.d.ts +9 -11
- package/lib/util/execution.d.ts.map +1 -1
- package/lib/util/execution.js +5 -1
- package/lib/util/execution.js.map +1 -1
- package/package.json +17 -17
- package/src/api/impl/beacon/blocks/index.ts +7 -4
- package/src/api/impl/beacon/index.ts +15 -0
- package/src/api/impl/beacon/state/index.ts +31 -3
- package/src/api/impl/debug/index.ts +28 -19
- package/src/api/impl/validator/index.ts +24 -2
- package/src/chain/archiveStore/archiveStore.ts +1 -1
- package/src/chain/archiveStore/historicalState/getHistoricalState.ts +5 -7
- package/src/chain/archiveStore/historicalState/historicalStateRegen.ts +1 -1
- package/src/chain/archiveStore/historicalState/types.ts +2 -2
- package/src/chain/archiveStore/historicalState/worker.ts +1 -2
- package/src/chain/archiveStore/interface.ts +1 -1
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +2 -1
- package/src/chain/blocks/verifyBlock.ts +1 -0
- package/src/chain/blocks/verifyBlocksStateTransitionOnly.ts +6 -3
- package/src/chain/chain.ts +64 -17
- package/src/chain/emitter.ts +7 -1
- package/src/chain/errors/envelopeReconstructionError.ts +3 -0
- package/src/chain/errors/gossipValidation.ts +1 -0
- package/src/chain/forkChoice/index.ts +2 -0
- package/src/chain/interface.ts +3 -2
- package/src/chain/opPools/proposerPreferencesPool.ts +14 -10
- package/src/chain/options.ts +3 -3
- package/src/chain/produceBlock/computeNewStateRoot.ts +1 -1
- package/src/chain/regen/regen.ts +12 -4
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +31 -13
- package/src/chain/validatorMonitor.ts +75 -57
- package/src/execution/engine/disabled.ts +0 -8
- package/src/execution/engine/http.ts +1 -30
- package/src/execution/engine/interface.ts +2 -6
- package/src/execution/engine/mock.ts +1 -19
- package/src/execution/engine/types.ts +0 -10
- package/src/metrics/metrics.ts +21 -6
- package/src/network/processor/gossipHandlers.ts +3 -0
- package/src/network/processor/gossipValidatorFn.ts +12 -2
- package/src/network/processor/index.ts +55 -2
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +1 -1
- package/src/node/nodejs.ts +31 -3
- package/src/sync/range/batch.ts +11 -0
- package/src/sync/range/chain.ts +6 -1
- package/src/sync/range/range.ts +50 -18
- package/src/sync/unknownBlock.ts +5 -4
- package/src/util/execution.ts +16 -8
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
BlobsBundleRpc,
|
|
23
23
|
EngineApiRpcParamTypes,
|
|
24
24
|
EngineApiRpcReturnTypes,
|
|
25
|
-
ExecutionPayloadBodyRpc,
|
|
26
25
|
ExecutionPayloadBodyV2Rpc,
|
|
27
26
|
ExecutionPayloadRpc,
|
|
28
27
|
ExecutionRequestsRpc,
|
|
@@ -50,7 +49,7 @@ type ExecutionBlock = {
|
|
|
50
49
|
blockHash: RootHex;
|
|
51
50
|
timestamp: number;
|
|
52
51
|
blockNumber: number;
|
|
53
|
-
/** Bodies as received via newPayload, served back by
|
|
52
|
+
/** Bodies as received via newPayload, served back by engine_getPayloadBodiesByHashV2 */
|
|
54
53
|
body: ExecutionPayloadBodyV2Rpc;
|
|
55
54
|
};
|
|
56
55
|
|
|
@@ -148,23 +147,13 @@ export class ExecutionEngineMockBackend implements JsonRpcBackend {
|
|
|
148
147
|
engine_getPayloadV4: this.getPayloadV5.bind(this),
|
|
149
148
|
engine_getPayloadV5: this.getPayloadV5.bind(this),
|
|
150
149
|
engine_getPayloadV6: this.getPayloadV5.bind(this),
|
|
151
|
-
engine_getPayloadBodiesByHashV1: this.getPayloadBodiesByHash.bind(this),
|
|
152
150
|
engine_getPayloadBodiesByHashV2: this.getPayloadBodiesByHashV2.bind(this),
|
|
153
|
-
engine_getPayloadBodiesByRangeV1: this.getPayloadBodiesByRange.bind(this),
|
|
154
151
|
engine_getClientVersionV1: this.getClientVersionV1.bind(this),
|
|
155
152
|
engine_getBlobsV1: this.getBlobs.bind(this),
|
|
156
153
|
engine_getBlobsV2: this.getBlobsV2.bind(this),
|
|
157
154
|
};
|
|
158
155
|
}
|
|
159
156
|
|
|
160
|
-
private getPayloadBodiesByHash(
|
|
161
|
-
blockHashes: EngineApiRpcParamTypes["engine_getPayloadBodiesByHashV1"][0]
|
|
162
|
-
): EngineApiRpcReturnTypes["engine_getPayloadBodiesByHashV1"] {
|
|
163
|
-
return this.getPayloadBodiesByHashV2(blockHashes).map((body) =>
|
|
164
|
-
body ? {transactions: body.transactions, withdrawals: body.withdrawals} : null
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
157
|
private getPayloadBodiesByHashV2(
|
|
169
158
|
blockHashes: EngineApiRpcParamTypes["engine_getPayloadBodiesByHashV2"][0]
|
|
170
159
|
): EngineApiRpcReturnTypes["engine_getPayloadBodiesByHashV2"] {
|
|
@@ -172,13 +161,6 @@ export class ExecutionEngineMockBackend implements JsonRpcBackend {
|
|
|
172
161
|
return blockHashes.map((hash) => this.validBlocks.get(hash)?.body ?? null);
|
|
173
162
|
}
|
|
174
163
|
|
|
175
|
-
private getPayloadBodiesByRange(
|
|
176
|
-
_start: EngineApiRpcParamTypes["engine_getPayloadBodiesByRangeV1"][0],
|
|
177
|
-
_count: EngineApiRpcParamTypes["engine_getPayloadBodiesByRangeV1"][1]
|
|
178
|
-
): EngineApiRpcReturnTypes["engine_getPayloadBodiesByRangeV1"] {
|
|
179
|
-
return [] as ExecutionPayloadBodyRpc[];
|
|
180
|
-
}
|
|
181
|
-
|
|
182
164
|
/**
|
|
183
165
|
* Mock manipulator to add predefined responses before execution engine client calls
|
|
184
166
|
*/
|
|
@@ -88,15 +88,8 @@ export type EngineApiRpcParamTypes = {
|
|
|
88
88
|
/**
|
|
89
89
|
* 1. Array of DATA - Array of block_hash field values of the ExecutionPayload structure
|
|
90
90
|
* */
|
|
91
|
-
engine_getPayloadBodiesByHashV1: DATA[][];
|
|
92
91
|
engine_getPayloadBodiesByHashV2: DATA[][];
|
|
93
92
|
|
|
94
|
-
/**
|
|
95
|
-
* 1. start: QUANTITY, 64 bits - Starting block number
|
|
96
|
-
* 2. count: QUANTITY, 64 bits - Number of blocks to return
|
|
97
|
-
*/
|
|
98
|
-
engine_getPayloadBodiesByRangeV1: [start: QUANTITY, count: QUANTITY];
|
|
99
|
-
|
|
100
93
|
/**
|
|
101
94
|
* Object - Instance of ClientVersion
|
|
102
95
|
*/
|
|
@@ -148,11 +141,8 @@ export type EngineApiRpcReturnTypes = {
|
|
|
148
141
|
engine_getPayloadV5: ExecutionPayloadResponse;
|
|
149
142
|
engine_getPayloadV6: ExecutionPayloadResponse;
|
|
150
143
|
|
|
151
|
-
engine_getPayloadBodiesByHashV1: (ExecutionPayloadBodyRpc | null)[];
|
|
152
144
|
engine_getPayloadBodiesByHashV2: (ExecutionPayloadBodyV2Rpc | null)[];
|
|
153
145
|
|
|
154
|
-
engine_getPayloadBodiesByRangeV1: (ExecutionPayloadBodyRpc | null)[];
|
|
155
|
-
|
|
156
146
|
engine_getClientVersionV1: ClientVersionRpc[];
|
|
157
147
|
|
|
158
148
|
engine_getBlobsV1: (BlobAndProofRpc | null)[];
|
package/src/metrics/metrics.ts
CHANGED
|
@@ -9,27 +9,42 @@ import {RegistryMetricCreator} from "./utils/registryMetricCreator.js";
|
|
|
9
9
|
|
|
10
10
|
export type Metrics = BeaconMetrics &
|
|
11
11
|
ForkChoiceMetrics &
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
LodestarMetrics & {
|
|
13
|
+
/** Null when we use native state transition metrics instead */
|
|
14
|
+
stateTransition: BeaconStateTransitionMetrics | null;
|
|
15
|
+
register: RegistryMetricCreator;
|
|
16
|
+
close: () => void;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type CreateMetricsOptions = {
|
|
20
|
+
/*
|
|
21
|
+
* Toggle inclusion of state transition metrics.
|
|
22
|
+
*
|
|
23
|
+
* False when native state transition is used, so we
|
|
24
|
+
* can grab metrics from the native implementation instead.
|
|
25
|
+
* */
|
|
26
|
+
includeStateTransitionMetrics?: boolean;
|
|
27
|
+
collectNodeMetrics?: boolean;
|
|
28
|
+
};
|
|
14
29
|
|
|
15
30
|
export function createMetrics(
|
|
16
31
|
opts: MetricsOptions,
|
|
17
32
|
genesisTime: number,
|
|
18
33
|
externalRegistries: Registry[] = [],
|
|
19
|
-
|
|
34
|
+
createOpts: CreateMetricsOptions = {}
|
|
20
35
|
): Metrics {
|
|
21
36
|
const register = new RegistryMetricCreator();
|
|
22
37
|
const beacon = createBeaconMetrics(register);
|
|
23
38
|
const forkChoice = getForkChoiceMetrics(register);
|
|
24
39
|
const lodestar = createLodestarMetrics(register, opts.metadata, genesisTime);
|
|
25
|
-
const stateTransition = getMetrics(register);
|
|
40
|
+
const stateTransition = createOpts.includeStateTransitionMetrics === false ? null : getMetrics(register);
|
|
26
41
|
|
|
27
42
|
const onUnhandledRejection = (_error: unknown): void => {
|
|
28
43
|
lodestar.unhandledPromiseRejections.inc();
|
|
29
44
|
};
|
|
30
45
|
process.on("unhandledRejection", onUnhandledRejection);
|
|
31
46
|
|
|
32
|
-
const nodeJsMetricsClose = collectNodeMetrics
|
|
47
|
+
const nodeJsMetricsClose = createOpts.collectNodeMetrics === false ? (): void => {} : collectNodeJSMetrics(register);
|
|
33
48
|
const close = (): void => {
|
|
34
49
|
process.removeListener("unhandledRejection", onUnhandledRejection);
|
|
35
50
|
nodeJsMetricsClose();
|
|
@@ -46,7 +61,7 @@ export function createMetrics(
|
|
|
46
61
|
...beacon,
|
|
47
62
|
...forkChoice,
|
|
48
63
|
...lodestar,
|
|
49
|
-
|
|
64
|
+
stateTransition,
|
|
50
65
|
register,
|
|
51
66
|
close,
|
|
52
67
|
};
|
|
@@ -1341,6 +1341,9 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1341
1341
|
case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
|
|
1342
1342
|
case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
|
|
1343
1343
|
core.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadGossipPayload");
|
|
1344
|
+
// The builder may have signed another envelope that is valid, keeping this one would make
|
|
1345
|
+
// by-root and range sync reuse it and never import the payload
|
|
1346
|
+
chain.seenPayloadEnvelopeInputCache.removeInvalid(payloadInput);
|
|
1344
1347
|
// Misbehaving peer, but could highlight an issue in another client
|
|
1345
1348
|
logLevel = LogLevel.warn;
|
|
1346
1349
|
break;
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
VoluntaryExitErrorCode,
|
|
21
21
|
} from "../../chain/errors/index.js";
|
|
22
22
|
import {Metrics} from "../../metrics/index.js";
|
|
23
|
+
import {PeerIdStr} from "../../util/peerId.js";
|
|
23
24
|
import {INetworkCore} from "../core/index.js";
|
|
24
25
|
import {
|
|
25
26
|
BatchGossipHandlerFn,
|
|
@@ -40,6 +41,8 @@ export type ValidatorFnModules = {
|
|
|
40
41
|
core: INetworkCore;
|
|
41
42
|
};
|
|
42
43
|
|
|
44
|
+
export type OnFatalPeerFn = (peer: PeerIdStr) => void;
|
|
45
|
+
|
|
43
46
|
type RejectPeerActionRule = {default: PeerAction; byCode?: Record<string, PeerAction>};
|
|
44
47
|
|
|
45
48
|
/**
|
|
@@ -123,7 +126,8 @@ export function rejectPeerAction(type: GossipType, code: string): PeerAction {
|
|
|
123
126
|
*/
|
|
124
127
|
export function getGossipValidatorBatchFn(
|
|
125
128
|
gossipHandlers: GossipHandlers,
|
|
126
|
-
modules: ValidatorFnModules
|
|
129
|
+
modules: ValidatorFnModules,
|
|
130
|
+
onFatalPeer: OnFatalPeerFn
|
|
127
131
|
): GossipValidatorBatchFn {
|
|
128
132
|
const {logger, metrics, core} = modules;
|
|
129
133
|
|
|
@@ -173,6 +177,7 @@ export function getGossipValidatorBatchFn(
|
|
|
173
177
|
metrics?.networkProcessor.gossipAttestationRejectByReason.inc({reason: e.type.code});
|
|
174
178
|
const peerAction = rejectPeerAction(type, e.type.code);
|
|
175
179
|
core.reportPeer(propagationSource, peerAction, e.type.code);
|
|
180
|
+
if (peerAction === PeerAction.Fatal) onFatalPeer(propagationSource);
|
|
176
181
|
logger.debug(
|
|
177
182
|
`Gossip validation ${type} rejected`,
|
|
178
183
|
{peer: propagationSource, clientAgent, clientVersion, peerAction},
|
|
@@ -208,7 +213,11 @@ export function getGossipValidatorBatchFn(
|
|
|
208
213
|
*
|
|
209
214
|
* @see getGossipHandlers for reasoning on why GossipHandlerFn are used for gossip validation.
|
|
210
215
|
*/
|
|
211
|
-
export function getGossipValidatorFn(
|
|
216
|
+
export function getGossipValidatorFn(
|
|
217
|
+
gossipHandlers: GossipHandlers,
|
|
218
|
+
modules: ValidatorFnModules,
|
|
219
|
+
onFatalPeer: OnFatalPeerFn
|
|
220
|
+
): GossipValidatorFn {
|
|
212
221
|
const {logger, metrics, core} = modules;
|
|
213
222
|
|
|
214
223
|
return async function gossipValidatorFn({
|
|
@@ -260,6 +269,7 @@ export function getGossipValidatorFn(gossipHandlers: GossipHandlers, modules: Va
|
|
|
260
269
|
metrics?.networkProcessor.gossipValidationReject.inc({topic: type});
|
|
261
270
|
const peerAction = rejectPeerAction(type, e.type.code);
|
|
262
271
|
core.reportPeer(propagationSource, peerAction, e.type.code);
|
|
272
|
+
if (peerAction === PeerAction.Fatal) onFatalPeer(propagationSource);
|
|
263
273
|
logger.debug(
|
|
264
274
|
`Gossip validation ${type} rejected`,
|
|
265
275
|
{peer: propagationSource, clientAgent, clientVersion, peerAction},
|
|
@@ -108,6 +108,12 @@ type SearchedRootEntry = {
|
|
|
108
108
|
*/
|
|
109
109
|
const DEFAULT_EARLIEST_PERMISSIBLE_SLOT_DISTANCE = 32;
|
|
110
110
|
|
|
111
|
+
/**
|
|
112
|
+
* The network worker bans a Fatal peer only on its next PeerManager heartbeat (30s). Until then the main thread
|
|
113
|
+
* would keep validating that peer's messages, so drop them here. 3 slots (36s) outlasts one heartbeat.
|
|
114
|
+
*/
|
|
115
|
+
export const FATAL_PEER_EXPIRY_SLOTS = 3;
|
|
116
|
+
|
|
111
117
|
type WorkOpts = {
|
|
112
118
|
bypassQueue?: boolean;
|
|
113
119
|
};
|
|
@@ -263,6 +269,8 @@ export class NetworkProcessor {
|
|
|
263
269
|
() => new MapDef<RootHex, SearchedRootEntry>(() => ({}))
|
|
264
270
|
);
|
|
265
271
|
private bufferedRootsBySlot = new MapDef<Slot, Set<RootHex>>(() => new Set());
|
|
272
|
+
/** peer -> clock slot when it was Fatal'ed */
|
|
273
|
+
private readonly fatalPeers = new Map<PeerIdStr, Slot>();
|
|
266
274
|
|
|
267
275
|
constructor(
|
|
268
276
|
modules: NetworkProcessorModules,
|
|
@@ -277,10 +285,15 @@ export class NetworkProcessor {
|
|
|
277
285
|
this.events = events;
|
|
278
286
|
this.gossipQueues = createGossipQueues();
|
|
279
287
|
this.gossipTopicConcurrency = mapValues(this.gossipQueues, () => 0);
|
|
280
|
-
this.gossipValidatorFn = getGossipValidatorFn(
|
|
288
|
+
this.gossipValidatorFn = getGossipValidatorFn(
|
|
289
|
+
modules.gossipHandlers ?? getGossipHandlers(modules, opts),
|
|
290
|
+
modules,
|
|
291
|
+
this.onFatalPeer
|
|
292
|
+
);
|
|
281
293
|
this.gossipValidatorBatchFn = getGossipValidatorBatchFn(
|
|
282
294
|
modules.gossipHandlers ?? getGossipHandlers(modules, opts),
|
|
283
|
-
modules
|
|
295
|
+
modules,
|
|
296
|
+
this.onFatalPeer
|
|
284
297
|
);
|
|
285
298
|
|
|
286
299
|
events.on(NetworkEvent.pendingGossipsubMessage, this.onPendingGossipsubMessage);
|
|
@@ -414,8 +427,18 @@ export class NetworkProcessor {
|
|
|
414
427
|
}
|
|
415
428
|
}
|
|
416
429
|
|
|
430
|
+
private onFatalPeer = (peer: PeerIdStr): void => {
|
|
431
|
+
this.fatalPeers.set(peer, this.chain.clock.currentSlot);
|
|
432
|
+
};
|
|
433
|
+
|
|
417
434
|
private onPendingGossipsubMessage = (message: PendingGossipsubMessage): void => {
|
|
418
435
|
const topicType = message.topic.type;
|
|
436
|
+
if (this.fatalPeers.has(message.propagationSource)) {
|
|
437
|
+
this.metrics?.networkProcessor.gossipValidationError.inc({topic: topicType, error: GossipErrorCode.FATAL_PEER});
|
|
438
|
+
// No need to report the dropped job to gossip. It will be eventually pruned from the mcache
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
|
|
419
442
|
const extractBlockSlotRootFn = this.extractBlockSlotRootFns[topicType];
|
|
420
443
|
if (extractBlockSlotRootFn === undefined) {
|
|
421
444
|
// some messages don't have slot and root
|
|
@@ -898,6 +921,10 @@ export class NetworkProcessor {
|
|
|
898
921
|
for (const slot of this.bufferedRootsBySlot.keys()) {
|
|
899
922
|
if (slot <= minSlot) this.bufferedRootsBySlot.delete(slot);
|
|
900
923
|
}
|
|
924
|
+
|
|
925
|
+
for (const [peer, slot] of this.fatalPeers) {
|
|
926
|
+
if (clockSlot - slot > FATAL_PEER_EXPIRY_SLOTS) this.fatalPeers.delete(peer);
|
|
927
|
+
}
|
|
901
928
|
};
|
|
902
929
|
|
|
903
930
|
private executeWork(): void {
|
|
@@ -953,6 +980,32 @@ export class NetworkProcessor {
|
|
|
953
980
|
private async processPendingGossipsubMessage(
|
|
954
981
|
messageOrArray: PendingGossipsubMessage | PendingGossipsubMessage[]
|
|
955
982
|
): Promise<void> {
|
|
983
|
+
// drop messages from peers Fatal'ed after these were queued or started awaiting a block/envelope
|
|
984
|
+
// No need to report the dropped jobs to gossip. They will be eventually pruned from the mcache
|
|
985
|
+
if (this.fatalPeers.size > 0) {
|
|
986
|
+
if (Array.isArray(messageOrArray)) {
|
|
987
|
+
const allowed: PendingGossipsubMessage[] = [];
|
|
988
|
+
for (const msg of messageOrArray) {
|
|
989
|
+
if (this.fatalPeers.has(msg.propagationSource)) {
|
|
990
|
+
this.metrics?.networkProcessor.gossipValidationError.inc({
|
|
991
|
+
topic: msg.topic.type,
|
|
992
|
+
error: GossipErrorCode.FATAL_PEER,
|
|
993
|
+
});
|
|
994
|
+
} else {
|
|
995
|
+
allowed.push(msg);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
if (allowed.length === 0) return;
|
|
999
|
+
messageOrArray = allowed;
|
|
1000
|
+
} else if (this.fatalPeers.has(messageOrArray.propagationSource)) {
|
|
1001
|
+
this.metrics?.networkProcessor.gossipValidationError.inc({
|
|
1002
|
+
topic: messageOrArray.topic.type,
|
|
1003
|
+
error: GossipErrorCode.FATAL_PEER,
|
|
1004
|
+
});
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
|
|
956
1009
|
const nowSec = Date.now() / 1000;
|
|
957
1010
|
if (Array.isArray(messageOrArray)) {
|
|
958
1011
|
for (const msg of messageOrArray) {
|
|
@@ -53,7 +53,7 @@ export async function* onExecutionPayloadEnvelopesByRoot(
|
|
|
53
53
|
const batch = requests.slice(i, i + MAX_BODIES_PER_REQUEST);
|
|
54
54
|
let envelopesBytes: (Uint8Array | null)[];
|
|
55
55
|
try {
|
|
56
|
-
// By-root allows omission, so
|
|
56
|
+
// By-root allows omission, so an envelope that cannot be rebuilt is left out rather than failing the response.
|
|
57
57
|
envelopesBytes = await chain.getSerializedExecutionPayloadEnvelopes(batch, "omit");
|
|
58
58
|
} catch (e) {
|
|
59
59
|
if (e instanceof EnvelopeReconstructionError) {
|
package/src/node/nodejs.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import {setMaxListeners} from "node:events";
|
|
2
2
|
import {PrivateKey} from "@libp2p/interface";
|
|
3
3
|
import {Registry} from "prom-client";
|
|
4
|
+
import {
|
|
5
|
+
init as initNativeMetrics,
|
|
6
|
+
registerLocalValidator as registerNativeLocalValidator,
|
|
7
|
+
scrapeMetrics as scrapeNativeMetrics,
|
|
8
|
+
unregisterLocalValidator as unregisterNativeLocalValidator,
|
|
9
|
+
} from "@chainsafe/lodestar-z/metrics";
|
|
4
10
|
import {type PubkeyCache} from "@chainsafe/lodestar-z/pubkeys";
|
|
5
11
|
import {hasher} from "@chainsafe/persistent-merkle-tree";
|
|
6
12
|
import {BeaconApiMethods} from "@lodestar/api/beacon/server";
|
|
@@ -171,7 +177,13 @@ export class BeaconNode {
|
|
|
171
177
|
// monitoring relies on metrics data
|
|
172
178
|
opts.monitoring.endpoint
|
|
173
179
|
) {
|
|
174
|
-
|
|
180
|
+
if (opts.chain.nativeStateTransition) {
|
|
181
|
+
initNativeMetrics();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
metrics = createMetrics(opts.metrics, anchorState.genesisTime, metricsRegistries, {
|
|
185
|
+
includeStateTransitionMetrics: !opts.chain.nativeStateTransition,
|
|
186
|
+
});
|
|
175
187
|
initBeaconMetrics(metrics, anchorState);
|
|
176
188
|
// Since the db is instantiated before this, metrics must be injected manually afterwards
|
|
177
189
|
db.setMetrics(metrics.db, metrics.flatFileStore);
|
|
@@ -185,7 +197,13 @@ export class BeaconNode {
|
|
|
185
197
|
config,
|
|
186
198
|
anchorState.genesisTime,
|
|
187
199
|
logger.child({module: LoggerModule.vmon}),
|
|
188
|
-
opts.validatorMonitor
|
|
200
|
+
opts.validatorMonitor,
|
|
201
|
+
opts.chain.nativeStateTransition
|
|
202
|
+
? {
|
|
203
|
+
registerLocalValidator: registerNativeLocalValidator,
|
|
204
|
+
unregisterLocalValidator: unregisterNativeLocalValidator,
|
|
205
|
+
}
|
|
206
|
+
: null
|
|
189
207
|
)
|
|
190
208
|
: null;
|
|
191
209
|
|
|
@@ -303,7 +321,17 @@ export class BeaconNode {
|
|
|
303
321
|
const metricsServer = opts.metrics.enabled
|
|
304
322
|
? await getHttpMetricsServer(opts.metrics, {
|
|
305
323
|
register: (metrics as Metrics).register,
|
|
306
|
-
getOtherMetrics: async () =>
|
|
324
|
+
getOtherMetrics: async () => {
|
|
325
|
+
const otherMetrics = await Promise.all([network.scrapeMetrics(), chain.archiveStore.scrapeMetrics()]);
|
|
326
|
+
if (opts.chain.nativeStateTransition) {
|
|
327
|
+
try {
|
|
328
|
+
otherMetrics.push(scrapeNativeMetrics());
|
|
329
|
+
} catch (e) {
|
|
330
|
+
logger.warn("Failed to scrape native state-transition metrics", {}, e as Error);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return otherMetrics;
|
|
334
|
+
},
|
|
307
335
|
logger: logger.child({module: LoggerModule.metrics}),
|
|
308
336
|
})
|
|
309
337
|
: null;
|
package/src/sync/range/batch.ts
CHANGED
|
@@ -773,6 +773,7 @@ export class Batch {
|
|
|
773
773
|
// remove any downloaded blocks and re-attempt
|
|
774
774
|
// TODO(fulu): need to remove the bad blocks from the SeenBlockInputCache
|
|
775
775
|
this.state = {status: BatchStatus.AwaitingDownload, blocks: [], payloadEnvelopes: null};
|
|
776
|
+
this.resetRequests();
|
|
776
777
|
}
|
|
777
778
|
|
|
778
779
|
private onProcessingError(attempt: FailedAttempt): void {
|
|
@@ -784,6 +785,16 @@ export class Batch {
|
|
|
784
785
|
// remove any downloaded blocks and re-attempt
|
|
785
786
|
// TODO(fulu): need to remove the bad blocks from the SeenBlockInputCache
|
|
786
787
|
this.state = {status: BatchStatus.AwaitingDownload, blocks: [], payloadEnvelopes: null};
|
|
788
|
+
this.resetRequests();
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* After a partial download `requests` only cover the data still missing from the downloaded blocks,
|
|
793
|
+
* e.g. a single column. Once those blocks are dropped, request the whole batch again, otherwise
|
|
794
|
+
* every peer fails the retry for lack of blocks to validate the data against.
|
|
795
|
+
*/
|
|
796
|
+
private resetRequests(): void {
|
|
797
|
+
this.requests = this.getRequests([]);
|
|
787
798
|
}
|
|
788
799
|
|
|
789
800
|
/** Helper to construct typed BatchError. Stack traces are correct as the error is thrown above */
|
package/src/sync/range/chain.ts
CHANGED
|
@@ -225,6 +225,9 @@ export class SyncChain {
|
|
|
225
225
|
Math.floor((localFinalizedEpoch - this.lastEpochWithProcessBlocks) / EPOCHS_PER_BATCH) * EPOCHS_PER_BATCH;
|
|
226
226
|
this.advanceChain(lastEpochWithProcessBlocksAligned);
|
|
227
227
|
|
|
228
|
+
// stopSyncing() cleared the retry timer, but peers can still be in rate limit backoff
|
|
229
|
+
this.scheduleRateLimitBackoffRetry();
|
|
230
|
+
|
|
228
231
|
// Potentially download new batches and process pending
|
|
229
232
|
this.triggerBatchDownloader();
|
|
230
233
|
this.triggerBatchProcessor();
|
|
@@ -417,8 +420,10 @@ export class SyncChain {
|
|
|
417
420
|
this.rateLimitBackoffTimeout = setTimeout(
|
|
418
421
|
() => {
|
|
419
422
|
this.rateLimitBackoffTimeout = undefined;
|
|
420
|
-
|
|
423
|
+
// Must run before the downloader, it drops expired peers without triggering a download. If it ran
|
|
424
|
+
// after, a backoff expiring in between would leave the peer idle with no retry scheduled.
|
|
421
425
|
this.scheduleRateLimitBackoffRetry();
|
|
426
|
+
this.triggerBatchDownloader();
|
|
422
427
|
},
|
|
423
428
|
Math.max(0, retryAt - now)
|
|
424
429
|
);
|
package/src/sync/range/range.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {IBeaconStateViewGloas, computeStartSlotAtEpoch, isStatePostGloas} from "
|
|
|
5
5
|
import {Epoch, Status, fulu} from "@lodestar/types";
|
|
6
6
|
import {Logger, prettyPrintIndices, toRootHex} from "@lodestar/utils";
|
|
7
7
|
import {IBlockInput} from "../../chain/blocks/blockInput/types.js";
|
|
8
|
+
import {PayloadError, PayloadErrorCode} from "../../chain/blocks/importExecutionPayload.js";
|
|
8
9
|
import {AttestationImportOpt, ImportBlockOpts} from "../../chain/blocks/index.js";
|
|
9
10
|
import {assertLinearChainSegment} from "../../chain/blocks/utils/chainSegment.js";
|
|
10
11
|
import {BlockError} from "../../chain/errors/index.js";
|
|
@@ -207,22 +208,50 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
|
|
|
207
208
|
blsVerifyOnMainThread: false,
|
|
208
209
|
};
|
|
209
210
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
211
|
+
try {
|
|
212
|
+
if (this.opts?.disableProcessAsChainSegment) {
|
|
213
|
+
// Should only be used for debugging or testing
|
|
214
|
+
for (const block of blocks) {
|
|
215
|
+
await this.chain.processBlock(block, flags);
|
|
216
|
+
const payloadEnvelope = payloadEnvelopes?.get(block.slot);
|
|
217
|
+
if (payloadEnvelope?.hasPayloadEnvelope()) {
|
|
218
|
+
await this.chain.processExecutionPayload(payloadEnvelope);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
} else {
|
|
222
|
+
const {orphaned, skipped} = await this.chain.processChainSegment(blocks, payloadEnvelopes, flags);
|
|
223
|
+
// We log orphaned payloads to work with different clients
|
|
224
|
+
// in the future, consider applying penalties in certain conditions
|
|
225
|
+
// make sure the payload source was from range sync in that case
|
|
226
|
+
for (const {slot, payloadEnvelopeInput} of orphaned) {
|
|
227
|
+
const {peerIdStr, source} = payloadEnvelopeInput.getPayloadEnvelopeSource();
|
|
228
|
+
let client = "unknown";
|
|
229
|
+
if (peerIdStr !== undefined) {
|
|
230
|
+
try {
|
|
231
|
+
client = this.getConnectedPeerSyncMeta(peerIdStr).client;
|
|
232
|
+
} catch {
|
|
233
|
+
// peer disconnected since serving the envelope, keep "unknown"
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
this.logger.debug("Orphaned payload envelope in range sync batch", {
|
|
237
|
+
slot,
|
|
238
|
+
root: payloadEnvelopeInput.blockRootHex,
|
|
239
|
+
peer: peerIdStr ?? "unknown",
|
|
240
|
+
source,
|
|
241
|
+
client,
|
|
242
|
+
skipped,
|
|
243
|
+
});
|
|
217
244
|
}
|
|
218
245
|
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
246
|
+
} catch (e) {
|
|
247
|
+
if (
|
|
248
|
+
e instanceof PayloadError &&
|
|
249
|
+
(e.type.code === PayloadErrorCode.INVALID_SIGNATURE ||
|
|
250
|
+
e.type.code === PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR ||
|
|
251
|
+
e.type.code === PayloadErrorCode.EXECUTION_ENGINE_INVALID)
|
|
252
|
+
) {
|
|
253
|
+
const {payloadInput} = e;
|
|
254
|
+
const {peerIdStr, source} = payloadInput.getPayloadEnvelopeSource();
|
|
226
255
|
let client = "unknown";
|
|
227
256
|
if (peerIdStr !== undefined) {
|
|
228
257
|
try {
|
|
@@ -231,15 +260,18 @@ export class RangeSync extends (EventEmitter as {new (): RangeSyncEmitter}) {
|
|
|
231
260
|
// peer disconnected since serving the envelope, keep "unknown"
|
|
232
261
|
}
|
|
233
262
|
}
|
|
234
|
-
this.logger.debug("
|
|
235
|
-
slot,
|
|
236
|
-
root:
|
|
263
|
+
this.logger.debug("Removing invalid payload envelope in range sync batch", {
|
|
264
|
+
slot: payloadInput.slot,
|
|
265
|
+
root: payloadInput.blockRootHex,
|
|
237
266
|
peer: peerIdStr ?? "unknown",
|
|
238
267
|
source,
|
|
239
268
|
client,
|
|
240
|
-
|
|
269
|
+
code: e.type.code,
|
|
241
270
|
});
|
|
271
|
+
// The batch is downloaded again, it would otherwise reuse the cached envelope and fail the same way
|
|
272
|
+
this.chain.seenPayloadEnvelopeInputCache.removeInvalid(payloadInput);
|
|
242
273
|
}
|
|
274
|
+
throw e;
|
|
243
275
|
}
|
|
244
276
|
};
|
|
245
277
|
|
package/src/sync/unknownBlock.ts
CHANGED
|
@@ -1426,20 +1426,23 @@ export class BlockInputSync {
|
|
|
1426
1426
|
pendingPayload.status = PendingPayloadInputStatus.downloaded;
|
|
1427
1427
|
break;
|
|
1428
1428
|
|
|
1429
|
+
// The invalid envelope is evicted from the seen cache in the cases below, otherwise the next
|
|
1430
|
+
// attempt would reuse it instead of fetching another envelope from peers
|
|
1429
1431
|
case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
|
|
1430
1432
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1433
|
+
this.chain.seenPayloadEnvelopeInputCache.removeInvalid(pendingPayload.payloadInput);
|
|
1431
1434
|
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.elInvalid);
|
|
1432
1435
|
break;
|
|
1433
1436
|
|
|
1434
1437
|
case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
|
|
1435
1438
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1439
|
+
this.chain.seenPayloadEnvelopeInputCache.removeInvalid(pendingPayload.payloadInput);
|
|
1436
1440
|
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidEnvelope);
|
|
1437
1441
|
break;
|
|
1438
1442
|
|
|
1439
1443
|
case PayloadErrorCode.INVALID_SIGNATURE:
|
|
1440
|
-
// TODO GLOAS: Decide how invalid payload inputs should eventually leave memory without
|
|
1441
|
-
// reintroducing envelope replacement / recreation flows.
|
|
1442
1444
|
this.logger.debug("Error processing payload from unknown sync", logCtx, res.err);
|
|
1445
|
+
this.chain.seenPayloadEnvelopeInputCache.removeInvalid(pendingPayload.payloadInput);
|
|
1443
1446
|
this.removePendingPayloadAndDescendants(rootHex, DroppedItemReason.invalidSignature);
|
|
1444
1447
|
break;
|
|
1445
1448
|
|
|
@@ -1879,8 +1882,6 @@ export class BlockInputSync {
|
|
|
1879
1882
|
|
|
1880
1883
|
// Once a parent payload is invalid, every descendant waiting on that payload lineage becomes unrecoverable too.
|
|
1881
1884
|
private removePendingPayloadAndDescendants(rootHex: RootHex, headReason: DroppedItemReason): void {
|
|
1882
|
-
// Keep PayloadEnvelopeInput resident in the seen cache. importBlock() owns that object and
|
|
1883
|
-
// later validation/finalization logic decides when it can leave memory.
|
|
1884
1885
|
if (this.pendingPayloads.delete(rootHex)) {
|
|
1885
1886
|
this.metrics?.blockInputSync.removedPayloads.inc({reason: headReason}, 1);
|
|
1886
1887
|
}
|
package/src/util/execution.ts
CHANGED
|
@@ -266,14 +266,15 @@ type SlotEnvelopeBytes = {slot: Slot; envelopeBytes: Uint8Array};
|
|
|
266
266
|
|
|
267
267
|
type RangeEntry = ArchivedEnvelope & {slot: Slot};
|
|
268
268
|
|
|
269
|
-
/** What
|
|
270
|
-
export type
|
|
269
|
+
/** What an envelope that cannot be rebuilt (EL-unavailable body or body root mismatch) means on a given serving path */
|
|
270
|
+
export type ReconstructMissPolicy = "throw" | "omit";
|
|
271
271
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
272
|
+
/**
|
|
273
|
+
* An archived envelope that could not be rebuilt. `"unavailable"`: the EL does not have the block or its
|
|
274
|
+
* block access list (BODY_UNAVAILABLE). `"mismatch"`: an EL body does not hash to its stored root, a local
|
|
275
|
+
* inconsistency (BODY_ROOT_MISMATCH). `error` carries the matching code for paths that surface it.
|
|
276
|
+
*/
|
|
277
|
+
export type RebuildMiss = {slot: Slot; reason: "unavailable" | "mismatch"; error: EnvelopeReconstructionError};
|
|
277
278
|
|
|
278
279
|
/**
|
|
279
280
|
* Stream finalized envelopes over [startSlot, endSlot) as serialized bytes, rebuilding header
|
|
@@ -405,7 +406,14 @@ async function reconstructEnvelopesBatch(
|
|
|
405
406
|
// A zero-length block access list cannot be valid, RLP encodes an empty list as 0xc0
|
|
406
407
|
if (body == null || body.withdrawals == null || body.blockAccessList == null || body.blockAccessList.length === 0) {
|
|
407
408
|
metrics?.payloadEnvelopeReconstruction.envelopes.inc({result: "unavailable"});
|
|
408
|
-
return {
|
|
409
|
+
return {
|
|
410
|
+
slot,
|
|
411
|
+
reason: "unavailable",
|
|
412
|
+
error: new EnvelopeReconstructionError(
|
|
413
|
+
{code: EnvelopeReconstructionErrorCode.BODY_UNAVAILABLE, slot},
|
|
414
|
+
`execution client cannot serve the payload body or block access list for archived envelope slot=${slot}`
|
|
415
|
+
),
|
|
416
|
+
};
|
|
409
417
|
}
|
|
410
418
|
try {
|
|
411
419
|
const envelope = signedHeaderEnvelopeToFull(headerEnvelope, {
|