From 9b5a3f2988036b616730e07a7c59afe278cbfa24 Mon Sep 17 00:00:00 2001 From: Klaudia Jazgar <67744705+kjazgar@users.noreply.github.com> Date: Fri, 5 Jan 2024 20:14:37 +0100 Subject: [PATCH] Feature/debug get bad blocks (#3838) Co-authored-by: lukasz.rozmej Co-authored-by: Marc --- .../Nethermind.Api/IApiWithStores.cs | 2 + src/Nethermind/Nethermind.Api/IInitConfig.cs | 3 + src/Nethermind/Nethermind.Api/InitConfig.cs | 1 + .../Nethermind.Api/NethermindApi.cs | 2 + .../Nethermind.Blockchain/BlockTree.cs | 4 + .../Blocks/BlockStore.cs | 28 ++++++- .../Blocks/IBlockStore.cs | 2 + .../Builders/BlockTreeBuilder.cs | 27 ++++++ .../Nethermind.Core/Caching/LruCache.cs | 14 ++++ src/Nethermind/Nethermind.Db/DbNames.cs | 1 + src/Nethermind/Nethermind.Db/IDbProvider.cs | 1 + src/Nethermind/Nethermind.Db/Metrics.cs | 8 ++ .../Nethermind.Db/StandardDbInitializer.cs | 1 + .../Steps/InitializeBlockTree.cs | 2 + .../Steps/RegisterRpcModules.cs | 2 + .../EthModuleBenchmarks.cs | 1 + .../Modules/DebugModuleTests.cs | 47 ++++++++++- .../Modules/DebugModule/DebugBridge.cs | 82 ++++++------------- .../Modules/DebugModule/DebugModuleFactory.cs | 7 +- .../Modules/DebugModule/DebugRpcModule.cs | 6 ++ .../Modules/DebugModule/IDebugBridge.cs | 1 + .../Modules/DebugModule/IDebugRpcModule.cs | 4 + .../Ethereum/ContextWithMocks.cs | 4 +- 23 files changed, 191 insertions(+), 59 deletions(-) diff --git a/src/Nethermind/Nethermind.Api/IApiWithStores.cs b/src/Nethermind/Nethermind.Api/IApiWithStores.cs index 8a5bfa981d5..f6e02313adc 100644 --- a/src/Nethermind/Nethermind.Api/IApiWithStores.cs +++ b/src/Nethermind/Nethermind.Api/IApiWithStores.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using Nethermind.Blockchain; +using Nethermind.Blockchain.Blocks; using Nethermind.Blockchain.Find; using Nethermind.Blockchain.Receipts; using Nethermind.Consensus; @@ -27,5 +28,6 @@ public interface IApiWithStores : IBasicApi IReceiptFinder? ReceiptFinder { get; set; } IReceiptMonitor? ReceiptMonitor { get; set; } IWallet? Wallet { get; set; } + IBlockStore? BadBlocksStore { get; set; } } } diff --git a/src/Nethermind/Nethermind.Api/IInitConfig.cs b/src/Nethermind/Nethermind.Api/IInitConfig.cs index ddc999b8b99..c72d7477666 100644 --- a/src/Nethermind/Nethermind.Api/IInitConfig.cs +++ b/src/Nethermind/Nethermind.Api/IInitConfig.cs @@ -74,6 +74,9 @@ public interface IInitConfig : IConfig [ConfigItem(Description = "The hint on the max memory limit, in bytes, to configure the database and networking memory allocations.", DefaultValue = "null")] long? MemoryHint { get; set; } + [ConfigItem(Description = "The maximum number of bad blocks observed on the network that will be stored to disk.", DefaultValue = "100")] + long? BadBlocksStored { get; set; } + [ConfigItem(Description = "[TECHNICAL] Disable garbage collector on newPayload", DefaultValue = "true", HiddenFromDocs = true)] bool DisableGcOnNewPayload { get; set; } diff --git a/src/Nethermind/Nethermind.Api/InitConfig.cs b/src/Nethermind/Nethermind.Api/InitConfig.cs index af7a04c6971..61ac7334584 100644 --- a/src/Nethermind/Nethermind.Api/InitConfig.cs +++ b/src/Nethermind/Nethermind.Api/InitConfig.cs @@ -31,6 +31,7 @@ public class InitConfig : IInitConfig public string RpcDbUrl { get; set; } = String.Empty; public long? MemoryHint { get; set; } + public long? BadBlocksStored { get; set; } = 100; public bool DisableGcOnNewPayload { get; set; } = true; public bool DisableMallocOpts { get; set; } = false; public long? ExitOnBlockNumber { get; set; } = null; diff --git a/src/Nethermind/Nethermind.Api/NethermindApi.cs b/src/Nethermind/Nethermind.Api/NethermindApi.cs index 373fa72e722..5dd23c8974e 100644 --- a/src/Nethermind/Nethermind.Api/NethermindApi.cs +++ b/src/Nethermind/Nethermind.Api/NethermindApi.cs @@ -7,6 +7,7 @@ using Nethermind.Abi; using Nethermind.Api.Extensions; using Nethermind.Blockchain; +using Nethermind.Blockchain.Blocks; using Nethermind.Blockchain.Filters; using Nethermind.Blockchain.Find; using Nethermind.Blockchain.FullPruning; @@ -210,6 +211,7 @@ public ISealEngine SealEngine public IEthSyncingInfo? EthSyncingInfo { get; set; } public IBlockProductionPolicy? BlockProductionPolicy { get; set; } public IWallet? Wallet { get; set; } + public IBlockStore? 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 4a7eefa42fb..368f6201ea8 100644 --- a/src/Nethermind/Nethermind.Blockchain/BlockTree.cs +++ b/src/Nethermind/Nethermind.Blockchain/BlockTree.cs @@ -43,6 +43,7 @@ public partial class BlockTree : IBlockTree private readonly IHeaderStore _headerStore; private readonly IDb _blockInfoDb; private readonly IDb _metadataDb; + private readonly IBlockStore _badBlockStore; private readonly LruCache _invalidBlocks = new(128, 128, "invalid blocks"); @@ -110,6 +111,7 @@ public BlockTree( IHeaderStore? headerDb, IDb? blockInfoDb, IDb? metadataDb, + IBlockStore? badBlockStore, IChainLevelInfoRepository? chainLevelInfoRepository, ISpecProvider? specProvider, IBloomStorage? bloomStorage, @@ -121,6 +123,7 @@ public BlockTree( _headerStore = headerDb ?? throw new ArgumentNullException(nameof(headerDb)); _blockInfoDb = blockInfoDb ?? throw new ArgumentNullException(nameof(blockInfoDb)); _metadataDb = metadataDb ?? throw new ArgumentNullException(nameof(metadataDb)); + _badBlockStore = badBlockStore ?? throw new ArgumentNullException(nameof(badBlockStore)); _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); _bloomStorage = bloomStorage ?? throw new ArgumentNullException(nameof(bloomStorage)); _syncConfig = syncConfig ?? throw new ArgumentNullException(nameof(syncConfig)); @@ -710,6 +713,7 @@ public void DeleteInvalidBlock(Block invalidBlock) if (_logger.IsDebug) _logger.Debug($"Deleting invalid block {invalidBlock.ToString(Block.Format.FullHashAndNumber)}"); _invalidBlocks.Set(invalidBlock.Hash, invalidBlock); + _badBlockStore.Insert(invalidBlock); BestSuggestedHeader = Head?.Header; BestSuggestedBody = Head; diff --git a/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs b/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs index 830223ec0ab..67bf3d902b1 100644 --- a/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs +++ b/src/Nethermind/Nethermind.Blockchain/Blocks/BlockStore.cs @@ -3,6 +3,8 @@ using System; using System.Buffers; +using System.Collections.Generic; +using System.Linq; using Nethermind.Core; using Nethermind.Core.Caching; using Nethermind.Core.Crypto; @@ -20,10 +22,12 @@ public class BlockStore : IBlockStore private readonly LruCache _blockCache = new(CacheSize, CacheSize, "blocks"); + private long? _maxSize; - public BlockStore(IDb blockDb) + public BlockStore(IDb blockDb, long? maxSize = null) { _blockDb = blockDb; + _maxSize = maxSize; } public void SetMetadata(byte[] key, byte[] value) @@ -36,6 +40,18 @@ public void SetMetadata(byte[] key, byte[] value) return _blockDb.Get(key); } + private void TruncateToMaxSize() + { + int toDelete = (int)(_blockDb.GetSize() - _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) @@ -48,6 +64,11 @@ 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) @@ -88,4 +109,9 @@ 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 a84f2c5fe4a..6621182c949 100644 --- a/src/Nethermind/Nethermind.Blockchain/Blocks/IBlockStore.cs +++ b/src/Nethermind/Nethermind.Blockchain/Blocks/IBlockStore.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System.Collections.Generic; using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Serialization.Rlp; @@ -16,6 +17,7 @@ public interface IBlockStore void Insert(Block block, WriteFlags writeFlags = WriteFlags.None); void Delete(long blockNumber, Hash256 blockHash); Block? Get(long blockNumber, Hash256 blockHash, bool shouldCache = true); + IEnumerable GetAll(); ReceiptRecoveryBlock? GetReceiptRecoveryBlock(long blockNumber, Hash256 blockHash); void Cache(Block block); diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/BlockTreeBuilder.cs b/src/Nethermind/Nethermind.Core.Test/Builders/BlockTreeBuilder.cs index 56433437a71..b61e4b993f7 100644 --- a/src/Nethermind/Nethermind.Core.Test/Builders/BlockTreeBuilder.cs +++ b/src/Nethermind/Nethermind.Core.Test/Builders/BlockTreeBuilder.cs @@ -47,6 +47,7 @@ public BlockTreeBuilder(Block genesisBlock, ISpecProvider specProvider) BlockNumbersDb = new TestMemDb(); BlockInfoDb = new TestMemDb(); MetadataDb = new TestMemDb(); + BadBlocksDb = new TestMemDb(); _genesisBlock = genesisBlock; _specProvider = specProvider; @@ -79,6 +80,7 @@ public BlockTree BlockTree HeaderStore, BlockInfoDb, MetadataDb, + BadBlockStore, ChainLevelInfoRepository, _specProvider, BloomStorage, @@ -105,6 +107,7 @@ protected override void BeforeReturn() public ISyncConfig SyncConfig { get; set; } = new SyncConfig(); public IDb BlocksDb { get; set; } + public IDb BadBlocksDb { get; set; } private IBlockStore? _blockStore; public IBlockStore BlockStore @@ -139,6 +142,18 @@ public IHeaderStore HeaderStore public IDb MetadataDb { get; set; } + private IBlockStore? _badBlockStore; + public IBlockStore BadBlockStore + { + get + { + return _badBlockStore ??= new BlockStore(BadBlocksDb, 100); + } + set + { + _badBlockStore = value; + } + } private IChainLevelInfoRepository? _chainLevelInfoRepository; public IChainLevelInfoRepository ChainLevelInfoRepository @@ -384,6 +399,18 @@ public BlockTreeBuilder WithBlockStore(IBlockStore blockStore) return this; } + public BlockTreeBuilder WithBadBlockStore(IBlockStore blockStore) + { + BadBlockStore = blockStore; + return this; + } + + public BlockTreeBuilder WithHeaderStore(IHeaderStore headerStore) + { + HeaderStore = headerStore; + return this; + } + public BlockTreeBuilder WithBlocksDb(IDb blocksDb) { BlocksDb = blocksDb; diff --git a/src/Nethermind/Nethermind.Core/Caching/LruCache.cs b/src/Nethermind/Nethermind.Core/Caching/LruCache.cs index d96a4bc8d4b..c2aca5bea05 100644 --- a/src/Nethermind/Nethermind.Core/Caching/LruCache.cs +++ b/src/Nethermind/Nethermind.Core/Caching/LruCache.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using Nethermind.Core.Extensions; using Nethermind.Core.Threading; @@ -146,6 +147,19 @@ public KeyValuePair[] ToArray() return array; } + [MethodImpl(MethodImplOptions.Synchronized)] + public TValue[] GetValues() + { + int i = 0; + TValue[] array = new TValue[_cacheMap.Count]; + foreach (KeyValuePair> kvp in _cacheMap) + { + array[i++] = kvp.Value.Value.Value; + } + + return array; + } + private void Replace(TKey key, TValue value) { LinkedListNode? node = _leastRecentlyUsed; diff --git a/src/Nethermind/Nethermind.Db/DbNames.cs b/src/Nethermind/Nethermind.Db/DbNames.cs index 4d9f0c21350..c6004120c0b 100644 --- a/src/Nethermind/Nethermind.Db/DbNames.cs +++ b/src/Nethermind/Nethermind.Db/DbNames.cs @@ -13,6 +13,7 @@ public static class DbNames public const string BlockNumbers = "blockNumbers"; public const string Receipts = "receipts"; public const string BlockInfos = "blockInfos"; + public const string BadBlocks = "badBlocks"; public const string Bloom = "bloom"; public const string Witness = "witness"; public const string CHT = "canonicalHashTrie"; diff --git a/src/Nethermind/Nethermind.Db/IDbProvider.cs b/src/Nethermind/Nethermind.Db/IDbProvider.cs index dce39fe558a..7c4b764ff11 100644 --- a/src/Nethermind/Nethermind.Db/IDbProvider.cs +++ b/src/Nethermind/Nethermind.Db/IDbProvider.cs @@ -22,6 +22,7 @@ public interface IDbProvider : IDisposable public IDb HeadersDb => GetDb(DbNames.Headers); public IDb BlockNumbersDb => GetDb(DbNames.BlockNumbers); public IDb BlockInfosDb => GetDb(DbNames.BlockInfos); + public IDb BadBlocksDb => GetDb(DbNames.BadBlocks); // BloomDB progress / config (does not contain blooms - they are kept in bloom storage) public IDb BloomDb => GetDb(DbNames.Bloom); diff --git a/src/Nethermind/Nethermind.Db/Metrics.cs b/src/Nethermind/Nethermind.Db/Metrics.cs index 349b7eb430e..114a5227bd1 100644 --- a/src/Nethermind/Nethermind.Db/Metrics.cs +++ b/src/Nethermind/Nethermind.Db/Metrics.cs @@ -126,6 +126,14 @@ public static class Metrics [Description("Number of Metadata DB writes.")] public static long MetadataDbWrites { get; set; } + [CounterMetric] + [Description("Number of BadBlocks DB writes.")] + public static long BadBlocksDbWrites { get; set; } + + [CounterMetric] + [Description("Number of BadBlocks DB reads.")] + public static long BadBlocksDbReads { get; set; } + [CounterMetric] [Description("Number of BlobTransactions DB reads.")] public static long BlobTransactionsDbReads { get; set; } diff --git a/src/Nethermind/Nethermind.Db/StandardDbInitializer.cs b/src/Nethermind/Nethermind.Db/StandardDbInitializer.cs index 543f9adf8ea..d3832216b0d 100644 --- a/src/Nethermind/Nethermind.Db/StandardDbInitializer.cs +++ b/src/Nethermind/Nethermind.Db/StandardDbInitializer.cs @@ -41,6 +41,7 @@ private void RegisterAll(bool useReceiptsDb, bool useBlobsDb) RegisterDb(BuildRocksDbSettings(DbNames.Headers, () => Metrics.HeaderDbReads++, () => Metrics.HeaderDbWrites++)); RegisterDb(BuildRocksDbSettings(DbNames.BlockNumbers, () => Metrics.BlockNumberDbReads++, () => Metrics.BlockNumberDbWrites++)); RegisterDb(BuildRocksDbSettings(DbNames.BlockInfos, () => Metrics.BlockInfosDbReads++, () => Metrics.BlockInfosDbWrites++)); + RegisterDb(BuildRocksDbSettings(DbNames.BadBlocks, () => Metrics.BadBlocksDbReads++, () => Metrics.BadBlocksDbWrites++)); RocksDbSettings stateDbSettings = BuildRocksDbSettings(DbNames.State, () => Metrics.StateDbReads++, () => Metrics.StateDbWrites++); RegisterCustomDb(DbNames.State, () => new FullPruningDb( diff --git a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockTree.cs b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockTree.cs index bd4036393c9..404a6c997a9 100644 --- a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockTree.cs +++ b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockTree.cs @@ -52,12 +52,14 @@ 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); IBlockTree blockTree = _set.BlockTree = new BlockTree( blockStore, headerStore, _get.DbProvider.BlockInfosDb, _get.DbProvider.MetadataDb, + badBlockStore, chainLevelInfoRepository, _get.SpecProvider, bloomStorage, diff --git a/src/Nethermind/Nethermind.Init/Steps/RegisterRpcModules.cs b/src/Nethermind/Nethermind.Init/Steps/RegisterRpcModules.cs index f660a4054a0..298c969c97c 100644 --- a/src/Nethermind/Nethermind.Init/Steps/RegisterRpcModules.cs +++ b/src/Nethermind/Nethermind.Init/Steps/RegisterRpcModules.cs @@ -116,6 +116,7 @@ public virtual async Task Execute(CancellationToken cancellationToken) if (_api.KeyStore is null) throw new StepDependencyException(nameof(_api.KeyStore)); if (_api.PeerPool is null) throw new StepDependencyException(nameof(_api.PeerPool)); if (_api.WitnessRepository is null) throw new StepDependencyException(nameof(_api.WitnessRepository)); + if (_api.BadBlocksStore is null) throw new StepDependencyException(nameof(_api.BadBlocksStore)); ProofModuleFactory proofModuleFactory = new(_api.WorldStateManager, _api.BlockTree, _api.BlockPreprocessor, _api.ReceiptFinder, _api.SpecProvider, _api.LogManager); rpcModuleProvider.RegisterBounded(proofModuleFactory, 2, rpcConfig.Timeout); @@ -133,6 +134,7 @@ public virtual async Task Execute(CancellationToken cancellationToken) _api.ConfigProvider, _api.SpecProvider, _api.SyncModeSelector, + _api.BadBlocksStore, _api.FileSystem, _api.LogManager); rpcModuleProvider.RegisterBoundedByCpuCount(debugModuleFactory, rpcConfig.Timeout); diff --git a/src/Nethermind/Nethermind.JsonRpc.Benchmark/EthModuleBenchmarks.cs b/src/Nethermind/Nethermind.JsonRpc.Benchmark/EthModuleBenchmarks.cs index c846f9c8169..a0fc56d439d 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Benchmark/EthModuleBenchmarks.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Benchmark/EthModuleBenchmarks.cs @@ -73,6 +73,7 @@ public void GlobalSetup() new HeaderStore(dbProvider.HeadersDb, dbProvider.BlockNumbersDb), dbProvider.BlockInfosDb, dbProvider.MetadataDb, + new BlockStore(dbProvider.BadBlocksDb), chainLevelInfoRepository, specProvider, NullBloomStorage.Instance, diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs index ed059c86e65..8b1a48dd697 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/DebugModuleTests.cs @@ -4,17 +4,20 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using FluentAssertions; +using Nethermind.Blockchain; +using Nethermind.Blockchain.Blocks; using Nethermind.Blockchain.Find; using Nethermind.Config; using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Core.Test.Builders; +using Nethermind.Db; using Nethermind.Evm.Tracing.GethStyle; +using Nethermind.Int256; using Nethermind.JsonRpc.Data; using Nethermind.JsonRpc.Modules.DebugModule; using Nethermind.Logging; @@ -31,6 +34,7 @@ public class DebugModuleTests { private readonly IJsonRpcConfig jsonRpcConfig = new JsonRpcConfig(); private readonly IDebugBridge debugBridge = Substitute.For(); + private MemDb _blocksDb = new(); [Test] public async Task Get_from_db() @@ -39,6 +43,7 @@ public async Task Get_from_db() byte[] value = new byte[] { 4, 5, 6 }; debugBridge.GetDbValue(Arg.Any(), Arg.Any()).Returns(value); + IConfigProvider configProvider = Substitute.For(); DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig); JsonRpcSuccessResponse? response = @@ -221,6 +226,46 @@ public async Task Get_trace_with_options() Assert.That(response, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":{\"gas\":\"0x0\",\"failed\":false,\"returnValue\":\"0xa2\",\"structLogs\":[{\"pc\":0,\"op\":\"STOP\",\"gas\":22000,\"gasCost\":1,\"depth\":1,\"error\":null,\"stack\":[],\"memory\":[\"0000000000000000000000000000000000000000000000000000000000000005\",\"0000000000000000000000000000000000000000000000000000000000000006\"],\"storage\":{\"0000000000000000000000000000000000000000000000000000000000000001\":\"0000000000000000000000000000000000000000000000000000000000000002\",\"0000000000000000000000000000000000000000000000000000000000000003\":\"0000000000000000000000000000000000000000000000000000000000000004\"}}]},\"id\":67}")); } + private BlockTree BuildBlockTree(Func? builderOptions = null) + { + BlockTreeBuilder builder = Build.A.BlockTree().WithBlocksDb(_blocksDb).WithBlockStore(new BlockStore(_blocksDb)); + builder = builderOptions?.Invoke(builder) ?? builder; + return builder.TestObject; + } + + [Test] + public void Debug_getBadBlocks_test() + { + IBlockStore badBlocksStore = null!; + BlockTree blockTree = BuildBlockTree(b => b.WithBadBlockStore(badBlocksStore = new BlockStore(b.BadBlocksDb))); + + Block block0 = Build.A.Block.WithNumber(0).WithDifficulty(1).TestObject; + Block block1 = Build.A.Block.WithNumber(1).WithDifficulty(2).WithParent(block0).TestObject; + Block block2 = Build.A.Block.WithNumber(2).WithDifficulty(3).WithParent(block1).TestObject; + Block block3 = Build.A.Block.WithNumber(2).WithDifficulty(4).WithParent(block2).TestObject; + + blockTree.SuggestBlock(block0); + blockTree.SuggestBlock(block1); + blockTree.SuggestBlock(block2); + blockTree.SuggestBlock(block3); + + blockTree.DeleteInvalidBlock(block1); + + BlockDecoder decoder = new(); + _blocksDb.Set(block1.Hash ?? new Hash256("0x0"), decoder.Encode(block1).Bytes); + + debugBridge.GetBadBlocks().Returns(badBlocksStore.GetAll()); + + AddBlockResult result = blockTree.SuggestBlock(block1); + Assert.That(result, Is.EqualTo(AddBlockResult.InvalidBlock)); + + DebugRpcModule rpcModule = new(LimboLogs.Instance, debugBridge, jsonRpcConfig); + ResultWrapper> blocks = rpcModule.debug_getBadBlocks(); + Assert.That(blocks.Data.Count, Is.EqualTo(1)); + Assert.That(blocks.Data.ElementAt(0).Hash, Is.EqualTo(block1.Hash)); + Assert.That(blocks.Data.ElementAt(0).Difficulty, Is.EqualTo(new UInt256(2))); + } + [Test] public async Task Get_trace_with_javascript_setup() { diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugBridge.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugBridge.cs index c55f8e32c61..c32e3b1f7cd 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugBridge.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugBridge.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Nethermind.Blockchain; +using Nethermind.Blockchain.Blocks; using Nethermind.Blockchain.Find; using Nethermind.Blockchain.Receipts; using Nethermind.Config; @@ -32,6 +33,7 @@ public class DebugBridge : IDebugBridge private readonly IReceiptsMigration _receiptsMigration; private readonly ISpecProvider _specProvider; private readonly ISyncModeSelector _syncModeSelector; + private readonly IBlockStore _badBlockStore; private readonly Dictionary _dbMappings; public DebugBridge( @@ -42,7 +44,8 @@ public DebugBridge( IReceiptStorage receiptStorage, IReceiptsMigration receiptsMigration, ISpecProvider specProvider, - ISyncModeSelector syncModeSelector) + ISyncModeSelector syncModeSelector, + IBlockStore badBlockStore) { _configProvider = configProvider ?? throw new ArgumentNullException(nameof(configProvider)); _tracer = tracer ?? throw new ArgumentNullException(nameof(tracer)); @@ -51,6 +54,7 @@ public DebugBridge( _receiptsMigration = receiptsMigration ?? throw new ArgumentNullException(nameof(receiptsMigration)); _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); _syncModeSelector = syncModeSelector ?? throw new ArgumentNullException(nameof(syncModeSelector)); + _badBlockStore = badBlockStore; dbProvider = dbProvider ?? throw new ArgumentNullException(nameof(dbProvider)); IDb blockInfosDb = dbProvider.BlockInfosDb ?? throw new ArgumentNullException(nameof(dbProvider.BlockInfosDb)); IDb blocksDb = dbProvider.BlocksDb ?? throw new ArgumentNullException(nameof(dbProvider.BlocksDb)); @@ -76,25 +80,15 @@ public DebugBridge( } } - public byte[] GetDbValue(string dbName, byte[] key) - { - return _dbMappings[dbName][key]; - } + public IEnumerable GetBadBlocks() => _badBlockStore.GetAll(); - public ChainLevelInfo GetLevelInfo(long number) - { - return _blockTree.FindLevel(number); - } + public byte[] GetDbValue(string dbName, byte[] key) => _dbMappings[dbName][key]; - public int DeleteChainSlice(long startNumber, bool force = false) - { - return _blockTree.DeleteChainSlice(startNumber, force: force); - } + public ChainLevelInfo GetLevelInfo(long number) => _blockTree.FindLevel(number); - public void UpdateHeadBlock(Hash256 blockHash) - { - _blockTree.UpdateHeadBlock(blockHash); - } + public int DeleteChainSlice(long startNumber, bool force = false) => _blockTree.DeleteChainSlice(startNumber, force: force); + + public void UpdateHeadBlock(Hash256 blockHash) => _blockTree.UpdateHeadBlock(blockHash); public Task MigrateReceipts(long blockNumber) => _receiptsMigration.Run(blockNumber + 1); // add 1 to make go from inclusive (better for API) to exclusive (better for internal) @@ -118,6 +112,8 @@ public void InsertReceipts(BlockParameter blockParameter, TxReceipt[] txReceipts _receiptStorage.Insert(block, txReceipts); } + public GethLikeTxTrace GetTransactionTrace(Hash256 transactionHash, CancellationToken cancellationToken, GethTraceOptions? gethTraceOptions = null) => + _tracer.Trace(transactionHash, gethTraceOptions ?? GethTraceOptions.Default, cancellationToken); public TxReceipt[]? GetReceiptsForBlock(BlockParameter blockParam) { SearchResult searchResult = _blockTree.SearchForBlock(blockParam); @@ -143,41 +139,25 @@ public void InsertReceipts(BlockParameter blockParameter, TxReceipt[] txReceipts return block?.Transactions[txReceipt.Index]; } - public GethLikeTxTrace GetTransactionTrace(Hash256 transactionHash, CancellationToken cancellationToken, GethTraceOptions gethTraceOptions = null) - { - return _tracer.Trace(transactionHash, gethTraceOptions ?? GethTraceOptions.Default, cancellationToken); - } + public GethLikeTxTrace GetTransactionTrace(long blockNumber, int index, CancellationToken cancellationToken, GethTraceOptions? gethTraceOptions = null) => + _tracer.Trace(blockNumber, index, gethTraceOptions ?? GethTraceOptions.Default, cancellationToken); - public GethLikeTxTrace GetTransactionTrace(long blockNumber, int index, CancellationToken cancellationToken, GethTraceOptions gethTraceOptions = null) - { - return _tracer.Trace(blockNumber, index, gethTraceOptions ?? GethTraceOptions.Default, cancellationToken); - } + public GethLikeTxTrace GetTransactionTrace(Hash256 blockHash, int index, CancellationToken cancellationToken, GethTraceOptions? gethTraceOptions = null) => + _tracer.Trace(blockHash, index, gethTraceOptions ?? GethTraceOptions.Default, cancellationToken); - public GethLikeTxTrace GetTransactionTrace(Hash256 blockHash, int index, CancellationToken cancellationToken, GethTraceOptions gethTraceOptions = null) - { - return _tracer.Trace(blockHash, index, gethTraceOptions ?? GethTraceOptions.Default, cancellationToken); - } + public GethLikeTxTrace GetTransactionTrace(Rlp blockRlp, Hash256 transactionHash, CancellationToken cancellationToken, GethTraceOptions? gethTraceOptions = null) => + _tracer.Trace(blockRlp, transactionHash, gethTraceOptions ?? GethTraceOptions.Default, cancellationToken); - public GethLikeTxTrace GetTransactionTrace(Rlp blockRlp, Hash256 transactionHash, CancellationToken cancellationToken, GethTraceOptions gethTraceOptions = null) - { - return _tracer.Trace(blockRlp, transactionHash, gethTraceOptions ?? GethTraceOptions.Default, cancellationToken); - } + public GethLikeTxTrace? GetTransactionTrace(Transaction transaction, BlockParameter blockParameter, CancellationToken cancellationToken, GethTraceOptions? gethTraceOptions = null) => + _tracer.Trace(blockParameter, transaction, gethTraceOptions ?? GethTraceOptions.Default, cancellationToken); - public GethLikeTxTrace? GetTransactionTrace(Transaction transaction, BlockParameter blockParameter, CancellationToken cancellationToken, GethTraceOptions? gethTraceOptions = null) - { - return _tracer.Trace(blockParameter, transaction, gethTraceOptions ?? GethTraceOptions.Default, cancellationToken); - } - - public IReadOnlyCollection GetBlockTrace(BlockParameter blockParameter, CancellationToken cancellationToken, GethTraceOptions gethTraceOptions = null) - { - return _tracer.TraceBlock(blockParameter, gethTraceOptions ?? GethTraceOptions.Default, cancellationToken); - } + public IReadOnlyCollection GetBlockTrace(BlockParameter blockParameter, CancellationToken cancellationToken, GethTraceOptions? gethTraceOptions = null) => + _tracer.TraceBlock(blockParameter, gethTraceOptions ?? GethTraceOptions.Default, cancellationToken); - public IReadOnlyCollection GetBlockTrace(Rlp blockRlp, CancellationToken cancellationToken, GethTraceOptions? gethTraceOptions = null) - { - return _tracer.TraceBlock(blockRlp, gethTraceOptions ?? GethTraceOptions.Default, cancellationToken); - } + public IReadOnlyCollection GetBlockTrace(Rlp blockRlp, CancellationToken cancellationToken, GethTraceOptions? gethTraceOptions = null) => + _tracer.TraceBlock(blockRlp, gethTraceOptions ?? GethTraceOptions.Default, cancellationToken); + public byte[] GetBlockRlp(Hash256 blockHash) => _dbMappings[DbNames.Blocks].Get(blockHash); public byte[]? GetBlockRlp(BlockParameter parameter) { @@ -193,11 +173,6 @@ public IReadOnlyCollection GetBlockTrace(Rlp blockRlp, Cancella return null; } - public byte[] GetBlockRlp(Hash256 blockHash) - { - return _dbMappings[DbNames.Blocks].Get(blockHash); - } - public Block? GetBlock(BlockParameter param) => _blockTree.FindBlock(param); public byte[] GetBlockRlp(long number) @@ -206,10 +181,7 @@ public byte[] GetBlockRlp(long number) return hash is null ? null : _dbMappings[DbNames.Blocks].Get(hash); } - public object GetConfigValue(string category, string name) - { - return _configProvider.GetRawValue(category, name); - } + public object GetConfigValue(string category, string name) => _configProvider.GetRawValue(category, name); public SyncReportSymmary GetCurrentSyncStage() { diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugModuleFactory.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugModuleFactory.cs index e9664e06f0d..948f48a22ce 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugModuleFactory.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugModuleFactory.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO.Abstractions; using Nethermind.Blockchain; +using Nethermind.Blockchain.Blocks; using Nethermind.Blockchain.Receipts; using Nethermind.Config; using Nethermind.Consensus.Processing; @@ -39,6 +40,7 @@ public class DebugModuleFactory : ModuleFactoryBase private readonly IReadOnlyDbProvider _dbProvider; private readonly IReadOnlyBlockTree _blockTree; private readonly ISyncModeSelector _syncModeSelector; + private readonly IBlockStore _badBlockStore; private readonly IFileSystem _fileSystem; private readonly ILogger _logger; @@ -55,6 +57,7 @@ public DebugModuleFactory( IConfigProvider configProvider, ISpecProvider specProvider, ISyncModeSelector syncModeSelector, + IBlockStore badBlockStore, IFileSystem fileSystem, ILogManager logManager) { @@ -71,6 +74,7 @@ public DebugModuleFactory( _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); _logManager = logManager ?? throw new ArgumentNullException(nameof(logManager)); _syncModeSelector = syncModeSelector ?? throw new ArgumentNullException(nameof(syncModeSelector)); + _badBlockStore = badBlockStore; _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); _logger = logManager.GetClassLogger(); } @@ -112,7 +116,8 @@ public override IDebugRpcModule Create() _receiptStorage, _receiptsMigration, _specProvider, - _syncModeSelector); + _syncModeSelector, + _badBlockStore); return new DebugRpcModule(_logManager, debugBridge, _jsonRpcConfig); } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugRpcModule.cs index d24f73cb7fc..2816c277070 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/DebugRpcModule.cs @@ -321,4 +321,10 @@ public ResultWrapper> debug_standardTraceBlockToFile(Hash256 return ResultWrapper>.Success(files); } + + public ResultWrapper> debug_getBadBlocks() + { + IEnumerable badBlocks = _debugBridge.GetBadBlocks(); + return ResultWrapper>.Success(badBlocks); + } } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugBridge.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugBridge.cs index 732dcdff7f7..73cc66f74b0 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugBridge.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugBridge.cs @@ -35,6 +35,7 @@ public interface IDebugBridge void InsertReceipts(BlockParameter blockParameter, TxReceipt[] receipts); SyncReportSymmary GetCurrentSyncStage(); IEnumerable TraceBlockToFile(Hash256 blockHash, CancellationToken cancellationToken, GethTraceOptions? gethTraceOptions = null); + public IEnumerable GetBadBlocks(); TxReceipt[]? GetReceiptsForBlock(BlockParameter param); Transaction? GetTransactionFromHash(Hash256 hash); } diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugRpcModule.cs index 932a1d300da..e17f692779e 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/DebugModule/IDebugRpcModule.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Nethermind.Blockchain.Find; +using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Evm.Tracing.GethStyle; using Nethermind.JsonRpc.Data; @@ -106,4 +107,7 @@ public interface IDebugRpcModule : IRpcModule [JsonRpcMethod(Description = "Writes to a file the full stack trace of all invoked opcodes of the transaction specified (or all transactions if not specified) that was included in the block specified. The parent of the block must be present or it will fail.", IsImplemented = true, IsSharable = false)] ResultWrapper> debug_standardTraceBlockToFile(Hash256 blockHash, GethTraceOptions options = null); + + [JsonRpcMethod(Description = "Return list of invalid blocks.")] + ResultWrapper> debug_getBadBlocks(); } diff --git a/src/Nethermind/Nethermind.Runner.Test/Ethereum/ContextWithMocks.cs b/src/Nethermind/Nethermind.Runner.Test/Ethereum/ContextWithMocks.cs index 0550a49d49f..6d8f3484754 100644 --- a/src/Nethermind/Nethermind.Runner.Test/Ethereum/ContextWithMocks.cs +++ b/src/Nethermind/Nethermind.Runner.Test/Ethereum/ContextWithMocks.cs @@ -44,6 +44,7 @@ using Nethermind.Specs; using Nethermind.Synchronization.SnapSync; using NSubstitute; +using Nethermind.Blockchain.Blocks; namespace Nethermind.Runner.Test.Ethereum { @@ -117,7 +118,8 @@ public static NethermindApi ContextWithMocks() SyncProgressResolver = Substitute.For(), BetterPeerStrategy = Substitute.For(), ReceiptMonitor = Substitute.For(), - WitnessRepository = Substitute.For() + WitnessRepository = Substitute.For(), + BadBlocksStore = Substitute.For() }; api.WorldStateManager = new ReadOnlyWorldStateManager(api.DbProvider, Substitute.For(), LimboLogs.Instance);