From b73cd38fa928896dda072347f82bfd1d30a97ecd Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 23 Oct 2024 12:36:40 -0300 Subject: [PATCH 1/5] Extract `TotalDifficulty` calculation - No behavior changes --- .../Synchronization/BeaconHeadersSyncFeed.cs | 10 ++--- .../FastBlocks/FastHeadersSyncTests.cs | 2 +- .../TotalDifficultyStrategyTests.cs | 43 +++++++++++++++++++ .../FastBlocks/FastHeadersSyncFeed.cs | 12 ++++-- .../ITotalDifficultyStrategy.cs | 20 +++++++++ 5 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 src/Nethermind/Nethermind.Synchronization.Test/TotalDifficultyStrategyTests.cs create mode 100644 src/Nethermind/Nethermind.Synchronization/ITotalDifficultyStrategy.cs diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/BeaconHeadersSyncFeed.cs b/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/BeaconHeadersSyncFeed.cs index 4820f52be48..44f16fcbd82 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/BeaconHeadersSyncFeed.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/BeaconHeadersSyncFeed.cs @@ -51,7 +51,7 @@ public BeaconHeadersSyncFeed( IMergeConfig? mergeConfig, IInvalidChainTracker invalidChainTracker, ILogManager logManager) - : base(blockTree, syncPeerPool, syncConfig, syncReport, logManager, true) // alwaysStartHeaderSync = true => for the merge we're forcing header sync start. It doesn't matter if it is archive sync or fast sync + : base(blockTree, syncPeerPool, syncConfig, syncReport, logManager, alwaysStartHeaderSync: true) // alwaysStartHeaderSync = true => for the merge we're forcing header sync start. It doesn't matter if it is archive sync or fast sync { _poSSwitcher = poSSwitcher ?? throw new ArgumentNullException(nameof(poSSwitcher)); _pivot = pivot ?? throw new ArgumentNullException(nameof(pivot)); @@ -79,7 +79,7 @@ protected override void ResetPivot() // First, we assume pivot _pivotNumber = ExpectedPivotNumber; _nextHeaderHash = ExpectedPivotHash; - _nextHeaderDiff = _poSSwitcher.FinalTotalDifficulty; + _nextHeaderTotalDifficulty = _poSSwitcher.FinalTotalDifficulty; long startNumber = _pivotNumber; @@ -89,7 +89,7 @@ protected override void ResetPivot() { startNumber = lowestInserted.Number - 1; _nextHeaderHash = lowestInserted.ParentHash ?? Keccak.Zero; - _nextHeaderDiff = lowestInserted.TotalDifficulty - lowestInserted.Difficulty; + _nextHeaderTotalDifficulty = lowestInserted.TotalDifficulty - lowestInserted.Difficulty; } // the base class with starts with _lowestRequestedHeaderNumber - 1, so we offset it here. @@ -168,7 +168,7 @@ protected override AddBlockResult InsertToBlockTree(BlockHeader header) _logger.Trace( $"Adding new header in beacon headers sync {header.ToString(BlockHeader.Format.FullHashAndNumber)}"); BlockTreeInsertHeaderOptions headerOptions = BlockTreeInsertHeaderOptions.BeaconHeaderInsert; - if (_nextHeaderDiff is null) + if (_nextHeaderTotalDifficulty is null) { headerOptions |= BlockTreeInsertHeaderOptions.TotalDifficultyNotNeeded; } @@ -188,7 +188,7 @@ protected override AddBlockResult InsertToBlockTree(BlockHeader header) if (insertOutcome == AddBlockResult.Added || insertOutcome == AddBlockResult.AlreadyKnown) { _nextHeaderHash = header.ParentHash!; - _nextHeaderDiff = header.TotalDifficulty is not null && header.TotalDifficulty >= header.Difficulty + _nextHeaderTotalDifficulty = header.TotalDifficulty is not null && header.TotalDifficulty >= header.Difficulty ? header.TotalDifficulty - header.Difficulty : null; } diff --git a/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/FastHeadersSyncTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/FastHeadersSyncTests.cs index 9216db0c51e..7f9d9e93c3b 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/FastHeadersSyncTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/FastHeadersSyncTests.cs @@ -574,7 +574,7 @@ public ResettableHeaderSyncFeed( long? hangOnBlockNumberAfterInsert = null, ManualResetEventSlim? hangLatch = null, bool alwaysStartHeaderSync = false - ) : base(blockTree, syncPeerPool, syncConfig, syncReport, logManager, alwaysStartHeaderSync) + ) : base(blockTree, syncPeerPool, syncConfig, syncReport, logManager, alwaysStartHeaderSync: alwaysStartHeaderSync) { _hangOnBlockNumber = hangOnBlockNumber; _hangOnBlockNumberAfterInsert = hangOnBlockNumberAfterInsert; diff --git a/src/Nethermind/Nethermind.Synchronization.Test/TotalDifficultyStrategyTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/TotalDifficultyStrategyTests.cs new file mode 100644 index 00000000000..a09608deb59 --- /dev/null +++ b/src/Nethermind/Nethermind.Synchronization.Test/TotalDifficultyStrategyTests.cs @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using FluentAssertions; +using Nethermind.Core; +using Nethermind.Core.Test.Builders; +using Nethermind.Int256; +using NUnit.Framework; + +namespace Nethermind.Synchronization.Test; + +public class TotalDifficultyStrategyTests +{ + [Test] + public void CumulativeTotalDifficultyStrategy_own_TotalDifficulty_is_Parent_TotalDifficulty_plus_own_difficulty() + { + ITotalDifficultyStrategy strategy = new CumulativeTotalDifficultyStrategy(); + + List<(BlockHeader Header, UInt256 ExpectedTotalDifficulty)> headers = + [ + (Build.A.BlockHeader.WithDifficulty(10).WithTotalDifficulty(39).TestObject, 39), + (Build.A.BlockHeader.WithDifficulty(8).TestObject, 29), + (Build.A.BlockHeader.WithDifficulty(5).TestObject, 21), + (Build.A.BlockHeader.WithDifficulty(12).TestObject, 16), + (Build.A.BlockHeader.WithDifficulty(3).TestObject, 4), + (Build.A.BlockHeader.WithDifficulty(1).TestObject, 1), + ]; + + for (int i = 0; i < headers.Count - 1; i++) + { + var header = headers[i].Header; + var parent = headers[i + 1].Header; + + parent.TotalDifficulty = strategy.ParentTotalDifficulty(header); + } + + foreach (var (header, expectedTotalDifficulty) in headers) + { + header.TotalDifficulty.Should().Be(expectedTotalDifficulty); + } + } +} diff --git a/src/Nethermind/Nethermind.Synchronization/FastBlocks/FastHeadersSyncFeed.cs b/src/Nethermind/Nethermind.Synchronization/FastBlocks/FastHeadersSyncFeed.cs index 8ff1b75d748..5e9bf450c7f 100644 --- a/src/Nethermind/Nethermind.Synchronization/FastBlocks/FastHeadersSyncFeed.cs +++ b/src/Nethermind/Nethermind.Synchronization/FastBlocks/FastHeadersSyncFeed.cs @@ -32,6 +32,7 @@ public class HeadersSyncFeed : ActivatedSyncFeed protected readonly ISyncReport _syncReport; protected readonly IBlockTree _blockTree; protected readonly ISyncConfig _syncConfig; + private readonly ITotalDifficultyStrategy _totalDifficultyStrategy; private readonly object _handlerLock = new(); @@ -39,7 +40,7 @@ public class HeadersSyncFeed : ActivatedSyncFeed protected long _lowestRequestedHeaderNumber; protected Hash256 _nextHeaderHash; - protected UInt256? _nextHeaderDiff; + protected UInt256? _nextHeaderTotalDifficulty; protected long _pivotNumber; @@ -154,6 +155,7 @@ public HeadersSyncFeed( ISyncConfig? syncConfig, ISyncReport? syncReport, ILogManager? logManager, + ITotalDifficultyStrategy? totalDifficultyStrategy = null, bool alwaysStartHeaderSync = false) { _syncPeerPool = syncPeerPool ?? throw new ArgumentNullException(nameof(syncPeerPool)); @@ -161,6 +163,7 @@ public HeadersSyncFeed( _blockTree = blockTree ?? throw new ArgumentNullException(nameof(blockTree)); _syncConfig = syncConfig ?? throw new ArgumentNullException(nameof(syncConfig)); _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(HeadersSyncFeed)); + _totalDifficultyStrategy = totalDifficultyStrategy ?? new CumulativeTotalDifficultyStrategy(); if (!_syncConfig.UseGethLimitsInFastBlocks) { @@ -196,7 +199,7 @@ protected virtual void ResetPivot() _pivotNumber = _syncConfig.PivotNumberParsed; _lowestRequestedHeaderNumber = _pivotNumber + 1; // Because we want the pivot to be requested _nextHeaderHash = _syncConfig.PivotHashParsed; - _nextHeaderDiff = _syncConfig.PivotTotalDifficultyParsed; + _nextHeaderTotalDifficulty = _syncConfig.PivotTotalDifficultyParsed; // Resume logic BlockHeader? lowestInserted = _blockTree.LowestInsertedHeader; @@ -610,7 +613,7 @@ protected virtual int InsertHeaders(HeadersSyncBatch batch) } } - header.TotalDifficulty = _nextHeaderDiff; + header.TotalDifficulty = _nextHeaderTotalDifficulty; AddBlockResult addBlockResult = InsertHeader(header); if (addBlockResult == AddBlockResult.InvalidBlock) { @@ -713,8 +716,9 @@ protected virtual AddBlockResult InsertToBlockTree(BlockHeader header) protected void SetExpectedNextHeaderToParent(BlockHeader header) { _nextHeaderHash = header.ParentHash!; - _nextHeaderDiff = (header.TotalDifficulty ?? 0) - header.Difficulty; + _nextHeaderTotalDifficulty = _totalDifficultyStrategy.ParentTotalDifficulty(header); } + private bool _disposed = false; public override void Dispose() { diff --git a/src/Nethermind/Nethermind.Synchronization/ITotalDifficultyStrategy.cs b/src/Nethermind/Nethermind.Synchronization/ITotalDifficultyStrategy.cs new file mode 100644 index 00000000000..676cbd60e9f --- /dev/null +++ b/src/Nethermind/Nethermind.Synchronization/ITotalDifficultyStrategy.cs @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Int256; + +namespace Nethermind.Synchronization; + +public interface ITotalDifficultyStrategy +{ + UInt256 ParentTotalDifficulty(BlockHeader header); +} + +public sealed class CumulativeTotalDifficultyStrategy : ITotalDifficultyStrategy +{ + public UInt256 ParentTotalDifficulty(BlockHeader header) + { + return (header.TotalDifficulty ?? 0) - header.Difficulty; + } +} From c391c2e946936096d5d1b34b50fdd13ae50a0d56 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 23 Oct 2024 12:45:31 -0300 Subject: [PATCH 2/5] Add `FixedTotalDifficultyStrategy` --- .../TotalDifficultyStrategyTests.cs | 33 +++++++++++++++++++ .../ITotalDifficultyStrategy.cs | 14 ++++++++ 2 files changed, 47 insertions(+) diff --git a/src/Nethermind/Nethermind.Synchronization.Test/TotalDifficultyStrategyTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/TotalDifficultyStrategyTests.cs index a09608deb59..f2dc2d448f3 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/TotalDifficultyStrategyTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/TotalDifficultyStrategyTests.cs @@ -40,4 +40,37 @@ public void CumulativeTotalDifficultyStrategy_own_TotalDifficulty_is_Parent_Tota header.TotalDifficulty.Should().Be(expectedTotalDifficulty); } } + + [Test] + public void FixedTotalDifficultyStrategy_fixes_a_block_header_to_specific_total_difficulty() + { + ITotalDifficultyStrategy strategy = new FixedTotalDifficultyStrategy( + new CumulativeTotalDifficultyStrategy(), + fixesBlockNumber: 3, + toTotalDifficulty: 21 + ); + + List<(BlockHeader Header, UInt256 ExpectedTotalDifficulty)> headers = + [ + (Build.A.BlockHeader.WithNumber(5).WithDifficulty(0).WithTotalDifficulty(0).TestObject, 0), + (Build.A.BlockHeader.WithNumber(4).WithDifficulty(0).TestObject, 0), + (Build.A.BlockHeader.WithNumber(3).WithDifficulty(5).TestObject, 21), + (Build.A.BlockHeader.WithNumber(2).WithDifficulty(12).TestObject, 16), + (Build.A.BlockHeader.WithNumber(1).WithDifficulty(3).TestObject, 4), + (Build.A.BlockHeader.WithNumber(0).WithDifficulty(1).TestObject, 1), + ]; + + for (int i = 0; i < headers.Count - 1; i++) + { + var header = headers[i].Header; + var parent = headers[i + 1].Header; + + parent.TotalDifficulty = strategy.ParentTotalDifficulty(header); + } + + foreach (var (header, expectedTotalDifficulty) in headers) + { + header.TotalDifficulty.Should().Be(expectedTotalDifficulty); + } + } } diff --git a/src/Nethermind/Nethermind.Synchronization/ITotalDifficultyStrategy.cs b/src/Nethermind/Nethermind.Synchronization/ITotalDifficultyStrategy.cs index 676cbd60e9f..2e1ef7c9f96 100644 --- a/src/Nethermind/Nethermind.Synchronization/ITotalDifficultyStrategy.cs +++ b/src/Nethermind/Nethermind.Synchronization/ITotalDifficultyStrategy.cs @@ -18,3 +18,17 @@ public UInt256 ParentTotalDifficulty(BlockHeader header) return (header.TotalDifficulty ?? 0) - header.Difficulty; } } + +public sealed class FixedTotalDifficultyStrategy( + ITotalDifficultyStrategy strategy, + long fixesBlockNumber, + UInt256 toTotalDifficulty +) : ITotalDifficultyStrategy +{ + public UInt256 ParentTotalDifficulty(BlockHeader header) + { + return header.Number > 0 && header.Number - 1 == fixesBlockNumber + ? toTotalDifficulty + : strategy.ParentTotalDifficulty(header); + } +} From cb4c7912804340230d6b2862c2d4347aab3cc94c Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 23 Oct 2024 14:36:22 -0300 Subject: [PATCH 3/5] Inject Optimism-Mainnet specific strategy --- .../Nethermind.Optimism/OptimismPlugin.cs | 2 +- .../OptimismSynchronizerModule.cs | 46 +++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/Nethermind/Nethermind.Optimism/OptimismSynchronizerModule.cs diff --git a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs index 33615392a99..1cacc740fa8 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs @@ -160,7 +160,7 @@ public Task InitSynchronization() .AddSingleton(_mergeConfig) .AddSingleton(_invalidChainTracker); - builder.RegisterModule(new SynchronizerModule(_syncConfig)); + builder.RegisterModule(new OptimismSynchronizerModule(parameters: _api.ChainSpec.Optimism, _api.SpecProvider, _syncConfig)); builder.RegisterModule(new MergeSynchronizerModule()); IContainer container = builder.Build(); diff --git a/src/Nethermind/Nethermind.Optimism/OptimismSynchronizerModule.cs b/src/Nethermind/Nethermind.Optimism/OptimismSynchronizerModule.cs new file mode 100644 index 00000000000..57736f19197 --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism/OptimismSynchronizerModule.cs @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Autofac; +using Nethermind.Blockchain.Synchronization; +using Nethermind.Core; +using Nethermind.Core.Specs; +using Nethermind.Specs.ChainSpecStyle; +using Nethermind.Synchronization; + +namespace Nethermind.Optimism; + +/// +/// In Optimism Mainnet, the gets resetted to 0 in the Bedrock block unlike other chains that went through The Merge fork. +/// Calculation is still the same: the current block's is the parent's plus the current block's . +/// +/// +public class OptimismSynchronizerModule( + OptimismParameters parameters, + ISpecProvider provider, + ISyncConfig syncConfig +) : SynchronizerModule(syncConfig) +{ + private const ulong OptimismMainnetChainId = 0xA; + + protected override void Load(ContainerBuilder builder) + { + base.Load(builder); + + if (provider.ChainId == OptimismMainnetChainId) + { + builder.AddSingleton( + new FixedTotalDifficultyStrategy( + new CumulativeTotalDifficultyStrategy(), + fixesBlockNumber: parameters.BedrockBlockNumber - 1, + toTotalDifficulty: provider.TerminalTotalDifficulty ?? throw new ArgumentNullException(nameof(provider.TerminalTotalDifficulty)) + ) + ); + } + else + { + builder.AddSingleton(new CumulativeTotalDifficultyStrategy()); + } + } +} From 280bf6892b34caf9f25a0a1ef99c6f543f866ba9 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 23 Oct 2024 14:59:29 -0300 Subject: [PATCH 4/5] Small log improvement --- .../Nethermind.Synchronization/Reporting/SyncReport.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Synchronization/Reporting/SyncReport.cs b/src/Nethermind/Nethermind.Synchronization/Reporting/SyncReport.cs index 30e7687c7c4..216f200c330 100644 --- a/src/Nethermind/Nethermind.Synchronization/Reporting/SyncReport.cs +++ b/src/Nethermind/Nethermind.Synchronization/Reporting/SyncReport.cs @@ -259,7 +259,7 @@ private void WriteDbSyncReport() private void WriteNotStartedReport() { - _logger.Info($"Waiting for peers... {(DateTime.UtcNow - StartTime).Seconds}s"); + _logger.Info($"Waiting for peers... {Math.Round((DateTime.UtcNow - StartTime).TotalSeconds)}s"); } private void WriteFullSyncReport() From a30e5160ee61609e9d63bbdfb855530b218ed5f1 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Thu, 24 Oct 2024 11:25:40 -0300 Subject: [PATCH 5/5] Cleanup DI --- src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs | 3 ++- .../OptimismSynchronizerModule.cs | 12 +----------- .../Nethermind.Synchronization/Synchronizer.cs | 3 +++ 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs index 1cacc740fa8..aa58d6f01bd 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs @@ -160,8 +160,9 @@ public Task InitSynchronization() .AddSingleton(_mergeConfig) .AddSingleton(_invalidChainTracker); - builder.RegisterModule(new OptimismSynchronizerModule(parameters: _api.ChainSpec.Optimism, _api.SpecProvider, _syncConfig)); + builder.RegisterModule(new SynchronizerModule(_syncConfig)); builder.RegisterModule(new MergeSynchronizerModule()); + builder.RegisterModule(new OptimismSynchronizerModule(_api.ChainSpec.Optimism, _api.SpecProvider)); IContainer container = builder.Build(); diff --git a/src/Nethermind/Nethermind.Optimism/OptimismSynchronizerModule.cs b/src/Nethermind/Nethermind.Optimism/OptimismSynchronizerModule.cs index 57736f19197..b1a1e60aba3 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismSynchronizerModule.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismSynchronizerModule.cs @@ -16,18 +16,12 @@ namespace Nethermind.Optimism; /// Calculation is still the same: the current block's is the parent's plus the current block's . /// /// -public class OptimismSynchronizerModule( - OptimismParameters parameters, - ISpecProvider provider, - ISyncConfig syncConfig -) : SynchronizerModule(syncConfig) +public sealed class OptimismSynchronizerModule(OptimismParameters parameters, ISpecProvider provider) : Module { private const ulong OptimismMainnetChainId = 0xA; protected override void Load(ContainerBuilder builder) { - base.Load(builder); - if (provider.ChainId == OptimismMainnetChainId) { builder.AddSingleton( @@ -38,9 +32,5 @@ protected override void Load(ContainerBuilder builder) ) ); } - else - { - builder.AddSingleton(new CumulativeTotalDifficultyStrategy()); - } } } diff --git a/src/Nethermind/Nethermind.Synchronization/Synchronizer.cs b/src/Nethermind/Nethermind.Synchronization/Synchronizer.cs index 826406ca9de..e6a3077b3cd 100644 --- a/src/Nethermind/Nethermind.Synchronization/Synchronizer.cs +++ b/src/Nethermind/Nethermind.Synchronization/Synchronizer.cs @@ -290,6 +290,9 @@ protected override void Load(ContainerBuilder builder) .AddScoped, FastBlocksPeerAllocationStrategyFactory>() .AddScoped>() + // Default TotalDifficulty calculation strategy used when processing headers + .AddScoped() + // SyncProgress resolver need one header sync batch feed, which is the fast header one. .Register(ctx => ctx .ResolveNamed>(nameof(HeadersSyncFeed))