net: Prevent node from binding to the same CService #33231

pull w0xlt wants to merge 1 commits into bitcoin:master from w0xlt:mulitple_binds changing 2 files +62 −0
  1. w0xlt commented at 11:52 pm on August 20, 2025: contributor

    Currently, if the node inadvertently starts with repeated -bind options (e.g. ./build/bin/bitcoind -listen -bind=0.0.0.0 -bind=0.0.0.0), the user will receive a misleading message followed by the node shutdown:

    0[net:error] Unable to bind to 0.0.0.0:8333 on this computer. Bitcoin Core is probably already running.
    1[error] Unable to bind to 0.0.0.0:8333 on this computer. Bitcoin Core is probably already running.
    

    And the user might spend some time looking for a bitcoind process or what application is using port 8333, when what happens is that Bitcoin Core successfully connected to port 8333 and then tries again, generating this fatal error.

    This PR proposes that repeated -bind options have no effect.

  2. DrahtBot added the label P2P on Aug 20, 2025
  3. DrahtBot commented at 11:52 pm on August 20, 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/33231.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK vasild
    Concept ACK jonatack, yuvicc, cedwies, l0rinc, naiyoma

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

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #29415 (Broadcast own transactions only via short-lived Tor or I2P connections by vasild)

    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.

    LLM Linter (✨ experimental)

    Possible typos and grammar issues:

    • configuration -> configurations [“across all binding configuration” should be pluralized to “configurations” for grammatical correctness]

    drahtbot_id_4_m

  4. in src/net.h:1593 in 132a59698e outdated
    1589@@ -1590,7 +1590,7 @@ class CConnman
    1590      * A vector of -bind=<address>:<port>=onion arguments each of which is
    1591      * an address and port that are designated for incoming Tor connections.
    1592      */
    1593-    std::vector<CService> m_onion_binds;
    1594+    std::set<CService> m_onion_binds;
    


    jonatack commented at 0:37 am on August 21, 2025:
    Would need to update the doxygen (“A vector…”)
  5. jonatack commented at 0:40 am on August 21, 2025: member

    Concept ACK

    This PR proposes that repeated -bind options have no effect.

    Only no effect for repeated -bind options having the same value, yes? Would it be preferable to give the user feedback instead?

    Would be good to add test coverage.

  6. yuvicc commented at 6:29 am on August 21, 2025: contributor
    Concept ACK
  7. in src/net.h:1072 in 132a59698e outdated
    1067@@ -1068,9 +1068,9 @@ class CConnman
    1068         std::vector<std::string> vSeedNodes;
    1069         std::vector<NetWhitelistPermissions> vWhitelistedRangeIncoming;
    1070         std::vector<NetWhitelistPermissions> vWhitelistedRangeOutgoing;
    1071-        std::vector<NetWhitebindPermissions> vWhiteBinds;
    1072-        std::vector<CService> vBinds;
    1073-        std::vector<CService> onion_binds;
    1074+        std::set<NetWhitebindPermissions> vWhiteBinds;
    1075+        std::set<CService> vBinds;
    


    l0rinc commented at 4:57 pm on August 21, 2025:
    the v prefix doesn’t make a lot of sense anymore, if they’re not vectors
  8. in src/net.h:1590 in 132a59698e outdated
    1589@@ -1590,7 +1590,7 @@ class CConnman
    1590      * A vector of -bind=<address>:<port>=onion arguments each of which is
    


    l0rinc commented at 4:59 pm on August 21, 2025:
    • I’d appreciate a test that checks what happens in this case, as @jonatack also mentioned
    • a release‑notes line under P2P noting that repeated -bind and -whitebind arguments are now deduplicated
  9. in src/net_permissions.h:89 in 132a59698e outdated
    83@@ -84,6 +84,10 @@ class NetWhitebindPermissions : public NetPermissions
    84 public:
    85     static bool TryParse(const std::string& str, NetWhitebindPermissions& output, bilingual_str& error);
    86     CService m_service;
    87+
    88+    bool operator<(const NetWhitebindPermissions& other) const {
    89+        return m_service < other.m_service;
    


    l0rinc commented at 5:05 pm on August 21, 2025:

    isn’t it confusing that in case of service duplicates the it’s not obvious which m_flags is ignored?

    What do we expect to happen in case of something like -whitebind=127.0.0.1:8335=relay,noban -whitebind=127.0.0.1:8335=relay vs -whitebind=127.0.0.1:8335=relay -whitebind=127.0.0.1:8335=relay,noban? Or even worse, -whitebind=127.0.0.1:8335=all -whitebind=127.0.0.1:8335=relay,mempool?

  10. in src/net_permissions.h:88 in 132a59698e outdated
    83@@ -84,6 +84,10 @@ class NetWhitebindPermissions : public NetPermissions
    84 public:
    85     static bool TryParse(const std::string& str, NetWhitebindPermissions& output, bilingual_str& error);
    86     CService m_service;
    87+
    88+    bool operator<(const NetWhitebindPermissions& other) const {
    


    l0rinc commented at 5:05 pm on August 21, 2025:

    We could modernize this to C++20

    0std::weak_ordering operator<=>(NetWhitebindPermissions const& other) const
    

    nit: formatting is off

  11. l0rinc changes_requested
  12. l0rinc commented at 5:17 pm on August 21, 2025: contributor

    Concept ACK, but approach NACK 132a59698e2df2e25fdbae3a6fba308b131419d2

    I only did a code review pass on it, please let me know if my concerns are off, happy to ack if I was mistaken. My biggest concern currently is silently dropping values by partial duplication, e.g. -whitebind=[::1]:8335=relay -whitebind=[::1]:8335=relay,addr.

  13. cedwies commented at 4:31 pm on August 22, 2025: none

    Concept ACK

    One point to consider is a behavioral change in how the onion_service_target is selected. Consider:

    0-bind=192.167.1.10:8333=onion
    1-bind=127.0.0.1:8333=onion
    

    Currently 192.167.1.10:8333 will be selected as onion_service_target. With this PR, it will be changed to 127.0.0.1:8333 (though one line in logs shows which address is used as target).

    One option would be to keep using sets for deduplication, while still preserving the historical “first specified wins” rule when selecting onion_service_target. That way dedup works as intended without changing existing semantics, and it matches operator expectations (since users may typically think in terms of argument order, not CService’s comparator). On the other hand, one could argue that relying on argument order is fragile, and that making the selection order-independent is preferable. Any thoughts?

  14. l0rinc commented at 7:23 pm on August 22, 2025: contributor
    Since these lists are tiny, Instead of a set for deduplication, we might as well use a simple O^2 algorithm, iterate from the back and remove whatever appeared in the front (it will likely be even faster than a set). If you decide to do that, I’d appreciate a randomized unit test checking it against a set deduplication.
  15. w0xlt force-pushed on Aug 24, 2025
  16. w0xlt force-pushed on Aug 24, 2025
  17. DrahtBot added the label CI failed on Aug 24, 2025
  18. DrahtBot commented at 5:32 am on August 24, 2025: contributor

    🚧 At least one of the CI tasks failed. Task lint: https://github.com/bitcoin/bitcoin/runs/48752053890 LLM reason (✨ experimental): The CI failure is caused by errors detected by the linter ruff, specifically two fixable issues in Python code: missing trailing newline and an f-string without placeholders.

    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. w0xlt force-pushed on Aug 24, 2025
  20. w0xlt force-pushed on Aug 24, 2025
  21. w0xlt commented at 6:11 am on August 24, 2025: contributor

    Thank you all for the reviews. I completely changed the approach.

    While addressing the reviews, I realized that using std::set to avoid duplicates doesn’t solve the problem. If the options with same address and port but different network like -bind=127.0.0.1:8333 -bind=127.0.0.1:8333=onion are present, the node will crash anyway, as one option will be allocated in the CConnman::Options::vBinds vector and the other in CConnman::Options::onion_binds.

    Therefore, even after deduplicating each vector, it is still necessary to check for conflicts between vectors.

    I used the following approach: If the duplicate binds are the same (e.g., bind=0.0.0.0 -bind=0.0.0.0), they will be ignored. But if they are different (e.g., bind=0.0.0.0:10080 -bind=0.0.0.0:10080=onion), the node will be terminated with a clear and specific message (e.g., Different bind settings assigned to address 0.0.0.0:10080), since we cannot know the user’s true intent.

    I added the tests as requested. For more explanation about the first commit, see #33250.

  22. DrahtBot removed the label CI failed on Aug 24, 2025
  23. in src/init.cpp:1305 in 1001b7ae76 outdated
    1300+        whitebind_services.insert(wb.m_service);
    1301+    }
    1302+
    1303+    // Check if any service from vBinds exists in vWhiteBinds
    1304+    for (const auto& bind : vBinds) {
    1305+        if (whitebind_services.count(bind)) {
    


    l0rinc commented at 8:47 pm on August 24, 2025:

    nit:

    0        if (whitebind_services.contains(bind)) {
    
  24. in src/init.cpp:1252 in 1001b7ae76 outdated
    1247+ * @return std::optional<CService> containing the first conflict found, or std::nullopt if no conflicts
    1248+ */
    1249+std::optional<CService> DeduplicateAndCheckBindingConflicts(
    1250+    std::vector<NetWhitebindPermissions>& vWhiteBinds,
    1251+    std::vector<CService>& vBinds,
    1252+    std::vector<CService>& onion_binds)
    


    l0rinc commented at 8:53 pm on August 24, 2025:
    output parameters are quite hard to reason about - given that the call-site assigns connOptions directly, can we either split this into 2-3 helper methods that return the new values (the vectors are tiny, they’re likely as fast as passing it around), or if you insist on this big one, can we pass a single CConnman::Options& connOptions instead? Also, some branches return, others don’t erase, others replace one of the inputs - seems like has more than a single, easily defined responsibility (the name even has an And in the name), I personally would prefer doing the same in more focused methods.
  25. in src/init.cpp:1279 in 1001b7ae76 outdated
    1274+    }
    1275+
    1276+    // Step 2: Simple deduplication for vBinds
    1277+    {
    1278+        std::set<CService> seen;
    1279+        auto it = std::remove_if(vBinds.begin(), vBinds.end(),
    


    l0rinc commented at 9:07 pm on August 24, 2025:

    nit:

    0        auto it = std::ranges::remove_if(onion_binds,
    
  26. in src/init.cpp:1270 in 1001b7ae76 outdated
    1265+            } else if (it->second == wb.m_flags) {
    1266+                // Same service, same permissions - harmless duplicate, skip it
    1267+                continue;
    1268+            } else {
    1269+                // Same service, different permissions - conflict!
    1270+                return wb.m_service;
    


    l0rinc commented at 9:08 pm on August 24, 2025:
    Since we don’t expect this to happen often, I guess it’s enough to stop at the first error instead of listing all 👍
  27. in src/init.cpp:1311 in 1001b7ae76 outdated
    1306+            return bind;  // Conflict between vBinds and vWhiteBinds
    1307+        }
    1308+    }
    1309+
    1310+    // Check if any service from onion_binds exists in vWhiteBinds or vBinds
    1311+    std::set<CService> bind_services(vBinds.begin(), vBinds.end());
    


    l0rinc commented at 0:11 am on August 25, 2025:
    as mentioned before, I think we can safely check vBinds directly - it’s simpler and likely even faster than this
  28. in src/init.cpp:1251 in 1001b7ae76 outdated
    1296+    // Step 4: Check for cross-vector conflicts
    1297+    // Build sets for efficient lookup
    1298+    std::set<CService> whitebind_services;
    1299+    for (const auto& wb : vWhiteBinds) {
    1300+        whitebind_services.insert(wb.m_service);
    1301+    }
    


    l0rinc commented at 0:12 am on August 25, 2025:
    do we really need to recreate this or can we reuse seen_whitebinds or even better, just iterate vWhiteBinds values directly?
  29. in test/functional/feature_bind_extra.py:68 in 6b448e2eab outdated
    74+                [(loopback_ipv4, port), (loopback_ipv4, port + 1)],
    75+                "both -bind and -bind=...=onion"
    76             ],
    77         )
    78-        port += 2
    79+        port += 2 # Increment port to avoid conflicts
    


    l0rinc commented at 0:26 am on August 25, 2025:

    nit: in first case increment means +1, here it’s +1 - but more generally, the code is already, it’s enough if we just document the intent:

    0        port += 2 # avoid conflicts since we've used two ports before
    

    or even better, why are we even reusing the same variable, why not create a port iterator and instead of the list a lambda accepting two ports something like (untested, since this test is skipped on a Mac):

     0def run_test(self):
     1    lp = addr_to_hex("127.0.0.1")
     2    test_cases = [
     3        lambda p, _: ([[f"-bind=127.0.0.1:{p}=onion"], # onion-only
     4                       [(lp, p)], "no normal -bind with -bind=...=onion"]),
     5        lambda p, q: ([[f"-bind=127.0.0.1:{p}", f"-bind=127.0.0.1:{q}=onion"], # bind + onion
     6                       [(lp, p), (lp, q)], "both -bind and -bind=...=onion"]),
     7        lambda p, _: ([[f"-bind=127.0.0.1:{p}"], # bind only
     8                       [(lp, p)], "no -bind=...=onion"]),
     9        lambda p, q: ([[f"-bind=127.0.0.1:{p}", f"-bind=127.0.0.1:{p}", f"-bind=127.0.0.1:{q}"], # duplicated bind
    10                       [(lp, p), (lp, p), (lp, q)], "duplicated -bind=... and -bind=..."]),
    11        lambda p, q: ([[f"-bind=127.0.0.1:{p}=onion", f"-bind=127.0.0.1:{p}=onion", f"-bind=127.0.0.1:{q}=onion"], # duplicated onion bind
    12                       [(lp, p), (lp, p), (lp, q)], "duplicated -bind=...=onion and -bind=...=onion"]),
    13    ]
    14
    15    ports = itertools.count(p2p_port(self.num_nodes))
    16    for i, make in enumerate(test_cases):
    17        args, expected_services, description = make(next(ports), next(ports))
    18        self.log.info(f"Test case {i + 1}: {description}")
    19        self.log.info(f"Restarting node 0 with args: {args}")
    

    It’s also a bit inconsistent that at the end we just hardcode -bind=127.0.0.1:11012, maybe we could extract that port as well

  30. in src/init.cpp:1293 in 79d2c0feda outdated
    1288+        std::set<CService> seen;
    1289+        auto it = std::remove_if(onion_binds.begin(), onion_binds.end(),
    1290+            [&seen](const CService& service) {
    1291+                return !seen.insert(service).second;  // Remove if already seen
    1292+            });
    1293+        onion_binds.erase(it, onion_binds.end());
    


    l0rinc commented at 4:28 am on August 25, 2025:
    I find it a bit inconsistent that we’re not erring here - we don’t know why they provided the same thing twice, seems like a mistake, not sure we should attempt to correct it silently. Would also simplify the code if we unified the way we handle these
  31. l0rinc changes_requested
  32. l0rinc commented at 4:31 am on August 25, 2025: contributor

    Concept ACK

    I personally would not try to correct the behavior (especially silently), given that it’s rare, I would simply give the user a warning or error and let them fix the arguments.

    I also think the tests are a bit complicated and more stateful now, I have added a few suggestions, maybe we can simplify based on them.

  33. in src/init.cpp:1261 in 1001b7ae76 outdated
    1256+        std::map<CService, NetPermissionFlags> seen_whitebinds;
    1257+        std::vector<NetWhitebindPermissions> deduplicated;
    1258+
    1259+        for (const auto& wb : vWhiteBinds) {
    1260+            auto it = seen_whitebinds.find(wb.m_service);
    1261+            if (it == seen_whitebinds.end()) {
    


    l0rinc commented at 4:34 am on August 25, 2025:
    nit: would make sense to invert this if wee keep this format, it makes a bit more sense to use it in the condition where we’re checking it (would also enable embedding it in the condition). Not sure this applies if you remove the reundant deduplication code and use iteration instead of sets. I think that would simplify all of this considerably.
  34. net: Prevent node from binding to the same CService
    Currently, if the user inadvertently starts the node with duplicate bind options,
    such as `-bind=0.0.0.0 -bind=0.0.0.0`, it will cause a fatal error with the
    misleading message "Bitcoin Core is probably already running".
    
    This commit adds early validation to detect duplicate bindings across all binding
    configurations (-bind, -whitebind, and onion bindings) before attempting to bind.
    When duplicates are detected, the node terminates with a clear, specific error
    message: "Duplicate binding configuration for address <addr>. Please check your
    -bind, -whitebind, and onion binding settings."
    
    The validation catches duplicates both within the same option type (e.g.,
    `-bind=X -bind=X`) and across different types (e.g., `-bind=X -whitebind=Y@X`),
    helping users identify and fix configuration mistakes.
    28fb7f74c4
  35. in test/functional/feature_bind_extra.py:133 in 1001b7ae76 outdated
    150+
    151+        self.stop_node(0)
    152+
    153+        self.nodes[0].assert_start_raises_init_error(
    154+            ["-bind=127.0.0.1:11012", "-bind=127.0.0.1:11012=onion"],
    155+            "Different binding configurations assigned to the address 127.0.0.1:11012",
    


    l0rinc commented at 4:37 am on August 25, 2025:
    nit: if it’s a regex, we need to escape some chars
  36. w0xlt force-pushed on Aug 26, 2025
  37. w0xlt commented at 3:32 am on August 26, 2025: contributor

    @l0rinc thank you for your detailed reviews.

    I personally would not try to correct the behavior (especially silently), given that it’s rare, I would simply give the user a warning or error and let them fix the arguments.

    I adjusted the approach to meet this requirement. Now when duplicates are detected, the node terminates with a clear, specific error message: “Duplicate binding configuration for address < addr >. Please check your -bind, -whitebind, and onion binding settings.

    I don’t have a strong opinion on whether the node should try to correct the behavior or not. For me, the main issue is the misleading message.

    This version is much more simplified compared to the previous ones

  38. in src/init.cpp:2180 in 28fb7f74c4
    2175@@ -2142,6 +2176,12 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
    2176 
    2177     connOptions.m_i2p_accept_incoming = args.GetBoolArg("-i2pacceptincoming", DEFAULT_I2P_ACCEPT_INCOMING);
    2178 
    2179+    if (auto conflict = CheckBindingConflicts(connOptions)) {
    2180+        return InitError(Untranslated(strprintf("Duplicate binding configuration for address %s. "
    


    cedwies commented at 2:07 pm on August 26, 2025:

    Nice and clear! One small UX suggestion: since we already know which container the duplicate came from, it could be helpful to mention the categories in the error (e.g. “-bind vs -whitebind”). That said, even without categories, I think this should at least be translatable for consistency with nearby init errors (e.g. i2psam). Something like:

    0return InitError(strprintf(
    1    _("Duplicate binding configuration for address %s. "
    2      "Please check your -bind, -whitebind, and onion binding settings."),
    3    conflict->ToStringAddrPort()));
    

    vasild commented at 9:04 am on September 2, 2025:

    since we already know which container the duplicate came from

    That is known inside CheckBindingConflicts() but the info is not signaled to the caller of the function. Also, even inside the function, only one of the containers is known, the other is not. E.g. if the 3rd for-loop finds a duplicate, then it is unknown whether the entry was added in the first or the second for-loop.

    I think this should at least be translatable for consistency with nearby init errors

    Right, +1 for translating it.

  39. in test/functional/feature_bind_extra.py:94 in 28fb7f74c4
    87@@ -87,5 +88,26 @@ def run_test(self):
    88             binds = set(filter(lambda e: e[1] != rpc_port(i), binds))
    89             assert_equal(binds, set(expected_services))
    90 
    91+        self.stop_node(0)
    92+
    93+        # Test all combinations of duplicate bindings (each should fail)
    94+        error_msg = "Error: Duplicate binding configuration for address 127.0.0.1:11012. " \
    


    l0rinc commented at 5:40 pm on August 26, 2025:
    nit: This seems to be used a single time, we might as well inline it
  40. in test/functional/feature_bind_extra.py:110 in 28fb7f74c4
    105+            for j, opt2 in enumerate(bind_options):
    106+                if i <= j:  # Avoid testing reverse duplicates (A,B) and (B,A)
    107+                    self.nodes[0].assert_start_raises_init_error(
    108+                        [opt1, opt2],
    109+                        error_msg,
    110+                        match=ErrorMatch.FULL_TEXT)
    


    l0rinc commented at 5:42 pm on August 26, 2025:

    What’s the advantage of duplicating the exact error here, wouldn’t this suffice?

    0                        "Error: Duplicate binding configuration",
    1                        match=ErrorMatch.PARTIAL_REGEX)
    
  41. in test/functional/feature_bind_extra.py:14 in 28fb7f74c4
    10@@ -11,6 +11,7 @@
    11     addr_to_hex,
    12     get_bind_addrs,
    13 )
    14+from test_framework.test_node import ErrorMatch
    


    l0rinc commented at 5:45 pm on August 26, 2025:

    The doc at the top is slightly off now:

    0"""
    1Test starting bitcoind with -bind and/or -bind=...=onion, confirm that
    2it binds to the expected ports, and verify that duplicate or conflicting
    3-bind/-whitebind configurations are rejected with a descriptive error.
    4"""
    
  42. in test/functional/feature_bind_extra.py:106 in 28fb7f74c4
    101+        ]
    102+
    103+        # Test all combinations (including same option repeated)
    104+        for i, opt1 in enumerate(bind_options):
    105+            for j, opt2 in enumerate(bind_options):
    106+                if i <= j:  # Avoid testing reverse duplicates (A,B) and (B,A)
    


    l0rinc commented at 5:56 pm on August 26, 2025:

    we could make this manual iteration more pythony with something like:

    0from itertools import combinations_with_replacement
    1addr = f"127.0.0.1:11012"
    2for opt1, opt2 in combinations_with_replacement([f"-bind={addr}", f"-bind={addr}=onion", f"-whitebind=noban@{addr}"], 2):
    3    print(f"opt1 = {opt1}, opt2 = {opt2}") # replace with assert, I just used this to validate if the iteration is the same
    
  43. in src/init.cpp:1241 in 28fb7f74c4
    1232@@ -1233,6 +1233,40 @@ bool CheckHostPortOptions(const ArgsManager& args) {
    1233     return true;
    1234 }
    1235 
    1236+/**
    1237+ * @brief Checks for duplicate bindings across all binding configuration.
    1238+ *
    1239+ * @param connOptions Connection options containing the binding vectors to check
    1240+ * @return std::optional<CService> containing the first duplicate found, or std::nullopt if no duplicates
    1241+ */
    


    l0rinc commented at 6:00 pm on August 26, 2025:
    I personally consider these comments noisy - all of this is already evident from the code. If it’s not, I’d rather change the code. Seeing the unupdated comment below, we shouldn’t just add extra work for our future selves.

    vasild commented at 8:37 am on September 2, 2025:

    It wasn’t clear to me what the return value is before reading the comment, so this is useful. Sometimes comments are more on the side of redundant/noisy/useless, but the line between that and useful comments is blurred and personally I prefer to err on the side of redundancy.

    If there are no comments whatsoever, this sets a standard. Then if the function is extended with not-so-obvious parameters or return value changed, the developer doing it will likely not add a comment for the new parameter.

    Yes, comments need to be maintained to be in sync with the code. But note that the argument “Lets omit some comments because we will have to maintain them (more future work)” is missing a point - a code is read many more times than it is modified. So it is better have code that is “easier to read and ‘harder’ to modify” than “harder to read and easier to modify”.

  44. in src/init.cpp:2179 in 28fb7f74c4
    2175@@ -2142,6 +2176,12 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
    2176 
    2177     connOptions.m_i2p_accept_incoming = args.GetBoolArg("-i2pacceptincoming", DEFAULT_I2P_ACCEPT_INCOMING);
    2178 
    2179+    if (auto conflict = CheckBindingConflicts(connOptions)) {
    


    l0rinc commented at 6:25 pm on August 26, 2025:

    Is it expected that I’m seeing this twice?

    02025-08-26T18:23:45Z [error] Duplicate binding configuration for address 127.0.0.1:11012. Please check your -bind, -whitebind, and onion binding settings.
    12025-08-26T18:23:45Z tor: Thread interrupt
    22025-08-26T18:23:45Z torcontrol thread exit
    32025-08-26T18:23:45Z Shutdown in progress...
    4Error: Duplicate binding configuration for address 127.0.0.1:11012. Please check your -bind, -whitebind, and onion binding settings.
    

    And if we’re not actually stopping the execution, shouldn’t it rather be a warning?


    yuvicc commented at 4:33 am on August 27, 2025:
    I guess the first err msg is from logging while the second is from init.cpp. From the comment #33231 (comment), I can see that we would want to stop the execution.

    w0xlt commented at 6:46 pm on August 27, 2025:

    The execution will be stopped either way (in the master branch or in this PR). This PR simply prevents the node from attempting to connect twice to the same address and port, and instead terminates it with a clear message — rather than the confusing one currently shown in the master branch.

    I guess the first err msg is from logging while the second is from init.cpp

    Yes, that makes sense. I’ll check again.


    l0rinc commented at 6:49 pm on August 27, 2025:
    it just continues IBD on my side after the warning, it doesn’t seem to stop after the error

    w0xlt commented at 8:03 pm on August 27, 2025:

    If you are running in the foreground, InitError() prints the message to the console. In debug.log, only a single entry is recorded. This behavior applies to all InitError() calls. https://github.com/bitcoin/bitcoin/blob/6ca6f3b37b992591726bd13b494369bee3bd6468/src/node/interface_ui.cpp#L64

    it just continues IBD on my side after the warning, it doesn’t seem to stop after the error

    Could you please check again? InitError() returns false, which should stop execution. https://github.com/bitcoin/bitcoin/blob/6ca6f3b37b992591726bd13b494369bee3bd6468/src/bitcoind.cpp#L237

    Also, the feature_bind_extra test would fail if the node didn’t actually stop.


    l0rinc commented at 9:08 pm on August 27, 2025:

    I tried it again:

    0$ git log -1 | head
    1commit 28fb7f74c4ae66dcb0f918774d644ac3c393f25b
    2Author: woltx <94266259+w0xlt@users.noreply.github.com>
    3Date:   Mon Aug 25 20:22:33 2025 -0700
    4
    5    net: Prevent node from binding to the same CService
    

    and did:

    0$ rm -rfd build && \  
    1  cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release && \
    2  ninja -C build && \
    3  build/bin/bitcoind -bind=127.0.0.1:11012 -bind=127.0.0.1:11012
    

    which resulted in:

     0...
     12025-08-27T21:07:33Z initload thread start
     22025-08-27T21:07:33Z mapport thread start
     32025-08-27T21:07:33Z [error] Duplicate binding configuration for address 127.0.0.1:11012. Please check your -bind, -whitebind, and onion binding settings.
     4Error: Duplicate binding configuration for address 127.0.0.1:11012. Please check your -bind, -whitebind, and onion binding settings.
     52025-08-27T21:07:33Z torcontrol thread start
     62025-08-27T21:07:33Z tor: Thread interrupt
     72025-08-27T21:07:33Z Shutdown in progress...
     82025-08-27T21:07:33Z [net:warning] pcp: Could not send request: Broken pipe (32)
     92025-08-27T21:07:33Z mapport thread exit
    102025-08-27T21:07:33Z torcontrol thread exit
    112025-08-27T21:07:33Z Disabling signature validations at block [#561836](/bitcoin-bitcoin/561836/) (0000000000000000000e61f02e2aab67704eecb035e02460c78b7aa212bc66c2).
    122025-08-27T21:07:33Z UpdateTip: new best=0000000000000000000e61f02e2aab67704eecb035e02460c78b7aa212bc66c2 height=561836 version=0x20000000 log2_work=90.318686 tx=380354077 date='2019-02-06T16:47:27Z' progress=0.308572 cache=1.1MiB(7516txo)
    132025-08-27T21:07:33Z UpdateTip: new best=0000000000000000002eef0205433100c4e6129c04f52bf9df6c417d3643ffee height=561837 version=0x20400000 log2_work=90.318709 tx=380355289 date='2019-02-06T16:51:11Z' progress=0.308573 cache=2.2MiB(14213txo)
    142025-08-27T21:07:33Z UpdateTip: new best=000000000000000000090475088fdd32eb00e5940bd9870518f981a7306108ef height=561838 version=0x20000000 log2_work=90.318733 tx=380356192 date='2019-02-06T16:52:57Z' progress=0.308574 cache=3.2MiB(22237txo)
    152025-08-27T21:07:33Z UpdateTip: new best=00000000000000000006636f8b9fa5428b02dee71e4daa365198c017a872336b height=561839 version=0x3fffe000 log2_work=90.318756 tx=380359091 date='2019-02-06T17:03:45Z' progress=0.308576 cache=4.4MiB(30786txo)
    162025-08-27T21:07:33Z UpdateTip: new best=0000000000000000000e1694f8afb51528dd737a9cb8cdcb81bc1bda88a3e34e height=561840 version=0x20000000 log2_work=90.318779 tx=380360877 date='2019-02-06T17:05:51Z' progress=0.308578 cache=5.4MiB(37695txo)
    17...
    

    w0xlt commented at 9:30 pm on August 27, 2025:

    That’s strange. On my machine, it’s interrupted. I tried running it by pointing the -datadir to an empty folder to see if that was the reason, but it also stopped (shown below). Wouldn’t the feature_bind_extra test fail if the node didn’t stop?

    Are you using a configuration file? Could some settings be affecting this?

     02025-08-27T21:27:45Z Loaded best chain: hashBestChain=000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f height=0 date=2009-01-03T18:15:05Z progress=0.000000
     12025-08-27T21:27:45Z init message: Verifying blocks
     22025-08-27T21:27:45Z Block index and chainstate loaded
     32025-08-27T21:27:45Z Setting NODE_NETWORK on non-prune mode
     42025-08-27T21:27:45Z block tree size = 1
     52025-08-27T21:27:45Z nBestHeight = 0
     62025-08-27T21:27:45Z initload thread start
     72025-08-27T21:27:45Z mapport thread start
     82025-08-27T21:27:45Z torcontrol thread start
     92025-08-27T21:27:45Z Loading 0 mempool transactions from file...
    102025-08-27T21:27:45Z [error] Duplicate binding configuration for address 0.0.0.0:11012. Please check your -bind, -whitebind, and onion binding settings.
    11Error: Duplicate binding configuration for address 0.0.0.0:11012. Please check your -bind, -whitebind, and onion binding settings.
    12
    132025-08-27T21:27:45Z Imported mempool transactions from file: 0 succeeded, 0 failed, 0 expired, 0 already there, 0 waiting for initial broadcast
    142025-08-27T21:27:45Z initload thread exit
    152025-08-27T21:27:45Z tor: Thread interrupt
    162025-08-27T21:27:45Z Shutdown in progress...
    172025-08-27T21:27:45Z torcontrol thread exit
    182025-08-27T21:27:45Z [net:warning] pcp: Could not receive response: Connection refused (61)
    192025-08-27T21:27:45Z mapport thread exit
    202025-08-27T21:27:45Z scheduler thread exit
    212025-08-27T21:27:45Z Writing 0 mempool transactions to file...
    222025-08-27T21:27:45Z Writing 0 unbroadcast transactions to file.
    232025-08-27T21:27:45Z Dumped mempool: 0.000s to copy, 0.004s to dump, 27 bytes dumped to file
    242025-08-27T21:27:45Z Flushed fee estimates to fee_estimates.dat.
    252025-08-27T21:27:45Z Shutdown done
    

    l0rinc commented at 9:35 pm on August 27, 2025:
    feature_bind_extra is skipped on a Mac

    w0xlt commented at 9:47 pm on August 27, 2025:
    I’m also testing on a Mac right now. On both Linux and macOS ARM (Sequoia 15.5), the node stops as expected. It’s curious that on your machine InitError() prints and logs the error but doesn’t actually stop the node.

    l0rinc commented at 11:11 pm on August 27, 2025:

    Tried the same on Linux:

     0# build/bin/bitcoind -datadir=/mnt/my_storage/BitcoinData -bind=127.0.0.1:11012 -bind=127.0.0.1:11012                  
     12025-08-27T23:09:10Z Bitcoin Core version v29.99.0-28fb7f74c4ae (release build)                                                                                                  
     22025-08-27T23:09:10Z parameter interaction: -bind set -> setting -listen=1                                                                                                       
     32025-08-27T23:09:10Z Using the 'sse4(1way),sse41(4way),avx2(8way)' SHA256 implementation                                                                                         
     42025-08-27T23:09:10Z Using RdSeed as an additional entropy source                                                                                                                
     52025-08-27T23:09:10Z Using RdRand as an additional entropy source                                                                                                                
     62025-08-27T23:09:10Z Default data directory /root/.bitcoin                                                                                                                       
     72025-08-27T23:09:10Z Using data directory /mnt/my_storage/BitcoinData                                                                                                            
     82025-08-27T23:09:10Z Config file: /mnt/my_storage/BitcoinData/bitcoin.conf (not found, skipping)                                                                                 
     92025-08-27T23:09:10Z Command-line arg: bind="127.0.0.1:11012"                                                                                                                    
    102025-08-27T23:09:10Z Command-line arg: bind="127.0.0.1:11012"                                                                                                                    
    112025-08-27T23:09:10Z Command-line arg: datadir="/mnt/my_storage/BitcoinData"                                                                                                     
    122025-08-27T23:09:10Z Using at most 125 automatic connections (1024 file descriptors available)                                                                                   
    132025-08-27T23:09:10Z scheduler thread start                                                                                                                                      
    142025-08-27T23:09:10Z Binding RPC on address ::1 port 8332                                                                                                                        
    152025-08-27T23:09:10Z Binding RPC on address 127.0.0.1 port 8332                                                                                                                  
    162025-08-27T23:09:10Z Generated RPC authentication cookie /mnt/my_storage/BitcoinData/.cookie                                                                                     
    172025-08-27T23:09:10Z Permissions used for cookie: rw-------                                                                                                                      
    182025-08-27T23:09:10Z Using random cookie authentication.                                                                                                                         
    192025-08-27T23:09:10Z Starting HTTP server with 16 worker threads                                                                                                                 
    202025-08-27T23:09:10Z Using wallet directory /mnt/my_storage/BitcoinData                                                                                                          
    212025-08-27T23:09:10Z init message: Verifying wallet(s)222025-08-27T23:09:10Z Using /16 prefix for IP bucketing                                                                                                                           
    232025-08-27T23:09:10Z init message: Loading P2P addresses…                                                                                                                        
    242025-08-27T23:09:10Z Loaded 14800 addresses from peers.dat  43ms                                                                                                                 
    252025-08-27T23:09:10Z init message: Loading banlist…                                                                                                                              
    262025-08-27T23:09:10Z SetNetworkActive: true                                                                                                                                      
    272025-08-27T23:09:10Z Fee estimation file /mnt/my_storage/BitcoinData/fee_estimates.dat too old (age=88 > 60 hours) and will not be used to avoid serving stale estimates.        
    282025-08-27T23:09:10Z Cache configuration:                                                                                                                                        
    292025-08-27T23:09:10Z * Using 2.0 MiB for block index database                                                                                                                    
    302025-08-27T23:09:10Z * Using 8.0 MiB for chain state database                                                                                                                    
    312025-08-27T23:09:10Z * Using 440.0 MiB for in-memory UTXO set (plus up to 286.1 MiB of unused mempool space)
    322025-08-27T23:09:10Z Script verification uses 15 additional threads
    332025-08-27T23:09:10Z Using obfuscation key for blocksdir *.dat files (/mnt/my_storage/BitcoinData/blocks): 'b5f4545321693e43'
    342025-08-27T23:09:10Z Opening LevelDB in /mnt/my_storage/BitcoinData/blocks/index
    352025-08-27T23:09:10Z Opened LevelDB successfully
    362025-08-27T23:09:10Z Using obfuscation key for /mnt/my_storage/BitcoinData/blocks/index: 0000000000000000
    372025-08-27T23:09:10Z Using 16 MiB out of 16 MiB requested for signature cache, able to store 524288 elements
    382025-08-27T23:09:10Z Using 16 MiB out of 16 MiB requested for script execution cache, able to store 524288 elements
    392025-08-27T23:09:10Z init message: Loading block index…
    402025-08-27T23:09:10Z Assuming ancestors of block 00000000000000000001b658dd1120e82e66d2790811f89ede9742ada3ed6d77 have valid signatures.
    412025-08-27T23:09:10Z Setting nMinimumChainWork=0000000000000000000000000000000000000000b1f3b93b65b16d035a82be84
    422025-08-27T23:09:14Z Loading block index db: last block file = 5077
    432025-08-27T23:09:14Z Loading block index db: last block file info: CBlockFileInfo(blocks=13, size=22987302, heights=908910...909336, time=2025-08-06...2025-08-09)
    442025-08-27T23:09:14Z Checking all blk files are present...
    452025-08-27T23:09:15Z Initializing chainstate Chainstate [ibd] @ height -1 (null)
    462025-08-27T23:09:15Z Opening LevelDB in /mnt/my_storage/BitcoinData/chainstate
    472025-08-27T23:09:15Z Opened LevelDB successfully
    482025-08-27T23:09:15Z Using obfuscation key for /mnt/my_storage/BitcoinData/chainstate: 884f5bfce6d4861a
    492025-08-27T23:09:15Z Loaded best chain: hashBestChain=0000000000000000000203390dfcf92da736eb138c76613acdcd39e7527adcff height=909090 date=2025-08-07T23:19:12Z progress=0.993520
    502025-08-27T23:09:15Z init message: Verifying blocks…
    512025-08-27T23:09:15Z Verifying last 6 blocks at level 3
    522025-08-27T23:09:15Z Verification progress: 0%
    532025-08-27T23:09:16Z Verification progress: 16%
    542025-08-27T23:09:16Z Verification progress: 33%
    552025-08-27T23:09:16Z Verification progress: 50%
    562025-08-27T23:09:16Z Verification progress: 66%
    572025-08-27T23:09:17Z Verification progress: 83%
    582025-08-27T23:09:17Z Verification progress: 99%
    592025-08-27T23:09:17Z Verification: No coin database inconsistencies in last 6 blocks (18277 transactions)
    602025-08-27T23:09:17Z Block index and chainstate loaded
    612025-08-27T23:09:17Z Setting NODE_NETWORK on non-prune mode
    622025-08-27T23:09:17Z block tree size = 911498
    632025-08-27T23:09:17Z nBestHeight = 909090
    642025-08-27T23:09:17Z initload thread start
    652025-08-27T23:09:17Z [error] Duplicate binding configuration for address 127.0.0.1:11012. Please check your -bind, -whitebind, and onion binding settings.
    66Error: Duplicate binding configuration for address 127.0.0.1:11012. Please check your -bind, -whitebind, and onion binding settings.
    672025-08-27T23:09:17Z mapport thread start
    682025-08-27T23:09:17Z tor: Thread interrupt
    692025-08-27T23:09:17Z Shutdown in progress... 
    702025-08-27T23:09:17Z torcontrol thread start 
    712025-08-27T23:09:17Z torcontrol thread exit
    722025-08-27T23:09:18Z UpdateTip: new best=0000000000000000000100082a0f69bd12276deb5598be2886b535840645d4cf height=909091 version=0x2007e000 log2_work=95.757925 tx=1223875773 date='2025-08-07T23:25:26Z' progress=0.993522 cache=1.7MiB(11624txo)
    732025-08-27T23:09:18Z UpdateTip: new best=00000000000000000001f67dbd5a313d33ac2c04d68a6b457207079ec7ec6bff height=909092 version=0x2e000000 log2_work=95.757936 tx=1223879231 date='2025-08-07T23:39:06Z' progress=0.993525 cache=3.6MiB(26194txo)
    742025-08-27T23:09:18Z UpdateTip: new best=0000000000000000000092a60d20a555cfdd75662eb5bd66d815ea99c8229442 height=909093 version=0x20a00000 log2_work=95.757948 tx=1223880425 date
    75='2025-08-07T23:44:04Z' progress=0.993526 cache=4.3MiB(30897txo)
    762025-08-27T23:09:19Z UpdateTip: new best=000000000000000000008016fc91ffdafccef70ba29f86e3b811a66436738fe1 height=909094 version=0x25b16000 log2_work=95.757960 tx=1223883603 date
    77='2025-08-08T00:06:29Z' progress=0.993531 cache=6.2MiB(44613txo)
    782025-08-27T23:09:19Z UpdateTip: new best=0000000000000000000013742182139d9d45ede7df5417d27f6b753408cd2d1d height=909095 version=0x24b68000 log2_work=95.757972 tx=1223886554 date
    79='2025-08-08T00:15:50Z' progress=0.993533 cache=7.4MiB(55171txo)
    802025-08-27T23:09:19Z UpdateTip: new best=000000000000000000013af8b554ea59b8a81877eaf44ffec6e3ecd2af2c1a9a height=909096 version=0x20a00000 log2_work=95.757984 tx=1223888127 date
    81='2025-08-08T00:17:27Z' progress=0.993534 cache=8.2MiB(59453txo)
    

    w0xlt commented at 11:30 pm on August 27, 2025:

    If you try another condition that also triggers InitError(), for example:

    0build/bin/bitcoind -i2psam=invalid_addr
    

    what happens on your side?


    l0rinc commented at 11:51 pm on August 27, 2025:

    it seems to stop for empty blockchain, but it will just continue if something already exists

     0# build/bin/bitcoind -datadir=/mnt/my_storage/BitcoinData -i2psam=invalid_addr
     1...
     2025-08-27T23:49:17Z Discover: 2a01:4f9:2a:a61::2
     32025-08-27T23:49:17Z [error] Invalid -i2psam address or hostname: 'invalid_addr'
     4Error: Invalid -i2psam address or hostname: 'invalid_addr'
     52025-08-27T23:49:17Z tor: Thread interrupt
     62025-08-27T23:49:17Z Shutdown in progress...
     72025-08-27T23:49:17Z torcontrol thread exit
     82025-08-27T23:49:17Z Loaded 0 blocks from external file in 165ms
     92025-08-27T23:49:17Z Reindexing block file blk00001.dat...
    10...
    

    vs

     0# build/bin/bitcoind -i2psam=invalid_addr                                                                                                                                                                                               
     12025-08-27T23:48:51Z Discover: 2a01:4f9:2a:a61::2                                                                                                                                
     22025-08-27T23:48:51Z [error] Invalid -i2psam address or hostname: 'invalid_addr'                                                                                                 
     3Error: Invalid -i2psam address or hostname: 'invalid_addr'                                                                                                                       
     42025-08-27T23:48:51Z tor: Thread interrupt                                                                                                                                       
     52025-08-27T23:48:51Z Shutdown in progress...                                                                                                                                     
     62025-08-27T23:48:51Z torcontrol thread exit                                                                                                                                      
     72025-08-27T23:48:57Z mapport thread exit                                                                                                                                     
     82025-08-27T23:48:57Z scheduler thread exit                                                                                                                                       
     92025-08-27T23:48:57Z Writing 0 mempool transactions to file...                                                                                                                   
    102025-08-27T23:48:57Z Writing 0 unbroadcast transactions to file.                                                                                                                 
    112025-08-27T23:48:57Z Dumped mempool: 0.000s to copy, 0.051s to dump, 27 bytes dumped to file                                                                                     
    122025-08-27T23:48:57Z Flushed fee estimates to fee_estimates.dat.                                                                                                                 
    132025-08-27T23:48:57Z Shutdown done                       
    

    cedwies commented at 4:36 pm on August 28, 2025:
    Was not yet able to reproduce the continuation. But just to clarify, @l0rinc : are we talking about a late-fail with “afterglow” or does the node truly keep running?

    l0rinc commented at 6:05 pm on August 28, 2025:
    I started it and accidentally left it in the background and it fetched 100k blocks - if it’s an “afterglow”, it’s a resilient one. And I did reproduce it on 3 separate servers, seems like a real bug - though likely not introduced in this PR, will open an issue for it or investigate it myself later this week.

    cedwies commented at 9:04 am on August 29, 2025:
    Curious to see what you find. I’ll try to follow if you open an issue/PR on it.

    l0rinc commented at 9:55 pm on September 1, 2025:
    Opened #33276, please resolve this comment
  45. l0rinc commented at 6:28 pm on August 26, 2025: contributor

    I like this simpler version more, left a few more comments

    Now that we’re not deduplicating anymore, the title could be adjusted:

    net: reject duplicate bind addresses across -bind/-whitebind/onion

    The PR desciption also hints that we still do:

    This PR proposes that repeated -bind options have no effect.

  46. naiyoma commented at 2:22 pm on August 28, 2025: contributor

    Concept ACK

    Tested with these 3 options:

    • ./build/bin/bitcoind -bind=0.0.0.0=onion -bind=0.0.0.0=onion
    • ./build/bin/bitcoind -bind=0.0.0.0 -bind=0.0.0.0
    • ./build/bin/bitcoind -whitebind=relay@127.0.0.1:8333 -whitebind=mempool@127.0.0.1:8333

    For duplicate onion binds, there’s an existing warning that appears before the error: 2025-08-28T13:58:57Z [warning] More than one onion bind address is provided. IMO this warning is sufficient, but if you keep the error approach, consider removing the warning.

  47. w0xlt commented at 7:54 pm on August 28, 2025: contributor

    IMO this warning is sufficient, but if you keep the error approach, consider removing the warning.

    Thanks for the review @naiyoma . The error will occur regardless, whether in master or in this PR. This PR simply provides a clearer and more useful message.

    Regarding removing the warning, I think the bind validation could be moved to occur before the onion validation. Good catch.

  48. in src/init.cpp:1237 in 28fb7f74c4
    1232@@ -1233,6 +1233,40 @@ bool CheckHostPortOptions(const ArgsManager& args) {
    1233     return true;
    1234 }
    1235 
    1236+/**
    1237+ * @brief Checks for duplicate bindings across all binding configuration.
    


    maflcko commented at 9:17 am on August 29, 2025:
    configuration -> configurations [“across all binding configuration” should be pluralized to “configurations” for grammatical correctness]
    
  49. in src/init.cpp:1239 in 28fb7f74c4
    1232@@ -1233,6 +1233,40 @@ bool CheckHostPortOptions(const ArgsManager& args) {
    1233     return true;
    1234 }
    1235 
    1236+/**
    1237+ * @brief Checks for duplicate bindings across all binding configuration.
    1238+ *
    1239+ * @param connOptions Connection options containing the binding vectors to check
    


    vasild commented at 8:22 am on September 2, 2025:
    0 * [@param](/bitcoin-bitcoin/contributor/param/)[in] connOptions Connection options containing the binding vectors to check
    
  50. in src/init.cpp:1242 in 28fb7f74c4
    1237+ * @brief Checks for duplicate bindings across all binding configuration.
    1238+ *
    1239+ * @param connOptions Connection options containing the binding vectors to check
    1240+ * @return std::optional<CService> containing the first duplicate found, or std::nullopt if no duplicates
    1241+ */
    1242+std::optional<CService> CheckBindingConflicts(const CConnman::Options& connOptions)
    


    vasild commented at 8:46 am on September 2, 2025:

    Since the new function is used only in this file:

    0static std::optional<CService> CheckBindingConflicts(const CConnman::Options& connOptions)
    
  51. in src/init.cpp:2181 in 28fb7f74c4
    2175@@ -2142,6 +2176,12 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
    2176 
    2177     connOptions.m_i2p_accept_incoming = args.GetBoolArg("-i2pacceptincoming", DEFAULT_I2P_ACCEPT_INCOMING);
    2178 
    2179+    if (auto conflict = CheckBindingConflicts(connOptions)) {
    2180+        return InitError(Untranslated(strprintf("Duplicate binding configuration for address %s. "
    2181+            "Please check your -bind, -whitebind, and onion binding settings.",
    


    vasild commented at 8:52 am on September 2, 2025:

    Here and also in the commit message, this lists 3 categories: -bind, -whitebind, onion. There is an -onion config option which is not related to binding. This could be confusing. The intention here is to mean “onion” -> “a subset of -bind if suffixed with =onion”. That is, just mentioning -bind also covers -bind=...=onion. So maybe change this to:

    Please check your -bind and -whitebind settings or to Please check your -bind, -bind=...=onion and -whitebind settings.

  52. vasild approved
  53. vasild commented at 9:05 am on September 2, 2025: contributor

    ACK 28fb7f74c4ae66dcb0f918774d644ac3c393f25b

    Some non-blocker comments below, would be happy to re-review if addressed.

  54. DrahtBot requested review from cedwies on Sep 2, 2025
  55. DrahtBot requested review from yuvicc on Sep 2, 2025
  56. DrahtBot requested review from jonatack on Sep 2, 2025
  57. DrahtBot requested review from l0rinc on Sep 2, 2025
  58. DrahtBot requested review from naiyoma on Sep 2, 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-09-02 12:13 UTC

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