aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/rpcwallet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/rpcwallet.cpp')
-rw-r--r--src/wallet/rpcwallet.cpp481
1 files changed, 216 insertions, 265 deletions
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 7d0219201e..bfac990639 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -8,6 +8,8 @@
#include <consensus/validation.h>
#include <core_io.h>
#include <httpserver.h>
+#include <init.h>
+#include <interfaces/chain.h>
#include <validation.h>
#include <key_io.h>
#include <net.h>
@@ -23,8 +25,8 @@
#include <script/sign.h>
#include <shutdown.h>
#include <timedata.h>
-#include <util.h>
-#include <utilmoneystr.h>
+#include <util/system.h>
+#include <util/moneystr.h>
#include <wallet/coincontrol.h>
#include <wallet/feebumper.h>
#include <wallet/rpcwallet.h>
@@ -89,9 +91,9 @@ void EnsureWalletIsUnlocked(CWallet * const pwallet)
}
}
-static void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static void WalletTxToJSON(interfaces::Chain& chain, interfaces::Chain::Lock& locked_chain, const CWalletTx& wtx, UniValue& entry)
{
- int confirms = wtx.GetDepthInMainChain();
+ int confirms = wtx.GetDepthInMainChain(locked_chain);
entry.pushKV("confirmations", confirms);
if (wtx.IsCoinBase())
entry.pushKV("generated", true);
@@ -101,7 +103,7 @@ static void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) EXCLUSIVE_LOCK
entry.pushKV("blockindex", wtx.nIndex);
entry.pushKV("blocktime", LookupBlockIndex(wtx.hashBlock)->GetBlockTime());
} else {
- entry.pushKV("trusted", wtx.IsTrusted());
+ entry.pushKV("trusted", wtx.IsTrusted(locked_chain));
}
uint256 hash = wtx.GetHash();
entry.pushKV("txid", hash.GetHex());
@@ -290,7 +292,7 @@ static UniValue setlabel(const JSONRPCRequest& request)
}
-static CTransactionRef SendMoney(CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, const CCoinControl& coin_control, mapValue_t mapValue)
+static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, const CCoinControl& coin_control, mapValue_t mapValue)
{
CAmount curBalance = pwallet->GetBalance();
@@ -317,7 +319,7 @@ static CTransactionRef SendMoney(CWallet * const pwallet, const CTxDestination &
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
vecSend.push_back(recipient);
CTransactionRef tx;
- if (!pwallet->CreateTransaction(vecSend, tx, reservekey, nFeeRequired, nChangePosRet, strError, coin_control)) {
+ if (!pwallet->CreateTransaction(locked_chain, vecSend, tx, reservekey, nFeeRequired, nChangePosRet, strError, coin_control)) {
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);
@@ -373,7 +375,8 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
CTxDestination dest = DecodeDestination(request.params[0].get_str());
if (!IsValidDestination(dest)) {
@@ -415,7 +418,7 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
EnsureWalletIsUnlocked(pwallet);
- CTransactionRef tx = SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, coin_control, std::move(mapValue));
+ CTransactionRef tx = SendMoney(*locked_chain, pwallet, dest, nAmount, fSubtractFeeFromAmount, coin_control, std::move(mapValue));
return tx->GetHash().GetHex();
}
@@ -455,10 +458,11 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
UniValue jsonGroupings(UniValue::VARR);
- std::map<CTxDestination, CAmount> balances = pwallet->GetAddressBalances();
+ std::map<CTxDestination, CAmount> balances = pwallet->GetAddressBalances(*locked_chain);
for (const std::set<CTxDestination>& grouping : pwallet->GetAddressGroupings()) {
UniValue jsonGrouping(UniValue::VARR);
for (const CTxDestination& address : grouping)
@@ -504,11 +508,12 @@ static UniValue signmessage(const JSONRPCRequest& request)
+ HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") +
"\nVerify the signature\n"
+ HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"signature\" \"my message\"") +
- "\nAs json rpc\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\"")
);
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
@@ -566,7 +571,7 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
+ HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 0") +
"\nThe amount with at least 6 confirmations\n"
+ HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 6") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6")
);
@@ -574,7 +579,9 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
// Bitcoin address
CTxDestination dest = DecodeDestination(request.params[0].get_str());
@@ -600,7 +607,7 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
for (const CTxOut& txout : wtx.tx->vout)
if (txout.scriptPubKey == scriptPubKey)
- if (wtx.GetDepthInMainChain() >= nMinDepth)
+ if (wtx.GetDepthInMainChain(*locked_chain) >= nMinDepth)
nAmount += txout.nValue;
}
@@ -633,7 +640,7 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
+ HelpExampleCli("getreceivedbylabel", "\"tabby\" 0") +
"\nThe amount with at least 6 confirmations\n"
+ HelpExampleCli("getreceivedbylabel", "\"tabby\" 6") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6")
);
@@ -641,7 +648,9 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
// Minimum confirmations
int nMinDepth = 1;
@@ -663,7 +672,7 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
{
CTxDestination address;
if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwallet, address) && setAddress.count(address)) {
- if (wtx.GetDepthInMainChain() >= nMinDepth)
+ if (wtx.GetDepthInMainChain(*locked_chain) >= nMinDepth)
nAmount += txout.nValue;
}
}
@@ -699,7 +708,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
+ HelpExampleCli("getbalance", "") +
"\nThe total amount in the wallet at least 6 blocks confirmed\n"
+ HelpExampleCli("getbalance", "\"*\" 6") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("getbalance", "\"*\", 6")
);
@@ -707,7 +716,8 @@ static UniValue getbalance(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ 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() != "*") {
@@ -745,7 +755,8 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
return ValueFromAmount(pwallet->GetUnconfirmedBalance());
}
@@ -798,7 +809,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
+ 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"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("sendmany", "\"\", {\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\"")
);
@@ -806,7 +817,8 @@ static UniValue sendmany(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
if (pwallet->GetBroadcastTransactions() && !g_connman) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
@@ -892,7 +904,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
int nChangePosRet = -1;
std::string strFailReason;
CTransactionRef tx;
- bool fCreated = pwallet->CreateTransaction(vecSend, tx, keyChange, nFeeRequired, nChangePosRet, strFailReason, coin_control);
+ bool fCreated = pwallet->CreateTransaction(*locked_chain, vecSend, tx, keyChange, nFeeRequired, nChangePosRet, strFailReason, coin_control);
if (!fCreated)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
CValidationState state;
@@ -939,13 +951,14 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
"\nExamples:\n"
"\nAdd a multisig address from 2 addresses\n"
+ HelpExampleCli("addmultisigaddress", "2 \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") +
- "\nAs json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("addmultisigaddress", "2, \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"")
;
throw std::runtime_error(msg);
}
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
std::string label;
if (!request.params[2].isNull())
@@ -982,131 +995,6 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
return result;
}
-class Witnessifier : public boost::static_visitor<bool>
-{
-public:
- CWallet * const pwallet;
- CTxDestination result;
- bool already_witness;
-
- explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet), already_witness(false) {}
-
- bool operator()(const CKeyID &keyID) {
- if (pwallet) {
- CScript basescript = GetScriptForDestination(keyID);
- CScript witscript = GetScriptForWitness(basescript);
- if (!IsSolvable(*pwallet, witscript)) {
- return false;
- }
- return ExtractDestination(witscript, result);
- }
- return false;
- }
-
- bool operator()(const CScriptID &scriptID) {
- CScript subscript;
- if (pwallet && pwallet->GetCScript(scriptID, subscript)) {
- int witnessversion;
- std::vector<unsigned char> witprog;
- if (subscript.IsWitnessProgram(witnessversion, witprog)) {
- ExtractDestination(subscript, result);
- already_witness = true;
- return true;
- }
- CScript witscript = GetScriptForWitness(subscript);
- if (!IsSolvable(*pwallet, witscript)) {
- return false;
- }
- return ExtractDestination(witscript, result);
- }
- return false;
- }
-
- bool operator()(const WitnessV0KeyHash& id)
- {
- already_witness = true;
- result = id;
- return true;
- }
-
- bool operator()(const WitnessV0ScriptHash& id)
- {
- already_witness = true;
- result = id;
- return true;
- }
-
- template<typename T>
- bool operator()(const T& dest) { return false; }
-};
-
-static UniValue addwitnessaddress(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)
- {
- std::string msg = "addwitnessaddress \"address\" ( p2sh )\n"
- "\nDEPRECATED: set the address_type argument of getnewaddress, or option -addresstype=[bech32|p2sh-segwit] instead.\n"
- "Add a witness address for a script (with pubkey or redeemscript known). Requires a new wallet backup.\n"
- "It returns the witness script.\n"
-
- "\nArguments:\n"
- "1. \"address\" (string, required) An address known to the wallet\n"
- "2. p2sh (bool, optional, default=true) Embed inside P2SH\n"
-
- "\nResult:\n"
- "\"witnessaddress\", (string) The value of the new address (P2SH or BIP173).\n"
- "}\n"
- ;
- throw std::runtime_error(msg);
- }
-
- if (!IsDeprecatedRPCEnabled("addwitnessaddress")) {
- throw JSONRPCError(RPC_METHOD_DEPRECATED, "addwitnessaddress is deprecated and will be fully removed in v0.17. "
- "To use addwitnessaddress in v0.16, restart bitcoind with -deprecatedrpc=addwitnessaddress.\n"
- "Projects should transition to using the address_type argument of getnewaddress, or option -addresstype=[bech32|p2sh-segwit] instead.\n");
- }
-
- CTxDestination dest = DecodeDestination(request.params[0].get_str());
- if (!IsValidDestination(dest)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
- }
-
- bool p2sh = true;
- if (!request.params[1].isNull()) {
- p2sh = request.params[1].get_bool();
- }
-
- Witnessifier w(pwallet);
- bool ret = boost::apply_visitor(w, dest);
- if (!ret) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet, or the key is uncompressed");
- }
-
- CScript witprogram = GetScriptForDestination(w.result);
-
- if (p2sh) {
- w.result = CScriptID(witprogram);
- }
-
- if (w.already_witness) {
- if (!(dest == w.result)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Cannot convert between witness address types");
- }
- } else {
- pwallet->AddCScript(witprogram); // Implicit for single-key now, but necessary for multisig and for compatibility with older software
- pwallet->SetAddressBook(w.result, "", "receive");
- }
-
- return EncodeDestination(w.result);
-}
-
struct tallyitem
{
CAmount nAmount;
@@ -1121,8 +1009,10 @@ struct tallyitem
}
};
-static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, CWallet * const pwallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
+ LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
+
// Minimum confirmations
int nMinDepth = 1;
if (!params[0].isNull())
@@ -1156,7 +1046,7 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo
if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
continue;
- int nDepth = wtx.GetDepthInMainChain();
+ int nDepth = wtx.GetDepthInMainChain(locked_chain);
if (nDepth < nMinDepth)
continue;
@@ -1310,9 +1200,10 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
- return ListReceived(pwallet, request.params, false);
+ return ListReceived(*locked_chain, pwallet, request.params, false);
}
static UniValue listreceivedbylabel(const JSONRPCRequest& request)
@@ -1354,9 +1245,10 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
- return ListReceived(pwallet, request.params, true);
+ return ListReceived(*locked_chain, pwallet, request.params, true);
}
static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
@@ -1376,7 +1268,7 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
* @param ret The UniValue into which the result is stored.
* @param filter The "is mine" filter bool.
*/
-static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter)
{
CAmount nFee;
std::list<COutputEntry> listReceived;
@@ -1404,14 +1296,14 @@ static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, int n
entry.pushKV("vout", s.vout);
entry.pushKV("fee", ValueFromAmount(-nFee));
if (fLong)
- WalletTxToJSON(wtx, entry);
+ WalletTxToJSON(pwallet->chain(), locked_chain, wtx, entry);
entry.pushKV("abandoned", wtx.isAbandoned());
ret.push_back(entry);
}
}
// Received
- if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth)
+ if (listReceived.size() > 0 && wtx.GetDepthInMainChain(locked_chain) >= nMinDepth)
{
for (const COutputEntry& r : listReceived)
{
@@ -1426,9 +1318,9 @@ static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, int n
MaybePushAddress(entry, r.destination);
if (wtx.IsCoinBase())
{
- if (wtx.GetDepthInMainChain() < 1)
+ if (wtx.GetDepthInMainChain(locked_chain) < 1)
entry.pushKV("category", "orphan");
- else if (wtx.IsImmatureCoinBase())
+ else if (wtx.IsImmatureCoinBase(locked_chain))
entry.pushKV("category", "immature");
else
entry.pushKV("category", "generate");
@@ -1443,7 +1335,7 @@ static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, int n
}
entry.pushKV("vout", r.vout);
if (fLong)
- WalletTxToJSON(wtx, entry);
+ WalletTxToJSON(pwallet->chain(), locked_chain, wtx, entry);
ret.push_back(entry);
}
}
@@ -1500,7 +1392,7 @@ UniValue listtransactions(const JSONRPCRequest& request)
+ HelpExampleCli("listtransactions", "") +
"\nList transactions 100 to 120\n"
+ HelpExampleCli("listtransactions", "\"*\" 20 100") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("listtransactions", "\"*\", 20, 100")
);
@@ -1530,7 +1422,8 @@ UniValue listtransactions(const JSONRPCRequest& request)
UniValue ret(UniValue::VARR);
{
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
const CWallet::TxItems & txOrdered = pwallet->wtxOrdered;
@@ -1538,7 +1431,7 @@ UniValue listtransactions(const JSONRPCRequest& request)
for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
{
CWalletTx *const pwtx = (*it).second;
- ListTransactions(pwallet, *pwtx, 0, true, ret, filter);
+ ListTransactions(*locked_chain, pwallet, *pwtx, 0, true, ret, filter);
if ((int)ret.size() >= (nCount+nFrom)) break;
}
}
@@ -1630,7 +1523,8 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
const CBlockIndex* pindex = nullptr; // Block index of the specified block or the common ancestor, if the block provided was in a deactivated chain.
const CBlockIndex* paltindex = nullptr; // Block index of the specified block, even if it's in a deactivated chain.
@@ -1673,8 +1567,8 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
CWalletTx tx = pairWtx.second;
- if (depth == -1 || tx.GetDepthInMainChain() < depth) {
- ListTransactions(pwallet, tx, 0, true, transactions, filter);
+ if (depth == -1 || tx.GetDepthInMainChain(*locked_chain) < depth) {
+ ListTransactions(*locked_chain, pwallet, tx, 0, true, transactions, filter);
}
}
@@ -1691,7 +1585,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
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(pwallet, it->second, -100000000, true, removed, filter);
+ ListTransactions(*locked_chain, pwallet, it->second, -100000000, true, removed, filter);
}
}
paltindex = paltindex->pprev;
@@ -1765,7 +1659,8 @@ static UniValue gettransaction(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
uint256 hash(ParseHashV(request.params[0], "txid"));
@@ -1781,7 +1676,7 @@ static UniValue gettransaction(const JSONRPCRequest& request)
}
const CWalletTx& wtx = it->second;
- CAmount nCredit = wtx.GetCredit(filter);
+ CAmount nCredit = wtx.GetCredit(*locked_chain, filter);
CAmount nDebit = wtx.GetDebit(filter);
CAmount nNet = nCredit - nDebit;
CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0);
@@ -1790,10 +1685,10 @@ static UniValue gettransaction(const JSONRPCRequest& request)
if (wtx.IsFromMe(filter))
entry.pushKV("fee", ValueFromAmount(nFee));
- WalletTxToJSON(wtx, entry);
+ WalletTxToJSON(pwallet->chain(), *locked_chain, wtx, entry);
UniValue details(UniValue::VARR);
- ListTransactions(pwallet, wtx, 0, false, details, filter);
+ ListTransactions(*locked_chain, pwallet, wtx, 0, false, details, filter);
entry.pushKV("details", details);
std::string strHex = EncodeHexTx(*wtx.tx, RPCSerializationFlags());
@@ -1832,14 +1727,15 @@ static UniValue abandontransaction(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
uint256 hash(ParseHashV(request.params[0], "txid"));
if (!pwallet->mapWallet.count(hash)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
}
- if (!pwallet->AbandonTransaction(hash)) {
+ if (!pwallet->AbandonTransaction(*locked_chain, hash)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not eligible for abandonment");
}
@@ -1871,7 +1767,8 @@ static UniValue backupwallet(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
std::string strDest = request.params[0].get_str();
if (!pwallet->BackupWallet(strDest)) {
@@ -1907,7 +1804,8 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet");
}
- LOCK2(cs_main, pwallet->cs_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;
@@ -1928,13 +1826,6 @@ static UniValue keypoolrefill(const JSONRPCRequest& request)
}
-static void LockWallet(CWallet* pWallet)
-{
- LOCK(pWallet->cs_wallet);
- pWallet->nRelockTime = 0;
- pWallet->Lock();
-}
-
static UniValue walletpassphrase(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
@@ -1960,12 +1851,13 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
+ HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") +
"\nLock the wallet again (before 60 seconds)\n"
+ HelpExampleCli("walletlock", "") +
- "\nAs json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60")
);
}
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
if (!pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
@@ -2004,7 +1896,18 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
pwallet->TopUpKeyPool();
pwallet->nRelockTime = GetTime() + nSleepTime;
- RPCRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), std::bind(LockWallet, pwallet), 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;
+ 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;
}
@@ -2032,7 +1935,8 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request)
);
}
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
if (!pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
@@ -2083,12 +1987,13 @@ static UniValue walletlock(const JSONRPCRequest& request)
+ HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 1.0") +
"\nClear the passphrase since we are done before 2 minutes is up\n"
+ HelpExampleCli("walletlock", "") +
- "\nAs json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("walletlock", "")
);
}
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
if (!pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
@@ -2129,12 +2034,13 @@ static UniValue encryptwallet(const JSONRPCRequest& request)
+ HelpExampleCli("signmessage", "\"address\" \"test message\"") +
"\nNow lock the wallet again by removing the passphrase\n"
+ HelpExampleCli("walletlock", "") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("encryptwallet", "\"my pass phrase\"")
);
}
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
if (pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called.");
@@ -2200,7 +2106,7 @@ static UniValue lockunspent(const JSONRPCRequest& request)
+ HelpExampleCli("listlockunspent", "") +
"\nUnlock the transaction again\n"
+ HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"")
);
@@ -2208,7 +2114,8 @@ static UniValue lockunspent(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
RPCTypeCheckArgument(request.params[0], UniValue::VBOOL);
@@ -2257,7 +2164,7 @@ static UniValue lockunspent(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout index out of bounds");
}
- if (pwallet->IsSpent(outpt.hash, outpt.n)) {
+ if (pwallet->IsSpent(*locked_chain, outpt.hash, outpt.n)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected unspent output");
}
@@ -2314,11 +2221,12 @@ static UniValue listlockunspent(const JSONRPCRequest& request)
+ HelpExampleCli("listlockunspent", "") +
"\nUnlock the transaction again\n"
+ HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") +
- "\nAs a json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("listlockunspent", "")
);
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
std::vector<COutPoint> vOutpts;
pwallet->ListLockedCoins(vOutpts);
@@ -2359,7 +2267,8 @@ static UniValue settxfee(const JSONRPCRequest& request)
);
}
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
CAmount nAmount = AmountFromValue(request.params[0]);
CFeeRate tx_fee_rate(nAmount, 1000);
@@ -2414,7 +2323,8 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
UniValue obj(UniValue::VOBJ);
@@ -2443,6 +2353,38 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
return obj;
}
+static UniValue listwalletdir(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 0) {
+ throw std::runtime_error(
+ "listwalletdir\n"
+ "Returns a list of wallets in the wallet directory.\n"
+ "{\n"
+ " \"wallets\" : [ (json array of objects)\n"
+ " {\n"
+ " \"name\" : \"name\" (string) The wallet name\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ "}\n"
+ "\nExamples:\n"
+ + HelpExampleCli("listwalletdir", "")
+ + HelpExampleRpc("listwalletdir", "")
+ );
+ }
+
+ 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 JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
@@ -2494,26 +2436,26 @@ static UniValue loadwallet(const JSONRPCRequest& request)
+ HelpExampleCli("loadwallet", "\"test.dat\"")
+ HelpExampleRpc("loadwallet", "\"test.dat\"")
);
- std::string wallet_file = request.params[0].get_str();
+
+ WalletLocation location(request.params[0].get_str());
std::string error;
- fs::path wallet_path = fs::absolute(wallet_file, GetWalletDir());
- if (fs::symlink_status(wallet_path).type() == fs::file_not_found) {
- throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Wallet " + wallet_file + " not found.");
- } else if (fs::is_directory(wallet_path)) {
+ 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 = wallet_path / "wallet.dat";
+ 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 " + wallet_file + " does not contain a wallet.dat file.");
+ throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Directory " + location.GetName() + " does not contain a wallet.dat file.");
}
}
std::string warning;
- if (!CWallet::Verify(wallet_file, false, error, warning)) {
+ if (!CWallet::Verify(*g_rpc_interfaces->chain, location, false, error, warning)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed: " + error);
}
- std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(wallet_file, fs::absolute(wallet_file, GetWalletDir()));
+ std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(*g_rpc_interfaces->chain, location);
if (!wallet) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet loading failed.");
}
@@ -2547,7 +2489,6 @@ static UniValue createwallet(const JSONRPCRequest& request)
+ HelpExampleRpc("createwallet", "\"testwallet\"")
);
}
- std::string wallet_name = request.params[0].get_str();
std::string error;
std::string warning;
@@ -2556,17 +2497,17 @@ static UniValue createwallet(const JSONRPCRequest& request)
disable_privatekeys = request.params[1].get_bool();
}
- fs::path wallet_path = fs::absolute(wallet_name, GetWalletDir());
- if (fs::symlink_status(wallet_path).type() != fs::file_not_found) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Wallet " + wallet_name + " already exists.");
+ 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 duplication name.
- if (!CWallet::Verify(wallet_name, false, error, warning)) {
+ if (!CWallet::Verify(*g_rpc_interfaces->chain, location, false, error, warning)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed: " + error);
}
- std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(wallet_name, fs::absolute(wallet_name, GetWalletDir()), (disable_privatekeys ? (uint64_t)WALLET_FLAG_DISABLE_PRIVATE_KEYS : 0));
+ std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(*g_rpc_interfaces->chain, location, (disable_privatekeys ? (uint64_t)WALLET_FLAG_DISABLE_PRIVATE_KEYS : 0));
if (!wallet) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet creation failed.");
}
@@ -2652,13 +2593,14 @@ static UniValue resendwallettransactions(const JSONRPCRequest& request)
if (!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
if (!pwallet->GetBroadcastTransactions()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet transaction broadcasting is disabled with -walletbroadcast");
}
- std::vector<uint256> txids = pwallet->ResendWalletTransactionsBefore(GetTime(), g_connman.get());
+ std::vector<uint256> txids = pwallet->ResendWalletTransactionsBefore(*locked_chain, GetTime(), g_connman.get());
UniValue result(UniValue::VARR);
for (const uint256& txid : txids)
{
@@ -2789,8 +2731,9 @@ static UniValue listunspent(const JSONRPCRequest& request)
UniValue results(UniValue::VARR);
std::vector<COutput> vecOutputs;
{
- LOCK2(cs_main, pwallet->cs_wallet);
- pwallet->AvailableCoins(vecOutputs, !include_unsafe, nullptr, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth);
+ 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);
@@ -3108,10 +3051,11 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
}
// Sign the transaction
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
- return SignTransaction(mtx, request.params[1], pwallet, false, request.params[2]);
+ return SignTransaction(pwallet->chain(), mtx, request.params[1], pwallet, false, request.params[2]);
}
static UniValue bumpfee(const JSONRPCRequest& request)
@@ -3213,7 +3157,8 @@ static UniValue bumpfee(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
@@ -3289,6 +3234,12 @@ UniValue generate(const JSONRPCRequest& request)
);
}
+ if (!IsDeprecatedRPCEnabled("generate")) {
+ throw JSONRPCError(RPC_METHOD_DEPRECATED, "The wallet generate rpc method is deprecated and will be fully removed in v0.19. "
+ "To use generate in v0.18, 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()) {
@@ -3347,7 +3298,7 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
CBlockIndex *pindexStop = nullptr;
CBlockIndex *pChainTip = nullptr;
{
- LOCK(cs_main);
+ auto locked_chain = pwallet->chain().lock();
pindexStart = chainActive.Genesis();
pChainTip = chainActive.Tip();
@@ -3371,7 +3322,7 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
// We can't rescan beyond non-pruned blocks, stop and throw an error
if (fPruneMode) {
- LOCK(cs_main);
+ auto locked_chain = pwallet->chain().lock();
CBlockIndex *block = pindexStop ? pindexStop : pChainTip;
while (block && block->nHeight >= pindexStart->nHeight) {
if (!(block->nStatus & BLOCK_HAVE_DATA)) {
@@ -3536,10 +3487,12 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
"\nResult:\n"
"{\n"
" \"address\" : \"address\", (string) The bitcoin address validated\n"
- " \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\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"
+ " \"solvable\" : true|false, (boolean) If the address is solvable by the wallet\n"
" \"iswatchonly\" : true|false, (boolean) If the address is watchonly\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"
" \"iswitness\" : true|false, (boolean) If the address is a witness address\n"
" \"witness_version\" : version (numeric, optional) The version number of the witness program\n"
" \"witness_program\" : \"hex\" (string, optional) The hex value of the witness program\n"
@@ -3592,11 +3545,13 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
isminetype mine = IsMine(*pwallet, dest);
ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE));
ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
+ ret.pushKV("solvable", IsSolvable(*pwallet, scriptPubKey));
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()) {
@@ -3705,7 +3660,7 @@ static UniValue listlabels(const JSONRPCRequest& request)
+ HelpExampleCli("listlabels", "receive") +
"\nList labels that have sending addresses\n"
+ HelpExampleCli("listlabels", "send") +
- "\nAs json rpc call\n"
+ "\nAs a JSON-RPC call\n"
+ HelpExampleRpc("listlabels", "receive")
);
@@ -3767,7 +3722,8 @@ UniValue sethdseed(const JSONRPCRequest& request)
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot set a new HD seed while still in Initial Block Download");
}
- LOCK2(cs_main, pwallet->cs_wallet);
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
// Do not do anything to non-HD wallets
if (!pwallet->IsHDEnabled()) {
@@ -3816,24 +3772,34 @@ void AddKeypathToMap(const CWallet* pwallet, const CKeyID& keyID, std::map<CPubK
hd_keypaths.emplace(vchPubKey, std::move(info));
}
-bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, const CTransaction* txConst, int sighash_type, bool sign, bool bip32derivs)
+bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs)
{
LOCK(pwallet->cs_wallet);
// Get all of the previous transactions
bool complete = true;
- for (unsigned int i = 0; i < txConst->vin.size(); ++i) {
- const CTxIn& txin = txConst->vin[i];
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ const CTxIn& txin = psbtx.tx->vin[i];
PSBTInput& input = psbtx.inputs.at(i);
- // If we don't know about this input, skip it and let someone else deal with it
- const uint256& txhash = txin.prevout.hash;
- const auto it = pwallet->mapWallet.find(txhash);
- if (it != pwallet->mapWallet.end()) {
- const CWalletTx& wtx = it->second;
- CTxOut utxo = wtx.tx->vout[txin.prevout.n];
- // Update both UTXOs from the wallet.
- input.non_witness_utxo = wtx.tx;
- input.witness_utxo = utxo;
+ if (PSBTInputSigned(input)) {
+ continue;
+ }
+
+ // Verify input looks sane. This will check that we have at most one uxto, witness or non-witness.
+ if (!input.IsSane()) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "PSBT input is not sane.");
+ }
+
+ // If we have no utxo, grab it from the wallet.
+ if (!input.non_witness_utxo && input.witness_utxo.IsNull()) {
+ const uint256& txhash = txin.prevout.hash;
+ const auto it = pwallet->mapWallet.find(txhash);
+ if (it != pwallet->mapWallet.end()) {
+ const CWalletTx& wtx = it->second;
+ // We only need the non_witness_utxo, which is a superset of the witness_utxo.
+ // The signing code will switch to the smaller witness_utxo if this is ok.
+ input.non_witness_utxo = wtx.tx;
+ }
}
// Get the Sighash type
@@ -3841,12 +3807,12 @@ bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, const C
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Specified Sighash and sighash in PSBT do not match.");
}
- complete &= SignPSBTInput(HidingSigningProvider(pwallet, !sign, !bip32derivs), *psbtx.tx, input, i, sighash_type);
+ complete &= SignPSBTInput(HidingSigningProvider(pwallet, !sign, !bip32derivs), psbtx, i, sighash_type);
}
// Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
- for (unsigned int i = 0; i < txConst->vout.size(); ++i) {
- const CTxOut& out = txConst->vout.at(i);
+ for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
+ const CTxOut& out = psbtx.tx->vout.at(i);
PSBTOutput& psbt_out = psbtx.outputs.at(i);
// Fill a SignatureData with output info
@@ -3911,19 +3877,15 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
// Get the sighash type
int nHashType = ParseSighashString(request.params[2]);
- // Use CTransaction for the constant parts of the
- // transaction to avoid rehashing.
- const CTransaction txConst(*psbtx.tx);
-
// 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 = FillPSBT(pwallet, psbtx, &txConst, nHashType, sign, bip32derivs);
+ bool complete = FillPSBT(pwallet, psbtx, nHashType, sign, bip32derivs);
UniValue result(UniValue::VOBJ);
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
- result.pushKV("psbt", EncodeBase64((unsigned char*)ssTx.data(), ssTx.size()));
+ result.pushKV("psbt", EncodeBase64(ssTx.str()));
result.pushKV("complete", complete);
return result;
@@ -3959,7 +3921,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
" \"address\": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT + "\n"
" },\n"
" {\n"
- " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n"
+ " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex-encoded data\n"
" }\n"
" ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
" accepted as second parameter.\n"
@@ -4015,29 +3977,18 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
FundTransaction(pwallet, rawTx, fee, change_position, request.params[3]);
// Make a blank psbt
- PartiallySignedTransaction psbtx;
- psbtx.tx = rawTx;
- for (unsigned int i = 0; i < rawTx.vin.size(); ++i) {
- psbtx.inputs.push_back(PSBTInput());
- }
- for (unsigned int i = 0; i < rawTx.vout.size(); ++i) {
- psbtx.outputs.push_back(PSBTOutput());
- }
-
- // Use CTransaction for the constant parts of the
- // transaction to avoid rehashing.
- const CTransaction txConst(*psbtx.tx);
+ PartiallySignedTransaction psbtx(rawTx);
// Fill transaction with out data but don't sign
bool bip32derivs = request.params[4].isNull() ? false : request.params[4].get_bool();
- FillPSBT(pwallet, psbtx, &txConst, 1, false, bip32derivs);
+ FillPSBT(pwallet, psbtx, 1, false, bip32derivs);
// Serialize the PSBT
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
UniValue result(UniValue::VOBJ);
- result.pushKV("psbt", EncodeBase64((unsigned char*)ssTx.data(), ssTx.size()));
+ result.pushKV("psbt", EncodeBase64(ssTx.str()));
result.pushKV("fee", ValueFromAmount(fee));
result.pushKV("changepos", change_position);
return result;
@@ -4059,7 +4010,6 @@ static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
{ "generating", "generate", &generate, {"nblocks","maxtries"} },
- { "hidden", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} },
{ "hidden", "resendwallettransactions", &resendwallettransactions, {} },
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options","iswitness"} },
{ "wallet", "abandontransaction", &abandontransaction, {"txid"} },
@@ -4096,6 +4046,7 @@ static const CRPCCommand commands[] =
{ "wallet", "listsinceblock", &listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} },
{ "wallet", "listtransactions", &listtransactions, {"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"} },