Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
a791964
feat: implement EIP-7843 slot_number in ExecutionPayload (specs#4840)
ensi321 Apr 20, 2026
3958681
Revert changes introduced by accident
ensi321 Apr 21, 2026
a912bc8
Bump version, skip fast_confirmation
ensi321 Apr 20, 2026
03c4349
Rest of the changes
ensi321 Apr 21, 2026
d3d74a9
Skip some tests
ensi321 Apr 21, 2026
40b5f9f
lint
ensi321 Apr 21, 2026
da7cc41
ethspecify
ensi321 Apr 21, 2026
e49b699
lint
ensi321 Apr 21, 2026
813cb52
ethspecify
ensi321 Apr 21, 2026
845bec9
fix unit test
ensi321 Apr 22, 2026
1d15ece
fix e2e
ensi321 Apr 22, 2026
c3d429d
address comment
ensi321 Apr 22, 2026
afbb9c1
feat: add processParentExecutionPayload and remove withdrawal early r…
ensi321 Apr 15, 2026
311eeca
refactor: add processParentExecutionPayload as first step in processB…
ensi321 Apr 15, 2026
e2d3ad2
refactor: transform processExecutionPayloadEnvelope to pure verification
ensi321 Apr 15, 2026
c3c73b4
refactor: remove executionPayloadStateRoot from fork choice onExecuti…
ensi321 Apr 15, 2026
bc621c3
refactor: simplify envelope import pipeline for deferred processing
ensi321 Apr 15, 2026
f4d4021
feat: block production, gossip validation, and cleanup for deferred p…
ensi321 Apr 15, 2026
63e5271
fix: type errors in processParentExecutionPayload and produceBlockBody
ensi321 Apr 15, 2026
06ab9c1
Fix spec test
ensi321 Apr 16, 2026
6552bc8
Address comments & follow up on spec change
ensi321 Apr 16, 2026
e10d287
fix upgrade state
ensi321 Apr 17, 2026
5421487
fix spec test
ensi321 Apr 20, 2026
b5937ec
Harmonize
ensi321 Apr 22, 2026
a0bbecf
Merge remote-tracking branch 'origin/unstable' into nc/defer-payload-…
nflaig Apr 22, 2026
941a710
review
nflaig Apr 22, 2026
532ffcc
ethspecify
nflaig Apr 22, 2026
16941ff
restore some comments
nflaig Apr 22, 2026
6b08376
Remove block production
ensi321 Apr 22, 2026
8967ae5
refactor
nflaig Apr 23, 2026
5b1e344
simplify applyParentExecutionPayload function signature
nflaig Apr 23, 2026
9a9fe6b
remove genesis block handling, will be separate pr
nflaig Apr 23, 2026
b07b363
Merge branch 'unstable' into nc/defer-payload-processing
nflaig Apr 23, 2026
bf7009e
we don't know and it's wrong, removed confusing todo
nflaig Apr 23, 2026
d2424d5
review computeAnchorCheckpoint
nflaig Apr 23, 2026
9bf5103
review fork_choice.test.ts
nflaig Apr 23, 2026
08b2712
revert changes to computeAnchorCheckpoint
nflaig Apr 23, 2026
e62a7a1
ahhh damn, we need that for spec tests
nflaig Apr 23, 2026
8acf7c8
remove type cast
nflaig Apr 23, 2026
24a3da5
clarify withdrawals
nflaig Apr 23, 2026
a04a4d0
review getExpectedWithdrawalsForFullParent
nflaig Apr 23, 2026
2fa1c4f
review processParentExecutionPayload.ts
nflaig Apr 23, 2026
b89af0b
review operations.test.ts
nflaig Apr 23, 2026
c2ef8aa
final pass on processParentExecutionPayload
nflaig Apr 23, 2026
78a7201
small nit
nflaig Apr 23, 2026
f8c73af
fix order of gossip checks
nflaig Apr 23, 2026
e399921
clarify comment
nflaig Apr 23, 2026
56f686c
review import
nflaig Apr 23, 2026
7728204
Update packages/beacon-node/src/chain/blocks/types.ts
nflaig Apr 23, 2026
11f9d16
Update packages/beacon-node/test/spec/presets/fork_choice.test.ts
nflaig Apr 23, 2026
2054188
Update packages/state-transition/src/block/index.ts
nflaig Apr 23, 2026
2a7d8e6
Update packages/state-transition/src/block/processWithdrawals.ts
nflaig Apr 23, 2026
372f485
.
nflaig Apr 23, 2026
6218a8e
wording
nflaig Apr 23, 2026
e450f52
restore some comments
nflaig Apr 23, 2026
232e294
ok that's it
nflaig Apr 23, 2026
e7759b0
not yet
nflaig Apr 23, 2026
c06c946
why is that comment even modified...
nflaig Apr 23, 2026
5ae6863
feat: cache the last 2 PayloadEnvelopeInputs
twoeths Apr 21, 2026
783d8d3
refactor: rename finalHead -> updatedHead
twoeths Apr 23, 2026
89a60b7
chore: revert executionPayloadEnvelope validation
twoeths Apr 23, 2026
ba90d3d
Merge remote-tracking branch 'origin/unstable' into te/gloas_cache_la…
twoeths Apr 23, 2026
d7bdb55
chore: address comments
twoeths Apr 23, 2026
4370934
remove redundant fc call
nflaig Apr 23, 2026
a8b0d5e
Merge branch 'unstable' into te/gloas_cache_last_2_payloads
nflaig Apr 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,14 @@ export async function persistPayloadEnvelopeInput(
this: BeaconChain,
payloadInput: PayloadEnvelopeInput
): Promise<void> {
await writePayloadEnvelopeInputToDb
.call(this, payloadInput)
.catch((e) => {
this.logger.error(
"Error persisting payload envelope in hot db",
{
slot: payloadInput.slot,
root: payloadInput.blockRootHex,
},
e
);
})
.finally(() => {
this.seenPayloadEnvelopeInputCache.prune(payloadInput.blockRootHex);
this.logger.debug("Pruned payload envelope input", {
await writePayloadEnvelopeInputToDb.call(this, payloadInput).catch((e) => {
this.logger.error(
"Error persisting payload envelope in hot db",
{
slot: payloadInput.slot,
root: payloadInput.blockRootHex,
});
});
},
e
);
});
}
18 changes: 14 additions & 4 deletions packages/beacon-node/src/chain/prepareNextSlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export class PrepareNextSlotScheduler {
const headBlock = this.chain.recomputeForkChoiceHead(ForkchoiceCaller.prepareNextSlot);
const {slot: headSlot, blockRoot: headRoot} = headBlock;
// may be updated below if we predict a proposer-boost-reorg
let updatedHeadRoot = headRoot;
let updatedHead = headBlock;

// PS: previously this was comparing slots, but that gave no leway on the skipped
// slots on epoch bounday. Making it more fluid.
Expand Down Expand Up @@ -148,7 +148,7 @@ export class PrepareNextSlotScheduler {
{dontTransferCache: !isEpochTransition},
RegenCaller.predictProposerHead
);
updatedHeadRoot = proposerHeadRoot;
updatedHead = proposerHead;
}

// Update the builder status, if enabled shoot an api call to check status
Expand All @@ -166,7 +166,7 @@ export class PrepareNextSlotScheduler {

let parentBlockHash: Bytes32;
if (isStatePostGloas(updatedPrepareState)) {
parentBlockHash = this.chain.forkChoice.shouldExtendPayload(updatedHeadRoot)
parentBlockHash = this.chain.forkChoice.shouldExtendPayload(updatedHead.blockRoot)
? updatedPrepareState.latestExecutionPayloadBid.blockHash
: updatedPrepareState.latestExecutionPayloadBid.parentBlockHash;
} else {
Expand All @@ -189,7 +189,7 @@ export class PrepareNextSlotScheduler {
this.chain,
this.logger,
fork as ForkPostBellatrix, // State is of execution type
fromHex(updatedHeadRoot),
fromHex(updatedHead.blockRoot),
parentBlockHash,
safeBlockHash,
finalizedBlockHash,
Expand All @@ -203,6 +203,16 @@ export class PrepareNextSlotScheduler {
});
}

if (ForkSeq[fork] >= ForkSeq.gloas) {
// Cutoff = slot of the parent of the block we'll actually build on (post-reorg).
// Steady state: cache holds just 2 entries — head (parent for next-slot production)
// and head.parent (proposer-boost-reorg fallback). Anything older is evicted.
const updatedHeadParent = this.chain.forkChoice.getBlockHexDefaultStatus(updatedHead.parentRoot);
if (updatedHeadParent) {
this.chain.seenPayloadEnvelopeInputCache.pruneBelow(updatedHeadParent.slot);
}
}
Comment thread
twoeths marked this conversation as resolved.

this.computeStateHashTreeRoot(updatedPrepareState, isEpochTransition);

// If emitPayloadAttributes is true emit a SSE payloadAttributes event
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {CheckpointWithHex} from "@lodestar/fork-choice";
import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
import {RootHex} from "@lodestar/types";
import {RootHex, Slot} from "@lodestar/types";
import {Logger} from "@lodestar/utils";
import {Metrics} from "../../metrics/metrics.js";
import {SerializedCache} from "../../util/serializedCache.js";
Expand All @@ -21,8 +21,15 @@ export type SeenPayloadEnvelopeInputModules = {
/**
* Cache for tracking PayloadEnvelopeInput instances, keyed by beacon block root.
*
* Created during block import when a block is processed.
* Pruned on finalization and after payload is written to DB.
* Created during block import when a block is processed. Two pruning paths:
* - `prepareNextSlot` calls `pruneBelow(headParentSlot)` every slot once the head we'll build
* on is known.
* - `onFinalized` calls `pruneBelow(finalizedSlot)` on every finalization for bulk cleanup.
*
* Steady state (linear chain, healthy progression): the cache holds ~2 entries — the head
* (parent for next-slot production) and its parent (proposer-boost-reorg fallback). It can
* transiently hold more during forks, range-sync bursts, or when `prepareNextSlot` skips
* ticks; subsequent ticks settle it back.
*/
export class SeenPayloadEnvelopeInput {
private readonly chainEvents: ChainEventEmitter;
Expand Down Expand Up @@ -58,16 +65,7 @@ export class SeenPayloadEnvelopeInput {
}

private onFinalized = (checkpoint: CheckpointWithHex): void => {
// Prune all entries with slot < finalized slot
const finalizedSlot = computeStartSlotAtEpoch(checkpoint.epoch);
let deletedCount = 0;
for (const [, input] of this.payloadInputs) {
if (input.slot < finalizedSlot) {
this.evictPayloadInput(input);
deletedCount++;
}
}
this.logger?.debug("SeenPayloadEnvelopeInput.onFinalized deleted cached entries", {deletedCount});
this.pruneBelow(computeStartSlotAtEpoch(checkpoint.epoch));
};

add(props: CreateFromBlockProps): PayloadEnvelopeInput {
Expand All @@ -88,17 +86,21 @@ export class SeenPayloadEnvelopeInput {
return this.payloadInputs.get(blockRootHex)?.hasPayloadEnvelope() ?? false;
}

prune(blockRootHex: RootHex): void {
const payloadInput = this.payloadInputs.get(blockRootHex);
if (payloadInput) {
this.evictPayloadInput(payloadInput);
}
}

size(): number {
return this.payloadInputs.size;
}

pruneBelow(slot: Slot): void {
let deletedCount = 0;
for (const [, input] of this.payloadInputs) {
if (input.slot < slot) {
this.evictPayloadInput(input);
deletedCount++;
}
}
this.logger?.debug("SeenPayloadEnvelopeInput.pruneBelow deleted entries", {slot, deletedCount});
}

private evictPayloadInput(payloadInput: PayloadEnvelopeInput): void {
this.serializedCache.delete(payloadInput.getSerializedCacheKeys());
this.payloadInputs.delete(payloadInput.blockRootHex);
Expand Down
Loading