From 512ee16620b25bdd47e9e42aa4a0e1ea184640e1 Mon Sep 17 00:00:00 2001 From: Nicolas Dorier Date: Fri, 24 May 2019 22:22:38 +0900 Subject: [PATCH] Refactor invoice entity to not have to inject the NetworkProvider (#858) --- BTCPayServer.Tests/UnitTest1.cs | 4 +- .../Controllers/InvoiceController.API.cs | 4 +- .../Controllers/InvoiceController.UI.cs | 10 ++--- BTCPayServer/Controllers/InvoiceController.cs | 7 ++-- .../InvoiceNotificationManager.cs | 2 +- BTCPayServer/HostedServices/InvoiceWatcher.cs | 2 +- BTCPayServer/Hosting/BTCPayServerServices.cs | 2 +- .../Payments/Bitcoin/NBXplorerListener.cs | 2 +- .../Payments/Lightning/LightningListener.cs | 4 +- .../Services/Invoices/InvoiceEntity.cs | 38 ++++++++++--------- .../Services/Invoices/InvoiceRepository.cs | 35 +++++++++-------- 11 files changed, 59 insertions(+), 51 deletions(-) diff --git a/BTCPayServer.Tests/UnitTest1.cs b/BTCPayServer.Tests/UnitTest1.cs index 26f16e9e2..225c2f439 100644 --- a/BTCPayServer.Tests/UnitTest1.cs +++ b/BTCPayServer.Tests/UnitTest1.cs @@ -201,7 +201,7 @@ namespace BTCPayServer.Tests entity.SetPaymentMethod(new PaymentMethod() { CryptoCode = "BTC", Rate = 5000, NextNetworkFee = Money.Coins(0.1m) }); entity.ProductInformation = new ProductInformation() { Price = 5000 }; - var paymentMethod = entity.GetPaymentMethods(null).TryGet("BTC", PaymentTypes.BTCLike); + var paymentMethod = entity.GetPaymentMethods().TryGet("BTC", PaymentTypes.BTCLike); var accounting = paymentMethod.Calculate(); Assert.Equal(Money.Coins(1.1m), accounting.Due); Assert.Equal(Money.Coins(1.1m), accounting.TotalDue); @@ -364,7 +364,7 @@ namespace BTCPayServer.Tests entity.PaymentTolerance = 0; - var paymentMethod = entity.GetPaymentMethods(null).TryGet("BTC", PaymentTypes.BTCLike); + var paymentMethod = entity.GetPaymentMethods().TryGet("BTC", PaymentTypes.BTCLike); var accounting = paymentMethod.Calculate(); Assert.Equal(Money.Coins(1.1m), accounting.Due); Assert.Equal(Money.Coins(1.1m), accounting.TotalDue); diff --git a/BTCPayServer/Controllers/InvoiceController.API.cs b/BTCPayServer/Controllers/InvoiceController.API.cs index e211cd791..5023fb70e 100644 --- a/BTCPayServer/Controllers/InvoiceController.API.cs +++ b/BTCPayServer/Controllers/InvoiceController.API.cs @@ -51,7 +51,7 @@ namespace BTCPayServer.Controllers })).FirstOrDefault(); if (invoice == null) throw new BitpayHttpException(404, "Object not found"); - var resp = invoice.EntityToDTO(_NetworkProvider); + var resp = invoice.EntityToDTO(); return new DataWrapper(resp); } [HttpGet] @@ -82,7 +82,7 @@ namespace BTCPayServer.Controllers }; var entities = (await _InvoiceRepository.GetInvoices(query)) - .Select((o) => o.EntityToDTO(_NetworkProvider)).ToArray(); + .Select((o) => o.EntityToDTO()).ToArray(); return DataWrapper.Create(entities); } diff --git a/BTCPayServer/Controllers/InvoiceController.UI.cs b/BTCPayServer/Controllers/InvoiceController.UI.cs index ffe008f2e..4d63be560 100644 --- a/BTCPayServer/Controllers/InvoiceController.UI.cs +++ b/BTCPayServer/Controllers/InvoiceController.UI.cs @@ -96,7 +96,7 @@ namespace BTCPayServer.Controllers { var model = new InvoiceDetailsModel(); - foreach (var data in invoice.GetPaymentMethods(null)) + foreach (var data in invoice.GetPaymentMethods()) { var accounting = data.Calculate(); var paymentMethodId = data.GetId(); @@ -248,18 +248,18 @@ namespace BTCPayServer.Controllers { if (!isDefaultPaymentId) return null; - var paymentMethodTemp = invoice.GetPaymentMethods(_NetworkProvider) + var paymentMethodTemp = invoice.GetPaymentMethods() .Where(c => paymentMethodId.CryptoCode == c.GetId().CryptoCode) .FirstOrDefault(); if (paymentMethodTemp == null) - paymentMethodTemp = invoice.GetPaymentMethods(_NetworkProvider).First(); + paymentMethodTemp = invoice.GetPaymentMethods().First(); network = paymentMethodTemp.Network; paymentMethodId = paymentMethodTemp.GetId(); } var paymentMethod = invoice.GetPaymentMethod(paymentMethodId, _NetworkProvider); var paymentMethodDetails = paymentMethod.GetPaymentMethodDetails(); - var dto = invoice.EntityToDTO(_NetworkProvider); + var dto = invoice.EntityToDTO(); var cryptoInfo = dto.CryptoInfo.First(o => o.GetpaymentMethodId() == paymentMethodId); var storeBlob = store.GetStoreBlob(); var currency = invoice.ProductInformation.Currency; @@ -332,7 +332,7 @@ namespace BTCPayServer.Controllers CoinSwitchMerchantId = coinswitch?.MerchantId, CoinSwitchMode = coinswitch?.Mode, StoreId = store.Id, - AvailableCryptos = invoice.GetPaymentMethods(_NetworkProvider) + AvailableCryptos = invoice.GetPaymentMethods() .Where(i => i.Network != null) .Select(kv => new PaymentModel.AvailableCrypto() { diff --git a/BTCPayServer/Controllers/InvoiceController.cs b/BTCPayServer/Controllers/InvoiceController.cs index 26bd7b49e..ed075be7f 100644 --- a/BTCPayServer/Controllers/InvoiceController.cs +++ b/BTCPayServer/Controllers/InvoiceController.cs @@ -75,7 +75,8 @@ namespace BTCPayServer.Controllers var entity = new InvoiceEntity { Version = InvoiceEntity.Lastest_Version, - InvoiceTime = DateTimeOffset.UtcNow + InvoiceTime = DateTimeOffset.UtcNow, + Networks = _NetworkProvider }; var getAppsTaggingStore = _InvoiceRepository.GetAppsTaggingStore(store.Id); @@ -208,7 +209,7 @@ namespace BTCPayServer.Controllers using (logs.Measure("Saving invoice")) { - entity = await _InvoiceRepository.CreateInvoiceAsync(store.Id, entity, _NetworkProvider); + entity = await _InvoiceRepository.CreateInvoiceAsync(store.Id, entity); } _ = Task.Run(async () => { @@ -223,7 +224,7 @@ namespace BTCPayServer.Controllers await _InvoiceRepository.AddInvoiceLogs(entity.Id, logs); }); _EventAggregator.Publish(new Events.InvoiceEvent(entity, 1001, InvoiceEvent.Created)); - var resp = entity.EntityToDTO(_NetworkProvider); + var resp = entity.EntityToDTO(); return new DataWrapper(resp) { Facade = "pos/invoice" }; } diff --git a/BTCPayServer/HostedServices/InvoiceNotificationManager.cs b/BTCPayServer/HostedServices/InvoiceNotificationManager.cs index 288066bbb..0a35efa58 100644 --- a/BTCPayServer/HostedServices/InvoiceNotificationManager.cs +++ b/BTCPayServer/HostedServices/InvoiceNotificationManager.cs @@ -64,7 +64,7 @@ namespace BTCPayServer.HostedServices void Notify(InvoiceEntity invoice, InvoiceEvent invoiceEvent, bool extendedNotification) { - var dto = invoice.EntityToDTO(_NetworkProvider); + var dto = invoice.EntityToDTO(); var notification = new InvoicePaymentNotificationEventWrapper() { Data = new InvoicePaymentNotification() diff --git a/BTCPayServer/HostedServices/InvoiceWatcher.cs b/BTCPayServer/HostedServices/InvoiceWatcher.cs index a9ac1160a..aa88ba73b 100644 --- a/BTCPayServer/HostedServices/InvoiceWatcher.cs +++ b/BTCPayServer/HostedServices/InvoiceWatcher.cs @@ -74,7 +74,7 @@ namespace BTCPayServer.HostedServices } var payments = invoice.GetPayments().Where(p => p.Accounted).ToArray(); - var allPaymentMethods = invoice.GetPaymentMethods(_NetworkProvider); + var allPaymentMethods = invoice.GetPaymentMethods(); var paymentMethod = GetNearestClearedPayment(allPaymentMethods, out var accounting, _NetworkProvider); if (paymentMethod == null) return; diff --git a/BTCPayServer/Hosting/BTCPayServerServices.cs b/BTCPayServer/Hosting/BTCPayServerServices.cs index fe25aba4a..c198e2448 100644 --- a/BTCPayServer/Hosting/BTCPayServerServices.cs +++ b/BTCPayServer/Hosting/BTCPayServerServices.cs @@ -74,7 +74,7 @@ namespace BTCPayServer.Hosting var dbpath = Path.Combine(opts.DataDir, "InvoiceDB"); if (!Directory.Exists(dbpath)) Directory.CreateDirectory(dbpath); - return new InvoiceRepository(dbContext, dbpath); + return new InvoiceRepository(dbContext, dbpath, o.GetRequiredService()); }); services.AddSingleton(); services.TryAddSingleton(); diff --git a/BTCPayServer/Payments/Bitcoin/NBXplorerListener.cs b/BTCPayServer/Payments/Bitcoin/NBXplorerListener.cs index 6a5c3b096..9bc744b81 100644 --- a/BTCPayServer/Payments/Bitcoin/NBXplorerListener.cs +++ b/BTCPayServer/Payments/Bitcoin/NBXplorerListener.cs @@ -345,7 +345,7 @@ namespace BTCPayServer.Payments.Bitcoin private DerivationStrategyBase GetDerivationStrategy(InvoiceEntity invoice, BTCPayNetwork network) { - return invoice.GetSupportedPaymentMethod(new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike), _ExplorerClients.NetworkProviders) + return invoice.GetSupportedPaymentMethod(new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike)) .Select(d => d.AccountDerivation) .FirstOrDefault(); } diff --git a/BTCPayServer/Payments/Lightning/LightningListener.cs b/BTCPayServer/Payments/Lightning/LightningListener.cs index f2224858e..3e4ad8df8 100644 --- a/BTCPayServer/Payments/Lightning/LightningListener.cs +++ b/BTCPayServer/Payments/Lightning/LightningListener.cs @@ -95,13 +95,13 @@ namespace BTCPayServer.Payments.Lightning { var listenedInvoices = new List(); var invoice = await _InvoiceRepository.GetInvoice(invoiceId); - foreach (var paymentMethod in invoice.GetPaymentMethods(_NetworkProvider) + foreach (var paymentMethod in invoice.GetPaymentMethods() .Where(c => c.GetId().PaymentType == PaymentTypes.LightningLike)) { var lightningMethod = paymentMethod.GetPaymentMethodDetails() as LightningLikePaymentMethodDetails; if (lightningMethod == null) continue; - var lightningSupportedMethod = invoice.GetSupportedPaymentMethod(_NetworkProvider) + var lightningSupportedMethod = invoice.GetSupportedPaymentMethod() .FirstOrDefault(c => c.CryptoCode == paymentMethod.GetId().CryptoCode); if (lightningSupportedMethod == null) continue; diff --git a/BTCPayServer/Services/Invoices/InvoiceEntity.cs b/BTCPayServer/Services/Invoices/InvoiceEntity.cs index cb70d6581..353f7eb48 100644 --- a/BTCPayServer/Services/Invoices/InvoiceEntity.cs +++ b/BTCPayServer/Services/Invoices/InvoiceEntity.cs @@ -113,6 +113,8 @@ namespace BTCPayServer.Services.Invoices } public class InvoiceEntity { + [JsonIgnore] + public BTCPayNetworkProvider Networks { get; set; } public const int InternalTagSupport_Version = 1; public const int Lastest_Version = 1; public int Version { get; set; } @@ -190,18 +192,18 @@ namespace BTCPayServer.Services.Invoices get; set; } - public IEnumerable GetSupportedPaymentMethod(PaymentMethodId paymentMethodId, BTCPayNetworkProvider networks) where T : ISupportedPaymentMethod + public IEnumerable GetSupportedPaymentMethod(PaymentMethodId paymentMethodId) where T : ISupportedPaymentMethod { return - GetSupportedPaymentMethod(networks) + GetSupportedPaymentMethod() .Where(p => paymentMethodId == null || p.PaymentId == paymentMethodId) .OfType(); } - public IEnumerable GetSupportedPaymentMethod(BTCPayNetworkProvider networks) where T : ISupportedPaymentMethod + public IEnumerable GetSupportedPaymentMethod() where T : ISupportedPaymentMethod { - return GetSupportedPaymentMethod(null, networks); + return GetSupportedPaymentMethod(null); } - public IEnumerable GetSupportedPaymentMethod(BTCPayNetworkProvider networks) + public IEnumerable GetSupportedPaymentMethod() { #pragma warning disable CS0618 bool btcReturned = false; @@ -211,10 +213,10 @@ namespace BTCPayServer.Services.Invoices foreach (var strat in strategies.Properties()) { var paymentMethodId = PaymentMethodId.Parse(strat.Name); - var network = networks.GetNetwork(paymentMethodId.CryptoCode); + var network = Networks.GetNetwork(paymentMethodId.CryptoCode); if (network != null) { - if (network == networks.BTC && paymentMethodId.PaymentType == PaymentTypes.BTCLike) + if (network == Networks.BTC && paymentMethodId.PaymentType == PaymentTypes.BTCLike) btcReturned = true; yield return PaymentMethodExtensions.Deserialize(paymentMethodId, strat.Value, network); } @@ -223,9 +225,9 @@ namespace BTCPayServer.Services.Invoices if (!btcReturned && !string.IsNullOrEmpty(DerivationStrategy)) { - if (networks.BTC != null) + if (Networks.BTC != null) { - yield return BTCPayServer.DerivationSchemeSettings.Parse(DerivationStrategy, networks.BTC); + yield return BTCPayServer.DerivationSchemeSettings.Parse(DerivationStrategy, Networks.BTC); } } #pragma warning restore CS0618 @@ -362,7 +364,7 @@ namespace BTCPayServer.Services.Invoices return DateTimeOffset.UtcNow > ExpirationTime; } - public InvoiceResponse EntityToDTO(BTCPayNetworkProvider networkProvider) + public InvoiceResponse EntityToDTO() { ServerUrl = ServerUrl ?? ""; InvoiceResponse dto = new InvoiceResponse @@ -391,7 +393,7 @@ namespace BTCPayServer.Services.Invoices dto.Url = ServerUrl.WithTrailingSlash() + $"invoice?id=" + Id; dto.CryptoInfo = new List(); dto.MinerFees = new Dictionary(); - foreach (var info in this.GetPaymentMethods(networkProvider)) + foreach (var info in this.GetPaymentMethods()) { var accounting = info.Calculate(); var cryptoInfo = new NBitpayClient.InvoiceCryptoInfo(); @@ -510,13 +512,13 @@ namespace BTCPayServer.Services.Invoices internal bool Support(PaymentMethodId paymentMethodId) { - var rates = GetPaymentMethods(null); + var rates = GetPaymentMethods(); return rates.TryGet(paymentMethodId) != null; } public PaymentMethod GetPaymentMethod(PaymentMethodId paymentMethodId, BTCPayNetworkProvider networkProvider) { - GetPaymentMethods(networkProvider).TryGetValue(paymentMethodId, out var data); + GetPaymentMethods().TryGetValue(paymentMethodId, out var data); return data; } public PaymentMethod GetPaymentMethod(BTCPayNetwork network, PaymentTypes paymentType, BTCPayNetworkProvider networkProvider) @@ -524,7 +526,7 @@ namespace BTCPayServer.Services.Invoices return GetPaymentMethod(new PaymentMethodId(network.CryptoCode, paymentType), networkProvider); } - public PaymentMethodDictionary GetPaymentMethods(BTCPayNetworkProvider networkProvider) + public PaymentMethodDictionary GetPaymentMethods() { PaymentMethodDictionary paymentMethods = new PaymentMethodDictionary(); var serializer = new Serializer(Dummy); @@ -538,8 +540,8 @@ namespace BTCPayServer.Services.Invoices r.CryptoCode = paymentMethodId.CryptoCode; r.PaymentType = paymentMethodId.PaymentType.ToString(); r.ParentEntity = this; - r.Network = networkProvider?.GetNetwork(r.CryptoCode); - if (r.Network != null || networkProvider == null) + r.Network = Networks?.GetNetwork(r.CryptoCode); + if (r.Network != null || Networks == null) paymentMethods.Add(r); } } @@ -551,7 +553,7 @@ namespace BTCPayServer.Services.Invoices public void SetPaymentMethod(PaymentMethod paymentMethod) { - var dict = GetPaymentMethods(null); + var dict = GetPaymentMethods(); dict.AddOrReplace(paymentMethod); SetPaymentMethods(dict); } @@ -830,7 +832,7 @@ namespace BTCPayServer.Services.Invoices public PaymentMethodAccounting Calculate(Func paymentPredicate = null) { paymentPredicate = paymentPredicate ?? new Func((p) => true); - var paymentMethods = ParentEntity.GetPaymentMethods(null); + var paymentMethods = ParentEntity.GetPaymentMethods(); var totalDue = ParentEntity.ProductInformation.Price / Rate; var paid = 0m; diff --git a/BTCPayServer/Services/Invoices/InvoiceRepository.cs b/BTCPayServer/Services/Invoices/InvoiceRepository.cs index 57b02f414..5d4467e1f 100644 --- a/BTCPayServer/Services/Invoices/InvoiceRepository.cs +++ b/BTCPayServer/Services/Invoices/InvoiceRepository.cs @@ -37,8 +37,9 @@ namespace BTCPayServer.Services.Invoices } private ApplicationDbContextFactory _ContextFactory; + private readonly BTCPayNetworkProvider _Networks; private CustomThreadPool _IndexerThread; - public InvoiceRepository(ApplicationDbContextFactory contextFactory, string dbreezePath) + public InvoiceRepository(ApplicationDbContextFactory contextFactory, string dbreezePath, BTCPayNetworkProvider networks) { int retryCount = 0; retry: @@ -49,6 +50,7 @@ retry: catch when (retryCount++ < 5) { goto retry; } _IndexerThread = new CustomThreadPool(1, "Invoice Indexer"); _ContextFactory = contextFactory; + _Networks = networks; } public async Task RemovePendingInvoice(string invoiceId) @@ -118,10 +120,11 @@ retry: } } - public async Task CreateInvoiceAsync(string storeId, InvoiceEntity invoice, BTCPayNetworkProvider networkProvider) + public async Task CreateInvoiceAsync(string storeId, InvoiceEntity invoice) { List textSearch = new List(); - invoice = Clone(invoice, null); + invoice = NBitcoin.JsonConverters.Serializer.ToObject(ToString(invoice, null), null); + invoice.Networks = _Networks; invoice.Id = Encoders.Base58.EncodeData(RandomUtils.GetBytes(16)); #pragma warning disable CS0618 invoice.Payments = new List(); @@ -143,7 +146,7 @@ retry: CustomerEmail = invoice.RefundMail }); - foreach (var paymentMethod in invoice.GetPaymentMethods(networkProvider)) + foreach (var paymentMethod in invoice.GetPaymentMethods()) { if (paymentMethod.Network == null) throw new InvalidOperationException("CryptoCode unsupported"); @@ -217,7 +220,7 @@ retry: if (invoice == null) return false; - var invoiceEntity = ToObject(invoice.Blob, network.NBitcoinNetwork); + var invoiceEntity = ToObject(invoice.Blob); var currencyData = invoiceEntity.GetPaymentMethod(network, paymentMethod.GetPaymentType(), null); if (currencyData == null) return false; @@ -278,7 +281,7 @@ retry: private static void MarkUnassigned(string invoiceId, InvoiceEntity entity, ApplicationDbContext context, PaymentMethodId paymentMethodId) { - foreach (var address in entity.GetPaymentMethods(null)) + foreach (var address in entity.GetPaymentMethods()) { if (paymentMethodId != null && paymentMethodId != address.GetId()) continue; @@ -298,7 +301,7 @@ retry: var invoiceData = await context.FindAsync(invoiceId).ConfigureAwait(false); if (invoiceData == null) return; - var invoiceEntity = ToObject(invoiceData.Blob, null); + var invoiceEntity = ToObject(invoiceData.Blob); MarkUnassigned(invoiceId, invoiceEntity, context, null); try { @@ -393,7 +396,7 @@ retry: private InvoiceEntity ToEntity(Data.InvoiceData invoice) { - var entity = ToObject(invoice.Blob, null); + var entity = ToObject(invoice.Blob); PaymentMethodDictionary paymentMethods = null; #pragma warning disable CS0618 entity.Payments = invoice.Payments.Select(p => @@ -406,7 +409,7 @@ retry: if (paymentEntity.Version == 0) { if (paymentMethods == null) - paymentMethods = entity.GetPaymentMethods(null); + paymentMethods = entity.GetPaymentMethods(); var paymentMethodDetails = paymentMethods.TryGet(paymentEntity.GetPaymentMethodId())?.GetPaymentMethodDetails(); if (paymentMethodDetails != null) // == null should never happen, but we never know. paymentEntity.NetworkFee = paymentMethodDetails.GetNextNetworkFee(); @@ -604,7 +607,7 @@ retry: var invoice = context.Invoices.Find(invoiceId); if (invoice == null) return null; - InvoiceEntity invoiceEntity = ToObject(invoice.Blob, network.NBitcoinNetwork); + InvoiceEntity invoiceEntity = ToObject(invoice.Blob); PaymentMethod paymentMethod = invoiceEntity.GetPaymentMethod(new PaymentMethodId(network.CryptoCode, paymentData.GetPaymentType()), null); IPaymentMethodDetails paymentMethodDetails = paymentMethod.GetPaymentMethodDetails(); PaymentEntity entity = new PaymentEntity @@ -669,21 +672,23 @@ retry: } } + private InvoiceEntity ToObject(byte[] value) + { + var entity = NBitcoin.JsonConverters.Serializer.ToObject(ZipUtils.Unzip(value), null); + entity.Networks = _Networks; + return entity; + } private T ToObject(byte[] value, Network network) { return NBitcoin.JsonConverters.Serializer.ToObject(ZipUtils.Unzip(value), network); } + private byte[] ToBytes(T obj, Network network) { return ZipUtils.Zip(NBitcoin.JsonConverters.Serializer.ToString(obj, network)); } - private T Clone(T invoice, Network network) - { - return NBitcoin.JsonConverters.Serializer.ToObject(ToString(invoice, network), network); - } - private string ToString(T data, Network network) { return NBitcoin.JsonConverters.Serializer.ToString(data, network);