fix subnet calculation in RPC and insert broadcast attestations into node's pool (#2207)

* fix subnet calculation in RPC and insert broadcast attestations into node's pool

* unify codepaths to ensure only mostly-checked-to-be-valid attestations enter the pool, even from node's own broadcasts

* update attestation pool tests for new validateAttestation param
This commit is contained in:
tersec 2020-12-23 13:59:04 +01:00 committed by GitHub
parent afbaa36ef7
commit 97c4f7c5c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 30 additions and 23 deletions

View File

@ -162,7 +162,7 @@ func check_attestation_subnet(
proc validateAttestation*( proc validateAttestation*(
pool: var AttestationPool, pool: var AttestationPool,
attestation: Attestation, wallTime: BeaconTime, attestation: Attestation, wallTime: BeaconTime,
topicCommitteeIndex: uint64): topicCommitteeIndex: uint64, checksExpensive: bool):
Result[HashSet[ValidatorIndex], (ValidationResult, cstring)] = Result[HashSet[ValidatorIndex], (ValidationResult, cstring)] =
# [REJECT] The attestation's epoch matches its target -- i.e. # [REJECT] The attestation's epoch matches its target -- i.e.
# attestation.data.target.epoch == # attestation.data.target.epoch ==
@ -258,6 +258,9 @@ proc validateAttestation*(
return err((ValidationResult.Ignore, cstring( return err((ValidationResult.Ignore, cstring(
"Validator has already voted in epoch"))) "Validator has already voted in epoch")))
if not checksExpensive:
return ok(attesting_indices)
# The signature of attestation is valid. # The signature of attestation is valid.
block: block:
let v = is_valid_indexed_attestation( let v = is_valid_indexed_attestation(

View File

@ -288,7 +288,8 @@ proc blockValidator*(
proc attestationValidator*( proc attestationValidator*(
self: var Eth2Processor, self: var Eth2Processor,
attestation: Attestation, attestation: Attestation,
committeeIndex: uint64): ValidationResult = committeeIndex: uint64,
checksExpensive: bool = true): ValidationResult =
logScope: logScope:
attestation = shortLog(attestation) attestation = shortLog(attestation)
committeeIndex committeeIndex
@ -307,7 +308,7 @@ proc attestationValidator*(
let delay = wallTime - attestation.data.slot.toBeaconTime let delay = wallTime - attestation.data.slot.toBeaconTime
debug "Attestation received", delay debug "Attestation received", delay
let v = self.attestationPool[].validateAttestation( let v = self.attestationPool[].validateAttestation(
attestation, wallTime, committeeIndex) attestation, wallTime, committeeIndex, checksExpensive)
if v.isErr(): if v.isErr():
debug "Dropping attestation", err = v.error() debug "Dropping attestation", err = v.error()
return v.error[0] return v.error[0]

View File

@ -181,8 +181,6 @@ proc onSlotStart(vc: ValidatorClient, lastSlot, scheduledSlot: Slot) {.gcsafe, a
vc.attestationsForEpoch[epoch].contains slot: vc.attestationsForEpoch[epoch].contains slot:
var validatorToAttestationDataRoot: Table[ValidatorPubKey, Eth2Digest] var validatorToAttestationDataRoot: Table[ValidatorPubKey, Eth2Digest]
for a in vc.attestationsForEpoch[epoch][slot]: for a in vc.attestationsForEpoch[epoch][slot]:
notice "Attesting", slot = slot, public_key = a.public_key
let validator = vc.attachedValidators.validators[a.public_key] let validator = vc.attachedValidators.validators[a.public_key]
let ad = await vc.client.get_v1_validator_attestation_data(slot, a.committee_index) let ad = await vc.client.get_v1_validator_attestation_data(slot, a.committee_index)
@ -206,6 +204,8 @@ proc onSlotStart(vc: ValidatorClient, lastSlot, scheduledSlot: Slot) {.gcsafe, a
ad, a.committee_length.int, a.validator_committee_index, ad, a.committee_length.int, a.validator_committee_index,
vc.fork, vc.beaconGenesis.genesis_validators_root) vc.fork, vc.beaconGenesis.genesis_validators_root)
notice "Attesting",
slot, public_key = a.public_key, attestation = shortLog(attestation)
discard await vc.client.post_v1_beacon_pool_attestations(attestation) discard await vc.client.post_v1_beacon_pool_attestations(attestation)
validatorToAttestationDataRoot[a.public_key] = attestation.data.hash_tree_root validatorToAttestationDataRoot[a.public_key] = attestation.data.hash_tree_root

View File

@ -142,7 +142,11 @@ proc installValidatorApiHandlers*(rpcServer: RpcServer, node: BeaconNode) =
raise newException(CatchableError, raise newException(CatchableError,
"Invalid slot signature") "Invalid slot signature")
let subnet = committee_index.uint8 let
head = node.doChecksAndGetCurrentHead(epoch)
epochRef = node.chainDag.getEpochRef(head, epoch)
let subnet = compute_subnet_for_attestation(
get_committee_count_per_slot(epochRef), slot, committee_index).uint8
if subnet notin node.attestationSubnets.subscribedSubnets[0] and if subnet notin node.attestationSubnets.subscribedSubnets[0] and
subnet notin node.attestationSubnets.subscribedSubnets[1]: subnet notin node.attestationSubnets.subscribedSubnets[1]:
await node.network.subscribe(getAttestationTopic( await node.network.subscribe(getAttestationTopic(

View File

@ -89,15 +89,6 @@ func getAttestationTopic*(forkDigest: ForkDigest, subnetIndex: uint64):
except ValueError as e: except ValueError as e:
raiseAssert e.msg raiseAssert e.msg
func getAttestationTopic*(forkDigest: ForkDigest,
attestation: Attestation,
num_active_validators: uint64): string =
getAttestationTopic(
forkDigest,
compute_subnet_for_attestation(
get_committee_count_per_slot(num_active_validators),
attestation.data.slot, attestation.data.index.CommitteeIndex))
func get_committee_assignments( func get_committee_assignments(
state: BeaconState, epoch: Epoch, state: BeaconState, epoch: Epoch,
validator_indices: HashSet[ValidatorIndex]): validator_indices: HashSet[ValidatorIndex]):

View File

@ -19,7 +19,8 @@ import
# Local modules # Local modules
./spec/[ ./spec/[
datatypes, digest, crypto, helpers, network, signatures, state_transition], datatypes, digest, crypto, helpers, network, signatures, state_transition,
validator],
./conf, ./time, ./validator_pool, ./conf, ./time, ./validator_pool,
./attestation_pool, ./exit_pool, ./attestation_pool, ./exit_pool,
./block_pools/[spec_cache, chain_dag, clearance], ./block_pools/[spec_cache, chain_dag, clearance],
@ -152,9 +153,16 @@ proc isSynced*(node: BeaconNode, head: BlockRef): bool =
proc sendAttestation*( proc sendAttestation*(
node: BeaconNode, attestation: Attestation, num_active_validators: uint64) = node: BeaconNode, attestation: Attestation, num_active_validators: uint64) =
let subnet_index =
compute_subnet_for_attestation(
get_committee_count_per_slot(num_active_validators), attestation.data.slot,
attestation.data.index.CommitteeIndex)
node.network.broadcast( node.network.broadcast(
getAttestationTopic(node.forkDigest, attestation, num_active_validators), getAttestationTopic(node.forkDigest, subnet_index), attestation)
attestation)
# Ensure node's own broadcast attestations end up in its attestation pool
discard node.processor[].attestationValidator(
attestation, subnet_index, false)
beacon_attestations_sent.inc() beacon_attestations_sent.inc()

View File

@ -438,22 +438,22 @@ suiteReport "Attestation validation " & preset():
beaconTime = attestation.data.slot.toBeaconTime() beaconTime = attestation.data.slot.toBeaconTime()
check: check:
validateAttestation(pool[], attestation, beaconTime, subnet).isOk validateAttestation(pool[], attestation, beaconTime, subnet, true).isOk
# Same validator again # Same validator again
validateAttestation(pool[], attestation, beaconTime, subnet).error()[0] == validateAttestation(pool[], attestation, beaconTime, subnet, true).error()[0] ==
ValidationResult.Ignore ValidationResult.Ignore
pool[].nextAttestationEpoch.setLen(0) # reset for test pool[].nextAttestationEpoch.setLen(0) # reset for test
check: check:
# Wrong subnet # Wrong subnet
validateAttestation(pool[], attestation, beaconTime, subnet + 1).isErr validateAttestation(pool[], attestation, beaconTime, subnet + 1, true).isErr
pool[].nextAttestationEpoch.setLen(0) # reset for test pool[].nextAttestationEpoch.setLen(0) # reset for test
check: check:
# Too far in the future # Too far in the future
validateAttestation( validateAttestation(
pool[], attestation, beaconTime - 1.seconds, subnet + 1).isErr pool[], attestation, beaconTime - 1.seconds, subnet + 1, true).isErr
pool[].nextAttestationEpoch.setLen(0) # reset for test pool[].nextAttestationEpoch.setLen(0) # reset for test
check: check:
@ -461,4 +461,4 @@ suiteReport "Attestation validation " & preset():
validateAttestation( validateAttestation(
pool[], attestation, pool[], attestation,
beaconTime - (SECONDS_PER_SLOT * SLOTS_PER_EPOCH - 1).int.seconds, beaconTime - (SECONDS_PER_SLOT * SLOTS_PER_EPOCH - 1).int.seconds,
subnet + 1).isErr subnet + 1, true).isErr