2022-10-08 17:20:50 +00:00
|
|
|
# Nimbus
|
|
|
|
# Copyright (c) 2021 Status Research & Development GmbH
|
|
|
|
# Licensed under either of
|
|
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0)
|
|
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
|
|
|
# http://opensource.org/licenses/MIT)
|
|
|
|
# at your option. This file may not be copied, modified, or distributed
|
|
|
|
# except according to those terms.
|
|
|
|
|
2022-12-24 09:54:18 +00:00
|
|
|
## Fetch accounts DB ranges
|
|
|
|
## ========================
|
2022-10-08 17:20:50 +00:00
|
|
|
##
|
2022-12-24 09:54:18 +00:00
|
|
|
## Acccount ranges allocated on the database are organised in the set
|
|
|
|
## `env.fetchAccounts.processed` and the ranges that can be fetched are in
|
|
|
|
## the pair of range sets `env.fetchAccounts.unprocessed`. The ranges of these
|
|
|
|
## sets are mutually disjunct yet the union of all ranges does not fully
|
|
|
|
## comprise the complete `[0,2^256]` range. The missing parts are the ranges
|
|
|
|
## currently processed by worker peers.
|
2022-10-08 17:20:50 +00:00
|
|
|
##
|
2022-12-24 09:54:18 +00:00
|
|
|
## Algorithm
|
|
|
|
## ---------
|
2022-11-01 15:07:44 +00:00
|
|
|
##
|
|
|
|
## * Some interval `iv` is removed from the `env.fetchAccounts.unprocessed`
|
2022-12-24 09:54:18 +00:00
|
|
|
## pair of set (so the interval `iv` is protected from other worker
|
|
|
|
## instances and might be safely accessed and manipulated by this function.)
|
|
|
|
## Stop if there are no more intervals.
|
2022-11-01 15:07:44 +00:00
|
|
|
##
|
2022-12-24 09:54:18 +00:00
|
|
|
## * The accounts data points in the interval `iv` (aka account hashes) are
|
|
|
|
## fetched from the network. This results in *key-value* pairs for accounts.
|
2022-11-01 15:07:44 +00:00
|
|
|
##
|
2022-12-24 09:54:18 +00:00
|
|
|
## * The received *key-value* pairs from the previous step are verified and
|
|
|
|
## merged into the accounts hexary trie persistent database.
|
2022-11-01 15:07:44 +00:00
|
|
|
##
|
2022-12-24 09:54:18 +00:00
|
|
|
## * *Key-value* pairs that were invalid or were not recevied from the network
|
|
|
|
## are merged back into the range set `env.fetchAccounts.unprocessed`. The
|
|
|
|
## remainder of successfully added ranges (and verified key gaps) are merged
|
|
|
|
## into `env.fetchAccounts.processed`.
|
|
|
|
##
|
|
|
|
## * For *Key-value* pairs that have an active account storage slot sub-trie,
|
|
|
|
## the account including administrative data is queued in
|
|
|
|
## `env.fetchStorageFull`.
|
2022-10-08 17:20:50 +00:00
|
|
|
##
|
|
|
|
import
|
|
|
|
chronicles,
|
|
|
|
chronos,
|
2022-10-20 16:59:54 +00:00
|
|
|
eth/[common, p2p],
|
2022-10-08 17:20:50 +00:00
|
|
|
stew/[interval_set, keyed_queue],
|
|
|
|
stint,
|
2022-12-19 21:22:09 +00:00
|
|
|
../../../../utils/prettify,
|
|
|
|
../../../sync_desc,
|
|
|
|
"../.."/[constants, range_desc, worker_desc],
|
|
|
|
../com/[com_error, get_account_range],
|
|
|
|
../db/[hexary_envelope, snapdb_accounts],
|
2022-12-24 09:54:18 +00:00
|
|
|
"."/[storage_queue_helper, swap_in]
|
2022-10-08 17:20:50 +00:00
|
|
|
|
|
|
|
{.push raises: [Defect].}
|
|
|
|
|
|
|
|
logScope:
|
Prep for full sync after snap make 4 (#1282)
* Re-arrange fetching storage slots in batch module
why;
Previously, fetching partial slot ranges first has a chance of
terminating the worker peer 9due to network error) while there were
many inheritable storage slots on the queue.
Now, inheritance is checked first, then full slot ranges and finally
partial ranges.
* Update logging
* Bundled node information for healing into single object `NodeSpecs`
why:
Previously, partial paths and node keys were kept in separate variables.
This approach was error prone due to copying/reassembling function
argument objects.
As all partial paths, keys, and node data types are more or less handled
as `Blob`s over the network (using Eth/6x, or Snap/1) it makes sense to
hold these `Blob`s as named field in a single object (even if not all
fields are active for the current purpose.)
* For good housekeeping, using `NodeKey` type only for account keys
why:
previously, a mixture of `NodeKey` and `Hash256` was used. Now, only
state or storage root keys use the `Hash256` type.
* Always accept latest pivot (and not a slightly older one)
why;
For testing it was tried to use a slightly older pivot state root than
available. Some anecdotal tests seemed to suggest an advantage so that
more peers are willing to serve on that older pivot. But this could not
be confirmed in subsequent tests (still anecdotal, though.)
As a side note, the distance of the latest pivot to its predecessor is
at least 128 (or whatever the constant `minPivotBlockDistance` is
assigned to.)
* Reshuffle name components for some file and function names
why:
Clarifies purpose:
"storages" becomes: "storage slots"
"store" becomes: "range fetch"
* Stash away currently unused modules in sub-folder named "notused"
2022-10-27 13:49:28 +00:00
|
|
|
topics = "snap-range"
|
2022-10-08 17:20:50 +00:00
|
|
|
|
|
|
|
const
|
|
|
|
extraTraceMessages = false or true
|
|
|
|
## Enabled additional logging noise
|
|
|
|
|
Prep for full sync after snap make 4 (#1282)
* Re-arrange fetching storage slots in batch module
why;
Previously, fetching partial slot ranges first has a chance of
terminating the worker peer 9due to network error) while there were
many inheritable storage slots on the queue.
Now, inheritance is checked first, then full slot ranges and finally
partial ranges.
* Update logging
* Bundled node information for healing into single object `NodeSpecs`
why:
Previously, partial paths and node keys were kept in separate variables.
This approach was error prone due to copying/reassembling function
argument objects.
As all partial paths, keys, and node data types are more or less handled
as `Blob`s over the network (using Eth/6x, or Snap/1) it makes sense to
hold these `Blob`s as named field in a single object (even if not all
fields are active for the current purpose.)
* For good housekeeping, using `NodeKey` type only for account keys
why:
previously, a mixture of `NodeKey` and `Hash256` was used. Now, only
state or storage root keys use the `Hash256` type.
* Always accept latest pivot (and not a slightly older one)
why;
For testing it was tried to use a slightly older pivot state root than
available. Some anecdotal tests seemed to suggest an advantage so that
more peers are willing to serve on that older pivot. But this could not
be confirmed in subsequent tests (still anecdotal, though.)
As a side note, the distance of the latest pivot to its predecessor is
at least 128 (or whatever the constant `minPivotBlockDistance` is
assigned to.)
* Reshuffle name components for some file and function names
why:
Clarifies purpose:
"storages" becomes: "storage slots"
"store" becomes: "range fetch"
* Stash away currently unused modules in sub-folder named "notused"
2022-10-27 13:49:28 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Private logging helpers
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template logTxt(info: static[string]): static[string] =
|
|
|
|
"Accounts range " & info
|
|
|
|
|
2022-12-24 09:54:18 +00:00
|
|
|
proc `$`(rs: NodeTagRangeSet): string =
|
|
|
|
rs.fullFactor.toPC(0)
|
|
|
|
|
|
|
|
proc `$`(iv: NodeTagRange): string =
|
|
|
|
iv.fullFactor.toPC(3)
|
|
|
|
|
|
|
|
proc fetchCtx(
|
|
|
|
buddy: SnapBuddyRef;
|
|
|
|
env: SnapPivotRef;
|
|
|
|
): string =
|
|
|
|
"{" &
|
|
|
|
"pivot=" & "#" & $env.stateHeader.blockNumber & "," &
|
|
|
|
"runState=" & $buddy.ctrl.state & "," &
|
|
|
|
"nStoQu=" & $env.storageQueueTotal() & "," &
|
|
|
|
"nSlotLists=" & $env.nSlotLists & "}"
|
|
|
|
|
2022-10-08 17:20:50 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
2022-11-28 09:03:23 +00:00
|
|
|
# Private helpers
|
2022-10-08 17:20:50 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
2022-11-08 18:56:04 +00:00
|
|
|
proc getUnprocessed(
|
|
|
|
buddy: SnapBuddyRef;
|
|
|
|
env: SnapPivotRef;
|
|
|
|
): Result[NodeTagRange,void] =
|
2022-10-08 17:20:50 +00:00
|
|
|
## Fetch an interval from one of the account range lists.
|
2022-11-08 18:56:04 +00:00
|
|
|
let accountRangeMax = high(UInt256) div buddy.ctx.buddiesMax.u256
|
2022-10-08 17:20:50 +00:00
|
|
|
|
2022-11-08 18:56:04 +00:00
|
|
|
env.fetchAccounts.unprocessed.fetch accountRangeMax
|
2022-10-08 17:20:50 +00:00
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
2022-11-01 15:07:44 +00:00
|
|
|
# Private functions: do the account fetching for one round
|
2022-10-08 17:20:50 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
2022-11-08 18:56:04 +00:00
|
|
|
proc accountsRangefetchImpl(
|
|
|
|
buddy: SnapBuddyRef;
|
|
|
|
env: SnapPivotRef;
|
2022-11-16 23:51:06 +00:00
|
|
|
): Future[bool]
|
|
|
|
{.async.} =
|
2022-11-01 15:07:44 +00:00
|
|
|
## Fetch accounts and store them in the database. Returns true while more
|
|
|
|
## data can probably be fetched.
|
2022-10-08 17:20:50 +00:00
|
|
|
let
|
|
|
|
ctx = buddy.ctx
|
|
|
|
peer = buddy.peer
|
2022-11-28 09:03:23 +00:00
|
|
|
db = ctx.data.snapDb
|
|
|
|
fa = env.fetchAccounts
|
2022-10-08 17:20:50 +00:00
|
|
|
stateRoot = env.stateHeader.stateRoot
|
|
|
|
|
|
|
|
# Get a range of accounts to fetch from
|
|
|
|
let iv = block:
|
2022-11-08 18:56:04 +00:00
|
|
|
let rc = buddy.getUnprocessed(env)
|
2022-10-08 17:20:50 +00:00
|
|
|
if rc.isErr:
|
2022-10-14 16:40:32 +00:00
|
|
|
when extraTraceMessages:
|
2022-12-24 09:54:18 +00:00
|
|
|
trace logTxt "currently all processed", peer, ctx=buddy.fetchCtx(env)
|
2022-10-08 17:20:50 +00:00
|
|
|
return
|
|
|
|
rc.value
|
|
|
|
|
|
|
|
# Process received accounts and stash storage slots to fetch later
|
|
|
|
let dd = block:
|
2022-12-24 09:54:18 +00:00
|
|
|
let
|
|
|
|
pivot = "#" & $env.stateHeader.blockNumber
|
|
|
|
rc = await buddy.getAccountRange(stateRoot, iv, pivot)
|
2022-10-08 17:20:50 +00:00
|
|
|
if rc.isErr:
|
2022-11-28 09:03:23 +00:00
|
|
|
fa.unprocessed.merge iv # fail => interval back to pool
|
2022-10-08 17:20:50 +00:00
|
|
|
let error = rc.error
|
|
|
|
if await buddy.ctrl.stopAfterSeriousComError(error, buddy.data.errors):
|
|
|
|
when extraTraceMessages:
|
2022-12-24 09:54:18 +00:00
|
|
|
trace logTxt "fetch error", peer, ctx=buddy.fetchCtx(env),
|
|
|
|
reqLen=iv.len, error
|
2022-10-08 17:20:50 +00:00
|
|
|
return
|
|
|
|
rc.value
|
|
|
|
|
2022-11-01 15:07:44 +00:00
|
|
|
# Reset error counts for detecting repeated timeouts, network errors, etc.
|
|
|
|
buddy.data.errors.resetComError()
|
|
|
|
|
2022-10-08 17:20:50 +00:00
|
|
|
let
|
2022-12-12 22:00:24 +00:00
|
|
|
gotAccounts = dd.data.accounts.len # comprises `gotStorage`
|
2022-10-19 10:04:06 +00:00
|
|
|
gotStorage = dd.withStorage.len
|
|
|
|
|
2022-11-28 09:03:23 +00:00
|
|
|
# Now, we fully own the scheduler. The original interval will savely be placed
|
|
|
|
# back for a moment (the `unprocessed` range set to be corrected below.)
|
|
|
|
fa.unprocessed.merge iv
|
2022-11-08 18:56:04 +00:00
|
|
|
|
|
|
|
# Processed accounts hashes are set up as a set of intervals which is needed
|
|
|
|
# if the data range returned from the network contains holes.
|
2022-11-28 09:03:23 +00:00
|
|
|
let covered = NodeTagRangeSet.init()
|
2022-11-08 18:56:04 +00:00
|
|
|
if 0 < dd.data.accounts.len:
|
2022-11-28 09:03:23 +00:00
|
|
|
discard covered.merge(iv.minPt, dd.data.accounts[^1].accKey.to(NodeTag))
|
2022-11-08 18:56:04 +00:00
|
|
|
else:
|
2022-11-28 09:03:23 +00:00
|
|
|
discard covered.merge iv
|
2022-11-08 18:56:04 +00:00
|
|
|
|
2022-11-16 23:51:06 +00:00
|
|
|
let gaps = block:
|
2022-11-08 18:56:04 +00:00
|
|
|
# No left boundary check needed. If there is a gap, the partial path for
|
|
|
|
# that gap is returned by the import function to be registered, below.
|
2022-11-28 09:03:23 +00:00
|
|
|
let rc = db.importAccounts(
|
2022-11-08 18:56:04 +00:00
|
|
|
peer, stateRoot, iv.minPt, dd.data, noBaseBoundCheck = true)
|
2022-10-08 17:20:50 +00:00
|
|
|
if rc.isErr:
|
|
|
|
# Bad data, just try another peer
|
|
|
|
buddy.ctrl.zombie = true
|
|
|
|
when extraTraceMessages:
|
2022-12-24 09:54:18 +00:00
|
|
|
trace logTxt "import failed", peer, ctx=buddy.fetchCtx(env),
|
|
|
|
gotAccounts, gotStorage, reqLen=iv.len, covered, error=rc.error
|
2022-10-08 17:20:50 +00:00
|
|
|
return
|
2022-11-08 18:56:04 +00:00
|
|
|
rc.value
|
2022-10-08 17:20:50 +00:00
|
|
|
|
|
|
|
# Statistics
|
2022-10-19 10:04:06 +00:00
|
|
|
env.nAccounts.inc(gotAccounts)
|
2022-10-08 17:20:50 +00:00
|
|
|
|
2022-11-25 14:56:42 +00:00
|
|
|
# Punch holes into the reported range of received accounts from the network
|
|
|
|
# if it there are gaps (described by dangling nodes.)
|
2022-11-16 23:51:06 +00:00
|
|
|
for w in gaps.innerGaps:
|
2022-12-06 17:35:56 +00:00
|
|
|
discard covered.reduce w.partialPath.hexaryEnvelope
|
2022-11-16 23:51:06 +00:00
|
|
|
|
2022-11-08 18:56:04 +00:00
|
|
|
# Update book keeping
|
2022-11-28 09:03:23 +00:00
|
|
|
for w in covered.increasing:
|
2022-11-08 18:56:04 +00:00
|
|
|
# Remove the processed range from the batch of unprocessed ones.
|
2022-11-28 09:03:23 +00:00
|
|
|
fa.unprocessed.reduce w
|
|
|
|
# Register consumed intervals on the accumulators over all state roots.
|
|
|
|
discard fa.processed.merge w
|
2023-01-17 09:28:14 +00:00
|
|
|
discard ctx.data.coveredAccounts.merge w
|
|
|
|
ctx.pivotAccountsCoverage100PcRollOver() # update coverage level roll over
|
2022-11-08 18:56:04 +00:00
|
|
|
|
|
|
|
# Register accounts with storage slots on the storage TODO list.
|
2022-12-24 09:54:18 +00:00
|
|
|
env.storageQueueAppend dd.withStorage
|
2022-11-08 18:56:04 +00:00
|
|
|
|
2022-12-24 09:54:18 +00:00
|
|
|
# Swap in from other pivots unless mothballed, already
|
2022-12-12 22:00:24 +00:00
|
|
|
var nSwapInLaps = 0
|
2022-12-24 09:54:18 +00:00
|
|
|
if not env.archived:
|
2022-12-12 22:00:24 +00:00
|
|
|
when extraTraceMessages:
|
2022-12-24 09:54:18 +00:00
|
|
|
trace logTxt "before swap in", peer, ctx=buddy.fetchCtx(env), covered,
|
|
|
|
gotAccounts, gotStorage, processed=fa.processed,
|
2022-12-12 22:00:24 +00:00
|
|
|
nProcessedChunks=fa.processed.chunks.uint.toSI
|
|
|
|
|
2022-12-24 09:54:18 +00:00
|
|
|
nSwapInLaps = ctx.swapInAccounts env
|
2022-12-12 22:00:24 +00:00
|
|
|
|
2022-11-28 09:03:23 +00:00
|
|
|
when extraTraceMessages:
|
2022-12-24 09:54:18 +00:00
|
|
|
trace logTxt "request done", peer, ctx=buddy.fetchCtx(env), gotAccounts,
|
|
|
|
gotStorage, nSwapInLaps, covered, processed=fa.processed,
|
2022-12-12 22:00:24 +00:00
|
|
|
nProcessedChunks=fa.processed.chunks.uint.toSI
|
2022-10-08 17:20:50 +00:00
|
|
|
|
2022-11-01 15:07:44 +00:00
|
|
|
return true
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public functions
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
2022-11-16 23:51:06 +00:00
|
|
|
proc rangeFetchAccounts*(
|
|
|
|
buddy: SnapBuddyRef;
|
|
|
|
env: SnapPivotRef;
|
|
|
|
) {.async.} =
|
2022-11-01 15:07:44 +00:00
|
|
|
## Fetch accounts and store them in the database.
|
2022-11-28 09:03:23 +00:00
|
|
|
let
|
|
|
|
fa = env.fetchAccounts
|
|
|
|
|
|
|
|
if not fa.processed.isFull():
|
2022-11-01 15:07:44 +00:00
|
|
|
let
|
|
|
|
ctx = buddy.ctx
|
|
|
|
peer = buddy.peer
|
|
|
|
|
|
|
|
when extraTraceMessages:
|
2022-12-24 09:54:18 +00:00
|
|
|
trace logTxt "start", peer, ctx=buddy.fetchCtx(env)
|
2022-11-01 15:07:44 +00:00
|
|
|
|
2022-11-28 09:03:23 +00:00
|
|
|
var nFetchAccounts = 0 # for logging
|
|
|
|
while not fa.processed.isFull() and
|
2022-11-08 18:56:04 +00:00
|
|
|
buddy.ctrl.running and
|
2022-12-12 22:00:24 +00:00
|
|
|
not env.archived:
|
2022-11-01 15:07:44 +00:00
|
|
|
nFetchAccounts.inc
|
2022-11-08 18:56:04 +00:00
|
|
|
if not await buddy.accountsRangefetchImpl(env):
|
|
|
|
break
|
|
|
|
|
|
|
|
# Clean up storage slots queue first it it becomes too large
|
|
|
|
let nStoQu = env.fetchStorageFull.len + env.fetchStoragePart.len
|
2022-12-24 09:54:18 +00:00
|
|
|
if storageSlotsQuPrioThresh < nStoQu:
|
2022-11-08 18:56:04 +00:00
|
|
|
break
|
2022-11-01 15:07:44 +00:00
|
|
|
|
|
|
|
when extraTraceMessages:
|
2022-12-24 09:54:18 +00:00
|
|
|
trace logTxt "done", peer, ctx=buddy.fetchCtx(env), nFetchAccounts
|
2022-10-08 17:20:50 +00:00
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# End
|
|
|
|
# ------------------------------------------------------------------------------
|