IBD/sync speedups: parallel RandomX pre-verify, adaptive dbcache, P2P download fixes

- Parallel RandomX PoW pre-verification pool (CCheckQueue) run ahead of the serial
  connect; consensus-neutral (inline CheckRandomXSolution fallback still verifies
  anything not pre-verified). New -randomxverifythreads (default = -par).
- Adaptive dbcache: default sizes the UTXO/coins cache to most of RAM and shrinks
  under memory pressure, always leaving a reserve free; -dbcache pins a fixed value.
- P2P block download: bounded socket recv-drain loop (tlsmanager); frontier-block
  reassignment to break head-of-line stalls (-blockreassigntimeout); ProcessGetData
  serves a bounded batch of blocks per pass instead of one (fixes the serve-side
  one-block-per-tick throttle that caps download network-wide).
- assumeutxo: dumptxoutset RPC + LoadSnapshot machinery + AssumeutxoData chainparams.
- Signed bootstrap verification (util/bootstrap-dragonx.sh, util/sign-bootstrap.md).
- gtest: RandomX pre-verify consensus-equivalence test + UTXO-snapshot round-trip;
  revived the gtest harness (Makefile.am include fix, Makefile.gtest.include).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-19 12:30:10 -05:00
parent 2b011d6ee2
commit 1673cfb6dc
18 changed files with 1599 additions and 154 deletions

View File

@@ -30,7 +30,9 @@
#include "rpc/server.h"
#include "streams.h"
#include "sync.h"
#include "txdb.h"
#include "util.h"
#include <boost/filesystem.hpp>
#include "script/script.h"
#include "script/script_error.h"
#include "script/sign.h"
@@ -860,6 +862,77 @@ UniValue gettxoutsetinfo(const UniValue& params, bool fHelp, const CPubKey& mypk
return ret;
}
UniValue dumptxoutset(const UniValue& params, bool fHelp, const CPubKey& mypk)
{
if (fHelp || params.size() != 1)
throw runtime_error(
"dumptxoutset \"path\"\n"
"\nWrite a trusted snapshot of the current chainstate (UTXO set + Sapling commitment\n"
"trees, nullifier set and pool value) to disk. The snapshot can be loaded by a fresh\n"
"node with -loadutxosnapshot=<file> to skip replaying the chain from genesis.\n"
"\nThis is intended to be run at a final/checkpoint height; the node must be fully synced.\n"
"\nArguments:\n"
"1. \"path\" (string, required) path to write the snapshot file (must not already exist)\n"
"\nResult:\n"
"{\n"
" \"height\": n, (numeric) snapshot height H\n"
" \"base_hash\": \"hex\", (string) block hash at height H\n"
" \"snapshot_hash\": \"hex\", (string) content hash to hardcode for verification\n"
" \"coins\": n, (numeric) number of UTXO records\n"
" \"sapling_anchors\": n, (numeric) number of Sapling anchor records\n"
" \"sapling_nullifiers\": n, (numeric) number of Sapling nullifier records\n"
" \"path\": \"...\" (string) the file written\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("dumptxoutset", "/path/to/dragonx-utxo.dat")
+ HelpExampleRpc("dumptxoutset", "\"/path/to/dragonx-utxo.dat\"")
);
boost::filesystem::path path = boost::filesystem::absolute(params[0].get_str());
if (boost::filesystem::exists(path))
throw JSONRPCError(RPC_INVALID_PARAMETER, "path already exists, refusing to overwrite: " + path.string());
LOCK(cs_main);
if (pcoinsdbview == nullptr || pcoinsTip == nullptr)
throw JSONRPCError(RPC_INTERNAL_ERROR, "chainstate not available");
// Flush so the on-disk chainstate matches the in-memory tip before we iterate it.
FlushStateToDisk();
CBlockIndex *tip = chainActive.Tip();
if (tip == nullptr)
throw JSONRPCError(RPC_INTERNAL_ERROR, "no chain tip");
CUTXOSnapshotHeader header;
header.nMagic = UTXO_SNAPSHOT_MAGIC;
header.nVersion = UTXO_SNAPSHOT_VERSION;
memcpy(&header.nNetworkMagic, Params().MessageStart(), 4);
header.baseBlockHash = tip->GetBlockHash();
header.nHeight = tip->GetHeight();
header.nChainTx = tip->nChainTx;
if (tip->nChainSaplingValue) {
header.fHasChainSaplingValue = 1;
header.nChainSaplingValue = *tip->nChainSaplingValue;
}
header.bestSaplingAnchor = pcoinsdbview->GetBestAnchor(SAPLING);
uint256 snapshotHash;
std::string strError;
if (!pcoinsdbview->DumpSnapshot(path.string(), header, snapshotHash, strError))
throw JSONRPCError(RPC_INTERNAL_ERROR, "dumptxoutset failed: " + strError);
UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("height", (int64_t)header.nHeight));
ret.push_back(Pair("base_hash", header.baseBlockHash.GetHex()));
ret.push_back(Pair("snapshot_hash", snapshotHash.GetHex()));
ret.push_back(Pair("coins", (int64_t)header.nCoins));
ret.push_back(Pair("sapling_anchors", (int64_t)header.nSaplingAnchors));
ret.push_back(Pair("sapling_nullifiers", (int64_t)header.nSaplingNullifiers));
ret.push_back(Pair("path", path.string()));
return ret;
}
UniValue getblockmerkletree(const UniValue& params, bool fHelp, const CPubKey& mypk)
{
if (fHelp || params.size() != 1 )
@@ -1851,6 +1924,7 @@ static const CRPCCommand commands[] =
{ "blockchain", "getrawmempool", &getrawmempool, true },
{ "blockchain", "gettxout", &gettxout, true },
{ "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true },
{ "blockchain", "dumptxoutset", &dumptxoutset, true },
{ "blockchain", "verifychain", &verifychain, true },
/* Not shown in help */