mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 22:44:29 +01:00
Fix bug: Paying a lightning invoice might miss 1 satoshi due to rounding error
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
<Version>1.0.1.49</Version>
|
<Version>1.0.1.50</Version>
|
||||||
<NoWarn>NU1701,CA1816,CA1308,CA1810,CA2208</NoWarn>
|
<NoWarn>NU1701,CA1816,CA1308,CA1810,CA2208</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -30,6 +30,19 @@ namespace BTCPayServer
|
|||||||
{
|
{
|
||||||
public static class Extensions
|
public static class Extensions
|
||||||
{
|
{
|
||||||
|
public static decimal RoundUp(decimal value, int precision)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < precision; i++)
|
||||||
|
{
|
||||||
|
value = value * 10m;
|
||||||
|
}
|
||||||
|
value = Math.Ceiling(value);
|
||||||
|
for (int i = 0; i < precision; i++)
|
||||||
|
{
|
||||||
|
value = value / 10m;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
public static PaymentMethodId GetpaymentMethodId(this InvoiceCryptoInfo info)
|
public static PaymentMethodId GetpaymentMethodId(this InvoiceCryptoInfo info)
|
||||||
{
|
{
|
||||||
return new PaymentMethodId(info.CryptoCode, Enum.Parse<PaymentTypes>(info.PaymentType));
|
return new PaymentMethodId(info.CryptoCode, Enum.Parse<PaymentTypes>(info.PaymentType));
|
||||||
|
|||||||
@@ -160,7 +160,8 @@ namespace BTCPayServer.Payments.Bitcoin
|
|||||||
if (!alreadyExist)
|
if (!alreadyExist)
|
||||||
{
|
{
|
||||||
var payment = await _InvoiceRepository.AddPayment(invoice.Id, DateTimeOffset.UtcNow, paymentData, network.CryptoCode);
|
var payment = await _InvoiceRepository.AddPayment(invoice.Id, DateTimeOffset.UtcNow, paymentData, network.CryptoCode);
|
||||||
await ReceivedPayment(wallet, invoice.Id, payment, evt.DerivationStrategy);
|
if(payment != null)
|
||||||
|
await ReceivedPayment(wallet, invoice.Id, payment, evt.DerivationStrategy);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -330,7 +331,8 @@ namespace BTCPayServer.Payments.Bitcoin
|
|||||||
var paymentData = new BitcoinLikePaymentData(coin.Coin, transaction.Transaction.RBF);
|
var paymentData = new BitcoinLikePaymentData(coin.Coin, transaction.Transaction.RBF);
|
||||||
var payment = await _InvoiceRepository.AddPayment(invoice.Id, coin.Timestamp, paymentData, network.CryptoCode).ConfigureAwait(false);
|
var payment = await _InvoiceRepository.AddPayment(invoice.Id, coin.Timestamp, paymentData, network.CryptoCode).ConfigureAwait(false);
|
||||||
alreadyAccounted.Add(coin.Coin.Outpoint);
|
alreadyAccounted.Add(coin.Coin.Outpoint);
|
||||||
invoice = await ReceivedPayment(wallet, invoice.Id, payment, strategy);
|
if (payment != null)
|
||||||
|
invoice = await ReceivedPayment(wallet, invoice.Id, payment, strategy);
|
||||||
totalPayment++;
|
totalPayment++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,12 +163,13 @@ namespace BTCPayServer.Payments.Lightning
|
|||||||
|
|
||||||
private async Task AddPayment(BTCPayNetwork network, ChargeInvoice notification, ListenedInvoice listenedInvoice)
|
private async Task AddPayment(BTCPayNetwork network, ChargeInvoice notification, ListenedInvoice listenedInvoice)
|
||||||
{
|
{
|
||||||
await _InvoiceRepository.AddPayment(listenedInvoice.InvoiceId, notification.PaidAt.Value, new LightningLikePaymentData()
|
var payment = await _InvoiceRepository.AddPayment(listenedInvoice.InvoiceId, notification.PaidAt.Value, new LightningLikePaymentData()
|
||||||
{
|
{
|
||||||
BOLT11 = notification.PaymentRequest,
|
BOLT11 = notification.PaymentRequest,
|
||||||
Amount = notification.MilliSatoshi
|
Amount = notification.MilliSatoshi
|
||||||
}, network.CryptoCode, accounted: true);
|
}, network.CryptoCode, accounted: true);
|
||||||
_Aggregator.Publish(new InvoiceEvent(listenedInvoice.InvoiceId, 1002, "invoice_receivedPayment"));
|
if(payment != null)
|
||||||
|
_Aggregator.Publish(new InvoiceEvent(listenedInvoice.InvoiceId, 1002, "invoice_receivedPayment"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ChargeClient GetChargeClient(LightningSupportedPaymentMethod supportedPaymentMethod, BTCPayNetwork network)
|
private static ChargeClient GetChargeClient(LightningSupportedPaymentMethod supportedPaymentMethod, BTCPayNetwork network)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace BTCPayServer.Payments.Lightning
|
|||||||
public override async Task<IPaymentMethodDetails> CreatePaymentMethodDetails(LightningSupportedPaymentMethod supportedPaymentMethod, PaymentMethod paymentMethod, BTCPayNetwork network)
|
public override async Task<IPaymentMethodDetails> CreatePaymentMethodDetails(LightningSupportedPaymentMethod supportedPaymentMethod, PaymentMethod paymentMethod, BTCPayNetwork network)
|
||||||
{
|
{
|
||||||
var invoice = paymentMethod.ParentEntity;
|
var invoice = paymentMethod.ParentEntity;
|
||||||
var due = invoice.ProductInformation.Price / paymentMethod.Rate;
|
var due = Extensions.RoundUp(invoice.ProductInformation.Price / paymentMethod.Rate, 8);
|
||||||
var client = GetClient(supportedPaymentMethod, network);
|
var client = GetClient(supportedPaymentMethod, network);
|
||||||
var expiry = invoice.ExpirationTime - DateTimeOffset.UtcNow;
|
var expiry = invoice.ExpirationTime - DateTimeOffset.UtcNow;
|
||||||
var lightningInvoice = await client.CreateInvoiceAsync(new CreateInvoiceRequest()
|
var lightningInvoice = await client.CreateInvoiceAsync(new CreateInvoiceRequest()
|
||||||
|
|||||||
@@ -646,8 +646,9 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
var paid = 0m;
|
var paid = 0m;
|
||||||
var cryptoPaid = 0.0m;
|
var cryptoPaid = 0.0m;
|
||||||
|
|
||||||
|
int precision = 8;
|
||||||
var paidTxFee = 0m;
|
var paidTxFee = 0m;
|
||||||
bool paidEnough = paid >= RoundUp(totalDue, 8);
|
bool paidEnough = paid >= Extensions.RoundUp(totalDue, precision);
|
||||||
int txRequired = 0;
|
int txRequired = 0;
|
||||||
var payments =
|
var payments =
|
||||||
ParentEntity.GetPayments()
|
ParentEntity.GetPayments()
|
||||||
@@ -662,7 +663,7 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
totalDue += txFee;
|
totalDue += txFee;
|
||||||
paidTxFee += txFee;
|
paidTxFee += txFee;
|
||||||
}
|
}
|
||||||
paidEnough |= paid >= RoundUp(totalDue, 8);
|
paidEnough |= paid >= Extensions.RoundUp(totalDue, precision);
|
||||||
if (GetId() == _.GetPaymentMethodId())
|
if (GetId() == _.GetPaymentMethodId())
|
||||||
{
|
{
|
||||||
cryptoPaid += _.GetCryptoPaymentData().GetValue();
|
cryptoPaid += _.GetCryptoPaymentData().GetValue();
|
||||||
@@ -681,7 +682,7 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
paidTxFee += GetTxFee();
|
paidTxFee += GetTxFee();
|
||||||
}
|
}
|
||||||
|
|
||||||
accounting.TotalDue = Money.Coins(RoundUp(totalDue, 8));
|
accounting.TotalDue = Money.Coins(Extensions.RoundUp(totalDue, precision));
|
||||||
accounting.Paid = Money.Coins(paid);
|
accounting.Paid = Money.Coins(paid);
|
||||||
accounting.TxRequired = txRequired;
|
accounting.TxRequired = txRequired;
|
||||||
accounting.CryptoPaid = Money.Coins(cryptoPaid);
|
accounting.CryptoPaid = Money.Coins(cryptoPaid);
|
||||||
@@ -691,20 +692,6 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
return accounting;
|
return accounting;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static decimal RoundUp(decimal value, int precision)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < precision; i++)
|
|
||||||
{
|
|
||||||
value = value * 10m;
|
|
||||||
}
|
|
||||||
value = Math.Ceiling(value);
|
|
||||||
for (int i = 0; i < precision; i++)
|
|
||||||
{
|
|
||||||
value = value / 10m;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private decimal GetTxFee()
|
private decimal GetTxFee()
|
||||||
{
|
{
|
||||||
var method = GetPaymentMethodDetails();
|
var method = GetPaymentMethodDetails();
|
||||||
|
|||||||
@@ -461,6 +461,15 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
AddToTextSearch(invoiceId, addresses.Select(a => a.ToString()).ToArray());
|
AddToTextSearch(invoiceId, addresses.Select(a => a.ToString()).ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a payment to an invoice
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="invoiceId"></param>
|
||||||
|
/// <param name="date"></param>
|
||||||
|
/// <param name="paymentData"></param>
|
||||||
|
/// <param name="cryptoCode"></param>
|
||||||
|
/// <param name="accounted"></param>
|
||||||
|
/// <returns>The PaymentEntity or null if already added</returns>
|
||||||
public async Task<PaymentEntity> AddPayment(string invoiceId, DateTimeOffset date, CryptoPaymentData paymentData, string cryptoCode, bool accounted = false)
|
public async Task<PaymentEntity> AddPayment(string invoiceId, DateTimeOffset date, CryptoPaymentData paymentData, string cryptoCode, bool accounted = false)
|
||||||
{
|
{
|
||||||
using (var context = _ContextFactory.CreateContext())
|
using (var context = _ContextFactory.CreateContext())
|
||||||
@@ -486,7 +495,11 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
|
|
||||||
context.Payments.Add(data);
|
context.Payments.Add(data);
|
||||||
|
|
||||||
await context.SaveChangesAsync().ConfigureAwait(false);
|
try
|
||||||
|
{
|
||||||
|
await context.SaveChangesAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch(DbUpdateException) { return null; } // Already exists
|
||||||
AddToTextSearch(invoiceId, paymentData.GetSearchTerms());
|
AddToTextSearch(invoiceId, paymentData.GetSearchTerms());
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user