From 206094a73e23c3e6c09f21a4888b1a8edb217aab Mon Sep 17 00:00:00 2001 From: Sergi Delgado Segura Date: Wed, 28 Jun 2023 14:51:09 -0400 Subject: [PATCH 1/2] Gives seednode priority over dnsseed if both are provided --- src/net.cpp | 199 ++++++++++++++++++++++++++++++---------------------- src/net.h | 2 + 2 files changed, 119 insertions(+), 82 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index 7c82f01d75730..1f319366a4148 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2189,7 +2189,30 @@ void CConnman::ThreadDNSAddressSeed() std::vector seeds = m_params.DNSSeeds(); Shuffle(seeds.begin(), seeds.end(), rng); int seeds_right_now = 0; // Number of seeds left before testing if we have enough connections - int found = 0; + int target_outbound_connections = 2; + int outbound_connection_count = 0; + + auto start = NodeClock::now(); + if (gArgs.IsArgSet("-seednode")) { + LogPrintf("-seednode enabled. Trying the provided seeds before defaulting to the dnsseeds.\n"); + while (!interruptNet) { + if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) + return; + + // Abort if we have spent enough time without reaching our target. + // Giving seed nodes 30 seconds so this does not become a race against fixedseeds (which triggers after 1 min) + if (NodeClock::now() > start + 30s) { + LogPrintf("Couldn't connect to enough peers via seed nodes. Handing fetch logic to the DNS seeds.\n"); + break; + } + + outbound_connection_count = GetFullOutboundConnCount(); + if (outbound_connection_count >= target_outbound_connections) { + LogPrintf("P2P peers available. Finished fetching data from seed nodes.\n"); + break; + } + } + } if (gArgs.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED)) { // When -forcednsseed is provided, query all. @@ -2201,98 +2224,97 @@ void CConnman::ThreadDNSAddressSeed() seeds_right_now = seeds.size(); } - // goal: only query DNS seed if address need is acute - // * If we have a reasonable number of peers in addrman, spend - // some time trying them first. This improves user privacy by - // creating fewer identifying DNS requests, reduces trust by - // giving seeds less influence on the network topology, and - // reduces traffic to the seeds. - // * When querying DNS seeds query a few at once, this ensures - // that we don't give DNS seeds the ability to eclipse nodes - // that query them. - // * If we continue having problems, eventually query all the - // DNS seeds, and if that fails too, also try the fixed seeds. - // (done in ThreadOpenConnections) - const std::chrono::seconds seeds_wait_time = (addrman.Size() >= DNSSEEDS_DELAY_PEER_THRESHOLD ? DNSSEEDS_DELAY_MANY_PEERS : DNSSEEDS_DELAY_FEW_PEERS); - - for (const std::string& seed : seeds) { - if (seeds_right_now == 0) { - seeds_right_now += DNSSEEDS_TO_QUERY_AT_ONCE; - - if (addrman.Size() > 0) { - LogPrintf("Waiting %d seconds before querying DNS seeds.\n", seeds_wait_time.count()); - std::chrono::seconds to_wait = seeds_wait_time; - while (to_wait.count() > 0) { - // if sleeping for the MANY_PEERS interval, wake up - // early to see if we have enough peers and can stop - // this thread entirely freeing up its resources - std::chrono::seconds w = std::min(DNSSEEDS_DELAY_FEW_PEERS, to_wait); - if (!interruptNet.sleep_for(w)) return; - to_wait -= w; - - int nRelevant = 0; - { - LOCK(m_nodes_mutex); - for (const CNode* pnode : m_nodes) { - if (pnode->fSuccessfullyConnected && pnode->IsFullOutboundConn()) ++nRelevant; + // Proceed with dnsseeds if seednodes hasn't reached the target or if forcednsseed is set + if (outbound_connection_count < target_outbound_connections || seeds_right_now) { + // goal: only query DNS seed if address need is acute + // * If we have a reasonable number of peers in addrman, spend + // some time trying them first. This improves user privacy by + // creating fewer identifying DNS requests, reduces trust by + // giving seeds less influence on the network topology, and + // reduces traffic to the seeds. + // * When querying DNS seeds query a few at once, this ensures + // that we don't give DNS seeds the ability to eclipse nodes + // that query them. + // * If we continue having problems, eventually query all the + // DNS seeds, and if that fails too, also try the fixed seeds. + // (done in ThreadOpenConnections) + int found = 0; + const std::chrono::seconds seeds_wait_time = (addrman.Size() >= DNSSEEDS_DELAY_PEER_THRESHOLD ? DNSSEEDS_DELAY_MANY_PEERS : DNSSEEDS_DELAY_FEW_PEERS); + + for (const std::string& seed : seeds) { + if (seeds_right_now == 0) { + seeds_right_now += DNSSEEDS_TO_QUERY_AT_ONCE; + + if (addrman.Size() > 0) { + LogPrintf("Waiting %d seconds before querying DNS seeds.\n", seeds_wait_time.count()); + std::chrono::seconds to_wait = seeds_wait_time; + while (to_wait.count() > 0) { + // if sleeping for the MANY_PEERS interval, wake up + // early to see if we have enough peers and can stop + // this thread entirely freeing up its resources + std::chrono::seconds w = std::min(DNSSEEDS_DELAY_FEW_PEERS, to_wait); + if (!interruptNet.sleep_for(w)) return; + to_wait -= w; + + if (GetFullOutboundConnCount() >= target_outbound_connections) { + if (found > 0) { + LogPrintf("%d addresses found from DNS seeds\n", found); + LogPrintf("P2P peers available. Finished DNS seeding.\n"); + } else { + LogPrintf("P2P peers available. Skipped DNS seeding.\n"); + } + return; } } - if (nRelevant >= 2) { - if (found > 0) { - LogPrintf("%d addresses found from DNS seeds\n", found); - LogPrintf("P2P peers available. Finished DNS seeding.\n"); - } else { - LogPrintf("P2P peers available. Skipped DNS seeding.\n"); - } - return; - } } } - } - - if (interruptNet) return; - // hold off on querying seeds if P2P network deactivated - if (!fNetworkActive) { - LogPrintf("Waiting for network to be reactivated before querying DNS seeds.\n"); - do { - if (!interruptNet.sleep_for(std::chrono::seconds{1})) return; - } while (!fNetworkActive); - } + if (interruptNet) return; - LogPrintf("Loading addresses from DNS seed %s\n", seed); - // If -proxy is in use, we make an ADDR_FETCH connection to the DNS resolved peer address - // for the base dns seed domain in chainparams - if (HaveNameProxy()) { - AddAddrFetch(seed); - } else { - std::vector vAdd; - constexpr ServiceFlags requiredServiceBits{SeedsServiceFlags()}; - std::string host = strprintf("x%x.%s", requiredServiceBits, seed); - CNetAddr resolveSource; - if (!resolveSource.SetInternal(host)) { - continue; + // hold off on querying seeds if P2P network deactivated + if (!fNetworkActive) { + LogPrintf("Waiting for network to be reactivated before querying DNS seeds.\n"); + do { + if (!interruptNet.sleep_for(std::chrono::seconds{1})) return; + } while (!fNetworkActive); } - unsigned int nMaxIPs = 256; // Limits number of IPs learned from a DNS seed - const auto addresses{LookupHost(host, nMaxIPs, true)}; - if (!addresses.empty()) { - for (const CNetAddr& ip : addresses) { - CAddress addr = CAddress(CService(ip, m_params.GetDefaultPort()), requiredServiceBits); - addr.nTime = rng.rand_uniform_delay(Now() - 3 * 24h, -4 * 24h); // use a random age between 3 and 7 days old - vAdd.push_back(addr); - found++; - } - addrman.Add(vAdd, resolveSource); - } else { - // If the seed does not support a subdomain with our desired service bits, - // we make an ADDR_FETCH connection to the DNS resolved peer address for the - // base dns seed domain in chainparams + + LogPrintf("Loading addresses from DNS seed %s\n", seed); + // If -proxy is in use, we make an ADDR_FETCH connection to the DNS resolved peer address + // for the base dns seed domain in chainparams + if (HaveNameProxy()) { AddAddrFetch(seed); + } else { + std::vector vAdd; + constexpr ServiceFlags requiredServiceBits{SeedsServiceFlags()}; + std::string host = strprintf("x%x.%s", requiredServiceBits, seed); + CNetAddr resolveSource; + if (!resolveSource.SetInternal(host)) { + continue; + } + unsigned int nMaxIPs = 256; // Limits number of IPs learned from a DNS seed + const auto addresses{LookupHost(host, nMaxIPs, true)}; + if (!addresses.empty()) { + for (const CNetAddr& ip : addresses) { + CAddress addr = CAddress(CService(ip, m_params.GetDefaultPort()), requiredServiceBits); + addr.nTime = rng.rand_uniform_delay(Now() - 3 * 24h, -4 * 24h); // use a random age between 3 and 7 days old + vAdd.push_back(addr); + found++; + } + addrman.Add(vAdd, resolveSource); + } else { + // If the seed does not support a subdomain with our desired service bits, + // we make an ADDR_FETCH connection to the DNS resolved peer address for the + // base dns seed domain in chainparams + AddAddrFetch(seed); + } } + --seeds_right_now; } - --seeds_right_now; + LogPrintf("%d addresses found from DNS seeds\n", found); + } else { + LogPrintf("Skipping DNS seeds. Enough peers have been found\n"); } - LogPrintf("%d addresses found from DNS seeds\n", found); } void CConnman::DumpAddresses() @@ -2343,6 +2365,19 @@ void CConnman::StartExtraBlockRelayPeers() m_start_extra_block_relay_peers = true; } +// Return the number of outbound connections that are full relay (not blocks only) +int CConnman::GetFullOutboundConnCount() const +{ + int nRelevant = 0; + { + LOCK(m_nodes_mutex); + for (const CNode* pnode : m_nodes) { + if (pnode->fSuccessfullyConnected && pnode->IsFullOutboundConn()) ++nRelevant; + } + } + return nRelevant; +} + // Return the number of peers we have over our outbound connection limit // Exclude peers that are marked for disconnect, or are going to be // disconnected soon (eg ADDR_FETCH and FEELER) diff --git a/src/net.h b/src/net.h index e78e122c44f98..282085cd3f50c 100644 --- a/src/net.h +++ b/src/net.h @@ -1174,6 +1174,8 @@ class CConnman void StartExtraBlockRelayPeers(); + // Count the number of full-relay peer we have. + int GetFullOutboundConnCount() const; // Return the number of outbound peers we have in excess of our target (eg, // if we previously called SetTryNewOutboundPeer(true), and have since set // to false, we may have extra peers that we wish to disconnect). This may From 2842e51a246b162a586941184b7694f187d7aee7 Mon Sep 17 00:00:00 2001 From: tdb3 <106488469+tdb3@users.noreply.github.com> Date: Fri, 23 Feb 2024 14:00:53 -0500 Subject: [PATCH 2/2] Added seednode prioritization message to help output --- src/init.cpp | 2 +- src/net.cpp | 26 ++++++++++++++------------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 988daefeec800..937107c273022 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -543,7 +543,7 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-port=", strprintf("Listen for connections on . Nodes not using the default ports (default: %u, testnet: %u, signet: %u, regtest: %u) are unlikely to get incoming connections. Not relevant for I2P (see doc/i2p.md).", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), signetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); argsman.AddArg("-proxy=", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled)", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_ELISION, OptionsCategory::CONNECTION); argsman.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); - argsman.AddArg("-seednode=", "Connect to a node to retrieve peer addresses, and disconnect. This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + argsman.AddArg("-seednode=", "Connect to a node to retrieve peer addresses, and disconnect. This option can be specified multiple times to connect to multiple nodes. During startup, seednodes will be tried before dnsseeds.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-networkactive", "Enable all P2P network activity (default: 1). Can be changed by the setnetworkactive RPC command", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-timeout=", strprintf("Specify socket connection timeout in milliseconds. If an initial attempt to connect is unsuccessful after this amount of time, drop it (minimum: 1, default: %d)", DEFAULT_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-peertimeout=", strprintf("Specify a p2p connection timeout delay in seconds. After connecting to a peer, wait this amount of time before considering disconnection based on inactivity (minimum: 1, default: %d)", DEFAULT_PEER_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION); diff --git a/src/net.cpp b/src/net.cpp index 1f319366a4148..9e9d34b4d229f 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2185,35 +2185,37 @@ void CConnman::WakeMessageHandler() void CConnman::ThreadDNSAddressSeed() { - FastRandomContext rng; - std::vector seeds = m_params.DNSSeeds(); - Shuffle(seeds.begin(), seeds.end(), rng); - int seeds_right_now = 0; // Number of seeds left before testing if we have enough connections - int target_outbound_connections = 2; + constexpr int TARGET_OUTBOUND_CONNECTIONS = 2; int outbound_connection_count = 0; - auto start = NodeClock::now(); if (gArgs.IsArgSet("-seednode")) { - LogPrintf("-seednode enabled. Trying the provided seeds before defaulting to the dnsseeds.\n"); + auto start = NodeClock::now(); + constexpr std::chrono::seconds SEEDNODE_TIMEOUT = 30s; + LogPrintf("-seednode enabled. Trying the provided seeds for %d seconds before defaulting to the dnsseeds.\n", SEEDNODE_TIMEOUT.count()); while (!interruptNet) { if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) return; // Abort if we have spent enough time without reaching our target. // Giving seed nodes 30 seconds so this does not become a race against fixedseeds (which triggers after 1 min) - if (NodeClock::now() > start + 30s) { + if (NodeClock::now() > start + SEEDNODE_TIMEOUT) { LogPrintf("Couldn't connect to enough peers via seed nodes. Handing fetch logic to the DNS seeds.\n"); break; } outbound_connection_count = GetFullOutboundConnCount(); - if (outbound_connection_count >= target_outbound_connections) { - LogPrintf("P2P peers available. Finished fetching data from seed nodes.\n"); + if (outbound_connection_count >= TARGET_OUTBOUND_CONNECTIONS) { + LogPrintf("P2P peers available. Finished fetching data from seed nodes.\n"); break; } } } + FastRandomContext rng; + std::vector seeds = m_params.DNSSeeds(); + Shuffle(seeds.begin(), seeds.end(), rng); + int seeds_right_now = 0; // Number of seeds left before testing if we have enough connections + if (gArgs.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED)) { // When -forcednsseed is provided, query all. seeds_right_now = seeds.size(); @@ -2225,7 +2227,7 @@ void CConnman::ThreadDNSAddressSeed() } // Proceed with dnsseeds if seednodes hasn't reached the target or if forcednsseed is set - if (outbound_connection_count < target_outbound_connections || seeds_right_now) { + if (outbound_connection_count < TARGET_OUTBOUND_CONNECTIONS || seeds_right_now) { // goal: only query DNS seed if address need is acute // * If we have a reasonable number of peers in addrman, spend // some time trying them first. This improves user privacy by @@ -2256,7 +2258,7 @@ void CConnman::ThreadDNSAddressSeed() if (!interruptNet.sleep_for(w)) return; to_wait -= w; - if (GetFullOutboundConnCount() >= target_outbound_connections) { + if (GetFullOutboundConnCount() >= TARGET_OUTBOUND_CONNECTIONS) { if (found > 0) { LogPrintf("%d addresses found from DNS seeds\n", found); LogPrintf("P2P peers available. Finished DNS seeding.\n");