aboutsummaryrefslogtreecommitdiff
path: root/src/rpc
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc')
-rw-r--r--src/rpc/blockchain.cpp88
-rw-r--r--src/rpc/client.cpp7
-rw-r--r--src/rpc/external_signer.cpp2
-rw-r--r--src/rpc/fees.cpp10
-rw-r--r--src/rpc/mempool.cpp52
-rw-r--r--src/rpc/mining.cpp26
-rw-r--r--src/rpc/net.cpp42
-rw-r--r--src/rpc/node.cpp11
-rw-r--r--src/rpc/output_script.cpp6
-rw-r--r--src/rpc/register.h2
-rw-r--r--src/rpc/server.cpp2
-rw-r--r--src/rpc/signmessage.cpp6
-rw-r--r--src/rpc/util.cpp53
-rw-r--r--src/rpc/util.h71
14 files changed, 272 insertions, 106 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 34d308211b..c9997ae063 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -17,6 +17,7 @@
#include <core_io.h>
#include <deploymentinfo.h>
#include <deploymentstatus.h>
+#include <flatfile.h>
#include <hash.h>
#include <index/blockfilterindex.h>
#include <index/coinstatsindex.h>
@@ -46,7 +47,6 @@
#include <validation.h>
#include <validationinterface.h>
#include <versionbits.h>
-#include <warnings.h>
#include <stdint.h>
@@ -395,7 +395,8 @@ static RPCHelpMan syncwithvalidationinterfacequeue()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- SyncWithValidationInterfaceQueue();
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ CHECK_NONFATAL(node.validation_signals)->SyncWithValidationInterfaceQueue();
return UniValue::VNULL;
},
};
@@ -594,6 +595,28 @@ static CBlock GetBlockChecked(BlockManager& blockman, const CBlockIndex& blockin
return block;
}
+static std::vector<uint8_t> GetRawBlockChecked(BlockManager& blockman, const CBlockIndex& blockindex)
+{
+ std::vector<uint8_t> data{};
+ FlatFilePos pos{};
+ {
+ LOCK(cs_main);
+ if (blockman.IsBlockPruned(blockindex)) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)");
+ }
+ pos = blockindex.GetBlockPos();
+ }
+
+ if (!blockman.ReadRawBlockFromDisk(data, pos)) {
+ // Block not found on disk. This could be because we have the block
+ // header in our index but not yet have the block or did not accept the
+ // block. Or if the block was pruned right after we released the lock above.
+ throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk");
+ }
+
+ return data;
+}
+
static CBlockUndo GetUndoChecked(BlockManager& blockman, const CBlockIndex& blockindex)
{
CBlockUndo blockUndo;
@@ -734,15 +757,16 @@ static RPCHelpMan getblock()
}
}
- const CBlock block{GetBlockChecked(chainman.m_blockman, *pblockindex)};
+ const std::vector<uint8_t> block_data{GetRawBlockChecked(chainman.m_blockman, *pblockindex)};
if (verbosity <= 0) {
- DataStream ssBlock;
- ssBlock << TX_WITH_WITNESS(block);
- std::string strHex = HexStr(ssBlock);
- return strHex;
+ return HexStr(block_data);
}
+ DataStream block_stream{block_data};
+ CBlock block{};
+ block_stream >> TX_WITH_WITNESS(block);
+
TxVerbosity tx_verbosity;
if (verbosity == 1) {
tx_verbosity = TxVerbosity::SHOW_TXID;
@@ -1235,7 +1259,14 @@ RPCHelpMan getblockchaininfo()
{RPCResult::Type::NUM, "pruneheight", /*optional=*/true, "height of the last block pruned, plus one (only present if pruning is enabled)"},
{RPCResult::Type::BOOL, "automatic_pruning", /*optional=*/true, "whether automatic pruning is enabled (only present if pruning is enabled)"},
{RPCResult::Type::NUM, "prune_target_size", /*optional=*/true, "the target size used by pruning (only present if automatic pruning is enabled)"},
- {RPCResult::Type::STR, "warnings", "any network and blockchain warnings"},
+ (IsDeprecatedRPCEnabled("warnings") ?
+ RPCResult{RPCResult::Type::STR, "warnings", "any network and blockchain warnings (DEPRECATED)"} :
+ RPCResult{RPCResult::Type::ARR, "warnings", "any network and blockchain warnings (run with `-deprecatedrpc=warnings` to return the latest warning as a single string)",
+ {
+ {RPCResult::Type::STR, "", "warning"},
+ }
+ }
+ ),
}},
RPCExamples{
HelpExampleCli("getblockchaininfo", "")
@@ -1273,7 +1304,7 @@ RPCHelpMan getblockchaininfo()
}
}
- obj.pushKV("warnings", GetWarnings(false).original);
+ obj.pushKV("warnings", GetNodeWarnings(IsDeprecatedRPCEnabled("warnings")));
return obj;
},
};
@@ -2149,7 +2180,8 @@ static RPCHelpMan scantxoutset()
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
UniValue result(UniValue::VOBJ);
- if (request.params[0].get_str() == "status") {
+ const auto action{self.Arg<std::string>("action")};
+ if (action == "status") {
CoinsViewScanReserver reserver;
if (reserver.reserve()) {
// no scan in progress
@@ -2157,7 +2189,7 @@ static RPCHelpMan scantxoutset()
}
result.pushKV("progress", g_scan_progress.load());
return result;
- } else if (request.params[0].get_str() == "abort") {
+ } else if (action == "abort") {
CoinsViewScanReserver reserver;
if (reserver.reserve()) {
// reserve was possible which means no scan was running
@@ -2166,7 +2198,7 @@ static RPCHelpMan scantxoutset()
// set the abort flag
g_should_abort_scan = true;
return true;
- } else if (request.params[0].get_str() == "start") {
+ } else if (action == "start") {
CoinsViewScanReserver reserver;
if (!reserver.reserve()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\"");
@@ -2235,7 +2267,7 @@ static RPCHelpMan scantxoutset()
result.pushKV("unspents", unspents);
result.pushKV("total_amount", ValueFromAmount(total_in));
} else {
- throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid action '%s'", request.params[0].get_str()));
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid action '%s'", action));
}
return result;
},
@@ -2571,7 +2603,7 @@ static RPCHelpMan dumptxoutset()
{
return RPCHelpMan{
"dumptxoutset",
- "Write the serialized UTXO set to disk.",
+ "Write the serialized UTXO set to a file.",
{
{"path", RPCArg::Type::STR, RPCArg::Optional::NO, "Path to the output file. If relative, will be prefixed by datadir."},
},
@@ -2699,7 +2731,7 @@ static RPCHelpMan loadtxoutset()
{
return RPCHelpMan{
"loadtxoutset",
- "Load the serialized UTXO set from disk.\n"
+ "Load the serialized UTXO set from a file.\n"
"Once this snapshot is loaded, its contents will be "
"deserialized into a second chainstate data structure, which is then used to sync to "
"the network's tip. "
@@ -2753,34 +2785,14 @@ static RPCHelpMan loadtxoutset()
throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Unable to load UTXO snapshot, "
"assumeutxo block hash in snapshot metadata not recognized (%s)", base_blockhash.ToString()));
}
- int max_secs_to_wait_for_headers = 60 * 10;
- CBlockIndex* snapshot_start_block = nullptr;
-
- LogPrintf("[snapshot] waiting to see blockheader %s in headers chain before snapshot activation\n",
- base_blockhash.ToString());
-
- while (max_secs_to_wait_for_headers > 0) {
- snapshot_start_block = WITH_LOCK(::cs_main,
+ CBlockIndex* snapshot_start_block = WITH_LOCK(::cs_main,
return chainman.m_blockman.LookupBlockIndex(base_blockhash));
- max_secs_to_wait_for_headers -= 1;
-
- if (!IsRPCRunning()) {
- throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");
- }
-
- if (!snapshot_start_block) {
- std::this_thread::sleep_for(std::chrono::seconds(1));
- } else {
- break;
- }
- }
if (!snapshot_start_block) {
- LogPrintf("[snapshot] timed out waiting for snapshot start blockheader %s\n",
- base_blockhash.ToString());
throw JSONRPCError(
RPC_INTERNAL_ERROR,
- "Timed out waiting for base block header to appear in headers chain");
+ strprintf("The base block header (%s) must appear in the headers chain. Make sure all headers are syncing, and call this RPC again.",
+ base_blockhash.ToString()));
}
if (!chainman.ActivateSnapshot(afile, metadata, false)) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to load UTXO snapshot " + fs::PathToString(path));
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 5825efdf82..b8dc148eae 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -128,6 +128,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "testmempoolaccept", 0, "rawtxs" },
{ "testmempoolaccept", 1, "maxfeerate" },
{ "submitpackage", 0, "package" },
+ { "submitpackage", 1, "maxfeerate" },
+ { "submitpackage", 2, "maxburnamount" },
{ "combinerawtransaction", 0, "txs" },
{ "fundrawtransaction", 1, "options" },
{ "fundrawtransaction", 1, "add_inputs"},
@@ -275,6 +277,11 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "logging", 1, "exclude" },
{ "disconnectnode", 1, "nodeid" },
{ "upgradewallet", 0, "version" },
+ { "gethdkeys", 0, "active_only" },
+ { "gethdkeys", 0, "options" },
+ { "gethdkeys", 0, "private" },
+ { "createwalletdescriptor", 1, "options" },
+ { "createwalletdescriptor", 1, "internal" },
// Echo with conversion (For testing only)
{ "echojson", 0, "arg0" },
{ "echojson", 1, "arg1" },
diff --git a/src/rpc/external_signer.cpp b/src/rpc/external_signer.cpp
index 310eec5f15..27c7394909 100644
--- a/src/rpc/external_signer.cpp
+++ b/src/rpc/external_signer.cpp
@@ -2,6 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <config/bitcoin-config.h> // IWYU pragma: keep
+
#include <common/args.h>
#include <common/system.h>
#include <external_signer.h>
diff --git a/src/rpc/fees.cpp b/src/rpc/fees.cpp
index 57ba486ed9..f3345b4f1c 100644
--- a/src/rpc/fees.cpp
+++ b/src/rpc/fees.cpp
@@ -4,6 +4,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <core_io.h>
+#include <node/context.h>
#include <policy/feerate.h>
#include <policy/fees.h>
#include <rpc/protocol.h>
@@ -21,10 +22,6 @@
#include <cmath>
#include <string>
-namespace node {
-struct NodeContext;
-}
-
using node::NodeContext;
static RPCHelpMan estimatesmartfee()
@@ -68,7 +65,7 @@ static RPCHelpMan estimatesmartfee()
const NodeContext& node = EnsureAnyNodeContext(request.context);
const CTxMemPool& mempool = EnsureMemPool(node);
- SyncWithValidationInterfaceQueue();
+ CHECK_NONFATAL(mempool.m_signals)->SyncWithValidationInterfaceQueue();
unsigned int max_target = fee_estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
bool conservative = true;
@@ -156,8 +153,9 @@ static RPCHelpMan estimaterawfee()
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
CBlockPolicyEstimator& fee_estimator = EnsureAnyFeeEstimator(request.context);
+ const NodeContext& node = EnsureAnyNodeContext(request.context);
- SyncWithValidationInterfaceQueue();
+ CHECK_NONFATAL(node.validation_signals)->SyncWithValidationInterfaceQueue();
unsigned int max_target = fee_estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
double threshold = 0.95;
diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp
index b04e7c6542..0672ec34b3 100644
--- a/src/rpc/mempool.cpp
+++ b/src/rpc/mempool.cpp
@@ -28,6 +28,7 @@
using kernel::DumpMempool;
+using node::DEFAULT_MAX_BURN_AMOUNT;
using node::DEFAULT_MAX_RAW_TX_FEE_RATE;
using node::MempoolPath;
using node::NodeContext;
@@ -45,8 +46,8 @@ static RPCHelpMan sendrawtransaction()
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
{"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
"Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
- "/kvB.\nSet to 0 to accept any fee rate."},
- {"maxburnamount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(0)},
+ "/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."},
+ {"maxburnamount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_BURN_AMOUNT)},
"Reject transactions with provably unspendable outputs (e.g. 'datacarrier' outputs that use the OP_RETURN opcode) greater than the specified value, expressed in " + CURRENCY_UNIT + ".\n"
"If burning funds through unspendable outputs is desired, increase this value.\n"
"This check is based on heuristics and does not guarantee spendability of outputs.\n"},
@@ -81,9 +82,7 @@ static RPCHelpMan sendrawtransaction()
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
- const CFeeRate max_raw_tx_fee_rate = request.params[1].isNull() ?
- DEFAULT_MAX_RAW_TX_FEE_RATE :
- CFeeRate(AmountFromValue(request.params[1]));
+ const CFeeRate max_raw_tx_fee_rate{ParseFeeRate(self.Arg<UniValue>(1))};
int64_t virtual_size = GetVirtualTransactionSize(*tx);
CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size);
@@ -117,7 +116,8 @@ static RPCHelpMan testmempoolaccept()
},
},
{"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
- "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kvB\n"},
+ "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
+ "/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."},
},
RPCResult{
RPCResult::Type::ARR, "", "The result of the mempool acceptance test for each raw transaction in the input array.\n"
@@ -162,9 +162,7 @@ static RPCHelpMan testmempoolaccept()
"Array must contain between 1 and " + ToString(MAX_PACKAGE_COUNT) + " transactions.");
}
- const CFeeRate max_raw_tx_fee_rate = request.params[1].isNull() ?
- DEFAULT_MAX_RAW_TX_FEE_RATE :
- CFeeRate(AmountFromValue(request.params[1]));
+ const CFeeRate max_raw_tx_fee_rate{ParseFeeRate(self.Arg<UniValue>(1))};
std::vector<CTransactionRef> txns;
txns.reserve(raw_transactions.size());
@@ -183,7 +181,7 @@ static RPCHelpMan testmempoolaccept()
Chainstate& chainstate = chainman.ActiveChainstate();
const PackageMempoolAcceptResult package_result = [&] {
LOCK(::cs_main);
- if (txns.size() > 1) return ProcessNewPackage(chainstate, mempool, txns, /*test_accept=*/true);
+ if (txns.size() > 1) return ProcessNewPackage(chainstate, mempool, txns, /*test_accept=*/true, /*client_maxfeerate=*/{});
return PackageMempoolAcceptResult(txns[0]->GetWitnessHash(),
chainman.ProcessTransaction(txns[0], /*test_accept=*/true));
}();
@@ -827,6 +825,14 @@ static RPCHelpMan submitpackage()
{"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
},
},
+ {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
+ "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
+ "/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."},
+ {"maxburnamount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_BURN_AMOUNT)},
+ "Reject transactions with provably unspendable outputs (e.g. 'datacarrier' outputs that use the OP_RETURN opcode) greater than the specified value, expressed in " + CURRENCY_UNIT + ".\n"
+ "If burning funds through unspendable outputs is desired, increase this value.\n"
+ "This check is based on heuristics and does not guarantee spendability of outputs.\n"
+ },
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -866,6 +872,17 @@ static RPCHelpMan submitpackage()
"Array must contain between 2 and " + ToString(MAX_PACKAGE_COUNT) + " transactions.");
}
+ // Fee check needs to be run with chainstate and package context
+ const CFeeRate max_raw_tx_fee_rate = ParseFeeRate(self.Arg<UniValue>(1));
+ std::optional<CFeeRate> client_maxfeerate{max_raw_tx_fee_rate};
+ // 0-value is special; it's mapped to no sanity check
+ if (max_raw_tx_fee_rate == CFeeRate(0)) {
+ client_maxfeerate = std::nullopt;
+ }
+
+ // Burn sanity check is run with no context
+ const CAmount max_burn_amount = request.params[2].isNull() ? 0 : AmountFromValue(request.params[2]);
+
std::vector<CTransactionRef> txns;
txns.reserve(raw_transactions.size());
for (const auto& rawtx : raw_transactions.getValues()) {
@@ -874,6 +891,13 @@ static RPCHelpMan submitpackage()
throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
"TX decode failed: " + rawtx.get_str() + " Make sure the tx has at least one input.");
}
+
+ for (const auto& out : mtx.vout) {
+ if((out.scriptPubKey.IsUnspendable() || !out.scriptPubKey.HasValidOps()) && out.nValue > max_burn_amount) {
+ throw JSONRPCTransactionError(TransactionError::MAX_BURN_EXCEEDED);
+ }
+ }
+
txns.emplace_back(MakeTransactionRef(std::move(mtx)));
}
if (!IsChildWithParentsTree(txns)) {
@@ -883,7 +907,7 @@ static RPCHelpMan submitpackage()
NodeContext& node = EnsureAnyNodeContext(request.context);
CTxMemPool& mempool = EnsureMemPool(node);
Chainstate& chainstate = EnsureChainman(node).ActiveChainstate();
- const auto package_result = WITH_LOCK(::cs_main, return ProcessNewPackage(chainstate, mempool, txns, /*test_accept=*/ false));
+ const auto package_result = WITH_LOCK(::cs_main, return ProcessNewPackage(chainstate, mempool, txns, /*test_accept=*/ false, client_maxfeerate));
std::string package_msg = "success";
@@ -971,10 +995,8 @@ static RPCHelpMan submitpackage()
fees.pushKV("effective-includes", effective_includes_res);
}
result_inner.pushKV("fees", fees);
- if (it->second.m_replaced_transactions.has_value()) {
- for (const auto& ptx : it->second.m_replaced_transactions.value()) {
- replaced_txids.insert(ptx->GetHash());
- }
+ for (const auto& ptx : it->second.m_replaced_transactions) {
+ replaced_txids.insert(ptx->GetHash());
}
break;
}
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index f1abfb6396..73987669a5 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -3,6 +3,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <config/bitcoin-config.h> // IWYU pragma: keep
+
#include <chain.h>
#include <chainparams.h>
#include <common/system.h>
@@ -35,7 +37,6 @@
#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
-#include <warnings.h>
#include <memory>
#include <stdint.h>
@@ -118,7 +119,7 @@ static RPCHelpMan getnetworkhashps()
{
ChainstateManager& chainman = EnsureAnyChainman(request.context);
LOCK(cs_main);
- return GetNetworkHashPS(self.Arg<int>(0), self.Arg<int>(1), chainman.ActiveChain());
+ return GetNetworkHashPS(self.Arg<int>("nblocks"), self.Arg<int>("height"), chainman.ActiveChain());
},
};
}
@@ -225,12 +226,12 @@ static RPCHelpMan generatetodescriptor()
"\nGenerate 11 blocks to mydesc\n" + HelpExampleCli("generatetodescriptor", "11 \"mydesc\"")},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- const auto num_blocks{self.Arg<int>(0)};
- const auto max_tries{self.Arg<uint64_t>(2)};
+ const auto num_blocks{self.Arg<int>("num_blocks")};
+ const auto max_tries{self.Arg<uint64_t>("maxtries")};
CScript coinbase_script;
std::string error;
- if (!getScriptFromDescriptor(self.Arg<std::string>(1), coinbase_script, error)) {
+ if (!getScriptFromDescriptor(self.Arg<std::string>("descriptor"), coinbase_script, error)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
}
@@ -422,7 +423,14 @@ static RPCHelpMan getmininginfo()
{RPCResult::Type::NUM, "networkhashps", "The network hashes per second"},
{RPCResult::Type::NUM, "pooledtx", "The size of the mempool"},
{RPCResult::Type::STR, "chain", "current network name (main, test, signet, regtest)"},
- {RPCResult::Type::STR, "warnings", "any network and blockchain warnings"},
+ (IsDeprecatedRPCEnabled("warnings") ?
+ RPCResult{RPCResult::Type::STR, "warnings", "any network and blockchain warnings (DEPRECATED)"} :
+ RPCResult{RPCResult::Type::ARR, "warnings", "any network and blockchain warnings (run with `-deprecatedrpc=warnings` to return the latest warning as a single string)",
+ {
+ {RPCResult::Type::STR, "", "warning"},
+ }
+ }
+ ),
}},
RPCExamples{
HelpExampleCli("getmininginfo", "")
@@ -444,7 +452,7 @@ static RPCHelpMan getmininginfo()
obj.pushKV("networkhashps", getnetworkhashps().HandleRequest(request));
obj.pushKV("pooledtx", (uint64_t)mempool.size());
obj.pushKV("chain", chainman.GetParams().GetChainTypeString());
- obj.pushKV("warnings", GetWarnings(false).original);
+ obj.pushKV("warnings", GetNodeWarnings(IsDeprecatedRPCEnabled("warnings")));
return obj;
},
};
@@ -1038,9 +1046,9 @@ static RPCHelpMan submitblock()
bool new_block;
auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
- RegisterSharedValidationInterface(sc);
+ CHECK_NONFATAL(chainman.m_options.signals)->RegisterSharedValidationInterface(sc);
bool accepted = chainman.ProcessNewBlock(blockptr, /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/&new_block);
- UnregisterSharedValidationInterface(sc);
+ CHECK_NONFATAL(chainman.m_options.signals)->UnregisterSharedValidationInterface(sc);
if (!new_block && accepted) {
return "duplicate";
}
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 5e6f42b596..04f9410b32 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -23,14 +23,12 @@
#include <rpc/server_util.h>
#include <rpc/util.h>
#include <sync.h>
-#include <timedata.h>
#include <util/chaintype.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/time.h>
#include <util/translation.h>
#include <validation.h>
-#include <warnings.h>
#include <optional>
@@ -239,7 +237,7 @@ static RPCHelpMan getpeerinfo()
obj.pushKV("bytessent", stats.nSendBytes);
obj.pushKV("bytesrecv", stats.nRecvBytes);
obj.pushKV("conntime", count_seconds(stats.m_connected));
- obj.pushKV("timeoffset", stats.nTimeOffset);
+ obj.pushKV("timeoffset", Ticks<std::chrono::seconds>(statestats.time_offset));
if (stats.m_last_ping_time > 0us) {
obj.pushKV("pingtime", Ticks<SecondsDouble>(stats.m_last_ping_time));
}
@@ -322,7 +320,7 @@ static RPCHelpMan addnode()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- const std::string command{request.params[1].get_str()};
+ const auto command{self.Arg<std::string>("command")};
if (command != "onetry" && command != "add" && command != "remove") {
throw std::runtime_error(
self.ToString());
@@ -331,9 +329,9 @@ static RPCHelpMan addnode()
NodeContext& node = EnsureAnyNodeContext(request.context);
CConnman& connman = EnsureConnman(node);
- const std::string node_arg{request.params[0].get_str()};
+ const auto node_arg{self.Arg<std::string>("node")};
bool node_v2transport = connman.GetLocalServices() & NODE_P2P_V2;
- bool use_v2transport = self.MaybeArg<bool>(2).value_or(node_v2transport);
+ bool use_v2transport = self.MaybeArg<bool>("v2transport").value_or(node_v2transport);
if (use_v2transport && !node_v2transport) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Error: v2transport requested but not enabled (see -v2transport)");
@@ -607,8 +605,8 @@ static UniValue GetNetworksInfo()
obj.pushKV("name", GetNetworkName(network));
obj.pushKV("limited", !g_reachable_nets.Contains(network));
obj.pushKV("reachable", g_reachable_nets.Contains(network));
- obj.pushKV("proxy", proxy.IsValid() ? proxy.proxy.ToStringAddrPort() : std::string());
- obj.pushKV("proxy_randomize_credentials", proxy.randomize_credentials);
+ obj.pushKV("proxy", proxy.IsValid() ? proxy.ToString() : std::string());
+ obj.pushKV("proxy_randomize_credentials", proxy.m_randomize_credentials);
networks.push_back(obj);
}
return networks;
@@ -658,7 +656,14 @@ static RPCHelpMan getnetworkinfo()
{RPCResult::Type::NUM, "score", "relative score"},
}},
}},
- {RPCResult::Type::STR, "warnings", "any network and blockchain warnings"},
+ (IsDeprecatedRPCEnabled("warnings") ?
+ RPCResult{RPCResult::Type::STR, "warnings", "any network and blockchain warnings (DEPRECATED)"} :
+ RPCResult{RPCResult::Type::ARR, "warnings", "any network and blockchain warnings (run with `-deprecatedrpc=warnings` to return the latest warning as a single string)",
+ {
+ {RPCResult::Type::STR, "", "warning"},
+ }
+ }
+ ),
}
},
RPCExamples{
@@ -679,9 +684,10 @@ static RPCHelpMan getnetworkinfo()
obj.pushKV("localservicesnames", GetServicesNames(services));
}
if (node.peerman) {
- obj.pushKV("localrelay", !node.peerman->IgnoresIncomingTxs());
+ auto peerman_info{node.peerman->GetInfo()};
+ obj.pushKV("localrelay", !peerman_info.ignores_incoming_txs);
+ obj.pushKV("timeoffset", Ticks<std::chrono::seconds>(peerman_info.median_outbound_time_offset));
}
- obj.pushKV("timeoffset", GetTimeOffset());
if (node.connman) {
obj.pushKV("networkactive", node.connman->GetNetworkActive());
obj.pushKV("connections", node.connman->GetNodeCount(ConnectionDirection::Both));
@@ -707,7 +713,7 @@ static RPCHelpMan getnetworkinfo()
}
}
obj.pushKV("localaddresses", localAddresses);
- obj.pushKV("warnings", GetWarnings(false).original);
+ obj.pushKV("warnings", GetNodeWarnings(IsDeprecatedRPCEnabled("warnings")));
return obj;
},
};
@@ -951,7 +957,7 @@ static RPCHelpMan getnodeaddresses()
static RPCHelpMan addpeeraddress()
{
return RPCHelpMan{"addpeeraddress",
- "\nAdd the address of a potential peer to the address manager. This RPC is for testing only.\n",
+ "Add the address of a potential peer to an address manager table. This RPC is for testing only.",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP address of the peer"},
{"port", RPCArg::Type::NUM, RPCArg::Optional::NO, "The port of the peer"},
@@ -960,7 +966,8 @@ static RPCHelpMan addpeeraddress()
RPCResult{
RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::BOOL, "success", "whether the peer address was successfully added to the address manager"},
+ {RPCResult::Type::BOOL, "success", "whether the peer address was successfully added to the address manager table"},
+ {RPCResult::Type::STR, "error", /*optional=*/true, "error description, if the address could not be added"},
},
},
RPCExamples{
@@ -989,8 +996,13 @@ static RPCHelpMan addpeeraddress()
success = true;
if (tried) {
// Attempt to move the address to the tried addresses table.
- addrman.Good(address);
+ if (!addrman.Good(address)) {
+ success = false;
+ obj.pushKV("error", "failed-adding-to-tried");
+ }
}
+ } else {
+ obj.pushKV("error", "failed-adding-to-new");
}
}
diff --git a/src/rpc/node.cpp b/src/rpc/node.cpp
index b085828215..447be2cf64 100644
--- a/src/rpc/node.cpp
+++ b/src/rpc/node.cpp
@@ -3,6 +3,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <config/bitcoin-config.h> // IWYU pragma: keep
+
#include <chainparams.h>
#include <httpserver.h>
#include <index/blockfilterindex.h>
@@ -22,6 +24,7 @@
#include <univalue.h>
#include <util/any.h>
#include <util/check.h>
+#include <util/time.h>
#include <stdint.h>
#ifdef HAVE_MALLOC_INFO
@@ -54,9 +57,11 @@ static RPCHelpMan setmocktime()
LOCK(cs_main);
const int64_t time{request.params[0].getInt<int64_t>()};
- if (time < 0) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Mocktime cannot be negative: %s.", time));
+ constexpr int64_t max_time{Ticks<std::chrono::seconds>(std::chrono::nanoseconds::max())};
+ if (time < 0 || time > max_time) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Mocktime must be in the range [0, %s], not %s.", max_time, time));
}
+
SetMockTime(time);
const NodeContext& node_context{EnsureAnyNodeContext(request.context)};
for (const auto& chain_client : node_context.chain_clients) {
@@ -90,7 +95,7 @@ static RPCHelpMan mockscheduler()
const NodeContext& node_context{EnsureAnyNodeContext(request.context)};
CHECK_NONFATAL(node_context.scheduler)->MockForward(std::chrono::seconds{delta_seconds});
- SyncWithValidationInterfaceQueue();
+ CHECK_NONFATAL(node_context.validation_signals)->SyncWithValidationInterfaceQueue();
for (const auto& chain_client : node_context.chain_clients) {
chain_client->schedulerMockForward(std::chrono::seconds(delta_seconds));
}
diff --git a/src/rpc/output_script.cpp b/src/rpc/output_script.cpp
index f9343f48a8..474d9076be 100644
--- a/src/rpc/output_script.cpp
+++ b/src/rpc/output_script.cpp
@@ -124,11 +124,7 @@ static RPCHelpMan createmultisig()
const UniValue& keys = request.params[1].get_array();
std::vector<CPubKey> pubkeys;
for (unsigned int i = 0; i < keys.size(); ++i) {
- if (IsHex(keys[i].get_str()) && (keys[i].get_str().length() == 66 || keys[i].get_str().length() == 130)) {
- pubkeys.push_back(HexToPubKey(keys[i].get_str()));
- } else {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid public key: %s\n.", keys[i].get_str()));
- }
+ pubkeys.push_back(HexToPubKey(keys[i].get_str()));
}
// Get the output type
diff --git a/src/rpc/register.h b/src/rpc/register.h
index c88f49ecf0..65fd29ff08 100644
--- a/src/rpc/register.h
+++ b/src/rpc/register.h
@@ -5,6 +5,8 @@
#ifndef BITCOIN_RPC_REGISTER_H
#define BITCOIN_RPC_REGISTER_H
+#include <config/bitcoin-config.h> // IWYU pragma: keep
+
/** These are in one header file to avoid creating tons of single-function
* headers for everything under src/rpc/ */
class CRPCTable;
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index e1ac510cba..a800451f4a 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -3,6 +3,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <config/bitcoin-config.h> // IWYU pragma: keep
+
#include <rpc/server.h>
#include <common/args.h>
diff --git a/src/rpc/signmessage.cpp b/src/rpc/signmessage.cpp
index 8c752ba1fd..9f3c24efcf 100644
--- a/src/rpc/signmessage.cpp
+++ b/src/rpc/signmessage.cpp
@@ -38,9 +38,9 @@ static RPCHelpMan verifymessage()
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
- std::string strAddress = request.params[0].get_str();
- std::string strSign = request.params[1].get_str();
- std::string strMessage = request.params[2].get_str();
+ std::string strAddress = self.Arg<std::string>("address");
+ std::string strSign = self.Arg<std::string>("signature");
+ std::string strMessage = self.Arg<std::string>("message");
switch (MessageVerify(strAddress, strSign, strMessage)) {
case MessageVerificationResult::ERR_INVALID_ADDRESS:
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index cf48ee11e7..9a7c731afe 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -2,6 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <config/bitcoin-config.h> // IWYU pragma: keep
+
#include <clientversion.h>
#include <core_io.h>
#include <common/args.h>
@@ -14,14 +16,19 @@
#include <script/signingprovider.h>
#include <script/solver.h>
#include <tinyformat.h>
+#include <univalue.h>
#include <util/check.h>
#include <util/result.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/translation.h>
+#include <warnings.h>
+#include <algorithm>
+#include <iterator>
#include <string_view>
#include <tuple>
+#include <utility>
const std::string UNIX_EPOCH_TIME = "UNIX epoch time";
const std::string EXAMPLE_ADDRESS[2] = {"bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl", "bc1q02ad21edsxd23d32dfgqqsz4vv4nmtfzuklhy3"};
@@ -75,6 +82,13 @@ CAmount AmountFromValue(const UniValue& value, int decimals)
return amount;
}
+CFeeRate ParseFeeRate(const UniValue& json)
+{
+ CAmount val{AmountFromValue(json)};
+ if (val >= COIN) throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee rates larger than or equal to 1BTC/kvB are not accepted");
+ return CFeeRate{val};
+}
+
uint256 ParseHashV(const UniValue& v, std::string_view name)
{
const std::string& strHex(v.get_str());
@@ -180,11 +194,14 @@ std::string HelpExampleRpcNamed(const std::string& methodname, const RPCArgList&
CPubKey HexToPubKey(const std::string& hex_in)
{
if (!IsHex(hex_in)) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid public key: " + hex_in);
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + hex_in + "\" must be a hex string");
+ }
+ if (hex_in.length() != 66 && hex_in.length() != 130) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + hex_in + "\" must have a length of either 33 or 65 bytes");
}
CPubKey vchPubKey(ParseHex(hex_in));
if (!vchPubKey.IsFullyValid()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid public key: " + hex_in);
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + hex_in + "\" must be cryptographically valid.");
}
return vchPubKey;
}
@@ -403,6 +420,7 @@ struct Sections {
/**
* Recursive helper to translate an RPCArg into sections
*/
+ // NOLINTNEXTLINE(misc-no-recursion)
void Push(const RPCArg& arg, const size_t current_indent = 5, const OuterType outer_type = OuterType::NONE)
{
const auto indent = std::string(current_indent, ' ');
@@ -678,11 +696,13 @@ static void CheckRequiredOrDefault(const RPCArg& param)
void force_semicolon(ret_type)
// Optional arg (without default). Can also be called on required args, if needed.
+TMPL_INST(nullptr, const UniValue*, maybe_arg;);
TMPL_INST(nullptr, std::optional<double>, maybe_arg ? std::optional{maybe_arg->get_real()} : std::nullopt;);
TMPL_INST(nullptr, std::optional<bool>, maybe_arg ? std::optional{maybe_arg->get_bool()} : std::nullopt;);
TMPL_INST(nullptr, const std::string*, maybe_arg ? &maybe_arg->get_str() : nullptr;);
// Required arg or optional arg with default value.
+TMPL_INST(CheckRequiredOrDefault, const UniValue&, *CHECK_NONFATAL(maybe_arg););
TMPL_INST(CheckRequiredOrDefault, bool, CHECK_NONFATAL(maybe_arg)->get_bool(););
TMPL_INST(CheckRequiredOrDefault, int, CHECK_NONFATAL(maybe_arg)->getInt<int>(););
TMPL_INST(CheckRequiredOrDefault, uint64_t, CHECK_NONFATAL(maybe_arg)->getInt<uint64_t>(););
@@ -715,6 +735,16 @@ std::vector<std::pair<std::string, bool>> RPCHelpMan::GetArgNames() const
return ret;
}
+size_t RPCHelpMan::GetParamIndex(std::string_view key) const
+{
+ auto it{std::find_if(
+ m_args.begin(), m_args.end(), [&key](const auto& arg) { return arg.GetName() == key;}
+ )};
+
+ CHECK_NONFATAL(it != m_args.end()); // TODO: ideally this is checked at compile time
+ return std::distance(m_args.begin(), it);
+}
+
std::string RPCHelpMan::ToString() const
{
std::string ret;
@@ -940,6 +970,7 @@ std::string RPCArg::ToDescriptionString(bool is_named_arg) const
return ret;
}
+// NOLINTNEXTLINE(misc-no-recursion)
void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const int current_indent) const
{
// Indentation
@@ -1073,6 +1104,7 @@ static std::optional<UniValue::VType> ExpectedType(RPCResult::Type type)
NONFATAL_UNREACHABLE();
}
+// NOLINTNEXTLINE(misc-no-recursion)
UniValue RPCResult::MatchesType(const UniValue& result) const
{
if (m_skip_type_check) {
@@ -1151,6 +1183,7 @@ void RPCResult::CheckInnerDoc() const
CHECK_NONFATAL(inner_needed != m_inner.empty());
}
+// NOLINTNEXTLINE(misc-no-recursion)
std::string RPCArg::ToStringObj(const bool oneline) const
{
std::string res;
@@ -1189,6 +1222,7 @@ std::string RPCArg::ToStringObj(const bool oneline) const
NONFATAL_UNREACHABLE();
}
+// NOLINTNEXTLINE(misc-no-recursion)
std::string RPCArg::ToString(const bool oneline) const
{
if (oneline && !m_opts.oneline_description.empty()) {
@@ -1215,6 +1249,7 @@ std::string RPCArg::ToString(const bool oneline) const
case Type::OBJ:
case Type::OBJ_NAMED_PARAMS:
case Type::OBJ_USER_KEYS: {
+ // NOLINTNEXTLINE(misc-no-recursion)
const std::string res = Join(m_inner, ",", [&](const RPCArg& i) { return i.ToStringObj(oneline); });
if (m_type == Type::OBJ) {
return "{" + res + "}";
@@ -1326,3 +1361,17 @@ void PushWarnings(const std::vector<bilingual_str>& warnings, UniValue& obj)
if (warnings.empty()) return;
obj.pushKV("warnings", BilingualStringsToUniValue(warnings));
}
+
+UniValue GetNodeWarnings(bool use_deprecated)
+{
+ if (use_deprecated) {
+ const auto all_warnings{GetWarnings()};
+ return all_warnings.empty() ? "" : all_warnings.back().original;
+ }
+
+ UniValue warnings{UniValue::VARR};
+ for (auto&& warning : GetWarnings()) {
+ warnings.push_back(std::move(warning.original));
+ }
+ return warnings;
+}
diff --git a/src/rpc/util.h b/src/rpc/util.h
index e2d5ed333c..0e4dcc27b5 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -103,6 +103,11 @@ std::vector<unsigned char> ParseHexO(const UniValue& o, std::string_view strKey)
* @returns a CAmount if the various checks pass.
*/
CAmount AmountFromValue(const UniValue& value, int decimals = 8);
+/**
+ * Parse a json number or string, denoting BTC/kvB, into a CFeeRate (sat/kvB).
+ * Reject negative values or rates larger than 1BTC/kvB.
+ */
+CFeeRate ParseFeeRate(const UniValue& json);
using RPCArgList = std::vector<std::pair<std::string, UniValue>>;
std::string HelpExampleCli(const std::string& methodname, const std::string& args);
@@ -157,6 +162,7 @@ struct RPCArgOptions {
//!< methods set the also_positional flag and read values from both positions.
};
+// NOLINTNEXTLINE(misc-no-recursion)
struct RPCArg {
enum class Type {
OBJ,
@@ -266,6 +272,7 @@ struct RPCArg {
std::string ToDescriptionString(bool is_named_arg) const;
};
+// NOLINTNEXTLINE(misc-no-recursion)
struct RPCResult {
enum class Type {
OBJ,
@@ -397,18 +404,25 @@ public:
UniValue HandleRequest(const JSONRPCRequest& request) const;
/**
- * Helper to get a request argument.
- * This function only works during m_fun(), i.e. it should only be used in
- * RPC method implementations. The helper internally checks whether the
- * user-passed argument isNull() and parses (from JSON) and returns the
- * user-passed argument, or the default value derived from the RPCArg
- * documentation, or a falsy value if no default was given.
+ * @brief Helper to get a required or default-valued request argument.
+ *
+ * Use this function when the argument is required or when it has a default value. If the
+ * argument is optional and may not be provided, use MaybeArg instead.
+ *
+ * This function only works during m_fun(), i.e., it should only be used in
+ * RPC method implementations. It internally checks whether the user-passed
+ * argument isNull() and parses (from JSON) and returns the user-passed argument,
+ * or the default value derived from the RPCArg documentation.
+ *
+ * There are two overloads of this function:
+ * - Use Arg<Type>(size_t i) to get the argument (or the default value) by index.
+ * - Use Arg<Type>(const std::string& key) to get the argument (or the default value) by key.
*
- * Use Arg<Type>(i) to get the argument or its default value. Otherwise,
- * use MaybeArg<Type>(i) to get the optional argument or a falsy value.
+ * The Type passed to this helper must match the corresponding RPCArg::Type.
*
- * The Type passed to this helper must match the corresponding
- * RPCArg::Type.
+ * @return The value of the RPC argument (or the default value) cast to type Type.
+ *
+ * @see MaybeArg for handling optional arguments without default values.
*/
template <typename R>
auto Arg(size_t i) const
@@ -422,6 +436,34 @@ public:
return ArgValue<const R&>(i);
}
}
+ template<typename R>
+ auto Arg(std::string_view key) const
+ {
+ return Arg<R>(GetParamIndex(key));
+ }
+ /**
+ * @brief Helper to get an optional request argument.
+ *
+ * Use this function when the argument is optional and does not have a default value. If the
+ * argument is required or has a default value, use Arg instead.
+ *
+ * This function only works during m_fun(), i.e., it should only be used in
+ * RPC method implementations. It internally checks whether the user-passed
+ * argument isNull() and parses (from JSON) and returns the user-passed argument,
+ * or a falsy value if no argument was passed.
+ *
+ * There are two overloads of this function:
+ * - Use MaybeArg<Type>(size_t i) to get the optional argument by index.
+ * - Use MaybeArg<Type>(const std::string& key) to get the optional argument by key.
+ *
+ * The Type passed to this helper must match the corresponding RPCArg::Type.
+ *
+ * @return For integral and floating-point types, a std::optional<Type> is returned.
+ * For other types, a Type* pointer to the argument is returned. If the
+ * argument is not provided, std::nullopt or a null pointer is returned.
+ *
+ * @see Arg for handling arguments that are required or have a default value.
+ */
template <typename R>
auto MaybeArg(size_t i) const
{
@@ -434,6 +476,11 @@ public:
return ArgValue<const R*>(i);
}
}
+ template<typename R>
+ auto MaybeArg(std::string_view key) const
+ {
+ return MaybeArg<R>(GetParamIndex(key));
+ }
std::string ToString() const;
/** Return the named args that need to be converted from string to another JSON type */
UniValue GetArgMap() const;
@@ -453,6 +500,8 @@ private:
mutable const JSONRPCRequest* m_req{nullptr}; // A pointer to the request for the duration of m_fun()
template <typename R>
R ArgValue(size_t i) const;
+ //! Return positional index of a parameter using its name as key.
+ size_t GetParamIndex(std::string_view key) const;
};
/**
@@ -464,4 +513,6 @@ private:
void PushWarnings(const UniValue& warnings, UniValue& obj);
void PushWarnings(const std::vector<bilingual_str>& warnings, UniValue& obj);
+UniValue GetNodeWarnings(bool use_deprecated);
+
#endif // BITCOIN_RPC_UTIL_H