diff --git a/src/Nethermind/Nethermind.Api/IApiWithNetwork.cs b/src/Nethermind/Nethermind.Api/IApiWithNetwork.cs index f26e0877cb2e..ee26b6d3128a 100644 --- a/src/Nethermind/Nethermind.Api/IApiWithNetwork.cs +++ b/src/Nethermind/Nethermind.Api/IApiWithNetwork.cs @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using Nethermind.Consensus; using Nethermind.Core; using Nethermind.JsonRpc; using Nethermind.JsonRpc.Modules; @@ -19,7 +18,6 @@ public interface IApiWithNetwork : IApiWithBlockchain IIPResolver IpResolver { get; } IMessageSerializationService MessageSerializationService { get; } - IGossipPolicy GossipPolicy { get; set; } IPeerManager? PeerManager { get; } IProtocolsManager? ProtocolsManager { get; set; } IProtocolValidator ProtocolValidator { get; } diff --git a/src/Nethermind/Nethermind.Api/NethermindApi.cs b/src/Nethermind/Nethermind.Api/NethermindApi.cs index a3aa79b2062a..e11c1d881c4c 100644 --- a/src/Nethermind/Nethermind.Api/NethermindApi.cs +++ b/src/Nethermind/Nethermind.Api/NethermindApi.cs @@ -80,7 +80,6 @@ ILifetimeScope Context public IKeyStore? KeyStore { get; set; } public ILogManager LogManager => _dependencies.LogManager; public IMessageSerializationService MessageSerializationService => Context.Resolve(); - public IGossipPolicy GossipPolicy { get; set; } = Policy.FullGossip; public IPeerManager? PeerManager => Context.Resolve(); public IProtocolsManager? ProtocolsManager { get; set; } public IProtocolValidator ProtocolValidator => Context.Resolve(); diff --git a/src/Nethermind/Nethermind.Core.Test/Modules/TestMergeModule.cs b/src/Nethermind/Nethermind.Core.Test/Modules/TestMergeModule.cs index ed9ea9ec127e..3182d4d7b5ff 100644 --- a/src/Nethermind/Nethermind.Core.Test/Modules/TestMergeModule.cs +++ b/src/Nethermind/Nethermind.Core.Test/Modules/TestMergeModule.cs @@ -32,8 +32,6 @@ protected override void Load(ContainerBuilder builder) .AddDecorator() - // Validators - .AddDecorator() .AddSingleton() .AddDecorator() diff --git a/src/Nethermind/Nethermind.Init/Modules/NetworkModule.cs b/src/Nethermind/Nethermind.Init/Modules/NetworkModule.cs index bb840927db66..bf8941035686 100644 --- a/src/Nethermind/Nethermind.Init/Modules/NetworkModule.cs +++ b/src/Nethermind/Nethermind.Init/Modules/NetworkModule.cs @@ -5,6 +5,7 @@ using Autofac; using Nethermind.Blockchain.Synchronization; using Nethermind.Config; +using Nethermind.Consensus; using Nethermind.Core; using Nethermind.Core.Container; using Nethermind.Core.Crypto; @@ -47,6 +48,7 @@ protected override void Load(ContainerBuilder builder) .AddCompositeOrderedComponents(singleInstance: true) .AddSingleton() .AddSingleton() + .AddSingleton(Policy.FullGossip) // Rlpxhost .AddSingleton() diff --git a/src/Nethermind/Nethermind.Merge.AuRa/AuRaMergePlugin.cs b/src/Nethermind/Nethermind.Merge.AuRa/AuRaMergePlugin.cs index d66e86961909..8c6e76fa6ae3 100644 --- a/src/Nethermind/Nethermind.Merge.AuRa/AuRaMergePlugin.cs +++ b/src/Nethermind/Nethermind.Merge.AuRa/AuRaMergePlugin.cs @@ -101,6 +101,7 @@ protected override void Load(ContainerBuilder builder) => builder .AddDecorator() .AddDecorator() .AddDecorator() + .AddDecorator() // Merge-aware override: skips wiring the branch processor on post-merge chains so // the AuRa finalization manager's startup catch-up walk never runs. diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/BlockCacheServiceTest.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/BlockCacheServiceTest.cs index bb18a3d65af4..161898b8aed2 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/BlockCacheServiceTest.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/BlockCacheServiceTest.cs @@ -1,11 +1,14 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Test.Builders; using Nethermind.Crypto; using Nethermind.Merge.Plugin.Handlers; +using Nethermind.Synchronization; +using NSubstitute; using NUnit.Framework; namespace Nethermind.Merge.Plugin.Test; @@ -37,15 +40,13 @@ public void prunes_highest_unprotected_block_and_returns_false_when_added_block_ [Test] public void preserves_head_and_finalized_blocks_when_pruning() { - BlockCacheService blockCacheService = new(2); Block finalizedBlock = Build.A.Block.WithNumber(1).TestObject; Block block2 = Build.A.Block.WithNumber(2).TestObject; Block headBlock = Build.A.Block.WithNumber(3).TestObject; Hash256 finalizedHash = finalizedBlock.GetOrCalculateHash(); Hash256 block2Hash = block2.GetOrCalculateHash(); Hash256 headHash = headBlock.GetOrCalculateHash(); - blockCacheService.FinalizedHash = finalizedHash; - blockCacheService.HeadBlockHash = headHash; + BlockCacheService blockCacheService = new(2, ProtectingStrategy(finalizedHash, headHash)); Assert.That(blockCacheService.TryAddBlock(finalizedBlock), Is.True); Assert.That(blockCacheService.TryAddBlock(block2), Is.True); @@ -60,13 +61,11 @@ public void preserves_head_and_finalized_blocks_when_pruning() [Test] public void preserves_protected_blocks_when_all_candidates_are_protected() { - BlockCacheService blockCacheService = new(1); Block finalizedBlock = Build.A.Block.WithNumber(1).TestObject; Block headBlock = Build.A.Block.WithNumber(2).TestObject; Hash256 finalizedHash = finalizedBlock.GetOrCalculateHash(); Hash256 headHash = headBlock.GetOrCalculateHash(); - blockCacheService.FinalizedHash = finalizedHash; - blockCacheService.HeadBlockHash = headHash; + BlockCacheService blockCacheService = new(1, ProtectingStrategy(finalizedHash, headHash)); Assert.That(blockCacheService.TryAddBlock(finalizedBlock), Is.True); Assert.That(blockCacheService.TryAddBlock(headBlock), Is.True); @@ -75,4 +74,26 @@ public void preserves_protected_blocks_when_all_candidates_are_protected() Assert.That(blockCacheService.BlockCache.ContainsKey(finalizedHash), Is.True); Assert.That(blockCacheService.BlockCache.ContainsKey(headHash), Is.True); } + + [Test] + public void clears_cache_when_beacon_sync_stops() + { + IBeaconSyncStrategy beaconSyncStrategy = Substitute.For(); + BlockCacheService blockCacheService = new(4, beaconSyncStrategy); + blockCacheService.TryAddBlock(Build.A.Block.WithNumber(1).TestObject); + blockCacheService.TryAddBlock(Build.A.Block.WithNumber(2).TestObject); + Assert.That(blockCacheService.BlockCache, Has.Count.EqualTo(2)); + + beaconSyncStrategy.BeaconSyncStopped += Raise.Event(); + + Assert.That(blockCacheService.BlockCache, Is.Empty); + } + + private static IBeaconSyncStrategy ProtectingStrategy(Hash256 finalizedHash, Hash256 headHash) + { + IBeaconSyncStrategy beaconSyncStrategy = Substitute.For(); + beaconSyncStrategy.GetFinalizedHash().Returns(finalizedHash); + beaconSyncStrategy.GetHeadBlockHash().Returns(headHash); + return beaconSyncStrategy; + } } diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/MergeFinalizedStateProviderTests.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/MergeFinalizedStateProviderTests.cs index e356edce806a..93d659220cb6 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/MergeFinalizedStateProviderTests.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/MergeFinalizedStateProviderTests.cs @@ -7,7 +7,7 @@ using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Test.Builders; -using Nethermind.Merge.Plugin.Handlers; +using Nethermind.Synchronization; using Nethermind.Trie.Pruning; using NSubstitute; using NUnit.Framework; @@ -22,7 +22,7 @@ public class MergeFinalizedStateProviderTests private IBlockTree _blockTree = null!; private IFinalizedStateProvider _baseFinalizedStateProvider = null!; private MergeFinalizedStateProvider _provider = null!; - private IBlockCacheService _blockCacheService; + private IBeaconSyncStrategy _beaconSyncStrategy; [SetUp] public void Setup() @@ -30,8 +30,8 @@ public void Setup() _poSSwitcher = Substitute.For(); _blockTree = Substitute.For(); _baseFinalizedStateProvider = Substitute.For(); - _blockCacheService = Substitute.For(); - _provider = new MergeFinalizedStateProvider(_poSSwitcher, _blockCacheService, _blockTree, _baseFinalizedStateProvider); + _beaconSyncStrategy = Substitute.For(); + _provider = new MergeFinalizedStateProvider(_poSSwitcher, _beaconSyncStrategy, _blockTree, _baseFinalizedStateProvider); } [Test] @@ -79,7 +79,7 @@ public void FinalizedBlockNumber_AfterTransition_WithBlockCacheFinalizedHash_Ret BlockHeader finalizedHeader = Build.A.BlockHeader.WithNumber(expectedBlockNumber).WithHash(finalizedHash).TestObject; _poSSwitcher.TransitionFinished.Returns(true); _blockTree.FinalizedHash.Returns((Hash256?)null); - _blockCacheService.FinalizedHash.Returns(finalizedHash); + _beaconSyncStrategy.GetFinalizedHash().Returns(finalizedHash); _blockTree.FindHeader(finalizedHash).Returns(finalizedHeader); // Act @@ -104,7 +104,7 @@ public void FinalizedBlockNumber_AfterTransition_BlockCacheHasHigherNumber_Retur _poSSwitcher.TransitionFinished.Returns(true); _blockTree.FinalizedHash.Returns(blockTreeHash); _blockTree.FindHeader(blockTreeHash, BlockTreeLookupOptions.None).Returns(blockTreeHeader); - _blockCacheService.FinalizedHash.Returns(blockCacheHash); + _beaconSyncStrategy.GetFinalizedHash().Returns(blockCacheHash); _blockTree.FindHeader(blockCacheHash).Returns(blockCacheHeader); // Act @@ -128,7 +128,7 @@ public void FinalizedBlockNumber_AfterTransition_BlockTreeHasHigherNumber_Return _poSSwitcher.TransitionFinished.Returns(true); _blockTree.FinalizedHash.Returns(blockTreeHash); _blockTree.FindHeader(blockTreeHash, BlockTreeLookupOptions.None).Returns(blockTreeHeader); - _blockCacheService.FinalizedHash.Returns(blockCacheHash); + _beaconSyncStrategy.GetFinalizedHash().Returns(blockCacheHash); _blockTree.FindHeader(blockCacheHash).Returns(blockCacheHeader); // Act @@ -150,7 +150,7 @@ public void FinalizedBlockNumber_AfterTransition_BlockCacheHeaderNotFound_UsesOn _poSSwitcher.TransitionFinished.Returns(true); _blockTree.FinalizedHash.Returns(blockTreeHash); _blockTree.FindHeader(blockTreeHash, BlockTreeLookupOptions.None).Returns(blockTreeHeader); - _blockCacheService.FinalizedHash.Returns(blockCacheHash); + _beaconSyncStrategy.GetFinalizedHash().Returns(blockCacheHash); _blockTree.FindHeader(blockCacheHash).Returns((BlockHeader?)null); // Act @@ -167,7 +167,7 @@ public void FinalizedBlockNumber_AfterTransition_NoFinalizedHeaders_DelegatesToB long expectedBlockNumber = 150; _poSSwitcher.TransitionFinished.Returns(true); _blockTree.FinalizedHash.Returns((Hash256?)null); - _blockCacheService.FinalizedHash.Returns((Hash256?)null); + _beaconSyncStrategy.GetFinalizedHash().Returns((Hash256?)null); _baseFinalizedStateProvider.FinalizedBlockNumber.Returns(expectedBlockNumber); // Act diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/MergePluginTests.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/MergePluginTests.cs index fd76d9db3917..1f41669e5813 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/MergePluginTests.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/MergePluginTests.cs @@ -192,7 +192,7 @@ public async Task Initializes_correctly() await _plugin.InitNetworkProtocol(); ISyncConfig syncConfig = api.Config(); Assert.That(syncConfig.NetworkingEnabled, Is.True); - Assert.That(api.GossipPolicy.CanGossipBlocks, Is.True); + Assert.That(container.Resolve().CanGossipBlocks, Is.True); IBlockProducer blockProducer = container.Resolve().InitBlockProducer(); Assert.That(blockProducer, Is.InstanceOf()); } diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/Synchronization/BeaconHeadersSyncTests.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/Synchronization/BeaconHeadersSyncTests.cs index 3730479f13ab..0f2bb769c58e 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/Synchronization/BeaconHeadersSyncTests.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/Synchronization/BeaconHeadersSyncTests.cs @@ -69,7 +69,7 @@ public IBeaconPivot BeaconPivot } private BeaconSync? _beaconSync; - public BeaconSync BeaconSync => _beaconSync ??= new(BeaconPivot, BlockTree, SyncConfig, BlockCacheService, PoSSwitcher, LimboLogs.Instance); + public BeaconSync BeaconSync => _beaconSync ??= new(BeaconPivot, BlockTree, SyncConfig, PoSSwitcher, LimboLogs.Instance); private IDb? _metadataDb; public IDb MetadataDb => _metadataDb ??= new MemDb(); diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/BlockCacheService.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/BlockCacheService.cs index 406141333033..8908fdf679c6 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/BlockCacheService.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/BlockCacheService.cs @@ -7,6 +7,7 @@ using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Crypto; +using Nethermind.Synchronization; namespace Nethermind.Merge.Plugin.Handlers; @@ -16,13 +17,15 @@ namespace Nethermind.Merge.Plugin.Handlers; public class BlockCacheService : IBlockCacheService { private readonly int _maxCachedBlocks; + private readonly IBeaconSyncStrategy _beaconSyncStrategy; private readonly ConcurrentDictionary _blockCache = new(); /// /// Initializes a block cache with the default merge sync bound. /// - public BlockCacheService() - : this((int)(Reorganization.MaxDepth * 2 + 16)) + /// Source of the finalized/head hashes protected from pruning. Defaults to . + public BlockCacheService(IBeaconSyncStrategy? beaconSyncStrategy = null) + : this((int)(Reorganization.MaxDepth * 2 + 16), beaconSyncStrategy) { } @@ -30,21 +33,18 @@ public BlockCacheService() /// Initializes a block cache with the provided maximum number of cached blocks. /// /// Maximum number of cached blocks before pruning unprotected entries. - public BlockCacheService(int maxCachedBlocks) + /// Source of the finalized/head hashes protected from pruning. Defaults to . + public BlockCacheService(int maxCachedBlocks, IBeaconSyncStrategy? beaconSyncStrategy = null) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(maxCachedBlocks); _maxCachedBlocks = maxCachedBlocks; + _beaconSyncStrategy = beaconSyncStrategy ?? No.BeaconSync; + _beaconSyncStrategy.BeaconSyncStopped += Clear; } /// public IReadOnlyDictionary BlockCache => _blockCache; - /// - public Hash256? FinalizedHash { get; set; } - - /// - public Hash256? HeadBlockHash { get; set; } - /// public bool TryAddBlock(Block block) { @@ -101,5 +101,5 @@ private bool TryGetHighestNumberedUnprotectedBlock(out Hash256AsKey blockHash) } private bool IsProtected(Hash256AsKey blockHash) => - Equals(blockHash.Value, FinalizedHash) || Equals(blockHash.Value, HeadBlockHash); + Equals(blockHash.Value, _beaconSyncStrategy.GetFinalizedHash()) || Equals(blockHash.Value, _beaconSyncStrategy.GetHeadBlockHash()); } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs index d8b155d955b7..e6220408d339 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/ForkchoiceUpdatedHandler.cs @@ -197,8 +197,7 @@ protected virtual bool IsOnMainChainBehindFinalized(BlockHeader newHeadHeader, F if (processingQueueCount == 0) { peerRefresher.RefreshPeers(newHeadHeader.Hash!, newHeadHeader.ParentHash!, finalizedBlockHash); - blockCacheService.FinalizedHash = finalizedBlockHash; - blockCacheService.HeadBlockHash = forkchoiceState.HeadBlockHash; + mergeSyncController.SetForkchoiceHashes(finalizedBlockHash, forkchoiceState.HeadBlockHash); mergeSyncController.StopBeaconModeControl(); // Debug as already output in Received ForkChoice @@ -336,8 +335,7 @@ private void StartNewBeaconHeaderSync(ForkchoiceStateV1 forkchoiceState, BlockHe mergeSyncController.InitBeaconHeaderSync(blockHeader); beaconPivot.ProcessDestination = blockHeader; peerRefresher.RefreshPeers(blockHeader.Hash!, blockHeader.ParentHash!, forkchoiceState.FinalizedBlockHash); - blockCacheService.FinalizedHash = forkchoiceState.FinalizedBlockHash; - blockCacheService.HeadBlockHash = forkchoiceState.HeadBlockHash; + mergeSyncController.SetForkchoiceHashes(forkchoiceState.FinalizedBlockHash, forkchoiceState.HeadBlockHash); if (_logger.IsInfo) _logger.Info($"Start a new sync process, Request: {requestStr}."); } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/IBlockCacheService.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/IBlockCacheService.cs index fc3146b978eb..5760a97a498f 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/IBlockCacheService.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/IBlockCacheService.cs @@ -14,16 +14,6 @@ public interface IBlockCacheService /// public IReadOnlyDictionary BlockCache { get; } - /// - /// Finalized block hash protected from cache pruning. - /// - Hash256? FinalizedHash { get; set; } - - /// - /// Head block hash protected from cache pruning. - /// - Hash256? HeadBlockHash { get; set; } - /// /// Adds a block to the bounded cache. /// diff --git a/src/Nethermind/Nethermind.Merge.Plugin/MergeFinalizedStateProvider.cs b/src/Nethermind/Nethermind.Merge.Plugin/MergeFinalizedStateProvider.cs index f2484cc65761..e5418ca9002c 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/MergeFinalizedStateProvider.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/MergeFinalizedStateProvider.cs @@ -5,12 +5,12 @@ using Nethermind.Consensus; using Nethermind.Core; using Nethermind.Core.Crypto; -using Nethermind.Merge.Plugin.Handlers; +using Nethermind.Synchronization; using Nethermind.Trie.Pruning; namespace Nethermind.Merge.Plugin; -public class MergeFinalizedStateProvider(IPoSSwitcher poSSwitcher, IBlockCacheService blockCacheService, IBlockTree blockTree, IFinalizedStateProvider baseFinalizedStateProvider) : IFinalizedStateProvider +public class MergeFinalizedStateProvider(IPoSSwitcher poSSwitcher, IBeaconSyncStrategy beaconSyncStrategy, IBlockTree blockTree, IFinalizedStateProvider baseFinalizedStateProvider) : IFinalizedStateProvider { public long FinalizedBlockNumber { @@ -25,10 +25,10 @@ public long FinalizedBlockNumber } // Finalized hash from blocktree is not updated until it is processed, which is a problem for long - // catchup. So we use from blockCacheService as a backup. - if (blockCacheService.FinalizedHash is { } blockCacheFinalizedHash) + // catchup. So we use the beacon sync strategy's finalized hash as a backup. + if (beaconSyncStrategy.GetFinalizedHash() is { } beaconFinalizedHash) { - BlockHeader? fromBlockCache = blockTree.FindHeader(blockCacheFinalizedHash); + BlockHeader? fromBlockCache = blockTree.FindHeader(beaconFinalizedHash); if (fromBlockCache is not null) { if (currentFinalized is null || fromBlockCache.Number > currentFinalized.Number) diff --git a/src/Nethermind/Nethermind.Merge.Plugin/MergeGossipPolicy.cs b/src/Nethermind/Nethermind.Merge.Plugin/MergeGossipPolicy.cs index 00908ae7f0df..e33205bc070f 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/MergeGossipPolicy.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/MergeGossipPolicy.cs @@ -1,22 +1,21 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; using Nethermind.Consensus; using Nethermind.Core; using Nethermind.Core.Crypto; -using Nethermind.Merge.Plugin.Handlers; +using Nethermind.Synchronization; namespace Nethermind.Merge.Plugin { public class MergeGossipPolicy( - IGossipPolicy? apiGossipPolicy, - IPoSSwitcher? poSSwitcher, - IBlockCacheService blockCacheService) : IGossipPolicy + IGossipPolicy apiGossipPolicy, + IPoSSwitcher poSSwitcher, + IBeaconSyncStrategy beaconSyncStrategy) : IGossipPolicy { - private readonly IGossipPolicy _preMergeGossipPolicy = apiGossipPolicy ?? throw new ArgumentNullException(nameof(apiGossipPolicy)); - private readonly IPoSSwitcher _poSSwitcher = poSSwitcher ?? throw new ArgumentNullException(nameof(poSSwitcher)); - private readonly IBlockCacheService _blockCacheService = blockCacheService ?? throw new ArgumentNullException(nameof(blockCacheService)); + private readonly IGossipPolicy _preMergeGossipPolicy = apiGossipPolicy; + private readonly IPoSSwitcher _poSSwitcher = poSSwitcher; + private readonly IBeaconSyncStrategy _beaconSyncStrategy = beaconSyncStrategy; // According to spec (https://github.com/ethereum/EIPs/blob/d896145678bd65d3eafd8749690c1b5228875c39/EIPS/eip-3675.md#network) // We SHOULD NOT advertise the descendant of any terminal PoW block. @@ -29,10 +28,10 @@ public class MergeGossipPolicy( // According to spec (https://github.com/ethereum/EIPs/blob/d896145678bd65d3eafd8749690c1b5228875c39/EIPS/eip-3675.md#network) // We MUST discard NewBlock/NewBlockHash messages after receiving FIRST_FINALIZED_BLOCK. public bool ShouldDiscardBlocks => _poSSwitcher.TransitionFinished || - (_blockCacheService.FinalizedHash is not null && _blockCacheService.FinalizedHash != Keccak.Zero); /* _blockCacheService.FinalizedHash is not null && _blockCacheService.FinalizedHash != Keccak.Zero + (_beaconSyncStrategy.GetFinalizedHash() is { } finalizedHash && finalizedHash != Keccak.Zero); /* _beaconSyncStrategy.GetFinalizedHash() is not null && _beaconSyncStrategy.GetFinalizedHash() != Keccak.Zero This condition was added for edge case situation. We started beacon sync, and we hadn't reached transition yet. If CL sent us non zero finalization hash, it would mean that network reached transition. - However, in edge case situation (verified by merge hive tests), our node needs to be reorged to PoW again, so we can't add this condition _blockCacheService.FinalizedHash != Keccak.Zero + However, in edge case situation (verified by merge hive tests), our node needs to be reorged to PoW again, so we can't add this condition _beaconSyncStrategy.GetFinalizedHash() != Keccak.Zero to PoSSwitcher.TransitionFinished. On the other hand, we don't want to receive any blocks from the network, so we want to discard blocks. */ // According to spec (https://github.com/ethereum/EIPs/blob/d896145678bd65d3eafd8749690c1b5228875c39/EIPS/eip-3675.md#network) diff --git a/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs b/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs index 5a03dc4774df..315d8019043b 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs @@ -54,7 +54,6 @@ public class MergePlugin(ChainSpec chainSpec, IMergeConfig mergeConfig) : INethe protected IBlocksConfig _blocksConfig = null!; protected ITxPoolConfig _txPoolConfig = null!; protected IPoSSwitcher _poSSwitcher = NoPoS.Instance; - private IBlockCacheService _blockCacheService = null!; private InvalidChainTracker.InvalidChainTracker _invalidChainTracker = null!; private IMergeBlockProductionPolicy? _mergeBlockProductionPolicy; @@ -89,7 +88,6 @@ public virtual Task Init(INethermindApi nethermindApi) EnsureJsonRpcUrl(); - _blockCacheService = _api.Context.Resolve(); _poSSwitcher = _api.Context.Resolve(); _invalidChainTracker = _api.Context.Resolve(); if (_txPoolConfig.BlobsSupport.SupportsReorgs()) @@ -98,8 +96,6 @@ public virtual Task Init(INethermindApi nethermindApi) _api.DisposeStack.Push(processedTransactionsDbCleaner); } - _api.GossipPolicy = new MergeGossipPolicy(_api.GossipPolicy, _poSSwitcher, _blockCacheService); - _api.BlockPreprocessor.AddFirst(new MergeProcessingRecoveryStep(_poSSwitcher)); } @@ -246,6 +242,7 @@ protected override void Load(ContainerBuilder builder) => builder .AddDecorator() .AddDecorator() .AddDecorator() + .AddDecorator() .AddSingleton() .AddSingleton( diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/BeaconSync.cs b/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/BeaconSync.cs index 9683a48b7734..c90ed7d88ea8 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/BeaconSync.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/BeaconSync.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using Nethermind.Blockchain; using Nethermind.Blockchain.Synchronization; using Nethermind.Consensus; @@ -8,7 +9,6 @@ using Nethermind.Core.Crypto; using Nethermind.Crypto; using Nethermind.Logging; -using Nethermind.Merge.Plugin.Handlers; using Nethermind.Synchronization; namespace Nethermind.Merge.Plugin.Synchronization @@ -17,24 +17,27 @@ public class BeaconSync( IBeaconPivot beaconPivot, IBlockTree blockTree, ISyncConfig syncConfig, - IBlockCacheService blockCacheService, IPoSSwitcher poSSwitcher, ILogManager logManager) : IMergeSyncController, IBeaconSyncStrategy { private readonly IBeaconPivot _beaconPivot = beaconPivot; private readonly IBlockTree _blockTree = blockTree; private readonly ISyncConfig _syncConfig = syncConfig; - private readonly IBlockCacheService _blockCacheService = blockCacheService; private readonly IPoSSwitcher _poSSwitcher = poSSwitcher; private bool _isInBeaconModeControl = false; + private volatile Hash256? _finalizedHash; + private volatile Hash256? _headBlockHash; private readonly ILogger _logger = logManager.GetClassLogger(); + /// + public event Action? BeaconSyncStopped; + public void StopSyncing() { if (!_isInBeaconModeControl) { _beaconPivot.RemoveBeaconPivot(); - _blockCacheService.Clear(); + BeaconSyncStopped?.Invoke(); } _isInBeaconModeControl = true; @@ -107,9 +110,15 @@ lowestInsertedBeaconHeader is not null && return null; } - public Hash256? GetFinalizedHash() => _blockCacheService.FinalizedHash; + public Hash256? GetFinalizedHash() => _finalizedHash; + + public Hash256? GetHeadBlockHash() => _headBlockHash; - public Hash256? GetHeadBlockHash() => _blockCacheService.HeadBlockHash; + public void SetForkchoiceHashes(Hash256? finalizedHash, Hash256? headBlockHash) + { + _finalizedHash = finalizedHash; + _headBlockHash = headBlockHash; + } } public interface IMergeSyncController @@ -119,5 +128,7 @@ public interface IMergeSyncController void InitBeaconHeaderSync(BlockHeader blockHeader); void StopBeaconModeControl(); + + void SetForkchoiceHashes(Hash256? finalizedHash, Hash256? headBlockHash); } } diff --git a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs index d6c5a8d9b1ed..15680a5c0b02 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs @@ -77,8 +77,6 @@ public Task Init(INethermindApi api) ArgumentNullException.ThrowIfNull(_api.SpecProvider); - _api.GossipPolicy = ShouldNotGossip.Instance; - _api.BlockPreprocessor.AddFirst(new MergeProcessingRecoveryStep(_api.Context.Resolve())); return Task.CompletedTask; @@ -210,6 +208,8 @@ protected override void Load(ContainerBuilder builder) .AddSingleton() .AddSingleton(Always.Valid) + .AddSingleton(ShouldNotGossip.Instance) + // Block processing .AddScoped() .AddScoped() diff --git a/src/Nethermind/Nethermind.Synchronization.Test/E2ESyncTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/E2ESyncTests.cs index b3b1117872ec..947f1468816c 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/E2ESyncTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/E2ESyncTests.cs @@ -814,7 +814,7 @@ public async Task SyncUntilFinished(IContainer server, CancellationToken cancell Block headBlock = otherBlockTree.Head!; blockCacheService.TryAddBlock(finalizedBlock); blockCacheService.TryAddBlock(headBlock); - blockCacheService.FinalizedHash = finalizedBlock.Hash!; + mergeSyncController.SetForkchoiceHashes(finalizedBlock.Hash!, headBlock.Hash!); await preMergeTestEnv.WaitForSyncMode(mode => mode != SyncMode.UpdatingPivot, cancellationToken); mergeSyncController.InitBeaconHeaderSync(headBlock.Header); diff --git a/src/Nethermind/Nethermind.Synchronization/IBeaconSyncStrategy.cs b/src/Nethermind/Nethermind.Synchronization/IBeaconSyncStrategy.cs index 51b78b1c351f..3f49890b0a02 100644 --- a/src/Nethermind/Nethermind.Synchronization/IBeaconSyncStrategy.cs +++ b/src/Nethermind/Nethermind.Synchronization/IBeaconSyncStrategy.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using Nethermind.Core; using Nethermind.Core.Crypto; @@ -21,6 +22,7 @@ private No() { } public long? GetTargetBlockHeight() => null; public Hash256? GetFinalizedHash() => null; public Hash256? GetHeadBlockHash() => null; + public event Action? BeaconSyncStopped { add { } remove { } } } public interface IBeaconSyncStrategy @@ -34,5 +36,11 @@ public interface IBeaconSyncStrategy public long? GetTargetBlockHeight(); public Hash256? GetFinalizedHash(); public Hash256? GetHeadBlockHash(); + + /// + /// Raised when beacon sync stops and the beacon pivot is removed, signalling that cached payload blocks + /// may be discarded. + /// + event Action? BeaconSyncStopped; } } diff --git a/src/Nethermind/Nethermind.Taiko/TaikoBeaconHeadAdvancer.cs b/src/Nethermind/Nethermind.Taiko/TaikoBeaconHeadAdvancer.cs index 5610e0d1a9e1..4d53b90d8d44 100644 --- a/src/Nethermind/Nethermind.Taiko/TaikoBeaconHeadAdvancer.cs +++ b/src/Nethermind/Nethermind.Taiko/TaikoBeaconHeadAdvancer.cs @@ -9,7 +9,7 @@ using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Logging; -using Nethermind.Merge.Plugin.Handlers; +using Nethermind.Synchronization; namespace Nethermind.Taiko; @@ -26,7 +26,7 @@ namespace Nethermind.Taiko; /// taiko-geth (and alethia-reth) avoid this by advancing the canonical head pointer as part of /// InsertChain(setHead=true) inside their downloader — the EL takes responsibility for its /// own canonical head during sync. This class restores the same property in the Nethermind -/// architecture without touching core: it watches for the cached +/// architecture without touching core: it watches for the /// to become processed, then enqueues every missing ancestor for processing and drives the /// resulting TryUpdateMainChain sequence to advance Head all the way to that hash. /// @@ -36,7 +36,7 @@ public sealed class TaikoBeaconHeadAdvancer : IDisposable private static readonly TimeSpan PollInterval = TimeSpan.FromSeconds(3); private readonly IBlockTree _blockTree; - private readonly IBlockCacheService _blockCacheService; + private readonly IBeaconSyncStrategy _beaconSyncStrategy; private readonly Lazy _processingQueue; private readonly ILogger _logger; private readonly CancellationTokenSource _cts = new(); @@ -51,12 +51,12 @@ public sealed class TaikoBeaconHeadAdvancer : IDisposable /// public TaikoBeaconHeadAdvancer( IBlockTree blockTree, - IBlockCacheService blockCacheService, + IBeaconSyncStrategy beaconSyncStrategy, Lazy processingQueue, ILogManager logManager) { _blockTree = blockTree; - _blockCacheService = blockCacheService; + _beaconSyncStrategy = beaconSyncStrategy; _processingQueue = processingQueue; _logger = logManager.GetClassLogger(); @@ -105,7 +105,7 @@ private async Task TryAdvanceOnceAsync(CancellationToken ct) Block? target = null; long bestNumber = -1L; - Hash256? cachedHash = _blockCacheService.HeadBlockHash; + Hash256? cachedHash = _beaconSyncStrategy.GetHeadBlockHash(); if (cachedHash is not null && cachedHash != Keccak.Zero) { Block? cached = _blockTree.FindBlock(cachedHash, BlockTreeLookupOptions.DoNotCreateLevelIfMissing); diff --git a/src/Nethermind/Nethermind.Taiko/TaikoBeaconSync.cs b/src/Nethermind/Nethermind.Taiko/TaikoBeaconSync.cs index 68732eb41664..72b606b7a224 100644 --- a/src/Nethermind/Nethermind.Taiko/TaikoBeaconSync.cs +++ b/src/Nethermind/Nethermind.Taiko/TaikoBeaconSync.cs @@ -86,4 +86,11 @@ public bool ShouldBeInBeaconHeaders() /// public Hash256? GetHeadBlockHash() => inner.GetHeadBlockHash(); + + /// + public event Action? BeaconSyncStopped + { + add => inner.BeaconSyncStopped += value; + remove => inner.BeaconSyncStopped -= value; + } } diff --git a/src/Nethermind/Nethermind.Taiko/TaikoPlugin.cs b/src/Nethermind/Nethermind.Taiko/TaikoPlugin.cs index 33e250b07951..6b09ebd712aa 100644 --- a/src/Nethermind/Nethermind.Taiko/TaikoPlugin.cs +++ b/src/Nethermind/Nethermind.Taiko/TaikoPlugin.cs @@ -59,8 +59,6 @@ public Task Init(INethermindApi api) { _api = (TaikoNethermindApi)api; - _api.GossipPolicy = ShouldNotGossip.Instance; - _api.BlockPreprocessor.AddFirst(new MergeProcessingRecoveryStep(_api.Context.Resolve())); InitializeL1Precompiles(); @@ -160,6 +158,8 @@ protected override void Load(ContainerBuilder builder) .AddSingleton() .AddSingleton(Always.Valid) + .AddSingleton(ShouldNotGossip.Instance) + // Block processing .AddSingleton() .AddSingleton() diff --git a/src/Nethermind/Nethermind.Xdc/XdcBeaconSyncStrategy.cs b/src/Nethermind/Nethermind.Xdc/XdcBeaconSyncStrategy.cs index 446c30463466..feb593ae0916 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcBeaconSyncStrategy.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcBeaconSyncStrategy.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using Nethermind.Blockchain.Synchronization; using Nethermind.Core; using Nethermind.Core.Crypto; @@ -25,5 +26,7 @@ public class XdcBeaconSyncStrategy(ISyncConfig syncConfig) : IBeaconSyncStrategy public Hash256? GetFinalizedHash() => null; public Hash256? GetHeadBlockHash() => null; + + public event Action? BeaconSyncStopped { add { } remove { } } }