diff --git a/BTCPayServer/BTCPayServer.csproj b/BTCPayServer/BTCPayServer.csproj index f3bd39239..c5ce06c30 100644 --- a/BTCPayServer/BTCPayServer.csproj +++ b/BTCPayServer/BTCPayServer.csproj @@ -1,4 +1,4 @@ - + @@ -51,7 +51,7 @@ - + diff --git a/BTCPayServer/Controllers/GreenField/GreenfieldPullPaymentController.cs b/BTCPayServer/Controllers/GreenField/GreenfieldPullPaymentController.cs index b81fda2c6..ca4b38a09 100644 --- a/BTCPayServer/Controllers/GreenField/GreenfieldPullPaymentController.cs +++ b/BTCPayServer/Controllers/GreenField/GreenfieldPullPaymentController.cs @@ -250,7 +250,7 @@ namespace BTCPayServer.Controllers.Greenfield [HttpPost("~/api/v1/pull-payments/{pullPaymentId}/payouts")] [AllowAnonymous] - public async Task CreatePayout(string pullPaymentId, CreatePayoutRequest request) + public async Task CreatePayout(string pullPaymentId, CreatePayoutRequest request, CancellationToken cancellationToken) { if (!PaymentMethodId.TryParse(request?.PaymentMethod, out var paymentMethodId)) { @@ -270,7 +270,7 @@ namespace BTCPayServer.Controllers.Greenfield if (pp is null) return PullPaymentNotFound(); var ppBlob = pp.GetBlob(); - var destination = await payoutHandler.ParseAndValidateClaimDestination(paymentMethodId, request!.Destination, ppBlob); + var destination = await payoutHandler.ParseAndValidateClaimDestination(paymentMethodId, request!.Destination, ppBlob, cancellationToken); if (destination.destination is null) { ModelState.AddModelError(nameof(request.Destination), destination.error ?? "The destination is invalid for the payment specified"); @@ -332,7 +332,7 @@ namespace BTCPayServer.Controllers.Greenfield return PullPaymentNotFound(); ppBlob = pp.GetBlob(); } - var destination = await payoutHandler.ParseAndValidateClaimDestination(paymentMethodId, request!.Destination, ppBlob); + var destination = await payoutHandler.ParseAndValidateClaimDestination(paymentMethodId, request!.Destination, ppBlob, default); if (destination.destination is null) { ModelState.AddModelError(nameof(request.Destination), destination.error ?? "The destination is invalid for the payment specified"); diff --git a/BTCPayServer/Controllers/GreenField/LocalBTCPayServerClient.cs b/BTCPayServer/Controllers/GreenField/LocalBTCPayServerClient.cs index 2948fdb06..f9c917a7a 100644 --- a/BTCPayServer/Controllers/GreenField/LocalBTCPayServerClient.cs +++ b/BTCPayServer/Controllers/GreenField/LocalBTCPayServerClient.cs @@ -357,7 +357,7 @@ namespace BTCPayServer.Controllers.Greenfield CancellationToken cancellationToken = default) { return GetFromActionResult( - await GetController().CreatePayout(pullPaymentId, payoutRequest)); + await GetController().CreatePayout(pullPaymentId, payoutRequest, cancellationToken)); } public override async Task CancelPayout(string storeId, string payoutId, diff --git a/BTCPayServer/Controllers/UIPullPaymentController.cs b/BTCPayServer/Controllers/UIPullPaymentController.cs index 1712030ce..ef52e7756 100644 --- a/BTCPayServer/Controllers/UIPullPaymentController.cs +++ b/BTCPayServer/Controllers/UIPullPaymentController.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using BTCPayServer.Abstractions.Constants; using BTCPayServer.Abstractions.Extensions; @@ -153,7 +154,7 @@ namespace BTCPayServer.Controllers [AllowAnonymous] [HttpPost("pull-payments/{pullPaymentId}/claim")] - public async Task ClaimPullPayment(string pullPaymentId, ViewPullPaymentModel vm) + public async Task ClaimPullPayment(string pullPaymentId, ViewPullPaymentModel vm, CancellationToken cancellationToken) { using var ctx = _dbContextFactory.CreateContext(); var pp = await ctx.PullPayments.FindAsync(pullPaymentId); @@ -172,7 +173,7 @@ namespace BTCPayServer.Controllers ModelState.AddModelError(nameof(vm.SelectedPaymentMethod), "Invalid destination with selected payment method"); return await ViewPullPayment(pullPaymentId); } - var destination = await payoutHandler.ParseAndValidateClaimDestination(paymentMethodId, vm.Destination, ppBlob); + var destination = await payoutHandler.ParseAndValidateClaimDestination(paymentMethodId, vm.Destination, ppBlob, cancellationToken); if (destination.destination is null) { ModelState.AddModelError(nameof(vm.Destination), destination.error ?? "Invalid destination with selected payment method"); diff --git a/BTCPayServer/Data/Payouts/BitcoinLike/BitcoinLikePayoutHandler.cs b/BTCPayServer/Data/Payouts/BitcoinLike/BitcoinLikePayoutHandler.cs index 346af2479..aa786f677 100644 --- a/BTCPayServer/Data/Payouts/BitcoinLike/BitcoinLikePayoutHandler.cs +++ b/BTCPayServer/Data/Payouts/BitcoinLike/BitcoinLikePayoutHandler.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using BTCPayServer; using BTCPayServer.Abstractions.Models; @@ -71,7 +72,7 @@ public class BitcoinLikePayoutHandler : IPayoutHandler await explorerClient.TrackAsync(TrackedSource.Create(bitcoinLikeClaimDestination.Address)); } - public Task<(IClaimDestination destination, string error)> ParseClaimDestination(PaymentMethodId paymentMethodId, string destination) + public Task<(IClaimDestination destination, string error)> ParseClaimDestination(PaymentMethodId paymentMethodId, string destination, CancellationToken cancellationToken) { var network = _btcPayNetworkProvider.GetNetwork(paymentMethodId.CryptoCode); destination = destination.Trim(); @@ -287,7 +288,7 @@ public class BitcoinLikePayoutHandler : IPayoutHandler var blob = payout.GetBlob(_jsonSerializerSettings); if (payout.GetPaymentMethodId() != paymentMethodId) continue; - var claim = await ParseClaimDestination(paymentMethodId, blob.Destination); + var claim = await ParseClaimDestination(paymentMethodId, blob.Destination, default); switch (claim.destination) { case UriClaimDestination uriClaimDestination: diff --git a/BTCPayServer/Data/Payouts/IPayoutHandler.cs b/BTCPayServer/Data/Payouts/IPayoutHandler.cs index 0f746b88b..ebee5903d 100644 --- a/BTCPayServer/Data/Payouts/IPayoutHandler.cs +++ b/BTCPayServer/Data/Payouts/IPayoutHandler.cs @@ -1,6 +1,7 @@ #nullable enable using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using BTCPayServer.Abstractions.Models; using BTCPayServer.Client.Models; @@ -15,11 +16,11 @@ public interface IPayoutHandler public bool CanHandle(PaymentMethodId paymentMethod); public Task TrackClaim(PaymentMethodId paymentMethodId, IClaimDestination claimDestination); //Allows payout handler to parse payout destinations on its own - public Task<(IClaimDestination destination, string error)> ParseClaimDestination(PaymentMethodId paymentMethodId, string destination); + public Task<(IClaimDestination destination, string error)> ParseClaimDestination(PaymentMethodId paymentMethodId, string destination, CancellationToken cancellationToken); public (bool valid, string? error) ValidateClaimDestination(IClaimDestination claimDestination, PullPaymentBlob? pullPaymentBlob); - public async Task<(IClaimDestination? destination, string? error)> ParseAndValidateClaimDestination(PaymentMethodId paymentMethodId, string destination, PullPaymentBlob? pullPaymentBlob) + public async Task<(IClaimDestination? destination, string? error)> ParseAndValidateClaimDestination(PaymentMethodId paymentMethodId, string destination, PullPaymentBlob? pullPaymentBlob, CancellationToken cancellationToken) { - var res = await ParseClaimDestination(paymentMethodId, destination); + var res = await ParseClaimDestination(paymentMethodId, destination, cancellationToken); if (res.destination is null) return res; var res2 = ValidateClaimDestination(res.destination, pullPaymentBlob); diff --git a/BTCPayServer/Data/Payouts/LightningLike/LightningLikePayoutHandler.cs b/BTCPayServer/Data/Payouts/LightningLike/LightningLikePayoutHandler.cs index 1031332bd..aadd73403 100644 --- a/BTCPayServer/Data/Payouts/LightningLike/LightningLikePayoutHandler.cs +++ b/BTCPayServer/Data/Payouts/LightningLike/LightningLikePayoutHandler.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text; +using System.Threading; using System.Threading.Tasks; using BTCPayServer.Abstractions.Models; using BTCPayServer.Client.Models; @@ -59,7 +60,7 @@ namespace BTCPayServer.Data.Payouts.LightningLike : LightningLikePayoutHandlerClearnetNamedClient); } - public async Task<(IClaimDestination destination, string error)> ParseClaimDestination(PaymentMethodId paymentMethodId, string destination) + public async Task<(IClaimDestination destination, string error)> ParseClaimDestination(PaymentMethodId paymentMethodId, string destination, CancellationToken cancellationToken) { destination = destination.Trim(); var network = _btcPayNetworkProvider.GetNetwork(paymentMethodId.CryptoCode); @@ -72,7 +73,9 @@ namespace BTCPayServer.Data.Payouts.LightningLike if (lnurlTag is null) { - var info = (LNURLPayRequest)(await LNURL.LNURL.FetchInformation(lnurl, CreateClient(lnurl))); + using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + using var t = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, cancellationToken); + var info = (LNURLPayRequest)(await LNURL.LNURL.FetchInformation(lnurl, CreateClient(lnurl), t.Token)); lnurlTag = info.Tag; } diff --git a/BTCPayServer/Data/Payouts/LightningLike/UILightningLikePayoutController.cs b/BTCPayServer/Data/Payouts/LightningLike/UILightningLikePayoutController.cs index 63e031c80..0ef5c29f7 100644 --- a/BTCPayServer/Data/Payouts/LightningLike/UILightningLikePayoutController.cs +++ b/BTCPayServer/Data/Payouts/LightningLike/UILightningLikePayoutController.cs @@ -115,7 +115,7 @@ namespace BTCPayServer.Data.Payouts.LightningLike } [HttpPost("pull-payments/payouts/lightning/{cryptoCode}")] - public async Task ProcessLightningPayout(string cryptoCode, string[] payoutIds) + public async Task ProcessLightningPayout(string cryptoCode, string[] payoutIds, CancellationToken cancellationToken) { await SetStoreContext(); @@ -164,27 +164,27 @@ namespace BTCPayServer.Data.Payouts.LightningLike { ResultVM result; var blob = payoutData.GetBlob(_btcPayNetworkJsonSerializerSettings); - var claim = await payoutHandler.ParseClaimDestination(pmi, blob.Destination); + var claim = await payoutHandler.ParseClaimDestination(pmi, blob.Destination, cancellationToken); try { switch (claim.destination) { case LNURLPayClaimDestinaton lnurlPayClaimDestinaton: var lnurlResult = await GetInvoiceFromLNURL(payoutData, payoutHandler, blob, - lnurlPayClaimDestinaton, network.NBitcoinNetwork); + lnurlPayClaimDestinaton, network.NBitcoinNetwork, cancellationToken); if (lnurlResult.Item2 is not null) { result = lnurlResult.Item2; } else { - result = await TrypayBolt(client, blob, payoutData, lnurlResult.Item1, pmi); + result = await TrypayBolt(client, blob, payoutData, lnurlResult.Item1, pmi, cancellationToken); } break; case BoltInvoiceClaimDestination item1: - result = await TrypayBolt(client, blob, payoutData, item1.PaymentRequest, pmi); + result = await TrypayBolt(client, blob, payoutData, item1.PaymentRequest, pmi, cancellationToken); break; default: @@ -216,7 +216,7 @@ namespace BTCPayServer.Data.Payouts.LightningLike return View("LightningPayoutResult", results); } public static async Task<(BOLT11PaymentRequest, ResultVM)> GetInvoiceFromLNURL(PayoutData payoutData, - LightningLikePayoutHandler handler,PayoutBlob blob, LNURLPayClaimDestinaton lnurlPayClaimDestinaton, Network network) + LightningLikePayoutHandler handler,PayoutBlob blob, LNURLPayClaimDestinaton lnurlPayClaimDestinaton, Network network, CancellationToken cancellationToken) { var endpoint = lnurlPayClaimDestinaton.LNURL.IsValidEmail() ? LNURL.LNURL.ExtractUriFromInternetIdentifier(lnurlPayClaimDestinaton.LNURL) @@ -224,7 +224,7 @@ namespace BTCPayServer.Data.Payouts.LightningLike var httpClient = handler.CreateClient(endpoint); var lnurlInfo = (LNURLPayRequest)await LNURL.LNURL.FetchInformation(endpoint, "payRequest", - httpClient); + httpClient, cancellationToken); var lm = new LightMoney(blob.CryptoAmount.Value, LightMoneyUnit.BTC); if (lm > lnurlInfo.MaxSendable || lm < lnurlInfo.MinSendable) { @@ -241,7 +241,7 @@ namespace BTCPayServer.Data.Payouts.LightningLike try { var lnurlPayRequestCallbackResponse = - await lnurlInfo.SendRequest(lm, network, httpClient); + await lnurlInfo.SendRequest(lm, network, httpClient, cancellationToken: cancellationToken); return (lnurlPayRequestCallbackResponse.GetPaymentRequest(network), null); } @@ -262,7 +262,7 @@ namespace BTCPayServer.Data.Payouts.LightningLike public static readonly TimeSpan SendTimeout = TimeSpan.FromSeconds(20); public static async Task TrypayBolt( ILightningClient lightningClient, PayoutBlob payoutBlob, PayoutData payoutData, BOLT11PaymentRequest bolt11PaymentRequest, - PaymentMethodId pmi) + PaymentMethodId pmi, CancellationToken cancellationToken) { var boltAmount = bolt11PaymentRequest.MinimumAmount.ToDecimal(LightMoneyUnit.BTC); if (boltAmount != payoutBlob.CryptoAmount) @@ -283,14 +283,15 @@ namespace BTCPayServer.Data.Payouts.LightningLike { // TODO: Incorporate the changes from this PR here: // https://github.com/btcpayserver/BTCPayServer.Lightning/pull/106 - using var cts = new CancellationTokenSource(SendTimeout); + using var timeout = new CancellationTokenSource(SendTimeout); + using var c = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, cancellationToken); var result = await lightningClient.Pay(bolt11PaymentRequest.ToString(), new PayInvoiceParams() { Amount = bolt11PaymentRequest.MinimumAmount == LightMoney.Zero ? new LightMoney((decimal)payoutBlob.CryptoAmount, LightMoneyUnit.BTC) : null - }, cts.Token); + }, c.Token); string message = null; if (result.Result == PayResult.Ok) { diff --git a/BTCPayServer/HostedServices/PullPaymentHostedService.cs b/BTCPayServer/HostedServices/PullPaymentHostedService.cs index be135350f..af4378476 100644 --- a/BTCPayServer/HostedServices/PullPaymentHostedService.cs +++ b/BTCPayServer/HostedServices/PullPaymentHostedService.cs @@ -383,7 +383,7 @@ namespace BTCPayServer.HostedServices var payoutHandler = _payoutHandlers.FindPayoutHandler(paymentMethod); if (payoutHandler is null) throw new InvalidOperationException($"No payout handler for {paymentMethod}"); - var dest = await payoutHandler.ParseClaimDestination(paymentMethod, payoutBlob.Destination); + var dest = await payoutHandler.ParseClaimDestination(paymentMethod, payoutBlob.Destination, default); decimal minimumCryptoAmount = await payoutHandler.GetMinimumPayoutAmount(paymentMethod, dest.destination); if (cryptoAmount < minimumCryptoAmount) diff --git a/BTCPayServer/Hosting/MigrationStartupTask.cs b/BTCPayServer/Hosting/MigrationStartupTask.cs index c5470963b..421ded47a 100644 --- a/BTCPayServer/Hosting/MigrationStartupTask.cs +++ b/BTCPayServer/Hosting/MigrationStartupTask.cs @@ -423,7 +423,7 @@ WHERE cte.""Id""=p.""Id"" { continue; } - var claim = await handler?.ParseClaimDestination(pmi, payoutData.GetBlob(_btcPayNetworkJsonSerializerSettings).Destination); + var claim = await handler?.ParseClaimDestination(pmi, payoutData.GetBlob(_btcPayNetworkJsonSerializerSettings).Destination, default); payoutData.Destination = claim.destination?.Id; } await ctx.SaveChangesAsync(); diff --git a/BTCPayServer/PayoutProcessors/BaseAutomatedPayoutProcessor.cs b/BTCPayServer/PayoutProcessors/BaseAutomatedPayoutProcessor.cs index 2cb478cef..b2cf8a116 100644 --- a/BTCPayServer/PayoutProcessors/BaseAutomatedPayoutProcessor.cs +++ b/BTCPayServer/PayoutProcessors/BaseAutomatedPayoutProcessor.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; diff --git a/BTCPayServer/PayoutProcessors/Lightning/LightningAutomatedPayoutProcessor.cs b/BTCPayServer/PayoutProcessors/Lightning/LightningAutomatedPayoutProcessor.cs index e8e43b575..5cb9c15e2 100644 --- a/BTCPayServer/PayoutProcessors/Lightning/LightningAutomatedPayoutProcessor.cs +++ b/BTCPayServer/PayoutProcessors/Lightning/LightningAutomatedPayoutProcessor.cs @@ -55,7 +55,6 @@ public class LightningAutomatedPayoutProcessor : BaseAutomatedPayoutProcessor payouts) { var lightningSupportedPaymentMethod = (LightningSupportedPaymentMethod)paymentMethod; - if (lightningSupportedPaymentMethod.IsInternalNode && !(await Task.WhenAll((await _storeRepository.GetStoreUsers(_PayoutProcesserSettings.StoreId)) .Where(user => user.Role == StoreRoles.Owner).Select(user => user.Id) @@ -63,7 +62,6 @@ public class LightningAutomatedPayoutProcessor : BaseAutomatedPayoutProcessor