Fix crash which can happen during export if someone remove support for a network, inject Network inside paymentdata

This commit is contained in:
nicolas.dorier
2019-06-07 13:24:36 +09:00
parent 410be51951
commit 281280d3ec
8 changed files with 58 additions and 51 deletions

View File

@@ -123,7 +123,7 @@ namespace BTCPayServer.Tests
}));
invoiceEntity.SetPaymentMethods(paymentMethods);
var btc = invoiceEntity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike), null);
var btc = invoiceEntity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike));
var accounting = btc.Calculate();
invoiceEntity.Payments.Add(
@@ -153,7 +153,7 @@ namespace BTCPayServer.Tests
Assert.Equal(Money.Zero, accounting.Due);
Assert.Equal(Money.Zero, accounting.DueUncapped);
var ltc = invoiceEntity.GetPaymentMethod(new PaymentMethodId("LTC", PaymentTypes.BTCLike), null);
var ltc = invoiceEntity.GetPaymentMethod(new PaymentMethodId("LTC", PaymentTypes.BTCLike));
accounting = ltc.Calculate();
Assert.Equal(Money.Zero, accounting.Due);
@@ -281,11 +281,11 @@ namespace BTCPayServer.Tests
new PaymentMethod() {CryptoCode = "LTC", Rate = 500, NextNetworkFee = Money.Coins(0.01m)});
entity.SetPaymentMethods(paymentMethods);
entity.Payments = new List<PaymentEntity>();
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike), null);
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike));
accounting = paymentMethod.Calculate();
Assert.Equal(Money.Coins(5.1m), accounting.Due);
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("LTC", PaymentTypes.BTCLike), null);
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("LTC", PaymentTypes.BTCLike));
accounting = paymentMethod.Calculate();
Assert.Equal(Money.Coins(10.01m), accounting.TotalDue);
@@ -298,7 +298,7 @@ namespace BTCPayServer.Tests
NetworkFee = 0.1m
});
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike), null);
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike));
accounting = paymentMethod.Calculate();
Assert.Equal(Money.Coins(4.2m), accounting.Due);
Assert.Equal(Money.Coins(1.0m), accounting.CryptoPaid);
@@ -306,7 +306,7 @@ namespace BTCPayServer.Tests
Assert.Equal(Money.Coins(5.2m), accounting.TotalDue);
Assert.Equal(2, accounting.TxRequired);
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("LTC", PaymentTypes.BTCLike), null);
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("LTC", PaymentTypes.BTCLike));
accounting = paymentMethod.Calculate();
Assert.Equal(Money.Coins(10.01m + 0.1m * 2 - 2.0m /* 8.21m */), accounting.Due);
Assert.Equal(Money.Coins(0.0m), accounting.CryptoPaid);
@@ -321,7 +321,7 @@ namespace BTCPayServer.Tests
NetworkFee = 0.01m
});
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike), null);
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike));
accounting = paymentMethod.Calculate();
Assert.Equal(Money.Coins(4.2m - 0.5m + 0.01m / 2), accounting.Due);
Assert.Equal(Money.Coins(1.0m), accounting.CryptoPaid);
@@ -329,7 +329,7 @@ namespace BTCPayServer.Tests
Assert.Equal(Money.Coins(5.2m + 0.01m / 2), accounting.TotalDue); // The fee for LTC added
Assert.Equal(2, accounting.TxRequired);
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("LTC", PaymentTypes.BTCLike), null);
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("LTC", PaymentTypes.BTCLike));
accounting = paymentMethod.Calculate();
Assert.Equal(Money.Coins(8.21m - 1.0m + 0.01m), accounting.Due);
Assert.Equal(Money.Coins(1.0m), accounting.CryptoPaid);
@@ -346,7 +346,7 @@ namespace BTCPayServer.Tests
NetworkFee = 0.1m
});
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike), null);
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike));
accounting = paymentMethod.Calculate();
Assert.Equal(Money.Zero, accounting.Due);
Assert.Equal(Money.Coins(1.0m) + remaining, accounting.CryptoPaid);
@@ -355,7 +355,7 @@ namespace BTCPayServer.Tests
Assert.Equal(accounting.Paid, accounting.TotalDue);
Assert.Equal(2, accounting.TxRequired);
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("LTC", PaymentTypes.BTCLike), null);
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("LTC", PaymentTypes.BTCLike));
accounting = paymentMethod.Calculate();
Assert.Equal(Money.Zero, accounting.Due);
Assert.Equal(Money.Coins(1.0m), accounting.CryptoPaid);

View File

@@ -111,23 +111,18 @@ namespace BTCPayServer.Controllers
foreach (var payment in invoice.GetPayments())
{
var paymentNetwork = _NetworkProvider.GetNetwork<BTCPayNetwork>(payment.GetCryptoCode());
if (paymentNetwork == null)
{
continue;
}
var paymentData = payment.GetCryptoPaymentData();
//TODO: abstract
if (paymentData is Payments.Bitcoin.BitcoinLikePaymentData onChainPaymentData)
{
var m = new InvoiceDetailsModel.Payment();
m.Crypto = payment.GetPaymentMethodId().CryptoCode;
m.DepositAddress = onChainPaymentData.GetDestination(paymentNetwork);
m.DepositAddress = onChainPaymentData.GetDestination();
int confirmationCount = onChainPaymentData.ConfirmationCount;
if (confirmationCount >= paymentNetwork.MaxTrackedConfirmation)
if (confirmationCount >= payment.Network.MaxTrackedConfirmation)
{
m.Confirmations = "At least " + (paymentNetwork.MaxTrackedConfirmation);
m.Confirmations = "At least " + (payment.Network.MaxTrackedConfirmation);
}
else
{
@@ -136,7 +131,7 @@ namespace BTCPayServer.Controllers
m.TransactionId = onChainPaymentData.Outpoint.Hash.ToString();
m.ReceivedTime = payment.ReceivedTime;
m.TransactionLink = string.Format(CultureInfo.InvariantCulture, paymentNetwork.BlockExplorerLink, m.TransactionId);
m.TransactionLink = string.Format(CultureInfo.InvariantCulture, payment.Network.BlockExplorerLink, m.TransactionId);
m.Replaced = !payment.Accounted;
model.OnChainPayments.Add(m);
}
@@ -145,7 +140,7 @@ namespace BTCPayServer.Controllers
var lightningPaymentData = (LightningLikePaymentData)paymentData;
model.OffChainPayments.Add(new InvoiceDetailsModel.OffChainPayment()
{
Crypto = paymentNetwork.CryptoCode,
Crypto = payment.Network.CryptoCode,
BOLT11 = lightningPaymentData.BOLT11
});
}
@@ -242,7 +237,7 @@ namespace BTCPayServer.Controllers
paymentMethodId = paymentMethodTemp.GetId();
}
var paymentMethod = invoice.GetPaymentMethod(paymentMethodId, _NetworkProvider);
var paymentMethod = invoice.GetPaymentMethod(paymentMethodId);
var paymentMethodDetails = paymentMethod.GetPaymentMethodDetails();
var dto = invoice.EntityToDTO();
var cryptoInfo = dto.CryptoInfo.First(o => o.GetpaymentMethodId() == paymentMethodId);
@@ -499,7 +494,7 @@ namespace BTCPayServer.Controllers
[BitpayAPIConstraint(false)]
public async Task<IActionResult> Export(string format, string searchTerm = null, int timezoneOffset = 0)
{
var model = new InvoiceExport(_NetworkProvider, _CurrencyNameTable);
var model = new InvoiceExport(_CurrencyNameTable);
InvoiceQuery invoiceQuery = GetInvoiceQuery(searchTerm, timezoneOffset);
invoiceQuery.Skip = 0;

View File

@@ -72,7 +72,6 @@ namespace BTCPayServer.HostedServices
var paymentMethod = GetNearestClearedPayment(allPaymentMethods, out var accounting, _NetworkProvider);
if (paymentMethod == null)
return;
var network = _NetworkProvider.GetNetwork<BTCPayNetworkBase>(paymentMethod.GetId().CryptoCode);
if (invoice.Status == InvoiceStatus.New || invoice.Status == InvoiceStatus.Expired)
{
if (accounting.Paid >= accounting.MinimumTotalDue)
@@ -125,7 +124,7 @@ namespace BTCPayServer.HostedServices
if (invoice.Status == InvoiceStatus.Paid)
{
var confirmedAccounting = paymentMethod.Calculate(p => p.GetCryptoPaymentData().PaymentConfirmed(p, invoice.SpeedPolicy, network));
var confirmedAccounting = paymentMethod.Calculate(p => p.GetCryptoPaymentData().PaymentConfirmed(p, invoice.SpeedPolicy));
if (// Is after the monitoring deadline
(invoice.MonitoringExpiration < DateTimeOffset.UtcNow)
@@ -149,7 +148,7 @@ namespace BTCPayServer.HostedServices
if (invoice.Status == InvoiceStatus.Confirmed)
{
var completedAccounting = paymentMethod.Calculate(p => p.GetCryptoPaymentData().PaymentCompleted(p, network));
var completedAccounting = paymentMethod.Calculate(p => p.GetCryptoPaymentData().PaymentCompleted(p));
if (completedAccounting.Paid >= accounting.MinimumTotalDue)
{
context.Events.Add(new InvoiceEvent(invoice, 1006, InvoiceEvent.Completed));

View File

@@ -27,6 +27,8 @@ namespace BTCPayServer.Payments.Bitcoin
RBF = rbf;
}
[JsonIgnore]
public BTCPayNetworkBase Network { get; set; }
[JsonIgnore]
public OutPoint Outpoint { get; set; }
[JsonIgnore]
public TxOut Output { get; set; }
@@ -54,12 +56,12 @@ namespace BTCPayServer.Payments.Bitcoin
return Output.Value.ToDecimal(MoneyUnit.BTC);
}
public bool PaymentCompleted(PaymentEntity entity, BTCPayNetworkBase network)
public bool PaymentCompleted(PaymentEntity entity)
{
return ConfirmationCount >= network.MaxTrackedConfirmation;
return ConfirmationCount >= Network.MaxTrackedConfirmation;
}
public bool PaymentConfirmed(PaymentEntity entity, SpeedPolicy speedPolicy, BTCPayNetworkBase network)
public bool PaymentConfirmed(PaymentEntity entity, SpeedPolicy speedPolicy)
{
if (speedPolicy == SpeedPolicy.HighSpeed)
{
@@ -80,14 +82,14 @@ namespace BTCPayServer.Payments.Bitcoin
return false;
}
public BitcoinAddress GetDestination(BTCPayNetworkBase network)
public BitcoinAddress GetDestination()
{
return Output.ScriptPubKey.GetDestinationAddress(((BTCPayNetwork)network).NBitcoinNetwork);
return Output.ScriptPubKey.GetDestinationAddress(((BTCPayNetwork)Network).NBitcoinNetwork);
}
string CryptoPaymentData.GetDestination(BTCPayNetworkBase network)
string CryptoPaymentData.GetDestination()
{
return GetDestination(network).ToString();
return GetDestination().ToString();
}
}
}

View File

@@ -14,13 +14,15 @@ namespace BTCPayServer.Payments.Lightning
{
public class LightningLikePaymentData : CryptoPaymentData
{
[JsonIgnore]
public BTCPayNetworkBase Network { get; set; }
[JsonConverter(typeof(LightMoneyJsonConverter))]
public LightMoney Amount { get; set; }
public string BOLT11 { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.UInt256JsonConverter))]
public uint256 PaymentHash { get; set; }
public string GetDestination(BTCPayNetworkBase network)
public string GetDestination()
{
return BOLT11;
}
@@ -49,12 +51,12 @@ namespace BTCPayServer.Payments.Lightning
return Amount.ToDecimal(LightMoneyUnit.BTC);
}
public bool PaymentCompleted(PaymentEntity entity, BTCPayNetworkBase network)
public bool PaymentCompleted(PaymentEntity entity)
{
return true;
}
public bool PaymentConfirmed(PaymentEntity entity, SpeedPolicy speedPolicy, BTCPayNetworkBase network)
public bool PaymentConfirmed(PaymentEntity entity, SpeedPolicy speedPolicy)
{
return true;
}

View File

@@ -16,9 +16,8 @@ namespace BTCPayServer.Services.Invoices.Export
public BTCPayNetworkProvider Networks { get; }
public CurrencyNameTable Currencies { get; }
public InvoiceExport(BTCPayNetworkProvider networks, CurrencyNameTable currencies)
public InvoiceExport(CurrencyNameTable currencies)
{
Networks = networks;
Currencies = currencies;
}
public string Process(InvoiceEntity[] invoices, string fileFormat)
@@ -68,7 +67,7 @@ namespace BTCPayServer.Services.Invoices.Export
var cryptoCode = payment.GetPaymentMethodId().CryptoCode;
var pdata = payment.GetCryptoPaymentData();
var pmethod = invoice.GetPaymentMethod(payment.GetPaymentMethodId(), Networks);
var pmethod = invoice.GetPaymentMethod(payment.GetPaymentMethodId());
var paidAfterNetworkFees = pdata.GetValue() - payment.NetworkFee;
invoiceDue -= paidAfterNetworkFees * pmethod.Rate;
@@ -79,7 +78,7 @@ namespace BTCPayServer.Services.Invoices.Export
CryptoCode = cryptoCode,
ConversionRate = pmethod.Rate,
PaymentType = payment.GetPaymentMethodId().PaymentType.ToPrettyString(),
Destination = payment.GetCryptoPaymentData().GetDestination(Networks.GetNetwork<BTCPayNetworkBase>(cryptoCode)),
Destination = pdata.GetDestination(),
Paid = pdata.GetValue().ToString(CultureInfo.InvariantCulture),
PaidCurrency = Math.Round(pdata.GetValue() * pmethod.Rate, currency.NumberDecimalDigits).ToString(CultureInfo.InvariantCulture),
// Adding NetworkFee because Paid doesn't take into account network fees

View File

@@ -15,6 +15,7 @@ using NBXplorer.DerivationStrategy;
using BTCPayServer.Payments;
using NBitpayClient;
using BTCPayServer.Payments.Bitcoin;
using System.ComponentModel.DataAnnotations.Schema;
namespace BTCPayServer.Services.Invoices
{
@@ -432,9 +433,9 @@ namespace BTCPayServer.Services.Invoices
Id = data.GetPaymentId(),
Fee = entity.NetworkFee,
Value = data.GetValue(),
Completed = data.PaymentCompleted(entity, info.Network),
Confirmed = data.PaymentConfirmed(entity, SpeedPolicy, info.Network),
Destination = data.GetDestination(info.Network),
Completed = data.PaymentCompleted(entity),
Confirmed = data.PaymentConfirmed(entity, SpeedPolicy),
Destination = data.GetDestination(),
PaymentType = data.GetPaymentType().ToString(),
ReceivedDate = entity.ReceivedTime.DateTime
};
@@ -519,14 +520,14 @@ namespace BTCPayServer.Services.Invoices
return rates.TryGet(paymentMethodId) != null;
}
public PaymentMethod GetPaymentMethod(PaymentMethodId paymentMethodId, BTCPayNetworkProvider networkProvider)
public PaymentMethod GetPaymentMethod(PaymentMethodId paymentMethodId)
{
GetPaymentMethods().TryGetValue(paymentMethodId, out var data);
return data;
}
public PaymentMethod GetPaymentMethod(BTCPayNetworkBase network, PaymentType paymentType, BTCPayNetworkProvider networkProvider)
public PaymentMethod GetPaymentMethod(BTCPayNetworkBase network, PaymentType paymentType)
{
return GetPaymentMethod(new PaymentMethodId(network.CryptoCode, paymentType), networkProvider);
return GetPaymentMethod(new PaymentMethodId(network.CryptoCode, paymentType));
}
public PaymentMethodDictionary GetPaymentMethods()
@@ -898,6 +899,9 @@ namespace BTCPayServer.Services.Invoices
public class PaymentEntity
{
[NotMapped]
[JsonIgnore]
public BTCPayNetwork Network { get; set; }
public int Version { get; set; }
public DateTimeOffset ReceivedTime
{
@@ -943,6 +947,7 @@ namespace BTCPayServer.Services.Invoices
{
// For invoices created when CryptoPaymentDataType was not existing, we just consider that it is a RBFed payment for safety
var bitcoin = new BitcoinLikePaymentData();
bitcoin.Network = Network;
bitcoin.Outpoint = Outpoint;
bitcoin.Output = Output;
bitcoin.RBF = true;
@@ -955,6 +960,7 @@ namespace BTCPayServer.Services.Invoices
else
{
paymentData = GetPaymentMethodId().PaymentType.DeserializePaymentData(CryptoPaymentData);
paymentData.Network = Network;
if (paymentData is BitcoinLikePaymentData bitcoin)
{
bitcoin.Output = Output;
@@ -1014,6 +1020,8 @@ namespace BTCPayServer.Services.Invoices
/// </summary>
public interface CryptoPaymentData
{
[JsonIgnore]
BTCPayNetworkBase Network { get; set; }
/// <summary>
/// Returns an identifier which uniquely identify the payment
/// </summary>
@@ -1030,10 +1038,10 @@ namespace BTCPayServer.Services.Invoices
/// </summary>
/// <returns>The amount paid</returns>
decimal GetValue();
bool PaymentCompleted(PaymentEntity entity, BTCPayNetworkBase network);
bool PaymentConfirmed(PaymentEntity entity, SpeedPolicy speedPolicy, BTCPayNetworkBase network);
bool PaymentCompleted(PaymentEntity entity);
bool PaymentConfirmed(PaymentEntity entity, SpeedPolicy speedPolicy);
PaymentType GetPaymentType();
string GetDestination(BTCPayNetworkBase network);
string GetDestination();
}
}

View File

@@ -248,7 +248,7 @@ retry:
return false;
var invoiceEntity = ToObject(invoice.Blob);
var currencyData = invoiceEntity.GetPaymentMethod(network, paymentMethod.GetPaymentType(), null);
var currencyData = invoiceEntity.GetPaymentMethod(network, paymentMethod.GetPaymentType());
if (currencyData == null)
return false;
@@ -441,6 +441,7 @@ retry:
entity.Payments = invoice.Payments.Select(p =>
{
var paymentEntity = ToObject<PaymentEntity>(p.Blob, null);
paymentEntity.Network = _Networks.GetNetwork<BTCPayNetwork>(paymentEntity.CryptoCode);
paymentEntity.Accounted = p.Accounted;
// PaymentEntity on version 0 does not have their own fee, because it was assumed that the payment method have fixed fee.
// We want to hide this legacy detail in InvoiceRepository, so we fetch the fee from the PaymentMethod and assign it to the PaymentEntity.
@@ -646,7 +647,7 @@ retry:
if (invoice == null)
return null;
InvoiceEntity invoiceEntity = ToObject(invoice.Blob);
PaymentMethod paymentMethod = invoiceEntity.GetPaymentMethod(new PaymentMethodId(network.CryptoCode, paymentData.GetPaymentType()), null);
PaymentMethod paymentMethod = invoiceEntity.GetPaymentMethod(new PaymentMethodId(network.CryptoCode, paymentData.GetPaymentType()));
IPaymentMethodDetails paymentMethodDetails = paymentMethod.GetPaymentMethodDetails();
PaymentEntity entity = new PaymentEntity
{
@@ -656,7 +657,8 @@ retry:
#pragma warning restore CS0618
ReceivedTime = date.UtcDateTime,
Accounted = accounted,
NetworkFee = paymentMethodDetails.GetNextNetworkFee()
NetworkFee = paymentMethodDetails.GetNextNetworkFee(),
Network = network as BTCPayNetwork
};
entity.SetCryptoPaymentData(paymentData);