/// <summary> /// Returns a number BEFORE the lowest beacon info where the forward beacon sync should start, or the latest /// block that was processed where we should continue processing. /// </summary> /// <returns></returns> private long?GetStartingPoint() { long startingPoint = Math.Min(_blockTree.BestKnownNumber + 1, _beaconPivot.ProcessDestination?.Number ?? long.MaxValue); bool foundBeaconBlock; if (_logger.IsTrace) { _logger.Trace($"ChainLevelHelper. starting point's starting point is {startingPoint}"); } BlockInfo?beaconMainChainBlock = GetBeaconMainChainBlockInfo(startingPoint); if (beaconMainChainBlock == null) { return(null); } if (!beaconMainChainBlock.IsBeaconInfo) { return(startingPoint); } Keccak currentHash = beaconMainChainBlock.BlockHash; // in normal situation we will have one iteration of this loop, in some cases a few. Thanks to that we don't need to add extra pointer to manage forward syncing do { BlockHeader?header = _blockTree.FindHeader(currentHash !, BlockTreeLookupOptions.None); if (header == null) { if (_logger.IsTrace) { _logger.Trace($"Header for number {startingPoint} was not found"); } return(null); } BlockInfo parentBlockInfo = (_blockTree.GetInfo(header.Number - 1, header.ParentHash !)).Info; foundBeaconBlock = parentBlockInfo.IsBeaconInfo; if (_logger.IsTrace) { _logger.Trace( $"Searching for starting point on level {startingPoint}. Header: {header.ToString(BlockHeader.Format.FullHashAndNumber)}, BlockInfo: {parentBlockInfo.IsBeaconBody}, {parentBlockInfo.IsBeaconHeader}"); } // Note: the starting point, points to the non-beacon info block. // MergeBlockDownloader does not download the first header so this is deliberate --startingPoint; currentHash = header.ParentHash !; if (_syncConfig.FastSync && startingPoint <= _syncConfig.PivotNumberParsed) { if (_logger.IsTrace) { _logger.Trace($"Reached syncConfig pivot. Starting point: {startingPoint}"); } break; } } while (foundBeaconBlock); return(startingPoint); }
public Task <ResultWrapper <ForkchoiceUpdatedV1Result> > Handle(ForkchoiceStateV1 forkchoiceState, PayloadAttributes?payloadAttributes) { string requestStr = $"{forkchoiceState} {payloadAttributes}"; if (_logger.IsInfo) { _logger.Info($"Received: {requestStr}"); } if (_invalidChainTracker.IsOnKnownInvalidChain(forkchoiceState.HeadBlockHash, out Keccak? lastValidHash)) { if (_logger.IsInfo) { _logger.Info($" FCU - Invalid - {requestStr} {forkchoiceState.HeadBlockHash} is known to be a part of an invalid chain."); } return(ForkchoiceUpdatedV1Result.Invalid(lastValidHash)); } Block?newHeadBlock = GetBlock(forkchoiceState.HeadBlockHash); if (newHeadBlock is null) // if a head is unknown we are syncing { if (_blockCacheService.BlockCache.TryGetValue(forkchoiceState.HeadBlockHash, out Block? block)) { StartNewBeaconHeaderSync(forkchoiceState, block, requestStr); return(ForkchoiceUpdatedV1Result.Syncing); } if (_logger.IsInfo) { _logger.Info($"Syncing... Unknown forkchoiceState head hash... Request: {requestStr}."); } return(ForkchoiceUpdatedV1Result.Syncing); } BlockInfo blockInfo = _blockTree.GetInfo(newHeadBlock.Number, newHeadBlock.GetOrCalculateHash()).Info; if (!blockInfo.WasProcessed) { BlockHeader?blockParent = _blockTree.FindHeader(newHeadBlock.ParentHash !); if (blockParent == null) { if (_logger.IsDebug) { _logger.Debug($"Parent of block not available. Starting new beacon header. sync."); } StartNewBeaconHeaderSync(forkchoiceState, newHeadBlock !, requestStr); return(ForkchoiceUpdatedV1Result.Syncing); } if (!blockInfo.IsBeaconMainChain && blockInfo.IsBeaconInfo) { ReorgBeaconChainDuringSync(newHeadBlock !, blockInfo); } int processingQueueCount = _processingQueue.Count; if (processingQueueCount == 0) { _peerRefresher.RefreshPeers(newHeadBlock !.Hash !, newHeadBlock.ParentHash !, forkchoiceState.FinalizedBlockHash); _blockCacheService.FinalizedHash = forkchoiceState.FinalizedBlockHash; _mergeSyncController.StopBeaconModeControl(); if (_logger.IsInfo) { _logger.Info($"Syncing beacon headers... Request: {requestStr}."); } } else { if (_logger.IsInfo) { _logger.Info($"Processing {_processingQueue.Count} blocks... Request: {requestStr}."); } } _beaconPivot.ProcessDestination ??= newHeadBlock !.Header; return(ForkchoiceUpdatedV1Result.Syncing); } if (_logger.IsInfo) { _logger.Info($"FCU - block {newHeadBlock} was processed."); } BlockHeader?finalizedHeader = ValidateBlockHash(forkchoiceState.FinalizedBlockHash, out string?finalizationErrorMsg); if (finalizationErrorMsg is not null) { if (_logger.IsWarn) { _logger.Warn($"Invalid finalized block hash {finalizationErrorMsg}. Request: {requestStr}."); } return(ForkchoiceUpdatedV1Result.Error(finalizationErrorMsg, MergeErrorCodes.InvalidForkchoiceState)); } ValidateBlockHash(forkchoiceState.SafeBlockHash, out string?safeBlockErrorMsg); if (safeBlockErrorMsg is not null) { if (_logger.IsWarn) { _logger.Warn($"Invalid safe block hash {finalizationErrorMsg}. Request: {requestStr}."); } return(ForkchoiceUpdatedV1Result.Error(safeBlockErrorMsg, MergeErrorCodes.InvalidForkchoiceState)); } if ((newHeadBlock.TotalDifficulty ?? 0) != 0 && (_poSSwitcher.MisconfiguredTerminalTotalDifficulty() || _poSSwitcher.BlockBeforeTerminalTotalDifficulty(newHeadBlock.Header))) { if (_logger.IsWarn) { _logger.Warn($"Invalid terminal block. Nethermind TTD {_poSSwitcher.TerminalTotalDifficulty}, NewHeadBlock TD: {newHeadBlock.Header.TotalDifficulty}. Request: {requestStr}."); } // https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md#specification // {status: INVALID, latestValidHash: 0x0000000000000000000000000000000000000000000000000000000000000000, validationError: errorMessage | null} if terminal block conditions are not satisfied return(ForkchoiceUpdatedV1Result.Invalid(Keccak.Zero)); } Block[]? blocks = EnsureNewHead(newHeadBlock, out string?setHeadErrorMsg); if (setHeadErrorMsg is not null) { if (_logger.IsWarn) { _logger.Warn($"Invalid new head block {setHeadErrorMsg}. Request: {requestStr}."); } return(ForkchoiceUpdatedV1Result.Error(setHeadErrorMsg, ErrorCodes.InvalidParams)); } if (_blockTree.IsOnMainChainBehindHead(newHeadBlock)) { if (_logger.IsInfo) { _logger.Info($"Valid. ForkchoiceUpdated ignored - already in canonical chain. Request: {requestStr}."); } return(ForkchoiceUpdatedV1Result.Valid(null, forkchoiceState.HeadBlockHash)); } EnsureTerminalBlock(forkchoiceState, blocks); bool newHeadTheSameAsCurrentHead = _blockTree.Head !.Hash == newHeadBlock.Hash; bool shouldUpdateHead = !newHeadTheSameAsCurrentHead && blocks is not null; if (shouldUpdateHead) { _blockTree.UpdateMainChain(blocks !, true, true); } if (IsInconsistent(forkchoiceState.FinalizedBlockHash)) { string errorMsg = $"Inconsistent forkchoiceState - finalized block hash. Request: {requestStr}"; if (_logger.IsWarn) { _logger.Warn(errorMsg); } return(ForkchoiceUpdatedV1Result.Error(errorMsg, MergeErrorCodes.InvalidForkchoiceState)); } if (IsInconsistent(forkchoiceState.SafeBlockHash)) { string errorMsg = $"Inconsistent forkchoiceState - safe block hash. Request: {requestStr}"; if (_logger.IsWarn) { _logger.Warn(errorMsg); } return(ForkchoiceUpdatedV1Result.Error(errorMsg, MergeErrorCodes.InvalidForkchoiceState)); } bool nonZeroFinalizedBlockHash = forkchoiceState.FinalizedBlockHash != Keccak.Zero; // bool nonZeroSafeBlockHash = forkchoiceState.SafeBlockHash != Keccak.Zero; if (nonZeroFinalizedBlockHash) { _manualBlockFinalizationManager.MarkFinalized(newHeadBlock.Header, finalizedHeader !); } if (shouldUpdateHead) { _poSSwitcher.ForkchoiceUpdated(newHeadBlock.Header, forkchoiceState.FinalizedBlockHash); if (_logger.IsInfo) { _logger.Info($"Block {forkchoiceState.HeadBlockHash} was set as head."); } } string?payloadId = null; if (payloadAttributes is not null) { payloadAttributes.GasLimit = null; if (newHeadBlock.Timestamp >= payloadAttributes.Timestamp) { if (_logger.IsWarn) { _logger.Warn($"Invalid payload attributes timestamp {payloadAttributes.Timestamp}, block timestamp {newHeadBlock.Timestamp}. Request: {requestStr}."); } return(ForkchoiceUpdatedV1Result.Error( $"Invalid payload attributes timestamp {payloadAttributes.Timestamp}, block timestamp {newHeadBlock.Timestamp}. Request: {requestStr}", MergeErrorCodes.InvalidPayloadAttributes)); } else { payloadId = _payloadPreparationService.StartPreparingPayload(newHeadBlock.Header, payloadAttributes); } } if (_logger.IsInfo) { _logger.Info($"Valid. Request: {requestStr}."); } _blockTree.ForkChoiceUpdated(forkchoiceState.FinalizedBlockHash, forkchoiceState.SafeBlockHash); return(ForkchoiceUpdatedV1Result.Valid(payloadId, forkchoiceState.HeadBlockHash)); }