/// <summary> /// Transition to the next slot. /// </summary> public static void NextSlot(IServiceProvider testServiceProvider, BeaconState state) { BeaconStateTransition beaconStateTransition = testServiceProvider.GetService <BeaconStateTransition>(); Slot slot = state.Slot + new Slot(1); beaconStateTransition.ProcessSlots(state, slot); }
/// <summary> /// Transition to the start slot of the next epoch /// </summary> public static void NextEpoch(IServiceProvider testServiceProvider, BeaconState state) { TimeParameters timeParameters = testServiceProvider.GetService <IOptions <TimeParameters> >().Value; BeaconStateTransition beaconStateTransition = testServiceProvider.GetService <BeaconStateTransition>(); Slot slot = (Slot)(state.Slot + timeParameters.SlotsPerEpoch - state.Slot % timeParameters.SlotsPerEpoch); beaconStateTransition.ProcessSlots(state, slot); }
public static void SignBlock(IServiceProvider testServiceProvider, BeaconState state, BeaconBlock block, ValidatorIndex proposerIndex) { MiscellaneousParameters miscellaneousParameters = testServiceProvider.GetService <IOptions <MiscellaneousParameters> >().Value; TimeParameters timeParameters = testServiceProvider.GetService <IOptions <TimeParameters> >().Value; MaxOperationsPerBlock maxOperationsPerBlock = testServiceProvider.GetService <IOptions <MaxOperationsPerBlock> >().Value; SignatureDomains signatureDomains = testServiceProvider.GetService <IOptions <SignatureDomains> >().Value; ICryptographyService cryptographyService = testServiceProvider.GetService <ICryptographyService>(); BeaconChainUtility beaconChainUtility = testServiceProvider.GetService <BeaconChainUtility>(); BeaconStateAccessor beaconStateAccessor = testServiceProvider.GetService <BeaconStateAccessor>(); BeaconStateTransition beaconStateTransition = testServiceProvider.GetService <BeaconStateTransition>(); if (state.Slot > block.Slot) { throw new ArgumentOutOfRangeException("block.Slot", block.Slot, $"Slot of block must be equal or less that state slot {state.Slot}"); } Epoch blockEpoch = beaconChainUtility.ComputeEpochAtSlot(block.Slot); if (proposerIndex == ValidatorIndex.None) { if (block.Slot == state.Slot) { proposerIndex = beaconStateAccessor.GetBeaconProposerIndex(state); } else { Epoch stateEpoch = beaconChainUtility.ComputeEpochAtSlot(state.Slot); if (stateEpoch + 1 > blockEpoch) { Console.WriteLine("WARNING: Block slot far away, and no proposer index manually given." + " Signing block is slow due to transition for proposer index calculation."); } // use stub state to get proposer index of future slot BeaconState stubState = BeaconState.Clone(state); beaconStateTransition.ProcessSlots(stubState, block.Slot); proposerIndex = beaconStateAccessor.GetBeaconProposerIndex(stubState); } } byte[][] privateKeys = TestKeys.PrivateKeys(timeParameters).ToArray(); byte[] privateKey = privateKeys[(int)(ulong)proposerIndex]; Domain randaoDomain = beaconStateAccessor.GetDomain(state, signatureDomains.Randao, blockEpoch); Hash32 randaoRevealHash = blockEpoch.HashTreeRoot(); BlsSignature randaoReveal = TestSecurity.BlsSign(randaoRevealHash, privateKey, randaoDomain); block.Body.SetRandaoReveal(randaoReveal); Domain signatureDomain = beaconStateAccessor.GetDomain(state, signatureDomains.BeaconProposer, blockEpoch); Hash32 signingRoot = cryptographyService.SigningRoot(block); BlsSignature signature = TestSecurity.BlsSign(signingRoot, privateKey, signatureDomain); block.SetSignature(signature); }
public static SignedBeaconBlock SignBlock(IServiceProvider testServiceProvider, BeaconState state, BeaconBlock block, ValidatorIndex?optionalProposerIndex) { TimeParameters timeParameters = testServiceProvider.GetService <IOptions <TimeParameters> >().Value; SignatureDomains signatureDomains = testServiceProvider.GetService <IOptions <SignatureDomains> >().Value; ICryptographyService cryptographyService = testServiceProvider.GetService <ICryptographyService>(); BeaconChainUtility beaconChainUtility = testServiceProvider.GetService <BeaconChainUtility>(); BeaconStateAccessor beaconStateAccessor = testServiceProvider.GetService <BeaconStateAccessor>(); BeaconStateTransition beaconStateTransition = testServiceProvider.GetService <BeaconStateTransition>(); if (state.Slot > block.Slot) { throw new ArgumentOutOfRangeException("block.Slot", block.Slot, $"Slot of block must be equal or less that state slot {state.Slot}"); } Epoch blockEpoch = beaconChainUtility.ComputeEpochAtSlot(block.Slot); ValidatorIndex proposerIndex; if (optionalProposerIndex.HasValue) { proposerIndex = optionalProposerIndex.Value; } else { if (block.Slot == state.Slot) { proposerIndex = beaconStateAccessor.GetBeaconProposerIndex(state); } else { Epoch stateEpoch = beaconChainUtility.ComputeEpochAtSlot(state.Slot); if (stateEpoch + 1 > blockEpoch) { Console.WriteLine("WARNING: Block slot far away, and no proposer index manually given." + " Signing block is slow due to transition for proposer index calculation."); } // use stub state to get proposer index of future slot BeaconState stubState = BeaconState.Clone(state); beaconStateTransition.ProcessSlots(stubState, block.Slot); proposerIndex = beaconStateAccessor.GetBeaconProposerIndex(stubState); } } byte[][] privateKeys = TestKeys.PrivateKeys(timeParameters).ToArray(); byte[] privateKey = privateKeys[(int)(ulong)proposerIndex]; Root blockHashTreeRoot = cryptographyService.HashTreeRoot(block); Domain proposerDomain = beaconStateAccessor.GetDomain(state, signatureDomains.BeaconProposer, blockEpoch); Root signingRoot = beaconChainUtility.ComputeSigningRoot(blockHashTreeRoot, proposerDomain); BlsSignature signature = TestSecurity.BlsSign(signingRoot, privateKey); return(new SignedBeaconBlock(block, signature)); }
/// <summary> /// Processes to the next epoch transition, up to, but not including, the sub-transition named ``process_name`` /// </summary> public static void RunEpochProcessingTo(IServiceProvider testServiceProvider, BeaconState state, TestProcessStep step) { TimeParameters timeParameters = testServiceProvider.GetService <IOptions <TimeParameters> >().Value; BeaconStateTransition beaconStateTransition = testServiceProvider.GetService <BeaconStateTransition>(); Slot slot = (Slot)(state.Slot + (timeParameters.SlotsPerEpoch - state.Slot % timeParameters.SlotsPerEpoch) - 1UL); // transition state to slot before epoch state transition beaconStateTransition.ProcessSlots(state, slot); // start transitioning, do one slot update before the epoch itself. beaconStateTransition.ProcessSlot(state); // process components of epoch transition before final-updates if (step == TestProcessStep.ProcessJustificationAndFinalization) { return; } // Note: only run when present. Later phases introduce more to the epoch-processing. beaconStateTransition.ProcessJustificationAndFinalization(state); if (step == TestProcessStep.ProcessRewardsAndPenalties) { return; } beaconStateTransition.ProcessRewardsAndPenalties(state); if (step == TestProcessStep.ProcessRegistryUpdates) { return; } beaconStateTransition.ProcessRegistryUpdates(state); if (step == TestProcessStep.ProcessSlashings) { return; } beaconStateTransition.ProcessSlashings(state); if (step == TestProcessStep.ProcessFinalUpdates) { return; } beaconStateTransition.ProcessFinalUpdates(state); }
public static void AddAttestationsToState(IServiceProvider testServiceProvider, BeaconState state, IEnumerable <Attestation> attestations, Slot slot) { BeaconStateTransition beaconStateTransition = testServiceProvider.GetService <BeaconStateTransition>(); BeaconBlock block = TestBlock.BuildEmptyBlock(testServiceProvider, state, slot, BlsSignature.Zero); foreach (Attestation attestation in attestations) { block.Body.AddAttestations(attestation); } beaconStateTransition.ProcessSlots(state, block.Slot); SignedBeaconBlock signedBlock = TestBlock.SignBlock(testServiceProvider, state, block, ValidatorIndex.None); beaconStateTransition.StateTransition(state, signedBlock, validateResult: false); }
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()); }
/// <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); } } }
public static BeaconBlock BuildEmptyBlock(IServiceProvider testServiceProvider, BeaconState state, Slot slot, BlsSignature randaoReveal) { //if (slot) is none TimeParameters timeParameters = testServiceProvider.GetService <IOptions <TimeParameters> >().Value; SignatureDomains signatureDomains = testServiceProvider.GetService <IOptions <SignatureDomains> >().Value; ICryptographyService cryptographyService = testServiceProvider.GetService <ICryptographyService>(); BeaconChainUtility beaconChainUtility = testServiceProvider.GetService <BeaconChainUtility>(); BeaconStateAccessor beaconStateAccessor = testServiceProvider.GetService <BeaconStateAccessor>(); BeaconStateTransition beaconStateTransition = testServiceProvider.GetService <BeaconStateTransition>(); Eth1Data eth1Data = new Eth1Data(Root.Zero, state.Eth1DepositIndex, Bytes32.Zero); Root stateRoot = !state.LatestBlockHeader.StateRoot.Equals(Root.Zero) ? state.LatestBlockHeader.StateRoot : cryptographyService.HashTreeRoot(state); BeaconBlockHeader previousBlockHeader = new BeaconBlockHeader(state.LatestBlockHeader.Slot, state.LatestBlockHeader.ParentRoot, stateRoot, state.LatestBlockHeader.BodyRoot); Root previousBlockHashTreeRoot = cryptographyService.HashTreeRoot(previousBlockHeader); if (randaoReveal.Equals(BlsSignature.Zero)) { Epoch blockEpoch = beaconChainUtility.ComputeEpochAtSlot(slot); ValidatorIndex proposerIndex; if (slot == state.Slot) { proposerIndex = beaconStateAccessor.GetBeaconProposerIndex(state); } else { Epoch stateEpoch = beaconChainUtility.ComputeEpochAtSlot(state.Slot); if (blockEpoch > stateEpoch + 1) { Console.WriteLine("WARNING: Block slot (epoch {0}) far away from state (epoch {1}), and no proposer index manually given." + " Signing block is slow due to transition for proposer index calculation.", blockEpoch, stateEpoch); } // use stub state to get proposer index of future slot BeaconState stubState = BeaconState.Clone(state); beaconStateTransition.ProcessSlots(stubState, slot); proposerIndex = beaconStateAccessor.GetBeaconProposerIndex(stubState); } byte[][] privateKeys = TestKeys.PrivateKeys(timeParameters).ToArray(); byte[] privateKey = privateKeys[(int)(ulong)proposerIndex]; Domain randaoDomain = beaconStateAccessor.GetDomain(state, signatureDomains.Randao, blockEpoch); Root epochHashTreeRoot = cryptographyService.HashTreeRoot(blockEpoch); Root randaoSigningRoot = beaconChainUtility.ComputeSigningRoot(epochHashTreeRoot, randaoDomain); randaoReveal = TestSecurity.BlsSign(randaoSigningRoot, privateKey); } BeaconBlock emptyBlock = new BeaconBlock(slot, previousBlockHashTreeRoot, Root.Zero, new BeaconBlockBody( randaoReveal, eth1Data, new Bytes32(), Array.Empty <ProposerSlashing>(), Array.Empty <AttesterSlashing>(), Array.Empty <Attestation>(), Array.Empty <Deposit>(), Array.Empty <SignedVoluntaryExit>() )); return(emptyBlock); }
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); }
private void PrepareStateForHeaderProcessing(BeaconState state, BeaconStateTransition beaconStateTransition) { beaconStateTransition.ProcessSlots(state, state.Slot + new Slot(1)); }
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); }
/// <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); } } }
public async Task <Attestation> NewAttestationAsync(BlsPublicKey validatorPublicKey, bool proofOfCustodyBit, Slot slot, CommitteeIndex index, CancellationToken cancellationToken) { Root head = await _store.GetHeadAsync().ConfigureAwait(false); BeaconBlock headBlock = (await _store.GetSignedBlockAsync(head).ConfigureAwait(false)).Message; BeaconState parentState = await _store.GetBlockStateAsync(head).ConfigureAwait(false); // Clone state (will mutate) and process outstanding slots BeaconState headState = BeaconState.Clone(parentState); _beaconStateTransition.ProcessSlots(headState, slot); // Set attestation_data.index = index where index is the index associated with the validator's committee. ValidatorIndex?validatorIndex = FindValidatorIndexByPublicKey(headState, validatorPublicKey); if (validatorIndex == null) { throw new Exception($"Can not find validator index for public key {validatorPublicKey}"); } // TODO: May need a more efficient way to try and find the committee and position within the committee. // Some of this may already be cached in Validator Assignments (generally stable for an epoch), // but not with the index within the committee. Easy enough to extend and use the same cache. IReadOnlyList <ValidatorIndex> committee = _beaconStateAccessor.GetBeaconCommittee(headState, headState.Slot, index); int committeeSize = committee.Count; int?committeeMemberIndexOfValidator = null; for (int committeeMemberIndex = 0; committeeMemberIndex < committee.Count; committeeMemberIndex++) { if (committee[committeeMemberIndex] == validatorIndex) { committeeMemberIndexOfValidator = committeeMemberIndex; break; } } if (committeeMemberIndexOfValidator == null) { throw new Exception($"Validator index {validatorIndex} is not a member of committee {index}"); } Root beaconBlockRoot = _cryptographyService.HashTreeRoot(headBlock); Checkpoint source = headState.CurrentJustifiedCheckpoint; Epoch currentEpoch = _beaconStateAccessor.GetCurrentEpoch(headState); Slot startSlot = _beaconChainUtility.ComputeStartSlotOfEpoch(currentEpoch); Root epochBoundaryBlockRoot; if (startSlot == headState.Slot) { epochBoundaryBlockRoot = beaconBlockRoot; } else { epochBoundaryBlockRoot = _beaconStateAccessor.GetBlockRootAtSlot(headState, startSlot); } Checkpoint target = new Checkpoint(currentEpoch, epochBoundaryBlockRoot); AttestationData attestationData = new AttestationData(slot, index, beaconBlockRoot, source, target); var aggregationBits = new BitArray(committeeSize); aggregationBits.Set(committeeMemberIndexOfValidator.Value, true); var attestation = new Attestation(aggregationBits, attestationData, BlsSignature.Zero); return(attestation); }