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