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(
|
proc containsBlock(
|
||||||
cfg: RuntimeConfig, db: BeaconChainDB, blck: BlockRef): bool =
|
cfg: RuntimeConfig, db: BeaconChainDB, blck: BlockRef): bool =
|
||||||
case cfg.stateForkAtEpoch(blck.slot.epoch)
|
case cfg.blockForkAtEpoch(blck.slot.epoch)
|
||||||
of forkMerge: db.containsBlockMerge(blck.root)
|
of BeaconBlockFork.Phase0: db.containsBlockPhase0(blck.root)
|
||||||
of forkAltair: db.containsBlockAltair(blck.root)
|
of BeaconBlockFork.Altair: db.containsBlockAltair(blck.root)
|
||||||
of forkPhase0: db.containsBlockPhase0(blck.root)
|
of BeaconBlockFork.Merge: db.containsBlockMerge(blck.root)
|
||||||
|
|
||||||
func isStateCheckpoint(bs: BlockSlot): bool =
|
func isStateCheckpoint(bs: BlockSlot): bool =
|
||||||
## State checkpoints are the points in time for which we store full state
|
## 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 == bs.blck.slot and bs.blck.parent == nil) or
|
||||||
(bs.slot.isEpoch and bs.slot.epoch == (bs.blck.slot.epoch + 1))
|
(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,
|
proc init*(T: type ChainDAGRef, cfg: RuntimeConfig, db: BeaconChainDB,
|
||||||
updateFlags: UpdateFlags, onBlockCb: OnBlockCallback = nil,
|
updateFlags: UpdateFlags, onBlockCb: OnBlockCallback = nil,
|
||||||
onHeadCb: OnHeadCallback = nil, onReorgCb: OnReorgCallback = 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
|
# Now that we have a head block, we need to find the most recent state that
|
||||||
# we have saved in the database
|
# we have saved in the database
|
||||||
while cur.blck != nil:
|
while cur.blck != nil and
|
||||||
if cur.isStateCheckpoint():
|
not getStateData(db, cfg, tmpState[], cur, noRollback):
|
||||||
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
|
|
||||||
cur = cur.parentOrSlot()
|
cur = cur.parentOrSlot()
|
||||||
|
|
||||||
if tmpState.blck == nil:
|
if tmpState.blck == nil:
|
||||||
|
@ -562,46 +590,6 @@ proc getEpochRef*(dag: ChainDAGRef, blck: BlockRef, epoch: Epoch): EpochRef =
|
||||||
proc getFinalizedEpochRef*(dag: ChainDAGRef): EpochRef =
|
proc getFinalizedEpochRef*(dag: ChainDAGRef): EpochRef =
|
||||||
dag.getEpochRef(dag.finalizedHead.blck, dag.finalizedHead.slot.epoch)
|
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 =
|
func stateCheckpoint*(bs: BlockSlot): BlockSlot =
|
||||||
## The first ancestor BlockSlot that is a state checkpoint
|
## The first ancestor BlockSlot that is a state checkpoint
|
||||||
var bs = bs
|
var bs = bs
|
||||||
|
@ -625,11 +613,21 @@ proc getState(dag: ChainDAGRef, state: var StateData, bs: BlockSlot): bool =
|
||||||
if not bs.isStateCheckpoint():
|
if not bs.isStateCheckpoint():
|
||||||
return false # Only state checkpoints are stored - no need to hit DB
|
return false # Only state checkpoints are stored - no need to hit DB
|
||||||
|
|
||||||
if (let stateRoot = dag.db.getStateRoot(bs.blck.root, bs.slot);
|
let stateRoot = dag.db.getStateRoot(bs.blck.root, bs.slot)
|
||||||
stateRoot.isSome()):
|
if stateRoot.isNone(): return false
|
||||||
return dag.getState(state, stateRoot.get(), bs.blck)
|
|
||||||
|
|
||||||
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) =
|
proc putState(dag: ChainDAGRef, state: StateData) =
|
||||||
# Store a state and its root
|
# Store a state and its root
|
||||||
|
@ -727,14 +725,19 @@ func getBlockBySlot*(dag: ChainDAGRef, slot: Slot): BlockRef =
|
||||||
dag.head.atSlot(slot).blck
|
dag.head.atSlot(slot).blck
|
||||||
|
|
||||||
proc getForkedBlock*(dag: ChainDAGRef, blck: BlockRef): ForkedTrustedSignedBeaconBlock =
|
proc getForkedBlock*(dag: ChainDAGRef, blck: BlockRef): ForkedTrustedSignedBeaconBlock =
|
||||||
# TODO implement this properly
|
case dag.cfg.blockForkAtEpoch(blck.slot.epoch)
|
||||||
let phase0Block = dag.db.getBlock(blck.root)
|
of BeaconBlockFork.Phase0:
|
||||||
if phase0Block.isOk:
|
let data = dag.db.getBlock(blck.root)
|
||||||
return ForkedTrustedSignedBeaconBlock.init(phase0Block.get)
|
if data.isOk():
|
||||||
|
return ForkedTrustedSignedBeaconBlock.init(data.get)
|
||||||
let altairBlock = dag.db.getAltairBlock(blck.root)
|
of BeaconBlockFork.Altair:
|
||||||
if altairBlock.isOk:
|
let data = dag.db.getAltairBlock(blck.root)
|
||||||
return ForkedTrustedSignedBeaconBlock.init(altairBlock.get)
|
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?"
|
raiseAssert "BlockRef without backing data, database corrupt?"
|
||||||
|
|
||||||
|
@ -782,26 +785,14 @@ proc applyBlock(
|
||||||
var statePtr = unsafeAddr state # safe because `restore` is locally scoped
|
var statePtr = unsafeAddr state # safe because `restore` is locally scoped
|
||||||
func restore(v: var ForkedHashedBeaconState) =
|
func restore(v: var ForkedHashedBeaconState) =
|
||||||
doAssert (addr(statePtr.data) == addr v)
|
doAssert (addr(statePtr.data) == addr v)
|
||||||
# TODO the block_clearance version uses assign() here
|
assign(statePtr[], dag.headState)
|
||||||
statePtr[] = dag.headState
|
|
||||||
|
|
||||||
loadStateCache(dag, cache, state.blck, getStateField(state.data, slot).epoch)
|
loadStateCache(dag, cache, state.blck, getStateField(state.data, slot).epoch)
|
||||||
|
|
||||||
# TODO some abstractions
|
let ok = withBlck(blck.data):
|
||||||
let ok =
|
state_transition(
|
||||||
case blck.data.kind:
|
dag.cfg, state.data, blck, cache, info,
|
||||||
of BeaconBlockFork.Phase0:
|
flags + dag.updateFlags + {slotProcessed}, restore)
|
||||||
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)
|
|
||||||
if ok:
|
if ok:
|
||||||
state.blck = blck.refs
|
state.blck = blck.refs
|
||||||
|
|
||||||
|
|
|
@ -320,6 +320,12 @@ func stateForkAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): BeaconStateFork =
|
||||||
elif epoch >= cfg.ALTAIR_FORK_EPOCH: forkAltair
|
elif epoch >= cfg.ALTAIR_FORK_EPOCH: forkAltair
|
||||||
else: forkPhase0
|
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
|
# 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 =
|
func get_current_epoch*(x: ForkedHashedBeaconState): Epoch =
|
||||||
## Return the current epoch.
|
## Return the current epoch.
|
||||||
|
|
Loading…
Reference in New Issue