public async Task <KeyManager> GenerateWalletAsync(string walletName) { var selectedDevice = SelectedDevice; try { if (selectedDevice?.Fingerprint is null) { throw new Exception("Cannot be null."); } await StopDetectionAsync(); var fingerPrint = (HDFingerprint)selectedDevice.Fingerprint; using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(3)); var extPubKey = await Client.GetXpubAsync( selectedDevice.Model, selectedDevice.Path, KeyManager.DefaultAccountKeyPath, cts.Token).ConfigureAwait(false); var path = WalletManager.WalletDirectories.GetWalletFilePaths(walletName).walletFilePath; return(KeyManager.CreateNewHardwareWalletWatchOnly(fingerPrint, extPubKey, path)); } catch (Exception) { StartDetection(); throw; } }
public static async Task <KeyManager> GenerateWalletAsync(HwiEnumerateEntry device, string walletFilePath, Network network, CancellationToken cancelToken) { if (device.Fingerprint is null) { throw new Exception("Fingerprint cannot be null."); } var client = new HwiClient(network); var fingerPrint = (HDFingerprint)device.Fingerprint; using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(3)); using var genCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, cancelToken); var extPubKey = await client.GetXpubAsync( device.Model, device.Path, KeyManager.GetAccountKeyPath(network), genCts.Token).ConfigureAwait(false); return(KeyManager.CreateNewHardwareWalletWatchOnly(fingerPrint, extPubKey, network, walletFilePath)); }
private static KeyManager GetKeyManagerByColdcardJson(WalletManager manager, JObject jsonWallet, string walletFullPath) { var xpubString = jsonWallet["ExtPubKey"]?.ToString() ?? throw new ArgumentNullException($"Can't get KeyManager, ExtPubKey was null."); var mfpString = jsonWallet["MasterFingerprint"]?.ToString() ?? throw new ArgumentNullException($"Can't get KeyManager, MasterFingerprint was null."); // https://github.com/zkSNACKs/WalletWasabi/pull/1663#issuecomment-508073066 // Coldcard 2.1.0 improperly implemented Wasabi skeleton fingerprint at first, so we must reverse byte order. // The solution was to add a ColdCardFirmwareVersion json field from 2.1.1 and correct the one generated by 2.1.0. var coldCardVersionString = jsonWallet["ColdCardFirmwareVersion"]?.ToString(); var reverseByteOrder = false; if (coldCardVersionString is null) { reverseByteOrder = true; } else { Version coldCardVersion = new(coldCardVersionString); if (coldCardVersion == new Version("2.1.0")) // Should never happen though. { reverseByteOrder = true; } } var bytes = ByteHelpers.FromHex(Guard.NotNullOrEmptyOrWhitespace(nameof(mfpString), mfpString, trim: true)); HDFingerprint mfp = reverseByteOrder ? new HDFingerprint(bytes.Reverse().ToArray()) : new HDFingerprint(bytes); if (manager.WalletExists(mfp)) { throw new InvalidOperationException(WalletExistsErrorMessage); } ExtPubKey extPubKey = NBitcoinHelpers.BetterParseExtPubKey(xpubString); return(KeyManager.CreateNewHardwareWalletWatchOnly(mfp, extPubKey, manager.Network, walletFullPath)); }
public LoadWalletViewModel(WalletManagerViewModel owner, LoadWalletType loadWalletType) : base(loadWalletType == LoadWalletType.Password ? "Test Password" : (loadWalletType == LoadWalletType.Desktop ? "Load Wallet" : "Hardware Wallet")) { Owner = owner; Password = ""; LoadWalletType = loadWalletType; Wallets = new ObservableCollection <LoadWalletEntry>(); WalletLock = new object(); this.WhenAnyValue(x => x.SelectedWallet) .Subscribe(selectedWallet => TrySetWalletStates()); this.WhenAnyValue(x => x.IsWalletOpened) .Subscribe(isWalletOpened => TrySetWalletStates()); this.WhenAnyValue(x => x.IsBusy) .Subscribe(x => TrySetWalletStates()); this.WhenAnyValue(x => x.Password).Subscribe(async x => { try { if (x.NotNullAndNotEmpty()) { char lastChar = x.Last(); if (lastChar == '\r' || lastChar == '\n') // If the last character is cr or lf then act like it'd be a sign to do the job. { Password = x.TrimEnd('\r', '\n'); await LoadKeyManagerAsync(requirePassword: true, isHardwareWallet: false); } } } catch (Exception ex) { Logger.LogTrace(ex); } }); LoadCommand = ReactiveCommand.CreateFromTask(async() => await LoadWalletAsync(), this.WhenAnyValue(x => x.CanLoadWallet)); TestPasswordCommand = ReactiveCommand.CreateFromTask(async() => await LoadKeyManagerAsync(requirePassword: true, isHardwareWallet: false), this.WhenAnyValue(x => x.CanTestPassword)); OpenFolderCommand = ReactiveCommand.Create(OpenWalletsFolder); ImportColdcardCommand = ReactiveCommand.CreateFromTask(async() => { try { var ofd = new OpenFileDialog { AllowMultiple = false, Title = "Import Coldcard" }; if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { ofd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Personal); } var selected = await ofd.ShowAsync(Application.Current.MainWindow, fallBack: true); if (selected != null && selected.Any()) { var path = selected.First(); var jsonString = await File.ReadAllTextAsync(path); var json = JObject.Parse(jsonString); var xpubString = json["ExtPubKey"].ToString(); var mfpString = json["MasterFingerprint"].ToString(); // https://github.com/zkSNACKs/WalletWasabi/pull/1663#issuecomment-508073066 // Coldcard 2.1.0 improperly implemented Wasabi skeleton fingerpring at first, so we must reverse byte order. // The solution was to add a ColdCardFirmwareVersion json field from 2.1.1 and correct the one generated by 2.1.0. var coldCardVersionString = json["ColdCardFirmwareVersion"]?.ToString(); var reverseByteOrder = false; if (coldCardVersionString is null) { reverseByteOrder = true; } else { Version coldCardVersion = new Version(coldCardVersionString); if (coldCardVersion == new Version("2.1.0")) // Should never happen though. { reverseByteOrder = true; } } HDFingerprint mfp = NBitcoinHelpers.BetterParseHDFingerprint(mfpString, reverseByteOrder: reverseByteOrder); ExtPubKey extPubKey = NBitcoinHelpers.BetterParseExtPubKey(xpubString); Logger.LogInfo <LoadWalletViewModel>("Creating new wallet file."); var walletName = Global.GetNextHardwareWalletName(customPrefix: "Coldcard"); var walletFullPath = Global.GetWalletFullPath(walletName); KeyManager.CreateNewHardwareWalletWatchOnly(mfp, extPubKey, walletFullPath); owner.SelectLoadWallet(); } } catch (Exception ex) { SetWarningMessage(ex.ToTypeMessageString()); Logger.LogError <LoadWalletViewModel>(ex); } }, outputScheduler: RxApp.MainThreadScheduler); OpenBrowserCommand = ReactiveCommand.Create <string>(x => { IoHelpers.OpenBrowser(x); }); OpenBrowserCommand.ThrownExceptions.Subscribe(Logger.LogWarning <LoadWalletViewModel>); LoadCommand.ThrownExceptions.Subscribe(Logger.LogWarning <LoadWalletViewModel>); TestPasswordCommand.ThrownExceptions.Subscribe(Logger.LogWarning <LoadWalletViewModel>); OpenFolderCommand.ThrownExceptions.Subscribe(Logger.LogWarning <LoadWalletViewModel>); ImportColdcardCommand.ThrownExceptions.Subscribe(Logger.LogWarning <LoadWalletViewModel>); SetLoadButtonText(); IsHwWalletSearchTextVisible = LoadWalletType == LoadWalletType.Hardware; }
public LoadWalletViewModel(WalletManagerViewModel owner, LoadWalletType loadWalletType) : base(loadWalletType == LoadWalletType.Password ? "Test Password" : (loadWalletType == LoadWalletType.Desktop ? "Load Wallet" : "Hardware Wallet")) { Owner = owner; Password = ""; LoadWalletType = loadWalletType; Wallets = new ObservableCollection <LoadWalletEntry>(); IsHwWalletSearchTextVisible = false; this.WhenAnyValue(x => x.SelectedWallet) .Subscribe(_ => TrySetWalletStates()); this.WhenAnyValue(x => x.IsWalletOpened) .Subscribe(_ => TrySetWalletStates()); this.WhenAnyValue(x => x.IsBusy) .Subscribe(_ => TrySetWalletStates()); LoadCommand = ReactiveCommand.CreateFromTask(async() => await LoadWalletAsync(), this.WhenAnyValue(x => x.CanLoadWallet)); TestPasswordCommand = ReactiveCommand.CreateFromTask(async() => await LoadKeyManagerAsync(requirePassword: true, isHardwareWallet: false), this.WhenAnyValue(x => x.CanTestPassword)); OpenFolderCommand = ReactiveCommand.Create(OpenWalletsFolder); ImportColdcardCommand = ReactiveCommand.CreateFromTask(async() => { var ofd = new OpenFileDialog { AllowMultiple = false, Title = "Import Coldcard" }; if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { ofd.Directory = Path.Combine("/media", Environment.UserName); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { ofd.Directory = Environment.GetFolderPath(Environment.SpecialFolder.Personal); } var window = (Application.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime).MainWindow; var selected = await ofd.ShowAsync(window, fallBack: true); if (selected != null && selected.Any()) { var path = selected.First(); var jsonString = await File.ReadAllTextAsync(path); var json = JObject.Parse(jsonString); var xpubString = json["ExtPubKey"].ToString(); var mfpString = json["MasterFingerprint"].ToString(); // https://github.com/zkSNACKs/WalletWasabi/pull/1663#issuecomment-508073066 // Coldcard 2.1.0 improperly implemented Wasabi skeleton fingerprint at first, so we must reverse byte order. // The solution was to add a ColdCardFirmwareVersion json field from 2.1.1 and correct the one generated by 2.1.0. var coldCardVersionString = json["ColdCardFirmwareVersion"]?.ToString(); var reverseByteOrder = false; if (coldCardVersionString is null) { reverseByteOrder = true; } else { Version coldCardVersion = new Version(coldCardVersionString); if (coldCardVersion == new Version("2.1.0")) // Should never happen though. { reverseByteOrder = true; } } HDFingerprint mfp = NBitcoinHelpers.BetterParseHDFingerprint(mfpString, reverseByteOrder: reverseByteOrder); ExtPubKey extPubKey = NBitcoinHelpers.BetterParseExtPubKey(xpubString); Logger.LogInfo("Creating a new wallet file."); var walletName = Global.GetNextHardwareWalletName(customPrefix: "Coldcard"); var walletFullPath = Global.GetWalletFullPath(walletName); KeyManager.CreateNewHardwareWalletWatchOnly(mfp, extPubKey, walletFullPath); owner.SelectLoadWallet(); } }); EnumerateHardwareWalletsCommand = ReactiveCommand.CreateFromTask(async() => await EnumerateHardwareWalletsAsync()); OpenBrowserCommand = ReactiveCommand.Create <string>(x => IoHelpers.OpenBrowser(x)); Observable.Merge(OpenBrowserCommand.ThrownExceptions) .Merge(LoadCommand.ThrownExceptions) .Merge(TestPasswordCommand.ThrownExceptions) .Merge(OpenFolderCommand.ThrownExceptions) .Merge(ImportColdcardCommand.ThrownExceptions) .Merge(EnumerateHardwareWalletsCommand.ThrownExceptions) .Subscribe(ex => { NotificationHelpers.Error(ex.ToTypeMessageString()); Logger.LogError(ex); }); SetLoadButtonText(); }
public ConnectHardwareWalletViewModel(WalletManagerViewModel owner) : base("Hardware Wallet") { Global = Locator.Current.GetService <Global>(); Owner = owner; Wallets = new ObservableCollection <LoadWalletEntry>(); IsHwWalletSearchTextVisible = false; this.WhenAnyValue(x => x.SelectedWallet) .Subscribe(_ => TrySetWalletStates()); this.WhenAnyValue(x => x.IsWalletOpened) .Subscribe(_ => TrySetWalletStates()); this.WhenAnyValue(x => x.IsBusy) .Subscribe(_ => TrySetWalletStates()); this.WhenAnyValue(x => x.IsBusy) .Subscribe(_ => TrySetWalletStates()); LoadCommand = ReactiveCommand.CreateFromTask(LoadWalletAsync, this.WhenAnyValue(x => x.CanLoadWallet)); ImportColdcardCommand = ReactiveCommand.CreateFromTask(async() => { var ofd = new OpenFileDialog { AllowMultiple = false, Title = "Import Coldcard" }; if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { ofd.Directory = Path.Combine("/media", Environment.UserName); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { ofd.Directory = Environment.GetFolderPath(Environment.SpecialFolder.Personal); } var window = (Application.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime).MainWindow; var selected = await ofd.ShowAsync(window, fallBack: true); if (selected is { } && selected.Any()) { var path = selected.First(); var jsonString = await File.ReadAllTextAsync(path); var json = JObject.Parse(jsonString); var xpubString = json["ExtPubKey"].ToString(); var mfpString = json["MasterFingerprint"].ToString(); // https://github.com/zkSNACKs/WalletWasabi/pull/1663#issuecomment-508073066 // Coldcard 2.1.0 improperly implemented Wasabi skeleton fingerprint at first, so we must reverse byte order. // The solution was to add a ColdCardFirmwareVersion json field from 2.1.1 and correct the one generated by 2.1.0. var coldCardVersionString = json["ColdCardFirmwareVersion"]?.ToString(); var reverseByteOrder = false; if (coldCardVersionString is null) { reverseByteOrder = true; } else { Version coldCardVersion = new Version(coldCardVersionString); if (coldCardVersion == new Version("2.1.0")) // Should never happen though. { reverseByteOrder = true; } } var bytes = ByteHelpers.FromHex(Guard.NotNullOrEmptyOrWhitespace(nameof(mfpString), mfpString, trim: true)); HDFingerprint mfp = reverseByteOrder ? new HDFingerprint(bytes.Reverse().ToArray()) : new HDFingerprint(bytes); ExtPubKey extPubKey = NBitcoinHelpers.BetterParseExtPubKey(xpubString); Logger.LogInfo("Creating a new wallet file."); var walletName = Global.WalletManager.WalletDirectories.GetNextWalletName("Coldcard"); var walletFullPath = Global.WalletManager.WalletDirectories.GetWalletFilePaths(walletName).walletFilePath; KeyManager.CreateNewHardwareWalletWatchOnly(mfp, extPubKey, walletFullPath); owner.SelectLoadWallet(); } });