nimbus-eth1/nimbus/sync/beacon/skeleton_main.nim

179 lines
5.6 KiB
Nim

# Nimbus
# Copyright (c) 2023 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at
# https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at
# https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed
# except according to those terms.
import
./skeleton_desc,
./skeleton_utils,
./skeleton_db,
./skeleton_algo
export
skeleton_desc,
skeleton_algo.isLinked,
skeleton_algo.putBlocks,
skeleton_algo.fillCanonicalChain
{.push gcsafe, raises: [].}
logScope:
topics = "skeleton"
# ------------------------------------------------------------------------------
# Constructors
# ------------------------------------------------------------------------------
proc new*(_: type SkeletonRef, chain: ChainRef): SkeletonRef =
SkeletonRef(
progress: Progress(),
pulled : 0,
filling : false,
chain : chain,
db : chain.db,
started : getTime(),
logged : getTime(),
conf : SkeletonConfig(
fillCanonicalBackStep: 100,
subchainMergeMinimum : 1000,
),
)
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
proc open*(sk: SkeletonRef): Result[void, string] =
if sk.chain.com.ttd.isNone:
return err("Cannot create skeleton as ttd not set")
sk.readProgress().isOkOr:
return err(error)
sk.started = getTime()
ok()
proc setHead*(sk: SkeletonRef, head: BlockHeader,
force = true, init = false,
reorgthrow = false): Result[StatusAndReorg, string] =
## Announce and integrate a new head.
## @params head - The block being attempted as a new head
## @params force - Flag to indicate if this is just a check of
## worthiness or a actually new head
## @params init - Flag this is the first time since the beacon
## sync start to perform additional tasks
## @params reorgthrow - Flag to indicate if we would actually like
## to throw if there is a reorg
## instead of just returning the boolean
##
## @returns True if the head (will) cause a reorg in the
## canonical skeleton subchain
let
number = head.u64
debug "New skeleton head announced",
number,
hash=head.blockHashStr,
force
let reorg = sk.processNewHead(head, force).valueOr:
return err(error)
if force and reorg:
# It could just be a reorg at this head with previous tail preserved
let
subchain = if sk.isEmpty: Segment(nil)
else: sk.last
maybeParent = sk.getHeader(number - 1).valueOr:
return err(error)
parentHash = maybeParent.blockHash
if subchain.isNil or maybeParent.isNone or parentHash != head.parentHash:
let sub = segment(number, number, head.parentHash)
sk.push(sub)
debug "Created new subchain", sub
else:
# Only the head differed, tail is preserved
subchain.head = number
# Reset the filling of canonical head from tail on reorg
sk.progress.canonicalHeadReset = true
# Put this block irrespective of the force
sk.putHeader(head)
if init:
sk.trySubChainsMerge().isOkOr:
return err(error)
if (force and reorg) or init:
sk.progress.linked = sk.isLinked.valueOr:
return err(error)
if force or init:
sk.writeProgress()
var res = StatusAndReorg(reorg: reorg)
if force and sk.progress.linked:
res.status.incl FillCanonical
# Earlier we were throwing on reorg, essentially for the purposes for
# killing the reverse fetcher
# but it can be handled properly in the calling fn without erroring
if reorg and reorgthrow:
if force:
res.status.incl SyncReorged
else:
res.status.incl ReorgDenied
ok(res)
proc initSync*(sk: SkeletonRef, head: BlockHeader,
reorgthrow = false): Result[StatusAndReorg, string] =
## Setup the skeleton to init sync with head
## @params head - The block with which we want to init the skeleton head
## @params reorgthrow - If we would like the function to throw instead of
## silently return if there is reorg of the skeleton head
##
## @returns True if the skeleton was reorged trying to init else false
sk.setHead(head, true, true, reorgthrow)
func bodyRange*(sk: SkeletonRef): Result[BodyRange, string] =
## Get range of bodies need to be downloaded by synchronizer
var canonicalHead = sk.blockHeight
let subchain = sk.last
if sk.progress.canonicalHeadReset:
if subchain.tail > canonicalHead + 1:
return err("Canonical head should already be on or " &
"ahead subchain tail canonicalHead=" &
$canonicalHead & ", tail=" & $subchain.tail)
let newHead = if subchain.tail > 0: subchain.tail - 1
else: 0
canonicalHead = newHead
ok(BodyRange(
min: canonicalHead,
max: subchain.head,
))
# ------------------------------------------------------------------------------
# Getters and setters
# ------------------------------------------------------------------------------
func fillCanonicalBackStep*(sk: SkeletonRef): uint64 =
sk.conf.fillCanonicalBackStep
func subchainMergeMinimum*(sk: SkeletonRef): uint64 =
sk.conf.subchainMergeMinimum
proc `fillCanonicalBackStep=`*(sk: SkeletonRef, val: uint64) =
sk.conf.fillCanonicalBackStep = val
proc `subchainMergeMinimum=`*(sk: SkeletonRef, val: uint64) =
sk.conf.subchainMergeMinimum = val