@lodestar/beacon-node 1.43.0-dev.ca1fc40294 → 1.43.0-dev.d166e3b6f7
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/chain/blocks/importExecutionPayload.d.ts +14 -13
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +59 -74
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +0 -1
- package/lib/chain/blocks/index.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/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 +2 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +7 -0
- package/lib/chain/chain.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.js +2 -2
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/interface.d.ts +2 -1
- 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 +27 -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/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 +11 -1
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/package.json +16 -15
- package/src/chain/blocks/importExecutionPayload.ts +73 -93
- package/src/chain/blocks/index.ts +0 -1
- package/src/chain/blocks/types.ts +14 -25
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +129 -0
- package/src/chain/blocks/writePayloadEnvelopeInputToDb.ts +9 -18
- package/src/chain/chain.ts +12 -0
- package/src/chain/errors/executionPayloadBid.ts +6 -0
- package/src/chain/errors/executionPayloadEnvelope.ts +6 -0
- package/src/chain/forkChoice/index.ts +2 -2
- package/src/chain/interface.ts +2 -0
- package/src/chain/prepareNextSlot.ts +42 -12
- package/src/chain/produceBlock/produceBlockBody.ts +30 -10
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +22 -20
- package/src/chain/validation/executionPayloadBid.ts +14 -0
- package/src/chain/validation/executionPayloadEnvelope.ts +12 -2
package/src/chain/interface.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
altair,
|
|
19
19
|
capella,
|
|
20
20
|
deneb,
|
|
21
|
+
electra,
|
|
21
22
|
gloas,
|
|
22
23
|
phase0,
|
|
23
24
|
rewards,
|
|
@@ -231,6 +232,7 @@ export interface IBeaconChain {
|
|
|
231
232
|
blockSlot: Slot,
|
|
232
233
|
blockRootHex: string
|
|
233
234
|
): Promise<gloas.SignedExecutionPayloadEnvelope | null>;
|
|
235
|
+
getParentExecutionRequests(parentBlockSlot: Slot, parentBlockRootHex: RootHex): Promise<electra.ExecutionRequests>;
|
|
234
236
|
|
|
235
237
|
produceCommonBlockBody(blockAttributes: BlockAttributes): Promise<CommonBlockBody>;
|
|
236
238
|
produceBlock(blockAttributes: BlockAttributes & {commonBlockBodyPromise: Promise<CommonBlockBody>}): Promise<{
|
|
@@ -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
|
}
|
|
@@ -214,9 +214,13 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
214
214
|
});
|
|
215
215
|
|
|
216
216
|
// Get execution payload from EL
|
|
217
|
-
const
|
|
217
|
+
const isExtendingPayload = this.forkChoice.shouldExtendPayload(toRootHex(parentBlockRoot));
|
|
218
|
+
const parentBlockHash = isExtendingPayload
|
|
218
219
|
? currentState.latestExecutionPayloadBid.blockHash
|
|
219
220
|
: currentState.latestExecutionPayloadBid.parentBlockHash;
|
|
221
|
+
const parentExecutionRequests = isExtendingPayload
|
|
222
|
+
? await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot)
|
|
223
|
+
: ssz.electra.ExecutionRequests.defaultValue();
|
|
220
224
|
const prepareRes = await prepareExecutionPayload(
|
|
221
225
|
this,
|
|
222
226
|
this.logger,
|
|
@@ -226,7 +230,8 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
226
230
|
safeBlockHash,
|
|
227
231
|
finalizedBlockHash ?? ZERO_HASH_HEX,
|
|
228
232
|
currentState,
|
|
229
|
-
feeRecipient
|
|
233
|
+
feeRecipient,
|
|
234
|
+
parentExecutionRequests
|
|
230
235
|
);
|
|
231
236
|
|
|
232
237
|
const {prepType, payloadId} = prepareRes;
|
|
@@ -282,6 +287,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
282
287
|
gloasBody.signedExecutionPayloadBid = signedBid;
|
|
283
288
|
// TODO GLOAS: Get payload attestations from pool for previous slot
|
|
284
289
|
gloasBody.payloadAttestations = [];
|
|
290
|
+
gloasBody.parentExecutionRequests = parentExecutionRequests;
|
|
285
291
|
blockBody = gloasBody as AssembledBodyType<T>;
|
|
286
292
|
|
|
287
293
|
// Store execution payload data required to construct execution payload envelope later
|
|
@@ -618,7 +624,8 @@ export async function prepareExecutionPayload(
|
|
|
618
624
|
safeBlockHash: RootHex,
|
|
619
625
|
finalizedBlockHash: RootHex,
|
|
620
626
|
state: IBeaconStateViewBellatrix,
|
|
621
|
-
suggestedFeeRecipient: string
|
|
627
|
+
suggestedFeeRecipient: string,
|
|
628
|
+
parentExecutionRequests?: electra.ExecutionRequests
|
|
622
629
|
): Promise<{prepType: PayloadPreparationType; payloadId: PayloadId}> {
|
|
623
630
|
const timestamp = computeTimeAtSlot(chain.config, state.slot, state.genesisTime);
|
|
624
631
|
const prevRandao = state.getRandaoMix(state.epoch);
|
|
@@ -655,6 +662,7 @@ export async function prepareExecutionPayload(
|
|
|
655
662
|
parentBlockRoot,
|
|
656
663
|
parentBlockHash,
|
|
657
664
|
feeRecipient: suggestedFeeRecipient,
|
|
665
|
+
parentExecutionRequests,
|
|
658
666
|
});
|
|
659
667
|
|
|
660
668
|
payloadId = await chain.executionEngine.notifyForkchoiceUpdate(
|
|
@@ -713,12 +721,14 @@ export function getPayloadAttributesForSSE(
|
|
|
713
721
|
parentBlockRoot,
|
|
714
722
|
parentBlockHash,
|
|
715
723
|
feeRecipient,
|
|
724
|
+
parentExecutionRequests,
|
|
716
725
|
}: {
|
|
717
726
|
prepareState: IBeaconStateViewBellatrix;
|
|
718
727
|
prepareSlot: Slot;
|
|
719
728
|
parentBlockRoot: Root;
|
|
720
729
|
parentBlockHash: Bytes32;
|
|
721
730
|
feeRecipient: string;
|
|
731
|
+
parentExecutionRequests?: electra.ExecutionRequests;
|
|
722
732
|
}
|
|
723
733
|
): SSEPayloadAttributes {
|
|
724
734
|
const payloadAttributes = preparePayloadAttributes(fork, chain, {
|
|
@@ -727,6 +737,7 @@ export function getPayloadAttributesForSSE(
|
|
|
727
737
|
parentBlockRoot,
|
|
728
738
|
parentBlockHash,
|
|
729
739
|
feeRecipient,
|
|
740
|
+
parentExecutionRequests,
|
|
730
741
|
});
|
|
731
742
|
|
|
732
743
|
let parentBlockNumber: number;
|
|
@@ -765,12 +776,14 @@ function preparePayloadAttributes(
|
|
|
765
776
|
parentBlockRoot,
|
|
766
777
|
parentBlockHash,
|
|
767
778
|
feeRecipient,
|
|
779
|
+
parentExecutionRequests,
|
|
768
780
|
}: {
|
|
769
781
|
prepareState: IBeaconStateViewBellatrix;
|
|
770
782
|
prepareSlot: Slot;
|
|
771
783
|
parentBlockRoot: Root;
|
|
772
784
|
parentBlockHash: Bytes32;
|
|
773
785
|
feeRecipient: string;
|
|
786
|
+
parentExecutionRequests?: electra.ExecutionRequests;
|
|
774
787
|
}
|
|
775
788
|
): SSEPayloadAttributes["payloadAttributes"] {
|
|
776
789
|
const timestamp = computeTimeAtSlot(chain.config, prepareSlot, prepareState.genesisTime);
|
|
@@ -788,13 +801,20 @@ function preparePayloadAttributes(
|
|
|
788
801
|
|
|
789
802
|
if (isStatePostGloas(prepareState)) {
|
|
790
803
|
const isExtendingPayload = byteArrayEquals(parentBlockHash, prepareState.latestExecutionPayloadBid.blockHash);
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
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
|
+
}
|
|
798
818
|
} else {
|
|
799
819
|
// withdrawals logic is now fork aware as it changes on electra fork post capella
|
|
800
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);
|
|
@@ -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";
|
|
@@ -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)
|