nimbus-eth1/nimbus/chain_config.nim

541 lines
21 KiB
Nim

# Nimbus
# Copyright (c) 2021 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/[tables, strutils, options, times],
eth/[common, rlp, p2p], stint, stew/[byteutils],
nimcrypto/hash,
json_serialization, chronicles,
json_serialization/std/options as jsoptions,
json_serialization/std/tables as jstable,
json_serialization/lexer,
"."/[forks, genesis_alloc]
{.push raises: [Defect].}
type
CliqueOptions = object
epoch : Option[int]
period: Option[int]
ChainOptions = 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]
mergeForkBlock : Option[BlockNumber] ##\
## EIP-3675 (TheMerge) switch block: "For the purposes of the EIP-2124\
## fork identifier, nodes implementing this EIP *MUST* set the\
## `FORK_NEXT` parameter to the `FORK_NEXT_VALUE`."
clique : CliqueOptions
terminalTotalDifficulty*: Option[UInt256]
ChainConfig* = ref object
chainId* : ChainId
homesteadBlock* : BlockNumber
daoForkBlock* : BlockNumber
daoForkSupport* : bool
# EIP150 implements the Gas price changes (https://github.com/ethereum/EIPs/issues/150)
eip150Block* : BlockNumber
eip150Hash* : Hash256
eip155Block* : BlockNumber
eip158Block* : BlockNumber
byzantiumBlock* : BlockNumber
constantinopleBlock*: BlockNumber
petersburgBlock* : BlockNumber
istanbulBlock* : BlockNumber
muirGlacierBlock* : BlockNumber
berlinBlock* : BlockNumber
londonBlock* : BlockNumber
arrowGlacierBlock* : BlockNumber
mergeForkBlock* : Option[BlockNumber] # EIP-3675 (TheMerge) switch block
poaEngine* : bool
cliquePeriod* : int
cliqueEpoch* : int
terminalTotalDifficulty*: Option[UInt256]
Genesis* = ref object
nonce* : BlockNonce
timestamp* : EthTime
extraData* : seq[byte]
gasLimit* : GasInt
difficulty* : DifficultyInt
mixHash* : Hash256
coinbase* : EthAddress
alloc* : GenesisAlloc
number* : BlockNumber
gasUser* : GasInt
parentHash* : Hash256
baseFeePerGas*: Option[UInt256]
GenesisAlloc* = Table[EthAddress, GenesisAccount]
GenesisAccount* = object
code* : seq[byte]
storage*: Table[UInt256, UInt256]
balance*: UInt256
nonce* : AccountNonce
NetworkParams* = object
config* : ChainConfig
genesis*: Genesis
AddressBalance = object
address {.rlpCustomSerialization.}: EthAddress
account {.rlpCustomSerialization.}: GenesisAccount
CustomChain = object
config : ChainOptions
genesis: Genesis
GenesisFile* = object
config : ChainOptions
nonce* : BlockNonce
timestamp* : EthTime
extraData* : seq[byte]
gasLimit* : GasInt
difficulty* : DifficultyInt
mixHash* : Hash256
coinbase* : EthAddress
alloc* : GenesisAlloc
number* : BlockNumber
gasUser* : GasInt
parentHash* : Hash256
baseFeePerGas*: Option[UInt256]
const
CustomNet* = 0.NetworkId
# these are public network id
MainNet* = 1.NetworkId
# No longer used: MordenNet = 2
RopstenNet* = 3.NetworkId
RinkebyNet* = 4.NetworkId
GoerliNet* = 5.NetworkId
KovanNet* = 42.NetworkId
SepoliaNet* = 11155111.NetworkId
proc read(rlp: var Rlp, x: var AddressBalance, _: type EthAddress): EthAddress
{.gcsafe, raises: [Defect,RlpError].} =
let val = rlp.read(UInt256).toByteArrayBE()
result[0 .. ^1] = val.toOpenArray(12, val.high)
proc read(rlp: var Rlp, x: var AddressBalance, _: type GenesisAccount): GenesisAccount
{.gcsafe, raises: [Defect,RlpError].} =
GenesisAccount(balance: rlp.read(UInt256))
func decodePrealloc*(data: seq[byte]): GenesisAlloc
{.gcsafe, raises: [Defect,RlpError].} =
for tup in rlp.decode(data, seq[AddressBalance]):
result[tup.address] = tup.account
# borrowed from `lexer.hexCharValue()` :)
proc fromHex(c: char): int =
case c
of '0'..'9': ord(c) - ord('0')
of 'a'..'f': ord(c) - ord('a') + 10
of 'A'..'F': ord(c) - ord('A') + 10
else: -1
proc readValue(reader: var JsonReader, value: var UInt256)
{.gcsafe, raises: [Defect,CatchableError].} =
## Mixin for `Json.loadFile()`. Note that this driver applies the same
## to `BlockNumber` fields as well as generic `UInt265` fields like the
## account `balance`.
var (accu, ok) = (0.u256, true)
if reader.lexer.lazyTok == tkNumeric:
try:
reader.lexer.customIntValueIt:
accu = accu * 10 + it.u256
ok = reader.lexer.lazyTok == tkExInt # non-negative wanted
except:
ok = false
elif reader.lexer.lazyTok == tkQuoted:
try:
var (sLen, base) = (0, 10)
reader.lexer.customTextValueIt:
if ok:
var num = it.fromHex
if base <= num:
ok = false # cannot be larger than base
elif sLen < 2:
if 0 <= num:
accu = accu * base.u256 + num.u256
elif sLen == 1 and it in {'x', 'X'}:
base = 16 # handle "0x" prefix
else:
ok = false
sLen.inc
elif num < 0:
ok = false # not a hex digit
elif base == 10:
accu = accu * 10 + num.u256
else:
accu = accu * 16 + num.u256
except:
reader.raiseUnexpectedValue("numeric string parse error")
else:
reader.raiseUnexpectedValue("expect int or hex/int string")
if not ok:
reader.raiseUnexpectedValue("Uint256 parse error")
value = accu
reader.lexer.next()
proc readValue(reader: var JsonReader, value: var ChainId)
{.gcsafe, raises: [Defect,CatchableError].} =
value = reader.readValue(int).ChainId
proc readValue(reader: var JsonReader, value: var Hash256)
{.gcsafe, raises: [Defect,CatchableError].} =
value = Hash256.fromHex(reader.readValue(string))
proc readValue(reader: var JsonReader, value: var BlockNonce)
{.gcsafe, raises: [Defect,CatchableError].} =
value = fromHex[uint64](reader.readValue(string)).toBlockNonce
proc readValue(reader: var JsonReader, value: var EthTime)
{.gcsafe, raises: [Defect,CatchableError].} =
value = fromHex[int64](reader.readValue(string)).fromUnix
proc readValue(reader: var JsonReader, value: var seq[byte])
{.gcsafe, raises: [Defect,CatchableError].} =
value = hexToSeqByte(reader.readValue(string))
proc readValue(reader: var JsonReader, value: var GasInt)
{.gcsafe, raises: [Defect,CatchableError].} =
value = fromHex[GasInt](reader.readValue(string))
proc readValue(reader: var JsonReader, value: var EthAddress)
{.gcsafe, raises: [Defect,CatchableError].} =
value = parseAddress(reader.readValue(string))
proc readValue(reader: var JsonReader, value: var AccountNonce)
{.gcsafe, raises: [Defect,CatchableError].} =
value = fromHex[uint64](reader.readValue(string))
template to(a: string, b: type EthAddress): EthAddress =
# json_serialization decode table stuff
parseAddress(a)
template to(a: string, b: type UInt256): UInt256 =
# json_serialization decode table stuff
UInt256.fromHex(a)
proc loadNetworkParams*(cc: CustomChain, cg: var NetworkParams): bool =
if cc.genesis.isNil:
cg.genesis = Genesis()
warn "Loaded custom network configuration contains no 'genesis' data"
else:
cg.genesis = cc.genesis
cg.config = ChainConfig()
cg.config.chainId = cc.config.chainId
cg.config.daoForkSupport = cc.config.daoForkSupport
cg.config.eip150Hash = cc.config.eip150Hash
cg.config.poaEngine = false
if cc.config.clique.period.isSome or
cc.config.clique.epoch.isSome:
cg.config.poaEngine = true
# set default clique period to 1 second.
cg.config.cliquePeriod = cc.config.clique.period.get(1)
if cc.config.clique.epoch.isSome:
cg.config.cliqueEpoch = cc.config.clique.epoch.get()
cg.config.terminalTotalDifficulty = cc.config.terminalTotalDifficulty
template validateFork(forkName: untyped, nextBlock: BlockNumber) =
let fork = astToStr(forkName)
if cc.config.forkName.isSome:
cg.config.forkName = cc.config.forkName.get()
else:
cg.config.forkName = nextBlock
if cg.config.forkName > nextBlock:
error "Forks can't be assigned out of order", fork=fork
return false
# Process optional block numbers in non-increasing order (otherwise error.)
#
# See also the go-lang ref implementation:
# params/config.go:508: func (c *ChainConfig) CheckConfigForkOrder() error {
#
# The difference to the ref implementation is that we have no optional values
# everywhere for the block numbers but rather assign the next larger block.
validateFork(arrowGlacierBlock, high(BlockNumber))
validateFork(londonBlock, cg.config.arrowGlacierBlock)
validateFork(berlinBlock, cg.config.londonBlock)
validateFork(muirGlacierBlock, cg.config.berlinBlock)
validateFork(istanbulBlock, cg.config.muirGlacierBlock)
validateFork(petersburgBlock, cg.config.istanbulBlock)
validateFork(constantinopleBlock, cg.config.petersburgBlock)
validateFork(byzantiumBlock, cg.config.constantinopleBlock)
validateFork(eip158Block, cg.config.byzantiumBlock)
validateFork(eip155Block, cg.config.eip158Block)
validateFork(eip150Block, cg.config.eip155Block)
validateFork(daoForkBlock, cg.config.eip150Block)
validateFork(homesteadBlock, cg.config.daoForkBlock)
# Only this last entry remains optional.
cg.config.mergeForkBlock = cc.config.mergeForkBlock
if cc.config.mergeForkBlock.isSome:
# Must be larger than the largest block
let topBlock = min(cg.config.arrowGlacierBlock, cg.config.londonBlock)
if cg.config.mergeForkBlock.get < topBlock:
error "Forks can't be assigned out of order", fork="mergeForkBlock"
return false
return true
proc loadNetworkParams*(fileName: string, cg: var NetworkParams):
bool {.raises: [Defect,SerializationError].} =
var cc: CustomChain
try:
cc = Json.loadFile(fileName, CustomChain, allowUnknownFields = true)
except IOError as e:
error "Network params I/O error", fileName, msg=e.msg
return false
except JsonReaderError as e:
error "Invalid network params file format", fileName, msg=e.formatMsg("")
return false
except Exception as e:
error "Error loading network params file",
fileName, exception = e.name, msg = e.msg
return false
loadNetworkParams(cc, cg)
proc decodeNetworkParams*(jsonString: string, cg: var NetworkParams): bool =
var cc: CustomChain
try:
cc = Json.decode(jsonString, CustomChain, allowUnknownFields = true)
except JsonReaderError as e:
error "Invalid network params format", msg=e.formatMsg("")
return false
except:
var msg = getCurrentExceptionMsg()
error "Error decoding network params", msg
return false
loadNetworkParams(cc, cg)
proc parseGenesisAlloc*(data: string, ga: var GenesisAlloc): bool
{.gcsafe, raises: [Defect,CatchableError].} =
try:
ga = Json.decode(data, GenesisAlloc, allowUnknownFields = true)
except JsonReaderError as e:
error "Invalid genesis config file format", msg=e.formatMsg("")
return false
return true
proc toFork*(c: ChainConfig, number: BlockNumber): Fork =
## Map to EVM fork, which doesn't include the DAO or Glacier forks.
if number >= c.londonBlock: FkLondon
elif number >= c.berlinBlock: FkBerlin
elif number >= c.istanbulBlock: FkIstanbul
elif number >= c.petersburgBlock: FkPetersburg
elif number >= c.constantinopleBlock: FkConstantinople
elif number >= c.byzantiumBlock: FkByzantium
elif number >= c.eip158Block: FkSpurious
elif number >= c.eip150Block: FkTangerine
elif number >= c.homesteadBlock: FkHomestead
else: FkFrontier
proc chainConfigForNetwork(id: NetworkId): ChainConfig =
# For some public networks, NetworkId and ChainId value are identical
# but that is not always the case
result = case id
of MainNet:
ChainConfig(
poaEngine: false,
chainId: MainNet.ChainId,
# Genesis (Frontier): # 2015-07-30 15:26:13 UTC
# Frontier Thawing: 200_000.toBlockNumber, # 2015-09-07 21:33:09 UTC
homesteadBlock: 1_150_000.toBlockNumber, # 2016-03-14 18:49:53 UTC
daoForkBlock: 1_920_000.toBlockNumber, # 2016-07-20 13:20:40 UTC
daoForkSupport: true,
eip150Block: 2_463_000.toBlockNumber, # 2016-10-18 13:19:31 UTC
eip150Hash: toDigest("2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"),
eip155Block: 2_675_000.toBlockNumber, # Same as EIP-158
eip158Block: 2_675_000.toBlockNumber, # 2016-11-22 16:15:44 UTC
byzantiumBlock: 4_370_000.toBlockNumber, # 2017-10-16 05:22:11 UTC
constantinopleBlock: 7_280_000.toBlockNumber, # Skipped on Mainnet
petersburgBlock: 7_280_000.toBlockNumber, # 2019-02-28 19:52:04 UTC
istanbulBlock: 9_069_000.toBlockNumber, # 2019-12-08 00:25:09 UTC
muirGlacierBlock: 9_200_000.toBlockNumber, # 2020-01-02 08:30:49 UTC
berlinBlock: 12_244_000.toBlockNumber, # 2021-04-15 10:07:03 UTC
londonBlock: 12_965_000.toBlockNumber, # 2021-08-05 12:33:42 UTC
arrowGlacierBlock: 13_773_000.toBlockNumber, # 2021-12-09 19:55:23 UTC
mergeForkBlock: none(BlockNumber),
)
of RopstenNet:
ChainConfig(
poaEngine: false,
chainId: RopstenNet.ChainId,
# Genesis: # 2016-11-20 11:48:50 UTC
homesteadBlock: 0.toBlockNumber, # Included in genesis
daoForkSupport: false,
eip150Block: 0.toBlockNumber, # Included in genesis
eip150Hash: toDigest("41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d"),
eip155Block: 10.toBlockNumber, # Same as EIP-158
eip158Block: 10.toBlockNumber, # 2016-11-20 11:50:44 UTC
byzantiumBlock: 1_700_000.toBlockNumber, # 2017-09-19 01:08:28 UTC
constantinopleBlock: 4_230_000.toBlockNumber, # 2018-10-13 17:19:06 UTC
petersburgBlock: 4_939_394.toBlockNumber, # 2019-02-02 07:39:08 UTC
istanbulBlock: 6_485_846.toBlockNumber, # 2019-09-30 03:38:06 UTC
muirGlacierBlock: 7_117_117.toBlockNumber, # 2020-01-13 06:37:37 UTC
berlinBlock: 9_812_189.toBlockNumber, # 2021-03-10 13:32:08 UTC
londonBlock: 10_499_401.toBlockNumber, # 2021-06-24 02:03:37 UTC
arrowGlacierBlock: high(BlockNumber), # No current plan
mergeForkBlock: none(BlockNumber),
)
of RinkebyNet:
ChainConfig(
poaEngine: true,
chainId: RinkebyNet.ChainId,
# Genesis: # 2017-04-12 15:20:50 UTC
homesteadBlock: 1.toBlockNumber, # 2017-04-12 15:20:58 UTC
daoForkSupport: false,
eip150Block: 2.toBlockNumber, # 2017-04-12 15:21:14 UTC
eip150Hash: toDigest("9b095b36c15eaf13044373aef8ee0bd3a382a5abb92e402afa44b8249c3a90e9"),
eip155Block: 3.toBlockNumber, # Same as EIP-158
eip158Block: 3.toBlockNumber, # 2017-04-12 15:21:29 UTC
byzantiumBlock: 1_035_301.toBlockNumber, # 2017-10-09 12:08:23 UTC
constantinopleBlock: 3_660_663.toBlockNumber, # 2019-01-09 13:00:55 UTC
petersburgBlock: 4_321_234.toBlockNumber, # 2019-05-04 05:32:45 UTC
istanbulBlock: 5_435_345.toBlockNumber, # 2019-11-13 18:21:53 UTC
muirGlacierBlock: 8_290_928.toBlockNumber, # Skipped on Rinkeby
berlinBlock: 8_290_928.toBlockNumber, # 2021-03-24 14:48:36 UTC
londonBlock: 8_897_988.toBlockNumber, # 2021-07-08 01:27:32 UTC
arrowGlacierBlock: high(BlockNumber), # No current plan
mergeForkBlock: none(BlockNumber),
)
of GoerliNet:
ChainConfig(
poaEngine: true,
chainId: GoerliNet.ChainId,
# Genesis: # 2015-07-30 15:26:13 UTC
homesteadBlock: 0.toBlockNumber, # Included in genesis
daoForkSupport: false,
eip150Block: 0.toBlockNumber, # Included in genesis
eip150Hash: toDigest("0000000000000000000000000000000000000000000000000000000000000000"),
eip155Block: 0.toBlockNumber, # Included in genesis
eip158Block: 0.toBlockNumber, # Included in genesis
byzantiumBlock: 0.toBlockNumber, # Included in genesis
constantinopleBlock: 0.toBlockNumber, # Included in genesis
petersburgBlock: 0.toBlockNumber, # Included in genesis
istanbulBlock: 1_561_651.toBlockNumber, # 2019-10-30 13:53:05 UTC
muirGlacierBlock: 4_460_644.toBlockNumber, # Skipped in Goerli
berlinBlock: 4_460_644.toBlockNumber, # 2021-03-18 05:29:51 UTC
londonBlock: 5_062_605.toBlockNumber, # 2021-07-01 03:19:39 UTC
arrowGlacierBlock: high(BlockNumber), # No current plan
mergeForkBlock: none(BlockNumber),
)
of SepoliaNet:
ChainConfig(
poaEngine: false,
chainId: SepoliaNet.ChainId,
arrowGlacierBlock: high(BlockNumber), # No current plan
mergeForkBlock: none(BlockNumber),
)
else:
ChainConfig()
proc genesisBlockForNetwork(id: NetworkId): Genesis
{.gcsafe, raises: [Defect, ValueError, RlpError].} =
result = case id
of MainNet:
Genesis(
nonce: 66.toBlockNonce,
extraData: hexToSeqByte("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"),
gasLimit: 5000,
difficulty: 17179869184.u256,
alloc: decodePrealloc(mainnetAllocData)
)
of RopstenNet:
Genesis(
nonce: 66.toBlockNonce,
extraData: hexToSeqByte("0x3535353535353535353535353535353535353535353535353535353535353535"),
gasLimit: 16777216,
difficulty: 1048576.u256,
alloc: decodePrealloc(testnetAllocData)
)
of RinkebyNet:
Genesis(
nonce: 0.toBlockNonce,
timestamp: initTime(0x58ee40ba, 0),
extraData: hexToSeqByte("0x52657370656374206d7920617574686f7269746168207e452e436172746d616e42eb768f2244c8811c63729a21a3569731535f067ffc57839b00206d1ad20c69a1981b489f772031b279182d99e65703f0076e4812653aab85fca0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
gasLimit: 4700000,
difficulty: 1.u256,
alloc: decodePrealloc(rinkebyAllocData)
)
of GoerliNet:
Genesis(
nonce: 0.toBlockNonce,
timestamp: initTime(0x5c51a607, 0),
extraData: hexToSeqByte("0x22466c6578692069732061207468696e6722202d204166726900000000000000e0a2bd4258d2768837baa26a28fe71dc079f84c70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
gasLimit: 0xa00000,
difficulty: 1.u256,
alloc: decodePrealloc(goerliAllocData)
)
of SepoliaNet:
Genesis(
nonce: 0.toBlockNonce,
timestamp: initTime(0x6159af19, 0),
extraData: hexToSeqByte("0x5365706f6c69612c20417468656e732c204174746963612c2047726565636521"),
gasLimit: 0x1c9c380,
difficulty: 0x20000.u256,
alloc: decodePrealloc(sepoliaAllocData)
)
else:
Genesis()
proc networkParams*(id: NetworkId): NetworkParams
{.gcsafe, raises: [Defect, ValueError, RlpError].} =
result.genesis = genesisBlockForNetwork(id)
result.config = chainConfigForNetwork(id)
proc `==`*(a, b: ChainId): bool =
a.uint64 == b.uint64
proc `==`*(a, b: Genesis): bool =
if a.isNil and b.isNil: return true
if a.isNil and not b.isNil: return false
if not a.isNil and b.isNil: return false
a[] == b[]
proc `==`*(a, b: ChainConfig): bool =
if a.isNil and b.isNil: return true
if a.isNil and not b.isNil: return false
if not a.isNil and b.isNil: return false
a[] == b[]