Since #34158 the reconnect backoff was only applied when connecting to the Tor control port failed. When an established connection was dropped, for example by Tor closing it after a failed AUTHENTICATE because of a wrong password, the control thread reconnected immediately in a loop without any wait. This was resulting in us trying to make tons of connections to torcontrol and producing tons of log entries in very short time. The fix restores the pre-#34158 behavior where every reconnect waits for the backoff timeout by going through disconnected_cb, which now also does the waiting. Also adds a functional test to cover that the waiting behavior is actually applied.
torcontrol: Use reconnect backoff after dropped connections #36260
pull fjahr wants to merge 1 commits into bitcoin:master from fjahr:2026-09-torcontrol-backoff changing 2 files +32 −33-
fjahr commented at 12:10 PM on September 15, 2026: contributor
-
DrahtBot commented at 12:10 PM on September 15, 2026: contributor
<!--e57a25ab6845829454e8d69fc972939a-->
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
<!--006a51241073e994b41acfe9ec718e94-->
Code Coverage & Benchmarks
For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/36260.
<!--021abf342d371248e50ceaed478a90ca-->
Reviews
See the guideline and AI policy for information on the review process.
Type Reviewers ACK willcl-ark, winterrdog, sedited Concept ACK fanquake If your review is incorrectly listed, please copy-paste <code><!--meta-tag:bot-skip--></code> into the comment that the bot should ignore.
<!--174a7506f384e20aa4161008e828411d-->
Conflicts
Reviewers, this pull request conflicts with the following ones:
- #36142 (net: validate Tor onion service replies and cached keys by l0rinc)
- #35292 (test: Add coverage for Tor control
HASHEDPASSWORDauthentication by winterrdog) - #34486 (net: Reduce local network activity when networkactive=0 by willcl-ark)
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.
<!--5faf32d7da4f0f540f40219e4f7537a3-->
- willcl-ark approved
-
willcl-ark commented at 2:01 PM on September 15, 2026: member
crACK 7fb0f7536773adb7cd7e52ff1be8adb455381bee
Restoring the reconnect backoff seems wise here; no point spamming the tor daemon with failures and flooding our own logs with fail spam in the case that e.g. the tor daemon is restarting or something.
The functional test covers the missing delay after a server closes the connection which seems OK.
- fanquake requested review from pinheadmz on Sep 15, 2026
-
fanquake commented at 2:03 PM on September 15, 2026: member
https://github.com/bitcoin/bitcoin/actions/runs/34967300433/job/104380659347?pr=36260#step:11:1818:
test 2026-09-15T13:34:29.088864Z TestFramework.utils (ERROR): wait_until() failed. Predicate: '''' self.wait_until(lambda: len(mock_tor.received_commands) == initial_len + 1) ''' test 2026-09-15T13:34:29.089353Z TestFramework (ERROR): Unexpected exception: Traceback (most recent call last): File "D:\a\bitcoin\bitcoin\test\functional\test_framework\test_framework.py", line 145, in main self.run_test() ~~~~~~~~~~~~~^^ File "D:\a\bitcoin\bitcoin/test/functional/feature_torcontrol.py", line 280, in run_test self.test_reconnect_backoff() ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^ File "D:\a\bitcoin\bitcoin/test/functional/feature_torcontrol.py", line 267, in test_reconnect_backoff with self.expect_disconnect(True, mock_tor): ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^ File "C:\hostedtoolcache\windows\Python\3.14.7\x64\Lib\contextlib.py", line 148, in __exit__ next(self.gen) ~~~~^^^^^^^^^^ File "D:\a\bitcoin\bitcoin/test/functional/feature_torcontrol.py", line 138, in expect_disconnect self.wait_until(lambda: len(mock_tor.received_commands) == initial_len + 1) ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\a\bitcoin\bitcoin\test\functional\test_framework\test_framework.py", line 747, in wait_until return wait_until_helper_internal(test_function, timeout=timeout, timeout_factor=self.options.timeout_factor, check_interval=check_interval) File "D:\a\bitcoin\bitcoin\test\functional\test_framework\util.py", line 451, in wait_until_helper_internal raise AssertionError("Predicate {} not true after {} seconds".format(predicate_source, timeout)) AssertionError: Predicate '''' self.wait_until(lambda: len(mock_tor.received_commands) == initial_len + 1) ''' not true after 2400.0 seconds node0 2026-09-15T13:34:29.109503Z [torcontrol] [torcontrol.cpp:161] [ReceiveAndProcess] [warning] tor: Error reading from socket: An existing connection was forcibly closed by the remote host. (10054) test 2026-09-15T13:34:29.109532Z TestFramework (DEBUG): Closing down network thread - DrahtBot added the label CI failed on Sep 15, 2026
- fjahr force-pushed on Sep 15, 2026
-
torcontrol: Apply reconnect backoff after dropped connections 4556ef6267
-
in test/functional/feature_torcontrol.py:270 in 7fb0f75367
265 | + self.restart_with_mock(mock_tor) 266 | + 267 | + with self.expect_disconnect(True, mock_tor): 268 | + # Reply before closing, like Tor does after a failed AUTHENTICATE 269 | + mock_tor.send_raw("515 Authentication failed\r\n") 270 | + mock_tor.conn.shutdown(socket.SHUT_RDWR)
willcl-ark commented at 4:15 PM on September 15, 2026:In 7fb0f7536773adb7cd7e52ff1be8adb455381bee
Codex thinks that we should use this in the test:
mock_tor.conn.shutdown(socket.SHUT_WR)With the explanation:
The mock server handles one connection at a time. It must finish reading the old connection before returning to
accept()to handle Bitcoin Core’s reconnect.The test originally used:
mock_tor.conn.shutdown(socket.SHUT_RDWR)That shuts down both sending and receiving. Bitcoin Core sees EOF and reconnects as expected. But on Windows, the mock’s next
recv()can raiseOSErrorbecause receiving was shut down. That exception reaches_serve(), whose error handler breaks out of the server loop.The listening socket stays open, so Bitcoin Core’s reconnect succeeds at the TCP level. Nobody accepts and reads it, though, so the test waits forever for another
PROTOCOLINFOcommand.The fix uses:
mock_tor.conn.shutdown(socket.SHUT_WR)This shuts down only sending. Bitcoin Core still receives the failure reply followed by EOF. The mock can continue receiving until Bitcoin Core closes its side, at which point
recv()returns empty data normally. The handler finishes, the server accepts the reconnect, and it records the newPROTOCOLINFOcommand....which it also claims is cross-platform, though I didn't verify any of this myself.
fjahr commented at 4:25 PM on September 15, 2026:Thanks, I got a similar result when looking into it but didn't notice that option, I dealt with the
OSErrorlike it was anEOFin my first push but this seems more elegant, so I have taken this instead.fjahr force-pushed on Sep 15, 2026DrahtBot removed the label CI failed on Sep 15, 2026willcl-ark approvedwillcl-ark commented at 7:34 AM on September 16, 2026: memberreACK 4556ef62675400a4702d73c0a3e1dd4c7061a077
sedited added the label Needs Backport (32.x) on Sep 16, 2026sedited added this to the milestone 32.0 on Sep 16, 2026winterrdog commented at 7:57 AM on September 16, 2026: contributortACK 4556ef62675400a4702d73c0a3e1dd4c7061a077
makes sense to have all disconnects go through
disconnected_cb()so that they consistently get the same reconnect backoff logicfanquake commented at 10:01 AM on September 16, 2026: memberConcept ACK
sedited approvedsedited commented at 10:51 AM on September 16, 2026: contributorACK 4556ef62675400a4702d73c0a3e1dd4c7061a077
DrahtBot requested review from fanquake on Sep 16, 2026sedited merged this on Sep 16, 2026sedited closed this on Sep 16, 2026fanquake referenced this in commit deac114123 on Sep 16, 2026fanquake removed the label Needs Backport (32.x) on Sep 16, 2026ContributorsMilestone
32.0
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-09-16 16:51 UTC
More mirrored repositories can be found on mirror.b10c.me