public override async Task StopAsync(CancellationToken cancellationToken) { if (_logger.IsDebug()) { LogDebug.BeaconNodeWorkerStopping(_logger, null); } _stopped = true; await base.StopAsync(cancellationToken); }
public override async Task StartAsync(CancellationToken cancellationToken) { if (_logger.IsDebug()) { LogDebug.BeaconNodeWorkerStarting(_logger, null); } await base.StartAsync(cancellationToken); if (_logger.IsDebug()) { LogDebug.BeaconNodeWorkerStarted(_logger, null); } }
public async Task <ApiResponse> PublishBlockAsync(SignedBeaconBlock signedBlock, CancellationToken cancellationToken) { try { if (!_store.IsInitialized) { return(new ApiResponse(StatusCode.CurrentlySyncing)); } bool acceptedLocally = false; try { await _forkChoice.OnBlockAsync(_store, signedBlock).ConfigureAwait(false); // TODO: validate as per honest validator spec and return true/false acceptedLocally = true; } catch (Exception ex) { if (_logger.IsWarn()) { Log.BlockNotAcceptedLocally(_logger, signedBlock.Message, ex); } } if (_logger.IsDebug()) { LogDebug.PublishingBlockToNetwork(_logger, signedBlock.Message, null); } await _networkPeering.PublishBeaconBlockAsync(signedBlock).ConfigureAwait(false); if (acceptedLocally) { return(new ApiResponse(StatusCode.Success)); } else { return(new ApiResponse(StatusCode.BroadcastButFailedValidation)); } } catch (Exception ex) { if (_logger.IsWarn()) { Log.ApiErrorPublishBlock(_logger, ex); } throw; } }
public async Task OnPeerDialOutConnected(string peerId) { Root headRoot = await _forkChoice.GetHeadAsync(_store).ConfigureAwait(false); BeaconState beaconState = await _store.GetBlockStateAsync(headRoot).ConfigureAwait(false); // Send request var status = BuildStatusFromHead(headRoot, beaconState); if (_logger.IsDebug()) { LogDebug.SendingStatusToPeer(_logger, RpcDirection.Request, status, peerId, null); } await _networkPeering.SendStatusAsync(peerId, RpcDirection.Request, status).ConfigureAwait(false); }
private async Task HandlePeerStatus(string peerId, PeeringStatus peerPeeringStatus, Root headRoot, BeaconState beaconState) { // if not valid, "immediately disconnect from one another following the handshake" bool isValidPeer = await IsValidPeerStatus(peerId, peerPeeringStatus, headRoot, beaconState) .ConfigureAwait(false); if (!isValidPeer) { await _networkPeering.DisconnectPeerAsync(peerId).ConfigureAwait(false); return; } // check if we should request blocks var isPeerAhead = IsPeerAhead(peerPeeringStatus, beaconState); if (isPeerAhead) { // In theory, our chain since finalized checkpoint could be wrong // However it may be more efficient to check if our head is correct and sync from there, // or use the step option to sample blocks and find where we diverge. Slot finalizedSlot = _beaconChainUtility.ComputeStartSlotOfEpoch(beaconState.FinalizedCheckpoint.Epoch); if (_logger.IsInfo()) { Log.RequestingBlocksFromAheadPeer(_logger, peerId, finalizedSlot, peerPeeringStatus.HeadRoot, peerPeeringStatus.HeadSlot, null); } // TODO: Need more sophistication, like Eth1; as peers are discovered, just put into a pool, // then, when need for sync determined, select the best peer(s) to use. await _networkPeering.RequestBlocksAsync(peerId, peerPeeringStatus.HeadRoot, finalizedSlot, peerPeeringStatus.HeadSlot); } else { if (_logger.IsDebug()) { LogDebug.PeerBehind(_logger, peerId, peerPeeringStatus.FinalizedEpoch, peerPeeringStatus.HeadRoot, peerPeeringStatus.HeadSlot, null); } } }
public async Task OnStatusRequestReceived(string peerId, PeeringStatus peerPeeringStatus) { Root headRoot = await _forkChoice.GetHeadAsync(_store).ConfigureAwait(false); BeaconState beaconState = await _store.GetBlockStateAsync(headRoot).ConfigureAwait(false); // Send response var status = BuildStatusFromHead(headRoot, beaconState); if (_logger.IsDebug()) { LogDebug.SendingStatusToPeer(_logger, RpcDirection.Response, status, peerId, null); } await _networkPeering.SendStatusAsync(peerId, RpcDirection.Response, status).ConfigureAwait(false); // Determine if the peer is valid, and if we need to request blocks await HandlePeerStatus(peerId, peerPeeringStatus, headRoot, beaconState); }
/// <param name="eth1BlockHash"></param> /// <param name="eth1Timestamp"></param> /// <param name="deposits"></param> /// <returns></returns> public async Task <bool> TryGenesisAsync(Bytes32 eth1BlockHash, ulong eth1Timestamp) { if (_logger.IsDebug()) { LogDebug.TryGenesis(_logger, eth1BlockHash, eth1Timestamp, (uint)_depositStore.Deposits.Count, null); } BeaconState candidateState = InitializeBeaconStateFromEth1(eth1BlockHash, eth1Timestamp); if (IsValidGenesisState(candidateState)) { BeaconState genesisState = candidateState; await _forkChoice.InitializeForkChoiceStoreAsync(_store, genesisState); return(true); } return(false); }
public async Task <bool> TryGenesisAsync(Hash32 eth1BlockHash, ulong eth1Timestamp, IList <Deposit> deposits) { return(await Task.Run(() => { if (_logger.IsDebug()) { LogDebug.TryGenesis(_logger, eth1BlockHash, eth1Timestamp, deposits.Count, null); } BeaconState candidateState = _genesis.InitializeBeaconStateFromEth1(eth1BlockHash, eth1Timestamp, deposits); if (_genesis.IsValidGenesisState(candidateState)) { BeaconState genesisState = candidateState; _ = _forkChoice.GetGenesisStore(genesisState); return true; } return 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()); }
public async Task OnBlockAsync(IStore store, SignedBeaconBlock signedBlock) { MiscellaneousParameters miscellaneousParameters = _miscellaneousParameterOptions.CurrentValue; MaxOperationsPerBlock maxOperationsPerBlock = _maxOperationsPerBlockOptions.CurrentValue; BeaconBlock block = signedBlock.Message; Root blockRoot = _cryptographyService.HashTreeRoot(block); if (_logger.IsInfo()) { Log.OnBlock(_logger, blockRoot, block, signedBlock.Signature, null); } // Make a copy of the state to avoid mutability issues BeaconState parentState = await store.GetBlockStateAsync(block.ParentRoot).ConfigureAwait(false); BeaconState preState = BeaconState.Clone(parentState); // Blocks cannot be in the future. If they are, their consideration must be delayed until the are in the past. ulong storeTime = store.Time; Slot storeCurrentSlot = GetCurrentSlot(store); if (storeCurrentSlot < block.Slot) { throw new ArgumentOutOfRangeException(nameof(block), block.Slot, $"Block slot time cannot be in the future, compared to store time {storeTime} (slot {storeCurrentSlot}, since genesis {store.GenesisTime}."); } // Add new block to the store await store.SetSignedBlockAsync(blockRoot, signedBlock).ConfigureAwait(false); // Check that block is later than the finalized epoch slot (optimization to reduce calls to get_ancestor) Slot finalizedSlot = _beaconChainUtility.ComputeStartSlotOfEpoch(store.FinalizedCheckpoint.Epoch); if (block.Slot <= finalizedSlot) { throw new ArgumentOutOfRangeException(nameof(block), block.Slot, $"Block slot must be later than the finalized epoch start slot {finalizedSlot}."); } // Check block is a descendant of the finalized block at the checkpoint finalized slot Root ancestorOfBlockAtFinalizedSlot = await GetAncestorAsync(store, blockRoot, finalizedSlot).ConfigureAwait(false); if (!ancestorOfBlockAtFinalizedSlot.Equals(store.FinalizedCheckpoint.Root)) { throw new Exception( $"Block with hash tree root {blockRoot} is not a descendant of the finalized block {store.FinalizedCheckpoint.Root} at slot {finalizedSlot}."); } // Check the block is valid and compute the post-state BeaconState state = _beaconStateTransition.StateTransition(preState, signedBlock, validateResult: true); // Add new state for this block to the store await store.SetBlockStateAsync(blockRoot, state).ConfigureAwait(false); if (_logger.IsDebug()) { LogDebug.AddedBlockToStore(_logger, block, state, blockRoot, null); } // Update justified checkpoint if (state.CurrentJustifiedCheckpoint.Epoch > store.JustifiedCheckpoint.Epoch) { if (state.CurrentJustifiedCheckpoint.Epoch > store.BestJustifiedCheckpoint.Epoch) { await store.SetBestJustifiedCheckpointAsync(state.CurrentJustifiedCheckpoint).ConfigureAwait(false); } bool shouldUpdateJustifiedCheckpoint = await ShouldUpdateJustifiedCheckpointAsync(store, state.CurrentJustifiedCheckpoint) .ConfigureAwait(false); if (shouldUpdateJustifiedCheckpoint) { await store.SetJustifiedCheckpointAsync(state.CurrentJustifiedCheckpoint).ConfigureAwait(false); if (_logger.IsDebug()) { LogDebug.UpdateJustifiedCheckpoint(_logger, state.CurrentJustifiedCheckpoint, null); } } else { if (_logger.IsDebug()) { LogDebug.UpdateBestJustifiedCheckpoint(_logger, state.CurrentJustifiedCheckpoint, null); } } } // Update finalized checkpoint if (state.FinalizedCheckpoint.Epoch > store.FinalizedCheckpoint.Epoch) { await store.SetFinalizedCheckpointAsync(state.FinalizedCheckpoint).ConfigureAwait(false); if (_logger.IsDebug()) { LogDebug.UpdateFinalizedCheckpoint(_logger, state.FinalizedCheckpoint, null); } // Update justified if new justified is later than store justified // or if store justified is not in chain with finalized checkpoint if (state.CurrentJustifiedCheckpoint.Epoch > store.JustifiedCheckpoint.Epoch) { await store.SetJustifiedCheckpointAsync(state.CurrentJustifiedCheckpoint).ConfigureAwait(false); if (_logger.IsDebug()) { LogDebug.UpdateJustifiedCheckpoint(_logger, state.CurrentJustifiedCheckpoint, null); } } else { Slot newFinalizedSlot = _beaconChainUtility.ComputeStartSlotOfEpoch(store.FinalizedCheckpoint.Epoch); Root ancestorOfJustifiedAtNewFinalizedSlot = await GetAncestorAsync(store, store.JustifiedCheckpoint.Root, newFinalizedSlot) .ConfigureAwait(false); if (!ancestorOfJustifiedAtNewFinalizedSlot.Equals(store.FinalizedCheckpoint.Root)) { await store.SetJustifiedCheckpointAsync(state.CurrentJustifiedCheckpoint).ConfigureAwait(false); if (_logger.IsDebug()) { LogDebug.UpdateJustifiedCheckpoint(_logger, state.CurrentJustifiedCheckpoint, null); } } } } }
protected override async Task ExecuteAsync(CancellationToken stoppingToken) { if (_logger.IsInfo()) { Log.BeaconNodeWorkerExecuteStarted(_logger, _clientVersion.Description, _dataDirectory.ResolvedPath, _environment.EnvironmentName, Thread.CurrentThread.ManagedThreadId, null); } try { await _nodeStart.InitializeNodeAsync(); bool started = false; while (!stoppingToken.IsCancellationRequested && !_stopped) { try { DateTimeOffset clockTime = _clock.UtcNow(); ulong time = (ulong)clockTime.ToUnixTimeSeconds(); if (_store.IsInitialized) { if (!started) { if (_logger.IsInfo()) { long slotValue = ((long)time - (long)_store.GenesisTime) / _timeParameterOptions.CurrentValue.SecondsPerSlot; Log.WorkerStoreAvailableTickStarted(_logger, _store !.GenesisTime, time, slotValue, Thread.CurrentThread.ManagedThreadId, null); } started = true; } if (time >= _store.GenesisTime) { await _forkChoice.OnTickAsync(_store, time); } else { long timeToGenesis = (long)_store.GenesisTime - (long)time; if (timeToGenesis < 10 || timeToGenesis % 10 == 0) { if (_logger.IsInfo()) { Log.GenesisCountdown(_logger, timeToGenesis, null); } } } } // Wait for remaining time, if any // NOTE: To fast forward time during testing, have the second call to test _clock.Now() jump forward to avoid waiting. DateTimeOffset nextClockTime = DateTimeOffset.FromUnixTimeSeconds((long)time + 1); TimeSpan remaining = nextClockTime - _clock.UtcNow(); if (remaining > TimeSpan.Zero) { await Task.Delay(remaining, stoppingToken); } } catch (TaskCanceledException) { // This is expected when exiting } catch (Exception ex) { if (_logger.IsError()) { Log.BeaconNodeWorkerLoopError(_logger, ex); } } } } catch (Exception ex) { Log.BeaconNodeWorkerCriticalError(_logger, ex); throw; } if (_logger.IsDebug()) { LogDebug.BeaconNodeWorkerExecuteExiting(_logger, Thread.CurrentThread.ManagedThreadId, null); } }
public async Task OnBlockAsync(IStore store, BeaconBlock block) { MiscellaneousParameters miscellaneousParameters = _miscellaneousParameterOptions.CurrentValue; MaxOperationsPerBlock maxOperationsPerBlock = _maxOperationsPerBlockOptions.CurrentValue; Hash32 signingRoot = _cryptographyService.SigningRoot(block); if (_logger.IsInfo()) { Log.OnBlock(_logger, signingRoot, block, null); } // Make a copy of the state to avoid mutability issues BeaconState parentState = await store.GetBlockStateAsync(block.ParentRoot).ConfigureAwait(false); BeaconState preState = BeaconState.Clone(parentState); // Blocks cannot be in the future. If they are, their consideration must be delayed until the are in the past. ulong blockTime = preState.GenesisTime + (ulong)block.Slot * _timeParameterOptions.CurrentValue.SecondsPerSlot; if (blockTime > store.Time) { throw new ArgumentOutOfRangeException(nameof(block), blockTime, $"Block slot time cannot be in the future, compared to store time {store.Time}."); } // Add new block to the store await store.SetBlockAsync(signingRoot, block).ConfigureAwait(false); // Check block is a descendant of the finalized block BeaconBlock finalizedCheckpointBlock = await store.GetBlockAsync(store.FinalizedCheckpoint.Root).ConfigureAwait(false); Hash32 ancestor = await GetAncestorAsync(store, signingRoot, finalizedCheckpointBlock !.Slot).ConfigureAwait(false); if (ancestor != store.FinalizedCheckpoint.Root) { throw new Exception($"Block with signing root {signingRoot} is not a descendant of the finalized block {store.FinalizedCheckpoint.Root} at slot {finalizedCheckpointBlock.Slot}."); } // Check that block is later than the finalized epoch slot Slot finalizedEpochStartSlot = _beaconChainUtility.ComputeStartSlotOfEpoch(store.FinalizedCheckpoint.Epoch); if (block.Slot <= finalizedEpochStartSlot) { throw new ArgumentOutOfRangeException(nameof(block), block.Slot, $"Block slot must be later than the finalized epoch start slot {finalizedEpochStartSlot}."); } // Check the block is valid and compute the post-state BeaconState state = _beaconStateTransition.StateTransition(preState, block, validateStateRoot: true); // Add new state for this block to the store await store.SetBlockStateAsync(signingRoot, state).ConfigureAwait(false); if (_logger.IsDebug()) { LogDebug.AddedBlockToStore(_logger, block, state, signingRoot, null); } // Update justified checkpoint if (state.CurrentJustifiedCheckpoint.Epoch > store.JustifiedCheckpoint.Epoch) { await store.SetBestJustifiedCheckpointAsync(state.CurrentJustifiedCheckpoint).ConfigureAwait(false); bool shouldUpdateJustifiedCheckpoint = await ShouldUpdateJustifiedCheckpointAsync(store, state.CurrentJustifiedCheckpoint).ConfigureAwait(false); if (shouldUpdateJustifiedCheckpoint) { await store.SetJustifiedCheckpointAsync(state.CurrentJustifiedCheckpoint).ConfigureAwait(false); if (_logger.IsDebug()) { LogDebug.UpdateJustifiedCheckpoint(_logger, state.CurrentJustifiedCheckpoint, null); } } else { if (_logger.IsDebug()) { LogDebug.UpdateBestJustifiedCheckpoint(_logger, state.CurrentJustifiedCheckpoint, null); } } } // Update finalized checkpoint if (state.FinalizedCheckpoint.Epoch > store.FinalizedCheckpoint.Epoch) { await store.SetFinalizedCheckpointAsync(state.FinalizedCheckpoint).ConfigureAwait(false); if (_logger.IsDebug()) { LogDebug.UpdateFinalizedCheckpoint(_logger, state.FinalizedCheckpoint, null); } } }
public async Task <ApiResponse> PublishAttestationAsync(Attestation signedAttestation, CancellationToken cancellationToken) { try { if (!_store.IsInitialized) { return(new ApiResponse(StatusCode.CurrentlySyncing)); } // NOTE: For an attestation generated 1/3 way during the slot, it will fail validation (as it is not in the past), // therefore it will never be accepted locally, but always broadcast. // i.e. broadcast, aggregated, added to operations pool, and pulled out to be included in subsequent blocks, // then applied (from the block) // But we want to include valid attestations in fork choice rules. // So, if might be valid in future, then hold until it is; i.e. check each slot // Once pending attestations are validated, add to latest message, but also to pool (for future blocks) // Need to exclude from pool if included in another block (but only if/when building on that chain) // A reorg could make previously invalid attestations now valid, or previously included attestations free, etc // i.e. don't remove, but need to keep/cache until finalisation (of target) has passed bool acceptedLocally = false; try { await _forkChoice.OnAttestationAsync(_store, signedAttestation).ConfigureAwait(false); acceptedLocally = true; } catch (Exception ex) { if (_logger.IsWarn()) { Log.AttestationNotAcceptedLocally(_logger, signedAttestation, ex); } } // TODO: Network publishing if (_logger.IsDebug()) { LogDebug.PublishingAttestationToNetwork(_logger, signedAttestation, null); } await _networkPeering.PublishAttestationAsync(signedAttestation).ConfigureAwait(false); if (acceptedLocally) { return(new ApiResponse(StatusCode.Success)); } else { return(new ApiResponse(StatusCode.BroadcastButFailedValidation)); } } catch (Exception ex) { if (_logger.IsWarn()) { Log.ApiErrorPublishAttestation(_logger, ex); } throw; } }