LCOV - code coverage report
Current view: top level - src/test/fuzz - p2p_headers_presync.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 98.4 % 122 120
Test Date: 2025-05-10 04:08:03 Functions: 100.0 % 16 16
Branches: 53.8 % 130 70

             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 <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                 :           1 : class HeadersSyncSetup : public TestingSetup
      26                 :             : {
      27                 :             :     std::vector<CNode*> m_connections;
      28                 :             : 
      29                 :             : public:
      30   [ +  -  +  - ]:           2 :     HeadersSyncSetup(const ChainType chain_type, TestOpts opts) : TestingSetup(chain_type, opts)
      31                 :             :     {
      32                 :           1 :         PeerManager::Options peerman_opts;
      33         [ +  - ]:           1 :         node::ApplyArgsManOptions(*m_node.args, peerman_opts);
      34                 :           1 :         peerman_opts.max_headers_result = FUZZ_MAX_HEADERS_RESULTS;
      35                 :             :         // The peerman's rng is a global that is re-used, so it will be re-used
      36                 :             :         // and may cause non-determinism between runs. This may even influence
      37                 :             :         // the global RNG, because seeding may be done from the gloabl one. For
      38                 :             :         // now, avoid it influencing the global RNG, and initialize it with a
      39                 :             :         // constant instead.
      40                 :           1 :         peerman_opts.deterministic_rng = true;
      41                 :             :         // No txs are relayed. Disable irrelevant and possibly
      42                 :             :         // non-deterministic code paths.
      43                 :           1 :         peerman_opts.ignore_incoming_txs = true;
      44                 :           2 :         m_node.peerman = PeerManager::make(*m_node.connman, *m_node.addrman,
      45         [ +  - ]:           1 :                                            m_node.banman.get(), *m_node.chainman,
      46         [ +  - ]:           1 :                                            *m_node.mempool, *m_node.warnings, peerman_opts);
      47                 :             : 
      48                 :           1 :         CConnman::Options options;
      49         [ +  - ]:           1 :         options.m_msgproc = m_node.peerman.get();
      50         [ +  - ]:           1 :         m_node.connman->Init(options);
      51                 :           1 :     }
      52                 :             : 
      53                 :             :     void ResetAndInitialize() EXCLUSIVE_LOCKS_REQUIRED(NetEventsInterface::g_msgproc_mutex);
      54                 :             :     void SendMessage(FuzzedDataProvider& fuzzed_data_provider, CSerializedNetMsg&& msg)
      55                 :             :         EXCLUSIVE_LOCKS_REQUIRED(NetEventsInterface::g_msgproc_mutex);
      56                 :             : };
      57                 :             : 
      58                 :         618 : void HeadersSyncSetup::ResetAndInitialize()
      59                 :             : {
      60         [ +  + ]:         618 :     m_connections.clear();
      61                 :         618 :     auto& connman = static_cast<ConnmanTestMsg&>(*m_node.connman);
      62                 :         618 :     connman.StopNodes();
      63                 :             : 
      64                 :         618 :     static NodeId id{0};
      65                 :         618 :     std::vector<ConnectionType> conn_types = {
      66                 :             :         ConnectionType::OUTBOUND_FULL_RELAY,
      67                 :             :         ConnectionType::BLOCK_RELAY,
      68                 :             :         ConnectionType::INBOUND
      69                 :         618 :     };
      70                 :             : 
      71         [ +  + ]:        2472 :     for (auto conn_type : conn_types) {
      72         [ +  - ]:        1854 :         CAddress addr{};
      73   [ +  -  +  -  :        3708 :         m_connections.push_back(new CNode(id++, nullptr, addr, 0, 0, addr, "", conn_type, false));
          +  -  -  +  -  
                      - ]
      74                 :        1854 :         CNode& p2p_node = *m_connections.back();
      75                 :             : 
      76         [ +  - ]:        1854 :         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         [ +  - ]:        1854 :         connman.AddTestNode(p2p_node);
      85                 :        1854 :     }
      86         [ +  - ]:        2472 : }
      87                 :             : 
      88                 :       21178 : void HeadersSyncSetup::SendMessage(FuzzedDataProvider& fuzzed_data_provider, CSerializedNetMsg&& msg)
      89                 :             : {
      90                 :       21178 :     auto& connman = static_cast<ConnmanTestMsg&>(*m_node.connman);
      91                 :       21178 :     CNode& connection = *PickValue(fuzzed_data_provider, m_connections);
      92                 :             : 
      93                 :       21178 :     connman.FlushSendBuffer(connection);
      94                 :       21178 :     (void)connman.ReceiveMsgFrom(connection, std::move(msg));
      95         [ +  - ]:       21178 :     connection.fPauseSend = false;
      96                 :       21178 :     try {
      97         [ +  - ]:       21178 :         connman.ProcessMessagesOnce(connection);
      98         [ -  - ]:           0 :     } catch (const std::ios_base::failure&) {
      99                 :           0 :     }
     100                 :       21178 :     m_node.peerman->SendMessages(&connection);
     101                 :       21178 : }
     102                 :             : 
     103                 :      147808 : CBlockHeader ConsumeHeader(FuzzedDataProvider& fuzzed_data_provider, const uint256& prev_hash, uint32_t prev_nbits)
     104                 :             : {
     105                 :      147808 :     CBlockHeader header;
     106                 :      147808 :     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
     116         [ +  + ]:      147808 :     if (fuzzed_data_provider.ConsumeBool()) {
     117                 :      142046 :         header.nBits = prev_nbits;
     118                 :             :     } else {
     119                 :        5762 :         arith_uint256 lower_target = UintToArith256(uint256{"0000000000000000000342190000000000000000000000000000000000000000"});
     120                 :        5762 :         arith_uint256 upper_target = UintToArith256(uint256{"00000000ffff0000000000000000000000000000000000000000000000000000"});
     121                 :        5762 :         arith_uint256 target = ConsumeArithUInt256InRange(fuzzed_data_provider, lower_target, upper_target);
     122                 :        5762 :         header.nBits = target.GetCompact();
     123                 :             :     }
     124                 :      147808 :     header.nTime = ConsumeTime(fuzzed_data_provider);
     125                 :      147808 :     header.hashPrevBlock = prev_hash;
     126                 :      147808 :     header.nVersion = fuzzed_data_provider.ConsumeIntegral<int32_t>();
     127                 :      147808 :     return header;
     128                 :             : }
     129                 :             : 
     130                 :       12736 : CBlock ConsumeBlock(FuzzedDataProvider& fuzzed_data_provider, const uint256& prev_hash, uint32_t prev_nbits)
     131                 :             : {
     132                 :       12736 :     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                 :       12736 :     CBlock block{header};
     136         [ +  - ]:       12736 :     CMutableTransaction tx;
     137         [ +  - ]:       12736 :     tx.vin.resize(1);
     138         [ +  - ]:       12736 :     tx.vout.resize(1);
     139                 :       12736 :     tx.vout[0].nValue = 0;
     140                 :       12736 :     tx.vin[0].scriptSig.resize(2);
     141   [ +  -  +  -  :       25472 :     block.vtx.push_back(MakeTransactionRef(tx));
                   -  + ]
     142                 :       12736 :     block.hashMerkleRoot = block.vtx[0]->GetHash();
     143                 :       12736 :     return block;
     144                 :       12736 : }
     145                 :             : 
     146                 :      147808 : void FinalizeHeader(CBlockHeader& header, const ChainstateManager& chainman)
     147                 :             : {
     148         [ +  + ]:      295002 :     while (!CheckProofOfWork(header.GetHash(), header.nBits, chainman.GetParams().GetConsensus())) {
     149                 :      147194 :         ++(header.nNonce);
     150                 :             :     }
     151                 :      147808 : }
     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                 :           1 : void initialize()
     158                 :             : {
     159                 :           1 :     static auto setup{
     160                 :             :         MakeNoLogFileContext<HeadersSyncSetup>(ChainType::MAIN,
     161                 :             :                                                {
     162                 :             :                                                    .setup_validation_interface = false,
     163                 :             :                                                }),
     164   [ +  -  +  - ]:           2 :     };
     165                 :           1 :     g_testing_setup = setup.get();
     166         [ +  - ]:           2 : }
     167                 :             : } // namespace
     168                 :             : 
     169         [ +  - ]:        1062 : FUZZ_TARGET(p2p_headers_presync, .init = initialize)
     170                 :             : {
     171                 :         618 :     SeedRandomStateForTest(SeedRand::ZEROS);
     172                 :         618 :     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                 :         618 :     ElapseSteady elapse_steady{};
     176                 :             : 
     177                 :         618 :     ChainstateManager& chainman = *g_testing_setup->m_node.chainman;
     178                 :         618 :     CBlockHeader base{chainman.GetParams().GenesisBlock()};
     179                 :         618 :     SetMockTime(base.nTime);
     180                 :             : 
     181                 :         618 :     LOCK(NetEventsInterface::g_msgproc_mutex);
     182                 :             : 
     183         [ +  - ]:         618 :     g_testing_setup->ResetAndInitialize();
     184                 :             : 
     185                 :             :     // The chain is just a single block, so this is equal to 1
     186         [ +  - ]:        1236 :     size_t original_index_size{WITH_LOCK(cs_main, return chainman.m_blockman.m_block_index.size())};
     187         [ +  - ]:        1854 :     arith_uint256 total_work{WITH_LOCK(cs_main, return chainman.m_best_header->nChainWork)};
     188                 :             : 
     189                 :         618 :     std::vector<CBlockHeader> all_headers;
     190                 :             : 
     191   [ +  +  +  + ]:       21796 :     LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100)
     192                 :             :     {
     193                 :       33914 :         auto finalized_block = [&]() {
     194                 :       12736 :             CBlock block = ConsumeBlock(fuzzed_data_provider, base.GetHash(), base.nBits);
     195         [ +  - ]:       12736 :             FinalizeHeader(block, chainman);
     196                 :       12736 :             return block;
     197                 :       21178 :         };
     198                 :             : 
     199                 :             :         // Send low-work headers, compact blocks, and blocks
     200         [ +  - ]:       21178 :         CallOneOf(
     201                 :             :             fuzzed_data_provider,
     202                 :        8442 :             [&]() NO_THREAD_SAFETY_ANALYSIS {
     203                 :             :                 // Send FUZZ_MAX_HEADERS_RESULTS headers
     204                 :        8442 :                 std::vector<CBlock> headers;
     205         [ +  - ]:        8442 :                 headers.resize(FUZZ_MAX_HEADERS_RESULTS);
     206         [ +  + ]:      143514 :                 for (CBlock& header : headers) {
     207   [ +  -  +  - ]:      135072 :                     header = ConsumeHeader(fuzzed_data_provider, base.GetHash(), base.nBits);
     208         [ +  - ]:      135072 :                     FinalizeHeader(header, chainman);
     209                 :      135072 :                     base = header;
     210                 :             :                 }
     211                 :             : 
     212         [ +  - ]:        8442 :                 all_headers.insert(all_headers.end(), headers.begin(), headers.end());
     213                 :             : 
     214   [ +  -  +  - ]:        8442 :                 auto headers_msg = NetMsg::Make(NetMsgType::HEADERS, TX_WITH_WITNESS(headers));
     215         [ +  - ]:        8442 :                 g_testing_setup->SendMessage(fuzzed_data_provider, std::move(headers_msg));
     216                 :        8442 :             },
     217                 :        3612 :             [&]() NO_THREAD_SAFETY_ANALYSIS {
     218                 :             :                 // Send a compact block
     219                 :        3612 :                 auto block = finalized_block();
     220         [ +  - ]:        3612 :                 CBlockHeaderAndShortTxIDs cmpct_block{block, fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
     221                 :             : 
     222         [ +  - ]:        3612 :                 all_headers.push_back(block);
     223                 :             : 
     224   [ +  -  +  - ]:        3612 :                 auto headers_msg = NetMsg::Make(NetMsgType::CMPCTBLOCK, TX_WITH_WITNESS(cmpct_block));
     225         [ +  - ]:        3612 :                 g_testing_setup->SendMessage(fuzzed_data_provider, std::move(headers_msg));
     226                 :        7224 :             },
     227                 :        9124 :             [&]() NO_THREAD_SAFETY_ANALYSIS {
     228                 :             :                 // Send a block
     229                 :        9124 :                 auto block = finalized_block();
     230                 :             : 
     231         [ +  - ]:        9124 :                 all_headers.push_back(block);
     232                 :             : 
     233   [ +  -  +  - ]:        9124 :                 auto headers_msg = NetMsg::Make(NetMsgType::BLOCK, TX_WITH_WITNESS(block));
     234         [ +  - ]:        9124 :                 g_testing_setup->SendMessage(fuzzed_data_provider, std::move(headers_msg));
     235                 :        9124 :             });
     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         [ +  - ]:         618 :     total_work += CalculateClaimedHeadersWork(all_headers);
     242                 :             : 
     243                 :             :     // This test should never create a chain with more work than MinimumChainWork.
     244   [ +  -  +  -  :         618 :     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   [ +  -  -  + ]:        1236 :     assert(WITH_LOCK(cs_main, return chainman.m_blockman.m_block_index.size()) == original_index_size);
     250         [ +  - ]:        1236 : }
        

Generated by: LCOV version 2.0-1