nimbus-eth1/nimbus/p2p/chain/chain_desc.nim

251 lines
8.2 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.
import
../../chain_config,
../../db/db_chain,
../../genesis,
../../utils,
../../chain_config,
../clique,
../validate,
../validate/epoch_hash_cache,
chronicles,
eth/[common, trie/db],
stew/endians2,
stint
type
# Chain's forks not always equals to EVM's forks
ChainFork* = enum
Frontier,
Homestead,
DAOFork,
Tangerine,
Spurious,
Byzantium,
Constantinople,
Petersburg,
Istanbul,
MuirGlacier,
Berlin,
London
Chain* = ref object of AbstractChainDB
db: BaseChainDB
forkIds: array[ChainFork, ForkID]
blockZeroHash: KeccakHash
extraValidation: bool ##\
## Trigger extra validation, currently within `persistBlocks()`
## function only.
verifyFrom: BlockNumber ##\
## First block to when `extraValidation` will be applied (only
## effective if `extraValidation` is true.)
cacheByEpoch: EpochHashCache ##\
## Objects cache to speed up hash lookup in validation functions.
poa: Clique ##\
## For non-PoA networks (when `db.config.poaEngine` is `false`),
## this descriptor is ignored.
{.push raises: [Defect].}
# ------------------------------------------------------------------------------
# Helpers
# ------------------------------------------------------------------------------
func toNextFork(n: BlockNumber): uint64 =
if n == high(BlockNumber):
result = 0'u64
else:
result = n.truncate(uint64)
func getNextFork(c: ChainConfig, fork: ChainFork): uint64 =
let next: array[ChainFork, uint64] = [
0'u64,
toNextFork(c.homesteadBlock),
toNextFork(c.daoForkBlock),
toNextFork(c.eip150Block),
toNextFork(c.eip158Block),
toNextFork(c.byzantiumBlock),
toNextFork(c.constantinopleBlock),
toNextFork(c.petersburgBlock),
toNextFork(c.istanbulBlock),
toNextFork(c.muirGlacierBlock),
toNextFork(c.berlinBlock),
toNextFork(c.londonBlock)
]
if fork == high(ChainFork):
result = 0
return
result = next[fork]
for x in fork..high(ChainFork):
if result != next[x]:
result = next[x]
break
func calculateForkId(c: ChainConfig, fork: ChainFork,
prevCRC: uint32, prevFork: uint64): ForkID =
result.nextFork = c.getNextFork(fork)
if result.nextFork != prevFork:
result.crc = crc32(prevCRC, toBytesBE(prevFork))
else:
result.crc = prevCRC
func calculateForkIds(c: ChainConfig,
genesisCRC: uint32): array[ChainFork, ForkID] =
var prevCRC = genesisCRC
var prevFork = c.getNextFork(Frontier)
for fork in ChainFork:
result[fork] = calculateForkId(c, fork, prevCRC, prevFork)
prevFork = result[fork].nextFork
prevCRC = result[fork].crc
proc setForkId(c: Chain, cn: CustomNetwork)
{. raises: [Defect,CatchableError].} =
let g = genesisBlockForNetwork(c.db.networkId, cn)
c.blockZeroHash = g.toBlock.blockHash
let genesisCRC = crc32(0, c.blockZeroHash.data)
c.forkIds = calculateForkIds(c.db.config, genesisCRC)
# ------------------------------------------------------------------------------
# Private constructor helper
# ------------------------------------------------------------------------------
proc initChain(c: Chain; db: BaseChainDB; poa: Clique; extraValidation: bool)
{.gcsafe, raises: [Defect,CatchableError].} =
## Constructor for the `Chain` descriptor object. For most applications,
## the `poa` argument is transparent and should be initilaised on the fly
## which is available below.
c.db = db
if not db.config.daoForkSupport:
db.config.daoForkBlock = db.config.homesteadBlock
c.extraValidation = extraValidation
c.setForkId(db.customNetwork)
# Initalise the PoA state regardless of whether it is needed on the current
# network. For non-PoA networks (when `db.config.poaEngine` is `false`),
# this descriptor is ignored.
c.poa = db.newClique
# Always initialise the epoch cache even though it migh no be used
# unless `extraValidation` is set `true`.
c.cacheByEpoch.initEpochHashCache
# ------------------------------------------------------------------------------
# Public constructors
# ------------------------------------------------------------------------------
proc newChain*(db: BaseChainDB; poa: Clique; extraValidation: bool): Chain
{.gcsafe, raises: [Defect,CatchableError].} =
## Constructor for the `Chain` descriptor object. For most applications,
## the `poa` argument is transparent and should be initilaised on the fly
## which is available below. The argument `extraValidation` enables extra
## block chain validation if set `true`.
new result
result.initChain(db, poa, extraValidation)
proc newChain*(db: BaseChainDB, extraValidation: bool): Chain
{.gcsafe, raises: [Defect,CatchableError].} =
## Constructor for the `Chain` descriptor object with default initialisation
## for the PoA handling. The argument `extraValidation` enables extra block
## chain validation if set `true`.
new result
result.initChain(db, db.newClique, extraValidation)
proc newChain*(db: BaseChainDB): Chain
{.gcsafe, raises: [Defect,CatchableError].} =
## Constructor for the `Chain` descriptor object. All sub-object descriptors
## are initialised with defaults. So is extra block chain validation
## * `enabled` for PoA networks (such as Goerli)
## * `disabled` for nopn-PaA networks
new result
result.initChain(db, db.newClique, db.config.poaEngine)
# ------------------------------------------------------------------------------
# Public `AbstractChainDB` getter overload methods
# ------------------------------------------------------------------------------
method genesisHash*(c: Chain): KeccakHash {.gcsafe.} =
## Getter: `AbstractChainDB` overload method
c.blockZeroHash
method getBestBlockHeader*(c: Chain): BlockHeader
{.gcsafe, raises: [Defect,CatchableError].} =
## Getter: `AbstractChainDB` overload method
c.db.getCanonicalHead()
method getTrieDB*(c: Chain): TrieDatabaseRef {.gcsafe.} =
## Getter: `AbstractChainDB` overload method
c.db.db
# ------------------------------------------------------------------------------
# Public `Chain` getters
# ------------------------------------------------------------------------------
proc clique*(c: Chain): var Clique {.inline.} =
## Getter
c.poa
proc cacheByEpoch*(c: Chain): var EpochHashCache {.inline.} =
## Getter
c.cacheByEpoch
proc db*(c: Chain): auto {.inline.} =
## Getter
c.db
proc extraValidation*(c: Chain): auto {.inline.} =
## Getter
c.extraValidation
proc forkIds*(c: Chain): auto {.inline.} =
## Getter
c.forkIds
proc verifyFrom*(c: Chain): auto {.inline.} =
## Getter
c.verifyFrom
proc currentBlock*(c: Chain): BlockHeader
{.gcsafe, raises: [Defect,CatchableError].} =
## currentBlock retrieves the current head block of the canonical chain.
## Ideally the block should be retrieved from the blockchain's internal cache.
## but now it's enough to retrieve it from database
c.db.getCanonicalHead()
# ------------------------------------------------------------------------------
# Public `Chain` setters
# ------------------------------------------------------------------------------
proc `extraValidation=`*(c: Chain; extraValidation: bool) {.inline.} =
## Setter. If set `true`, the assignment value `extraValidation` enables
## extra block chain validation.
c.extraValidation = extraValidation
proc `verifyFrom=`*(c: Chain; verifyFrom: uint64) {.inline.} =
## Setter. The assignment value `verifyFrom` defines the first block where
## validation should start if the `Clique` field `extraValidation` was set
## `true`.
c.verifyFrom = verifyFrom.u256
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------