public void ProcessAttestation(BeaconState state, Attestation attestation) { _logger.LogInformation(Event.ProcessAttestation, "Process block operation attestation {Attestation} for state {BeaconState}.", attestation, state); var timeParameters = _timeParameterOptions.CurrentValue; var data = attestation.Data; var committeeCount = _beaconStateAccessor.GetCommitteeCountAtSlot(state, data.Slot); if ((ulong)data.Index >= committeeCount) { throw new ArgumentOutOfRangeException("attestation.Data.Index", data.Index, $"Attestation data committee index must be less that the committee count {committeeCount}."); } var previousEpoch = _beaconStateAccessor.GetPreviousEpoch(state); var currentEpoch = _beaconStateAccessor.GetCurrentEpoch(state); if (data.Target.Epoch != previousEpoch && data.Target.Epoch != currentEpoch) { throw new ArgumentOutOfRangeException("attestation.Data.Target.Epoch", data.Target.Epoch, $"Attestation data target epoch must be either the previous epoch {previousEpoch} or the current epoch {currentEpoch}."); } var minimumSlot = data.Slot + timeParameters.MinimumAttestationInclusionDelay; var maximumSlot = data.Slot + timeParameters.SlotsPerEpoch; if (state.Slot < minimumSlot) { throw new Exception($"Current state slot {state.Slot} must be equal or greater than the attestion slot {data.Slot} plus minimum delay {timeParameters.MinimumAttestationInclusionDelay}."); } if (state.Slot > maximumSlot) { throw new Exception($"Current state slot {state.Slot} must be equal or less than the attestation slot {data.Slot} plus slots per epoch {timeParameters.SlotsPerEpoch}."); } var committee = _beaconStateAccessor.GetBeaconCommittee(state, data.Slot, data.Index); if (attestation.AggregationBits.Count != attestation.CustodyBits.Count) { throw new Exception($"The attestation aggregation bit length {attestation.AggregationBits.Count} must be the same as the custody bit length {attestation.CustodyBits.Count}."); } if (attestation.AggregationBits.Count != committee.Count) { throw new Exception($"The attestation aggregation bit (and custody bit) length {attestation.AggregationBits.Count} must be the same as the committee length {committee.Count}."); } var inclusionDelay = state.Slot - data.Slot; var proposerIndex = _beaconStateAccessor.GetBeaconProposerIndex(state); var pendingAttestation = new PendingAttestation(attestation.AggregationBits, data, inclusionDelay, proposerIndex); if (data.Target.Epoch == currentEpoch) { if (!data.Source.Equals(state.CurrentJustifiedCheckpoint)) { throw new Exception($"For a current epoch target attestation the data source checkpoint {data.Source} must be the same as the current justified checkpoint {state.CurrentJustifiedCheckpoint}."); } state.AddCurrentAttestation(pendingAttestation); } else { if (!data.Source.Equals(state.PreviousJustifiedCheckpoint)) { throw new Exception($"For a previous epoch target attestation the data source checkpoint {data.Source} must be the same as the previous justified checkpoint {state.PreviousJustifiedCheckpoint}."); } state.AddPreviousAttestation(pendingAttestation); } // Check signature var indexedAttestation = _beaconStateAccessor.GetIndexedAttestation(state, attestation); var domain = _beaconStateAccessor.GetDomain(state, _signatureDomainOptions.CurrentValue.BeaconAttester, data.Target.Epoch); var isValid = _beaconChainUtility.IsValidIndexedAttestation(state, indexedAttestation, domain); if (!isValid) { throw new Exception($"Indexed attestation {indexedAttestation} is not valid."); } }