Changeset View
Changeset View
Standalone View
Standalone View
src/txdb.cpp
Show All 13 Lines | |||||
#include <util/system.h> | #include <util/system.h> | ||||
#include <util/translation.h> | #include <util/translation.h> | ||||
#include <util/vector.h> | #include <util/vector.h> | ||||
#include <version.h> | #include <version.h> | ||||
#include <boost/thread.hpp> // boost::this_thread::interruption_point() (mingw) | #include <boost/thread.hpp> // boost::this_thread::interruption_point() (mingw) | ||||
#include <cstdint> | #include <cstdint> | ||||
#include <memory> | |||||
static const char DB_COIN = 'C'; | static const char DB_COIN = 'C'; | ||||
static const char DB_COINS = 'c'; | static const char DB_COINS = 'c'; | ||||
static const char DB_BLOCK_FILES = 'f'; | static const char DB_BLOCK_FILES = 'f'; | ||||
static const char DB_BLOCK_INDEX = 'b'; | static const char DB_BLOCK_INDEX = 'b'; | ||||
static const char DB_BEST_BLOCK = 'B'; | static const char DB_BEST_BLOCK = 'B'; | ||||
static const char DB_HEAD_BLOCKS = 'H'; | static const char DB_HEAD_BLOCKS = 'H'; | ||||
Show All 15 Lines | SERIALIZE_METHODS(CoinEntry, obj) { | ||||
READWRITE(obj.key, id, VARINT(n)); | READWRITE(obj.key, id, VARINT(n)); | ||||
SER_READ(obj, *obj.outpoint = COutPoint(id, n)); | SER_READ(obj, *obj.outpoint = COutPoint(id, n)); | ||||
} | } | ||||
}; | }; | ||||
} // namespace | } // namespace | ||||
CCoinsViewDB::CCoinsViewDB(fs::path ldb_path, size_t nCacheSize, bool fMemory, | CCoinsViewDB::CCoinsViewDB(fs::path ldb_path, size_t nCacheSize, bool fMemory, | ||||
bool fWipe) | bool fWipe) | ||||
: db(ldb_path, nCacheSize, fMemory, fWipe, true) {} | : m_db(std::make_unique<CDBWrapper>(ldb_path, nCacheSize, fMemory, fWipe, | ||||
true)), | |||||
m_ldb_path(ldb_path), m_is_memory(fMemory) {} | |||||
void CCoinsViewDB::ResizeCache(size_t new_cache_size) { | |||||
// Have to do a reset first to get the original `m_db` state to release its | |||||
// filesystem lock. | |||||
m_db.reset(); | |||||
m_db = std::make_unique<CDBWrapper>(m_ldb_path, new_cache_size, m_is_memory, | |||||
/*fWipe*/ false, /*obfuscate*/ true); | |||||
} | |||||
bool CCoinsViewDB::GetCoin(const COutPoint &outpoint, Coin &coin) const { | bool CCoinsViewDB::GetCoin(const COutPoint &outpoint, Coin &coin) const { | ||||
return db.Read(CoinEntry(&outpoint), coin); | return m_db->Read(CoinEntry(&outpoint), coin); | ||||
} | } | ||||
bool CCoinsViewDB::HaveCoin(const COutPoint &outpoint) const { | bool CCoinsViewDB::HaveCoin(const COutPoint &outpoint) const { | ||||
return db.Exists(CoinEntry(&outpoint)); | return m_db->Exists(CoinEntry(&outpoint)); | ||||
} | } | ||||
BlockHash CCoinsViewDB::GetBestBlock() const { | BlockHash CCoinsViewDB::GetBestBlock() const { | ||||
BlockHash hashBestChain; | BlockHash hashBestChain; | ||||
if (!db.Read(DB_BEST_BLOCK, hashBestChain)) { | if (!m_db->Read(DB_BEST_BLOCK, hashBestChain)) { | ||||
return BlockHash(); | return BlockHash(); | ||||
} | } | ||||
return hashBestChain; | return hashBestChain; | ||||
} | } | ||||
std::vector<BlockHash> CCoinsViewDB::GetHeadBlocks() const { | std::vector<BlockHash> CCoinsViewDB::GetHeadBlocks() const { | ||||
std::vector<BlockHash> vhashHeadBlocks; | std::vector<BlockHash> vhashHeadBlocks; | ||||
if (!db.Read(DB_HEAD_BLOCKS, vhashHeadBlocks)) { | if (!m_db->Read(DB_HEAD_BLOCKS, vhashHeadBlocks)) { | ||||
return std::vector<BlockHash>(); | return std::vector<BlockHash>(); | ||||
} | } | ||||
return vhashHeadBlocks; | return vhashHeadBlocks; | ||||
} | } | ||||
bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const BlockHash &hashBlock) { | bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const BlockHash &hashBlock) { | ||||
CDBBatch batch(db); | CDBBatch batch(*m_db); | ||||
size_t count = 0; | size_t count = 0; | ||||
size_t changed = 0; | size_t changed = 0; | ||||
size_t batch_size = | size_t batch_size = | ||||
(size_t)gArgs.GetArg("-dbbatchsize", DEFAULT_DB_BATCH_SIZE); | (size_t)gArgs.GetArg("-dbbatchsize", DEFAULT_DB_BATCH_SIZE); | ||||
int crash_simulate = gArgs.GetArg("-dbcrashratio", 0); | int crash_simulate = gArgs.GetArg("-dbcrashratio", 0); | ||||
assert(!hashBlock.IsNull()); | assert(!hashBlock.IsNull()); | ||||
BlockHash old_tip = GetBestBlock(); | BlockHash old_tip = GetBestBlock(); | ||||
Show All 24 Lines | for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { | ||||
changed++; | changed++; | ||||
} | } | ||||
count++; | count++; | ||||
CCoinsMap::iterator itOld = it++; | CCoinsMap::iterator itOld = it++; | ||||
mapCoins.erase(itOld); | mapCoins.erase(itOld); | ||||
if (batch.SizeEstimate() > batch_size) { | if (batch.SizeEstimate() > batch_size) { | ||||
LogPrint(BCLog::COINDB, "Writing partial batch of %.2f MiB\n", | LogPrint(BCLog::COINDB, "Writing partial batch of %.2f MiB\n", | ||||
batch.SizeEstimate() * (1.0 / 1048576.0)); | batch.SizeEstimate() * (1.0 / 1048576.0)); | ||||
db.WriteBatch(batch); | m_db->WriteBatch(batch); | ||||
batch.Clear(); | batch.Clear(); | ||||
if (crash_simulate) { | if (crash_simulate) { | ||||
static FastRandomContext rng; | static FastRandomContext rng; | ||||
if (rng.randrange(crash_simulate) == 0) { | if (rng.randrange(crash_simulate) == 0) { | ||||
LogPrintf("Simulating a crash. Goodbye.\n"); | LogPrintf("Simulating a crash. Goodbye.\n"); | ||||
_Exit(0); | _Exit(0); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// In the last batch, mark the database as consistent with hashBlock again. | // In the last batch, mark the database as consistent with hashBlock again. | ||||
batch.Erase(DB_HEAD_BLOCKS); | batch.Erase(DB_HEAD_BLOCKS); | ||||
batch.Write(DB_BEST_BLOCK, hashBlock); | batch.Write(DB_BEST_BLOCK, hashBlock); | ||||
LogPrint(BCLog::COINDB, "Writing final batch of %.2f MiB\n", | LogPrint(BCLog::COINDB, "Writing final batch of %.2f MiB\n", | ||||
batch.SizeEstimate() * (1.0 / 1048576.0)); | batch.SizeEstimate() * (1.0 / 1048576.0)); | ||||
bool ret = db.WriteBatch(batch); | bool ret = m_db->WriteBatch(batch); | ||||
LogPrint(BCLog::COINDB, | LogPrint(BCLog::COINDB, | ||||
"Committed %u changed transaction outputs (out of " | "Committed %u changed transaction outputs (out of " | ||||
"%u) to coin database...\n", | "%u) to coin database...\n", | ||||
(unsigned int)changed, (unsigned int)count); | (unsigned int)changed, (unsigned int)count); | ||||
return ret; | return ret; | ||||
} | } | ||||
size_t CCoinsViewDB::EstimateSize() const { | size_t CCoinsViewDB::EstimateSize() const { | ||||
return db.EstimateSize(DB_COIN, char(DB_COIN + 1)); | return m_db->EstimateSize(DB_COIN, char(DB_COIN + 1)); | ||||
} | } | ||||
CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) | CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) | ||||
: CDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, | : CDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, | ||||
fWipe) {} | fWipe) {} | ||||
bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) { | bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) { | ||||
return Read(std::make_pair(DB_BLOCK_FILES, nFile), info); | return Read(std::make_pair(DB_BLOCK_FILES, nFile), info); | ||||
Show All 12 Lines | |||||
} | } | ||||
bool CBlockTreeDB::ReadLastBlockFile(int &nFile) { | bool CBlockTreeDB::ReadLastBlockFile(int &nFile) { | ||||
return Read(DB_LAST_BLOCK, nFile); | return Read(DB_LAST_BLOCK, nFile); | ||||
} | } | ||||
CCoinsViewCursor *CCoinsViewDB::Cursor() const { | CCoinsViewCursor *CCoinsViewDB::Cursor() const { | ||||
CCoinsViewDBCursor *i = new CCoinsViewDBCursor( | CCoinsViewDBCursor *i = new CCoinsViewDBCursor( | ||||
const_cast<CDBWrapper &>(db).NewIterator(), GetBestBlock()); | const_cast<CDBWrapper &>(*m_db).NewIterator(), GetBestBlock()); | ||||
/** | /** | ||||
* It seems that there are no "const iterators" for LevelDB. Since we only | * It seems that there are no "const iterators" for LevelDB. Since we only | ||||
* need read operations on it, use a const-cast to get around that | * need read operations on it, use a const-cast to get around that | ||||
* restriction. | * restriction. | ||||
*/ | */ | ||||
i->pcursor->Seek(DB_COIN); | i->pcursor->Seek(DB_COIN); | ||||
// Cache key of first record | // Cache key of first record | ||||
if (i->pcursor->Valid()) { | if (i->pcursor->Valid()) { | ||||
▲ Show 20 Lines • Show All 188 Lines • ▼ Show 20 Lines | |||||
} // namespace | } // namespace | ||||
/** | /** | ||||
* Upgrade the database from older formats. | * Upgrade the database from older formats. | ||||
* | * | ||||
* Currently implemented: from the per-tx utxo model (0.8..0.14.x) to per-txout. | * Currently implemented: from the per-tx utxo model (0.8..0.14.x) to per-txout. | ||||
*/ | */ | ||||
bool CCoinsViewDB::Upgrade() { | bool CCoinsViewDB::Upgrade() { | ||||
std::unique_ptr<CDBIterator> pcursor(db.NewIterator()); | std::unique_ptr<CDBIterator> pcursor(m_db->NewIterator()); | ||||
pcursor->Seek(std::make_pair(DB_COINS, uint256())); | pcursor->Seek(std::make_pair(DB_COINS, uint256())); | ||||
if (!pcursor->Valid()) { | if (!pcursor->Valid()) { | ||||
return true; | return true; | ||||
} | } | ||||
int64_t count = 0; | int64_t count = 0; | ||||
LogPrintf("Upgrading utxo-set database...\n"); | LogPrintf("Upgrading utxo-set database...\n"); | ||||
size_t batch_size = 1 << 24; | size_t batch_size = 1 << 24; | ||||
CDBBatch batch(db); | CDBBatch batch(*m_db); | ||||
int reportDone = -1; | int reportDone = -1; | ||||
std::pair<uint8_t, uint256> key; | std::pair<uint8_t, uint256> key; | ||||
std::pair<uint8_t, uint256> prev_key = {DB_COINS, uint256()}; | std::pair<uint8_t, uint256> prev_key = {DB_COINS, uint256()}; | ||||
while (pcursor->Valid()) { | while (pcursor->Valid()) { | ||||
boost::this_thread::interruption_point(); | boost::this_thread::interruption_point(); | ||||
if (ShutdownRequested()) { | if (ShutdownRequested()) { | ||||
break; | break; | ||||
} | } | ||||
Show All 29 Lines | while (pcursor->Valid()) { | ||||
COutPoint outpoint(id, i); | COutPoint outpoint(id, i); | ||||
CoinEntry entry(&outpoint); | CoinEntry entry(&outpoint); | ||||
batch.Write(entry, newcoin); | batch.Write(entry, newcoin); | ||||
} | } | ||||
} | } | ||||
batch.Erase(key); | batch.Erase(key); | ||||
if (batch.SizeEstimate() > batch_size) { | if (batch.SizeEstimate() > batch_size) { | ||||
db.WriteBatch(batch); | m_db->WriteBatch(batch); | ||||
batch.Clear(); | batch.Clear(); | ||||
db.CompactRange(prev_key, key); | m_db->CompactRange(prev_key, key); | ||||
prev_key = key; | prev_key = key; | ||||
} | } | ||||
pcursor->Next(); | pcursor->Next(); | ||||
} | } | ||||
db.WriteBatch(batch); | m_db->WriteBatch(batch); | ||||
db.CompactRange({DB_COINS, uint256()}, key); | m_db->CompactRange({DB_COINS, uint256()}, key); | ||||
uiInterface.ShowProgress("", 100, false); | uiInterface.ShowProgress("", 100, false); | ||||
LogPrintf("[%s].\n", ShutdownRequested() ? "CANCELLED" : "DONE"); | LogPrintf("[%s].\n", ShutdownRequested() ? "CANCELLED" : "DONE"); | ||||
return !ShutdownRequested(); | return !ShutdownRequested(); | ||||
} | } | ||||
bool CBlockTreeDB::Upgrade(const Consensus::Params ¶ms) { | bool CBlockTreeDB::Upgrade(const Consensus::Params ¶ms) { | ||||
std::unique_ptr<CDBIterator> pcursor(NewIterator()); | std::unique_ptr<CDBIterator> pcursor(NewIterator()); | ||||
▲ Show 20 Lines • Show All 101 Lines • Show Last 20 Lines |