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