Skip to content

Commit f421de5

Browse files
committed
Merge bitcoin/bitcoin#24236: Remove utxo db upgrade code
fa9112a Remove utxo db upgrade code (MarcoFalke) Pull request description: It is not possible to upgrade Bitcoin Core pre-segwit (pre-0.13.1) to a recent version without a full IBD from scratch after commit 19a56d1 (released in version 22.0). Any Bitcoin Core version with the new database format after commit 1088b02 (released in version 0.15), can upgrade to any version that is supported as of today. This leaves the versions 0.13.1-0.14.x. Even though those versions are unsupported, some users with an existing datadir may want to upgrade to a recent version. However, it seems reasonable to simply ask them to `-reindex` to run a full IBD from scratch. This allows us to remove the utxo db upgrade code. ACKs for top commit: Sjors: re-ACK fa9112a laanwj: Code review ACK fa9112a Tree-SHA512: 4243bb35df9ac4892f9fad30fe486d338745952bcff4160bcb0937c772d57b13b800647da14695e21e3655e85ee0d95fa3dc7789ee309d59ad84f422297fecb8
2 parents ee9af95 + fa9112a commit f421de5

File tree

9 files changed

+87
-134
lines changed

9 files changed

+87
-134
lines changed

ci/test/00_setup_env_native_qt5.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ export TEST_RUNNER_EXTRA="--previous-releases --coverage --extended --exclude fe
1414
export RUN_UNIT_TESTS_SEQUENTIAL="true"
1515
export RUN_UNIT_TESTS="false"
1616
export GOAL="install"
17-
export PREVIOUS_RELEASES_TO_DOWNLOAD="v0.15.2 v0.16.3 v0.17.2 v0.18.1 v0.19.1 v0.20.1 v0.21.0 v22.0"
17+
export PREVIOUS_RELEASES_TO_DOWNLOAD="v0.14.3 v0.15.2 v0.16.3 v0.17.2 v0.18.1 v0.19.1 v0.20.1 v0.21.0 v22.0"
1818
export BITCOIN_CONFIG="--enable-zmq --with-libs=no --with-gui=qt5 --enable-reduce-exports \
1919
--enable-debug CFLAGS=\"-g0 -O2 -funsigned-char\" CXXFLAGS=\"-g0 -O2 -funsigned-char\" CC=gcc-8 CXX=g++-8"

src/init.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1453,8 +1453,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
14531453
strLoadError = _("Error initializing block database");
14541454
break;
14551455
case ChainstateLoadingError::ERROR_CHAINSTATE_UPGRADE_FAILED:
1456-
strLoadError = _("Error upgrading chainstate database");
1457-
break;
1456+
return InitError(_("Unsupported chainstate database format found. "
1457+
"Please restart with -reindex-chainstate. This will "
1458+
"rebuild the chainstate database."));
14581459
case ChainstateLoadingError::ERROR_REPLAYBLOCKS_FAILED:
14591460
strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.");
14601461
break;

src/node/chainstate.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,9 @@ std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
9090
chainstate->CoinsErrorCatcher().AddReadErrCallback(coins_error_cb);
9191
}
9292

93-
// If necessary, upgrade from older database format.
93+
// Refuse to load unsupported database format.
9494
// This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
95-
if (!chainstate->CoinsDB().Upgrade()) {
95+
if (chainstate->CoinsDB().NeedsUpgrade()) {
9696
return ChainstateLoadingError::ERROR_CHAINSTATE_UPGRADE_FAILED;
9797
}
9898

src/txdb.cpp

Lines changed: 11 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
#include <txdb.h>
77

88
#include <chain.h>
9-
#include <node/ui_interface.h>
109
#include <pow.h>
1110
#include <random.h>
1211
#include <shutdown.h>
@@ -18,7 +17,6 @@
1817
#include <stdint.h>
1918

2019
static constexpr uint8_t DB_COIN{'C'};
21-
static constexpr uint8_t DB_COINS{'c'};
2220
static constexpr uint8_t DB_BLOCK_FILES{'f'};
2321
static constexpr uint8_t DB_BLOCK_INDEX{'b'};
2422

@@ -29,6 +27,7 @@ static constexpr uint8_t DB_REINDEX_FLAG{'R'};
2927
static constexpr uint8_t DB_LAST_BLOCK{'l'};
3028

3129
// Keys used in previous version that might still be found in the DB:
30+
static constexpr uint8_t DB_COINS{'c'};
3231
static constexpr uint8_t DB_TXINDEX_BLOCK{'T'};
3332
// uint8_t DB_TXINDEX{'t'}
3433

@@ -50,6 +49,15 @@ std::optional<bilingual_str> CheckLegacyTxindex(CBlockTreeDB& block_tree_db)
5049
return std::nullopt;
5150
}
5251

52+
bool CCoinsViewDB::NeedsUpgrade()
53+
{
54+
std::unique_ptr<CDBIterator> cursor{m_db->NewIterator()};
55+
// DB_COINS was deprecated in v0.15.0, commit
56+
// 1088b02f0ccd7358d2b7076bb9e122d59d502d02
57+
cursor->Seek(std::make_pair(DB_COINS, uint256{}));
58+
return cursor->Valid();
59+
}
60+
5361
namespace {
5462

5563
struct CoinEntry {
@@ -60,7 +68,7 @@ struct CoinEntry {
6068
SERIALIZE_METHODS(CoinEntry, obj) { READWRITE(obj.key, obj.outpoint->hash, VARINT(obj.outpoint->n)); }
6169
};
6270

63-
}
71+
} // namespace
6472

6573
CCoinsViewDB::CCoinsViewDB(fs::path ldb_path, size_t nCacheSize, bool fMemory, bool fWipe) :
6674
m_db(std::make_unique<CDBWrapper>(ldb_path, nCacheSize, fMemory, fWipe, true)),
@@ -337,125 +345,3 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,
337345

338346
return true;
339347
}
340-
341-
namespace {
342-
343-
//! Legacy class to deserialize pre-pertxout database entries without reindex.
344-
class CCoins
345-
{
346-
public:
347-
//! whether transaction is a coinbase
348-
bool fCoinBase;
349-
350-
//! unspent transaction outputs; spent outputs are .IsNull(); spent outputs at the end of the array are dropped
351-
std::vector<CTxOut> vout;
352-
353-
//! at which height this transaction was included in the active block chain
354-
int nHeight;
355-
356-
//! empty constructor
357-
CCoins() : fCoinBase(false), vout(0), nHeight(0) { }
358-
359-
template<typename Stream>
360-
void Unserialize(Stream &s) {
361-
unsigned int nCode = 0;
362-
// version
363-
unsigned int nVersionDummy;
364-
::Unserialize(s, VARINT(nVersionDummy));
365-
// header code
366-
::Unserialize(s, VARINT(nCode));
367-
fCoinBase = nCode & 1;
368-
std::vector<bool> vAvail(2, false);
369-
vAvail[0] = (nCode & 2) != 0;
370-
vAvail[1] = (nCode & 4) != 0;
371-
unsigned int nMaskCode = (nCode / 8) + ((nCode & 6) != 0 ? 0 : 1);
372-
// spentness bitmask
373-
while (nMaskCode > 0) {
374-
unsigned char chAvail = 0;
375-
::Unserialize(s, chAvail);
376-
for (unsigned int p = 0; p < 8; p++) {
377-
bool f = (chAvail & (1 << p)) != 0;
378-
vAvail.push_back(f);
379-
}
380-
if (chAvail != 0)
381-
nMaskCode--;
382-
}
383-
// txouts themself
384-
vout.assign(vAvail.size(), CTxOut());
385-
for (unsigned int i = 0; i < vAvail.size(); i++) {
386-
if (vAvail[i])
387-
::Unserialize(s, Using<TxOutCompression>(vout[i]));
388-
}
389-
// coinbase height
390-
::Unserialize(s, VARINT_MODE(nHeight, VarIntMode::NONNEGATIVE_SIGNED));
391-
}
392-
};
393-
394-
}
395-
396-
/** Upgrade the database from older formats.
397-
*
398-
* Currently implemented: from the per-tx utxo model (0.8..0.14.x) to per-txout.
399-
*/
400-
bool CCoinsViewDB::Upgrade() {
401-
std::unique_ptr<CDBIterator> pcursor(m_db->NewIterator());
402-
pcursor->Seek(std::make_pair(DB_COINS, uint256()));
403-
if (!pcursor->Valid()) {
404-
return true;
405-
}
406-
407-
int64_t count = 0;
408-
LogPrintf("Upgrading utxo-set database...\n");
409-
LogPrintf("[0%%]..."); /* Continued */
410-
uiInterface.ShowProgress(_("Upgrading UTXO database").translated, 0, true);
411-
size_t batch_size = 1 << 24;
412-
CDBBatch batch(*m_db);
413-
int reportDone = 0;
414-
std::pair<unsigned char, uint256> key;
415-
std::pair<unsigned char, uint256> prev_key = {DB_COINS, uint256()};
416-
while (pcursor->Valid()) {
417-
if (ShutdownRequested()) {
418-
break;
419-
}
420-
if (pcursor->GetKey(key) && key.first == DB_COINS) {
421-
if (count++ % 256 == 0) {
422-
uint32_t high = 0x100 * *key.second.begin() + *(key.second.begin() + 1);
423-
int percentageDone = (int)(high * 100.0 / 65536.0 + 0.5);
424-
uiInterface.ShowProgress(_("Upgrading UTXO database").translated, percentageDone, true);
425-
if (reportDone < percentageDone/10) {
426-
// report max. every 10% step
427-
LogPrintf("[%d%%]...", percentageDone); /* Continued */
428-
reportDone = percentageDone/10;
429-
}
430-
}
431-
CCoins old_coins;
432-
if (!pcursor->GetValue(old_coins)) {
433-
return error("%s: cannot parse CCoins record", __func__);
434-
}
435-
COutPoint outpoint(key.second, 0);
436-
for (size_t i = 0; i < old_coins.vout.size(); ++i) {
437-
if (!old_coins.vout[i].IsNull() && !old_coins.vout[i].scriptPubKey.IsUnspendable()) {
438-
Coin newcoin(std::move(old_coins.vout[i]), old_coins.nHeight, old_coins.fCoinBase);
439-
outpoint.n = i;
440-
CoinEntry entry(&outpoint);
441-
batch.Write(entry, newcoin);
442-
}
443-
}
444-
batch.Erase(key);
445-
if (batch.SizeEstimate() > batch_size) {
446-
m_db->WriteBatch(batch);
447-
batch.Clear();
448-
m_db->CompactRange(prev_key, key);
449-
prev_key = key;
450-
}
451-
pcursor->Next();
452-
} else {
453-
break;
454-
}
455-
}
456-
m_db->WriteBatch(batch);
457-
m_db->CompactRange({DB_COINS, uint256()}, key);
458-
uiInterface.ShowProgress("", 100, false);
459-
LogPrintf("[%s].\n", ShutdownRequested() ? "CANCELLED" : "DONE");
460-
return !ShutdownRequested();
461-
}

src/txdb.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ class CCoinsViewDB final : public CCoinsView
6565
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override;
6666
std::unique_ptr<CCoinsViewCursor> Cursor() const override;
6767

68-
//! Attempt to update from an older database format. Returns whether an error occurred.
69-
bool Upgrade();
68+
//! Whether an unsupported database format is used.
69+
bool NeedsUpgrade();
7070
size_t EstimateSize() const override;
7171

7272
//! Dynamically alter the underlying leveldb cache size.

test/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ test/functional/test_runner.py --extended
9898
In order to run backwards compatibility tests, download the previous node binaries:
9999

100100
```
101-
test/get_previous_releases.py -b v22.0 v0.21.0 v0.20.1 v0.19.1 v0.18.1 v0.17.2 v0.16.3 v0.15.2
101+
test/get_previous_releases.py -b v22.0 v0.21.0 v0.20.1 v0.19.1 v0.18.1 v0.17.2 v0.16.3 v0.15.2 v0.14.3
102102
```
103103

104104
By default, up to 4 tests will be run in parallel by test_runner. To specify
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2022 The Bitcoin Core developers
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
"""Test that unsupported utxo db causes an init error.
6+
7+
Previous releases are required by this test, see test/README.md.
8+
"""
9+
10+
import shutil
11+
12+
from test_framework.test_framework import BitcoinTestFramework
13+
from test_framework.util import assert_equal
14+
15+
16+
class UnsupportedUtxoDbTest(BitcoinTestFramework):
17+
def set_test_params(self):
18+
self.setup_clean_chain = True
19+
self.num_nodes = 2
20+
21+
def skip_test_if_missing_module(self):
22+
self.skip_if_no_previous_releases()
23+
24+
def setup_network(self):
25+
self.add_nodes(
26+
self.num_nodes,
27+
versions=[
28+
140300, # Last release with previous utxo db format
29+
None, # For MiniWallet, without migration code
30+
],
31+
)
32+
33+
def run_test(self):
34+
self.log.info("Create previous version (v0.14.3) utxo db")
35+
self.start_node(0)
36+
block = self.generate(self.nodes[0], 1, sync_fun=self.no_op)[-1]
37+
assert_equal(self.nodes[0].getbestblockhash(), block)
38+
assert_equal(self.nodes[0].gettxoutsetinfo()["total_amount"], 50)
39+
self.stop_nodes()
40+
41+
self.log.info("Check init error")
42+
legacy_utxos_dir = self.nodes[0].chain_path / "chainstate"
43+
legacy_blocks_dir = self.nodes[0].chain_path / "blocks"
44+
recent_utxos_dir = self.nodes[1].chain_path / "chainstate"
45+
recent_blocks_dir = self.nodes[1].chain_path / "blocks"
46+
shutil.copytree(legacy_utxos_dir, recent_utxos_dir)
47+
shutil.copytree(legacy_blocks_dir, recent_blocks_dir)
48+
self.nodes[1].assert_start_raises_init_error(
49+
expected_msg="Error: Unsupported chainstate database format found. "
50+
"Please restart with -reindex-chainstate. "
51+
"This will rebuild the chainstate database.",
52+
)
53+
54+
self.log.info("Drop legacy utxo db")
55+
self.start_node(1, extra_args=["-reindex-chainstate"])
56+
assert_equal(self.nodes[1].getbestblockhash(), block)
57+
assert_equal(self.nodes[1].gettxoutsetinfo()["total_amount"], 50)
58+
59+
60+
if __name__ == "__main__":
61+
UnsupportedUtxoDbTest().main()

test/functional/test_runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@
306306
'p2p_ping.py',
307307
'rpc_scantxoutset.py',
308308
'feature_txindex_compatibility.py',
309+
'feature_unsupported_utxo_db.py',
309310
'feature_logging.py',
310311
'feature_anchors.py',
311312
'feature_coinstatsindex.py',

test/get_previous_releases.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,12 @@
1919
import sys
2020
import hashlib
2121

22-
2322
SHA256_SUMS = {
23+
"0e2819135366f150d9906e294b61dff58fd1996ebd26c2f8e979d6c0b7a79580": "bitcoin-0.14.3-aarch64-linux-gnu.tar.gz",
24+
"d86fc90824a85c38b25c8488115178d5785dbc975f5ff674f9f5716bc8ad6e65": "bitcoin-0.14.3-arm-linux-gnueabihf.tar.gz",
25+
"1b0a7408c050e3d09a8be8e21e183ef7ee570385dc41216698cc3ab392a484e7": "bitcoin-0.14.3-osx64.tar.gz",
26+
"706e0472dbc933ed2757650d54cbcd780fd3829ebf8f609b32780c7eedebdbc9": "bitcoin-0.14.3-x86_64-linux-gnu.tar.gz",
27+
#
2428
"d40f18b4e43c6e6370ef7db9131f584fbb137276ec2e3dba67a4b267f81cb644": "bitcoin-0.15.2-aarch64-linux-gnu.tar.gz",
2529
"54fb877a148a6ad189a1e1ab1ff8b11181e58ff2aaf430da55b3fd46ae549a6b": "bitcoin-0.15.2-arm-linux-gnueabihf.tar.gz",
2630
"87e9340ff3d382d543b2b69112376077f0c8b4f7450d372e83b68f5a1e22b2df": "bitcoin-0.15.2-osx64.tar.gz",

0 commit comments

Comments
 (0)