net: call `Select` with reachable networks in `ThreadOpenConnections` #29436

pull brunoerg wants to merge 3 commits into bitcoin:master from brunoerg:2024-02-addrman-select-networks changing 8 files +72 −43
  1. brunoerg commented at 6:49 PM on February 14, 2024: contributor

    This PR changes addrman's Select to support multiple networks and change ThreadOpenConnections to call it with reachable networks. It can avoid unnecessary Select calls and avoid exceeding the max number of tries (100), especially when turning a clearnet + Tor/I2P/CJDNS node to Tor/I2P/CJDNS. Compared to #29330, this approach is "less aggresive". It does not add a new init flag and does not impact address relay.

    I did an experiment of calling Select without passing a network until it finds an address from a network that compose 20% ~ 25% of the addrman (limited to 100 tries).

    Screenshot 2024-02-14 at 14 37 58

  2. DrahtBot commented at 6:49 PM on February 14, 2024: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    <!--006a51241073e994b41acfe9ec718e94-->

    Code Coverage

    For detailed information about the code coverage, see the test coverage report.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK vasild, naumenkogs, achow101
    Stale ACK Empact

    If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    No conflicts as of last run.

  3. DrahtBot added the label P2P on Feb 14, 2024
  4. brunoerg commented at 6:52 PM on February 14, 2024: contributor
  5. in src/addrman.h:162 in 2096aeabec outdated
     159 |       *                     slow down the search.
     160 |       * @return    CAddress The record for the selected peer.
     161 |       *            seconds  The last time we attempted to connect to that peer.
     162 |       */
     163 | -    std::pair<CAddress, NodeSeconds> Select(bool new_only = false, std::optional<Network> network = std::nullopt) const;
     164 | +    std::pair<CAddress, NodeSeconds> Select(bool new_only = false, std::optional<std::vector<Network>> networks = std::nullopt) const;
    


    amitiuttarwar commented at 5:04 AM on February 15, 2024:

    instead of an optional of a vector, you could have the default value explicitly be all networks, or an empty vector be interpreted as all networks. probably the first one is clearer.


    vasild commented at 11:22 AM on February 15, 2024:

    With this PR all production code passes both arguments. No need anymore for defaults. This compiles:

    -    std::pair<CAddress, NodeSeconds> Select(bool new_only = false, std::optional<std::vector<Network>> networks = std::nullopt) const;
    +    std::pair<CAddress, NodeSeconds> Select(bool new_only, std::vector<Network> networks) const;
    

    Some tests use Select() and I adjusted them to do Select(false, {}) just to check that the compilation will succeed. They can be adjusted to Select(false, g_reachable_nets.All()) or Select(false, {NET_IPV4, NET_IPV6, NET_ONION, NET_I2P, NET_CJDNS}) if you decide to drop the defaults.

  6. in src/addrman.cpp:725 in 2096aeabec outdated
     723 | +    if (networks.has_value()) {
     724 | +        new_count = 0;
     725 | +        tried_count = 0;
     726 | +        for (auto& network : *networks) {
     727 | +            auto it = m_network_counts.find(network);
     728 | +            if (it == m_network_counts.end()) return {};
    


    vasild commented at 10:11 AM on February 15, 2024:

    This should be continue;? If addrman has 0 addresses from some of the requested networks, it can still return an address from another requested network.

                if (it == m_network_counts.end()) {
                    continue;
                }
    

    brunoerg commented at 12:06 PM on February 15, 2024:

    Yes, nice catch. I'll address it.

  7. in src/addrman.cpp:766 in 2096aeabec outdated
     759 | @@ -756,9 +760,10 @@ std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool new_only, std::option
     760 |              position = (initial_position + i) % ADDRMAN_BUCKET_SIZE;
     761 |              node_id = GetEntry(search_tried, bucket, position);
     762 |              if (node_id != -1) {
     763 | -                if (network.has_value()) {
     764 | +                if (networks.has_value()) {
     765 |                      const auto it{mapInfo.find(node_id)};
     766 | -                    if (Assume(it != mapInfo.end()) && it->second.GetNetwork() == *network) break;
     767 | +                    auto& nets = *networks;
     768 | +                    if (Assume(it != mapInfo.end()) && std::find(nets.begin(), nets.end(), it->second.GetNetwork()) != nets.end()) break;
    


    vasild commented at 10:18 AM on February 15, 2024:

    This code is correct, but is doing linear search in the list of networks. For values of up to 6 that is irrelevant, but if it is changed from vector to unordered_set then lookup is also shorter to write, in addition to being O(1):

                        if (Assume(it != mapInfo.end()) && networks->contains(it->second.GetNetwork())) break;
    

    brunoerg commented at 1:27 PM on February 15, 2024:

    Nice, I'll address it.

  8. in src/bench/addrman.cpp:132 in 2096aeabec outdated
     125 | @@ -126,8 +126,10 @@ static void AddrManSelectByNetwork(benchmark::Bench& bench)
     126 |  
     127 |      FillAddrMan(addrman);
     128 |  
     129 | +    std::vector<Network> net_i2p = {NET_I2P};
     130 | +
     131 |      bench.run([&] {
     132 | -        (void)addrman.Select(/*new_only=*/false, NET_I2P);
     133 | +        (void)addrman.Select(/*new_only=*/false, net_i2p);
    


    vasild commented at 10:21 AM on February 15, 2024:

    This should work as well (untested):

    (void)addrman.Select(/*new_only=*/false, {NET_I2P});
    

    Same in src/test/addrman_tests.cpp.


    brunoerg commented at 12:30 PM on February 15, 2024:

    It doesn't work with vector I guess. But since I'll change it to unordered_set, it will work.


    vasild commented at 2:18 PM on February 16, 2024:

    Does not matter now, but it should work with vector as well.

  9. in src/net.cpp:2693 in 2096aeabec outdated
    2690 |                  // ensures that a peer from another network will be evicted.
    2691 | -                std::tie(addr, addr_last_try) = addrman.Select(false, preferred_net);
    2692 | +                if (preferred_net.has_value()) {
    2693 | +                    networks = {*preferred_net};
    2694 | +                }
    2695 | +                std::tie(addr, addr_last_try) = addrman.Select(false, networks);
    


    vasild commented at 10:29 AM on February 15, 2024:

    This could be shorter (untested):

    std::tie(addr, addr_last_try) = preferred_net.has_value()
        ? addrman.Select(false, {*preferred_net})
        : addrman.Select(false, g_reachable_nets.All());
    
  10. in src/test/fuzz/addrman.cpp:295 in 2096aeabec outdated
     282 | +    Shuffle(all_networks.begin(), all_networks.end(), fast_random_context);
     283 | +    std::vector<Network> nets;
     284 | +    for (size_t i = 0; i < all_networks.size() && fuzzed_data_provider.ConsumeBool(); i++) {
     285 | +        nets.push_back(all_networks[i]);
     286 | +    }
     287 | +    (void)const_addr_man.Select(fuzzed_data_provider.ConsumeBool(), nets);
    


    vasild commented at 10:39 AM on February 15, 2024:

    This could end up passing an empty container (vector or unordered_set if you decide to change it). Maybe this:

    if (nets.empty()) {
        (void)const_addr_man.Select(fuzzed_data_provider.ConsumeBool());
    } else {
        (void)const_addr_man.Select(fuzzed_data_provider.ConsumeBool(), nets);
    }
    

    brunoerg commented at 1:26 PM on February 15, 2024:

    Empty container and nothing means the same, all networks.

  11. in src/net.cpp:2573 in 2096aeabec outdated
    2568 | +        for (int i = 0; i != NET_MAX; i++) {
    2569 | +            Network net = static_cast<Network>(i);
    2570 | +            if (g_reachable_nets.Contains(net)) {
    2571 | +                networks.push_back(net);
    2572 | +            }
    2573 | +        }
    


    vasild commented at 10:46 AM on February 15, 2024:

    If you extend the ReachableNets class like this:

    --- i/src/netbase.h
    +++ w/src/netbase.h
    @@ -102,12 +102,18 @@ public:
         [[nodiscard]] bool Contains(const CNetAddr& addr) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
         {   
             AssertLockNotHeld(m_mutex);
             return Contains(addr.GetNetwork());
         }
    
    +    [[nodiscard]] std::unordered_set<Network> All() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
    +    {
    +        LOCK(m_mutex);
    +        return m_reachable;
    +    }
    +
     private:
         mutable Mutex m_mutex;
    
         std::unordered_set<Network> m_reachable GUARDED_BY(m_mutex){
    

    Then you can pass directly g_reachable_nets.All() to Select() without an intermediate variable.


    brunoerg commented at 1:27 PM on February 15, 2024:

    Nice idea! I'll address it.

  12. vasild commented at 10:52 AM on February 15, 2024: contributor

    Approach ACK 2096aeabec288bd2852593f0c965a2a9d22ad0c8

    Some non-blockers below (feel free to ignore them) plus one return vs continue that probably needs to be addressed.

  13. brunoerg force-pushed on Feb 15, 2024
  14. brunoerg commented at 1:45 PM on February 15, 2024: contributor
  15. vasild approved
  16. vasild commented at 2:17 PM on February 16, 2024: contributor

    ACK 7edb07ca800991f7c88d582b880a392efe17f31d

    About the graph in the OP: "... until it finds an address from a network that compose 20% ...". The chance of not getting the result after N tries is (8/10)^N. For example for N=25 that is less than 0.004. What is the explanation of having so many dots so high? Uneven distribution within addrman?

  17. brunoerg commented at 7:45 PM on February 16, 2024: contributor

    About the graph in the OP: "... until it finds an address from a network that compose 20% ...". The chance of not getting the result after N tries is (8/10)^N. For example for N=25 that is less than 0.004. What is the explanation of having so many dots so high? Uneven distribution within addrman?

    That was what I was expecting too. I'll investigate but yes I think it might be an uneven distribution. Btw, this is not deterministic.

  18. in src/addrman.cpp:731 in 3f99f5a6e5 outdated
     731 | +            auto it = m_network_counts.find(network);
     732 | +            if (it == m_network_counts.end()) {
     733 | +                continue;
     734 | +            }
     735 | +            auto counts = it->second;
     736 | +            new_count += counts.n_new;
    


    naumenkogs commented at 9:02 AM on February 19, 2024:

    3f99f5a6e58fae4981228929045ed7be32463381

    Might be worth making this overflow-safe?


    naumenkogs commented at 10:25 AM on February 19, 2024:

    Might as well fix if (new_count + tried_count == 0) return {}; above. While it seems like a bug in the existing code, it's really hard to trigger.... feeding addrman with that much garbage would be a failure probably before the size_t limit is hit :)


    brunoerg commented at 6:10 PM on February 19, 2024:

    Might be worth making this overflow-safe?

    I don't know, I don't see a viable way of reaching that.

    it's really hard to trigger.... feeding addrman with that much garbage would be a failure probably before the size_t limit is hit :)

    I believe that's impractical due to the limited size of addrman.


    naumenkogs commented at 8:38 AM on February 20, 2024:

    I believe that's impractical due to the limited size of addrman.

    Right, probably fine to keep it this way for both cases.

  19. naumenkogs commented at 8:39 AM on February 20, 2024: member

    ACK 7edb07ca800991f7c88d582b880a392efe17f31d

  20. mzumsande commented at 5:48 PM on February 20, 2024: contributor

    I tried to test the performance impact on the "normal" case of an addrman that only has addrs for reachable nets: If I change the AddrManSelect benchmark to use addrman.Select(false, g_reachable_nets.All()); the performance goes down by a factor of ~5 for me (from ~160ns/op to ~770 ns/op). Do you see a similar effect?

  21. in src/addrman.h:163 in 3f99f5a6e5 outdated
     160 |       *                     slow down the search.
     161 |       * @return    CAddress The record for the selected peer.
     162 |       *            seconds  The last time we attempted to connect to that peer.
     163 |       */
     164 | -    std::pair<CAddress, NodeSeconds> Select(bool new_only = false, std::optional<Network> network = std::nullopt) const;
     165 | +    std::pair<CAddress, NodeSeconds> Select(bool new_only = false, std::unordered_set<Network> networks = {}) const;
    


    mzumsande commented at 6:07 PM on February 20, 2024:

    Looks like the case of empty networks is no longer used outside of tests after this PR. In that case, should we drop the default args and simplify the implementation?


    brunoerg commented at 1:59 PM on February 22, 2024:

    Yes, sure! I'll address it.

  22. in src/test/addrman_tests.cpp:282 in 7edb07ca80 outdated
     278 | @@ -275,7 +279,8 @@ BOOST_AUTO_TEST_CASE(addrman_select_special)
     279 |      // since the only ipv4 address is on the new table, ensure that the new
     280 |      // table gets selected even if new_only is false. if the table was being
     281 |      // selected at random, this test will sporadically fail
     282 | -    BOOST_CHECK(addrman->Select(/*new_only=*/false, NET_IPV4).first == addr1);
     283 | +    std::unordered_set<Network> {NET_IPV4} = {NET_IPV4};
    


    luke-jr commented at 3:02 PM on February 21, 2024:

    What is this supposed to do? :/


    brunoerg commented at 1:48 PM on February 22, 2024:

    Leftover from previous changes, thanks, I'll remove it.

  23. mzumsande commented at 4:09 PM on February 22, 2024: contributor

    the performance goes down by a factor of ~5 for me

    If this is correct and can be reproduced by others, I think that something should be done about it. If this PR would increase performance for a rare and transient case (right after the users switched their onlynet parameters) but decrease performance for each Select() call in the typical scenario (addrman has mostly addrs from reachable networks), the overall impact on performance of this PR would be clearly negative. One possibility might be to track the percentage of non-reachable addrs while loading AddrMan (we can't get more unreachable addrs afterwards) and only execute the slower logic if that is larger than some threshold (for example, 50%).

  24. brunoerg commented at 4:46 PM on February 22, 2024: contributor

    If this is correct and can be reproduced by others, I think that something should be done about it. If this PR would increase performance for a rare and transient case (right after the users switched their onlynet parameters) but decrease performance for each Select() call in the typical scenario (addrman has mostly addrs from reachable networks), the overall impact on performance of this PR would be clearly negative. One possibility might be to track the percentage of non-reachable addrs while loading AddrMan (we can't get more unreachable addrs afterwards) and only execute the slower logic if that is larger than some threshold (for example, 50%).

    I'm reproducing it to check it.

  25. brunoerg force-pushed on Feb 26, 2024
  26. brunoerg force-pushed on Feb 26, 2024
  27. DrahtBot commented at 11:02 AM on February 26, 2024: contributor

    <!--85328a0da195eb286784d51f73fa0af9-->

    🚧 At least one of the CI tasks failed. Make sure to run all tests locally, according to the documentation.

    Possibly this is due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    Leave a comment here, if you need help tracking down a confusing failure.

    <sub>Debug: https://github.com/bitcoin/bitcoin/runs/21977629586</sub>

  28. DrahtBot added the label CI failed on Feb 26, 2024
  29. brunoerg commented at 11:06 AM on February 26, 2024: contributor

    @mzumsande you're right!

    I checked the performance goes down when calling Select when addrman only has addrs for reachable nets. So, I changed the approach to call Select with networks only whether the number of addresses from reachable networks in the addrman is less than 50%. This should be enough to cover the cases where this is an issue.

    So, when initializing CConnman, we check whether the number of addresses from reachable networks in the addrman is less than 50%, if so, we call Select passing the networks.

    cc: @vasild @naumenkogs (due previous ACK)

  30. DrahtBot removed the label CI failed on Feb 26, 2024
  31. in src/addrman.cpp:1041 in 7b4b9535a7 outdated
    1036 | +        const auto& net{item.first};
    1037 | +        const auto& count{item.second};
    1038 | +
    1039 | +        total_size += (count.n_new + count.n_tried);
    1040 | +
    1041 | +        if (networks.count(net)) {
    


    Empact commented at 5:31 AM on February 27, 2024:

    nit: contains is equivalent but more semantically correct here

  32. in src/net.cpp:3293 in 7b4b9535a7 outdated
    3287 | @@ -3285,6 +3288,12 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
    3288 |          scheduler.scheduleEvery([this] { ASMapHealthCheck(); }, ASMAP_HEALTH_CHECK_INTERVAL);
    3289 |      }
    3290 |  
    3291 | +    auto reachable_nets{g_reachable_nets.All()};
    3292 | +    const bool has_few_addresses{addrman.HasFewReachableAddresses(reachable_nets)};
    3293 | +    if (has_few_addresses) {
    


    Empact commented at 5:45 AM on February 27, 2024:

    nit: could inline these

  33. in src/addrman.cpp:1046 in 7b4b9535a7 outdated
    1041 | +        if (networks.count(net)) {
    1042 | +            reachable_size += (count.n_new + count.n_tried);
    1043 | +        }
    1044 | +    }
    1045 | +
    1046 | +    if (reachable_size < total_size / 2) {
    


    Empact commented at 5:50 AM on February 27, 2024:

    nit: could return this directly

  34. in src/net.h:1400 in 7b4b9535a7 outdated
    1395 | @@ -1396,6 +1396,9 @@ class CConnman
    1396 |      // P2P timeout in seconds
    1397 |      std::chrono::seconds m_peer_connect_timeout;
    1398 |  
    1399 | +    // Reachable networks that have addresses in the addrman
    1400 | +    std::unordered_set<Network> networks_with_addrs;
    


    Empact commented at 5:56 AM on February 27, 2024:
  35. in src/addrman.cpp:715 in 7b4b9535a7 outdated
     711 | @@ -712,7 +712,7 @@ void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, NodeSeconds
     712 |      }
     713 |  }
     714 |  
     715 | -std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool new_only, std::optional<Network> network) const
     716 | +std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool new_only, std::unordered_set<Network> networks) const
    


    Empact commented at 6:06 AM on February 27, 2024:

    networks could be a const reference throughout, no?


    brunoerg commented at 12:09 PM on February 27, 2024:

    Yes, I'll address it.

  36. in src/net.cpp:3294 in 7b4b9535a7 outdated
    3287 | @@ -3285,6 +3288,12 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
    3288 |          scheduler.scheduleEvery([this] { ASMapHealthCheck(); }, ASMAP_HEALTH_CHECK_INTERVAL);
    3289 |      }
    3290 |  
    3291 | +    auto reachable_nets{g_reachable_nets.All()};
    3292 | +    const bool has_few_addresses{addrman.HasFewReachableAddresses(reachable_nets)};
    3293 | +    if (has_few_addresses) {
    3294 | +        networks_with_addrs = reachable_nets;
    


    Empact commented at 6:32 AM on February 27, 2024:

    So, to be explicit, this uses g_reachable_nets if reachable_size < total_size / 2 otherwise all nets. The switch is to avoid the cost of the filtering if the filtering doesn't much reduce the set size, yes?

    A comment to that effect might be helpful, as I had to puzzle out a bit why the value was set only in this case.


    brunoerg commented at 11:07 AM on February 27, 2024:

    The switch is to avoid the cost of the filtering if the filtering doesn't much reduce the set size, yes?

    Yes, I'll put a comment explaining that.

  37. in src/addrman.cpp:1019 in 44aba6cbff outdated
    1015 | @@ -1016,6 +1016,35 @@ std::optional<AddressPosition> AddrManImpl::FindAddressEntry_(const CAddress& ad
    1016 |      }
    1017 |  }
    1018 |  
    1019 | +bool AddrManImpl::HasFewReachableAddresses_(const std::unordered_set<Network>& networks)
    


    naumenkogs commented at 10:13 AM on February 27, 2024:

    An outside node easily influences the outcome by feeding garbage, right? Then I'd suggest commenting along the following lines: "Should not be used for anything critical since it's easily [...]"


    naumenkogs commented at 10:14 AM on February 27, 2024:

    Another improvement could be differentiating new/tried here, since tried is harder to influence.


    brunoerg commented at 11:34 AM on February 27, 2024:

    An outside node easily influences the outcome by feeding garbage, right?

    I don't think so, since we're checking by network and we do not store addresses from unreachable networks.


    brunoerg commented at 11:35 AM on February 27, 2024:

    Another improvement could be differentiating new/tried here, since tried is harder to influence.

    Sure!

  38. brunoerg force-pushed on Feb 27, 2024
  39. brunoerg commented at 5:31 PM on February 27, 2024: contributor

    Force-pushed addressing: #29436 (review) #29436 (review) #29436 (review) #29436 (review) #29436 (review) #29436 (review) #29436 (review)

    I changed the approach to know verify new and tried table isolated, so if there is less than 50% of reachable addresses than we filter the selection (selecting from new table will consider data from new table only, for example).

    Thanks, @empact and @naumenkogs.

  40. in src/net.h:1404 in ef1cd394bb outdated
    1395 | @@ -1396,6 +1396,13 @@ class CConnman
    1396 |      // P2P timeout in seconds
    1397 |      std::chrono::seconds m_peer_connect_timeout;
    1398 |  
    1399 | +    /**
    1400 | +     * Reachable networks that have addresses in the addrman (new, tried). It is filled when there
    1401 | +     * are few reachable addresses in addrman and used to filter the addrman's selection.
    1402 | +     * Otherwise, we avoid the cost of the filtering.
    1403 | +     */
    1404 | +    std::pair<std::unordered_set<Network>, std::unordered_set<Network>>  m_networks_with_addrs;
    


    Empact commented at 6:08 PM on February 27, 2024:

    nit: IMO the code will be more self-documenting if you use 2 separate variables for new vs tried, rather than a pair here, because to make sense of the pair you have to refer back to the definition, while new vs tried in the variable name will be immediately apparent.

    Could still return a pair from NetworksWithAddresses, and unpack it for assignment.


    brunoerg commented at 6:25 PM on February 27, 2024:

    Sounds good, I'll address it.

  41. brunoerg force-pushed on Feb 27, 2024
  42. brunoerg commented at 6:25 PM on February 27, 2024: contributor

    Force-pushed addressing #29436 (review).

  43. Empact approved
  44. Empact commented at 9:07 PM on February 27, 2024: contributor
  45. DrahtBot requested review from vasild on Feb 27, 2024
  46. DrahtBot requested review from naumenkogs on Feb 27, 2024
  47. in src/addrman.cpp:713 in b85893ca02 outdated
     711 | @@ -712,7 +712,7 @@ void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, NodeSeconds
     712 |      }
     713 |  }
     714 |  
     715 | -std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool new_only, std::optional<Network> network) const
     716 | +std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool new_only, const std::unordered_set<Network>& networks) const
    


    ajtowns commented at 2:26 AM on February 29, 2024:

    Having an unordered_set to choose a subset of 7 values from an enum seems a bit overkill. Doing:

        std::bitset<NET_MAX> network_bitset;
        for (net : networks) { if (0 <= net && net < NET_MAX) network_bitset.set(net); }
    
        ...
    
        if (network_bitset.test(it->second.GetNetwork())) { /* this is one of the networks i wanted!! */ }
    

    seems like it would be better. The network_bitset value could be set as part of initializing addrman, and the test should be fast enough to do it always, I think?


    vasild commented at 2:41 PM on February 29, 2024:

    unordered_set is the most semantically sound container for this. It is also the most convenient and easy to use. For the same reasons we use std::string to store fixed strings or string literals. IMO a deviation from that with a more verbose alternative is justified only if it provides a noticeable performance improvement.


    brunoerg commented at 12:13 PM on March 4, 2024:

    I'll leave as it for now, but free to change if other reviewers agree.

  48. in src/addrman.cpp:722 in b85893ca02 outdated
     726 | -        if (it == m_network_counts.end()) return {};
     727 | -
     728 | -        auto counts = it->second;
     729 | -        new_count = counts.n_new;
     730 | -        tried_count = counts.n_tried;
     731 | +    if (!networks.empty()) {
    


    vasild commented at 5:36 AM on March 4, 2024:

    I am trying to understand better the performance of this. Posting here to avoid cluttering the PR's main discussion thread.

    #29436#pullrequestreview-1891096814:

    I tried to test the performance impact on the "normal" case of an addrman that only has addrs for reachable nets: If I change the AddrManSelect benchmark to use addrman.Select(false, g_reachable_nets.All()); the performance goes down by a factor of ~5 for me

    One thing to note here is that the benchmark fills addrman solely with IPv6 addresses. That's not representative. So I borrowed the peers.dat from my mainnet node and ran the tests below.

    As a start I began from 7edb07ca80 (this PR before the performance related tweaks). My aim was to 1. confirm that there is indeed a performance issue and 2. to understand where exactly it comes from so it can be addressed in the best way. Then I compared with the PR and then I compared with the PR + a change to make the last argument of Select() const ref.

    addrman size:

    IPV4: 49123
    IPV6: 11071
    ONION:15096
    I2P:   1863
    CJDNS:    2
    

    Numbers in ns/op (bigger number => slower).

    \ Baseline (master @ 7edb07ca80~3) PR (7edb07ca80) PR (7edb07ca80) + const ref argument
    any (empty optional or empty map) 130 140 <sup>A</sup> 135
    IPV4 or {IPV4} 190 320 200
    IPV6 or {IPV6} 490 640 510
    ONION or {ONION} 1200 1370 1270
    I2P or {I2P} 2500 2650 2550
    CJDNS or {CJDNS} 1500000 1700000 1600000
    {IPV4, IPV6, ONION, I2P, CJDNS} - 550 <sup>B</sup> 150 <sup>C</sup>

    <sup>A</sup> vs <sup>B</sup> is probably what @mzumsande observed in #29436#pullrequestreview-1891096814. But fixing the arg changes that to <sup>A</sup> vs <sup>C</sup>.

    Based on the above, I think that it is best to go back to 7edb07ca80 and only fix the Select() argument.

    <details> <summary>benchmark for Select() to read real peers.dat</summary>

    diff --git a/src/bench/addrman.cpp b/src/bench/addrman.cpp
    index f044feebba..dd45b9e69b 100644
    --- a/src/bench/addrman.cpp
    +++ b/src/bench/addrman.cpp
    @@ -1,12 +1,15 @@
     // Copyright (c) 2020-2022 The Bitcoin Core developers
     // Distributed under the MIT software license, see the accompanying
     // file COPYING or http://www.opensource.org/licenses/mit-license.php.
     
    +#include <addrdb.h>
     #include <addrman.h>
     #include <bench/bench.h>
    +#include <chainparams.h>
    +#include <common/args.h>
     #include <netbase.h>
     #include <netgroup.h>
     #include <random.h>
     #include <util/check.h>
     #include <util/time.h>
     
    @@ -81,12 +84,37 @@ static void AddrManAdd(benchmark::Bench& bench)
         bench.run([&] {
             AddrMan addrman{EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
             AddAddressesToAddrMan(addrman);
         });
     }
     
    +static void AddrManSelectMain(benchmark::Bench& bench)
    +{
    +    SelectParams(ChainType::MAIN);
    +    gArgs.ForceSetArg("checkaddrman", "0");
    +    gArgs.ForceSetArg("datadir", "/tmp");
    +    auto result = LoadAddrman(EMPTY_NETGROUPMAN, gArgs);
    +    AddrMan& addrman = *(*result).get();
    +
    +    std::cout << "addrman size:\n";
    +    std::cout << "IPv4: " << addrman.Size(NET_IPV4) << "\n";
    +    std::cout << "IPv6: " << addrman.Size(NET_IPV6) << "\n";
    +    std::cout << "Tor:  " << addrman.Size(NET_ONION) << "\n";
    +    std::cout << "I2P:  " << addrman.Size(NET_I2P) << "\n";
    +    std::cout << "CJDNS:" << addrman.Size(NET_CJDNS) << "\n";
    +
    +    //const std::optional<Network> n{NET_CJDNS};
    +    //const std::unordered_set<Network> n{NET_IPV4, NET_IPV6, NET_ONION, NET_I2P, NET_CJDNS};
    +    //const std::unordered_set<Network> n{};
    +    //const std::unordered_set<Network> n{g_reachable_nets.All()};
    +    bench.run([&] {
    +        const auto& address = addrman.Select(/*new_only=*/false, n);
    +        assert(address.first.IsValid());
    +    });
    +}
    +
     static void AddrManSelect(benchmark::Bench& bench)
     {
         AddrMan addrman{EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
     
         FillAddrMan(addrman);
     
    @@ -167,11 +195,12 @@ static void AddrManAddThenGood(benchmark::Bench& bench)
     
             markSomeAsGood(addrman);
         });
     }
     
     BENCHMARK(AddrManAdd, benchmark::PriorityLevel::HIGH);
    +BENCHMARK(AddrManSelectMain, benchmark::PriorityLevel::HIGH);
     BENCHMARK(AddrManSelect, benchmark::PriorityLevel::HIGH);
     BENCHMARK(AddrManSelectFromAlmostEmpty, benchmark::PriorityLevel::HIGH);
     BENCHMARK(AddrManSelectByNetwork, benchmark::PriorityLevel::HIGH);
     BENCHMARK(AddrManGetAddr, benchmark::PriorityLevel::HIGH);
     BENCHMARK(AddrManAddThenGood, benchmark::PriorityLevel::HIGH);
    

    </details>

    <details> <summary>Fix Select() argument</summary>

    diff --git i/src/addrman.cpp w/src/addrman.cpp
    index 4e551e81c3..bde2d90ef2 100644
    --- i/src/addrman.cpp
    +++ w/src/addrman.cpp
    @@ -705,13 +705,13 @@ void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, NodeSeconds
         if (fCountFailure && info.m_last_count_attempt < m_last_good) {
             info.m_last_count_attempt = time;
             info.nAttempts++;
         }
     }
     
    -std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool new_only, std::unordered_set<Network> networks) const
    +std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool new_only, const std::unordered_set<Network>& networks) const
     {
         AssertLockHeld(cs);
     
         if (vRandom.empty()) return {};
     
         size_t new_count = nNew;
    @@ -1208,13 +1208,13 @@ std::pair<CAddress, NodeSeconds> AddrManImpl::SelectTriedCollision()
         Check();
         auto ret = SelectTriedCollision_();
         Check();
         return ret;
     }
     
    -std::pair<CAddress, NodeSeconds> AddrManImpl::Select(bool new_only, std::unordered_set<Network> networks) const
    +std::pair<CAddress, NodeSeconds> AddrManImpl::Select(bool new_only, const std::unordered_set<Network>& networks) const
     {
         LOCK(cs);
         Check();
         auto addrRet = Select_(new_only, networks);
         Check();
         return addrRet;
    @@ -1315,13 +1315,13 @@ void AddrMan::ResolveCollisions()
     
     std::pair<CAddress, NodeSeconds> AddrMan::SelectTriedCollision()
     {
         return m_impl->SelectTriedCollision();
     }
     
    -std::pair<CAddress, NodeSeconds> AddrMan::Select(bool new_only, std::unordered_set<Network> networks) const
    +std::pair<CAddress, NodeSeconds> AddrMan::Select(bool new_only, const std::unordered_set<Network>& networks) const
     {
         return m_impl->Select(new_only, networks);
     }
     
     std::vector<CAddress> AddrMan::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered) const
     {
    diff --git i/src/addrman.h w/src/addrman.h
    index 2d2846993a..b6fdb07f82 100644
    --- i/src/addrman.h
    +++ w/src/addrman.h
    @@ -157,13 +157,13 @@ public:
          *                     guarantee a tried entry).
          * [@param](/bitcoin-bitcoin/contributor/param/)[in] networks Select only addresses of these networks (empty = all). Passing networks may
          *                     slow down the search.
          * [@return](/bitcoin-bitcoin/contributor/return/)    CAddress The record for the selected peer.
          *            seconds  The last time we attempted to connect to that peer.
          */
    -    std::pair<CAddress, NodeSeconds> Select(bool new_only = false, std::unordered_set<Network> networks = {}) const;
    +    std::pair<CAddress, NodeSeconds> Select(bool new_only = false, const std::unordered_set<Network>& networks = {}) const;
     
         /**
          * Return all or many randomly selected addresses, optionally by network.
          *
          * [@param](/bitcoin-bitcoin/contributor/param/)[in] max_addresses  Maximum number of addresses to return (0 = all).
          * [@param](/bitcoin-bitcoin/contributor/param/)[in] max_pct        Maximum percentage of addresses to return (0 = all).
    diff --git i/src/addrman_impl.h w/src/addrman_impl.h
    index bb89213bb7..2a4596e81f 100644
    --- i/src/addrman_impl.h
    +++ w/src/addrman_impl.h
    @@ -123,13 +123,13 @@ public:
             EXCLUSIVE_LOCKS_REQUIRED(!cs);
     
         void ResolveCollisions() EXCLUSIVE_LOCKS_REQUIRED(!cs);
     
         std::pair<CAddress, NodeSeconds> SelectTriedCollision() EXCLUSIVE_LOCKS_REQUIRED(!cs);
     
    -    std::pair<CAddress, NodeSeconds> Select(bool new_only, std::unordered_set<Network> networks) const
    +    std::pair<CAddress, NodeSeconds> Select(bool new_only, const std::unordered_set<Network>& networks) const
             EXCLUSIVE_LOCKS_REQUIRED(!cs);
     
         std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered = true) const
             EXCLUSIVE_LOCKS_REQUIRED(!cs);
     
         std::vector<std::pair<AddrInfo, AddressPosition>> GetEntries(bool from_tried) const
    @@ -250,13 +250,13 @@ private:
         bool Good_(const CService& addr, bool test_before_evict, NodeSeconds time) EXCLUSIVE_LOCKS_REQUIRED(cs);
     
         bool Add_(const std::vector<CAddress>& vAddr, const CNetAddr& source, std::chrono::seconds time_penalty) EXCLUSIVE_LOCKS_REQUIRED(cs);
     
         void Attempt_(const CService& addr, bool fCountFailure, NodeSeconds time) EXCLUSIVE_LOCKS_REQUIRED(cs);
     
    -    std::pair<CAddress, NodeSeconds> Select_(bool new_only, std::unordered_set<Network> networks) const EXCLUSIVE_LOCKS_REQUIRED(cs);
    +    std::pair<CAddress, NodeSeconds> Select_(bool new_only, const std::unordered_set<Network>& networks) const EXCLUSIVE_LOCKS_REQUIRED(cs);
     
         /** Helper to generalize looking up an addrman entry from either table.
          *
          * [@return](/bitcoin-bitcoin/contributor/return/)  int The nid of the entry. If the addrman position is empty or not found, returns -1.
          * */
         int GetEntry(bool use_tried, size_t bucket, size_t position) const EXCLUSIVE_LOCKS_REQUIRED(cs);
    

    </details>


    brunoerg commented at 2:24 PM on March 4, 2024:

    Thanks for it, @vasild


    brunoerg commented at 2:24 PM on March 4, 2024:

    About:

    I tried to test the performance impact on the "normal" case of an addrman that only has addrs for reachable nets: If I change the AddrManSelect benchmark to use addrman.Select(false, g_reachable_nets.All()); the performance goes down by a factor of ~5 for me (from ~160ns/op to ~770 ns/op). Do you see a similar effect?

    In fact, I just realized that the performance issue is related to g_reachable_nets.All(). With the following diff I realized the performance is not relevantly affected. cc: @mzumsande

    diff --git a/src/bench/addrman.cpp b/src/bench/addrman.cpp
    index ee4070b07a..8760f134ca 100644
    --- a/src/bench/addrman.cpp
    +++ b/src/bench/addrman.cpp
    @@ -96,6 +96,31 @@ static void AddrManSelect(benchmark::Bench& bench)
         });
     }
     
    +static void AddrManSelect1(benchmark::Bench& bench)
    +{
    +    AddrMan addrman{EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
    +
    +    FillAddrMan(addrman);
    +    const auto nets{g_reachable_nets.All()};
    +
    +    bench.run([&] {
    +        const auto& address = addrman.Select(false, nets);
    +        assert(address.first.GetPort() > 0);
    +    });
    +}
    +
    +static void AddrManSelect2(benchmark::Bench& bench)
    +{
    +    AddrMan addrman{EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
    +
    +    FillAddrMan(addrman);
    +
    +    bench.run([&] {
    +        const auto& address = addrman.Select(false, g_reachable_nets.All());
    +        assert(address.first.GetPort() > 0);
    +    });
    +}
    +
     // The worst case performance of the Select() function is when there is only
     // one address on the table, because it linearly searches every position of
     // several buckets before identifying the correct bucket
    @@ -171,6 +196,8 @@ static void AddrManAddThenGood(benchmark::Bench& bench)
     
     BENCHMARK(AddrManAdd, benchmark::PriorityLevel::HIGH);
     BENCHMARK(AddrManSelect, benchmark::PriorityLevel::HIGH);
    +BENCHMARK(AddrManSelect1, benchmark::PriorityLevel::HIGH);
    +BENCHMARK(AddrManSelect2, benchmark::PriorityLevel::HIGH);
     BENCHMARK(AddrManSelectFromAlmostEmpty, benchmark::PriorityLevel::HIGH);
     BENCHMARK(AddrManSelectByNetwork, benchmark::PriorityLevel::HIGH);
     BENCHMARK(AddrManGetAddr, benchmark::PriorityLevel::HIGH);
    
    |               84.73 |       11,801,666.44 |    5.2% |      0.01 | :wavy_dash: `AddrManSelect` (Unstable with ~11,349.7 iters. Increase `minEpochIterations` to e.g. 113497)
    |              110.53 |        9,047,580.50 |    3.0% |      0.01 | `AddrManSelect1`
    |              558.40 |        1,790,829.47 |    6.7% |      0.01 | :wavy_dash: `AddrManSelect2` (Unstable with ~1,690.3 iters. Increase `minEpochIterations` to e.g. 16903)
    

    brunoerg commented at 9:43 AM on March 5, 2024:

    Based on the above, I think that it is best to go back to 7edb07ca80 and only fix the Select() argument.

    Since it is simpler and does not impact the performance, I just addressed this suggestion. Thanks.

  49. DrahtBot requested review from vasild on Mar 4, 2024
  50. brunoerg force-pushed on Mar 5, 2024
  51. brunoerg commented at 9:42 AM on March 5, 2024: contributor

    Force-pushed simplifying the approach. I changed the last argument of Select() to be a const ref and now we just call Select with reachable networks since it does not significantly impact the performance (see #29436 (review)).

    Thanks @vasild for the benchmarking and review.

  52. vasild approved
  53. vasild commented at 1:21 PM on March 6, 2024: contributor

    ACK e66b32b2f49b71b37f48191afb761538a52e8f08

  54. DrahtBot requested review from Empact on Mar 6, 2024
  55. DrahtBot commented at 12:30 AM on July 14, 2024: contributor

    <!--85328a0da195eb286784d51f73fa0af9-->

    🚧 At least one of the CI tasks failed. <sub>Debug: https://github.com/bitcoin/bitcoin/runs/22286694905</sub>

    <details><summary>Hints</summary>

    Make sure to run all tests locally, according to the documentation.

    The failure may happen due to a number of reasons, for example:

    • Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    • A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.

    • An intermittent issue.

    Leave a comment here, if you need help tracking down a confusing failure.

    </details>

  56. DrahtBot added the label CI failed on Jul 14, 2024
  57. brunoerg force-pushed on Jul 15, 2024
  58. brunoerg commented at 1:58 PM on July 15, 2024: contributor

    Rebased

  59. brunoerg commented at 3:06 PM on July 15, 2024: contributor

    CI failure is unrelated.

  60. DrahtBot removed the label CI failed on Jul 15, 2024
  61. brunoerg force-pushed on Jul 16, 2024
  62. vasild approved
  63. vasild commented at 9:16 AM on July 24, 2024: contributor

    ACK ef525de104ef6116b4532801ef3fcbc1ad5e5552

  64. DrahtBot added the label Needs rebase on Sep 4, 2024
  65. brunoerg force-pushed on Sep 5, 2024
  66. brunoerg commented at 3:26 PM on September 5, 2024: contributor

    Rebased

  67. DrahtBot removed the label Needs rebase on Sep 5, 2024
  68. brunoerg requested review from vasild on Sep 6, 2024
  69. vasild approved
  70. vasild commented at 7:39 AM on September 9, 2024: contributor

    ACK f1704c021e615f740130fff7cd47093d708a3028

  71. in src/net.cpp:2742 in 8a82ab9840 outdated
    2738 | @@ -2739,7 +2739,11 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, Spa
    2739 |                  // If preferred_net has a value set, pick an extra outbound
    2740 |                  // peer from that network. The eviction logic in net_processing
    2741 |                  // ensures that a peer from another network will be evicted.
    2742 | -                std::tie(addr, addr_last_try) = addrman.Select(false, preferred_net);
    2743 | +                std::unordered_set<Network> preferred_nets;
    


    naumenkogs commented at 1:06 PM on September 10, 2024:

    8a82ab9840a46f6382fca92b4f6bb60e5fc5ac6d

    nit Deserves commenting what if preferred_net is not set (Well I guess there is already a comment "empty = all").

  72. naumenkogs approved
  73. naumenkogs commented at 1:09 PM on September 10, 2024: member

    ACK f1704c021e615f740130fff7cd47093d708a3028

  74. in src/test/fuzz/addrman.cpp:289 in f1704c021e outdated
     284 | @@ -285,7 +285,16 @@ FUZZ_TARGET(addrman, .init = initialize_addrman)
     285 |      auto max_pct = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096);
     286 |      auto filtered = fuzzed_data_provider.ConsumeBool();
     287 |      (void)const_addr_man.GetAddr(max_addresses, max_pct, network, filtered);
     288 | -    (void)const_addr_man.Select(fuzzed_data_provider.ConsumeBool(), network);
     289 | +
     290 | +    auto all_networks = ALL_NETWORKS;
     291 | +    FastRandomContext fast_random_context{ConsumeUInt256(fuzzed_data_provider)};
     292 | +    std::shuffle(all_networks.begin(), all_networks.end(), fast_random_context);
     293 | +    std::unordered_set<Network> nets;
    


    maflcko commented at 1:43 PM on September 10, 2024:

    Is there a reason why the entries are shuffled before inserting them into an unordered set? if there is a reason, it seems that shuffling of a handful of values can be done more efficiently than consuming a full 256 bits.


    brunoerg commented at 3:01 PM on September 10, 2024:

    Nevermind, I just force-pushed a better solution. No need to shuffle anymore.

  75. net: add `All()` in `ReachableNets`
    Co-authored-by: Vasil Dimov <vd@FreeBSD.org>
    f698636ec8
  76. brunoerg force-pushed on Sep 10, 2024
  77. brunoerg commented at 3:02 PM on September 10, 2024: contributor

    Force-pushed with a small improvement on fuzz target (https://github.com/bitcoin/bitcoin/pull/29436#discussion_r1752007850)

  78. in src/test/fuzz/addrman.cpp:291 in be41baf0b7 outdated
     284 | @@ -285,7 +285,14 @@ FUZZ_TARGET(addrman, .init = initialize_addrman)
     285 |      auto max_pct = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096);
     286 |      auto filtered = fuzzed_data_provider.ConsumeBool();
     287 |      (void)const_addr_man.GetAddr(max_addresses, max_pct, network, filtered);
     288 | -    (void)const_addr_man.Select(fuzzed_data_provider.ConsumeBool(), network);
     289 | +
     290 | +    std::unordered_set<Network> nets;
     291 | +    LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), ALL_NETWORKS.size()) {
     292 | +        auto net{fuzzed_data_provider.PickValueInArray(ALL_NETWORKS)};
    


    maflcko commented at 3:34 PM on September 10, 2024:

    This still reads a bit odd, because a set shouldn't hold duplicate values, but the loop may produce duplicate values?

    Not sure what you are trying to achieve, but for (const auto& net : ALL_NETWORKS) { if (fdp.Bool()) nets.insert(net); } may be what you are looking for?


    brunoerg commented at 3:56 PM on September 10, 2024:

    This still reads a bit odd, because a set shouldn't hold duplicate values, but the loop may produce duplicate values?

    Yes, it may produce but would be "ignored" when trying to add.

    Not sure what you are trying to achieve, but for (const auto& net : ALL_NETWORKS) { if (fdp.Bool()) nets.insert(net); } may be what you are looking for?

    Yes, will address it.


    brunoerg commented at 3:59 PM on September 10, 2024:

    Done, thanks.

  79. addrman: change `Select` to support multiple networks 829becd990
  80. net: call `Select` with reachable networks in `ThreadOpenConnections`
    Calling `Select` with reachable networks can avoid unecessary
    calls and avoid exceed the max number of tries.
    e4e3b44e9c
  81. brunoerg force-pushed on Sep 10, 2024
  82. vasild approved
  83. vasild commented at 7:12 AM on September 11, 2024: contributor

    ACK e4e3b44e9cc7227b3ad765397c884999f57bac2e

  84. DrahtBot requested review from naumenkogs on Sep 11, 2024
  85. naumenkogs approved
  86. naumenkogs commented at 8:08 AM on September 11, 2024: member

    ACK e4e3b44e9cc7227b3ad765397c884999f57bac2e

  87. DrahtBot added the label CI failed on Sep 11, 2024
  88. DrahtBot removed the label CI failed on Sep 15, 2024
  89. achow101 commented at 8:49 PM on September 16, 2024: member

    ACK e4e3b44e9cc7227b3ad765397c884999f57bac2e

  90. achow101 merged this on Sep 16, 2024
  91. achow101 closed this on Sep 16, 2024

  92. in src/addrman.cpp:1216 in e4e3b44e9c
    1212 | @@ -1208,11 +1213,11 @@ std::pair<CAddress, NodeSeconds> AddrManImpl::SelectTriedCollision()
    1213 |      return ret;
    1214 |  }
    1215 |  
    1216 | -std::pair<CAddress, NodeSeconds> AddrManImpl::Select(bool new_only, std::optional<Network> network) const
    1217 | +std::pair<CAddress, NodeSeconds> AddrManImpl::Select(bool new_only, const std::unordered_set<Network>& networks) const
    


    jonatack commented at 8:58 PM on September 16, 2024:

    Missing #include <unordered_set> in this file (include what you use)

    /src/addrman.cpp should add these lines:
    #include <algorithm>                // for max, min
    #include <compare>                  // for operator<, strong_ordering, operator>, operator<=
    #include <iterator>                 // for advance
    #include <ratio>                    // for ratio
    #include <set>                      // for set, _Rb_tree_const_iterator, operator==
    #include <unordered_map>            // for unordered_map
    #include "netgroup.h"               // for NetGroupManager
    #include "sync.h"                   // for AssertLockHeldInternal, AssertLockHeld, LOCK, UniqueLock
    
  93. in src/addrman.cpp:725 in e4e3b44e9c
     727 | -        new_count = counts.n_new;
     728 | -        tried_count = counts.n_tried;
     729 | +    if (!networks.empty()) {
     730 | +        new_count = 0;
     731 | +        tried_count = 0;
     732 | +        for (auto& network : networks) {
    


    jonatack commented at 9:01 PM on September 16, 2024:

    A Network is a cheaply-copied int enum; no need to use it by reference, unless I'm missing something.

            for (Network network : networks) {
    
  94. in src/test/fuzz/addrman.cpp:290 in e4e3b44e9c
     284 | @@ -285,7 +285,15 @@ FUZZ_TARGET(addrman, .init = initialize_addrman)
     285 |      auto max_pct = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096);
     286 |      auto filtered = fuzzed_data_provider.ConsumeBool();
     287 |      (void)const_addr_man.GetAddr(max_addresses, max_pct, network, filtered);
     288 | -    (void)const_addr_man.Select(fuzzed_data_provider.ConsumeBool(), network);
     289 | +
     290 | +    std::unordered_set<Network> nets;
     291 | +    for (const auto& net : ALL_NETWORKS) {
    


    jonatack commented at 9:03 PM on September 16, 2024:
        for (Network net : ALL_NETWORKS) {
    
  95. jonatack commented at 9:19 PM on September 16, 2024: member

    (Happy to re-review if you squash the minor comments below into #30183.)

  96. brunoerg deleted the branch on Sep 23, 2024
  97. TheCharlatan referenced this in commit 8bb47d4c2c on Nov 2, 2024
  98. bitcoin locked this on Sep 23, 2025

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-05-02 12:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me