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