Better connection error handling in MainchainMonitor
This commit is contained in:
parent
ea241e407d
commit
2e875ea17e
|
@ -1,12 +1,11 @@
|
||||||
import
|
import
|
||||||
chronos, web3, json,
|
chronos, web3, json, chronicles,
|
||||||
spec/[datatypes, digest, crypto, beaconstate, helpers],
|
spec/[datatypes, digest, crypto, beaconstate, helpers],
|
||||||
./extras
|
./extras
|
||||||
|
|
||||||
type
|
type
|
||||||
MainchainMonitor* = ref object
|
MainchainMonitor* = ref object
|
||||||
web3Url: string
|
web3Url: string
|
||||||
web3: Web3
|
|
||||||
depositContractAddress: Address
|
depositContractAddress: Address
|
||||||
|
|
||||||
genesisState: ref BeaconState
|
genesisState: ref BeaconState
|
||||||
|
@ -21,6 +20,8 @@ type
|
||||||
eth1Block: BlockHash
|
eth1Block: BlockHash
|
||||||
eth1Data*: Eth1Data
|
eth1Data*: Eth1Data
|
||||||
|
|
||||||
|
runFut: Future[void]
|
||||||
|
|
||||||
QueueElement = (BlockHash, DepositData)
|
QueueElement = (BlockHash, DepositData)
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,21 +40,34 @@ contract(DepositContract):
|
||||||
|
|
||||||
const MIN_GENESIS_TIME = 0
|
const MIN_GENESIS_TIME = 0
|
||||||
|
|
||||||
proc updateEth1Data*(m: MainchainMonitor) {.async.} =
|
proc updateEth1Data(m: MainchainMonitor, count: uint64, root: FixedBytes[32]) =
|
||||||
let ns = m.web3.contractSender(DepositContract, m.depositContractAddress)
|
m.eth1Data.deposit_count = count
|
||||||
|
m.eth1Data.deposit_root.data = array[32, byte](root)
|
||||||
|
m.eth1Data.block_hash.data = array[32, byte](m.eth1Block)
|
||||||
|
|
||||||
|
proc processDeposits(m: MainchainMonitor, web3: Web3) {.async.} =
|
||||||
|
while true:
|
||||||
|
let (blkHash, data) = await m.depositQueue.popFirst()
|
||||||
|
var blk: BlockObject
|
||||||
|
var depositCount: uint64
|
||||||
|
var depositRoot: FixedBytes[32]
|
||||||
|
try:
|
||||||
|
blk = await web3.provider.eth_getBlockByHash(blkHash, false)
|
||||||
|
|
||||||
|
let ns = web3.contractSender(DepositContract, m.depositContractAddress)
|
||||||
|
|
||||||
# TODO: use m.eth1Block for web3 calls
|
# TODO: use m.eth1Block for web3 calls
|
||||||
let cnt = await ns.get_deposit_count().call()
|
let cnt = await ns.get_deposit_count().call()
|
||||||
let htr = await ns.get_deposit_root().call()
|
depositRoot = await ns.get_deposit_root().call()
|
||||||
m.eth1Data.deposit_count = bytes_to_int(array[8, byte](cnt))
|
depositCount = bytes_to_int(array[8, byte](cnt))
|
||||||
m.eth1Data.deposit_root.data = array[32, byte](htr)
|
|
||||||
m.eth1Data.block_hash.data = array[32, byte](m.eth1Block)
|
|
||||||
|
|
||||||
proc processDeposits(m: MainchainMonitor) {.async.} =
|
except:
|
||||||
while true:
|
# Connection problem? Put the unprocessed deposit back to queue
|
||||||
let (blkHash, data) = await m.depositQueue.popFirst()
|
m.depositQueue.addFirstNoWait((blkHash, data))
|
||||||
|
raise
|
||||||
|
|
||||||
|
debug "Got deposit from eth1", pubKey = data.pubKey
|
||||||
|
|
||||||
let blk = await m.web3.provider.eth_getBlockByHash(blkHash, false)
|
|
||||||
let dep = datatypes.Deposit(data: data)
|
let dep = datatypes.Deposit(data: data)
|
||||||
m.pendingDeposits.add(dep)
|
m.pendingDeposits.add(dep)
|
||||||
inc m.depositCount
|
inc m.depositCount
|
||||||
|
@ -80,10 +94,10 @@ proc processDeposits(m: MainchainMonitor) {.async.} =
|
||||||
|
|
||||||
# TODO: This should be progressing in more independent way.
|
# TODO: This should be progressing in more independent way.
|
||||||
# The Eth1 cross-link can advance even when there are no new deposits.
|
# The Eth1 cross-link can advance even when there are no new deposits.
|
||||||
await m.updateEth1Data
|
m.updateEth1Data(depositCount, depositRoot)
|
||||||
|
|
||||||
proc isRunning*(m: MainchainMonitor): bool =
|
proc isRunning*(m: MainchainMonitor): bool =
|
||||||
not m.web3.isNil
|
not m.runFut.isNil
|
||||||
|
|
||||||
proc getGenesis*(m: MainchainMonitor): Future[BeaconState] {.async.} =
|
proc getGenesis*(m: MainchainMonitor): Future[BeaconState] {.async.} =
|
||||||
if m.genesisState.isNil:
|
if m.genesisState.isNil:
|
||||||
|
@ -95,11 +109,29 @@ proc getGenesis*(m: MainchainMonitor): Future[BeaconState] {.async.} =
|
||||||
doAssert(not m.genesisState.isNil)
|
doAssert(not m.genesisState.isNil)
|
||||||
return m.genesisState[]
|
return m.genesisState[]
|
||||||
|
|
||||||
proc run(m: MainchainMonitor) {.async.} =
|
proc getBlockNumber(web3: Web3, hash: BlockHash): Future[Quantity] {.async.} =
|
||||||
m.web3 = await newWeb3(m.web3Url)
|
let blk = await web3.provider.eth_getBlockByHash(hash, false)
|
||||||
let ns = m.web3.contractSender(DepositContract, m.depositContractAddress)
|
return blk.number
|
||||||
|
|
||||||
let s = await ns.subscribe(DepositEvent, %*{"fromBlock": m.eth1Block}) do(pubkey: Bytes48, withdrawalCredentials: Bytes32, amount: Bytes8, signature: Bytes96, merkleTreeIndex: Bytes8, j: JsonNode):
|
proc run(m: MainchainMonitor, delayBeforeStart: Duration) {.async.} =
|
||||||
|
if delayBeforeStart != ZeroDuration:
|
||||||
|
await sleepAsync(delayBeforeStart)
|
||||||
|
|
||||||
|
let web3 = await newWeb3(m.web3Url)
|
||||||
|
defer: await web3.close()
|
||||||
|
|
||||||
|
let processFut = m.processDeposits(web3)
|
||||||
|
|
||||||
|
web3.onDisconnect = proc() =
|
||||||
|
error "Web3 server disconnected", ulr = m.web3Url
|
||||||
|
processFut.cancel()
|
||||||
|
|
||||||
|
let startBlkNum = await web3.getBlockNumber(m.eth1Block)
|
||||||
|
debug "Starting eth1 monitor", fromBlock = startBlkNum.uint64
|
||||||
|
|
||||||
|
let ns = web3.contractSender(DepositContract, m.depositContractAddress)
|
||||||
|
|
||||||
|
let s = await ns.subscribe(DepositEvent, %*{"fromBlock": startBlkNum}) do(pubkey: Bytes48, withdrawalCredentials: Bytes32, amount: Bytes8, signature: Bytes96, merkleTreeIndex: Bytes8, j: JsonNode):
|
||||||
|
|
||||||
let blkHash = BlockHash.fromHex(j["blockHash"].getStr())
|
let blkHash = BlockHash.fromHex(j["blockHash"].getStr())
|
||||||
let amount = bytes_to_int(array[8, byte](amount))
|
let amount = bytes_to_int(array[8, byte](amount))
|
||||||
|
@ -111,14 +143,27 @@ proc run(m: MainchainMonitor) {.async.} =
|
||||||
signature: ValidatorSig.init(array[96, byte](signature)))))
|
signature: ValidatorSig.init(array[96, byte](signature)))))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await m.processDeposits()
|
await processFut
|
||||||
finally:
|
finally:
|
||||||
await s.unsubscribe()
|
await s.unsubscribe()
|
||||||
# await m.web3.close()
|
|
||||||
m.web3 = nil
|
|
||||||
|
|
||||||
proc start*(m: MainchainMonitor) =
|
proc start(m: MainchainMonitor, delayBeforeStart: Duration) =
|
||||||
asyncCheck m.run()
|
if m.runFut.isNil:
|
||||||
|
let runFut = m.run(delayBeforeStart)
|
||||||
|
m.runFut = runFut
|
||||||
|
runFut.addCallback() do(p: pointer):
|
||||||
|
if runFut.failed and runFut == m.runFut:
|
||||||
|
error "Mainchain monitor failure, restarting", err = runFut.error.msg
|
||||||
|
m.runFut = nil
|
||||||
|
m.start(5.seconds)
|
||||||
|
|
||||||
|
proc start*(m: MainchainMonitor) {.inline.} =
|
||||||
|
m.start(0.seconds)
|
||||||
|
|
||||||
|
proc stop*(m: MainchainMonitor) =
|
||||||
|
if not m.runFut.isNil:
|
||||||
|
m.runFut.cancel()
|
||||||
|
m.runFut = nil
|
||||||
|
|
||||||
proc getPendingDeposits*(m: MainchainMonitor): seq[Deposit] =
|
proc getPendingDeposits*(m: MainchainMonitor): seq[Deposit] =
|
||||||
# This should be a simple accessor for the reference kept above
|
# This should be a simple accessor for the reference kept above
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 299adfa76f77384f4b3f90cded16e0f6a7e8f2c0
|
Subproject commit 2518a4161f723405004c3e2a743fa08ec67404dc
|
|
@ -1 +1 @@
|
||||||
Subproject commit 9214b095fb0266e5cac44804663343cd8288a84c
|
Subproject commit b6336cb7257a02a1074aaab7891150f2ecc83fc9
|
|
@ -1 +1 @@
|
||||||
Subproject commit 7bc29e747004b8aa7988eab537029ecab73dcb45
|
Subproject commit 89d7a0c8fd1eb0f749432bd7136d8f385351c48e
|
Loading…
Reference in New Issue