diff --git a/BTCPayServer.Tests/AltcoinTests/AltcoinTests.cs b/BTCPayServer.Tests/AltcoinTests/AltcoinTests.cs index 762411bf5..dbb1d68fb 100644 --- a/BTCPayServer.Tests/AltcoinTests/AltcoinTests.cs +++ b/BTCPayServer.Tests/AltcoinTests/AltcoinTests.cs @@ -1,61 +1,31 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.IO; using System.Linq; -using System.Net; -using System.Net.Http; -using System.Runtime.CompilerServices; -using System.Security; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; using System.Threading.Tasks; -using BTCPayServer.Client; -using BTCPayServer.Client.Models; -using BTCPayServer.Configuration; using BTCPayServer.Controllers; using BTCPayServer.Data; -using BTCPayServer.Events; using BTCPayServer.HostedServices; using BTCPayServer.Lightning; -using BTCPayServer.Models; -using BTCPayServer.Models.AccountViewModels; using BTCPayServer.Models.AppViewModels; -using BTCPayServer.Models.InvoicingModels; -using BTCPayServer.Models.ServerViewModels; using BTCPayServer.Models.StoreViewModels; using BTCPayServer.Models.WalletViewModels; using BTCPayServer.Payments; using BTCPayServer.Payments.Bitcoin; using BTCPayServer.Payments.Lightning; -using BTCPayServer.Rating; -using BTCPayServer.Security.Bitpay; -using BTCPayServer.Services; using BTCPayServer.Services.Apps; using BTCPayServer.Services.Invoices; -using BTCPayServer.Services.Rates; using BTCPayServer.Tests.Logging; -using BTCPayServer.U2F.Models; -using BTCPayServer.Validation; -using ExchangeSharp; using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; using NBitcoin; -using NBitcoin.DataEncoders; -using NBitcoin.Payment; using NBitcoin.Scripting.Parser; using NBitpayClient; using NBXplorer.DerivationStrategy; using NBXplorer.Models; -using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using Newtonsoft.Json.Schema; using OpenQA.Selenium; using Xunit; using Xunit.Abstractions; -using Xunit.Sdk; -using RatesViewModel = BTCPayServer.Models.StoreViewModels.RatesViewModel; namespace BTCPayServer.Tests { @@ -72,7 +42,7 @@ namespace BTCPayServer.Tests [Trait("Integration", "Integration")] [Trait("Altcoins", "Altcoins")] [Trait("Lightning", "Lightning")] - public async Task CanAddDerivationSchemes() + public async Task CanSetupWallet() { using (var tester = ServerTester.Create()) { @@ -99,37 +69,37 @@ namespace BTCPayServer.Tests Assert.Equal(3, invoice.CryptoInfo.Length); var controller = user.GetController(); - var lightningVM = - (LightningNodeViewModel)Assert.IsType(controller.AddLightningNode(user.StoreId, "BTC")) - .Model; - Assert.True(lightningVM.Enabled); - lightningVM.Enabled = false; - controller.AddLightningNode(user.StoreId, lightningVM, "save", "BTC").GetAwaiter().GetResult(); - lightningVM = - (LightningNodeViewModel)Assert.IsType(controller.AddLightningNode(user.StoreId, "BTC")) - .Model; - Assert.False(lightningVM.Enabled); + var lightningVm = (LightningNodeViewModel)Assert.IsType(controller.AddLightningNode(user.StoreId, "BTC")).Model; + Assert.True(lightningVm.Enabled); + lightningVm.Enabled = false; + controller.AddLightningNode(user.StoreId, lightningVm, "save", "BTC").GetAwaiter().GetResult(); + lightningVm = (LightningNodeViewModel)Assert.IsType(controller.AddLightningNode(user.StoreId, "BTC")).Model; + Assert.False(lightningVm.Enabled); + + WalletSetupViewModel setupVm; + var storeId = user.StoreId; + var cryptoCode = "BTC"; + var response = await controller.GenerateWallet(storeId, cryptoCode, WalletSetupMethod.GenerateOptions, new GenerateWalletRequest()); + Assert.IsType(response); + + // Get setup view model from modify action + response = await controller.ModifyWallet(new WalletSetupViewModel { StoreId = storeId, CryptoCode = cryptoCode }); + setupVm = (WalletSetupViewModel)Assert.IsType(response).Model; + Assert.True(setupVm.Enabled); // Only Enabling/Disabling the payment method must redirect to store page - var derivationVM = (DerivationSchemeViewModel)Assert - .IsType(await controller.AddDerivationScheme(user.StoreId, "BTC")).Model; - Assert.True(derivationVM.Enabled); - derivationVM.Enabled = false; - Assert.IsType(controller.AddDerivationScheme(user.StoreId, derivationVM, "BTC") - .GetAwaiter().GetResult()); - derivationVM = (DerivationSchemeViewModel)Assert - .IsType(await controller.AddDerivationScheme(user.StoreId, "BTC")).Model; - Assert.False(derivationVM.Enabled); + setupVm.Enabled = false; + response = controller.UpdateWallet(setupVm).GetAwaiter().GetResult(); + Assert.IsType(response); - // Clicking next without changing anything should send to the confirmation screen - derivationVM = (DerivationSchemeViewModel)Assert - .IsType(await controller.AddDerivationScheme(user.StoreId, "BTC")).Model; - derivationVM = (DerivationSchemeViewModel)Assert.IsType(controller - .AddDerivationScheme(user.StoreId, derivationVM, "BTC").GetAwaiter().GetResult()).Model; - Assert.True(derivationVM.Confirmation); + response = await controller.ModifyWallet(new WalletSetupViewModel { StoreId = storeId, CryptoCode = cryptoCode }); + setupVm = (WalletSetupViewModel)Assert.IsType(response).Model; + Assert.False(setupVm.Enabled); + + var oldScheme = setupVm.DerivationScheme; invoice = user.BitPay.CreateInvoice( - new Invoice() + new Invoice { Price = 1.5m, Currency = "USD", @@ -143,76 +113,57 @@ namespace BTCPayServer.Tests Assert.Equal("LTC", invoice.CryptoInfo[0].CryptoCode); // Removing the derivation scheme, should redirect to store page - var oldScheme = derivationVM.DerivationScheme; - derivationVM = (DerivationSchemeViewModel)Assert - .IsType(await controller.AddDerivationScheme(user.StoreId, "BTC")).Model; - derivationVM.DerivationScheme = null; - Assert.IsType(controller.AddDerivationScheme(user.StoreId, derivationVM, "BTC") - .GetAwaiter().GetResult()); + response = controller.ConfirmDeleteWallet(user.StoreId, "BTC").GetAwaiter().GetResult(); + Assert.IsType(response); - // Setting it again should redirect to the confirmation page - derivationVM = (DerivationSchemeViewModel)Assert - .IsType(await controller.AddDerivationScheme(user.StoreId, "BTC")).Model; - derivationVM.DerivationScheme = oldScheme; - derivationVM = (DerivationSchemeViewModel)Assert.IsType(controller - .AddDerivationScheme(user.StoreId, derivationVM, "BTC").GetAwaiter().GetResult()).Model; - Assert.True(derivationVM.Confirmation); + // Setting it again should show the confirmation page + response = await controller.UpdateWallet(new WalletSetupViewModel {StoreId = storeId, CryptoCode = cryptoCode, DerivationScheme = oldScheme }); + setupVm = (WalletSetupViewModel)Assert.IsType(response).Model; + Assert.True(setupVm.Confirmation); + // The following part posts a wallet update, confirms it and checks the result - //cobo vault file + // cobo vault file var content = "{\"ExtPubKey\":\"xpub6CEqRFZ7yZxCFXuEWZBAdnC8bdvu9SRHevaoU2SsW9ZmKhrCShmbpGZWwaR15hdLURf8hg47g4TpPGaqEU8hw5LEJCE35AUhne67XNyFGBk\",\"MasterFingerprint\":\"7a7563b5\",\"DerivationPath\":\"M\\/84'\\/0'\\/0'\",\"CoboVaultFirmwareVersion\":\"1.2.0(BTC-Only)\"}"; - derivationVM = (DerivationSchemeViewModel)Assert - .IsType(await controller.AddDerivationScheme(user.StoreId, "BTC")).Model; - derivationVM.WalletFile = TestUtils.GetFormFile("wallet3.json", content); - derivationVM.Enabled = true; - derivationVM = (DerivationSchemeViewModel)Assert.IsType(controller - .AddDerivationScheme(user.StoreId, derivationVM, "BTC").GetAwaiter().GetResult()).Model; - Assert.True(derivationVM.Confirmation); - Assert.IsType(controller.AddDerivationScheme(user.StoreId, derivationVM, "BTC") - .GetAwaiter().GetResult()); - - //wasabi wallet file - content = - "{\r\n \"EncryptedSecret\": \"6PYWBQ1zsukowsnTNA57UUx791aBuJusm7E4egXUmF5WGw3tcdG3cmTL57\",\r\n \"ChainCode\": \"waSIVbn8HaoovoQg/0t8IS1+ZCxGsJRGFT21i06nWnc=\",\r\n \"MasterFingerprint\": \"7a7563b5\",\r\n \"ExtPubKey\": \"xpub6CEqRFZ7yZxCFXuEWZBAdnC8bdvu9SRHevaoU2SsW9ZmKhrCShmbpGZWwaR15hdLURf8hg47g4TpPGaqEU8hw5LEJCE35AUhne67XNyFGBk\",\r\n \"PasswordVerified\": false,\r\n \"MinGapLimit\": 21,\r\n \"AccountKeyPath\": \"84'/0'/0'\",\r\n \"BlockchainState\": {\r\n \"Network\": \"RegTest\",\r\n \"Height\": \"0\"\r\n },\r\n \"HdPubKeys\": []\r\n}"; - - derivationVM = (DerivationSchemeViewModel)Assert - .IsType(await controller.AddDerivationScheme(user.StoreId, "BTC")).Model; - derivationVM.WalletFile = TestUtils.GetFormFile("wallet4.json", content); - derivationVM.Enabled = true; - derivationVM = (DerivationSchemeViewModel)Assert.IsType(controller - .AddDerivationScheme(user.StoreId, derivationVM, "BTC").GetAwaiter().GetResult()).Model; - Assert.True(derivationVM.Confirmation); - Assert.IsType(controller.AddDerivationScheme(user.StoreId, derivationVM, "BTC") - .GetAwaiter().GetResult()); + response = await controller.UpdateWallet(new WalletSetupViewModel {StoreId = storeId, CryptoCode = cryptoCode, WalletFile = TestUtils.GetFormFile("cobovault.json", content)}); + setupVm = (WalletSetupViewModel)Assert.IsType(response).Model; + Assert.True(setupVm.Confirmation); + response = await controller.UpdateWallet(setupVm); + Assert.IsType(response); + response = await controller.ModifyWallet(new WalletSetupViewModel { StoreId = storeId, CryptoCode = cryptoCode }); + setupVm = (WalletSetupViewModel)Assert.IsType(response).Model; + Assert.Equal("CoboVault", setupVm.Source); + // wasabi wallet file + content = "{\r\n \"EncryptedSecret\": \"6PYWBQ1zsukowsnTNA57UUx791aBuJusm7E4egXUmF5WGw3tcdG3cmTL57\",\r\n \"ChainCode\": \"waSIVbn8HaoovoQg/0t8IS1+ZCxGsJRGFT21i06nWnc=\",\r\n \"MasterFingerprint\": \"7a7563b5\",\r\n \"ExtPubKey\": \"xpub6CEqRFZ7yZxCFXuEWZBAdnC8bdvu9SRHevaoU2SsW9ZmKhrCShmbpGZWwaR15hdLURf8hg47g4TpPGaqEU8hw5LEJCE35AUhne67XNyFGBk\",\r\n \"PasswordVerified\": false,\r\n \"MinGapLimit\": 21,\r\n \"AccountKeyPath\": \"84'/0'/0'\",\r\n \"BlockchainState\": {\r\n \"Network\": \"RegTest\",\r\n \"Height\": \"0\"\r\n },\r\n \"HdPubKeys\": []\r\n}"; + response = await controller.UpdateWallet(new WalletSetupViewModel {StoreId = storeId, CryptoCode = cryptoCode, WalletFile = TestUtils.GetFormFile("wasabi.json", content)}); + setupVm = (WalletSetupViewModel)Assert.IsType(response).Model; + Assert.True(setupVm.Confirmation); + response = await controller.UpdateWallet(setupVm); + Assert.IsType(response); + response = await controller.ModifyWallet(new WalletSetupViewModel { StoreId = storeId, CryptoCode = cryptoCode }); + setupVm = (WalletSetupViewModel)Assert.IsType(response).Model; + Assert.Equal("WasabiFile", setupVm.Source); // Can we upload coldcard settings? (Should fail, we are giving a mainnet file to a testnet network) - derivationVM = (DerivationSchemeViewModel)Assert - .IsType(await controller.AddDerivationScheme(user.StoreId, "BTC")).Model; - content = - "{\"keystore\": {\"ckcc_xpub\": \"xpub661MyMwAqRbcGVBsTGeNZN6QGVHmMHLdSA4FteGsRrEriu4pnVZMZWnruFFFXkMnyoBjyHndD3Qwcfz4MPzBUxjSevweNFQx7SAYZATtcDw\", \"xpub\": \"ypub6WWc2gWwHbdnAAyJDnR4SPL1phRh7REqrPBfZeizaQ1EmTshieRXJC3Z5YoU4wkcdKHEjQGkh6AYEzCQC1Kz3DNaWSwdc1pc8416hAjzqyD\", \"label\": \"Coldcard Import 0x60d1af8b\", \"ckcc_xfp\": 1624354699, \"type\": \"hardware\", \"hw_type\": \"coldcard\", \"derivation\": \"m/49'/0'/0'\"}, \"wallet_type\": \"standard\", \"use_encryption\": false, \"seed_version\": 17}"; - derivationVM.WalletFile = TestUtils.GetFormFile("wallet.json", content); - derivationVM = (DerivationSchemeViewModel)Assert.IsType(controller - .AddDerivationScheme(user.StoreId, derivationVM, "BTC").GetAwaiter().GetResult()).Model; - Assert.False(derivationVM - .Confirmation); // Should fail, we are giving a mainnet file to a testnet network + content = "{\"keystore\": {\"ckcc_xpub\": \"xpub661MyMwAqRbcGVBsTGeNZN6QGVHmMHLdSA4FteGsRrEriu4pnVZMZWnruFFFXkMnyoBjyHndD3Qwcfz4MPzBUxjSevweNFQx7SAYZATtcDw\", \"xpub\": \"ypub6WWc2gWwHbdnAAyJDnR4SPL1phRh7REqrPBfZeizaQ1EmTshieRXJC3Z5YoU4wkcdKHEjQGkh6AYEzCQC1Kz3DNaWSwdc1pc8416hAjzqyD\", \"label\": \"Coldcard Import 0x60d1af8b\", \"ckcc_xfp\": 1624354699, \"type\": \"hardware\", \"hw_type\": \"coldcard\", \"derivation\": \"m/49'/0'/0'\"}, \"wallet_type\": \"standard\", \"use_encryption\": false, \"seed_version\": 17}"; + response = await controller.UpdateWallet(new WalletSetupViewModel {StoreId = storeId, CryptoCode = cryptoCode, WalletFile = TestUtils.GetFormFile("coldcard-ypub.json", content)}); + setupVm = (WalletSetupViewModel)Assert.IsType(response).Model; + Assert.False(setupVm.Confirmation); // Should fail, we are giving a mainnet file to a testnet network // And with a good file? (upub) - content = - "{\"keystore\": {\"ckcc_xpub\": \"tpubD6NzVbkrYhZ4YHNiuTdTmHRmbcPRLfqgyneZFCL1mkzkUBjXriQShxTh9HL34FK2mhieasJVk9EzJrUfkFqRNQBjiXgx3n5BhPkxKBoFmaS\", \"xpub\": \"upub5DBYp1qGgsTrkzCptMGZc2x18pquLwGrBw6nS59T4NViZ4cni1mGowQzziy85K8vzkp1jVtWrSkLhqk9KDfvrGeB369wGNYf39kX8rQfiLn\", \"label\": \"Coldcard Import 0x60d1af8b\", \"ckcc_xfp\": 1624354699, \"type\": \"hardware\", \"hw_type\": \"coldcard\", \"derivation\": \"m/49'/0'/0'\"}, \"wallet_type\": \"standard\", \"use_encryption\": false, \"seed_version\": 17}"; - derivationVM = (DerivationSchemeViewModel)Assert - .IsType(await controller.AddDerivationScheme(user.StoreId, "BTC")).Model; - derivationVM.WalletFile = TestUtils.GetFormFile("wallet2.json", content); - derivationVM.Enabled = true; - derivationVM = (DerivationSchemeViewModel)Assert.IsType(controller - .AddDerivationScheme(user.StoreId, derivationVM, "BTC").GetAwaiter().GetResult()).Model; - Assert.True(derivationVM.Confirmation); - Assert.IsType(controller.AddDerivationScheme(user.StoreId, derivationVM, "BTC") - .GetAwaiter().GetResult()); - + content = "{\"keystore\": {\"ckcc_xpub\": \"tpubD6NzVbkrYhZ4YHNiuTdTmHRmbcPRLfqgyneZFCL1mkzkUBjXriQShxTh9HL34FK2mhieasJVk9EzJrUfkFqRNQBjiXgx3n5BhPkxKBoFmaS\", \"xpub\": \"upub5DBYp1qGgsTrkzCptMGZc2x18pquLwGrBw6nS59T4NViZ4cni1mGowQzziy85K8vzkp1jVtWrSkLhqk9KDfvrGeB369wGNYf39kX8rQfiLn\", \"label\": \"Coldcard Import 0x60d1af8b\", \"ckcc_xfp\": 1624354699, \"type\": \"hardware\", \"hw_type\": \"coldcard\", \"derivation\": \"m/49'/0'/0'\"}, \"wallet_type\": \"standard\", \"use_encryption\": false, \"seed_version\": 17}"; + response = await controller.UpdateWallet(new WalletSetupViewModel {StoreId = storeId, CryptoCode = cryptoCode, WalletFile = TestUtils.GetFormFile("coldcard-upub.json", content)}); + setupVm = (WalletSetupViewModel)Assert.IsType(response).Model; + Assert.True(setupVm.Confirmation); + response = await controller.UpdateWallet(setupVm); + Assert.IsType(response); + response = await controller.ModifyWallet(new WalletSetupViewModel { StoreId = storeId, CryptoCode = cryptoCode }); + setupVm = (WalletSetupViewModel)Assert.IsType(response).Model; + Assert.Equal("ElectrumFile", setupVm.Source); // Now let's check that no data has been lost in the process - var store = tester.PayTester.StoreRepository.FindStore(user.StoreId).GetAwaiter().GetResult(); + var store = tester.PayTester.StoreRepository.FindStore(storeId).GetAwaiter().GetResult(); var onchainBTC = store.GetSupportedPaymentMethods(tester.PayTester.Networks) #pragma warning disable CS0618 // Type or member is obsolete .OfType().First(o => o.PaymentId.IsBTCOnChain); @@ -295,7 +246,6 @@ namespace BTCPayServer.Tests var invoice = await user.BitPay.CreateInvoiceAsync(new Invoice(100, "BTC")); Assert.Equal(2, invoice.SupportedTransactionCurrencies.Count); - invoice = await user.BitPay.CreateInvoiceAsync(new Invoice(100, "BTC") { SupportedTransactionCurrencies = new Dictionary() diff --git a/BTCPayServer.Tests/SeleniumTester.cs b/BTCPayServer.Tests/SeleniumTester.cs index 05e934820..41c7b4349 100644 --- a/BTCPayServer.Tests/SeleniumTester.cs +++ b/BTCPayServer.Tests/SeleniumTester.cs @@ -125,10 +125,12 @@ namespace BTCPayServer.Tests public Mnemonic GenerateWallet(string cryptoCode = "BTC", string seed = "", bool importkeys = false, bool privkeys = false, ScriptPubKeyType format = ScriptPubKeyType.Segwit) { Driver.FindElement(By.Id($"Modify{cryptoCode}")).Click(); - // Modify case + + // Replace previous wallet case if (Driver.PageSource.Contains("id=\"ChangeWalletLink\"")) { Driver.FindElement(By.Id("ChangeWalletLink")).Click(); + Driver.FindElement(By.Id("continue")).Click(); } if (string.IsNullOrEmpty(seed)) diff --git a/BTCPayServer.Tests/TestAccount.cs b/BTCPayServer.Tests/TestAccount.cs index 93e18b469..85e57c2ee 100644 --- a/BTCPayServer.Tests/TestAccount.cs +++ b/BTCPayServer.Tests/TestAccount.cs @@ -184,9 +184,11 @@ namespace BTCPayServer.Tests ScriptPubKeyType = segwit, SavePrivateKeys = importKeysToNBX, }); - await store.AddDerivationScheme(StoreId, - new DerivationSchemeViewModel() + await store.UpdateWallet( + new WalletSetupViewModel { + StoreId = StoreId, + Method = importKeysToNBX ? WalletSetupMethod.HotWallet : WalletSetupMethod.WatchOnly, Enabled = true, CryptoCode = cryptoCode, Network = SupportedNetwork, @@ -198,7 +200,7 @@ namespace BTCPayServer.Tests KeyPath = GenerateWalletResponseV.AccountKeyPath.KeyPath.ToString(), DerivationScheme = DerivationScheme.ToString(), Confirmation = true - }, cryptoCode); + }); return new WalletId(StoreId, cryptoCode); } diff --git a/BTCPayServer/Controllers/StoresController.BTCLike.cs b/BTCPayServer/Controllers/StoresController.BTCLike.cs deleted file mode 100644 index 172c4e51e..000000000 --- a/BTCPayServer/Controllers/StoresController.BTCLike.cs +++ /dev/null @@ -1,400 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using BTCPayServer.Abstractions.Extensions; -using BTCPayServer.Abstractions.Models; -using BTCPayServer.Client; -using BTCPayServer.Data; -using BTCPayServer.Events; -using BTCPayServer.Models.StoreViewModels; -using BTCPayServer.Payments; -using BTCPayServer.Services; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using NBitcoin; -using NBXplorer.DerivationStrategy; -using NBXplorer.Models; - -namespace BTCPayServer.Controllers -{ - public partial class StoresController - { - [HttpGet] - [Route("{storeId}/derivations/{cryptoCode}")] - public async Task AddDerivationScheme(string storeId, string cryptoCode) - { - var store = HttpContext.GetStoreData(); - if (store == null) - return NotFound(); - var network = cryptoCode == null ? null : _ExplorerProvider.GetNetwork(cryptoCode); - if (network == null) - { - return NotFound(); - } - - DerivationSchemeViewModel vm = new DerivationSchemeViewModel(); - vm.CryptoCode = cryptoCode; - vm.RootKeyPath = network.GetRootKeyPath(); - vm.Network = network; - 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)); - var hotWallet = await CanUseHotWallet(); - vm.CanUseHotWallet = hotWallet.HotWallet; - vm.CanUseRPCImport = hotWallet.RPCImport; - return View(vm); - } - - private DerivationSchemeSettings GetExistingDerivationStrategy(string cryptoCode, StoreData store) - { - var id = new PaymentMethodId(cryptoCode, PaymentTypes.BTCLike); - var existing = store.GetSupportedPaymentMethods(_NetworkProvider) - .OfType() - .FirstOrDefault(d => d.PaymentId == id); - return existing; - } - - [HttpPost] - [Route("{storeId}/derivations/{cryptoCode}")] - [ApiExplorerSettings(IgnoreApi = true)] - public async Task AddDerivationScheme(string storeId, [FromForm] DerivationSchemeViewModel vm, - string cryptoCode) - { - vm.CryptoCode = cryptoCode; - var store = HttpContext.GetStoreData(); - if (store == null) - return NotFound(); - - var network = cryptoCode == null ? null : _ExplorerProvider.GetNetwork(cryptoCode); - if (network == null) - { - return NotFound(); - } - - vm.Network = network; - vm.RootKeyPath = network.GetRootKeyPath(); - DerivationSchemeSettings strategy = null; - - var wallet = _WalletProvider.GetWallet(network); - if (wallet == null) - { - return NotFound(); - } - - if (!string.IsNullOrEmpty(vm.Config)) - { - if (!DerivationSchemeSettings.TryParseFromJson(vm.Config, network, out strategy)) - { - TempData.SetStatusMessageModel(new StatusMessageModel() - { - Severity = StatusMessageModel.StatusSeverity.Error, - Message = "Config file was not in the correct format" - }); - vm.Confirmation = false; - return View(nameof(AddDerivationScheme), vm); - } - } - - if (vm.WalletFile != null) - { - if (!DerivationSchemeSettings.TryParseFromWalletFile(await ReadAllText(vm.WalletFile), network, - out strategy)) - { - TempData.SetStatusMessageModel(new StatusMessageModel() - { - Severity = StatusMessageModel.StatusSeverity.Error, - Message = "Wallet file was not in the correct format" - }); - vm.Confirmation = false; - return View(nameof(AddDerivationScheme), vm); - } - } - else if (!string.IsNullOrEmpty(vm.WalletFileContent)) - { - if (!DerivationSchemeSettings.TryParseFromWalletFile(vm.WalletFileContent, network, out strategy)) - { - TempData.SetStatusMessageModel(new StatusMessageModel() - { - Severity = StatusMessageModel.StatusSeverity.Error, - Message = "QR import was not in the correct format" - }); - vm.Confirmation = false; - return View(nameof(AddDerivationScheme), vm); - } - } - else - { - try - { - if (!string.IsNullOrEmpty(vm.DerivationScheme)) - { - var newStrategy = ParseDerivationStrategy(vm.DerivationScheme, null, network); - if (newStrategy.AccountDerivation != strategy?.AccountDerivation) - { - var accountKey = string.IsNullOrEmpty(vm.AccountKey) - ? null - : new BitcoinExtPubKey(vm.AccountKey, network.NBitcoinNetwork); - if (accountKey != null) - { - var accountSettings = - newStrategy.AccountKeySettings.FirstOrDefault(a => a.AccountKey == accountKey); - if (accountSettings != null) - { - accountSettings.AccountKeyPath = - vm.KeyPath == null ? null : KeyPath.Parse(vm.KeyPath); - accountSettings.RootFingerprint = string.IsNullOrEmpty(vm.RootFingerprint) - ? (HDFingerprint?)null - : new HDFingerprint( - NBitcoin.DataEncoders.Encoders.Hex.DecodeData(vm.RootFingerprint)); - } - } - - strategy = newStrategy; - strategy.Source = vm.Source; - vm.DerivationScheme = strategy.AccountDerivation.ToString(); - } - } - else - { - strategy = null; - } - } - catch - { - ModelState.AddModelError(nameof(vm.DerivationScheme), "Invalid Derivation Scheme"); - vm.Confirmation = false; - return View(nameof(AddDerivationScheme), vm); - } - } - - var oldConfig = vm.Config; - vm.Config = strategy == null ? null : strategy.ToJson(); - - PaymentMethodId paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike); - var exisingStrategy = store.GetSupportedPaymentMethods(_NetworkProvider) - .Where(c => c.PaymentId == paymentMethodId) - .OfType() - .FirstOrDefault(); - var storeBlob = store.GetStoreBlob(); - var wasExcluded = storeBlob.GetExcludedPaymentMethods().Match(paymentMethodId); - var willBeExcluded = !vm.Enabled; - - 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 && oldConfig != vm.Config) || - // - The user is clicking on continue without changing config nor enabling/disabling - (!vm.Confirmation && oldConfig == vm.Config && willBeExcluded == wasExcluded); - - showAddress = showAddress && strategy != null; - if (!showAddress) - { - try - { - if (strategy != null) - await wallet.TrackAsync(strategy.AccountDerivation); - store.SetSupportedPaymentMethod(paymentMethodId, strategy); - storeBlob.SetExcluded(paymentMethodId, willBeExcluded); - storeBlob.Hints.Wallet = false; - store.SetStoreBlob(storeBlob); - } - catch - { - ModelState.AddModelError(nameof(vm.DerivationScheme), "Invalid Derivation Scheme"); - return View(vm); - } - - await _Repo.UpdateStore(store); - _EventAggregator.Publish(new WalletChangedEvent() {WalletId = new WalletId(storeId, cryptoCode)}); - - if (willBeExcluded != wasExcluded) - { - var label = willBeExcluded ? "disabled" : "enabled"; - TempData[WellKnownTempData.SuccessMessage] = - $"On-Chain payments for {network.CryptoCode} has been {label}."; - } - else - { - TempData[WellKnownTempData.SuccessMessage] = - $"Derivation settings for {network.CryptoCode} has been modified."; - } - - // This is success case when derivation scheme is added to the store - return RedirectToAction(nameof(UpdateStore), new {storeId = storeId}); - } - else if (!string.IsNullOrEmpty(vm.HintAddress)) - { - BitcoinAddress address = null; - try - { - address = BitcoinAddress.Create(vm.HintAddress, network.NBitcoinNetwork); - } - catch - { - ModelState.AddModelError(nameof(vm.HintAddress), "Invalid hint address"); - return ShowAddresses(vm, strategy); - } - - try - { - var newStrategy = ParseDerivationStrategy(vm.DerivationScheme, address.ScriptPubKey, network); - if (newStrategy.AccountDerivation != strategy.AccountDerivation) - { - strategy.AccountDerivation = newStrategy.AccountDerivation; - strategy.AccountOriginal = null; - } - } - catch - { - ModelState.AddModelError(nameof(vm.HintAddress), "Impossible to find a match with this address"); - return ShowAddresses(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 ShowAddresses(vm, strategy); - } - - [HttpPost] - [Route("{storeId}/derivations/{cryptoCode}/generatenbxwallet")] - public async Task GenerateNBXWallet(string storeId, string cryptoCode, - GenerateWalletRequest request) - { - var hotWallet = await CanUseHotWallet(); - if (!hotWallet.HotWallet || (!hotWallet.RPCImport && request.ImportKeysToRPC)) - { - return NotFound(); - } - - var network = _NetworkProvider.GetNetwork(cryptoCode); - var client = _ExplorerProvider.GetExplorerClient(cryptoCode); - GenerateWalletResponse response; - try - { - response = await client.GenerateWalletAsync(request); - } - catch (Exception e) - { - TempData.SetStatusMessageModel(new StatusMessageModel() - { - Severity = StatusMessageModel.StatusSeverity.Error, - Html = $"There was an error generating your wallet: {e.Message}" - }); - return RedirectToAction(nameof(AddDerivationScheme), new {storeId, cryptoCode}); - } - - if (response == null) - { - TempData.SetStatusMessageModel(new StatusMessageModel() - { - Severity = StatusMessageModel.StatusSeverity.Error, - Html = "There was an error generating your wallet. Is your node available?" - }); - return RedirectToAction(nameof(AddDerivationScheme), new {storeId, cryptoCode}); - } - - var store = HttpContext.GetStoreData(); - var result = await AddDerivationScheme(storeId, - new DerivationSchemeViewModel() - { - Confirmation = string.IsNullOrEmpty(request.ExistingMnemonic), - Network = network, - RootFingerprint = response.AccountKeyPath.MasterFingerprint.ToString(), - RootKeyPath = network.GetRootKeyPath(), - CryptoCode = cryptoCode, - DerivationScheme = response.DerivationScheme.ToString(), - Source = "NBXplorer", - AccountKey = response.AccountHDKey.Neuter().ToWif(), - DerivationSchemeFormat = "BTCPay", - KeyPath = response.AccountKeyPath.KeyPath.ToString(), - Enabled = !store.GetStoreBlob() - .IsExcluded(new PaymentMethodId(cryptoCode, PaymentTypes.BTCLike)) - }, cryptoCode); - if (!ModelState.IsValid || !(result is RedirectToActionResult)) - return result; - TempData.Clear(); - if (string.IsNullOrEmpty(request.ExistingMnemonic)) - { - TempData.SetStatusMessageModel(new StatusMessageModel() - { - Severity = StatusMessageModel.StatusSeverity.Success, - Html = $"Your wallet has been generated." - }); - var vm = new RecoverySeedBackupViewModel() - { - CryptoCode = cryptoCode, - Mnemonic = response.Mnemonic, - Passphrase = response.Passphrase, - IsStored = request.SavePrivateKeys, - ReturnUrl = Url.Action(nameof(UpdateStore), new {storeId}) - }; - return this.RedirectToRecoverySeedBackup(vm); - } - else - { - TempData.SetStatusMessageModel(new StatusMessageModel() - { - Severity = StatusMessageModel.StatusSeverity.Warning, - Html = "Please check your addresses and confirm" - }); - } - return result; - } - - private async Task<(bool HotWallet, bool RPCImport)> CanUseHotWallet() - { - var isAdmin = (await _authorizationService.AuthorizeAsync(User, Policies.CanModifyServerSettings)) - .Succeeded; - if (isAdmin) - return (true, true); - var policies = await _settingsRepository.GetSettingAsync(); - var hotWallet = policies?.AllowHotWalletForAll is true; - return (hotWallet, hotWallet && policies?.AllowHotWalletRPCImportForAll is true); - } - - private async Task ReadAllText(IFormFile file) - { - using (var stream = new StreamReader(file.OpenReadStream())) - { - return await stream.ReadToEndAsync(); - } - } - - private IActionResult - ShowAddresses(DerivationSchemeViewModel vm, DerivationSchemeSettings strategy) - { - vm.DerivationScheme = strategy.AccountDerivation.ToString(); - var deposit = new NBXplorer.KeyPathTemplates(null).GetKeyPathTemplate(DerivationFeature.Deposit); - if (!string.IsNullOrEmpty(vm.DerivationScheme)) - { - var line = strategy.AccountDerivation.GetLineFor(deposit); - - for (int i = 0; i < 10; i++) - { - var keyPath = deposit.GetKeyPath((uint)i); - var rootedKeyPath = vm.GetAccountKeypath()?.Derive(keyPath); - var derivation = line.Derive((uint)i); - var address = strategy.Network.NBXplorerNetwork.CreateAddress(strategy.AccountDerivation, - line.KeyPathTemplate.GetKeyPath((uint)i), - derivation.ScriptPubKey).ToString(); - vm.AddressSamples.Add((keyPath.ToString(), address, rootedKeyPath)); - } - } - vm.Confirmation = true; - ModelState.Remove(nameof(vm.Config)); // Remove the cached value - return View(nameof(AddDerivationScheme), vm); - } - } -} diff --git a/BTCPayServer/Controllers/StoresController.Onchain.cs b/BTCPayServer/Controllers/StoresController.Onchain.cs index 52a9ad42d..f2808dfd9 100644 --- a/BTCPayServer/Controllers/StoresController.Onchain.cs +++ b/BTCPayServer/Controllers/StoresController.Onchain.cs @@ -1,16 +1,21 @@ using System; +using System.IO; using System.Linq; using System.Threading.Tasks; using BTCPayServer.Abstractions.Extensions; using BTCPayServer.Abstractions.Models; +using BTCPayServer.Client; using BTCPayServer.Data; using BTCPayServer.Events; using BTCPayServer.Models; using BTCPayServer.Models.StoreViewModels; using BTCPayServer.Payments; -using BTCPayServer.Services.Wallets; +using BTCPayServer.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using NBitcoin; +using NBXplorer; using NBXplorer.DerivationStrategy; using NBXplorer.Models; @@ -105,42 +110,34 @@ namespace BTCPayServer.Controllers return View(vm.ViewName, vm); } } - else + else if (!string.IsNullOrEmpty(vm.DerivationScheme)) { try { - if (!string.IsNullOrEmpty(vm.DerivationScheme)) + var newStrategy = ParseDerivationStrategy(vm.DerivationScheme, null, network); + if (newStrategy.AccountDerivation != strategy?.AccountDerivation) { - var newStrategy = ParseDerivationStrategy(vm.DerivationScheme, null, network); - if (newStrategy.AccountDerivation != strategy?.AccountDerivation) + var accountKey = string.IsNullOrEmpty(vm.AccountKey) + ? null + : new BitcoinExtPubKey(vm.AccountKey, network.NBitcoinNetwork); + if (accountKey != null) { - var accountKey = string.IsNullOrEmpty(vm.AccountKey) - ? null - : new BitcoinExtPubKey(vm.AccountKey, network.NBitcoinNetwork); - if (accountKey != null) + var accountSettings = + newStrategy.AccountKeySettings.FirstOrDefault(a => a.AccountKey == accountKey); + if (accountSettings != null) { - var accountSettings = - newStrategy.AccountKeySettings.FirstOrDefault(a => a.AccountKey == accountKey); - if (accountSettings != null) - { - accountSettings.AccountKeyPath = - vm.KeyPath == null ? null : KeyPath.Parse(vm.KeyPath); - accountSettings.RootFingerprint = string.IsNullOrEmpty(vm.RootFingerprint) - ? (HDFingerprint?)null - : new HDFingerprint( - NBitcoin.DataEncoders.Encoders.Hex.DecodeData(vm.RootFingerprint)); - } + accountSettings.AccountKeyPath = + vm.KeyPath == null ? null : KeyPath.Parse(vm.KeyPath); + accountSettings.RootFingerprint = string.IsNullOrEmpty(vm.RootFingerprint) + ? (HDFingerprint?)null + : new HDFingerprint( + NBitcoin.DataEncoders.Encoders.Hex.DecodeData(vm.RootFingerprint)); } - - strategy = newStrategy; - strategy.Source = vm.Source; - vm.DerivationScheme = strategy.AccountDerivation.ToString(); } - } - else - { - ModelState.AddModelError(nameof(vm.DerivationScheme), "Please provide your extended public key"); - return View(vm.ViewName, vm); + + strategy = newStrategy; + strategy.Source = vm.Source; + vm.DerivationScheme = strategy.AccountDerivation.ToString(); } } catch @@ -149,6 +146,11 @@ namespace BTCPayServer.Controllers return View(vm.ViewName, vm); } } + else + { + ModelState.AddModelError(nameof(vm.DerivationScheme), "Please provide your extended public key"); + return View(vm.ViewName, vm); + } var oldConfig = vm.Config; vm.Config = strategy?.ToJson(); @@ -409,6 +411,8 @@ namespace BTCPayServer.Controllers } var (hotWallet, rpcImport) = await CanUseHotWallet(); + var isHotWallet = await IsHotWallet(vm.CryptoCode, derivation); + vm.CanUseHotWallet = hotWallet; vm.CanUseRPCImport = rpcImport; vm.RootKeyPath = network.GetRootKeyPath(); @@ -419,12 +423,13 @@ namespace BTCPayServer.Controllers vm.KeyPath = derivation.GetSigningAccountKeySettings().AccountKeyPath?.ToString(); vm.Config = derivation.ToJson(); vm.Enabled = !store.GetStoreBlob().IsExcluded(new PaymentMethodId(vm.CryptoCode, PaymentTypes.BTCLike)); + vm.IsHotWallet = isHotWallet; return View(vm); } - [HttpGet("{storeId}/onchain/{cryptoCode}/delete")] - public IActionResult DeleteWallet(string storeId, string cryptoCode) + [HttpGet("{storeId}/onchain/{cryptoCode}/replace")] + public async Task ReplaceWallet(string storeId, string cryptoCode) { var checkResult = IsAvailable(cryptoCode, out var store, out var network); if (checkResult != null) @@ -433,9 +438,62 @@ namespace BTCPayServer.Controllers } var derivation = GetExistingDerivationStrategy(cryptoCode, store); + var isHotWallet = await IsHotWallet(cryptoCode, derivation); + var walletType = isHotWallet ? "hot" : "watch-only"; + var additionalText = isHotWallet + ? "" + : " or imported into an external wallet. If you no longer have access to your private key (recovery seed), immediately replace the wallet"; var description = - (derivation.IsHotWallet ? "

Please note that this is a hot wallet!

" : "") + - "

Do not remove the wallet if you have not backed it up!

" + + $"

Please note that this is a {walletType} wallet!

" + + $"

Do not replace the wallet if you have not backed it up{additionalText}.

" + + "

Replacing the wallet will erase the current wallet data from the server. " + + "The current wallet will be replaced once you finish the setup of the new wallet. If you cancel the setup, the current wallet will stay active .

"; + + return View("Confirm", new ConfirmModel + { + Title = $"Replace {network.CryptoCode} wallet", + Description = description, + DescriptionHtml = true, + Action = "Setup new wallet" + }); + } + + [HttpPost("{storeId}/onchain/{cryptoCode}/replace")] + public IActionResult ConfirmReplaceWallet(string storeId, string cryptoCode) + { + var checkResult = IsAvailable(cryptoCode, out var store, out _); + if (checkResult != null) + { + return checkResult; + } + + var derivation = GetExistingDerivationStrategy(cryptoCode, store); + if (derivation == null) + { + return NotFound(); + } + + return RedirectToAction(nameof(SetupWallet), new {storeId, cryptoCode}); + } + + [HttpGet("{storeId}/onchain/{cryptoCode}/delete")] + public async Task DeleteWallet(string storeId, string cryptoCode) + { + var checkResult = IsAvailable(cryptoCode, out var store, out var network); + if (checkResult != null) + { + return checkResult; + } + + var derivation = GetExistingDerivationStrategy(cryptoCode, store); + var isHotWallet = await IsHotWallet(cryptoCode, derivation); + var walletType = isHotWallet ? "hot" : "watch-only"; + var additionalText = isHotWallet + ? "" + : " or imported into an external wallet. If you no longer have access to your private key (recovery seed), immediately replace the wallet"; + var description = + $"

Please note that this is a {walletType} wallet!

" + + $"

Do not remove the wallet if you have not backed it up{additionalText}.

" + "

Removing the wallet will erase the wallet data from the server. " + $"The store won't be able to receive {network.CryptoCode} onchain payments until a new wallet is set up.

"; @@ -478,7 +536,7 @@ namespace BTCPayServer.Controllers private IActionResult ConfirmAddresses(WalletSetupViewModel vm, DerivationSchemeSettings strategy) { vm.DerivationScheme = strategy.AccountDerivation.ToString(); - var deposit = new NBXplorer.KeyPathTemplates(null).GetKeyPathTemplate(DerivationFeature.Deposit); + var deposit = new KeyPathTemplates(null).GetKeyPathTemplate(DerivationFeature.Deposit); if (!string.IsNullOrEmpty(vm.DerivationScheme)) { @@ -509,5 +567,39 @@ namespace BTCPayServer.Controllers return store == null || network == null ? NotFound() : null; } + + private DerivationSchemeSettings GetExistingDerivationStrategy(string cryptoCode, StoreData store) + { + var id = new PaymentMethodId(cryptoCode, PaymentTypes.BTCLike); + var existing = store.GetSupportedPaymentMethods(_NetworkProvider) + .OfType() + .FirstOrDefault(d => d.PaymentId == id); + return existing; + } + + private async Task<(bool HotWallet, bool RPCImport)> CanUseHotWallet() + { + var isAdmin = (await _authorizationService.AuthorizeAsync(User, Policies.CanModifyServerSettings)) + .Succeeded; + if (isAdmin) + return (true, true); + var policies = await _settingsRepository.GetSettingAsync(); + var hotWallet = policies?.AllowHotWalletForAll is true; + return (hotWallet, hotWallet && policies.AllowHotWalletRPCImportForAll is true); + } + + private async Task ReadAllText(IFormFile file) + { + using (var stream = new StreamReader(file.OpenReadStream())) + { + return await stream.ReadToEndAsync(); + } + } + + private async Task IsHotWallet(string cryptoCode, DerivationSchemeSettings derivation) + { + return derivation.IsHotWallet && await _ExplorerProvider.GetExplorerClient(cryptoCode) + .GetMetadataAsync(derivation.AccountDerivation, WellknownMetadataKeys.MasterHDKey) != null; + } } } diff --git a/BTCPayServer/Models/StoreViewModels/WalletSetupViewModel.cs b/BTCPayServer/Models/StoreViewModels/WalletSetupViewModel.cs index 82f400771..9803bac02 100644 --- a/BTCPayServer/Models/StoreViewModels/WalletSetupViewModel.cs +++ b/BTCPayServer/Models/StoreViewModels/WalletSetupViewModel.cs @@ -20,6 +20,7 @@ namespace BTCPayServer.Models.StoreViewModels public WalletSetupMethod? Method { get; set; } public GenerateWalletRequest SetupRequest { get; set; } public string StoreId { get; set; } + public bool IsHotWallet { get; set; } public string ViewName => Method switch diff --git a/BTCPayServer/Views/Shared/Confirm.cshtml b/BTCPayServer/Views/Shared/Confirm.cshtml index 23aed1ae7..a87dc385e 100644 --- a/BTCPayServer/Views/Shared/Confirm.cshtml +++ b/BTCPayServer/Views/Shared/Confirm.cshtml @@ -31,8 +31,8 @@ @if (!String.IsNullOrEmpty(Model.Action)) { } diff --git a/BTCPayServer/Views/Stores/AddDerivationScheme.cshtml b/BTCPayServer/Views/Stores/AddDerivationScheme.cshtml deleted file mode 100644 index 06ec4a313..000000000 --- a/BTCPayServer/Views/Stores/AddDerivationScheme.cshtml +++ /dev/null @@ -1,234 +0,0 @@ -@model DerivationSchemeViewModel -@addTagHelper *, BundlerMinifier.TagHelpers -@{ - Layout = "../Shared/_NavLayout.cshtml"; - ViewData.SetActivePageAndTitle(StoreNavPages.Index, $"{Model.CryptoCode} Derivation scheme"); -} - -@section HeadScripts { - -} - - - -@if (!ViewContext.ModelState.IsValid) -{ -
-
-
-
-
-} - - - - - -
-
- - @if (!Model.Confirmation) - { - - } - else - { - - } -
- - - - @if (!Model.Confirmation) - { - - - -
-
- Derivation scheme - -
-

- A derivation scheme facilitates generation of the destination addresses for your invoices so funds can be received on-chain. -

-
-
- - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Address typeExample
P2WPKHxpub...
P2SH-P2WPKHxpub...-[p2sh]
P2PKHxpub...-[legacy]
Multi-sig P2WSH2-of-xpub1...-xpub2...
Multi-sig P2SH-P2WSH2-of-xpub1...-xpub2...-[p2sh]
Multi-sig P2SH2-of-xpub1...-xpub2...-[legacy]
- -
-
Additional pairing information
-
-
- - -
-
- - -
-
- - -
-
-
- - -
-
- - } - else - { -
-
Confirm the addresses (@Model.CryptoCode)
- Please check that your @Model.CryptoCode wallet is generating the same addresses as below. -
- - - -
- - - - - - @if (Model.Source == "Vault") - { - - } - - - - @foreach (var sample in Model.AddressSamples) - { - - - - @if (Model.Source == "Vault") - { - - } - - } - -
Key pathAddressActions
@sample.KeyPath@sample.AddressShow on device
-
- -
-
Wrong addresses?
- Help us to find the correct settings by telling us the first address of your wallet -
-
- - - -
- - } -
-
-
-@section Scripts { - @await Html.PartialAsync("_ValidationScriptsPartial") - - - - - - -} diff --git a/BTCPayServer/Views/Stores/AddDerivationSchemes_HardwareWalletDialogs.cshtml b/BTCPayServer/Views/Stores/AddDerivationSchemes_HardwareWalletDialogs.cshtml deleted file mode 100644 index b64259611..000000000 --- a/BTCPayServer/Views/Stores/AddDerivationSchemes_HardwareWalletDialogs.cshtml +++ /dev/null @@ -1,111 +0,0 @@ -@using NBXplorer.Models -@model DerivationSchemeViewModel -@if (Model.CanUseHotWallet) -{ - ViewData.Add(nameof(Model.CanUseRPCImport), Model.CanUseRPCImport); - - -} - -
- -
- - - diff --git a/BTCPayServer/Views/Stores/AddDerivationSchemes_NBXWalletGenerate.cshtml b/BTCPayServer/Views/Stores/AddDerivationSchemes_NBXWalletGenerate.cshtml deleted file mode 100644 index 43790865b..000000000 --- a/BTCPayServer/Views/Stores/AddDerivationSchemes_NBXWalletGenerate.cshtml +++ /dev/null @@ -1,101 +0,0 @@ -@using NBitcoin -@model NBXplorer.Models.GenerateWalletRequest - - diff --git a/BTCPayServer/Views/Stores/GenerateWalletOptions.cshtml b/BTCPayServer/Views/Stores/GenerateWalletOptions.cshtml index 4c8d93f3d..699a713f3 100644 --- a/BTCPayServer/Views/Stores/GenerateWalletOptions.cshtml +++ b/BTCPayServer/Views/Stores/GenerateWalletOptions.cshtml @@ -18,13 +18,14 @@ {
- +

Hot wallet

- Allows spending directly from your BTCPay Server. - Each private key associated with an address generated will be stored as metadata and would be accessible to anyone with admin access to your server. Use at your own risk! + Wallet's private key is stored on the server. + Spending the funds you received is convenient. + To minimize the risk of theft, regularly withdraw funds to a different wallet.

@@ -34,7 +35,7 @@ {
- +

Hot wallet

@@ -47,11 +48,14 @@
- +

Watch-only wallet

-

Needs to be imported into an external wallet to spend or provide the seed while spending.

+

+ Wallet's private key is erased from the server. Higher security. + To spend, you have to manually input the private key or import it into an external wallet. +

diff --git a/BTCPayServer/Views/Stores/ImportWallet/File.cshtml b/BTCPayServer/Views/Stores/ImportWallet/File.cshtml index 0a68b7a4f..8dc7e2636 100644 --- a/BTCPayServer/Views/Stores/ImportWallet/File.cshtml +++ b/BTCPayServer/Views/Stores/ImportWallet/File.cshtml @@ -51,8 +51,8 @@ Tools ❯ Wallet Manager ❯ Open Wallets Folder - Specter - Wallet ❯ Settings ❯ Export ❯ Export To Wallet Software ❯ Save wallet file + Specter Desktop + Wallet ❯ Settings ❯ Export ❯ Export To Wallet Software ❯ Save wallet file diff --git a/BTCPayServer/Views/Stores/ImportWallet/Xpub.cshtml b/BTCPayServer/Views/Stores/ImportWallet/Xpub.cshtml index abd348c99..9ad4e098c 100644 --- a/BTCPayServer/Views/Stores/ImportWallet/Xpub.cshtml +++ b/BTCPayServer/Views/Stores/ImportWallet/Xpub.cshtml @@ -33,11 +33,14 @@ - - + +
- + @@ -78,30 +81,39 @@ - + - + - + - + - + - +
Address typeAddress type Example
pkh(xpub…/0/*)
Multi-sig P2WSH 2-of-xpub1…-xpub2…
wsh(multi(2,
[…/48'/0'/0'/2']xpub…/0/*,
[…/48'/0'/0'/2']xpub…/0/*))
Multi-sig P2SH-P2WSH 2-of-xpub1…-xpub2…-[p2sh]
sh(wsh(multi(2,
[…/48'/0'/0'/1']xpub…/0/*,
[…/48'/0'/0'/1']xpub…/0/*)))
Multi-sig P2SH 2-of-xpub1…-xpub2…-[legacy]
sh(multi(2,
[…/45'/0]xpub…/0/*,
[…/45'/0]xpub…/0/*))
+Show multi-sig examples + @section Scripts { @await Html.PartialAsync("_ValidationScriptsPartial") + + } diff --git a/BTCPayServer/Views/Stores/ImportWalletOptions.cshtml b/BTCPayServer/Views/Stores/ImportWalletOptions.cshtml index cd59069d0..85c8df49a 100644 --- a/BTCPayServer/Views/Stores/ImportWalletOptions.cshtml +++ b/BTCPayServer/Views/Stores/ImportWalletOptions.cshtml @@ -24,26 +24,26 @@
-
-

- Connect hardware wallet - Recommended -

-

Import your keys using our Vault application

+
+
+

Connect hardware wallet

+

Import your keys using our Vault application

+
+ Recommended