UNPKG

@lodestar/beacon-node

Version:

A Typescript implementation of the beacon chain

128 lines 5.47 kB
import { computeStartSlotAtEpoch } from "@lodestar/state-transition"; import { isDaOutOfRange } from "../blocks/blockInput/index.js"; import { PayloadEnvelopeInput } from "../blocks/payloadEnvelopeInput/index.js"; import { ChainEvent } from "../emitter.js"; export { PayloadEnvelopeInput } from "../blocks/payloadEnvelopeInput/index.js"; /** * Cache for tracking PayloadEnvelopeInput instances, keyed by beacon block root. * * Created whenever we have a block because it needs block bid. * 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 { config; clock; forkChoice; chainEvents; signal; serializedCache; metrics; logger; payloadInputs = new Map(); constructor({ config, clock, forkChoice, chainEvents, signal, serializedCache, metrics, logger, }) { this.config = config; this.clock = clock; this.forkChoice = forkChoice; this.chainEvents = chainEvents; this.signal = signal; this.serializedCache = serializedCache; this.metrics = metrics; this.logger = logger; if (metrics) { metrics.seenCache.payloadEnvelopeInput.count.addCollect(() => { metrics.seenCache.payloadEnvelopeInput.count.set(this.payloadInputs.size); metrics.seenCache.payloadEnvelopeInput.serializedObjectRefs.set(Array.from(this.payloadInputs.values()).reduce((count, payloadInput) => count + payloadInput.getSerializedCacheKeys().length, 0)); }); } this.chainEvents.on(ChainEvent.forkChoiceFinalized, this.pruneFinalized); this.signal.addEventListener("abort", () => { this.chainEvents.off(ChainEvent.forkChoiceFinalized, this.pruneFinalized); }); } pruneFinalized = (checkpoint) => { 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.pruneFinalized deleted entries", { finalizedSlot, finalizedRoot: checkpoint.rootHex, deletedCount, }); }; add(props) { const existing = this.payloadInputs.get(props.blockRootHex); if (existing !== undefined) { this.logger?.verbose("SeenPayloadEnvelopeInput.add reused existing entry", { slot: existing.slot, root: props.blockRootHex, }); return existing; } const daOutOfRange = isDaOutOfRange(this.config, props.forkName, props.block.message.slot, this.clock.currentEpoch); const input = PayloadEnvelopeInput.createFromBlock({ ...props, daOutOfRange }); this.payloadInputs.set(props.blockRootHex, input); this.metrics?.seenCache.payloadEnvelopeInput.created.inc(); this.logger?.verbose("SeenPayloadEnvelopeInput.add created new entry", { slot: input.slot, root: props.blockRootHex, daOutOfRange, }); return input; } /** * Used at chain initialization to seed the anchor block's PayloadEnvelopeInput from * `state.latestExecutionPayloadBid`. */ addFromBid(props) { const existing = this.payloadInputs.get(props.blockRootHex); if (existing !== undefined) { return existing; } const daOutOfRange = isDaOutOfRange(this.config, props.forkName, props.slot, this.clock.currentEpoch); const input = PayloadEnvelopeInput.createFromBid({ ...props, daOutOfRange }); this.payloadInputs.set(props.blockRootHex, input); this.metrics?.seenCache.payloadEnvelopeInput.created.inc(); this.logger?.verbose("SeenPayloadEnvelopeInput.addFromBid created new entry", { slot: input.slot, root: props.blockRootHex, daOutOfRange, }); return input; } get(blockRootHex) { return this.payloadInputs.get(blockRootHex); } hasPayload(blockRootHex) { return this.payloadInputs.get(blockRootHex)?.hasPayloadEnvelope() ?? false; } size() { return this.payloadInputs.size; } pruneBelowParent(parentBlock) { for (const block of this.forkChoice.getAllAncestorBlocks(parentBlock.blockRoot, parentBlock.payloadStatus)) { if (block.slot < parentBlock.slot) { const input = this.payloadInputs.get(block.blockRoot); if (input) { this.evictPayloadInput(input); this.logger?.verbose("SeenPayloadEnvelopeInput.pruneBelowParent deleted", { slot: block.slot, root: block.blockRoot, }); } } } } evictPayloadInput(payloadInput) { this.serializedCache.delete(payloadInput.getSerializedCacheKeys()); this.payloadInputs.delete(payloadInput.blockRootHex); } } //# sourceMappingURL=seenPayloadEnvelopeInput.js.map