public GenerateWalletViewModel(WalletManagerViewModel owner) : base("Generate Wallet")
        {
            Owner = owner;

            GenerateCommand = ReactiveCommand.Create(() =>
            {
                DoGenerateCommand();
            },
                                                     this.WhenAnyValue(x => x.TermsAccepted));

            this.WhenAnyValue(x => x.Password).Subscribe(x =>
            {
                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');
                    }
                }
            });
            this.WhenAnyValue(x => x.PasswordConfirmation).Subscribe(x =>
            {
                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.
                    {
                        PasswordConfirmation = x.TrimEnd('\r', '\n');
                        DoGenerateCommand();
                    }
                }
            });
        }
Пример #2
0
        public GenerateWalletViewModel(WalletManagerViewModel owner) : base("Generate Wallet")
        {
            Disposables = new CompositeDisposable();

            Owner = owner;

            GenerateCommand = ReactiveCommand.Create(() =>
            {
                DoGenerateCommand();
            },
                                                     this.WhenAnyValue(x => x.TermsAccepted)).DisposeWith(Disposables);

            this.WhenAnyValue(x => x.Password).Subscribe(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');
                            if (TermsAccepted)
                            {
                                DoGenerateCommand();
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    Logger.LogTrace(ex);
                }
            }).DisposeWith(Disposables);
        }
Пример #3
0
        public GenerateWalletSuccessViewModel(WalletManagerViewModel owner, Mnemonic mnemonic) : base("Wallet Generated Successfully!")
        {
            _mnemonicWords = mnemonic.ToString();

            ConfirmCommand = ReactiveCommand.Create(() =>
            {
                owner.SelectTestPassword();
            });
        }
        public RecoverWalletViewModel(WalletManagerViewModel owner) : base("Recover Wallet")
        {
            RecoverCommand = ReactiveCommand.Create(() =>
            {
                WalletName    = Guard.Correct(WalletName);
                MnemonicWords = Guard.Correct(MnemonicWords);
                Password      = Guard.Correct(Password);            // Don't let whitespaces to the beginning and to the end.

                string walletFilePath = Path.Combine(Global.WalletsDir, $"{WalletName}.json");

                if (TermsAccepted == false)
                {
                    ValidationMessage = "Terms are not accepted.";
                }
                else if (string.IsNullOrWhiteSpace(WalletName))
                {
                    ValidationMessage = $"The name {WalletName} is not valid.";
                }
                else if (File.Exists(walletFilePath))
                {
                    ValidationMessage = $"The name {WalletName} is already taken.";
                }
                else if (string.IsNullOrWhiteSpace(MnemonicWords))
                {
                    ValidationMessage = $"Mnemonic words were not supplied.";
                }
                else
                {
                    try
                    {
                        var mnemonic = new Mnemonic(MnemonicWords);
                        KeyManager.Recover(mnemonic, Password, walletFilePath);

                        owner.SelectLoadWallet();
                    }
                    catch (Exception ex)
                    {
                        ValidationMessage = ex.ToTypeMessageString();
                        Logger.LogError <LoadWalletViewModel>(ex);
                    }
                }
            },
                                                    this.WhenAnyValue(x => x.TermsAccepted));
            this.WhenAnyValue(x => x.MnemonicWords).Subscribe(x => UpdateSuggestions(x));
            this.WhenAnyValue(x => x.Password).Subscribe(x =>
            {
                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');
                    }
                }
            });
        }
        public GenerateWalletSuccessViewModel(WalletManagerViewModel owner, Mnemonic mnemonic) : base("Wallet Generated Successfully!")
        {
            _mnemonicWords = mnemonic.ToString();

            ConfirmCommand = ReactiveCommand.Create(() => owner.SelectTestPassword());

            ConfirmCommand.ThrownExceptions
            .ObserveOn(RxApp.TaskpoolScheduler)
            .Subscribe(ex => Logger.LogError(ex));
        }
		public GenerateWalletViewModel(WalletManagerViewModel owner) : base("Generate Wallet")
		{
			Owner = owner;

			IObservable<bool> canGenerate = Observable.CombineLatest(
				this.WhenAnyValue(x => x.TermsAccepted),
				this.WhenAnyValue(x => x.Password).Select(pw => !ValidatePassword().HasErrors),
				(terms, pw) => terms && pw);

			GenerateCommand = ReactiveCommand.Create(DoGenerateCommand, canGenerate);
		}
Пример #7
0
        public GenerateWalletSuccessViewModel(WalletManagerViewModel owner, Mnemonic mnemonic) : base("Wallet Generated Successfully!")
        {
            Disposables = new CompositeDisposable();

            _mnemonicWords = mnemonic.ToString();

            ConfirmCommand = ReactiveCommand.Create(() =>
            {
                owner.SelectTestPassword();
            }).DisposeWith(Disposables);
        }
Пример #8
0
        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);

            LoadCommand.ThrownExceptions.Subscribe(ex => Logger.LogWarning <LoadWalletViewModel>(ex));
            TestPasswordCommand.ThrownExceptions.Subscribe(ex => Logger.LogWarning <LoadWalletViewModel>(ex));
            OpenFolderCommand.ThrownExceptions.Subscribe(ex => Logger.LogWarning <LoadWalletViewModel>(ex));

            SetLoadButtonText();

            IsHwWalletSearchTextVisible = LoadWalletType == LoadWalletType.Hardware;
        }
        public GenerateWalletViewModel(WalletManagerViewModel owner) : base("Generate Wallet")
        {
            Owner = owner;

            IObservable <bool> canGenerate = Observable.CombineLatest(
                this.WhenAnyValue(x => x.TermsAccepted),
                this.WhenAnyValue(x => x.Password).Select(pw => !ValidatePassword().HasErrors),
                (terms, pw) => terms && pw);

            NextCommand = ReactiveCommand.Create(DoNextCommand, canGenerate);

            NextCommand.ThrownExceptions
            .ObserveOn(RxApp.TaskpoolScheduler)
            .Subscribe(ex => Logger.LogError(ex));
        }
Пример #10
0
        public RecoverWalletViewModel(WalletManagerViewModel owner) : base("Recover Wallet")
        {
            RecoverCommand = ReactiveCommand.Create(() =>
            {
                WalletName    = Guard.Correct(WalletName);
                MnemonicWords = Guard.Correct(MnemonicWords);

                string walletFilePath = Path.Combine(Global.WalletsDir, $"{WalletName}.json");

                if (TermsAccepted == false)
                {
                    ValidationMessage = "Terms are not accepted.";
                }
                else if (string.IsNullOrWhiteSpace(WalletName))
                {
                    ValidationMessage = $"The name {WalletName} is not valid.";
                }
                else if (File.Exists(walletFilePath))
                {
                    ValidationMessage = $"The name {WalletName} is already taken.";
                }
                else if (string.IsNullOrWhiteSpace(MnemonicWords))
                {
                    ValidationMessage = $"Mnemonic words were not supplied.";
                }
                else
                {
                    try
                    {
                        var mnemonic = new Mnemonic(MnemonicWords);
                        KeyManager.Recover(mnemonic, Password, walletFilePath);

                        owner.SelectLoadWallet();
                    }
                    catch (Exception ex)
                    {
                        ValidationMessage = ex.ToTypeMessageString();
                        Logger.LogError <LoadWalletViewModel>(ex);
                    }
                }
            },
                                                    this.WhenAnyValue(x => x.TermsAccepted));
            this.WhenAnyValue(x => x.MnemonicWords).Subscribe(x => UpdateSuggestions(x));
        }
Пример #11
0
        public LoadWalletViewModel(WalletManagerViewModel owner) : base("Load Wallet")
        {
            Owner    = owner;
            _wallets = new ObservableCollection <string>();

            this.WhenAnyValue(x => x.SelectedWallet)
            .Subscribe(selectedWallet => CanLoadWallet = !string.IsNullOrEmpty(selectedWallet) && !IsWalletOpened);

            this.WhenAnyValue(x => x.IsWalletOpened)
            .Subscribe(isWalletOpened => CanLoadWallet = !string.IsNullOrEmpty(SelectedWallet) && !isWalletOpened);

            this.WhenAnyValue(x => x.IsWalletOpened)
            .Subscribe(isWalletOpened => WarningMessage = isWalletOpened
                                        ? "There is already an open wallet. Restart the application to open another one."
                                        : string.Empty);

            LoadCommand       = ReactiveCommand.Create(LoadWalletAsync, this.WhenAnyValue(x => x.CanLoadWallet));
            OpenFolderCommand = ReactiveCommand.Create(OpenWalletsFolder);
            SetLoadButtonText(IsBusy);
        }
Пример #12
0
        public GenerateWalletViewModel(WalletManagerViewModel owner) : base("Generate Wallet")
        {
            GenerateCommand = ReactiveCommand.Create(() =>
            {
                WalletName = Guard.Correct(WalletName);

                string walletFilePath = Path.Combine(Global.WalletsDir, $"{WalletName}.json");

                if (TermsAccepted == false)
                {
                    ValidationMessage = "Terms are not accepted.";
                }
                else if (string.IsNullOrWhiteSpace(WalletName))
                {
                    ValidationMessage = $"The name {WalletName} is not valid.";
                }
                else if (File.Exists(walletFilePath))
                {
                    ValidationMessage = $"The name {WalletName} is already taken.";
                }
                else if (Password != PasswordConfirmation)
                {
                    ValidationMessage = $"The passwords do not match.";
                }
                else
                {
                    try
                    {
                        KeyManager.CreateNew(out Mnemonic mnemonic, Password, walletFilePath);

                        owner.CurrentView = new GenerateWalletSuccessViewModel(owner, mnemonic);
                    }
                    catch (Exception ex)
                    {
                        ValidationMessage = ex.ToTypeMessageString();
                        Logger.LogError <GenerateWalletViewModel>(ex);
                    }
                }
            },
                                                     this.WhenAnyValue(x => x.TermsAccepted));
        }
Пример #13
0
        public LoadWalletViewModel(WalletManagerViewModel owner, bool requirePassword) : base(requirePassword ? "Test Password" : "Load Wallet")
        {
            Disposables = new CompositeDisposable();

            Owner           = owner;
            Password        = "";
            RequirePassword = requirePassword;
            Wallets         = new ObservableCollection <string>();

            this.WhenAnyValue(x => x.SelectedWallet)
            .Subscribe(selectedWallet => SetWalletStates()).DisposeWith(Disposables);

            this.WhenAnyValue(x => x.IsWalletOpened)
            .Subscribe(isWalletOpened => SetWalletStates()).DisposeWith(Disposables);

            this.WhenAnyValue(x => x.Password).Subscribe(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');
                            LoadKeyManager(requirePassword: true);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Logger.LogTrace(ex);
                }
            }).DisposeWith(Disposables);

            LoadCommand         = ReactiveCommand.Create(LoadWalletAsync, this.WhenAnyValue(x => x.CanLoadWallet)).DisposeWith(Disposables);
            TestPasswordCommand = ReactiveCommand.Create(() => LoadKeyManager(requirePassword: true), this.WhenAnyValue(x => x.CanTestPassword)).DisposeWith(Disposables);
            OpenFolderCommand   = ReactiveCommand.Create(OpenWalletsFolder).DisposeWith(Disposables);
            SetLoadButtonText(IsBusy);
        }
        public RecoverWalletViewModel(WalletManagerViewModel owner) : base("Recover Wallet")
        {
            MnemonicWords = "";

            RecoverCommand = ReactiveCommand.Create(() =>
            {
                WalletName    = Guard.Correct(WalletName);
                MnemonicWords = Guard.Correct(MnemonicWords);
                Password      = Guard.Correct(Password);            // Don't let whitespaces to the beginning and to the end.

                string walletFilePath = Path.Combine(Global.WalletsDir, $"{WalletName}.json");

                if (string.IsNullOrWhiteSpace(WalletName))
                {
                    ValidationMessage = $"The name {WalletName} is not valid.";
                }
                else if (File.Exists(walletFilePath))
                {
                    ValidationMessage = $"The name {WalletName} is already taken.";
                }
                else if (string.IsNullOrWhiteSpace(MnemonicWords))
                {
                    ValidationMessage = "Recovery Words were not supplied.";
                }
                else if (string.IsNullOrWhiteSpace(AccountKeyPath))
                {
                    ValidationMessage = "The account key path is not valid.";
                }
                else if (MinGapLimit < KeyManager.AbsoluteMinGapLimit)
                {
                    ValidationMessage = $"Min Gap Limit cannot be smaller than {KeyManager.AbsoluteMinGapLimit}.";
                }
                else if (MinGapLimit > 1_000_000)
                {
                    ValidationMessage = $"Min Gap Limit cannot be larger than {1_000_000}.";
                }
                else if (!KeyPath.TryParse(AccountKeyPath, out KeyPath keyPath))
                {
                    ValidationMessage = "The account key path is not a valid derivation path.";
                }
                else
                {
                    try
                    {
                        var mnemonic = new Mnemonic(MnemonicWords);
                        KeyManager.Recover(mnemonic, Password, walletFilePath, keyPath, MinGapLimit);

                        owner.SelectLoadWallet();
                    }
                    catch (Exception ex)
                    {
                        ValidationMessage = ex.ToTypeMessageString();
                        Logger.LogError <LoadWalletViewModel>(ex);
                    }
                }
            });

            this.WhenAnyValue(x => x.MnemonicWords).Subscribe(x => UpdateSuggestions(x));
            this.WhenAnyValue(x => x.Password).Subscribe(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');
                        }
                    }
                }
                catch (Exception ex)
                {
                    Logger.LogTrace(ex);
                }
            });

            this.WhenAnyValue(x => x.CaretIndex).Subscribe(_ =>
            {
                if (CaretIndex != MnemonicWords.Length)
                {
                    CaretIndex = MnemonicWords.Length;
                }
            });

            _suggestions = new ObservableCollection <SuggestionViewModel>();
        }
Пример #15
0
        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;
        }
Пример #16
0
        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();
        }
Пример #17
0
        public RecoverWalletViewModel(WalletManagerViewModel owner) : base("Recover Wallet")
        {
            Global        = owner.Global;
            MnemonicWords = "";

            RecoverCommand = ReactiveCommand.Create(() =>
            {
                WalletName    = Guard.Correct(WalletName);
                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");

                if (string.IsNullOrWhiteSpace(WalletName))
                {
                    ValidationMessage = $"The name {WalletName} is not valid.";
                }
                else if (File.Exists(walletFilePath))
                {
                    ValidationMessage = $"The name {WalletName} is already taken.";
                }
                else if (string.IsNullOrWhiteSpace(MnemonicWords))
                {
                    ValidationMessage = "Recovery Words were not supplied.";
                }
                else if (string.IsNullOrWhiteSpace(AccountKeyPath))
                {
                    ValidationMessage = "The account key path is not valid.";
                }
                else if (MinGapLimit < KeyManager.AbsoluteMinGapLimit)
                {
                    ValidationMessage = $"Min Gap Limit cannot be smaller than {KeyManager.AbsoluteMinGapLimit}.";
                }
                else if (MinGapLimit > 1_000_000)
                {
                    ValidationMessage = $"Min Gap Limit cannot be larger than {1_000_000}.";
                }
                else if (!KeyPath.TryParse(AccountKeyPath, out KeyPath keyPath))
                {
                    ValidationMessage = "The account key path is not a valid derivation path.";
                }
                else
                {
                    try
                    {
                        var mnemonic = new Mnemonic(MnemonicWords);
                        KeyManager.Recover(mnemonic, Password, walletFilePath, keyPath, MinGapLimit);

                        owner.SelectLoadWallet();
                    }
                    catch (Exception ex)
                    {
                        ValidationMessage = ex.ToTypeMessageString();
                        Logger.LogError(ex);
                    }
                }
            });

            this.WhenAnyValue(x => x.MnemonicWords).Subscribe(UpdateSuggestions);

            _suggestions = new ObservableCollection <SuggestionViewModel>();
        }
Пример #18
0
        public GenerateWalletViewModel(WalletManagerViewModel owner) : base("Generate Wallet")
        {
            Owner = owner;

            GenerateCommand = ReactiveCommand.Create(DoGenerateCommand, this.WhenAnyValue(x => x.TermsAccepted));
        }
Пример #19
0
        public RecoverWalletViewModel(WalletManagerViewModel owner) : base("Recover Wallet")
        {
            Global = Locator.Current.GetService <Global>();

            MnemonicWords = "";

            RecoverCommand = ReactiveCommand.Create(() =>
            {
                WalletName    = Guard.Correct(WalletName);
                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");

                if (string.IsNullOrWhiteSpace(WalletName))
                {
                    NotificationHelpers.Error("Invalid wallet name.");
                }
                else if (File.Exists(walletFilePath))
                {
                    NotificationHelpers.Error("Wallet name is already taken.");
                }
                else if (string.IsNullOrWhiteSpace(MnemonicWords))
                {
                    NotificationHelpers.Error("Recovery Words were not supplied.");
                }
                else if (string.IsNullOrWhiteSpace(AccountKeyPath))
                {
                    NotificationHelpers.Error("The account key path is not valid.");
                }
                else if (MinGapLimit < KeyManager.AbsoluteMinGapLimit)
                {
                    NotificationHelpers.Error($"Min Gap Limit cannot be smaller than {KeyManager.AbsoluteMinGapLimit}.");
                }
                else if (MinGapLimit > 1_000_000)
                {
                    NotificationHelpers.Error($"Min Gap Limit cannot be larger than {1_000_000}.");
                }
                else if (!KeyPath.TryParse(AccountKeyPath, out KeyPath keyPath))
                {
                    NotificationHelpers.Error("The account key path is not a valid derivation path.");
                }
                else
                {
                    try
                    {
                        var mnemonic = new Mnemonic(MnemonicWords);
                        var km       = KeyManager.Recover(mnemonic, Password, filePath: null, keyPath, MinGapLimit);
                        km.SetNetwork(Global.Network);
                        km.SetFilePath(walletFilePath);
                        km.ToFile();

                        NotificationHelpers.Success("Wallet is successfully recovered!");

                        owner.SelectLoadWallet();
                    }
                    catch (Exception ex)
                    {
                        NotificationHelpers.Error(ex.ToTypeMessageString());
                        Logger.LogError(ex);
                    }
                }
            });

            this.WhenAnyValue(x => x.MnemonicWords).Subscribe(UpdateSuggestions);

            _suggestions = new ObservableCollection <SuggestionViewModel>();

            RecoverCommand.ThrownExceptions
            .ObserveOn(RxApp.TaskpoolScheduler)
            .Subscribe(ex => Logger.LogError(ex));
        }