693dfa6 tests: cover txindex hash prefix collisions and legacy fallback:
The tests are quite a mouthful, maybe we can simplify them a bit with stuff like:
BOOST_CHECK(Assert(tx_disk)->GetHash() == target_txid);
(note that we weren't requiring this after every FindTx call, this would also unify that)
<details><summary>simplify txindex result check</summary>
diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp
index f1728b8d54..ec54524968 100644
--- a/src/test/txindex_tests.cpp
+++ b/src/test/txindex_tests.cpp
@@ -180,8 +180,7 @@ BOOST_FIXTURE_TEST_CASE(txindex_collision_scan_path, TestChain100Setup)
CTransactionRef tx_disk;
uint256 block_hash;
BOOST_REQUIRE(txindex.FindTx(target_txid, block_hash, tx_disk));
- BOOST_REQUIRE(tx_disk);
- BOOST_CHECK(tx_disk->GetHash() == target_txid);
+ BOOST_CHECK(Assert(tx_disk)->GetHash() == target_txid);
// A database created fresh by this version cannot contain legacy entries, so
// lookups skip the legacy fallback: drop the last coinbase's hashed entry and
@@ -226,8 +225,7 @@ BOOST_FIXTURE_TEST_CASE(txindex_legacy_fallback, TestChain100Setup)
CTransactionRef tx_disk;
uint256 block_hash;
BOOST_REQUIRE(txindex.FindTx(legacy_txid, block_hash, tx_disk));
- BOOST_REQUIRE(tx_disk);
- BOOST_CHECK(tx_disk->GetHash() == legacy_txid);
+ BOOST_CHECK(Assert(tx_disk)->GetHash() == legacy_txid);
txindex.Stop();
}
@@ -256,7 +254,7 @@ BOOST_FIXTURE_TEST_CASE(txindex_reorg_keeps_stale_entries, TestChain100Setup)
CTransactionRef tx_disk;
uint256 block_hash;
BOOST_REQUIRE(txindex.FindTx(unique_txid, block_hash, tx_disk));
- BOOST_CHECK(tx_disk->GetHash() == unique_txid);
+ BOOST_CHECK(Assert(tx_disk)->GetHash() == unique_txid);
BOOST_CHECK(block_hash == stale_block_hash);
CDBWrapper& db{TxIndexTest::GetDB(txindex)};
@@ -278,7 +276,7 @@ BOOST_FIXTURE_TEST_CASE(txindex_reorg_keeps_stale_entries, TestChain100Setup)
// The disconnected transaction is still found, in the now-stale block.
BOOST_REQUIRE(txindex.FindTx(unique_txid, block_hash, tx_disk));
- BOOST_CHECK(tx_disk->GetHash() == unique_txid);
+ BOOST_CHECK(Assert(tx_disk)->GetHash() == unique_txid);
BOOST_CHECK(block_hash == stale_block_hash);
{
LOCK(cs_main);
@@ -306,7 +304,7 @@ BOOST_FIXTURE_TEST_CASE(txindex_reorg_keeps_stale_entries, TestChain100Setup)
// The transaction is found in the reconnected (again active) block, and its
// bucket keeps the original position.
BOOST_REQUIRE(txindex.FindTx(unique_txid, block_hash, tx_disk));
- BOOST_CHECK(tx_disk->GetHash() == unique_txid);
+ BOOST_CHECK(Assert(tx_disk)->GetHash() == unique_txid);
BOOST_CHECK(block_hash == stale_block_hash);
BOOST_CHECK(BucketPositions(db, prefix) == original_bucket);
</details>
We could also extract some commonly used primitived here like looking up a tx and invalidating a block.
<details><summary>share txindex test helpers</summary>
diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp
index 213e0da54e..5965e3c72f 100644
--- a/src/test/txindex_tests.cpp
+++ b/src/test/txindex_tests.cpp
@@ -23,6 +23,7 @@
#include <sync.h>
#include <test/util/setup_common.h>
#include <util/byte_units.h>
+#include <util/check.h>
#include <util/strencodings.h>
#include <validation.h>
@@ -79,6 +80,23 @@ FlatFilePos BlockFilePos(const ChainstateManager& chainman, uint32_t height)
return {block_index->nFile, block_index->nDataPos};
}
+uint256 LookupTx(const TxIndex& txindex, const Txid& txid)
+{
+ CTransactionRef tx;
+ uint256 block_hash;
+ BOOST_REQUIRE(txindex.FindTx(txid, block_hash, tx));
+ BOOST_CHECK(Assert(tx)->GetHash() == txid);
+ return block_hash;
+}
+
+void InvalidateBlock(ChainstateManager& chainman, const uint256& block_hash)
+{
+ CBlockIndex* block_index{WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(block_hash))};
+ BOOST_REQUIRE(block_index);
+ BlockValidationState state;
+ BOOST_REQUIRE(chainman.ActiveChainstate().InvalidateBlock(state, block_index));
+}
+
} // namespace
BOOST_AUTO_TEST_CASE(txindex_position_encoding)
@@ -137,11 +155,7 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup)
// Check that txindex has all txs that were in the chain before it started.
for (const auto& txn : m_coinbase_txns) {
- if (!txindex.FindTx(txn->GetHash(), block_hash, tx_disk)) {
- BOOST_ERROR("FindTx failed");
- } else if (tx_disk->GetHash() != txn->GetHash()) {
- BOOST_ERROR("Read incorrect tx");
- }
+ LookupTx(txindex, txn->GetHash());
}
// Check that new transactions in new blocks make it into the index.
@@ -152,11 +166,7 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup)
const CTransaction& txn = *block.vtx[0];
BOOST_CHECK(txindex.BlockUntilSyncedToCurrentChain());
- if (!txindex.FindTx(txn.GetHash(), block_hash, tx_disk)) {
- BOOST_ERROR("FindTx failed");
- } else if (tx_disk->GetHash() != txn.GetHash()) {
- BOOST_ERROR("Read incorrect tx");
- }
+ LookupTx(txindex, txn.GetHash());
}
// shutdown sequence (c.f. Shutdown() in init.cpp)
@@ -217,11 +227,8 @@ BOOST_FIXTURE_TEST_CASE(txindex_collision_scan_path, TestChain100Setup)
BOOST_CHECK(target_bucket[0] == fake_pos);
BOOST_CHECK(target_bucket[1] != fake_pos);
- CTransactionRef tx_disk;
uint256 block_hash;
- BOOST_REQUIRE(txindex.FindTx(target_txid, block_hash, tx_disk));
- BOOST_REQUIRE(tx_disk);
- BOOST_CHECK(tx_disk->GetHash() == target_txid);
+ LookupTx(txindex, target_txid);
// A database created fresh by this version cannot contain legacy entries, so
// lookups skip the legacy fallback: drop the last coinbase's hashed entry and
@@ -263,11 +270,7 @@ BOOST_FIXTURE_TEST_CASE(txindex_legacy_fallback, TestChain100Setup)
BOOST_REQUIRE(!bucket.empty());
for (const auto& pos : bucket) db.Erase(txindex::DBKey{prefix, pos});
- CTransactionRef tx_disk;
- uint256 block_hash;
- BOOST_REQUIRE(txindex.FindTx(legacy_txid, block_hash, tx_disk));
- BOOST_REQUIRE(tx_disk);
- BOOST_CHECK(tx_disk->GetHash() == legacy_txid);
+ LookupTx(txindex, legacy_txid);
txindex.Stop();
}
@@ -293,11 +296,7 @@ BOOST_FIXTURE_TEST_CASE(txindex_reorg_keeps_stale_entries, TestChain100Setup)
const uint256 stale_block_hash{CreateAndProcessBlock({unique_mtx}, coinbase_script).GetHash()};
BOOST_REQUIRE(txindex.BlockUntilSyncedToCurrentChain());
- CTransactionRef tx_disk;
- uint256 block_hash;
- BOOST_REQUIRE(txindex.FindTx(unique_txid, block_hash, tx_disk));
- BOOST_CHECK(tx_disk->GetHash() == unique_txid);
- BOOST_CHECK(block_hash == stale_block_hash);
+ BOOST_CHECK(LookupTx(txindex, unique_txid) == stale_block_hash);
CDBWrapper& db{TxIndexTest::GetDB(txindex)};
const auto prefix{txindex::CreateKeyPrefix(ReadHasher(db), unique_txid)};
@@ -307,22 +306,16 @@ BOOST_FIXTURE_TEST_CASE(txindex_reorg_keeps_stale_entries, TestChain100Setup)
ChainstateManager& chainman{*m_node.chainman};
// Invalidate the block holding the unique transaction, then mine a longer branch.
- {
- CBlockIndex* tip{WITH_LOCK(cs_main, return chainman.ActiveChain().Tip())};
- BlockValidationState state;
- BOOST_REQUIRE(chainman.ActiveChainstate().InvalidateBlock(state, tip));
- }
+ InvalidateBlock(chainman, stale_block_hash);
const uint256 branch_block_hash{CreateAndProcessBlock({}, coinbase_script).GetHash()};
CreateAndProcessBlock({}, coinbase_script);
BOOST_REQUIRE(txindex.BlockUntilSyncedToCurrentChain());
// The disconnected transaction is still found, in the now-stale block.
- BOOST_REQUIRE(txindex.FindTx(unique_txid, block_hash, tx_disk));
- BOOST_CHECK(tx_disk->GetHash() == unique_txid);
- BOOST_CHECK(block_hash == stale_block_hash);
+ BOOST_CHECK(LookupTx(txindex, unique_txid) == stale_block_hash);
{
LOCK(cs_main);
- const CBlockIndex* stale_index{chainman.m_blockman.LookupBlockIndex(block_hash)};
+ const CBlockIndex* stale_index{chainman.m_blockman.LookupBlockIndex(stale_block_hash)};
BOOST_REQUIRE(stale_index);
BOOST_CHECK(!chainman.ActiveChain().Contains(*stale_index));
}
@@ -334,20 +327,15 @@ BOOST_FIXTURE_TEST_CASE(txindex_reorg_keeps_stale_entries, TestChain100Setup)
LOCK(cs_main);
chainman.ActiveChainstate().ResetBlockFailureFlags(chainman.m_blockman.LookupBlockIndex(stale_block_hash));
}
- {
- CBlockIndex* branch_index{WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(branch_block_hash))};
- BlockValidationState state;
- BOOST_REQUIRE(chainman.ActiveChainstate().InvalidateBlock(state, branch_index));
- BOOST_REQUIRE(chainman.ActiveChainstate().ActivateBestChain(state));
- }
+ InvalidateBlock(chainman, branch_block_hash);
+ BlockValidationState state;
+ BOOST_REQUIRE(chainman.ActiveChainstate().ActivateBestChain(state));
BOOST_REQUIRE(txindex.BlockUntilSyncedToCurrentChain());
BOOST_CHECK(WITH_LOCK(cs_main, return chainman.ActiveChain().Tip()->GetBlockHash()) == stale_block_hash);
// The transaction is found in the reconnected (again active) block, and its
// bucket keeps the original position.
- BOOST_REQUIRE(txindex.FindTx(unique_txid, block_hash, tx_disk));
- BOOST_CHECK(tx_disk->GetHash() == unique_txid);
- BOOST_CHECK(block_hash == stale_block_hash);
+ BOOST_CHECK(LookupTx(txindex, unique_txid) == stale_block_hash);
BOOST_CHECK(BucketPositions(db, prefix) == original_bucket);
</details>