load altair state from database on startup (#2994)
* fixes replay from last phase0 block on startup * better forked block loading
This commit is contained in:
parent
4f7a8cf79d
commit
5b33783898
|
@ -313,10 +313,10 @@ func contains*(dag: ChainDAGRef, root: Eth2Digest): bool =
|
|||
|
||||
proc containsBlock(
|
||||
cfg: RuntimeConfig, db: BeaconChainDB, blck: BlockRef): bool =
|
||||
case cfg.stateForkAtEpoch(blck.slot.epoch)
|
||||
of forkMerge: db.containsBlockMerge(blck.root)
|
||||
of forkAltair: db.containsBlockAltair(blck.root)
|
||||
of forkPhase0: db.containsBlockPhase0(blck.root)
|
||||
case cfg.blockForkAtEpoch(blck.slot.epoch)
|
||||
of BeaconBlockFork.Phase0: db.containsBlockPhase0(blck.root)
|
||||
of BeaconBlockFork.Altair: db.containsBlockAltair(blck.root)
|
||||
of BeaconBlockFork.Merge: db.containsBlockMerge(blck.root)
|
||||
|
||||
func isStateCheckpoint(bs: BlockSlot): bool =
|
||||
## State checkpoints are the points in time for which we store full state
|
||||
|
@ -333,6 +333,41 @@ func isStateCheckpoint(bs: BlockSlot): bool =
|
|||
(bs.slot == bs.blck.slot and bs.blck.parent == nil) or
|
||||
(bs.slot.isEpoch and bs.slot.epoch == (bs.blck.slot.epoch + 1))
|
||||
|
||||
proc getStateData(
|
||||
db: BeaconChainDB, cfg: RuntimeConfig, state: var StateData, bs: BlockSlot,
|
||||
rollback: RollbackProc): bool =
|
||||
if not bs.isStateCheckpoint():
|
||||
return false
|
||||
|
||||
let root = db.getStateRoot(bs.blck.root, bs.slot)
|
||||
if not root.isSome():
|
||||
return false
|
||||
|
||||
case cfg.stateForkAtEpoch(bs.slot.epoch)
|
||||
of forkMerge:
|
||||
if state.data.beaconStateFork != forkMerge:
|
||||
state.data = (ref ForkedHashedBeaconState)(beaconStateFork: forkMerge)[]
|
||||
|
||||
if not db.getMergeState(root.get(), state.data.hbsMerge.data, rollback):
|
||||
return false
|
||||
of forkAltair:
|
||||
if state.data.beaconStateFork != forkAltair:
|
||||
state.data = (ref ForkedHashedBeaconState)(beaconStateFork: forkAltair)[]
|
||||
|
||||
if not db.getAltairState(root.get(), state.data.hbsAltair.data, rollback):
|
||||
return false
|
||||
of forkPhase0:
|
||||
if state.data.beaconStateFork != forkPhase0:
|
||||
state.data = (ref ForkedHashedBeaconState)(beaconStateFork: forkPhase0)[]
|
||||
|
||||
if not db.getState(root.get(), state.data.hbsPhase0.data, rollback):
|
||||
return false
|
||||
|
||||
state.blck = bs.blck
|
||||
setStateRoot(state.data, root.get())
|
||||
|
||||
true
|
||||
|
||||
proc init*(T: type ChainDAGRef, cfg: RuntimeConfig, db: BeaconChainDB,
|
||||
updateFlags: UpdateFlags, onBlockCb: OnBlockCallback = nil,
|
||||
onHeadCb: OnHeadCallback = nil, onReorgCb: OnReorgCallback = nil,
|
||||
|
@ -412,15 +447,8 @@ proc init*(T: type ChainDAGRef, cfg: RuntimeConfig, db: BeaconChainDB,
|
|||
|
||||
# Now that we have a head block, we need to find the most recent state that
|
||||
# we have saved in the database
|
||||
while cur.blck != nil:
|
||||
if cur.isStateCheckpoint():
|
||||
let root = db.getStateRoot(cur.blck.root, cur.slot)
|
||||
if root.isSome():
|
||||
if db.getState(root.get(), tmpState.data.hbsPhase0.data, noRollback):
|
||||
setStateRoot(tmpState.data, root.get())
|
||||
tmpState.blck = cur.blck
|
||||
|
||||
break
|
||||
while cur.blck != nil and
|
||||
not getStateData(db, cfg, tmpState[], cur, noRollback):
|
||||
cur = cur.parentOrSlot()
|
||||
|
||||
if tmpState.blck == nil:
|
||||
|
@ -562,46 +590,6 @@ proc getEpochRef*(dag: ChainDAGRef, blck: BlockRef, epoch: Epoch): EpochRef =
|
|||
proc getFinalizedEpochRef*(dag: ChainDAGRef): EpochRef =
|
||||
dag.getEpochRef(dag.finalizedHead.blck, dag.finalizedHead.slot.epoch)
|
||||
|
||||
proc getState(
|
||||
dag: ChainDAGRef, state: var StateData, stateRoot: Eth2Digest,
|
||||
blck: BlockRef): bool =
|
||||
let restoreAddr =
|
||||
# Any restore point will do as long as it's not the object being updated
|
||||
if unsafeAddr(state) == unsafeAddr(dag.headState):
|
||||
unsafeAddr dag.clearanceState
|
||||
else:
|
||||
unsafeAddr dag.headState
|
||||
|
||||
let v = addr state.data
|
||||
|
||||
func restore() =
|
||||
assign(v[], restoreAddr[].data)
|
||||
|
||||
case dag.cfg.stateForkAtEpoch(blck.slot.epoch)
|
||||
of forkMerge:
|
||||
if state.data.beaconStateFork != forkMerge:
|
||||
state.data = (ref ForkedHashedBeaconState)(beaconStateFork: forkMerge)[]
|
||||
|
||||
if not dag.db.getMergeState(stateRoot, state.data.hbsMerge.data, restore):
|
||||
return false
|
||||
of forkAltair:
|
||||
if state.data.beaconStateFork != forkAltair:
|
||||
state.data = (ref ForkedHashedBeaconState)(beaconStateFork: forkAltair)[]
|
||||
|
||||
if not dag.db.getAltairState(stateRoot, state.data.hbsAltair.data, restore):
|
||||
return false
|
||||
of forkPhase0:
|
||||
if state.data.beaconStateFork != forkPhase0:
|
||||
state.data = (ref ForkedHashedBeaconState)(beaconStateFork: forkPhase0)[]
|
||||
|
||||
if not dag.db.getState(stateRoot, state.data.hbsPhase0.data, restore):
|
||||
return false
|
||||
|
||||
state.blck = blck
|
||||
setStateRoot(state.data, stateRoot)
|
||||
|
||||
true
|
||||
|
||||
func stateCheckpoint*(bs: BlockSlot): BlockSlot =
|
||||
## The first ancestor BlockSlot that is a state checkpoint
|
||||
var bs = bs
|
||||
|
@ -625,11 +613,21 @@ proc getState(dag: ChainDAGRef, state: var StateData, bs: BlockSlot): bool =
|
|||
if not bs.isStateCheckpoint():
|
||||
return false # Only state checkpoints are stored - no need to hit DB
|
||||
|
||||
if (let stateRoot = dag.db.getStateRoot(bs.blck.root, bs.slot);
|
||||
stateRoot.isSome()):
|
||||
return dag.getState(state, stateRoot.get(), bs.blck)
|
||||
let stateRoot = dag.db.getStateRoot(bs.blck.root, bs.slot)
|
||||
if stateRoot.isNone(): return false
|
||||
|
||||
false
|
||||
let restoreAddr =
|
||||
# Any restore point will do as long as it's not the object being updated
|
||||
if unsafeAddr(state) == unsafeAddr(dag.headState):
|
||||
unsafeAddr dag.clearanceState
|
||||
else:
|
||||
unsafeAddr dag.headState
|
||||
|
||||
let v = addr state.data
|
||||
func restore() =
|
||||
assign(v[], restoreAddr[].data)
|
||||
|
||||
getStateData(dag.db, dag.cfg, state, bs, restore)
|
||||
|
||||
proc putState(dag: ChainDAGRef, state: StateData) =
|
||||
# Store a state and its root
|
||||
|
@ -727,14 +725,19 @@ func getBlockBySlot*(dag: ChainDAGRef, slot: Slot): BlockRef =
|
|||
dag.head.atSlot(slot).blck
|
||||
|
||||
proc getForkedBlock*(dag: ChainDAGRef, blck: BlockRef): ForkedTrustedSignedBeaconBlock =
|
||||
# TODO implement this properly
|
||||
let phase0Block = dag.db.getBlock(blck.root)
|
||||
if phase0Block.isOk:
|
||||
return ForkedTrustedSignedBeaconBlock.init(phase0Block.get)
|
||||
|
||||
let altairBlock = dag.db.getAltairBlock(blck.root)
|
||||
if altairBlock.isOk:
|
||||
return ForkedTrustedSignedBeaconBlock.init(altairBlock.get)
|
||||
case dag.cfg.blockForkAtEpoch(blck.slot.epoch)
|
||||
of BeaconBlockFork.Phase0:
|
||||
let data = dag.db.getBlock(blck.root)
|
||||
if data.isOk():
|
||||
return ForkedTrustedSignedBeaconBlock.init(data.get)
|
||||
of BeaconBlockFork.Altair:
|
||||
let data = dag.db.getAltairBlock(blck.root)
|
||||
if data.isOk():
|
||||
return ForkedTrustedSignedBeaconBlock.init(data.get)
|
||||
of BeaconBlockFork.Merge:
|
||||
let data = dag.db.getMergeBlock(blck.root)
|
||||
if data.isOk():
|
||||
return ForkedTrustedSignedBeaconBlock.init(data.get)
|
||||
|
||||
raiseAssert "BlockRef without backing data, database corrupt?"
|
||||
|
||||
|
@ -782,26 +785,14 @@ proc applyBlock(
|
|||
var statePtr = unsafeAddr state # safe because `restore` is locally scoped
|
||||
func restore(v: var ForkedHashedBeaconState) =
|
||||
doAssert (addr(statePtr.data) == addr v)
|
||||
# TODO the block_clearance version uses assign() here
|
||||
statePtr[] = dag.headState
|
||||
assign(statePtr[], dag.headState)
|
||||
|
||||
loadStateCache(dag, cache, state.blck, getStateField(state.data, slot).epoch)
|
||||
|
||||
# TODO some abstractions
|
||||
let ok =
|
||||
case blck.data.kind:
|
||||
of BeaconBlockFork.Phase0:
|
||||
state_transition(
|
||||
dag.cfg, state.data, blck.data.phase0Block,
|
||||
cache, info, flags + dag.updateFlags + {slotProcessed}, restore)
|
||||
of BeaconBlockFork.Altair:
|
||||
state_transition(
|
||||
dag.cfg, state.data, blck.data.altairBlock,
|
||||
cache, info, flags + dag.updateFlags + {slotProcessed}, restore)
|
||||
of BeaconBlockFork.Merge:
|
||||
state_transition(
|
||||
dag.cfg, state.data, blck.data.mergeBlock,
|
||||
cache, info, flags + dag.updateFlags + {slotProcessed}, restore)
|
||||
let ok = withBlck(blck.data):
|
||||
state_transition(
|
||||
dag.cfg, state.data, blck, cache, info,
|
||||
flags + dag.updateFlags + {slotProcessed}, restore)
|
||||
if ok:
|
||||
state.blck = blck.refs
|
||||
|
||||
|
|
|
@ -320,6 +320,12 @@ func stateForkAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): BeaconStateFork =
|
|||
elif epoch >= cfg.ALTAIR_FORK_EPOCH: forkAltair
|
||||
else: forkPhase0
|
||||
|
||||
func blockForkAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): BeaconBlockFork =
|
||||
## Return the current fork for the given epoch.
|
||||
if epoch >= cfg.MERGE_FORK_EPOCH: BeaconBlockFork.Merge
|
||||
elif epoch >= cfg.ALTAIR_FORK_EPOCH: BeaconBlockFork.Altair
|
||||
else: BeaconBlockFork.Phase0
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_current_epoch
|
||||
func get_current_epoch*(x: ForkedHashedBeaconState): Epoch =
|
||||
## Return the current epoch.
|
||||
|
|
Loading…
Reference in New Issue