From 26aac6c45dc1c7f2c3fe25f78954036e6851512f Mon Sep 17 00:00:00 2001 From: Kukks Date: Sun, 9 Aug 2020 14:43:13 +0200 Subject: [PATCH] Do not crash UI and background services after switching to BItcoin Only when you have altcoin payments --- .../Controllers/InvoiceController.UI.cs | 11 ++++--- BTCPayServer/Data/StoreDataExtensions.cs | 8 +++-- BTCPayServer/Extensions.cs | 5 +-- .../TransactionLabelMarkerHostedService.cs | 2 +- .../AppViewModels/ViewCrowdfundViewModel.cs | 2 +- .../PaymentRequest/PaymentRequestHub.cs | 2 +- .../PaymentRequest/PaymentRequestService.cs | 12 +++++-- .../Payments/Bitcoin/NBXplorerListener.cs | 2 +- BTCPayServer/Payments/PaymentMethodId.cs | 1 + BTCPayServer/Payments/PaymentTypes.Bitcoin.cs | 2 +- .../Payments/PaymentTypes.Lightning.cs | 2 +- BTCPayServer/Services/Apps/AppHubStreamer.cs | 2 +- BTCPayServer/Services/Apps/AppService.cs | 10 +++--- .../Services/Invoices/InvoiceEntity.cs | 33 ++++++++++++++++--- .../Services/Invoices/InvoiceRepository.cs | 2 +- .../ListInvoicesPaymentsPartial.cshtml | 2 +- .../Shared/ViewBitcoinLikePaymentData.cshtml | 8 +++-- .../ViewLightningLikePaymentData.cshtml | 8 +++-- 18 files changed, 79 insertions(+), 35 deletions(-) diff --git a/BTCPayServer/Controllers/InvoiceController.UI.cs b/BTCPayServer/Controllers/InvoiceController.UI.cs index 802b47cac..e20516223 100644 --- a/BTCPayServer/Controllers/InvoiceController.UI.cs +++ b/BTCPayServer/Controllers/InvoiceController.UI.cs @@ -135,7 +135,7 @@ namespace BTCPayServer.Controllers else { var paymentMethods = invoice.GetBlob(_NetworkProvider).GetPaymentMethods(); - var options = invoice.GetBlob(_NetworkProvider).GetPaymentMethods() + var options = paymentMethods .Select(o => o.GetId()) .Select(o => o.CryptoCode) .Where(o => _NetworkProvider.GetNetwork(o) is BTCPayNetwork n && !n.ReadonlyWallet) @@ -143,14 +143,15 @@ namespace BTCPayServer.Controllers .OrderBy(o => o) .Select(o => new PaymentMethodId(o, PaymentTypes.BTCLike)) .ToList(); - var defaultRefund = invoice.Payments.Select(p => p.GetBlob(_NetworkProvider)) - .Select(p => p.GetPaymentMethodId().CryptoCode) - .FirstOrDefault(); + var defaultRefund = invoice.Payments + .Select(p => p.GetBlob(_NetworkProvider)) + .Select(p => p.GetPaymentMethodId()) + .FirstOrDefault(p => p != null && p.PaymentType == BitcoinPaymentType.Instance); // TODO: What if no option? var refund = new RefundModel(); refund.Title = "Select a payment method"; refund.AvailablePaymentMethods = new SelectList(options, nameof(PaymentMethodId.CryptoCode), nameof(PaymentMethodId.CryptoCode)); - refund.SelectedPaymentMethod = defaultRefund ?? options.Select(o => o.CryptoCode).First(); + refund.SelectedPaymentMethod = defaultRefund?.ToString() ?? options.Select(o => o.CryptoCode).First(); // Nothing to select, skip to next if (refund.AvailablePaymentMethods.Count() == 1) diff --git a/BTCPayServer/Data/StoreDataExtensions.cs b/BTCPayServer/Data/StoreDataExtensions.cs index 20f67fe1f..c2cde2fe2 100644 --- a/BTCPayServer/Data/StoreDataExtensions.cs +++ b/BTCPayServer/Data/StoreDataExtensions.cs @@ -15,8 +15,7 @@ namespace BTCPayServer.Data public static PaymentMethodId GetDefaultPaymentId(this StoreData storeData, BTCPayNetworkProvider networks) { PaymentMethodId[] paymentMethodIds = storeData.GetEnabledPaymentIds(networks); - - var defaultPaymentId = string.IsNullOrEmpty(storeData.DefaultCrypto) ? null : PaymentMethodId.Parse(storeData.DefaultCrypto); + PaymentMethodId.TryParse(storeData.DefaultCrypto, out var defaultPaymentId); var chosen = paymentMethodIds.FirstOrDefault(f => f == defaultPaymentId) ?? paymentMethodIds.FirstOrDefault(f => f.CryptoCode == defaultPaymentId?.CryptoCode) ?? paymentMethodIds.FirstOrDefault(); @@ -80,7 +79,10 @@ namespace BTCPayServer.Data JObject strategies = JObject.Parse(storeData.DerivationStrategies); foreach (var strat in strategies.Properties()) { - var paymentMethodId = PaymentMethodId.Parse(strat.Name); + if (!PaymentMethodId.TryParse(strat.Name, out var paymentMethodId)) + { + continue; + } var network = networks.GetNetwork(paymentMethodId.CryptoCode); if (network != null) { diff --git a/BTCPayServer/Extensions.cs b/BTCPayServer/Extensions.cs index eb4ad0f44..f98679aab 100644 --- a/BTCPayServer/Extensions.cs +++ b/BTCPayServer/Extensions.cs @@ -117,8 +117,9 @@ namespace BTCPayServer public static IEnumerable GetAllBitcoinPaymentData(this InvoiceEntity invoice) { return invoice.GetPayments() - .Where(p => p.GetPaymentMethodId().PaymentType == PaymentTypes.BTCLike) - .Select(p => (BitcoinLikePaymentData)p.GetCryptoPaymentData()); + .Where(p => p.GetPaymentMethodId()?.PaymentType == PaymentTypes.BTCLike) + .Select(p => (BitcoinLikePaymentData)p.GetCryptoPaymentData()) + .Where(data => data != null); } public static async Task> GetTransactions(this BTCPayWallet client, uint256[] hashes, bool includeOffchain = false, CancellationToken cts = default(CancellationToken)) diff --git a/BTCPayServer/HostedServices/TransactionLabelMarkerHostedService.cs b/BTCPayServer/HostedServices/TransactionLabelMarkerHostedService.cs index 53ef9d782..f5b9cfea0 100644 --- a/BTCPayServer/HostedServices/TransactionLabelMarkerHostedService.cs +++ b/BTCPayServer/HostedServices/TransactionLabelMarkerHostedService.cs @@ -33,7 +33,7 @@ namespace BTCPayServer.HostedServices protected override async Task ProcessEvent(object evt, CancellationToken cancellationToken) { if (evt is InvoiceEvent invoiceEvent && invoiceEvent.Name == InvoiceEvent.ReceivedPayment && - invoiceEvent.Payment.GetPaymentMethodId().PaymentType == BitcoinPaymentType.Instance && + invoiceEvent.Payment.GetPaymentMethodId()?.PaymentType == BitcoinPaymentType.Instance && invoiceEvent.Payment.GetCryptoPaymentData() is BitcoinLikePaymentData bitcoinLikePaymentData) { var walletId = new WalletId(invoiceEvent.Invoice.StoreId, invoiceEvent.Payment.GetCryptoCode()); diff --git a/BTCPayServer/Models/AppViewModels/ViewCrowdfundViewModel.cs b/BTCPayServer/Models/AppViewModels/ViewCrowdfundViewModel.cs index 545e8dc90..d0b3d89c5 100644 --- a/BTCPayServer/Models/AppViewModels/ViewCrowdfundViewModel.cs +++ b/BTCPayServer/Models/AppViewModels/ViewCrowdfundViewModel.cs @@ -55,7 +55,7 @@ namespace BTCPayServer.Models.AppViewModels } public class Contribution { - public PaymentMethodId PaymentMehtodId { get; set; } + public PaymentMethodId PaymentMethodId { get; set; } public decimal Value { get; set; } public decimal CurrencyValue { get; set; } } diff --git a/BTCPayServer/PaymentRequest/PaymentRequestHub.cs b/BTCPayServer/PaymentRequest/PaymentRequestHub.cs index 3f9093160..66ab2ad6c 100644 --- a/BTCPayServer/PaymentRequest/PaymentRequestHub.cs +++ b/BTCPayServer/PaymentRequest/PaymentRequestHub.cs @@ -159,7 +159,7 @@ namespace BTCPayServer.PaymentRequest { data.GetValue(), invoiceEvent.Payment.GetCryptoCode(), - invoiceEvent.Payment.GetPaymentMethodId().PaymentType.ToString() + invoiceEvent.Payment.GetPaymentMethodId()?.PaymentType?.ToString() }); } diff --git a/BTCPayServer/PaymentRequest/PaymentRequestService.cs b/BTCPayServer/PaymentRequest/PaymentRequestService.cs index 72f873a6c..da6f7cbaf 100644 --- a/BTCPayServer/PaymentRequest/PaymentRequestService.cs +++ b/BTCPayServer/PaymentRequest/PaymentRequestService.cs @@ -104,10 +104,16 @@ namespace BTCPayServer.PaymentRequest Currency = entity.ProductInformation.Currency, ExpiryDate = entity.ExpirationTime.DateTime, Status = entity.GetInvoiceState().ToString(), - Payments = entity.GetPayments().Select(paymentEntity => + Payments = entity + .GetPayments() + .Select(paymentEntity => { var paymentData = paymentEntity.GetCryptoPaymentData(); var paymentMethodId = paymentEntity.GetPaymentMethodId(); + if (paymentData is null || paymentMethodId is null) + { + return null; + } string txId = paymentData.GetPaymentId(); string link = GetTransactionLink(paymentMethodId, txId); @@ -118,7 +124,9 @@ namespace BTCPayServer.PaymentRequest Link = link, Id = txId }; - }).ToList() + }) + .Where(payment => payment != null) + .ToList() }).ToList() }; } diff --git a/BTCPayServer/Payments/Bitcoin/NBXplorerListener.cs b/BTCPayServer/Payments/Bitcoin/NBXplorerListener.cs index 6f6e470d0..243ab0f55 100644 --- a/BTCPayServer/Payments/Bitcoin/NBXplorerListener.cs +++ b/BTCPayServer/Payments/Bitcoin/NBXplorerListener.cs @@ -222,7 +222,7 @@ namespace BTCPayServer.Payments.Bitcoin var paymentEntitiesByPrevOut = new Dictionary(); foreach (var payment in invoice.GetPayments(wallet.Network)) { - if (payment.GetPaymentMethodId().PaymentType != PaymentTypes.BTCLike) + if (payment.GetPaymentMethodId()?.PaymentType != PaymentTypes.BTCLike) continue; var paymentData = (BitcoinLikePaymentData)payment.GetCryptoPaymentData(); if (!transactions.TryGetValue(paymentData.Outpoint.Hash, out TransactionResult tx)) diff --git a/BTCPayServer/Payments/PaymentMethodId.cs b/BTCPayServer/Payments/PaymentMethodId.cs index e136f9b59..77a047af5 100644 --- a/BTCPayServer/Payments/PaymentMethodId.cs +++ b/BTCPayServer/Payments/PaymentMethodId.cs @@ -72,6 +72,7 @@ namespace BTCPayServer.Payments public static bool TryParse(string str, out PaymentMethodId paymentMethodId) { + str ??= ""; paymentMethodId = null; var parts = str.Split('_', StringSplitOptions.RemoveEmptyEntries); if (parts.Length == 0 || parts.Length > 2) diff --git a/BTCPayServer/Payments/PaymentTypes.Bitcoin.cs b/BTCPayServer/Payments/PaymentTypes.Bitcoin.cs index 384b02a0e..a6fe91f54 100644 --- a/BTCPayServer/Payments/PaymentTypes.Bitcoin.cs +++ b/BTCPayServer/Payments/PaymentTypes.Bitcoin.cs @@ -22,7 +22,7 @@ namespace BTCPayServer.Payments public override CryptoPaymentData DeserializePaymentData(BTCPayNetworkBase network, string str) { - return ((BTCPayNetwork)network).ToObject(str); + return ((BTCPayNetwork)network)?.ToObject(str); } public override string SerializePaymentData(BTCPayNetworkBase network, CryptoPaymentData paymentData) diff --git a/BTCPayServer/Payments/PaymentTypes.Lightning.cs b/BTCPayServer/Payments/PaymentTypes.Lightning.cs index 0e6b4044a..9236d39f7 100644 --- a/BTCPayServer/Payments/PaymentTypes.Lightning.cs +++ b/BTCPayServer/Payments/PaymentTypes.Lightning.cs @@ -20,7 +20,7 @@ namespace BTCPayServer.Payments public override CryptoPaymentData DeserializePaymentData(BTCPayNetworkBase network, string str) { - return ((BTCPayNetwork)network).ToObject(str); + return ((BTCPayNetwork)network)?.ToObject(str); } public override string SerializePaymentData(BTCPayNetworkBase network, CryptoPaymentData paymentData) diff --git a/BTCPayServer/Services/Apps/AppHubStreamer.cs b/BTCPayServer/Services/Apps/AppHubStreamer.cs index e27e953f0..ee43a8255 100644 --- a/BTCPayServer/Services/Apps/AppHubStreamer.cs +++ b/BTCPayServer/Services/Apps/AppHubStreamer.cs @@ -39,7 +39,7 @@ namespace BTCPayServer.Services.Apps { data.GetValue(), invoiceEvent.Payment.GetCryptoCode(), - invoiceEvent.Payment.GetPaymentMethodId().PaymentType.ToString() + invoiceEvent.Payment.GetPaymentMethodId()?.PaymentType?.ToString() }, cancellationToken); } await InfoUpdated(appId); diff --git a/BTCPayServer/Services/Apps/AppService.cs b/BTCPayServer/Services/Apps/AppService.cs index 264cf7d7c..2a1f92abd 100644 --- a/BTCPayServer/Services/Apps/AppService.cs +++ b/BTCPayServer/Services/Apps/AppService.cs @@ -333,7 +333,7 @@ namespace BTCPayServer.Services.Apps .SelectMany(p => { var contribution = new Contribution(); - contribution.PaymentMehtodId = new PaymentMethodId(p.ProductInformation.Currency, PaymentTypes.BTCLike); + contribution.PaymentMethodId = new PaymentMethodId(p.ProductInformation.Currency, PaymentTypes.BTCLike); contribution.CurrencyValue = p.ProductInformation.Price; contribution.Value = contribution.CurrencyValue; @@ -363,18 +363,18 @@ namespace BTCPayServer.Services.Apps .Select(pay => { var paymentMethodContribution = new Contribution(); - paymentMethodContribution.PaymentMehtodId = pay.GetPaymentMethodId(); + paymentMethodContribution.PaymentMethodId = pay.GetPaymentMethodId(); paymentMethodContribution.Value = pay.GetCryptoPaymentData().GetValue() - pay.NetworkFee; - var rate = p.GetPaymentMethod(paymentMethodContribution.PaymentMehtodId).Rate; + var rate = p.GetPaymentMethod(paymentMethodContribution.PaymentMethodId).Rate; paymentMethodContribution.CurrencyValue = rate * paymentMethodContribution.Value; return paymentMethodContribution; }) .ToArray(); }) - .GroupBy(p => p.PaymentMehtodId) + .GroupBy(p => p.PaymentMethodId) .ToDictionary(p => p.Key, p => new Contribution() { - PaymentMehtodId = p.Key, + PaymentMethodId = p.Key, Value = p.Select(v => v.Value).Sum(), CurrencyValue = p.Select(v => v.CurrencyValue).Sum() }); diff --git a/BTCPayServer/Services/Invoices/InvoiceEntity.cs b/BTCPayServer/Services/Invoices/InvoiceEntity.cs index 3d5106d51..30948bc94 100644 --- a/BTCPayServer/Services/Invoices/InvoiceEntity.cs +++ b/BTCPayServer/Services/Invoices/InvoiceEntity.cs @@ -267,11 +267,11 @@ namespace BTCPayServer.Services.Invoices #pragma warning disable CS0618 public List GetPayments() { - return Payments?.ToList() ?? new List(); + return Payments?.Where(entity => entity.GetPaymentMethodId() != null).ToList() ?? new List(); } public List GetPayments(string cryptoCode) { - return Payments.Where(p => p.CryptoCode == cryptoCode).ToList(); + return GetPayments().Where(p => p.CryptoCode == cryptoCode).ToList(); } public List GetPayments(BTCPayNetworkBase network) { @@ -551,7 +551,10 @@ namespace BTCPayServer.Services.Invoices foreach (var prop in PaymentMethod.Properties()) { var r = serializer.ToObject(prop.Value.ToString()); - var paymentMethodId = PaymentMethodId.Parse(prop.Name); + if (!PaymentMethodId.TryParse(prop.Name, out var paymentMethodId)) + { + continue; + } r.CryptoCode = paymentMethodId.CryptoCode; r.PaymentType = paymentMethodId.PaymentType.ToString(); r.ParentEntity = this; @@ -1006,7 +1009,18 @@ namespace BTCPayServer.Services.Invoices } else { - paymentData = GetPaymentMethodId().PaymentType.DeserializePaymentData(Network, CryptoPaymentData); + var paymentMethodId = GetPaymentMethodId(); + if (paymentMethodId is null) + { + return null; + } + + paymentData = paymentMethodId.PaymentType.DeserializePaymentData(Network, CryptoPaymentData); + if (paymentData is null) + { + return null; + } + paymentData.Network = Network; if (paymentData is BitcoinLikePaymentData bitcoin) { @@ -1051,7 +1065,16 @@ namespace BTCPayServer.Services.Invoices public PaymentMethodId GetPaymentMethodId() { #pragma warning disable CS0618 // Type or member is obsolete - return new PaymentMethodId(CryptoCode ?? "BTC", string.IsNullOrEmpty(CryptoPaymentDataType) ? PaymentTypes.BTCLike : PaymentTypes.Parse(CryptoPaymentDataType)); + PaymentType paymentType; + if (string.IsNullOrEmpty(CryptoPaymentDataType)) + { + paymentType = BitcoinPaymentType.Instance;; + } + else if(!PaymentTypes.TryParse(CryptoPaymentDataType, out paymentType)) + { + return null; + } + return new PaymentMethodId(CryptoCode ?? "BTC", paymentType); #pragma warning restore CS0618 // Type or member is obsolete } diff --git a/BTCPayServer/Services/Invoices/InvoiceRepository.cs b/BTCPayServer/Services/Invoices/InvoiceRepository.cs index 15aedefcd..59d70116f 100644 --- a/BTCPayServer/Services/Invoices/InvoiceRepository.cs +++ b/BTCPayServer/Services/Invoices/InvoiceRepository.cs @@ -701,7 +701,7 @@ retry: Accounted = accounted }; - context.Payments.Add(data); + await context.Payments.AddAsync(data); try { diff --git a/BTCPayServer/Views/Invoice/ListInvoicesPaymentsPartial.cshtml b/BTCPayServer/Views/Invoice/ListInvoicesPaymentsPartial.cshtml index 56dc56463..4919520d1 100644 --- a/BTCPayServer/Views/Invoice/ListInvoicesPaymentsPartial.cshtml +++ b/BTCPayServer/Views/Invoice/ListInvoicesPaymentsPartial.cshtml @@ -47,7 +47,7 @@ @{ - var grouped = invoice.Payments.GroupBy(payment => payment.GetPaymentMethodId().PaymentType); + var grouped = invoice.Payments.GroupBy(payment => payment.GetPaymentMethodId()?.PaymentType).Where(entities => entities.Key!= null); } @foreach (var paymentGroup in grouped) { diff --git a/BTCPayServer/Views/Shared/ViewBitcoinLikePaymentData.cshtml b/BTCPayServer/Views/Shared/ViewBitcoinLikePaymentData.cshtml index 0618eb7d9..b4230f815 100644 --- a/BTCPayServer/Views/Shared/ViewBitcoinLikePaymentData.cshtml +++ b/BTCPayServer/Views/Shared/ViewBitcoinLikePaymentData.cshtml @@ -5,10 +5,14 @@ @{ PayjoinInformation payjoinIformation = null; - var onchainPayments = Model.Where(entity => entity.GetPaymentMethodId().PaymentType == BitcoinPaymentType.Instance).Select(payment => + var onchainPayments = Model.Where(entity => entity.GetPaymentMethodId()?.PaymentType == BitcoinPaymentType.Instance).Select(payment => { var m = new OnchainPaymentViewModel(); var onChainPaymentData = payment.GetCryptoPaymentData() as BitcoinLikePaymentData; + if (onChainPaymentData is null) + { + return null; + } m.Crypto = payment.GetPaymentMethodId().CryptoCode; m.DepositAddress = onChainPaymentData.GetDestination(); @@ -38,7 +42,7 @@ m.Replaced = !payment.Accounted; m.CryptoPaymentData = onChainPaymentData; return m; - }); + }).Where(model => model != null); } @if (onchainPayments.Any()) diff --git a/BTCPayServer/Views/Shared/ViewLightningLikePaymentData.cshtml b/BTCPayServer/Views/Shared/ViewLightningLikePaymentData.cshtml index d59a1d277..3f2234b3a 100644 --- a/BTCPayServer/Views/Shared/ViewLightningLikePaymentData.cshtml +++ b/BTCPayServer/Views/Shared/ViewLightningLikePaymentData.cshtml @@ -3,15 +3,19 @@ @model IEnumerable @{ - var offchainPayments = Model.Where(entity => entity.GetPaymentMethodId().PaymentType == LightningPaymentType.Instance).Select(payment => + var offchainPayments = Model.Where(entity => entity.GetPaymentMethodId()?.PaymentType == LightningPaymentType.Instance).Select(payment => { var offChainPaymentData = payment.GetCryptoPaymentData() as LightningLikePaymentData; + if (offChainPaymentData is null) + { + return null; + } return new OffChainPaymentViewModel() { Crypto = payment.Network.CryptoCode, BOLT11 = offChainPaymentData.BOLT11 }; - }); + }).Where(model => model != null); }