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:
zah 2020-11-24 23:21:47 +02:00 committed by GitHub
parent 7b7dc6fed4
commit 372c9b798c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 694 additions and 504 deletions

4
.gitignore vendored
View File

@ -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

View File

@ -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
###

View File

@ -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")

View File

@ -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]

View File

@ -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

View File

@ -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:

View File

@ -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,6 +120,11 @@ proc init*(T: type BeaconNode,
tailBlock: SignedBeaconBlock
if genesisStateContents == nil and checkpointState == nil:
when hasGenesisDetection:
if genesisDepositsSnapshotContents != nil:
fatal "A deposits snapshot cannot be provided without also providing a matching beacon state snapshot"
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:
@ -166,6 +173,10 @@ proc init*(T: type BeaconNode,
genesisTime = genesisState.genesisTime,
eth1Block = genesisState.eth1_data.block_hash,
totalDeposits = genesisState.eth1_data.deposit_count
else:
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)

View File

@ -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]

View File

@ -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(

View File

@ -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:
let finalizedEth1Data = node.chainDag.getFinalizedEpochRef().eth1_data
node.eth1Monitor.getBlockProposalData(state, finalizedEth1Data)
result.vote = state.eth1_data
else:
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
View File

@ -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

@ -1 +1 @@
Subproject commit 308921315f7a083719fc7ace51315be9887e46a0
Subproject commit dd56a2c99e70acc0814db523fa8c58278b2b33da