aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/compat.h2
-rw-r--r--src/net.cpp1
-rw-r--r--src/netbase.cpp12
-rw-r--r--src/netbase.h4
-rw-r--r--src/qt/rpcconsole.cpp9
-rw-r--r--src/rpc/client.cpp1
-rw-r--r--src/wallet/rpcwallet.cpp132
-rw-r--r--src/wallet/test/wallet_tests.cpp4
-rw-r--r--src/wallet/wallet.h4
9 files changed, 131 insertions, 38 deletions
diff --git a/src/compat.h b/src/compat.h
index e76ab94c82..e022659c01 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -76,7 +76,7 @@ typedef unsigned int SOCKET;
size_t strnlen( const char *start, size_t max_len);
#endif // HAVE_DECL_STRNLEN
-bool static inline IsSelectableSocket(SOCKET s) {
+bool static inline IsSelectableSocket(const SOCKET& s) {
#ifdef WIN32
return true;
#else
diff --git a/src/net.cpp b/src/net.cpp
index 5bf3af7ea3..0994af3021 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -2076,6 +2076,7 @@ bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, b
// Set to non-blocking, incoming connections will also inherit this
if (!SetSocketNonBlocking(hListenSocket, true)) {
+ CloseSocket(hListenSocket);
strError = strprintf("BindListenPort: Setting listening socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
LogPrintf("%s\n", strError);
return false;
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 1f668a5d4c..8952468ecd 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -203,7 +203,7 @@ enum class IntrRecvError {
*
* @note This function requires that hSocket is in non-blocking mode.
*/
-static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, SOCKET& hSocket)
+static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, const SOCKET& hSocket)
{
int64_t curTime = GetTimeMillis();
int64_t endTime = curTime + timeout;
@@ -424,8 +424,10 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
SetSocketNoDelay(hSocket);
// Set to non-blocking
- if (!SetSocketNonBlocking(hSocket, true))
+ if (!SetSocketNonBlocking(hSocket, true)) {
+ CloseSocket(hSocket);
return error("ConnectSocketDirectly: Setting socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
+ }
if (connect(hSocket, (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR)
{
@@ -682,7 +684,7 @@ bool CloseSocket(SOCKET& hSocket)
return ret != SOCKET_ERROR;
}
-bool SetSocketNonBlocking(SOCKET& hSocket, bool fNonBlocking)
+bool SetSocketNonBlocking(const SOCKET& hSocket, bool fNonBlocking)
{
if (fNonBlocking) {
#ifdef WIN32
@@ -692,7 +694,6 @@ bool SetSocketNonBlocking(SOCKET& hSocket, bool fNonBlocking)
int fFlags = fcntl(hSocket, F_GETFL, 0);
if (fcntl(hSocket, F_SETFL, fFlags | O_NONBLOCK) == SOCKET_ERROR) {
#endif
- CloseSocket(hSocket);
return false;
}
} else {
@@ -703,7 +704,6 @@ bool SetSocketNonBlocking(SOCKET& hSocket, bool fNonBlocking)
int fFlags = fcntl(hSocket, F_GETFL, 0);
if (fcntl(hSocket, F_SETFL, fFlags & ~O_NONBLOCK) == SOCKET_ERROR) {
#endif
- CloseSocket(hSocket);
return false;
}
}
@@ -711,7 +711,7 @@ bool SetSocketNonBlocking(SOCKET& hSocket, bool fNonBlocking)
return true;
}
-bool SetSocketNoDelay(SOCKET& hSocket)
+bool SetSocketNoDelay(const SOCKET& hSocket)
{
int set = 1;
int rc = setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&set, sizeof(int));
diff --git a/src/netbase.h b/src/netbase.h
index fd4b34c8f1..941da31f9c 100644
--- a/src/netbase.h
+++ b/src/netbase.h
@@ -57,9 +57,9 @@ std::string NetworkErrorString(int err);
/** Close socket and set hSocket to INVALID_SOCKET */
bool CloseSocket(SOCKET& hSocket);
/** Disable or enable blocking-mode for a socket */
-bool SetSocketNonBlocking(SOCKET& hSocket, bool fNonBlocking);
+bool SetSocketNonBlocking(const SOCKET& hSocket, bool fNonBlocking);
/** Set the TCP_NODELAY flag on a socket */
-bool SetSocketNoDelay(SOCKET& hSocket);
+bool SetSocketNoDelay(const SOCKET& hSocket);
/**
* Convert milliseconds to a struct timeval for e.g. select.
*/
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index ec0580b81c..232068bf45 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -25,6 +25,7 @@
#ifdef ENABLE_WALLET
#include <db_cxx.h>
+#include <wallet/wallet.h>
#endif
#include <QKeyEvent>
@@ -301,6 +302,14 @@ bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string &
JSONRPCRequest req;
req.params = RPCConvertValues(stack.back()[0], std::vector<std::string>(stack.back().begin() + 1, stack.back().end()));
req.strMethod = stack.back()[0];
+#ifdef ENABLE_WALLET
+ // TODO: Move this logic to WalletModel
+ if (!vpwallets.empty()) {
+ // in Qt, use always the wallet with index 0 when running with multiple wallets
+ QByteArray encodedName = QUrl::toPercentEncoding(QString::fromStdString(vpwallets[0]->GetName()));
+ req.URI = "/wallet/"+std::string(encodedName.constData(), encodedName.length());
+ }
+#endif
lastResult = tableRPC.execute(req);
}
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 7c75586d03..4179453782 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -68,6 +68,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getblocktemplate", 0, "template_request" },
{ "listsinceblock", 1, "target_confirmations" },
{ "listsinceblock", 2, "include_watchonly" },
+ { "listsinceblock", 3, "include_removed" },
{ "sendmany", 1, "amounts" },
{ "sendmany", 2, "minconf" },
{ "sendmany", 4, "subtractfeefrom" },
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 4275a9181d..2b745c5040 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -622,7 +622,7 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request)
+ HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") +
"\nThe amount including unconfirmed transactions, zero confirmations\n"
+ HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 0") +
- "\nThe amount with at least 6 confirmation, very safe\n"
+ "\nThe amount with at least 6 confirmations\n"
+ HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 6") +
"\nAs a json rpc call\n"
+ HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6")
@@ -682,7 +682,7 @@ UniValue getreceivedbyaccount(const JSONRPCRequest& request)
+ HelpExampleCli("getreceivedbyaccount", "\"\"") +
"\nAmount received at the tabby account including unconfirmed amounts with zero confirmations\n"
+ HelpExampleCli("getreceivedbyaccount", "\"tabby\" 0") +
- "\nThe amount with at least 6 confirmation, very safe\n"
+ "\nThe amount with at least 6 confirmations\n"
+ HelpExampleCli("getreceivedbyaccount", "\"tabby\" 6") +
"\nAs a json rpc call\n"
+ HelpExampleRpc("getreceivedbyaccount", "\"tabby\", 6")
@@ -1426,6 +1426,17 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
entry.push_back(Pair("address", addr.ToString()));
}
+/**
+ * List transactions based on the given criteria.
+ *
+ * @param pwallet The wallet.
+ * @param wtx The wallet transaction.
+ * @param strAccount The account, if any, or "*" for all.
+ * @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 The "is mine" filter bool.
+ */
void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, const std::string& strAccount, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter)
{
CAmount nFee;
@@ -1742,14 +1753,18 @@ UniValue listsinceblock(const JSONRPCRequest& request)
return NullUniValue;
}
- if (request.fHelp || request.params.size() > 3)
+ if (request.fHelp || request.params.size() > 4)
throw std::runtime_error(
- "listsinceblock ( \"blockhash\" target_confirmations include_watchonly)\n"
- "\nGet all transactions in blocks since block [blockhash], or all transactions if omitted\n"
+ "listsinceblock ( \"blockhash\" target_confirmations include_watchonly include_removed )\n"
+ "\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"
"\nArguments:\n"
"1. \"blockhash\" (string, optional) The block hash to list transactions since\n"
- "2. target_confirmations: (numeric, optional) The confirmations required, must be 1 or more\n"
- "3. include_watchonly: (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')"
+ "2. target_confirmations: (numeric, optional, default=1) The confirmations required, must be 1 or more\n"
+ "3. include_watchonly: (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')\n"
+ "4. include_removed: (bool, optional, default=true) Show transactions that were removed due to a reorg in the \"removed\" array\n"
+ " (not guaranteed to work on pruned nodes)\n"
"\nResult:\n"
"{\n"
" \"transactions\": [\n"
@@ -1774,7 +1789,11 @@ UniValue listsinceblock(const JSONRPCRequest& request)
" \"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"
+ " ],\n"
+ " \"removed\": [\n"
+ " <structure is the same as \"transactions\" above, only present if include_removed=true>\n"
+ " Note: transactions that were readded 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 last block\n"
"}\n"
"\nExamples:\n"
@@ -1785,21 +1804,19 @@ UniValue listsinceblock(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet);
- const CBlockIndex *pindex = NULL;
+ const CBlockIndex* pindex = NULL; // Block index of the specified block or the common ancestor, if the block provided was in a deactivated chain.
+ const CBlockIndex* paltindex = NULL; // Block index of the specified block, even if it's in a deactivated chain.
int target_confirms = 1;
isminefilter filter = ISMINE_SPENDABLE;
- if (!request.params[0].isNull())
- {
+ if (!request.params[0].isNull()) {
uint256 blockId;
blockId.SetHex(request.params[0].get_str());
BlockMap::iterator it = mapBlockIndex.find(blockId);
- if (it != mapBlockIndex.end())
- {
- pindex = it->second;
- if (chainActive[pindex->nHeight] != pindex)
- {
+ if (it != mapBlockIndex.end()) {
+ paltindex = pindex = it->second;
+ if (chainActive[pindex->nHeight] != pindex) {
// the block being asked for is a part of a deactivated chain;
// we don't want to depend on its perceived height in the block
// chain, we want to instead use the last common ancestor
@@ -1808,19 +1825,20 @@ UniValue listsinceblock(const JSONRPCRequest& request)
}
}
- if (!request.params[1].isNull())
- {
+ if (!request.params[1].isNull()) {
target_confirms = request.params[1].get_int();
- if (target_confirms < 1)
+ if (target_confirms < 1) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
+ }
}
- if (request.params.size() > 2 && request.params[2].get_bool())
- {
+ 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());
+
int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1;
UniValue transactions(UniValue::VARR);
@@ -1828,8 +1846,27 @@ UniValue listsinceblock(const JSONRPCRequest& request)
for (const std::pair<uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
CWalletTx tx = pairWtx.second;
- if (depth == -1 || tx.GetDepthInMainChain() < depth)
+ if (depth == -1 || tx.GetDepthInMainChain() < depth) {
ListTransactions(pwallet, tx, "*", 0, true, transactions, filter);
+ }
+ }
+
+ // 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 && paltindex && paltindex != pindex) {
+ CBlock block;
+ if (!ReadBlockFromDisk(block, paltindex, Params().GetConsensus())) {
+ throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
+ }
+ for (const CTransactionRef& tx : block.vtx) {
+ if (pwallet->mapWallet.count(tx->GetHash()) > 0) {
+ // We want all transactions regardless of confirmation count to appear here,
+ // even negative confirmation ones, hence the big negative.
+ ListTransactions(pwallet, pwallet->mapWallet[tx->GetHash()], "*", -100000000, true, removed, filter);
+ }
+ }
+ paltindex = paltindex->pprev;
}
CBlockIndex *pblockLast = chainActive[chainActive.Height() + 1 - target_confirms];
@@ -1837,6 +1874,7 @@ UniValue listsinceblock(const JSONRPCRequest& request)
UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("transactions", transactions));
+ if (include_removed) ret.push_back(Pair("removed", removed));
ret.push_back(Pair("lastblock", lastblock.GetHex()));
return ret;
@@ -2065,7 +2103,7 @@ UniValue walletpassphrase(const JSONRPCRequest& request)
"Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n"
"time that overrides the old one.\n"
"\nExamples:\n"
- "\nunlock the wallet for 60 seconds\n"
+ "\nUnlock the wallet for 60 seconds\n"
+ HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") +
"\nLock the wallet again (before 60 seconds)\n"
+ HelpExampleCli("walletlock", "") +
@@ -2220,11 +2258,11 @@ UniValue encryptwallet(const JSONRPCRequest& request)
"\nArguments:\n"
"1. \"passphrase\" (string) The pass phrase to encrypt the wallet with. It must be at least 1 character, but should be long.\n"
"\nExamples:\n"
- "\nEncrypt you wallet\n"
+ "\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 so something like sign\n"
+ "\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", "") +
@@ -2447,6 +2485,7 @@ UniValue getwalletinfo(const JSONRPCRequest& request)
"Returns an object containing various wallet state info.\n"
"\nResult:\n"
"{\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"
@@ -2469,6 +2508,7 @@ UniValue getwalletinfo(const JSONRPCRequest& request)
UniValue obj(UniValue::VOBJ);
size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
+ obj.push_back(Pair("walletname", pwallet->GetName()));
obj.push_back(Pair("walletversion", pwallet->GetVersion()));
obj.push_back(Pair("balance", ValueFromAmount(pwallet->GetBalance())));
obj.push_back(Pair("unconfirmed_balance", ValueFromAmount(pwallet->GetUnconfirmedBalance())));
@@ -2489,6 +2529,39 @@ UniValue getwalletinfo(const JSONRPCRequest& request)
return obj;
}
+UniValue listwallets(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 0)
+ throw std::runtime_error(
+ "listwallets\n"
+ "Returns a list of currently loaded wallets.\n"
+ "For full information on the wallet, use \"getwalletinfo\"\n"
+ "\nResult:\n"
+ "[ (json array of strings)\n"
+ " \"walletname\" (string) the wallet name\n"
+ " ...\n"
+ "]\n"
+ "\nExamples:\n"
+ + HelpExampleCli("listwallets", "")
+ + HelpExampleRpc("listwallets", "")
+ );
+
+ UniValue obj(UniValue::VARR);
+
+ for (CWalletRef pwallet : vpwallets) {
+
+ if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
+ return NullUniValue;
+ }
+
+ LOCK(pwallet->cs_wallet);
+
+ obj.push_back(pwallet->GetName());
+ }
+
+ return obj;
+}
+
UniValue resendwallettransactions(const JSONRPCRequest& request)
{
CWallet * const pwallet = GetWalletForJSONRPCRequest(request);
@@ -2802,9 +2875,15 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
coinControl.signalRbf = options["replaceable"].get_bool();
}
if (options.exists("conf_target")) {
+ if (options.exists("feeRate")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both conf_target and feeRate");
+ }
coinControl.m_confirm_target = ParseConfirmTarget(options["conf_target"]);
}
if (options.exists("estimate_mode")) {
+ if (options.exists("feeRate")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and feeRate");
+ }
if (!FeeModeFromString(options["estimate_mode"].get_str(), coinControl.m_fee_mode)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
}
@@ -3082,9 +3161,10 @@ static const CRPCCommand commands[] =
{ "wallet", "listlockunspent", &listlockunspent, false, {} },
{ "wallet", "listreceivedbyaccount", &listreceivedbyaccount, false, {"minconf","include_empty","include_watchonly"} },
{ "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, {"minconf","include_empty","include_watchonly"} },
- { "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly"} },
+ { "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly","include_removed"} },
{ "wallet", "listtransactions", &listtransactions, false, {"account","count","skip","include_watchonly"} },
{ "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","include_unsafe","query_options"} },
+ { "wallet", "listwallets", &listwallets, true, {} },
{ "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} },
{ "wallet", "move", &movecmd, false, {"fromaccount","toaccount","amount","minconf","comment"} },
{ "wallet", "sendfrom", &sendfrom, false, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} },
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 8176a0017c..4a2cc9a139 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -469,7 +469,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
JSONRPCRequest request;
request.params.setArray();
- request.params.push_back("wallet.backup");
+ request.params.push_back((pathTemp / "wallet.backup").string());
vpwallets.insert(vpwallets.begin(), &wallet);
::dumpwallet(request);
}
@@ -481,7 +481,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
JSONRPCRequest request;
request.params.setArray();
- request.params.push_back("wallet.backup");
+ request.params.push_back((pathTemp / "wallet.backup").string());
vpwallets[0] = &wallet;
::importwallet(request);
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index bcd7e4b4ee..7ef2e6f1d8 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -1066,7 +1066,9 @@ public:
//! Flush wallet (bitdb flush)
void Flush(bool shutdown=false);
- //! Verify the wallet database and perform salvage if required
+ //! Responsible for reading and validating the -wallet arguments and verifying the wallet database.
+ // This function will perform salvage on the wallet if requested, as long as only one wallet is
+ // being loaded (CWallet::ParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
static bool Verify();
/**