nimbus-eth1/nimbus/db/era1_db/db_utils.nim

167 lines
4.8 KiB
Nim

# Nimbus
# Copyright (c) 2021-2024 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.
{.push raises: [].}
import
std/os,
eth/common,
stew/[interval_set, keyed_queue, sorted_set],
../../fluffy/eth_data/era1,
./db_desc
export
BlockTuple
# ------------------------------------------------------------------------------
# Private helpers
# ------------------------------------------------------------------------------
proc getEra1DbBlocks(
db: Era1DbRef;
bn: uint64;
): Result[SortedSetItemRef[uint64,Era1DbBlocks],void] =
## Get item referring to particular `era1` file
let w = db.byBlkNum.le(bn).valueOr:
return err()
if w.key + w.data.nBlocks <= bn:
return err()
ok(w)
proc deleteEra1DbBlocks(
db: Era1DbRef;
it: SortedSetItemRef[uint64,Era1DbBlocks];
) =
## Remove `era1` file index descriptor from list and LRU table
discard db.byBlkNum.delete it.key
db.blocks.del it.key
discard db.ranges.reduce(it.key, it.data.nBlocks-1)
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
proc hasAllKeys*(db: Era1DbRef, first, last: uint64): bool =
if first <= last:
db.ranges.covered(first, last) == last - first + 1
else:
false
proc hasSomeKey*(db: Era1DbRef, first, last: uint64): bool =
if first <= last:
0 < db.ranges.covered(first, last)
else:
false
proc hasKey*(db: Era1DbRef, key: uint64): bool =
0 < db.ranges.covered(key, key)
proc fetch*(
db: Era1DbRef;
blockNumber: uint64;
updateInxOnError = true;
): Result[BlockTuple,string] =
## Fetch block data for argument height `blockNumber`. If `updateInxOnError`
## is set `true` (which is the default), a data file that cannot be opened
## anymore will be ignored in future.
##
let blkDsc = db.getEra1DbBlocks(blockNumber).valueOr:
return err("")
# Get `era1` file index descriptor
let dsc = block:
let rc = db.blocks.lruFetch(blkDsc.key)
if rc.isOk:
rc.value
else:
let w = Era1File.open(db.dir / blkDsc.data.fileName).valueOr:
if updateInxOnError:
db.deleteEra1DbBlocks blkDsc
return err("") # no way
while NumOpenEra1DbBlocks <= db.blocks.len:
db.blocks.shift.value.data.close() # unqueue first/least item
discard db.blocks.lruAppend(blkDsc.key, w, NumOpenEra1DbBlocks)
w
# Fetch the result via `dsc`
dsc.getBlockTuple(blockNumber)
proc clearInx*(db: Era1DbRef, blockNumber: uint64): bool {.discardable.} =
## Remove the `era1` block containing `blockNumber` from index. This might
## be useful after rejection the block contents for height `blockNumber`.
##
## The function returns true if the index was found and could be cleared.
##
let blkDsc = db.getEra1DbBlocks(blockNumber).valueOr:
return false
db.deleteEra1DbBlocks blkDsc
true
# -----------------
iterator blockRanges*(db: Era1DbRef): tuple[startBlock,endBlock: uint64] =
for w in db.ranges.increasing:
yield (w.minPt, w.maxPt)
iterator headerBodyPairs*(
db: Era1DbRef;
firstBlockNumber = 0u64;
maxBlockNumber = high(uint64);
blocksPerUnit = 192;
): (seq[BlockHeader],seq[BlockBody]) =
## Provide blocks until there are no more or the block number exceeds
## `maxBlockNumber`.
##
let uSize = blocksPerUnit.uint64
var left = 1u64
block yieldBody:
if 0 < firstBlockNumber:
left = firstBlockNumber
elif db.hasKey(0):
# Zero block (aka genesis)
let tp = db.fetch(0).expect "valid genesis"
yield(@[tp.header],@[tp.body])
else:
break yieldBody # no way
# Full block ranges
while left+uSize < maxBlockNumber and db.hasAllKeys(left,left+uSize-1):
var
hdr: seq[BlockHeader]
bdy: seq[BlockBody]
for bn in left ..< left+uSize:
let tp = db.fetch(bn).expect "valid block tuple"
hdr.add tp.header
bdy.add tp.body
yield(hdr,bdy)
left += uSize
# Final range (if any)
block:
var
hdr: seq[BlockHeader]
bdy: seq[BlockBody]
while left <= maxBlockNumber and db.hasKey(left):
let tp = db.fetch(left).expect "valid block tuple"
hdr.add tp.header
bdy.add tp.body
left.inc
if 0 < hdr.len:
yield(hdr,bdy)
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------