public (IList <Gwei> rewards, IList <Gwei> penalties) GetAttestationDeltas(BeaconState state) { var rewardsAndPenalties = _rewardsAndPenaltiesOptions.CurrentValue; var previousEpoch = _beaconStateAccessor.GetPreviousEpoch(state); var totalBalance = _beaconStateAccessor.GetTotalActiveBalance(state); var validatorCount = state.Validators.Count; var rewards = Enumerable.Repeat(Gwei.Zero, validatorCount).ToList(); var penalties = Enumerable.Repeat(Gwei.Zero, validatorCount).ToList(); var eligibleValidatorIndices = new List <ValidatorIndex>(); for (var index = 0; index < validatorCount; index++) { var validator = state.Validators[index]; var isActive = _beaconChainUtility.IsActiveValidator(validator, previousEpoch); if (isActive || (validator.IsSlashed && previousEpoch + new Epoch(1) < validator.WithdrawableEpoch)) { eligibleValidatorIndices.Add(new ValidatorIndex((ulong)index)); } } // Micro-incentives for matching FFG source, FFG target, and head var matchingSourceAttestations = GetMatchingSourceAttestations(state, previousEpoch); var matchingTargetAttestations = GetMatchingTargetAttestations(state, previousEpoch); var matchingHeadAttestations = GetMatchingHeadAttestations(state, previousEpoch); var attestationSets = new[] { matchingSourceAttestations, matchingTargetAttestations, matchingHeadAttestations }; var setNames = new[] { "Source", "Target", "Head" }; var setIndex = 0; foreach (var attestationSet in attestationSets) { var unslashedAttestingIndices = GetUnslashedAttestingIndices(state, attestationSet); var attestingBalance = _beaconStateAccessor.GetTotalBalance(state, unslashedAttestingIndices); foreach (var index in eligibleValidatorIndices) { if (unslashedAttestingIndices.Contains(index)) { var reward = (GetBaseReward(state, index) * (ulong)attestingBalance) / (ulong)totalBalance; _logger.LogDebug(0, "Reward validator {ValidatorIndex} matching {SetName} +{Reward}", index, setNames[setIndex], reward); rewards[(int)(ulong)index] += reward; } else { var penalty = GetBaseReward(state, index); _logger.LogDebug(0, "Penalty validator {ValidatorIndex} non-matching {SetName} -{Penalty}", index, setNames[setIndex], penalty); penalties[(int)(ulong)index] += penalty; } } setIndex++; } // Proposer and inclusion delay micro-rewards var unslashedSourceAttestingIndices = GetUnslashedAttestingIndices(state, matchingSourceAttestations); foreach (var index in unslashedSourceAttestingIndices) { var attestation = matchingSourceAttestations .Where(x => { var attestingIndices = _beaconStateAccessor.GetAttestingIndices(state, x.Data, x.AggregationBits); return(attestingIndices.Contains(index)); }) .OrderBy(x => x.InclusionDelay) .First(); var baseReward = GetBaseReward(state, index); var proposerReward = baseReward / rewardsAndPenalties.ProposerRewardQuotient; _logger.LogDebug(0, "Reward validator {ValidatorIndex} proposer +{Reward}", attestation.ProposerIndex, proposerReward); rewards[(int)(ulong)attestation.ProposerIndex] += proposerReward; var maxAttesterReward = baseReward - proposerReward; var attesterReward = maxAttesterReward / (ulong)attestation.InclusionDelay; _logger.LogDebug(0, "Reward validator {ValidatorIndex} attester inclusion delay +{Reward}", index, attesterReward); rewards[(int)(ulong)index] += attesterReward; } // Inactivity penalty var finalityDelay = previousEpoch - state.FinalizedCheckpoint.Epoch; if (finalityDelay > _timeParameterOptions.CurrentValue.MinimumEpochsToInactivityPenalty) { var matchingTargetAttestingIndices = GetUnslashedAttestingIndices(state, matchingTargetAttestations); foreach (var index in eligibleValidatorIndices) { var delayPenalty = GetBaseReward(state, index) * _chainConstants.BaseRewardsPerEpoch; _logger.LogDebug(0, "Penalty validator {ValidatorIndex} finality delay -{Penalty}", index, delayPenalty); penalties[(int)(ulong)index] += delayPenalty; if (!matchingTargetAttestingIndices.Contains(index)) { var effectiveBalance = state.Validators[(int)(ulong)index].EffectiveBalance; var additionalInactivityPenalty = (effectiveBalance * (ulong)finalityDelay) / rewardsAndPenalties.InactivityPenaltyQuotient; _logger.LogDebug(0, "Penalty validator {ValidatorIndex} inactivity -{Penalty}", index, additionalInactivityPenalty); penalties[(int)(ulong)index] += additionalInactivityPenalty; } } } return(rewards, penalties); }
/// <summary> /// Run ``on_attestation`` upon receiving a new ``attestation`` from either within a block or directly on the wire. /// An ``attestation`` that is asserted as invalid may be valid at a later time, /// consider scheduling it for later processing in such case. /// </summary> public void OnAttestation(IStore store, Attestation attestation) { var initialValues = _initialValueOptions.CurrentValue; var timeParameters = _timeParameterOptions.CurrentValue; var target = attestation.Data.Target; // Attestations must be from the current or previous epoch var currentSlot = GetCurrentSlot(store); var currentEpoch = _beaconChainUtility.ComputeEpochAtSlot(currentSlot); // Use GENESIS_EPOCH for previous when genesis to avoid underflow var previousEpoch = currentEpoch > initialValues.GenesisEpoch ? currentEpoch - new Epoch(1) : initialValues.GenesisEpoch; if (target.Epoch != currentEpoch && target.Epoch != previousEpoch) { throw new ArgumentOutOfRangeException("attestation.Target.Epoch", target.Epoch, $"Attestation target epoch must be either the current epoch {currentEpoch} or previous epoch {previousEpoch}."); } // Cannot calculate the current shuffling if have not seen the target if (!store.TryGetBlock(target.Root, out var targetBlock)) { throw new ArgumentOutOfRangeException("attestation.Target.Root", target.Root, "Attestation target root not found in the block history."); } // Attestations target be for a known block. If target block is unknown, delay consideration until the block is found if (!store.TryGetBlockState(target.Root, out var targetStoredState)) { throw new ArgumentOutOfRangeException("attestation.Target.Root", target.Root, "Attestation target root not found in the block stores history."); } // Attestations cannot be from future epochs. If they are, delay consideration until the epoch arrives var baseState = BeaconState.Clone(targetStoredState); var targetEpochStartSlot = _beaconChainUtility.ComputeStartSlotOfEpoch(target.Epoch); var targetEpochStartSlotTime = baseState.GenesisTime + (ulong)targetEpochStartSlot * timeParameters.SecondsPerSlot; if (store.Time < targetEpochStartSlotTime) { throw new Exception($"Ättestation target state time {targetEpochStartSlotTime} should not be larger than the store time {store.Time})."); } // Attestations must be for a known block. If block is unknown, delay consideration until the block is found if (!store.TryGetBlock(attestation.Data.BeaconBlockRoot, out var attestationBlock)) { throw new ArgumentOutOfRangeException("attestation.Data.BeaconBlockRoot", attestation.Data.BeaconBlockRoot, "Attestation data root not found in the block history."); } // Attestations must not be for blocks in the future. If not, the attestation should not be considered if (attestationBlock.Slot > attestation.Data.Slot) { throw new Exception($"Ättestation data root slot {attestationBlock.Slot} should not be larger than the attestation data slot {attestation.Data.Slot})."); } // Store target checkpoint state if not yet seen if (!store.TryGetCheckpointState(target, out var targetState)) { _beaconStateTransition.ProcessSlots(baseState, targetEpochStartSlot); store.SetCheckpointState(target, baseState); targetState = baseState; } // Attestations can only affect the fork choice of subsequent slots. // Delay consideration in the fork choice until their slot is in the past. //var attestationDataSlotTime = ((ulong)attestation.Data.Slot + 1) * timeParameters.SecondsPerSlot; var attestationDataSlotTime = targetState.GenesisTime + ((ulong)attestation.Data.Slot + 1) * timeParameters.SecondsPerSlot; if (store.Time < attestationDataSlotTime) { throw new Exception($"Ättestation data time {attestationDataSlotTime} should not be larger than the store time {store.Time})."); } // Get state at the `target` to validate attestation and calculate the committees var indexedAttestation = _beaconStateAccessor.GetIndexedAttestation(targetState, attestation); var domain = _beaconStateAccessor.GetDomain(targetState, _signatureDomainOptions.CurrentValue.BeaconAttester, indexedAttestation.Data.Target.Epoch); var isValid = _beaconChainUtility.IsValidIndexedAttestation(targetState, indexedAttestation, domain); if (!isValid) { throw new Exception($"Indexed attestation {indexedAttestation} is not valid."); } // Update latest messages var attestingIndices = _beaconStateAccessor.GetAttestingIndices(targetState, attestation.Data, attestation.AggregationBits); foreach (var index in attestingIndices) { if (!store.TryGetLatestMessage(index, out var latestMessage) || target.Epoch > latestMessage.Epoch) { var newLatestMessage = new LatestMessage(target.Epoch, attestation.Data.BeaconBlockRoot); store.SetLatestMessage(index, newLatestMessage); } } }