Bitcoin Core 32.99.0
P2P Digital Currency
Loading...
Searching...
No Matches
txindex_tests.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 <addresstype.h>
6#include <chain.h>
7#include <chainparams.h>
8#include <common/args.h>
9#include <consensus/amount.h>
11#include <crypto/hex_base.h>
12#include <dbwrapper.h>
13#include <flatfile.h>
14#include <index/disktxpos.h>
15#include <index/txindex.h>
16#include <index/txindex_key.h>
17#include <interfaces/chain.h>
18#include <key.h>
19#include <node/blockstorage.h>
20#include <primitives/block.h>
21#include <script/script.h>
22#include <streams.h>
23#include <sync.h>
26#include <util/byte_units.h>
27#include <util/check.h>
28#include <util/strencodings.h>
29#include <validation.h>
30
31#include <cstdint>
32#include <memory>
33#include <string>
34#include <string_view>
35#include <utility>
36#include <vector>
37
38#include <boost/test/unit_test.hpp>
39
41
42// Grants tests access to the otherwise non-public txindex database handle.
44{
45public:
46 static CDBWrapper& GetDB(const TxIndex& txindex) { return txindex.GetDB(); }
47 static CBlockLocator ReadBestBlock(const TxIndex& txindex) { return txindex.GetDB().ReadBestBlock(); }
48 static void WriteBestBlock(const TxIndex& txindex, const CBlockLocator& locator)
49 {
50 auto& db{txindex.GetDB()};
51 CDBBatch batch{db};
52 db.WriteBestBlock(batch, locator);
53 db.WriteBatch(batch);
54 }
55};
56
57namespace {
58
60{
61 std::pair<uint64_t, uint64_t> salt;
63 return SipHasher13UJ{salt.first, salt.second};
64}
65
66std::vector<txindex::BlockTxPosition> BucketPositions(CDBWrapper& db, txindex::TxHashKeyPrefix prefix)
67{
68 std::vector<txindex::BlockTxPosition> positions;
69 std::unique_ptr<CDBIterator> it{db.NewIterator()};
70 txindex::DBKey key{prefix, {}};
71 for (it->Seek(key); it->Valid() && it->GetKey(key) && key.hash_prefix == prefix; it->Next()) {
72 positions.push_back(key.pos);
73 }
74 return positions;
75}
76
78{
80 const CBlockIndex* block_index{chainman.ActiveChain()[height]};
82 return {block_index->nFile, block_index->nDataPos};
83}
84
85uint256 LookupTx(const TxIndex& txindex, const Txid& txid)
86{
87 const auto result{txindex.FindTx(txid)};
88 BOOST_REQUIRE(result);
89 BOOST_CHECK(result->tx->GetHash() == txid);
90 return result->block_hash;
91}
92
93void InvalidateBlock(ChainstateManager& chainman, const uint256& block_hash)
94{
98 BOOST_REQUIRE(chainman.ActiveChainstate().InvalidateBlock(state, block_index));
99}
100
101} // namespace
102
104{
105 constexpr struct { txindex::BlockTxPosition position; std::string_view encoded; } test_vectors[]{
106 {{0, 0}, "00000000"},
107 {{1, 2}, "01000002"},
108 {{10'000'000, 123}, "83e1ac0000007b"},
109 {{456, 3'999'999}, "82483d08ff"},
110 };
111
112 for (const auto& [position, encoded] : test_vectors) {
114
117 BOOST_CHECK(decoded == position);
118 }
119
120 // Pin the full key encodings, including the type prefixes.
122 BOOST_CHECK_EQUAL(HexStr(DataStream{} << txindex::DBKey{0x0102030405, {1, 2}}),
123 "78010203040501000002");
124
126}
127
129{
132 SipHasher13UJ{0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL},
133 Txid{"1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100"}),
134 0xc67d87b08cULL);
135}
136
138{
139 TxIndex txindex(interfaces::MakeChain(m_node), /*n_cache_size=*/1_MiB, /*f_memory=*/true);
140 BOOST_REQUIRE(txindex.Init());
141
142 // Transaction should not be found in the index before it is started.
143 for (const auto& txn : m_coinbase_txns) {
144 BOOST_CHECK(!txindex.FindTx(txn->GetHash()));
145 }
146
147 // BlockUntilSyncedToCurrentChain should return false before txindex is started.
148 BOOST_CHECK(!txindex.BlockUntilSyncedToCurrentChain());
149
150 txindex.Sync();
151
152 // Check that txindex excludes genesis block transactions.
154 for (const auto& txn : genesis_block.vtx) {
155 BOOST_CHECK(!txindex.FindTx(txn->GetHash()));
156 }
157
158 // Check that txindex has all txs that were in the chain before it started.
159 for (const auto& txn : m_coinbase_txns) {
160 LookupTx(txindex, txn->GetHash());
161 }
162
163 // Check that new transactions in new blocks make it into the index.
164 for (int i = 0; i < 10; i++) {
166 std::vector<CMutableTransaction> no_txns;
167 const CBlock& block = CreateAndProcessBlock(no_txns, coinbase_script_pub_key);
168 const CTransaction& txn = *block.vtx[0];
169
170 BOOST_CHECK(txindex.BlockUntilSyncedToCurrentChain());
171 LookupTx(txindex, txn.GetHash());
172 }
173
175}
176
178{
179 // On-disk, so the legacy-entry probe at construction runs against a fresh
180 // database, as it would on a node whose index was created by this version.
181 TxIndex txindex(interfaces::MakeChain(m_node), /*n_cache_size=*/1_MiB, /*f_memory=*/false);
182 BOOST_REQUIRE(txindex.Init());
183 txindex.Sync();
184
187
188 // Lookups scan candidates in descending sequence order, so entries of
189 // later-connected blocks are tried first. Forge a colliding entry under the
190 // first coinbase's prefix pointing at the last coinbase, so looking up the
191 // first tx must scan that false positive first.
192 const Txid fake_txid{m_coinbase_txns.back()->GetHash()};
193 const Txid target_txid{m_coinbase_txns.front()->GetHash()};
196 // Distinct prefixes guarantee the target's bucket initially holds only the target.
198
199 // Read the last coinbase's encoded position straight from its bucket.
203
205
206 // The target's bucket now holds the real target first (lower sequence
207 // number), then the forged false positive, which the descending scan tries first.
212
214
215 // A database created fresh by this version cannot contain legacy entries, so
216 // lookups skip the legacy fallback: drop the last coinbase's hashed entry and
217 // re-add it under the old 't' + txid schema (a physical CDiskTxPos), then
218 // confirm the lookup misses even though the legacy row exists.
219 // BlockTxPosition offsets are from the block start (header included), while
220 // the legacy CDiskTxPos.nTxOffset is measured after the header.
224 BOOST_CHECK(!txindex.FindTx(fake_txid));
225
227}
228
230{
231 // Seed the on-disk database with a legacy ('t' + txid) entry before the index
232 // is opened, as if it had been written by a pre-hashing version.
233 const Txid legacy_txid{m_coinbase_txns.front()->GetHash()};
234 // The block at height 1 holds only the coinbase, so the tx starts right after
235 // the header and the 1-byte tx count.
237 {
238 CDBWrapper db{DBParams{.path = gArgs.GetDataDirNet() / "indexes" / "txindex", .cache_bytes = 1_MiB}};
240 }
241
242 TxIndex txindex(interfaces::MakeChain(m_node), /*n_cache_size=*/1_MiB, /*f_memory=*/false);
243 BOOST_REQUIRE(txindex.Init());
244 txindex.Sync();
245
246 // Drop the hashed entries so only the legacy row remains, then confirm the
247 // lookup succeeds through the fallback.
250 const auto bucket{BucketPositions(db, prefix)};
251 BOOST_REQUIRE(!bucket.empty());
252 for (const auto& pos : bucket) db.Erase(txindex::DBKey{prefix, pos});
253
255
257}
258
280
282{
283 TxIndex txindex(interfaces::MakeChain(m_node), /*n_cache_size=*/1_MiB, /*f_memory=*/true);
284 BOOST_REQUIRE(txindex.Init());
285 txindex.Sync();
286
287 const CScript coinbase_script{CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG};
288
289 // Mine a unique (non-coinbase) transaction into a new block at height 101.
290 CMutableTransaction unique_mtx{CreateValidMempoolTransaction(
291 /*input_transaction=*/m_coinbase_txns[0],
292 /*input_vout=*/0,
293 /*input_height=*/1,
294 /*input_signing_key=*/coinbaseKey,
295 /*output_destination=*/CScript() << OP_TRUE,
296 /*output_amount=*/CAmount{1 * COIN},
297 /*submit=*/false)};
299 const uint256 stale_block_hash{CreateAndProcessBlock({unique_mtx}, coinbase_script).GetHash()};
300 BOOST_REQUIRE(txindex.BlockUntilSyncedToCurrentChain());
301
303
308
310
311 // Invalidate the block holding the unique transaction.
313 BOOST_REQUIRE(txindex.BlockUntilSyncedToCurrentChain());
314
315 // The disconnected transaction is still found, in the now-stale block.
317 {
318 LOCK(cs_main);
319 const CBlockIndex* stale_index{chainman.m_blockman.LookupBlockIndex(stale_block_hash)};
321 BOOST_CHECK(!chainman.ActiveChain().Contains(*stale_index));
322 }
323
324 // Mine the same transaction into a replacement branch, which gets a later
325 // sequence number. The lookup must now return the branch block in the active chain.
326 const uint256 branch_block_hash{CreateAndProcessBlock({unique_mtx}, CScript() << OP_TRUE).GetHash()};
327 CreateAndProcessBlock({}, coinbase_script);
328 BOOST_REQUIRE(txindex.BlockUntilSyncedToCurrentChain());
330
331 // Reorg back to the original branch. The original branch block must be
332 // now be preferred even though the replacement branch has a later sequence.
333 {
334 LOCK(cs_main);
335 chainman.ActiveChainstate().ResetBlockFailureFlags(chainman.m_blockman.LookupBlockIndex(stale_block_hash));
336 }
338 {
340 BOOST_REQUIRE(chainman.ActiveChainstate().ActivateBestChain(state));
341 }
342 BOOST_REQUIRE(txindex.BlockUntilSyncedToCurrentChain());
343 BOOST_CHECK(WITH_LOCK(cs_main, return chainman.ActiveChain().Tip()->GetBlockHash()) == stale_block_hash);
344
346
347 // Reconnecting the original block must not create duplicate entries.
350 BOOST_CHECK(reorg_bucket.front() == original_bucket.front());
351
353}
354
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
int64_t CAmount
Amount in satoshis (Can be negative)
Definition amount.h:12
constexpr CAmount COIN
The amount of satoshis in one BTC.
Definition amount.h:15
ArgsManager gArgs
Definition args.cpp:38
node::NodeContext m_node
void InvalidateBlock(ChainstateManager &chainman, const uint256 block_hash)
const CChainParams & Params()
Return the currently selected parameters.
#define Assert(val)
Identity function.
Definition check.h:119
fs::path GetDataDirNet() const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Get data directory path with appended network identifier.
Definition args.cpp:328
Nodes collect new transactions into a block, hash them into a hash tree, and scan through nonce value...
Definition block.h:27
Definition block.h:74
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
const CBlock & GenesisBlock() const
Definition chainparams.h:94
Batch of changes queued to be written to a CDBWrapper.
Definition dbwrapper.h:90
bool Read(const K &key, V &value) const
Wrapper around TryRead() that preserves the original Read() semantics: returns true on success,...
Definition dbwrapper.h:288
CDBIterator * NewIterator()
void Erase(const K &key, bool fSync=false)
Definition dbwrapper.h:317
void WriteBatch(CDBBatch &batch, bool fSync=false)
void Write(const K &key, const V &value, bool fSync=false)
Definition dbwrapper.h:300
Serialized script, used inside transaction inputs and outputs.
Definition script.h:406
The basic transaction that is broadcasted on the network and contained in blocks.
const Txid & GetHash() const LIFETIMEBOUND
Interface for managing multiple Chainstate objects, where each chainstate is associated with chainsta...
Definition validation.h:950
Chainstate & ActiveChainstate() const
Alternatives to CurrentChainstate() used by older code to query latest chainstate information without...
CChain & ActiveChain() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
node::BlockManager m_blockman
A single BlockManager instance is shared across each constructed chainstate to avoid duplicating bloc...
Double ended buffer combining vector and stream-like interfaces.
Definition streams.h:165
A custom weaker variant of SipHash-1-3 without padding, and supporting "jumbo" inputs.
Definition siphash.h:161
TxIndex is used to look up transactions included in the blockchain by hash.
Definition txindex.h:37
static CBlockLocator ReadBestBlock(const TxIndex &txindex)
static CDBWrapper & GetDB(const TxIndex &txindex)
static void WriteBestBlock(const TxIndex &txindex, const CBlockLocator &locator)
CBlockIndex * LookupBlockIndex(const uint256 &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
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
BOOST_AUTO_TEST_SUITE_END()
std::string HexStr(const std::span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
Definition hex_base.cpp:30
std::unique_ptr< Chain > MakeChain(node::NodeContext &node)
Return implementation of Chain interface.
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
TxHashKeyPrefix CreateKeyPrefix(const SipHasher13UJ &hasher, const Txid &txid)
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
uint64_t TxHashKeyPrefix
std::pair< uint8_t, uint256 > LegacyTxKey(const Txid &txid)
Key of a legacy (pre-hashing) txindex row: the full txid under the 't' prefix.
#define BOOST_CHECK_EQUAL(v1, v2)
Definition object.cpp:17
#define BOOST_CHECK(expr)
Definition object.cpp:16
static CTransactionRef MakeTransactionRef(Tx &&txIn)
const char * prefix
Definition rest.cpp:1197
@ OP_CHECKSIG
Definition script.h:191
@ OP_TRUE
Definition script.h:85
std::vector< unsigned char > ToByteVector(const T &in)
Definition script.h:68
uint64_t GetSerializeSize(const T &t)
Definition serialize.h:1157
std::vector< Byte > ParseHex(std::string_view hex_str)
Like TryParseHex, but returns an empty vector on invalid input.
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
std::vector< uint256 > vHave
Definition block.h:127
A mutable version of CTransaction.
Application-specific storage settings.
Definition dbwrapper.h:43
fs::path path
Location in the filesystem where leveldb data will be stored.
Definition dbwrapper.h:45
Testing fixture that pre-creates a 100-block REGTEST-mode block chain.
std::unique_ptr< ChainstateManager > chainman
Definition context.h:74
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
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition sync.h:299
CDBWrapper db
void StopIndex(BaseIndex &index, node::NodeContext &node)
Drain validation callbacks before stopping an index, matching Shutdown() in init.cpp.
BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup)
BOOST_AUTO_TEST_CASE(txindex_position_encoding)
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.
Definition time.h:82