From 98eb1036c1ee9c0f64500e0a53b0b5139981c234 Mon Sep 17 00:00:00 2001
From: Inso <insomniak.fr@gmail.com>
Date: Thu, 5 Mar 2015 19:50:41 +0100
Subject: [PATCH] Network refactoring

Nodes now remember there currency
Community creation depends on node creation
---
 src/cutecoin/core/community.py            |  5 +-
 src/cutecoin/core/net/network.py          | 16 +++---
 src/cutecoin/core/net/node.py             | 60 +++++++++++++++++------
 src/cutecoin/gui/community_tab.py         |  2 +-
 src/cutecoin/gui/mainwindow.py            |  4 +-
 src/cutecoin/gui/process_cfg_account.py   |  9 +---
 src/cutecoin/gui/process_cfg_community.py | 51 ++++++-------------
 src/cutecoin/tools/exceptions.py          | 13 +++++
 8 files changed, 90 insertions(+), 70 deletions(-)

diff --git a/src/cutecoin/core/community.py b/src/cutecoin/core/community.py
index 018f65f1..12def58c 100644
--- a/src/cutecoin/core/community.py
+++ b/src/cutecoin/core/community.py
@@ -85,8 +85,9 @@ class Community(object):
         self._cache.refresh()
 
     @classmethod
-    def create(cls, currency, peer):
-        community = cls(currency, [peer])
+    def create(cls, node):
+        network = Network.create(node)
+        community = cls(node.currency, network)
         logging.debug("Creating community")
         return community
 
diff --git a/src/cutecoin/core/net/network.py b/src/cutecoin/core/net/network.py
index 665fe9d6..68a41729 100644
--- a/src/cutecoin/core/net/network.py
+++ b/src/cutecoin/core/net/network.py
@@ -42,23 +42,23 @@ class Network(QObject):
             logging.debug("Loading : {:}".format(data['pubkey']))
         block_max = max([n.block for n in nodes])
         for node in nodes:
-            node.check_sync(currency, block_max)
+            node.check_sync(block_max)
         return cls(currency, nodes)
 
     @classmethod
-    def create(cls, currency, node):
+    def create(cls, node):
         nodes = [node]
-        network = cls(currency, nodes)
-        nodes = network.crawling
+        network = cls(node.currency, nodes)
+        nodes = network.crawling()
         block_max = max([n.block for n in nodes])
         for node in nodes:
-            node.check_sync(currency, block_max)
+            node.check_sync(block_max)
         network._nodes = nodes
         return network
 
     def jsonify(self):
         data = []
-        for node in self.nodes:
+        for node in self._nodes:
             data.append(node.jsonify())
         return data
 
@@ -105,13 +105,13 @@ class Network(QObject):
             logging.debug("Peering : next to read : {0} : {1}".format(n.pubkey,
                           (n.pubkey not in traversed_pubkeys)))
             if n.pubkey not in traversed_pubkeys:
-                n.peering_traversal(self.currency, nodes,
+                n.peering_traversal(nodes,
                                     traversed_pubkeys, interval)
                 time.sleep(interval)
 
         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(self.currency, block_max)
+            node.check_sync(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 448e14a8..5c84c5f0 100644
--- a/src/cutecoin/core/net/node.py
+++ b/src/cutecoin/core/net/node.py
@@ -6,7 +6,9 @@ Created on 21 févr. 2015
 
 from ucoinpy.documents.peer import Peer, BMAEndpoint, Endpoint
 from ucoinpy.api import bma
+from ucoinpy.api.bma import ConnectionHandler
 from requests.exceptions import RequestException
+from ...tools.exceptions import InvalidNodeCurrency
 import logging
 import time
 
@@ -25,7 +27,7 @@ class Node(QObject):
 
     changed = pyqtSignal()
 
-    def __init__(self, endpoints, pubkey, block, state):
+    def __init__(self, currency, endpoints, pubkey, block, state):
         '''
         Constructor
         '''
@@ -35,11 +37,31 @@ class Node(QObject):
         self._block = block
         self._state = state
         self._neighbours = []
+        self._currency = currency
+
+    @classmethod
+    def from_address(cls, currency, address, port):
+        peer_data = bma.network.Peering(ConnectionHandler(address, port)).get()
+
+        peer = Peer.from_signed_raw("{0}{1}\n".format(peer_data['raw'],
+                                                  peer_data['signature']))
+
+        if currency is not None:
+            if peer.currency != currency:
+                raise InvalidNodeCurrency(peer.currency, currency)
+
+        node = cls(peer.currency, peer.endpoints, peer.pubkey, 0, Node.ONLINE)
+        node.refresh_state()
+        return node
 
     @classmethod
     def from_peer(cls, currency, peer):
-        node = cls(peer.endpoints, "", 0, Node.ONLINE)
-        node.refresh_state(currency)
+        if currency is not None:
+            if peer.currency != currency:
+                raise InvalidNodeCurrency(peer.currency, currency)
+
+        node = cls(peer.currency, peer.endpoints, "", 0, Node.ONLINE)
+        node.refresh_state()
         return node
 
     @classmethod
@@ -48,12 +70,15 @@ class Node(QObject):
         for endpoint_data in data['endpoints']:
             endpoints.append(Endpoint.from_inline(endpoint_data))
 
-        node = cls(endpoints, "", 0, Node.ONLINE)
-        node.refresh_state(currency)
+        currency = data['currency']
+
+        node = cls(currency, endpoints, "", 0, Node.ONLINE)
+        node.refresh_state()
         return node
 
     def jsonify(self):
-        data = {'pubkey': self._pubkey}
+        data = {'pubkey': self._pubkey,
+                'currency': self._currency}
         endpoints = []
         for e in self._endpoints:
             endpoints.append(e.inline())
@@ -76,17 +101,21 @@ class Node(QObject):
     def state(self):
         return self._state
 
+    @property
+    def currency(self):
+        return self._currency
+
     @property
     def neighbours(self):
         return self._neighbours
 
-    def check_sync(self, currency, block):
+    def check_sync(self, block):
         if self._block < block:
             self._state = Node.DESYNCED
         else:
             self._state = Node.ONLINE
 
-    def refresh_state(self, currency):
+    def refresh_state(self):
         emit_change = False
         try:
             informations = bma.network.Peering(self.endpoint.conn_handler()).get()
@@ -108,7 +137,7 @@ class Node(QObject):
             self._state = Node.OFFLINE
             emit_change = True
 
-        if node_currency != currency:
+        if node_currency != self._currency:
             self.state = Node.CORRUPTED
             emit_change = True
 
@@ -133,26 +162,27 @@ class Node(QObject):
         if emit_change:
             self.changed.emit()
 
-    def peering_traversal(self, currency, found_nodes,
+    def peering_traversal(self, found_nodes,
                           traversed_pubkeys, interval):
         logging.debug("Read {0} peering".format(self.pubkey))
         traversed_pubkeys.append(self.pubkey)
-        self.refresh_state(currency)
+        self.refresh_state()
         if self.pubkey not in [n.pubkey for n in found_nodes]:
             found_nodes.append(self)
 
         try:
+            logging.debug(self.neighbours)
             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()
+                e = next(e for e in n if type(e) is BMAEndpoint)
+                peering = bma.network.Peering(e.conn_handler()).get()
                 peer = Peer.from_signed_raw("{0}{1}\n".format(peering['raw'],
                                                             peering['signature']))
-                node = Node.from_peer(currency, peer)
+                node = Node.from_peer(self._currency, peer)
                 logging.debug(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,
+                    node.peering_traversal(found_nodes,
                                         traversed_pubkeys, interval)
                     time.sleep(interval)
         except RequestException as e:
diff --git a/src/cutecoin/gui/community_tab.py b/src/cutecoin/gui/community_tab.py
index 3016af98..ade37b23 100644
--- a/src/cutecoin/gui/community_tab.py
+++ b/src/cutecoin/gui/community_tab.py
@@ -57,7 +57,7 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget):
         model = self.table_community_members.model()
         if index.row() < model.rowCount():
             source_index = model.mapToSource(index)
-            pubkey_col = model.sourceModel().columns.index('Pubkey')
+            pubkey_col = model.sourceModel().columns_ids.index('pubkey')
             pubkey_index = model.sourceModel().index(source_index.row(),
                                                    pubkey_col)
             pubkey = model.sourceModel().data(pubkey_index, Qt.DisplayRole)
diff --git a/src/cutecoin/gui/mainwindow.py b/src/cutecoin/gui/mainwindow.py
index 90d7c29d..0f49b9f9 100644
--- a/src/cutecoin/gui/mainwindow.py
+++ b/src/cutecoin/gui/mainwindow.py
@@ -179,7 +179,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
     def open_configure_account_dialog(self):
         dialog = ProcessConfigureAccount(self.app, self.app.current_account)
         dialog.accepted.connect(self.refresh)
-        dialog.exec_()
+        result = dialog.exec_()
+        if result == QDialog.Accepted:
+            self.action_change_account(self.app.current_account.name)
 
     def open_about_popup(self):
         """
diff --git a/src/cutecoin/gui/process_cfg_account.py b/src/cutecoin/gui/process_cfg_account.py
index d61e3f43..795d1ec2 100644
--- a/src/cutecoin/gui/process_cfg_account.py
+++ b/src/cutecoin/gui/process_cfg_account.py
@@ -121,16 +121,11 @@ class StepPageCommunities(Step):
         nb_wallets = self.config_dialog.spinbox_wallets.value()
         self.config_dialog.account.set_walletpool_size(nb_wallets, password)
 
+        self.config_dialog.app.add_account(self.config_dialog.account)
         if len(self.config_dialog.app.accounts) == 1:
             self.config_dialog.app.default_account = self.config_dialog.account.name
-
-        try:
-            self.config_dialog.app.add_account(self.config_dialog.account)
-        except KeyAlreadyUsed as e:
-            QMessageBox.critical(self, "Error",
-                                 str(e), QMessageBox.Ok)
-            return
         self.config_dialog.app.save(self.config_dialog.account)
+        self.config_dialog.app.current_account = self.config_dialog.account
 
     def display_page(self):
         logging.debug("Communities DISPLAY")
diff --git a/src/cutecoin/gui/process_cfg_community.py b/src/cutecoin/gui/process_cfg_community.py
index 29a6e72b..f0356966 100644
--- a/src/cutecoin/gui/process_cfg_community.py
+++ b/src/cutecoin/gui/process_cfg_community.py
@@ -17,6 +17,7 @@ from ..gen_resources.community_cfg_uic import Ui_CommunityConfigurationDialog
 from ..models.peering import PeeringTreeModel
 from ..core.community import Community
 from ..core.person import Person
+from ..core.net.node import Node
 from ..tools.exceptions import PersonNotFoundError, NoPeerAvailable
 
 
@@ -33,6 +34,7 @@ class StepPageInit(Step):
     '''
     def __init__(self, config_dialog):
         super().__init__(config_dialog)
+        self.node = None
         logging.debug("Init")
 
     def is_valid(self):
@@ -40,38 +42,21 @@ class StepPageInit(Step):
         port = self.config_dialog.spinbox_port.value()
         logging.debug("Is valid ? ")
         try:
-            peer_data = bma.network.Peering(ConnectionHandler(server, port))
-            peer_data.get()['raw']
-        except:
-            QMessageBox.critical(self.config_dialog, "Server error",
-                              "Cannot get node peering")
-            return False
+            self.node = Node.from_address(None, server, port)
+        except Exception as e:
+            QMessageBox.critical(self.config_dialog, ":(",
+                        str(e),
+                        QMessageBox.Ok)
+
         return True
 
     def process_next(self):
         '''
         We create the community
         '''
-        server = self.config_dialog.lineedit_server.text()
-        port = self.config_dialog.spinbox_port.value()
         account = self.config_dialog.account
         logging.debug("Account : {0}".format(account))
-        try:
-            peering = bma.network.Peering(ConnectionHandler(server, port))
-            peer_data = peering.get()
-            peer = Peer.from_signed_raw("{0}{1}\n".format(peer_data['raw'],
-                                                          peer_data['signature']))
-            currency = peer.currency
-            self.config_dialog.community = Community.create(currency, peer)
-        except NoPeerAvailable:
-            QMessageBox.critical(self.config_dialog, "Server Error",
-                              "Cannot join any peer in this community.")
-            raise
-        except requests.exceptions.RequestException as e:
-            QMessageBox.critical(self.config_dialog, ":(",
-                        str(e),
-                        QMessageBox.Ok)
-            raise
+        self.config_dialog.community = Community.create(self.node)
 
     def display_page(self):
         self.config_dialog.button_previous.setEnabled(False)
@@ -168,19 +153,13 @@ class ProcessConfigureCommunity(QDialog, Ui_CommunityConfigurationDialog):
         '''
         server = self.lineedit_server.text()
         port = self.spinbox_port.value()
-        try:
-            peer_data = bma.network.Peering(ConnectionHandler(server, port)).get()
 
-            peer = Peer.from_signed_raw("{0}{1}\n".format(peer_data['raw'],
-                                                      peer_data['signature']))
-            if peer.currency == self.community.currency:
-                self.community.add_peer(peer)
-            else:
-                QMessageBox.critical(self, "Error",
-                                     "This peer doesn't use this community currency.")
-        except requests.exceptions.RequestException as e:
-            QMessageBox.critical(self, "Server error",
-                              "Cannot get node peering")
+        try:
+            node = Node.from_address(self.community.currency, server, port)
+            self.community.add_node(node)
+        except Exception as e:
+            QMessageBox.critical(self, "Error",
+                                 str(e))
         self.tree_peers.setModel(PeeringTreeModel(self.community))
 
     def showContextMenu(self, point):
diff --git a/src/cutecoin/tools/exceptions.py b/src/cutecoin/tools/exceptions.py
index d063899b..f394b397 100644
--- a/src/cutecoin/tools/exceptions.py
+++ b/src/cutecoin/tools/exceptions.py
@@ -163,6 +163,19 @@ class NoPeerAvailable(Error):
             .format(currency, peers))
 
 
+class InvalidNodeCurrency(Error):
+    '''
+    Exception raised when a node doesn't use the intended currency
+    '''
+    def __init__(self, currency, node_currency):
+        '''
+        Constructor
+        '''
+        super() .__init__(
+            "Node is working for {0} currency, but should be {1}"
+            .format(node_currency, currency))
+
+
 class ContactAlreadyExists(Error):
     '''
     Exception raised when a community doesn't have any
-- 
GitLab