@lodestar/beacon-node 1.43.0-dev.7622a1076c → 1.43.0-dev.78c66bac71
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/writePayloadEnvelopeInputToDb.d.ts.map +1 -1
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js +1 -10
- 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/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 -13
- 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/package.json +15 -15
- package/src/chain/blocks/writePayloadEnvelopeInputToDb.ts +8 -17
- package/src/chain/chain.ts +12 -0
- package/src/chain/interface.ts +2 -0
- package/src/chain/prepareNextSlot.ts +42 -12
- package/src/chain/produceBlock/produceBlockBody.ts +30 -11
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +22 -20
|
@@ -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,7 +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 = [];
|
|
285
|
-
|
|
290
|
+
gloasBody.parentExecutionRequests = parentExecutionRequests;
|
|
286
291
|
blockBody = gloasBody as AssembledBodyType<T>;
|
|
287
292
|
|
|
288
293
|
// Store execution payload data required to construct execution payload envelope later
|
|
@@ -619,7 +624,8 @@ export async function prepareExecutionPayload(
|
|
|
619
624
|
safeBlockHash: RootHex,
|
|
620
625
|
finalizedBlockHash: RootHex,
|
|
621
626
|
state: IBeaconStateViewBellatrix,
|
|
622
|
-
suggestedFeeRecipient: string
|
|
627
|
+
suggestedFeeRecipient: string,
|
|
628
|
+
parentExecutionRequests?: electra.ExecutionRequests
|
|
623
629
|
): Promise<{prepType: PayloadPreparationType; payloadId: PayloadId}> {
|
|
624
630
|
const timestamp = computeTimeAtSlot(chain.config, state.slot, state.genesisTime);
|
|
625
631
|
const prevRandao = state.getRandaoMix(state.epoch);
|
|
@@ -656,6 +662,7 @@ export async function prepareExecutionPayload(
|
|
|
656
662
|
parentBlockRoot,
|
|
657
663
|
parentBlockHash,
|
|
658
664
|
feeRecipient: suggestedFeeRecipient,
|
|
665
|
+
parentExecutionRequests,
|
|
659
666
|
});
|
|
660
667
|
|
|
661
668
|
payloadId = await chain.executionEngine.notifyForkchoiceUpdate(
|
|
@@ -714,12 +721,14 @@ export function getPayloadAttributesForSSE(
|
|
|
714
721
|
parentBlockRoot,
|
|
715
722
|
parentBlockHash,
|
|
716
723
|
feeRecipient,
|
|
724
|
+
parentExecutionRequests,
|
|
717
725
|
}: {
|
|
718
726
|
prepareState: IBeaconStateViewBellatrix;
|
|
719
727
|
prepareSlot: Slot;
|
|
720
728
|
parentBlockRoot: Root;
|
|
721
729
|
parentBlockHash: Bytes32;
|
|
722
730
|
feeRecipient: string;
|
|
731
|
+
parentExecutionRequests?: electra.ExecutionRequests;
|
|
723
732
|
}
|
|
724
733
|
): SSEPayloadAttributes {
|
|
725
734
|
const payloadAttributes = preparePayloadAttributes(fork, chain, {
|
|
@@ -728,6 +737,7 @@ export function getPayloadAttributesForSSE(
|
|
|
728
737
|
parentBlockRoot,
|
|
729
738
|
parentBlockHash,
|
|
730
739
|
feeRecipient,
|
|
740
|
+
parentExecutionRequests,
|
|
731
741
|
});
|
|
732
742
|
|
|
733
743
|
let parentBlockNumber: number;
|
|
@@ -766,12 +776,14 @@ function preparePayloadAttributes(
|
|
|
766
776
|
parentBlockRoot,
|
|
767
777
|
parentBlockHash,
|
|
768
778
|
feeRecipient,
|
|
779
|
+
parentExecutionRequests,
|
|
769
780
|
}: {
|
|
770
781
|
prepareState: IBeaconStateViewBellatrix;
|
|
771
782
|
prepareSlot: Slot;
|
|
772
783
|
parentBlockRoot: Root;
|
|
773
784
|
parentBlockHash: Bytes32;
|
|
774
785
|
feeRecipient: string;
|
|
786
|
+
parentExecutionRequests?: electra.ExecutionRequests;
|
|
775
787
|
}
|
|
776
788
|
): SSEPayloadAttributes["payloadAttributes"] {
|
|
777
789
|
const timestamp = computeTimeAtSlot(chain.config, prepareSlot, prepareState.genesisTime);
|
|
@@ -789,13 +801,20 @@ function preparePayloadAttributes(
|
|
|
789
801
|
|
|
790
802
|
if (isStatePostGloas(prepareState)) {
|
|
791
803
|
const isExtendingPayload = byteArrayEquals(parentBlockHash, prepareState.latestExecutionPayloadBid.blockHash);
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
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
|
+
}
|
|
799
818
|
} else {
|
|
800
819
|
// withdrawals logic is now fork aware as it changes on electra fork post capella
|
|
801
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);
|