diff --git a/peer/README.md b/peer/README.md new file mode 100644 index 0000000..8a4ba3f --- /dev/null +++ b/peer/README.md @@ -0,0 +1,3 @@ +# PeerStore + +The PeerStore contains a mapping of peer IDs to PeerData objects. Each PeerData object represents a peer, and each PeerData contains a collection of protocols, addresses, and a mapping of metadata. PeerStore implements the IPeerStore (peer protocols), IAddrBook (address book), and IPeerMetadata (peer metadata) interfaces, which allows the peer store to effectively function as a dictionary for peer ID to protocol, address, and metadata. diff --git a/peer/addrbook_interface.py b/peer/addrbook_interface.py new file mode 100644 index 0000000..c27bd67 --- /dev/null +++ b/peer/addrbook_interface.py @@ -0,0 +1,52 @@ +from abc import ABC, abstractmethod + +class IAddrBook(ABC): + + def __init__(self, context): + self.context = context + + @abstractmethod + def add_addr(self, peer_id, addr, ttl): + """ + Calls add_addrs(peer_id, [addr], ttl) + :param peer_id: the peer to add address for + :param addr: multiaddress of the peer + :param ttl: time-to-live for the address (after this time, address is no longer valid) + """ + pass + + @abstractmethod + def add_addrs(self, peer_id, addrs, ttl): + """ + Adds addresses for a given peer all with the same time-to-live. If one of the + addresses already exists for the peer and has a longer TTL, no operation should take place. + If one of the addresses exists with a shorter TTL, extend the TTL to equal param ttl. + :param peer_id: the peer to add address for + :param addr: multiaddresses of the peer + :param ttl: time-to-live for the address (after this time, address is no longer valid + """ + pass + + @abstractmethod + def addrs(self, peer_id): + """ + :param peer_id: peer to get addresses of + :return: all known (and valid) addresses for the given peer + """ + pass + + @abstractmethod + def clear_addrs(self, peer_id): + """ + Removes all previously stored addresses + :param peer_id: peer to remove addresses of + """ + pass + + @abstractmethod + def peers_with_addrs(self): + """ + :return: all of the peer IDs stored with addresses + """ + pass + \ No newline at end of file diff --git a/peer/peer.py b/peer/peer.py deleted file mode 100644 index e69de29..0000000 diff --git a/peer/peerdata.py b/peer/peerdata.py new file mode 100644 index 0000000..d1bcf6f --- /dev/null +++ b/peer/peerdata.py @@ -0,0 +1,31 @@ +from .peerdata_interface import IPeerData + +class PeerData(IPeerData): + + def __init__(self): + self.metadata = {} + self.protocols = [] + self.addrs = [] + + def get_protocols(self): + return self.protocols + + def add_protocols(self, protocols): + self.protocols.extend(protocols) + + def add_addrs(self, addrs): + self.addrs.extend(addrs) + + def get_addrs(self): + return self.addrs + + def clear_addrs(self): + self.addrs = [] + + def put_metadata(self, key, val): + self.metadata[key] = val + + def get_metadata(self, key): + if key in self.metadata: + return self.metadata[key], None + return None, "key not found" diff --git a/peer/peerdata_interface.py b/peer/peerdata_interface.py new file mode 100644 index 0000000..f7e8b5d --- /dev/null +++ b/peer/peerdata_interface.py @@ -0,0 +1,47 @@ +from abc import ABC, abstractmethod + +class IPeerData(ABC): + + """ + :return: all protocols associated with given peer + """ + def get_protocols(self): + pass + + """ + :param protocols: protocols to add + """ + def add_protocols(self, protocols): + pass + + """ + :param addrs: multiaddresses to add + """ + def add_addrs(self, addrs): + pass + + """ + :return: all multiaddresses + """ + def get_addrs(self): + pass + + """ + Clear all addresses + """ + def clear_addrs(self): + pass + + """ + :param key: key in KV pair + :param val: val to associate with key + """ + def put_metadata(self, key, val): + pass + + """ + :param key: key in KV pair + :return: val for key, error (only defined if key not found) + """ + def get_metadata(self, key): + pass diff --git a/peer/peermetadata_interface.py b/peer/peermetadata_interface.py new file mode 100644 index 0000000..6fda82f --- /dev/null +++ b/peer/peermetadata_interface.py @@ -0,0 +1,26 @@ +from abc import ABC, abstractmethod + +class IPeerMetadata(ABC): + + def __init__(self, context): + self.context = context + + @abstractmethod + def get(self, peer_id, key): + """ + :param peer_id: peer ID to lookup key for + :param key: key to look up + :return: value at key for given peer, error + """ + pass + + @abstractmethod + def put(self, peer_id, key, val): + """ + :param peer_id: peer ID to lookup key for + :param key: key to associate with peer + :param val: value to associated with key + :return: error + """ + pass + \ No newline at end of file diff --git a/peer/peerstore.py b/peer/peerstore.py index e69de29..3922b7f 100644 --- a/peer/peerstore.py +++ b/peer/peerstore.py @@ -0,0 +1,82 @@ +from .peerstore_interface import IPeerStore +from .peerdata import PeerData + +class PeerStore(IPeerStore): + + def __init__(self, context): + IPeerStore.__init__(self, context) + self.peer_map = {} + + def __create_or_get_peer(self, peer_id): + """ + Returns the peer data for peer_id or creates a new + peer data (and stores it in peer_map) if peer + data for peer_id does not yet exist + :param peer_id: peer ID + :return: peer data + """ + if peer_id in self.peer_map: + return self.peer_map[peer_id] + data = PeerData() + self.peer_map[peer_id] = data + return self.peer_map[peer_id] + + def peer_info(self, peer_id): + if peer_id in self.peer_map: + peer = self.peer_map[peer_id] + return { + "peer_id": peer_id, + "addrs": peer.get_addrs() + } + return None + + def get_protocols(self, peer_id): + if peer_id in self.peer_map: + return self.peer_map[peer_id].get_protocols(), None + return None, peer_id + " not found" + + def add_protocols(self, peer_id, protocols): + peer = self.__create_or_get_peer(peer_id) + peer.add_protocols(protocols) + + def peers(self): + return self.peer_map.keys() + + def get(self, peer_id, key): + if peer_id in self.peer_map: + val, error = self.peer_map[peer_id].get_metadata(key) + return val, error + return None, peer_id + " not found" + + def put(self, peer_id, key, val): + # <> + # This can output an error, not sure what the possible errors are + peer = self.__create_or_get_peer(peer_id) + peer.put_metadata(key, val) + + def add_addr(self, peer_id, addr, ttl): + self.add_addrs(self, peer_id, [addr]) + + def add_addrs(self, peer_id, addrs, ttl): + # Ignore ttl for now + peer = self.__create_or_get_peer(peer_id) + peer.add_addrs(addrs) + + def addrs(self, peer_id): + if peer_id in self.peer_map: + return self.peer_map[peer_id].get_addrs(), None + return None, peer_id + " not found" + + def clear_addrs(self, peer_id): + # Only clear addresses if the peer is in peer map + if peer_id in self.peer_map: + self.peer_map[peer_id].clear_addrs() + + def peers_with_addrs(self): + # Add all peers with addrs at least 1 to output + output = [] + + for key in self.peer_map: + if len(self.peer_map[key].get_addrs()) >= 1: + output.append(key) + return output diff --git a/peer/peerstore_interface.py b/peer/peerstore_interface.py new file mode 100644 index 0000000..67f75c1 --- /dev/null +++ b/peer/peerstore_interface.py @@ -0,0 +1,50 @@ +from abc import ABC, abstractmethod +from .addrbook_interface import IAddrBook +from .peermetadata_interface import IPeerMetadata + +class IPeerStore(ABC, IAddrBook, IPeerMetadata): + + def __init__(self, context): + IPeerMetadata.__init__(self, context) + IAddrBook.__init__(self, context) + + @abstractmethod + def peer_info(self, peer_id): + """ + :param peer_id: peer ID to get info for + :return: peer info object + """ + pass + + @abstractmethod + def get_protocols(self, peer_id): + """ + :param peer_id: peer ID to get protocols for + :return: protocols (as strings), error + """ + pass + + @abstractmethod + def add_protocols(self, peer_id, protocols): + """ + :param peer_id: peer ID to add protocols for + :param protocols: protocols to add + :return: error + """ + pass + + @abstractmethod + def set_protocols(self, peer_id, protocols): + """ + :param peer_id: peer ID to set protocols for + :param protocols: protocols to set + :return: error + """ + pass + + @abstractmethod + def peers(self): + """ + :return: all of the peer IDs stored in peer store + """ + pass