LCOV - code coverage report
Current view: top level - src/index - txindex.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 56 142 39.4 %
Date: 2021-06-29 14:35:33 Functions: 10 11 90.9 %
Legend: Modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed

Not modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed
Branches: 14 46 30.4 %

           Branch data     Line data    Source code
#       1                 :            : // Copyright (c) 2017-2020 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 <index/disktxpos.h>
#       6                 :            : #include <index/txindex.h>
#       7                 :            : #include <node/blockstorage.h>
#       8                 :            : #include <node/ui_interface.h>
#       9                 :            : #include <shutdown.h>
#      10                 :            : #include <util/system.h>
#      11                 :            : #include <util/translation.h>
#      12                 :            : #include <validation.h>
#      13                 :            : 
#      14                 :            : constexpr uint8_t DB_BEST_BLOCK{'B'};
#      15                 :            : constexpr uint8_t DB_TXINDEX{'t'};
#      16                 :            : constexpr uint8_t DB_TXINDEX_BLOCK{'T'};
#      17                 :            : 
#      18                 :            : std::unique_ptr<TxIndex> g_txindex;
#      19                 :            : 
#      20                 :            : 
#      21                 :            : /** Access to the txindex database (indexes/txindex/) */
#      22                 :            : class TxIndex::DB : public BaseIndex::DB
#      23                 :            : {
#      24                 :            : public:
#      25                 :            :     explicit DB(size_t n_cache_size, bool f_memory = false, bool f_wipe = false);
#      26                 :            : 
#      27                 :            :     /// Read the disk location of the transaction data with the given hash. Returns false if the
#      28                 :            :     /// transaction hash is not indexed.
#      29                 :            :     bool ReadTxPos(const uint256& txid, CDiskTxPos& pos) const;
#      30                 :            : 
#      31                 :            :     /// Write a batch of transaction positions to the DB.
#      32                 :            :     bool WriteTxs(const std::vector<std::pair<uint256, CDiskTxPos>>& v_pos);
#      33                 :            : 
#      34                 :            :     /// Migrate txindex data from the block tree DB, where it may be for older nodes that have not
#      35                 :            :     /// been upgraded yet to the new database.
#      36                 :            :     bool MigrateData(CBlockTreeDB& block_tree_db, const CBlockLocator& best_locator);
#      37                 :            : };
#      38                 :            : 
#      39                 :            : TxIndex::DB::DB(size_t n_cache_size, bool f_memory, bool f_wipe) :
#      40                 :            :     BaseIndex::DB(gArgs.GetDataDirNet() / "indexes" / "txindex", n_cache_size, f_memory, f_wipe)
#      41                 :         11 : {}
#      42                 :            : 
#      43                 :            : bool TxIndex::DB::ReadTxPos(const uint256 &txid, CDiskTxPos& pos) const
#      44                 :        428 : {
#      45                 :        428 :     return Read(std::make_pair(DB_TXINDEX, txid), pos);
#      46                 :        428 : }
#      47                 :            : 
#      48                 :            : bool TxIndex::DB::WriteTxs(const std::vector<std::pair<uint256, CDiskTxPos>>& v_pos)
#      49                 :       1414 : {
#      50                 :       1414 :     CDBBatch batch(*this);
#      51         [ +  + ]:       1469 :     for (const auto& tuple : v_pos) {
#      52                 :       1469 :         batch.Write(std::make_pair(DB_TXINDEX, tuple.first), tuple.second);
#      53                 :       1469 :     }
#      54                 :       1414 :     return WriteBatch(batch);
#      55                 :       1414 : }
#      56                 :            : 
#      57                 :            : /*
#      58                 :            :  * Safely persist a transfer of data from the old txindex database to the new one, and compact the
#      59                 :            :  * range of keys updated. This is used internally by MigrateData.
#      60                 :            :  */
#      61                 :            : static void WriteTxIndexMigrationBatches(CDBWrapper& newdb, CDBWrapper& olddb,
#      62                 :            :                                          CDBBatch& batch_newdb, CDBBatch& batch_olddb,
#      63                 :            :                                          const std::pair<uint8_t, uint256>& begin_key,
#      64                 :            :                                          const std::pair<uint8_t, uint256>& end_key)
#      65                 :          0 : {
#      66                 :            :     // Sync new DB changes to disk before deleting from old DB.
#      67                 :          0 :     newdb.WriteBatch(batch_newdb, /*fSync=*/ true);
#      68                 :          0 :     olddb.WriteBatch(batch_olddb);
#      69                 :          0 :     olddb.CompactRange(begin_key, end_key);
#      70                 :            : 
#      71                 :          0 :     batch_newdb.Clear();
#      72                 :          0 :     batch_olddb.Clear();
#      73                 :          0 : }
#      74                 :            : 
#      75                 :            : bool TxIndex::DB::MigrateData(CBlockTreeDB& block_tree_db, const CBlockLocator& best_locator)
#      76                 :         11 : {
#      77                 :            :     // The prior implementation of txindex was always in sync with block index
#      78                 :            :     // and presence was indicated with a boolean DB flag. If the flag is set,
#      79                 :            :     // this means the txindex from a previous version is valid and in sync with
#      80                 :            :     // the chain tip. The first step of the migration is to unset the flag and
#      81                 :            :     // write the chain hash to a separate key, DB_TXINDEX_BLOCK. After that, the
#      82                 :            :     // index entries are copied over in batches to the new database. Finally,
#      83                 :            :     // DB_TXINDEX_BLOCK is erased from the old database and the block hash is
#      84                 :            :     // written to the new database.
#      85                 :            :     //
#      86                 :            :     // Unsetting the boolean flag ensures that if the node is downgraded to a
#      87                 :            :     // previous version, it will not see a corrupted, partially migrated index
#      88                 :            :     // -- it will see that the txindex is disabled. When the node is upgraded
#      89                 :            :     // again, the migration will pick up where it left off and sync to the block
#      90                 :            :     // with hash DB_TXINDEX_BLOCK.
#      91                 :         11 :     bool f_legacy_flag = false;
#      92                 :         11 :     block_tree_db.ReadFlag("txindex", f_legacy_flag);
#      93         [ -  + ]:         11 :     if (f_legacy_flag) {
#      94         [ #  # ]:          0 :         if (!block_tree_db.Write(DB_TXINDEX_BLOCK, best_locator)) {
#      95                 :          0 :             return error("%s: cannot write block indicator", __func__);
#      96                 :          0 :         }
#      97         [ #  # ]:          0 :         if (!block_tree_db.WriteFlag("txindex", false)) {
#      98                 :          0 :             return error("%s: cannot write block index db flag", __func__);
#      99                 :          0 :         }
#     100                 :         11 :     }
#     101                 :            : 
#     102                 :         11 :     CBlockLocator locator;
#     103         [ +  - ]:         11 :     if (!block_tree_db.Read(DB_TXINDEX_BLOCK, locator)) {
#     104                 :         11 :         return true;
#     105                 :         11 :     }
#     106                 :            : 
#     107                 :          0 :     int64_t count = 0;
#     108                 :          0 :     LogPrintf("Upgrading txindex database... [0%%]\n");
#     109                 :          0 :     uiInterface.ShowProgress(_("Upgrading txindex database").translated, 0, true);
#     110                 :          0 :     int report_done = 0;
#     111                 :          0 :     const size_t batch_size = 1 << 24; // 16 MiB
#     112                 :            : 
#     113                 :          0 :     CDBBatch batch_newdb(*this);
#     114                 :          0 :     CDBBatch batch_olddb(block_tree_db);
#     115                 :            : 
#     116                 :          0 :     std::pair<uint8_t, uint256> key;
#     117                 :          0 :     std::pair<uint8_t, uint256> begin_key{DB_TXINDEX, uint256()};
#     118                 :          0 :     std::pair<uint8_t, uint256> prev_key = begin_key;
#     119                 :            : 
#     120                 :          0 :     bool interrupted = false;
#     121                 :          0 :     std::unique_ptr<CDBIterator> cursor(block_tree_db.NewIterator());
#     122         [ #  # ]:          0 :     for (cursor->Seek(begin_key); cursor->Valid(); cursor->Next()) {
#     123         [ #  # ]:          0 :         if (ShutdownRequested()) {
#     124                 :          0 :             interrupted = true;
#     125                 :          0 :             break;
#     126                 :          0 :         }
#     127                 :            : 
#     128         [ #  # ]:          0 :         if (!cursor->GetKey(key)) {
#     129                 :          0 :             return error("%s: cannot get key from valid cursor", __func__);
#     130                 :          0 :         }
#     131         [ #  # ]:          0 :         if (key.first != DB_TXINDEX) {
#     132                 :          0 :             break;
#     133                 :          0 :         }
#     134                 :            : 
#     135                 :            :         // Log progress every 10%.
#     136         [ #  # ]:          0 :         if (++count % 256 == 0) {
#     137                 :            :             // Since txids are uniformly random and traversed in increasing order, the high 16 bits
#     138                 :            :             // of the hash can be used to estimate the current progress.
#     139                 :          0 :             const uint256& txid = key.second;
#     140                 :          0 :             uint32_t high_nibble =
#     141                 :          0 :                 (static_cast<uint32_t>(*(txid.begin() + 0)) << 8) +
#     142                 :          0 :                 (static_cast<uint32_t>(*(txid.begin() + 1)) << 0);
#     143                 :          0 :             int percentage_done = (int)(high_nibble * 100.0 / 65536.0 + 0.5);
#     144                 :            : 
#     145                 :          0 :             uiInterface.ShowProgress(_("Upgrading txindex database").translated, percentage_done, true);
#     146         [ #  # ]:          0 :             if (report_done < percentage_done/10) {
#     147                 :          0 :                 LogPrintf("Upgrading txindex database... [%d%%]\n", percentage_done);
#     148                 :          0 :                 report_done = percentage_done/10;
#     149                 :          0 :             }
#     150                 :          0 :         }
#     151                 :            : 
#     152                 :          0 :         CDiskTxPos value;
#     153         [ #  # ]:          0 :         if (!cursor->GetValue(value)) {
#     154                 :          0 :             return error("%s: cannot parse txindex record", __func__);
#     155                 :          0 :         }
#     156                 :          0 :         batch_newdb.Write(key, value);
#     157                 :          0 :         batch_olddb.Erase(key);
#     158                 :            : 
#     159 [ #  # ][ #  # ]:          0 :         if (batch_newdb.SizeEstimate() > batch_size || batch_olddb.SizeEstimate() > batch_size) {
#     160                 :            :             // NOTE: it's OK to delete the key pointed at by the current DB cursor while iterating
#     161                 :            :             // because LevelDB iterators are guaranteed to provide a consistent view of the
#     162                 :            :             // underlying data, like a lightweight snapshot.
#     163                 :          0 :             WriteTxIndexMigrationBatches(*this, block_tree_db,
#     164                 :          0 :                                          batch_newdb, batch_olddb,
#     165                 :          0 :                                          prev_key, key);
#     166                 :          0 :             prev_key = key;
#     167                 :          0 :         }
#     168                 :          0 :     }
#     169                 :            : 
#     170                 :            :     // If these final DB batches complete the migration, write the best block
#     171                 :            :     // hash marker to the new database and delete from the old one. This signals
#     172                 :            :     // that the former is fully caught up to that point in the blockchain and
#     173                 :            :     // that all txindex entries have been removed from the latter.
#     174         [ #  # ]:          0 :     if (!interrupted) {
#     175                 :          0 :         batch_olddb.Erase(DB_TXINDEX_BLOCK);
#     176                 :          0 :         batch_newdb.Write(DB_BEST_BLOCK, locator);
#     177                 :          0 :     }
#     178                 :            : 
#     179                 :          0 :     WriteTxIndexMigrationBatches(*this, block_tree_db,
#     180                 :          0 :                                  batch_newdb, batch_olddb,
#     181                 :          0 :                                  begin_key, key);
#     182                 :            : 
#     183         [ #  # ]:          0 :     if (interrupted) {
#     184                 :          0 :         LogPrintf("[CANCELLED].\n");
#     185                 :          0 :         return false;
#     186                 :          0 :     }
#     187                 :            : 
#     188                 :          0 :     uiInterface.ShowProgress("", 100, false);
#     189                 :            : 
#     190                 :          0 :     LogPrintf("[DONE].\n");
#     191                 :          0 :     return true;
#     192                 :          0 : }
#     193                 :            : 
#     194                 :            : TxIndex::TxIndex(size_t n_cache_size, bool f_memory, bool f_wipe)
#     195                 :            :     : m_db(std::make_unique<TxIndex::DB>(n_cache_size, f_memory, f_wipe))
#     196                 :         11 : {}
#     197                 :            : 
#     198                 :         11 : TxIndex::~TxIndex() {}
#     199                 :            : 
#     200                 :            : bool TxIndex::Init()
#     201                 :         11 : {
#     202                 :         11 :     LOCK(cs_main);
#     203                 :            : 
#     204                 :            :     // Attempt to migrate txindex from the old database to the new one. Even if
#     205                 :            :     // chain_tip is null, the node could be reindexing and we still want to
#     206                 :            :     // delete txindex records in the old database.
#     207         [ -  + ]:         11 :     if (!m_db->MigrateData(*pblocktree, ::ChainActive().GetLocator())) {
#     208                 :          0 :         return false;
#     209                 :          0 :     }
#     210                 :            : 
#     211                 :         11 :     return BaseIndex::Init();
#     212                 :         11 : }
#     213                 :            : 
#     214                 :            : bool TxIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
#     215                 :       1425 : {
#     216                 :            :     // Exclude genesis block transaction because outputs are not spendable.
#     217         [ +  + ]:       1425 :     if (pindex->nHeight == 0) return true;
#     218                 :            : 
#     219                 :       1414 :     CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size()));
#     220                 :       1414 :     std::vector<std::pair<uint256, CDiskTxPos>> vPos;
#     221                 :       1414 :     vPos.reserve(block.vtx.size());
#     222         [ +  + ]:       1469 :     for (const auto& tx : block.vtx) {
#     223                 :       1469 :         vPos.emplace_back(tx->GetHash(), pos);
#     224                 :       1469 :         pos.nTxOffset += ::GetSerializeSize(*tx, CLIENT_VERSION);
#     225                 :       1469 :     }
#     226                 :       1414 :     return m_db->WriteTxs(vPos);
#     227                 :       1414 : }
#     228                 :            : 
#     229                 :         68 : BaseIndex::DB& TxIndex::GetDB() const { return *m_db; }
#     230                 :            : 
#     231                 :            : bool TxIndex::FindTx(const uint256& tx_hash, uint256& block_hash, CTransactionRef& tx) const
#     232                 :        428 : {
#     233                 :        428 :     CDiskTxPos postx;
#     234         [ +  + ]:        428 :     if (!m_db->ReadTxPos(tx_hash, postx)) {
#     235                 :        202 :         return false;
#     236                 :        202 :     }
#     237                 :            : 
#     238                 :        226 :     CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
#     239         [ -  + ]:        226 :     if (file.IsNull()) {
#     240                 :          0 :         return error("%s: OpenBlockFile failed", __func__);
#     241                 :          0 :     }
#     242                 :        226 :     CBlockHeader header;
#     243                 :        226 :     try {
#     244                 :        226 :         file >> header;
#     245         [ -  + ]:        226 :         if (fseek(file.Get(), postx.nTxOffset, SEEK_CUR)) {
#     246                 :          0 :             return error("%s: fseek(...) failed", __func__);
#     247                 :          0 :         }
#     248                 :        226 :         file >> tx;
#     249                 :        226 :     } catch (const std::exception& e) {
#     250                 :          0 :         return error("%s: Deserialize or I/O error - %s", __func__, e.what());
#     251                 :          0 :     }
#     252         [ -  + ]:        226 :     if (tx->GetHash() != tx_hash) {
#     253                 :          0 :         return error("%s: txid mismatch", __func__);
#     254                 :          0 :     }
#     255                 :        226 :     block_hash = header.GetHash();
#     256                 :        226 :     return true;
#     257                 :        226 : }

Generated by: LCOV version 1.14