diff --git a/src/utxosnapshot.cpp b/src/utxosnapshot.cpp index cfe432d..7d3e24e 100644 --- a/src/utxosnapshot.cpp +++ b/src/utxosnapshot.cpp @@ -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> 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; diff --git a/src/utxosnapshot.h b/src/utxosnapshot.h index aa47e69..81b8ef7 100644 --- a/src/utxosnapshot.h +++ b/src/utxosnapshot.h @@ -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,