From f04bf7045e1ab779d798d6d37fef961502f1eee8 Mon Sep 17 00:00:00 2001 From: "lukasz.rozmej" Date: Mon, 3 Feb 2025 09:23:23 +0100 Subject: [PATCH 1/3] FCU should move GlobalWorldState Root --- .../EngineModuleTests.Setup.cs | 1 + .../Handlers/ForkchoiceUpdatedHandler.cs | 118 +++++++----------- .../Nethermind.Merge.Plugin/MergePlugin.cs | 2 + .../Nethermind.Optimism/OptimismPlugin.cs | 2 + .../TaikoEngineApiTests.cs | 2 + .../Rpc/TaikoForkchoiceUpdatedHandler.cs | 3 + .../Nethermind.Taiko/TaikoPlugin.cs | 1 + 7 files changed, 57 insertions(+), 72 deletions(-) diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Setup.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Setup.cs index 5c8be744150..3970373570b 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Setup.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Setup.cs @@ -138,6 +138,7 @@ protected IEngineRpcModule CreateEngineModule(MergeTestBlockchain chain, ISyncCo peerRefresher, chain.SpecProvider, chain.SyncPeerPool, + chain.WorldStateManager.GlobalWorldState, chain.LogManager, new BlocksConfig().SecondsPerSlot), new GetPayloadBodiesByHashV1Handler(chain.BlockTree, chain.LogManager), diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs index 3412ec614c6..920bc447953 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs @@ -22,6 +22,7 @@ using Nethermind.Merge.Plugin.Data; using Nethermind.Merge.Plugin.InvalidChainTracker; using Nethermind.Merge.Plugin.Synchronization; +using Nethermind.State; using Nethermind.Synchronization.Peers; namespace Nethermind.Merge.Plugin.Handlers; @@ -34,57 +35,29 @@ namespace Nethermind.Merge.Plugin.Handlers; /// /// May initiate a new payload creation. /// -public class ForkchoiceUpdatedHandler : IForkchoiceUpdatedHandler +public class ForkchoiceUpdatedHandler( + IBlockTree blockTree, + IManualBlockFinalizationManager manualBlockFinalizationManager, + IPoSSwitcher poSSwitcher, + IPayloadPreparationService payloadPreparationService, + IBlockProcessingQueue processingQueue, + IBlockCacheService blockCacheService, + IInvalidChainTracker invalidChainTracker, + IMergeSyncController mergeSyncController, + IBeaconPivot beaconPivot, + IPeerRefresher peerRefresher, + ISpecProvider specProvider, + ISyncPeerPool syncPeerPool, + IWorldState globalWorldState, + ILogManager logManager, + ulong secondsPerSlot, + bool simulateBlockProduction = false) + : IForkchoiceUpdatedHandler { - protected readonly IBlockTree _blockTree; - private readonly IManualBlockFinalizationManager _manualBlockFinalizationManager; - private readonly IPoSSwitcher _poSSwitcher; - private readonly IPayloadPreparationService _payloadPreparationService; - private readonly IBlockProcessingQueue _processingQueue; - private readonly IBlockCacheService _blockCacheService; - private readonly IInvalidChainTracker _invalidChainTracker; - private readonly IMergeSyncController _mergeSyncController; - private readonly IBeaconPivot _beaconPivot; - private readonly ILogger _logger; - private readonly IPeerRefresher _peerRefresher; - private readonly ISpecProvider _specProvider; - private readonly bool _simulateBlockProduction; - private readonly ulong _secondsPerSlot; - private readonly ISyncPeerPool _syncPeerPool; - - public ForkchoiceUpdatedHandler( - IBlockTree blockTree, - IManualBlockFinalizationManager manualBlockFinalizationManager, - IPoSSwitcher poSSwitcher, - IPayloadPreparationService payloadPreparationService, - IBlockProcessingQueue processingQueue, - IBlockCacheService blockCacheService, - IInvalidChainTracker invalidChainTracker, - IMergeSyncController mergeSyncController, - IBeaconPivot beaconPivot, - IPeerRefresher peerRefresher, - ISpecProvider specProvider, - ISyncPeerPool syncPeerPool, - ILogManager logManager, - ulong secondsPerSlot, - bool simulateBlockProduction = false) - { - _blockTree = blockTree ?? throw new ArgumentNullException(nameof(blockTree)); - _manualBlockFinalizationManager = manualBlockFinalizationManager ?? throw new ArgumentNullException(nameof(manualBlockFinalizationManager)); - _poSSwitcher = poSSwitcher ?? throw new ArgumentNullException(nameof(poSSwitcher)); - _payloadPreparationService = payloadPreparationService; - _processingQueue = processingQueue; - _blockCacheService = blockCacheService; - _invalidChainTracker = invalidChainTracker; - _mergeSyncController = mergeSyncController; - _beaconPivot = beaconPivot; - _peerRefresher = peerRefresher; - _specProvider = specProvider; - _syncPeerPool = syncPeerPool; - _simulateBlockProduction = simulateBlockProduction; - _secondsPerSlot = secondsPerSlot; - _logger = logManager.GetClassLogger(); - } + protected readonly IBlockTree _blockTree = blockTree ?? throw new ArgumentNullException(nameof(blockTree)); + private readonly IManualBlockFinalizationManager _manualBlockFinalizationManager = manualBlockFinalizationManager ?? throw new ArgumentNullException(nameof(manualBlockFinalizationManager)); + private readonly IPoSSwitcher _poSSwitcher = poSSwitcher ?? throw new ArgumentNullException(nameof(poSSwitcher)); + private readonly ILogger _logger = logManager.GetClassLogger(); public async Task> Handle(ForkchoiceStateV1 forkchoiceState, PayloadAttributes? payloadAttributes, int version) { @@ -116,7 +89,7 @@ protected virtual bool IsOnMainChainBehindHead(Block newHeadBlock, ForkchoiceSta default : // Don't boost priority if we are definitely syncing Thread.CurrentThread.BoostPriority(); - if (_invalidChainTracker.IsOnKnownInvalidChain(forkchoiceState.HeadBlockHash, out Hash256? lastValidHash)) + if (invalidChainTracker.IsOnKnownInvalidChain(forkchoiceState.HeadBlockHash, out Hash256? lastValidHash)) { if (_logger.IsWarn) _logger.Warn($"Received Invalid {forkchoiceState} {payloadAttributes} - {forkchoiceState.HeadBlockHash} is known to be a part of an invalid chain."); return ForkchoiceUpdatedV1Result.Invalid(lastValidHash); @@ -129,7 +102,7 @@ protected virtual bool IsOnMainChainBehindHead(Block newHeadBlock, ForkchoiceSta BlockHeader? headBlockHeader = null; - if (_blockCacheService.BlockCache.TryGetValue(forkchoiceState.HeadBlockHash, out Block? block)) + if (blockCacheService.BlockCache.TryGetValue(forkchoiceState.HeadBlockHash, out Block? block)) { headBlockHeader = block.Header; } @@ -137,7 +110,7 @@ protected virtual bool IsOnMainChainBehindHead(Block newHeadBlock, ForkchoiceSta if (headBlockHeader is null) { if (_logger.IsDebug) _logger.Debug($"Attempting to fetch header from peer: {simpleRequestStr}."); - headBlockHeader = await _syncPeerPool.FetchHeaderFromPeer(forkchoiceState.HeadBlockHash); + headBlockHeader = await syncPeerPool.FetchHeaderFromPeer(forkchoiceState.HeadBlockHash); } if (headBlockHeader is not null) @@ -185,7 +158,7 @@ protected virtual bool IsOnMainChainBehindHead(Block newHeadBlock, ForkchoiceSta return ForkchoiceUpdatedV1Result.Syncing; } - if (_beaconPivot.ShouldForceStartNewSync) + if (beaconPivot.ShouldForceStartNewSync) { if (_logger.IsInfo) _logger.Info("Force starting new sync."); @@ -199,23 +172,23 @@ protected virtual bool IsOnMainChainBehindHead(Block newHeadBlock, ForkchoiceSta ReorgBeaconChainDuringSync(newHeadBlock!, blockInfo); } - int processingQueueCount = _processingQueue.Count; + int processingQueueCount = processingQueue.Count; if (processingQueueCount == 0) { - _peerRefresher.RefreshPeers(newHeadBlock!.Hash!, newHeadBlock.ParentHash!, finalizedBlockHash); - _blockCacheService.FinalizedHash = finalizedBlockHash; - _blockCacheService.HeadBlockHash = forkchoiceState.HeadBlockHash; - _mergeSyncController.StopBeaconModeControl(); + peerRefresher.RefreshPeers(newHeadBlock!.Hash!, newHeadBlock.ParentHash!, finalizedBlockHash); + blockCacheService.FinalizedHash = finalizedBlockHash; + blockCacheService.HeadBlockHash = forkchoiceState.HeadBlockHash; + mergeSyncController.StopBeaconModeControl(); // Debug as already output in Received ForkChoice if (_logger.IsDebug) _logger.Debug($"Syncing beacon headers, Request: {requestStr}"); } else { - if (_logger.IsInfo) _logger.Info($"Processing {_processingQueue.Count} blocks, Request: {requestStr}"); + if (_logger.IsInfo) _logger.Info($"Processing {processingQueue.Count} blocks, Request: {requestStr}"); } - _beaconPivot.ProcessDestination ??= newHeadBlock!.Header; + beaconPivot.ProcessDestination ??= newHeadBlock!.Header; return ForkchoiceUpdatedV1Result.Syncing; } @@ -259,6 +232,7 @@ protected virtual bool IsOnMainChainBehindHead(Block newHeadBlock, ForkchoiceSta if (shouldUpdateHead) { _blockTree.UpdateMainChain(blocks!, true, true); + globalWorldState.StateRoot = newHeadBlock.StateRoot!; } if (IsInconsistent(finalizedBlockHash)) @@ -308,11 +282,11 @@ private ResultWrapper StartBuildingPayload(Block newH { string? payloadId = null; - if (_simulateBlockProduction) + if (simulateBlockProduction) { payloadAttributes ??= new PayloadAttributes() { - Timestamp = newHeadBlock.Timestamp + _secondsPerSlot, + Timestamp = newHeadBlock.Timestamp + secondsPerSlot, ParentBeaconBlockRoot = newHeadBlock.ParentHash, // it doesn't matter PrevRandao = newHeadBlock.ParentHash ?? Keccak.Zero, // it doesn't matter Withdrawals = [], @@ -328,7 +302,7 @@ private ResultWrapper StartBuildingPayload(Block newH return errorResult; } - payloadId = _payloadPreparationService.StartPreparingPayload(newHeadBlock.Header, payloadAttributes); + payloadId = payloadPreparationService.StartPreparingPayload(newHeadBlock.Header, payloadAttributes); } _blockTree.ForkChoiceUpdated(forkchoiceState.FinalizedBlockHash, forkchoiceState.SafeBlockHash); @@ -338,7 +312,7 @@ private ResultWrapper StartBuildingPayload(Block newH private ResultWrapper? ValidateAttributes(PayloadAttributes? payloadAttributes, int version) { string? error = null; - return payloadAttributes?.Validate(_specProvider, version, out error) switch + return payloadAttributes?.Validate(specProvider, version, out error) switch { PayloadAttributesValidationResult.InvalidParams => ResultWrapper.Fail(error!, ErrorCodes.InvalidParams), @@ -352,11 +326,11 @@ private ResultWrapper StartBuildingPayload(Block newH private void StartNewBeaconHeaderSync(ForkchoiceStateV1 forkchoiceState, BlockHeader blockHeader, string requestStr) { - bool isSyncInitialized = _mergeSyncController.TryInitBeaconHeaderSync(blockHeader); - _beaconPivot.ProcessDestination = blockHeader; - _peerRefresher.RefreshPeers(blockHeader.Hash!, blockHeader.ParentHash!, forkchoiceState.FinalizedBlockHash); - _blockCacheService.FinalizedHash = forkchoiceState.FinalizedBlockHash; - _blockCacheService.HeadBlockHash = forkchoiceState.HeadBlockHash; + bool isSyncInitialized = mergeSyncController.TryInitBeaconHeaderSync(blockHeader); + beaconPivot.ProcessDestination = blockHeader; + peerRefresher.RefreshPeers(blockHeader.Hash!, blockHeader.ParentHash!, forkchoiceState.FinalizedBlockHash); + blockCacheService.FinalizedHash = forkchoiceState.FinalizedBlockHash; + blockCacheService.HeadBlockHash = forkchoiceState.HeadBlockHash; if (isSyncInitialized && _logger.IsInfo) _logger.Info($"Start a new sync process, Request: {requestStr}."); } @@ -434,8 +408,8 @@ private void ReorgBeaconChainDuringSync(Block newHeadBlock, BlockInfo newHeadBlo { if (_logger.IsInfo) _logger.Info("BeaconChain reorged during the sync or cache rebuilt"); BlockInfo[] beaconMainChainBranch = GetBeaconChainBranch(newHeadBlock, newHeadBlockInfo); - _blockTree.UpdateBeaconMainChain(beaconMainChainBranch, Math.Max(_beaconPivot.ProcessDestination?.Number ?? 0, newHeadBlock.Number)); - _beaconPivot.ProcessDestination = newHeadBlock.Header; + _blockTree.UpdateBeaconMainChain(beaconMainChainBranch, Math.Max(beaconPivot.ProcessDestination?.Number ?? 0, newHeadBlock.Number)); + beaconPivot.ProcessDestination = newHeadBlock.Header; } private BlockInfo[] GetBeaconChainBranch(Block newHeadBlock, BlockInfo newHeadBlockInfo) diff --git a/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs b/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs index e44fb9f259e..12289ff2140 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs @@ -290,6 +290,7 @@ public Task InitRpcModules() if (_api.TxPool is null) throw new ArgumentNullException(nameof(_api.TxPool)); if (_api.SpecProvider is null) throw new ArgumentNullException(nameof(_api.SpecProvider)); if (_api.StateReader is null) throw new ArgumentNullException(nameof(_api.StateReader)); + if (_api.WorldStateManager is null) throw new ArgumentNullException(nameof(_api.WorldStateManager)); if (_beaconPivot is null) throw new ArgumentNullException(nameof(_beaconPivot)); if (_beaconSync is null) throw new ArgumentNullException(nameof(_beaconSync)); if (_peerRefresher is null) throw new ArgumentNullException(nameof(_peerRefresher)); @@ -357,6 +358,7 @@ IBlockImprovementContextFactory CreateBlockImprovementContextFactory() _peerRefresher, _api.SpecProvider, _api.SyncPeerPool!, + _api.WorldStateManager.GlobalWorldState, _api.LogManager, _api.Config().SecondsPerSlot, _api.Config().SimulateBlockProduction), diff --git a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs index b0a87e678b6..388d5e33690 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs @@ -204,6 +204,7 @@ public async Task InitRpcModules() ArgumentNullException.ThrowIfNull(_api.RpcModuleProvider); ArgumentNullException.ThrowIfNull(_api.BlockProducer); ArgumentNullException.ThrowIfNull(_api.TxPool); + ArgumentNullException.ThrowIfNull(_api.WorldStateManager); ArgumentNullException.ThrowIfNull(_beaconSync); ArgumentNullException.ThrowIfNull(_beaconPivot); @@ -265,6 +266,7 @@ public async Task InitRpcModules() _peerRefresher, _api.SpecProvider, _api.SyncPeerPool!, + _api.WorldStateManager.GlobalWorldState, _api.LogManager, _api.Config().SecondsPerSlot, _api.Config().SimulateBlockProduction), diff --git a/src/Nethermind/Nethermind.Taiko.Test/TaikoEngineApiTests.cs b/src/Nethermind/Nethermind.Taiko.Test/TaikoEngineApiTests.cs index 1b556b9f597..33b418a9246 100644 --- a/src/Nethermind/Nethermind.Taiko.Test/TaikoEngineApiTests.cs +++ b/src/Nethermind/Nethermind.Taiko.Test/TaikoEngineApiTests.cs @@ -18,6 +18,7 @@ using Nethermind.Core.Test.Builders; using Nethermind.Merge.Plugin.Data; using Nethermind.JsonRpc; +using Nethermind.State; using Nethermind.Taiko.Rpc; namespace Nethermind.Taiko.Test; @@ -47,6 +48,7 @@ public async Task Test_ForkchoiceUpdatedHandler_Allows_UnknownFinalizedSafeBlock Substitute.For(), Substitute.For(), Substitute.For(), + Substitute.For(), Substitute.For(), 12UL, // secondsPerSlot false // simulateBlockProduction diff --git a/src/Nethermind/Nethermind.Taiko/Rpc/TaikoForkchoiceUpdatedHandler.cs b/src/Nethermind/Nethermind.Taiko/Rpc/TaikoForkchoiceUpdatedHandler.cs index b2db92a5700..96f3874ec47 100644 --- a/src/Nethermind/Nethermind.Taiko/Rpc/TaikoForkchoiceUpdatedHandler.cs +++ b/src/Nethermind/Nethermind.Taiko/Rpc/TaikoForkchoiceUpdatedHandler.cs @@ -18,6 +18,7 @@ using Nethermind.Merge.Plugin.Synchronization; using Nethermind.Synchronization.Peers; using System.Diagnostics.CodeAnalysis; +using Nethermind.State; namespace Nethermind.Taiko.Rpc; @@ -33,6 +34,7 @@ internal class TaikoForkchoiceUpdatedHandler(IBlockTree blockTree, IPeerRefresher peerRefresher, ISpecProvider specProvider, ISyncPeerPool syncPeerPool, + IWorldState globalWorldState, ILogManager logManager, ulong secondsPerSlot, bool simulateBlockProduction = false) : ForkchoiceUpdatedHandler(blockTree, @@ -47,6 +49,7 @@ internal class TaikoForkchoiceUpdatedHandler(IBlockTree blockTree, peerRefresher, specProvider, syncPeerPool, + globalWorldState, logManager, secondsPerSlot, simulateBlockProduction) diff --git a/src/Nethermind/Nethermind.Taiko/TaikoPlugin.cs b/src/Nethermind/Nethermind.Taiko/TaikoPlugin.cs index ab00de5eefd..5dd065f9072 100644 --- a/src/Nethermind/Nethermind.Taiko/TaikoPlugin.cs +++ b/src/Nethermind/Nethermind.Taiko/TaikoPlugin.cs @@ -230,6 +230,7 @@ public async Task InitRpcModules() _peerRefresher, _api.SpecProvider, _api.SyncPeerPool, + _api.WorldStateManager.GlobalWorldState, _api.LogManager, _api.Config().SecondsPerSlot, _api.Config().SimulateBlockProduction), From 0f849cf113e9331a49a170fcaa0233d703b3795d Mon Sep 17 00:00:00 2001 From: "lukasz.rozmej" Date: Mon, 3 Feb 2025 10:29:45 +0100 Subject: [PATCH 2/3] fix for not empty processing queue --- .../Handlers/ForkchoiceUpdatedHandler.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs index 920bc447953..b7519d0d4ff 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs @@ -232,7 +232,7 @@ protected virtual bool IsOnMainChainBehindHead(Block newHeadBlock, ForkchoiceSta if (shouldUpdateHead) { _blockTree.UpdateMainChain(blocks!, true, true); - globalWorldState.StateRoot = newHeadBlock.StateRoot!; + UpdateGlobalStateRoot(newHeadBlock.StateRoot!); } if (IsInconsistent(finalizedBlockHash)) @@ -264,6 +264,24 @@ protected virtual bool IsOnMainChainBehindHead(Block newHeadBlock, ForkchoiceSta return null; } + private void UpdateGlobalStateRoot(Hash256 stateRoot) + { + if (processingQueue.IsEmpty) + { + globalWorldState.StateRoot = stateRoot; + } + else + { + EventHandler handler = null!; + handler = (_, _) => + { + globalWorldState.StateRoot = stateRoot; + processingQueue.ProcessingQueueEmpty -= handler; + }; + processingQueue.ProcessingQueueEmpty += handler; + } + } + protected virtual bool IsPayloadAttributesTimestampValid(Block newHeadBlock, ForkchoiceStateV1 forkchoiceState, PayloadAttributes payloadAttributes, [NotNullWhen(false)] out ResultWrapper? errorResult) { From d15ad492d3a34d7d3abbdab72e1491fa4a2071dc Mon Sep 17 00:00:00 2001 From: "lukasz.rozmej" Date: Mon, 3 Feb 2025 10:36:43 +0100 Subject: [PATCH 3/3] Add BlockProcessingQueueExtensions.RunOnEmpty --- .../Processing/IBlockProcessingQueue.cs | 21 +++++++++++++++++++ .../Handlers/ForkchoiceUpdatedHandler.cs | 20 +----------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/IBlockProcessingQueue.cs b/src/Nethermind/Nethermind.Consensus/Processing/IBlockProcessingQueue.cs index a95e5f49448..8c7dea2fea2 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/IBlockProcessingQueue.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/IBlockProcessingQueue.cs @@ -34,4 +34,25 @@ public interface IBlockProcessingQueue public bool IsEmpty => Count == 0; } + + public static class BlockProcessingQueueExtensions + { + public static void RunOnEmpty(this IBlockProcessingQueue queue, Action action) + { + if (queue.IsEmpty) + { + action(); + } + else + { + EventHandler handler = null!; + handler = (_, _) => + { + action(); + queue.ProcessingQueueEmpty -= handler!; + }; + queue.ProcessingQueueEmpty += handler; + } + } + } } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs index b7519d0d4ff..a1961bdad3b 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs @@ -232,7 +232,7 @@ protected virtual bool IsOnMainChainBehindHead(Block newHeadBlock, ForkchoiceSta if (shouldUpdateHead) { _blockTree.UpdateMainChain(blocks!, true, true); - UpdateGlobalStateRoot(newHeadBlock.StateRoot!); + processingQueue.RunOnEmpty(() => globalWorldState.StateRoot = newHeadBlock.StateRoot!); } if (IsInconsistent(finalizedBlockHash)) @@ -264,24 +264,6 @@ protected virtual bool IsOnMainChainBehindHead(Block newHeadBlock, ForkchoiceSta return null; } - private void UpdateGlobalStateRoot(Hash256 stateRoot) - { - if (processingQueue.IsEmpty) - { - globalWorldState.StateRoot = stateRoot; - } - else - { - EventHandler handler = null!; - handler = (_, _) => - { - globalWorldState.StateRoot = stateRoot; - processingQueue.ProcessingQueueEmpty -= handler; - }; - processingQueue.ProcessingQueueEmpty += handler; - } - } - protected virtual bool IsPayloadAttributesTimestampValid(Block newHeadBlock, ForkchoiceStateV1 forkchoiceState, PayloadAttributes payloadAttributes, [NotNullWhen(false)] out ResultWrapper? errorResult) {