コード例 #1
0
        public async Task <IActionResult> WalletPSBT([ModelBinder(typeof(WalletIdModelBinder))]
                                                     WalletId walletId, WalletPSBTViewModel vm)
        {
            var network = NetworkProvider.GetNetwork(walletId.CryptoCode);

            if (await vm.GetPSBT(network.NBitcoinNetwork) is PSBT psbt)
            {
                vm.Decoded = psbt.ToString();
                vm.PSBT    = psbt.ToBase64();
            }
            return(View(vm ?? new WalletPSBTViewModel()));
        }
コード例 #2
0
        public async Task FundStoreWallet(WalletId walletId, int coins = 1, decimal denomination = 1m)
        {
            GoToWalletReceive(walletId);
            Driver.FindElement(By.Id("generateButton")).Click();
            var addressStr = Driver.FindElement(By.Id("vue-address")).GetProperty("value");
            var address    = BitcoinAddress.Create(addressStr, ((BTCPayNetwork)Server.NetworkProvider.GetNetwork(walletId.CryptoCode)).NBitcoinNetwork);

            for (int i = 0; i < coins; i++)
            {
                await Server.ExplorerNode.SendToAddressAsync(address, Money.Coins(denomination));
            }
        }
コード例 #3
0
        public async Task <IActionResult> WalletPSBTReady(
            [ModelBinder(typeof(WalletIdModelBinder))]
            WalletId walletId, WalletPSBTReadyViewModel vm, string command = null)
        {
            PSBT psbt    = null;
            var  network = NetworkProvider.GetNetwork(walletId.CryptoCode);

            try
            {
                psbt = PSBT.Parse(vm.PSBT, network.NBitcoinNetwork);
                await FetchTransactionDetails(walletId, vm, network);
            }
            catch
            {
                vm.GlobalError = "Invalid PSBT";
                return(View(vm));
            }
            if (command == "broadcast")
            {
                if (!psbt.IsAllFinalized() && !psbt.TryFinalize(out var errors))
                {
                    vm.SetErrors(errors);
                    return(View(vm));
                }
                var transaction = psbt.ExtractTransaction();
                try
                {
                    var broadcastResult = await ExplorerClientProvider.GetExplorerClient(network).BroadcastAsync(transaction);

                    if (!broadcastResult.Success)
                    {
                        vm.GlobalError = $"RPC Error while broadcasting: {broadcastResult.RPCCode} {broadcastResult.RPCCodeMessage} {broadcastResult.RPCMessage}";
                        return(View(vm));
                    }
                }
                catch (Exception ex)
                {
                    vm.GlobalError = "Error while broadcasting: " + ex.Message;
                    return(View(vm));
                }
                return(await RedirectToWalletTransaction(walletId, transaction));
            }
            else if (command == "analyze-psbt")
            {
                return(RedirectToAction(nameof(WalletPSBT), new { walletId = walletId, psbt = psbt.ToBase64() }));
            }
            else
            {
                vm.GlobalError = "Unknown command";
                return(View(vm));
            }
        }
コード例 #4
0
        public async Task <IActionResult> PullPayments(
            [ModelBinder(typeof(WalletIdModelBinder))]
            WalletId walletId)
        {
            using var ctx = this._dbContextFactory.CreateContext();
            var now     = DateTimeOffset.UtcNow;
            var storeId = walletId.StoreId;
            var pps     = await ctx.PullPayments.Where(p => p.StoreId == storeId && !p.Archived)
                          .OrderByDescending(p => p.StartDate)
                          .Select(o => new
            {
                PullPayment = o,
                Awaiting    = o.Payouts
                              .Where(p => p.State == PayoutState.AwaitingPayment || p.State == PayoutState.AwaitingApproval),
                Completed = o.Payouts
                            .Where(p => p.State == PayoutState.Completed || p.State == PayoutState.InProgress)
            })
                          .ToListAsync();

            var vm = new PullPaymentsModel
            {
                HasDerivationSchemeSettings = GetDerivationSchemeSettings(walletId) != null
            };

            foreach (var o in pps)
            {
                var pp             = o.PullPayment;
                var totalCompleted = o.Completed.Where(o => o.IsInPeriod(pp, now))
                                     .Select(o => o.GetBlob(_jsonSerializerSettings).Amount).Sum();
                var totalAwaiting = o.Awaiting.Where(o => o.IsInPeriod(pp, now))
                                    .Select(o => o.GetBlob(_jsonSerializerSettings).Amount).Sum();
                var ppBlob = pp.GetBlob();
                var ni     = _currencyTable.GetCurrencyData(ppBlob.Currency, true);
                var nfi    = _currencyTable.GetNumberFormatInfo(ppBlob.Currency, true);
                var period = pp.GetPeriod(now);
                vm.PullPayments.Add(new PullPaymentsModel.PullPaymentModel()
                {
                    StartDate = pp.StartDate,
                    EndDate   = pp.EndDate,
                    Id        = pp.Id,
                    Name      = ppBlob.Name,
                    Progress  = new PullPaymentsModel.PullPaymentModel.ProgressModel()
                    {
                        CompletedPercent = (int)(totalCompleted / ppBlob.Limit * 100m),
                        AwaitingPercent  = (int)(totalAwaiting / ppBlob.Limit * 100m),
                        Awaiting         = totalAwaiting.RoundToSignificant(ni.Divisibility).ToString("C", nfi),
                        Completed        = totalCompleted.RoundToSignificant(ni.Divisibility).ToString("C", nfi),
                        Limit            = _currencyTable.DisplayFormatCurrency(ppBlob.Limit, ppBlob.Currency),
                        ResetIn          = period?.End is DateTimeOffset nr ? ZeroIfNegative(nr - now).TimeString() : null,
                        EndIn            = pp.EndDate is DateTimeOffset end ? ZeroIfNegative(end - now).TimeString() : null
                    }
                });
コード例 #5
0
        public async Task <IActionResult> WalletSend(
            [ModelBinder(typeof(WalletIdModelBinder))]
            WalletId walletId, string defaultDestination = null, string defaultAmount = null)
        {
            if (walletId?.StoreId == null)
            {
                return(NotFound());
            }
            var store = await Repository.FindStore(walletId.StoreId, GetUserId());

            DerivationStrategy paymentMethod = GetPaymentMethod(walletId, store);

            if (paymentMethod == null)
            {
                return(NotFound());
            }

            var storeData = store.GetStoreBlob();
            var rateRules = store.GetStoreBlob().GetRateRules(NetworkProvider);

            rateRules.Spread = 0.0m;
            var         currencyPair = new Rating.CurrencyPair(paymentMethod.PaymentId.CryptoCode, GetCurrencyCode(storeData.DefaultLang) ?? "USD");
            WalletModel model        = new WalletModel()
            {
                DefaultAddress = defaultDestination,
                DefaultAmount  = defaultAmount,
                ServerUrl      = GetLedgerWebsocketUrl(this.HttpContext, walletId.CryptoCode, paymentMethod.DerivationStrategyBase),
                CryptoCurrency = walletId.CryptoCode
            };

            using (CancellationTokenSource cts = new CancellationTokenSource())
            {
                try
                {
                    cts.CancelAfter(TimeSpan.FromSeconds(5));
                    var result = await RateFetcher.FetchRate(currencyPair, rateRules).WithCancellation(cts.Token);

                    if (result.BidAsk != null)
                    {
                        model.Rate         = result.BidAsk.Center;
                        model.Divisibility = _currencyTable.GetNumberFormatInfo(currencyPair.Right, true).CurrencyDecimalDigits;
                        model.Fiat         = currencyPair.Right;
                    }
                    else
                    {
                        model.RateError = $"{result.EvaluatedRule} ({string.Join(", ", result.Errors.OfType<object>().ToArray())})";
                    }
                }
                catch (Exception ex) { model.RateError = ex.Message; }
            }
            return(View(model));
        }
コード例 #6
0
        private IActionResult RedirectToWalletTransaction(WalletId walletId, Transaction transaction)
        {
            var network = NetworkProvider.GetNetwork <BTCPayNetwork>(walletId.CryptoCode);

            if (transaction != null)
            {
                var wallet             = _walletProvider.GetWallet(network);
                var derivationSettings = GetDerivationSchemeSettings(walletId);
                wallet.InvalidateCache(derivationSettings.AccountDerivation);
                TempData[WellKnownTempData.SuccessMessage] = $"Transaction broadcasted successfully ({transaction.GetHash().ToString()})";
            }
            return(RedirectToAction(nameof(WalletTransactions), new { walletId = walletId.ToString() }));
        }
コード例 #7
0
        public async Task <IActionResult> WalletSend(
            [ModelBinder(typeof(WalletIdModelBinder))]
            WalletId walletId, WalletSendModel vm)
        {
            if (walletId?.StoreId == null)
            {
                return(NotFound());
            }
            var store = await Repository.FindStore(walletId.StoreId, GetUserId());

            if (store == null)
            {
                return(NotFound());
            }
            var network = this.NetworkProvider.GetNetwork(walletId?.CryptoCode);

            if (network == null)
            {
                return(NotFound());
            }
            var destination = ParseDestination(vm.Destination, network.NBitcoinNetwork);

            if (destination == null)
            {
                ModelState.AddModelError(nameof(vm.Destination), "Invalid address");
            }

            if (vm.Amount.HasValue)
            {
                if (vm.CurrentBalance == vm.Amount.Value && !vm.SubstractFees)
                {
                    ModelState.AddModelError(nameof(vm.Amount), "You are sending all your balance to the same destination, you should substract the fees");
                }
                if (vm.CurrentBalance < vm.Amount.Value)
                {
                    ModelState.AddModelError(nameof(vm.Amount), "You are sending more than what you own");
                }
            }
            if (!ModelState.IsValid)
            {
                return(View(vm));
            }

            return(RedirectToAction(nameof(WalletSendLedger), new WalletSendLedgerModel()
            {
                Destination = vm.Destination,
                Amount = vm.Amount.Value,
                SubstractFees = vm.SubstractFees,
                FeeSatoshiPerByte = vm.FeeSatoshiPerByte
            }));
        }
コード例 #8
0
        public Mnemonic GenerateWallet(string cryptoCode = "BTC", string seed = "", bool importkeys = false, bool privkeys = false, ScriptPubKeyType format = ScriptPubKeyType.Segwit)
        {
            Driver.FindElement(By.Id($"Modify{cryptoCode}")).Click();

            // Replace previous wallet case
            if (Driver.PageSource.Contains("id=\"ChangeWalletLink\""))
            {
                Driver.FindElement(By.Id("ChangeWalletLink")).Click();
                Driver.FindElement(By.Id("continue")).Click();
            }

            if (string.IsNullOrEmpty(seed))
            {
                var option = privkeys ? "Hotwallet" : "Watchonly";
                Logs.Tester.LogInformation($"Generating new seed ({option})");
                Driver.FindElement(By.Id("GenerateWalletLink")).Click();
                Driver.FindElement(By.Id($"Generate{option}Link")).Click();
            }
            else
            {
                Logs.Tester.LogInformation("Progressing with existing seed");
                Driver.FindElement(By.Id("ImportWalletOptionsLink")).Click();
                Driver.FindElement(By.Id("ImportSeedLink")).Click();
                Driver.FindElement(By.Id("ExistingMnemonic")).SendKeys(seed);
                Driver.SetCheckbox(By.Id("SavePrivateKeys"), privkeys);
            }

            Driver.FindElement(By.Id("ScriptPubKeyType")).Click();
            Driver.FindElement(By.CssSelector($"#ScriptPubKeyType option[value={format}]")).Click();

            // Open advanced settings via JS, because if we click the link it triggers the toggle animation.
            // This leads to Selenium trying to click the button while it is moving resulting in an error.
            Driver.ExecuteJavaScript("document.getElementById('AdvancedSettings').classList.add('show')");

            Driver.SetCheckbox(By.Id("ImportKeysToRPC"), importkeys);
            Driver.FindElement(By.Id("Continue")).Click();

            // Seed backup page
            FindAlertMessage();
            if (string.IsNullOrEmpty(seed))
            {
                seed = Driver.FindElements(By.Id("RecoveryPhrase")).First().GetAttribute("data-mnemonic");
            }

            // Confirm seed backup
            Driver.FindElement(By.Id("confirm")).Click();
            Driver.FindElement(By.Id("submit")).Click();

            WalletId = new WalletId(StoreId, cryptoCode);
            return(new Mnemonic(seed));
        }
コード例 #9
0
        public async Task <IActionResult> NewPullPayment([ModelBinder(typeof(WalletIdModelBinder))]
                                                         WalletId walletId, NewPullPaymentModel model)
        {
            if (GetDerivationSchemeSettings(walletId) == null)
            {
                return(NotFound());
            }

            model.Name ??= string.Empty;
            model.Currency = model.Currency.ToUpperInvariant().Trim();
            if (_currencyTable.GetCurrencyData(model.Currency, false) is null)
            {
                ModelState.AddModelError(nameof(model.Currency), "Invalid currency");
            }
            if (model.Amount <= 0.0m)
            {
                ModelState.AddModelError(nameof(model.Amount), "The amount should be more than zero");
            }
            if (model.Name.Length > 50)
            {
                ModelState.AddModelError(nameof(model.Name), "The name should be maximum 50 characters.");
            }
            var paymentMethodId = walletId.GetPaymentMethodId();
            var n = this.NetworkProvider.GetNetwork <BTCPayNetwork>(paymentMethodId.CryptoCode);

            if (n is null || paymentMethodId.PaymentType != PaymentTypes.BTCLike || n.ReadonlyWallet)
            {
                ModelState.AddModelError(nameof(model.Name), "Pull payments are not supported with this wallet");
            }
            if (!ModelState.IsValid)
            {
                return(View(model));
            }
            await _pullPaymentService.CreatePullPayment(new HostedServices.CreatePullPayment()
            {
                Name             = model.Name,
                Amount           = model.Amount,
                Currency         = model.Currency,
                StoreId          = walletId.StoreId,
                PaymentMethodIds = new[] { paymentMethodId },
                EmbeddedCSS      = model.EmbeddedCSS,
                CustomCSSLink    = model.CustomCSSLink
            });

            this.TempData.SetStatusMessageModel(new StatusMessageModel()
            {
                Message  = "Pull payment request created",
                Severity = StatusMessageModel.StatusSeverity.Success
            });
            return(RedirectToAction(nameof(PullPayments), new { walletId = walletId.ToString() }));
        }
コード例 #10
0
        public async Task <IActionResult> WalletRescan(
            [ModelBinder(typeof(WalletIdModelBinder))]
            WalletId walletId)
        {
            if (walletId?.StoreId == null)
            {
                return(NotFound());
            }
            var store = await Repository.FindStore(walletId.StoreId, GetUserId());

            DerivationStrategy paymentMethod = GetPaymentMethod(walletId, store);

            if (paymentMethod == null)
            {
                return(NotFound());
            }

            var vm = new RescanWalletModel();

            vm.IsFullySync = _dashboard.IsFullySynched(walletId.CryptoCode, out var unused);
            // We need to ensure it is segwit,
            // because hardware wallet support need the parent transactions to sign, which NBXplorer don't have. (Nor does a pruned node)
            vm.IsSegwit              = paymentMethod.DerivationStrategyBase.IsSegwit();
            vm.IsServerAdmin         = User.Claims.Any(c => c.Type == Policies.CanModifyServerSettings.Key && c.Value == "true");
            vm.IsSupportedByCurrency = _dashboard.Get(walletId.CryptoCode)?.Status?.BitcoinStatus?.Capabilities?.CanScanTxoutSet == true;
            var explorer     = ExplorerClientProvider.GetExplorerClient(walletId.CryptoCode);
            var scanProgress = await explorer.GetScanUTXOSetInformationAsync(paymentMethod.DerivationStrategyBase);

            if (scanProgress != null)
            {
                vm.PreviousError = scanProgress.Error;
                if (scanProgress.Status == ScanUTXOStatus.Queued || scanProgress.Status == ScanUTXOStatus.Pending)
                {
                    if (scanProgress.Progress == null)
                    {
                        vm.Progress = 0;
                    }
                    else
                    {
                        vm.Progress      = scanProgress.Progress.OverallProgress;
                        vm.RemainingTime = TimeSpan.FromSeconds(scanProgress.Progress.RemainingSeconds).PrettyPrint();
                    }
                }
                if (scanProgress.Status == ScanUTXOStatus.Complete)
                {
                    vm.LastSuccess = scanProgress.Progress;
                    vm.TimeOfScan  = (scanProgress.Progress.CompletedAt.Value - scanProgress.Progress.StartedAt).PrettyPrint();
                }
            }
            return(View(vm));
        }
コード例 #11
0
 public void GoToWallet(WalletId walletId = null, WalletsNavPages navPages = WalletsNavPages.Send)
 {
     walletId ??= WalletId;
     Driver.Navigate().GoToUrl(new Uri(ServerUri, $"wallets/{walletId}"));
     if (navPages == WalletsNavPages.PSBT)
     {
         Driver.FindElement(By.Id("WalletNav-Send")).Click();
         Driver.FindElement(By.Id("PSBT")).Click();
     }
     else if (navPages != WalletsNavPages.Transactions)
     {
         Driver.FindElement(By.Id($"WalletNav-{navPages}")).Click();
     }
 }
コード例 #12
0
        public async Task <IActionResult> WalletSettings(string storeId, string cryptoCode)
        {
            var checkResult = IsAvailable(cryptoCode, out var store, out var network);

            if (checkResult != null)
            {
                return(checkResult);
            }

            var derivation = GetExistingDerivationStrategy(cryptoCode, store);

            if (derivation == null)
            {
                return(NotFound());
            }

            var storeBlob      = store.GetStoreBlob();
            var excludeFilters = storeBlob.GetExcludedPaymentMethods();

            (bool canUseHotWallet, bool rpcImport) = await CanUseHotWallet();

            var client = _ExplorerProvider.GetExplorerClient(network);

            var vm = new WalletSettingsViewModel
            {
                StoreId               = storeId,
                CryptoCode            = cryptoCode,
                WalletId              = new WalletId(storeId, cryptoCode),
                Enabled               = !excludeFilters.Match(derivation.PaymentId),
                Network               = network,
                IsHotWallet           = derivation.IsHotWallet,
                Source                = derivation.Source,
                RootFingerprint       = derivation.GetSigningAccountKeySettings().RootFingerprint.ToString(),
                DerivationScheme      = derivation.AccountDerivation.ToString(),
                DerivationSchemeInput = derivation.AccountOriginal,
                KeyPath               = derivation.GetSigningAccountKeySettings().AccountKeyPath?.ToString(),
                UriScheme             = derivation.Network.NBitcoinNetwork.UriScheme,
                Label = derivation.Label,
                SelectedSigningKey = derivation.SigningKey.ToString(),
                NBXSeedAvailable   = derivation.IsHotWallet &&
                                     canUseHotWallet &&
                                     !string.IsNullOrEmpty(await client.GetMetadataAsync <string>(derivation.AccountDerivation,
                                                                                                  WellknownMetadataKeys.MasterHDKey)),
                AccountKeys = derivation.AccountKeySettings
                              .Select(e => new WalletSettingsAccountKeyViewModel
                {
                    AccountKey        = e.AccountKey.ToString(),
                    MasterFingerprint = e.RootFingerprint is HDFingerprint fp ? fp.ToString() : null,
                    AccountKeyPath    = e.AccountKeyPath == null ? "" : $"m/{e.AccountKeyPath}"
                }).ToList(),
コード例 #13
0
        private async Task <IActionResult> RedirectToWalletTransaction(WalletId walletId, Transaction transaction)
        {
            var network = NetworkProvider.GetNetwork <BTCPayNetwork>(walletId.CryptoCode);

            if (transaction != null)
            {
                var wallet             = _walletProvider.GetWallet(network);
                var derivationSettings = await GetDerivationSchemeSettings(walletId);

                wallet.InvalidateCache(derivationSettings.AccountDerivation);
                StatusMessage = $"Transaction broadcasted successfully ({transaction.GetHash().ToString()})";
            }
            return(RedirectToAction(nameof(WalletTransactions)));
        }
コード例 #14
0
        private DerivationStrategy GetPaymentMethod(WalletId walletId, StoreData store)
        {
            if (store == null || !store.HasClaim(Policies.CanModifyStoreSettings.Key))
            {
                return(null);
            }

            var paymentMethod = store
                                .GetSupportedPaymentMethods(_NetworkProvider)
                                .OfType <DerivationStrategy>()
                                .FirstOrDefault(p => p.PaymentId.PaymentType == Payments.PaymentTypes.BTCLike && p.PaymentId.CryptoCode == walletId.CryptoCode);

            return(paymentMethod);
        }
コード例 #15
0
        public async Task <IActionResult> WalletPSBT(
            [ModelBinder(typeof(WalletIdModelBinder))]
            WalletId walletId,
            WalletPSBTViewModel vm, string command = null)
        {
            var network = NetworkProvider.GetNetwork(walletId.CryptoCode);
            var psbt    = await vm.GetPSBT(network.NBitcoinNetwork);

            if (psbt == null)
            {
                ModelState.AddModelError(nameof(vm.PSBT), "Invalid PSBT");
                return(View(vm));
            }
            switch (command)
            {
            case null:
                vm.Decoded = psbt.ToString();
                ModelState.Remove(nameof(vm.PSBT));
                ModelState.Remove(nameof(vm.FileName));
                ModelState.Remove(nameof(vm.UploadedPSBTFile));
                vm.PSBT     = psbt.ToBase64();
                vm.FileName = vm.UploadedPSBTFile?.FileName;
                return(View(vm));

            case "ledger":
                return(ViewWalletSendLedger(psbt));

            case "seed":
                return(SignWithSeed(walletId, psbt.ToBase64()));

            case "broadcast":
            {
                return(await WalletPSBTReady(walletId, psbt.ToBase64()));
            }

            case "combine":
                ModelState.Remove(nameof(vm.PSBT));
                return(View(nameof(WalletPSBTCombine), new WalletPSBTCombineViewModel()
                {
                    OtherPSBT = psbt.ToBase64()
                }));

            case "save-psbt":
                return(FilePSBT(psbt, vm.FileName));

            default:
                return(View(vm));
            }
        }
コード例 #16
0
        public async Task <IActionResult> WalletSign([ModelBinder(typeof(WalletIdModelBinder))]
                                                     WalletId walletId, WalletPSBTViewModel vm, string returnUrl = null, string command = null)
        {
            var network = NetworkProvider.GetNetwork <BTCPayNetwork>(walletId.CryptoCode);

            if (returnUrl is null)
            {
                returnUrl = Url.Action(nameof(WalletTransactions), new { walletId });
            }
            var psbt = await vm.GetPSBT(network.NBitcoinNetwork);

            if (psbt is null || vm.InvalidPSBT)
            {
                ModelState.AddModelError(nameof(vm.PSBT), "Invalid PSBT");
                return(View("WalletSigningOptions", new WalletSigningOptionsModel(vm.SigningContext, returnUrl)));
            }
            switch (command)
            {
            case "vault":
                return(ViewVault(walletId, vm.SigningContext));

            case "seed":
                return(SignWithSeed(walletId, vm.SigningContext));

            case "decode":
                return(await WalletPSBT(walletId, vm, "decode"));

            default:
                break;
            }

            if (await CanUseHotWallet())
            {
                var derivationScheme = GetDerivationSchemeSettings(walletId);
                if (derivationScheme.IsHotWallet)
                {
                    var extKey = await ExplorerClientProvider.GetExplorerClient(walletId.CryptoCode)
                                 .GetMetadataAsync <string>(derivationScheme.AccountDerivation,
                                                            WellknownMetadataKeys.MasterHDKey);

                    if (extKey != null)
                    {
                        return(await SignWithSeed(walletId,
                                                  new SignWithSeedViewModel { SeedOrKey = extKey, SigningContext = vm.SigningContext }));
                    }
                }
            }
            return(View("WalletSigningOptions", new WalletSigningOptionsModel(vm.SigningContext, returnUrl)));
        }
コード例 #17
0
        public ActionResult GenerateWalletConfirm(string storeId, string cryptoCode)
        {
            var checkResult = IsAvailable(cryptoCode, out _, out var network);

            if (checkResult != null)
            {
                return(checkResult);
            }

            TempData[WellKnownTempData.SuccessMessage] = $"Wallet settings for {network.CryptoCode} have been updated.";

            var walletId = new WalletId(storeId, cryptoCode);

            return(RedirectToAction(nameof(UIWalletsController.WalletTransactions), "UIWallets", new { walletId }));
        }
コード例 #18
0
ファイル: SeleniumTests.cs プロジェクト: safly/btcpayserver
        public async Task CanUseCoinSelection()
        {
            using (var s = SeleniumTester.Create())
            {
                await s.StartAsync();

                var userId  = s.RegisterNewUser(true);
                var storeId = s.CreateNewStore().storeId;
                s.GenerateWallet("BTC", "", false, true);
                var walletId = new WalletId(storeId, "BTC");
                s.GoToWalletReceive(walletId);
                s.Driver.FindElement(By.Id("generateButton")).Click();
                var addressStr = s.Driver.FindElement(By.Id("vue-address")).GetProperty("value");
                var address    = BitcoinAddress.Create(addressStr, ((BTCPayNetwork)s.Server.NetworkProvider.GetNetwork("BTC")).NBitcoinNetwork);
                await s.Server.ExplorerNode.GenerateAsync(1);

                for (int i = 0; i < 6; i++)
                {
                    await s.Server.ExplorerNode.SendToAddressAsync(address, Money.Coins(1.0m));
                }
                var targetTx = await s.Server.ExplorerNode.SendToAddressAsync(address, Money.Coins(1.2m));

                var tx = await s.Server.ExplorerNode.GetRawTransactionAsync(targetTx);

                var spentOutpoint = new OutPoint(targetTx, tx.Outputs.FindIndex(txout => txout.Value == Money.Coins(1.2m)));
                await s.Server.ExplorerNode.GenerateAsync(1);

                s.GoToWalletSend(walletId);
                s.Driver.FindElement(By.Id("advancedSettings")).Click();
                s.Driver.FindElement(By.Id("toggleInputSelection")).Click();
                s.Driver.WaitForElement(By.Id(spentOutpoint.ToString()));
                var el = s.Driver.FindElement(By.Id(spentOutpoint.ToString()));
                s.Driver.FindElement(By.Id(spentOutpoint.ToString())).Click();
                var bob = new Key().PubKey.Hash.GetAddress(Network.RegTest);
                SetTransactionOutput(s, 0, bob, 0.3m);
                s.Driver.FindElement(By.Id("SendMenu")).Click();
                s.Driver.FindElement(By.Id("spendWithNBxplorer")).Click();
                s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).ForceClick();
                var happyElement = s.AssertHappyMessage();
                var happyText    = happyElement.Text;
                var txid         = Regex.Match(happyText, @"\((.*)\)").Groups[1].Value;

                tx = await s.Server.ExplorerNode.GetRawTransactionAsync(new uint256(txid));

                Assert.Single(tx.Inputs);
                Assert.Equal(spentOutpoint, tx.Inputs[0].PrevOut);
            }
        }
コード例 #19
0
        public Mnemonic GenerateWallet(string cryptoCode = "BTC", string seed = "", bool importkeys = false, bool privkeys = false, ScriptPubKeyType format = ScriptPubKeyType.Segwit)
        {
            Driver.FindElement(By.Id($"Modify{cryptoCode}")).Click();

            // Replace previous wallet case
            if (Driver.PageSource.Contains("id=\"ChangeWalletLink\""))
            {
                Driver.FindElement(By.Id("ChangeWalletLink")).Click();
                Driver.FindElement(By.Id("continue")).Click();
            }

            if (string.IsNullOrEmpty(seed))
            {
                var option = privkeys ? "Hotwallet" : "Watchonly";
                Logs.Tester.LogInformation($"Generating new seed ({option})");
                Driver.FindElement(By.Id("GenerateWalletLink")).Click();
                Driver.FindElement(By.Id($"Generate{option}Link")).Click();
            }
            else
            {
                Logs.Tester.LogInformation("Progressing with existing seed");
                Driver.FindElement(By.Id("ImportWalletOptionsLink")).Click();
                Driver.FindElement(By.Id("ImportSeedLink")).Click();
                Driver.FindElement(By.Id("ExistingMnemonic")).SendKeys(seed);
                Driver.SetCheckbox(By.Id("SavePrivateKeys"), privkeys);
            }

            Driver.FindElement(By.Id("ScriptPubKeyType")).Click();
            Driver.FindElement(By.CssSelector($"#ScriptPubKeyType option[value={format}]")).Click();

            Driver.ToggleCollapse("AdvancedSettings");
            Driver.SetCheckbox(By.Id("ImportKeysToRPC"), importkeys);
            Driver.FindElement(By.Id("Continue")).Click();

            // Seed backup page
            FindAlertMessage();
            if (string.IsNullOrEmpty(seed))
            {
                seed = Driver.FindElements(By.Id("RecoveryPhrase")).First().GetAttribute("data-mnemonic");
            }

            // Confirm seed backup
            Driver.FindElement(By.Id("confirm")).Click();
            Driver.FindElement(By.Id("submit")).Click();

            WalletId = new WalletId(StoreId, cryptoCode);
            return(new Mnemonic(seed));
        }
コード例 #20
0
        public IActionResult NewPullPayment([ModelBinder(typeof(WalletIdModelBinder))]
                                            WalletId walletId)
        {
            if (GetDerivationSchemeSettings(walletId) == null)
            {
                return(NotFound());
            }

            return(View(new NewPullPaymentModel
            {
                Name = "",
                Currency = "BTC",
                CustomCSSLink = "",
                EmbeddedCSS = "",
            }));
        }
コード例 #21
0
        public async Task <WalletBlobInfo> GetWalletInfo(WalletId walletId)
        {
            if (walletId == null)
            {
                throw new ArgumentNullException(nameof(walletId));
            }
            using (var ctx = _ContextFactory.CreateContext())
            {
                var data = await ctx.Wallets
                           .Where(w => w.Id == walletId.ToString())
                           .Select(w => w)
                           .FirstOrDefaultAsync();

                return(data?.GetBlobInfo() ?? new WalletBlobInfo());
            }
        }
コード例 #22
0
        public void PayInvoice(WalletId walletId, string invoiceId)
        {
            GoToInvoiceCheckout(invoiceId);
            var bip21 = Driver.FindElement(By.ClassName("payment__details__instruction__open-wallet__btn"))
                        .GetAttribute("href");

            Assert.Contains($"{PayjoinClient.BIP21EndpointKey}", bip21);

            GoToWallet(walletId);
            Driver.FindElement(By.Id("bip21parse")).Click();
            Driver.SwitchTo().Alert().SendKeys(bip21);
            Driver.SwitchTo().Alert().Accept();
            Driver.FindElement(By.Id("SendMenu")).Click();
            Driver.FindElement(By.CssSelector("button[value=nbx-seed]")).Click();
            Driver.FindElement(By.CssSelector("button[value=broadcast]")).Click();
        }
コード例 #23
0
        public Mnemonic GenerateWallet(string cryptoCode = "BTC", string seed = "", bool importkeys = false, bool privkeys = false, ScriptPubKeyType format = ScriptPubKeyType.Segwit)
        {
            Driver.FindElement(By.Id($"Modify{cryptoCode}")).Click();
            // Modify case
            if (Driver.PageSource.Contains("id=\"change-wallet-link\""))
            {
                Driver.FindElement(By.Id("change-wallet-link")).Click();
            }

            if (string.IsNullOrEmpty(seed))
            {
                var option = privkeys ? "hotwallet" : "watchonly";
                Logs.Tester.LogInformation($"Generating new seed ({option})");
                Driver.FindElement(By.Id("generate-wallet-link")).Click();
                Driver.FindElement(By.Id($"generate-{option}-link")).Click();
            }
            else
            {
                Logs.Tester.LogInformation("Progressing with existing seed");
                Driver.FindElement(By.Id("import-wallet-options-link")).Click();
                Driver.FindElement(By.Id("import-seed-link")).Click();
                Driver.FindElement(By.Id("ExistingMnemonic")).SendKeys(seed);
                SetCheckbox(Driver.FindElement(By.Id("SavePrivateKeys")), privkeys);
            }

            Driver.FindElement(By.Id("ScriptPubKeyType")).Click();
            Driver.FindElement(By.CssSelector($"#ScriptPubKeyType option[value={format}]")).Click();
            Driver.FindElement(By.Id("advanced-settings-button")).Click();
            SetCheckbox(Driver.FindElement(By.Id("ImportKeysToRPC")), importkeys);
            Driver.FindElement(By.Id("advanced-settings-button")).Click(); // close settings again , otherwise the button might not be clickable for Selenium

            Logs.Tester.LogInformation("Trying to click Continue button");
            Driver.FindElement(By.Id("Continue")).Click();
            // Seed backup page
            FindAlertMessage();
            if (string.IsNullOrEmpty(seed))
            {
                seed = Driver.FindElements(By.Id("recovery-phrase")).First().GetAttribute("data-mnemonic");
            }

            // Confirm seed backup
            Driver.FindElement(By.Id("confirm")).Click();
            Driver.FindElement(By.Id("submit")).Click();

            WalletId = new WalletId(StoreId, cryptoCode);
            return(new Mnemonic(seed));
        }
コード例 #24
0
        public async Task <IActionResult> WalletRescan(
            [ModelBinder(typeof(WalletIdModelBinder))]
            WalletId walletId)
        {
            if (walletId?.StoreId == null)
            {
                return(NotFound());
            }
            DerivationSchemeSettings paymentMethod = GetDerivationSchemeSettings(walletId);

            if (paymentMethod == null)
            {
                return(NotFound());
            }

            var vm = new RescanWalletModel();

            vm.IsFullySync           = _dashboard.IsFullySynched(walletId.CryptoCode, out var unused);
            vm.IsServerAdmin         = (await _authorizationService.AuthorizeAsync(User, Policies.CanModifyServerSettings.Key)).Succeeded;
            vm.IsSupportedByCurrency = _dashboard.Get(walletId.CryptoCode)?.Status?.BitcoinStatus?.Capabilities?.CanScanTxoutSet == true;
            var explorer     = ExplorerClientProvider.GetExplorerClient(walletId.CryptoCode);
            var scanProgress = await explorer.GetScanUTXOSetInformationAsync(paymentMethod.AccountDerivation);

            if (scanProgress != null)
            {
                vm.PreviousError = scanProgress.Error;
                if (scanProgress.Status == ScanUTXOStatus.Queued || scanProgress.Status == ScanUTXOStatus.Pending)
                {
                    if (scanProgress.Progress == null)
                    {
                        vm.Progress = 0;
                    }
                    else
                    {
                        vm.Progress      = scanProgress.Progress.OverallProgress;
                        vm.RemainingTime = TimeSpan.FromSeconds(scanProgress.Progress.RemainingSeconds).PrettyPrint();
                    }
                }
                if (scanProgress.Status == ScanUTXOStatus.Complete)
                {
                    vm.LastSuccess = scanProgress.Progress;
                    vm.TimeOfScan  = (scanProgress.Progress.CompletedAt.Value - scanProgress.Progress.StartedAt).PrettyPrint();
                }
            }
            return(View(vm));
        }
コード例 #25
0
        public async Task <IActionResult> WalletTransactions(
            [ModelBinder(typeof(WalletIdModelBinder))]
            WalletId walletId, string labelFilter = null)
        {
            DerivationSchemeSettings paymentMethod = GetDerivationSchemeSettings(walletId);

            if (paymentMethod == null)
            {
                return(NotFound());
            }

            var wallet                      = _walletProvider.GetWallet(paymentMethod.Network);
            var walletBlobAsync             = WalletRepository.GetWalletInfo(walletId);
            var walletTransactionsInfoAsync = WalletRepository.GetWalletTransactionsInfo(walletId);
            var transactions                = await wallet.FetchTransactions(paymentMethod.AccountDerivation);

            var walletBlob             = await walletBlobAsync;
            var walletTransactionsInfo = await walletTransactionsInfoAsync;
            var model = new ListTransactionsViewModel();

            foreach (var tx in transactions.UnconfirmedTransactions.Transactions.Concat(transactions.ConfirmedTransactions.Transactions).ToArray())
            {
                var vm = new ListTransactionsViewModel.TransactionViewModel();
                vm.Id          = tx.TransactionId.ToString();
                vm.Link        = string.Format(CultureInfo.InvariantCulture, paymentMethod.Network.BlockExplorerLink, vm.Id);
                vm.Timestamp   = tx.Timestamp;
                vm.Positive    = tx.BalanceChange >= Money.Zero;
                vm.Balance     = tx.BalanceChange.ToString();
                vm.IsConfirmed = tx.Confirmations != 0;

                if (walletTransactionsInfo.TryGetValue(tx.TransactionId.ToString(), out var transactionInfo))
                {
                    var labels = walletBlob.GetLabels(transactionInfo);
                    vm.Labels.AddRange(labels);
                    model.Labels.AddRange(labels);
                    vm.Comment = transactionInfo.Comment;
                }

                if (labelFilter == null || vm.Labels.Any(l => l.Value.Equals(labelFilter, StringComparison.OrdinalIgnoreCase)))
                {
                    model.Transactions.Add(vm);
                }
            }
            model.Transactions = model.Transactions.OrderByDescending(t => t.Timestamp).ToList();
            return(View(model));
        }
コード例 #26
0
        public async Task<IActionResult> WalletPSBT([ModelBinder(typeof(WalletIdModelBinder))]
            WalletId walletId, WalletPSBTViewModel vm)
        {
            var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
            vm.CryptoCode = network.CryptoCode;
            vm.NBXSeedAvailable = await CanUseHotWallet() && !string.IsNullOrEmpty(await ExplorerClientProvider.GetExplorerClient(network)
                .GetMetadataAsync<string>(GetDerivationSchemeSettings(walletId).AccountDerivation,
                    WellknownMetadataKeys.Mnemonic));
            if (await vm.GetPSBT(network.NBitcoinNetwork) is PSBT psbt)
            {
                vm.Decoded = psbt.ToString();
                vm.PSBT = psbt.ToBase64();
                vm.PSBTHex = psbt.ToHex();
            }

            return View(nameof(WalletPSBT), vm ?? new WalletPSBTViewModel() { CryptoCode = walletId.CryptoCode });
        }
        public async Task <IActionResult> ShowOnChainWalletTransactions(
            string storeId,
            string cryptoCode,
            [FromQuery] TransactionStatus[]?statusFilter = null,
            [FromQuery] int skip  = 0,
            [FromQuery] int limit = int.MaxValue
            )
        {
            if (IsInvalidWalletRequest(cryptoCode, out var network,
                                       out var derivationScheme, out var actionResult))
            {
                return(actionResult);
            }

            var wallet   = _btcPayWalletProvider.GetWallet(network);
            var walletId = new WalletId(storeId, cryptoCode);
            var walletTransactionsInfoAsync = await _walletRepository.GetWalletTransactionsInfo(walletId);

            var txs = await wallet.FetchTransactions(derivationScheme.AccountDerivation);

            var filteredFlatList = new List <TransactionInformation>();

            if (statusFilter is null || !statusFilter.Any() || statusFilter.Contains(TransactionStatus.Confirmed))
            {
                filteredFlatList.AddRange(txs.ConfirmedTransactions.Transactions);
            }

            if (statusFilter is null || !statusFilter.Any() || statusFilter.Contains(TransactionStatus.Unconfirmed))
            {
                filteredFlatList.AddRange(txs.UnconfirmedTransactions.Transactions);
            }

            if (statusFilter is null || !statusFilter.Any() || statusFilter.Contains(TransactionStatus.Replaced))
            {
                filteredFlatList.AddRange(txs.ReplacedTransactions.Transactions);
            }

            var result = filteredFlatList.Skip(skip).Take(limit).Select(information =>
            {
                walletTransactionsInfoAsync.TryGetValue(information.TransactionId.ToString(), out var transactionInfo);
                return(ToModel(transactionInfo, information, wallet));
            }).ToList();

            return(Ok(result));
        }
コード例 #28
0
        private async Task <IActionResult> TryHandleSigningCommands(WalletId walletId, PSBT psbt, string command,
                                                                    SigningContextModel signingContext, string actionBack)
        {
            signingContext.PSBT = psbt.ToBase64();
            switch (command)
            {
            case "sign":
                var routeBack = new Dictionary <string, string>
                {
                    { "action", actionBack }, { "walletId", walletId.ToString() }
                };
                return(View("WalletSigningOptions", new WalletSigningOptionsModel(signingContext, routeBack)));

            case "vault":
                return(ViewVault(walletId, signingContext));

            case "seed":
                return(SignWithSeed(walletId, signingContext));

            case "nbx-seed":
                if (await CanUseHotWallet())
                {
                    var derivationScheme = GetDerivationSchemeSettings(walletId);
                    if (derivationScheme.IsHotWallet)
                    {
                        var extKey = await ExplorerClientProvider.GetExplorerClient(walletId.CryptoCode)
                                     .GetMetadataAsync <string>(derivationScheme.AccountDerivation,
                                                                WellknownMetadataKeys.MasterHDKey);

                        return(SignWithSeed(walletId,
                                            new SignWithSeedViewModel {
                            SeedOrKey = extKey, SigningContext = signingContext
                        }));
                    }
                }
                TempData.SetStatusMessageModel(new StatusMessageModel
                {
                    Severity = StatusMessageModel.StatusSeverity.Error,
                    Message  = "NBX seed functionality is not available"
                });
                break;
            }

            return(null);
        }
コード例 #29
0
        public static string GetImplicitStoreId(this HttpContext httpContext)
        {
            // 1. Check in the routeData
            var    routeData = httpContext.GetRouteData();
            string storeId   = null;

            if (routeData != null)
            {
                if (routeData.Values.TryGetValue("storeId", out var v))
                {
                    storeId = v as string;
                }
            }

            if (storeId == null)
            {
                if (httpContext.Request.Query.TryGetValue("storeId", out var sv))
                {
                    storeId = sv.FirstOrDefault();
                }
            }

            // 2. Check in forms
            if (storeId == null)
            {
                if (httpContext.Request.HasFormContentType &&
                    httpContext.Request.Form != null &&
                    httpContext.Request.Form.TryGetValue("storeId", out var sv))
                {
                    storeId = sv.FirstOrDefault();
                }
            }

            // 3. Checks in walletId
            if (storeId == null && routeData != null)
            {
                if (routeData.Values.TryGetValue("walletId", out var walletId) &&
                    WalletId.TryParse((string)walletId, out var w))
                {
                    storeId = w.StoreId;
                }
            }

            return(storeId);
        }
コード例 #30
0
        private IActionResult RedirectToWalletPSBT(WalletId walletId, PSBT psbt, string fileName = null)
        {
            var vm = new PostRedirectViewModel()
            {
                AspController = "Wallets",
                AspAction     = nameof(WalletPSBT),
                Parameters    =
                {
                    new KeyValuePair <string, string>("psbt", psbt.ToBase64())
                }
            };

            if (!string.IsNullOrEmpty(fileName))
            {
                vm.Parameters.Add(new KeyValuePair <string, string>("fileName", fileName));
            }
            return(View("PostRedirect", vm));
        }