From 41b7c6210a67b7602cc8320614c404bf7b1b0126 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Oct 2025 06:53:34 +0000 Subject: [PATCH 1/4] Initial plan From eec047aba64b305cb0103fa56389a74ffd6dd968 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Oct 2025 07:06:54 +0000 Subject: [PATCH 2/4] Add SnapServingMaxDepth configuration and update LastNStateRootTracker Co-authored-by: tanishqjasoria <11698398+tanishqjasoria@users.noreply.github.com> --- .../Utils/LastNStateRootTrackerTests.cs | 27 +++++++++++++++++++ .../Synchronization/ISyncConfig.cs | 3 +++ .../Synchronization/SyncConfig.cs | 1 + .../PruningTrieStateFactory.cs | 8 +++--- 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Utils/LastNStateRootTrackerTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Utils/LastNStateRootTrackerTests.cs index 1af52fc05346..699ef5593bfa 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Utils/LastNStateRootTrackerTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Utils/LastNStateRootTrackerTests.cs @@ -106,4 +106,31 @@ public void Test_OnReorg_RebuildSet() tracker.HasStateRoot(Keccak.Compute(100.ToBigEndianByteArray())).Should().BeTrue(); } + + [Test] + public void Test_TrackLastN_WithCustomDepth() + { + System.Collections.Generic.List blocks = new(); + Block currentBlock = Build.A.Block.Genesis.TestObject; + blocks.Add(currentBlock); + for (int i = 0; i < 300; i++) + { + currentBlock = Build.A.Block + .WithParent(currentBlock) + .WithStateRoot(Keccak.Compute(i.ToBigEndianByteArray())) + .TestObject; + blocks.Add(currentBlock); + } + + BlockTree tree = Build.A.BlockTree().WithBlocks(blocks.ToArray()).TestObject; + + // Test with a custom depth of 256 blocks (useful for networks with fast block times like Arbitrum) + LastNStateRootTracker tracker = new LastNStateRootTracker(tree, 256); + + for (int i = 0; i < 320; i++) + { + tracker.HasStateRoot(Keccak.Compute(i.ToBigEndianByteArray())) + .Should().Be(i is >= 44 and < 300); + } + } } diff --git a/src/Nethermind/Nethermind.Blockchain/Synchronization/ISyncConfig.cs b/src/Nethermind/Nethermind.Blockchain/Synchronization/ISyncConfig.cs index dca9b5979433..45871f73fa1d 100644 --- a/src/Nethermind/Nethermind.Blockchain/Synchronization/ISyncConfig.cs +++ b/src/Nethermind/Nethermind.Blockchain/Synchronization/ISyncConfig.cs @@ -138,6 +138,9 @@ public interface ISyncConfig : IConfig [ConfigItem(Description = "_Technical._ Whether to enable snap serving. WARNING: Very slow on hash db layout. Default is to enable on halfpath layout.", DefaultValue = "null", HiddenFromDocs = true)] bool? SnapServingEnabled { get; set; } + [ConfigItem(Description = "The maximum depth (in blocks) for serving snap sync requests. Higher values allow serving requests for older blocks, useful for networks with fast block times like Arbitrum.", DefaultValue = "128")] + int SnapServingMaxDepth { get; set; } + [ConfigItem(Description = "_Technical._ MultiSyncModeSelector sync mode timer loop interval. Used for testing.", DefaultValue = "1000", HiddenFromDocs = true)] int MultiSyncModeSelectorLoopTimerMs { get; set; } diff --git a/src/Nethermind/Nethermind.Blockchain/Synchronization/SyncConfig.cs b/src/Nethermind/Nethermind.Blockchain/Synchronization/SyncConfig.cs index 096d8f57444c..22c592973191 100644 --- a/src/Nethermind/Nethermind.Blockchain/Synchronization/SyncConfig.cs +++ b/src/Nethermind/Nethermind.Blockchain/Synchronization/SyncConfig.cs @@ -65,6 +65,7 @@ public string? PivotHash public int ExitOnSyncedWaitTimeSec { get; set; } = 60; public int MallocTrimIntervalSec { get; set; } = 300; public bool? SnapServingEnabled { get; set; } = null; + public int SnapServingMaxDepth { get; set; } = 128; public int MultiSyncModeSelectorLoopTimerMs { get; set; } = 1000; public int SyncDispatcherEmptyRequestDelayMs { get; set; } = 10; public int SyncDispatcherAllocateTimeoutMs { get; set; } = 1000; diff --git a/src/Nethermind/Nethermind.Init/PruningTrieStateFactory.cs b/src/Nethermind/Nethermind.Init/PruningTrieStateFactory.cs index 55707205ddb5..4153e411934b 100644 --- a/src/Nethermind/Nethermind.Init/PruningTrieStateFactory.cs +++ b/src/Nethermind/Nethermind.Init/PruningTrieStateFactory.cs @@ -87,7 +87,7 @@ ILogManager logManager trieStore, dbProvider, logManager, - new LastNStateRootTracker(blockTree, 128)); + new LastNStateRootTracker(blockTree, syncConfig.SnapServingMaxDepth)); // NOTE: Don't forget this! Very important! TrieStoreBoundaryWatcher trieStoreBoundaryWatcher = new(stateManager, blockTree!, logManager); @@ -190,10 +190,10 @@ ILogManager logManager AdviseConfig(pruningConfig, dbConfig, hardwareInfo); - if (syncConfig.SnapServingEnabled == true && pruningConfig.PruningBoundary < 128) + if (syncConfig.SnapServingEnabled == true && pruningConfig.PruningBoundary < syncConfig.SnapServingMaxDepth) { - if (_logger.IsInfo) _logger.Info($"Snap serving enabled, but {nameof(pruningConfig.PruningBoundary)} is less than 128. Setting to 128."); - pruningConfig.PruningBoundary = 128; + if (_logger.IsInfo) _logger.Info($"Snap serving enabled, but {nameof(pruningConfig.PruningBoundary)} is less than {syncConfig.SnapServingMaxDepth}. Setting to {syncConfig.SnapServingMaxDepth}."); + pruningConfig.PruningBoundary = syncConfig.SnapServingMaxDepth; } if (pruningConfig.PruningBoundary < 64) From 57b0b236e27da60f9e8f2cd4c21f844a4db4680d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Oct 2025 07:13:11 +0000 Subject: [PATCH 3/4] Add clarifying comments for configuration changes Co-authored-by: tanishqjasoria <11698398+tanishqjasoria@users.noreply.github.com> --- .../Nethermind.Blockchain/Utils/LastNStateRootTracker.cs | 4 ++-- .../Nethermind.State.Test/WorldStateManagerTests.cs | 2 +- src/Nethermind/Nethermind.State/Snap/Constants.cs | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain/Utils/LastNStateRootTracker.cs b/src/Nethermind/Nethermind.Blockchain/Utils/LastNStateRootTracker.cs index c1824d21eaaa..66c4727bfac7 100644 --- a/src/Nethermind/Nethermind.Blockchain/Utils/LastNStateRootTracker.cs +++ b/src/Nethermind/Nethermind.Blockchain/Utils/LastNStateRootTracker.cs @@ -11,8 +11,8 @@ namespace Nethermind.Blockchain.Utils; -// TODO: Move responsibility to IWorldStateManager? Could be, but if IWorldStateManager store more than 128 blocks -// of state, that would be out of spec for snap and it would fail hive test. +// TODO: Move responsibility to IWorldStateManager? Could be, but if IWorldStateManager stores more blocks +// of state than configured, that would require updating the snap serving configuration (ISyncConfig.SnapServingMaxDepth). public class LastNStateRootTracker : ILastNStateRootTracker, IDisposable { private readonly IBlockTree _blockTree; diff --git a/src/Nethermind/Nethermind.State.Test/WorldStateManagerTests.cs b/src/Nethermind/Nethermind.State.Test/WorldStateManagerTests.cs index 63ba31d6fedd..5bb6af60a208 100644 --- a/src/Nethermind/Nethermind.State.Test/WorldStateManagerTests.cs +++ b/src/Nethermind/Nethermind.State.Test/WorldStateManagerTests.cs @@ -76,7 +76,7 @@ public void ShouldNotSupportHashLookupOnHalfpath(INodeStorage.KeyScheme keySchem public void ShouldAnnounceReorgOnDispose() { int lastBlock = 256; - int reorgDepth = 128; // Default reorg depth with snap serving + int reorgDepth = 128; // Default value of ISyncConfig.SnapServingMaxDepth IBlockTree blockTree = Substitute.For(); IConfigProvider configProvider = new ConfigProvider(); diff --git a/src/Nethermind/Nethermind.State/Snap/Constants.cs b/src/Nethermind/Nethermind.State/Snap/Constants.cs index 394923319b95..ef2ab6cf3850 100644 --- a/src/Nethermind/Nethermind.State/Snap/Constants.cs +++ b/src/Nethermind/Nethermind.State/Snap/Constants.cs @@ -5,6 +5,10 @@ namespace Nethermind.State.Snap { public class Constants { + /// + /// Maximum distance from head for pivot block validation in merge/beacon chain sync. + /// Note: For snap serving depth configuration, use ISyncConfig.SnapServingMaxDepth instead. + /// public const int MaxDistanceFromHead = 128; } } From a594d9be5183323df03cbe2564c09e9e32ee2c7f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Oct 2025 11:54:42 +0000 Subject: [PATCH 4/4] Get reorgDepth from config instead of hardcoding in test Co-authored-by: LukaszRozmej <12445221+LukaszRozmej@users.noreply.github.com> --- src/Nethermind/Nethermind.State.Test/WorldStateManagerTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.State.Test/WorldStateManagerTests.cs b/src/Nethermind/Nethermind.State.Test/WorldStateManagerTests.cs index 5bb6af60a208..805bf20237ab 100644 --- a/src/Nethermind/Nethermind.State.Test/WorldStateManagerTests.cs +++ b/src/Nethermind/Nethermind.State.Test/WorldStateManagerTests.cs @@ -5,6 +5,7 @@ using Autofac; using FluentAssertions; using Nethermind.Blockchain; +using Nethermind.Blockchain.Synchronization; using Nethermind.Config; using Nethermind.Core; using Nethermind.Core.Crypto; @@ -76,10 +77,10 @@ public void ShouldNotSupportHashLookupOnHalfpath(INodeStorage.KeyScheme keySchem public void ShouldAnnounceReorgOnDispose() { int lastBlock = 256; - int reorgDepth = 128; // Default value of ISyncConfig.SnapServingMaxDepth IBlockTree blockTree = Substitute.For(); IConfigProvider configProvider = new ConfigProvider(); + int reorgDepth = configProvider.GetConfig().SnapServingMaxDepth; { using IContainer ctx = new ContainerBuilder()