init: Handle dropped UPnP support more gracefully #31916

pull laanwj wants to merge 1 commits into bitcoin:master from laanwj:2025-02-upnp-setting-upgrade changing 1 files +25 −6
  1. laanwj commented at 4:08 pm on February 20, 2025: member

    Closes bitcoin-core/gui#843.

    In that issue it was brought up that users likely don’t care what kind of port forwarding is used, and that the setting is opportunistic anyway, so instead of showing an extensive warning, we can simply “upgrade” from UPNP to NAT-PMP+PCP.

    • Change the logic for removed runtime setting -upnp to set -natpmp instead, and log a message.

    • Also remove any lingering upnp from settings.json and replace it with natpmp, when it makes sense (this is important so that the UI shows the right values in the settings):

    0{
    1    "upnp": true
    2}
    

    becomes

    0{
    1    "natpmp": true
    2}
    

    and

    0{
    1    "upnp": false
    2}
    

    becomes

    0{
    1}
    

    If this solution is acceptable i will add a functional test.

  2. init: Handle dropped UPnP support more gracefully
    Closes bitcoin-core/gui#843.
    
    In that issue it was brought up that users likely don't care what kind
    of port forwarding is used, and the setting is oppertunistic anyway, so
    instead of showing an extensive warning, we can simply "upgrade" from
    UPNP to NAT-PMP+PCP.
    
    - Change the logic for removed runtime setting `-upnp` to set `-natpmp`
      instead, and only log a message.
    
    - Also replace any lingering `upnp` in `settings.json` with `natpmp`,
      when it makes sense:
    
    ```json
    {
        "upnp": true
    }
    ```
    becomes
    ```json
    {
        "natpmp": true
    }
    ```
    
    ```json
    {
        "upnp": false
    }
    ```
    becomes
    ```json
    {
    }
    ```
    
    If this solution is acceptable i will add a functional test.
    bd4679a608
  3. DrahtBot commented at 4:08 pm on February 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/31916.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    Concept ACK davidgumberg

    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:

    • #31649 (consensus: Remove checkpoints (take 2) by marcofleon)
    • #31260 (scripted-diff: Type-safe settings retrieval by ryanofsky)

    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.

  4. in src/init.cpp:799 in bd4679a608
    792@@ -793,6 +793,31 @@ void InitParameterInteraction(ArgsManager& args)
    793             LogInfo("parameter interaction: -onlynet excludes IPv4 and IPv6 -> setting -dnsseed=0\n");
    794         }
    795     }
    796+
    797+    // We drop UPnP support but kept the arg as hidden for now to display a friendlier error to user who have the
    798+    // option in their config, and upgrade the setting to -natpmp. TODO: remove for version 30.0.
    799+    if (args.IsArgSet("-upnp")) {
    


    maflcko commented at 4:19 pm on February 20, 2025:

    Is this the right check to turn on natpmp? IIUC IsArgSet will return true even when the value is negated (-noupnp)?

    A fix could be to call args.SoftSetBoolArg("-natpmp", args.GetBoolArg("-upnp", false)) below?


    laanwj commented at 4:31 pm on February 20, 2025:
    Good catch, this is the wrong check. For the settings.json case i did consider the “is set” and “is set to true” case differently but consider them the same here.

    maflcko commented at 4:39 pm on February 20, 2025:

    I haven’t tried to compile, but the modern way of my suggestion would be:

    0    if (const auto arg{args.GetBoolArg("-upnp")}) {
    1        bool overridden = args.SoftSetBoolArg("-natpmp", *arg);
    

    laanwj commented at 8:17 am on February 21, 2025:

    Thank you!

    -upnp=0 setting the default for -natpmp to false sounds like reasonable behavior.

  5. hebasto added this to the milestone 29.0 on Feb 20, 2025
  6. achow101 requested review from davidgumberg on Feb 20, 2025
  7. achow101 requested review from jarolrod on Feb 20, 2025
  8. in src/init.cpp:808 in bd4679a608
    803+    }
    804+
    805+    // If dynamic settings are enabled, also replace it in settings.json.
    806+    fs::path path;
    807+    if (args.GetSettingsPath(&path, /* temp= */ false)) {
    808+        common::SettingsValue value = args.GetPersistentSetting("upnp");
    


    davidgumberg commented at 9:33 pm on February 20, 2025:
    Slightly surprisingly to me, GetPersistentSetting() gets values from bitcoin.conf, so if you set upnp=1 in bitcoin.conf, the branch below is executed, even if there’s no upnp setting in settings.json. Maybe this is intended, but in that case if (value.isTrue) below fails, so natpmp does not get enabled, despite the warning that gets logged.

    laanwj commented at 8:04 am on February 21, 2025:
    No, that’s not intended. i’ll look into a way to query the json only.

    Sjors commented at 9:00 am on February 21, 2025:
    Why would value.isTrue fail? I suspect what will happen (still have to test) is that you’ll keep upnp=1 in the config and also have natpmp: 1 in settings. If the former is ignored and the latter used, that should be fine.

    Sjors commented at 9:22 am on February 21, 2025:

    value.isTrue is indeed false when I set upnp=1 in bitcoin.conf, very strange.

    Another strange thing is that this code appears to be run twice, so I’m seeing the log entries twice.


    Sjors commented at 9:36 am on February 21, 2025:
    Aha, !value.IsBool() for upnp=1

    laanwj commented at 9:49 am on February 21, 2025:
    The real problem here is that GetPersistentSetting also looks into the (non-mutable) configuration file. i’ll add a flag to skip that and this should work as intended.

    laanwj commented at 10:18 am on February 21, 2025:
    Alternatively it could directly check the value and if necessary manipulate settings.rw_settings, inside the LockSettings lock. This also keeps it atomic (not that we care here). But it also gives the clearest code (super clear that this should only affect the dynamic settings) and doesn’t need API changes.
  9. davidgumberg commented at 9:33 pm on February 20, 2025: contributor

    Concept ACK

    I agree with the motivation that most users don’t “care about what method of automatic port forwarding method is used.” (https://github.com/bitcoin-core/gui/issues/843#issuecomment-2671668235)

    I think it is a little bit unfortunate that there might be some users of the GUI who don’t check the release notes or debug.log whose networks did support UPnP but don’t support NAT-PMP or PCP will never be warned that their port forwarding setup is broken, but I think the negatives of exposing UPnP deprecation to GUI users outweighs the benefits.1

    Sanity tested by enabling UPnP in bitcoin-qt v28.1.0 on a fresh datadir created the following settings.json

    0{
    1    "_warning_": "This file is automatically generated and updated by Bitcoin Core. Please do not edit this file while the node is running, as any changes might be ignored or overwritten.",
    2    "upnp": true
    3}
    

    Launching bitcoin-qt built with https://github.com/bitcoin/bitcoin/pull/31916/commits/bd4679a608b2cbc206fa60a180b0fc4ce8a39ec4 modified settings.json as described:

    0{
    1    "_warning_": "This file is automatically generated and updated by Bitcoin Core. Please do not edit this file while the node is running, as any changes might be ignored or overwritten.",
    2    "natpmp": true
    3}
    

    and the setting is displayed correctly in the settings tab.

    Screenshot from 2025-02-20 12-24-39


    1. Maybe a better and more general solution to that class of problem that is outside the scope of this PR and the issue it resolves is to provide some kind of indication to users about whether or not their node is reachable, maybe using a heuristic like node uptime > 1 hour, outbound slots filled, 0 inbound connections. ↩︎

  10. laanwj commented at 8:00 am on February 21, 2025: member

    I think it is a little bit unfortunate that there might be some users of the GUI who don’t check the release notes or debug.log whose networks did support UPnP but don’t support NAT-PMP or PCP will never be warned that their port forwarding setup is broken

    Maybe a better and more general solution to that class of problem that is outside the scope of this PR and the issue it resolves is to provide some kind of indication to users about whether or not their node is reachable, maybe using a heuristic like node uptime > 1 hour, outbound slots filled, 0 inbound connections.

    Exactly. The point @ryanofsky made in bitcoin-core/gui#843 is that there is, overall, no feedback wheher port forwarding is working. It could be broken in many other ways without warning (that’s what i meant with “oppertunistic”). If we had an icon in the GUI that showed the current port forwarding status (or as you say, whether there are incoming connections), this would be solved too. But that’s something for a future PR.

  11. maflcko commented at 8:14 am on February 21, 2025: member

    If we had an icon in the GUI that showed the current port forwarding status (or as you say, whether there are incoming connections), this would be solved too. But that’s something for a future PR.

    IIRC I designed the “connections” icon so that it shows 3 “arms” when there are 8 connections, thus likely no incoming ones. Though, this probably broke with block-relay-only connections and it now shows always 4 “arms”. One could address that, to have that approximation again, but as you say it would be a separate PR (I’d be happy to review).

  12. Sjors commented at 8:41 am on February 21, 2025: member
    I remember the arms feature being very useful for exactly that reason, but haven’t tried recently.
  13. in src/init.cpp:798 in bd4679a608
    792@@ -793,6 +793,31 @@ void InitParameterInteraction(ArgsManager& args)
    793             LogInfo("parameter interaction: -onlynet excludes IPv4 and IPv6 -> setting -dnsseed=0\n");
    794         }
    795     }
    796+
    797+    // We drop UPnP support but kept the arg as hidden for now to display a friendlier error to user who have the
    798+    // option in their config, and upgrade the setting to -natpmp. TODO: remove for version 30.0.
    


    Sjors commented at 8:51 am on February 21, 2025:
    We might want to wait longer, because people can upgrade many years later (e.g. because they suddenly remember they have a wallet). Maybe just drop the TODO.

    laanwj commented at 9:43 am on February 21, 2025:

    Probably not a good place to have this discussion.

    But i also don’t think TODOs in comments make much sense in general, planning should happen on github and not in the code repository, so will remove it.


    maflcko commented at 11:55 am on February 21, 2025:
    If the goal is to enable natpmp by default, then the TODO: remove for version 30.0. can be replaced by This can be removed when the default value for natpmp is true. ?
  14. in src/init.cpp:810 in bd4679a608
    805+    // If dynamic settings are enabled, also replace it in settings.json.
    806+    fs::path path;
    807+    if (args.GetSettingsPath(&path, /* temp= */ false)) {
    808+        common::SettingsValue value = args.GetPersistentSetting("upnp");
    809+        if (!value.isNull()) {
    810+            LogWarning("Option '-upnp' has a value in settings.json, replacing it with `-natpmp`");
    


    Sjors commented at 9:10 am on February 21, 2025:

    in bitcoin.conf or settings.json, replacing it with -natpmp in settings.json

    When it’s in bitcoin.conf the warning will get repeated every startup, which seems fine.


    laanwj commented at 9:40 am on February 21, 2025:
    Yup. That’s intentional. The first warning can happen as many times as needed, the json manipulation should ideally only happen once. Oh, i misread. No this one shouldn’t be happening every time.
  15. Sjors commented at 9:52 am on February 21, 2025: member

    The following works correctly for me on macOS when either setting upnp=1 in bitcoin.conf or "upnp": true, in settings.json.

     0diff --git a/src/init.cpp b/src/init.cpp
     1index ca708085d8..5ecbd6bc20 100644
     2--- a/src/init.cpp
     3+++ b/src/init.cpp
     4@@ -807,10 +807,10 @@ void InitParameterInteraction(ArgsManager& args)
     5     if (args.GetSettingsPath(&path, /* temp= */ false)) {
     6         common::SettingsValue value = args.GetPersistentSetting("upnp");
     7         if (!value.isNull()) {
     8-            LogWarning("Option '-upnp' has a value in settings.json, replacing it with `-natpmp`");
     9+            LogWarning("Option '-upnp' has a value in bitcoin.conf or settings.json, replacing it with '-natpmp' in settings.json");
    10             args.LockSettings([&](common::Settings& settings) {
    11                 settings.rw_settings.erase("upnp");
    12-                if (value.isTrue()) {
    13+                if ((value.isBool() && value.isTrue()) || (value.isStr() && value.get_str() == "1")) {
    14                     // Only set the natpmp setting if upnp was set.
    15                     settings.rw_settings["natpmp"] = UniValue(true);
    16                 }
    

    We can’t edit a bitcoin.conf file, so I don’t think there’s more we can do?

    Also when using noupnp=1 in bitcoin.conf this emits a log, but leaves natpmp off.

  16. laanwj commented at 9:59 am on February 21, 2025: member
    i’ll stop repeating it now, but that second if() for updating the JSON is not supposed to trigger at all if the value is in bitcoin.conf only and not in the json.
  17. Sjors commented at 11:21 am on February 21, 2025: member

    In #31916 (review)

    The real problem here is that GetPersistentSetting also looks into the (non-mutable) configuration file. i’ll add a flag to skip that and this should work as intended.

    I think that’s a useful flag, if only so we can distinguish the two cases.

    Though I’m confused about what you intend to happen for users who have upnp=1 in bitcoin.conf.

  18. laanwj commented at 11:47 am on February 21, 2025: member

    Though I’m confused about what you intend to happen for users who have upnp=1 in bitcoin.conf.

    The intent is to interpret -upnp=1 as -natpmp=1 and log a warning message. Both on the command line and in bitcoin.conf. Pretty much like the other parameter interaction. This is what the first if(args.IsArgSet("-upnp")) {...} does, or is supposed to do.

  19. Sjors commented at 12:44 pm on February 21, 2025: member

    That makes sense.

    I re-tested the bitcoin.conf behavior by disabling the GetPersistentSetting bit. It prints the right log message and enables PCP. However in the GUI options dialog the check box is unchecked. And there’s nothing in “options overriden by the command line”. So this might be confusing, although it works.

    In contrast, when I set natpmp=1 in bitcoin.conf the checkbox is marked.

  20. laanwj commented at 12:59 pm on February 21, 2025: member

    I re-tested the bitcoin.conf behavior by disabling the GetPersistentSetting bit. It prints the right log message and enables PCP. However in the GUI options dialog the check box is unchecked. And there’s nothing in “options overriden by the command line”. So this might be confusing, although it works.

    Yes that seems wrong, i’ll see if i find a way to get the checkbox correct without spurious “write-through of bitcoin.conf settings to settings.json” behavior.

  21. in src/init.cpp:820 in bd4679a608
    815+                    settings.rw_settings["natpmp"] = UniValue(true);
    816+                }
    817+            });
    818+            args.WriteSettingsFile();
    819+        }
    820+    }
    


    ryanofsky commented at 3:56 pm on February 21, 2025:

    In commit “init: Handle dropped UPnP support more gracefully” (bd4679a608b2cbc206fa60a180b0fc4ce8a39ec4)

    I think I might not understanding what this code is trying to do exactly, but it seems overcomplicated and maybe a little off. Like the check for GetSettingPath seems unnecessary, the LogWarning seems vague, the GetPersistentSetting calls not really checking for the right thing as mentioned in an earlier comment. Also the check for isTrue seems overly narrow and the UniValue(true) cast should be unnecessary. I would suggest following implementation:

     0    // If settings.json contains a "upnp" option, migrate it to use "natpmp" instead
     1    bool settings_changed{false}; // Whether settings.json file needs to be rewritten
     2    args.LockSettings([&](common::Settings& settings) {
     3        if (auto* upnp{FindKey(settings.rw_settings, "upnp")}) {
     4            if (!FindKey(settings.rw_settings, "natpmp")) {
     5                LogWarning(R"(Adding "natpmp": %s to settings.json to replace obsolete "upnp" setting)", upnp->write());
     6                settings.rw_settings["natpmp"] = *upnp;
     7            }
     8            LogWarning(R"(Removing obsolete "upnp" setting from settings.json)");
     9            settings.rw_settings.erase("upnp");
    10            settings_changed = true;
    11        }
    12    });
    13    if (settings_changed) args.WriteSettingsFile();
    

    with using common::FindKey at top of the file. This seems like the most simple and direct implementation and I think comments and log messages should be a little better too.


    laanwj commented at 4:21 pm on February 21, 2025:

    Like the check for GetSettingPath seems unnecessar

    WriteSettingsFile will raise an exception if that check fails, it seems to make a big point of “dynamic settings being enabled”.


    ryanofsky commented at 4:28 pm on February 21, 2025:

    WriteSettingsFile will raise an exception if that check fails, it seems to make a big point of “dynamic settings being enabled”.

    I think that is ok at least with code above because settings_changed will be false in that case.


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

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