From 6eb4f1f39dd47d55bd11dcba5cde799d51781784 Mon Sep 17 00:00:00 2001 From: tersec Date: Wed, 1 Apr 2020 09:59:55 +0000 Subject: [PATCH] initial attestation aggregation (#769) * initial attestation aggregation * fix usage of committee index, vs index in committee; uniformly set trailing/following distance; document how the only-broadcast-if mechanism works better and what aggregation already happens, not otherwise sufficiently clear; use correct BlockSlot across epoch boundaries * address inconsistent notion of which slot in past to target for aggregate broadcast; follow 0.11.x aggregate broadcast p2p interface topic * Fix get_slot_signature(...) call after get_domain(...) change required genesis_validators_root * mark all spec references which aren't dealt with in other PRs as v0.11.1 * update two more spec refs to v0.11.1 --- beacon_chain/attestation_aggregation.nim | 57 +++++++++--------- beacon_chain/beacon_node.nim | 74 +++++++++++++++++++++--- beacon_chain/spec/network.nim | 1 + 3 files changed, 98 insertions(+), 34 deletions(-) diff --git a/beacon_chain/attestation_aggregation.nim b/beacon_chain/attestation_aggregation.nim index 0c0f1b7b5..584e47e9e 100644 --- a/beacon_chain/attestation_aggregation.nim +++ b/beacon_chain/attestation_aggregation.nim @@ -5,20 +5,12 @@ # * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). # at your option. This file may not be copied, modified, or distributed except according to those terms. -# Have an an aggregated aggregation ready for broadcast at -# SECONDS_PER_SLOT * 2 / 3, i.e. 2/3 through relevant slot -# intervals. -# # The other part is arguably part of attestation pool -- the validation's # something that should be happing on receipt, not aggregation per se. In # that part, check that messages conform -- so, check for each type # https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/p2p-interface.md#topics-and-messages # specifies. So by the time this calls attestation pool, all validation's # already done. -# -# Finally, some of the filtering's libp2p stuff. Consistency checks between -# topic/message types and GOSSIP_MAX_SIZE -- mostly doesn't belong here, so -# while TODO, isn't TODO for this module. import options, @@ -26,11 +18,7 @@ import state_transition_block], ./attestation_pool, ./beacon_node_types, ./ssz -# TODO gossipsub validation lives somewhere, maybe here -# TODO add tests, especially for validation -# https://github.com/status-im/nim-beacon-chain/issues/122#issuecomment-562479965 - -# https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md#aggregation-selection +# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#aggregation-selection func is_aggregator(state: BeaconState, slot: Slot, index: uint64, slot_signature: ValidatorSig): bool = # TODO index is a CommitteeIndex, aka uint64 @@ -43,34 +31,49 @@ func is_aggregator(state: BeaconState, slot: Slot, index: uint64, proc aggregate_attestations*( pool: AttestationPool, state: BeaconState, index: uint64, - privkey: ValidatorPrivKey): Option[AggregateAndProof] = + privkey: ValidatorPrivKey, trailing_distance: uint64): Option[AggregateAndProof] = # TODO alias CommitteeIndex to actual type then convert various uint64's here - let - slot = state.slot - 2 - slot_signature = get_slot_signature(state.fork, slot, privkey) + doAssert state.slot >= trailing_distance + + # https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/p2p-interface.md#configuration + doAssert trailing_distance <= ATTESTATION_PROPAGATION_SLOT_RANGE + + let + slot = state.slot - trailing_distance + slot_signature = get_slot_signature( + state.fork, state.genesis_validators_root, slot, privkey) - if slot < 0: - return none(AggregateAndProof) doAssert slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= state.slot doAssert state.slot >= slot - # https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md#aggregation-selection + # TODO performance issue for future, via get_active_validator_indices(...) + doAssert index < get_committee_count_at_slot(state, slot) + + # TODO for testing purposes, refactor this into the condition check + # and just calculation + # https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#aggregation-selection if not is_aggregator(state, slot, index, slot_signature): return none(AggregateAndProof) - let attestation_data = - makeAttestationData(state, slot, index, get_block_root_at_slot(state, slot)) + # https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#attestation-data + # describes how to construct an attestation, which applies for makeAttestationData(...) + # TODO this won't actually match anything + let attestation_data = AttestationData( + slot: slot, + index: index, + beacon_block_root: get_block_root_at_slot(state, slot)) - # https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md#construct-aggregate - for attestation in getAttestationsForBlock(pool, state, slot): + # https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#construct-aggregate + # TODO once EV goes in w/ refactoring of getAttestationsForBlock, pull out the getSlot version and use + # it. This is incorrect. + for attestation in getAttestationsForBlock(pool, state): + # getAttestationsForBlock(...) already aggregates if attestation.data == attestation_data: - # https://github.com/ethereum/eth2.0-specs/blob/v0.10.1/specs/phase0/validator.md#aggregateandproof + # https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#aggregateandproof return some(AggregateAndProof( aggregator_index: index, aggregate: attestation, selection_proof: slot_signature)) - # TODO in catch-up mode, we could get here, so probably shouldn't assert - doAssert false none(AggregateAndProof) diff --git a/beacon_chain/beacon_node.nim b/beacon_chain/beacon_node.nim index bcc3668f8..a3787214b 100644 --- a/beacon_chain/beacon_node.nim +++ b/beacon_chain/beacon_node.nim @@ -15,7 +15,8 @@ import conf, time, state_transition, beacon_chain_db, validator_pool, extras, attestation_pool, block_pool, eth2_network, eth2_discovery, beacon_node_types, mainchain_monitor, version, ssz, ssz/dynamic_navigator, - sync_protocol, request_manager, validator_keygen, interop, statusbar + sync_protocol, request_manager, validator_keygen, interop, statusbar, + attestation_aggregation const genesisFile = "genesis.ssz" @@ -602,6 +603,45 @@ proc verifyFinalization(node: BeaconNode, slot: Slot) = node.blockPool.finalizedHead.blck.slot.compute_epoch_at_slot() doAssert finalizedEpoch + 2 == epoch +proc broadcastAggregatedAttestations( + node: BeaconNode, state: auto, head: var auto, slot: Slot, + trailing_distance: uint64) = + # The index is via a + # locally attested validator. Unlike in handleAttestations(...) there's a + # single one at most per slot (because that's how aggregation attestation + # works), so the machinery that has to handle looping across, basically a + # set of locally attached validators is in principle not necessary, but a + # way to organize this. Then the private key for that validator should be + # the corresponding one -- whatver they are, they match. + + let + bs = BlockSlot(blck: head, slot: slot) + committees_per_slot = get_committee_count_at_slot(state, slot) + var cache = get_empty_per_epoch_cache() + for committee_index in 0'u64.. 2: + const TRAILING_DISTANCE = 1 + let aggregationSlot = slot - TRAILING_DISTANCE + var aggregationHead = getAncestorAt(head, aggregationSlot) + + let bs = BlockSlot(blck: aggregationHead, slot: aggregationSlot) + node.blockPool.withState(node.blockPool.tmpState, bs): + let twoThirdsSlot = + toBeaconTime(slot, seconds(2*int64(SECONDS_PER_SLOT)) div 3) + addTimer(saturate(node.beaconClock.fromNow(twoThirdsSlot))) do (p: pointer): + broadcastAggregatedAttestations( + node, state, aggregationHead, aggregationSlot, TRAILING_DISTANCE) + # TODO ... and beacon clock might jump here also. sigh. let nextSlotStart = saturate(node.beaconClock.fromNow(nextSlot)) diff --git a/beacon_chain/spec/network.nim b/beacon_chain/spec/network.nim index 719237d0c..41076a13d 100644 --- a/beacon_chain/spec/network.nim +++ b/beacon_chain/spec/network.nim @@ -13,6 +13,7 @@ const topicVoluntaryExits* = "/eth2/voluntary_exit/ssz" topicProposerSlashings* = "/eth2/proposer_slashing/ssz" topicAttesterSlashings* = "/eth2/attester_slashing/ssz" + topicAggregateAndProof* = "/eth2/beacon_aggregate_and_proof/ssz" # https://github.com/ethereum/eth2.0-specs/blob/v0.11.0/specs/phase0/p2p-interface.md#configuration ATTESTATION_SUBNET_COUNT* = 64