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 : }
|