From 15b99e4c119ed8f604f0609a72ecf3e59135a36b Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Wed, 5 Aug 2020 08:28:43 +0200 Subject: [PATCH] cache beacon proposer indices (#1440) also clear old epochrefs as they're growing unwieldy in particular, this speeds up gossip block validation by avoiding the rewind --- beacon_chain/attestation_aggregation.nim | 4 +- .../block_pools/block_pools_types.nim | 2 + beacon_chain/block_pools/chain_dag.nim | 74 +++++++++++-------- beacon_chain/block_pools/clearance.nim | 4 +- 4 files changed, 51 insertions(+), 33 deletions(-) diff --git a/beacon_chain/attestation_aggregation.nim b/beacon_chain/attestation_aggregation.nim index cb25e3840..a0acaa16b 100644 --- a/beacon_chain/attestation_aggregation.nim +++ b/beacon_chain/attestation_aggregation.nim @@ -197,8 +197,9 @@ proc isValidAttestation*( # committees_per_slot = get_committee_count_per_slot(state, # attestation.data.target.epoch), which may be pre-computed along with the # committee information for the signature check. + var cache = getEpochCache(blck, state) let - epochInfo = blck.getEpochInfo(state) + epochInfo = blck.getEpochInfo(state, cache) requiredSubnetIndex = compute_subnet_for_attestation( get_committee_count_per_slot(epochInfo), @@ -212,7 +213,6 @@ proc isValidAttestation*( return false # The signature of attestation is valid. - var cache = getEpochCache(blck, state) if not is_valid_indexed_attestation( state, get_indexed_attestation(state, attestation, cache), {}): debug "signature verification failed" diff --git a/beacon_chain/block_pools/block_pools_types.nim b/beacon_chain/block_pools/block_pools_types.nim index ac8044565..9180a4a33 100644 --- a/beacon_chain/block_pools/block_pools_types.nim +++ b/beacon_chain/block_pools/block_pools_types.nim @@ -140,6 +140,8 @@ type epoch*: Epoch current_justified_checkpoint*: Checkpoint finalized_checkpoint*: Checkpoint + beacon_proposers*: array[ + SLOTS_PER_EPOCH, Option[(ValidatorIndex, ValidatorPubKey)]] shuffled_active_validator_indices*: seq[ValidatorIndex] BlockRef* = ref object diff --git a/beacon_chain/block_pools/chain_dag.nim b/beacon_chain/block_pools/chain_dag.nim index 37213fa58..ee424d9e4 100644 --- a/beacon_chain/block_pools/chain_dag.nim +++ b/beacon_chain/block_pools/chain_dag.nim @@ -62,14 +62,22 @@ func parent*(bs: BlockSlot): BlockSlot = slot: bs.slot - 1 ) -func init*(T: type EpochRef, state: BeaconState): T = - let epoch = state.get_current_epoch() - EpochRef( - epoch: epoch, - current_justified_checkpoint: state.current_justified_checkpoint, - finalized_checkpoint: state.finalized_checkpoint, - shuffled_active_validator_indices: - get_shuffled_active_validator_indices(state, epoch)) +proc init*(T: type EpochRef, state: BeaconState, cache: var StateCache): T = + let + epoch = state.get_current_epoch() + epochRef = EpochRef( + epoch: epoch, + current_justified_checkpoint: state.current_justified_checkpoint, + finalized_checkpoint: state.finalized_checkpoint, + shuffled_active_validator_indices: + cache.get_shuffled_active_validator_indices(state, epoch)) + for i in 0'u64.. 0: # When doing state transitioning, both the current and previous epochs are # useful from a cache perspective since attestations may come from either - @@ -193,6 +207,10 @@ func getEpochCache*(blck: BlockRef, state: BeaconState): StateCache = result.shuffled_active_validator_indices[state.get_current_epoch()] = epochInfo.shuffled_active_validator_indices + for i, idx in epochInfo.beacon_proposers: + result.beacon_proposer_indices[ + epochInfo.epoch.compute_start_slot_at_epoch + i] = + if idx.isSome: some(idx.get()[0]) else: none(ValidatorIndex) func init(T: type BlockRef, root: Eth2Digest, slot: Slot): BlockRef = BlockRef( @@ -332,7 +350,8 @@ proc getEpochRef*(dag: ChainDAGRef, blck: BlockRef, epoch: Epoch): EpochRef = bs = bs.parent dag.withState(dag.tmpState, bs): - getEpochInfo(blck, state) + var cache = StateCache() + getEpochInfo(blck, state, cache) proc getState( dag: ChainDAGRef, db: BeaconChainDB, stateRoot: Eth2Digest, blck: BlockRef, @@ -814,6 +833,15 @@ proc updateHead*(dag: ChainDAGRef, newHead: BlockRef) = cur = cur.parent dag.heads.del(n) + block: # Clean up old EpochRef instances + # After finalization, we can clear up the epoch cache and save memory - + # it will be recomputed if needed + # TODO don't store recomputed pre-finalization epoch refs + var tmp = finalizedHead.blck + while tmp != dag.finalizedHead.blck: + # leave the epoch cache in the last block of the epoch.. + tmp = tmp.parent + tmp.epochsInfo = @[] dag.finalizedHead = finalizedHead @@ -865,20 +893,8 @@ proc preInit*( proc getProposer*( dag: ChainDAGRef, head: BlockRef, slot: Slot): Option[(ValidatorIndex, ValidatorPubKey)] = - dag.withState(dag.tmpState, head.atSlot(slot)): - var cache = StateCache() + let + epochRef = dag.getEpochRef(head, slot.compute_epoch_at_slot()) + slotInEpoch = slot - slot.compute_epoch_at_slot().compute_start_slot_at_epoch() - let proposerIdx = get_beacon_proposer_index(state, cache) - if proposerIdx.isNone: - warn "Missing proposer index", - slot=slot, - epoch=slot.compute_epoch_at_slot, - num_validators=state.validators.len, - active_validators= - get_active_validator_indices(state, slot.compute_epoch_at_slot), - balances=state.balances - return - - return some(( - proposerIdx.get(), - state.validators[proposerIdx.get()].pubkey.initPubKey)) + epochRef.beacon_proposers[slotInEpoch] diff --git a/beacon_chain/block_pools/clearance.nim b/beacon_chain/block_pools/clearance.nim index b7f4bde69..86530b55d 100644 --- a/beacon_chain/block_pools/clearance.nim +++ b/beacon_chain/block_pools/clearance.nim @@ -43,7 +43,7 @@ proc addRawBlock*( proc addResolvedBlock( dag: var ChainDAGRef, quarantine: var QuarantineRef, state: HashedBeaconState, signedBlock: SignedBeaconBlock, - parent: BlockRef, cache: StateCache, + parent: BlockRef, cache: var StateCache, onBlockAdded: OnBlockAdded ): BlockRef = # TODO move quarantine processing out of here @@ -61,7 +61,7 @@ proc addResolvedBlock( blockRef.epochsInfo = filterIt(parent.epochsInfo, it.epoch == blockEpoch) else: # Ensure we collect the epoch info if it's missing - discard getEpochInfo(blockRef, state.data) + discard getEpochInfo(blockRef, state.data, cache) link(parent, blockRef)