java.lang.Object
net.i2p.router.networkdb.kademlia.PeerSelector
net.i2p.router.networkdb.kademlia.FloodfillPeerSelector

public class FloodfillPeerSelectorextends PeerSelector
This is where we implement semi-Kademlia with the floodfills, by selecting floodfills closest to a given key for searches and stores.

Warning - most methods taking a key as an argument require the routing key, not the original key.

  • Field Details

    • _banLogger

      private BanLogger _banLogger
      Records why floodfills were banned.
    • _facade

      private final KademliaNetworkDatabaseFacade _facade
      The floodfill's own database facade.
    • _cfgCtx

      private static volatile RouterContext _cfgCtx
      Context the cached config values were read from.
    • _cfgRefreshed

      private static volatile long _cfgRefreshed
      When the config values were last refreshed.
    • _cachedEnableUnresponsiveFloodfillBan

      private static volatile boolean _cachedEnableUnresponsiveFloodfillBan
      Cached ban setting for unresponsive floodfills.
    • CONFIG_REFRESH_MS

      private static final long CONFIG_REFRESH_MS
      Interval between config refreshes.
      See Also:
    • _failedLookupRateStatSlot

      private final RateStat[] _failedLookupRateStatSlot
      Rate stat slots for the failed lookups.
    • _testSuccessTimeStatSlot

      private final RateStat[] _testSuccessTimeStatSlot
      Rate stat slots for the test success times.
    • NO_FAIL_STORE_OK

      private static final int NO_FAIL_STORE_OK
      Age a no-fail floodfill counts as OK.
      See Also:
    • NO_FAIL_STORE_GOOD

      private static final int NO_FAIL_STORE_GOOD
      Age a no-fail floodfill counts as good.
      See Also:
    • NO_FAIL_LOOKUP_OK

      private static final int NO_FAIL_LOOKUP_OK
      This must be longer than the max streaming timeout (60s).
      See Also:
    • NO_FAIL_LOOKUP_GOOD

      private static final int NO_FAIL_LOOKUP_GOOD
      Age a lookup counts as good with no failures.
      See Also:
    • MAX_GOOD_RESP_TIME

      private static final int MAX_GOOD_RESP_TIME
      Response time still counted as good, in ms.
      See Also:
    • HEARD_AGE

      private static final long HEARD_AGE
      Age after which a floodfill counts as recently heard about.
      See Also:
    • STARTUP_GRACE_PERIOD

      private static final long STARTUP_GRACE_PERIOD
      Grace period after router startup before enforcing HEARD_AGE. After a restart, profiles may not be fully loaded yet and floodfills we re-discover get temporary firstHeardAbout=now() — locking them out for 45 minutes would hurt the most vulnerable moment.
      See Also:
    • MAX_RI_AGE

      private static final long MAX_RI_AGE
      Floodfills with RouterInfo published longer ago than this are skipped.
      See Also:
    • SAME_IP_MASK

      private static final int SAME_IP_MASK
      Byte count matched when fingerprinting a peer for same-subnet exclusion: /16 for IPv4, /32 for IPv6, mirroring the historic MaskedIPSet(_context, entry, info, 2) call.
      See Also:
  • Constructor Details

    • FloodfillPeerSelector

      public FloodfillPeerSelector(RouterContext ctx, KademliaNetworkDatabaseFacade facade)
      Binds the facade and prepares the ban logger.
      Parameters:
      ctx - the router context
      facade - the network database to select peers from
  • Method Details

    • refreshConfig

      private static void refreshConfig(RouterContext ctx)
      Refresh the cached configuration from properties at most once per CONFIG_REFRESH_MS, or immediately when the context changes. Benign race: duplicate refreshes are idempotent writes.
      Parameters:
      ctx - the router context supplying the clock and properties
    • getEnableUnresponsiveFloodfillBan

      private static boolean getEnableUnresponsiveFloodfillBan(RouterContext ctx)
      Reads the unresponsive floodfill ban setting.
      Parameters:
      ctx - the context to read the config from
      Returns:
      true to ban unresponsive floodfills
    • getRateStat

      private RateStat getRateStat(RateStat[] slot, String name)
      Get the RateStat handle, caching it in the slot for subsequent calls. Null is not cached, the stat may register later.
      Parameters:
      slot - one-element array holding the cached stat between calls
      name - full stat name, e.g. "peer.failedLookupRate"
      Returns:
      the registered stat, or null while it does not exist yet
    • selectNearestExplicit

      List<Hash> selectNearestExplicit(Hash key, int maxNumRouters, Set<Hash> peersToIgnore, KBucketSet<Hash> kbuckets)
      Pick out peers with the floodfill capacity set, returning them first, but then after they're complete, sort via kademlia.

      Walks the kbuckets with a FloodfillSelectionCollector, which gathers floodfill peers in groups (good floodfills first, then bad, then non-floodfills sorted by Kademlia distance to the key).

      Does not prefer the floodfill peers that are directly connected. List will not include our own hash. Returns new list, may be modified.

      Specified by:
      selectNearestExplicit in class PeerSelector
      Parameters:
      key - the ROUTING key (NOT the original key)
      maxNumRouters - the most hashes to return, zero or less meaning no limit
      peersToIgnore - can be null
      kbuckets - the routing table to select from
      Returns:
      List of Hash for the peers selected
    • selectFloodfillParticipants

      List<Hash> selectFloodfillParticipants(KBucketSet<Hash> kbuckets)
      List will not include our own hash. List is not sorted and not shuffled. Returns new list, may be modified.
      Parameters:
      kbuckets - unused, retained for signature compatibility; null is accepted
      Returns:
      all floodfills not banlisted forever.
    • selectFloodfillParticipants

      List<Hash> selectFloodfillParticipants(Set<Hash> toIgnore, KBucketSet<Hash> _kbuckets)
      List MAY INCLUDE our own hash. List is not sorted and not shuffled. Returns new list, may be modified.
      Parameters:
      toIgnore - can be null
      _kbuckets - unused, retained for signature compatibility; null is accepted
      Returns:
      all floodfills not banlisted forever and not flagged unreachable.
    • buildFloodfillCandidates

      private List<Hash> buildFloodfillCandidates(Set<Hash> toIgnore)
      Build the floodfill candidate list with only the cheap per-peer checks: the exclusion set, the two banlist tiers, and the unreachable capability flag. No profile is touched.

      ProfileOrganizer.peerSendsBadReplies(Hash) is deliberately not applied here — it takes ProfileOrganizer's read lock and then four synchronized Rate reads per peer, which on a healthy router is ~1000 lock acquisitions and ~4000 Rate reads for a single exploratory lookup, nearly all of them thrown away because selectFloodfillParticipantsIncludingUs(Hash, int, Set, KBucketSet) only ever walks a bounded prefix of the XOR order. That path applies the predicate lazily instead, which yields the identical selection.

      Parameters:
      toIgnore - can be null
      Returns:
      the candidates, neither sorted nor shuffled; may be modified
    • selectFloodfillParticipants

      List<Hash> selectFloodfillParticipants(Hash key, int maxNumRouters, KBucketSet<Hash> kbuckets)
      Sort the floodfills. The challenge here is to keep the good ones at the front and the bad ones at the back. If they are all good or bad, searches and stores won't work well.

      List will not include our own hash. Returns new list, may be modified. Sorted by closest to the key if > maxNumRouters, otherwise not. The list is in 3 groups - sorted by routing key within each group. Group 1: No store or lookup failure in a long time, and lookup fail rate no more than 1.5 * average Group 2: No store or lookup failure in a little while or success newer than failure Group 3: All others.

      Parameters:
      key - the ROUTING key (NOT the original key)
      maxNumRouters - max to return
      kbuckets - unused, retained for signature compatibility; null is accepted
      Returns:
      floodfills closest to the key that are not banlisted forever
    • selectFloodfillParticipants

      List<Hash> selectFloodfillParticipants(Hash key, int howMany, Set<Hash> toIgnore, KBucketSet<Hash> kbuckets)
      See above for description List will not include our own hash Returns new list, may be modified.
      Parameters:
      key - the ROUTING key (NOT the original key)
      howMany - the maximum number of peers to return
      toIgnore - can be null
      kbuckets - unused, retained for signature compatibility; null is accepted
      Returns:
      the selected peers, ordered best first, never null
    • selectFloodfillParticipantsIncludingUs

      private List<Hash> selectFloodfillParticipantsIncludingUs(Hash key, int howMany, Set<Hash> toIgnore, KBucketSet<Hash> kbuckets)
      See above for description List MAY CONTAIN our own hash unless included in toIgnore Returns new list, may be modified.
      Parameters:
      key - the ROUTING key (NOT the original key)
      howMany - maximum number of floodfill peers to return
      toIgnore - can be null
      kbuckets - unused, retained for signature compatibility; null is accepted
      Returns:
      a new shuffled-by-tier list of up to howMany floodfill hashes, GOOD peers first, then OK, then BAD; shorter if too few qualify
    • maskedIPKey

      static long maskedIPKey(byte[] ip, int mask)
      Compact long fingerprint of a masked IP, replacing the per-candidate String construction (one StringBuilder plus up to 2*mask heap chars) that the historic MaskedIPSet.maskedIP() did per address on the selection hot path. Semantics match MaskedIPSet: the first mask bytes are matched, and an IPv6 address doubles the match width (8 bytes for mask=2, i.e. /64).

      Bit layout: bit 63 is an IPv6 marker (the analog of MaskedIPSet's ':' vs '.' delimiter, so the two families can never collide); the masked bytes' nibbles then occupy bits 59 downward. The largest supported packed key is 15 nibbles (60 bits) below the marker, i.e. IPv4 mask 4 or IPv6 mask 3.

      Parameters:
      ip - an IPv4 (4-byte) or IPv6 (16-byte) address
      mask - 1-4, the number of leading bytes to match
      Returns:
      the packed fingerprint; equal keys mean "same masked subnet"
      Throws:
      IllegalArgumentException - if the packed key would exceed 60 bits (IPv6 with mask 4), which no caller uses
    • addSameIPFingerprint

      static boolean addSameIPFingerprint(Set<Long> maskedIPs, Set<Integer> ports, Set<String> families, RouterContext ctx, Hash peer, RouterInfo pinfo, int mask)
      Fingerprint a candidate peer into the exclusion accumulators, adding compact masked-IP keys, ports, and the claimed family option. One call replaces the per-candidate MaskedIPSet construction (and its per-address toString churn) of the historic selection loop.

      Returns whether any added key collided with a previously seen peer; this is the definition of "same IP" used to push a peer to the BAD tier. The three spaces (masked IP, port, family) are kept separate so, just like MaskedIPSet's '.' / 'p' / 'x' prefixes, a cross-space collision can never occur.

      Parameters:
      maskedIPs - accumulator of packed masked-IP keys, mutated on add
      ports - accumulator of router ports, mutated on add
      families - accumulator of family option values, mutated on add
      ctx - used to resolve the peer's communicate-address
      peer - the candidate's hash
      pinfo - the candidate RouterInfo (validated, never null here)
      mask - byte count for maskedIPKey(byte[], int)
      Returns:
      true if at least one key was already present
    • computeMaxFailRate

      private double computeMaxFailRate(long uptime)
      Compute the maximum acceptable failure rate for a floodfill peer, based on the network average over the last 10 minutes. Returns a value between 0.20 and 0.95.
      Parameters:
      uptime - router uptime in ms; under 2 hours the network average is not consulted
      Returns:
      the failure rate ceiling, 0.95 while uptime or stats are too fresh, otherwise 1.25 times the network average clamped to 0.20 - 0.95
    • classifyFloodfillPeer

      private FloodfillPeerSelector.PeerClass classifyFloodfillPeer(Hash entry, RouterInfo info, boolean sameIP, long now, boolean enforceHeard, double maxFailRate)
      Classify a single floodfill peer as GOOD, OK, or BAD for selection. GOOD: no recent failures, fast response, good profile. OK: some recent failures but still usable. BAD: unreachable, stale, slow, same IP, strict country, or poor profile.
      Parameters:
      entry - hash of the floodfill peer being classified
      info - the peer's RouterInfo from the local netdb, or null when absent
      sameIP - true if the peer shares a /16, port, or family with an already-accepted candidate
      now - the current time in ms for age comparisons
      enforceHeard - true after the startup grace period, to reject peers heard about less than 45 minutes ago
      maxFailRate - failure rate ceiling from computeMaxFailRate(long)
      Returns:
      the peer's class: GOOD, OK, or BAD
    • classifyFloodfillPeerForDisplay

      public FloodfillPeerSelector.PeerClass classifyFloodfillPeerForDisplay(Hash entry, RouterInfo info, long now)
      Classify a floodfill peer for display purposes. Uses the same logic as the internal selection classifier but without same-IP tracking.
      Parameters:
      entry - the peer hash
      info - the peer's RouterInfo, may be null
      now - current time in milliseconds
      Returns:
      GOOD, OK, or BAD
    • banAndDisconnect

      private void banAndDisconnect(Hash entry, RouterInfo info, String banReason, String logReason, String disconnectReason, long banDuration, long logDuration, long now)
      Ban, log, and disconnect from a floodfill peer, with separate texts for the banlist, the ban log, and the disconnect reason.
      Parameters:
      entry - the peer hash
      info - the peer's RouterInfo
      banReason - the reason recorded in the banlist
      logReason - the reason recorded in the ban log
      disconnectReason - the reason passed to forceDisconnect
      banDuration - the ban length in ms
      logDuration - the ban log length in ms
      now - the current time in ms
    • logSelectionResults

      private void logSelectionResults(List<Hash> good, List<Hash> ok, List<Hash> bad)
      Log the classification results at debug level.
      Parameters:
      good - hashes classified GOOD, listed first
      ok - hashes classified OK
      bad - hashes classified BAD, listed last
    • selectNearest

      List<Hash> selectNearest(Hash key, int maxNumRouters, Set<Hash> peersToIgnore, KBucketSet<Hash> kbuckets)
      Floodfill peers only. Used only by HandleDatabaseLookupMessageJob to populate the DSRM. UNLESS peersToIgnore contains Hash.FAKE_HASH (all zeros), in which case this is an exploratory lookup, and the response should not include floodfills. List MAY INCLUDE our own router - add to peersToIgnore if you don't want.
      Specified by:
      selectNearest in class PeerSelector
      Parameters:
      key - the original key (NOT the routing key)
      maxNumRouters - the most hashes to return, zero or less meaning no limit
      peersToIgnore - can be null
      kbuckets - the routing table to select from
      Returns:
      List of Hash for the peers selected, ordered
    • getIPFromRouterInfo

      private String getIPFromRouterInfo(RouterInfo ri)
      Extract IP:port from RouterInfo.
      Parameters:
      ri - the router info to read addresses from, may be null
      Returns:
      host:port of the first address with both set, or null if ri is null or no address qualifies