public IActionResult CreateColdStakingAccount([FromBody] CreateColdStakingAccountRequest request)
        {
            Guard.NotNull(request, nameof(request));

            // Checks that the request is valid.
            if (!this.ModelState.IsValid)
            {
                this.logger.LogTrace("(-)[MODEL_STATE_INVALID]");
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            try
            {
                var model = new CreateColdStakingAccountResponse
                {
                    AccountName = this.ColdStakingManager.GetOrCreateColdStakingAccount(request.WalletName, request.IsColdWalletAccount, request.WalletPassword).Name
                };

                this.logger.LogTrace("(-):'{0}'", model);
                return(this.Json(model));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                this.logger.LogTrace("(-)[ERROR]");
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
        public async Task SignColdStakingSetupOfflineWithThirdPartyHotAddress()
        {
            using (NodeBuilder builder = NodeBuilder.Create(this))
            {
                CoreNode miningNode  = builder.CreateStratisColdStakingNode(this.network).WithReadyBlockchainData(ReadyBlockchain.StraxRegTest150Miner).Start();
                CoreNode onlineNode  = builder.CreateStratisColdStakingNode(this.network).Start();
                CoreNode offlineNode = builder.CreateStratisColdStakingNode(this.network).WithWallet().Start();

                // The offline node never gets connected to anything.
                TestHelper.ConnectAndSync(miningNode, onlineNode);

                // Get the default (non-special) account extpubkey from the offline node to restore on the online node.
                string extPubKey = await $"http://localhost:{offlineNode.ApiPort}/api"
                                   .AppendPathSegment("wallet/extpubkey")
                                   .SetQueryParams(new { walletName = "mywallet", accountName = "account 0" })
                                   .GetJsonAsync <string>();

                // Load the extpubkey onto the online node.
                await $"http://localhost:{onlineNode.ApiPort}/api"
                .AppendPathSegment("wallet/recover-via-extpubkey")
                .PostJsonAsync(new WalletExtPubRecoveryRequest
                {
                    Name         = "coldwallet",
                    AccountIndex = 0,
                    ExtPubKey    = extPubKey,
                    CreationDate = DateTime.Today - TimeSpan.FromDays(1)
                })
                .ReceiveJson();

                // Make the hot address an arbitrary one, to show that we can withdraw without the hot node's involvement.
                string hotAddress = new Key().PubKey.Hash.ScriptPubKey.GetDestinationAddress(this.network).ToString();

                string coldWalletUnusedAddress = await $"http://localhost:{onlineNode.ApiPort}/api"
                                                 .AppendPathSegment("wallet/unusedaddress")
                                                 .SetQueryParams(new { walletName = "coldwallet", accountName = "account 0" })
                                                 .GetJsonAsync <string>();

                // Send some funds to the cold wallet's default (non-special) account to use for the staking setup.
                string fundTransaction = (await $"http://localhost:{miningNode.ApiPort}/api"
                                          .AppendPathSegment("wallet/build-transaction")
                                          .PostJsonAsync(new BuildTransactionRequest
                {
                    WalletName = "mywallet",
                    Password = "******",
                    AccountName = "account 0",
                    FeeType = "high",
                    Recipients = new List <RecipientModel>()
                    {
                        new RecipientModel()
                        {
                            Amount = "5", DestinationAddress = coldWalletUnusedAddress
                        }
                    }
                })
                                          .ReceiveJson <WalletBuildTransactionModel>()).Hex;

                await $"http://localhost:{miningNode.ApiPort}/api"
                .AppendPathSegment("wallet/send-transaction")
                .PostJsonAsync(new SendTransactionRequest()
                {
                    Hex = fundTransaction
                })
                .ReceiveJson <WalletSendTransactionModel>();

                TestBase.WaitLoop(() => miningNode.CreateRPCClient().GetRawMempool().Length > 0);
                TestHelper.MineBlocks(miningNode, 1);

                // Set up cold staking account on offline node to get the needed cold address.
                CreateColdStakingAccountResponse coldAccount = await $"http://localhost:{offlineNode.ApiPort}/api"
                                                               .AppendPathSegment("coldstaking/cold-staking-account")
                                                               .PostJsonAsync(new CreateColdStakingAccountRequest()
                {
                    WalletName          = "mywallet",
                    WalletPassword      = "******",
                    IsColdWalletAccount = true
                })
                                                               .ReceiveJson <CreateColdStakingAccountResponse>();

                string coldAddress = (await $"http://localhost:{offlineNode.ApiPort}/api"
                                      .AppendPathSegment("coldstaking/cold-staking-address")
                                      .SetQueryParams(new { walletName = "mywallet", isColdWalletAddress = "true" })
                                      .GetJsonAsync <GetColdStakingAddressResponse>()).Address;

                // Get the cold account extpubkey from the offline node to restore on the online node.
                string coldExtPubKey = await $"http://localhost:{offlineNode.ApiPort}/api"
                                       .AppendPathSegment("wallet/extpubkey")
                                       .SetQueryParams(new { walletName = "mywallet", accountName = "coldStakingColdAddresses" })
                                       .GetJsonAsync <string>();

                // Restore the cold account extpubkey on the online node. Since we have no access to the hot address and its account,
                // the cold account is the only way we can construct a withdrawal.
                await $"http://localhost:{onlineNode.ApiPort}/api"
                .AppendPathSegment("wallet/recover-via-extpubkey")
                .PostJsonAsync(new WalletExtPubRecoveryRequest
                {
                    Name         = "coldaccount",
                    AccountIndex = 100_000_000,
                    ExtPubKey    = coldExtPubKey,
                    CreationDate = DateTime.Today - TimeSpan.FromDays(1)
                })
        public async Task SignColdStakingSetupOffline()
        {
            using (NodeBuilder builder = NodeBuilder.Create(this))
            {
                CoreNode miningNode  = builder.CreateStratisColdStakingNode(this.network).WithReadyBlockchainData(ReadyBlockchain.StraxRegTest150Miner).Start();
                CoreNode onlineNode  = builder.CreateStratisColdStakingNode(this.network).Start();
                CoreNode offlineNode = builder.CreateStratisColdStakingNode(this.network).WithWallet().Start();

                // The offline node never gets connected to anything.
                TestHelper.ConnectAndSync(miningNode, onlineNode);

                // Get the extpubkey from the offline node to restore on the online node.
                string extPubKey = await $"http://localhost:{offlineNode.ApiPort}/api"
                                   .AppendPathSegment("wallet/extpubkey")
                                   .SetQueryParams(new { walletName = "mywallet", accountName = "account 0" })
                                   .GetJsonAsync <string>();

                // Load the extpubkey onto the online node.
                await $"http://localhost:{onlineNode.ApiPort}/api"
                .AppendPathSegment("wallet/recover-via-extpubkey")
                .PostJsonAsync(new WalletExtPubRecoveryRequest
                {
                    Name         = "coldwallet",
                    AccountIndex = 0,
                    ExtPubKey    = extPubKey,
                    CreationDate = DateTime.Today - TimeSpan.FromDays(1)
                })
                .ReceiveJson();

                // Get a mnemonic for the hot wallet.
                string hotWalletMnemonic = await $"http://localhost:{onlineNode.ApiPort}/api"
                                           .AppendPathSegment("wallet/mnemonic")
                                           .GetJsonAsync <string>();

                // Restore the hot wallet on the online node.
                // This is needed because the hot address needs to have a private key available to stake with.
                await $"http://localhost:{onlineNode.ApiPort}/api"
                .AppendPathSegment("wallet/recover")
                .PostJsonAsync(new WalletRecoveryRequest()
                {
                    Name         = "hotwallet",
                    Mnemonic     = hotWalletMnemonic,
                    Password     = "******",
                    Passphrase   = "",
                    CreationDate = DateTime.Today - TimeSpan.FromDays(1)
                })
                .ReceiveJson();

                // Get the hot address from the online node.
                CreateColdStakingAccountResponse hotAccount = await $"http://localhost:{onlineNode.ApiPort}/api"
                                                              .AppendPathSegment("coldstaking/cold-staking-account")
                                                              .PostJsonAsync(new CreateColdStakingAccountRequest()
                {
                    WalletName          = "hotwallet",
                    WalletPassword      = "******",
                    IsColdWalletAccount = false
                })
                                                              .ReceiveJson <CreateColdStakingAccountResponse>();

                string hotAddress = (await $"http://localhost:{onlineNode.ApiPort}/api"
                                     .AppendPathSegment("coldstaking/cold-staking-address")
                                     .SetQueryParams(new { walletName = "hotwallet", isColdWalletAddress = "false" })
                                     .GetJsonAsync <GetColdStakingAddressResponse>()).Address;

                string coldWalletUnusedAddress = await $"http://localhost:{onlineNode.ApiPort}/api"
                                                 .AppendPathSegment("wallet/unusedaddress")
                                                 .SetQueryParams(new { walletName = "coldwallet", accountName = "account 0" })
                                                 .GetJsonAsync <string>();

                // Send some funds to the cold wallet's default (non-special) account to use for the staking setup.
                string fundTransaction = (await $"http://localhost:{miningNode.ApiPort}/api"
                                          .AppendPathSegment("wallet/build-transaction")
                                          .PostJsonAsync(new BuildTransactionRequest
                {
                    WalletName = "mywallet",
                    Password = "******",
                    AccountName = "account 0",
                    FeeType = "high",
                    Recipients = new List <RecipientModel>()
                    {
                        new RecipientModel()
                        {
                            Amount = "5", DestinationAddress = coldWalletUnusedAddress
                        }
                    }
                })
                                          .ReceiveJson <WalletBuildTransactionModel>()).Hex;

                await $"http://localhost:{miningNode.ApiPort}/api"
                .AppendPathSegment("wallet/send-transaction")
                .PostJsonAsync(new SendTransactionRequest()
                {
                    Hex = fundTransaction
                })
                .ReceiveJson <WalletSendTransactionModel>();

                TestBase.WaitLoop(() => miningNode.CreateRPCClient().GetRawMempool().Length > 0);
                TestHelper.MineBlocks(miningNode, 1);

                // Set up cold staking account on offline node to get the needed cold address.
                CreateColdStakingAccountResponse coldAccount = await $"http://localhost:{offlineNode.ApiPort}/api"
                                                               .AppendPathSegment("coldstaking/cold-staking-account")
                                                               .PostJsonAsync(new CreateColdStakingAccountRequest()
                {
                    WalletName          = "mywallet",
                    WalletPassword      = "******",
                    IsColdWalletAccount = true
                })
                                                               .ReceiveJson <CreateColdStakingAccountResponse>();

                string coldAddress = (await $"http://localhost:{offlineNode.ApiPort}/api"
                                      .AppendPathSegment("coldstaking/cold-staking-address")
                                      .SetQueryParams(new { walletName = "mywallet", isColdWalletAddress = "true" })
                                      .GetJsonAsync <GetColdStakingAddressResponse>()).Address;

                // Build the offline cold staking template from the online node. No password is needed.
                BuildOfflineSignResponse offlineTemplate = await $"http://localhost:{onlineNode.ApiPort}/api"
                                                           .AppendPathSegment("coldstaking/setup-offline-cold-staking")
                                                           .PostJsonAsync(new SetupOfflineColdStakingRequest()
                {
                    ColdWalletAddress = coldAddress,
                    HotWalletAddress  = hotAddress,
                    WalletName        = "coldwallet",
                    WalletAccount     = "account 0",
                    Amount            = "5", // Check that we can send the entire available balance in the setup
                    Fees = "0.01",
                    SubtractFeeFromAmount = true,
                    SegwitChangeAddress   = false,
                    SplitCount            = 10
                })
                                                           .ReceiveJson <BuildOfflineSignResponse>();

                // Now build the actual transaction on the offline node. It is not synced with the others and only has the information
                // in the signing request and its own wallet to construct the transaction with.
                // Note that the wallet name and account name on the offline node may not actually match those from the online node.
                WalletBuildTransactionModel builtTransactionModel = await $"http://localhost:{offlineNode.ApiPort}/api"
                                                                    .AppendPathSegment("wallet/offline-sign-request")
                                                                    .PostJsonAsync(new OfflineSignRequest()
                {
                    WalletName          = "mywallet",
                    WalletAccount       = offlineTemplate.WalletAccount,
                    WalletPassword      = "******",
                    UnsignedTransaction = offlineTemplate.UnsignedTransaction,
                    Fee       = offlineTemplate.Fee,
                    Utxos     = offlineTemplate.Utxos,
                    Addresses = offlineTemplate.Addresses
                })
                                                                    .ReceiveJson <WalletBuildTransactionModel>();

                Dictionary <string, int> txCountBefore = await $"http://localhost:{onlineNode.ApiPort}/api"
                                                         .AppendPathSegment("wallet/transactionCount")
                                                         .SetQueryParams(new { walletName = "hotwallet", accountName = hotAccount.AccountName })
                                                         .GetJsonAsync <Dictionary <string, int> >();

                Assert.True(txCountBefore.Values.First() == 0);

                // Send the signed transaction from the online node (doesn't really matter, could equally be from the mining node).
                await $"http://localhost:{onlineNode.ApiPort}/api"
                .AppendPathSegment("wallet/send-transaction")
                .PostJsonAsync(new SendTransactionRequest
                {
                    Hex = builtTransactionModel.Hex
                })
                .ReceiveJson <WalletSendTransactionModel>();

                // Check that the transaction is valid and therefore relayed, and able to be mined into a block.
                TestBase.WaitLoop(() => miningNode.CreateRPCClient().GetRawMempool().Length == 1);
                TestHelper.MineBlocks(miningNode, 1);
                TestBase.WaitLoop(() => miningNode.CreateRPCClient().GetRawMempool().Length == 0);

                Dictionary <string, int> txCountAfter = await $"http://localhost:{onlineNode.ApiPort}/api"
                                                        .AppendPathSegment("wallet/transactionCount")
                                                        .SetQueryParams(new { walletName = "hotwallet", accountName = hotAccount.AccountName })
                                                        .GetJsonAsync <Dictionary <string, int> >();

                Assert.True(txCountAfter.Values.First() > 0);

                string onlineNodeUnusedAddress = await $"http://localhost:{onlineNode.ApiPort}/api"
                                                 .AppendPathSegment("wallet/unusedaddress")
                                                 .SetQueryParams(new { walletName = "hotwallet", accountName = "account 0" })
                                                 .GetJsonAsync <string>();

                // Now attempt a withdrawal. First get the estimated fee.
                Money offlineWithdrawalFee = await $"http://localhost:{onlineNode.ApiPort}/api"
                                             .AppendPathSegment("coldstaking/estimate-offline-cold-staking-withdrawal-tx-fee")
                                             .PostJsonAsync(new OfflineColdStakingWithdrawalFeeEstimationRequest()
                {
                    WalletName            = "hotwallet",
                    AccountName           = hotAccount.AccountName,
                    ReceivingAddress      = onlineNodeUnusedAddress,
                    Amount                = "4", // Withdraw part of the available balance in the cold account.
                    SubtractFeeFromAmount = true
                })
                                             .ReceiveJson <Money>();

                // Now generate the actual unsigned template transaction.
                BuildOfflineSignResponse offlineWithdrawalTemplate = await $"http://localhost:{onlineNode.ApiPort}/api"
                                                                     .AppendPathSegment("coldstaking/offline-cold-staking-withdrawal")
                                                                     .PostJsonAsync(new OfflineColdStakingWithdrawalRequest()
                {
                    WalletName            = "hotwallet",
                    AccountName           = hotAccount.AccountName,
                    ReceivingAddress      = onlineNodeUnusedAddress,
                    Amount                = "4", // Withdraw part of the available balance in the cold account.
                    Fees                  = offlineWithdrawalFee.ToString(),
                    SubtractFeeFromAmount = true
                })
                                                                     .ReceiveJson <BuildOfflineSignResponse>();

                WalletBuildTransactionModel builtWithdrawalTransactionModel = await $"http://localhost:{offlineNode.ApiPort}/api"
                                                                              .AppendPathSegment("wallet/offline-sign-request")
                                                                              .PostJsonAsync(new OfflineSignRequest()
                {
                    WalletName          = "mywallet",
                    WalletAccount       = "coldStakingColdAddresses",
                    WalletPassword      = "******",
                    UnsignedTransaction = offlineWithdrawalTemplate.UnsignedTransaction,
                    Fee       = offlineWithdrawalTemplate.Fee,
                    Utxos     = offlineWithdrawalTemplate.Utxos,
                    Addresses = offlineWithdrawalTemplate.Addresses
                })
                                                                              .ReceiveJson <WalletBuildTransactionModel>();

                Dictionary <string, int> txCountBefore2 = await $"http://localhost:{onlineNode.ApiPort}/api"
                                                          .AppendPathSegment("wallet/transactionCount")
                                                          .SetQueryParams(new { walletName = "hotwallet", accountName = "account 0" })
                                                          .GetJsonAsync <Dictionary <string, int> >();

                Assert.True(txCountBefore2.Values.First() == 0);

                // Send the signed transaction from the online node (doesn't really matter, could equally be from the mining node).
                await $"http://localhost:{onlineNode.ApiPort}/api"
                .AppendPathSegment("wallet/send-transaction")
                .PostJsonAsync(new SendTransactionRequest
                {
                    Hex = builtWithdrawalTransactionModel.Hex
                })
                .ReceiveJson <WalletSendTransactionModel>();

                // Check that the transaction is valid and therefore relayed, and able to be mined into a block.
                TestBase.WaitLoop(() => miningNode.CreateRPCClient().GetRawMempool().Length == 1);
                TestHelper.MineBlocks(miningNode, 1);
                TestBase.WaitLoop(() => miningNode.CreateRPCClient().GetRawMempool().Length == 0);

                Dictionary <string, int> txCountAfter2 = await $"http://localhost:{onlineNode.ApiPort}/api"
                                                         .AppendPathSegment("wallet/transactionCount")
                                                         .SetQueryParams(new { walletName = "hotwallet", accountName = "account 0" })
                                                         .GetJsonAsync <Dictionary <string, int> >();

                Assert.True(txCountAfter2.Values.First() > 0);
            }
        }