txindex.cpp
1 // Copyright (c) 2017-2022 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/txindex.h> 6 7 #include <common/args.h> 8 #include <dbwrapper.h> 9 #include <flatfile.h> 10 #include <index/base.h> 11 #include <index/disktxpos.h> 12 #include <interfaces/chain.h> 13 #include <logging.h> 14 #include <node/blockstorage.h> 15 #include <primitives/block.h> 16 #include <primitives/transaction.h> 17 #include <serialize.h> 18 #include <streams.h> 19 #include <uint256.h> 20 #include <util/fs.h> 21 #include <validation.h> 22 23 #include <cassert> 24 #include <cstdint> 25 #include <cstdio> 26 #include <exception> 27 #include <iterator> 28 #include <span> 29 #include <string> 30 #include <utility> 31 #include <vector> 32 33 constexpr uint8_t DB_TXINDEX{'t'}; 34 35 std::unique_ptr<TxIndex> g_txindex; 36 37 38 /** Access to the txindex database (indexes/txindex/) */ 39 class TxIndex::DB : public BaseIndex::DB 40 { 41 public: 42 explicit DB(size_t n_cache_size, bool f_memory = false, bool f_wipe = false); 43 44 /// Read the disk location of the transaction data with the given hash. Returns false if the 45 /// transaction hash is not indexed. 46 bool ReadTxPos(const Txid& txid, CDiskTxPos& pos) const; 47 48 /// Write a batch of transaction positions to the DB. 49 void WriteTxs(const std::vector<std::pair<Txid, CDiskTxPos>>& v_pos); 50 }; 51 52 TxIndex::DB::DB(size_t n_cache_size, bool f_memory, bool f_wipe) : 53 BaseIndex::DB(gArgs.GetDataDirNet() / "indexes" / "txindex", n_cache_size, f_memory, f_wipe) 54 {} 55 56 bool TxIndex::DB::ReadTxPos(const Txid& txid, CDiskTxPos& pos) const 57 { 58 return Read(std::make_pair(DB_TXINDEX, txid.ToUint256()), pos); 59 } 60 61 void TxIndex::DB::WriteTxs(const std::vector<std::pair<Txid, CDiskTxPos>>& v_pos) 62 { 63 CDBBatch batch(*this); 64 for (const auto& [txid, pos] : v_pos) { 65 batch.Write(std::make_pair(DB_TXINDEX, txid.ToUint256()), pos); 66 } 67 WriteBatch(batch); 68 } 69 70 TxIndex::TxIndex(std::unique_ptr<interfaces::Chain> chain, size_t n_cache_size, bool f_memory, bool f_wipe) 71 : BaseIndex(std::move(chain), "txindex"), m_db(std::make_unique<TxIndex::DB>(n_cache_size, f_memory, f_wipe)) 72 {} 73 74 TxIndex::~TxIndex() = default; 75 76 bool TxIndex::CustomAppend(const interfaces::BlockInfo& block) 77 { 78 // Exclude genesis block transaction because outputs are not spendable. 79 if (block.height == 0) return true; 80 81 assert(block.data); 82 CDiskTxPos pos({block.file_number, block.data_pos}, GetSizeOfCompactSize(block.data->vtx.size())); 83 std::vector<std::pair<Txid, CDiskTxPos>> vPos; 84 vPos.reserve(block.data->vtx.size()); 85 for (const auto& tx : block.data->vtx) { 86 vPos.emplace_back(tx->GetHash(), pos); 87 pos.nTxOffset += ::GetSerializeSize(TX_WITH_WITNESS(*tx)); 88 } 89 m_db->WriteTxs(vPos); 90 return true; 91 } 92 93 BaseIndex::DB& TxIndex::GetDB() const { return *m_db; } 94 95 bool TxIndex::FindTx(const Txid& tx_hash, uint256& block_hash, CTransactionRef& tx) const 96 { 97 CDiskTxPos postx; 98 if (!m_db->ReadTxPos(tx_hash, postx)) { 99 return false; 100 } 101 102 AutoFile file{m_chainstate->m_blockman.OpenBlockFile(postx, true)}; 103 if (file.IsNull()) { 104 LogError("OpenBlockFile failed"); 105 return false; 106 } 107 CBlockHeader header; 108 try { 109 file >> header; 110 file.seek(postx.nTxOffset, SEEK_CUR); 111 file >> TX_WITH_WITNESS(tx); 112 } catch (const std::exception& e) { 113 LogError("Deserialize or I/O error - %s", e.what()); 114 return false; 115 } 116 if (tx->GetHash() != tx_hash) { 117 LogError("txid mismatch"); 118 return false; 119 } 120 block_hash = header.GetHash(); 121 return true; 122 }