Make LightningListener nullable, fix some NRE

This commit is contained in:
nicolas.dorier 2023-04-27 13:37:10 +09:00
parent 0c78e9e4ac
commit 6193835ea1
No known key found for this signature in database
GPG key ID: 6618763EF09186FE

View file

@ -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<string> _CheckInvoices = Channel.CreateUnbounded<string>();
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<BTCPayNetwork>(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<LightningNetworkOptions> 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<bool> 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; }
}
}