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