mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-01-10 22:36:01 +00:00
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
This commit is contained in:
parent
ae072d39e6
commit
15b99e4c11
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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..<SLOTS_PER_EPOCH:
|
||||
let idx = get_beacon_proposer_index(
|
||||
state, cache, epoch.compute_start_slot_at_epoch() + i)
|
||||
if idx.isSome():
|
||||
epochRef.beacon_proposers[i] =
|
||||
some((idx.get(), state.validators[idx.get].pubkey.initPubKey))
|
||||
epochRef
|
||||
|
||||
func link*(parent, child: BlockRef) =
|
||||
doAssert (not (parent.root == Eth2Digest() or child.root == Eth2Digest())),
|
||||
@ -153,14 +161,14 @@ func atEpochEnd*(blck: BlockRef, epoch: Epoch): BlockSlot =
|
||||
## Return the BlockSlot corresponding to the last slot in the given epoch
|
||||
atSlot(blck, (epoch + 1).compute_start_slot_at_epoch - 1)
|
||||
|
||||
func getEpochInfo*(blck: BlockRef, state: BeaconState): EpochRef =
|
||||
proc getEpochInfo*(blck: BlockRef, state: BeaconState, cache: var StateCache): EpochRef =
|
||||
# This is the only intended mechanism by which to get an EpochRef
|
||||
let
|
||||
state_epoch = state.get_current_epoch()
|
||||
matching_epochinfo = blck.epochsInfo.filterIt(it.epoch == state_epoch)
|
||||
|
||||
if matching_epochinfo.len == 0:
|
||||
let epochInfo = EpochRef.init(state)
|
||||
let epochInfo = EpochRef.init(state, cache)
|
||||
|
||||
# Don't use BlockRef caching as far as the epoch where the active
|
||||
# validator indices can diverge.
|
||||
@ -175,8 +183,14 @@ func getEpochInfo*(blck: BlockRef, state: BeaconState): EpochRef =
|
||||
else:
|
||||
raiseAssert "multiple EpochRefs per epoch per BlockRef invalid"
|
||||
|
||||
func getEpochCache*(blck: BlockRef, state: BeaconState): StateCache =
|
||||
let epochInfo = getEpochInfo(blck, state)
|
||||
proc getEpochInfo*(blck: BlockRef, state: BeaconState): EpochRef =
|
||||
# This is the only intended mechanism by which to get an EpochRef
|
||||
var cache = StateCache()
|
||||
getEpochInfo(blck, state, cache)
|
||||
|
||||
proc getEpochCache*(blck: BlockRef, state: BeaconState): StateCache =
|
||||
var tmp = StateCache() # TODO Resolve circular init issue
|
||||
let epochInfo = getEpochInfo(blck, state, tmp)
|
||||
if epochInfo.epoch > 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]
|
||||
|
@ -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)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user