import random import time from typing import Set, List, Optional from dataclasses import dataclass from chia.types.peer_info import PeerInfo from chia.util.ints import uint64, uint16 @dataclass(frozen=False) class VettedPeer: host: str port: uint16 # 0 means we have not attempted to vet this peer yet # a negative number means we have failed that many vetting attempts in a row # a positive number means we have successfully vetted the peer this many # times in a row vetted: int = 0 # the timestamp of the last *successful* vetting of this peer vetted_timestamp: uint64 = uint64(0) # the last time we attempted to vet this peer, or 0 if we haven't tried yet # we set this regardless of whether the vetting is successful or not last_attempt: uint64 = uint64(0) time_added: uint64 = uint64(0) def __init__(self, h: str, p: uint16): self.host = h self.port = p def __eq__(self, rhs): return self.host == rhs.host and self.port == rhs.port def __hash__(self): return hash((self.host, self.port)) class IntroducerPeers: """ Has the list of known full node peers that are already connected or may be connected to, and the time that they were last added. """ def __init__(self) -> None: self._peers: Set[VettedPeer] = set() def add(self, peer: Optional[PeerInfo]) -> bool: if peer is None or not peer.port: return False p = VettedPeer(peer.host, peer.port) p.time_added = uint64(int(time.time())) if p in self._peers: return True self._peers.add(p) return True def remove(self, peer: Optional[VettedPeer]) -> bool: if peer is None or not peer.port: return False try: self._peers.remove(peer) return True except ValueError: return False def get_peers(self, max_peers: int = 0, randomize: bool = False, recent_threshold=9999999) -> List[VettedPeer]: target_peers = [peer for peer in self._peers if time.time() - peer.time_added < recent_threshold] if not max_peers or max_peers > len(target_peers): max_peers = len(target_peers) if randomize: return random.sample(target_peers, max_peers) else: return target_peers[:max_peers]