A Python port of the Invisible Internet Project (I2P)
at main 80 lines 2.8 kB view raw
1"""Peer selector for tunnel building — selects peers by tier and generates hop configs. 2 3Ported from net.i2p.router.tunnel.pool.TunnelPeerSelector. 4""" 5 6from __future__ import annotations 7 8import os 9import struct 10from typing import TYPE_CHECKING 11 12from i2p_peer.hop_config import TunnelHopConfig 13from i2p_peer.organizer import PeerTier, _TIER_ORDER 14 15if TYPE_CHECKING: 16 from i2p_peer.organizer import ProfileOrganizer 17 18 19class PeerSelector: 20 """Selects peers for tunnel building based on profile scores.""" 21 22 def __init__(self, organizer: ProfileOrganizer): 23 self._organizer = organizer 24 25 def select_peers(self, count: int, exclude: set[bytes] | None = None) -> list[bytes]: 26 """Select count peers, preferring higher-tier peers. 27 28 Selection strategy: 29 1. Walk tiers from FAST down to STANDARD, collecting eligible peers. 30 2. Exclude any in the exclude set. 31 3. Return top ``count`` peers (tier order preserved). 32 """ 33 exclude = exclude or set() 34 selected: list[bytes] = [] 35 36 # Walk tiers best-to-worst, skip FAILING and BANNED 37 eligible_tiers = [PeerTier.FAST, PeerTier.HIGH_CAPACITY, PeerTier.STANDARD] 38 for tier in eligible_tiers: 39 tier_hashes = self._organizer._tiers.get(tier, set()) 40 for h in tier_hashes: 41 if h not in exclude: 42 selected.append(h) 43 if len(selected) >= count: 44 return selected[:count] 45 46 return selected[:count] 47 48 def select_hops(self, length: int, exclude: set[bytes] | None = None) -> list[TunnelHopConfig]: 49 """Select peers and generate full hop configs for tunnel building. 50 51 - First hop is gateway (is_gateway=True) 52 - Last hop is endpoint (is_endpoint=True) 53 - A single hop is both gateway and endpoint 54 - Each hop gets random keys generated with os.urandom() 55 - receive_tunnel_id is a random positive uint32 56 """ 57 peer_hashes = self.select_peers(length, exclude=exclude) 58 if not peer_hashes: 59 return [] 60 61 hops: list[TunnelHopConfig] = [] 62 for i, peer_hash in enumerate(peer_hashes): 63 # Random positive tunnel ID (1 to 2^32-1) 64 tid = struct.unpack("!I", os.urandom(4))[0] 65 if tid == 0: 66 tid = 1 # ensure positive 67 68 hop = TunnelHopConfig( 69 peer_hash=peer_hash, 70 receive_tunnel_id=tid, 71 layer_key=os.urandom(32), 72 iv_key=os.urandom(32), 73 reply_key=os.urandom(32), 74 reply_iv=os.urandom(16), 75 is_gateway=(i == 0), 76 is_endpoint=(i == len(peer_hashes) - 1), 77 ) 78 hops.append(hop) 79 80 return hops