rpc: add loadtxoutset

Co-authored-by: Sebastian Falbesoner <sebastian.falbesoner@gmail.com>
This commit is contained in:
James O'Beirne
2019-03-29 15:31:54 -04:00
committed by James O'Beirne
parent 62ac519e71
commit ce585a9a15
4 changed files with 123 additions and 2 deletions

View File

@@ -8,6 +8,7 @@
#include <blockfilter.h>
#include <chain.h>
#include <chainparams.h>
#include <clientversion.h>
#include <coins.h>
#include <common/args.h>
#include <consensus/amount.h>
@@ -2699,6 +2700,105 @@ UniValue CreateUTXOSnapshot(
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)
{
static const CRPCCommand commands[]{
@@ -2722,13 +2822,14 @@ void RegisterBlockchainRPCCommands(CRPCTable& t)
{"blockchain", &scantxoutset},
{"blockchain", &scanblocks},
{"blockchain", &getblockfilter},
{"blockchain", &dumptxoutset},
{"blockchain", &loadtxoutset},
{"hidden", &invalidateblock},
{"hidden", &reconsiderblock},
{"hidden", &waitfornewblock},
{"hidden", &waitforblock},
{"hidden", &waitforblockheight},
{"hidden", &syncwithvalidationinterfacequeue},
{"hidden", &dumptxoutset},
};
for (const auto& c : commands) {
t.appendCommand(c.name, &c);