Repository: bitcoin/bitcoin
File: src/init.cpp (C++)

Code before:

        if (args.IsArgSet("-dnsseed") && args.GetBoolArg("-dnsseed", DEFAULT_DNSSEED) && args.IsArgSet("-proxy")) {
            LogInfo("-dnsseed is ignored when -connect is used and -proxy is specified");
        }
    }

    const std::string& i2psam_arg = args.GetArg("-i2psam", "");
    if (!i2psam_arg.empty()) {
        const std::optional<CService> addr{Lookup(i2psam_arg, 7656, fNameLookup)};
        if (!addr.has_value() || !addr->IsValid()) {
            return InitError(strprintf(_("Invalid -i2psam address or hostname: '%s'"), i2psam_arg));
        }
        SetProxy(NET_I2P, Proxy{addr.value()});
    } else {
        if (!onlynets.empty() && g_reachable_nets.Contains(NET_I2P)) {
            return InitError(
                _("Outbound connections restricted to i2p (-onlynet=i2p) but "
                  "-i2psam is not provided"));
        }
        g_reachable_nets.Remove(NET_I2P);
    }

    connOptions.m_i2p_accept_incoming = args.GetBoolArg("-i2pacceptincoming", DEFAULT_I2P_ACCEPT_INCOMING);

    if (auto conflict = CheckBindingConflicts(connOptions)) {
        return InitError(Untranslated(strprintf("Duplicate binding configuration for address %s. "
            "Please check your -bind, -whitebind, and onion binding settings.",
            conflict->ToStringAddrPort())));
    }

    if (!node.connman->Start(scheduler, connOptions)) {
        return false;
    }

    // ********************************************************* Step 13: finished

    // At this point, the RPC is "started", but still in warmup, which means it
    // cannot yet be called. Before we make it callable, we need to make sure
    // that the RPC's view of the best block is valid and consistent with
    // ChainstateManager's active tip.
    SetRPCWarmupFinished();

    uiInterface.InitMessage(_("Done loading"));

    for (const auto& client : node.chain_clients) {
        client->start(scheduler);
    }

    BanMan* banman = node.banman.get();
    scheduler.scheduleEvery([banman]{
        banman->DumpBanlist();

Review comment (refactor):
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: 
```cpp
return InitError(strprintf(
    _("Duplicate binding configuration for address %s. "
      "Please check your -bind, -whitebind, and onion binding settings."),
    conflict->ToStringAddrPort()));
```

Code after:

        if (args.IsArgSet("-dnsseed") && args.GetBoolArg("-dnsseed", DEFAULT_DNSSEED) && args.IsArgSet("-proxy")) {
            LogInfo("-dnsseed is ignored when -connect is used and -proxy is specified");
        }
    }

    const std::string& i2psam_arg = args.GetArg("-i2psam", "");
    if (!i2psam_arg.empty()) {
        const std::optional<CService> addr{Lookup(i2psam_arg, 7656, fNameLookup)};
        if (!addr.has_value() || !addr->IsValid()) {
            return InitError(strprintf(_("Invalid -i2psam address or hostname: '%s'"), i2psam_arg));
        }
        SetProxy(NET_I2P, Proxy{addr.value()});
    } else {
        if (!onlynets.empty() && g_reachable_nets.Contains(NET_I2P)) {
            return InitError(
                _("Outbound connections restricted to i2p (-onlynet=i2p) but "
                  "-i2psam is not provided"));
        }
        g_reachable_nets.Remove(NET_I2P);
    }

    connOptions.m_i2p_accept_incoming = args.GetBoolArg("-i2pacceptincoming", DEFAULT_I2P_ACCEPT_INCOMING);

    if (auto conflict = CheckBindingConflicts(connOptions)) {
        return InitError(strprintf(
            _("Duplicate binding configuration for address %s. "
                "Please check your -bind, -bind=...=onion and -whitebind settings."),
                    conflict->ToStringAddrPort()));
    }

    if (!node.connman->Start(scheduler, connOptions)) {
        return false;
    }

    // ********************************************************* Step 13: finished

    // At this point, the RPC is "started", but still in warmup, which means it
    // cannot yet be called. Before we make it callable, we need to make sure
    // that the RPC's view of the best block is valid and consistent with
    // ChainstateManager's active tip.
    SetRPCWarmupFinished();

    uiInterface.InitMessage(_("Done loading"));

    for (const auto& client : node.chain_clients) {
        client->start(scheduler);
    }

    BanMan* banman = node.banman.get();
    scheduler.scheduleEvery([banman]{