Creating walletDirectories class
MODIFY
WalletWasabi.Gui/Global.cs
WalletWasabi.Gui/Global.cs
@@ -47,9 +47,6 @@ namespace WalletWasabi.Gui
public string DataDir { get; }
public string TorLogsFile { get; }
- public string WalletsDir { get; }
- public string WalletBackupsDir { get; }
-
public BitcoinStore BitcoinStore { get; private set; }
public LegalDocuments LegalDocuments { get; set; }
public Config Config { get; private set; }
@@ -83,15 +80,11 @@ namespace WalletWasabi.Gui
StoppingCts = new CancellationTokenSource();
DataDir = EnvironmentHelpers.GetDataDir(Path.Combine("WalletWasabi", "Client"));
TorLogsFile = Path.Combine(DataDir, "TorLogs.txt");
- WalletsDir = Path.Combine(DataDir, "Wallets");
- WalletBackupsDir = Path.Combine(DataDir, "WalletBackups");
Directory.CreateDirectory(DataDir);
- Directory.CreateDirectory(WalletsDir);
- Directory.CreateDirectory(WalletBackupsDir);
HostedServices = new HostedServices();
- WalletManager = new WalletManager(WalletBackupsDir);
+ WalletManager = new WalletManager(new WalletDirectories(DataDir));
LegalDocuments = LegalDocuments.TryLoadAgreed(DataDir);

MODIFY
WalletWasabi.Gui/Tabs/WalletManager/LoadWallets/LoadWalletViewModel.cs
WalletWasabi.Gui/Tabs/WalletManager/LoadWallets/LoadWalletViewModel.cs
@@ -296,9 +296,7 @@ namespace WalletWasabi.Gui.Tabs.WalletManager.LoadWallets
Wallets.Clear();
Password = "";
- var directoryInfo = new DirectoryInfo(Global.WalletsDir);
- var walletFiles = directoryInfo.GetFiles("*.json", SearchOption.TopDirectoryOnly).OrderByDescending(t => t.LastAccessTimeUtc);
- foreach (var file in walletFiles)
+ foreach (var file in Global.WalletManager.WalletDirectories.EnumerateWalletFiles())
{
var wallet = new LoadWalletEntry(Path.GetFileNameWithoutExtension(file.FullName));
if (IsPasswordRequired)

MODIFY
WalletWasabi.Gui/Tabs/WalletManager/RecoverWallets/RecoverWalletViewModel.cs
WalletWasabi.Gui/Tabs/WalletManager/RecoverWallets/RecoverWalletViewModel.cs
@@ -44,7 +44,7 @@ namespace WalletWasabi.Gui.Tabs.WalletManager.RecoverWallets
MnemonicWords = Guard.Correct(MnemonicWords);
Password = Guard.Correct(Password); // Do not let whitespaces to the beginning and to the end.
- string walletFilePath = Path.Combine(Global.WalletsDir, $"{WalletName}.json");
+ string walletFilePath = Global.WalletManager.WalletDirectories.GetWalletPath(WalletName);
if (string.IsNullOrWhiteSpace(WalletName))
{

MODIFY
WalletWasabi.Gui/ViewModels/MainWindowViewModel.cs
WalletWasabi.Gui/ViewModels/MainWindowViewModel.cs
@@ -132,7 +132,7 @@ namespace WalletWasabi.Gui.ViewModels
var global = Locator.Current.GetService<Global>();
- var isAnyDesktopWalletAvailable = Directory.Exists(global.WalletsDir) && Directory.EnumerateFiles(global.WalletsDir).Any();
+ var isAnyDesktopWalletAvailable = global.WalletManager.WalletDirectories.EnumerateWalletFiles().Any();
if (isAnyDesktopWalletAvailable)
{

ADD
WalletWasabi.Tests/UnitTests/WalletDirTests.cs
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using WalletWasabi.Helpers;
+using WalletWasabi.Wallets;
+using Xunit;
+
+namespace WalletWasabi.Tests.UnitTests
+{
+ public class WalletDirTests
+ {
+ private async Task<(string walletsPath, string walletsBackupPath)> CleanupWalletDirectoriesAsync(string baseDir)
+ {
+ var walletsPath = Path.Combine(baseDir, WalletDirectories.WalletsDirName);
+ var walletsBackupPath = Path.Combine(baseDir, WalletDirectories.WalletsBackupDirName);
+ await IoHelpers.DeleteRecursivelyWithMagicDustAsync(walletsPath);
+ await IoHelpers.DeleteRecursivelyWithMagicDustAsync(walletsBackupPath);
+
+ return (walletsPath, walletsBackupPath);
+ }
+
+ [Fact]
+ public async Task CreatesWalletDirectoriesAsync()
+ {
+ var baseDir = Path.Combine(Global.Instance.DataDir, EnvironmentHelpers.GetCallerFileName(), EnvironmentHelpers.GetMethodName());
+ (string walletsPath, string walletsBackupPath) = await CleanupWalletDirectoriesAsync(baseDir);
+
+ new WalletDirectories(baseDir);
+ Assert.True(Directory.Exists(walletsPath));
+ Assert.True(Directory.Exists(walletsBackupPath));
+
+ // Testing what happens if the directories are already exist.
+ new WalletDirectories(baseDir);
+ Assert.True(Directory.Exists(walletsPath));
+ Assert.True(Directory.Exists(walletsBackupPath));
+ }
+
+ [Fact]
+ public async Task CorrestWalletDirectoryNameAsync()
+ {
+ var baseDir = Path.Combine(Global.Instance.DataDir, EnvironmentHelpers.GetCallerFileName(), EnvironmentHelpers.GetMethodName());
+ (string walletsPath, string walletsBackupPath) = await CleanupWalletDirectoriesAsync(baseDir);
+
+ var walletDirectories = new WalletDirectories($" {baseDir} ");
+ Assert.False(Directory.Exists(walletsPath));
+ Assert.False(Directory.Exists(walletsBackupPath));
+ Assert.Equal(baseDir, walletDirectories.WorkDir);
+ Assert.Equal(walletsPath, walletDirectories.WalletsDir);
+ Assert.Equal(walletsBackupPath, walletDirectories.WalletsBackupDir);
+ }
+
+ [Fact]
+ public async Task ServesWalletFilesAsync()
+ {
+ var baseDir = Path.Combine(Global.Instance.DataDir, EnvironmentHelpers.GetCallerFileName(), EnvironmentHelpers.GetMethodName());
+ await CleanupWalletDirectoriesAsync(baseDir);
+
+ var walletDirectories = new WalletDirectories(baseDir);
+ string walletName = "FooWallet.json";
+
+ var walletPath = walletDirectories.GetWalletPath(walletName);
+ var walletBackupPath = walletDirectories.GetWalletBackupPath(walletName);
+
+ Assert.Equal(Path.Combine(walletDirectories.WalletsDir, walletName), walletPath);
+ Assert.Equal(Path.Combine(walletDirectories.WalletsBackupDir, walletName), walletBackupPath);
+ }
+
+ [Fact]
+ public async Task EnsuresJsonAsync()
+ {
+ var baseDir = Path.Combine(Global.Instance.DataDir, EnvironmentHelpers.GetCallerFileName(), EnvironmentHelpers.GetMethodName());
+ await CleanupWalletDirectoriesAsync(baseDir);
+
+ var walletDirectories = new WalletDirectories(baseDir);
+ string walletName = "FooWallet";
+ string walletFileName = $"{walletName}.json";
+
+ var walletPath = walletDirectories.GetWalletPath(walletName);
+ var walletBackupPath = walletDirectories.GetWalletBackupPath(walletName);
+
+ Assert.Equal(Path.Combine(walletDirectories.WalletsDir, walletFileName), walletPath);
+ Assert.Equal(Path.Combine(walletDirectories.WalletsBackupDir, walletFileName), walletBackupPath);
+ }
+
+ [Fact]
+ public async Task EnumerateFilesAsync()
+ {
+ var baseDir = Path.Combine(Global.Instance.DataDir, EnvironmentHelpers.GetCallerFileName(), EnvironmentHelpers.GetMethodName());
+ await CleanupWalletDirectoriesAsync(baseDir);
+
+ var walletDirectories = new WalletDirectories(baseDir);
+
+ List<string> wallets = new List<string>();
+ const int NumberOfWallets = 4;
+ for (int i = 0; i < NumberOfWallets; i++)
+ {
+ var walletFile = Path.Combine(walletDirectories.WalletsDir, $"FooWallet{i}.json");
+ var dummyFile = Path.Combine(walletDirectories.WalletsDir, $"FooWallet{i}.dummy");
+ wallets.Add(walletFile);
+ File.Create(walletFile);
+ File.Create(dummyFile);
+ File.Create(Path.Combine(walletDirectories.WalletsBackupDir, $"FooWallet{i}.json"));
+ }
+
+ Assert.True(wallets.ToHashSet().SetEquals(walletDirectories.EnumerateWalletFiles().Select(x => x.FullName).ToHashSet()));
+ }
+
+ [Fact]
+ public async Task EnumerateOrdersByAccessAsync()
+ {
+ var baseDir = Path.Combine(Global.Instance.DataDir, EnvironmentHelpers.GetCallerFileName(), EnvironmentHelpers.GetMethodName());
+ await CleanupWalletDirectoriesAsync(baseDir);
+
+ var walletDirectories = new WalletDirectories(baseDir);
+
+ var walletFile1 = Path.Combine(walletDirectories.WalletsDir, $"FooWallet1.json");
+ File.SetLastAccessTimeUtc(walletFile1, new DateTime(2005, 1, 1, 1, 1, 1, DateTimeKind.Utc));
+ var walletFile2 = Path.Combine(walletDirectories.WalletsDir, $"FooWallet2.json");
+ File.SetLastAccessTimeUtc(walletFile2, new DateTime(2000, 1, 1, 1, 1, 1, DateTimeKind.Utc));
+ var walletFile3 = Path.Combine(walletDirectories.WalletsDir, $"FooWallet3.json");
+ File.SetLastAccessTimeUtc(walletFile1, new DateTime(2010, 1, 1, 1, 1, 1, DateTimeKind.Utc));
+ var orderedWallets = new[] { walletFile3, walletFile1, walletFile2 };
+
+ Assert.Equal(orderedWallets, walletDirectories.EnumerateWalletFiles().Select(x => x.FullName));
+ }
+
+ [Fact]
+ public async Task EnumerateMissingDirAsync()
+ {
+ var baseDir = Path.Combine(Global.Instance.DataDir, EnvironmentHelpers.GetCallerFileName(), EnvironmentHelpers.GetMethodName());
+ (string walletsPath, string walletsBackupPath) = await CleanupWalletDirectoriesAsync(baseDir);
+
+ var walletDirectories = new WalletDirectories(baseDir);
+
+ Assert.Empty(walletDirectories.EnumerateWalletFiles());
+ Directory.Delete(walletsBackupPath);
+ Assert.Empty(walletDirectories.EnumerateWalletFiles());
+ Directory.Delete(walletsPath);
+ Assert.Empty(walletDirectories.EnumerateWalletFiles());
+ Directory.Delete(baseDir);
+ Assert.Empty(walletDirectories.EnumerateWalletFiles());
+ }
+ }
+}

MODIFY
WalletWasabi.Tests/WalletWasabi.Tests.csproj
WalletWasabi.Tests/WalletWasabi.Tests.csproj
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\WalletWasabi.Gui\WalletWasabi.Gui.csproj" />
<ProjectReference Include="..\WalletWasabi.Backend\WalletWasabi.Backend.csproj" />
<ProjectReference Include="..\WalletWasabi.Gui\WalletWasabi.Gui.csproj" />
<ProjectReference Include="..\WalletWasabi\WalletWasabi.csproj" />

ADD
WalletWasabi/Wallets/WalletDirectories.cs
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using WalletWasabi.Helpers;
+
+namespace WalletWasabi.Wallets
+{
+ public class WalletDirectories
+ {
+ public const string WalletsDirName = "Wallets";
+ public const string WalletsBackupDirName = "WalletBackups";
+ private const string WalletFileExtension = "json";
+
+ public WalletDirectories(string workDir)
+ {
+ WorkDir = Guard.NotNullOrEmptyOrWhitespace(nameof(workDir), workDir, true);
+
+ Directory.CreateDirectory(WalletsDir);
+ Directory.CreateDirectory(WalletsBackupDir);
+ }
+
+ public string WorkDir { get; }
+ public string WalletsDir => Path.Combine(WorkDir, WalletsDirName);
+ public string WalletsBackupDir => Path.Combine(WorkDir, WalletsBackupDirName);
+
+ private static string GetWalltFilePath(string dir, string walletName)
+ {
+ if (!walletName.ToLowerInvariant().EndsWith($".{WalletFileExtension}"))
+ {
+ walletName = $"{walletName}.{WalletFileExtension}";
+ }
+ return Path.Combine(dir, walletName);
+ }
+
+ public string GetWalletPath(string walletName)
+ {
+ return GetWalltFilePath(WalletsDir, walletName);
+ }
+
+ public string GetWalletBackupPath(string walletName)
+ {
+ return GetWalltFilePath(WalletsBackupDir, walletName);
+ }
+
+ public IEnumerable<FileInfo> EnumerateWalletFiles()
+ {
+ if (!Directory.Exists(WalletsDir))
+ {
+ return Enumerable.Empty<FileInfo>();
+ }
+
+ var directoryInfo = new DirectoryInfo(WalletsDir);
+ return directoryInfo.GetFiles($"*.{WalletFileExtension}", SearchOption.TopDirectoryOnly).OrderByDescending(t => t.LastAccessTimeUtc);
+ }
+ }
+}

MODIFY
WalletWasabi/Wallets/WalletManager.cs
WalletWasabi/Wallets/WalletManager.cs
@@ -25,13 +25,13 @@ namespace WalletWasabi.Wallets
{
public class WalletManager
{
- public WalletManager(string walletBackupsDir)
+ public WalletManager(WalletDirectories walletDirectories)
{
- WalletBackupsDir = walletBackupsDir;
Wallets = new Dictionary<WalletService, HashSet<uint256>>();
Lock = new object();
AddRemoveLock = new AsyncLock();
CancelAllInitialization = new CancellationTokenSource();
+ WalletDirectories = walletDirectories;
}
private CancellationTokenSource CancelAllInitialization { get; }
@@ -43,7 +43,6 @@ namespace WalletWasabi.Wallets
private Dictionary<WalletService, HashSet<uint256>> Wallets { get; }
private object Lock { get; }
private AsyncLock AddRemoveLock { get; }
- private string WalletBackupsDir { get; }
private IEnumerable<KeyValuePair<WalletService, HashSet<uint256>>> AliveWalletsNoLock => Wallets.Where(x => x.Key is { IsStoppingOrStopped: var isDisposed } && !isDisposed);
@@ -66,6 +65,7 @@ namespace WalletWasabi.Wallets
private IFeeProvider FeeProvider { get; set; }
private CoreNode BitcoinCoreNode { get; set; }
+ public WalletDirectories WalletDirectories { get; }
public WalletService GetFirstOrDefaultWallet()
{
@@ -188,9 +188,9 @@ namespace WalletWasabi.Wallets
try
{
var keyManager = walletService.KeyManager;
- if (keyManager is { } && !string.IsNullOrWhiteSpace(WalletBackupsDir))
+ if (keyManager is { } && WalletDirectories is { })
{
- string backupWalletFilePath = Path.Combine(WalletBackupsDir, Path.GetFileName(keyManager.FilePath));
+ string backupWalletFilePath = WalletDirectories.GetWalletBackupPath(Path.GetFileName(keyManager.FilePath));
keyManager.ToFile(backupWalletFilePath);
Logger.LogInfo($"{nameof(walletService.KeyManager)} backup saved to `{backupWalletFilePath}`.");
}

Creating walletDirectories class