Merge pull request #861 from rockstardev/extendconfcount

Extending invoice monitoring time if we haven't reached MaxTrackedConfirmation
This commit is contained in:
Nicolas Dorier 2019-05-26 11:18:22 +09:00 committed by GitHub
commit a2251d245f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 80 additions and 23 deletions

View file

@ -288,31 +288,18 @@ namespace BTCPayServer.HostedServices
if (invoice.Status == InvoiceStatus.Complete ||
((invoice.Status == InvoiceStatus.Invalid || invoice.Status == InvoiceStatus.Expired) && invoice.MonitoringExpiration < DateTimeOffset.UtcNow))
{
var updateConfirmationCountIfNeeded = invoice
.GetPayments()
.Select<PaymentEntity, Task>(async payment =>
{
var paymentNetwork = _NetworkProvider.GetNetwork(payment.GetCryptoCode());
var paymentData = payment.GetCryptoPaymentData();
if (paymentData is Payments.Bitcoin.BitcoinLikePaymentData onChainPaymentData)
{
// Do update if confirmation count in the paymentData is not up to date
if ((onChainPaymentData.ConfirmationCount < paymentNetwork.MaxTrackedConfirmation && payment.Accounted)
&& (onChainPaymentData.Legacy || invoice.MonitoringExpiration < DateTimeOffset.UtcNow))
{
var transactionResult = await _ExplorerClientProvider.GetExplorerClient(payment.GetCryptoCode())?.GetTransactionAsync(onChainPaymentData.Outpoint.Hash);
var confirmationCount = transactionResult?.Confirmations ?? 0;
onChainPaymentData.ConfirmationCount = confirmationCount;
payment.SetCryptoPaymentData(onChainPaymentData);
await _InvoiceRepository.UpdatePayments(new List<PaymentEntity> { payment });
}
}
})
.ToArray();
await Task.WhenAll(updateConfirmationCountIfNeeded);
var extendInvoiceMonitoring = await UpdateConfirmationCount(invoice);
if (await _InvoiceRepository.RemovePendingInvoice(invoice.Id))
// we extend monitor time if we haven't reached max confirmation count
// say user used low fee and we only got 3 confirmations right before it's time to remove
if (extendInvoiceMonitoring)
{
await _InvoiceRepository.ExtendInvoiceMonitor(invoice.Id);
}
else if (await _InvoiceRepository.RemovePendingInvoice(invoice.Id))
{
_EventAggregator.Publish(new InvoiceStopWatchedEvent(invoice.Id));
}
break;
}
@ -330,6 +317,46 @@ namespace BTCPayServer.HostedServices
}
}
private async Task<bool> UpdateConfirmationCount(InvoiceEntity invoice)
{
bool extendInvoiceMonitoring = false;
var updateConfirmationCountIfNeeded = invoice
.GetPayments()
.Select<PaymentEntity, Task<PaymentEntity>>(async payment =>
{
var paymentNetwork = _NetworkProvider.GetNetwork(payment.GetCryptoCode());
var paymentData = payment.GetCryptoPaymentData();
if (paymentData is Payments.Bitcoin.BitcoinLikePaymentData onChainPaymentData)
{
// Do update if confirmation count in the paymentData is not up to date
if ((onChainPaymentData.ConfirmationCount < paymentNetwork.MaxTrackedConfirmation && payment.Accounted)
&& (onChainPaymentData.Legacy || invoice.MonitoringExpiration < DateTimeOffset.UtcNow))
{
var transactionResult = await _ExplorerClientProvider.GetExplorerClient(payment.GetCryptoCode())?.GetTransactionAsync(onChainPaymentData.Outpoint.Hash);
var confirmationCount = transactionResult?.Confirmations ?? 0;
onChainPaymentData.ConfirmationCount = confirmationCount;
payment.SetCryptoPaymentData(onChainPaymentData);
// we want to extend invoice monitoring until we reach max confirmations on all onchain payment methods
if (confirmationCount < paymentNetwork.MaxTrackedConfirmation)
extendInvoiceMonitoring = true;
return payment;
}
}
return null;
})
.ToArray();
await Task.WhenAll(updateConfirmationCountIfNeeded);
var updatedPaymentData = updateConfirmationCountIfNeeded.Where(a => a.Result != null).Select(a => a.Result).ToList();
if (updatedPaymentData.Count > 0)
{
await _InvoiceRepository.UpdatePayments(updatedPaymentData);
}
return extendInvoiceMonitoring;
}
public async Task StopAsync(CancellationToken cancellationToken)
{
if (_Cts == null)

View file

@ -238,6 +238,10 @@ namespace BTCPayServer.Payments.Bitcoin
}
}
// if needed add invoice back to pending to track number of confirmations
if (paymentData.ConfirmationCount < wallet.Network.MaxTrackedConfirmation)
await _InvoiceRepository.AddPendingInvoiceIfNotPresent(invoice.Id);
if (updated)
updatedPaymentEntities.Add(payment);
}

View file

@ -120,6 +120,20 @@ retry:
}
}
public async Task ExtendInvoiceMonitor(string invoiceId)
{
using (var ctx = _ContextFactory.CreateContext())
{
var invoiceData = await ctx.Invoices.FindAsync(invoiceId);
var invoice = ToObject(invoiceData.Blob);
invoice.MonitoringExpiration = invoice.MonitoringExpiration.AddHours(1);
invoiceData.Blob = ToBytes(invoice, null);
await ctx.SaveChangesAsync();
}
}
public async Task<InvoiceEntity> CreateInvoiceAsync(string storeId, InvoiceEntity invoice)
{
List<string> textSearch = new List<string>();
@ -260,6 +274,18 @@ retry:
}
}
public async Task AddPendingInvoiceIfNotPresent(string invoiceId)
{
using (var context = _ContextFactory.CreateContext())
{
if (!context.PendingInvoices.Any(a => a.Id == invoiceId))
{
context.PendingInvoices.Add(new PendingInvoiceData() { Id = invoiceId });
await context.SaveChangesAsync();
}
}
}
public async Task AddInvoiceEvent(string invoiceId, object evt)
{
using (var context = _ContextFactory.CreateContext())