RocksDB+IBD integration: CActiveTxDB wrapper, dual-backend utxosnapshot, headers-first IBD patch, backend-aware bootstrap
Build All Platforms / test-linux-unit (push) Failing after 40s
Build All Platforms / build-linux-qt (push) Failing after 40s
Build All Platforms / build-linux-daemon (push) Failing after 40s
Build All Platforms / build-windows-qt (push) Has been cancelled
Build All Platforms / build-windows-daemon (push) Has been cancelled
Build All Platforms / build-macos (push) Has been cancelled
Build All Platforms / release (push) Has been cancelled
Build All Platforms / Trigger TRI-PI ARM64 Build (push) Has been cancelled

This commit is contained in:
Krystie
2026-04-28 21:50:14 -07:00
parent 2b5471283e
commit 6d70b41844
8 changed files with 297 additions and 91 deletions
+1
View File
@@ -74,6 +74,7 @@ set(CORE_SOURCES
rpcsmessage.cpp
zmqpublishnotifier.cpp
txdb-base.cpp
txdb.cpp
txdb-leveldb.cpp
utxosnapshot.cpp
snapshotnet.cpp
+9 -8
View File
@@ -3,6 +3,7 @@
#include "bootstrap.h"
#include "utxosnapshot.h"
#include "txdb.h"
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
@@ -718,16 +719,16 @@ bool DownloadBootstrap(const std::string& host,
return false;
}
// Check if the archive included a trusted pre-built index (txleveldb/)
// with a valid snapshot.manifest. If verified, keep it to skip the
// Check if the archive included a trusted pre-built index for the active
// backend with a valid snapshot.manifest. If verified, keep it to skip the
// multi-hour FastImportBlockFile() rebuild.
fs::path txleveldb = dataDir / "txleveldb";
fs::path chainDbPath = dataDir / GetActiveChainDbDirName();
fs::path database = dataDir / "database";
fs::path manifestPath = dataDir / "snapshot.manifest";
bool keepIndex = false;
if (fs::exists(manifestPath) && fs::exists(txleveldb)) {
if (fs::exists(manifestPath) && fs::exists(chainDbPath)) {
SnapshotManifest manifest;
std::string manifestError;
@@ -754,9 +755,9 @@ bool DownloadBootstrap(const std::string& host,
if (!keepIndex) {
// No valid manifest or verification failed - delete the index.
// FastImportBlockFile() will rebuild from blk0001.dat on next startup.
printf("Bootstrap: removing extracted txleveldb/ (will rebuild index from blk0001.dat)\n");
if (fs::exists(txleveldb))
fs::remove_all(txleveldb);
printf("Bootstrap: removing extracted %s/ (will rebuild index from blk0001.dat)\n", GetActiveChainDbDirName());
if (fs::exists(chainDbPath))
fs::remove_all(chainDbPath);
}
// Always remove BDB database/ dir (wallet environment from another machine)
@@ -791,7 +792,7 @@ bool DownloadUtxoSnapshot(const std::string& host,
printf("Bootstrap: UTXO snapshot downloaded, loading into database...\n");
// Load the snapshot into a fresh txleveldb
// Load the snapshot into a fresh active chain DB
if (!UtxoSnapshot::LoadSnapshot(tmpPath, dataDir, strError)) {
fs::remove(tmpPath);
return false;
+10 -10
View File
@@ -250,7 +250,7 @@ void Shutdown(void* parg)
pNotificationQueue = NULL;
}
// CTxDB().Close();
// CActiveTxDB().Close();
bitdb.Flush(false);
bitdb.Flush(true);
fs::remove(GetPidFile());
@@ -955,7 +955,7 @@ bool AppInit2()
// Try UTXO snapshot first (fast: ~2-10 MB download)
bool success = false;
bool triedUtxoSnapshot = false;
if (needsBootstrap && !fs::exists(dataPath / "txleveldb")) {
if (needsBootstrap && !fs::exists(dataPath / GetActiveChainDbDirName())) {
uiInterface.InitMessage(_("Downloading UTXO snapshot..."));
printf("Bootstrap: trying UTXO snapshot from %s (fast path)...\n", host.c_str());
@@ -992,13 +992,13 @@ bool AppInit2()
#endif
// ********************************************************* Step 6c: manual UTXO snapshot loading
// If utxo-snapshot.bin exists in data dir and no txleveldb, load it.
// If utxo-snapshot.bin exists in data dir and no active chain DB, load it.
{
fs::path dataPath = GetDataDir();
fs::path snapshotFile = dataPath / "utxo-snapshot.bin";
fs::path txleveldbDir = dataPath / "txleveldb";
fs::path chainDbDir = dataPath / GetActiveChainDbDirName();
if (fs::exists(snapshotFile) && !fs::exists(txleveldbDir)) {
if (fs::exists(snapshotFile) && !fs::exists(chainDbDir)) {
printf("Found utxo-snapshot.bin — loading UTXO snapshot...\n");
uiInterface.InitMessage(_("Loading UTXO snapshot..."));
@@ -1024,7 +1024,7 @@ bool AppInit2()
if (GetBoolArg("-loadblockindextest"))
{
CTxDB txdb("r");
CActiveTxDB txdb("r");
txdb.LoadBlockIndex();
PrintBlockTree();
return false;
@@ -1037,9 +1037,9 @@ bool AppInit2()
{
printf("Reindex requested: removing block index database...\n");
uiInterface.InitMessage(_("Removing block index for reindex..."));
fs::path txleveldbPath = GetDataDir() / "txleveldb";
if (fs::exists(txleveldbPath))
fs::remove_all(txleveldbPath);
fs::path chainDbPath = GetDataDir() / GetActiveChainDbDirName();
if (fs::exists(chainDbPath))
fs::remove_all(chainDbPath);
}
uiInterface.InitMessage(_("Loading block index..."));
@@ -1224,7 +1224,7 @@ bool AppInit2()
bool fScannedWithIndex = false;
if (fAddressIndex && !GetBoolArg("-rescan"))
{
CTxDB txdb("r");
CActiveTxDB txdb("r");
int nAddressIndexStartHeight = 0;
uint256 hashAddressIndexBestChain = 0;
if (txdb.ReadAddressIndexStartHeight(nAddressIndexStartHeight) &&
+60 -29
View File
@@ -931,7 +931,7 @@ bool CTransaction::ReadFromDisk(CTxDBBase& txdb, COutPoint prevout)
bool CTransaction::ReadFromDisk(COutPoint prevout)
{
CTxDB txdb("r");
CActiveTxDB txdb("r");
CTxIndex txindex;
return ReadFromDisk(txdb, prevout, txindex);
}
@@ -1064,7 +1064,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
{
// Load the block this tx is in
CTxIndex txindex;
if (!CTxDB("r").ReadTxIndex(GetHash(), txindex))
if (!CActiveTxDB("r").ReadTxIndex(GetHash(), txindex))
return 0;
if (!blockTmp.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos))
return 0;
@@ -1487,7 +1487,7 @@ bool CMerkleTx::AcceptToMemoryPool(CTxDBBase& txdb, bool fCheckInputs)
bool CMerkleTx::AcceptToMemoryPool()
{
CTxDB txdb("r");
CActiveTxDB txdb("r");
return AcceptToMemoryPool(txdb);
}
@@ -1515,7 +1515,7 @@ bool CWalletTx::AcceptWalletTransaction(CTxDBBase& txdb, bool fCheckInputs)
bool CWalletTx::AcceptWalletTransaction()
{
CTxDB txdb("r");
CActiveTxDB txdb("r");
return AcceptWalletTransaction(txdb);
}
@@ -1548,7 +1548,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock)
return true;
}
}
CTxDB txdb("r");
CActiveTxDB txdb("r");
CTxIndex txindex;
if (tx.ReadFromDisk(txdb, COutPoint(hash, 0), txindex))
{
@@ -1858,7 +1858,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
if (pindexNew->nChainTrust > nBestInvalidTrust)
{
nBestInvalidTrust = pindexNew->nChainTrust;
CTxDB().WriteBestInvalidTrust(CBigNum(nBestInvalidTrust));
CActiveTxDB().WriteBestInvalidTrust(CBigNum(nBestInvalidTrust));
uiInterface.NotifyBlocksChanged();
}
@@ -3171,7 +3171,7 @@ bool CBlock::GetCoinAge(uint64_t& nCoinAge) const
{
nCoinAge = 0;
CTxDB txdb("r");
CActiveTxDB txdb("r");
for (const CTransaction& tx : vtx)
{
uint64_t nTxCoinAge;
@@ -3250,7 +3250,7 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos, const u
pindexNew->phashBlock = &((*mi).first);
// Write to disk block index
CTxDB txdb;
CActiveTxDB txdb;
if (!txdb.TxnBegin())
return false;
txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew));
@@ -3897,7 +3897,7 @@ bool LoadBlockIndex(bool fAllowNew)
//
// Load block index
//
CTxDB txdb("cr+");
CActiveTxDB txdb("cr+");
if (!txdb.LoadBlockIndex())
return false;
@@ -4181,7 +4181,7 @@ bool FastImportBlockFile()
LOCK(cs_main);
CAutoFile blkdat(fileIn, SER_DISK, CLIENT_VERSION);
CTxDB txdb;
CActiveTxDB txdb;
txdb.TxnBegin();
unsigned int nPos = 0;
@@ -4783,7 +4783,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
printf("IBD-DIAG: inv received: %d blocks, %d tx from %s (our height=%d)\n",
nBlockInv, nTxInv, pfrom->addr.ToString().c_str(), nBestHeight);
CTxDB txdb("r");
CActiveTxDB txdb("r");
int nNew = 0, nAlready = 0, nAboveBest = 0;
int nFirstInvHeight = -1, nLastInvHeight = -1;
for (unsigned int nInv = 0; nInv < vInv.size(); nInv++)
@@ -5129,7 +5129,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
vector<uint256> vWorkQueue;
vector<uint256> vEraseQueue;
CDataStream vMsg(vRecv);
CTxDB txdb("r");
CActiveTxDB txdb("r");
CTransaction tx;
vRecv >> tx;
@@ -6107,24 +6107,55 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
(int)pto->mapAskFor.size());
nLastStallLog = GetTime();
}
// Use the walk-forward progress point if available, to avoid
// restarting from pindexBest (which hits the CBlockLocator
// exponential gap and starts the walk-forward from scratch).
pto->pindexLastGetBlocksBegin = NULL;
if (nHighestInvWalk > nBestHeight && hashHighestInvWalk != 0 &&
mapBlockIndex.count(hashHighestInvWalk))
// During IBD, avoid falling back to legacy getblocks recovery
// anchored at pindexBest or a stale inv walk point. That path can
// repeatedly resolve the locator to the same low common ancestor
// on a weak peer set, which looks like a sync "freeze" near an
// early height (for example around 570) even though the real bug
// is the recovery loop. Keep stall recovery header-driven instead
// so the planner tip advances from the newest known header state.
if (IsInitialBlockDownload())
{
pto->PushGetBlocks(mapBlockIndex[hashHighestInvWalk], uint256(0));
printf("SYNC-DIAG: stall re-request from walk=%d (not best=%d)\n",
nHighestInvWalk, nBestHeight);
} else {
pto->PushGetBlocks(pindexBest, uint256(0));
pto->pindexLastGetHeadersBegin = NULL;
uint256 hashLocatorTip = hashBestHeaderSync;
if (hashLocatorTip == 0 && nHighestInvWalk > nBestHeight &&
hashHighestInvWalk != 0 && mapBlockIndex.count(hashHighestInvWalk))
{
hashLocatorTip = hashHighestInvWalk;
}
unsigned int nRefilled = RequestHeaderSyncRefillAllPeers(
hashLocatorTip,
0,
"stall-recovery");
unsigned int nQueued = QueueHeaderSyncBlocksParallel(HEADER_DOWNLOAD_WINDOW);
printf("SYNC-DIAG: stall recovery used headers-first path (locator=%s, refillPeers=%u, queued=%u)\n",
hashLocatorTip.ToString().substr(0,20).c_str(),
nRefilled,
nQueued);
}
else
{
// Outside IBD, preserve the older walk-forward getblocks
// behavior since we're no longer building out a header planner.
pto->pindexLastGetBlocksBegin = NULL;
if (nHighestInvWalk > nBestHeight && hashHighestInvWalk != 0 &&
mapBlockIndex.count(hashHighestInvWalk))
{
pto->PushGetBlocks(mapBlockIndex[hashHighestInvWalk], uint256(0));
printf("SYNC-DIAG: stall re-request from walk=%d (not best=%d)\n",
nHighestInvWalk, nBestHeight);
}
else
{
pto->PushGetBlocks(pindexBest, uint256(0));
}
pto->pindexLastGetHeadersBegin = NULL;
pto->PushGetHeaders(pindexBest, uint256(0));
}
// Also send getheaders during stall to restart the header planner.
// Without this, a drained header cache stays empty because only
// getblocks is sent on stall, which can't refill mapHeaderSync.
pto->pindexLastGetHeadersBegin = NULL;
pto->PushGetHeaders(pindexBest, uint256(0));
nLastBlockReceived = GetTime();
}
}
@@ -6212,7 +6243,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
vector<CInv> vGetData;
int64_t nNow = GetTime() * 1000000;
CTxDB txdb("r");
CActiveTxDB txdb("r");
// During IBD, send larger getdata batches since PoS blocks are small
// and the bottleneck is round-trip latency, not bandwidth.
unsigned int nGetDataBatchSize = IsInitialBlockDownload() ? 4000 : 1000;
+7
View File
@@ -80,6 +80,13 @@ public:
bool IsReadOnly() const { return fReadOnly; }
// Wrapper accessors for backend forwarding (used by CActiveTxDB).
bool ReadRawBytes(const std::string& key, std::string& value) const { return ReadRaw(key, value); }
bool WriteRawBytes(const std::string& key, const std::string& value) { return WriteRaw(key, value); }
bool EraseRawBytes(const std::string& key) { return EraseRaw(key); }
bool ExistsRawBytes(const std::string& key) const { return ExistsRaw(key); }
std::unique_ptr<CTxDBIteratorBase> NewRawIterator() const { return NewIterator(); }
// ── Schema versioning ────────────────────────────────────────────────────
bool ReadVersion(int& nVersion);
bool WriteVersion(int nVersion);
+59
View File
@@ -0,0 +1,59 @@
// Copyright (c) 2026 The Triangles developers.
// Distributed under the MIT/X11 software license.
#include "txdb.h"
#include "util.h"
bool UseRocksDbBackend()
{
#ifdef BUILD_ROCKSDB
return GetBoolArg("-rocksdb", false);
#else
return false;
#endif
}
const char* GetActiveChainDbBackendName()
{
#ifdef BUILD_ROCKSDB
return UseRocksDbBackend() ? "rocksdb" : "leveldb";
#else
return "leveldb";
#endif
}
const char* GetActiveChainDbDirName()
{
#ifdef BUILD_ROCKSDB
return UseRocksDbBackend() ? "rocksdb" : "txleveldb";
#else
return "txleveldb";
#endif
}
CActiveTxDB::CActiveTxDB(const char* pszMode)
{
#ifdef BUILD_ROCKSDB
if (UseRocksDbBackend())
impl.reset(new CRocksTxDB(pszMode));
else
impl.reset(new CTxDB(pszMode));
#else
impl.reset(new CTxDB(pszMode));
#endif
}
CActiveTxDB::~CActiveTxDB() = default;
void CActiveTxDB::Close() { impl->Close(); }
bool CActiveTxDB::TxnBegin() { return impl->TxnBegin(); }
bool CActiveTxDB::TxnCommit() { return impl->TxnCommit(); }
bool CActiveTxDB::TxnAbort() { return impl->TxnAbort(); }
bool CActiveTxDB::LoadBlockIndex() { return impl->LoadBlockIndex(); }
bool CActiveTxDB::ReadRaw(const std::string& key, std::string& value) const { return impl->ReadRawBytes(key, value); }
bool CActiveTxDB::WriteRaw(const std::string& key, const std::string& value) { return impl->WriteRawBytes(key, value); }
bool CActiveTxDB::EraseRaw(const std::string& key) { return impl->EraseRawBytes(key); }
bool CActiveTxDB::ExistsRaw(const std::string& key) const { return impl->ExistsRawBytes(key); }
std::unique_ptr<CTxDBIteratorBase> CActiveTxDB::NewIterator() const { return impl->NewRawIterator(); }
+32
View File
@@ -7,5 +7,37 @@
#define TRIANGLES_TXDB_H
#include "txdb-leveldb.h"
#ifdef BUILD_ROCKSDB
#include "txdb-rocksdb.h"
#endif
#include <memory>
bool UseRocksDbBackend();
const char* GetActiveChainDbBackendName();
const char* GetActiveChainDbDirName();
class CActiveTxDB final : public CTxDBBase
{
public:
explicit CActiveTxDB(const char* pszMode = "r+");
~CActiveTxDB() override;
void Close() override;
bool TxnBegin() override;
bool TxnCommit() override;
bool TxnAbort() override;
bool LoadBlockIndex() override;
protected:
bool ReadRaw(const std::string& key, std::string& value) const override;
bool WriteRaw(const std::string& key, const std::string& value) override;
bool EraseRaw(const std::string& key) override;
bool ExistsRaw(const std::string& key) const override;
std::unique_ptr<CTxDBIteratorBase> NewIterator() const override;
private:
std::unique_ptr<CTxDBBase> impl;
};
#endif // TRIANGLES_TXDB_H
+119 -44
View File
@@ -16,6 +16,13 @@
#include <leveldb/write_batch.h>
#include <leveldb/cache.h>
#include <leveldb/filter_policy.h>
#ifdef BUILD_ROCKSDB
#include <rocksdb/db.h>
#include <rocksdb/write_batch.h>
#include <rocksdb/cache.h>
#include <rocksdb/filter_policy.h>
#include <rocksdb/table.h>
#endif
#include <openssl/sha.h>
@@ -64,7 +71,7 @@ bool DumpSnapshot(const fs::path& destPath,
// Count UTXOs first
int nUtxoCount = 0;
{
CTxDB txdbRead("r");
CActiveTxDB txdbRead("r");
txdbRead.SumUtxoValues(nUtxoCount);
}
@@ -271,47 +278,104 @@ bool LoadSnapshot(const fs::path& snapshotPath,
printf("UtxoSnapshot: loading snapshot at height %d (%d headers, %d UTXOs)\n",
height, numHeaders, numUtxos);
// Create fresh LevelDB directory
fs::path txleveldbPath = dataDir / "txleveldb";
if (fs::exists(txleveldbPath))
fs::remove_all(txleveldbPath);
fs::create_directories(txleveldbPath);
// Create fresh chain DB directory for the active backend.
const bool useRocksDb = UseRocksDbBackend();
fs::path chainDbPath = dataDir / GetActiveChainDbDirName();
if (fs::exists(chainDbPath))
fs::remove_all(chainDbPath);
fs::create_directories(chainDbPath);
// Open LevelDB directly (not via CTxDB - it's not initialized yet)
leveldb::Options options;
int nCacheSizeMB = GetArg("-dbcache", 2048);
options.block_cache = leveldb::NewLRUCache(nCacheSizeMB * 1048576);
options.filter_policy = leveldb::NewBloomFilterPolicy(10);
options.write_buffer_size = 64 * 1048576;
options.max_open_files = 1000;
options.create_if_missing = true;
leveldb::DB* pdb = NULL;
leveldb::Status status = leveldb::DB::Open(options, txleveldbPath.string(), &pdb);
if (!status.ok()) {
leveldb::Options options;
options.block_cache = NULL;
options.filter_policy = NULL;
#ifdef BUILD_ROCKSDB
rocksdb::DB* rdb = NULL;
std::shared_ptr<rocksdb::Cache> rocksCache;
rocksdb::BlockBasedTableOptions rocksTableOptions;
#endif
if (useRocksDb) {
#ifdef BUILD_ROCKSDB
rocksdb::Options rocksOptions;
rocksOptions.create_if_missing = true;
rocksOptions.compression = rocksdb::kSnappyCompression;
rocksOptions.write_buffer_size = 64 * 1048576;
rocksOptions.max_open_files = 1000;
rocksCache = rocksdb::NewLRUCache(static_cast<size_t>(nCacheSizeMB) * 1048576);
rocksTableOptions.block_cache = rocksCache;
rocksTableOptions.filter_policy.reset(rocksdb::NewBloomFilterPolicy(10, false));
rocksOptions.table_factory.reset(rocksdb::NewBlockBasedTableFactory(rocksTableOptions));
rocksdb::Status rocksStatus = rocksdb::DB::Open(rocksOptions, chainDbPath.string(), &rdb);
if (!rocksStatus.ok()) {
fclose(file);
strError = "Cannot create RocksDB: " + rocksStatus.ToString();
return false;
}
#else
fclose(file);
delete options.filter_policy;
delete options.block_cache;
strError = "Cannot create LevelDB: " + status.ToString();
strError = "RocksDB snapshot load requested but binary was built without RocksDB";
return false;
#endif
} else {
options.block_cache = leveldb::NewLRUCache(nCacheSizeMB * 1048576);
options.filter_policy = leveldb::NewBloomFilterPolicy(10);
options.write_buffer_size = 64 * 1048576;
options.max_open_files = 1000;
options.create_if_missing = true;
leveldb::Status status = leveldb::DB::Open(options, chainDbPath.string(), &pdb);
if (!status.ok()) {
fclose(file);
delete options.filter_policy;
delete options.block_cache;
strError = "Cannot create LevelDB: " + status.ToString();
return false;
}
}
SHA256_CTX sha256;
SHA256_Init(&sha256);
leveldb::WriteBatch batch;
bool success = true;
unsigned int nBatchSize = 0;
leveldb::WriteBatch batch;
#ifdef BUILD_ROCKSDB
rocksdb::WriteBatch rocksBatch;
#endif
auto batchPut = [&](const std::string& key, const std::string& value) {
if (useRocksDb) {
#ifdef BUILD_ROCKSDB
rocksBatch.Put(key, value);
#endif
} else {
batch.Put(key, value);
}
nBatchSize++;
};
auto flushBatch = [&]() -> bool {
if (nBatchSize == 0)
return true;
leveldb::Status s = pdb->Write(leveldb::WriteOptions(), &batch);
if (!s.ok()) {
strError = "LevelDB write failed: " + s.ToString();
return false;
if (useRocksDb) {
#ifdef BUILD_ROCKSDB
rocksdb::Status s = rdb->Write(rocksdb::WriteOptions(), &rocksBatch);
if (!s.ok()) {
strError = "RocksDB write failed: " + s.ToString();
return false;
}
rocksBatch.Clear();
#endif
} else {
leveldb::Status s = pdb->Write(leveldb::WriteOptions(), &batch);
if (!s.ok()) {
strError = "LevelDB write failed: " + s.ToString();
return false;
}
batch.Clear();
}
batch.Clear();
nBatchSize = 0;
return true;
};
@@ -352,8 +416,7 @@ bool LoadSnapshot(const fs::path& snapshotPath,
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
ssValue << diskindex;
batch.Put(ssKey.str(), ssValue.str());
nBatchSize++;
batchPut(ssKey.str(), ssValue.str());
if (nBatchSize >= 1000) {
if (!flushBatch()) { success = false; break; }
@@ -401,8 +464,7 @@ bool LoadSnapshot(const fs::path& snapshotPath,
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
ssValue << entry;
batch.Put(ssKey.str(), ssValue.str());
nBatchSize++;
batchPut(ssKey.str(), ssValue.str());
if (nBatchSize >= 50000) {
if (!flushBatch()) { success = false; break; }
@@ -433,37 +495,50 @@ bool LoadSnapshot(const fs::path& snapshotPath,
// Write metadata
if (success) {
leveldb::WriteBatch metaBatch;
// hashBestChain
CDataStream ssKey1(SER_DISK, CLIENT_VERSION);
ssKey1 << std::string("hashBestChain");
CDataStream ssVal1(SER_DISK, CLIENT_VERSION);
ssVal1 << blockHash;
metaBatch.Put(ssKey1.str(), ssVal1.str());
// dbformat = 3
CDataStream ssKey2(SER_DISK, CLIENT_VERSION);
ssKey2 << std::string("dbformat");
CDataStream ssVal2(SER_DISK, CLIENT_VERSION);
ssVal2 << (int)3;
metaBatch.Put(ssKey2.str(), ssVal2.str());
// version
CDataStream ssKey3(SER_DISK, CLIENT_VERSION);
ssKey3 << std::string("version");
CDataStream ssVal3(SER_DISK, CLIENT_VERSION);
ssVal3 << DATABASE_VERSION;
metaBatch.Put(ssKey3.str(), ssVal3.str());
leveldb::Status s = pdb->Write(leveldb::WriteOptions(), &metaBatch);
if (!s.ok()) {
success = false;
strError = "Failed to write metadata: " + s.ToString();
if (useRocksDb) {
#ifdef BUILD_ROCKSDB
rocksdb::WriteBatch metaBatch;
metaBatch.Put(ssKey1.str(), ssVal1.str());
metaBatch.Put(ssKey2.str(), ssVal2.str());
metaBatch.Put(ssKey3.str(), ssVal3.str());
rocksdb::Status s = rdb->Write(rocksdb::WriteOptions(), &metaBatch);
if (!s.ok()) {
success = false;
strError = "Failed to write RocksDB metadata: " + s.ToString();
}
#endif
} else {
leveldb::WriteBatch metaBatch;
metaBatch.Put(ssKey1.str(), ssVal1.str());
metaBatch.Put(ssKey2.str(), ssVal2.str());
metaBatch.Put(ssKey3.str(), ssVal3.str());
leveldb::Status s = pdb->Write(leveldb::WriteOptions(), &metaBatch);
if (!s.ok()) {
success = false;
strError = "Failed to write metadata: " + s.ToString();
}
}
}
// Clean up LevelDB
// Clean up chain DB
#ifdef BUILD_ROCKSDB
delete rdb;
#endif
delete pdb;
delete options.filter_policy;
delete options.block_cache;
@@ -473,8 +548,8 @@ bool LoadSnapshot(const fs::path& snapshotPath,
if (!success) {
// Remove corrupted/incomplete database
printf("UtxoSnapshot: load failed: %s\n", strError.c_str());
if (fs::exists(txleveldbPath))
fs::remove_all(txleveldbPath);
if (fs::exists(chainDbPath))
fs::remove_all(chainDbPath);
return false;
}