mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-01-11 14:54:12 +00:00
Fix the corrupted database state on Pyrmont nodes; Add mainnet genesis (#2056)
* Handle some web3 timeouts better * Add support for developer .env files * Eth1 improvements; Mainnet genesis state Notable changes: * The deposits table have been removed from the database. The client will no longer process all deposits on start-up. * The network metadata now includes a "state snapshot" of the deposit contract. This allows the client to skip syncing deposits made prior to the snapshot (i.e. genesis). Suitable metadata added for Pyrmont and Mainnet. * The Eth1 monitor won't be started unless there are validators attached to the node. * The genesis detection code is now optional and disabled by default * Bugfix: The client should not produce blocks that will fail validation when it hasn't downloaded the latest deposits yet * Bugfix: Work around the database corruption affecting Pyrmont nodes * Remove metadata for Toledo and Medalla
This commit is contained in:
parent
7b7dc6fed4
commit
372c9b798c
4
.gitignore
vendored
4
.gitignore
vendored
@ -12,6 +12,10 @@ build/
|
||||
# vscode
|
||||
.vscode
|
||||
|
||||
# Each developer can create a personal .env file with
|
||||
# local settings overrides (e.g. WEB3_URL)
|
||||
.env
|
||||
|
||||
# Ignore dynamic, static libs and libtool archive files
|
||||
*.so
|
||||
*.dylib
|
||||
|
23
Makefile
23
Makefile
@ -291,29 +291,6 @@ define CLEAN_NETWORK
|
||||
rm -rf build/data/shared_$(1)*/*.log
|
||||
endef
|
||||
|
||||
###
|
||||
### toledo
|
||||
###
|
||||
toledo-build: | nimbus_beacon_node nimbus_signing_process
|
||||
|
||||
# https://www.gnu.org/software/make/manual/html_node/Call-Function.html#Call-Function
|
||||
toledo: | toledo-build
|
||||
$(call CONNECT_TO_NETWORK,toledo,nimbus_beacon_node)
|
||||
|
||||
toledo-vc: | toledo-build nimbus_validator_client
|
||||
$(call CONNECT_TO_NETWORK_WITH_VALIDATOR_CLIENT,toledo,nimbus_beacon_node)
|
||||
|
||||
ifneq ($(LOG_LEVEL), TRACE)
|
||||
toledo-dev:
|
||||
+ "$(MAKE)" LOG_LEVEL=TRACE $@
|
||||
else
|
||||
toledo-dev: | toledo-build
|
||||
$(call CONNECT_TO_NETWORK_IN_DEV_MODE,toledo,nimbus_beacon_node)
|
||||
endif
|
||||
|
||||
clean-toledo:
|
||||
$(call CLEAN_NETWORK,toledo)
|
||||
|
||||
###
|
||||
### pyrmont
|
||||
###
|
||||
|
@ -6,7 +6,7 @@ import
|
||||
serialization, chronicles, snappy,
|
||||
eth/db/[kvstore, kvstore_sqlite3],
|
||||
./network_metadata,
|
||||
./spec/[datatypes, digest, crypto, state_transition, signatures],
|
||||
./spec/[datatypes, digest, crypto, state_transition],
|
||||
./ssz/[ssz_serialization, merkleization],
|
||||
merkle_minimal, filepath
|
||||
|
||||
@ -26,6 +26,10 @@ type
|
||||
|
||||
DepositsMerkleizer* = SszMerkleizer[depositContractLimit]
|
||||
|
||||
DepositContractSnapshot* = object
|
||||
eth1Block*: Eth2Digest
|
||||
depositContractState*: DepositContractState
|
||||
|
||||
BeaconChainDB* = ref object
|
||||
## Database storing resolved blocks and states - resolved blocks are such
|
||||
## blocks that form a chain back to the tail block.
|
||||
@ -41,20 +45,7 @@ type
|
||||
## database.
|
||||
backend: KvStoreRef
|
||||
preset: RuntimePreset
|
||||
|
||||
deposits*: DepositsSeq
|
||||
immutableValidatorData*: ImmutableValidatorDataSeq
|
||||
validatorKeyToIndex*: ValidatorKeyToIndexMap
|
||||
|
||||
finalizedEth1DepositsMerkleizer*: DepositsMerkleizer
|
||||
## A merkleizer keeping track of the `deposit_root` value obtained from
|
||||
## Eth1 after finalizing blocks with ETH1_FOLLOW_DISTANCE confirmations.
|
||||
## The value is used when voting for Eth1 heads.
|
||||
|
||||
finalizedEth2DepositsMerkleizer*: DepositsMerkleizer
|
||||
## A separate merkleizer which is advanced when the Eth2 chain finalizes.
|
||||
## It will lag behind the "eth1 merkleizer". We use to produce merkle
|
||||
## proofs for deposits when they are added to Eth2 blocks.
|
||||
genesisDeposits*: DepositsSeq
|
||||
|
||||
Keyspaces* = enum
|
||||
defaultKeyspace = "kvstore"
|
||||
@ -77,19 +68,27 @@ type
|
||||
## Immutable reference to the network genesis state
|
||||
## (needed for satisfying requests to the beacon node API).
|
||||
kEth1PersistedTo
|
||||
## The latest ETH1 block hash which satisfied the follow distance and
|
||||
## had its deposits persisted to disk.
|
||||
kFinalizedEth1DepositsMarkleizer
|
||||
## A merkleizer used to compute the `deposit_root` of all finalized
|
||||
## deposits (i.e. deposits confirmed by ETH1_FOLLOW_DISTANCE blocks)
|
||||
kFinalizedEth2DepositsMarkleizer
|
||||
## A merkleizer used for computing merkle proofs of deposits added
|
||||
## to Eth2 blocks (it may lag behind the finalized deposits merkleizer).
|
||||
## (Obsolete) Used to point to the the latest ETH1 block hash which
|
||||
## satisfied the follow distance and had its deposits persisted to disk.
|
||||
kDepositsFinalizedByEth1
|
||||
## A merkleizer checkpoint which can be used for computing the
|
||||
## `deposit_root` of all eth1 finalized deposits (i.e. deposits
|
||||
## confirmed by ETH1_FOLLOW_DISTANCE blocks). The `deposit_root`
|
||||
## is acknowledged and confirmed by the attached web3 provider.
|
||||
kDepositsFinalizedByEth2
|
||||
## A merkleizer checkpoint used for computing merkle proofs of
|
||||
## deposits added to Eth2 blocks (it may lag behind the finalized
|
||||
## eth1 deposits checkpoint).
|
||||
kHashToBlockSummary
|
||||
## Cache of beacon block summaries - during startup when we construct the
|
||||
## chain dag, loading full blocks takes a lot of time - the block
|
||||
## summary contains a minimal snapshot of what's needed to instanciate
|
||||
## the BlockRef tree.
|
||||
kSpeculativeDeposits
|
||||
## A merkelizer checkpoint created on the basis of deposit events
|
||||
## that we were not able to verify against a `deposit_root` served
|
||||
## by the web3 provider. This may happen on Geth nodes that serve
|
||||
## only recent contract state data (i.e. only recent `deposit_roots`).
|
||||
|
||||
BeaconBlockSummary* = object
|
||||
slot*: Slot
|
||||
@ -156,7 +155,7 @@ proc init*[T](Seq: type DbSeq[T], db: SqStoreRef, name: string): Seq =
|
||||
int64, openArray[byte]).expect("this is a valid statement")
|
||||
|
||||
countStmt = db.prepareStmt(
|
||||
"SELECT COUNT(*) FROM " & name & ";",
|
||||
"SELECT COUNT(1) FROM " & name & ";",
|
||||
NoParams, int64).expect("this is a valid statement")
|
||||
|
||||
var recordCount = int64 0
|
||||
@ -204,34 +203,6 @@ proc contains*[K, V](m: DbMap[K, V], key: K): bool =
|
||||
template insert*[K, V](t: var Table[K, V], key: K, value: V) =
|
||||
add(t, key, value)
|
||||
|
||||
proc produceDerivedData(deposit: DepositData,
|
||||
preset: RuntimePreset,
|
||||
validators: var ImmutableValidatorDataSeq,
|
||||
validatorKeyToIndex: var ValidatorKeyToIndexMap,
|
||||
finalizedEth1DepositsMerkleizer: var DepositsMerkleizer,
|
||||
skipBlsCheck = false) =
|
||||
let htr = hash_tree_root(deposit)
|
||||
finalizedEth1DepositsMerkleizer.addChunk htr.data
|
||||
|
||||
if skipBlsCheck or verify_deposit_signature(preset, deposit):
|
||||
let pubkey = deposit.pubkey
|
||||
if pubkey notin validatorKeyToIndex:
|
||||
let idx = ValidatorIndex validators.len
|
||||
validators.add ImmutableValidatorData(
|
||||
pubkey: pubkey,
|
||||
withdrawal_credentials: deposit.withdrawal_credentials)
|
||||
validatorKeyToIndex.insert(pubkey, idx)
|
||||
|
||||
proc processDeposit*(db: BeaconChainDB, newDeposit: DepositData) =
|
||||
db.deposits.add newDeposit
|
||||
|
||||
produceDerivedData(
|
||||
newDeposit,
|
||||
db.preset,
|
||||
db.immutableValidatorData,
|
||||
db.validatorKeyToIndex,
|
||||
db.finalizedEth1DepositsMerkleizer)
|
||||
|
||||
proc init*(T: type BeaconChainDB,
|
||||
preset: RuntimePreset,
|
||||
dir: string,
|
||||
@ -242,9 +213,7 @@ proc init*(T: type BeaconChainDB,
|
||||
# functionalityof the database-backed one (i.e. tracking of deposits
|
||||
# and validators)
|
||||
T(backend: kvStore MemStoreRef.init(),
|
||||
preset: preset,
|
||||
finalizedEth1DepositsMerkleizer: init DepositsMerkleizer,
|
||||
finalizedEth2DepositsMerkleizer: init DepositsMerkleizer)
|
||||
preset: preset)
|
||||
else:
|
||||
let s = secureCreatePath(dir)
|
||||
doAssert s.isOk # TODO(zah) Handle this in a better way
|
||||
@ -252,43 +221,21 @@ proc init*(T: type BeaconChainDB,
|
||||
let sqliteStore = SqStoreRef.init(dir, "nbc", Keyspaces).expect(
|
||||
"working database")
|
||||
|
||||
# Remove the deposits table we used before we switched
|
||||
# to storing only deposit contract checkpoints
|
||||
if sqliteStore.exec("DROP TABLE IF EXISTS deposits;").isErr:
|
||||
debug "Failed to drop the deposits table"
|
||||
|
||||
var
|
||||
immutableValidatorData = newSeq[ImmutableValidatorData]()
|
||||
validatorKeyToIndex = initTable[ValidatorPubKey, ValidatorIndex]()
|
||||
depositsSeq = DbSeq[DepositData].init(sqliteStore, "deposits")
|
||||
finalizedEth1DepositsMerkleizer = init DepositsMerkleizer
|
||||
finalizedEth2DepositsMerkleizer = init DepositsMerkleizer
|
||||
genesisDepositsSeq = DbSeq[DepositData].init(sqliteStore, "genesis_deposits")
|
||||
|
||||
let isPyrmont =
|
||||
not pyrmontMetadata.incompatible and preset == pyrmontMetadata.runtimePreset
|
||||
|
||||
for i in 0 ..< depositsSeq.len:
|
||||
# TODO this is a hack to avoid long startup times on pyrmont - it should
|
||||
# be removed when the storage of deposits is fixed. It works because
|
||||
# we know that he first 100k deposits on pyrmont have a valid
|
||||
# signature
|
||||
let skipBlsCheck = isPyrmont and i < 100010
|
||||
|
||||
produceDerivedData(
|
||||
depositsSeq.get(i),
|
||||
preset,
|
||||
immutableValidatorData,
|
||||
validatorKeyToIndex,
|
||||
finalizedEth1DepositsMerkleizer, skipBlsCheck)
|
||||
|
||||
T(backend: kvStore sqliteStore,
|
||||
preset: preset,
|
||||
deposits: depositsSeq,
|
||||
immutableValidatorData: immutableValidatorData,
|
||||
validatorKeyToIndex: validatorKeyToIndex,
|
||||
finalizedEth1DepositsMerkleizer: finalizedEth1DepositsMerkleizer,
|
||||
finalizedEth2DepositsMerkleizer: finalizedEth2DepositsMerkleizer)
|
||||
|
||||
proc advanceTo*(merkleizer: var DepositsMerkleizer,
|
||||
db: BeaconChainDB,
|
||||
deposit_index: uint64) =
|
||||
for i in merkleizer.totalChunks ..< depositIndex:
|
||||
merkleizer.addChunk hash_tree_root(db.deposits.get(i)).data
|
||||
genesisDeposits: genesisDepositsSeq)
|
||||
|
||||
proc snappyEncode(inp: openArray[byte]): seq[byte] =
|
||||
try:
|
||||
@ -319,9 +266,9 @@ proc get(db: BeaconChainDB, key: openArray[byte], T: type Eth2Digest): Opt[T] =
|
||||
res
|
||||
|
||||
type GetResult = enum
|
||||
found
|
||||
notFound
|
||||
corrupted
|
||||
found = "Found"
|
||||
notFound = "Not found"
|
||||
corrupted = "Corrupted"
|
||||
|
||||
proc get[T](db: BeaconChainDB, key: openArray[byte], output: var T): GetResult =
|
||||
var status = GetResult.notFound
|
||||
@ -397,8 +344,17 @@ proc putTailBlock*(db: BeaconChainDB, key: Eth2Digest) =
|
||||
proc putGenesisBlockRoot*(db: BeaconChainDB, key: Eth2Digest) =
|
||||
db.put(subkey(kGenesisBlockRoot), key)
|
||||
|
||||
proc putEth1PersistedTo*(db: BeaconChainDB, key: Eth1Data) =
|
||||
db.put(subkey(kEth1PersistedTo), key)
|
||||
proc putEth1FinalizedTo*(db: BeaconChainDB,
|
||||
eth1Checkpoint: DepositContractSnapshot) =
|
||||
db.put(subkey(kDepositsFinalizedByEth1), eth1Checkpoint)
|
||||
|
||||
proc putEth2FinalizedTo*(db: BeaconChainDB,
|
||||
eth1Checkpoint: DepositContractSnapshot) =
|
||||
db.put(subkey(kDepositsFinalizedByEth2), eth1Checkpoint)
|
||||
|
||||
proc putSpeculativeDeposits*(db: BeaconChainDB,
|
||||
eth1Checkpoint: DepositContractSnapshot) =
|
||||
db.put(subkey(kSpeculativeDeposits), eth1Checkpoint)
|
||||
|
||||
proc getBlock*(db: BeaconChainDB, key: Eth2Digest): Opt[TrustedSignedBeaconBlock] =
|
||||
# We only store blocks that we trust in the database
|
||||
@ -452,10 +408,23 @@ proc getGenesisBlockRoot*(db: BeaconChainDB): Eth2Digest =
|
||||
db.get(subkey(kGenesisBlockRoot), Eth2Digest).expect(
|
||||
"The database must be seeded with the genesis state")
|
||||
|
||||
proc getEth1PersistedTo*(db: BeaconChainDB): Opt[Eth1Data] =
|
||||
result.ok(Eth1Data())
|
||||
if db.get(subkey(kEth1PersistedTo), result.get) != GetResult.found:
|
||||
result.err()
|
||||
proc getEth1FinalizedTo*(db: BeaconChainDB): Opt[DepositContractSnapshot] =
|
||||
result.ok(DepositContractSnapshot())
|
||||
let r = db.get(subkey(kDepositsFinalizedByEth1), result.get)
|
||||
if r != found: result.err()
|
||||
|
||||
proc getEth2FinalizedTo*(db: BeaconChainDB): Opt[DepositContractSnapshot] =
|
||||
result.ok(DepositContractSnapshot())
|
||||
let r = db.get(subkey(kDepositsFinalizedByEth2), result.get)
|
||||
if r != found: result.err()
|
||||
|
||||
proc getSpeculativeDeposits*(db: BeaconChainDB): Opt[DepositContractSnapshot] =
|
||||
result.ok(DepositContractSnapshot())
|
||||
let r = db.get(subkey(kSpeculativeDeposits), result.get)
|
||||
if r != found: result.err()
|
||||
|
||||
proc delSpeculativeDeposits*(db: BeaconChainDB) =
|
||||
db.backend.del(subkey(kSpeculativeDeposits)).expect("working database")
|
||||
|
||||
proc containsBlock*(db: BeaconChainDB, key: Eth2Digest): bool =
|
||||
db.backend.contains(subkey(SignedBeaconBlock, key)).expect("working database")
|
||||
|
@ -140,6 +140,7 @@ type
|
||||
current_justified_checkpoint*: Checkpoint
|
||||
finalized_checkpoint*: Checkpoint
|
||||
eth1_data*: Eth1Data
|
||||
eth1_deposit_index*: uint64
|
||||
beacon_proposers*: array[
|
||||
SLOTS_PER_EPOCH, Option[(ValidatorIndex, ValidatorPubKey)]]
|
||||
shuffled_active_validator_indices*: seq[ValidatorIndex]
|
||||
|
@ -95,6 +95,7 @@ proc init*(
|
||||
epochRef = EpochRef(
|
||||
epoch: epoch,
|
||||
eth1_data: state.eth1_data,
|
||||
eth1_deposit_index: state.eth1_deposit_index,
|
||||
current_justified_checkpoint: state.current_justified_checkpoint,
|
||||
finalized_checkpoint: state.finalized_checkpoint,
|
||||
shuffled_active_validator_indices:
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -63,9 +63,13 @@ type
|
||||
# `genesisData` will have `len == 0` for networks with a still
|
||||
# unknown genesis state.
|
||||
genesisData*: string
|
||||
genesisDepositsSnapshot*: string
|
||||
else:
|
||||
incompatibilityDesc*: string
|
||||
|
||||
const
|
||||
eth2testnetsDir = currentSourcePath.parentDir / ".." / "vendor" / "eth2-testnets"
|
||||
|
||||
const presetValueLoaders = genExpr(nnkBracket):
|
||||
for constName in PresetValue:
|
||||
let
|
||||
@ -104,6 +108,7 @@ proc loadEth2NetworkMetadata*(path: string): Eth2NetworkMetadata
|
||||
try:
|
||||
let
|
||||
genesisPath = path / "genesis.ssz"
|
||||
genesisDepositsSnapshotPath = path / "genesis_deposit_contract_snapshot.ssz"
|
||||
configPath = path / "config.yaml"
|
||||
depositContractPath = path / "deposit_contract.txt"
|
||||
depositContractBlockPath = path / "deposit_contract_block.txt"
|
||||
@ -134,8 +139,15 @@ proc loadEth2NetworkMetadata*(path: string): Eth2NetworkMetadata
|
||||
else:
|
||||
@[]
|
||||
|
||||
genesisData = if fileExists(genesisPath): readFile(genesisPath)
|
||||
else: ""
|
||||
genesisData = if fileExists(genesisPath):
|
||||
readFile(genesisPath)
|
||||
else:
|
||||
""
|
||||
|
||||
genesisDepositsSnapshot = if fileExists(genesisDepositsSnapshotPath):
|
||||
readFile(genesisDepositsSnapshotPath)
|
||||
else:
|
||||
""
|
||||
|
||||
Eth2NetworkMetadata(
|
||||
incompatible: false,
|
||||
@ -144,24 +156,27 @@ proc loadEth2NetworkMetadata*(path: string): Eth2NetworkMetadata
|
||||
bootstrapNodes: bootstrapNodes,
|
||||
depositContractAddress: depositContractAddress,
|
||||
depositContractDeployedAt: depositContractDeployedAt,
|
||||
genesisData: genesisData)
|
||||
genesisData: genesisData,
|
||||
genesisDepositsSnapshot: genesisDepositsSnapshot)
|
||||
|
||||
except PresetIncompatible as err:
|
||||
Eth2NetworkMetadata(incompatible: true,
|
||||
incompatibilityDesc: err.msg)
|
||||
|
||||
const
|
||||
mainnetMetadataDir = eth2testnetsDir / "shared" / "mainnet"
|
||||
|
||||
mainnetMetadata* = when const_preset == "mainnet":
|
||||
Eth2NetworkMetadata(
|
||||
incompatible: false, # TODO: This can be more accurate if we verify
|
||||
# that there are no constant overrides
|
||||
eth1Network: some mainnet,
|
||||
runtimePreset: mainnetRuntimePreset,
|
||||
# TODO(zah) Add bootstrap nodes for mainnet
|
||||
bootstrapNodes: @[],
|
||||
bootstrapNodes: readFile(mainnetMetadataDir / "bootstrap_nodes.txt").splitLines,
|
||||
depositContractAddress: Eth1Address.fromHex "0x00000000219ab540356cBB839Cbe05303d7705Fa",
|
||||
depositContractDeployedAt: BlockHashOrNumber.init "11052984",
|
||||
genesisData: "")
|
||||
genesisData: readFile(mainnetMetadataDir / "genesis.ssz"),
|
||||
genesisDepositsSnapshot: readFile(mainnetMetadataDir / "genesis_deposit_contract_snapshot.ssz"))
|
||||
else:
|
||||
Eth2NetworkMetadata(
|
||||
incompatible: true,
|
||||
@ -169,14 +184,10 @@ const
|
||||
"It's not compatible with mainnet")
|
||||
|
||||
template eth2testnet(path: string): Eth2NetworkMetadata =
|
||||
loadEth2NetworkMetadata(currentSourcePath.parentDir / ".." / "vendor" / "eth2-testnets" / path)
|
||||
loadEth2NetworkMetadata(eth2testnetsDir / path)
|
||||
|
||||
const
|
||||
medallaMetadata* = eth2testnet "shared/medalla"
|
||||
toledoMetadata* = eth2testnet "shared/toledo"
|
||||
pyrmontMetadata* = eth2testnet "shared/pyrmont"
|
||||
testnet0Metadata* = eth2testnet "nimbus/testnet0"
|
||||
testnet1Metadata* = eth2testnet "nimbus/testnet1"
|
||||
|
||||
{.pop.} # the following pocedures raise more than just `Defect`
|
||||
|
||||
@ -185,16 +196,8 @@ proc getMetadataForNetwork*(networkName: string): Eth2NetworkMetadata =
|
||||
metadata = case toLowerAscii(networkName)
|
||||
of "mainnet":
|
||||
mainnetMetadata
|
||||
of "medalla":
|
||||
medallaMetadata
|
||||
of "toledo":
|
||||
toledoMetadata
|
||||
of "pyrmont":
|
||||
pyrmontMetadata
|
||||
of "testnet0":
|
||||
testnet0Metadata
|
||||
of "testnet1":
|
||||
testnet1Metadata
|
||||
else:
|
||||
if fileExists(networkName):
|
||||
try:
|
||||
|
@ -70,13 +70,15 @@ func enrForkIdFromState(state: BeaconState): ENRForkID =
|
||||
proc init*(T: type BeaconNode,
|
||||
rng: ref BrHmacDrbgContext,
|
||||
conf: BeaconNodeConf,
|
||||
eth1Network: Option[Eth1Network],
|
||||
genesisStateContents: ref string,
|
||||
eth1Network: Option[Eth1Network]): Future[BeaconNode] {.async.} =
|
||||
genesisDepositsSnapshotContents: ref string): Future[BeaconNode] {.async.} =
|
||||
let
|
||||
db = BeaconChainDB.init(conf.runtimePreset, conf.databaseDir)
|
||||
|
||||
var
|
||||
genesisState, checkpointState: ref BeaconState
|
||||
genesisDepositsSnapshot: DepositContractSnapshot
|
||||
checkpointBlock: SignedBeaconBlock
|
||||
|
||||
if conf.finalizedCheckpointState.isSome:
|
||||
@ -118,54 +120,63 @@ proc init*(T: type BeaconNode,
|
||||
tailBlock: SignedBeaconBlock
|
||||
|
||||
if genesisStateContents == nil and checkpointState == nil:
|
||||
# This is a fresh start without a known genesis state
|
||||
# (most likely, it hasn't arrived yet). We'll try to
|
||||
# obtain a genesis through the Eth1 deposits monitor:
|
||||
if conf.web3Url.len == 0:
|
||||
fatal "Web3 URL not specified"
|
||||
quit 1
|
||||
when hasGenesisDetection:
|
||||
if genesisDepositsSnapshotContents != nil:
|
||||
fatal "A deposits snapshot cannot be provided without also providing a matching beacon state snapshot"
|
||||
quit 1
|
||||
|
||||
if conf.depositContractAddress.isNone:
|
||||
fatal "Deposit contract address not specified"
|
||||
quit 1
|
||||
# This is a fresh start without a known genesis state
|
||||
# (most likely, it hasn't arrived yet). We'll try to
|
||||
# obtain a genesis through the Eth1 deposits monitor:
|
||||
if conf.web3Url.len == 0:
|
||||
fatal "Web3 URL not specified"
|
||||
quit 1
|
||||
|
||||
if conf.depositContractDeployedAt.isNone:
|
||||
# When we don't have a known genesis state, the network metadata
|
||||
# must specify the deployment block of the contract.
|
||||
fatal "Deposit contract deployment block not specified"
|
||||
quit 1
|
||||
if conf.depositContractAddress.isNone:
|
||||
fatal "Deposit contract address not specified"
|
||||
quit 1
|
||||
|
||||
# TODO Could move this to a separate "GenesisMonitor" process or task
|
||||
# that would do only this - see Paul's proposal for this.
|
||||
let eth1MonitorRes = await Eth1Monitor.init(
|
||||
db,
|
||||
conf.runtimePreset,
|
||||
conf.web3Url,
|
||||
conf.depositContractAddress.get,
|
||||
conf.depositContractDeployedAt.get,
|
||||
eth1Network)
|
||||
if conf.depositContractDeployedAt.isNone:
|
||||
# When we don't have a known genesis state, the network metadata
|
||||
# must specify the deployment block of the contract.
|
||||
fatal "Deposit contract deployment block not specified"
|
||||
quit 1
|
||||
|
||||
if eth1MonitorRes.isErr:
|
||||
fatal "Failed to start Eth1 monitor",
|
||||
reason = eth1MonitorRes.error,
|
||||
web3Url = conf.web3Url,
|
||||
depositContractAddress = conf.depositContractAddress.get,
|
||||
depositContractDeployedAt = conf.depositContractDeployedAt.get
|
||||
quit 1
|
||||
# TODO Could move this to a separate "GenesisMonitor" process or task
|
||||
# that would do only this - see Paul's proposal for this.
|
||||
let eth1MonitorRes = await Eth1Monitor.init(
|
||||
db,
|
||||
conf.runtimePreset,
|
||||
conf.web3Url,
|
||||
conf.depositContractAddress.get,
|
||||
conf.depositContractDeployedAt.get,
|
||||
eth1Network)
|
||||
|
||||
if eth1MonitorRes.isErr:
|
||||
fatal "Failed to start Eth1 monitor",
|
||||
reason = eth1MonitorRes.error,
|
||||
web3Url = conf.web3Url,
|
||||
depositContractAddress = conf.depositContractAddress.get,
|
||||
depositContractDeployedAt = conf.depositContractDeployedAt.get
|
||||
quit 1
|
||||
else:
|
||||
eth1Monitor = eth1MonitorRes.get
|
||||
|
||||
genesisState = await eth1Monitor.waitGenesis()
|
||||
if bnStatus == BeaconNodeStatus.Stopping:
|
||||
return nil
|
||||
|
||||
tailState = genesisState
|
||||
tailBlock = get_initial_beacon_block(genesisState[])
|
||||
|
||||
notice "Eth2 genesis state detected",
|
||||
genesisTime = genesisState.genesisTime,
|
||||
eth1Block = genesisState.eth1_data.block_hash,
|
||||
totalDeposits = genesisState.eth1_data.deposit_count
|
||||
else:
|
||||
eth1Monitor = eth1MonitorRes.get
|
||||
|
||||
genesisState = await eth1Monitor.waitGenesis()
|
||||
if bnStatus == BeaconNodeStatus.Stopping:
|
||||
return nil
|
||||
|
||||
tailState = genesisState
|
||||
tailBlock = get_initial_beacon_block(genesisState[])
|
||||
|
||||
notice "Eth2 genesis state detected",
|
||||
genesisTime = genesisState.genesisTime,
|
||||
eth1Block = genesisState.eth1_data.block_hash,
|
||||
totalDeposits = genesisState.eth1_data.deposit_count
|
||||
fatal "The beacon node must be compiled with -d:has_genesis_detection " &
|
||||
"in order to support monitoring for genesis events"
|
||||
quit 1
|
||||
|
||||
elif genesisStateContents == nil:
|
||||
if checkpointState.slot == GENESIS_SLOT:
|
||||
@ -226,7 +237,9 @@ proc init*(T: type BeaconNode,
|
||||
if eth1Monitor.isNil and
|
||||
conf.web3Url.len > 0 and
|
||||
conf.depositContractAddress.isSome and
|
||||
conf.depositContractDeployedAt.isSome:
|
||||
genesisDepositsSnapshotContents != nil:
|
||||
let genesisDepositsSnapshot = SSZ.decode(genesisDepositsSnapshotContents[],
|
||||
DepositContractSnapshot)
|
||||
# TODO(zah) if we don't have any validators attached,
|
||||
# we don't need a mainchain monitor
|
||||
let eth1MonitorRes = await Eth1Monitor.init(
|
||||
@ -234,7 +247,7 @@ proc init*(T: type BeaconNode,
|
||||
conf.runtimePreset,
|
||||
conf.web3Url,
|
||||
conf.depositContractAddress.get,
|
||||
conf.depositContractDeployedAt.get,
|
||||
genesisDepositsSnapshot,
|
||||
eth1Network)
|
||||
|
||||
if eth1MonitorRes.isErr:
|
||||
@ -839,7 +852,7 @@ proc start(node: BeaconNode) =
|
||||
|
||||
waitFor node.initializeNetworking()
|
||||
|
||||
if node.eth1Monitor != nil:
|
||||
if node.eth1Monitor != nil and node.attachedValidators.count > 0:
|
||||
node.eth1Monitor.start()
|
||||
|
||||
node.run()
|
||||
@ -1003,6 +1016,7 @@ programMain:
|
||||
config = makeBannerAndConfig(clientId, BeaconNodeConf)
|
||||
# This is ref so we can mutate it (to erase it) after the initial loading.
|
||||
genesisStateContents: ref string
|
||||
genesisDepositsSnapshotContents: ref string
|
||||
eth1Network: Option[Eth1Network]
|
||||
|
||||
setupStdoutLogging(config.logLevel)
|
||||
@ -1040,6 +1054,9 @@ programMain:
|
||||
if metadata.genesisData.len > 0:
|
||||
genesisStateContents = newClone metadata.genesisData
|
||||
|
||||
if metadata.genesisDepositsSnapshot.len > 0:
|
||||
genesisDepositsSnapshotContents = newClone metadata.genesisDepositsSnapshot
|
||||
|
||||
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
|
||||
@ -1155,12 +1172,18 @@ programMain:
|
||||
# letting the default Ctrl+C handler exit is safe, since we only read from
|
||||
# the db.
|
||||
var node = waitFor BeaconNode.init(
|
||||
rng, config, genesisStateContents, eth1Network)
|
||||
rng, config, eth1Network,
|
||||
genesisStateContents,
|
||||
genesisDepositsSnapshotContents)
|
||||
|
||||
if bnStatus == BeaconNodeStatus.Stopping:
|
||||
return
|
||||
|
||||
# The memory for the initial snapshot won't be needed anymore
|
||||
if genesisStateContents != nil: genesisStateContents[] = ""
|
||||
if genesisStateContents != nil:
|
||||
genesisStateContents[] = ""
|
||||
if genesisDepositsSnapshotContents != nil:
|
||||
genesisDepositsSnapshotContents[] = ""
|
||||
|
||||
when hasPrompt:
|
||||
initPrompt(node)
|
||||
|
@ -461,6 +461,12 @@ type
|
||||
stabilitySubnet*: uint64
|
||||
stabilitySubnetExpirationEpoch*: Epoch
|
||||
|
||||
# This matches the mutable state of the Solidity deposit contract
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/solidity_deposit_contract/deposit_contract.sol
|
||||
DepositContractState* = object
|
||||
branch*: array[DEPOSIT_CONTRACT_TREE_DEPTH, Eth2Digest]
|
||||
deposit_count*: array[32, byte] # Uint256
|
||||
|
||||
func shortValidatorKey*(state: BeaconState, validatorIdx: int): string =
|
||||
($state.validators[validatorIdx].pubkey)[0..7]
|
||||
|
||||
|
@ -44,6 +44,12 @@ type
|
||||
template chunks*(m: SszMerkleizerImpl): openArray[Eth2Digest] =
|
||||
m.combinedChunks.toOpenArray(0, m.topIndex)
|
||||
|
||||
template getChunkCount*(m: SszMerkleizer): uint64 =
|
||||
m.impl.totalChunks
|
||||
|
||||
template getCombinedChunks*(m: SszMerkleizer): openArray[Eth2Digest] =
|
||||
toOpenArray(m.impl.combinedChunks, 0, m.impl.topIndex)
|
||||
|
||||
func digest(a, b: openArray[byte]): Eth2Digest =
|
||||
result = withEth2Hash:
|
||||
trs "DIGESTING ARRAYS ", toHex(a), " ", toHex(b)
|
||||
@ -309,7 +315,7 @@ proc init*(S: type SszMerkleizer,
|
||||
topIndex: binaryTreeHeight(result.limit) - 1,
|
||||
totalChunks: totalChunks)
|
||||
|
||||
proc clone*[L: static[Limit]](cloned: SszMerkleizer[L]): SszMerkleizer[L] =
|
||||
proc copy*[L: static[Limit]](cloned: SszMerkleizer[L]): SszMerkleizer[L] =
|
||||
new result.combinedChunks
|
||||
result.combinedChunks[] = cloned.combinedChunks[]
|
||||
result.impl = SszMerkleizerImpl(
|
||||
|
@ -185,10 +185,16 @@ proc createAndSendAttestation(node: BeaconNode,
|
||||
proc getBlockProposalEth1Data*(node: BeaconNode,
|
||||
state: BeaconState): BlockProposalEth1Data =
|
||||
if node.eth1Monitor.isNil:
|
||||
BlockProposalEth1Data(vote: state.eth1_data)
|
||||
var pendingDepositsCount = state.eth1_data.deposit_count -
|
||||
state.eth1_deposit_index
|
||||
if pendingDepositsCount > 0:
|
||||
result.hasMissingDeposits = true
|
||||
else:
|
||||
result.vote = state.eth1_data
|
||||
else:
|
||||
let finalizedEth1Data = node.chainDag.getFinalizedEpochRef().eth1_data
|
||||
node.eth1Monitor.getBlockProposalData(state, finalizedEth1Data)
|
||||
let finalizedEpochRef = node.chainDag.getFinalizedEpochRef()
|
||||
result = node.eth1Monitor.getBlockProposalData(
|
||||
state, finalizedEpochRef.eth1_data, finalizedEpochRef.eth1_deposit_index)
|
||||
|
||||
proc makeBeaconBlockForHeadAndSlot*(node: BeaconNode,
|
||||
randao_reveal: ValidatorSig,
|
||||
@ -202,6 +208,10 @@ proc makeBeaconBlockForHeadAndSlot*(node: BeaconNode,
|
||||
eth1Proposal = node.getBlockProposalEth1Data(state)
|
||||
poolPtr = unsafeAddr node.chainDag # safe because restore is short-lived
|
||||
|
||||
if eth1Proposal.hasMissingDeposits:
|
||||
error "Eth1 deposits not available. Skipping block proposal", slot
|
||||
return none(BeaconBlock)
|
||||
|
||||
func restore(v: var HashedBeaconState) =
|
||||
# TODO address this ugly workaround - there should probably be a
|
||||
# `state_transition` that takes a `StateData` instead and updates
|
||||
|
7
env.sh
7
env.sh
@ -21,5 +21,12 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
# The user env file allows you to specify personal overrides for some
|
||||
# settings such as WEB3_URL, CPU_LIMIT, etc:
|
||||
USER_ENV_FILE="${ABS_PATH}/.env"
|
||||
if [ -f "${USER_ENV_FILE}" ]; then
|
||||
source "${USER_ENV_FILE}"
|
||||
fi
|
||||
|
||||
source ${ABS_PATH}/vendor/nimbus-build-system/scripts/env.sh
|
||||
|
||||
|
2
vendor/eth2-testnets
vendored
2
vendor/eth2-testnets
vendored
@ -1 +1 @@
|
||||
Subproject commit 308921315f7a083719fc7ace51315be9887e46a0
|
||||
Subproject commit dd56a2c99e70acc0814db523fa8c58278b2b33da
|
Loading…
x
Reference in New Issue
Block a user