155 lines
4.5 KiB
Nim
155 lines
4.5 KiB
Nim
# Nimbus
|
|
# Copyright (c) 2018 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.
|
|
|
|
## LRU Cache for Epoch Indexed Hashimoto Dataset
|
|
## =============================================
|
|
##
|
|
## This module uses the eth-block number (mapped to epoch) to hold and re-use
|
|
## the dataset needed for running the `hasimotoFull()` proof-of-work function.
|
|
|
|
import
|
|
std/[options],
|
|
./pow_cache,
|
|
eth/common,
|
|
ethash,
|
|
stew/keyed_queue
|
|
|
|
{.push raises: [Defect].}
|
|
|
|
type
|
|
PowDatasetItemRef* = ref object
|
|
size*: uint64
|
|
data*: seq[MDigest[512]]
|
|
|
|
PowDatasetStats* = tuple
|
|
maxItems: int
|
|
size: int
|
|
|
|
PowDataset* = object
|
|
datasetMax: int
|
|
dataset: KeyedQueue[uint64,PowDatasetItemRef]
|
|
cache: PowCacheRef
|
|
|
|
PowDatasetRef* = ref PowDataset
|
|
|
|
const
|
|
nItemsMax = 2
|
|
nItemsInit = 2
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Private helpers
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc toKey(bn: BlockNumber): uint64 =
|
|
bn.truncate(uint64) div EPOCH_LENGTH
|
|
|
|
proc init(pd: var PowDataset;
|
|
maxItems: Option[int]; cache: Option[PowCacheRef]) =
|
|
## Constructor for LRU cache
|
|
pd.dataset.init(nItemsInit)
|
|
|
|
if maxItems.isSome:
|
|
pd.datasetMax = maxItems.get
|
|
else:
|
|
pd.datasetMax = nItemsMax
|
|
|
|
if cache.isSome:
|
|
pd.cache = cache.get
|
|
else:
|
|
pd.cache = PowCacheRef.new(nItemsInit)
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public functions, constructor
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc init*(pd: var PowDataset; maxItems = nItemsMax; cache: PowCacheRef) =
|
|
## Constructor for PoW dataset
|
|
pd.init(some(maxItems), some(cache))
|
|
|
|
proc init*(pd: var PowDataset; maxItems = nItemsMax) =
|
|
## Constructor variant
|
|
pd.init(some(maxItems), none(PowCacheRef))
|
|
|
|
|
|
proc init*(T: type PowDataset; maxItems = nItemsMax; cache: PowCacheRef): T =
|
|
## Constructor variant
|
|
result.init(some(maxItems), some(cache))
|
|
|
|
proc init*(T: type PowDataset; maxItems = nItemsMax): T =
|
|
## Constructor variant
|
|
result.init(some(maxItems), none(PowCacheRef))
|
|
|
|
|
|
proc new*(T: type PowDatasetRef; maxItems = nItemsMax; cache: PowCacheRef): T =
|
|
## Constructor variant
|
|
new result
|
|
result[].init(some(maxItems), some(cache))
|
|
|
|
proc new*(T: type PowDatasetRef; maxItems = nItemsMax): T =
|
|
## Constructor for PoW dataset reference
|
|
new result
|
|
result[].init(some(maxItems), none(PowCacheRef))
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public functions, constructor
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc get*(pd: var PowDataset; bn: BlockNumber): PowDatasetItemRef
|
|
{.gcsafe,raises: [Defect,CatchableError].} =
|
|
## Return a cache derived from argument `blockNumber` ready to be used
|
|
## for the `hashimotoLight()` method.
|
|
let
|
|
key = bn.toKey
|
|
rc = pd.dataset.lruFetch(key)
|
|
|
|
if rc.isOk:
|
|
return rc.value
|
|
|
|
let
|
|
# note that `getDataSize()` and `getCacheSize()` depend on
|
|
# `key * EPOCH_LENGTH` rather than the original block number.
|
|
top = key * EPOCH_LENGTH
|
|
cache = pd.cache.get(bn)
|
|
pair = PowDatasetItemRef(
|
|
size: cache.size,
|
|
data: cache.size.calcDataset(cache.data))
|
|
|
|
pd.dataset.lruAppend(key, pair, pd.datasetMax)
|
|
|
|
proc get*(pdr: PowDatasetRef; bn: BlockNumber): PowDatasetItemRef
|
|
{.gcsafe,raises: [Defect,CatchableError].} =
|
|
## Variant of `getCache()`
|
|
pdr[].get(bn)
|
|
|
|
|
|
proc hasItem*(pd: var PowDataset; bn: BlockNumber): bool
|
|
{.gcsafe,raises: [Defect,CatchableError].} =
|
|
##Returns true if there is a cache entry for argument `bn`.
|
|
pd.dataset.hasKey(bn.toKey)
|
|
|
|
proc hasItem*(pdr: PowDatasetRef; bn: BlockNumber): bool
|
|
{.gcsafe,raises: [Defect,CatchableError].} =
|
|
## Variant of `hasItem()`
|
|
pdr[].hasItem(bn)
|
|
|
|
# -------------------------
|
|
|
|
proc stats*(pd: var PowDataset): PowDatasetStats =
|
|
## Return current cache sizes
|
|
result = (maxItems: pd.datasetMax, size: pd.dataset.len)
|
|
|
|
proc stats*(pd: PowDatasetRef): PowDatasetStats =
|
|
## Variant of `stats()`
|
|
pd[].stats
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# End
|
|
# ------------------------------------------------------------------------------
|