public IActionResult OfflineColdStakingWithdrawal([FromBody] OfflineColdStakingWithdrawalRequest request)
        {
            Guard.NotNull(request, nameof(request));

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

            try
            {
                Money amount    = Money.Parse(request.Amount);
                Money feeAmount = Money.Parse(request.Fees);

                BuildOfflineSignResponse response = this.ColdStakingManager.BuildOfflineColdStakingWithdrawalRequest(this.walletTransactionHandler,
                                                                                                                     request.ReceivingAddress, request.WalletName, request.AccountName, amount, feeAmount, request.SubtractFeeFromAmount);

                this.logger.LogTrace("(-):'{0}'", response);

                return(this.Json(response));
            }
            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 SignTransactionOffline()
        {
            using (NodeBuilder builder = NodeBuilder.Create(this))
            {
                CoreNode miningNode  = builder.CreateStratisPosNode(this.network).WithReadyBlockchainData(ReadyBlockchain.StraxRegTest150Miner).Start();
                CoreNode onlineNode  = builder.CreateStratisPosNode(this.network).Start();
                CoreNode offlineNode = builder.CreateStratisPosNode(this.network).WithWallet().Start();
                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         = "mywallet",
                    AccountIndex = 0,
                    ExtPubKey    = extPubKey,
                    CreationDate = DateTime.Today
                })
                .ReceiveJson();

                TestHelper.SendCoins(miningNode, onlineNode, Money.Coins(5.0m));
                TestHelper.MineBlocks(miningNode, 1);

                // Build the offline signing template from the online node. No password is needed.
                BuildOfflineSignResponse offlineTemplate = await $"http://localhost:{onlineNode.ApiPort}/api"
                                                           .AppendPathSegment("wallet/build-offline-sign-request")
                                                           .PostJsonAsync(new BuildTransactionRequest
                {
                    WalletName       = "mywallet",
                    AccountName      = "account 0",
                    FeeAmount        = "0.01",
                    ShuffleOutputs   = true,
                    AllowUnconfirmed = true,
                    Recipients       = new List <RecipientModel>()
                    {
                        new RecipientModel
                        {
                            DestinationAddress = new Key().ScriptPubKey.GetDestinationAddress(this.network).ToString(),
                            Amount             = "1"
                        }
                    }
                })
                                                           .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.
                WalletBuildTransactionModel builtTransactionModel = await $"http://localhost:{offlineNode.ApiPort}/api"
                                                                    .AppendPathSegment("wallet/offline-sign-request")
                                                                    .PostJsonAsync(new OfflineSignRequest()
                {
                    WalletName          = offlineTemplate.WalletName,
                    WalletAccount       = offlineTemplate.WalletAccount,
                    WalletPassword      = "******",
                    UnsignedTransaction = offlineTemplate.UnsignedTransaction,
                    Fee       = offlineTemplate.Fee,
                    Utxos     = offlineTemplate.Utxos,
                    Addresses = offlineTemplate.Addresses
                })
                                                                    .ReceiveJson <WalletBuildTransactionModel>();

                // 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);
            }
        }
        public IActionResult SetupOfflineColdStaking([FromBody] SetupOfflineColdStakingRequest request)
        {
            Guard.NotNull(request, nameof(request));

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

            try
            {
                Money amount    = Money.Parse(request.Amount);
                Money feeAmount = Money.Parse(request.Fees);

                (Transaction transaction, TransactionBuildContext context) = this.ColdStakingManager.GetColdStakingSetupTransaction(
                    this.walletTransactionHandler,
                    request.ColdWalletAddress,
                    request.HotWalletAddress,
                    request.WalletName,
                    request.WalletAccount,
                    null,
                    amount,
                    feeAmount,
                    request.SubtractFeeFromAmount,
                    true,
                    request.SplitCount,
                    request.SegwitChangeAddress);

                // TODO: We use the same code in the regular wallet for offline signing request construction, perhaps it should be moved to a common method
                // Need to be able to look up the keypath for the UTXOs that were used.
                IEnumerable <UnspentOutputReference> spendableTransactions = this.ColdStakingManager.GetSpendableTransactionsInAccount(
                    new WalletAccountReference(request.WalletName, request.WalletAccount)).ToList();

                var utxos     = new List <UtxoDescriptor>();
                var addresses = new List <AddressDescriptor>();
                foreach (ICoin coin in context.TransactionBuilder.FindSpentCoins(transaction))
                {
                    utxos.Add(new UtxoDescriptor()
                    {
                        Amount        = coin.TxOut.Value.ToUnit(MoneyUnit.BTC).ToString(),
                        TransactionId = coin.Outpoint.Hash.ToString(),
                        Index         = coin.Outpoint.N.ToString(),
                        ScriptPubKey  = coin.TxOut.ScriptPubKey.ToHex()
                    });

                    UnspentOutputReference outputReference = spendableTransactions.FirstOrDefault(u => u.Transaction.Id == coin.Outpoint.Hash && u.Transaction.Index == coin.Outpoint.N);

                    if (outputReference != null)
                    {
                        bool segwit = outputReference.Transaction.ScriptPubKey.IsScriptType(ScriptType.P2WPKH);
                        addresses.Add(new AddressDescriptor()
                        {
                            Address = segwit ? outputReference.Address.Bech32Address : outputReference.Address.Address, AddressType = segwit ? "p2wpkh" : "p2pkh", KeyPath = outputReference.Address.HdPath
                        });
                    }
                }

                // Return transaction hex, UTXO list, address list. The offline signer will infer from the transaction structure that a cold staking setup is being made.
                var model = new BuildOfflineSignResponse()
                {
                    WalletName          = request.WalletName,
                    WalletAccount       = request.WalletAccount,
                    Fee                 = context.TransactionFee.ToUnit(MoneyUnit.BTC).ToString(),
                    UnsignedTransaction = transaction.ToHex(),
                    Utxos               = utxos,
                    Addresses           = addresses
                };

                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 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);
            }
        }