public async Task <IList <ValidatorDuty> > ValidatorDutiesAsync(IEnumerable <BlsPublicKey> validatorPublicKeys, Epoch epoch) { // TODO: Rather than check one by one (each of which loops through potentially all slots for the epoch), optimise by either checking multiple, or better possibly caching or pre-calculating List <ValidatorDuty> validatorDuties = new List <ValidatorDuty>(); foreach (BlsPublicKey validatorPublicKey in validatorPublicKeys) { ValidatorDuty validatorDuty = await _validatorAssignments.GetValidatorDutyAsync(validatorPublicKey, epoch); validatorDuties.Add(validatorDuty); } return(validatorDuties); }
public async Task <IList <ValidatorDuty> > GetValidatorDutiesAsync( IList <BlsPublicKey> validatorPublicKeys, Epoch?optionalEpoch) { Root head = await _forkChoice.GetHeadAsync(_store).ConfigureAwait(false); BeaconState headState = await _store.GetBlockStateAsync(head).ConfigureAwait(false); Epoch currentEpoch = _beaconStateAccessor.GetCurrentEpoch(headState); Epoch epoch = optionalEpoch ?? currentEpoch; Epoch nextEpoch = currentEpoch + Epoch.One; if (epoch > nextEpoch) { throw new ArgumentOutOfRangeException(nameof(epoch), epoch, $"Duties cannot look ahead more than the next epoch {nextEpoch}."); } Slot startSlot = _beaconChainUtility.ComputeStartSlotOfEpoch(epoch); Root epochStartRoot = await _store.GetAncestorAsync(head, startSlot); TimeParameters timeParameters = _timeParameterOptions.CurrentValue; (Root epochStartRoot, Epoch epoch)cacheKey = (epochStartRoot, epoch); ConcurrentDictionary <BlsPublicKey, ValidatorDuty> dutiesForEpoch = await _validatorAssignmentsCache.Cache.GetOrCreateAsync(cacheKey, entry => { entry.SlidingExpiration = TimeSpan.FromSeconds(2 * timeParameters.SecondsPerSlot); return(Task.FromResult(new ConcurrentDictionary <BlsPublicKey, ValidatorDuty>())); }).ConfigureAwait(false); IEnumerable <BlsPublicKey> missingValidators = validatorPublicKeys.Except(dutiesForEpoch.Keys); if (missingValidators.Any()) { if (_logger.IsDebug()) { LogDebug.GettingMissingValidatorDutiesForCache(_logger, missingValidators.Count(), epoch, epochStartRoot, null); } BeaconState storedState = await _store.GetBlockStateAsync(epochStartRoot); // Clone, so that it can be safely mutated (transitioned forward) BeaconState state = BeaconState.Clone(storedState); // Transition to start slot, of target epoch (may have been a skip slot, i.e. stored state root may have been older) _beaconStateTransition.ProcessSlots(state, startSlot); // Check validators are valid (if not duties are empty). IList <DutyDetails> dutyDetailsList = new List <DutyDetails>(); foreach (BlsPublicKey validatorPublicKey in missingValidators) { ValidatorIndex?validatorIndex = FindValidatorIndexByPublicKey(state, validatorPublicKey); if (validatorIndex.HasValue) { bool validatorActive = CheckIfValidatorActive(state, validatorIndex.Value); if (validatorActive) { dutyDetailsList.Add(new DutyDetails(validatorPublicKey, validatorIndex.Value)); } else { if (_logger.IsWarn()) { Log.ValidatorNotActiveAtEpoch(_logger, epoch, validatorIndex.Value, validatorPublicKey, null); } dutiesForEpoch[validatorPublicKey] = new ValidatorDuty(validatorPublicKey, Slot.None, CommitteeIndex.None, Slot.None); } } else { if (_logger.IsWarn()) { Log.ValidatorNotFoundAtEpoch(_logger, epoch, validatorPublicKey, null); } dutiesForEpoch[validatorPublicKey] = new ValidatorDuty(validatorPublicKey, Slot.None, CommitteeIndex.None, Slot.None); } } if (dutyDetailsList.Any()) { // Check starting state UpdateDutyDetailsForState(dutyDetailsList, state); // Check other slots in epoch, if needed Slot endSlotExclusive = startSlot + new Slot(timeParameters.SlotsPerEpoch); Slot slotToCheck = startSlot + Slot.One; while (slotToCheck < endSlotExclusive) { _beaconStateTransition.ProcessSlots(state, slotToCheck); UpdateDutyDetailsForState(dutyDetailsList, state); slotToCheck += Slot.One; } // Active validators should always have attestation slots; warn if they don't foreach (var dutyDetails in dutyDetailsList) { if (!dutyDetails.AttestationSlot.HasValue) { if (_logger.IsWarn()) { Log.ValidatorDoesNotHaveAttestationSlot(_logger, epoch, dutyDetails.ValidatorPublicKey, null); } } } // Add to cached dictionary foreach (var dutyDetails in dutyDetailsList) { ValidatorDuty validatorDuty = new ValidatorDuty(dutyDetails.ValidatorPublicKey, dutyDetails.AttestationSlot, dutyDetails.AttestationCommitteeIndex, dutyDetails.BlockProposalSlot); dutiesForEpoch[dutyDetails.ValidatorPublicKey] = validatorDuty; } } } return(dutiesForEpoch .Where(x => validatorPublicKeys.Contains(x.Key)) .Select(x => x.Value) .ToList()); }
public async Task <ValidatorDuty> GetValidatorDutyAsync(BlsPublicKey validatorPublicKey, Epoch epoch) { Root head = await _forkChoice.GetHeadAsync(_store).ConfigureAwait(false); BeaconState headState = await _store.GetBlockStateAsync(head).ConfigureAwait(false); Epoch currentEpoch = _beaconStateAccessor.GetCurrentEpoch(headState); Epoch nextEpoch = currentEpoch + Epoch.One; if (epoch == Epoch.None) { epoch = currentEpoch; } else if (epoch > nextEpoch) { throw new ArgumentOutOfRangeException(nameof(epoch), epoch, $"Duties cannot look ahead more than the next epoch {nextEpoch}."); } TimeParameters timeParameters = _timeParameterOptions.CurrentValue; Slot startSlot = _beaconChainUtility.ComputeStartSlotOfEpoch(epoch); Slot endSlot = startSlot + new Slot(timeParameters.SlotsPerEpoch); Duty duty = new Duty() { AttestationSlot = Slot.None, AttestationCommitteeIndex = CommitteeIndex.None, BlockProposalSlot = Slot.None }; if (epoch == nextEpoch) { // Clone for next or current, so that it can be safely mutated (transitioned forward) BeaconState state = BeaconState.Clone(headState); _beaconStateTransition.ProcessSlots(state, startSlot); // Check base state ValidatorIndex validatorIndex = CheckValidatorIndex(state, validatorPublicKey); duty = CheckStateDuty(state, validatorIndex, duty); // Check future states duty = CheckFutureSlots(state, endSlot, validatorIndex, duty); } else if (epoch == currentEpoch) { // Take block slot and roots before cloning (for historical checks) IReadOnlyList <Root> historicalBlockRoots = headState.BlockRoots; Slot fromSlot = headState.Slot; BeaconState state = BeaconState.Clone(headState); // Check base state ValidatorIndex validatorIndex = CheckValidatorIndex(state, validatorPublicKey); duty = CheckStateDuty(state, validatorIndex, duty); // Check future states duty = CheckFutureSlots(state, endSlot, validatorIndex, duty); // Check historical states if (startSlot < fromSlot && (duty.AttestationSlot == Slot.None || duty.BlockProposalSlot == Slot.None)) { duty = await CheckHistoricalSlotsAsync(_store, historicalBlockRoots, fromSlot, startSlot, validatorIndex, duty).ConfigureAwait(false); } } else { Root endRoot = await _forkChoice.GetAncestorAsync(_store, head, endSlot - Slot.One); BeaconState state = await _store.GetBlockStateAsync(endRoot).ConfigureAwait(false); // Check base state ValidatorIndex validatorIndex = CheckValidatorIndex(state, validatorPublicKey); duty = CheckStateDuty(state, validatorIndex, duty); // Check historical states IReadOnlyList <Root> historicalBlockRoots = state.BlockRoots; if (duty.AttestationSlot == Slot.None || duty.BlockProposalSlot == Slot.None) { Slot fromSlot = state.Slot; duty = await CheckHistoricalSlotsAsync(_store, historicalBlockRoots, fromSlot, startSlot, validatorIndex, duty).ConfigureAwait(false); } } // HACK: Shards were removed from Phase 0, but analogy is committee index, so use for initial testing. Shard attestationShard = new Shard((ulong)duty.AttestationCommitteeIndex); ValidatorDuty validatorDuty = new ValidatorDuty(validatorPublicKey, duty.AttestationSlot, attestationShard, duty.BlockProposalSlot); return(validatorDuty); }
public async Task <ValidatorDuty> GetValidatorDutyAsync(BlsPublicKey validatorPublicKey, Epoch epoch) { if (!_storeProvider.TryGetStore(out IStore? retrievedStore)) { throw new Exception("Beacon chain is currently syncing or waiting for genesis."); } IStore store = retrievedStore !; Hash32 head = await _forkChoice.GetHeadAsync(store); if (!store.TryGetBlockState(head, out BeaconState? headState)) { throw new Exception($"Head state {head} not found."); } Epoch currentEpoch = _beaconStateAccessor.GetCurrentEpoch(headState !); Epoch nextEpoch = currentEpoch + Epoch.One; if (epoch == Epoch.None) { epoch = currentEpoch; } else if (epoch > nextEpoch) { throw new ArgumentOutOfRangeException(nameof(epoch), epoch, $"Duties cannot look ahead more than the next epoch {nextEpoch}."); } TimeParameters timeParameters = _timeParameterOptions.CurrentValue; Slot startSlot = _beaconChainUtility.ComputeStartSlotOfEpoch(epoch); Slot endSlot = startSlot + new Slot(timeParameters.SlotsPerEpoch); Slot attestationSlot = Slot.None; CommitteeIndex attestationCommitteeIndex = CommitteeIndex.None; Slot blockProposalSlot = Slot.None; if (epoch == nextEpoch) { // Clone for next or current, so that it can be safely mutated (transitioned forward) BeaconState state = BeaconState.Clone(headState !); _beaconStateTransition.ProcessSlots(state, startSlot); // Check base state ValidatorIndex validatorIndex = CheckValidatorIndex(state, validatorPublicKey); CheckStateDuty(state, validatorIndex, ref attestationSlot, ref attestationCommitteeIndex, ref blockProposalSlot); // Check future states CheckFutureSlots(state, endSlot, validatorIndex, ref attestationSlot, ref attestationCommitteeIndex, ref blockProposalSlot); } else if (epoch == currentEpoch) { // Take block slot and roots before cloning (for historical checks) IReadOnlyList <Hash32> historicalBlockRoots = headState !.BlockRoots; Slot fromSlot = headState !.Slot; BeaconState state = BeaconState.Clone(headState !); // Check base state ValidatorIndex validatorIndex = CheckValidatorIndex(state, validatorPublicKey); CheckStateDuty(state, validatorIndex, ref attestationSlot, ref attestationCommitteeIndex, ref blockProposalSlot); // Check future states CheckFutureSlots(state, endSlot, validatorIndex, ref attestationSlot, ref attestationCommitteeIndex, ref blockProposalSlot); // Check historical states if (startSlot < fromSlot && (attestationSlot == Slot.None || blockProposalSlot == Slot.None)) { CheckHistoricalSlots(store, historicalBlockRoots, fromSlot, startSlot, validatorIndex, ref attestationSlot, ref attestationCommitteeIndex, ref blockProposalSlot); } } else { Hash32 endRoot = _forkChoice.GetAncestor(store, head, endSlot - Slot.One); if (!store.TryGetBlockState(endRoot, out BeaconState? endState)) { throw new Exception($"State {endRoot} for slot {endSlot} not found."); } BeaconState state = endState !; // Check base state ValidatorIndex validatorIndex = CheckValidatorIndex(state, validatorPublicKey); CheckStateDuty(state, validatorIndex, ref attestationSlot, ref attestationCommitteeIndex, ref blockProposalSlot); // Check historical states IReadOnlyList <Hash32> historicalBlockRoots = state.BlockRoots; Slot fromSlot = state.Slot; if (attestationSlot == Slot.None || blockProposalSlot == Slot.None) { CheckHistoricalSlots(store, historicalBlockRoots, fromSlot, startSlot, validatorIndex, ref attestationSlot, ref attestationCommitteeIndex, ref blockProposalSlot); } } // HACK: Shards were removed from Phase 0, but analogy is committee index, so use for initial testing. Shard attestationShard = new Shard((ulong)attestationCommitteeIndex); ValidatorDuty validatorDuty = new ValidatorDuty(validatorPublicKey, attestationSlot, attestationShard, blockProposalSlot); return(validatorDuty); }