return best height
MODIFY
WalletWasabi.Backend/Controllers/BlockchainController.cs
WalletWasabi.Backend/Controllers/BlockchainController.cs
@@ -7,7 +7,9 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using WalletWasabi.Backend.Models;
+using WalletWasabi.Backend.Models.Responses;
using WalletWasabi.Logging;
+using WalletWasabi.Models;
namespace WalletWasabi.Backend.Controllers
{
@@ -158,8 +160,8 @@ namespace WalletWasabi.Backend.Controllers
/// </remarks>
/// <param name="bestKnownBlockHash">The best block hash the client knows its filter.</param>
/// <param name="count">The number of filters to return.</param>
- /// <returns>An array of block hash : element count : filter pairs.</returns>
- /// <response code="200">An array of block hash : element count : filter pairs.</response>
+ /// <returns>The best height and an array of block hash : element count : filter pairs.</returns>
+ /// <response code="200">The best height and an array of block hash : element count : filter pairs.</response>
/// <response code="204">When the provided hash is the tip.</response>
/// <response code="400">The provided hash was malformed or the count value is out of range</response>
/// <response code="404">If the hash is not found. This happens at blockhain reorg.</response>
@@ -177,7 +179,7 @@ namespace WalletWasabi.Backend.Controllers
var knownHash = new uint256(bestKnownBlockHash);
- IEnumerable<string> filters = Global.IndexBuilderService.GetFilterLinesExcluding(knownHash, count, out bool found);
+ (Height bestHeight, IEnumerable<string> filters) = Global.IndexBuilderService.GetFilterLinesExcluding(knownHash, count, out bool found);
if (!found)
{
@@ -189,7 +191,13 @@ namespace WalletWasabi.Backend.Controllers
return NoContent();
}
- return Ok(filters);
+ var response = new FiltersResponse
+ {
+ BestHeight = bestHeight,
+ Filters = filters
+ };
+
+ return Ok(response);
}
private async Task<EstimateSmartFeeResponse> GetEstimateSmartFeeAsync(int target, EstimateSmartFeeMode mode)

MODIFY
WalletWasabi.Gui/Controls/StatusBar.xaml
WalletWasabi.Gui/Controls/StatusBar.xaml
<StackPanel Orientation="Horizontal" Gap="4" DockPanel.Dock="Right">
<TextBlock>Connections:</TextBlock>
<TextBlock Text="{Binding Connections}" />
- <TextBlock>Filters:</TextBlock>
- <TextBlock Text="{Binding Filters}" />
+ <TextBlock>Filters Left:</TextBlock>
+ <TextBlock Text="{Binding FiltersLeft}" />
<TextBlock>Blocks Left:</TextBlock>
<TextBlock Text="{Binding BlocksLeft}" />
<TextBlock>Mempool:</TextBlock>

MODIFY
WalletWasabi.Gui/ViewModels/StatusBarViewModel.cs
WalletWasabi.Gui/ViewModels/StatusBarViewModel.cs
@@ -24,14 +24,14 @@ namespace WalletWasabi.Gui.ViewModels
}
}
- private int _filters;
+ private int _filtersLeft;
- public int Filters
+ public int FiltersLeft
{
- get { return _filters; }
+ get { return _filtersLeft; }
set
{
- this.RaiseAndSetIfChanged(ref _filters, value);
+ this.RaiseAndSetIfChanged(ref _filtersLeft, value);
}
}
@@ -78,7 +78,7 @@ namespace WalletWasabi.Gui.ViewModels
IndexDownloader = indexDownloader;
IndexDownloader.NewFilter += IndexDownloader_NewFilter;
- Filters = IndexDownloader.BestFilter.BlockHeight.Value;
+ FiltersLeft = IndexDownloader.GetFiltersLeft();
this.WhenAnyValue(x => x.BlocksLeft).Subscribe(blocks =>
{
@@ -95,7 +95,7 @@ namespace WalletWasabi.Gui.ViewModels
private void IndexDownloader_NewFilter(object sender, Backend.Models.FilterModel e)
{
- Filters = IndexDownloader.BestFilter.BlockHeight.Value;
+ FiltersLeft = IndexDownloader.GetFiltersLeft();
}
private void MemPoolService_TransactionReceived(object sender, Models.SmartTransaction e)

MODIFY
WalletWasabi.Tests/LiveServerTests.cs
WalletWasabi.Tests/LiveServerTests.cs
@@ -4,6 +4,7 @@ using System.Linq;
using System.Threading.Tasks;
using NBitcoin;
using WalletWasabi.Backend.Models;
+using WalletWasabi.Backend.Models.Responses;
using WalletWasabi.Crypto;
using WalletWasabi.Models;
using WalletWasabi.Services;
@@ -50,10 +51,10 @@ namespace WalletWasabi.Tests
{
var filterModel = IndexDownloader.GetStartingFilter(Network.GetNetwork(networkType.ToString()));
- var filters = await client.GetFiltersAsync(filterModel.BlockHash, 2);
+ FiltersResponse filtersResponse = await client.GetFiltersAsync(filterModel.BlockHash, 2);
- Assert.True(filters.NotNullAndNotEmpty());
- Assert.True(filters.Count() == 2);
+ Assert.NotNull(filtersResponse);
+ Assert.True(filtersResponse.Filters.Count() == 2);
}
}

MODIFY
WalletWasabi.Tests/P2pTests.cs
WalletWasabi.Tests/P2pTests.cs
@@ -217,7 +217,7 @@ namespace WalletWasabi.Tests
// Test initial synchronization.
var times = 0;
uint256 firstHash = await rpc.GetBlockHashAsync(0);
- while (indexBuilderService.GetFilterLinesExcluding(firstHash, 102, out _).Count() != 101)
+ while (indexBuilderService.GetFilterLinesExcluding(firstHash, 102, out _).filters.Count() != 101)
{
if (times > 500) // 30 sec
{
@@ -230,7 +230,7 @@ namespace WalletWasabi.Tests
// Test later synchronization.
regtestNode.Generate(10);
times = 0;
- while (indexBuilderService.GetFilterLinesExcluding(firstHash, 112, out bool found5).Count() != 111)
+ while (indexBuilderService.GetFilterLinesExcluding(firstHash, 112, out bool found5).filters.Count() != 111)
{
Assert.True(found5);
if (times > 500) // 30 sec
@@ -243,16 +243,16 @@ namespace WalletWasabi.Tests
// Test correct number of filters is received.
var hundredthHash = await rpc.GetBlockHashAsync(100);
- Assert.Equal(11, indexBuilderService.GetFilterLinesExcluding(hundredthHash, 12, out bool found).Count());
+ Assert.Equal(11, indexBuilderService.GetFilterLinesExcluding(hundredthHash, 12, out bool found).filters.Count());
Assert.True(found);
var bestHash = await rpc.GetBestBlockHashAsync();
- Assert.Empty(indexBuilderService.GetFilterLinesExcluding(bestHash, 1, out bool found2));
+ Assert.Empty(indexBuilderService.GetFilterLinesExcluding(bestHash, 1, out bool found2).filters);
Assert.True(found2);
- Assert.Empty(indexBuilderService.GetFilterLinesExcluding(uint256.Zero, 1, out bool found3));
+ Assert.Empty(indexBuilderService.GetFilterLinesExcluding(uint256.Zero, 1, out bool found3).filters);
Assert.False(found3);
// Test filter block hashes are correct.
- var filters = indexBuilderService.GetFilterLinesExcluding(firstHash, 112, out bool found4).ToArray();
+ var filters = indexBuilderService.GetFilterLinesExcluding(firstHash, 112, out bool found4).filters.ToArray();
Assert.True(found4);
for (int i = 0; i < 111; i++)
{

MODIFY
WalletWasabi.Tests/RegTests.cs
WalletWasabi.Tests/RegTests.cs
@@ -51,10 +51,10 @@ namespace WalletWasabi.Tests
{
using (var client = new WasabiClient(new Uri(RegTestFixture.BackendEndPoint)))
{
- var filters = await client.GetFiltersAsync(firstHash, 1000);
- Assert.True(filters.NotNullAndNotEmpty());
+ FiltersResponse filtersResponse = await client.GetFiltersAsync(firstHash, 1000);
+ Assert.NotNull(filtersResponse);
- var filterCount = filters.Count();
+ var filterCount = filtersResponse.Filters.Count();
if (filterCount >= 101)
{
break;
@@ -462,7 +462,7 @@ namespace WalletWasabi.Tests
Assert.Single(wallet.Coins);
var firstCoin = wallet.Coins.Single();
Assert.Equal(Money.Coins(0.1m), firstCoin.Amount);
- Assert.Equal(indexDownloader.BestFilter.BlockHeight, firstCoin.Height);
+ Assert.Equal(indexDownloader.BestKnownFilter.BlockHeight, firstCoin.Height);
Assert.InRange(firstCoin.Index, 0U, 1U);
Assert.False(firstCoin.SpentOrCoinJoinInProcess);
Assert.Equal("foo label", firstCoin.Label);
@@ -491,9 +491,9 @@ namespace WalletWasabi.Tests
var thirdCoin = wallet.Coins.OrderBy(x => x.Height).Last();
Assert.Equal(Money.Coins(0.01m), secondCoin.Amount);
Assert.Equal(Money.Coins(0.02m), thirdCoin.Amount);
- Assert.Equal(indexDownloader.BestFilter.BlockHeight.Value - 2, firstCoin.Height.Value);
- Assert.Equal(indexDownloader.BestFilter.BlockHeight.Value - 1, secondCoin.Height.Value);
- Assert.Equal(indexDownloader.BestFilter.BlockHeight, thirdCoin.Height);
+ Assert.Equal(indexDownloader.BestKnownFilter.BlockHeight.Value - 2, firstCoin.Height.Value);
+ Assert.Equal(indexDownloader.BestKnownFilter.BlockHeight.Value - 1, secondCoin.Height.Value);
+ Assert.Equal(indexDownloader.BestKnownFilter.BlockHeight, thirdCoin.Height);
Assert.False(thirdCoin.SpentOrCoinJoinInProcess);
Assert.Equal("foo label", firstCoin.Label);
Assert.Equal("bar label", secondCoin.Label);
@@ -549,7 +549,7 @@ namespace WalletWasabi.Tests
var rbfCoin = wallet.Coins.Where(x => x.TransactionId == tx4bumpRes.TransactionId).Single();
Assert.Equal(Money.Coins(0.03m), rbfCoin.Amount);
- Assert.Equal(indexDownloader.BestFilter.BlockHeight.Value - 2, rbfCoin.Height.Value);
+ Assert.Equal(indexDownloader.BestKnownFilter.BlockHeight.Value - 2, rbfCoin.Height.Value);
Assert.False(rbfCoin.SpentOrCoinJoinInProcess);
Assert.Equal("bar label", rbfCoin.Label);
Assert.Equal(key2.GetP2wpkhScript(), rbfCoin.ScriptPubKey);
@@ -583,7 +583,7 @@ namespace WalletWasabi.Tests
await rpc.GenerateAsync(1);
await WaitForFiltersToBeProcessedAsync(TimeSpan.FromSeconds(120), 1);
var res = await rpc.GetTxOutAsync(mempoolCoin.TransactionId, (int)mempoolCoin.Index, true);
- Assert.Equal(indexDownloader.BestFilter.BlockHeight, mempoolCoin.Height);
+ Assert.Equal(indexDownloader.BestKnownFilter.BlockHeight, mempoolCoin.Height);
}
finally
{
@@ -994,7 +994,7 @@ namespace WalletWasabi.Tests
await rpc.GenerateAsync(1);
await WaitForFiltersToBeProcessedAsync(TimeSpan.FromSeconds(120), 1);
- var bestHeight = wallet.IndexDownloader.BestFilter.BlockHeight;
+ var bestHeight = wallet.IndexDownloader.BestKnownFilter.BlockHeight;
Assert.Contains("change of (outgoing, outgoing2)", wallet.Coins.Where(x => x.Height == bestHeight).Select(x => x.Label));
Assert.Contains("change of (outgoing, outgoing2)", keyManager.GetKeys().Select(x => x.Label));

MODIFY
WalletWasabi/Services/IndexBuilderService.cs
WalletWasabi/Services/IndexBuilderService.cs
@@ -368,7 +368,7 @@ namespace WalletWasabi.Services
});
}
- public IEnumerable<string> GetFilterLinesExcluding(uint256 bestKnownBlockHash, int count, out bool found)
+ public (Height bestHeight, IEnumerable<string> filters) GetFilterLinesExcluding(uint256 bestKnownBlockHash, int count, out bool found)
{
using (IndexLock.Lock())
{
@@ -393,7 +393,14 @@ namespace WalletWasabi.Services
}
}
- return filters;
+ if (Index.Count == 0)
+ {
+ return (Height.Unknown, Enumerable.Empty<string>());
+ }
+ else
+ {
+ return (Index.Last().BlockHeight, filters);
+ }
}
}

MODIFY
WalletWasabi/Services/IndexDownloader.cs
WalletWasabi/Services/IndexDownloader.cs
@@ -14,6 +14,7 @@ using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.WebClients.ChaumianCoinJoin;
+using WalletWasabi.Backend.Models.Responses;
namespace WalletWasabi.Services
{
@@ -21,7 +22,10 @@ namespace WalletWasabi.Services
{
public Network Network { get; }
- public FilterModel BestFilter { get; private set; }
+ public FilterModel BestKnownFilter { get; private set; }
+ public int BestHeight { get; private set; }
+
+ public int GetFiltersLeft() => BestHeight - BestKnownFilter.BlockHeight;
public WasabiClient WasabiClient { get; }
@@ -108,7 +112,8 @@ namespace WalletWasabi.Services
File.WriteAllLines(IndexFilePath, Index.Select(x => x.ToLine()));
}
- BestFilter = Index.Last();
+ BestKnownFilter = Index.Last();
+ BestHeight = 0; // At this point we don't know it.
}
public void Synchronize(TimeSpan requestInterval)
@@ -118,7 +123,7 @@ namespace WalletWasabi.Services
Task.Run(async () =>
{
- FilterModel bestKnownFilter = null;
+ FilterModel bestKnownStartingFilter = null;
try
{
@@ -129,36 +134,38 @@ namespace WalletWasabi.Services
// If stop was requested return.
if (!IsRunning) return;
- bestKnownFilter = BestFilter;
+ bestKnownStartingFilter = BestKnownFilter;
- var filters = await WasabiClient.GetFiltersAsync(bestKnownFilter.BlockHash, 1000, Cancel.Token);
+ FiltersResponse filtersResponse = await WasabiClient.GetFiltersAsync(bestKnownStartingFilter.BlockHash, 1000, Cancel.Token);
- if (!filters.Any())
+ if (filtersResponse == null) // no-content, we are synced
{
continue;
}
+ BestHeight = filtersResponse.BestHeight;
+
using (await IndexLock.LockAsync(Cancel.Token))
{
- var filtersList = filters.ToList(); // performance
+ var filtersList = filtersResponse.Filters.ToList(); // performance
for (int i = 0; i < filtersList.Count; i++)
{
- var filterModel = FilterModel.FromLine(filtersList[i], bestKnownFilter.BlockHeight + i + 1);
+ var filterModel = FilterModel.FromLine(filtersList[i], bestKnownStartingFilter.BlockHeight + i + 1);
Index.Add(filterModel);
- BestFilter = filterModel;
+ BestKnownFilter = filterModel;
NewFilter?.Invoke(this, filterModel);
}
if (filtersList.Count == 1) // minor optimization
{
- await File.AppendAllLinesAsync(IndexFilePath, new[] { BestFilter.ToLine() });
+ await File.AppendAllLinesAsync(IndexFilePath, new[] { BestKnownFilter.ToLine() });
}
else
{
await File.WriteAllLinesAsync(IndexFilePath, Index.Select(x => x.ToLine()));
}
- Logger.LogInfo<IndexDownloader>($"Downloaded filters for blocks from {bestKnownFilter.BlockHeight.Value + 1} to {BestFilter.BlockHeight}.");
+ Logger.LogInfo<IndexDownloader>($"Downloaded filters for blocks from {bestKnownStartingFilter.BlockHeight.Value + 1} to {BestKnownFilter.BlockHeight}.");
}
continue;
@@ -174,13 +181,13 @@ namespace WalletWasabi.Services
catch (HttpRequestException ex) when (ex.Message.StartsWith(HttpStatusCode.NotFound.ToReasonString()))
{
// Reorg happened
- var reorgedHash = bestKnownFilter.BlockHash;
+ var reorgedHash = bestKnownStartingFilter.BlockHash;
Logger.LogInfo<IndexDownloader>($"REORG Invalid Block: {reorgedHash}");
// 1. Rollback index
using (await IndexLock.LockAsync(Cancel.Token))
{
Index.RemoveAt(Index.Count - 1);
- BestFilter = Index.Last();
+ BestKnownFilter = Index.Last();
}
Reorged?.Invoke(this, reorgedHash);

MODIFY
WalletWasabi/WebClients/ChaumianCoinJoin/WasabiClient.cs
WalletWasabi/WebClients/ChaumianCoinJoin/WasabiClient.cs
@@ -13,6 +13,7 @@ using WalletWasabi.Models;
using System.Text;
using NBitcoin.RPC;
using System.Threading;
+using WalletWasabi.Backend.Models.Responses;
namespace WalletWasabi.WebClients.ChaumianCoinJoin
{
@@ -28,7 +29,7 @@ namespace WalletWasabi.WebClients.ChaumianCoinJoin
/// <remarks>
/// Throws OperationCancelledException if <paramref name="cancel"/> is set.
/// </remarks>
- public async Task<IEnumerable<string>> GetFiltersAsync(uint256 bestKnownBlockHash, int count, CancellationToken cancel = default)
+ public async Task<FiltersResponse> GetFiltersAsync(uint256 bestKnownBlockHash, int count, CancellationToken cancel = default)
{
using (var response = await TorClient.SendAndRetryAsync(HttpMethod.Get,
HttpStatusCode.OK,
@@ -37,7 +38,7 @@ namespace WalletWasabi.WebClients.ChaumianCoinJoin
{
if (response.StatusCode == HttpStatusCode.NoContent)
{
- return Enumerable.Empty<string>();
+ return null;
}
if (response.StatusCode != HttpStatusCode.OK)
{
@@ -48,7 +49,7 @@ namespace WalletWasabi.WebClients.ChaumianCoinJoin
using (HttpContent content = response.Content)
{
- var ret = await content.ReadAsJsonAsync<IEnumerable<string>>();
+ var ret = await content.ReadAsJsonAsync<FiltersResponse>();
return ret;
}
}

return best height