limit fork choice initialization horizon (#1910)
This helps manage the long startup time on chains that are not finalizing for long periods of time
This commit is contained in:
parent
020a32ffa4
commit
81f4fe0783
|
@ -41,22 +41,46 @@ proc init*(T: type AttestationPool, chainDag: ChainDAGRef, quarantine: Quarantin
|
||||||
|
|
||||||
var blocks: seq[BlockRef]
|
var blocks: seq[BlockRef]
|
||||||
var cur = chainDag.head
|
var cur = chainDag.head
|
||||||
|
|
||||||
|
# When the chain is finalizing, the votes between the head block and the
|
||||||
|
# finalized checkpoint should be enough for a stable fork choice - when the
|
||||||
|
# chain is not finalizing, we want to seed it with as many votes as possible
|
||||||
|
# since the whole history of each branch might be significant. It is however
|
||||||
|
# a game of diminishing returns, and we have to weigh it against the time
|
||||||
|
# it takes to replay that many blocks during startup and thus miss _new_
|
||||||
|
# votes.
|
||||||
|
const ForkChoiceHorizon = 256
|
||||||
while cur != chainDag.finalizedHead.blck:
|
while cur != chainDag.finalizedHead.blck:
|
||||||
blocks.add cur
|
blocks.add cur
|
||||||
cur = cur.parent
|
cur = cur.parent
|
||||||
|
|
||||||
debug "Preloading fork choice with blocks", blocks = blocks.len
|
info "Initializing fork choice from block database",
|
||||||
|
unfinalized_blocks = blocks.len
|
||||||
|
|
||||||
for blck in reversed(blocks):
|
var epochRef = finalizedEpochRef
|
||||||
|
for i in 0..<blocks.len:
|
||||||
let
|
let
|
||||||
epochRef = chainDag.getEpochRef(blck, blck.slot.compute_epoch_at_slot)
|
blck = blocks[blocks.len - i - 1]
|
||||||
status =
|
status =
|
||||||
forkChoice.process_block(
|
if i > (blocks.len - ForkChoiceHorizon) or (i mod 1024 != 0):
|
||||||
chainDag, epochRef, blck, chainDag.get(blck).data.message, blck.slot)
|
# Fork choice needs to know about the full block tree up to the
|
||||||
|
# finalization point, but doesn't really need to have overly accurate
|
||||||
|
# justification and finalization points until we get close to head -
|
||||||
|
# nonetheless, we'll make sure to pass a fresh finalization point now
|
||||||
|
# and then to make sure the fork choice data structure doesn't grow
|
||||||
|
# too big - getting an EpochRef can be expensive.
|
||||||
|
forkChoice.backend.process_block(
|
||||||
|
blck.root, blck.parent.root,
|
||||||
|
epochRef.current_justified_checkpoint.epoch,
|
||||||
|
epochRef.finalized_checkpoint.epoch)
|
||||||
|
else:
|
||||||
|
epochRef = chainDag.getEpochRef(blck, blck.slot.epoch)
|
||||||
|
forkChoice.process_block(
|
||||||
|
chainDag, epochRef, blck, chainDag.get(blck).data.message, blck.slot)
|
||||||
|
|
||||||
doAssert status.isOk(), "Error in preloading the fork choice: " & $status.error
|
doAssert status.isOk(), "Error in preloading the fork choice: " & $status.error
|
||||||
|
|
||||||
debug "Fork choice initialized",
|
info "Fork choice initialized",
|
||||||
justified_epoch = chainDag.headState.data.data.current_justified_checkpoint.epoch,
|
justified_epoch = chainDag.headState.data.data.current_justified_checkpoint.epoch,
|
||||||
finalized_epoch = chainDag.headState.data.data.finalized_checkpoint.epoch,
|
finalized_epoch = chainDag.headState.data.data.finalized_checkpoint.epoch,
|
||||||
finalized_root = shortlog(chainDag.finalizedHead.blck.root)
|
finalized_root = shortlog(chainDag.finalizedHead.blck.root)
|
||||||
|
|
|
@ -35,6 +35,15 @@ proc parseBootstrapAddress*(address: TaintedString):
|
||||||
else:
|
else:
|
||||||
return err "Ignoring unrecognized bootstrap address type"
|
return err "Ignoring unrecognized bootstrap address type"
|
||||||
|
|
||||||
|
iterator strippedLines(filename: string): string {.raises: [ref IOError].} =
|
||||||
|
for line in lines(filename):
|
||||||
|
let stripped = strip(line)
|
||||||
|
if stripped.startsWith('#'): # Comments
|
||||||
|
continue
|
||||||
|
|
||||||
|
if stripped.len > 0:
|
||||||
|
yield stripped
|
||||||
|
|
||||||
proc addBootstrapNode*(bootstrapAddr: string,
|
proc addBootstrapNode*(bootstrapAddr: string,
|
||||||
bootstrapEnrs: var seq[enr.Record]) =
|
bootstrapEnrs: var seq[enr.Record]) =
|
||||||
# Ignore empty lines or lines starting with #
|
# Ignore empty lines or lines starting with #
|
||||||
|
@ -54,7 +63,7 @@ proc loadBootstrapFile*(bootstrapFile: string,
|
||||||
let ext = splitFile(bootstrapFile).ext
|
let ext = splitFile(bootstrapFile).ext
|
||||||
if cmpIgnoreCase(ext, ".txt") == 0 or cmpIgnoreCase(ext, ".enr") == 0 :
|
if cmpIgnoreCase(ext, ".txt") == 0 or cmpIgnoreCase(ext, ".enr") == 0 :
|
||||||
try:
|
try:
|
||||||
for ln in lines(bootstrapFile):
|
for ln in strippedLines(bootstrapFile):
|
||||||
addBootstrapNode(ln, bootstrapEnrs)
|
addBootstrapNode(ln, bootstrapEnrs)
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
error "Could not read bootstrap file", msg = e.msg
|
error "Could not read bootstrap file", msg = e.msg
|
||||||
|
@ -64,7 +73,7 @@ proc loadBootstrapFile*(bootstrapFile: string,
|
||||||
# TODO. This is very ugly, but let's try to negotiate the
|
# TODO. This is very ugly, but let's try to negotiate the
|
||||||
# removal of YAML metadata.
|
# removal of YAML metadata.
|
||||||
try:
|
try:
|
||||||
for ln in lines(bootstrapFile):
|
for ln in strippedLines(bootstrapFile):
|
||||||
addBootstrapNode(string(ln.strip()[3..^2]), bootstrapEnrs)
|
addBootstrapNode(string(ln.strip()[3..^2]), bootstrapEnrs)
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
error "Could not read bootstrap file", msg = e.msg
|
error "Could not read bootstrap file", msg = e.msg
|
||||||
|
|
Loading…
Reference in New Issue