Bitcoin Core 31.99.0
P2P Digital Currency
txindex.cpp
Go to the documentation of this file.
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 <chain.h>
8#include <common/args.h>
9#include <crypto/siphash.h>
10#include <dbwrapper.h>
11#include <flatfile.h>
12#include <index/base.h>
13#include <index/disktxpos.h>
14#include <index/txindex_key.h>
15#include <interfaces/chain.h>
16#include <node/blockstorage.h>
17#include <primitives/block.h>
19#include <random.h>
20#include <serialize.h>
21#include <streams.h>
22#include <sync.h>
23#include <uint256.h>
24#include <util/fs.h>
25#include <util/log.h>
26#include <validation.h>
27
28#include <algorithm>
29#include <array>
30#include <cassert>
31#include <cstdint>
32#include <cstdio>
33#include <exception>
34#include <functional>
35#include <memory>
36#include <optional>
37#include <string>
38#include <utility>
39#include <vector>
40
41std::unique_ptr<TxIndex> g_txindex;
42
43namespace {
44SipHasher13UJ ReadOrCreateTxidHasher(CDBWrapper& db)
45{
46 std::pair<uint64_t, uint64_t> salt;
49 salt = {rng.rand64(), rng.rand64()};
50 db.Write(txindex::DB_TXID_HASH_SALT, salt, /*fSync=*/true);
51 }
52 return SipHasher13UJ{salt.first, salt.second};
53}
54} // namespace
55
58{
59public:
60 explicit DB(size_t n_cache_size, bool f_memory = false, bool f_wipe = false);
61
63 void WriteTxs(const interfaces::BlockInfo& block);
64
67
69 const bool m_has_legacy;
70
71 CBlockLocator ReadBestBlock() const override;
72 void WriteBestBlock(CDBBatch& batch, const CBlockLocator& locator) override;
73
74private:
75 DB(size_t n_cache_size, bool f_memory, bool f_wipe, bool has_legacy);
76};
77
78static fs::path TxIndexDBPath() { return gArgs.GetDataDirNet() / "indexes" / "txindex"; }
79
80TxIndex::DB::DB(size_t n_cache_size, bool f_memory, bool f_wipe) :
81 // Bloom filters are built for every key but only consulted by point reads,
82 // which iterators bypass: the per-tx hashed ('x') lookups seek with an
83 // iterator, and the 's'/'h' point reads are at most one per block against a
84 // tiny keyspace. Only the legacy entries' per-tx point lookups benefit, so
85 // enable the filters only for databases still containing them.
86 DB(n_cache_size, f_memory, f_wipe,
87 /*has_legacy=*/!f_memory && !f_wipe && CDBWrapper::HasKeyStartingWith(TxIndexDBPath(), txindex::DB_TXINDEX))
88{}
89
90TxIndex::DB::DB(size_t n_cache_size, bool f_memory, bool f_wipe, bool has_legacy) :
91 BaseIndex::DB(TxIndexDBPath(), n_cache_size, f_memory, f_wipe, /*f_obfuscate=*/false, /*f_bloom=*/has_legacy),
92 m_hasher{ReadOrCreateTxidHasher(*this)},
93 m_has_legacy{has_legacy}
94{}
95
97{
98 CBlockLocator locator;
99 if (Read(txindex::DB_BEST_BLOCK_V2, locator)) {
100 return locator;
101 }
102 // If we don't have a locator yet, start from the legacy best block.
104}
105
107{
108 batch.Write(txindex::DB_BEST_BLOCK_V2, locator);
109}
110
112{
113 // A block may be submitted again after it was already indexed, e.g. when it
114 // reconnects after a reorg or is re-processed after an unclean shutdown. It
115 // keeps its original sequence number, so skip it to avoid duplicate entries.
116 if (Exists(txindex::BlockHashKey{block.hash})) return;
117
118 uint32_t block_seq{0};
120
121 CDBBatch batch(*this);
122 batch.Write(txindex::BlockHashKey{block.hash}, block_seq);
123 batch.Write(txindex::BlockSeqKey{block_seq}, block.hash);
124 batch.Write(txindex::DB_NEXT_BLOCK_SEQ, block_seq + 1);
125 uint32_t tx_offset_in_block{txindex::BLOCK_HEADER_SIZE + GetSizeOfCompactSize(block.data->vtx.size())};
126 for (const auto& tx : block.data->vtx) {
127 const txindex::DBKey key{txindex::CreateKeyPrefix(m_hasher, tx->GetHash()),
128 txindex::BlockTxPosition{block_seq, tx_offset_in_block}};
129 batch.Write(key, txindex::EMPTY_VALUE);
130 tx_offset_in_block += tx->ComputeTotalSize();
131 }
132 WriteBatch(batch);
133}
134
135TxIndex::TxIndex(std::unique_ptr<interfaces::Chain> chain, size_t n_cache_size, bool f_memory, bool f_wipe)
136 : BaseIndex(std::move(chain), "txindex", "txidx"), m_db(std::make_unique<TxIndex::DB>(n_cache_size, f_memory, f_wipe))
137{
138 if (m_db->m_has_legacy) {
139 LogInfo("txindex contains entries in the legacy format, which uses excessive disk space. "
140 "To reclaim disk space, stop the node, delete %s and restart to rebuild the index.",
142 }
143}
144
145TxIndex::~TxIndex() = default;
146
148{
149 // Exclude genesis block transaction because outputs are not spendable.
150 if (block.height == 0) return true;
151
152 assert(block.data);
153 m_db->WriteTxs(block);
154 return true;
155}
156
157BaseIndex::DB& TxIndex::GetDB() const { return *m_db; }
158
159std::optional<TxIndexResult> TxIndex::FindTx(const Txid& tx_hash) const
160{
161 struct Candidate {
162 FlatFilePos tx_position;
163 uint256 block_hash;
164 uint32_t block_seq;
168 bool in_active_chain;
169 };
170 std::vector<Candidate> candidates;
171 {
172 std::unique_ptr<CDBIterator> it{m_db->NewIterator()};
174 txindex::DBKey key{prefix, {}};
175 for (it->Seek(key); it->Valid() && it->GetKey(key) && key.hash_prefix == prefix; it->Next()) {
176 uint256 candidate_block_hash;
177 if (!m_db->Read(txindex::BlockSeqKey{key.pos.block_seq}, candidate_block_hash)) {
178 LogWarning("Block sequence %u not found for txid %s", key.pos.block_seq, tx_hash.ToString());
179 continue;
180 }
181 LOCK(cs_main);
182 const CBlockIndex* block_index{m_chainstate->m_blockman.LookupBlockIndex(candidate_block_hash)};
183 if (!block_index) {
184 LogWarning("Block index entry %s not found for txid %s", candidate_block_hash.ToString(), tx_hash.ToString());
185 continue;
186 }
187 if (!(block_index->nStatus & BLOCK_HAVE_DATA)) continue;
188 const FlatFilePos tx_position{block_index->nFile, block_index->nDataPos + key.pos.tx_offset_in_block};
189 candidates.emplace_back(tx_position, candidate_block_hash, key.pos.block_seq, m_chainstate->m_chain.Contains(*block_index));
190 }
191 }
192
193 // Prefer active-chain matches, then later-connected blocks.
194 std::ranges::sort(candidates, std::greater{}, [](const Candidate& c) {
195 return std::pair{c.in_active_chain, c.block_seq};
196 });
197
198 for (const auto& candidate : candidates) {
199 AutoFile file{m_chainstate->m_blockman.OpenBlockFile(candidate.tx_position, /*fReadOnly=*/true)};
200 if (file.IsNull()) {
201 LogWarning("OpenBlockFile failed for txid %s", tx_hash.ToString());
202 continue;
203 }
205 try {
206 file >> TX_WITH_WITNESS(tx);
207 } catch (const std::exception& e) {
208 LogWarning("Deserialize or I/O error - %s", e.what());
209 continue;
210 }
211 if (tx->GetHash() == tx_hash) {
212 return TxIndexResult{candidate.block_hash, std::move(tx)};
213 }
214 }
215 // Fall back to legacy if no hashed entry matched. This makes misses pay an
216 // extra lookup, but keeps existing full-txid entries readable after upgrade.
217 return m_db->m_has_legacy ? FindLegacyTx(tx_hash) : std::nullopt;
218}
219
220std::optional<TxIndexResult> TxIndex::FindLegacyTx(const Txid& tx_hash) const
221{
222 CDiskTxPos postx;
223 if (!m_db->Read(txindex::LegacyTxKey(tx_hash), postx)) {
224 return std::nullopt;
225 }
226
227 AutoFile file{m_chainstate->m_blockman.OpenBlockFile(postx, /*fReadOnly=*/true)};
228 if (file.IsNull()) {
229 LogError("OpenBlockFile failed");
230 return std::nullopt;
231 }
232 CBlockHeader header;
234 try {
235 file >> header;
236 file.seek(postx.nTxOffset, SEEK_CUR);
237 file >> TX_WITH_WITNESS(tx);
238 } catch (const std::exception& e) {
239 LogError("Deserialize or I/O error - %s", e.what());
240 return std::nullopt;
241 }
242 if (tx->GetHash() != tx_hash) {
243 LogError("txid mismatch");
244 return std::nullopt;
245 }
246 return TxIndexResult{header.GetHash(), std::move(tx)};
247}
ArgsManager gArgs
Definition: args.cpp:38
@ BLOCK_HAVE_DATA
full block available in blk*.dat
Definition: chain.h:75
fs::path GetDataDirNet() const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Get data directory path with appended network identifier.
Definition: args.cpp:328
Non-refcounted RAII wrapper for FILE*.
Definition: streams.h:395
The database stores a block locator of the chain the database is synced to so that the index can effi...
Definition: base.h:65
virtual CBlockLocator ReadBestBlock() const
Read block locator of the chain that the index is in sync with.
Definition: base.cpp:79
Base class for indices of blockchain data.
Definition: base.h:55
Chainstate * m_chainstate
Definition: base.h:115
Nodes collect new transactions into a block, hash them into a hash tree, and scan through nonce value...
Definition: block.h:27
uint256 GetHash() const
Definition: block.cpp:14
std::vector< CTransactionRef > vtx
Definition: block.h:77
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: chain.h:94
bool Contains(const CBlockIndex &index) const
Efficiently check whether a block is present in this chain.
Definition: chain.h:410
Batch of changes queued to be written to a CDBWrapper.
Definition: dbwrapper.h:88
void Write(const K &key, const V &value)
Definition: dbwrapper.h:112
bool Read(const K &key, V &value) const
Definition: dbwrapper.h:220
void Write(const K &key, const V &value, bool fSync=false)
Definition: dbwrapper.h:240
CChain m_chain
The current chain of blockheaders we consult and build on.
Definition: validation.h:628
node::BlockManager & m_blockman
Reference to a BlockManager instance which itself is shared across all Chainstate instances.
Definition: validation.h:581
Fast randomness source.
Definition: random.h:386
uint64_t rand64() noexcept
Generate a random 64-bit integer.
Definition: random.h:404
A custom weaker variant of SipHash-1-3 without padding, and supporting "jumbo" inputs.
Definition: siphash.h:161
Access to the txindex database (indexes/txindex/)
Definition: txindex.cpp:58
const bool m_has_legacy
Whether the database contains any legacy ('t' + txid) entries.
Definition: txindex.cpp:69
DB(size_t n_cache_size, bool f_memory=false, bool f_wipe=false)
Definition: txindex.cpp:80
void WriteBestBlock(CDBBatch &batch, const CBlockLocator &locator) override
Write block locator of the chain that the index is in sync with.
Definition: txindex.cpp:106
const SipHasher13UJ m_hasher
Used to hash the txid to compute the prefix.
Definition: txindex.cpp:66
CBlockLocator ReadBestBlock() const override
Read block locator of the chain that the index is in sync with.
Definition: txindex.cpp:96
void WriteTxs(const interfaces::BlockInfo &block)
Write a block of transaction positions to the DB.
Definition: txindex.cpp:111
TxIndex is used to look up transactions included in the blockchain by hash.
Definition: txindex.h:37
BaseIndex::DB & GetDB() const override
Definition: txindex.cpp:157
std::optional< TxIndexResult > FindTx(const Txid &tx_hash) const
Look up a transaction by hash.
Definition: txindex.cpp:159
bool CustomAppend(const interfaces::BlockInfo &block) override
Write update index entries for a newly connected block.
Definition: txindex.cpp:147
TxIndex(std::unique_ptr< interfaces::Chain > chain, size_t n_cache_size, bool f_memory=false, bool f_wipe=false)
Constructs the index, which becomes available to be queried.
Definition: txindex.cpp:135
virtual ~TxIndex() override
const std::unique_ptr< DB > m_db
Definition: txindex.h:43
std::optional< TxIndexResult > FindLegacyTx(const Txid &tx_hash) const
Look up a transaction among the legacy (full-txid) entries.
Definition: txindex.cpp:220
std::string ToString() const
Definition: uint256.cpp:21
CBlockIndex * LookupBlockIndex(const uint256 &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
bool IsBlockPruned(const CBlockIndex &block) const EXCLUSIVE_LOCKS_REQUIRED(void UpdatePruneLock(const std::string &name, const PruneLockInfo &lock_info) EXCLUSIVE_LOCKS_REQUIRED(bool DeletePruneLock(const std::string &name) EXCLUSIVE_LOCKS_REQUIRED(AutoFile OpenBlockFile(const FlatFilePos &pos, bool fReadOnly) const
Check whether the block associated with this index entry is pruned or not.
Definition: blockstorage.h:465
std::string ToString() const
256-bit opaque blob.
Definition: uint256.h:196
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:8
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition: fs.h:162
#define LogWarning(...)
Definition: log.h:126
#define LogInfo(...)
Definition: log.h:125
#define LogError(...)
Definition: log.h:127
const std::string DB_TXID_HASH_SALT
Definition: txindex_key.h:39
constexpr std::array< std::byte, 0 > EMPTY_VALUE
Empty value of a hashed txindex row, whose position is encoded in its key.
Definition: txindex_key.h:45
constexpr uint8_t DB_TXINDEX
Prefix of a legacy (pre-hashing) txindex row.
Definition: txindex_key.h:42
TxHashKeyPrefix CreateKeyPrefix(const SipHasher13UJ &hasher, const Txid &txid)
Definition: txindex_key.h:102
constexpr uint32_t BLOCK_HEADER_SIZE
Serialized size of a block header, the offset of the first byte after it.
Definition: txindex_key.h:48
const std::string DB_NEXT_BLOCK_SEQ
Definition: txindex_key.h:38
uint64_t TxHashKeyPrefix
Definition: txindex_key.h:100
std::pair< uint8_t, uint256 > LegacyTxKey(const Txid &txid)
Key of a legacy (pre-hashing) txindex row: the full txid under the 't' prefix.
Definition: txindex_key.h:121
const std::string DB_BEST_BLOCK_V2
Definition: txindex_key.h:40
constexpr TransactionSerParams TX_WITH_WITNESS
Definition: transaction.h:180
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:403
const char * prefix
Definition: rest.cpp:1180
constexpr unsigned int GetSizeOfCompactSize(uint64_t nSize)
Compact Size size < 253 – 1 byte size <= USHRT_MAX – 3 bytes (253 + 2 bytes) size <= UINT_MAX – 5 byt...
Definition: serialize.h:291
Describes a place in the block chain to another node such that if the other node doesn't have the sam...
Definition: block.h:117
uint32_t nTxOffset
Definition: disktxpos.h:13
int32_t nFile
Definition: flatfile.h:16
A found transaction and the hash of the block that contains it.
Definition: txindex.h:26
uint256 block_hash
Definition: txindex.h:27
Block data sent with blockConnected, blockDisconnected notifications.
Definition: chain.h:19
const CBlock * data
Definition: chain.h:25
const uint256 & hash
Definition: chain.h:20
Key for looking up the sequence number assigned to the block with the given hash.
Definition: txindex_key.h:87
Key for looking up the hash of the block with the given sequence number.
Definition: txindex_key.h:74
The location of a transaction: the sequence number of the block that contains it and the transaction'...
Definition: txindex_key.h:55
#define LOCK(cs)
Definition: sync.h:268
CDBWrapper db
Definition: dbwrapper.cpp:371
FastRandomContext rng
Definition: dbwrapper.cpp:413
static fs::path TxIndexDBPath()
Definition: txindex.cpp:78
std::unique_ptr< TxIndex > g_txindex
The global transaction index, used in GetTransaction. May be null.
Definition: txindex.cpp:41
assert(!tx.IsCoinBase())