mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 14:34:23 +01:00
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:
@@ -11,6 +11,7 @@ using System.Text;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Abstractions.Contracts;
|
||||||
using BTCPayServer.Abstractions.Models;
|
using BTCPayServer.Abstractions.Models;
|
||||||
using BTCPayServer.Client;
|
using BTCPayServer.Client;
|
||||||
using BTCPayServer.Client.Models;
|
using BTCPayServer.Client.Models;
|
||||||
@@ -19,6 +20,7 @@ using BTCPayServer.Controllers;
|
|||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
using BTCPayServer.Events;
|
using BTCPayServer.Events;
|
||||||
using BTCPayServer.HostedServices;
|
using BTCPayServer.HostedServices;
|
||||||
|
using BTCPayServer.Hosting;
|
||||||
using BTCPayServer.Lightning;
|
using BTCPayServer.Lightning;
|
||||||
using BTCPayServer.Models;
|
using BTCPayServer.Models;
|
||||||
using BTCPayServer.Models.AccountViewModels;
|
using BTCPayServer.Models.AccountViewModels;
|
||||||
@@ -45,10 +47,12 @@ using DBriize.Utils;
|
|||||||
using ExchangeSharp;
|
using ExchangeSharp;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
using NBitcoin.DataEncoders;
|
using NBitcoin.DataEncoders;
|
||||||
using NBitcoin.Payment;
|
using NBitcoin.Payment;
|
||||||
using NBitpayClient;
|
using NBitpayClient;
|
||||||
|
using NBXplorer;
|
||||||
using NBXplorer.DerivationStrategy;
|
using NBXplorer.DerivationStrategy;
|
||||||
using NBXplorer.Models;
|
using NBXplorer.Models;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@@ -2118,21 +2122,6 @@ namespace BTCPayServer.Tests
|
|||||||
|
|
||||||
Assert.Single(invoice.CryptoInfo);
|
Assert.Single(invoice.CryptoInfo);
|
||||||
Assert.Equal(PaymentTypes.BTCLike.ToString(), invoice.CryptoInfo[0].PaymentType);
|
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.Single(invoice.CryptoInfo);
|
||||||
Assert.Equal(PaymentTypes.LightningLike.ToString(), invoice.CryptoInfo[0].PaymentType);
|
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)]
|
[Fact(Timeout = TestTimeout)]
|
||||||
[Trait("Integration", "Integration")]
|
[Trait("Integration", "Integration")]
|
||||||
public async Task EmailSenderTests()
|
public async Task EmailSenderTests()
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ namespace BTCPayServer.Controllers
|
|||||||
.Where(c => c != null))
|
.Where(c => c != null))
|
||||||
{
|
{
|
||||||
currencyPairsToFetch.Add(new CurrencyPair(network.CryptoCode, entity.Currency));
|
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)
|
if (paymentMethodCriteria.Value != null)
|
||||||
{
|
{
|
||||||
@@ -341,7 +341,7 @@ namespace BTCPayServer.Controllers
|
|||||||
paymentMethod.SetPaymentMethodDetails(paymentDetails);
|
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)
|
if (criteria?.Value != null)
|
||||||
{
|
{
|
||||||
var currentRateToCrypto =
|
var currentRateToCrypto =
|
||||||
|
|||||||
@@ -372,15 +372,31 @@ namespace BTCPayServer.Controllers
|
|||||||
var storeBlob = CurrentStore.GetStoreBlob();
|
var storeBlob = CurrentStore.GetStoreBlob();
|
||||||
var vm = new CheckoutExperienceViewModel();
|
var vm = new CheckoutExperienceViewModel();
|
||||||
SetCryptoCurrencies(vm, CurrentStore);
|
SetCryptoCurrencies(vm, CurrentStore);
|
||||||
vm.PaymentMethodCriteria =
|
vm.PaymentMethodCriteria = CurrentStore.GetSupportedPaymentMethods(_NetworkProvider).Select(method =>
|
||||||
CurrentStore.GetPaymentMethodCriteria(_NetworkProvider, storeBlob).Select(criteria =>
|
|
||||||
new PaymentMethodCriteriaViewModel()
|
|
||||||
{
|
{
|
||||||
PaymentMethod = criteria.PaymentMethod.ToString(),
|
var existing =
|
||||||
Type = criteria.Above ? PaymentMethodCriteriaViewModel.CriteriaType.GreaterThan : PaymentMethodCriteriaViewModel.CriteriaType.LessThan,
|
storeBlob.PaymentMethodCriteria.SingleOrDefault(criteria =>
|
||||||
Value = criteria.Value?.ToString() ?? ""
|
criteria.PaymentMethod == method.PaymentId);
|
||||||
|
if (existing is null)
|
||||||
|
{
|
||||||
|
return new PaymentMethodCriteriaViewModel()
|
||||||
|
{
|
||||||
|
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();
|
}).ToList();
|
||||||
|
|
||||||
vm.RequiresRefundEmail = storeBlob.RequiresRefundEmail;
|
vm.RequiresRefundEmail = storeBlob.RequiresRefundEmail;
|
||||||
vm.LightningAmountInSatoshi = storeBlob.LightningAmountInSatoshi;
|
vm.LightningAmountInSatoshi = storeBlob.LightningAmountInSatoshi;
|
||||||
vm.LightningPrivateRouteHints = storeBlob.LightningPrivateRouteHints;
|
vm.LightningPrivateRouteHints = storeBlob.LightningPrivateRouteHints;
|
||||||
@@ -454,10 +470,6 @@ namespace BTCPayServer.Controllers
|
|||||||
CurrencyValue.TryParse(viewModel.Value, out var cv);
|
CurrencyValue.TryParse(viewModel.Value, out var cv);
|
||||||
return new PaymentMethodCriteria() { Above = viewModel.Type == PaymentMethodCriteriaViewModel.CriteriaType.GreaterThan, Value = cv, PaymentMethod = PaymentMethodId.Parse(viewModel.PaymentMethod) };
|
return new PaymentMethodCriteria() { Above = viewModel.Type == PaymentMethodCriteriaViewModel.CriteriaType.GreaterThan, Value = cv, PaymentMethod = PaymentMethodId.Parse(viewModel.PaymentMethod) };
|
||||||
}).ToList();
|
}).ToList();
|
||||||
#pragma warning disable 612
|
|
||||||
blob.LightningMaxValue = null;
|
|
||||||
blob.OnChainMinValue = null;
|
|
||||||
#pragma warning restore 612
|
|
||||||
|
|
||||||
blob.RequiresRefundEmail = model.RequiresRefundEmail;
|
blob.RequiresRefundEmail = model.RequiresRefundEmail;
|
||||||
blob.LightningAmountInSatoshi = model.LightningAmountInSatoshi;
|
blob.LightningAmountInSatoshi = model.LightningAmountInSatoshi;
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ namespace BTCPayServer
|
|||||||
public static bool TryParse(string str, out CurrencyValue value)
|
public static bool TryParse(string str, out CurrencyValue value)
|
||||||
{
|
{
|
||||||
value = null;
|
value = null;
|
||||||
|
if (string.IsNullOrEmpty(str))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
var match = _Regex.Match(str);
|
var match = _Regex.Match(str);
|
||||||
if (!match.Success ||
|
if (!match.Success ||
|
||||||
!decimal.TryParse(match.Groups[1].Value, out var v))
|
!decimal.TryParse(match.Groups[1].Value, out var v))
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using BTCPayServer.Services.Mails;
|
|||||||
using BTCPayServer.Services.Rates;
|
using BTCPayServer.Services.Rates;
|
||||||
using BTCPayServer.Services.Shopify.Models;
|
using BTCPayServer.Services.Shopify.Models;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace BTCPayServer.Data
|
namespace BTCPayServer.Data
|
||||||
{
|
{
|
||||||
@@ -25,14 +26,11 @@ namespace BTCPayServer.Data
|
|||||||
PaymentTolerance = 0;
|
PaymentTolerance = 0;
|
||||||
ShowRecommendedFee = true;
|
ShowRecommendedFee = true;
|
||||||
RecommendedFeeBlockTarget = 1;
|
RecommendedFeeBlockTarget = 1;
|
||||||
|
PaymentMethodCriteria = new List<PaymentMethodCriteria>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShopifySettings Shopify { get; set; }
|
public ShopifySettings Shopify { get; set; }
|
||||||
|
|
||||||
[Obsolete("Use NetworkFeeMode instead")]
|
|
||||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
|
|
||||||
public bool? NetworkFeeDisabled { get; set; }
|
|
||||||
|
|
||||||
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
|
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
|
||||||
public NetworkFeeMode NetworkFeeMode { get; set; }
|
public NetworkFeeMode NetworkFeeMode { get; set; }
|
||||||
|
|
||||||
@@ -80,24 +78,9 @@ namespace BTCPayServer.Data
|
|||||||
|
|
||||||
public decimal Spread { get; set; } = 0.0m;
|
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 string PreferredExchange { get; set; }
|
||||||
|
|
||||||
public List<PaymentMethodCriteria> PaymentMethodCriteria
|
public List<PaymentMethodCriteria> PaymentMethodCriteria { get; set; }
|
||||||
{
|
|
||||||
[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 string CustomCSS { get; set; }
|
public string CustomCSS { get; set; }
|
||||||
public string CustomLogo { get; set; }
|
public string CustomLogo { get; set; }
|
||||||
public string HtmlTitle { get; set; }
|
public string HtmlTitle { get; set; }
|
||||||
@@ -170,14 +153,14 @@ namespace BTCPayServer.Data
|
|||||||
[Obsolete("Use GetExcludedPaymentMethods instead")]
|
[Obsolete("Use GetExcludedPaymentMethods instead")]
|
||||||
public string[] ExcludedPaymentMethods { get; set; }
|
public string[] ExcludedPaymentMethods { get; set; }
|
||||||
|
|
||||||
[Obsolete("Use DerivationSchemeSettings instead")]
|
|
||||||
public Dictionary<string, string> WalletKeyPathRoots { get; set; }
|
|
||||||
|
|
||||||
public EmailSettings EmailSettings { get; set; }
|
public EmailSettings EmailSettings { get; set; }
|
||||||
public bool PayJoinEnabled { get; set; }
|
public bool PayJoinEnabled { get; set; }
|
||||||
|
|
||||||
public StoreHints Hints { get; set; }
|
public StoreHints Hints { get; set; }
|
||||||
|
|
||||||
|
[JsonExtensionData]
|
||||||
|
public IDictionary<string, JToken> AdditionalData { get; set; } = new Dictionary<string, JToken>();
|
||||||
|
|
||||||
public class StoreHints
|
public class StoreHints
|
||||||
{
|
{
|
||||||
public bool Wallet { get; set; }
|
public bool Wallet { get; set; }
|
||||||
@@ -218,20 +201,4 @@ namespace BTCPayServer.Data
|
|||||||
public CurrencyValue Value { get; set; }
|
public CurrencyValue Value { get; set; }
|
||||||
public bool Above { 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,46 +52,6 @@ namespace BTCPayServer.Data
|
|||||||
return result;
|
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)
|
public static bool SetStoreBlob(this StoreData storeData, StoreBlob storeBlob)
|
||||||
{
|
{
|
||||||
var original = new Serializer(null).ToString(storeData.GetStoreBlob());
|
var original = new Serializer(null).ToString(storeData.GetStoreBlob());
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using BTCPayServer.Client.Models;
|
|||||||
using BTCPayServer.Configuration;
|
using BTCPayServer.Configuration;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
using BTCPayServer.Logging;
|
using BTCPayServer.Logging;
|
||||||
|
using BTCPayServer.Payments;
|
||||||
using BTCPayServer.Services;
|
using BTCPayServer.Services;
|
||||||
using BTCPayServer.Services.Stores;
|
using BTCPayServer.Services.Stores;
|
||||||
using DBriize;
|
using DBriize;
|
||||||
@@ -18,6 +19,8 @@ using Microsoft.AspNetCore.Identity;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NBitcoin.DataEncoders;
|
using NBitcoin.DataEncoders;
|
||||||
|
using NBXplorer;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace BTCPayServer.Hosting
|
namespace BTCPayServer.Hosting
|
||||||
{
|
{
|
||||||
@@ -27,21 +30,18 @@ namespace BTCPayServer.Hosting
|
|||||||
private readonly StoreRepository _StoreRepository;
|
private readonly StoreRepository _StoreRepository;
|
||||||
private readonly BTCPayNetworkProvider _NetworkProvider;
|
private readonly BTCPayNetworkProvider _NetworkProvider;
|
||||||
private readonly SettingsRepository _Settings;
|
private readonly SettingsRepository _Settings;
|
||||||
private readonly BTCPayServerOptions _btcPayServerOptions;
|
|
||||||
private readonly UserManager<ApplicationUser> _userManager;
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
public MigrationStartupTask(
|
public MigrationStartupTask(
|
||||||
BTCPayNetworkProvider networkProvider,
|
BTCPayNetworkProvider networkProvider,
|
||||||
StoreRepository storeRepository,
|
StoreRepository storeRepository,
|
||||||
ApplicationDbContextFactory dbContextFactory,
|
ApplicationDbContextFactory dbContextFactory,
|
||||||
UserManager<ApplicationUser> userManager,
|
UserManager<ApplicationUser> userManager,
|
||||||
SettingsRepository settingsRepository,
|
SettingsRepository settingsRepository)
|
||||||
BTCPayServerOptions btcPayServerOptions)
|
|
||||||
{
|
{
|
||||||
_DBContextFactory = dbContextFactory;
|
_DBContextFactory = dbContextFactory;
|
||||||
_StoreRepository = storeRepository;
|
_StoreRepository = storeRepository;
|
||||||
_NetworkProvider = networkProvider;
|
_NetworkProvider = networkProvider;
|
||||||
_Settings = settingsRepository;
|
_Settings = settingsRepository;
|
||||||
_btcPayServerOptions = btcPayServerOptions;
|
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
}
|
}
|
||||||
public async Task ExecuteAsync(CancellationToken cancellationToken = default)
|
public async Task ExecuteAsync(CancellationToken cancellationToken = default)
|
||||||
@@ -50,6 +50,12 @@ namespace BTCPayServer.Hosting
|
|||||||
{
|
{
|
||||||
await Migrate(cancellationToken);
|
await Migrate(cancellationToken);
|
||||||
var settings = (await _Settings.GetSettingAsync<MigrationSettings>()) ?? new MigrationSettings();
|
var settings = (await _Settings.GetSettingAsync<MigrationSettings>()) ?? new MigrationSettings();
|
||||||
|
if (!settings.PaymentMethodCriteria)
|
||||||
|
{
|
||||||
|
await MigratePaymentMethodCriteria();
|
||||||
|
settings.PaymentMethodCriteria = true;
|
||||||
|
await _Settings.UpdateSetting(settings);
|
||||||
|
}
|
||||||
if (!settings.DeprecatedLightningConnectionStringCheck)
|
if (!settings.DeprecatedLightningConnectionStringCheck)
|
||||||
{
|
{
|
||||||
await DeprecatedLightningConnectionStringCheck();
|
await DeprecatedLightningConnectionStringCheck();
|
||||||
@@ -95,6 +101,13 @@ namespace BTCPayServer.Hosting
|
|||||||
settings.CheckedFirstRun = true;
|
settings.CheckedFirstRun = true;
|
||||||
await _Settings.UpdateSetting(settings);
|
await _Settings.UpdateSetting(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!settings.TransitionToStoreBlobAdditionalData)
|
||||||
|
{
|
||||||
|
await TransitionToStoreBlobAdditionalData();
|
||||||
|
settings.TransitionToStoreBlobAdditionalData = true;
|
||||||
|
await _Settings.UpdateSetting(settings);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
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)
|
private async Task Migrate(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
using (CancellationTokenSource timeout = new CancellationTokenSource(10_000))
|
using (CancellationTokenSource timeout = new CancellationTokenSource(10_000))
|
||||||
@@ -131,23 +161,32 @@ retry:
|
|||||||
bool save = false;
|
bool save = false;
|
||||||
using (var ctx = _DBContextFactory.CreateContext())
|
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
|
#pragma warning disable CS0618 // Type or member is obsolete
|
||||||
var blob = store.GetStoreBlob();
|
var blob = store.GetStoreBlob();
|
||||||
if (blob.WalletKeyPathRoots == null)
|
|
||||||
continue;
|
if (blob.AdditionalData.TryGetValue("walletKeyPathRoots", out var walletKeyPathRootsJToken))
|
||||||
foreach (var scheme in store.GetSupportedPaymentMethods(_NetworkProvider).OfType<DerivationSchemeSettings>())
|
|
||||||
{
|
{
|
||||||
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>())
|
||||||
|
{
|
||||||
|
if (walletKeyPathRoots.TryGetValue(scheme.PaymentId.ToString().ToLowerInvariant(),
|
||||||
|
out var root))
|
||||||
{
|
{
|
||||||
scheme.AccountKeyPath = new NBitcoin.KeyPath(root);
|
scheme.AccountKeyPath = new NBitcoin.KeyPath(root);
|
||||||
store.SetSupportedPaymentMethod(scheme);
|
store.SetSupportedPaymentMethod(scheme);
|
||||||
save = true;
|
save = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
blob.WalletKeyPathRoots = null;
|
|
||||||
|
blob.AdditionalData.Remove("walletKeyPathRoots");
|
||||||
store.SetStoreBlob(blob);
|
store.SetStoreBlob(blob);
|
||||||
|
}
|
||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
#pragma warning restore CS0618 // Type or member is obsolete
|
||||||
}
|
}
|
||||||
if (save)
|
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()
|
private async Task ConvertNetworkFeeProperty()
|
||||||
{
|
{
|
||||||
using (var ctx = _DBContextFactory.CreateContext())
|
using (var ctx = _DBContextFactory.CreateContext())
|
||||||
@@ -180,14 +271,17 @@ retry:
|
|||||||
foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync())
|
foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync())
|
||||||
{
|
{
|
||||||
var blob = store.GetStoreBlob();
|
var blob = store.GetStoreBlob();
|
||||||
#pragma warning disable CS0618 // Type or member is obsolete
|
if (blob.AdditionalData.TryGetValue("networkFeeDisabled", out var networkFeeModeJToken))
|
||||||
if (blob.NetworkFeeDisabled != null)
|
|
||||||
{
|
{
|
||||||
blob.NetworkFeeMode = blob.NetworkFeeDisabled.Value ? NetworkFeeMode.Never : NetworkFeeMode.Always;
|
var networkFeeMode = networkFeeModeJToken.ToObject<bool?>();
|
||||||
blob.NetworkFeeDisabled = null;
|
if (networkFeeMode != null)
|
||||||
|
{
|
||||||
|
blob.NetworkFeeMode = networkFeeMode.Value ? NetworkFeeMode.Never : NetworkFeeMode.Always;
|
||||||
|
}
|
||||||
|
|
||||||
|
blob.AdditionalData.Remove("networkFeeDisabled");
|
||||||
store.SetStoreBlob(blob);
|
store.SetStoreBlob(blob);
|
||||||
}
|
}
|
||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
|
||||||
}
|
}
|
||||||
await ctx.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
@@ -197,27 +291,46 @@ retry:
|
|||||||
{
|
{
|
||||||
using (var ctx = _DBContextFactory.CreateContext())
|
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();
|
var blob = store.GetStoreBlob();
|
||||||
#pragma warning disable CS0612 // Type or member is obsolete
|
|
||||||
decimal multiplier = 1.0m;
|
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)
|
||||||
|
{
|
||||||
|
foreach (var rule in rateRules)
|
||||||
{
|
{
|
||||||
multiplier = rule.Apply(null, multiplier);
|
multiplier = rule.Apply(null, multiplier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
blob.RateRules = null;
|
|
||||||
|
blob.AdditionalData.Remove("rateRules");
|
||||||
blob.Spread = Math.Min(1.0m, Math.Max(0m, -(multiplier - 1.0m)));
|
blob.Spread = Math.Min(1.0m, Math.Max(0m, -(multiplier - 1.0m)));
|
||||||
store.SetStoreBlob(blob);
|
store.SetStoreBlob(blob);
|
||||||
#pragma warning restore CS0612 // Type or member is obsolete
|
}
|
||||||
}
|
}
|
||||||
await ctx.SaveChangesAsync();
|
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()
|
private Task UnreachableStoreCheck()
|
||||||
{
|
{
|
||||||
return _StoreRepository.CleanUnreachableStores();
|
return _StoreRepository.CleanUnreachableStores();
|
||||||
@@ -227,7 +340,7 @@ retry:
|
|||||||
{
|
{
|
||||||
using (var ctx = _DBContextFactory.CreateContext())
|
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>())
|
foreach (var method in store.GetSupportedPaymentMethods(_NetworkProvider).OfType<Payments.Lightning.LightningSupportedPaymentMethod>())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ namespace BTCPayServer.Services
|
|||||||
public bool ConvertCrowdfundOldSettings { get; set; }
|
public bool ConvertCrowdfundOldSettings { get; set; }
|
||||||
public bool ConvertWalletKeyPathRoots { get; set; }
|
public bool ConvertWalletKeyPathRoots { get; set; }
|
||||||
public bool CheckedFirstRun { get; set; }
|
public bool CheckedFirstRun { get; set; }
|
||||||
|
public bool PaymentMethodCriteria { get; set; }
|
||||||
|
public bool TransitionToStoreBlobAdditionalData { get; set; }
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
|
|||||||
Reference in New Issue
Block a user