Merge bitcoin/bitcoin#30469: index: Fix coinstats overflow

c767974811 clang-tidy: Fix critical warnings (Fabian Jahr)
54dc34ec22 index: Remove unused coinstatsindex recovery code (Fabian Jahr)
37c4fba1f4 index: Check BIP30 blocks when rewinding Coinstatsindex (Fabian Jahr)
51df9de8e5 doc: Add release note for 30469 (Fabian Jahr)
bb8d673183 test: Add coinstatsindex compatibility test (Fabian Jahr)
b2e8b64ddc index, refactor: Append blocks to coinstatsindex without db read (Fabian Jahr)
431a076ae6 index: Fix coinstatsindex overflow issue (Fabian Jahr)
84e813a02b index, refactor: DRY coinbase check (Fabian Jahr)
fab842b324 index, refactor: Rename ReverseBlock to RevertBlock (Fabian Jahr)

Pull request description:

  Closes https://github.com/bitcoin/bitcoin/issues/26362

  This continues the work that was started with #26426. It fixes the overflow issue by switching the tracked values that are in danger of overflowing from `CAmount` to `arith_uint256`.

  The current approach opts for a simple solution to ensure compatibility with datadirs including the previous version of the index: The new version of the index goes into a separate location in the datadir (`index/coinstatsindex/` rather than `index/coinstats/` before, the new naming is more consistent with the naming of the other indexes). There is no explicit concept of versioning of the index which earlier versions of this PR had. Having the two different versions of the index in separate places allows for downgrading of the node without having to rebuild the index. However, there will be a warning printed in the logs if the new code (v30) detects the old index still being present. A future version could delete a left-over legacy index automatically.

  The PR also includes several minor improvements but most notably it lets new entries be calculated and stored without needing to read any DB records.

ACKs for top commit:
  achow101:
    ACK c767974811
  TheCharlatan:
    ACK c767974811
  mzumsande:
    Tested / Code Review ACK c767974811

Tree-SHA512: 3fa4a19dd1a01c1b01390247bc9daa6871eece7c1899eac976e0cc21ede09c79c65f758d14daafc46a43c4ddd7055c85fb28ff03029132d48936b248639c6ab9
This commit is contained in:
Ava Chow
2025-09-08 17:06:30 -07:00
10 changed files with 200 additions and 126 deletions

View File

@@ -0,0 +1,64 @@
#!/usr/bin/env python3
# Copyright (c) 2025 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test coinstatsindex across node versions.
This test may be removed some time after v29 has reached end of life.
"""
import shutil
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
class CoinStatsIndexTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
self.supports_cli = False
self.extra_args = [["-coinstatsindex"],["-coinstatsindex"]]
def skip_test_if_missing_module(self):
self.skip_if_no_previous_releases()
def setup_nodes(self):
self.add_nodes(
self.num_nodes,
extra_args=self.extra_args,
versions=[
None,
280200,
],
)
self.start_nodes()
def run_test(self):
self._test_coin_stats_index_compatibility()
def _test_coin_stats_index_compatibility(self):
node = self.nodes[0]
legacy_node = self.nodes[1]
for n in self.nodes:
self.wait_until(lambda: n.getindexinfo()['coinstatsindex']['synced'] is True)
self.log.info("Test that gettxoutsetinfo() output is consistent between the different index versions")
res0 = node.gettxoutsetinfo('muhash')
res1 = legacy_node.gettxoutsetinfo('muhash')
assert_equal(res1, res0)
self.log.info("Test that gettxoutsetinfo() output is consistent for the new index running on a datadir with the old version")
self.stop_nodes()
shutil.rmtree(node.chain_path / "indexes" / "coinstatsindex")
shutil.copytree(legacy_node.chain_path / "indexes" / "coinstats", node.chain_path / "indexes" / "coinstats")
old_version_path = node.chain_path / "indexes" / "coinstats"
msg = f'[warning] Old version of coinstatsindex found at {old_version_path}. This folder can be safely deleted unless you plan to downgrade your node to version 29 or lower.'
with node.assert_debug_log(expected_msgs=[msg]):
self.start_node(0, ['-coinstatsindex'])
self.wait_until(lambda: node.getindexinfo()['coinstatsindex']['synced'] is True)
res2 = node.gettxoutsetinfo('muhash')
assert_equal(res2, res0)
if __name__ == '__main__':
CoinStatsIndexTest(__file__).main()

View File

@@ -128,7 +128,7 @@ class InitTest(BitcoinTestFramework):
'startup_args': ['-txindex=1'],
},
# Removing these files does not result in a startup error:
# 'indexes/blockfilter/basic/*.dat', 'indexes/blockfilter/basic/db/*.*', 'indexes/coinstats/db/*.*',
# 'indexes/blockfilter/basic/*.dat', 'indexes/blockfilter/basic/db/*.*', 'indexes/coinstatsindex/db/*.*',
# 'indexes/txindex/*.log', 'indexes/txindex/CURRENT', 'indexes/txindex/LOCK'
]
@@ -154,7 +154,7 @@ class InitTest(BitcoinTestFramework):
'startup_args': ['-blockfilterindex=1'],
},
{
'filepath_glob': 'indexes/coinstats/db/*.*',
'filepath_glob': 'indexes/coinstatsindex/db/*.*',
'error_message': 'LevelDB error: Corruption',
'startup_args': ['-coinstatsindex=1'],
},

View File

@@ -343,6 +343,7 @@ BASE_SCRIPTS = [
'feature_anchors.py',
'mempool_datacarrier.py',
'feature_coinstatsindex.py',
'feature_coinstatsindex_compatibility.py',
'wallet_orphanedreward.py',
'wallet_timelock.py',
'p2p_permissions.py',