mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-09-06 18:01:09 +02:00
rpc: add loadtxoutset
Co-authored-by: Sebastian Falbesoner <sebastian.falbesoner@gmail.com>
This commit is contained in:
committed by
James O'Beirne
parent
62ac519e71
commit
ce585a9a15
@@ -3,7 +3,7 @@
|
|||||||
Assumeutxo is a feature that allows fast bootstrapping of a validating bitcoind
|
Assumeutxo is a feature that allows fast bootstrapping of a validating bitcoind
|
||||||
instance with a very similar security model to assumevalid.
|
instance with a very similar security model to assumevalid.
|
||||||
|
|
||||||
The RPC commands `dumptxoutset` and `loadtxoutset` (yet to be merged) are used to
|
The RPC commands `dumptxoutset` and `loadtxoutset` are used to
|
||||||
respectively generate and load UTXO snapshots. The utility script
|
respectively generate and load UTXO snapshots. The utility script
|
||||||
`./contrib/devtools/utxo_snapshot.sh` may be of use.
|
`./contrib/devtools/utxo_snapshot.sh` may be of use.
|
||||||
|
|
||||||
|
@@ -5,3 +5,22 @@ When using assumeutxo with `-prune`, the prune budget may be exceeded if it is s
|
|||||||
lower than 1100MB (i.e. `MIN_DISK_SPACE_FOR_BLOCK_FILES * 2`). Prune budget is normally
|
lower than 1100MB (i.e. `MIN_DISK_SPACE_FOR_BLOCK_FILES * 2`). Prune budget is normally
|
||||||
split evenly across each chainstate, unless the resulting prune budget per chainstate
|
split evenly across each chainstate, unless the resulting prune budget per chainstate
|
||||||
is beneath `MIN_DISK_SPACE_FOR_BLOCK_FILES` in which case that value will be used.
|
is beneath `MIN_DISK_SPACE_FOR_BLOCK_FILES` in which case that value will be used.
|
||||||
|
|
||||||
|
RPC
|
||||||
|
---
|
||||||
|
|
||||||
|
`loadtxoutset` has been added, which allows loading a UTXO snapshot of the format
|
||||||
|
generated by `dumptxoutset`. Once this snapshot is loaded, its contents will be
|
||||||
|
deserialized into a second chainstate data structure, which is then used to sync to
|
||||||
|
the network's tip under a security model very much like `assumevalid`.
|
||||||
|
|
||||||
|
Meanwhile, the original chainstate will complete the initial block download process in
|
||||||
|
the background, eventually validating up to the block that the snapshot is based upon.
|
||||||
|
|
||||||
|
The result is a usable bitcoind instance that is current with the network tip in a
|
||||||
|
matter of minutes rather than hours. UTXO snapshot are typically obtained via
|
||||||
|
third-party sources (HTTP, torrent, etc.) which is reasonable since their contents
|
||||||
|
are always checked by hash.
|
||||||
|
|
||||||
|
You can find more information on this process in the `assumeutxo` design
|
||||||
|
document (<https://github.com/bitcoin/bitcoin/blob/master/doc/design/assumeutxo.md>).
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
#include <blockfilter.h>
|
#include <blockfilter.h>
|
||||||
#include <chain.h>
|
#include <chain.h>
|
||||||
#include <chainparams.h>
|
#include <chainparams.h>
|
||||||
|
#include <clientversion.h>
|
||||||
#include <coins.h>
|
#include <coins.h>
|
||||||
#include <common/args.h>
|
#include <common/args.h>
|
||||||
#include <consensus/amount.h>
|
#include <consensus/amount.h>
|
||||||
@@ -2699,6 +2700,105 @@ UniValue CreateUTXOSnapshot(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static RPCHelpMan loadtxoutset()
|
||||||
|
{
|
||||||
|
return RPCHelpMan{
|
||||||
|
"loadtxoutset",
|
||||||
|
"Load the serialized UTXO set from disk.\n"
|
||||||
|
"Once this snapshot is loaded, its contents will be "
|
||||||
|
"deserialized into a second chainstate data structure, which is then used to sync to "
|
||||||
|
"the network's tip under a security model very much like `assumevalid`. "
|
||||||
|
"Meanwhile, the original chainstate will complete the initial block download process in "
|
||||||
|
"the background, eventually validating up to the block that the snapshot is based upon.\n\n"
|
||||||
|
|
||||||
|
"The result is a usable bitcoind instance that is current with the network tip in a "
|
||||||
|
"matter of minutes rather than hours. UTXO snapshot are typically obtained from "
|
||||||
|
"third-party sources (HTTP, torrent, etc.) which is reasonable since their "
|
||||||
|
"contents are always checked by hash.\n\n"
|
||||||
|
|
||||||
|
"You can find more information on this process in the `assumeutxo` design "
|
||||||
|
"document (<https://github.com/bitcoin/bitcoin/blob/master/doc/design/assumeutxo.md>).",
|
||||||
|
{
|
||||||
|
{"path",
|
||||||
|
RPCArg::Type::STR,
|
||||||
|
RPCArg::Optional::NO,
|
||||||
|
"path to the snapshot file. If relative, will be prefixed by datadir."},
|
||||||
|
},
|
||||||
|
RPCResult{
|
||||||
|
RPCResult::Type::OBJ, "", "",
|
||||||
|
{
|
||||||
|
{RPCResult::Type::NUM, "coins_loaded", "the number of coins loaded from the snapshot"},
|
||||||
|
{RPCResult::Type::STR_HEX, "tip_hash", "the hash of the base of the snapshot"},
|
||||||
|
{RPCResult::Type::NUM, "base_height", "the height of the base of the snapshot"},
|
||||||
|
{RPCResult::Type::STR, "path", "the absolute path that the snapshot was loaded from"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RPCExamples{
|
||||||
|
HelpExampleCli("loadtxoutset", "utxo.dat")
|
||||||
|
},
|
||||||
|
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||||
|
{
|
||||||
|
NodeContext& node = EnsureAnyNodeContext(request.context);
|
||||||
|
fs::path path{AbsPathForConfigVal(EnsureArgsman(node), fs::u8path(request.params[0].get_str()))};
|
||||||
|
|
||||||
|
FILE* file{fsbridge::fopen(path, "rb")};
|
||||||
|
AutoFile afile{file};
|
||||||
|
if (afile.IsNull()) {
|
||||||
|
throw JSONRPCError(
|
||||||
|
RPC_INVALID_PARAMETER,
|
||||||
|
"Couldn't open file " + path.u8string() + " for reading.");
|
||||||
|
}
|
||||||
|
|
||||||
|
SnapshotMetadata metadata;
|
||||||
|
afile >> metadata;
|
||||||
|
|
||||||
|
uint256 base_blockhash = metadata.m_base_blockhash;
|
||||||
|
int max_secs_to_wait_for_headers = 60 * 10;
|
||||||
|
CBlockIndex* snapshot_start_block = nullptr;
|
||||||
|
|
||||||
|
LogPrintf("[snapshot] waiting to see blockheader %s in headers chain before snapshot activation\n",
|
||||||
|
base_blockhash.ToString());
|
||||||
|
|
||||||
|
ChainstateManager& chainman = *node.chainman;
|
||||||
|
|
||||||
|
while (max_secs_to_wait_for_headers > 0) {
|
||||||
|
snapshot_start_block = WITH_LOCK(::cs_main,
|
||||||
|
return chainman.m_blockman.LookupBlockIndex(base_blockhash));
|
||||||
|
max_secs_to_wait_for_headers -= 1;
|
||||||
|
|
||||||
|
if (!IsRPCRunning()) {
|
||||||
|
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!snapshot_start_block) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!snapshot_start_block) {
|
||||||
|
LogPrintf("[snapshot] timed out waiting for snapshot start blockheader %s\n",
|
||||||
|
base_blockhash.ToString());
|
||||||
|
throw JSONRPCError(
|
||||||
|
RPC_INTERNAL_ERROR,
|
||||||
|
"Timed out waiting for base block header to appear in headers chain");
|
||||||
|
}
|
||||||
|
if (!chainman.ActivateSnapshot(afile, metadata, false)) {
|
||||||
|
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to load UTXO snapshot " + fs::PathToString(path));
|
||||||
|
}
|
||||||
|
CBlockIndex* new_tip{WITH_LOCK(::cs_main, return chainman.ActiveTip())};
|
||||||
|
|
||||||
|
UniValue result(UniValue::VOBJ);
|
||||||
|
result.pushKV("coins_loaded", metadata.m_coins_count);
|
||||||
|
result.pushKV("tip_hash", new_tip->GetBlockHash().ToString());
|
||||||
|
result.pushKV("base_height", new_tip->nHeight);
|
||||||
|
result.pushKV("path", fs::PathToString(path));
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void RegisterBlockchainRPCCommands(CRPCTable& t)
|
void RegisterBlockchainRPCCommands(CRPCTable& t)
|
||||||
{
|
{
|
||||||
static const CRPCCommand commands[]{
|
static const CRPCCommand commands[]{
|
||||||
@@ -2722,13 +2822,14 @@ void RegisterBlockchainRPCCommands(CRPCTable& t)
|
|||||||
{"blockchain", &scantxoutset},
|
{"blockchain", &scantxoutset},
|
||||||
{"blockchain", &scanblocks},
|
{"blockchain", &scanblocks},
|
||||||
{"blockchain", &getblockfilter},
|
{"blockchain", &getblockfilter},
|
||||||
|
{"blockchain", &dumptxoutset},
|
||||||
|
{"blockchain", &loadtxoutset},
|
||||||
{"hidden", &invalidateblock},
|
{"hidden", &invalidateblock},
|
||||||
{"hidden", &reconsiderblock},
|
{"hidden", &reconsiderblock},
|
||||||
{"hidden", &waitfornewblock},
|
{"hidden", &waitfornewblock},
|
||||||
{"hidden", &waitforblock},
|
{"hidden", &waitforblock},
|
||||||
{"hidden", &waitforblockheight},
|
{"hidden", &waitforblockheight},
|
||||||
{"hidden", &syncwithvalidationinterfacequeue},
|
{"hidden", &syncwithvalidationinterfacequeue},
|
||||||
{"hidden", &dumptxoutset},
|
|
||||||
};
|
};
|
||||||
for (const auto& c : commands) {
|
for (const auto& c : commands) {
|
||||||
t.appendCommand(c.name, &c);
|
t.appendCommand(c.name, &c);
|
||||||
|
@@ -80,6 +80,7 @@ const std::vector<std::string> RPC_COMMANDS_NOT_SAFE_FOR_FUZZING{
|
|||||||
"gettxoutproof", // avoid prohibitively slow execution
|
"gettxoutproof", // avoid prohibitively slow execution
|
||||||
"importmempool", // avoid reading from disk
|
"importmempool", // avoid reading from disk
|
||||||
"importwallet", // avoid reading from disk
|
"importwallet", // avoid reading from disk
|
||||||
|
"loadtxoutset", // avoid reading from disk
|
||||||
"loadwallet", // avoid reading from disk
|
"loadwallet", // avoid reading from disk
|
||||||
"savemempool", // disabled as a precautionary measure: may take a file path argument in the future
|
"savemempool", // disabled as a precautionary measure: may take a file path argument in the future
|
||||||
"setban", // avoid DNS lookups
|
"setban", // avoid DNS lookups
|
||||||
|
Reference in New Issue
Block a user