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 <nicolas.dorier@gmail.com>
This commit is contained in:
Andrew Camilleri 2020-12-29 09:58:35 +01:00 committed by GitHub
parent 5803512820
commit ae32fdeea7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 262 additions and 153 deletions

View file

@ -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<PaymentMethodCriteria>();
#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<PaymentMethodCriteria>();
#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<string, JToken>();
blob.AdditionalData.Add("rateRules", JToken.Parse(
serializer.ToString(new List<MigrationStartupTask.RateRule_Obsolete>()
{
new MigrationStartupTask.RateRule_Obsolete()
{
Multiplier = 2
}
})));
blob.AdditionalData.Add("walletKeyPathRoots", JToken.Parse(
serializer.ToString(new Dictionary<string, string>()
{
{
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<SettingsRepository>();
await settings.UpdateSetting<MigrationSettings>(new MigrationSettings());
var migrationStartupTask = tester.PayTester.GetService<IServiceProvider>().GetServices<IStartupTask>()
.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()

View file

@ -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 =

View file

@ -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;

View file

@ -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))

View file

@ -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<PaymentMethodCriteria>();
}
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<RateRule_Obsolete> RateRules { get; set; } = new List<RateRule_Obsolete>();
public string PreferredExchange { get; set; }
public List<PaymentMethodCriteria> 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> 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<string, string> WalletKeyPathRoots { get; set; }
public EmailSettings EmailSettings { get; set; }
public bool PayJoinEnabled { get; set; }
public StoreHints Hints { get; set; }
[JsonExtensionData]
public IDictionary<string, JToken> AdditionalData { get; set; } = new Dictionary<string, JToken>();
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;
}
}
}

View file

@ -52,46 +52,6 @@ namespace BTCPayServer.Data
return result;
}
public static List<PaymentMethodCriteria> 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());

View file

@ -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<ApplicationUser> _userManager;
public MigrationStartupTask(
BTCPayNetworkProvider networkProvider,
StoreRepository storeRepository,
ApplicationDbContextFactory dbContextFactory,
UserManager<ApplicationUser> 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<MigrationSettings>()) ?? 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<DerivationSchemeSettings>())
if (blob.AdditionalData.TryGetValue("walletKeyPathRoots", out var walletKeyPathRootsJToken))
{
if (blob.WalletKeyPathRoots.TryGetValue(scheme.PaymentId.ToString().ToLowerInvariant(), out var root))
var walletKeyPathRoots = walletKeyPathRootsJToken.ToObject<Dictionary<string, string>>();
if (!(walletKeyPathRoots?.Any() is true))
continue;
foreach (var scheme in store.GetSupportedPaymentMethods(_NetworkProvider)
.OfType<DerivationSchemeSettings>())
{
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<string>(), out onChainMinValue);
blob.AdditionalData.Remove("onChainMinValue");
}
if (blob.AdditionalData.TryGetValue("lightningMaxValue", out var lightningMaxValueJToken))
{
CurrencyValue.TryParse(lightningMaxValueJToken.Value<string>(), 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<bool?>();
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<List<RateRule_Obsolete>>(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<Payments.Lightning.LightningSupportedPaymentMethod>())
{

View file

@ -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;