diff --git a/src/cutecoin/core/community.py b/src/cutecoin/core/community.py index 78c29b399663e5b10914680d18394dc445a2b9bf..90eef4b0cb620bf56191c845b55dc49be0fc49f2 100644 --- a/src/cutecoin/core/community.py +++ b/src/cutecoin/core/community.py @@ -169,6 +169,10 @@ class Community(object): def nodes(self): return self._network.all_nodes + @property + def network(self): + return self._network + def add_peer(self, peer): self._network.add_node(Node.from_peer(peer)) diff --git a/src/cutecoin/core/net/network.py b/src/cutecoin/core/net/network.py index 22d31623caee14084f25bb4ab5f3c4bfbedcda73..44fc92ce42da0e3d05959a994b76aa87c5b5322b 100644 --- a/src/cutecoin/core/net/network.py +++ b/src/cutecoin/core/net/network.py @@ -64,7 +64,6 @@ class Network(QObject): def __del__(self): self.must_crawl = False - @pyqtSlot() def stop_crawling(self): self.must_crawl = False @@ -80,19 +79,17 @@ class Network(QObject): self._nodes.append(node) node.changed.connect(self.nodes_changed) - @pyqtSlot() - def perpetual_crawling(self): + def start_perpetual_crawling(self): self.must_crawl = True while self.must_crawl: self.nodes = self.crawling(interval=10) - self.nodes_changed.disconnect() for n in self._nodes: n.changed.connect(self.nodes_changed) def crawling(self, interval=0): nodes = [] traversed_pubkeys = [] - for n in self._nodes: + for n in self.nodes: logging.debug(traversed_pubkeys) logging.debug("Peering : next to read : {0} : {1}".format(n.pubkey, (n.pubkey not in traversed_pubkeys))) @@ -103,7 +100,7 @@ class Network(QObject): block_max = max([n.block for n in nodes]) for node in [n for n in nodes if n.state == Node.ONLINE]: - node.check_sync(block_max) + node.check_sync(self.currency, block_max) #TODO: Offline nodes for too long have to be removed #TODO: Corrupted nodes should maybe be removed faster ? diff --git a/src/cutecoin/core/net/node.py b/src/cutecoin/core/net/node.py index 2d53f67c161b24be1f964bfa5f3f85694afd4685..e484e97a61403630d605b61aa328c62fb9fdf006 100644 --- a/src/cutecoin/core/net/node.py +++ b/src/cutecoin/core/net/node.py @@ -34,6 +34,7 @@ class Node(QObject): self._pubkey = pubkey self._block = block self._state = state + self._neighbours = [] @classmethod def from_peer(cls, currency, peer): @@ -67,6 +68,10 @@ class Node(QObject): def state(self): return self._state + @property + def neighbours(self): + return self._neighbours + def check_sync(self, currency, block): if self._block < block: self._state = Node.DESYNCED @@ -74,9 +79,17 @@ class Node(QObject): self._state = Node.ONLINE def refresh_state(self, currency): + emit_change = False try: informations = bma.network.Peering(self.endpoint.conn_handler()).get() block = bma.blockchain.Current(self.endpoint.conn_handler()).get() + peers_data = bma.network.peering.Peers(self.endpoint.conn_handler()).get() + neighbours = [] + for p in peers_data: + peer = Peer.from_signed_raw("{0}{1}\n".format(p['value']['raw'], + p['value']['signature'])) + neighbours.append(peer.endpoints) + block_number = block["number"] node_pubkey = informations["pubkey"] node_currency = informations["currency"] @@ -85,14 +98,34 @@ class Node(QObject): block_number = 0 except RequestException: self._state = Node.OFFLINE + emit_change = True if node_currency != currency: self.state = Node.CORRUPTED + emit_change = True + + if block_number != self._block: + self._block = block_number + emit_change = True - self._block = block_number - self._pubkey = node_pubkey + if node_pubkey != self._pubkey: + self._pubkey = node_pubkey + emit_change = True - def peering_traversal(self, currency, found_nodes, traversed_pubkeys, interval): + new_inlines = [e.inline() for e in [n for n in self._neighbours]] + last_inlines = [e.inline() for e in [n for n in self._neighbours]] + + hash_new_neighbours = hash(tuple(frozenset(sorted(new_inlines)))) + hash_last_neighbours = hash(tuple(frozenset(sorted(last_inlines)))) + if hash_new_neighbours != hash_last_neighbours: + self._neighbours = neighbours + emit_change = True + + if emit_change: + self.changed.emit() + + def peering_traversal(self, currency, found_nodes, + traversed_pubkeys, interval): logging.debug("Read {0} peering".format(self.pubkey)) traversed_pubkeys.append(self.pubkey) self.refresh_state(currency) @@ -100,16 +133,18 @@ class Node(QObject): found_nodes.append(self) try: - next_peers = bma.network.peering.Peers(self.endpoint.conn_handler()).get() - for p in next_peers: - next_peer = Peer.from_signed_raw("{0}{1}\n".format(p['value']['raw'], - p['value']['signature'])) + for n in self.neighbours: + e = next((e for e in self._endpoints if type(e) is BMAEndpoint)) + peering = bma.network.Peering(self.endpoint.conn_handler()).get() + peer = Peer.from_signed_raw("{0}{1}\n".format(peering['raw'], + peering['signature'])) + node = Node.from_peer(peer) logging.debug(traversed_pubkeys) - logging.debug("Traversing : next to read : {0} : {1}".format(next_peer.pubkey, - (next_peer.pubkey not in traversed_pubkeys))) - next_node = Node.from_peer(next_peer) - if next_node.pubkey not in traversed_pubkeys: - next_node.peering_traversal(currency, found_nodes, traversed_pubkeys) + logging.debug("Traversing : next to read : {0} : {1}".format(node.pubkey, + (node.pubkey not in traversed_pubkeys))) + if node.pubkey not in traversed_pubkeys: + node.peering_traversal(currency, found_nodes, + traversed_pubkeys, interval) time.sleep(interval) except RequestException as e: self._state = Node.OFFLINE diff --git a/src/cutecoin/core/watchers/network.py b/src/cutecoin/core/watchers/network.py new file mode 100644 index 0000000000000000000000000000000000000000..39d55edccb40f829cbd24ba01bbfc607fae672fb --- /dev/null +++ b/src/cutecoin/core/watchers/network.py @@ -0,0 +1,27 @@ +''' +Created on 27 févr. 2015 + +@author: inso +''' + +from PyQt5.QtCore import QObject, pyqtSlot + + +class NetworkWatcher(QObject): + ''' + This will crawl the network to always + have up to date informations about the nodes + ''' + + def __init__(self, community): + super().__init__() + self.community = community + + @pyqtSlot() + def watch(self): + self.community.network.moveToThread(self.thread()) + self.community.network.start_perpetual_crawling() + + @pyqtSlot() + def stop(self): + self.community.network.stop_crawling() diff --git a/src/cutecoin/core/watchers/nodes.py b/src/cutecoin/core/watchers/nodes.py deleted file mode 100644 index f7f61d3b3a4eb4f974bbf17f238c243f3e89dc70..0000000000000000000000000000000000000000 --- a/src/cutecoin/core/watchers/nodes.py +++ /dev/null @@ -1,17 +0,0 @@ -''' -Created on 27 févr. 2015 - -@author: inso -''' - - -class NodesWatcher(object): - ''' - This will crawl the network to always - have up to date informations about the nodes - ''' - - def __init__(self): - ''' - Constructor - ''' diff --git a/src/cutecoin/gui/network_tab.py b/src/cutecoin/gui/network_tab.py index 6783e33be18411fe8ac94b661eb95854c7b112af..aa46dbe1a623c7bd0398b540c6186b257c4464dd 100644 --- a/src/cutecoin/gui/network_tab.py +++ b/src/cutecoin/gui/network_tab.py @@ -5,6 +5,8 @@ Created on 20 févr. 2015 ''' from PyQt5.QtWidgets import QWidget +from PyQt5.QtCore import QModelIndex, QThread +from ..core.watchers.network import NetworkWatcher from ..models.network import NetworkTableModel, NetworkFilterProxyModel from ..gen_resources.network_tab_uic import Ui_NetworkTabWidget @@ -25,4 +27,15 @@ class NetworkTabWidget(QWidget, Ui_NetworkTabWidget): proxy.setSourceModel(model) self.table_network.setModel(proxy) + self.network_watcher = NetworkWatcher(community) + self.watcher_thread = QThread() + self.network_watcher.moveToThread(self.watcher_thread) + self.watcher_thread.started.connect(self.network_watcher.watch) + self.watcher_thread.start() + + community.network.nodes_changed.connect(self.refresh_nodes) + + def refresh_nodes(self): + self.table_network.sourceModel.dataChanged.emit(QModelIndex(), QModelIndex()) +