/// <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 async Task OnAttestationAsync(IStore store, Attestation attestation) { if (_logger.IsInfo()) { Log.OnAttestation(_logger, attestation, null); } TimeParameters timeParameters = _timeParameterOptions.CurrentValue; Checkpoint target = attestation.Data.Target; // Attestations must be from the current or previous epoch Slot currentSlot = GetCurrentSlot(store); Epoch currentEpoch = _beaconChainUtility.ComputeEpochAtSlot(currentSlot); // Use GENESIS_EPOCH for previous when genesis to avoid underflow Epoch previousEpoch = currentEpoch > _chainConstants.GenesisEpoch ? currentEpoch - new Epoch(1) : _chainConstants.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}."); } Epoch dataSlotEpoch = _beaconChainUtility.ComputeEpochAtSlot(attestation.Data.Slot); if (attestation.Data.Target.Epoch != dataSlotEpoch) { throw new ArgumentOutOfRangeException("attestation.Data.Target.Epoch", attestation.Data.Target.Epoch, $"Attestation data target epoch must match the attestation data slot {attestation.Data.Slot} (epoch {dataSlotEpoch})."); } // Attestations target be for a known block. If target block is unknown, delay consideration until the block is found BeaconBlock targetBlock = (await store.GetSignedBlockAsync(target.Root).ConfigureAwait(false)).Message; // Attestations cannot be from future epochs. If they are, delay consideration until the epoch arrives BeaconState targetStoredState = await store.GetBlockStateAsync(target.Root).ConfigureAwait(false); BeaconState baseState = BeaconState.Clone(targetStoredState); Slot targetEpochStartSlot = _beaconChainUtility.ComputeStartSlotOfEpoch(target.Epoch); if (currentSlot < targetEpochStartSlot) { throw new Exception( $"Ättestation target epoch start slot {targetEpochStartSlot} should not be larger than the store current slot {currentSlot})."); } // Attestations must be for a known block. If block is unknown, delay consideration until the block is found BeaconBlock attestationBlock = (await store.GetSignedBlockAsync(attestation.Data.BeaconBlockRoot).ConfigureAwait(false)).Message; // 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( $"Attestation 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 BeaconState?targetState = await store.GetCheckpointStateAsync(target, false).ConfigureAwait(false); if (targetState == null) { _beaconStateTransition.ProcessSlots(baseState, targetEpochStartSlot); await store.SetCheckpointStateAsync(target, baseState).ConfigureAwait(false); 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. Slot newCurrentSlot = GetCurrentSlot(store); if (newCurrentSlot < attestation.Data.Slot + 1) { throw new Exception( $"Attestation data slot {attestation.Data.Slot} should not be larger than the store current slot {newCurrentSlot})."); } // Get state at the `target` to validate attestation and calculate the committees IndexedAttestation indexedAttestation = _beaconStateAccessor.GetIndexedAttestation(targetState, attestation); Domain domain = _beaconStateAccessor.GetDomain(targetState, _signatureDomainOptions.CurrentValue.BeaconAttester, indexedAttestation.Data.Target.Epoch); bool isValid = _beaconChainUtility.IsValidIndexedAttestation(targetState, indexedAttestation, domain); if (!isValid) { throw new Exception($"Indexed attestation {indexedAttestation} is not valid."); } // Update latest messages IEnumerable <ValidatorIndex> attestingIndices = _beaconStateAccessor.GetAttestingIndices(targetState, attestation.Data, attestation.AggregationBits); foreach (ValidatorIndex index in attestingIndices) { LatestMessage?latestMessage = await store.GetLatestMessageAsync(index, false).ConfigureAwait(false); if (latestMessage == null || target.Epoch > latestMessage !.Epoch) { latestMessage = new LatestMessage(target.Epoch, attestation.Data.BeaconBlockRoot); await store.SetLatestMessageAsync(index, latestMessage).ConfigureAwait(false); } } }
/// <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 storedAttestationBlock)) { throw new ArgumentOutOfRangeException("attestation.Data.BeaconBlockRoot", attestation.Data.BeaconBlockRoot, "Attestation data root not found in the block history."); } var attestationBlock = storedAttestationBlock !; // 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($"Attestation 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; ulong 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) { latestMessage = new LatestMessage(target.Epoch, attestation.Data.BeaconBlockRoot); store.SetLatestMessage(index, latestMessage); } } }