diff --git a/doc/release-notes.md b/doc/release-notes.md
index 3be26adad..c82682126 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -1,17 +1,22 @@
 Bitcoin ABC version 0.21.9 is now available from:
 
   <https://download.bitcoinabc.org/0.21.9/>
 
 This release includes the following features and fixes:
 - Improve management of maxfee by the wallet.
 
 Wallet changes
 --------------
 When creating a transaction with a fee above `-maxtxfee` (default 0.1 BCH),
 the RPC commands `walletcreatefundedpsbt` and  `fundrawtransaction` will now fail
 instead of rounding down the fee. Beware that the `feeRate` argument is specified
 in BCH per kilobyte, not satoshi per byte.
 
 RPC changes
 -----------
 The `getblockstats` RPC is faster for fee calculation by using BlockUndo data. Also, `-txindex` is no longer required and `getblockstats` works for all non-pruned blocks.
+
+Miscellaneous RPC changes
+------------
+
+- `createwallet` can now create encrypted wallets if a non-empty passphrase is specified.
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 2c9f5b960..daa048b75 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -1,4806 +1,4850 @@
 // Copyright (c) 2010 Satoshi Nakamoto
 // Copyright (c) 2009-2018 The Bitcoin Core developers
 // Distributed under the MIT software license, see the accompanying
 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
 
 #include <amount.h>
 #include <chainparams.h> // for GetConsensus.
 #include <coins.h>
 #include <config.h>
 #include <consensus/validation.h>
 #include <core_io.h>
 #include <interfaces/chain.h>
 #include <key_io.h>
 #include <node/context.h>
 #include <node/transaction.h>
 #include <outputtype.h>
 #include <policy/fees.h>
 #include <rpc/mining.h>
 #include <rpc/rawtransaction_util.h>
 #include <rpc/server.h>
 #include <rpc/util.h>
 #include <script/descriptor.h>
 #include <util/bip32.h>
 #include <util/error.h>
 #include <util/moneystr.h>
 #include <util/system.h>
 #include <util/url.h>
 #include <util/validation.h>
 #include <wallet/coincontrol.h>
 #include <wallet/psbtwallet.h>
 #include <wallet/rpcwallet.h>
 #include <wallet/wallet.h>
 #include <wallet/walletdb.h>
 #include <wallet/walletutil.h>
 
 #include <univalue.h>
 
 #include <event2/http.h>
 
 #include <functional>
 
 static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
 
 [[maybe_unused]] static inline bool GetAvoidReuseFlag(CWallet *const pwallet,
                                                       const UniValue &param) {
     bool can_avoid_reuse = pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
     bool avoid_reuse = param.isNull() ? can_avoid_reuse : param.get_bool();
 
     if (avoid_reuse && !can_avoid_reuse) {
         throw JSONRPCError(
             RPC_WALLET_ERROR,
             "wallet does not have the \"avoid reuse\" feature enabled");
     }
 
     return avoid_reuse;
 }
 
 bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest &request,
                                      std::string &wallet_name) {
     if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) ==
         WALLET_ENDPOINT_BASE) {
         // wallet endpoint was used
         wallet_name =
             urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size()));
         return true;
     }
     return false;
 }
 
 std::shared_ptr<CWallet>
 GetWalletForJSONRPCRequest(const JSONRPCRequest &request) {
     std::string wallet_name;
     if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
         std::shared_ptr<CWallet> pwallet = GetWallet(wallet_name);
         if (!pwallet) {
             throw JSONRPCError(
                 RPC_WALLET_NOT_FOUND,
                 "Requested wallet does not exist or is not loaded");
         }
         return pwallet;
     }
 
     std::vector<std::shared_ptr<CWallet>> wallets = GetWallets();
     return wallets.size() == 1 || (request.fHelp && wallets.size() > 0)
                ? wallets[0]
                : nullptr;
 }
 
 std::string HelpRequiringPassphrase(CWallet *const pwallet) {
     return pwallet && pwallet->IsCrypted()
                ? "\nRequires wallet passphrase to be set with walletpassphrase "
                  "call."
                : "";
 }
 
 bool EnsureWalletIsAvailable(CWallet *const pwallet, bool avoidException) {
     if (pwallet) {
         return true;
     }
     if (avoidException) {
         return false;
     }
     if (!HasWallets()) {
         throw JSONRPCError(RPC_METHOD_NOT_FOUND,
                            "Method not found (wallet method is disabled "
                            "because no wallet is loaded)");
     }
 
     throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED,
                        "Wallet file not specified (must request wallet RPC "
                        "through /wallet/<filename> uri-path).");
 }
 
 void EnsureWalletIsUnlocked(CWallet *const pwallet) {
     if (pwallet->IsLocked()) {
         throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED,
                            "Error: Please enter the wallet passphrase with "
                            "walletpassphrase first.");
     }
 }
 
 static void WalletTxToJSON(interfaces::Chain &chain,
                            interfaces::Chain::Lock &locked_chain,
                            const CWalletTx &wtx, UniValue &entry) {
     int confirms = wtx.GetDepthInMainChain(locked_chain);
     entry.pushKV("confirmations", confirms);
     if (wtx.IsCoinBase()) {
         entry.pushKV("generated", true);
     }
     if (confirms > 0) {
         entry.pushKV("blockhash", wtx.hashBlock.GetHex());
         entry.pushKV("blockindex", wtx.nIndex);
         int64_t block_time;
         bool found_block =
             chain.findBlock(wtx.hashBlock, nullptr /* block */, &block_time);
         assert(found_block);
         entry.pushKV("blocktime", block_time);
     } else {
         entry.pushKV("trusted", wtx.IsTrusted(locked_chain));
     }
     uint256 hash = wtx.GetId();
     entry.pushKV("txid", hash.GetHex());
     UniValue conflicts(UniValue::VARR);
     for (const uint256 &conflict : wtx.GetConflicts()) {
         conflicts.push_back(conflict.GetHex());
     }
     entry.pushKV("walletconflicts", conflicts);
     entry.pushKV("time", wtx.GetTxTime());
     entry.pushKV("timereceived", (int64_t)wtx.nTimeReceived);
 
     for (const std::pair<const std::string, std::string> &item : wtx.mapValue) {
         entry.pushKV(item.first, item.second);
     }
 }
 
 static std::string LabelFromValue(const UniValue &value) {
     std::string label = value.get_str();
     if (label == "*") {
         throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, "Invalid label name");
     }
     return label;
 }
 
 static UniValue getnewaddress(const Config &config,
                               const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() > 2) {
         throw std::runtime_error(RPCHelpMan{
             "getnewaddress",
             "\nReturns a new Bitcoin address for receiving payments.\n"
             "If 'label' is specified, it is added to the address book \n"
             "so payments received with the address will be associated with "
             "'label'.\n",
             {
                 {"label", RPCArg::Type::STR, /* default */ "null",
                  "The label name for the address to be linked to. If not "
                  "provided, the default label \"\" is used. It can also be set "
                  "to the empty string \"\" to represent the default label. The "
                  "label does not need to exist, it will be created if there is "
                  "no label by the given name."},
             },
             RPCResult{"\"address\"    (string) The new bitcoin address\n"},
             RPCExamples{HelpExampleCli("getnewaddress", "") +
                         HelpExampleRpc("getnewaddress", "")},
         }
                                      .ToString());
     }
 
     LOCK(pwallet->cs_wallet);
 
     if (!pwallet->CanGetAddresses()) {
         throw JSONRPCError(RPC_WALLET_ERROR,
                            "Error: This wallet has no available keys");
     }
 
     // Parse the label first so we don't generate a key if there's an error
     std::string label;
     if (!request.params[0].isNull()) {
         label = LabelFromValue(request.params[0]);
     }
 
     OutputType output_type = pwallet->m_default_address_type;
     if (!request.params[1].isNull()) {
         if (!ParseOutputType(request.params[1].get_str(), output_type)) {
             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
                                strprintf("Unknown address type '%s'",
                                          request.params[1].get_str()));
         }
     }
 
     if (!pwallet->IsLocked()) {
         pwallet->TopUpKeyPool();
     }
 
     // Generate a new key that is added to wallet
     CPubKey newKey;
     if (!pwallet->GetKeyFromPool(newKey)) {
         throw JSONRPCError(
             RPC_WALLET_KEYPOOL_RAN_OUT,
             "Error: Keypool ran out, please call keypoolrefill first");
     }
     pwallet->LearnRelatedScripts(newKey, output_type);
     CTxDestination dest = GetDestinationForKey(newKey, output_type);
 
     pwallet->SetAddressBook(dest, label, "receive");
 
     return EncodeDestination(dest, config);
 }
 
 static UniValue getrawchangeaddress(const Config &config,
                                     const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() > 1) {
         throw std::runtime_error(RPCHelpMan{
             "getrawchangeaddress",
             "\nReturns a new Bitcoin address, for receiving change.\n"
             "This is for use with raw transactions, NOT normal use.\n",
             {},
             RPCResult{"\"address\"    (string) The address\n"},
             RPCExamples{HelpExampleCli("getrawchangeaddress", "") +
                         HelpExampleRpc("getrawchangeaddress", "")},
         }
                                      .ToString());
     }
 
     LOCK(pwallet->cs_wallet);
 
     if (!pwallet->CanGetAddresses(true)) {
         throw JSONRPCError(RPC_WALLET_ERROR,
                            "Error: This wallet has no available keys");
     }
 
     if (!pwallet->IsLocked()) {
         pwallet->TopUpKeyPool();
     }
 
     OutputType output_type =
         pwallet->m_default_change_type != OutputType::CHANGE_AUTO
             ? pwallet->m_default_change_type
             : pwallet->m_default_address_type;
     if (!request.params[0].isNull()) {
         if (!ParseOutputType(request.params[0].get_str(), output_type)) {
             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
                                strprintf("Unknown address type '%s'",
                                          request.params[0].get_str()));
         }
     }
 
     CReserveKey reservekey(pwallet);
     CPubKey vchPubKey;
     if (!reservekey.GetReservedKey(vchPubKey, true)) {
         throw JSONRPCError(
             RPC_WALLET_KEYPOOL_RAN_OUT,
             "Error: Keypool ran out, please call keypoolrefill first");
     }
 
     reservekey.KeepKey();
 
     pwallet->LearnRelatedScripts(vchPubKey, output_type);
     CTxDestination dest = GetDestinationForKey(vchPubKey, output_type);
 
     return EncodeDestination(dest, config);
 }
 
 static UniValue setlabel(const Config &config, const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() != 2) {
         throw std::runtime_error(RPCHelpMan{
             "setlabel",
             "\nSets the label associated with the given address.\n",
             {
                 {"address", RPCArg::Type::STR, RPCArg::Optional::NO,
                  "The bitcoin address to be associated with a label."},
                 {"label", RPCArg::Type::STR, RPCArg::Optional::NO,
                  "The label to assign to the address."},
             },
             RPCResults{},
             RPCExamples{
                 HelpExampleCli(
                     "setlabel",
                     "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"tabby\"") +
                 HelpExampleRpc(
                     "setlabel",
                     "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"tabby\"")},
         }
                                      .ToString());
     }
     LOCK(pwallet->cs_wallet);
 
     CTxDestination dest =
         DecodeDestination(request.params[0].get_str(), config.GetChainParams());
     if (!IsValidDestination(dest)) {
         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
                            "Invalid Bitcoin address");
     }
 
     std::string old_label = pwallet->mapAddressBook[dest].name;
     std::string label = LabelFromValue(request.params[1]);
 
     if (IsMine(*pwallet, dest)) {
         pwallet->SetAddressBook(dest, label, "receive");
     } else {
         pwallet->SetAddressBook(dest, label, "send");
     }
 
     return NullUniValue;
 }
 
 static CTransactionRef SendMoney(interfaces::Chain::Lock &locked_chain,
                                  CWallet *const pwallet,
                                  const CTxDestination &address, Amount nValue,
                                  bool fSubtractFeeFromAmount,
                                  const CCoinControl &coin_control,
                                  mapValue_t mapValue) {
     Amount curBalance = pwallet->GetBalance().m_mine_trusted;
 
     // Check amount
     if (nValue <= Amount::zero()) {
         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount");
     }
 
     if (nValue > curBalance) {
         throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
     }
 
     if (pwallet->GetBroadcastTransactions() && !pwallet->chain().p2pEnabled()) {
         throw JSONRPCError(
             RPC_CLIENT_P2P_DISABLED,
             "Error: Peer-to-peer functionality missing or disabled");
     }
 
     // Parse Bitcoin address
     CScript scriptPubKey = GetScriptForDestination(address);
 
     // Create and send the transaction
     CReserveKey reservekey(pwallet);
     Amount nFeeRequired;
     std::string strError;
     std::vector<CRecipient> vecSend;
     int nChangePosRet = -1;
     CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
     vecSend.push_back(recipient);
 
     CCoinControl coinControl;
     CTransactionRef tx;
     if (!pwallet->CreateTransaction(locked_chain, vecSend, tx, reservekey,
                                     nFeeRequired, nChangePosRet, strError,
                                     coinControl)) {
         if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) {
             strError = strprintf("Error: This transaction requires a "
                                  "transaction fee of at least %s",
                                  FormatMoney(nFeeRequired));
         }
         throw JSONRPCError(RPC_WALLET_ERROR, strError);
     }
     CValidationState state;
     if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */,
                                     reservekey, state)) {
         strError =
             strprintf("Error: The transaction was rejected! Reason given: %s",
                       FormatStateMessage(state));
         throw JSONRPCError(RPC_WALLET_ERROR, strError);
     }
     return tx;
 }
 
 static UniValue sendtoaddress(const Config &config,
                               const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() < 2 ||
         request.params.size() > 5) {
         throw std::runtime_error(RPCHelpMan{
             "sendtoaddress",
             "\nSend an amount to a given address.\n" +
                 HelpRequiringPassphrase(pwallet) + "\n",
             {
                 {"address", RPCArg::Type::STR, RPCArg::Optional::NO,
                  "The bitcoin address to send to."},
                 {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO,
                  "The amount in " + CURRENCY_UNIT + " to send. eg 0.1"},
                 {"comment", RPCArg::Type::STR,
                  RPCArg::Optional::OMITTED_NAMED_ARG,
                  "A comment used to store what the transaction is for.\n"
                  "                             This is not part of the "
                  "transaction, just kept in your wallet."},
                 {"comment_to", RPCArg::Type::STR,
                  RPCArg::Optional::OMITTED_NAMED_ARG,
                  "A comment to store the name of the person or organization\n"
                  "                             to which you're sending the "
                  "transaction. This is not part of the \n"
                  "                             transaction, just kept in "
                  "your wallet."},
                 {"subtractfeefromamount", RPCArg::Type::BOOL,
                  /* default */ "false",
                  "The fee will be deducted from the amount being sent.\n"
                  "                             The recipient will receive "
                  "less bitcoins than you enter in the amount field."},
             },
             RPCResult{
                 "\"txid\"                  (string) The transaction id.\n"},
             RPCExamples{
                 HelpExampleCli("sendtoaddress",
                                "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") +
                 HelpExampleCli("sendtoaddress",
                                "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvay"
                                "dd\" 0.1 \"donation\" \"seans "
                                "outpost\"") +
                 HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44"
                                                 "Jvaydd\" 0.1 \"\" \"\" true") +
                 HelpExampleRpc("sendtoaddress",
                                "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvay"
                                "dd\", 0.1, \"donation\", \"seans "
                                "outpost\"")},
         }
                                      .ToString());
     }
 
     // Make sure the results are valid at least up to the most recent block
     // the user could have gotten from another RPC command prior to now
     pwallet->BlockUntilSyncedToCurrentChain();
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     CTxDestination dest =
         DecodeDestination(request.params[0].get_str(), config.GetChainParams());
     if (!IsValidDestination(dest)) {
         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
     }
 
     // Amount
     Amount nAmount = AmountFromValue(request.params[1]);
     if (nAmount <= Amount::zero()) {
         throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
     }
 
     // Wallet comments
     mapValue_t mapValue;
     if (!request.params[2].isNull() && !request.params[2].get_str().empty()) {
         mapValue["comment"] = request.params[2].get_str();
     }
     if (!request.params[3].isNull() && !request.params[3].get_str().empty()) {
         mapValue["to"] = request.params[3].get_str();
     }
 
     bool fSubtractFeeFromAmount = false;
     if (!request.params[4].isNull()) {
         fSubtractFeeFromAmount = request.params[4].get_bool();
     }
 
     CCoinControl coin_control;
 
     EnsureWalletIsUnlocked(pwallet);
 
     CTransactionRef tx =
         SendMoney(*locked_chain, pwallet, dest, nAmount, fSubtractFeeFromAmount,
                   coin_control, std::move(mapValue));
     return tx->GetId().GetHex();
 }
 
 static UniValue listaddressgroupings(const Config &config,
                                      const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() != 0) {
         throw std::runtime_error(RPCHelpMan{
             "listaddressgroupings",
             "\nLists groups of addresses which have had their "
             "common ownership\n"
             "made public by common use as inputs or as the "
             "resulting change\n"
             "in past transactions\n",
             {},
             RPCResult{
                 "[\n"
                 "  [\n"
                 "    [\n"
                 "      \"address\",            (string) The bitcoin address\n"
                 "      amount,                 (numeric) The amount in " +
                 CURRENCY_UNIT +
                 "\n"
                 "      \"label\"               (string, optional) The label\n"
                 "    ]\n"
                 "    ,...\n"
                 "  ]\n"
                 "  ,...\n"
                 "]\n"},
             RPCExamples{HelpExampleCli("listaddressgroupings", "") +
                         HelpExampleRpc("listaddressgroupings", "")},
         }
                                      .ToString());
     }
 
     // Make sure the results are valid at least up to the most recent block
     // the user could have gotten from another RPC command prior to now
     pwallet->BlockUntilSyncedToCurrentChain();
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     UniValue jsonGroupings(UniValue::VARR);
     std::map<CTxDestination, Amount> balances =
         pwallet->GetAddressBalances(*locked_chain);
     for (const std::set<CTxDestination> &grouping :
          pwallet->GetAddressGroupings()) {
         UniValue jsonGrouping(UniValue::VARR);
         for (const CTxDestination &address : grouping) {
             UniValue addressInfo(UniValue::VARR);
             addressInfo.push_back(EncodeDestination(address, config));
             addressInfo.push_back(ValueFromAmount(balances[address]));
 
             if (pwallet->mapAddressBook.find(address) !=
                 pwallet->mapAddressBook.end()) {
                 addressInfo.push_back(
                     pwallet->mapAddressBook.find(address)->second.name);
             }
             jsonGrouping.push_back(addressInfo);
         }
         jsonGroupings.push_back(jsonGrouping);
     }
 
     return jsonGroupings;
 }
 
 static UniValue signmessage(const Config &config,
                             const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() != 2) {
         throw std::runtime_error(RPCHelpMan{
             "signmessage",
             "\nSign a message with the private key of an address" +
                 HelpRequiringPassphrase(pwallet) + "\n",
             {
                 {"address", RPCArg::Type::STR, RPCArg::Optional::NO,
                  "The bitcoin address to use for the private key."},
                 {"message", RPCArg::Type::STR, RPCArg::Optional::NO,
                  "The message to create a signature of."},
             },
             RPCResult{
                 "\"signature\"          (string) The signature of the message "
                 "encoded in base 64\n"},
             RPCExamples{
                 "\nUnlock the wallet for 30 seconds\n" +
                 HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") +
                 "\nCreate the signature\n" +
                 HelpExampleCli(
                     "signmessage",
                     "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") +
                 "\nVerify the signature\n" +
                 HelpExampleCli("verifymessage",
                                "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4"
                                "XX\" \"signature\" \"my "
                                "message\"") +
                 "\nAs a JSON-RPC call\n" +
                 HelpExampleRpc(
                     "signmessage",
                     "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\"")},
         }
                                      .ToString());
     }
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     EnsureWalletIsUnlocked(pwallet);
 
     std::string strAddress = request.params[0].get_str();
     std::string strMessage = request.params[1].get_str();
 
     CTxDestination dest =
         DecodeDestination(strAddress, config.GetChainParams());
     if (!IsValidDestination(dest)) {
         throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
     }
 
     const PKHash *pkhash = boost::get<PKHash>(&dest);
     if (!pkhash) {
         throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
     }
 
     CKey key;
     CKeyID keyID(*pkhash);
     if (!pwallet->GetKey(keyID, key)) {
         throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
     }
 
     CHashWriter ss(SER_GETHASH, 0);
     ss << strMessageMagic;
     ss << strMessage;
 
     std::vector<uint8_t> vchSig;
     if (!key.SignCompact(ss.GetHash(), vchSig)) {
         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
     }
 
     return EncodeBase64(vchSig.data(), vchSig.size());
 }
 
 static UniValue getreceivedbyaddress(const Config &config,
                                      const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() < 1 ||
         request.params.size() > 2) {
         throw std::runtime_error(RPCHelpMan{
             "getreceivedbyaddress",
             "\nReturns the total amount received by the given address in "
             "transactions with at least minconf confirmations.\n",
             {
                 {"address", RPCArg::Type::STR, RPCArg::Optional::NO,
                  "The bitcoin address for transactions."},
                 {"minconf", RPCArg::Type::NUM, /* default */ "1",
                  "Only include transactions confirmed at least this many "
                  "times."},
             },
             RPCResult{"amount   (numeric) The total amount in " +
                       CURRENCY_UNIT + " received at this address.\n"},
             RPCExamples{
                 "\nThe amount from transactions with at least 1 "
                 "confirmation\n" +
                 HelpExampleCli("getreceivedbyaddress",
                                "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") +
                 "\nThe amount including unconfirmed transactions, zero "
                 "confirmations\n" +
                 HelpExampleCli("getreceivedbyaddress",
                                "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 0") +
                 "\nThe amount with at least 6 confirmations\n" +
                 HelpExampleCli("getreceivedbyaddress",
                                "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 6") +
                 "\nAs a JSON-RPC call\n" +
                 HelpExampleRpc("getreceivedbyaddress",
                                "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6")},
         }
                                      .ToString());
     }
 
     // Make sure the results are valid at least up to the most recent block
     // the user could have gotten from another RPC command prior to now
     pwallet->BlockUntilSyncedToCurrentChain();
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     // Bitcoin address
     CTxDestination dest =
         DecodeDestination(request.params[0].get_str(), config.GetChainParams());
     if (!IsValidDestination(dest)) {
         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
                            "Invalid Bitcoin address");
     }
     CScript scriptPubKey = GetScriptForDestination(dest);
     if (!IsMine(*pwallet, scriptPubKey)) {
         throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet");
     }
 
     // Minimum confirmations
     int nMinDepth = 1;
     if (!request.params[1].isNull()) {
         nMinDepth = request.params[1].get_int();
     }
 
     // Tally
     Amount nAmount = Amount::zero();
     for (const std::pair<const TxId, CWalletTx> &pairWtx : pwallet->mapWallet) {
         const CWalletTx &wtx = pairWtx.second;
 
         CValidationState state;
         if (wtx.IsCoinBase() ||
             !locked_chain->contextualCheckTransactionForCurrentBlock(
                 config.GetChainParams().GetConsensus(), *wtx.tx, state)) {
             continue;
         }
 
         for (const CTxOut &txout : wtx.tx->vout) {
             if (txout.scriptPubKey == scriptPubKey) {
                 if (wtx.GetDepthInMainChain(*locked_chain) >= nMinDepth) {
                     nAmount += txout.nValue;
                 }
             }
         }
     }
 
     return ValueFromAmount(nAmount);
 }
 
 static UniValue getreceivedbylabel(const Config &config,
                                    const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() < 1 ||
         request.params.size() > 2) {
         throw std::runtime_error(RPCHelpMan{
             "getreceivedbylabel",
             "\nReturns the total amount received by addresses with <label> "
             "in transactions with at least [minconf] confirmations.\n",
             {
                 {"label", RPCArg::Type::STR, RPCArg::Optional::NO,
                  "The selected label, may be the default label "
                  "using \"\"."},
                 {"minconf", RPCArg::Type::NUM, /* default */ "1",
                  "Only include transactions confirmed at least this "
                  "many times."},
             },
             RPCResult{"amount              (numeric) The total amount in " +
                       CURRENCY_UNIT + " received for this label.\n"},
             RPCExamples{
                 "\nAmount received by the default label with at least 1 "
                 "confirmation\n" +
                 HelpExampleCli("getreceivedbylabel", "\"\"") +
                 "\nAmount received at the tabby label including unconfirmed "
                 "amounts with zero confirmations\n" +
                 HelpExampleCli("getreceivedbylabel", "\"tabby\" 0") +
                 "\nThe amount with at least 6 confirmations\n" +
                 HelpExampleCli("getreceivedbylabel", "\"tabby\" 6") +
                 "\nAs a JSON-RPC call\n" +
                 HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6")},
         }
                                      .ToString());
     }
 
     // Make sure the results are valid at least up to the most recent block
     // the user could have gotten from another RPC command prior to now
     pwallet->BlockUntilSyncedToCurrentChain();
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     // Minimum confirmations
     int nMinDepth = 1;
     if (!request.params[1].isNull()) {
         nMinDepth = request.params[1].get_int();
     }
 
     // Get the set of pub keys assigned to label
     std::string label = LabelFromValue(request.params[0]);
     std::set<CTxDestination> setAddress = pwallet->GetLabelAddresses(label);
 
     // Tally
     Amount nAmount = Amount::zero();
     for (const std::pair<const TxId, CWalletTx> &pairWtx : pwallet->mapWallet) {
         const CWalletTx &wtx = pairWtx.second;
         CValidationState state;
         if (wtx.IsCoinBase() ||
             !locked_chain->contextualCheckTransactionForCurrentBlock(
                 config.GetChainParams().GetConsensus(), *wtx.tx, state)) {
             continue;
         }
 
         for (const CTxOut &txout : wtx.tx->vout) {
             CTxDestination address;
             if (ExtractDestination(txout.scriptPubKey, address) &&
                 IsMine(*pwallet, address) && setAddress.count(address)) {
                 if (wtx.GetDepthInMainChain(*locked_chain) >= nMinDepth) {
                     nAmount += txout.nValue;
                 }
             }
         }
     }
 
     return ValueFromAmount(nAmount);
 }
 
 static UniValue getbalance(const Config &config,
                            const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || (request.params.size() > 3)) {
         throw std::runtime_error(RPCHelpMan{
             "getbalance",
             "\nReturns the total available balance.\n"
             "The available balance is what the wallet considers "
             "currently spendable, and is\n"
             "thus affected by options which limit spendability such "
             "as -spendzeroconfchange.\n",
             {
                 {"dummy", RPCArg::Type::STR,
                  RPCArg::Optional::OMITTED_NAMED_ARG,
                  "Remains for backward compatibility. Must be excluded or set "
                  "to \"*\"."},
                 {"minconf", RPCArg::Type::NUM, /* default */ "0",
                  "Only include transactions confirmed at least this "
                  "many times."},
                 {"include_watchonly", RPCArg::Type::BOOL,
                  /* default */ "false",
                  "Also include balance in watch-only addresses (see "
                  "'importaddress')"},
             },
             RPCResult{"amount              (numeric) The total amount in " +
                       CURRENCY_UNIT + " received for this wallet.\n"},
             RPCExamples{"\nThe total amount in the wallet with 1 or more "
                         "confirmations\n" +
                         HelpExampleCli("getbalance", "") +
                         "\nThe total amount in the wallet at least 6 blocks "
                         "confirmed\n" +
                         HelpExampleCli("getbalance", "\"*\" 6") +
                         "\nAs a JSON-RPC call\n" +
                         HelpExampleRpc("getbalance", "\"*\", 6")},
         }
                                      .ToString());
     }
 
     // Make sure the results are valid at least up to the most recent block
     // the user could have gotten from another RPC command prior to now
     pwallet->BlockUntilSyncedToCurrentChain();
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     const UniValue &dummy_value = request.params[0];
     if (!dummy_value.isNull() && dummy_value.get_str() != "*") {
         throw JSONRPCError(
             RPC_METHOD_DEPRECATED,
             "dummy first argument must be excluded or set to \"*\".");
     }
 
     int min_depth = 0;
     if (!request.params[1].isNull()) {
         min_depth = request.params[1].get_int();
     }
 
     bool include_watchonly = false;
     if (!request.params[2].isNull() && request.params[2].get_bool()) {
         include_watchonly = true;
     }
 
     const auto bal = pwallet->GetBalance(min_depth);
 
     return ValueFromAmount(bal.m_mine_trusted + (include_watchonly
                                                      ? bal.m_watchonly_trusted
                                                      : Amount::zero()));
 }
 
 static UniValue getunconfirmedbalance(const Config &config,
                                       const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() > 0) {
         throw std::runtime_error(RPCHelpMan{
             "getunconfirmedbalance",
             "Returns the server's total unconfirmed balance\n",
             {},
             RPCResults{},
             RPCExamples{""},
         }
                                      .ToString());
     }
 
     // Make sure the results are valid at least up to the most recent block
     // the user could have gotten from another RPC command prior to now
     pwallet->BlockUntilSyncedToCurrentChain();
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     return ValueFromAmount(pwallet->GetBalance().m_mine_untrusted_pending);
 }
 
 static UniValue sendmany(const Config &config, const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     const RPCHelpMan help{
         "sendmany",
         "\nSend multiple times. Amounts are double-precision "
         "floating point numbers." +
             HelpRequiringPassphrase(pwallet) + "\n",
         {
             {"dummy", RPCArg::Type::STR, RPCArg::Optional::NO,
              "Must be set to \"\" for backwards compatibility.", "\"\""},
             {
                 "amounts",
                 RPCArg::Type::OBJ,
                 RPCArg::Optional::NO,
                 "A json object with addresses and amounts",
                 {
                     {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO,
                      "The bitcoin address is the key, the numeric amount (can "
                      "be string) in " +
                          CURRENCY_UNIT + " is the value"},
                 },
             },
             {"minconf", RPCArg::Type::NUM, /* default */ "1",
              "Only use the balance confirmed at least this many times."},
             {"comment", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG,
              "A comment"},
             {
                 "subtractfeefrom",
                 RPCArg::Type::ARR,
                 RPCArg::Optional::OMITTED_NAMED_ARG,
                 "A json array with addresses.\n"
                 "                           The fee will be equally deducted "
                 "from the amount of each selected address.\n"
                 "                           Those recipients will receive less "
                 "bitcoins than you enter in their corresponding amount field.\n"
                 "                           If no addresses are specified "
                 "here, the sender pays the fee.",
                 {
                     {"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED,
                      "Subtract fee from this address"},
                 },
             },
         },
         RPCResult{
             "\"txid\"                   (string) The transaction id for the "
             "send. Only 1 transaction is created regardless of \n"
             "                                    the number of addresses.\n"},
         RPCExamples{
             "\nSend two amounts to two different addresses:\n" +
             HelpExampleCli(
                 "sendmany",
                 "\"\" "
                 "\"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,"
                 "\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"") +
             "\nSend two amounts to two different addresses setting the "
             "confirmation and comment:\n" +
             HelpExampleCli("sendmany",
                            "\"\" "
                            "\"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,"
                            "\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" "
                            "6 \"testing\"") +
             "\nSend two amounts to two different addresses, subtract fee "
             "from amount:\n" +
             HelpExampleCli(
                 "sendmany",
                 "\"\" "
                 "\"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,"
                 "\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" "
                 "\"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\","
                 "\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") +
             "\nAs a JSON-RPC call\n" +
             HelpExampleRpc("sendmany",
                            "\"\", "
                            "\"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,"
                            "\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\","
                            " 6, \"testing\"")},
     };
 
     if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
         throw std::runtime_error(help.ToString());
     }
 
     // Make sure the results are valid at least up to the most recent block
     // the user could have gotten from another RPC command prior to now
     pwallet->BlockUntilSyncedToCurrentChain();
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     if (pwallet->GetBroadcastTransactions() && !pwallet->chain().p2pEnabled()) {
         throw JSONRPCError(
             RPC_CLIENT_P2P_DISABLED,
             "Error: Peer-to-peer functionality missing or disabled");
     }
 
     if (!request.params[0].isNull() && !request.params[0].get_str().empty()) {
         throw JSONRPCError(RPC_INVALID_PARAMETER,
                            "Dummy value must be set to \"\"");
     }
     UniValue sendTo = request.params[1].get_obj();
 
     mapValue_t mapValue;
     if (!request.params[3].isNull() && !request.params[3].get_str().empty()) {
         mapValue["comment"] = request.params[3].get_str();
     }
 
     UniValue subtractFeeFromAmount(UniValue::VARR);
     if (!request.params[4].isNull()) {
         subtractFeeFromAmount = request.params[4].get_array();
     }
 
     std::set<CTxDestination> destinations;
     std::vector<CRecipient> vecSend;
 
     std::vector<std::string> keys = sendTo.getKeys();
     for (const std::string &name_ : keys) {
         CTxDestination dest = DecodeDestination(name_, config.GetChainParams());
         if (!IsValidDestination(dest)) {
             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
                                std::string("Invalid Bitcoin address: ") +
                                    name_);
         }
 
         if (destinations.count(dest)) {
             throw JSONRPCError(
                 RPC_INVALID_PARAMETER,
                 std::string("Invalid parameter, duplicated address: ") + name_);
         }
         destinations.insert(dest);
 
         CScript scriptPubKey = GetScriptForDestination(dest);
         Amount nAmount = AmountFromValue(sendTo[name_]);
         if (nAmount <= Amount::zero()) {
             throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
         }
 
         bool fSubtractFeeFromAmount = false;
         for (size_t idx = 0; idx < subtractFeeFromAmount.size(); idx++) {
             const UniValue &addr = subtractFeeFromAmount[idx];
             if (addr.get_str() == name_) {
                 fSubtractFeeFromAmount = true;
             }
         }
 
         CRecipient recipient = {scriptPubKey, nAmount, fSubtractFeeFromAmount};
         vecSend.push_back(recipient);
     }
 
     EnsureWalletIsUnlocked(pwallet);
 
     // Shuffle recipient list
     std::shuffle(vecSend.begin(), vecSend.end(), FastRandomContext());
 
     // Send
     CReserveKey keyChange(pwallet);
     Amount nFeeRequired = Amount::zero();
     int nChangePosRet = -1;
     std::string strFailReason;
     CTransactionRef tx;
     CCoinControl coinControl;
     bool fCreated = pwallet->CreateTransaction(
         *locked_chain, vecSend, tx, keyChange, nFeeRequired, nChangePosRet,
         strFailReason, coinControl);
     if (!fCreated) {
         throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
     }
     CValidationState state;
     if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */,
                                     keyChange, state)) {
         strFailReason = strprintf("Transaction commit failed:: %s",
                                   FormatStateMessage(state));
         throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
     }
 
     return tx->GetId().GetHex();
 }
 
 static UniValue addmultisigaddress(const Config &config,
                                    const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() < 2 ||
         request.params.size() > 3) {
         std::string msg =
             RPCHelpMan{
                 "addmultisigaddress",
                 "\nAdd a nrequired-to-sign multisignature address to the "
                 "wallet. "
                 "Requires a new wallet backup.\n"
                 "Each key is a Bitcoin address or hex-encoded public key.\n"
                 "If 'label' is specified (DEPRECATED), assign address to that "
                 "label.\n",
                 {
                     {"nrequired", RPCArg::Type::NUM, RPCArg::Optional::NO,
                      "The number of required signatures out of the n keys or "
                      "addresses."},
                     {
                         "keys",
                         RPCArg::Type::ARR,
                         RPCArg::Optional::NO,
                         "A json array of bitcoin addresses or hex-encoded "
                         "public keys",
                         {
                             {"key", RPCArg::Type::STR,
                              RPCArg::Optional::OMITTED,
                              "bitcoin address or hex-encoded public key"},
                         },
                     },
                     {"label", RPCArg::Type::STR,
                      RPCArg::Optional::OMITTED_NAMED_ARG,
                      "A label to assign the addresses to."},
                 },
                 RPCResult{"{\n"
                           "  \"address\":\"multisigaddress\",    (string) The "
                           "value of the new multisig address.\n"
                           "  \"redeemScript\":\"script\"         (string) The "
                           "string value of the hex-encoded redemption script.\n"
                           "}\n"},
                 RPCExamples{
                     "\nAdd a multisig address from 2 addresses\n" +
                     HelpExampleCli(
                         "addmultisigaddress",
                         "2 "
                         "\"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\","
                         "\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") +
                     "\nAs a JSON-RPC call\n" +
                     HelpExampleRpc(
                         "addmultisigaddress",
                         "2, "
                         "\"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\","
                         "\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"")},
             }
                 .ToString();
         throw std::runtime_error(msg);
     }
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     std::string label;
     if (!request.params[2].isNull()) {
         label = LabelFromValue(request.params[2]);
     }
 
     int required = request.params[0].get_int();
 
     // Get the public keys
     const UniValue &keys_or_addrs = request.params[1].get_array();
     std::vector<CPubKey> pubkeys;
     for (size_t i = 0; i < keys_or_addrs.size(); ++i) {
         if (IsHex(keys_or_addrs[i].get_str()) &&
             (keys_or_addrs[i].get_str().length() == 66 ||
              keys_or_addrs[i].get_str().length() == 130)) {
             pubkeys.push_back(HexToPubKey(keys_or_addrs[i].get_str()));
         } else {
             pubkeys.push_back(AddrToPubKey(config.GetChainParams(), pwallet,
                                            keys_or_addrs[i].get_str()));
         }
     }
 
     OutputType output_type = pwallet->m_default_address_type;
 
     // Construct using pay-to-script-hash:
     CScript inner = CreateMultisigRedeemscript(required, pubkeys);
     CTxDestination dest =
         AddAndGetDestinationForScript(*pwallet, inner, output_type);
     pwallet->SetAddressBook(dest, label, "send");
 
     UniValue result(UniValue::VOBJ);
     result.pushKV("address", EncodeDestination(dest, config));
     result.pushKV("redeemScript", HexStr(inner.begin(), inner.end()));
     return result;
 }
 
 struct tallyitem {
     Amount nAmount{Amount::zero()};
     int nConf{std::numeric_limits<int>::max()};
     std::vector<uint256> txids;
     bool fIsWatchonly{false};
     tallyitem() {}
 };
 
 static UniValue
 ListReceived(const Config &config, interfaces::Chain::Lock &locked_chain,
              CWallet *const pwallet, const UniValue &params, bool by_label)
     EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
     // Minimum confirmations
     int nMinDepth = 1;
     if (!params[0].isNull()) {
         nMinDepth = params[0].get_int();
     }
 
     // Whether to include empty labels
     bool fIncludeEmpty = false;
     if (!params[1].isNull()) {
         fIncludeEmpty = params[1].get_bool();
     }
 
     isminefilter filter = ISMINE_SPENDABLE;
     if (!params[2].isNull() && params[2].get_bool()) {
         filter = filter | ISMINE_WATCH_ONLY;
     }
 
     bool has_filtered_address = false;
     CTxDestination filtered_address = CNoDestination();
     if (!by_label && params.size() > 3) {
         if (!IsValidDestinationString(params[3].get_str(),
                                       config.GetChainParams())) {
             throw JSONRPCError(RPC_WALLET_ERROR,
                                "address_filter parameter was invalid");
         }
         filtered_address =
             DecodeDestination(params[3].get_str(), config.GetChainParams());
         has_filtered_address = true;
     }
 
     // Tally
     std::map<CTxDestination, tallyitem> mapTally;
     for (const std::pair<const TxId, CWalletTx> &pairWtx : pwallet->mapWallet) {
         const CWalletTx &wtx = pairWtx.second;
 
         CValidationState state;
         if (wtx.IsCoinBase() ||
             !locked_chain.contextualCheckTransactionForCurrentBlock(
                 config.GetChainParams().GetConsensus(), *wtx.tx, state)) {
             continue;
         }
 
         int nDepth = wtx.GetDepthInMainChain(locked_chain);
         if (nDepth < nMinDepth) {
             continue;
         }
 
         for (const CTxOut &txout : wtx.tx->vout) {
             CTxDestination address;
             if (!ExtractDestination(txout.scriptPubKey, address)) {
                 continue;
             }
 
             if (has_filtered_address && !(filtered_address == address)) {
                 continue;
             }
 
             isminefilter mine = IsMine(*pwallet, address);
             if (!(mine & filter)) {
                 continue;
             }
 
             tallyitem &item = mapTally[address];
             item.nAmount += txout.nValue;
             item.nConf = std::min(item.nConf, nDepth);
             item.txids.push_back(wtx.GetId());
             if (mine & ISMINE_WATCH_ONLY) {
                 item.fIsWatchonly = true;
             }
         }
     }
 
     // Reply
     UniValue ret(UniValue::VARR);
     std::map<std::string, tallyitem> label_tally;
 
     // Create mapAddressBook iterator
     // If we aren't filtering, go from begin() to end()
     auto start = pwallet->mapAddressBook.begin();
     auto end = pwallet->mapAddressBook.end();
     // If we are filtering, find() the applicable entry
     if (has_filtered_address) {
         start = pwallet->mapAddressBook.find(filtered_address);
         if (start != end) {
             end = std::next(start);
         }
     }
 
     for (auto item_it = start; item_it != end; ++item_it) {
         const CTxDestination &address = item_it->first;
         const std::string &label = item_it->second.name;
         std::map<CTxDestination, tallyitem>::iterator it =
             mapTally.find(address);
         if (it == mapTally.end() && !fIncludeEmpty) {
             continue;
         }
 
         Amount nAmount = Amount::zero();
         int nConf = std::numeric_limits<int>::max();
         bool fIsWatchonly = false;
         if (it != mapTally.end()) {
             nAmount = (*it).second.nAmount;
             nConf = (*it).second.nConf;
             fIsWatchonly = (*it).second.fIsWatchonly;
         }
 
         if (by_label) {
             tallyitem &_item = label_tally[label];
             _item.nAmount += nAmount;
             _item.nConf = std::min(_item.nConf, nConf);
             _item.fIsWatchonly = fIsWatchonly;
         } else {
             UniValue obj(UniValue::VOBJ);
             if (fIsWatchonly) {
                 obj.pushKV("involvesWatchonly", true);
             }
             obj.pushKV("address", EncodeDestination(address, config));
             obj.pushKV("amount", ValueFromAmount(nAmount));
             obj.pushKV("confirmations",
                        (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
             obj.pushKV("label", label);
             UniValue transactions(UniValue::VARR);
             if (it != mapTally.end()) {
                 for (const uint256 &_item : (*it).second.txids) {
                     transactions.push_back(_item.GetHex());
                 }
             }
             obj.pushKV("txids", transactions);
             ret.push_back(obj);
         }
     }
 
     if (by_label) {
         for (const auto &entry : label_tally) {
             Amount nAmount = entry.second.nAmount;
             int nConf = entry.second.nConf;
             UniValue obj(UniValue::VOBJ);
             if (entry.second.fIsWatchonly) {
                 obj.pushKV("involvesWatchonly", true);
             }
             obj.pushKV("amount", ValueFromAmount(nAmount));
             obj.pushKV("confirmations",
                        (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
             obj.pushKV("label", entry.first);
             ret.push_back(obj);
         }
     }
 
     return ret;
 }
 
 static UniValue listreceivedbyaddress(const Config &config,
                                       const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() > 4) {
         throw std::runtime_error(RPCHelpMan{
             "listreceivedbyaddress",
             "\nList balances by receiving address.\n",
             {
                 {"minconf", RPCArg::Type::NUM, /* default */ "1",
                  "The minimum number of confirmations before payments are "
                  "included."},
                 {"include_empty", RPCArg::Type::BOOL, /* default */ "false",
                  "Whether to include addresses that haven't received any "
                  "payments."},
                 {"include_watchonly", RPCArg::Type::BOOL, /* default */ "false",
                  "Whether to include watch-only addresses (see "
                  "'importaddress')."},
                 {"address_filter", RPCArg::Type::STR,
                  RPCArg::Optional::OMITTED_NAMED_ARG,
                  "If present, only return information on this address."},
             },
             RPCResult{
                 "[\n"
                 "  {\n"
                 "    \"involvesWatchonly\" : true,        (bool) Only returned "
                 "if imported addresses were involved in transaction\n"
                 "    \"address\" : \"receivingaddress\",  (string) The "
                 "receiving address\n"
                 "    \"amount\" : x.xxx,                  (numeric) The total "
                 "amount in " +
                 CURRENCY_UNIT +
                 " received by the address\n"
                 "    \"confirmations\" : n,               (numeric) The number "
                 "of confirmations of the most recent transaction included\n"
                 "    \"label\" : \"label\",               (string) The label "
                 "of the receiving address. The default label is \"\".\n"
                 "    \"txids\": [\n"
                 "       \"txid\",                         (string) The ids of "
                 "transactions received with the address \n"
                 "       ...\n"
                 "    ]\n"
                 "  }\n"
                 "  ,...\n"
                 "]\n"},
             RPCExamples{
                 HelpExampleCli("listreceivedbyaddress", "") +
                 HelpExampleCli("listreceivedbyaddress", "6 true") +
                 HelpExampleRpc("listreceivedbyaddress", "6, true, true") +
                 HelpExampleRpc(
                     "listreceivedbyaddress",
                     "6, true, true, \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\"")},
         }
                                      .ToString());
     }
 
     // Make sure the results are valid at least up to the most recent block
     // the user could have gotten from another RPC command prior to now
     pwallet->BlockUntilSyncedToCurrentChain();
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     return ListReceived(config, *locked_chain, pwallet, request.params, false);
 }
 
 static UniValue listreceivedbylabel(const Config &config,
                                     const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() > 3) {
         throw std::runtime_error(RPCHelpMan{
             "listreceivedbylabel",
             "\nList received transactions by label.\n",
             {
                 {"minconf", RPCArg::Type::NUM, /* default */ "1",
                  "The minimum number of confirmations before "
                  "payments are included."},
                 {"include_empty", RPCArg::Type::BOOL, /* default */ "false",
                  "Whether to include labels that haven't received "
                  "any payments."},
                 {"include_watchonly", RPCArg::Type::BOOL,
                  /* default */ "false",
                  "Whether to include watch-only addresses (see "
                  "'importaddress')."},
             },
             RPCResult{
                 "[\n"
                 "  {\n"
                 "    \"involvesWatchonly\" : true,   (bool) Only returned if "
                 "imported addresses were involved in transaction\n"
                 "    \"amount\" : x.xxx,             (numeric) The total "
                 "amount received by addresses with this label\n"
                 "    \"confirmations\" : n,          (numeric) The number of "
                 "confirmations of the most recent transaction included\n"
                 "    \"label\" : \"label\"           (string) The label of the "
                 "receiving address. The default label is \"\".\n"
                 "  }\n"
                 "  ,...\n"
                 "]\n"},
             RPCExamples{HelpExampleCli("listreceivedbylabel", "") +
                         HelpExampleCli("listreceivedbylabel", "6 true") +
                         HelpExampleRpc("listreceivedbylabel", "6, true, true")},
         }
                                      .ToString());
     }
 
     // Make sure the results are valid at least up to the most recent block
     // the user could have gotten from another RPC command prior to now
     pwallet->BlockUntilSyncedToCurrentChain();
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     return ListReceived(config, *locked_chain, pwallet, request.params, true);
 }
 
 static void MaybePushAddress(UniValue &entry, const CTxDestination &dest) {
     if (IsValidDestination(dest)) {
         entry.pushKV("address", EncodeDestination(dest, GetConfig()));
     }
 }
 
 /**
  * List transactions based on the given criteria.
  *
  * @param  pwallet        The wallet.
  * @param  wtx            The wallet transaction.
  * @param  nMinDepth      The minimum confirmation depth.
  * @param  fLong          Whether to include the JSON version of the
  * transaction.
  * @param  ret            The UniValue into which the result is stored.
  * @param  filter_ismine  The "is mine" filter flags.
  * @param  filter_label   Optional label string to filter incoming transactions.
  */
 static void ListTransactions(interfaces::Chain::Lock &locked_chain,
                              CWallet *const pwallet, const CWalletTx &wtx,
                              int nMinDepth, bool fLong, UniValue &ret,
                              const isminefilter &filter_ismine,
                              const std::string *filter_label)
     EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
     Amount nFee;
     std::list<COutputEntry> listReceived;
     std::list<COutputEntry> listSent;
 
     wtx.GetAmounts(listReceived, listSent, nFee, filter_ismine);
 
     bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY);
 
     // Sent
     if (!filter_label) {
         for (const COutputEntry &s : listSent) {
             UniValue entry(UniValue::VOBJ);
             if (involvesWatchonly ||
                 (::IsMine(*pwallet, s.destination) & ISMINE_WATCH_ONLY)) {
                 entry.pushKV("involvesWatchonly", true);
             }
             MaybePushAddress(entry, s.destination);
             entry.pushKV("category", "send");
             entry.pushKV("amount", ValueFromAmount(-s.amount));
             if (pwallet->mapAddressBook.count(s.destination)) {
                 entry.pushKV("label",
                              pwallet->mapAddressBook[s.destination].name);
             }
             entry.pushKV("vout", s.vout);
             entry.pushKV("fee", ValueFromAmount(-1 * nFee));
             if (fLong) {
                 WalletTxToJSON(pwallet->chain(), locked_chain, wtx, entry);
             }
             entry.pushKV("abandoned", wtx.isAbandoned());
             ret.push_back(entry);
         }
     }
 
     // Received
     if (listReceived.size() > 0 &&
         wtx.GetDepthInMainChain(locked_chain) >= nMinDepth) {
         for (const COutputEntry &r : listReceived) {
             std::string label;
             if (pwallet->mapAddressBook.count(r.destination)) {
                 label = pwallet->mapAddressBook[r.destination].name;
             }
             if (filter_label && label != *filter_label) {
                 continue;
             }
             UniValue entry(UniValue::VOBJ);
             if (involvesWatchonly ||
                 (::IsMine(*pwallet, r.destination) & ISMINE_WATCH_ONLY)) {
                 entry.pushKV("involvesWatchonly", true);
             }
             MaybePushAddress(entry, r.destination);
             if (wtx.IsCoinBase()) {
                 if (wtx.GetDepthInMainChain(locked_chain) < 1) {
                     entry.pushKV("category", "orphan");
                 } else if (wtx.IsImmatureCoinBase(locked_chain)) {
                     entry.pushKV("category", "immature");
                 } else {
                     entry.pushKV("category", "generate");
                 }
             } else {
                 entry.pushKV("category", "receive");
             }
             entry.pushKV("amount", ValueFromAmount(r.amount));
             if (pwallet->mapAddressBook.count(r.destination)) {
                 entry.pushKV("label", label);
             }
             entry.pushKV("vout", r.vout);
             if (fLong) {
                 WalletTxToJSON(pwallet->chain(), locked_chain, wtx, entry);
             }
             ret.push_back(entry);
         }
     }
 }
 
 UniValue listtransactions(const Config &config, const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() > 4) {
         throw std::runtime_error(RPCHelpMan{
             "listtransactions",
             "\nIf a label name is provided, this will return only incoming "
             "transactions paying to addresses with the specified label.\n"
             "\nReturns up to 'count' most recent transactions skipping the "
             "first 'from' transactions.\n",
             {
                 {"label", RPCArg::Type::STR,
                  RPCArg::Optional::OMITTED_NAMED_ARG,
                  "If set, should be a valid label name to return only incoming "
                  "transactions with the specified label, or \"*\" to disable "
                  "filtering and return all transactions."},
                 {"count", RPCArg::Type::NUM, /* default */ "10",
                  "The number of transactions to return"},
                 {"skip", RPCArg::Type::NUM, /* default */ "0",
                  "The number of transactions to skip"},
                 {"include_watchonly", RPCArg::Type::BOOL, /* default */ "false",
                  "Include transactions to watch-only addresses (see "
                  "'importaddress')"},
             },
             RPCResult{
                 "[\n"
                 "  {\n"
                 "    \"address\":\"address\",    (string) The bitcoin address "
                 "of the transaction.\n"
                 "    \"category\":\"send|receive\", (string) The transaction "
                 "category.\n"
                 "    \"amount\": x.xxx,          (numeric) The amount in " +
                 CURRENCY_UNIT +
                 ". This is negative for the 'send' category, and is positive\n"
                 "                                        for the 'receive' "
                 "category,\n"
                 "    \"label\": \"label\",       (string) A comment for the "
                 "address/transaction, if any\n"
                 "    \"vout\": n,                (numeric) the vout value\n"
                 "    \"fee\": x.xxx,             (numeric) The amount of the "
                 "fee in " +
                 CURRENCY_UNIT +
                 ". This is negative and only available for the \n"
                 "                                         'send' category of "
                 "transactions.\n"
                 "    \"confirmations\": n,       (numeric) The number of "
                 "confirmations for the transaction. Negative confirmations "
                 "indicate the\n"
                 "                                         transaction "
                 "conflicts with the block chain\n"
                 "    \"trusted\": xxx,           (bool) Whether we consider "
                 "the outputs of this unconfirmed transaction safe to spend.\n"
                 "    \"blockhash\": \"hashvalue\", (string) The block hash "
                 "containing the transaction.\n"
                 "    \"blockindex\": n,          (numeric) The index of the "
                 "transaction in the block that includes it.\n"
                 "    \"blocktime\": xxx,         (numeric) The block time in "
                 "seconds since epoch (1 Jan 1970 GMT).\n"
                 "    \"txid\": \"transactionid\", (string) The transaction "
                 "id.\n"
                 "    \"time\": xxx,              (numeric) The transaction "
                 "time in seconds since epoch (midnight Jan 1 1970 GMT).\n"
                 "    \"timereceived\": xxx,      (numeric) The time received "
                 "in seconds since epoch (midnight Jan 1 1970 GMT).\n"
                 "    \"comment\": \"...\",       (string) If a comment is "
                 "associated with the transaction.\n"
                 "    \"abandoned\": xxx          (bool) 'true' if the "
                 "transaction has been abandoned (inputs are respendable). Only "
                 "available for the \n"
                 "                                         'send' category of "
                 "transactions.\n"
                 "  }\n"
                 "]\n"},
             RPCExamples{
                 "\nList the most recent 10 transactions in the systems\n" +
                 HelpExampleCli("listtransactions", "") +
                 "\nList transactions 100 to 120\n" +
                 HelpExampleCli("listtransactions", "\"*\" 20 100") +
                 "\nAs a JSON-RPC call\n" +
                 HelpExampleRpc("listtransactions", "\"*\", 20, 100")},
         }
                                      .ToString());
     }
 
     // Make sure the results are valid at least up to the most recent block
     // the user could have gotten from another RPC command prior to now
     pwallet->BlockUntilSyncedToCurrentChain();
 
     const std::string *filter_label = nullptr;
     if (!request.params[0].isNull() && request.params[0].get_str() != "*") {
         filter_label = &request.params[0].get_str();
         if (filter_label->empty()) {
             throw JSONRPCError(
                 RPC_INVALID_PARAMETER,
                 "Label argument must be a valid label name or \"*\".");
         }
     }
     int nCount = 10;
     if (!request.params[1].isNull()) {
         nCount = request.params[1].get_int();
     }
 
     int nFrom = 0;
     if (!request.params[2].isNull()) {
         nFrom = request.params[2].get_int();
     }
 
     isminefilter filter = ISMINE_SPENDABLE;
     if (!request.params[3].isNull() && request.params[3].get_bool()) {
         filter = filter | ISMINE_WATCH_ONLY;
     }
 
     if (nCount < 0) {
         throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
     }
     if (nFrom < 0) {
         throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
     }
     UniValue ret(UniValue::VARR);
 
     {
         auto locked_chain = pwallet->chain().lock();
         LOCK(pwallet->cs_wallet);
 
         const CWallet::TxItems &txOrdered = pwallet->wtxOrdered;
 
         // iterate backwards until we have nCount items to return:
         for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin();
              it != txOrdered.rend(); ++it) {
             CWalletTx *const pwtx = (*it).second;
             ListTransactions(*locked_chain, pwallet, *pwtx, 0, true, ret,
                              filter, filter_label);
             if (int(ret.size()) >= (nCount + nFrom)) {
                 break;
             }
         }
     }
 
     // ret is newest to oldest
 
     if (nFrom > (int)ret.size()) {
         nFrom = ret.size();
     }
     if ((nFrom + nCount) > (int)ret.size()) {
         nCount = ret.size() - nFrom;
     }
 
     std::vector<UniValue> arrTmp = ret.getValues();
 
     std::vector<UniValue>::iterator first = arrTmp.begin();
     std::advance(first, nFrom);
     std::vector<UniValue>::iterator last = arrTmp.begin();
     std::advance(last, nFrom + nCount);
 
     if (last != arrTmp.end()) {
         arrTmp.erase(last, arrTmp.end());
     }
     if (first != arrTmp.begin()) {
         arrTmp.erase(arrTmp.begin(), first);
     }
 
     // Return oldest to newest
     std::reverse(arrTmp.begin(), arrTmp.end());
 
     ret.clear();
     ret.setArray();
     ret.push_backV(arrTmp);
 
     return ret;
 }
 
 static UniValue listsinceblock(const Config &config,
                                const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() > 4) {
         throw std::runtime_error(RPCHelpMan{
             "listsinceblock",
             "\nGet all transactions in blocks since block [blockhash], or all "
             "transactions if omitted.\n"
             "If \"blockhash\" is no longer a part of the main chain, "
             "transactions from the fork point onward are included.\n"
             "Additionally, if include_removed is set, transactions affecting "
             "the wallet which were removed are returned in the \"removed\" "
             "array.\n",
             {
                 {"blockhash", RPCArg::Type::STR,
                  RPCArg::Optional::OMITTED_NAMED_ARG,
                  "If set, the block hash to list transactions since, otherwise "
                  "list all transactions."},
                 {"target_confirmations", RPCArg::Type::NUM, /* default */ "1",
                  "Return the nth block hash from the main chain. e.g. 1 "
                  "would mean the best block hash. Note: this is not used "
                  "as a filter, but only affects [lastblock] in the return "
                  "value"},
                 {"include_watchonly", RPCArg::Type::BOOL, /* default */ "false",
                  "Include transactions to watch-only addresses (see "
                  "'importaddress')"},
                 {"include_removed", RPCArg::Type::BOOL, /* default */ "true",
                  "Show transactions that were removed due to a reorg in "
                  "the \"removed\" array\n"
                  "                                                         "
                  "  (not guaranteed to work on pruned nodes)"},
             },
             RPCResult{
                 "{\n"
                 "  \"transactions\": [\n"
                 "    \"address\":\"address\",    (string) The bitcoin address "
                 "of the transaction. Not present for move transactions "
                 "(category = move).\n"
                 "    \"category\":\"send|receive\",     (string) The "
                 "transaction category. 'send' has negative amounts, 'receive' "
                 "has positive amounts.\n"
                 "    \"amount\": x.xxx,          (numeric) The amount in " +
                 CURRENCY_UNIT +
                 ". This is negative for the 'send' category, and for the "
                 "'move' category for moves \n"
                 "                                          outbound. It is "
                 "positive for the 'receive' category, and for the 'move' "
                 "category for inbound funds.\n"
                 "    \"vout\" : n,               (numeric) the vout value\n"
                 "    \"fee\": x.xxx,             (numeric) The amount of the "
                 "fee in " +
                 CURRENCY_UNIT +
                 ". This is negative and only available for the 'send' category "
                 "of transactions.\n"
                 "    \"confirmations\": n,       (numeric) The number of "
                 "confirmations for the transaction. Available for 'send' and "
                 "'receive' category of transactions.\n"
                 "                                          When it's < 0, it "
                 "means the transaction conflicted that many blocks ago.\n"
                 "    \"blockhash\": \"hashvalue\",     (string) The block hash "
                 "containing the transaction. Available for 'send' and "
                 "'receive' category of transactions.\n"
                 "    \"blockindex\": n,          (numeric) The index of the "
                 "transaction in the block that includes it. Available for "
                 "'send' and 'receive' category of transactions.\n"
                 "    \"blocktime\": xxx,         (numeric) The block time in "
                 "seconds since epoch (1 Jan 1970 GMT).\n"
                 "    \"txid\": \"transactionid\",  (string) The transaction "
                 "id. Available for 'send' and 'receive' category of "
                 "transactions.\n"
                 "    \"time\": xxx,              (numeric) The transaction "
                 "time in seconds since epoch (Jan 1 1970 GMT).\n"
                 "    \"timereceived\": xxx,      (numeric) The time received "
                 "in seconds since epoch (Jan 1 1970 GMT). Available for 'send' "
                 "and 'receive' category of transactions.\n"
                 "    \"abandoned\": xxx,         (bool) 'true' if the "
                 "transaction has been abandoned (inputs are respendable). Only "
                 "available for the 'send' category of transactions.\n"
                 "    \"comment\": \"...\",       (string) If a comment is "
                 "associated with the transaction.\n"
                 "    \"label\" : \"label\"       (string) A comment for the "
                 "address/transaction, if any\n"
                 "    \"to\": \"...\",            (string) If a comment to is "
                 "associated with the transaction.\n"
                 "  ],\n"
                 "  \"removed\": [\n"
                 "    <structure is the same as \"transactions\" above, only "
                 "present if include_removed=true>\n"
                 "    Note: transactions that were re-added in the active chain "
                 "will appear as-is in this array, and may thus have a positive "
                 "confirmation count.\n"
                 "  ],\n"
                 "  \"lastblock\": \"lastblockhash\"     (string) The hash of "
                 "the block (target_confirmations-1) from the best block on the "
                 "main chain. This is typically used to feed back into "
                 "listsinceblock the next time you call it. So you would "
                 "generally use a target_confirmations of say 6, so you will be "
                 "continually re-notified of transactions until they've reached "
                 "6 confirmations plus any new ones\n"
                 "}\n"},
             RPCExamples{HelpExampleCli("listsinceblock", "") +
                         HelpExampleCli("listsinceblock",
                                        "\"000000000000000bacf66f7497b7dc4"
                                        "5ef753ee9a7d38571037cdb1a57f663ad"
                                        "\" 6") +
                         HelpExampleRpc("listsinceblock",
                                        "\"000000000000000bacf66f7497b7dc4"
                                        "5ef753ee9a7d38571037cdb1a57f663ad"
                                        "\", 6")},
         }
                                      .ToString());
     }
 
     // Make sure the results are valid at least up to the most recent block
     // the user could have gotten from another RPC command prior to now
     pwallet->BlockUntilSyncedToCurrentChain();
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     // The way the 'height' is initialized is just a workaround for the gcc bug
     // #47679 since version 4.6.0. Height of the specified block or the common
     // ancestor, if the block provided was in a deactivated chain.
     Optional<int> height = MakeOptional(false, int());
 
     // Height of the specified block, even if it's in a deactivated chain.
     Optional<int> altheight;
     int target_confirms = 1;
     isminefilter filter = ISMINE_SPENDABLE;
 
     BlockHash blockId;
     if (!request.params[0].isNull() && !request.params[0].get_str().empty()) {
         blockId = BlockHash(ParseHashV(request.params[0], "blockhash"));
         height = locked_chain->findFork(blockId, &altheight);
         if (!height) {
             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
         }
     }
 
     if (!request.params[1].isNull()) {
         target_confirms = request.params[1].get_int();
 
         if (target_confirms < 1) {
             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
         }
     }
 
     if (!request.params[2].isNull() && request.params[2].get_bool()) {
         filter = filter | ISMINE_WATCH_ONLY;
     }
 
     bool include_removed =
         (request.params[3].isNull() || request.params[3].get_bool());
 
     const Optional<int> tip_height = locked_chain->getHeight();
     int depth = tip_height && height ? (1 + *tip_height - *height) : -1;
 
     UniValue transactions(UniValue::VARR);
 
     for (const std::pair<const TxId, CWalletTx> &pairWtx : pwallet->mapWallet) {
         CWalletTx tx = pairWtx.second;
 
         if (depth == -1 || tx.GetDepthInMainChain(*locked_chain) < depth) {
             ListTransactions(*locked_chain, pwallet, tx, 0, true, transactions,
                              filter, nullptr /* filter_label */);
         }
     }
 
     // when a reorg'd block is requested, we also list any relevant transactions
     // in the blocks of the chain that was detached
     UniValue removed(UniValue::VARR);
     while (include_removed && altheight && *altheight > *height) {
         CBlock block;
         if (!pwallet->chain().findBlock(blockId, &block) || block.IsNull()) {
             throw JSONRPCError(RPC_INTERNAL_ERROR,
                                "Can't read block from disk");
         }
         for (const CTransactionRef &tx : block.vtx) {
             auto it = pwallet->mapWallet.find(tx->GetId());
             if (it != pwallet->mapWallet.end()) {
                 // We want all transactions regardless of confirmation count to
                 // appear here, even negative confirmation ones, hence the big
                 // negative.
                 ListTransactions(*locked_chain, pwallet, it->second, -100000000,
                                  true, removed, filter,
                                  nullptr /* filter_label */);
             }
         }
         blockId = block.hashPrevBlock;
         --*altheight;
     }
 
     int last_height = tip_height ? *tip_height + 1 - target_confirms : -1;
     BlockHash lastblock = last_height >= 0
                               ? locked_chain->getBlockHash(last_height)
                               : BlockHash();
 
     UniValue ret(UniValue::VOBJ);
     ret.pushKV("transactions", transactions);
     if (include_removed) {
         ret.pushKV("removed", removed);
     }
     ret.pushKV("lastblock", lastblock.GetHex());
 
     return ret;
 }
 
 static UniValue gettransaction(const Config &config,
                                const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() < 1 ||
         request.params.size() > 2) {
         throw std::runtime_error(RPCHelpMan{
             "gettransaction",
             "\nGet detailed information about in-wallet transaction "
             "<txid>\n",
             {
                 {"txid", RPCArg::Type::STR, RPCArg::Optional::NO,
                  "The transaction id"},
                 {"include_watchonly", RPCArg::Type::BOOL,
                  /* default */ "false",
                  "Whether to include watch-only addresses in "
                  "balance calculation and details[]"},
             },
             RPCResult{
                 "{\n"
                 "  \"amount\" : x.xxx,        (numeric) The transaction amount "
                 "in " +
                 CURRENCY_UNIT +
                 "\n"
                 "  \"fee\": x.xxx,            (numeric) The amount of the fee "
                 "in " +
                 CURRENCY_UNIT +
                 ". This is negative and only available for the \n"
                 "                              'send' category of "
                 "transactions.\n"
                 "  \"confirmations\" : n,     (numeric) The number of "
                 "confirmations\n"
                 "  \"blockhash\" : \"hash\",  (string) The block hash\n"
                 "  \"blockindex\" : xx,       (numeric) The index of the "
                 "transaction in the block that includes it\n"
                 "  \"blocktime\" : ttt,       (numeric) The time in seconds "
                 "since epoch (1 Jan 1970 GMT)\n"
                 "  \"txid\" : \"transactionid\",   (string) The transaction "
                 "id.\n"
                 "  \"time\" : ttt,            (numeric) The transaction time "
                 "in seconds since epoch (1 Jan 1970 GMT)\n"
                 "  \"timereceived\" : ttt,    (numeric) The time received in "
                 "seconds since epoch (1 Jan 1970 GMT)\n"
                 "  \"bip125-replaceable\": \"yes|no|unknown\",  (string) "
                 "Whether this transaction could be replaced due to BIP125 "
                 "(replace-by-fee);\n"
                 "                                                   may be "
                 "unknown for unconfirmed transactions not in the mempool\n"
                 "  \"details\" : [\n"
                 "    {\n"
                 "      \"address\" : \"address\",          (string) The "
                 "bitcoin address involved in the transaction\n"
                 "      \"category\" : \"send|receive\",    (string) The "
                 "category, either 'send' or 'receive'\n"
                 "      \"amount\" : x.xxx,                 (numeric) The "
                 "amount in " +
                 CURRENCY_UNIT +
                 "\n"
                 "      \"label\" : \"label\",              (string) A comment "
                 "for the address/transaction, if any\n"
                 "      \"vout\" : n,                       (numeric) the vout "
                 "value\n"
                 "      \"fee\": x.xxx,                     (numeric) The "
                 "amount of the fee in " +
                 CURRENCY_UNIT +
                 ". This is negative and only available for the \n"
                 "                                           'send' category of "
                 "transactions.\n"
                 "      \"abandoned\": xxx                  (bool) 'true' if "
                 "the transaction has been abandoned (inputs are respendable). "
                 "Only available for the \n"
                 "                                           'send' category of "
                 "transactions.\n"
                 "    }\n"
                 "    ,...\n"
                 "  ],\n"
                 "  \"hex\" : \"data\"         (string) Raw data for "
                 "transaction\n"
                 "}\n"},
             RPCExamples{HelpExampleCli("gettransaction",
                                        "\"1075db55d416d3ca199f55b6084e211"
                                        "5b9345e16c5cf302fc80e9d5fbf5d48d"
                                        "\"") +
                         HelpExampleCli("gettransaction",
                                        "\"1075db55d416d3ca199f55b6084e211"
                                        "5b9345e16c5cf302fc80e9d5fbf5d48d"
                                        "\" true") +
                         HelpExampleRpc("gettransaction",
                                        "\"1075db55d416d3ca199f55b6084e211"
                                        "5b9345e16c5cf302fc80e9d5fbf5d48d"
                                        "\"")},
         }
                                      .ToString());
     }
 
     // Make sure the results are valid at least up to the most recent block
     // the user could have gotten from another RPC command prior to now
     pwallet->BlockUntilSyncedToCurrentChain();
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     TxId txid(ParseHashV(request.params[0], "txid"));
 
     isminefilter filter = ISMINE_SPENDABLE;
     if (!request.params[1].isNull() && request.params[1].get_bool()) {
         filter = filter | ISMINE_WATCH_ONLY;
     }
 
     UniValue entry(UniValue::VOBJ);
     auto it = pwallet->mapWallet.find(txid);
     if (it == pwallet->mapWallet.end()) {
         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
                            "Invalid or non-wallet transaction id");
     }
     const CWalletTx &wtx = it->second;
 
     Amount nCredit = wtx.GetCredit(*locked_chain, filter);
     Amount nDebit = wtx.GetDebit(filter);
     Amount nNet = nCredit - nDebit;
     Amount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit
                                         : Amount::zero());
 
     entry.pushKV("amount", ValueFromAmount(nNet - nFee));
     if (wtx.IsFromMe(filter)) {
         entry.pushKV("fee", ValueFromAmount(nFee));
     }
 
     WalletTxToJSON(pwallet->chain(), *locked_chain, wtx, entry);
 
     UniValue details(UniValue::VARR);
     ListTransactions(*locked_chain, pwallet, wtx, 0, false, details, filter,
                      nullptr /* filter_label */);
     entry.pushKV("details", details);
 
     std::string strHex =
         EncodeHexTx(*wtx.tx, pwallet->chain().rpcSerializationFlags());
     entry.pushKV("hex", strHex);
 
     return entry;
 }
 
 static UniValue abandontransaction(const Config &config,
                                    const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() != 1) {
         throw std::runtime_error(RPCHelpMan{
             "abandontransaction",
             "\nMark in-wallet transaction <txid> as abandoned\n"
             "This will mark this transaction and all its in-wallet descendants "
             "as abandoned which will allow\n"
             "for their inputs to be respent.  It can be used to replace "
             "\"stuck\" or evicted transactions.\n"
             "It only works on transactions which are not included in a block "
             "and are not currently in the mempool.\n"
             "It has no effect on transactions which are already abandoned.\n",
             {
                 {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO,
                  "The transaction id"},
             },
             RPCResults{},
             RPCExamples{HelpExampleCli("abandontransaction",
                                        "\"1075db55d416d3ca199f55b6084"
                                        "e2115b9345e16c5cf302fc80e9d5f"
                                        "bf5d48d\"") +
                         HelpExampleRpc("abandontransaction",
                                        "\"1075db55d416d3ca199f55b6084"
                                        "e2115b9345e16c5cf302fc80e9d5f"
                                        "bf5d48d\"")},
         }
                                      .ToString());
     }
 
     // Make sure the results are valid at least up to the most recent block
     // the user could have gotten from another RPC command prior to now
     pwallet->BlockUntilSyncedToCurrentChain();
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     TxId txid(ParseHashV(request.params[0], "txid"));
 
     if (!pwallet->mapWallet.count(txid)) {
         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
                            "Invalid or non-wallet transaction id");
     }
 
     if (!pwallet->AbandonTransaction(*locked_chain, txid)) {
         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
                            "Transaction not eligible for abandonment");
     }
 
     return NullUniValue;
 }
 
 static UniValue backupwallet(const Config &config,
                              const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() != 1) {
         throw std::runtime_error(RPCHelpMan{
             "backupwallet",
             "\nSafely copies current wallet file to destination, "
             "which can be a directory or a path with filename.\n",
             {
                 {"destination", RPCArg::Type::STR, RPCArg::Optional::NO,
                  "The destination directory or file"},
             },
             RPCResults{},
             RPCExamples{HelpExampleCli("backupwallet", "\"backup.dat\"") +
                         HelpExampleRpc("backupwallet", "\"backup.dat\"")},
         }
                                      .ToString());
     }
 
     // Make sure the results are valid at least up to the most recent block
     // the user could have gotten from another RPC command prior to now
     pwallet->BlockUntilSyncedToCurrentChain();
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     std::string strDest = request.params[0].get_str();
     if (!pwallet->BackupWallet(strDest)) {
         throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
     }
 
     return NullUniValue;
 }
 
 static UniValue keypoolrefill(const Config &config,
                               const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() > 1) {
         throw std::runtime_error(RPCHelpMan{
             "keypoolrefill",
             "\nFills the keypool." + HelpRequiringPassphrase(pwallet) + "\n",
             {
                 {"newsize", RPCArg::Type::NUM, /* default */ "100",
                  "The new keypool size"},
             },
             RPCResults{},
             RPCExamples{HelpExampleCli("keypoolrefill", "") +
                         HelpExampleRpc("keypoolrefill", "")},
         }
                                      .ToString());
     }
 
     if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
         throw JSONRPCError(RPC_WALLET_ERROR,
                            "Error: Private keys are disabled for this wallet");
     }
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     // 0 is interpreted by TopUpKeyPool() as the default keypool size given by
     // -keypool
     unsigned int kpSize = 0;
     if (!request.params[0].isNull()) {
         if (request.params[0].get_int() < 0) {
             throw JSONRPCError(RPC_INVALID_PARAMETER,
                                "Invalid parameter, expected valid size.");
         }
         kpSize = (unsigned int)request.params[0].get_int();
     }
 
     EnsureWalletIsUnlocked(pwallet);
     pwallet->TopUpKeyPool(kpSize);
 
     if (pwallet->GetKeyPoolSize() < kpSize) {
         throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
     }
 
     return NullUniValue;
 }
 
 static UniValue walletpassphrase(const Config &config,
                                  const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 2)) {
         throw std::runtime_error(RPCHelpMan{
             "walletpassphrase",
             "\nStores the wallet decryption key in memory for 'timeout' "
             "seconds.\n"
             "This is needed prior to performing transactions related to "
             "private keys such as sending bitcoins\n"
             "\nNote:\n"
             "Issuing the walletpassphrase command while the wallet is already "
             "unlocked will set a new unlock\n"
             "time that overrides the old one.\n",
             {
                 {"passphrase", RPCArg::Type::STR, RPCArg::Optional::NO,
                  "The wallet passphrase"},
                 {"timeout", RPCArg::Type::NUM, RPCArg::Optional::NO,
                  "The time to keep the decryption key in seconds; capped "
                  "at 100000000 (~3 years)."},
             },
             RPCResults{},
             RPCExamples{
                 "\nUnlock the wallet for 60 seconds\n" +
                 HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") +
                 "\nLock the wallet again (before 60 seconds)\n" +
                 HelpExampleCli("walletlock", "") + "\nAs a JSON-RPC call\n" +
                 HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60")},
         }
                                      .ToString());
     }
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     if (request.fHelp) {
         return true;
     }
 
     if (!pwallet->IsCrypted()) {
         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE,
                            "Error: running with an unencrypted wallet, but "
                            "walletpassphrase was called.");
     }
 
     // Note that the walletpassphrase is stored in request.params[0] which is
     // not mlock()ed
     SecureString strWalletPass;
     strWalletPass.reserve(100);
     // TODO: get rid of this .c_str() by implementing
     // SecureString::operator=(std::string)
     // Alternately, find a way to make request.params[0] mlock()'d to begin
     // with.
     strWalletPass = request.params[0].get_str().c_str();
 
     // Get the timeout
     int64_t nSleepTime = request.params[1].get_int64();
     // Timeout cannot be negative, otherwise it will relock immediately
     if (nSleepTime < 0) {
         throw JSONRPCError(RPC_INVALID_PARAMETER,
                            "Timeout cannot be negative.");
     }
     // Clamp timeout
     // larger values trigger a macos/libevent bug?
     constexpr int64_t MAX_SLEEP_TIME = 100000000;
     if (nSleepTime > MAX_SLEEP_TIME) {
         nSleepTime = MAX_SLEEP_TIME;
     }
 
     if (strWalletPass.empty()) {
         throw JSONRPCError(RPC_INVALID_PARAMETER,
                            "passphrase can not be empty");
     }
 
     if (!pwallet->Unlock(strWalletPass)) {
         throw JSONRPCError(
             RPC_WALLET_PASSPHRASE_INCORRECT,
             "Error: The wallet passphrase entered was incorrect.");
     }
 
     pwallet->TopUpKeyPool();
 
     pwallet->nRelockTime = GetTime() + nSleepTime;
 
     // Keep a weak pointer to the wallet so that it is possible to unload the
     // wallet before the following callback is called. If a valid shared pointer
     // is acquired in the callback then the wallet is still loaded.
     std::weak_ptr<CWallet> weak_wallet = wallet;
     pwallet->chain().rpcRunLater(
         strprintf("lockwallet(%s)", pwallet->GetName()),
         [weak_wallet] {
             if (auto shared_wallet = weak_wallet.lock()) {
                 LOCK(shared_wallet->cs_wallet);
                 shared_wallet->Lock();
                 shared_wallet->nRelockTime = 0;
             }
         },
         nSleepTime);
 
     return NullUniValue;
 }
 
 static UniValue walletpassphrasechange(const Config &config,
                                        const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 2)) {
         throw std::runtime_error(RPCHelpMan{
             "walletpassphrasechange",
             "\nChanges the wallet passphrase from 'oldpassphrase' to "
             "'newpassphrase'.\n",
             {
                 {"oldpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO,
                  "The current passphrase"},
                 {"newpassphrase", RPCArg::Type::STR, RPCArg::Optional::NO,
                  "The new passphrase"},
             },
             RPCResults{},
             RPCExamples{HelpExampleCli("walletpassphrasechange",
                                        "\"old one\" \"new one\"") +
                         HelpExampleRpc("walletpassphrasechange",
                                        "\"old one\", \"new one\"")},
         }
                                      .ToString());
     }
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     if (request.fHelp) {
         return true;
     }
     if (!pwallet->IsCrypted()) {
         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE,
                            "Error: running with an unencrypted wallet, but "
                            "walletpassphrasechange was called.");
     }
 
     // TODO: get rid of these .c_str() calls by implementing
     // SecureString::operator=(std::string)
     // Alternately, find a way to make request.params[0] mlock()'d to begin
     // with.
     SecureString strOldWalletPass;
     strOldWalletPass.reserve(100);
     strOldWalletPass = request.params[0].get_str().c_str();
 
     SecureString strNewWalletPass;
     strNewWalletPass.reserve(100);
     strNewWalletPass = request.params[1].get_str().c_str();
 
     if (strOldWalletPass.empty() || strNewWalletPass.empty()) {
         throw JSONRPCError(RPC_INVALID_PARAMETER,
                            "passphrase can not be empty");
     }
 
     if (!pwallet->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) {
         throw JSONRPCError(
             RPC_WALLET_PASSPHRASE_INCORRECT,
             "Error: The wallet passphrase entered was incorrect.");
     }
 
     return NullUniValue;
 }
 
 static UniValue walletlock(const Config &config,
                            const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 0)) {
         throw std::runtime_error(RPCHelpMan{
             "walletlock",
             "\nRemoves the wallet encryption key from memory, locking the "
             "wallet.\n"
             "After calling this method, you will need to call walletpassphrase "
             "again\n"
             "before being able to call any methods which require the wallet to "
             "be unlocked.\n",
             {},
             RPCResults{},
             RPCExamples{
                 "\nSet the passphrase for 2 minutes to perform a "
                 "transaction\n" +
                 HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") +
                 "\nPerform a send (requires passphrase set)\n" +
                 HelpExampleCli("sendtoaddress",
                                "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 1.0") +
                 "\nClear the passphrase since we are done before 2 minutes is "
                 "up\n" +
                 HelpExampleCli("walletlock", "") + "\nAs a JSON-RPC call\n" +
                 HelpExampleRpc("walletlock", "")},
         }
                                      .ToString());
     }
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     if (request.fHelp) {
         return true;
     }
     if (!pwallet->IsCrypted()) {
         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE,
                            "Error: running with an unencrypted wallet, but "
                            "walletlock was called.");
     }
 
     pwallet->Lock();
     pwallet->nRelockTime = 0;
 
     return NullUniValue;
 }
 
 static UniValue encryptwallet(const Config &config,
                               const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (!pwallet->IsCrypted() &&
         (request.fHelp || request.params.size() != 1)) {
         throw std::runtime_error(RPCHelpMan{
             "encryptwallet",
             "\nEncrypts the wallet with 'passphrase'. This is for first time "
             "encryption.\n"
             "After this, any calls that interact with private keys such as "
             "sending or signing \n"
             "will require the passphrase to be set prior the making these "
             "calls.\n"
             "Use the walletpassphrase call for this, and then walletlock "
             "call.\n"
             "If the wallet is already encrypted, use the "
             "walletpassphrasechange call.\n",
             {
                 {"passphrase", RPCArg::Type::STR, RPCArg::Optional::NO,
                  "The pass phrase to encrypt the wallet with. It must be "
                  "at least 1 character, but should be long."},
             },
             RPCResults{},
             RPCExamples{
                 "\nEncrypt your wallet\n" +
                 HelpExampleCli("encryptwallet", "\"my pass phrase\"") +
                 "\nNow set the passphrase to use the wallet, such as for "
                 "signing or sending bitcoin\n" +
                 HelpExampleCli("walletpassphrase", "\"my pass phrase\"") +
                 "\nNow we can do something like sign\n" +
                 HelpExampleCli("signmessage", "\"address\" \"test message\"") +
                 "\nNow lock the wallet again by removing the passphrase\n" +
                 HelpExampleCli("walletlock", "") + "\nAs a JSON-RPC call\n" +
                 HelpExampleRpc("encryptwallet", "\"my pass phrase\"")},
         }
                                      .ToString());
     }
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     if (request.fHelp) {
         return true;
     }
     if (pwallet->IsCrypted()) {
         throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE,
                            "Error: running with an encrypted wallet, but "
                            "encryptwallet was called.");
     }
 
     // TODO: get rid of this .c_str() by implementing
     // SecureString::operator=(std::string)
     // Alternately, find a way to make request.params[0] mlock()'d to begin
     // with.
     SecureString strWalletPass;
     strWalletPass.reserve(100);
     strWalletPass = request.params[0].get_str().c_str();
 
     if (strWalletPass.empty()) {
         throw JSONRPCError(RPC_INVALID_PARAMETER,
                            "passphrase can not be empty");
     }
 
     if (!pwallet->EncryptWallet(strWalletPass)) {
         throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED,
                            "Error: Failed to encrypt the wallet.");
     }
 
     return "wallet encrypted; The keypool has been flushed and a new HD seed "
            "was generated (if you are using HD). You need to make a new "
            "backup.";
 }
 
 static UniValue lockunspent(const Config &config,
                             const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() < 1 ||
         request.params.size() > 2) {
         throw std::runtime_error(RPCHelpMan{
             "lockunspent",
             "\nUpdates list of temporarily unspendable outputs.\n"
             "Temporarily lock (unlock=false) or unlock (unlock=true) specified "
             "transaction outputs.\n"
             "If no transaction outputs are specified when unlocking then all "
             "current locked transaction outputs are unlocked.\n"
             "A locked transaction output will not be chosen by automatic coin "
             "selection, when spending bitcoins.\n"
             "Locks are stored in memory only. Nodes start with zero locked "
             "outputs, and the locked output list\n"
             "is always cleared (by virtue of process exit) when a node stops "
             "or fails.\n"
             "Also see the listunspent call\n",
             {
                 {"unlock", RPCArg::Type::BOOL, RPCArg::Optional::NO,
                  "Whether to unlock (true) or lock (false) the specified "
                  "transactions"},
                 {
                     "transactions",
                     RPCArg::Type::ARR,
                     /* default */ "empty array",
                     "A json array of objects. Each object the txid (string) "
                     "vout (numeric).",
                     {
                         {
                             "",
                             RPCArg::Type::OBJ,
                             RPCArg::Optional::OMITTED,
                             "",
                             {
                                 {"txid", RPCArg::Type::STR_HEX,
                                  RPCArg::Optional::NO, "The transaction id"},
                                 {"vout", RPCArg::Type::NUM,
                                  RPCArg::Optional::NO, "The output number"},
                             },
                         },
                     },
                 },
             },
             RPCResult{"true|false    (boolean) Whether the command was "
                       "successful or not\n"},
             RPCExamples{"\nList the unspent transactions\n" +
                         HelpExampleCli("listunspent", "") +
                         "\nLock an unspent transaction\n" +
                         HelpExampleCli("lockunspent",
                                        "false "
                                        "\"[{\\\"txid\\\":"
                                        "\\\"a08e6907dbbd3d809776dbfc5d82e371"
                                        "b764ed838b5655e72f463568df1aadf0\\\""
                                        ",\\\"vout\\\":1}]\"") +
                         "\nList the locked transactions\n" +
                         HelpExampleCli("listlockunspent", "") +
                         "\nUnlock the transaction again\n" +
                         HelpExampleCli("lockunspent",
                                        "true "
                                        "\"[{\\\"txid\\\":"
                                        "\\\"a08e6907dbbd3d809776dbfc5d82e371"
                                        "b764ed838b5655e72f463568df1aadf0\\\""
                                        ",\\\"vout\\\":1}]\"") +
                         "\nAs a JSON-RPC call\n" +
                         HelpExampleRpc("lockunspent",
                                        "false, "
                                        "\"[{\\\"txid\\\":"
                                        "\\\"a08e6907dbbd3d809776dbfc5d82e371"
                                        "b764ed838b5655e72f463568df1aadf0\\\""
                                        ",\\\"vout\\\":1}]\"")},
         }
                                      .ToString());
     }
 
     // Make sure the results are valid at least up to the most recent block
     // the user could have gotten from another RPC command prior to now
     pwallet->BlockUntilSyncedToCurrentChain();
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     RPCTypeCheckArgument(request.params[0], UniValue::VBOOL);
 
     bool fUnlock = request.params[0].get_bool();
 
     if (request.params[1].isNull()) {
         if (fUnlock) {
             pwallet->UnlockAllCoins();
         }
         return true;
     }
 
     RPCTypeCheckArgument(request.params[1], UniValue::VARR);
 
     const UniValue &output_params = request.params[1];
 
     // Create and validate the COutPoints first.
 
     std::vector<COutPoint> outputs;
     outputs.reserve(output_params.size());
 
     for (size_t idx = 0; idx < output_params.size(); idx++) {
         const UniValue &o = output_params[idx].get_obj();
 
         RPCTypeCheckObj(o, {
                                {"txid", UniValueType(UniValue::VSTR)},
                                {"vout", UniValueType(UniValue::VNUM)},
                            });
 
         const int nOutput = find_value(o, "vout").get_int();
         if (nOutput < 0) {
             throw JSONRPCError(RPC_INVALID_PARAMETER,
                                "Invalid parameter, vout must be positive");
         }
 
         const TxId txid(ParseHashO(o, "txid"));
         const auto it = pwallet->mapWallet.find(txid);
         if (it == pwallet->mapWallet.end()) {
             throw JSONRPCError(RPC_INVALID_PARAMETER,
                                "Invalid parameter, unknown transaction");
         }
 
         const COutPoint output(txid, nOutput);
         const CWalletTx &trans = it->second;
         if (output.GetN() >= trans.tx->vout.size()) {
             throw JSONRPCError(RPC_INVALID_PARAMETER,
                                "Invalid parameter, vout index out of bounds");
         }
 
         if (pwallet->IsSpent(*locked_chain, output)) {
             throw JSONRPCError(RPC_INVALID_PARAMETER,
                                "Invalid parameter, expected unspent output");
         }
 
         const bool is_locked = pwallet->IsLockedCoin(output);
         if (fUnlock && !is_locked) {
             throw JSONRPCError(RPC_INVALID_PARAMETER,
                                "Invalid parameter, expected locked output");
         }
 
         if (!fUnlock && is_locked) {
             throw JSONRPCError(RPC_INVALID_PARAMETER,
                                "Invalid parameter, output already locked");
         }
 
         outputs.push_back(output);
     }
 
     // Atomically set (un)locked status for the outputs.
     for (const COutPoint &output : outputs) {
         if (fUnlock) {
             pwallet->UnlockCoin(output);
         } else {
             pwallet->LockCoin(output);
         }
     }
 
     return true;
 }
 
 static UniValue listlockunspent(const Config &config,
                                 const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() > 0) {
         throw std::runtime_error(RPCHelpMan{
             "listlockunspent",
             "\nReturns list of temporarily unspendable outputs.\n"
             "See the lockunspent call to lock and unlock transactions for "
             "spending.\n",
             {},
             RPCResult{"[\n"
                       "  {\n"
                       "    \"txid\" : \"transactionid\",     (string) The "
                       "transaction id locked\n"
                       "    \"vout\" : n                      (numeric) The "
                       "vout value\n"
                       "  }\n"
                       "  ,...\n"
                       "]\n"},
             RPCExamples{"\nList the unspent transactions\n" +
                         HelpExampleCli("listunspent", "") +
                         "\nLock an unspent transaction\n" +
                         HelpExampleCli("lockunspent",
                                        "false "
                                        "\"[{\\\"txid\\\":"
                                        "\\\"a08e6907dbbd3d809776dbfc5d82e371"
                                        "b764ed838b5655e72f463568df1aadf0\\\""
                                        ",\\\"vout\\\":1}]\"") +
                         "\nList the locked transactions\n" +
                         HelpExampleCli("listlockunspent", "") +
                         "\nUnlock the transaction again\n" +
                         HelpExampleCli("lockunspent",
                                        "true "
                                        "\"[{\\\"txid\\\":"
                                        "\\\"a08e6907dbbd3d809776dbfc5d82e371"
                                        "b764ed838b5655e72f463568df1aadf0\\\""
                                        ",\\\"vout\\\":1}]\"") +
                         "\nAs a JSON-RPC call\n" +
                         HelpExampleRpc("listlockunspent", "")},
         }
                                      .ToString());
     }
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     std::vector<COutPoint> vOutpts;
     pwallet->ListLockedCoins(vOutpts);
 
     UniValue ret(UniValue::VARR);
 
     for (const COutPoint &output : vOutpts) {
         UniValue o(UniValue::VOBJ);
 
         o.pushKV("txid", output.GetTxId().GetHex());
         o.pushKV("vout", int(output.GetN()));
         ret.push_back(o);
     }
 
     return ret;
 }
 
 static UniValue settxfee(const Config &config, const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() < 1 ||
         request.params.size() > 1) {
         throw std::runtime_error(RPCHelpMan{
             "settxfee",
             "\nSet the transaction fee per kB for this wallet. Overrides the "
             "global -paytxfee command line parameter.\n",
             {
                 {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO,
                  "The transaction fee in " + CURRENCY_UNIT + "/kB"},
             },
             RPCResult{
                 "true|false        (boolean) Returns true if successful\n"},
             RPCExamples{HelpExampleCli("settxfee", "0.00001") +
                         HelpExampleRpc("settxfee", "0.00001")},
         }
                                      .ToString());
     }
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     Amount nAmount = AmountFromValue(request.params[0]);
     CFeeRate tx_fee_rate(nAmount, 1000);
     if (tx_fee_rate == CFeeRate()) {
         // automatic selection
     } else if (tx_fee_rate < pwallet->chain().relayMinFee()) {
         throw JSONRPCError(
             RPC_INVALID_PARAMETER,
             strprintf("txfee cannot be less than min relay tx fee (%s)",
                       pwallet->chain().relayMinFee().ToString()));
     } else if (tx_fee_rate < pwallet->m_min_fee) {
         throw JSONRPCError(
             RPC_INVALID_PARAMETER,
             strprintf("txfee cannot be less than wallet min fee (%s)",
                       pwallet->m_min_fee.ToString()));
     }
 
     pwallet->m_pay_tx_fee = tx_fee_rate;
     return true;
 }
 
 static UniValue getwalletinfo(const Config &config,
                               const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() != 0) {
         throw std::runtime_error(RPCHelpMan{
             "getwalletinfo",
             "Returns an object containing various wallet state info.\n",
             {},
             RPCResult{
                 "{\n"
                 "  \"walletname\": xxxxx,             (string) the wallet "
                 "name\n"
                 "  \"walletversion\": xxxxx,          (numeric) the wallet "
                 "version\n"
                 "  \"balance\": xxxxxxx,              (numeric) the total "
                 "confirmed balance of the wallet in " +
                 CURRENCY_UNIT +
                 "\n"
                 "  \"unconfirmed_balance\": xxx,      (numeric) the total "
                 "unconfirmed balance of the wallet in " +
                 CURRENCY_UNIT +
                 "\n"
                 "  \"immature_balance\": xxxxxx,      (numeric) the total "
                 "immature balance of the wallet in " +
                 CURRENCY_UNIT +
                 "\n"
                 "  \"txcount\": xxxxxxx,              (numeric) the total "
                 "number of transactions in the wallet\n"
                 "  \"keypoololdest\": xxxxxx,         (numeric) the timestamp "
                 "(seconds since Unix epoch) of the oldest pre-generated key in "
                 "the key pool\n"
                 "  \"keypoolsize\": xxxx,             (numeric) how many new "
                 "keys are pre-generated (only counts external keys)\n"
                 "  \"keypoolsize_hd_internal\": xxxx, (numeric) how many new "
                 "keys are pre-generated for internal use (used for change "
                 "outputs, only appears if the wallet is using this feature, "
                 "otherwise external keys are used)\n"
                 "  \"unlocked_until\": ttt,           (numeric) the timestamp "
                 "in seconds since epoch (midnight Jan 1 1970 GMT) that the "
                 "wallet is unlocked for transfers, or 0 if the wallet is "
                 "locked\n"
                 "  \"paytxfee\": x.xxxx,              (numeric) the "
                 "transaction fee configuration, set in " +
                 CURRENCY_UNIT +
                 "/kB\n"
                 "  \"hdseedid\": \"<hash160>\"          (string, optional) the "
                 "Hash160 of the HD seed (only present when HD is enabled)\n"
                 "  \"private_keys_enabled\": true|false (boolean) false if "
                 "privatekeys are disabled for this wallet (enforced watch-only "
                 "wallet)\n"
                 "}\n"},
             RPCExamples{HelpExampleCli("getwalletinfo", "") +
                         HelpExampleRpc("getwalletinfo", "")},
         }
                                      .ToString());
     }
 
     // Make sure the results are valid at least up to the most recent block
     // the user could have gotten from another RPC command prior to now
     pwallet->BlockUntilSyncedToCurrentChain();
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     UniValue obj(UniValue::VOBJ);
 
     size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
     const auto bal = pwallet->GetBalance();
     obj.pushKV("walletname", pwallet->GetName());
     obj.pushKV("walletversion", pwallet->GetVersion());
     obj.pushKV("balance", ValueFromAmount(bal.m_mine_trusted));
     obj.pushKV("unconfirmed_balance",
                ValueFromAmount(bal.m_mine_untrusted_pending));
     obj.pushKV("immature_balance", ValueFromAmount(bal.m_mine_immature));
     obj.pushKV("txcount", (int)pwallet->mapWallet.size());
     obj.pushKV("keypoololdest", pwallet->GetOldestKeyPoolTime());
     obj.pushKV("keypoolsize", (int64_t)kpExternalSize);
     CKeyID seed_id = pwallet->GetHDChain().seed_id;
     if (pwallet->CanSupportFeature(FEATURE_HD_SPLIT)) {
         obj.pushKV("keypoolsize_hd_internal",
                    int64_t(pwallet->GetKeyPoolSize() - kpExternalSize));
     }
     if (pwallet->IsCrypted()) {
         obj.pushKV("unlocked_until", pwallet->nRelockTime);
     }
     obj.pushKV("paytxfee", ValueFromAmount(pwallet->m_pay_tx_fee.GetFeePerK()));
     if (!seed_id.IsNull()) {
         obj.pushKV("hdseedid", seed_id.GetHex());
     }
     obj.pushKV("private_keys_enabled",
                !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
     return obj;
 }
 
 static UniValue listwalletdir(const Config &config,
                               const JSONRPCRequest &request) {
     if (request.fHelp || request.params.size() != 0) {
         throw std::runtime_error(RPCHelpMan{
             "listwalletdir",
             "Returns a list of wallets in the wallet directory.\n",
             {},
             RPCResult{
                 "{\n"
                 "  \"wallets\" : [                (json array of objects)\n"
                 "    {\n"
                 "      \"name\" : \"name\"          (string) The wallet name\n"
                 "    }\n"
                 "    ,...\n"
                 "  ]\n"
                 "}\n"},
             RPCExamples{HelpExampleCli("listwalletdir", "") +
                         HelpExampleRpc("listwalletdir", "")},
         }
                                      .ToString());
     }
 
     UniValue wallets(UniValue::VARR);
     for (const auto &path : ListWalletDir()) {
         UniValue wallet(UniValue::VOBJ);
         wallet.pushKV("name", path.string());
         wallets.push_back(wallet);
     }
 
     UniValue result(UniValue::VOBJ);
     result.pushKV("wallets", wallets);
     return result;
 }
 
 static UniValue listwallets(const Config &config,
                             const JSONRPCRequest &request) {
     if (request.fHelp || request.params.size() != 0) {
         throw std::runtime_error(RPCHelpMan{
             "listwallets",
             "Returns a list of currently loaded wallets.\n"
             "For full information on the wallet, use \"getwalletinfo\"\n",
             {},
             RPCResult{"[                         (json array of strings)\n"
                       "  \"walletname\"            (string) the wallet name\n"
                       "   ...\n"
                       "]\n"},
             RPCExamples{HelpExampleCli("listwallets", "") +
                         HelpExampleRpc("listwallets", "")},
         }
                                      .ToString());
     }
 
     UniValue obj(UniValue::VARR);
 
     for (const std::shared_ptr<CWallet> &wallet : GetWallets()) {
         if (!EnsureWalletIsAvailable(wallet.get(), request.fHelp)) {
             return NullUniValue;
         }
 
         LOCK(wallet->cs_wallet);
 
         obj.push_back(wallet->GetName());
     }
 
     return obj;
 }
 
 static UniValue loadwallet(const Config &config,
                            const JSONRPCRequest &request) {
     if (request.fHelp || request.params.size() != 1) {
         throw std::runtime_error(RPCHelpMan{
             "loadwallet",
             "\nLoads a wallet from a wallet file or directory."
             "\nNote that all wallet command-line options used when starting "
             "bitcoind will be"
             "\napplied to the new wallet (eg -zapwallettxes, upgradewallet, "
             "rescan, etc).\n",
             {
                 {"filename", RPCArg::Type::STR, RPCArg::Optional::NO,
                  "The wallet directory or .dat file."},
             },
             RPCResult{"{\n"
                       "  \"name\" :    <wallet_name>,        (string) The "
                       "wallet name if loaded successfully.\n"
                       "  \"warning\" : <warning>,            (string) Warning "
                       "message if wallet was not loaded cleanly.\n"
                       "}\n"},
             RPCExamples{HelpExampleCli("loadwallet", "\"test.dat\"") +
                         HelpExampleRpc("loadwallet", "\"test.dat\"")},
         }
                                      .ToString());
     }
 
     const CChainParams &chainParams = config.GetChainParams();
 
     WalletLocation location(request.params[0].get_str());
 
     if (!location.Exists()) {
         throw JSONRPCError(RPC_WALLET_NOT_FOUND,
                            "Wallet " + location.GetName() + " not found.");
     } else if (fs::is_directory(location.GetPath())) {
         // The given filename is a directory. Check that there's a wallet.dat
         // file.
         fs::path wallet_dat_file = location.GetPath() / "wallet.dat";
         if (fs::symlink_status(wallet_dat_file).type() == fs::file_not_found) {
             throw JSONRPCError(RPC_WALLET_NOT_FOUND,
                                "Directory " + location.GetName() +
                                    " does not contain a wallet.dat file.");
         }
     }
 
     std::string error, warning;
     std::shared_ptr<CWallet> const wallet =
         LoadWallet(chainParams, *g_rpc_node->chain, location, error, warning);
     if (!wallet) {
         throw JSONRPCError(RPC_WALLET_ERROR, error);
     }
 
     UniValue obj(UniValue::VOBJ);
     obj.pushKV("name", wallet->GetName());
     obj.pushKV("warning", warning);
 
     return obj;
 }
 
 static UniValue createwallet(const Config &config,
                              const JSONRPCRequest &request) {
-    if (request.fHelp || request.params.size() < 1 ||
-        request.params.size() > 3) {
-        throw std::runtime_error(RPCHelpMan{
-            "createwallet",
-            "\nCreates and loads a new wallet.\n",
-            {
-                {"wallet_name", RPCArg::Type::STR, RPCArg::Optional::NO,
-                 "The name for the new wallet. If this is a path, "
-                 "the wallet will be created at the path location."},
-                {"disable_private_keys", RPCArg::Type::BOOL,
-                 /* default */ "false",
-                 "Disable the possibility of private keys (only "
-                 "watchonlys are possible in this mode)."},
-                {"blank", RPCArg::Type::BOOL, /* default */ "false",
-                 "Create a blank wallet. A blank wallet has no keys "
-                 "or HD seed. One can be set using sethdseed.\n"},
-            },
-            RPCResult{
-                "{\n"
-                "  \"name\" :    <wallet_name>,        (string) The wallet "
-                "name if created successfully. If the wallet was created using "
-                "a full path, the wallet_name will be the full path.\n"
-                "  \"warning\" : <warning>,            (string) Warning "
-                "message if wallet was not loaded cleanly.\n"
-                "}\n"},
-            RPCExamples{HelpExampleCli("createwallet", "\"testwallet\"") +
-                        HelpExampleRpc("createwallet", "\"testwallet\"")},
-        }
-                                     .ToString());
+    const RPCHelpMan help{
+        "createwallet",
+        "\nCreates and loads a new wallet.\n",
+        {
+            {"wallet_name", RPCArg::Type::STR, RPCArg::Optional::NO,
+             "The name for the new wallet. If this is a path, the wallet will "
+             "be created at the path location."},
+            {"disable_private_keys", RPCArg::Type::BOOL, /* default */ "false",
+             "Disable the possibility of private keys (only watchonlys are "
+             "possible in this mode)."},
+            {"blank", RPCArg::Type::BOOL, /* default */ "false",
+             "Create a blank wallet. A blank wallet has no keys or HD seed. "
+             "One can be set using sethdseed."},
+            {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED,
+             "Encrypt the wallet with this passphrase."},
+        },
+        RPCResult{"{\n"
+                  "  \"name\" :    <wallet_name>,        (string) The wallet "
+                  "name if created successfully. If the wallet was created "
+                  "using a full path, the wallet_name will be the full path.\n"
+                  "  \"warning\" : <warning>,            (string) Warning "
+                  "message if wallet was not loaded cleanly.\n"
+                  "}\n"},
+        RPCExamples{HelpExampleCli("createwallet", "\"testwallet\"") +
+                    HelpExampleRpc("createwallet", "\"testwallet\"")},
+    };
+
+    if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
+        throw std::runtime_error(help.ToString());
     }
 
     const CChainParams &chainParams = config.GetChainParams();
 
     std::string error;
     std::string warning;
 
     uint64_t flags = 0;
     if (!request.params[1].isNull() && request.params[1].get_bool()) {
         flags |= WALLET_FLAG_DISABLE_PRIVATE_KEYS;
     }
 
+    // Indicate that the wallet is actually supposed to be blank and not just
+    // blank to make it encrypted
+    bool create_blank = false;
+
     if (!request.params[2].isNull() && request.params[2].get_bool()) {
+        create_blank = true;
+        flags |= WALLET_FLAG_BLANK_WALLET;
+    }
+    SecureString passphrase;
+    passphrase.reserve(100);
+    if (!request.params[3].isNull()) {
+        passphrase = request.params[3].get_str().c_str();
+        if (passphrase.empty()) {
+            // Empty string is invalid
+            throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED,
+                               "Cannot encrypt a wallet with a blank password");
+        }
+        // Born encrypted wallets need to be blank first so that wallet creation
+        // doesn't make any unencrypted keys
         flags |= WALLET_FLAG_BLANK_WALLET;
     }
 
     WalletLocation location(request.params[0].get_str());
     if (location.Exists()) {
         throw JSONRPCError(RPC_WALLET_ERROR,
                            "Wallet " + location.GetName() + " already exists.");
     }
 
     // Wallet::Verify will check if we're trying to create a wallet with a
     // duplicate name.
     if (!CWallet::Verify(chainParams, *g_rpc_node->chain, location, false,
                          error, warning)) {
         throw JSONRPCError(RPC_WALLET_ERROR,
                            "Wallet file verification failed: " + error);
     }
 
     std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(
         chainParams, *g_rpc_node->chain, location, flags);
     if (!wallet) {
         throw JSONRPCError(RPC_WALLET_ERROR, "Wallet creation failed.");
     }
+
+    // Encrypt the wallet if there's a passphrase
+    if (!passphrase.empty() && !(flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+        if (!wallet->EncryptWallet(passphrase)) {
+            throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED,
+                               "Error: Wallet created but failed to encrypt.");
+        }
+
+        if (!create_blank) {
+            // Unlock the wallet
+            if (!wallet->Unlock(passphrase)) {
+                throw JSONRPCError(
+                    RPC_WALLET_ENCRYPTION_FAILED,
+                    "Error: Wallet was encrypted but could not be unlocked");
+            }
+
+            // Set a seed for the wallet
+            CPubKey master_pub_key = wallet->GenerateNewSeed();
+            wallet->SetHDSeed(master_pub_key);
+            wallet->NewKeyPool();
+
+            // Relock the wallet
+            wallet->Lock();
+        }
+    }
+
     AddWallet(wallet);
 
     wallet->postInitProcess();
 
     UniValue obj(UniValue::VOBJ);
     obj.pushKV("name", wallet->GetName());
     obj.pushKV("warning", warning);
 
     return obj;
 }
 
 static UniValue unloadwallet(const Config &config,
                              const JSONRPCRequest &request) {
     if (request.fHelp || request.params.size() > 1) {
         throw std::runtime_error(RPCHelpMan{
             "unloadwallet",
             "Unloads the wallet referenced by the request endpoint "
             "otherwise unloads the wallet specified in the argument.\n"
             "Specifying the wallet name on a wallet endpoint is invalid.",
             {
                 {"wallet_name", RPCArg::Type::STR,
                  /* default */ "the wallet name from the RPC request",
                  "The name of the wallet to unload."},
             },
             RPCResults{},
             RPCExamples{HelpExampleCli("unloadwallet", "wallet_name") +
                         HelpExampleRpc("unloadwallet", "wallet_name")},
         }
                                      .ToString());
     }
 
     std::string wallet_name;
     if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
         if (!request.params[0].isNull()) {
             throw JSONRPCError(RPC_INVALID_PARAMETER,
                                "Cannot unload the requested wallet");
         }
     } else {
         wallet_name = request.params[0].get_str();
     }
 
     std::shared_ptr<CWallet> wallet = GetWallet(wallet_name);
     if (!wallet) {
         throw JSONRPCError(RPC_WALLET_NOT_FOUND,
                            "Requested wallet does not exist or is not loaded");
     }
 
     // Release the "main" shared pointer and prevent further notifications.
     // Note that any attempt to load the same wallet would fail until the wallet
     // is destroyed (see CheckUniqueFileid).
     if (!RemoveWallet(wallet)) {
         throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded");
     }
 
     UnloadWallet(std::move(wallet));
 
     return NullUniValue;
 }
 
 static UniValue listunspent(const Config &config,
                             const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() > 5) {
         throw std::runtime_error(RPCHelpMan{
             "listunspent",
             "\nReturns array of unspent transaction outputs\n"
             "with between minconf and maxconf (inclusive) confirmations.\n"
             "Optionally filter to only include txouts paid to specified "
             "addresses.\n",
             {
                 {"minconf", RPCArg::Type::NUM, /* default */ "1",
                  "The minimum confirmations to filter"},
                 {"maxconf", RPCArg::Type::NUM, /* default */ "9999999",
                  "The maximum confirmations to filter"},
                 {
                     "addresses",
                     RPCArg::Type::ARR,
                     /* default */ "empty array",
                     "A json array of bitcoin addresses to filter",
                     {
                         {"address", RPCArg::Type::STR,
                          RPCArg::Optional::OMITTED, "bitcoin address"},
                     },
                 },
                 {"include_unsafe", RPCArg::Type::BOOL, /* default */ "true",
                  "Include outputs that are not safe to spend\n"
                  "                  See description of \"safe\" attribute "
                  "below."},
                 {"query_options",
                  RPCArg::Type::OBJ,
                  RPCArg::Optional::OMITTED_NAMED_ARG,
                  "JSON with query options",
                  {
                      {"minimumAmount", RPCArg::Type::AMOUNT, /* default */ "0",
                       "Minimum value of each UTXO in " + CURRENCY_UNIT + ""},
                      {"maximumAmount", RPCArg::Type::AMOUNT,
                       /* default */ "unlimited",
                       "Maximum value of each UTXO in " + CURRENCY_UNIT + ""},
                      {"maximumCount", RPCArg::Type::NUM,
                       /* default */ "unlimited", "Maximum number of UTXOs"},
                      {"minimumSumAmount", RPCArg::Type::AMOUNT,
                       /* default */ "unlimited",
                       "Minimum sum value of all UTXOs in " + CURRENCY_UNIT +
                           ""},
                  },
                  "query_options"},
             },
             RPCResult{
                 "[                   (array of json object)\n"
                 "  {\n"
                 "    \"txid\" : \"txid\",          (string) the transaction id "
                 "\n"
                 "    \"vout\" : n,               (numeric) the vout value\n"
                 "    \"address\" : \"address\",    (string) the bitcoin "
                 "address\n"
                 "    \"label\" : \"label\",        (string) The associated "
                 "label, or \"\" for the default label\n"
                 "    \"scriptPubKey\" : \"key\",   (string) the script key\n"
                 "    \"amount\" : x.xxx,         (numeric) the transaction "
                 "output amount in " +
                 CURRENCY_UNIT +
                 "\n"
                 "    \"confirmations\" : n,      (numeric) The number of "
                 "confirmations\n"
                 "    \"redeemScript\" : n        (string) The redeemScript if "
                 "scriptPubKey is P2SH\n"
                 "    \"spendable\" : xxx,        (bool) Whether we have the "
                 "private keys to spend this output\n"
                 "    \"solvable\" : xxx,         (bool) Whether we know how to "
                 "spend this output, ignoring the lack of keys\n"
                 "    \"desc\" : xxx,             (string, only when solvable) "
                 "A descriptor for spending this output\n"
                 "    \"safe\" : xxx              (bool) Whether this output is "
                 "considered safe to spend. Unconfirmed transactions\n"
                 "                              from outside keys are "
                 "considered unsafe and are not eligible for spending by\n"
                 "                              fundrawtransaction and "
                 "sendtoaddress.\n"
                 "  }\n"
                 "  ,...\n"
                 "]\n"},
             RPCExamples{
                 HelpExampleCli("listunspent", "") +
                 HelpExampleCli(
                     "listunspent",
                     "6 9999999 "
                     "\"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\","
                     "\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") +
                 HelpExampleRpc(
                     "listunspent",
                     "6, 9999999 "
                     "\"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\","
                     "\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") +
                 HelpExampleCli(
                     "listunspent",
                     "6 9999999 '[]' true '{ \"minimumAmount\": 0.005 }'") +
                 HelpExampleRpc(
                     "listunspent",
                     "6, 9999999, [] , true, { \"minimumAmount\": 0.005 } ")},
         }
                                      .ToString());
     }
 
     int nMinDepth = 1;
     if (!request.params[0].isNull()) {
         RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
         nMinDepth = request.params[0].get_int();
     }
 
     int nMaxDepth = 9999999;
     if (!request.params[1].isNull()) {
         RPCTypeCheckArgument(request.params[1], UniValue::VNUM);
         nMaxDepth = request.params[1].get_int();
     }
 
     std::set<CTxDestination> destinations;
     if (!request.params[2].isNull()) {
         RPCTypeCheckArgument(request.params[2], UniValue::VARR);
         UniValue inputs = request.params[2].get_array();
         for (size_t idx = 0; idx < inputs.size(); idx++) {
             const UniValue &input = inputs[idx];
             CTxDestination dest =
                 DecodeDestination(input.get_str(), config.GetChainParams());
             if (!IsValidDestination(dest)) {
                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
                                    std::string("Invalid Bitcoin address: ") +
                                        input.get_str());
             }
             if (!destinations.insert(dest).second) {
                 throw JSONRPCError(
                     RPC_INVALID_PARAMETER,
                     std::string("Invalid parameter, duplicated address: ") +
                         input.get_str());
             }
         }
     }
 
     bool include_unsafe = true;
     if (!request.params[3].isNull()) {
         RPCTypeCheckArgument(request.params[3], UniValue::VBOOL);
         include_unsafe = request.params[3].get_bool();
     }
 
     Amount nMinimumAmount = Amount::zero();
     Amount nMaximumAmount = MAX_MONEY;
     Amount nMinimumSumAmount = MAX_MONEY;
     uint64_t nMaximumCount = 0;
 
     if (!request.params[4].isNull()) {
         const UniValue &options = request.params[4].get_obj();
 
         if (options.exists("minimumAmount")) {
             nMinimumAmount = AmountFromValue(options["minimumAmount"]);
         }
 
         if (options.exists("maximumAmount")) {
             nMaximumAmount = AmountFromValue(options["maximumAmount"]);
         }
 
         if (options.exists("minimumSumAmount")) {
             nMinimumSumAmount = AmountFromValue(options["minimumSumAmount"]);
         }
 
         if (options.exists("maximumCount")) {
             nMaximumCount = options["maximumCount"].get_int64();
         }
     }
 
     // Make sure the results are valid at least up to the most recent block
     // the user could have gotten from another RPC command prior to now
     pwallet->BlockUntilSyncedToCurrentChain();
 
     UniValue results(UniValue::VARR);
     std::vector<COutput> vecOutputs;
     {
         auto locked_chain = pwallet->chain().lock();
         LOCK(pwallet->cs_wallet);
         pwallet->AvailableCoins(*locked_chain, vecOutputs, !include_unsafe,
                                 nullptr, nMinimumAmount, nMaximumAmount,
                                 nMinimumSumAmount, nMaximumCount, nMinDepth,
                                 nMaxDepth);
     }
 
     LOCK(pwallet->cs_wallet);
 
     for (const COutput &out : vecOutputs) {
         CTxDestination address;
         const CScript &scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
         bool fValidAddress = ExtractDestination(scriptPubKey, address);
 
         if (destinations.size() &&
             (!fValidAddress || !destinations.count(address))) {
             continue;
         }
 
         UniValue entry(UniValue::VOBJ);
         entry.pushKV("txid", out.tx->GetId().GetHex());
         entry.pushKV("vout", out.i);
 
         if (fValidAddress) {
             entry.pushKV("address", EncodeDestination(address, config));
 
             auto i = pwallet->mapAddressBook.find(address);
             if (i != pwallet->mapAddressBook.end()) {
                 entry.pushKV("label", i->second.name);
             }
 
             if (scriptPubKey.IsPayToScriptHash()) {
                 const CScriptID &hash =
                     CScriptID(boost::get<ScriptHash>(address));
                 CScript redeemScript;
                 if (pwallet->GetCScript(hash, redeemScript)) {
                     entry.pushKV("redeemScript", HexStr(redeemScript.begin(),
                                                         redeemScript.end()));
                 }
             }
         }
 
         entry.pushKV("scriptPubKey",
                      HexStr(scriptPubKey.begin(), scriptPubKey.end()));
         entry.pushKV("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue));
         entry.pushKV("confirmations", out.nDepth);
         entry.pushKV("spendable", out.fSpendable);
         entry.pushKV("solvable", out.fSolvable);
         if (out.fSolvable) {
             auto descriptor = InferDescriptor(scriptPubKey, *pwallet);
             entry.pushKV("desc", descriptor->ToString());
         }
         entry.pushKV("safe", out.fSafe);
         results.push_back(entry);
     }
 
     return results;
 }
 
 void FundTransaction(CWallet *const pwallet, CMutableTransaction &tx,
                      Amount &fee_out, int &change_position, UniValue options) {
     // Make sure the results are valid at least up to the most recent block
     // the user could have gotten from another RPC command prior to now
     pwallet->BlockUntilSyncedToCurrentChain();
 
     CCoinControl coinControl;
     change_position = -1;
     bool lockUnspents = false;
     UniValue subtractFeeFromOutputs;
     std::set<int> setSubtractFeeFromOutputs;
 
     if (!options.isNull()) {
         if (options.type() == UniValue::VBOOL) {
             // backward compatibility bool only fallback
             coinControl.fAllowWatchOnly = options.get_bool();
         } else {
             RPCTypeCheckArgument(options, UniValue::VOBJ);
             RPCTypeCheckObj(
                 options,
                 {
                     {"changeAddress", UniValueType(UniValue::VSTR)},
                     {"changePosition", UniValueType(UniValue::VNUM)},
                     {"includeWatching", UniValueType(UniValue::VBOOL)},
                     {"lockUnspents", UniValueType(UniValue::VBOOL)},
                     // will be checked below
                     {"feeRate", UniValueType()},
                     {"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
                 },
                 true, true);
 
             if (options.exists("changeAddress")) {
                 CTxDestination dest = DecodeDestination(
                     options["changeAddress"].get_str(), pwallet->chainParams);
 
                 if (!IsValidDestination(dest)) {
                     throw JSONRPCError(
                         RPC_INVALID_ADDRESS_OR_KEY,
                         "changeAddress must be a valid bitcoin address");
                 }
 
                 coinControl.destChange = dest;
             }
 
             if (options.exists("changePosition")) {
                 change_position = options["changePosition"].get_int();
             }
 
             if (options.exists("includeWatching")) {
                 coinControl.fAllowWatchOnly =
                     options["includeWatching"].get_bool();
             }
 
             if (options.exists("lockUnspents")) {
                 lockUnspents = options["lockUnspents"].get_bool();
             }
 
             if (options.exists("feeRate")) {
                 coinControl.m_feerate =
                     CFeeRate(AmountFromValue(options["feeRate"]));
                 coinControl.fOverrideFeeRate = true;
             }
 
             if (options.exists("subtractFeeFromOutputs")) {
                 subtractFeeFromOutputs =
                     options["subtractFeeFromOutputs"].get_array();
             }
         }
     }
 
     if (tx.vout.size() == 0) {
         throw JSONRPCError(RPC_INVALID_PARAMETER,
                            "TX must have at least one output");
     }
 
     if (change_position != -1 &&
         (change_position < 0 ||
          (unsigned int)change_position > tx.vout.size())) {
         throw JSONRPCError(RPC_INVALID_PARAMETER,
                            "changePosition out of bounds");
     }
 
     for (size_t idx = 0; idx < subtractFeeFromOutputs.size(); idx++) {
         int pos = subtractFeeFromOutputs[idx].get_int();
         if (setSubtractFeeFromOutputs.count(pos)) {
             throw JSONRPCError(
                 RPC_INVALID_PARAMETER,
                 strprintf("Invalid parameter, duplicated position: %d", pos));
         }
         if (pos < 0) {
             throw JSONRPCError(
                 RPC_INVALID_PARAMETER,
                 strprintf("Invalid parameter, negative position: %d", pos));
         }
         if (pos >= int(tx.vout.size())) {
             throw JSONRPCError(
                 RPC_INVALID_PARAMETER,
                 strprintf("Invalid parameter, position too large: %d", pos));
         }
         setSubtractFeeFromOutputs.insert(pos);
     }
 
     std::string strFailReason;
 
     if (!pwallet->FundTransaction(tx, fee_out, change_position, strFailReason,
                                   lockUnspents, setSubtractFeeFromOutputs,
                                   coinControl)) {
         throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
     }
 }
 
 static UniValue fundrawtransaction(const Config &config,
                                    const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() < 1 ||
         request.params.size() > 2) {
         throw std::runtime_error(RPCHelpMan{
             "fundrawtransaction",
             "\nAdd inputs to a transaction until it has enough in value to "
             "meet its out value.\n"
             "This will not modify existing inputs, and will add at most one "
             "change output to the outputs.\n"
             "No existing outputs will be modified unless "
             "\"subtractFeeFromOutputs\" is specified.\n"
             "Note that inputs which were signed may need to be resigned after "
             "completion since in/outputs have been added.\n"
             "The inputs added will not be signed, use "
             "signrawtransactionwithkey or signrawtransactionwithwallet for "
             "that.\n"
             "Note that all existing inputs must have their previous output "
             "transaction be in the wallet.\n"
             "Note that all inputs selected must be of standard form and P2SH "
             "scripts must be\n"
             "in the wallet using importaddress or addmultisigaddress (to "
             "calculate fees).\n"
             "You can see whether this is the case by checking the \"solvable\" "
             "field in the listunspent output.\n"
             "Only pay-to-pubkey, multisig, and P2SH versions thereof are "
             "currently supported for watch-only\n",
             {
                 {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO,
                  "The hex string of the raw transaction"},
                 {"options",
                  RPCArg::Type::OBJ,
                  RPCArg::Optional::OMITTED_NAMED_ARG,
                  "for backward compatibility: passing in a true instead of an "
                  "object will result in {\"includeWatching\":true}",
                  {
                      {"changeAddress", RPCArg::Type::STR,
                       /* default */ "pool address",
                       "The bitcoin address to receive the change"},
                      {"changePosition", RPCArg::Type::NUM, /* default */ "",
                       "The index of the change output"},
                      {"includeWatching", RPCArg::Type::BOOL,
                       /* default */ "false",
                       "Also select inputs which are watch only"},
                      {"lockUnspents", RPCArg::Type::BOOL, /* default */ "false",
                       "Lock selected unspent outputs"},
                      {"feeRate", RPCArg::Type::AMOUNT, /* default */
                       "not set: makes wallet determine the fee",
                       "Set a specific fee rate in " + CURRENCY_UNIT + "/kB"},
                      {
                          "subtractFeeFromOutputs",
                          RPCArg::Type::ARR,
                          /* default */ "empty array",
                          "A json array of integers.\n"
                          "                              The fee will be "
                          "equally deducted from the amount of each "
                          "specified output.\n"
                          "                              Those recipients "
                          "will receive less bitcoins than you enter in "
                          "their corresponding amount field.\n"
                          "                              If no outputs are "
                          "specified here, the sender pays the fee.",
                          {
                              {"vout_index", RPCArg::Type::NUM,
                               RPCArg::Optional::OMITTED,
                               "The zero-based output index, before a "
                               "change output is added."},
                          },
                      },
                  },
                  "options"},
             },
             RPCResult{"{\n"
                       "  \"hex\":       \"value\", (string)  The resulting raw "
                       "transaction (hex-encoded string)\n"
                       "  \"fee\":       n,         (numeric) Fee in " +
                       CURRENCY_UNIT +
                       " the resulting transaction pays\n"
                       "  \"changepos\": n          (numeric) The position of "
                       "the added change output, or -1\n"
                       "}\n"},
             RPCExamples{
                 "\nCreate a transaction with no inputs\n" +
                 HelpExampleCli("createrawtransaction",
                                "\"[]\" \"{\\\"myaddress\\\":0.01}\"") +
                 "\nAdd sufficient unsigned inputs to meet the output value\n" +
                 HelpExampleCli("fundrawtransaction", "\"rawtransactionhex\"") +
                 "\nSign the transaction\n" +
                 HelpExampleCli("signrawtransactionwithwallet",
                                "\"fundedtransactionhex\"") +
                 "\nSend the transaction\n" +
                 HelpExampleCli("sendrawtransaction",
                                "\"signedtransactionhex\"")},
         }
                                      .ToString());
     }
 
     RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType()});
 
     // parse hex string from parameter
     CMutableTransaction tx;
     if (!DecodeHexTx(tx, request.params[0].get_str())) {
         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
     }
 
     Amount fee;
     int change_position;
     FundTransaction(pwallet, tx, fee, change_position, request.params[1]);
 
     UniValue result(UniValue::VOBJ);
     result.pushKV("hex", EncodeHexTx(CTransaction(tx)));
     result.pushKV("fee", ValueFromAmount(fee));
     result.pushKV("changepos", change_position);
 
     return result;
 }
 
 UniValue signrawtransactionwithwallet(const Config &config,
                                       const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() < 1 ||
         request.params.size() > 3) {
         throw std::runtime_error(RPCHelpMan{
             "signrawtransactionwithwallet",
             "\nSign inputs for raw transaction (serialized, hex-encoded).\n"
             "The second optional argument (may be null) is an array of "
             "previous transaction outputs that\n"
             "this transaction depends on but may not yet be in the block "
             "chain.\n" +
                 HelpRequiringPassphrase(pwallet) + "\n",
             {
                 {"hexstring", RPCArg::Type::STR, RPCArg::Optional::NO,
                  "The transaction hex string"},
                 {
                     "prevtxs",
                     RPCArg::Type::ARR,
                     RPCArg::Optional::OMITTED_NAMED_ARG,
                     "A json array of previous dependent transaction outputs",
                     {
                         {
                             "",
                             RPCArg::Type::OBJ,
                             RPCArg::Optional::OMITTED,
                             "",
                             {
                                 {"txid", RPCArg::Type::STR_HEX,
                                  RPCArg::Optional::NO, "The transaction id"},
                                 {"vout", RPCArg::Type::NUM,
                                  RPCArg::Optional::NO, "The output number"},
                                 {"scriptPubKey", RPCArg::Type::STR_HEX,
                                  RPCArg::Optional::NO, "script key"},
                                 {"redeemScript", RPCArg::Type::STR_HEX,
                                  RPCArg::Optional::OMITTED,
                                  "(required for P2SH)"},
                                 {"amount", RPCArg::Type::AMOUNT,
                                  RPCArg::Optional::NO, "The amount spent"},
                             },
                         },
                     },
                 },
                 {"sighashtype", RPCArg::Type::STR, /* default */ "ALL|FORKID",
                  "The signature hash type. Must be one of\n"
                  "       \"ALL|FORKID\"\n"
                  "       \"NONE|FORKID\"\n"
                  "       \"SINGLE|FORKID\"\n"
                  "       \"ALL|FORKID|ANYONECANPAY\"\n"
                  "       \"NONE|FORKID|ANYONECANPAY\"\n"
                  "       \"SINGLE|FORKID|ANYONECANPAY\""},
             },
             RPCResult{"{\n"
                       "  \"hex\" : \"value\",                  (string) The "
                       "hex-encoded raw transaction with signature(s)\n"
                       "  \"complete\" : true|false,          (boolean) If the "
                       "transaction has a complete set of signatures\n"
                       "  \"errors\" : [                      (json array of "
                       "objects) Script verification errors (if there are any)\n"
                       "    {\n"
                       "      \"txid\" : \"hash\",              (string) The "
                       "hash of the referenced, previous transaction\n"
                       "      \"vout\" : n,                   (numeric) The "
                       "index of the output to spent and used as input\n"
                       "      \"scriptSig\" : \"hex\",          (string) The "
                       "hex-encoded signature script\n"
                       "      \"sequence\" : n,               (numeric) Script "
                       "sequence number\n"
                       "      \"error\" : \"text\"              (string) "
                       "Verification or signing error related to the input\n"
                       "    }\n"
                       "    ,...\n"
                       "  ]\n"
                       "}\n"},
             RPCExamples{
                 HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") +
                 HelpExampleRpc("signrawtransactionwithwallet", "\"myhex\"")},
         }
                                      .ToString());
     }
 
     RPCTypeCheck(request.params,
                  {UniValue::VSTR, UniValue::VARR, UniValue::VSTR}, true);
 
     CMutableTransaction mtx;
     if (!DecodeHexTx(mtx, request.params[0].get_str())) {
         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
     }
 
     // Sign the transaction
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
     EnsureWalletIsUnlocked(pwallet);
 
     // Fetch previous transactions (inputs):
     std::map<COutPoint, Coin> coins;
     for (const CTxIn &txin : mtx.vin) {
         // Create empty map entry keyed by prevout.
         coins[txin.prevout];
     }
     pwallet->chain().findCoins(coins);
 
     return SignTransaction(mtx, request.params[1], pwallet, coins, false,
                            request.params[2]);
 }
 
 UniValue generate(const Config &config, const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() < 1 ||
         request.params.size() > 2) {
         throw std::runtime_error(RPCHelpMan{
             "generate",
             "\nMine up to nblocks blocks immediately (before the RPC call "
             "returns) to an address in the wallet.\n",
             {
                 {"nblocks", RPCArg::Type::NUM, RPCArg::Optional::NO,
                  "How many blocks are generated immediately."},
                 {"maxtries", RPCArg::Type::NUM, /* default */ "1000000",
                  "How many iterations to try."},
             },
             RPCResult{
                 "[ blockhashes ]     (array) hashes of blocks generated\n"},
             RPCExamples{"\nGenerate 11 blocks\n" +
                         HelpExampleCli("generate", "11")}}
                                      .ToString());
     }
 
     if (!IsDeprecatedRPCEnabled(gArgs, "generate")) {
         throw JSONRPCError(RPC_METHOD_DEPRECATED,
                            "The wallet generate rpc method is deprecated and "
                            "will be fully removed in v0.22. "
                            "To use generate in v0.21, restart bitcoind with "
                            "-deprecatedrpc=generate.\n"
                            "Clients should transition to using the node rpc "
                            "method generatetoaddress\n");
     }
 
     int num_generate = request.params[0].get_int();
     uint64_t max_tries = 1000000;
     if (!request.params[1].isNull()) {
         max_tries = request.params[1].get_int();
     }
 
     std::shared_ptr<CReserveScript> coinbase_script;
     pwallet->GetScriptForMining(coinbase_script);
 
     // If the keypool is exhausted, no script is returned at all.  Catch this.
     if (!coinbase_script) {
         throw JSONRPCError(
             RPC_WALLET_KEYPOOL_RAN_OUT,
             "Error: Keypool ran out, please call keypoolrefill first");
     }
 
     // throw an error if no script was provided
     if (coinbase_script->reserveScript.empty()) {
         throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available");
     }
 
     return generateBlocks(config, coinbase_script, num_generate, max_tries,
                           true);
 }
 
 UniValue rescanblockchain(const Config &config, const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() > 2) {
         throw std::runtime_error(RPCHelpMan{
             "rescanblockchain",
             "\nRescan the local blockchain for wallet related transactions.\n",
             {
                 {"start_height", RPCArg::Type::NUM, /* default */ "0",
                  "block height where the rescan should start"},
                 {"stop_height", RPCArg::Type::NUM,
                  RPCArg::Optional::OMITTED_NAMED_ARG,
                  "the last block height that should be scanned"},
             },
             RPCResult{"{\n"
                       "  \"start_height\"     (numeric) The block height where "
                       "the rescan started (the requested height or 0\n"
                       "  \"stop_height\"      (numeric) The height of the last "
                       "rescanned block. May be null in rare cases if there was "
                       "a reorg and the call didn't scan any blocks because "
                       "they were already scanned in the background.\n"
                       "}\n"},
             RPCExamples{HelpExampleCli("rescanblockchain", "100000 120000") +
                         HelpExampleRpc("rescanblockchain", "100000, 120000")},
         }
                                      .ToString());
     }
 
     WalletRescanReserver reserver(pwallet);
     if (!reserver.reserve()) {
         throw JSONRPCError(
             RPC_WALLET_ERROR,
             "Wallet is currently rescanning. Abort existing rescan or wait.");
     }
 
     int start_height = 0;
     BlockHash start_block, stop_block;
     {
         auto locked_chain = pwallet->chain().lock();
         Optional<int> tip_height = locked_chain->getHeight();
 
         if (!request.params[0].isNull()) {
             start_height = request.params[0].get_int();
             if (start_height < 0 || !tip_height || start_height > *tip_height) {
                 throw JSONRPCError(RPC_INVALID_PARAMETER,
                                    "Invalid start_height");
             }
         }
 
         Optional<int> stop_height;
         if (!request.params[1].isNull()) {
             stop_height = request.params[1].get_int();
             if (*stop_height < 0 || !tip_height || *stop_height > *tip_height) {
                 throw JSONRPCError(RPC_INVALID_PARAMETER,
                                    "Invalid stop_height");
             } else if (*stop_height < start_height) {
                 throw JSONRPCError(
                     RPC_INVALID_PARAMETER,
                     "stop_height must be greater than start_height");
             }
         }
 
         // We can't rescan beyond non-pruned blocks, stop and throw an error
         if (locked_chain->findPruned(start_height, stop_height)) {
             throw JSONRPCError(
                 RPC_MISC_ERROR,
                 "Can't rescan beyond pruned data. Use RPC call "
                 "getblockchaininfo to determine your pruned height.");
         }
 
         if (tip_height) {
             start_block = locked_chain->getBlockHash(start_height);
 
             // If called with a stop_height, set the stop_height here to
             // trigger a rescan to that height.
             // If called without a stop height, leave stop_height as null here
             // so rescan continues to the tip (even if the tip advances during
             // rescan).
             if (stop_height) {
                 stop_block = locked_chain->getBlockHash(*stop_height);
             }
         }
     }
 
     CWallet::ScanResult result = pwallet->ScanForWalletTransactions(
         start_block, stop_block, reserver, true /* fUpdate */);
     switch (result.status) {
         case CWallet::ScanResult::SUCCESS:
             break;
         case CWallet::ScanResult::FAILURE:
             throw JSONRPCError(
                 RPC_MISC_ERROR,
                 "Rescan failed. Potentially corrupted data files.");
         case CWallet::ScanResult::USER_ABORT:
             throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted.");
             // no default case, so the compiler can warn about missing cases
     }
     UniValue response(UniValue::VOBJ);
     response.pushKV("start_height", start_height);
     response.pushKV("stop_height", result.last_scanned_height
                                        ? *result.last_scanned_height
                                        : UniValue());
     return response;
 }
 
 class DescribeWalletAddressVisitor : public boost::static_visitor<UniValue> {
 public:
     CWallet *const pwallet;
 
     void ProcessSubScript(const CScript &subscript, UniValue &obj) const {
         // Always present: script type and redeemscript
         std::vector<std::vector<uint8_t>> solutions_data;
         txnouttype which_type = Solver(subscript, solutions_data);
         obj.pushKV("script", GetTxnOutputType(which_type));
         obj.pushKV("hex", HexStr(subscript.begin(), subscript.end()));
 
         CTxDestination embedded;
         if (ExtractDestination(subscript, embedded)) {
             // Only when the script corresponds to an address.
             UniValue subobj(UniValue::VOBJ);
             UniValue detail = DescribeAddress(embedded);
             subobj.pushKVs(detail);
             UniValue wallet_detail = boost::apply_visitor(*this, embedded);
             subobj.pushKVs(wallet_detail);
             subobj.pushKV("address", EncodeDestination(embedded, GetConfig()));
             subobj.pushKV("scriptPubKey",
                           HexStr(subscript.begin(), subscript.end()));
             // Always report the pubkey at the top level, so that
             // `getnewaddress()['pubkey']` always works.
             if (subobj.exists("pubkey")) {
                 obj.pushKV("pubkey", subobj["pubkey"]);
             }
             obj.pushKV("embedded", std::move(subobj));
         } else if (which_type == TX_MULTISIG) {
             // Also report some information on multisig scripts (which do not
             // have a corresponding address).
             // TODO: abstract out the common functionality between this logic
             // and ExtractDestinations.
             obj.pushKV("sigsrequired", solutions_data[0][0]);
             UniValue pubkeys(UniValue::VARR);
             for (size_t i = 1; i < solutions_data.size() - 1; ++i) {
                 CPubKey key(solutions_data[i].begin(), solutions_data[i].end());
                 pubkeys.push_back(HexStr(key.begin(), key.end()));
             }
             obj.pushKV("pubkeys", std::move(pubkeys));
         }
     }
 
     explicit DescribeWalletAddressVisitor(CWallet *_pwallet)
         : pwallet(_pwallet) {}
 
     UniValue operator()(const CNoDestination &dest) const {
         return UniValue(UniValue::VOBJ);
     }
 
     UniValue operator()(const PKHash &pkhash) const {
         CKeyID keyID(pkhash);
         UniValue obj(UniValue::VOBJ);
         CPubKey vchPubKey;
         if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) {
             obj.pushKV("pubkey", HexStr(vchPubKey));
             obj.pushKV("iscompressed", vchPubKey.IsCompressed());
         }
         return obj;
     }
 
     UniValue operator()(const ScriptHash &scripthash) const {
         CScriptID scriptID(scripthash);
         UniValue obj(UniValue::VOBJ);
         CScript subscript;
         if (pwallet && pwallet->GetCScript(scriptID, subscript)) {
             ProcessSubScript(subscript, obj);
         }
         return obj;
     }
 };
 
 static UniValue DescribeWalletAddress(CWallet *pwallet,
                                       const CTxDestination &dest) {
     UniValue ret(UniValue::VOBJ);
     UniValue detail = DescribeAddress(dest);
     ret.pushKVs(detail);
     ret.pushKVs(
         boost::apply_visitor(DescribeWalletAddressVisitor(pwallet), dest));
     return ret;
 }
 
 /** Convert CAddressBookData to JSON record.  */
 static UniValue AddressBookDataToJSON(const CAddressBookData &data,
                                       const bool verbose) {
     UniValue ret(UniValue::VOBJ);
     if (verbose) {
         ret.pushKV("name", data.name);
     }
     ret.pushKV("purpose", data.purpose);
     return ret;
 }
 
 UniValue getaddressinfo(const Config &config, const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() != 1) {
         throw std::runtime_error(RPCHelpMan{
             "getaddressinfo",
             "\nReturn information about the given bitcoin address. Some "
             "information requires the address\n"
             "to be in the wallet.\n",
             {
                 {"address", RPCArg::Type::STR, RPCArg::Optional::NO,
                  "The bitcoin address to get the information of."},
             },
             RPCResult{
                 "{\n"
                 "  \"address\" : \"address\",        (string) The bitcoin "
                 "address validated\n"
                 "  \"scriptPubKey\" : \"hex\",       (string) The hex-encoded "
                 "scriptPubKey generated by the address\n"
                 "  \"ismine\" : true|false,        (boolean) If the address is "
                 "yours or not\n"
                 "  \"iswatchonly\" : true|false,   (boolean) If the address is "
                 "watchonly\n"
                 "  \"solvable\" : true|false,      (boolean) Whether we know "
                 "how to spend coins sent to this address, ignoring the "
                 "possible lack of private keys\n"
                 "  \"desc\" : \"desc\",            (string, optional) A "
                 "descriptor for spending coins sent to this address (only when "
                 "solvable)\n"
                 "  \"isscript\" : true|false,      (boolean) If the key is a "
                 "script\n"
                 "  \"ischange\" : true|false,      (boolean) If the address "
                 "was used for change output\n"
                 "  \"script\" : \"type\"             (string, optional) The "
                 "output script type. Only if \"isscript\" is true and the "
                 "redeemscript is known. Possible types: nonstandard, pubkey, "
                 "pubkeyhash, scripthash, multisig, nulldata\n"
                 "  \"hex\" : \"hex\",                (string, optional) The "
                 "redeemscript for the p2sh address\n"
                 "  \"pubkeys\"                     (string, optional) Array of "
                 "pubkeys associated with the known redeemscript (only if "
                 "\"script\" is \"multisig\")\n"
                 "    [\n"
                 "      \"pubkey\"\n"
                 "      ,...\n"
                 "    ]\n"
                 "  \"sigsrequired\" : xxxxx        (numeric, optional) Number "
                 "of signatures required to spend multisig output (only if "
                 "\"script\" is \"multisig\")\n"
                 "  \"pubkey\" : \"publickeyhex\",    (string, optional) The "
                 "hex value of the raw public key, for single-key addresses "
                 "(possibly embedded in P2SH or P2WSH)\n"
                 "  \"embedded\" : {...},           (object, optional) "
                 "Information about the address embedded in P2SH or P2WSH, if "
                 "relevant and known. It includes all getaddressinfo output "
                 "fields for the embedded address, excluding metadata "
                 "(\"timestamp\", \"hdkeypath\", \"hdseedid\") and relation to "
                 "the wallet (\"ismine\", \"iswatchonly\").\n"
                 "  \"iscompressed\" : true|false,  (boolean) If the address is "
                 "compressed\n"
                 "  \"label\" :  \"label\"         (string) The label "
                 "associated with the address, \"\" is the default label\n"
                 "  \"timestamp\" : timestamp,      (number, optional) The "
                 "creation time of the key if available in seconds since epoch "
                 "(Jan 1 1970 GMT)\n"
                 "  \"hdkeypath\" : \"keypath\"       (string, optional) The HD "
                 "keypath if the key is HD and available\n"
                 "  \"hdseedid\" : \"<hash160>\"      (string, optional) The "
                 "Hash160 of the HD seed\n"
                 "  \"hdmasterfingerprint\" : \"<hash160>\" (string, optional) "
                 "The fingperint of the master key.\n"
                 "  \"labels\"                      (object) Array of labels "
                 "associated with the address.\n"
                 "    [\n"
                 "      { (json object of label data)\n"
                 "        \"name\": \"labelname\" (string) The label\n"
                 "        \"purpose\": \"string\" (string) Purpose of address "
                 "(\"send\" for sending address, \"receive\" for receiving "
                 "address)\n"
                 "      },...\n"
                 "    ]\n"
                 "}\n"},
             RPCExamples{
                 HelpExampleCli("getaddressinfo",
                                "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") +
                 HelpExampleRpc("getaddressinfo",
                                "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")},
         }
                                      .ToString());
     }
 
     LOCK(pwallet->cs_wallet);
 
     UniValue ret(UniValue::VOBJ);
     CTxDestination dest =
         DecodeDestination(request.params[0].get_str(), config.GetChainParams());
 
     // Make sure the destination is valid
     if (!IsValidDestination(dest)) {
         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
     }
 
     std::string currentAddress = EncodeDestination(dest, config);
     ret.pushKV("address", currentAddress);
 
     CScript scriptPubKey = GetScriptForDestination(dest);
     ret.pushKV("scriptPubKey",
                HexStr(scriptPubKey.begin(), scriptPubKey.end()));
 
     isminetype mine = IsMine(*pwallet, dest);
     ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE));
     bool solvable = IsSolvable(*pwallet, scriptPubKey);
     ret.pushKV("solvable", solvable);
     if (solvable) {
         ret.pushKV("desc", InferDescriptor(scriptPubKey, *pwallet)->ToString());
     }
     ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
     UniValue detail = DescribeWalletAddress(pwallet, dest);
     ret.pushKVs(detail);
     if (pwallet->mapAddressBook.count(dest)) {
         ret.pushKV("label", pwallet->mapAddressBook[dest].name);
     }
     ret.pushKV("ischange", pwallet->IsChange(scriptPubKey));
     const CKeyMetadata *meta = nullptr;
     CKeyID key_id = GetKeyForDestination(*pwallet, dest);
     if (!key_id.IsNull()) {
         auto it = pwallet->mapKeyMetadata.find(key_id);
         if (it != pwallet->mapKeyMetadata.end()) {
             meta = &it->second;
         }
     }
     if (!meta) {
         auto it = pwallet->m_script_metadata.find(CScriptID(scriptPubKey));
         if (it != pwallet->m_script_metadata.end()) {
             meta = &it->second;
         }
     }
     if (meta) {
         ret.pushKV("timestamp", meta->nCreateTime);
         if (meta->has_key_origin) {
             ret.pushKV("hdkeypath", WriteHDKeypath(meta->key_origin.path));
             ret.pushKV("hdseedid", meta->hd_seed_id.GetHex());
             ret.pushKV("hdmasterfingerprint",
                        HexStr(meta->key_origin.fingerprint,
                               meta->key_origin.fingerprint + 4));
         }
     }
 
     // Currently only one label can be associated with an address, return an
     // array so the API remains stable if we allow multiple labels to be
     // associated with an address.
     UniValue labels(UniValue::VARR);
     std::map<CTxDestination, CAddressBookData>::iterator mi =
         pwallet->mapAddressBook.find(dest);
     if (mi != pwallet->mapAddressBook.end()) {
         labels.push_back(AddressBookDataToJSON(mi->second, true));
     }
     ret.pushKV("labels", std::move(labels));
 
     return ret;
 }
 
 UniValue getaddressesbylabel(const Config &config,
                              const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() != 1) {
         throw std::runtime_error(RPCHelpMan{
             "getaddressesbylabel",
             "\nReturns the list of addresses assigned the specified "
             "label.\n",
             {
                 {"label", RPCArg::Type::STR, RPCArg::Optional::NO,
                  "The label."},
             },
             RPCResult{"{ (json object with addresses as keys)\n"
                       "  \"address\": { (json object with information about "
                       "address)\n"
                       "    \"purpose\": \"string\" (string)  Purpose of "
                       "address (\"send\" for sending address, \"receive\" for "
                       "receiving address)\n"
                       "  },...\n"
                       "}\n"},
             RPCExamples{HelpExampleCli("getaddressesbylabel", "\"tabby\"") +
                         HelpExampleRpc("getaddressesbylabel", "\"tabby\"")},
         }
                                      .ToString());
     }
 
     LOCK(pwallet->cs_wallet);
 
     std::string label = LabelFromValue(request.params[0]);
 
     // Find all addresses that have the given label
     UniValue ret(UniValue::VOBJ);
     std::set<std::string> addresses;
     for (const std::pair<const CTxDestination, CAddressBookData> &item :
          pwallet->mapAddressBook) {
         if (item.second.name == label) {
             std::string address = EncodeDestination(item.first, config);
             // CWallet::mapAddressBook is not expected to contain duplicate
             // address strings, but build a separate set as a precaution just in
             // case it does.
             bool unique = addresses.emplace(address).second;
             assert(unique);
             // UniValue::pushKV checks if the key exists in O(N)
             // and since duplicate addresses are unexpected (checked with
             // std::set in O(log(N))), UniValue::__pushKV is used instead,
             // which currently is O(1).
             ret.__pushKV(address, AddressBookDataToJSON(item.second, false));
         }
     }
 
     if (ret.empty()) {
         throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME,
                            std::string("No addresses with label " + label));
     }
 
     return ret;
 }
 
 UniValue listlabels(const Config &config, const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() > 1) {
         throw std::runtime_error(RPCHelpMan{
             "listlabels",
             "\nReturns the list of all labels, or labels that are "
             "assigned to addresses with a specific purpose.\n",
             {
                 {"purpose", RPCArg::Type::STR,
                  RPCArg::Optional::OMITTED_NAMED_ARG,
                  "Address purpose to list labels for ('send','receive'). An "
                  "empty string is the same as not providing this argument."},
             },
             RPCResult{"[               (json array of string)\n"
                       "  \"label\",      (string) Label name\n"
                       "  ...\n"
                       "]\n"},
             RPCExamples{"\nList all labels\n" +
                         HelpExampleCli("listlabels", "") +
                         "\nList labels that have receiving addresses\n" +
                         HelpExampleCli("listlabels", "receive") +
                         "\nList labels that have sending addresses\n" +
                         HelpExampleCli("listlabels", "send") +
                         "\nAs a JSON-RPC call\n" +
                         HelpExampleRpc("listlabels", "receive")},
         }
                                      .ToString());
     }
 
     LOCK(pwallet->cs_wallet);
 
     std::string purpose;
     if (!request.params[0].isNull()) {
         purpose = request.params[0].get_str();
     }
 
     // Add to a set to sort by label name, then insert into Univalue array
     std::set<std::string> label_set;
     for (const std::pair<const CTxDestination, CAddressBookData> &entry :
          pwallet->mapAddressBook) {
         if (purpose.empty() || entry.second.purpose == purpose) {
             label_set.insert(entry.second.name);
         }
     }
 
     UniValue ret(UniValue::VARR);
     for (const std::string &name : label_set) {
         ret.push_back(name);
     }
 
     return ret;
 }
 
 static UniValue sethdseed(const Config &config, const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() > 2) {
         throw std::runtime_error(RPCHelpMan{
             "sethdseed",
             "\nSet or generate a new HD wallet seed. Non-HD wallets will not "
             "be upgraded to being a HD wallet. Wallets that are already\n"
             "HD will have a new HD seed set so that new keys added to the "
             "keypool will be derived from this new seed.\n"
             "\nNote that you will need to MAKE A NEW BACKUP of your wallet "
             "after setting the HD wallet seed.\n" +
                 HelpRequiringPassphrase(pwallet) + "\n",
             {
                 {"newkeypool", RPCArg::Type::BOOL, /* default */ "true",
                  "Whether to flush old unused addresses, including change "
                  "addresses, from the keypool and regenerate it.\n"
                  "                             If true, the next address from "
                  "getnewaddress and change address from getrawchangeaddress "
                  "will be from this new seed.\n"
                  "                             If false, addresses (including "
                  "change addresses if the wallet already had HD Chain Split "
                  "enabled) from the existing\n"
                  "                             keypool will be used until it "
                  "has been depleted."},
                 {"seed", RPCArg::Type::STR, /* default */ "random seed",
                  "The WIF private key to use as the new HD seed.\n"
                  "                             The seed value can be retrieved "
                  "using the dumpwallet command. It is the private key marked "
                  "hdseed=1"},
             },
             RPCResults{},
             RPCExamples{HelpExampleCli("sethdseed", "") +
                         HelpExampleCli("sethdseed", "false") +
                         HelpExampleCli("sethdseed", "true \"wifkey\"") +
                         HelpExampleRpc("sethdseed", "true, \"wifkey\"")},
         }
                                      .ToString());
     }
 
     if (pwallet->chain().isInitialBlockDownload()) {
         throw JSONRPCError(
             RPC_CLIENT_IN_INITIAL_DOWNLOAD,
             "Cannot set a new HD seed while still in Initial Block Download");
     }
 
     if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
         throw JSONRPCError(
             RPC_WALLET_ERROR,
             "Cannot set a HD seed to a wallet with private keys disabled");
     }
 
     auto locked_chain = pwallet->chain().lock();
     LOCK(pwallet->cs_wallet);
 
     // Do not do anything to non-HD wallets
     if (!pwallet->CanSupportFeature(FEATURE_HD)) {
         throw JSONRPCError(
             RPC_WALLET_ERROR,
             "Cannot set a HD seed on a non-HD wallet. Start with "
             "-upgradewallet in order to upgrade a non-HD wallet to HD");
     }
 
     EnsureWalletIsUnlocked(pwallet);
 
     bool flush_key_pool = true;
     if (!request.params[0].isNull()) {
         flush_key_pool = request.params[0].get_bool();
     }
 
     CPubKey master_pub_key;
     if (request.params[1].isNull()) {
         master_pub_key = pwallet->GenerateNewSeed();
     } else {
         CKey key = DecodeSecret(request.params[1].get_str());
         if (!key.IsValid()) {
             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
                                "Invalid private key");
         }
 
         if (HaveKey(*pwallet, key)) {
             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
                                "Already have this key (either as an HD seed or "
                                "as a loose private key)");
         }
 
         master_pub_key = pwallet->DeriveNewSeed(key);
     }
 
     pwallet->SetHDSeed(master_pub_key);
     if (flush_key_pool) {
         pwallet->NewKeyPool();
     }
 
     return NullUniValue;
 }
 
 static UniValue walletprocesspsbt(const Config &config,
                                   const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() < 1 ||
         request.params.size() > 4) {
         throw std::runtime_error(RPCHelpMan{
             "walletprocesspsbt",
             "\nUpdate a PSBT with input information from our wallet and then "
             "sign inputs that we can sign for." +
                 HelpRequiringPassphrase(pwallet) + "\n",
             {
                 {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO,
                  "The transaction base64 string"},
                 {"sign", RPCArg::Type::BOOL, /* default */ "true",
                  "Also sign the transaction when updating"},
                 {"sighashtype", RPCArg::Type::STR, /* default */ "ALL|FORKID",
                  "The signature hash type to sign with if not specified by "
                  "the PSBT. Must be one of\n"
                  "       \"ALL|FORKID\"\n"
                  "       \"NONE|FORKID\"\n"
                  "       \"SINGLE|FORKID\"\n"
                  "       \"ALL|FORKID|ANYONECANPAY\"\n"
                  "       \"NONE|FORKID|ANYONECANPAY\"\n"
                  "       \"SINGLE|FORKID|ANYONECANPAY\""},
                 {"bip32derivs", RPCArg::Type::BOOL, /* default */ "false",
                  "If true, includes the BIP 32 derivation paths for public "
                  "keys if we know them"},
             },
             RPCResult{"{\n"
                       "  \"psbt\" : \"value\",          (string) The "
                       "base64-encoded partially signed transaction\n"
                       "  \"complete\" : true|false,   (boolean) If the "
                       "transaction has a complete set of signatures\n"
                       "  ]\n"
                       "}\n"},
             RPCExamples{HelpExampleCli("walletprocesspsbt", "\"psbt\"")},
         }
                                      .ToString());
     }
 
     RPCTypeCheck(request.params,
                  {UniValue::VSTR, UniValue::VBOOL, UniValue::VSTR});
 
     // Unserialize the transaction
     PartiallySignedTransaction psbtx;
     std::string error;
     if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) {
         throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
                            strprintf("TX decode failed %s", error));
     }
 
     // Get the sighash type
     SigHashType nHashType = ParseSighashString(request.params[2]);
     if (!nHashType.hasForkId()) {
         throw JSONRPCError(RPC_INVALID_PARAMETER,
                            "Signature must use SIGHASH_FORKID");
     }
 
     // Fill transaction with our data and also sign
     bool sign =
         request.params[1].isNull() ? true : request.params[1].get_bool();
     bool bip32derivs =
         request.params[3].isNull() ? false : request.params[3].get_bool();
     bool complete = true;
     const TransactionError err =
         FillPSBT(pwallet, psbtx, complete, nHashType, sign, bip32derivs);
     if (err != TransactionError::OK) {
         throw JSONRPCTransactionError(err);
     }
 
     UniValue result(UniValue::VOBJ);
     CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
     ssTx << psbtx;
     result.pushKV("psbt", EncodeBase64(ssTx.str()));
     result.pushKV("complete", complete);
 
     return result;
 }
 
 static UniValue walletcreatefundedpsbt(const Config &config,
                                        const JSONRPCRequest &request) {
     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     CWallet *const pwallet = wallet.get();
 
     if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
         return NullUniValue;
     }
 
     if (request.fHelp || request.params.size() < 2 ||
         request.params.size() > 5) {
         throw std::runtime_error(RPCHelpMan{
             "walletcreatefundedpsbt",
             "\nCreates and funds a transaction in the Partially Signed "
             "Transaction format. Inputs will be added if supplied inputs are "
             "not enough\n"
             "Implements the Creator and Updater roles.\n",
             {
                 {
                     "inputs",
                     RPCArg::Type::ARR,
                     RPCArg::Optional::NO,
                     "A json array of json objects",
                     {
                         {
                             "",
                             RPCArg::Type::OBJ,
                             RPCArg::Optional::OMITTED,
                             "",
                             {
                                 {"txid", RPCArg::Type::STR_HEX,
                                  RPCArg::Optional::NO, "The transaction id"},
                                 {"vout", RPCArg::Type::NUM,
                                  RPCArg::Optional::NO, "The output number"},
                                 {"sequence", RPCArg::Type::NUM,
                                  RPCArg::Optional::NO, "The sequence number"},
                             },
                         },
                     },
                 },
                 {
                     "outputs",
                     RPCArg::Type::ARR,
                     RPCArg::Optional::NO,
                     "a json array with outputs (key-value pairs).\n"
                     "For compatibility reasons, a dictionary, which holds "
                     "the key-value pairs directly, is also\n"
                     "                             accepted as second "
                     "parameter.",
                     {
                         {
                             "",
                             RPCArg::Type::OBJ,
                             RPCArg::Optional::OMITTED,
                             "",
                             {
                                 {"address", RPCArg::Type::AMOUNT,
                                  RPCArg::Optional::NO,
                                  "A key-value pair. The key (string) is the "
                                  "bitcoin address, the value (float or string) "
                                  "is the amount in " +
                                      CURRENCY_UNIT + ""},
                             },
                         },
                         {
                             "",
                             RPCArg::Type::OBJ,
                             RPCArg::Optional::OMITTED,
                             "",
                             {
                                 {"data", RPCArg::Type::STR_HEX,
                                  RPCArg::Optional::NO,
                                  "A key-value pair. The key must be \"data\", "
                                  "the value is hex-encoded data"},
                             },
                         },
                     },
                 },
                 {"locktime", RPCArg::Type::NUM, /* default */ "0",
                  "Raw locktime. Non-0 value also locktime-activates inputs\n"
                  "                             Allows this transaction to "
                  "be replaced by a transaction with higher fees. If provided, "
                  "it is an error if explicit sequence numbers are "
                  "incompatible."},
                 {"options",
                  RPCArg::Type::OBJ,
                  RPCArg::Optional::OMITTED_NAMED_ARG,
                  "",
                  {
                      {"changeAddress", RPCArg::Type::STR_HEX,
                       /* default */ "pool address",
                       "The bitcoin address to receive the change"},
                      {"changePosition", RPCArg::Type::NUM,
                       /* default */ "random", "The index of the change output"},
                      {"includeWatching", RPCArg::Type::BOOL,
                       /* default */ "false",
                       "Also select inputs which are watch only"},
                      {"lockUnspents", RPCArg::Type::BOOL, /* default */ "false",
                       "Lock selected unspent outputs"},
                      {"feeRate", RPCArg::Type::AMOUNT, /* default */
                       "not set: makes wallet determine the fee",
                       "Set a specific fee rate in " + CURRENCY_UNIT + "/kB"},
                      {
                          "subtractFeeFromOutputs",
                          RPCArg::Type::ARR,
                          /* default */ "empty array",
                          "A json array of integers.\n"
                          "                              The fee will be "
                          "equally deducted from the amount of each specified "
                          "output.\n"
                          "                              Those recipients will "
                          "receive less bitcoins than you enter in their "
                          "corresponding amount field.\n"
                          "                              If no outputs are "
                          "specified here, the sender pays the fee.",
                          {
                              {"vout_index", RPCArg::Type::NUM,
                               RPCArg::Optional::OMITTED,
                               "The zero-based output index, before a change "
                               "output is added."},
                          },
                      },
                  },
                  "options"},
                 {"bip32derivs", RPCArg::Type::BOOL, /* default */ "false",
                  "If true, includes the BIP 32 derivation paths for public "
                  "keys if we know them"},
             },
             RPCResult{"{\n"
                       "  \"psbt\": \"value\",        (string)  The resulting "
                       "raw transaction (base64-encoded string)\n"
                       "  \"fee\":       n,         (numeric) Fee in " +
                       CURRENCY_UNIT +
                       " the resulting transaction pays\n"
                       "  \"changepos\": n          (numeric) The position of "
                       "the added change output, or -1\n"
                       "}\n"},
             RPCExamples{"\nCreate a transaction with no inputs\n" +
                         HelpExampleCli(
                             "walletcreatefundedpsbt",
                             "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" "
                             "\"[{\\\"data\\\":\\\"00010203\\\"}]\"")},
         }
                                      .ToString());
     }
 
     RPCTypeCheck(request.params,
                  {UniValue::VARR,
                   UniValueType(), // ARR or OBJ, checked later
                   UniValue::VNUM, UniValue::VOBJ},
                  true);
 
     Amount fee;
     int change_position;
     CMutableTransaction rawTx =
         ConstructTransaction(config.GetChainParams(), request.params[0],
                              request.params[1], request.params[2]);
     FundTransaction(pwallet, rawTx, fee, change_position, request.params[3]);
 
     // Make a blank psbt
     PartiallySignedTransaction psbtx(rawTx);
 
     // Fill transaction with out data but don't sign
     bool bip32derivs =
         request.params[4].isNull() ? false : request.params[4].get_bool();
     bool complete = true;
     const TransactionError err =
         FillPSBT(pwallet, psbtx, complete, SigHashType().withForkId(), false,
                  bip32derivs);
     if (err != TransactionError::OK) {
         throw JSONRPCTransactionError(err);
     }
 
     // Serialize the PSBT
     CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
     ssTx << psbtx;
 
     UniValue result(UniValue::VOBJ);
     result.pushKV("psbt", EncodeBase64(ssTx.str()));
     result.pushKV("fee", ValueFromAmount(fee));
     result.pushKV("changepos", change_position);
     return result;
 }
 
 // clang-format off
 static const CRPCCommand commands[] = {
     //  category            name                            actor (function)              argNames
     //  ------------------- ------------------------        ----------------------        ----------
     { "generating",         "generate",                     generate,                     {"nblocks","maxtries"} },
     { "rawtransactions",    "fundrawtransaction",           fundrawtransaction,           {"hexstring","options"} },
     { "wallet",             "abandontransaction",           abandontransaction,           {"txid"} },
     { "wallet",             "addmultisigaddress",           addmultisigaddress,           {"nrequired","keys","label"} },
     { "wallet",             "backupwallet",                 backupwallet,                 {"destination"} },
-    { "wallet",             "createwallet",                 createwallet,                 {"wallet_name", "disable_private_keys", "blank"} },
+    { "wallet",             "createwallet",                 createwallet,                 {"wallet_name", "disable_private_keys", "blank", "passphrase"} },
     { "wallet",             "encryptwallet",                encryptwallet,                {"passphrase"} },
     { "wallet",             "getaddressesbylabel",          getaddressesbylabel,          {"label"} },
     { "wallet",             "getaddressinfo",               getaddressinfo,               {"address"} },
     { "wallet",             "getbalance",                   getbalance,                   {"dummy","minconf","include_watchonly"} },
     { "wallet",             "getnewaddress",                getnewaddress,                {"label", "address_type"} },
     { "wallet",             "getrawchangeaddress",          getrawchangeaddress,          {"address_type"} },
     { "wallet",             "getreceivedbyaddress",         getreceivedbyaddress,         {"address","minconf"} },
     { "wallet",             "getreceivedbylabel",           getreceivedbylabel,           {"label","minconf"} },
     { "wallet",             "gettransaction",               gettransaction,               {"txid","include_watchonly"} },
     { "wallet",             "getunconfirmedbalance",        getunconfirmedbalance,        {} },
     { "wallet",             "getwalletinfo",                getwalletinfo,                {} },
     { "wallet",             "keypoolrefill",                keypoolrefill,                {"newsize"} },
     { "wallet",             "listaddressgroupings",         listaddressgroupings,         {} },
     { "wallet",             "listlabels",                   listlabels,                   {"purpose"} },
     { "wallet",             "listlockunspent",              listlockunspent,              {} },
     { "wallet",             "listreceivedbyaddress",        listreceivedbyaddress,        {"minconf","include_empty","include_watchonly","address_filter"} },
     { "wallet",             "listreceivedbylabel",          listreceivedbylabel,          {"minconf","include_empty","include_watchonly"} },
     { "wallet",             "listsinceblock",               listsinceblock,               {"blockhash","target_confirmations","include_watchonly","include_removed"} },
     { "wallet",             "listtransactions",             listtransactions,             {"label|dummy","count","skip","include_watchonly"} },
     { "wallet",             "listunspent",                  listunspent,                  {"minconf","maxconf","addresses","include_unsafe","query_options"} },
     { "wallet",             "listwalletdir",                listwalletdir,                {} },
     { "wallet",             "listwallets",                  listwallets,                  {} },
     { "wallet",             "loadwallet",                   loadwallet,                   {"filename"} },
     { "wallet",             "lockunspent",                  lockunspent,                  {"unlock","transactions"} },
     { "wallet",             "rescanblockchain",             rescanblockchain,             {"start_height", "stop_height"} },
     { "wallet",             "sendmany",                     sendmany,                     {"dummy","amounts","minconf","comment","subtractfeefrom"} },
     { "wallet",             "sendtoaddress",                sendtoaddress,                {"address","amount","comment","comment_to","subtractfeefromamount"} },
     { "wallet",             "sethdseed",                    sethdseed,                    {"newkeypool","seed"} },
     { "wallet",             "setlabel",                     setlabel,                     {"address","label"} },
     { "wallet",             "settxfee",                     settxfee,                     {"amount"} },
     { "wallet",             "signmessage",                  signmessage,                  {"address","message"} },
     { "wallet",             "signrawtransactionwithwallet", signrawtransactionwithwallet, {"hextring","prevtxs","sighashtype"} },
     { "wallet",             "unloadwallet",                 unloadwallet,                 {"wallet_name"} },
     { "wallet",             "walletcreatefundedpsbt",       walletcreatefundedpsbt,       {"inputs","outputs","locktime","options","bip32derivs"} },
     { "wallet",             "walletlock",                   walletlock,                   {} },
     { "wallet",             "walletpassphrase",             walletpassphrase,             {"passphrase","timeout"} },
     { "wallet",             "walletpassphrasechange",       walletpassphrasechange,       {"oldpassphrase","newpassphrase"} },
     { "wallet",             "walletprocesspsbt",            walletprocesspsbt,            {"psbt","sign","sighashtype","bip32derivs"} },
 };
 // clang-format on
 
 void RegisterWalletRPCCommands(
     interfaces::Chain &chain,
     std::vector<std::unique_ptr<interfaces::Handler>> &handlers) {
     for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) {
         handlers.emplace_back(chain.handleRpc(commands[vcidx]));
     }
 }
diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py
index a7b09dae0..810ac2162 100755
--- a/test/functional/wallet_createwallet.py
+++ b/test/functional/wallet_createwallet.py
@@ -1,128 +1,179 @@
 #!/usr/bin/env python3
 # Copyright (c) 2018 The Bitcoin Core developers
 # Distributed under the MIT software license, see the accompanying
 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
 """Test createwallet arguments.
 """
 
 from test_framework.test_framework import BitcoinTestFramework
 from test_framework.util import (
     assert_equal,
     assert_raises_rpc_error,
 )
 
 
 class CreateWalletTest(BitcoinTestFramework):
     def set_test_params(self):
         self.setup_clean_chain = False
         self.num_nodes = 1
         self.supports_cli = True
 
     def skip_test_if_missing_module(self):
         self.skip_if_no_wallet()
 
     def run_test(self):
         node = self.nodes[0]
         # Leave IBD for sethdseed
         node.generate(1)
 
         self.nodes[0].createwallet(wallet_name='w0')
         w0 = node.get_wallet_rpc('w0')
         address1 = w0.getnewaddress()
 
         self.log.info("Test disableprivatekeys creation.")
         self.nodes[0].createwallet(wallet_name='w1', disable_private_keys=True)
         w1 = node.get_wallet_rpc('w1')
         assert_raises_rpc_error(-4,
                                 "Error: This wallet has no available keys", w1.getnewaddress)
         assert_raises_rpc_error(-4, "Error: This wallet has no available keys",
                                 w1.getrawchangeaddress)
         w1.importpubkey(w0.getaddressinfo(address1)['pubkey'])
 
         self.log.info('Test that private keys cannot be imported')
         addr = w0.getnewaddress('', 'legacy')
         privkey = w0.dumpprivkey(addr)
         assert_raises_rpc_error(
             -4, 'Cannot import private keys to a wallet with private keys disabled', w1.importprivkey, privkey)
         result = w1.importmulti(
             [{'scriptPubKey': {'address': addr}, 'timestamp': 'now', 'keys': [privkey]}])
         assert(not result[0]['success'])
         assert('warning' not in result[0])
         assert_equal(result[0]['error']['code'], -4)
         assert_equal(result[0]['error']['message'],
                      'Cannot import private keys to a wallet with private keys disabled')
 
         self.log.info("Test blank creation with private keys disabled.")
         self.nodes[0].createwallet(
             wallet_name='w2', disable_private_keys=True, blank=True)
         w2 = node.get_wallet_rpc('w2')
         assert_raises_rpc_error(-4,
                                 "Error: This wallet has no available keys", w2.getnewaddress)
         assert_raises_rpc_error(-4, "Error: This wallet has no available keys",
                                 w2.getrawchangeaddress)
         w2.importpubkey(w0.getaddressinfo(address1)['pubkey'])
 
         self.log.info("Test blank creation with private keys enabled.")
         self.nodes[0].createwallet(
             wallet_name='w3', disable_private_keys=False, blank=True)
         w3 = node.get_wallet_rpc('w3')
         assert_equal(w3.getwalletinfo()['keypoolsize'], 0)
         assert_raises_rpc_error(-4,
                                 "Error: This wallet has no available keys", w3.getnewaddress)
         assert_raises_rpc_error(-4, "Error: This wallet has no available keys",
                                 w3.getrawchangeaddress)
         # Import private key
         w3.importprivkey(w0.dumpprivkey(address1))
         # Imported private keys are currently ignored by the keypool
         assert_equal(w3.getwalletinfo()['keypoolsize'], 0)
         assert_raises_rpc_error(-4,
                                 "Error: This wallet has no available keys", w3.getnewaddress)
         # Set the seed
         w3.sethdseed()
         assert_equal(w3.getwalletinfo()['keypoolsize'], 1)
         w3.getnewaddress()
         w3.getrawchangeaddress()
 
         self.log.info(
             "Test blank creation with privkeys enabled and then encryption")
         self.nodes[0].createwallet(
             wallet_name='w4', disable_private_keys=False, blank=True)
         w4 = node.get_wallet_rpc('w4')
         assert_equal(w4.getwalletinfo()['keypoolsize'], 0)
         assert_raises_rpc_error(-4,
                                 "Error: This wallet has no available keys", w4.getnewaddress)
         assert_raises_rpc_error(-4, "Error: This wallet has no available keys",
                                 w4.getrawchangeaddress)
         # Encrypt the wallet. Nothing should change about the keypool
         w4.encryptwallet('pass')
         assert_raises_rpc_error(-4,
                                 "Error: This wallet has no available keys", w4.getnewaddress)
         assert_raises_rpc_error(-4, "Error: This wallet has no available keys",
                                 w4.getrawchangeaddress)
         # Now set a seed and it should work. Wallet should also be encrypted
         w4.walletpassphrase('pass', 2)
         w4.sethdseed()
         w4.getnewaddress()
         w4.getrawchangeaddress()
 
         self.log.info(
             "Test blank creation with privkeys disabled and then encryption")
         self.nodes[0].createwallet(
             wallet_name='w5', disable_private_keys=True, blank=True)
 
         w5 = node.get_wallet_rpc('w5')
         assert_equal(w5.getwalletinfo()['keypoolsize'], 0)
         assert_raises_rpc_error(-4,
                                 "Error: This wallet has no available keys", w5.getnewaddress)
         assert_raises_rpc_error(-4, "Error: This wallet has no available keys",
                                 w5.getrawchangeaddress)
         # Encrypt the wallet
         w5.encryptwallet('pass')
         assert_raises_rpc_error(-4,
                                 "Error: This wallet has no available keys", w5.getnewaddress)
         assert_raises_rpc_error(-4, "Error: This wallet has no available keys",
                                 w5.getrawchangeaddress)
 
+        self.log.info('New blank and encrypted wallets can be created')
+        self.nodes[0].createwallet(
+            wallet_name='wblank',
+            disable_private_keys=False,
+            blank=True,
+            passphrase='thisisapassphrase')
+        wblank = node.get_wallet_rpc('wblank')
+        assert_raises_rpc_error(
+            -13,
+            "Error: Please enter the wallet passphrase with walletpassphrase first.",
+            wblank.signmessage,
+            "needanargument",
+            "test")
+        wblank.walletpassphrase('thisisapassphrase', 10)
+        assert_raises_rpc_error(-4,
+                                "Error: This wallet has no available keys",
+                                wblank.getnewaddress)
+        assert_raises_rpc_error(-4,
+                                "Error: This wallet has no available keys",
+                                wblank.getrawchangeaddress)
+
+        self.log.info('Test creating a new encrypted wallet.')
+        # Born encrypted wallet is created (has keys)
+        self.nodes[0].createwallet(
+            wallet_name='w6',
+            disable_private_keys=False,
+            blank=False,
+            passphrase='thisisapassphrase')
+        w6 = node.get_wallet_rpc('w6')
+        assert_raises_rpc_error(
+            -13,
+            "Error: Please enter the wallet passphrase with walletpassphrase first.",
+            w6.signmessage,
+            "needanargument",
+            "test")
+        w6.walletpassphrase('thisisapassphrase', 10)
+        w6.signmessage(w6.getnewaddress('', 'legacy'), "test")
+        w6.keypoolrefill(1)
+        # There should only be 1 key
+        walletinfo = w6.getwalletinfo()
+        assert_equal(walletinfo['keypoolsize'], 1)
+        assert_equal(walletinfo['keypoolsize_hd_internal'], 1)
+        # Empty passphrase, error
+        assert_raises_rpc_error(-16,
+                                'Cannot encrypt a wallet with a blank password',
+                                self.nodes[0].createwallet,
+                                'w7',
+                                False,
+                                False,
+                                '')
+
 
 if __name__ == '__main__':
     CreateWalletTest().main()