Line data Source code
1 : // Copyright (c) 2017-2021 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 <index/disktxpos.h> 8 : #include <node/blockstorage.h> 9 : #include <util/system.h> 10 : #include <validation.h> 11 : 12 : using node::OpenBlockFile; 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 138 : TxIndex::DB::DB(size_t n_cache_size, bool f_memory, bool f_wipe) : 34 69 : BaseIndex::DB(gArgs.GetDataDirNet() / "indexes" / "txindex", n_cache_size, f_memory, f_wipe) 35 138 : {} 36 : 37 391 : bool TxIndex::DB::ReadTxPos(const uint256 &txid, CDiskTxPos& pos) const 38 : { 39 391 : return Read(std::make_pair(DB_TXINDEX, txid), pos); 40 : } 41 : 42 23703 : bool TxIndex::DB::WriteTxs(const std::vector<std::pair<uint256, CDiskTxPos>>& v_pos) 43 : { 44 23703 : CDBBatch batch(*this); 45 61941 : for (const auto& tuple : v_pos) { 46 38238 : batch.Write(std::make_pair(DB_TXINDEX, tuple.first), tuple.second); 47 : } 48 23703 : return WriteBatch(batch); 49 23703 : } 50 : 51 138 : TxIndex::TxIndex(size_t n_cache_size, bool f_memory, bool f_wipe) 52 69 : : m_db(std::make_unique<TxIndex::DB>(n_cache_size, f_memory, f_wipe)) 53 138 : {} 54 : 55 138 : TxIndex::~TxIndex() = default; 56 : 57 23772 : bool TxIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex) 58 : { 59 : // Exclude genesis block transaction because outputs are not spendable. 60 23772 : if (pindex->nHeight == 0) return true; 61 : 62 23703 : CDiskTxPos pos{ 63 47406 : WITH_LOCK(::cs_main, return pindex->GetBlockPos()), 64 23703 : GetSizeOfCompactSize(block.vtx.size())}; 65 23703 : std::vector<std::pair<uint256, CDiskTxPos>> vPos; 66 23703 : vPos.reserve(block.vtx.size()); 67 61941 : for (const auto& tx : block.vtx) { 68 38238 : vPos.emplace_back(tx->GetHash(), pos); 69 38238 : pos.nTxOffset += ::GetSerializeSize(*tx, CLIENT_VERSION); 70 : } 71 23703 : return m_db->WriteTxs(vPos); 72 23772 : } 73 : 74 585 : BaseIndex::DB& TxIndex::GetDB() const { return *m_db; } 75 : 76 391 : bool TxIndex::FindTx(const uint256& tx_hash, uint256& block_hash, CTransactionRef& tx) const 77 : { 78 391 : CDiskTxPos postx; 79 391 : if (!m_db->ReadTxPos(tx_hash, postx)) { 80 101 : return false; 81 : } 82 : 83 290 : AutoFile file{OpenBlockFile(postx, true)}; 84 290 : if (file.IsNull()) { 85 0 : return error("%s: OpenBlockFile failed", __func__); 86 : } 87 290 : CBlockHeader header; 88 : try { 89 290 : file >> header; 90 290 : if (fseek(file.Get(), postx.nTxOffset, SEEK_CUR)) { 91 0 : return error("%s: fseek(...) failed", __func__); 92 : } 93 290 : file >> tx; 94 290 : } catch (const std::exception& e) { 95 0 : return error("%s: Deserialize or I/O error - %s", __func__, e.what()); 96 0 : } 97 290 : if (tx->GetHash() != tx_hash) { 98 0 : return error("%s: txid mismatch", __func__); 99 : } 100 290 : block_hash = header.GetHash(); 101 290 : return true; 102 391 : } 103 : 104 0 : bool TxIndex::HasTx(const uint256& tx_hash) const 105 : { 106 0 : CDiskTxPos postx; 107 0 : return m_db->ReadTxPos(tx_hash, postx); 108 : }