From e8f8b74a46aa075bf6c74c104fd572cc89d3b53b Mon Sep 17 00:00:00 2001 From: furszy Date: Fri, 20 Feb 2026 14:20:43 -0500 Subject: [PATCH] test: index, improve txospenderindex_initial_sync() test code The index is now initialized after the setup phase (chain generation and txs creation), since it doesn't participate on it at all. This improves readability and splits setup from what we actually want to check. This also adds a check after Sync() to verify the index best block hash matches the tip, so we know it fully synced before checking the processed data. This will help catching errors as Sync() could have aborted prematurely. As a happy side effect, the SyncWithValidationInterfaceQueue() call at the end of the test is no longer needed and has been removed. --- src/test/txospenderindex_tests.cpp | 60 +++++++++++++++--------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/src/test/txospenderindex_tests.cpp b/src/test/txospenderindex_tests.cpp index 889ef7ff8a4..c54370a5f87 100644 --- a/src/test/txospenderindex_tests.cpp +++ b/src/test/txospenderindex_tests.cpp @@ -2,10 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include #include #include -#include #include #include @@ -14,63 +12,63 @@ BOOST_AUTO_TEST_SUITE(txospenderindex_tests) BOOST_FIXTURE_TEST_CASE(txospenderindex_initial_sync, TestChain100Setup) { - TxoSpenderIndex txospenderindex(interfaces::MakeChain(m_node), 1 << 20, true); - BOOST_REQUIRE(txospenderindex.Init()); - + // Setup phase: // Mine blocks for coinbase maturity, so we can spend some coinbase outputs in the test. - for (int i = 0; i < 50; i++) { - std::vector no_txns; - CreateAndProcessBlock(no_txns, this->m_coinbase_txns[i]->vout[0].scriptPubKey); - } + const CScript& coinbase_script = m_coinbase_txns[0]->vout[0].scriptPubKey; + for (int i = 0; i < 10; i++) CreateAndProcessBlock({}, coinbase_script); + + // Spend 10 outputs std::vector spent(10); std::vector spender(spent.size()); - for (size_t i = 0; i < spent.size(); i++) { - spent[i] = COutPoint(this->m_coinbase_txns[i]->GetHash(), 0); + // Outpoint + auto coinbase_tx = m_coinbase_txns[i]; + spent[i] = COutPoint(coinbase_tx->GetHash(), 0); + + // Spending tx spender[i].version = 1; spender[i].vin.resize(1); spender[i].vin[0].prevout.hash = spent[i].hash; spender[i].vin[0].prevout.n = spent[i].n; spender[i].vout.resize(1); - spender[i].vout[0].nValue = this->m_coinbase_txns[i]->GetValueOut(); - spender[i].vout[0].scriptPubKey = this->m_coinbase_txns[i]->vout[0].scriptPubKey; + spender[i].vout[0].nValue = coinbase_tx->GetValueOut(); + spender[i].vout[0].scriptPubKey = coinbase_script; - // Sign: + // Sign std::vector vchSig; - const uint256 hash = SignatureHash(this->m_coinbase_txns[i]->vout[0].scriptPubKey, spender[i], 0, SIGHASH_ALL, 0, SigVersion::BASE); - coinbaseKey.Sign(hash, vchSig); + const uint256 hash = SignatureHash(coinbase_script, spender[i], 0, SIGHASH_ALL, 0, SigVersion::BASE); + BOOST_REQUIRE(coinbaseKey.Sign(hash, vchSig)); vchSig.push_back((unsigned char)SIGHASH_ALL); spender[i].vin[0].scriptSig << vchSig; } - CBlock block = CreateAndProcessBlock(spender, this->m_coinbase_txns[0]->vout[0].scriptPubKey); + // Generate and ensure block has been fully processed + const uint256 tip_hash = CreateAndProcessBlock(spender, coinbase_script).GetHash(); + m_node.validation_signals->SyncWithValidationInterfaceQueue(); + BOOST_CHECK_EQUAL(WITH_LOCK(::cs_main, return m_node.chainman->ActiveTip()->GetBlockHash()), tip_hash); - // Transaction should not be found in the index before it is started. + // Now we concluded the setup phase, run index + TxoSpenderIndex txospenderindex(interfaces::MakeChain(m_node), 1 << 20, true); + BOOST_REQUIRE(txospenderindex.Init()); + BOOST_CHECK(!txospenderindex.BlockUntilSyncedToCurrentChain()); // false when not synced + BOOST_CHECK_NE(txospenderindex.GetSummary().best_block_hash, tip_hash); + + // Transaction should not be found in the index before it is synced. for (const auto& outpoint : spent) { BOOST_CHECK(!txospenderindex.FindSpender(outpoint).value()); } - // BlockUntilSyncedToCurrentChain should return false before txospenderindex is started. - BOOST_CHECK(!txospenderindex.BlockUntilSyncedToCurrentChain()); - txospenderindex.Sync(); + BOOST_CHECK_EQUAL(txospenderindex.GetSummary().best_block_hash, tip_hash); + for (size_t i = 0; i < spent.size(); i++) { const auto tx_spender{txospenderindex.FindSpender(spent[i])}; BOOST_REQUIRE(tx_spender.has_value()); BOOST_REQUIRE(tx_spender->has_value()); BOOST_CHECK_EQUAL((*tx_spender)->tx->GetHash(), spender[i].GetHash()); - BOOST_CHECK_EQUAL((*tx_spender)->block_hash, block.GetHash()); + BOOST_CHECK_EQUAL((*tx_spender)->block_hash, tip_hash); } - // It is not safe to stop and destroy the index until it finishes handling - // the last BlockConnected notification. The BlockUntilSyncedToCurrentChain() - // call above is sufficient to ensure this, but the - // SyncWithValidationInterfaceQueue() call below is also needed to ensure - // TSAN always sees the test thread waiting for the notification thread, and - // avoid potential false positive reports. - m_node.validation_signals->SyncWithValidationInterfaceQueue(); - - // shutdown sequence (c.f. Shutdown() in init.cpp) txospenderindex.Stop(); }