From ae32fdeea7b709762a65efdd48aa32ba1a219fb0 Mon Sep 17 00:00:00 2001 From: Andrew Camilleri Date: Tue, 29 Dec 2020 09:58:35 +0100 Subject: [PATCH] Introduce Additional Data to Store Blob and move obsolete props to migration (#2065) * Introduce Additional Data to Store Blob and move obsolete props to migration * Fixes and tests * Small adjustements to prevent tracking too many objects Co-authored-by: nicolas.dorier --- BTCPayServer.Tests/UnitTest1.cs | 112 ++++++++---- BTCPayServer/Controllers/InvoiceController.cs | 4 +- BTCPayServer/Controllers/StoresController.cs | 36 ++-- BTCPayServer/CurrencyValue.cs | 4 + BTCPayServer/Data/StoreBlob.cs | 45 +---- BTCPayServer/Data/StoreDataExtensions.cs | 40 ---- BTCPayServer/Hosting/MigrationStartupTask.cs | 171 +++++++++++++++--- BTCPayServer/Services/MigrationSettings.cs | 3 + 8 files changed, 262 insertions(+), 153 deletions(-) diff --git a/BTCPayServer.Tests/UnitTest1.cs b/BTCPayServer.Tests/UnitTest1.cs index 297e40a9f..7fed2f47e 100644 --- a/BTCPayServer.Tests/UnitTest1.cs +++ b/BTCPayServer.Tests/UnitTest1.cs @@ -11,6 +11,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using BTCPayServer.Abstractions.Contracts; using BTCPayServer.Abstractions.Models; using BTCPayServer.Client; using BTCPayServer.Client.Models; @@ -19,6 +20,7 @@ using BTCPayServer.Controllers; using BTCPayServer.Data; using BTCPayServer.Events; using BTCPayServer.HostedServices; +using BTCPayServer.Hosting; using BTCPayServer.Lightning; using BTCPayServer.Models; using BTCPayServer.Models.AccountViewModels; @@ -45,10 +47,12 @@ using DBriize.Utils; using ExchangeSharp; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; using NBitcoin; using NBitcoin.DataEncoders; using NBitcoin.Payment; using NBitpayClient; +using NBXplorer; using NBXplorer.DerivationStrategy; using NBXplorer.Models; using Newtonsoft.Json; @@ -2118,21 +2122,6 @@ namespace BTCPayServer.Tests Assert.Single(invoice.CryptoInfo); Assert.Equal(PaymentTypes.BTCLike.ToString(), invoice.CryptoInfo[0].PaymentType); - - //test backward compat - var store = await tester.PayTester.StoreRepository.FindStore(user.StoreId); - var blob = store.GetStoreBlob(); - blob.PaymentMethodCriteria = new List(); -#pragma warning disable 612 - blob.OnChainMinValue = new CurrencyValue() -#pragma warning restore 612 - { - Currency = "USD", - Value = 2m - }; - var criteriaCompat = store.GetPaymentMethodCriteria(tester.NetworkProvider, blob); - Assert.Single(criteriaCompat); - Assert.NotNull(criteriaCompat.FirstOrDefault(methodCriteria => methodCriteria.Value.ToString() == "2 USD" && methodCriteria.Above && methodCriteria.PaymentMethod == new PaymentMethodId("BTC", BitcoinPaymentType.Instance))); } } @@ -2229,22 +2218,6 @@ namespace BTCPayServer.Tests Assert.Single(invoice.CryptoInfo); Assert.Equal(PaymentTypes.LightningLike.ToString(), invoice.CryptoInfo[0].PaymentType); - - //test backward compat - var store = await tester.PayTester.StoreRepository.FindStore(user.StoreId); - var blob = store.GetStoreBlob(); - blob.PaymentMethodCriteria = new List(); -#pragma warning disable 612 - blob.LightningMaxValue = new CurrencyValue() -#pragma warning restore 612 - { - Currency = "USD", - Value = 2m - }; - var criteriaCompat = store.GetPaymentMethodCriteria(tester.NetworkProvider, blob); - Assert.Single(criteriaCompat); - Assert.NotNull(criteriaCompat.FirstOrDefault(methodCriteria => methodCriteria.Value.ToString() == "2 USD" && !methodCriteria.Above && methodCriteria.PaymentMethod == new PaymentMethodId("BTC", LightningPaymentType.Instance))); - } } @@ -3437,6 +3410,83 @@ namespace BTCPayServer.Tests } } + [Fact(Timeout = TestTimeout)] + [Trait("Integration", "Integration")] + public async Task CanDoInvoiceMigrations() + { + using (var tester = ServerTester.Create(newDb: true)) + { + await tester.StartAsync(); + + var acc = tester.NewAccount(); + await acc.GrantAccessAsync(true); + await acc.CreateStoreAsync(); + await acc.RegisterDerivationSchemeAsync("BTC"); + var store = await tester.PayTester.StoreRepository.FindStore(acc.StoreId); + + var blob = store.GetStoreBlob(); + var serializer = new Serializer(null); + + blob.AdditionalData = new Dictionary(); + blob.AdditionalData.Add("rateRules", JToken.Parse( + serializer.ToString(new List() + { + new MigrationStartupTask.RateRule_Obsolete() + { + Multiplier = 2 + } + }))); + blob.AdditionalData.Add("walletKeyPathRoots", JToken.Parse( + serializer.ToString(new Dictionary() + { + { + new PaymentMethodId("BTC", BitcoinPaymentType.Instance).ToString(), + new KeyPath("44'/0'/0'").ToString() + } + }))); + + blob.AdditionalData.Add("networkFeeDisabled", JToken.Parse( + serializer.ToString((bool?)true))); + + blob.AdditionalData.Add("onChainMinValue", JToken.Parse( + serializer.ToString(new CurrencyValue() + { + Currency = "USD", + Value = 5m + }.ToString()))); + blob.AdditionalData.Add("lightningMaxValue", JToken.Parse( + serializer.ToString(new CurrencyValue() + { + Currency = "USD", + Value = 5m + }.ToString()))); + + store.SetStoreBlob(blob); + await tester.PayTester.StoreRepository.UpdateStore(store); + var settings = tester.PayTester.GetService(); + await settings.UpdateSetting(new MigrationSettings()); + var migrationStartupTask = tester.PayTester.GetService().GetServices() + .Single(task => task is MigrationStartupTask); + await migrationStartupTask.ExecuteAsync(); + + + store = await tester.PayTester.StoreRepository.FindStore(acc.StoreId); + + blob = store.GetStoreBlob(); + Assert.Empty(blob.AdditionalData); + Assert.Single(blob.PaymentMethodCriteria); + Assert.Contains(blob.PaymentMethodCriteria, + criteria => criteria.PaymentMethod == new PaymentMethodId("BTC", BitcoinPaymentType.Instance) && + criteria.Above && criteria.Value.Value == 5m && criteria.Value.Currency == "USD"); + Assert.Equal(NetworkFeeMode.Never, blob.NetworkFeeMode); + Assert.Contains(store.GetSupportedPaymentMethods(tester.NetworkProvider), method => + method is DerivationSchemeSettings dss && + method.PaymentId == new PaymentMethodId("BTC", BitcoinPaymentType.Instance) && + dss.AccountKeyPath == new KeyPath("44'/0'/0'")); + + } + } + [Fact(Timeout = TestTimeout)] [Trait("Integration", "Integration")] public async Task EmailSenderTests() diff --git a/BTCPayServer/Controllers/InvoiceController.cs b/BTCPayServer/Controllers/InvoiceController.cs index 386c1d5b4..642e7080e 100644 --- a/BTCPayServer/Controllers/InvoiceController.cs +++ b/BTCPayServer/Controllers/InvoiceController.cs @@ -217,7 +217,7 @@ namespace BTCPayServer.Controllers .Where(c => c != null)) { currencyPairsToFetch.Add(new CurrencyPair(network.CryptoCode, entity.Currency)); - foreach (var paymentMethodCriteria in store.GetPaymentMethodCriteria(_NetworkProvider, storeBlob)) + foreach (var paymentMethodCriteria in storeBlob.PaymentMethodCriteria) { if (paymentMethodCriteria.Value != null) { @@ -341,7 +341,7 @@ namespace BTCPayServer.Controllers paymentMethod.SetPaymentMethodDetails(paymentDetails); } - var criteria = store.GetPaymentMethodCriteria(_NetworkProvider, storeBlob)?.Find(methodCriteria => methodCriteria.PaymentMethod == supportedPaymentMethod.PaymentId); + var criteria = storeBlob.PaymentMethodCriteria?.Find(methodCriteria => methodCriteria.PaymentMethod == supportedPaymentMethod.PaymentId); if (criteria?.Value != null) { var currentRateToCrypto = diff --git a/BTCPayServer/Controllers/StoresController.cs b/BTCPayServer/Controllers/StoresController.cs index 69d4406c5..1c0bbc9ef 100644 --- a/BTCPayServer/Controllers/StoresController.cs +++ b/BTCPayServer/Controllers/StoresController.cs @@ -372,15 +372,31 @@ namespace BTCPayServer.Controllers var storeBlob = CurrentStore.GetStoreBlob(); var vm = new CheckoutExperienceViewModel(); SetCryptoCurrencies(vm, CurrentStore); - vm.PaymentMethodCriteria = - CurrentStore.GetPaymentMethodCriteria(_NetworkProvider, storeBlob).Select(criteria => - new PaymentMethodCriteriaViewModel() + vm.PaymentMethodCriteria = CurrentStore.GetSupportedPaymentMethods(_NetworkProvider).Select(method => + { + var existing = + storeBlob.PaymentMethodCriteria.SingleOrDefault(criteria => + criteria.PaymentMethod == method.PaymentId); + if (existing is null) + { + return new PaymentMethodCriteriaViewModel() { - PaymentMethod = criteria.PaymentMethod.ToString(), - Type = criteria.Above ? PaymentMethodCriteriaViewModel.CriteriaType.GreaterThan : PaymentMethodCriteriaViewModel.CriteriaType.LessThan, - Value = criteria.Value?.ToString() ?? "" - }).ToList(); - + PaymentMethod = method.PaymentId.ToString(), + Value = "" + }; + } + else + { + return new PaymentMethodCriteriaViewModel() + { + PaymentMethod = existing.PaymentMethod.ToString(), + Type = existing.Above + ? PaymentMethodCriteriaViewModel.CriteriaType.GreaterThan + : PaymentMethodCriteriaViewModel.CriteriaType.LessThan, + Value = existing.Value?.ToString() ?? "" + }; + } + }).ToList(); vm.RequiresRefundEmail = storeBlob.RequiresRefundEmail; vm.LightningAmountInSatoshi = storeBlob.LightningAmountInSatoshi; vm.LightningPrivateRouteHints = storeBlob.LightningPrivateRouteHints; @@ -454,10 +470,6 @@ namespace BTCPayServer.Controllers CurrencyValue.TryParse(viewModel.Value, out var cv); return new PaymentMethodCriteria() { Above = viewModel.Type == PaymentMethodCriteriaViewModel.CriteriaType.GreaterThan, Value = cv, PaymentMethod = PaymentMethodId.Parse(viewModel.PaymentMethod) }; }).ToList(); -#pragma warning disable 612 - blob.LightningMaxValue = null; - blob.OnChainMinValue = null; -#pragma warning restore 612 blob.RequiresRefundEmail = model.RequiresRefundEmail; blob.LightningAmountInSatoshi = model.LightningAmountInSatoshi; diff --git a/BTCPayServer/CurrencyValue.cs b/BTCPayServer/CurrencyValue.cs index bfbbd86de..39ec0190f 100644 --- a/BTCPayServer/CurrencyValue.cs +++ b/BTCPayServer/CurrencyValue.cs @@ -11,6 +11,10 @@ namespace BTCPayServer public static bool TryParse(string str, out CurrencyValue value) { value = null; + if (string.IsNullOrEmpty(str)) + { + return false; + } var match = _Regex.Match(str); if (!match.Success || !decimal.TryParse(match.Groups[1].Value, out var v)) diff --git a/BTCPayServer/Data/StoreBlob.cs b/BTCPayServer/Data/StoreBlob.cs index 79d3f8d65..2a6ac0534 100644 --- a/BTCPayServer/Data/StoreBlob.cs +++ b/BTCPayServer/Data/StoreBlob.cs @@ -13,6 +13,7 @@ using BTCPayServer.Services.Mails; using BTCPayServer.Services.Rates; using BTCPayServer.Services.Shopify.Models; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace BTCPayServer.Data { @@ -25,14 +26,11 @@ namespace BTCPayServer.Data PaymentTolerance = 0; ShowRecommendedFee = true; RecommendedFeeBlockTarget = 1; + PaymentMethodCriteria = new List(); } public ShopifySettings Shopify { get; set; } - [Obsolete("Use NetworkFeeMode instead")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public bool? NetworkFeeDisabled { get; set; } - [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] public NetworkFeeMode NetworkFeeMode { get; set; } @@ -80,24 +78,9 @@ namespace BTCPayServer.Data public decimal Spread { get; set; } = 0.0m; - [Obsolete] - public List RateRules { get; set; } = new List(); public string PreferredExchange { get; set; } - public List PaymentMethodCriteria - { - [Obsolete("Use GetPaymentMethodCriteria instead")] - get; - set; - } - - [Obsolete] - [JsonConverter(typeof(CurrencyValueJsonConverter))] - public CurrencyValue OnChainMinValue { get; set; } - [Obsolete] - [JsonConverter(typeof(CurrencyValueJsonConverter))] - public CurrencyValue LightningMaxValue { get; set; } - + public List PaymentMethodCriteria { get; set; } public string CustomCSS { get; set; } public string CustomLogo { get; set; } public string HtmlTitle { get; set; } @@ -170,14 +153,14 @@ namespace BTCPayServer.Data [Obsolete("Use GetExcludedPaymentMethods instead")] public string[] ExcludedPaymentMethods { get; set; } - [Obsolete("Use DerivationSchemeSettings instead")] - public Dictionary WalletKeyPathRoots { get; set; } - public EmailSettings EmailSettings { get; set; } public bool PayJoinEnabled { get; set; } public StoreHints Hints { get; set; } + [JsonExtensionData] + public IDictionary AdditionalData { get; set; } = new Dictionary(); + public class StoreHints { public bool Wallet { get; set; } @@ -218,20 +201,4 @@ namespace BTCPayServer.Data public CurrencyValue Value { get; set; } public bool Above { get; set; } } - - public class RateRule_Obsolete - { - public RateRule_Obsolete() - { - RuleName = "Multiplier"; - } - public string RuleName { get; set; } - - public double Multiplier { get; set; } - - public decimal Apply(BTCPayNetworkBase network, decimal rate) - { - return rate * (decimal)Multiplier; - } - } } diff --git a/BTCPayServer/Data/StoreDataExtensions.cs b/BTCPayServer/Data/StoreDataExtensions.cs index 3de895543..eb0d36e50 100644 --- a/BTCPayServer/Data/StoreDataExtensions.cs +++ b/BTCPayServer/Data/StoreDataExtensions.cs @@ -52,46 +52,6 @@ namespace BTCPayServer.Data return result; } - public static List GetPaymentMethodCriteria(this StoreData storeData, BTCPayNetworkProvider networkProvider,StoreBlob storeBlob = null) - { -#pragma warning disable 612 - storeBlob ??= storeData.GetStoreBlob(); - - return storeData.GetEnabledPaymentIds(networkProvider).Select(paymentMethodId=> - { - var matchedFromBlob = -#pragma warning disable CS0618 // Type or member is obsolete - storeBlob.PaymentMethodCriteria?.SingleOrDefault(criteria => criteria.PaymentMethod == paymentMethodId && criteria.Value != null); -#pragma warning restore CS0618 // Type or member is obsolete - if (matchedFromBlob is null && paymentMethodId.PaymentType == LightningPaymentType.Instance && storeBlob.LightningMaxValue != null) - { - return new PaymentMethodCriteria() - { - Above = false, - PaymentMethod = paymentMethodId, - Value = storeBlob.LightningMaxValue - }; - } - if (matchedFromBlob is null && paymentMethodId.PaymentType == BitcoinPaymentType.Instance && storeBlob.OnChainMinValue != null) - { - return new PaymentMethodCriteria() - { - Above = true, - PaymentMethod = paymentMethodId, - Value = storeBlob.OnChainMinValue - }; - } - - return new PaymentMethodCriteria() - { - PaymentMethod = paymentMethodId, - Above = matchedFromBlob?.Above??true, - Value = matchedFromBlob?.Value - }; - }).ToList(); -#pragma warning restore 612 - } - public static bool SetStoreBlob(this StoreData storeData, StoreBlob storeBlob) { var original = new Serializer(null).ToString(storeData.GetStoreBlob()); diff --git a/BTCPayServer/Hosting/MigrationStartupTask.cs b/BTCPayServer/Hosting/MigrationStartupTask.cs index 77d48ef15..3d09b1954 100644 --- a/BTCPayServer/Hosting/MigrationStartupTask.cs +++ b/BTCPayServer/Hosting/MigrationStartupTask.cs @@ -10,6 +10,7 @@ using BTCPayServer.Client.Models; using BTCPayServer.Configuration; using BTCPayServer.Data; using BTCPayServer.Logging; +using BTCPayServer.Payments; using BTCPayServer.Services; using BTCPayServer.Services.Stores; using DBriize; @@ -18,6 +19,8 @@ using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using NBitcoin.DataEncoders; +using NBXplorer; +using Newtonsoft.Json.Linq; namespace BTCPayServer.Hosting { @@ -27,21 +30,18 @@ namespace BTCPayServer.Hosting private readonly StoreRepository _StoreRepository; private readonly BTCPayNetworkProvider _NetworkProvider; private readonly SettingsRepository _Settings; - private readonly BTCPayServerOptions _btcPayServerOptions; private readonly UserManager _userManager; public MigrationStartupTask( BTCPayNetworkProvider networkProvider, StoreRepository storeRepository, ApplicationDbContextFactory dbContextFactory, UserManager userManager, - SettingsRepository settingsRepository, - BTCPayServerOptions btcPayServerOptions) + SettingsRepository settingsRepository) { _DBContextFactory = dbContextFactory; _StoreRepository = storeRepository; _NetworkProvider = networkProvider; _Settings = settingsRepository; - _btcPayServerOptions = btcPayServerOptions; _userManager = userManager; } public async Task ExecuteAsync(CancellationToken cancellationToken = default) @@ -50,6 +50,12 @@ namespace BTCPayServer.Hosting { await Migrate(cancellationToken); var settings = (await _Settings.GetSettingAsync()) ?? new MigrationSettings(); + if (!settings.PaymentMethodCriteria) + { + await MigratePaymentMethodCriteria(); + settings.PaymentMethodCriteria = true; + await _Settings.UpdateSetting(settings); + } if (!settings.DeprecatedLightningConnectionStringCheck) { await DeprecatedLightningConnectionStringCheck(); @@ -95,6 +101,13 @@ namespace BTCPayServer.Hosting settings.CheckedFirstRun = true; await _Settings.UpdateSetting(settings); } + + if (!settings.TransitionToStoreBlobAdditionalData) + { + await TransitionToStoreBlobAdditionalData(); + settings.TransitionToStoreBlobAdditionalData = true; + await _Settings.UpdateSetting(settings); + } } catch (Exception ex) { @@ -103,6 +116,23 @@ namespace BTCPayServer.Hosting } } + private async Task TransitionToStoreBlobAdditionalData() + { + await using var ctx = _DBContextFactory.CreateContext(); + foreach (var store in await ctx.Stores.AsNoTracking().ToArrayAsync()) + { + var blob = store.GetStoreBlob(); + blob.AdditionalData.Remove("walletKeyPathRoots"); + blob.AdditionalData.Remove("onChainMinValue"); + blob.AdditionalData.Remove("lightningMaxValue"); + blob.AdditionalData.Remove("networkFeeDisabled"); + blob.AdditionalData.Remove("rateRules"); + store.SetStoreBlob(blob); + } + + await ctx.SaveChangesAsync(); + } + private async Task Migrate(CancellationToken cancellationToken) { using (CancellationTokenSource timeout = new CancellationTokenSource(10_000)) @@ -131,23 +161,32 @@ retry: bool save = false; using (var ctx = _DBContextFactory.CreateContext()) { - foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync()) + foreach (var store in await ctx.Stores.AsNoTracking().ToArrayAsync()) { #pragma warning disable CS0618 // Type or member is obsolete var blob = store.GetStoreBlob(); - if (blob.WalletKeyPathRoots == null) - continue; - foreach (var scheme in store.GetSupportedPaymentMethods(_NetworkProvider).OfType()) + + if (blob.AdditionalData.TryGetValue("walletKeyPathRoots", out var walletKeyPathRootsJToken)) { - if (blob.WalletKeyPathRoots.TryGetValue(scheme.PaymentId.ToString().ToLowerInvariant(), out var root)) + var walletKeyPathRoots = walletKeyPathRootsJToken.ToObject>(); + + if (!(walletKeyPathRoots?.Any() is true)) + continue; + foreach (var scheme in store.GetSupportedPaymentMethods(_NetworkProvider) + .OfType()) { - scheme.AccountKeyPath = new NBitcoin.KeyPath(root); - store.SetSupportedPaymentMethod(scheme); - save = true; + if (walletKeyPathRoots.TryGetValue(scheme.PaymentId.ToString().ToLowerInvariant(), + out var root)) + { + scheme.AccountKeyPath = new NBitcoin.KeyPath(root); + store.SetSupportedPaymentMethod(scheme); + save = true; + } } + + blob.AdditionalData.Remove("walletKeyPathRoots"); + store.SetStoreBlob(blob); } - blob.WalletKeyPathRoots = null; - store.SetStoreBlob(blob); #pragma warning restore CS0618 // Type or member is obsolete } if (save) @@ -173,6 +212,58 @@ retry: } } + private async Task MigratePaymentMethodCriteria() + { + using (var ctx = _DBContextFactory.CreateContext()) + { + foreach (var store in await ctx.Stores.AsNoTracking().ToArrayAsync()) + { + var blob = store.GetStoreBlob(); + + CurrencyValue onChainMinValue = null; + CurrencyValue lightningMaxValue = null; + if (blob.AdditionalData.TryGetValue("onChainMinValue", out var onChainMinValueJToken)) + { + CurrencyValue.TryParse(onChainMinValueJToken.Value(), out onChainMinValue); + blob.AdditionalData.Remove("onChainMinValue"); + } + if (blob.AdditionalData.TryGetValue("lightningMaxValue", out var lightningMaxValueJToken)) + { + CurrencyValue.TryParse(lightningMaxValueJToken.Value(), out lightningMaxValue); + blob.AdditionalData.Remove("lightningMaxValue"); + } + blob.PaymentMethodCriteria = store.GetEnabledPaymentIds(_NetworkProvider).Select(paymentMethodId=> + { + var matchedFromBlob = + blob.PaymentMethodCriteria?.SingleOrDefault(criteria => criteria.PaymentMethod == paymentMethodId && criteria.Value != null); + return matchedFromBlob switch + { + null when paymentMethodId.PaymentType == LightningPaymentType.Instance && + lightningMaxValue != null => new PaymentMethodCriteria() + { + Above = false, PaymentMethod = paymentMethodId, Value = lightningMaxValue + }, + null when paymentMethodId.PaymentType == BitcoinPaymentType.Instance && + onChainMinValue != null => new PaymentMethodCriteria() + { + Above = true, PaymentMethod = paymentMethodId, Value = onChainMinValue + }, + _ => new PaymentMethodCriteria() + { + PaymentMethod = paymentMethodId, + Above = matchedFromBlob?.Above ?? true, + Value = matchedFromBlob?.Value + } + }; + }).ToList(); + + store.SetStoreBlob(blob); + } + + await ctx.SaveChangesAsync(); + } + } + private async Task ConvertNetworkFeeProperty() { using (var ctx = _DBContextFactory.CreateContext()) @@ -180,14 +271,17 @@ retry: foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync()) { var blob = store.GetStoreBlob(); -#pragma warning disable CS0618 // Type or member is obsolete - if (blob.NetworkFeeDisabled != null) + if (blob.AdditionalData.TryGetValue("networkFeeDisabled", out var networkFeeModeJToken)) { - blob.NetworkFeeMode = blob.NetworkFeeDisabled.Value ? NetworkFeeMode.Never : NetworkFeeMode.Always; - blob.NetworkFeeDisabled = null; + var networkFeeMode = networkFeeModeJToken.ToObject(); + if (networkFeeMode != null) + { + blob.NetworkFeeMode = networkFeeMode.Value ? NetworkFeeMode.Never : NetworkFeeMode.Always; + } + + blob.AdditionalData.Remove("networkFeeDisabled"); store.SetStoreBlob(blob); } -#pragma warning restore CS0618 // Type or member is obsolete } await ctx.SaveChangesAsync(); } @@ -197,27 +291,46 @@ retry: { using (var ctx = _DBContextFactory.CreateContext()) { - foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync()) + foreach (var store in await ctx.Stores.AsNoTracking().ToArrayAsync()) { var blob = store.GetStoreBlob(); -#pragma warning disable CS0612 // Type or member is obsolete decimal multiplier = 1.0m; - if (blob.RateRules != null && blob.RateRules.Count != 0) + if (blob.AdditionalData.TryGetValue("rateRules", out var rateRulesJToken)) { - foreach (var rule in blob.RateRules) + var rateRules = new Serializer(null).ToObject>(rateRulesJToken.ToString()); + if (rateRules != null && rateRules.Count != 0) { - multiplier = rule.Apply(null, multiplier); + foreach (var rule in rateRules) + { + multiplier = rule.Apply(null, multiplier); + } } + + blob.AdditionalData.Remove("rateRules"); + blob.Spread = Math.Min(1.0m, Math.Max(0m, -(multiplier - 1.0m))); + store.SetStoreBlob(blob); } - blob.RateRules = null; - blob.Spread = Math.Min(1.0m, Math.Max(0m, -(multiplier - 1.0m))); - store.SetStoreBlob(blob); -#pragma warning restore CS0612 // Type or member is obsolete } await ctx.SaveChangesAsync(); } } + public class RateRule_Obsolete + { + public RateRule_Obsolete() + { + RuleName = "Multiplier"; + } + public string RuleName { get; set; } + + public double Multiplier { get; set; } + + public decimal Apply(BTCPayNetworkBase network, decimal rate) + { + return rate * (decimal)Multiplier; + } + } + private Task UnreachableStoreCheck() { return _StoreRepository.CleanUnreachableStores(); @@ -227,7 +340,7 @@ retry: { using (var ctx = _DBContextFactory.CreateContext()) { - foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync()) + foreach (var store in await ctx.Stores.AsNoTracking().ToArrayAsync()) { foreach (var method in store.GetSupportedPaymentMethods(_NetworkProvider).OfType()) { diff --git a/BTCPayServer/Services/MigrationSettings.cs b/BTCPayServer/Services/MigrationSettings.cs index c81949382..20ca2a5da 100644 --- a/BTCPayServer/Services/MigrationSettings.cs +++ b/BTCPayServer/Services/MigrationSettings.cs @@ -9,6 +9,9 @@ namespace BTCPayServer.Services public bool ConvertCrowdfundOldSettings { get; set; } public bool ConvertWalletKeyPathRoots { get; set; } public bool CheckedFirstRun { get; set; } + public bool PaymentMethodCriteria { get; set; } + public bool TransitionToStoreBlobAdditionalData { get; set; } + public override string ToString() { return string.Empty;