net: Reduce local network activity when networkactive=0 #34486

pull willcl-ark wants to merge 14 commits into bitcoin:master from willcl-ark:respect-networkactive changing 15 files +760 −324
  1. willcl-ark commented at 5:46 pm on February 2, 2026: member

    Drafted until #34158 is concluded so as not to collide.

    Fixes #34190

    When networkactive=0 is set, NAT-PMP port mapping and Tor control connections still run in the background, mapping ports and logging retry attempts despite the node being “inactive.”

    This wires both subsystems to CConnman::SetNetworkActive so they start and stop with the network state:

    • mapport: EnableMapPort is injected as a callback via CConnman::Options. SetNetworkActive and SetMapPortEnabled both gate on network state.

    • torcontrol: is injected similarly. The callback dispatches onto the tor event loop via event_base_once to respect thread ownership. EVLOOP_NO_EXIT_ON_EMPTY is used to keep the event loop alive without keepalive timers, so the controller starts disconnected and waits for CConnman::Start() to signal.

  2. DrahtBot added the label P2P on Feb 2, 2026
  3. willcl-ark commented at 5:47 pm on February 2, 2026: member

    This is an alternative to #34467. The key difference is that #34467 gates mapport and tor control at init time with if (networkactive) guards, which means those subsystems are permanently disabled for the lifetime of the process. setnetworkactive true via RPC won’t start them. Boot-time and runtime behavior would now differ.

    This PR instead wires both subsystems into CConnman::SetNetworkActive so they follow the network state through the full lifecycle: starting with -networkactive=0 and later calling setnetworkactive true works as expected.

    I’m unsure how this will conflict with the work currently being done to remove libevent.

  4. DrahtBot commented at 5:47 pm on February 2, 2026: contributor

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

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    Concept ACK sedited, fanquake, Jhackman2019, brunoerg

    If your review is incorrectly listed, please copy-paste <!–meta-tag:bot-skip–> into the comment that the bot should ignore.

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #34741 (refactor: Return std::optional from GetNameProxy/GetProxy by maflcko)
    • #34534 (rpc: Manual prune lock management (Take 2) by fjahr)
    • #34520 (refactor: Add [[nodiscard]] to functions returning bool+mutable ref by maflcko)
    • #34158 (torcontrol: Remove libevent usage by fjahr)
    • #30951 (net: option to disallow v1 connection on ipv4 and ipv6 peers by stratospher)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

  5. sedited commented at 5:50 pm on February 2, 2026: contributor
    Concept ACK
  6. bradleystachurski commented at 9:42 pm on February 2, 2026: none

    Reviewed 35da4e03b18b7ef5eb23e1b038f5755fe37825a1

    Tested on Linux: started with -networkactive=0 -natpmp=1 -debug=net -debug=tor, confirmed no portmap/tor activity at startup, toggled via setnetworkactive, confirmed mapport thread start/stop and tor connection attempts follow network state.

    Verified feature_mapport.py fails when the m_mapport callback is removed from SetNetworkActive.

    nit: TorController::SetNetworkActive doesn’t reset reconnect_timeout, so re-enabling network after backoff has grown continues from the stale value. Verified this fixes it:

     0diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
     1index d5aeb55a72..934499d72b 100644
     2--- a/src/torcontrol.cpp
     3+++ b/src/torcontrol.cpp
     4@@ -677,6 +677,8 @@ void TorController::SetNetworkActive(bool set_active)
     5         // Disconnect if currently connected
     6         conn.Disconnect();
     7     }
     8+    // Reset backoff when re-enabling network
     9+    reconnect_timeout = RECONNECT_TIMEOUT_START;
    10     // Connect handles both cases: connects if active, reschedules if inactive
    11     Connect();
    12 }
    
  7. willcl-ark force-pushed on Feb 3, 2026
  8. willcl-ark commented at 9:28 am on February 3, 2026: member

    Thanks @bradleystachurski that’s a nice suggestion which I’ve taken in 745aed37224102888332823aadc6d766e701aa0b

    I also reworked the if logic slightly to make the flow of this function clearer (and not always call Connect()).

  9. fanquake commented at 10:10 am on February 3, 2026: member
    Concept ACK
  10. willcl-ark commented at 10:25 am on February 3, 2026: member
    I will address the LLM linter suggestion if I push again.
  11. Jhackman2019 commented at 3:37 am on February 7, 2026: none

    Tested this on my Pi 5 (ARM64, Debian Bookworm). Builds clean, all the networking-related tests pass:

    0feature_mapport.py                  PASSED
    1feature_proxy.py                    PASSED
    2p2p_addr_relay.py                   PASSED
    3p2p_disconnect_ban.py --v1transport PASSED
    4p2p_disconnect_ban.py --v2transport PASSED
    5p2p_dns_seeds.py                    PASSED
    

    Had to clear my test/cache first since I had a stale cache from an autotools build — after that everything was smooth.

    Concept ACK

  12. brunoerg commented at 1:47 pm on February 17, 2026: contributor
    Concept ACK
  13. brunoerg commented at 5:10 pm on February 17, 2026: contributor
    tested up to 396b56af0ac261863b076ab8d2d02a99f6e03f3a: I checked that portmap activity follows network activity.
  14. in src/torcontrol.cpp:639 in 745aed3722 outdated
    635@@ -640,6 +636,11 @@ void TorController::disconnected_cb(TorControlConnection& _conn)
    636     if (!reconnect)
    637         return;
    638 
    639+    if (!m_network_active) {
    


    brunoerg commented at 5:18 pm on February 17, 2026:

    Is there any way to test this condition? I’ve tried several scenarios manually, but haven’t been able to achieve it.

    However, checking that it is not connected to any Tor control port and then will retry was easier, could also check it on a functional test, e.g:

     0diff --git a/test/functional/p2p_private_broadcast.py b/test/functional/p2p_private_broadcast.py
     1index 4943841790..b163020e85 100755
     2--- a/test/functional/p2p_private_broadcast.py
     3+++ b/test/functional/p2p_private_broadcast.py
     4@@ -433,9 +433,11 @@ class P2PPrivateBroadcast(BitcoinTestFramework):
     5             # the RPC should throw.
     6             "-torcontrol=127.0.0.1:1",
     7             "-listenonion",
     8+            "-debug=tor",
     9         ])
    10-        assert_raises_rpc_error(-1, "none of the Tor or I2P networks is reachable",
    11-                                tx_originator.sendrawtransaction, hexstring=txs[0]["hex"], maxfeerate=0.1)
    12+        with tx_originator.assert_debug_log(['Not connected to Tor control port'], timeout=5):
    13+            assert_raises_rpc_error(-1, "none of the Tor or I2P networks is reachable",
    14+                                    tx_originator.sendrawtransaction, hexstring=txs[0]["hex"], maxfeerate=0.1)
    

    willcl-ark commented at 4:20 pm on February 19, 2026:

    Thanksf or the review!

    Yes I think this is hard to reach in a functional test, as conn.Disconnect() frees the bufferevent without firing the callback, so this guard only triggers if a libevent disconnect event was already queued when SetNetworkActive(false) runs.

    It’s mainly intended to be defensive against an event-loop ordering edge case, and even if it wasn’t here, Connect() has its own m_network_active check, but this guard also prevents a misleading “retrying” log message.

    Your p2p_private_broadcast.py change seems like good coverage for the reworked connection initiation path. Happy to include it.

  15. willcl-ark marked this as a draft on Mar 15, 2026
  16. willcl-ark commented at 9:03 pm on March 15, 2026: member
    Drafting this to rework/rebase on #34158, and not undo libevent removal.
  17. willcl-ark force-pushed on Mar 16, 2026
  18. DrahtBot added the label Needs rebase on Mar 23, 2026
  19. refactor: Use constexpr in torcontrol where possible eeb9350a99
  20. refactor: Modernize member variable names in torcontrol bcbf98225f
  21. refactor: Get rid of unnecessary newlines in logs 8e01fcf2ec
  22. torcontrol: Remove libevent usage
    Replace libevent-based approach with using the Sock class and CThreadInterrupt.
    44550813f1
  23. torcontrol: Move tor controller into node context
    Co-authored-by: sedited <seb.kung@gmail.com>
    87fcfcfe9b
  24. fuzz: Improve torcontrol fuzz test
    Gets rid of the Dummy class and adds coverage of get_socks_cb.
    
    Also explicitly handles an exception case within Torcontrol rather than
    relying on a guard in the fuzz test.
    7abbbf1693
  25. test: Add simple functional test for torcontrol 81c0f8f299
  26. test: Add test for partial message handling in torcontrol 4d9dc7d50c
  27. test: Add torcontrol coverage for PoW defense enablement 19f02779c1
  28. net: wire mapport lifecycle to CConnman
    When -networkactive=0 is set, NAT-PMP port mapping was still running and
    attempting gateway queries. Move mapport lifecycle management into
    CConnman, which already owns SetNetworkActive, so it can control mapport
    based on both the -natpmp setting and network state.
    
    EnableMapPort is injected as a callback via CConnman::Options to avoid a
    circular dependency between net and mapport.
    5e6abe1985
  29. test: add test for mapport networkactive
    Add functional test to verify that PCP/NAT-PMP port mapping respects the
    networkactive state:
    
    - Does not run when started with -networkactive=0
    - Starts when network is activated via setnetworkactive RPC
    - Stops when network is deactivated
    - Resumes when network is reactivated
    123beb4b01
  30. net: run tor control based on networkactive
    Wire tor control lifecycle to CConnman on top of the libevent-free tor
    controller being introduced in upstream/pr/34158.
    
    Have CConnman propagate networkactive changes to the node-owned
    TorController and make the controller thread idle while networking is
    disabled instead of attempting Tor control connections or reconnects.
    2f7b08c272
  31. test: add torcontrol networkactive coverage
    Add functional coverage for torcontrol when networkactive is disabled at startup and toggled back on later.
    
    Also clear the mock server connection handle on close so the disconnect assertion is reliable.
    4174f20fdb
  32. test: check tor control reconnection in p2p_private_broadcast
    Verify that the tor controller attempts reconnection to the control port
    by asserting the retry log message when started with an unreachable
    `-torcontrol` address.
    
    Co-authored-by: brunoerg <brunoerg@users.noreply.github.com>
    98ddbb6999
  33. willcl-ark force-pushed on Mar 24, 2026
  34. DrahtBot added the label CI failed on Mar 24, 2026
  35. DrahtBot removed the label Needs rebase on Mar 24, 2026
  36. DrahtBot added the label Needs rebase on Mar 26, 2026
  37. DrahtBot commented at 7:45 pm on March 26, 2026: contributor
    🐙 This pull request conflicts with the target branch and needs rebase.

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-03-30 12:13 UTC

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