diff --git a/BTCPayServer/Payments/Lightning/LightningListener.cs b/BTCPayServer/Payments/Lightning/LightningListener.cs index 4977e36ae..a447ed577 100644 --- a/BTCPayServer/Payments/Lightning/LightningListener.cs +++ b/BTCPayServer/Payments/Lightning/LightningListener.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -38,7 +39,7 @@ namespace BTCPayServer.Payments.Lightning private readonly StoreRepository _storeRepository; private readonly PaymentService _paymentService; readonly Channel _CheckInvoices = Channel.CreateUnbounded(); - Task _CheckingInvoice; + Task? _CheckingInvoice; readonly Dictionary<(string, string), LightningInstanceListener> _InstanceListeners = new Dictionary<(string, string), LightningInstanceListener>(); public LightningListener(EventAggregator aggregator, @@ -87,12 +88,15 @@ namespace BTCPayServer.Payments.Lightning var invoice = await GetInvoice(invoiceId); foreach (var listenedInvoice in GetListenedInvoices(invoice)) { - var instanceListenerKey = (listenedInvoice.Network.CryptoCode, GetLightningUrl(listenedInvoice.SupportedPaymentMethod).ToString()); + var connStr = GetLightningUrl(listenedInvoice.SupportedPaymentMethod); + if (connStr is null) + continue; + var instanceListenerKey = (listenedInvoice.Network.CryptoCode, connStr.ToString()); lock (_InstanceListeners) { if (!_InstanceListeners.TryGetValue(instanceListenerKey, out var instanceListener)) { - instanceListener ??= new LightningInstanceListener(_InvoiceRepository, _Aggregator, lightningClientFactory, listenedInvoice.Network, GetLightningUrl(listenedInvoice.SupportedPaymentMethod), _paymentService, Logs); + instanceListener ??= new LightningInstanceListener(_InvoiceRepository, _Aggregator, lightningClientFactory, listenedInvoice.Network, connStr, _paymentService, Logs); _InstanceListeners.TryAdd(instanceListenerKey, instanceListener); } instanceListener.AddListenedInvoice(listenedInvoice); @@ -143,7 +147,7 @@ namespace BTCPayServer.Payments.Lightning .Where(c => new[] { PaymentTypes.LightningLike, LNURLPayPaymentType.Instance }.Contains(c.GetId().PaymentType))) { LightningLikePaymentMethodDetails lightningMethod; - LightningSupportedPaymentMethod lightningSupportedMethod; + LightningSupportedPaymentMethod? lightningSupportedMethod; switch (paymentMethod.GetPaymentMethodDetails()) { case LNURLPayPaymentMethodDetails lnurlPayPaymentMethodDetails: @@ -167,16 +171,17 @@ namespace BTCPayServer.Payments.Lightning continue; var network = _NetworkProvider.GetNetwork(paymentMethod.GetId().CryptoCode); - listenedInvoices.Add(new ListenedInvoice() - { - Expiration = invoice.ExpirationTime, - Uri = GetLightningUrl(lightningSupportedMethod).BaseUri.AbsoluteUri, - PaymentMethodDetails = lightningMethod, - SupportedPaymentMethod = lightningSupportedMethod, - PaymentMethod = paymentMethod, - Network = network, - InvoiceId = invoice.Id - }); + var lnUri = GetLightningUrl(lightningSupportedMethod); + if (lnUri == null) + continue; + listenedInvoices.Add(new ListenedInvoice( + lnUri.BaseUri, + invoice.ExpirationTime, + lightningMethod, + lightningSupportedMethod, + paymentMethod, + network, + invoice.Id)); } return listenedInvoices; } @@ -262,6 +267,8 @@ namespace BTCPayServer.Payments.Lightning .Where(method => new[] { PaymentTypes.LightningLike, LNURLPayPaymentType.Instance }.Contains(method.GetId().PaymentType)) .ToArray(); var store = await _storeRepository.FindStore(invoice.StoreId); + if (store is null) + return; if (paymentMethods.Any()) { var logs = new InvoiceLogs(); @@ -334,9 +341,11 @@ namespace BTCPayServer.Payments.Lightning .CreatePaymentMethodDetails(logs, supportedMethod, paymentMethod, store, paymentMethod.Network, prepObj, pmis)); - var instanceListenerKey = (paymentMethod.Network.CryptoCode, - GetLightningUrl(supportedMethod).ToString()); - LightningInstanceListener instanceListener; + var connStr = GetLightningUrl(supportedMethod); + if (connStr is null) + continue; + var instanceListenerKey = (paymentMethod.Network.CryptoCode, connStr.ToString()); + LightningInstanceListener? instanceListener; lock (_InstanceListeners) { _InstanceListeners.TryGetValue(instanceListenerKey, out instanceListener); @@ -346,16 +355,17 @@ namespace BTCPayServer.Payments.Lightning await _InvoiceRepository.NewPaymentDetails(invoice.Id, newPaymentMethodDetails, paymentMethod.Network); - instanceListener.AddListenedInvoice(new ListenedInvoice() - { - Expiration = invoice.ExpirationTime, - Uri = GetLightningUrl(supportedMethod).BaseUri.AbsoluteUri, - PaymentMethodDetails = newPaymentMethodDetails, - SupportedPaymentMethod = supportedMethod, - PaymentMethod = paymentMethod, - Network = (BTCPayNetwork)paymentMethod.Network, - InvoiceId = invoice.Id - }); + var url = GetLightningUrl(supportedMethod); + if (url is null) + continue; + instanceListener.AddListenedInvoice(new ListenedInvoice( + url.BaseUri, + invoice.ExpirationTime, + newPaymentMethodDetails, + supportedMethod, + paymentMethod, + (BTCPayNetwork)paymentMethod.Network, + invoice.Id)); _Aggregator.Publish(new Events.InvoiceNewPaymentDetailsEvent(invoice.Id, newPaymentMethodDetails, paymentMethod.GetId())); @@ -374,14 +384,14 @@ namespace BTCPayServer.Payments.Lightning } - private LightningConnectionString GetLightningUrl(LightningSupportedPaymentMethod supportedMethod) + private LightningConnectionString? GetLightningUrl(LightningSupportedPaymentMethod supportedMethod) { var url = supportedMethod.GetExternalLightningUrl(); if (url != null) return url; if (Options.Value.InternalLightningByCryptoCode.TryGetValue(supportedMethod.CryptoCode, out var conn)) return conn; - throw new InvalidOperationException($"{supportedMethod.CryptoCode}: The internal lightning node is not set up"); + return null; } TimeSpan _PollInterval = TimeSpan.FromMinutes(1.0); @@ -400,7 +410,7 @@ namespace BTCPayServer.Payments.Lightning } } } - private Timer _ListenPoller; + private Timer? _ListenPoller; public IOptions Options { get; } @@ -412,7 +422,8 @@ namespace BTCPayServer.Payments.Lightning _Cts.Cancel(); try { - await _CheckingInvoice; + if (_CheckingInvoice != null) + await _CheckingInvoice; } catch (OperationCanceledException) { @@ -420,7 +431,7 @@ namespace BTCPayServer.Payments.Lightning } try { - await Task.WhenAll(_ListeningInstances.Select(c => c.Value.Listening).ToArray()); + await Task.WhenAll(_ListeningInstances.Select(c => c.Value.Listening).Where(c => c != null).ToArray()!); } catch (OperationCanceledException) { @@ -479,7 +490,7 @@ namespace BTCPayServer.Payments.Lightning public bool Empty => _ListenedInvoices.IsEmpty; public bool IsListening => Listening?.Status is TaskStatus.Running || Listening?.Status is TaskStatus.WaitingForActivation; - public Task Listening { get; set; } + public Task? Listening { get; set; } public void EnsureListening(CancellationToken cancellation) { if (!IsListening) @@ -490,7 +501,7 @@ namespace BTCPayServer.Payments.Lightning Listening = Listen(StopListeningCancellationTokenSource.Token); } } - public CancellationTokenSource StopListeningCancellationTokenSource; + public CancellationTokenSource? StopListeningCancellationTokenSource; async Task Listen(CancellationToken cancellation) { Logs.PayServer.LogInformation($"{_network.CryptoCode} (Lightning): Start listening {ConnectionString.BaseUri}"); @@ -566,6 +577,8 @@ namespace BTCPayServer.Payments.Lightning public async Task AddPayment(LightningInvoice notification, string invoiceId, PaymentType paymentType) { + if (notification?.PaidAt is null) + return false; var payment = await _paymentService.AddPayment(invoiceId, notification.PaidAt.Value, new LightningLikePaymentData { BOLT11 = notification.BOLT11, @@ -595,15 +608,15 @@ namespace BTCPayServer.Payments.Lightning } } - class ListenedInvoice + public record ListenedInvoice( + Uri Uri, + DateTimeOffset Expiration, + LightningLikePaymentMethodDetails PaymentMethodDetails, + LightningSupportedPaymentMethod SupportedPaymentMethod, + PaymentMethod PaymentMethod, + BTCPayNetwork Network, + string InvoiceId) { public bool IsExpired() { return DateTimeOffset.UtcNow > Expiration; } - public DateTimeOffset Expiration { get; set; } - public LightningLikePaymentMethodDetails PaymentMethodDetails { get; set; } - public LightningSupportedPaymentMethod SupportedPaymentMethod { get; set; } - public PaymentMethod PaymentMethod { get; set; } - public string Uri { get; internal set; } - public BTCPayNetwork Network { get; internal set; } - public string InvoiceId { get; internal set; } } }