From ec85308e039122d64dce10790ed8fdbf40fa96c7 Mon Sep 17 00:00:00 2001 From: Amirul Ashraf Date: Sat, 26 Oct 2024 14:27:59 +0800 Subject: [PATCH 01/13] Do not redownload unneded block --- .../Nethermind.Api/IApiWithStores.cs | 3 +- .../Blocks/BlockStore.cs | 7 +++++ .../Blocks/IBlockStore.cs | 1 + .../Simulate/SimulateDictionaryBlockStore.cs | 5 ++++ .../FastBlocks/BodiesSyncFeedTests.cs | 2 ++ .../FastBlocks/BodiesSyncFeed.cs | 28 +++++++++++++++++-- .../FastBlocks/ReceiptsSyncFeed.cs | 23 ++++++++++++++- 7 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Api/IApiWithStores.cs b/src/Nethermind/Nethermind.Api/IApiWithStores.cs index eed8ed62430..2444c8422e0 100644 --- a/src/Nethermind/Nethermind.Api/IApiWithStores.cs +++ b/src/Nethermind/Nethermind.Api/IApiWithStores.cs @@ -4,7 +4,6 @@ using Autofac; using Nethermind.Blockchain; using Nethermind.Blockchain.Blocks; -using Nethermind.Blockchain.Find; using Nethermind.Blockchain.Receipts; using Nethermind.Consensus; using Nethermind.Core; @@ -31,6 +30,8 @@ public interface IApiWithStores : IBasicApi IReceiptFinder? ReceiptFinder { get; set; } IReceiptMonitor? ReceiptMonitor { get; set; } IWallet? Wallet { get; set; } + + [SkipServiceCollection] IBlockStore? BadBlocksStore { get; set; } public ContainerBuilder ConfigureContainerBuilderFromApiWithStores(ContainerBuilder builder) diff --git a/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs b/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs index 43b8c6533f1..db988983791 100644 --- a/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs +++ b/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs @@ -40,6 +40,13 @@ public void SetMetadata(byte[] key, byte[] value) return _blockDb.Get(key); } + public bool HasBlock(long blockNumber, Hash256 blockHash) + { + Span dbKey = stackalloc byte[40]; + KeyValueStoreExtensions.GetBlockNumPrefixedKey(blockNumber, blockHash, dbKey); + return _blockDb.Get(dbKey) is not null; + } + private void TruncateToMaxSize() { int toDelete = (int)(_blockDb.GatherMetric().Size - _maxSize!); diff --git a/src/Nethermind/Nethermind.Blockchain/Blocks/IBlockStore.cs b/src/Nethermind/Nethermind.Blockchain/Blocks/IBlockStore.cs index 1d704d19109..3480e5a866d 100644 --- a/src/Nethermind/Nethermind.Blockchain/Blocks/IBlockStore.cs +++ b/src/Nethermind/Nethermind.Blockchain/Blocks/IBlockStore.cs @@ -26,4 +26,5 @@ public interface IBlockStore // These two are used by blocktree. Try not to use them... void SetMetadata(byte[] key, byte[] value); byte[]? GetMetadata(byte[] key); + bool HasBlock(long blockNumber, Hash256 blockHash); } diff --git a/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryBlockStore.cs b/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryBlockStore.cs index 4b76d9bcdb5..6eb4be29bcf 100644 --- a/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryBlockStore.cs +++ b/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryBlockStore.cs @@ -91,4 +91,9 @@ public void SetMetadata(byte[] key, byte[] value) { return _metadataDict.TryGetValue(key, out var value) ? value : readonlyBaseBlockStore.GetMetadata(key); } + + public bool HasBlock(long blockNumber, Hash256 blockHash) + { + return _blockNumDict.ContainsKey(blockNumber); + } } diff --git a/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/BodiesSyncFeedTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/BodiesSyncFeedTests.cs index 8e4e7a52d99..202a3b60c86 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/BodiesSyncFeedTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/BodiesSyncFeedTests.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using FluentAssertions; using Nethermind.Blockchain; +using Nethermind.Blockchain.Blocks; using Nethermind.Blockchain.Synchronization; using Nethermind.Core; using Nethermind.Core.Extensions; @@ -66,6 +67,7 @@ public void Setup() _feed = new BodiesSyncFeed( MainnetSpecProvider.Instance, _syncingToBlockTree, + new BlockStore(_blocksDb), Substitute.For(), _syncConfig, new NullSyncReport(), diff --git a/src/Nethermind/Nethermind.Synchronization/FastBlocks/BodiesSyncFeed.cs b/src/Nethermind/Nethermind.Synchronization/FastBlocks/BodiesSyncFeed.cs index 1152f416054..2fdd1716c53 100644 --- a/src/Nethermind/Nethermind.Synchronization/FastBlocks/BodiesSyncFeed.cs +++ b/src/Nethermind/Nethermind.Synchronization/FastBlocks/BodiesSyncFeed.cs @@ -7,6 +7,7 @@ using Autofac.Features.AttributeFilters; using Microsoft.Extensions.DependencyInjection; using Nethermind.Blockchain; +using Nethermind.Blockchain.Blocks; using Nethermind.Blockchain.Synchronization; using Nethermind.Consensus.Validators; using Nethermind.Core; @@ -34,6 +35,7 @@ public class BodiesSyncFeed : BarrierSyncFeed private readonly long _flushDbInterval; // About every 10GB on mainnet private readonly IBlockTree _blockTree; + private readonly IBlockStore _blockStore; private readonly ISyncConfig _syncConfig; private readonly ISyncReport _syncReport; private readonly ISyncPeerPool _syncPeerPool; @@ -49,6 +51,7 @@ public class BodiesSyncFeed : BarrierSyncFeed public BodiesSyncFeed( ISpecProvider specProvider, IBlockTree blockTree, + IBlockStore blockStore, ISyncPeerPool syncPeerPool, ISyncConfig syncConfig, ISyncReport syncReport, @@ -59,6 +62,7 @@ public BodiesSyncFeed( : base(metadataDb, specProvider, logManager.GetClassLogger()) { _blockTree = blockTree ?? throw new ArgumentNullException(nameof(blockTree)); + _blockStore = blockStore ?? throw new ArgumentNullException(nameof(blockStore)); _syncPeerPool = syncPeerPool ?? throw new ArgumentNullException(nameof(syncPeerPool)); _syncConfig = syncConfig ?? throw new ArgumentNullException(nameof(syncConfig)); _syncReport = syncReport ?? throw new ArgumentNullException(nameof(syncReport)); @@ -131,11 +135,31 @@ private void PostFinishCleanUp() if (ShouldBuildANewBatch()) { BlockInfo?[] infos = new BlockInfo[_requestSize]; - _syncStatusList.GetInfosForBatch(infos); + + long minNumber = 0; + bool needMoreInfo = true; + while (needMoreInfo) + { + token.ThrowIfCancellationRequested(); + needMoreInfo = false; + _syncStatusList.GetInfosForBatch(infos); + + foreach (BlockInfo? blockInfo in infos) + { + minNumber = Math.Max(minNumber, blockInfo.BlockNumber); + if (blockInfo == null) continue; + if (!_blockStore.HasBlock(blockInfo.BlockNumber, blockInfo.BlockHash)) continue; + + _syncStatusList.MarkInserted(blockInfo.BlockNumber); + needMoreInfo = true; + } + } + if (infos[0] is not null) { batch = new BodiesSyncBatch(infos); - batch.MinNumber = infos[0].BlockNumber; + // Used for peer allocation. It pick peer which have the at least this number + batch.MinNumber = minNumber; batch.Prioritized = true; } } diff --git a/src/Nethermind/Nethermind.Synchronization/FastBlocks/ReceiptsSyncFeed.cs b/src/Nethermind/Nethermind.Synchronization/FastBlocks/ReceiptsSyncFeed.cs index cbfce46215c..862aaa4c503 100644 --- a/src/Nethermind/Nethermind.Synchronization/FastBlocks/ReceiptsSyncFeed.cs +++ b/src/Nethermind/Nethermind.Synchronization/FastBlocks/ReceiptsSyncFeed.cs @@ -135,10 +135,31 @@ private void PostFinishCleanUp() { BlockInfo?[] infos = new BlockInfo[_requestSize]; _syncStatusList.GetInfosForBatch(infos); + + long minNumber = 0; + bool needMoreInfo = true; + while (needMoreInfo) + { + token.ThrowIfCancellationRequested(); + needMoreInfo = false; + _syncStatusList.GetInfosForBatch(infos); + + foreach (BlockInfo? blockInfo in infos) + { + minNumber = Math.Max(minNumber, blockInfo.BlockNumber); + + if (blockInfo is null) continue; + if (!_receiptStorage.HasBlock(blockInfo.BlockNumber, blockInfo.BlockHash)) continue; + + _syncStatusList.MarkInserted(blockInfo.BlockNumber); + needMoreInfo = true; + } + } + if (infos[0] is not null) { batch = new ReceiptsSyncBatch(infos); - batch.MinNumber = infos[0].BlockNumber; + batch.MinNumber = minNumber; batch.Prioritized = true; } From a9f509e876ff6ee4c1aca35ea0acabc8d2fe0ba0 Mon Sep 17 00:00:00 2001 From: Amirul Ashraf Date: Sat, 26 Oct 2024 14:45:45 +0800 Subject: [PATCH 02/13] Separate block store and badd block store --- .../Nethermind.Api/IApiWithStores.cs | 2 +- .../Nethermind.Api/NethermindApi.cs | 2 +- .../Nethermind.Blockchain/BlockTree.cs | 4 +- .../Blocks/BadBlockStore.cs | 59 +++++++++++++++++++ .../Blocks/BlockStore.cs | 21 +------ .../Blocks/IBadBlockStore.cs | 13 ++++ .../Tracing/GethStyleTracer.cs | 4 +- .../Blockchain/TestBlockchain.cs | 2 +- .../Builders/BlockTreeBuilder.cs | 8 +-- ...ulateReadOnlyBlocksProcessingEnvFactory.cs | 2 +- .../Steps/InitializeBlockTree.cs | 2 +- .../Modules/DebugModuleTests.cs | 4 +- .../Modules/DebugModule/DebugBridge.cs | 4 +- .../Modules/DebugModule/DebugModuleFactory.cs | 4 +- .../Ethereum/ContextWithMocks.cs | 2 +- 15 files changed, 93 insertions(+), 40 deletions(-) create mode 100644 src/Nethermind/Nethermind.Blockchain/Blocks/BadBlockStore.cs create mode 100644 src/Nethermind/Nethermind.Blockchain/Blocks/IBadBlockStore.cs diff --git a/src/Nethermind/Nethermind.Api/IApiWithStores.cs b/src/Nethermind/Nethermind.Api/IApiWithStores.cs index eed8ed62430..09142b2c0ed 100644 --- a/src/Nethermind/Nethermind.Api/IApiWithStores.cs +++ b/src/Nethermind/Nethermind.Api/IApiWithStores.cs @@ -31,7 +31,7 @@ public interface IApiWithStores : IBasicApi IReceiptFinder? ReceiptFinder { get; set; } IReceiptMonitor? ReceiptMonitor { get; set; } IWallet? Wallet { get; set; } - IBlockStore? BadBlocksStore { get; set; } + IBadBlockStore? BadBlocksStore { get; set; } public ContainerBuilder ConfigureContainerBuilderFromApiWithStores(ContainerBuilder builder) { diff --git a/src/Nethermind/Nethermind.Api/NethermindApi.cs b/src/Nethermind/Nethermind.Api/NethermindApi.cs index 566a094eaa0..bc1288d8529 100644 --- a/src/Nethermind/Nethermind.Api/NethermindApi.cs +++ b/src/Nethermind/Nethermind.Api/NethermindApi.cs @@ -225,7 +225,7 @@ public ISealEngine SealEngine public BackgroundTaskScheduler BackgroundTaskScheduler { get; set; } = null!; public CensorshipDetector CensorshipDetector { get; set; } = null!; public IWallet? Wallet { get; set; } - public IBlockStore? BadBlocksStore { get; set; } + public IBadBlockStore? BadBlocksStore { get; set; } public ITransactionComparerProvider? TransactionComparerProvider { get; set; } public IWebSocketsManager WebSocketsManager { get; set; } = new WebSocketsManager(); diff --git a/src/Nethermind/Nethermind.Blockchain/BlockTree.cs b/src/Nethermind/Nethermind.Blockchain/BlockTree.cs index c7338fa2aec..9cf43a62c25 100644 --- a/src/Nethermind/Nethermind.Blockchain/BlockTree.cs +++ b/src/Nethermind/Nethermind.Blockchain/BlockTree.cs @@ -45,7 +45,7 @@ public partial class BlockTree : IBlockTree private readonly IHeaderStore _headerStore; private readonly IDb _blockInfoDb; private readonly IDb _metadataDb; - private readonly IBlockStore _badBlockStore; + private readonly IBadBlockStore _badBlockStore; private readonly LruCache _invalidBlocks = new(128, 128, "invalid blocks"); @@ -113,7 +113,7 @@ public BlockTree( IHeaderStore? headerDb, IDb? blockInfoDb, IDb? metadataDb, - IBlockStore? badBlockStore, + IBadBlockStore? badBlockStore, IChainLevelInfoRepository? chainLevelInfoRepository, ISpecProvider? specProvider, IBloomStorage? bloomStorage, diff --git a/src/Nethermind/Nethermind.Blockchain/Blocks/BadBlockStore.cs b/src/Nethermind/Nethermind.Blockchain/Blocks/BadBlockStore.cs new file mode 100644 index 00000000000..fa6430c3ef6 --- /dev/null +++ b/src/Nethermind/Nethermind.Blockchain/Blocks/BadBlockStore.cs @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using System.Linq; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Db; +using Nethermind.Serialization.Rlp; + +namespace Nethermind.Blockchain.Blocks; + +public class BadBlockStore(IDb blockDb, long? maxSize = null) : IBadBlockStore +{ + private readonly BlockDecoder _blockDecoder = new(); + + public void Insert(Block block, WriteFlags writeFlags = WriteFlags.None) + { + if (block.Hash is null) + { + throw new InvalidOperationException("An attempt to store a block with a null hash."); + } + + // if we carry Rlp from the network message all the way here we could avoid encoding back to RLP here + // Although cpu is the main bottleneck since NettyRlpStream uses pooled memory which avoid unnecessary allocations.. + using NettyRlpStream newRlp = _blockDecoder.EncodeToNewNettyStream(block); + + blockDb.Set(block.Number, block.Hash, newRlp.AsSpan(), writeFlags); + + if (maxSize is not null) + { + TruncateToMaxSize(); + } + } + + public IEnumerable GetAll() + { + return blockDb.GetAllValues(true).Select(bytes => _blockDecoder.Decode(ByteArrayExtensions.AsRlpStream((byte[]?)bytes))); + } + + private void TruncateToMaxSize() + { + int toDelete = (int)(blockDb.GatherMetric().Size - maxSize!); + if (toDelete > 0) + { + foreach (var blockToDelete in GetAll().Take(toDelete)) + { + Delete(blockToDelete.Number, blockToDelete.Hash); + } + } + } + + private void Delete(long blockNumber, Hash256 blockHash) + { + blockDb.Delete(blockNumber, blockHash); + blockDb.Remove(blockHash.Bytes); + } +} diff --git a/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs b/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs index 43b8c6533f1..5c45e29384e 100644 --- a/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs +++ b/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs @@ -22,12 +22,10 @@ public class BlockStore : IBlockStore private readonly ClockCache _blockCache = new(CacheSize); - private readonly long? _maxSize; - public BlockStore(IDb blockDb, long? maxSize = null) + public BlockStore(IDb blockDb) { _blockDb = blockDb; - _maxSize = maxSize; } public void SetMetadata(byte[] key, byte[] value) @@ -40,18 +38,6 @@ public void SetMetadata(byte[] key, byte[] value) return _blockDb.Get(key); } - private void TruncateToMaxSize() - { - int toDelete = (int)(_blockDb.GatherMetric().Size - _maxSize!); - if (toDelete > 0) - { - foreach (var blockToDelete in GetAll().Take(toDelete)) - { - Delete(blockToDelete.Number, blockToDelete.Hash); - } - } - } - public void Insert(Block block, WriteFlags writeFlags = WriteFlags.None) { if (block.Hash is null) @@ -64,11 +50,6 @@ public void Insert(Block block, WriteFlags writeFlags = WriteFlags.None) using NettyRlpStream newRlp = _blockDecoder.EncodeToNewNettyStream(block); _blockDb.Set(block.Number, block.Hash, newRlp.AsSpan(), writeFlags); - - if (_maxSize is not null) - { - TruncateToMaxSize(); - } } private static void GetBlockNumPrefixedKey(long blockNumber, Hash256 blockHash, Span output) diff --git a/src/Nethermind/Nethermind.Blockchain/Blocks/IBadBlockStore.cs b/src/Nethermind/Nethermind.Blockchain/Blocks/IBadBlockStore.cs new file mode 100644 index 00000000000..7c71f63eaae --- /dev/null +++ b/src/Nethermind/Nethermind.Blockchain/Blocks/IBadBlockStore.cs @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using Nethermind.Core; + +namespace Nethermind.Blockchain.Blocks; + +public interface IBadBlockStore +{ + void Insert(Block block, WriteFlags writeFlags = WriteFlags.None); + IEnumerable GetAll(); +} diff --git a/src/Nethermind/Nethermind.Consensus/Tracing/GethStyleTracer.cs b/src/Nethermind/Nethermind.Consensus/Tracing/GethStyleTracer.cs index 3e240bef779..7ff20c4d203 100644 --- a/src/Nethermind/Nethermind.Consensus/Tracing/GethStyleTracer.cs +++ b/src/Nethermind/Nethermind.Consensus/Tracing/GethStyleTracer.cs @@ -28,7 +28,7 @@ namespace Nethermind.Consensus.Tracing; public class GethStyleTracer : IGethStyleTracer { - private readonly IBlockStore _badBlockStore; + private readonly IBadBlockStore _badBlockStore; private readonly IBlockTree _blockTree; private readonly ISpecProvider _specProvider; private readonly ChangeableTransactionProcessorAdapter _transactionProcessorAdapter; @@ -41,7 +41,7 @@ public GethStyleTracer(IBlockchainProcessor processor, IWorldState worldState, IReceiptStorage receiptStorage, IBlockTree blockTree, - IBlockStore badBlockStore, + IBadBlockStore badBlockStore, ISpecProvider specProvider, ChangeableTransactionProcessorAdapter transactionProcessorAdapter, IFileSystem fileSystem) diff --git a/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs b/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs index fb5118b4861..d9f198e6e5b 100644 --- a/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs +++ b/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs @@ -173,7 +173,7 @@ protected virtual async Task Build(ISpecProvider? specProvider = new HeaderStore(DbProvider.HeadersDb, DbProvider.BlockNumbersDb), DbProvider.BlockInfosDb, DbProvider.MetadataDb, - new BlockStore(new TestMemDb(), 100), + new BadBlockStore(new TestMemDb(), 100), ChainLevelInfoRepository, SpecProvider, NullBloomStorage.Instance, diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/BlockTreeBuilder.cs b/src/Nethermind/Nethermind.Core.Test/Builders/BlockTreeBuilder.cs index 52f1039b9cf..1c2bdc8419b 100644 --- a/src/Nethermind/Nethermind.Core.Test/Builders/BlockTreeBuilder.cs +++ b/src/Nethermind/Nethermind.Core.Test/Builders/BlockTreeBuilder.cs @@ -143,12 +143,12 @@ public IHeaderStore HeaderStore public IDb MetadataDb { get; set; } - private IBlockStore? _badBlockStore; - public IBlockStore BadBlockStore + private IBadBlockStore? _badBlockStore; + public IBadBlockStore BadBlockStore { get { - return _badBlockStore ??= new BlockStore(BadBlocksDb, 100); + return _badBlockStore ??= new BadBlockStore(BadBlocksDb, 100); } set { @@ -411,7 +411,7 @@ public BlockTreeBuilder WithBlockStore(IBlockStore blockStore) return this; } - public BlockTreeBuilder WithBadBlockStore(IBlockStore blockStore) + public BlockTreeBuilder WithBadBlockStore(IBadBlockStore blockStore) { BadBlockStore = blockStore; return this; diff --git a/src/Nethermind/Nethermind.Facade/Simulate/SimulateReadOnlyBlocksProcessingEnvFactory.cs b/src/Nethermind/Nethermind.Facade/Simulate/SimulateReadOnlyBlocksProcessingEnvFactory.cs index 99078525673..077d5ca67fa 100644 --- a/src/Nethermind/Nethermind.Facade/Simulate/SimulateReadOnlyBlocksProcessingEnvFactory.cs +++ b/src/Nethermind/Nethermind.Facade/Simulate/SimulateReadOnlyBlocksProcessingEnvFactory.cs @@ -47,7 +47,7 @@ private static BlockTree CreateTempBlockTree(IReadOnlyDbProvider readOnlyDbProvi const int badBlocksStored = 1; SimulateDictionaryBlockStore tmpBlockStore = new(mainblockStore); - IBlockStore badBlockStore = new BlockStore(editableDbProvider.BadBlocksDb, badBlocksStored); + IBadBlockStore badBlockStore = new BadBlockStore(editableDbProvider.BadBlocksDb, badBlocksStored); return new(tmpBlockStore, tmpHeaderStore, diff --git a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockTree.cs b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockTree.cs index 0cd10de51f0..765b8c16dbd 100644 --- a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockTree.cs +++ b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockTree.cs @@ -53,7 +53,7 @@ public Task Execute(CancellationToken cancellationToken) IBlockStore blockStore = new BlockStore(_get.DbProvider.BlocksDb); IHeaderStore headerStore = new HeaderStore(_get.DbProvider.HeadersDb, _get.DbProvider.BlockNumbersDb); - IBlockStore badBlockStore = _set.BadBlocksStore = new BlockStore(_get.DbProvider.BadBlocksDb, initConfig.BadBlocksStored); + IBadBlockStore badBlockStore = _set.BadBlocksStore = new BadBlockStore(_get.DbProvider.BadBlocksDb, initConfig.BadBlocksStored); IBlockTree blockTree = _set.BlockTree = new BlockTree( blockStore, diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs index fed0e3ccfba..aed36e88f60 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs @@ -276,8 +276,8 @@ private BlockTree BuildBlockTree(Func? build [Test] public void Debug_getBadBlocks_test() { - IBlockStore badBlocksStore = null!; - BlockTree blockTree = BuildBlockTree(b => b.WithBadBlockStore(badBlocksStore = new BlockStore(b.BadBlocksDb))); + IBadBlockStore badBlocksStore = null!; + BlockTree blockTree = BuildBlockTree(b => b.WithBadBlockStore(badBlocksStore = new BadBlockStore(b.BadBlocksDb))); Block block0 = Build.A.Block.WithNumber(0).WithDifficulty(1).TestObject; Block block1 = Build.A.Block.WithNumber(1).WithDifficulty(2).WithParent(block0).TestObject; diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugBridge.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugBridge.cs index 0646695c919..f9886a97848 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugBridge.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugBridge.cs @@ -32,7 +32,7 @@ public class DebugBridge : IDebugBridge private readonly IReceiptsMigration _receiptsMigration; private readonly ISpecProvider _specProvider; private readonly ISyncModeSelector _syncModeSelector; - private readonly IBlockStore _badBlockStore; + private readonly IBadBlockStore _badBlockStore; private readonly IBlockStore _blockStore; private readonly Dictionary _dbMappings; @@ -45,7 +45,7 @@ public DebugBridge( IReceiptsMigration receiptsMigration, ISpecProvider specProvider, ISyncModeSelector syncModeSelector, - IBlockStore badBlockStore) + IBadBlockStore badBlockStore) { _configProvider = configProvider ?? throw new ArgumentNullException(nameof(configProvider)); _tracer = tracer ?? throw new ArgumentNullException(nameof(tracer)); diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugModuleFactory.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugModuleFactory.cs index 651ae2e0b69..9856232921d 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugModuleFactory.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugModuleFactory.cs @@ -36,7 +36,7 @@ public class DebugModuleFactory : ModuleFactoryBase private readonly IReadOnlyDbProvider _dbProvider; private readonly IReadOnlyBlockTree _blockTree; private readonly ISyncModeSelector _syncModeSelector; - private readonly IBlockStore _badBlockStore; + private readonly IBadBlockStore _badBlockStore; private readonly IFileSystem _fileSystem; private readonly ILogger _logger; @@ -53,7 +53,7 @@ public DebugModuleFactory( IConfigProvider configProvider, ISpecProvider specProvider, ISyncModeSelector syncModeSelector, - IBlockStore badBlockStore, + IBadBlockStore badBlockStore, IFileSystem fileSystem, ILogManager logManager) { diff --git a/src/Nethermind/Nethermind.Runner.Test/Ethereum/ContextWithMocks.cs b/src/Nethermind/Nethermind.Runner.Test/Ethereum/ContextWithMocks.cs index 94ab1d2ea4b..190f5cfb667 100644 --- a/src/Nethermind/Nethermind.Runner.Test/Ethereum/ContextWithMocks.cs +++ b/src/Nethermind/Nethermind.Runner.Test/Ethereum/ContextWithMocks.cs @@ -118,7 +118,7 @@ public static NethermindApi ContextWithMocks() BlockProductionPolicy = Substitute.For(), BetterPeerStrategy = Substitute.For(), ReceiptMonitor = Substitute.For(), - BadBlocksStore = Substitute.For(), + BadBlocksStore = Substitute.For(), ApiWithNetworkServiceContainer = new ContainerBuilder() .AddSingleton(Substitute.For()) From 0b1c5731a86ac085f89d88dae4ee629330d9a5b5 Mon Sep 17 00:00:00 2001 From: Amirul Ashraf Date: Sat, 26 Oct 2024 15:04:38 +0800 Subject: [PATCH 03/13] Separate block store and bad block store --- .../Blocks/BadBlockStoreTests.cs | 56 +++++++++++++++++++ .../Blocks/BadBlockStore.cs | 9 +-- .../Blocks/BlockStore.cs | 40 +++++-------- .../Blocks/IBlockStore.cs | 4 +- .../Simulate/SimulateDictionaryBlockStore.cs | 14 +---- .../Steps/InitializeBlockTree.cs | 2 +- .../Modules/DebugModuleTests.cs | 2 +- .../Modules/DebugModule/DebugBridge.cs | 5 +- 8 files changed, 78 insertions(+), 54 deletions(-) create mode 100644 src/Nethermind/Nethermind.Blockchain.Test/Blocks/BadBlockStoreTests.cs diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Blocks/BadBlockStoreTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Blocks/BadBlockStoreTests.cs new file mode 100644 index 00000000000..d7a5c98a533 --- /dev/null +++ b/src/Nethermind/Nethermind.Blockchain.Test/Blocks/BadBlockStoreTests.cs @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using Nethermind.Blockchain.Blocks; +using Nethermind.Core; +using Nethermind.Core.Test; +using Nethermind.Core.Test.Builders; +using NUnit.Framework; + +namespace Nethermind.Blockchain.Test.Blocks; + +public class BadBlockStoreTests +{ + [Test] + public void Test_CanInsert() + { + BadBlockStore badBlockStore = new BadBlockStore(new TestMemDb(), 10); + + List toAdd = new() + { + Build.A.Block.WithNumber(1).TestObject, + Build.A.Block.WithNumber(2).TestObject, + Build.A.Block.WithNumber(3).TestObject, + }; + + foreach (Block block in toAdd) + { + badBlockStore.Insert(block); + } + + badBlockStore.GetAll().Should().BeEquivalentTo(toAdd); + } + + [Test] + public void Test_LimitInsertedBlock() + { + BadBlockStore badBlockStore = new BadBlockStore(new TestMemDb(), 2); + + List toAdd = new() + { + Build.A.Block.WithNumber(1).TestObject, + Build.A.Block.WithNumber(2).TestObject, + Build.A.Block.WithNumber(3).TestObject, + }; + + foreach (Block block in toAdd) + { + badBlockStore.Insert(block); + } + + badBlockStore.GetAll().Count().Should().Be(2); + } +} diff --git a/src/Nethermind/Nethermind.Blockchain/Blocks/BadBlockStore.cs b/src/Nethermind/Nethermind.Blockchain/Blocks/BadBlockStore.cs index fa6430c3ef6..1181ce30b7c 100644 --- a/src/Nethermind/Nethermind.Blockchain/Blocks/BadBlockStore.cs +++ b/src/Nethermind/Nethermind.Blockchain/Blocks/BadBlockStore.cs @@ -11,7 +11,7 @@ namespace Nethermind.Blockchain.Blocks; -public class BadBlockStore(IDb blockDb, long? maxSize = null) : IBadBlockStore +public class BadBlockStore(IDb blockDb, long maxSize) : IBadBlockStore { private readonly BlockDecoder _blockDecoder = new(); @@ -25,13 +25,9 @@ public void Insert(Block block, WriteFlags writeFlags = WriteFlags.None) // if we carry Rlp from the network message all the way here we could avoid encoding back to RLP here // Although cpu is the main bottleneck since NettyRlpStream uses pooled memory which avoid unnecessary allocations.. using NettyRlpStream newRlp = _blockDecoder.EncodeToNewNettyStream(block); - blockDb.Set(block.Number, block.Hash, newRlp.AsSpan(), writeFlags); - if (maxSize is not null) - { - TruncateToMaxSize(); - } + TruncateToMaxSize(); } public IEnumerable GetAll() @@ -54,6 +50,5 @@ private void TruncateToMaxSize() private void Delete(long blockNumber, Hash256 blockHash) { blockDb.Delete(blockNumber, blockHash); - blockDb.Remove(blockHash.Bytes); } } diff --git a/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs b/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs index 5c45e29384e..98bb1030fff 100644 --- a/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs +++ b/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs @@ -3,8 +3,6 @@ using System; using System.Buffers; -using System.Collections.Generic; -using System.Linq; using Nethermind.Core; using Nethermind.Core.Caching; using Nethermind.Core.Crypto; @@ -14,28 +12,22 @@ namespace Nethermind.Blockchain.Blocks; -public class BlockStore : IBlockStore +public class BlockStore(IDb blockDb) : IBlockStore { - private readonly IDb _blockDb; private readonly BlockDecoder _blockDecoder = new(); public const int CacheSize = 128 + 32; private readonly ClockCache _blockCache = new(CacheSize); - public BlockStore(IDb blockDb) - { - _blockDb = blockDb; - } - public void SetMetadata(byte[] key, byte[] value) { - _blockDb.Set(key, value); + blockDb.Set(key, value); } public byte[]? GetMetadata(byte[] key) { - return _blockDb.Get(key); + return blockDb.Get(key); } public void Insert(Block block, WriteFlags writeFlags = WriteFlags.None) @@ -49,7 +41,7 @@ public void Insert(Block block, WriteFlags writeFlags = WriteFlags.None) // Although cpu is the main bottleneck since NettyRlpStream uses pooled memory which avoid unnecessary allocations.. using NettyRlpStream newRlp = _blockDecoder.EncodeToNewNettyStream(block); - _blockDb.Set(block.Number, block.Hash, newRlp.AsSpan(), writeFlags); + blockDb.Set(block.Number, block.Hash, newRlp.AsSpan(), writeFlags); } private static void GetBlockNumPrefixedKey(long blockNumber, Hash256 blockHash, Span output) @@ -61,24 +53,24 @@ private static void GetBlockNumPrefixedKey(long blockNumber, Hash256 blockHash, public void Delete(long blockNumber, Hash256 blockHash) { _blockCache.Delete(blockHash); - _blockDb.Delete(blockNumber, blockHash); - _blockDb.Remove(blockHash.Bytes); + blockDb.Delete(blockNumber, blockHash); + blockDb.Remove(blockHash.Bytes); } public Block? Get(long blockNumber, Hash256 blockHash, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = false) { - Block? b = _blockDb.Get(blockNumber, blockHash, _blockDecoder, _blockCache, rlpBehaviors, shouldCache); + Block? b = blockDb.Get(blockNumber, blockHash, _blockDecoder, _blockCache, rlpBehaviors, shouldCache); if (b is not null) return b; - return _blockDb.Get(blockHash, _blockDecoder, _blockCache, rlpBehaviors, shouldCache); + return blockDb.Get(blockHash, _blockDecoder, _blockCache, rlpBehaviors, shouldCache); } - public byte[]? GetRaw(long blockNumber, Hash256 blockHash) + public byte[]? GetRlp(long blockNumber, Hash256 blockHash) { Span dbKey = stackalloc byte[40]; KeyValueStoreExtensions.GetBlockNumPrefixedKey(blockNumber, blockHash, dbKey); - var b = _blockDb.Get(dbKey); + var b = blockDb.Get(dbKey); if (b is not null) return b; - return _blockDb.Get(blockHash); + return blockDb.Get(blockHash); } public ReceiptRecoveryBlock? GetReceiptRecoveryBlock(long blockNumber, Hash256 blockHash) @@ -86,8 +78,8 @@ public void Delete(long blockNumber, Hash256 blockHash) Span keyWithBlockNumber = stackalloc byte[40]; GetBlockNumPrefixedKey(blockNumber, blockHash, keyWithBlockNumber); - MemoryManager? memoryOwner = _blockDb.GetOwnedMemory(keyWithBlockNumber); - memoryOwner ??= _blockDb.GetOwnedMemory(blockHash.Bytes); + MemoryManager? memoryOwner = blockDb.GetOwnedMemory(keyWithBlockNumber); + memoryOwner ??= blockDb.GetOwnedMemory(blockHash.Bytes); return BlockDecoder.DecodeToReceiptRecoveryBlock(memoryOwner, memoryOwner?.Memory ?? Memory.Empty, RlpBehaviors.None); } @@ -96,10 +88,4 @@ public void Cache(Block block) { _blockCache.Set(block.Hash, block); } - - public IEnumerable GetAll() - { - return _blockDb.GetAllValues(true).Select(bytes => _blockDecoder.Decode(bytes.AsRlpStream())); - } - } diff --git a/src/Nethermind/Nethermind.Blockchain/Blocks/IBlockStore.cs b/src/Nethermind/Nethermind.Blockchain/Blocks/IBlockStore.cs index 1d704d19109..05bfec926de 100644 --- a/src/Nethermind/Nethermind.Blockchain/Blocks/IBlockStore.cs +++ b/src/Nethermind/Nethermind.Blockchain/Blocks/IBlockStore.cs @@ -17,12 +17,10 @@ public interface IBlockStore void Insert(Block block, WriteFlags writeFlags = WriteFlags.None); void Delete(long blockNumber, Hash256 blockHash); Block? Get(long blockNumber, Hash256 blockHash, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool shouldCache = true); - byte[]? GetRaw(long blockNumber, Hash256 blockHash); - IEnumerable GetAll(); + byte[]? GetRlp(long blockNumber, Hash256 blockHash); ReceiptRecoveryBlock? GetReceiptRecoveryBlock(long blockNumber, Hash256 blockHash); void Cache(Block block); - // These two are used by blocktree. Try not to use them... void SetMetadata(byte[] key, byte[] value); byte[]? GetMetadata(byte[] key); diff --git a/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryBlockStore.cs b/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryBlockStore.cs index 4b76d9bcdb5..c3825373730 100644 --- a/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryBlockStore.cs +++ b/src/Nethermind/Nethermind.Facade/Simulate/SimulateDictionaryBlockStore.cs @@ -45,24 +45,14 @@ public void Delete(long blockNumber, Hash256 blockHash) return block; } - public byte[]? GetRaw(long blockNumber, Hash256 blockHash) + public byte[]? GetRlp(long blockNumber, Hash256 blockHash) { if (_blockNumDict.TryGetValue(blockNumber, out Block block)) { using NettyRlpStream newRlp = _blockDecoder.EncodeToNewNettyStream(block); return newRlp.AsSpan().ToArray(); } - return readonlyBaseBlockStore.GetRaw(blockNumber, blockHash); - } - - public IEnumerable GetAll() - { - var allBlocks = new HashSet(readonlyBaseBlockStore.GetAll()); - foreach (Block block in _blockDict.Values) - { - allBlocks.Add(block); - } - return allBlocks; + return readonlyBaseBlockStore.GetRlp(blockNumber, blockHash); } public ReceiptRecoveryBlock? GetReceiptRecoveryBlock(long blockNumber, Hash256 blockHash) diff --git a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockTree.cs b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockTree.cs index 765b8c16dbd..dbd7011fd17 100644 --- a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockTree.cs +++ b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockTree.cs @@ -53,7 +53,7 @@ public Task Execute(CancellationToken cancellationToken) IBlockStore blockStore = new BlockStore(_get.DbProvider.BlocksDb); IHeaderStore headerStore = new HeaderStore(_get.DbProvider.HeadersDb, _get.DbProvider.BlockNumbersDb); - IBadBlockStore badBlockStore = _set.BadBlocksStore = new BadBlockStore(_get.DbProvider.BadBlocksDb, initConfig.BadBlocksStored); + IBadBlockStore badBlockStore = _set.BadBlocksStore = new BadBlockStore(_get.DbProvider.BadBlocksDb, initConfig.BadBlocksStored ?? 100); IBlockTree blockTree = _set.BlockTree = new BlockTree( blockStore, diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs index aed36e88f60..15038cf51b9 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs @@ -277,7 +277,7 @@ private BlockTree BuildBlockTree(Func? build public void Debug_getBadBlocks_test() { IBadBlockStore badBlocksStore = null!; - BlockTree blockTree = BuildBlockTree(b => b.WithBadBlockStore(badBlocksStore = new BadBlockStore(b.BadBlocksDb))); + BlockTree blockTree = BuildBlockTree(b => b.WithBadBlockStore(badBlocksStore = new BadBlockStore(b.BadBlocksDb, 100))); Block block0 = Build.A.Block.WithNumber(0).WithDifficulty(1).TestObject; Block block1 = Build.A.Block.WithNumber(1).WithDifficulty(2).WithParent(block0).TestObject; diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugBridge.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugBridge.cs index f9886a97848..df9dea1b87f 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugBridge.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugBridge.cs @@ -177,14 +177,13 @@ public byte[] GetBlockRlp(Hash256 blockHash) { BlockHeader? header = _blockTree.FindHeader(blockHash); if (header is null) return null; - - return _blockStore.GetRaw(header.Number, blockHash); + return _blockStore.GetRlp(header.Number, blockHash); } public byte[] GetBlockRlp(long number) { Hash256 hash = _blockTree.FindHash(number); - return hash is null ? null : _blockStore.GetRaw(number, hash); + return hash is null ? null : _blockStore.GetRlp(number, hash); } public Block? GetBlock(BlockParameter param) From 4d11ef58c38e930407ecd2acd8a67b32771461e5 Mon Sep 17 00:00:00 2001 From: Amirul Ashraf Date: Sat, 26 Oct 2024 15:12:05 +0800 Subject: [PATCH 04/13] Fix test --- .../Blocks/BadBlockStoreTests.cs | 2 +- .../Nethermind.Blockchain/Blocks/BadBlockStore.cs | 2 -- src/Nethermind/Nethermind.Db/MemDb.cs | 8 ++++++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Blocks/BadBlockStoreTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Blocks/BadBlockStoreTests.cs index d7a5c98a533..c817e186987 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Blocks/BadBlockStoreTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Blocks/BadBlockStoreTests.cs @@ -35,7 +35,7 @@ public void Test_CanInsert() } [Test] - public void Test_LimitInsertedBlock() + public void Test_LimitStoredBlock() { BadBlockStore badBlockStore = new BadBlockStore(new TestMemDb(), 2); diff --git a/src/Nethermind/Nethermind.Blockchain/Blocks/BadBlockStore.cs b/src/Nethermind/Nethermind.Blockchain/Blocks/BadBlockStore.cs index 1181ce30b7c..d9a8356113e 100644 --- a/src/Nethermind/Nethermind.Blockchain/Blocks/BadBlockStore.cs +++ b/src/Nethermind/Nethermind.Blockchain/Blocks/BadBlockStore.cs @@ -22,8 +22,6 @@ public void Insert(Block block, WriteFlags writeFlags = WriteFlags.None) throw new InvalidOperationException("An attempt to store a block with a null hash."); } - // if we carry Rlp from the network message all the way here we could avoid encoding back to RLP here - // Although cpu is the main bottleneck since NettyRlpStream uses pooled memory which avoid unnecessary allocations.. using NettyRlpStream newRlp = _blockDecoder.EncodeToNewNettyStream(block); blockDb.Set(block.Number, block.Hash, newRlp.AsSpan(), writeFlags); diff --git a/src/Nethermind/Nethermind.Db/MemDb.cs b/src/Nethermind/Nethermind.Db/MemDb.cs index 5d78c8e68b5..fc6972d8785 100644 --- a/src/Nethermind/Nethermind.Db/MemDb.cs +++ b/src/Nethermind/Nethermind.Db/MemDb.cs @@ -146,5 +146,13 @@ public virtual void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags } _db[key] = value; } + + public IDbMeta.DbMetric GatherMetric(bool includeSharedCache = false) + { + return new IDbMeta.DbMetric() + { + Size = Count + }; + } } } From f2ba192a90f78395b0ad98ecd752b70c5592f3c3 Mon Sep 17 00:00:00 2001 From: Amirul Ashraf Date: Mon, 28 Oct 2024 09:35:38 +0800 Subject: [PATCH 05/13] Dont' redownload existing blocks --- .../Nethermind.Api/IApiWithStores.cs | 1 + .../Nethermind.Api/NethermindApi.cs | 1 + .../Blocks/BlockStore.cs | 2 +- .../Steps/InitializeBlockTree.cs | 2 +- .../FastBlocks/SyncStatusListTests.cs | 38 ++++++++- .../FastBlocks/BodiesSyncFeed.cs | 23 ++---- .../FastBlocks/ReceiptsSyncFeed.cs | 35 +++------ .../FastBlocks/SyncStatusList.cs | 78 ++++++++++++++++++- 8 files changed, 135 insertions(+), 45 deletions(-) diff --git a/src/Nethermind/Nethermind.Api/IApiWithStores.cs b/src/Nethermind/Nethermind.Api/IApiWithStores.cs index 459b92e3339..31f1740763e 100644 --- a/src/Nethermind/Nethermind.Api/IApiWithStores.cs +++ b/src/Nethermind/Nethermind.Api/IApiWithStores.cs @@ -31,6 +31,7 @@ public interface IApiWithStores : IBasicApi IReceiptMonitor? ReceiptMonitor { get; set; } IWallet? Wallet { get; set; } IBadBlockStore? BadBlocksStore { get; set; } + IBlockStore BlockStore { get; set; } public ContainerBuilder ConfigureContainerBuilderFromApiWithStores(ContainerBuilder builder) { diff --git a/src/Nethermind/Nethermind.Api/NethermindApi.cs b/src/Nethermind/Nethermind.Api/NethermindApi.cs index bc1288d8529..31694188be8 100644 --- a/src/Nethermind/Nethermind.Api/NethermindApi.cs +++ b/src/Nethermind/Nethermind.Api/NethermindApi.cs @@ -226,6 +226,7 @@ public ISealEngine SealEngine public CensorshipDetector CensorshipDetector { get; set; } = null!; public IWallet? Wallet { get; set; } public IBadBlockStore? BadBlocksStore { get; set; } + public IBlockStore BlockStore { get; set; } = null!; public ITransactionComparerProvider? TransactionComparerProvider { get; set; } public IWebSocketsManager WebSocketsManager { get; set; } = new WebSocketsManager(); diff --git a/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs b/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs index e303768bd6b..d4de86e653c 100644 --- a/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs +++ b/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs @@ -34,7 +34,7 @@ public bool HasBlock(long blockNumber, Hash256 blockHash) { Span dbKey = stackalloc byte[40]; KeyValueStoreExtensions.GetBlockNumPrefixedKey(blockNumber, blockHash, dbKey); - return blockDb.Get(dbKey) is not null; + return blockDb.KeyExists(dbKey); } public void Insert(Block block, WriteFlags writeFlags = WriteFlags.None) diff --git a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockTree.cs b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockTree.cs index dbd7011fd17..d23554314a8 100644 --- a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockTree.cs +++ b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockTree.cs @@ -51,7 +51,7 @@ public Task Execute(CancellationToken cancellationToken) IChainLevelInfoRepository chainLevelInfoRepository = _set.ChainLevelInfoRepository = new ChainLevelInfoRepository(_get.DbProvider!.BlockInfosDb); - IBlockStore blockStore = new BlockStore(_get.DbProvider.BlocksDb); + IBlockStore blockStore = _set.BlockStore = new BlockStore(_get.DbProvider.BlocksDb); IHeaderStore headerStore = new HeaderStore(_get.DbProvider.HeadersDb, _get.DbProvider.BlockNumbersDb); IBadBlockStore badBlockStore = _set.BadBlocksStore = new BadBlockStore(_get.DbProvider.BadBlocksDb, initConfig.BadBlocksStored ?? 100); diff --git a/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/SyncStatusListTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/SyncStatusListTests.cs index 98a375ba26a..9bd37ffc643 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/SyncStatusListTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/SyncStatusListTests.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using FluentAssertions; @@ -10,6 +11,7 @@ using Nethermind.Core.Test.Builders; using Nethermind.Synchronization.FastBlocks; using NSubstitute; +using NSubstitute.Core; using NUnit.Framework; namespace Nethermind.Synchronization.Test.FastBlocks; @@ -50,12 +52,44 @@ public void Will_not_go_below_ancient_barrier() blockTree.FindCanonicalBlockInfo(Arg.Any()).Returns(new BlockInfo(TestItem.KeccakA, 0)); SyncStatusList syncStatusList = new SyncStatusList(blockTree, 1000, null, 900); - BlockInfo?[] infos = new BlockInfo?[500]; - syncStatusList.GetInfosForBatch(infos); + BlockInfo?[] infos; + syncStatusList.TryGetInfosForBatch(500, (_) => false, out infos); infos.Count((it) => it is not null).Should().Be(101); } + [Test] + public void Will_skip_existing_keys() + { + IBlockTree blockTree = Substitute.For(); + blockTree.FindCanonicalBlockInfo(Arg.Any()) + .Returns((Func) ((ci) => + { + long blockNumber = (long)ci[0]; + return new BlockInfo(TestItem.KeccakA, 0) + { + BlockNumber = blockNumber + }; + })); + + SyncStatusList syncStatusList = new SyncStatusList(blockTree, 100000, null, 1000); + + HashSet needToFetchBlocks = [99999, 99995, 99950, 99000, 99001, 99003, 85000]; + + List TryGetInfos() + { + BlockInfo?[] infos; + syncStatusList.TryGetInfosForBatch(50, (bi) => !needToFetchBlocks.Contains(bi.BlockNumber), out infos); + return infos.Where(bi => bi != null).Select((bi) => bi!.BlockNumber).ToList(); + } + + TryGetInfos().Should().BeEquivalentTo([99999, 99995]); // first two as it will try the first 50 only + TryGetInfos().Should().BeEquivalentTo([99950]); // Then the next 50 + TryGetInfos().Should().BeEquivalentTo([99000, 99001, 99003]); // If the next 50 failed, it will try looking far back. + TryGetInfos().Should().BeEmpty(); // If it look far back enough and still does not find anything it will just return so that progress can update. + TryGetInfos().Should().BeEquivalentTo([85000]); // But as the existing blocks was already marked as inserted, it should be able to make progress on later call. + } + [Test] public void Can_read_back_all_parallel_set_values() { diff --git a/src/Nethermind/Nethermind.Synchronization/FastBlocks/BodiesSyncFeed.cs b/src/Nethermind/Nethermind.Synchronization/FastBlocks/BodiesSyncFeed.cs index 2fdd1716c53..3ee65e153c9 100644 --- a/src/Nethermind/Nethermind.Synchronization/FastBlocks/BodiesSyncFeed.cs +++ b/src/Nethermind/Nethermind.Synchronization/FastBlocks/BodiesSyncFeed.cs @@ -134,32 +134,21 @@ private void PostFinishCleanUp() BodiesSyncBatch? batch = null; if (ShouldBuildANewBatch()) { - BlockInfo?[] infos = new BlockInfo[_requestSize]; - - long minNumber = 0; - bool needMoreInfo = true; - while (needMoreInfo) + BlockInfo?[] infos = null; + while (!_syncStatusList.TryGetInfosForBatch(_requestSize, (info) => _blockStore.HasBlock(info.BlockNumber, info.BlockHash), out infos)) { token.ThrowIfCancellationRequested(); - needMoreInfo = false; - _syncStatusList.GetInfosForBatch(infos); - - foreach (BlockInfo? blockInfo in infos) - { - minNumber = Math.Max(minNumber, blockInfo.BlockNumber); - if (blockInfo == null) continue; - if (!_blockStore.HasBlock(blockInfo.BlockNumber, blockInfo.BlockHash)) continue; - _syncStatusList.MarkInserted(blockInfo.BlockNumber); - needMoreInfo = true; - } + // Otherwise, the progress does not update correctly + _blockTree.LowestInsertedBodyNumber = _syncStatusList.LowestInsertWithoutGaps; + UpdateSyncReport(); } if (infos[0] is not null) { batch = new BodiesSyncBatch(infos); // Used for peer allocation. It pick peer which have the at least this number - batch.MinNumber = minNumber; + batch.MinNumber = infos[0].BlockNumber; batch.Prioritized = true; } } diff --git a/src/Nethermind/Nethermind.Synchronization/FastBlocks/ReceiptsSyncFeed.cs b/src/Nethermind/Nethermind.Synchronization/FastBlocks/ReceiptsSyncFeed.cs index 862aaa4c503..0869d2bcbbc 100644 --- a/src/Nethermind/Nethermind.Synchronization/FastBlocks/ReceiptsSyncFeed.cs +++ b/src/Nethermind/Nethermind.Synchronization/FastBlocks/ReceiptsSyncFeed.cs @@ -133,33 +133,18 @@ private void PostFinishCleanUp() ReceiptsSyncBatch? batch = null; if (ShouldBuildANewBatch()) { - BlockInfo?[] infos = new BlockInfo[_requestSize]; - _syncStatusList.GetInfosForBatch(infos); - - long minNumber = 0; - bool needMoreInfo = true; - while (needMoreInfo) + BlockInfo?[] infos = null; + while (!_syncStatusList.TryGetInfosForBatch(_requestSize, (info) => _receiptStorage.HasBlock(info.BlockNumber, info.BlockHash), out infos)) { token.ThrowIfCancellationRequested(); - needMoreInfo = false; - _syncStatusList.GetInfosForBatch(infos); - - foreach (BlockInfo? blockInfo in infos) - { - minNumber = Math.Max(minNumber, blockInfo.BlockNumber); - - if (blockInfo is null) continue; - if (!_receiptStorage.HasBlock(blockInfo.BlockNumber, blockInfo.BlockHash)) continue; - - _syncStatusList.MarkInserted(blockInfo.BlockNumber); - needMoreInfo = true; - } + _receiptStorage.LowestInsertedReceiptBlockNumber = _syncStatusList.LowestInsertWithoutGaps; + UpdateSyncReport(); } if (infos[0] is not null) { batch = new ReceiptsSyncBatch(infos); - batch.MinNumber = minNumber; + batch.MinNumber = infos[0].BlockNumber; batch.Prioritized = true; } @@ -301,11 +286,9 @@ private int InsertReceipts(ReceiptsSyncBatch batch) } } + UpdateSyncReport(); AdjustRequestSize(batch, validResponsesCount); LogPostProcessingBatchInfo(batch, validResponsesCount); - - _syncReport.FastBlocksReceipts.Update(_pivotNumber - _syncStatusList.LowestInsertWithoutGaps); - _syncReport.ReceiptsInQueue.Update(_syncStatusList.QueueSize); return validResponsesCount; } @@ -316,6 +299,12 @@ private void LogPostProcessingBatchInfo(ReceiptsSyncBatch batch, int validRespon $"{nameof(ReceiptsSyncBatch)} back from {batch.ResponseSourcePeer} with {validResponsesCount}/{batch.Infos.Length}"); } + private void UpdateSyncReport() + { + _syncReport.FastBlocksReceipts.Update(_pivotNumber - _syncStatusList.LowestInsertWithoutGaps); + _syncReport.ReceiptsInQueue.Update(_syncStatusList.QueueSize); + } + private void AdjustRequestSize(ReceiptsSyncBatch batch, int validResponsesCount) { int currentRequestSize = Volatile.Read(ref _requestSize); diff --git a/src/Nethermind/Nethermind.Synchronization/FastBlocks/SyncStatusList.cs b/src/Nethermind/Nethermind.Synchronization/FastBlocks/SyncStatusList.cs index 373afedd1d2..9aaabd7a705 100644 --- a/src/Nethermind/Nethermind.Synchronization/FastBlocks/SyncStatusList.cs +++ b/src/Nethermind/Nethermind.Synchronization/FastBlocks/SyncStatusList.cs @@ -3,6 +3,7 @@ using System; using System.Threading; +using System.Threading.Tasks; using Nethermind.Blockchain; using Nethermind.Core; using Nethermind.Core.Caching; @@ -11,6 +12,7 @@ namespace Nethermind.Synchronization.FastBlocks { internal class SyncStatusList { + private const int _parallelExistCheckSize = 1024; private long _queueSize; private readonly IBlockTree _blockTree; private readonly FastBlockStatusList _statuses; @@ -35,7 +37,7 @@ public SyncStatusList(IBlockTree blockTree, long pivotNumber, long? lowestInsert _lowerBound = lowerBound; } - public void GetInfosForBatch(BlockInfo?[] blockInfos) + private void GetInfosForBatch(BlockInfo?[] blockInfos) { int collected = 0; long currentNumber = Volatile.Read(ref _lowestInsertWithoutGaps); @@ -77,6 +79,80 @@ public void GetInfosForBatch(BlockInfo?[] blockInfos) } } + /// + /// Try get block infos of size `batchSize`. + /// + /// + /// + /// + /// + public bool TryGetInfosForBatch(int batchSize, Func blockExist, out BlockInfo?[] infos) + { + BlockInfo?[] outputArray = new BlockInfo?[batchSize]; + BlockInfo?[] workingArray = new BlockInfo?[batchSize]; + + for (int attempt = 0; attempt < 8; attempt++) + { + // Because the last clause of GetInfosForBatch increment the _lowestInsertWithoutGap need to be run + // sequentially, can't find an easy way to parallelize the checking for block exist part in the check + // So here we are... + GetInfosForBatch(workingArray); + + bool hasNonNull = false; + bool hasInserted = false; + Parallel.For(0, workingArray.Length, (i) => + { + if (workingArray[i] is not null) + { + if (blockExist(workingArray[i])) + { + MarkInserted(workingArray[i].BlockNumber); + hasInserted = true; + workingArray[i] = null; + } + else + { + hasNonNull = true; + } + } + }); + + if (hasNonNull || !hasInserted) + { + int slot = 0; + for (int i = 0; i < workingArray.Length; i++) + { + if (workingArray[i] is not null) + { + if (slot < outputArray.Length) + { + outputArray[slot] = workingArray[i]; + slot++; + } + else + { + // Not enough space in output we'll need to put back the block + MarkPending(workingArray[i]); + } + } + } + + infos = outputArray; + return true; + } + + // At this point, hasNonNull is false and hasInserted is true, meaning all entry in workingArray + // already exist. We switch to a bigger array to improve parallelization throughput + if (workingArray.Length < _parallelExistCheckSize) + { + workingArray = new BlockInfo[_parallelExistCheckSize]; + } + } + + infos = workingArray; + return false; + } + public void MarkInserted(long blockNumber) { if (_statuses.TrySet(blockNumber, FastBlockStatus.Inserted)) From e6483047802da98808b74fc33d2f101e62aa98a4 Mon Sep 17 00:00:00 2001 From: Amirul Ashraf Date: Mon, 28 Oct 2024 09:39:32 +0800 Subject: [PATCH 06/13] Fix build --- .../Nethermind.JsonRpc.Benchmark/EthModuleBenchmarks.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.JsonRpc.Benchmark/EthModuleBenchmarks.cs b/src/Nethermind/Nethermind.JsonRpc.Benchmark/EthModuleBenchmarks.cs index 02a8abacccd..875acb33763 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Benchmark/EthModuleBenchmarks.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Benchmark/EthModuleBenchmarks.cs @@ -76,7 +76,7 @@ public void GlobalSetup() new HeaderStore(dbProvider.HeadersDb, dbProvider.BlockNumbersDb), dbProvider.BlockInfosDb, dbProvider.MetadataDb, - new BlockStore(dbProvider.BadBlocksDb), + new BadBlockStore(dbProvider.BadBlocksDb, 100), chainLevelInfoRepository, specProvider, NullBloomStorage.Instance, From 7c54f94ebb108b3f2d5511c86070138810f00073 Mon Sep 17 00:00:00 2001 From: Amirul Ashraf Date: Mon, 28 Oct 2024 09:55:56 +0800 Subject: [PATCH 07/13] Bodies sync feed unit test --- .../FastBlocks/BodiesSyncFeedTests.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/BodiesSyncFeedTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/BodiesSyncFeedTests.cs index 202a3b60c86..c88ee5a45d4 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/BodiesSyncFeedTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/BodiesSyncFeedTests.cs @@ -118,6 +118,31 @@ async Task HandleAndPrepareNextRequest() req.Dispose(); } + [Test] + public async Task ShouldNotReDownloadExistingBlock() + { + _feed.InitializeFeed(); + + _syncingToBlockTree.Insert(_syncingFromBlockTree.FindBlock(_pivotBlock.Number - 2)!); + _syncingToBlockTree.Insert(_syncingFromBlockTree.FindBlock(_pivotBlock.Number - 4)!); + + BodiesSyncBatch req = (await _feed.PrepareRequest())!; + req.Infos + .Where((bi) => bi is not null) + .Select((bi) => bi!.BlockNumber) + .Take(4) + .Should() + .BeEquivalentTo([ + _pivotBlock.Number, + _pivotBlock.Number - 1, + // Skipped + _pivotBlock.Number - 3, + // Skipped + _pivotBlock.Number - 5]); + + req.Dispose(); + } + [Test] public async Task ShouldRecoverOnInsertFailure() { From f0c862fe4f586ecc8527a713f10091061c477000 Mon Sep 17 00:00:00 2001 From: Amirul Ashraf Date: Mon, 28 Oct 2024 09:59:32 +0800 Subject: [PATCH 08/13] Maybe add it to IBlockFinder --- src/Nethermind/Nethermind.Api/IApiWithStores.cs | 1 - src/Nethermind/Nethermind.Api/NethermindApi.cs | 1 - src/Nethermind/Nethermind.Blockchain/BlockTree.cs | 2 ++ src/Nethermind/Nethermind.Blockchain/BlockTreeOverlay.cs | 3 +++ src/Nethermind/Nethermind.Blockchain/Find/IBlockFinder.cs | 2 ++ src/Nethermind/Nethermind.Blockchain/ReadOnlyBlockTree.cs | 2 ++ src/Nethermind/Nethermind.Init/Steps/InitializeBlockTree.cs | 2 +- .../FastBlocks/BodiesSyncFeedTests.cs | 1 - .../Nethermind.Synchronization/FastBlocks/BodiesSyncFeed.cs | 5 +---- 9 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Nethermind/Nethermind.Api/IApiWithStores.cs b/src/Nethermind/Nethermind.Api/IApiWithStores.cs index 31f1740763e..459b92e3339 100644 --- a/src/Nethermind/Nethermind.Api/IApiWithStores.cs +++ b/src/Nethermind/Nethermind.Api/IApiWithStores.cs @@ -31,7 +31,6 @@ public interface IApiWithStores : IBasicApi IReceiptMonitor? ReceiptMonitor { get; set; } IWallet? Wallet { get; set; } IBadBlockStore? BadBlocksStore { get; set; } - IBlockStore BlockStore { get; set; } public ContainerBuilder ConfigureContainerBuilderFromApiWithStores(ContainerBuilder builder) { diff --git a/src/Nethermind/Nethermind.Api/NethermindApi.cs b/src/Nethermind/Nethermind.Api/NethermindApi.cs index 31694188be8..bc1288d8529 100644 --- a/src/Nethermind/Nethermind.Api/NethermindApi.cs +++ b/src/Nethermind/Nethermind.Api/NethermindApi.cs @@ -226,7 +226,6 @@ public ISealEngine SealEngine public CensorshipDetector CensorshipDetector { get; set; } = null!; public IWallet? Wallet { get; set; } public IBadBlockStore? BadBlocksStore { get; set; } - public IBlockStore BlockStore { get; set; } = null!; public ITransactionComparerProvider? TransactionComparerProvider { get; set; } public IWebSocketsManager WebSocketsManager { get; set; } = new WebSocketsManager(); diff --git a/src/Nethermind/Nethermind.Blockchain/BlockTree.cs b/src/Nethermind/Nethermind.Blockchain/BlockTree.cs index 9cf43a62c25..f649be65a65 100644 --- a/src/Nethermind/Nethermind.Blockchain/BlockTree.cs +++ b/src/Nethermind/Nethermind.Blockchain/BlockTree.cs @@ -446,6 +446,8 @@ public AddBlockResult SuggestBlock(Block block, BlockTreeSuggestOptions options public Hash256? FindBlockHash(long blockNumber) => GetBlockHashOnMainOrBestDifficultyHash(blockNumber); + public bool HasBlock(long blockNumber, Hash256 blockHash) => _blockStore.HasBlock(blockNumber, blockHash); + public BlockHeader? FindHeader(Hash256? blockHash, BlockTreeLookupOptions options, long? blockNumber = null) { if (blockHash is null || blockHash == Keccak.Zero) diff --git a/src/Nethermind/Nethermind.Blockchain/BlockTreeOverlay.cs b/src/Nethermind/Nethermind.Blockchain/BlockTreeOverlay.cs index c16834f63a8..214a0ada99e 100644 --- a/src/Nethermind/Nethermind.Blockchain/BlockTreeOverlay.cs +++ b/src/Nethermind/Nethermind.Blockchain/BlockTreeOverlay.cs @@ -231,6 +231,9 @@ public void UpdateBeaconMainChain(BlockInfo[]? blockInfos, long clearBeaconMainC public Block? FindBlock(long blockNumber, BlockTreeLookupOptions options) => _overlayTree.FindBlock(blockNumber, options) ?? _baseTree.FindBlock(blockNumber, options); + public bool HasBlock(long blockNumber, Hash256 blockHash) => + _overlayTree.HasBlock(blockNumber, blockHash) || _baseTree.HasBlock(blockNumber, blockHash); + public BlockHeader? FindHeader(Hash256 blockHash, BlockTreeLookupOptions options, long? blockNumber = null) => _overlayTree.FindHeader(blockHash, options, blockNumber) ?? _baseTree.FindHeader(blockHash, options, blockNumber); diff --git a/src/Nethermind/Nethermind.Blockchain/Find/IBlockFinder.cs b/src/Nethermind/Nethermind.Blockchain/Find/IBlockFinder.cs index e65b57f8c49..16788b8c901 100644 --- a/src/Nethermind/Nethermind.Blockchain/Find/IBlockFinder.cs +++ b/src/Nethermind/Nethermind.Blockchain/Find/IBlockFinder.cs @@ -25,6 +25,8 @@ public interface IBlockFinder Block? FindBlock(long blockNumber, BlockTreeLookupOptions options); + bool HasBlock(long blockNumber, Hash256 blockHash); + /// Find a header. blockNumber is optional, but specifying it can improve performance. BlockHeader? FindHeader(Hash256 blockHash, BlockTreeLookupOptions options, long? blockNumber = null); diff --git a/src/Nethermind/Nethermind.Blockchain/ReadOnlyBlockTree.cs b/src/Nethermind/Nethermind.Blockchain/ReadOnlyBlockTree.cs index 506973f7194..45aa4969678 100644 --- a/src/Nethermind/Nethermind.Blockchain/ReadOnlyBlockTree.cs +++ b/src/Nethermind/Nethermind.Blockchain/ReadOnlyBlockTree.cs @@ -94,6 +94,8 @@ public void UpdateHeadBlock(Hash256 blockHash) public Block FindBlock(Hash256 blockHash, BlockTreeLookupOptions options, long? blockNumber = null) => _wrapped.FindBlock(blockHash, options, blockNumber); + public bool HasBlock(long blockNumber, Hash256 blockHash) => _wrapped.HasBlock(blockNumber, blockHash); + public BlockHeader FindHeader(Hash256 blockHash, BlockTreeLookupOptions options, long? blockNumber = null) => _wrapped.FindHeader(blockHash, options, blockNumber: blockNumber); public BlockHeader FindHeader(long blockNumber, BlockTreeLookupOptions options) => _wrapped.FindHeader(blockNumber, options); diff --git a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockTree.cs b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockTree.cs index d23554314a8..dbd7011fd17 100644 --- a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockTree.cs +++ b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockTree.cs @@ -51,7 +51,7 @@ public Task Execute(CancellationToken cancellationToken) IChainLevelInfoRepository chainLevelInfoRepository = _set.ChainLevelInfoRepository = new ChainLevelInfoRepository(_get.DbProvider!.BlockInfosDb); - IBlockStore blockStore = _set.BlockStore = new BlockStore(_get.DbProvider.BlocksDb); + IBlockStore blockStore = new BlockStore(_get.DbProvider.BlocksDb); IHeaderStore headerStore = new HeaderStore(_get.DbProvider.HeadersDb, _get.DbProvider.BlockNumbersDb); IBadBlockStore badBlockStore = _set.BadBlocksStore = new BadBlockStore(_get.DbProvider.BadBlocksDb, initConfig.BadBlocksStored ?? 100); diff --git a/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/BodiesSyncFeedTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/BodiesSyncFeedTests.cs index c88ee5a45d4..a355e32a626 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/BodiesSyncFeedTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/BodiesSyncFeedTests.cs @@ -67,7 +67,6 @@ public void Setup() _feed = new BodiesSyncFeed( MainnetSpecProvider.Instance, _syncingToBlockTree, - new BlockStore(_blocksDb), Substitute.For(), _syncConfig, new NullSyncReport(), diff --git a/src/Nethermind/Nethermind.Synchronization/FastBlocks/BodiesSyncFeed.cs b/src/Nethermind/Nethermind.Synchronization/FastBlocks/BodiesSyncFeed.cs index 3ee65e153c9..edf132f066c 100644 --- a/src/Nethermind/Nethermind.Synchronization/FastBlocks/BodiesSyncFeed.cs +++ b/src/Nethermind/Nethermind.Synchronization/FastBlocks/BodiesSyncFeed.cs @@ -35,7 +35,6 @@ public class BodiesSyncFeed : BarrierSyncFeed private readonly long _flushDbInterval; // About every 10GB on mainnet private readonly IBlockTree _blockTree; - private readonly IBlockStore _blockStore; private readonly ISyncConfig _syncConfig; private readonly ISyncReport _syncReport; private readonly ISyncPeerPool _syncPeerPool; @@ -51,7 +50,6 @@ public class BodiesSyncFeed : BarrierSyncFeed public BodiesSyncFeed( ISpecProvider specProvider, IBlockTree blockTree, - IBlockStore blockStore, ISyncPeerPool syncPeerPool, ISyncConfig syncConfig, ISyncReport syncReport, @@ -62,7 +60,6 @@ public BodiesSyncFeed( : base(metadataDb, specProvider, logManager.GetClassLogger()) { _blockTree = blockTree ?? throw new ArgumentNullException(nameof(blockTree)); - _blockStore = blockStore ?? throw new ArgumentNullException(nameof(blockStore)); _syncPeerPool = syncPeerPool ?? throw new ArgumentNullException(nameof(syncPeerPool)); _syncConfig = syncConfig ?? throw new ArgumentNullException(nameof(syncConfig)); _syncReport = syncReport ?? throw new ArgumentNullException(nameof(syncReport)); @@ -135,7 +132,7 @@ private void PostFinishCleanUp() if (ShouldBuildANewBatch()) { BlockInfo?[] infos = null; - while (!_syncStatusList.TryGetInfosForBatch(_requestSize, (info) => _blockStore.HasBlock(info.BlockNumber, info.BlockHash), out infos)) + while (!_syncStatusList.TryGetInfosForBatch(_requestSize, (info) => _blockTree.HasBlock(info.BlockNumber, info.BlockHash), out infos)) { token.ThrowIfCancellationRequested(); From 39f8da40e8661304d9736c4e2807469998d404cd Mon Sep 17 00:00:00 2001 From: Amirul Ashraf Date: Mon, 28 Oct 2024 10:15:58 +0800 Subject: [PATCH 09/13] Unit tests --- .../FastBlocks/BodiesSyncFeedTests.cs | 4 +- .../ReceiptSyncFeedTests.cs | 90 ++++++++++++++----- 2 files changed, 67 insertions(+), 27 deletions(-) diff --git a/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/BodiesSyncFeedTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/BodiesSyncFeedTests.cs index a355e32a626..98bf09d31f8 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/BodiesSyncFeedTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/BodiesSyncFeedTests.cs @@ -125,7 +125,7 @@ public async Task ShouldNotReDownloadExistingBlock() _syncingToBlockTree.Insert(_syncingFromBlockTree.FindBlock(_pivotBlock.Number - 2)!); _syncingToBlockTree.Insert(_syncingFromBlockTree.FindBlock(_pivotBlock.Number - 4)!); - BodiesSyncBatch req = (await _feed.PrepareRequest())!; + using BodiesSyncBatch req = (await _feed.PrepareRequest())!; req.Infos .Where((bi) => bi is not null) .Select((bi) => bi!.BlockNumber) @@ -138,8 +138,6 @@ public async Task ShouldNotReDownloadExistingBlock() _pivotBlock.Number - 3, // Skipped _pivotBlock.Number - 5]); - - req.Dispose(); } [Test] diff --git a/src/Nethermind/Nethermind.Synchronization.Test/ReceiptSyncFeedTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/ReceiptSyncFeedTests.cs index d92204e234e..eef137e816f 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/ReceiptSyncFeedTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/ReceiptSyncFeedTests.cs @@ -9,6 +9,7 @@ using Nethermind.Blockchain.Receipts; using Nethermind.Blockchain.Synchronization; using Nethermind.Core; +using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Core.Test.Builders; using Nethermind.Db; @@ -25,54 +26,72 @@ namespace Nethermind.Synchronization.Test; public class ReceiptSyncFeedTests { + private IBlockTree _syncingFromBlockTree = null!; + private IBlockTree _syncingToBlockTree = null!; + private ReceiptsSyncFeed _feed = null!; + private ISyncConfig _syncConfig = null!; + private Block _pivotBlock = null!; + private InMemoryReceiptStorage _syncingFromReceiptStore; + private IReceiptStorage _receiptStorage; - [Test] - public async Task ShouldRecoverOnInsertFailure() + [SetUp] + public void Setup() { - InMemoryReceiptStorage syncingFromReceiptStore = new InMemoryReceiptStorage(); - BlockTree syncingFromBlockTree = Build.A.BlockTree() - .WithTransactions(syncingFromReceiptStore) + _syncingFromReceiptStore = new InMemoryReceiptStorage(); + _syncingFromBlockTree = Build.A.BlockTree() + .WithTransactions(_syncingFromReceiptStore) .OfChainLength(100) .TestObject; - BlockTree syncingTooBlockTree = Build.A.BlockTree() + _receiptStorage = Substitute.For(); + _syncingToBlockTree = Build.A.BlockTree() .TestObject; for (int i = 1; i < 100; i++) { - Block block = syncingFromBlockTree.FindBlock(i, BlockTreeLookupOptions.None)!; - syncingTooBlockTree.Insert(block.Header); - syncingTooBlockTree.Insert(block); + Block block = _syncingFromBlockTree.FindBlock(i, BlockTreeLookupOptions.None)!; + _syncingToBlockTree.Insert(block.Header); + _syncingToBlockTree.Insert(block); } - Block pivot = syncingFromBlockTree.FindBlock(99, BlockTreeLookupOptions.None)!; + _pivotBlock = _syncingFromBlockTree.FindBlock(99, BlockTreeLookupOptions.None)!; - SyncConfig syncConfig = new() + _syncConfig = new SyncConfig() { FastSync = true, - PivotHash = pivot.Hash!.ToString(), - PivotNumber = pivot.Number.ToString(), + PivotHash = _pivotBlock.Hash!.ToString(), + PivotNumber = _pivotBlock.Number.ToString(), AncientBodiesBarrier = 0, DownloadBodiesInFastSync = true, }; - IReceiptStorage receiptStorage = Substitute.For(); - ReceiptsSyncFeed syncFeed = new ReceiptsSyncFeed( + _feed = new ReceiptsSyncFeed( MainnetSpecProvider.Instance, - syncingTooBlockTree, - receiptStorage, + _syncingToBlockTree, + _receiptStorage, Substitute.For(), - syncConfig, + _syncConfig, new NullSyncReport(), new MemDb(), LimboLogs.Instance ); - syncFeed.InitializeFeed(); + } + + [TearDown] + public void TearDown() + { + _feed.Dispose(); + } + + [Test] + public async Task ShouldRecoverOnInsertFailure() + { + _feed.InitializeFeed(); - using ReceiptsSyncBatch req = (await syncFeed.PrepareRequest())!; - req.Response = req.Infos.Take(8).Select(info => syncingFromReceiptStore.Get(info!.BlockHash)).ToPooledList(8)!; + using ReceiptsSyncBatch req = (await _feed.PrepareRequest())!; + req.Response = req.Infos.Take(8).Select(info => _syncingFromReceiptStore.Get(info!.BlockHash)).ToPooledList(8)!; - receiptStorage + _receiptStorage .When((it) => it.Insert(Arg.Any(), Arg.Any(), Arg.Any())) .Do((callInfo) => { @@ -80,9 +99,32 @@ public async Task ShouldRecoverOnInsertFailure() if (block.Number == 95) throw new Exception("test exception"); }); - Func act = () => syncFeed.HandleResponse(req); + Func act = () => _feed.HandleResponse(req); act.Should().Throw(); - using ReceiptsSyncBatch req2 = (await syncFeed.PrepareRequest())!; + using ReceiptsSyncBatch req2 = (await _feed.PrepareRequest())!; req2.Infos[0]!.BlockNumber.Should().Be(95); } + + [Test] + public async Task ShouldNotRedownloadExistingReceipts() + { + _feed.InitializeFeed(); + _receiptStorage.HasBlock(Arg.Is(_pivotBlock.Number - 2), Arg.Any()).Returns(true); + _receiptStorage.HasBlock(Arg.Is(_pivotBlock.Number - 4), Arg.Any()).Returns(true); + + using ReceiptsSyncBatch req = (await _feed.PrepareRequest())!; + + req.Infos + .Where((bi) => bi is not null) + .Select((bi) => bi!.BlockNumber) + .Take(4) + .Should() + .BeEquivalentTo([ + _pivotBlock.Number, + _pivotBlock.Number - 1, + // Skipped + _pivotBlock.Number - 3, + // Skipped + _pivotBlock.Number - 5]); + } } From 43ab1ffd7189e302969b14e7014689a39f3779d2 Mon Sep 17 00:00:00 2001 From: Amirul Ashraf Date: Mon, 28 Oct 2024 14:20:06 +0800 Subject: [PATCH 10/13] Disalbe optimize filter for hits --- src/Nethermind/Nethermind.Db.Rocks/Config/DbConfig.cs | 2 ++ src/Nethermind/Nethermind.Db.Rocks/Config/IDbConfig.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/Nethermind/Nethermind.Db.Rocks/Config/DbConfig.cs b/src/Nethermind/Nethermind.Db.Rocks/Config/DbConfig.cs index 62944818adf..73d6ecd1547 100644 --- a/src/Nethermind/Nethermind.Db.Rocks/Config/DbConfig.cs +++ b/src/Nethermind/Nethermind.Db.Rocks/Config/DbConfig.cs @@ -67,6 +67,7 @@ public class DbConfig : IDbConfig public ulong? ReceiptsDbCompactionReadAhead { get; set; } public ulong ReceiptsDbTargetFileSizeBase { get; set; } = (ulong)64.MiB(); public double ReceiptsDbCompressibilityHint { get; set; } = 0.35; + public bool ReceiptsDbOptimizeFiltersForHits { get; set; } = false; public string? ReceiptsDbAdditionalRocksDbOptions { get; set; } = "compaction_pri=kOldestLargestSeqFirst"; public ulong BlocksDbWriteBufferSize { get; set; } = (ulong)64.MiB(); @@ -79,6 +80,7 @@ public class DbConfig : IDbConfig public bool? BlocksDbUseDirectReads { get; set; } public bool? BlocksDbUseDirectIoForFlushAndCompactions { get; set; } public ulong? BlocksDbCompactionReadAhead { get; set; } + public bool BlocksDbOptimizeFiltersForHits { get; set; } = false; public string? BlocksDbAdditionalRocksDbOptions { get; set; } = "compaction_pri=kOldestLargestSeqFirst"; public ulong HeadersDbWriteBufferSize { get; set; } = (ulong)8.MiB(); diff --git a/src/Nethermind/Nethermind.Db.Rocks/Config/IDbConfig.cs b/src/Nethermind/Nethermind.Db.Rocks/Config/IDbConfig.cs index a1d6af06eb6..0af4a0e66e0 100644 --- a/src/Nethermind/Nethermind.Db.Rocks/Config/IDbConfig.cs +++ b/src/Nethermind/Nethermind.Db.Rocks/Config/IDbConfig.cs @@ -68,6 +68,7 @@ public interface IDbConfig : IConfig ulong? ReceiptsDbCompactionReadAhead { get; set; } ulong ReceiptsDbTargetFileSizeBase { get; set; } double ReceiptsDbCompressibilityHint { get; set; } + bool ReceiptsDbOptimizeFiltersForHits { get; set; } string? ReceiptsDbAdditionalRocksDbOptions { get; set; } ulong BlocksDbWriteBufferSize { get; set; } @@ -80,6 +81,7 @@ public interface IDbConfig : IConfig bool? BlocksDbUseDirectReads { get; set; } bool? BlocksDbUseDirectIoForFlushAndCompactions { get; set; } ulong? BlocksDbCompactionReadAhead { get; set; } + bool BlocksDbOptimizeFiltersForHits { get; set; } string? BlocksDbAdditionalRocksDbOptions { get; set; } ulong HeadersDbWriteBufferSize { get; set; } From 14d5a9038a8375ee2768584358c6ceb88b22e9b2 Mon Sep 17 00:00:00 2001 From: Amirul Ashraf Date: Mon, 28 Oct 2024 14:21:04 +0800 Subject: [PATCH 11/13] Whitespace --- .../FastBlocks/SyncStatusListTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/SyncStatusListTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/SyncStatusListTests.cs index 9bd37ffc643..bbd4ea0774c 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/SyncStatusListTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/FastBlocks/SyncStatusListTests.cs @@ -53,7 +53,7 @@ public void Will_not_go_below_ancient_barrier() SyncStatusList syncStatusList = new SyncStatusList(blockTree, 1000, null, 900); BlockInfo?[] infos; - syncStatusList.TryGetInfosForBatch(500, (_) => false, out infos); + syncStatusList.TryGetInfosForBatch(500, (_) => false, out infos); infos.Count((it) => it is not null).Should().Be(101); } @@ -63,7 +63,7 @@ public void Will_skip_existing_keys() { IBlockTree blockTree = Substitute.For(); blockTree.FindCanonicalBlockInfo(Arg.Any()) - .Returns((Func) ((ci) => + .Returns((Func)((ci) => { long blockNumber = (long)ci[0]; return new BlockInfo(TestItem.KeccakA, 0) @@ -79,7 +79,7 @@ public void Will_skip_existing_keys() List TryGetInfos() { BlockInfo?[] infos; - syncStatusList.TryGetInfosForBatch(50, (bi) => !needToFetchBlocks.Contains(bi.BlockNumber), out infos); + syncStatusList.TryGetInfosForBatch(50, (bi) => !needToFetchBlocks.Contains(bi.BlockNumber), out infos); return infos.Where(bi => bi != null).Select((bi) => bi!.BlockNumber).ToList(); } From 47fe2927911b6b45d338f5485f43ee3b437e1368 Mon Sep 17 00:00:00 2001 From: Amirul Ashraf Date: Thu, 31 Oct 2024 08:07:24 +0800 Subject: [PATCH 12/13] Addressing comment --- .../FastBlocks/SyncStatusList.cs | 83 +++++++++++-------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/src/Nethermind/Nethermind.Synchronization/FastBlocks/SyncStatusList.cs b/src/Nethermind/Nethermind.Synchronization/FastBlocks/SyncStatusList.cs index 9aaabd7a705..31ad2050936 100644 --- a/src/Nethermind/Nethermind.Synchronization/FastBlocks/SyncStatusList.cs +++ b/src/Nethermind/Nethermind.Synchronization/FastBlocks/SyncStatusList.cs @@ -7,12 +7,13 @@ using Nethermind.Blockchain; using Nethermind.Core; using Nethermind.Core.Caching; +using Nethermind.Core.Collections; namespace Nethermind.Synchronization.FastBlocks { internal class SyncStatusList { - private const int _parallelExistCheckSize = 1024; + private const int ParallelExistCheckSize = 1024; private long _queueSize; private readonly IBlockTree _blockTree; private readonly FastBlockStatusList _statuses; @@ -37,7 +38,7 @@ public SyncStatusList(IBlockTree blockTree, long pivotNumber, long? lowestInsert _lowerBound = lowerBound; } - private void GetInfosForBatch(BlockInfo?[] blockInfos) + private void GetInfosForBatch(Span blockInfos) { int collected = 0; long currentNumber = Volatile.Read(ref _lowestInsertWithoutGaps); @@ -88,19 +89,41 @@ private void GetInfosForBatch(BlockInfo?[] blockInfos) /// public bool TryGetInfosForBatch(int batchSize, Func blockExist, out BlockInfo?[] infos) { - BlockInfo?[] outputArray = new BlockInfo?[batchSize]; - BlockInfo?[] workingArray = new BlockInfo?[batchSize]; + ArrayPoolList workingArray = new(batchSize, batchSize); - for (int attempt = 0; attempt < 8; attempt++) + // Need to be a max attempt to update sync progress + const int maxAttempt = 8; + for (int attempt = 0; attempt < maxAttempt; attempt++) { // Because the last clause of GetInfosForBatch increment the _lowestInsertWithoutGap need to be run // sequentially, can't find an easy way to parallelize the checking for block exist part in the check // So here we are... - GetInfosForBatch(workingArray); + GetInfosForBatch(workingArray.AsSpan()); + (bool hasNonNull, bool hasInserted) = ClearExistingBlock(); + + if (hasNonNull || !hasInserted) + { + CompileOutput(out infos); + return true; + } + + // At this point, hasNonNull is false and hasInserted is true, meaning all entry in workingArray + // already exist. We switch to a bigger array to improve parallelization throughput + if (workingArray.Count < ParallelExistCheckSize) + { + workingArray = new ArrayPoolList(ParallelExistCheckSize, ParallelExistCheckSize); + } + } + + infos = null; + return false; + + (bool, bool) ClearExistingBlock() + { bool hasNonNull = false; bool hasInserted = false; - Parallel.For(0, workingArray.Length, (i) => + Parallel.For(0, workingArray.Count, (i) => { if (workingArray[i] is not null) { @@ -116,41 +139,29 @@ public bool TryGetInfosForBatch(int batchSize, Func blockExist, } } }); + return (hasNonNull, hasInserted); + } - if (hasNonNull || !hasInserted) + void CompileOutput(out BlockInfo?[] outputArray) + { + int slot = 0; + outputArray = new BlockInfo?[batchSize]; + for (int i = 0; i < workingArray.Count; i++) { - int slot = 0; - for (int i = 0; i < workingArray.Length; i++) + if (workingArray[i] is null) continue; + + if (slot < outputArray.Length) { - if (workingArray[i] is not null) - { - if (slot < outputArray.Length) - { - outputArray[slot] = workingArray[i]; - slot++; - } - else - { - // Not enough space in output we'll need to put back the block - MarkPending(workingArray[i]); - } - } + outputArray[slot] = workingArray[i]; + slot++; + } + else + { + // Not enough space in output we'll need to put back the block + MarkPending(workingArray[i]); } - - infos = outputArray; - return true; - } - - // At this point, hasNonNull is false and hasInserted is true, meaning all entry in workingArray - // already exist. We switch to a bigger array to improve parallelization throughput - if (workingArray.Length < _parallelExistCheckSize) - { - workingArray = new BlockInfo[_parallelExistCheckSize]; } } - - infos = workingArray; - return false; } public void MarkInserted(long blockNumber) From 89acf70fc21ac5d17c910d976198ad88bb1826c3 Mon Sep 17 00:00:00 2001 From: Amirul Ashraf Date: Thu, 31 Oct 2024 08:20:32 +0800 Subject: [PATCH 13/13] Fix test --- .../Nethermind.Synchronization/FastBlocks/SyncStatusList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Synchronization/FastBlocks/SyncStatusList.cs b/src/Nethermind/Nethermind.Synchronization/FastBlocks/SyncStatusList.cs index 31ad2050936..208d355a844 100644 --- a/src/Nethermind/Nethermind.Synchronization/FastBlocks/SyncStatusList.cs +++ b/src/Nethermind/Nethermind.Synchronization/FastBlocks/SyncStatusList.cs @@ -116,7 +116,7 @@ public bool TryGetInfosForBatch(int batchSize, Func blockExist, } } - infos = null; + infos = []; return false; (bool, bool) ClearExistingBlock()