Branch data Line data Source code
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>
12 : : #include <test/fuzz/FuzzedDataProvider.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 <uint256.h>
19 : : #include <validation.h>
20 : :
21 : : namespace {
22 : : constexpr uint32_t FUZZ_MAX_HEADERS_RESULTS{16};
23 : :
24 : 1 : class HeadersSyncSetup : public TestingSetup
25 : : {
26 : : std::vector<CNode*> m_connections;
27 : :
28 : : public:
29 [ + - + - ]: 2 : HeadersSyncSetup(const ChainType chain_type, TestOpts opts) : TestingSetup(chain_type, opts)
30 : : {
31 : 1 : PeerManager::Options peerman_opts;
32 [ + - ]: 1 : node::ApplyArgsManOptions(*m_node.args, peerman_opts);
33 : 1 : peerman_opts.max_headers_result = FUZZ_MAX_HEADERS_RESULTS;
34 : 2 : m_node.peerman = PeerManager::make(*m_node.connman, *m_node.addrman,
35 [ + - ]: 1 : m_node.banman.get(), *m_node.chainman,
36 [ + - ]: 1 : *m_node.mempool, *m_node.warnings, peerman_opts);
37 : :
38 : 1 : CConnman::Options options;
39 [ + - ]: 1 : options.m_msgproc = m_node.peerman.get();
40 [ + - ]: 1 : m_node.connman->Init(options);
41 : 1 : }
42 : :
43 : : void ResetAndInitialize() EXCLUSIVE_LOCKS_REQUIRED(NetEventsInterface::g_msgproc_mutex);
44 : : void SendMessage(FuzzedDataProvider& fuzzed_data_provider, CSerializedNetMsg&& msg)
45 : : EXCLUSIVE_LOCKS_REQUIRED(NetEventsInterface::g_msgproc_mutex);
46 : : };
47 : :
48 : 923 : void HeadersSyncSetup::ResetAndInitialize()
49 : : {
50 [ + + ]: 923 : m_connections.clear();
51 : 923 : auto& connman = static_cast<ConnmanTestMsg&>(*m_node.connman);
52 : 923 : connman.StopNodes();
53 : :
54 : 923 : NodeId id{0};
55 : 923 : std::vector<ConnectionType> conn_types = {
56 : : ConnectionType::OUTBOUND_FULL_RELAY,
57 : : ConnectionType::BLOCK_RELAY,
58 : : ConnectionType::INBOUND
59 : 923 : };
60 : :
61 [ + + ]: 3692 : for (auto conn_type : conn_types) {
62 [ + - ]: 2769 : CAddress addr{};
63 [ + - + - : 5538 : m_connections.push_back(new CNode(id++, nullptr, addr, 0, 0, addr, "", conn_type, false));
+ - - + -
- ]
64 : 2769 : CNode& p2p_node = *m_connections.back();
65 : :
66 [ + - ]: 2769 : connman.Handshake(
67 : : /*node=*/p2p_node,
68 : : /*successfully_connected=*/true,
69 : : /*remote_services=*/ServiceFlags(NODE_NETWORK | NODE_WITNESS),
70 : : /*local_services=*/ServiceFlags(NODE_NETWORK | NODE_WITNESS),
71 : : /*version=*/PROTOCOL_VERSION,
72 : : /*relay_txs=*/true);
73 : :
74 [ + - ]: 2769 : connman.AddTestNode(p2p_node);
75 : 2769 : }
76 [ + - ]: 3692 : }
77 : :
78 : 17363 : void HeadersSyncSetup::SendMessage(FuzzedDataProvider& fuzzed_data_provider, CSerializedNetMsg&& msg)
79 : : {
80 : 17363 : auto& connman = static_cast<ConnmanTestMsg&>(*m_node.connman);
81 : 17363 : CNode& connection = *PickValue(fuzzed_data_provider, m_connections);
82 : :
83 : 17363 : connman.FlushSendBuffer(connection);
84 : 17363 : (void)connman.ReceiveMsgFrom(connection, std::move(msg));
85 [ + - ]: 17363 : connection.fPauseSend = false;
86 : 17363 : try {
87 [ + - ]: 17363 : connman.ProcessMessagesOnce(connection);
88 [ - - ]: 0 : } catch (const std::ios_base::failure&) {
89 : 0 : }
90 : 17363 : m_node.peerman->SendMessages(&connection);
91 : 17363 : }
92 : :
93 : 130013 : CBlockHeader ConsumeHeader(FuzzedDataProvider& fuzzed_data_provider, const uint256& prev_hash, uint32_t prev_nbits)
94 : : {
95 : 130013 : CBlockHeader header;
96 : 130013 : header.nNonce = 0;
97 : : // Either use the previous difficulty or let the fuzzer choose. The upper target in the
98 : : // range comes from the bits value of the genesis block, which is 0x1d00ffff. The lower
99 : : // target comes from the bits value of mainnet block 840000, which is 0x17034219.
100 : : // Calling lower_target.SetCompact(0x17034219) and upper_target.SetCompact(0x1d00ffff)
101 : : // should return the values below.
102 : : //
103 : : // RPC commands to verify:
104 : : // getblockheader 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
105 : : // getblockheader 0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5
106 [ + + ]: 130013 : if (fuzzed_data_provider.ConsumeBool()) {
107 : 127379 : header.nBits = prev_nbits;
108 : : } else {
109 : 2634 : arith_uint256 lower_target = UintToArith256(uint256{"0000000000000000000342190000000000000000000000000000000000000000"});
110 : 2634 : arith_uint256 upper_target = UintToArith256(uint256{"00000000ffff0000000000000000000000000000000000000000000000000000"});
111 : 2634 : arith_uint256 target = ConsumeArithUInt256InRange(fuzzed_data_provider, lower_target, upper_target);
112 : 2634 : header.nBits = target.GetCompact();
113 : : }
114 : 130013 : header.nTime = ConsumeTime(fuzzed_data_provider);
115 : 130013 : header.hashPrevBlock = prev_hash;
116 : 130013 : header.nVersion = fuzzed_data_provider.ConsumeIntegral<int32_t>();
117 : 130013 : return header;
118 : : }
119 : :
120 : 9853 : CBlock ConsumeBlock(FuzzedDataProvider& fuzzed_data_provider, const uint256& prev_hash, uint32_t prev_nbits)
121 : : {
122 : 9853 : auto header = ConsumeHeader(fuzzed_data_provider, prev_hash, prev_nbits);
123 : : // In order to reach the headers acceptance logic, the block is
124 : : // constructed in a way that will pass the mutation checks.
125 : 9853 : CBlock block{header};
126 [ + - ]: 9853 : CMutableTransaction tx;
127 [ + - ]: 9853 : tx.vin.resize(1);
128 [ + - ]: 9853 : tx.vout.resize(1);
129 : 9853 : tx.vout[0].nValue = 0;
130 : 9853 : tx.vin[0].scriptSig.resize(2);
131 [ + - + - : 19706 : block.vtx.push_back(MakeTransactionRef(tx));
- + ]
132 : 9853 : block.hashMerkleRoot = block.vtx[0]->GetHash();
133 : 9853 : return block;
134 : 9853 : }
135 : :
136 : 130013 : void FinalizeHeader(CBlockHeader& header, const ChainstateManager& chainman)
137 : : {
138 [ + + ]: 261933 : while (!CheckProofOfWork(header.GetHash(), header.nBits, chainman.GetParams().GetConsensus())) {
139 : 131920 : ++(header.nNonce);
140 : : }
141 : 130013 : }
142 : :
143 : : // Global setup works for this test as state modification (specifically in the
144 : : // block index) would indicate a bug.
145 : : HeadersSyncSetup* g_testing_setup;
146 : :
147 : 1 : void initialize()
148 : : {
149 [ + - + - : 2 : static auto setup = MakeNoLogFileContext<HeadersSyncSetup>(ChainType::MAIN, {.extra_args = {"-checkpoints=0"}});
+ - ]
150 : 1 : g_testing_setup = setup.get();
151 [ + - ]: 2 : }
152 : : } // namespace
153 : :
154 [ + - ]: 1337 : FUZZ_TARGET(p2p_headers_presync, .init = initialize)
155 : : {
156 : 923 : SeedRandomStateForTest(SeedRand::ZEROS);
157 : 923 : FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
158 : 923 : SetMockTime(ConsumeTime(fuzzed_data_provider));
159 : :
160 : 923 : ChainstateManager& chainman = *g_testing_setup->m_node.chainman;
161 : :
162 : 923 : LOCK(NetEventsInterface::g_msgproc_mutex);
163 : :
164 [ + - ]: 923 : g_testing_setup->ResetAndInitialize();
165 : :
166 [ + - ]: 923 : CBlockHeader base{chainman.GetParams().GenesisBlock()};
167 [ + - ]: 923 : SetMockTime(base.nTime);
168 : :
169 : : // The chain is just a single block, so this is equal to 1
170 [ + - ]: 1846 : size_t original_index_size{WITH_LOCK(cs_main, return chainman.m_blockman.m_block_index.size())};
171 [ + - ]: 2769 : arith_uint256 total_work{WITH_LOCK(cs_main, return chainman.m_best_header->nChainWork)};
172 : :
173 : 923 : std::vector<CBlockHeader> all_headers;
174 : :
175 [ + + + + ]: 18286 : LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100)
176 : : {
177 : 27216 : auto finalized_block = [&]() {
178 : 9853 : CBlock block = ConsumeBlock(fuzzed_data_provider, base.GetHash(), base.nBits);
179 [ + - ]: 9853 : FinalizeHeader(block, chainman);
180 : 9853 : return block;
181 : 17363 : };
182 : :
183 : : // Send low-work headers, compact blocks, and blocks
184 [ + - ]: 17363 : CallOneOf(
185 : : fuzzed_data_provider,
186 : 7510 : [&]() NO_THREAD_SAFETY_ANALYSIS {
187 : : // Send FUZZ_MAX_HEADERS_RESULTS headers
188 : 7510 : std::vector<CBlock> headers;
189 [ + - ]: 7510 : headers.resize(FUZZ_MAX_HEADERS_RESULTS);
190 [ + + ]: 127670 : for (CBlock& header : headers) {
191 [ + - + - ]: 120160 : header = ConsumeHeader(fuzzed_data_provider, base.GetHash(), base.nBits);
192 [ + - ]: 120160 : FinalizeHeader(header, chainman);
193 : 120160 : base = header;
194 : : }
195 : :
196 [ + - ]: 7510 : all_headers.insert(all_headers.end(), headers.begin(), headers.end());
197 : :
198 [ + - + - ]: 7510 : auto headers_msg = NetMsg::Make(NetMsgType::HEADERS, TX_WITH_WITNESS(headers));
199 [ + - ]: 7510 : g_testing_setup->SendMessage(fuzzed_data_provider, std::move(headers_msg));
200 : 7510 : },
201 : 3652 : [&]() NO_THREAD_SAFETY_ANALYSIS {
202 : : // Send a compact block
203 : 3652 : auto block = finalized_block();
204 [ + - ]: 3652 : CBlockHeaderAndShortTxIDs cmpct_block{block, fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
205 : :
206 [ + - ]: 3652 : all_headers.push_back(block);
207 : :
208 [ + - + - ]: 3652 : auto headers_msg = NetMsg::Make(NetMsgType::CMPCTBLOCK, TX_WITH_WITNESS(cmpct_block));
209 [ + - ]: 3652 : g_testing_setup->SendMessage(fuzzed_data_provider, std::move(headers_msg));
210 : 7304 : },
211 : 6201 : [&]() NO_THREAD_SAFETY_ANALYSIS {
212 : : // Send a block
213 : 6201 : auto block = finalized_block();
214 : :
215 [ + - ]: 6201 : all_headers.push_back(block);
216 : :
217 [ + - + - ]: 6201 : auto headers_msg = NetMsg::Make(NetMsgType::BLOCK, TX_WITH_WITNESS(block));
218 [ + - ]: 6201 : g_testing_setup->SendMessage(fuzzed_data_provider, std::move(headers_msg));
219 : 6201 : });
220 : : }
221 : :
222 : : // This is a conservative overestimate, as base is only moved forward when sending headers. In theory,
223 : : // the longest chain generated by this test is 1600 (FUZZ_MAX_HEADERS_RESULTS * 100) headers. In that case,
224 : : // this variable will accurately reflect the chain's total work.
225 [ + - ]: 923 : total_work += CalculateClaimedHeadersWork(all_headers);
226 : :
227 : : // This test should never create a chain with more work than MinimumChainWork.
228 [ + - + - : 923 : assert(total_work < chainman.MinimumChainWork());
- + ]
229 : :
230 : : // The headers/blocks sent in this test should never be stored, as the chains don't have the work required
231 : : // to meet the anti-DoS work threshold. So, if at any point the block index grew in size, then there's a bug
232 : : // in the headers pre-sync logic.
233 [ + - - + ]: 1846 : assert(WITH_LOCK(cs_main, return chainman.m_blockman.m_block_index.size()) == original_index_size);
234 : :
235 [ + - ]: 923 : g_testing_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue();
236 [ + - ]: 1846 : }
|