Merge bitcoin/bitcoin#34653: test: improve txospender index tests code

e8f8b74a46 test: index, improve txospenderindex_initial_sync() test code (furszy)
ac3bea07cd test: improve rpc_gettxspendingprevout.py code (furszy)

Pull request description:

  Fixes #34637.

  Was reviewing #34637 and, while reading the new txospender index
  test code for the first time, found it could use some cleanups. Finding
  stuff in there is harder than it should be due to the amount of dup code.

  The first commit cleans up `rpc_gettxspendingprevout.py` by introducing
  helper functions to avoid repeating the same dicts everywhere, using
  for-loops instead of duplicating the same checks for each node, and
  renaming variables to better reflect what they actually represent.

  The second commit reorganizes `txospenderindex_initial_sync()`
  moving index initialization after the test setup phase, since the index
  doesn't participate in it anyway. It adds a post-sync check to catch
  cases where `Sync()` aborted prematurely.

  Note:
  This is just a pre-work for deeper index changes I'm cooking.

ACKs for top commit:
  achow101:
    ACK e8f8b74a46
  sedited:
    Re-ACK e8f8b74a46
  w0xlt:
    reACK e8f8b74a46

Tree-SHA512: 3f7026712ab20a43f376afa28c683dcd5daec8ed1bbf1c36d7ec6bbf231f468d4de74efae4aa8295ff3afb83986286ccaf31c03b34e45fc9971652f064791ed0
This commit is contained in:
Ava Chow
2026-02-24 11:12:23 -08:00
2 changed files with 141 additions and 115 deletions

View File

@@ -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 <chainparams.h>
#include <index/txospenderindex.h>
#include <test/util/setup_common.h>
#include <util/time.h>
#include <validation.h>
#include <boost/test/unit_test.hpp>
@@ -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<CMutableTransaction> 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<COutPoint> spent(10);
std::vector<CMutableTransaction> 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<unsigned char> 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();
}