A Python port of the Invisible Internet Project (I2P)
at main 102 lines 3.1 kB view raw
1"""Peer history tracking. 2 3Ported from net.i2p.router.peermanager.DBHistory / TunnelHistory. 4""" 5 6import time 7from dataclasses import dataclass, field 8 9 10@dataclass 11class HistoryEntry: 12 timestamp: float 13 success: bool 14 latency_ms: float = 0.0 15 detail: str = "" 16 17 18class DBHistory: 19 """Historical record of NetDB operations with a peer.""" 20 21 def __init__(self, max_entries: int = 100): 22 self._entries: list[HistoryEntry] = [] 23 self._max_entries = max_entries 24 25 def _add_entry(self, entry: HistoryEntry) -> None: 26 self._entries.append(entry) 27 if len(self._entries) > self._max_entries: 28 self._entries = self._entries[-self._max_entries:] 29 30 def record_store(self, success: bool, latency_ms: float = 0.0) -> None: 31 self._add_entry(HistoryEntry( 32 timestamp=time.time(), 33 success=success, 34 latency_ms=latency_ms, 35 detail="store", 36 )) 37 38 def record_lookup(self, success: bool, latency_ms: float = 0.0) -> None: 39 self._add_entry(HistoryEntry( 40 timestamp=time.time(), 41 success=success, 42 latency_ms=latency_ms, 43 detail="lookup", 44 )) 45 46 @property 47 def success_rate(self) -> float: 48 if not self._entries: 49 return 0.0 50 successes = sum(1 for e in self._entries if e.success) 51 return successes / len(self._entries) 52 53 @property 54 def average_latency(self) -> float: 55 if not self._entries: 56 return 0.0 57 return sum(e.latency_ms for e in self._entries) / len(self._entries) 58 59 @property 60 def recent_entries(self) -> list[HistoryEntry]: 61 return list(self._entries) 62 63 64class TunnelHistory: 65 """Historical record of tunnel participation.""" 66 67 def __init__(self, max_entries: int = 100): 68 self._entries: list[HistoryEntry] = [] 69 self._max_entries = max_entries 70 71 def _add_entry(self, entry: HistoryEntry) -> None: 72 self._entries.append(entry) 73 if len(self._entries) > self._max_entries: 74 self._entries = self._entries[-self._max_entries:] 75 76 def record_build(self, success: bool) -> None: 77 self._add_entry(HistoryEntry( 78 timestamp=time.time(), 79 success=success, 80 detail="build", 81 )) 82 83 def record_participation(self, success: bool, data_transferred: int = 0) -> None: 84 self._add_entry(HistoryEntry( 85 timestamp=time.time(), 86 success=success, 87 detail=f"participate:{data_transferred}", 88 )) 89 90 @property 91 def build_success_rate(self) -> float: 92 builds = [e for e in self._entries if e.detail == "build"] 93 if not builds: 94 return 0.0 95 return sum(1 for e in builds if e.success) / len(builds) 96 97 @property 98 def participation_rate(self) -> float: 99 participations = [e for e in self._entries if e.detail.startswith("participate")] 100 if not participations: 101 return 0.0 102 return sum(1 for e in participations if e.success) / len(participations)