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

290 lines
9.3 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,
../../utils/pow,
../../chain_config,
../clique,
../validate,
chronicles,
eth/common/chaindb,
eth/[common, trie/db],
stew/endians2,
stint
type
ChainFork* = enum
## `ChainFork` has extra forks not in the EVM fork list. These are the
## unique `DAOFork`, and Glacier forks `MuirGlacier` and `ArrowGlacier`.
## At the Glacier forks, only block difficulty calculation changed.
Frontier,
Homestead,
DAOFork,
Tangerine,
Spurious,
Byzantium,
Constantinople,
Petersburg,
Istanbul,
MuirGlacier,
Berlin,
London,
ArrowGlacier,
MergeFork
Chain* = ref object of AbstractChainDB
db: BaseChainDB
forkIds: array[ChainFork, ForkID]
blockZeroHash: KeccakHash ##\
## Overload cache for `genesisHash()` method
blockZeroStateRoot: 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.)
pow: PowRef ##\
## Wrapper around `hashimotoLight()` and lookup cache
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 toNextFork(n: Option[BlockNumber]): uint64 =
if n.isSome:
n.get.truncate(uint64)
else:
0'u64
proc isBlockAfterTtd*(c: Chain, header: BlockHeader): bool
{.gcsafe, raises: [Defect,CatchableError].} =
let
ttd = c.db.ttd
ptd = c.db.getScore(header.parentHash)
td = ptd + header.difficulty
# c.db.totalDifficulty is parent.totalDifficulty
# TerminalBlock is defined as header.totalDifficulty >= TTD
# and parent.totalDifficulty < TTD
# So blockAfterTTD must be both header.totalDifficulty >= TTD
# and parent.totalDifficulty >= TTD
ptd >= ttd and td >= ttd
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),
toNextFork(c.arrowGlacierBlock),
toNextFork(c.mergeForkBlock)
]
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)
{. raises: [Defect,CatchableError].} =
let blockZero = c.db.toGenesisHeader
c.blockZeroHash = blockZero.blockHash
c.blockZeroStateRoot = blockZero.stateRoot
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()
# 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 PoW epoch cache even though it migh no be used
# unless `extraValidation` is set `true`.
c.pow = PowRef.new
# ------------------------------------------------------------------------------
# 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 genesisStateRoot*(c: Chain): KeccakHash {.gcsafe, base.} =
## Getter: `AbstractChainDB` overloadable base method
c.blockZeroStateRoot
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 =
## Getter
c.poa
proc pow*(c: Chain): PowRef =
## Getter
c.pow
proc db*(c: Chain): BaseChainDB =
## Getter
c.db
proc extraValidation*(c: Chain): bool =
## Getter
c.extraValidation
proc forkIds*(c: Chain): array[ChainFork,ForkID] =
## Getter
c.forkIds
proc verifyFrom*(c: Chain): BlockNumber =
## 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) =
## Setter. If set `true`, the assignment value `extraValidation` enables
## extra block chain validation.
c.extraValidation = extraValidation
proc `verifyFrom=`*(c: Chain; verifyFrom: BlockNumber) =
## Setter. The assignment value `verifyFrom` defines the first block where
## validation should start if the `Clique` field `extraValidation` was set
## `true`.
c.verifyFrom = verifyFrom
proc `verifyFrom=`*(c: Chain; verifyFrom: uint64) =
## Variant of `verifyFrom=`
c.verifyFrom = verifyFrom.u256
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------