mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-02-23 11:48:33 +00:00
Carry out the sync committee gossip duties
Other changes: * Add server getBlockV2(), and produceBlockV2(). * Add getBlockV2() to REST test suite. * Add client getBlockV2(), and produceBlockV2(). * Fix URLs in comments. * Add some primitives and fix some issues in forks.nim. * Switch `validator_client` to V2 calls usage. * Bump `chronos` with imports fixes. * Bump `nim-json-serialization` for `requireAllFields`.
This commit is contained in:
parent
82eac7a522
commit
3689c68cbf
@ -56,7 +56,7 @@ export
|
|||||||
# Eventually, we could also differentiate between user/tainted data and
|
# Eventually, we could also differentiate between user/tainted data and
|
||||||
# internal state that's gone through sanity checks already.
|
# internal state that's gone through sanity checks already.
|
||||||
|
|
||||||
const SPEC_VERSION* = "1.0.1"
|
const SPEC_VERSION* = "1.1.0"
|
||||||
## Spec version we're aiming to be compatible with, right now
|
## Spec version we're aiming to be compatible with, right now
|
||||||
|
|
||||||
const
|
const
|
||||||
|
@ -724,13 +724,14 @@ proc publishAggregateAndProofs*(vc: ValidatorClientRef,
|
|||||||
raise newException(ValidatorApiError,
|
raise newException(ValidatorApiError,
|
||||||
"Unable to publish aggregate and proofs")
|
"Unable to publish aggregate and proofs")
|
||||||
|
|
||||||
proc produceBlock*(vc: ValidatorClientRef, slot: Slot,
|
proc produceBlockV2*(vc: ValidatorClientRef, slot: Slot,
|
||||||
randao_reveal: ValidatorSig,
|
randao_reveal: ValidatorSig,
|
||||||
graffiti: GraffitiBytes): Future[phase0.BeaconBlock] {.async.} =
|
graffiti: GraffitiBytes): Future[ProduceBlockResponseV2] {.
|
||||||
logScope: request = "produceBlock"
|
async.} =
|
||||||
vc.firstSuccessTimeout(RestResponse[ProduceBlockResponse],
|
logScope: request = "produceBlockV2"
|
||||||
|
vc.firstSuccessTimeout(RestResponse[ProduceBlockResponseV2],
|
||||||
SlotDuration,
|
SlotDuration,
|
||||||
produceBlock(it, slot, randao_reveal, graffiti)):
|
produceBlockV2(it, slot, randao_reveal, graffiti)):
|
||||||
if apiResponse.isErr():
|
if apiResponse.isErr():
|
||||||
debug "Unable to retrieve block data", endpoint = node,
|
debug "Unable to retrieve block data", endpoint = node,
|
||||||
error = apiResponse.error()
|
error = apiResponse.error()
|
||||||
@ -740,7 +741,7 @@ proc produceBlock*(vc: ValidatorClientRef, slot: Slot,
|
|||||||
case response.status:
|
case response.status:
|
||||||
of 200:
|
of 200:
|
||||||
debug "Received successfull response", endpoint = node
|
debug "Received successfull response", endpoint = node
|
||||||
return response.data.data
|
return response.data
|
||||||
of 400:
|
of 400:
|
||||||
debug "Received invalid request response",
|
debug "Received invalid request response",
|
||||||
response_code = response.status, endpoint = node
|
response_code = response.status, endpoint = node
|
||||||
@ -761,10 +762,15 @@ proc produceBlock*(vc: ValidatorClientRef, slot: Slot,
|
|||||||
raise newException(ValidatorApiError, "Unable to retrieve block data")
|
raise newException(ValidatorApiError, "Unable to retrieve block data")
|
||||||
|
|
||||||
proc publishBlock*(vc: ValidatorClientRef,
|
proc publishBlock*(vc: ValidatorClientRef,
|
||||||
data: phase0.SignedBeaconBlock): Future[bool] {.async.} =
|
data: ForkedSignedBeaconBlock): Future[bool] {.async.} =
|
||||||
logScope: request = "publishBlock"
|
logScope: request = "publishBlock"
|
||||||
vc.firstSuccessTimeout(RestPlainResponse,
|
vc.firstSuccessTimeout(RestPlainResponse, SlotDuration):
|
||||||
SlotDuration, publishBlock(it, data)):
|
case data.kind
|
||||||
|
of BeaconBlockFork.Phase0:
|
||||||
|
publishBlock(it, data.phase0Block)
|
||||||
|
of BeaconBlockFork.Altair:
|
||||||
|
publishBlock(it, data.altairBlock)
|
||||||
|
do:
|
||||||
if apiResponse.isErr():
|
if apiResponse.isErr():
|
||||||
debug "Unable to publish block", endpoint = node,
|
debug "Unable to publish block", endpoint = node,
|
||||||
error = apiResponse.error()
|
error = apiResponse.error()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import ../spec/datatypes/[phase0, altair]
|
import ".."/spec/forks
|
||||||
import common, api
|
import common, api
|
||||||
import chronicles
|
import chronicles
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ proc publishBlock(vc: ValidatorClientRef, currentSlot, slot: Slot,
|
|||||||
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.produceBlockV2(slot, randaoReveal, graffiti)
|
||||||
except ValidatorApiError:
|
except ValidatorApiError:
|
||||||
error "Unable to retrieve block data", slot = slot,
|
error "Unable to retrieve block data", slot = slot,
|
||||||
wall_slot = currentSlot, validator = shortLog(validator)
|
wall_slot = currentSlot, validator = shortLog(validator)
|
||||||
@ -36,25 +36,20 @@ proc publishBlock(vc: ValidatorClientRef, currentSlot, slot: Slot,
|
|||||||
return
|
return
|
||||||
|
|
||||||
let blockRoot = hash_tree_root(beaconBlock)
|
let blockRoot = hash_tree_root(beaconBlock)
|
||||||
var signedBlock = phase0.SignedBeaconBlock(message: beaconBlock,
|
|
||||||
root: hash_tree_root(beaconBlock))
|
|
||||||
|
|
||||||
# TODO: signing_root is recomputed in signBlockProposal just after
|
# TODO: signing_root is recomputed in signBlockProposal just after
|
||||||
let signing_root = compute_block_root(fork, genesisRoot, slot,
|
let signing_root = compute_block_root(fork, genesisRoot, slot,
|
||||||
signedBlock.root)
|
blockRoot)
|
||||||
let notSlashable = vc.attachedValidators
|
let notSlashable = vc.attachedValidators
|
||||||
.slashingProtection
|
.slashingProtection
|
||||||
.registerBlock(ValidatorIndex(signedBlock.message.proposer_index),
|
.registerBlock(ValidatorIndex(beaconBlock.proposer_index),
|
||||||
validator.pubKey, slot, signing_root)
|
validator.pubKey, slot, signing_root)
|
||||||
|
|
||||||
if notSlashable.isOk():
|
if notSlashable.isOk():
|
||||||
let signature = await validator.signBlockProposal(fork, genesisRoot, slot,
|
let signature = await validator.signBlockProposal(fork, genesisRoot, slot,
|
||||||
blockRoot)
|
blockRoot)
|
||||||
let signedBlock =
|
let signedBlock = ForkedSignedBeaconBlock.init(beaconBlock, blockRoot,
|
||||||
phase0.SignedBeaconBlock(message: beaconBlock, root: blockRoot,
|
signature)
|
||||||
signature: signature)
|
debug "Sending block", blck = shortLog(signedBlock),
|
||||||
|
|
||||||
debug "Sending block", blck = shortLog(signedBlock.message),
|
|
||||||
signature = shortLog(signature), blockRoot = shortLog(blockRoot),
|
signature = shortLog(signature), blockRoot = shortLog(blockRoot),
|
||||||
validator = shortLog(validator)
|
validator = shortLog(validator)
|
||||||
|
|
||||||
@ -62,7 +57,7 @@ proc publishBlock(vc: ValidatorClientRef, currentSlot, slot: Slot,
|
|||||||
try:
|
try:
|
||||||
await vc.publishBlock(signedBlock)
|
await vc.publishBlock(signedBlock)
|
||||||
except ValidatorApiError:
|
except ValidatorApiError:
|
||||||
error "Unable to publish block", blck = shortLog(signedBlock.message),
|
error "Unable to publish block", blck = shortLog(signedBlock),
|
||||||
blockRoot = shortLog(blockRoot),
|
blockRoot = shortLog(blockRoot),
|
||||||
validator = shortLog(validator),
|
validator = shortLog(validator),
|
||||||
validator_index = validator.index.get(),
|
validator_index = validator.index.get(),
|
||||||
@ -73,12 +68,12 @@ proc publishBlock(vc: ValidatorClientRef, currentSlot, slot: Slot,
|
|||||||
err_name = exc.name, err_msg = exc.msg
|
err_name = exc.name, err_msg = exc.msg
|
||||||
return
|
return
|
||||||
if res:
|
if res:
|
||||||
notice "Block published", blck = shortLog(signedBlock.message),
|
notice "Block published", blck = shortLog(signedBlock),
|
||||||
blockRoot = shortLog(blockRoot), validator = shortLog(validator),
|
blockRoot = shortLog(blockRoot), validator = shortLog(validator),
|
||||||
validator_index = validator.index.get()
|
validator_index = validator.index.get()
|
||||||
else:
|
else:
|
||||||
warn "Block was not accepted by beacon node",
|
warn "Block was not accepted by beacon node",
|
||||||
blck = shortLog(signedBlock.message),
|
blck = shortLog(signedBlock),
|
||||||
blockRoot = shortLog(blockRoot),
|
blockRoot = shortLog(blockRoot),
|
||||||
validator = shortLog(validator),
|
validator = shortLog(validator),
|
||||||
validator_index = validator.index.get(),
|
validator_index = validator.index.get(),
|
||||||
|
@ -584,6 +584,147 @@ proc handleAttestations(node: BeaconNode, head: BlockRef, slot: Slot) =
|
|||||||
validator = validator.pubkey,
|
validator = validator.pubkey,
|
||||||
badVoteDetails = $registered.error()
|
badVoteDetails = $registered.error()
|
||||||
|
|
||||||
|
proc createAndSendSyncCommitteeMessage(node: BeaconNode,
|
||||||
|
slot: Slot,
|
||||||
|
validator: AttachedValidator,
|
||||||
|
committeeIdx: SyncCommitteeIndex,
|
||||||
|
head: BlockRef) {.async.} =
|
||||||
|
try:
|
||||||
|
let
|
||||||
|
fork = node.dag.forkAtEpoch(slot.epoch)
|
||||||
|
genesisValidatorsRoot = node.dag.genesisValidatorsRoot
|
||||||
|
msg = await signSyncCommitteeMessage(validator, slot, fork,
|
||||||
|
genesisValidatorsRoot, head.root)
|
||||||
|
|
||||||
|
let ok = await node.sendSyncCommitteeMessage(
|
||||||
|
msg, committeeIdx, checkSignature = false)
|
||||||
|
if not ok: # Logged in sendSyncCommitteeMessage
|
||||||
|
return
|
||||||
|
|
||||||
|
if node.config.dumpEnabled:
|
||||||
|
dump(node.config.dumpDirOutgoing, msg, validator.pubKey)
|
||||||
|
|
||||||
|
let
|
||||||
|
wallTime = node.beaconClock.now()
|
||||||
|
deadline = msg.slot.toBeaconTime() +
|
||||||
|
seconds(int(SECONDS_PER_SLOT div 3))
|
||||||
|
|
||||||
|
let (delayStr, delaySecs) =
|
||||||
|
if wallTime < deadline:
|
||||||
|
("-" & $(deadline - wallTime), -toFloatSeconds(deadline - wallTime))
|
||||||
|
else:
|
||||||
|
($(wallTime - deadline), toFloatSeconds(wallTime - deadline))
|
||||||
|
|
||||||
|
notice "Sync committee message sent",
|
||||||
|
message = shortLog(msg),
|
||||||
|
validator = shortLog(validator),
|
||||||
|
delay = delayStr
|
||||||
|
|
||||||
|
beacon_sync_committee_message_sent_delay.observe(delaySecs)
|
||||||
|
except CatchableError as exc:
|
||||||
|
# An error could happen here when the signature task fails - we must
|
||||||
|
# not leak the exception because this is an asyncSpawn task
|
||||||
|
notice "Error sending sync committee message", err = exc.msg
|
||||||
|
|
||||||
|
proc handleSyncCommitteeMessages(node: BeaconNode, head: BlockRef, slot: Slot) =
|
||||||
|
# TODO Use a view type to avoid the copy
|
||||||
|
var syncCommittee = @(node.dag.syncCommitteeParticipants(slot + 1))
|
||||||
|
|
||||||
|
for committeeIdx in allSyncCommittees():
|
||||||
|
for valKey in syncSubcommittee(syncCommittee, committeeIdx):
|
||||||
|
let validator = node.getAttachedValidator(valKey)
|
||||||
|
if validator == nil or not validator.index.isSome():
|
||||||
|
continue
|
||||||
|
asyncSpawn createAndSendSyncCommitteeMessage(node, slot, validator,
|
||||||
|
committeeIdx, head)
|
||||||
|
|
||||||
|
proc signAndSendContribution(node: BeaconNode,
|
||||||
|
validator: AttachedValidator,
|
||||||
|
contribution: SyncCommitteeContribution,
|
||||||
|
selectionProof: ValidatorSig) {.async.} =
|
||||||
|
try:
|
||||||
|
let msg = (ref SignedContributionAndProof)(
|
||||||
|
message: ContributionAndProof(
|
||||||
|
aggregator_index: uint64 validator.index.get,
|
||||||
|
contribution: contribution,
|
||||||
|
selection_proof: selectionProof))
|
||||||
|
|
||||||
|
await validator.sign(msg,
|
||||||
|
node.dag.forkAtEpoch(contribution.slot.epoch),
|
||||||
|
node.dag.genesisValidatorsRoot)
|
||||||
|
|
||||||
|
# Failures logged in sendSyncCommitteeContribution
|
||||||
|
discard await node.sendSyncCommitteeContribution(msg[], false)
|
||||||
|
except CatchableError as exc:
|
||||||
|
# An error could happen here when the signature task fails - we must
|
||||||
|
# not leak the exception because this is an asyncSpawn task
|
||||||
|
notice "Error sending sync committee contribution", err = exc.msg
|
||||||
|
|
||||||
|
proc handleSyncCommitteeContributions(node: BeaconNode,
|
||||||
|
head: BlockRef, slot: Slot) {.async.} =
|
||||||
|
# TODO Use a view type to avoid the copy
|
||||||
|
let
|
||||||
|
fork = node.dag.forkAtEpoch(slot.epoch)
|
||||||
|
genesisValidatorsRoot = node.dag.genesisValidatorsRoot
|
||||||
|
syncCommittee = @(node.dag.syncCommitteeParticipants(slot + 1))
|
||||||
|
|
||||||
|
type
|
||||||
|
AggregatorCandidate = object
|
||||||
|
validator: AttachedValidator
|
||||||
|
committeeIdx: SyncCommitteeIndex
|
||||||
|
|
||||||
|
var candidateAggregators: seq[AggregatorCandidate]
|
||||||
|
var selectionProofs: seq[Future[ValidatorSig]]
|
||||||
|
|
||||||
|
var time = timeIt:
|
||||||
|
for committeeIdx in allSyncCommittees():
|
||||||
|
# TODO Hoist outside of the loop with a view type
|
||||||
|
# to avoid the repeated offset calculations
|
||||||
|
for valKey in syncSubcommittee(syncCommittee, committeeIdx):
|
||||||
|
let validator = node.getAttachedValidator(valKey)
|
||||||
|
if validator == nil:
|
||||||
|
continue
|
||||||
|
|
||||||
|
candidateAggregators.add AggregatorCandidate(
|
||||||
|
validator: validator,
|
||||||
|
committeeIdx: committeeIdx)
|
||||||
|
|
||||||
|
selectionProofs.add validator.getSyncCommitteeSelectionProof(
|
||||||
|
fork, genesisValidatorsRoot, slot, committeeIdx.asUInt64)
|
||||||
|
|
||||||
|
await allFutures(selectionProofs)
|
||||||
|
|
||||||
|
debug "Prepared contributions selection proofs",
|
||||||
|
count = selectionProofs.len, time
|
||||||
|
|
||||||
|
var contributionsSent = 0
|
||||||
|
time = timeIt:
|
||||||
|
for i in 0 ..< selectionProofs.len:
|
||||||
|
if not selectionProofs[i].completed:
|
||||||
|
continue
|
||||||
|
|
||||||
|
let selectionProof = selectionProofs[i].read
|
||||||
|
if not is_sync_committee_aggregator(selectionProof):
|
||||||
|
continue
|
||||||
|
|
||||||
|
var contribution: SyncCommitteeContribution
|
||||||
|
let contributionWasProduced = node.syncCommitteeMsgPool[].produceContribution(
|
||||||
|
slot, head, candidateAggregators[i].committeeIdx, contribution)
|
||||||
|
|
||||||
|
if contributionWasProduced:
|
||||||
|
asyncSpawn signAndSendContribution(
|
||||||
|
node,
|
||||||
|
candidateAggregators[i].validator,
|
||||||
|
contribution,
|
||||||
|
selectionProof)
|
||||||
|
debug "Contribution sent", contribution = shortLog(contribution)
|
||||||
|
inc contributionsSent
|
||||||
|
else:
|
||||||
|
debug "Failure to produce contribution",
|
||||||
|
slot, head, subnet = candidateAggregators[i].committeeIdx
|
||||||
|
|
||||||
|
notice "Contributions sent", count = contributionsSent, time
|
||||||
|
|
||||||
proc handleProposal(node: BeaconNode, head: BlockRef, slot: Slot):
|
proc handleProposal(node: BeaconNode, head: BlockRef, slot: Slot):
|
||||||
Future[BlockRef] {.async.} =
|
Future[BlockRef] {.async.} =
|
||||||
## Perform the proposal for the given slot, iff we have a validator attached
|
## Perform the proposal for the given slot, iff we have a validator attached
|
||||||
@ -827,6 +968,7 @@ proc handleValidatorDuties*(node: BeaconNode, lastSlot, slot: Slot) {.async.} =
|
|||||||
head = node.dag.head
|
head = node.dag.head
|
||||||
|
|
||||||
handleAttestations(node, head, slot)
|
handleAttestations(node, head, slot)
|
||||||
|
handleSyncCommitteeMessages(node, head, slot)
|
||||||
|
|
||||||
updateValidatorMetrics(node) # the important stuff is done, update the vanity numbers
|
updateValidatorMetrics(node) # the important stuff is done, update the vanity numbers
|
||||||
|
|
||||||
@ -848,6 +990,10 @@ proc handleValidatorDuties*(node: BeaconNode, lastSlot, slot: Slot) {.async.} =
|
|||||||
let sendAggregatedAttestationsFut =
|
let sendAggregatedAttestationsFut =
|
||||||
sendAggregatedAttestations(node, head, slot)
|
sendAggregatedAttestations(node, head, slot)
|
||||||
|
|
||||||
|
let handleSyncCommitteeContributionsFut =
|
||||||
|
handleSyncCommitteeContributions(node, head, slot)
|
||||||
|
|
||||||
|
await handleSyncCommitteeContributionsFut
|
||||||
await sendAggregatedAttestationsFut
|
await sendAggregatedAttestationsFut
|
||||||
|
|
||||||
if node.eth1Monitor != nil and (slot mod SLOTS_PER_EPOCH) == 0:
|
if node.eth1Monitor != nil and (slot mod SLOTS_PER_EPOCH) == 0:
|
||||||
|
@ -338,6 +338,7 @@ MIN_GENESIS_TIME: 0
|
|||||||
GENESIS_DELAY: 10
|
GENESIS_DELAY: 10
|
||||||
DEPOSIT_CONTRACT_ADDRESS: ${DEPOSIT_CONTRACT_ADDRESS}
|
DEPOSIT_CONTRACT_ADDRESS: ${DEPOSIT_CONTRACT_ADDRESS}
|
||||||
ETH1_FOLLOW_DISTANCE: 1
|
ETH1_FOLLOW_DISTANCE: 1
|
||||||
|
ALTAIR_FORK_EPOCH: 2
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
dump_logs() {
|
dump_logs() {
|
||||||
|
@ -205,6 +205,7 @@ GENESIS_DELAY: 10
|
|||||||
GENESIS_FORK_VERSION: 0x00000000
|
GENESIS_FORK_VERSION: 0x00000000
|
||||||
DEPOSIT_CONTRACT_ADDRESS: ${DEPOSIT_CONTRACT_ADDRESS}
|
DEPOSIT_CONTRACT_ADDRESS: ${DEPOSIT_CONTRACT_ADDRESS}
|
||||||
ETH1_FOLLOW_DISTANCE: 1
|
ETH1_FOLLOW_DISTANCE: 1
|
||||||
|
ALTAIR_FORK_EPOCH: 2
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
if [[ "$USE_TMUX" == "yes" ]]; then
|
if [[ "$USE_TMUX" == "yes" ]]; then
|
||||||
|
Loading…
x
Reference in New Issue
Block a user