コード例 #1
0
        public async Task CanSetupWallet()
        {
            using (var tester = CreateServerTester())
            {
                tester.ActivateLTC();
                tester.ActivateLightning();
                await tester.StartAsync();

                var user       = tester.NewAccount();
                var cryptoCode = "BTC";
                await user.GrantAccessAsync(true);

                user.RegisterDerivationScheme(cryptoCode);
                user.RegisterDerivationScheme("LTC");
                user.RegisterLightningNode(cryptoCode, LightningConnectionType.CLightning);
                var btcNetwork = tester.PayTester.Networks.GetNetwork <BTCPayNetwork>(cryptoCode);
                var invoice    = await user.BitPay.CreateInvoiceAsync(
                    new Invoice
                {
                    Price             = 1.5m,
                    Currency          = "USD",
                    PosData           = "posData",
                    OrderId           = "orderId",
                    ItemDesc          = "Some description",
                    FullNotifications = true
                }, Facade.Merchant);

                Assert.Equal(3, invoice.CryptoInfo.Length);

                // Setup Lightning
                var controller  = user.GetController <UIStoresController>();
                var lightningVm = (LightningNodeViewModel)Assert.IsType <ViewResult>(await controller.SetupLightningNode(user.StoreId, cryptoCode)).Model;
                Assert.True(lightningVm.Enabled);
                var response = await controller.SetLightningNodeEnabled(user.StoreId, cryptoCode, false);

                Assert.IsType <RedirectToActionResult>(response);

                // Get enabled state from settings
                LightningSettingsViewModel lnSettingsModel;
                response        = controller.LightningSettings(user.StoreId, cryptoCode).GetAwaiter().GetResult();
                lnSettingsModel = (LightningSettingsViewModel)Assert.IsType <ViewResult>(response).Model;
                Assert.NotNull(lnSettingsModel?.ConnectionString);
                Assert.False(lnSettingsModel.Enabled);

                // Setup wallet
                WalletSetupViewModel setupVm;
                var storeId = user.StoreId;
                response = await controller.GenerateWallet(storeId, cryptoCode, WalletSetupMethod.GenerateOptions, new WalletSetupRequest());

                Assert.IsType <ViewResult>(response);

                // Get enabled state from settings
                response = controller.WalletSettings(user.StoreId, cryptoCode).GetAwaiter().GetResult();
                var onchainSettingsModel = (WalletSettingsViewModel)Assert.IsType <ViewResult>(response).Model;
                Assert.NotNull(onchainSettingsModel?.DerivationScheme);
                Assert.True(onchainSettingsModel.Enabled);

                // Disable wallet
                onchainSettingsModel.Enabled = false;
                response = controller.UpdateWalletSettings(onchainSettingsModel).GetAwaiter().GetResult();
                Assert.IsType <RedirectToActionResult>(response);
                response             = controller.WalletSettings(user.StoreId, cryptoCode).GetAwaiter().GetResult();
                onchainSettingsModel = (WalletSettingsViewModel)Assert.IsType <ViewResult>(response).Model;
                Assert.NotNull(onchainSettingsModel?.DerivationScheme);
                Assert.False(onchainSettingsModel.Enabled);

                var oldScheme = onchainSettingsModel.DerivationScheme;

                invoice = await user.BitPay.CreateInvoiceAsync(
                    new Invoice
                {
                    Price             = 1.5m,
                    Currency          = "USD",
                    PosData           = "posData",
                    OrderId           = "orderId",
                    ItemDesc          = "Some description",
                    FullNotifications = true
                }, Facade.Merchant);

                Assert.Single(invoice.CryptoInfo);
                Assert.Equal("LTC", invoice.CryptoInfo[0].CryptoCode);

                // Removing the derivation scheme, should redirect to store page
                response = controller.ConfirmDeleteWallet(user.StoreId, cryptoCode).GetAwaiter().GetResult();
                Assert.IsType <RedirectToActionResult>(response);

                // Setting it again should show the confirmation page
                response = await controller.UpdateWallet(new WalletSetupViewModel { StoreId = storeId, CryptoCode = cryptoCode, DerivationScheme = oldScheme });

                setupVm = (WalletSetupViewModel)Assert.IsType <ViewResult>(response).Model;
                Assert.True(setupVm.Confirmation);

                // The following part posts a wallet update, confirms it and checks the result

                // cobo vault file
                var content = "{\"ExtPubKey\":\"xpub6CEqRFZ7yZxCFXuEWZBAdnC8bdvu9SRHevaoU2SsW9ZmKhrCShmbpGZWwaR15hdLURf8hg47g4TpPGaqEU8hw5LEJCE35AUhne67XNyFGBk\",\"MasterFingerprint\":\"7a7563b5\",\"DerivationPath\":\"M\\/84'\\/0'\\/0'\",\"CoboVaultFirmwareVersion\":\"1.2.0(BTC-Only)\"}";
                response = await controller.UpdateWallet(new WalletSetupViewModel { StoreId = storeId, CryptoCode = cryptoCode, WalletFile = TestUtils.GetFormFile("cobovault.json", content) });

                setupVm = (WalletSetupViewModel)Assert.IsType <ViewResult>(response).Model;
                Assert.True(setupVm.Confirmation);
                response = await controller.UpdateWallet(setupVm);

                Assert.IsType <RedirectToActionResult>(response);
                response = await controller.WalletSettings(storeId, cryptoCode);

                var settingsVm = (WalletSettingsViewModel)Assert.IsType <ViewResult>(response).Model;
                Assert.Equal("CoboVault", settingsVm.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 <ViewResult>(response).Model;
                Assert.True(setupVm.Confirmation);
                response = await controller.UpdateWallet(setupVm);

                Assert.IsType <RedirectToActionResult>(response);
                response = await controller.WalletSettings(storeId, cryptoCode);

                settingsVm = (WalletSettingsViewModel)Assert.IsType <ViewResult>(response).Model;
                Assert.Equal("WasabiFile", settingsVm.Source);

                // Can we upload coldcard settings? (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 <ViewResult>(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}";
                response = await controller.UpdateWallet(new WalletSetupViewModel { StoreId = storeId, CryptoCode = cryptoCode, WalletFile = TestUtils.GetFormFile("coldcard-upub.json", content) });

                setupVm = (WalletSetupViewModel)Assert.IsType <ViewResult>(response).Model;
                Assert.True(setupVm.Confirmation);
                response = await controller.UpdateWallet(setupVm);

                Assert.IsType <RedirectToActionResult>(response);
                response = await controller.WalletSettings(storeId, cryptoCode);

                settingsVm = (WalletSettingsViewModel)Assert.IsType <ViewResult>(response).Model;
                Assert.Equal("ElectrumFile", settingsVm.Source);

                // Now let's check that no data has been lost in the process
                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 <DerivationSchemeSettings>().First(o => o.PaymentId.IsBTCOnChain);
#pragma warning restore CS0618 // Type or member is obsolete
                DerivationSchemeSettings.TryParseFromWalletFile(content, onchainBTC.Network, out var expected);
                Assert.Equal(expected.ToJson(), onchainBTC.ToJson());

                // Let's check that the root hdkey and account key path are taken into account when making a PSBT
                invoice = await user.BitPay.CreateInvoiceAsync(
                    new Invoice
                {
                    Price             = 1.5m,
                    Currency          = "USD",
                    PosData           = "posData",
                    OrderId           = "orderId",
                    ItemDesc          = "Some description",
                    FullNotifications = true
                }, Facade.Merchant);

                tester.ExplorerNode.Generate(1);
                var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo.First(c => c.CryptoCode == cryptoCode).Address,
                                                           tester.ExplorerNode.Network);
                tester.ExplorerNode.SendToAddress(invoiceAddress, Money.Coins(1m));
                TestUtils.Eventually(() =>
                {
                    invoice = user.BitPay.GetInvoice(invoice.Id);
                    Assert.Equal("paid", invoice.Status);
                });
                var wallet = tester.PayTester.GetController <UIWalletsController>();
                var psbt   = wallet.CreatePSBT(btcNetwork, onchainBTC,
                                               new WalletSendModel()
                {
                    Outputs = new List <WalletSendModel.TransactionOutput>
                    {
                        new WalletSendModel.TransactionOutput
                        {
                            Amount             = 0.5m,
                            DestinationAddress = new Key().PubKey.GetAddress(ScriptPubKeyType.Legacy, btcNetwork.NBitcoinNetwork)
                                                 .ToString(),
                        }
                    },
                    FeeSatoshiPerByte = 1
                }, default).GetAwaiter().GetResult();

                Assert.NotNull(psbt);

                var root = new Mnemonic(
                    "usage fever hen zero slide mammal silent heavy donate budget pulse say brain thank sausage brand craft about save attract muffin advance illegal cabbage")
                           .DeriveExtKey().AsHDKeyCache();
                var account = root.Derive(new KeyPath("m/49'/0'/0'"));
                Assert.All(psbt.PSBT.Inputs, input =>
                {
                    var keyPath = input.HDKeyPaths.Single();
                    Assert.False(keyPath.Value.KeyPath.IsHardened);
                    Assert.Equal(account.Derive(keyPath.Value.KeyPath).GetPublicKey(), keyPath.Key);
                    Assert.Equal(keyPath.Value.MasterFingerprint,
                                 onchainBTC.AccountKeySettings[0].AccountKey.GetPublicKey().GetHDFingerPrint());
                });
            }
        }
コード例 #2
0
        public async Task <IActionResult> UpdateWallet(WalletSetupViewModel vm)
        {
            var checkResult = IsAvailable(vm.CryptoCode, out var store, out var network);

            if (checkResult != null)
            {
                return(checkResult);
            }

            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))
                {
                    ModelState.AddModelError(nameof(vm.Config), "Config file was not in the correct format");
                    return(View(vm.ViewName, vm));
                }
            }

            if (vm.WalletFile != null)
            {
                if (!DerivationSchemeSettings.TryParseFromWalletFile(await ReadAllText(vm.WalletFile), network, out strategy))
                {
                    ModelState.AddModelError(nameof(vm.WalletFile), "Wallet file was not in the correct format");
                    return(View(vm.ViewName, vm));
                }
            }
            else if (!string.IsNullOrEmpty(vm.WalletFileContent))
            {
                if (!DerivationSchemeSettings.TryParseFromWalletFile(vm.WalletFileContent, network, out strategy))
                {
                    ModelState.AddModelError(nameof(vm.WalletFileContent), "QR import was not in the correct format");
                    return(View(vm.ViewName, vm));
                }
            }
            else if (!string.IsNullOrEmpty(vm.DerivationScheme))
            {
                try
                {
                    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();
                    }
                }
                catch
                {
                    ModelState.AddModelError(nameof(vm.DerivationScheme), "Invalid wallet format");
                    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();
            var             configChanged   = oldConfig != vm.Config;
            PaymentMethodId paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike);
            var             storeBlob       = store.GetStoreBlob();
            var             wasExcluded     = storeBlob.GetExcludedPaymentMethods().Match(paymentMethodId);
            var             willBeExcluded  = !vm.Enabled;
            var             excludedChanged = willBeExcluded != wasExcluded;

            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
                {
                    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.ViewName, vm));
                }

                await _Repo.UpdateStore(store);

                _EventAggregator.Publish(new WalletChangedEvent {
                    WalletId = new WalletId(vm.StoreId, vm.CryptoCode)
                });

                if (excludedChanged)
                {
                    var label = willBeExcluded ? "disabled" : "enabled";
                    TempData[WellKnownTempData.SuccessMessage] =
                        $"On-Chain payments for {network.CryptoCode} have been {label}.";
                }
                else
                {
                    TempData[WellKnownTempData.SuccessMessage] =
                        $"Derivation settings for {network.CryptoCode} have been modified.";
                }

                // This is success case when derivation scheme is added to the store
                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));
                }

                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. 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));
        }
コード例 #3
0
        public async Task <IActionResult> UpdateWallet(WalletSetupViewModel vm)
        {
            var checkResult = IsAvailable(vm.CryptoCode, out var store, out var network);

            if (checkResult != null)
            {
                return(checkResult);
            }

            vm.Network = network;
            DerivationSchemeSettings strategy = null;

            var wallet = _WalletProvider.GetWallet(network);

            if (wallet == null)
            {
                return(NotFound());
            }

            if (vm.WalletFile != null)
            {
                if (!DerivationSchemeSettings.TryParseFromWalletFile(await ReadAllText(vm.WalletFile), network, out strategy))
                {
                    ModelState.AddModelError(nameof(vm.WalletFile), "Wallet file was not in the correct format");
                    return(View(vm.ViewName, vm));
                }
            }
            else if (!string.IsNullOrEmpty(vm.WalletFileContent))
            {
                if (!DerivationSchemeSettings.TryParseFromWalletFile(vm.WalletFileContent, network, out strategy))
                {
                    ModelState.AddModelError(nameof(vm.WalletFileContent), "QR import was not in the correct format");
                    return(View(vm.ViewName, vm));
                }
            }
            else if (!string.IsNullOrEmpty(vm.DerivationScheme))
            {
                try
                {
                    strategy        = ParseDerivationStrategy(vm.DerivationScheme, network);
                    strategy.Source = "ManualDerivationScheme";
                    if (!string.IsNullOrEmpty(vm.AccountKey))
                    {
                        var accountKey      = new BitcoinExtPubKey(vm.AccountKey, network.NBitcoinNetwork);
                        var accountSettings =
                            strategy.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));
                        }
                    }
                    vm.DerivationScheme = strategy.AccountDerivation.ToString();
                    ModelState.Remove(nameof(vm.DerivationScheme));
                }
                catch
                {
                    ModelState.AddModelError(nameof(vm.DerivationScheme), "Invalid wallet format");
                    return(View(vm.ViewName, vm));
                }
            }
            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");
                return(View(vm.ViewName, vm));
            }

            vm.Config = ProtectString(strategy.ToJson());
            ModelState.Remove(nameof(vm.Config));

            PaymentMethodId paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike);
            var             storeBlob       = store.GetStoreBlob();

            if (vm.Confirmation)
            {
                try
                {
                    await wallet.TrackAsync(strategy.AccountDerivation);

                    store.SetSupportedPaymentMethod(paymentMethodId, strategy);
                    storeBlob.SetExcluded(paymentMethodId, false);
                    storeBlob.Hints.Wallet   = false;
                    storeBlob.PayJoinEnabled = strategy.IsHotWallet && !(vm.SetupRequest?.PayJoinEnabled is false);
                    store.SetStoreBlob(storeBlob);
                }
                catch
                {
                    ModelState.AddModelError(nameof(vm.DerivationScheme), "Invalid derivation scheme");
                    return(View(vm.ViewName, vm));
                }
                await _Repo.UpdateStore(store);

                _EventAggregator.Publish(new WalletChangedEvent {
                    WalletId = new WalletId(vm.StoreId, vm.CryptoCode)
                });

                TempData[WellKnownTempData.SuccessMessage] = $"Wallet settings for {network.CryptoCode} have been updated.";

                // This is success case when derivation scheme is added to the store
                return(RedirectToAction(nameof(UpdateStore), new { storeId = vm.StoreId }));
            }
            return(ConfirmAddresses(vm, strategy));
        }
コード例 #4
0
        public async Task <IActionResult> 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
            {
                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 <DerivationSchemeSettings>()
                                              .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 clickingon 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);
                    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.";
                }
                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));
        }