@lodestar/beacon-node 1.43.0-dev.d166e3b6f7 → 1.43.0-dev.dfb984e779
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 +3 -2
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/lodestar/index.js +1 -1
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +6 -3
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +19 -8
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +31 -20
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/blocks/index.d.ts +5 -3
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +28 -9
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeProcessor.js +2 -2
- package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
- package/lib/chain/blocks/types.d.ts +2 -2
- package/lib/chain/blocks/types.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.d.ts +23 -2
- package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.js +81 -12
- package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
- package/lib/chain/blocks/verifyBlock.d.ts +3 -2
- package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.js +30 -5
- package/lib/chain/blocks/verifyBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.js +15 -4
- package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
- package/lib/chain/chain.d.ts +1 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +7 -3
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/blockError.d.ts +8 -1
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +2 -0
- package/lib/chain/errors/blockError.js.map +1 -1
- package/lib/chain/interface.d.ts +1 -1
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +8 -2
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +1 -0
- package/lib/chain/validation/block.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +1 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +4 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/processor/gossipHandlers.js +4 -6
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js +14 -6
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/blobSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/blobSidecarsByRange.js +11 -5
- package/lib/network/reqresp/handlers/blobSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +17 -5
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +7 -4
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
- package/lib/node/notifier.js +7 -1
- package/lib/node/notifier.js.map +1 -1
- package/lib/sync/range/batch.d.ts +12 -2
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +56 -30
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts +6 -2
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +4 -3
- 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 +17 -6
- package/lib/sync/range/range.js.map +1 -1
- package/lib/sync/types.d.ts +34 -0
- package/lib/sync/types.d.ts.map +1 -1
- package/lib/sync/types.js +34 -0
- package/lib/sync/types.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +24 -1
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +649 -53
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts +46 -10
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +147 -24
- 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 +6 -2
- package/lib/sync/utils/downloadByRoot.js.map +1 -1
- package/lib/sync/utils/pendingBlocksTree.d.ts +0 -1
- package/lib/sync/utils/pendingBlocksTree.d.ts.map +1 -1
- package/lib/sync/utils/pendingBlocksTree.js +0 -9
- package/lib/sync/utils/pendingBlocksTree.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/beacon/blocks/index.ts +5 -2
- package/src/api/impl/lodestar/index.ts +1 -1
- package/src/chain/blocks/importBlock.ts +4 -2
- package/src/chain/blocks/importExecutionPayload.ts +36 -21
- package/src/chain/blocks/index.ts +44 -12
- package/src/chain/blocks/payloadEnvelopeProcessor.ts +2 -2
- package/src/chain/blocks/types.ts +2 -2
- package/src/chain/blocks/utils/chainSegment.ts +106 -17
- package/src/chain/blocks/verifyBlock.ts +35 -6
- package/src/chain/blocks/verifyBlocksSanityChecks.ts +16 -7
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +2 -2
- package/src/chain/chain.ts +11 -3
- package/src/chain/errors/blockError.ts +4 -1
- package/src/chain/interface.ts +5 -1
- package/src/chain/produceBlock/produceBlockBody.ts +8 -2
- package/src/chain/validation/block.ts +1 -0
- package/src/metrics/metrics/lodestar.ts +4 -0
- package/src/network/processor/gossipHandlers.ts +6 -6
- package/src/network/reqresp/handlers/beaconBlocksByRange.ts +14 -6
- package/src/network/reqresp/handlers/blobSidecarsByRange.ts +11 -5
- package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +17 -5
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +7 -4
- package/src/node/notifier.ts +8 -1
- package/src/sync/range/batch.ts +90 -35
- package/src/sync/range/chain.ts +13 -5
- package/src/sync/range/range.ts +18 -6
- package/src/sync/types.ts +72 -0
- package/src/sync/unknownBlock.ts +810 -57
- package/src/sync/utils/downloadByRange.ts +256 -39
- package/src/sync/utils/downloadByRoot.ts +12 -2
- package/src/sync/utils/pendingBlocksTree.ts +0 -15
|
@@ -128,6 +128,7 @@ export async function importBlock(
|
|
|
128
128
|
blockDelaySec,
|
|
129
129
|
currentSlot,
|
|
130
130
|
fork >= ForkSeq.gloas ? ExecutionStatus.PayloadSeparated : executionStatus,
|
|
131
|
+
// TODO GLOAS: this is not useful post-gloas, may need to remove it?
|
|
131
132
|
dataAvailabilityStatus
|
|
132
133
|
);
|
|
133
134
|
|
|
@@ -135,8 +136,9 @@ export async function importBlock(
|
|
|
135
136
|
// Some block event handlers require state being in state cache so need to do this before emitting EventType.block
|
|
136
137
|
this.regen.processState(blockRootHex, postState);
|
|
137
138
|
|
|
138
|
-
// For
|
|
139
|
-
|
|
139
|
+
// For range sync, PayloadEnvelope is created before reaching this
|
|
140
|
+
// we also don't need to trigger getBlobs() in that case
|
|
141
|
+
if (fork >= ForkSeq.gloas && !opts.fromRangeSync) {
|
|
140
142
|
const payloadInput = this.seenPayloadEnvelopeInputCache.add({
|
|
141
143
|
blockRootHex,
|
|
142
144
|
block: block as SignedBeaconBlock<ForkPostGloas>,
|
|
@@ -75,21 +75,22 @@ function toForkChoiceExecutionStatus(status: ExecutionPayloadStatus): PayloadExe
|
|
|
75
75
|
* The envelope is only verified here, no state mutation. State effects from the payload
|
|
76
76
|
* are applied on the next block via processParentExecutionPayload.
|
|
77
77
|
*
|
|
78
|
+
* The DA wait must have run upstream (range sync awaits DA in `verifyBlocksInEpoch` for the
|
|
79
|
+
* whole segment; gossip / API path uses the `processExecutionPayload` wrapper below).
|
|
80
|
+
*
|
|
78
81
|
* Steps:
|
|
79
82
|
* 1. Emit `execution_payload_available` event for payload attestation
|
|
80
83
|
* 2. Get the ProtoBlock from fork choice
|
|
81
|
-
* 3.
|
|
82
|
-
* 4.
|
|
83
|
-
* 5.
|
|
84
|
-
* 6.
|
|
85
|
-
* 7.
|
|
86
|
-
* 8.
|
|
87
|
-
* 9. Emit `execution_payload` event
|
|
84
|
+
* 3. Regenerate state for envelope verification
|
|
85
|
+
* 4. Verify envelope (fields against state, signature, and EL in parallel where possible)
|
|
86
|
+
* 5. Persist verified payload envelope to hot DB (waits for write-queue space for backpressure)
|
|
87
|
+
* 6. Update fork choice (transitions the block's PENDING variant to FULL)
|
|
88
|
+
* 7. Record metrics for payload envelope and column sources
|
|
89
|
+
* 8. Emit `execution_payload` event
|
|
88
90
|
*/
|
|
89
91
|
export async function importExecutionPayload(
|
|
90
92
|
this: BeaconChain,
|
|
91
93
|
payloadInput: PayloadEnvelopeInput,
|
|
92
|
-
signal: AbortSignal,
|
|
93
94
|
opts: ImportPayloadOpts = {}
|
|
94
95
|
): Promise<void> {
|
|
95
96
|
const signedEnvelope = payloadInput.getPayloadEnvelope();
|
|
@@ -119,11 +120,7 @@ export async function importExecutionPayload(
|
|
|
119
120
|
});
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
// 3.
|
|
123
|
-
// The helper is shared with future gloas sync services; take the single-item batch form here.
|
|
124
|
-
await verifyPayloadsDataAvailability([payloadInput], signal);
|
|
125
|
-
|
|
126
|
-
// 4. Regenerate state for envelope verification
|
|
123
|
+
// 3. Regenerate state for envelope verification
|
|
127
124
|
const blockState = await this.regen.getBlockSlotState(
|
|
128
125
|
protoBlock,
|
|
129
126
|
protoBlock.slot,
|
|
@@ -137,7 +134,7 @@ export async function importExecutionPayload(
|
|
|
137
134
|
});
|
|
138
135
|
}
|
|
139
136
|
|
|
140
|
-
//
|
|
137
|
+
// 4. Verify envelope fields against state first to fail fast before the EL + BLS work.
|
|
141
138
|
// When validSignature is true, gossip/API has already verified both the signature and the
|
|
142
139
|
// executionRequestsRoot, so we skip those checks here.
|
|
143
140
|
try {
|
|
@@ -154,7 +151,7 @@ export async function importExecutionPayload(
|
|
|
154
151
|
);
|
|
155
152
|
}
|
|
156
153
|
|
|
157
|
-
//
|
|
154
|
+
// 4a. Run EL and signature verification in parallel
|
|
158
155
|
const [execResult, signatureValid] = await Promise.all([
|
|
159
156
|
this.executionEngine.notifyNewPayload(
|
|
160
157
|
fork,
|
|
@@ -176,12 +173,12 @@ export async function importExecutionPayload(
|
|
|
176
173
|
),
|
|
177
174
|
]);
|
|
178
175
|
|
|
179
|
-
//
|
|
176
|
+
// 4b. Check signature verification result
|
|
180
177
|
if (!signatureValid) {
|
|
181
178
|
throw new PayloadError({code: PayloadErrorCode.INVALID_SIGNATURE});
|
|
182
179
|
}
|
|
183
180
|
|
|
184
|
-
//
|
|
181
|
+
// 4c. Handle EL response
|
|
185
182
|
switch (execResult.status) {
|
|
186
183
|
case ExecutionPayloadStatus.VALID:
|
|
187
184
|
break;
|
|
@@ -207,7 +204,7 @@ export async function importExecutionPayload(
|
|
|
207
204
|
});
|
|
208
205
|
}
|
|
209
206
|
|
|
210
|
-
//
|
|
207
|
+
// 5. Persist payload envelope to hot DB. Wait for write-queue space here to apply backpressure
|
|
211
208
|
// on the import pipeline during sync, then perform the write asynchronously to avoid blocking.
|
|
212
209
|
await this.unfinalizedPayloadEnvelopeWrites.waitForSpace();
|
|
213
210
|
this.unfinalizedPayloadEnvelopeWrites.push(payloadInput).catch((e) => {
|
|
@@ -220,17 +217,17 @@ export async function importExecutionPayload(
|
|
|
220
217
|
}
|
|
221
218
|
});
|
|
222
219
|
|
|
223
|
-
//
|
|
220
|
+
// 6. Update fork choice, transitions the block's PENDING variant to FULL
|
|
224
221
|
const execStatus = toForkChoiceExecutionStatus(execResult.status);
|
|
225
222
|
this.forkChoice.onExecutionPayload(blockRootHex, blockHashHex, envelope.payload.blockNumber, execStatus);
|
|
226
223
|
|
|
227
|
-
//
|
|
224
|
+
// 7. Record metrics for payload envelope and column sources
|
|
228
225
|
this.metrics?.importPayload.bySource.inc({source: payloadInput.getPayloadEnvelopeSource().source});
|
|
229
226
|
for (const {source} of payloadInput.getSampledColumnsWithSource()) {
|
|
230
227
|
this.metrics?.importPayload.columnsBySource.inc({source});
|
|
231
228
|
}
|
|
232
229
|
|
|
233
|
-
//
|
|
230
|
+
// 8. Emit event after payload is fully verified and imported to fork choice, only for recent enough payloads
|
|
234
231
|
if (this.clock.currentSlot - slot < EVENTSTREAM_EMIT_RECENT_EXECUTION_PAYLOAD_SLOTS) {
|
|
235
232
|
this.emitter.emit(routes.events.EventType.executionPayload, {
|
|
236
233
|
slot,
|
|
@@ -249,3 +246,21 @@ export async function importExecutionPayload(
|
|
|
249
246
|
blockHash: blockHashHex,
|
|
250
247
|
});
|
|
251
248
|
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Process an execution payload envelope end-to-end: wait for DA, then import.
|
|
252
|
+
*
|
|
253
|
+
* Used by the PayloadEnvelopeProcessor queue (gossip / API / unknown-payload sync) — i.e.
|
|
254
|
+
* callers that have NOT already awaited DA themselves. Range sync's inline dispatch in
|
|
255
|
+
* processBlocks skips this wrapper and calls `importExecutionPayload` directly, since
|
|
256
|
+
* `verifyBlocksInEpoch` already awaited DA for the segment.
|
|
257
|
+
*/
|
|
258
|
+
export async function processExecutionPayload(
|
|
259
|
+
this: BeaconChain,
|
|
260
|
+
payloadInput: PayloadEnvelopeInput,
|
|
261
|
+
signal: AbortSignal,
|
|
262
|
+
opts: ImportPayloadOpts = {}
|
|
263
|
+
): Promise<void> {
|
|
264
|
+
await verifyPayloadsDataAvailability([payloadInput], signal);
|
|
265
|
+
await importExecutionPayload.call(this, payloadInput, opts);
|
|
266
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {SignedBeaconBlock} from "@lodestar/types";
|
|
1
|
+
import {SignedBeaconBlock, Slot} from "@lodestar/types";
|
|
2
2
|
import {isErrorAborted, toRootHex} from "@lodestar/utils";
|
|
3
3
|
import {Metrics} from "../../metrics/metrics.js";
|
|
4
4
|
import {nextEventLoop} from "../../util/eventLoop.js";
|
|
@@ -8,6 +8,8 @@ import {BlockError, BlockErrorCode, isBlockErrorAborted} from "../errors/index.j
|
|
|
8
8
|
import {BlockProcessOpts} from "../options.js";
|
|
9
9
|
import {IBlockInput} from "./blockInput/types.js";
|
|
10
10
|
import {importBlock} from "./importBlock.js";
|
|
11
|
+
import {importExecutionPayload} from "./importExecutionPayload.js";
|
|
12
|
+
import {PayloadEnvelopeInput} from "./payloadEnvelopeInput/payloadEnvelopeInput.js";
|
|
11
13
|
import {FullyVerifiedBlock, ImportBlockOpts} from "./types.js";
|
|
12
14
|
import {assertLinearChainSegment} from "./utils/chainSegment.js";
|
|
13
15
|
import {verifyBlocksInEpoch} from "./verifyBlock.js";
|
|
@@ -21,20 +23,24 @@ const QUEUE_MAX_LENGTH = 256;
|
|
|
21
23
|
* BlockProcessor processes block jobs in a queued fashion, one after the other.
|
|
22
24
|
*/
|
|
23
25
|
export class BlockProcessor {
|
|
24
|
-
readonly jobQueue: JobItemQueue<[IBlockInput[], ImportBlockOpts], void>;
|
|
26
|
+
readonly jobQueue: JobItemQueue<[IBlockInput[], Map<Slot, PayloadEnvelopeInput> | null, ImportBlockOpts], void>;
|
|
25
27
|
|
|
26
28
|
constructor(chain: BeaconChain, metrics: Metrics | null, opts: BlockProcessOpts, signal: AbortSignal) {
|
|
27
|
-
this.jobQueue = new JobItemQueue<[IBlockInput[], ImportBlockOpts], void>(
|
|
28
|
-
(job, importOpts) => {
|
|
29
|
-
return processBlocks.call(chain, job, {...opts, ...importOpts});
|
|
29
|
+
this.jobQueue = new JobItemQueue<[IBlockInput[], Map<Slot, PayloadEnvelopeInput> | null, ImportBlockOpts], void>(
|
|
30
|
+
(job, payloadEnvelopes, importOpts) => {
|
|
31
|
+
return processBlocks.call(chain, job, payloadEnvelopes, {...opts, ...importOpts});
|
|
30
32
|
},
|
|
31
33
|
{maxLength: QUEUE_MAX_LENGTH, noYieldIfOneItem: true, signal},
|
|
32
34
|
metrics?.blockProcessorQueue ?? undefined
|
|
33
35
|
);
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
async processBlocksJob(
|
|
37
|
-
|
|
38
|
+
async processBlocksJob(
|
|
39
|
+
job: IBlockInput[],
|
|
40
|
+
payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
|
|
41
|
+
opts: ImportBlockOpts = {}
|
|
42
|
+
): Promise<void> {
|
|
43
|
+
await this.jobQueue.push(job, payloadEnvelopes, opts);
|
|
38
44
|
}
|
|
39
45
|
}
|
|
40
46
|
|
|
@@ -51,16 +57,13 @@ export class BlockProcessor {
|
|
|
51
57
|
export async function processBlocks(
|
|
52
58
|
this: BeaconChain,
|
|
53
59
|
blocks: IBlockInput[],
|
|
60
|
+
payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
|
|
54
61
|
opts: BlockProcessOpts & ImportBlockOpts
|
|
55
62
|
): Promise<void> {
|
|
56
63
|
if (blocks.length === 0) {
|
|
57
64
|
return; // TODO: or throw?
|
|
58
65
|
}
|
|
59
66
|
|
|
60
|
-
if (blocks.length > 1) {
|
|
61
|
-
assertLinearChainSegment(this.config, blocks);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
67
|
try {
|
|
65
68
|
const {relevantBlocks, parentSlots, parentBlock} = verifyBlocksSanityChecks(this, blocks, opts);
|
|
66
69
|
|
|
@@ -70,10 +73,25 @@ export async function processBlocks(
|
|
|
70
73
|
return;
|
|
71
74
|
}
|
|
72
75
|
|
|
76
|
+
const {warnings: orphanedPayloads} = assertLinearChainSegment(
|
|
77
|
+
this.config,
|
|
78
|
+
relevantBlocks,
|
|
79
|
+
payloadEnvelopes,
|
|
80
|
+
parentBlock
|
|
81
|
+
);
|
|
82
|
+
if (orphanedPayloads != null) {
|
|
83
|
+
for (const orphaned of orphanedPayloads) {
|
|
84
|
+
this.logger.debug("Orphaned payload envelope in chain segment", {
|
|
85
|
+
slot: orphaned.slot,
|
|
86
|
+
blockRoot: orphaned.payloadEnvelopeInput.blockRootHex,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
73
91
|
// Fully verify a block to be imported immediately after. Does not produce any side-effects besides adding intermediate
|
|
74
92
|
// states in the state cache through regen.
|
|
75
93
|
const {postStates, dataAvailabilityStatuses, proposerBalanceDeltas, segmentExecStatus, indexedAttestationsByBlock} =
|
|
76
|
-
await verifyBlocksInEpoch.call(this, parentBlock, relevantBlocks, opts);
|
|
94
|
+
await verifyBlocksInEpoch.call(this, parentBlock, relevantBlocks, payloadEnvelopes, opts);
|
|
77
95
|
|
|
78
96
|
// If segmentExecStatus has lvhForkchoice then, the entire segment should be invalid
|
|
79
97
|
// and we need to further propagate
|
|
@@ -103,6 +121,20 @@ export async function processBlocks(
|
|
|
103
121
|
for (const fullyVerifiedBlock of fullyVerifiedBlocks) {
|
|
104
122
|
// TODO: Consider batching importBlock too if it takes significant time
|
|
105
123
|
await importBlock.call(this, fullyVerifiedBlock, opts);
|
|
124
|
+
|
|
125
|
+
const slot = fullyVerifiedBlock.blockInput.getBlock().message.slot;
|
|
126
|
+
const payloadInput = payloadEnvelopes?.get(slot);
|
|
127
|
+
if (payloadInput?.hasPayloadEnvelope()) {
|
|
128
|
+
if (!payloadInput.isComplete()) {
|
|
129
|
+
// we validated DA before reaching this
|
|
130
|
+
throw new Error(`Payload envelope for slot ${slot} not complete after DA verification`);
|
|
131
|
+
}
|
|
132
|
+
// we already awaited DA in verifyBlocksInEpoch for this segment
|
|
133
|
+
// TODO GLOAS: may need FullyVerifiedPayload here with DatAvailabilityStatus added from here
|
|
134
|
+
// the current flow use that data from the forkchoice pending node which is not correct
|
|
135
|
+
await importExecutionPayload.call(this, payloadInput, {validSignature: false});
|
|
136
|
+
}
|
|
137
|
+
|
|
106
138
|
await nextEventLoop();
|
|
107
139
|
}
|
|
108
140
|
} catch (e) {
|
|
@@ -2,7 +2,7 @@ import {Metrics} from "../../metrics/metrics.js";
|
|
|
2
2
|
import {JobItemQueue} from "../../util/queue/index.js";
|
|
3
3
|
import type {BeaconChain} from "../chain.js";
|
|
4
4
|
import {PayloadEnvelopeInput} from "../seenCache/seenPayloadEnvelopeInput.js";
|
|
5
|
-
import {
|
|
5
|
+
import {processExecutionPayload} from "./importExecutionPayload.js";
|
|
6
6
|
import {ImportPayloadOpts} from "./types.js";
|
|
7
7
|
|
|
8
8
|
// TODO GLOAS: Set to be equal to DEFAULT_MAX_PENDING_UNFINALIZED_PAYLOAD_ENVELOPE_WRITES for now
|
|
@@ -30,7 +30,7 @@ export class PayloadEnvelopeProcessor {
|
|
|
30
30
|
this.jobQueue = new JobItemQueue<[PayloadEnvelopeInput, ImportPayloadOpts], void>(
|
|
31
31
|
(payloadInput, opts) => {
|
|
32
32
|
this.importStatus.set(payloadInput, PayloadEnvelopeImportStatus.importing);
|
|
33
|
-
return
|
|
33
|
+
return processExecutionPayload.call(chain, payloadInput, signal, opts);
|
|
34
34
|
},
|
|
35
35
|
{maxLength: QUEUE_MAX_LENGTH, noYieldIfOneItem: true, signal},
|
|
36
36
|
metrics?.payloadEnvelopeProcessorQueue ?? undefined
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type {ChainForkConfig} from "@lodestar/config";
|
|
2
|
-
import {BlockExecutionStatus} from "@lodestar/fork-choice";
|
|
2
|
+
import type {BlockExecutionStatus, PayloadExecutionStatus} from "@lodestar/fork-choice";
|
|
3
3
|
import {ForkSeq} from "@lodestar/params";
|
|
4
4
|
import {DataAvailabilityStatus, IBeaconStateView, computeEpochAtSlot} from "@lodestar/state-transition";
|
|
5
5
|
import type {IndexedAttestation, Slot, fulu} from "@lodestar/types";
|
|
@@ -107,5 +107,5 @@ export type FullyVerifiedBlock = {
|
|
|
107
107
|
/** Seen timestamp seconds */
|
|
108
108
|
seenTimestampSec: number;
|
|
109
109
|
/** If the execution payload couldn't be verified because of EL syncing status, used in optimistic sync */
|
|
110
|
-
executionStatus: BlockExecutionStatus;
|
|
110
|
+
executionStatus: BlockExecutionStatus | PayloadExecutionStatus;
|
|
111
111
|
};
|
|
@@ -1,29 +1,118 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
|
-
import {
|
|
2
|
+
import {ProtoBlock} from "@lodestar/fork-choice";
|
|
3
|
+
import {Slot, isGloasBeaconBlock, ssz} from "@lodestar/types";
|
|
4
|
+
import {toRootHex} from "@lodestar/utils";
|
|
3
5
|
import {BlockError, BlockErrorCode} from "../../errors/index.js";
|
|
4
6
|
import {IBlockInput} from "../blockInput/types.js";
|
|
7
|
+
import {PayloadEnvelopeInput} from "../payloadEnvelopeInput/payloadEnvelopeInput.js";
|
|
8
|
+
|
|
9
|
+
export type OrphanedPayloadEnvelope = {
|
|
10
|
+
slot: Slot;
|
|
11
|
+
payloadEnvelopeInput: PayloadEnvelopeInput;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type ChainSegmentResult = {warnings: OrphanedPayloadEnvelope[] | null};
|
|
5
15
|
|
|
6
16
|
/**
|
|
7
|
-
* Assert this chain segment of blocks is linear with slot numbers and hashes
|
|
17
|
+
* Assert this chain segment of blocks is linear with slot numbers and hashes,
|
|
18
|
+
* and that the provided envelopes are consistent with their respective blocks.
|
|
19
|
+
*
|
|
20
|
+
* Must be called after verifyBlocksSanityChecks so that parentBlock (from forkchoice)
|
|
21
|
+
* is available to seed the execution hash chain.
|
|
22
|
+
*
|
|
23
|
+
* For each block:
|
|
24
|
+
* - Verifies parent root + slot linearity
|
|
25
|
+
* - For gloas: verifies bid.parentBlockHash matches the tracked execution hash; if not, the
|
|
26
|
+
* previous FULL envelope is treated as orphaned (segment continues as if previous slot was EMPTY)
|
|
27
|
+
* - If an envelope exists for this slot: verifies it references this block's root
|
|
28
|
+
* - Advances the tracked execution hash (FULL if envelope present, EMPTY if not)
|
|
8
29
|
*/
|
|
30
|
+
export function assertLinearChainSegment(
|
|
31
|
+
config: ChainForkConfig,
|
|
32
|
+
blocks: IBlockInput[],
|
|
33
|
+
payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
|
|
34
|
+
parentBlock: ProtoBlock
|
|
35
|
+
): ChainSegmentResult {
|
|
36
|
+
const warnings: OrphanedPayloadEnvelope[] = [];
|
|
9
37
|
|
|
10
|
-
|
|
11
|
-
|
|
38
|
+
// Track the expected execution payload block hash through the segment.
|
|
39
|
+
// Starts from the known forkchoice parent's execution hash.
|
|
40
|
+
// - FULL variant (envelope present for slot): advances to envelope.payload.blockHash
|
|
41
|
+
// - EMPTY variant (no envelope for slot): execution hash is unchanged
|
|
42
|
+
// null only for pre-merge parents, which cannot precede gloas blocks.
|
|
43
|
+
let currentExecHash: string | null = parentBlock.executionPayloadBlockHash;
|
|
44
|
+
// Track the execution hash before the last FULL advancement so we can recover
|
|
45
|
+
// if the next block reveals that envelope was orphaned.
|
|
46
|
+
let prevExecHash: string | null = currentExecHash;
|
|
47
|
+
// The slot whose envelope last advanced currentExecHash (for warning context).
|
|
48
|
+
let lastFullSlot: Slot | null = null;
|
|
49
|
+
|
|
50
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
12
51
|
const block = blocks[i].getBlock();
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
52
|
+
const slot = block.message.slot;
|
|
53
|
+
|
|
54
|
+
if (i > 0) {
|
|
55
|
+
const prevBlock = blocks[i - 1].getBlock();
|
|
56
|
+
// Ensure parent root matches the previous block's root
|
|
57
|
+
if (
|
|
58
|
+
!ssz.Root.equals(
|
|
59
|
+
config.getForkTypes(prevBlock.message.slot).BeaconBlock.hashTreeRoot(prevBlock.message),
|
|
60
|
+
block.message.parentRoot
|
|
61
|
+
)
|
|
62
|
+
) {
|
|
63
|
+
throw new BlockError(block, {code: BlockErrorCode.NON_LINEAR_PARENT_ROOTS});
|
|
64
|
+
}
|
|
65
|
+
// Ensure slots are strictly increasing
|
|
66
|
+
if (slot <= prevBlock.message.slot) {
|
|
67
|
+
throw new BlockError(block, {code: BlockErrorCode.NON_LINEAR_SLOTS});
|
|
68
|
+
}
|
|
23
69
|
}
|
|
24
|
-
|
|
25
|
-
if (
|
|
26
|
-
|
|
70
|
+
|
|
71
|
+
if (isGloasBeaconBlock(block.message) && currentExecHash !== null) {
|
|
72
|
+
// Verify the bid's parentBlockHash matches the tracked execution hash.
|
|
73
|
+
// This ensures the block was built on the correct FULL or EMPTY variant of its parent.
|
|
74
|
+
const bidParentHash = toRootHex(block.message.body.signedExecutionPayloadBid.message.parentBlockHash);
|
|
75
|
+
if (bidParentHash !== currentExecHash) {
|
|
76
|
+
// Maybe the previous slot's FULL envelope was orphaned — try falling back.
|
|
77
|
+
// If even prevExecHash doesn't match, the segment is non-linear.
|
|
78
|
+
if (bidParentHash !== prevExecHash) {
|
|
79
|
+
throw new BlockError(block, {
|
|
80
|
+
code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN,
|
|
81
|
+
parentRoot: toRootHex(block.message.parentRoot),
|
|
82
|
+
parentBlockHash: bidParentHash,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (lastFullSlot !== null && payloadEnvelopes !== null) {
|
|
86
|
+
const orphanedInput = payloadEnvelopes.get(lastFullSlot);
|
|
87
|
+
if (orphanedInput != null) {
|
|
88
|
+
warnings.push({slot: lastFullSlot, payloadEnvelopeInput: orphanedInput});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
currentExecHash = prevExecHash;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const payloadInput = payloadEnvelopes?.get(slot) ?? null;
|
|
95
|
+
const payloadEnvelope = payloadInput?.hasPayloadEnvelope() ? payloadInput.getPayloadEnvelope() : null;
|
|
96
|
+
if (payloadEnvelope !== null) {
|
|
97
|
+
// Verify the envelope references this block's root
|
|
98
|
+
const blockRoot = toRootHex(config.getForkTypes(slot).BeaconBlock.hashTreeRoot(block.message));
|
|
99
|
+
const envelopeBlockRoot = toRootHex(payloadEnvelope.message.beaconBlockRoot);
|
|
100
|
+
if (blockRoot !== envelopeBlockRoot) {
|
|
101
|
+
throw new BlockError(block, {
|
|
102
|
+
code: BlockErrorCode.ENVELOPE_BLOCK_ROOT_MISMATCH,
|
|
103
|
+
envelopeBlockRoot,
|
|
104
|
+
blockRoot,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// FULL variant: save state before advancing, then advance
|
|
109
|
+
prevExecHash = currentExecHash;
|
|
110
|
+
lastFullSlot = slot;
|
|
111
|
+
currentExecHash = toRootHex(payloadEnvelope.message.payload.blockHash);
|
|
112
|
+
}
|
|
113
|
+
// EMPTY variant: currentExecHash unchanged
|
|
27
114
|
}
|
|
28
115
|
}
|
|
116
|
+
|
|
117
|
+
return {warnings: warnings.length > 0 ? warnings : null};
|
|
29
118
|
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import {ExecutionStatus, ProtoBlock} from "@lodestar/fork-choice";
|
|
2
|
-
import {ForkName, isForkPostFulu} from "@lodestar/params";
|
|
2
|
+
import {ForkName, ForkSeq, isForkPostFulu} from "@lodestar/params";
|
|
3
3
|
import {DataAvailabilityStatus, IBeaconStateView, computeEpochAtSlot} from "@lodestar/state-transition";
|
|
4
|
-
import {IndexedAttestation, deneb} from "@lodestar/types";
|
|
4
|
+
import {IndexedAttestation, Slot, deneb} from "@lodestar/types";
|
|
5
|
+
import {getBlobKzgCommitments} from "../../util/dataColumns.js";
|
|
5
6
|
import type {BeaconChain} from "../chain.js";
|
|
6
7
|
import {BlockError, BlockErrorCode} from "../errors/index.js";
|
|
7
8
|
import {BlockProcessOpts} from "../options.js";
|
|
8
9
|
import {RegenCaller} from "../regen/index.js";
|
|
9
10
|
import {DAType, IBlockInput} from "./blockInput/index.js";
|
|
11
|
+
import {PayloadEnvelopeInput} from "./payloadEnvelopeInput/payloadEnvelopeInput.js";
|
|
10
12
|
import {ImportBlockOpts} from "./types.js";
|
|
11
13
|
import {DENEB_BLOWFISH_BANNER} from "./utils/blowfishBanner.js";
|
|
12
14
|
import {ELECTRA_GIRAFFE_BANNER} from "./utils/giraffeBanner.js";
|
|
@@ -16,6 +18,7 @@ import {verifyBlocksDataAvailability} from "./verifyBlocksDataAvailability.js";
|
|
|
16
18
|
import {SegmentExecStatus, verifyBlocksExecutionPayload} from "./verifyBlocksExecutionPayloads.js";
|
|
17
19
|
import {verifyBlocksSignatures} from "./verifyBlocksSignatures.js";
|
|
18
20
|
import {verifyBlocksStateTransitionOnly} from "./verifyBlocksStateTransitionOnly.js";
|
|
21
|
+
import {verifyPayloadsDataAvailability} from "./verifyPayloadsDataAvailability.js";
|
|
19
22
|
|
|
20
23
|
/**
|
|
21
24
|
* Verifies 1 or more blocks are fully valid; from a linear sequence of blocks.
|
|
@@ -32,6 +35,7 @@ export async function verifyBlocksInEpoch(
|
|
|
32
35
|
this: BeaconChain,
|
|
33
36
|
parentBlock: ProtoBlock,
|
|
34
37
|
blockInputs: IBlockInput[],
|
|
38
|
+
payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
|
|
35
39
|
opts: BlockProcessOpts & ImportBlockOpts
|
|
36
40
|
): Promise<{
|
|
37
41
|
postStates: IBeaconStateView[];
|
|
@@ -110,6 +114,26 @@ export async function verifyBlocksInEpoch(
|
|
|
110
114
|
});
|
|
111
115
|
}
|
|
112
116
|
|
|
117
|
+
// Pick the data-availability source by fork:
|
|
118
|
+
// - Pre-Gloas: blob/Fulu-column data lives in IBlockInput → verifyBlocksDataAvailability.
|
|
119
|
+
// - Post-Gloas: verifyPayloadsDataAvailability
|
|
120
|
+
const daAvailabilityPromise =
|
|
121
|
+
fork >= ForkSeq.gloas
|
|
122
|
+
? (async () => {
|
|
123
|
+
const payloadInputsForDa: PayloadEnvelopeInput[] = [];
|
|
124
|
+
for (const input of blockInputs) {
|
|
125
|
+
const pi = payloadEnvelopes?.get(input.slot);
|
|
126
|
+
if (pi !== undefined) payloadInputsForDa.push(pi);
|
|
127
|
+
}
|
|
128
|
+
await verifyPayloadsDataAvailability(payloadInputsForDa, abortController.signal);
|
|
129
|
+
return {
|
|
130
|
+
// post-gloas, DataAvailabilityStatus is NotRequired for forkChoice.onBlock() ProtoBlock
|
|
131
|
+
dataAvailabilityStatuses: blockInputs.map(() => DataAvailabilityStatus.NotRequired),
|
|
132
|
+
availableTime: Date.now(),
|
|
133
|
+
};
|
|
134
|
+
})()
|
|
135
|
+
: verifyBlocksDataAvailability(blockInputs, abortController.signal);
|
|
136
|
+
|
|
113
137
|
// batch all I/O operations to reduce overhead
|
|
114
138
|
const [
|
|
115
139
|
segmentExecStatus,
|
|
@@ -119,8 +143,8 @@ export async function verifyBlocksInEpoch(
|
|
|
119
143
|
] = await Promise.all([
|
|
120
144
|
verifyExecutionPayloadsPromise,
|
|
121
145
|
|
|
122
|
-
// data availability
|
|
123
|
-
|
|
146
|
+
// data availability (fork-specific; see daAvailabilityPromise above)
|
|
147
|
+
daAvailabilityPromise,
|
|
124
148
|
|
|
125
149
|
// Run state transition only
|
|
126
150
|
// TODO: Ensure it yields to allow flushing to workers and engine API
|
|
@@ -149,6 +173,9 @@ export async function verifyBlocksInEpoch(
|
|
|
149
173
|
opts
|
|
150
174
|
)
|
|
151
175
|
: Promise.resolve({verifySignaturesTime: Date.now()}),
|
|
176
|
+
|
|
177
|
+
// TODO GLOAS: can verify payload signatures in batch too
|
|
178
|
+
// maybe chain with the above verifyBlocksSignatures()
|
|
152
179
|
]);
|
|
153
180
|
|
|
154
181
|
if (opts.verifyOnly !== true) {
|
|
@@ -200,7 +227,9 @@ export async function verifyBlocksInEpoch(
|
|
|
200
227
|
blockInputs.length === 1 &&
|
|
201
228
|
// gossip blocks have seenTimestampSec
|
|
202
229
|
opts.seenTimestampSec !== undefined &&
|
|
230
|
+
// PreData (pre-deneb) and NoData (gloas) carry no blob data on the block — skip metric
|
|
203
231
|
blockInputs[0].type !== DAType.PreData &&
|
|
232
|
+
blockInputs[0].type !== DAType.NoData &&
|
|
204
233
|
executionStatuses[0] === ExecutionStatus.Valid
|
|
205
234
|
) {
|
|
206
235
|
// Find the max time when the block was actually verified
|
|
@@ -209,8 +238,8 @@ export async function verifyBlocksInEpoch(
|
|
|
209
238
|
this.metrics?.gossipBlock.receivedToFullyVerifiedTime.observe(recvTofullyVerifedTime);
|
|
210
239
|
|
|
211
240
|
const verifiedToBlobsAvailabiltyTime = Math.max(availableTime - fullyVerifiedTime, 0) / 1000;
|
|
212
|
-
const block = blockInputs[0].getBlock()
|
|
213
|
-
const numBlobs = block.
|
|
241
|
+
const block = blockInputs[0].getBlock();
|
|
242
|
+
const numBlobs = getBlobKzgCommitments(blockInputs[0].forkName, block as deneb.SignedBeaconBlock).length;
|
|
214
243
|
|
|
215
244
|
this.metrics?.gossipBlock.verifiedToBlobsAvailabiltyTime.observe({numBlobs}, verifiedToBlobsAvailabiltyTime);
|
|
216
245
|
this.logger.verbose("Verified blockInput fully with blobs availability", {
|
|
@@ -90,15 +90,24 @@ export function verifyBlocksSanityChecks(
|
|
|
90
90
|
} else {
|
|
91
91
|
// When importing a block segment, only the first NON-IGNORED block must be known to the fork-choice.
|
|
92
92
|
const parentRoot = toRootHex(block.message.parentRoot);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
parentRoot,
|
|
96
|
-
toRootHex(block.message.body.signedExecutionPayloadBid.message.parentBlockHash)
|
|
97
|
-
)
|
|
98
|
-
: chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
|
|
99
|
-
if (!parentBlock) {
|
|
93
|
+
const parentBlockDefaultStatus = chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
|
|
94
|
+
if (!parentBlockDefaultStatus) {
|
|
100
95
|
throw new BlockError(block, {code: BlockErrorCode.PARENT_UNKNOWN, parentRoot});
|
|
101
96
|
}
|
|
97
|
+
|
|
98
|
+
parentBlock = parentBlockDefaultStatus;
|
|
99
|
+
if (isGloasBeaconBlock(block.message)) {
|
|
100
|
+
const parentBlockHash = toRootHex(block.message.body.signedExecutionPayloadBid.message.parentBlockHash);
|
|
101
|
+
const parentBlockWithPayload = chain.forkChoice.getBlockHexAndBlockHash(parentRoot, parentBlockHash);
|
|
102
|
+
if (!parentBlockWithPayload) {
|
|
103
|
+
throw new BlockError(block, {
|
|
104
|
+
code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN,
|
|
105
|
+
parentRoot,
|
|
106
|
+
parentBlockHash,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
parentBlock = parentBlockWithPayload;
|
|
110
|
+
}
|
|
102
111
|
// Parent is known to the fork-choice
|
|
103
112
|
parentBlockSlot = parentBlock.slot;
|
|
104
113
|
}
|
|
@@ -32,8 +32,8 @@ export function verifyExecutionPayloadEnvelope(
|
|
|
32
32
|
const payload = envelope.payload;
|
|
33
33
|
|
|
34
34
|
// Verify consistency with the beacon block.
|
|
35
|
-
// Compute header root on a
|
|
36
|
-
const headerValue =
|
|
35
|
+
// Compute header root on a clone of latestBlockHeader to avoid mutating state.
|
|
36
|
+
const headerValue = ssz.phase0.BeaconBlockHeader.clone(state.latestBlockHeader);
|
|
37
37
|
if (byteArrayEquals(headerValue.stateRoot, ssz.Root.defaultValue())) {
|
|
38
38
|
headerValue.stateRoot = state.hashTreeRoot();
|
|
39
39
|
}
|
package/src/chain/chain.ts
CHANGED
|
@@ -891,6 +891,10 @@ export class BeaconChain implements IBeaconChain {
|
|
|
891
891
|
parentBlockSlot: Slot,
|
|
892
892
|
parentBlockRootHex: RootHex
|
|
893
893
|
): Promise<electra.ExecutionRequests> {
|
|
894
|
+
// at the fork boundary, parent is pre-gloas
|
|
895
|
+
if (!isForkPostGloas(this.config.getForkName(parentBlockSlot))) {
|
|
896
|
+
return ssz.electra.ExecutionRequests.defaultValue();
|
|
897
|
+
}
|
|
894
898
|
const envelope = await this.getExecutionPayloadEnvelope(parentBlockSlot, parentBlockRootHex);
|
|
895
899
|
if (envelope === null) {
|
|
896
900
|
throw Error(`Parent execution payload envelope not found slot=${parentBlockSlot}, root=${parentBlockRootHex}`);
|
|
@@ -1094,11 +1098,15 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1094
1098
|
}
|
|
1095
1099
|
|
|
1096
1100
|
async processBlock(block: IBlockInput, opts?: ImportBlockOpts): Promise<void> {
|
|
1097
|
-
return this.blockProcessor.processBlocksJob([block], opts);
|
|
1101
|
+
return this.blockProcessor.processBlocksJob([block], null, opts);
|
|
1098
1102
|
}
|
|
1099
1103
|
|
|
1100
|
-
async processChainSegment(
|
|
1101
|
-
|
|
1104
|
+
async processChainSegment(
|
|
1105
|
+
blocks: IBlockInput[],
|
|
1106
|
+
payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
|
|
1107
|
+
opts?: ImportBlockOpts
|
|
1108
|
+
): Promise<void> {
|
|
1109
|
+
await this.blockProcessor.processBlocksJob(blocks, payloadEnvelopes, opts);
|
|
1102
1110
|
}
|
|
1103
1111
|
|
|
1104
1112
|
async processExecutionPayload(payloadInput: PayloadEnvelopeInput, opts?: ImportPayloadOpts): Promise<void> {
|
|
@@ -74,6 +74,8 @@ export enum BlockErrorCode {
|
|
|
74
74
|
PARENT_EXECUTION_INVALID = "BLOCK_ERROR_PARENT_EXECUTION_INVALID",
|
|
75
75
|
/** The block's parent execution payload (defined by bid.parent_block_hash) has not been seen */
|
|
76
76
|
PARENT_PAYLOAD_UNKNOWN = "BLOCK_ERROR_PARENT_PAYLOAD_UNKNOWN",
|
|
77
|
+
/** An execution payload envelope in the chain segment references a block root that does not match its slot's block */
|
|
78
|
+
ENVELOPE_BLOCK_ROOT_MISMATCH = "BLOCK_ERROR_ENVELOPE_BLOCK_ROOT_MISMATCH",
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
type ExecutionErrorStatus = Exclude<
|
|
@@ -107,6 +109,7 @@ export type BlockErrorType =
|
|
|
107
109
|
| {code: BlockErrorCode.NOT_LATER_THAN_PARENT; parentSlot: Slot; slot: Slot}
|
|
108
110
|
| {code: BlockErrorCode.NON_LINEAR_PARENT_ROOTS}
|
|
109
111
|
| {code: BlockErrorCode.NON_LINEAR_SLOTS}
|
|
112
|
+
| {code: BlockErrorCode.ENVELOPE_BLOCK_ROOT_MISMATCH; envelopeBlockRoot: RootHex; blockRoot: RootHex}
|
|
110
113
|
| {code: BlockErrorCode.PER_BLOCK_PROCESSING_ERROR; error: Error}
|
|
111
114
|
| {code: BlockErrorCode.BEACON_CHAIN_ERROR; error: Error}
|
|
112
115
|
| {code: BlockErrorCode.KNOWN_BAD_BLOCK}
|
|
@@ -120,7 +123,7 @@ export type BlockErrorType =
|
|
|
120
123
|
| {code: BlockErrorCode.TOO_MANY_KZG_COMMITMENTS; blobKzgCommitmentsLen: number; commitmentLimit: number}
|
|
121
124
|
| {code: BlockErrorCode.BID_PARENT_ROOT_MISMATCH; bidParentRoot: RootHex; blockParentRoot: RootHex}
|
|
122
125
|
| {code: BlockErrorCode.PARENT_EXECUTION_INVALID; parentRoot: RootHex}
|
|
123
|
-
| {code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN; parentBlockHash: RootHex};
|
|
126
|
+
| {code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN; parentRoot: RootHex; parentBlockHash: RootHex};
|
|
124
127
|
|
|
125
128
|
export class BlockGossipError extends GossipActionError<BlockErrorType> {}
|
|
126
129
|
|
package/src/chain/interface.ts
CHANGED
|
@@ -250,7 +250,11 @@ export interface IBeaconChain {
|
|
|
250
250
|
/** Process a block until complete */
|
|
251
251
|
processBlock(block: IBlockInput, opts?: ImportBlockOpts): Promise<void>;
|
|
252
252
|
/** Process a chain of blocks until complete */
|
|
253
|
-
processChainSegment(
|
|
253
|
+
processChainSegment(
|
|
254
|
+
blocks: IBlockInput[],
|
|
255
|
+
payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
|
|
256
|
+
opts?: ImportBlockOpts
|
|
257
|
+
): Promise<void>;
|
|
254
258
|
|
|
255
259
|
/** Process execution payload envelope: verify, import to fork choice, and persist to DB */
|
|
256
260
|
processExecutionPayload(payloadInput: PayloadEnvelopeInput, opts?: ImportPayloadOpts): Promise<void>;
|