diff --git a/src/sakia/data/processors/dividends.py b/src/sakia/data/processors/dividends.py index 6f24b1b3493ed7e5791b14643efbeec302222811..a094f90b3d1be54f22d788b2dbe1b43e2d1471b2 100644 --- a/src/sakia/data/processors/dividends.py +++ b/src/sakia/data/processors/dividends.py @@ -36,20 +36,20 @@ class DividendsProcessor: self._logger.debug("Dividend already in db") return False - async def initialize_dividends(self, identity, transactions, log_stream): + async def initialize_dividends(self, connection, transactions, log_stream): """ Request transactions from the network to initialize data for a given pubkey - :param sakia.data.entities.Identity identity: + :param sakia.data.entities.Connection connection: :param List[sakia.data.entities.Transaction] transactions: the list of transactions found by tx processor :param function log_stream: """ - history_data = await self._bma_connector.get(identity.currency, bma.ud.history, - req_args={'pubkey': identity.pubkey}, verify=False) + history_data = await self._bma_connector.get(connection.currency, bma.ud.history, + req_args={'pubkey': connection.pubkey}, verify=False) log_stream("Found {0} available dividends".format(len(history_data["history"]["history"]))) block_numbers = [] for ud_data in history_data["history"]["history"]: - dividend = Dividend(currency=identity.currency, - pubkey=identity.pubkey, + dividend = Dividend(currency=connection.currency, + pubkey=connection.pubkey, block_number=ud_data["block_number"], timestamp=ud_data["time"], amount=ud_data["amount"], diff --git a/src/sakia/data/processors/transactions.py b/src/sakia/data/processors/transactions.py index be32c11b69b1e623933a4330eb9d09b4338fb5e1..39e765b10ab32f9b22841abb69b4fa494aef0f61 100644 --- a/src/sakia/data/processors/transactions.py +++ b/src/sakia/data/processors/transactions.py @@ -124,7 +124,9 @@ class TransactionsProcessor: responses = await self._bma_connector.broadcast(currency, bma.tx.process, req_args={'transaction': txdoc.signed_raw()}) result = (False, "") for r in responses: - if r.status == 200: + if isinstance(r, BaseException): + result = (False, str(r)) + elif r.status == 200: result = (True, (await r.json())) elif not result[0]: result = (False, (await r.text())) @@ -133,14 +135,14 @@ class TransactionsProcessor: self.run_state_transitions(tx, [r.status for r in responses]) return result, tx - async def initialize_transactions(self, identity, log_stream): + async def initialize_transactions(self, connection, log_stream): """ Request transactions from the network to initialize data for a given pubkey - :param sakia.data.entities.Identity pubkey: + :param sakia.data.entities.Connection connection: :param function log_stream: """ - history_data = await self._bma_connector.get(identity.currency, bma.tx.history, - req_args={'pubkey': identity.pubkey}, verify=False) + history_data = await self._bma_connector.get(connection.currency, bma.tx.history, + req_args={'pubkey': connection.pubkey}, verify=False) txid = 0 nb_tx = len(history_data["history"]["sent"]) + len(history_data["history"]["received"]) log_stream("Found {0} transactions".format(nb_tx)) @@ -149,8 +151,8 @@ class TransactionsProcessor: sent = TransactionDoc.from_bma_history(history_data["currency"], sent_data) log_stream("{0}/{1} transactions".format(txid, nb_tx)) try: - tx = parse_transaction_doc(sent, identity.pubkey, sent_data["block_number"], - sent_data["time"], txid) + tx = parse_transaction_doc(sent, connection.pubkey, sent_data["block_number"], + sent_data["time"], txid) self._repo.insert(tx) transactions.append(tx) except sqlite3.IntegrityError: diff --git a/src/sakia/gui/dialogs/connection_cfg/connection_cfg.ui b/src/sakia/gui/dialogs/connection_cfg/connection_cfg.ui index 24b9bcbd3f3ab7a3f1c170400bb4285f1edc8ae5..a70c6198cab5d72587155461fde27ac00e9d70f2 100644 --- a/src/sakia/gui/dialogs/connection_cfg/connection_cfg.ui +++ b/src/sakia/gui/dialogs/connection_cfg/connection_cfg.ui @@ -105,17 +105,17 @@ </widget> </item> <item> - <widget class="QPushButton" name="button_guest"> + <widget class="QPushButton" name="button_wallet"> <property name="text"> - <string>Connect as a guest</string> + <string>Connect a wallet</string> </property> <property name="icon"> <iconset resource="../../../../../res/icons/icons.qrc"> - <normaloff>:/icons/guest_icon</normaloff>:/icons/guest_icon</iconset> + <normaloff>:/icons/wallet_icon</normaloff>:/icons/wallet_icon</iconset> </property> <property name="iconSize"> <size> - <width>32</width> + <width>50</width> <height>32</height> </size> </property> diff --git a/src/sakia/gui/dialogs/connection_cfg/controller.py b/src/sakia/gui/dialogs/connection_cfg/controller.py index f272621f71233feba39d0860662525b953ecf4b7..fd0e056ecce8ec321d0c6e969391ad17f2cbfbe7 100644 --- a/src/sakia/gui/dialogs/connection_cfg/controller.py +++ b/src/sakia/gui/dialogs/connection_cfg/controller.py @@ -21,7 +21,7 @@ class ConnectionConfigController(QObject): CONNECT = 0 REGISTER = 1 - GUEST = 2 + WALLET = 2 def __init__(self, parent, view, model): """ @@ -40,8 +40,8 @@ class ConnectionConfigController(QObject): lambda: self.step_node.set_result(ConnectionConfigController.CONNECT)) self.view.button_register.clicked.connect( lambda: self.step_node.set_result(ConnectionConfigController.REGISTER)) - self.view.button_guest.clicked.connect( - lambda: self.step_node.set_result(ConnectionConfigController.GUEST)) + self.view.button_wallet.clicked.connect( + lambda: self.step_node.set_result(ConnectionConfigController.WALLET)) self.password_asker = None self.view.values_changed.connect(lambda: self.view.button_next.setEnabled(self.check_key())) self.view.values_changed.connect(lambda: self.view.button_generate.setEnabled(self.check_key())) @@ -148,6 +148,12 @@ class ConnectionConfigController(QObject): self.view.button_next.clicked.connect(self.check_connect) self.view.stacked_pages.setCurrentWidget(self.view.page_connection) connection_identity = await self.step_key + elif mode == ConnectionConfigController.WALLET: + self._logger.debug("Wallet mode") + self.view.button_next.clicked.connect(self.check_wallet) + self.view.edit_uid.hide() + self.view.stacked_pages.setCurrentWidget(self.view.page_connection) + connection_identity = await self.step_key self.model.insert_or_update_connection() self.view.stacked_pages.setCurrentWidget(self.view.page_services) @@ -177,10 +183,14 @@ class ConnectionConfigController(QObject): await self.model.initialize_identity(connection_identity, log_stream=self.view.stream_log) self.view.stream_log("Initializing certifications informations...") await self.model.initialize_certifications(connection_identity, log_stream=self.view.stream_log) + + if mode in (ConnectionConfigController.REGISTER, + ConnectionConfigController.CONNECT, + ConnectionConfigController.WALLET): self.view.stream_log("Initializing transactions history...") - transactions = await self.model.initialize_transactions(connection_identity, log_stream=self.view.stream_log) - self.view.stream_log("Initializing transactions history...") - await self.model.initialize_dividends(connection_identity, transactions, log_stream=self.view.stream_log) + transactions = await self.model.initialize_transactions(self.model.connection, log_stream=self.view.stream_log) + self.view.stream_log("Initializing dividends history...") + await self.model.initialize_dividends(self.model.connection, transactions, log_stream=self.view.stream_log) self.view.progress_bar.setValue(3) await self.model.initialize_sources(self.view.stream_log) @@ -197,6 +207,7 @@ class ConnectionConfigController(QObject): self.step_node.set_result(mode) self.step_key = asyncio.Future() self.view.button_next.disconnect() + self.view.edit_uid.show() asyncio.ensure_future(self.process()) return self.accept() @@ -235,6 +246,30 @@ class ConnectionConfigController(QObject): self.view.label_info.setText("") return True + @asyncify + async def check_wallet(self, checked=False): + self._logger.debug("Is valid ? ") + self.view.display_info(self.tr("connecting...")) + try: + salt = self.view.edit_salt.text() + password = self.view.edit_password.text() + self.model.set_scrypt_infos(salt, password, self.view.scrypt_params) + self.model.set_uid("") + if not self.model.key_exists(): + registered, found_identity = await self.model.check_registered() + self.view.button_connect.setEnabled(True) + self.view.button_register.setEnabled(True) + if registered[0] is False and registered[2] is None: + self.step_key.set_result(None) + elif registered[2]: + self.view.display_info(self.tr("""Your pubkey is associated to a pubkey. + Yours : {0}, the network : {1}""".format(registered[1], registered[2]))) + else: + self.view.display_info(self.tr("A connection already exists using this key.")) + + except NoPeerAvailable: + self.config_dialog.label_error.setText(self.tr("Could not connect. Check node peering entry")) + @asyncify async def check_connect(self, checked=False): self._logger.debug("Is valid ? ") diff --git a/src/sakia/gui/navigation/controller.py b/src/sakia/gui/navigation/controller.py index 465c86bbf6431eb54cf7278cb48208d00e1e9ddb..e2d21ba33b97028eb4b4ce69185f037f944d052c 100644 --- a/src/sakia/gui/navigation/controller.py +++ b/src/sakia/gui/navigation/controller.py @@ -107,21 +107,28 @@ class NavigationController(QObject): raw_data = self.view.tree_view.model().data(index, GenericTreeModel.ROLE_RAW_DATA) if raw_data and raw_data["component"] == "Informations": menu = QMenu(self.view) - action_gen_revokation = QAction(self.tr("Save revokation document"), menu) - menu.addAction(action_gen_revokation) - action_gen_revokation.triggered.connect(lambda c: - self.action_save_revokation(raw_data['misc']['connection'])) - - action_publish_uid = QAction(self.tr("Publish UID"), menu) - menu.addAction(action_publish_uid) - action_publish_uid.triggered.connect(lambda c: - self.publish_uid(raw_data['misc']['connection'])) - action_publish_uid.setEnabled(self.model.identity_published(raw_data['misc']['connection'])) - - action_leave = QAction(self.tr("Leave the currency"), menu) - menu.addAction(action_leave) - action_leave.triggered.connect(lambda c: self.send_leave(raw_data['misc']['connection'])) - action_leave.setEnabled(self.model.identity_is_member(raw_data['misc']['connection'])) + if raw_data['misc']['connection'].uid: + action_gen_revokation = QAction(self.tr("Save revokation document"), menu) + menu.addAction(action_gen_revokation) + action_gen_revokation.triggered.connect(lambda c: + self.action_save_revokation(raw_data['misc']['connection'])) + + action_publish_uid = QAction(self.tr("Publish UID"), menu) + menu.addAction(action_publish_uid) + action_publish_uid.triggered.connect(lambda c: + self.publish_uid(raw_data['misc']['connection'])) + action_publish_uid.setEnabled(self.model.identity_published(raw_data['misc']['connection'])) + + action_leave = QAction(self.tr("Leave the currency"), menu) + menu.addAction(action_leave) + action_leave.triggered.connect(lambda c: self.send_leave(raw_data['misc']['connection'])) + action_leave.setEnabled(self.model.identity_is_member(raw_data['misc']['connection'])) + + copy_pubkey = QAction(menu.tr("Copy pubkey to clipboard"), menu.parent()) + copy_pubkey.triggered.connect(lambda checked, + c=raw_data['misc']['connection']: \ + NavigationModel.copy_pubkey_to_clipboard(c)) + menu.addAction(copy_pubkey) action_remove = QAction(self.tr("Remove the connection"), menu) menu.addAction(action_remove) diff --git a/src/sakia/gui/navigation/informations/model.py b/src/sakia/gui/navigation/informations/model.py index 2cd757c26bc333dece3174cafc4763d0fc281564..729eade25b96b9c935f3bdb035835d0bf5b6826e 100644 --- a/src/sakia/gui/navigation/informations/model.py +++ b/src/sakia/gui/navigation/informations/model.py @@ -119,31 +119,32 @@ class InformationsModel(QObject): mstime_remaining_text = self.tr("Expired or never published") outdistanced_text = self.tr("Outdistanced") is_member = False - - try: - identity = self.identities_service.get_identity(self.connection.pubkey, self.connection.uid) - mstime_remaining = self.identities_service.ms_time_remaining(identity) - is_member = identity.member - nb_certs = len(self.identities_service.certifications_received(identity.pubkey)) - if not identity.outdistanced: - outdistanced_text = self.tr("In WoT range") - - if mstime_remaining > 0: - days, remainder = divmod(mstime_remaining, 3600 * 24) - hours, remainder = divmod(remainder, 3600) - minutes, seconds = divmod(remainder, 60) - mstime_remaining_text = self.tr("Expires in ") - if days > 0: - mstime_remaining_text += "{days} days".format(days=days) + nb_certs = 0 + + if self.connection.uid: + try: + identity = self.identities_service.get_identity(self.connection.pubkey, self.connection.uid) + mstime_remaining = self.identities_service.ms_time_remaining(identity) + is_member = identity.member + nb_certs = len(self.identities_service.certifications_received(identity.pubkey)) + if not identity.outdistanced: + outdistanced_text = self.tr("In WoT range") + + if mstime_remaining > 0: + days, remainder = divmod(mstime_remaining, 3600 * 24) + hours, remainder = divmod(remainder, 3600) + minutes, seconds = divmod(remainder, 60) + mstime_remaining_text = self.tr("Expires in ") + if days > 0: + mstime_remaining_text += "{days} days".format(days=days) + else: + mstime_remaining_text += "{hours} hours and {min} min.".format(hours=hours, + min=minutes) + except errors.DuniterError as e: + if e.ucode == errors.NO_MEMBER_MATCHING_PUB_OR_UID: + pass else: - mstime_remaining_text += "{hours} hours and {min} min.".format(hours=hours, - min=minutes) - except errors.DuniterError as e: - if e.ucode == errors.NO_MEMBER_MATCHING_PUB_OR_UID: - nb_certs = 0 - else: - self._logger.error(str(e)) - nb_certs = 0 + self._logger.error(str(e)) return { 'amount': localized_amount, diff --git a/src/sakia/gui/navigation/model.py b/src/sakia/gui/navigation/model.py index 825b7552c617df76822ea45f3e01383bfb057e13..7f2387246f1f1d75816bc9936e6e5a350bb7df11 100644 --- a/src/sakia/gui/navigation/model.py +++ b/src/sakia/gui/navigation/model.py @@ -1,6 +1,6 @@ -from PyQt5.QtCore import QObject +from PyQt5.QtCore import QObject, pyqtSignal +from PyQt5.QtWidgets import QApplication from sakia.models.generic_tree import GenericTreeModel -from PyQt5.QtCore import pyqtSignal from sakia.data.processors import ConnectionsProcessor @@ -22,23 +22,40 @@ class NavigationModel(QObject): self._current_data = None def init_navigation_data(self): - self.navigation = [ - { - 'title': self.app.parameters.profile_name, - 'component': "HomeScreen", - 'parameters': self.app.parameters, - 'dependencies': {}, - 'misc': {}, - 'children': [] - } - ] + currencies = ConnectionsProcessor.instanciate(self.app).currencies() + if currencies: + self.navigation = [ + { + 'title': self.tr('Network'), + 'icon': ':/icons/network_icon', + 'component': "Network", + 'dependencies': { + 'network_service': self.app.network_services[currencies[0]], + }, + 'misc': { + }, + 'children': [] + } + ] + else: + self.navigation = [ + { + 'title': self.tr("No connection configured"), + 'component': "HomeScreen", + 'parameters': self.app.parameters, + 'dependencies': {}, + 'misc': {}, + 'children': [] + } + ] + self._current_data = self.navigation[0] for connection in self.app.db.connections_repo.get_all(): self.navigation[0]['children'].append(self.create_node(connection)) return self.navigation def create_node(self, connection): - return { + node = { 'title': connection.title(), 'component': "Informations", 'dependencies': { @@ -65,46 +82,37 @@ class NavigationModel(QObject): 'misc': { 'connection': connection } - }, - { - 'title': self.tr('Network'), - 'icon': ':/icons/network_icon', - 'component': "Network", - 'dependencies': { - 'network_service': self.app.network_services[connection.currency], - }, - 'misc': { - 'connection': connection - } - }, - { - 'title': self.tr('Identities'), - 'icon': ':/icons/members_icon', - 'component': "Identities", - 'dependencies': { - 'connection': connection, - 'blockchain_service': self.app.blockchain_services[connection.currency], - 'identities_service': self.app.identities_services[connection.currency], - }, - 'misc': { - 'connection': connection - } - }, - { - 'title': self.tr('Web of Trust'), - 'icon': ':/icons/wot_icon', - 'component': "Wot", - 'dependencies': { - 'connection': connection, - 'blockchain_service': self.app.blockchain_services[connection.currency], - 'identities_service': self.app.identities_services[connection.currency], - }, - 'misc': { - 'connection': connection - } } ] } + if connection.uid: + node["children"] += [{ + 'title': self.tr('Identities'), + 'icon': ':/icons/members_icon', + 'component': "Identities", + 'dependencies': { + 'connection': connection, + 'blockchain_service': self.app.blockchain_services[connection.currency], + 'identities_service': self.app.identities_services[connection.currency], + }, + 'misc': { + 'connection': connection + } + }, + { + 'title': self.tr('Web of Trust'), + 'icon': ':/icons/wot_icon', + 'component': "Wot", + 'dependencies': { + 'connection': connection, + 'blockchain_service': self.app.blockchain_services[connection.currency], + 'identities_service': self.app.identities_services[connection.currency], + }, + 'misc': { + 'connection': connection + } + }] + return node def generic_tree(self): return GenericTreeModel.create("Navigation", self.navigation) @@ -140,6 +148,10 @@ class NavigationModel(QObject): async def remove_connection(self, connection): await self.app.remove_connection(connection) - async def send_leave(self, connection, password): return await self.app.documents_service.send_membership(connection, password, "OUT") + + @staticmethod + def copy_pubkey_to_clipboard(connection): + clipboard = QApplication.clipboard() + clipboard.setText(connection.pubkey) \ No newline at end of file diff --git a/src/sakia/gui/navigation/network/controller.py b/src/sakia/gui/navigation/network/controller.py index 296b15a8064b055ec9d6efc864be2645822715c9..f610d90f02cbb7d662bfc6fc28cf57086693600d 100644 --- a/src/sakia/gui/navigation/network/controller.py +++ b/src/sakia/gui/navigation/network/controller.py @@ -33,7 +33,6 @@ class NetworkController(QObject): :param PyQt5.QObject parent: :param sakia.app.Application app: :param sakia.services.NetworkService network_service: - :param sakia.data.entities.Connection connection: :return: """ view = NetworkView(parent.view,) diff --git a/src/sakia/gui/navigation/view.py b/src/sakia/gui/navigation/view.py index 6874120bbecf1675d6e7525a7394bf18c3ff630b..6c5acc029ba1ffda077d4f10c3fe6e8e33dfe1cb 100644 --- a/src/sakia/gui/navigation/view.py +++ b/src/sakia/gui/navigation/view.py @@ -42,7 +42,7 @@ class NavigationView(QFrame, Ui_Navigation): raw_data = self.tree_view.model().data(index, GenericTreeModel.ROLE_RAW_DATA) if 'widget' in raw_data: widget = raw_data['widget'] - if self.stacked_widget.indexOf(widget): + if self.stacked_widget.indexOf(widget) != -1: self.stacked_widget.setCurrentWidget(widget) self.current_view_changed.emit(raw_data)