A Python port of the Invisible Internet Project (I2P)
at main 88 lines 2.9 kB view raw
1"""OutboundCache — per-destination send state caching. 2 3Ported from net.i2p.router.message.OutboundClientMessageOneShotJob 4(internal cache fields). 5 6Caches lease, tunnel, and reply state for destination pairs 7to avoid redundant lookups and rate-limit ACK requests. 8""" 9 10from __future__ import annotations 11 12from dataclasses import dataclass 13 14 15@dataclass(frozen=True) 16class HashPair: 17 """Immutable pair of (from_hash, to_hash) used as cache key.""" 18 from_hash: bytes 19 to_hash: bytes 20 21 def __eq__(self, other: object) -> bool: 22 if not isinstance(other, HashPair): 23 return NotImplemented 24 return self.from_hash == other.from_hash and self.to_hash == other.to_hash 25 26 def __hash__(self) -> int: 27 return hash((self.from_hash, self.to_hash)) 28 29 30class OutboundCache: 31 """Per-destination send state cache. 32 33 Caches recently used leases, tunnels, and ACK timing 34 to optimize outbound message routing. 35 """ 36 37 REPLY_REQUEST_INTERVAL_MS = 60_000 # min 60s between ACK requests 38 39 def __init__(self) -> None: 40 self._lease_cache: dict[HashPair, object] = {} 41 self._tunnel_cache: dict[HashPair, object] = {} 42 self._leaseset_acked: dict[HashPair, int] = {} # pair -> ack timestamp 43 self._last_reply_request: dict[HashPair, int] = {} # pair -> request timestamp 44 45 # --- Lease caching --- 46 47 def get_cached_lease(self, pair: HashPair) -> object | None: 48 return self._lease_cache.get(pair) 49 50 def set_cached_lease(self, pair: HashPair, lease: object) -> None: 51 self._lease_cache[pair] = lease 52 53 def clear_cached_lease(self, pair: HashPair) -> None: 54 self._lease_cache.pop(pair, None) 55 56 # --- Tunnel caching --- 57 58 def get_cached_tunnel(self, pair: HashPair) -> object | None: 59 return self._tunnel_cache.get(pair) 60 61 def set_cached_tunnel(self, pair: HashPair, tunnel: object) -> None: 62 self._tunnel_cache[pair] = tunnel 63 64 def clear_cached_tunnel(self, pair: HashPair) -> None: 65 self._tunnel_cache.pop(pair, None) 66 67 # --- Reply request rate limiting --- 68 69 def should_request_reply(self, pair: HashPair, now_ms: int) -> bool: 70 """True if enough time has passed since last reply request.""" 71 last = self._last_reply_request.get(pair) 72 if last is None: 73 return True 74 return (now_ms - last) >= self.REPLY_REQUEST_INTERVAL_MS 75 76 def record_reply_request(self, pair: HashPair, now_ms: int) -> None: 77 self._last_reply_request[pair] = now_ms 78 79 # --- LeaseSet ACK tracking --- 80 81 def is_leaseset_acked(self, pair: HashPair) -> bool: 82 return pair in self._leaseset_acked 83 84 def record_leaseset_ack(self, pair: HashPair, now_ms: int) -> None: 85 self._leaseset_acked[pair] = now_ms 86 87 def clear_leaseset_ack(self, pair: HashPair) -> None: 88 self._leaseset_acked.pop(pair, None)