Merge pull request #2575 from NicolasDorier/refactor/wallet-cleanup

Cleanup some old code in wallet setup
This commit is contained in:
Nicolas Dorier
2021-06-17 21:20:28 +09:00
committed by GitHub
11 changed files with 122 additions and 224 deletions

View File

@@ -954,28 +954,6 @@ normal:
result = testnetParser.Parse(tpub); result = testnetParser.Parse(tpub);
Assert.Equal(tpub, result.ToString()); Assert.Equal(tpub, result.ToString());
testnetParser.HintScriptPubKey = BitcoinAddress
.Create("tb1q4s33amqm8l7a07zdxcunqnn3gcsjcfz3xc573l", testnetParser.Network).ScriptPubKey;
result = testnetParser.Parse(tpub);
Assert.Equal(tpub, result.ToString());
testnetParser.HintScriptPubKey = BitcoinAddress
.Create("2N2humNio3YTApSfY6VztQ9hQwDnhDvaqFQ", testnetParser.Network).ScriptPubKey;
result = testnetParser.Parse(tpub);
Assert.Equal($"{tpub}-[p2sh]", result.ToString());
testnetParser.HintScriptPubKey = BitcoinAddress
.Create("mwD8bHS65cdgUf6rZUUSoVhi3wNQFu1Nfi", testnetParser.Network).ScriptPubKey;
result = testnetParser.Parse(tpub);
Assert.Equal($"{tpub}-[legacy]", result.ToString());
testnetParser.HintScriptPubKey = BitcoinAddress
.Create("2N2humNio3YTApSfY6VztQ9hQwDnhDvaqFQ", testnetParser.Network).ScriptPubKey;
result = testnetParser.Parse($"{tpub}-[legacy]");
Assert.Equal($"{tpub}-[p2sh]", result.ToString());
result = testnetParser.Parse(tpub);
Assert.Equal($"{tpub}-[p2sh]", result.ToString());
var regtestParser = new DerivationSchemeParser(regtestNetworkProvider.GetNetwork<BTCPayNetwork>("BTC")); var regtestParser = new DerivationSchemeParser(regtestNetworkProvider.GetNetwork<BTCPayNetwork>("BTC"));
var parsed = var parsed =

View File

@@ -179,30 +179,18 @@ namespace BTCPayServer.Tests
if (StoreId is null) if (StoreId is null)
await CreateStoreAsync(); await CreateStoreAsync();
SupportedNetwork = parent.NetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode); SupportedNetwork = parent.NetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
var store = parent.PayTester.GetController<StoresController>(UserId, StoreId); var store = parent.PayTester.GetController<StoresController>(UserId, StoreId, true);
GenerateWalletResponseV = await parent.ExplorerClient.GenerateWalletAsync(new GenerateWalletRequest()
var generateRequest = new GenerateWalletRequest()
{ {
ScriptPubKeyType = segwit, ScriptPubKeyType = segwit,
SavePrivateKeys = importKeysToNBX, SavePrivateKeys = importKeysToNBX,
ImportKeysToRPC = importsKeysToBitcoinCore ImportKeysToRPC = importsKeysToBitcoinCore
}); };
await store.UpdateWallet(
new WalletSetupViewModel await store.GenerateWallet(StoreId, cryptoCode, WalletSetupMethod.HotWallet, generateRequest);
{ Assert.NotNull(store.GenerateWalletResponse);
StoreId = StoreId, GenerateWalletResponseV = store.GenerateWalletResponse;
Method = importKeysToNBX ? WalletSetupMethod.HotWallet : WalletSetupMethod.WatchOnly,
Enabled = true,
CryptoCode = cryptoCode,
Network = SupportedNetwork,
RootFingerprint = GenerateWalletResponseV.AccountKeyPath.MasterFingerprint.ToString(),
RootKeyPath = SupportedNetwork.GetRootKeyPath(),
Source = "NBXplorer",
AccountKey = GenerateWalletResponseV.AccountHDKey.Neuter().ToWif(),
DerivationSchemeFormat = "BTCPay",
KeyPath = GenerateWalletResponseV.AccountKeyPath.KeyPath.ToString(),
DerivationScheme = DerivationScheme.ToString(),
Confirmation = true
});
return new WalletId(StoreId, cryptoCode); return new WalletId(StoreId, cryptoCode);
} }

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Abstractions.Extensions; using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Abstractions.Models; using BTCPayServer.Abstractions.Models;
@@ -11,9 +12,11 @@ using BTCPayServer.Models;
using BTCPayServer.Models.StoreViewModels; using BTCPayServer.Models.StoreViewModels;
using BTCPayServer.Payments; using BTCPayServer.Payments;
using BTCPayServer.Services; using BTCPayServer.Services;
using ExchangeSharp;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using NBitcoin; using NBitcoin;
using NBXplorer; using NBXplorer;
using NBXplorer.DerivationStrategy; using NBXplorer.DerivationStrategy;
@@ -85,15 +88,6 @@ namespace BTCPayServer.Controllers
return NotFound(); return NotFound();
} }
if (!string.IsNullOrEmpty(vm.Config))
{
if (!DerivationSchemeSettings.TryParseFromJson(vm.Config, network, out strategy))
{
ModelState.AddModelError(nameof(vm.Config), "Config file was not in the correct format");
return View(vm.ViewName, vm);
}
}
if (vm.WalletFile != null) if (vm.WalletFile != null)
{ {
if (!DerivationSchemeSettings.TryParseFromWalletFile(await ReadAllText(vm.WalletFile), network, out strategy)) if (!DerivationSchemeSettings.TryParseFromWalletFile(await ReadAllText(vm.WalletFile), network, out strategy))
@@ -114,16 +108,13 @@ namespace BTCPayServer.Controllers
{ {
try try
{ {
var newStrategy = ParseDerivationStrategy(vm.DerivationScheme, null, network); strategy = ParseDerivationStrategy(vm.DerivationScheme, network);
if (newStrategy.AccountDerivation != strategy?.AccountDerivation) strategy.Source = "ManualDerivationScheme";
{ if (!string.IsNullOrEmpty(vm.AccountKey))
var accountKey = string.IsNullOrEmpty(vm.AccountKey)
? null
: new BitcoinExtPubKey(vm.AccountKey, network.NBitcoinNetwork);
if (accountKey != null)
{ {
var accountKey = new BitcoinExtPubKey(vm.AccountKey, network.NBitcoinNetwork);
var accountSettings = var accountSettings =
newStrategy.AccountKeySettings.FirstOrDefault(a => a.AccountKey == accountKey); strategy.AccountKeySettings.FirstOrDefault(a => a.AccountKey == accountKey);
if (accountSettings != null) if (accountSettings != null)
{ {
accountSettings.AccountKeyPath = accountSettings.AccountKeyPath =
@@ -134,11 +125,8 @@ namespace BTCPayServer.Controllers
NBitcoin.DataEncoders.Encoders.Hex.DecodeData(vm.RootFingerprint)); NBitcoin.DataEncoders.Encoders.Hex.DecodeData(vm.RootFingerprint));
} }
} }
strategy = newStrategy;
strategy.Source = vm.Source;
vm.DerivationScheme = strategy.AccountDerivation.ToString(); vm.DerivationScheme = strategy.AccountDerivation.ToString();
} ModelState.Remove(nameof(vm.DerivationScheme));
} }
catch catch
{ {
@@ -146,34 +134,33 @@ namespace BTCPayServer.Controllers
return View(vm.ViewName, vm); return View(vm.ViewName, vm);
} }
} }
else else if (!string.IsNullOrEmpty(vm.Config))
{
if (!DerivationSchemeSettings.TryParseFromJson(UnprotectString(vm.Config), network, out strategy))
{
ModelState.AddModelError(nameof(vm.Config), "Config file was not in the correct format");
return View(vm.ViewName, vm);
}
}
if (strategy is null)
{ {
ModelState.AddModelError(nameof(vm.DerivationScheme), "Please provide your extended public key"); ModelState.AddModelError(nameof(vm.DerivationScheme), "Please provide your extended public key");
return View(vm.ViewName, vm); return View(vm.ViewName, vm);
} }
var oldConfig = vm.Config; vm.Config = ProtectString(strategy.ToJson());
vm.Config = strategy?.ToJson(); ModelState.Remove(nameof(vm.Config));
var configChanged = oldConfig != vm.Config;
PaymentMethodId paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike); PaymentMethodId paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike);
var storeBlob = store.GetStoreBlob(); var storeBlob = store.GetStoreBlob();
var willBeExcluded = !vm.Enabled; if (vm.Confirmation)
var showAddress = // Show addresses if:
// - If the user is testing the hint address in confirmation screen
(vm.Confirmation && !string.IsNullOrWhiteSpace(vm.HintAddress)) ||
// - The user is clicking on continue after changing the config
(!vm.Confirmation && configChanged);
showAddress = showAddress && strategy != null;
if (!showAddress)
{ {
try try
{ {
if (strategy != null)
await wallet.TrackAsync(strategy.AccountDerivation); await wallet.TrackAsync(strategy.AccountDerivation);
store.SetSupportedPaymentMethod(paymentMethodId, strategy); store.SetSupportedPaymentMethod(paymentMethodId, strategy);
storeBlob.SetExcluded(paymentMethodId, willBeExcluded); storeBlob.SetExcluded(paymentMethodId, false);
storeBlob.Hints.Wallet = false; storeBlob.Hints.Wallet = false;
store.SetStoreBlob(storeBlob); store.SetStoreBlob(storeBlob);
} }
@@ -191,43 +178,16 @@ namespace BTCPayServer.Controllers
// This is success case when derivation scheme is added to the store // This is success case when derivation scheme is added to the store
return RedirectToAction(nameof(UpdateStore), new { storeId = vm.StoreId }); return RedirectToAction(nameof(UpdateStore), new { storeId = vm.StoreId });
} }
if (!string.IsNullOrEmpty(vm.HintAddress))
{
BitcoinAddress address;
try
{
address = BitcoinAddress.Create(vm.HintAddress, network.NBitcoinNetwork);
}
catch
{
ModelState.AddModelError(nameof(vm.HintAddress), "Invalid hint address");
return ConfirmAddresses(vm, strategy); return ConfirmAddresses(vm, strategy);
} }
try private string ProtectString(string str)
{ {
var newStrategy = ParseDerivationStrategy(vm.DerivationScheme, address.ScriptPubKey, network); return Convert.ToBase64String(DataProtector.Protect(Encoding.UTF8.GetBytes(str)));
if (newStrategy.AccountDerivation != strategy.AccountDerivation) }
private string UnprotectString(string str)
{ {
strategy.AccountDerivation = newStrategy.AccountDerivation; return Encoding.UTF8.GetString(DataProtector.Unprotect(Convert.FromBase64String(str)));
strategy.AccountOriginal = null;
}
}
catch
{
ModelState.AddModelError(nameof(vm.HintAddress), "Impossible to find a match with this address. Are you sure the wallet and address provided are correct and from the same source?");
return ConfirmAddresses(vm, strategy);
}
vm.HintAddress = "";
TempData[WellKnownTempData.SuccessMessage] =
"Address successfully found, please verify that the rest is correct and click on \"Confirm\"";
ModelState.Remove(nameof(vm.HintAddress));
ModelState.Remove(nameof(vm.DerivationScheme));
}
return ConfirmAddresses(vm, strategy);
} }
[HttpGet("{storeId}/onchain/{cryptoCode}/generate/{method?}")] [HttpGet("{storeId}/onchain/{cryptoCode}/generate/{method?}")]
@@ -246,14 +206,6 @@ namespace BTCPayServer.Controllers
return NotFound(); return NotFound();
} }
var derivation = GetExistingDerivationStrategy(vm.CryptoCode, store);
if (derivation != null)
{
vm.DerivationScheme = derivation.AccountDerivation.ToString();
vm.Config = derivation.ToJson();
}
vm.Enabled = !store.GetStoreBlob().IsExcluded(new PaymentMethodId(vm.CryptoCode, PaymentTypes.BTCLike));
vm.CanUseHotWallet = hotWallet; vm.CanUseHotWallet = hotWallet;
vm.CanUseRPCImport = rpcImport; vm.CanUseRPCImport = rpcImport;
vm.RootKeyPath = network.GetRootKeyPath(); vm.RootKeyPath = network.GetRootKeyPath();
@@ -270,7 +222,7 @@ namespace BTCPayServer.Controllers
return View(vm.ViewName, vm); return View(vm.ViewName, vm);
} }
internal GenerateWalletResponse GenerateWalletResponse;
[HttpPost("{storeId}/onchain/{cryptoCode}/generate/{method}")] [HttpPost("{storeId}/onchain/{cryptoCode}/generate/{method}")]
public async Task<IActionResult> GenerateWallet(string storeId, string cryptoCode, WalletSetupMethod method, GenerateWalletRequest request) public async Task<IActionResult> GenerateWallet(string storeId, string cryptoCode, WalletSetupMethod method, GenerateWalletRequest request)
{ {
@@ -288,6 +240,7 @@ namespace BTCPayServer.Controllers
var client = _ExplorerProvider.GetExplorerClient(cryptoCode); var client = _ExplorerProvider.GetExplorerClient(cryptoCode);
var isImport = method == WalletSetupMethod.Seed; var isImport = method == WalletSetupMethod.Seed;
var vm = new WalletSetupViewModel var vm = new WalletSetupViewModel
{ {
StoreId = storeId, StoreId = storeId,
@@ -297,8 +250,8 @@ namespace BTCPayServer.Controllers
Confirmation = string.IsNullOrEmpty(request.ExistingMnemonic), Confirmation = string.IsNullOrEmpty(request.ExistingMnemonic),
Network = network, Network = network,
RootKeyPath = network.GetRootKeyPath(), RootKeyPath = network.GetRootKeyPath(),
Enabled = !store.GetStoreBlob().IsExcluded(new PaymentMethodId(cryptoCode, PaymentTypes.BTCLike)), Source = isImport ? "SeedImported" : "NBXplorerGenerated",
Source = "NBXplorer", IsHotWallet = isImport ? request.SavePrivateKeys : method == WalletSetupMethod.HotWallet,
DerivationSchemeFormat = "BTCPay", DerivationSchemeFormat = "BTCPay",
CanUseHotWallet = true, CanUseHotWallet = true,
CanUseRPCImport = rpcImport CanUseRPCImport = rpcImport
@@ -329,11 +282,26 @@ namespace BTCPayServer.Controllers
return View(vm.ViewName, vm); return View(vm.ViewName, vm);
} }
var derivationSchemeSettings = new DerivationSchemeSettings(response.DerivationScheme, network);
if (method == WalletSetupMethod.Seed)
{
derivationSchemeSettings.Source = "ImportedSeed";
derivationSchemeSettings.IsHotWallet = request.SavePrivateKeys;
}
else
{
derivationSchemeSettings.Source = "NBXplorerGenerated";
derivationSchemeSettings.IsHotWallet = method == WalletSetupMethod.HotWallet;
}
var accountSettings = derivationSchemeSettings.GetSigningAccountKeySettings();
accountSettings.AccountKeyPath = response.AccountKeyPath.KeyPath;
accountSettings.RootFingerprint = response.AccountKeyPath.MasterFingerprint;
derivationSchemeSettings.AccountOriginal = response.DerivationScheme.ToString();
// Set wallet properties from generate response // Set wallet properties from generate response
vm.RootFingerprint = response.AccountKeyPath.MasterFingerprint.ToString(); vm.Config = ProtectString(derivationSchemeSettings.ToJson());
vm.DerivationScheme = response.DerivationScheme.ToString();
vm.AccountKey = response.AccountHDKey.Neuter().ToWif();
vm.KeyPath = response.AccountKeyPath.KeyPath.ToString();
var result = await UpdateWallet(vm); var result = await UpdateWallet(vm);
@@ -355,6 +323,10 @@ namespace BTCPayServer.Controllers
IsStored = request.SavePrivateKeys, IsStored = request.SavePrivateKeys,
ReturnUrl = Url.Action(nameof(GenerateWalletConfirm), new { storeId, cryptoCode }) ReturnUrl = Url.Action(nameof(GenerateWalletConfirm), new { storeId, cryptoCode })
}; };
if (this._BTCPayEnv.IsDeveloping)
{
GenerateWalletResponse = response;
}
return this.RedirectToRecoverySeedBackup(seedVm); return this.RedirectToRecoverySeedBackup(seedVm);
} }
@@ -408,8 +380,7 @@ namespace BTCPayServer.Controllers
vm.RootFingerprint = derivation.GetSigningAccountKeySettings().RootFingerprint.ToString(); vm.RootFingerprint = derivation.GetSigningAccountKeySettings().RootFingerprint.ToString();
vm.DerivationScheme = derivation.AccountDerivation.ToString(); vm.DerivationScheme = derivation.AccountDerivation.ToString();
vm.KeyPath = derivation.GetSigningAccountKeySettings().AccountKeyPath?.ToString(); vm.KeyPath = derivation.GetSigningAccountKeySettings().AccountKeyPath?.ToString();
vm.Config = derivation.ToJson(); vm.Config = ProtectString(derivation.ToJson());
vm.Enabled = !store.GetStoreBlob().IsExcluded(new PaymentMethodId(vm.CryptoCode, PaymentTypes.BTCLike));
vm.IsHotWallet = isHotWallet; vm.IsHotWallet = isHotWallet;
return View(vm); return View(vm);

View File

@@ -27,6 +27,7 @@ using BTCPayServer.Services.Stores;
using BTCPayServer.Services.Wallets; using BTCPayServer.Services.Wallets;
using BundlerMinifier.TagHelpers; using BundlerMinifier.TagHelpers;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@@ -69,7 +70,8 @@ namespace BTCPayServer.Controllers
AppService appService, AppService appService,
IWebHostEnvironment webHostEnvironment, IWebHostEnvironment webHostEnvironment,
WebhookNotificationManager webhookNotificationManager, WebhookNotificationManager webhookNotificationManager,
IOptions<LightningNetworkOptions> lightningNetworkOptions) IOptions<LightningNetworkOptions> lightningNetworkOptions,
IDataProtectionProvider dataProtector)
{ {
_RateFactory = rateFactory; _RateFactory = rateFactory;
_Repo = repo; _Repo = repo;
@@ -85,6 +87,7 @@ namespace BTCPayServer.Controllers
_appService = appService; _appService = appService;
_webHostEnvironment = webHostEnvironment; _webHostEnvironment = webHostEnvironment;
_lightningNetworkOptions = lightningNetworkOptions; _lightningNetworkOptions = lightningNetworkOptions;
DataProtector = dataProtector.CreateProtector("ConfigProtector");
WebhookNotificationManager = webhookNotificationManager; WebhookNotificationManager = webhookNotificationManager;
_EventAggregator = eventAggregator; _EventAggregator = eventAggregator;
_NetworkProvider = networkProvider; _NetworkProvider = networkProvider;
@@ -689,10 +692,9 @@ namespace BTCPayServer.Controllers
} }
private DerivationSchemeSettings ParseDerivationStrategy(string derivationScheme, Script hint, BTCPayNetwork network) private DerivationSchemeSettings ParseDerivationStrategy(string derivationScheme, BTCPayNetwork network)
{ {
var parser = new DerivationSchemeParser(network); var parser = new DerivationSchemeParser(network);
parser.HintScriptPubKey = hint;
try try
{ {
var derivationSchemeSettings = new DerivationSchemeSettings(); var derivationSchemeSettings = new DerivationSchemeSettings();
@@ -827,6 +829,7 @@ namespace BTCPayServer.Controllers
public string GeneratedPairingCode { get; set; } public string GeneratedPairingCode { get; set; }
public WebhookNotificationManager WebhookNotificationManager { get; } public WebhookNotificationManager WebhookNotificationManager { get; }
public IDataProtector DataProtector { get; }
[HttpGet] [HttpGet]
[Route("{storeId}/Tokens/Create")] [Route("{storeId}/Tokens/Create")]

View File

@@ -1088,7 +1088,9 @@ namespace BTCPayServer.Controllers
DerivationScheme = derivationSchemeSettings.AccountDerivation.ToString(), DerivationScheme = derivationSchemeSettings.AccountDerivation.ToString(),
DerivationSchemeInput = derivationSchemeSettings.AccountOriginal, DerivationSchemeInput = derivationSchemeSettings.AccountOriginal,
SelectedSigningKey = derivationSchemeSettings.SigningKey.ToString(), SelectedSigningKey = derivationSchemeSettings.SigningKey.ToString(),
NBXSeedAvailable = await CanUseHotWallet() && !string.IsNullOrEmpty(await ExplorerClientProvider.GetExplorerClient(walletId.CryptoCode) NBXSeedAvailable = derivationSchemeSettings.IsHotWallet &&
await CanUseHotWallet() &&
!string.IsNullOrEmpty(await ExplorerClientProvider.GetExplorerClient(walletId.CryptoCode)
.GetMetadataAsync<string>(GetDerivationSchemeSettings(walletId).AccountDerivation, .GetMetadataAsync<string>(GetDerivationSchemeSettings(walletId).AccountDerivation,
WellknownMetadataKeys.MasterHDKey)) WellknownMetadataKeys.MasterHDKey))
}; };

View File

@@ -13,8 +13,6 @@ namespace BTCPayServer
public Network Network => BtcPayNetwork.NBitcoinNetwork; public Network Network => BtcPayNetwork.NBitcoinNetwork;
public Script HintScriptPubKey { get; set; }
public DerivationSchemeParser(BTCPayNetwork expectedNetwork) public DerivationSchemeParser(BTCPayNetwork expectedNetwork)
{ {
if (expectedNetwork == null) if (expectedNetwork == null)
@@ -131,19 +129,6 @@ namespace BTCPayServer
HashSet<string> hintedLabels = new HashSet<string>(); HashSet<string> hintedLabels = new HashSet<string>();
var hintDestination = HintScriptPubKey?.GetDestination();
if (hintDestination != null)
{
if (hintDestination is KeyId)
{
hintedLabels.Add("legacy");
}
if (hintDestination is ScriptId)
{
hintedLabels.Add("p2sh");
}
}
if (!Network.Consensus.SupportSegwit) if (!Network.Consensus.SupportSegwit)
{ {
hintedLabels.Add("legacy"); hintedLabels.Add("legacy");
@@ -152,8 +137,7 @@ namespace BTCPayServer
try try
{ {
var result = BtcPayNetwork.NBXplorerNetwork.DerivationStrategyFactory.Parse(str); return BtcPayNetwork.NBXplorerNetwork.DerivationStrategyFactory.Parse(str);
return FindMatch(hintedLabels, result);
} }
catch catch
{ {
@@ -205,22 +189,13 @@ namespace BTCPayServer
catch { continue; } catch { continue; }
} }
if (hintDestination != null)
{
if (hintDestination is WitKeyId)
{
hintedLabels.Remove("legacy");
hintedLabels.Remove("p2sh");
}
}
str = string.Join('-', parts.Where(p => !IsLabel(p))); str = string.Join('-', parts.Where(p => !IsLabel(p)));
foreach (var label in hintedLabels) foreach (var label in hintedLabels)
{ {
str = $"{str}-[{label}]"; str = $"{str}-[{label}]";
} }
return FindMatch(hintedLabels, BtcPayNetwork.NBXplorerNetwork.DerivationStrategyFactory.Parse(str)); return BtcPayNetwork.NBXplorerNetwork.DerivationStrategyFactory.Parse(str);
} }
public static BitcoinExtPubKey GetBitcoinExtPubKeyByNetwork(Network network, byte[] data) public static BitcoinExtPubKey GetBitcoinExtPubKeyByNetwork(Network network, byte[] data)
@@ -235,27 +210,6 @@ namespace BTCPayServer
} }
} }
private DerivationStrategyBase FindMatch(HashSet<string> hintLabels, DerivationStrategyBase result)
{
var firstKeyPath = new KeyPath("0/0");
if (HintScriptPubKey == null)
return result;
if (HintScriptPubKey == result.GetDerivation(firstKeyPath).ScriptPubKey)
return result;
if (result is MultisigDerivationStrategy)
hintLabels.Add("keeporder");
var resultNoLabels = result.ToString();
resultNoLabels = string.Join('-', resultNoLabels.Split('-').Where(p => !IsLabel(p)));
foreach (var labels in ItemCombinations(hintLabels.ToList()))
{
var hinted = BtcPayNetwork.NBXplorerNetwork.DerivationStrategyFactory.Parse(resultNoLabels + '-' + string.Join('-', labels.Select(l => $"[{l}]").ToArray()));
if (HintScriptPubKey == hinted.GetDerivation(firstKeyPath).ScriptPubKey)
return hinted;
}
throw new FormatException("Could not find any match");
}
private static bool IsLabel(string v) private static bool IsLabel(string v)
{ {

View File

@@ -276,8 +276,8 @@ namespace BTCPayServer
[JsonIgnore] [JsonIgnore]
public BTCPayNetwork Network { get; set; } public BTCPayNetwork Network { get; set; }
public string Source { get; set; } public string Source { get; set; }
[JsonIgnore]
public bool IsHotWallet => Source == "NBXplorer"; public bool IsHotWallet { get; set; }
[Obsolete("Use GetSigningAccountKeySettings().AccountKeyPath instead")] [Obsolete("Use GetSigningAccountKeySettings().AccountKeyPath instead")]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]

View File

@@ -128,6 +128,12 @@ namespace BTCPayServer.Hosting
settings.MigrateU2FToFIDO2 = true; settings.MigrateU2FToFIDO2 = true;
await _Settings.UpdateSetting(settings); await _Settings.UpdateSetting(settings);
} }
if (!settings.MigrateHotwalletProperty)
{
await MigrateHotwalletProperty();
settings.MigrateHotwalletProperty = true;
await _Settings.UpdateSetting(settings);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -136,6 +142,20 @@ namespace BTCPayServer.Hosting
} }
} }
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";
paymentMethod.Source = "NBXplorerGenerated";
}
}
await ctx.SaveChangesAsync();
}
private async Task MigrateU2FToFIDO2() private async Task MigrateU2FToFIDO2()
{ {
await using var ctx = _DBContextFactory.CreateContext(); await using var ctx = _DBContextFactory.CreateContext();

View File

@@ -20,10 +20,7 @@ namespace BTCPayServer.Models.StoreViewModels
public string KeyPath { get; set; } public string KeyPath { get; set; }
[Display(Name = "Root fingerprint")] [Display(Name = "Root fingerprint")]
public string RootFingerprint { get; set; } public string RootFingerprint { get; set; }
[Display(Name = "Hint address")]
public string HintAddress { get; set; }
public bool Confirmation { get; set; } public bool Confirmation { get; set; }
public bool Enabled { get; set; } = true;
public KeyPath RootKeyPath { get; set; } public KeyPath RootKeyPath { get; set; }

View File

@@ -2,6 +2,7 @@ namespace BTCPayServer.Services
{ {
public class MigrationSettings public class MigrationSettings
{ {
public bool MigrateHotwalletProperty { get; set; }
public bool MigrateU2FToFIDO2{ get; set; } public bool MigrateU2FToFIDO2{ get; set; }
public bool UnreachableStoreCheck { get; set; } public bool UnreachableStoreCheck { get; set; }
public bool DeprecatedLightningConnectionStringCheck { get; set; } public bool DeprecatedLightningConnectionStringCheck { get; set; }

View File

@@ -50,7 +50,6 @@
<input asp-for="Config" type="hidden"/> <input asp-for="Config" type="hidden"/>
<input asp-for="Confirmation" type="hidden"/> <input asp-for="Confirmation" type="hidden"/>
<input asp-for="DerivationScheme" type="hidden"/> <input asp-for="DerivationScheme" type="hidden"/>
<input asp-for="Enabled" type="hidden"/>
<div class="form-group"> <div class="form-group">
<table class="table table-sm table-responsive-md"> <table class="table table-sm table-responsive-md">
@@ -83,21 +82,6 @@
</table> </table>
</div> </div>
<div class="text-center mb-4">
<button class="btn btn-link" type="button" data-bs-toggle="collapse" data-bs-target="#wrong-addresses" aria-expanded="false" aria-controls="wrong-addresses">
Wrong addresses?
</button>
<div id="wrong-addresses" class="collapse @(ViewContext.ModelState.IsValid ? "" : "show")">
<div class="pb-1">
<div class="form-group">
<label asp-for="HintAddress" class="form-label">Help us to find the correct settings by telling us the first address of your wallet.</label>
<input asp-for="HintAddress" class="form-control"/>
<span asp-validation-for="HintAddress" class="text-danger"></span>
</div>
</div>
</div>
</div>
<div class="text-center"> <div class="text-center">
<button name="command" type="submit" class="btn btn-primary" value="save" id="Confirm">Confirm</button> <button name="command" type="submit" class="btn btn-primary" value="save" id="Confirm">Confirm</button>
</div> </div>