diff options
Diffstat (limited to 'src')
77 files changed, 1503 insertions, 1159 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 4fbd605d9e..9ca2b2c82a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -135,10 +135,10 @@ BITCOIN_CORE_H = \ rpc/register.h \ rpc/util.h \ scheduler.h \ + script/ismine.h \ script/sigcache.h \ script/sign.h \ script/standard.h \ - script/ismine.h \ streams.h \ support/allocators/secure.h \ support/allocators/zeroafterfree.h \ @@ -216,7 +216,6 @@ libbitcoin_server_a_SOURCES = \ rpc/safemode.cpp \ rpc/server.cpp \ script/sigcache.cpp \ - script/ismine.cpp \ timedata.cpp \ torcontrol.cpp \ txdb.cpp \ @@ -333,6 +332,7 @@ libbitcoin_common_a_SOURCES = \ policy/feerate.cpp \ protocol.cpp \ scheduler.cpp \ + script/ismine.cpp \ script/sign.cpp \ script/standard.cpp \ warnings.cpp \ @@ -389,10 +389,10 @@ endif bitcoind_LDADD = \ $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_WALLET) \ $(LIBBITCOIN_COMMON) \ $(LIBUNIVALUE) \ $(LIBBITCOIN_UTIL) \ - $(LIBBITCOIN_WALLET) \ $(LIBBITCOIN_ZMQ) \ $(LIBBITCOIN_CONSENSUS) \ $(LIBBITCOIN_CRYPTO) \ diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index 8e2e587d32..13c27299f8 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -35,6 +35,7 @@ bench_bench_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CLFAGS bench_bench_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) bench_bench_bitcoin_LDADD = \ $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_WALLET) \ $(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CONSENSUS) \ @@ -51,7 +52,6 @@ endif if ENABLE_WALLET bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp -bench_bench_bitcoin_LDADD += $(LIBBITCOIN_WALLET) $(LIBBITCOIN_CRYPTO) endif bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) diff --git a/src/addrman.h b/src/addrman.h index 172cb5f45b..38da754afb 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -59,7 +59,7 @@ public: template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(*(CAddress*)this); + READWRITE(*static_cast<CAddress*>(this)); READWRITE(source); READWRITE(nLastSuccess); READWRITE(nAttempts); diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index e9ef75eb7b..a60d3b3b6d 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -35,20 +35,20 @@ std::string HelpMessageCli() std::string strUsage; strUsage += HelpMessageGroup(_("Options:")); strUsage += HelpMessageOpt("-?", _("This help message")); - strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME)); + strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)"), BITCOIN_CONF_FILENAME)); strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory")); strUsage += HelpMessageOpt("-getinfo", _("Get general information from the remote server. Note that unlike server-side RPC calls, the results of -getinfo is the result of multiple non-atomic requests. Some entries in the result may represent results from different states (e.g. wallet balance may be as of a different block from the chain state reported)")); AppendParamsHelpMessages(strUsage); strUsage += HelpMessageOpt("-named", strprintf(_("Pass named instead of positional arguments (default: %s)"), DEFAULT_NAMED)); + strUsage += HelpMessageOpt("-rpcclienttimeout=<n>", strprintf(_("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT)); strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), DEFAULT_RPCCONNECT)); + strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections")); strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort())); - strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start")); strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections")); - 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("-stdinrpcpass", strprintf(_("Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password."))); - strUsage += HelpMessageOpt("-stdin", _("Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password.")); + strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start")); 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)")); + strUsage += HelpMessageOpt("-stdin", _("Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password.")); + strUsage += HelpMessageOpt("-stdinrpcpass", strprintf(_("Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password."))); return strUsage; } @@ -198,6 +198,7 @@ static void http_error_cb(enum evhttp_request_error err, void *ctx) class BaseRequestHandler { public: + virtual ~BaseRequestHandler() {} virtual UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) = 0; virtual UniValue ProcessReply(const UniValue &batch_in) = 0; }; diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index f1cf7c9d2d..a9f7264f68 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -77,18 +77,18 @@ static int AppInitRawTx(int argc, char* argv[]) strUsage += HelpMessageOpt("in=TXID:VOUT(:SEQUENCE_NUMBER)", _("Add input to TX")); strUsage += HelpMessageOpt("locktime=N", _("Set TX lock time to N")); strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N")); - strUsage += HelpMessageOpt("replaceable(=N)", _("Set RBF opt-in sequence number for input N (if not provided, opt-in all available inputs)")); strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", _("Add address-based output to TX")); + strUsage += HelpMessageOpt("outdata=[VALUE:]DATA", _("Add data-based output to TX")); + strUsage += HelpMessageOpt("outmultisig=VALUE:REQUIRED:PUBKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]", _("Add Pay To n-of-m Multi-sig output to TX. n = REQUIRED, m = PUBKEYS") + ". " + + _("Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output") + ". " + + _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.")); strUsage += HelpMessageOpt("outpubkey=VALUE:PUBKEY[:FLAGS]", _("Add pay-to-pubkey output to TX") + ". " + _("Optionally add the \"W\" flag to produce a pay-to-witness-pubkey-hash output") + ". " + _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.")); - strUsage += HelpMessageOpt("outdata=[VALUE:]DATA", _("Add data-based output to TX")); strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT[:FLAGS]", _("Add raw script output to TX") + ". " + _("Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output") + ". " + _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.")); - strUsage += HelpMessageOpt("outmultisig=VALUE:REQUIRED:PUBKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]", _("Add Pay To n-of-m Multi-sig output to TX. n = REQUIRED, m = PUBKEYS") + ". " + - _("Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output") + ". " + - _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.")); + strUsage += HelpMessageOpt("replaceable(=N)", _("Set RBF opt-in sequence number for input N (if not provided, opt-in all available inputs)")); strUsage += HelpMessageOpt("sign=SIGHASH-FLAGS", _("Add zero or more signatures to transaction") + ". " + _("This command requires JSON registers:") + _("prevtxs=JSON object") + ", " + diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 96e9b2727b..6eb223171f 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -126,7 +126,7 @@ public: // Note that of those which support the service bits prefix, most only support a subset of // possible options. - // This is fine at runtime as we'll fall back to using them as a oneshot if they dont support the + // This is fine at runtime as we'll fall back to using them as a oneshot if they don't support the // service bits we want, but we should get them updated to support all service bits wanted by any // release ASAP to avoid it where possible. vSeeds.emplace_back("seed.bitcoin.sipa.be"); // Pieter Wuille, only supports x1, x5, x9, and xd diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index a03137f407..a04258fd40 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -17,51 +17,13 @@ const std::string CBaseChainParams::REGTEST = "regtest"; void AppendParamsHelpMessages(std::string& strUsage, bool debugHelp) { strUsage += HelpMessageGroup(_("Chain selection options:")); - strUsage += HelpMessageOpt("-testnet", _("Use the test chain")); if (debugHelp) { strUsage += HelpMessageOpt("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. " "This is intended for regression testing tools and app development."); } + strUsage += HelpMessageOpt("-testnet", _("Use the test chain")); } -/** - * Main network - */ -class CBaseMainParams : public CBaseChainParams -{ -public: - CBaseMainParams() - { - nRPCPort = 8332; - } -}; - -/** - * Testnet (v3) - */ -class CBaseTestNetParams : public CBaseChainParams -{ -public: - CBaseTestNetParams() - { - nRPCPort = 18332; - strDataDir = "testnet3"; - } -}; - -/* - * Regression test - */ -class CBaseRegTestParams : public CBaseChainParams -{ -public: - CBaseRegTestParams() - { - nRPCPort = 18443; - strDataDir = "regtest"; - } -}; - static std::unique_ptr<CBaseChainParams> globalChainBaseParams; const CBaseChainParams& BaseParams() @@ -73,11 +35,11 @@ const CBaseChainParams& BaseParams() std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain) { if (chain == CBaseChainParams::MAIN) - return std::unique_ptr<CBaseChainParams>(new CBaseMainParams()); + return MakeUnique<CBaseChainParams>("", 8332); else if (chain == CBaseChainParams::TESTNET) - return std::unique_ptr<CBaseChainParams>(new CBaseTestNetParams()); + return MakeUnique<CBaseChainParams>("testnet3", 18332); else if (chain == CBaseChainParams::REGTEST) - return std::unique_ptr<CBaseChainParams>(new CBaseRegTestParams()); + return MakeUnique<CBaseChainParams>("regtest", 18443); else throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain)); } diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h index b4d2bb4f08..2cb860380e 100644 --- a/src/chainparamsbase.h +++ b/src/chainparamsbase.h @@ -24,9 +24,10 @@ public: const std::string& DataDir() const { return strDataDir; } int RPCPort() const { return nRPCPort; } -protected: - CBaseChainParams() {} + CBaseChainParams() = delete; + CBaseChainParams(const std::string& data_dir, int rpc_port) : nRPCPort(rpc_port), strDataDir(data_dir) {} +private: int nRPCPort; std::string strDataDir; }; diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h index 2b102c464f..6e2b3c34a2 100644 --- a/src/chainparamsseeds.h +++ b/src/chainparamsseeds.h @@ -5,7 +5,7 @@ * AUTOGENERATED by contrib/seeds/generate-seeds.py * * Each line contains a 16-byte IPv6 address and a port. - * IPv4 as well as onion addresses are wrapped inside a IPv6 address accordingly. + * IPv4 as well as onion addresses are wrapped inside an IPv6 address accordingly. */ static SeedSpec6 pnSeed6_main[] = { {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x13,0x05,0x7f}, 8333}, diff --git a/src/clientversion.cpp b/src/clientversion.cpp index 7aa0a8d660..662fbb6e77 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -43,7 +43,7 @@ const std::string CLIENT_NAME("Satoshi"); //! git will put "#define GIT_ARCHIVE 1" on the next line inside archives. $Format:%n#define GIT_ARCHIVE 1$ #ifdef GIT_ARCHIVE -#define GIT_COMMIT_ID "$Format:%h$" +#define GIT_COMMIT_ID "$Format:%H$" #define GIT_COMMIT_DATE "$Format:%cD$" #endif diff --git a/src/core_read.cpp b/src/core_read.cpp index 4ccb967087..6a8308f869 100644 --- a/src/core_read.cpp +++ b/src/core_read.cpp @@ -33,14 +33,14 @@ CScript ParseScript(const std::string& s) if (op < OP_NOP && op != OP_RESERVED) continue; - const char* name = GetOpName((opcodetype)op); + const char* name = GetOpName(static_cast<opcodetype>(op)); if (strcmp(name, "OP_UNKNOWN") == 0) continue; std::string strName(name); - mapOpNames[strName] = (opcodetype)op; + mapOpNames[strName] = static_cast<opcodetype>(op); // Convenience: OP_ADD and just ADD are both recognized: boost::algorithm::replace_first(strName, "OP_", ""); - mapOpNames[strName] = (opcodetype)op; + mapOpNames[strName] = static_cast<opcodetype>(op); } } diff --git a/src/httpserver.cpp b/src/httpserver.cpp index a022d220e0..36db530c82 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -73,34 +73,13 @@ private: std::deque<std::unique_ptr<WorkItem>> queue; bool running; size_t maxDepth; - int numThreads; - - /** RAII object to keep track of number of running worker threads */ - class ThreadCounter - { - public: - WorkQueue &wq; - explicit ThreadCounter(WorkQueue &w): wq(w) - { - std::lock_guard<std::mutex> lock(wq.cs); - wq.numThreads += 1; - } - ~ThreadCounter() - { - std::lock_guard<std::mutex> lock(wq.cs); - wq.numThreads -= 1; - wq.cond.notify_all(); - } - }; public: explicit WorkQueue(size_t _maxDepth) : running(true), - maxDepth(_maxDepth), - numThreads(0) + maxDepth(_maxDepth) { } - /** Precondition: worker threads have all stopped - * (call WaitExit) + /** Precondition: worker threads have all stopped (they have been joined). */ ~WorkQueue() { @@ -119,7 +98,6 @@ public: /** Thread function */ void Run() { - ThreadCounter count(*this); while (true) { std::unique_ptr<WorkItem> i; { @@ -141,13 +119,6 @@ public: running = false; cond.notify_all(); } - /** Wait for worker threads to exit */ - void WaitExit() - { - std::unique_lock<std::mutex> lock(cs); - while (numThreads > 0) - cond.wait(lock); - } }; struct HTTPPathHandler @@ -449,6 +420,7 @@ bool UpdateHTTPServerLogging(bool enable) { std::thread threadHTTP; std::future<bool> threadResult; +static std::vector<std::thread> g_thread_http_workers; bool StartHTTPServer() { @@ -460,8 +432,7 @@ bool StartHTTPServer() threadHTTP = std::thread(std::move(task), eventBase, eventHTTP); for (int i = 0; i < rpcThreads; i++) { - std::thread rpc_worker(HTTPWorkQueueRun, workQueue); - rpc_worker.detach(); + g_thread_http_workers.emplace_back(HTTPWorkQueueRun, workQueue); } return true; } @@ -486,7 +457,10 @@ void StopHTTPServer() LogPrint(BCLog::HTTP, "Stopping HTTP server\n"); if (workQueue) { LogPrint(BCLog::HTTP, "Waiting for HTTP worker threads to exit\n"); - workQueue->WaitExit(); + for (auto& thread: g_thread_http_workers) { + thread.join(); + } + g_thread_http_workers.clear(); delete workQueue; workQueue = nullptr; } @@ -525,7 +499,7 @@ struct event_base* EventBase() static void httpevent_callback_fn(evutil_socket_t, short, void* data) { // Static handler: simply call inner handler - HTTPEvent *self = ((HTTPEvent*)data); + HTTPEvent *self = static_cast<HTTPEvent*>(data); self->handler(); if (self->deleteWhenTriggered) delete self; diff --git a/src/init.cpp b/src/init.cpp index 238da00ee2..5c5d1ee792 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -165,6 +165,7 @@ void Interrupt() InterruptRPC(); InterruptREST(); InterruptTorControl(); + InterruptMapPort(); if (g_connman) g_connman->Interrupt(); } @@ -191,7 +192,7 @@ void Shutdown() #ifdef ENABLE_WALLET FlushWallets(); #endif - MapPort(false); + StopMapPort(); // Because these depend on each-other, we make sure that neither can be // using the other before destroying them. @@ -213,7 +214,7 @@ void Shutdown() if (fFeeEstimatesInitialized) { - ::feeEstimator.FlushUnconfirmed(::mempool); + ::feeEstimator.FlushUnconfirmed(); fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME; CAutoFile est_fileout(fsbridge::fopen(est_path, "wb"), SER_DISK, CLIENT_VERSION); if (!est_fileout.IsNull()) @@ -331,11 +332,12 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-?", _("Print this help message and exit")); strUsage += HelpMessageOpt("-version", _("Print version and exit")); strUsage += HelpMessageOpt("-alertnotify=<cmd>", _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)")); + strUsage +=HelpMessageOpt("-assumevalid=<hex>", strprintf(_("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)"), defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex())); strUsage += HelpMessageOpt("-blocknotify=<cmd>", _("Execute command when the best block changes (%s in cmd is replaced by block hash)")); + strUsage += HelpMessageOpt("-blockreconstructionextratxn=<n>", strprintf(_("Extra transactions to keep in memory for compact block reconstructions (default: %u)"), DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN)); if (showDebug) strUsage += HelpMessageOpt("-blocksonly", strprintf(_("Whether to operate in a blocks only mode (default: %u)"), DEFAULT_BLOCKSONLY)); - strUsage +=HelpMessageOpt("-assumevalid=<hex>", strprintf(_("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)"), defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex())); - strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME)); + strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)"), BITCOIN_CONF_FILENAME)); if (mode == HMM_BITCOIND) { #if HAVE_DECL_DAEMON @@ -347,28 +349,27 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize)); } strUsage += HelpMessageOpt("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache)); + strUsage += HelpMessageOpt("-debuglogfile=<file>", strprintf(_("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)"), DEFAULT_DEBUGLOGFILE)); if (showDebug) strUsage += HelpMessageOpt("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER)); strUsage += HelpMessageOpt("-loadblock=<file>", _("Imports blocks from external blk000??.dat file on startup")); - strUsage += HelpMessageOpt("-debuglogfile=<file>", strprintf(_("Specify location of debug log file: this can be an absolute path or a path relative to the data directory (default: %s)"), DEFAULT_DEBUGLOGFILE)); - strUsage += HelpMessageOpt("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS)); strUsage += HelpMessageOpt("-maxmempool=<n>", strprintf(_("Keep the transaction memory pool below <n> megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE)); + strUsage += HelpMessageOpt("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS)); strUsage += HelpMessageOpt("-mempoolexpiry=<n>", strprintf(_("Do not keep transactions in the mempool longer than <n> hours (default: %u)"), DEFAULT_MEMPOOL_EXPIRY)); if (showDebug) { strUsage += HelpMessageOpt("-minimumchainwork=<hex>", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex())); } - strUsage += HelpMessageOpt("-persistmempool", strprintf(_("Whether to save the mempool on shutdown and load on restart (default: %u)"), DEFAULT_PERSIST_MEMPOOL)); - strUsage += HelpMessageOpt("-blockreconstructionextratxn=<n>", strprintf(_("Extra transactions to keep in memory for compact block reconstructions (default: %u)"), DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN)); strUsage += HelpMessageOpt("-par=<n>", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"), -GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS)); + strUsage += HelpMessageOpt("-persistmempool", strprintf(_("Whether to save the mempool on shutdown and load on restart (default: %u)"), DEFAULT_PERSIST_MEMPOOL)); #ifndef WIN32 - strUsage += HelpMessageOpt("-pid=<file>", strprintf(_("Specify pid file (default: %s)"), BITCOIN_PID_FILENAME)); + strUsage += HelpMessageOpt("-pid=<file>", strprintf(_("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)"), BITCOIN_PID_FILENAME)); #endif strUsage += HelpMessageOpt("-prune=<n>", strprintf(_("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. " "Warning: Reverting this setting requires re-downloading the entire blockchain. " "(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB)"), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); - strUsage += HelpMessageOpt("-reindex-chainstate", _("Rebuild chain state from the currently indexed blocks")); strUsage += HelpMessageOpt("-reindex", _("Rebuild chain state and block index from the blk*.dat files on disk")); + strUsage += HelpMessageOpt("-reindex-chainstate", _("Rebuild chain state from the currently indexed blocks")); #ifndef WIN32 strUsage += HelpMessageOpt("-sysperms", _("Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)")); #endif @@ -391,10 +392,11 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-maxreceivebuffer=<n>", strprintf(_("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)"), DEFAULT_MAXRECEIVEBUFFER)); strUsage += HelpMessageOpt("-maxsendbuffer=<n>", strprintf(_("Maximum per-connection send buffer, <n>*1000 bytes (default: %u)"), DEFAULT_MAXSENDBUFFER)); strUsage += HelpMessageOpt("-maxtimeadjustment", strprintf(_("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)"), DEFAULT_MAX_TIME_ADJUSTMENT)); + strUsage += HelpMessageOpt("-maxuploadtarget=<n>", strprintf(_("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)"), DEFAULT_MAX_UPLOAD_TARGET)); strUsage += HelpMessageOpt("-onion=<ip:port>", strprintf(_("Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s)"), "-proxy")); strUsage += HelpMessageOpt("-onlynet=<net>", _("Only connect to nodes in network <net> (ipv4, ipv6 or onion)")); - strUsage += HelpMessageOpt("-permitbaremultisig", strprintf(_("Relay non-P2SH multisig (default: %u)"), DEFAULT_PERMIT_BAREMULTISIG)); strUsage += HelpMessageOpt("-peerbloomfilters", strprintf(_("Support filtering of blocks and transaction with bloom filters (default: %u)"), DEFAULT_PEERBLOOMFILTERS)); + strUsage += HelpMessageOpt("-permitbaremultisig", strprintf(_("Relay non-P2SH multisig (default: %u)"), DEFAULT_PERMIT_BAREMULTISIG)); strUsage += HelpMessageOpt("-port=<port>", strprintf(_("Listen for connections on <port> (default: %u or testnet: %u)"), defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort())); strUsage += HelpMessageOpt("-proxy=<ip:port>", _("Connect through SOCKS5 proxy")); strUsage += HelpMessageOpt("-proxyrandomize", strprintf(_("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)"), DEFAULT_PROXYRANDOMIZE)); @@ -412,7 +414,6 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-whitebind=<addr>", _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6")); strUsage += HelpMessageOpt("-whitelist=<IP address or network>", _("Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times.") + " " + _("Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway")); - strUsage += HelpMessageOpt("-maxuploadtarget=<n>", strprintf(_("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)"), DEFAULT_MAX_UPLOAD_TARGET)); #ifdef ENABLE_WALLET strUsage += GetWalletHelpString(showDebug); @@ -427,7 +428,6 @@ std::string HelpMessage(HelpMessageMode mode) #endif strUsage += HelpMessageGroup(_("Debugging/Testing options:")); - strUsage += HelpMessageOpt("-uacomment=<cmt>", _("Append comment to the user agent string")); if (showDebug) { strUsage += HelpMessageOpt("-checkblocks=<n>", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), DEFAULT_CHECKBLOCKS)); @@ -464,12 +464,13 @@ std::string HelpMessage(HelpMessageMode mode) } strUsage += HelpMessageOpt("-maxtxfee=<amt>", strprintf(_("Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE))); - strUsage += HelpMessageOpt("-printtoconsole", _("Send trace/debug info to console instead of debug.log file")); if (showDebug) { strUsage += HelpMessageOpt("-printpriority", strprintf("Log transaction fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY)); } + strUsage += HelpMessageOpt("-printtoconsole", _("Send trace/debug info to console instead of debug.log file")); strUsage += HelpMessageOpt("-shrinkdebugfile", _("Shrink debug.log file on client startup (default: 1 when no -debug)")); + strUsage += HelpMessageOpt("-uacomment=<cmt>", _("Append comment to the user agent string")); AppendParamsHelpMessages(strUsage, showDebug); @@ -485,32 +486,32 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-mempoolreplacement", strprintf(_("Enable transaction replacement in the memory pool (default: %u)"), DEFAULT_ENABLE_REPLACEMENT)); strUsage += HelpMessageOpt("-minrelaytxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE))); - strUsage += HelpMessageOpt("-whitelistrelay", strprintf(_("Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)"), DEFAULT_WHITELISTRELAY)); strUsage += HelpMessageOpt("-whitelistforcerelay", strprintf(_("Force relay of transactions from whitelisted peers even if they violate local relay policy (default: %d)"), DEFAULT_WHITELISTFORCERELAY)); + strUsage += HelpMessageOpt("-whitelistrelay", strprintf(_("Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)"), DEFAULT_WHITELISTRELAY)); strUsage += HelpMessageGroup(_("Block creation options:")); - strUsage += HelpMessageOpt("-blockmaxweight=<n>", strprintf(_("Set maximum BIP141 block weight (default: %d)"), DEFAULT_BLOCK_MAX_WEIGHT)); strUsage += HelpMessageOpt("-blockmaxsize=<n>", _("Set maximum BIP141 block weight to this * 4. Deprecated, use blockmaxweight")); + strUsage += HelpMessageOpt("-blockmaxweight=<n>", strprintf(_("Set maximum BIP141 block weight (default: %d)"), DEFAULT_BLOCK_MAX_WEIGHT)); strUsage += HelpMessageOpt("-blockmintxfee=<amt>", strprintf(_("Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE))); if (showDebug) strUsage += HelpMessageOpt("-blockversion=<n>", "Override block version to test forking scenarios"); strUsage += HelpMessageGroup(_("RPC server options:")); - strUsage += HelpMessageOpt("-server", _("Accept command line and JSON-RPC commands")); strUsage += HelpMessageOpt("-rest", strprintf(_("Accept public REST requests (default: %u)"), DEFAULT_REST_ENABLE)); + strUsage += HelpMessageOpt("-rpcallowip=<ip>", _("Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times")); + strUsage += HelpMessageOpt("-rpcauth=<userpw>", _("Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times")); strUsage += HelpMessageOpt("-rpcbind=<addr>[:port]", _("Bind to given address to listen for JSON-RPC connections. This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost, or if -rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)")); - strUsage += HelpMessageOpt("-rpccookiefile=<loc>", _("Location of the auth cookie (default: data dir)")); - strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections")); + strUsage += HelpMessageOpt("-rpccookiefile=<loc>", _("Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)")); strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections")); - strUsage += HelpMessageOpt("-rpcauth=<userpw>", _("Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times")); strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Listen for JSON-RPC connections on <port> (default: %u or testnet: %u)"), defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort())); - strUsage += HelpMessageOpt("-rpcallowip=<ip>", _("Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times")); strUsage += HelpMessageOpt("-rpcserialversion", strprintf(_("Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)"), DEFAULT_RPC_SERIALIZE_VERSION)); + if (showDebug) + strUsage += HelpMessageOpt("-rpcservertimeout=<n>", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT)); strUsage += HelpMessageOpt("-rpcthreads=<n>", strprintf(_("Set the number of threads to service RPC calls (default: %d)"), DEFAULT_HTTP_THREADS)); - if (showDebug) { + strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections")); + if (showDebug) strUsage += HelpMessageOpt("-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE)); - strUsage += HelpMessageOpt("-rpcservertimeout=<n>", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT)); - } + strUsage += HelpMessageOpt("-server", _("Accept command line and JSON-RPC commands")); return strUsage; } @@ -545,7 +546,8 @@ static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex std::string strCmd = gArgs.GetArg("-blocknotify", ""); if (!strCmd.empty()) { boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex()); - boost::thread t(runCommand, strCmd); // thread runs free + std::thread t(runCommand, strCmd); + t.detach(); // thread runs free } } @@ -678,11 +680,13 @@ void ThreadImport(std::vector<fs::path> vImportFiles) if (!ActivateBestChain(state, chainparams)) { LogPrintf("Failed to connect best block"); StartShutdown(); + return; } if (gArgs.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) { LogPrintf("Stopping after block import\n"); StartShutdown(); + return; } } // End scope of CImportingNow if (gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) { @@ -1425,6 +1429,9 @@ bool AppInitMain() pcoinsTip.reset(); pcoinsdbview.reset(); pcoinscatcher.reset(); + // new CBlockTreeDB tries to delete the existing file, which + // fails if it's still open from the previous loop. Close it first: + pblocktree.reset(); pblocktree.reset(new CBlockTreeDB(nBlockTreeDBCache, false, fReset)); if (fReset) { @@ -1519,7 +1526,7 @@ bool AppInitMain() if (!is_coinsview_empty) { uiInterface.InitMessage(_("Verifying blocks...")); if (fHavePruned && gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) { - LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks", + LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n", MIN_BLOCKS_TO_KEEP); } @@ -1645,12 +1652,19 @@ bool AppInitMain() // Wait for genesis block to be processed { WaitableLock lock(cs_GenesisWait); - while (!fHaveGenesis) { - condvar_GenesisWait.wait(lock); + // We previously could hang here if StartShutdown() is called prior to + // ThreadImport getting started, so instead we just wait on a timer to + // check ShutdownRequested() regularly. + while (!fHaveGenesis && !ShutdownRequested()) { + condvar_GenesisWait.wait_for(lock, std::chrono::milliseconds(500)); } uiInterface.NotifyBlockTip.disconnect(BlockNotifyGenesisWait); } + if (ShutdownRequested()) { + return false; + } + // ********************************************************* Step 11: start node int chain_active_height; @@ -1664,12 +1678,14 @@ bool AppInitMain() LogPrintf("nBestHeight = %d\n", chain_active_height); if (gArgs.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) - StartTorControl(threadGroup, scheduler); + StartTorControl(); - Discover(threadGroup); + Discover(); // Map ports with UPnP - MapPort(gArgs.GetBoolArg("-upnp", DEFAULT_UPNP)); + if (gArgs.GetBoolArg("-upnp", DEFAULT_UPNP)) { + StartMapPort(); + } CConnman::Options connOptions; connOptions.nLocalServices = nLocalServices; diff --git a/src/key.cpp b/src/key.cpp index e998e3db6e..ffed989be1 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -44,7 +44,7 @@ static int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *ou if (end - privkey < 1 || !(*privkey & 0x80u)) { return 0; } - size_t lenb = *privkey & ~0x80u; privkey++; + ptrdiff_t lenb = *privkey & ~0x80u; privkey++; if (lenb < 1 || lenb > 2) { return 0; } @@ -52,7 +52,7 @@ static int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *ou return 0; } /* sequence length */ - size_t len = privkey[lenb-1] | (lenb > 1 ? privkey[lenb-2] << 8 : 0u); + ptrdiff_t len = privkey[lenb-1] | (lenb > 1 ? privkey[lenb-2] << 8 : 0u); privkey += lenb; if (end - privkey < len) { return 0; @@ -66,7 +66,7 @@ static int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *ou if (end - privkey < 2 || privkey[0] != 0x04u) { return 0; } - size_t oslen = privkey[1]; + ptrdiff_t oslen = privkey[1]; privkey += 2; if (oslen > 32 || end - privkey < oslen) { return 0; diff --git a/src/net.cpp b/src/net.cpp index 8111390749..201914685c 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -297,7 +297,7 @@ CNode* CConnman::FindNode(const CNetAddr& ip) { LOCK(cs_vNodes); for (CNode* pnode : vNodes) { - if ((CNetAddr)pnode->addr == ip) { + if (static_cast<CNetAddr>(pnode->addr) == ip) { return pnode; } } @@ -308,7 +308,7 @@ CNode* CConnman::FindNode(const CSubNet& subNet) { LOCK(cs_vNodes); for (CNode* pnode : vNodes) { - if (subNet.Match((CNetAddr)pnode->addr)) { + if (subNet.Match(static_cast<CNetAddr>(pnode->addr))) { return pnode; } } @@ -330,7 +330,7 @@ CNode* CConnman::FindNode(const CService& addr) { LOCK(cs_vNodes); for (CNode* pnode : vNodes) { - if ((CService)pnode->addr == addr) { + if (static_cast<CService>(pnode->addr) == addr) { return pnode; } } @@ -370,7 +370,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo return nullptr; // Look for an existing connection - CNode* pnode = FindNode((CService)addrConnect); + CNode* pnode = FindNode(static_cast<CService>(addrConnect)); if (pnode) { LogPrintf("Failed to open new connection, already connected\n"); @@ -398,7 +398,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo // Also store the name we used to connect in that CNode, so that future FindNode() calls to that // name catch this early. LOCK(cs_vNodes); - CNode* pnode = FindNode((CService)addrConnect); + CNode* pnode = FindNode(static_cast<CService>(addrConnect)); if (pnode) { pnode->MaybeSetAddrName(std::string(pszDest)); @@ -410,7 +410,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo // Connect bool connected = false; - SOCKET hSocket; + SOCKET hSocket = INVALID_SOCKET; proxyType proxy; if (addrConnect.IsValid()) { bool proxyConnectionFailed = false; @@ -559,7 +559,7 @@ void CConnman::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t ba { LOCK(cs_vNodes); for (CNode* pnode : vNodes) { - if (subNet.Match((CNetAddr)pnode->addr)) + if (subNet.Match(static_cast<CNetAddr>(pnode->addr))) pnode->fDisconnect = true; } } @@ -1431,7 +1431,7 @@ void CConnman::ThreadSocketHandler() } else if (!pnode->fSuccessfullyConnected) { - LogPrintf("version handshake timeout from %d\n", pnode->GetId()); + LogPrint(BCLog::NET, "version handshake timeout from %d\n", pnode->GetId()); pnode->fDisconnect = true; } } @@ -1459,6 +1459,8 @@ void CConnman::WakeMessageHandler() #ifdef USE_UPNP +static CThreadInterrupt g_upnp_interrupt; +static std::thread g_upnp_thread; void ThreadMapPort() { std::string port = strprintf("%u", GetListenPort()); @@ -1509,35 +1511,29 @@ void ThreadMapPort() std::string strDesc = "Bitcoin " + FormatFullVersion(); - try { - while (true) { + do { #ifndef UPNPDISCOVER_SUCCESS - /* miniupnpc 1.5 */ - r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, - port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0); + /* miniupnpc 1.5 */ + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, + port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0); #else - /* miniupnpc 1.6 */ - r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, - port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); + /* miniupnpc 1.6 */ + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, + port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); #endif - if(r!=UPNPCOMMAND_SUCCESS) - LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", - port, port, lanaddr, r, strupnperror(r)); - else - LogPrintf("UPnP Port Mapping successful.\n"); - - MilliSleep(20*60*1000); // Refresh every 20 minutes - } - } - catch (const boost::thread_interrupted&) - { - r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); - LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r); - freeUPNPDevlist(devlist); devlist = nullptr; - FreeUPNPUrls(&urls); - throw; + if(r!=UPNPCOMMAND_SUCCESS) + LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", + port, port, lanaddr, r, strupnperror(r)); + else + LogPrintf("UPnP Port Mapping successful.\n"); } + while(g_upnp_interrupt.sleep_for(std::chrono::minutes(20))); + + r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); + LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r); + freeUPNPDevlist(devlist); devlist = nullptr; + FreeUPNPUrls(&urls); } else { LogPrintf("No valid UPnP IGDs found\n"); freeUPNPDevlist(devlist); devlist = nullptr; @@ -1546,27 +1542,39 @@ void ThreadMapPort() } } -void MapPort(bool fUseUPnP) +void StartMapPort() { - static std::unique_ptr<boost::thread> upnp_thread; + if (!g_upnp_thread.joinable()) { + assert(!g_upnp_interrupt); + g_upnp_thread = std::thread((std::bind(&TraceThread<void (*)()>, "upnp", &ThreadMapPort))); + } +} - if (fUseUPnP) - { - if (upnp_thread) { - upnp_thread->interrupt(); - upnp_thread->join(); - } - upnp_thread.reset(new boost::thread(boost::bind(&TraceThread<void (*)()>, "upnp", &ThreadMapPort))); +void InterruptMapPort() +{ + if(g_upnp_thread.joinable()) { + g_upnp_interrupt(); } - else if (upnp_thread) { - upnp_thread->interrupt(); - upnp_thread->join(); - upnp_thread.reset(); +} + +void StopMapPort() +{ + if(g_upnp_thread.joinable()) { + g_upnp_thread.join(); + g_upnp_interrupt.reset(); } } #else -void MapPort(bool) +void StartMapPort() +{ + // Intentionally left blank. +} +void InterruptMapPort() +{ + // Intentionally left blank. +} +void StopMapPort() { // Intentionally left blank. } @@ -1682,8 +1690,7 @@ void CConnman::ProcessOneShot() CAddress addr; CSemaphoreGrant grant(*semOutbound, true); if (grant) { - if (!OpenNetworkConnection(addr, false, &grant, strDest.c_str(), true)) - AddOneShot(strDest); + OpenNetworkConnection(addr, false, &grant, strDest.c_str(), true); } } @@ -1953,29 +1960,29 @@ void CConnman::ThreadOpenAddedConnections() } // if successful, this moves the passed grant to the constructed node -bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool manual_connection) +void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool manual_connection) { // // Initiate outbound network connection // if (interruptNet) { - return false; + return; } if (!fNetworkActive) { - return false; + return; } if (!pszDest) { if (IsLocal(addrConnect) || - FindNode((CNetAddr)addrConnect) || IsBanned(addrConnect) || + FindNode(static_cast<CNetAddr>(addrConnect)) || IsBanned(addrConnect) || FindNode(addrConnect.ToStringIPPort())) - return false; + return; } else if (FindNode(std::string(pszDest))) - return false; + return; CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure); if (!pnode) - return false; + return; if (grantOutbound) grantOutbound->MoveTo(pnode->grantOutbound); if (fOneShot) @@ -1990,8 +1997,6 @@ bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai LOCK(cs_vNodes); vNodes.push_back(pnode); } - - return true; } void CConnman::ThreadMessageHandler() @@ -2124,7 +2129,7 @@ bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, b return true; } -void Discover(boost::thread_group& threadGroup) +void Discover() { if (!fDiscover) return; @@ -37,10 +37,6 @@ class CScheduler; class CNode; -namespace boost { - class thread_group; -} // namespace boost - /** Time between pings automatically sent out for latency probing and keepalive (in seconds). */ static const int PING_INTERVAL = 2 * 60; /** Time after which to disconnect, after waiting for a ping response (or inactivity). */ @@ -177,7 +173,7 @@ public: void Interrupt(); bool GetNetworkActive() const { return fNetworkActive; }; void SetNetworkActive(bool active); - bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, bool fOneShot = false, bool fFeeler = false, bool manual_connection = false); + void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, bool fOneShot = false, bool fFeeler = false, bool manual_connection = false); bool CheckIncomingNonce(uint64_t nonce); bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func); @@ -441,8 +437,10 @@ private: friend struct CConnmanTest; }; extern std::unique_ptr<CConnman> g_connman; -void Discover(boost::thread_group& threadGroup); -void MapPort(bool fUseUPnP); +void Discover(); +void StartMapPort(); +void InterruptMapPort(); +void StopMapPort(); unsigned short GetListenPort(); bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 3cf96be61a..bf9307727a 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -750,7 +750,7 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) } // Requires cs_main. -void Misbehaving(NodeId pnode, int howmuch) +void Misbehaving(NodeId pnode, int howmuch, const std::string& message) { if (howmuch == 0) return; @@ -761,12 +761,13 @@ void Misbehaving(NodeId pnode, int howmuch) state->nMisbehavior += howmuch; int banscore = gArgs.GetArg("-banscore", DEFAULT_BANSCORE_THRESHOLD); + std::string message_prefixed = message.empty() ? "" : (": " + message); if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore) { - LogPrintf("%s: %s peer=%d (%d -> %d) BAN THRESHOLD EXCEEDED\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior); + LogPrint(BCLog::NET, "%s: %s peer=%d (%d -> %d) BAN THRESHOLD EXCEEDED%s\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior, message_prefixed); state->fShouldBan = true; } else - LogPrintf("%s: %s peer=%d (%d -> %d)\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior); + LogPrint(BCLog::NET, "%s: %s peer=%d (%d -> %d)%s\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior, message_prefixed); } @@ -1225,10 +1226,10 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } // release cs_main - if (it != pfrom->vRecvGetData.end()) { + if (it != pfrom->vRecvGetData.end() && !pfrom->fPauseSend) { const CInv &inv = *it; - it++; if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK) { + it++; ProcessGetBlockData(pfrom, consensusParams, inv, connman, interruptMsgProc); } } @@ -1260,8 +1261,7 @@ inline void static SendBlockTransactions(const CBlock& block, const BlockTransac for (size_t i = 0; i < req.indexes.size(); i++) { if (req.indexes[i] >= block.vtx.size()) { LOCK(cs_main); - Misbehaving(pfrom->GetId(), 100); - LogPrintf("Peer %d sent us a getblocktxn with out-of-bounds tx indices", pfrom->GetId()); + Misbehaving(pfrom->GetId(), 100, strprintf("Peer %d sent us a getblocktxn with out-of-bounds tx indices", pfrom->GetId())); return; } resp.txn[i] = block.vtx[req.indexes[i]]; @@ -1318,8 +1318,8 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve uint256 hashLastBlock; for (const CBlockHeader& header : headers) { if (!hashLastBlock.IsNull() && header.hashPrevBlock != hashLastBlock) { - Misbehaving(pfrom->GetId(), 20); - return error("non-continuous headers sequence"); + Misbehaving(pfrom->GetId(), 20, "non-continuous headers sequence"); + return false; } hashLastBlock = header.GetHash(); } @@ -1338,7 +1338,9 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve if (state.IsInvalid(nDoS)) { LOCK(cs_main); if (nDoS > 0) { - Misbehaving(pfrom->GetId(), nDoS); + Misbehaving(pfrom->GetId(), nDoS, "invalid header received"); + } else { + LogPrint(BCLog::NET, "peer=%d: invalid header received\n", pfrom->GetId()); } if (punish_duplicate_invalid && mapBlockIndex.find(first_invalid_header.GetHash()) != mapBlockIndex.end()) { // Goal: don't allow outbound peers to use up our outbound @@ -1374,7 +1376,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve // etc), and not just the duplicate-invalid case. pfrom->fDisconnect = true; } - return error("invalid header received"); + return false; } } @@ -1783,8 +1785,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (vAddr.size() > 1000) { LOCK(cs_main); - Misbehaving(pfrom->GetId(), 20); - return error("message addr size() = %u", vAddr.size()); + Misbehaving(pfrom->GetId(), 20, strprintf("message addr size() = %u", vAddr.size())); + return false; } // Store the new addresses @@ -1859,8 +1861,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (vInv.size() > MAX_INV_SZ) { LOCK(cs_main); - Misbehaving(pfrom->GetId(), 20); - return error("message inv size() = %u", vInv.size()); + Misbehaving(pfrom->GetId(), 20, strprintf("message inv size() = %u", vInv.size())); + return false; } bool fBlocksOnly = !fRelayTxes; @@ -1920,8 +1922,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (vInv.size() > MAX_INV_SZ) { LOCK(cs_main); - Misbehaving(pfrom->GetId(), 20); - return error("message getdata size() = %u", vInv.size()); + Misbehaving(pfrom->GetId(), 20, strprintf("message getdata size() = %u", vInv.size())); + return false; } LogPrint(BCLog::NET, "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom->GetId()); @@ -2323,9 +2325,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr int nDoS; if (state.IsInvalid(nDoS)) { if (nDoS > 0) { - LogPrintf("Peer %d sent us invalid header via cmpctblock\n", pfrom->GetId()); LOCK(cs_main); - Misbehaving(pfrom->GetId(), nDoS); + Misbehaving(pfrom->GetId(), nDoS, strprintf("Peer %d sent us invalid header via cmpctblock\n", pfrom->GetId())); } else { LogPrint(BCLog::NET, "Peer %d sent us invalid header via cmpctblock\n", pfrom->GetId()); } @@ -2411,8 +2412,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr ReadStatus status = partialBlock.InitData(cmpctblock, vExtraTxnForCompact); if (status == READ_STATUS_INVALID) { MarkBlockAsReceived(pindex->GetBlockHash()); // Reset in-flight state in case of whitelist - Misbehaving(pfrom->GetId(), 100); - LogPrintf("Peer %d sent us invalid compact block\n", pfrom->GetId()); + Misbehaving(pfrom->GetId(), 100, strprintf("Peer %d sent us invalid compact block\n", pfrom->GetId())); return true; } else if (status == READ_STATUS_FAILED) { // Duplicate txindexes, the block is now in-flight, so just request it @@ -2539,8 +2539,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr ReadStatus status = partialBlock.FillBlock(*pblock, resp.txn); if (status == READ_STATUS_INVALID) { MarkBlockAsReceived(resp.blockhash); // Reset in-flight state in case of whitelist - Misbehaving(pfrom->GetId(), 100); - LogPrintf("Peer %d sent us invalid compact block/non-matching block transactions\n", pfrom->GetId()); + Misbehaving(pfrom->GetId(), 100, strprintf("Peer %d sent us invalid compact block/non-matching block transactions\n", pfrom->GetId())); return true; } else if (status == READ_STATUS_FAILED) { // Might have collided, fall back to getdata now :( @@ -2602,8 +2601,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr unsigned int nCount = ReadCompactSize(vRecv); if (nCount > MAX_HEADERS_RESULTS) { LOCK(cs_main); - Misbehaving(pfrom->GetId(), 20); - return error("headers message size = %u", nCount); + Misbehaving(pfrom->GetId(), 20, strprintf("headers message size = %u", nCount)); + return false; } headers.resize(nCount); for (unsigned int n = 0; n < nCount; n++) { @@ -2864,7 +2863,7 @@ static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman) CNodeState &state = *State(pnode->GetId()); for (const CBlockReject& reject : state.rejects) { - connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, (std::string)NetMsgType::BLOCK, reject.chRejectCode, reject.strRejectReason, reject.hashBlock)); + connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, std::string(NetMsgType::BLOCK), reject.chRejectCode, reject.strRejectReason, reject.hashBlock)); } state.rejects.clear(); diff --git a/src/net_processing.h b/src/net_processing.h index 4f2b8935f4..b534ef01c3 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -79,6 +79,6 @@ struct CNodeStateStats { /** Get statistics from node state */ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats); /** Increase a node's misbehavior score. */ -void Misbehaving(NodeId nodeid, int howmuch); +void Misbehaving(NodeId nodeid, int howmuch, const std::string& message=""); #endif // BITCOIN_NET_PROCESSING_H diff --git a/src/netaddress.cpp b/src/netaddress.cpp index 81f72879f4..4f231d73c8 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -522,17 +522,17 @@ unsigned short CService::GetPort() const bool operator==(const CService& a, const CService& b) { - return (CNetAddr)a == (CNetAddr)b && a.port == b.port; + return static_cast<CNetAddr>(a) == static_cast<CNetAddr>(b) && a.port == b.port; } bool operator!=(const CService& a, const CService& b) { - return (CNetAddr)a != (CNetAddr)b || a.port != b.port; + return static_cast<CNetAddr>(a) != static_cast<CNetAddr>(b) || a.port != b.port; } bool operator<(const CService& a, const CService& b) { - return (CNetAddr)a < (CNetAddr)b || ((CNetAddr)a == (CNetAddr)b && a.port < b.port); + return static_cast<CNetAddr>(a) < static_cast<CNetAddr>(b) || (static_cast<CNetAddr>(a) == static_cast<CNetAddr>(b) && a.port < b.port); } bool CService::GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const diff --git a/src/netbase.cpp b/src/netbase.cpp index 276b2f4dc2..5be3fe34f8 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -572,7 +572,7 @@ bool HaveNameProxy() { bool IsProxy(const CNetAddr &addr) { LOCK(cs_proxyInfos); for (int i = 0; i < NET_MAX; i++) { - if (addr == (CNetAddr)proxyInfo[i].proxy) + if (addr == static_cast<CNetAddr>(proxyInfo[i].proxy)) return true; } return false; @@ -682,6 +682,9 @@ bool CloseSocket(SOCKET& hSocket) #else int ret = close(hSocket); #endif + if (ret) { + LogPrintf("Socket close failed: %d. Error: %s\n", hSocket, NetworkErrorString(WSAGetLastError())); + } hSocket = INVALID_SOCKET; return ret != SOCKET_ERROR; } diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index 9142f3706d..79b450e3e6 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -981,16 +981,17 @@ bool CBlockPolicyEstimator::Read(CAutoFile& filein) return true; } -void CBlockPolicyEstimator::FlushUnconfirmed(CTxMemPool& pool) { +void CBlockPolicyEstimator::FlushUnconfirmed() { int64_t startclear = GetTimeMicros(); - std::vector<uint256> txids; - pool.queryHashes(txids); LOCK(cs_feeEstimator); - for (auto& txid : txids) { - removeTx(txid, false); + size_t num_entries = mapMemPoolTxs.size(); + // Remove every entry in mapMemPoolTxs + while (!mapMemPoolTxs.empty()) { + auto mi = mapMemPoolTxs.begin(); + removeTx(mi->first, false); // this calls erase() on mapMemPoolTxs } int64_t endclear = GetTimeMicros(); - LogPrint(BCLog::ESTIMATEFEE, "Recorded %u unconfirmed txs from mempool in %gs\n",txids.size(), (endclear - startclear)*0.000001); + LogPrint(BCLog::ESTIMATEFEE, "Recorded %u unconfirmed txs from mempool in %gs\n", num_entries, (endclear - startclear)*0.000001); } FeeFilterRounder::FeeFilterRounder(const CFeeRate& minIncrementalFee) diff --git a/src/policy/fees.h b/src/policy/fees.h index 96a842b7a1..5f69e989c1 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -223,7 +223,7 @@ public: bool Read(CAutoFile& filein); /** Empty mempool transactions on shutdown to record failure to confirm for txs still in mempool */ - void FlushUnconfirmed(CTxMemPool& pool); + void FlushUnconfirmed(); /** Calculation of highest target that estimates are tracked for */ unsigned int HighestTargetTracked(FeeEstimateHorizon horizon) const; diff --git a/src/primitives/block.h b/src/primitives/block.h index 612a9fa203..5d6d44ac76 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -86,14 +86,14 @@ public: CBlock(const CBlockHeader &header) { SetNull(); - *((CBlockHeader*)this) = header; + *(static_cast<CBlockHeader*>(this)) = header; } ADD_SERIALIZE_METHODS; template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(*(CBlockHeader*)this); + READWRITE(*static_cast<CBlockHeader*>(this)); READWRITE(vtx); } diff --git a/src/protocol.h b/src/protocol.h index 43d8ac067a..42eb57e4f0 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -294,7 +294,7 @@ enum ServiceFlags : uint64_t { * Thus, generally, avoid calling with peerServices == NODE_NONE, unless * state-specific flags must absolutely be avoided. When called with * peerServices == NODE_NONE, the returned desirable service flags are - * guaranteed to not change dependant on state - ie they are suitable for + * guaranteed to not change dependent on state - ie they are suitable for * use when describing peers which we know to be desirable, but for which * we do not have a confirmed set of service flags. * @@ -346,8 +346,8 @@ public: READWRITE(nTime); uint64_t nServicesInt = nServices; READWRITE(nServicesInt); - nServices = (ServiceFlags)nServicesInt; - READWRITE(*(CService*)this); + nServices = static_cast<ServiceFlags>(nServicesInt); + READWRITE(*static_cast<CService*>(this)); } // TODO: make private (improves encapsulation) diff --git a/src/qt/README.md b/src/qt/README.md new file mode 100644 index 0000000000..7ffea98170 --- /dev/null +++ b/src/qt/README.md @@ -0,0 +1,95 @@ +This directory contains the BitcoinQT graphical user interface (GUI). It uses the cross platform framework [QT](https://www1.qt.io/developers/). + +The current precise version for QT 5 is specified in [qt.mk](/depends/packages/qt.mk). QT 4 is also supported (see [#8263](https://github.com/bitcoin/bitcoin/issues/8263)). + +## Compile and run + +See build instructions ([OSX](/doc/build-osx.md), [Windows](/doc/build-windows.md), [Unix](/doc/build-unix.md), etc). + +To run: + +```sh +./src/qt/bitcoin-qt +``` + +## Files and directories + +### forms + +Contains [Designer UI](http://doc.qt.io/qt-5.9/designer-using-a-ui-file.html) files. They are created with [Qt Creator](#use-qt-Creator-as IDE), but can be edited using any text editor. + +### locale + +Contains translations. They are periodically updated. The process is described [here](/doc/translation_process.md). + +### res + +Resources such as the icon. + +### test + +Tests. + +### bitcoingui.(h/cpp) + +Represents the main window of the Bitcoin UI. + +### \*model.(h/cpp) + +The model. When it has a corresponding controller, it generally inherits from [QAbstractTableModel](http://doc.qt.io/qt-5/qabstracttablemodel.html). Models that are used by controllers as helpers inherit from other QT classes like [QValidator](http://doc.qt.io/qt-5/qvalidator.html). + +ClientModel is used by the main application `bitcoingui` and several models like `peertablemodel`. + +### \*page.(h/cpp) + +A controller. `:NAMEpage.cpp` generally includes `:NAMEmodel.h` and `forms/:NAME.page.ui` with a similar `:NAME`. + +### \*dialog.(h/cpp) + +Various dialogs, e.g. to open a URL. Inherit from [QDialog](http://doc.qt.io/qt-4.8/qdialog.html). + +### paymentserver.(h/cpp) + +Used to process BIP21 and BIP70 (see https://github.com/bitcoin/bitcoin/pull/11622) payment URI / requests. Also handles URI based application switching (e.g. when following a bitcoin:... link from a browser). + +### walletview.(h/cpp) + +Represents the view to a single wallet. + +### Other .h/cpp files + +* UI elements like BitcoinAmountField, which inherit from QWidget. +* `bitcoinstrings.cpp`: automatically generated +* `bitcoinunits.(h/cpp)`: BTC / mBTC / etc handling +* `callback.h` +* `guiconstants.h`: UI colors, app name, etc +* `guiutil.h`: several helper functions +* `macdockiconhandler.(h/cpp)` +* `macdockiconhandler.(h/cpp)`: display notifications in OSX + +## Contribute + +See [CONTRIBUTING.md](/CONTRIBUTING.md) for general guidelines. Specifically for QT: + +* don't change `local/bitcoin_en.ts`; this happens [automatically](/doc/translation_process.md#writing-code-with-translations) + +## Using Qt Creator as IDE + +You can use Qt Creator as an IDE. This is especially useful if you want to change +the UI layout. + +Download and install the community edition of [Qt Creator](https://www.qt.io/download/). +Uncheck everything except Qt Creator during the installation process. + +Instructions for OSX: + +1. Make sure you installed everything through Homebrew mentioned in the [OSX build instructions](/docs/build-osx.md) +2. Use `./configure` with the `--enable-debug` flag +3. In Qt Creator do "New Project" -> Import Project -> Import Existing Project +4. Enter "bitcoin-qt" as project name, enter src/qt as location +5. Leave the file selection as it is +6. Confirm the "summary page" +7. In the "Projects" tab select "Manage Kits..." +8. Select the default "Desktop" kit and select "Clang (x86 64bit in /usr/bin)" as compiler +9. Select LLDB as debugger (you might need to set the path to your installation) +10. Start debugging with Qt Creator (you might need to the executable to "bitcoin-qt" under "Run", which is where you can also add command line arguments) diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 74f0db3520..ffb5bff4de 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -393,11 +393,8 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con } // Add entry - { - LOCK(wallet->cs_wallet); - wallet->SetAddressBook(DecodeDestination(strAddress), strLabel, - (type == Send ? "send" : "receive")); - } + wallet->SetAddressBook(DecodeDestination(strAddress), strLabel, + (type == Send ? "send" : "receive")); return QString::fromStdString(strAddress); } @@ -411,10 +408,7 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent // Also refuse to remove receiving addresses. return false; } - { - LOCK(wallet->cs_wallet); - wallet->DelAddressBook(DecodeDestination(rec->address.toStdString())); - } + wallet->DelAddressBook(DecodeDestination(rec->address.toStdString())); return true; } diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index b26d99a20a..06e1f1a37c 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -388,7 +388,6 @@ void BitcoinApplication::createWindow(const NetworkStyle *networkStyle) pollShutdownTimer = new QTimer(window); connect(pollShutdownTimer, SIGNAL(timeout()), window, SLOT(detectShutdown())); - pollShutdownTimer->start(200); } void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle) @@ -515,14 +514,16 @@ void BitcoinApplication::initializeResult(bool success) window, SLOT(message(QString,QString,unsigned int))); QTimer::singleShot(100, paymentServer, SLOT(uiReady())); #endif + pollShutdownTimer->start(200); } else { - quit(); // Exit main loop + Q_EMIT splashFinished(window); // Make sure splash screen doesn't stick around during shutdown + quit(); // Exit first main loop invocation } } void BitcoinApplication::shutdownResult() { - quit(); // Exit main loop after shutdown finished + quit(); // Exit second main loop invocation after shutdown finished } void BitcoinApplication::handleRunawayException(const QString &message) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index afd90a3bc6..4e868b7c17 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -599,7 +599,7 @@ void BitcoinGUI::createTrayIconMenu() #else // Note: On Mac, the dock icon is used to provide the tray's functionality. MacDockIconHandler *dockIconHandler = MacDockIconHandler::instance(); - dockIconHandler->setMainWindow((QMainWindow *)this); + dockIconHandler->setMainWindow(static_cast<QMainWindow*>(this)); trayIconMenu = dockIconHandler->dockMenu(); #endif @@ -922,13 +922,13 @@ void BitcoinGUI::message(const QString &title, const QString &message, unsigned buttons = QMessageBox::Ok; showNormalIfMinimized(); - QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, buttons, this); + QMessageBox mBox(static_cast<QMessageBox::Icon>(nMBoxIcon), strTitle, message, buttons, this); int r = mBox.exec(); if (ret != nullptr) *ret = r == QMessageBox::Ok; } else - notificator->notify((Notificator::Class)nNotifyIcon, strTitle, message); + notificator->notify(static_cast<Notificator::Class>(nNotifyIcon), strTitle, message); } void BitcoinGUI::changeEvent(QEvent *e) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index ea4d6a4816..8d2e5619e0 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -31,7 +31,6 @@ #include <QTreeWidget> QList<CAmount> CoinControlDialog::payAmounts; -CCoinControl* CoinControlDialog::coinControl = new CCoinControl(); bool CoinControlDialog::fSubtractFeeFromAmount = false; bool CCoinControlWidgetItem::operator<(const QTreeWidgetItem &other) const { @@ -143,7 +142,7 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *_platformStyle, QWidge if (settings.contains("nCoinControlMode") && !settings.value("nCoinControlMode").toBool()) ui->radioTreeMode->click(); if (settings.contains("nCoinControlSortColumn") && settings.contains("nCoinControlSortOrder")) - sortView(settings.value("nCoinControlSortColumn").toInt(), ((Qt::SortOrder)settings.value("nCoinControlSortOrder").toInt())); + sortView(settings.value("nCoinControlSortColumn").toInt(), (static_cast<Qt::SortOrder>(settings.value("nCoinControlSortOrder").toInt()))); } CoinControlDialog::~CoinControlDialog() @@ -193,7 +192,7 @@ void CoinControlDialog::buttonSelectAllClicked() ui->treeWidget->topLevelItem(i)->setCheckState(COLUMN_CHECKBOX, state); ui->treeWidget->setEnabled(true); if (state == Qt::Unchecked) - coinControl->UnSelectAll(); // just to be sure + coinControl()->UnSelectAll(); // just to be sure CoinControlDialog::updateLabels(model, this); } @@ -379,11 +378,11 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column) COutPoint outpt(uint256S(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt()); if (item->checkState(COLUMN_CHECKBOX) == Qt::Unchecked) - coinControl->UnSelect(outpt); + coinControl()->UnSelect(outpt); else if (item->isDisabled()) // locked (this happens if "check all" through parent node) item->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); else - coinControl->Select(outpt); + coinControl()->Select(outpt); // selection changed -> update labels if (ui->treeWidget->isEnabled()) // do not update on every click for (un)select all @@ -429,7 +428,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) if (amount > 0) { - CTxOut txout(amount, (CScript)std::vector<unsigned char>(24, 0)); + CTxOut txout(amount, static_cast<CScript>(std::vector<unsigned char>(24, 0))); txDummy.vout.push_back(txout); fDust |= IsDust(txout, ::dustRelayFee); } @@ -446,7 +445,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) std::vector<COutPoint> vCoinControl; std::vector<COutput> vOutputs; - coinControl->ListSelected(vCoinControl); + coinControl()->ListSelected(vCoinControl); model->getOutputs(vCoinControl, vOutputs); for (const COutput& out : vOutputs) { @@ -456,7 +455,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) COutPoint outpt(txhash, out.i); if (model->isSpent(outpt)) { - coinControl->UnSelect(outpt); + coinControl()->UnSelect(outpt); continue; } @@ -509,7 +508,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) nBytes -= 34; // Fee - nPayFee = GetMinimumFee(nBytes, *coinControl, ::mempool, ::feeEstimator, nullptr /* FeeCalculation */); + nPayFee = GetMinimumFee(nBytes, *coinControl(), ::mempool, ::feeEstimator, nullptr /* FeeCalculation */); if (nPayAmount > 0) { @@ -520,7 +519,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) // Never create dust outputs; if we would, just add the dust to the fee. if (nChange > 0 && nChange < MIN_CHANGE) { - CTxOut txout(nChange, (CScript)std::vector<unsigned char>(24, 0)); + CTxOut txout(nChange, static_cast<CScript>(std::vector<unsigned char>(24, 0))); if (IsDust(txout, ::dustRelayFee)) { nPayFee += nChange; @@ -600,6 +599,12 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) label->setVisible(nChange < 0); } +CCoinControl* CoinControlDialog::coinControl() +{ + static CCoinControl coin_control; + return &coin_control; +} + void CoinControlDialog::updateView() { if (!model || !model->getOptionsModel() || !model->getAddressTableModel()) @@ -703,13 +708,13 @@ void CoinControlDialog::updateView() if (model->isLockedCoin(txhash, out.i)) { COutPoint outpt(txhash, out.i); - coinControl->UnSelect(outpt); // just to be sure + coinControl()->UnSelect(outpt); // just to be sure itemOutput->setDisabled(true); itemOutput->setIcon(COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed")); } // set checkbox - if (coinControl->IsSelected(COutPoint(txhash, out.i))) + if (coinControl()->IsSelected(COutPoint(txhash, out.i))) itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked); } diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h index 355ed5f553..a254c55556 100644 --- a/src/qt/coincontroldialog.h +++ b/src/qt/coincontroldialog.h @@ -52,7 +52,7 @@ public: static void updateLabels(WalletModel*, QDialog*); static QList<CAmount> payAmounts; - static CCoinControl *coinControl; + static CCoinControl *coinControl(); static bool fSubtractFeeFromAmount; private: diff --git a/src/qt/coincontroltreewidget.cpp b/src/qt/coincontroltreewidget.cpp index 82db28d586..e7326d3f7a 100644 --- a/src/qt/coincontroltreewidget.cpp +++ b/src/qt/coincontroltreewidget.cpp @@ -24,7 +24,7 @@ void CoinControlTreeWidget::keyPressEvent(QKeyEvent *event) else if (event->key() == Qt::Key_Escape) // press esc -> close dialog { event->ignore(); - CoinControlDialog *coinControlDialog = (CoinControlDialog*)this->parentWidget(); + CoinControlDialog *coinControlDialog = static_cast<CoinControlDialog*>(this->parentWidget()); coinControlDialog->done(QDialog::Accepted); } else diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 558d4f108c..edf1c29ea1 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -101,7 +101,7 @@ QFont fixedPitchFont() #endif } -// Just some dummy data to generate an convincing random-looking (but consistent) address +// Just some dummy data to generate a convincing random-looking (but consistent) address static const uint8_t dummydata[] = {0xeb,0x15,0x23,0x1d,0xfc,0xeb,0x60,0x92,0x58,0x86,0xb6,0x7d,0x06,0x52,0x99,0x92,0x59,0x15,0xae,0xb1,0x72,0xc0,0x66,0x47}; // Generate a dummy address with invalid CRC, starting with the network prefix. diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 4ade88d843..909be1c264 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -315,7 +315,12 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in break; case MapPortUPnP: // core option - can be changed on-the-fly settings.setValue("fUseUPnP", value.toBool()); - MapPort(value.toBool()); + if (value.toBool()) { + StartMapPort(); + } else { + InterruptMapPort(); + StopMapPort(); + } break; case MinimizeOnClose: fMinimizeOnClose = value.toBool(); diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 9fd61db70e..871822ccb4 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -256,7 +256,7 @@ void SendCoinsDialog::on_sendButton_clicked() // Always use a CCoinControl instance, use the CoinControlDialog instance if CoinControl has been enabled CCoinControl ctrl; if (model->getOptionsModel()->getCoinControlFeatures()) - ctrl = *CoinControlDialog::coinControl; + ctrl = *CoinControlDialog::coinControl(); updateCoinControlState(ctrl); @@ -351,7 +351,7 @@ void SendCoinsDialog::on_sendButton_clicked() SendConfirmationDialog confirmationDialog(tr("Confirm send coins"), questionString.arg(formatted.join("<br />")), SEND_CONFIRM_DELAY, this); confirmationDialog.exec(); - QMessageBox::StandardButton retval = (QMessageBox::StandardButton)confirmationDialog.result(); + QMessageBox::StandardButton retval = static_cast<QMessageBox::StandardButton>(confirmationDialog.result()); if(retval != QMessageBox::Yes) { @@ -367,7 +367,7 @@ void SendCoinsDialog::on_sendButton_clicked() if (sendStatus.status == WalletModel::OK) { accept(); - CoinControlDialog::coinControl->UnSelectAll(); + CoinControlDialog::coinControl()->UnSelectAll(); coinControlUpdateLabels(); } fNewRecipientAllowed = true; @@ -604,7 +604,7 @@ void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry) // Get CCoinControl instance if CoinControl is enabled or create a new one. CCoinControl coin_control; if (model->getOptionsModel()->getCoinControlFeatures()) { - coin_control = *CoinControlDialog::coinControl; + coin_control = *CoinControlDialog::coinControl(); } // Calculate available amount to send. @@ -754,7 +754,7 @@ void SendCoinsDialog::coinControlFeatureChanged(bool checked) ui->frameCoinControl->setVisible(checked); if (!checked && model) // coin control features disabled - CoinControlDialog::coinControl->SetNull(); + CoinControlDialog::coinControl()->SetNull(); coinControlUpdateLabels(); } @@ -773,7 +773,7 @@ void SendCoinsDialog::coinControlChangeChecked(int state) { if (state == Qt::Unchecked) { - CoinControlDialog::coinControl->destChange = CNoDestination(); + CoinControlDialog::coinControl()->destChange = CNoDestination(); ui->labelCoinControlChangeLabel->clear(); } else @@ -789,7 +789,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) if (model && model->getAddressTableModel()) { // Default to no change address until verified - CoinControlDialog::coinControl->destChange = CNoDestination(); + CoinControlDialog::coinControl()->destChange = CNoDestination(); ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}"); const CTxDestination dest = DecodeDestination(text.toStdString()); @@ -812,7 +812,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); if(btnRetVal == QMessageBox::Yes) - CoinControlDialog::coinControl->destChange = dest; + CoinControlDialog::coinControl()->destChange = dest; else { ui->lineEditCoinControlChange->setText(""); @@ -831,7 +831,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) else ui->labelCoinControlChangeLabel->setText(tr("(no label)")); - CoinControlDialog::coinControl->destChange = dest; + CoinControlDialog::coinControl()->destChange = dest; } } } @@ -843,7 +843,7 @@ void SendCoinsDialog::coinControlUpdateLabels() if (!model || !model->getOptionsModel()) return; - updateCoinControlState(*CoinControlDialog::coinControl); + updateCoinControlState(*CoinControlDialog::coinControl()); // set pay amounts CoinControlDialog::payAmounts.clear(); @@ -861,7 +861,7 @@ void SendCoinsDialog::coinControlUpdateLabels() } } - if (CoinControlDialog::coinControl->HasSelected()) + if (CoinControlDialog::coinControl()->HasSelected()) { // actual coin control calculation CoinControlDialog::updateLabels(model, this); diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index fa3bd18d5a..5df1282f73 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -38,7 +38,7 @@ SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle) float fontFactor = 1.0; float devicePixelRatio = 1.0; #if QT_VERSION > 0x050100 - devicePixelRatio = ((QGuiApplication*)QCoreApplication::instance())->devicePixelRatio(); + devicePixelRatio = static_cast<QGuiApplication*>(QCoreApplication::instance())->devicePixelRatio(); #endif // define text to place diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index fa43ab750a..88f8f463bc 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -322,7 +322,7 @@ void TransactionView::chooseWatchonly(int idx) if(!transactionProxyModel) return; transactionProxyModel->setWatchOnlyFilter( - (TransactionFilterProxy::WatchOnlyFilter)watchOnlyWidget->itemData(idx).toInt()); + static_cast<TransactionFilterProxy::WatchOnlyFilter>(watchOnlyWidget->itemData(idx).toInt())); } void TransactionView::changedSearch() diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index 5d4a92f760..65e7775f2b 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -86,9 +86,9 @@ HelpMessageDialog::HelpMessageDialog(QWidget *parent, bool about) : strUsage += HelpMessageOpt("-choosedatadir", strprintf(tr("Choose data directory on startup (default: %u)").toStdString(), DEFAULT_CHOOSE_DATADIR)); strUsage += HelpMessageOpt("-lang=<lang>", tr("Set language, for example \"de_DE\" (default: system locale)").toStdString()); strUsage += HelpMessageOpt("-min", tr("Start minimized").toStdString()); + strUsage += HelpMessageOpt("-resetguisettings", tr("Reset all settings changed in the GUI").toStdString()); strUsage += HelpMessageOpt("-rootcertificates=<file>", tr("Set SSL root certificates for payment request (default: -system-)").toStdString()); strUsage += HelpMessageOpt("-splash", strprintf(tr("Show splash screen on startup (default: %u)").toStdString(), DEFAULT_SPLASHSCREEN)); - strUsage += HelpMessageOpt("-resetguisettings", tr("Reset all settings changed in the GUI").toStdString()); if (showDebug) { strUsage += HelpMessageOpt("-uiplatform", strprintf("Select platform to customize UI for (one of windows, macosx, other; default: %s)", BitcoinGUI::DEFAULT_UIPLATFORM)); } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 4d7e977fcb..34954a6bfa 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -42,6 +42,7 @@ WalletModel::WalletModel(const PlatformStyle *platformStyle, CWallet *_wallet, O transactionTableModel(0), recentRequestsTableModel(0), cachedBalance(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0), + cachedWatchOnlyBalance{0}, cachedWatchUnconfBalance{0}, cachedWatchImmatureBalance{0}, cachedEncryptionStatus(Unencrypted), cachedNumBlocks(0) { @@ -695,7 +696,7 @@ bool WalletModel::bumpFee(uint256 hash) questionString.append("</td></tr></table>"); SendConfirmationDialog confirmationDialog(tr("Confirm fee bump"), questionString); confirmationDialog.exec(); - QMessageBox::StandardButton retval = (QMessageBox::StandardButton)confirmationDialog.result(); + QMessageBox::StandardButton retval = static_cast<QMessageBox::StandardButton>(confirmationDialog.result()); // cancel sign&broadcast if users doesn't want to bump the fee if (retval != QMessageBox::Yes) { diff --git a/src/rest.cpp b/src/rest.cpp index 30e481171f..eeeb3f5141 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -540,23 +540,23 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) // pack in some essentials // use more or less the same output as mentioned in Bip64 - objGetUTXOResponse.push_back(Pair("chainHeight", chainActive.Height())); - objGetUTXOResponse.push_back(Pair("chaintipHash", chainActive.Tip()->GetBlockHash().GetHex())); - objGetUTXOResponse.push_back(Pair("bitmap", bitmapStringRepresentation)); + objGetUTXOResponse.pushKV("chainHeight", chainActive.Height()); + objGetUTXOResponse.pushKV("chaintipHash", chainActive.Tip()->GetBlockHash().GetHex()); + objGetUTXOResponse.pushKV("bitmap", bitmapStringRepresentation); UniValue utxos(UniValue::VARR); for (const CCoin& coin : outs) { UniValue utxo(UniValue::VOBJ); - utxo.push_back(Pair("height", (int32_t)coin.nHeight)); - utxo.push_back(Pair("value", ValueFromAmount(coin.out.nValue))); + utxo.pushKV("height", (int32_t)coin.nHeight); + utxo.pushKV("value", ValueFromAmount(coin.out.nValue)); // include the script in a json output UniValue o(UniValue::VOBJ); ScriptPubKeyToUniv(coin.out.scriptPubKey, o, true); - utxo.push_back(Pair("scriptPubKey", o)); + utxo.pushKV("scriptPubKey", o); utxos.push_back(utxo); } - objGetUTXOResponse.push_back(Pair("utxos", utxos)); + objGetUTXOResponse.pushKV("utxos", utxos); // return json string std::string strJSON = objGetUTXOResponse.write() + "\n"; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 346672e45a..f1352a13cf 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -88,28 +88,28 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex) { AssertLockHeld(cs_main); UniValue result(UniValue::VOBJ); - result.push_back(Pair("hash", blockindex->GetBlockHash().GetHex())); + result.pushKV("hash", blockindex->GetBlockHash().GetHex()); int confirmations = -1; // Only report confirmations if the block is on the main chain if (chainActive.Contains(blockindex)) confirmations = chainActive.Height() - blockindex->nHeight + 1; - result.push_back(Pair("confirmations", confirmations)); - result.push_back(Pair("height", blockindex->nHeight)); - result.push_back(Pair("version", blockindex->nVersion)); - result.push_back(Pair("versionHex", strprintf("%08x", blockindex->nVersion))); - result.push_back(Pair("merkleroot", blockindex->hashMerkleRoot.GetHex())); - result.push_back(Pair("time", (int64_t)blockindex->nTime)); - result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast())); - result.push_back(Pair("nonce", (uint64_t)blockindex->nNonce)); - result.push_back(Pair("bits", strprintf("%08x", blockindex->nBits))); - result.push_back(Pair("difficulty", GetDifficulty(blockindex))); - result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); + result.pushKV("confirmations", confirmations); + result.pushKV("height", blockindex->nHeight); + result.pushKV("version", blockindex->nVersion); + result.pushKV("versionHex", strprintf("%08x", blockindex->nVersion)); + result.pushKV("merkleroot", blockindex->hashMerkleRoot.GetHex()); + result.pushKV("time", (int64_t)blockindex->nTime); + result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast()); + result.pushKV("nonce", (uint64_t)blockindex->nNonce); + result.pushKV("bits", strprintf("%08x", blockindex->nBits)); + result.pushKV("difficulty", GetDifficulty(blockindex)); + result.pushKV("chainwork", blockindex->nChainWork.GetHex()); if (blockindex->pprev) - result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); + result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()); CBlockIndex *pnext = chainActive.Next(blockindex); if (pnext) - result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); + result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex()); return result; } @@ -117,19 +117,19 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx { AssertLockHeld(cs_main); UniValue result(UniValue::VOBJ); - result.push_back(Pair("hash", blockindex->GetBlockHash().GetHex())); + result.pushKV("hash", blockindex->GetBlockHash().GetHex()); int confirmations = -1; // Only report confirmations if the block is on the main chain if (chainActive.Contains(blockindex)) confirmations = chainActive.Height() - blockindex->nHeight + 1; - result.push_back(Pair("confirmations", confirmations)); - result.push_back(Pair("strippedsize", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS))); - result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); - result.push_back(Pair("weight", (int)::GetBlockWeight(block))); - result.push_back(Pair("height", blockindex->nHeight)); - result.push_back(Pair("version", block.nVersion)); - result.push_back(Pair("versionHex", strprintf("%08x", block.nVersion))); - result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); + result.pushKV("confirmations", confirmations); + result.pushKV("strippedsize", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS)); + result.pushKV("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION)); + result.pushKV("weight", (int)::GetBlockWeight(block)); + result.pushKV("height", blockindex->nHeight); + result.pushKV("version", block.nVersion); + result.pushKV("versionHex", strprintf("%08x", block.nVersion)); + result.pushKV("merkleroot", block.hashMerkleRoot.GetHex()); UniValue txs(UniValue::VARR); for(const auto& tx : block.vtx) { @@ -142,19 +142,19 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx else txs.push_back(tx->GetHash().GetHex()); } - result.push_back(Pair("tx", txs)); - result.push_back(Pair("time", block.GetBlockTime())); - result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast())); - result.push_back(Pair("nonce", (uint64_t)block.nNonce)); - result.push_back(Pair("bits", strprintf("%08x", block.nBits))); - result.push_back(Pair("difficulty", GetDifficulty(blockindex))); - result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); + result.pushKV("tx", txs); + result.pushKV("time", block.GetBlockTime()); + result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast()); + result.pushKV("nonce", (uint64_t)block.nNonce); + result.pushKV("bits", strprintf("%08x", block.nBits)); + result.pushKV("difficulty", GetDifficulty(blockindex)); + result.pushKV("chainwork", blockindex->nChainWork.GetHex()); if (blockindex->pprev) - result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); + result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()); CBlockIndex *pnext = chainActive.Next(blockindex); if (pnext) - result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex())); + result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex()); return result; } @@ -235,8 +235,8 @@ UniValue waitfornewblock(const JSONRPCRequest& request) block = latestblock; } UniValue ret(UniValue::VOBJ); - ret.push_back(Pair("hash", block.hash.GetHex())); - ret.push_back(Pair("height", block.height)); + ret.pushKV("hash", block.hash.GetHex()); + ret.pushKV("height", block.height); return ret; } @@ -277,8 +277,8 @@ UniValue waitforblock(const JSONRPCRequest& request) } UniValue ret(UniValue::VOBJ); - ret.push_back(Pair("hash", block.hash.GetHex())); - ret.push_back(Pair("height", block.height)); + ret.pushKV("hash", block.hash.GetHex()); + ret.pushKV("height", block.height); return ret; } @@ -319,8 +319,8 @@ UniValue waitforblockheight(const JSONRPCRequest& request) block = latestblock; } UniValue ret(UniValue::VOBJ); - ret.push_back(Pair("hash", block.hash.GetHex())); - ret.push_back(Pair("height", block.height)); + ret.pushKV("hash", block.hash.GetHex()); + ret.pushKV("height", block.height); return ret; } @@ -379,18 +379,18 @@ void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) { AssertLockHeld(mempool.cs); - info.push_back(Pair("size", (int)e.GetTxSize())); - info.push_back(Pair("fee", ValueFromAmount(e.GetFee()))); - info.push_back(Pair("modifiedfee", ValueFromAmount(e.GetModifiedFee()))); - info.push_back(Pair("time", e.GetTime())); - info.push_back(Pair("height", (int)e.GetHeight())); - info.push_back(Pair("descendantcount", e.GetCountWithDescendants())); - info.push_back(Pair("descendantsize", e.GetSizeWithDescendants())); - info.push_back(Pair("descendantfees", e.GetModFeesWithDescendants())); - info.push_back(Pair("ancestorcount", e.GetCountWithAncestors())); - info.push_back(Pair("ancestorsize", e.GetSizeWithAncestors())); - info.push_back(Pair("ancestorfees", e.GetModFeesWithAncestors())); - info.push_back(Pair("wtxid", mempool.vTxHashes[e.vTxHashesIdx].first.ToString())); + info.pushKV("size", (int)e.GetTxSize()); + info.pushKV("fee", ValueFromAmount(e.GetFee())); + info.pushKV("modifiedfee", ValueFromAmount(e.GetModifiedFee())); + info.pushKV("time", e.GetTime()); + info.pushKV("height", (int)e.GetHeight()); + info.pushKV("descendantcount", e.GetCountWithDescendants()); + info.pushKV("descendantsize", e.GetSizeWithDescendants()); + info.pushKV("descendantfees", e.GetModFeesWithDescendants()); + info.pushKV("ancestorcount", e.GetCountWithAncestors()); + info.pushKV("ancestorsize", e.GetSizeWithAncestors()); + info.pushKV("ancestorfees", e.GetModFeesWithAncestors()); + info.pushKV("wtxid", mempool.vTxHashes[e.vTxHashesIdx].first.ToString()); const CTransaction& tx = e.GetTx(); std::set<std::string> setDepends; for (const CTxIn& txin : tx.vin) @@ -405,7 +405,7 @@ void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) depends.push_back(dep); } - info.push_back(Pair("depends", depends)); + info.pushKV("depends", depends); } UniValue mempoolToJSON(bool fVerbose) @@ -419,7 +419,7 @@ UniValue mempoolToJSON(bool fVerbose) const uint256& hash = e.GetTx().GetHash(); UniValue info(UniValue::VOBJ); entryToJSON(info, e); - o.push_back(Pair(hash.ToString(), info)); + o.pushKV(hash.ToString(), info); } return o; } @@ -526,7 +526,7 @@ UniValue getmempoolancestors(const JSONRPCRequest& request) const uint256& _hash = e.GetTx().GetHash(); UniValue info(UniValue::VOBJ); entryToJSON(info, e); - o.push_back(Pair(_hash.ToString(), info)); + o.pushKV(_hash.ToString(), info); } return o; } @@ -590,7 +590,7 @@ UniValue getmempooldescendants(const JSONRPCRequest& request) const uint256& _hash = e.GetTx().GetHash(); UniValue info(UniValue::VOBJ); entryToJSON(info, e); - o.push_back(Pair(_hash.ToString(), info)); + o.pushKV(_hash.ToString(), info); } return o; } @@ -952,14 +952,14 @@ UniValue gettxoutsetinfo(const JSONRPCRequest& request) CCoinsStats stats; FlushStateToDisk(); if (GetUTXOStats(pcoinsdbview.get(), stats)) { - ret.push_back(Pair("height", (int64_t)stats.nHeight)); - ret.push_back(Pair("bestblock", stats.hashBlock.GetHex())); - ret.push_back(Pair("transactions", (int64_t)stats.nTransactions)); - ret.push_back(Pair("txouts", (int64_t)stats.nTransactionOutputs)); - ret.push_back(Pair("bogosize", (int64_t)stats.nBogoSize)); - ret.push_back(Pair("hash_serialized_2", stats.hashSerialized.GetHex())); - ret.push_back(Pair("disk_size", stats.nDiskSize)); - ret.push_back(Pair("total_amount", ValueFromAmount(stats.nTotalAmount))); + ret.pushKV("height", (int64_t)stats.nHeight); + ret.pushKV("bestblock", stats.hashBlock.GetHex()); + ret.pushKV("transactions", (int64_t)stats.nTransactions); + ret.pushKV("txouts", (int64_t)stats.nTransactionOutputs); + ret.pushKV("bogosize", (int64_t)stats.nBogoSize); + ret.pushKV("hash_serialized_2", stats.hashSerialized.GetHex()); + ret.pushKV("disk_size", stats.nDiskSize); + ret.pushKV("total_amount", ValueFromAmount(stats.nTotalAmount)); } else { throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set"); } @@ -1031,17 +1031,17 @@ UniValue gettxout(const JSONRPCRequest& request) BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); CBlockIndex *pindex = it->second; - ret.push_back(Pair("bestblock", pindex->GetBlockHash().GetHex())); + ret.pushKV("bestblock", pindex->GetBlockHash().GetHex()); if (coin.nHeight == MEMPOOL_HEIGHT) { - ret.push_back(Pair("confirmations", 0)); + ret.pushKV("confirmations", 0); } else { - ret.push_back(Pair("confirmations", (int64_t)(pindex->nHeight - coin.nHeight + 1))); + ret.pushKV("confirmations", (int64_t)(pindex->nHeight - coin.nHeight + 1)); } - ret.push_back(Pair("value", ValueFromAmount(coin.out.nValue))); + ret.pushKV("value", ValueFromAmount(coin.out.nValue)); UniValue o(UniValue::VOBJ); ScriptPubKeyToUniv(coin.out.scriptPubKey, o, true); - ret.push_back(Pair("scriptPubKey", o)); - ret.push_back(Pair("coinbase", (bool)coin.fCoinBase)); + ret.pushKV("scriptPubKey", o); + ret.pushKV("coinbase", (bool)coin.fCoinBase); return ret; } @@ -1091,16 +1091,16 @@ static UniValue SoftForkMajorityDesc(int version, CBlockIndex* pindex, const Con activated = pindex->nHeight >= consensusParams.BIP65Height; break; } - rv.push_back(Pair("status", activated)); + rv.pushKV("status", activated); return rv; } static UniValue SoftForkDesc(const std::string &name, int version, CBlockIndex* pindex, const Consensus::Params& consensusParams) { UniValue rv(UniValue::VOBJ); - rv.push_back(Pair("id", name)); - rv.push_back(Pair("version", version)); - rv.push_back(Pair("reject", SoftForkMajorityDesc(version, pindex, consensusParams))); + rv.pushKV("id", name); + rv.pushKV("version", version); + rv.pushKV("reject", SoftForkMajorityDesc(version, pindex, consensusParams)); return rv; } @@ -1109,29 +1109,29 @@ static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Conse UniValue rv(UniValue::VOBJ); const ThresholdState thresholdState = VersionBitsTipState(consensusParams, id); switch (thresholdState) { - case THRESHOLD_DEFINED: rv.push_back(Pair("status", "defined")); break; - case THRESHOLD_STARTED: rv.push_back(Pair("status", "started")); break; - case THRESHOLD_LOCKED_IN: rv.push_back(Pair("status", "locked_in")); break; - case THRESHOLD_ACTIVE: rv.push_back(Pair("status", "active")); break; - case THRESHOLD_FAILED: rv.push_back(Pair("status", "failed")); break; + case THRESHOLD_DEFINED: rv.pushKV("status", "defined"); break; + case THRESHOLD_STARTED: rv.pushKV("status", "started"); break; + case THRESHOLD_LOCKED_IN: rv.pushKV("status", "locked_in"); break; + case THRESHOLD_ACTIVE: rv.pushKV("status", "active"); break; + case THRESHOLD_FAILED: rv.pushKV("status", "failed"); break; } if (THRESHOLD_STARTED == thresholdState) { - rv.push_back(Pair("bit", consensusParams.vDeployments[id].bit)); + rv.pushKV("bit", consensusParams.vDeployments[id].bit); } - rv.push_back(Pair("startTime", consensusParams.vDeployments[id].nStartTime)); - rv.push_back(Pair("timeout", consensusParams.vDeployments[id].nTimeout)); - rv.push_back(Pair("since", VersionBitsTipStateSinceHeight(consensusParams, id))); + rv.pushKV("startTime", consensusParams.vDeployments[id].nStartTime); + rv.pushKV("timeout", consensusParams.vDeployments[id].nTimeout); + rv.pushKV("since", VersionBitsTipStateSinceHeight(consensusParams, id)); if (THRESHOLD_STARTED == thresholdState) { UniValue statsUV(UniValue::VOBJ); BIP9Stats statsStruct = VersionBitsTipStatistics(consensusParams, id); - statsUV.push_back(Pair("period", statsStruct.period)); - statsUV.push_back(Pair("threshold", statsStruct.threshold)); - statsUV.push_back(Pair("elapsed", statsStruct.elapsed)); - statsUV.push_back(Pair("count", statsStruct.count)); - statsUV.push_back(Pair("possible", statsStruct.possible)); - rv.push_back(Pair("statistics", statsUV)); + statsUV.pushKV("period", statsStruct.period); + statsUV.pushKV("threshold", statsStruct.threshold); + statsUV.pushKV("elapsed", statsStruct.elapsed); + statsUV.pushKV("count", statsStruct.count); + statsUV.pushKV("possible", statsStruct.possible); + rv.pushKV("statistics", statsUV); } return rv; } @@ -1142,7 +1142,7 @@ void BIP9SoftForkDescPushBack(UniValue& bip9_softforks, const Consensus::Params& // A timeout value of 0 guarantees a softfork will never be activated. // This is used when softfork codes are merged without specifying the deployment schedule. if (consensusParams.vDeployments[id].nTimeout > 0) - bip9_softforks.push_back(Pair(VersionBitsDeploymentInfo[id].name, BIP9SoftForkDesc(consensusParams, id))); + bip9_softforks.pushKV(VersionBitsDeploymentInfo[id].name, BIP9SoftForkDesc(consensusParams, id)); } UniValue getblockchaininfo(const JSONRPCRequest& request) @@ -1202,17 +1202,17 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) LOCK(cs_main); UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("chain", Params().NetworkIDString())); - obj.push_back(Pair("blocks", (int)chainActive.Height())); - obj.push_back(Pair("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1)); - obj.push_back(Pair("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex())); - obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("mediantime", (int64_t)chainActive.Tip()->GetMedianTimePast())); - obj.push_back(Pair("verificationprogress", GuessVerificationProgress(Params().TxData(), chainActive.Tip()))); - obj.push_back(Pair("initialblockdownload", IsInitialBlockDownload())); - obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex())); - obj.push_back(Pair("size_on_disk", CalculateCurrentUsage())); - obj.push_back(Pair("pruned", fPruneMode)); + obj.pushKV("chain", Params().NetworkIDString()); + obj.pushKV("blocks", (int)chainActive.Height()); + obj.pushKV("headers", pindexBestHeader ? pindexBestHeader->nHeight : -1); + obj.pushKV("bestblockhash", chainActive.Tip()->GetBlockHash().GetHex()); + obj.pushKV("difficulty", (double)GetDifficulty()); + obj.pushKV("mediantime", (int64_t)chainActive.Tip()->GetMedianTimePast()); + obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), chainActive.Tip())); + obj.pushKV("initialblockdownload", IsInitialBlockDownload()); + obj.pushKV("chainwork", chainActive.Tip()->nChainWork.GetHex()); + obj.pushKV("size_on_disk", CalculateCurrentUsage()); + obj.pushKV("pruned", fPruneMode); if (fPruneMode) { CBlockIndex* block = chainActive.Tip(); assert(block); @@ -1220,13 +1220,13 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) block = block->pprev; } - obj.push_back(Pair("pruneheight", block->nHeight)); + obj.pushKV("pruneheight", block->nHeight); // if 0, execution bypasses the whole if block. bool automatic_pruning = (gArgs.GetArg("-prune", 0) != 1); - obj.push_back(Pair("automatic_pruning", automatic_pruning)); + obj.pushKV("automatic_pruning", automatic_pruning); if (automatic_pruning) { - obj.push_back(Pair("prune_target_size", nPruneTarget)); + obj.pushKV("prune_target_size", nPruneTarget); } } @@ -1240,10 +1240,10 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) for (int pos = Consensus::DEPLOYMENT_CSV; pos != Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++pos) { BIP9SoftForkDescPushBack(bip9_softforks, consensusParams, static_cast<Consensus::DeploymentPos>(pos)); } - obj.push_back(Pair("softforks", softforks)); - obj.push_back(Pair("bip9_softforks", bip9_softforks)); + obj.pushKV("softforks", softforks); + obj.pushKV("bip9_softforks", bip9_softforks); - obj.push_back(Pair("warnings", GetWarnings("statusbar"))); + obj.pushKV("warnings", GetWarnings("statusbar")); return obj; } @@ -1331,11 +1331,11 @@ UniValue getchaintips(const JSONRPCRequest& request) for (const CBlockIndex* block : setTips) { UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("height", block->nHeight)); - obj.push_back(Pair("hash", block->phashBlock->GetHex())); + obj.pushKV("height", block->nHeight); + obj.pushKV("hash", block->phashBlock->GetHex()); const int branchLen = block->nHeight - chainActive.FindFork(block)->nHeight; - obj.push_back(Pair("branchlen", branchLen)); + obj.pushKV("branchlen", branchLen); std::string status; if (chainActive.Contains(block)) { @@ -1357,7 +1357,7 @@ UniValue getchaintips(const JSONRPCRequest& request) // No clue. status = "unknown"; } - obj.push_back(Pair("status", status)); + obj.pushKV("status", status); res.push_back(obj); } @@ -1368,13 +1368,13 @@ UniValue getchaintips(const JSONRPCRequest& request) UniValue mempoolInfoToJSON() { UniValue ret(UniValue::VOBJ); - ret.push_back(Pair("size", (int64_t) mempool.size())); - ret.push_back(Pair("bytes", (int64_t) mempool.GetTotalTxSize())); - ret.push_back(Pair("usage", (int64_t) mempool.DynamicMemoryUsage())); + ret.pushKV("size", (int64_t) mempool.size()); + ret.pushKV("bytes", (int64_t) mempool.GetTotalTxSize()); + ret.pushKV("usage", (int64_t) mempool.DynamicMemoryUsage()); size_t maxmempool = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; - ret.push_back(Pair("maxmempool", (int64_t) maxmempool)); - ret.push_back(Pair("mempoolminfee", ValueFromAmount(std::max(mempool.GetMinFee(maxmempool), ::minRelayTxFee).GetFeePerK()))); - ret.push_back(Pair("minrelaytxfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()))); + ret.pushKV("maxmempool", (int64_t) maxmempool); + ret.pushKV("mempoolminfee", ValueFromAmount(std::max(mempool.GetMinFee(maxmempool), ::minRelayTxFee).GetFeePerK())); + ret.pushKV("minrelaytxfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())); return ret; } @@ -1434,7 +1434,7 @@ UniValue preciousblock(const JSONRPCRequest& request) PreciousBlock(state, Params(), pblockindex); if (!state.IsValid()) { - throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); + throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state)); } return NullUniValue; @@ -1472,7 +1472,7 @@ UniValue invalidateblock(const JSONRPCRequest& request) } if (!state.IsValid()) { - throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); + throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state)); } return NullUniValue; @@ -1509,7 +1509,7 @@ UniValue reconsiderblock(const JSONRPCRequest& request) ActivateBestChain(state, Params()); if (!state.IsValid()) { - throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason()); + throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state)); } return NullUniValue; @@ -1526,12 +1526,13 @@ UniValue getchaintxstats(const JSONRPCRequest& request) "2. \"blockhash\" (string, optional) The hash of the block that ends the window.\n" "\nResult:\n" "{\n" - " \"time\": xxxxx, (numeric) The timestamp for the final block in the window in UNIX format.\n" - " \"txcount\": xxxxx, (numeric) The total number of transactions in the chain up to that point.\n" - " \"window_block_count\": xxxxx, (numeric) Size of the window in number of blocks.\n" - " \"window_tx_count\": xxxxx, (numeric) The number of transactions in the window. Only returned if \"window_block_count\" is > 0.\n" - " \"window_interval\": xxxxx, (numeric) The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0.\n" - " \"txrate\": x.xx, (numeric) The average rate of transactions per second in the window. Only returned if \"window_interval\" is > 0.\n" + " \"time\": xxxxx, (numeric) The timestamp for the final block in the window in UNIX format.\n" + " \"txcount\": xxxxx, (numeric) The total number of transactions in the chain up to that point.\n" + " \"window_final_block_hash\": \"...\", (string) The hash of the final block in the window.\n" + " \"window_block_count\": xxxxx, (numeric) Size of the window in number of blocks.\n" + " \"window_tx_count\": xxxxx, (numeric) The number of transactions in the window. Only returned if \"window_block_count\" is > 0.\n" + " \"window_interval\": xxxxx, (numeric) The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0.\n" + " \"txrate\": x.xx, (numeric) The average rate of transactions per second in the window. Only returned if \"window_interval\" is > 0.\n" "}\n" "\nExamples:\n" + HelpExampleCli("getchaintxstats", "") @@ -1562,7 +1563,7 @@ UniValue getchaintxstats(const JSONRPCRequest& request) pindex = chainActive.Tip(); } } - + assert(pindex != nullptr); if (request.params[0].isNull()) { @@ -1580,14 +1581,15 @@ UniValue getchaintxstats(const JSONRPCRequest& request) int nTxDiff = pindex->nChainTx - pindexPast->nChainTx; UniValue ret(UniValue::VOBJ); - ret.push_back(Pair("time", (int64_t)pindex->nTime)); - ret.push_back(Pair("txcount", (int64_t)pindex->nChainTx)); - ret.push_back(Pair("window_block_count", blockcount)); + ret.pushKV("time", (int64_t)pindex->nTime); + ret.pushKV("txcount", (int64_t)pindex->nChainTx); + ret.pushKV("window_final_block_hash", pindex->GetBlockHash().GetHex()); + ret.pushKV("window_block_count", blockcount); if (blockcount > 0) { - ret.push_back(Pair("window_tx_count", nTxDiff)); - ret.push_back(Pair("window_interval", nTimeDiff)); + ret.pushKV("window_tx_count", nTxDiff); + ret.pushKV("window_interval", nTimeDiff); if (nTimeDiff > 0) { - ret.push_back(Pair("txrate", ((double)nTxDiff) / nTimeDiff)); + ret.pushKV("txrate", ((double)nTxDiff) / nTimeDiff); } } diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index b88c1bccd5..99c1242d8a 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -114,7 +114,6 @@ static const CRPCConvertParam vRPCConvertParams[] = { "pruneblockchain", 0, "height" }, { "keypoolrefill", 0, "newsize" }, { "getrawmempool", 0, "verbose" }, - { "estimatefee", 0, "nblocks" }, { "estimatesmartfee", 0, "conf_target" }, { "estimaterawfee", 0, "conf_target" }, { "estimaterawfee", 1, "threshold" }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index c22d0ac377..3f3bfa0cfd 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -201,7 +201,6 @@ UniValue getmininginfo(const JSONRPCRequest& request) " \"pooledtx\": n (numeric) The size of the mempool\n" " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" " \"warnings\": \"...\" (string) any network and blockchain warnings\n" - " \"errors\": \"...\" (string) DEPRECATED. Same as warnings. Only shown when bitcoind is started with -deprecatedrpc=getmininginfo\n" "}\n" "\nExamples:\n" + HelpExampleCli("getmininginfo", "") @@ -212,18 +211,14 @@ UniValue getmininginfo(const JSONRPCRequest& request) LOCK(cs_main); UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("blocks", (int)chainActive.Height())); - obj.push_back(Pair("currentblockweight", (uint64_t)nLastBlockWeight)); - obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx)); - obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("networkhashps", getnetworkhashps(request))); - obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); - obj.push_back(Pair("chain", Params().NetworkIDString())); - if (IsDeprecatedRPCEnabled("getmininginfo")) { - obj.push_back(Pair("errors", GetWarnings("statusbar"))); - } else { - obj.push_back(Pair("warnings", GetWarnings("statusbar"))); - } + obj.pushKV("blocks", (int)chainActive.Height()); + obj.pushKV("currentblockweight", (uint64_t)nLastBlockWeight); + obj.pushKV("currentblocktx", (uint64_t)nLastBlockTx); + obj.pushKV("difficulty", (double)GetDifficulty()); + obj.pushKV("networkhashps", getnetworkhashps(request)); + obj.pushKV("pooledtx", (uint64_t)mempool.size()); + obj.pushKV("chain", Params().NetworkIDString()); + obj.pushKV("warnings", GetWarnings("statusbar")); return obj; } @@ -269,11 +264,11 @@ static UniValue BIP22ValidationResult(const CValidationState& state) if (state.IsValid()) return NullUniValue; - std::string strRejectReason = state.GetRejectReason(); if (state.IsError()) - throw JSONRPCError(RPC_VERIFY_ERROR, strRejectReason); + throw JSONRPCError(RPC_VERIFY_ERROR, FormatStateMessage(state)); if (state.IsInvalid()) { + std::string strRejectReason = state.GetRejectReason(); if (strRejectReason.empty()) return "rejected"; return strRejectReason; @@ -555,9 +550,9 @@ UniValue getblocktemplate(const JSONRPCRequest& request) UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("data", EncodeHexTx(tx))); - entry.push_back(Pair("txid", txHash.GetHex())); - entry.push_back(Pair("hash", tx.GetWitnessHash().GetHex())); + entry.pushKV("data", EncodeHexTx(tx)); + entry.pushKV("txid", txHash.GetHex()); + entry.pushKV("hash", tx.GetWitnessHash().GetHex()); UniValue deps(UniValue::VARR); for (const CTxIn &in : tx.vin) @@ -565,23 +560,23 @@ UniValue getblocktemplate(const JSONRPCRequest& request) if (setTxIndex.count(in.prevout.hash)) deps.push_back(setTxIndex[in.prevout.hash]); } - entry.push_back(Pair("depends", deps)); + entry.pushKV("depends", deps); int index_in_template = i - 1; - entry.push_back(Pair("fee", pblocktemplate->vTxFees[index_in_template])); + entry.pushKV("fee", pblocktemplate->vTxFees[index_in_template]); int64_t nTxSigOps = pblocktemplate->vTxSigOpsCost[index_in_template]; if (fPreSegWit) { assert(nTxSigOps % WITNESS_SCALE_FACTOR == 0); nTxSigOps /= WITNESS_SCALE_FACTOR; } - entry.push_back(Pair("sigops", nTxSigOps)); - entry.push_back(Pair("weight", GetTransactionWeight(tx))); + entry.pushKV("sigops", nTxSigOps); + entry.pushKV("weight", GetTransactionWeight(tx)); transactions.push_back(entry); } UniValue aux(UniValue::VOBJ); - aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end()))); + aux.pushKV("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end())); arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits); @@ -591,7 +586,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request) aMutable.push_back("prevblock"); UniValue result(UniValue::VOBJ); - result.push_back(Pair("capabilities", aCaps)); + result.pushKV("capabilities", aCaps); UniValue aRules(UniValue::VARR); UniValue vbavailable(UniValue::VOBJ); @@ -610,7 +605,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request) case THRESHOLD_STARTED: { const struct VBDeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos]; - vbavailable.push_back(Pair(gbt_vb_name(pos), consensusParams.vDeployments[pos].bit)); + vbavailable.pushKV(gbt_vb_name(pos), consensusParams.vDeployments[pos].bit); if (setClientRules.find(vbinfo.name) == setClientRules.end()) { if (!vbinfo.gbt_force) { // If the client doesn't support this, don't indicate it in the [default] version @@ -635,10 +630,10 @@ UniValue getblocktemplate(const JSONRPCRequest& request) } } } - result.push_back(Pair("version", pblock->nVersion)); - result.push_back(Pair("rules", aRules)); - result.push_back(Pair("vbavailable", vbavailable)); - result.push_back(Pair("vbrequired", int(0))); + result.pushKV("version", pblock->nVersion); + result.pushKV("rules", aRules); + result.pushKV("vbavailable", vbavailable); + result.pushKV("vbrequired", int(0)); if (nMaxVersionPreVB >= 2) { // If VB is supported by the client, nMaxVersionPreVB is -1, so we won't get here @@ -648,15 +643,15 @@ UniValue getblocktemplate(const JSONRPCRequest& request) aMutable.push_back("version/force"); } - result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex())); - result.push_back(Pair("transactions", transactions)); - result.push_back(Pair("coinbaseaux", aux)); - result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue)); - result.push_back(Pair("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast))); - result.push_back(Pair("target", hashTarget.GetHex())); - result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1)); - result.push_back(Pair("mutable", aMutable)); - result.push_back(Pair("noncerange", "00000000ffffffff")); + result.pushKV("previousblockhash", pblock->hashPrevBlock.GetHex()); + result.pushKV("transactions", transactions); + result.pushKV("coinbaseaux", aux); + result.pushKV("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue); + result.pushKV("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast)); + result.pushKV("target", hashTarget.GetHex()); + result.pushKV("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1); + result.pushKV("mutable", aMutable); + result.pushKV("noncerange", "00000000ffffffff"); int64_t nSigOpLimit = MAX_BLOCK_SIGOPS_COST; int64_t nSizeLimit = MAX_BLOCK_SERIALIZED_SIZE; if (fPreSegWit) { @@ -665,17 +660,17 @@ UniValue getblocktemplate(const JSONRPCRequest& request) assert(nSizeLimit % WITNESS_SCALE_FACTOR == 0); nSizeLimit /= WITNESS_SCALE_FACTOR; } - result.push_back(Pair("sigoplimit", nSigOpLimit)); - result.push_back(Pair("sizelimit", nSizeLimit)); + result.pushKV("sigoplimit", nSigOpLimit); + result.pushKV("sizelimit", nSizeLimit); if (!fPreSegWit) { - result.push_back(Pair("weightlimit", (int64_t)MAX_BLOCK_WEIGHT)); + result.pushKV("weightlimit", (int64_t)MAX_BLOCK_WEIGHT); } - result.push_back(Pair("curtime", pblock->GetBlockTime())); - result.push_back(Pair("bits", strprintf("%08x", pblock->nBits))); - result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1))); + result.pushKV("curtime", pblock->GetBlockTime()); + result.pushKV("bits", strprintf("%08x", pblock->nBits)); + result.pushKV("height", (int64_t)(pindexPrev->nHeight+1)); if (!pblocktemplate->vchCoinbaseCommitment.empty() && fSupportsSegwit) { - result.push_back(Pair("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment.begin(), pblocktemplate->vchCoinbaseCommitment.end()))); + result.pushKV("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment.begin(), pblocktemplate->vchCoinbaseCommitment.end())); } return result; @@ -772,43 +767,8 @@ UniValue submitblock(const JSONRPCRequest& request) UniValue estimatefee(const JSONRPCRequest& request) { - if (request.fHelp || request.params.size() != 1) - throw std::runtime_error( - "estimatefee nblocks\n" - "\nDEPRECATED. Please use estimatesmartfee for more intelligent estimates." - "\nEstimates the approximate fee per kilobyte needed for a transaction to begin\n" - "confirmation within nblocks blocks. Uses virtual transaction size of transaction\n" - "as defined in BIP 141 (witness data is discounted).\n" - "\nArguments:\n" - "1. nblocks (numeric, required)\n" - "\nResult:\n" - "n (numeric) estimated fee-per-kilobyte\n" - "\n" - "A negative value is returned if not enough transactions and blocks\n" - "have been observed to make an estimate.\n" - "-1 is always returned for nblocks == 1 as it is impossible to calculate\n" - "a fee that is high enough to get reliably included in the next block.\n" - "\nExample:\n" - + HelpExampleCli("estimatefee", "6") - ); - - if (!IsDeprecatedRPCEnabled("estimatefee")) { - throw JSONRPCError(RPC_METHOD_DEPRECATED, "estimatefee is deprecated and will be fully removed in v0.17. " - "To use estimatefee in v0.16, restart bitcoind with -deprecatedrpc=estimatefee.\n" - "Projects should transition to using estimatesmartfee before upgrading to v0.17"); - } - - RPCTypeCheck(request.params, {UniValue::VNUM}); - - int nBlocks = request.params[0].get_int(); - if (nBlocks < 1) - nBlocks = 1; - - CFeeRate feeRate = ::feeEstimator.estimateFee(nBlocks); - if (feeRate == CFeeRate(0)) - return -1.0; - - return ValueFromAmount(feeRate.GetFeePerK()); + throw JSONRPCError(RPC_METHOD_DEPRECATED, "estimatefee was removed in v0.17.\n" + "Clients should use estimatesmartfee."); } UniValue estimatesmartfee(const JSONRPCRequest& request) @@ -863,12 +823,12 @@ UniValue estimatesmartfee(const JSONRPCRequest& request) FeeCalculation feeCalc; CFeeRate feeRate = ::feeEstimator.estimateSmartFee(conf_target, &feeCalc, conservative); if (feeRate != CFeeRate(0)) { - result.push_back(Pair("feerate", ValueFromAmount(feeRate.GetFeePerK()))); + result.pushKV("feerate", ValueFromAmount(feeRate.GetFeePerK())); } else { errors.push_back("Insufficient data or no feerate found"); - result.push_back(Pair("errors", errors)); + result.pushKV("errors", errors); } - result.push_back(Pair("blocks", feeCalc.returnedTarget)); + result.pushKV("blocks", feeCalc.returnedTarget); return result; } @@ -939,37 +899,37 @@ UniValue estimaterawfee(const JSONRPCRequest& request) UniValue horizon_result(UniValue::VOBJ); UniValue errors(UniValue::VARR); UniValue passbucket(UniValue::VOBJ); - passbucket.push_back(Pair("startrange", round(buckets.pass.start))); - passbucket.push_back(Pair("endrange", round(buckets.pass.end))); - passbucket.push_back(Pair("withintarget", round(buckets.pass.withinTarget * 100.0) / 100.0)); - passbucket.push_back(Pair("totalconfirmed", round(buckets.pass.totalConfirmed * 100.0) / 100.0)); - passbucket.push_back(Pair("inmempool", round(buckets.pass.inMempool * 100.0) / 100.0)); - passbucket.push_back(Pair("leftmempool", round(buckets.pass.leftMempool * 100.0) / 100.0)); + passbucket.pushKV("startrange", round(buckets.pass.start)); + passbucket.pushKV("endrange", round(buckets.pass.end)); + passbucket.pushKV("withintarget", round(buckets.pass.withinTarget * 100.0) / 100.0); + passbucket.pushKV("totalconfirmed", round(buckets.pass.totalConfirmed * 100.0) / 100.0); + passbucket.pushKV("inmempool", round(buckets.pass.inMempool * 100.0) / 100.0); + passbucket.pushKV("leftmempool", round(buckets.pass.leftMempool * 100.0) / 100.0); UniValue failbucket(UniValue::VOBJ); - failbucket.push_back(Pair("startrange", round(buckets.fail.start))); - failbucket.push_back(Pair("endrange", round(buckets.fail.end))); - failbucket.push_back(Pair("withintarget", round(buckets.fail.withinTarget * 100.0) / 100.0)); - failbucket.push_back(Pair("totalconfirmed", round(buckets.fail.totalConfirmed * 100.0) / 100.0)); - failbucket.push_back(Pair("inmempool", round(buckets.fail.inMempool * 100.0) / 100.0)); - failbucket.push_back(Pair("leftmempool", round(buckets.fail.leftMempool * 100.0) / 100.0)); + failbucket.pushKV("startrange", round(buckets.fail.start)); + failbucket.pushKV("endrange", round(buckets.fail.end)); + failbucket.pushKV("withintarget", round(buckets.fail.withinTarget * 100.0) / 100.0); + failbucket.pushKV("totalconfirmed", round(buckets.fail.totalConfirmed * 100.0) / 100.0); + failbucket.pushKV("inmempool", round(buckets.fail.inMempool * 100.0) / 100.0); + failbucket.pushKV("leftmempool", round(buckets.fail.leftMempool * 100.0) / 100.0); // CFeeRate(0) is used to indicate error as a return value from estimateRawFee if (feeRate != CFeeRate(0)) { - horizon_result.push_back(Pair("feerate", ValueFromAmount(feeRate.GetFeePerK()))); - horizon_result.push_back(Pair("decay", buckets.decay)); - horizon_result.push_back(Pair("scale", (int)buckets.scale)); - horizon_result.push_back(Pair("pass", passbucket)); + horizon_result.pushKV("feerate", ValueFromAmount(feeRate.GetFeePerK())); + horizon_result.pushKV("decay", buckets.decay); + horizon_result.pushKV("scale", (int)buckets.scale); + horizon_result.pushKV("pass", passbucket); // buckets.fail.start == -1 indicates that all buckets passed, there is no fail bucket to output - if (buckets.fail.start != -1) horizon_result.push_back(Pair("fail", failbucket)); + if (buckets.fail.start != -1) horizon_result.pushKV("fail", failbucket); } else { // Output only information that is still meaningful in the event of error - horizon_result.push_back(Pair("decay", buckets.decay)); - horizon_result.push_back(Pair("scale", (int)buckets.scale)); - horizon_result.push_back(Pair("fail", failbucket)); + horizon_result.pushKV("decay", buckets.decay); + horizon_result.pushKV("scale", (int)buckets.scale); + horizon_result.pushKV("fail", failbucket); errors.push_back("Insufficient data or no feerate found which meets threshold"); - horizon_result.push_back(Pair("errors",errors)); + horizon_result.pushKV("errors",errors); } - result.push_back(Pair(StringForFeeEstimateHorizon(horizon), horizon_result)); + result.pushKV(StringForFeeEstimateHorizon(horizon), horizon_result); } return result; } @@ -986,7 +946,7 @@ static const CRPCCommand commands[] = { "generating", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} }, - { "util", "estimatefee", &estimatefee, {"nblocks"} }, + { "hidden", "estimatefee", &estimatefee, {} }, { "util", "estimatesmartfee", &estimatesmartfee, {"conf_target", "estimate_mode"} }, { "hidden", "estimaterawfee", &estimaterawfee, {"conf_target", "threshold"} }, diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index c11dda22c4..61bd59e9ed 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -33,221 +33,56 @@ #include <univalue.h> -#ifdef ENABLE_WALLET -class DescribeAddressVisitor : public boost::static_visitor<UniValue> -{ -public: - CWallet * const pwallet; - - explicit DescribeAddressVisitor(CWallet *_pwallet) : pwallet(_pwallet) {} - - void ProcessSubScript(const CScript& subscript, UniValue& obj, bool include_addresses = false) const - { - // Always present: script type and redeemscript - txnouttype which_type; - std::vector<std::vector<unsigned char>> solutions_data; - Solver(subscript, which_type, solutions_data); - obj.pushKV("script", GetTxnOutputType(which_type)); - obj.pushKV("hex", HexStr(subscript.begin(), subscript.end())); - - CTxDestination embedded; - UniValue a(UniValue::VARR); - if (ExtractDestination(subscript, embedded)) { - // Only when the script corresponds to an address. - UniValue subobj = boost::apply_visitor(*this, embedded); - subobj.pushKV("address", EncodeDestination(embedded)); - subobj.pushKV("scriptPubKey", HexStr(subscript.begin(), subscript.end())); - // Always report the pubkey at the top level, so that `getnewaddress()['pubkey']` always works. - if (subobj.exists("pubkey")) obj.pushKV("pubkey", subobj["pubkey"]); - obj.pushKV("embedded", std::move(subobj)); - if (include_addresses) a.push_back(EncodeDestination(embedded)); - } else if (which_type == TX_MULTISIG) { - // Also report some information on multisig scripts (which do not have a corresponding address). - // TODO: abstract out the common functionality between this logic and ExtractDestinations. - obj.pushKV("sigsrequired", solutions_data[0][0]); - UniValue pubkeys(UniValue::VARR); - for (size_t i = 1; i < solutions_data.size() - 1; ++i) { - CPubKey key(solutions_data[i].begin(), solutions_data[i].end()); - if (include_addresses) a.push_back(EncodeDestination(key.GetID())); - pubkeys.push_back(HexStr(key.begin(), key.end())); - } - obj.pushKV("pubkeys", std::move(pubkeys)); - } - - // The "addresses" field is confusing because it refers to public keys using their P2PKH address. - // For that reason, only add the 'addresses' field when needed for backward compatibility. New applications - // can use the 'embedded'->'address' field for P2SH or P2WSH wrapped addresses, and 'pubkeys' for - // inspecting multisig participants. - if (include_addresses) obj.pushKV("addresses", std::move(a)); - } - - UniValue operator()(const CNoDestination &dest) const { return UniValue(UniValue::VOBJ); } - - UniValue operator()(const CKeyID &keyID) const { - UniValue obj(UniValue::VOBJ); - CPubKey vchPubKey; - obj.push_back(Pair("isscript", false)); - obj.push_back(Pair("iswitness", false)); - if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) { - obj.push_back(Pair("pubkey", HexStr(vchPubKey))); - obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed())); - } - return obj; - } - - UniValue operator()(const CScriptID &scriptID) const { - UniValue obj(UniValue::VOBJ); - CScript subscript; - obj.push_back(Pair("isscript", true)); - obj.push_back(Pair("iswitness", false)); - if (pwallet && pwallet->GetCScript(scriptID, subscript)) { - ProcessSubScript(subscript, obj, true); - } - return obj; - } - - UniValue operator()(const WitnessV0KeyHash& id) const - { - UniValue obj(UniValue::VOBJ); - CPubKey pubkey; - obj.push_back(Pair("isscript", false)); - obj.push_back(Pair("iswitness", true)); - obj.push_back(Pair("witness_version", 0)); - obj.push_back(Pair("witness_program", HexStr(id.begin(), id.end()))); - if (pwallet && pwallet->GetPubKey(CKeyID(id), pubkey)) { - obj.push_back(Pair("pubkey", HexStr(pubkey))); - } - return obj; - } - - UniValue operator()(const WitnessV0ScriptHash& id) const - { - UniValue obj(UniValue::VOBJ); - CScript subscript; - obj.push_back(Pair("isscript", true)); - obj.push_back(Pair("iswitness", true)); - obj.push_back(Pair("witness_version", 0)); - obj.push_back(Pair("witness_program", HexStr(id.begin(), id.end()))); - CRIPEMD160 hasher; - uint160 hash; - hasher.Write(id.begin(), 32).Finalize(hash.begin()); - if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) { - ProcessSubScript(subscript, obj); - } - return obj; - } - - UniValue operator()(const WitnessUnknown& id) const - { - UniValue obj(UniValue::VOBJ); - CScript subscript; - obj.push_back(Pair("iswitness", true)); - obj.push_back(Pair("witness_version", (int)id.version)); - obj.push_back(Pair("witness_program", HexStr(id.program, id.program + id.length))); - return obj; - } -}; -#endif - UniValue validateaddress(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "validateaddress \"address\"\n" "\nReturn information about the given bitcoin address.\n" + "DEPRECATION WARNING: Parts of this command have been deprecated and moved to getaddressinfo. Clients must\n" + "transition to using getaddressinfo to access this information before upgrading to v0.18. The following deprecated\n" + "fields have moved to getaddressinfo and will only be shown here with -deprecatedrpc=validateaddress: ismine, iswatchonly,\n" + "script, hex, pubkeys, sigsrequired, pubkey, addresses, embedded, iscompressed, account, timestamp, hdkeypath, kdmasterkeyid.\n" "\nArguments:\n" - "1. \"address\" (string, required) The bitcoin address to validate\n" + "1. \"address\" (string, required) The bitcoin address to validate\n" "\nResult:\n" "{\n" " \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n" " \"address\" : \"address\", (string) The bitcoin address validated\n" " \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\n" - " \"ismine\" : true|false, (boolean) If the address is yours or not\n" - " \"iswatchonly\" : true|false, (boolean) If the address is watchonly\n" - " \"isscript\" : true|false, (boolean, optional) If the address is P2SH or P2WSH. Not included for unknown witness types.\n" - " \"iswitness\" : true|false, (boolean) If the address is P2WPKH, P2WSH, or an unknown witness version\n" - " \"witness_version\" : version (number, optional) For all witness output types, gives the version number.\n" - " \"witness_program\" : \"hex\" (string, optional) For all witness output types, gives the script or key hash present in the address.\n" - " \"script\" : \"type\" (string, optional) The output script type. Only if \"isscript\" is true and the redeemscript is known. Possible types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash, witness_v0_scripthash, witness_unknown\n" - " \"hex\" : \"hex\", (string, optional) The redeemscript for the P2SH or P2WSH address\n" - " \"addresses\" (string, optional) Array of addresses associated with the known redeemscript (only if \"iswitness\" is false). This field is superseded by the \"pubkeys\" field and the address inside \"embedded\".\n" - " [\n" - " \"address\"\n" - " ,...\n" - " ]\n" - " \"pubkeys\" (string, optional) Array of pubkeys associated with the known redeemscript (only if \"script\" is \"multisig\")\n" - " [\n" - " \"pubkey\"\n" - " ,...\n" - " ]\n" - " \"sigsrequired\" : xxxxx (numeric, optional) Number of signatures required to spend multisig output (only if \"script\" is \"multisig\")\n" - " \"pubkey\" : \"publickeyhex\", (string, optional) The hex value of the raw public key, for single-key addresses (possibly embedded in P2SH or P2WSH)\n" - " \"embedded\" : {...}, (object, optional) information about the address embedded in P2SH or P2WSH, if relevant and known. It includes all validateaddress output fields for the embedded address, excluding \"isvalid\", metadata (\"timestamp\", \"hdkeypath\", \"hdmasterkeyid\") and relation to the wallet (\"ismine\", \"iswatchonly\", \"account\").\n" - " \"iscompressed\" : true|false, (boolean) If the address is compressed\n" - " \"account\" : \"account\" (string) DEPRECATED. The account associated with the address, \"\" is the default account\n" - " \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n" - " \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n" - " \"hdmasterkeyid\" : \"<hash160>\" (string, optional) The Hash160 of the HD master pubkey\n" + " \"isscript\" : true|false, (boolean) If the key is a script\n" + " \"iswitness\" : true|false, (boolean) If the address is a witness address\n" + " \"witness_version\" : version (numeric, optional) The version number of the witness program\n" + " \"witness_program\" : \"hex\" (string, optional) The hex value of the witness program\n" "}\n" "\nExamples:\n" + HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") + HelpExampleRpc("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") ); -#ifdef ENABLE_WALLET - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); - - LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr); -#else - LOCK(cs_main); -#endif - CTxDestination dest = DecodeDestination(request.params[0].get_str()); bool isValid = IsValidDestination(dest); UniValue ret(UniValue::VOBJ); - ret.push_back(Pair("isvalid", isValid)); + ret.pushKV("isvalid", isValid); if (isValid) { - std::string currentAddress = EncodeDestination(dest); - ret.push_back(Pair("address", currentAddress)); - - CScript scriptPubKey = GetScriptForDestination(dest); - ret.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); #ifdef ENABLE_WALLET - isminetype mine = pwallet ? IsMine(*pwallet, dest) : ISMINE_NO; - ret.push_back(Pair("ismine", bool(mine & ISMINE_SPENDABLE))); - ret.push_back(Pair("iswatchonly", bool(mine & ISMINE_WATCH_ONLY))); - UniValue detail = boost::apply_visitor(DescribeAddressVisitor(pwallet), dest); - ret.pushKVs(detail); - if (pwallet && pwallet->mapAddressBook.count(dest)) { - ret.push_back(Pair("account", pwallet->mapAddressBook[dest].name)); - } - if (pwallet) { - const CKeyMetadata* meta = nullptr; - CKeyID key_id = GetKeyForDestination(*pwallet, dest); - if (!key_id.IsNull()) { - auto it = pwallet->mapKeyMetadata.find(key_id); - if (it != pwallet->mapKeyMetadata.end()) { - meta = &it->second; - } - } - if (!meta) { - auto it = pwallet->m_script_metadata.find(CScriptID(scriptPubKey)); - if (it != pwallet->m_script_metadata.end()) { - meta = &it->second; - } - } - if (meta) { - ret.push_back(Pair("timestamp", meta->nCreateTime)); - if (!meta->hdKeypath.empty()) { - ret.push_back(Pair("hdkeypath", meta->hdKeypath)); - ret.push_back(Pair("hdmasterkeyid", meta->hdMasterKeyID.GetHex())); - } - } + if (!::vpwallets.empty() && IsDeprecatedRPCEnabled("validateaddress")) { + ret.pushKVs(getaddressinfo(request)); } #endif + if (ret["address"].isNull()) { + std::string currentAddress = EncodeDestination(dest); + ret.pushKV("address", currentAddress); + + CScript scriptPubKey = GetScriptForDestination(dest); + ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));; + + UniValue detail = DescribeAddress(dest); + ret.pushKVs(detail); + } } return ret; } @@ -262,9 +97,6 @@ UniValue createmultisig(const JSONRPCRequest& request) std::string msg = "createmultisig nrequired [\"key\",...]\n" "\nCreates a multi-signature address with n signature of m keys required.\n" "It returns a json object with the address and redeemScript.\n" - "DEPRECATION WARNING: Using addresses with createmultisig is deprecated. Clients must\n" - "transition to using addmultisigaddress to create multisig addresses with addresses known\n" - "to the wallet before upgrading to v0.17. To use the deprecated functionality, start bitcoind with -deprecatedrpc=createmultisig\n" "\nArguments:\n" "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n" "2. \"keys\" (string, required) A json array of hex-encoded public keys\n" @@ -297,15 +129,8 @@ UniValue createmultisig(const JSONRPCRequest& request) if (IsHex(keys[i].get_str()) && (keys[i].get_str().length() == 66 || keys[i].get_str().length() == 130)) { pubkeys.push_back(HexToPubKey(keys[i].get_str())); } else { -#ifdef ENABLE_WALLET - CWallet* const pwallet = GetWalletForJSONRPCRequest(request); - if (IsDeprecatedRPCEnabled("createmultisig") && EnsureWalletIsAvailable(pwallet, false)) { - pubkeys.push_back(AddrToPubKey(pwallet, keys[i].get_str())); - } else -#endif throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid public key: %s\nNote that from v0.16, createmultisig no longer accepts addresses." - " Clients must transition to using addmultisigaddress to create multisig addresses with addresses known to the wallet before upgrading to v0.17." - " To use the deprecated functionality, start bitcoind with -deprecatedrpc=createmultisig", keys[i].get_str())); + " Users must use addmultisigaddress to create multisig addresses with addresses known to the wallet.", keys[i].get_str())); } } @@ -314,8 +139,8 @@ UniValue createmultisig(const JSONRPCRequest& request) CScriptID innerID(inner); UniValue result(UniValue::VOBJ); - result.push_back(Pair("address", EncodeDestination(innerID))); - result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end()))); + result.pushKV("address", EncodeDestination(innerID)); + result.pushKV("redeemScript", HexStr(inner.begin(), inner.end())); return result; } @@ -449,12 +274,12 @@ static UniValue RPCLockedMemoryInfo() { LockedPool::Stats stats = LockedPoolManager::Instance().stats(); UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("used", uint64_t(stats.used))); - obj.push_back(Pair("free", uint64_t(stats.free))); - obj.push_back(Pair("total", uint64_t(stats.total))); - obj.push_back(Pair("locked", uint64_t(stats.locked))); - obj.push_back(Pair("chunks_used", uint64_t(stats.chunks_used))); - obj.push_back(Pair("chunks_free", uint64_t(stats.chunks_free))); + obj.pushKV("used", uint64_t(stats.used)); + obj.pushKV("free", uint64_t(stats.free)); + obj.pushKV("total", uint64_t(stats.total)); + obj.pushKV("locked", uint64_t(stats.locked)); + obj.pushKV("chunks_used", uint64_t(stats.chunks_used)); + obj.pushKV("chunks_free", uint64_t(stats.chunks_free)); return obj; } @@ -511,7 +336,7 @@ UniValue getmemoryinfo(const JSONRPCRequest& request) std::string mode = request.params[0].isNull() ? "stats" : request.params[0].get_str(); if (mode == "stats") { UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("locked", RPCLockedMemoryInfo())); + obj.pushKV("locked", RPCLockedMemoryInfo()); return obj; } else if (mode == "mallocinfo") { #ifdef HAVE_MALLOC_INFO diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 59b376c59a..7a0225ff0d 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -130,59 +130,59 @@ UniValue getpeerinfo(const JSONRPCRequest& request) UniValue obj(UniValue::VOBJ); CNodeStateStats statestats; bool fStateStats = GetNodeStateStats(stats.nodeid, statestats); - obj.push_back(Pair("id", stats.nodeid)); - obj.push_back(Pair("addr", stats.addrName)); + obj.pushKV("id", stats.nodeid); + obj.pushKV("addr", stats.addrName); if (!(stats.addrLocal.empty())) - obj.push_back(Pair("addrlocal", stats.addrLocal)); + obj.pushKV("addrlocal", stats.addrLocal); if (stats.addrBind.IsValid()) - obj.push_back(Pair("addrbind", stats.addrBind.ToString())); - obj.push_back(Pair("services", strprintf("%016x", stats.nServices))); - obj.push_back(Pair("relaytxes", stats.fRelayTxes)); - obj.push_back(Pair("lastsend", stats.nLastSend)); - obj.push_back(Pair("lastrecv", stats.nLastRecv)); - obj.push_back(Pair("bytessent", stats.nSendBytes)); - obj.push_back(Pair("bytesrecv", stats.nRecvBytes)); - obj.push_back(Pair("conntime", stats.nTimeConnected)); - obj.push_back(Pair("timeoffset", stats.nTimeOffset)); + obj.pushKV("addrbind", stats.addrBind.ToString()); + obj.pushKV("services", strprintf("%016x", stats.nServices)); + obj.pushKV("relaytxes", stats.fRelayTxes); + obj.pushKV("lastsend", stats.nLastSend); + obj.pushKV("lastrecv", stats.nLastRecv); + obj.pushKV("bytessent", stats.nSendBytes); + obj.pushKV("bytesrecv", stats.nRecvBytes); + obj.pushKV("conntime", stats.nTimeConnected); + obj.pushKV("timeoffset", stats.nTimeOffset); if (stats.dPingTime > 0.0) - obj.push_back(Pair("pingtime", stats.dPingTime)); + obj.pushKV("pingtime", stats.dPingTime); if (stats.dMinPing < static_cast<double>(std::numeric_limits<int64_t>::max())/1e6) - obj.push_back(Pair("minping", stats.dMinPing)); + obj.pushKV("minping", stats.dMinPing); if (stats.dPingWait > 0.0) - obj.push_back(Pair("pingwait", stats.dPingWait)); - obj.push_back(Pair("version", stats.nVersion)); + obj.pushKV("pingwait", stats.dPingWait); + obj.pushKV("version", stats.nVersion); // Use the sanitized form of subver here, to avoid tricksy remote peers from // corrupting or modifying the JSON output by putting special characters in // their ver message. - obj.push_back(Pair("subver", stats.cleanSubVer)); - obj.push_back(Pair("inbound", stats.fInbound)); - obj.push_back(Pair("addnode", stats.m_manual_connection)); - obj.push_back(Pair("startingheight", stats.nStartingHeight)); + obj.pushKV("subver", stats.cleanSubVer); + obj.pushKV("inbound", stats.fInbound); + obj.pushKV("addnode", stats.m_manual_connection); + obj.pushKV("startingheight", stats.nStartingHeight); if (fStateStats) { - obj.push_back(Pair("banscore", statestats.nMisbehavior)); - obj.push_back(Pair("synced_headers", statestats.nSyncHeight)); - obj.push_back(Pair("synced_blocks", statestats.nCommonHeight)); + obj.pushKV("banscore", statestats.nMisbehavior); + obj.pushKV("synced_headers", statestats.nSyncHeight); + obj.pushKV("synced_blocks", statestats.nCommonHeight); UniValue heights(UniValue::VARR); for (int height : statestats.vHeightInFlight) { heights.push_back(height); } - obj.push_back(Pair("inflight", heights)); + obj.pushKV("inflight", heights); } - obj.push_back(Pair("whitelisted", stats.fWhitelisted)); + obj.pushKV("whitelisted", stats.fWhitelisted); UniValue sendPerMsgCmd(UniValue::VOBJ); for (const mapMsgCmdSize::value_type &i : stats.mapSendBytesPerMsgCmd) { if (i.second > 0) - sendPerMsgCmd.push_back(Pair(i.first, i.second)); + sendPerMsgCmd.pushKV(i.first, i.second); } - obj.push_back(Pair("bytessent_per_msg", sendPerMsgCmd)); + obj.pushKV("bytessent_per_msg", sendPerMsgCmd); UniValue recvPerMsgCmd(UniValue::VOBJ); for (const mapMsgCmdSize::value_type &i : stats.mapRecvBytesPerMsgCmd) { if (i.second > 0) - recvPerMsgCmd.push_back(Pair(i.first, i.second)); + recvPerMsgCmd.pushKV(i.first, i.second); } - obj.push_back(Pair("bytesrecv_per_msg", recvPerMsgCmd)); + obj.pushKV("bytesrecv_per_msg", recvPerMsgCmd); ret.push_back(obj); } @@ -331,16 +331,16 @@ UniValue getaddednodeinfo(const JSONRPCRequest& request) for (const AddedNodeInfo& info : vInfo) { UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("addednode", info.strAddedNode)); - obj.push_back(Pair("connected", info.fConnected)); + obj.pushKV("addednode", info.strAddedNode); + obj.pushKV("connected", info.fConnected); UniValue addresses(UniValue::VARR); if (info.fConnected) { UniValue address(UniValue::VOBJ); - address.push_back(Pair("address", info.resolvedAddress.ToString())); - address.push_back(Pair("connected", info.fInbound ? "inbound" : "outbound")); + address.pushKV("address", info.resolvedAddress.ToString()); + address.pushKV("connected", info.fInbound ? "inbound" : "outbound"); addresses.push_back(address); } - obj.push_back(Pair("addresses", addresses)); + obj.pushKV("addresses", addresses); ret.push_back(obj); } @@ -377,18 +377,18 @@ UniValue getnettotals(const JSONRPCRequest& request) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("totalbytesrecv", g_connman->GetTotalBytesRecv())); - obj.push_back(Pair("totalbytessent", g_connman->GetTotalBytesSent())); - obj.push_back(Pair("timemillis", GetTimeMillis())); + obj.pushKV("totalbytesrecv", g_connman->GetTotalBytesRecv()); + obj.pushKV("totalbytessent", g_connman->GetTotalBytesSent()); + obj.pushKV("timemillis", GetTimeMillis()); UniValue outboundLimit(UniValue::VOBJ); - outboundLimit.push_back(Pair("timeframe", g_connman->GetMaxOutboundTimeframe())); - outboundLimit.push_back(Pair("target", g_connman->GetMaxOutboundTarget())); - outboundLimit.push_back(Pair("target_reached", g_connman->OutboundTargetReached(false))); - outboundLimit.push_back(Pair("serve_historical_blocks", !g_connman->OutboundTargetReached(true))); - outboundLimit.push_back(Pair("bytes_left_in_cycle", g_connman->GetOutboundTargetBytesLeft())); - outboundLimit.push_back(Pair("time_left_in_cycle", g_connman->GetMaxOutboundTimeLeftInCycle())); - obj.push_back(Pair("uploadtarget", outboundLimit)); + outboundLimit.pushKV("timeframe", g_connman->GetMaxOutboundTimeframe()); + outboundLimit.pushKV("target", g_connman->GetMaxOutboundTarget()); + outboundLimit.pushKV("target_reached", g_connman->OutboundTargetReached(false)); + outboundLimit.pushKV("serve_historical_blocks", !g_connman->OutboundTargetReached(true)); + outboundLimit.pushKV("bytes_left_in_cycle", g_connman->GetOutboundTargetBytesLeft()); + outboundLimit.pushKV("time_left_in_cycle", g_connman->GetMaxOutboundTimeLeftInCycle()); + obj.pushKV("uploadtarget", outboundLimit); return obj; } @@ -403,11 +403,11 @@ static UniValue GetNetworksInfo() proxyType proxy; UniValue obj(UniValue::VOBJ); GetProxy(network, proxy); - obj.push_back(Pair("name", GetNetworkName(network))); - obj.push_back(Pair("limited", IsLimited(network))); - obj.push_back(Pair("reachable", IsReachable(network))); - obj.push_back(Pair("proxy", proxy.IsValid() ? proxy.proxy.ToStringIPPort() : std::string())); - obj.push_back(Pair("proxy_randomize_credentials", proxy.randomize_credentials)); + obj.pushKV("name", GetNetworkName(network)); + obj.pushKV("limited", IsLimited(network)); + obj.pushKV("reachable", IsReachable(network)); + obj.pushKV("proxy", proxy.IsValid() ? proxy.proxy.ToStringIPPort() : std::string()); + obj.pushKV("proxy_randomize_credentials", proxy.randomize_credentials); networks.push_back(obj); } return networks; @@ -458,34 +458,34 @@ UniValue getnetworkinfo(const JSONRPCRequest& request) LOCK(cs_main); UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("version", CLIENT_VERSION)); - obj.push_back(Pair("subversion", strSubVersion)); - obj.push_back(Pair("protocolversion",PROTOCOL_VERSION)); + obj.pushKV("version", CLIENT_VERSION); + obj.pushKV("subversion", strSubVersion); + obj.pushKV("protocolversion",PROTOCOL_VERSION); if(g_connman) - obj.push_back(Pair("localservices", strprintf("%016x", g_connman->GetLocalServices()))); - obj.push_back(Pair("localrelay", fRelayTxes)); - obj.push_back(Pair("timeoffset", GetTimeOffset())); + obj.pushKV("localservices", strprintf("%016x", g_connman->GetLocalServices())); + obj.pushKV("localrelay", fRelayTxes); + obj.pushKV("timeoffset", GetTimeOffset()); if (g_connman) { - obj.push_back(Pair("networkactive", g_connman->GetNetworkActive())); - obj.push_back(Pair("connections", (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL))); + obj.pushKV("networkactive", g_connman->GetNetworkActive()); + obj.pushKV("connections", (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL)); } - obj.push_back(Pair("networks", GetNetworksInfo())); - obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()))); - obj.push_back(Pair("incrementalfee", ValueFromAmount(::incrementalRelayFee.GetFeePerK()))); + obj.pushKV("networks", GetNetworksInfo()); + obj.pushKV("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())); + obj.pushKV("incrementalfee", ValueFromAmount(::incrementalRelayFee.GetFeePerK())); UniValue localAddresses(UniValue::VARR); { LOCK(cs_mapLocalHost); for (const std::pair<CNetAddr, LocalServiceInfo> &item : mapLocalHost) { UniValue rec(UniValue::VOBJ); - rec.push_back(Pair("address", item.first.ToString())); - rec.push_back(Pair("port", item.second.nPort)); - rec.push_back(Pair("score", item.second.nScore)); + rec.pushKV("address", item.first.ToString()); + rec.pushKV("port", item.second.nPort); + rec.pushKV("score", item.second.nScore); localAddresses.push_back(rec); } } - obj.push_back(Pair("localaddresses", localAddresses)); - obj.push_back(Pair("warnings", GetWarnings("statusbar"))); + obj.pushKV("localaddresses", localAddresses); + obj.pushKV("warnings", GetWarnings("statusbar")); return obj; } @@ -575,10 +575,10 @@ UniValue listbanned(const JSONRPCRequest& request) { const CBanEntry& banEntry = entry.second; UniValue rec(UniValue::VOBJ); - rec.push_back(Pair("address", entry.first.ToString())); - rec.push_back(Pair("banned_until", banEntry.nBanUntil)); - rec.push_back(Pair("ban_created", banEntry.nCreateTime)); - rec.push_back(Pair("ban_reason", banEntry.banReasonToString())); + rec.pushKV("address", entry.first.ToString()); + rec.pushKV("banned_until", banEntry.nBanUntil); + rec.pushKV("ban_created", banEntry.nCreateTime); + rec.pushKV("ban_reason", banEntry.banReasonToString()); bannedAddresses.push_back(rec); } diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp index ddc1bb6232..0635b757c6 100644 --- a/src/rpc/protocol.cpp +++ b/src/rpc/protocol.cpp @@ -26,9 +26,9 @@ UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id) { UniValue request(UniValue::VOBJ); - request.push_back(Pair("method", strMethod)); - request.push_back(Pair("params", params)); - request.push_back(Pair("id", id)); + request.pushKV("method", strMethod); + request.pushKV("params", params); + request.pushKV("id", id); return request; } @@ -36,11 +36,11 @@ UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const Un { UniValue reply(UniValue::VOBJ); if (!error.isNull()) - reply.push_back(Pair("result", NullUniValue)); + reply.pushKV("result", NullUniValue); else - reply.push_back(Pair("result", result)); - reply.push_back(Pair("error", error)); - reply.push_back(Pair("id", id)); + reply.pushKV("result", result); + reply.pushKV("error", error); + reply.pushKV("id", id); return reply; } @@ -53,8 +53,8 @@ std::string JSONRPCReply(const UniValue& result, const UniValue& error, const Un UniValue JSONRPCError(int code, const std::string& message) { UniValue error(UniValue::VOBJ); - error.push_back(Pair("code", code)); - error.push_back(Pair("message", message)); + error.pushKV("code", code); + error.pushKV("message", message); return error; } @@ -72,9 +72,7 @@ static fs::path GetAuthCookieFile(bool temp=false) if (temp) { arg += ".tmp"; } - fs::path path(arg); - if (!path.is_complete()) path = GetDataDir() / path; - return path; + return AbsPathForConfigVal(fs::path(arg)); } bool GenerateAuthCookie(std::string *cookie_out) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 00e73675b4..ef5f04e4ee 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -47,17 +47,17 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) TxToUniv(tx, uint256(), entry, true, RPCSerializationFlags()); if (!hashBlock.IsNull()) { - entry.push_back(Pair("blockhash", hashBlock.GetHex())); + entry.pushKV("blockhash", hashBlock.GetHex()); BlockMap::iterator mi = mapBlockIndex.find(hashBlock); if (mi != mapBlockIndex.end() && (*mi).second) { CBlockIndex* pindex = (*mi).second; if (chainActive.Contains(pindex)) { - entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight)); - entry.push_back(Pair("time", pindex->GetBlockTime())); - entry.push_back(Pair("blocktime", pindex->GetBlockTime())); + entry.pushKV("confirmations", 1 + chainActive.Height() - pindex->nHeight); + entry.pushKV("time", pindex->GetBlockTime()); + entry.pushKV("blocktime", pindex->GetBlockTime()); } else - entry.push_back(Pair("confirmations", 0)); + entry.pushKV("confirmations", 0); } } } @@ -190,7 +190,7 @@ UniValue getrawtransaction(const JSONRPCRequest& request) } UniValue result(UniValue::VOBJ); - if (blockindex) result.push_back(Pair("in_active_chain", in_active_chain)); + if (blockindex) result.pushKV("in_active_chain", in_active_chain); TxToJSON(*tx, hash_block, result); return result; } @@ -562,7 +562,7 @@ UniValue decodescript(const JSONRPCRequest& request) if (type.isStr() && type.get_str() != "scripthash") { // P2SH cannot be wrapped in a P2SH. If this script is already a P2SH, // don't return the address for a P2SH of the P2SH. - r.push_back(Pair("p2sh", EncodeDestination(CScriptID(script)))); + r.pushKV("p2sh", EncodeDestination(CScriptID(script))); } return r; @@ -572,16 +572,16 @@ UniValue decodescript(const JSONRPCRequest& request) static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage) { UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("txid", txin.prevout.hash.ToString())); - entry.push_back(Pair("vout", (uint64_t)txin.prevout.n)); + entry.pushKV("txid", txin.prevout.hash.ToString()); + entry.pushKV("vout", (uint64_t)txin.prevout.n); UniValue witness(UniValue::VARR); for (unsigned int i = 0; i < txin.scriptWitness.stack.size(); i++) { witness.push_back(HexStr(txin.scriptWitness.stack[i].begin(), txin.scriptWitness.stack[i].end())); } - entry.push_back(Pair("witness", witness)); - entry.push_back(Pair("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); - entry.push_back(Pair("sequence", (uint64_t)txin.nSequence)); - entry.push_back(Pair("error", strMessage)); + entry.pushKV("witness", witness); + entry.pushKV("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())); + entry.pushKV("sequence", (uint64_t)txin.nSequence); + entry.pushKV("error", strMessage); vErrorsRet.push_back(entry); } @@ -692,7 +692,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request) "\nArguments:\n" "1. \"hexstring\" (string, required) The transaction hex string\n" - "2. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n" + "2. \"prevtxs\" (string, optional) A json array of previous dependent transaction outputs\n" " [ (json array of json objects, or 'null' if none provided)\n" " {\n" " \"txid\":\"id\", (string, required) The transaction id\n" @@ -848,6 +848,8 @@ UniValue signrawtransaction(const JSONRPCRequest& request) std::vector<unsigned char> rsData(ParseHexV(v, "redeemScript")); CScript redeemScript(rsData.begin(), rsData.end()); tempKeystore.AddCScript(redeemScript); + // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH). + tempKeystore.AddCScript(GetScriptForWitness(redeemScript)); } } } @@ -916,10 +918,10 @@ UniValue signrawtransaction(const JSONRPCRequest& request) bool fComplete = vErrors.empty(); UniValue result(UniValue::VOBJ); - result.push_back(Pair("hex", EncodeHexTx(mtx))); - result.push_back(Pair("complete", fComplete)); + result.pushKV("hex", EncodeHexTx(mtx)); + result.pushKV("complete", fComplete); if (!vErrors.empty()) { - result.push_back(Pair("errors", vErrors)); + result.pushKV("errors", vErrors); } return result; @@ -981,12 +983,12 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) if (!AcceptToMemoryPool(mempool, state, std::move(tx), &fMissingInputs, nullptr /* plTxnReplaced */, false /* bypass_limits */, nMaxRawTxFee)) { if (state.IsInvalid()) { - throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); + throw JSONRPCError(RPC_TRANSACTION_REJECTED, FormatStateMessage(state)); } else { if (fMissingInputs) { throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs"); } - throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason()); + throw JSONRPCError(RPC_TRANSACTION_ERROR, FormatStateMessage(state)); } } else { // If wallet is enabled, ensure that the wallet has been made aware diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 09ded4e46e..cdcb68d15f 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -66,3 +66,64 @@ CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey return result; } + +class DescribeAddressVisitor : public boost::static_visitor<UniValue> +{ +public: + explicit DescribeAddressVisitor() {} + + UniValue operator()(const CNoDestination& dest) const + { + return UniValue(UniValue::VOBJ); + } + + UniValue operator()(const CKeyID& keyID) const + { + UniValue obj(UniValue::VOBJ); + obj.pushKV("isscript", false); + obj.pushKV("iswitness", false); + return obj; + } + + UniValue operator()(const CScriptID& scriptID) const + { + UniValue obj(UniValue::VOBJ); + obj.pushKV("isscript", true); + obj.pushKV("iswitness", false); + return obj; + } + + UniValue operator()(const WitnessV0KeyHash& id) const + { + UniValue obj(UniValue::VOBJ); + obj.pushKV("isscript", false); + obj.pushKV("iswitness", true); + obj.pushKV("witness_version", 0); + obj.pushKV("witness_program", HexStr(id.begin(), id.end())); + return obj; + } + + UniValue operator()(const WitnessV0ScriptHash& id) const + { + UniValue obj(UniValue::VOBJ); + obj.pushKV("isscript", true); + obj.pushKV("iswitness", true); + obj.pushKV("witness_version", 0); + obj.pushKV("witness_program", HexStr(id.begin(), id.end())); + return obj; + } + + UniValue operator()(const WitnessUnknown& id) const + { + UniValue obj(UniValue::VOBJ); + obj.pushKV("iswitness", true); + obj.pushKV("witness_version", (int)id.version); + obj.pushKV("witness_program", HexStr(id.program, id.program + id.length)); + return obj; + } +}; + +UniValue DescribeAddress(const CTxDestination& dest) +{ + return boost::apply_visitor(DescribeAddressVisitor(), dest); +} diff --git a/src/rpc/util.h b/src/rpc/util.h index 568a4260ba..5380d45a83 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -5,6 +5,13 @@ #ifndef BITCOIN_RPC_UTIL_H #define BITCOIN_RPC_UTIL_H +#include <pubkey.h> +#include <script/standard.h> +#include <univalue.h> +#include <utilstrencodings.h> + +#include <boost/variant/static_visitor.hpp> + #include <string> #include <vector> @@ -16,4 +23,6 @@ CPubKey HexToPubKey(const std::string& hex_in); CPubKey AddrToPubKey(CKeyStore* const keystore, const std::string& addr_in); CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey>& pubkeys); +UniValue DescribeAddress(const CTxDestination& dest); + #endif // BITCOIN_RPC_UTIL_H diff --git a/src/script/interpreter.h b/src/script/interpreter.h index e12329be76..4dad6b44c5 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -104,7 +104,7 @@ enum // SCRIPT_VERIFY_MINIMALIF = (1U << 13), - // Signature(s) must be empty vector if an CHECK(MULTI)SIG operation failed + // Signature(s) must be empty vector if a CHECK(MULTI)SIG operation failed // SCRIPT_VERIFY_NULLFAIL = (1U << 14), diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index d0dd272550..35d794b983 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -13,16 +13,13 @@ typedef std::vector<unsigned char> valtype; -unsigned int HaveKeys(const std::vector<valtype>& pubkeys, const CKeyStore& keystore) +static bool HaveKeys(const std::vector<valtype>& pubkeys, const CKeyStore& keystore) { - unsigned int nResult = 0; - for (const valtype& pubkey : pubkeys) - { + for (const valtype& pubkey : pubkeys) { CKeyID keyID = CPubKey(pubkey).GetID(); - if (keystore.HaveKey(keyID)) - ++nResult; + if (!keystore.HaveKey(keyID)) return false; } - return nResult; + return true; } isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, SigVersion sigversion) @@ -140,7 +137,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& } } } - if (HaveKeys(keys, keystore) == keys.size()) + if (HaveKeys(keys, keystore)) return ISMINE_SPENDABLE; break; } diff --git a/src/script/ismine.h b/src/script/ismine.h index b54879cc15..c1338c3a8e 100644 --- a/src/script/ismine.h +++ b/src/script/ismine.h @@ -29,7 +29,7 @@ enum isminetype typedef uint8_t isminefilter; /* isInvalid becomes true when the script is found invalid by consensus or policy. This will terminate the recursion - * and return a ISMINE_NO immediately, as an invalid script should never be considered as "mine". This is needed as + * and return ISMINE_NO immediately, as an invalid script should never be considered as "mine". This is needed as * different SIGVERSION may have different network rules. Currently the only use of isInvalid is indicate uncompressed * keys in SIGVERSION_WITNESS_V0 script, but could also be used in similar cases in the future */ diff --git a/src/script/script.h b/src/script/script.h index bedf5f9be5..591777672e 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -568,7 +568,7 @@ public: pc += nSize; } - opcodeRet = (opcodetype)opcode; + opcodeRet = static_cast<opcodetype>(opcode); return true; } diff --git a/src/streams.h b/src/streams.h index 98e25178e1..9f86c4a163 100644 --- a/src/streams.h +++ b/src/streams.h @@ -82,7 +82,7 @@ class CVectorWriter * @param[in] nVersionIn Serialization Version (including any flags) * @param[in] vchDataIn Referenced byte vector to overwrite/append * @param[in] nPosIn Starting position. Vector index where writes should start. The vector will initially - * grow as necessary to max(nPosIn, vec.size()). So to append, use vec.size(). + * grow as necessary to max(nPosIn, vec.size()). So to append, use vec.size(). */ CVectorWriter(int nTypeIn, int nVersionIn, std::vector<unsigned char>& vchDataIn, size_t nPosIn) : nType(nTypeIn), nVersion(nVersionIn), vchData(vchDataIn), nPos(nPosIn) { diff --git a/src/sync.cpp b/src/sync.cpp index ae6e721466..bf3d131e4e 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -25,8 +25,8 @@ void PrintLockContention(const char* pszName, const char* pszFile, int nLine) // // Early deadlock detection. // Problem being solved: -// Thread 1 locks A, then B, then C -// Thread 2 locks D, then C, then A +// Thread 1 locks A, then B, then C +// Thread 2 locks D, then C, then A // --> may result in deadlock between the two threads, depending on when they run. // Solution implemented here: // Keep track of pairs of locks: (A before B), (A before C), etc. diff --git a/src/test/data/script_tests.json b/src/test/data/script_tests.json index 63f43c0fc6..ccefe52246 100644 --- a/src/test/data/script_tests.json +++ b/src/test/data/script_tests.json @@ -168,6 +168,18 @@ ["1 0 BOOLOR", "NOP", "P2SH,STRICTENC", "OK"], ["0 1 BOOLOR", "NOP", "P2SH,STRICTENC", "OK"], ["0 0 BOOLOR", "NOT", "P2SH,STRICTENC", "OK"], +["0x01 0x80", "DUP BOOLOR", "P2SH,STRICTENC", "EVAL_FALSE", "negative-0 negative-0 BOOLOR"], +["0x01 0x00", "DUP BOOLOR", "P2SH,STRICTENC", "EVAL_FALSE", " non-minimal-0 non-minimal-0 BOOLOR"], +["0x01 0x81", "DUP BOOLOR", "P2SH,STRICTENC", "OK", "-1 -1 BOOLOR"], +["0x01 0x80", "DUP BOOLAND", "P2SH,STRICTENC", "EVAL_FALSE", "negative-0 negative-0 BOOLAND"], +["0x01 0x00", "DUP BOOLAND", "P2SH,STRICTENC", "EVAL_FALSE", " non-minimal-0 non-minimal-0 BOOLAND"], +["0x01 0x81", "DUP BOOLAND", "P2SH,STRICTENC", "OK", "-1 -1 BOOLAND"], +["0x01 0x00", "NOT", "P2SH,STRICTENC", "OK", "non-minimal-0 NOT"], +["0x01 0x80", "NOT", "P2SH,STRICTENC", "OK", "negative-0 NOT"], +["0x01 0x81", "NOT", "P2SH,STRICTENC", "EVAL_FALSE", "negative 1 NOT"], +["0x01 0x80 0", "NUMEQUAL", "P2SH", "OK", "-0 0 NUMEQUAL"], +["0x01 0x00 0", "NUMEQUAL", "P2SH", "OK", "non-minimal-0 0 NUMEQUAL"], +["0x02 0x00 0x00 0", "NUMEQUAL", "P2SH", "OK", "non-minimal-0 0 NUMEQUAL"], ["16 17 BOOLOR", "NOP", "P2SH,STRICTENC", "OK"], ["11 10 1 ADD", "NUMEQUAL", "P2SH,STRICTENC", "OK"], ["11 10 1 ADD", "NUMEQUALVERIFY 1", "P2SH,STRICTENC", "OK"], diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json index 09442b7f9f..f8a1347c31 100644 --- a/src/test/data/tx_invalid.json +++ b/src/test/data/tx_invalid.json @@ -92,11 +92,11 @@ [[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a010047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"], -["As above, but using a OP_1"], +["As above, but using an OP_1"], [[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000495147304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"], -["As above, but using a OP_1NEGATE"], +["As above, but using an OP_1NEGATE"], [[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000494f47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"], diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json index a845083636..7e39ec7599 100644 --- a/src/test/data/tx_valid.json +++ b/src/test/data/tx_valid.json @@ -23,11 +23,11 @@ [[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a01ff47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"], -["As above, but using a OP_1"], +["As above, but using an OP_1"], [[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000495147304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"], -["As above, but using a OP_1NEGATE"], +["As above, but using an OP_1NEGATE"], [[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000494f47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"], diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 55d60d95e9..463bed5957 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -13,6 +13,11 @@ #include <stdint.h> #include <vector> +#ifndef WIN32 +#include <signal.h> +#include <sys/types.h> +#include <sys/wait.h> +#endif #include <boost/test/unit_test.hpp> @@ -603,4 +608,130 @@ BOOST_AUTO_TEST_CASE(test_ParseFixedPoint) BOOST_CHECK(!ParseFixedPoint("1.", 8, &amount)); } +static void TestOtherThread(fs::path dirname, std::string lockname, bool *result) +{ + *result = LockDirectory(dirname, lockname); +} + +#ifndef WIN32 // Cannot do this test on WIN32 due to lack of fork() +static constexpr char LockCommand = 'L'; +static constexpr char UnlockCommand = 'U'; +static constexpr char ExitCommand = 'X'; + +static void TestOtherProcess(fs::path dirname, std::string lockname, int fd) +{ + char ch; + int rv; + while (true) { + rv = read(fd, &ch, 1); // Wait for command + assert(rv == 1); + switch(ch) { + case LockCommand: + ch = LockDirectory(dirname, lockname); + rv = write(fd, &ch, 1); + assert(rv == 1); + break; + case UnlockCommand: + ReleaseDirectoryLocks(); + ch = true; // Always succeeds + rv = write(fd, &ch, 1); + break; + case ExitCommand: + close(fd); + exit(0); + default: + assert(0); + } + } +} +#endif + +BOOST_AUTO_TEST_CASE(test_LockDirectory) +{ + fs::path dirname = fs::temp_directory_path() / fs::unique_path(); + const std::string lockname = ".lock"; +#ifndef WIN32 + // Revert SIGCHLD to default, otherwise boost.test will catch and fail on + // it: there is BOOST_TEST_IGNORE_SIGCHLD but that only works when defined + // at build-time of the boost library + void (*old_handler)(int) = signal(SIGCHLD, SIG_DFL); + + // Fork another process for testing before creating the lock, so that we + // won't fork while holding the lock (which might be undefined, and is not + // relevant as test case as that is avoided with -daemonize). + int fd[2]; + BOOST_CHECK_EQUAL(socketpair(AF_UNIX, SOCK_STREAM, 0, fd), 0); + pid_t pid = fork(); + if (!pid) { + BOOST_CHECK_EQUAL(close(fd[1]), 0); // Child: close parent end + TestOtherProcess(dirname, lockname, fd[0]); + } + BOOST_CHECK_EQUAL(close(fd[0]), 0); // Parent: close child end +#endif + // Lock on non-existent directory should fail + BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname), false); + + fs::create_directories(dirname); + + // Probing lock on new directory should succeed + BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true); + + // Persistent lock on new directory should succeed + BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname), true); + + // Another lock on the directory from the same thread should succeed + BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname), true); + + // Another lock on the directory from a different thread within the same process should succeed + bool threadresult; + std::thread thr(TestOtherThread, dirname, lockname, &threadresult); + thr.join(); + BOOST_CHECK_EQUAL(threadresult, true); +#ifndef WIN32 + // Try to aquire lock in child process while we're holding it, this should fail. + char ch; + BOOST_CHECK_EQUAL(write(fd[1], &LockCommand, 1), 1); + BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1); + BOOST_CHECK_EQUAL((bool)ch, false); + + // Give up our lock + ReleaseDirectoryLocks(); + // Probing lock from our side now should succeed, but not hold on to the lock. + BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true); + + // Try to acquire the lock in the child process, this should be succesful. + BOOST_CHECK_EQUAL(write(fd[1], &LockCommand, 1), 1); + BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1); + BOOST_CHECK_EQUAL((bool)ch, true); + + // When we try to probe the lock now, it should fail. + BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), false); + + // Unlock the lock in the child process + BOOST_CHECK_EQUAL(write(fd[1], &UnlockCommand, 1), 1); + BOOST_CHECK_EQUAL(read(fd[1], &ch, 1), 1); + BOOST_CHECK_EQUAL((bool)ch, true); + + // When we try to probe the lock now, it should succeed. + BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true); + + // Re-lock the lock in the child process, then wait for it to exit, check + // successful return. After that, we check that exiting the process + // has released the lock as we would expect by probing it. + int processstatus; + BOOST_CHECK_EQUAL(write(fd[1], &LockCommand, 1), 1); + BOOST_CHECK_EQUAL(write(fd[1], &ExitCommand, 1), 1); + BOOST_CHECK_EQUAL(waitpid(pid, &processstatus, 0), pid); + BOOST_CHECK_EQUAL(processstatus, 0); + BOOST_CHECK_EQUAL(LockDirectory(dirname, lockname, true), true); + + // Restore SIGCHLD + signal(SIGCHLD, old_handler); + BOOST_CHECK_EQUAL(close(fd[1]), 0); // Close our side of the socketpair +#endif + // Clean up + ReleaseDirectoryLocks(); + fs::remove_all(dirname); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp index 12f113655a..5d6f781404 100644 --- a/src/test/versionbits_tests.cpp +++ b/src/test/versionbits_tests.cpp @@ -226,7 +226,7 @@ BOOST_AUTO_TEST_CASE(versionbits_test) const auto chainParams = CreateChainParams(CBaseChainParams::MAIN); const Consensus::Params &mainnetParams = chainParams->GetConsensus(); for (int i=0; i<(int) Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) { - uint32_t bitmask = VersionBitsMask(mainnetParams, (Consensus::DeploymentPos)i); + uint32_t bitmask = VersionBitsMask(mainnetParams, static_cast<Consensus::DeploymentPos>(i)); // Make sure that no deployment tries to set an invalid bit. BOOST_CHECK_EQUAL(bitmask & ~(uint32_t)VERSIONBITS_TOP_MASK, bitmask); @@ -238,7 +238,7 @@ BOOST_AUTO_TEST_CASE(versionbits_test) // activated soft fork could be later changed to be earlier to avoid // overlap.) for (int j=i+1; j<(int) Consensus::MAX_VERSION_BITS_DEPLOYMENTS; j++) { - if (VersionBitsMask(mainnetParams, (Consensus::DeploymentPos)j) == bitmask) { + if (VersionBitsMask(mainnetParams, static_cast<Consensus::DeploymentPos>(j)) == bitmask) { BOOST_CHECK(mainnetParams.vDeployments[j].nStartTime > mainnetParams.vDeployments[i].nTimeout || mainnetParams.vDeployments[i].nStartTime > mainnetParams.vDeployments[j].nTimeout); } diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index c45b5dac0d..717d1cf7e5 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -133,7 +133,7 @@ TorControlConnection::~TorControlConnection() void TorControlConnection::readcb(struct bufferevent *bev, void *ctx) { - TorControlConnection *self = (TorControlConnection*)ctx; + TorControlConnection *self = static_cast<TorControlConnection*>(ctx); struct evbuffer *input = bufferevent_get_input(bev); size_t n_read_out = 0; char *line; @@ -178,7 +178,7 @@ void TorControlConnection::readcb(struct bufferevent *bev, void *ctx) void TorControlConnection::eventcb(struct bufferevent *bev, short what, void *ctx) { - TorControlConnection *self = (TorControlConnection*)ctx; + TorControlConnection *self = static_cast<TorControlConnection*>(ctx); if (what & BEV_EVENT_CONNECTED) { LogPrint(BCLog::TOR, "tor: Successfully connected!\n"); self->connected(*self); @@ -725,13 +725,13 @@ fs::path TorController::GetPrivateKeyFile() void TorController::reconnect_cb(evutil_socket_t fd, short what, void *arg) { - TorController *self = (TorController*)arg; + TorController *self = static_cast<TorController*>(arg); self->Reconnect(); } /****** Thread ********/ static struct event_base *gBase; -static boost::thread torControlThread; +static std::thread torControlThread; static void TorControlThread() { @@ -740,7 +740,7 @@ static void TorControlThread() event_base_dispatch(gBase); } -void StartTorControl(boost::thread_group& threadGroup, CScheduler& scheduler) +void StartTorControl() { assert(!gBase); #ifdef WIN32 @@ -754,7 +754,7 @@ void StartTorControl(boost::thread_group& threadGroup, CScheduler& scheduler) return; } - torControlThread = boost::thread(boost::bind(&TraceThread<void (*)()>, "torcontrol", &TorControlThread)); + torControlThread = std::thread(std::bind(&TraceThread<void (*)()>, "torcontrol", &TorControlThread)); } void InterruptTorControl() diff --git a/src/torcontrol.h b/src/torcontrol.h index 20514f7bbf..2be6701fa5 100644 --- a/src/torcontrol.h +++ b/src/torcontrol.h @@ -13,7 +13,7 @@ extern const std::string DEFAULT_TOR_CONTROL; static const bool DEFAULT_LISTEN_ONION = true; -void StartTorControl(boost::thread_group& threadGroup, CScheduler& scheduler); +void StartTorControl(); void InterruptTorControl(); void StopTorControl(); diff --git a/src/txdb.h b/src/txdb.h index 3edeb4648e..2fc69e563b 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -46,7 +46,7 @@ struct CDiskTxPos : public CDiskBlockPos template <typename Stream, typename Operation> inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(*(CDiskBlockPos*)this); + READWRITE(*static_cast<CDiskBlockPos*>(this)); READWRITE(VARINT(nTxOffset)); } diff --git a/src/txmempool.h b/src/txmempool.h index d6f8e7094b..c6a1bf08ce 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -241,15 +241,18 @@ public: /** \class CompareTxMemPoolEntryByScore * - * Sort by score of entry ((fee+delta)/size) in descending order + * Sort by feerate of entry (fee/size) in descending order + * This is only used for transaction relay, so we use GetFee() + * instead of GetModifiedFee() to avoid leaking prioritization + * information via the sort order. */ class CompareTxMemPoolEntryByScore { public: bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) const { - double f1 = (double)a.GetModifiedFee() * b.GetTxSize(); - double f2 = (double)b.GetModifiedFee() * a.GetTxSize(); + double f1 = (double)a.GetFee() * b.GetTxSize(); + double f2 = (double)b.GetFee() * a.GetTxSize(); if (f1 == f2) { return b.GetTx().GetHash() < a.GetTx().GetHash(); } @@ -379,8 +382,9 @@ public: * * mapTx is a boost::multi_index that sorts the mempool on 4 criteria: * - transaction hash - * - feerate [we use max(feerate of tx, feerate of tx with all descendants)] + * - descendant feerate [we use max(feerate of tx, feerate of tx with all descendants)] * - time in mempool + * - ancestor feerate [we use min(feerate of tx, feerate of tx with all unconfirmed ancestors)] * * Note: the term "descendant" refers to in-mempool transactions that depend on * this one, while "ancestor" refers to in-mempool transactions that a given diff --git a/src/univalue/include/univalue.h b/src/univalue/include/univalue.h index 4fd2223b30..c15b2f051e 100644 --- a/src/univalue/include/univalue.h +++ b/src/univalue/include/univalue.h @@ -130,6 +130,10 @@ public: UniValue tmpVal(val_); return pushKV(key, tmpVal); } + bool pushKV(const std::string& key, bool val_) { + UniValue tmpVal((bool)val_); + return pushKV(key, tmpVal); + } bool pushKV(const std::string& key, int val_) { UniValue tmpVal((int64_t)val_); return pushKV(key, tmpVal); diff --git a/src/univalue/test/object.cpp b/src/univalue/test/object.cpp index 679cc9f143..70ccc0d08a 100644 --- a/src/univalue/test/object.cpp +++ b/src/univalue/test/object.cpp @@ -261,6 +261,12 @@ BOOST_AUTO_TEST_CASE(univalue_object) strKey = "temperature"; BOOST_CHECK(obj.pushKV(strKey, (double) 90.012)); + strKey = "moon"; + BOOST_CHECK(obj.pushKV(strKey, true)); + + strKey = "spoon"; + BOOST_CHECK(obj.pushKV(strKey, false)); + UniValue obj2(UniValue::VOBJ); BOOST_CHECK(obj2.pushKV("cat1", 9000)); BOOST_CHECK(obj2.pushKV("cat2", 12345)); @@ -268,7 +274,7 @@ BOOST_AUTO_TEST_CASE(univalue_object) BOOST_CHECK(obj.pushKVs(obj2)); BOOST_CHECK_EQUAL(obj.empty(), false); - BOOST_CHECK_EQUAL(obj.size(), 9); + BOOST_CHECK_EQUAL(obj.size(), 11); BOOST_CHECK_EQUAL(obj["age"].getValStr(), "100"); BOOST_CHECK_EQUAL(obj["first"].getValStr(), "John"); @@ -277,6 +283,8 @@ BOOST_AUTO_TEST_CASE(univalue_object) BOOST_CHECK_EQUAL(obj["time"].getValStr(), "3600"); BOOST_CHECK_EQUAL(obj["calories"].getValStr(), "12"); BOOST_CHECK_EQUAL(obj["temperature"].getValStr(), "90.012"); + BOOST_CHECK_EQUAL(obj["moon"].getValStr(), "1"); + BOOST_CHECK_EQUAL(obj["spoon"].getValStr(), ""); BOOST_CHECK_EQUAL(obj["cat1"].getValStr(), "9000"); BOOST_CHECK_EQUAL(obj["cat2"].getValStr(), "12345"); @@ -289,6 +297,8 @@ BOOST_AUTO_TEST_CASE(univalue_object) BOOST_CHECK(obj.exists("time")); BOOST_CHECK(obj.exists("calories")); BOOST_CHECK(obj.exists("temperature")); + BOOST_CHECK(obj.exists("moon")); + BOOST_CHECK(obj.exists("spoon")); BOOST_CHECK(obj.exists("cat1")); BOOST_CHECK(obj.exists("cat2")); @@ -302,6 +312,8 @@ BOOST_AUTO_TEST_CASE(univalue_object) objTypes["time"] = UniValue::VNUM; objTypes["calories"] = UniValue::VNUM; objTypes["temperature"] = UniValue::VNUM; + objTypes["moon"] = UniValue::VBOOL; + objTypes["spoon"] = UniValue::VBOOL; objTypes["cat1"] = UniValue::VNUM; objTypes["cat2"] = UniValue::VNUM; BOOST_CHECK(obj.checkObject(objTypes)); diff --git a/src/util.cpp b/src/util.cpp index 80eed24ffd..dcf7ed38b1 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -4,6 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <util.h> +#include <fs.h> #include <chainparamsbase.h> #include <random.h> @@ -188,11 +189,7 @@ static void DebugPrintInit() fs::path GetDebugLogPath() { fs::path logfile(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)); - if (logfile.is_absolute()) { - return logfile; - } else { - return GetDataDir() / logfile; - } + return AbsPathForConfigVal(logfile); } bool OpenDebugLog() @@ -376,20 +373,37 @@ int LogPrintStr(const std::string &str) return ret; } +/** A map that contains all the currently held directory locks. After + * successful locking, these will be held here until the global destructor + * cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks + * is called. + */ +static std::map<std::string, std::unique_ptr<boost::interprocess::file_lock>> dir_locks; +/** Mutex to protect dir_locks. */ +static std::mutex cs_dir_locks; + bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only) { + std::lock_guard<std::mutex> ulock(cs_dir_locks); fs::path pathLockFile = directory / lockfile_name; - FILE* file = fsbridge::fopen(pathLockFile, "a"); // empty lock file; created if it doesn't exist. + + // If a lock for this directory already exists in the map, don't try to re-lock it + if (dir_locks.count(pathLockFile.string())) { + return true; + } + + // Create empty lock file if it doesn't exist. + FILE* file = fsbridge::fopen(pathLockFile, "a"); if (file) fclose(file); try { - static std::map<std::string, boost::interprocess::file_lock> locks; - boost::interprocess::file_lock& lock = locks.emplace(pathLockFile.string(), pathLockFile.string().c_str()).first->second; - if (!lock.try_lock()) { + auto lock = MakeUnique<boost::interprocess::file_lock>(pathLockFile.string().c_str()); + if (!lock->try_lock()) { return false; } - if (probe_only) { - lock.unlock(); + if (!probe_only) { + // Lock successful and we're not just probing, put it into the map + dir_locks.emplace(pathLockFile.string(), std::move(lock)); } } catch (const boost::interprocess::interprocess_exception& e) { return error("Error while attempting to lock directory %s: %s", directory.string(), e.what()); @@ -397,6 +411,12 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b return true; } +void ReleaseDirectoryLocks() +{ + std::lock_guard<std::mutex> ulock(cs_dir_locks); + dir_locks.clear(); +} + /** Interpret string as boolean, for argument parsing */ static bool InterpretBool(const std::string& strValue) { @@ -624,11 +644,7 @@ void ClearDatadirCache() fs::path GetConfigFile(const std::string& confPath) { - fs::path pathConfigFile(confPath); - if (!pathConfigFile.is_complete()) - pathConfigFile = GetDataDir(false) / pathConfigFile; - - return pathConfigFile; + return AbsPathForConfigVal(fs::path(confPath), false); } void ArgsManager::ReadConfigFile(const std::string& confPath) @@ -663,9 +679,7 @@ void ArgsManager::ReadConfigFile(const std::string& confPath) #ifndef WIN32 fs::path GetPidFile() { - fs::path pathPidFile(gArgs.GetArg("-pid", BITCOIN_PID_FILENAME)); - if (!pathPidFile.is_complete()) pathPidFile = GetDataDir() / pathPidFile; - return pathPidFile; + return AbsPathForConfigVal(fs::path(gArgs.GetArg("-pid", BITCOIN_PID_FILENAME))); } void CreatePidFile(const fs::path &path, pid_t pid) @@ -936,3 +950,8 @@ int64_t GetStartupTime() { return nStartupTime; } + +fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific) +{ + return fs::absolute(path, GetDataDir(net_specific)); +} diff --git a/src/util.h b/src/util.h index 277b4c66af..9490a5678f 100644 --- a/src/util.h +++ b/src/util.h @@ -174,6 +174,12 @@ int RaiseFileDescriptorLimit(int nMinFD); void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length); bool RenameOver(fs::path src, fs::path dest); bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only=false); + +/** Release all directory locks. This is used for unit testing only, at runtime + * the global destructor will take care of the locks. + */ +void ReleaseDirectoryLocks(); + bool TryCreateDirectories(const fs::path& p); fs::path GetDefaultDataDir(); const fs::path &GetDataDir(bool fNetSpecific = true); @@ -191,6 +197,16 @@ bool OpenDebugLog(); void ShrinkDebugFile(); void runCommand(const std::string& strCommand); +/** + * Most paths passed as configuration arguments are treated as relative to + * the datadir if they are not absolute. + * + * @param path The path to be conditionally prefixed with datadir. + * @param net_specific Forwarded to GetDataDir(). + * @return The normalized path. + */ +fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific = true); + inline bool IsSwitchChar(char c) { #ifdef WIN32 diff --git a/src/validation.cpp b/src/validation.cpp index 8cee0dfac3..e809f66e25 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -547,8 +547,10 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool const CTransaction& tx = *ptx; const uint256 hash = tx.GetHash(); AssertLockHeld(cs_main); - if (pfMissingInputs) + LOCK(pool.cs); // mempool "read lock" (held through GetMainSignals().TransactionAddedToMempool()) + if (pfMissingInputs) { *pfMissingInputs = false; + } if (!CheckTransaction(tx, state)) return false; // state filled in by CheckTransaction @@ -581,8 +583,6 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // Check for conflicts with in-memory transactions std::set<uint256> setConflicts; - { - LOCK(pool.cs); // protect pool.mapNextTx for (const CTxIn &txin : tx.vin) { auto itConflicting = pool.mapNextTx.find(txin.prevout); @@ -623,15 +623,12 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool } } } - } { CCoinsView dummy; CCoinsViewCache view(&dummy); LockPoints lp; - { - LOCK(pool.cs); CCoinsViewMemPool viewMemPool(pcoinsTip.get(), pool); view.SetBackend(viewMemPool); @@ -670,8 +667,6 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool if (!CheckSequenceLocks(tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp)) return state.DoS(0, false, REJECT_NONSTANDARD, "non-BIP68-final"); - } // end LOCK(pool.cs) - CAmount nFees = 0; if (!Consensus::CheckTxInputs(tx, state, view, GetSpendHeight(view), nFees)) { return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state)); @@ -717,7 +712,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool CAmount mempoolRejectFee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); if (!bypass_limits && mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) { - return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee)); + return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nModifiedFees, mempoolRejectFee)); } // No transactions are allowed below minRelayTxFee except from disconnected blocks @@ -768,7 +763,6 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // If we don't hold the lock allConflicting might be incomplete; the // subsequent RemoveStaged() and addUnchecked() calls don't guarantee // mempool consistency for us. - LOCK(pool.cs); const bool fReplacementTransaction = setConflicts.size(); if (fReplacementTransaction) { @@ -1188,7 +1182,8 @@ static void AlertNotify(const std::string& strMessage) safeStatus = singleQuote+safeStatus+singleQuote; boost::replace_all(strCmd, "%s", safeStatus); - boost::thread t(runCommand, strCmd); // thread runs free + std::thread t(runCommand, strCmd); + t.detach(); // thread runs free } static void CheckForkWarningConditions() @@ -1689,9 +1684,9 @@ int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Para int32_t nVersion = VERSIONBITS_TOP_BITS; for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) { - ThresholdState state = VersionBitsState(pindexPrev, params, (Consensus::DeploymentPos)i, versionbitscache); + ThresholdState state = VersionBitsState(pindexPrev, params, static_cast<Consensus::DeploymentPos>(i), versionbitscache); if (state == THRESHOLD_LOCKED_IN || state == THRESHOLD_STARTED) { - nVersion |= VersionBitsMask(params, (Consensus::DeploymentPos)i); + nVersion |= VersionBitsMask(params, static_cast<Consensus::DeploymentPos>(i)); } } @@ -2092,7 +2087,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState & nLastWrite = nNow; } // Flush best chain related state. This can only be done if the blocks / block index write was also done. - if (fDoFullFlush) { + if (fDoFullFlush && !pcoinsTip->GetBestBlock().IsNull()) { // Typical Coin structures on disk are around 48 bytes in size. // Pushing a new one to the database can cause it to be written // twice (once in the log, and once in the tables). This is already @@ -2577,13 +2572,10 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& if (GetMainSignals().CallbacksPending() > 10) { // Block until the validation queue drains. This should largely // never happen in normal operation, however may happen during - // reindex, causing memory blowup if we run too far ahead. + // reindex, causing memory blowup if we run too far ahead. SyncWithValidationInterfaceQueue(); } - if (ShutdownRequested()) - break; - const CBlockIndex *pindexFork; bool fInitialDownload; { @@ -2630,6 +2622,13 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& } if (nStopAtHeight && pindexNewTip && pindexNewTip->nHeight >= nStopAtHeight) StartShutdown(); + + // We check shutdown only after giving ActivateBestChainStep a chance to run once so that we + // never shutdown before connecting the genesis block during LoadChainTip(). Previously this + // caused an assert() failure during shutdown in such cases as the UTXO DB flushing checks + // that the best block hash is non-null. + if (ShutdownRequested()) + break; } while (pindexNewTip != pindexMostWork); CheckBlockIndex(chainparams.GetConsensus()); diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 9bfcab54a5..9cae660c60 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -65,16 +65,39 @@ static feebumper::Result PreconditionChecks(const CWallet* wallet, const CWallet errors.push_back("Transaction has been mined, or is conflicted with a mined transaction"); return feebumper::Result::WALLET_ERROR; } + + if (!SignalsOptInRBF(*wtx.tx)) { + errors.push_back("Transaction is not BIP 125 replaceable"); + return feebumper::Result::WALLET_ERROR; + } + + if (wtx.mapValue.count("replaced_by_txid")) { + errors.push_back(strprintf("Cannot bump transaction %s which was already bumped by transaction %s", wtx.GetHash().ToString(), wtx.mapValue.at("replaced_by_txid"))); + return feebumper::Result::WALLET_ERROR; + } + + // check that original tx consists entirely of our inputs + // if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee) + if (!wallet->IsAllFromMe(*wtx.tx, ISMINE_SPENDABLE)) { + errors.push_back("Transaction contains inputs that don't belong to this wallet"); + return feebumper::Result::WALLET_ERROR; + } + + return feebumper::Result::OK; } namespace feebumper { -bool TransactionCanBeBumped(CWallet* wallet, const uint256& txid) +bool TransactionCanBeBumped(const CWallet* wallet, const uint256& txid) { LOCK2(cs_main, wallet->cs_wallet); const CWalletTx* wtx = wallet->GetWalletTx(txid); - return wtx && SignalsOptInRBF(*wtx->tx) && !wtx->mapValue.count("replaced_by_txid"); + if (wtx == nullptr) return false; + + std::vector<std::string> errors_dummy; + feebumper::Result res = PreconditionChecks(wallet, *wtx, errors_dummy); + return res == feebumper::Result::OK; } Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoinControl& coin_control, CAmount total_fee, std::vector<std::string>& errors, @@ -94,23 +117,6 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin return result; } - if (!SignalsOptInRBF(*wtx.tx)) { - errors.push_back("Transaction is not BIP 125 replaceable"); - return Result::WALLET_ERROR; - } - - if (wtx.mapValue.count("replaced_by_txid")) { - errors.push_back(strprintf("Cannot bump transaction %s which was already bumped by transaction %s", txid.ToString(), wtx.mapValue.at("replaced_by_txid"))); - return Result::WALLET_ERROR; - } - - // check that original tx consists entirely of our inputs - // if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee) - if (!wallet->IsAllFromMe(*wtx.tx, ISMINE_SPENDABLE)) { - errors.push_back("Transaction contains inputs that don't belong to this wallet"); - return Result::WALLET_ERROR; - } - // figure out which output was change // if there was no change output or multiple change outputs, fail int nOutput = -1; @@ -228,6 +234,7 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin } } + return Result::OK; } @@ -267,7 +274,7 @@ Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransacti CValidationState state; if (!wallet->CommitTransaction(wtxBumped, reservekey, g_connman.get(), state)) { // NOTE: CommitTransaction never returns false, so this should never happen. - errors.push_back(strprintf("The transaction was rejected: %s", state.GetRejectReason())); + errors.push_back(strprintf("The transaction was rejected: %s", FormatStateMessage(state))); return Result::WALLET_ERROR; } @@ -290,4 +297,3 @@ Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransacti } } // namespace feebumper - diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h index 8eec30440c..7e36a9766b 100644 --- a/src/wallet/feebumper.h +++ b/src/wallet/feebumper.h @@ -26,7 +26,7 @@ enum class Result }; //! Return whether transaction can be bumped. -bool TransactionCanBeBumped(CWallet* wallet, const uint256& txid); +bool TransactionCanBeBumped(const CWallet* wallet, const uint256& txid); //! Create bumpfee transaction. Result CreateTransaction(const CWallet* wallet, diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index ace95204b8..74036f4f0f 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -19,12 +19,12 @@ std::string GetWalletHelpString(bool showDebug) strUsage += HelpMessageOpt("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default: \"%s\")", FormatOutputType(OUTPUT_TYPE_DEFAULT))); strUsage += HelpMessageOpt("-changetype", "What type of change to use (\"legacy\", \"p2sh-segwit\", or \"bech32\"). Default is same as -addresstype, except when -addresstype=p2sh-segwit a native segwit output is used when sending to a native segwit address)"); strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls")); - 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) that indicates your tolerance for discarding change by adding it to the fee (default: %s). " "Note: An output is discarded if it is dust at this rate, but we will always discard up to the dust relay fee and a discard fee above that is limited by the fee estimate for the longest target"), CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_FEE))); + 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("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), DEFAULT_KEYPOOL_SIZE)); 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)"), @@ -33,12 +33,12 @@ std::string GetWalletHelpString(bool showDebug) strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup")); strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); strUsage += HelpMessageOpt("-txconfirmtarget=<n>", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET)); - strUsage += HelpMessageOpt("-walletrbf", strprintf(_("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)"), DEFAULT_WALLET_RBF)); strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup")); strUsage += HelpMessageOpt("-wallet=<file>", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST)); strUsage += HelpMessageOpt("-walletdir=<dir>", _("Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)")); strUsage += HelpMessageOpt("-walletnotify=<cmd>", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)")); + strUsage += HelpMessageOpt("-walletrbf", strprintf(_("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)"), DEFAULT_WALLET_RBF)); strUsage += HelpMessageOpt("-zapwallettxes=<mode>", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") + " " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)")); diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 0b021f9fe0..741ea25340 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -71,6 +71,28 @@ std::string DecodeDumpString(const std::string &str) { return ret.str(); } +bool GetWalletAddressesForKey(CWallet * const pwallet, const CKeyID &keyid, std::string &strAddr, std::string &strLabel) +{ + bool fLabelFound = false; + CKey key; + pwallet->GetKey(keyid, key); + for (const auto& dest : GetAllDestinationsForKey(key.GetPubKey())) { + if (pwallet->mapAddressBook.count(dest)) { + if (!strAddr.empty()) { + strAddr += ","; + } + strAddr += EncodeDestination(dest); + strLabel = EncodeDumpString(pwallet->mapAddressBook[dest].name); + fLabelFound = true; + } + } + if (!fLabelFound) { + strAddr = EncodeDestination(GetDestinationForKey(key.GetPubKey(), g_address_type)); + } + return fLabelFound; +} + + UniValue importprivkey(const JSONRPCRequest& request) { CWallet * const pwallet = GetWalletForJSONRPCRequest(request); @@ -729,12 +751,13 @@ UniValue dumpwallet(const JSONRPCRequest& request) for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) { const CKeyID &keyid = it->second; std::string strTime = EncodeDumpTime(it->first); - std::string strAddr = EncodeDestination(keyid); + std::string strAddr; + std::string strLabel; CKey key; if (pwallet->GetKey(keyid, key)) { file << strprintf("%s %s ", CBitcoinSecret(key).ToString(), strTime); - if (pwallet->mapAddressBook.count(keyid)) { - file << strprintf("label=%s", EncodeDumpString(pwallet->mapAddressBook[keyid].name)); + if (GetWalletAddressesForKey(pwallet, keyid, strAddr, strLabel)) { + file << strprintf("label=%s", strLabel); } else if (keyid == masterKeyID) { file << "hdmaster=1"; } else if (mapKeyPool.count(keyid)) { @@ -767,7 +790,7 @@ UniValue dumpwallet(const JSONRPCRequest& request) file.close(); UniValue reply(UniValue::VOBJ); - reply.push_back(Pair("filename", filepath.string())); + reply.pushKV("filename", filepath.string()); return reply; } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 9364d18d30..b504b84274 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -25,6 +25,7 @@ #include <utilmoneystr.h> #include <wallet/coincontrol.h> #include <wallet/feebumper.h> +#include <wallet/rpcwallet.h> #include <wallet/wallet.h> #include <wallet/walletdb.h> #include <wallet/walletutil.h> @@ -86,25 +87,25 @@ void EnsureWalletIsUnlocked(CWallet * const pwallet) void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) { int confirms = wtx.GetDepthInMainChain(); - entry.push_back(Pair("confirmations", confirms)); + entry.pushKV("confirmations", confirms); if (wtx.IsCoinBase()) - entry.push_back(Pair("generated", true)); + entry.pushKV("generated", true); if (confirms > 0) { - entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex())); - entry.push_back(Pair("blockindex", wtx.nIndex)); - entry.push_back(Pair("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime())); + entry.pushKV("blockhash", wtx.hashBlock.GetHex()); + entry.pushKV("blockindex", wtx.nIndex); + entry.pushKV("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime()); } else { - entry.push_back(Pair("trusted", wtx.IsTrusted())); + entry.pushKV("trusted", wtx.IsTrusted()); } uint256 hash = wtx.GetHash(); - entry.push_back(Pair("txid", hash.GetHex())); + entry.pushKV("txid", hash.GetHex()); UniValue conflicts(UniValue::VARR); for (const uint256& conflict : wtx.GetConflicts()) conflicts.push_back(conflict.GetHex()); - entry.push_back(Pair("walletconflicts", conflicts)); - entry.push_back(Pair("time", wtx.GetTxTime())); - entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived)); + entry.pushKV("walletconflicts", conflicts); + entry.pushKV("time", wtx.GetTxTime()); + entry.pushKV("timereceived", (int64_t)wtx.nTimeReceived); // Add opt-in RBF status std::string rbfStatus = "no"; @@ -116,10 +117,10 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) else if (rbfState == RBF_TRANSACTIONSTATE_REPLACEABLE_BIP125) rbfStatus = "yes"; } - entry.push_back(Pair("bip125-replaceable", rbfStatus)); + entry.pushKV("bip125-replaceable", rbfStatus); for (const std::pair<std::string, std::string>& item : wtx.mapValue) - entry.push_back(Pair(item.first, item.second)); + entry.pushKV(item.first, item.second); } std::string AccountFromValue(const UniValue& value) @@ -435,7 +436,7 @@ static void SendMoney(CWallet * const pwallet, const CTxDestination &address, CA } CValidationState state; if (!pwallet->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) { - strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); + strError = strprintf("Error: The transaction was rejected! Reason given: %s", FormatStateMessage(state)); throw JSONRPCError(RPC_WALLET_ERROR, strError); } } @@ -1155,7 +1156,7 @@ UniValue sendmany(const JSONRPCRequest& request) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); CValidationState state; if (!pwallet->CommitTransaction(wtx, keyChange, g_connman.get(), state)) { - strFailReason = strprintf("Transaction commit failed:: %s", state.GetRejectReason()); + strFailReason = strprintf("Transaction commit failed:: %s", FormatStateMessage(state)); throw JSONRPCError(RPC_WALLET_ERROR, strFailReason); } @@ -1192,10 +1193,6 @@ UniValue addmultisigaddress(const JSONRPCRequest& request) " \"address\":\"multisigaddress\", (string) The value of the new multisig address.\n" " \"redeemScript\":\"script\" (string) The string value of the hex-encoded redemption script.\n" "}\n" - "\nResult (DEPRECATED. To see this result in v0.16 instead, please start bitcoind with -deprecatedrpc=addmultisigaddress).\n" - " clients should transition to the new output api before upgrading to v0.17.\n" - "\"address\" (string) A bitcoin address associated with the keys.\n" - "\nExamples:\n" "\nAdd a multisig address from 2 addresses\n" + HelpExampleCli("addmultisigaddress", "2 \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + @@ -1238,11 +1235,6 @@ UniValue addmultisigaddress(const JSONRPCRequest& request) CTxDestination dest = pwallet->AddAndGetDestinationForScript(inner, output_type); pwallet->SetAddressBook(dest, strAccount, "send"); - // Return old style interface - if (IsDeprecatedRPCEnabled("addmultisigaddress")) { - return EncodeDestination(dest); - } - UniValue result(UniValue::VOBJ); result.pushKV("address", EncodeDestination(dest)); result.pushKV("redeemScript", HexStr(inner.begin(), inner.end())); @@ -1472,13 +1464,13 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA { UniValue obj(UniValue::VOBJ); if(fIsWatchonly) - obj.push_back(Pair("involvesWatchonly", true)); - obj.push_back(Pair("address", EncodeDestination(dest))); - obj.push_back(Pair("account", strAccount)); - obj.push_back(Pair("amount", ValueFromAmount(nAmount))); - obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf))); + obj.pushKV("involvesWatchonly", true); + obj.pushKV("address", EncodeDestination(dest)); + obj.pushKV("account", strAccount); + obj.pushKV("amount", ValueFromAmount(nAmount)); + obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)); if (!fByAccounts) - obj.push_back(Pair("label", strAccount)); + obj.pushKV("label", strAccount); UniValue transactions(UniValue::VARR); if (it != mapTally.end()) { @@ -1487,7 +1479,7 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA transactions.push_back(_item.GetHex()); } } - obj.push_back(Pair("txids", transactions)); + obj.pushKV("txids", transactions); ret.push_back(obj); } } @@ -1500,10 +1492,10 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA int nConf = entry.second.nConf; UniValue obj(UniValue::VOBJ); if (entry.second.fIsWatchonly) - obj.push_back(Pair("involvesWatchonly", true)); - obj.push_back(Pair("account", entry.first)); - obj.push_back(Pair("amount", ValueFromAmount(nAmount))); - obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf))); + obj.pushKV("involvesWatchonly", true); + obj.pushKV("account", entry.first); + obj.pushKV("amount", ValueFromAmount(nAmount)); + obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)); ret.push_back(obj); } } @@ -1609,7 +1601,7 @@ UniValue listreceivedbyaccount(const JSONRPCRequest& request) static void MaybePushAddress(UniValue & entry, const CTxDestination &dest) { if (IsValidDestination(dest)) { - entry.push_back(Pair("address", EncodeDestination(dest))); + entry.pushKV("address", EncodeDestination(dest)); } } @@ -1643,20 +1635,20 @@ void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, const std::s { UniValue entry(UniValue::VOBJ); if (involvesWatchonly || (::IsMine(*pwallet, s.destination) & ISMINE_WATCH_ONLY)) { - entry.push_back(Pair("involvesWatchonly", true)); + entry.pushKV("involvesWatchonly", true); } - entry.push_back(Pair("account", strSentAccount)); + entry.pushKV("account", strSentAccount); MaybePushAddress(entry, s.destination); - entry.push_back(Pair("category", "send")); - entry.push_back(Pair("amount", ValueFromAmount(-s.amount))); + entry.pushKV("category", "send"); + entry.pushKV("amount", ValueFromAmount(-s.amount)); if (pwallet->mapAddressBook.count(s.destination)) { - entry.push_back(Pair("label", pwallet->mapAddressBook[s.destination].name)); + entry.pushKV("label", pwallet->mapAddressBook[s.destination].name); } - entry.push_back(Pair("vout", s.vout)); - entry.push_back(Pair("fee", ValueFromAmount(-nFee))); + entry.pushKV("vout", s.vout); + entry.pushKV("fee", ValueFromAmount(-nFee)); if (fLong) WalletTxToJSON(wtx, entry); - entry.push_back(Pair("abandoned", wtx.isAbandoned())); + entry.pushKV("abandoned", wtx.isAbandoned()); ret.push_back(entry); } } @@ -1674,28 +1666,28 @@ void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, const std::s { UniValue entry(UniValue::VOBJ); if (involvesWatchonly || (::IsMine(*pwallet, r.destination) & ISMINE_WATCH_ONLY)) { - entry.push_back(Pair("involvesWatchonly", true)); + entry.pushKV("involvesWatchonly", true); } - entry.push_back(Pair("account", account)); + entry.pushKV("account", account); MaybePushAddress(entry, r.destination); if (wtx.IsCoinBase()) { if (wtx.GetDepthInMainChain() < 1) - entry.push_back(Pair("category", "orphan")); + entry.pushKV("category", "orphan"); else if (wtx.GetBlocksToMaturity() > 0) - entry.push_back(Pair("category", "immature")); + entry.pushKV("category", "immature"); else - entry.push_back(Pair("category", "generate")); + entry.pushKV("category", "generate"); } else { - entry.push_back(Pair("category", "receive")); + entry.pushKV("category", "receive"); } - entry.push_back(Pair("amount", ValueFromAmount(r.amount))); + entry.pushKV("amount", ValueFromAmount(r.amount)); if (pwallet->mapAddressBook.count(r.destination)) { - entry.push_back(Pair("label", account)); + entry.pushKV("label", account); } - entry.push_back(Pair("vout", r.vout)); + entry.pushKV("vout", r.vout); if (fLong) WalletTxToJSON(wtx, entry); ret.push_back(entry); @@ -1711,12 +1703,12 @@ void AcentryToJSON(const CAccountingEntry& acentry, const std::string& strAccoun if (fAllAccounts || acentry.strAccount == strAccount) { UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("account", acentry.strAccount)); - entry.push_back(Pair("category", "move")); - entry.push_back(Pair("time", acentry.nTime)); - entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit))); - entry.push_back(Pair("otheraccount", acentry.strOtherAccount)); - entry.push_back(Pair("comment", acentry.strComment)); + entry.pushKV("account", acentry.strAccount); + entry.pushKV("category", "move"); + entry.pushKV("time", acentry.nTime); + entry.pushKV("amount", ValueFromAmount(acentry.nCreditDebit)); + entry.pushKV("otheraccount", acentry.strOtherAccount); + entry.pushKV("comment", acentry.strComment); ret.push_back(entry); } } @@ -1794,8 +1786,6 @@ UniValue listtransactions(const JSONRPCRequest& request) // the user could have gotten from another RPC command prior to now pwallet->BlockUntilSyncedToCurrentChain(); - LOCK2(cs_main, pwallet->cs_wallet); - std::string strAccount = "*"; if (!request.params[0].isNull()) strAccount = request.params[0].get_str(); @@ -1817,20 +1807,25 @@ UniValue listtransactions(const JSONRPCRequest& request) UniValue ret(UniValue::VARR); - const CWallet::TxItems & txOrdered = pwallet->wtxOrdered; - - // iterate backwards until we have nCount items to return: - for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) { - CWalletTx *const pwtx = (*it).second.first; - if (pwtx != nullptr) - ListTransactions(pwallet, *pwtx, strAccount, 0, true, ret, filter); - CAccountingEntry *const pacentry = (*it).second.second; - if (pacentry != nullptr) - AcentryToJSON(*pacentry, strAccount, ret); + LOCK2(cs_main, pwallet->cs_wallet); - if ((int)ret.size() >= (nCount+nFrom)) break; + const CWallet::TxItems & txOrdered = pwallet->wtxOrdered; + + // iterate backwards until we have nCount items to return: + for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) + { + CWalletTx *const pwtx = (*it).second.first; + if (pwtx != nullptr) + ListTransactions(pwallet, *pwtx, strAccount, 0, true, ret, filter); + CAccountingEntry *const pacentry = (*it).second.second; + if (pacentry != nullptr) + AcentryToJSON(*pacentry, strAccount, ret); + + if ((int)ret.size() >= (nCount+nFrom)) break; + } } + // ret is newest to oldest if (nFrom > (int)ret.size()) @@ -1940,7 +1935,7 @@ UniValue listaccounts(const JSONRPCRequest& request) UniValue ret(UniValue::VOBJ); for (const std::pair<std::string, CAmount>& accountBalance : mapAccountBalances) { - ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); + ret.pushKV(accountBalance.first, ValueFromAmount(accountBalance.second)); } return ret; } @@ -2080,9 +2075,9 @@ UniValue listsinceblock(const JSONRPCRequest& request) uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : uint256(); UniValue ret(UniValue::VOBJ); - ret.push_back(Pair("transactions", transactions)); - if (include_removed) ret.push_back(Pair("removed", removed)); - ret.push_back(Pair("lastblock", lastblock.GetHex())); + ret.pushKV("transactions", transactions); + if (include_removed) ret.pushKV("removed", removed); + ret.pushKV("lastblock", lastblock.GetHex()); return ret; } @@ -2167,18 +2162,18 @@ UniValue gettransaction(const JSONRPCRequest& request) CAmount nNet = nCredit - nDebit; CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0); - entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); + entry.pushKV("amount", ValueFromAmount(nNet - nFee)); if (wtx.IsFromMe(filter)) - entry.push_back(Pair("fee", ValueFromAmount(nFee))); + entry.pushKV("fee", ValueFromAmount(nFee)); WalletTxToJSON(wtx, entry); UniValue details(UniValue::VARR); ListTransactions(pwallet, wtx, "*", 0, false, details, filter); - entry.push_back(Pair("details", details)); + entry.pushKV("details", details); std::string strHex = EncodeHexTx(*wtx.tx, RPCSerializationFlags()); - entry.push_back(Pair("hex", strHex)); + entry.pushKV("hex", strHex); return entry; } @@ -2190,14 +2185,14 @@ UniValue abandontransaction(const JSONRPCRequest& request) return NullUniValue; } - if (request.fHelp || request.params.size() != 1) + if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "abandontransaction \"txid\"\n" "\nMark in-wallet transaction <txid> as abandoned\n" "This will mark this transaction and all its in-wallet descendants as abandoned which will allow\n" "for their inputs to be respent. It can be used to replace \"stuck\" or evicted transactions.\n" "It only works on transactions which are not included in a block and are not currently in the mempool.\n" - "It has no effect on transactions which are already conflicted or abandoned.\n" + "It has no effect on transactions which are already abandoned.\n" "\nArguments:\n" "1. \"txid\" (string, required) The transaction id\n" "\nResult:\n" @@ -2205,6 +2200,7 @@ UniValue abandontransaction(const JSONRPCRequest& request) + HelpExampleCli("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + HelpExampleRpc("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") ); + } ObserveSafeMode(); @@ -2707,8 +2703,8 @@ UniValue listlockunspent(const JSONRPCRequest& request) for (COutPoint &outpt : vOutpts) { UniValue o(UniValue::VOBJ); - o.push_back(Pair("txid", outpt.hash.GetHex())); - o.push_back(Pair("vout", (int)outpt.n)); + o.pushKV("txid", outpt.hash.GetHex()); + o.pushKV("vout", (int)outpt.n); ret.push_back(o); } @@ -2786,24 +2782,24 @@ UniValue getwalletinfo(const JSONRPCRequest& request) UniValue obj(UniValue::VOBJ); size_t kpExternalSize = pwallet->KeypoolCountExternalKeys(); - obj.push_back(Pair("walletname", pwallet->GetName())); - obj.push_back(Pair("walletversion", pwallet->GetVersion())); - obj.push_back(Pair("balance", ValueFromAmount(pwallet->GetBalance()))); - obj.push_back(Pair("unconfirmed_balance", ValueFromAmount(pwallet->GetUnconfirmedBalance()))); - obj.push_back(Pair("immature_balance", ValueFromAmount(pwallet->GetImmatureBalance()))); - obj.push_back(Pair("txcount", (int)pwallet->mapWallet.size())); - obj.push_back(Pair("keypoololdest", pwallet->GetOldestKeyPoolTime())); - obj.push_back(Pair("keypoolsize", (int64_t)kpExternalSize)); + obj.pushKV("walletname", pwallet->GetName()); + obj.pushKV("walletversion", pwallet->GetVersion()); + obj.pushKV("balance", ValueFromAmount(pwallet->GetBalance())); + obj.pushKV("unconfirmed_balance", ValueFromAmount(pwallet->GetUnconfirmedBalance())); + obj.pushKV("immature_balance", ValueFromAmount(pwallet->GetImmatureBalance())); + obj.pushKV("txcount", (int)pwallet->mapWallet.size()); + obj.pushKV("keypoololdest", pwallet->GetOldestKeyPoolTime()); + obj.pushKV("keypoolsize", (int64_t)kpExternalSize); CKeyID masterKeyID = pwallet->GetHDChain().masterKeyID; if (!masterKeyID.IsNull() && pwallet->CanSupportFeature(FEATURE_HD_SPLIT)) { - obj.push_back(Pair("keypoolsize_hd_internal", (int64_t)(pwallet->GetKeyPoolSize() - kpExternalSize))); + obj.pushKV("keypoolsize_hd_internal", (int64_t)(pwallet->GetKeyPoolSize() - kpExternalSize)); } if (pwallet->IsCrypted()) { - obj.push_back(Pair("unlocked_until", pwallet->nRelockTime)); + obj.pushKV("unlocked_until", pwallet->nRelockTime); } - obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK()))); + obj.pushKV("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())); if (!masterKeyID.IsNull()) - obj.push_back(Pair("hdmasterkeyid", masterKeyID.GetHex())); + obj.pushKV("hdmasterkeyid", masterKeyID.GetHex()); return obj; } @@ -3008,31 +3004,31 @@ UniValue listunspent(const JSONRPCRequest& request) continue; UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); - entry.push_back(Pair("vout", out.i)); + entry.pushKV("txid", out.tx->GetHash().GetHex()); + entry.pushKV("vout", out.i); if (fValidAddress) { - entry.push_back(Pair("address", EncodeDestination(address))); + entry.pushKV("address", EncodeDestination(address)); if (pwallet->mapAddressBook.count(address)) { - entry.push_back(Pair("account", pwallet->mapAddressBook[address].name)); + entry.pushKV("account", pwallet->mapAddressBook[address].name); } if (scriptPubKey.IsPayToScriptHash()) { const CScriptID& hash = boost::get<CScriptID>(address); CScript redeemScript; if (pwallet->GetCScript(hash, redeemScript)) { - entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); + entry.pushKV("redeemScript", HexStr(redeemScript.begin(), redeemScript.end())); } } } - entry.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); - entry.push_back(Pair("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue))); - entry.push_back(Pair("confirmations", out.nDepth)); - entry.push_back(Pair("spendable", out.fSpendable)); - entry.push_back(Pair("solvable", out.fSolvable)); - entry.push_back(Pair("safe", out.fSafe)); + entry.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())); + entry.pushKV("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue)); + entry.pushKV("confirmations", out.nDepth); + entry.pushKV("spendable", out.fSpendable); + entry.pushKV("solvable", out.fSolvable); + entry.pushKV("safe", out.fSafe); results.push_back(entry); } @@ -3134,7 +3130,6 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) {"change_type", UniValueType(UniValue::VSTR)}, {"includeWatching", UniValueType(UniValue::VBOOL)}, {"lockUnspents", 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)}, @@ -3234,9 +3229,9 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) } UniValue result(UniValue::VOBJ); - result.push_back(Pair("hex", EncodeHexTx(tx))); - result.push_back(Pair("changepos", changePosition)); - result.push_back(Pair("fee", ValueFromAmount(nFeeOut))); + result.pushKV("hex", EncodeHexTx(tx)); + result.pushKV("changepos", changePosition); + result.pushKV("fee", ValueFromAmount(nFeeOut)); return result; } @@ -3257,8 +3252,8 @@ UniValue bumpfee(const JSONRPCRequest& request) "If the change output is not big enough to cover the increased fee, the command will currently fail\n" "instead of adding new inputs to compensate. (A future implementation could improve this.)\n" "The command will fail if the wallet or mempool contains a transaction that spends one of T's outputs.\n" - "By default, the new fee will be calculated automatically using estimatefee.\n" - "The user can specify a confirmation target for estimatefee.\n" + "By default, the new fee will be calculated automatically using estimatesmartfee.\n" + "The user can specify a confirmation target for estimatesmartfee.\n" "Alternatively, the user can specify totalFee, or use RPC settxfee to set a higher fee rate.\n" "At a minimum, the new fee rate must be high enough to pay an additional new relay fee (incrementalfee\n" "returned by getnetworkinfo) to enter the node's mempool.\n" @@ -3378,14 +3373,14 @@ UniValue bumpfee(const JSONRPCRequest& request) throw JSONRPCError(RPC_WALLET_ERROR, errors[0]); } UniValue result(UniValue::VOBJ); - result.push_back(Pair("txid", txid.GetHex())); - result.push_back(Pair("origfee", ValueFromAmount(old_fee))); - result.push_back(Pair("fee", ValueFromAmount(new_fee))); + result.pushKV("txid", txid.GetHex()); + result.pushKV("origfee", ValueFromAmount(old_fee)); + result.pushKV("fee", ValueFromAmount(new_fee)); UniValue result_errors(UniValue::VARR); for (const std::string& error : errors) { result_errors.push_back(error); } - result.push_back(Pair("errors", result_errors)); + result.pushKV("errors", result_errors); return result; } @@ -3520,6 +3515,209 @@ UniValue rescanblockchain(const JSONRPCRequest& request) return response; } +class DescribeWalletAddressVisitor : public boost::static_visitor<UniValue> +{ +public: + CWallet * const pwallet; + + void ProcessSubScript(const CScript& subscript, UniValue& obj, bool include_addresses = false) const + { + // Always present: script type and redeemscript + txnouttype which_type; + std::vector<std::vector<unsigned char>> solutions_data; + Solver(subscript, which_type, solutions_data); + obj.pushKV("script", GetTxnOutputType(which_type)); + obj.pushKV("hex", HexStr(subscript.begin(), subscript.end())); + + CTxDestination embedded; + UniValue a(UniValue::VARR); + if (ExtractDestination(subscript, embedded)) { + // Only when the script corresponds to an address. + UniValue subobj(UniValue::VOBJ); + UniValue detail = DescribeAddress(embedded); + subobj.pushKVs(detail); + UniValue wallet_detail = boost::apply_visitor(*this, embedded); + subobj.pushKVs(wallet_detail); + subobj.pushKV("address", EncodeDestination(embedded)); + subobj.pushKV("scriptPubKey", HexStr(subscript.begin(), subscript.end())); + // Always report the pubkey at the top level, so that `getnewaddress()['pubkey']` always works. + if (subobj.exists("pubkey")) obj.pushKV("pubkey", subobj["pubkey"]); + obj.pushKV("embedded", std::move(subobj)); + if (include_addresses) a.push_back(EncodeDestination(embedded)); + } else if (which_type == TX_MULTISIG) { + // Also report some information on multisig scripts (which do not have a corresponding address). + // TODO: abstract out the common functionality between this logic and ExtractDestinations. + obj.pushKV("sigsrequired", solutions_data[0][0]); + UniValue pubkeys(UniValue::VARR); + for (size_t i = 1; i < solutions_data.size() - 1; ++i) { + CPubKey key(solutions_data[i].begin(), solutions_data[i].end()); + if (include_addresses) a.push_back(EncodeDestination(key.GetID())); + pubkeys.push_back(HexStr(key.begin(), key.end())); + } + obj.pushKV("pubkeys", std::move(pubkeys)); + } + + // The "addresses" field is confusing because it refers to public keys using their P2PKH address. + // For that reason, only add the 'addresses' field when needed for backward compatibility. New applications + // can use the 'embedded'->'address' field for P2SH or P2WSH wrapped addresses, and 'pubkeys' for + // inspecting multisig participants. + if (include_addresses) obj.pushKV("addresses", std::move(a)); + } + + explicit DescribeWalletAddressVisitor(CWallet* _pwallet) : pwallet(_pwallet) {} + + UniValue operator()(const CNoDestination& dest) const { return UniValue(UniValue::VOBJ); } + + UniValue operator()(const CKeyID& keyID) const + { + UniValue obj(UniValue::VOBJ); + CPubKey vchPubKey; + if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) { + obj.pushKV("pubkey", HexStr(vchPubKey)); + obj.pushKV("iscompressed", vchPubKey.IsCompressed()); + } + return obj; + } + + UniValue operator()(const CScriptID& scriptID) const + { + UniValue obj(UniValue::VOBJ); + CScript subscript; + if (pwallet && pwallet->GetCScript(scriptID, subscript)) { + ProcessSubScript(subscript, obj, IsDeprecatedRPCEnabled("validateaddress")); + } + return obj; + } + + UniValue operator()(const WitnessV0KeyHash& id) const + { + UniValue obj(UniValue::VOBJ); + CPubKey pubkey; + if (pwallet && pwallet->GetPubKey(CKeyID(id), pubkey)) { + obj.pushKV("pubkey", HexStr(pubkey)); + } + return obj; + } + + UniValue operator()(const WitnessV0ScriptHash& id) const + { + UniValue obj(UniValue::VOBJ); + CScript subscript; + CRIPEMD160 hasher; + uint160 hash; + hasher.Write(id.begin(), 32).Finalize(hash.begin()); + if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) { + ProcessSubScript(subscript, obj); + } + return obj; + } + + UniValue operator()(const WitnessUnknown& id) const { return UniValue(UniValue::VOBJ); } +}; + +UniValue DescribeWalletAddress(CWallet* pwallet, const CTxDestination& dest) +{ + UniValue ret(UniValue::VOBJ); + UniValue detail = DescribeAddress(dest); + ret.pushKVs(detail); + ret.pushKVs(boost::apply_visitor(DescribeWalletAddressVisitor(pwallet), dest)); + return ret; +} + +UniValue getaddressinfo(const JSONRPCRequest& request) +{ + CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { + return NullUniValue; + } + + if (request.fHelp || request.params.size() != 1) { + throw std::runtime_error( + "getaddressinfo \"address\"\n" + "\nReturn information about the given bitcoin address. Some information requires the address\n" + "to be in the wallet.\n" + "\nArguments:\n" + "1. \"address\" (string, required) The bitcoin address to get the information of.\n" + "\nResult:\n" + "{\n" + " \"address\" : \"address\", (string) The bitcoin address validated\n" + " \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\n" + " \"ismine\" : true|false, (boolean) If the address is yours or not\n" + " \"iswatchonly\" : true|false, (boolean) If the address is watchonly\n" + " \"isscript\" : true|false, (boolean) If the key is a script\n" + " \"iswitness\" : true|false, (boolean) If the address is a witness address\n" + " \"witness_version\" : version (numeric, optional) The version number of the witness program\n" + " \"witness_program\" : \"hex\" (string, optional) The hex value of the witness program\n" + " \"script\" : \"type\" (string, optional) The output script type. Only if \"isscript\" is true and the redeemscript is known. Possible types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash, witness_v0_scripthash, witness_unknown\n" + " \"hex\" : \"hex\", (string, optional) The redeemscript for the p2sh address\n" + " \"pubkeys\" (string, optional) Array of pubkeys associated with the known redeemscript (only if \"script\" is \"multisig\")\n" + " [\n" + " \"pubkey\"\n" + " ,...\n" + " ]\n" + " \"sigsrequired\" : xxxxx (numeric, optional) Number of signatures required to spend multisig output (only if \"script\" is \"multisig\")\n" + " \"pubkey\" : \"publickeyhex\", (string, optional) The hex value of the raw public key, for single-key addresses (possibly embedded in P2SH or P2WSH)\n" + " \"embedded\" : {...}, (object, optional) Information about the address embedded in P2SH or P2WSH, if relevant and known. It includes all getaddressinfo output fields for the embedded address, excluding metadata (\"timestamp\", \"hdkeypath\", \"hdmasterkeyid\") and relation to the wallet (\"ismine\", \"iswatchonly\", \"account\").\n" + " \"iscompressed\" : true|false, (boolean) If the address is compressed\n" + " \"account\" : \"account\" (string) The account associated with the address, \"\" is the default account\n" + " \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n" + " \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n" + " \"hdmasterkeyid\" : \"<hash160>\" (string, optional) The Hash160 of the HD master pubkey\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getaddressinfo", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") + + HelpExampleRpc("getaddressinfo", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") + ); + } + + LOCK(pwallet->cs_wallet); + + UniValue ret(UniValue::VOBJ); + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + + // Make sure the destination is valid + if (!IsValidDestination(dest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + } + + std::string currentAddress = EncodeDestination(dest); + ret.pushKV("address", currentAddress); + + CScript scriptPubKey = GetScriptForDestination(dest); + ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())); + + isminetype mine = IsMine(*pwallet, dest); + ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE)); + ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY)); + UniValue detail = DescribeWalletAddress(pwallet, dest); + ret.pushKVs(detail); + if (pwallet->mapAddressBook.count(dest)) { + ret.pushKV("account", pwallet->mapAddressBook[dest].name); + } + const CKeyMetadata* meta = nullptr; + CKeyID key_id = GetKeyForDestination(*pwallet, dest); + if (!key_id.IsNull()) { + auto it = pwallet->mapKeyMetadata.find(key_id); + if (it != pwallet->mapKeyMetadata.end()) { + meta = &it->second; + } + } + if (!meta) { + auto it = pwallet->m_script_metadata.find(CScriptID(scriptPubKey)); + if (it != pwallet->m_script_metadata.end()) { + meta = &it->second; + } + } + if (meta) { + ret.pushKV("timestamp", meta->nCreateTime); + if (!meta->hdKeypath.empty()) { + ret.pushKV("hdkeypath", meta->hdKeypath); + ret.pushKV("hdmasterkeyid", meta->hdMasterKeyID.GetHex()); + } + } + return ret; +} + extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp extern UniValue importprivkey(const JSONRPCRequest& request); @@ -3549,6 +3747,7 @@ static const CRPCCommand commands[] = { "wallet", "getaccountaddress", &getaccountaddress, {"account"} }, { "wallet", "getaccount", &getaccount, {"address"} }, { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, {"account"} }, + { "wallet", "getaddressinfo", &getaddressinfo, {"address"} }, { "wallet", "getbalance", &getbalance, {"account","minconf","include_watchonly"} }, { "wallet", "getnewaddress", &getnewaddress, {"account","address_type"} }, { "wallet", "getrawchangeaddress", &getrawchangeaddress, {"address_type"} }, diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h index 77f7b42b23..43cf041665 100644 --- a/src/wallet/rpcwallet.h +++ b/src/wallet/rpcwallet.h @@ -10,6 +10,7 @@ class CRPCTable; class CWallet; class JSONRPCRequest; +class UniValue; void RegisterWalletRPCCommands(CRPCTable &t); @@ -25,4 +26,6 @@ std::string HelpRequiringPassphrase(CWallet *); void EnsureWalletIsUnlocked(CWallet *); bool EnsureWalletIsAvailable(CWallet *, bool avoidException); +UniValue getaddressinfo(const JSONRPCRequest& request); + #endif //BITCOIN_WALLET_RPCWALLET_H diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 7e0881afd7..9db5d63922 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -103,7 +103,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // we can't make 3 cents of mature coins BOOST_CHECK(!testWallet.SelectCoinsMinConf( 3 * CENT, 1, 6, 0, vCoins, setCoinsRet, nValueRet)); - // we can make 3 cents of new coins + // we can make 3 cents of new coins BOOST_CHECK( testWallet.SelectCoinsMinConf( 3 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 3 * CENT); @@ -451,6 +451,9 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) // than or equal to key birthday. BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) { + g_address_type = OUTPUT_TYPE_DEFAULT; + g_change_type = OUTPUT_TYPE_DEFAULT; + // Create two blocks with same timestamp to verify that importwallet rescan // will pick up both blocks, not just the first. const int64_t BLOCK_TIME = chainActive.Tip()->GetBlockTimeMax() + 5; @@ -676,18 +679,24 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) BOOST_CHECK_EQUAL(list.begin()->second.size(), 2); // Lock both coins. Confirm number of available coins drops to 0. - std::vector<COutput> available; - wallet->AvailableCoins(available); - BOOST_CHECK_EQUAL(available.size(), 2); + { + LOCK2(cs_main, wallet->cs_wallet); + std::vector<COutput> available; + wallet->AvailableCoins(available); + BOOST_CHECK_EQUAL(available.size(), 2); + } for (const auto& group : list) { for (const auto& coin : group.second) { LOCK(wallet->cs_wallet); wallet->LockCoin(COutPoint(coin.tx->GetHash(), coin.i)); } } - wallet->AvailableCoins(available); - BOOST_CHECK_EQUAL(available.size(), 0); - + { + LOCK2(cs_main, wallet->cs_wallet); + std::vector<COutput> available; + wallet->AvailableCoins(available); + BOOST_CHECK_EQUAL(available.size(), 0); + } // Confirm ListCoins still returns same result as before, despite coins // being locked. list = wallet->ListCoins(); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 2b8019395c..408a01c50b 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -34,7 +34,6 @@ #include <future> #include <boost/algorithm/string/replace.hpp> -#include <boost/thread.hpp> std::vector<CWalletRef> vpwallets; /** Transaction fee set by the user */ @@ -976,7 +975,8 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) if (!strCmd.empty()) { boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); - boost::thread t(runCommand, strCmd); // thread runs free + std::thread t(runCommand, strCmd); + t.detach(); // thread runs free } return true; @@ -1078,7 +1078,7 @@ bool CWallet::TransactionCanBeAbandoned(const uint256& hashTx) const { LOCK2(cs_main, cs_wallet); const CWalletTx* wtx = GetWalletTx(hashTx); - return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() <= 0 && !wtx->InMempool(); + return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() == 0 && !wtx->InMempool(); } bool CWallet::AbandonTransaction(const uint256& hashTx) @@ -1094,7 +1094,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx) auto it = mapWallet.find(hashTx); assert(it != mapWallet.end()); CWalletTx& origtx = it->second; - if (origtx.GetDepthInMainChain() > 0 || origtx.InMempool()) { + if (origtx.GetDepthInMainChain() != 0 || origtx.InMempool()) { return false; } @@ -2198,111 +2198,109 @@ CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const CCoinControl *coinControl, const CAmount &nMinimumAmount, const CAmount &nMaximumAmount, const CAmount &nMinimumSumAmount, const uint64_t nMaximumCount, const int nMinDepth, const int nMaxDepth) const { + AssertLockHeld(cs_main); + AssertLockHeld(cs_wallet); + vCoins.clear(); + CAmount nTotal = 0; + for (const auto& entry : mapWallet) { - LOCK2(cs_main, cs_wallet); + const uint256& wtxid = entry.first; + const CWalletTx* pcoin = &entry.second; - CAmount nTotal = 0; + if (!CheckFinalTx(*pcoin->tx)) + continue; - for (const auto& entry : mapWallet) - { - const uint256& wtxid = entry.first; - const CWalletTx* pcoin = &entry.second; + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + continue; - if (!CheckFinalTx(*pcoin->tx)) - continue; + int nDepth = pcoin->GetDepthInMainChain(); + if (nDepth < 0) + continue; - if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) - continue; + // We should not consider coins which aren't at least in our mempool + // It's possible for these to be conflicted via ancestors which we may never be able to detect + if (nDepth == 0 && !pcoin->InMempool()) + continue; - int nDepth = pcoin->GetDepthInMainChain(); - if (nDepth < 0) - continue; + bool safeTx = pcoin->IsTrusted(); + + // We should not consider coins from transactions that are replacing + // other transactions. + // + // Example: There is a transaction A which is replaced by bumpfee + // transaction B. In this case, we want to prevent creation of + // a transaction B' which spends an output of B. + // + // Reason: If transaction A were initially confirmed, transactions B + // and B' would no longer be valid, so the user would have to create + // a new transaction C to replace B'. However, in the case of a + // one-block reorg, transactions B' and C might BOTH be accepted, + // when the user only wanted one of them. Specifically, there could + // be a 1-block reorg away from the chain where transactions A and C + // were accepted to another chain where B, B', and C were all + // accepted. + if (nDepth == 0 && pcoin->mapValue.count("replaces_txid")) { + safeTx = false; + } - // We should not consider coins which aren't at least in our mempool - // It's possible for these to be conflicted via ancestors which we may never be able to detect - if (nDepth == 0 && !pcoin->InMempool()) - continue; + // Similarly, we should not consider coins from transactions that + // have been replaced. In the example above, we would want to prevent + // creation of a transaction A' spending an output of A, because if + // transaction B were initially confirmed, conflicting with A and + // A', we wouldn't want to the user to create a transaction D + // intending to replace A', but potentially resulting in a scenario + // where A, A', and D could all be accepted (instead of just B and + // D, or just A and A' like the user would want). + if (nDepth == 0 && pcoin->mapValue.count("replaced_by_txid")) { + safeTx = false; + } - bool safeTx = pcoin->IsTrusted(); - - // We should not consider coins from transactions that are replacing - // other transactions. - // - // Example: There is a transaction A which is replaced by bumpfee - // transaction B. In this case, we want to prevent creation of - // a transaction B' which spends an output of B. - // - // Reason: If transaction A were initially confirmed, transactions B - // and B' would no longer be valid, so the user would have to create - // a new transaction C to replace B'. However, in the case of a - // one-block reorg, transactions B' and C might BOTH be accepted, - // when the user only wanted one of them. Specifically, there could - // be a 1-block reorg away from the chain where transactions A and C - // were accepted to another chain where B, B', and C were all - // accepted. - if (nDepth == 0 && pcoin->mapValue.count("replaces_txid")) { - safeTx = false; - } + if (fOnlySafe && !safeTx) { + continue; + } - // Similarly, we should not consider coins from transactions that - // have been replaced. In the example above, we would want to prevent - // creation of a transaction A' spending an output of A, because if - // transaction B were initially confirmed, conflicting with A and - // A', we wouldn't want to the user to create a transaction D - // intending to replace A', but potentially resulting in a scenario - // where A, A', and D could all be accepted (instead of just B and - // D, or just A and A' like the user would want). - if (nDepth == 0 && pcoin->mapValue.count("replaced_by_txid")) { - safeTx = false; - } + if (nDepth < nMinDepth || nDepth > nMaxDepth) + continue; - if (fOnlySafe && !safeTx) { + for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { + if (pcoin->tx->vout[i].nValue < nMinimumAmount || pcoin->tx->vout[i].nValue > nMaximumAmount) continue; - } - if (nDepth < nMinDepth || nDepth > nMaxDepth) + if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint(entry.first, i))) continue; - for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { - if (pcoin->tx->vout[i].nValue < nMinimumAmount || pcoin->tx->vout[i].nValue > nMaximumAmount) - continue; - - if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint(entry.first, i))) - continue; - - if (IsLockedCoin(entry.first, i)) - continue; - - if (IsSpent(wtxid, i)) - continue; + if (IsLockedCoin(entry.first, i)) + continue; - isminetype mine = IsMine(pcoin->tx->vout[i]); + if (IsSpent(wtxid, i)) + continue; - if (mine == ISMINE_NO) { - continue; - } + isminetype mine = IsMine(pcoin->tx->vout[i]); - bool fSpendableIn = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO); - bool fSolvableIn = (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO; + if (mine == ISMINE_NO) { + continue; + } - vCoins.push_back(COutput(pcoin, i, nDepth, fSpendableIn, fSolvableIn, safeTx)); + bool fSpendableIn = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO); + bool fSolvableIn = (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO; - // Checks the sum amount of all UTXO's. - if (nMinimumSumAmount != MAX_MONEY) { - nTotal += pcoin->tx->vout[i].nValue; + vCoins.push_back(COutput(pcoin, i, nDepth, fSpendableIn, fSolvableIn, safeTx)); - if (nTotal >= nMinimumSumAmount) { - return; - } - } + // Checks the sum amount of all UTXO's. + if (nMinimumSumAmount != MAX_MONEY) { + nTotal += pcoin->tx->vout[i].nValue; - // Checks the maximum number of UTXO's. - if (nMaximumCount > 0 && vCoins.size() >= nMaximumCount) { + if (nTotal >= nMinimumSumAmount) { return; } } + + // Checks the maximum number of UTXO's. + if (nMaximumCount > 0 && vCoins.size() >= nMaximumCount) { + return; + } } } } @@ -2320,11 +2318,11 @@ std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const // avoid adding some extra complexity to the Qt code. std::map<CTxDestination, std::vector<COutput>> result; - std::vector<COutput> availableCoins; - AvailableCoins(availableCoins); LOCK2(cs_main, cs_wallet); + AvailableCoins(availableCoins); + for (auto& coin : availableCoins) { CTxDestination address; if (coin.fSpendable && @@ -3094,7 +3092,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon { // Broadcast if (!wtx.AcceptToMemoryPool(maxTxFee, state)) { - LogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", state.GetRejectReason()); + LogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", FormatStateMessage(state)); // TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure. } else { wtx.RelayWalletTransaction(connman); @@ -4181,11 +4179,6 @@ int CMerkleTx::GetBlocksToMaturity() const bool CWalletTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) { - // Quick check to avoid re-setting fInMempool to false - if (mempool.exists(tx->GetHash())) { - return false; - } - // We must set fInMempool here - while it will be re-set to true by the // entered-mempool callback, if we did not there would be a race where a // user could call sendmoney in a loop and hit spurious out of funds errors @@ -4193,7 +4186,7 @@ bool CWalletTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& // unavailable as we're not yet aware its in mempool. bool ret = ::AcceptToMemoryPool(mempool, state, tx, nullptr /* pfMissingInputs */, nullptr /* plTxnReplaced */, false /* bypass_limits */, nAbsurdFee); - fInMempool = ret; + fInMempool |= ret; return ret; } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index a4684c2935..fefe415bb1 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -408,7 +408,7 @@ public: mapValue["timesmart"] = strprintf("%u", nTimeSmart); } - READWRITE(*(CMerkleTx*)this); + READWRITE(*static_cast<CMerkleTx*>(this)); std::vector<CMerkleTx> vUnused; //!< Used to be vtxPrev READWRITE(vUnused); READWRITE(mapValue); |