Add key stats for wallet stats
MODIFY
WalletWasabi.Fluent/ViewModels/Wallets/Advanced/WalletStatsViewModel.cs
WalletWasabi.Fluent/ViewModels/Wallets/Advanced/WalletStatsViewModel.cs
@@ -3,6 +3,7 @@ using System.Reactive.Disposables;
using System.Reactive.Linq;
using NBitcoin;
using ReactiveUI;
+using WalletWasabi.Blockchain.Keys;
using WalletWasabi.Fluent.Helpers;
using WalletWasabi.Fluent.ViewModels.Navigation;
using WalletWasabi.Wallets;
@@ -16,6 +17,13 @@ public partial class WalletStatsViewModel : RoutableViewModel
[AutoNotify] private string _balance = "";
[AutoNotify] private string _confirmedBalance = "";
[AutoNotify] private string _unconfirmedBalance = "";
+ [AutoNotify] private int _generatedKeyCount;
+ [AutoNotify] private int _generatedCleanKeyCount;
+ [AutoNotify] private int _generatedLockedKeyCount;
+ [AutoNotify] private int _generatedUsedKeyCount;
+ [AutoNotify] private int _minGapLimit;
+ [AutoNotify] private int _largestExternalKeyGap;
+ [AutoNotify] private int _largestInternalKeyGap;
private readonly Wallet _wallet;
@@ -51,5 +59,14 @@ public partial class WalletStatsViewModel : RoutableViewModel
// Total amount of unconfirmed money in the wallet.
UnconfirmedBalance = $"{_wallet.Coins.Unconfirmed().TotalAmount().ToFormattedString()}";
+
+ GeneratedKeyCount = _wallet.KeyManager.GetKeys().Count();
+ GeneratedCleanKeyCount = _wallet.KeyManager.GetKeys(KeyState.Clean).Count();
+ GeneratedLockedKeyCount = _wallet.KeyManager.GetKeys(KeyState.Locked).Count();
+ GeneratedUsedKeyCount = _wallet.KeyManager.GetKeys(KeyState.Used).Count();
+
+ MinGapLimit = _wallet.KeyManager.MinGapLimit;
+ LargestExternalKeyGap = _wallet.KeyManager.CountConsecutiveUnusedKeys(isInternal: false, ignoreTail: true);
+ LargestInternalKeyGap = _wallet.KeyManager.CountConsecutiveUnusedKeys(isInternal: true, ignoreTail: true);
}
}

MODIFY
WalletWasabi.Fluent/Views/Wallets/Advanced/WalletStatsView.axaml
WalletWasabi.Fluent/Views/Wallets/Advanced/WalletStatsView.axaml
TextValue="{Binding Balance}">
<c:PrivacyContentControl NumberOfPrivacyChars="11" Content="{Binding Balance, StringFormat={}{0} BTC, Mode=OneWay}" Classes="monoSpaced" />
</c:PreviewItem>
+
+ <Separator />
+ <c:PreviewItem Label="Number of addresses"
+ TextValue="{Binding GeneratedKeyCount}">
+ <c:PrivacyContentControl Classes="monoSpaced"
+ NumberOfPrivacyChars="8"
+ Content="{Binding GeneratedKeyCount}" />
+ </c:PreviewItem>
+ <Separator />
+ <c:PreviewItem Label="Number of clean addresses"
+ TextValue="{Binding GeneratedCleanKeyCount}">
+ <c:PrivacyContentControl Classes="monoSpaced"
+ NumberOfPrivacyChars="8"
+ Content="{Binding GeneratedCleanKeyCount}" />
+ </c:PreviewItem>
+ <Separator />
+ <c:PreviewItem Label="Number of locked addresses"
+ TextValue="{Binding GeneratedLockedKeyCount}">
+ <c:PrivacyContentControl Classes="monoSpaced"
+ NumberOfPrivacyChars="8"
+ Content="{Binding GeneratedLockedKeyCount}" />
+ </c:PreviewItem>
+ <Separator />
+ <c:PreviewItem Label="Number of used addresses"
+ TextValue="{Binding GeneratedUsedKeyCount}">
+ <c:PrivacyContentControl Classes="monoSpaced"
+ NumberOfPrivacyChars="8"
+ Content="{Binding GeneratedUsedKeyCount}" />
+ </c:PreviewItem>
+
+ <Separator />
+ <c:PreviewItem Label="Minimum key gap limit"
+ TextValue="{Binding MinGapLimit}">
+ <c:PrivacyContentControl Classes="monoSpaced"
+ NumberOfPrivacyChars="8"
+ Content="{Binding MinGapLimit}" />
+ </c:PreviewItem>
+ <Separator />
+ <c:PreviewItem Label="Largest receive key gap"
+ TextValue="{Binding LargestExternalKeyGap}">
+ <c:PrivacyContentControl Classes="monoSpaced"
+ NumberOfPrivacyChars="8"
+ Content="{Binding LargestExternalKeyGap}" />
+ </c:PreviewItem>
+ <Separator />
+ <c:PreviewItem Label="Largest change key gap"
+ TextValue="{Binding LargestInternalKeyGap}">
+ <c:PrivacyContentControl Classes="monoSpaced"
+ NumberOfPrivacyChars="8"
+ Content="{Binding LargestInternalKeyGap}" />
+ </c:PreviewItem>
</StackPanel>
</c:ContentArea>
</UserControl>

MODIFY
WalletWasabi.Tests/UnitTests/KeyManagementTests.cs
WalletWasabi.Tests/UnitTests/KeyManagementTests.cs
@@ -165,17 +165,17 @@ public class KeyManagementTests
public void GapCountingTests()
{
var km = KeyManager.CreateNew(out _, "", Network.Main);
- Assert.Equal(0, km.CountConsecutiveUnusedKeys(true));
- Assert.Equal(0, km.CountConsecutiveUnusedKeys(false));
+ Assert.Equal(0, km.CountConsecutiveUnusedKeys(true, ignoreTail: false));
+ Assert.Equal(0, km.CountConsecutiveUnusedKeys(false, ignoreTail: false));
var k = km.GenerateNewKey("", KeyState.Clean, true);
- Assert.Equal(1, km.CountConsecutiveUnusedKeys(true));
+ Assert.Equal(1, km.CountConsecutiveUnusedKeys(true, ignoreTail: false));
km.GenerateNewKey("", KeyState.Locked, true);
- Assert.Equal(2, km.CountConsecutiveUnusedKeys(true));
+ Assert.Equal(2, km.CountConsecutiveUnusedKeys(true, ignoreTail: false));
k.SetKeyState(KeyState.Used);
- Assert.Equal(1, km.CountConsecutiveUnusedKeys(true));
+ Assert.Equal(1, km.CountConsecutiveUnusedKeys(true, ignoreTail: false));
for (int i = 0; i < 100; i++)
{
@@ -185,11 +185,11 @@ public class KeyManagementTests
k = k2;
}
}
- Assert.Equal(101, km.CountConsecutiveUnusedKeys(true));
+ Assert.Equal(101, km.CountConsecutiveUnusedKeys(true, ignoreTail: false));
k.SetKeyState(KeyState.Locked);
- Assert.Equal(101, km.CountConsecutiveUnusedKeys(true));
+ Assert.Equal(101, km.CountConsecutiveUnusedKeys(true, ignoreTail: false));
k.SetKeyState(KeyState.Used);
- Assert.Equal(51, km.CountConsecutiveUnusedKeys(true));
+ Assert.Equal(51, km.CountConsecutiveUnusedKeys(true, ignoreTail: false));
}
private static void DeleteFileAndDirectoryIfExists(string filePath)

MODIFY
WalletWasabi/Blockchain/Keys/KeyManager.cs
WalletWasabi/Blockchain/Keys/KeyManager.cs
@@ -446,10 +446,24 @@ public class KeyManager
}
}
- public int CountConsecutiveUnusedKeys(bool isInternal)
+ /// <param name="ignoreTail">If true it does only consider the gap between used keys and does not care about the nonused keys at the end.</param>
+ public int CountConsecutiveUnusedKeys(bool isInternal, bool ignoreTail)
{
var keyIndexes = GetKeys(x => x.IsInternal == isInternal && x.KeyState != KeyState.Used).Select(x => x.Index).ToArray();
+ if (ignoreTail)
+ {
+ var lastUsedIndex = GetKeys(x => x.IsInternal == isInternal && x.KeyState == KeyState.Used).LastOrDefault()?.Index;
+ if (lastUsedIndex is null)
+ {
+ return 0;
+ }
+ else
+ {
+ keyIndexes = keyIndexes.Where(x => x < lastUsedIndex).ToArray();
+ }
+ }
+
var hs = keyIndexes.ToHashSet();
int largerConsecutiveSequence = 0;
@@ -561,18 +575,18 @@ public class KeyManager
if (isInternal.HasValue)
{
- while (CountConsecutiveUnusedKeys(isInternal.Value) < MinGapLimit)
+ while (CountConsecutiveUnusedKeys(isInternal.Value, ignoreTail: false) < MinGapLimit)
{
newKeys.Add(GenerateNewKey(SmartLabel.Empty, KeyState.Clean, isInternal.Value, toFile: false));
}
}
else
{
- while (CountConsecutiveUnusedKeys(true) < MinGapLimit)
+ while (CountConsecutiveUnusedKeys(true, ignoreTail: false) < MinGapLimit)
{
newKeys.Add(GenerateNewKey(SmartLabel.Empty, KeyState.Clean, true, toFile: false));
}
- while (CountConsecutiveUnusedKeys(false) < MinGapLimit)
+ while (CountConsecutiveUnusedKeys(false, ignoreTail: false) < MinGapLimit)
{
newKeys.Add(GenerateNewKey(SmartLabel.Empty, KeyState.Clean, false, toFile: false));
}

Add key stats for wallet stats