Delete old files on the client side, ensure no empty filter created.
MODIFY
WalletWasabi.Tests/IntegrationTests/ExtPubKeyExplorerTests.cs
WalletWasabi.Tests/IntegrationTests/ExtPubKeyExplorerTests.cs
@@ -30,7 +30,7 @@ namespace WalletWasabi.Tests.IntegrationTests
{
var filters = new[]
{
- FilterModel.FromHeightlessLine("000000000000de90e633e1b1330859842795d39018d033044e8b003e8cbf58e4:050a2f58828c9820642769ae320a40", 0)
+ FilterModel.FromFullLine("0:000000000000de90e633e1b1330859842795d39018d033044e8b003e8cbf58e4:050a2f58828c9820642769ae320a40")
};
var unusedKeyIndex = ExtPubKeyExplorer.GetUnusedBech32Keys(1, true, ExtPubKey.GetWif(Network.Main), filters).First().ScriptPubKey.ToCompressedBytes();
@@ -118,12 +118,7 @@ namespace WalletWasabi.Tests.IntegrationTests
.SetP(20);
builder.AddEntries(scripts);
- return new FilterModel
- {
- BlockHeight = 0,
- BlockHash = new uint256(blockHash),
- Filter = builder.Build()
- };
+ return new FilterModel(0, new uint256(blockHash), builder.Build());
}
private byte[][] GetScripts(bool isInternal, int offset, int count)

MODIFY
WalletWasabi/Backend/Models/FilterModel.cs
WalletWasabi/Backend/Models/FilterModel.cs
@@ -15,32 +15,26 @@ namespace WalletWasabi.Backend.Models
public class FilterModel
{
[JsonConverter(typeof(HeightJsonConverter))]
- public Height BlockHeight { get; set; }
+ public Height BlockHeight { get; }
[JsonConverter(typeof(Uint256JsonConverter))]
- public uint256 BlockHash { get; set; }
+ public uint256 BlockHash { get; }
[JsonConverter(typeof(GolombRiceFilterJsonConverter))]
- public GolombRiceFilter Filter { get; set; }
+ public GolombRiceFilter Filter { get; }
+
+ public FilterModel(Height blockHeight, uint256 blockHash, GolombRiceFilter filter)
+ {
+ BlockHeight = blockHeight;
+ BlockHash = Guard.NotNull(nameof(blockHash), blockHash);
+ Filter = Guard.NotNull(nameof(filter), filter);
+ }
// https://github.com/bitcoin/bips/blob/master/bip-0158.mediawiki
// The parameter k MUST be set to the first 16 bytes of the hash of the block for which the filter
// is constructed.This ensures the key is deterministic while still varying from block to block.
public byte[] FilterKey => BlockHash.ToBytes().Take(16).ToArray();
- public string ToHeightlessLine()
- {
- var builder = new StringBuilder();
- builder.Append(BlockHash);
- if (Filter != null) // bech found here
- {
- builder.Append(":");
- builder.Append(Filter);
- }
-
- return builder.ToString();
- }
-
public string ToFullLine()
{
var builder = new StringBuilder();
@@ -68,7 +62,7 @@ namespace WalletWasabi.Backend.Models
}
else if (parts.Length == 2) // no bech here
{
- filter = null;
+ filter = new GolombRiceFilter(new byte[] { 0 });
}
else
{
@@ -78,70 +72,13 @@ namespace WalletWasabi.Backend.Models
if (Height.TryParse(parts[0], out Height blockHeight))
{
- return new FilterModel
- {
- BlockHeight = blockHeight,
- BlockHash = new uint256(parts[1]),
- Filter = filter
- };
+ uint256 blockHash = new uint256(parts[1]);
+ return new FilterModel(blockHeight, blockHash, filter);
}
else
{
throw new FormatException($"Could not parse {nameof(Height)}.");
}
}
-
- public static FilterModel FromHeightlessLine(string line, Height height)
- {
- Guard.NotNullOrEmptyOrWhitespace(nameof(line), line);
- var parts = line.Split(':');
-
- if (parts.Length == 1) // no bech here
- {
- return new FilterModel
- {
- BlockHeight = Guard.NotNull(nameof(height), height),
- BlockHash = new uint256(parts[0]),
- Filter = null
- };
- }
-
- var data = Encoders.Hex.DecodeData(parts[1]);
- var filter = new GolombRiceFilter(data, 20, 1 << 20);
-
- return new FilterModel
- {
- BlockHeight = Guard.NotNull(nameof(height), height),
- BlockHash = new uint256(parts[0]),
- Filter = filter
- };
- }
-
- public byte[] ToBytes()
- {
- byte[] blockHashBytes = BlockHash.ToBytes();
- byte[] filterBytes = Filter is null ? Array.Empty<byte>() : Filter.ToBytes();
- byte[] filterLengthBytes = BitConverter.GetBytes(filterBytes.Length);
- byte[] buffer = new byte[blockHashBytes.Length + filterLengthBytes.Length + filterBytes.Length];
- Buffer.BlockCopy(blockHashBytes, 0, buffer, 0, blockHashBytes.Length);
- Buffer.BlockCopy(filterLengthBytes, 0, buffer, blockHashBytes.Length, filterLengthBytes.Length);
- Buffer.BlockCopy(filterBytes, 0, buffer, blockHashBytes.Length + filterLengthBytes.Length, filterBytes.Length);
- return buffer;
- }
-
- public static FilterModel FromStream(Stream stream, Height height)
- {
- uint256 blockHash = new uint256(stream.ReadBytes(32));
- int filterSize = BitConverter.ToInt32(stream.ReadBytes(4));
- byte[] data = stream.ReadBytes(filterSize);
- GolombRiceFilter filter = filterSize > 0 ? new GolombRiceFilter(data, 20, 1 << 20) : null;
-
- return new FilterModel
- {
- BlockHeight = Guard.NotNull(nameof(height), height),
- BlockHash = blockHash,
- Filter = filter
- };
- }
}
}

MODIFY
WalletWasabi/Blockchain/BlockFilters/IndexBuilderService.cs
WalletWasabi/Blockchain/BlockFilters/IndexBuilderService.cs
@@ -65,11 +65,9 @@ namespace WalletWasabi.Blockchain.BlockFilters
}
else
{
- var height = StartingHeight;
foreach (var line in File.ReadAllLines(IndexFilePath))
{
- var filter = FilterModel.FromHeightlessLine(line, height);
- height++;
+ var filter = FilterModel.FromFullLine(line);
Index.Add(filter);
}
}
@@ -250,14 +248,9 @@ namespace WalletWasabi.Blockchain.BlockFilters
.AddEntries(scripts.Select(x => x.ToCompressedBytes()))
.Build();
- var filterModel = new FilterModel
- {
- BlockHash = block.GetHash(),
- BlockHeight = heightToRequest,
- Filter = filter
- };
+ var filterModel = new FilterModel(heightToRequest, block.GetHash(), filter);
- await File.AppendAllLinesAsync(IndexFilePath, new[] { filterModel.ToHeightlessLine() });
+ await File.AppendAllLinesAsync(IndexFilePath, new[] { filterModel.ToFullLine() });
using (await IndexLock.LockAsync())
{
Index.Add(filterModel);

MODIFY
WalletWasabi/Blockchain/BlockFilters/StartingFilters.cs
WalletWasabi/Blockchain/BlockFilters/StartingFilters.cs
@@ -15,15 +15,15 @@ namespace WalletWasabi.Blockchain.BlockFilters
{
if (network == Network.Main)
{
- return FilterModel.FromHeightlessLine("0000000000000000001c8018d9cb3b742ef25114f27563e3fc4a1902167f9893:02832810ec08a0", GetStartingHeight(network));
+ return FilterModel.FromFullLine($"{GetStartingHeight(network)}:0000000000000000001c8018d9cb3b742ef25114f27563e3fc4a1902167f9893:02832810ec08a0");
}
else if (network == Network.TestNet)
{
- return FilterModel.FromHeightlessLine("00000000000f0d5edcaeba823db17f366be49a80d91d15b77747c2e017b8c20a:017821b8", GetStartingHeight(network));
+ return FilterModel.FromFullLine($"{GetStartingHeight(network)}:00000000000f0d5edcaeba823db17f366be49a80d91d15b77747c2e017b8c20a:017821b8");
}
else if (network == Network.RegTest)
{
- return FilterModel.FromHeightlessLine("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206", GetStartingHeight(network));
+ return FilterModel.FromFullLine($"{GetStartingHeight(network)}:0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206");
}
else
{

MODIFY
WalletWasabi/Stores/IndexStore.cs
WalletWasabi/Stores/IndexStore.cs
@@ -72,7 +72,7 @@ namespace WalletWasabi.Stores
if (!MatureIndexFileManager.Exists())
{
- await MatureIndexFileManager.WriteAllLinesAsync(new[] { StartingFilter.ToHeightlessLine() }).ConfigureAwait(false);
+ await MatureIndexFileManager.WriteAllLinesAsync(new[] { StartingFilter.ToFullLine() }).ConfigureAwait(false);
}
await InitializeFiltersAsync().ConfigureAwait(false);
@@ -80,12 +80,44 @@ namespace WalletWasabi.Stores
}
}
- private async Task InitializeFiltersAsync()
+ private async Task DeleteIfDeprecatedAsync()
+ {
+ if (MatureIndexFileManager.Exists())
+ {
+ await DeleteIfDeprecatedAsync(MatureIndexFileManager).ConfigureAwait(false);
+ }
+
+ if (ImmatureIndexFileManager.Exists())
{
+ await DeleteIfDeprecatedAsync(ImmatureIndexFileManager).ConfigureAwait(false);
+ }
+ }
+
+ private async Task DeleteIfDeprecatedAsync(DigestableSafeMutexIoManager ioManager)
+ {
+ string firstLine;
+ using (var content = ioManager.OpenText())
+ {
+ firstLine = await content.ReadLineAsync().ConfigureAwait(false);
+ }
+
try
{
- Height height = StartingHeight;
+ FilterModel.FromFullLine(firstLine);
+ }
+ catch
+ {
+ Logger.LogWarning("Old Index file detected. Deleting it.");
+ MatureIndexFileManager.DeleteMe();
+ ImmatureIndexFileManager.DeleteMe();
+ Logger.LogWarning("Successfully deleted old Index file.");
+ }
+ }
+ private async Task InitializeFiltersAsync()
+ {
+ try
+ {
if (MatureIndexFileManager.Exists())
{
using var sr = MatureIndexFileManager.OpenText();
@@ -102,8 +134,7 @@ namespace WalletWasabi.Stores
lineTask = sr.EndOfStream ? null : sr.ReadLineAsync();
- ProcessLine(height, line, enqueue: false);
- height++;
+ ProcessLine(line, enqueue: false);
line = null;
}
@@ -114,8 +145,7 @@ namespace WalletWasabi.Stores
{
foreach (var line in await ImmatureIndexFileManager.ReadAllLinesAsync().ConfigureAwait(false)) // We can load ImmatureIndexFileManager to the memory, no problem.
{
- ProcessLine(height, line, enqueue: true);
- height++;
+ ProcessLine(line, enqueue: true);
}
}
}
@@ -131,9 +161,9 @@ namespace WalletWasabi.Stores
}
}
- private void ProcessLine(Height height, string line, bool enqueue)
+ private void ProcessLine(string line, bool enqueue)
{
- var filter = FilterModel.FromHeightlessLine(line, height);
+ var filter = FilterModel.FromFullLine(line);
ProcessFilter(filter, enqueue);
}
@@ -190,6 +220,8 @@ namespace WalletWasabi.Stores
File.Delete(oldIndexFilePath);
}
+
+ await DeleteIfDeprecatedAsync().ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -310,7 +342,7 @@ namespace WalletWasabi.Stores
using (await IndexLock.LockAsync(cancel).ConfigureAwait(false))
{
// Do not feed the cancellationToken here I always want this to finish running for safety.
- var currentImmatureLines = ImmatureFilters.Select(x => x.ToHeightlessLine()).ToArray(); // So we do not read on ImmatureFilters while removing them.
+ var currentImmatureLines = ImmatureFilters.Select(x => x.ToFullLine()).ToArray(); // So we do not read on ImmatureFilters while removing them.
var matureLinesToAppend = currentImmatureLines.SkipLast(100);
var immatureLines = currentImmatureLines.TakeLast(100);
var tasks = new Task[] { MatureIndexFileManager.AppendAllLinesAsync(matureLinesToAppend), ImmatureIndexFileManager.WriteAllLinesAsync(immatureLines) };
@@ -369,7 +401,7 @@ namespace WalletWasabi.Stores
continue;
}
- var filter = FilterModel.FromHeightlessLine(line, height);
+ var filter = FilterModel.FromFullLine(line);
await tTask.ConfigureAwait(false);
tTask = todo(filter);
@@ -396,7 +428,7 @@ namespace WalletWasabi.Stores
continue;
}
- var filter = FilterModel.FromHeightlessLine(line, height);
+ var filter = FilterModel.FromFullLine(line);
await todo(filter).ConfigureAwait(false);
height++;

Delete old files on the client side, ensure no empty filter created.