Skip to content

Commit ef29c8b

Browse files
committed
assumeutxo: Get rid of faked nTx and nChainTx values
The `PopulateAndValidateSnapshot` function introduced in f6e2da5 from #19806 has been setting fake `nTx` and `nChainTx` values that can show up in RPC results (see #29328) and make `CBlockIndex` state hard to reason about, because it is difficult to know whether the values are real or fake. Revert to previous behavior of setting `nTx` and `nChainTx` to 0 when the values are unknown, instead of faking them. This commit fixes at least two assert failures in the (pindex->nChainTx == pindex->nTx + prev_chain_tx) check that would happen previously. Tests for these failures are added separately in the next two commits. Compatibility note: This change could result in -checkblockindex failures if a snapshot was loaded by a previous version of Bitcoin Core and not fully validated, because fake nTx values will have been saved to the block index. It would be pretty easy to avoid these failures by adding some compatibility code to `LoadBlockIndex` and changing `nTx` values from 1 to 0 when they are fake (when `(pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TRANSACTIONS`), but a little simpler not to worry about being compatible in this case.
1 parent 9b97d5b commit ef29c8b

File tree

5 files changed

+118
-87
lines changed

5 files changed

+118
-87
lines changed

src/chain.h

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -98,16 +98,20 @@ enum BlockStatus : uint32_t {
9898

9999
/**
100100
* Only first tx is coinbase, 2 <= coinbase input script length <= 100, transactions valid, no duplicate txids,
101-
* sigops, size, merkle root. Implies all parents are at least TREE but not necessarily TRANSACTIONS. When all
102-
* parent blocks also have TRANSACTIONS, CBlockIndex::nChainTx will be set.
101+
* sigops, size, merkle root. Implies all parents are at least TREE but not necessarily TRANSACTIONS.
102+
*
103+
* If a block's validity is at least VALID_TRANSACTIONS, CBlockIndex::nTx will be set. If a block and all previous
104+
* blocks back to the genesis block or an assumeutxo snapshot block are at least VALID_TRANSACTIONS,
105+
* CBlockIndex::nChainTx will be set.
103106
*/
104107
BLOCK_VALID_TRANSACTIONS = 3,
105108

106109
//! Outputs do not overspend inputs, no double spends, coinbase output ok, no immature coinbase spends, BIP30.
107-
//! Implies all parents are either at least VALID_CHAIN, or are ASSUMED_VALID
110+
//! Implies all previous blocks back to the genesis block or an assumeutxo snapshot block are at least VALID_CHAIN.
108111
BLOCK_VALID_CHAIN = 4,
109112

110-
//! Scripts & signatures ok. Implies all parents are either at least VALID_SCRIPTS, or are ASSUMED_VALID.
113+
//! Scripts & signatures ok. Implies all previous blocks back to the genesis block or an assumeutxo snapshot block
114+
//! are at least VALID_SCRIPTS.
111115
BLOCK_VALID_SCRIPTS = 5,
112116

113117
//! All validity bits.
@@ -173,21 +177,16 @@ class CBlockIndex
173177
//! (memory only) Total amount of work (expected number of hashes) in the chain up to and including this block
174178
arith_uint256 nChainWork{};
175179

176-
//! Number of transactions in this block.
180+
//! Number of transactions in this block. This will be nonzero if the block
181+
//! reached the VALID_TRANSACTIONS level, and zero otherwise.
177182
//! Note: in a potential headers-first mode, this number cannot be relied upon
178-
//! Note: this value is faked during UTXO snapshot load to ensure that
179-
//! LoadBlockIndex() will load index entries for blocks that we lack data for.
180-
//! @sa ActivateSnapshot
181183
unsigned int nTx{0};
182184

183185
//! (memory only) Number of transactions in the chain up to and including this block.
184-
//! This value will be non-zero only if and only if transactions for this block and all its parents are available.
186+
//! This value will be non-zero if this block and all previous blocks back
187+
//! to the genesis block or an assumeutxo snapshot block have reached the
188+
//! VALID_TRANSACTIONS level.
185189
//! Change to 64-bit type before 2024 (assuming worst case of 60 byte transactions).
186-
//!
187-
//! Note: this value is faked during use of a UTXO snapshot because we don't
188-
//! have the underlying block data available during snapshot load.
189-
//! @sa AssumeutxoData
190-
//! @sa ActivateSnapshot
191190
unsigned int nChainTx{0};
192191

193192
//! Verification status of this block. See enum BlockStatus
@@ -262,15 +261,14 @@ class CBlockIndex
262261
}
263262

264263
/**
265-
* Check whether this block's and all previous blocks' transactions have been
266-
* downloaded (and stored to disk) at some point.
264+
* Check whether this block and all previous blocks back to the genesis block or an assumeutxo snapshot block have
265+
* reached VALID_TRANSACTIONS and had transactions downloaded (and stored to disk) at some point.
267266
*
268267
* Does not imply the transactions are consensus-valid (ConnectTip might fail)
269268
* Does not imply the transactions are still stored on disk. (IsBlockPruned might return true)
270269
*
271-
* Note that this will be true for the snapshot base block, if one is loaded (and
272-
* all subsequent assumed-valid blocks) since its nChainTx value will have been set
273-
* manually based on the related AssumeutxoData entry.
270+
* Note that this will be true for the snapshot base block, if one is loaded, since its nChainTx value will have
271+
* been set manually based on the related AssumeutxoData entry.
274272
*/
275273
bool HaveNumChainTxs() const { return nChainTx != 0; }
276274

src/test/util/chainstate.h

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,16 @@ CreateAndActivateUTXOSnapshot(
9191
// these blocks instead
9292
CBlockIndex *pindex = orig_tip;
9393
while (pindex && pindex != chain.m_chain.Tip()) {
94-
pindex->nStatus &= ~BLOCK_HAVE_DATA;
95-
pindex->nStatus &= ~BLOCK_HAVE_UNDO;
96-
// We have to set the ASSUMED_VALID flag, because otherwise it
97-
// would not be possible to have a block index entry without HAVE_DATA
98-
// and with nTx > 0 (since we aren't setting the pruned flag);
99-
// see CheckBlockIndex().
100-
pindex->nStatus |= BLOCK_ASSUMED_VALID;
94+
// Remove all data and validity flags by just setting
95+
// BLOCK_VALID_TREE. Also reset transaction counts and sequence
96+
// ids that are set when blocks are received, to make test setup
97+
// more realistic and satisfy consistency checks in
98+
// CheckBlockIndex().
99+
assert(pindex->IsValid(BlockStatus::BLOCK_VALID_TREE));
100+
pindex->nStatus = BlockStatus::BLOCK_VALID_TREE;
101+
pindex->nTx = 0;
102+
pindex->nChainTx = 0;
103+
pindex->nSequenceId = 0;
101104
pindex = pindex->pprev;
102105
}
103106
}

src/test/validation_chainstatemanager_tests.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,8 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
464464
// Blocks with heights in range [91, 110] are marked ASSUMED_VALID
465465
if (i < last_assumed_valid_idx && i >= assumed_valid_start_idx) {
466466
index->nStatus = BlockStatus::BLOCK_VALID_TREE | BlockStatus::BLOCK_ASSUMED_VALID;
467+
index->nTx = 0;
468+
index->nChainTx = 0;
467469
}
468470

469471
++num_indexes;

0 commit comments

Comments
 (0)