8
8
#include < blockfilter.h>
9
9
#include < chain.h>
10
10
#include < chainparams.h>
11
+ #include < clientversion.h>
11
12
#include < coins.h>
12
13
#include < common/args.h>
13
14
#include < consensus/amount.h>
@@ -2699,6 +2700,105 @@ UniValue CreateUTXOSnapshot(
2699
2700
return result;
2700
2701
}
2701
2702
2703
+ static RPCHelpMan loadtxoutset ()
2704
+ {
2705
+ return RPCHelpMan{
2706
+ " loadtxoutset" ,
2707
+ " Load the serialized UTXO set from disk.\n "
2708
+ " Once this snapshot is loaded, its contents will be "
2709
+ " deserialized into a second chainstate data structure, which is then used to sync to "
2710
+ " the network's tip under a security model very much like `assumevalid`. "
2711
+ " Meanwhile, the original chainstate will complete the initial block download process in "
2712
+ " the background, eventually validating up to the block that the snapshot is based upon.\n\n "
2713
+
2714
+ " The result is a usable bitcoind instance that is current with the network tip in a "
2715
+ " matter of minutes rather than hours. UTXO snapshot are typically obtained from "
2716
+ " third-party sources (HTTP, torrent, etc.) which is reasonable since their "
2717
+ " contents are always checked by hash.\n\n "
2718
+
2719
+ " You can find more information on this process in the `assumeutxo` design "
2720
+ " document (<https://github.com/bitcoin/bitcoin/blob/master/doc/design/assumeutxo.md>)." ,
2721
+ {
2722
+ {" path" ,
2723
+ RPCArg::Type::STR,
2724
+ RPCArg::Optional::NO,
2725
+ " path to the snapshot file. If relative, will be prefixed by datadir." },
2726
+ },
2727
+ RPCResult{
2728
+ RPCResult::Type::OBJ, " " , " " ,
2729
+ {
2730
+ {RPCResult::Type::NUM, " coins_loaded" , " the number of coins loaded from the snapshot" },
2731
+ {RPCResult::Type::STR_HEX, " tip_hash" , " the hash of the base of the snapshot" },
2732
+ {RPCResult::Type::NUM, " base_height" , " the height of the base of the snapshot" },
2733
+ {RPCResult::Type::STR, " path" , " the absolute path that the snapshot was loaded from" },
2734
+ }
2735
+ },
2736
+ RPCExamples{
2737
+ HelpExampleCli (" loadtxoutset" , " utxo.dat" )
2738
+ },
2739
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
2740
+ {
2741
+ NodeContext& node = EnsureAnyNodeContext (request.context );
2742
+ fs::path path{AbsPathForConfigVal (EnsureArgsman (node), fs::u8path (request.params [0 ].get_str ()))};
2743
+
2744
+ FILE* file{fsbridge::fopen (path, " rb" )};
2745
+ AutoFile afile{file};
2746
+ if (afile.IsNull ()) {
2747
+ throw JSONRPCError (
2748
+ RPC_INVALID_PARAMETER,
2749
+ " Couldn't open file " + path.u8string () + " for reading." );
2750
+ }
2751
+
2752
+ SnapshotMetadata metadata;
2753
+ afile >> metadata;
2754
+
2755
+ uint256 base_blockhash = metadata.m_base_blockhash ;
2756
+ int max_secs_to_wait_for_headers = 60 * 10 ;
2757
+ CBlockIndex* snapshot_start_block = nullptr ;
2758
+
2759
+ LogPrintf (" [snapshot] waiting to see blockheader %s in headers chain before snapshot activation\n " ,
2760
+ base_blockhash.ToString ());
2761
+
2762
+ ChainstateManager& chainman = *node.chainman ;
2763
+
2764
+ while (max_secs_to_wait_for_headers > 0 ) {
2765
+ snapshot_start_block = WITH_LOCK (::cs_main,
2766
+ return chainman.m_blockman .LookupBlockIndex (base_blockhash));
2767
+ max_secs_to_wait_for_headers -= 1 ;
2768
+
2769
+ if (!IsRPCRunning ()) {
2770
+ throw JSONRPCError (RPC_CLIENT_NOT_CONNECTED, " Shutting down" );
2771
+ }
2772
+
2773
+ if (!snapshot_start_block) {
2774
+ std::this_thread::sleep_for (std::chrono::seconds (1 ));
2775
+ } else {
2776
+ break ;
2777
+ }
2778
+ }
2779
+
2780
+ if (!snapshot_start_block) {
2781
+ LogPrintf (" [snapshot] timed out waiting for snapshot start blockheader %s\n " ,
2782
+ base_blockhash.ToString ());
2783
+ throw JSONRPCError (
2784
+ RPC_INTERNAL_ERROR,
2785
+ " Timed out waiting for base block header to appear in headers chain" );
2786
+ }
2787
+ if (!chainman.ActivateSnapshot (afile, metadata, false )) {
2788
+ throw JSONRPCError (RPC_INTERNAL_ERROR, " Unable to load UTXO snapshot " + fs::PathToString (path));
2789
+ }
2790
+ CBlockIndex* new_tip{WITH_LOCK (::cs_main, return chainman.ActiveTip ())};
2791
+
2792
+ UniValue result (UniValue::VOBJ);
2793
+ result.pushKV (" coins_loaded" , metadata.m_coins_count );
2794
+ result.pushKV (" tip_hash" , new_tip->GetBlockHash ().ToString ());
2795
+ result.pushKV (" base_height" , new_tip->nHeight );
2796
+ result.pushKV (" path" , fs::PathToString (path));
2797
+ return result;
2798
+ },
2799
+ };
2800
+ }
2801
+
2702
2802
void RegisterBlockchainRPCCommands (CRPCTable& t)
2703
2803
{
2704
2804
static const CRPCCommand commands[]{
@@ -2722,13 +2822,14 @@ void RegisterBlockchainRPCCommands(CRPCTable& t)
2722
2822
{" blockchain" , &scantxoutset},
2723
2823
{" blockchain" , &scanblocks},
2724
2824
{" blockchain" , &getblockfilter},
2825
+ {" blockchain" , &dumptxoutset},
2826
+ {" blockchain" , &loadtxoutset},
2725
2827
{" hidden" , &invalidateblock},
2726
2828
{" hidden" , &reconsiderblock},
2727
2829
{" hidden" , &waitfornewblock},
2728
2830
{" hidden" , &waitforblock},
2729
2831
{" hidden" , &waitforblockheight},
2730
2832
{" hidden" , &syncwithvalidationinterfacequeue},
2731
- {" hidden" , &dumptxoutset},
2732
2833
};
2733
2834
for (const auto & c : commands) {
2734
2835
t.appendCommand (c.name , &c);
0 commit comments