aboutsummaryrefslogtreecommitdiff
path: root/src/rpc/blockchain.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc/blockchain.cpp')
-rw-r--r--src/rpc/blockchain.cpp206
1 files changed, 151 insertions, 55 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index a51bc7d0be..7cbe7e6159 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -185,7 +185,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
case TxVerbosity::SHOW_DETAILS:
case TxVerbosity::SHOW_DETAILS_AND_PREVOUT:
CBlockUndo blockUndo;
- const bool have_undo = !IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex);
+ const bool have_undo{WITH_LOCK(::cs_main, return !IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex))};
for (size_t i = 0; i < block.vtx.size(); ++i) {
const CTransactionRef& tx = block.vtx.at(i);
@@ -790,10 +790,10 @@ static RPCHelpMan getblockfrompeer()
{
return RPCHelpMan{
"getblockfrompeer",
- "Attempt to fetch block from a given peer.\n"
- "\nWe must have the header for this block, e.g. using submitheader.\n"
- "Subsequent calls for the same block and a new peer will cause the response from the previous peer to be ignored.\n"
- "\nReturns an empty JSON object if the request was successfully scheduled.",
+ "Attempt to fetch block from a given peer.\n\n"
+ "We must have the header for this block, e.g. using submitheader.\n"
+ "Subsequent calls for the same block and a new peer will cause the response from the previous peer to be ignored.\n\n"
+ "Returns an empty JSON object if the request was successfully scheduled.",
{
{"block_hash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash to try to fetch"},
{"peer_id", RPCArg::Type::NUM, RPCArg::Optional::NO, "The peer to fetch it from (see getpeerinfo for peer IDs)"},
@@ -818,7 +818,8 @@ static RPCHelpMan getblockfrompeer()
throw JSONRPCError(RPC_MISC_ERROR, "Block header missing");
}
- if (index->nStatus & BLOCK_HAVE_DATA) {
+ const bool block_has_data = WITH_LOCK(::cs_main, return index->nStatus & BLOCK_HAVE_DATA);
+ if (block_has_data) {
throw JSONRPCError(RPC_MISC_ERROR, "Block already downloaded");
}
@@ -929,8 +930,9 @@ static RPCHelpMan getblockheader()
};
}
-static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
+static CBlock GetBlockChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
+ AssertLockHeld(::cs_main);
CBlock block;
if (IsBlockPruned(pblockindex)) {
throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)");
@@ -946,8 +948,9 @@ static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
return block;
}
-static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex)
+static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
+ AssertLockHeld(::cs_main);
CBlockUndo blockUndo;
if (IsBlockPruned(pblockindex)) {
throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)");
@@ -1331,6 +1334,7 @@ static RPCHelpMan gettxout()
{RPCResult::Type::STR_AMOUNT, "value", "The transaction value in " + CURRENCY_UNIT},
{RPCResult::Type::OBJ, "scriptPubKey", "", {
{RPCResult::Type::STR, "asm", ""},
+ {RPCResult::Type::STR, "desc", "Inferred descriptor for the output"},
{RPCResult::Type::STR_HEX, "hex", ""},
{RPCResult::Type::STR, "type", "The type, eg pubkeyhash"},
{RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"},
@@ -1434,7 +1438,7 @@ static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue&
UniValue rv(UniValue::VOBJ);
rv.pushKV("type", "buried");
- // getblockchaininfo reports the softfork as active from when the chain height is
+ // getdeploymentinfo reports the softfork as active from when the chain height is
// one below the activation height
rv.pushKV("active", DeploymentActiveAfter(active_chain_tip, params, dep));
rv.pushKV("height", params.DeploymentHeight(dep));
@@ -1446,51 +1450,82 @@ static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue&
// For BIP9 deployments.
if (!DeploymentEnabled(consensusParams, id)) return;
+ if (active_chain_tip == nullptr) return;
+
+ auto get_state_name = [](const ThresholdState state) -> std::string {
+ switch (state) {
+ case ThresholdState::DEFINED: return "defined";
+ case ThresholdState::STARTED: return "started";
+ case ThresholdState::LOCKED_IN: return "locked_in";
+ case ThresholdState::ACTIVE: return "active";
+ case ThresholdState::FAILED: return "failed";
+ }
+ return "invalid";
+ };
UniValue bip9(UniValue::VOBJ);
- const ThresholdState thresholdState = g_versionbitscache.State(active_chain_tip, consensusParams, id);
- switch (thresholdState) {
- case ThresholdState::DEFINED: bip9.pushKV("status", "defined"); break;
- case ThresholdState::STARTED: bip9.pushKV("status", "started"); break;
- case ThresholdState::LOCKED_IN: bip9.pushKV("status", "locked_in"); break;
- case ThresholdState::ACTIVE: bip9.pushKV("status", "active"); break;
- case ThresholdState::FAILED: bip9.pushKV("status", "failed"); break;
- }
- const bool has_signal = (ThresholdState::STARTED == thresholdState || ThresholdState::LOCKED_IN == thresholdState);
+
+ const ThresholdState next_state = g_versionbitscache.State(active_chain_tip, consensusParams, id);
+ const ThresholdState current_state = g_versionbitscache.State(active_chain_tip->pprev, consensusParams, id);
+
+ const bool has_signal = (ThresholdState::STARTED == current_state || ThresholdState::LOCKED_IN == current_state);
+
+ // BIP9 parameters
if (has_signal) {
bip9.pushKV("bit", consensusParams.vDeployments[id].bit);
}
bip9.pushKV("start_time", consensusParams.vDeployments[id].nStartTime);
bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
- int64_t since_height = g_versionbitscache.StateSinceHeight(active_chain_tip, consensusParams, id);
- bip9.pushKV("since", since_height);
+ bip9.pushKV("min_activation_height", consensusParams.vDeployments[id].min_activation_height);
+
+ // BIP9 status
+ bip9.pushKV("status", get_state_name(current_state));
+ bip9.pushKV("since", g_versionbitscache.StateSinceHeight(active_chain_tip->pprev, consensusParams, id));
+ bip9.pushKV("status-next", get_state_name(next_state));
+
+ // BIP9 signalling status, if applicable
if (has_signal) {
UniValue statsUV(UniValue::VOBJ);
- BIP9Stats statsStruct = g_versionbitscache.Statistics(active_chain_tip, consensusParams, id);
+ std::vector<bool> signals;
+ BIP9Stats statsStruct = g_versionbitscache.Statistics(active_chain_tip, consensusParams, id, &signals);
statsUV.pushKV("period", statsStruct.period);
statsUV.pushKV("elapsed", statsStruct.elapsed);
statsUV.pushKV("count", statsStruct.count);
- if (ThresholdState::LOCKED_IN != thresholdState) {
+ if (ThresholdState::LOCKED_IN != current_state) {
statsUV.pushKV("threshold", statsStruct.threshold);
statsUV.pushKV("possible", statsStruct.possible);
}
bip9.pushKV("statistics", statsUV);
+
+ std::string sig;
+ sig.reserve(signals.size());
+ for (const bool s : signals) {
+ sig.push_back(s ? '#' : '-');
+ }
+ bip9.pushKV("signalling", sig);
}
- bip9.pushKV("min_activation_height", consensusParams.vDeployments[id].min_activation_height);
UniValue rv(UniValue::VOBJ);
rv.pushKV("type", "bip9");
- rv.pushKV("bip9", bip9);
- if (ThresholdState::ACTIVE == thresholdState) {
- rv.pushKV("height", since_height);
+ if (ThresholdState::ACTIVE == next_state) {
+ rv.pushKV("height", g_versionbitscache.StateSinceHeight(active_chain_tip, consensusParams, id));
}
- rv.pushKV("active", ThresholdState::ACTIVE == thresholdState);
+ rv.pushKV("active", ThresholdState::ACTIVE == next_state);
+ rv.pushKV("bip9", bip9);
softforks.pushKV(DeploymentName(id), rv);
}
+namespace {
+/* TODO: when -dprecatedrpc=softforks is removed, drop these */
+UniValue DeploymentInfo(const CBlockIndex* tip, const Consensus::Params& consensusParams);
+extern const std::vector<RPCResult> RPCHelpForDeployment;
+}
+
+// used by rest.cpp:rest_chaininfo, so cannot be static
RPCHelpMan getblockchaininfo()
{
+ /* TODO: from v24, remove -deprecatedrpc=softforks */
return RPCHelpMan{"getblockchaininfo",
"Returns an object containing various state info regarding blockchain processing.\n",
{},
@@ -1512,31 +1547,11 @@ RPCHelpMan getblockchaininfo()
{RPCResult::Type::NUM, "pruneheight", /*optional=*/true, "lowest-height complete block stored (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::OBJ_DYN, "softforks", "status of softforks",
+ {RPCResult::Type::OBJ_DYN, "softforks", "(DEPRECATED, returned only if config option -deprecatedrpc=softforks is passed) status of softforks",
{
{RPCResult::Type::OBJ, "xxxx", "name of the softfork",
- {
- {RPCResult::Type::STR, "type", "one of \"buried\", \"bip9\""},
- {RPCResult::Type::OBJ, "bip9", /*optional=*/true, "status of bip9 softforks (only for \"bip9\" type)",
- {
- {RPCResult::Type::STR, "status", "one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\""},
- {RPCResult::Type::NUM, "bit", /*optional=*/true, "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" and \"locked_in\" status)"},
- {RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"},
- {RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"},
- {RPCResult::Type::NUM, "since", "height of the first block to which the status applies"},
- {RPCResult::Type::NUM, "min_activation_height", "minimum height of blocks for which the rules may be enforced"},
- {RPCResult::Type::OBJ, "statistics", /*optional=*/true, "numeric statistics about signalling for a softfork (only for \"started\" and \"locked_in\" status)",
- {
- {RPCResult::Type::NUM, "period", "the length in blocks of the signalling period"},
- {RPCResult::Type::NUM, "threshold", /*optional=*/true, "the number of blocks with the version bit set required to activate the feature (only for \"started\" status)"},
- {RPCResult::Type::NUM, "elapsed", "the number of blocks elapsed since the beginning of the current period"},
- {RPCResult::Type::NUM, "count", "the number of blocks with the version bit set in the current period"},
- {RPCResult::Type::BOOL, "possible", /*optional=*/true, "returns false if there are not enough blocks left in this period to pass activation threshold (only for \"started\" status)"},
- }},
- }},
- {RPCResult::Type::NUM, "height", /*optional=*/true, "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"},
- {RPCResult::Type::BOOL, "active", "true if the rules are enforced for the mempool and the next block"},
- }},
+ RPCHelpForDeployment
+ },
}},
{RPCResult::Type::STR, "warnings", "any network and blockchain warnings"},
}},
@@ -1584,7 +1599,45 @@ RPCHelpMan getblockchaininfo()
}
}
- const Consensus::Params& consensusParams = Params().GetConsensus();
+ if (IsDeprecatedRPCEnabled("softforks")) {
+ const Consensus::Params& consensusParams = Params().GetConsensus();
+ obj.pushKV("softforks", DeploymentInfo(tip, consensusParams));
+ }
+
+ obj.pushKV("warnings", GetWarnings(false).original);
+ return obj;
+},
+ };
+}
+
+namespace {
+const std::vector<RPCResult> RPCHelpForDeployment{
+ {RPCResult::Type::STR, "type", "one of \"buried\", \"bip9\""},
+ {RPCResult::Type::NUM, "height", /*optional=*/true, "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"},
+ {RPCResult::Type::BOOL, "active", "true if the rules are enforced for the mempool and the next block"},
+ {RPCResult::Type::OBJ, "bip9", /*optional=*/true, "status of bip9 softforks (only for \"bip9\" type)",
+ {
+ {RPCResult::Type::NUM, "bit", /*optional=*/true, "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" and \"locked_in\" status)"},
+ {RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"},
+ {RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"},
+ {RPCResult::Type::NUM, "min_activation_height", "minimum height of blocks for which the rules may be enforced"},
+ {RPCResult::Type::STR, "status", "bip9 status of specified block (one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\")"},
+ {RPCResult::Type::NUM, "since", "height of the first block to which the status applies"},
+ {RPCResult::Type::STR, "status-next", "bip9 status of next block"},
+ {RPCResult::Type::OBJ, "statistics", /*optional=*/true, "numeric statistics about signalling for a softfork (only for \"started\" and \"locked_in\" status)",
+ {
+ {RPCResult::Type::NUM, "period", "the length in blocks of the signalling period"},
+ {RPCResult::Type::NUM, "threshold", /*optional=*/true, "the number of blocks with the version bit set required to activate the feature (only for \"started\" status)"},
+ {RPCResult::Type::NUM, "elapsed", "the number of blocks elapsed since the beginning of the current period"},
+ {RPCResult::Type::NUM, "count", "the number of blocks with the version bit set in the current period"},
+ {RPCResult::Type::BOOL, "possible", /*optional=*/true, "returns false if there are not enough blocks left in this period to pass activation threshold (only for \"started\" status)"},
+ }},
+ {RPCResult::Type::STR, "signalling", "indicates blocks that signalled with a # and blocks that did not with a -"},
+ }},
+};
+
+UniValue DeploymentInfo(const CBlockIndex* tip, const Consensus::Params& consensusParams)
+{
UniValue softforks(UniValue::VOBJ);
SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_HEIGHTINCB);
SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_DERSIG);
@@ -1593,11 +1646,53 @@ RPCHelpMan getblockchaininfo()
SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_SEGWIT);
SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_TESTDUMMY);
SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_TAPROOT);
- obj.pushKV("softforks", softforks);
+ return softforks;
+}
+} // anon namespace
- obj.pushKV("warnings", GetWarnings(false).original);
- return obj;
-},
+static RPCHelpMan getdeploymentinfo()
+{
+ return RPCHelpMan{"getdeploymentinfo",
+ "Returns an object containing various state info regarding soft-forks.",
+ {
+ {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Default{"chain tip"}, "The block hash at which to query fork state"},
+ },
+ RPCResult{
+ RPCResult::Type::OBJ, "", "", {
+ {RPCResult::Type::STR, "hash", "requested block hash (or tip)"},
+ {RPCResult::Type::NUM, "height", "requested block height (or tip)"},
+ {RPCResult::Type::OBJ, "deployments", "", {
+ {RPCResult::Type::OBJ, "xxxx", "name of the deployment", RPCHelpForDeployment}
+ }},
+ }
+ },
+ RPCExamples{ HelpExampleCli("getdeploymentinfo", "") + HelpExampleRpc("getdeploymentinfo", "") },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+ {
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
+ LOCK(cs_main);
+ CChainState& active_chainstate = chainman.ActiveChainstate();
+
+ const CBlockIndex* tip;
+ if (request.params[0].isNull()) {
+ tip = active_chainstate.m_chain.Tip();
+ CHECK_NONFATAL(tip);
+ } else {
+ uint256 hash(ParseHashV(request.params[0], "blockhash"));
+ tip = chainman.m_blockman.LookupBlockIndex(hash);
+ if (!tip) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
+ }
+ }
+
+ const Consensus::Params& consensusParams = Params().GetConsensus();
+
+ UniValue deploymentinfo(UniValue::VOBJ);
+ deploymentinfo.pushKV("hash", tip->GetBlockHash().ToString());
+ deploymentinfo.pushKV("height", tip->nHeight);
+ deploymentinfo.pushKV("deployments", DeploymentInfo(tip, consensusParams));
+ return deploymentinfo;
+ },
};
}
@@ -2747,6 +2842,7 @@ static const CRPCCommand commands[] =
{ "blockchain", &getblockheader, },
{ "blockchain", &getchaintips, },
{ "blockchain", &getdifficulty, },
+ { "blockchain", &getdeploymentinfo, },
{ "blockchain", &getmempoolancestors, },
{ "blockchain", &getmempooldescendants, },
{ "blockchain", &getmempoolentry, },