From aab17b3f3cb713a0a47c2708a92b5eee80eb17f5 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 25 Apr 2017 11:29:46 -0700 Subject: [PATCH] Upgrade from per-tx database to per-txout --- src/coins.h | 1 + src/init.cpp | 9 +++++++- src/txdb.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++++++------ src/txdb.h | 2 ++ 4 files changed, 64 insertions(+), 8 deletions(-) diff --git a/src/coins.h b/src/coins.h index 229b9f4e5f118..b60ab742dbc52 100644 --- a/src/coins.h +++ b/src/coins.h @@ -90,6 +90,7 @@ class Coin } }; +//! Legacy class to deserialize pre-pertxout database entries without reindex. class CCoins { public: diff --git a/src/init.cpp b/src/init.cpp index 9381f63dac85b..3a998f102069e 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1563,8 +1563,15 @@ bool AppInit2() pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview); pcoinsTip = new CCoinsViewCache(pcoinscatcher); - if (fReindex) + if (fReindex) { pblocktree->WriteReindexing(true); + } else { + // If necessary, upgrade from older database format. + if (!pcoinsdbview->Upgrade()) { + strLoadError = _("Error upgrading chainstate database"); + break; + } + } // End loop if shutdown was requested if (ShutdownRequested()) break; diff --git a/src/txdb.cpp b/src/txdb.cpp index 807c750bb112d..603431a877425 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -28,11 +28,11 @@ static const char DB_MONEY_SUPPLY = 'M'; namespace { -struct CoinsEntry +struct CoinEntry { COutPoint* outpoint; char key; - CoinsEntry(const COutPoint* ptr) : outpoint(const_cast(ptr)), key(DB_COIN) {} + explicit CoinEntry(const COutPoint* ptr) : outpoint(const_cast(ptr)), key(DB_COIN) {} template void Serialize(Stream &s) const { @@ -58,12 +58,12 @@ CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(Get bool CCoinsViewDB::GetCoins(const COutPoint& outpoint, Coin& coin) const { - return db.Read(CoinsEntry(&outpoint), coin); + return db.Read(CoinEntry(&outpoint), coin); } bool CCoinsViewDB::HaveCoins(const COutPoint& outpoint) const { - return db.Exists(CoinsEntry(&outpoint)); + return db.Exists(CoinEntry(&outpoint)); } uint256 CCoinsViewDB::GetBestBlock() const @@ -81,7 +81,7 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock) size_t changed = 0; for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { if (it->second.flags & CCoinsCacheEntry::DIRTY) { - CoinsEntry entry(&it->first); + CoinEntry entry(&it->first); if (it->second.coins.IsPruned()) batch.Erase(entry); else @@ -148,7 +148,7 @@ CCoinsViewCursor *CCoinsViewDB::Cursor() const // Cache key of first record // Cache key of first record if (i->pcursor->Valid()) { - CoinsEntry entry(&i->keyTmp.second); + CoinEntry entry(&i->keyTmp.second); i->pcursor->GetKey(entry); i->keyTmp.first = entry.key; } else { @@ -185,7 +185,7 @@ bool CCoinsViewDBCursor::Valid() const void CCoinsViewDBCursor::Next() { pcursor->Next(); - CoinsEntry entry(&keyTmp.second); + CoinEntry entry(&keyTmp.second); if (!pcursor->Valid() || !pcursor->GetKey(entry)) { keyTmp.first = 0; // Invalidate cached key after last record so that Valid() and GetKey() return false } else { @@ -478,3 +478,49 @@ bool CZerocoinDB::WipeAccChecksums() LogPrintf("%s: AccChecksum database removed.\n", __func__); return true; } + +/** Upgrade the database from older formats. + * + * Currently implemented: + * - from the per-tx utxo model (4.2.0) to per-txout (4.2.99) + */ +bool CCoinsViewDB::Upgrade() { + std::unique_ptr pcursor(db.NewIterator()); + pcursor->Seek(std::make_pair(DB_COINS, uint256())); + if (!pcursor->Valid()) { + return true; + } + + LogPrintf("Upgrading database...\n"); + size_t batch_size = 1 << 24; + CDBBatch batch; + while (pcursor->Valid()) { + boost::this_thread::interruption_point(); + std::pair key; + if (pcursor->GetKey(key) && key.first == DB_COINS) { + CCoins old_coins; + if (!pcursor->GetValue(old_coins)) { + return error("%s: cannot parse CCoins record", __func__); + } + COutPoint outpoint(key.second, 0); + for (size_t i = 0; i < old_coins.vout.size(); ++i) { + if (!old_coins.vout[i].IsNull() && !old_coins.vout[i].scriptPubKey.IsUnspendable()) { + Coin newcoin(std::move(old_coins.vout[i]), old_coins.nHeight, old_coins.fCoinBase, old_coins.fCoinStake); + outpoint.n = i; + CoinEntry entry(&outpoint); + batch.Write(entry, newcoin); + } + } + batch.Erase(key); + if (batch.SizeEstimate() > batch_size) { + db.WriteBatch(batch); + batch.Clear(); + } + pcursor->Next(); + } else { + break; + } + } + db.WriteBatch(batch); + return true; +} diff --git a/src/txdb.h b/src/txdb.h index 22885e8d83d7f..007aa4d6f5a98 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -80,6 +80,8 @@ class CCoinsViewDB : public CCoinsView bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock) override; CCoinsViewCursor* Cursor() const override; + //! Attempt to update from an older database format. Returns whether an error occurred. + bool Upgrade(); size_t EstimateSize() const override; };