LCOV - code coverage report
Current view: top level - src/test - txindex_tests.cpp (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 97.8 % 183 179
Test Date: 2026-08-25 06:16:57 Functions: 100.0 % 23 23
Branches: 50.8 % 1034 525

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2017-present The Bitcoin Core developers
       2                 :             : // Distributed under the MIT software license, see the accompanying
       3                 :             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4                 :             : 
       5                 :             : #include <addresstype.h>
       6                 :             : #include <chain.h>
       7                 :             : #include <chainparams.h>
       8                 :             : #include <common/args.h>
       9                 :             : #include <consensus/amount.h>
      10                 :             : #include <consensus/validation.h>
      11                 :             : #include <crypto/hex_base.h>
      12                 :             : #include <dbwrapper.h>
      13                 :             : #include <flatfile.h>
      14                 :             : #include <index/disktxpos.h>
      15                 :             : #include <index/txindex.h>
      16                 :             : #include <index/txindex_key.h>
      17                 :             : #include <interfaces/chain.h>
      18                 :             : #include <key.h>
      19                 :             : #include <node/blockstorage.h>
      20                 :             : #include <primitives/block.h>
      21                 :             : #include <script/script.h>
      22                 :             : #include <streams.h>
      23                 :             : #include <sync.h>
      24                 :             : #include <test/util/setup_common.h>
      25                 :             : #include <util/byte_units.h>
      26                 :             : #include <util/check.h>
      27                 :             : #include <util/strencodings.h>
      28                 :             : #include <validation.h>
      29                 :             : 
      30                 :             : #include <cstdint>
      31                 :             : #include <memory>
      32                 :             : #include <string>
      33                 :             : #include <string_view>
      34                 :             : #include <utility>
      35                 :             : #include <vector>
      36                 :             : 
      37                 :             : #include <boost/test/unit_test.hpp>
      38                 :             : 
      39                 :             : BOOST_AUTO_TEST_SUITE(txindex_tests)
      40                 :             : 
      41                 :             : // Grants tests access to the otherwise non-public txindex database handle.
      42                 :             : class TxIndexTest
      43                 :             : {
      44                 :             : public:
      45         [ +  - ]:           1 :     static CDBWrapper& GetDB(const TxIndex& txindex) { return txindex.GetDB(); }
      46                 :           2 :     static CBlockLocator ReadBestBlock(const TxIndex& txindex) { return txindex.GetDB().ReadBestBlock(); }
      47                 :           1 :     static void WriteBestBlock(const TxIndex& txindex, const CBlockLocator& locator)
      48                 :             :     {
      49                 :           1 :         auto& db{txindex.GetDB()};
      50                 :           1 :         CDBBatch batch{db};
      51         [ +  - ]:           1 :         db.WriteBestBlock(batch, locator);
      52         [ +  - ]:           1 :         db.WriteBatch(batch);
      53                 :           1 :     }
      54                 :             : };
      55                 :             : 
      56                 :             : namespace {
      57                 :             : 
      58                 :           3 : SipHasher13UJ ReadHasher(const CDBWrapper& db)
      59                 :             : {
      60                 :           3 :     std::pair<uint64_t, uint64_t> salt;
      61   [ +  -  +  - ]:           6 :     BOOST_REQUIRE(db.Read(txindex::DB_TXID_HASH_SALT, salt));
      62                 :           3 :     return SipHasher13UJ{salt.first, salt.second};
      63                 :             : }
      64                 :             : 
      65                 :           5 : std::vector<txindex::BlockTxPosition> BucketPositions(CDBWrapper& db, txindex::TxHashKeyPrefix prefix)
      66                 :             : {
      67                 :           5 :     std::vector<txindex::BlockTxPosition> positions;
      68   [ +  -  +  - ]:           5 :     std::unique_ptr<CDBIterator> it{db.NewIterator()};
      69                 :           5 :     txindex::DBKey key{prefix, {}};
      70   [ +  -  +  -  :          24 :     for (it->Seek(key); it->Valid() && it->GetKey(key) && key.hash_prefix == prefix; it->Next()) {
          +  -  +  -  +  
             -  +  -  +  
                      + ]
      71         [ +  - ]:           7 :         positions.push_back(key.pos);
      72                 :             :     }
      73                 :           5 :     return positions;
      74                 :           5 : }
      75                 :             : 
      76                 :           2 : FlatFilePos BlockFilePos(const ChainstateManager& chainman, uint32_t height)
      77                 :             : {
      78                 :           2 :     LOCK(cs_main);
      79   [ +  -  +  - ]:           2 :     const CBlockIndex* block_index{chainman.ActiveChain()[height]};
      80   [ +  -  +  -  :           4 :     BOOST_REQUIRE(block_index);
                   +  - ]
      81         [ +  - ]:           2 :     return {block_index->nFile, block_index->nDataPos};
      82                 :           2 : }
      83                 :             : 
      84                 :         116 : uint256 LookupTx(const TxIndex& txindex, const Txid& txid)
      85                 :             : {
      86                 :         116 :     const auto result{txindex.FindTx(txid)};
      87   [ +  -  +  -  :         232 :     BOOST_REQUIRE(result);
                   +  - ]
      88   [ +  -  -  +  :         232 :     BOOST_CHECK(result->tx->GetHash() == txid);
                   +  - ]
      89                 :         116 :     return result->block_hash;
      90                 :         116 : }
      91                 :             : 
      92                 :           2 : void InvalidateBlock(ChainstateManager& chainman, const uint256& block_hash)
      93                 :             : {
      94   [ +  -  +  - ]:           6 :     CBlockIndex* block_index{WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(block_hash))};
      95   [ +  -  +  - ]:           4 :     BOOST_REQUIRE(block_index);
      96         [ +  - ]:           2 :     BlockValidationState state;
      97   [ +  -  +  -  :           4 :     BOOST_REQUIRE(chainman.ActiveChainstate().InvalidateBlock(state, block_index));
             +  -  +  - ]
      98                 :           2 : }
      99                 :             : 
     100                 :             : } // namespace
     101                 :             : 
     102   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(txindex_position_encoding)
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  -  +  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          -  +  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
     103                 :             : {
     104                 :           1 :     constexpr struct { txindex::BlockTxPosition position; std::string_view encoded; } test_vectors[]{
     105                 :             :         {{0, 0}, "00000000"},
     106                 :             :         {{1, 2}, "01000002"},
     107                 :             :         {{10'000'000, 123}, "83e1ac0000007b"},
     108                 :             :         {{456, 3'999'999}, "82483d08ff"},
     109                 :             :     };
     110                 :             : 
     111         [ +  + ]:           5 :     for (const auto& [position, encoded] : test_vectors) {
     112   [ +  -  -  +  :           4 :         BOOST_CHECK_EQUAL(HexStr(DataStream{} << position), encoded);
             +  -  +  - ]
     113                 :             : 
     114                 :           4 :         txindex::BlockTxPosition decoded;
     115   [ +  -  +  -  :          16 :         BOOST_CHECK((DataStream{ParseHex(encoded)} >> decoded).empty());
             -  +  +  - ]
     116   [ +  -  +  - ]:          12 :         BOOST_CHECK(decoded == position);
     117                 :             :     }
     118                 :             : 
     119                 :             :     // Pin the full key encodings, including the type prefixes.
     120   [ +  -  -  +  :           1 :     BOOST_CHECK_EQUAL(HexStr(DataStream{} << txindex::BlockSeqKey{1}), "7301");
             +  -  +  - ]
     121   [ +  -  -  +  :           1 :     BOOST_CHECK_EQUAL(HexStr(DataStream{} << txindex::DBKey{0x0102030405, {1, 2}}),
             +  -  +  - ]
     122                 :             :                       "78010203040501000002");
     123                 :             : 
     124         [ +  - ]:           1 :     BOOST_CHECK_EQUAL(txindex::BLOCK_HEADER_SIZE, GetSerializeSize(CBlockHeader{}));
     125                 :           1 : }
     126                 :             : 
     127   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(txindex_hash_prefix)
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  -  +  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          -  +  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
     128                 :             : {
     129         [ +  - ]:           1 :     BOOST_CHECK_EQUAL(
     130                 :             :         txindex::CreateKeyPrefix(
     131                 :             :             SipHasher13UJ{0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL},
     132                 :             :             Txid{"1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100"}),
     133                 :             :         0xc67d87b08cULL);
     134                 :           1 : }
     135                 :             : 
     136   [ +  -  +  -  :           7 : BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup)
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
     137                 :             : {
     138         [ +  - ]:           1 :     TxIndex txindex(interfaces::MakeChain(m_node), /*n_cache_size=*/1_MiB, /*f_memory=*/true);
     139   [ +  -  +  -  :           2 :     BOOST_REQUIRE(txindex.Init());
                   +  - ]
     140                 :             : 
     141                 :             :     // Transaction should not be found in the index before it is started.
     142         [ +  + ]:         101 :     for (const auto& txn : m_coinbase_txns) {
     143   [ +  -  +  -  :         200 :         BOOST_CHECK(!txindex.FindTx(txn->GetHash()));
                   +  - ]
     144                 :             :     }
     145                 :             : 
     146                 :             :     // BlockUntilSyncedToCurrentChain should return false before txindex is started.
     147   [ +  -  +  -  :           2 :     BOOST_CHECK(!txindex.BlockUntilSyncedToCurrentChain());
             +  -  +  - ]
     148                 :             : 
     149         [ +  - ]:           1 :     txindex.Sync();
     150                 :             : 
     151                 :             :     // Check that txindex excludes genesis block transactions.
     152         [ +  - ]:           1 :     const CBlock& genesis_block = Params().GenesisBlock();
     153         [ +  + ]:           2 :     for (const auto& txn : genesis_block.vtx) {
     154   [ +  -  +  -  :           2 :         BOOST_CHECK(!txindex.FindTx(txn->GetHash()));
                   +  - ]
     155                 :             :     }
     156                 :             : 
     157                 :             :     // Check that txindex has all txs that were in the chain before it started.
     158         [ +  + ]:         101 :     for (const auto& txn : m_coinbase_txns) {
     159         [ +  - ]:         100 :         LookupTx(txindex, txn->GetHash());
     160                 :             :     }
     161                 :             : 
     162                 :             :     // Check that new transactions in new blocks make it into the index.
     163         [ +  + ]:          11 :     for (int i = 0; i < 10; i++) {
     164   [ +  -  +  -  :          10 :         CScript coinbase_script_pub_key = GetScriptForDestination(PKHash(coinbaseKey.GetPubKey()));
                   +  - ]
     165                 :          10 :         std::vector<CMutableTransaction> no_txns;
     166         [ +  - ]:          10 :         const CBlock& block = CreateAndProcessBlock(no_txns, coinbase_script_pub_key);
     167         [ +  - ]:          10 :         const CTransaction& txn = *block.vtx[0];
     168                 :             : 
     169   [ +  -  +  -  :          20 :         BOOST_CHECK(txindex.BlockUntilSyncedToCurrentChain());
             +  -  +  - ]
     170         [ +  - ]:          10 :         LookupTx(txindex, txn.GetHash());
     171                 :          10 :     }
     172                 :             : 
     173                 :             :     // shutdown sequence (c.f. Shutdown() in init.cpp)
     174         [ +  - ]:           1 :     txindex.Stop();
     175                 :           1 : }
     176                 :             : 
     177   [ +  -  +  -  :           7 : BOOST_FIXTURE_TEST_CASE(txindex_collision_scan_path, TestChain100Setup)
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
     178                 :             : {
     179                 :             :     // On-disk, so the legacy-entry probe at construction runs against a fresh
     180                 :             :     // database, as it would on a node whose index was created by this version.
     181         [ +  - ]:           1 :     TxIndex txindex(interfaces::MakeChain(m_node), /*n_cache_size=*/1_MiB, /*f_memory=*/false);
     182   [ +  -  +  -  :           2 :     BOOST_REQUIRE(txindex.Init());
             +  -  +  - ]
     183         [ +  - ]:           1 :     txindex.Sync();
     184                 :             : 
     185         [ +  - ]:           1 :     CDBWrapper& db{TxIndexTest::GetDB(txindex)};
     186         [ +  - ]:           1 :     const SipHasher13UJ hasher{ReadHasher(db)};
     187                 :             : 
     188                 :             :     // Lookups scan candidates in descending sequence order, so entries of
     189                 :             :     // later-connected blocks are tried first. Forge a colliding entry under the
     190                 :             :     // first coinbase's prefix pointing at the last coinbase, so looking up the
     191                 :             :     // first tx must scan that false positive first.
     192         [ +  - ]:           1 :     const Txid fake_txid{m_coinbase_txns.back()->GetHash()};
     193         [ +  - ]:           1 :     const Txid target_txid{m_coinbase_txns.front()->GetHash()};
     194                 :           1 :     const auto fake_prefix{txindex::CreateKeyPrefix(hasher, fake_txid)};
     195                 :           1 :     const auto target_prefix{txindex::CreateKeyPrefix(hasher, target_txid)};
     196                 :             :     // Distinct prefixes guarantee the target's bucket initially holds only the target.
     197   [ +  -  +  -  :           2 :     BOOST_REQUIRE(fake_prefix != target_prefix);
                   +  - ]
     198                 :             : 
     199                 :             :     // Read the last coinbase's encoded position straight from its bucket.
     200         [ +  - ]:           1 :     const auto fake_bucket{BucketPositions(db, fake_prefix)};
     201   [ +  -  -  +  :           1 :     BOOST_REQUIRE_EQUAL(fake_bucket.size(), 1U);
                   +  - ]
     202         [ +  - ]:           1 :     const txindex::BlockTxPosition fake_pos{fake_bucket.front()};
     203                 :             : 
     204         [ +  - ]:           1 :     db.Write(txindex::DBKey{target_prefix, fake_pos}, txindex::EMPTY_VALUE);
     205                 :             : 
     206                 :             :     // The target's bucket now holds the real target first (lower sequence
     207                 :             :     // number), then the forged false positive, which the descending scan tries first.
     208         [ +  - ]:           1 :     const auto target_bucket{BucketPositions(db, target_prefix)};
     209   [ +  -  -  +  :           1 :     BOOST_REQUIRE_EQUAL(target_bucket.size(), 2U);
                   +  - ]
     210   [ +  -  -  +  :           2 :     BOOST_CHECK(target_bucket[0] != fake_pos);
             +  -  +  - ]
     211   [ +  -  +  -  :           3 :     BOOST_CHECK(target_bucket[1] == fake_pos);
             +  -  +  - ]
     212                 :             : 
     213         [ +  - ]:           1 :     LookupTx(txindex, target_txid);
     214                 :             : 
     215                 :             :     // A database created fresh by this version cannot contain legacy entries, so
     216                 :             :     // lookups skip the legacy fallback: drop the last coinbase's hashed entry and
     217                 :             :     // re-add it under the old 't' + txid schema (a physical CDiskTxPos), then
     218                 :             :     // confirm the lookup misses even though the legacy row exists.
     219                 :             :     // BlockTxPosition offsets are from the block start (header included), while
     220                 :             :     // the legacy CDiskTxPos.nTxOffset is measured after the header.
     221   [ +  -  +  - ]:           1 :     const CDiskTxPos fake_physical{BlockFilePos(*m_node.chainman, fake_pos.block_seq + 1), fake_pos.tx_offset_in_block - txindex::BLOCK_HEADER_SIZE};
     222         [ +  - ]:           1 :     db.Erase(txindex::DBKey{fake_prefix, fake_pos});
     223         [ +  - ]:           1 :     db.Write(txindex::LegacyTxKey(fake_txid), fake_physical);
     224   [ +  -  +  -  :           2 :     BOOST_CHECK(!txindex.FindTx(fake_txid));
             +  -  +  - ]
     225                 :             : 
     226         [ +  - ]:           1 :     txindex.Stop();
     227                 :           1 : }
     228                 :             : 
     229   [ +  -  +  -  :           7 : BOOST_FIXTURE_TEST_CASE(txindex_legacy_fallback, TestChain100Setup)
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
     230                 :             : {
     231                 :             :     // Seed the on-disk database with a legacy ('t' + txid) entry before the index
     232                 :             :     // is opened, as if it had been written by a pre-hashing version.
     233                 :           1 :     const Txid legacy_txid{m_coinbase_txns.front()->GetHash()};
     234                 :             :     // The block at height 1 holds only the coinbase, so the tx starts right after
     235                 :             :     // the header and the 1-byte tx count.
     236                 :           1 :     const CDiskTxPos legacy_pos{BlockFilePos(*m_node.chainman, 1), 1};
     237                 :           1 :     {
     238   [ +  -  +  -  :           5 :         CDBWrapper db{DBParams{.path = gArgs.GetDataDirNet() / "indexes" / "txindex", .cache_bytes = 1_MiB}};
                   +  - ]
     239         [ +  - ]:           1 :         db.Write(txindex::LegacyTxKey(legacy_txid), legacy_pos);
     240                 :           1 :     }
     241                 :             : 
     242         [ +  - ]:           1 :     TxIndex txindex(interfaces::MakeChain(m_node), /*n_cache_size=*/1_MiB, /*f_memory=*/false);
     243   [ +  -  +  -  :           2 :     BOOST_REQUIRE(txindex.Init());
             +  -  +  - ]
     244         [ +  - ]:           1 :     txindex.Sync();
     245                 :             : 
     246                 :             :     // Drop the hashed entries so only the legacy row remains, then confirm the
     247                 :             :     // lookup succeeds through the fallback.
     248         [ +  - ]:           1 :     CDBWrapper& db{TxIndexTest::GetDB(txindex)};
     249         [ +  - ]:           1 :     const auto prefix{txindex::CreateKeyPrefix(ReadHasher(db), legacy_txid)};
     250         [ +  - ]:           1 :     const auto bucket{BucketPositions(db, prefix)};
     251   [ +  -  +  - ]:           2 :     BOOST_REQUIRE(!bucket.empty());
     252   [ +  -  +  + ]:           2 :     for (const auto& pos : bucket) db.Erase(txindex::DBKey{prefix, pos});
     253                 :             : 
     254         [ +  - ]:           1 :     LookupTx(txindex, legacy_txid);
     255                 :             : 
     256         [ +  - ]:           1 :     txindex.Stop();
     257                 :           1 : }
     258                 :             : 
     259   [ +  -  +  -  :           7 : BOOST_FIXTURE_TEST_CASE(txindex_locator_upgrade, TestChain100Setup)
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
     260                 :             : {
     261                 :           1 :     uint256 legacy_hash, new_hash;
     262                 :           1 :     {
     263                 :           1 :         LOCK(cs_main);
     264   [ +  -  -  +  :           2 :         legacy_hash = Assert(m_node.chainman->ActiveChain()[1])->GetBlockHash();
                   -  + ]
     265   [ +  -  -  +  :           2 :         new_hash = Assert(m_node.chainman->ActiveChain().Tip())->GetBlockHash();
             -  +  +  - ]
     266                 :           0 :     }
     267   [ +  -  +  - ]:           1 :     CBlockLocator legacy_locator{{legacy_hash}}, new_locator{{new_hash}};
     268   [ +  -  +  -  :           5 :     { CDBWrapper{DBParams{.path = gArgs.GetDataDirNet() / "indexes" / "txindex", .cache_bytes = 1_MiB}}.Write(uint8_t{'B'}, legacy_locator); }
             +  -  +  - ]
     269                 :             : 
     270   [ +  -  +  - ]:           1 :     TxIndex txindex(interfaces::MakeChain(m_node), /*n_cache_size=*/1_MiB, /*f_memory=*/false);
     271   [ +  -  +  -  :           2 :     BOOST_CHECK(TxIndexTest::ReadBestBlock(txindex).vHave == legacy_locator.vHave);
             +  -  +  - ]
     272                 :             : 
     273         [ +  - ]:           1 :     TxIndexTest::WriteBestBlock(txindex, new_locator);
     274   [ +  -  +  -  :           2 :     BOOST_CHECK(TxIndexTest::ReadBestBlock(txindex).vHave == new_locator.vHave);
             +  -  +  - ]
     275                 :             : 
     276                 :           1 :     CBlockLocator stored_legacy_locator;
     277   [ +  -  +  -  :           2 :     BOOST_REQUIRE(TxIndexTest::GetDB(txindex).Read(uint8_t{'B'}, stored_legacy_locator));
          +  -  +  -  +  
                      - ]
     278   [ +  -  +  - ]:           2 :     BOOST_CHECK(stored_legacy_locator.vHave == legacy_locator.vHave);
     279                 :           1 : }
     280                 :             : 
     281   [ +  -  +  -  :           7 : BOOST_FIXTURE_TEST_CASE(txindex_reorg_keeps_stale_entries, TestChain100Setup)
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
     282                 :             : {
     283         [ +  - ]:           1 :     TxIndex txindex(interfaces::MakeChain(m_node), /*n_cache_size=*/1_MiB, /*f_memory=*/true);
     284   [ +  -  +  -  :           2 :     BOOST_REQUIRE(txindex.Init());
             +  -  +  - ]
     285         [ +  - ]:           1 :     txindex.Sync();
     286                 :             : 
     287   [ +  -  +  -  :           2 :     const CScript coinbase_script{CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG};
                   +  - ]
     288                 :             : 
     289                 :             :     // Mine a unique (non-coinbase) transaction into a new block at height 101.
     290         [ +  - ]:           2 :     CMutableTransaction unique_mtx{CreateValidMempoolTransaction(
     291         [ +  - ]:           1 :         /*input_transaction=*/m_coinbase_txns[0],
     292                 :             :         /*input_vout=*/0,
     293                 :             :         /*input_height=*/1,
     294                 :             :         /*input_signing_key=*/coinbaseKey,
     295                 :           1 :         /*output_destination=*/CScript() << OP_TRUE,
     296                 :             :         /*output_amount=*/CAmount{1 * COIN},
     297   [ +  -  +  - ]:           2 :         /*submit=*/false)};
     298   [ +  -  +  - ]:           2 :     const Txid unique_txid{MakeTransactionRef(unique_mtx)->GetHash()};
     299   [ +  -  +  -  :           3 :     const uint256 stale_block_hash{CreateAndProcessBlock({unique_mtx}, coinbase_script).GetHash()};
          +  -  +  +  -  
                      - ]
     300   [ +  -  +  -  :           2 :     BOOST_REQUIRE(txindex.BlockUntilSyncedToCurrentChain());
             +  -  +  - ]
     301                 :             : 
     302   [ +  -  +  -  :           3 :     BOOST_CHECK(LookupTx(txindex, unique_txid) == stale_block_hash);
             +  -  +  - ]
     303                 :             : 
     304         [ +  - ]:           1 :     CDBWrapper& db{TxIndexTest::GetDB(txindex)};
     305         [ +  - ]:           1 :     const auto prefix{txindex::CreateKeyPrefix(ReadHasher(db), unique_txid)};
     306         [ +  - ]:           1 :     const auto original_bucket{BucketPositions(db, prefix)};
     307   [ +  -  -  +  :           1 :     BOOST_REQUIRE_EQUAL(original_bucket.size(), 1U);
                   +  - ]
     308                 :             : 
     309         [ +  - ]:           1 :     ChainstateManager& chainman{*m_node.chainman};
     310                 :             : 
     311                 :             :     // Invalidate the block holding the unique transaction.
     312         [ +  - ]:           1 :     InvalidateBlock(chainman, stale_block_hash);
     313   [ +  -  +  -  :           2 :     BOOST_REQUIRE(txindex.BlockUntilSyncedToCurrentChain());
             +  -  +  - ]
     314                 :             : 
     315                 :             :     // The disconnected transaction is still found, in the now-stale block.
     316   [ +  -  +  -  :           3 :     BOOST_CHECK(LookupTx(txindex, unique_txid) == stale_block_hash);
             +  -  +  - ]
     317                 :           1 :     {
     318         [ +  - ]:           1 :         LOCK(cs_main);
     319         [ +  - ]:           1 :         const CBlockIndex* stale_index{chainman.m_blockman.LookupBlockIndex(stale_block_hash)};
     320   [ +  -  +  -  :           2 :         BOOST_REQUIRE(stale_index);
                   +  - ]
     321   [ +  -  +  -  :           2 :         BOOST_CHECK(!chainman.ActiveChain().Contains(*stale_index));
             +  -  +  - ]
     322                 :           0 :     }
     323                 :             : 
     324                 :             :     // Mine the same transaction into a replacement branch, which gets a later
     325                 :             :     // sequence number. The lookup must now return the branch block in the active chain.
     326   [ +  -  +  -  :           4 :     const uint256 branch_block_hash{CreateAndProcessBlock({unique_mtx}, CScript() << OP_TRUE).GetHash()};
          +  -  +  -  +  
                +  -  - ]
     327         [ +  - ]:           2 :     CreateAndProcessBlock({}, coinbase_script);
     328   [ +  -  +  -  :           2 :     BOOST_REQUIRE(txindex.BlockUntilSyncedToCurrentChain());
             +  -  +  - ]
     329   [ +  -  +  -  :           3 :     BOOST_CHECK(LookupTx(txindex, unique_txid) == branch_block_hash);
             +  -  +  - ]
     330                 :             : 
     331                 :             :     // Reorg back to the original branch. The original branch block must be
     332                 :             :     // now be preferred even though the replacement branch has a later sequence.
     333                 :           1 :     {
     334         [ +  - ]:           1 :         LOCK(cs_main);
     335   [ +  -  +  -  :           1 :         chainman.ActiveChainstate().ResetBlockFailureFlags(chainman.m_blockman.LookupBlockIndex(stale_block_hash));
                   +  - ]
     336                 :           0 :     }
     337         [ +  - ]:           1 :     InvalidateBlock(chainman, branch_block_hash);
     338                 :           1 :     {
     339         [ +  - ]:           1 :         BlockValidationState state;
     340   [ +  -  +  -  :           2 :         BOOST_REQUIRE(chainman.ActiveChainstate().ActivateBestChain(state));
          +  -  +  -  -  
                      + ]
     341                 :           0 :     }
     342   [ +  -  +  -  :           2 :     BOOST_REQUIRE(txindex.BlockUntilSyncedToCurrentChain());
             +  -  +  - ]
     343   [ +  -  +  +  :           6 :     BOOST_CHECK(WITH_LOCK(cs_main, return chainman.ActiveChain().Tip()->GetBlockHash()) == stale_block_hash);
             +  -  +  - ]
     344                 :             : 
     345   [ +  -  +  -  :           3 :     BOOST_CHECK(LookupTx(txindex, unique_txid) == stale_block_hash);
             +  -  +  - ]
     346                 :             : 
     347                 :             :     // Reconnecting the original block must not create duplicate entries.
     348         [ +  - ]:           1 :     const auto reorg_bucket{BucketPositions(db, prefix)};
     349   [ +  -  -  +  :           1 :     BOOST_REQUIRE_EQUAL(reorg_bucket.size(), 2U);
                   +  - ]
     350   [ +  -  +  -  :           3 :     BOOST_CHECK(reorg_bucket.front() == original_bucket.front());
             +  -  +  - ]
     351                 :             : 
     352         [ +  - ]:           1 :     txindex.Stop();
     353   [ +  -  +  - ]:           4 : }
     354                 :             : 
     355                 :             : BOOST_AUTO_TEST_SUITE_END()
        

Generated by: LCOV version 2.0-1