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.cpp98
1 files changed, 73 insertions, 25 deletions
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index eed004806a..3e3e91927c 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -34,6 +34,7 @@
#include <rpc/server_util.h>
#include <rpc/util.h>
#include <script/descriptor.h>
+#include <serialize.h>
#include <streams.h>
#include <sync.h>
#include <txdb.h>
@@ -47,7 +48,6 @@
#include <validation.h>
#include <validationinterface.h>
#include <versionbits.h>
-#include <warnings.h>
#include <stdint.h>
@@ -189,12 +189,12 @@ UniValue blockToJSON(BlockManager& blockman, const CBlock& block, const CBlockIn
const CTxUndo* txundo = (have_undo && i > 0) ? &blockUndo.vtxundo.at(i - 1) : nullptr;
UniValue objTx(UniValue::VOBJ);
TxToUniv(*tx, /*block_hash=*/uint256(), /*entry=*/objTx, /*include_hex=*/true, txundo, verbosity);
- txs.push_back(objTx);
+ txs.push_back(std::move(objTx));
}
break;
}
- result.pushKV("tx", txs);
+ result.pushKV("tx", std::move(txs));
return result;
}
@@ -1026,9 +1026,9 @@ static RPCHelpMan gettxoutsetinfo()
unspendables.pushKV("bip30", ValueFromAmount(stats.total_unspendables_bip30 - prev_stats.total_unspendables_bip30));
unspendables.pushKV("scripts", ValueFromAmount(stats.total_unspendables_scripts - prev_stats.total_unspendables_scripts));
unspendables.pushKV("unclaimed_rewards", ValueFromAmount(stats.total_unspendables_unclaimed_rewards - prev_stats.total_unspendables_unclaimed_rewards));
- block_info.pushKV("unspendables", unspendables);
+ block_info.pushKV("unspendables", std::move(unspendables));
- ret.pushKV("block_info", block_info);
+ ret.pushKV("block_info", std::move(block_info));
}
} else {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
@@ -1112,7 +1112,7 @@ static RPCHelpMan gettxout()
ret.pushKV("value", ValueFromAmount(coin.out.nValue));
UniValue o(UniValue::VOBJ);
ScriptToUniv(coin.out.scriptPubKey, /*out=*/o, /*include_hex=*/true, /*include_address=*/true);
- ret.pushKV("scriptPubKey", o);
+ ret.pushKV("scriptPubKey", std::move(o));
ret.pushKV("coinbase", (bool)coin.fCoinBase);
return ret;
@@ -1162,7 +1162,7 @@ static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softfo
// one below the activation height
rv.pushKV("active", DeploymentActiveAfter(blockindex, chainman, dep));
rv.pushKV("height", chainman.GetConsensus().DeploymentHeight(dep));
- softforks.pushKV(DeploymentName(dep), rv);
+ softforks.pushKV(DeploymentName(dep), std::move(rv));
}
static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softforks, const ChainstateManager& chainman, Consensus::DeploymentPos id)
@@ -1215,7 +1215,7 @@ static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softfo
statsUV.pushKV("threshold", statsStruct.threshold);
statsUV.pushKV("possible", statsStruct.possible);
}
- bip9.pushKV("statistics", statsUV);
+ bip9.pushKV("statistics", std::move(statsUV));
std::string sig;
sig.reserve(signals.size());
@@ -1231,9 +1231,9 @@ static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softfo
rv.pushKV("height", chainman.m_versionbitscache.StateSinceHeight(blockindex, chainman.GetConsensus(), id));
}
rv.pushKV("active", ThresholdState::ACTIVE == next_state);
- rv.pushKV("bip9", bip9);
+ rv.pushKV("bip9", std::move(bip9));
- softforks.pushKV(DeploymentName(id), rv);
+ softforks.pushKV(DeploymentName(id), std::move(rv));
}
// used by rest.cpp:rest_chaininfo, so cannot be static
@@ -1260,7 +1260,14 @@ RPCHelpMan getblockchaininfo()
{RPCResult::Type::NUM, "pruneheight", /*optional=*/true, "height of the last block pruned, plus one (only present if pruning is enabled)"},
{RPCResult::Type::BOOL, "automatic_pruning", /*optional=*/true, "whether automatic pruning is enabled (only present if pruning is enabled)"},
{RPCResult::Type::NUM, "prune_target_size", /*optional=*/true, "the target size used by pruning (only present if automatic pruning is enabled)"},
- {RPCResult::Type::STR, "warnings", "any network and blockchain warnings"},
+ (IsDeprecatedRPCEnabled("warnings") ?
+ RPCResult{RPCResult::Type::STR, "warnings", "any network and blockchain warnings (DEPRECATED)"} :
+ RPCResult{RPCResult::Type::ARR, "warnings", "any network and blockchain warnings (run with `-deprecatedrpc=warnings` to return the latest warning as a single string)",
+ {
+ {RPCResult::Type::STR, "", "warning"},
+ }
+ }
+ ),
}},
RPCExamples{
HelpExampleCli("getblockchaininfo", "")
@@ -1298,7 +1305,7 @@ RPCHelpMan getblockchaininfo()
}
}
- obj.pushKV("warnings", GetWarnings(false).original);
+ obj.pushKV("warnings", GetNodeWarnings(IsDeprecatedRPCEnabled("warnings")));
return obj;
},
};
@@ -1492,7 +1499,7 @@ static RPCHelpMan getchaintips()
}
obj.pushKV("status", status);
- res.push_back(obj);
+ res.push_back(std::move(obj));
}
return res;
@@ -1972,7 +1979,7 @@ static RPCHelpMan getblockstats()
ret_all.pushKV("avgfeerate", total_weight ? (totalfee * WITNESS_SCALE_FACTOR) / total_weight : 0); // Unit: sat/vbyte
ret_all.pushKV("avgtxsize", (block.vtx.size() > 1) ? total_size / (block.vtx.size() - 1) : 0);
ret_all.pushKV("blockhash", pindex.GetBlockHash().GetHex());
- ret_all.pushKV("feerate_percentiles", feerates_res);
+ ret_all.pushKV("feerate_percentiles", std::move(feerates_res));
ret_all.pushKV("height", (int64_t)pindex.nHeight);
ret_all.pushKV("ins", inputs);
ret_all.pushKV("maxfee", maxfee);
@@ -2256,9 +2263,9 @@ static RPCHelpMan scantxoutset()
unspent.pushKV("coinbase", coin.IsCoinBase());
unspent.pushKV("height", (int32_t)coin.nHeight);
- unspents.push_back(unspent);
+ unspents.push_back(std::move(unspent));
}
- result.pushKV("unspents", unspents);
+ result.pushKV("unspents", std::move(unspents));
result.pushKV("total_amount", ValueFromAmount(total_in));
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid action '%s'", action));
@@ -2498,7 +2505,7 @@ static RPCHelpMan scanblocks()
ret.pushKV("from_height", start_block_height);
ret.pushKV("to_height", start_index->nHeight); // start_index is always the last scanned block here
- ret.pushKV("relevant_blocks", blocks);
+ ret.pushKV("relevant_blocks", std::move(blocks));
ret.pushKV("completed", completed);
}
else {
@@ -2690,29 +2697,60 @@ UniValue CreateUTXOSnapshot(
tip->nHeight, tip->GetBlockHash().ToString(),
fs::PathToString(path), fs::PathToString(temppath)));
- SnapshotMetadata metadata{tip->GetBlockHash(), maybe_stats->coins_count};
+ SnapshotMetadata metadata{chainstate.m_chainman.GetParams().MessageStart(), tip->GetBlockHash(), tip->nHeight, maybe_stats->coins_count};
afile << metadata;
COutPoint key;
+ Txid last_hash;
Coin coin;
unsigned int iter{0};
+ size_t written_coins_count{0};
+ std::vector<std::pair<uint32_t, Coin>> coins;
+
+ // To reduce space the serialization format of the snapshot avoids
+ // duplication of tx hashes. The code takes advantage of the guarantee by
+ // leveldb that keys are lexicographically sorted.
+ // In the coins vector we collect all coins that belong to a certain tx hash
+ // (key.hash) and when we have them all (key.hash != last_hash) we write
+ // them to file using the below lambda function.
+ // See also https://github.com/bitcoin/bitcoin/issues/25675
+ auto write_coins_to_file = [&](AutoFile& afile, const Txid& last_hash, const std::vector<std::pair<uint32_t, Coin>>& coins, size_t& written_coins_count) {
+ afile << last_hash;
+ WriteCompactSize(afile, coins.size());
+ for (const auto& [n, coin] : coins) {
+ WriteCompactSize(afile, n);
+ afile << coin;
+ ++written_coins_count;
+ }
+ };
+ pcursor->GetKey(key);
+ last_hash = key.hash;
while (pcursor->Valid()) {
if (iter % 5000 == 0) node.rpc_interruption_point();
++iter;
if (pcursor->GetKey(key) && pcursor->GetValue(coin)) {
- afile << key;
- afile << coin;
+ if (key.hash != last_hash) {
+ write_coins_to_file(afile, last_hash, coins, written_coins_count);
+ last_hash = key.hash;
+ coins.clear();
+ }
+ coins.emplace_back(key.n, coin);
}
-
pcursor->Next();
}
+ if (!coins.empty()) {
+ write_coins_to_file(afile, last_hash, coins, written_coins_count);
+ }
+
+ CHECK_NONFATAL(written_coins_count == maybe_stats->coins_count);
+
afile.fclose();
UniValue result(UniValue::VOBJ);
- result.pushKV("coins_written", maybe_stats->coins_count);
+ result.pushKV("coins_written", written_coins_count);
result.pushKV("base_hash", tip->GetBlockHash().ToString());
result.pushKV("base_height", tip->nHeight);
result.pushKV("path", path.utf8string());
@@ -2771,13 +2809,23 @@ static RPCHelpMan loadtxoutset()
"Couldn't open file " + path.utf8string() + " for reading.");
}
- SnapshotMetadata metadata;
- afile >> metadata;
+ SnapshotMetadata metadata{chainman.GetParams().MessageStart()};
+ try {
+ afile >> metadata;
+ } catch (const std::ios_base::failure& e) {
+ 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 (%s)", base_blockhash.ToString()));
+ "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));