class ClientPeerSelectorextends TunnelPeerSelector
Pick peers randomly out of the fast pool, and put them into tunnels ordered by XOR distance from a random key.
  • Field Details

    • SEVERE_ATTACK_THRESHOLD

      private static final double SEVERE_ATTACK_THRESHOLD
      Build success ratio entering severe attack mode.
      See Also:
    • STARTUP_GRACE_MS

      private static final long STARTUP_GRACE_MS
      Startup grace period: strict first-hop gates and soft fallbacks are relaxed for the first 15 minutes.
      See Also:
    • ESTABLISHED_PREF_ATTEMPTS

      private static final int ESTABLISHED_PREF_ATTEMPTS
      First-hop quality attempts before preferring connecting peers.
      See Also:
    • CONNECTING_PREF_ATTEMPTS

      private static final int CONNECTING_PREF_ATTEMPTS
      First-hop quality attempts before accepting any tier-passing peer.
      See Also:
    • FIRST_HOP_CANDIDATES

      private static final int FIRST_HOP_CANDIDATES
      How many first-hop candidates to fetch at once.

      The quality loop can only iterate while it has a candidate to reject, so asking the tiers for exactly one left it a single attempt per refill and the 16-attempt budget went largely unspent. A small batch lets one tier scan serve several attempts. Only the accepted peer is kept. See the trim after the loop — so the rest never reach the tunnel.

      See Also:
    • WIDEN_BUILD_SUCCESS

      static final double WIDEN_BUILD_SUCCESS
      Build success below which the first hop draws on both the Fast and HighCapacity tiers.

      The two tiers hold different peers: Fast is speed-ranked, HighCapacity is proven-reliability. Below this the network is not delivering often enough for the distinction to earn its cost, and taking from only one of them needlessly halves the pool.

      See Also:
    • WIDEN_FAST_PEERS

      static final int WIDEN_FAST_PEERS
      Fast-tier size below which the first hop draws on both tiers regardless of build success.

      A small Fast tier cannot supply FIRST_HOP_CANDIDATES candidates however healthy the network looks, so the batch is worth completing from the other tier even when success is high. The same 300 marks the point where cross-pool diversity engages, so both decisions agree on what counts as a large enough Fast tier.

      See Also:
    • CONNECTING_PREF_BUILD_SUCCESS

      static final double CONNECTING_PREF_BUILD_SUCCESS
      Build success below which the first-hop quality loop starts one tier lower, accepting peers with only a connecting transport session rather than requiring an established one.

      Distinct from WIDEN_BUILD_SUCCESS: this widens the transport requirement, which costs build latency, so it waits until success is closer to healthy.

      See Also:
    • TIER_LOG_INTERVAL_MS

      private static final long TIER_LOG_INTERVAL_MS
      How often the accepted-first-hop tier line may be written, per tier. @since 0.9.71+.
      See Also:
    • _tierLogTime

      private final Map<String,Long> _tierLogTime
      Last write time per accepted tier key, for rate limiting. @since 0.9.71+.
    • _tierLogCount

      private final Map<String,Long> _tierLogCount
      Accepts per tier since the last log line, so volume is visible. @since 0.9.71+.
    • PRECONNECT_COOLDOWN_MS

      private static final long PRECONNECT_COOLDOWN_MS
      Pre-connect cooldown: peers connected within this window don't need pre-connect again.
      See Also:
    • MAX_PRECONNECT_MAP_SIZE

      static final int MAX_PRECONNECT_MAP_SIZE
      Soft ceiling on _lastPreConnect. Once exceeded, entries past the cooldown are swept, so the map cannot grow without bound across a long uptime as the router pre-connects to ever more distinct peers.
      See Also:
    • PROP_PRECONNECT_OPTIMIZE

      static final String PROP_PRECONNECT_OPTIMIZE
      Property to enable/disable conditional pre-connect in selectFirstHop.
      See Also:
    • PROP_PRECONNECT_OPTIMIZE_DEFAULT

      private static final boolean PROP_PRECONNECT_OPTIMIZE_DEFAULT
      Default: true.
      See Also:
    • CROSS_POOL_DIVERSITY_THRESHOLD

      static final int CROSS_POOL_DIVERSITY_THRESHOLD
      Cross-pool diversity: when fast tier exceeds this count, exclude peers in ANY active tunnel across ALL pools to force each pool to use different fast peers.
      See Also:
    • CROSS_POOL_BUILD_SUCCESS_MIN

      static final double CROSS_POOL_BUILD_SUCCESS_MIN
      Cross-pool diversity is only active when build success is above this threshold. Higher than TunnelPeerSelector.ATTACK_THRESHOLD because cross-pool exclusion is far more aggressive (excludes ALL active peers across ALL pools) and can starve builds when most fast peers are in active tunnels.
      See Also:
    • CROSS_POOL_EXCLUSION_RATIO

      static final double CROSS_POOL_EXCLUSION_RATIO
      Cross-pool exclusion may not consume more than this fraction of the fast tier. When most fast peers are in active tunnels, natural pool-local diversity already provides enough variability — explicit exclusion risks starvation.
      See Also:
    • PROP_LEGACY_SELECTION

      private static final String PROP_LEGACY_SELECTION
      Config key for the legacy selection code.
      See Also:
    • PROP_STRATEGY

      private static final String PROP_STRATEGY
      Config key naming the selection strategy.
      See Also:
    • STRATEGY_DEFAULT

      private static final String STRATEGY_DEFAULT
      Strategy name of the default selection.
      See Also:
    • STRATEGY_RELIABILITY

      private static final String STRATEGY_RELIABILITY
      Strategy name of the reliability selection.
      See Also:
    • STRATEGY_DIVERSITY

      private static final String STRATEGY_DIVERSITY
      Strategy name of the diversity selection.
      See Also:
    • MAX_DIVERSITY_RETRIES

      private static final int MAX_DIVERSITY_RETRIES
      Maximum first-hop diversity retries when concurrent builds exclude already-selected first hops. Bounded so a near-total exclusion set cannot turn selection into a long loop.
      See Also:
    • MIN_STRUCTURAL_NON_SELF_HOPS

      static final int MIN_STRUCTURAL_NON_SELF_HOPS
      Minimum non-self hops a selection needs to be buildable at all: the peer adjacent to us (IBGW for inbound, our first hop for outbound) and the far endpoint. Matches the keepAtLeast floor TunnelPeerSelector.dropUnreliable(List, Set, int) already uses.
      See Also:
    • _lastPreConnect

      private static final ConcurrentHashMap<Hash,Long> _lastPreConnect
      Tracks the last time preConnectTo was successfully called per peer.
  • Constructor Details

    • ClientPeerSelector

      public ClientPeerSelector(RouterContext context)
      Creates the selector over the given router context.
      Parameters:
      context - the router context
  • Method Details

    • getStrategy

      private String getStrategy()
      Returns the configured peer selection strategy.
      Returns:
      the strategy name
    • formatPeerList

      private static String formatPeerList(List<Hash> peers)
      Formats a peer list for the log.
      Parameters:
      peers - the peers to format
      Returns:
      the base64 peer hashes, space separated
    • selectPeers

      public List<Hash> selectPeers(TunnelPoolSettings settings)
      Returns ENDPOINT FIRST, GATEWAY LAST!!!! In: us .. closest .. middle .. IBGW Out: OBGW .. middle .. closest .. us.
      Specified by:
      selectPeers in class TunnelPeerSelector
      Parameters:
      settings - the tunnel pool settings
      Returns:
      ordered list of Hash objects (one per peer) specifying what order they should appear in a tunnel (ENDPOINT FIRST). This includes the local router in the list. Never null; an empty list means no peers could be selected.
    • selectPeers

      public List<Hash> selectPeers(TunnelPoolSettings settings, Set<Hash> excludeFirstHops)
      Select peers for a new tunnel, excluding given peers from first-hop selection. Used by the build executor's dispatch loop to ensure concurrent builds in the same batch target diverse first-hop peers — stacking multiple build requests on one peer floods it and makes the first build answer slower.

      The first hop is the last element of the returned list (endpoint-first order). If the selected first hop is excluded, selection retries up to MAX_DIVERSITY_RETRIES times before returning the last result, so a caller always gets a usable set even when the exclusion set covers most of the tier.

      Overrides:
      selectPeers in class TunnelPeerSelector
      Parameters:
      settings - the pool's settings, supplying tunnel length, variance, depth, bandwidth and IP restriction for this selection
      excludeFirstHops - first-hop peers already targeted by concurrent builds in the same dispatch batch, may be null
      Returns:
      ordered hops, endpoint first; empty when nothing usable exists
    • selectPeersBase

      private List<Hash> selectPeersBase(TunnelPoolSettings settings)
      Select peers for a new tunnel. Delegates to selectPeers(TunnelPoolSettings, Set) with a null exclusion set.
      Parameters:
      settings - the pool's settings, supplying tunnel length, variance, depth, bandwidth and IP restriction for this selection
      Returns:
      ordered hops, endpoint first; empty when nothing usable exists
    • shouldDiscardShortSelection

      static boolean shouldDiscardShortSelection(int rvSize, int min)
      Whether a post-finalize selection is too short to build and must be discarded for a redraw.

      Pure decision helper for the shortfall guard in selectPeers(), extracted for unit testing. A selection is discarded only when it holds fewer than MIN_STRUCTURAL_NON_SELF_HOPS non-self hops — that is, when it cannot form a tunnel regardless of what was requested. Anything longer is returned as a short tunnel, which the shortfall ladder already produces deliberately; discarding those to force a redraw converts a one-hop shortfall into a whole lost build cycle, and at minimum length (what a starved pool asks for) there is no slack to absorb it.

      Parameters:
      rvSize - selection size after finalizeSelection, including self
      min - minimum requested tunnel length, from minRequestedLength(TunnelPoolSettings)
      Returns:
      true if the selection must be discarded for a redraw
    • selectHopsWithRetry

      private List<Hash> selectHopsWithRetry(TunnelPoolSettings settings, int length, ClientPeerSelector.SelectionParams params, ClientPeerSelector.SelectionExclusions ex)
      Draws every hop, drops unsuitable peers (ghost and banlisted), and redraws once from a fresh exclusion set when filtering emptied the selection. The retry is what turns "all chosen peers were unusable" from a wasted build cycle into a usable tunnel.
      Parameters:
      settings - the pool's settings, supplying tunnel length, variance, depth, bandwidth and IP restriction for this selection
      length - tunnel length being built
      params - shared selection parameters
      ex - live exclusions, mutated so a retry cannot re-pick rejects
      Returns:
      ordered hops, endpoint first; empty when nothing usable exists
    • selectHops

      Single-hop or multi-hop selection for one tunnel length. Never null.
      Parameters:
      settings - the pool's settings, deciding the single- or multi-hop path
      length - tunnel length being built; 1 selects a single hop
      params - shared selection parameters for this attempt
      ex - live exclusions, mutated by whichever path runs
      Returns:
      ordered hops with the endpoint first, empty if nothing usable was found
    • computeSelectionParams

      private ClientPeerSelector.SelectionParams computeSelectionParams(TunnelPoolSettings settings, int length, boolean isInbound)
      Compute the shared selection parameters: tier priority, closest-hop checks, hidden flags and the IP restriction.
      Parameters:
      settings - the pool's settings, source of the configured IP restriction
      length - tunnel length being built; a length of 1 or less clears the IP restriction outright
      isInbound - true for an inbound tunnel, which decides which of the two hidden flags is set
      Returns:
      the parameters shared by every hop selection of this attempt
    • buildExclusions

      private ClientPeerSelector.SelectionExclusions buildExclusions(TunnelPoolSettings settings, boolean isInbound, double buildSuccess, int length)
      Build the lazy Excluder with client/shared cooldowns, first/last peer and pool diversity exclusions.
      Parameters:
      settings - the pool's settings, source of the destination and of the first/last peer exclusions
      isInbound - true to use the inbound pool and its cooldown bookkeeping
      buildSuccess - the tunnel build success ratio in [0.0, 1.0], fetched once per selection
      length - tunnel length being built, used to decide whether first-hop cooldowns may be relaxed
      Returns:
      the exclusions to apply while selecting this tunnel's hops
    • shouldRelaxCooldownToFirstHop

      static boolean shouldRelaxCooldownToFirstHop(int usableTunnels, int length)
      Whether shared peer cooldowns should be relaxed to a first-hop-only filter: with at most one usable tunnel the pool is near-collapsed, and excluding checkTunnel-cooled peers from every hop can leave no selectable path at all. The cooled peers stay filtered at first-hop selection (where the build actually contacts them). Middle and last hops may use them. Single-hop tunnels (no multi-hop quality loop) and unknown pools (negative count) never relax.
      Parameters:
      usableTunnels - usable tunnels in this pool, or -1 when the pool is unknown
      length - tunnel length being selected for
      Returns:
      true when cooldowns should apply to the first hop only
    • selectSingleHop

      private List<Hash> selectSingleHop(TunnelPoolSettings settings, int length, ClientPeerSelector.SelectionParams params, ClientPeerSelector.SelectionExclusions ex, ArraySet<Hash> matches)
      Select the single hop of a 1-hop tunnel (with hidden-inbound special case).
      Parameters:
      settings - the pool's settings; not consulted on this path
      length - tunnel length, used as the draw size for the fallback tiers
      params - shared selection parameters, deciding the hidden and closest-hop paths
      ex - live exclusions; the wrapped exclude set is written back for the shortfall fallback
      matches - the pre-sized accumulator the tier draws fill
      Returns:
      the hop in a list, empty when no tier could supply a peer
    • selectMultiHop

      private List<Hash> selectMultiHop(TunnelPoolSettings settings, int length, ClientPeerSelector.SelectionParams params, ClientPeerSelector.SelectionExclusions ex, ArraySet<Hash> matches)
      Select all hops of a multi-hop tunnel: last hop, middle hops, then first hop. rv ends up [endpoint .. first hop]. The vetted first hop is recorded on ex so a shortfall fill inserts middles before it instead of shifting roles. A missing first hop is logged and left for that fill — discarding the whole selection would waste a usable endpoint and middles.
      Parameters:
      settings - the pool's settings, source of the random key and the destination used for sub-tier placement
      length - tunnel length being built, two or more
      params - shared selection parameters for this attempt
      ex - live exclusions; the vetted first hop is recorded on it
      matches - scratch accumulator, cleared and reused for each hop group
      Returns:
      the hops ordered endpoint first, empty if no last hop was found
    • buildLastHopExclude

      private Set<Hash> buildLastHopExclude(ClientPeerSelector.SelectionParams params, Set<Hash> exclude)
      Build the last-hop exclusion set: closest-hop wrapped for inbound, OBEP wrapped for outbound.
      Parameters:
      params - shared selection parameters, deciding the wrapper and whether the closest-hop check applies
      exclude - the live exclusion set to wrap
      Returns:
      the wrapped exclusions used when picking the last hop
    • selectLastHop

      private boolean selectLastHop(TunnelPoolSettings settings, int length, ClientPeerSelector.SelectionParams params, SessionKey randomKey, Set<Hash> lastHopExclude, Set<Hash> rawExclude, ArraySet<Hash> matches)
      Select the last hop (hidden-inbound closest hop, hidden-outbound OBEP, or normal OBEP).
      Parameters:
      settings - the pool's settings, source of the destination whose inbound pool is consulted
      length - tunnel length being built, used for the two-hop sub-tier split
      params - shared selection parameters, deciding which last-hop role applies
      randomKey - the tunnel's random key for sub-tier placement, not consulted here
      lastHopExclude - wrapped exclusions (closest-hop / OBEP role checks)
      rawExclude - the unwrapped exclusion set, used only for the final recovery pass when the wrapper excluded every candidate
      matches - accumulator filled with the chosen last hop
      Returns:
      false to abort the selection
    • selectMiddleHops

      private void selectMiddleHops(int length, ClientPeerSelector.SelectionParams params, SessionKey randomKey, ClientPeerSelector.SelectionExclusions ex, ArraySet<Hash> matches, List<Hash> rv)
      Select the middle hop(s) of a tunnel longer than 2 hops, subtiers 2-3, quality-ordered.
      Parameters:
      length - tunnel length; the middle count is length minus two
      params - shared selection parameters, deciding which tier leads the draw
      randomKey - key used to order the chosen middles, so their sequence is specific to this tunnel
      ex - live exclusions, extended with the peers chosen here
      matches - accumulator for the middle draws, cleared on the way out
      rv - the hop list built so far; the ordered middles are appended to it
    • selectFirstHop

      private void selectFirstHop(int length, ClientPeerSelector.SelectionParams params, SessionKey randomKey, ClientPeerSelector.SelectionExclusions ex, ArraySet<Hash> matches)
      Select the first hop, IBGW for inbound and the closest outbound peer otherwise, with a quality loop and pre-connect.
      Parameters:
      length - tunnel length, not consulted on this path
      params - shared selection parameters: tier priority, hidden flags and the IP restriction
      randomKey - the tunnel's random key, used when refilling a rejected first hop
      ex - live exclusions; first-hop cooldowns are read from it and rejections are added back to it
      matches - accumulator filled with the chosen first hop, left empty if none passed the quality loop
    • trimToAcceptedFirstHop

      static void trimToAcceptedFirstHop(ArraySet<Hash> matches, Hash accepted)
      Reduce a first-hop candidate batch to the single peer the quality loop accepted.

      When the loop accepted a candidate it is the one to keep, and it is the head of the set because every attempt takes iterator().next(). When nothing was accepted the loop has already ejected everything it vetted and rejected, so whatever remains was never cleared — kept deliberately rather than discarded, since returning empty costs the cycle a build outright.

      No side effects beyond the set, so unit tests exercise it directly.

      Parameters:
      matches - the candidate batch, modified to hold at most the accepted peer
      accepted - the peer the quality loop accepted, or null if none was
    • shouldWidenBothTiers

      static boolean shouldWidenBothTiers(double buildSuccess, int fastPeers)
      Whether the first hop should draw candidates from both the Fast and HighCapacity tiers.

      True when the network is struggling, or when the Fast tier is too small to fill the candidate batch on its own. Either way, taking from one tier alone needlessly shrinks the pool the quality loop chooses from.

      Pure decision — no context access, safe for unit tests.

      Parameters:
      buildSuccess - the build success ratio in [0.0, 1.0]
      fastPeers - the current Fast-tier size
      Returns:
      whether to take candidates from both tiers
    • topUpFromFastTier

      private void topUpFromFastTier(ClientPeerSelector.SelectionParams params, Set<Hash> exclude, ArraySet<Hash> matches)
      Add Fast-tier candidates until the batch is full, used when the first hop is widening past the HighCapacity tier it started from.

      Passes the batch's full size and lets the selector compute the shortfall — the same accounting ProfileOrganizer.remainingToSelect(int, Set) does for every other caller, so the top-up cannot drift from the tiers beneath it.

      Parameters:
      params - shared selection parameters, source of the IP restriction and the subnet accumulator the draw must respect
      exclude - peers the batch draw must skip
      matches - the first-hop batch, topped up to FIRST_HOP_CANDIDATES entries
    • topUpFromHighCapacityTier

      private void topUpFromHighCapacityTier(ClientPeerSelector.SelectionParams params, Set<Hash> exclude, ArraySet<Hash> matches)
      Add HighCapacity-tier candidates until the batch is full. See topUpFromFastTier(ClientPeerSelector.SelectionParams, Set, ArraySet).
      Parameters:
      params - shared selection parameters, source of the IP restriction and set
      exclude - peers the batch draw must skip
      matches - the first-hop batch being filled
    • refillFirstHop

      private void refillFirstHop(ClientPeerSelector.SelectionParams params, SessionKey randomKey, ClientPeerSelector.SelectionExclusions ex, ArraySet<Hash> matches, boolean inStartup)
      Refill the first-hop candidate batch after a quality-loop ejection. Tries the same tier cascade as selectFirstHop (HighCap → Fast → Active → NotFailing) with the ejected peer excluded, so the slot isn't wasted. At most 3 refills per build cycle.

      Refills the whole FIRST_HOP_CANDIDATES batch rather than a single peer. The quality loop is budgeted for 16 attempts, and a batch that shrinks by one per ejection reaches that budget by draining rather than by exhausting it: with a batch of 4 and three refills of one, only seven candidates are ever examined and the remaining nine attempts — plus the degraded-candidate fallback behind them — never run.

      Parameters:
      params - the tier cascade flags, held constant across every refill
      randomKey - random key for Fast tier sub-tiering
      ex - exclusion set (ejected peer added by caller)
      matches - empty set to populate with a fresh batch
      inStartup - true if still in startup grace period
    • isStartupGracePeriod

      static boolean isStartupGracePeriod(RouterContext ctx)
      Whether the router is still in the startup grace period (first STARTUP_GRACE_MS ms of uptime). The startup grace relaxes the number of quality attempts (fewer candidates early on) but does not skip hard-fail gates: first-hop-failing peers, stale peers, and the established/connecting preference are still enforced to avoid selecting unreliable tunnel targets.

      Pure decision — no side effects.

      Parameters:
      ctx - the router context
      Returns:
      whether the router is within the startup grace period
    • shouldPreConnect

      static boolean shouldPreConnect(RouterContext ctx, Hash peer)
      Determines whether preConnectTo() should be called for a first-hop candidate. Returns false (skip pre-connect) when the build timeout is sufficient to survive the ~8.5s SSU2 handshake, the peer was recently connected, or the feature is disabled. Returns true when pre-connect is needed to ensure reliable tunnel delivery.

      Approach 1: If the request timeout exceeds 15s, the message survives the handshake, so skip pre-connect. Approach 3: Property toggle allows runtime disabling. Approach 4: Recently connected peers don't need pre-connect again.

      The timeout is read through BuildRequestor.getRequestTimeout(RouterContext) so it reflects the budget the builder actually runs with, including runtime tuning. Reading ctx.getProperty here instead saw neither the tuned value nor a configured one: with the property unset its own default equalled the threshold, so the comparison was always true and first-hop pre-connect never ran on a default install.

      Pure decision — no side effects.

      Parameters:
      ctx - the router context
      peer - the first-hop candidate
      Returns:
      whether preConnectTo() should be called for this peer
    • wasRecentlyConnected

      private static boolean wasRecentlyConnected(RouterContext ctx, Hash peer)
      Check whether the peer was connected within the cooldown window.
      Parameters:
      ctx - the router context
      peer - the peer to check
      Returns:
      true if the peer was established or connecting recently
    • recordPreConnect

      static void recordPreConnect(Hash peer, long time)
      Records that preConnectTo was called for the peer at the given time.
      Parameters:
      peer - the peer that was pre-connected
      time - the router clock value to store as the peer's last pre-connect
    • clearPreConnectHistory

      static void clearPreConnectHistory()
      Clears the pre-connect history (for testing).
    • pruneStalePreConnect

      static int pruneStalePreConnect(ConcurrentHashMap<Hash,Long> map, long now)
      Drop pre-connect records older than the cooldown window. An entry is only ever read as "connected within the last PRECONNECT_COOLDOWN_MS" (see wasRecentlyConnected(RouterContext, Hash)), so once it ages past that window it can never change a decision again — it is dead weight. Without this sweep the map grows with every distinct peer the router pre-connects to over its lifetime. Weakly-consistent iteration, so it never blocks a concurrent recordPreConnect(Hash, long).
      Parameters:
      map - the pre-connect history to prune in place
      now - the router clock value to age entries against
      Returns:
      the number of entries removed
    • logAcceptedFirstHopTier

      private void logAcceptedFirstHopTier(RouterContext ctx, Hash firstHop, int tier, int attempt, int attempts, int remaining, double buildSuccess)
      Log the quality tier that actually produced this build's first hop.

      The first-hop quality ladder relaxes its reachability requirement as attempts accumulate: tiers 0 and 1 require the peer to be established or connecting, tier 2 requires neither. A gateway taken at tier 2 is therefore one we have no transport session with, and cannot receive a build request — which is indistinguishable, from the outside, from a peer that went silent. This line is the only way to tell those apart after the fact.

      Rate limited to one line per TIER_LOG_INTERVAL_MS per tier so a busy build loop cannot turn the diagnosis into the noise it is meant to explain; the counters accumulate regardless and are reported in the same line. The limiter uses compute rather than replace(k, old, new): replace is a no-op returning false when the key is absent, so the first log for each tier would never happen and the key would never be inserted.

      Parameters:
      ctx - the router context
      firstHop - the accepted gateway, or null if none was accepted
      tier - the tier it was accepted at, or -1
      attempt - the attempt number it was accepted on, or -1
      attempts - total quality attempts made
      remaining - candidates left in the set
      buildSuccess - the tunnel build success ratio, reported in the log line
    • firstHopQualityTier

      static int firstHopQualityTier(int attempts, boolean inStartup, int currentTier)
      Build first-hop quality tier. Pure so the ladder is testable.

      Tier 0 and 1 require an established or connecting peer; tier 2 requires neither. The ladder reaches tier 2 after CONNECTING_PREF_ATTEMPTS rejections, which is the point where an unreachable gateway stops being filtered out.

      Parameters:
      attempts - 1-based quality attempt count
      inStartup - whether the router is inside its startup grace period
      currentTier - the tier to start from
      Returns:
      the tier to apply for this attempt
    • buildSoftFallbackExclude

      private Set<Hash> buildSoftFallbackExclude(Set<Hash> exclude, ClientPeerSelector.SelectionExclusions ex)
      Builds the soft-fallback exclusion set: a copy of the first-hop exclusion set without no-signal-excluded peers that have proven track records (tunnel acceptance > 50% and at least one successful test), giving them a chance when all standard tiers fail. No side effects.
      Parameters:
      exclude - the current first-hop exclusion set
      ex - the selection exclusions with the per-peer reason map
      Returns:
      the reduced exclusion set
    • canUseStressFallback

      static boolean canUseStressFallback(double buildSuccess, boolean useHighCapPrimary, int rvSize)
      Whether the selection may use the progressive stress fallbacks and the shortened-tunnel allowance: network stress (build success below the attack threshold) or HighCap-primary mode, with at least one peer already selected. Pure decision — no side effects.
      Parameters:
      buildSuccess - current tunnel build success rate
      useHighCapPrimary - whether the selection prefers high-capacity peers
      rvSize - current number of selected peers
      Returns:
      whether stress fallbacks may be attempted
    • applyShortfallFallbacks

      private List<Hash> applyShortfallFallbacks(TunnelPoolSettings settings, List<Hash> rv, int length, ClientPeerSelector.SelectionParams params, ClientPeerSelector.SelectionExclusions ex)
      Progressive fallbacks when the selected peers are short of the requested length.
      Parameters:
      settings - the pool's settings, source of the minimum acceptable length
      rv - the hops chosen so far, ordered endpoint first
      length - the tunnel length that was requested
      params - shared selection parameters, deciding which stress fallbacks are allowed
      ex - live exclusions and the vetted first hop the fills are ordered around
      Returns:
      the extended hop list, or an empty list when the shortfall cannot be filled
    • fillRemainingHops

      private int fillRemainingHops(List<Hash> rv, int min, ClientPeerSelector.SelectionParams params, ClientPeerSelector.SelectionExclusions ex)
      Tops rv up to min hops from the tier ladder, inserting new middle hops before the vetted first hop so rv keeps its [endpoint .. first hop] order.

      The ladder only ever widens on genuine scarcity: banlisted, ghost, first-hop-failing and chronically failing peers are rejected by the tier gates before a candidate is ever collected, so a wider tier is more peers, not lower quality. Unsuitable peers are therefore excluded from selection rather than discovered afterwards.

      Parameters:
      rv - the partial selection, endpoint first and non-empty
      min - the minimum hop count to reach
      params - selection parameters (tier priority, stress level)
      ex - live exclusions and the vetted first hop
      Returns:
      number of hops added
    • collectFromTier

      private int collectFromTier(List<Hash> rv, int min, ClientPeerSelector.SelectionParams params, ClientPeerSelector.SelectionExclusions ex)
      One cascading tier selection. The ProfileOrganizer tiers fall through to wider tiers internally (Fast -> HighCap -> NotFailing -> all peers), so a single call with the right entry tier covers the whole ladder.
      Parameters:
      rv - the hop list to extend; new peers go in before the vetted first hop
      min - total hops the selection needs, counted against rv's current size
      params - shared selection parameters, deciding whether HighCap or Fast leads
      ex - live exclusions, extended with the peers taken here
      Returns:
      number of hops added
    • collectActive

      private int collectActive(List<Hash> rv, int min, ClientPeerSelector.SelectionExclusions ex)
      Pulls connected peers, which are not reachable through the profile tier cascade at all: a peer with an established transport session but no usable profile still builds fast.
      Parameters:
      rv - the hop list to extend
      min - total hops the selection needs, counted against rv's current size
      ex - live exclusions the draw must skip
      Returns:
      number of hops added
    • firstHopIndex

      private static int firstHopIndex(List<Hash> rv, Hash firstHop)
      Index at which a missing middle hop must be inserted: just before the vetted first hop, or at the end when there is none yet.
      Parameters:
      rv - the hop list being filled
      firstHop - the vetted first hop, or null when none was selected
      Returns:
      the index of firstHop in rv, or rv.size() when it is absent
    • insertNewPeers

      static int insertNewPeers(List<Hash> rv, int index, Collection<Hash> picked, int need)
      Adds up to need peers from picked that are not already in rv, inserting them at index so the surrounding order is untouched. Mutates only rv.
      Parameters:
      rv - the selection to extend
      index - insert position, clamped to the list bounds
      picked - candidate peers, order preserved
      need - maximum number to insert
      Returns:
      number of peers inserted
    • minRequestedLength

      static int minRequestedLength(TunnelPoolSettings settings)
      Smallest tunnel the pool will accept: the configured length minus any negative variance, never below zero. Pure decision — no side effects.
      Parameters:
      settings - the pool's settings, supplying tunnel length, variance, depth, bandwidth and IP restriction for this selection
      Returns:
      minimum hop count for this pool
    • finalizeSelection

      List<Hash> finalizeSelection(TunnelPoolSettings settings, List<Hash> rv, boolean isInbound)
      Insert self, ghost-filter, strategy post-processing, duplicate re-check, and cooldowns. Quality-sorts the non-self hops for inbound selections only (see below).
      Parameters:
      settings - the pool settings for the tunnel being built
      rv - the selected peers, reordered and filtered in place
      isInbound - true for a client pool tunnel, false for the exploratory pool
      Returns:
      the finalized peer list with this router inserted at the inbound or outbound end, or an empty list if the tunnel check failed
    • filterByReliability

      List<Hash> filterByReliability(Set<Hash> candidates, Set<Hash> exclude)
      Filter candidates through the reliability gate: a peer must have an adequate acceptance ratio and at least one recent activity or connection signal to remain. The input order is preserved. Ranking is left to the caller's quality sort so each reliability signal plays a single role.
      Parameters:
      candidates - peers to filter
      exclude - peers to exclude
      Returns:
      list of peers passing the reliability gate, in input order
    • provenCandidates

      private List<Hash> provenCandidates(Set<Hash> excludePeers)
      Fresh proven responders, freshest first, filtered for basic eligibility: not us, not banlisted, not on selection cooldown, with a locally cached RouterInfo so a build can reach them, and not already active in the destination's pools (a swap must never introduce a per-pool duplicate).
      Parameters:
      excludePeers - peers to exclude because they are already active in this destination's tunnels
      Returns:
      eligible proven candidates for hop swaps, never null
    • positionalConflicts

      private Set<Hash> positionalConflicts(List<Hash> nonSelf, boolean isInbound, Hash dest, long now)
      Selected hops whose placement would give a single peer concurrent entry and exit visibility across this destination's pools — the pairing that enables traffic correlation. Two rules are checked:
      • inbound selection: the chosen IBGW already serves as the OBEP of one of our active outbound tunnels
      • outbound selection: the chosen OBEP already serves as the IBGW of one of our active inbound tunnels
      Parameters:
      nonSelf - the selected non-self peers, gateway-first order
      isInbound - true when selecting for an inbound tunnel
      dest - the destination hash, or null
      now - current time from the router clock
      Returns:
      conflicting peers, possibly empty
    • findUnreliable

      private Set<Hash> findUnreliable(List<Hash> hops, long now)
      The selected hops that fail the reliability gate. Peers without a profile are unknown rather than poor — they are never returned.
      Parameters:
      hops - the selected non-self peers
      now - current time from the router clock
      Returns:
      the subset failing the reliability gate, never null
    • isReliable

      boolean isReliable(PeerProfile profile, long now, long tenMinutes, long thirtyMinutes)
      Reliability gate: the acceptance ratio must be at least 0.3, and the peer must show a recent tunnel-test success, recent activity, or an established connection. Each signal is consulted exactly once.
      Parameters:
      profile - the peer profile (may be null)
      now - current time from the router clock
      tenMinutes - tunnel-test recency window in ms
      thirtyMinutes - activity recency window in ms
      Returns:
      true if the peer passes the reliability gate
    • sortByPeerQuality

      private void sortByPeerQuality(List<Hash> peers, Set<Hash> exclude)
      Sort peers by quality for tunnel building preference. Higher quality peers (recently tested, active, connected) sort first.
      Parameters:
      peers - the selected peers, reordered in place; nothing happens when the list is null or empty
      exclude - peers to push to the end of the list, or null
    • compareQuality

      static int compareQuality(Hash p1, Hash p2, Set<Hash> exclude, PeerProfile prof1, PeerProfile prof2, Map<Hash,Long> proven, long now, long thirtyMinutes)
      Full quality comparison cascade. Stage order is significant — each stage short-circuits the ones below it: excluded last, then proven responders (recent successful participation), then acceptance ratio, slow tunnel-test latency, activity recency, and latency.
      Parameters:
      p1 - peer on the left of the comparison
      p2 - peer on the right of the comparison
      exclude - peers to deprioritize, or null
      prof1 - first peer's profile, or null
      prof2 - second peer's profile, or null
      proven - local snapshot mapping peer hash to proven-responder timestamp; must not be the live shared map, or the sort's contract can be violated mid-flight by build-thread writes
      now - current time from the router clock
      thirtyMinutes - activity window in ms
      Returns:
      negative, zero, or positive
    • compareReliability

      public static int compareReliability(PeerProfile prof1, PeerProfile prof2)
      Compare floodfill reliability: GOOD > OK > UNKNOWN > BAD. Unproven floodfills sort lower to reduce load on them until their reliability is established.
      Parameters:
      prof1 - first peer's profile, or null
      prof2 - second peer's profile, or null
      Returns:
      negative if p1 is more reliable, positive if p2 is more reliable, 0 if equal
    • compareExcluded

      static int compareExcluded(Hash p1, Hash p2, Set<Hash> exclude)
      Excluded peers sort last; two excluded peers compare equal.
      Parameters:
      p1 - peer on the left of the comparison
      p2 - peer on the right of the comparison
      exclude - peers to deprioritize, or null
      Returns:
      negative, zero, or positive
    • compareAcceptance

      static int compareAcceptance(PeerProfile prof1, PeerProfile prof2)
      Acceptance ratio tiers: good (> 0.3) ranks above low (< 0.3), which ranks above dead (<= 0). Missing profiles default to 1.0.
      Parameters:
      prof1 - first peer's profile, or null
      prof2 - second peer's profile, or null
      Returns:
      negative, zero, or positive
    • compareSlowLatency

      static int compareSlowLatency(float lat1, float lat2)
      Peers with tunnel test latency over 15s sort last; when both are slow, the less slow one sorts first. 0 latency means no data (unknown).
      Parameters:
      lat1 - first peer's tunnel test time average
      lat2 - second peer's tunnel test time average
      Returns:
      negative, zero, or positive
    • compareActivity

      static int compareActivity(PeerProfile prof1, PeerProfile prof2, long now, long thirtyMinutes)
      Peers active within the activity window sort first.
      Parameters:
      prof1 - first peer's profile, or null
      prof2 - second peer's profile, or null
      now - current time from the router clock
      thirtyMinutes - activity window in ms
      Returns:
      negative, zero, or positive
    • compareLatency

      static int compareLatency(float lat1, float lat2)
      Lower measured tunnel-test latency sorts first; measured beats unknown. When both latencies are 0 (unknown), peers that have been tested before (have a persisted test timestamp) sort before never-tested peers, so proven peers with expired latency data are preferred over new peers.
      Parameters:
      lat1 - first peer's tunnel test time average
      lat2 - second peer's tunnel test time average
      Returns:
      negative, zero, or positive
    • compareTestHistory

      static int compareTestHistory(PeerProfile prof1, PeerProfile prof2)
      When both peers have unknown latency (lat=0), peers with a proven test history sort first. Uses the persisted EWMA update timestamp to distinguish tested peers from never-tested peers.
      Parameters:
      prof1 - first peer's profile, or null
      prof2 - second peer's profile, or null
      Returns:
      negative, zero, or positive
    • filterGhostPeers

      List<Hash> filterGhostPeers(List<Hash> peers)
      Filter out ghost peers from the selected peer list. Ghost peers are those with consistent tunnel build timeouts.
      Parameters:
      peers - the list of selected peers (excluding self)
      Returns:
      filtered list without ghost peers; never null
    • filterBannedPeers

      List<Hash> filterBannedPeers(List<Hash> peers)
      Drop banlisted peers from a selection so builds do not dispatch requests that BuildHandler will reject with "Next peer is banned". Mirrors filterGhostPeers(List): an all-banned selection returns empty so the caller can redraw from usable candidates instead of dispatching a build that cannot succeed.
      Parameters:
      peers - the list of selected peers (excluding self)
      Returns:
      filtered list without banlisted peers; empty when all were banned