From 4eb7f091f23ac26e5e0d899547c9d74246c51a3f Mon Sep 17 00:00:00 2001 From: Amirul Ashraf Date: Fri, 1 Nov 2024 00:21:57 +0800 Subject: [PATCH] Feature/verify trie on state finish (#7691) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Lukasz Rozmej Co-authored-by: Szymon Kulec Co-authored-by: Kamil ChodoĊ‚a <43241881+kamilchodola@users.noreply.github.com> --- .github/workflows/sync-supported-chains.yml | 8 +- .../Synchronization/ISyncConfig.cs | 3 + .../Synchronization/SyncConfig.cs | 1 + .../ContainerBuilderExtensions.cs | 8 ++ .../Nethermind.Init/InitializeStateDb.cs | 18 +--- .../Nethermind.State/StateReaderExtensions.cs | 5 +- .../SynchronizerModuleTests.cs | 100 ++++++++++++++++++ .../FastSync/ITreeSync.cs | 17 +++ .../FastSync/StateSyncFeed.cs | 2 +- .../FastSync/TreeSync.cs | 7 +- .../VerifyStateOnStateSyncFinished.cs | 67 ++++++++++++ .../Synchronizer.cs | 10 +- src/Nethermind/Nethermind.Trie/TrieStats.cs | 64 +++++------ .../Nethermind.Trie/TrieStatsCollector.cs | 37 +++++-- 14 files changed, 284 insertions(+), 63 deletions(-) create mode 100644 src/Nethermind/Nethermind.Synchronization.Test/SynchronizerModuleTests.cs create mode 100644 src/Nethermind/Nethermind.Synchronization/FastSync/ITreeSync.cs create mode 100644 src/Nethermind/Nethermind.Synchronization/FastSync/VerifyStateOnStateSyncFinished.cs diff --git a/.github/workflows/sync-supported-chains.yml b/.github/workflows/sync-supported-chains.yml index 5456b896195..319ed4bb758 100644 --- a/.github/workflows/sync-supported-chains.yml +++ b/.github/workflows/sync-supported-chains.yml @@ -160,6 +160,7 @@ jobs: --network $stripped_network \ --consensus-url $CONSENSUS_URL \ --execution-api-url $EXECUTION_URL \ + --el-op-extra-flag Sync.VerifyTrieOnStateSyncFinished=true \ $extra_param else ./build/sedge generate \ @@ -176,6 +177,7 @@ jobs: --el-extra-flag Sync.DownloadBodiesInFastSync=false \ --el-extra-flag Sync.DownloadReceiptsInFastSync=false \ --el-extra-flag JsonRpc.EnabledModules=[Eth,Subscribe,Trace,TxPool,Web3,Personal,Proof,Net,Parity,Health,Rpc,Debug] \ + --el-extra-flag Sync.VerifyTrieOnStateSyncFinished=true \ --el-extra-flag Sync.SnapSync=true \ --checkpoint-sync-url=${{ matrix.config.checkpoint-sync-url }} fi @@ -190,12 +192,14 @@ jobs: declare -A good_logs declare -A required_count - bad_logs["Corrupt"]=1 - bad_logs["Exception"]=1 + #bad_logs["Corrupt"]=1 + #bad_logs["Exception"]=1 + bad_logs["Error in verify trie"]=1 good_logs["Processed"]=0 required_count["Processed"]=20 + required_count["Stats after finishing state"]=1 network="${{ matrix.config.network }}" if [[ "$network" != "joc-mainnet" && "$network" != "joc-testnet" && "$network" != "linea-mainnet" && "$network" != "linea-sepolia" ]]; then diff --git a/src/Nethermind/Nethermind.Blockchain/Synchronization/ISyncConfig.cs b/src/Nethermind/Nethermind.Blockchain/Synchronization/ISyncConfig.cs index 24cca9a46d5..958eb673a67 100644 --- a/src/Nethermind/Nethermind.Blockchain/Synchronization/ISyncConfig.cs +++ b/src/Nethermind/Nethermind.Blockchain/Synchronization/ISyncConfig.cs @@ -146,4 +146,7 @@ public interface ISyncConfig : IConfig [ConfigItem(Description = "_Technical._ MultiSyncModeSelector will wait for header to completely sync first.", DefaultValue = "false", HiddenFromDocs = true)] bool NeedToWaitForHeader { get; set; } + + [ConfigItem(Description = "_Technical._ Run verify trie on state sync is finished.", DefaultValue = "false", HiddenFromDocs = true)] + bool VerifyTrieOnStateSyncFinished { get; } } diff --git a/src/Nethermind/Nethermind.Blockchain/Synchronization/SyncConfig.cs b/src/Nethermind/Nethermind.Blockchain/Synchronization/SyncConfig.cs index c467650ca7a..546d5017286 100644 --- a/src/Nethermind/Nethermind.Blockchain/Synchronization/SyncConfig.cs +++ b/src/Nethermind/Nethermind.Blockchain/Synchronization/SyncConfig.cs @@ -66,6 +66,7 @@ public string? PivotHash public bool? SnapServingEnabled { get; set; } = null; public int MultiSyncModeSelectorLoopTimerMs { get; set; } = 1000; public bool NeedToWaitForHeader { get; set; } + public bool VerifyTrieOnStateSyncFinished { get; set; } public bool TrieHealing { get; set; } = true; public override string ToString() diff --git a/src/Nethermind/Nethermind.Core/ContainerBuilderExtensions.cs b/src/Nethermind/Nethermind.Core/ContainerBuilderExtensions.cs index 1c30a830b55..a64ba5d833a 100644 --- a/src/Nethermind/Nethermind.Core/ContainerBuilderExtensions.cs +++ b/src/Nethermind/Nethermind.Core/ContainerBuilderExtensions.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Reflection; using Autofac; +using Autofac.Core; using Autofac.Features.AttributeFilters; namespace Nethermind.Core; @@ -115,6 +116,13 @@ public static ContainerBuilder RegisterNamedComponentInItsOwnLifetime(this Co return builder; } + + public static ContainerBuilder AddModule(this ContainerBuilder builder, IModule module) + { + builder.RegisterModule(module); + + return builder; + } } /// diff --git a/src/Nethermind/Nethermind.Init/InitializeStateDb.cs b/src/Nethermind/Nethermind.Init/InitializeStateDb.cs index 8f53c42a517..4fd09fe8e97 100644 --- a/src/Nethermind/Nethermind.Init/InitializeStateDb.cs +++ b/src/Nethermind/Nethermind.Init/InitializeStateDb.cs @@ -198,20 +198,10 @@ public Task Execute(CancellationToken cancellationToken) if (_api.Config().DiagnosticMode == DiagnosticMode.VerifyTrie) { - Task.Run(() => - { - try - { - _logger!.Info("Collecting trie stats and verifying that no nodes are missing..."); - Hash256 stateRoot = getApi.BlockTree!.Head?.StateRoot ?? Keccak.EmptyTreeHash; - TrieStats stats = stateManager.GlobalStateReader.CollectStats(stateRoot, getApi.DbProvider.CodeDb, _api.LogManager); - _logger.Info($"Starting from {getApi.BlockTree.Head?.Number} {getApi.BlockTree.Head?.StateRoot}{Environment.NewLine}" + stats); - } - catch (Exception ex) - { - _logger!.Error(ex.ToString()); - } - }); + _logger!.Info("Collecting trie stats and verifying that no nodes are missing..."); + Hash256 stateRoot = getApi.BlockTree!.Head?.StateRoot ?? Keccak.EmptyTreeHash; + TrieStats stats = stateManager.GlobalStateReader.CollectStats(stateRoot, getApi.DbProvider.CodeDb, _api.LogManager, _api.ProcessExit!.Token); + _logger.Info($"Starting from {getApi.BlockTree.Head?.Number} {getApi.BlockTree.Head?.StateRoot}{Environment.NewLine}" + stats); } // Init state if we need system calls before actual processing starts diff --git a/src/Nethermind/Nethermind.State/StateReaderExtensions.cs b/src/Nethermind/Nethermind.State/StateReaderExtensions.cs index ac63366349c..2fcb0179f24 100644 --- a/src/Nethermind/Nethermind.State/StateReaderExtensions.cs +++ b/src/Nethermind/Nethermind.State/StateReaderExtensions.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Threading; using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; @@ -47,9 +48,9 @@ public static bool HasStateForBlock(this IStateReader stateReader, BlockHeader h return stateReader.HasStateForRoot(header.StateRoot!); } - public static TrieStats CollectStats(this IStateReader stateProvider, Hash256 root, IKeyValueStore codeStorage, ILogManager logManager) + public static TrieStats CollectStats(this IStateReader stateProvider, Hash256 root, IKeyValueStore codeStorage, ILogManager logManager, CancellationToken cancellationToken = default) { - TrieStatsCollector collector = new(codeStorage, logManager); + TrieStatsCollector collector = new(codeStorage, logManager, cancellationToken); stateProvider.RunTreeVisitor(collector, root, new VisitingOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, diff --git a/src/Nethermind/Nethermind.Synchronization.Test/SynchronizerModuleTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/SynchronizerModuleTests.cs new file mode 100644 index 00000000000..d7ba3f27cad --- /dev/null +++ b/src/Nethermind/Nethermind.Synchronization.Test/SynchronizerModuleTests.cs @@ -0,0 +1,100 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Threading; +using System.Threading.Tasks; +using Autofac; +using FluentAssertions; +using Nethermind.Blockchain.Synchronization; +using Nethermind.Config; +using Nethermind.Consensus.Processing; +using Nethermind.Core; +using Nethermind.Core.Test.Builders; +using Nethermind.Db; +using Nethermind.Logging; +using Nethermind.State; +using Nethermind.Synchronization.FastSync; +using Nethermind.Trie; +using NSubstitute; +using NUnit.Framework; + +namespace Nethermind.Synchronization.Test; + +public class SynchronizerModuleTests +{ + public IContainer CreateTestContainer() + { + ITreeSync treeSync = Substitute.For(); + IStateReader stateReader = Substitute.For(); + IBlockProcessingQueue blockQueue = Substitute.For(); + + return new ContainerBuilder() + .AddModule(new SynchronizerModule(new SyncConfig() + { + FastSync = true, + VerifyTrieOnStateSyncFinished = true + })) + .AddKeyedSingleton(DbNames.Code, Substitute.For()) + .AddSingleton(stateReader) + .AddSingleton(treeSync) + .AddSingleton(blockQueue) + .AddSingleton(Substitute.For()) + .AddSingleton(LimboLogs.Instance) + .Build(); + } + + [Test] + public void TestOnTreeSyncFinish_CallVisit() + { + IContainer ctx = CreateTestContainer(); + ITreeSync treeSync = ctx.Resolve(); + IStateReader stateReader = ctx.Resolve(); + + treeSync.SyncCompleted += Raise.EventWith(null, new ITreeSync.SyncCompletedEventArgs(TestItem.KeccakA)); + + stateReader + .Received() + .RunTreeVisitor(Arg.Any(), Arg.Is(TestItem.KeccakA), Arg.Any()); + } + + [Test] + public async Task TestOnTreeSyncFinish_BlockProcessingQueue_UntilFinished() + { + IContainer ctx = CreateTestContainer(); + ITreeSync treeSync = ctx.Resolve(); + IStateReader stateReader = ctx.Resolve(); + IBlockProcessingQueue blockQueue = ctx.Resolve(); + + ManualResetEvent treeVisitorBlocker = new ManualResetEvent(false); + + stateReader + .When(sr => sr.RunTreeVisitor(Arg.Any(), Arg.Is(TestItem.KeccakA), Arg.Any())) + .Do((ci) => + { + treeVisitorBlocker.WaitOne(); + }); + + Task triggerTask = Task.Run(() => + { + treeSync.SyncCompleted += Raise.EventWith(null, new ITreeSync.SyncCompletedEventArgs(TestItem.KeccakA)); + }); + + await Task.Delay(100); + + Task blockQueueTask = Task.Run(() => + { + blockQueue.BlockRemoved += + Raise.EventWith(null, new BlockRemovedEventArgs(null!, ProcessingResult.Success)); + }); + + await Task.Delay(100); + + blockQueueTask.IsCompleted.Should().BeFalse(); + treeVisitorBlocker.Set(); + + await triggerTask; + await blockQueueTask; + blockQueue.BlockRemoved += Raise.EventWith(null, new BlockRemovedEventArgs(null!, ProcessingResult.Success)); + } +} diff --git a/src/Nethermind/Nethermind.Synchronization/FastSync/ITreeSync.cs b/src/Nethermind/Nethermind.Synchronization/FastSync/ITreeSync.cs new file mode 100644 index 00000000000..3dc7ca4450e --- /dev/null +++ b/src/Nethermind/Nethermind.Synchronization/FastSync/ITreeSync.cs @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core.Crypto; + +namespace Nethermind.Synchronization.FastSync; + +public interface ITreeSync +{ + public event EventHandler SyncCompleted; + + public class SyncCompletedEventArgs(Hash256 root) : EventArgs + { + public Hash256 Root => root; + } +} diff --git a/src/Nethermind/Nethermind.Synchronization/FastSync/StateSyncFeed.cs b/src/Nethermind/Nethermind.Synchronization/FastSync/StateSyncFeed.cs index 875eb380899..23f4001a023 100644 --- a/src/Nethermind/Nethermind.Synchronization/FastSync/StateSyncFeed.cs +++ b/src/Nethermind/Nethermind.Synchronization/FastSync/StateSyncFeed.cs @@ -11,7 +11,7 @@ namespace Nethermind.Synchronization.FastSync { - public partial class StateSyncFeed : SyncFeed, IDisposable + public class StateSyncFeed : SyncFeed, IDisposable { private const StateSyncBatch EmptyBatch = null; diff --git a/src/Nethermind/Nethermind.Synchronization/FastSync/TreeSync.cs b/src/Nethermind/Nethermind.Synchronization/FastSync/TreeSync.cs index 12d2540e789..03e05a65660 100644 --- a/src/Nethermind/Nethermind.Synchronization/FastSync/TreeSync.cs +++ b/src/Nethermind/Nethermind.Synchronization/FastSync/TreeSync.cs @@ -26,7 +26,7 @@ namespace Nethermind.Synchronization.FastSync { - public class TreeSync + public class TreeSync : ITreeSync { public const int AlreadySavedCapacity = 1024 * 1024; public const int MaxRequestSize = 384; @@ -76,6 +76,8 @@ public class TreeSync private long _blockNumber; private readonly SyncMode _syncMode; + public event EventHandler? SyncCompleted; + public TreeSync([KeyFilter(DbNames.Code)] IDb codeDb, INodeStorage nodeStorage, IBlockTree blockTree, ILogManager logManager) : this(SyncMode.StateNodes, codeDb, nodeStorage, blockTree, logManager) { @@ -707,7 +709,6 @@ private void VerifyPostSyncCleanUp() } _dependencies = new Dictionary>(); - // _alreadySaved = new LruKeyCache(AlreadySavedCapacity, "saved nodes"); } if (_pendingItems.Count != 0) @@ -716,6 +717,8 @@ private void VerifyPostSyncCleanUp() } CleanupMemory(); + + SyncCompleted?.Invoke(this, new ITreeSync.SyncCompletedEventArgs(_rootNode)); } private void CleanupMemory() diff --git a/src/Nethermind/Nethermind.Synchronization/FastSync/VerifyStateOnStateSyncFinished.cs b/src/Nethermind/Nethermind.Synchronization/FastSync/VerifyStateOnStateSyncFinished.cs new file mode 100644 index 00000000000..1b194729446 --- /dev/null +++ b/src/Nethermind/Nethermind.Synchronization/FastSync/VerifyStateOnStateSyncFinished.cs @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Threading; +using Autofac; +using Autofac.Features.AttributeFilters; +using Nethermind.Config; +using Nethermind.Consensus.Processing; +using Nethermind.Core.Crypto; +using Nethermind.Db; +using Nethermind.Logging; +using Nethermind.State; +using Nethermind.Trie; + +namespace Nethermind.Synchronization.FastSync; + +public class VerifyStateOnStateSyncFinished( + IBlockProcessingQueue processingQueue, + ITreeSync treeSync, + IStateReader stateReader, + [KeyFilter(DbNames.Code)] IDb codeDb, + IProcessExitSource exitSource, + ILogManager logManager) : IStartable +{ + private readonly ILogger _logger = logManager.GetClassLogger(); + + public void Start() + { + treeSync.SyncCompleted += TreeSyncOnOnVerifyPostSyncCleanup; + } + + private void TreeSyncOnOnVerifyPostSyncCleanup(object? sender, ITreeSync.SyncCompletedEventArgs evt) + { + ManualResetEvent processingBlocker = new ManualResetEvent(false); + + processingQueue.BlockRemoved += ProcessingQueueOnBlockRemoved; + + try + { + Hash256 rootNode = evt.Root; + _logger!.Info("Collecting trie stats and verifying that no nodes are missing..."); + TrieStats stats = stateReader.CollectStats(rootNode, codeDb, logManager, exitSource.Token); + if (stats.MissingNodes > 0) + { + _logger.Error($"Missing node found!"); + } + _logger.Info($"Stats after finishing state \n" + stats); + } + catch (Exception e) + { + _logger.Error($"Error in verify trie", e); + } + finally + { + processingBlocker.Set(); + processingQueue.BlockRemoved -= ProcessingQueueOnBlockRemoved; + } + + return; + + void ProcessingQueueOnBlockRemoved(object? o, BlockRemovedEventArgs blockRemovedEventArgs) + { + processingBlocker.WaitOne(); + } + } +} diff --git a/src/Nethermind/Nethermind.Synchronization/Synchronizer.cs b/src/Nethermind/Nethermind.Synchronization/Synchronizer.cs index e6a3077b3cd..451ea36a47a 100644 --- a/src/Nethermind/Nethermind.Synchronization/Synchronizer.cs +++ b/src/Nethermind/Nethermind.Synchronization/Synchronizer.cs @@ -380,12 +380,20 @@ private void ConfigureBodiesSyncComponent(ContainerBuilder serviceCollection) private void ConfigureStateSyncComponent(ContainerBuilder serviceCollection) { serviceCollection - .AddSingleton(); + .AddSingleton(); ConfigureSingletonSyncFeed(serviceCollection); // Disable it by setting noop if (!syncConfig.FastSync) serviceCollection.AddSingleton, NoopSyncFeed>(); + + if (syncConfig.FastSync && syncConfig.VerifyTrieOnStateSyncFinished) + { + serviceCollection + .RegisterType() + .WithAttributeFiltering() + .As(); + } } private static void ConfigureSingletonSyncFeed(ContainerBuilder serviceCollection) where TFeed : class, ISyncFeed where TDownloader : class, ISyncDownloader where TAllocationStrategy : class, IPeerAllocationStrategyFactory diff --git a/src/Nethermind/Nethermind.Trie/TrieStats.cs b/src/Nethermind/Nethermind.Trie/TrieStats.cs index adaf65d57cf..ee17e7259fa 100644 --- a/src/Nethermind/Nethermind.Trie/TrieStats.cs +++ b/src/Nethermind/Nethermind.Trie/TrieStats.cs @@ -8,50 +8,50 @@ namespace Nethermind.Trie public class TrieStats { private const int Levels = 128; - internal int _stateBranchCount; - internal int _stateExtensionCount; - internal int _accountCount; - internal int _storageBranchCount; - internal int _storageExtensionCount; - internal int _storageLeafCount; - internal int _codeCount; - internal int _missingState; - internal int _missingCode; - internal int _missingStorage; + internal long _stateBranchCount; + internal long _stateExtensionCount; + internal long _accountCount; + internal long _storageBranchCount; + internal long _storageExtensionCount; + internal long _storageLeafCount; + internal long _codeCount; + internal long _missingState; + internal long _missingCode; + internal long _missingStorage; internal long _storageSize; internal long _codeSize; internal long _stateSize; - internal readonly int[] _stateLevels = new int[Levels]; - internal readonly int[] _storageLevels = new int[Levels]; - internal readonly int[] _codeLevels = new int[Levels]; + internal readonly long[] _stateLevels = new long[Levels]; + internal readonly long[] _storageLevels = new long[Levels]; + internal readonly long[] _codeLevels = new long[Levels]; - public int StateBranchCount => _stateBranchCount; + public long StateBranchCount => _stateBranchCount; - public int StateExtensionCount => _stateExtensionCount; + public long StateExtensionCount => _stateExtensionCount; - public int AccountCount => _accountCount; + public long AccountCount => _accountCount; - public int StorageBranchCount => _storageBranchCount; + public long StorageBranchCount => _storageBranchCount; - public int StorageExtensionCount => _storageExtensionCount; + public long StorageExtensionCount => _storageExtensionCount; - public int StorageLeafCount => _storageLeafCount; + public long StorageLeafCount => _storageLeafCount; - public int CodeCount => _codeCount; + public long CodeCount => _codeCount; - public int MissingState => _missingState; + public long MissingState => _missingState; - public int MissingCode => _missingCode; + public long MissingCode => _missingCode; - public int MissingStorage => _missingStorage; + public long MissingStorage => _missingStorage; - public int MissingNodes => MissingCode + MissingState + MissingStorage; + public long MissingNodes => MissingCode + MissingState + MissingStorage; - public int StorageCount => StorageLeafCount + StorageExtensionCount + StorageBranchCount; + public long StorageCount => StorageLeafCount + StorageExtensionCount + StorageBranchCount; - public int StateCount => AccountCount + StateExtensionCount + StateBranchCount; + public long StateCount => AccountCount + StateExtensionCount + StateBranchCount; - public int NodesCount => StorageCount + StateCount + CodeCount; + public long NodesCount => StorageCount + StateCount + CodeCount; public long StorageSize => _storageSize; @@ -63,14 +63,14 @@ public class TrieStats // public List MissingNodes { get; set; } = new List(); - public int[] StateLevels => _stateLevels; - public int[] StorageLevels => _storageLevels; - public int[] CodeLevels => _codeLevels; - public int[] AllLevels + public long[] StateLevels => _stateLevels; + public long[] StorageLevels => _storageLevels; + public long[] CodeLevels => _codeLevels; + public long[] AllLevels { get { - int[] result = new int[Levels]; + long[] result = new long[Levels]; for (int i = 0; i < result.Length; i++) { result[i] = _stateLevels[i] + _storageLevels[i] + _codeLevels[i]; diff --git a/src/Nethermind/Nethermind.Trie/TrieStatsCollector.cs b/src/Nethermind/Nethermind.Trie/TrieStatsCollector.cs index 259a1e96139..6fce7f826e7 100644 --- a/src/Nethermind/Nethermind.Trie/TrieStatsCollector.cs +++ b/src/Nethermind/Nethermind.Trie/TrieStatsCollector.cs @@ -4,6 +4,7 @@ using System; using System.Threading; using Nethermind.Core; +using Nethermind.Core.Caching; using Nethermind.Core.Crypto; using Nethermind.Logging; @@ -11,15 +12,18 @@ namespace Nethermind.Trie { public class TrieStatsCollector : ITreeVisitor { + private readonly ClockCache _existingCodeHash = new ClockCache(1024 * 8); private readonly IKeyValueStore _codeKeyValueStore; - private int _lastAccountNodeCount = 0; + private long _lastAccountNodeCount = 0; private readonly ILogger _logger; + private readonly CancellationToken _cancellationToken; - public TrieStatsCollector(IKeyValueStore codeKeyValueStore, ILogManager logManager) + public TrieStatsCollector(IKeyValueStore codeKeyValueStore, ILogManager logManager, CancellationToken cancellationToken = default) { _codeKeyValueStore = codeKeyValueStore ?? throw new ArgumentNullException(nameof(codeKeyValueStore)); _logger = logManager.GetClassLogger(); + _cancellationToken = cancellationToken; } public TrieStats Stats { get; } = new(); @@ -49,6 +53,8 @@ public void VisitMissingNode(Hash256 nodeHash, TrieVisitContext trieVisitContext public void VisitBranch(TrieNode node, TrieVisitContext trieVisitContext) { + _cancellationToken.ThrowIfCancellationRequested(); + if (trieVisitContext.IsStorage) { Interlocked.Add(ref Stats._storageSize, node.FullRlp.Length); @@ -81,9 +87,10 @@ public void VisitExtension(TrieNode node, TrieVisitContext trieVisitContext) public void VisitLeaf(TrieNode node, TrieVisitContext trieVisitContext, ReadOnlySpan value) { - if (Stats.NodesCount - _lastAccountNodeCount > 1_000_000) + long lastAccountNodeCount = _lastAccountNodeCount; + long currentNodeCount = Stats.NodesCount; + if (currentNodeCount - lastAccountNodeCount > 1_000_000 && Interlocked.CompareExchange(ref _lastAccountNodeCount, currentNodeCount, lastAccountNodeCount) == lastAccountNodeCount) { - _lastAccountNodeCount = Stats.NodesCount; _logger.Warn($"Collected info from {Stats.NodesCount} nodes. Missing CODE {Stats.MissingCode} STATE {Stats.MissingState} STORAGE {Stats.MissingStorage}"); } @@ -103,10 +110,22 @@ public void VisitLeaf(TrieNode node, TrieVisitContext trieVisitContext, ReadOnly public void VisitCode(Hash256 codeHash, TrieVisitContext trieVisitContext) { - byte[] code = _codeKeyValueStore[codeHash.Bytes]; - if (code is not null) + ValueHash256 key = new ValueHash256(codeHash.Bytes); + bool codeExist = _existingCodeHash.TryGet(key, out int codeLength); + if (!codeExist) + { + byte[] code = _codeKeyValueStore[codeHash.Bytes]; + codeExist = code is not null; + if (codeExist) + { + codeLength = code.Length; + _existingCodeHash.Set(key, codeLength); + } + } + + if (codeExist) { - Interlocked.Add(ref Stats._codeSize, code.Length); + Interlocked.Add(ref Stats._codeSize, codeLength); Interlocked.Increment(ref Stats._codeCount); } else @@ -119,11 +138,11 @@ public void VisitCode(Hash256 codeHash, TrieVisitContext trieVisitContext) private void IncrementLevel(TrieVisitContext trieVisitContext) { - int[] levels = trieVisitContext.IsStorage ? Stats._storageLevels : Stats._stateLevels; + long[] levels = trieVisitContext.IsStorage ? Stats._storageLevels : Stats._stateLevels; IncrementLevel(trieVisitContext, levels); } - private static void IncrementLevel(TrieVisitContext trieVisitContext, int[] levels) + private static void IncrementLevel(TrieVisitContext trieVisitContext, long[] levels) { Interlocked.Increment(ref levels[trieVisitContext.Level]); }