VC: various fixes (#2730)
* Fix firstSuccess() template missing timeouts. * Fix validator race condition. Fix logs to be compatible with beacon_node logs. Add CatchableError handlers to avoid crashes. Move some logs from Notice to Debug level. Fix some [unused] warnings. * Fix block proposal issue for slots in the past and from the future. * Change sent to published. * Address review comments #1.
This commit is contained in:
parent
3e3e17fec3
commit
f0c30e31b4
|
@ -186,7 +186,7 @@ programMain:
|
||||||
fatal "Not enough beacon nodes in command line"
|
fatal "Not enough beacon nodes in command line"
|
||||||
quit 1
|
quit 1
|
||||||
|
|
||||||
debug "Launching validator client", version = fullVersionStr,
|
notice "Launching validator client", version = fullVersionStr,
|
||||||
cmdParams = commandLineParams(),
|
cmdParams = commandLineParams(),
|
||||||
config,
|
config,
|
||||||
beacon_nodes_count = len(beaconNodes)
|
beacon_nodes_count = len(beaconNodes)
|
||||||
|
|
|
@ -342,27 +342,30 @@ template firstSuccessTimeout*(vc: ValidatorClientRef, respType: typedesc,
|
||||||
RestBeaconNodeStatus.Uninitalized}
|
RestBeaconNodeStatus.Uninitalized}
|
||||||
let offlineNodes = vc.beaconNodes.filterIt(it.status in offlineMask)
|
let offlineNodes = vc.beaconNodes.filterIt(it.status in offlineMask)
|
||||||
|
|
||||||
warn "There no beacon nodes available, refreshing nodes status",
|
warn "No working beacon nodes available, refreshing nodes status",
|
||||||
online_nodes = len(onlineNodes), offline_nodes = len(offlineNodes)
|
online_nodes = len(onlineNodes), offline_nodes = len(offlineNodes)
|
||||||
|
|
||||||
var checkFut = vc.checkNodes(offlineMask)
|
var checkFut = vc.checkNodes(offlineMask)
|
||||||
|
|
||||||
let checkOp =
|
let checkOp =
|
||||||
block:
|
block:
|
||||||
var dontRushFut = sleepAsync(500.milliseconds)
|
|
||||||
if isNil(timerFut):
|
if isNil(timerFut):
|
||||||
try:
|
try:
|
||||||
# We use `allFutures()` to keep result in `checkFut`, but still
|
# We use `allFutures()` to keep result in `checkFut`, but still
|
||||||
# be able to check errors.
|
# be able to check errors.
|
||||||
await allFutures(checkFut, dontRushFut)
|
await allFutures(checkFut)
|
||||||
|
let onlineCount = vc.beaconNodes.countIt(
|
||||||
|
it.status == RestBeaconNodeStatus.Online)
|
||||||
|
if onlineCount == 0:
|
||||||
|
# Small pause here to avoid continous spam beacon nodes with
|
||||||
|
# checking requests.
|
||||||
|
await sleepAsync(500.milliseconds)
|
||||||
ApiOperation.Success
|
ApiOperation.Success
|
||||||
except CancelledError:
|
except CancelledError:
|
||||||
# `allFutures()` could not cancel Futures.
|
# `allFutures()` could not cancel Futures.
|
||||||
if not(checkFut.finished()):
|
if not(checkFut.finished()):
|
||||||
checkFut.cancel()
|
checkFut.cancel()
|
||||||
if not(dontRushFut.finished()):
|
await allFutures(checkFut)
|
||||||
dontRushFut.cancel()
|
|
||||||
await allFutures(checkFut, dontRushFut)
|
|
||||||
ApiOperation.Interrupt
|
ApiOperation.Interrupt
|
||||||
except CatchableError as exc:
|
except CatchableError as exc:
|
||||||
# This only could happened if `race()` or `allFutures()` start raise
|
# This only could happened if `race()` or `allFutures()` start raise
|
||||||
|
@ -370,24 +373,26 @@ template firstSuccessTimeout*(vc: ValidatorClientRef, respType: typedesc,
|
||||||
ApiOperation.Failure
|
ApiOperation.Failure
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
discard await race(allFutures(checkFut, dontRushFut), timerFut)
|
discard await race(checkFut, timerFut)
|
||||||
if checkFut.finished():
|
if checkFut.finished():
|
||||||
|
let onlineCount = vc.beaconNodes.countIt(
|
||||||
|
it.status == RestBeaconNodeStatus.Online)
|
||||||
|
if onlineCount == 0:
|
||||||
|
# Small pause here to avoid continous spam beacon nodes with
|
||||||
|
# checking requests.
|
||||||
|
await sleepAsync(500.milliseconds)
|
||||||
ApiOperation.Success
|
ApiOperation.Success
|
||||||
else:
|
else:
|
||||||
checkFut.cancel()
|
checkFut.cancel()
|
||||||
if not(dontRushFut.finished()):
|
await allFutures(checkFut)
|
||||||
dontRushFut.cancel()
|
|
||||||
await allFutures(checkFut, dontRushFut)
|
|
||||||
ApiOperation.Timeout
|
ApiOperation.Timeout
|
||||||
except CancelledError:
|
except CancelledError:
|
||||||
# `race()` and `allFutures()` could not cancel Futures.
|
# `race()` and `allFutures()` could not cancel Futures.
|
||||||
if not(timerFut.finished()):
|
if not(timerFut.finished()):
|
||||||
timerFut.cancel()
|
timerFut.cancel()
|
||||||
if not(dontRushFut.finished()):
|
|
||||||
dontRushFut.cancel()
|
|
||||||
if not(checkFut.finished()):
|
if not(checkFut.finished()):
|
||||||
checkFut.cancel()
|
checkFut.cancel()
|
||||||
await allFutures(checkFut, dontRushFut, timerFut)
|
await allFutures(checkFut, timerFut)
|
||||||
ApiOperation.Interrupt
|
ApiOperation.Interrupt
|
||||||
except CatchableError as exc:
|
except CatchableError as exc:
|
||||||
# This only could happened if `race` or `allFutures` start raise
|
# This only could happened if `race` or `allFutures` start raise
|
||||||
|
|
|
@ -13,10 +13,12 @@ type
|
||||||
proc serveAttestation(service: AttestationServiceRef, adata: AttestationData,
|
proc serveAttestation(service: AttestationServiceRef, adata: AttestationData,
|
||||||
duty: DutyAndProof): Future[bool] {.async.} =
|
duty: DutyAndProof): Future[bool] {.async.} =
|
||||||
let vc = service.client
|
let vc = service.client
|
||||||
let validator = vc.attachedValidators.getValidator(duty.data.pubkey)
|
let validator =
|
||||||
if validator.index.isNone():
|
block:
|
||||||
warn "Validator index is missing", validator = validator.pubKey
|
let res = vc.getValidator(duty.data.pubkey)
|
||||||
|
if res.isNone():
|
||||||
return false
|
return false
|
||||||
|
res.get()
|
||||||
|
|
||||||
let fork = vc.fork.get()
|
let fork = vc.fork.get()
|
||||||
|
|
||||||
|
@ -32,10 +34,10 @@ proc serveAttestation(service: AttestationServiceRef, adata: AttestationData,
|
||||||
adata.source.epoch,
|
adata.source.epoch,
|
||||||
adata.target.epoch, signingRoot)
|
adata.target.epoch, signingRoot)
|
||||||
if notSlashable.isErr():
|
if notSlashable.isErr():
|
||||||
warn "Slashing protection activated for attestation", slot = duty.data.slot,
|
warn "Slashing protection activated for attestation",
|
||||||
validator = validator.pubKey,
|
slot = duty.data.slot,
|
||||||
validator_index = duty.data.validator_index,
|
validator = shortLog(validator),
|
||||||
badVoteDetails = $notSlashable.error
|
validator_index = vindex, badVoteDetails = $notSlashable.error
|
||||||
return false
|
return false
|
||||||
|
|
||||||
let attestation = await validator.produceAndSignAttestation(adata,
|
let attestation = await validator.produceAndSignAttestation(adata,
|
||||||
|
@ -46,24 +48,35 @@ proc serveAttestation(service: AttestationServiceRef, adata: AttestationData,
|
||||||
let res =
|
let res =
|
||||||
try:
|
try:
|
||||||
await vc.submitPoolAttestations(@[attestation])
|
await vc.submitPoolAttestations(@[attestation])
|
||||||
except ValidatorApiError as exc:
|
except ValidatorApiError:
|
||||||
error "Unable to submit attestation", slot = duty.data.slot,
|
error "Unable to publish attestation",
|
||||||
validator = validator.pubKey,
|
attestation = shortLog(attestation),
|
||||||
validator_index = duty.data.validator_index
|
validator = shortLog(validator),
|
||||||
raise exc
|
validator_index = vindex
|
||||||
|
return false
|
||||||
|
except CatchableError as exc:
|
||||||
|
error "Unexpected error occured while publishing attestation",
|
||||||
|
attestation = shortLog(attestation),
|
||||||
|
validator = shortLog(validator),
|
||||||
|
validator_index = vindex,
|
||||||
|
err_name = exc.name, err_msg = exc.msg
|
||||||
|
return false
|
||||||
|
|
||||||
let delay = vc.getDelay(seconds(int64(SECONDS_PER_SLOT) div 3))
|
let delay = vc.getDelay(seconds(int64(SECONDS_PER_SLOT) div 3))
|
||||||
|
let indexInCommittee = duty.data.validator_committee_index
|
||||||
if res:
|
if res:
|
||||||
notice "Attestation published", validator = validator.pubKey,
|
notice "Attestation published", attestation = shortLog(attestation),
|
||||||
validator_index = duty.data.validator_index, slot = duty.data.slot,
|
validator = shortLog(validator),
|
||||||
delay = delay
|
validator_index = vindex,
|
||||||
return true
|
delay = delay,
|
||||||
|
indexInCommittee = indexInCommittee
|
||||||
else:
|
else:
|
||||||
warn "Attestation was not accepted by beacon node",
|
warn "Attestation was not accepted by beacon node",
|
||||||
validator = validator.pubKey,
|
attestation = shortLog(attestation),
|
||||||
validator_index = duty.data.validator_index,
|
validator = shortLog(validator),
|
||||||
slot = duty.data.slot, delay = delay
|
validator_index = vindex, delay = delay,
|
||||||
return false
|
indexInCommittee = indexInCommittee
|
||||||
|
return res
|
||||||
|
|
||||||
proc serveAggregateAndProof*(service: AttestationServiceRef,
|
proc serveAggregateAndProof*(service: AttestationServiceRef,
|
||||||
proof: AggregateAndProof,
|
proof: AggregateAndProof,
|
||||||
|
@ -78,16 +91,40 @@ proc serveAggregateAndProof*(service: AttestationServiceRef,
|
||||||
genesisRoot)
|
genesisRoot)
|
||||||
let signedProof = SignedAggregateAndProof(message: proof,
|
let signedProof = SignedAggregateAndProof(message: proof,
|
||||||
signature: signature)
|
signature: signature)
|
||||||
|
|
||||||
|
let aggregationSlot = proof.aggregate.data.slot
|
||||||
|
let vindex = validator.index.get()
|
||||||
|
let res =
|
||||||
try:
|
try:
|
||||||
return await vc.publishAggregateAndProofs(@[signedProof]):
|
await vc.publishAggregateAndProofs(@[signedProof])
|
||||||
except ValidatorApiError:
|
except ValidatorApiError:
|
||||||
warn "Unable to publish aggregate and proofs"
|
error "Unable to publish aggregated attestation",
|
||||||
|
attestation = shortLog(signedProof.message.aggregate),
|
||||||
|
validator = shortLog(validator),
|
||||||
|
aggregationSlot = aggregationSlot,
|
||||||
|
validator_index = vindex
|
||||||
return false
|
return false
|
||||||
except CatchableError as exc:
|
except CatchableError as exc:
|
||||||
error "Unexpected error happened", err_name = exc.name,
|
error "Unexpected error occured while publishing aggregated attestation",
|
||||||
err_msg = exc.msg
|
attestation = shortLog(signedProof.message.aggregate),
|
||||||
|
validator = shortLog(validator),
|
||||||
|
aggregationSlot = aggregationSlot,
|
||||||
|
validator_index = vindex,
|
||||||
|
err_name = exc.name, err_msg = exc.msg
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
if res:
|
||||||
|
notice "Aggregated attestation published",
|
||||||
|
attestation = shortLog(signedProof.message.aggregate),
|
||||||
|
validator = shortLog(validator),
|
||||||
|
aggregationSlot = aggregationSlot, validator_index = vindex
|
||||||
|
else:
|
||||||
|
warn "Aggregated attestation was not accepted by beacon node",
|
||||||
|
attestation = shortLog(signedProof.message.aggregate),
|
||||||
|
validator = shortLog(validator),
|
||||||
|
aggregationSlot = aggregationSlot, validator_index = vindex
|
||||||
|
return res
|
||||||
|
|
||||||
proc produceAndPublishAttestations*(service: AttestationServiceRef,
|
proc produceAndPublishAttestations*(service: AttestationServiceRef,
|
||||||
slot: Slot, committee_index: CommitteeIndex,
|
slot: Slot, committee_index: CommitteeIndex,
|
||||||
duties: seq[DutyAndProof]
|
duties: seq[DutyAndProof]
|
||||||
|
@ -108,7 +145,8 @@ proc produceAndPublishAttestations*(service: AttestationServiceRef,
|
||||||
if (duty.data.slot != ad.slot) or
|
if (duty.data.slot != ad.slot) or
|
||||||
(uint64(duty.data.committee_index) != ad.index):
|
(uint64(duty.data.committee_index) != ad.index):
|
||||||
error "Inconsistent validator duties during attestation signing",
|
error "Inconsistent validator duties during attestation signing",
|
||||||
validator = duty.data.pubkey, duty_slot = duty.data.slot,
|
validator = shortLog(duty.data.pubkey),
|
||||||
|
duty_slot = duty.data.slot,
|
||||||
duty_index = duty.data.committee_index,
|
duty_index = duty.data.committee_index,
|
||||||
attestation_slot = ad.slot, attestation_index = ad.index
|
attestation_slot = ad.slot, attestation_index = ad.index
|
||||||
continue
|
continue
|
||||||
|
@ -182,7 +220,13 @@ proc produceAndPublishAggregates(service: AttestationServiceRef,
|
||||||
try:
|
try:
|
||||||
await vc.getAggregatedAttestation(slot, attestationRoot)
|
await vc.getAggregatedAttestation(slot, attestationRoot)
|
||||||
except ValidatorApiError:
|
except ValidatorApiError:
|
||||||
error "Unable to retrieve aggregated attestation data"
|
error "Unable to get aggregated attestation data", slot = slot,
|
||||||
|
attestation_root = shortLog(attestationRoot)
|
||||||
|
return
|
||||||
|
except CatchableError as exc:
|
||||||
|
error "Unexpected error occured while getting aggregated attestation",
|
||||||
|
slot = slot, attestation_root = shortLog(attestationRoot),
|
||||||
|
err_name = exc.name, err_msg = exc.msg
|
||||||
return
|
return
|
||||||
|
|
||||||
let pendingAggregates =
|
let pendingAggregates =
|
||||||
|
@ -219,13 +263,13 @@ proc produceAndPublishAggregates(service: AttestationServiceRef,
|
||||||
(succeed, errored, failed)
|
(succeed, errored, failed)
|
||||||
|
|
||||||
let delay = vc.getDelay(seconds((int64(SECONDS_PER_SLOT) div 3) * 2))
|
let delay = vc.getDelay(seconds((int64(SECONDS_PER_SLOT) div 3) * 2))
|
||||||
debug "Aggregate attestation statistics", total = len(pendingAggregates),
|
debug "Aggregated attestation statistics", total = len(pendingAggregates),
|
||||||
succeed = statistics[0], failed_to_deliver = statistics[1],
|
succeed = statistics[0], failed_to_deliver = statistics[1],
|
||||||
not_accepted = statistics[2], delay = delay, slot = slot,
|
not_accepted = statistics[2], delay = delay, slot = slot,
|
||||||
committee_index = committeeIndex
|
committee_index = committeeIndex
|
||||||
|
|
||||||
else:
|
else:
|
||||||
notice "No aggregate and proofs scheduled for slot", slot = slot,
|
debug "No aggregate and proofs scheduled for slot", slot = slot,
|
||||||
committee_index = committeeIndex
|
committee_index = committeeIndex
|
||||||
|
|
||||||
proc publishAttestationsAndAggregates(service: AttestationServiceRef,
|
proc publishAttestationsAndAggregates(service: AttestationServiceRef,
|
||||||
|
@ -242,22 +286,26 @@ proc publishAttestationsAndAggregates(service: AttestationServiceRef,
|
||||||
# TODO (cheatfate): Here should be present timeout.
|
# TODO (cheatfate): Here should be present timeout.
|
||||||
let startTime = Moment.now()
|
let startTime = Moment.now()
|
||||||
await vc.waitForBlockPublished(slot)
|
await vc.waitForBlockPublished(slot)
|
||||||
let finishTime = Moment.now()
|
let dur = Moment.now() - startTime
|
||||||
debug "Block proposal awaited", slot = slot,
|
debug "Block proposal awaited", slot = slot, duration = dur
|
||||||
duration = (finishTime - startTime)
|
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let delay = vc.getDelay(seconds(int64(SECONDS_PER_SLOT) div 3))
|
let delay = vc.getDelay(seconds(int64(SECONDS_PER_SLOT) div 3))
|
||||||
notice "Producing attestations", delay = delay, slot = slot,
|
debug "Producing attestations", delay = delay, slot = slot,
|
||||||
committee_index = committee_index,
|
committee_index = committee_index,
|
||||||
duties_count = len(duties)
|
duties_count = len(duties)
|
||||||
|
|
||||||
let ad =
|
let ad =
|
||||||
try:
|
try:
|
||||||
await service.produceAndPublishAttestations(slot, committee_index,
|
await service.produceAndPublishAttestations(slot, committee_index, duties)
|
||||||
duties)
|
|
||||||
except ValidatorApiError:
|
except ValidatorApiError:
|
||||||
error "Unable to proceed attestations"
|
error "Unable to proceed attestations", slot = slot,
|
||||||
|
committee_index = committee_index, duties_count = len(duties)
|
||||||
|
return
|
||||||
|
except CatchableError as exc:
|
||||||
|
error "Unexpected error while producing attestations", slot = slot,
|
||||||
|
committee_index = committee_index, duties_count = len(duties),
|
||||||
|
err_name = exc.name, err_msg = exc.msg
|
||||||
return
|
return
|
||||||
|
|
||||||
if aggregateTime != ZeroDuration:
|
if aggregateTime != ZeroDuration:
|
||||||
|
@ -265,7 +313,7 @@ proc publishAttestationsAndAggregates(service: AttestationServiceRef,
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let delay = vc.getDelay(seconds((int64(SECONDS_PER_SLOT) div 3) * 2))
|
let delay = vc.getDelay(seconds((int64(SECONDS_PER_SLOT) div 3) * 2))
|
||||||
notice "Producing aggregate and proofs", delay = delay
|
debug "Producing aggregate and proofs", delay = delay
|
||||||
await service.produceAndPublishAggregates(ad, duties)
|
await service.produceAndPublishAggregates(ad, duties)
|
||||||
|
|
||||||
proc spawnAttestationTasks(service: AttestationServiceRef,
|
proc spawnAttestationTasks(service: AttestationServiceRef,
|
||||||
|
|
|
@ -5,11 +5,6 @@ logScope: service = "block_service"
|
||||||
|
|
||||||
proc publishBlock(vc: ValidatorClientRef, currentSlot, slot: Slot,
|
proc publishBlock(vc: ValidatorClientRef, currentSlot, slot: Slot,
|
||||||
validator: AttachedValidator) {.async.} =
|
validator: AttachedValidator) {.async.} =
|
||||||
logScope:
|
|
||||||
validator = validator.pubKey
|
|
||||||
slot = slot
|
|
||||||
wallSlot = currentSlot
|
|
||||||
|
|
||||||
let
|
let
|
||||||
genesisRoot = vc.beaconGenesis.genesis_validators_root
|
genesisRoot = vc.beaconGenesis.genesis_validators_root
|
||||||
graffiti =
|
graffiti =
|
||||||
|
@ -19,21 +14,26 @@ proc publishBlock(vc: ValidatorClientRef, currentSlot, slot: Slot,
|
||||||
defaultGraffitiBytes()
|
defaultGraffitiBytes()
|
||||||
fork = vc.fork.get()
|
fork = vc.fork.get()
|
||||||
|
|
||||||
debug "Publishing block", validator = validator.pubKey,
|
debug "Publishing block", validator = shortLog(validator),
|
||||||
delay = vc.getDelay(ZeroDuration),
|
delay = vc.getDelay(ZeroDuration),
|
||||||
|
wall_slot = currentSlot,
|
||||||
genesis_root = genesisRoot,
|
genesis_root = genesisRoot,
|
||||||
graffiti = graffiti, fork = fork, slot = slot,
|
graffiti = graffiti, fork = fork, slot = slot,
|
||||||
wall_slot = currentSlot
|
wall_slot = currentSlot
|
||||||
|
|
||||||
try:
|
try:
|
||||||
let randaoReveal = await validator.genRandaoReveal(fork, genesisRoot, slot)
|
let randaoReveal = await validator.genRandaoReveal(fork, genesisRoot, slot)
|
||||||
let beaconBlock =
|
let beaconBlock =
|
||||||
try:
|
try:
|
||||||
await vc.produceBlock(slot, randaoReveal, graffiti)
|
await vc.produceBlock(slot, randaoReveal, graffiti)
|
||||||
except ValidatorApiError as exc:
|
except ValidatorApiError:
|
||||||
error "Unable to retrieve block data", slot = currentSlot,
|
error "Unable to retrieve block data", slot = slot,
|
||||||
validator = validator.pubKey
|
wall_slot = currentSlot, validator = shortLog(validator)
|
||||||
return
|
return
|
||||||
|
except CatchableError as exc:
|
||||||
|
error "An unexpected error occurred while getting block data",
|
||||||
|
err_name = exc.name, err_msg = exc.msg
|
||||||
|
return
|
||||||
|
|
||||||
let blockRoot = hash_tree_root(beaconBlock)
|
let blockRoot = hash_tree_root(beaconBlock)
|
||||||
var signedBlock = SignedBeaconBlock(message: beaconBlock,
|
var signedBlock = SignedBeaconBlock(message: beaconBlock,
|
||||||
root: hash_tree_root(beaconBlock))
|
root: hash_tree_root(beaconBlock))
|
||||||
|
@ -55,28 +55,33 @@ proc publishBlock(vc: ValidatorClientRef, currentSlot, slot: Slot,
|
||||||
try:
|
try:
|
||||||
await vc.publishBlock(signedBlock)
|
await vc.publishBlock(signedBlock)
|
||||||
except ValidatorApiError:
|
except ValidatorApiError:
|
||||||
error "Unable to submit block", slot = currentSlot,
|
error "Unable to publish block", blck = shortLog(signedBlock.message),
|
||||||
validator = validator.pubKey, block_root = blockRoot,
|
blockRoot = shortLog(blockRoot),
|
||||||
deposits = len(signedBlock.message.body.deposits),
|
validator = shortLog(validator),
|
||||||
attestations = len(signedBlock.message.body.attestations),
|
validator_index = validator.index.get(),
|
||||||
graffiti = graffiti
|
wall_slot = currentSlot
|
||||||
|
return
|
||||||
|
except CatchableError as exc:
|
||||||
|
error "An unexpected error occurred while publishing block",
|
||||||
|
err_name = exc.name, err_msg = exc.msg
|
||||||
return
|
return
|
||||||
if res:
|
if res:
|
||||||
notice "Block published", slot = currentSlot,
|
notice "Block published", blck = shortLog(signedBlock.message),
|
||||||
validator = validator.pubKey, validator_index = validator.index.get(),
|
blockRoot = shortLog(blockRoot), validator = shortLog(validator),
|
||||||
deposits = len(signedBlock.message.body.deposits),
|
validator_index = validator.index.get()
|
||||||
attestations = len(signedBlock.message.body.attestations),
|
|
||||||
graffiti = graffiti
|
|
||||||
else:
|
else:
|
||||||
warn "Block was not accepted by beacon node", slot = currentSlot,
|
warn "Block was not accepted by beacon node",
|
||||||
validator = validator.pubKey, validator_index = validator.index.get(),
|
blck = shortLog(signedBlock.message),
|
||||||
deposits = len(signedBlock.message.body.deposits),
|
blockRoot = shortLog(blockRoot),
|
||||||
attestations = len(signedBlock.message.body.attestations),
|
validator = shortLog(validator),
|
||||||
graffiti = graffiti
|
validator_index = validator.index.get(),
|
||||||
|
wall_slot = currentSlot
|
||||||
else:
|
else:
|
||||||
warn "Slashing protection activated for block proposal",
|
warn "Slashing protection activated for block proposal",
|
||||||
slot = currentSlot, validator = validator.pubKey,
|
blck = shortLog(beaconBlock), blockRoot = shortLog(blockRoot),
|
||||||
|
validator = shortLog(validator),
|
||||||
validator_index = validator.index.get(),
|
validator_index = validator.index.get(),
|
||||||
|
wall_slot = currentSlot,
|
||||||
existingProposal = notSlashable.error
|
existingProposal = notSlashable.error
|
||||||
except CatchableError as exc:
|
except CatchableError as exc:
|
||||||
error "Unexpected error happens while proposing block",
|
error "Unexpected error happens while proposing block",
|
||||||
|
@ -87,25 +92,27 @@ proc proposeBlock(vc: ValidatorClientRef, slot: Slot,
|
||||||
let (inFuture, timeToSleep) = vc.beaconClock.fromNow(slot)
|
let (inFuture, timeToSleep) = vc.beaconClock.fromNow(slot)
|
||||||
try:
|
try:
|
||||||
if inFuture:
|
if inFuture:
|
||||||
debug "Proposing block", timeIn = timeToSleep, validator = proposerKey
|
debug "Proposing block", timeIn = timeToSleep,
|
||||||
|
validator = shortLog(proposerKey)
|
||||||
await sleepAsync(timeToSleep)
|
await sleepAsync(timeToSleep)
|
||||||
else:
|
else:
|
||||||
debug "Proposing block", timeIn = 0.seconds, validator = proposerKey
|
debug "Proposing block", timeIn = 0.seconds,
|
||||||
|
validator = shortLog(proposerKey)
|
||||||
|
|
||||||
let sres = vc.getCurrentSlot()
|
let sres = vc.getCurrentSlot()
|
||||||
if sres.isSome():
|
if sres.isSome():
|
||||||
let currentSlot = sres.get()
|
let currentSlot = sres.get()
|
||||||
# We need to check that we still have validator in our pool.
|
let validator =
|
||||||
let validator = vc.attachedValidators.getValidator(proposerKey)
|
block:
|
||||||
if isNil(validator):
|
let res = vc.getValidator(proposerKey)
|
||||||
debug "Validator is not present in pool anymore, exiting",
|
if res.isNone():
|
||||||
validator = proposerKey
|
|
||||||
return
|
return
|
||||||
|
res.get()
|
||||||
await vc.publishBlock(currentSlot, slot, validator)
|
await vc.publishBlock(currentSlot, slot, validator)
|
||||||
|
|
||||||
except CancelledError:
|
except CancelledError:
|
||||||
debug "Proposing task was cancelled", slot = slot, validator = proposerKey
|
debug "Proposing task was cancelled", slot = slot,
|
||||||
|
validator = shortLog(proposerKey)
|
||||||
|
|
||||||
proc spawnProposalTask(vc: ValidatorClientRef,
|
proc spawnProposalTask(vc: ValidatorClientRef,
|
||||||
duty: RestProposerDuty): ProposerTask =
|
duty: RestProposerDuty): ProposerTask =
|
||||||
|
@ -124,15 +131,36 @@ proc contains(data: openArray[ProposerTask], duty: RestProposerDuty): bool =
|
||||||
return true
|
return true
|
||||||
false
|
false
|
||||||
|
|
||||||
|
proc checkDuty(duty: RestProposerDuty, epoch: Epoch, slot: Slot): bool =
|
||||||
|
let lastSlot = compute_start_slot_at_epoch(epoch + 1'u64)
|
||||||
|
if duty.slot >= slot:
|
||||||
|
if duty.slot < lastSlot:
|
||||||
|
true
|
||||||
|
else:
|
||||||
|
warn "Block proposal duty is in the far future, ignoring",
|
||||||
|
duty_slot = duty.slot, validator = shortLog(duty.pubkey),
|
||||||
|
wall_slot = slot, last_slot_in_epoch = (lastSlot - 1'u64)
|
||||||
|
false
|
||||||
|
else:
|
||||||
|
warn "Block proposal duty is in the past, ignoring", duty_slot = duty.slot,
|
||||||
|
validator = shortLog(duty.pubkey), wall_slot = slot
|
||||||
|
false
|
||||||
|
|
||||||
proc addOrReplaceProposers*(vc: ValidatorClientRef, epoch: Epoch,
|
proc addOrReplaceProposers*(vc: ValidatorClientRef, epoch: Epoch,
|
||||||
dependentRoot: Eth2Digest,
|
dependentRoot: Eth2Digest,
|
||||||
duties: openArray[RestProposerDuty]) =
|
duties: openArray[RestProposerDuty]) =
|
||||||
let epochDuties = vc.proposers.getOrDefault(epoch)
|
let default = ProposedData(epoch: Epoch(0xFFFF_FFFF_FFFF_FFFF'u64))
|
||||||
|
let sres = vc.getCurrentSlot()
|
||||||
|
if sres.isSome():
|
||||||
|
let
|
||||||
|
currentSlot = sres.get()
|
||||||
|
epochDuties = vc.proposers.getOrDefault(epoch, default)
|
||||||
if not(epochDuties.isDefault()):
|
if not(epochDuties.isDefault()):
|
||||||
if epochDuties.dependentRoot != dependentRoot:
|
if epochDuties.dependentRoot != dependentRoot:
|
||||||
warn "Proposer duties re-organization",
|
warn "Proposer duties re-organization", duties_count = len(duties),
|
||||||
|
wall_slot = currentSlot, epoch = epoch,
|
||||||
prior_dependent_root = epochDuties.dependentRoot,
|
prior_dependent_root = epochDuties.dependentRoot,
|
||||||
dependent_root = dependentRoot
|
dependent_root = dependentRoot, wall_slot = currentSlot
|
||||||
let tasks =
|
let tasks =
|
||||||
block:
|
block:
|
||||||
var res: seq[ProposerTask]
|
var res: seq[ProposerTask]
|
||||||
|
@ -142,18 +170,21 @@ proc addOrReplaceProposers*(vc: ValidatorClientRef, epoch: Epoch,
|
||||||
if task notin duties:
|
if task notin duties:
|
||||||
# Task is no more relevant, so cancel it.
|
# Task is no more relevant, so cancel it.
|
||||||
debug "Cancelling running proposal duty task",
|
debug "Cancelling running proposal duty task",
|
||||||
slot = task.duty.slot, validator = task.duty.pubkey
|
slot = task.duty.slot,
|
||||||
|
validator = shortLog(task.duty.pubkey)
|
||||||
task.future.cancel()
|
task.future.cancel()
|
||||||
else:
|
else:
|
||||||
# If task is already running for proper slot, we keep it alive.
|
# If task is already running for proper slot, we keep it alive.
|
||||||
debug "Keep running previous proposal duty task",
|
debug "Keep running previous proposal duty task",
|
||||||
slot = task.duty.slot, validator = task.duty.pubkey
|
slot = task.duty.slot,
|
||||||
|
validator = shortLog(task.duty.pubkey)
|
||||||
res.add(task)
|
res.add(task)
|
||||||
|
|
||||||
for duty in duties:
|
for duty in duties:
|
||||||
if duty notin res:
|
if duty notin res:
|
||||||
debug "New proposal duty received", slot = duty.slot,
|
debug "New proposal duty received", slot = duty.slot,
|
||||||
validator = duty.pubkey
|
validator = shortLog(duty.pubkey)
|
||||||
|
if checkDuty(duty, epoch, currentSlot):
|
||||||
let task = vc.spawnProposalTask(duty)
|
let task = vc.spawnProposalTask(duty)
|
||||||
if duty.slot in hashset:
|
if duty.slot in hashset:
|
||||||
error "Multiple block proposers for this slot, " &
|
error "Multiple block proposers for this slot, " &
|
||||||
|
@ -164,6 +195,9 @@ proc addOrReplaceProposers*(vc: ValidatorClientRef, epoch: Epoch,
|
||||||
res
|
res
|
||||||
vc.proposers[epoch] = ProposedData.init(epoch, dependentRoot, tasks)
|
vc.proposers[epoch] = ProposedData.init(epoch, dependentRoot, tasks)
|
||||||
else:
|
else:
|
||||||
|
debug "New block proposal duties received",
|
||||||
|
dependent_root = dependentRoot, duties_count = len(duties),
|
||||||
|
wall_slot = currentSlot, epoch = epoch
|
||||||
# Spawn new proposer tasks and modify proposers map.
|
# Spawn new proposer tasks and modify proposers map.
|
||||||
let tasks =
|
let tasks =
|
||||||
block:
|
block:
|
||||||
|
@ -171,7 +205,8 @@ proc addOrReplaceProposers*(vc: ValidatorClientRef, epoch: Epoch,
|
||||||
var res: seq[ProposerTask]
|
var res: seq[ProposerTask]
|
||||||
for duty in duties:
|
for duty in duties:
|
||||||
debug "New proposal duty received", slot = duty.slot,
|
debug "New proposal duty received", slot = duty.slot,
|
||||||
validator = duty.pubkey
|
validator = shortLog(duty.pubkey)
|
||||||
|
if checkDuty(duty, epoch, currentSlot):
|
||||||
let task = vc.spawnProposalTask(duty)
|
let task = vc.spawnProposalTask(duty)
|
||||||
if duty.slot in hashset:
|
if duty.slot in hashset:
|
||||||
error "Multiple block proposers for this slot, " &
|
error "Multiple block proposers for this slot, " &
|
||||||
|
|
|
@ -244,3 +244,16 @@ proc getDelay*(vc: ValidatorClientRef, instant: Duration): Duration =
|
||||||
let slotStartTime = currentBeaconTime.slotOrZero().toBeaconTime()
|
let slotStartTime = currentBeaconTime.slotOrZero().toBeaconTime()
|
||||||
let idealTime = Duration(slotStartTime) + instant
|
let idealTime = Duration(slotStartTime) + instant
|
||||||
currentTime - idealTime
|
currentTime - idealTime
|
||||||
|
|
||||||
|
proc getValidator*(vc: ValidatorClientRef,
|
||||||
|
key: ValidatorPubkey): Option[AttachedValidator] =
|
||||||
|
let validator = vc.attachedValidators.getValidator(key)
|
||||||
|
if isNil(validator):
|
||||||
|
warn "Validator not in pool anymore", validator = shortLog(validator)
|
||||||
|
none[AttachedValidator]()
|
||||||
|
else:
|
||||||
|
if validator.index.isNone():
|
||||||
|
warn "Validator index is missing", validator = shortLog(validator)
|
||||||
|
none[AttachedValidator]()
|
||||||
|
else:
|
||||||
|
some(validator)
|
||||||
|
|
|
@ -48,8 +48,12 @@ proc pollForValidatorIndices*(vc: ValidatorClientRef) {.async.} =
|
||||||
let res =
|
let res =
|
||||||
try:
|
try:
|
||||||
await vc.getValidators(idents)
|
await vc.getValidators(idents)
|
||||||
except ValidatorApiError as exc:
|
except ValidatorApiError:
|
||||||
error "Unable to retrieve head state's validator information"
|
error "Unable to get head state's validator information"
|
||||||
|
return
|
||||||
|
except CatchableError as exc:
|
||||||
|
error "Unexpected error occurred while getting validator information",
|
||||||
|
err_name = exc.name, err_msg = exc.msg
|
||||||
return
|
return
|
||||||
|
|
||||||
for item in res:
|
for item in res:
|
||||||
|
@ -94,8 +98,12 @@ proc pollForAttesterDuties*(vc: ValidatorClientRef,
|
||||||
let res =
|
let res =
|
||||||
try:
|
try:
|
||||||
await vc.getAttesterDuties(epoch, indices)
|
await vc.getAttesterDuties(epoch, indices)
|
||||||
except ValidatorApiError as exc:
|
except ValidatorApiError:
|
||||||
error "Unable to retrieve attester duties", epoch = epoch
|
error "Unable to get attester duties", epoch = epoch
|
||||||
|
return 0
|
||||||
|
except CatchableError as exc:
|
||||||
|
error "Unexpected error occured while getting attester duties",
|
||||||
|
epoch = epoch, err_name = exc.name, err_msg = exc.msg
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
if currentRoot.isNone():
|
if currentRoot.isNone():
|
||||||
|
@ -264,9 +272,13 @@ proc pollForBeaconProposers*(vc: ValidatorClientRef) {.async.} =
|
||||||
else:
|
else:
|
||||||
debug "No relevant proposer duties received", slot = currentSlot,
|
debug "No relevant proposer duties received", slot = currentSlot,
|
||||||
duties_count = len(duties)
|
duties_count = len(duties)
|
||||||
except ValidatorApiError as exc:
|
except ValidatorApiError:
|
||||||
debug "Unable to retrieve proposer duties", slot = currentSlot,
|
debug "Unable to get proposer duties", slot = currentSlot,
|
||||||
epoch = currentEpoch
|
epoch = currentEpoch
|
||||||
|
except CatchableError as exc:
|
||||||
|
debug "Unexpected error occured while getting proposer duties",
|
||||||
|
slot = currentSlot, epoch = currentEpoch, err_name = exc.name,
|
||||||
|
err_msg = exc.msg
|
||||||
|
|
||||||
vc.pruneBeaconProposers(currentEpoch)
|
vc.pruneBeaconProposers(currentEpoch)
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,10 @@ proc pollForFork(vc: ValidatorClientRef) {.async.} =
|
||||||
except ValidatorApiError as exc:
|
except ValidatorApiError as exc:
|
||||||
error "Unable to retrieve head state's fork", reason = exc.msg
|
error "Unable to retrieve head state's fork", reason = exc.msg
|
||||||
return
|
return
|
||||||
|
except CatchableError as exc:
|
||||||
|
error "Unexpected error occured while getting fork information",
|
||||||
|
err_name = exc.name, err_msg = exc.msg
|
||||||
|
return
|
||||||
|
|
||||||
if vc.fork.isNone() or vc.fork.get() != fork:
|
if vc.fork.isNone() or vc.fork.get() != fork:
|
||||||
vc.fork = some(fork)
|
vc.fork = some(fork)
|
||||||
|
|
Loading…
Reference in New Issue