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)); }