diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/chainparams.cpp | 4 | ||||
-rw-r--r-- | src/chainparams.h | 3 | ||||
-rw-r--r-- | src/init.cpp | 19 | ||||
-rw-r--r-- | src/node/context.cpp | 1 | ||||
-rw-r--r-- | src/node/context.h | 2 | ||||
-rw-r--r-- | src/qt/clientmodel.cpp | 5 | ||||
-rw-r--r-- | src/qt/walletmodel.cpp | 4 | ||||
-rw-r--r-- | src/rpc/blockchain.cpp | 222 | ||||
-rw-r--r-- | src/rpc/client.cpp | 1 | ||||
-rw-r--r-- | src/rpc/mining.cpp | 30 | ||||
-rw-r--r-- | src/rpc/misc.cpp | 57 | ||||
-rw-r--r-- | src/rpc/net.cpp | 126 | ||||
-rw-r--r-- | src/rpc/rawtransaction.cpp | 54 | ||||
-rw-r--r-- | src/rpc/server.cpp | 6 | ||||
-rw-r--r-- | src/scheduler.cpp | 22 | ||||
-rw-r--r-- | src/scheduler.h | 7 | ||||
-rw-r--r-- | src/test/denialofservice_tests.cpp | 10 | ||||
-rw-r--r-- | src/test/scheduler_tests.cpp | 43 | ||||
-rw-r--r-- | src/test/util/setup_common.cpp | 7 | ||||
-rw-r--r-- | src/test/util/setup_common.h | 1 | ||||
-rw-r--r-- | src/util/system.cpp | 10 | ||||
-rw-r--r-- | src/util/system.h | 3 | ||||
-rw-r--r-- | src/wallet/init.cpp | 2 | ||||
-rw-r--r-- | src/wallet/rpcwallet.cpp | 183 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 8 | ||||
-rw-r--r-- | src/zmq/zmqrpc.cpp | 6 |
26 files changed, 480 insertions, 356 deletions
diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 31592b0f0a..a9183ac970 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -135,6 +135,7 @@ public: fDefaultConsistencyChecks = false; fRequireStandard = true; m_is_test_chain = false; + m_is_mockable_chain = false; checkpointData = { { @@ -231,7 +232,7 @@ public: fDefaultConsistencyChecks = false; fRequireStandard = false; m_is_test_chain = true; - + m_is_mockable_chain = false; checkpointData = { { @@ -303,6 +304,7 @@ public: fDefaultConsistencyChecks = true; fRequireStandard = true; m_is_test_chain = true; + m_is_mockable_chain = true; checkpointData = { { diff --git a/src/chainparams.h b/src/chainparams.h index 63398e587e..379c75e4be 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -68,6 +68,8 @@ public: bool RequireStandard() const { return fRequireStandard; } /** If this chain is exclusively used for testing */ bool IsTestChain() const { return m_is_test_chain; } + /** If this chain allows time to be mocked */ + bool IsMockableChain() const { return m_is_mockable_chain; } uint64_t PruneAfterHeight() const { return nPruneAfterHeight; } /** Minimum free space (in GB) needed for data directory */ uint64_t AssumedBlockchainSize() const { return m_assumed_blockchain_size; } @@ -102,6 +104,7 @@ protected: bool fDefaultConsistencyChecks; bool fRequireStandard; bool m_is_test_chain; + bool m_is_mockable_chain; CCheckpointData checkpointData; ChainTxData chainTxData; }; diff --git a/src/init.cpp b/src/init.cpp index 90d2624c7f..24a1fd27db 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -157,7 +157,6 @@ NODISCARD static bool CreatePidFile() static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle; static boost::thread_group threadGroup; -static CScheduler scheduler; void Interrupt(NodeContext& node) { @@ -295,6 +294,7 @@ void Shutdown(NodeContext& node) globalVerifyHandle.reset(); ECC_Stop(); if (node.mempool) node.mempool = nullptr; + node.scheduler.reset(); LogPrintf("%s: done\n", __func__); } @@ -1268,16 +1268,19 @@ bool AppInitMain(NodeContext& node) } } + assert(!node.scheduler); + node.scheduler = MakeUnique<CScheduler>(); + // Start the lightweight task scheduler thread - CScheduler::Function serviceLoop = std::bind(&CScheduler::serviceQueue, &scheduler); + CScheduler::Function serviceLoop = [&node]{ node.scheduler->serviceQueue(); }; threadGroup.create_thread(std::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop)); // Gather some entropy once per minute. - scheduler.scheduleEvery([]{ + node.scheduler->scheduleEvery([]{ RandAddPeriodic(); }, 60000); - GetMainSignals().RegisterBackgroundSignalScheduler(scheduler); + GetMainSignals().RegisterBackgroundSignalScheduler(*node.scheduler); // Create client interfaces for wallets that are supposed to be loaded // according to -wallet and -disablewallet options. This only constructs @@ -1327,7 +1330,7 @@ bool AppInitMain(NodeContext& node) assert(!node.connman); node.connman = std::unique_ptr<CConnman>(new CConnman(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max()))); - node.peer_logic.reset(new PeerLogicValidation(node.connman.get(), node.banman.get(), scheduler)); + node.peer_logic.reset(new PeerLogicValidation(node.connman.get(), node.banman.get(), *node.scheduler)); RegisterValidationInterface(node.peer_logic.get()); // sanitize comments per BIP-0014, format user agent and check total size @@ -1819,7 +1822,7 @@ bool AppInitMain(NodeContext& node) connOptions.m_specified_outgoing = connect; } } - if (!node.connman->Start(scheduler, connOptions)) { + if (!node.connman->Start(*node.scheduler, connOptions)) { return false; } @@ -1848,11 +1851,11 @@ bool AppInitMain(NodeContext& node) uiInterface.InitMessage(_("Done loading").translated); for (const auto& client : node.chain_clients) { - client->start(scheduler); + client->start(*node.scheduler); } BanMan* banman = node.banman.get(); - scheduler.scheduleEvery([banman]{ + node.scheduler->scheduleEvery([banman]{ banman->DumpBanlist(); }, DUMP_BANS_INTERVAL * 1000); diff --git a/src/node/context.cpp b/src/node/context.cpp index 26a01420c8..5b19a41bd4 100644 --- a/src/node/context.cpp +++ b/src/node/context.cpp @@ -8,6 +8,7 @@ #include <interfaces/chain.h> #include <net.h> #include <net_processing.h> +#include <scheduler.h> NodeContext::NodeContext() {} NodeContext::~NodeContext() {} diff --git a/src/node/context.h b/src/node/context.h index dab5b5d048..1c592b456b 100644 --- a/src/node/context.h +++ b/src/node/context.h @@ -10,6 +10,7 @@ class BanMan; class CConnman; +class CScheduler; class CTxMemPool; class PeerLogicValidation; namespace interfaces { @@ -34,6 +35,7 @@ struct NodeContext { std::unique_ptr<BanMan> banman; std::unique_ptr<interfaces::Chain> chain; std::vector<std::unique_ptr<interfaces::ChainClient>> chain_clients; + std::unique_ptr<CScheduler> scheduler; //! Declare default constructor and destructor that are not inline, so code //! instantiating the NodeContext struct doesn't need to #include class diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index e8146982f9..a1ec3eaab1 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -242,8 +242,9 @@ static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, int heig clientmodel->cachedBestHeaderHeight = height; clientmodel->cachedBestHeaderTime = blockTime; } - // if we are in-sync or if we notify a header update, update the UI regardless of last update time - if (fHeader || !initialSync || now - nLastUpdateNotification > MODEL_UPDATE_DELAY) { + + // During initial sync, block notifications, and header notifications from reindexing are both throttled. + if (!initialSync || (fHeader && !clientmodel->node().getReindex()) || now - nLastUpdateNotification > MODEL_UPDATE_DELAY) { //pass an async signal to the UI thread bool invoked = QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection, Q_ARG(int, height), diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 6c3a06f3a2..8a84a8c168 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -82,12 +82,12 @@ void WalletModel::pollBalanceChanged() return; } - if(fForceCheckBalanceChanged || m_node.getNumBlocks() != cachedNumBlocks) + if(fForceCheckBalanceChanged || numBlocks != cachedNumBlocks) { fForceCheckBalanceChanged = false; // Balance and number of transactions might have changed - cachedNumBlocks = m_node.getNumBlocks(); + cachedNumBlocks = numBlocks; checkBalanceChanged(new_balances); if(transactionTableModel) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index eb5148eebd..9b06aba22b 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -229,7 +229,7 @@ static UniValue waitfornewblock(const JSONRPCRequest& request) RPCResult{ "{ (json object)\n" " \"hash\" : { (string) The blockhash\n" - " \"height\" : { (int) Block height\n" + " \"height\" : { (numeric) Block height\n" "}\n" }, RPCExamples{ @@ -269,7 +269,7 @@ static UniValue waitforblock(const JSONRPCRequest& request) RPCResult{ "{ (json object)\n" " \"hash\" : { (string) The blockhash\n" - " \"height\" : { (int) Block height\n" + " \"height\" : { (numeric) Block height\n" "}\n" }, RPCExamples{ @@ -313,7 +313,7 @@ static UniValue waitforblockheight(const JSONRPCRequest& request) RPCResult{ "{ (json object)\n" " \"hash\" : { (string) The blockhash\n" - " \"height\" : { (int) Block height\n" + " \"height\" : { (numeric) Block height\n" "}\n" }, RPCExamples{ @@ -400,10 +400,10 @@ static std::string EntryDescriptionString() " \"ancestor\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one) in " + CURRENCY_UNIT + "\n" " \"descendant\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one) in " + CURRENCY_UNIT + "\n" " }\n" - " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n" + " \"depends\" : [ (json array) unconfirmed transactions used as inputs for this transaction\n" " \"transactionid\", (string) parent transaction id\n" " ... ]\n" - " \"spentby\" : [ (array) unconfirmed transactions spending outputs from this transaction\n" + " \"spentby\" : [ (json array) unconfirmed transactions spending outputs from this transaction\n" " \"transactionid\", (string) child transaction id\n" " ... ]\n" " \"bip125-replaceable\" : true|false, (boolean) Whether this transaction could be replaced due to BIP125 (replace-by-fee)\n"; @@ -984,14 +984,14 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request) {}, RPCResult{ "{\n" - " \"height\":n, (numeric) The current block height (index)\n" - " \"bestblock\": \"hex\", (string) The hash of the block at the tip of the chain\n" - " \"transactions\": n, (numeric) The number of transactions with unspent outputs\n" - " \"txouts\": n, (numeric) The number of unspent transaction outputs\n" - " \"bogosize\": n, (numeric) A meaningless metric for UTXO set size\n" + " \"height\" : n, (numeric) The current block height (index)\n" + " \"bestblock\" : \"hex\", (string) The hash of the block at the tip of the chain\n" + " \"transactions\" : n, (numeric) The number of transactions with unspent outputs\n" + " \"txouts\" : n, (numeric) The number of unspent transaction outputs\n" + " \"bogosize\" : n, (numeric) A meaningless metric for UTXO set size\n" " \"hash_serialized_2\": \"hash\", (string) The serialized hash\n" - " \"disk_size\": n, (numeric) The estimated size of the chainstate on disk\n" - " \"total_amount\": x.xxx (numeric) The total amount\n" + " \"disk_size\" : n, (numeric) The estimated size of the chainstate on disk\n" + " \"total_amount\" : x.xxx (numeric) The total amount\n" "}\n" }, RPCExamples{ @@ -1032,7 +1032,7 @@ UniValue gettxout(const JSONRPCRequest& request) }, RPCResult{ "{\n" - " \"bestblock\": \"hash\", (string) The hash of the block at the tip of the chain\n" + " \"bestblock\" : \"hash\", (string) The hash of the block at the tip of the chain\n" " \"confirmations\" : n, (numeric) The number of confirmations\n" " \"value\" : x.xxx, (numeric) The transaction value in " + CURRENCY_UNIT + "\n" " \"scriptPubKey\" : { (json object)\n" @@ -1204,39 +1204,39 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) {}, RPCResult{ "{\n" - " \"chain\": \"xxxx\", (string) current network name (main, test, regtest)\n" - " \"blocks\": xxxxxx, (numeric) the height of the most-work fully-validated chain. The genesis block has height 0\n" - " \"headers\": xxxxxx, (numeric) the current number of headers we have validated\n" - " \"bestblockhash\": \"...\", (string) the hash of the currently best block\n" - " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" - " \"mediantime\": xxxxxx, (numeric) median time for the current best block\n" - " \"verificationprogress\": xxxx, (numeric) estimate of verification progress [0..1]\n" - " \"initialblockdownload\": xxxx, (bool) (debug information) estimate of whether this node is in Initial Block Download mode.\n" - " \"chainwork\": \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n" - " \"size_on_disk\": xxxxxx, (numeric) the estimated size of the block and undo files on disk\n" - " \"pruned\": xx, (boolean) if the blocks are subject to pruning\n" - " \"pruneheight\": xxxxxx, (numeric) lowest-height complete block stored (only present if pruning is enabled)\n" - " \"automatic_pruning\": xx, (boolean) whether automatic pruning is enabled (only present if pruning is enabled)\n" - " \"prune_target_size\": xxxxxx, (numeric) the target size used by pruning (only present if automatic pruning is enabled)\n" - " \"softforks\": { (object) status of softforks\n" + " \"chain\" : \"xxxx\", (string) current network name (main, test, regtest)\n" + " \"blocks\" : xxxxxx, (numeric) the height of the most-work fully-validated chain. The genesis block has height 0\n" + " \"headers\" : xxxxxx, (numeric) the current number of headers we have validated\n" + " \"bestblockhash\" : \"...\", (string) the hash of the currently best block\n" + " \"difficulty\" : xxxxxx, (numeric) the current difficulty\n" + " \"mediantime\" : xxxxxx, (numeric) median time for the current best block\n" + " \"verificationprogress\" : xxxx, (numeric) estimate of verification progress [0..1]\n" + " \"initialblockdownload\" : xxxx, (boolean) (debug information) estimate of whether this node is in Initial Block Download mode.\n" + " \"chainwork\" : \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n" + " \"size_on_disk\" : xxxxxx, (numeric) the estimated size of the block and undo files on disk\n" + " \"pruned\" : xx, (boolean) if the blocks are subject to pruning\n" + " \"pruneheight\" : xxxxxx, (numeric) lowest-height complete block stored (only present if pruning is enabled)\n" + " \"automatic_pruning\" : xx, (boolean) whether automatic pruning is enabled (only present if pruning is enabled)\n" + " \"prune_target_size\" : xxxxxx, (numeric) the target size used by pruning (only present if automatic pruning is enabled)\n" + " \"softforks\" : { (json object) status of softforks\n" " \"xxxx\" : { (string) name of the softfork\n" - " \"type\": \"xxxx\", (string) one of \"buried\", \"bip9\"\n" - " \"bip9\": { (object) status of bip9 softforks (only for \"bip9\" type)\n" - " \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n" - " \"bit\": xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n" - " \"start_time\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n" - " \"timeout\": xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n" - " \"since\": xx, (numeric) height of the first block to which the status applies\n" - " \"statistics\": { (object) numeric statistics about BIP9 signalling for a softfork\n" - " \"period\": xx, (numeric) the length in blocks of the BIP9 signalling period \n" - " \"threshold\": xx, (numeric) the number of blocks with the version bit set required to activate the feature \n" - " \"elapsed\": xx, (numeric) the number of blocks elapsed since the beginning of the current period \n" - " \"count\": xx, (numeric) the number of blocks with the version bit set in the current period \n" - " \"possible\": xx (boolean) returns false if there are not enough blocks left in this period to pass activation threshold \n" + " \"type\" : \"xxxx\", (string) one of \"buried\", \"bip9\"\n" + " \"bip9\": { (json object) status of bip9 softforks (only for \"bip9\" type)\n" + " \"status\" : \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n" + " \"bit\" : xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n" + " \"start_time\" : xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n" + " \"timeout\" : xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n" + " \"since\" : xx, (numeric) height of the first block to which the status applies\n" + " \"statistics\" : { (json object) numeric statistics about BIP9 signalling for a softfork\n" + " \"period\" : xx, (numeric) the length in blocks of the BIP9 signalling period \n" + " \"threshold\" : xx, (numeric) the number of blocks with the version bit set required to activate the feature \n" + " \"elapsed\" : xx, (numeric) the number of blocks elapsed since the beginning of the current period \n" + " \"count\" : xx, (numeric) the number of blocks with the version bit set in the current period \n" + " \"possible\" : xx (boolean) returns false if there are not enough blocks left in this period to pass activation threshold \n" " }\n" " },\n" - " \"height\": \"xxxxxx\", (numeric) height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)\n" - " \"active\": xx, (boolean) true if the rules are enforced for the mempool and the next block\n" + " \"height\" : \"xxxxxx\", (numeric) height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)\n" + " \"active\" : xx, (boolean) true if the rules are enforced for the mempool and the next block\n" " }\n" " }\n" " \"warnings\" : \"...\", (string) any network and blockchain warnings.\n" @@ -1318,16 +1318,16 @@ static UniValue getchaintips(const JSONRPCRequest& request) RPCResult{ "[\n" " {\n" - " \"height\": xxxx, (numeric) height of the chain tip\n" - " \"hash\": \"xxxx\", (string) block hash of the tip\n" - " \"branchlen\": 0 (numeric) zero for main chain\n" - " \"status\": \"active\" (string) \"active\" for the main chain\n" + " \"height\" : xxxx, (numeric) height of the chain tip\n" + " \"hash\" : \"xxxx\", (string) block hash of the tip\n" + " \"branchlen\" : 0 (numeric) zero for main chain\n" + " \"status\" : \"active\" (string) \"active\" for the main chain\n" " },\n" " {\n" - " \"height\": xxxx,\n" - " \"hash\": \"xxxx\",\n" - " \"branchlen\": 1 (numeric) length of branch connecting the tip to the main chain\n" - " \"status\": \"xxxx\" (string) status of the chain (active, valid-fork, valid-headers, headers-only, invalid)\n" + " \"height\" : xxxx,\n" + " \"hash\" : \"xxxx\",\n" + " \"branchlen\" : 1 (numeric) length of branch connecting the tip to the main chain\n" + " \"status\" : \"xxxx\" (string) status of the chain (active, valid-fork, valid-headers, headers-only, invalid)\n" " }\n" "]\n" "Possible values for status:\n" @@ -1437,13 +1437,13 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request) {}, RPCResult{ "{\n" - " \"loaded\": true|false (boolean) True if the mempool is fully loaded\n" - " \"size\": xxxxx, (numeric) Current tx count\n" - " \"bytes\": xxxxx, (numeric) Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted\n" - " \"usage\": xxxxx, (numeric) Total memory usage for the mempool\n" - " \"maxmempool\": xxxxx, (numeric) Maximum memory usage for the mempool\n" - " \"mempoolminfee\": xxxxx (numeric) Minimum fee rate in " + CURRENCY_UNIT + "/kB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee\n" - " \"minrelaytxfee\": xxxxx (numeric) Current minimum relay fee for transactions\n" + " \"loaded\" : true|false (boolean) True if the mempool is fully loaded\n" + " \"size\" : xxxxx, (numeric) Current tx count\n" + " \"bytes\" : xxxxx, (numeric) Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted\n" + " \"usage\" : xxxxx, (numeric) Total memory usage for the mempool\n" + " \"maxmempool\" : xxxxx, (numeric) Maximum memory usage for the mempool\n" + " \"mempoolminfee\" : xxxxx (numeric) Minimum fee rate in " + CURRENCY_UNIT + "/kB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee\n" + " \"minrelaytxfee\" : xxxxx (numeric) Current minimum relay fee for transactions\n" "}\n" }, RPCExamples{ @@ -1577,14 +1577,14 @@ static UniValue getchaintxstats(const JSONRPCRequest& request) }, RPCResult{ "{\n" - " \"time\": xxxxx, (numeric) The timestamp for the final block in the window, expressed in " + UNIX_EPOCH_TIME + ".\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_final_block_height\": xxxxx, (numeric) The height 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" + " \"time\" : xxxxx, (numeric) The timestamp for the final block in the window, expressed in " + UNIX_EPOCH_TIME + ".\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_final_block_height\" : xxxxx, (numeric) The height 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" }, RPCExamples{ @@ -1716,41 +1716,41 @@ static UniValue getblockstats(const JSONRPCRequest& request) }, RPCResult{ "{ (json object)\n" - " \"avgfee\": xxxxx, (numeric) Average fee in the block\n" - " \"avgfeerate\": xxxxx, (numeric) Average feerate (in satoshis per virtual byte)\n" - " \"avgtxsize\": xxxxx, (numeric) Average transaction size\n" - " \"blockhash\": xxxxx, (string) The block hash (to check for potential reorgs)\n" - " \"feerate_percentiles\": [ (array of numeric) Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in satoshis per virtual byte)\n" + " \"avgfee\" : xxxxx, (numeric) Average fee in the block\n" + " \"avgfeerate\" : xxxxx, (numeric) Average feerate (in satoshis per virtual byte)\n" + " \"avgtxsize\" : xxxxx, (numeric) Average transaction size\n" + " \"blockhash\" : xxxxx, (string) The block hash (to check for potential reorgs)\n" + " \"feerate_percentiles\" : [ (array of numeric) Feerates at the 10th, 25th, 50th, 75th, and 90th percentile weight unit (in satoshis per virtual byte)\n" " \"10th_percentile_feerate\", (numeric) The 10th percentile feerate\n" " \"25th_percentile_feerate\", (numeric) The 25th percentile feerate\n" " \"50th_percentile_feerate\", (numeric) The 50th percentile feerate\n" " \"75th_percentile_feerate\", (numeric) The 75th percentile feerate\n" " \"90th_percentile_feerate\", (numeric) The 90th percentile feerate\n" " ],\n" - " \"height\": xxxxx, (numeric) The height of the block\n" - " \"ins\": xxxxx, (numeric) The number of inputs (excluding coinbase)\n" - " \"maxfee\": xxxxx, (numeric) Maximum fee in the block\n" - " \"maxfeerate\": xxxxx, (numeric) Maximum feerate (in satoshis per virtual byte)\n" - " \"maxtxsize\": xxxxx, (numeric) Maximum transaction size\n" - " \"medianfee\": xxxxx, (numeric) Truncated median fee in the block\n" - " \"mediantime\": xxxxx, (numeric) The block median time past\n" - " \"mediantxsize\": xxxxx, (numeric) Truncated median transaction size\n" - " \"minfee\": xxxxx, (numeric) Minimum fee in the block\n" - " \"minfeerate\": xxxxx, (numeric) Minimum feerate (in satoshis per virtual byte)\n" - " \"mintxsize\": xxxxx, (numeric) Minimum transaction size\n" - " \"outs\": xxxxx, (numeric) The number of outputs\n" - " \"subsidy\": xxxxx, (numeric) The block subsidy\n" - " \"swtotal_size\": xxxxx, (numeric) Total size of all segwit transactions\n" - " \"swtotal_weight\": xxxxx, (numeric) Total weight of all segwit transactions divided by segwit scale factor (4)\n" - " \"swtxs\": xxxxx, (numeric) The number of segwit transactions\n" - " \"time\": xxxxx, (numeric) The block time\n" - " \"total_out\": xxxxx, (numeric) Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])\n" - " \"total_size\": xxxxx, (numeric) Total size of all non-coinbase transactions\n" - " \"total_weight\": xxxxx, (numeric) Total weight of all non-coinbase transactions divided by segwit scale factor (4)\n" - " \"totalfee\": xxxxx, (numeric) The fee total\n" - " \"txs\": xxxxx, (numeric) The number of transactions (excluding coinbase)\n" - " \"utxo_increase\": xxxxx, (numeric) The increase/decrease in the number of unspent outputs\n" - " \"utxo_size_inc\": xxxxx, (numeric) The increase/decrease in size for the utxo index (not discounting op_return and similar)\n" + " \"height\" : xxxxx, (numeric) The height of the block\n" + " \"ins\" : xxxxx, (numeric) The number of inputs (excluding coinbase)\n" + " \"maxfee\" : xxxxx, (numeric) Maximum fee in the block\n" + " \"maxfeerate\" : xxxxx, (numeric) Maximum feerate (in satoshis per virtual byte)\n" + " \"maxtxsize\" : xxxxx, (numeric) Maximum transaction size\n" + " \"medianfee\" : xxxxx, (numeric) Truncated median fee in the block\n" + " \"mediantime\" : xxxxx, (numeric) The block median time past\n" + " \"mediantxsize\" : xxxxx, (numeric) Truncated median transaction size\n" + " \"minfee\" : xxxxx, (numeric) Minimum fee in the block\n" + " \"minfeerate\" : xxxxx, (numeric) Minimum feerate (in satoshis per virtual byte)\n" + " \"mintxsize\" : xxxxx, (numeric) Minimum transaction size\n" + " \"outs\" : xxxxx, (numeric) The number of outputs\n" + " \"subsidy\" : xxxxx, (numeric) The block subsidy\n" + " \"swtotal_size\" : xxxxx, (numeric) Total size of all segwit transactions\n" + " \"swtotal_weight\" : xxxxx, (numeric) Total weight of all segwit transactions divided by segwit scale factor (4)\n" + " \"swtxs\" : xxxxx, (numeric) The number of segwit transactions\n" + " \"time\" : xxxxx, (numeric) The block time\n" + " \"total_out\" : xxxxx, (numeric) Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])\n" + " \"total_size\" : xxxxx, (numeric) Total size of all non-coinbase transactions\n" + " \"total_weight\" : xxxxx, (numeric) Total weight of all non-coinbase transactions divided by segwit scale factor (4)\n" + " \"totalfee\" : xxxxx, (numeric) The fee total\n" + " \"txs\" : xxxxx, (numeric) The number of transactions (excluding coinbase)\n" + " \"utxo_increase\" : xxxxx, (numeric) The increase/decrease in the number of unspent outputs\n" + " \"utxo_size_inc\" : xxxxx, (numeric) The increase/decrease in size for the utxo index (not discounting op_return and similar)\n" "}\n" }, RPCExamples{ @@ -2076,21 +2076,21 @@ UniValue scantxoutset(const JSONRPCRequest& request) }, RPCResult{ "{\n" - " \"success\": true|false, (boolean) Whether the scan was completed\n" - " \"txouts\": n, (numeric) The number of unspent transaction outputs scanned\n" - " \"height\": n, (numeric) The current block height (index)\n" - " \"bestblock\": \"hex\", (string) The hash of the block at the tip of the chain\n" - " \"unspents\": [\n" + " \"success\" : true|false, (boolean) Whether the scan was completed\n" + " \"txouts\" : n, (numeric) The number of unspent transaction outputs scanned\n" + " \"height\" : n, (numeric) The current block height (index)\n" + " \"bestblock\" : \"hex\", (string) The hash of the block at the tip of the chain\n" + " \"unspents\" : [\n" " {\n" - " \"txid\": \"hash\", (string) The transaction id\n" - " \"vout\": n, (numeric) The vout value\n" - " \"scriptPubKey\": \"script\", (string) The script key\n" - " \"desc\": \"descriptor\", (string) A specialized descriptor for the matched scriptPubKey\n" - " \"amount\": x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " of the unspent output\n" - " \"height\": n, (numeric) Height of the unspent transaction output\n" + " \"txid\" : \"hash\", (string) The transaction id\n" + " \"vout\" : n, (numeric) The vout value\n" + " \"scriptPubKey\" : \"script\", (string) The script key\n" + " \"desc\" : \"descriptor\", (string) A specialized descriptor for the matched scriptPubKey\n" + " \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " of the unspent output\n" + " \"height\" : n, (numeric) Height of the unspent transaction output\n" " }\n" " ,...],\n" - " \"total_amount\": x.xxx, (numeric) The total amount of all found unspent outputs in " + CURRENCY_UNIT + "\n" + " \"total_amount\" : x.xxx, (numeric) The total amount of all found unspent outputs in " + CURRENCY_UNIT + "\n" "]\n" }, RPCExamples{""}, @@ -2284,10 +2284,10 @@ UniValue dumptxoutset(const JSONRPCRequest& request) }, RPCResult{ "{\n" - " \"coins_written\": n, (numeric) the number of coins written in the snapshot\n" - " \"base_hash\": \"...\", (string) the hash of the base of the snapshot\n" - " \"base_height\": n, (string) the height of the base of the snapshot\n" - " \"path\": \"...\" (string) the absolute path that the snapshot was written to\n" + " \"coins_written\" : n, (numeric) the number of coins written in the snapshot\n" + " \"base_hash\" : \"...\", (string) the hash of the base of the snapshot\n" + " \"base_height\" : n, (string) the height of the base of the snapshot\n" + " \"path\" : \"...\" (string) the absolute path that the snapshot was written to\n" "]\n" }, RPCExamples{ diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 2eaa3427eb..c1762483e9 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -27,6 +27,7 @@ public: static const CRPCConvertParam vRPCConvertParams[] = { { "setmocktime", 0, "timestamp" }, + { "mockscheduler", 0, "delta_time" }, { "utxoupdatepsbt", 1, "descriptors" }, { "generatetoaddress", 0, "nblocks" }, { "generatetoaddress", 2, "maxtries" }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 69885546c8..ab5d830b2a 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -154,7 +154,7 @@ static UniValue generatetodescriptor(const JSONRPCRequest& request) {"maxtries", RPCArg::Type::NUM, /* default */ "1000000", "How many iterations to try."}, }, RPCResult{ - "[ blockhashes ] (array) hashes of blocks generated\n"}, + "[ blockhashes ] (json array) hashes of blocks generated\n"}, RPCExamples{ "\nGenerate 11 blocks to mydesc\n" + HelpExampleCli("generatetodescriptor", "11 \"mydesc\"")}, } @@ -196,7 +196,7 @@ static UniValue generatetoaddress(const JSONRPCRequest& request) {"maxtries", RPCArg::Type::NUM, /* default */ "1000000", "How many iterations to try."}, }, RPCResult{ - "[ blockhashes ] (array) hashes of blocks generated\n" + "[ blockhashes ] (json array) hashes of blocks generated\n" }, RPCExamples{ "\nGenerate 11 blocks to myaddress\n" @@ -231,14 +231,14 @@ static UniValue getmininginfo(const JSONRPCRequest& request) {}, RPCResult{ "{\n" - " \"blocks\": nnn, (numeric) The current block\n" - " \"currentblockweight\": nnn, (numeric, optional) The block weight of the last assembled block (only present if a block was ever assembled)\n" - " \"currentblocktx\": nnn, (numeric, optional) The number of block transactions of the last assembled block (only present if a block was ever assembled)\n" - " \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n" - " \"networkhashps\": nnn, (numeric) The network hashes per second\n" - " \"pooledtx\": n (numeric) The size of the mempool\n" - " \"chain\": \"xxxx\", (string) current network name (main, test, regtest)\n" - " \"warnings\": \"...\" (string) any network and blockchain warnings\n" + " \"blocks\" : nnn, (numeric) The current block\n" + " \"currentblockweight\" : nnn, (numeric, optional) The block weight of the last assembled block (only present if a block was ever assembled)\n" + " \"currentblocktx\" : nnn, (numeric, optional) The number of block transactions of the last assembled block (only present if a block was ever assembled)\n" + " \"difficulty\" : xxx.xxxxx (numeric) The current difficulty\n" + " \"networkhashps\" : nnn, (numeric) The network hashes per second\n" + " \"pooledtx\" : n (numeric) The size of the mempool\n" + " \"chain\" : \"xxxx\", (string) current network name (main, test, regtest)\n" + " \"warnings\" : \"...\" (string) any network and blockchain warnings\n" "}\n" }, RPCExamples{ @@ -365,16 +365,16 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) " },\n" " \"vbrequired\" : n, (numeric) bit mask of versionbits the server requires set in submissions\n" " \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n" - " \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n" + " \"transactions\" : [ (json array) contents of non-coinbase transactions that should be included in the next block\n" " {\n" " \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n" " \"txid\" : \"xxxx\", (string) transaction id encoded in little-endian hexadecimal\n" " \"hash\" : \"xxxx\", (string) hash encoded in little-endian hexadecimal (including witness data)\n" - " \"depends\" : [ (array) array of numbers \n" + " \"depends\" : [ (json array) array of numbers \n" " n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n" " ,...\n" " ],\n" - " \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n" + " \"fee\" : n, (numeric) difference in value between transaction inputs and outputs (in satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n" " \"sigops\" : n, (numeric) total SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero\n" " \"weight\" : n, (numeric) total transaction weight, as counted for purposes of block limits\n" " }\n" @@ -850,7 +850,7 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request) RPCResult{ "{\n" " \"feerate\" : x.x, (numeric, optional) estimate fee rate in " + CURRENCY_UNIT + "/kB\n" - " \"errors\": [ str... ] (json array of strings, optional) Errors encountered during processing\n" + " \"errors\" : [ str... ] (json array of strings, optional) Errors encountered during processing\n" " \"blocks\" : n (numeric) block number where estimate was found\n" "}\n" "\n" @@ -924,7 +924,7 @@ static UniValue estimaterawfee(const JSONRPCRequest& request) " \"fail\" : { (json object, optional) information about the highest range of feerates to fail to meet the threshold\n" " ...\n" " },\n" - " \"errors\": [ (json array, optional) Errors encountered during processing\n" + " \"errors\" : [ (json array, optional) Errors encountered during processing\n" " \"str\", (string)\n" " ...\n" " ],\n" diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index f360cb7525..2b4ee62c71 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -5,10 +5,12 @@ #include <httpserver.h> #include <key_io.h> +#include <node/context.h> #include <outputtype.h> #include <rpc/blockchain.h> #include <rpc/server.h> #include <rpc/util.h> +#include <scheduler.h> #include <script/descriptor.h> #include <util/check.h> #include <util/strencodings.h> @@ -81,9 +83,9 @@ static UniValue createmultisig(const JSONRPCRequest& request) }, RPCResult{ "{\n" - " \"address\":\"multisigaddress\", (string) The value of the new multisig address.\n" - " \"redeemScript\":\"script\" (string) The string value of the hex-encoded redemption script.\n" - " \"descriptor\":\"descriptor\" (string) The descriptor for this multisig\n" + " \"address\" : \"multisigaddress\", (string) The value of the new multisig address.\n" + " \"redeemScript\" : \"script\" (string) The string value of the hex-encoded redemption script.\n" + " \"descriptor\" : \"descriptor\" (string) The descriptor for this multisig\n" "}\n" }, RPCExamples{ @@ -187,7 +189,7 @@ UniValue deriveaddresses(const JSONRPCRequest& request) {"range", RPCArg::Type::RANGE, RPCArg::Optional::OMITTED_NAMED_ARG, "If a ranged descriptor is used, this specifies the end or the range (in [begin,end] notation) to derive."}, }, RPCResult{ - "[ address ] (array) the derived addresses\n" + "[ address ] (json array) the derived addresses\n" }, RPCExamples{ "First three native segwit receive addresses\n" + @@ -371,6 +373,36 @@ static UniValue setmocktime(const JSONRPCRequest& request) return NullUniValue; } +static UniValue mockscheduler(const JSONRPCRequest& request) +{ + RPCHelpMan{"mockscheduler", + "\nBump the scheduler into the future (-regtest only)\n", + { + {"delta_time", RPCArg::Type::NUM, RPCArg::Optional::NO, "Number of seconds to forward the scheduler into the future." }, + }, + RPCResults{}, + RPCExamples{""}, + }.Check(request); + + if (!Params().IsMockableChain()) { + throw std::runtime_error("mockscheduler is for regression testing (-regtest mode) only"); + } + + // check params are valid values + RPCTypeCheck(request.params, {UniValue::VNUM}); + int64_t delta_seconds = request.params[0].get_int64(); + if ((delta_seconds <= 0) || (delta_seconds > 3600)) { + throw std::runtime_error("delta_time must be between 1 and 3600 seconds (1 hr)"); + } + + // protect against null pointer dereference + CHECK_NONFATAL(g_rpc_node); + CHECK_NONFATAL(g_rpc_node->scheduler); + g_rpc_node->scheduler->MockForward(boost::chrono::seconds(delta_seconds)); + + return NullUniValue; +} + static UniValue RPCLockedMemoryInfo() { LockedPool::Stats stats = LockedPoolManager::Instance().stats(); @@ -418,13 +450,13 @@ static UniValue getmemoryinfo(const JSONRPCRequest& request) { RPCResult{"mode \"stats\"", "{\n" - " \"locked\": { (json object) Information about locked memory manager\n" - " \"used\": xxxxx, (numeric) Number of bytes used\n" - " \"free\": xxxxx, (numeric) Number of bytes available in current arenas\n" - " \"total\": xxxxxxx, (numeric) Total number of bytes managed\n" - " \"locked\": xxxxxx, (numeric) Amount of bytes that succeeded locking. If this number is smaller than total, locking pages failed at some point and key data could be swapped to disk.\n" - " \"chunks_used\": xxxxx, (numeric) Number allocated chunks\n" - " \"chunks_free\": xxxxx, (numeric) Number unused chunks\n" + " \"locked\" : { (json object) Information about locked memory manager\n" + " \"used\" : xxxxx, (numeric) Number of bytes used\n" + " \"free\" : xxxxx, (numeric) Number of bytes available in current arenas\n" + " \"total\" : xxxxxxx, (numeric) Total number of bytes managed\n" + " \"locked\" : xxxxxx, (numeric) Amount of bytes that succeeded locking. If this number is smaller than total, locking pages failed at some point and key data could be swapped to disk.\n" + " \"chunks_used\" : xxxxx, (numeric) Number allocated chunks\n" + " \"chunks_free\" : xxxxx, (numeric) Number unused chunks\n" " }\n" "}\n" }, @@ -497,7 +529,7 @@ UniValue logging(const JSONRPCRequest& request) }, RPCResult{ "{ (json object where keys are the logging categories, and values indicates its status\n" - " \"category\": true|false, (bool) if being debug logged or not. false:inactive, true:active\n" + " \"category\" : true|false, (boolean) if being debug logged or not. false:inactive, true:active\n" " ...\n" "}\n" }, @@ -575,6 +607,7 @@ static const CRPCCommand commands[] = /* Not shown in help */ { "hidden", "setmocktime", &setmocktime, {"timestamp"}}, + { "hidden", "mockscheduler", &mockscheduler, {"delta_time"}}, { "hidden", "echo", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, { "hidden", "echojson", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, }; diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 42aec08b45..92542539df 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -79,48 +79,48 @@ static UniValue getpeerinfo(const JSONRPCRequest& request) RPCResult{ "[\n" " {\n" - " \"id\": n, (numeric) Peer index\n" - " \"addr\":\"host:port\", (string) The IP address and port of the peer\n" - " \"addrbind\":\"ip:port\", (string) Bind address of the connection to the peer\n" - " \"addrlocal\":\"ip:port\", (string) Local address as reported by the peer\n" - " \"mapped_as\":\"mapped_as\", (string) The AS in the BGP route to the peer used for diversifying peer selection\n" - " \"services\":\"xxxxxxxxxxxxxxxx\", (string) The services offered\n" - " \"servicesnames\":[ (array) the services offered, in human-readable form\n" + " \"id\" : n, (numeric) Peer index\n" + " \"addr\" : \"host:port\", (string) The IP address and port of the peer\n" + " \"addrbind\" : \"ip:port\", (string) Bind address of the connection to the peer\n" + " \"addrlocal\" : \"ip:port\", (string) Local address as reported by the peer\n" + " \"mapped_as\" : \"mapped_as\", (string) The AS in the BGP route to the peer used for diversifying peer selection\n" + " \"services\" : \"xxxxxxxxxxxxxxxx\", (string) The services offered\n" + " \"servicesnames\" : [ (json array) the services offered, in human-readable form\n" " \"SERVICE_NAME\", (string) the service name if it is recognised\n" " ...\n" " ],\n" - " \"relaytxes\":true|false, (boolean) Whether peer has asked us to relay transactions to it\n" - " \"lastsend\": ttt, (numeric) The " + UNIX_EPOCH_TIME + " of the last send\n" - " \"lastrecv\": ttt, (numeric) The " + UNIX_EPOCH_TIME + " of the last receive\n" - " \"bytessent\": n, (numeric) The total bytes sent\n" - " \"bytesrecv\": n, (numeric) The total bytes received\n" - " \"conntime\": ttt, (numeric) The " + UNIX_EPOCH_TIME + " of the connection\n" - " \"timeoffset\": ttt, (numeric) The time offset in seconds\n" - " \"pingtime\": n, (numeric) ping time (if available)\n" - " \"minping\": n, (numeric) minimum observed ping time (if any at all)\n" - " \"pingwait\": n, (numeric) ping wait (if non-zero)\n" - " \"version\": v, (numeric) The peer version, such as 70001\n" - " \"subver\": \"/Satoshi:0.8.5/\", (string) The string version\n" - " \"inbound\": true|false, (boolean) Inbound (true) or Outbound (false)\n" - " \"addnode\": true|false, (boolean) Whether connection was due to addnode/-connect or if it was an automatic/inbound connection\n" - " \"startingheight\": n, (numeric) The starting height (block) of the peer\n" - " \"banscore\": n, (numeric) The ban score\n" - " \"synced_headers\": n, (numeric) The last header we have in common with this peer\n" - " \"synced_blocks\": n, (numeric) The last block we have in common with this peer\n" - " \"inflight\": [\n" + " \"relaytxes\" : true|false, (boolean) Whether peer has asked us to relay transactions to it\n" + " \"lastsend\" : ttt, (numeric) The " + UNIX_EPOCH_TIME + " of the last send\n" + " \"lastrecv\" : ttt, (numeric) The " + UNIX_EPOCH_TIME + " of the last receive\n" + " \"bytessent\" : n, (numeric) The total bytes sent\n" + " \"bytesrecv\" : n, (numeric) The total bytes received\n" + " \"conntime\" : ttt, (numeric) The " + UNIX_EPOCH_TIME + " of the connection\n" + " \"timeoffset\" : ttt, (numeric) The time offset in seconds\n" + " \"pingtime\" : n, (numeric) ping time (if available)\n" + " \"minping\" : n, (numeric) minimum observed ping time (if any at all)\n" + " \"pingwait\" : n, (numeric) ping wait (if non-zero)\n" + " \"version\" : v, (numeric) The peer version, such as 70001\n" + " \"subver\" : \"/Satoshi:0.8.5/\", (string) The string version\n" + " \"inbound\" : true|false, (boolean) Inbound (true) or Outbound (false)\n" + " \"addnode\" : true|false, (boolean) Whether connection was due to addnode/-connect or if it was an automatic/inbound connection\n" + " \"startingheight\" : n, (numeric) The starting height (block) of the peer\n" + " \"banscore\" : n, (numeric) The ban score\n" + " \"synced_headers\" : n, (numeric) The last header we have in common with this peer\n" + " \"synced_blocks\" : n, (numeric) The last block we have in common with this peer\n" + " \"inflight\" : [\n" " n, (numeric) The heights of blocks we're currently asking from this peer\n" " ...\n" " ],\n" - " \"whitelisted\": true|false, (boolean) Whether the peer is whitelisted\n" - " \"minfeefilter\": n, (numeric) The minimum fee rate for transactions this peer accepts\n" - " \"bytessent_per_msg\": {\n" - " \"msg\": n, (numeric) The total bytes sent aggregated by message type\n" + " \"whitelisted\" : true|false, (boolean) Whether the peer is whitelisted\n" + " \"minfeefilter\" : n, (numeric) The minimum fee rate for transactions this peer accepts\n" + " \"bytessent_per_msg\" : {\n" + " \"msg\" : n, (numeric) The total bytes sent aggregated by message type\n" " When a message type is not listed in this json object, the bytes sent are 0.\n" " Only known message types can appear as keys in the object.\n" " ...\n" " },\n" - " \"bytesrecv_per_msg\": {\n" - " \"msg\": n, (numeric) The total bytes received aggregated by message type\n" + " \"bytesrecv_per_msg\" : {\n" + " \"msg\" : n, (numeric) The total bytes received aggregated by message type\n" " When a message type is not listed in this json object, the bytes received are 0.\n" " Only known message types can appear as keys in the object and all bytes received of unknown message types are listed under '"+NET_MESSAGE_COMMAND_OTHER+"'.\n" " ...\n" @@ -387,17 +387,17 @@ static UniValue getnettotals(const JSONRPCRequest& request) {}, RPCResult{ "{\n" - " \"totalbytesrecv\": n, (numeric) Total bytes received\n" - " \"totalbytessent\": n, (numeric) Total bytes sent\n" - " \"timemillis\": t, (numeric) Current UNIX time in milliseconds\n" - " \"uploadtarget\":\n" + " \"totalbytesrecv\" : n, (numeric) Total bytes received\n" + " \"totalbytessent\" : n, (numeric) Total bytes sent\n" + " \"timemillis\" : t, (numeric) Current UNIX time in milliseconds\n" + " \"uploadtarget\" : \n" " {\n" - " \"timeframe\": n, (numeric) Length of the measuring timeframe in seconds\n" - " \"target\": n, (numeric) Target in bytes\n" - " \"target_reached\": true|false, (boolean) True if target is reached\n" - " \"serve_historical_blocks\": true|false, (boolean) True if serving historical blocks\n" - " \"bytes_left_in_cycle\": t, (numeric) Bytes left in current time cycle\n" - " \"time_left_in_cycle\": t (numeric) Seconds left in current time cycle\n" + " \"timeframe\" : n, (numeric) Length of the measuring timeframe in seconds\n" + " \"target\" : n, (numeric) Target in bytes\n" + " \"target_reached\" : true|false, (boolean) True if target is reached\n" + " \"serve_historical_blocks\" : true|false, (boolean) True if serving historical blocks\n" + " \"bytes_left_in_cycle\" : t, (numeric) Bytes left in current time cycle\n" + " \"time_left_in_cycle\" : t (numeric) Seconds left in current time cycle\n" " }\n" "}\n" }, @@ -453,35 +453,35 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request) {}, RPCResult{ "{ (json object)\n" - " \"version\": xxxxx, (numeric) the server version\n" + " \"version\" : xxxxx, (numeric) the server version\n" " \"subversion\" : \"str\", (string) the server subversion string\n" - " \"protocolversion\": xxxxx, (numeric) the protocol version\n" + " \"protocolversion\" : xxxxx, (numeric) the protocol version\n" " \"localservices\" : \"hex\", (string) the services we offer to the network\n" - " \"localservicesnames\": [ (array) the services we offer to the network, in human-readable form\n" + " \"localservicesnames\" : [ (json array) the services we offer to the network, in human-readable form\n" " \"SERVICE_NAME\", (string) the service name\n" " ...\n" " ],\n" - " \"localrelay\": true|false, (bool) true if transaction relay is requested from peers\n" - " \"timeoffset\": xxxxx, (numeric) the time offset\n" - " \"connections\": xxxxx, (numeric) the number of connections\n" - " \"networkactive\": true|false, (bool) whether p2p networking is enabled\n" - " \"networks\": [ (array) information per network\n" + " \"localrelay\" : true|false, (boolean) true if transaction relay is requested from peers\n" + " \"timeoffset\" : xxxxx, (numeric) the time offset\n" + " \"connections\" : xxxxx, (numeric) the number of connections\n" + " \"networkactive\" : true|false, (boolean) whether p2p networking is enabled\n" + " \"networks\" : [ (json array) information per network\n" " { (json object)\n" - " \"name\": \"str\", (string) network (ipv4, ipv6 or onion)\n" - " \"limited\": true|false, (boolean) is the network limited using -onlynet?\n" - " \"reachable\": true|false, (boolean) is the network reachable?\n" + " \"name\" : \"str\", (string) network (ipv4, ipv6 or onion)\n" + " \"limited\" : true|false, (boolean) is the network limited using -onlynet?\n" + " \"reachable\" : true|false, (boolean) is the network reachable?\n" " \"proxy\" : \"str\" (string) (\"host:port\") the proxy that is used for this network, or empty if none\n" - " \"proxy_randomize_credentials\" : true|false, (bool) Whether randomized credentials are used\n" + " \"proxy_randomize_credentials\" : true|false, (boolean) Whether randomized credentials are used\n" " },\n" " ...\n" " ],\n" - " \"relayfee\": x.xxxxxxxx, (numeric) minimum relay fee for transactions in " + CURRENCY_UNIT + "/kB\n" - " \"incrementalfee\": x.xxxxxxxx, (numeric) minimum fee increment for mempool limiting or BIP 125 replacement in " + CURRENCY_UNIT + "/kB\n" - " \"localaddresses\": [ (array) list of local addresses\n" + " \"relayfee\" : x.xxxxxxxx, (numeric) minimum relay fee for transactions in " + CURRENCY_UNIT + "/kB\n" + " \"incrementalfee\" : x.xxxxxxxx, (numeric) minimum fee increment for mempool limiting or BIP 125 replacement in " + CURRENCY_UNIT + "/kB\n" + " \"localaddresses\" : [ (json array) list of local addresses\n" " { (json object)\n" " \"address\" : \"xxxx\", (string) network address\n" - " \"port\": xxx, (numeric) network port\n" - " \"score\": xxx (numeric) relative score\n" + " \"port\" : xxx, (numeric) network port\n" + " \"score\" : xxx (numeric) relative score\n" " },\n" " ...\n" " ],\n" @@ -695,10 +695,10 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request) RPCResult{ "[\n" " {\n" - " \"time\": ttt, (numeric) The " + UNIX_EPOCH_TIME + " of when the node was last seen\n" - " \"services\": n, (numeric) The services offered\n" - " \"address\": \"host\", (string) The address of the node\n" - " \"port\": n (numeric) The port of the node\n" + " \"time\" : ttt, (numeric) The " + UNIX_EPOCH_TIME + " of when the node was last seen\n" + " \"services\" : n, (numeric) The services offered\n" + " \"address\" : \"host\", (string) The address of the node\n" + " \"port\" : n (numeric) The port of the node\n" " }\n" " ,....\n" "]\n" diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 972809a65b..cd1c657c26 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -98,7 +98,7 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) }, RPCResult{"if verbose is set to true", "{\n" - " \"in_active_chain\": b, (bool) Whether specified block is in the active chain or not (only present with explicit \"blockhash\" argument)\n" + " \"in_active_chain\" : b, (boolean) Whether specified block is in the active chain or not (only present with explicit \"blockhash\" argument)\n" " \"hex\" : \"data\", (string) The serialized, hex-encoded data for 'txid'\n" " \"txid\" : \"id\", (string) The transaction id (same as provided)\n" " \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n" @@ -109,14 +109,14 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) " \"locktime\" : ttt, (numeric) The lock time\n" " \"vin\" : [ (array of json objects)\n" " {\n" - " \"txid\": \"id\", (string) The transaction id\n" - " \"vout\": n, (numeric) \n" - " \"scriptSig\": { (json object) The script\n" - " \"asm\": \"asm\", (string) asm\n" - " \"hex\": \"hex\" (string) hex\n" + " \"txid\" : \"id\", (string) The transaction id\n" + " \"vout\" : n, (numeric) \n" + " \"scriptSig\" : { (json object) The script\n" + " \"asm\" : \"asm\", (string) asm\n" + " \"hex\" : \"hex\" (string) hex\n" " },\n" - " \"sequence\": n (numeric) The script sequence number\n" - " \"txinwitness\": [\"hex\", ...] (array of string) hex-encoded witness data (if any)\n" + " \"sequence\" : n (numeric) The script sequence number\n" + " \"txinwitness\" : [\"hex\", ...] (array of string) hex-encoded witness data (if any)\n" " }\n" " ,...\n" " ],\n" @@ -442,14 +442,14 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request) " \"locktime\" : ttt, (numeric) The lock time\n" " \"vin\" : [ (array of json objects)\n" " {\n" - " \"txid\": \"id\", (string) The transaction id\n" - " \"vout\": n, (numeric) The output number\n" - " \"scriptSig\": { (json object) The script\n" - " \"asm\": \"asm\", (string) asm\n" - " \"hex\": \"hex\" (string) hex\n" + " \"txid\" : \"id\", (string) The transaction id\n" + " \"vout\" : n, (numeric) The output number\n" + " \"scriptSig\" : { (json object) The script\n" + " \"asm\" : \"asm\", (string) asm\n" + " \"hex\" : \"hex\" (string) hex\n" " },\n" - " \"txinwitness\": [\"hex\", ...] (array of string) hex-encoded witness data (if any)\n" - " \"sequence\": n (numeric) The script sequence number\n" + " \"txinwitness\" : [\"hex\", ...] (array of string) hex-encoded witness data (if any)\n" + " \"sequence\" : n (numeric) The script sequence number\n" " }\n" " ,...\n" " ],\n" @@ -514,20 +514,20 @@ static UniValue decodescript(const JSONRPCRequest& request) }, RPCResult{ "{\n" - " \"asm\":\"asm\", (string) Script public key\n" - " \"type\":\"type\", (string) The output type (e.g. "+GetAllOutputTypes()+")\n" - " \"reqSigs\": n, (numeric) The required signatures\n" - " \"addresses\": [ (json array of string)\n" + " \"asm\" : \"asm\", (string) Script public key\n" + " \"type\" : \"type\", (string) The output type (e.g. "+GetAllOutputTypes()+")\n" + " \"reqSigs\" : n, (numeric) The required signatures\n" + " \"addresses\" : [ (json array of string)\n" " \"address\" (string) bitcoin address\n" " ,...\n" " ],\n" " \"p2sh\":\"str\" (string) address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH).\n" - " \"segwit\": { (json object) Result of a witness script public key wrapping this redeem script (not returned if the script is a P2SH or witness).\n" - " \"asm\":\"str\", (string) String representation of the script public key\n" - " \"hex\":\"hexstr\", (string) Hex string of the script public key\n" - " \"type\":\"str\", (string) The type of the script public key (e.g. witness_v0_keyhash or witness_v0_scripthash)\n" - " \"reqSigs\": n, (numeric) The required signatures (always 1)\n" - " \"addresses\": [ (json array of string) (always length 1)\n" + " \"segwit\" : { (json object) Result of a witness script public key wrapping this redeem script (not returned if the script is a P2SH or witness).\n" + " \"asm\" : \"str\", (string) String representation of the script public key\n" + " \"hex\" : \"hexstr\", (string) Hex string of the script public key\n" + " \"type\" : \"str\", (string) The type of the script public key (e.g. witness_v0_keyhash or witness_v0_scripthash)\n" + " \"reqSigs\" : n, (numeric) The required signatures (always 1)\n" + " \"addresses\" : [ (json array of string) (always length 1)\n" " \"address\" (string) segwit address\n" " ,...\n" " ],\n" @@ -846,7 +846,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request) {"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK()), "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kB\n"}, }, RPCResult{ - "[ (array) The result of the mempool acceptance test for each raw transaction in the input array.\n" + "[ (json array) The result of the mempool acceptance test for each raw transaction in the input array.\n" " Length is exactly one for now.\n" " {\n" " \"txid\" (string) The transaction hash in hex\n" @@ -998,7 +998,7 @@ UniValue decodepsbt(const JSONRPCRequest& request) " \"asm\" : \"asm\", (string) The asm\n" " \"hex\" : \"hex\", (string) The hex\n" " }\n" - " \"final_scriptwitness\": [\"hex\", ...] (array of string) hex-encoded witness data (if any)\n" + " \"final_scriptwitness\" : [\"hex\", ...] (array of string) hex-encoded witness data (if any)\n" " \"unknown\" : { (json object) The unknown global fields\n" " \"key\" : \"value\" (key-value pair) An unknown key-value pair\n" " ...\n" diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index df8e687d82..b62490ed29 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -198,14 +198,14 @@ static UniValue getrpcinfo(const JSONRPCRequest& request) {}, RPCResult{ "{\n" - " \"active_commands\" (array) All active commands\n" + " \"active_commands\" (json array) All active commands\n" " [\n" - " { (object) Information about an active command\n" + " { (json object) Information about an active command\n" " \"method\" (string) The name of the RPC command \n" " \"duration\" (numeric) The running time in microseconds\n" " },...\n" " ],\n" - " \"logpath\": \"xxx\" (string) The complete file path to the debug log\n" + " \"logpath\" : \"xxx\" (string) The complete file path to the debug log\n" "}\n" }, RPCExamples{ diff --git a/src/scheduler.cpp b/src/scheduler.cpp index 927a3f3820..72cca89d99 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -114,6 +114,28 @@ void CScheduler::scheduleFromNow(CScheduler::Function f, int64_t deltaMilliSecon schedule(f, boost::chrono::system_clock::now() + boost::chrono::milliseconds(deltaMilliSeconds)); } +void CScheduler::MockForward(boost::chrono::seconds delta_seconds) +{ + assert(delta_seconds.count() > 0 && delta_seconds < boost::chrono::hours{1}); + + { + boost::unique_lock<boost::mutex> lock(newTaskMutex); + + // use temp_queue to maintain updated schedule + std::multimap<boost::chrono::system_clock::time_point, Function> temp_queue; + + for (const auto& element : taskQueue) { + temp_queue.emplace_hint(temp_queue.cend(), element.first - delta_seconds, element.second); + } + + // point taskQueue to temp_queue + taskQueue = std::move(temp_queue); + } + + // notify that the taskQueue needs to be processed + newTaskScheduled.notify_one(); +} + static void Repeat(CScheduler* s, CScheduler::Function f, int64_t deltaMilliSeconds) { f(); diff --git a/src/scheduler.h b/src/scheduler.h index 7080adf34c..d18be0ea5e 100644 --- a/src/scheduler.h +++ b/src/scheduler.h @@ -55,6 +55,13 @@ public: // need more accurate scheduling, don't use this method. void scheduleEvery(Function f, int64_t deltaMilliSeconds); + /** + * Mock the scheduler to fast forward in time. + * Iterates through items on taskQueue and reschedules them + * to be delta_seconds sooner. + */ + void MockForward(boost::chrono::seconds delta_seconds); + // To keep things as simple as possible, there is no unschedule. // Services the queue 'forever'. Should be run in a thread, diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp index 2c2b3035e3..e5d51ab83b 100644 --- a/src/test/denialofservice_tests.cpp +++ b/src/test/denialofservice_tests.cpp @@ -78,7 +78,7 @@ BOOST_FIXTURE_TEST_SUITE(denialofservice_tests, TestingSetup) BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction) { auto connman = MakeUnique<CConnman>(0x1337, 0x1337); - auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), nullptr, scheduler); + auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), nullptr, *m_node.scheduler); // Mock an outbound peer CAddress addr1(ip(0xa0b0c001), NODE_NONE); @@ -148,7 +148,7 @@ static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerLogicValidat BOOST_AUTO_TEST_CASE(stale_tip_peer_management) { auto connman = MakeUnique<CConnmanTest>(0x1337, 0x1337); - auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), nullptr, scheduler); + auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), nullptr, *m_node.scheduler); const Consensus::Params& consensusParams = Params().GetConsensus(); constexpr int max_outbound_full_relay = 8; @@ -221,7 +221,7 @@ BOOST_AUTO_TEST_CASE(DoS_banning) { auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); auto connman = MakeUnique<CConnman>(0x1337, 0x1337); - auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), scheduler); + auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), *m_node.scheduler); banman->ClearBanned(); CAddress addr1(ip(0xa0b0c001), NODE_NONE); @@ -276,7 +276,7 @@ BOOST_AUTO_TEST_CASE(DoS_banscore) { auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); auto connman = MakeUnique<CConnman>(0x1337, 0x1337); - auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), scheduler); + auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), *m_node.scheduler); banman->ClearBanned(); gArgs.ForceSetArg("-banscore", "111"); // because 11 is my favorite number @@ -323,7 +323,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime) { auto banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); auto connman = MakeUnique<CConnman>(0x1337, 0x1337); - auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), scheduler); + auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), banman.get(), *m_node.scheduler); banman->ClearBanned(); int64_t nStartTime = GetTime(); diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp index b292d5b0d0..a6cb34cf28 100644 --- a/src/test/scheduler_tests.cpp +++ b/src/test/scheduler_tests.cpp @@ -5,8 +5,6 @@ #include <random.h> #include <scheduler.h> -#include <test/util/setup_common.h> - #include <boost/thread.hpp> #include <boost/test/unit_test.hpp> @@ -155,4 +153,45 @@ BOOST_AUTO_TEST_CASE(singlethreadedscheduler_ordered) BOOST_CHECK_EQUAL(counter2, 100); } +BOOST_AUTO_TEST_CASE(mockforward) +{ + CScheduler scheduler; + + int counter{0}; + CScheduler::Function dummy = [&counter]{counter++;}; + + // schedule jobs for 2, 5 & 8 minutes into the future + int64_t min_in_milli = 60*1000; + scheduler.scheduleFromNow(dummy, 2*min_in_milli); + scheduler.scheduleFromNow(dummy, 5*min_in_milli); + scheduler.scheduleFromNow(dummy, 8*min_in_milli); + + // check taskQueue + boost::chrono::system_clock::time_point first, last; + size_t num_tasks = scheduler.getQueueInfo(first, last); + BOOST_CHECK_EQUAL(num_tasks, 3ul); + + std::thread scheduler_thread([&]() { scheduler.serviceQueue(); }); + + // bump the scheduler forward 5 minutes + scheduler.MockForward(boost::chrono::seconds(5*60)); + + // ensure scheduler has chance to process all tasks queued for before 1 ms from now. + scheduler.scheduleFromNow([&scheduler]{ scheduler.stop(false); }, 1); + scheduler_thread.join(); + + // check that the queue only has one job remaining + num_tasks = scheduler.getQueueInfo(first, last); + BOOST_CHECK_EQUAL(num_tasks, 1ul); + + // check that the dummy function actually ran + BOOST_CHECK_EQUAL(counter, 2); + + // check that the time of the remaining job has been updated + boost::chrono::system_clock::time_point now = boost::chrono::system_clock::now(); + int delta = boost::chrono::duration_cast<boost::chrono::seconds>(first - now).count(); + // should be between 2 & 3 minutes from now + BOOST_CHECK(delta > 2*60 && delta < 3*60); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index ccb3064d59..360377e58a 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -103,10 +103,12 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha g_rpc_node = &m_node; RegisterAllCoreRPCCommands(tableRPC); + m_node.scheduler = MakeUnique<CScheduler>(); + // We have to run a scheduler thread to prevent ActivateBestChain // from blocking due to queue overrun. - threadGroup.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler)); - GetMainSignals().RegisterBackgroundSignalScheduler(scheduler); + threadGroup.create_thread([&]{ m_node.scheduler->serviceQueue(); }); + GetMainSignals().RegisterBackgroundSignalScheduler(*g_rpc_node->scheduler); pblocktree.reset(new CBlockTreeDB(1 << 20, true)); g_chainstate = MakeUnique<CChainState>(); @@ -147,6 +149,7 @@ TestingSetup::~TestingSetup() m_node.connman.reset(); m_node.banman.reset(); m_node.mempool = nullptr; + m_node.scheduler.reset(); UnloadBlockIndex(); g_chainstate.reset(); pblocktree.reset(); diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h index 6741be8480..56ad62eb24 100644 --- a/src/test/util/setup_common.h +++ b/src/test/util/setup_common.h @@ -85,7 +85,6 @@ private: struct TestingSetup : public BasicTestingSetup { NodeContext m_node; boost::thread_group threadGroup; - CScheduler scheduler; explicit TestingSetup(const std::string& chainName = CBaseChainParams::MAIN); ~TestingSetup(); diff --git a/src/util/system.cpp b/src/util/system.cpp index 13ff99c663..b0a538b527 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -63,6 +63,7 @@ #include <malloc.h> #endif +#include <boost/algorithm/string/replace.hpp> #include <thread> #include <typeinfo> #include <univalue.h> @@ -1047,6 +1048,15 @@ fs::path GetSpecialFolderPath(int nFolder, bool fCreate) } #endif +#ifndef WIN32 +std::string ShellEscape(const std::string& arg) +{ + std::string escaped = arg; + boost::replace_all(escaped, "'", "'\"'\"'"); + return "'" + escaped + "'"; +} +#endif + #if HAVE_SYSTEM void runCommand(const std::string& strCommand) { diff --git a/src/util/system.h b/src/util/system.h index bb69181de9..3138522b5c 100644 --- a/src/util/system.h +++ b/src/util/system.h @@ -81,6 +81,9 @@ fs::path GetConfigFile(const std::string& confPath); #ifdef WIN32 fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true); #endif +#ifndef WIN32 +std::string ShellEscape(const std::string& arg); +#endif #if HAVE_SYSTEM void runCommand(const std::string& strCommand); #endif diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 2ebc9aba39..50f064b305 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -62,7 +62,7 @@ void WalletInit::AddWalletOptions() const gArgs.AddArg("-walletbroadcast", strprintf("Make the wallet broadcast transactions (default: %u)", DEFAULT_WALLETBROADCAST), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); gArgs.AddArg("-walletdir=<dir>", "Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET); #if HAVE_SYSTEM - gArgs.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); + gArgs.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes. %s in cmd is replaced by TxID and %w is replaced by wallet name. %w is not currently implemented on windows. On systems where %w is supported, it should NOT be quoted because this would break shell escaping used to invoke the command.", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); #endif gArgs.AddArg("-walletrbf", strprintf("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)", DEFAULT_WALLET_RBF), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); gArgs.AddArg("-zapwallettxes=<mode>", "Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup" diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index dc9124e1a3..bc4ec77e31 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -972,9 +972,9 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request) }, RPCResult{ "{\n" - " \"address\":\"multisigaddress\", (string) The value of the new multisig address.\n" - " \"redeemScript\":\"script\" (string) The string value of the hex-encoded redemption script.\n" - " \"descriptor\":\"descriptor\" (string) The descriptor for this multisig\n" + " \"address\" : \"multisigaddress\", (string) The value of the new multisig address.\n" + " \"redeemScript\" : \"script\" (string) The string value of the hex-encoded redemption script.\n" + " \"descriptor\" : \"descriptor\" (string) The descriptor for this multisig\n" "}\n" }, RPCExamples{ @@ -1206,12 +1206,12 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request) RPCResult{ "[\n" " {\n" - " \"involvesWatchonly\" : true, (bool) Only returns true if imported addresses were involved in transaction.\n" + " \"involvesWatchonly\" : true, (boolean) Only returns true if imported addresses were involved in transaction.\n" " \"address\" : \"receivingaddress\", (string) The receiving address\n" " \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " received by the address\n" " \"confirmations\" : n, (numeric) The number of confirmations of the most recent transaction included\n" " \"label\" : \"label\", (string) The label of the receiving address. The default label is \"\".\n" - " \"txids\": [\n" + " \"txids\" : [\n" " \"txid\", (string) The ids of transactions received with the address \n" " ...\n" " ]\n" @@ -1256,7 +1256,7 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request) RPCResult{ "[\n" " {\n" - " \"involvesWatchonly\" : true, (bool) Only returns true if imported addresses were involved in transaction.\n" + " \"involvesWatchonly\" : true, (boolean) Only returns true if imported addresses were involved in transaction.\n" " \"amount\" : x.xxx, (numeric) The total amount received by addresses with this label\n" " \"confirmations\" : n, (numeric) The number of confirmations of the most recent transaction included\n" " \"label\" : \"label\" (string) The label of the receiving address. The default label is \"\".\n" @@ -1378,20 +1378,20 @@ static const std::string TransactionDescriptionString() { return " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Negative confirmations means the\n" " transaction conflicted that many blocks ago.\n" - " \"generated\": xxx, (bool) Only present if transaction only input is a coinbase one.\n" - " \"trusted\": xxx, (bool) Only present if we consider transaction to be trusted and so safe to spend from.\n" - " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction.\n" - " \"blockheight\": n, (numeric) The block height containing the transaction.\n" - " \"blockindex\": n, (numeric) The index of the transaction in the block that includes it.\n" - " \"blocktime\": xxx, (numeric) The block time expressed in " + UNIX_EPOCH_TIME + ".\n" - " \"txid\": \"transactionid\", (string) The transaction id.\n" - " \"walletconflicts\": [ (array) Conflicting transaction ids.\n" + " \"generated\" : xxx, (boolean) Only present if transaction only input is a coinbase one.\n" + " \"trusted\" : xxx, (boolean) Only present if we consider transaction to be trusted and so safe to spend from.\n" + " \"blockhash\" : \"hashvalue\", (string) The block hash containing the transaction.\n" + " \"blockheight\" : n, (numeric) The block height containing the transaction.\n" + " \"blockindex\" : n, (numeric) The index of the transaction in the block that includes it.\n" + " \"blocktime\" : xxx, (numeric) The block time expressed in " + UNIX_EPOCH_TIME + ".\n" + " \"txid\" : \"transactionid\", (string) The transaction id.\n" + " \"walletconflicts\" : [ (json array) Conflicting transaction ids.\n" " \"txid\", (string) The transaction id.\n" " ...\n" " ],\n" - " \"time\": xxx, (numeric) The transaction time expressed in " + UNIX_EPOCH_TIME + ".\n" - " \"timereceived\": xxx, (numeric) The time received expressed in " + UNIX_EPOCH_TIME + ".\n" - " \"comment\": \"...\", (string) If a comment is associated with the transaction, only present if not empty.\n" + " \"time\" : xxx, (numeric) The transaction time expressed in " + UNIX_EPOCH_TIME + ".\n" + " \"timereceived\" : xxx, (numeric) The time received expressed in " + UNIX_EPOCH_TIME + ".\n" + " \"comment\" : \"...\", (string) If a comment is associated with the transaction, only present if not empty.\n" " \"bip125-replaceable\" : \"str\", (string) (\"yes|no|unknown\") Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" " may be unknown for unconfirmed transactions not in the mempool\n"; } @@ -1418,22 +1418,22 @@ UniValue listtransactions(const JSONRPCRequest& request) RPCResult{ "[\n" " {\n" - " \"involvesWatchonly\": xxx, (bool) Only returns true if imported addresses were involved in transaction.\n" - " \"address\":\"address\", (string) The bitcoin address of the transaction.\n" - " \"category\": (string) The transaction category.\n" + " \"involvesWatchonly\" : xxx, (boolean) Only returns true if imported addresses were involved in transaction.\n" + " \"address\" : \"address\", (string) The bitcoin address of the transaction.\n" + " \"category\" : (string) The transaction category.\n" " \"send\" Transactions sent.\n" " \"receive\" Non-coinbase transactions received.\n" " \"generate\" Coinbase transactions received with more than 100 confirmations.\n" " \"immature\" Coinbase transactions received with 100 or fewer confirmations.\n" " \"orphan\" Orphaned coinbase transactions received.\n" - " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n" + " \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n" " for all other categories\n" - " \"label\": \"label\", (string) A comment for the address/transaction, if any\n" - " \"vout\": n, (numeric) the vout value\n" - " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" + " \"label\" : \"label\", (string) A comment for the address/transaction, if any\n" + " \"vout\" : n, (numeric) the vout value\n" + " \"fee\" : x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" " 'send' category of transactions.\n" + TransactionDescriptionString() - + " \"abandoned\": xxx (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n" + + " \"abandoned\": xxx (boolean) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n" " 'send' category of transactions.\n" " }\n" "]\n" @@ -1500,23 +1500,10 @@ UniValue listtransactions(const JSONRPCRequest& request) if ((nFrom + nCount) > (int)ret.size()) nCount = ret.size() - nFrom; - std::vector<UniValue> arrTmp = ret.getValues(); - - std::vector<UniValue>::iterator first = arrTmp.begin(); - std::advance(first, nFrom); - std::vector<UniValue>::iterator last = arrTmp.begin(); - std::advance(last, nFrom+nCount); - - if (last != arrTmp.end()) arrTmp.erase(last, arrTmp.end()); - if (first != arrTmp.begin()) arrTmp.erase(arrTmp.begin(), first); - - std::reverse(arrTmp.begin(), arrTmp.end()); // Return oldest to newest - - ret.clear(); - ret.setArray(); - ret.push_backV(arrTmp); - - return ret; + const std::vector<UniValue>& txs = ret.getValues(); + UniValue result{UniValue::VARR}; + result.push_backV({ txs.rend() - nFrom - nCount, txs.rend() - nFrom }); // Return oldest to newest + return result; } static UniValue listsinceblock(const JSONRPCRequest& request) @@ -1543,7 +1530,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request) "{ (json object)\n" " \"transactions\" : [ (json array)\n" " { (json object)\n" - " \"involvesWatchonly\": xxx, (bool) Only returns true if imported addresses were involved in transaction.\n" + " \"involvesWatchonly\" : xxx, (boolean) Only returns true if imported addresses were involved in transaction.\n" " \"address\" : \"str\", (string) The bitcoin address of the transaction.\n" " \"category\" : \"str\", (string) The transaction category.\n" " \"send\" Transactions sent.\n" @@ -1551,22 +1538,22 @@ static UniValue listsinceblock(const JSONRPCRequest& request) " \"generate\" Coinbase transactions received with more than 100 confirmations.\n" " \"immature\" Coinbase transactions received with 100 or fewer confirmations.\n" " \"orphan\" Orphaned coinbase transactions received.\n" - " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n" + " \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n" " for all other categories\n" " \"vout\" : n, (numeric) the vout value\n" - " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the 'send' category of transactions.\n" + " \"fee\" : x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the 'send' category of transactions.\n" + TransactionDescriptionString() - + " \"abandoned\": xxx, (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the 'send' category of transactions.\n" + + " \"abandoned\": xxx, (boolean) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the 'send' category of transactions.\n" " \"label\" : \"label\" (string) A comment for the address/transaction, if any\n" - " \"to\": \"...\", (string) If a comment to is associated with the transaction.\n" + " \"to\" : \"...\", (string) If a comment to is associated with the transaction.\n" " },\n" " ...\n" " ],\n" - " \"removed\": [ (json array)\n" + " \"removed\" : [ (json array)\n" " <structure is the same as \"transactions\" above, only present if include_removed=true>\n" " Note: transactions that were re-added in the active chain will appear as-is in this array, and may thus have a positive confirmation count.\n" " ],\n" - " \"lastblock\": \"hex\" (string) The hash of the block (target_confirmations-1) from the best block on the main chain. This is typically used to feed back into listsinceblock the next time you call it. So you would generally use a target_confirmations of say 6, so you will be continually re-notified of transactions until they've reached 6 confirmations plus any new ones\n" + " \"lastblock\" : \"hex\" (string) The hash of the block (target_confirmations-1) from the best block on the main chain. This is typically used to feed back into listsinceblock the next time you call it. So you would generally use a target_confirmations of say 6, so you will be continually re-notified of transactions until they've reached 6 confirmations plus any new ones\n" "}\n" }, RPCExamples{ @@ -1677,12 +1664,12 @@ static UniValue gettransaction(const JSONRPCRequest& request) RPCResult{ "{\n" " \"amount\" : x.xxx, (numeric) The transaction amount in " + CURRENCY_UNIT + "\n" - " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" + " \"fee\" : x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" " 'send' category of transactions.\n" + TransactionDescriptionString() + " \"details\" : [\n" " {\n" - " \"involvesWatchonly\": xxx, (bool) Only returns true if imported addresses were involved in transaction.\n" + " \"involvesWatchonly\" : xxx, (boolean) Only returns true if imported addresses were involved in transaction.\n" " \"address\" : \"address\", (string) The bitcoin address involved in the transaction\n" " \"category\" : (string) The transaction category.\n" " \"send\" Transactions sent.\n" @@ -1693,9 +1680,9 @@ static UniValue gettransaction(const JSONRPCRequest& request) " \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + "\n" " \"label\" : \"label\", (string) A comment for the address/transaction, if any\n" " \"vout\" : n, (numeric) the vout value\n" - " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" + " \"fee\" : x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" " 'send' category of transactions.\n" - " \"abandoned\": xxx (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n" + " \"abandoned\" : xxx (boolean) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n" " 'send' category of transactions.\n" " }\n" " ,...\n" @@ -2367,16 +2354,16 @@ static UniValue getbalances(const JSONRPCRequest& request) {}, RPCResult{ "{\n" - " \"mine\": { (object) balances from outputs that the wallet can sign\n" - " \"trusted\": xxx (numeric) trusted balance (outputs created by the wallet or confirmed outputs)\n" - " \"untrusted_pending\": xxx (numeric) untrusted pending balance (outputs created by others that are in the mempool)\n" - " \"immature\": xxx (numeric) balance from immature coinbase outputs\n" - " \"used\": xxx (numeric) (only present if avoid_reuse is set) balance from coins sent to addresses that were previously spent from (potentially privacy violating)\n" + " \"mine\" : { (json object) balances from outputs that the wallet can sign\n" + " \"trusted\" : xxx (numeric) trusted balance (outputs created by the wallet or confirmed outputs)\n" + " \"untrusted_pending\" : xxx (numeric) untrusted pending balance (outputs created by others that are in the mempool)\n" + " \"immature\" : xxx (numeric) balance from immature coinbase outputs\n" + " \"used\" : xxx (numeric) (only present if avoid_reuse is set) balance from coins sent to addresses that were previously spent from (potentially privacy violating)\n" " },\n" - " \"watchonly\": { (object) watchonly balances (not present if wallet does not watch anything)\n" - " \"trusted\": xxx (numeric) trusted balance (outputs created by the wallet or confirmed outputs)\n" - " \"untrusted_pending\": xxx (numeric) untrusted pending balance (outputs created by others that are in the mempool)\n" - " \"immature\": xxx (numeric) balance from immature coinbase outputs\n" + " \"watchonly\" : { (json object) watchonly balances (not present if wallet does not watch anything)\n" + " \"trusted\" : xxx (numeric) trusted balance (outputs created by the wallet or confirmed outputs)\n" + " \"untrusted_pending\" : xxx (numeric) untrusted pending balance (outputs created by others that are in the mempool)\n" + " \"immature\" : xxx (numeric) balance from immature coinbase outputs\n" " },\n" "}\n"}, RPCExamples{ @@ -2433,21 +2420,21 @@ static UniValue getwalletinfo(const JSONRPCRequest& request) {}, RPCResult{ "{\n" - " \"walletname\": xxxxx, (string) the wallet name\n" - " \"walletversion\": xxxxx, (numeric) the wallet version\n" - " \"balance\": xxxxxxx, (numeric) DEPRECATED. Identical to getbalances().mine.trusted\n" - " \"unconfirmed_balance\": xxx, (numeric) DEPRECATED. Identical to getbalances().mine.untrusted_pending\n" - " \"immature_balance\": xxxxxx, (numeric) DEPRECATED. Identical to getbalances().mine.immature\n" - " \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n" - " \"keypoololdest\": xxxxxx, (numeric) the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool\n" - " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated (only counts external keys)\n" - " \"keypoolsize_hd_internal\": xxxx, (numeric) how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)\n" - " \"unlocked_until\": ttt, (numeric) the " + UNIX_EPOCH_TIME + " until which the wallet is unlocked for transfers, or 0 if the wallet is locked\n" - " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n" - " \"hdseedid\": \"<hash160>\" (string, optional) the Hash160 of the HD seed (only present when HD is enabled)\n" - " \"private_keys_enabled\": true|false (boolean) false if privatekeys are disabled for this wallet (enforced watch-only wallet)\n" - " \"avoid_reuse\": true|false (boolean) whether this wallet tracks clean/dirty coins in terms of reuse\n" - " \"scanning\": (json object) current scanning details, or false if no scan is in progress\n" + " \"walletname\" : xxxxx, (string) the wallet name\n" + " \"walletversion\" : xxxxx, (numeric) the wallet version\n" + " \"balance\" : xxxxxxx, (numeric) DEPRECATED. Identical to getbalances().mine.trusted\n" + " \"unconfirmed_balance\" : xxx, (numeric) DEPRECATED. Identical to getbalances().mine.untrusted_pending\n" + " \"immature_balance\" : xxxxxx, (numeric) DEPRECATED. Identical to getbalances().mine.immature\n" + " \"txcount\" : xxxxxxx, (numeric) the total number of transactions in the wallet\n" + " \"keypoololdest\" : xxxxxx, (numeric) the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool\n" + " \"keypoolsize\" : xxxx, (numeric) how many new keys are pre-generated (only counts external keys)\n" + " \"keypoolsize_hd_internal\" : xxxx, (numeric) how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)\n" + " \"unlocked_until\" : ttt, (numeric) the " + UNIX_EPOCH_TIME + " until which the wallet is unlocked for transfers, or 0 if the wallet is locked\n" + " \"paytxfee\" : x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n" + " \"hdseedid\" : \"<hash160>\" (string, optional) the Hash160 of the HD seed (only present when HD is enabled)\n" + " \"private_keys_enabled\" : true|false (boolean) false if privatekeys are disabled for this wallet (enforced watch-only wallet)\n" + " \"avoid_reuse\" : true|false (boolean) whether this wallet tracks clean/dirty coins in terms of reuse\n" + " \"scanning\" : (json object) current scanning details, or false if no scan is in progress\n" " {\n" " \"duration\" : xxxx (numeric) elapsed seconds since scan start\n" " \"progress\" : x.xxxx, (numeric) scanning progress percentage [0.0, 1.0]\n" @@ -2640,9 +2627,9 @@ static UniValue setwalletflag(const JSONRPCRequest& request) }, RPCResult{ "{\n" - " \"flag_name\": string (string) The name of the flag that was modified\n" - " \"flag_state\": bool (bool) The new state of the flag\n" - " \"warnings\": string (string) Any warnings associated with the change\n" + " \"flag_name\" : string (string) The name of the flag that was modified\n" + " \"flag_state\" : bool (boolean) The new state of the flag\n" + " \"warnings\" : string (string) Any warnings associated with the change\n" "}\n" }, RPCExamples{ @@ -2839,11 +2826,11 @@ static UniValue listunspent(const JSONRPCRequest& request) " \"confirmations\" : n, (numeric) The number of confirmations\n" " \"redeemScript\" : \"script\" (string) The redeemScript if scriptPubKey is P2SH\n" " \"witnessScript\" : \"script\" (string) witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH\n" - " \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n" - " \"solvable\" : xxx, (bool) Whether we know how to spend this output, ignoring the lack of keys\n" - " \"reused\" : xxx, (bool) (only present if avoid_reuse is set) Whether this output is reused/dirty (sent to an address that was previously spent from)\n" + " \"spendable\" : xxx, (boolean) Whether we have the private keys to spend this output\n" + " \"solvable\" : xxx, (boolean) Whether we know how to spend this output, ignoring the lack of keys\n" + " \"reused\" : xxx, (boolean) (only present if avoid_reuse is set) Whether this output is reused/dirty (sent to an address that was previously spent from)\n" " \"desc\" : xxx, (string, only when solvable) A descriptor for spending this output\n" - " \"safe\" : xxx (bool) Whether this output is considered safe to spend. Unconfirmed transactions\n" + " \"safe\" : xxx (boolean) Whether this output is considered safe to spend. Unconfirmed transactions\n" " from outside keys and unconfirmed replacement transactions are considered unsafe\n" " and are not eligible for spending by fundrawtransaction and sendtoaddress.\n" " }\n" @@ -3187,9 +3174,9 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request) }, RPCResult{ "{\n" - " \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n" - " \"fee\": n, (numeric) Fee in " + CURRENCY_UNIT + " the resulting transaction pays\n" - " \"changepos\": n (numeric) The position of the added change output, or -1\n" + " \"hex\" : \"value\", (string) The resulting raw transaction (hex-encoded string)\n" + " \"fee\" : n, (numeric) Fee in " + CURRENCY_UNIT + " the resulting transaction pays\n" + " \"changepos\" : n (numeric) The position of the added change output, or -1\n" "}\n" }, RPCExamples{ @@ -3376,11 +3363,11 @@ static UniValue bumpfee(const JSONRPCRequest& request) }, RPCResult{ "{\n" - " \"psbt\": \"psbt\", (string) The base64-encoded unsigned PSBT of the new transaction. Only returned when wallet private keys are disabled.\n" - " \"txid\": \"value\", (string) The id of the new transaction. Only returned when wallet private keys are enabled.\n" - " \"origfee\": n, (numeric) The fee of the replaced transaction.\n" - " \"fee\": n, (numeric) The fee of the new transaction.\n" - " \"errors\": [ str... ] (json array of strings) Errors encountered during processing (may be empty).\n" + " \"psbt\" : \"psbt\", (string) The base64-encoded unsigned PSBT of the new transaction. Only returned when wallet private keys are disabled.\n" + " \"txid\" : \"value\", (string) The id of the new transaction. Only returned when wallet private keys are enabled.\n" + " \"origfee\" : n, (numeric) The fee of the replaced transaction.\n" + " \"fee\" : n, (numeric) The fee of the new transaction.\n" + " \"errors\" : [ str... ] (json array of strings) Errors encountered during processing (may be empty).\n" "}\n" }, RPCExamples{ @@ -3778,7 +3765,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request) " \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath, if the key is HD and available.\n" " \"hdseedid\" : \"<hash160>\" (string, optional) The Hash160 of the HD seed.\n" " \"hdmasterfingerprint\" : \"<hash160>\" (string, optional) The fingerprint of the master key.\n" - " \"labels\" (array) Array of labels associated with the address. Currently limited to one label but returned\n" + " \"labels\" (json array) Array of labels associated with the address. Currently limited to one label but returned\n" " as an array to keep the API stable if multiple labels are enabled in the future.\n" " [\n" " \"label name\" (string) The label name. Defaults to \"\".\n" @@ -3886,8 +3873,8 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request) }, RPCResult{ "{ (json object with addresses as keys)\n" - " \"address\": { (json object with information about address)\n" - " \"purpose\": \"string\" (string) Purpose of address (\"send\" for sending address, \"receive\" for receiving address)\n" + " \"address\" : { (json object with information about address)\n" + " \"purpose\" : \"string\" (string) Purpose of address (\"send\" for sending address, \"receive\" for receiving address)\n" " },...\n" "}\n" }, @@ -4197,9 +4184,9 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request) }, RPCResult{ "{\n" - " \"psbt\": \"value\", (string) The resulting raw transaction (base64-encoded string)\n" - " \"fee\": n, (numeric) Fee in " + CURRENCY_UNIT + " the resulting transaction pays\n" - " \"changepos\": n (numeric) The position of the added change output, or -1\n" + " \"psbt\" : \"value\", (string) The resulting raw transaction (base64-encoded string)\n" + " \"fee\" : n, (numeric) Fee in " + CURRENCY_UNIT + " the resulting transaction pays\n" + " \"changepos\" : n (numeric) The position of the added change output, or -1\n" "}\n" }, RPCExamples{ diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 405afb6d8d..6b9f53f7c5 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -847,6 +847,14 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) if (!strCmd.empty()) { boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); +#ifndef WIN32 + // Substituting the wallet name isn't currently supported on windows + // because windows shell escaping has not been implemented yet: + // https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-537384875 + // A few ways it could be implemented in the future are described in: + // https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-461288094 + boost::replace_all(strCmd, "%w", ShellEscape(GetName())); +#endif std::thread t(runCommand, strCmd); t.detach(); // thread runs free } diff --git a/src/zmq/zmqrpc.cpp b/src/zmq/zmqrpc.cpp index 5652877f3c..9c9b27a413 100644 --- a/src/zmq/zmqrpc.cpp +++ b/src/zmq/zmqrpc.cpp @@ -21,9 +21,9 @@ UniValue getzmqnotifications(const JSONRPCRequest& request) RPCResult{ "[\n" " { (json object)\n" - " \"type\": \"pubhashtx\", (string) Type of notification\n" - " \"address\": \"...\", (string) Address of the publisher\n" - " \"hwm\": n (numeric) Outbound message high water mark\n" + " \"type\" : \"pubhashtx\", (string) Type of notification\n" + " \"address\" : \"...\", (string) Address of the publisher\n" + " \"hwm\" : n (numeric) Outbound message high water mark\n" " },\n" " ...\n" "]\n" |