net: Fix Discover() not running when using -bind=0.0.0.0:port #32757

pull b-l-u-e wants to merge 2 commits into bitcoin:master from b-l-u-e:net-fix-discover-bind-any changing 2 files +72 −21
  1. b-l-u-e commented at 4:28 am on June 16, 2025: none

    Summary:
    This PR fixes two related issues with address discovery when using explicit bind addresses in Bitcoin Core:

    Problem

    When using -bind=0.0.0.0:port (or -bind=::), the Discover() function was not being executed because the code only checked the bind_on_any flag. This led to two problems:

    • The node would not discover its own local addresses if an explicit “any” bind was used.
    • The functional test feature_bind_port_discover.py would fail, as it expects local addresses to be discovered in these cases.

    The Fix

    This PR:

    1. Checks both bind_on_any and any bind addresses using IsBindAny()
      Ensures Discover() runs when binding to 0.0.0.0 or ::, even if specified explicitly.
    2. Ensures correct address discovery
      The node will now discover its own addresses when using explicit “any” binds, matching user expectations and fixing the test.
    3. Maintains backward compatibility
      The semantic meaning of bind_on_any is preserved as defined in net.h:

      “True if the user did not specify -bind= or -whitebind= and thus we should bind on 0.0.0.0 (IPv4) and :: (IPv6)”

    4. Updates the test to use dynamic ports
      The functional test feature_bind_port_discover.py is updated to use dynamic ports instead of hardcoded ones, improving reliability.

    How this PR Differs from #31492

    • Preserves the semantic meaning of bind_on_any (see net.h).
    • Uses a simpler approach: checks IsBindAny() on bind addresses, without modifying GetListenPort().
    • Avoids code duplication with DefaultOnionServiceTarget().

    References

    • Implementation follows the approach proposed in my [review comment on #31492](/bitcoin-bitcoin/31492/#issuecomment-2941773645).
    • Closes #31293 and #31336.
  2. DrahtBot commented at 4:28 am on June 16, 2025: contributor

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

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/32757.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    Approach ACK vasild
    Stale ACK PeterWrighten, yuvicc

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

    Conflicts

    No conflicts as of last run.

  3. DrahtBot added the label P2P on Jun 16, 2025
  4. in src/init.cpp:506 in 5d5bf98bdb outdated
    502@@ -503,7 +503,7 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
    503                    ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
    504     argsman.AddArg("-pid=<file>", strprintf("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)", BITCOIN_PID_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
    505     argsman.AddArg("-prune=<n>", strprintf("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex. "
    506-            "Warning: Reverting this setting requires re-downloading the entire blockchain. "
    507+                                           "Warning: Reverting this setting requires re-downloading the entire blockchain. "
    


    maflcko commented at 6:34 am on June 16, 2025:
    all the whitespace changes look unrelated. Please remove them again. Otherwise, it is unclear what you are really changing in this commit.
  5. b-l-u-e force-pushed on Jun 16, 2025
  6. b-l-u-e requested review from maflcko on Jun 16, 2025
  7. yuvicc commented at 3:40 pm on June 16, 2025: contributor

    Concept ACK

    Removing duplicate code from DefaultOnionServiceTarget() is better IMO

  8. b-l-u-e force-pushed on Jun 16, 2025
  9. PeterWrighten commented at 3:43 am on June 20, 2025: none
    utACK 776b7ee I consider you could report error message when running in original codebase, and in comparison with this PR one. Or you can find related issue and declare to fix them, it could be clearer for reviewers.
  10. in src/init.cpp:2029 in 705fe35719 outdated
    2023@@ -2024,7 +2024,17 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
    2024         StartTorControl(onion_service_target);
    2025     }
    2026 
    2027-    if (connOptions.bind_on_any) {
    2028+    bool should_discover = connOptions.bind_on_any;
    2029+    if (!should_discover) {
    2030+        for (const auto& bind_addr : connOptions.vBinds) {
    


    yuvicc commented at 10:14 pm on June 20, 2025:
    What about -whitebind here? There was a patch suggestion from vasild here

    yuvicc commented at 10:17 pm on June 20, 2025:
    ahh my bad its already checked with bind_on_any bool.

    vasild commented at 11:04 am on June 30, 2025:

    its already checked with bind_on_any bool.

    I do not think so - the bind_on_any bool will be true if -whitebind and -bind are not given. The current patch will not run Discover() if -whitebind=0.0.0.0 is used but it should. Here is an example patch to fix that:

     0--- i/src/init.cpp
     1+++ w/src/init.cpp
     2@@ -2030,12 +2030,20 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
     3             if (bind_addr.IsBindAny()) {
     4                 should_discover = true;
     5                 break;
     6             }
     7         }
     8     }
     9+    if (!should_discover) {
    10+        for (const auto& whitebind : connOptions.vWhiteBinds) {
    11+            if (whitebind.m_service.IsBindAny()) {
    12+                should_discover = true;
    13+                break;
    14+            }
    15+        }
    16+    }
    17     
    18     if (should_discover) {
    19         // Only add all IP addresses of the machine if we would be listening on
    20         // any address - 0.0.0.0 (IPv4) and :: (IPv6).
    21         Discover();
    22     }
    

    you can amend it to 705fe35719 net: Fix Discover() not running when using -bind=0.0.0.0:port


    b-l-u-e commented at 8:50 am on July 1, 2025:
    thank you for the suggestion.
  11. yuvicc commented at 10:25 pm on June 20, 2025: contributor
    lgtm ACK 776b7ee5855f7572798eff3014030bcc53aa7393
  12. in src/init.cpp:2026 in 776b7ee585 outdated
    2023@@ -2024,7 +2024,17 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
    2024         StartTorControl(onion_service_target);
    2025     }
    2026 
    


    vasild commented at 11:09 am on June 30, 2025:
    Would be nice to keep the length of lines in commit messages shorter.
  13. in test/functional/feature_bind_port_discover.py:36 in 776b7ee585 outdated
    35         self.bind_to_localhost_only = False
    36+        # Get dynamic ports for each node from the test framework
    37+        self.bind_port_0 = p2p_port(0)
    38+        self.bind_port_1 = p2p_port(1)
    39         self.extra_args = [
    40-            ['-discover', f'-port={BIND_PORT}'], # bind on any
    


    vasild commented at 11:12 am on June 30, 2025:

    The testing framework will add -bind=0.0.0.0, we want to test this. But we also want to test if no -bind is given at all. Here is a patch to do that and also to test -whitebind=0.0.0.0:

      0--- i/test/functional/feature_bind_port_discover.py
      1+++ w/test/functional/feature_bind_port_discover.py
      2@@ -26,23 +26,47 @@ from test_framework.util import (
      3 ADDR1 = '1.1.1.1'
      4 ADDR2 = '2.2.2.2'
      5 
      6 class BindPortDiscoverTest(BitcoinTestFramework):
      7     def set_test_params(self):
      8         # Avoid any -bind= on the command line. Force the framework to avoid adding -bind=127.0.0.1.
      9-        self.setup_clean_chain = True
     10         self.bind_to_localhost_only = False
     11         # Get dynamic ports for each node from the test framework
     12-        self.bind_port_0 = p2p_port(0)
     13-        self.bind_port_1 = p2p_port(1)
     14+        self.bind_ports = [
     15+            p2p_port(0),
     16+            p2p_port(2), # node0 will use their port + 1 for onion listen, which is the same as p2p_port(1), so avoid collision
     17+            p2p_port(3),
     18+            p2p_port(4),
     19+        ]
     20         self.extra_args = [
     21-            ['-discover', f'-port={self.bind_port_0}'], # bind on any
     22-            ['-discover', f'-bind={ADDR1}:{self.bind_port_1}'],
     23+            ['-discover', f'-port={self.bind_ports[0]}', '-listen=1'], # Without any -bind
     24+            ['-discover', f'-bind=0.0.0.0:{self.bind_ports[1]}'], # Explicit -bind=0.0.0.0
     25+            ['-discover', f'-whitebind=0.0.0.0:{self.bind_ports[2]}'], # Explicit -whitebind=0.0.0.0
     26+            ['-discover', f'-bind={ADDR1}:{self.bind_ports[3]}'], # Explicit -bind=routable_addr
     27         ]
     28         self.num_nodes = len(self.extra_args)
     29 
     30+    def setup_network(self):
     31+        """
     32+        BitcoinTestFramework.setup_network() without connecting node0 to node1,
     33+        we don't need that and avoiding it makes the test faster.
     34+        """
     35+        self.setup_nodes()
     36+
     37+    def setup_nodes(self):
     38+        """
     39+        BitcoinTestFramework.setup_nodes() with overriden has_explicit_bind=True
     40+        for each node and without the chain setup.
     41+        """
     42+        self.add_nodes(self.num_nodes, self.extra_args)
     43+        # TestNode.start() will add -bind= to extra_args if has_explicit_bind is
     44+        # False. We do not want any -bind= thus set has_explicit_bind to True.
     45+        for node in self.nodes:
     46+            node.has_explicit_bind = True
     47+        self.start_nodes()
     48+
     49     def add_options(self, parser):
     50         parser.add_argument(
     51             "--ihave1111and2222", action='store_true', dest="ihave1111and2222",
     52             help=f"Run the test, assuming {ADDR1} and {ADDR2} are configured on the machine",
     53             default=False)
     54 
     55@@ -51,33 +75,39 @@ class BindPortDiscoverTest(BitcoinTestFramework):
     56             raise SkipTest(
     57                 f"To run this test make sure that {ADDR1} and {ADDR2} (routable addresses) are "
     58                 "assigned to the interfaces on this machine and rerun with --ihave1111and2222")
     59 
     60     def run_test(self):
     61         self.log.info(
     62-                "Test that if -bind= is not passed then all addresses are "
     63+                "Test that if -bind= is not passed or -bind=0.0.0.0 is used then all addresses are "
     64                 "added to localaddresses")
     65-        found_addr1 = False
     66-        found_addr2 = False
     67-        for local in self.nodes[0].getnetworkinfo()['localaddresses']:
     68-            if local['address'] == ADDR1:
     69-                found_addr1 = True
     70-                assert_equal(local['port'], self.bind_port_0)
     71-            if local['address'] == ADDR2:
     72-                found_addr2 = True
     73-                assert_equal(local['port'], self.bind_port_0)
     74-        assert found_addr1
     75-        assert found_addr2
     76+        for i in [0, 1, 2]:
     77+            found_addr1 = False
     78+            found_addr2 = False
     79+            for local in self.nodes[i].getnetworkinfo()['localaddresses']:
     80+                if local['address'] == ADDR1:
     81+                    found_addr1 = True
     82+                    assert_equal(local['port'], self.bind_ports[i])
     83+                if local['address'] == ADDR2:
     84+                    found_addr2 = True
     85+                    assert_equal(local['port'], self.bind_ports[i])
     86+            if not found_addr1:
     87+                self.log.error(f"Address {ADDR1} not found in node{i}'s local addresses: {local}")
     88+                assert False
     89+            if not found_addr2:
     90+                self.log.error(f"Address {ADDR2} not found in node{i}'s local addresses: {local}")
     91+                assert False
     92 
     93         self.log.info(
     94-                "Test that if -bind= is passed then only that address is "
     95+                "Test that if -bind=routable_addr is passed then only that address is "
     96                 "added to localaddresses")
     97         found_addr1 = False
     98-        for local in self.nodes[1].getnetworkinfo()['localaddresses']:
     99+        i = 3
    100+        for local in self.nodes[i].getnetworkinfo()['localaddresses']:
    101             if local['address'] == ADDR1:
    102                 found_addr1 = True
    103-                assert_equal(local['port'], self.bind_port_1)
    104+                assert_equal(local['port'], self.bind_ports[i])
    105             assert_not_equal(local['address'], ADDR2)
    106         assert found_addr1
    107 
    108 if __name__ == '__main__':
    109     BindPortDiscoverTest(__file__).main()
    

    b-l-u-e commented at 8:51 am on July 1, 2025:
    thank you for the suggestion..i will make changes
  14. vasild commented at 11:12 am on June 30, 2025: contributor
    Approach ACK 776b7ee585
  15. b-l-u-e force-pushed on Jul 4, 2025
  16. b-l-u-e commented at 6:50 am on July 4, 2025: none

    @vasild Thanks for the suggested improvements! I have a question about the self.extra_args configuration.

    In your patch, node 2 only has -whitebind=0.0.0.0 without any onion bind:

    0['-discover', f'-whitebind=0.0.0.0:{self.bind_ports[2]}'], # Explicit -whitebind=0.0.0.0
    

    However, when I tried this configuration, I encountered port conflicts because the test framework automatically adds an onion bind to node 2, which conflicts with the port already used by node 1.

    Test Results:

    Without dummy onion bind (your suggested configuration):

    0Error: Unable to bind to 127.0.0.1:12866 on this computer. Bitcoin Core is probably already running.
    1Error: Failed to listen on any port. Use -listen=0 if you want this.
    

    With dummy onion bind (current implementation):

    0TestFramework (INFO): Tests successful
    

    The issue is that when using only -whitebind=0.0.0.0, the test framework automatically adds an onion bind to the same node, causing a port conflict with other nodes in the test.

    To avoid this, I added a dummy onion bind:

    0['-discover', f'-whitebind=0.0.0.0:{self.bind_ports[2]}', f'-bind=127.0.0.1:{self.bind_ports[2]+100}=onion']
    

    This ensures the test runs successfully while still testing the intended -whitebind=0.0.0.0 functionality. Would you like me to explore alternative approaches to avoid this port conflict?

    what do you think?

  17. DrahtBot added the label CI failed on Jul 4, 2025
  18. DrahtBot commented at 6:56 am on July 4, 2025: contributor

    🚧 At least one of the CI tasks failed. Task lint: https://github.com/bitcoin/bitcoin/runs/45342616996 LLM reason (✨ experimental): The CI failure is caused by lint errors due to trailing whitespace and missing trailing newline in the code.

    Try to run the tests locally, according to the documentation. However, a CI failure may still 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.

  19. DrahtBot removed the label CI failed on Jul 4, 2025
  20. net: Fix Discover() not running when using -bind=0.0.0.0:port and -whitebind=0.0.0.0 496c3ef227
  21. test: Update feature_bind_port_discover.py to use dynamic ports and add comprehensive test coverage ad3f60b0ea
  22. b-l-u-e force-pushed on Jul 4, 2025
  23. b-l-u-e requested review from vasild on Jul 4, 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: 2025-07-11 12:13 UTC

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