Working genesis detection for Altona

This commit is contained in:
Zahary Karadjov 2020-06-28 23:17:47 +03:00 committed by zah
parent 31e31bb30c
commit b9e653f7fd
3 changed files with 101 additions and 35 deletions

View File

@ -167,7 +167,11 @@ proc init*(T: type BeaconNode, conf: BeaconNodeConf): Future[BeaconNode] {.async
Eth1Data(block_hash: conf.depositContractDeployedAt.get, deposit_count: 0))
mainchainMonitor.start()
genesisState = await mainchainMonitor.getGenesis()
genesisState = await mainchainMonitor.waitGenesis()
info "Eth2 genesis state detected",
genesisTime = genesisState.genesisTime,
eth1Block = genesisState.eth1_data.block_hash,
totalDeposits = genesisState.eth1_data.deposit_count
# This is needed to prove the not nil property from here on
if genesisState == nil:

View File

@ -315,8 +315,6 @@ method getBlockByNumber*(p: Web3DataProviderRef, number: Eth1BlockNumber): Futur
return p.web3.provider.eth_getBlockByNumber(&"0x{number:X}", false)
proc getBlockNumber(p: DataProviderRef, hash: BlockHash): Future[Eth1BlockNumber] {.async.} =
debug "Querying block number", hash = $hash
try:
let blk = awaitWithTimeout(p.getBlockByHash(hash), web3Timeouts):
return 0
@ -377,6 +375,7 @@ proc readJsonDeposits(depositsList: JsonNode): seq[Eth1Block] =
method fetchDepositData*(p: Web3DataProviderRef,
fromBlock, toBlock: Eth1BlockNumber): Future[seq[Eth1Block]]
{.async, locks: 0.} =
info "Obtaing deposit log events", fromBlock, toBlock
return readJsonDeposits(await p.ns.getJsonLogs(DepositEvent,
fromBlock = some blockId(fromBlock),
toBlock = some blockId(toBlock)))
@ -495,35 +494,65 @@ proc minGenesisCandidateBlockIdx(eth1Chain: Eth1Chain): Option[int]
return some(candidatePos)
proc tryCreatingGenesisState(m: MainchainMonitor,
genesisCandidate: Eth1Block,
deposits: var openarray[Deposit]): uint64 =
proc createBeaconStateAux(eth1Block: Eth1Block,
deposits: var openarray[Deposit]): BeaconStateRef =
attachMerkleProofs deposits
result = initialize_beacon_state_from_eth1(eth1Block.voteData.block_hash,
eth1Block.timestamp.uint64,
deposits, {})
let activeValidators = get_active_validator_indices(result[], GENESIS_EPOCH)
eth1Block.knownGoodDepositsCount = some len(activeValidators).uint64
let
s = initialize_beacon_state_from_eth1(genesisCandidate.voteData.block_hash,
genesisCandidate.timestamp.uint64,
deposits, {})
active_validator_indices = get_active_validator_indices(s[], GENESIS_EPOCH)
proc createBeaconState(eth1Chain: var Eth1Chain, eth1Block: Eth1Block): BeaconStateRef =
createBeaconStateAux(
eth1Block,
eth1Chain.allDeposits.toOpenArray(0, int(eth1Block.voteData.deposit_count - 1)))
if is_valid_genesis_state(s[], active_validator_indices):
# https://github.com/ethereum/eth2.0-pm/tree/6e41fcf383ebeb5125938850d8e9b4e9888389b4/interop/mocked_start#create-genesis-state
info "Eth2 genesis state detected",
genesisTime = s.genesisTime,
genesisEth1Block = genesisCandidate.voteData.block_hash
proc signalGenesis(m: MainchainMonitor, genesisState: BeaconStateRef) =
m.genesisState = genesisState
m.genesisState = s
if not m.genesisStateFut.isNil:
m.genesisStateFut.complete()
m.genesisStateFut = nil
if not m.genesisStateFut.isNil:
m.genesisStateFut.complete()
m.genesisStateFut = nil
else:
info "Eth2 genesis candidate block rejected",
`block` = shortLog(genesisCandidate),
validDeposits = active_validator_indices.len,
needed = totalDepositsNeededForGenesis
proc findGenesisBlockInRange(m: MainchainMonitor,
startBlock, endBlock: Eth1Block): Future[Eth1Block]
{.async.} =
let dataProvider = await m.dataProviderFactory.new(m.depositContractAddress)
if dataProvider == nil:
error "Failed to initialize Eth1 data provider",
provider = m.dataProviderFactory.desc
raise newException(CatchableError, "Failed to initialize Eth1 data provider")
uint64(active_validator_indices.len)
var
startBlock = startBlock
endBlock = endBlock
depositData = startBlock.voteData
while startBlock.number + 1 < endBlock.number:
let
startBlockTime = genesis_time_from_eth1_timestamp(startBlock.timestamp)
secondsPerBlock = float(endBlock.timestamp - startBlock.timestamp) /
float(endBlock.number - startBlock.number)
blocksToJump = max(float(MIN_GENESIS_TIME - startBlockTime) / secondsPerBlock, 1.0)
candidateNumber = min(endBlock.number - 1, startBlock.number + blocksToJump.uint64)
candidateBlock = await dataProvider.getBlockByNumber(candidateNumber)
var candidateAsEth1Block = Eth1Block(number: candidateBlock.number.uint64,
timestamp: candidateBlock.timestamp.uint64,
voteData: depositData)
candidateAsEth1Block.voteData.block_hash = candidateBlock.hash.asEth2Digest
info "Probing possible genesis block",
`block` = candidateBlock.number.uint64,
timestamp = genesis_time_from_eth1_timestamp(candidateBlock.timestamp.uint64)
if genesis_time_from_eth1_timestamp(candidateBlock.timestamp.uint64) < MIN_GENESIS_TIME:
startBlock = candidateAsEth1Block
else:
endBlock = candidateAsEth1Block
return endBlock
proc checkForGenesisLoop(m: MainchainMonitor) {.async.} =
while true:
@ -534,12 +563,45 @@ proc checkForGenesisLoop(m: MainchainMonitor) {.async.} =
let genesisCandidateIdx = m.eth1Chain.minGenesisCandidateBlockIdx
if genesisCandidateIdx.isSome:
let
genesisCandidate = m.eth1Chain.blocks[genesisCandidateIdx.get]
eligibleDepositCount = genesisCandidate.voteData.deposit_count
genesisCandidateIdx = genesisCandidateIdx.get
genesisCandidate = m.eth1Chain.blocks[genesisCandidateIdx]
candidateState = m.eth1Chain.createBeaconState(genesisCandidate)
genesisCandidate.knownGoodDepositsCount = some tryCreatingGenesisState(
m, genesisCandidate,
m.eth1Chain.allDeposits.toOpenArray(0, int(eligibleDepositCount - 1)))
if genesisCandidate.knownGoodDepositsCount.get >= totalDepositsNeededForGenesis:
# We have a candidate state on our hands, but our current Eth1Chain
# may consist only of blocks that have deposits attached to them
# 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
# 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
# discover the ETh1 block with minimal valid genesis time.
if genesisCandidateIdx > 0:
let preceedingEth1Block = m.eth1Chain.blocks[genesisCandidateIdx - 1]
if preceedingEth1Block.voteData.deposit_root == genesisCandidate.voteData.deposit_root:
preceedingEth1Block.knownGoodDepositsCount = genesisCandidate.knownGoodDepositsCount
else:
discard m.eth1Chain.createBeaconState(preceedingEth1Block)
if preceedingEth1Block.knownGoodDepositsCount.get >= totalDepositsNeededForGenesis and
genesisCandidate.number - preceedingEth1Block.number > 1:
let genesisBlock = await m.findGenesisBlockInRange(preceedingEth1Block, genesisCandidate)
if genesisBlock.number != genesisCandidate.number:
m.signalGenesis m.eth1Chain.createBeaconState(genesisBlock)
return
m.signalGenesis candidateState
return
else:
info "Eth2 genesis candidate block rejected",
`block` = shortLog(genesisCandidate),
validDeposits = genesisCandidate.knownGoodDepositsCount.get,
needed = totalDepositsNeededForGenesis
else:
# TODO: check for a stale monitor
discard
@ -548,10 +610,10 @@ proc checkForGenesisLoop(m: MainchainMonitor) {.async.} =
await sleepAsync(1.seconds)
proc getGenesis*(m: MainchainMonitor): Future[BeaconStateRef] {.async.} =
proc waitGenesis*(m: MainchainMonitor): Future[BeaconStateRef] {.async.} =
if m.genesisState.isNil:
if m.genesisStateFut.isNil:
m.genesisStateFut = newFuture[void]("getGenesis")
m.genesisStateFut = newFuture[void]("waitGenesis")
m.genesisMonitoringFut = m.checkForGenesisLoop()
await m.genesisStateFut
@ -673,7 +735,7 @@ proc run(m: MainchainMonitor, delayBeforeStart: Duration) {.async.} =
raise newException(CatchableError, "Failed to initialize Eth1 data provider")
try:
info "Monitoring eth1 deposits",
info "Starting Eth1 deposit contract monitoring",
contract = $m.depositContractAddress,
url = m.dataProviderFactory.desc

View File

@ -83,7 +83,7 @@ proc getMerkleProof*[Depth: static int](tree: SparseMerkleTree[Depth],
else:
result[depth] = zeroHashes[depth]
proc attachMerkleProofs*(deposits: var openarray[Deposit]) =
func attachMerkleProofs*(deposits: var openarray[Deposit]) =
let deposit_data_roots = mapIt(deposits, it.data.hash_tree_root)
var
deposit_data_sums: seq[Eth2Digest]