2020-06-29 04:44:35 +02:00
|
|
|
using System;
|
2020-12-28 11:10:53 +01:00
|
|
|
using System.Collections.Generic;
|
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;
|
2020-11-17 13:46:23 +01:00
|
|
|
using BTCPayServer.Abstractions.Contracts;
|
2020-06-28 11:00:51 +02:00
|
|
|
using BTCPayServer.Client.Models;
|
2020-12-28 11:10:53 +01:00
|
|
|
using BTCPayServer.Configuration;
|
2021-10-11 12:46:05 +02:00
|
|
|
using BTCPayServer.Controllers;
|
2020-06-28 11:00:51 +02:00
|
|
|
using BTCPayServer.Data;
|
2021-04-28 06:14:15 +02:00
|
|
|
using BTCPayServer.Fido2;
|
|
|
|
using BTCPayServer.Fido2.Models;
|
2020-06-28 11:00:51 +02:00
|
|
|
using BTCPayServer.Logging;
|
2021-10-11 12:46:05 +02:00
|
|
|
using BTCPayServer.Models.AppViewModels;
|
2020-12-29 09:58:35 +01:00
|
|
|
using BTCPayServer.Payments;
|
2021-03-02 03:11:58 +01:00
|
|
|
using BTCPayServer.Payments.Lightning;
|
2020-06-28 11:00:51 +02:00
|
|
|
using BTCPayServer.Services;
|
2021-10-11 12:46:05 +02:00
|
|
|
using BTCPayServer.Services.Apps;
|
2020-06-28 11:00:51 +02:00
|
|
|
using BTCPayServer.Services.Stores;
|
2021-10-11 12:46:05 +02:00
|
|
|
using ExchangeSharp;
|
2021-04-28 06:14:15 +02:00
|
|
|
using Fido2NetLib.Objects;
|
2020-06-28 11:00:51 +02:00
|
|
|
using Microsoft.AspNetCore.Identity;
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
using Microsoft.Extensions.Logging;
|
2021-03-02 03:11:58 +01:00
|
|
|
using Microsoft.Extensions.Options;
|
2022-04-24 05:19:34 +02:00
|
|
|
using NBitcoin;
|
|
|
|
using NBitcoin.DataEncoders;
|
2020-12-29 09:58:35 +01:00
|
|
|
using NBXplorer;
|
|
|
|
using Newtonsoft.Json.Linq;
|
2021-04-28 06:14:15 +02:00
|
|
|
using PeterO.Cbor;
|
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; }
|
|
|
|
|
2020-06-29 05:07:48 +02:00
|
|
|
private readonly ApplicationDbContextFactory _DBContextFactory;
|
|
|
|
private readonly StoreRepository _StoreRepository;
|
|
|
|
private readonly BTCPayNetworkProvider _NetworkProvider;
|
|
|
|
private readonly SettingsRepository _Settings;
|
2021-10-11 12:46:05 +02:00
|
|
|
private readonly AppService _appService;
|
2021-10-21 17:43:02 +02:00
|
|
|
private readonly IEnumerable<IPayoutHandler> _payoutHandlers;
|
|
|
|
private readonly BTCPayNetworkJsonSerializerSettings _btcPayNetworkJsonSerializerSettings;
|
2022-04-19 09:58:31 +02:00
|
|
|
private readonly LightningAddressService _lightningAddressService;
|
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;
|
2021-03-02 03:11:58 +01:00
|
|
|
|
|
|
|
public IOptions<LightningNetworkOptions> LightningOptions { get; }
|
|
|
|
|
2020-06-28 11:00:51 +02:00
|
|
|
public MigrationStartupTask(
|
|
|
|
BTCPayNetworkProvider networkProvider,
|
|
|
|
StoreRepository storeRepository,
|
|
|
|
ApplicationDbContextFactory dbContextFactory,
|
|
|
|
UserManager<ApplicationUser> userManager,
|
2021-03-02 03:11:58 +01:00
|
|
|
IOptions<LightningNetworkOptions> lightningOptions,
|
2021-10-11 12:46:05 +02:00
|
|
|
SettingsRepository settingsRepository,
|
2021-12-31 08:59:02 +01:00
|
|
|
AppService appService,
|
2021-10-21 17:43:02 +02:00
|
|
|
IEnumerable<IPayoutHandler> payoutHandlers,
|
2021-11-22 09:16:08 +01:00
|
|
|
BTCPayNetworkJsonSerializerSettings btcPayNetworkJsonSerializerSettings,
|
2022-04-19 09:58:31 +02:00
|
|
|
LightningAddressService lightningAddressService,
|
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;
|
2021-10-11 12:46:05 +02:00
|
|
|
_appService = appService;
|
2021-10-21 17:43:02 +02:00
|
|
|
_payoutHandlers = payoutHandlers;
|
|
|
|
_btcPayNetworkJsonSerializerSettings = btcPayNetworkJsonSerializerSettings;
|
2022-04-19 09:58:31 +02:00
|
|
|
_lightningAddressService = lightningAddressService;
|
2022-04-24 05:19:34 +02:00
|
|
|
_logger = logger;
|
2020-06-28 11:00:51 +02:00
|
|
|
_userManager = userManager;
|
2021-03-02 03:11:58 +01:00
|
|
|
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 };
|
2020-12-29 09:58:35 +01:00
|
|
|
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);
|
|
|
|
}
|
2020-12-29 09:58:35 +01:00
|
|
|
|
|
|
|
if (!settings.TransitionToStoreBlobAdditionalData)
|
|
|
|
{
|
|
|
|
await TransitionToStoreBlobAdditionalData();
|
|
|
|
settings.TransitionToStoreBlobAdditionalData = true;
|
|
|
|
await _Settings.UpdateSetting(settings);
|
|
|
|
}
|
2021-03-02 03:11:58 +01:00
|
|
|
|
|
|
|
if (!settings.TransitionInternalNodeConnectionString)
|
|
|
|
{
|
|
|
|
await TransitionInternalNodeConnectionString();
|
|
|
|
settings.TransitionInternalNodeConnectionString = true;
|
|
|
|
await _Settings.UpdateSetting(settings);
|
|
|
|
}
|
2021-04-28 06:14:15 +02:00
|
|
|
|
2021-04-28 06:26:59 +02:00
|
|
|
if (!settings.MigrateU2FToFIDO2)
|
2021-04-28 06:14:15 +02:00
|
|
|
{
|
|
|
|
await MigrateU2FToFIDO2();
|
|
|
|
settings.MigrateU2FToFIDO2 = true;
|
|
|
|
await _Settings.UpdateSetting(settings);
|
|
|
|
}
|
2021-06-17 08:36:22 +02:00
|
|
|
if (!settings.MigrateHotwalletProperty)
|
|
|
|
{
|
|
|
|
await MigrateHotwalletProperty();
|
|
|
|
settings.MigrateHotwalletProperty = true;
|
|
|
|
await _Settings.UpdateSetting(settings);
|
|
|
|
}
|
2021-10-11 12:46:05 +02:00
|
|
|
if (!settings.MigrateAppCustomOption)
|
|
|
|
{
|
|
|
|
await MigrateAppCustomOption();
|
|
|
|
settings.MigrateAppCustomOption = true;
|
|
|
|
await _Settings.UpdateSetting(settings);
|
|
|
|
}
|
2021-10-21 17:43:02 +02:00
|
|
|
if (!settings.MigratePayoutDestinationId)
|
|
|
|
{
|
|
|
|
await MigratePayoutDestinationId();
|
|
|
|
settings.MigratePayoutDestinationId = true;
|
|
|
|
await _Settings.UpdateSetting(settings);
|
|
|
|
}
|
2021-11-26 15:13:41 +01:00
|
|
|
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);
|
|
|
|
}
|
2022-04-19 09:58:31 +02:00
|
|
|
if (!settings.LighingAddressDatabaseMigration)
|
|
|
|
{
|
|
|
|
await MigrateLighingAddressDatabaseMigration();
|
|
|
|
settings.LighingAddressDatabaseMigration = true;
|
2022-04-24 05:19:34 +02:00
|
|
|
}
|
|
|
|
if (!settings.AddStoreToPayout)
|
|
|
|
{
|
|
|
|
await MigrateAddStoreToPayout();
|
|
|
|
settings.AddStoreToPayout = true;
|
2022-04-19 09:58:31 +02:00
|
|
|
await _Settings.UpdateSetting(settings);
|
|
|
|
}
|
2020-06-28 11:00:51 +02:00
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
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
|
|
|
|
2022-04-19 09:58:31 +02: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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-26 15:13:41 +01:00
|
|
|
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
|
|
|
|
2021-10-21 17:43:02 +02: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;
|
|
|
|
}
|
2022-01-24 12:17:09 +01:00
|
|
|
var claim = await handler?.ParseClaimDestination(pmi, payoutData.GetBlob(_btcPayNetworkJsonSerializerSettings).Destination);
|
2021-12-31 08:59:02 +01:00
|
|
|
payoutData.Destination = claim.destination?.Id;
|
2021-10-21 17:43:02 +02:00
|
|
|
}
|
|
|
|
await ctx.SaveChangesAsync();
|
|
|
|
}
|
2020-06-28 11:00:51 +02:00
|
|
|
|
2021-10-11 12:46:05 +02:00
|
|
|
private async Task MigrateAppCustomOption()
|
|
|
|
{
|
|
|
|
await using var ctx = _DBContextFactory.CreateContext();
|
2021-10-28 12:23:21 +02:00
|
|
|
foreach (var app in await ctx.Apps.Include(data => data.StoreData).AsQueryable().ToArrayAsync())
|
2021-10-11 12:46:05 +02:00
|
|
|
{
|
|
|
|
ViewPointOfSaleViewModel.Item[] items;
|
|
|
|
string newTemplate;
|
|
|
|
switch (app.AppType)
|
|
|
|
{
|
|
|
|
case nameof(AppType.Crowdfund):
|
|
|
|
var settings1 = app.GetSettings<CrowdfundSettings>();
|
2021-10-28 12:23:21 +02:00
|
|
|
if (string.IsNullOrEmpty(settings1.TargetCurrency))
|
|
|
|
{
|
|
|
|
settings1.TargetCurrency = app.StoreData.GetStoreBlob().DefaultCurrency;
|
|
|
|
app.SetSettings(settings1);
|
|
|
|
}
|
2021-10-11 12:46:05 +02:00
|
|
|
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
|
|
|
|
2021-10-11 12:46:05 +02:00
|
|
|
case nameof(AppType.PointOfSale):
|
2021-12-31 08:59:02 +01:00
|
|
|
|
2022-02-27 06:19:02 +01:00
|
|
|
var settings2 = app.GetSettings<PointOfSaleSettings>();
|
2021-10-28 12:23:21 +02:00
|
|
|
if (string.IsNullOrEmpty(settings2.Currency))
|
|
|
|
{
|
|
|
|
settings2.Currency = app.StoreData.GetStoreBlob().DefaultCurrency;
|
|
|
|
app.SetSettings(settings2);
|
|
|
|
}
|
2021-10-11 12:46:05 +02:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2021-06-17 08:36:22 +02:00
|
|
|
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);
|
|
|
|
}
|
2021-06-17 08:36:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
await ctx.SaveChangesAsync();
|
|
|
|
}
|
|
|
|
|
2021-04-28 06:14:15 +02:00
|
|
|
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(),
|
2021-04-28 06:14:15 +02:00
|
|
|
UserHandle = u2FDevice.KeyHandle,
|
|
|
|
Descriptor = new PublicKeyCredentialDescriptor(u2FDevice.KeyHandle),
|
|
|
|
CredType = "u2f"
|
|
|
|
});
|
|
|
|
|
|
|
|
await ctx.AddAsync(fido2);
|
2021-12-31 08:59:02 +01:00
|
|
|
|
2021-04-28 06:14:15 +02: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;
|
|
|
|
}
|
|
|
|
|
2021-03-02 03:11:58 +01:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2020-12-29 09:58:35 +01:00
|
|
|
private async Task TransitionToStoreBlobAdditionalData()
|
|
|
|
{
|
|
|
|
await using var ctx = _DBContextFactory.CreateContext();
|
2021-01-01 09:01:33 +01:00
|
|
|
foreach (var store in await ctx.Stores.AsQueryable().ToArrayAsync())
|
2020-12-29 09:58:35 +01:00
|
|
|
{
|
|
|
|
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();
|
2020-12-29 09:58:35 +01:00
|
|
|
|
2022-01-14 09:50:29 +01:00
|
|
|
if (blob.AdditionalData.TryGetValue("walletKeyPathRoots", out var walletKeyPathRootsJToken))
|
|
|
|
{
|
|
|
|
var walletKeyPathRoots = walletKeyPathRootsJToken.ToObject<Dictionary<string, string>>();
|
2020-12-29 09:58:35 +01:00
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-12-29 09:58:35 +01: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())
|
2020-12-29 09:58:35 +01:00
|
|
|
{
|
2022-01-14 09:50:29 +01:00
|
|
|
var blob = store.GetStoreBlob();
|
2020-12-29 09:58:35 +01:00
|
|
|
|
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();
|
2020-12-29 09:58:35 +01:00
|
|
|
|
2022-01-14 09:50:29 +01:00
|
|
|
store.SetStoreBlob(blob);
|
2020-12-29 09:58:35 +01:00
|
|
|
}
|
2022-01-14 09:50:29 +01:00
|
|
|
|
|
|
|
await ctx.SaveChangesAsync();
|
2020-12-29 09:58:35 +01:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-12-29 09:58:35 +01: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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|