aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.test.include7
-rw-r--r--src/bitcoin-cli.cpp16
-rw-r--r--src/bitcoind.cpp7
-rw-r--r--src/httprpc.cpp5
-rw-r--r--src/httpserver.cpp11
-rw-r--r--src/httpserver.h2
-rw-r--r--src/init.cpp12
-rw-r--r--src/init.h16
-rw-r--r--src/merkleblock.cpp3
-rw-r--r--src/merkleblock.h2
-rw-r--r--src/policy/fees.cpp19
-rw-r--r--src/qt/bitcoin.cpp62
-rw-r--r--src/qt/clientmodel.cpp1
-rw-r--r--src/random.cpp5
-rw-r--r--src/rpc/client.cpp1
-rw-r--r--src/rpc/mining.cpp75
-rw-r--r--src/rpc/misc.cpp1
-rw-r--r--src/rpc/net.cpp2
-rw-r--r--src/validation.h8
-rw-r--r--src/validationinterface.h1
-rw-r--r--src/wallet/rpcwallet.cpp25
-rw-r--r--src/wallet/wallet.cpp77
-rw-r--r--src/wallet/wallet.h14
-rw-r--r--src/warnings.h7
-rwxr-xr-xtest/functional/fundrawtransaction.py17
-rwxr-xr-xtest/functional/multiwallet.py47
-rw-r--r--test/functional/test_framework/authproxy.py3
-rw-r--r--test/functional/test_framework/coverage.py2
-rwxr-xr-xtest/functional/test_runner.py1
29 files changed, 306 insertions, 143 deletions
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 90504ad52d..6415b3d2e3 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -96,12 +96,13 @@ endif
test_test_bitcoin_SOURCES = $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES)
test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS) $(EVENT_CFLAGS)
-test_test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) \
- $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS)
-test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_test_bitcoin_LDADD =
if ENABLE_WALLET
test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET)
endif
+test_test_bitcoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) \
+ $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS)
+test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_test_bitcoin_LDADD += $(LIBBITCOIN_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS)
test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 8b48c7f8e4..c2bc15584f 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -46,6 +46,7 @@ std::string HelpMessageCli()
strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
strUsage += HelpMessageOpt("-rpcclienttimeout=<n>", strprintf(_("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT));
strUsage += HelpMessageOpt("-stdin", _("Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases)"));
+ strUsage += HelpMessageOpt("-usewallet=<walletname>", _("Send RPC for non-default wallet on RPC server (argument is wallet filename in bitcoind directory, required if bitcoind/-Qt runs with multiple wallets)"));
return strUsage;
}
@@ -241,7 +242,20 @@ UniValue CallRPC(const std::string& strMethod, const UniValue& params)
assert(output_buffer);
evbuffer_add(output_buffer, strRequest.data(), strRequest.size());
- int r = evhttp_make_request(evcon.get(), req.get(), EVHTTP_REQ_POST, "/");
+ // check if we should use a special wallet endpoint
+ std::string endpoint = "/";
+ std::string walletName = GetArg("-usewallet", "");
+ if (!walletName.empty()) {
+ char *encodedURI = evhttp_uriencode(walletName.c_str(), walletName.size(), false);
+ if (encodedURI) {
+ endpoint = "/wallet/"+ std::string(encodedURI);
+ free(encodedURI);
+ }
+ else {
+ throw CConnectionFailed("uri-encode failed");
+ }
+ }
+ int r = evhttp_make_request(evcon.get(), req.get(), EVHTTP_REQ_POST, endpoint.c_str());
req.release(); // ownership moved to evcon in above call
if (r != 0) {
throw CConnectionFailed("send http request failed");
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 374678310c..f3844e9d47 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -159,7 +159,12 @@ bool AppInit(int argc, char* argv[])
return false;
#endif // HAVE_DECL_DAEMON
}
-
+ // Lock data directory after daemonization
+ if (!AppInitLockDataDirectory())
+ {
+ // If locking the data directory failed, exit immediately
+ exit(EXIT_FAILURE);
+ }
fRet = AppInitMain(threadGroup, scheduler);
}
catch (const std::exception& e) {
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index a207d5ece4..69c3e3f49f 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -233,7 +233,10 @@ bool StartHTTPRPC()
return false;
RegisterHTTPHandler("/", true, HTTPReq_JSONRPC);
-
+#ifdef ENABLE_WALLET
+ // ifdef can be removed once we switch to better endpoint support and API versioning
+ RegisterHTTPHandler("/wallet/", false, HTTPReq_JSONRPC);
+#endif
assert(EventBase());
httpRPCTimerInterface = new HTTPRPCTimerInterface(EventBase());
RPCSetTimerInterface(httpRPCTimerInterface);
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 290a2efca2..ba01255400 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -666,3 +666,14 @@ void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch)
}
}
+std::string urlDecode(const std::string &urlEncoded) {
+ std::string res;
+ if (!urlEncoded.empty()) {
+ char *decoded = evhttp_uridecode(urlEncoded.c_str(), false, NULL);
+ if (decoded) {
+ res = std::string(decoded);
+ free(decoded);
+ }
+ }
+ return res;
+}
diff --git a/src/httpserver.h b/src/httpserver.h
index 9df56e5fc5..3e434bf0a0 100644
--- a/src/httpserver.h
+++ b/src/httpserver.h
@@ -148,4 +148,6 @@ private:
struct event* ev;
};
+std::string urlDecode(const std::string &urlEncoded);
+
#endif // BITCOIN_HTTPSERVER_H
diff --git a/src/init.cpp b/src/init.cpp
index bda0e905b3..f0dbdb47c6 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1170,13 +1170,13 @@ bool AppInitSanityChecks()
return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), _(PACKAGE_NAME)));
// Probe the data directory lock to give an early error message, if possible
+ // We cannot hold the data directory lock here, as the forking for daemon() hasn't yet happened,
+ // and a fork will cause weird behavior to it.
return LockDataDirectory(true);
}
-bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
+bool AppInitLockDataDirectory()
{
- const CChainParams& chainparams = Params();
- // ********************************************************* Step 4a: application initialization
// After daemonization get the data directory lock again and hold on to it until exit
// This creates a slight window for a race condition to happen, however this condition is harmless: it
// will at most make us exit without printing a message to console.
@@ -1184,7 +1184,13 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
// Detailed error printed inside LockDataDirectory
return false;
}
+ return true;
+}
+bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
+{
+ const CChainParams& chainparams = Params();
+ // ********************************************************* Step 4a: application initialization
#ifndef WIN32
CreatePidFile(GetPidFile(), getpid());
#endif
diff --git a/src/init.h b/src/init.h
index 8222794374..a0a824738c 100644
--- a/src/init.h
+++ b/src/init.h
@@ -27,27 +27,33 @@ void InitLogging();
void InitParameterInteraction();
/** Initialize bitcoin core: Basic context setup.
- * @note This can be done before daemonization.
+ * @note This can be done before daemonization. Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read.
*/
bool AppInitBasicSetup();
/**
* Initialization: parameter interaction.
- * @note This can be done before daemonization.
+ * @note This can be done before daemonization. Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read, AppInitBasicSetup should have been called.
*/
bool AppInitParameterInteraction();
/**
* Initialization sanity checks: ecc init, sanity checks, dir lock.
- * @note This can be done before daemonization.
+ * @note This can be done before daemonization. Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read, AppInitParameterInteraction should have been called.
*/
bool AppInitSanityChecks();
/**
- * Bitcoin core main initialization.
- * @note This should only be done after daemonization.
+ * Lock bitcoin core data directory.
+ * @note This should only be done after daemonization. Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read, AppInitSanityChecks should have been called.
*/
+bool AppInitLockDataDirectory();
+/**
+ * Bitcoin core main initialization.
+ * @note This should only be done after daemonization. Call Shutdown() if this function fails.
+ * @pre Parameters should be parsed and config file should be read, AppInitLockDataDirectory should have been called.
+ */
bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler);
/** The help message mode determines what help message to show */
diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp
index ba5f7b400c..f0abea0611 100644
--- a/src/merkleblock.cpp
+++ b/src/merkleblock.cpp
@@ -59,6 +59,9 @@ CMerkleBlock::CMerkleBlock(const CBlock& block, const std::set<uint256>& txids)
}
uint256 CPartialMerkleTree::CalcHash(int height, unsigned int pos, const std::vector<uint256> &vTxid) {
+ //we can never have zero txs in a merkle block, we always need the coinbase tx
+ //if we do not have this assert, we can hit a memory access violation when indexing into vTxid
+ assert(vTxid.size() != 0);
if (height == 0) {
// hash at height 0 is the txids themself
return vTxid[pos];
diff --git a/src/merkleblock.h b/src/merkleblock.h
index de4c5c8d29..f590c487de 100644
--- a/src/merkleblock.h
+++ b/src/merkleblock.h
@@ -121,6 +121,8 @@ public:
/**
* Used to relay blocks as header + vector<merkle branch>
* to filtered nodes.
+ *
+ * NOTE: The class assumes that the given CBlock has *at least* 1 transaction. If the CBlock has 0 txs, it will hit an assertion.
*/
class CMerkleBlock
{
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index 0f186fa845..73cc0b4a5e 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -839,20 +839,20 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation
EstimationResult tempResult;
// Return failure if trying to analyze a target we're not tracking
- if (confTarget <= 0 || (unsigned int)confTarget > longStats->GetMaxConfirms())
- return CFeeRate(0);
+ if (confTarget <= 0 || (unsigned int)confTarget > longStats->GetMaxConfirms()) {
+ return CFeeRate(0); // error conditon
+ }
// It's not possible to get reasonable estimates for confTarget of 1
- if (confTarget == 1)
- confTarget = 2;
+ if (confTarget == 1) confTarget = 2;
unsigned int maxUsableEstimate = MaxUsableEstimate();
- if (maxUsableEstimate <= 1)
- return CFeeRate(0);
-
if ((unsigned int)confTarget > maxUsableEstimate) {
confTarget = maxUsableEstimate;
}
+ if (feeCalc) feeCalc->returnedTarget = confTarget;
+
+ if (confTarget <= 1) return CFeeRate(0); // error conditon
assert(confTarget > 0); //estimateCombinedFee and estimateConservativeFee take unsigned ints
/** true is passed to estimateCombined fee for target/2 and target so
@@ -899,10 +899,7 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation
}
}
- if (feeCalc) feeCalc->returnedTarget = confTarget;
-
- if (median < 0)
- return CFeeRate(0);
+ if (median < 0) return CFeeRate(0); // error conditon
return CFeeRate(median);
}
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 8a745cadce..4a4116c670 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -178,6 +178,10 @@ class BitcoinCore: public QObject
Q_OBJECT
public:
explicit BitcoinCore();
+ /** Basic initialization, before starting initialization/shutdown thread.
+ * Return true on success.
+ */
+ static bool baseInitialize();
public Q_SLOTS:
void initialize();
@@ -270,26 +274,32 @@ void BitcoinCore::handleRunawayException(const std::exception *e)
Q_EMIT runawayException(QString::fromStdString(GetWarnings("gui")));
}
+bool BitcoinCore::baseInitialize()
+{
+ if (!AppInitBasicSetup())
+ {
+ return false;
+ }
+ if (!AppInitParameterInteraction())
+ {
+ return false;
+ }
+ if (!AppInitSanityChecks())
+ {
+ return false;
+ }
+ if (!AppInitLockDataDirectory())
+ {
+ return false;
+ }
+ return true;
+}
+
void BitcoinCore::initialize()
{
try
{
qDebug() << __func__ << ": Running initialization in thread";
- if (!AppInitBasicSetup())
- {
- Q_EMIT initializeResult(false);
- return;
- }
- if (!AppInitParameterInteraction())
- {
- Q_EMIT initializeResult(false);
- return;
- }
- if (!AppInitSanityChecks())
- {
- Q_EMIT initializeResult(false);
- return;
- }
bool rv = AppInitMain(threadGroup, scheduler);
Q_EMIT initializeResult(rv);
} catch (const std::exception& e) {
@@ -689,16 +699,26 @@ int main(int argc, char *argv[])
if (GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !GetBoolArg("-min", false))
app.createSplashScreen(networkStyle.data());
+ int rv = EXIT_SUCCESS;
try
{
app.createWindow(networkStyle.data());
- app.requestInitialize();
+ // Perform base initialization before spinning up initialization/shutdown thread
+ // This is acceptable because this function only contains steps that are quick to execute,
+ // so the GUI thread won't be held up.
+ if (BitcoinCore::baseInitialize()) {
+ app.requestInitialize();
#if defined(Q_OS_WIN) && QT_VERSION >= 0x050000
- WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely...").arg(QObject::tr(PACKAGE_NAME)), (HWND)app.getMainWinId());
+ WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely...").arg(QObject::tr(PACKAGE_NAME)), (HWND)app.getMainWinId());
#endif
- app.exec();
- app.requestShutdown();
- app.exec();
+ app.exec();
+ app.requestShutdown();
+ app.exec();
+ rv = app.getReturnValue();
+ } else {
+ // A dialog with detailed error will have been shown by InitError()
+ rv = EXIT_FAILURE;
+ }
} catch (const std::exception& e) {
PrintExceptionContinue(&e, "Runaway exception");
app.handleRunawayException(QString::fromStdString(GetWarnings("gui")));
@@ -706,6 +726,6 @@ int main(int argc, char *argv[])
PrintExceptionContinue(NULL, "Runaway exception");
app.handleRunawayException(QString::fromStdString(GetWarnings("gui")));
}
- return app.getReturnValue();
+ return rv;
}
#endif // BITCOIN_QT_TEST
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 33f4535ee2..52ce11cefd 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -18,6 +18,7 @@
#include "txmempool.h"
#include "ui_interface.h"
#include "util.h"
+#include "warnings.h"
#include <stdint.h>
diff --git a/src/random.cpp b/src/random.cpp
index 1ee6158e4b..b308e8f4a1 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -186,6 +186,7 @@ void GetDevURandom(unsigned char *ent32)
do {
ssize_t n = read(f, ent32 + have, NUM_OS_RANDOM_BYTES - have);
if (n <= 0 || n + have > NUM_OS_RANDOM_BYTES) {
+ close(f);
RandFailure();
}
have += n;
@@ -226,10 +227,12 @@ void GetOSRand(unsigned char *ent32)
RandFailure();
}
}
-#elif defined(HAVE_GETENTROPY)
+#elif defined(HAVE_GETENTROPY) && defined(__OpenBSD__)
/* On OpenBSD this can return up to 256 bytes of entropy, will return an
* error if more are requested.
* The call cannot return less than the requested number of bytes.
+ getentropy is explicitly limited to openbsd here, as a similar (but not
+ the same) function may exist on other platforms via glibc.
*/
if (getentropy(ent32, NUM_OS_RANDOM_BYTES) != 0) {
RandFailure();
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 775ad4b6c9..1ab1956124 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -114,7 +114,6 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getrawmempool", 0, "verbose" },
{ "estimatefee", 0, "nblocks" },
{ "estimatesmartfee", 0, "nblocks" },
- { "estimatesmartfee", 1, "conservative" },
{ "estimaterawfee", 0, "nblocks" },
{ "estimaterawfee", 1, "threshold" },
{ "prioritisetransaction", 1, "dummy" },
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index b8c94d32ec..daca32d251 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -24,6 +24,7 @@
#include "util.h"
#include "utilstrencodings.h"
#include "validationinterface.h"
+#include "warnings.h"
#include <memory>
#include <stdint.h>
@@ -806,42 +807,59 @@ UniValue estimatesmartfee(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "estimatesmartfee nblocks (conservative)\n"
+ "estimatesmartfee conf_target (\"estimate_mode\")\n"
"\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"
- "confirmation within nblocks blocks if possible and return the number of blocks\n"
+ "confirmation within conf_target blocks if possible and return the number of blocks\n"
"for which the estimate is valid. Uses virtual transaction size as defined\n"
"in BIP 141 (witness data is discounted).\n"
"\nArguments:\n"
- "1. nblocks (numeric)\n"
- "2. conservative (bool, optional, default=true) Whether to return a more conservative estimate which\n"
- " also satisfies a longer history. A conservative estimate potentially returns a higher\n"
- " feerate and is more likely to be sufficient for the desired target, but is not as\n"
- " responsive to short term drops in the prevailing fee market\n"
+ "1. conf_target (numeric) Confirmation target in blocks (1 - 1008)\n"
+ "2. \"estimate_mode\" (string, optional, default=CONSERVATIVE) The fee estimate mode.\n"
+ " Whether to return a more conservative estimate which also satisfies\n"
+ " a longer history. A conservative estimate potentially returns a\n"
+ " higher feerate and is more likely to be sufficient for the desired\n"
+ " target, but is not as responsive to short term drops in the\n"
+ " prevailing fee market. Must be one of:\n"
+ " \"UNSET\" (defaults to CONSERVATIVE)\n"
+ " \"ECONOMICAL\"\n"
+ " \"CONSERVATIVE\"\n"
"\nResult:\n"
"{\n"
- " \"feerate\" : x.x, (numeric) estimate fee-per-kilobyte (in BTC)\n"
+ " \"feerate\" : x.x, (numeric, optional) estimate fee-per-kilobyte (in BTC)\n"
+ " \"errors\": [ str... ] (json array of strings, optional) Errors encountered during processing\n"
" \"blocks\" : n (numeric) block number where estimate was found\n"
"}\n"
"\n"
- "A negative value is returned if not enough transactions and blocks\n"
+ "The request target will be clamped between 2 and the highest target\n"
+ "fee estimation is able to return based on how long it has been running.\n"
+ "An error is returned if not enough transactions and blocks\n"
"have been observed to make an estimate for any number of blocks.\n"
"\nExample:\n"
+ HelpExampleCli("estimatesmartfee", "6")
);
- RPCTypeCheck(request.params, {UniValue::VNUM});
-
- int nBlocks = request.params[0].get_int();
+ RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VSTR});
+ RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
+ unsigned int conf_target = ParseConfirmTarget(request.params[0]);
bool conservative = true;
if (request.params.size() > 1 && !request.params[1].isNull()) {
- RPCTypeCheckArgument(request.params[1], UniValue::VBOOL);
- conservative = request.params[1].get_bool();
+ FeeEstimateMode fee_mode;
+ if (!FeeModeFromString(request.params[1].get_str(), fee_mode)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
+ }
+ if (fee_mode == FeeEstimateMode::ECONOMICAL) conservative = false;
}
UniValue result(UniValue::VOBJ);
+ UniValue errors(UniValue::VARR);
FeeCalculation feeCalc;
- CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocks, &feeCalc, conservative);
- result.push_back(Pair("feerate", feeRate == CFeeRate(0) ? -1.0 : ValueFromAmount(feeRate.GetFeePerK())));
+ CFeeRate feeRate = ::feeEstimator.estimateSmartFee(conf_target, &feeCalc, conservative);
+ if (feeRate != CFeeRate(0)) {
+ result.push_back(Pair("feerate", ValueFromAmount(feeRate.GetFeePerK())));
+ } else {
+ errors.push_back("Insufficient data or no feerate found");
+ result.push_back(Pair("errors", errors));
+ }
result.push_back(Pair("blocks", feeCalc.returnedTarget));
return result;
}
@@ -850,18 +868,18 @@ UniValue estimaterawfee(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "estimaterawfee nblocks (threshold)\n"
+ "estimaterawfee conf_target (threshold)\n"
"\nWARNING: This interface is unstable and may disappear or change!\n"
"\nWARNING: This is an advanced API call that is tightly coupled to the specific\n"
" implementation of fee estimation. The parameters it can be called with\n"
" and the results it returns will change if the internal implementation changes.\n"
"\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n"
- "confirmation within nblocks blocks if possible. Uses virtual transaction size as defined\n"
- "in BIP 141 (witness data is discounted).\n"
+ "confirmation within conf_target blocks if possible. Uses virtual transaction size as\n"
+ "defined in BIP 141 (witness data is discounted).\n"
"\nArguments:\n"
- "1. nblocks (numeric) Confirmation target in blocks (1 - 1008)\n"
+ "1. conf_target (numeric) Confirmation target in blocks (1 - 1008)\n"
"2. threshold (numeric, optional) The proportion of transactions in a given feerate range that must have been\n"
- " confirmed within nblocks in order to consider those feerates as high enough and proceed to check\n"
+ " confirmed within conf_target in order to consider those feerates as high enough and proceed to check\n"
" lower buckets. Default: 0.95\n"
"\nResult:\n"
"{\n"
@@ -889,12 +907,9 @@ UniValue estimaterawfee(const JSONRPCRequest& request)
+ HelpExampleCli("estimaterawfee", "6 0.9")
);
- RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM, UniValue::VNUM}, true);
+ RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM}, true);
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
- int nBlocks = request.params[0].get_int();
- if (nBlocks < 1 || (unsigned int)nBlocks > ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid nblocks");
- }
+ unsigned int conf_target = ParseConfirmTarget(request.params[0]);
double threshold = 0.95;
if (!request.params[1].isNull()) {
threshold = request.params[1].get_real();
@@ -910,9 +925,9 @@ UniValue estimaterawfee(const JSONRPCRequest& request)
EstimationResult buckets;
// Only output results for horizons which track the target
- if ((unsigned int)nBlocks > ::feeEstimator.HighestTargetTracked(horizon)) continue;
+ if (conf_target > ::feeEstimator.HighestTargetTracked(horizon)) continue;
- feeRate = ::feeEstimator.estimateRawFee(nBlocks, threshold, horizon, &buckets);
+ feeRate = ::feeEstimator.estimateRawFee(conf_target, threshold, horizon, &buckets);
UniValue horizon_result(UniValue::VOBJ);
UniValue errors(UniValue::VARR);
UniValue passbucket(UniValue::VOBJ);
@@ -963,9 +978,9 @@ static const CRPCCommand commands[] =
{ "generating", "generatetoaddress", &generatetoaddress, true, {"nblocks","address","maxtries"} },
{ "util", "estimatefee", &estimatefee, true, {"nblocks"} },
- { "util", "estimatesmartfee", &estimatesmartfee, true, {"nblocks", "conservative"} },
+ { "util", "estimatesmartfee", &estimatesmartfee, true, {"conf_target", "estimate_mode"} },
- { "hidden", "estimaterawfee", &estimaterawfee, true, {"nblocks", "threshold"} },
+ { "hidden", "estimaterawfee", &estimaterawfee, true, {"conf_target", "threshold"} },
};
void RegisterMiningRPCCommands(CRPCTable &t)
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index fcbbe1ceed..3c4f071fcf 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -21,6 +21,7 @@
#include "wallet/wallet.h"
#include "wallet/walletdb.h"
#endif
+#include "warnings.h"
#include <stdint.h>
#ifdef HAVE_MALLOC_INFO
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index ed452fcb02..090d9e448e 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -18,7 +18,7 @@
#include "util.h"
#include "utilstrencodings.h"
#include "version.h"
-
+#include "warnings.h"
#include <univalue.h>
diff --git a/src/validation.h b/src/validation.h
index a9f995abb8..95c8e5b93e 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -268,14 +268,6 @@ void UnloadBlockIndex();
void ThreadScriptCheck();
/** Check whether we are doing an initial block download (synchronizing from disk or network) */
bool IsInitialBlockDownload();
-/** Format a string that describes several potential problems detected by the core.
- * strFor can have three values:
- * - "rpc": get critical warnings, which should put the client in safe mode if non-empty
- * - "statusbar": get all warnings
- * - "gui": get all warnings, translated (where possible) for GUI
- * This function only returns the highest priority warning of the set selected by strFor.
- */
-std::string GetWarnings(const std::string& strFor);
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
bool GetTransaction(const uint256 &hash, CTransactionRef &tx, const Consensus::Params& params, uint256 &hashBlock, bool fAllowSlow = false);
/** Find the best known block, and make it the tip of the block chain */
diff --git a/src/validationinterface.h b/src/validationinterface.h
index 568da66df2..d6da2bc1fd 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -86,7 +86,6 @@ public:
void TransactionAddedToMempool(const CTransactionRef &);
void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef> &);
void BlockDisconnected(const std::shared_ptr<const CBlock> &);
- void UpdatedTransaction(const uint256 &);
void SetBestChain(const CBlockLocator &);
void Inventory(const uint256 &);
void Broadcast(int64_t nBestBlockTime, CConnman* connman);
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index ee8c7548fc..f63ce1bb48 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -9,6 +9,7 @@
#include "consensus/validation.h"
#include "core_io.h"
#include "init.h"
+#include "httpserver.h"
#include "validation.h"
#include "net.h"
#include "policy/feerate.h"
@@ -30,10 +31,21 @@
#include <univalue.h>
+static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
+
CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
{
- // TODO: Some way to access secondary wallets
- return vpwallets.empty() ? nullptr : vpwallets[0];
+ if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) {
+ // wallet endpoint was used
+ std::string requestedWallet = urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size()));
+ for (CWalletRef pwallet : ::vpwallets) {
+ if (pwallet->GetName() == requestedWallet) {
+ return pwallet;
+ }
+ }
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Requested wallet does not exist or is not loaded");
+ }
+ return ::vpwallets.size() == 1 || (request.fHelp && ::vpwallets.size() > 0) ? ::vpwallets[0] : nullptr;
}
std::string HelpRequiringPassphrase(CWallet * const pwallet)
@@ -2693,7 +2705,6 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
" \"changePosition\" (numeric, optional, default random) The index of the change output\n"
" \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n"
" \"lockUnspents\" (boolean, optional, default false) Lock selected unspent outputs\n"
- " \"reserveChangeKey\" (boolean, optional, default true) Reserves the change output key from the keypool\n"
" \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific feerate (" + CURRENCY_UNIT + " per KB)\n"
" \"subtractFeeFromOutputs\" (array, optional) A json array of integers.\n"
" The fee will be equally deducted from the amount of each specified output.\n"
@@ -2732,7 +2743,6 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
CCoinControl coinControl;
int changePosition = -1;
bool lockUnspents = false;
- bool reserveChangeKey = true;
UniValue subtractFeeFromOutputs;
std::set<int> setSubtractFeeFromOutputs;
@@ -2752,7 +2762,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
{"changePosition", UniValueType(UniValue::VNUM)},
{"includeWatching", UniValueType(UniValue::VBOOL)},
{"lockUnspents", UniValueType(UniValue::VBOOL)},
- {"reserveChangeKey", UniValueType(UniValue::VBOOL)},
+ {"reserveChangeKey", UniValueType(UniValue::VBOOL)}, // DEPRECATED (and ignored), should be removed in 0.16 or so.
{"feeRate", UniValueType()}, // will be checked below
{"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
{"replaceable", UniValueType(UniValue::VBOOL)},
@@ -2779,9 +2789,6 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
if (options.exists("lockUnspents"))
lockUnspents = options["lockUnspents"].get_bool();
- if (options.exists("reserveChangeKey"))
- reserveChangeKey = options["reserveChangeKey"].get_bool();
-
if (options.exists("feeRate"))
{
coinControl.m_feerate = CFeeRate(AmountFromValue(options["feeRate"]));
@@ -2830,7 +2837,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
CAmount nFeeOut;
std::string strFailReason;
- if (!pwallet->FundTransaction(tx, nFeeOut, changePosition, strFailReason, lockUnspents, setSubtractFeeFromOutputs, coinControl, reserveChangeKey)) {
+ if (!pwallet->FundTransaction(tx, nFeeOut, changePosition, strFailReason, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
}
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index e230ee0eb0..7a0aac5f53 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -89,7 +89,7 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
return &(it->second);
}
-CPubKey CWallet::GenerateNewKey(bool internal)
+CPubKey CWallet::GenerateNewKey(CWalletDB &walletdb, bool internal)
{
AssertLockHeld(cs_wallet); // mapKeyMetadata
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
@@ -102,14 +102,15 @@ CPubKey CWallet::GenerateNewKey(bool internal)
// use HD key derivation if HD was enabled during wallet creation
if (IsHDEnabled()) {
- DeriveNewChildKey(metadata, secret, (CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false));
+ DeriveNewChildKey(walletdb, metadata, secret, (CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false));
} else {
secret.MakeNewKey(fCompressed);
}
// Compressed public keys were introduced in version 0.6.0
- if (fCompressed)
+ if (fCompressed) {
SetMinVersion(FEATURE_COMPRPUBKEY);
+ }
CPubKey pubkey = secret.GetPubKey();
assert(secret.VerifyPubKey(pubkey));
@@ -117,12 +118,13 @@ CPubKey CWallet::GenerateNewKey(bool internal)
mapKeyMetadata[pubkey.GetID()] = metadata;
UpdateTimeFirstKey(nCreationTime);
- if (!AddKeyPubKey(secret, pubkey))
+ if (!AddKeyPubKeyWithDB(walletdb, secret, pubkey)) {
throw std::runtime_error(std::string(__func__) + ": AddKey failed");
+ }
return pubkey;
}
-void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool internal)
+void CWallet::DeriveNewChildKey(CWalletDB &walletdb, CKeyMetadata& metadata, CKey& secret, bool internal)
{
// for now we use a fixed keypath scheme of m/0'/0'/k
CKey key; //master key seed (256bit)
@@ -164,33 +166,52 @@ void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool inter
secret = childKey.key;
metadata.hdMasterKeyID = hdChain.masterKeyID;
// update the chain model in the database
- if (!CWalletDB(*dbw).WriteHDChain(hdChain))
+ if (!walletdb.WriteHDChain(hdChain))
throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed");
}
-bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey)
+bool CWallet::AddKeyPubKeyWithDB(CWalletDB &walletdb, const CKey& secret, const CPubKey &pubkey)
{
AssertLockHeld(cs_wallet); // mapKeyMetadata
- if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey))
+
+ // CCryptoKeyStore has no concept of wallet databases, but calls AddCryptedKey
+ // which is overridden below. To avoid flushes, the database handle is
+ // tunneled through to it.
+ bool needsDB = !pwalletdbEncryption;
+ if (needsDB) {
+ pwalletdbEncryption = &walletdb;
+ }
+ if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) {
+ if (needsDB) pwalletdbEncryption = NULL;
return false;
+ }
+ if (needsDB) pwalletdbEncryption = NULL;
// check if we need to remove from watch-only
CScript script;
script = GetScriptForDestination(pubkey.GetID());
- if (HaveWatchOnly(script))
+ if (HaveWatchOnly(script)) {
RemoveWatchOnly(script);
+ }
script = GetScriptForRawPubKey(pubkey);
- if (HaveWatchOnly(script))
+ if (HaveWatchOnly(script)) {
RemoveWatchOnly(script);
+ }
if (!IsCrypted()) {
- return CWalletDB(*dbw).WriteKey(pubkey,
+ return walletdb.WriteKey(pubkey,
secret.GetPrivKey(),
mapKeyMetadata[pubkey.GetID()]);
}
return true;
}
+bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey)
+{
+ CWalletDB walletdb(*dbw);
+ return CWallet::AddKeyPubKeyWithDB(walletdb, secret, pubkey);
+}
+
bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
const std::vector<unsigned char> &vchCryptedSecret)
{
@@ -2452,7 +2473,7 @@ bool CWallet::SignTransaction(CMutableTransaction &tx)
return true;
}
-bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl, bool keepReserveKey)
+bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl)
{
std::vector<CRecipient> vecSend;
@@ -2474,8 +2495,13 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosInOut, strFailReason, coinControl, false)) {
return false;
}
- if (nChangePosInOut != -1)
+
+ if (nChangePosInOut != -1) {
tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.tx->vout[nChangePosInOut]);
+ // we dont have the normal Create/Commit cycle, and dont want to risk reusing change,
+ // so just remove the key from the keypool here.
+ reservekey.KeepKey();
+ }
// Copy output sizes from new transaction; they may have had the fee subtracted from them
for (unsigned int idx = 0; idx < tx.vout.size(); idx++)
@@ -2496,9 +2522,6 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
}
}
- // optionally keep the change output key
- if (keepReserveKey)
- reservekey.KeepKey();
return true;
}
@@ -3204,22 +3227,21 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
internal = true;
}
- if (!setInternalKeyPool.empty()) {
- nEnd = *(setInternalKeyPool.rbegin()) + 1;
- }
- if (!setExternalKeyPool.empty()) {
- nEnd = std::max(nEnd, *(setExternalKeyPool.rbegin()) + 1);
- }
+ assert(m_max_keypool_index < std::numeric_limits<int64_t>::max()); // How in the hell did you use so many keys?
+ int64_t index = ++m_max_keypool_index;
- if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey(internal), internal)))
+ if (!walletdb.WritePool(index, CKeyPool(GenerateNewKey(walletdb, internal), internal))) {
throw std::runtime_error(std::string(__func__) + ": writing generated key failed");
+ }
if (internal) {
- setInternalKeyPool.insert(nEnd);
+ setInternalKeyPool.insert(index);
} else {
- setExternalKeyPool.insert(nEnd);
+ setExternalKeyPool.insert(index);
}
- LogPrintf("keypool added key %d, size=%u (%u internal), new key is %s\n", nEnd, setInternalKeyPool.size() + setExternalKeyPool.size(), setInternalKeyPool.size(), internal ? "internal" : "external");
+ }
+ if (missingInternal + missingExternal > 0) {
+ LogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size(), setInternalKeyPool.size());
}
}
return true;
@@ -3294,7 +3316,8 @@ bool CWallet::GetKeyFromPool(CPubKey& result, bool internal)
if (nIndex == -1)
{
if (IsLocked()) return false;
- result = GenerateNewKey(internal);
+ CWalletDB walletdb(*dbw);
+ result = GenerateNewKey(walletdb, internal);
return true;
}
KeepKey(nIndex);
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index a7fef8a802..bcd7e4b4ee 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -40,7 +40,7 @@ extern unsigned int nTxConfirmTarget;
extern bool bSpendZeroConfChange;
extern bool fWalletRbf;
-static const unsigned int DEFAULT_KEYPOOL_SIZE = 100;
+static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000;
//! -paytxfee default
static const CAmount DEFAULT_TRANSACTION_FEE = 0;
//! -fallbackfee default
@@ -699,10 +699,11 @@ private:
CHDChain hdChain;
/* HD derive new child key (on internal or external chain) */
- void DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret, bool internal = false);
+ void DeriveNewChildKey(CWalletDB &walletdb, CKeyMetadata& metadata, CKey& secret, bool internal = false);
std::set<int64_t> setInternalKeyPool;
std::set<int64_t> setExternalKeyPool;
+ int64_t m_max_keypool_index;
int64_t nTimeFirstKey;
@@ -745,13 +746,14 @@ public:
}
}
- void LoadKeyPool(int nIndex, const CKeyPool &keypool)
+ void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool)
{
if (keypool.fInternal) {
setInternalKeyPool.insert(nIndex);
} else {
setExternalKeyPool.insert(nIndex);
}
+ m_max_keypool_index = std::max(m_max_keypool_index, nIndex);
// If no metadata exists yet, create a default with the pool key's
// creation time. Note that this may be overwritten by actually
@@ -797,6 +799,7 @@ public:
nAccountingEntryNumber = 0;
nNextResend = 0;
nLastResend = 0;
+ m_max_keypool_index = 0;
nTimeFirstKey = 0;
fBroadcastTransactions = false;
nRelockTime = 0;
@@ -868,9 +871,10 @@ public:
* keystore implementation
* Generate a new key
*/
- CPubKey GenerateNewKey(bool internal = false);
+ CPubKey GenerateNewKey(CWalletDB& walletdb, bool internal = false);
//! Adds a key to the store, and saves it to disk.
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override;
+ bool AddKeyPubKeyWithDB(CWalletDB &walletdb,const CKey& key, const CPubKey &pubkey);
//! Adds a key to the store, without saving it to disk (used by LoadWallet)
bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); }
//! Load metadata (used by LoadWallet)
@@ -947,7 +951,7 @@ public:
* Insert additional inputs into the transaction by
* calling CreateTransaction();
*/
- bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl, bool keepReserveKey = true);
+ bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl);
bool SignTransaction(CMutableTransaction& tx);
/**
diff --git a/src/warnings.h b/src/warnings.h
index a7aa657426..fd0ca53942 100644
--- a/src/warnings.h
+++ b/src/warnings.h
@@ -14,6 +14,13 @@ void SetfLargeWorkForkFound(bool flag);
bool GetfLargeWorkForkFound();
void SetfLargeWorkInvalidChainFound(bool flag);
bool GetfLargeWorkInvalidChainFound();
+/** Format a string that describes several potential problems detected by the core.
+ * strFor can have three values:
+ * - "rpc": get critical warnings, which should put the client in safe mode if non-empty
+ * - "statusbar": get all warnings
+ * - "gui": get all warnings, translated (where possible) for GUI
+ * This function only returns the highest priority warning of the set selected by strFor.
+ */
std::string GetWarnings(const std::string& strFor);
static const bool DEFAULT_TESTSAFEMODE = false;
diff --git a/test/functional/fundrawtransaction.py b/test/functional/fundrawtransaction.py
index 0baab6d01c..e52e773918 100755
--- a/test/functional/fundrawtransaction.py
+++ b/test/functional/fundrawtransaction.py
@@ -636,20 +636,9 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_fee_amount(result2['fee'], count_bytes(result2['hex']), 2 * result_fee_rate)
assert_fee_amount(result3['fee'], count_bytes(result3['hex']), 10 * result_fee_rate)
- #############################
- # Test address reuse option #
- #############################
-
- result3 = self.nodes[3].fundrawtransaction(rawtx, {"reserveChangeKey": False})
- res_dec = self.nodes[0].decoderawtransaction(result3["hex"])
- changeaddress = ""
- for out in res_dec['vout']:
- if out['value'] > 1.0:
- changeaddress += out['scriptPubKey']['addresses'][0]
- assert(changeaddress != "")
- nextaddr = self.nodes[3].getrawchangeaddress()
- # frt should not have removed the key from the keypool
- assert(changeaddress == nextaddr)
+ ################################
+ # Test no address reuse occurs #
+ ################################
result3 = self.nodes[3].fundrawtransaction(rawtx)
res_dec = self.nodes[0].decoderawtransaction(result3["hex"])
diff --git a/test/functional/multiwallet.py b/test/functional/multiwallet.py
new file mode 100755
index 0000000000..2b4dd2d3e7
--- /dev/null
+++ b/test/functional/multiwallet.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python3
+# Copyright (c) 2017 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 multiwallet."""
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import *
+
+class MultiWalletTest(BitcoinTestFramework):
+
+ def __init__(self):
+ super().__init__()
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+ self.extra_args = [['-wallet=w1', '-wallet=w2', '-wallet=w3']]
+
+ def run_test(self):
+ w1 = self.nodes[0] / "wallet/w1"
+ w1.generate(1)
+
+ #accessing wallet RPC without using wallet endpoint fails
+ assert_raises_jsonrpc(-32601, "Method not found", self.nodes[0].getwalletinfo)
+
+ #check w1 wallet balance
+ walletinfo = w1.getwalletinfo()
+ assert_equal(walletinfo['immature_balance'], 50)
+
+ #check w1 wallet balance
+ w2 = self.nodes[0] / "wallet/w2"
+ walletinfo = w2.getwalletinfo()
+ assert_equal(walletinfo['immature_balance'], 0)
+
+ w3 = self.nodes[0] / "wallet/w3"
+
+ w1.generate(101)
+ assert_equal(w1.getbalance(), 100)
+ assert_equal(w2.getbalance(), 0)
+ assert_equal(w3.getbalance(), 0)
+
+ w1.sendtoaddress(w2.getnewaddress(), 1)
+ w1.sendtoaddress(w3.getnewaddress(), 2)
+ w1.generate(1)
+ assert_equal(w2.getbalance(), 1)
+ assert_equal(w3.getbalance(), 2)
+
+if __name__ == '__main__':
+ MultiWalletTest().main()
diff --git a/test/functional/test_framework/authproxy.py b/test/functional/test_framework/authproxy.py
index dfcc524313..b3671cbdc5 100644
--- a/test/functional/test_framework/authproxy.py
+++ b/test/functional/test_framework/authproxy.py
@@ -191,3 +191,6 @@ class AuthServiceProxy(object):
else:
log.debug("<-- [%.6f] %s"%(elapsed,responsedata))
return response
+
+ def __truediv__(self, relative_uri):
+ return AuthServiceProxy("{}/{}".format(self.__service_url, relative_uri), self._service_name, connection=self.__conn)
diff --git a/test/functional/test_framework/coverage.py b/test/functional/test_framework/coverage.py
index 3f87ef91f6..227b1a17af 100644
--- a/test/functional/test_framework/coverage.py
+++ b/test/functional/test_framework/coverage.py
@@ -56,6 +56,8 @@ class AuthServiceProxyWrapper(object):
def url(self):
return self.auth_service_proxy_instance.url
+ def __truediv__(self, relative_uri):
+ return AuthServiceProxyWrapper(self.auth_service_proxy_instance / relative_uri)
def get_filename(dirname, n_node):
"""
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index b7bc6e841b..51577589fe 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -63,6 +63,7 @@ BASE_SCRIPTS= [
'segwit.py',
# vv Tests less than 2m vv
'wallet.py',
+ 'multiwallet.py',
'wallet-accounts.py',
'p2p-segwit.py',
'wallet-dump.py',