public TransactionViewerViewModel() : base("Transaction")
        {
            Global = Locator.Current.GetService <Global>();

            OpenTransactionBroadcaster = ReactiveCommand.Create(() => IoC.Get <IShell>().AddOrSelectDocument(() => new TransactionBroadcasterViewModel()));
            ExportBinaryPsbt           = ReactiveCommand.CreateFromTask(async() =>
            {
                var psbtExtension = "psbt";
                var sfd           = new SaveFileDialog
                {
                    DefaultExtension = psbtExtension,
                    InitialFileName  = TxId.Substring(0, 7),
                    Title            = "Export Binary PSBT"
                };

                if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                {
                    var initialDirectory = Path.Combine("/media", Environment.UserName);
                    if (!Directory.Exists(initialDirectory))
                    {
                        initialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
                    }
                    sfd.Directory = initialDirectory;
                }
                else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
                {
                    sfd.Directory = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
                }

                var window          = (Application.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime).MainWindow;
                string fileFullName = await sfd.ShowAsync(window, fallBack: true);
                if (!string.IsNullOrWhiteSpace(fileFullName))
                {
                    var ext = Path.GetExtension(fileFullName);
                    if (string.IsNullOrWhiteSpace(ext))
                    {
                        fileFullName = $"{fileFullName}.{psbtExtension}";
                    }
                    await File.WriteAllBytesAsync(fileFullName, PsbtBytes);
                }
                NotificationHelpers.Success("PSBT file was exported.");
            });

            Observable
            .Merge(ExportBinaryPsbt.ThrownExceptions)
            .Merge(OpenTransactionBroadcaster.ThrownExceptions)
            .ObserveOn(RxApp.TaskpoolScheduler)
            .Subscribe(ex =>
            {
                Logger.LogError(ex);
                NotificationHelpers.Error(ex.ToUserFriendlyString());
            });
        }
Пример #2
0
        protected override async Task BuildTransaction(string password, PaymentIntent payments, FeeStrategy feeStrategy, bool allowUnconfirmed = false, IEnumerable <OutPoint> allowedInputs = null)
        {
            BuildTransactionResult result = await Task.Run(() => Wallet.BuildTransaction(Password, payments, feeStrategy, allowUnconfirmed: true, allowedInputs: allowedInputs));

            var txviewer = new TransactionViewerViewModel();

            IoC.Get <IShell>().AddDocument(txviewer);
            IoC.Get <IShell>().Select(txviewer);

            txviewer.Update(result);
            ResetUi();
            NotificationHelpers.Success("Transaction was built.");
        }
        public GenerateWalletSuccessViewModel(WalletManagerViewModel owner, KeyManager keyManager, Mnemonic mnemonic) : base("Wallet Generated Successfully!")
        {
            _mnemonicWords = mnemonic.ToString();

            ConfirmCommand = ReactiveCommand.Create(() =>
            {
                keyManager.ToFile();
                NotificationHelpers.Success("Wallet was generated.");
                owner.SelectTestPassword();
            });

            ConfirmCommand.ThrownExceptions
            .ObserveOn(RxApp.TaskpoolScheduler)
            .Subscribe(ex => Logger.LogError(ex));
        }
        public GenerateWalletSuccessViewModel(WalletManagerViewModel owner, KeyManager keyManager, Mnemonic mnemonic) : base("Wallet Generated Successfully!")
        {
            _mnemonicWords = mnemonic.ToString();
            var global = Locator.Current.GetService <Global>();

            ConfirmCommand = ReactiveCommand.Create(() =>
            {
                var wallet = global.WalletManager.AddWallet(keyManager);
                NotificationHelpers.Success("Wallet was generated.");
                owner.SelectTestPassword(wallet.WalletName);
            });

            ConfirmCommand.ThrownExceptions
            .ObserveOn(RxApp.TaskpoolScheduler)
            .Subscribe(ex => Logger.LogError(ex));
        }
        private void RecoverWallet(WalletManagerViewModel owner)
        {
            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 = WalletManager.WalletDirectories.GetWalletFilePaths(WalletName).walletFilePath;

            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
            {
                var minGapLimit = int.Parse(MinGapLimit);
                var keyPath     = KeyPath.Parse(AccountKeyPath);

                try
                {
                    var mnemonic = new Mnemonic(MnemonicWords);
                    var km       = KeyManager.Recover(mnemonic, Password, filePath: null, keyPath, minGapLimit);
                    km.SetNetwork(Global.Network);
                    km.SetFilePath(walletFilePath);
                    WalletManager.AddWallet(km);

                    NotificationHelpers.Success("Wallet was recovered.");

                    owner.SelectLoadWallet(km);
                }
                catch (Exception ex)
                {
                    Logger.LogError(ex);
                    NotificationHelpers.Error(ex.ToUserFriendlyString());
                }
            }
        }
Пример #6
0
        private void DoGenerateCommand()
        {
            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.

                    KeyManager.CreateNew(out Mnemonic mnemonic, Password, walletFilePath);

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

                    Owner.CurrentView = new GenerateWalletSuccessViewModel(Owner, mnemonic);
                }
                catch (Exception ex)
                {
                    NotificationHelpers.Error(ex.ToTypeMessageString());
                    Logger.LogError(ex);
                }
            }
        }
Пример #7
0
        protected override Task DoAfterBuildTransaction(BuildTransactionResult result)
        {
            try
            {
                var txviewer = new TransactionViewerViewModel();
                IoC.Get <IShell>().AddDocument(txviewer);
                IoC.Get <IShell>().Select(txviewer);

                txviewer.Update(result);

                ResetUi();

                NotificationHelpers.Success("Transaction was built.");
            }
            catch (Exception ex)
            {
                return(Task.FromException(ex));
            }
            return(Task.CompletedTask);
        }
Пример #8
0
        public GenerateWalletSuccessViewModel(WalletManagerViewModel owner, KeyManager keyManager, Mnemonic mnemonic) : base("Wallet Generated Successfully!")
        {
            _mnemonicWords = new List <string>(mnemonic.Words.Length);

            for (int i = 0; i < mnemonic.Words.Length; i++)
            {
                _mnemonicWords.Add($"{i + 1}. {mnemonic.Words[i]}");
            }

            var global = Locator.Current.GetService <Global>();

            ConfirmCommand = ReactiveCommand.Create(() =>
            {
                var wallet = global.WalletManager.AddWallet(keyManager);
                NotificationHelpers.Success("Wallet was generated.");
                owner.SelectTestPassword(wallet.WalletName);
            }, this.WhenAnyValue(x => x.IsConfirmed));

            ConfirmCommand.ThrownExceptions
            .ObserveOn(RxApp.TaskpoolScheduler)
            .Subscribe(ex => Logger.LogError(ex));
        }
Пример #9
0
        protected override Task DoAfterBuildTransaction(BuildTransactionResult result)
        {
            try
            {
                var txviewer = IoC.Get <IShell>().Documents?.OfType <TransactionViewerViewModel>()?.FirstOrDefault(x => x.Wallet.Id == Wallet.Id);
                if (txviewer is null)
                {
                    txviewer = new TransactionViewerViewModel(Wallet);
                    IoC.Get <IShell>().AddDocument(txviewer);
                }
                IoC.Get <IShell>().Select(txviewer);

                txviewer.Update(result);

                ResetUi();

                NotificationHelpers.Success("Transaction is successfully built!", "");
            }
            catch (Exception ex)
            {
                return(Task.FromException(ex));
            }
            return(Task.CompletedTask);
        }
Пример #10
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 = Global.WalletManager.WalletDirectories.GetWalletFilePaths(WalletName).walletFilePath;

                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 was recovered.");

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

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

            _suggestions = new ObservableCollection <SuggestionViewModel>();

            RecoverCommand.ThrownExceptions
            .ObserveOn(RxApp.TaskpoolScheduler)
            .Subscribe(ex => Logger.LogError(ex));
        }
Пример #11
0
        public SettingsViewModel() : base("Settings")
        {
            Global = Locator.Current.GetService <Global>();

            this.ValidateProperty(x => x.SomePrivacyLevel, ValidateSomePrivacyLevel);
            this.ValidateProperty(x => x.FinePrivacyLevel, ValidateFinePrivacyLevel);
            this.ValidateProperty(x => x.StrongPrivacyLevel, ValidateStrongPrivacyLevel);
            this.ValidateProperty(x => x.DustThreshold, ValidateDustThreshold);
            this.ValidateProperty(x => x.TorSocks5EndPoint, ValidateTorSocks5EndPoint);
            this.ValidateProperty(x => x.BitcoinP2pEndPoint, ValidateBitcoinP2pEndPoint);

            Autocopy            = Global.UiConfig.Autocopy;
            CustomFee           = Global.UiConfig.IsCustomFee;
            CustomChangeAddress = Global.UiConfig.IsCustomChangeAddress;

            var config = new Config(Global.Config.FilePath);

            config.LoadOrCreateDefaultFile();

            Network           = config.Network;
            TorSocks5EndPoint = config.TorSocks5EndPoint.ToString(-1);
            UseTor            = config.UseTor;
            StartLocalBitcoinCoreOnStartup = config.StartLocalBitcoinCoreOnStartup;
            StopLocalBitcoinCoreOnShutdown = config.StopLocalBitcoinCoreOnShutdown;

            SomePrivacyLevel   = config.PrivacyLevelSome.ToString();
            FinePrivacyLevel   = config.PrivacyLevelFine.ToString();
            StrongPrivacyLevel = config.PrivacyLevelStrong.ToString();

            DustThreshold = config.DustThreshold.ToString();

            BitcoinP2pEndPoint      = config.GetP2PEndpoint().ToString(defaultPort: -1);
            LocalBitcoinCoreDataDir = config.LocalBitcoinCoreDataDir;

            IsModified = !Global.Config.AreDeepEqual(config);

            this.WhenAnyValue(
                x => x.Network,
                x => x.UseTor,
                x => x.StartLocalBitcoinCoreOnStartup,
                x => x.StopLocalBitcoinCoreOnShutdown)
            .ObserveOn(RxApp.TaskpoolScheduler)
            .Subscribe(_ => Save());

            this.WhenAnyValue(x => x.Autocopy)
            .ObserveOn(RxApp.TaskpoolScheduler)
            .Subscribe(x => Global.UiConfig.Autocopy = x);

            this.WhenAnyValue(x => x.CustomFee)
            .ObserveOn(RxApp.TaskpoolScheduler)
            .Subscribe(x => Global.UiConfig.IsCustomFee = x);

            this.WhenAnyValue(x => x.CustomChangeAddress)
            .ObserveOn(RxApp.TaskpoolScheduler)
            .Subscribe(x => Global.UiConfig.IsCustomChangeAddress = x);

            OpenConfigFileCommand = ReactiveCommand.CreateFromTask(OpenConfigFileAsync);

            LurkingWifeModeCommand = ReactiveCommand.Create(() =>
            {
                Global.UiConfig.LurkingWifeMode = !LurkingWifeMode;
                Global.UiConfig.ToFile();
            });

            SetClearPinCommand = ReactiveCommand.Create(() =>
            {
                var pinBoxText = PinBoxText;
                if (string.IsNullOrEmpty(pinBoxText))
                {
                    NotificationHelpers.Error("Please provide a PIN.");
                    return;
                }

                var trimmedPinBoxText = pinBoxText?.Trim();
                if (string.IsNullOrEmpty(trimmedPinBoxText) ||
                    trimmedPinBoxText.Any(x => !char.IsDigit(x)))
                {
                    NotificationHelpers.Error("Invalid PIN.");
                    return;
                }

                if (trimmedPinBoxText.Length > 10)
                {
                    NotificationHelpers.Error("PIN is too long.");
                    return;
                }

                var uiConfigPinHash = Global.UiConfig.LockScreenPinHash;
                var enteredPinHash  = HashHelpers.GenerateSha256Hash(trimmedPinBoxText);

                if (IsPinSet)
                {
                    if (uiConfigPinHash != enteredPinHash)
                    {
                        NotificationHelpers.Error("PIN is incorrect.");
                        PinBoxText = "";
                        return;
                    }

                    Global.UiConfig.LockScreenPinHash = "";
                    NotificationHelpers.Success("PIN was cleared.");
                }
                else
                {
                    Global.UiConfig.LockScreenPinHash = enteredPinHash;
                    NotificationHelpers.Success("PIN was changed.");
                }

                PinBoxText = "";
            });

            TextBoxLostFocusCommand = ReactiveCommand.Create(Save);

            Observable
            .Merge(OpenConfigFileCommand.ThrownExceptions)
            .Merge(LurkingWifeModeCommand.ThrownExceptions)
            .Merge(SetClearPinCommand.ThrownExceptions)
            .Merge(TextBoxLostFocusCommand.ThrownExceptions)
            .ObserveOn(RxApp.TaskpoolScheduler)
            .Subscribe(ex => Logger.LogError(ex));
        }
Пример #12
0
        public SettingsViewModel() : base("Settings")
        {
            Global = Locator.Current.GetService <Global>();

            Autocopy  = Global.UiConfig?.Autocopy is true;
            CustomFee = Global.UiConfig?.IsCustomFee is true;

            // Use global config's data as default filler until the real data is filled out by the loading of the config onopen.
            var globalConfig = Global.Config;

            Network           = globalConfig.Network;
            TorSocks5EndPoint = globalConfig.TorSocks5EndPoint.ToString(-1);
            UseTor            = globalConfig.UseTor;
            StartLocalBitcoinCoreOnStartup = globalConfig.StartLocalBitcoinCoreOnStartup;
            StopLocalBitcoinCoreOnShutdown = globalConfig.StopLocalBitcoinCoreOnShutdown;
            SomePrivacyLevel        = globalConfig.PrivacyLevelSome.ToString();
            FinePrivacyLevel        = globalConfig.PrivacyLevelFine.ToString();
            StrongPrivacyLevel      = globalConfig.PrivacyLevelStrong.ToString();
            DustThreshold           = globalConfig.DustThreshold.ToString();
            BitcoinP2pEndPoint      = globalConfig.GetP2PEndpoint().ToString(defaultPort: -1);
            LocalBitcoinCoreDataDir = globalConfig.LocalBitcoinCoreDataDir;
            IsModified = false;

            this.WhenAnyValue(
                x => x.Network,
                x => x.UseTor,
                x => x.StartLocalBitcoinCoreOnStartup,
                x => x.StopLocalBitcoinCoreOnShutdown)
            .ObserveOn(RxApp.TaskpoolScheduler)
            .Subscribe(_ => Save());

            this.WhenAnyValue(x => x.Autocopy)
            .ObserveOn(RxApp.TaskpoolScheduler)
            .Subscribe(x => Global.UiConfig.Autocopy = x);

            this.WhenAnyValue(x => x.CustomFee)
            .ObserveOn(RxApp.TaskpoolScheduler)
            .Subscribe(x => Global.UiConfig.IsCustomFee = x);

            OpenConfigFileCommand = ReactiveCommand.CreateFromTask(OpenConfigFileAsync);

            LurkingWifeModeCommand = ReactiveCommand.CreateFromTask(async() =>
            {
                Global.UiConfig.LurkingWifeMode = !LurkingWifeMode;
                await Global.UiConfig.ToFileAsync();
            });

            SetClearPinCommand = ReactiveCommand.Create(() =>
            {
                var pinBoxText = PinBoxText;
                if (string.IsNullOrEmpty(pinBoxText))
                {
                    NotificationHelpers.Error("Please provide a PIN.");
                    return;
                }

                var trimmedPinBoxText = pinBoxText?.Trim();
                if (string.IsNullOrEmpty(trimmedPinBoxText) ||
                    trimmedPinBoxText.Any(x => !char.IsDigit(x)))
                {
                    NotificationHelpers.Error("Invalid PIN.");
                    return;
                }

                if (trimmedPinBoxText.Length > 10)
                {
                    NotificationHelpers.Error("PIN is too long.");
                    return;
                }

                var uiConfigPinHash = Global.UiConfig.LockScreenPinHash;
                var enteredPinHash  = HashHelpers.GenerateSha256Hash(trimmedPinBoxText);

                if (IsPinSet)
                {
                    if (uiConfigPinHash != enteredPinHash)
                    {
                        NotificationHelpers.Error("PIN is incorrect.");
                        PinBoxText = string.Empty;
                        return;
                    }

                    Global.UiConfig.LockScreenPinHash = string.Empty;
                    NotificationHelpers.Success("PIN cleared successfully.");
                }
                else
                {
                    Global.UiConfig.LockScreenPinHash = enteredPinHash;
                    NotificationHelpers.Success("PIN changed successfully.");
                }

                PinBoxText = string.Empty;
            });

            TextBoxLostFocusCommand = ReactiveCommand.Create(Save);

            Observable
            .Merge(OpenConfigFileCommand.ThrownExceptions)
            .Merge(LurkingWifeModeCommand.ThrownExceptions)
            .Merge(SetClearPinCommand.ThrownExceptions)
            .Merge(TextBoxLostFocusCommand.ThrownExceptions)
            .ObserveOn(RxApp.TaskpoolScheduler)
            .Subscribe(ex => Logger.LogError(ex));
        }
Пример #13
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))
                {
                    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);
                        KeyManager.Recover(mnemonic, Password, walletFilePath, keyPath, MinGapLimit);

                        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>();
        }