Bitcoin Core  31.0.0
P2P Digital Currency
block_index_tree.cpp
Go to the documentation of this file.
1 // Copyright (c) 2020-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 
6 #include <chain.h>
7 #include <chainparams.h>
8 #include <flatfile.h>
9 #include <primitives/block.h>
10 #include <primitives/transaction.h>
12 #include <test/fuzz/fuzz.h>
13 #include <test/fuzz/util.h>
14 #include <test/util/setup_common.h>
15 #include <test/util/validation.h>
16 #include <validation.h>
17 
18 #include <ranges>
19 #include <vector>
20 
22 
23 CBlockHeader ConsumeBlockHeader(FuzzedDataProvider& provider, uint256 prev_hash, int& nonce_counter)
24 {
25  CBlockHeader header;
26  header.nVersion = provider.ConsumeIntegral<decltype(header.nVersion)>();
27  header.hashPrevBlock = prev_hash;
28  header.hashMerkleRoot = uint256{}; // never used
29  header.nTime = provider.ConsumeIntegral<decltype(header.nTime)>();
30  header.nBits = Params().GenesisBlock().nBits; // not fuzzed because not used (validation is mocked).
31  header.nNonce = nonce_counter++; // prevent creating multiple block headers with the same hash
32  return header;
33 }
34 
36 {
37  static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
38  g_setup = testing_setup.get();
39 }
40 
42 {
43  FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
45  auto& chainman = static_cast<TestChainstateManager&>(*g_setup->m_node.chainman);
46  auto& blockman = static_cast<TestBlockManager&>(chainman.m_blockman);
47  CBlockIndex* genesis = chainman.ActiveChainstate().m_chain[0];
48  int nonce_counter = 0;
49  std::vector<CBlockIndex*> blocks;
50  blocks.push_back(genesis);
51  bool abort_run{false};
52 
53  std::vector<CBlockIndex*> pruned_blocks;
54 
56  {
57  if (abort_run) break;
58  CallOneOf(
60  [&] {
61  // Receive a header building on an existing valid one. This assumes headers are valid, so PoW is not relevant here.
62  LOCK(cs_main);
63  CBlockIndex* prev_block = PickValue(fuzzed_data_provider, blocks);
64  if (!(prev_block->nStatus & BLOCK_FAILED_VALID)) {
65  CBlockHeader header = ConsumeBlockHeader(fuzzed_data_provider, prev_block->GetBlockHash(), nonce_counter);
66  CBlockIndex* index = blockman.AddToBlockIndex(header, chainman.m_best_header);
67  assert(index->nStatus & BLOCK_VALID_TREE);
68  assert(index->pprev == prev_block);
69  blocks.push_back(index);
70  }
71  },
72  [&] {
73  // Receive a full block (valid or invalid) for an existing header, but don't attempt to connect it yet
74  LOCK(cs_main);
75  CBlockIndex* index = PickValue(fuzzed_data_provider, blocks);
76  // Must be new to us and not known to be invalid (e.g. because of an invalid ancestor).
77  if (index->nTx == 0 && !(index->nStatus & BLOCK_FAILED_VALID)) {
78  if (fuzzed_data_provider.ConsumeBool()) { // Invalid
80  state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "consensus-invalid");
81  chainman.InvalidBlockFound(index, state);
82  } else {
83  size_t nTx = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 1000);
84  CBlock block; // Dummy block, so that ReceivedBlockTransactions can infer a nTx value.
85  block.vtx = std::vector<CTransactionRef>(nTx);
87  chainman.ReceivedBlockTransactions(block, index, pos);
88  assert(index->nStatus & BLOCK_VALID_TRANSACTIONS);
89  assert(index->nStatus & BLOCK_HAVE_DATA);
90  }
91  }
92  },
93  [&] {
94  // Simplified ActivateBestChain(): Try to move to a chain with more work - with the possibility of finding blocks to be invalid on the way
95  LOCK(cs_main);
96  auto& chain = chainman.ActiveChain();
97  CBlockIndex* old_tip = chain.Tip();
98  assert(old_tip);
99  do {
100  CBlockIndex* best_tip = chainman.FindMostWorkChain();
101  assert(best_tip); // Should at least return current tip
102  if (best_tip == chain.Tip()) break; // Nothing to do
103  // Rewind chain to forking point
104  const CBlockIndex* fork = chain.FindFork(best_tip);
105  // If we can't go back to the fork point due to pruned data, abort this run. In reality, a pruned node would also currently just crash in this scenario.
106  // This is very unlikely to happen due to the minimum pruning threshold of 550MiB.
107  CBlockIndex* it = chain.Tip();
108  while (it && it->nHeight != fork->nHeight) {
109  if (!(it->nStatus & BLOCK_HAVE_UNDO)) {
110  assert(blockman.m_have_pruned);
111  abort_run = true;
112  return;
113  }
114  it = it->pprev;
115  }
116  chain.SetTip(*chain[fork->nHeight]);
117 
118  // Prepare new blocks to connect
119  std::vector<CBlockIndex*> to_connect;
120  it = best_tip;
121  while (it && it->nHeight != fork->nHeight) {
122  to_connect.push_back(it);
123  it = it->pprev;
124  }
125  // Connect blocks, possibly fail
126  for (CBlockIndex* block : to_connect | std::views::reverse) {
127  assert(!(block->nStatus & BLOCK_FAILED_VALID));
128  assert(block->nStatus & BLOCK_HAVE_DATA);
129  if (!block->IsValid(BLOCK_VALID_SCRIPTS)) {
130  if (fuzzed_data_provider.ConsumeBool()) { // Invalid
131  BlockValidationState state;
132  state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "consensus-invalid");
133  chainman.InvalidBlockFound(block, state);
134  // This results in duplicate calls to InvalidChainFound, but mirrors the behavior in validation
135  chainman.InvalidChainFound(to_connect.front());
136  break;
137  } else {
138  block->RaiseValidity(BLOCK_VALID_SCRIPTS);
139  block->nStatus |= BLOCK_HAVE_UNDO;
140  }
141  }
142  chain.SetTip(*block);
143  chainman.ActiveChainstate().PruneBlockIndexCandidates();
144  // ActivateBestChainStep may release cs_main / not connect all blocks in one go - but only if we have at least as much chain work as we had at the start.
145  if (block->nChainWork > old_tip->nChainWork && fuzzed_data_provider.ConsumeBool()) {
146  break;
147  }
148  }
149  } while (node::CBlockIndexWorkComparator()(chain.Tip(), old_tip));
150  assert(chain.Tip()->nChainWork >= old_tip->nChainWork);
151  },
152  [&] {
153  // Prune chain - dealing with block files is beyond the scope of this test, so just prune random blocks, making no assumptions
154  // about what blocks are pruned together because they are in the same block file.
155  // Also don't prune blocks outside of the chain for now - this would make the fuzzer crash because of the problem described in
156  // https://github.com/bitcoin/bitcoin/issues/31512
157  LOCK(cs_main);
158  auto& chain = chainman.ActiveChain();
159  int prune_height = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, chain.Height());
160  CBlockIndex* prune_block{chain[prune_height]};
161  if (prune_block != chain.Tip() && (prune_block->nStatus & BLOCK_HAVE_DATA)) {
162  blockman.m_have_pruned = true;
163  prune_block->nStatus &= ~BLOCK_HAVE_DATA;
164  prune_block->nStatus &= ~BLOCK_HAVE_UNDO;
165  prune_block->nFile = 0;
166  prune_block->nDataPos = 0;
167  prune_block->nUndoPos = 0;
168  auto range = blockman.m_blocks_unlinked.equal_range(prune_block->pprev);
169  while (range.first != range.second) {
170  std::multimap<CBlockIndex*, CBlockIndex*>::iterator _it = range.first;
171  range.first++;
172  if (_it->second == prune_block) {
173  blockman.m_blocks_unlinked.erase(_it);
174  }
175  }
176  pruned_blocks.push_back(prune_block);
177  }
178  },
179  [&] {
180  // Download a previously pruned block
181  LOCK(cs_main);
182  size_t num_pruned = pruned_blocks.size();
183  if (num_pruned == 0) return;
184  size_t i = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, num_pruned - 1);
185  CBlockIndex* index = pruned_blocks[i];
186  assert(!(index->nStatus & BLOCK_HAVE_DATA));
187  CBlock block;
188  block.vtx = std::vector<CTransactionRef>(index->nTx); // Set the number of tx to the prior value.
190  chainman.ReceivedBlockTransactions(block, index, pos);
191  assert(index->nStatus & BLOCK_VALID_TRANSACTIONS);
192  assert(index->nStatus & BLOCK_HAVE_DATA);
193  pruned_blocks.erase(pruned_blocks.begin() + i);
194  });
195  }
196  if (!abort_run) {
197  chainman.CheckBlockIndex();
198  }
199 
200  // clean up global state changed by last iteration and prepare for next iteration
201  {
202  LOCK(cs_main);
203  genesis->nStatus |= BLOCK_HAVE_DATA;
204  genesis->nStatus |= BLOCK_HAVE_UNDO;
205  chainman.m_best_header = genesis;
206  chainman.ResetBestInvalid();
207  chainman.nBlockSequenceId = 2;
208  chainman.ActiveChain().SetTip(*genesis);
209  chainman.ActiveChainstate().setBlockIndexCandidates.clear();
210  chainman.m_cached_is_ibd = true;
211  blockman.m_blocks_unlinked.clear();
212  blockman.m_have_pruned = false;
213  blockman.CleanupForFuzzing();
214  // Delete all blocks but Genesis from block index
215  uint256 genesis_hash = genesis->GetBlockHash();
216  for (auto it = blockman.m_block_index.begin(); it != blockman.m_block_index.end();) {
217  if (it->first != genesis_hash) {
218  it = blockman.m_block_index.erase(it);
219  } else {
220  ++it;
221  }
222  }
223  chainman.ActiveChainstate().TryAddBlockIndexCandidate(genesis);
224  assert(blockman.m_block_index.size() == 1);
225  assert(chainman.ActiveChainstate().setBlockIndexCandidates.size() == 1);
226  assert(chainman.ActiveChain().Height() == 0);
227  }
228 }
uint32_t nNonce
Definition: block.h:35
arith_uint256 nChainWork
(memory only) Total amount of work (expected number of hashes) in the chain up to and including this ...
Definition: chain.h:118
assert(!tx.IsCoinBase())
CBlockIndex * pprev
pointer to the index of the predecessor of this block
Definition: chain.h:100
Definition: block.h:73
All parent headers found, difficulty matches, timestamp >= median previous.
Definition: chain.h:51
stage after last reached validness failed
Definition: chain.h:79
Only first tx is coinbase, 2 <= coinbase input script length <= 100, transactions valid...
Definition: chain.h:61
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:22
const CBlock & GenesisBlock() const
Definition: chainparams.h:94
const TestingSetup * g_setup
CBlockHeader ConsumeBlockHeader(FuzzedDataProvider &provider, uint256 prev_hash, int &nonce_counter)
undo data available in rev*.dat
Definition: chain.h:76
FUZZ_TARGET(block_index_tree,.init=initialize_block_index_tree)
uint32_t nTime
Definition: block.h:33
uint256 GetBlockHash() const
Definition: chain.h:198
bool Invalid(Result result, const std::string &reject_reason="", const std::string &debug_message="")
Definition: validation.h:88
Scripts & signatures ok.
Definition: chain.h:69
uint256 hashMerkleRoot
Definition: block.h:32
#define LOCK(cs)
Definition: sync.h:258
uint256 hashPrevBlock
Definition: block.h:31
void initialize_block_index_tree()
NodeSeconds ConsumeTime(FuzzedDataProvider &fuzzed_data_provider, const std::optional< int64_t > &min, const std::optional< int64_t > &max) noexcept
Definition: util.cpp:34
256-bit opaque blob.
Definition: uint256.h:195
invalid by consensus rules (excluding any below reasons)
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:93
const CChainParams & Params()
Return the currently selected parameters.
FuzzedDataProvider & fuzzed_data_provider
Definition: fees.cpp:38
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition: time.cpp:44
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition: util.h:35
auto & PickValue(FuzzedDataProvider &fuzzed_data_provider, Collection &col)
Definition: util.h:47
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: chain.h:106
T ConsumeIntegralInRange(T min, T max)
full block available in blk*.dat
Definition: chain.h:75
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate...
Definition: cs_main.cpp:8
node::NodeContext m_node
Definition: setup_common.h:66
int32_t nVersion
Definition: block.h:30
unsigned int nTx
Number of transactions in this block.
Definition: chain.h:123
Nodes collect new transactions into a block, hash them into a hash tree, and scan through nonce value...
Definition: block.h:26
std::unique_ptr< ChainstateManager > chainman
Definition: context.h:72
Testing setup that configures a complete environment.
Definition: setup_common.h:121
uint32_t nBits
Definition: block.h:34