@lodestar/beacon-node 1.43.0-dev.0bc48d3b54 → 1.43.0-dev.1540a78cd0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/impl/beacon/blocks/index.js +4 -6
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/state/utils.d.ts +2 -2
- package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/utils.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/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +0 -4
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/interface.d.ts +4 -4
- package/lib/chain/archiveStore/interface.d.ts.map +1 -1
- package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts +4 -4
- package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts.map +1 -1
- package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts +2 -2
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.js +110 -58
- package/lib/chain/archiveStore/utils/archiveBlocks.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 +26 -14
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +73 -86
- 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 -10
- 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 +14 -20
- 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.d.ts +24 -0
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -0
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +76 -0
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -0
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.d.ts +1 -1
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.d.ts.map +1 -1
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js +2 -11
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js.map +1 -1
- package/lib/chain/chain.d.ts +6 -5
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +16 -5
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +3 -3
- package/lib/chain/emitter.d.ts.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/errors/executionPayloadBid.d.ts +5 -0
- package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadBid.js +1 -0
- package/lib/chain/errors/executionPayloadBid.js.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.d.ts +5 -0
- package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.js +1 -0
- package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +5 -17
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/interface.d.ts +5 -4
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +30 -10
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -2
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +29 -12
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +11 -4
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +20 -18
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.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/chain/validation/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +13 -1
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +19 -9
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/db/repositories/executionPayloadEnvelopeArchive.js +1 -1
- package/lib/db/repositories/executionPayloadEnvelopeArchive.js.map +1 -1
- package/lib/execution/engine/mock.d.ts.map +1 -1
- package/lib/execution/engine/mock.js +3 -0
- package/lib/execution/engine/mock.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/gossip/topic.d.ts +21 -2
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/network.js +1 -1
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +5 -4
- 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/lib/util/sszBytes.d.ts.map +1 -1
- package/lib/util/sszBytes.js +16 -3
- package/lib/util/sszBytes.js.map +1 -1
- package/package.json +16 -15
- package/src/api/impl/beacon/blocks/index.ts +6 -6
- package/src/api/impl/beacon/state/utils.ts +2 -2
- package/src/api/impl/lodestar/index.ts +1 -1
- package/src/api/impl/validator/index.ts +0 -4
- package/src/chain/archiveStore/archiveStore.ts +5 -5
- package/src/chain/archiveStore/interface.ts +4 -4
- package/src/chain/archiveStore/strategies/frequencyStateArchiveStrategy.ts +4 -4
- package/src/chain/archiveStore/utils/archiveBlocks.ts +153 -94
- package/src/chain/blocks/importBlock.ts +4 -2
- package/src/chain/blocks/importExecutionPayload.ts +92 -107
- package/src/chain/blocks/index.ts +44 -13
- package/src/chain/blocks/payloadEnvelopeProcessor.ts +2 -2
- package/src/chain/blocks/types.ts +14 -25
- 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 +129 -0
- package/src/chain/blocks/writePayloadEnvelopeInputToDb.ts +9 -18
- package/src/chain/chain.ts +33 -19
- package/src/chain/emitter.ts +3 -3
- package/src/chain/errors/blockError.ts +4 -1
- package/src/chain/errors/executionPayloadBid.ts +6 -0
- package/src/chain/errors/executionPayloadEnvelope.ts +6 -0
- package/src/chain/forkChoice/index.ts +2 -22
- package/src/chain/interface.ts +9 -3
- package/src/chain/prepareNextSlot.ts +42 -12
- package/src/chain/produceBlock/produceBlockBody.ts +32 -10
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +22 -20
- package/src/chain/validation/block.ts +1 -0
- package/src/chain/validation/executionPayloadBid.ts +14 -0
- package/src/chain/validation/executionPayloadEnvelope.ts +20 -10
- package/src/db/repositories/executionPayloadEnvelopeArchive.ts +1 -1
- package/src/execution/engine/mock.ts +5 -1
- package/src/metrics/metrics/lodestar.ts +4 -0
- package/src/network/network.ts +1 -1
- package/src/network/processor/gossipHandlers.ts +7 -4
- 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
- package/src/util/sszBytes.ts +21 -3
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
isStatePostBellatrix,
|
|
11
11
|
isStatePostGloas,
|
|
12
12
|
} from "@lodestar/state-transition";
|
|
13
|
-
import {Bytes32, Slot} from "@lodestar/types";
|
|
13
|
+
import {Bytes32, Slot, electra} from "@lodestar/types";
|
|
14
14
|
import {Logger, fromHex, isErrorAborted, sleep} from "@lodestar/utils";
|
|
15
15
|
import {GENESIS_SLOT, ZERO_HASH_HEX} from "../constants/constants.js";
|
|
16
16
|
import {BuilderStatus} from "../execution/builder/http.js";
|
|
@@ -83,7 +83,7 @@ export class PrepareNextSlotScheduler {
|
|
|
83
83
|
const headBlock = this.chain.recomputeForkChoiceHead(ForkchoiceCaller.prepareNextSlot);
|
|
84
84
|
const {slot: headSlot, blockRoot: headRoot} = headBlock;
|
|
85
85
|
// may be updated below if we predict a proposer-boost-reorg
|
|
86
|
-
let
|
|
86
|
+
let updatedHead = headBlock;
|
|
87
87
|
|
|
88
88
|
// PS: previously this was comparing slots, but that gave no leway on the skipped
|
|
89
89
|
// slots on epoch bounday. Making it more fluid.
|
|
@@ -148,7 +148,7 @@ export class PrepareNextSlotScheduler {
|
|
|
148
148
|
{dontTransferCache: !isEpochTransition},
|
|
149
149
|
RegenCaller.predictProposerHead
|
|
150
150
|
);
|
|
151
|
-
|
|
151
|
+
updatedHead = proposerHead;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
// Update the builder status, if enabled shoot an api call to check status
|
|
@@ -165,14 +165,19 @@ export class PrepareNextSlotScheduler {
|
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
let parentBlockHash: Bytes32;
|
|
168
|
+
let isExtendingPayload = false;
|
|
168
169
|
if (isStatePostGloas(updatedPrepareState)) {
|
|
169
|
-
|
|
170
|
+
isExtendingPayload = this.chain.forkChoice.shouldExtendPayload(updatedHead.blockRoot);
|
|
171
|
+
parentBlockHash = isExtendingPayload
|
|
170
172
|
? updatedPrepareState.latestExecutionPayloadBid.blockHash
|
|
171
173
|
: updatedPrepareState.latestExecutionPayloadBid.parentBlockHash;
|
|
172
174
|
} else {
|
|
173
175
|
parentBlockHash = updatedPrepareState.latestExecutionPayloadHeader.blockHash;
|
|
174
176
|
}
|
|
175
177
|
|
|
178
|
+
// Reused by the SSE emit below to avoid a second DB lookup on cache miss
|
|
179
|
+
let parentExecutionRequests: electra.ExecutionRequests | undefined;
|
|
180
|
+
|
|
176
181
|
if (feeRecipient) {
|
|
177
182
|
const preparationTime =
|
|
178
183
|
computeTimeAtSlot(this.config, prepareSlot, this.chain.genesisTime) - Date.now() / 1000;
|
|
@@ -182,6 +187,13 @@ export class PrepareNextSlotScheduler {
|
|
|
182
187
|
const finalizedBlockHash =
|
|
183
188
|
this.chain.forkChoice.getFinalizedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX;
|
|
184
189
|
|
|
190
|
+
if (isExtendingPayload) {
|
|
191
|
+
parentExecutionRequests = await this.chain.getParentExecutionRequests(
|
|
192
|
+
updatedHead.slot,
|
|
193
|
+
updatedHead.blockRoot
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
185
197
|
// awaiting here instead of throwing an async call because there is no other task
|
|
186
198
|
// left for scheduler and this gives nice semantics to catch and log errors in the
|
|
187
199
|
// try/catch wrapper here.
|
|
@@ -189,12 +201,13 @@ export class PrepareNextSlotScheduler {
|
|
|
189
201
|
this.chain,
|
|
190
202
|
this.logger,
|
|
191
203
|
fork as ForkPostBellatrix, // State is of execution type
|
|
192
|
-
fromHex(
|
|
204
|
+
fromHex(updatedHead.blockRoot),
|
|
193
205
|
parentBlockHash,
|
|
194
206
|
safeBlockHash,
|
|
195
207
|
finalizedBlockHash,
|
|
196
208
|
updatedPrepareState,
|
|
197
|
-
feeRecipient
|
|
209
|
+
feeRecipient,
|
|
210
|
+
parentExecutionRequests
|
|
198
211
|
);
|
|
199
212
|
this.logger.verbose("PrepareNextSlotScheduler prepared new payload", {
|
|
200
213
|
prepareSlot,
|
|
@@ -203,21 +216,38 @@ export class PrepareNextSlotScheduler {
|
|
|
203
216
|
});
|
|
204
217
|
}
|
|
205
218
|
|
|
219
|
+
if (ForkSeq[fork] >= ForkSeq.gloas) {
|
|
220
|
+
// Cutoff = slot of the parent of the block we'll actually build on (post-reorg).
|
|
221
|
+
// Steady state: cache holds just 2 entries — head (parent for next-slot production)
|
|
222
|
+
// and head.parent (proposer-boost-reorg fallback). Anything older is evicted.
|
|
223
|
+
const updatedHeadParent = this.chain.forkChoice.getBlockHexDefaultStatus(updatedHead.parentRoot);
|
|
224
|
+
if (updatedHeadParent) {
|
|
225
|
+
this.chain.seenPayloadEnvelopeInputCache.pruneBelow(updatedHeadParent.slot);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
206
229
|
this.computeStateHashTreeRoot(updatedPrepareState, isEpochTransition);
|
|
207
230
|
|
|
208
|
-
// If emitPayloadAttributes is true emit a SSE payloadAttributes event
|
|
231
|
+
// If emitPayloadAttributes is true emit a SSE payloadAttributes event for
|
|
232
|
+
// every slot. Without the flag, only emit the event if we are proposing in the next slot.
|
|
209
233
|
if (
|
|
210
|
-
this.chain.opts.emitPayloadAttributes === true &&
|
|
234
|
+
(feeRecipient || this.chain.opts.emitPayloadAttributes === true) &&
|
|
211
235
|
this.chain.emitter.listenerCount(routes.events.EventType.payloadAttributes)
|
|
212
236
|
) {
|
|
237
|
+
// if we didn't fetch above (not proposing), SSE still needs it here
|
|
238
|
+
if (!parentExecutionRequests && isExtendingPayload) {
|
|
239
|
+
parentExecutionRequests = await this.chain.getParentExecutionRequests(
|
|
240
|
+
updatedHead.slot,
|
|
241
|
+
updatedHead.blockRoot
|
|
242
|
+
);
|
|
243
|
+
}
|
|
213
244
|
const data = getPayloadAttributesForSSE(fork as ForkPostBellatrix, this.chain, {
|
|
214
245
|
prepareState: updatedPrepareState,
|
|
215
246
|
prepareSlot,
|
|
216
|
-
parentBlockRoot: fromHex(
|
|
247
|
+
parentBlockRoot: fromHex(updatedHead.blockRoot),
|
|
217
248
|
parentBlockHash,
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
feeRecipient: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
249
|
+
feeRecipient: feeRecipient ?? "0x0000000000000000000000000000000000000000",
|
|
250
|
+
parentExecutionRequests,
|
|
221
251
|
});
|
|
222
252
|
this.chain.emitter.emit(routes.events.EventType.payloadAttributes, {data, version: fork});
|
|
223
253
|
}
|
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
electra,
|
|
47
47
|
fulu,
|
|
48
48
|
gloas,
|
|
49
|
+
ssz,
|
|
49
50
|
} from "@lodestar/types";
|
|
50
51
|
import {Logger, byteArrayEquals, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
|
|
51
52
|
import {ZERO_HASH_HEX} from "../../constants/index.js";
|
|
@@ -213,9 +214,13 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
213
214
|
});
|
|
214
215
|
|
|
215
216
|
// Get execution payload from EL
|
|
216
|
-
const
|
|
217
|
+
const isExtendingPayload = this.forkChoice.shouldExtendPayload(toRootHex(parentBlockRoot));
|
|
218
|
+
const parentBlockHash = isExtendingPayload
|
|
217
219
|
? currentState.latestExecutionPayloadBid.blockHash
|
|
218
220
|
: currentState.latestExecutionPayloadBid.parentBlockHash;
|
|
221
|
+
const parentExecutionRequests = isExtendingPayload
|
|
222
|
+
? await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot)
|
|
223
|
+
: ssz.electra.ExecutionRequests.defaultValue();
|
|
219
224
|
const prepareRes = await prepareExecutionPayload(
|
|
220
225
|
this,
|
|
221
226
|
this.logger,
|
|
@@ -225,7 +230,8 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
225
230
|
safeBlockHash,
|
|
226
231
|
finalizedBlockHash ?? ZERO_HASH_HEX,
|
|
227
232
|
currentState,
|
|
228
|
-
feeRecipient
|
|
233
|
+
feeRecipient,
|
|
234
|
+
parentExecutionRequests
|
|
229
235
|
);
|
|
230
236
|
|
|
231
237
|
const {prepType, payloadId} = prepareRes;
|
|
@@ -269,6 +275,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
269
275
|
value: 0,
|
|
270
276
|
executionPayment: 0,
|
|
271
277
|
blobKzgCommitments: blobsBundle.commitments,
|
|
278
|
+
executionRequestsRoot: ssz.electra.ExecutionRequests.hashTreeRoot(executionRequests),
|
|
272
279
|
};
|
|
273
280
|
const signedBid: gloas.SignedExecutionPayloadBid = {
|
|
274
281
|
message: bid,
|
|
@@ -280,6 +287,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
280
287
|
gloasBody.signedExecutionPayloadBid = signedBid;
|
|
281
288
|
// TODO GLOAS: Get payload attestations from pool for previous slot
|
|
282
289
|
gloasBody.payloadAttestations = [];
|
|
290
|
+
gloasBody.parentExecutionRequests = parentExecutionRequests;
|
|
283
291
|
blockBody = gloasBody as AssembledBodyType<T>;
|
|
284
292
|
|
|
285
293
|
// Store execution payload data required to construct execution payload envelope later
|
|
@@ -616,7 +624,8 @@ export async function prepareExecutionPayload(
|
|
|
616
624
|
safeBlockHash: RootHex,
|
|
617
625
|
finalizedBlockHash: RootHex,
|
|
618
626
|
state: IBeaconStateViewBellatrix,
|
|
619
|
-
suggestedFeeRecipient: string
|
|
627
|
+
suggestedFeeRecipient: string,
|
|
628
|
+
parentExecutionRequests?: electra.ExecutionRequests
|
|
620
629
|
): Promise<{prepType: PayloadPreparationType; payloadId: PayloadId}> {
|
|
621
630
|
const timestamp = computeTimeAtSlot(chain.config, state.slot, state.genesisTime);
|
|
622
631
|
const prevRandao = state.getRandaoMix(state.epoch);
|
|
@@ -653,6 +662,7 @@ export async function prepareExecutionPayload(
|
|
|
653
662
|
parentBlockRoot,
|
|
654
663
|
parentBlockHash,
|
|
655
664
|
feeRecipient: suggestedFeeRecipient,
|
|
665
|
+
parentExecutionRequests,
|
|
656
666
|
});
|
|
657
667
|
|
|
658
668
|
payloadId = await chain.executionEngine.notifyForkchoiceUpdate(
|
|
@@ -711,12 +721,14 @@ export function getPayloadAttributesForSSE(
|
|
|
711
721
|
parentBlockRoot,
|
|
712
722
|
parentBlockHash,
|
|
713
723
|
feeRecipient,
|
|
724
|
+
parentExecutionRequests,
|
|
714
725
|
}: {
|
|
715
726
|
prepareState: IBeaconStateViewBellatrix;
|
|
716
727
|
prepareSlot: Slot;
|
|
717
728
|
parentBlockRoot: Root;
|
|
718
729
|
parentBlockHash: Bytes32;
|
|
719
730
|
feeRecipient: string;
|
|
731
|
+
parentExecutionRequests?: electra.ExecutionRequests;
|
|
720
732
|
}
|
|
721
733
|
): SSEPayloadAttributes {
|
|
722
734
|
const payloadAttributes = preparePayloadAttributes(fork, chain, {
|
|
@@ -725,6 +737,7 @@ export function getPayloadAttributesForSSE(
|
|
|
725
737
|
parentBlockRoot,
|
|
726
738
|
parentBlockHash,
|
|
727
739
|
feeRecipient,
|
|
740
|
+
parentExecutionRequests,
|
|
728
741
|
});
|
|
729
742
|
|
|
730
743
|
let parentBlockNumber: number;
|
|
@@ -763,12 +776,14 @@ function preparePayloadAttributes(
|
|
|
763
776
|
parentBlockRoot,
|
|
764
777
|
parentBlockHash,
|
|
765
778
|
feeRecipient,
|
|
779
|
+
parentExecutionRequests,
|
|
766
780
|
}: {
|
|
767
781
|
prepareState: IBeaconStateViewBellatrix;
|
|
768
782
|
prepareSlot: Slot;
|
|
769
783
|
parentBlockRoot: Root;
|
|
770
784
|
parentBlockHash: Bytes32;
|
|
771
785
|
feeRecipient: string;
|
|
786
|
+
parentExecutionRequests?: electra.ExecutionRequests;
|
|
772
787
|
}
|
|
773
788
|
): SSEPayloadAttributes["payloadAttributes"] {
|
|
774
789
|
const timestamp = computeTimeAtSlot(chain.config, prepareSlot, prepareState.genesisTime);
|
|
@@ -786,13 +801,20 @@ function preparePayloadAttributes(
|
|
|
786
801
|
|
|
787
802
|
if (isStatePostGloas(prepareState)) {
|
|
788
803
|
const isExtendingPayload = byteArrayEquals(parentBlockHash, prepareState.latestExecutionPayloadBid.blockHash);
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
804
|
+
if (isExtendingPayload) {
|
|
805
|
+
if (parentExecutionRequests === undefined) {
|
|
806
|
+
throw new Error("parentExecutionRequests required when extending full parent");
|
|
807
|
+
}
|
|
808
|
+
(payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
|
|
809
|
+
prepareState.getExpectedWithdrawalsForFullParent(parentExecutionRequests);
|
|
810
|
+
} else {
|
|
811
|
+
// When the parent block is empty, state.payloadExpectedWithdrawals holds a batch
|
|
812
|
+
// already deducted from CL balances but never credited on the EL (the envelope
|
|
813
|
+
// was not delivered). The next payload must carry those same withdrawals to
|
|
814
|
+
// restore CL/EL consistency, otherwise validators permanently lose that balance.
|
|
815
|
+
(payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
|
|
816
|
+
prepareState.payloadExpectedWithdrawals;
|
|
817
|
+
}
|
|
796
818
|
} else {
|
|
797
819
|
// withdrawals logic is now fork aware as it changes on electra fork post capella
|
|
798
820
|
(payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {CheckpointWithHex} from "@lodestar/fork-choice";
|
|
2
2
|
import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
|
3
|
-
import {RootHex} from "@lodestar/types";
|
|
3
|
+
import {RootHex, Slot} from "@lodestar/types";
|
|
4
4
|
import {Logger} from "@lodestar/utils";
|
|
5
5
|
import {Metrics} from "../../metrics/metrics.js";
|
|
6
6
|
import {SerializedCache} from "../../util/serializedCache.js";
|
|
@@ -21,8 +21,15 @@ export type SeenPayloadEnvelopeInputModules = {
|
|
|
21
21
|
/**
|
|
22
22
|
* Cache for tracking PayloadEnvelopeInput instances, keyed by beacon block root.
|
|
23
23
|
*
|
|
24
|
-
* Created during block import when a block is processed.
|
|
25
|
-
*
|
|
24
|
+
* Created during block import when a block is processed. Two pruning paths:
|
|
25
|
+
* - `prepareNextSlot` calls `pruneBelow(headParentSlot)` every slot once the head we'll build
|
|
26
|
+
* on is known.
|
|
27
|
+
* - `onFinalized` calls `pruneBelow(finalizedSlot)` on every finalization for bulk cleanup.
|
|
28
|
+
*
|
|
29
|
+
* Steady state (linear chain, healthy progression): the cache holds ~2 entries — the head
|
|
30
|
+
* (parent for next-slot production) and its parent (proposer-boost-reorg fallback). It can
|
|
31
|
+
* transiently hold more during forks, range-sync bursts, or when `prepareNextSlot` skips
|
|
32
|
+
* ticks; subsequent ticks settle it back.
|
|
26
33
|
*/
|
|
27
34
|
export class SeenPayloadEnvelopeInput {
|
|
28
35
|
private readonly chainEvents: ChainEventEmitter;
|
|
@@ -58,16 +65,7 @@ export class SeenPayloadEnvelopeInput {
|
|
|
58
65
|
}
|
|
59
66
|
|
|
60
67
|
private onFinalized = (checkpoint: CheckpointWithHex): void => {
|
|
61
|
-
|
|
62
|
-
const finalizedSlot = computeStartSlotAtEpoch(checkpoint.epoch);
|
|
63
|
-
let deletedCount = 0;
|
|
64
|
-
for (const [, input] of this.payloadInputs) {
|
|
65
|
-
if (input.slot < finalizedSlot) {
|
|
66
|
-
this.evictPayloadInput(input);
|
|
67
|
-
deletedCount++;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
this.logger?.debug("SeenPayloadEnvelopeInput.onFinalized deleted cached entries", {deletedCount});
|
|
68
|
+
this.pruneBelow(computeStartSlotAtEpoch(checkpoint.epoch));
|
|
71
69
|
};
|
|
72
70
|
|
|
73
71
|
add(props: CreateFromBlockProps): PayloadEnvelopeInput {
|
|
@@ -88,17 +86,21 @@ export class SeenPayloadEnvelopeInput {
|
|
|
88
86
|
return this.payloadInputs.get(blockRootHex)?.hasPayloadEnvelope() ?? false;
|
|
89
87
|
}
|
|
90
88
|
|
|
91
|
-
prune(blockRootHex: RootHex): void {
|
|
92
|
-
const payloadInput = this.payloadInputs.get(blockRootHex);
|
|
93
|
-
if (payloadInput) {
|
|
94
|
-
this.evictPayloadInput(payloadInput);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
89
|
size(): number {
|
|
99
90
|
return this.payloadInputs.size;
|
|
100
91
|
}
|
|
101
92
|
|
|
93
|
+
pruneBelow(slot: Slot): void {
|
|
94
|
+
let deletedCount = 0;
|
|
95
|
+
for (const [, input] of this.payloadInputs) {
|
|
96
|
+
if (input.slot < slot) {
|
|
97
|
+
this.evictPayloadInput(input);
|
|
98
|
+
deletedCount++;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
this.logger?.debug("SeenPayloadEnvelopeInput.pruneBelow deleted entries", {slot, deletedCount});
|
|
102
|
+
}
|
|
103
|
+
|
|
102
104
|
private evictPayloadInput(payloadInput: PayloadEnvelopeInput): void {
|
|
103
105
|
this.serializedCache.delete(payloadInput.getSerializedCacheKeys());
|
|
104
106
|
this.payloadInputs.delete(payloadInput.blockRootHex);
|
|
@@ -103,6 +103,7 @@ export async function validateGossipBlock(
|
|
|
103
103
|
if (chain.forkChoice.getBlockHexAndBlockHash(parentRoot, parentBlockHashHex) === null) {
|
|
104
104
|
throw new BlockGossipError(GossipAction.IGNORE, {
|
|
105
105
|
code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN,
|
|
106
|
+
parentRoot,
|
|
106
107
|
parentBlockHash: parentBlockHashHex,
|
|
107
108
|
});
|
|
108
109
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {PublicKey} from "@chainsafe/blst";
|
|
2
2
|
import {
|
|
3
|
+
computeEpochAtSlot,
|
|
3
4
|
createSingleSignatureSetFromComponents,
|
|
4
5
|
getExecutionPayloadBidSigningRoot,
|
|
5
6
|
isActiveBuilder,
|
|
@@ -76,6 +77,19 @@ async function validateExecutionPayloadBid(
|
|
|
76
77
|
// `SignedProposerPreferences` associated with `bid.slot`.
|
|
77
78
|
// TODO GLOAS: Implement this along with proposer preference
|
|
78
79
|
|
|
80
|
+
// [REJECT] The length of KZG commitments is less than or equal to the limitation defined in the
|
|
81
|
+
// consensus layer -- i.e. validate that
|
|
82
|
+
// `len(bid.blob_kzg_commitments) <= get_blob_parameters(compute_epoch_at_slot(bid.slot)).max_blobs_per_block`.
|
|
83
|
+
const blobKzgCommitmentsLen = bid.blobKzgCommitments.length;
|
|
84
|
+
const maxBlobsPerBlock = chain.config.getMaxBlobsPerBlock(computeEpochAtSlot(bid.slot));
|
|
85
|
+
if (blobKzgCommitmentsLen > maxBlobsPerBlock) {
|
|
86
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
87
|
+
code: ExecutionPayloadBidErrorCode.TOO_MANY_KZG_COMMITMENTS,
|
|
88
|
+
blobKzgCommitmentsLen,
|
|
89
|
+
commitmentLimit: maxBlobsPerBlock,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
79
93
|
// [IGNORE] this is the first signed bid seen with a valid signature from the given builder for this slot.
|
|
80
94
|
if (chain.seenExecutionPayloadBids.isKnown(bid.slot, bid.builderIndex)) {
|
|
81
95
|
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
getExecutionPayloadEnvelopeSignatureSet,
|
|
5
5
|
isStatePostGloas,
|
|
6
6
|
} from "@lodestar/state-transition";
|
|
7
|
-
import {gloas} from "@lodestar/types";
|
|
8
|
-
import {toRootHex} from "@lodestar/utils";
|
|
7
|
+
import {gloas, ssz} from "@lodestar/types";
|
|
8
|
+
import {byteArrayEquals, toRootHex} from "@lodestar/utils";
|
|
9
9
|
import {ExecutionPayloadEnvelopeError, ExecutionPayloadEnvelopeErrorCode, GossipAction} from "../errors/index.js";
|
|
10
10
|
import {IBeaconChain} from "../index.js";
|
|
11
11
|
import {RegenCaller} from "../regen/index.js";
|
|
@@ -53,7 +53,7 @@ async function validateExecutionPayloadEnvelope(
|
|
|
53
53
|
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
54
54
|
code: ExecutionPayloadEnvelopeErrorCode.ENVELOPE_ALREADY_KNOWN,
|
|
55
55
|
blockRoot: blockRootHex,
|
|
56
|
-
slot:
|
|
56
|
+
slot: payload.slotNumber,
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -65,13 +65,13 @@ async function validateExecutionPayloadEnvelope(
|
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
// [IGNORE] The envelope is from a slot greater than or equal to the latest finalized slot -- i.e. validate that `
|
|
68
|
+
// [IGNORE] The envelope is from a slot greater than or equal to the latest finalized slot -- i.e. validate that `payload.slotNumber >= compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)`
|
|
69
69
|
const finalizedCheckpoint = chain.forkChoice.getFinalizedCheckpoint();
|
|
70
70
|
const finalizedSlot = computeStartSlotAtEpoch(finalizedCheckpoint.epoch);
|
|
71
|
-
if (
|
|
71
|
+
if (payload.slotNumber < finalizedSlot) {
|
|
72
72
|
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
73
73
|
code: ExecutionPayloadEnvelopeErrorCode.BELONG_TO_FINALIZED_BLOCK,
|
|
74
|
-
envelopeSlot:
|
|
74
|
+
envelopeSlot: payload.slotNumber,
|
|
75
75
|
finalizedSlot,
|
|
76
76
|
});
|
|
77
77
|
}
|
|
@@ -80,11 +80,11 @@ async function validateExecutionPayloadEnvelope(
|
|
|
80
80
|
// TODO GLOAS: implement this. Technically if we cannot get proto block from fork choice,
|
|
81
81
|
// it is possible that the block didn't pass the validation
|
|
82
82
|
|
|
83
|
-
// [REJECT] `block.slot` equals `
|
|
84
|
-
if (block.slot !==
|
|
83
|
+
// [REJECT] `block.slot` equals `payload.slotNumber`.
|
|
84
|
+
if (block.slot !== payload.slotNumber) {
|
|
85
85
|
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
86
86
|
code: ExecutionPayloadEnvelopeErrorCode.SLOT_MISMATCH,
|
|
87
|
-
envelopeSlot:
|
|
87
|
+
envelopeSlot: payload.slotNumber,
|
|
88
88
|
blockSlot: block.slot,
|
|
89
89
|
});
|
|
90
90
|
}
|
|
@@ -107,6 +107,16 @@ async function validateExecutionPayloadEnvelope(
|
|
|
107
107
|
});
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
// [REJECT] `hash_tree_root(envelope.execution_requests) == bid.execution_requests_root`
|
|
111
|
+
const requestsRoot = ssz.electra.ExecutionRequests.hashTreeRoot(envelope.executionRequests);
|
|
112
|
+
if (!byteArrayEquals(requestsRoot, payloadInput.getBid().executionRequestsRoot)) {
|
|
113
|
+
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
114
|
+
code: ExecutionPayloadEnvelopeErrorCode.EXECUTION_REQUESTS_ROOT_MISMATCH,
|
|
115
|
+
envelopeRequestsRoot: toRootHex(requestsRoot),
|
|
116
|
+
bidRequestsRoot: toRootHex(payloadInput.getBid().executionRequestsRoot),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
110
120
|
// Get the block state to verify the builder's signature.
|
|
111
121
|
const blockState = await chain.regen
|
|
112
122
|
.getState(block.stateRoot, RegenCaller.validateGossipPayloadEnvelope)
|
|
@@ -114,7 +124,7 @@ async function validateExecutionPayloadEnvelope(
|
|
|
114
124
|
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
115
125
|
code: ExecutionPayloadEnvelopeErrorCode.UNKNOWN_BLOCK_STATE,
|
|
116
126
|
blockRoot: blockRootHex,
|
|
117
|
-
slot:
|
|
127
|
+
slot: payload.slotNumber,
|
|
118
128
|
});
|
|
119
129
|
});
|
|
120
130
|
if (!isStatePostGloas(blockState)) {
|
|
@@ -19,7 +19,7 @@ export class ExecutionPayloadEnvelopeArchiveRepository extends Repository<Slot,
|
|
|
19
19
|
* Id is the slot from the envelope
|
|
20
20
|
*/
|
|
21
21
|
getId(value: gloas.SignedExecutionPayloadEnvelope): Slot {
|
|
22
|
-
return value.message.
|
|
22
|
+
return value.message.payload.slotNumber;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
encodeKey(id: Slot): Uint8Array {
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
SLOTS_PER_EPOCH,
|
|
12
12
|
} from "@lodestar/params";
|
|
13
13
|
import {computeTimeAtSlot} from "@lodestar/state-transition";
|
|
14
|
-
import {ExecutionPayload, RootHex, bellatrix, deneb, ssz} from "@lodestar/types";
|
|
14
|
+
import {ExecutionPayload, RootHex, bellatrix, deneb, gloas, ssz} from "@lodestar/types";
|
|
15
15
|
import {fromHex, toRootHex} from "@lodestar/utils";
|
|
16
16
|
import {ZERO_HASH_HEX} from "../../constants/index.js";
|
|
17
17
|
import {INTEROP_BLOCK_HASH} from "../../node/utils/interop/state.js";
|
|
@@ -382,6 +382,10 @@ export class ExecutionEngineMockBackend implements JsonRpcBackend {
|
|
|
382
382
|
(executionPayload as ExecutionPayload<ForkPostCapella>).withdrawals = ssz.capella.Withdrawals.defaultValue();
|
|
383
383
|
}
|
|
384
384
|
|
|
385
|
+
if (ForkSeq[fork] >= ForkSeq.gloas && payloadAttributes.slotNumber != null) {
|
|
386
|
+
(executionPayload as gloas.ExecutionPayload).slotNumber = payloadAttributes.slotNumber;
|
|
387
|
+
}
|
|
388
|
+
|
|
385
389
|
this.preparingPayloads.set(payloadId, {
|
|
386
390
|
executionPayload: serializeExecutionPayload(fork, executionPayload),
|
|
387
391
|
blobsBundle: serializeBlobsBundle({
|
|
@@ -613,6 +613,10 @@ export function createLodestarMetrics(
|
|
|
613
613
|
name: "lodestar_sync_unknown_block_pending_blocks_size",
|
|
614
614
|
help: "Current size of UnknownBlockSync pending blocks cache",
|
|
615
615
|
}),
|
|
616
|
+
pendingPayloads: register.gauge({
|
|
617
|
+
name: "lodestar_sync_unknown_block_pending_payloads_size",
|
|
618
|
+
help: "Current size of UnknownBlockSync pending payloads cache",
|
|
619
|
+
}),
|
|
616
620
|
knownBadBlocks: register.gauge({
|
|
617
621
|
name: "lodestar_sync_unknown_block_known_bad_blocks_size",
|
|
618
622
|
help: "Current size of UnknownBlockSync known bad blocks cache",
|
package/src/network/network.ts
CHANGED
|
@@ -505,7 +505,7 @@ export class Network implements INetwork {
|
|
|
505
505
|
}
|
|
506
506
|
|
|
507
507
|
async publishSignedExecutionPayloadEnvelope(signedEnvelope: gloas.SignedExecutionPayloadEnvelope): Promise<number> {
|
|
508
|
-
const epoch = computeEpochAtSlot(signedEnvelope.message.
|
|
508
|
+
const epoch = computeEpochAtSlot(signedEnvelope.message.payload.slotNumber);
|
|
509
509
|
const boundary = this.config.getForkBoundaryAtEpoch(epoch);
|
|
510
510
|
|
|
511
511
|
return this.publishGossip<GossipType.execution_payload>(
|
|
@@ -198,7 +198,10 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
198
198
|
} catch (e) {
|
|
199
199
|
if (e instanceof BlockGossipError) {
|
|
200
200
|
logger.debug("Gossip block has error", {slot, root: blockShortHex, code: e.type.code});
|
|
201
|
-
if (
|
|
201
|
+
if (
|
|
202
|
+
(e.type.code === BlockErrorCode.PARENT_UNKNOWN || e.type.code === BlockErrorCode.PARENT_PAYLOAD_UNKNOWN) &&
|
|
203
|
+
blockInput
|
|
204
|
+
) {
|
|
202
205
|
chain.emitter.emit(ChainEvent.blockUnknownParent, {
|
|
203
206
|
blockInput,
|
|
204
207
|
peer: peerIdStr,
|
|
@@ -1065,7 +1068,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1065
1068
|
await validateGossipExecutionPayloadEnvelope(chain, signedEnvelope);
|
|
1066
1069
|
} catch (e) {
|
|
1067
1070
|
if (e instanceof ExecutionPayloadEnvelopeError) {
|
|
1068
|
-
const {
|
|
1071
|
+
const {beaconBlockRoot} = signedEnvelope.message;
|
|
1072
|
+
const slot = signedEnvelope.message.payload.slotNumber;
|
|
1069
1073
|
logger.debug("Gossip envelope has error", {slot, root: toRootHex(beaconBlockRoot), code: e.type.code});
|
|
1070
1074
|
if (e.type.code === ExecutionPayloadEnvelopeErrorCode.BLOCK_ROOT_UNKNOWN) {
|
|
1071
1075
|
// TODO GLOAS: UnknownBlockSync to handle this
|
|
@@ -1088,7 +1092,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1088
1092
|
throw e;
|
|
1089
1093
|
}
|
|
1090
1094
|
|
|
1091
|
-
const slot = envelope.
|
|
1095
|
+
const slot = envelope.payload.slotNumber;
|
|
1092
1096
|
const delaySec = seenTimestampSec - computeTimeAtSlot(config, slot, chain.genesisTime);
|
|
1093
1097
|
metrics?.gossipExecutionPayloadEnvelope.elapsedTimeTillReceived.observe({source: OpSource.gossip}, delaySec);
|
|
1094
1098
|
chain.validatorMonitor?.registerExecutionPayloadEnvelope(OpSource.gossip, delaySec, signedEnvelope);
|
|
@@ -1118,7 +1122,6 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1118
1122
|
builderIndex: envelope.builderIndex,
|
|
1119
1123
|
blockHash: toRootHex(envelope.payload.blockHash),
|
|
1120
1124
|
blockRoot: blockRootHex,
|
|
1121
|
-
stateRoot: toRootHex(envelope.stateRoot),
|
|
1122
1125
|
});
|
|
1123
1126
|
|
|
1124
1127
|
chain.processExecutionPayload(payloadInput, {validSignature: true}).catch((e) => {
|
|
@@ -24,6 +24,10 @@ export async function* onBeaconBlocksByRange(
|
|
|
24
24
|
// in the case of initializing from a non-finalized state, we don't have the finalized block so this api does not work
|
|
25
25
|
// chain.forkChoice.getFinalizeBlock().slot
|
|
26
26
|
const finalizedSlot = chain.forkChoice.getFinalizedCheckpointSlot();
|
|
27
|
+
// Blocks are migrated to blockArchive at finalization (including the finalized block itself),
|
|
28
|
+
// so the archive loop serves up to AND INCLUDING finalizedSlot and the headChain loop
|
|
29
|
+
// starts above it to avoid duplicate yields. See archiveBlocks.ts for the migration logic.
|
|
30
|
+
const archiveMaxSlot = finalizedSlot;
|
|
27
31
|
|
|
28
32
|
const forkName = chain.config.getForkName(startSlot);
|
|
29
33
|
if (isForkPostFulu(forkName) && startSlot < chain.earliestAvailableSlot) {
|
|
@@ -35,9 +39,12 @@ export async function* onBeaconBlocksByRange(
|
|
|
35
39
|
}
|
|
36
40
|
|
|
37
41
|
// Finalized range of blocks
|
|
38
|
-
if (startSlot <=
|
|
42
|
+
if (startSlot <= archiveMaxSlot) {
|
|
39
43
|
// Chain of blobs won't change
|
|
40
|
-
for await (const {key, value} of finalized.binaryEntriesStream({
|
|
44
|
+
for await (const {key, value} of finalized.binaryEntriesStream({
|
|
45
|
+
gte: startSlot,
|
|
46
|
+
lt: Math.min(endSlot, archiveMaxSlot + 1),
|
|
47
|
+
})) {
|
|
41
48
|
yield {
|
|
42
49
|
data: value,
|
|
43
50
|
boundary: chain.config.getForkBoundaryAtEpoch(computeEpochAtSlot(finalized.decodeKey(key))),
|
|
@@ -46,19 +53,20 @@ export async function* onBeaconBlocksByRange(
|
|
|
46
53
|
}
|
|
47
54
|
|
|
48
55
|
// Non-finalized range of blocks
|
|
49
|
-
if (endSlot >
|
|
56
|
+
if (endSlot > archiveMaxSlot) {
|
|
50
57
|
const headBlock = chain.forkChoice.getHead();
|
|
51
58
|
const headRoot = headBlock.blockRoot;
|
|
52
59
|
// TODO DENEB: forkChoice should mantain an array of canonical blocks, and change only on reorg
|
|
53
60
|
const headChain = chain.forkChoice.getAllAncestorBlocks(headRoot, headBlock.payloadStatus);
|
|
54
|
-
// getAllAncestorBlocks
|
|
61
|
+
// `getAllAncestorBlocks` includes both the head and the previous-finalized boundary.
|
|
55
62
|
|
|
56
63
|
// Iterate head chain with ascending block numbers
|
|
57
64
|
for (let i = headChain.length - 1; i >= 0; i--) {
|
|
58
65
|
const block = headChain[i];
|
|
59
66
|
|
|
60
|
-
// Must include only blocks in the range requested
|
|
61
|
-
|
|
67
|
+
// Must include only blocks in the range requested, and skip anything the archive loop
|
|
68
|
+
// above already served via the block.slot > archiveMaxSlot filter.
|
|
69
|
+
if (block.slot > archiveMaxSlot && block.slot >= startSlot && block.slot < endSlot) {
|
|
62
70
|
// Note: Here the forkChoice head may change due to a re-org, so the headChain reflects the canonical chain
|
|
63
71
|
// at the time of the start of the request. Spec is clear the chain of blobs must be consistent, but on
|
|
64
72
|
// re-org there's no need to abort the request
|
|
@@ -20,31 +20,37 @@ export async function* onBlobSidecarsByRange(
|
|
|
20
20
|
const finalized = db.blobSidecarsArchive;
|
|
21
21
|
const unfinalized = db.blobSidecars;
|
|
22
22
|
const finalizedSlot = chain.forkChoice.getFinalizedBlock().slot;
|
|
23
|
+
// Blobs are migrated to blobSidecarsArchive at finalization (including the finalized block
|
|
24
|
+
// itself), so the archive loop serves up to AND INCLUDING finalizedSlot and the headChain
|
|
25
|
+
// loop starts above it to avoid duplicate yields. See archiveBlocks.ts for the migration logic.
|
|
26
|
+
const archiveMaxSlot = finalizedSlot;
|
|
23
27
|
|
|
24
28
|
// Finalized range of blobs
|
|
25
|
-
if (startSlot <=
|
|
29
|
+
if (startSlot <= archiveMaxSlot) {
|
|
26
30
|
// Chain of blobs won't change
|
|
27
31
|
for await (const {key, value: blobSideCarsBytesWrapped} of finalized.binaryEntriesStream({
|
|
28
32
|
gte: startSlot,
|
|
29
|
-
lt: endSlot,
|
|
33
|
+
lt: Math.min(endSlot, archiveMaxSlot + 1),
|
|
30
34
|
})) {
|
|
31
35
|
yield* iterateBlobBytesFromWrapper(chain, blobSideCarsBytesWrapped, finalized.decodeKey(key));
|
|
32
36
|
}
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
// Non-finalized range of blobs
|
|
36
|
-
if (endSlot >
|
|
40
|
+
if (endSlot > archiveMaxSlot) {
|
|
37
41
|
const headBlock = chain.forkChoice.getHead();
|
|
38
42
|
const headRoot = headBlock.blockRoot;
|
|
39
43
|
// TODO DENEB: forkChoice should mantain an array of canonical blocks, and change only on reorg
|
|
40
44
|
const headChain = chain.forkChoice.getAllAncestorBlocks(headRoot, headBlock.payloadStatus);
|
|
45
|
+
// `getAllAncestorBlocks` includes both the head and the previous-finalized boundary.
|
|
41
46
|
|
|
42
47
|
// Iterate head chain with ascending block numbers
|
|
43
48
|
for (let i = headChain.length - 1; i >= 0; i--) {
|
|
44
49
|
const block = headChain[i];
|
|
45
50
|
|
|
46
|
-
// Must include only blobs in the range requested
|
|
47
|
-
|
|
51
|
+
// Must include only blobs in the range requested, and skip anything the archive loop
|
|
52
|
+
// above already served via the block.slot > archiveMaxSlot filter.
|
|
53
|
+
if (block.slot > archiveMaxSlot && block.slot >= startSlot && block.slot < endSlot) {
|
|
48
54
|
// Note: Here the forkChoice head may change due to a re-org, so the headChain reflects the canonical chain
|
|
49
55
|
// at the time of the start of the request. Spec is clear the chain of blobs must be consistent, but on
|
|
50
56
|
// re-org there's no need to abort the request
|