mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-02-03 07:45:18 +00:00
use general lru_cache for EpochHashCache
why: generic implementation will be also be used elsewhere
This commit is contained in:
parent
3663b1603f
commit
a5e0bb6139
@ -17,7 +17,7 @@ import
|
||||
../vm_state,
|
||||
../vm_types,
|
||||
../vm_types2,
|
||||
./validate/cache,
|
||||
./validate/epoch_hash_cache,
|
||||
chronicles,
|
||||
eth/[common, rlp, trie/trie_defs],
|
||||
ethash,
|
||||
@ -30,8 +30,8 @@ import
|
||||
times
|
||||
|
||||
export
|
||||
cache.EpochHashCache,
|
||||
cache.initEpochHashCache,
|
||||
epoch_hash_cache.EpochHashCache,
|
||||
epoch_hash_cache.initEpochHashCache,
|
||||
results
|
||||
|
||||
type
|
||||
@ -99,10 +99,10 @@ func cacheHash(x: EpochHashDigest): Hash256 =
|
||||
|
||||
proc checkPOW(blockNumber: Uint256; miningHash, mixHash: Hash256;
|
||||
nonce: BlockNonce; difficulty: DifficultyInt;
|
||||
cacheByEpoch: var EpochHashCache): Result[void,string] =
|
||||
hashCache: var EpochHashCache): Result[void,string] =
|
||||
let
|
||||
blockNumber = blockNumber.truncate(uint64)
|
||||
cache = cacheByEpoch.getEpochCacheHash(blockNumber)
|
||||
cache = hashCache.getEpochHash(blockNumber)
|
||||
size = getDataSize(blockNumber)
|
||||
miningOutput = hashimotoLight(
|
||||
size, cache, miningHash, uint64.fromBytesBE(nonce))
|
||||
@ -126,13 +126,13 @@ proc checkPOW(blockNumber: Uint256; miningHash, mixHash: Hash256;
|
||||
result = ok()
|
||||
|
||||
|
||||
proc validateSeal(cacheByEpoch: var EpochHashCache;
|
||||
proc validateSeal(hashCache: var EpochHashCache;
|
||||
header: BlockHeader): Result[void,string] =
|
||||
let miningHeader = header.toMiningHeader
|
||||
let miningHash = miningHeader.hash
|
||||
|
||||
checkPOW(header.blockNumber, miningHash,
|
||||
header.mixDigest, header.nonce, header.difficulty, cacheByEpoch)
|
||||
header.mixDigest, header.nonce, header.difficulty, hashCache)
|
||||
|
||||
|
||||
proc validateGasLimit(chainDB: BaseChainDB;
|
||||
@ -161,7 +161,7 @@ func validateGasLimit(gasLimit, parentGasLimit: GasInt): Result[void,string] =
|
||||
result = ok()
|
||||
|
||||
proc validateHeader(header, parentHeader: BlockHeader; checkSealOK: bool;
|
||||
cacheByEpoch: var EpochHashCache): Result[void,string] =
|
||||
hashCache: var EpochHashCache): Result[void,string] =
|
||||
if header.extraData.len > 32:
|
||||
return err("BlockHeader.extraData larger than 32 bytes")
|
||||
|
||||
@ -176,7 +176,7 @@ proc validateHeader(header, parentHeader: BlockHeader; checkSealOK: bool;
|
||||
return err("timestamp must be strictly later than parent")
|
||||
|
||||
if checkSealOK:
|
||||
return cacheByEpoch.validateSeal(header)
|
||||
return hashCache.validateSeal(header)
|
||||
|
||||
result = ok()
|
||||
|
||||
@ -200,7 +200,7 @@ func validateUncle(currBlock, uncle, uncleParent: BlockHeader):
|
||||
|
||||
proc validateUncles(chainDB: BaseChainDB; header: BlockHeader;
|
||||
uncles: seq[BlockHeader]; checkSealOK: bool;
|
||||
cacheByEpoch: var EpochHashCache): Result[void,string] =
|
||||
hashCache: var EpochHashCache): Result[void,string] =
|
||||
let hasUncles = uncles.len > 0
|
||||
let shouldHaveUncles = header.ommersHash != EMPTY_UNCLE_HASH
|
||||
|
||||
@ -249,7 +249,7 @@ proc validateUncles(chainDB: BaseChainDB; header: BlockHeader;
|
||||
|
||||
# Now perform VM level validation of the uncle
|
||||
if checkSealOK:
|
||||
result = cacheByEpoch.validateSeal(uncle)
|
||||
result = hashCache.validateSeal(uncle)
|
||||
if result.isErr:
|
||||
return
|
||||
|
||||
@ -310,14 +310,14 @@ proc validateTransaction*(vmState: BaseVMState, tx: Transaction,
|
||||
|
||||
proc validateKinship*(chainDB: BaseChainDB; header: BlockHeader;
|
||||
uncles: seq[BlockHeader]; checkSealOK: bool;
|
||||
cacheByEpoch: var EpochHashCache): Result[void,string] =
|
||||
hashCache: var EpochHashCache): Result[void,string] =
|
||||
if header.isGenesis:
|
||||
if header.extraData.len > 32:
|
||||
return err("BlockHeader.extraData larger than 32 bytes")
|
||||
return ok()
|
||||
|
||||
let parentHeader = chainDB.getBlockHeader(header.parentHash)
|
||||
result = header.validateHeader(parentHeader, checkSealOK, cacheByEpoch)
|
||||
result = header.validateHeader(parentHeader, checkSealOK, hashCache)
|
||||
if result.isErr:
|
||||
return
|
||||
|
||||
@ -327,7 +327,7 @@ proc validateKinship*(chainDB: BaseChainDB; header: BlockHeader;
|
||||
if not chainDB.exists(header.stateRoot):
|
||||
return err("`state_root` was not found in the db.")
|
||||
|
||||
result = chainDB.validateUncles(header, uncles, checkSealOK, cacheByEpoch)
|
||||
result = chainDB.validateUncles(header, uncles, checkSealOK, hashCache)
|
||||
if result.isOk:
|
||||
result = chainDB.validateGaslimit(header)
|
||||
|
||||
|
@ -1,75 +0,0 @@
|
||||
# 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.
|
||||
|
||||
## Hash as hash can
|
||||
## ================
|
||||
##
|
||||
## provide hash lists, indexed by epoch
|
||||
|
||||
import
|
||||
ethash,
|
||||
nimcrypto,
|
||||
tables
|
||||
|
||||
type
|
||||
EpochHashDigest* = seq[MDigest[512]]
|
||||
|
||||
EpochHashCache* = object
|
||||
maxItems: int ## max number of entries
|
||||
tab: OrderedTable[uint64,EpochHashDigest] ## cache data table
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Private cache management functions
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc mkCacheBytes(blockNumber: uint64): seq[MDigest[512]] {.inline.} =
|
||||
mkcache(getCacheSize(blockNumber), getSeedhash(blockNumber))
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public functions
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc initEpochHashCache*(cache: var EpochHashCache; cacheMaxItems = 10) =
|
||||
## Initialise a new cache indexed by block epoch
|
||||
cache.maxItems = cacheMaxItems
|
||||
|
||||
# note: Starting from Nim v0.20, tables are initialized by default and it
|
||||
# is not necessary to call initOrderedTable() function explicitly.
|
||||
|
||||
|
||||
proc getEpochCacheHash*(cache: var EpochHashCache;
|
||||
blockNumber: uint64): EpochHashDigest =
|
||||
## Return hash list, indexed by epoch of argument `blockNumber`
|
||||
let epochIndex = blockNumber div EPOCH_LENGTH
|
||||
|
||||
# Get the cache if already generated, marking it as recently used
|
||||
if epochIndex in cache.tab:
|
||||
let value = cache.tab[epochIndex]
|
||||
cache.tab.del(epochIndex) # pop and append at end
|
||||
cache.tab[epochIndex] = value
|
||||
return value
|
||||
|
||||
# Limit memory usage for cache
|
||||
if cache.maxItems <= cache.tab.len:
|
||||
# Delete oldest entry
|
||||
for key in cache.tab.keys:
|
||||
# Kludge: OrderedTable[] still misses a proper API
|
||||
cache.tab.del(key)
|
||||
break
|
||||
|
||||
# Simulate requesting mkcache by block number: multiply index by epoch length
|
||||
var data = mkCacheBytes(epochIndex * EPOCH_LENGTH)
|
||||
cache.tab[epochIndex] = data
|
||||
|
||||
result = system.move(data)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
66
nimbus/p2p/validate/epoch_hash_cache.nim
Normal file
66
nimbus/p2p/validate/epoch_hash_cache.nim
Normal file
@ -0,0 +1,66 @@
|
||||
# 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.
|
||||
|
||||
## Hash Cache
|
||||
## ==========
|
||||
##
|
||||
## provide LRU hash, indexed by epoch
|
||||
|
||||
import
|
||||
../../utils/lru_cache,
|
||||
ethash,
|
||||
nimcrypto,
|
||||
tables
|
||||
|
||||
type
|
||||
BlockEpoch = distinct uint64
|
||||
|
||||
EpochHashDigest* = seq[MDigest[512]]
|
||||
EpochHashCache* = LruCache[uint64,BlockEpoch,EpochHashDigest,void]
|
||||
|
||||
{.push raises: [Defect,CatchableError].}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Private cache management functions
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# needed for table key to work
|
||||
proc `==`(a,b: BlockEpoch): bool {.borrow.}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public functions
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc initEpochHashCache*(cache: var EpochHashCache; cacheMaxItems = 10) =
|
||||
## Initialise a new cache indexed by block epoch
|
||||
|
||||
template bnToEpoch(num: uint64): BlockEpoch =
|
||||
BlockEpoch(blockNumber div EPOCH_LENGTH)
|
||||
|
||||
var toKey: LruKey[uint64,BlockEpoch] =
|
||||
proc(blockNumber: uint64): BlockEpoch =
|
||||
blockNumber.bnToEpoch
|
||||
|
||||
var toValue: LruValue[uint64,EpochHashDigest,void] =
|
||||
proc(blockNumber: uint64): Result[EpochHashDigest,void] =
|
||||
let top = blockNumber.bnToEpoch.uint64 * EPOCH_LENGTH
|
||||
ok( mkcache( getCacheSize(top), getSeedhash(top)))
|
||||
|
||||
cache.initLruCache(toKey, toValue, cacheMaxItems)
|
||||
|
||||
|
||||
proc getEpochHash*(cache: var EpochHashCache;
|
||||
blockNumber: uint64): auto {.inline.} =
|
||||
## Return hash list, indexed by epoch of argument `blockNumber`
|
||||
cache.getLruItem(blockNumber).value
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
135
nimbus/utils/lru_cache.nim
Normal file
135
nimbus/utils/lru_cache.nim
Normal file
@ -0,0 +1,135 @@
|
||||
# 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.
|
||||
|
||||
## Hash as hash can: LRU cache
|
||||
## ===========================
|
||||
##
|
||||
## provide last-recently-used cache mapper
|
||||
|
||||
const
|
||||
# debugging, enable with: nim c -r -d:noisy:3 ...
|
||||
noisy {.intdefine.}: int = 0
|
||||
isMainOk {.used.} = noisy > 2
|
||||
|
||||
import
|
||||
stew/results,
|
||||
tables
|
||||
|
||||
export
|
||||
results
|
||||
|
||||
type
|
||||
LruKey*[T,K] = ## derive an LRU key from function argument
|
||||
proc(arg: T): K {.gcsafe, raises: [Defect,CatchableError].}
|
||||
|
||||
LruValue*[T,V,E] = ## derive an LRU value from function argument
|
||||
proc(arg: T): Result[V,E] {.gcsafe, raises: [Defect,CatchableError].}
|
||||
|
||||
LruCache*[T,K,V,E] = object
|
||||
maxItems: int ## max number of entries
|
||||
tab: OrderedTable[K,V] ## cache data table
|
||||
toKey: LruKey[T,K]
|
||||
toValue: LruValue[T,V,E]
|
||||
|
||||
{.push raises: [Defect,CatchableError].}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public functions
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc initLruCache*[T,K,V,E](cache: var LruCache[T,K,V,E];
|
||||
toKey: LruKey[T,K], toValue: LruValue[T,V,E];
|
||||
cacheMaxItems = 10) =
|
||||
## Initialise new LRU cache
|
||||
cache.maxItems = cacheMaxItems
|
||||
cache.toKey = toKey
|
||||
cache.toValue = toValue
|
||||
# note: Starting from Nim v0.20, tables are initialized by default and it
|
||||
# is not necessary to call initOrderedTable() function explicitly.
|
||||
|
||||
|
||||
proc getLruItem*[T,K,V,E](cache: var LruCache[T,K,V,E]; arg: T): Result[V,E] =
|
||||
## Return `toValue(arg)`, preferably from result cached earlier
|
||||
let key = cache.toKey(arg)
|
||||
|
||||
# Get the cache if already generated, marking it as recently used
|
||||
if cache.tab.hasKey(key):
|
||||
let value = cache.tab[key]
|
||||
# Pop and append at end. Note that according to manual, this
|
||||
# costs O(n) => inefficient
|
||||
cache.tab.del(key)
|
||||
cache.tab[key] = value
|
||||
return ok(value)
|
||||
|
||||
# Return unless OK
|
||||
let rcValue = ? cache.toValue(arg)
|
||||
|
||||
# Limit mumer of cached items
|
||||
if cache.maxItems <= cache.tab.len:
|
||||
# Delete oldest/first entry
|
||||
var tbd: K
|
||||
# Kludge: OrderedTable[] still misses a proper API.
|
||||
for key in cache.tab.keys:
|
||||
# Tests suggest that deleting here also works in that particular case.
|
||||
tbd = key
|
||||
break
|
||||
cache.tab.del(tbd)
|
||||
|
||||
# Add cache entry
|
||||
cache.tab[key] = rcValue
|
||||
result = ok(rcValue)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Debugging/testing
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
when isMainModule and isMainOK:
|
||||
|
||||
import
|
||||
sequtils
|
||||
|
||||
const
|
||||
cacheLimit = 10
|
||||
keyList = [
|
||||
185, 208, 53, 54, 196, 189, 187, 117, 94, 29, 6, 173, 207, 45, 31,
|
||||
208, 127, 106, 117, 49, 40, 171, 6, 94, 84, 60, 125, 87, 168, 183,
|
||||
200, 155, 34, 27, 67, 107, 108, 223, 249, 4, 113, 9, 205, 100, 77,
|
||||
224, 19, 196, 14, 83, 145, 154, 95, 56, 236, 97, 115, 140, 134, 97,
|
||||
153, 167, 23, 17, 182, 116, 253, 32, 108, 148, 135, 169, 178, 124, 147,
|
||||
231, 236, 174, 211, 247, 22, 118, 144, 224, 68, 124, 200, 92, 63, 183,
|
||||
56, 107, 45, 180, 113, 233, 59, 246, 29, 212, 172, 161, 183, 207, 189,
|
||||
56, 198, 130, 62, 28, 53, 122]
|
||||
|
||||
var
|
||||
getKey: LruKey[int,int] =
|
||||
proc(x: int): int = x
|
||||
|
||||
getValue: LruValue[int,string,int] =
|
||||
proc(x: int): Result[string,int] = ok($x)
|
||||
|
||||
cache: LruCache[int,int,string,int]
|
||||
|
||||
cache.initLruCache(getKey, getValue, cacheLimit)
|
||||
|
||||
var lastQ: seq[int]
|
||||
for w in keyList:
|
||||
var
|
||||
key = w mod 13
|
||||
reSched = cache.tab.hasKey(key)
|
||||
value = cache.getLruItem(key)
|
||||
queue = toSeq(cache.tab.keys)
|
||||
if reSched:
|
||||
echo "+++ rotate ", value, " => ", queue
|
||||
else:
|
||||
echo "*** append ", value, " => ", queue
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
Loading…
x
Reference in New Issue
Block a user