406 lines
14 KiB
Nim
406 lines
14 KiB
Nim
# Nimbus
|
|
# Copyright (c) 2022 Status Research & Development GmbH
|
|
# Licensed under either of
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
|
# at your option.
|
|
# This file may not be copied, modified, or distributed except according to
|
|
# those terms.
|
|
|
|
import
|
|
std/[options],
|
|
eth/common,
|
|
stew/endians2,
|
|
json_serialization,
|
|
../utils/utils,
|
|
./evmforks
|
|
|
|
{.push raises: [].}
|
|
|
|
type
|
|
ConsensusType* {.pure.} = enum
|
|
# Proof of Work
|
|
# algorithm: Ethash
|
|
POW
|
|
|
|
# Proof of Authority
|
|
# algorithm: Clique
|
|
POA
|
|
|
|
# Proof of Stake
|
|
# algorithm: Casper
|
|
POS
|
|
|
|
HardFork* = enum
|
|
Frontier
|
|
Homestead
|
|
DAOFork
|
|
Tangerine # a.k.a. EIP150
|
|
Spurious # a.k.a. EIP158
|
|
Byzantium
|
|
Constantinople
|
|
Petersburg # a.k.a. ConstantinopleFix
|
|
Istanbul
|
|
MuirGlacier
|
|
Berlin
|
|
London
|
|
ArrowGlacier
|
|
GrayGlacier
|
|
MergeFork # a.k.a. Paris
|
|
Shanghai
|
|
Cancun
|
|
|
|
const lastPurelyBlockNumberBasedFork* = GrayGlacier
|
|
# MergeFork is special because of TTD.
|
|
const firstTimeBasedFork* = Shanghai
|
|
|
|
|
|
type
|
|
CliqueOptions* = object
|
|
epoch* : Option[int]
|
|
period*: Option[int]
|
|
|
|
MergeForkTransitionThreshold* = object
|
|
blockNumber*: Option[BlockNumber]
|
|
ttd*: Option[DifficultyInt]
|
|
ttdPassed*: Option[bool]
|
|
|
|
ForkTransitionTable* = object
|
|
blockNumberThresholds*: array[Frontier..GrayGlacier, Option[BlockNumber]]
|
|
mergeForkTransitionThreshold*: MergeForkTransitionThreshold
|
|
timeThresholds*: array[Shanghai..Cancun, Option[EthTime]]
|
|
|
|
# Starting with Shanghai, forking is based on timestamp
|
|
# rather than block number.
|
|
#
|
|
# I'm not sure what to call this type, but we used to pass
|
|
# just the block number into various places that need to
|
|
# determine which fork we're on, and now we need to pass
|
|
# around both block number and also time. And the config
|
|
# info for each individual fork will be either a block
|
|
# number or a time.
|
|
#
|
|
# Note that time and TD are optional. TD being optional
|
|
# is because it's perfectly fine, if mergeForkBlock is
|
|
# set, to not bother with TTD anymore. But I'm not sure
|
|
# it makes sense to allow time to be optional. See the
|
|
# comment below on blockNumberToForkDeterminationInfo.
|
|
ForkDeterminationInfo* = object
|
|
blockNumber*: BlockNumber
|
|
time*: Option[EthTime]
|
|
td*: Option[DifficultyInt]
|
|
|
|
func blockNumberToForkDeterminationInfo*(n: BlockNumber): ForkDeterminationInfo =
|
|
# FIXME: All callers of this function are suspect; I'm guess we should
|
|
# always be using both block number and time. But we have a few places,
|
|
# like various tests, where we only have block number and the tests are
|
|
# meant for pre-Merge forks, so maybe those are okay.
|
|
ForkDeterminationInfo(blockNumber: n, time: none[EthTime](), td: none[DifficultyInt]())
|
|
|
|
func forkDeterminationInfo*(n: BlockNumber, t: EthTime): ForkDeterminationInfo =
|
|
ForkDeterminationInfo(blockNumber: n, time: some(t), td: none[DifficultyInt]())
|
|
|
|
# FIXME: Is this called anywhere?
|
|
func forkDeterminationInfoIncludingTd*(n: BlockNumber, t: EthTime, td: DifficultyInt): ForkDeterminationInfo =
|
|
ForkDeterminationInfo(blockNumber: n, time: some(t), td: some(td))
|
|
|
|
proc adjustForNextBlock*(n: BlockNumber): BlockNumber =
|
|
n + 1
|
|
|
|
func adjustForNextBlock*(t: EthTime): EthTime =
|
|
# FIXME-Adam: what's the right thing to do here?
|
|
# How do we calculate "the timestamp for the block
|
|
# after this one"?
|
|
#
|
|
# If this makes no sense, what should the callers
|
|
# do instead?
|
|
t + 12
|
|
|
|
func adjustForNextBlock*(f: ForkDeterminationInfo): ForkDeterminationInfo =
|
|
ForkDeterminationInfo(
|
|
blockNumber: adjustForNextBlock(f.blockNumber),
|
|
time: f.time.map(adjustForNextBlock),
|
|
td: f.td
|
|
)
|
|
|
|
# This function is awkward because there are various different ways now of
|
|
# doing a hard-fork transition (block number, ttd, time, block number *or*
|
|
# time). We used to have a simple array called forkToBlock that mapped each
|
|
# HardFork to a BlockNumber; now we have this ForkTransitionTable, which
|
|
# contains a couple of arrays and also special cases for MergeBlock and
|
|
# Shanghai.
|
|
func isGTETransitionThreshold*(map: ForkTransitionTable, forkDeterminer: ForkDeterminationInfo, fork: HardFork): bool =
|
|
if fork <= lastPurelyBlockNumberBasedFork:
|
|
map.blockNumberThresholds[fork].isSome and forkDeterminer.blockNumber >= map.blockNumberThresholds[fork].get
|
|
elif fork == MergeFork:
|
|
# MergeFork is a special case that can use either block number or ttd;
|
|
# ttdPassed > block number > ttd takes precedence.
|
|
let t = map.mergeForkTransitionThreshold
|
|
if t.ttdPassed.isSome:
|
|
t.ttdPassed.get
|
|
elif t.blockNumber.isSome:
|
|
forkDeterminer.blockNumber >= t.blockNumber.get
|
|
elif t.ttd.isSome and forkDeterminer.td.isSome:
|
|
forkDeterminer.td.get >= t.ttd.get
|
|
else:
|
|
false
|
|
elif fork <= HardFork.high:
|
|
map.timeThresholds[fork].isSome and forkDeterminer.time.isSome and forkDeterminer.time.get >= map.timeThresholds[fork].get
|
|
else:
|
|
raise newException(Defect, "Why is this hard fork not in one of the above categories?")
|
|
|
|
type
|
|
# if you add more fork block
|
|
# please update forkBlockField constant too
|
|
ChainConfig* = ref object
|
|
chainId* : ChainId
|
|
homesteadBlock* : Option[BlockNumber]
|
|
daoForkBlock* : Option[BlockNumber]
|
|
daoForkSupport* : bool
|
|
eip150Block* : Option[BlockNumber]
|
|
eip150Hash* : Hash256
|
|
eip155Block* : Option[BlockNumber]
|
|
eip158Block* : Option[BlockNumber]
|
|
byzantiumBlock* : Option[BlockNumber]
|
|
constantinopleBlock*: Option[BlockNumber]
|
|
petersburgBlock* : Option[BlockNumber]
|
|
istanbulBlock* : Option[BlockNumber]
|
|
muirGlacierBlock* : Option[BlockNumber]
|
|
berlinBlock* : Option[BlockNumber]
|
|
londonBlock* : Option[BlockNumber]
|
|
arrowGlacierBlock* : Option[BlockNumber]
|
|
grayGlacierBlock* : Option[BlockNumber]
|
|
mergeForkBlock* : Option[BlockNumber]
|
|
shanghaiTime* : Option[EthTime]
|
|
cancunTime* : Option[EthTime]
|
|
|
|
clique* : CliqueOptions
|
|
terminalTotalDifficulty*: Option[UInt256]
|
|
terminalTotalDifficultyPassed*: Option[bool]
|
|
consensusType*
|
|
{.dontSerialize.} : ConsensusType
|
|
|
|
# These are used for checking that the values of the fields
|
|
# are in a valid order.
|
|
BlockNumberBasedForkOptional* = object
|
|
name*: string
|
|
number*: Option[BlockNumber]
|
|
TimeBasedForkOptional* = object
|
|
name*: string
|
|
time*: Option[EthTime]
|
|
|
|
|
|
|
|
const
|
|
# this table is used for generate
|
|
# code at compile time to check
|
|
# the order of blok number in ChainConfig
|
|
forkBlockField* = [
|
|
"homesteadBlock",
|
|
"daoForkBlock",
|
|
"eip150Block",
|
|
"eip155Block",
|
|
"eip158Block",
|
|
"byzantiumBlock",
|
|
"constantinopleBlock",
|
|
"petersburgBlock",
|
|
"istanbulBlock",
|
|
"muirGlacierBlock",
|
|
"berlinBlock",
|
|
"londonBlock",
|
|
"arrowGlacierBlock",
|
|
"grayGlacierBlock",
|
|
"mergeForkBlock",
|
|
]
|
|
|
|
forkTimeField* = [
|
|
"shanghaiTime",
|
|
"cancunTime",
|
|
]
|
|
|
|
|
|
func mergeForkTransitionThreshold*(conf: ChainConfig): MergeForkTransitionThreshold =
|
|
MergeForkTransitionThreshold(
|
|
blockNumber: conf.mergeForkBlock,
|
|
ttd: conf.terminalTotalDifficulty,
|
|
ttdPassed: conf.terminalTotalDifficultyPassed
|
|
)
|
|
|
|
proc toForkTransitionTable*(conf: ChainConfig): ForkTransitionTable =
|
|
# We used to auto-generate this code from a list of
|
|
# field names, but it doesn't seem worthwhile anymore
|
|
# (now that there's irregularity due to block-based vs
|
|
# timestamp-based forking).
|
|
result.blockNumberThresholds[Frontier ] = some(0.toBlockNumber)
|
|
result.blockNumberThresholds[Homestead ] = conf.homesteadBlock
|
|
result.blockNumberThresholds[DAOFork ] = conf.daoForkBlock
|
|
result.blockNumberThresholds[Tangerine ] = conf.eip150Block
|
|
result.blockNumberThresholds[Spurious ] = conf.eip158Block
|
|
result.blockNumberThresholds[Byzantium ] = conf.byzantiumBlock
|
|
result.blockNumberThresholds[Constantinople] = conf.constantinopleBlock
|
|
result.blockNumberThresholds[Petersburg ] = conf.petersburgBlock
|
|
result.blockNumberThresholds[Istanbul ] = conf.istanbulBlock
|
|
result.blockNumberThresholds[MuirGlacier ] = conf.muirGlacierBlock
|
|
result.blockNumberThresholds[Berlin ] = conf.berlinBlock
|
|
result.blockNumberThresholds[London ] = conf.londonBlock
|
|
result.blockNumberThresholds[ArrowGlacier ] = conf.arrowGlacierBlock
|
|
result.blockNumberThresholds[GrayGlacier ] = conf.grayGlacierBlock
|
|
result.mergeForkTransitionThreshold = conf.mergeForkTransitionThreshold
|
|
result.timeThresholds[Shanghai] = conf.shanghaiTime
|
|
result.timeThresholds[Cancun] = conf.cancunTime
|
|
|
|
proc populateFromForkTransitionTable*(conf: ChainConfig, t: ForkTransitionTable) =
|
|
conf.homesteadBlock = t.blockNumberThresholds[HardFork.Homestead]
|
|
conf.daoForkBlock = t.blockNumberThresholds[HardFork.DAOFork]
|
|
conf.eip150Block = t.blockNumberThresholds[HardFork.Tangerine]
|
|
conf.eip155Block = t.blockNumberThresholds[HardFork.Spurious]
|
|
conf.eip158Block = t.blockNumberThresholds[HardFork.Spurious]
|
|
conf.byzantiumBlock = t.blockNumberThresholds[HardFork.Byzantium]
|
|
conf.constantinopleBlock = t.blockNumberThresholds[HardFork.Constantinople]
|
|
conf.petersburgBlock = t.blockNumberThresholds[HardFork.Petersburg]
|
|
conf.istanbulBlock = t.blockNumberThresholds[HardFork.Istanbul]
|
|
conf.muirGlacierBlock = t.blockNumberThresholds[HardFork.MuirGlacier]
|
|
conf.berlinBlock = t.blockNumberThresholds[HardFork.Berlin]
|
|
conf.londonBlock = t.blockNumberThresholds[HardFork.London]
|
|
conf.arrowGlacierBlock = t.blockNumberThresholds[HardFork.ArrowGlacier]
|
|
conf.grayGlacierBlock = t.blockNumberThresholds[HardFork.GrayGlacier]
|
|
|
|
conf.mergeForkBlock = t.mergeForkTransitionThreshold.blockNumber
|
|
conf.terminalTotalDifficulty = t.mergeForkTransitionThreshold.ttd
|
|
conf.terminalTotalDifficultyPassed = t.mergeForkTransitionThreshold.ttdPassed
|
|
|
|
conf.shanghaiTime = t.timeThresholds[HardFork.Shanghai]
|
|
conf.cancunTime = t.timeThresholds[HardFork.Cancun]
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Map HardFork to EVM/EVMC Fork
|
|
# ------------------------------------------------------------------------------
|
|
|
|
const
|
|
ToEVMFork*: array[HardFork, EVMFork] = [
|
|
FkFrontier, # Frontier
|
|
FkHomestead, # Homestead
|
|
FkHomestead, # DAOFork
|
|
FkTangerine, # Tangerine
|
|
FkSpurious, # Spurious
|
|
FkByzantium, # Byzantium
|
|
FkConstantinople, # Constantinople
|
|
FkPetersburg, # Petersburg
|
|
FkIstanbul, # Istanbul
|
|
FkIstanbul, # MuirGlacier
|
|
FkBerlin, # Berlin
|
|
FkLondon, # London
|
|
FkLondon, # ArrowGlacier
|
|
FkLondon, # GrayGlacier
|
|
FkParis, # MergeFork
|
|
FkShanghai, # Shanghai
|
|
FkCancun, # Cancun
|
|
]
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Block reward helpers
|
|
# ------------------------------------------------------------------------------
|
|
|
|
func eth(n: int): UInt256 {.compileTime.} =
|
|
n.u256 * pow(10.u256, 18)
|
|
|
|
const
|
|
eth5 = 5.eth
|
|
eth3 = 3.eth
|
|
eth2 = 2.eth
|
|
eth0 = 0.u256
|
|
|
|
BlockRewards*: array[HardFork, UInt256] = [
|
|
eth5, # Frontier
|
|
eth5, # Homestead
|
|
eth5, # DAOFork
|
|
eth5, # Tangerine
|
|
eth5, # Spurious
|
|
eth3, # Byzantium
|
|
eth2, # Constantinople
|
|
eth2, # Petersburg
|
|
eth2, # Istanbul
|
|
eth2, # MuirGlacier
|
|
eth2, # Berlin
|
|
eth2, # London
|
|
eth2, # ArrowGlacier
|
|
eth2, # GrayGlacier
|
|
eth0, # MergeFork
|
|
eth0, # Shanghai
|
|
eth0, # Cancun
|
|
]
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Fork ID helpers
|
|
# ------------------------------------------------------------------------------
|
|
|
|
func toNextFork(n: Option[BlockNumber]): uint64 =
|
|
if n.isSome:
|
|
n.get.truncate(uint64)
|
|
else:
|
|
0'u64
|
|
|
|
# EIP-6122: ForkID now works with timestamps too.
|
|
func toNextFork(t: Option[EthTime]): uint64 =
|
|
if t.isSome:
|
|
t.get.uint64
|
|
else:
|
|
0'u64
|
|
|
|
func arrayMappingHardForkToNextFork(c: ChainConfig): array[HardFork, uint64] =
|
|
return [
|
|
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.grayGlacierBlock),
|
|
toNextFork(c.mergeForkBlock),
|
|
toNextFork(c.shanghaiTime),
|
|
toNextFork(c.cancunTime),
|
|
]
|
|
|
|
func getNextFork(next: array[HardFork, uint64], fork: HardFork): uint64 =
|
|
if fork == high(HardFork):
|
|
result = 0
|
|
return
|
|
|
|
result = next[fork]
|
|
for x in fork..high(HardFork):
|
|
if result != next[x]:
|
|
result = next[x]
|
|
break
|
|
|
|
func calculateForkId(next: array[HardFork, uint64], fork: HardFork,
|
|
prevCRC: uint32, prevFork: uint64): ForkID =
|
|
result.nextFork = getNextFork(next, fork)
|
|
|
|
if result.nextFork != prevFork:
|
|
result.crc = crc32(prevCRC, toBytesBE(prevFork))
|
|
else:
|
|
result.crc = prevCRC
|
|
|
|
func calculateForkIds*(c: ChainConfig,
|
|
genesisCRC: uint32): array[HardFork, ForkID] =
|
|
let next = arrayMappingHardForkToNextFork(c)
|
|
|
|
var prevCRC = genesisCRC
|
|
var prevFork = getNextFork(next, Frontier)
|
|
|
|
for fork in HardFork:
|
|
result[fork] = calculateForkId(next, fork, prevCRC, prevFork)
|
|
prevFork = result[fork].nextFork
|
|
prevCRC = result[fork].crc
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# End
|
|
# ------------------------------------------------------------------------------
|