Bugfix: the client can miss the genesis event in the absence of new deposits
This commit is contained in:
parent
9f28a464fb
commit
8012102704
|
@ -463,6 +463,13 @@ proc signalGenesis(m: Eth1Monitor, genesisState: BeaconStateRef) =
|
||||||
template hasEnoughValidators(m: Eth1Monitor, blk: Eth1Block): bool =
|
template hasEnoughValidators(m: Eth1Monitor, blk: Eth1Block): bool =
|
||||||
blk.activeValidatorsCount >= m.preset.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT
|
blk.activeValidatorsCount >= m.preset.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT
|
||||||
|
|
||||||
|
func chainHasEnoughValidators(m: Eth1Monitor): bool =
|
||||||
|
if m.eth1Chain.blocks.len > 0:
|
||||||
|
m.hasEnoughValidators(m.eth1Chain.blocks[^1])
|
||||||
|
else:
|
||||||
|
m.eth1Chain.knownStart.deposit_count >=
|
||||||
|
m.preset.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT
|
||||||
|
|
||||||
func isAfterMinGenesisTime(m: Eth1Monitor, blk: Eth1Block): bool =
|
func isAfterMinGenesisTime(m: Eth1Monitor, blk: Eth1Block): bool =
|
||||||
doAssert blk.timestamp != 0
|
doAssert blk.timestamp != 0
|
||||||
let t = genesis_time_from_eth1_timestamp(m.preset, uint64 blk.timestamp)
|
let t = genesis_time_from_eth1_timestamp(m.preset, uint64 blk.timestamp)
|
||||||
|
@ -527,21 +534,23 @@ proc stop*(m: Eth1Monitor) =
|
||||||
proc syncBlockRange(m: Eth1Monitor, fromBlock, toBlock: Eth1BlockNumber) {.async.} =
|
proc syncBlockRange(m: Eth1Monitor, fromBlock, toBlock: Eth1BlockNumber) {.async.} =
|
||||||
var currentBlock = fromBlock
|
var currentBlock = fromBlock
|
||||||
while currentBlock <= toBlock:
|
while currentBlock <= toBlock:
|
||||||
var depositLogs: JsonNode = nil
|
var
|
||||||
|
depositLogs: JsonNode = nil
|
||||||
|
blocksPerRequest = 5000'u64 # This is roughly a day of Eth1 blocks
|
||||||
|
maxBlockNumberRequested: Eth1BlockNumber
|
||||||
|
|
||||||
var blocksPerRequest = 5000'u64 # This is roughly a day of Eth1 blocks
|
|
||||||
while true:
|
while true:
|
||||||
let requestToBlock = min(toBlock, currentBlock + blocksPerRequest - 1)
|
maxBlockNumberRequested = min(toBlock, currentBlock + blocksPerRequest - 1)
|
||||||
|
|
||||||
debug "Obtaining deposit log events",
|
debug "Obtaining deposit log events",
|
||||||
fromBlock = currentBlock,
|
fromBlock = currentBlock,
|
||||||
toBlock = requestToBlock
|
toBlock = maxBlockNumberRequested
|
||||||
try:
|
try:
|
||||||
depositLogs = await m.dataProvider.ns.getJsonLogs(
|
depositLogs = await m.dataProvider.ns.getJsonLogs(
|
||||||
DepositEvent,
|
DepositEvent,
|
||||||
fromBlock = some blockId(currentBlock),
|
fromBlock = some blockId(currentBlock),
|
||||||
toBlock = some blockId(requestToBlock))
|
toBlock = some blockId(maxBlockNumberRequested))
|
||||||
currentBlock = requestToBlock + 1
|
currentBlock = maxBlockNumberRequested + 1
|
||||||
break
|
break
|
||||||
except CatchableError as err:
|
except CatchableError as err:
|
||||||
blocksPerRequest = blocksPerRequest div 2
|
blocksPerRequest = blocksPerRequest div 2
|
||||||
|
@ -568,7 +577,8 @@ proc syncBlockRange(m: Eth1Monitor, fromBlock, toBlock: Eth1BlockNumber) {.async
|
||||||
m.eth1Chain.addBlock blk
|
m.eth1Chain.addBlock blk
|
||||||
|
|
||||||
if eth1Blocks.len > 0:
|
if eth1Blocks.len > 0:
|
||||||
let lastBlock = eth1Blocks[^1]
|
let lastIdx = eth1Blocks.len - 1
|
||||||
|
template lastBlock: auto = eth1Blocks[lastIdx]
|
||||||
|
|
||||||
when hasDepositRootChecks:
|
when hasDepositRootChecks:
|
||||||
let status = await m.dataProvider.fetchDepositContractData(lastBlock)
|
let status = await m.dataProvider.fetchDepositContractData(lastBlock)
|
||||||
|
@ -582,41 +592,62 @@ proc syncBlockRange(m: Eth1Monitor, fromBlock, toBlock: Eth1BlockNumber) {.async
|
||||||
blockNumber = lastBlock.number,
|
blockNumber = lastBlock.number,
|
||||||
depositsProcessed = lastBlock.voteData.deposit_count
|
depositsProcessed = lastBlock.voteData.deposit_count
|
||||||
|
|
||||||
if m.genesisStateFut != nil and m.hasEnoughValidators(lastBlock):
|
if m.genesisStateFut != nil and m.chainHasEnoughValidators:
|
||||||
|
let lastIdx = m.eth1Chain.blocks.len - 1
|
||||||
|
template lastBlock: auto = m.eth1Chain.blocks[lastIdx]
|
||||||
|
|
||||||
|
if maxBlockNumberRequested == toBlock and
|
||||||
|
(m.eth1Chain.blocks.len == 0 or lastBlock.number != toBlock):
|
||||||
|
let web3Block = await m.dataProvider.getBlockByNumber(toBlock)
|
||||||
|
|
||||||
|
debug "Latest block doesn't hold deposits. Obtaining it",
|
||||||
|
ts = web3Block.timestamp.uint64,
|
||||||
|
number = web3Block.number.uint64
|
||||||
|
|
||||||
|
m.eth1Chain.addBlock Eth1Block(
|
||||||
|
number: Eth1BlockNumber web3Block.number,
|
||||||
|
timestamp: Eth1BlockTimestamp web3Block.timestamp,
|
||||||
|
voteData: Eth1Data(
|
||||||
|
block_hash: web3Block.hash.asEth2Digest,
|
||||||
|
deposit_count: lastBlock.voteData.deposit_count,
|
||||||
|
deposit_root: lastBlock.voteData.deposit_root),
|
||||||
|
activeValidatorsCount: lastBlock.activeValidatorsCount)
|
||||||
|
else:
|
||||||
await m.dataProvider.fetchTimestamp(lastBlock)
|
await m.dataProvider.fetchTimestamp(lastBlock)
|
||||||
if m.isAfterMinGenesisTime(lastBlock):
|
|
||||||
var genesisBlockIdx = m.eth1Chain.blocks.len - 1
|
var genesisBlockIdx = m.eth1Chain.blocks.len - 1
|
||||||
for i in 1 ..< eth1Blocks.len:
|
if m.isAfterMinGenesisTime(m.eth1Chain.blocks[genesisBlockIdx]):
|
||||||
let idx = (m.eth1Chain.blocks.len - 1) - i
|
for i in 1 ..< eth1Blocks.len:
|
||||||
let blk = m.eth1Chain.blocks[idx]
|
let idx = (m.eth1Chain.blocks.len - 1) - i
|
||||||
await m.dataProvider.fetchTimestamp(blk)
|
let blk = m.eth1Chain.blocks[idx]
|
||||||
if m.isGenesisCandidate(blk):
|
await m.dataProvider.fetchTimestamp(blk)
|
||||||
genesisBlockIdx = idx
|
if m.isGenesisCandidate(blk):
|
||||||
else:
|
genesisBlockIdx = idx
|
||||||
break
|
else:
|
||||||
# We have a candidate state on our hands, but our current Eth1Chain
|
break
|
||||||
# may consist only of blocks that have deposits attached to them
|
# We have a candidate state on our hands, but our current Eth1Chain
|
||||||
# while the real genesis may have happened in a block without any
|
# may consist only of blocks that have deposits attached to them
|
||||||
# deposits (triggered by MIN_GENESIS_TIME).
|
# while the real genesis may have happened in a block without any
|
||||||
#
|
# deposits (triggered by MIN_GENESIS_TIME).
|
||||||
# This can happen when the beacon node is launched after the genesis
|
#
|
||||||
# event. We take a short cut when constructing the initial Eth1Chain
|
# This can happen when the beacon node is launched after the genesis
|
||||||
# by downloading only deposit log entries. Thus, we'll see all the
|
# event. We take a short cut when constructing the initial Eth1Chain
|
||||||
# blocks with deposits, but not the regular blocks in between.
|
# by downloading only deposit log entries. Thus, we'll see all the
|
||||||
#
|
# blocks with deposits, but not the regular blocks in between.
|
||||||
# We'll handle this special case below by examing whether we are in
|
#
|
||||||
# this potential scenario and we'll use a fast guessing algorith to
|
# We'll handle this special case below by examing whether we are in
|
||||||
# discover the ETh1 block with minimal valid genesis time.
|
# this potential scenario and we'll use a fast guessing algorith to
|
||||||
var genesisBlock = m.eth1Chain.blocks[genesisBlockIdx]
|
# discover the ETh1 block with minimal valid genesis time.
|
||||||
if genesisBlockIdx > 0:
|
var genesisBlock = m.eth1Chain.blocks[genesisBlockIdx]
|
||||||
let genesisParent = m.eth1Chain.blocks[genesisBlockIdx - 1]
|
if genesisBlockIdx > 0:
|
||||||
if genesisParent.timestamp == 0:
|
let genesisParent = m.eth1Chain.blocks[genesisBlockIdx - 1]
|
||||||
await m.dataProvider.fetchTimestamp(genesisParent)
|
if genesisParent.timestamp == 0:
|
||||||
if m.hasEnoughValidators(genesisParent) and
|
await m.dataProvider.fetchTimestamp(genesisParent)
|
||||||
genesisBlock.number - genesisParent.number > 1:
|
if m.hasEnoughValidators(genesisParent) and
|
||||||
genesisBlock = await m.findGenesisBlockInRange(genesisParent,
|
genesisBlock.number - genesisParent.number > 1:
|
||||||
genesisBlock)
|
genesisBlock = await m.findGenesisBlockInRange(genesisParent,
|
||||||
m.signalGenesis m.createGenesisState(genesisBlock)
|
genesisBlock)
|
||||||
|
m.signalGenesis m.createGenesisState(genesisBlock)
|
||||||
|
|
||||||
proc handleEth1Progress(m: Eth1Monitor) {.async.} =
|
proc handleEth1Progress(m: Eth1Monitor) {.async.} =
|
||||||
# ATTENTION!
|
# ATTENTION!
|
||||||
|
@ -650,7 +681,7 @@ proc handleEth1Progress(m: Eth1Monitor) {.async.} =
|
||||||
await m.syncBlockRange(eth1SyncedTo + 1, targetBlock)
|
await m.syncBlockRange(eth1SyncedTo + 1, targetBlock)
|
||||||
eth1SyncedTo = targetBlock
|
eth1SyncedTo = targetBlock
|
||||||
|
|
||||||
while m.eth1Chain.blocks.len > 0:
|
while m.eth1Chain.blocks.len > 1:
|
||||||
# We'll clean old blocks that can no longer be voting candidates.
|
# We'll clean old blocks that can no longer be voting candidates.
|
||||||
# Technically, we should check that the block is outside of the current
|
# Technically, we should check that the block is outside of the current
|
||||||
# voting period as determined by its timestamp, but we'll approximate
|
# voting period as determined by its timestamp, but we'll approximate
|
||||||
|
|
Loading…
Reference in New Issue