LCOV - code coverage report
Current view: top level - src/test/fuzz - p2p_private_broadcast.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 3.9 % 152 6
Test Date: 2026-07-21 06:28:42 Functions: 20.0 % 10 2
Branches: 1.8 % 218 4

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 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 <banman.h>
       6                 :             : #include <net.h>
       7                 :             : #include <net_processing.h>
       8                 :             : #include <protocol.h>
       9                 :             : #include <sync.h>
      10                 :             : #include <test/fuzz/FuzzedDataProvider.h>
      11                 :             : #include <test/fuzz/fuzz.h>
      12                 :             : #include <test/fuzz/util.h>
      13                 :             : #include <test/fuzz/util/net.h>
      14                 :             : #include <test/util/net.h>
      15                 :             : #include <test/util/setup_common.h>
      16                 :             : #include <test/util/time.h>
      17                 :             : #include <test/util/validation.h>
      18                 :             : #include <util/time.h>
      19                 :             : #include <validationinterface.h>
      20                 :             : 
      21                 :             : #include <array>
      22                 :             : #include <ios>
      23                 :             : #include <memory>
      24                 :             : #include <vector>
      25                 :             : 
      26                 :             : namespace {
      27                 :             : TestingSetup* g_setup;
      28                 :             : 
      29                 :           1 : void initialize()
      30                 :             : {
      31                 :           1 :     static const auto testing_setup = MakeNoLogFileContext<TestingSetup>(
      32   [ +  -  +  -  :           1 :         /*chain_type=*/ChainType::REGTEST);
                   +  - ]
      33                 :           1 :     g_setup = testing_setup.get();
      34                 :           1 : }
      35                 :             : 
      36                 :             : // Inbound message types with private broadcast specific handling.
      37                 :             : // Used as the guided path in the CallOneOf() below.
      38                 :             : constexpr std::array INBOUND_MSG_TYPES{
      39                 :             :     NetMsgType::VERSION,
      40                 :             :     NetMsgType::VERACK,
      41                 :             :     NetMsgType::GETDATA,
      42                 :             :     NetMsgType::PONG,
      43                 :             : };
      44                 :             : } // namespace
      45                 :             : 
      46         [ +  - ]:         474 : FUZZ_TARGET(p2p_private_broadcast, .init = ::initialize)
      47                 :             : {
      48                 :           0 :     SeedRandomStateForTest(SeedRand::ZEROS);
      49                 :           0 :     FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
      50                 :             : 
      51                 :           0 :     auto& node{g_setup->m_node};
      52                 :           0 :     auto& connman{static_cast<ConnmanTestMsg&>(*node.connman)};
      53                 :           0 :     connman.Reset();
      54                 :           0 :     auto& chainman{static_cast<TestChainstateManager&>(*node.chainman)};
      55                 :             : 
      56                 :           0 :     FakeNodeClock clock_ctx{1610000000s};
      57         [ #  # ]:           0 :     FakeSteadyClock steady_clock;
      58         [ #  # ]:           0 :     chainman.ResetIbd();
      59                 :             :     // Sometimes leave IBD: incoming TX processing (the broadcast-abort path)
      60                 :             :     // returns early during IBD.
      61   [ #  #  #  # ]:           0 :     if (fuzzed_data_provider.ConsumeBool()) chainman.JumpOutOfIbd();
      62                 :             : 
      63                 :             :     // Reset, so that dangling pointers can be detected by sanitizers.
      64         [ #  # ]:           0 :     node.banman.reset();
      65         [ #  # ]:           0 :     node.addrman.reset();
      66         [ #  # ]:           0 :     node.peerman.reset();
      67                 :           0 :     node.addrman = std::make_unique<AddrMan>(
      68         [ #  # ]:           0 :         *node.netgroupman, /*deterministic=*/true, /*consistency_check_ratio=*/0);
      69                 :           0 :     node.peerman = PeerManager::make(connman, *node.addrman,
      70                 :             :                                      /*banman=*/nullptr, chainman,
      71         [ #  # ]:           0 :                                      *node.mempool, *node.warnings,
      72                 :             :                                      PeerManager::Options{
      73                 :             :                                          .reconcile_txs = true,
      74                 :             :                                          .deterministic_rng = true,
      75                 :           0 :                                      });
      76         [ #  # ]:           0 :     connman.SetMsgProc(node.peerman.get());
      77                 :           0 :     connman.SetAddrman(*node.addrman);
      78                 :             : 
      79                 :             :     // Seed with 0-3 transactions to test multiple pending broadcasts; zero exercises
      80                 :             :     // the connected-in-vain disconnect in PushPrivateBroadcastTx().
      81                 :           0 :     const int num_txs{fuzzed_data_provider.ConsumeIntegralInRange(0, 3)};
      82                 :           0 :     std::vector<CTransactionRef> seeded_txs;
      83         [ #  # ]:           0 :     for (int i = 0; i < num_txs; ++i) {
      84         [ #  # ]:           0 :         auto tx{MakeTransactionRef(ConsumeTransaction(fuzzed_data_provider, /*prevout_txids=*/std::nullopt))};
      85         [ #  # ]:           0 :         (void)node.peerman->InitiateTxBroadcastPrivate(tx);
      86         [ #  # ]:           0 :         seeded_txs.push_back(tx);
      87                 :           0 :     }
      88                 :             : 
      89         [ #  # ]:           0 :     LOCK(NetEventsInterface::g_msgproc_mutex);
      90                 :             : 
      91                 :           0 :     static NodeId node_id{0};
      92                 :             :     // Create at least one PRIVATE_BROADCAST peer, optionally add others of random types.
      93                 :           0 :     std::vector<CNode*> peers;
      94                 :             : 
      95                 :           0 :     CNode* pb_node = new CNode(
      96                 :             :         /*id=*/node_id++,
      97   [ #  #  #  # ]:           0 :         /*sock=*/std::make_shared<FuzzedSock>(fuzzed_data_provider, steady_clock),
      98         [ #  # ]:           0 :         /*addrIn=*/ConsumeAddress(fuzzed_data_provider),
      99                 :             :         /*nKeyedNetGroupIn=*/0,
     100                 :             :         /*nLocalHostNonceIn=*/0,
     101         [ #  # ]:           0 :         /*addrBindIn=*/CService{},
     102                 :           0 :         /*addrNameIn=*/"",
     103                 :             :         /*conn_type_in=*/ConnectionType::PRIVATE_BROADCAST,
     104                 :             :         /*inbound_onion=*/false,
     105   [ #  #  #  #  :           0 :         /*network_key=*/0);
             #  #  #  # ]
     106                 :             : 
     107         [ #  # ]:           0 :     peers.push_back(pb_node);
     108         [ #  # ]:           0 :     connman.AddTestNode(*pb_node);
     109                 :             :     // Capture outbound messages to verify if well formed (and to learn the PING
     110                 :             :     // nonce), before SocketSendData drains vSendMsg.
     111         [ #  # ]:           0 :     connman.SetCaptureMessages(true);
     112         [ #  # ]:           0 :     const auto CaptureMessageOrig = CaptureMessage;
     113                 :           0 :     const CAddress pb_addr = pb_node->addr;
     114                 :           0 :     std::optional<uint64_t> pb_ping_nonce;
     115                 :           0 :     CaptureMessage = [&](const CAddress& addr, const std::string& msg_type,
     116                 :             :                          std::span<const unsigned char> data, bool is_incoming) {
     117   [ #  #  #  # ]:           0 :         if (is_incoming || addr != pb_addr) return;
     118         [ #  # ]:           0 :         if (msg_type == NetMsgType::PING) {
     119         [ #  # ]:           0 :             Assert(data.size() == sizeof(uint64_t));
     120                 :           0 :             uint64_t nonce;
     121                 :           0 :             SpanReader{data} >> nonce;
     122                 :           0 :             pb_ping_nonce = nonce;
     123                 :           0 :             return;
     124                 :             :         }
     125         [ #  # ]:           0 :         if (msg_type == NetMsgType::VERSION) {
     126                 :           0 :             SpanReader ds{data};
     127                 :           0 :             int32_t version;
     128                 :           0 :             uint64_t my_services, your_services, my_services_dup, nonce;
     129                 :           0 :             int64_t my_time;
     130         [ #  # ]:           0 :             CService your_addr, my_addr;
     131         [ #  # ]:           0 :             std::string user_agent;
     132                 :           0 :             int32_t height;
     133                 :           0 :             bool relay;
     134   [ #  #  #  #  :           0 :             ds >> version >> my_services >> my_time >>
             #  #  #  # ]
     135   [ #  #  #  # ]:           0 :                 your_services >> CNetAddr::V1(your_addr) >>
     136   [ #  #  #  # ]:           0 :                 my_services_dup >> CNetAddr::V1(my_addr) >>
     137   [ #  #  #  #  :           0 :                 nonce >> user_agent >> height >> relay;
                   #  # ]
     138         [ #  # ]:           0 :             Assert(version == WTXID_RELAY_VERSION);
     139   [ #  #  #  #  :           0 :             Assert(my_services == NODE_NONE && my_services_dup == NODE_NONE);
                   #  # ]
     140         [ #  # ]:           0 :             Assert(my_time == 0);
     141         [ #  # ]:           0 :             Assert(your_services == NODE_NONE);
     142   [ #  #  #  #  :           0 :             Assert(your_addr == CService{});
                   #  # ]
     143         [ #  # ]:           0 :             Assert(user_agent == "/pynode:0.0.1/");
     144         [ #  # ]:           0 :             Assert(height == 0);
     145         [ #  # ]:           0 :             Assert(!relay);
     146                 :           0 :             return;
     147                 :           0 :         }
     148         [ #  # ]:           0 :         if (msg_type != NetMsgType::INV) return;
     149                 :           0 :         SpanReader ds{data};
     150                 :           0 :         std::vector<CInv> invs;
     151         [ #  # ]:           0 :         ds >> invs;
     152   [ #  #  #  # ]:           0 :         Assert(invs.size() == 1);
     153         [ #  # ]:           0 :         Assert(invs[0].IsMsgTx());
     154                 :           0 :     };
     155                 :             : 
     156                 :             :     // Complete handshake so PushPrivateBroadcastTx runs.
     157         [ #  # ]:           0 :     connman.Handshake(
     158                 :             :         /*node=*/*pb_node,
     159                 :             :         /*successfully_connected=*/true,
     160                 :             :         /*remote_services=*/ServiceFlags(NODE_NETWORK | NODE_WITNESS),
     161                 :             :         /*local_services=*/NODE_NONE,
     162                 :             :         /*version=*/PROTOCOL_VERSION,
     163                 :             :         /*relay_txs=*/true);
     164                 :             : 
     165                 :             :     // Optionally add extra peers of random connection types.
     166                 :           0 :     const int extra_peers{fuzzed_data_provider.ConsumeIntegralInRange(0, 2)};
     167         [ #  # ]:           0 :     for (int i = 0; i < extra_peers; ++i) {
     168                 :           0 :         auto extra_peer{ConsumeNodeAsUniquePtr(fuzzed_data_provider, steady_clock, node_id++)};
     169                 :             :         // An address collision would match the capture hook's filter and fail
     170                 :             :         // its assertions on this peer's (legitimate) other-typed messages.
     171   [ #  #  #  # ]:           0 :         if (extra_peer->addr == pb_addr) continue;
     172         [ #  # ]:           0 :         peers.push_back(extra_peer.release());
     173         [ #  # ]:           0 :         connman.AddTestNode(*peers.back());
     174                 :           0 :         node.peerman->InitializeNode(
     175         [ #  # ]:           0 :             *peers.back(),
     176                 :           0 :             static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>()));
     177                 :           0 :     }
     178                 :             : 
     179   [ #  #  #  # ]:           0 :     LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100)
     180                 :             :     {
     181                 :             :         // Pick any random peer to test interleaved message handling.
     182                 :           0 :         CNode& p2p_node = *PickValue(fuzzed_data_provider, peers);
     183         [ #  # ]:           0 :         if (p2p_node.fDisconnect) continue;
     184                 :             : 
     185         [ #  # ]:           0 :         clock_ctx += ConsumeDuration<std::chrono::seconds>(fuzzed_data_provider, 0s, 600s);
     186                 :             : 
     187                 :           0 :         std::optional<CSerializedNetMsg> net_msg;
     188         [ #  # ]:           0 :         CallOneOf(
     189                 :             :             fuzzed_data_provider,
     190                 :           0 :             [&] {
     191                 :           0 :                 net_msg.emplace();
     192                 :           0 :                 net_msg->m_type = std::string{PickValue(fuzzed_data_provider, INBOUND_MSG_TYPES)};
     193                 :           0 :             },
     194                 :           0 :             [&] {
     195                 :           0 :                 net_msg.emplace();
     196                 :           0 :                 net_msg->m_type = fuzzed_data_provider.ConsumeRandomLengthString(CMessageHeader::MESSAGE_TYPE_SIZE);
     197                 :           0 :             },
     198                 :           0 :             [&] {
     199                 :           0 :                 (void)node.peerman->InitiateTxBroadcastPrivate(
     200   [ #  #  #  # ]:           0 :                     MakeTransactionRef(ConsumeTransaction(fuzzed_data_provider, /*prevout_txids=*/std::nullopt)));
     201                 :           0 :             },
     202                 :           0 :             [&] {
     203                 :             :                 // Construct a valid GETDATA for a seeded tx to exercise the TX send path.
     204   [ #  #  #  # ]:           0 :                 if (p2p_node.IsPrivateBroadcastConn() &&
     205   [ #  #  #  # ]:           0 :                     p2p_node.fSuccessfullyConnected &&
     206         [ #  # ]:           0 :                     !seeded_txs.empty()) {
     207                 :           0 :                     const auto& tx{PickValue(fuzzed_data_provider, seeded_txs)};
     208         [ #  # ]:           0 :                     net_msg.emplace(NetMsg::Make(
     209         [ #  # ]:           0 :                         NetMsgType::GETDATA,
     210                 :           0 :                         std::vector<CInv>{{MSG_TX, tx->GetHash().ToUint256()}}));
     211                 :             :                 }
     212                 :           0 :             },
     213                 :           0 :             [&] {
     214                 :             :                 // Confirm reception of the pushed TX with a PONG matching the captured PING nonce.
     215   [ #  #  #  # ]:           0 :                 if (&p2p_node == pb_node && pb_ping_nonce) {
     216         [ #  # ]:           0 :                     net_msg.emplace(NetMsg::Make(NetMsgType::PONG, *pb_ping_nonce));
     217                 :             :                 }
     218                 :           0 :             },
     219                 :           0 :             [&] {
     220                 :             :                 // Echo a seeded tx back from a non-private-broadcast peer to exercise
     221                 :             :                 // the received-from-network broadcast-abort path.
     222   [ #  #  #  # ]:           0 :                 if (!p2p_node.IsPrivateBroadcastConn() &&
     223   [ #  #  #  # ]:           0 :                     p2p_node.fSuccessfullyConnected &&
     224         [ #  # ]:           0 :                     !seeded_txs.empty()) {
     225                 :           0 :                     const auto& tx{PickValue(fuzzed_data_provider, seeded_txs)};
     226         [ #  # ]:           0 :                     net_msg.emplace(NetMsg::Make(NetMsgType::TX, TX_WITH_WITNESS(*tx)));
     227                 :             :                 }
     228                 :           0 :             });
     229                 :             : 
     230         [ #  # ]:           0 :         if (net_msg) {
     231         [ #  # ]:           0 :             if (net_msg->data.empty()) {
     232                 :           0 :                 net_msg->data = ConsumeRandomLengthByteVector(fuzzed_data_provider, MAX_PROTOCOL_MESSAGE_LENGTH);
     233                 :             :             }
     234         [ #  # ]:           0 :             connman.FlushSendBuffer(p2p_node);
     235         [ #  # ]:           0 :             (void)connman.ReceiveMsgFrom(p2p_node, std::move(*net_msg));
     236                 :             : 
     237                 :             :             bool more_work{true};
     238         [ #  # ]:           0 :             while (more_work) {
     239         [ #  # ]:           0 :                 p2p_node.fPauseSend = false;
     240                 :           0 :                 try {
     241         [ #  # ]:           0 :                     more_work = connman.ProcessMessagesOnce(p2p_node);
     242         [ -  - ]:           0 :                 } catch (const std::ios_base::failure&) {
     243                 :           0 :                 }
     244         [ #  # ]:           0 :                 node.peerman->SendMessages(p2p_node);
     245                 :             :             }
     246                 :             :         }
     247                 :           0 :     }
     248                 :             : 
     249         [ #  # ]:           0 :     CaptureMessage = CaptureMessageOrig;
     250         [ #  # ]:           0 :     connman.SetCaptureMessages(false);
     251                 :             : 
     252         [ #  # ]:           0 :     node.connman->StopNodes();
     253         [ #  # ]:           0 : }
        

Generated by: LCOV version 2.0-1