aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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.cpp14
-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/policy/policy.cpp10
-rw-r--r--src/policy/policy.h4
-rw-r--r--src/qt/bitcoin.cpp62
-rw-r--r--src/qt/clientmodel.cpp1
-rw-r--r--src/random.cpp4
-rw-r--r--src/rpc/client.cpp2
-rw-r--r--src/rpc/mining.cpp75
-rw-r--r--src/rpc/misc.cpp8
-rw-r--r--src/rpc/net.cpp2
-rw-r--r--src/rpc/rawtransaction.cpp135
-rw-r--r--src/test/transaction_tests.cpp10
-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.cpp109
-rw-r--r--src/wallet/wallet.h17
-rw-r--r--src/warnings.h7
28 files changed, 401 insertions, 181 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..445b9d8e89 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("-rpcwallet=<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("-rpcwallet", "");
+ 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 1e85642019..f0dbdb47c6 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -479,7 +479,7 @@ std::string HelpMessage(HelpMessageMode mode)
if (showDebug) {
strUsage += HelpMessageOpt("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", defaultChainParams->RequireStandard()));
strUsage += HelpMessageOpt("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)));
- strUsage += HelpMessageOpt("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to defined dust, the value of an output such that it will cost about 1/3 of its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)));
+ strUsage += HelpMessageOpt("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to defined dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)));
}
strUsage += HelpMessageOpt("-bytespersigop", strprintf(_("Equivalent bytes per sigop in transactions for relay and mining (default: %u)"), DEFAULT_BYTES_PER_SIGOP));
strUsage += HelpMessageOpt("-datacarrier", strprintf(_("Relay and mine data carrier transactions (default: %u)"), DEFAULT_ACCEPT_DATACARRIER));
@@ -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..b9476407cf 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 condition
+ }
// 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 condition
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 condition
return CFeeRate(median);
}
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index 9f2d623e76..605e3e0696 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -19,16 +19,18 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
{
// "Dust" is defined in terms of dustRelayFee,
// which has units satoshis-per-kilobyte.
- // If you'd pay more than 1/3 in fees
+ // If you'd pay more in fees than the value of the output
// to spend something, then we consider it dust.
// A typical spendable non-segwit txout is 34 bytes big, and will
// need a CTxIn of at least 148 bytes to spend:
// so dust is a spendable txout less than
- // 546*dustRelayFee/1000 (in satoshis).
+ // 182*dustRelayFee/1000 (in satoshis).
+ // 546 satoshis at the default rate of 3000 sat/kB.
// A typical spendable segwit txout is 31 bytes big, and will
// need a CTxIn of at least 67 bytes to spend:
// so dust is a spendable txout less than
- // 294*dustRelayFee/1000 (in satoshis).
+ // 98*dustRelayFee/1000 (in satoshis).
+ // 294 satoshis at the default rate of 3000 sat/kB.
if (txout.scriptPubKey.IsUnspendable())
return 0;
@@ -44,7 +46,7 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
nSize += (32 + 4 + 1 + 107 + 4); // the 148 mentioned above
}
- return 3 * dustRelayFeeIn.GetFee(nSize);
+ return dustRelayFeeIn.GetFee(nSize);
}
bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
diff --git a/src/policy/policy.h b/src/policy/policy.h
index 2c2ea9d5b8..c06820f84e 100644
--- a/src/policy/policy.h
+++ b/src/policy/policy.h
@@ -40,12 +40,12 @@ static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEMS = 100;
static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEM_SIZE = 80;
/** The maximum size of a standard witnessScript */
static const unsigned int MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600;
-/** Min feerate for defining dust. Historically this has been the same as the
+/** Min feerate for defining dust. Historically this has been based on the
* minRelayTxFee, however changing the dust limit changes which transactions are
* standard and should be done with care and ideally rarely. It makes sense to
* only increase the dust limit after prior releases were already not creating
* outputs below the new threshold */
-static const unsigned int DUST_RELAY_TX_FEE = 1000;
+static const unsigned int DUST_RELAY_TX_FEE = 3000;
/**
* Standard script verification flags that standard transactions will comply
* with. However scripts violating these flags may still be present in valid
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 3226abb69e..b308e8f4a1 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -227,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..7c75586d03 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -95,6 +95,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "signrawtransaction", 1, "prevtxs" },
{ "signrawtransaction", 2, "privkeys" },
{ "sendrawtransaction", 1, "allowhighfees" },
+ { "combinerawtransaction", 0, "txs" },
{ "fundrawtransaction", 1, "options" },
{ "gettxout", 1, "n" },
{ "gettxout", 2, "include_mempool" },
@@ -114,7 +115,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 26f4378718..7d292a4635 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..f3c86038a3 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
@@ -50,6 +51,7 @@ UniValue getinfo(const JSONRPCRequest& request)
"\nDEPRECATED. Returns an object containing various state info.\n"
"\nResult:\n"
"{\n"
+ " \"deprecation-warning\": \"...\" (string) warning that the getinfo command is deprecated and will be removed in 0.16\n"
" \"version\": xxxxx, (numeric) the server version\n"
" \"protocolversion\": xxxxx, (numeric) the protocol version\n"
" \"walletversion\": xxxxx, (numeric) the wallet version\n"
@@ -57,7 +59,7 @@ UniValue getinfo(const JSONRPCRequest& request)
" \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n"
" \"timeoffset\": xxxxx, (numeric) the time offset\n"
" \"connections\": xxxxx, (numeric) the number of connections\n"
- " \"proxy\": \"host:port\", (string, optional) the proxy used by the server\n"
+ " \"proxy\": \"host:port\", (string, optional) the proxy used by the server\n"
" \"difficulty\": xxxxxx, (numeric) the current difficulty\n"
" \"testnet\": true|false, (boolean) if the server is using testnet or not\n"
" \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since Unix epoch) of the oldest pre-generated key in the key pool\n"
@@ -65,7 +67,7 @@ UniValue getinfo(const JSONRPCRequest& request)
" \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n"
" \"paytxfee\": x.xxxx, (numeric) the transaction fee set in " + CURRENCY_UNIT + "/kB\n"
" \"relayfee\": x.xxxx, (numeric) minimum relay fee for transactions in " + CURRENCY_UNIT + "/kB\n"
- " \"errors\": \"...\" (string) any error messages\n"
+ " \"errors\": \"...\" (string) any error messages\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getinfo", "")
@@ -84,6 +86,8 @@ UniValue getinfo(const JSONRPCRequest& request)
GetProxy(NET_IPV4, proxy);
UniValue obj(UniValue::VOBJ);
+ obj.push_back(Pair("deprecation-warning", "WARNING: getinfo is deprecated and will be fully removed in 0.16."
+ " Projects should transition to using getblockchaininfo, getnetworkinfo, and getwalletinfo before upgrading to 0.16"));
obj.push_back(Pair("version", CLIENT_VERSION));
obj.push_back(Pair("protocolversion", PROTOCOL_VERSION));
#ifdef ENABLE_WALLET
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index fde0b27a33..b4d6795e62 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/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 185bc7fd3d..b967f2dbf8 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -554,6 +554,93 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::
vErrorsRet.push_back(entry);
}
+UniValue combinerawtransaction(const JSONRPCRequest& request)
+{
+
+ if (request.fHelp || request.params.size() != 1)
+ throw std::runtime_error(
+ "combinerawtransaction [\"hexstring\",...]\n"
+ "\nCombine multiple partially signed transactions into one transaction.\n"
+ "The combined transaction may be another partially signed transaction or a \n"
+ "fully signed transaction."
+
+ "\nArguments:\n"
+ "1. \"txs\" (string) A json array of hex strings of partially signed transactions\n"
+ " [\n"
+ " \"hexstring\" (string) A transaction hash\n"
+ " ,...\n"
+ " ]\n"
+
+ "\nResult:\n"
+ "\"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n"
+
+ "\nExamples:\n"
+ + HelpExampleCli("combinerawtransaction", "[\"myhex1\", \"myhex2\", \"myhex3\"]")
+ );
+
+
+ UniValue txs = request.params[0].get_array();
+ std::vector<CMutableTransaction> txVariants(txs.size());
+
+ for (unsigned int idx = 0; idx < txs.size(); idx++) {
+ if (!DecodeHexTx(txVariants[idx], txs[idx].get_str(), true)) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed for tx %d", idx));
+ }
+ }
+
+ if (txVariants.empty()) {
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transactions");
+ }
+
+ // mergedTx will end up with all the signatures; it
+ // starts as a clone of the rawtx:
+ CMutableTransaction mergedTx(txVariants[0]);
+
+ // Fetch previous transactions (inputs):
+ CCoinsView viewDummy;
+ CCoinsViewCache view(&viewDummy);
+ {
+ LOCK(cs_main);
+ LOCK(mempool.cs);
+ CCoinsViewCache &viewChain = *pcoinsTip;
+ CCoinsViewMemPool viewMempool(&viewChain, mempool);
+ view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
+
+ for (const CTxIn& txin : mergedTx.vin) {
+ view.AccessCoin(txin.prevout); // Load entries from viewChain into view; can fail.
+ }
+
+ view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
+ }
+
+ // Use CTransaction for the constant parts of the
+ // transaction to avoid rehashing.
+ const CTransaction txConst(mergedTx);
+ // Sign what we can:
+ for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
+ CTxIn& txin = mergedTx.vin[i];
+ const Coin& coin = view.AccessCoin(txin.prevout);
+ if (coin.IsSpent()) {
+ throw JSONRPCError(RPC_VERIFY_ERROR, "Input not found or already spent");
+ }
+ const CScript& prevPubKey = coin.out.scriptPubKey;
+ const CAmount& amount = coin.out.nValue;
+
+ SignatureData sigdata;
+
+ // ... and merge in other signatures:
+ for (const CMutableTransaction& txv : txVariants) {
+ if (txv.vin.size() > i) {
+ sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i));
+ }
+ }
+
+ UpdateTransaction(mergedTx, i, sigdata);
+ }
+
+ return EncodeHexTx(mergedTx);
+}
+
UniValue signrawtransaction(const JSONRPCRequest& request)
{
#ifdef ENABLE_WALLET
@@ -626,26 +713,9 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
#endif
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true);
- std::vector<unsigned char> txData(ParseHexV(request.params[0], "argument 1"));
- CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
- std::vector<CMutableTransaction> txVariants;
- while (!ssData.empty()) {
- try {
- CMutableTransaction tx;
- ssData >> tx;
- txVariants.push_back(tx);
- }
- catch (const std::exception&) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
- }
- }
-
- if (txVariants.empty())
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction");
-
- // mergedTx will end up with all the signatures; it
- // starts as a clone of the rawtx:
- CMutableTransaction mergedTx(txVariants[0]);
+ CMutableTransaction mtx;
+ if (!DecodeHexTx(mtx, request.params[0].get_str(), true))
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
// Fetch previous transactions (inputs):
CCoinsView viewDummy;
@@ -656,7 +726,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
CCoinsViewMemPool viewMempool(&viewChain, mempool);
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
- for (const CTxIn& txin : mergedTx.vin) {
+ for (const CTxIn& txin : mtx.vin) {
view.AccessCoin(txin.prevout); // Load entries from viewChain into view; can fail.
}
@@ -781,10 +851,10 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
// Use CTransaction for the constant parts of the
// transaction to avoid rehashing.
- const CTransaction txConst(mergedTx);
+ const CTransaction txConst(mtx);
// Sign what we can:
- for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
- CTxIn& txin = mergedTx.vin[i];
+ for (unsigned int i = 0; i < mtx.vin.size(); i++) {
+ CTxIn& txin = mtx.vin[i];
const Coin& coin = view.AccessCoin(txin.prevout);
if (coin.IsSpent()) {
TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
@@ -795,17 +865,11 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
SignatureData sigdata;
// Only sign SIGHASH_SINGLE if there's a corresponding output:
- if (!fHashSingle || (i < mergedTx.vout.size()))
- ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata);
+ if (!fHashSingle || (i < mtx.vout.size()))
+ ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mtx, i, amount, nHashType), prevPubKey, sigdata);
+ sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(mtx, i));
- // ... and merge in other signatures:
- for (const CMutableTransaction& txv : txVariants) {
- if (txv.vin.size() > i) {
- sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i));
- }
- }
-
- UpdateTransaction(mergedTx, i, sigdata);
+ UpdateTransaction(mtx, i, sigdata);
ScriptError serror = SCRIPT_ERR_OK;
if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) {
@@ -815,7 +879,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
bool fComplete = vErrors.empty();
UniValue result(UniValue::VOBJ);
- result.push_back(Pair("hex", EncodeHexTx(mergedTx)));
+ result.push_back(Pair("hex", EncodeHexTx(mtx)));
result.push_back(Pair("complete", fComplete));
if (!vErrors.empty()) {
result.push_back(Pair("errors", vErrors));
@@ -905,6 +969,7 @@ static const CRPCCommand commands[] =
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, true, {"hexstring"} },
{ "rawtransactions", "decodescript", &decodescript, true, {"hexstring"} },
{ "rawtransactions", "sendrawtransaction", &sendrawtransaction, false, {"hexstring","allowhighfees"} },
+ { "rawtransactions", "combinerawtransaction", &combinerawtransaction, true, {"txs"} },
{ "rawtransactions", "signrawtransaction", &signrawtransaction, false, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */
{ "blockchain", "gettxoutproof", &gettxoutproof, true, {"txids", "blockhash"} },
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 39f9f58604..6654634bf1 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -692,7 +692,7 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
BOOST_CHECK(IsStandardTx(t, reason));
// Check dust with default relay fee:
- CAmount nDustThreshold = 182 * dustRelayFee.GetFeePerK()/1000 * 3;
+ CAmount nDustThreshold = 182 * dustRelayFee.GetFeePerK()/1000;
BOOST_CHECK_EQUAL(nDustThreshold, 546);
// dust:
t.vout[0].nValue = nDustThreshold - 1;
@@ -702,13 +702,13 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
BOOST_CHECK(IsStandardTx(t, reason));
// Check dust with odd relay fee to verify rounding:
- // nDustThreshold = 182 * 1234 / 1000 * 3
- dustRelayFee = CFeeRate(1234);
+ // nDustThreshold = 182 * 3702 / 1000
+ dustRelayFee = CFeeRate(3702);
// dust:
- t.vout[0].nValue = 672 - 1;
+ t.vout[0].nValue = 673 - 1;
BOOST_CHECK(!IsStandardTx(t, reason));
// not dust:
- t.vout[0].nValue = 672;
+ t.vout[0].nValue = 673;
BOOST_CHECK(IsStandardTx(t, reason));
dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
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 55f4abd7a3..4275a9181d 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 13da7bb2c2..223790aa40 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -57,6 +57,8 @@ CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE);
*/
CFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE);
+CFeeRate CWallet::m_discard_rate = CFeeRate(DEFAULT_DISCARD_FEE);
+
const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
/** @defgroup mapWallet
@@ -87,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
@@ -100,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));
@@ -115,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)
@@ -162,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)
{
@@ -2450,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;
@@ -2472,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++)
@@ -2494,13 +2522,21 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
}
}
- // optionally keep the change output key
- if (keepReserveKey)
- reservekey.KeepKey();
return true;
}
+static CFeeRate GetDiscardRate(const CBlockPolicyEstimator& estimator)
+{
+ unsigned int highest_target = estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
+ CFeeRate discard_rate = estimator.estimateSmartFee(highest_target, nullptr /* FeeCalculation */, false /* conservative */);
+ // Don't let discard_rate be greater than longest possible fee estimate if we get a valid fee estimate
+ discard_rate = (discard_rate == CFeeRate(0)) ? CWallet::m_discard_rate : std::min(discard_rate, CWallet::m_discard_rate);
+ // Discard rate must be at least dustRelayFee
+ discard_rate = std::max(discard_rate, ::dustRelayFee);
+ return discard_rate;
+}
+
bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet,
int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign)
{
@@ -2600,6 +2636,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
CTxOut change_prototype_txout(0, scriptChange);
size_t change_prototype_size = GetSerializeSize(change_prototype_txout, SER_DISK, 0);
+ CFeeRate discard_rate = GetDiscardRate(::feeEstimator);
nFeeRet = 0;
bool pick_new_inputs = true;
CAmount nValueIn = 0;
@@ -2667,7 +2704,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
// Never create dust outputs; if we would, just
// add the dust to the fee.
- if (IsDust(newTxOut, ::dustRelayFee))
+ if (IsDust(newTxOut, discard_rate))
{
nChangePosInOut = -1;
nFeeRet += nChange;
@@ -2747,7 +2784,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
// (because of reduced tx size) and so we should add a
// change output. Only try this once.
CAmount fee_needed_for_change = GetMinimumFee(change_prototype_size, coin_control, ::mempool, ::feeEstimator, nullptr);
- CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, ::dustRelayFee);
+ CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, discard_rate);
CAmount max_excess_fee = fee_needed_for_change + minimum_value_for_change;
if (nFeeRet > nFeeNeeded + max_excess_fee && nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) {
pick_new_inputs = false;
@@ -3185,27 +3222,25 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
CWalletDB walletdb(*dbw);
for (int64_t i = missingInternal + missingExternal; i--;)
{
- int64_t nEnd = 1;
if (i < missingInternal) {
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;
@@ -3280,7 +3315,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);
@@ -3788,6 +3824,9 @@ std::string CWallet::GetWalletHelpString(bool showDebug)
strUsage += HelpMessageOpt("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), DEFAULT_KEYPOOL_SIZE));
strUsage += HelpMessageOpt("-fallbackfee=<amt>", strprintf(_("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)"),
CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE)));
+ strUsage += HelpMessageOpt("-discardfee=<amt>", strprintf(_("The fee rate (in %s/kB) used to discard change (to fee) if it would be dust at this fee rate (default: %s) "
+ "Note: We will always discard up to the dust relay fee and a discard fee above that is limited by the longest target fee estimate"),
+ CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_FEE)));
strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"),
CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)));
strUsage += HelpMessageOpt("-paytxfee=<amt>", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"),
@@ -4113,6 +4152,16 @@ bool CWallet::ParameterInteraction()
_("This is the transaction fee you may pay when fee estimates are not available."));
CWallet::fallbackFee = CFeeRate(nFeePerK);
}
+ if (IsArgSet("-discardfee"))
+ {
+ CAmount nFeePerK = 0;
+ if (!ParseMoney(GetArg("-discardfee", ""), nFeePerK))
+ return InitError(strprintf(_("Invalid amount for -discardfee=<amount>: '%s'"), GetArg("-discardfee", "")));
+ if (nFeePerK > HIGH_TX_FEE_PER_KB)
+ InitWarning(AmountHighWarn("-discardfee") + " " +
+ _("This is the transaction fee you may discard if change is smaller than dust at this level"));
+ CWallet::m_discard_rate = CFeeRate(nFeePerK);
+ }
if (IsArgSet("-paytxfee"))
{
CAmount nFeePerK = 0;
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index b716e46a7d..bcd7e4b4ee 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -40,11 +40,13 @@ 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
static const CAmount DEFAULT_FALLBACK_FEE = 20000;
+//! -m_discard_rate default
+static const CAmount DEFAULT_DISCARD_FEE = 10000;
//! -mintxfee default
static const CAmount DEFAULT_TRANSACTION_MINFEE = 1000;
//! minimum recommended increment for BIP 125 replacement txs
@@ -697,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;
@@ -743,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
@@ -795,6 +799,7 @@ public:
nAccountingEntryNumber = 0;
nNextResend = 0;
nLastResend = 0;
+ m_max_keypool_index = 0;
nTimeFirstKey = 0;
fBroadcastTransactions = false;
nRelockTime = 0;
@@ -866,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)
@@ -945,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);
/**
@@ -965,6 +971,7 @@ public:
static CFeeRate minTxFee;
static CFeeRate fallbackFee;
+ static CFeeRate m_discard_rate;
/**
* Estimate the minimum fee considering user set parameters
* and the required fee
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;