# 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 # TODO: this need to be fixed somehow # using `real` engine configuration poaEngine* : bool 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 # TODO: this need to be fixed somehow # using `real` engine configuration cg.config.poaEngine = false 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