aboutsummaryrefslogtreecommitdiff
path: root/src/rpc
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc')
-rw-r--r--src/rpc/blockchain.cpp276
-rw-r--r--src/rpc/blockchain.h8
-rw-r--r--src/rpc/client.cpp1
-rw-r--r--src/rpc/mining.cpp184
-rw-r--r--src/rpc/misc.cpp23
-rw-r--r--src/rpc/net.cpp33
-rw-r--r--src/rpc/rawtransaction.cpp76
-rw-r--r--src/rpc/util.cpp17
-rw-r--r--src/rpc/util.h2
9 files changed, 309 insertions, 311 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index e41a78f917..4d5449952d 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -17,8 +17,8 @@
#include <node/coinstats.h>
#include <node/context.h>
#include <node/utxo_snapshot.h>
-#include <policy/fees.h>
#include <policy/feerate.h>
+#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/rbf.h>
#include <primitives/transaction.h>
@@ -156,21 +156,11 @@ UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex
UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, bool txDetails)
{
- // Serialize passed information without accessing chain state of the active chain!
- AssertLockNotHeld(cs_main); // For performance reasons
+ UniValue result = blockheaderToJSON(tip, blockindex);
- UniValue result(UniValue::VOBJ);
- result.pushKV("hash", blockindex->GetBlockHash().GetHex());
- const CBlockIndex* pnext;
- int confirmations = ComputeNextBlockAndDepth(tip, blockindex, pnext);
- result.pushKV("confirmations", confirmations);
result.pushKV("strippedsize", (int)::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS));
result.pushKV("size", (int)::GetSerializeSize(block, PROTOCOL_VERSION));
result.pushKV("weight", (int)::GetBlockWeight(block));
- result.pushKV("height", blockindex->nHeight);
- result.pushKV("version", block.nVersion);
- result.pushKV("versionHex", strprintf("%08x", block.nVersion));
- result.pushKV("merkleroot", block.hashMerkleRoot.GetHex());
UniValue txs(UniValue::VARR);
if (txDetails) {
CBlockUndo blockUndo;
@@ -189,18 +179,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
}
}
result.pushKV("tx", txs);
- result.pushKV("time", block.GetBlockTime());
- result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast());
- result.pushKV("nonce", (uint64_t)block.nNonce);
- result.pushKV("bits", strprintf("%08x", block.nBits));
- result.pushKV("difficulty", GetDifficulty(blockindex));
- result.pushKV("chainwork", blockindex->nChainWork.GetHex());
- result.pushKV("nTx", (uint64_t)blockindex->nTx);
- if (blockindex->pprev)
- result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex());
- if (pnext)
- result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex());
return result;
}
@@ -821,8 +800,8 @@ static RPCHelpMan getblockheader()
{RPCResult::Type::NUM, "difficulty", "The difficulty"},
{RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the current chain"},
{RPCResult::Type::NUM, "nTx", "The number of transactions in the block"},
- {RPCResult::Type::STR_HEX, "previousblockhash", "The hash of the previous block"},
- {RPCResult::Type::STR_HEX, "nextblockhash", "The hash of the next block"},
+ {RPCResult::Type::STR_HEX, "previousblockhash", /* optional */ true, "The hash of the previous block (if available)"},
+ {RPCResult::Type::STR_HEX, "nextblockhash", /* optional */ true, "The hash of the next block (if available)"},
}},
RPCResult{"for verbose=false",
RPCResult::Type::STR_HEX, "", "A string that is serialized, hex-encoded data for block 'hash'"},
@@ -843,7 +822,7 @@ static RPCHelpMan getblockheader()
const CBlockIndex* tip;
{
LOCK(cs_main);
- pblockindex = LookupBlockIndex(hash);
+ pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash);
tip = ::ChainActive().Tip();
}
@@ -929,8 +908,8 @@ static RPCHelpMan getblock()
{RPCResult::Type::NUM, "difficulty", "The difficulty"},
{RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the chain up to this block (in hex)"},
{RPCResult::Type::NUM, "nTx", "The number of transactions in the block"},
- {RPCResult::Type::STR_HEX, "previousblockhash", "The hash of the previous block"},
- {RPCResult::Type::STR_HEX, "nextblockhash", "The hash of the next block"},
+ {RPCResult::Type::STR_HEX, "previousblockhash", /* optional */ true, "The hash of the previous block (if available)"},
+ {RPCResult::Type::STR_HEX, "nextblockhash", /* optional */ true, "The hash of the next block (if available)"},
}},
RPCResult{"for verbosity = 2",
RPCResult::Type::OBJ, "", "",
@@ -967,7 +946,7 @@ static RPCHelpMan getblock()
const CBlockIndex* tip;
{
LOCK(cs_main);
- pblockindex = LookupBlockIndex(hash);
+ pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash);
tip = ::ChainActive().Tip();
if (!pblockindex) {
@@ -1036,7 +1015,7 @@ static RPCHelpMan pruneblockchain()
height = chainHeight - MIN_BLOCKS_TO_KEEP;
}
- PruneBlockFilesManual(height);
+ PruneBlockFilesManual(::ChainstateActive(), height);
const CBlockIndex* block = ::ChainActive().Tip();
CHECK_NONFATAL(block);
while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
@@ -1047,13 +1026,26 @@ static RPCHelpMan pruneblockchain()
};
}
+CoinStatsHashType ParseHashType(const std::string& hash_type_input)
+{
+ if (hash_type_input == "hash_serialized_2") {
+ return CoinStatsHashType::HASH_SERIALIZED;
+ } else if (hash_type_input == "muhash") {
+ return CoinStatsHashType::MUHASH;
+ } else if (hash_type_input == "none") {
+ return CoinStatsHashType::NONE;
+ } else {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s is not a valid hash_type", hash_type_input));
+ }
+}
+
static RPCHelpMan gettxoutsetinfo()
{
return RPCHelpMan{"gettxoutsetinfo",
"\nReturns statistics about the unspent transaction output set.\n"
"Note this call may take some time.\n",
{
- {"hash_type", RPCArg::Type::STR, /* default */ "hash_serialized_2", "Which UTXO set hash should be calculated. Options: 'hash_serialized_2' (the legacy algorithm), 'none'."},
+ {"hash_type", RPCArg::Type::STR, /* default */ "hash_serialized_2", "Which UTXO set hash should be calculated. Options: 'hash_serialized_2' (the legacy algorithm), 'muhash', 'none'."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@@ -1063,7 +1055,8 @@ static RPCHelpMan gettxoutsetinfo()
{RPCResult::Type::NUM, "transactions", "The number of transactions with unspent outputs"},
{RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs"},
{RPCResult::Type::NUM, "bogosize", "A meaningless metric for UTXO set size"},
- {RPCResult::Type::STR_HEX, "hash_serialized_2", "The serialized hash (only present if 'hash_serialized_2' hash_type is chosen)"},
+ {RPCResult::Type::STR_HEX, "hash_serialized_2", /* optional */ true, "The serialized hash (only present if 'hash_serialized_2' hash_type is chosen)"},
+ {RPCResult::Type::STR_HEX, "muhash", /* optional */ true, "The serialized hash (only present if 'muhash' hash_type is chosen)"},
{RPCResult::Type::NUM, "disk_size", "The estimated size of the chainstate on disk"},
{RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount"},
}},
@@ -1078,11 +1071,11 @@ static RPCHelpMan gettxoutsetinfo()
CCoinsStats stats;
::ChainstateActive().ForceFlushStateToDisk();
- const CoinStatsHashType hash_type = ParseHashType(request.params[0], CoinStatsHashType::HASH_SERIALIZED);
+ const CoinStatsHashType hash_type{request.params[0].isNull() ? CoinStatsHashType::HASH_SERIALIZED : ParseHashType(request.params[0].get_str())};
- CCoinsView* coins_view = WITH_LOCK(cs_main, return &ChainstateActive().CoinsDB());
+ CCoinsView* coins_view = WITH_LOCK(::cs_main, return &::ChainstateActive().CoinsDB());
NodeContext& node = EnsureNodeContext(request.context);
- if (GetUTXOStats(coins_view, stats, hash_type, node.rpc_interruption_point)) {
+ if (GetUTXOStats(coins_view, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)), stats, hash_type, node.rpc_interruption_point)) {
ret.pushKV("height", (int64_t)stats.nHeight);
ret.pushKV("bestblock", stats.hashBlock.GetHex());
ret.pushKV("transactions", (int64_t)stats.nTransactions);
@@ -1091,6 +1084,9 @@ static RPCHelpMan gettxoutsetinfo()
if (hash_type == CoinStatsHashType::HASH_SERIALIZED) {
ret.pushKV("hash_serialized_2", stats.hashSerialized.GetHex());
}
+ if (hash_type == CoinStatsHashType::MUHASH) {
+ ret.pushKV("muhash", stats.hashSerialized.GetHex());
+ }
ret.pushKV("disk_size", stats.nDiskSize);
ret.pushKV("total_amount", ValueFromAmount(stats.nTotalAmount));
} else {
@@ -1104,30 +1100,29 @@ static RPCHelpMan gettxoutsetinfo()
static RPCHelpMan gettxout()
{
return RPCHelpMan{"gettxout",
- "\nReturns details about an unspent transaction output.\n",
- {
- {"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
- {"n", RPCArg::Type::NUM, RPCArg::Optional::NO, "vout number"},
- {"include_mempool", RPCArg::Type::BOOL, /* default */ "true", "Whether to include the mempool. Note that an unspent output that is spent in the mempool won't appear."},
- },
- RPCResult{
- RPCResult::Type::OBJ, "", "",
- {
- {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"},
- {RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
- {RPCResult::Type::STR_AMOUNT, "value", "The transaction value in " + CURRENCY_UNIT},
- {RPCResult::Type::OBJ, "scriptPubKey", "",
- {
- {RPCResult::Type::STR_HEX, "asm", ""},
- {RPCResult::Type::STR_HEX, "hex", ""},
- {RPCResult::Type::NUM, "reqSigs", "Number of required signatures"},
- {RPCResult::Type::STR_HEX, "type", "The type, eg pubkeyhash"},
- {RPCResult::Type::ARR, "addresses", "array of bitcoin addresses",
- {{RPCResult::Type::STR, "address", "bitcoin address"}}},
- }},
- {RPCResult::Type::BOOL, "coinbase", "Coinbase or not"},
- }},
- RPCExamples{
+ "\nReturns details about an unspent transaction output.\n",
+ {
+ {"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
+ {"n", RPCArg::Type::NUM, RPCArg::Optional::NO, "vout number"},
+ {"include_mempool", RPCArg::Type::BOOL, /* default */ "true", "Whether to include the mempool. Note that an unspent output that is spent in the mempool won't appear."},
+ },
+ {
+ RPCResult{"If the UTXO was not found", RPCResult::Type::NONE, "", ""},
+ RPCResult{"Otherwise", RPCResult::Type::OBJ, "", "", {
+ {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"},
+ {RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
+ {RPCResult::Type::STR_AMOUNT, "value", "The transaction value in " + CURRENCY_UNIT},
+ {RPCResult::Type::OBJ, "scriptPubKey", "", {
+ {RPCResult::Type::STR_HEX, "asm", ""},
+ {RPCResult::Type::STR_HEX, "hex", ""},
+ {RPCResult::Type::NUM, "reqSigs", "Number of required signatures"},
+ {RPCResult::Type::STR_HEX, "type", "The type, eg pubkeyhash"},
+ {RPCResult::Type::ARR, "addresses", "array of bitcoin addresses", {{RPCResult::Type::STR, "address", "bitcoin address"}}},
+ }},
+ {RPCResult::Type::BOOL, "coinbase", "Coinbase or not"},
+ }},
+ },
+ RPCExamples{
"\nGet unspent transactions\n"
+ HelpExampleCli("listunspent", "") +
"\nView the details\n"
@@ -1164,7 +1159,7 @@ static RPCHelpMan gettxout()
}
}
- const CBlockIndex* pindex = LookupBlockIndex(coins_view->GetBestBlock());
+ const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(coins_view->GetBestBlock());
ret.pushKV("bestblock", pindex->GetBlockHash().GetHex());
if (coin.nHeight == MEMPOOL_HEIGHT) {
ret.pushKV("confirmations", 0);
@@ -1204,7 +1199,7 @@ static RPCHelpMan verifychain()
LOCK(cs_main);
- return CVerifyDB().VerifyDB(Params(), &::ChainstateActive().CoinsTip(), check_level, check_depth);
+ return CVerifyDB().VerifyDB(Params(), ::ChainstateActive(), &::ChainstateActive().CoinsTip(), check_level, check_depth);
},
};
}
@@ -1236,7 +1231,7 @@ static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &nam
if (consensusParams.vDeployments[id].nTimeout <= 1230768000) return;
UniValue bip9(UniValue::VOBJ);
- const ThresholdState thresholdState = VersionBitsTipState(consensusParams, id);
+ const ThresholdState thresholdState = VersionBitsState(::ChainActive().Tip(), consensusParams, id, versionbitscache);
switch (thresholdState) {
case ThresholdState::DEFINED: bip9.pushKV("status", "defined"); break;
case ThresholdState::STARTED: bip9.pushKV("status", "started"); break;
@@ -1250,12 +1245,12 @@ static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &nam
}
bip9.pushKV("start_time", consensusParams.vDeployments[id].nStartTime);
bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
- int64_t since_height = VersionBitsTipStateSinceHeight(consensusParams, id);
+ int64_t since_height = VersionBitsStateSinceHeight(::ChainActive().Tip(), consensusParams, id, versionbitscache);
bip9.pushKV("since", since_height);
if (ThresholdState::STARTED == thresholdState)
{
UniValue statsUV(UniValue::VOBJ);
- BIP9Stats statsStruct = VersionBitsTipStatistics(consensusParams, id);
+ BIP9Stats statsStruct = VersionBitsStatistics(::ChainActive().Tip(), consensusParams, id);
statsUV.pushKV("period", statsStruct.period);
statsUV.pushKV("threshold", statsStruct.threshold);
statsUV.pushKV("elapsed", statsStruct.elapsed);
@@ -1500,6 +1495,7 @@ UniValue MempoolInfoToJSON(const CTxMemPool& pool)
ret.pushKV("size", (int64_t)pool.size());
ret.pushKV("bytes", (int64_t)pool.GetTotalTxSize());
ret.pushKV("usage", (int64_t)pool.DynamicMemoryUsage());
+ ret.pushKV("total_fee", ValueFromAmount(pool.GetTotalFee()));
size_t maxmempool = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
ret.pushKV("maxmempool", (int64_t) maxmempool);
ret.pushKV("mempoolminfee", ValueFromAmount(std::max(pool.GetMinFee(maxmempool), ::minRelayTxFee).GetFeePerK()));
@@ -1520,6 +1516,7 @@ static RPCHelpMan getmempoolinfo()
{RPCResult::Type::NUM, "size", "Current tx count"},
{RPCResult::Type::NUM, "bytes", "Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted"},
{RPCResult::Type::NUM, "usage", "Total memory usage for the mempool"},
+ {RPCResult::Type::STR_AMOUNT, "total_fee", "Total fees for the mempool in " + CURRENCY_UNIT + ", ignoring modified fees through prioritizetransaction"},
{RPCResult::Type::NUM, "maxmempool", "Maximum memory usage for the mempool"},
{RPCResult::Type::STR_AMOUNT, "mempoolminfee", "Minimum fee rate in " + CURRENCY_UNIT + "/kB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee"},
{RPCResult::Type::STR_AMOUNT, "minrelaytxfee", "Current minimum relay fee for transactions"},
@@ -1557,14 +1554,14 @@ static RPCHelpMan preciousblock()
{
LOCK(cs_main);
- pblockindex = LookupBlockIndex(hash);
+ pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash);
if (!pblockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
}
BlockValidationState state;
- PreciousBlock(state, Params(), pblockindex);
+ ::ChainstateActive().PreciousBlock(state, Params(), pblockindex);
if (!state.IsValid()) {
throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
@@ -1595,15 +1592,15 @@ static RPCHelpMan invalidateblock()
CBlockIndex* pblockindex;
{
LOCK(cs_main);
- pblockindex = LookupBlockIndex(hash);
+ pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash);
if (!pblockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
}
- InvalidateBlock(state, Params(), pblockindex);
+ ::ChainstateActive().InvalidateBlock(state, Params(), pblockindex);
if (state.IsValid()) {
- ActivateBestChain(state, Params());
+ ::ChainstateActive().ActivateBestChain(state, Params());
}
if (!state.IsValid()) {
@@ -1634,16 +1631,16 @@ static RPCHelpMan reconsiderblock()
{
LOCK(cs_main);
- CBlockIndex* pblockindex = LookupBlockIndex(hash);
+ CBlockIndex* pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash);
if (!pblockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
- ResetBlockFailureFlags(pblockindex);
+ ::ChainstateActive().ResetBlockFailureFlags(pblockindex);
}
BlockValidationState state;
- ActivateBestChain(state, Params());
+ ::ChainstateActive().ActivateBestChain(state, Params());
if (!state.IsValid()) {
throw JSONRPCError(RPC_DATABASE_ERROR, state.ToString());
@@ -1689,7 +1686,7 @@ static RPCHelpMan getchaintxstats()
} else {
uint256 hash(ParseHashV(request.params[1], "blockhash"));
LOCK(cs_main);
- pindex = LookupBlockIndex(hash);
+ pindex = g_chainman.m_blockman.LookupBlockIndex(hash);
if (!pindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
@@ -1867,7 +1864,7 @@ static RPCHelpMan getblockstats()
pindex = ::ChainActive()[height];
} else {
const uint256 hash(ParseHashV(request.params[0], "hash_or_height"));
- pindex = LookupBlockIndex(hash);
+ pindex = g_chainman.m_blockman.LookupBlockIndex(hash);
if (!pindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
@@ -2139,59 +2136,64 @@ public:
static RPCHelpMan scantxoutset()
{
return RPCHelpMan{"scantxoutset",
- "\nEXPERIMENTAL warning: this call may be removed or changed in future releases.\n"
- "\nScans the unspent transaction output set for entries that match certain output descriptors.\n"
- "Examples of output descriptors are:\n"
- " addr(<address>) Outputs whose scriptPubKey corresponds to the specified address (does not include P2PK)\n"
- " raw(<hex script>) Outputs whose scriptPubKey equals the specified hex scripts\n"
- " combo(<pubkey>) P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH outputs for the given pubkey\n"
- " pkh(<pubkey>) P2PKH outputs for the given pubkey\n"
- " sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n"
- "\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n"
- "or more path elements separated by \"/\", and optionally ending in \"/*\" (unhardened), or \"/*'\" or \"/*h\" (hardened) to specify all\n"
- "unhardened or hardened child keys.\n"
- "In the latter case, a range needs to be specified by below if different from 1000.\n"
- "For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n",
+ "\nEXPERIMENTAL warning: this call may be removed or changed in future releases.\n"
+ "\nScans the unspent transaction output set for entries that match certain output descriptors.\n"
+ "Examples of output descriptors are:\n"
+ " addr(<address>) Outputs whose scriptPubKey corresponds to the specified address (does not include P2PK)\n"
+ " raw(<hex script>) Outputs whose scriptPubKey equals the specified hex scripts\n"
+ " combo(<pubkey>) P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH outputs for the given pubkey\n"
+ " pkh(<pubkey>) P2PKH outputs for the given pubkey\n"
+ " sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n"
+ "\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n"
+ "or more path elements separated by \"/\", and optionally ending in \"/*\" (unhardened), or \"/*'\" or \"/*h\" (hardened) to specify all\n"
+ "unhardened or hardened child keys.\n"
+ "In the latter case, a range needs to be specified by below if different from 1000.\n"
+ "For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n",
+ {
+ {"action", RPCArg::Type::STR, RPCArg::Optional::NO, "The action to execute\n"
+ "\"start\" for starting a scan\n"
+ "\"abort\" for aborting the current scan (returns true when abort was successful)\n"
+ "\"status\" for progress report (in %) of the current scan"},
+ {"scanobjects", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "Array of scan objects. Required for \"start\" action\n"
+ "Every scan object is either a string descriptor or an object:",
+ {
+ {"descriptor", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "An output descriptor"},
+ {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "An object with output descriptor and metadata",
{
- {"action", RPCArg::Type::STR, RPCArg::Optional::NO, "The action to execute\n"
- " \"start\" for starting a scan\n"
- " \"abort\" for aborting the current scan (returns true when abort was successful)\n"
- " \"status\" for progress report (in %) of the current scan"},
- {"scanobjects", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "Array of scan objects. Required for \"start\" action\n"
- " Every scan object is either a string descriptor or an object:",
- {
- {"descriptor", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "An output descriptor"},
- {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "An object with output descriptor and metadata",
- {
- {"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "An output descriptor"},
- {"range", RPCArg::Type::RANGE, /* default */ "1000", "The range of HD chain indexes to explore (either end or [begin,end])"},
- },
- },
- },
+ {"desc", RPCArg::Type::STR, RPCArg::Optional::NO, "An output descriptor"},
+ {"range", RPCArg::Type::RANGE, /* default */ "1000", "The range of HD chain indexes to explore (either end or [begin,end])"},
+ }},
+ },
"[scanobjects,...]"},
- },
- RPCResult{
- RPCResult::Type::OBJ, "", "",
+ },
+ {
+ RPCResult{"When action=='abort'", RPCResult::Type::BOOL, "", ""},
+ RPCResult{"When action=='status' and no scan is in progress", RPCResult::Type::NONE, "", ""},
+ RPCResult{"When action=='status' and scan is in progress", RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::NUM, "progress", "The scan progress"},
+ }},
+ RPCResult{"When action=='start'", RPCResult::Type::OBJ, "", "", {
+ {RPCResult::Type::BOOL, "success", "Whether the scan was completed"},
+ {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs scanned"},
+ {RPCResult::Type::NUM, "height", "The current block height (index)"},
+ {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"},
+ {RPCResult::Type::ARR, "unspents", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::BOOL, "success", "Whether the scan was completed"},
- {RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs scanned"},
- {RPCResult::Type::NUM, "height", "The current block height (index)"},
- {RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at the tip of the chain"},
- {RPCResult::Type::ARR, "unspents", "",
- {
- {RPCResult::Type::OBJ, "", "",
- {
- {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
- {RPCResult::Type::NUM, "vout", "The vout value"},
- {RPCResult::Type::STR_HEX, "scriptPubKey", "The script key"},
- {RPCResult::Type::STR, "desc", "A specialized descriptor for the matched scriptPubKey"},
- {RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " of the unspent output"},
- {RPCResult::Type::NUM, "height", "Height of the unspent transaction output"},
- }},
- }},
- {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of all found unspent outputs in " + CURRENCY_UNIT},
+ {RPCResult::Type::STR_HEX, "txid", "The transaction id"},
+ {RPCResult::Type::NUM, "vout", "The vout value"},
+ {RPCResult::Type::STR_HEX, "scriptPubKey", "The script key"},
+ {RPCResult::Type::STR, "desc", "A specialized descriptor for the matched scriptPubKey"},
+ {RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " of the unspent output"},
+ {RPCResult::Type::NUM, "height", "Height of the unspent transaction output"},
}},
- RPCExamples{""},
+ }},
+ {RPCResult::Type::STR_AMOUNT, "total_amount", "The total amount of all found unspent outputs in " + CURRENCY_UNIT},
+ }},
+ },
+ RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR});
@@ -2330,7 +2332,7 @@ static RPCHelpMan getblockfilter()
bool block_was_connected;
{
LOCK(cs_main);
- block_index = LookupBlockIndex(block_hash);
+ block_index = g_chainman.m_blockman.LookupBlockIndex(block_hash);
if (!block_index) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
@@ -2413,10 +2415,21 @@ static RPCHelpMan dumptxoutset()
FILE* file{fsbridge::fopen(temppath, "wb")};
CAutoFile afile{file, SER_DISK, CLIENT_VERSION};
+ NodeContext& node = EnsureNodeContext(request.context);
+ UniValue result = CreateUTXOSnapshot(node, node.chainman->ActiveChainstate(), afile);
+ fs::rename(temppath, path);
+
+ result.pushKV("path", path.string());
+ return result;
+},
+ };
+}
+
+UniValue CreateUTXOSnapshot(NodeContext& node, CChainState& chainstate, CAutoFile& afile)
+{
std::unique_ptr<CCoinsViewCursor> pcursor;
CCoinsStats stats;
CBlockIndex* tip;
- NodeContext& node = EnsureNodeContext(request.context);
{
// We need to lock cs_main to ensure that the coinsdb isn't written to
@@ -2433,14 +2446,14 @@ static RPCHelpMan dumptxoutset()
//
LOCK(::cs_main);
- ::ChainstateActive().ForceFlushStateToDisk();
+ chainstate.ForceFlushStateToDisk();
- if (!GetUTXOStats(&::ChainstateActive().CoinsDB(), stats, CoinStatsHashType::NONE, node.rpc_interruption_point)) {
+ if (!GetUTXOStats(&chainstate.CoinsDB(), chainstate.m_blockman, stats, CoinStatsHashType::NONE, node.rpc_interruption_point)) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
}
- pcursor = std::unique_ptr<CCoinsViewCursor>(::ChainstateActive().CoinsDB().Cursor());
- tip = LookupBlockIndex(stats.hashBlock);
+ pcursor = std::unique_ptr<CCoinsViewCursor>(chainstate.CoinsDB().Cursor());
+ tip = g_chainman.m_blockman.LookupBlockIndex(stats.hashBlock);
CHECK_NONFATAL(tip);
}
@@ -2464,16 +2477,13 @@ static RPCHelpMan dumptxoutset()
}
afile.fclose();
- fs::rename(temppath, path);
UniValue result(UniValue::VOBJ);
result.pushKV("coins_written", stats.coins_count);
result.pushKV("base_hash", tip->GetBlockHash().ToString());
result.pushKV("base_height", tip->nHeight);
- result.pushKV("path", path.string());
+
return result;
-},
- };
}
void RegisterBlockchainRPCCommands(CRPCTable &t)
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index e4ce80400e..d8cae4dd24 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -6,6 +6,7 @@
#define BITCOIN_RPC_BLOCKCHAIN_H
#include <amount.h>
+#include <streams.h>
#include <sync.h>
#include <stdint.h>
@@ -16,6 +17,7 @@ extern RecursiveMutex cs_main;
class CBlock;
class CBlockIndex;
class CBlockPolicyEstimator;
+class CChainState;
class CTxMemPool;
class ChainstateManager;
class UniValue;
@@ -57,4 +59,10 @@ CTxMemPool& EnsureMemPool(const util::Ref& context);
ChainstateManager& EnsureChainman(const util::Ref& context);
CBlockPolicyEstimator& EnsureFeeEstimator(const util::Ref& context);
+/**
+ * Helper to create UTXO snapshots given a chainstate and a file handle.
+ * @return a UniValue map containing metadata about the snapshot.
+ */
+UniValue CreateUTXOSnapshot(NodeContext& node, CChainState& chainstate, CAutoFile& afile);
+
#endif
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index d1eb849b7e..2b593cd10b 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -183,6 +183,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "createwallet", 4, "avoid_reuse"},
{ "createwallet", 5, "descriptors"},
{ "createwallet", 6, "load_on_startup"},
+ { "createwallet", 7, "external_signer"},
{ "loadwallet", 1, "load_on_startup"},
{ "unloadwallet", 1, "load_on_startup"},
{ "getnodeaddresses", 0, "count"},
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index c723f1d7ea..fd780ba782 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -150,7 +150,7 @@ static UniValue generateBlocks(ChainstateManager& chainman, const CTxMemPool& me
UniValue blockHashes(UniValue::VARR);
while (nHeight < nHeightEnd && !ShutdownRequested())
{
- std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(mempool, Params()).CreateNewBlock(coinbase_script));
+ std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(mempool, Params()).CreateNewBlock(::ChainstateActive(), coinbase_script));
if (!pblocktemplate.get())
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
CBlock *pblock = &pblocktemplate->block;
@@ -358,7 +358,7 @@ static RPCHelpMan generateblock()
LOCK(cs_main);
CTxMemPool empty_mempool;
- std::unique_ptr<CBlockTemplate> blocktemplate(BlockAssembler(empty_mempool, chainparams).CreateNewBlock(coinbase_script));
+ std::unique_ptr<CBlockTemplate> blocktemplate(BlockAssembler(empty_mempool, chainparams).CreateNewBlock(::ChainstateActive(), coinbase_script));
if (!blocktemplate) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
}
@@ -369,13 +369,13 @@ static RPCHelpMan generateblock()
// Add transactions
block.vtx.insert(block.vtx.end(), txs.begin(), txs.end());
- RegenerateCommitments(block);
+ RegenerateCommitments(block, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)));
{
LOCK(cs_main);
BlockValidationState state;
- if (!TestBlockValidity(state, chainparams, block, LookupBlockIndex(block.hashPrevBlock), false, false)) {
+ if (!TestBlockValidity(state, chainparams, ::ChainstateActive(), block, g_chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock), false, false)) {
throw JSONRPCError(RPC_VERIFY_ERROR, strprintf("TestBlockValidity failed: %s", state.ToString()));
}
}
@@ -505,83 +505,84 @@ static std::string gbt_vb_name(const Consensus::DeploymentPos pos) {
static RPCHelpMan getblocktemplate()
{
return RPCHelpMan{"getblocktemplate",
- "\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n"
- "It returns data needed to construct a block to work on.\n"
- "For full specification, see BIPs 22, 23, 9, and 145:\n"
- " https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n"
- " https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki\n"
- " https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n"
- " https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n",
+ "\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n"
+ "It returns data needed to construct a block to work on.\n"
+ "For full specification, see BIPs 22, 23, 9, and 145:\n"
+ " https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n"
+ " https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki\n"
+ " https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n"
+ " https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n",
+ {
+ {"template_request", RPCArg::Type::OBJ, "{}", "Format of the template",
+ {
+ {"mode", RPCArg::Type::STR, /* treat as named arg */ RPCArg::Optional::OMITTED_NAMED_ARG, "This must be set to \"template\", \"proposal\" (see BIP 23), or omitted"},
+ {"capabilities", RPCArg::Type::ARR, /* treat as named arg */ RPCArg::Optional::OMITTED_NAMED_ARG, "A list of strings",
{
- {"template_request", RPCArg::Type::OBJ, "{}", "Format of the template",
- {
- {"mode", RPCArg::Type::STR, /* treat as named arg */ RPCArg::Optional::OMITTED_NAMED_ARG, "This must be set to \"template\", \"proposal\" (see BIP 23), or omitted"},
- {"capabilities", RPCArg::Type::ARR, /* treat as named arg */ RPCArg::Optional::OMITTED_NAMED_ARG, "A list of strings",
- {
- {"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "client side supported feature, 'longpoll', 'coinbasevalue', 'proposal', 'serverlist', 'workid'"},
- },
- },
- {"rules", RPCArg::Type::ARR, RPCArg::Optional::NO, "A list of strings",
- {
- {"segwit", RPCArg::Type::STR, RPCArg::Optional::NO, "(literal) indicates client side segwit support"},
- {"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "other client side supported softfork deployment"},
- },
- },
- },
+ {"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "client side supported feature, 'longpoll', 'coinbasevalue', 'proposal', 'serverlist', 'workid'"},
+ }},
+ {"rules", RPCArg::Type::ARR, RPCArg::Optional::NO, "A list of strings",
+ {
+ {"segwit", RPCArg::Type::STR, RPCArg::Optional::NO, "(literal) indicates client side segwit support"},
+ {"str", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "other client side supported softfork deployment"},
+ }},
+ },
"\"template_request\""},
- },
- RPCResult{
- RPCResult::Type::OBJ, "", "",
+ },
+ {
+ RPCResult{"If the proposal was accepted with mode=='proposal'", RPCResult::Type::NONE, "", ""},
+ RPCResult{"If the proposal was not accepted with mode=='proposal'", RPCResult::Type::STR, "", "According to BIP22"},
+ RPCResult{"Otherwise", RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::NUM, "version", "The preferred block version"},
+ {RPCResult::Type::ARR, "rules", "specific block rules that are to be enforced",
+ {
+ {RPCResult::Type::STR, "", "name of a rule the client must understand to some extent; see BIP 9 for format"},
+ }},
+ {RPCResult::Type::OBJ_DYN, "vbavailable", "set of pending, supported versionbit (BIP 9) softfork deployments",
+ {
+ {RPCResult::Type::NUM, "rulename", "identifies the bit number as indicating acceptance and readiness for the named softfork rule"},
+ }},
+ {RPCResult::Type::NUM, "vbrequired", "bit mask of versionbits the server requires set in submissions"},
+ {RPCResult::Type::STR, "previousblockhash", "The hash of current highest block"},
+ {RPCResult::Type::ARR, "transactions", "contents of non-coinbase transactions that should be included in the next block",
+ {
+ {RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::NUM, "version", "The preferred block version"},
- {RPCResult::Type::ARR, "rules", "specific block rules that are to be enforced",
- {
- {RPCResult::Type::STR, "", "name of a rule the client must understand to some extent; see BIP 9 for format"},
- }},
- {RPCResult::Type::OBJ_DYN, "vbavailable", "set of pending, supported versionbit (BIP 9) softfork deployments",
- {
- {RPCResult::Type::NUM, "rulename", "identifies the bit number as indicating acceptance and readiness for the named softfork rule"},
- }},
- {RPCResult::Type::NUM, "vbrequired", "bit mask of versionbits the server requires set in submissions"},
- {RPCResult::Type::STR, "previousblockhash", "The hash of current highest block"},
- {RPCResult::Type::ARR, "transactions", "contents of non-coinbase transactions that should be included in the next block",
- {
- {RPCResult::Type::OBJ, "", "",
- {
- {RPCResult::Type::STR_HEX, "data", "transaction data encoded in hexadecimal (byte-for-byte)"},
- {RPCResult::Type::STR_HEX, "txid", "transaction id encoded in little-endian hexadecimal"},
- {RPCResult::Type::STR_HEX, "hash", "hash encoded in little-endian hexadecimal (including witness data)"},
- {RPCResult::Type::ARR, "depends", "array of numbers",
- {
- {RPCResult::Type::NUM, "", "transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is"},
- }},
- {RPCResult::Type::NUM, "fee", "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"},
- {RPCResult::Type::NUM, "sigops", "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"},
- {RPCResult::Type::NUM, "weight", "total transaction weight, as counted for purposes of block limits"},
- }},
- }},
- {RPCResult::Type::OBJ_DYN, "coinbaseaux", "data that should be included in the coinbase's scriptSig content",
+ {RPCResult::Type::STR_HEX, "data", "transaction data encoded in hexadecimal (byte-for-byte)"},
+ {RPCResult::Type::STR_HEX, "txid", "transaction id encoded in little-endian hexadecimal"},
+ {RPCResult::Type::STR_HEX, "hash", "hash encoded in little-endian hexadecimal (including witness data)"},
+ {RPCResult::Type::ARR, "depends", "array of numbers",
{
- {RPCResult::Type::STR_HEX, "key", "values must be in the coinbase (keys may be ignored)"},
+ {RPCResult::Type::NUM, "", "transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is"},
}},
- {RPCResult::Type::NUM, "coinbasevalue", "maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)"},
- {RPCResult::Type::STR, "longpollid", "an id to include with a request to longpoll on an update to this template"},
- {RPCResult::Type::STR, "target", "The hash target"},
- {RPCResult::Type::NUM_TIME, "mintime", "The minimum timestamp appropriate for the next block time, expressed in " + UNIX_EPOCH_TIME},
- {RPCResult::Type::ARR, "mutable", "list of ways the block template may be changed",
- {
- {RPCResult::Type::STR, "value", "A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'"},
- }},
- {RPCResult::Type::STR_HEX, "noncerange", "A range of valid nonces"},
- {RPCResult::Type::NUM, "sigoplimit", "limit of sigops in blocks"},
- {RPCResult::Type::NUM, "sizelimit", "limit of block size"},
- {RPCResult::Type::NUM, "weightlimit", "limit of block weight"},
- {RPCResult::Type::NUM_TIME, "curtime", "current timestamp in " + UNIX_EPOCH_TIME},
- {RPCResult::Type::STR, "bits", "compressed target of next block"},
- {RPCResult::Type::NUM, "height", "The height of the next block"},
- {RPCResult::Type::STR, "default_witness_commitment", /* optional */ true, "a valid witness commitment for the unmodified block template"}
+ {RPCResult::Type::NUM, "fee", "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"},
+ {RPCResult::Type::NUM, "sigops", "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"},
+ {RPCResult::Type::NUM, "weight", "total transaction weight, as counted for purposes of block limits"},
}},
- RPCExamples{
+ }},
+ {RPCResult::Type::OBJ_DYN, "coinbaseaux", "data that should be included in the coinbase's scriptSig content",
+ {
+ {RPCResult::Type::STR_HEX, "key", "values must be in the coinbase (keys may be ignored)"},
+ }},
+ {RPCResult::Type::NUM, "coinbasevalue", "maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)"},
+ {RPCResult::Type::STR, "longpollid", "an id to include with a request to longpoll on an update to this template"},
+ {RPCResult::Type::STR, "target", "The hash target"},
+ {RPCResult::Type::NUM_TIME, "mintime", "The minimum timestamp appropriate for the next block time, expressed in " + UNIX_EPOCH_TIME},
+ {RPCResult::Type::ARR, "mutable", "list of ways the block template may be changed",
+ {
+ {RPCResult::Type::STR, "value", "A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'"},
+ }},
+ {RPCResult::Type::STR_HEX, "noncerange", "A range of valid nonces"},
+ {RPCResult::Type::NUM, "sigoplimit", "limit of sigops in blocks"},
+ {RPCResult::Type::NUM, "sizelimit", "limit of block size"},
+ {RPCResult::Type::NUM, "weightlimit", "limit of block weight"},
+ {RPCResult::Type::NUM_TIME, "curtime", "current timestamp in " + UNIX_EPOCH_TIME},
+ {RPCResult::Type::STR, "bits", "compressed target of next block"},
+ {RPCResult::Type::NUM, "height", "The height of the next block"},
+ {RPCResult::Type::STR, "default_witness_commitment", /* optional */ true, "a valid witness commitment for the unmodified block template"},
+ }},
+ },
+ RPCExamples{
HelpExampleCli("getblocktemplate", "'{\"rules\": [\"segwit\"]}'")
+ HelpExampleRpc("getblocktemplate", "{\"rules\": [\"segwit\"]}")
},
@@ -618,7 +619,7 @@ static RPCHelpMan getblocktemplate()
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
uint256 hash = block.GetHash();
- const CBlockIndex* pindex = LookupBlockIndex(hash);
+ const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash);
if (pindex) {
if (pindex->IsValid(BLOCK_VALID_SCRIPTS))
return "duplicate";
@@ -632,7 +633,7 @@ static RPCHelpMan getblocktemplate()
if (block.hashPrevBlock != pindexPrev->GetBlockHash())
return "inconclusive-not-best-prevblk";
BlockValidationState state;
- TestBlockValidity(state, Params(), block, pindexPrev, false, true);
+ TestBlockValidity(state, Params(), ::ChainstateActive(), block, pindexPrev, false, true);
return BIP22ValidationResult(state);
}
@@ -659,7 +660,7 @@ static RPCHelpMan getblocktemplate()
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
if (!Params().IsTestChain()) {
- if (node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0) {
+ if (node.connman->GetNodeCount(ConnectionDirection::Both) == 0) {
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, PACKAGE_NAME " is not connected!");
}
@@ -747,7 +748,7 @@ static RPCHelpMan getblocktemplate()
// Create new block
CScript scriptDummy = CScript() << OP_TRUE;
- pblocktemplate = BlockAssembler(mempool, Params()).CreateNewBlock(scriptDummy);
+ pblocktemplate = BlockAssembler(mempool, Params()).CreateNewBlock(::ChainstateActive(), scriptDummy);
if (!pblocktemplate)
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
@@ -940,14 +941,17 @@ static RPCHelpMan submitblock()
{
// We allow 2 arguments for compliance with BIP22. Argument 2 is ignored.
return RPCHelpMan{"submitblock",
- "\nAttempts to submit new block to network.\n"
- "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n",
- {
- {"hexdata", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded block data to submit"},
- {"dummy", RPCArg::Type::STR, /* default */ "ignored", "dummy value, for compatibility with BIP22. This value is ignored."},
- },
- RPCResult{RPCResult::Type::NONE, "", "Returns JSON Null when valid, a string according to BIP22 otherwise"},
- RPCExamples{
+ "\nAttempts to submit new block to network.\n"
+ "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n",
+ {
+ {"hexdata", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "the hex-encoded block data to submit"},
+ {"dummy", RPCArg::Type::STR, /* default */ "ignored", "dummy value, for compatibility with BIP22. This value is ignored."},
+ },
+ {
+ RPCResult{"If the block was accepted", RPCResult::Type::NONE, "", ""},
+ RPCResult{"Otherwise", RPCResult::Type::STR, "", "According to BIP22"},
+ },
+ RPCExamples{
HelpExampleCli("submitblock", "\"mydata\"")
+ HelpExampleRpc("submitblock", "\"mydata\"")
},
@@ -966,7 +970,7 @@ static RPCHelpMan submitblock()
uint256 hash = block.GetHash();
{
LOCK(cs_main);
- const CBlockIndex* pindex = LookupBlockIndex(hash);
+ const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash);
if (pindex) {
if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) {
return "duplicate";
@@ -979,7 +983,7 @@ static RPCHelpMan submitblock()
{
LOCK(cs_main);
- const CBlockIndex* pindex = LookupBlockIndex(block.hashPrevBlock);
+ const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock);
if (pindex) {
UpdateUncommittedBlockStructures(block, pindex, Params().GetConsensus());
}
@@ -1023,7 +1027,7 @@ static RPCHelpMan submitheader()
}
{
LOCK(cs_main);
- if (!LookupBlockIndex(h.hashPrevBlock)) {
+ if (!g_chainman.m_blockman.LookupBlockIndex(h.hashPrevBlock)) {
throw JSONRPCError(RPC_VERIFY_ERROR, "Must submit previous header (" + h.hashPrevBlock.GetHex() + ") first");
}
}
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index b75a7b8d26..143be1274e 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -305,11 +305,11 @@ static RPCHelpMan verifymessage()
switch (MessageVerify(strAddress, strSign, strMessage)) {
case MessageVerificationResult::ERR_INVALID_ADDRESS:
- throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
case MessageVerificationResult::ERR_ADDRESS_NO_KEY:
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
case MessageVerificationResult::ERR_MALFORMED_SIGNATURE:
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
+ throw JSONRPCError(RPC_TYPE_ERROR, "Malformed base64 encoding");
case MessageVerificationResult::ERR_PUBKEY_NOT_RECOVERED:
case MessageVerificationResult::ERR_NOT_SIGNED:
return false;
@@ -365,13 +365,13 @@ static RPCHelpMan signmessagewithprivkey()
static RPCHelpMan setmocktime()
{
return RPCHelpMan{"setmocktime",
- "\nSet the local time to given timestamp (-regtest only)\n",
- {
- {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, UNIX_EPOCH_TIME + "\n"
- " Pass 0 to go back to using the system time."},
- },
- RPCResult{RPCResult::Type::NONE, "", ""},
- RPCExamples{""},
+ "\nSet the local time to given timestamp (-regtest only)\n",
+ {
+ {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, UNIX_EPOCH_TIME + "\n"
+ "Pass 0 to go back to using the system time."},
+ },
+ RPCResult{RPCResult::Type::NONE, "", ""},
+ RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
if (!Params().IsMockableChain()) {
@@ -386,7 +386,10 @@ static RPCHelpMan setmocktime()
LOCK(cs_main);
RPCTypeCheck(request.params, {UniValue::VNUM});
- int64_t time = request.params[0].get_int64();
+ const int64_t time{request.params[0].get_int64()};
+ if (time < 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Mocktime can not be negative: %s.", time));
+ }
SetMockTime(time);
if (request.context.Has<NodeContext>()) {
for (const auto& chain_client : request.context.Get<NodeContext>().chain_clients) {
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 47d77b341a..cf4d46cf2c 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -57,7 +57,7 @@ static RPCHelpMan getconnectioncount()
if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- return (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL);
+ return (int)node.connman->GetNodeCount(ConnectionDirection::Both);
},
};
}
@@ -77,13 +77,12 @@ static RPCHelpMan ping()
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
NodeContext& node = EnsureNodeContext(request.context);
- if(!node.connman)
+ if (!node.peerman) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+ }
// Request that each node send a ping during next message processing pass
- node.connman->ForEachNode([](CNode* pnode) {
- pnode->fPingQueued = true;
- });
+ node.peerman->SendPings();
return NullUniValue;
},
};
@@ -104,7 +103,7 @@ static RPCHelpMan getpeerinfo()
{RPCResult::Type::STR, "addr", "(host:port) The IP address and port of the peer"},
{RPCResult::Type::STR, "addrbind", "(ip:port) Bind address of the connection to the peer"},
{RPCResult::Type::STR, "addrlocal", "(ip:port) Local address as reported by the peer"},
- {RPCResult::Type::STR, "network", "Network (ipv4, ipv6, or onion) the peer connected through"},
+ {RPCResult::Type::STR, "network", "Network (" + Join(GetNetworkNames(/* append_unroutable */ true), ", ") + ")"},
{RPCResult::Type::NUM, "mapped_as", "The AS in the BGP route to the peer used for diversifying\n"
"peer selection (only available if the asmap config flag is set)"},
{RPCResult::Type::STR_HEX, "services", "The services offered"},
@@ -203,14 +202,14 @@ static RPCHelpMan getpeerinfo()
obj.pushKV("bytesrecv", stats.nRecvBytes);
obj.pushKV("conntime", stats.nTimeConnected);
obj.pushKV("timeoffset", stats.nTimeOffset);
- if (stats.m_ping_usec > 0) {
- obj.pushKV("pingtime", ((double)stats.m_ping_usec) / 1e6);
+ if (stats.m_last_ping_time > 0us) {
+ obj.pushKV("pingtime", CountSecondsDouble(stats.m_last_ping_time));
}
- if (stats.m_min_ping_usec < std::numeric_limits<int64_t>::max()) {
- obj.pushKV("minping", ((double)stats.m_min_ping_usec) / 1e6);
+ if (stats.m_min_ping_time < std::chrono::microseconds::max()) {
+ obj.pushKV("minping", CountSecondsDouble(stats.m_min_ping_time));
}
- if (stats.m_ping_wait_usec > 0) {
- obj.pushKV("pingwait", ((double)stats.m_ping_wait_usec) / 1e6);
+ if (fStateStats && statestats.m_ping_wait > 0s) {
+ obj.pushKV("pingwait", CountSecondsDouble(statestats.m_ping_wait));
}
obj.pushKV("version", stats.nVersion);
// Use the sanitized form of subver here, to avoid tricksy remote peers from
@@ -547,7 +546,7 @@ static UniValue GetNetworksInfo()
UniValue networks(UniValue::VARR);
for (int n = 0; n < NET_MAX; ++n) {
enum Network network = static_cast<enum Network>(n);
- if (network == NET_UNROUTABLE || network == NET_I2P || network == NET_CJDNS || network == NET_INTERNAL) continue;
+ if (network == NET_UNROUTABLE || network == NET_CJDNS || network == NET_INTERNAL) continue;
proxyType proxy;
UniValue obj(UniValue::VOBJ);
GetProxy(network, proxy);
@@ -587,7 +586,7 @@ static RPCHelpMan getnetworkinfo()
{
{RPCResult::Type::OBJ, "", "",
{
- {RPCResult::Type::STR, "name", "network (ipv4, ipv6 or onion)"},
+ {RPCResult::Type::STR, "name", "network (" + Join(GetNetworkNames(), ", ") + ")"},
{RPCResult::Type::BOOL, "limited", "is the network limited using -onlynet?"},
{RPCResult::Type::BOOL, "reachable", "is the network reachable?"},
{RPCResult::Type::STR, "proxy", "(\"host:port\") the proxy that is used for this network, or empty if none"},
@@ -631,9 +630,9 @@ static RPCHelpMan getnetworkinfo()
obj.pushKV("timeoffset", GetTimeOffset());
if (node.connman) {
obj.pushKV("networkactive", node.connman->GetNetworkActive());
- obj.pushKV("connections", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL));
- obj.pushKV("connections_in", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_IN));
- obj.pushKV("connections_out", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_OUT));
+ obj.pushKV("connections", (int)node.connman->GetNodeCount(ConnectionDirection::Both));
+ obj.pushKV("connections_in", (int)node.connman->GetNodeCount(ConnectionDirection::In));
+ obj.pushKV("connections_out", (int)node.connman->GetNodeCount(ConnectionDirection::Out));
}
obj.pushKV("networks", GetNetworksInfo());
obj.pushKV("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()));
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 031ed31da1..47c776bbd1 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -54,7 +54,7 @@ static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue&
LOCK(cs_main);
entry.pushKV("blockhash", hashBlock.GetHex());
- CBlockIndex* pindex = LookupBlockIndex(hashBlock);
+ CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock);
if (pindex) {
if (::ChainActive().Contains(pindex)) {
entry.pushKV("confirmations", 1 + ::ChainActive().Height() - pindex->nHeight);
@@ -178,7 +178,7 @@ static RPCHelpMan getrawtransaction()
LOCK(cs_main);
uint256 blockhash = ParseHashV(request.params[2], "parameter 3");
- blockindex = LookupBlockIndex(blockhash);
+ blockindex = g_chainman.m_blockman.LookupBlockIndex(blockhash);
if (!blockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block hash not found");
}
@@ -260,7 +260,7 @@ static RPCHelpMan gettxoutproof()
if (!request.params[1].isNull()) {
LOCK(cs_main);
hashBlock = ParseHashV(request.params[1], "blockhash");
- pblockindex = LookupBlockIndex(hashBlock);
+ pblockindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock);
if (!pblockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
@@ -290,7 +290,7 @@ static RPCHelpMan gettxoutproof()
if (!tx || hashBlock.IsNull()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block");
}
- pblockindex = LookupBlockIndex(hashBlock);
+ pblockindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock);
if (!pblockindex) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt");
}
@@ -350,7 +350,7 @@ static RPCHelpMan verifytxoutproof()
LOCK(cs_main);
- const CBlockIndex* pindex = LookupBlockIndex(merkleBlock.header.GetHash());
+ const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(merkleBlock.header.GetHash());
if (!pindex || !::ChainActive().Contains(pindex) || pindex->nTx == 0) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
}
@@ -816,10 +816,11 @@ static RPCHelpMan sendrawtransaction()
{
return RPCHelpMan{"sendrawtransaction",
"\nSubmit a raw transaction (serialized, hex-encoded) to local node and network.\n"
- "\nNote that the transaction will be sent unconditionally to all peers, so using this\n"
+ "\nThe transaction will be sent unconditionally to all peers, so using sendrawtransaction\n"
"for manual rebroadcast may degrade privacy by leaking the transaction's origin, as\n"
"nodes will normally not rebroadcast non-wallet transactions already in their mempool.\n"
- "\nAlso see createrawtransaction and signrawtransactionwithkey calls.\n",
+ "\nA specific exception, RPC_TRANSACTION_ALREADY_IN_CHAIN, may throw if the transaction cannot be added to the mempool.\n"
+ "\nRelated RPCs: createrawtransaction, signrawtransactionwithkey\n",
{
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
{"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK()),
@@ -945,44 +946,35 @@ static RPCHelpMan testmempoolaccept()
result_0.pushKV("txid", tx->GetHash().GetHex());
result_0.pushKV("wtxid", tx->GetWitnessHash().GetHex());
- TxValidationState state;
- bool test_accept_res;
- CAmount fee{0};
- {
- LOCK(cs_main);
- test_accept_res = AcceptToMemoryPool(mempool, state, std::move(tx),
- nullptr /* plTxnReplaced */, false /* bypass_limits */, /* test_accept */ true, &fee);
- }
-
- // Check that fee does not exceed maximum fee
- if (test_accept_res && max_raw_tx_fee && fee > max_raw_tx_fee) {
- result_0.pushKV("allowed", false);
- result_0.pushKV("reject-reason", "max-fee-exceeded");
- result.push_back(std::move(result_0));
- return result;
- }
- result_0.pushKV("allowed", test_accept_res);
+ const MempoolAcceptResult accept_result = WITH_LOCK(cs_main, return AcceptToMemoryPool(::ChainstateActive(), mempool, std::move(tx),
+ false /* bypass_limits */, /* test_accept */ true));
// Only return the fee and vsize if the transaction would pass ATMP.
// These can be used to calculate the feerate.
- if (test_accept_res) {
- result_0.pushKV("vsize", virtual_size);
- UniValue fees(UniValue::VOBJ);
- fees.pushKV("base", ValueFromAmount(fee));
- result_0.pushKV("fees", fees);
+ if (accept_result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
+ const CAmount fee = accept_result.m_base_fees.value();
+ // Check that fee does not exceed maximum fee
+ if (max_raw_tx_fee && fee > max_raw_tx_fee) {
+ result_0.pushKV("allowed", false);
+ result_0.pushKV("reject-reason", "max-fee-exceeded");
+ } else {
+ result_0.pushKV("allowed", true);
+ result_0.pushKV("vsize", virtual_size);
+ UniValue fees(UniValue::VOBJ);
+ fees.pushKV("base", ValueFromAmount(fee));
+ result_0.pushKV("fees", fees);
+ }
+ result.push_back(std::move(result_0));
} else {
- if (state.IsInvalid()) {
- if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
- result_0.pushKV("reject-reason", "missing-inputs");
- } else {
- result_0.pushKV("reject-reason", strprintf("%s", state.GetRejectReason()));
- }
+ result_0.pushKV("allowed", false);
+ const TxValidationState state = accept_result.m_state;
+ if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
+ result_0.pushKV("reject-reason", "missing-inputs");
} else {
result_0.pushKV("reject-reason", state.GetRejectReason());
}
+ result.push_back(std::move(result_0));
}
-
- result.push_back(std::move(result_0));
return result;
},
};
@@ -1344,7 +1336,7 @@ static RPCHelpMan combinepsbt()
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << merged_psbt;
- return EncodeBase64(MakeUCharSpan(ssTx));
+ return EncodeBase64(ssTx);
},
};
}
@@ -1483,7 +1475,7 @@ static RPCHelpMan createpsbt()
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
- return EncodeBase64(MakeUCharSpan(ssTx));
+ return EncodeBase64(ssTx);
},
};
}
@@ -1552,7 +1544,7 @@ static RPCHelpMan converttopsbt()
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
- return EncodeBase64(MakeUCharSpan(ssTx));
+ return EncodeBase64(ssTx);
},
};
}
@@ -1643,7 +1635,7 @@ static RPCHelpMan utxoupdatepsbt()
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
- return EncodeBase64(MakeUCharSpan(ssTx));
+ return EncodeBase64(ssTx);
},
};
}
@@ -1739,7 +1731,7 @@ static RPCHelpMan joinpsbts()
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << shuffled_psbt;
- return EncodeBase64(MakeUCharSpan(ssTx));
+ return EncodeBase64(ssTx);
},
};
}
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index c7472fc5c1..44e58cb75f 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -113,23 +113,6 @@ std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
return ParseHexV(find_value(o, strKey), strKey);
}
-CoinStatsHashType ParseHashType(const UniValue& param, const CoinStatsHashType default_type)
-{
- if (param.isNull()) {
- return default_type;
- } else {
- std::string hash_type_input = param.get_str();
-
- if (hash_type_input == "hash_serialized_2") {
- return CoinStatsHashType::HASH_SERIALIZED;
- } else if (hash_type_input == "none") {
- return CoinStatsHashType::NONE;
- } else {
- throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%d is not a valid hash_type", hash_type_input));
- }
- }
-}
-
std::string HelpExampleCli(const std::string& methodname, const std::string& args)
{
return "> bitcoin-cli " + methodname + " " + args + "\n";
diff --git a/src/rpc/util.h b/src/rpc/util.h
index 90521949a6..94c2d2d626 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -77,8 +77,6 @@ extern uint256 ParseHashO(const UniValue& o, std::string strKey);
extern std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName);
extern std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey);
-CoinStatsHashType ParseHashType(const UniValue& param, const CoinStatsHashType default_type);
-
extern CAmount AmountFromValue(const UniValue& value);
extern std::string HelpExampleCli(const std::string& methodname, const std::string& args);
extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args);