Revert "utxosnapshot: v2 format — embed full blk0001.dat into snapshot"

This reverts commit 69529ea4c7.
This commit is contained in:
Sami Ahmed
2026-06-19 03:33:05 -07:00
parent 69529ea4c7
commit be865c5944
2 changed files with 21 additions and 138 deletions
+14 -112
View File
@@ -42,27 +42,20 @@ bool DumpSnapshot(const fs::path& destPath,
return false;
}
// Collect block index entries.
// v2 always includes EVERY block from genesis to tip, so a node
// loading this snapshot has full chain index (every block addressable
// via mapBlockIndex + readable from blk0001.dat).
// The nHeaders argument is honored only for legacy v1 generation.
const bool includeAllBlocks = (nHeaders == 0 || nHeaders > (unsigned int)nBestHeight);
// Collect block index entries (last nHeaders blocks, height ascending)
std::vector<std::pair<uint256, CDiskBlockIndex>> vHeaders;
vHeaders.reserve(nHeaders);
{
CBlockIndex* pindex = pindexBest;
while (pindex) {
unsigned int nCollected = 0;
while (pindex && nCollected < nHeaders) {
CDiskBlockIndex diskindex(pindex);
vHeaders.push_back({*pindex->phashBlock, diskindex});
pindex = pindex->pprev;
nCollected++;
}
// Reverse to height ascending order (genesis first)
// Reverse to height ascending order
std::reverse(vHeaders.begin(), vHeaders.end());
// Legacy v1 path: trim to last nHeaders if requested and not already all
if (!includeAllBlocks && vHeaders.size() > nHeaders) {
vHeaders.erase(vHeaders.begin(), vHeaders.begin() + (vHeaders.size() - nHeaders));
}
}
// Open the chain DB once and reuse for both the UTXO count and the
@@ -98,18 +91,6 @@ bool DumpSnapshot(const fs::path& destPath,
int64_t moneySupply = pindexBest->nMoneySupply;
unsigned int numHeaders = (unsigned int)vHeaders.size();
unsigned int numUtxos = (unsigned int)nUtxoCount;
// numBlocks = size of raw blk0001.dat content to embed in the snapshot.
// We size this up front; the actual write below streams the file.
unsigned int numBlocks = 0;
{
FILE* blkFile = fopen((GetDataDir() / "blk0001.dat").string().c_str(), "rb");
if (blkFile) {
fseek(blkFile, 0, SEEK_END);
long blkSize = ftell(blkFile);
fclose(blkFile);
if (blkSize > 0) numBlocks = (unsigned int)blkSize;
}
}
uint256 contentHash; // placeholder, filled after writing data
fwrite(&magic, sizeof(magic), 1, file);
@@ -120,7 +101,6 @@ bool DumpSnapshot(const fs::path& destPath,
fwrite(&moneySupply, sizeof(moneySupply), 1, file);
fwrite(&numHeaders, sizeof(numHeaders), 1, file);
fwrite(&numUtxos, sizeof(numUtxos), 1, file);
fwrite(&numBlocks, sizeof(numBlocks), 1, file); // v2+: size of embedded blk0001.dat
long contentHashPos = ftell(file);
fwrite(&contentHash, sizeof(contentHash), 1, file); // placeholder
@@ -202,34 +182,6 @@ bool DumpSnapshot(const fs::path& destPath,
}
}
// v2: Append the raw blk0001.dat content so a fresh node is fully
// self-contained. Streams in chunks; SHA256 covers the bytes.
if (numBlocks > 0) {
FILE* blkFile = fopen((GetDataDir() / "blk0001.dat").string().c_str(), "rb");
if (!blkFile) {
fclose(file);
strError = "Cannot open blk0001.dat for snapshot embedding";
return false;
}
printf("UtxoSnapshot: embedding blk0001.dat (%u bytes) into snapshot\n", numBlocks);
unsigned char blkBuf[64 * 1024];
size_t nLeft = numBlocks;
while (nLeft > 0) {
size_t nWant = nLeft > sizeof(blkBuf) ? sizeof(blkBuf) : nLeft;
size_t nRead = fread(blkBuf, 1, nWant, blkFile);
if (nRead != nWant) {
fclose(blkFile);
fclose(file);
strError = "Short read on blk0001.dat during snapshot embed";
return false;
}
fwrite(blkBuf, 1, nRead, file);
SHA256_Update(&sha256, blkBuf, nRead);
nLeft -= nRead;
}
fclose(blkFile);
}
// Finalize content hash and write it to the header
SHA256_Final((unsigned char*)&contentHash, &sha256);
fseek(file, contentHashPos, SEEK_SET);
@@ -237,8 +189,8 @@ bool DumpSnapshot(const fs::path& destPath,
fclose(file);
printf("UtxoSnapshot: wrote %s (%d headers, %d UTXOs, %u block bytes, hash=%s)\n",
destPath.string().c_str(), numHeaders, numUtxos, numBlocks,
printf("UtxoSnapshot: wrote %s (%d headers, %d UTXOs, hash=%s)\n",
destPath.string().c_str(), numHeaders, numUtxos,
contentHash.ToString().c_str());
return true;
@@ -288,7 +240,7 @@ bool LoadSnapshot(const fs::path& snapshotPath,
int height;
uint256 blockHash;
int64_t moneySupply;
unsigned int numHeaders = 0, numUtxos = 0, numBlocks = 0;
unsigned int numHeaders, numUtxos;
uint256 expectedContentHash;
if (fread(&magic, sizeof(magic), 1, file) != 1 ||
@@ -296,32 +248,12 @@ bool LoadSnapshot(const fs::path& snapshotPath,
fread(&network, sizeof(network), 1, file) != 1 ||
fread(&height, sizeof(height), 1, file) != 1 ||
fread(&blockHash, sizeof(blockHash), 1, file) != 1 ||
fread(&moneySupply, sizeof(moneySupply), 1, file) != 1) {
fread(&moneySupply, sizeof(moneySupply), 1, file) != 1 ||
fread(&numHeaders, sizeof(numHeaders), 1, file) != 1 ||
fread(&numUtxos, sizeof(numUtxos), 1, file) != 1 ||
fread(&expectedContentHash, sizeof(expectedContentHash), 1, file) != 1) {
fclose(file);
strError = "Truncated snapshot header (common fields)";
return false;
}
if (fread(&numHeaders, sizeof(numHeaders), 1, file) != 1) {
fclose(file);
strError = "Truncated snapshot header (numHeaders)";
return false;
}
if (fread(&numUtxos, sizeof(numUtxos), 1, file) != 1) {
fclose(file);
strError = "Truncated snapshot header (numUtxos)";
return false;
}
// v2+ has numBlocks between numUtxos and contentHash. v1 stops here.
if (version >= 2) {
if (fread(&numBlocks, sizeof(numBlocks), 1, file) != 1) {
fclose(file);
strError = "Truncated snapshot header (numBlocks)";
return false;
}
}
if (fread(&expectedContentHash, sizeof(expectedContentHash), 1, file) != 1) {
fclose(file);
strError = "Truncated snapshot header (contentHash)";
strError = "Truncated snapshot header";
return false;
}
@@ -528,36 +460,6 @@ bool LoadSnapshot(const fs::path& snapshotPath,
success = false;
}
// v2: After UTXOs, extract the raw blk0001.dat content. This makes the
// loaded node fully self-contained — no separate bootstrap needed.
if (success && version >= 2 && numBlocks > 0) {
printf("UtxoSnapshot: extracting %u block bytes to blk0001.dat...\n", numBlocks);
fs::path blkOut = dataDir / "blk0001.dat";
FILE* blkOutFile = fopen(blkOut.string().c_str(), "wb");
if (!blkOutFile) {
success = false;
strError = "Cannot create blk0001.dat for snapshot extract: " + blkOut.string();
} else {
unsigned char blkBuf[64 * 1024];
size_t nLeft = numBlocks;
while (nLeft > 0 && success) {
size_t nWant = nLeft > sizeof(blkBuf) ? sizeof(blkBuf) : nLeft;
size_t nRead = fread(blkBuf, 1, nWant, file);
if (nRead != nWant) {
success = false;
strError = "Short read on snapshot blocks section";
break;
}
fwrite(blkBuf, 1, nRead, blkOutFile);
SHA256_Update(&sha256, blkBuf, nRead);
nLeft -= nRead;
}
fclose(blkOutFile);
if (success)
printf("UtxoSnapshot: wrote blk0001.dat (%u bytes)\n", numBlocks);
}
}
// Verify content hash
if (success) {
uint256 actualHash;
+7 -26
View File
@@ -11,46 +11,27 @@
static const unsigned int UTXO_SNAPSHOT_MAGIC = 0x53585455; // "UTXS" little-endian
// UTXO snapshot format version
//
// v1: headers (last N=2000) + UTXOs only. Node still needs blk0001.dat from
// somewhere (legacy bootstrap tarball or P2P) to fully verify.
// v2: full chain archive. Adds a `blocks` section with raw blk0001.dat content
// and writes ALL CDiskBlockIndex entries (every block from genesis to
// snapshot tip). After loading a v2 snapshot, the node is fully
// self-contained: it can serve any block to peers, fully verify the
// chain, validate transactions, and resume syncing from the snapshot
// tip forward. Replaces the legacy `tri-bootstrap.tar.gz` artifact.
static const unsigned int UTXO_SNAPSHOT_VERSION = 2;
static const unsigned int UTXO_SNAPSHOT_VERSION = 1;
// Number of block index entries to include in snapshot (v1 only).
// v2 always includes ALL headers — this constant is retained for the v1
// fallback path and as a CLI override for diagnostic snapshots.
// Number of block index entries to include in snapshot (covers difficulty,
// median time, stake modifier, and reorg depth requirements)
static const unsigned int UTXO_SNAPSHOT_DEFAULT_HEADERS = 2000;
namespace UtxoSnapshot {
// Create a UTXO snapshot from the current chain state.
// (v2) Writes ALL CDiskBlockIndex entries + the raw blk0001.dat bytes +
// all UTXOs to destPath. Output is a complete chain archive at the
// current tip — a fresh node loading this is fully usable (can serve
// blocks, fully verify, sync forward).
// Writes last nHeaders block index entries + all UTXOs to destPath.
// Returns true on success, sets strError on failure.
bool DumpSnapshot(const std::filesystem::path& destPath,
unsigned int nHeaders,
std::string& strError);
// Load a UTXO snapshot from a file into the data directory.
// Writes:
// - all CDiskBlockIndex entries → chain DB
// - all UTXOs → chain DB
// - raw blk0001.dat bytes → dataDir/blk0001.dat
// - hashBestChain, dbformat → chain DB
// The chain DB must NOT be open yet (call before LoadBlockIndex).
// Load a UTXO snapshot from a file into a fresh LevelDB.
// Writes block index entries, UTXOs, hashBestChain, and dbformat.
// The LevelDB must NOT be open yet (call before LoadBlockIndex).
// `requireCheckpoint` enforces that the snapshot tip is a known
// checkpoint (for P2P-delivered snapshots). Local loads from a
// trusted operator pass false.
// v1 snapshots (headers+UTXOs only) still load via the partial-load
// path; the blocks section is simply absent.
// Returns true on success, sets strError on failure.
bool LoadSnapshot(const std::filesystem::path& snapshotPath,
const std::filesystem::path& dataDir,