Bitcoin Core  31.0.0
P2P Digital Currency
p2p_headers_presync.cpp
Go to the documentation of this file.
1 // Copyright (c) 2024-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 <arith_uint256.h>
6 #include <blockencodings.h>
7 #include <net.h>
8 #include <net_processing.h>
9 #include <netmessagemaker.h>
10 #include <node/peerman_args.h>
11 #include <pow.h>
13 #include <test/fuzz/fuzz.h>
14 #include <test/fuzz/util.h>
15 #include <test/util/net.h>
16 #include <test/util/script.h>
17 #include <test/util/setup_common.h>
18 #include <test/util/time.h>
19 #include <uint256.h>
20 #include <validation.h>
21 
22 namespace {
23 constexpr uint32_t FUZZ_MAX_HEADERS_RESULTS{16};
24 
25 class HeadersSyncSetup : public TestingSetup
26 {
27  std::vector<CNode*> m_connections;
28 
29 public:
30  HeadersSyncSetup(const ChainType chain_type, TestOpts opts) : TestingSetup(chain_type, opts)
31  {
32  PeerManager::Options peerman_opts;
33  node::ApplyArgsManOptions(*m_node.args, peerman_opts);
34  peerman_opts.max_headers_result = FUZZ_MAX_HEADERS_RESULTS;
35  // The peerman's rng is a global that is reused, so it will be reused
36  // and may cause non-determinism between runs. This may even influence
37  // the global RNG, because seeding may be done from the global one. For
38  // now, avoid it influencing the global RNG, and initialize it with a
39  // constant instead.
40  peerman_opts.deterministic_rng = true;
41  // No txs are relayed. Disable irrelevant and possibly
42  // non-deterministic code paths.
43  peerman_opts.ignore_incoming_txs = true;
45  m_node.banman.get(), *m_node.chainman,
46  *m_node.mempool, *m_node.warnings, peerman_opts);
47 
48  CConnman::Options options;
49  options.m_msgproc = m_node.peerman.get();
50  m_node.connman->Init(options);
51  }
52 
53  void ResetAndInitialize() EXCLUSIVE_LOCKS_REQUIRED(NetEventsInterface::g_msgproc_mutex);
56 };
57 
58 void HeadersSyncSetup::ResetAndInitialize()
59 {
60  m_connections.clear();
61  auto& connman = static_cast<ConnmanTestMsg&>(*m_node.connman);
62  connman.StopNodes();
63 
64  static NodeId id{0};
65  std::vector<ConnectionType> conn_types = {
69  };
70 
71  for (auto conn_type : conn_types) {
72  CAddress addr{};
73  m_connections.push_back(new CNode(id++, nullptr, addr, 0, 0, addr, "", conn_type, false, 0));
74  CNode& p2p_node = *m_connections.back();
75 
76  connman.Handshake(
77  /*node=*/p2p_node,
78  /*successfully_connected=*/true,
79  /*remote_services=*/ServiceFlags(NODE_NETWORK | NODE_WITNESS),
80  /*local_services=*/ServiceFlags(NODE_NETWORK | NODE_WITNESS),
81  /*version=*/PROTOCOL_VERSION,
82  /*relay_txs=*/true);
83 
84  connman.AddTestNode(p2p_node);
85  }
86 }
87 
88 void HeadersSyncSetup::SendMessage(FuzzedDataProvider& fuzzed_data_provider, CSerializedNetMsg&& msg)
89 {
90  auto& connman = static_cast<ConnmanTestMsg&>(*m_node.connman);
91  CNode& connection = *PickValue(fuzzed_data_provider, m_connections);
92 
93  connman.FlushSendBuffer(connection);
94  (void)connman.ReceiveMsgFrom(connection, std::move(msg));
95  connection.fPauseSend = false;
96  try {
97  connman.ProcessMessagesOnce(connection);
98  } catch (const std::ios_base::failure&) {
99  }
100  m_node.peerman->SendMessages(connection);
101 }
102 
103 CBlockHeader ConsumeHeader(FuzzedDataProvider& fuzzed_data_provider, const uint256& prev_hash, uint32_t prev_nbits)
104 {
105  CBlockHeader header;
106  header.nNonce = 0;
107  // Either use the previous difficulty or let the fuzzer choose. The upper target in the
108  // range comes from the bits value of the genesis block, which is 0x1d00ffff. The lower
109  // target comes from the bits value of mainnet block 840000, which is 0x17034219.
110  // Calling lower_target.SetCompact(0x17034219) and upper_target.SetCompact(0x1d00ffff)
111  // should return the values below.
112  //
113  // RPC commands to verify:
114  // getblockheader 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
115  // getblockheader 0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5
117  header.nBits = prev_nbits;
118  } else {
119  arith_uint256 lower_target = UintToArith256(uint256{"0000000000000000000342190000000000000000000000000000000000000000"});
120  arith_uint256 upper_target = UintToArith256(uint256{"00000000ffff0000000000000000000000000000000000000000000000000000"});
121  arith_uint256 target = ConsumeArithUInt256InRange(fuzzed_data_provider, lower_target, upper_target);
122  header.nBits = target.GetCompact();
123  }
124  header.nTime = TicksSinceEpoch<std::chrono::seconds>(ConsumeTime(fuzzed_data_provider));
125  header.hashPrevBlock = prev_hash;
126  header.nVersion = fuzzed_data_provider.ConsumeIntegral<int32_t>();
127  return header;
128 }
129 
130 CBlock ConsumeBlock(FuzzedDataProvider& fuzzed_data_provider, const uint256& prev_hash, uint32_t prev_nbits)
131 {
132  auto header = ConsumeHeader(fuzzed_data_provider, prev_hash, prev_nbits);
133  // In order to reach the headers acceptance logic, the block is
134  // constructed in a way that will pass the mutation checks.
135  CBlock block{header};
137  tx.vin.resize(1);
138  tx.vout.resize(1);
139  tx.vout[0].nValue = 0;
140  tx.vin[0].scriptSig.resize(2);
141  block.vtx.push_back(MakeTransactionRef(tx));
142  block.hashMerkleRoot = block.vtx[0]->GetHash().ToUint256();
143  return block;
144 }
145 
146 void FinalizeHeader(CBlockHeader& header, const ChainstateManager& chainman)
147 {
148  while (!CheckProofOfWork(header.GetHash(), header.nBits, chainman.GetParams().GetConsensus())) {
149  ++(header.nNonce);
150  }
151 }
152 
153 // Global setup works for this test as state modification (specifically in the
154 // block index) would indicate a bug.
155 HeadersSyncSetup* g_testing_setup;
156 
157 void initialize()
158 {
159  static auto setup{
160  MakeNoLogFileContext<HeadersSyncSetup>(ChainType::MAIN,
161  {
162  .setup_validation_interface = false,
163  }),
164  };
165  g_testing_setup = setup.get();
166 }
167 } // namespace
168 
169 FUZZ_TARGET(p2p_headers_presync, .init = initialize)
170 {
172  FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
173  // The steady clock is currently only used for logging, so a constant
174  // time-point seems acceptable for now.
175  SteadyClockContext steady_ctx{};
176 
177  ChainstateManager& chainman = *g_testing_setup->m_node.chainman;
178  CBlockHeader base{chainman.GetParams().GenesisBlock()};
179  SetMockTime(base.nTime);
180 
182 
183  g_testing_setup->ResetAndInitialize();
184 
185  // The chain is just a single block, so this is equal to 1
186  size_t original_index_size{WITH_LOCK(cs_main, return chainman.m_blockman.m_block_index.size())};
187  arith_uint256 total_work{WITH_LOCK(cs_main, return chainman.m_best_header->nChainWork)};
188 
189  std::vector<CBlockHeader> all_headers;
190 
192  {
193  auto finalized_block = [&]() {
194  CBlock block = ConsumeBlock(fuzzed_data_provider, base.GetHash(), base.nBits);
195  FinalizeHeader(block, chainman);
196  return block;
197  };
198 
199  // Send low-work headers, compact blocks, and blocks
200  CallOneOf(
203  // Send FUZZ_MAX_HEADERS_RESULTS headers
204  std::vector<CBlock> headers;
205  headers.resize(FUZZ_MAX_HEADERS_RESULTS);
206  for (CBlock& header : headers) {
207  header = ConsumeHeader(fuzzed_data_provider, base.GetHash(), base.nBits);
208  FinalizeHeader(header, chainman);
209  base = header;
210  }
211 
212  all_headers.insert(all_headers.end(), headers.begin(), headers.end());
213 
214  auto headers_msg = NetMsg::Make(NetMsgType::HEADERS, TX_WITH_WITNESS(headers));
215  g_testing_setup->SendMessage(fuzzed_data_provider, std::move(headers_msg));
216  },
218  // Send a compact block
219  auto block = finalized_block();
220  CBlockHeaderAndShortTxIDs cmpct_block{block, fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
221 
222  all_headers.push_back(block);
223 
224  auto headers_msg = NetMsg::Make(NetMsgType::CMPCTBLOCK, TX_WITH_WITNESS(cmpct_block));
225  g_testing_setup->SendMessage(fuzzed_data_provider, std::move(headers_msg));
226  },
228  // Send a block
229  auto block = finalized_block();
230 
231  all_headers.push_back(block);
232 
233  auto headers_msg = NetMsg::Make(NetMsgType::BLOCK, TX_WITH_WITNESS(block));
234  g_testing_setup->SendMessage(fuzzed_data_provider, std::move(headers_msg));
235  });
236  }
237 
238  // This is a conservative overestimate, as base is only moved forward when sending headers. In theory,
239  // the longest chain generated by this test is 1600 (FUZZ_MAX_HEADERS_RESULTS * 100) headers. In that case,
240  // this variable will accurately reflect the chain's total work.
241  total_work += CalculateClaimedHeadersWork(all_headers);
242 
243  // This test should never create a chain with more work than MinimumChainWork.
244  assert(total_work < chainman.MinimumChainWork());
245 
246  // The headers/blocks sent in this test should never be stored, as the chains don't have the work required
247  // to meet the anti-DoS work threshold. So, if at any point the block index grew in size, then there's a bug
248  // in the headers pre-sync logic.
249  assert(WITH_LOCK(cs_main, return chainman.m_blockman.m_block_index.size()) == original_index_size);
250 }
uint32_t nNonce
Definition: block.h:35
static Mutex g_msgproc_mutex
Mutex for anything that is only accessed via the msg processing thread.
Definition: net.h:1031
#define NO_THREAD_SAFETY_ANALYSIS
Definition: threadsafety.h:53
node::BlockManager m_blockman
A single BlockManager instance is shared across each constructed chainstate to avoid duplicating bloc...
Definition: validation.h:1038
std::atomic_bool fPauseSend
Definition: net.h:744
std::unique_ptr< node::Warnings > warnings
Manages all the node warnings.
Definition: context.h:91
ServiceFlags
nServices flags
Definition: protocol.h:309
Inbound connections are those initiated by a peer.
assert(!tx.IsCoinBase())
Definition: block.h:73
Interface for managing multiple Chainstate objects, where each chainstate is associated with chainsta...
Definition: validation.h:939
node::NodeContext m_node
Definition: bitcoin-gui.cpp:43
FUZZ_TARGET(p2p_headers_presync,.init=initialize)
std::vector< CTxIn > vin
Definition: transaction.h:359
static std::unique_ptr< PeerManager > make(CConnman &connman, AddrMan &addrman, BanMan *banman, ChainstateManager &chainman, CTxMemPool &pool, node::Warnings &warnings, Options opts)
std::unique_ptr< AddrMan > addrman
Definition: context.h:66
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:22
uint32_t max_headers_result
Number of headers sent in one getheaders message result (this is a test-only option).
void StopNodes() EXCLUSIVE_LOCKS_REQUIRED(!m_reconnections_mutex)
Definition: net.cpp:3633
constexpr const char * HEADERS
The headers message sends one or more block headers to a node which previously requested certain head...
Definition: protocol.h:123
Interface for message handling.
Definition: net.h:1027
const CBlock & GenesisBlock() const
Definition: chainparams.h:94
Helper to initialize the global MockableSteadyClock, let a duration elapse, and reset it after use in...
Definition: time.h:14
static void initialize()
Definition: fuzz.cpp:95
util::Result< void > ApplyArgsManOptions(const ArgsManager &args, BlockManager::Options &opts)
These are the default connections that we use to connect with the network.
uint32_t nTime
Definition: block.h:33
CSerializedNetMsg Make(std::string msg_type, Args &&... args)
arith_uint256 UintToArith256(const uint256 &a)
std::unique_ptr< CTxMemPool > mempool
Definition: context.h:68
std::unique_ptr< BanMan > banman
Definition: context.h:73
bool deterministic_rng
Whether or not the internal RNG behaves deterministically (this is a test-only option).
ChainType
Definition: chaintype.h:11
#define LOCK(cs)
Definition: sync.h:258
ArgsManager * args
Definition: context.h:74
NetEventsInterface * m_msgproc
Definition: net.h:1080
uint256 hashPrevBlock
Definition: block.h:31
arith_uint256 ConsumeArithUInt256InRange(FuzzedDataProvider &fuzzed_data_provider, const arith_uint256 &min, const arith_uint256 &max) noexcept
Definition: util.h:181
A CService with information about it as peer.
Definition: protocol.h:366
arith_uint256 CalculateClaimedHeadersWork(std::span< const CBlockHeader > headers)
Return the sum of the claimed work on a given set of headers.
static const int PROTOCOL_VERSION
network protocol versioning
int64_t NodeId
Definition: net.h:103
constexpr const char * BLOCK
The block message transmits a single serialized block.
Definition: protocol.h:127
bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params &params)
Check whether a block hash satisfies the proof-of-work requirement specified by nBits.
Definition: pow.cpp:140
std::vector< CTxOut > vout
Definition: transaction.h:360
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:289
256-bit unsigned big integer.
NodeSeconds ConsumeTime(FuzzedDataProvider &fuzzed_data_provider, const std::optional< int64_t > &min, const std::optional< int64_t > &max) noexcept
Definition: util.cpp:34
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:404
uint256 GetHash() const
Definition: block.cpp:15
constexpr const char * CMPCTBLOCK
Contains a CBlockHeaderAndShortTxIDs object - providing a header and list of "short txids"...
Definition: protocol.h:206
const CChainParams & GetParams() const
Definition: validation.h:1007
void SeedRandomStateForTest(SeedRand seedtype)
Seed the global RNG state for testing and log the seed value.
Definition: random.cpp:19
256-bit opaque blob.
Definition: uint256.h:195
const arith_uint256 & MinimumChainWork() const
Definition: validation.h:1010
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:51
FuzzedDataProvider & fuzzed_data_provider
Definition: fees.cpp:38
static int setup(void)
Definition: tests.c:7808
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition: time.cpp:44
A mutable version of CTransaction.
Definition: transaction.h:357
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
std::unique_ptr< CConnman > connman
Definition: context.h:67
Information about a peer.
Definition: net.h:679
const Consensus::Params & GetConsensus() const
Definition: chainparams.h:89
Seed with a compile time constant of zeros.
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate...
Definition: cs_main.cpp:8
int32_t nVersion
Definition: block.h:30
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
bool ignore_incoming_txs
Whether this node is running in -blocksonly mode.
We use block-relay-only connections to help prevent against partition attacks.
uint32_t nBits
Definition: block.h:34
static constexpr TransactionSerParams TX_WITH_WITNESS
Definition: transaction.h:180
std::unique_ptr< PeerManager > peerman
Definition: context.h:71