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

@@ -56,6 +56,61 @@ static const int64_t nMaxDbCache = sizeof(void*) > 4 ? 16384 : 1024;
//! min. -dbcache in (MiB)
static const int64_t nMinDbCache = 4;
/** Magic + version for the trusted UTXO-snapshot (assumeutxo-style) file format. */
static const uint32_t UTXO_SNAPSHOT_MAGIC = 0x58535844; // 'DXSX'
static const uint8_t UTXO_SNAPSHOT_VERSION = 1;
/**
* Header of a trusted chainstate snapshot taken at a final height H. On this private
* chain the chainstate is more than transparent UTXOs, so the snapshot also carries the
* Sapling commitment trees, the nullifier set, the best Sapling anchor and the pool value.
*
* File layout: [CUTXOSnapshotHeader]
* nCoins × (uint256 txid, CCoins)
* nSaplingAnchors × (uint256 root, SaplingMerkleTree)
* nSaplingNullifiers × (uint256 nullifier)
* uint256 contentHash // hash over everything above (NOT itself)
*/
struct CUTXOSnapshotHeader
{
uint32_t nMagic;
uint8_t nVersion;
uint32_t nNetworkMagic; // Params().MessageStart() as uint32 — prevents cross-network use
uint256 baseBlockHash; // hash of block H (the snapshot tip)
int32_t nHeight; // H
uint64_t nChainTx; // cumulative tx count at H (needed for tip fix-up)
uint8_t fHasChainSaplingValue;
int64_t nChainSaplingValue; // cumulative Sapling pool value at H (valid iff fHasChainSaplingValue)
uint256 bestSaplingAnchor; // best Sapling anchor root at H
uint64_t nCoins;
uint64_t nSaplingAnchors;
uint64_t nSaplingNullifiers;
CUTXOSnapshotHeader() { SetNull(); }
void SetNull() {
nMagic = 0; nVersion = 0; nNetworkMagic = 0; baseBlockHash.SetNull();
nHeight = 0; nChainTx = 0; fHasChainSaplingValue = 0; nChainSaplingValue = 0;
bestSaplingAnchor.SetNull(); nCoins = 0; nSaplingAnchors = 0; nSaplingNullifiers = 0;
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(nMagic);
READWRITE(nVersion);
READWRITE(nNetworkMagic);
READWRITE(baseBlockHash);
READWRITE(nHeight);
READWRITE(nChainTx);
READWRITE(fHasChainSaplingValue);
READWRITE(nChainSaplingValue);
READWRITE(bestSaplingAnchor);
READWRITE(nCoins);
READWRITE(nSaplingAnchors);
READWRITE(nSaplingNullifiers);
}
};
/** CCoinsView backed by the coin database (chainstate/) */
class CCoinsViewDB : public CCoinsView
{
@@ -81,6 +136,19 @@ public:
CNullifiersMap &mapSproutNullifiers,
CNullifiersMap &mapSaplingNullifiers);
bool GetStats(CCoinsStats &stats) const;
//! Stream the full chainstate at the current tip into a snapshot file (assumeutxo-style
//! producer). Caller fills the metadata fields of `header` (height, baseBlockHash, nChainTx,
//! pool value, bestSaplingAnchor); this fills the counts, writes the file, and returns the
//! content hash. Caller must hold cs_main and have flushed the cache to disk first.
bool DumpSnapshot(const std::string &path, CUTXOSnapshotHeader &header, uint256 &hashRet, std::string &strError) const;
//! Load a snapshot file produced by DumpSnapshot into the (empty) chainstate DB. Two passes:
//! pass 1 reads everything and verifies the internal content hash (and, if fRequireExpected,
//! that it equals expectedHash) WITHOUT touching the DB; pass 2 writes coins/anchors/nullifiers
//! plus the best-block / best-sapling-anchor pointers. Returns the header + computed hash.
bool LoadSnapshot(const std::string &path, const uint256 &expectedHash, bool fRequireExpected,
CUTXOSnapshotHeader &headerRet, uint256 &hashRet, std::string &strError);
};
/** Access to the block database (blocks/index/) */
@@ -117,6 +185,9 @@ public:
bool ReadTimestampBlockIndex(const uint256 &hash, unsigned int &logicalTS) const;
bool WriteFlag(const std::string &name, bool fValue);
bool ReadFlag(const std::string &name, bool &fValue) const;
//! Persist/restore the height of a loaded UTXO snapshot so the reorg-below-H guard survives restarts.
bool WriteAssumeutxoHeight(int nHeight);
bool ReadAssumeutxoHeight(int &nHeight) const;
bool LoadBlockIndexGuts();
bool blockOnchainActive(const uint256 &hash);
UniValue Snapshot(int top);