Line data Source code
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 2 : 18 2 : 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 0 : TxIndex::DB::DB(size_t n_cache_size, bool f_memory, bool f_wipe) : 34 0 : BaseIndex::DB(gArgs.GetDataDirNet() / "indexes" / "txindex", n_cache_size, f_memory, f_wipe) 35 0 : {} 36 : 37 0 : bool TxIndex::DB::ReadTxPos(const uint256 &txid, CDiskTxPos& pos) const 38 : { 39 0 : return Read(std::make_pair(DB_TXINDEX, txid), pos); 40 : } 41 : 42 0 : bool TxIndex::DB::WriteTxs(const std::vector<std::pair<uint256, CDiskTxPos>>& v_pos) 43 : { 44 0 : CDBBatch batch(*this); 45 0 : for (const auto& tuple : v_pos) { 46 0 : batch.Write(std::make_pair(DB_TXINDEX, tuple.first), tuple.second); 47 : } 48 0 : return WriteBatch(batch); 49 0 : } 50 : 51 0 : TxIndex::TxIndex(std::unique_ptr<interfaces::Chain> chain, size_t n_cache_size, bool f_memory, bool f_wipe) 52 0 : : BaseIndex(std::move(chain), "txindex"), m_db(std::make_unique<TxIndex::DB>(n_cache_size, f_memory, f_wipe)) 53 0 : {} 54 : 55 0 : TxIndex::~TxIndex() = default; 56 : 57 0 : bool TxIndex::CustomAppend(const interfaces::BlockInfo& block) 58 : { 59 : // Exclude genesis block transaction because outputs are not spendable. 60 0 : if (block.height == 0) return true; 61 : 62 0 : assert(block.data); 63 0 : CDiskTxPos pos({block.file_number, block.data_pos}, GetSizeOfCompactSize(block.data->vtx.size())); 64 0 : std::vector<std::pair<uint256, CDiskTxPos>> vPos; 65 0 : vPos.reserve(block.data->vtx.size()); 66 0 : for (const auto& tx : block.data->vtx) { 67 0 : vPos.emplace_back(tx->GetHash(), pos); 68 0 : pos.nTxOffset += ::GetSerializeSize(*tx, CLIENT_VERSION); 69 : } 70 0 : return m_db->WriteTxs(vPos); 71 0 : } 72 : 73 0 : BaseIndex::DB& TxIndex::GetDB() const { return *m_db; } 74 2 : 75 0 : bool TxIndex::FindTx(const uint256& tx_hash, uint256& block_hash, CTransactionRef& tx) const 76 : { 77 0 : CDiskTxPos postx; 78 0 : if (!m_db->ReadTxPos(tx_hash, postx)) { 79 0 : return false; 80 : } 81 : 82 0 : CAutoFile file{m_chainstate->m_blockman.OpenBlockFile(postx, true), CLIENT_VERSION}; 83 0 : if (file.IsNull()) { 84 0 : return error("%s: OpenBlockFile failed", __func__); 85 : } 86 0 : CBlockHeader header; 87 : try { 88 0 : file >> header; 89 0 : if (fseek(file.Get(), postx.nTxOffset, SEEK_CUR)) { 90 0 : return error("%s: fseek(...) failed", __func__); 91 : } 92 0 : file >> tx; 93 0 : } catch (const std::exception& e) { 94 0 : return error("%s: Deserialize or I/O error - %s", __func__, e.what()); 95 0 : } 96 0 : if (tx->GetHash() != tx_hash) { 97 0 : return error("%s: txid mismatch", __func__); 98 : } 99 0 : block_hash = header.GetHash(); 100 0 : return true; 101 0 : }