aboutsummaryrefslogtreecommitdiff
path: root/src/rpc
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc')
-rw-r--r--src/rpc/blockchain.cpp95
-rw-r--r--src/rpc/blockchain.h4
-rw-r--r--src/rpc/mempool.cpp8
-rw-r--r--src/rpc/mining.cpp12
-rw-r--r--src/rpc/rawtransaction.cpp4
-rw-r--r--src/rpc/request.cpp21
-rw-r--r--src/rpc/request.h3
-rw-r--r--src/rpc/server.h4
8 files changed, 88 insertions, 63 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index e785678614..9899a13a1e 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -62,9 +62,7 @@ using kernel::CoinStatsHashType;
using node::BlockManager;
using node::NodeContext;
using node::SnapshotMetadata;
-using util::Join;
using util::MakeUnorderedList;
-using util::ToString;
struct CUpdatedBlock
{
@@ -433,6 +431,7 @@ static RPCHelpMan getblockfrompeer()
"getblockfrompeer",
"Attempt to fetch block from a given peer.\n\n"
"We must have the header for this block, e.g. using submitheader.\n"
+ "The block will not have any undo data which can limit the usage of the block data in a context where the undo data is needed.\n"
"Subsequent calls for the same block may cause the response from the previous peer to be ignored.\n"
"Peers generally ignore requests for a stale block that they never fully verified, or one that is more than a month old.\n"
"When a peer does not respond with a block, we will disconnect.\n"
@@ -786,6 +785,32 @@ static RPCHelpMan getblock()
};
}
+//! Return height of highest block that has been pruned, or std::nullopt if no blocks have been pruned
+std::optional<int> GetPruneHeight(const BlockManager& blockman, const CChain& chain) {
+ AssertLockHeld(::cs_main);
+
+ // Search for the last block missing block data or undo data. Don't let the
+ // search consider the genesis block, because the genesis block does not
+ // have undo data, but should not be considered pruned.
+ const CBlockIndex* first_block{chain[1]};
+ const CBlockIndex* chain_tip{chain.Tip()};
+
+ // If there are no blocks after the genesis block, or no blocks at all, nothing is pruned.
+ if (!first_block || !chain_tip) return std::nullopt;
+
+ // If the chain tip is pruned, everything is pruned.
+ if (!((chain_tip->nStatus & BLOCK_HAVE_MASK) == BLOCK_HAVE_MASK)) return chain_tip->nHeight;
+
+ const auto& first_unpruned{*CHECK_NONFATAL(blockman.GetFirstBlock(*chain_tip, /*status_mask=*/BLOCK_HAVE_MASK, first_block))};
+ if (&first_unpruned == first_block) {
+ // All blocks between first_block and chain_tip have data, so nothing is pruned.
+ return std::nullopt;
+ }
+
+ // Block before the first unpruned block is the last pruned block.
+ return CHECK_NONFATAL(first_unpruned.pprev)->nHeight;
+}
+
static RPCHelpMan pruneblockchain()
{
return RPCHelpMan{"pruneblockchain", "",
@@ -838,8 +863,7 @@ static RPCHelpMan pruneblockchain()
}
PruneBlockFilesManual(active_chainstate, height);
- const CBlockIndex& block{*CHECK_NONFATAL(active_chain.Tip())};
- return block.nStatus & BLOCK_HAVE_DATA ? active_chainstate.m_blockman.GetFirstStoredBlock(block)->nHeight - 1 : block.nHeight;
+ return GetPruneHeight(chainman.m_blockman, active_chain).value_or(-1);
},
};
}
@@ -1299,8 +1323,8 @@ RPCHelpMan getblockchaininfo()
obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage());
obj.pushKV("pruned", chainman.m_blockman.IsPruneMode());
if (chainman.m_blockman.IsPruneMode()) {
- bool has_tip_data = tip.nStatus & BLOCK_HAVE_DATA;
- obj.pushKV("pruneheight", has_tip_data ? chainman.m_blockman.GetFirstStoredBlock(tip)->nHeight : tip.nHeight + 1);
+ const auto prune_height{GetPruneHeight(chainman.m_blockman, active_chainstate.m_chain)};
+ obj.pushKV("pruneheight", prune_height ? prune_height.value() + 1 : 0);
const bool automatic_pruning{chainman.m_blockman.GetPruneTarget() != BlockManager::PRUNE_TARGET_MANUAL};
obj.pushKV("automatic_pruning", automatic_pruning);
@@ -1645,13 +1669,19 @@ static RPCHelpMan getchaintxstats()
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::NUM_TIME, "time", "The timestamp for the final block in the window, expressed in " + UNIX_EPOCH_TIME},
- {RPCResult::Type::NUM, "txcount", "The total number of transactions in the chain up to that point"},
+ {RPCResult::Type::NUM, "txcount", /*optional=*/true,
+ "The total number of transactions in the chain up to that point, if known. "
+ "It may be unknown when using assumeutxo."},
{RPCResult::Type::STR_HEX, "window_final_block_hash", "The hash of the final block in the window"},
{RPCResult::Type::NUM, "window_final_block_height", "The height of the final block in the window."},
{RPCResult::Type::NUM, "window_block_count", "Size of the window in number of blocks"},
- {RPCResult::Type::NUM, "window_tx_count", /*optional=*/true, "The number of transactions in the window. Only returned if \"window_block_count\" is > 0"},
{RPCResult::Type::NUM, "window_interval", /*optional=*/true, "The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0"},
- {RPCResult::Type::NUM, "txrate", /*optional=*/true, "The average rate of transactions per second in the window. Only returned if \"window_interval\" is > 0"},
+ {RPCResult::Type::NUM, "window_tx_count", /*optional=*/true,
+ "The number of transactions in the window. "
+ "Only returned if \"window_block_count\" is > 0 and if txcount exists for the start and end of the window."},
+ {RPCResult::Type::NUM, "txrate", /*optional=*/true,
+ "The average rate of transactions per second in the window. "
+ "Only returned if \"window_interval\" is > 0 and if window_tx_count exists."},
}},
RPCExamples{
HelpExampleCli("getchaintxstats", "")
@@ -1692,19 +1722,25 @@ static RPCHelpMan getchaintxstats()
const CBlockIndex& past_block{*CHECK_NONFATAL(pindex->GetAncestor(pindex->nHeight - blockcount))};
const int64_t nTimeDiff{pindex->GetMedianTimePast() - past_block.GetMedianTimePast()};
- const int nTxDiff = pindex->nChainTx - past_block.nChainTx;
+ const auto window_tx_count{
+ (pindex->nChainTx != 0 && past_block.nChainTx != 0) ? std::optional{pindex->nChainTx - past_block.nChainTx} : std::nullopt,
+ };
UniValue ret(UniValue::VOBJ);
ret.pushKV("time", (int64_t)pindex->nTime);
- ret.pushKV("txcount", (int64_t)pindex->nChainTx);
+ if (pindex->nChainTx) {
+ ret.pushKV("txcount", pindex->nChainTx);
+ }
ret.pushKV("window_final_block_hash", pindex->GetBlockHash().GetHex());
ret.pushKV("window_final_block_height", pindex->nHeight);
ret.pushKV("window_block_count", blockcount);
if (blockcount > 0) {
- ret.pushKV("window_tx_count", nTxDiff);
ret.pushKV("window_interval", nTimeDiff);
- if (nTimeDiff > 0) {
- ret.pushKV("txrate", ((double)nTxDiff) / nTimeDiff);
+ if (window_tx_count) {
+ ret.pushKV("window_tx_count", *window_tx_count);
+ if (nTimeDiff > 0) {
+ ret.pushKV("txrate", double(*window_tx_count) / nTimeDiff);
+ }
}
}
@@ -2804,7 +2840,7 @@ static RPCHelpMan loadtxoutset()
{
NodeContext& node = EnsureAnyNodeContext(request.context);
ChainstateManager& chainman = EnsureChainman(node);
- fs::path path{AbsPathForConfigVal(EnsureArgsman(node), fs::u8path(request.params[0].get_str()))};
+ const fs::path path{AbsPathForConfigVal(EnsureArgsman(node), fs::u8path(self.Arg<std::string>("path")))};
FILE* file{fsbridge::fopen(path, "rb")};
AutoFile afile{file};
@@ -2821,34 +2857,15 @@ static RPCHelpMan loadtxoutset()
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("Unable to parse metadata: %s", e.what()));
}
- uint256 base_blockhash = metadata.m_base_blockhash;
- int base_blockheight = metadata.m_base_blockheight;
- if (!chainman.GetParams().AssumeutxoForBlockhash(base_blockhash).has_value()) {
- auto available_heights = chainman.GetParams().GetAvailableSnapshotHeights();
- std::string heights_formatted = Join(available_heights, ", ", [&](const auto& i) { return ToString(i); });
- throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Unable to load UTXO snapshot, "
- "assumeutxo block hash in snapshot metadata not recognized (hash: %s, height: %s). The following snapshot heights are available: %s.",
- base_blockhash.ToString(),
- base_blockheight,
- heights_formatted));
- }
- CBlockIndex* snapshot_start_block = WITH_LOCK(::cs_main,
- return chainman.m_blockman.LookupBlockIndex(base_blockhash));
-
- if (!snapshot_start_block) {
- throw JSONRPCError(
- RPC_INTERNAL_ERROR,
- 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));
+ auto activation_result{chainman.ActivateSnapshot(afile, metadata, false)};
+ if (!activation_result) {
+ throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Unable to load UTXO snapshot: %s. (%s)", util::ErrorString(activation_result).original, path.utf8string()));
}
UniValue result(UniValue::VOBJ);
result.pushKV("coins_loaded", metadata.m_coins_count);
- result.pushKV("tip_hash", snapshot_start_block->GetBlockHash().ToString());
- result.pushKV("base_height", snapshot_start_block->nHeight);
+ result.pushKV("tip_hash", metadata.m_base_blockhash.ToString());
+ result.pushKV("base_height", metadata.m_base_blockheight);
result.pushKV("path", fs::PathToString(path));
return result;
},
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index c2021c3608..f6a7fe236c 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -21,6 +21,7 @@ class CBlockIndex;
class Chainstate;
class UniValue;
namespace node {
+class BlockManager;
struct NodeContext;
} // namespace node
@@ -57,4 +58,7 @@ UniValue CreateUTXOSnapshot(
const fs::path& path,
const fs::path& tmppath);
+//! Return height of highest block that has been pruned, or std::nullopt if no blocks have been pruned
+std::optional<int> GetPruneHeight(const node::BlockManager& blockman, const CChain& chain) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+
#endif // BITCOIN_RPC_BLOCKCHAIN_H
diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp
index fd11f6cfeb..b67d272b65 100644
--- a/src/rpc/mempool.cpp
+++ b/src/rpc/mempool.cpp
@@ -5,7 +5,7 @@
#include <rpc/blockchain.h>
-#include <kernel/mempool_persist.h>
+#include <node/mempool_persist.h>
#include <chainparams.h>
#include <core_io.h>
@@ -27,7 +27,7 @@
#include <utility>
-using kernel::DumpMempool;
+using node::DumpMempool;
using node::DEFAULT_MAX_BURN_AMOUNT;
using node::DEFAULT_MAX_RAW_TX_FEE_RATE;
@@ -759,13 +759,13 @@ static RPCHelpMan importmempool()
const UniValue& use_current_time{request.params[1]["use_current_time"]};
const UniValue& apply_fee_delta{request.params[1]["apply_fee_delta_priority"]};
const UniValue& apply_unbroadcast{request.params[1]["apply_unbroadcast_set"]};
- kernel::ImportMempoolOptions opts{
+ node::ImportMempoolOptions opts{
.use_current_time = use_current_time.isNull() ? true : use_current_time.get_bool(),
.apply_fee_delta_priority = apply_fee_delta.isNull() ? false : apply_fee_delta.get_bool(),
.apply_unbroadcast_set = apply_unbroadcast.isNull() ? false : apply_unbroadcast.get_bool(),
};
- if (!kernel::LoadMempool(mempool, load_path, chainstate, std::move(opts))) {
+ if (!node::LoadMempool(mempool, load_path, chainstate, std::move(opts))) {
throw JSONRPCError(RPC_MISC_ERROR, "Unable to import mempool file, see debug.log for details.");
}
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 2b93c18965..7e420dcd9b 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -371,8 +371,6 @@ static RPCHelpMan generateblock()
ChainstateManager& chainman = EnsureChainman(node);
{
- LOCK(cs_main);
-
std::unique_ptr<CBlockTemplate> blocktemplate{miner.createNewBlock(coinbase_script, /*use_mempool=*/false)};
if (!blocktemplate) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
@@ -387,10 +385,8 @@ static RPCHelpMan generateblock()
RegenerateCommitments(block, chainman);
{
- LOCK(cs_main);
-
BlockValidationState state;
- if (!miner.testBlockValidity(state, block, /*check_merkle_root=*/false)) {
+ if (!miner.testBlockValidity(block, /*check_merkle_root=*/false, state)) {
throw JSONRPCError(RPC_VERIFY_ERROR, strprintf("testBlockValidity failed: %s", state.ToString()));
}
}
@@ -667,9 +663,7 @@ static RPCHelpMan getblocktemplate()
ChainstateManager& chainman = EnsureChainman(node);
Mining& miner = EnsureMining(node);
LOCK(cs_main);
- std::optional<uint256> maybe_tip{miner.getTipHash()};
- CHECK_NONFATAL(maybe_tip);
- uint256 tip{maybe_tip.value()};
+ uint256 tip{CHECK_NONFATAL(miner.getTipHash()).value()};
std::string strMode = "template";
UniValue lpval = NullUniValue;
@@ -713,7 +707,7 @@ static RPCHelpMan getblocktemplate()
return "inconclusive-not-best-prevblk";
}
BlockValidationState state;
- miner.testBlockValidity(state, block);
+ miner.testBlockValidity(block, /*check_merkle_root=*/true, state);
return BIP22ValidationResult(state);
}
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 75b538061d..ed9ef2c159 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -1790,8 +1790,8 @@ static RPCHelpMan joinpsbts()
std::iota(output_indices.begin(), output_indices.end(), 0);
// Shuffle input and output indices lists
- Shuffle(input_indices.begin(), input_indices.end(), FastRandomContext());
- Shuffle(output_indices.begin(), output_indices.end(), FastRandomContext());
+ std::shuffle(input_indices.begin(), input_indices.end(), FastRandomContext());
+ std::shuffle(output_indices.begin(), output_indices.end(), FastRandomContext());
PartiallySignedTransaction shuffled_psbt;
shuffled_psbt.tx = CMutableTransaction();
diff --git a/src/rpc/request.cpp b/src/rpc/request.cpp
index 87b9f18b33..083d1be44f 100644
--- a/src/rpc/request.cpp
+++ b/src/rpc/request.cpp
@@ -5,12 +5,11 @@
#include <rpc/request.h>
-#include <util/fs.h>
-
#include <common/args.h>
#include <logging.h>
#include <random.h>
#include <rpc/protocol.h>
+#include <util/fs.h>
#include <util/fs_helpers.h>
#include <util/strencodings.h>
@@ -95,7 +94,7 @@ static fs::path GetAuthCookieFile(bool temp=false)
static bool g_generated_cookie = false;
-bool GenerateAuthCookie(std::string *cookie_out)
+bool GenerateAuthCookie(std::string* cookie_out, std::optional<fs::perms> cookie_perms)
{
const size_t COOKIE_SIZE = 32;
unsigned char rand_pwd[COOKIE_SIZE];
@@ -109,7 +108,7 @@ bool GenerateAuthCookie(std::string *cookie_out)
fs::path filepath_tmp = GetAuthCookieFile(true);
file.open(filepath_tmp);
if (!file.is_open()) {
- LogPrintf("Unable to open cookie authentication file %s for writing\n", fs::PathToString(filepath_tmp));
+ LogInfo("Unable to open cookie authentication file %s for writing\n", fs::PathToString(filepath_tmp));
return false;
}
file << cookie;
@@ -117,11 +116,21 @@ bool GenerateAuthCookie(std::string *cookie_out)
fs::path filepath = GetAuthCookieFile(false);
if (!RenameOver(filepath_tmp, filepath)) {
- LogPrintf("Unable to rename cookie authentication file %s to %s\n", fs::PathToString(filepath_tmp), fs::PathToString(filepath));
+ LogInfo("Unable to rename cookie authentication file %s to %s\n", fs::PathToString(filepath_tmp), fs::PathToString(filepath));
return false;
}
+ if (cookie_perms) {
+ std::error_code code;
+ fs::permissions(filepath, cookie_perms.value(), fs::perm_options::replace, code);
+ if (code) {
+ LogInfo("Unable to set permissions on cookie authentication file %s\n", fs::PathToString(filepath_tmp));
+ return false;
+ }
+ }
+
g_generated_cookie = true;
- LogPrintf("Generated RPC authentication cookie %s\n", fs::PathToString(filepath));
+ LogInfo("Generated RPC authentication cookie %s\n", fs::PathToString(filepath));
+ LogInfo("Permissions used for cookie: %s\n", PermsToSymbolicString(fs::status(filepath).permissions()));
if (cookie_out)
*cookie_out = cookie;
diff --git a/src/rpc/request.h b/src/rpc/request.h
index 9968426636..24887e8691 100644
--- a/src/rpc/request.h
+++ b/src/rpc/request.h
@@ -11,6 +11,7 @@
#include <string>
#include <univalue.h>
+#include <util/fs.h>
enum class JSONRPCVersion {
V1_LEGACY,
@@ -23,7 +24,7 @@ UniValue JSONRPCReplyObj(UniValue result, UniValue error, std::optional<UniValue
UniValue JSONRPCError(int code, const std::string& message);
/** Generate a new RPC authentication cookie and write it to disk */
-bool GenerateAuthCookie(std::string *cookie_out);
+bool GenerateAuthCookie(std::string* cookie_out, std::optional<fs::perms> cookie_perms=std::nullopt);
/** Read the RPC authentication cookie from disk */
bool GetAuthCookie(std::string *cookie_out);
/** Delete RPC authentication cookie from disk */
diff --git a/src/rpc/server.h b/src/rpc/server.h
index 5735aff821..56e8a63088 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -48,7 +48,7 @@ bool RPCIsInWarmup(std::string *outStatus);
class RPCTimerBase
{
public:
- virtual ~RPCTimerBase() {}
+ virtual ~RPCTimerBase() = default;
};
/**
@@ -57,7 +57,7 @@ public:
class RPCTimerInterface
{
public:
- virtual ~RPCTimerInterface() {}
+ virtual ~RPCTimerInterface() = default;
/** Implementation name */
virtual const char *Name() = 0;
/** Factory function for timers.