322 lines
12 KiB
Nim
322 lines
12 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
|
|
|
|
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]
|
|
clique : CliqueOptions
|
|
|
|
ChainConfig* = 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
|
|
|
|
poaEngine* : bool
|
|
cliquePeriod* : int
|
|
cliqueEpoch* : int
|
|
|
|
Genesis* = object
|
|
nonce* : BlockNonce
|
|
timestamp* : EthTime
|
|
extraData* : seq[byte]
|
|
gasLimit* : GasInt
|
|
difficulty* : DifficultyInt
|
|
mixHash* : Hash256
|
|
coinbase* : EthAddress
|
|
alloc* : GenesisAlloc
|
|
baseFeePerGas*: Option[UInt256]
|
|
|
|
GenesisAlloc* = Table[EthAddress, GenesisAccount]
|
|
GenesisAccount* = object
|
|
code* : seq[byte]
|
|
storage*: Table[UInt256, UInt256]
|
|
balance*: UInt256
|
|
nonce* : AccountNonce
|
|
|
|
CustomNetwork* = object
|
|
config* : ChainConfig
|
|
genesis*: Genesis
|
|
|
|
AddressBalance = object
|
|
address {.rlpCustomSerialization.}: EthAddress
|
|
account {.rlpCustomSerialization.}: GenesisAccount
|
|
|
|
CustomChain = object
|
|
config : ChainOptions
|
|
genesis: Genesis
|
|
|
|
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
|
|
|
|
proc read(rlp: var Rlp, x: var AddressBalance, _: type EthAddress): EthAddress {.inline.} =
|
|
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 {.inline.} =
|
|
GenesisAccount(balance: rlp.read(UInt256))
|
|
|
|
func decodePrealloc*(data: seq[byte]): GenesisAlloc =
|
|
for tup in rlp.decode(data, seq[AddressBalance]):
|
|
result[tup.address] = tup.account
|
|
|
|
proc readValue(reader: var JsonReader, value: var BlockNumber) =
|
|
let tok = reader.lexer.tok
|
|
if tok == tkInt:
|
|
value = toBlockNumber(reader.lexer.absintVal)
|
|
reader.lexer.next()
|
|
elif tok == tkString:
|
|
value = UInt256.fromHex(reader.lexer.strVal)
|
|
reader.lexer.next()
|
|
else:
|
|
reader.raiseUnexpectedValue("expect int or hex string")
|
|
|
|
proc readValue(reader: var JsonReader, value: var ChainId) =
|
|
value = reader.readValue(int).ChainId
|
|
|
|
proc readValue(reader: var JsonReader, value: var Hash256) =
|
|
value = Hash256.fromHex(reader.readValue(string))
|
|
|
|
proc readValue(reader: var JsonReader, value: var BlockNonce) =
|
|
value = fromHex[uint64](reader.readValue(string)).toBlockNonce
|
|
|
|
proc readValue(reader: var JsonReader, value: var EthTime) =
|
|
value = fromHex[int64](reader.readValue(string)).fromUnix
|
|
|
|
proc readValue(reader: var JsonReader, value: var seq[byte]) =
|
|
value = hexToSeqByte(reader.readValue(string))
|
|
|
|
proc readValue(reader: var JsonReader, value: var GasInt) =
|
|
value = fromHex[GasInt](reader.readValue(string))
|
|
|
|
proc readValue(reader: var JsonReader, value: var EthAddress) =
|
|
value = parseAddress(reader.readValue(string))
|
|
|
|
proc readValue(reader: var JsonReader, value: var AccountNonce) =
|
|
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 loadCustomNetwork*(fileName: string, cg: var CustomNetwork): bool =
|
|
var cc: CustomChain
|
|
try:
|
|
cc = Json.loadFile(fileName, CustomChain, allowUnknownFields = true)
|
|
except IOError as e:
|
|
error "Genesis config file error", fileName, msg=e.msg
|
|
return false
|
|
except JsonReaderError as e:
|
|
error "Invalid genesis config file format", fileName, msg=e.formatMsg("")
|
|
return false
|
|
except:
|
|
var msg = getCurrentExceptionMsg()
|
|
error "Error loading genesis block config file", fileName, msg
|
|
return false
|
|
|
|
cg.genesis = cc.genesis
|
|
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
|
|
|
|
if cc.config.clique.period.isSome:
|
|
cg.config.cliquePeriod = cc.config.clique.period.get()
|
|
|
|
if cc.config.clique.epoch.isSome:
|
|
cg.config.cliqueEpoch = cc.config.clique.epoch.get()
|
|
|
|
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
|
|
|
|
validateFork(londonBlock, high(BlockNumber).toBlockNumber)
|
|
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)
|
|
|
|
return true
|
|
|
|
proc parseGenesisAlloc*(data: string, ga: var GenesisAlloc): bool =
|
|
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 =
|
|
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 chainConfig*(id: NetworkId, cn: CustomNetwork): 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, # TODO: use real engine conf: PoW
|
|
chainId: MainNet.ChainId,
|
|
homesteadBlock: 1_150_000.toBlockNumber, # 14/03/2016 20:49:53
|
|
daoForkBlock: 1_920_000.toBlockNumber,
|
|
daoForkSupport: true,
|
|
eip150Block: 2_463_000.toBlockNumber, # 18/10/2016 17:19:31
|
|
eip150Hash: toDigest("2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"),
|
|
eip155Block: 2_675_000.toBlockNumber, # 22/11/2016 18:15:44
|
|
eip158Block: 2_675_000.toBlockNumber,
|
|
byzantiumBlock: 4_370_000.toBlockNumber, # 16/10/2017 09:22:11
|
|
constantinopleBlock: 7_280_000.toBlockNumber, # Never Occured in MainNet
|
|
petersburgBlock:7_280_000.toBlockNumber, # 28/02/2019 07:52:04
|
|
istanbulBlock: 9_069_000.toBlockNumber, # 08/12/2019 12:25:09
|
|
muirGlacierBlock: 9_200_000.toBlockNumber, # 02/01/2020 08:30:49
|
|
berlinBlock: 12_244_000.toBlockNumber, # 15/04/2021 10:07:03
|
|
londonBlock: 12_965_000.toBlockNumber, # 05/08/2021 12:33:42
|
|
)
|
|
of RopstenNet:
|
|
ChainConfig(
|
|
poaEngine: false, # TODO: use real engine conf: PoW
|
|
chainId: RopstenNet.ChainId,
|
|
homesteadBlock: 0.toBlockNumber,
|
|
daoForkSupport: false,
|
|
eip150Block: 0.toBlockNumber,
|
|
eip150Hash: toDigest("41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d"),
|
|
eip155Block: 10.toBlockNumber,
|
|
eip158Block: 10.toBlockNumber,
|
|
byzantiumBlock: 1_700_000.toBlockNumber,
|
|
constantinopleBlock: 4_230_000.toBlockNumber,
|
|
petersburgBlock:4_939_394.toBlockNumber,
|
|
istanbulBlock: 6_485_846.toBlockNumber,
|
|
muirGlacierBlock: 7_117_117.toBlockNumber,
|
|
berlinBlock: 9_812_189.toBlockNumber,
|
|
londonBlock: 10_499_401.toBlockNumber # June 24, 2021
|
|
)
|
|
of RinkebyNet:
|
|
ChainConfig(
|
|
poaEngine: true, # TODO: use real engine conf: PoA
|
|
chainId: RinkebyNet.ChainId,
|
|
homesteadBlock: 1.toBlockNumber,
|
|
daoForkSupport: false,
|
|
eip150Block: 2.toBlockNumber,
|
|
eip150Hash: toDigest("9b095b36c15eaf13044373aef8ee0bd3a382a5abb92e402afa44b8249c3a90e9"),
|
|
eip155Block: 3.toBlockNumber,
|
|
eip158Block: 3.toBlockNumber,
|
|
byzantiumBlock: 1_035_301.toBlockNumber,
|
|
constantinopleBlock: 3_660_663.toBlockNumber,
|
|
petersburgBlock:4_321_234.toBlockNumber,
|
|
istanbulBlock: 5_435_345.toBlockNumber,
|
|
muirGlacierBlock: 8_290_928.toBlockNumber, # never occured in rinkeby network
|
|
berlinBlock: 8_290_928.toBlockNumber,
|
|
londonBlock: 8_897_988.toBlockNumber # July 7, 2021
|
|
)
|
|
of GoerliNet:
|
|
ChainConfig(
|
|
poaEngine: true, # TODO: use real engine conf: PoA
|
|
chainId: GoerliNet.ChainId,
|
|
homesteadBlock: 0.toBlockNumber,
|
|
daoForkSupport: false,
|
|
eip150Block: 0.toBlockNumber,
|
|
eip150Hash: toDigest("0000000000000000000000000000000000000000000000000000000000000000"),
|
|
eip155Block: 0.toBlockNumber,
|
|
eip158Block: 0.toBlockNumber,
|
|
byzantiumBlock: 0.toBlockNumber,
|
|
constantinopleBlock: 0.toBlockNumber,
|
|
petersburgBlock: 0.toBlockNumber,
|
|
istanbulBlock: 1_561_651.toBlockNumber,
|
|
muirGlacierBlock: 4_460_644.toBlockNumber, # never occured in goerli network
|
|
berlinBlock: 4_460_644.toBlockNumber,
|
|
londonBlock: 5_062_605.toBlockNumber # June 30, 2021
|
|
)
|
|
else:
|
|
# everything else will use CustomNet config
|
|
trace "Custom genesis block configuration loaded", conf=cn.config
|
|
cn.config
|