Fix race condition on calculation of contributions, refactor the methods to AppHelper

This commit is contained in:
nicolas.dorier
2019-02-17 19:17:59 +09:00
parent 7e0f9f6e0d
commit 8fdaeb7bac
2 changed files with 73 additions and 83 deletions

View File

@@ -9,23 +9,21 @@ using System.Threading.Tasks;
using BTCPayServer.Crowdfund;
using BTCPayServer.Data;
using BTCPayServer.Filters;
using BTCPayServer.Hubs;
using BTCPayServer.Models;
using BTCPayServer.Models.AppViewModels;
using BTCPayServer.Payments;
using BTCPayServer.Rating;
using BTCPayServer.Security;
using BTCPayServer.Services.Apps;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Rates;
using Ganss.XSS;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using NBitpayClient;
using Newtonsoft.Json.Linq;
using YamlDotNet.RepresentationModel;
using static BTCPayServer.Controllers.AppsController;
@@ -126,35 +124,34 @@ namespace BTCPayServer.Controllers
public async Task<IActionResult> ContributeToCrowdfund(string appId, ContributeToCrowdfund request)
{
var app = await _AppsHelper.GetApp(appId, AppType.Crowdfund, true);
if (app == null)
return NotFound();
var settings = app.GetSettings<CrowdfundSettings>();
var isAdmin = await _AppsHelper.GetAppDataIfOwner(GetUserId(), appId, AppType.Crowdfund) != null;
if (!settings.Enabled)
{
if(!isAdmin)
if (!isAdmin)
return NotFound("Crowdfund is not currently active");
}
var info = await _CrowdfundHubStreamer.GetCrowdfundInfo(appId);
if(!isAdmin &&
((settings.StartDate.HasValue && DateTime.Now < settings.StartDate) ||
(settings.EndDate.HasValue && DateTime.Now > settings.EndDate) ||
(settings.EnforceTargetAmount &&
(info.Info.PendingProgressPercentage.GetValueOrDefault(0) +
info.Info.ProgressPercentage.GetValueOrDefault(0)) >= 100)))
if (!isAdmin &&
((settings.StartDate.HasValue && DateTime.Now < settings.StartDate) ||
(settings.EndDate.HasValue && DateTime.Now > settings.EndDate) ||
(settings.EnforceTargetAmount &&
(info.Info.PendingProgressPercentage.GetValueOrDefault(0) +
info.Info.ProgressPercentage.GetValueOrDefault(0)) >= 100)))
{
return NotFound("Crowdfund is not currently active");
}
var store = await _AppsHelper.GetStore(app);
var title = settings.Title;
var title = settings.Title;
var price = request.Amount;
ViewPointOfSaleViewModel.Item choice = null;
if (!string.IsNullOrEmpty(request.ChoiceKey))
@@ -170,11 +167,11 @@ namespace BTCPayServer.Controllers
}
if (!isAdmin && (settings.EnforceTargetAmount && info.TargetAmount.HasValue && price >
(info.TargetAmount - (info.Info.CurrentAmount + info.Info.CurrentPendingAmount))))
(info.TargetAmount - (info.Info.CurrentAmount + info.Info.CurrentPendingAmount))))
{
return NotFound("Contribution Amount is more than is currently allowed.");
}
store.AdditionalClaims.Add(new Claim(Policies.CanCreateInvoice.Key, store.Id));
try
{
@@ -189,9 +186,7 @@ namespace BTCPayServer.Controllers
NotificationURL = settings.NotificationUrl,
FullNotifications = true,
ExtendedNotifications = true,
RedirectURL = request.RedirectUrl ?? Request.GetDisplayUrl(),
RedirectURL = request.RedirectUrl ?? Request.GetDisplayUrl()
}, store, HttpContext.Request.GetAbsoluteRoot());
if (request.RedirectToCheckout)
{
@@ -286,7 +281,7 @@ namespace BTCPayServer.Controllers
public class AppsHelper
{
ApplicationDbContextFactory _ContextFactory;
private CurrencyNameTable _Currencies;
CurrencyNameTable _Currencies;
private readonly RateFetcher _RateFetcher;
private readonly HtmlSanitizer _HtmlSanitizer;
public CurrencyNameTable Currencies => _Currencies;
@@ -395,6 +390,57 @@ namespace BTCPayServer.Controllers
.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
{
public string Key { get; set; }