State/block pruning
This commit is contained in:
parent
76d3e74b02
commit
777b3f4e29
|
@ -86,6 +86,12 @@ proc putStateRoot*(db: BeaconChainDB, root: Eth2Digest, slot: Slot,
|
||||||
proc putBlock*(db: BeaconChainDB, value: BeaconBlock) =
|
proc putBlock*(db: BeaconChainDB, value: BeaconBlock) =
|
||||||
db.putBlock(signing_root(value), value)
|
db.putBlock(signing_root(value), value)
|
||||||
|
|
||||||
|
proc delBlock*(db: BeaconChainDB, key: Eth2Digest) =
|
||||||
|
db.backend.del(subkey(BeaconBlock, key))
|
||||||
|
|
||||||
|
proc delState*(db: BeaconChainDB, key: Eth2Digest) =
|
||||||
|
db.backend.del(subkey(BeaconState, key))
|
||||||
|
|
||||||
proc putHeadBlock*(db: BeaconChainDB, key: Eth2Digest) =
|
proc putHeadBlock*(db: BeaconChainDB, key: Eth2Digest) =
|
||||||
db.backend.put(subkey(kHeadBlock), key.data) # TODO head block?
|
db.backend.put(subkey(kHeadBlock), key.data) # TODO head block?
|
||||||
|
|
||||||
|
|
|
@ -118,7 +118,7 @@ type
|
||||||
## Tree of blocks pointing back to a finalized block on the chain we're
|
## Tree of blocks pointing back to a finalized block on the chain we're
|
||||||
## interested in - we call that block the tail
|
## interested in - we call that block the tail
|
||||||
|
|
||||||
blocksBySlot*: Table[uint64, seq[BlockRef]]
|
blocksBySlot*: Table[Slot, seq[BlockRef]]
|
||||||
|
|
||||||
tail*: BlockRef ##\
|
tail*: BlockRef ##\
|
||||||
## The earliest finalized block we know about
|
## The earliest finalized block we know about
|
||||||
|
|
|
@ -90,10 +90,10 @@ proc init*(T: type BlockPool, db: BeaconChainDB): BlockPool =
|
||||||
else:
|
else:
|
||||||
headRef = tailRef
|
headRef = tailRef
|
||||||
|
|
||||||
var blocksBySlot = initTable[uint64, seq[BlockRef]]()
|
var blocksBySlot = initTable[Slot, seq[BlockRef]]()
|
||||||
for _, b in tables.pairs(blocks):
|
for _, b in tables.pairs(blocks):
|
||||||
let slot = db.getBlock(b.root).get().slot
|
let slot = db.getBlock(b.root).get().slot
|
||||||
blocksBySlot.mgetOrPut(slot.uint64, @[]).add(b)
|
blocksBySlot.mgetOrPut(slot, @[]).add(b)
|
||||||
|
|
||||||
let
|
let
|
||||||
# The head state is necessary to find out what we considered to be the
|
# The head state is necessary to find out what we considered to be the
|
||||||
|
@ -132,11 +132,21 @@ proc init*(T: type BlockPool, db: BeaconChainDB): BlockPool =
|
||||||
heads: @[head]
|
heads: @[head]
|
||||||
)
|
)
|
||||||
|
|
||||||
func addSlotMapping(pool: BlockPool, slot: uint64, br: BlockRef) =
|
proc addSlotMapping(pool: BlockPool, br: BlockRef) =
|
||||||
proc addIfMissing(s: var seq[BlockRef], v: BlockRef) =
|
proc addIfMissing(s: var seq[BlockRef], v: BlockRef) =
|
||||||
if v notin s:
|
if v notin s:
|
||||||
s.add(v)
|
s.add(v)
|
||||||
pool.blocksBySlot.mgetOrPut(slot, @[]).addIfMissing(br)
|
pool.blocksBySlot.mgetOrPut(br.slot, @[]).addIfMissing(br)
|
||||||
|
|
||||||
|
proc delSlotMapping(pool: BlockPool, br: BlockRef) =
|
||||||
|
var blks = pool.blocksBySlot.getOrDefault(br.slot)
|
||||||
|
if blks.len != 0:
|
||||||
|
let i = blks.find(br)
|
||||||
|
if i >= 0: blks.del(i)
|
||||||
|
if blks.len == 0:
|
||||||
|
pool.blocksBySlot.del(br.slot)
|
||||||
|
else:
|
||||||
|
pool.blocksBySlot[br.slot] = blks
|
||||||
|
|
||||||
proc updateStateData*(
|
proc updateStateData*(
|
||||||
pool: BlockPool, state: var StateData, bs: BlockSlot) {.gcsafe.}
|
pool: BlockPool, state: var StateData, bs: BlockSlot) {.gcsafe.}
|
||||||
|
@ -155,7 +165,7 @@ proc addResolvedBlock(
|
||||||
|
|
||||||
pool.blocks[blockRoot] = blockRef
|
pool.blocks[blockRoot] = blockRef
|
||||||
|
|
||||||
pool.addSlotMapping(blck.slot.uint64, blockRef)
|
pool.addSlotMapping(blockRef)
|
||||||
|
|
||||||
# Resolved blocks should be stored in database
|
# Resolved blocks should be stored in database
|
||||||
pool.db.putBlock(blockRoot, blck)
|
pool.db.putBlock(blockRoot, blck)
|
||||||
|
@ -393,8 +403,8 @@ func getOrResolve*(pool: var BlockPool, root: Eth2Digest): BlockRef =
|
||||||
if result.isNil:
|
if result.isNil:
|
||||||
pool.missing[root] = MissingBlock(slots: 1)
|
pool.missing[root] = MissingBlock(slots: 1)
|
||||||
|
|
||||||
iterator blockRootsForSlot*(pool: BlockPool, slot: uint64|Slot): Eth2Digest =
|
iterator blockRootsForSlot*(pool: BlockPool, slot: Slot): Eth2Digest =
|
||||||
for br in pool.blocksBySlot.getOrDefault(slot.uint64, @[]):
|
for br in pool.blocksBySlot.getOrDefault(slot, @[]):
|
||||||
yield br.root
|
yield br.root
|
||||||
|
|
||||||
func checkMissing*(pool: var BlockPool): seq[FetchRecord] =
|
func checkMissing*(pool: var BlockPool): seq[FetchRecord] =
|
||||||
|
@ -580,6 +590,44 @@ func isAncestorOf*(a, b: BlockRef): bool =
|
||||||
else:
|
else:
|
||||||
a.isAncestorOf(b.parent)
|
a.isAncestorOf(b.parent)
|
||||||
|
|
||||||
|
proc delBlockAndState(pool: BlockPool, blockRoot: Eth2Digest) =
|
||||||
|
if (let blk = pool.db.getBlock(blockRoot); blk.isSome):
|
||||||
|
pool.db.delState(blk.get.stateRoot)
|
||||||
|
pool.db.delBlock(blockRoot)
|
||||||
|
|
||||||
|
proc delFinalizedStateIfNeeded(pool: BlockPool, b: BlockRef) =
|
||||||
|
# Delete finalized state for block `b` from the database, that doesn't need
|
||||||
|
# to be kept for replaying.
|
||||||
|
# TODO: Currently the protocol doesn't provide a way to request states,
|
||||||
|
# so we don't need any of the finalized states, and thus remove all of them
|
||||||
|
# (except the most recent)
|
||||||
|
if (let blk = pool.db.getBlock(b.root); blk.isSome):
|
||||||
|
pool.db.delState(blk.get.stateRoot)
|
||||||
|
|
||||||
|
proc setTailBlock(pool: BlockPool, newTail: BlockRef) =
|
||||||
|
## Advance tail block, pruning all the states and blocks with older slots
|
||||||
|
let oldTail = pool.tail
|
||||||
|
let fromSlot = oldTail.slot.uint64
|
||||||
|
let toSlot = newTail.slot.uint64 - 1
|
||||||
|
assert(toSlot > fromSlot)
|
||||||
|
for s in fromSlot .. toSlot:
|
||||||
|
for b in pool.blocksBySlot.getOrDefault(s.Slot, @[]):
|
||||||
|
pool.delBlockAndState(b.root)
|
||||||
|
b.children = @[]
|
||||||
|
b.parent = nil
|
||||||
|
pool.blocks.del(b.root)
|
||||||
|
pool.pending.del(b.root)
|
||||||
|
pool.missing.del(b.root)
|
||||||
|
|
||||||
|
pool.blocksBySlot.del(s.Slot)
|
||||||
|
|
||||||
|
pool.db.putTailBlock(newTail.root)
|
||||||
|
pool.tail = newTail
|
||||||
|
pool.addSlotMapping(newTail)
|
||||||
|
info "Tail block updated",
|
||||||
|
slot = newTail.slot,
|
||||||
|
root = shortLog(newTail.root)
|
||||||
|
|
||||||
proc updateHead*(pool: BlockPool, state: var StateData, blck: BlockRef) =
|
proc updateHead*(pool: BlockPool, state: var StateData, blck: BlockRef) =
|
||||||
## Update what we consider to be the current head, as given by the fork
|
## Update what we consider to be the current head, as given by the fork
|
||||||
## choice.
|
## choice.
|
||||||
|
@ -634,10 +682,9 @@ proc updateHead*(pool: BlockPool, state: var StateData, blck: BlockRef) =
|
||||||
cat = "fork_choice"
|
cat = "fork_choice"
|
||||||
|
|
||||||
let
|
let
|
||||||
|
finalizedEpochStartSlot = state.data.data.finalized_checkpoint.epoch.compute_start_slot_at_epoch()
|
||||||
# TODO there might not be a block at the epoch boundary - what then?
|
# TODO there might not be a block at the epoch boundary - what then?
|
||||||
finalizedHead =
|
finalizedHead = blck.findAncestorBySlot(finalizedEpochStartSlot)
|
||||||
blck.findAncestorBySlot(
|
|
||||||
state.data.data.finalized_checkpoint.epoch.compute_start_slot_at_epoch())
|
|
||||||
|
|
||||||
doAssert (not finalizedHead.blck.isNil),
|
doAssert (not finalizedHead.blck.isNil),
|
||||||
"Block graph should always lead to a finalized block"
|
"Block graph should always lead to a finalized block"
|
||||||
|
@ -666,6 +713,10 @@ proc updateHead*(pool: BlockPool, state: var StateData, blck: BlockRef) =
|
||||||
for child in cur.parent.children:
|
for child in cur.parent.children:
|
||||||
if child != cur:
|
if child != cur:
|
||||||
pool.blocks.del(child.root)
|
pool.blocks.del(child.root)
|
||||||
|
pool.delBlockAndState(child.root)
|
||||||
|
pool.delSlotMapping(child)
|
||||||
|
else:
|
||||||
|
pool.delFinalizedStateIfNeeded(child)
|
||||||
cur.parent.children = @[cur]
|
cur.parent.children = @[cur]
|
||||||
cur = cur.parent
|
cur = cur.parent
|
||||||
|
|
||||||
|
@ -678,6 +729,14 @@ proc updateHead*(pool: BlockPool, state: var StateData, blck: BlockRef) =
|
||||||
not pool.heads[n].blck.isAncestorOf(pool.finalizedHead.blck):
|
not pool.heads[n].blck.isAncestorOf(pool.finalizedHead.blck):
|
||||||
pool.heads.del(n)
|
pool.heads.del(n)
|
||||||
|
|
||||||
|
# Calculate new tail block and set it
|
||||||
|
# New tail should be WEAK_SUBJECTIVITY_PERIOD * 2 older than finalizedHead
|
||||||
|
const tailSlotInterval = WEAK_SUBJECTVITY_PERIOD * 2
|
||||||
|
if finalizedEpochStartSlot - GENESIS_SLOT > tailSlotInterval:
|
||||||
|
let tailSlot = finalizedEpochStartSlot - tailSlotInterval
|
||||||
|
let newTail = finalizedHead.blck.findAncestorBySlot(tailSlot)
|
||||||
|
pool.setTailBlock(newTail.blck)
|
||||||
|
|
||||||
func latestJustifiedBlock*(pool: BlockPool): BlockSlot =
|
func latestJustifiedBlock*(pool: BlockPool): BlockSlot =
|
||||||
## Return the most recent block that is justified and at least as recent
|
## Return the most recent block that is justified and at least as recent
|
||||||
## as the latest finalized block
|
## as the latest finalized block
|
||||||
|
|
|
@ -67,6 +67,12 @@ const
|
||||||
# Not part of spec. Still useful, pending removing usage if appropriate.
|
# Not part of spec. Still useful, pending removing usage if appropriate.
|
||||||
ZERO_HASH* = Eth2Digest()
|
ZERO_HASH* = Eth2Digest()
|
||||||
|
|
||||||
|
# Not part of spec
|
||||||
|
WEAK_SUBJECTVITY_PERIOD* =
|
||||||
|
Slot(uint64(4 * 30 * 24 * 60 * 60) div SECONDS_PER_SLOT)
|
||||||
|
# TODO: This needs revisiting.
|
||||||
|
# Why was the validator WITHDRAWAL_PERIOD altered in the spec?
|
||||||
|
|
||||||
template maxSize*(n: int) {.pragma.}
|
template maxSize*(n: int) {.pragma.}
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
|
@ -2,12 +2,6 @@ import
|
||||||
os, chronos, json_serialization,
|
os, chronos, json_serialization,
|
||||||
spec/[datatypes], beacon_chain_db
|
spec/[datatypes], beacon_chain_db
|
||||||
|
|
||||||
const
|
|
||||||
WEAK_SUBJECTVITY_PERIOD* =
|
|
||||||
Slot(uint64(4 * 30 * 24 * 60 * 60) div SECONDS_PER_SLOT)
|
|
||||||
# TODO: This needs revisiting.
|
|
||||||
# Why was the validator WITHDRAWAL_PERIOD altered in the spec?
|
|
||||||
|
|
||||||
proc obtainTrustedStateSnapshot*(db: BeaconChainDB): Future[BeaconState] {.async.} =
|
proc obtainTrustedStateSnapshot*(db: BeaconChainDB): Future[BeaconState] {.async.} =
|
||||||
# In case our latest state is too old, we must obtain a recent snapshot
|
# In case our latest state is too old, we must obtain a recent snapshot
|
||||||
# of the state from a trusted location. This is explained in detail here:
|
# of the state from a trusted location. This is explained in detail here:
|
||||||
|
|
Loading…
Reference in New Issue