mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 22:44:29 +01:00
Fix race condition on calculation of contributions, refactor the methods to AppHelper
This commit is contained in:
@@ -9,23 +9,21 @@ using System.Threading.Tasks;
|
|||||||
using BTCPayServer.Crowdfund;
|
using BTCPayServer.Crowdfund;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
using BTCPayServer.Filters;
|
using BTCPayServer.Filters;
|
||||||
using BTCPayServer.Hubs;
|
|
||||||
using BTCPayServer.Models;
|
using BTCPayServer.Models;
|
||||||
using BTCPayServer.Models.AppViewModels;
|
using BTCPayServer.Models.AppViewModels;
|
||||||
|
using BTCPayServer.Payments;
|
||||||
using BTCPayServer.Rating;
|
using BTCPayServer.Rating;
|
||||||
using BTCPayServer.Security;
|
using BTCPayServer.Security;
|
||||||
using BTCPayServer.Services.Apps;
|
using BTCPayServer.Services.Apps;
|
||||||
using BTCPayServer.Services.Invoices;
|
using BTCPayServer.Services.Invoices;
|
||||||
using BTCPayServer.Services.Rates;
|
using BTCPayServer.Services.Rates;
|
||||||
using Ganss.XSS;
|
using Ganss.XSS;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Cors;
|
using Microsoft.AspNetCore.Cors;
|
||||||
using Microsoft.AspNetCore.Http.Extensions;
|
using Microsoft.AspNetCore.Http.Extensions;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NBitpayClient;
|
using NBitpayClient;
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using YamlDotNet.RepresentationModel;
|
using YamlDotNet.RepresentationModel;
|
||||||
using static BTCPayServer.Controllers.AppsController;
|
using static BTCPayServer.Controllers.AppsController;
|
||||||
|
|
||||||
@@ -143,7 +141,6 @@ namespace BTCPayServer.Controllers
|
|||||||
var info = await _CrowdfundHubStreamer.GetCrowdfundInfo(appId);
|
var info = await _CrowdfundHubStreamer.GetCrowdfundInfo(appId);
|
||||||
|
|
||||||
if (!isAdmin &&
|
if (!isAdmin &&
|
||||||
|
|
||||||
((settings.StartDate.HasValue && DateTime.Now < settings.StartDate) ||
|
((settings.StartDate.HasValue && DateTime.Now < settings.StartDate) ||
|
||||||
(settings.EndDate.HasValue && DateTime.Now > settings.EndDate) ||
|
(settings.EndDate.HasValue && DateTime.Now > settings.EndDate) ||
|
||||||
(settings.EnforceTargetAmount &&
|
(settings.EnforceTargetAmount &&
|
||||||
@@ -189,9 +186,7 @@ namespace BTCPayServer.Controllers
|
|||||||
NotificationURL = settings.NotificationUrl,
|
NotificationURL = settings.NotificationUrl,
|
||||||
FullNotifications = true,
|
FullNotifications = true,
|
||||||
ExtendedNotifications = true,
|
ExtendedNotifications = true,
|
||||||
RedirectURL = request.RedirectUrl ?? Request.GetDisplayUrl(),
|
RedirectURL = request.RedirectUrl ?? Request.GetDisplayUrl()
|
||||||
|
|
||||||
|
|
||||||
}, store, HttpContext.Request.GetAbsoluteRoot());
|
}, store, HttpContext.Request.GetAbsoluteRoot());
|
||||||
if (request.RedirectToCheckout)
|
if (request.RedirectToCheckout)
|
||||||
{
|
{
|
||||||
@@ -286,7 +281,7 @@ namespace BTCPayServer.Controllers
|
|||||||
public class AppsHelper
|
public class AppsHelper
|
||||||
{
|
{
|
||||||
ApplicationDbContextFactory _ContextFactory;
|
ApplicationDbContextFactory _ContextFactory;
|
||||||
private CurrencyNameTable _Currencies;
|
CurrencyNameTable _Currencies;
|
||||||
private readonly RateFetcher _RateFetcher;
|
private readonly RateFetcher _RateFetcher;
|
||||||
private readonly HtmlSanitizer _HtmlSanitizer;
|
private readonly HtmlSanitizer _HtmlSanitizer;
|
||||||
public CurrencyNameTable Currencies => _Currencies;
|
public CurrencyNameTable Currencies => _Currencies;
|
||||||
@@ -395,6 +390,57 @@ namespace BTCPayServer.Controllers
|
|||||||
.ToArray();
|
.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<decimal> GetCurrentContributionAmount(Dictionary<string, decimal> stats, string primaryCurrency, RateRules rateRules)
|
||||||
|
{
|
||||||
|
var result = new List<decimal>();
|
||||||
|
|
||||||
|
var ratesTask = _RateFetcher.FetchRates(
|
||||||
|
stats.Keys
|
||||||
|
.Select((x) => new CurrencyPair(primaryCurrency, PaymentMethodId.Parse(x).CryptoCode))
|
||||||
|
.Distinct()
|
||||||
|
.ToHashSet(),
|
||||||
|
rateRules).Select(async rateTask =>
|
||||||
|
{
|
||||||
|
var (key, value) = rateTask;
|
||||||
|
var tResult = await value;
|
||||||
|
var rate = tResult.BidAsk?.Bid;
|
||||||
|
if (rate == null) return;
|
||||||
|
|
||||||
|
foreach (var stat in stats)
|
||||||
|
{
|
||||||
|
if (string.Equals(PaymentMethodId.Parse(stat.Key).CryptoCode, key.Right,
|
||||||
|
StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
result.Add((1m / rate.Value) * stat.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Task.WhenAll(ratesTask);
|
||||||
|
|
||||||
|
return result.Sum();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<string, decimal> GetCurrentContributionAmountStats(InvoiceEntity[] invoices, bool usePaymentData = true)
|
||||||
|
{
|
||||||
|
if(usePaymentData){
|
||||||
|
var payments = invoices.SelectMany(entity => entity.GetPayments());
|
||||||
|
|
||||||
|
var groupedByMethod = payments.GroupBy(entity => entity.GetPaymentMethodId());
|
||||||
|
|
||||||
|
return groupedByMethod.ToDictionary(entities => entities.Key.ToString(),
|
||||||
|
entities => entities.Sum(entity => entity.GetCryptoPaymentData().GetValue()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return invoices
|
||||||
|
.GroupBy(entity => entity.ProductInformation.Currency)
|
||||||
|
.ToDictionary(
|
||||||
|
entities => entities.Key,
|
||||||
|
entities => entities.Sum(entity => entity.ProductInformation.Price));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class PosHolder
|
private class PosHolder
|
||||||
{
|
{
|
||||||
public string Key { get; set; }
|
public string Key { get; set; }
|
||||||
|
|||||||
@@ -184,62 +184,6 @@ namespace BTCPayServer.Crowdfund
|
|||||||
}, TaskScheduler.Current);
|
}, TaskScheduler.Current);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<decimal> GetCurrentContributionAmount(Dictionary<string, decimal> stats, string primaryCurrency, RateRules rateRules)
|
|
||||||
{
|
|
||||||
decimal result = 0;
|
|
||||||
|
|
||||||
var ratesTask = _RateFetcher .FetchRates(
|
|
||||||
stats.Keys
|
|
||||||
.Select((x) => new CurrencyPair( primaryCurrency, PaymentMethodId.Parse(x).CryptoCode))
|
|
||||||
.Distinct()
|
|
||||||
.ToHashSet(),
|
|
||||||
rateRules);
|
|
||||||
|
|
||||||
var finalTasks = new List<Task>();
|
|
||||||
foreach (var rateTask in ratesTask)
|
|
||||||
{
|
|
||||||
finalTasks.Add(Task.Run(async () =>
|
|
||||||
{
|
|
||||||
var tResult = await rateTask.Value;
|
|
||||||
var rate = tResult.BidAsk?.Bid;
|
|
||||||
if (rate == null) return;
|
|
||||||
|
|
||||||
foreach (var stat in stats)
|
|
||||||
{
|
|
||||||
if (string.Equals(PaymentMethodId.Parse(stat.Key).CryptoCode, rateTask.Key.Right,
|
|
||||||
StringComparison.InvariantCultureIgnoreCase))
|
|
||||||
{
|
|
||||||
result += (1m / rate.Value) * stat.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.WhenAll(finalTasks);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Dictionary<string, decimal> GetCurrentContributionAmountStats(InvoiceEntity[] invoices, bool usePaymentData = true)
|
|
||||||
{
|
|
||||||
if(usePaymentData){
|
|
||||||
var payments = invoices.SelectMany(entity => entity.GetPayments());
|
|
||||||
|
|
||||||
var groupedByMethod = payments.GroupBy(entity => entity.GetPaymentMethodId());
|
|
||||||
|
|
||||||
return groupedByMethod.ToDictionary(entities => entities.Key.ToString(),
|
|
||||||
entities => entities.Sum(entity => entity.GetCryptoPaymentData().GetValue()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return invoices
|
|
||||||
.GroupBy(entity => entity.ProductInformation.Currency)
|
|
||||||
.ToDictionary(
|
|
||||||
entities => entities.Key,
|
|
||||||
entities => entities.Sum(entity => entity.ProductInformation.Price));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private async Task<ViewCrowdfundViewModel> GetInfo(AppData appData, string statusMessage= null)
|
private async Task<ViewCrowdfundViewModel> GetInfo(AppData appData, string statusMessage= null)
|
||||||
{
|
{
|
||||||
var settings = appData.GetSettings<AppsController.CrowdfundSettings>();
|
var settings = appData.GetSettings<AppsController.CrowdfundSettings>();
|
||||||
@@ -280,13 +224,13 @@ namespace BTCPayServer.Crowdfund
|
|||||||
|
|
||||||
var rateRules = appData.StoreData.GetStoreBlob().GetRateRules(_BtcPayNetworkProvider);
|
var rateRules = appData.StoreData.GetStoreBlob().GetRateRules(_BtcPayNetworkProvider);
|
||||||
|
|
||||||
var pendingPaymentStats = GetCurrentContributionAmountStats(pendingInvoices, !settings.UseInvoiceAmount);
|
var pendingPaymentStats = _AppsHelper.GetCurrentContributionAmountStats(pendingInvoices, !settings.UseInvoiceAmount);
|
||||||
var paymentStats = GetCurrentContributionAmountStats(completeInvoices, !settings.UseInvoiceAmount);
|
var paymentStats = _AppsHelper.GetCurrentContributionAmountStats(completeInvoices, !settings.UseInvoiceAmount);
|
||||||
|
|
||||||
var currentAmount = await GetCurrentContributionAmount(
|
var currentAmount = await _AppsHelper.GetCurrentContributionAmount(
|
||||||
paymentStats,
|
paymentStats,
|
||||||
settings.TargetCurrency, rateRules);
|
settings.TargetCurrency, rateRules);
|
||||||
var currentPendingAmount = await GetCurrentContributionAmount(
|
var currentPendingAmount = await _AppsHelper.GetCurrentContributionAmount(
|
||||||
pendingPaymentStats,
|
pendingPaymentStats,
|
||||||
settings.TargetCurrency, rateRules);
|
settings.TargetCurrency, rateRules);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user