Пример #1
0
        private volatile bool _disposedValue = false;         // To detect redundant calls

        public AddressViewModel(HdPubKey model, KeyManager km, ReceiveTabViewModel owner)
        {
            Global                       = Locator.Current.GetService <Global>();
            KeyManager                   = km;
            Owner                        = owner;
            IsHardwareWallet             = km.IsHardwareWallet;
            Model                        = model;
            ClipboardNotificationVisible = false;
            ClipboardNotificationOpacity = 0;
            _label                       = model.Label;

            this.WhenAnyValue(x => x.IsExpanded)
            .ObserveOn(RxApp.TaskpoolScheduler)
            .Where(x => x)
            .Take(1)
            .Select(x =>
            {
                var encoder = new QrEncoder();
                encoder.TryEncode(Address, out var qrCode);
                return(qrCode.Matrix.InternalArray);
            })
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(qr => QrCode = qr, onError: ex => Logger.LogError(ex));                     // Catch the exceptions everywhere (e.g.: Select) except in Subscribe.

            Global.UiConfig
            .WhenAnyValue(x => x.LurkingWifeMode)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(_ =>
            {
                this.RaisePropertyChanged(nameof(IsLurkingWifeMode));
                this.RaisePropertyChanged(nameof(Address));
                this.RaisePropertyChanged(nameof(Label));
            }).DisposeWith(Disposables);

            this.WhenAnyValue(x => x.Label)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(newLabel =>
            {
                if (InEditMode)
                {
                    KeyManager keyManager = KeyManager;
                    HdPubKey hdPubKey     = keyManager.GetKeys(x => Model == x).FirstOrDefault();

                    if (hdPubKey != default)
                    {
                        hdPubKey.SetLabel(newLabel, kmToFile: keyManager);
                    }
                }
            });

            _expandMenuCaption = this
                                 .WhenAnyValue(x => x.IsExpanded)
                                 .Select(x => (x ? "Hide " : "Show ") + "QR Code")
                                 .ObserveOn(RxApp.MainThreadScheduler)
                                 .ToProperty(this, x => x.ExpandMenuCaption)
                                 .DisposeWith(Disposables);

            ToggleQrCode = ReactiveCommand.Create(() => IsExpanded = !IsExpanded);

            SaveQRCode = ReactiveCommand.CreateFromTask(SaveQRCodeAsync);

            CopyAddress = ReactiveCommand.CreateFromTask(TryCopyToClipboardAsync);

            CopyLabel = ReactiveCommand.CreateFromTask(async() => await Application.Current.Clipboard.SetTextAsync(Label ?? string.Empty));

            ChangeLabel = ReactiveCommand.Create(() => InEditMode = true);

            DisplayAddressOnHw = ReactiveCommand.CreateFromTask(async() =>
            {
                var client    = new HwiClient(Global.Network);
                using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60));
                try
                {
                    await client.DisplayAddressAsync(KeyManager.MasterFingerprint.Value, Model.FullKeyPath, cts.Token);
                }
                catch (HwiException)
                {
                    await PinPadViewModel.UnlockAsync();
                    await client.DisplayAddressAsync(KeyManager.MasterFingerprint.Value, Model.FullKeyPath, cts.Token);
                }
            });

            LockAddress = ReactiveCommand.CreateFromTask(async() =>
            {
                Model.SetKeyState(KeyState.Locked, km);
                owner.InitializeAddresses();

                bool isAddressCopied = await Application.Current.Clipboard.GetTextAsync() == Address;
                if (isAddressCopied)
                {
                    await Application.Current.Clipboard.ClearAsync();
                }
            });

            Observable
            .Merge(ToggleQrCode.ThrownExceptions)
            .Merge(SaveQRCode.ThrownExceptions)
            .Merge(CopyAddress.ThrownExceptions)
            .Merge(CopyLabel.ThrownExceptions)
            .Merge(ChangeLabel.ThrownExceptions)
            .Merge(DisplayAddressOnHw.ThrownExceptions)
            .Merge(LockAddress.ThrownExceptions)
            .Subscribe(ex =>
            {
                Logger.LogError(ex);
                NotificationHelpers.Error(ex.ToUserFriendlyString());
            });
        }
        public TransactionPreviewViewModel(Wallet wallet, TransactionInfo info, TransactionBroadcaster broadcaster,
                                           BuildTransactionResult transaction)
        {
            var destinationAmount = transaction.CalculateDestinationAmount().ToDecimal(MoneyUnit.BTC);

            var fee = transaction.Fee;

            var labels = "";

            if (info.Labels.Count() == 1)
            {
                labels = info.Labels.First() + " ";
            }
            else if (info.Labels.Count() > 1)
            {
                labels = string.Join(", ", info.Labels.Take(info.Labels.Count() - 1));

                labels += $" and {info.Labels.Last()} ";
            }

            BtcAmountText = $"{destinationAmount} bitcoins ";

            FiatAmountText = $"(≈{(destinationAmount * wallet.Synchronizer.UsdExchangeRate).FormattedFiat()} USD) ";

            LabelsText = labels;

            AddressText = info.Address.ToString();

            ConfirmationTimeText = "~20 minutes ";

            BtcFeeText = $"{fee.ToDecimal(MoneyUnit.Satoshi)} satoshis ";

            FiatFeeText =
                $"(≈{(fee.ToDecimal(MoneyUnit.BTC) * wallet.Synchronizer.UsdExchangeRate).FormattedFiat()} USD)";

            PercentFeeText = $"{transaction.FeePercentOfSent:F2}%";

            NextCommand = ReactiveCommand.CreateFromTask(async() =>
            {
                var dialogResult =
                    await NavigateDialog(new EnterPasswordDialogViewModel(""), NavigationTarget.DialogScreen);

                if (dialogResult.Kind == DialogResultKind.Normal)
                {
                    IsBusy = true;

                    var passwordValid = await Task.Run(
                        () => PasswordHelper.TryPassword(
                            wallet.KeyManager,
                            dialogResult.Result,
                            out string?compatibilityPasswordUsed));

                    if (passwordValid)
                    {
                        // Dequeue any coin-joining coins.
                        await wallet.ChaumianClient.DequeueAllCoinsFromMixAsync(DequeueReason.TransactionBuilding);

                        var signedTransaction = transaction.Transaction;

                        // If it's a hardware wallet and still has a private key then it's password.
                        if (wallet.KeyManager.IsHardwareWallet && !transaction.Signed)
                        {
                            try
                            {
                                var client = new HwiClient(wallet.Network);

                                using var cts   = new CancellationTokenSource(TimeSpan.FromMinutes(3));
                                PSBT?signedPsbt = null;
                                try
                                {
                                    signedPsbt = await client.SignTxAsync(
                                        wallet.KeyManager.MasterFingerprint !.Value,
                                        transaction.Psbt,
                                        cts.Token);
                                }
                                catch (HwiException ex) when(ex.ErrorCode is not HwiErrorCode.ActionCanceled)
                                {
                                    await PinPadViewModel.UnlockAsync();

                                    signedPsbt = await client.SignTxAsync(
                                        wallet.KeyManager.MasterFingerprint !.Value,
                                        transaction.Psbt,
                                        cts.Token);
                                }

                                signedTransaction = signedPsbt.ExtractSmartTransaction(transaction.Transaction);
                            }
                            catch (Exception _)
                            {
                                // probably throw something here?
                            }
                        }

                        await broadcaster.SendTransactionAsync(signedTransaction);

                        Navigate().Clear();

                        IsBusy = false;
                    }
                    else
                    {
                        IsBusy = false;
                        await ShowErrorAsync("Password was incorrect.", "Please try again.", "");
                    }
                }
            });
        }