diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Filters/FilterManagerTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Filters/FilterManagerTests.cs index 36194e57e04d..0b863d6112bc 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Filters/FilterManagerTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Filters/FilterManagerTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using FluentAssertions; using Nethermind.Blockchain.Filters; using Nethermind.Blockchain.Test.Builders; @@ -11,6 +12,7 @@ using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Test.Builders; +using Nethermind.Core.Timers; using Nethermind.Facade.Filters; using Nethermind.Logging; using Nethermind.TxPool; @@ -21,7 +23,7 @@ namespace Nethermind.Blockchain.Test.Filters; public class FilterManagerTests { - private IFilterStore _filterStore = null!; + private FilterStore _filterStore = null!; private IBranchProcessor _branchProcessor = null!; private IMainProcessingContext _mainProcessingContext = null!; private ITxPool _txPool = null!; @@ -34,7 +36,7 @@ public class FilterManagerTests public void Setup() { _currentFilterId = 0; - _filterStore = Substitute.For(); + _filterStore = new FilterStore(new TimerFactory(), 20, 10); _branchProcessor = Substitute.For(); _mainProcessingContext = Substitute.For(); _mainProcessingContext.BranchProcessor.Returns(_branchProcessor); @@ -49,11 +51,11 @@ public void TearDown() } [Test, MaxTime(Timeout.MaxTestTime)] - public void removing_filter_removes_data() + public async Task removing_filter_removes_data() { LogsShouldNotBeEmpty(static _ => { }, static _ => { }); _filterManager.GetLogs(0).Should().NotBeEmpty(); - _filterStore.FilterRemoved += Raise.EventWith(new FilterEventArgs(0)); + await Task.Delay(60); _filterManager.GetLogs(0).Should().BeEmpty(); } @@ -324,8 +326,8 @@ private void Assert(IEnumerable> filterBuilders, BlockFilter blockFilter = new(_currentFilterId++); filters.Add(blockFilter); - _filterStore.GetFilters().Returns(filters.OfType().ToArray()); - _filterStore.GetFilters().Returns(filters.OfType().ToArray()); + _filterStore.SaveFilters(filters.OfType()); + _filterStore.SaveFilters(filters.OfType()); _filterManager = new FilterManager(_filterStore, _mainProcessingContext, _txPool, _logManager); _branchProcessor.BlockProcessed += Raise.EventWith(_branchProcessor, new BlockProcessedEventArgs(block, [])); @@ -367,3 +369,15 @@ private static TxReceipt BuildReceipt(Action builder) return builderInstance.TestObject; } } + +file static class FilterExtensions +{ + public static void SaveFilters(this FilterStore store, IEnumerable filters) + where T : FilterBase + { + foreach (T filter in filters) + { + store.SaveFilter(filter); + } + } +} diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Filters/FilterStoreTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Filters/FilterStoreTests.cs index f7fdea314f18..d6706b6a5e13 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Filters/FilterStoreTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Filters/FilterStoreTests.cs @@ -166,6 +166,7 @@ public async Task CleanUps_filters() Assert.That(() => store.FilterExists(1), Is.False.After(30, 5), "filter 1 doesn't exist"); Assert.That(() => store.FilterExists(2), Is.False.After(30, 5), "filter 2 doesn't exist"); Assert.That(() => store.FilterExists(3), Is.False.After(30, 5), "filter 3 doesn't exist"); + store.RefreshFilter(0); Assert.That(() => removedFilterIds, Is.EquivalentTo([1, 2, 3]).After(30, 5)); } } diff --git a/src/Nethermind/Nethermind.Facade/BlockchainBridge.cs b/src/Nethermind/Nethermind.Facade/BlockchainBridge.cs index bd319ebb8686..430ab4cb28b5 100644 --- a/src/Nethermind/Nethermind.Facade/BlockchainBridge.cs +++ b/src/Nethermind/Nethermind.Facade/BlockchainBridge.cs @@ -44,8 +44,8 @@ public class BlockchainBridge( IStateReader stateReader, ITxPool txPool, IReceiptFinder receiptStorage, - IFilterStore filterStore, - IFilterManager filterManager, + FilterStore filterStore, + FilterManager filterManager, IEthereumEcdsa ecdsa, ITimestamper timestamper, ILogFinder logFinder, diff --git a/src/Nethermind/Nethermind.Facade/Filters/FilterManager.cs b/src/Nethermind/Nethermind.Facade/Filters/FilterManager.cs index 4cdf04664915..179c8648e199 100644 --- a/src/Nethermind/Nethermind.Facade/Filters/FilterManager.cs +++ b/src/Nethermind/Nethermind.Facade/Filters/FilterManager.cs @@ -15,7 +15,7 @@ namespace Nethermind.Blockchain.Filters { - public class FilterManager : IFilterManager + public sealed class FilterManager { private readonly ConcurrentDictionary> _logs = new(); @@ -27,12 +27,12 @@ public class FilterManager : IFilterManager new(); private Hash256? _lastBlockHash; - private readonly IFilterStore _filterStore; + private readonly FilterStore _filterStore; private readonly ILogger _logger; private long _logIndex; public FilterManager( - IFilterStore filterStore, + FilterStore filterStore, IMainProcessingContext mainProcessingContext, ITxPool txPool, ILogManager logManager) diff --git a/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs b/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs index 808f05d887df..f7b192cb42ce 100644 --- a/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs +++ b/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs @@ -17,7 +17,7 @@ namespace Nethermind.Blockchain.Filters { - public class FilterStore : IFilterStore + public sealed class FilterStore : IDisposable { private readonly TimeSpan _timeout; private int _currentFilterId = -1; @@ -84,6 +84,16 @@ private void CleanupStaleFilters() } public IEnumerable GetFilters() where T : FilterBase + { + // Return Array.Empty() to avoid allocating enumerator + // and which has a non-allocating fast-path for + // foreach via IEnumerable + if (_filters.IsEmpty) return Array.Empty(); + + return GetFiltersEnumerate(); + } + + private IEnumerable GetFiltersEnumerate() where T : FilterBase { // Reuse the enumerator var enumerator = Interlocked.Exchange(ref _enumerator, null) ?? _filters.GetEnumerator(); @@ -131,7 +141,7 @@ public void RemoveFilter(int filterId) public void SaveFilter(FilterBase filter) { - if (_filters.ContainsKey(filter.Id)) + if (!_filters.TryAdd(filter.Id, filter)) { throw new InvalidOperationException($"Filter with ID {filter.Id} already exists"); } @@ -140,8 +150,6 @@ public void SaveFilter(FilterBase filter) { _currentFilterId = Math.Max(filter.Id, _currentFilterId); } - - _filters[filter.Id] = filter; } private int GetFilterId(bool generateId) diff --git a/src/Nethermind/Nethermind.Facade/Filters/IFilterManager.cs b/src/Nethermind/Nethermind.Facade/Filters/IFilterManager.cs deleted file mode 100644 index 27a07e029b89..000000000000 --- a/src/Nethermind/Nethermind.Facade/Filters/IFilterManager.cs +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using Nethermind.Core.Crypto; -using Nethermind.Facade.Filters; - -namespace Nethermind.Blockchain.Filters -{ - public interface IFilterManager - { - FilterLog[] PollLogs(int filterId); - Hash256[] PollBlockHashes(int filterId); - Hash256[] PollPendingTransactionHashes(int filterId); - } -} diff --git a/src/Nethermind/Nethermind.Facade/Filters/IFilterStore.cs b/src/Nethermind/Nethermind.Facade/Filters/IFilterStore.cs deleted file mode 100644 index 58e978f67d96..000000000000 --- a/src/Nethermind/Nethermind.Facade/Filters/IFilterStore.cs +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Collections.Generic; -using Nethermind.Blockchain.Find; - -namespace Nethermind.Blockchain.Filters -{ - public interface IFilterStore : IDisposable - { - bool FilterExists(int filterId); - IEnumerable GetFilters() where T : FilterBase; - T? GetFilter(int filterId) where T : FilterBase; - BlockFilter CreateBlockFilter(bool setId = true); - PendingTransactionFilter CreatePendingTransactionFilter(bool setId = true); - - LogFilter CreateLogFilter( - BlockParameter fromBlock, - BlockParameter toBlock, - object? address = null, - IEnumerable? topics = null, - bool setId = true); - - void SaveFilter(FilterBase filter); - void RemoveFilter(int filterId); - void RefreshFilter(int filterId); - FilterType GetFilterType(int filterId); - - event EventHandler FilterRemoved; - } -} diff --git a/src/Nethermind/Nethermind.Facade/Filters/NullFilterManager.cs b/src/Nethermind/Nethermind.Facade/Filters/NullFilterManager.cs deleted file mode 100644 index 6fc634af8d23..000000000000 --- a/src/Nethermind/Nethermind.Facade/Filters/NullFilterManager.cs +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using Nethermind.Core.Crypto; -using Nethermind.Facade.Filters; - -namespace Nethermind.Blockchain.Filters -{ - public class NullFilterManager : IFilterManager - { - public static NullFilterManager Instance { get; } = new(); - - private NullFilterManager() - { - } - - public FilterLog[] GetLogs(int filterId) - { - return []; - } - - public FilterLog[] PollLogs(int filterId) - { - return []; - } - - public Hash256[] GetBlocksHashes(int filterId) - { - return []; - } - - public Hash256[] PollBlockHashes(int filterId) - { - return []; - } - - public Hash256[] PollPendingTransactionHashes(int filterId) - { - return []; - } - } -} diff --git a/src/Nethermind/Nethermind.Facade/Filters/NullFilterStore.cs b/src/Nethermind/Nethermind.Facade/Filters/NullFilterStore.cs deleted file mode 100644 index a034ba44364e..000000000000 --- a/src/Nethermind/Nethermind.Facade/Filters/NullFilterStore.cs +++ /dev/null @@ -1,76 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Collections.Generic; -using Nethermind.Blockchain.Find; - -namespace Nethermind.Blockchain.Filters -{ - public class NullFilterStore : IFilterStore - { - private NullFilterStore() - { - } - - public static NullFilterStore Instance { get; } = new(); - - public bool FilterExists(int filterId) - { - return false; - } - - public IEnumerable GetFilters() where T : FilterBase - { - return Array.Empty(); - } - - public BlockFilter CreateBlockFilter(bool setId = true) - { - throw new InvalidOperationException($"{nameof(NullFilterStore)} does not support filter creation"); - } - - public PendingTransactionFilter CreatePendingTransactionFilter(bool setId = true) - { - throw new InvalidOperationException($"{nameof(NullFilterStore)} does not support filter creation"); - } - - public LogFilter CreateLogFilter(BlockParameter fromBlock, BlockParameter toBlock, object? address = null, IEnumerable? topics = null, bool setId = true) - { - throw new InvalidOperationException($"{nameof(NullFilterStore)} does not support filter creation"); - } - - public void SaveFilter(FilterBase filter) - { - throw new InvalidOperationException($"{nameof(NullFilterStore)} does not support filter creation"); - } - - public void RemoveFilter(int filterId) - { - throw new InvalidOperationException($"{nameof(NullFilterStore)} does not support filter creation"); - } - - public void RefreshFilter(int filterId) - { - throw new InvalidOperationException($"{nameof(NullFilterStore)} does not support filter refreshing"); - } - - public FilterType GetFilterType(int filterId) - { - throw new InvalidOperationException($"{nameof(NullFilterStore)} does not support filter creation"); - } - - public T? GetFilter(int filterId) where T : FilterBase - { - return null; - } - - public event EventHandler FilterRemoved - { - add { } - remove { } - } - - public void Dispose() { } - } -} diff --git a/src/Nethermind/Nethermind.Init/Modules/RpcModules.cs b/src/Nethermind/Nethermind.Init/Modules/RpcModules.cs index 420c1fa26b01..8f0ee78e45ff 100644 --- a/src/Nethermind/Nethermind.Init/Modules/RpcModules.cs +++ b/src/Nethermind/Nethermind.Init/Modules/RpcModules.cs @@ -81,8 +81,8 @@ protected override void Load(ContainerBuilder builder) .AddSingleton() .AddScoped((ctx) => ctx.Resolve().CreateBlockchainBridge()) .AddSingleton() - .AddSingleton((timerFactory, rpcConfig) => new FilterStore(timerFactory, rpcConfig.FiltersTimeout)) - .AddSingleton() + .AddSingleton((timerFactory, rpcConfig) => new FilterStore(timerFactory, rpcConfig.FiltersTimeout)) + .AddSingleton() .AddSingleton() // Proof diff --git a/src/Nethermind/Nethermind.Init/Steps/RegisterRpcModules.cs b/src/Nethermind/Nethermind.Init/Steps/RegisterRpcModules.cs index ed451c7272a9..bb808aad08dd 100644 --- a/src/Nethermind/Nethermind.Init/Steps/RegisterRpcModules.cs +++ b/src/Nethermind/Nethermind.Init/Steps/RegisterRpcModules.cs @@ -30,7 +30,7 @@ public class RegisterRpcModules( IBlockTree blockTree, ISpecProvider specProvider, IReceiptMonitor receiptMonitor, - IFilterStore filterStore, + FilterStore filterStore, ITxPool txPool, IEthSyncingInfo ethSyncingInfo, IPeerPool peerPool, diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/SubscribeModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/SubscribeModuleTests.cs index 64c5f12b8676..edf0f8bbfa5c 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/SubscribeModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/SubscribeModuleTests.cs @@ -52,7 +52,7 @@ public class SubscribeModuleTests private IBlockTree _blockTree = null!; private ITxPool _txPool = null!; private IReceiptStorage _receiptStorage = null!; - private IFilterStore _filterStore = null!; + private FilterStore _filterStore = null!; private ISubscriptionManager _subscriptionManager = null!; private IJsonRpcDuplexClient _jsonRpcDuplexClient = null!; private IJsonSerializer _jsonSerializer = null!; diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/LogsSubscription.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/LogsSubscription.cs index 5afc5f206636..4ea94824146e 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/LogsSubscription.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/LogsSubscription.cs @@ -24,7 +24,7 @@ public class LogsSubscription : Subscription public LogsSubscription( IJsonRpcDuplexClient jsonRpcDuplexClient, IReceiptMonitor receiptCanonicalityMonitor, - IFilterStore? store, + FilterStore? store, IBlockTree? blockTree, ILogManager? logManager, Filter? filter = null) @@ -33,7 +33,7 @@ public LogsSubscription( _blockTree = blockTree ?? throw new ArgumentNullException(nameof(blockTree)); _receiptCanonicalityMonitor = receiptCanonicalityMonitor ?? throw new ArgumentNullException(nameof(receiptCanonicalityMonitor)); _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); - IFilterStore filterStore = store ?? throw new ArgumentNullException(nameof(store)); + FilterStore filterStore = store ?? throw new ArgumentNullException(nameof(store)); if (filter is not null) { diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/SubscriptionFactoryExtensions.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/SubscriptionFactoryExtensions.cs index 3b4fcc3d4df9..bc5ee9b6d7cd 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/SubscriptionFactoryExtensions.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/SubscriptionFactoryExtensions.cs @@ -2,10 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Nethermind.Blockchain; using Nethermind.Blockchain.Filters; using Nethermind.Core.Specs; @@ -37,7 +33,7 @@ ISpecProvider specProvider public static void RegisterLogsSubscription( this ISubscriptionFactory subscriptionFactory, IReceiptMonitor receiptMonitor, - IFilterStore? filterStore, + FilterStore? filterStore, IBlockTree? blockTree, ILogManager? logManager ) @@ -110,7 +106,7 @@ public static void RegisterStandardSubscriptions( ILogManager? logManager, ISpecProvider specProvider, IReceiptMonitor receiptMonitor, - IFilterStore? filterStore, + FilterStore? filterStore, ITxPool? txPool, IEthSyncingInfo ethSyncingInfo, IPeerPool? peerPool, @@ -131,7 +127,7 @@ public static void RegisterStandardEthSubscriptions( ILogManager? logManager, ISpecProvider specProvider, IReceiptMonitor receiptMonitor, - IFilterStore? filterStore, + FilterStore? filterStore, ITxPool? txPool, IEthSyncingInfo ethSyncingInfo )