Separate business logic from UI logic
MODIFY
WalletWasabi.Gui/Tabs/WalletManager/GenerateWalletViewModel.cs
WalletWasabi.Gui/Tabs/WalletManager/GenerateWalletViewModel.cs
using AvalonStudio.Extensibility;
using AvalonStudio.Shell;
-using NBitcoin;
using ReactiveUI;
using Splat;
using System;
-using System.Collections.Generic;
-using System.IO;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
-using WalletWasabi.Blockchain.Keys;
using WalletWasabi.Gui.Helpers;
using WalletWasabi.Gui.ViewModels;
using WalletWasabi.Gui.ViewModels.Validation;
@@ -19,6 +15,7 @@ using WalletWasabi.Models;
namespace WalletWasabi.Gui.Tabs.WalletManager
{
+
internal class GenerateWalletViewModel : CategoryViewModel
{
private string _password;
@@ -46,39 +43,13 @@ namespace WalletWasabi.Gui.Tabs.WalletManager
}
private void DoNextCommand()
- {
- WalletName = Guard.Correct(WalletName);
-
- if (!ValidateWalletName(WalletName))
- {
- NotificationHelpers.Error("Invalid wallet name.");
- return;
- }
-
- string walletFilePath = Path.Combine(Global.WalletsDir, $"{WalletName}.json");
-
- if (!TermsAccepted)
- {
- NotificationHelpers.Error("Terms are not accepted.");
- }
- else if (string.IsNullOrWhiteSpace(WalletName))
- {
- NotificationHelpers.Error("Invalid wallet name.");
- }
- else if (File.Exists(walletFilePath))
- {
- NotificationHelpers.Error("Wallet name is already taken.");
- }
- else
{
try
{
- PasswordHelper.Guard(Password); // Here we are not letting anything that will be autocorrected later. We need to generate the wallet exactly with the entered password bacause of compatibility.
-
- var km = KeyManager.CreateNew(out Mnemonic mnemonic, Password);
- km.SetNetwork(Global.Network);
- km.SetBestHeight(new Height(Global.BitcoinStore.SmartHeaderChain.TipHeight));
- km.SetFilePath(walletFilePath);
+ var walletGenerator = new WalletGenerator(Global.WalletsDir, Global.Network);
+ walletGenerator.TermsAccepted = TermsAccepted;
+ walletGenerator.TipHeight = Global.BitcoinStore.SmartHeaderChain.TipHeight;
+ var (km, mnemonic) = walletGenerator.GenerateWallet(WalletName, Password);
Owner.CurrentView = new GenerateWalletSuccessViewModel(Owner, km, mnemonic);
}
catch(Exception ex)
@@ -87,22 +58,6 @@ namespace WalletWasabi.Gui.Tabs.WalletManager
NotificationHelpers.Error(ex.ToUserFriendlyString());
}
}
- }
-
- private static readonly string[] ReservedFileNames = new string[]
- {
- "CON", "PRN", "AUX", "NUL",
- "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
- "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
- };
-
- private bool ValidateWalletName(string walletName)
- {
- var invalidChars = Path.GetInvalidFileNameChars();
- var isValid = !walletName.Any(c => invalidChars.Contains(c)) && !walletName.EndsWith(".");
- var isReserved = ReservedFileNames.Any(w => walletName.ToUpper() == w || walletName.ToUpper().StartsWith(w + "."));
- return isValid && !isReserved;
- }
public ErrorDescriptors ValidatePassword()
{

ADD
WalletWasabi/Blockchain/Keys/WalletGenerator.cs
+using NBitcoin;
+using System;
+using System.IO;
+using System.Linq;
+using WalletWasabi.Helpers;
+using WalletWasabi.Models;
+
+namespace WalletWasabi.Blockchain.Keys
+{
+ internal class WalletGenerator
+ {
+ public string WalletsDir { get; private set; }
+ public Network Network { get; private set; }
+ public bool TermsAccepted { get; set; }
+ public uint TipHeight { get; set; }
+
+ public WalletGenerator(string walletsDir, Network network)
+ {
+ WalletsDir = walletsDir;
+ Network = network;
+ }
+
+ public (KeyManager, Mnemonic) GenerateWallet(string walletName, string password)
+ {
+ if (!ValidateWalletName(walletName))
+ {
+ throw new ArgumentException("Invalid wallet name.");
+ }
+
+ if (!TermsAccepted)
+ {
+ throw new InvalidOperationException("Terms are not accepted.");
+ }
+
+ string walletFilePath = Path.Combine(WalletsDir, $"{walletName}.json");
+ if (File.Exists(walletFilePath))
+ {
+ throw new ArgumentException("Wallet name is already taken.");
+ }
+
+ PasswordHelper.Guard(password); // Here we are not letting anything that will be autocorrected later. We need to generate the wallet exactly with the entered password bacause of compatibility.
+
+ var km = KeyManager.CreateNew(out Mnemonic mnemonic, password);
+ km.SetNetwork(Network);
+ km.SetBestHeight(new Height(TipHeight));
+ km.SetFilePath(walletFilePath);
+ return (km, mnemonic);
+ }
+
+ private static readonly string[] ReservedFileNames = new string[]
+ {
+ "CON", "PRN", "AUX", "NUL",
+ "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
+ "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
+ };
+
+ private bool ValidateWalletName(string walletName)
+ {
+ if (string.IsNullOrWhiteSpace(walletName))
+ {
+ return false;
+ }
+ var invalidChars = Path.GetInvalidFileNameChars();
+ var isValid = !walletName.Any(c => invalidChars.Contains(c)) && !walletName.EndsWith(".");
+ var isReserved = ReservedFileNames.Any(w => walletName.ToUpper() == w || walletName.ToUpper().StartsWith(w + "."));
+ return isValid && !isReserved;
+ }
+ }
+}

MODIFY
WalletWasabi/Extensions/ExceptionExtensions.cs
WalletWasabi/Extensions/ExceptionExtensions.cs
@@ -36,7 +36,9 @@ namespace System
["bad-txns-nonfinal"] = "The transaction is not final and cannot be broadcasted.",
["bad-txns-oversize"] = "The transaction is too big.",
- ["invalid password"] = "Wrong password."
+ ["invalid password"] = "Wrong password.",
+ ["Invalid wallet name"] = "Invalid wallet name.",
+ ["Wallet name is already taken"] = "Wallet name is already taken."
};
public static string ToUserFriendlyString(this Exception ex)

Separate business logic from UI logic