示例#1
0
        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));
        }