public async Task QuickStart64DutiesForEpochZeroHaveExactlyOneProposerPerSlot() { // Arrange int numberOfValidators = 64; int genesisTime = 1578009600; IServiceCollection testServiceCollection = TestSystem.BuildTestServiceCollection(useStore: true); IConfigurationRoot configuration = new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary <string, string> { ["QuickStart:ValidatorCount"] = $"{numberOfValidators}", ["QuickStart:GenesisTime"] = $"{genesisTime}" }) .Build(); testServiceCollection.AddBeaconNodeQuickStart(configuration); testServiceCollection.AddSingleton <IHostEnvironment>(Substitute.For <IHostEnvironment>()); ServiceProvider testServiceProvider = testServiceCollection.BuildServiceProvider(); INodeStart quickStart = testServiceProvider.GetService <INodeStart>(); await quickStart.InitializeNodeAsync(); IStoreProvider storeProvider = testServiceProvider.GetService <IStoreProvider>(); storeProvider.TryGetStore(out IStore? store).ShouldBeTrue(); BeaconState state = await store !.GetBlockStateAsync(store !.FinalizedCheckpoint.Root); // Act Epoch targetEpoch = new Epoch(0); IEnumerable <BlsPublicKey> validatorPublicKeys = state !.Validators.Select(x => x.PublicKey); IBeaconNodeApi beaconNode = testServiceProvider.GetService <IBeaconNodeApi>(); beaconNode.ShouldBeOfType(typeof(BeaconNodeFacade)); int validatorDutyIndex = 0; List <ValidatorDuty> validatorDuties = new List <ValidatorDuty>(); await foreach (ValidatorDuty validatorDuty in beaconNode.ValidatorDutiesAsync(validatorPublicKeys, targetEpoch, CancellationToken.None)) { validatorDuties.Add(validatorDuty); Console.WriteLine("Index [{0}], Epoch {1}, Validator {2}, : attestation slot {3}, shard {4}, proposal slot {5}", validatorDutyIndex, targetEpoch, validatorDuty.ValidatorPublicKey, validatorDuty.AttestationSlot, (ulong)validatorDuty.AttestationShard, validatorDuty.BlockProposalSlot); validatorDutyIndex++; } // Assert Dictionary <Slot, IGrouping <Slot, ValidatorDuty> > groupsByProposalSlot = validatorDuties .GroupBy(x => x.BlockProposalSlot) .ToDictionary(x => x.Key, x => x); groupsByProposalSlot[new Slot(0)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(1)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(2)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(3)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(4)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(5)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(6)].Count().ShouldBe(1); //groupsByProposalSlot[new Slot(7)].Count().ShouldBe(1); //groupsByProposalSlot[Slot.None].Count().ShouldBe(numberOfValidators - 8); }
public async Task TestValidators80DutiesForEpochZeroHaveExactlyOneProposerPerSlot() { // Arrange IServiceCollection testServiceCollection = TestSystem.BuildTestServiceCollection(useStore: true); testServiceCollection.AddSingleton <IHostEnvironment>(Substitute.For <IHostEnvironment>()); testServiceCollection.AddSingleton <IEth1DataProvider>(Substitute.For <IEth1DataProvider>()); testServiceCollection.AddSingleton <IOperationPool>(Substitute.For <IOperationPool>()); ServiceProvider testServiceProvider = testServiceCollection.BuildServiceProvider(); BeaconState state = TestState.PrepareTestState(testServiceProvider); ForkChoice forkChoice = testServiceProvider.GetService <ForkChoice>(); // Get genesis store initialise MemoryStoreProvider with the state _ = forkChoice.GetGenesisStore(state); TimeParameters timeParameters = testServiceProvider.GetService <IOptions <TimeParameters> >().Value; int numberOfValidators = state.Validators.Count; Console.WriteLine("Number of validators: {0}", numberOfValidators); BlsPublicKey[] publicKeys = TestKeys.PublicKeys(timeParameters).ToArray(); byte[][] privateKeys = TestKeys.PrivateKeys(timeParameters).ToArray(); for (int index = 0; index < numberOfValidators; index++) { Console.WriteLine("[{0}] priv:{1} pub:{2}", index, "0x" + BitConverter.ToString(privateKeys[index]).Replace("-", ""), publicKeys[index]); } // Act Epoch targetEpoch = new Epoch(0); IEnumerable <BlsPublicKey> validatorPublicKeys = publicKeys.Take(numberOfValidators); IBeaconNodeApi beaconNode = testServiceProvider.GetService <IBeaconNodeApi>(); beaconNode.ShouldBeOfType(typeof(BeaconNodeFacade)); int validatorDutyIndex = 0; List <ValidatorDuty> validatorDuties = new List <ValidatorDuty>(); await foreach (ValidatorDuty validatorDuty in beaconNode.ValidatorDutiesAsync(validatorPublicKeys, targetEpoch, CancellationToken.None)) { validatorDuties.Add(validatorDuty); Console.WriteLine("Index [{0}], Epoch {1}, Validator {2}, : attestation slot {3}, shard {4}, proposal slot {5}", validatorDutyIndex, targetEpoch, validatorDuty.ValidatorPublicKey, validatorDuty.AttestationSlot, (ulong)validatorDuty.AttestationShard, validatorDuty.BlockProposalSlot); validatorDutyIndex++; } // Assert Dictionary <Slot, IGrouping <Slot, ValidatorDuty> > groupsByProposalSlot = validatorDuties .GroupBy(x => x.BlockProposalSlot) .ToDictionary(x => x.Key, x => x); groupsByProposalSlot[new Slot(0)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(1)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(2)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(3)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(4)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(5)].Count().ShouldBe(1); //groupsByProposalSlot[new Slot(6)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(7)].Count().ShouldBe(1); //groupsByProposalSlot[Slot.None].Count().ShouldBe(numberOfValidators - 7); }
// ReSharper disable once InconsistentNaming // ReSharper disable once IdentifierTypo public async Task <IActionResult> GetAsync([FromQuery] byte[][] validator_pubkeys, [FromQuery] ulong?epoch, CancellationToken cancellationToken) { IList <BlsPublicKey> publicKeys = validator_pubkeys.Select(x => new BlsPublicKey(x)).ToList(); Epoch?targetEpoch = (Epoch?)epoch; ApiResponse <IList <ValidatorDuty> > apiResponse = await _beaconNode.ValidatorDutiesAsync(publicKeys, targetEpoch, cancellationToken) .ConfigureAwait(false); switch (apiResponse.StatusCode) { case Core2.Api.StatusCode.Success: return(Ok(apiResponse.Content)); case Core2.Api.StatusCode.InvalidRequest: return(Problem("Invalid request syntax.", statusCode: (int)apiResponse.StatusCode)); case Core2.Api.StatusCode.CurrentlySyncing: return(Problem("Beacon node is currently syncing, try again later.", statusCode: (int)apiResponse.StatusCode)); case Core2.Api.StatusCode.DutiesNotAvailableForRequestedEpoch: return(Problem("Duties cannot be provided for the requested epoch.", statusCode: (int)apiResponse.StatusCode)); } return(Problem("Beacon node internal error.", statusCode: (int)apiResponse.StatusCode)); }
public async Task TestValidators80DutiesForEpochOneHaveExactlyOneProposerPerSlot() { // Arrange IServiceCollection testServiceCollection = TestSystem.BuildTestServiceCollection(useStore: true); testServiceCollection.AddSingleton <IHostEnvironment>(Substitute.For <IHostEnvironment>()); testServiceCollection.AddSingleton <IEth1DataProvider>(Substitute.For <IEth1DataProvider>()); testServiceCollection.AddSingleton <IOperationPool>(Substitute.For <IOperationPool>()); ServiceProvider testServiceProvider = testServiceCollection.BuildServiceProvider(); BeaconState state = TestState.PrepareTestState(testServiceProvider); ForkChoice forkChoice = testServiceProvider.GetService <ForkChoice>(); // Get genesis store initialise MemoryStoreProvider with the state _ = forkChoice.GetGenesisStore(state); TimeParameters timeParameters = testServiceProvider.GetService <IOptions <TimeParameters> >().Value; int numberOfValidators = state.Validators.Count; Console.WriteLine("Number of validators: {0}", numberOfValidators); BlsPublicKey[] publicKeys = TestKeys.PublicKeys(timeParameters).ToArray(); // Act Epoch targetEpoch = new Epoch(1); var validatorPublicKeys = publicKeys.Take(numberOfValidators); IBeaconNodeApi beaconNode = testServiceProvider.GetService <IBeaconNodeApi>(); beaconNode.ShouldBeOfType(typeof(BeaconNodeFacade)); var validatorDuties = await beaconNode.ValidatorDutiesAsync(validatorPublicKeys, targetEpoch); for (var index = 0; index < validatorDuties.Count; index++) { ValidatorDuty validatorDuty = validatorDuties[index]; Console.WriteLine("Index [{0}], Epoch {1}, Validator {2}, : attestation slot {3}, shard {4}, proposal slot {5}", index, targetEpoch, validatorDuty.ValidatorPublicKey, validatorDuty.AttestationSlot, (ulong)validatorDuty.AttestationShard, validatorDuty.BlockProposalSlot); } // Assert Dictionary <Slot, IGrouping <Slot, ValidatorDuty> > groupsByProposalSlot = validatorDuties .GroupBy(x => x.BlockProposalSlot) .ToDictionary(x => x.Key, x => x); groupsByProposalSlot[new Slot(8)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(9)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(10)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(11)].Count().ShouldBe(1); //groupsByProposalSlot[new Slot(12)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(13)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(14)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(15)].Count().ShouldBe(1); //groupsByProposalSlot[Slot.None].Count().ShouldBe(numberOfValidators - 8); }
/// <summary>Get validator duties for the requested validators.</summary> /// <param name="validator_pubkeys">An array of hex-encoded BLS public keys</param> /// <returns>Success response</returns> public async Task <ICollection <ValidatorDuty> > DutiesAsync(System.Collections.Generic.IEnumerable <byte[]> validator_pubkeys, ulong?epoch) { IEnumerable <BlsPublicKey> publicKeys = validator_pubkeys.Select(x => new BlsPublicKey(x)); Epoch targetEpoch = epoch.HasValue ? new Epoch((ulong)epoch) : Epoch.None; var duties = _beaconNode.ValidatorDutiesAsync(publicKeys, targetEpoch, CancellationToken.None); List <ValidatorDuty> result = new List <ValidatorDuty>(); await foreach (var duty in duties) { ValidatorDuty validatorDuty = new ValidatorDuty(); validatorDuty.Validator_pubkey = duty.ValidatorPublicKey.Bytes; validatorDuty.Attestation_slot = duty.AttestationSlot; validatorDuty.Attestation_shard = (ulong)duty.AttestationShard; validatorDuty.Block_proposal_slot = duty.BlockProposalSlot == Slot.None ? null : (ulong?)duty.BlockProposalSlot; result.Add(validatorDuty); } return(result); }
/// <summary>Get validator duties for the requested validators.</summary> /// <param name="validator_pubkeys">An array of hex-encoded BLS public keys</param> /// <returns>Success response</returns> public async Task <ICollection <ValidatorDuty> > DutiesAsync(System.Collections.Generic.IEnumerable <byte[]> validator_pubkeys, int?epoch) { IEnumerable <BlsPublicKey> publicKeys = validator_pubkeys.Select(x => new BlsPublicKey(x)); Epoch targetEpoch = epoch.HasValue ? new Epoch((ulong)epoch) : Epoch.None; IList <BeaconNode.ValidatorDuty> duties = await _beaconNode.ValidatorDutiesAsync(publicKeys, targetEpoch); List <ValidatorDuty> result = duties.Select(x => { ValidatorDuty validatorDuty = new ValidatorDuty(); validatorDuty.Validator_pubkey = x.ValidatorPublicKey.Bytes; validatorDuty.Attestation_slot = (int)x.AttestationSlot; validatorDuty.Attestation_shard = (int)(ulong)x.AttestationShard; validatorDuty.Block_proposal_slot = x.BlockProposalSlot == Slot.None ? null : (int?)x.BlockProposalSlot; return(validatorDuty); }) .ToList(); return(result); }
public async Task UpdateDutiesAsync(Epoch epoch, CancellationToken cancellationToken) { IEnumerable <BlsPublicKey> publicKeys = _validatorKeyProvider.GetPublicKeys(); IAsyncEnumerable <ValidatorDuty> validatorDuties = _beaconNodeApi.ValidatorDutiesAsync(publicKeys, epoch, cancellationToken); await foreach (ValidatorDuty validatorDuty in validatorDuties.ConfigureAwait(false)) { Slot?currentAttestationSlot = _validatorState.AttestationSlot.GetValueOrDefault(validatorDuty.ValidatorPublicKey); Shard?currentAttestationShard = _validatorState.AttestationShard.GetValueOrDefault(validatorDuty.ValidatorPublicKey); if (validatorDuty.AttestationSlot != currentAttestationSlot || validatorDuty.AttestationShard != currentAttestationShard) { _validatorState.SetAttestationDuty(validatorDuty.ValidatorPublicKey, validatorDuty.AttestationSlot, validatorDuty.AttestationShard); if (_logger.IsInfo()) { Log.ValidatorDutyAttestationChanged(_logger, validatorDuty.ValidatorPublicKey, epoch, validatorDuty.AttestationSlot, validatorDuty.AttestationShard, null); } } } await foreach (ValidatorDuty validatorDuty in validatorDuties.ConfigureAwait(false)) { Slot?currentProposalSlot = _validatorState.ProposalSlot.GetValueOrDefault(validatorDuty.ValidatorPublicKey); if (validatorDuty.BlockProposalSlot != Slot.None && validatorDuty.BlockProposalSlot != currentProposalSlot) { _validatorState.SetProposalDuty(validatorDuty.ValidatorPublicKey, validatorDuty.BlockProposalSlot); if (_logger.IsInfo()) { Log.ValidatorDutyProposalChanged(_logger, validatorDuty.ValidatorPublicKey, epoch, validatorDuty.BlockProposalSlot, null); } } } }
// ReSharper disable once InconsistentNaming // ReSharper disable once IdentifierTypo public async Task <IActionResult> GetAsync([FromQuery] byte[][] validator_pubkeys, [FromQuery] ulong?epoch, CancellationToken cancellationToken) { IList <BlsPublicKey> publicKeys = validator_pubkeys.Select(x => new BlsPublicKey(x)).ToList(); Epoch?targetEpoch = (Epoch?)epoch; // NOTE: Spec 0.10.1 still has old Shard references in OAPI in the Duties JSON, although the spec has changed to Index; // use Index as it is easier to understand (i.e. the spec OAPI in 0.10.1 is wrong) ApiResponse <IList <ValidatorDuty> > apiResponse = await _beaconNode.ValidatorDutiesAsync(publicKeys, targetEpoch, cancellationToken) .ConfigureAwait(false); return(apiResponse.StatusCode switch { Core2.Api.StatusCode.Success => Ok(apiResponse.Content), Core2.Api.StatusCode.InvalidRequest => Problem("Invalid request syntax.", statusCode: (int)apiResponse.StatusCode), Core2.Api.StatusCode.CurrentlySyncing => Problem("Beacon node is currently syncing, try again later.", statusCode: (int)apiResponse.StatusCode), Core2.Api.StatusCode.DutiesNotAvailableForRequestedEpoch => Problem( "Duties cannot be provided for the requested epoch.", statusCode: (int)apiResponse.StatusCode), _ => Problem("Beacon node internal error.", statusCode: (int)apiResponse.StatusCode) });
public async Task TestValidators80DutiesForEpochOneHaveExactlyOneProposerPerSlot() { // Arrange IServiceCollection testServiceCollection = TestSystem.BuildTestServiceCollection(useStore: true); testServiceCollection.AddSingleton <IHostEnvironment>(Substitute.For <IHostEnvironment>()); testServiceCollection.AddSingleton <IEth1DataProvider>(Substitute.For <IEth1DataProvider>()); testServiceCollection.AddSingleton <IOperationPool>(Substitute.For <IOperationPool>()); ServiceProvider testServiceProvider = testServiceCollection.BuildServiceProvider(); BeaconState state = TestState.PrepareTestState(testServiceProvider); IForkChoice forkChoice = testServiceProvider.GetService <IForkChoice>(); // Get genesis store initialise MemoryStoreProvider with the state IStore store = testServiceProvider.GetService <IStore>(); await forkChoice.InitializeForkChoiceStoreAsync(store, state); TimeParameters timeParameters = testServiceProvider.GetService <IOptions <TimeParameters> >().Value; int numberOfValidators = state.Validators.Count; Console.WriteLine("Number of validators: {0}", numberOfValidators); BlsPublicKey[] publicKeys = TestKeys.PublicKeys(timeParameters).ToArray(); // Act Epoch targetEpoch = new Epoch(1); var validatorPublicKeys = publicKeys.Take(numberOfValidators).ToList(); IBeaconNodeApi beaconNode = testServiceProvider.GetService <IBeaconNodeApi>(); beaconNode.ShouldBeOfType(typeof(BeaconNodeFacade)); int validatorDutyIndex = 0; List <ValidatorDuty> validatorDuties = new List <ValidatorDuty>(); var validatorDutiesResponse = await beaconNode.ValidatorDutiesAsync(validatorPublicKeys, targetEpoch, CancellationToken.None); foreach (ValidatorDuty validatorDuty in validatorDutiesResponse.Content) { validatorDuties.Add(validatorDuty); Console.WriteLine("Index [{0}], Epoch {1}, Validator {2}, : attestation slot {3}, shard {4}, proposal slot {5}", validatorDutyIndex, targetEpoch, validatorDuty.ValidatorPublicKey, validatorDuty.AttestationSlot, (ulong)validatorDuty.AttestationShard, validatorDuty.BlockProposalSlot); validatorDutyIndex++; } Console.WriteLine(); Console.WriteLine("** ValidatorDuty summary"); foreach (ValidatorDuty validatorDuty in validatorDuties) { Console.WriteLine("Index [{0}], Epoch {1}, Validator {2}, : attestation slot {3}, shard {4}, proposal slot {5}", validatorDutyIndex, targetEpoch, validatorDuty.ValidatorPublicKey, validatorDuty.AttestationSlot, (ulong)validatorDuty.AttestationShard, validatorDuty.BlockProposalSlot); } // Assert Dictionary <Slot, IGrouping <Slot, ValidatorDuty> > groupsByProposalSlot = validatorDuties .Where(x => x.BlockProposalSlot.HasValue) .GroupBy(x => x.BlockProposalSlot !.Value) .ToDictionary(x => x.Key, x => x); groupsByProposalSlot[new Slot(8)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(9)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(10)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(11)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(12)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(13)].Count().ShouldBe(1); groupsByProposalSlot[new Slot(14)].Count().ShouldBe(1); //groupsByProposalSlot[new Slot(15)].Count().ShouldBe(1); validatorDuties.Count(x => !x.BlockProposalSlot.HasValue).ShouldBe(numberOfValidators - 7); }
public async Task UpdateDutiesActivityAsync(Epoch epoch, CancellationToken cancellationToken) { Activity activity = new Activity("update-duties"); activity.Start(); try { IList <BlsPublicKey> publicKeys = _validatorKeyProvider.GetPublicKeys(); ApiResponse <IList <ValidatorDuty> > validatorDutiesResponse = await _beaconNodeApi.ValidatorDutiesAsync(publicKeys, epoch, cancellationToken); if (validatorDutiesResponse.StatusCode != StatusCode.Success) { Log.ErrorGettingValidatorDuties(_logger, (int)validatorDutiesResponse.StatusCode, validatorDutiesResponse.StatusCode, null); return; } // Record proposal duties first, in case there is an error foreach (ValidatorDuty validatorDuty in validatorDutiesResponse.Content) { Slot?currentProposalSlot = _validatorState.ProposalSlot.GetValueOrDefault(validatorDuty.ValidatorPublicKey); if (validatorDuty.BlockProposalSlot != Slot.None && validatorDuty.BlockProposalSlot != currentProposalSlot) { _validatorState.SetProposalDuty(validatorDuty.ValidatorPublicKey, validatorDuty.BlockProposalSlot); if (_logger.IsInfo()) { Log.ValidatorDutyProposalChanged(_logger, validatorDuty.ValidatorPublicKey, epoch, validatorDuty.BlockProposalSlot, null); } } } foreach (ValidatorDuty validatorDuty in validatorDutiesResponse.Content) { Slot?currentAttestationSlot = _validatorState.AttestationSlot.GetValueOrDefault(validatorDuty.ValidatorPublicKey); Shard?currentAttestationShard = _validatorState.AttestationShard.GetValueOrDefault(validatorDuty.ValidatorPublicKey); if (validatorDuty.AttestationSlot != currentAttestationSlot || validatorDuty.AttestationShard != currentAttestationShard) { _validatorState.SetAttestationDuty(validatorDuty.ValidatorPublicKey, validatorDuty.AttestationSlot, validatorDuty.AttestationShard); if (_logger.IsDebug()) { LogDebug.ValidatorDutyAttestationChanged(_logger, validatorDuty.ValidatorPublicKey, epoch, validatorDuty.AttestationSlot, validatorDuty.AttestationShard, null); } } } } catch (Exception ex) { Log.ExceptionGettingValidatorDuties(_logger, ex.Message, ex); } finally { activity.Stop(); } }
public async Task UpdateDutiesActivityAsync(Epoch epoch, CancellationToken cancellationToken) { Activity activity = new Activity("update-duties"); activity.Start(); using IDisposable activityScope = _logger.BeginScope("[TraceId, {TraceId}], [SpanId, {SpanId}]", activity.TraceId, activity.SpanId); try { IList <BlsPublicKey> publicKeys = _validatorKeyProvider.GetPublicKeys(); ApiResponse <IList <ValidatorDuty> > validatorDutiesResponse = await _beaconNodeApi.ValidatorDutiesAsync(publicKeys, epoch, cancellationToken); if (validatorDutiesResponse.StatusCode != StatusCode.Success) { Log.ErrorGettingValidatorDuties(_logger, (int)validatorDutiesResponse.StatusCode, validatorDutiesResponse.StatusCode, null); return; } // Record proposal duties first, in case there is an error foreach (ValidatorDuty validatorDuty in validatorDutiesResponse.Content) { if (validatorDuty.BlockProposalSlot.HasValue) { Slot?currentProposalSlot = _validatorState.ProposalSlot.GetValueOrDefault(validatorDuty.ValidatorPublicKey); bool needsProposalUpdate = validatorDuty.BlockProposalSlot != currentProposalSlot; if (needsProposalUpdate) { _validatorState.SetProposalDuty(validatorDuty.ValidatorPublicKey, validatorDuty.BlockProposalSlot.Value); if (_logger.IsInfo()) { Log.ValidatorDutyProposalChanged(_logger, validatorDuty.ValidatorPublicKey, epoch, validatorDuty.BlockProposalSlot.Value, null); } } } } foreach (ValidatorDuty validatorDuty in validatorDutiesResponse.Content) { if (validatorDuty.AttestationSlot.HasValue) { bool needsAttestationUpdate; if (_validatorState.AttestationSlotAndIndex.TryGetValue(validatorDuty.ValidatorPublicKey, out (Slot, CommitteeIndex)currentValue)) { (Slot currentAttestationSlot, CommitteeIndex currentAttestationIndex) = currentValue; needsAttestationUpdate = validatorDuty.AttestationSlot != currentAttestationSlot || validatorDuty.AttestationIndex != currentAttestationIndex; } else { needsAttestationUpdate = true; } if (needsAttestationUpdate) { _validatorState.SetAttestationDuty(validatorDuty.ValidatorPublicKey, validatorDuty.AttestationSlot.Value, validatorDuty.AttestationIndex !.Value); if (_logger.IsDebug()) { LogDebug.ValidatorDutyAttestationChanged(_logger, validatorDuty.ValidatorPublicKey, epoch, validatorDuty.AttestationSlot.Value, validatorDuty.AttestationIndex.Value, null); } } } } } catch (Exception ex) { Log.ExceptionGettingValidatorDuties(_logger, ex.Message, ex); } finally { activity.Stop(); } }