178 lines
6.0 KiB
Nim
178 lines
6.0 KiB
Nim
|
# 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.
|
||
|
|
||
|
## Fetch accounts stapshot
|
||
|
## =======================
|
||
|
##
|
||
|
## Worker items state diagram:
|
||
|
## ::
|
||
|
## unprocessed slot requests | peer workers + storages database update
|
||
|
## ===================================================================
|
||
|
##
|
||
|
## +-----------------------------------------------+
|
||
|
## | |
|
||
|
## v |
|
||
|
## <unprocessed> ------------+-------> <worker-0> ------+
|
||
|
## | |
|
||
|
## +-------> <worker-1> ------+
|
||
|
## | |
|
||
|
## +-------> <worker-2> ------+
|
||
|
## : :
|
||
|
##
|
||
|
|
||
|
import
|
||
|
chronicles,
|
||
|
chronos,
|
||
|
eth/[common/eth_types, p2p],
|
||
|
stew/keyed_queue,
|
||
|
stint,
|
||
|
../../sync_desc,
|
||
|
".."/[range_desc, worker_desc],
|
||
|
./com/[com_error, get_storage_ranges],
|
||
|
./db/snap_db
|
||
|
|
||
|
{.push raises: [Defect].}
|
||
|
|
||
|
logScope:
|
||
|
topics = "snap-fetch"
|
||
|
|
||
|
const
|
||
|
extraTraceMessages = false or true
|
||
|
## Enabled additional logging noise
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
# Private functions
|
||
|
# ------------------------------------------------------------------------------
|
||
|
|
||
|
proc getNextSlotItem(buddy: SnapBuddyRef): Result[SnapSlotQueueItemRef,void] =
|
||
|
let env = buddy.data.pivotEnv
|
||
|
for w in env.fetchStorage.nextKeys:
|
||
|
# Make sure that this item was not fetched and rejected earlier
|
||
|
if w notin buddy.data.vetoSlots:
|
||
|
env.fetchStorage.del(w)
|
||
|
return ok(w)
|
||
|
err()
|
||
|
|
||
|
proc fetchAndImportStorageSlots(
|
||
|
buddy: SnapBuddyRef;
|
||
|
reqSpecs: seq[AccountSlotsHeader];
|
||
|
): Future[Result[seq[SnapSlotQueueItemRef],ComError]]
|
||
|
{.async.} =
|
||
|
## Fetch storage slots data from the network, store it on disk and
|
||
|
## return data to process in the next cycle.
|
||
|
let
|
||
|
ctx = buddy.ctx
|
||
|
peer = buddy.peer
|
||
|
env = buddy.data.pivotEnv
|
||
|
stateRoot = env.stateHeader.stateRoot
|
||
|
|
||
|
# Get storage slots
|
||
|
var stoRange = block:
|
||
|
let rc = await buddy.getStorageRanges(stateRoot, reqSpecs)
|
||
|
if rc.isErr:
|
||
|
return err(rc.error)
|
||
|
rc.value
|
||
|
|
||
|
if 0 < stoRange.data.storages.len:
|
||
|
# Verify/process data and save to disk
|
||
|
block:
|
||
|
let rc = ctx.data.snapDb.importStorages(peer, stoRange.data)
|
||
|
|
||
|
if rc.isErr:
|
||
|
# Push back parts of the error item
|
||
|
var once = false
|
||
|
for w in rc.error:
|
||
|
if 0 <= w[0]:
|
||
|
# Reset any partial requests by not copying the `firstSlot` field.
|
||
|
# So all the storage slots are re-fetched completely for this
|
||
|
# account.
|
||
|
stoRange.addLeftOver(
|
||
|
@[AccountSlotsHeader(
|
||
|
accHash: stoRange.data.storages[w[0]].account.accHash,
|
||
|
storageRoot: stoRange.data.storages[w[0]].account.storageRoot)],
|
||
|
forceNew = not once)
|
||
|
once = true
|
||
|
# Do not ask for the same entries again on this `peer`
|
||
|
if once:
|
||
|
buddy.data.vetoSlots.incl stoRange.leftOver[^1]
|
||
|
|
||
|
if rc.error[^1][0] < 0:
|
||
|
discard
|
||
|
# TODO: disk storage failed or something else happend, so what?
|
||
|
|
||
|
# Return the remaining part to be processed later
|
||
|
return ok(stoRange.leftOver)
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
# Public functions
|
||
|
# ------------------------------------------------------------------------------
|
||
|
|
||
|
proc storeStorages*(buddy: SnapBuddyRef) {.async.} =
|
||
|
## Fetch account storage slots and store them in the database.
|
||
|
let
|
||
|
ctx = buddy.ctx
|
||
|
peer = buddy.peer
|
||
|
env = buddy.data.pivotEnv
|
||
|
stateRoot = env.stateHeader.stateRoot
|
||
|
var
|
||
|
once = true # for logging
|
||
|
|
||
|
# Fetch storage data and save it on disk. Storage requests are managed by
|
||
|
# a request queue for handling partioal replies and re-fetch issues. For
|
||
|
# all practical puroses, this request queue should mostly be empty.
|
||
|
while true:
|
||
|
# Pull out the next request item from the queue
|
||
|
let req = block:
|
||
|
let rc = buddy.getNextSlotItem()
|
||
|
if rc.isErr:
|
||
|
return # currently nothing to do
|
||
|
rc.value
|
||
|
|
||
|
when extraTraceMessages:
|
||
|
if once:
|
||
|
once = false
|
||
|
let nAccounts = 1 + env.fetchStorage.len
|
||
|
trace "Start fetching storage slotss", peer,
|
||
|
nAccounts, nVetoSlots=buddy.data.vetoSlots.len
|
||
|
|
||
|
block:
|
||
|
# Fetch and store account storage slots. On success, the `rc` value will
|
||
|
# contain a list of left-over items to be re-processed.
|
||
|
let rc = await buddy.fetchAndImportStorageSlots(req.q)
|
||
|
if rc.isErr:
|
||
|
# Save accounts/storage list to be processed later, then stop
|
||
|
discard env.fetchStorage.append req
|
||
|
let error = rc.error
|
||
|
if await buddy.ctrl.stopAfterSeriousComError(error, buddy.data.errors):
|
||
|
trace "Error fetching storage slots => stop", peer, error
|
||
|
discard
|
||
|
return
|
||
|
|
||
|
# Reset error counts for detecting repeated timeouts
|
||
|
buddy.data.errors.nTimeouts = 0
|
||
|
|
||
|
for qLo in rc.value:
|
||
|
# Handle queue left-overs for processing in the next cycle
|
||
|
if qLo.q[0].firstSlot == Hash256() and 0 < env.fetchStorage.len:
|
||
|
# Appending to last queue item is preferred over adding new item
|
||
|
let item = env.fetchStorage.first.value
|
||
|
item.q = item.q & qLo.q
|
||
|
else:
|
||
|
# Put back as-is.
|
||
|
discard env.fetchStorage.append qLo
|
||
|
# End while
|
||
|
|
||
|
when extraTraceMessages:
|
||
|
trace "Done fetching storage slots", peer, nAccounts=env.fetchStorage.len
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
# End
|
||
|
# ------------------------------------------------------------------------------
|