Formatting code to align with .editorconfig

This commit is contained in:
rockstardev
2025-05-12 23:42:21 -05:00
parent 268ed71afe
commit 95bef5a7c4
3 changed files with 100 additions and 56 deletions

View File

@@ -10,9 +10,12 @@ namespace BTCPayServer.Payments.Lightning
{
public static bool IsConfigured(this LightningPaymentMethodConfig supportedPaymentMethod, BTCPayNetwork network, LightningNetworkOptions options)
{
return supportedPaymentMethod.GetExternalLightningUrl() is not null || (supportedPaymentMethod.IsInternalNode && options.InternalLightningByCryptoCode.ContainsKey(network.CryptoCode));
return supportedPaymentMethod.GetExternalLightningUrl() is not null ||
(supportedPaymentMethod.IsInternalNode && options.InternalLightningByCryptoCode.ContainsKey(network.CryptoCode));
}
public static ILightningClient CreateLightningClient(this LightningPaymentMethodConfig supportedPaymentMethod, BTCPayNetwork network, LightningNetworkOptions options, LightningClientFactoryService lightningClientFactory)
public static ILightningClient CreateLightningClient(this LightningPaymentMethodConfig supportedPaymentMethod, BTCPayNetwork network,
LightningNetworkOptions options, LightningClientFactoryService lightningClientFactory)
{
var external = supportedPaymentMethod.GetExternalLightningUrl();
if (external != null)

View File

@@ -26,6 +26,7 @@ namespace BTCPayServer.Payments.Lightning
{
new LightningPaymentData ParsePaymentDetails(JToken details);
}
public class LightningLikePaymentHandler : IPaymentMethodHandler, ILightningPaymentHandler
{
public JsonSerializer Serializer { get; }
@@ -73,6 +74,7 @@ namespace BTCPayServer.Payments.Lightning
public BTCPayNetwork Network => _Network;
static LightMoney OneSat = LightMoney.FromUnit(1.0m, LightMoneyUnit.Satoshi);
public async Task ConfigurePrompt(PaymentMethodContext context)
{
if (context.InvoiceEntity.Type == InvoiceType.TopUp)
@@ -101,8 +103,8 @@ namespace BTCPayServer.Payments.Lightning
string description = storeBlob.LightningDescriptionTemplate;
description = description.Replace("{StoreName}", store.StoreName ?? "", StringComparison.OrdinalIgnoreCase)
.Replace("{ItemDescription}", invoice.Metadata.ItemDesc ?? "", StringComparison.OrdinalIgnoreCase)
.Replace("{OrderId}", invoice.Metadata.OrderId ?? "", StringComparison.OrdinalIgnoreCase);
.Replace("{ItemDescription}", invoice.Metadata.ItemDesc ?? "", StringComparison.OrdinalIgnoreCase)
.Replace("{OrderId}", invoice.Metadata.OrderId ?? "", StringComparison.OrdinalIgnoreCase);
using (var cts = new CancellationTokenSource(LightningTimeout))
{
try
@@ -139,7 +141,8 @@ namespace BTCPayServer.Payments.Lightning
}
public async Task<NodeInfo[]> GetNodeInfo(LightningPaymentMethodConfig supportedPaymentMethod, PrefixedInvoiceLogs? invoiceLogs, bool? preferOnion = null, bool throws = false)
public async Task<NodeInfo[]> GetNodeInfo(LightningPaymentMethodConfig supportedPaymentMethod, PrefixedInvoiceLogs? invoiceLogs,
bool? preferOnion = null, bool throws = false)
{
var synced = _Dashboard.IsFullySynched(_Network.CryptoCode, out var summary);
if (supportedPaymentMethod.IsInternalNode && !synced)
@@ -193,6 +196,7 @@ namespace BTCPayServer.Payments.Lightning
$"The lightning node is not synched ({blocksGap} blocks left)");
}
}
return nodeInfo;
}
catch (Exception e) when (!throws)
@@ -222,30 +226,37 @@ namespace BTCPayServer.Payments.Lightning
throw new PaymentMethodUnavailableException($"Error while connecting to the lightning node via {nodeInfo.Host}:{nodeInfo.Port} ({ex.Message})");
}
}
public LightningPaymentMethodConfig ParsePaymentMethodConfig(JToken config)
{
return config.ToObject<LightningPaymentMethodConfig>(Serializer) ?? throw new FormatException($"Invalid {nameof(LightningPaymentMethodConfig)}");
}
object IPaymentMethodHandler.ParsePaymentMethodConfig(JToken config)
{
return ParsePaymentMethodConfig(config);
}
object IPaymentMethodHandler.ParsePaymentPromptDetails(JToken details)
{
return ParsePaymentPromptDetails(details);
}
public LigthningPaymentPromptDetails ParsePaymentPromptDetails(JToken details)
{
return details.ToObject<LigthningPaymentPromptDetails>(Serializer) ?? throw new FormatException($"Invalid {nameof(LigthningPaymentPromptDetails)}");
}
public LightningPaymentData ParsePaymentDetails(JToken details)
{
return details.ToObject<LightningPaymentData>(Serializer) ?? throw new FormatException($"Invalid {nameof(LightningPaymentData)}");
}
object IPaymentMethodHandler.ParsePaymentDetails(JToken details)
{
return ParsePaymentDetails(details);
}
public async Task ValidatePaymentMethodConfig(PaymentMethodConfigValidationContext validationContext)
{
if (validationContext.Config is JValue { Type: JTokenType.String })
@@ -273,13 +284,15 @@ namespace BTCPayServer.Payments.Lightning
return;
}
}
if (!client.IsSafe(config.ConnectionString))
{
var canManage = (await validationContext.AuthorizationService.AuthorizeAsync(validationContext.User, null,
new PolicyRequirement(Policies.CanModifyServerSettings))).Succeeded;
new PolicyRequirement(Policies.CanModifyServerSettings))).Succeeded;
if (!canManage)
{
validationContext.ModelState.AddModelError(nameof(config.ConnectionString), $"You do not have 'btcpay.server.canmodifyserversettings' rights, so the connection string should not contain 'cookiefilepath', 'macaroondirectorypath', 'macaroonfilepath', and should not point to a local ip or to a dns name ending with '.internal', '.local', '.lan' or '.'.");
validationContext.ModelState.AddModelError(nameof(config.ConnectionString),
$"You do not have 'btcpay.server.canmodifyserversettings' rights, so the connection string should not contain 'cookiefilepath', 'macaroondirectorypath', 'macaroonfilepath', and should not point to a local ip or to a dns name ending with '.internal', '.local', '.lan' or '.'.");
return;
}
}
@@ -299,11 +312,13 @@ namespace BTCPayServer.Payments.Lightning
if (oldConfig?.IsInternalNode != config.IsInternalNode && config.IsInternalNode)
{
var canUseInternalNode = _policies.Settings.AllowLightningInternalNodeForAll ||
(await validationContext.AuthorizationService.AuthorizeAsync(validationContext.User, null,
new PolicyRequirement(Policies.CanUseInternalLightningNode))).Succeeded && _lightningNetworkOptions.Value.InternalLightningByCryptoCode.ContainsKey(_Network.CryptoCode);
(await validationContext.AuthorizationService.AuthorizeAsync(validationContext.User, null,
new PolicyRequirement(Policies.CanUseInternalLightningNode))).Succeeded &&
_lightningNetworkOptions.Value.InternalLightningByCryptoCode.ContainsKey(_Network.CryptoCode);
if (!canUseInternalNode)
{
validationContext.SetMissingPermission(Policies.CanUseInternalLightningNode, $"You are not authorized to use the internal lightning node. Either add '{Policies.CanUseInternalLightningNode}' to an API Key, or allow non-admin users to use the internal lightning node in the server settings.");
validationContext.SetMissingPermission(Policies.CanUseInternalLightningNode,
$"You are not authorized to use the internal lightning node. Either add '{Policies.CanUseInternalLightningNode}' to an API Key, or allow non-admin users to use the internal lightning node in the server settings.");
return;
}
}
@@ -313,6 +328,7 @@ namespace BTCPayServer.Payments.Lightning
validationContext.ModelState.AddModelError(nameof(config.ConnectionString), "The connection string or setting the internal node is required");
return;
}
validationContext.Config = JToken.FromObject(config, Serializer);
#pragma warning restore CS0618 // Type or member is obsolete
}

View File

@@ -40,18 +40,18 @@ namespace BTCPayServer.Payments.Lightning
private readonly PaymentMethodHandlerDictionary _handlers;
readonly Channel<string> _CheckInvoices = Channel.CreateUnbounded<string>();
Task? _CheckingInvoice;
readonly Dictionary<(string, string), LightningInstanceListener> _InstanceListeners = new Dictionary<(string, string), LightningInstanceListener>();
readonly Dictionary<(string, string), LightningInstanceListener> _InstanceListeners = new();
public LightningListener(EventAggregator aggregator,
InvoiceRepository invoiceRepository,
IMemoryCache memoryCache,
BTCPayNetworkProvider networkProvider,
LightningClientFactoryService lightningClientFactory,
StoreRepository storeRepository,
IOptions<LightningNetworkOptions> options,
PaymentService paymentService,
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary,
Logs logs)
InvoiceRepository invoiceRepository,
IMemoryCache memoryCache,
BTCPayNetworkProvider networkProvider,
LightningClientFactoryService lightningClientFactory,
StoreRepository storeRepository,
IOptions<LightningNetworkOptions> options,
PaymentService paymentService,
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary,
Logs logs)
{
Logs = logs;
_Aggregator = aggregator;
@@ -66,12 +66,13 @@ namespace BTCPayServer.Payments.Lightning
}
bool needCheckOfflinePayments = true;
async Task CheckingInvoice(CancellationToken cancellation)
{
var pmis = _handlers.Where(h => h is LightningLikePaymentHandler).Select(handler => handler.PaymentMethodId).ToArray();
foreach (var pmi in pmis)
{
retry:
retry:
try
{
Logs.PayServer.LogInformation("Checking if any payment arrived on lightning while the server was offline...");
@@ -83,13 +84,13 @@ retry:
_memoryCache.Set(GetCacheKey(invoice.Id), invoice, GetExpiration(invoice));
}
}
needCheckOfflinePayments = false;
Logs.PayServer.LogInformation("Processing lightning payments...");
while (await _CheckInvoices.Reader.WaitToReadAsync(cancellation) &&
_CheckInvoices.Reader.TryRead(out var invoiceId))
_CheckInvoices.Reader.TryRead(out var invoiceId))
{
var invoice = await GetInvoice(invoiceId);
@@ -107,9 +108,11 @@ retry:
{
if (!_InstanceListeners.TryGetValue(instanceListenerKey, out var instanceListener))
{
instanceListener ??= new LightningInstanceListener(_InvoiceRepository, _Aggregator, lightningClientFactory, listenedInvoice.Network, _handlers, connStr, _paymentService, Logs);
instanceListener ??= new LightningInstanceListener(_InvoiceRepository, _Aggregator, lightningClientFactory,
listenedInvoice.Network, _handlers, connStr, _paymentService, Logs);
_InstanceListeners.TryAdd(instanceListenerKey, instanceListener);
}
instanceListener.AddListenedInvoice(listenedInvoice);
_ = instanceListener.PollPayment(listenedInvoice, cancellation);
}
@@ -135,6 +138,7 @@ retry:
{
return $"{nameof(GetListenedInvoices)}-{invoiceId}";
}
private Task<InvoiceEntity> GetInvoice(string invoiceId)
{
return _memoryCache.GetOrCreateAsync(GetCacheKey(invoiceId), async (cacheEntry) =>
@@ -176,6 +180,7 @@ retry:
yield return (handler, prompt, handler.ParsePaymentPromptDetails(prompt.Details));
}
}
private List<ListenedInvoice> GetListenedInvoices(InvoiceEntity invoice)
{
var listenedInvoices = new List<ListenedInvoice>();
@@ -190,11 +195,13 @@ retry:
((IHasNetwork)o.Handler).Network,
invoice.Id));
}
return listenedInvoices;
}
readonly ConcurrentDictionary<string, LightningInstanceListener> _ListeningInstances = new ConcurrentDictionary<string, LightningInstanceListener>();
readonly CompositeDisposable leases = new CompositeDisposable();
public Task StartAsync(CancellationToken cancellationToken)
{
leases.Add(_Aggregator.SubscribeAsync<Events.InvoiceEvent>(async inv =>
@@ -204,7 +211,8 @@ retry:
_CheckInvoices.Writer.TryWrite(inv.Invoice.Id);
}
if (inv.Name == InvoiceEvent.ReceivedPayment && inv.Invoice.Status == InvoiceStatus.New && inv.Invoice.ExceptionStatus == InvoiceExceptionStatus.PaidPartial)
if (inv.Name == InvoiceEvent.ReceivedPayment && inv.Invoice.Status == InvoiceStatus.New &&
inv.Invoice.ExceptionStatus == InvoiceExceptionStatus.PaidPartial)
{
var pm = inv.Invoice.GetPaymentPrompts().First();
if (pm.Calculate().Due > 0m)
@@ -221,7 +229,6 @@ retry:
var invoice = await _InvoiceRepository.GetInvoice(inv.InvoiceId);
await CreateNewLNInvoiceForBTCPayInvoice(invoice);
}
}));
leases.Add(_Aggregator.Subscribe<Events.InvoicePaymentMethodActivated>(inv =>
{
@@ -249,7 +256,6 @@ retry:
CheckConnections();
}
catch { }
}, null, 0, (int)PollInterval.TotalMilliseconds);
leases.Add(_ListenPoller);
return Task.CompletedTask;
@@ -318,10 +324,10 @@ retry:
{
//not a fully supported option
}
lnurlPayPaymentMethodDetails = new LNURLPayPaymentMethodDetails()
{
Bech32Mode = lnurlPayPaymentMethodDetails.Bech32Mode,
NodeInfo = lnurlPayPaymentMethodDetails.NodeInfo,
Bech32Mode = lnurlPayPaymentMethodDetails.Bech32Mode, NodeInfo = lnurlPayPaymentMethodDetails.NodeInfo,
};
o.PaymentPrompt.Destination = null;
@@ -344,7 +350,8 @@ retry:
//not a fully supported option
}
var paymentContext = new PaymentMethodContext(store, store.GetStoreBlob(), JToken.FromObject(lnConfig, _handlers.GetLightningHandler(network).Serializer), lightningHandler, invoice, logs);
var paymentContext = new PaymentMethodContext(store, store.GetStoreBlob(),
JToken.FromObject(lnConfig, _handlers.GetLightningHandler(network).Serializer), lightningHandler, invoice, logs);
var paymentPrompt = paymentContext.Prompt;
await paymentContext.BeforeFetchingRates();
await paymentContext.CreatePaymentPrompt();
@@ -356,6 +363,7 @@ retry:
{
_InstanceListeners.TryGetValue(instanceListenerKey, out instanceListener);
}
if (instanceListener is not null)
{
await _InvoiceRepository.NewPaymentPrompt(invoice.Id, paymentContext);
@@ -377,10 +385,10 @@ retry:
InvoiceEventData.EventSeverity.Error);
}
}
await _InvoiceRepository.AddInvoiceLogs(invoice.Id, logs);
_CheckInvoices.Writer.TryWrite(invoice.Id);
}
}
private string? GetLightningUrl(string cryptoCode, LightningPaymentMethodConfig supportedMethod)
@@ -392,6 +400,7 @@ retry:
}
TimeSpan _PollInterval = TimeSpan.FromMinutes(1.0);
public TimeSpan PollInterval
{
get
@@ -407,6 +416,7 @@ retry:
}
}
}
private Timer? _ListenPoller;
public IOptions<LightningNetworkOptions> Options { get; }
@@ -424,16 +434,16 @@ retry:
}
catch (OperationCanceledException)
{
}
try
{
await Task.WhenAll(_ListeningInstances.Select(c => c.Value.Listening).Where(c => c != null).ToArray()!);
}
catch (OperationCanceledException)
{
}
Logs.PayServer.LogInformation($"{this.GetType().Name} successfully exited...");
}
}
@@ -453,13 +463,13 @@ retry:
public string ConnectionString { get; }
public LightningInstanceListener(InvoiceRepository invoiceRepository,
EventAggregator eventAggregator,
LightningClientFactoryService lightningClientFactory,
BTCPayNetwork network,
PaymentMethodHandlerDictionary handlers,
string connectionString,
PaymentService paymentService,
Logs logs)
EventAggregator eventAggregator,
LightningClientFactoryService lightningClientFactory,
BTCPayNetwork network,
PaymentMethodHandlerDictionary handlers,
string connectionString,
PaymentService paymentService,
Logs logs)
{
ArgumentNullException.ThrowIfNull(connectionString);
Logs = logs;
@@ -471,6 +481,7 @@ retry:
_lightningClientFactory = lightningClientFactory;
ConnectionString = connectionString;
}
internal bool AddListenedInvoice(ListenedInvoice invoice)
{
return _ListenedInvoices.TryAdd(invoice.PaymentMethodDetails.InvoiceId, invoice);
@@ -485,12 +496,14 @@ retry:
{
Logs.PayServer.LogInformation($"{_network.CryptoCode} (Lightning): Payment detected via polling on {listenedInvoice.InvoiceId}");
}
return lightningInvoice?.Status;
}
public bool Empty => _ListenedInvoices.IsEmpty;
public bool IsListening => Listening?.Status is TaskStatus.Running || Listening?.Status is TaskStatus.WaitingForActivation;
public Task? Listening { get; set; }
public void EnsureListening(CancellationToken cancellation)
{
if (!IsListening)
@@ -501,14 +514,16 @@ retry:
Listening = Listen(StopListeningCancellationTokenSource.Token);
}
}
public CancellationTokenSource? StopListeningCancellationTokenSource;
async Task Listen(CancellationToken cancellation)
{
string? uri = null;
try
{
var lightningClient = _lightningClientFactory.Create(ConnectionString, _network);
if(lightningClient is null)
if (lightningClient is null)
return;
uri = lightningClient.GetServerUri(ConnectionString)?.RemoveUserInfo() ?? "";
Logs.PayServer.LogInformation("{CryptoCode} (Lightning): Start listening {Uri}", _network.CryptoCode, uri);
@@ -519,6 +534,7 @@ retry:
{
Logs.PayServer.LogInformation("{CryptoCode} (Lightning): Could reconnect successfully to {Uri}", _network.CryptoCode, uri);
}
_ErrorAlreadyLogged = false;
while (!_ListenedInvoices.IsEmpty)
{
@@ -534,8 +550,10 @@ retry:
{
if (await AddPayment(notification, listenedInvoice.InvoiceId, listenedInvoice.PaymentMethod.PaymentMethodId))
{
Logs.PayServer.LogInformation("{CryptoCode} (Lightning): Payment detected via notification ({InvoiceId})", _network.CryptoCode, listenedInvoice.InvoiceId);
Logs.PayServer.LogInformation("{CryptoCode} (Lightning): Payment detected via notification ({InvoiceId})", _network.CryptoCode,
listenedInvoice.InvoiceId);
}
_ListenedInvoices.TryRemove(notification.Id, out var _);
}
else if (notification.Status == LightningInvoiceStatus.Expired)
@@ -552,8 +570,10 @@ retry:
Logs.PayServer.LogInformation("{CryptoCode} (Lightning): Stop listening {Uri}", _network.CryptoCode, uri);
}
catch (OperationCanceledException) when (cancellation.IsCancellationRequested) { }
if (_ListenedInvoices.IsEmpty)
Logs.PayServer.LogInformation("{CryptoCode} (Lightning): No more invoice to listen on {Uri}, releasing the connection", _network.CryptoCode, uri);
Logs.PayServer.LogInformation("{CryptoCode} (Lightning): No more invoice to listen on {Uri}, releasing the connection", _network.CryptoCode,
uri);
}
private uint256? GetPaymentHash(ListenedInvoice listenedInvoice)
@@ -570,10 +590,11 @@ retry:
{
var status = await PollPayment(invoice, cancellation);
if (status is null ||
status is LightningInvoiceStatus.Paid ||
status is LightningInvoiceStatus.Expired)
status is LightningInvoiceStatus.Paid ||
status is LightningInvoiceStatus.Expired)
_ListenedInvoices.TryRemove(invoice.PaymentMethodDetails.InvoiceId, out var _);
}
LastFullPoll = DateTimeOffset.UtcNow;
if (_ListenedInvoices.IsEmpty)
{
@@ -600,28 +621,31 @@ retry:
Currency = _network.CryptoCode,
InvoiceDataId = invoiceId,
Amount = (notification.AmountReceived ?? notification.Amount).ToDecimal(LightMoneyUnit.BTC),
}.Set(invoiceEntity, handler, new LightningLikePaymentData()
{
PaymentHash = paymentHash,
Preimage = string.IsNullOrEmpty(notification.Preimage) ? null : uint256.Parse(notification.Preimage),
});
}.Set(invoiceEntity, handler,
new LightningLikePaymentData()
{
PaymentHash = paymentHash, Preimage = string.IsNullOrEmpty(notification.Preimage) ? null : uint256.Parse(notification.Preimage),
});
var payment = await _paymentService.AddPayment(paymentData, [notification.BOLT11]);
if (payment != null)
{
if (notification.Preimage is not null)
{
var details = (LigthningPaymentPromptDetails)handler.ParsePaymentPromptDetails(invoiceEntity.GetPaymentPrompt(handler.PaymentMethodId)!.Details);
var details = (LigthningPaymentPromptDetails)handler.ParsePaymentPromptDetails(invoiceEntity.GetPaymentPrompt(handler.PaymentMethodId)!
.Details);
if (details.Preimage is null)
{
details.Preimage = uint256.Parse(notification.Preimage);
await _invoiceRepository.UpdatePaymentDetails(invoiceId, handler, details);
}
}
var invoice = await _invoiceRepository.GetInvoice(invoiceId);
if (invoice != null)
_eventAggregator.Publish(new InvoiceEvent(invoice, InvoiceEvent.ReceivedPayment) { Payment = payment });
}
return payment != null;
}
@@ -632,17 +656,18 @@ retry:
if (invoice.Value.IsExpired())
_ListenedInvoices.TryRemove(invoice.Key, out var _);
}
if (_ListenedInvoices.IsEmpty)
StopListeningCancellationTokenSource?.Cancel();
}
}
public record ListenedInvoice(
DateTimeOffset Expiration,
LigthningPaymentPromptDetails PaymentMethodDetails,
PaymentPrompt PaymentMethod,
BTCPayNetwork Network,
string InvoiceId)
DateTimeOffset Expiration,
LigthningPaymentPromptDetails PaymentMethodDetails,
PaymentPrompt PaymentMethod,
BTCPayNetwork Network,
string InvoiceId)
{
public bool IsExpired() { return DateTimeOffset.UtcNow > Expiration; }
}