-
-
Notifications
You must be signed in to change notification settings - Fork 300
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: n historical states #6008
Changes from 39 commits
8c25f9f
763ebde
e3d1bee
6df1b5a
e7bcece
a39cc5f
7214e9d
2b9c491
253811d
2390594
a48f8f6
c5dffed
c4090eb
87cca20
4302ecd
6c1c720
7d5e4f6
de65f2c
aaaa88a
801d521
b206639
ee2e55a
6fdae10
6beaf19
fe0883b
843b824
166ee37
96dba21
f116b93
f624126
0b15ae2
ea56579
dad4483
64e8a4c
63b156c
99d6af5
4669871
d4443ab
0da184d
76d6f99
22cf38c
6e16d94
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -5,6 +5,7 @@ import {computeEpochAtSlot, computeStartSlotAtEpoch} from "@lodestar/state-trans | |||||
import {CheckpointWithHex} from "@lodestar/fork-choice"; | ||||||
import {IBeaconDb} from "../../db/index.js"; | ||||||
import {IStateRegenerator} from "../regen/interface.js"; | ||||||
import {getStateSlotFromBytes} from "../../util/multifork.js"; | ||||||
|
||||||
/** | ||||||
* Minimum number of epochs between single temp archived states | ||||||
|
@@ -83,13 +84,22 @@ export class StatesArchiver { | |||||
* Only the new finalized state is stored to disk | ||||||
*/ | ||||||
async archiveState(finalized: CheckpointWithHex): Promise<void> { | ||||||
nflaig marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
const finalizedState = this.regen.getCheckpointStateSync(finalized); | ||||||
if (!finalizedState) { | ||||||
throw Error("No state in cache for finalized checkpoint state epoch #" + finalized.epoch); | ||||||
// the finalized state could be from to disk | ||||||
const finalizedStateOrBytes = await this.regen.getCheckpointStateOrBytes(finalized); | ||||||
const {rootHex} = finalized; | ||||||
if (!finalizedStateOrBytes) { | ||||||
throw Error(`No state in cache for finalized checkpoint state epoch #${finalized.epoch} root ${rootHex}`); | ||||||
} | ||||||
if (finalizedStateOrBytes instanceof Uint8Array) { | ||||||
const slot = getStateSlotFromBytes(finalizedStateOrBytes); | ||||||
await this.db.stateArchive.putBinary(slot, finalizedStateOrBytes); | ||||||
this.logger.verbose("Archived finalized state bytes", {finalizedEpoch: finalized.epoch, slot, root: rootHex}); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Log context is different from the one below
Suggested change
|
||||||
} else { | ||||||
// state | ||||||
await this.db.stateArchive.put(finalizedStateOrBytes.slot, finalizedStateOrBytes); | ||||||
this.logger.verbose("Archived finalized state", {epoch: finalized.epoch, root: rootHex}); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could consider logging slot here (same as above) |
||||||
} | ||||||
await this.db.stateArchive.put(finalizedState.slot, finalizedState); | ||||||
// don't delete states before the finalized state, auto-prune will take care of it | ||||||
this.logger.verbose("Archived finalized state", {finalizedEpoch: finalized.epoch}); | ||||||
} | ||||||
} | ||||||
|
||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,7 @@ import {ZERO_HASH_HEX} from "../../constants/index.js"; | |
import {toCheckpointHex} from "../stateCache/index.js"; | ||
import {isOptimisticBlock} from "../../util/forkChoice.js"; | ||
import {isQueueErrorAborted} from "../../util/queue/index.js"; | ||
import {ChainEvent, ReorgEventData} from "../emitter.js"; | ||
import {ReorgEventData} from "../emitter.js"; | ||
import {REPROCESS_MIN_TIME_TO_NEXT_SLOT_SEC} from "../reprocess.js"; | ||
import type {BeaconChain} from "../chain.js"; | ||
import {FullyVerifiedBlock, ImportBlockOpts, AttestationImportOpt} from "./types.js"; | ||
|
@@ -62,6 +62,7 @@ export async function importBlock( | |
const blockRootHex = toHexString(blockRoot); | ||
const currentEpoch = computeEpochAtSlot(this.forkChoice.getTime()); | ||
const blockEpoch = computeEpochAtSlot(block.message.slot); | ||
const parentEpoch = computeEpochAtSlot(parentBlockSlot); | ||
const prevFinalizedEpoch = this.forkChoice.getFinalizedCheckpoint().epoch; | ||
const blockDelaySec = (fullyVerifiedBlock.seenTimestampSec - postState.genesisTime) % this.config.SECONDS_PER_SLOT; | ||
|
||
|
@@ -202,16 +203,15 @@ export async function importBlock( | |
} | ||
} | ||
|
||
// 5. Compute head. If new head, immediately stateCache.setHeadState() | ||
// 5. Compute head, always add to state cache so that it'll not be pruned soon | ||
|
||
const oldHead = this.forkChoice.getHead(); | ||
const newHead = this.recomputeForkChoiceHead(); | ||
const currFinalizedEpoch = this.forkChoice.getFinalizedCheckpoint().epoch; | ||
|
||
// always set head state so it'll never be pruned from state cache | ||
this.regen.updateHeadState(newHead.stateRoot, postState); | ||
if (newHead.blockRoot !== oldHead.blockRoot) { | ||
// Set head state as strong reference | ||
this.regen.updateHeadState(newHead.stateRoot, postState); | ||
|
||
this.emitter.emit(routes.events.EventType.head, { | ||
block: newHead.blockRoot, | ||
epochTransition: computeStartSlotAtEpoch(computeEpochAtSlot(newHead.slot)) === newHead.slot, | ||
|
@@ -331,12 +331,20 @@ export async function importBlock( | |
this.logger.verbose("After importBlock caching postState without SSZ cache", {slot: postState.slot}); | ||
} | ||
|
||
if (block.message.slot % SLOTS_PER_EPOCH === 0) { | ||
// Cache state to preserve epoch transition work | ||
if (parentEpoch < blockEpoch) { | ||
// current epoch and previous epoch are likely cached in previous states | ||
this.shufflingCache.processState(postState, postState.epochCtx.nextShuffling.epoch); | ||
this.logger.verbose("Processed shuffling for next epoch", {parentEpoch, blockEpoch, slot: block.message.slot}); | ||
|
||
// This is the real check point state per spec because the root is in current epoch | ||
// it's important to add this to cache, when chain is finalized we'll query this state later | ||
const checkpointState = postState; | ||
const cp = getCheckpointFromState(checkpointState); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. put this inside |
||
this.regen.addCheckpointState(cp, checkpointState); | ||
this.emitter.emit(ChainEvent.checkpoint, cp, checkpointState); | ||
// add Current Root Checkpoint State to the checkpoint state cache | ||
// this could be the justified/finalized checkpoint state later according to https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.2/specs/phase0/beacon-chain.md | ||
if (block.message.slot % SLOTS_PER_EPOCH === 0) { | ||
twoeths marked this conversation as resolved.
Show resolved
Hide resolved
|
||
this.regen.addCheckpointState(cp, checkpointState); | ||
} | ||
|
||
// Note: in-lined code from previos handler of ChainEvent.checkpoint | ||
this.logger.verbose("Checkpoint processed", toCheckpointHex(cp)); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a jsdoc comment?