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

Code before:
/**
 * @brief Deduplicates binding vectors internally and checks for cross-vector conflicts.
 *
 * For vWhiteBinds: Removes duplicates with identical service AND permissions, but reports
 * conflicts when same service has different permissions.
 * For other vectors: Simply removes duplicates.
 * Cross-vector duplicates are always reported as conflicts.
 *
 * @param vWhiteBinds Vector of whitelisted bind permissions (will be deduplicated)
 * @param vBinds Vector of regular bind services (will be deduplicated)
 * @param onion_binds Vector of onion-specific bind services (will be deduplicated)
 * @return std::optional<CService> containing the first conflict found, or std::nullopt if no conflicts
 */
std::optional<CService> DeduplicateAndCheckBindingConflicts(
    std::vector<NetWhitebindPermissions>& vWhiteBinds,
    std::vector<CService>& vBinds,
    std::vector<CService>& onion_binds)
{
    // Step 1: Handle vWhiteBinds deduplication with special logic for permissions
    {
        std::map<CService, NetPermissionFlags> seen_whitebinds;
        std::vector<NetWhitebindPermissions> deduplicated;

        for (const auto& wb : vWhiteBinds) {
            auto it = seen_whitebinds.find(wb.m_service);
            if (it == seen_whitebinds.end()) {
                // First occurrence of this service
                seen_whitebinds[wb.m_service] = wb.m_flags;
                deduplicated.push_back(wb);
            } else if (it->second == wb.m_flags) {
                // Same service, same permissions - harmless duplicate, skip it
                continue;
            } else {
                // Same service, different permissions - conflict!
                return wb.m_service;
            }
        }
        vWhiteBinds = std::move(deduplicated);
    }

    // Step 2: Simple deduplication for vBinds
    {
        std::set<CService> seen;
        auto it = std::remove_if(vBinds.begin(), vBinds.end(),
            [&seen](const CService& service) {
                return !seen.insert(service).second;  // Remove if already seen
            });
        vBinds.erase(it, vBinds.end());
    }

    // Step 3: Simple deduplication for onion_binds

Review comment (refactor):
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.

Code after:
/**
 * @brief Checks for duplicate bindings across all binding configurations.
 *
 * @param[in] conn_options Connection options containing the binding vectors to check
 * @return std::optional<CService> containing the first duplicate found, or std::nullopt if no duplicates
 */
static std::optional<CService> CheckBindingConflicts(const CConnman::Options& conn_options)
{
    std::set<CService> seen;

    // Check all whitelisted bindings
    for (const auto& wb : conn_options.vWhiteBinds) {
        if (!seen.insert(wb.m_service).second) {
            return wb.m_service;
        }
    }

    // Check regular bindings
    for (const auto& bind : conn_options.vBinds) {
        if (!seen.insert(bind).second) {
            return bind;
        }
    }

    // Check onion bindings
    for (const auto& onion_bind : conn_options.onion_binds) {
        if (!seen.insert(onion_bind).second) {
            return onion_bind;
        }
    }

    return std::nullopt;
}

// A GUI user may opt to retry once with do_reindex set if there is a failure during chainstate initialization.
// The function therefore has to support re-entry.
static ChainstateLoadResult InitAndLoadChainstate(
    NodeContext& node,
    bool do_reindex,
    const bool do_reindex_chainstate,
    const kernel::CacheSizes& cache_sizes,
    const ArgsManager& args)
{
    // This function may be called twice, so any dirty state must be reset.
    node.notifications.reset(); // Drop state, such as a cached tip block
    node.mempool.reset();
    node.chainman.reset(); // Drop state, such as an initialized m_block_tree_db

    const CChainParams& chainparams = Params();

    Assert(!node.notifications); // Was reset above