mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-02-23 11:48:33 +00:00
Add blob sidecar gossip validation (#4785)
* Add blob gossip validation * lint * Add test for getBlobSidecarTopic * Fix closure variable capture issue * Update beacon_chain/nimbus_beacon_node.nim Co-authored-by: tersec <tersec@users.noreply.github.com> --------- Co-authored-by: tersec <tersec@users.noreply.github.com>
This commit is contained in:
parent
5167a373ab
commit
04302081b4
@ -41,6 +41,10 @@ declareCounter beacon_blocks_received,
|
|||||||
"Number of valid blocks processed by this node"
|
"Number of valid blocks processed by this node"
|
||||||
declareCounter beacon_blocks_dropped,
|
declareCounter beacon_blocks_dropped,
|
||||||
"Number of invalid blocks dropped by this node", labels = ["reason"]
|
"Number of invalid blocks dropped by this node", labels = ["reason"]
|
||||||
|
declareCounter blob_sidecars_received,
|
||||||
|
"Number of valid blobs processed by this node"
|
||||||
|
declareCounter blob_sidecars_dropped,
|
||||||
|
"Number of invalid blobs dropped by this node", labels = ["reason"]
|
||||||
declareCounter beacon_attester_slashings_received,
|
declareCounter beacon_attester_slashings_received,
|
||||||
"Number of valid attester slashings processed by this node"
|
"Number of valid attester slashings processed by this node"
|
||||||
declareCounter beacon_attester_slashings_dropped,
|
declareCounter beacon_attester_slashings_dropped,
|
||||||
@ -85,6 +89,9 @@ declareHistogram beacon_aggregate_delay,
|
|||||||
declareHistogram beacon_block_delay,
|
declareHistogram beacon_block_delay,
|
||||||
"Time(s) between slot start and beacon block reception", buckets = delayBuckets
|
"Time(s) between slot start and beacon block reception", buckets = delayBuckets
|
||||||
|
|
||||||
|
declareHistogram blob_sidecar_delay,
|
||||||
|
"Time(s) between slot start and blob sidecar reception", buckets = delayBuckets
|
||||||
|
|
||||||
type
|
type
|
||||||
DoppelgangerProtection = object
|
DoppelgangerProtection = object
|
||||||
broadcastStartEpoch*: Epoch ##\
|
broadcastStartEpoch*: Epoch ##\
|
||||||
@ -246,6 +253,41 @@ proc processSignedBeaconBlock*(
|
|||||||
|
|
||||||
v
|
v
|
||||||
|
|
||||||
|
proc processSignedBlobSidecar*(
|
||||||
|
self: var Eth2Processor, src: MsgSource,
|
||||||
|
signedBlobSidecar: deneb.SignedBlobSidecar, idx: BlobIndex): ValidationRes =
|
||||||
|
let
|
||||||
|
wallTime = self.getCurrentBeaconTime()
|
||||||
|
(afterGenesis, wallSlot) = wallTime.toSlot()
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
blob = shortLog(signedBlobSidecar.message)
|
||||||
|
signature = shortLog(signedBlobSidecar.signature)
|
||||||
|
wallSlot
|
||||||
|
|
||||||
|
# Potential under/overflows are fine; would just create odd metrics and logs
|
||||||
|
let delay = wallTime - signedBlobSidecar.message.slot.start_beacon_time
|
||||||
|
|
||||||
|
debug "Blob received", delay
|
||||||
|
|
||||||
|
let v =
|
||||||
|
self.dag.validateBlobSidecar(self.quarantine, signedBlobSidecar, wallTime, idx)
|
||||||
|
|
||||||
|
if v.isOk():
|
||||||
|
trace "Blob validated"
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
# hand blob off to blob quarantine
|
||||||
|
|
||||||
|
blob_sidecars_received.inc()
|
||||||
|
blob_sidecar_delay.observe(delay.toFloatSeconds())
|
||||||
|
else:
|
||||||
|
debug "Dropping blob", error = v.error()
|
||||||
|
|
||||||
|
blob_sidecars_dropped.inc(1, [$v.error[0]])
|
||||||
|
|
||||||
|
v
|
||||||
|
|
||||||
proc setupDoppelgangerDetection*(self: var Eth2Processor, slot: Slot) =
|
proc setupDoppelgangerDetection*(self: var Eth2Processor, slot: Slot) =
|
||||||
# When another client's already running, this is very likely to detect
|
# When another client's already running, this is very likely to detect
|
||||||
# potential duplicate validators, which can trigger slashing.
|
# potential duplicate validators, which can trigger slashing.
|
||||||
|
@ -220,6 +220,79 @@ template validateBeaconBlockBellatrix(
|
|||||||
# `ACCEPTED` or `SYNCING` from the EL to get this far.
|
# `ACCEPTED` or `SYNCING` from the EL to get this far.
|
||||||
|
|
||||||
|
|
||||||
|
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.5/specs/deneb/p2p-interface.md#blob_sidecar_index
|
||||||
|
proc validateBlobSidecar*(
|
||||||
|
dag: ChainDAGRef, quarantine: ref Quarantine,
|
||||||
|
sbs: SignedBlobSidecar,
|
||||||
|
wallTime: BeaconTime, idx: BlobIndex): Result[void, ValidationError] =
|
||||||
|
|
||||||
|
# [REJECT] The sidecar is for the correct topic --
|
||||||
|
# i.e. sidecar.index matches the topic {index}.
|
||||||
|
if sbs.message.index != idx:
|
||||||
|
return errReject("SignedBlobSidecar: mismatched gossip topic index")
|
||||||
|
|
||||||
|
if dag.getBlockRef(sbs.message.block_root).isSome():
|
||||||
|
return errIgnore("SignedBlobSidecar: already have block")
|
||||||
|
|
||||||
|
# [IGNORE] The sidecar is not from a future slot (with a
|
||||||
|
# MAXIMUM_GOSSIP_CLOCK_DISPARITY allowance) -- i.e. validate that
|
||||||
|
# sidecar.slot <= current_slot (a client MAY queue future sidecars
|
||||||
|
# for processing at the appropriate slot).
|
||||||
|
if not (sbs.message.slot <=
|
||||||
|
(wallTime + MAXIMUM_GOSSIP_CLOCK_DISPARITY).slotOrZero):
|
||||||
|
return errIgnore("SignedBlobSidecar: slot too high")
|
||||||
|
|
||||||
|
# [IGNORE] The block is from a slot greater than the latest
|
||||||
|
# finalized slot -- i.e. validate that
|
||||||
|
# signed_beacon_block.message.slot >
|
||||||
|
# compute_start_slot_at_epoch(state.finalized_checkpoint.epoch)
|
||||||
|
if not (sbs.message.slot > dag.finalizedHead.slot):
|
||||||
|
return errIgnore("SignedBlobSidecar: slot already finalized")
|
||||||
|
|
||||||
|
# [REJECT] The sidecar's block's parent (defined by sidecar.block_parent_root)
|
||||||
|
# passes validation.
|
||||||
|
let parent = dag.getBlockRef(sbs.message.block_parent_root).valueOr:
|
||||||
|
return errReject("SignedBlobSidecar: parent not validated")
|
||||||
|
|
||||||
|
# [REJECT] The sidecar is from a higher slot than the sidecar's
|
||||||
|
# block's parent (defined by sidecar.block_parent_root).
|
||||||
|
if sbs.message.slot <= parent.bid.slot:
|
||||||
|
return errReject("SignedBlobSidecar: slot lower than parents'")
|
||||||
|
|
||||||
|
# [REJECT] The sidecar is proposed by the expected proposer_index
|
||||||
|
# for the block's slot in the context of the current shuffling
|
||||||
|
# (defined by block_parent_root/slot). If the proposer_index
|
||||||
|
# cannot immediately be verified against the expected shuffling,
|
||||||
|
# the sidecar MAY be queued for later processing while proposers
|
||||||
|
# for the block's branch are calculated -- in such a case do not
|
||||||
|
# REJECT, instead IGNORE this message.
|
||||||
|
let
|
||||||
|
proposer = getProposer(
|
||||||
|
dag, parent, sbs.message.slot).valueOr:
|
||||||
|
warn "cannot compute proposer for blob"
|
||||||
|
return errIgnore("SignedBlobSidecar: Cannot compute proposer")
|
||||||
|
|
||||||
|
if uint64(proposer) != sbs.message.proposer_index:
|
||||||
|
return errReject("SignedBlobSidecar: Unexpected proposer")
|
||||||
|
|
||||||
|
# [REJECT] The proposer signature, signed_blob_sidecar.signature,
|
||||||
|
# is valid as verified by verify_sidecar_signature.
|
||||||
|
if not verify_blob_signature(
|
||||||
|
dag.forkAtEpoch(sbs.message.slot.epoch),
|
||||||
|
getStateField(dag.headState, genesis_validators_root),
|
||||||
|
sbs.message.slot,
|
||||||
|
sbs.message,
|
||||||
|
dag.validatorKey(proposer).get(),
|
||||||
|
sbs.signature):
|
||||||
|
return errReject("SignedBlobSidecar: invalid blob signature")
|
||||||
|
|
||||||
|
# [IGNORE] The sidecar is the only sidecar with valid signature received for the tuple (sidecar.block_root, sidecar.index).
|
||||||
|
# TODO
|
||||||
|
# check that there isn't a conflicting sidecar in blob_quarantine
|
||||||
|
|
||||||
|
ok()
|
||||||
|
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.9/specs/phase0/p2p-interface.md#beacon_block
|
# https://github.com/ethereum/consensus-specs/blob/v1.1.9/specs/phase0/p2p-interface.md#beacon_block
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-alpha.0/specs/bellatrix/p2p-interface.md#beacon_block
|
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-alpha.0/specs/bellatrix/p2p-interface.md#beacon_block
|
||||||
proc validateBeaconBlock*(
|
proc validateBeaconBlock*(
|
||||||
|
@ -1478,6 +1478,15 @@ proc installMessageValidators(node: BeaconNode) =
|
|||||||
toValidationResult(node.processor[].processSignedBeaconBlock(
|
toValidationResult(node.processor[].processSignedBeaconBlock(
|
||||||
MsgSource.gossip, signedBlock)))
|
MsgSource.gossip, signedBlock)))
|
||||||
|
|
||||||
|
for i in 0 ..< MAX_BLOBS_PER_BLOCK:
|
||||||
|
closureScope:
|
||||||
|
let idx = i
|
||||||
|
node.network.addValidator(
|
||||||
|
getBlobSidecarTopic(forkDigests.deneb, idx),
|
||||||
|
proc (signedBlobSidecar: deneb.SignedBlobSidecar): ValidationResult =
|
||||||
|
toValidationResult(node.processor[].processSignedBlobSidecar(
|
||||||
|
MsgSource.gossip, signedBlobSidecar, idx)))
|
||||||
|
|
||||||
template installSyncCommitteeeValidators(digest: auto) =
|
template installSyncCommitteeeValidators(digest: auto) =
|
||||||
for subcommitteeIdx in SyncSubcommitteeIndex:
|
for subcommitteeIdx in SyncSubcommitteeIndex:
|
||||||
closureScope:
|
closureScope:
|
||||||
|
@ -39,6 +39,9 @@ const
|
|||||||
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.5/specs/capella/beacon-chain.md#domain-types
|
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.5/specs/capella/beacon-chain.md#domain-types
|
||||||
DOMAIN_BLS_TO_EXECUTION_CHANGE* = DomainType([byte 0x0a, 0x00, 0x00, 0x00])
|
DOMAIN_BLS_TO_EXECUTION_CHANGE* = DomainType([byte 0x0a, 0x00, 0x00, 0x00])
|
||||||
|
|
||||||
|
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.5/specs/deneb/beacon-chain.md
|
||||||
|
DOMAIN_BLOB_SIDECAR* = DomainType([byte 0x0b, 0x00, 0x00, 0x00])
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.5/specs/bellatrix/beacon-chain.md#transition-settings
|
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.5/specs/bellatrix/beacon-chain.md#transition-settings
|
||||||
TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH* = FAR_FUTURE_EPOCH
|
TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH* = FAR_FUTURE_EPOCH
|
||||||
|
|
||||||
|
@ -525,6 +525,22 @@ func shortLog*(v: SomeBeaconBlock): auto =
|
|||||||
fee_recipient: to0xHex(v.body.execution_payload.fee_recipient.data),
|
fee_recipient: to0xHex(v.body.execution_payload.fee_recipient.data),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func shortLog*(v: BlobSidecar): auto =
|
||||||
|
(
|
||||||
|
block_root: shortLog(v.block_root),
|
||||||
|
index: v.index,
|
||||||
|
slot: shortLog(v.slot),
|
||||||
|
block_parent_root: shortLog(v.block_parent_root),
|
||||||
|
proposer_index: v.proposer_index,
|
||||||
|
bloblen: v.blob.len(),
|
||||||
|
)
|
||||||
|
|
||||||
|
func shortLog*(v: SignedBlobSidecar): auto =
|
||||||
|
(
|
||||||
|
blob: shortLog(v.message),
|
||||||
|
signature: shortLog(v.signature)
|
||||||
|
)
|
||||||
|
|
||||||
func shortLog*(v: SomeSignedBeaconBlock): auto =
|
func shortLog*(v: SomeSignedBeaconBlock): auto =
|
||||||
(
|
(
|
||||||
blck: shortLog(v.message),
|
blck: shortLog(v.message),
|
||||||
|
@ -97,6 +97,11 @@ func getSyncCommitteeContributionAndProofTopic*(forkDigest: ForkDigest): string
|
|||||||
## For subscribing and unsubscribing to/from a subnet.
|
## For subscribing and unsubscribing to/from a subnet.
|
||||||
eth2Prefix(forkDigest) & "sync_committee_contribution_and_proof/ssz_snappy"
|
eth2Prefix(forkDigest) & "sync_committee_contribution_and_proof/ssz_snappy"
|
||||||
|
|
||||||
|
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.5/specs/deneb/p2p-interface.md#blob_sidecar_index
|
||||||
|
func getBlobSidecarTopic*(forkDigest: ForkDigest,
|
||||||
|
index: BlobIndex): string =
|
||||||
|
eth2Prefix(forkDigest) & "blob_sidecar_" & $index & "/ssz_snappy"
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.5/specs/altair/light-client/p2p-interface.md#light_client_finality_update
|
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.5/specs/altair/light-client/p2p-interface.md#light_client_finality_update
|
||||||
func getLightClientFinalityUpdateTopic*(forkDigest: ForkDigest): string =
|
func getLightClientFinalityUpdateTopic*(forkDigest: ForkDigest): string =
|
||||||
## For broadcasting or obtaining the latest `LightClientFinalityUpdate`.
|
## For broadcasting or obtaining the latest `LightClientFinalityUpdate`.
|
||||||
|
@ -98,6 +98,15 @@ func compute_block_signing_root*(
|
|||||||
fork, DOMAIN_BEACON_PROPOSER, epoch, genesis_validators_root)
|
fork, DOMAIN_BEACON_PROPOSER, epoch, genesis_validators_root)
|
||||||
compute_signing_root(blck, domain)
|
compute_signing_root(blck, domain)
|
||||||
|
|
||||||
|
func compute_blob_signing_root*(
|
||||||
|
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
|
||||||
|
blob: BlobSidecar): Eth2Digest =
|
||||||
|
let
|
||||||
|
epoch = epoch(slot)
|
||||||
|
domain = get_domain(fork, DOMAIN_BLOB_SIDECAR, epoch,
|
||||||
|
genesis_validators_root)
|
||||||
|
compute_signing_root(blob, domain)
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.5/specs/phase0/validator.md#signature
|
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.5/specs/phase0/validator.md#signature
|
||||||
func get_block_signature*(
|
func get_block_signature*(
|
||||||
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
|
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
|
||||||
@ -118,6 +127,17 @@ proc verify_block_signature*(
|
|||||||
|
|
||||||
blsVerify(pubkey, signing_root.data, signature)
|
blsVerify(pubkey, signing_root.data, signature)
|
||||||
|
|
||||||
|
proc verify_blob_signature*(
|
||||||
|
fork: Fork, genesis_validators_root: Eth2Digest, slot: Slot,
|
||||||
|
blobSidecar: BlobSidecar,
|
||||||
|
pubkey: ValidatorPubKey | CookedPubKey, signature: SomeSig): bool =
|
||||||
|
withTrust(signature):
|
||||||
|
let
|
||||||
|
signing_root = compute_blob_signing_root(
|
||||||
|
fork, genesis_validators_root, slot, blobSidecar)
|
||||||
|
|
||||||
|
blsVerify(pubkey, signing_root.data, signature)
|
||||||
|
|
||||||
func compute_aggregate_and_proof_signing_root*(
|
func compute_aggregate_and_proof_signing_root*(
|
||||||
fork: Fork, genesis_validators_root: Eth2Digest,
|
fork: Fork, genesis_validators_root: Eth2Digest,
|
||||||
aggregate_and_proof: AggregateAndProof): Eth2Digest =
|
aggregate_and_proof: AggregateAndProof): Eth2Digest =
|
||||||
|
@ -76,6 +76,8 @@ suite "Honest validator":
|
|||||||
"/eth2/00000000/sync_committee_1/ssz_snappy"
|
"/eth2/00000000/sync_committee_1/ssz_snappy"
|
||||||
getSyncCommitteeTopic(forkDigest, SyncSubcommitteeIndex(3)) ==
|
getSyncCommitteeTopic(forkDigest, SyncSubcommitteeIndex(3)) ==
|
||||||
"/eth2/00000000/sync_committee_3/ssz_snappy"
|
"/eth2/00000000/sync_committee_3/ssz_snappy"
|
||||||
|
getBlobSidecarTopic(forkDigest, BlobIndex(1)) ==
|
||||||
|
"/eth2/00000000/blob_sidecar_1/ssz_snappy"
|
||||||
|
|
||||||
test "is_aggregator":
|
test "is_aggregator":
|
||||||
check:
|
check:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user