Less risk for Eth1-induced delays in block proposal

This commit is contained in:
Zahary Karadjov 2020-12-01 01:59:35 +02:00 committed by zah
parent 38f7558e50
commit 4eaf29875a
4 changed files with 62 additions and 39 deletions

View File

@ -389,6 +389,10 @@ proc pruneOldBlocks(m: Eth1Monitor, depositIndex: uint64) =
eth1Block: lastBlock.voteData.block_hash,
depositContractState: m.eth2FinalizedDepositsMerkleizer.toDepositContractState)
debug "Eth1 blocks pruned",
newTailBlock = lastBlock.voteData.block_hash,
depositsCount = lastBlock.voteData.deposit_count
proc advanceMerkleizer(eth1Chain: Eth1Chain,
merkleizer: var DepositsMerkleizer,
depositIndex: uint64): bool =
@ -434,39 +438,61 @@ proc getDepositsRange(eth1Chain: Eth1Chain, first, last: uint64): seq[Deposit] =
if globalIdx >= first and globalIdx < last:
result.add Deposit(data: blk.deposits[i])
# https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/validator.md#get_eth1_data
proc getBlockProposalData*(m: Eth1Monitor,
state: BeaconState,
finalizedEth1Data: Eth1Data,
finalizedStateDepositIndex: uint64): BlockProposalEth1Data =
let finalizedEth1Block = m.eth1Chain.findBlock(finalizedEth1Data)
let hasLatestDeposits = if finalizedEth1Block != nil:
if finalizedEth1Block.voteData.deposit_root == finalizedEth1Data.deposit_root:
finalizedEth1Block.voteDataVerified = true
proc lowerBound(chain: Eth1Chain, depositCount: uint64): Eth1Block =
# TODO: This can be replaced with a proper binary search in the
# future, but the `algorithm` module currently requires an
# `openArray`, which the `deques` module can't provide yet.
for eth1Block in chain.blocks:
if eth1Block.voteData.deposit_count > depositCount:
return
result = eth1Block
proc trackFinalizedState*(m: Eth1Monitor,
finalizedEth1Data: Eth1Data,
finalizedStateDepositIndex: uint64): bool =
# Returns true if the Eth1Monitor is synced to the finalization point
if m.eth1Chain.blocks.len == 0:
debug "Eth1 chain not initialized"
return false
let latest = m.eth1Chain.blocks.peekLast
if latest.voteData.deposit_count < finalizedEth1Data.deposit_count:
debug "Eth1 chain not synced",
ourDepositsCount = latest.voteData.deposit_count,
targetDepositsCount = finalizedEth1Data.deposit_count
return false
let matchingBlock = m.eth1Chain.lowerBound(finalizedEth1Data.deposit_count)
result = if matchingBlock != nil:
if matchingBlock.voteData.deposit_root == finalizedEth1Data.deposit_root:
matchingBlock.voteDataVerified = true
true
else:
error "Corrupted deposits history detected",
depositsCount = finalizedEth1Data.deposit_count,
targetDepositsRoot = finalizedEth1Data.deposit_root,
targetDepositsCount = finalizedEth1Data.deposit_count,
ourDepositsCount = finalizedEth1Block.voteData.deposit_count,
ourDepositsRoot = finalizedEth1Block.voteData.deposit_root
ourDepositsRoot = matchingBlock.voteData.deposit_root
false
else:
debug "Finalized Eth1 checkpoint not present in local chain",
error "The Eth1 chain is in inconsistent state",
checkpointHash = finalizedEth1Data.block_hash,
checkpointDeposits = finalizedEth1Data.deposit_count,
localChainStart = shortLog(m.eth1Chain.blocks.peekFirst),
localChainEnd = shortLog(m.eth1Chain.blocks.peekLast)
false
if hasLatestDeposits:
# TODO(zah)
# To make block proposal cheaper, we can perform this action more regularly
# (e.g. in BeaconNode.onSlot). But keep in mind that this action needs to be
# performed only when there are validators attached to the node.
if result:
m.pruneOldBlocks(finalizedStateDepositIndex)
let periodStart = voting_period_start_time(state)
# https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/validator.md#get_eth1_data
proc getBlockProposalData*(m: Eth1Monitor,
state: BeaconState,
finalizedEth1Data: Eth1Data,
finalizedStateDepositIndex: uint64): BlockProposalEth1Data =
let
periodStart = voting_period_start_time(state)
hasLatestDeposits = m.trackFinalizedState(finalizedEth1Data,
finalizedStateDepositIndex)
var otherVotesCountTable = initCountTable[Eth1Data]()
for vote in state.eth1_data_votes:
@ -648,11 +674,6 @@ proc syncBlockRange(m: Eth1Monitor,
debug.logTime "Deposits grouped in blocks":
let blocksWithDeposits = depositEventsToBlocks(depositLogs)
var
# A temporary sequence for stroing all new blocks aiming to make the
# updates to m.eth1Chain more atomic/transactional.
blocksToAddToChain = newSeq[Eth1Block]()
for i in 0 ..< blocksWithDeposits.len:
let blk = blocksWithDeposits[i]
@ -664,14 +685,13 @@ proc syncBlockRange(m: Eth1Monitor,
blk.voteData.deposit_root = merkleizer[].getDepositsRoot
if blk.number > fullSyncFromBlock:
let lastBlock = if blocksToAddToChain.len > 0: blocksToAddToChain[^1]
else: m.eth1Chain.blocks.peekLast
let lastBlock = m.eth1Chain.blocks.peekLast
for n in max(lastBlock.number + 1, fullSyncFromBlock) ..< blk.number:
let blockWithoutDeposits = await m.dataProvider.getBlockByNumber(n)
blocksToAddToChain.add(
m.eth1Chain.addBlock(
lastBlock.makeSuccessorWithoutDeposits(blockWithoutDeposits))
blocksToAddToChain.add blk
m.eth1Chain.addBlock blk
if blocksWithDeposits.len > 0:
let lastIdx = blocksWithDeposits.len - 1
@ -697,8 +717,7 @@ proc syncBlockRange(m: Eth1Monitor,
raise newException(CorruptDataProvider,
"The deposit log events disagree with the deposit contract state")
of VerifiedCorrect:
for blk in blocksToAddToChain:
blk.voteDataVerified = true
lastBlock.voteDataVerified = true
else:
discard
@ -706,9 +725,6 @@ proc syncBlockRange(m: Eth1Monitor,
blockNumber = lastBlock.number,
depositsProcessed = lastBlock.voteData.deposit_count
for blk in blocksToAddToChain:
m.eth1Chain.addBlock blk
when hasGenesisDetection:
if m.genesisStateFut != nil:
for blk in blocksWithDeposits:
@ -798,6 +814,8 @@ proc startEth1Syncing(m: Eth1Monitor) {.async.} =
var eth1SyncedTo = Eth1BlockNumber startBlock.number
var scratchMerkleizer = newClone(copy m.eth2FinalizedDepositsMerkleizer)
debug "Starting Eth1 syncing", `from` = shortLog(m.eth1Chain.blocks[0])
while true:
if bnStatus == BeaconNodeStatus.Stopping:
when hasGenesisDetection:

View File

@ -51,7 +51,7 @@ proc installValidatorApiHandlers*(rpcServer: RpcServer, node: BeaconNode) =
if head.slot >= body.message.slot:
raise newException(CatchableError,
"Proposal is for a past slot: " & $body.message.slot)
if head == await proposeSignedBlock(node, head, AttachedValidator(), body):
if head == proposeSignedBlock(node, head, AttachedValidator(), body):
raise newException(CatchableError, "Could not propose block")
return true

View File

@ -264,10 +264,9 @@ proc makeBeaconBlockForHeadAndSlot*(node: BeaconNode,
proc proposeSignedBlock*(node: BeaconNode,
head: BlockRef,
validator: AttachedValidator,
newBlock: SignedBeaconBlock): Future[BlockRef] {.async.} =
newBlock: SignedBeaconBlock): BlockRef =
let newBlockRef = node.chainDag.addRawBlock(node.quarantine,
newBlock) do (
let newBlockRef = node.chainDag.addRawBlock(node.quarantine, newBlock) do (
blckRef: BlockRef, signedBlock: SignedBeaconBlock,
epochRef: EpochRef, state: HashedBeaconState):
# Callback add to fork choice if valid
@ -348,7 +347,7 @@ proc proposeBlock(node: BeaconNode,
newBlock.signature = await validator.signBlockProposal(
fork, genesis_validators_root, slot, newBlock.root)
return await node.proposeSignedBlock(head, validator, newBlock)
return node.proposeSignedBlock(head, validator, newBlock)
proc handleAttestations(node: BeaconNode, head: BlockRef, slot: Slot) =
## Perform all attestations that the validators attached to this node should
@ -688,3 +687,9 @@ proc handleValidatorDuties*(node: BeaconNode, lastSlot, slot: Slot) {.async.} =
aggregationHead = get_ancestor(head, aggregationSlot)
await broadcastAggregatedAttestations(node, aggregationHead, aggregationSlot)
if node.eth1Monitor != nil and (slot mod SLOTS_PER_EPOCH) == 0:
let finalizedEpochRef = node.chainDag.getFinalizedEpochRef()
discard node.eth1Monitor.trackFinalizedState(
finalizedEpochRef.eth1_data, finalizedEpochRef.eth1_deposit_index)

2
vendor/news vendored

@ -1 +1 @@
Subproject commit 852b707b5daa99c380b716c8efe49807b8bfd4f0
Subproject commit 1d2ca5b059db7591c044d5d2fa5e5cd3e98b21a7