test: node will not try to connect to anchors from an unreachable network #34431

pull brunoerg wants to merge 2 commits into bitcoin:master from brunoerg:2026-01-test-anchors-unreachable changing 2 files +15 −2
  1. brunoerg commented at 2:28 pm on January 28, 2026: contributor

    This PR adds a test to check that a node will not try to connect to an anchor from an unreachable network when initializing. It kills and can be tested with the following mutant:

     0diff --git a/src/net.cpp b/src/net.cpp
     1index 6b79e913e8..cd8aa4c502 100644
     2--- a/src/net.cpp
     3+++ b/src/net.cpp
     4@@ -2774,7 +2774,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
     5             if (anchor && !m_anchors.empty()) {
     6                 const CAddress addr = m_anchors.back();
     7                 m_anchors.pop_back();
     8-                if (!addr.IsValid() || IsLocal(addr) || !g_reachable_nets.Contains(addr) ||
     9+                if (!addr.IsValid() || IsLocal(addr) ||
    10                     !m_msgproc->HasAllDesirableServiceFlags(addr.nServices) ||
    11                     outbound_ipv46_peer_netgroups.contains(m_netgroupman.GetGroup(addr))) continue;
    12                 addrConnect = addr;
    

    Also, it fixes a bug in assert_debug_log which has a timing asymmetry between expected and unexpected message checking. When all expected messages were found early, the function would return immediately without waiting for the full timeout period. This created a race condition where unexpected messages appearing after the expected messages (but before the timeout) would not be detected.

  2. test: check node will not attempt to connect to anchors from unreachable networks d39c7196b5
  3. test: fix unexpected_msgs logic in assert_debug_log
    The assert_debug_log has a timing asymmetry between expected and unexpected message
    checking. When all expected messages were found early, the function would return
    immediately without waiting for the full timeout period. This created a race condition
    where unexpected messages appearing after the expected messages (but before the timeout)
    would not be detected
    5efca62f3d
  4. DrahtBot added the label Tests on Jan 28, 2026
  5. DrahtBot commented at 2:28 pm on January 28, 2026: 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/34431.

    Reviews

    See the guideline for information on the review process. A summary of reviews will appear here.

  6. bitcoin deleted a comment on Jan 28, 2026
  7. in test/functional/test_framework/test_node.py:579 in 5efca62f3d
    575@@ -576,8 +576,9 @@ def join_log(log):
    576             while remaining_expected and remaining_expected[-1] in log:
    577                 remaining_expected.pop()
    578             if not remaining_expected:
    579-                return
    580-            if time.time() >= time_end:
    581+                if time.time() >= time_end:
    


    maflcko commented at 3:09 pm on January 28, 2026:

    not sure about this. assert_debug_log should not be used with a timeout value at all, because that makes tests hard to read and understand.

    Putting more features behind the timeout here seems backward.

    Also, it seems brittle, because the timeout can be scaled by the test runner, so a failing test on one machine may pass on the next machine when the timeout is decreased, which seems backward.

    Also, scaling the timeout up will then needlessly block the test


    brunoerg commented at 4:17 pm on January 28, 2026:

    Yes, was wondering about the same, not trusting the timeout is better and would avoid any chance of having intermittent issues as well. My first idea was something like:

     0         self.log.info("Check node will not attempt to connect to anchors from unreachable networks")
     1-        with self.nodes[0].assert_debug_log(timeout=60, expected_msgs=["Loaded 1 addresses from \"anchors.dat\""],
     2-            unexpected_msgs=[f"Trying to make an anchor connection to {ONION_ADDR}"]):
     3-            self.start_node(0)
     4+        assertion_raised = False
     5+        try:
     6+            with self.nodes[0].assert_debug_log([f"Trying to make an anchor connection to {ONION_ADDR}"]):
     7+                self.start_node(0)
     8+        except AssertionError:
     9+            assertion_raised = True
    10+        assert_equal(assertion_raised, True)
    

    What do you think?


    maflcko commented at 4:22 pm on January 28, 2026:

    I think the same issues exist here as well:

    Also, it seems brittle, because the timeout can be scaled by the test runner, so a failing test on one machine may pass on the next machine when the timeout is decreased, which seems backward.

    Also, scaling the timeout up will then needlessly block the test


    brunoerg commented at 4:34 pm on January 28, 2026:

    Increasing or decreasing the timeout here would not be a huge problem here because we’re expecting this log to never appear, so the only chance of having an issue here would be if someone tries to test it against the provided mutant. Even in this case, I think it would hard to have an issue because this is one of the first actions when starting a node.

    Ensuring it’s not appearing may have less issues then trying to assert it?


    maflcko commented at 4:49 pm on January 28, 2026:
    Why not. If someone sets the timeout factor to 999, how long will the test take and do nothing all the time?

    brunoerg commented at 4:56 pm on January 28, 2026:
    Fair enough. Going to close it, since there is no other way to test it.

    maflcko commented at 5:07 pm on January 28, 2026:

    I am not familiar with the internal code, but in theory the idea is that you do any action which only can happen after:

    code on master:

    0        self.log.info("Check node will not attempt to connect to anchors from unreachable networks")
    1        with self.nodes[0].assert_debug_log(timeout=60, expected_msgs=["Loaded 1 addresses from \"anchors.dat\""],
    2            unexpected_msgs=[f"Trying to make an anchor connection to {ONION_ADDR}"]):
    3            self.start_node(0)
    4            # any action that happens after trying to make anchor connections
    5            # maybe adding a real connection and waiting for it to be established?
    
  8. brunoerg closed this on Jan 28, 2026

  9. brunoerg deleted the branch on Jan 28, 2026

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-02-02 06:13 UTC

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