btcpayserver/BTCPayServer/Hosting/MigrationStartupTask.cs

770 lines
33 KiB
C#
Raw Normal View History

2020-06-29 04:44:35 +02:00
using System;
using System.Collections.Generic;
Transfer Processors (#3476) * Automated Transfer processors This PR introduces a few things: * Payouts can now be directly nested under a store instead of through a pull payment. * The Wallet Send screen now has an option to "schedule" instead of simply creating a transaction. When you click on schedule, all transaction destinations are converted into approved payouts. Any options relating to fees or coin selection are discarded. * There is a new concept introduced, called "Transfer Processors". Transfer Processors are services for stores that process payouts that are awaiting payment. Each processor specifies which payment methods it can handle. BTCPay Server will have some forms of transfer processors baked in but it has been designed to allow the Plugin System to provide additional processors. * The initial transfer processors provided are "automated processors", for on chain and lightning payment methods. They can be configured to process payouts every X amount of minutes. For on-chain, this means payments are batched into one transaction, resulting in more efficient and cheaper fees for processing. * * fix build * extract * remove magic string stuff * fix error message when scheduling * Paginate migration * add payout count to payment method tab * remove unused var * add protip * optimzie payout migration dramatically * Remove useless double condition * Fix bunch of warnings * Remove warning * Remove warnigns * Rename to Payout processors * fix typo Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2022-04-24 05:19:34 +02:00
using System.Diagnostics;
2020-06-28 11:00:51 +02:00
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts;
2020-06-28 11:00:51 +02:00
using BTCPayServer.Client.Models;
using BTCPayServer.Configuration;
using BTCPayServer.Controllers;
2020-06-28 11:00:51 +02:00
using BTCPayServer.Data;
using BTCPayServer.Fido2;
using BTCPayServer.Fido2.Models;
2020-06-28 11:00:51 +02:00
using BTCPayServer.Logging;
using BTCPayServer.Models.AppViewModels;
using BTCPayServer.Payments;
using BTCPayServer.Payments.Lightning;
2020-06-28 11:00:51 +02:00
using BTCPayServer.Services;
using BTCPayServer.Services.Apps;
2020-06-28 11:00:51 +02:00
using BTCPayServer.Services.Stores;
using ExchangeSharp;
using Fido2NetLib.Objects;
2020-06-28 11:00:51 +02:00
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
Transfer Processors (#3476) * Automated Transfer processors This PR introduces a few things: * Payouts can now be directly nested under a store instead of through a pull payment. * The Wallet Send screen now has an option to "schedule" instead of simply creating a transaction. When you click on schedule, all transaction destinations are converted into approved payouts. Any options relating to fees or coin selection are discarded. * There is a new concept introduced, called "Transfer Processors". Transfer Processors are services for stores that process payouts that are awaiting payment. Each processor specifies which payment methods it can handle. BTCPay Server will have some forms of transfer processors baked in but it has been designed to allow the Plugin System to provide additional processors. * The initial transfer processors provided are "automated processors", for on chain and lightning payment methods. They can be configured to process payouts every X amount of minutes. For on-chain, this means payments are batched into one transaction, resulting in more efficient and cheaper fees for processing. * * fix build * extract * remove magic string stuff * fix error message when scheduling * Paginate migration * add payout count to payment method tab * remove unused var * add protip * optimzie payout migration dramatically * Remove useless double condition * Fix bunch of warnings * Remove warning * Remove warnigns * Rename to Payout processors * fix typo Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2022-04-24 05:19:34 +02:00
using NBitcoin;
using NBitcoin.DataEncoders;
using NBXplorer;
using Newtonsoft.Json.Linq;
using PeterO.Cbor;
Transfer Processors (#3476) * Automated Transfer processors This PR introduces a few things: * Payouts can now be directly nested under a store instead of through a pull payment. * The Wallet Send screen now has an option to "schedule" instead of simply creating a transaction. When you click on schedule, all transaction destinations are converted into approved payouts. Any options relating to fees or coin selection are discarded. * There is a new concept introduced, called "Transfer Processors". Transfer Processors are services for stores that process payouts that are awaiting payment. Each processor specifies which payment methods it can handle. BTCPay Server will have some forms of transfer processors baked in but it has been designed to allow the Plugin System to provide additional processors. * The initial transfer processors provided are "automated processors", for on chain and lightning payment methods. They can be configured to process payouts every X amount of minutes. For on-chain, this means payments are batched into one transaction, resulting in more efficient and cheaper fees for processing. * * fix build * extract * remove magic string stuff * fix error message when scheduling * Paginate migration * add payout count to payment method tab * remove unused var * add protip * optimzie payout migration dramatically * Remove useless double condition * Fix bunch of warnings * Remove warning * Remove warnigns * Rename to Payout processors * fix typo Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2022-04-24 05:19:34 +02:00
using PayoutData = BTCPayServer.Data.PayoutData;
using PullPaymentData = BTCPayServer.Data.PullPaymentData;
using StoreData = BTCPayServer.Data.StoreData;
2020-06-28 11:00:51 +02:00
namespace BTCPayServer.Hosting
{
public class MigrationStartupTask : IStartupTask
{
2021-11-22 09:16:08 +01:00
public Logs Logs { get; }
private readonly ApplicationDbContextFactory _DBContextFactory;
private readonly StoreRepository _StoreRepository;
private readonly BTCPayNetworkProvider _NetworkProvider;
private readonly SettingsRepository _Settings;
private readonly AppService _appService;
private readonly IEnumerable<IPayoutHandler> _payoutHandlers;
private readonly BTCPayNetworkJsonSerializerSettings _btcPayNetworkJsonSerializerSettings;
private readonly LightningAddressService _lightningAddressService;
Transfer Processors (#3476) * Automated Transfer processors This PR introduces a few things: * Payouts can now be directly nested under a store instead of through a pull payment. * The Wallet Send screen now has an option to "schedule" instead of simply creating a transaction. When you click on schedule, all transaction destinations are converted into approved payouts. Any options relating to fees or coin selection are discarded. * There is a new concept introduced, called "Transfer Processors". Transfer Processors are services for stores that process payouts that are awaiting payment. Each processor specifies which payment methods it can handle. BTCPay Server will have some forms of transfer processors baked in but it has been designed to allow the Plugin System to provide additional processors. * The initial transfer processors provided are "automated processors", for on chain and lightning payment methods. They can be configured to process payouts every X amount of minutes. For on-chain, this means payments are batched into one transaction, resulting in more efficient and cheaper fees for processing. * * fix build * extract * remove magic string stuff * fix error message when scheduling * Paginate migration * add payout count to payment method tab * remove unused var * add protip * optimzie payout migration dramatically * Remove useless double condition * Fix bunch of warnings * Remove warning * Remove warnigns * Rename to Payout processors * fix typo Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2022-04-24 05:19:34 +02:00
private readonly ILogger<MigrationStartupTask> _logger;
2020-06-28 11:00:51 +02:00
private readonly UserManager<ApplicationUser> _userManager;
public IOptions<LightningNetworkOptions> LightningOptions { get; }
2020-06-28 11:00:51 +02:00
public MigrationStartupTask(
BTCPayNetworkProvider networkProvider,
StoreRepository storeRepository,
ApplicationDbContextFactory dbContextFactory,
UserManager<ApplicationUser> userManager,
IOptions<LightningNetworkOptions> lightningOptions,
SettingsRepository settingsRepository,
2021-12-31 08:59:02 +01:00
AppService appService,
IEnumerable<IPayoutHandler> payoutHandlers,
2021-11-22 09:16:08 +01:00
BTCPayNetworkJsonSerializerSettings btcPayNetworkJsonSerializerSettings,
LightningAddressService lightningAddressService,
Transfer Processors (#3476) * Automated Transfer processors This PR introduces a few things: * Payouts can now be directly nested under a store instead of through a pull payment. * The Wallet Send screen now has an option to "schedule" instead of simply creating a transaction. When you click on schedule, all transaction destinations are converted into approved payouts. Any options relating to fees or coin selection are discarded. * There is a new concept introduced, called "Transfer Processors". Transfer Processors are services for stores that process payouts that are awaiting payment. Each processor specifies which payment methods it can handle. BTCPay Server will have some forms of transfer processors baked in but it has been designed to allow the Plugin System to provide additional processors. * The initial transfer processors provided are "automated processors", for on chain and lightning payment methods. They can be configured to process payouts every X amount of minutes. For on-chain, this means payments are batched into one transaction, resulting in more efficient and cheaper fees for processing. * * fix build * extract * remove magic string stuff * fix error message when scheduling * Paginate migration * add payout count to payment method tab * remove unused var * add protip * optimzie payout migration dramatically * Remove useless double condition * Fix bunch of warnings * Remove warning * Remove warnigns * Rename to Payout processors * fix typo Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2022-04-24 05:19:34 +02:00
ILogger<MigrationStartupTask> logger)
2020-06-28 11:00:51 +02:00
{
_DBContextFactory = dbContextFactory;
_StoreRepository = storeRepository;
_NetworkProvider = networkProvider;
_Settings = settingsRepository;
_appService = appService;
_payoutHandlers = payoutHandlers;
_btcPayNetworkJsonSerializerSettings = btcPayNetworkJsonSerializerSettings;
_lightningAddressService = lightningAddressService;
Transfer Processors (#3476) * Automated Transfer processors This PR introduces a few things: * Payouts can now be directly nested under a store instead of through a pull payment. * The Wallet Send screen now has an option to "schedule" instead of simply creating a transaction. When you click on schedule, all transaction destinations are converted into approved payouts. Any options relating to fees or coin selection are discarded. * There is a new concept introduced, called "Transfer Processors". Transfer Processors are services for stores that process payouts that are awaiting payment. Each processor specifies which payment methods it can handle. BTCPay Server will have some forms of transfer processors baked in but it has been designed to allow the Plugin System to provide additional processors. * The initial transfer processors provided are "automated processors", for on chain and lightning payment methods. They can be configured to process payouts every X amount of minutes. For on-chain, this means payments are batched into one transaction, resulting in more efficient and cheaper fees for processing. * * fix build * extract * remove magic string stuff * fix error message when scheduling * Paginate migration * add payout count to payment method tab * remove unused var * add protip * optimzie payout migration dramatically * Remove useless double condition * Fix bunch of warnings * Remove warning * Remove warnigns * Rename to Payout processors * fix typo Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2022-04-24 05:19:34 +02:00
_logger = logger;
2020-06-28 11:00:51 +02:00
_userManager = userManager;
LightningOptions = lightningOptions;
2020-06-28 11:00:51 +02:00
}
public async Task ExecuteAsync(CancellationToken cancellationToken = default)
{
try
{
await Migrate(cancellationToken);
2022-06-13 05:37:20 +02:00
var settings = (await _Settings.GetSettingAsync<MigrationSettings>()) ?? new MigrationSettings() { MigratedInvoiceTextSearchPages = int.MaxValue };
if (!settings.PaymentMethodCriteria)
{
await MigratePaymentMethodCriteria();
settings.PaymentMethodCriteria = true;
await _Settings.UpdateSetting(settings);
}
2020-06-28 11:00:51 +02:00
if (!settings.DeprecatedLightningConnectionStringCheck)
{
await DeprecatedLightningConnectionStringCheck();
settings.DeprecatedLightningConnectionStringCheck = true;
await _Settings.UpdateSetting(settings);
}
if (!settings.UnreachableStoreCheck)
{
await UnreachableStoreCheck();
settings.UnreachableStoreCheck = true;
await _Settings.UpdateSetting(settings);
}
if (!settings.ConvertMultiplierToSpread)
{
await ConvertMultiplierToSpread();
settings.ConvertMultiplierToSpread = true;
await _Settings.UpdateSetting(settings);
}
if (!settings.ConvertNetworkFeeProperty)
{
await ConvertNetworkFeeProperty();
settings.ConvertNetworkFeeProperty = true;
await _Settings.UpdateSetting(settings);
}
if (!settings.ConvertCrowdfundOldSettings)
{
await ConvertCrowdfundOldSettings();
settings.ConvertCrowdfundOldSettings = true;
await _Settings.UpdateSetting(settings);
}
if (!settings.ConvertWalletKeyPathRoots)
{
await ConvertConvertWalletKeyPathRoots();
settings.ConvertWalletKeyPathRoots = true;
await _Settings.UpdateSetting(settings);
}
if (!settings.CheckedFirstRun)
{
var themeSettings = await _Settings.GetSettingAsync<ThemeSettings>() ?? new ThemeSettings();
var admin = await _userManager.GetUsersInRoleAsync(Roles.ServerAdmin);
themeSettings.FirstRun = admin.Count == 0;
await _Settings.UpdateSetting(themeSettings);
settings.CheckedFirstRun = true;
await _Settings.UpdateSetting(settings);
}
if (!settings.TransitionToStoreBlobAdditionalData)
{
await TransitionToStoreBlobAdditionalData();
settings.TransitionToStoreBlobAdditionalData = true;
await _Settings.UpdateSetting(settings);
}
if (!settings.TransitionInternalNodeConnectionString)
{
await TransitionInternalNodeConnectionString();
settings.TransitionInternalNodeConnectionString = true;
await _Settings.UpdateSetting(settings);
}
if (!settings.MigrateU2FToFIDO2)
{
await MigrateU2FToFIDO2();
settings.MigrateU2FToFIDO2 = true;
await _Settings.UpdateSetting(settings);
}
if (!settings.MigrateHotwalletProperty)
{
await MigrateHotwalletProperty();
settings.MigrateHotwalletProperty = true;
await _Settings.UpdateSetting(settings);
}
if (!settings.MigrateAppCustomOption)
{
await MigrateAppCustomOption();
settings.MigrateAppCustomOption = true;
await _Settings.UpdateSetting(settings);
}
if (!settings.MigratePayoutDestinationId)
{
await MigratePayoutDestinationId();
settings.MigratePayoutDestinationId = true;
await _Settings.UpdateSetting(settings);
}
if (!settings.AddInitialUserBlob)
{
await AddInitialUserBlob();
settings.AddInitialUserBlob = true;
await _Settings.UpdateSetting(settings);
}
2022-01-27 17:06:02 +01:00
if (!settings.LighingAddressSettingRename)
{
await MigrateLighingAddressSettingRename();
settings.LighingAddressSettingRename = true;
await _Settings.UpdateSetting(settings);
}
if (!settings.LighingAddressDatabaseMigration)
{
await MigrateLighingAddressDatabaseMigration();
settings.LighingAddressDatabaseMigration = true;
Transfer Processors (#3476) * Automated Transfer processors This PR introduces a few things: * Payouts can now be directly nested under a store instead of through a pull payment. * The Wallet Send screen now has an option to "schedule" instead of simply creating a transaction. When you click on schedule, all transaction destinations are converted into approved payouts. Any options relating to fees or coin selection are discarded. * There is a new concept introduced, called "Transfer Processors". Transfer Processors are services for stores that process payouts that are awaiting payment. Each processor specifies which payment methods it can handle. BTCPay Server will have some forms of transfer processors baked in but it has been designed to allow the Plugin System to provide additional processors. * The initial transfer processors provided are "automated processors", for on chain and lightning payment methods. They can be configured to process payouts every X amount of minutes. For on-chain, this means payments are batched into one transaction, resulting in more efficient and cheaper fees for processing. * * fix build * extract * remove magic string stuff * fix error message when scheduling * Paginate migration * add payout count to payment method tab * remove unused var * add protip * optimzie payout migration dramatically * Remove useless double condition * Fix bunch of warnings * Remove warning * Remove warnigns * Rename to Payout processors * fix typo Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2022-04-24 05:19:34 +02:00
}
if (!settings.AddStoreToPayout)
{
await MigrateAddStoreToPayout();
settings.AddStoreToPayout = true;
await _Settings.UpdateSetting(settings);
}
2020-06-28 11:00:51 +02:00
}
catch (Exception ex)
{
Transfer Processors (#3476) * Automated Transfer processors This PR introduces a few things: * Payouts can now be directly nested under a store instead of through a pull payment. * The Wallet Send screen now has an option to "schedule" instead of simply creating a transaction. When you click on schedule, all transaction destinations are converted into approved payouts. Any options relating to fees or coin selection are discarded. * There is a new concept introduced, called "Transfer Processors". Transfer Processors are services for stores that process payouts that are awaiting payment. Each processor specifies which payment methods it can handle. BTCPay Server will have some forms of transfer processors baked in but it has been designed to allow the Plugin System to provide additional processors. * The initial transfer processors provided are "automated processors", for on chain and lightning payment methods. They can be configured to process payouts every X amount of minutes. For on-chain, this means payments are batched into one transaction, resulting in more efficient and cheaper fees for processing. * * fix build * extract * remove magic string stuff * fix error message when scheduling * Paginate migration * add payout count to payment method tab * remove unused var * add protip * optimzie payout migration dramatically * Remove useless double condition * Fix bunch of warnings * Remove warning * Remove warnigns * Rename to Payout processors * fix typo Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2022-04-24 05:19:34 +02:00
_logger.LogError(ex, "Error on the MigrationStartupTask");
2020-06-28 11:00:51 +02:00
throw;
}
}
2021-12-31 08:59:02 +01:00
private async Task MigrateLighingAddressDatabaseMigration()
{
await using var ctx = _DBContextFactory.CreateContext();
var lightningAddressSettings =
await _Settings.GetSettingAsync<UILNURLController.LightningAddressSettings>(
nameof(UILNURLController.LightningAddressSettings));
if (lightningAddressSettings is null)
{
return;
}
var storeids = lightningAddressSettings.StoreToItemMap.Keys.ToArray();
var existingStores = (await ctx.Stores.Where(data => storeids.Contains(data.Id)).Select(data => data.Id ).ToArrayAsync()).ToHashSet();
foreach (var storeMap in lightningAddressSettings.StoreToItemMap)
{
if (!existingStores.Contains(storeMap.Key)) continue;
foreach (var storeitem in storeMap.Value)
{
if (lightningAddressSettings.Items.TryGetValue(storeitem, out var val))
{
await _lightningAddressService.Set(
new LightningAddressData()
{
StoreDataId = storeMap.Key,
Username = storeitem,
Blob = new LightningAddressDataBlob()
{
Max = val.Max,
Min = val.Min,
CurrencyCode = val.CurrencyCode
}.SerializeBlob()
}, ctx);
}
}
}
await ctx.SaveChangesAsync();
await _Settings.UpdateSetting<object>(null, nameof(UILNURLController.LightningAddressSettings));
}
2022-01-27 17:06:02 +01:00
private async Task MigrateLighingAddressSettingRename()
{
var old = await _Settings.GetSettingAsync<UILNURLController.LightningAddressSettings>("BTCPayServer.LNURLController+LightningAddressSettings");
if (old is not null)
{
await _Settings.UpdateSetting(old, nameof(UILNURLController.LightningAddressSettings));
}
}
Transfer Processors (#3476) * Automated Transfer processors This PR introduces a few things: * Payouts can now be directly nested under a store instead of through a pull payment. * The Wallet Send screen now has an option to "schedule" instead of simply creating a transaction. When you click on schedule, all transaction destinations are converted into approved payouts. Any options relating to fees or coin selection are discarded. * There is a new concept introduced, called "Transfer Processors". Transfer Processors are services for stores that process payouts that are awaiting payment. Each processor specifies which payment methods it can handle. BTCPay Server will have some forms of transfer processors baked in but it has been designed to allow the Plugin System to provide additional processors. * The initial transfer processors provided are "automated processors", for on chain and lightning payment methods. They can be configured to process payouts every X amount of minutes. For on-chain, this means payments are batched into one transaction, resulting in more efficient and cheaper fees for processing. * * fix build * extract * remove magic string stuff * fix error message when scheduling * Paginate migration * add payout count to payment method tab * remove unused var * add protip * optimzie payout migration dramatically * Remove useless double condition * Fix bunch of warnings * Remove warning * Remove warnigns * Rename to Payout processors * fix typo Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2022-04-24 05:19:34 +02:00
private async Task MigrateAddStoreToPayout()
{
await using var ctx = _DBContextFactory.CreateContext();
if (ctx.Database.IsNpgsql())
{
await ctx.Database.ExecuteSqlRawAsync(@"
WITH cte AS (
SELECT DISTINCT p.""Id"", pp.""StoreId"" FROM ""Payouts"" p
JOIN ""PullPayments"" pp ON pp.""Id"" = p.""PullPaymentDataId""
WHERE p.""StoreDataId"" IS NULL
)
UPDATE ""Payouts"" p
SET ""StoreDataId""=cte.""StoreId""
FROM cte
WHERE cte.""Id""=p.""Id""
");
}
else
{
var queryable = ctx.Payouts.Where(data => data.StoreDataId == null);
var count = await queryable.CountAsync();
_logger.LogInformation($"Migrating {count} payouts to have a store id explicitly");
for (int i = 0; i < count; i+=1000)
{
await queryable.Include(data => data.PullPaymentData).Skip(i).Take(1000)
.ForEachAsync(data => data.StoreDataId = data.PullPaymentData.StoreId);
await ctx.SaveChangesAsync();
_logger.LogInformation($"Migrated {i+1000}/{count} payouts to have a store id explicitly");
}
}
}
private async Task AddInitialUserBlob()
{
await using var ctx = _DBContextFactory.CreateContext();
foreach (var user in await ctx.Users.AsQueryable().ToArrayAsync())
{
user.SetBlob(new UserBlob() { ShowInvoiceStatusChangeHint = true });
}
await ctx.SaveChangesAsync();
}
2021-12-31 08:59:02 +01:00
private async Task MigratePayoutDestinationId()
{
await using var ctx = _DBContextFactory.CreateContext();
foreach (var payoutData in await ctx.Payouts.AsQueryable().ToArrayAsync())
{
2021-12-31 08:59:02 +01:00
var pmi = payoutData.GetPaymentMethodId();
if (pmi is null)
{
continue;
}
var handler = _payoutHandlers
.FindPayoutHandler(pmi);
if (handler is null)
{
continue;
}
var claim = await handler?.ParseClaimDestination(pmi, payoutData.GetBlob(_btcPayNetworkJsonSerializerSettings).Destination);
2021-12-31 08:59:02 +01:00
payoutData.Destination = claim.destination?.Id;
}
await ctx.SaveChangesAsync();
}
2020-06-28 11:00:51 +02:00
private async Task MigrateAppCustomOption()
{
await using var ctx = _DBContextFactory.CreateContext();
foreach (var app in await ctx.Apps.Include(data => data.StoreData).AsQueryable().ToArrayAsync())
{
ViewPointOfSaleViewModel.Item[] items;
string newTemplate;
switch (app.AppType)
{
case nameof(AppType.Crowdfund):
var settings1 = app.GetSettings<CrowdfundSettings>();
if (string.IsNullOrEmpty(settings1.TargetCurrency))
{
settings1.TargetCurrency = app.StoreData.GetStoreBlob().DefaultCurrency;
app.SetSettings(settings1);
}
items = _appService.Parse(settings1.PerksTemplate, settings1.TargetCurrency);
newTemplate = _appService.SerializeTemplate(items);
if (settings1.PerksTemplate != newTemplate)
{
settings1.PerksTemplate = newTemplate;
app.SetSettings(settings1);
};
break;
2021-12-31 08:59:02 +01:00
case nameof(AppType.PointOfSale):
2021-12-31 08:59:02 +01:00
var settings2 = app.GetSettings<PointOfSaleSettings>();
if (string.IsNullOrEmpty(settings2.Currency))
{
settings2.Currency = app.StoreData.GetStoreBlob().DefaultCurrency;
app.SetSettings(settings2);
}
items = _appService.Parse(settings2.Template, settings2.Currency);
newTemplate = _appService.SerializeTemplate(items);
if (settings2.Template != newTemplate)
{
settings2.Template = newTemplate;
app.SetSettings(settings2);
};
break;
}
}
await ctx.SaveChangesAsync();
}
private async Task MigrateHotwalletProperty()
{
await using var ctx = _DBContextFactory.CreateContext();
foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync())
{
foreach (var paymentMethod in store.GetSupportedPaymentMethods(_NetworkProvider).OfType<DerivationSchemeSettings>())
{
paymentMethod.IsHotWallet = paymentMethod.Source == "NBXplorer";
2021-08-10 05:13:00 +02:00
if (paymentMethod.IsHotWallet)
{
paymentMethod.Source = "NBXplorerGenerated";
store.SetSupportedPaymentMethod(paymentMethod);
}
}
}
await ctx.SaveChangesAsync();
}
private async Task MigrateU2FToFIDO2()
{
await using var ctx = _DBContextFactory.CreateContext();
var u2fDevices = await ctx.U2FDevices.ToListAsync();
foreach (U2FDevice u2FDevice in u2fDevices)
{
var fido2 = new Fido2Credential()
{
ApplicationUserId = u2FDevice.ApplicationUserId,
Name = u2FDevice.Name,
Type = Fido2Credential.CredentialType.FIDO2
};
fido2.SetBlob(new Fido2CredentialBlob()
{
SignatureCounter = (uint)u2FDevice.Counter,
2021-12-31 08:59:02 +01:00
PublicKey = CreatePublicKeyFromU2fRegistrationData(u2FDevice.PublicKey).EncodeToBytes(),
UserHandle = u2FDevice.KeyHandle,
Descriptor = new PublicKeyCredentialDescriptor(u2FDevice.KeyHandle),
CredType = "u2f"
});
await ctx.AddAsync(fido2);
2021-12-31 08:59:02 +01:00
ctx.Remove(u2FDevice);
}
await ctx.SaveChangesAsync();
}
//from https://github.com/abergs/fido2-net-lib/blob/0fa7bb4b4a1f33f46c5f7ca4ee489b47680d579b/Test/ExistingU2fRegistrationDataTests.cs#L70
private static CBORObject CreatePublicKeyFromU2fRegistrationData(byte[] publicKeyData)
{
if (publicKeyData.Length != 65)
{
throw new ArgumentException("u2f public key must be 65 bytes", nameof(publicKeyData));
}
var x = new byte[32];
var y = new byte[32];
Buffer.BlockCopy(publicKeyData, 1, x, 0, 32);
Buffer.BlockCopy(publicKeyData, 33, y, 0, 32);
var coseKey = CBORObject.NewMap();
coseKey.Add(COSE.KeyCommonParameter.KeyType, COSE.KeyType.EC2);
coseKey.Add(COSE.KeyCommonParameter.Alg, -7);
coseKey.Add(COSE.KeyTypeParameter.Crv, COSE.EllipticCurve.P256);
coseKey.Add(COSE.KeyTypeParameter.X, x);
coseKey.Add(COSE.KeyTypeParameter.Y, y);
return coseKey;
}
private async Task TransitionInternalNodeConnectionString()
{
var nodes = LightningOptions.Value.InternalLightningByCryptoCode.Values.Select(c => c.ToString()).ToHashSet();
await using var ctx = _DBContextFactory.CreateContext();
foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync())
{
#pragma warning disable CS0618 // Type or member is obsolete
if (!string.IsNullOrEmpty(store.DerivationStrategy))
{
var noLabel = store.DerivationStrategy.Split('-')[0];
JObject jObject = new JObject();
jObject.Add("BTC", new JObject()
{
new JProperty("signingKey", noLabel),
new JProperty("accountDerivation", store.DerivationStrategy),
new JProperty("accountOriginal", store.DerivationStrategy),
new JProperty("accountKeySettings", new JArray()
{
new JObject()
{
new JProperty("accountKey", noLabel)
}
})
});
store.DerivationStrategies = jObject.ToString();
store.DerivationStrategy = null;
}
if (string.IsNullOrEmpty(store.DerivationStrategies))
continue;
var strats = JObject.Parse(store.DerivationStrategies);
bool updated = false;
foreach (var prop in strats.Properties().Where(p => p.Name.EndsWith("LightningLike", StringComparison.OrdinalIgnoreCase)))
{
var method = ((JObject)prop.Value);
var lightningCharge = method.Property("LightningChargeUrl", StringComparison.OrdinalIgnoreCase);
var ln = method.Property("LightningConnectionString", StringComparison.OrdinalIgnoreCase);
if (lightningCharge != null)
{
var chargeUrl = lightningCharge.Value.Value<string>();
var usr = method.Property("Username", StringComparison.OrdinalIgnoreCase)?.Value.Value<string>();
var pass = method.Property("Password", StringComparison.OrdinalIgnoreCase)?.Value.Value<string>();
updated = true;
if (chargeUrl != null)
{
var fullUri = new UriBuilder(chargeUrl)
{
UserName = usr,
Password = pass
}.Uri.AbsoluteUri;
var newStr = $"type=charge;server={fullUri};allowinsecure=true";
if (ln is null)
{
ln = new JProperty("LightningConnectionString", newStr);
method.Add(ln);
}
else
{
ln.Value = newStr;
}
}
foreach (var p in new[] { "Username", "Password", "LightningChargeUrl" })
method.Property(p, StringComparison.OrdinalIgnoreCase)?.Remove();
}
var paymentId = method.Property("PaymentId", StringComparison.OrdinalIgnoreCase);
if (paymentId != null)
{
paymentId.Remove();
updated = true;
}
if (ln is null)
continue;
if (nodes.Contains(ln.Value.Value<string>()))
{
updated = true;
ln.Value = null;
if (!(method.Property("InternalNodeRef", StringComparison.OrdinalIgnoreCase) is JProperty internalNode))
{
internalNode = new JProperty("InternalNodeRef", null);
method.Add(internalNode);
}
internalNode.Value = new JValue(LightningSupportedPaymentMethod.InternalNode);
}
}
if (updated)
store.DerivationStrategies = strats.ToString();
#pragma warning restore CS0618 // Type or member is obsolete
}
await ctx.SaveChangesAsync();
}
private async Task TransitionToStoreBlobAdditionalData()
{
await using var ctx = _DBContextFactory.CreateContext();
foreach (var store in await ctx.Stores.AsQueryable().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();
}
2020-06-28 11:00:51 +02:00
private async Task Migrate(CancellationToken cancellationToken)
{
using (CancellationTokenSource timeout = new CancellationTokenSource(10_000))
using (CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, cancellationToken))
{
retry:
try
{
await _DBContextFactory.CreateContext().Database.MigrateAsync();
}
// Starting up
catch when (!cts.Token.IsCancellationRequested)
{
try
{
await Task.Delay(1000, cts.Token);
}
catch { }
goto retry;
}
}
}
private async Task ConvertConvertWalletKeyPathRoots()
{
bool save = false;
2022-01-14 09:50:29 +01:00
using var ctx = _DBContextFactory.CreateContext();
foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync())
2020-06-28 11:00:51 +02:00
{
#pragma warning disable CS0618 // Type or member is obsolete
2022-01-14 09:50:29 +01:00
var blob = store.GetStoreBlob();
2022-01-14 09:50:29 +01:00
if (blob.AdditionalData.TryGetValue("walletKeyPathRoots", out var walletKeyPathRootsJToken))
{
var walletKeyPathRoots = walletKeyPathRootsJToken.ToObject<Dictionary<string, string>>();
2022-01-14 09:50:29 +01:00
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))
2020-06-28 11:00:51 +02:00
{
2022-01-14 09:50:29 +01:00
scheme.AccountKeyPath = new NBitcoin.KeyPath(root);
store.SetSupportedPaymentMethod(scheme);
save = true;
2020-06-28 11:00:51 +02:00
}
}
2022-01-14 09:50:29 +01:00
blob.AdditionalData.Remove("walletKeyPathRoots");
store.SetStoreBlob(blob);
2020-06-28 11:00:51 +02:00
}
2022-01-14 09:50:29 +01:00
#pragma warning restore CS0618 // Type or member is obsolete
2020-06-28 11:00:51 +02:00
}
2022-01-14 09:50:29 +01:00
if (save)
await ctx.SaveChangesAsync();
2020-06-28 11:00:51 +02:00
}
private async Task ConvertCrowdfundOldSettings()
{
2022-01-14 09:50:29 +01:00
using var ctx = _DBContextFactory.CreateContext();
foreach (var app in await ctx.Apps.Where(a => a.AppType == "Crowdfund").ToArrayAsync())
2020-06-28 11:00:51 +02:00
{
2022-01-14 09:50:29 +01:00
var settings = app.GetSettings<Services.Apps.CrowdfundSettings>();
2020-06-28 11:00:51 +02:00
#pragma warning disable CS0618 // Type or member is obsolete
2022-01-14 09:50:29 +01:00
if (settings.UseAllStoreInvoices)
2020-06-28 11:00:51 +02:00
#pragma warning restore CS0618 // Type or member is obsolete
2022-01-14 09:50:29 +01:00
{
app.TagAllInvoices = true;
2020-06-28 11:00:51 +02:00
}
}
2022-01-14 09:50:29 +01:00
await ctx.SaveChangesAsync();
2020-06-28 11:00:51 +02:00
}
private async Task MigratePaymentMethodCriteria()
{
2022-01-14 09:50:29 +01:00
using var ctx = _DBContextFactory.CreateContext();
foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync())
{
2022-01-14 09:50:29 +01:00
var blob = store.GetStoreBlob();
2022-01-14 09:50:29 +01:00
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
2021-12-31 08:59:02 +01:00
{
2022-01-14 09:50:29 +01:00
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()
2021-12-31 08:59:02 +01:00
{
2022-01-14 09:50:29 +01:00
PaymentMethod = paymentMethodId,
Above = matchedFromBlob?.Above ?? true,
Value = matchedFromBlob?.Value
}
};
}).ToList();
2022-01-14 09:50:29 +01:00
store.SetStoreBlob(blob);
}
2022-01-14 09:50:29 +01:00
await ctx.SaveChangesAsync();
}
2020-06-28 11:00:51 +02:00
private async Task ConvertNetworkFeeProperty()
{
2022-01-14 09:50:29 +01:00
using var ctx = _DBContextFactory.CreateContext();
foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync())
2020-06-28 11:00:51 +02:00
{
2022-01-14 09:50:29 +01:00
var blob = store.GetStoreBlob();
if (blob.AdditionalData.TryGetValue("networkFeeDisabled", out var networkFeeModeJToken))
2020-06-28 11:00:51 +02:00
{
2022-01-14 09:50:29 +01:00
var networkFeeMode = networkFeeModeJToken.ToObject<bool?>();
if (networkFeeMode != null)
2020-06-28 11:00:51 +02:00
{
2022-01-14 09:50:29 +01:00
blob.NetworkFeeMode = networkFeeMode.Value ? NetworkFeeMode.Never : NetworkFeeMode.Always;
2020-06-28 11:00:51 +02:00
}
2022-01-14 09:50:29 +01:00
blob.AdditionalData.Remove("networkFeeDisabled");
store.SetStoreBlob(blob);
2020-06-28 11:00:51 +02:00
}
}
2022-01-14 09:50:29 +01:00
await ctx.SaveChangesAsync();
2020-06-28 11:00:51 +02:00
}
private async Task ConvertMultiplierToSpread()
{
2022-01-14 09:50:29 +01:00
using var ctx = _DBContextFactory.CreateContext();
foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync())
2020-06-28 11:00:51 +02:00
{
2022-01-14 09:50:29 +01:00
var blob = store.GetStoreBlob();
decimal multiplier = 1.0m;
if (blob.AdditionalData.TryGetValue("rateRules", out var rateRulesJToken))
2020-06-28 11:00:51 +02:00
{
2022-01-14 09:50:29 +01:00
var rateRules = new Serializer(null).ToObject<List<RateRule_Obsolete>>(rateRulesJToken.ToString());
if (rateRules != null && rateRules.Count != 0)
2020-06-28 11:00:51 +02:00
{
2022-01-14 09:50:29 +01:00
foreach (var rule in rateRules)
2020-06-28 11:00:51 +02:00
{
2022-01-14 09:50:29 +01:00
multiplier = rule.Apply(null, multiplier);
2020-06-28 11:00:51 +02:00
}
}
2022-01-14 09:50:29 +01:00
blob.AdditionalData.Remove("rateRules");
blob.Spread = Math.Min(1.0m, Math.Max(0m, -(multiplier - 1.0m)));
store.SetStoreBlob(blob);
2020-06-28 11:00:51 +02:00
}
}
2022-01-14 09:50:29 +01:00
await ctx.SaveChangesAsync();
2020-06-28 11:00:51 +02:00
}
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;
}
}
2020-06-28 11:00:51 +02:00
private Task UnreachableStoreCheck()
{
return _StoreRepository.CleanUnreachableStores();
}
private async Task DeprecatedLightningConnectionStringCheck()
{
2022-01-14 09:50:29 +01:00
using var ctx = _DBContextFactory.CreateContext();
foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync())
2020-06-28 11:00:51 +02:00
{
2022-01-14 09:50:29 +01:00
foreach (var method in store.GetSupportedPaymentMethods(_NetworkProvider).OfType<Payments.Lightning.LightningSupportedPaymentMethod>())
2020-06-28 11:00:51 +02:00
{
2022-01-14 09:50:29 +01:00
var lightning = method.GetExternalLightningUrl();
if (lightning?.IsLegacy is true)
2020-06-28 11:00:51 +02:00
{
2022-01-14 09:50:29 +01:00
method.SetLightningUrl(lightning);
store.SetSupportedPaymentMethod(method);
2020-06-28 11:00:51 +02:00
}
}
}
2022-01-14 09:50:29 +01:00
await ctx.SaveChangesAsync();
2020-06-28 11:00:51 +02:00
}
}
}