Fix flaky test (#4330)

* Fix flaky test

* Update BTCPayServer/PayoutProcessors/BaseAutomatedPayoutProcessor.cs

Co-authored-by: d11n <mail@dennisreimann.de>

Co-authored-by: d11n <mail@dennisreimann.de>
This commit is contained in:
Nicolas Dorier 2022-11-22 20:17:29 +09:00 committed by GitHub
parent d959f5096b
commit 9404819dbe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 40 additions and 35 deletions

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
 <Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="../Build/Version.csproj" Condition="Exists('../Build/Version.csproj')" />
<Import Project="../Build/Common.csproj" />
@ -51,7 +51,7 @@
<PackageReference Include="Fido2" Version="2.0.2" />
<PackageReference Include="Fido2.AspNet" Version="2.0.2" />
<PackageReference Include="HtmlSanitizer" Version="5.0.372" />
<PackageReference Include="LNURL" Version="0.0.24" />
<PackageReference Include="LNURL" Version="0.0.26" />
<PackageReference Include="MailKit" Version="3.3.0" />
<PackageReference Include="McMaster.NETCore.Plugins.Mvc" Version="1.4.0" />
<PackageReference Include="QRCoder" Version="1.4.3" />

View File

@ -250,7 +250,7 @@ namespace BTCPayServer.Controllers.Greenfield
[HttpPost("~/api/v1/pull-payments/{pullPaymentId}/payouts")]
[AllowAnonymous]
public async Task<IActionResult> CreatePayout(string pullPaymentId, CreatePayoutRequest request)
public async Task<IActionResult> 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");

View File

@ -357,7 +357,7 @@ namespace BTCPayServer.Controllers.Greenfield
CancellationToken cancellationToken = default)
{
return GetFromActionResult<PayoutData>(
await GetController<GreenfieldPullPaymentController>().CreatePayout(pullPaymentId, payoutRequest));
await GetController<GreenfieldPullPaymentController>().CreatePayout(pullPaymentId, payoutRequest, cancellationToken));
}
public override async Task CancelPayout(string storeId, string payoutId,

View File

@ -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<IActionResult> ClaimPullPayment(string pullPaymentId, ViewPullPaymentModel vm)
public async Task<IActionResult> 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");

View File

@ -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<BTCPayNetwork>(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:

View File

@ -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);

View File

@ -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<BTCPayNetwork>(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;
}

View File

@ -115,7 +115,7 @@ namespace BTCPayServer.Data.Payouts.LightningLike
}
[HttpPost("pull-payments/payouts/lightning/{cryptoCode}")]
public async Task<IActionResult> ProcessLightningPayout(string cryptoCode, string[] payoutIds)
public async Task<IActionResult> 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<ResultVM> 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)
{

View File

@ -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)

View File

@ -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();

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

View File

@ -55,7 +55,6 @@ public class LightningAutomatedPayoutProcessor : BaseAutomatedPayoutProcessor<Au
protected override async Task Process(ISupportedPaymentMethod paymentMethod, List<PayoutData> 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<Au
{
return;
}
var client =
lightningSupportedPaymentMethod.CreateLightningClient(_network, _options.Value,
_lightningClientFactoryService);
@ -71,7 +69,7 @@ public class LightningAutomatedPayoutProcessor : BaseAutomatedPayoutProcessor<Au
foreach (var payoutData in payouts)
{
var blob = payoutData.GetBlob(_btcPayNetworkJsonSerializerSettings);
var claim = await _payoutHandler.ParseClaimDestination(PaymentMethodId, blob.Destination);
var claim = await _payoutHandler.ParseClaimDestination(PaymentMethodId, blob.Destination, CancellationToken);
try
{
switch (claim.destination)
@ -79,7 +77,7 @@ public class LightningAutomatedPayoutProcessor : BaseAutomatedPayoutProcessor<Au
case LNURLPayClaimDestinaton lnurlPayClaimDestinaton:
var lnurlResult = await UILightningLikePayoutController.GetInvoiceFromLNURL(payoutData,
_payoutHandler, blob,
lnurlPayClaimDestinaton, _network.NBitcoinNetwork);
lnurlPayClaimDestinaton, _network.NBitcoinNetwork, CancellationToken);
if (lnurlResult.Item2 is not null)
{
continue;
@ -104,6 +102,6 @@ public class LightningAutomatedPayoutProcessor : BaseAutomatedPayoutProcessor<Au
BOLT11PaymentRequest bolt11PaymentRequest)
{
return (await UILightningLikePayoutController.TrypayBolt(lightningClient, payoutBlob, payoutData, bolt11PaymentRequest,
payoutData.GetPaymentMethodId())).Result == PayResult.Ok;
payoutData.GetPaymentMethodId(), CancellationToken)).Result == PayResult.Ok;
}
}

View File

@ -107,7 +107,7 @@ namespace BTCPayServer.PayoutProcessors.OnChain
}
var claimDestination =
await _bitcoinLikePayoutHandler.ParseClaimDestination(paymentMethodId, blob.Destination);
await _bitcoinLikePayoutHandler.ParseClaimDestination(paymentMethodId, blob.Destination, CancellationToken);
if (!string.IsNullOrEmpty(claimDestination.error))
{
continue;