Make new classes hosted services
MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/SendControlViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/SendControlViewModel.cs
@@ -553,7 +553,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
private set => this.RaiseAndSetIfChanged(ref _isCustomFee, value);
}
- public bool IsEstimateAvailable => Global.FeeProvider?.AllFeeEstimate is { };
+ public bool IsEstimateAvailable => Global.HostedServices.GetOrDefault<HybridFeeProvider>()?.AllFeeEstimate is { };
public string AmountWatermarkText
{
@@ -634,7 +634,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
private void SetFees()
{
- AllFeeEstimate? allFeeEstimate = Global.FeeProvider?.AllFeeEstimate;
+ AllFeeEstimate? allFeeEstimate = Global.HostedServices.GetOrDefault<HybridFeeProvider>()?.AllFeeEstimate;
int feeTarget = -1; // 1 => 10 minutes
if (IsSliderFeeUsed && allFeeEstimate is { })
@@ -795,7 +795,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
private void SetFeeTargetLimits()
{
- var allFeeEstimate = Global.FeeProvider?.AllFeeEstimate;
+ var allFeeEstimate = Global.HostedServices.GetOrDefault<HybridFeeProvider>()?.AllFeeEstimate;
if (allFeeEstimate is { })
{
@@ -871,8 +871,9 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
public override void OnOpen(CompositeDisposable disposables)
{
+ var feeProvider = Global.HostedServices.Get<HybridFeeProvider>();
Observable
- .FromEventPattern<AllFeeEstimate>(Global.FeeProvider, nameof(Global.FeeProvider.AllFeeEstimateChanged))
+ .FromEventPattern<AllFeeEstimate>(feeProvider, nameof(feeProvider.AllFeeEstimateChanged))
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(_ => this.RaisePropertyChanged(nameof(IsEstimateAvailable)))
.DisposeWith(disposables);
@@ -880,7 +881,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
this.RaisePropertyChanged(nameof(IsEstimateAvailable));
Observable
- .FromEventPattern<AllFeeEstimate>(Global.FeeProvider, nameof(Global.FeeProvider.AllFeeEstimateChanged))
+ .FromEventPattern<AllFeeEstimate>(feeProvider, nameof(feeProvider.AllFeeEstimateChanged))
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(_ =>
{

MODIFY
WalletWasabi.Gui/Global.cs
WalletWasabi.Gui/Global.cs
@@ -56,8 +56,6 @@ namespace WalletWasabi.Gui
public NodesGroup Nodes { get; private set; }
public WasabiSynchronizer Synchronizer { get; private set; }
- public HybridFeeProvider FeeProvider { get; private set; }
- public FilterProcessor FilterProcessor { get; private set; }
public WalletManager WalletManager { get; }
public TransactionBroadcaster TransactionBroadcaster { get; set; }
public CoinJoinProcessor CoinJoinProcessor { get; set; }
@@ -186,6 +184,8 @@ namespace WalletWasabi.Gui
#endregion BitcoinStoreInitialization
+ HostedServices.Register<FilterProcessor>(new FilterProcessor(Synchronizer, BitcoinStore), "Filter Processor");
+
cancel.ThrowIfCancellationRequested();
#region BitcoinCoreInitialization
@@ -225,17 +225,7 @@ namespace WalletWasabi.Gui
#endregion BitcoinCoreInitialization
- #region FeeProviderInitialization
-
- FeeProvider = new HybridFeeProvider(Synchronizer, HostedServices.GetOrDefault<RpcFeeProvider>());
-
- #endregion FeeProviderInitialization
-
- #region FilterProcessorInitialization
-
- FilterProcessor = new FilterProcessor(Synchronizer, BitcoinStore);
-
- #endregion FilterProcessorInitialization
+ HostedServices.Register<HybridFeeProvider>(new HybridFeeProvider(Synchronizer, HostedServices.GetOrDefault<RpcFeeProvider>()), "Hybrid Fee Provider");
await HostedServices.StartAllAsync(cancel).ConfigureAwait(false);
@@ -353,7 +343,7 @@ namespace WalletWasabi.Gui
cancel.ThrowIfCancellationRequested();
- WalletManager.RegisterServices(BitcoinStore, Synchronizer, Config.ServiceConfiguration, FeeProvider, blockProvider);
+ WalletManager.RegisterServices(BitcoinStore, Synchronizer, Config.ServiceConfiguration, HostedServices.Get<HybridFeeProvider>(), blockProvider);
}
finally
{
@@ -642,22 +632,6 @@ namespace WalletWasabi.Gui
Logger.LogInfo($"{nameof(RpcServer)} is stopped.", nameof(Global));
}
- Logger.LogDebug($"Step: {nameof(FilterProcessor)}.", nameof(Global));
-
- if (FilterProcessor is { } filterProcessor)
- {
- filterProcessor.Dispose();
- Logger.LogInfo($"Disposed {nameof(FilterProcessor)}.");
- }
-
- Logger.LogDebug($"Step: {nameof(FeeProvider)}.", nameof(Global));
-
- if (FeeProvider is { } feeProviders)
- {
- feeProviders.Dispose();
- Logger.LogInfo($"Disposed {nameof(FeeProvider)}.");
- }
-
Logger.LogDebug($"Step: {nameof(CoinJoinProcessor)}.", nameof(Global));
if (CoinJoinProcessor is { } coinJoinProcessor)

MODIFY
WalletWasabi.Tests/IntegrationTests/P2pTests.cs
WalletWasabi.Tests/IntegrationTests/P2pTests.cs
@@ -96,8 +96,9 @@ namespace WalletWasabi.Tests.IntegrationTests
KeyManager keyManager = KeyManager.CreateNew(out _, "password");
HttpClientFactory httpClientFactory = new(Common.TorSocks5Endpoint, backendUriGetter: () => new Uri("http://localhost:12345"));
- WasabiSynchronizer syncer = new(bitcoinStore, httpClientFactory);
- using var feeProvider = new HybridFeeProvider(syncer, null);
+ WasabiSynchronizer synchronizer = new(bitcoinStore, httpClientFactory);
+ var feeProvider = new HybridFeeProvider(synchronizer, null);
+
ServiceConfiguration serviceConfig = new(MixUntilAnonymitySet.PrivacyLevelStrong.ToString(), 2, 21, 50, new IPEndPoint(IPAddress.Loopback, network.DefaultPort), Money.Coins(Constants.DefaultDustThreshold));
CachedBlockProvider blockProvider = new(
new P2pBlockProvider(nodes, null, httpClientFactory, serviceConfig, network),
@@ -107,7 +108,7 @@ namespace WalletWasabi.Tests.IntegrationTests
network,
bitcoinStore,
keyManager,
- syncer,
+ synchronizer,
dataDir,
new ServiceConfiguration(MixUntilAnonymitySet.PrivacyLevelStrong.ToString(), 2, 21, 50, new IPEndPoint(IPAddress.Loopback, network.DefaultPort), Money.Coins(Constants.DefaultDustThreshold)),
feeProvider,
@@ -168,8 +169,6 @@ namespace WalletWasabi.Tests.IntegrationTests
addressManager?.SavePeerFile(addressManagerFilePath, network);
Logger.LogInfo($"Saved {nameof(AddressManager)} to `{addressManagerFilePath}`.");
- await syncer?.StopAsync();
-
await bitcoinStore.DisposeAsync();
}
}

MODIFY
WalletWasabi.Tests/RegressionTests/BuildTests.cs
WalletWasabi.Tests/RegressionTests/BuildTests.cs
@@ -56,8 +56,8 @@ namespace WalletWasabi.Tests.RegressionTests
// 3. Create wasabi synchronizer service.
HttpClientFactory httpClientFactory = new(torEndPoint: null, backendUriGetter: () => new Uri(RegTestFixture.BackendEndPoint));
WasabiSynchronizer synchronizer = new(bitcoinStore, httpClientFactory);
- using FilterProcessor filterProcessor = new(synchronizer, bitcoinStore);
- using HybridFeeProvider feeProvider = new(synchronizer, null);
+ FilterProcessor filterProcessor = new(synchronizer, bitcoinStore);
+ HybridFeeProvider feeProvider = new(synchronizer, null);
// 4. Create key manager service.
var keyManager = KeyManager.CreateNew(out _, password);
@@ -132,6 +132,9 @@ namespace WalletWasabi.Tests.RegressionTests
node.VersionHandshake(); // Start mempool service.
synchronizer.Start(requestInterval: TimeSpan.FromSeconds(3), 10000); // Start wasabi synchronizer service.
+ await filterProcessor.StartAsync(CancellationToken.None);
+ await feeProvider.StartAsync(CancellationToken.None);
+
// Wait until the filter our previous transaction is present.
var blockCount = await rpc.GetBlockCountAsync();
await Common.WaitForFiltersToBeProcessedAsync(TimeSpan.FromSeconds(120), blockCount);
@@ -188,14 +191,10 @@ namespace WalletWasabi.Tests.RegressionTests
finally
{
await wallet.StopAsync(CancellationToken.None);
- // Dispose wasabi synchronizer service.
- if (synchronizer is { })
- {
await synchronizer.StopAsync();
- }
- // Dispose connection service.
+ await filterProcessor.StopAsync(CancellationToken.None);
+ await feeProvider.StopAsync(CancellationToken.None);
nodes?.Dispose();
- // Dispose mempool serving node.
node?.Disconnect();
}
}
@@ -217,8 +216,8 @@ namespace WalletWasabi.Tests.RegressionTests
// 3. Create wasabi synchronizer service.
HttpClientFactory httpClientFactory = new(torEndPoint: null, backendUriGetter: () => new Uri(RegTestFixture.BackendEndPoint));
WasabiSynchronizer synchronizer = new(bitcoinStore, httpClientFactory);
- using FilterProcessor filterProcessor = new(synchronizer, bitcoinStore);
- using HybridFeeProvider feeProvider = new(synchronizer, null);
+ FilterProcessor filterProcessor = new(synchronizer, bitcoinStore);
+ HybridFeeProvider feeProvider = new(synchronizer, null);
// 4. Create key manager service.
var keyManager = KeyManager.CreateNew(out _, password);
@@ -248,6 +247,8 @@ namespace WalletWasabi.Tests.RegressionTests
nodes.Connect(); // Start connection service.
node.VersionHandshake(); // Start mempool service.
synchronizer.Start(requestInterval: TimeSpan.FromSeconds(3), 10000); // Start wasabi synchronizer service.
+ await filterProcessor.StartAsync(CancellationToken.None);
+ await feeProvider.StartAsync(CancellationToken.None);
// Wait until the filter our previous transaction is present.
var blockCount = await rpc.GetBlockCountAsync();
@@ -408,14 +409,10 @@ namespace WalletWasabi.Tests.RegressionTests
{
bitcoinStore.IndexStore.NewFilter -= Common.Wallet_NewFilterProcessed;
await walletManager.RemoveAndStopAllAsync(CancellationToken.None);
- // Dispose wasabi synchronizer service.
- if (synchronizer is { })
- {
await synchronizer.StopAsync();
- }
- // Dispose connection service.
+ await filterProcessor.StopAsync(CancellationToken.None);
+ await feeProvider.StopAsync(CancellationToken.None);
nodes?.Dispose();
- // Dispose mempool serving node.
node?.Disconnect();
}
}

MODIFY
WalletWasabi.Tests/RegressionTests/CoinJoinTests.cs
WalletWasabi.Tests/RegressionTests/CoinJoinTests.cs
@@ -1380,13 +1380,13 @@ namespace WalletWasabi.Tests.RegressionTests
// 3. Create wasabi synchronizer service.
HttpClientFactory httpClientFactory = new(torEndPoint: null, backendUriGetter: () => new Uri(RegTestFixture.BackendEndPoint));
WasabiSynchronizer synchronizer = new(bitcoinStore, httpClientFactory);
- using FilterProcessor filterProcessor = new(synchronizer, bitcoinStore);
- using HybridFeeProvider feeProvider = new(synchronizer, null);
+ FilterProcessor filterProcessor = new(synchronizer, bitcoinStore);
+ HybridFeeProvider feeProvider = new(synchronizer, null);
var indexFilePath2 = Path.Combine(Helpers.Common.GetWorkDir(), $"Index{network}2.dat");
WasabiSynchronizer synchronizer2 = new(bitcoinStore, httpClientFactory);
- using FilterProcessor filterProcessor2 = new(synchronizer2, bitcoinStore);
- using HybridFeeProvider feeProvider2 = new(synchronizer2, null);
+ FilterProcessor filterProcessor2 = new(synchronizer, bitcoinStore);
+ HybridFeeProvider feeProvider2 = new(synchronizer, null);
// 4. Create key manager service.
var keyManager = KeyManager.CreateNew(out _, password);
@@ -1434,6 +1434,11 @@ namespace WalletWasabi.Tests.RegressionTests
node2.VersionHandshake(); // Start mempool service.
synchronizer2.Start(requestInterval: TimeSpan.FromSeconds(3), 10000); // Start wasabi synchronizer service.
+ await filterProcessor.StartAsync(CancellationToken.None);
+ await feeProvider.StartAsync(CancellationToken.None);
+ await filterProcessor2.StartAsync(CancellationToken.None);
+ await feeProvider2.StartAsync(CancellationToken.None);
+
// Wait until the filter our previous transaction is present.
var blockCount = await rpc.GetBlockCountAsync();
await Common.WaitForFiltersToBeProcessedAsync(TimeSpan.FromSeconds(120), blockCount);
@@ -1532,17 +1537,15 @@ namespace WalletWasabi.Tests.RegressionTests
{
wallet.NewFilterProcessed -= Common.Wallet_NewFilterProcessed;
await wallet.StopAsync(CancellationToken.None);
- // Dispose connection service.
nodes?.Dispose();
- // Dispose mempool serving node.
node?.Disconnect();
await wallet2.StopAsync(CancellationToken.None);
- // Dispose wasabi synchronizer service.
- if (synchronizer is { })
- {
await synchronizer.StopAsync();
- }
- // Dispose connection service.
+ await synchronizer2.StopAsync();
+ await filterProcessor.StopAsync(CancellationToken.None);
+ await filterProcessor2.StopAsync(CancellationToken.None);
+ await feeProvider.StopAsync(CancellationToken.None);
+ await feeProvider2.StopAsync(CancellationToken.None);
nodes2?.Dispose();
}
}

MODIFY
WalletWasabi.Tests/RegressionTests/SendTests.cs
WalletWasabi.Tests/RegressionTests/SendTests.cs
@@ -53,8 +53,8 @@ namespace WalletWasabi.Tests.RegressionTests
// 3. Create wasabi synchronizer service.
HttpClientFactory httpClientFactory = new(torEndPoint: null, backendUriGetter: () => new Uri(RegTestFixture.BackendEndPoint));
WasabiSynchronizer synchronizer = new(bitcoinStore, httpClientFactory);
- using FilterProcessor filterProcessor = new(synchronizer, bitcoinStore);
- using HybridFeeProvider feeProvider = new(synchronizer, null);
+ FilterProcessor filterProcessor = new(synchronizer, bitcoinStore);
+ HybridFeeProvider feeProvider = new(synchronizer, null);
// 4. Create key manager service.
var keyManager = KeyManager.CreateNew(out _, password);
@@ -85,6 +85,8 @@ namespace WalletWasabi.Tests.RegressionTests
nodes.Connect(); // Start connection service.
node.VersionHandshake(); // Start mempool service.
synchronizer.Start(requestInterval: TimeSpan.FromSeconds(3), 10000); // Start wasabi synchronizer service.
+ await filterProcessor.StartAsync(CancellationToken.None);
+ await feeProvider.StartAsync(CancellationToken.None);
// Wait until the filter our previous transaction is present.
var blockCount = await rpc.GetBlockCountAsync();
@@ -506,14 +508,10 @@ namespace WalletWasabi.Tests.RegressionTests
{
bitcoinStore.IndexStore.NewFilter -= Common.Wallet_NewFilterProcessed;
await walletManager.RemoveAndStopAllAsync(CancellationToken.None);
- // Dispose wasabi synchronizer service.
- if (synchronizer is { })
- {
await synchronizer.StopAsync();
- }
- // Dispose connection service.
+ await filterProcessor.StopAsync(CancellationToken.None);
+ await feeProvider.StopAsync(CancellationToken.None);
nodes?.Dispose();
- // Dispose mempool serving node.
node?.Disconnect();
}
}
@@ -536,8 +534,8 @@ namespace WalletWasabi.Tests.RegressionTests
// 3. Create wasabi synchronizer service.
HttpClientFactory httpClientFactory = new(torEndPoint: null, backendUriGetter: () => new Uri(RegTestFixture.BackendEndPoint));
WasabiSynchronizer synchronizer = new(bitcoinStore, httpClientFactory);
- using FilterProcessor filterProcessor = new(synchronizer, bitcoinStore);
- using HybridFeeProvider feeProvider = new(synchronizer, null);
+ FilterProcessor filterProcessor = new(synchronizer, bitcoinStore);
+ HybridFeeProvider feeProvider = new(synchronizer, null);
// 4. Create key manager service.
var keyManager = KeyManager.CreateNew(out _, password);
@@ -560,6 +558,8 @@ namespace WalletWasabi.Tests.RegressionTests
nodes.Connect(); // Start connection service.
node.VersionHandshake(); // Start mempool service.
synchronizer.Start(requestInterval: TimeSpan.FromSeconds(3), 10000); // Start wasabi synchronizer service.
+ await filterProcessor.StartAsync(CancellationToken.None);
+ await feeProvider.StartAsync(CancellationToken.None);
// Wait until the filter our previous transaction is present.
var blockCount = await rpc.GetBlockCountAsync();
@@ -680,14 +680,10 @@ namespace WalletWasabi.Tests.RegressionTests
{
bitcoinStore.IndexStore.NewFilter -= Common.Wallet_NewFilterProcessed;
await walletManager.RemoveAndStopAllAsync(CancellationToken.None);
- // Dispose wasabi synchronizer service.
- if (synchronizer is { })
- {
await synchronizer.StopAsync();
- }
- // Dispose connection service.
+ await filterProcessor.StopAsync(CancellationToken.None);
+ await feeProvider.StopAsync(CancellationToken.None);
nodes?.Dispose();
- // Dispose mempool serving node.
node?.Disconnect();
}
}
@@ -710,8 +706,8 @@ namespace WalletWasabi.Tests.RegressionTests
// 3. Create wasabi synchronizer service.
HttpClientFactory httpClientFactory = new(torEndPoint: null, backendUriGetter: () => new Uri(RegTestFixture.BackendEndPoint));
WasabiSynchronizer synchronizer = new(bitcoinStore, httpClientFactory);
- using FilterProcessor filterProcessor = new(synchronizer, bitcoinStore);
- using HybridFeeProvider feeProvider = new(synchronizer, null);
+ FilterProcessor filterProcessor = new(synchronizer, bitcoinStore);
+ HybridFeeProvider feeProvider = new(synchronizer, null);
// 4. Create key manager service.
var keyManager = KeyManager.CreateNew(out _, password);
@@ -736,6 +732,8 @@ namespace WalletWasabi.Tests.RegressionTests
nodes.Connect(); // Start connection service.
node.VersionHandshake(); // Start mempool service.
synchronizer.Start(requestInterval: TimeSpan.FromSeconds(3), 10000); // Start wasabi synchronizer service.
+ await filterProcessor.StartAsync(CancellationToken.None);
+ await feeProvider.StartAsync(CancellationToken.None);
// Wait until the filter our previous transaction is present.
var blockCount = await rpc.GetBlockCountAsync();
@@ -783,14 +781,10 @@ namespace WalletWasabi.Tests.RegressionTests
finally
{
await wallet.StopAsync(CancellationToken.None);
- // Dispose wasabi synchronizer service.
- if (synchronizer is { })
- {
await synchronizer.StopAsync();
- }
- // Dispose connection service.
+ await filterProcessor.StopAsync(CancellationToken.None);
+ await feeProvider.StopAsync(CancellationToken.None);
nodes?.Dispose();
- // Dispose mempool serving node.
node?.Disconnect();
}
}

MODIFY
WalletWasabi.Tests/RegressionTests/WalletTests.cs
WalletWasabi.Tests/RegressionTests/WalletTests.cs
@@ -55,10 +55,11 @@ namespace WalletWasabi.Tests.RegressionTests
HttpClientFactory httpClientFactory = new(torEndPoint: null, backendUriGetter: () => new Uri(RegTestFixture.BackendEndPoint));
WasabiSynchronizer synchronizer = new(bitcoinStore, httpClientFactory);
- using FilterProcessor filterProcessor = new(synchronizer, bitcoinStore);
+ FilterProcessor filterProcessor = new(synchronizer, bitcoinStore);
try
{
synchronizer.Start(requestInterval: TimeSpan.FromSeconds(1), 1000);
+ await filterProcessor.StartAsync(CancellationToken.None);
var blockCount = await rpc.GetBlockCountAsync() + 1; // Plus one because of the zeroth.
// Test initial synchronization.
@@ -111,11 +112,9 @@ namespace WalletWasabi.Tests.RegressionTests
}
}
finally
- {
- if (synchronizer is { })
{
await synchronizer.StopAsync();
- }
+ await filterProcessor.StopAsync(CancellationToken.None);
}
}
@@ -142,11 +141,12 @@ namespace WalletWasabi.Tests.RegressionTests
HttpClientFactory httpClientFactory = new(torEndPoint: null, backendUriGetter: () => new Uri(RegTestFixture.BackendEndPoint));
WasabiSynchronizer synchronizer = new(bitcoinStore, httpClientFactory);
- using FilterProcessor filterProcessor = new(synchronizer, bitcoinStore);
+ FilterProcessor filterProcessor = new(synchronizer, bitcoinStore);
try
{
synchronizer.Start(requestInterval: TimeSpan.FromSeconds(3), 1000);
+ await filterProcessor.StartAsync(CancellationToken.None);
var reorgAwaiter = new EventsAwaiter<FilterModel>(
h => bitcoinStore.IndexStore.Reorged += h,
@@ -224,11 +224,9 @@ namespace WalletWasabi.Tests.RegressionTests
await reorgAwaiter.WaitAsync(TimeSpan.FromSeconds(10));
}
finally
- {
- if (synchronizer is { })
{
await synchronizer.StopAsync();
- }
+ await filterProcessor.StopAsync(CancellationToken.None);
}
}
@@ -248,8 +246,8 @@ namespace WalletWasabi.Tests.RegressionTests
// 2. Create wasabi synchronizer service.
HttpClientFactory httpClientFactory = new(torEndPoint: null, backendUriGetter: () => new Uri(RegTestFixture.BackendEndPoint));
WasabiSynchronizer synchronizer = new(bitcoinStore, httpClientFactory);
- using FilterProcessor filterProcessor = new(synchronizer, bitcoinStore);
- using HybridFeeProvider feeProvider = new(synchronizer, null);
+ FilterProcessor filterProcessor = new(synchronizer, bitcoinStore);
+ HybridFeeProvider feeProvider = new(synchronizer, null);
// 3. Create key manager service.
var keyManager = KeyManager.CreateNew(out _, password);
@@ -275,6 +273,8 @@ namespace WalletWasabi.Tests.RegressionTests
nodes.Connect(); // Start connection service.
node.VersionHandshake(); // Start mempool service.
synchronizer.Start(requestInterval: TimeSpan.FromSeconds(3), 1000); // Start wasabi synchronizer service.
+ await filterProcessor.StartAsync(CancellationToken.None);
+ await feeProvider.StartAsync(CancellationToken.None);
// Wait until the filter our previous transaction is present.
var blockCount = await rpc.GetBlockCountAsync();
@@ -406,16 +406,10 @@ namespace WalletWasabi.Tests.RegressionTests
{
wallet.NewFilterProcessed -= Common.Wallet_NewFilterProcessed;
await wallet.StopAsync(CancellationToken.None);
- // Dispose wasabi synchronizer service.
- if (synchronizer is { })
- {
await synchronizer.StopAsync();
- }
-
- // Dispose connection service.
+ await filterProcessor.StopAsync(CancellationToken.None);
+ await feeProvider.StopAsync(CancellationToken.None);
nodes?.Dispose();
-
- // Dispose mempool serving node.
node?.Disconnect();
}
}

MODIFY
WalletWasabi/Blockchain/Analysis/FeesEstimation/HybridFeeProvider.cs
WalletWasabi/Blockchain/Analysis/FeesEstimation/HybridFeeProvider.cs
+using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
using WalletWasabi.Backend.Models.Responses;
using WalletWasabi.BitcoinCore.Monitoring;
using WalletWasabi.Logging;
using WalletWasabi.Models;
+using WalletWasabi.Nito.AsyncEx;
using WalletWasabi.Services;
namespace WalletWasabi.Blockchain.Analysis.FeesEstimation
@@ -13,32 +17,46 @@ namespace WalletWasabi.Blockchain.Analysis.FeesEstimation
/// Manages multiple fee sources. Returns the best one.
/// Prefers local full node, as long as the fee is accurate.
/// </summary>
- public class HybridFeeProvider : IDisposable
+ public class HybridFeeProvider : IHostedService
{
- private volatile bool _disposedValue = false; // To detect redundant calls
-
public HybridFeeProvider(WasabiSynchronizer synchronizer, RpcFeeProvider? rpcFeeProvider)
{
Synchronizer = synchronizer;
RpcFeeProvider = rpcFeeProvider;
+ }
- Synchronizer.ResponseArrived += Synchronizer_ResponseArrived;
+ public event EventHandler<AllFeeEstimate>? AllFeeEstimateChanged;
+
+ public WasabiSynchronizer Synchronizer { get; }
+ public RpcFeeProvider? RpcFeeProvider { get; }
+ private object Lock { get; } = new object();
+ public AllFeeEstimate? AllFeeEstimate { get; private set; }
+ private AbandonedTasks ProcessingEvents { get; } = new();
+
+ public Task StartAsync(CancellationToken cancellationToken)
+ {
+ SetAllFeeEstimateIfLooksBetter(RpcFeeProvider?.LastAllFeeEstimate);
+ SetAllFeeEstimateIfLooksBetter(Synchronizer.LastResponse?.AllFeeEstimate);
+ Synchronizer.ResponseArrived += Synchronizer_ResponseArrived;
if (RpcFeeProvider is not null)
{
RpcFeeProvider.AllFeeEstimateArrived += OnAllFeeEstimateArrived;
}
- SetAllFeeEstimateIfLooksBetter(RpcFeeProvider?.LastAllFeeEstimate);
- SetAllFeeEstimateIfLooksBetter(Synchronizer.LastResponse?.AllFeeEstimate);
+ return Task.CompletedTask;
}
- public event EventHandler<AllFeeEstimate>? AllFeeEstimateChanged;
+ public async Task StopAsync(CancellationToken cancellationToken)
+ {
+ Synchronizer.ResponseArrived -= Synchronizer_ResponseArrived;
+ if (RpcFeeProvider is not null)
+ {
+ RpcFeeProvider.AllFeeEstimateArrived -= OnAllFeeEstimateArrived;
+ }
- public WasabiSynchronizer Synchronizer { get; }
- public RpcFeeProvider? RpcFeeProvider { get; }
- private object Lock { get; } = new object();
- public AllFeeEstimate? AllFeeEstimate { get; private set; }
+ await ProcessingEvents.WhenAllAsync().ConfigureAwait(false);
+ }
private void Synchronizer_ResponseArrived(object? sender, SynchronizeResponse response)
{
@@ -46,6 +64,8 @@ namespace WalletWasabi.Blockchain.Analysis.FeesEstimation
}
private void OnAllFeeEstimateArrived(object? sender, AllFeeEstimate? fees)
+ {
+ using (RunningTasks.RememberWith(ProcessingEvents))
{
// Only go further if we have estimations.
if (fees?.Estimations?.Any() is not true)
@@ -63,14 +83,15 @@ namespace WalletWasabi.Blockchain.Analysis.FeesEstimation
}
else if (sender is WasabiSynchronizer syncer)
{
- if (RpcFeeProvider is null)
+ var rpcProvider = RpcFeeProvider;
+ if (rpcProvider is null)
{
// If user doesn't use full node, then set it, this is the best we got.
notify = SetAllFeeEstimate(fees);
}
else
{
- if (RpcFeeProvider.LastAllFeeEstimate?.IsAccurate is true && !RpcFeeProvider.InError)
+ if (rpcProvider.LastAllFeeEstimate?.IsAccurate is true && !rpcProvider.InError)
{
// If user's full node is properly serving data, then we don't care about the backend.
return;
@@ -122,6 +143,7 @@ namespace WalletWasabi.Blockchain.Analysis.FeesEstimation
AllFeeEstimateChanged?.Invoke(this, fees);
}
}
+ }
/// <returns>True if changed.</returns>
private bool SetAllFeeEstimateIfLooksBetter(AllFeeEstimate? fees)
@@ -146,34 +168,5 @@ namespace WalletWasabi.Blockchain.Analysis.FeesEstimation
AllFeeEstimate = fees;
return true;
}
-
- #region IDisposable Support
-
- protected virtual void Dispose(bool disposing)
- {
- if (!_disposedValue)
- {
- if (disposing)
- {
- Synchronizer.ResponseArrived -= Synchronizer_ResponseArrived;
-
- if (RpcFeeProvider is not null)
- {
- RpcFeeProvider.AllFeeEstimateArrived -= OnAllFeeEstimateArrived;
- }
- }
-
- _disposedValue = true;
- }
- }
-
- // This code added to correctly implement the disposable pattern.
- public void Dispose()
- {
- // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
- Dispose(true);
- }
-
- #endregion IDisposable Support
}
}

MODIFY
WalletWasabi/Blockchain/BlockFilters/FilterProcessor.cs
WalletWasabi/Blockchain/BlockFilters/FilterProcessor.cs
+using Microsoft.Extensions.Hosting;
using Nito.AsyncEx;
using System;
using System.Collections.Generic;
@@ -8,28 +9,42 @@ using System.Threading.Tasks;
using WalletWasabi.Backend.Models;
using WalletWasabi.Backend.Models.Responses;
using WalletWasabi.Logging;
+using WalletWasabi.Nito.AsyncEx;
using WalletWasabi.Services;
using WalletWasabi.Stores;
namespace WalletWasabi.Blockchain.BlockFilters
{
- public class FilterProcessor : IDisposable
+ public class FilterProcessor : IHostedService
{
- private volatile bool _disposedValue = false; // To detect redundant calls
-
public FilterProcessor(WasabiSynchronizer synchronizer, BitcoinStore bitcoinStore)
{
Synchronizer = synchronizer;
BitcoinStore = bitcoinStore;
-
- Synchronizer.ResponseArrived += Synchronizer_ResponseArrivedAsync;
}
public WasabiSynchronizer Synchronizer { get; }
public BitcoinStore BitcoinStore { get; }
- public AsyncLock AsyncLock { get; } = new AsyncLock();
+ public AsyncLock AsyncLock { get; } = new();
+ private AbandonedTasks ProcessingEvents { get; } = new();
+
+ public Task StartAsync(CancellationToken cancellationToken)
+ {
+ Synchronizer.ResponseArrived += Synchronizer_ResponseArrivedAsync;
+
+ return Task.CompletedTask;
+ }
+
+ public async Task StopAsync(CancellationToken cancellationToken)
+ {
+ Synchronizer.ResponseArrived -= Synchronizer_ResponseArrivedAsync;
+
+ await ProcessingEvents.WhenAllAsync().ConfigureAwait(false);
+ }
private async void Synchronizer_ResponseArrivedAsync(object? sender, SynchronizeResponse response)
+ {
+ using (RunningTasks.RememberWith(ProcessingEvents))
{
uint serverBestHeight = (uint)response.BestHeight;
var filters = response.Filters;
@@ -37,6 +52,7 @@ namespace WalletWasabi.Blockchain.BlockFilters
await ProcessAsync(serverBestHeight, filtersResponseState, filters).ConfigureAwait(false);
}
+ }
private async Task ProcessAsync(uint serverBestHeight, FiltersResponseState filtersResponseState, IEnumerable<FilterModel> filters)
{
@@ -103,29 +119,5 @@ namespace WalletWasabi.Blockchain.BlockFilters
Logger.LogError(ex);
}
}
-
- #region IDisposable Support
-
- protected virtual void Dispose(bool disposing)
- {
- if (!_disposedValue)
- {
- if (disposing)
- {
- Synchronizer.ResponseArrived -= Synchronizer_ResponseArrivedAsync;
- }
-
- _disposedValue = true;
- }
- }
-
- // This code added to correctly implement the disposable pattern.
- public void Dispose()
- {
- // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
- Dispose(true);
- }
-
- #endregion IDisposable Support
}
}

Make new classes hosted services