Skip to content

Commit

Permalink
Merge pull request WalletWasabi#12939 from turbolay/reorgBlockFilterI…
Browse files Browse the repository at this point in the history
…terator

Remove filters from BlockFilterIterator after a reorg
  • Loading branch information
Turbolay authored Apr 28, 2024
2 parents 9396468 + 418f810 commit 06ede9d
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 6 deletions.
37 changes: 34 additions & 3 deletions WalletWasabi/Wallets/FilterProcessor/BlockFilterIterator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Nito.AsyncEx;
using WalletWasabi.Backend.Models;
using WalletWasabi.Stores;

Expand All @@ -20,7 +21,10 @@ public BlockFilterIterator(IIndexStore indexStore, int maxNumberFiltersInMemory
}

/// <remarks>Internal only to allow modifications in tests.</remarks>
internal Dictionary<uint, FilterModel> Cache { get; } = new();
internal Dictionary<uint, FilterModel> Cache { get; } = [];

/// <remarks>Lock object to guard <see cref="Cache"/>.</remarks>
private AsyncLock Lock { get; } = new();
private IIndexStore IndexStore { get; }
public int MaxNumberFiltersInMemory { get; }

Expand All @@ -30,14 +34,16 @@ public BlockFilterIterator(IIndexStore indexStore, int maxNumberFiltersInMemory
/// <remarks>Filter is immediately removed from the cache once the method returns. Repeated calls for single height are thus expensive.</remarks>
public async Task<FilterModel> GetAndRemoveAsync(uint height, CancellationToken cancellationToken)
{
using IDisposable _ = await Lock.LockAsync(cancellationToken).ConfigureAwait(false);

// Each block filter is to needed just once, so we can remove the block right now and free the memory sooner.
if (Cache.Remove(height, out FilterModel? result))
{
return result;
}

// We don't have the next filter to process, so fetch another batch of filters from the database.
Clear();
ClearNoLock();

FilterModel[] filtersBatch = await IndexStore.FetchBatchAsync(height, MaxNumberFiltersInMemory, cancellationToken).ConfigureAwait(false);

Expand Down Expand Up @@ -73,7 +79,32 @@ public async Task<FilterModel> GetAndRemoveAsync(uint height, CancellationToken
return result;
}

public void Clear()
public async Task RemoveNewerThanAsync(uint height, CancellationToken cancellationToken)
{
using IDisposable _ = await Lock.LockAsync(cancellationToken).ConfigureAwait(false);

List<uint> keysToRemove = Cache.Keys
.Where(key => key > height)
.ToList();

foreach (uint heightToRemove in keysToRemove)
{
if (!Cache.Remove(heightToRemove))
{
throw new UnreachableException($"Filter {heightToRemove} was already removed from the Cache.");
}
}
}

public async Task ClearAsync(CancellationToken cancellationToken)
{
using IDisposable _ = await Lock.LockAsync(cancellationToken).ConfigureAwait(false);

ClearNoLock();
}

/// <remarks>Needs to be guarded by <see cref="Lock"/></remarks>
private void ClearNoLock()
{
Cache.Clear();
}
Expand Down
10 changes: 7 additions & 3 deletions WalletWasabi/Wallets/WalletFilterProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
foreach (SyncType syncType in Enum.GetValues<SyncType>())
{
FilterIteratorsBySyncType[syncType].Clear();
await FilterIteratorsBySyncType[syncType].ClearAsync(cancellationToken).ConfigureAwait(false);
}
}
}
Expand Down Expand Up @@ -265,13 +265,17 @@ private async void ReorgedAsync(object? sender, FilterModel invalidFilter)
try
{
uint256 invalidBlockHash = invalidFilter.Header.BlockHash;
uint newBestHeight = invalidFilter.Header.Height - 1;

using (await ReorgLock.LockAsync(CancellationToken.None).ConfigureAwait(false))
{
KeyManager.SetMaxBestHeight(new Height(invalidFilter.Header.Height - 1));
KeyManager.SetMaxBestHeight(new Height(newBestHeight));
TransactionProcessor.UndoBlock((int)invalidFilter.Header.Height);
BitcoinStore.TransactionStore.ReleaseToMempoolFromBlock(invalidBlockHash);

foreach (SyncType syncType in Enum.GetValues<SyncType>())
{
await FilterIteratorsBySyncType[syncType].RemoveNewerThanAsync(newBestHeight, CancellationToken.None).ConfigureAwait(false);
}
await BlockDownloadService.RemoveBlocksAsync(invalidFilter.Header.Height).ConfigureAwait(false);
}
}
Expand Down

0 comments on commit 06ede9d

Please sign in to comment.