Working genesis detection for Altona
This commit is contained in:
parent
31e31bb30c
commit
b9e653f7fd
|
@ -167,7 +167,11 @@ proc init*(T: type BeaconNode, conf: BeaconNodeConf): Future[BeaconNode] {.async
|
||||||
Eth1Data(block_hash: conf.depositContractDeployedAt.get, deposit_count: 0))
|
Eth1Data(block_hash: conf.depositContractDeployedAt.get, deposit_count: 0))
|
||||||
mainchainMonitor.start()
|
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
|
# This is needed to prove the not nil property from here on
|
||||||
if genesisState == nil:
|
if genesisState == nil:
|
||||||
|
|
|
@ -315,8 +315,6 @@ method getBlockByNumber*(p: Web3DataProviderRef, number: Eth1BlockNumber): Futur
|
||||||
return p.web3.provider.eth_getBlockByNumber(&"0x{number:X}", false)
|
return p.web3.provider.eth_getBlockByNumber(&"0x{number:X}", false)
|
||||||
|
|
||||||
proc getBlockNumber(p: DataProviderRef, hash: BlockHash): Future[Eth1BlockNumber] {.async.} =
|
proc getBlockNumber(p: DataProviderRef, hash: BlockHash): Future[Eth1BlockNumber] {.async.} =
|
||||||
debug "Querying block number", hash = $hash
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
let blk = awaitWithTimeout(p.getBlockByHash(hash), web3Timeouts):
|
let blk = awaitWithTimeout(p.getBlockByHash(hash), web3Timeouts):
|
||||||
return 0
|
return 0
|
||||||
|
@ -377,6 +375,7 @@ proc readJsonDeposits(depositsList: JsonNode): seq[Eth1Block] =
|
||||||
method fetchDepositData*(p: Web3DataProviderRef,
|
method fetchDepositData*(p: Web3DataProviderRef,
|
||||||
fromBlock, toBlock: Eth1BlockNumber): Future[seq[Eth1Block]]
|
fromBlock, toBlock: Eth1BlockNumber): Future[seq[Eth1Block]]
|
||||||
{.async, locks: 0.} =
|
{.async, locks: 0.} =
|
||||||
|
info "Obtaing deposit log events", fromBlock, toBlock
|
||||||
return readJsonDeposits(await p.ns.getJsonLogs(DepositEvent,
|
return readJsonDeposits(await p.ns.getJsonLogs(DepositEvent,
|
||||||
fromBlock = some blockId(fromBlock),
|
fromBlock = some blockId(fromBlock),
|
||||||
toBlock = some blockId(toBlock)))
|
toBlock = some blockId(toBlock)))
|
||||||
|
@ -495,35 +494,65 @@ proc minGenesisCandidateBlockIdx(eth1Chain: Eth1Chain): Option[int]
|
||||||
|
|
||||||
return some(candidatePos)
|
return some(candidatePos)
|
||||||
|
|
||||||
proc tryCreatingGenesisState(m: MainchainMonitor,
|
proc createBeaconStateAux(eth1Block: Eth1Block,
|
||||||
genesisCandidate: Eth1Block,
|
deposits: var openarray[Deposit]): BeaconStateRef =
|
||||||
deposits: var openarray[Deposit]): uint64 =
|
|
||||||
attachMerkleProofs deposits
|
attachMerkleProofs deposits
|
||||||
|
result = initialize_beacon_state_from_eth1(eth1Block.voteData.block_hash,
|
||||||
let
|
eth1Block.timestamp.uint64,
|
||||||
s = initialize_beacon_state_from_eth1(genesisCandidate.voteData.block_hash,
|
|
||||||
genesisCandidate.timestamp.uint64,
|
|
||||||
deposits, {})
|
deposits, {})
|
||||||
active_validator_indices = get_active_validator_indices(s[], GENESIS_EPOCH)
|
let activeValidators = get_active_validator_indices(result[], GENESIS_EPOCH)
|
||||||
|
eth1Block.knownGoodDepositsCount = some len(activeValidators).uint64
|
||||||
|
|
||||||
if is_valid_genesis_state(s[], active_validator_indices):
|
proc createBeaconState(eth1Chain: var Eth1Chain, eth1Block: Eth1Block): BeaconStateRef =
|
||||||
# https://github.com/ethereum/eth2.0-pm/tree/6e41fcf383ebeb5125938850d8e9b4e9888389b4/interop/mocked_start#create-genesis-state
|
createBeaconStateAux(
|
||||||
info "Eth2 genesis state detected",
|
eth1Block,
|
||||||
genesisTime = s.genesisTime,
|
eth1Chain.allDeposits.toOpenArray(0, int(eth1Block.voteData.deposit_count - 1)))
|
||||||
genesisEth1Block = genesisCandidate.voteData.block_hash
|
|
||||||
|
|
||||||
m.genesisState = s
|
proc signalGenesis(m: MainchainMonitor, genesisState: BeaconStateRef) =
|
||||||
|
m.genesisState = genesisState
|
||||||
|
|
||||||
if not m.genesisStateFut.isNil:
|
if not m.genesisStateFut.isNil:
|
||||||
m.genesisStateFut.complete()
|
m.genesisStateFut.complete()
|
||||||
m.genesisStateFut = nil
|
m.genesisStateFut = nil
|
||||||
else:
|
|
||||||
info "Eth2 genesis candidate block rejected",
|
|
||||||
`block` = shortLog(genesisCandidate),
|
|
||||||
validDeposits = active_validator_indices.len,
|
|
||||||
needed = totalDepositsNeededForGenesis
|
|
||||||
|
|
||||||
uint64(active_validator_indices.len)
|
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")
|
||||||
|
|
||||||
|
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.} =
|
proc checkForGenesisLoop(m: MainchainMonitor) {.async.} =
|
||||||
while true:
|
while true:
|
||||||
|
@ -534,12 +563,45 @@ proc checkForGenesisLoop(m: MainchainMonitor) {.async.} =
|
||||||
let genesisCandidateIdx = m.eth1Chain.minGenesisCandidateBlockIdx
|
let genesisCandidateIdx = m.eth1Chain.minGenesisCandidateBlockIdx
|
||||||
if genesisCandidateIdx.isSome:
|
if genesisCandidateIdx.isSome:
|
||||||
let
|
let
|
||||||
genesisCandidate = m.eth1Chain.blocks[genesisCandidateIdx.get]
|
genesisCandidateIdx = genesisCandidateIdx.get
|
||||||
eligibleDepositCount = genesisCandidate.voteData.deposit_count
|
genesisCandidate = m.eth1Chain.blocks[genesisCandidateIdx]
|
||||||
|
candidateState = m.eth1Chain.createBeaconState(genesisCandidate)
|
||||||
|
|
||||||
genesisCandidate.knownGoodDepositsCount = some tryCreatingGenesisState(
|
if genesisCandidate.knownGoodDepositsCount.get >= totalDepositsNeededForGenesis:
|
||||||
m, genesisCandidate,
|
# We have a candidate state on our hands, but our current Eth1Chain
|
||||||
m.eth1Chain.allDeposits.toOpenArray(0, int(eligibleDepositCount - 1)))
|
# 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:
|
else:
|
||||||
# TODO: check for a stale monitor
|
# TODO: check for a stale monitor
|
||||||
discard
|
discard
|
||||||
|
@ -548,10 +610,10 @@ proc checkForGenesisLoop(m: MainchainMonitor) {.async.} =
|
||||||
|
|
||||||
await sleepAsync(1.seconds)
|
await sleepAsync(1.seconds)
|
||||||
|
|
||||||
proc getGenesis*(m: MainchainMonitor): Future[BeaconStateRef] {.async.} =
|
proc waitGenesis*(m: MainchainMonitor): Future[BeaconStateRef] {.async.} =
|
||||||
if m.genesisState.isNil:
|
if m.genesisState.isNil:
|
||||||
if m.genesisStateFut.isNil:
|
if m.genesisStateFut.isNil:
|
||||||
m.genesisStateFut = newFuture[void]("getGenesis")
|
m.genesisStateFut = newFuture[void]("waitGenesis")
|
||||||
|
|
||||||
m.genesisMonitoringFut = m.checkForGenesisLoop()
|
m.genesisMonitoringFut = m.checkForGenesisLoop()
|
||||||
await m.genesisStateFut
|
await m.genesisStateFut
|
||||||
|
@ -673,7 +735,7 @@ proc run(m: MainchainMonitor, delayBeforeStart: Duration) {.async.} =
|
||||||
raise newException(CatchableError, "Failed to initialize Eth1 data provider")
|
raise newException(CatchableError, "Failed to initialize Eth1 data provider")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
info "Monitoring eth1 deposits",
|
info "Starting Eth1 deposit contract monitoring",
|
||||||
contract = $m.depositContractAddress,
|
contract = $m.depositContractAddress,
|
||||||
url = m.dataProviderFactory.desc
|
url = m.dataProviderFactory.desc
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,7 @@ proc getMerkleProof*[Depth: static int](tree: SparseMerkleTree[Depth],
|
||||||
else:
|
else:
|
||||||
result[depth] = zeroHashes[depth]
|
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)
|
let deposit_data_roots = mapIt(deposits, it.data.hash_tree_root)
|
||||||
var
|
var
|
||||||
deposit_data_sums: seq[Eth2Digest]
|
deposit_data_sums: seq[Eth2Digest]
|
||||||
|
|
Loading…
Reference in New Issue