2020-07-02 15:14:11 +00:00
|
|
|
import
|
2020-09-01 09:01:57 +00:00
|
|
|
tables, strutils, os,
|
2020-07-09 22:08:54 +00:00
|
|
|
stew/shims/macros, nimcrypto/hash,
|
2020-11-12 19:01:26 +00:00
|
|
|
eth/common/eth_types as commonEthTypes,
|
2020-07-09 22:08:54 +00:00
|
|
|
web3/[ethtypes, conversions],
|
2020-09-01 09:01:57 +00:00
|
|
|
chronicles,
|
|
|
|
spec/presets,
|
|
|
|
spec/datatypes,
|
|
|
|
json_serialization,
|
|
|
|
json_serialization/std/[options, sets, net], serialization/errors
|
2020-07-02 15:14:11 +00:00
|
|
|
|
|
|
|
# ATTENTION! This file will produce a large C file, because we are inlining
|
|
|
|
# genesis states as C literals in the generated code (and blobs in the final
|
|
|
|
# binary). It makes sense to keep the file small and separated from the rest
|
|
|
|
# of the module in order go gain maximum efficiency in incremental compilation.
|
|
|
|
#
|
2020-11-10 18:41:04 +00:00
|
|
|
# TODO(zah):
|
2020-07-02 15:14:11 +00:00
|
|
|
# We can compress the embedded states with snappy before embedding them here.
|
|
|
|
|
|
|
|
{.push raises: [Defect].}
|
|
|
|
|
|
|
|
export
|
2020-07-09 22:08:54 +00:00
|
|
|
ethtypes, conversions, RuntimePreset
|
2020-07-02 15:14:11 +00:00
|
|
|
|
|
|
|
type
|
2020-07-09 22:08:54 +00:00
|
|
|
Eth1Address* = ethtypes.Address
|
|
|
|
Eth1BlockHash* = ethtypes.BlockHash
|
2020-07-02 15:14:11 +00:00
|
|
|
|
|
|
|
Eth1Network* = enum
|
|
|
|
mainnet
|
|
|
|
rinkeby
|
|
|
|
goerli
|
|
|
|
|
2020-07-03 19:29:23 +00:00
|
|
|
PresetIncompatible* = object of CatchableError
|
|
|
|
|
2020-07-02 15:14:11 +00:00
|
|
|
Eth2NetworkMetadata* = object
|
2020-07-09 22:08:54 +00:00
|
|
|
case incompatible*: bool
|
|
|
|
of false:
|
2020-11-06 21:45:56 +00:00
|
|
|
# TODO work-around a Nim codegen issue where upon constant assignment
|
|
|
|
# the compiler will copy `incompatibilityDesc` even when the case
|
|
|
|
# branch is not active and thus it will override the first variable
|
|
|
|
# in this branch.
|
|
|
|
dummy: string
|
2020-07-09 22:08:54 +00:00
|
|
|
eth1Network*: Option[Eth1Network]
|
|
|
|
runtimePreset*: RuntimePreset
|
|
|
|
|
|
|
|
# Parsing `enr.Records` is still not possible at compile-time
|
|
|
|
bootstrapNodes*: seq[string]
|
|
|
|
|
|
|
|
depositContractAddress*: Eth1Address
|
2020-11-12 19:01:26 +00:00
|
|
|
depositContractDeployedAt*: BlockHashOrNumber
|
2020-07-09 22:08:54 +00:00
|
|
|
|
|
|
|
# Please note that we are using `string` here because SSZ.decode
|
|
|
|
# is not currently usable at compile time and we want to load the
|
|
|
|
# network metadata into a constant.
|
|
|
|
#
|
|
|
|
# We could have also used `seq[byte]`, but this results in a lot
|
|
|
|
# more generated code that slows down compilation. The impact on
|
|
|
|
# compilation times of embedding the genesis as a string is roughly
|
|
|
|
# 0.1s on my machine (you can test this by choosing an invalid name
|
|
|
|
# for the genesis file below).
|
|
|
|
#
|
|
|
|
# `genesisData` will have `len == 0` for networks with a still
|
|
|
|
# unknown genesis state.
|
|
|
|
genesisData*: string
|
|
|
|
else:
|
|
|
|
incompatibilityDesc*: string
|
2020-07-02 15:14:11 +00:00
|
|
|
|
2020-07-07 23:02:14 +00:00
|
|
|
const presetValueLoaders = genExpr(nnkBracket):
|
2020-07-03 19:29:23 +00:00
|
|
|
for constName in PresetValue:
|
|
|
|
let
|
|
|
|
constNameIdent = ident $constName
|
|
|
|
constType = ident getType(constName)
|
|
|
|
|
|
|
|
yield quote do:
|
2020-07-07 23:02:14 +00:00
|
|
|
(
|
|
|
|
proc (preset: var RuntimePreset, presetValue: string): bool
|
|
|
|
{.gcsafe, noSideEffect, raises: [Defect].} =
|
|
|
|
try:
|
|
|
|
when PresetValue.`constNameIdent` in runtimeValues:
|
|
|
|
preset.`constNameIdent` = parse(`constType`, presetValue)
|
|
|
|
true
|
|
|
|
elif PresetValue.`constNameIdent` in ignoredValues:
|
|
|
|
true
|
|
|
|
else:
|
|
|
|
`constType`(`constNameIdent`) == parse(`constType`, presetValue)
|
2020-08-03 12:07:25 +00:00
|
|
|
except CatchableError:
|
2020-07-07 23:02:14 +00:00
|
|
|
false
|
|
|
|
)
|
|
|
|
|
|
|
|
proc extractRuntimePreset*(configPath: string, configData: PresetFile): RuntimePreset
|
|
|
|
{.raises: [PresetIncompatible, Defect].} =
|
|
|
|
result = RuntimePreset()
|
|
|
|
|
|
|
|
for name, value in configData.values:
|
2020-07-13 09:10:26 +00:00
|
|
|
if not presetValueLoaders[name.int](result, value):
|
|
|
|
let errMsg = "The preset '" & configPath & "'is not compatible with " &
|
|
|
|
"the current build due to an incompatible value " &
|
|
|
|
$name & " = " & value.string
|
|
|
|
raise newException(PresetIncompatible, errMsg)
|
2020-07-03 19:29:23 +00:00
|
|
|
|
2020-07-02 15:14:11 +00:00
|
|
|
proc loadEth2NetworkMetadata*(path: string): Eth2NetworkMetadata
|
|
|
|
{.raises: [CatchableError, Defect].} =
|
2020-07-09 22:08:54 +00:00
|
|
|
try:
|
|
|
|
let
|
|
|
|
genesisPath = path / "genesis.ssz"
|
|
|
|
configPath = path / "config.yaml"
|
2020-07-10 14:31:24 +00:00
|
|
|
depositContractPath = path / "deposit_contract.txt"
|
|
|
|
depositContractBlockPath = path / "deposit_contract_block.txt"
|
2020-07-28 10:46:22 +00:00
|
|
|
bootstrapNodesPath = path / "bootstrap_nodes.txt"
|
2020-07-10 14:31:24 +00:00
|
|
|
|
|
|
|
runtimePreset = if fileExists(configPath):
|
|
|
|
extractRuntimePreset(configPath, readPresetFile(configPath))
|
|
|
|
else:
|
|
|
|
mainnetRuntimePreset
|
|
|
|
|
|
|
|
depositContractAddress = if fileExists(depositContractPath):
|
|
|
|
Eth1Address.fromHex readFile(depositContractPath).strip
|
|
|
|
else:
|
|
|
|
default(Eth1Address)
|
|
|
|
|
|
|
|
depositContractBlock = if fileExists(depositContractBlockPath):
|
2020-07-28 13:36:11 +00:00
|
|
|
readFile(depositContractBlockPath).strip
|
2020-07-10 14:31:24 +00:00
|
|
|
else:
|
2020-07-28 13:36:11 +00:00
|
|
|
""
|
2020-07-10 14:31:24 +00:00
|
|
|
|
2020-11-12 19:01:26 +00:00
|
|
|
depositContractDeployedAt = if depositContractBlock.len > 0:
|
|
|
|
BlockHashOrNumber.init(depositContractBlock)
|
|
|
|
else:
|
|
|
|
BlockHashOrNumber(isHash: false, number: 1)
|
|
|
|
|
2020-07-28 10:46:22 +00:00
|
|
|
bootstrapNodes = if fileExists(bootstrapNodesPath):
|
|
|
|
readFile(bootstrapNodesPath).splitLines()
|
|
|
|
else:
|
|
|
|
@[]
|
|
|
|
|
2020-07-10 14:31:24 +00:00
|
|
|
genesisData = if fileExists(genesisPath): readFile(genesisPath)
|
|
|
|
else: ""
|
2020-07-09 22:08:54 +00:00
|
|
|
|
|
|
|
Eth2NetworkMetadata(
|
|
|
|
incompatible: false,
|
|
|
|
eth1Network: some goerli,
|
|
|
|
runtimePreset: runtimePreset,
|
2020-07-28 10:46:22 +00:00
|
|
|
bootstrapNodes: bootstrapNodes,
|
2020-07-10 14:31:24 +00:00
|
|
|
depositContractAddress: depositContractAddress,
|
2020-11-12 19:01:26 +00:00
|
|
|
depositContractDeployedAt: depositContractDeployedAt,
|
2020-07-10 14:31:24 +00:00
|
|
|
genesisData: genesisData)
|
|
|
|
|
2020-07-09 22:08:54 +00:00
|
|
|
except PresetIncompatible as err:
|
|
|
|
Eth2NetworkMetadata(incompatible: true,
|
|
|
|
incompatibilityDesc: err.msg)
|
2020-07-02 15:14:11 +00:00
|
|
|
|
2020-07-10 13:21:36 +00:00
|
|
|
const
|
|
|
|
mainnetMetadata* = when const_preset == "mainnet":
|
|
|
|
Eth2NetworkMetadata(
|
2020-07-09 22:08:54 +00:00
|
|
|
incompatible: false, # TODO: This can be more accurate if we verify
|
|
|
|
# that there are no constant overrides
|
|
|
|
eth1Network: some mainnet,
|
2020-07-07 23:02:14 +00:00
|
|
|
runtimePreset: mainnetRuntimePreset,
|
2020-11-10 18:41:04 +00:00
|
|
|
# TODO(zah) Add bootstrap nodes for mainnet
|
2020-07-07 23:02:14 +00:00
|
|
|
bootstrapNodes: @[],
|
2020-11-05 22:33:31 +00:00
|
|
|
depositContractAddress: Eth1Address.fromHex "0x00000000219ab540356cBB839Cbe05303d7705Fa",
|
2020-11-12 19:01:26 +00:00
|
|
|
depositContractDeployedAt: BlockHashOrNumber.init "11052984",
|
2020-07-07 23:02:14 +00:00
|
|
|
genesisData: "")
|
2020-07-10 13:21:36 +00:00
|
|
|
else:
|
|
|
|
Eth2NetworkMetadata(
|
|
|
|
incompatible: true,
|
|
|
|
incompatibilityDesc: "This build is compiled with the " & const_preset & " const preset. " &
|
|
|
|
"It's not compatible with mainnet")
|
2020-07-07 23:02:14 +00:00
|
|
|
|
2020-09-21 14:19:34 +00:00
|
|
|
template eth2testnet(path: string): Eth2NetworkMetadata =
|
|
|
|
loadEth2NetworkMetadata(currentSourcePath.parentDir / ".." / "vendor" / "eth2-testnets" / path)
|
2020-07-28 15:14:13 +00:00
|
|
|
|
2020-09-21 14:19:34 +00:00
|
|
|
const
|
|
|
|
medallaMetadata* = eth2testnet "shared/medalla"
|
2020-11-09 09:32:51 +00:00
|
|
|
toledoMetadata* = eth2testnet "shared/toledo"
|
2020-11-12 19:50:49 +00:00
|
|
|
pyrmontMetadata* = eth2testnet "shared/pyrmont"
|
2020-09-21 14:19:34 +00:00
|
|
|
testnet0Metadata* = eth2testnet "nimbus/testnet0"
|
|
|
|
testnet1Metadata* = eth2testnet "nimbus/testnet1"
|
2020-07-10 14:31:24 +00:00
|
|
|
|
2020-09-01 09:01:57 +00:00
|
|
|
{.pop.} # the following pocedures raise more than just `Defect`
|
|
|
|
|
|
|
|
proc getMetadataForNetwork*(networkName: string): Eth2NetworkMetadata =
|
|
|
|
let
|
|
|
|
metadata = case toLowerAscii(networkName)
|
|
|
|
of "mainnet":
|
|
|
|
mainnetMetadata
|
|
|
|
of "medalla":
|
|
|
|
medallaMetadata
|
2020-11-09 09:32:51 +00:00
|
|
|
of "toledo":
|
|
|
|
toledoMetadata
|
2020-11-12 19:50:49 +00:00
|
|
|
of "pyrmont":
|
|
|
|
pyrmontMetadata
|
2020-09-01 09:01:57 +00:00
|
|
|
of "testnet0":
|
|
|
|
testnet0Metadata
|
|
|
|
of "testnet1":
|
|
|
|
testnet1Metadata
|
|
|
|
else:
|
|
|
|
if fileExists(networkName):
|
|
|
|
try:
|
|
|
|
Json.loadFile(networkName, Eth2NetworkMetadata)
|
|
|
|
except SerializationError as err:
|
|
|
|
echo err.formatMsg(networkName)
|
|
|
|
quit 1
|
|
|
|
else:
|
|
|
|
fatal "Unrecognized network name", networkName
|
|
|
|
quit 1
|
|
|
|
|
|
|
|
if metadata.incompatible:
|
|
|
|
fatal "The selected network is not compatible with the current build",
|
|
|
|
reason = metadata.incompatibilityDesc
|
|
|
|
quit 1
|
|
|
|
return metadata
|
|
|
|
|
|
|
|
proc getRuntimePresetForNetwork*(eth2Network: Option[string]): RuntimePreset =
|
|
|
|
if eth2Network.isSome:
|
|
|
|
return getMetadataForNetwork(eth2Network.get).runtimePreset
|
|
|
|
return defaultRuntimePreset
|