Embed the Altona metadata in the NBC executable

This commit is contained in:
Zahary Karadjov 2020-07-02 18:14:11 +03:00
parent 76c12d493e
commit e342b96d2e
No known key found for this signature in database
GPG Key ID: C8936F8A3073D609
12 changed files with 158 additions and 40 deletions

5
.gitmodules vendored
View File

@ -178,3 +178,8 @@
url = https://github.com/status-im/nim-chronicles-tail.git url = https://github.com/status-im/nim-chronicles-tail.git
ignore = dirty ignore = dirty
branch = master branch = master
[submodule "vendor/eth2-testnets"]
path = vendor/eth2-testnets
url = https://github.com/eth2-clients/eth2-testnets.git
ignore = dirty
branch = master

View File

@ -23,7 +23,7 @@ import
conf, time, beacon_chain_db, validator_pool, extras, conf, time, beacon_chain_db, validator_pool, extras,
attestation_pool, block_pool, eth2_network, eth2_discovery, attestation_pool, block_pool, eth2_network, eth2_discovery,
beacon_node_common, beacon_node_types, block_pools/block_pools_types, beacon_node_common, beacon_node_types, block_pools/block_pools_types,
nimbus_binary_common, nimbus_binary_common, network_metadata,
mainchain_monitor, version, ssz/[merkleization], sszdump, mainchain_monitor, version, ssz/[merkleization], sszdump,
sync_protocol, request_manager, keystore_management, interop, statusbar, sync_protocol, request_manager, keystore_management, interop, statusbar,
sync_manager, validator_duties, validator_api, attestation_aggregation sync_manager, validator_duties, validator_api, attestation_aggregation
@ -148,7 +148,7 @@ proc init*(T: type BeaconNode, conf: BeaconNodeConf): Future[BeaconNode] {.async
fatal "Web3 URL not specified" fatal "Web3 URL not specified"
quit 1 quit 1
if conf.depositContractAddress.len == 0: if conf.depositContractAddress.isNone:
fatal "Deposit contract address not specified" fatal "Deposit contract address not specified"
quit 1 quit 1
@ -162,7 +162,7 @@ proc init*(T: type BeaconNode, conf: BeaconNodeConf): Future[BeaconNode] {.async
# that would do only this - see Paul's proposal for this. # that would do only this - see Paul's proposal for this.
mainchainMonitor = MainchainMonitor.init( mainchainMonitor = MainchainMonitor.init(
web3Provider(conf.web3Url), web3Provider(conf.web3Url),
conf.depositContractAddress, conf.depositContractAddress.get,
Eth1Data(block_hash: conf.depositContractDeployedAt.get, deposit_count: 0)) Eth1Data(block_hash: conf.depositContractDeployedAt.get, deposit_count: 0))
mainchainMonitor.start() mainchainMonitor.start()
@ -203,10 +203,10 @@ proc init*(T: type BeaconNode, conf: BeaconNodeConf): Future[BeaconNode] {.async
if mainchainMonitor.isNil and if mainchainMonitor.isNil and
conf.web3Url.len > 0 and conf.web3Url.len > 0 and
conf.depositContractAddress.len > 0: conf.depositContractAddress.isSome:
mainchainMonitor = MainchainMonitor.init( mainchainMonitor = MainchainMonitor.init(
web3Provider(conf.web3Url), web3Provider(conf.web3Url),
conf.depositContractAddress, conf.depositContractAddress.get,
blockPool.headState.data.data.eth1_data) blockPool.headState.data.data.eth1_data)
# TODO if we don't have any validators attached, we don't need a mainchain # TODO if we don't have any validators attached, we don't need a mainchain
# monitor # monitor
@ -1114,10 +1114,43 @@ proc createWalletInteractively(conf: BeaconNodeConf): OutFile {.raises: [Defect]
keystore_management.burnMem(confirmedPassword) keystore_management.burnMem(confirmedPassword)
programMain: programMain:
let config = makeBannerAndConfig(clientId, BeaconNodeConf) var config = makeBannerAndConfig(clientId, BeaconNodeConf)
setupMainProc(config.logLevel) setupMainProc(config.logLevel)
if config.eth2Network.isSome:
let
networkName = config.eth2Network.get
metadata = case toLowerAscii(networkName)
of "altona":
altonaMetadata
else:
if fileExists(networkName):
Json.loadFile(networkName, Eth2NetworkMetadata)
else:
fatal "Unrecognized network name", networkName
quit 1
if config.cmd == noCommand:
for node in metadata.bootstrapNodes:
config.bootstrapNodes.add node
template checkForIncompatibleOption(flagName, fieldName) =
# TODO: This will have to be reworked slightly when we introduce config files.
# We'll need to keep track of the "origin" of the config value, so we can
# discriminate between values from config files that can be overridden and
# regular command-line options (that may conflict).
if config.fieldName.isSome:
fatal "Invalid CLI arguments specified. You must not specify '--network' and '" & flagName & "' at the same time",
networkParam = networkName, `flagName` = config.fieldName.get
quit 1
checkForIncompatibleOption "deposit-contract", depositContractAddress
checkForIncompatibleOption "deposit-contract-block", depositContractDeployedAt
config.depositContractAddress = some metadata.depositContractAddress
config.depositContractDeployedAt = some metadata.depositContractDeployedAt
case config.cmd case config.cmd
of createTestnet: of createTestnet:
var var

View File

@ -49,6 +49,7 @@ type
topicAggregateAndProofs*: string topicAggregateAndProofs*: string
forwardSyncLoop*: Future[void] forwardSyncLoop*: Future[void]
onSecondLoop*: Future[void] onSecondLoop*: Future[void]
genesisSnapshotContent*: string
const const
MaxEmptySlotCount* = uint64(10*60) div SECONDS_PER_SLOT MaxEmptySlotCount* = uint64(10*60) div SECONDS_PER_SLOT

View File

@ -2,13 +2,14 @@
import import
os, options, os, options,
chronicles, confutils, json_serialization, chronicles, chronicles/options as chroniclesOptions,
confutils/defs, confutils/std/net, confutils, confutils/defs, confutils/std/net,
chronicles/options as chroniclesOptions, json_serialization, web3/ethtypes,
spec/[crypto, keystore, digest] network_metadata, spec/[crypto, keystore, digest]
export export
defs, enabledLogLevel, parseCmdArg, completeCmdArg defs, enabledLogLevel, parseCmdArg, completeCmdArg,
network_metadata
type type
ValidatorKeyPath* = TypedInputFile[ValidatorPrivKey, Txt, "privkey"] ValidatorKeyPath* = TypedInputFile[ValidatorPrivKey, Txt, "privkey"]
@ -32,11 +33,7 @@ type
VCStartUpCmd* = enum VCStartUpCmd* = enum
VCNoCommand VCNoCommand
Eth1Network* = enum Web3Url* = distinct string
custom
mainnet
rinkeby
goerli
BeaconNodeConf* = object BeaconNodeConf* = object
logLevel* {. logLevel* {.
@ -44,10 +41,9 @@ type
desc: "Sets the log level" desc: "Sets the log level"
name: "log-level" }: string name: "log-level" }: string
eth1Network* {. eth2Network* {.
defaultValue: goerli desc: "The Eth2 network to join"
desc: "The Eth1 network tracked by the beacon node" name: "network" }: Option[string]
name: "eth1-network" }: Eth1Network
dataDir* {. dataDir* {.
defaultValue: config.defaultDataDir() defaultValue: config.defaultDataDir()
@ -61,9 +57,8 @@ type
name: "web3-url" }: string name: "web3-url" }: string
depositContractAddress* {. depositContractAddress* {.
defaultValue: ""
desc: "Address of the deposit contract" desc: "Address of the deposit contract"
name: "deposit-contract" }: string name: "deposit-contract" }: Option[Eth1Address]
depositContractDeployedAt* {. depositContractDeployedAt* {.
desc: "The Eth1 block hash where the deposit contract has been deployed" desc: "The Eth1 block hash where the deposit contract has been deployed"
@ -412,6 +407,13 @@ proc createDumpDirs*(conf: BeaconNodeConf) =
# Dumping is mainly a debugging feature, so ignore these.. # Dumping is mainly a debugging feature, so ignore these..
warn "Cannot create dump directories", msg = err.msg warn "Cannot create dump directories", msg = err.msg
func parseCmdArg*(T: type Eth1Address, input: TaintedString): T
{.raises: [ValueError, Defect].} =
fromHex(T, string input)
func completeCmdArg*(T: type Eth1Address, input: TaintedString): seq[string] =
return @[]
func parseCmdArg*(T: type Eth2Digest, input: TaintedString): T func parseCmdArg*(T: type Eth2Digest, input: TaintedString): T
{.raises: [ValueError, Defect].} = {.raises: [ValueError, Defect].} =
fromHex(T, string input) fromHex(T, string input)
@ -453,3 +455,4 @@ func defaultAdminListenAddress*(conf: BeaconNodeConf|ValidatorClientConf): Valid
template writeValue*(writer: var JsonWriter, template writeValue*(writer: var JsonWriter,
value: TypedInputFile|InputFile|InputDir|OutPath|OutDir|OutFile) = value: TypedInputFile|InputFile|InputDir|OutPath|OutDir|OutFile) =
writer.writeValue(string value) writer.writeValue(string value)

View File

@ -75,12 +75,12 @@ proc main() {.async.} =
case cfg.cmd case cfg.cmd
of StartUpCommand.deploy: of StartUpCommand.deploy:
let receipt = await web3.deployContract(contractCode) let receipt = await web3.deployContract(contractCode)
echo "0x", receipt.contractAddress.get, ";", receipt.blockHash echo receipt.contractAddress.get, ";", receipt.blockHash
of StartUpCommand.drain: of StartUpCommand.drain:
let sender = web3.contractSender(Deposit, Address.fromHex(cfg.contractAddress)) let sender = web3.contractSender(Deposit, Address.fromHex(cfg.contractAddress))
discard await sender.drain().send(gasPrice = 1) discard await sender.drain().send(gasPrice = 1)
of StartUpCommand.sendEth: of StartUpCommand.sendEth:
echo "0x", await sendEth(web3, cfg.toAddress, cfg.valueEth.parseInt) echo await sendEth(web3, cfg.toAddress, cfg.valueEth.parseInt)
when isMainModule: waitFor main() when isMainModule: waitFor main()

View File

@ -1,7 +1,7 @@
import import
os, strutils, terminal, os, strutils, terminal,
chronicles, chronos, blscurve, nimcrypto, json_serialization, serialization, chronicles, chronos, blscurve, nimcrypto, json_serialization, serialization,
web3, stint, eth/keys, confutils, web3, stint, eth/common/eth_types, eth/keys, confutils,
spec/[datatypes, digest, crypto, keystore], conf, ssz/merkleization, merkle_minimal spec/[datatypes, digest, crypto, keystore], conf, ssz/merkleization, merkle_minimal
export export
@ -15,6 +15,7 @@ const
depositFileName* = "deposit.json" depositFileName* = "deposit.json"
type type
Eth1Address* = eth_types.EthAddress
DelayGenerator* = proc(): chronos.Duration {.closure, gcsafe.} DelayGenerator* = proc(): chronos.Duration {.closure, gcsafe.}
{.push raises: [Defect].} {.push raises: [Defect].}
@ -165,7 +166,8 @@ proc loadDeposits*(depositsDir: string): seq[Deposit] =
# TODO: async functions should note take `seq` inputs because # TODO: async functions should note take `seq` inputs because
# this leads to full copies. # this leads to full copies.
proc sendDeposits*(deposits: seq[Deposit], proc sendDeposits*(deposits: seq[Deposit],
web3Url, depositContractAddress, privateKey: string, web3Url, privateKey: string,
depositContractAddress: Eth1Address,
delayGenerator: DelayGenerator = nil) {.async.} = delayGenerator: DelayGenerator = nil) {.async.} =
var web3 = await newWeb3(web3Url) var web3 = await newWeb3(web3Url)
if privateKey.len != 0: if privateKey.len != 0:
@ -177,8 +179,8 @@ proc sendDeposits*(deposits: seq[Deposit],
return return
web3.defaultAccount = accounts[0] web3.defaultAccount = accounts[0]
let contractAddress = Address.fromHex(depositContractAddress) let depositContract = web3.contractSender(DepositContract,
let depositContract = web3.contractSender(DepositContract, contractAddress) Address depositContractAddress)
for i, dp in deposits: for i, dp in deposits:
let status = await depositContract.deposit( let status = await depositContract.deposit(
@ -202,7 +204,7 @@ proc sendDeposits*(config: BeaconNodeConf,
await sendDeposits( await sendDeposits(
deposits, deposits,
config.web3Url, config.web3Url,
config.depositContractAddress,
config.depositPrivateKey, config.depositPrivateKey,
config.depositContractAddress.get,
delayGenerator) delayGenerator)

View File

@ -1,13 +1,14 @@
import import
deques, tables, hashes, options, strformat, deques, tables, hashes, options, strformat,
chronos, web3, web3/ethtypes, json, chronicles, eth/async_utils, chronos, web3, web3/ethtypes as web3Types, json, chronicles,
eth/common/eth_types, eth/async_utils,
spec/[datatypes, digest, crypto, beaconstate, helpers], spec/[datatypes, digest, crypto, beaconstate, helpers],
merkle_minimal merkle_minimal
from times import epochTime from times import epochTime
export export
ethtypes web3Types
contract(DepositContract): contract(DepositContract):
proc deposit(pubkey: Bytes48, proc deposit(pubkey: Bytes48,
@ -28,8 +29,10 @@ contract(DepositContract):
# Exceptions being reported from Chronos's asyncfutures2. # Exceptions being reported from Chronos's asyncfutures2.
type type
Eth1Address = eth_types.EthAddress
Eth1BlockNumber* = uint64 Eth1BlockNumber* = uint64
Eth1BlockTimestamp* = uint64 Eth1BlockTimestamp* = uint64
Eth1BlockHeader = web3Types.BlockHeader
Eth1Block* = ref object Eth1Block* = ref object
number*: Eth1BlockNumber number*: Eth1BlockNumber
@ -56,7 +59,7 @@ type
eth1Chain: Eth1Chain eth1Chain: Eth1Chain
depositQueue: AsyncQueue[BlockHeader] depositQueue: AsyncQueue[Eth1BlockHeader]
runFut: Future[void] runFut: Future[void]
DataProvider* = object of RootObj DataProvider* = object of RootObj
@ -458,11 +461,11 @@ template getBlockProposalData*(m: MainchainMonitor, state: BeaconState): untyped
proc init*(T: type MainchainMonitor, proc init*(T: type MainchainMonitor,
dataProviderFactory: DataProviderFactory, dataProviderFactory: DataProviderFactory,
depositContractAddress: string, depositContractAddress: Eth1Address,
startPosition: Eth1Data): T = startPosition: Eth1Data): T =
T(depositQueue: newAsyncQueue[BlockHeader](), T(depositQueue: newAsyncQueue[Eth1BlockHeader](),
dataProviderFactory: dataProviderFactory, dataProviderFactory: dataProviderFactory,
depositContractAddress: Address.fromHex(depositContractAddress), depositContractAddress: Address depositContractAddress,
eth1Chain: Eth1Chain(knownStart: startPosition)) eth1Chain: Eth1Chain(knownStart: startPosition))
proc isCandidateForGenesis(timeNow: float, blk: Eth1Block): bool = proc isCandidateForGenesis(timeNow: float, blk: Eth1Block): bool =
@ -742,7 +745,7 @@ proc run(m: MainchainMonitor, delayBeforeStart: Duration) {.async.} =
contract = $m.depositContractAddress, contract = $m.depositContractAddress,
url = m.dataProviderFactory.desc url = m.dataProviderFactory.desc
await dataProvider.onBlockHeaders do (blk: BlockHeader) await dataProvider.onBlockHeaders do (blk: Eth1BlockHeader)
{.raises: [Defect], gcsafe}: {.raises: [Defect], gcsafe}:
try: try:
m.depositQueue.addLastNoWait(blk) m.depositQueue.addLastNoWait(blk)

View File

@ -0,0 +1,70 @@
import
os, strutils,
stew/byteutils, nimcrypto/hash,
eth/common/[eth_types, eth_types_json_serialization]
# 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.
#
# TODO:
# We can compress the embedded states with snappy before embedding them here.
{.push raises: [Defect].}
export
eth_types_json_serialization
type
Eth1Address* = eth_types.EthAddress
Eth1BlockHash* = eth_types.Hash256
Eth1Network* = enum
mainnet
rinkeby
goerli
Eth2Network* = enum
customEth2Network
altona
Eth2NetworkMetadata* = object
eth1Network*: Eth1Network
# Parsing `enr.Records` is still not possible at compile-time
bootstrapNodes*: seq[string]
depositContractAddress*: Eth1Address
depositContractDeployedAt*: Eth1BlockHash
# 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
proc loadEth2NetworkMetadata*(path: string): Eth2NetworkMetadata
{.raises: [CatchableError, Defect].} =
let
genesisPath = path / "genesis.ssz"
Eth2NetworkMetadata(
eth1Network: goerli,
bootstrapNodes: readFile(path / "bootstrap_nodes.txt").split("\n"),
depositContractAddress: Eth1Address.fromHex readFile(path / "deposit_contract.txt").strip,
depositContractDeployedAt: Eth1BlockHash.fromHex readFile(path / "deposit_contract_block.txt").strip,
genesisData: if fileExists(genesisPath): readFile(genesisPath) else: "")
const
altonaMetadata* = loadEth2NetworkMetadata(
currentSourcePath.parentDir / ".." / "vendor" / "eth2-testnets" / "shared" / "altona")

1
vendor/eth2-testnets vendored Submodule

@ -0,0 +1 @@
Subproject commit 61d94737812fc40438b70ea49a6a87d17c2f3d4c

@ -1 +1 @@
Subproject commit a76faa5eec2454309753bbe99a68fb3cc25782f1 Subproject commit f9415621f87287524d26aa99a94e2613b237cc3c

2
vendor/nim-stew vendored

@ -1 +1 @@
Subproject commit 152eb1b58cd618a175dc2ae6fba39e620115c356 Subproject commit 61d5cfc37677f2b434d43c06d06695b00e56613b

2
vendor/nim-web3 vendored

@ -1 +1 @@
Subproject commit a75519fe1264ea861fb65eb2ffec1d6566ebd033 Subproject commit 227927ddc80e7d6bd432f70fe8c803286b46e770