A Python port of the Invisible Internet Project (I2P)
at main 71 lines 1.9 kB view raw
1"""NetDB lookup service — query peers for RouterInfo/LeaseSet.""" 2 3import enum 4 5 6def _xor_distance(a: bytes, b: bytes) -> bytes: 7 return bytes(x ^ y for x, y in zip(a, b)) 8 9 10class LookupState(enum.Enum): 11 NEW = 0 12 QUERYING = 1 13 FOUND = 2 14 NOT_FOUND = 3 15 FAILED = 4 16 17 18class LookupRequest: 19 """A single lookup request for a key in the NetDB.""" 20 21 def __init__(self, key: bytes, timeout_ms: int = 10000): 22 self.key = key 23 self.timeout_ms = timeout_ms 24 self.state = LookupState.NEW 25 self.result: bytes | None = None 26 self.error: str | None = None 27 self.queried_peers: set[bytes] = set() 28 29 def start(self): 30 self.state = LookupState.QUERYING 31 32 def set_found(self, data: bytes): 33 self.result = data 34 self.state = LookupState.FOUND 35 36 def set_not_found(self): 37 self.state = LookupState.NOT_FOUND 38 39 def set_failed(self, error: str): 40 self.error = error 41 self.state = LookupState.FAILED 42 43 def add_queried_peer(self, peer_hash: bytes): 44 self.queried_peers.add(peer_hash) 45 46 def already_queried(self, peer_hash: bytes) -> bool: 47 return peer_hash in self.queried_peers 48 49 50class FloodfillSet: 51 """Set of known floodfill routers.""" 52 53 def __init__(self): 54 self._floodfills: dict[bytes, bytes | None] = {} 55 56 def add(self, router_hash: bytes, router_info: bytes | None = None): 57 self._floodfills[router_hash] = router_info 58 59 def remove(self, router_hash: bytes): 60 self._floodfills.pop(router_hash, None) 61 62 def is_floodfill(self, router_hash: bytes) -> bool: 63 return router_hash in self._floodfills 64 65 def count(self) -> int: 66 return len(self._floodfills) 67 68 def closest(self, target: bytes, n: int) -> list[bytes]: 69 hashes = list(self._floodfills.keys()) 70 hashes.sort(key=lambda h: _xor_distance(h, target)) 71 return hashes[:n]