191 lines
6.5 KiB
Nim
191 lines
6.5 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], stint, stew/[byteutils],
|
||
|
nimcrypto/hash,
|
||
|
json_serialization, chronicles,
|
||
|
json_serialization/std/options as jsoptions,
|
||
|
json_serialization/std/tables as jstable,
|
||
|
json_serialization/lexer
|
||
|
|
||
|
type
|
||
|
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]
|
||
|
|
||
|
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
|
||
|
|
||
|
Genesis* = object
|
||
|
nonce* : BlockNonce
|
||
|
timestamp* : EthTime
|
||
|
extraData* : seq[byte]
|
||
|
gasLimit* : GasInt
|
||
|
difficulty* : DifficultyInt
|
||
|
mixHash* : Hash256
|
||
|
coinbase* : EthAddress
|
||
|
alloc* : GenesisAlloc
|
||
|
|
||
|
GenesisAlloc* = Table[EthAddress, GenesisAccount]
|
||
|
GenesisAccount* = object
|
||
|
code* : seq[byte]
|
||
|
storage*: Table[UInt256, UInt256]
|
||
|
balance*: UInt256
|
||
|
nonce* : AccountNonce
|
||
|
|
||
|
CustomGenesis* = object
|
||
|
config* : ChainConfig
|
||
|
genesis*: Genesis
|
||
|
|
||
|
AddressBalance = object
|
||
|
address {.rlpCustomSerialization.}: EthAddress
|
||
|
account {.rlpCustomSerialization.}: GenesisAccount
|
||
|
|
||
|
CustomChain = object
|
||
|
config : ChainOptions
|
||
|
genesis: Genesis
|
||
|
|
||
|
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 loadCustomGenesis*(fileName: string, cg: var CustomGenesis): 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
|
||
|
|
||
|
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(berlinBlock, high(BlockNumber).toBlockNumber)
|
||
|
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
|