diff --git a/src/cutecoin/core/account.py b/src/cutecoin/core/account.py index af77bc3902db74ce4fac26071de738d207562a55..0bfbed849579e1ba2484e378b6fe536dca7ddbce 100644 --- a/src/cutecoin/core/account.py +++ b/src/cutecoin/core/account.py @@ -125,6 +125,8 @@ class Account(QObject): wallets_changed = pyqtSignal() membership_broadcasted = pyqtSignal() certification_broadcasted = pyqtSignal() + selfcert_broadcased = pyqtSignal() + revoke_broadcasted = pyqtSignal() broadcast_error = pyqtSignal(int, str) def __init__(self, salt, pubkey, name, communities, wallets, contacts, identities_registry): @@ -338,88 +340,6 @@ class Account(QObject): self.wallets = self.wallets[:size] self.wallets_changed.emit() - @asyncio.coroutine - def certify(self, password, community, pubkey): - """ - Certify an other identity - - :param str password: The account SigningKey password - :param cutecoin.core.community.Community community: The community target of the certification - :param str pubkey: The certified identity pubkey - """ - logging.debug("Certdata") - blockid = yield from community.blockid() - identity = yield from self._identities_registry.future_lookup(pubkey, community) - selfcert = yield from identity.selfcert(community) - certification = Certification(PROTOCOL_VERSION, community.currency, - self.pubkey, pubkey, - blockid['number'], blockid['hash'], None) - - key = SigningKey(self.salt, password) - certification.sign(selfcert, [key]) - signed_cert = certification.signed_raw(selfcert) - logging.debug("Certification : {0}".format(signed_cert)) - - data = {'pubkey': pubkey, - 'self_': selfcert.signed_raw(), - 'other': "{0}\n".format(certification.inline())} - logging.debug("Posted data : {0}".format(data)) - replies = community.bma_access.broadcast(qtbma.wot.Add, {}, data) - for r in replies: - r.finished.connect(lambda reply=r: self.__handle_certification_reply(replies, reply)) - return True - - def __handle_certification_reply(self, replies, reply): - """ - Handle the reply, if the request was accepted, disconnect - all other replies - - :param QNetworkReply reply: The reply of this handler - :param list of QNetworkReply replies: All request replies - :return: - """ - strdata = bytes(reply.readAll()).decode('utf-8') - logging.debug("Received reply : {0} : {1}".format(reply.error(), strdata)) - if reply.error() == QNetworkReply.NoError: - self.certification_broadcasted.emit() - for r in replies: - try: - r.disconnect() - except TypeError as e: - if "disconnect()" in str(e): - logging.debug("Could not disconnect a reply") - else: - for r in replies: - if not r.isFinished() or r.error() == QNetworkReply.NoError: - return - self.broadcast_error.emit(r.error(), strdata) - - def revoke(self, password, community): - """ - Revoke self-identity on server, not in blockchain - - :param str password: The account SigningKey password - :param cutecoin.core.community.Community community: The community target of the revocation - """ - revoked = self._identities_registry.lookup(self.pubkey, community) - - revocation = Revocation(PROTOCOL_VERSION, community.currency, None) - selfcert = revoked.selfcert(community) - - key = SigningKey(self.salt, password) - revocation.sign(selfcert, [key]) - - logging.debug("Self-Revocation Document : \n{0}".format(revocation.raw(selfcert))) - logging.debug("Signature : \n{0}".format(revocation.signatures[0])) - - data = { - 'pubkey': revoked.pubkey, - 'self_': selfcert.signed_raw(), - 'sig': revocation.signatures[0] - } - logging.debug("Posted data : {0}".format(data)) - community.broadcast(qtbma.wot.Revoke, {}, data) - def transfers(self, community): ''' Get all transfers done in a community by all the wallets @@ -462,6 +382,7 @@ class Account(QObject): value += val return value + @asyncio.coroutine def send_selfcert(self, password, community): ''' Send our self certification to a target community @@ -478,9 +399,36 @@ class Account(QObject): key = SigningKey(self.salt, password) selfcert.sign([key]) logging.debug("Key publish : {0}".format(selfcert.signed_raw())) - community.broadcast(qtbma.wot.Add, {}, {'pubkey': self.pubkey, + replies = community.broadcast(qtbma.wot.Add, {}, {'pubkey': self.pubkey, 'self_': selfcert.signed_raw(), 'other': []}) + for r in replies: + r.finished.connect(lambda reply=r: self.__handle_selfcert_replies(replies, reply)) + + def __handle_selfcert_replies(self, replies, reply): + """ + Handle the reply, if the request was accepted, disconnect + all other replies + + :param QNetworkReply reply: The reply of this handler + :param list of QNetworkReply replies: All request replies + :return: + """ + strdata = bytes(reply.readAll()).decode('utf-8') + logging.debug("Received reply : {0} : {1}".format(reply.error(), strdata)) + if reply.error() == QNetworkReply.NoError: + self.selfcert_broadcasted.emit() + for r in replies: + try: + r.disconnect() + except TypeError as e: + if "disconnect()" in str(e): + logging.debug("Could not disconnect a reply") + else: + for r in replies: + if not r.isFinished() or r.error() == QNetworkReply.NoError: + return + self.broadcast_error.emit(r.error(), strdata) @asyncio.coroutine def send_membership(self, password, community, mstype): @@ -535,6 +483,115 @@ class Account(QObject): return self.broadcast_error.emit(r.error(), strdata) + @asyncio.coroutine + def certify(self, password, community, pubkey): + """ + Certify an other identity + + :param str password: The account SigningKey password + :param cutecoin.core.community.Community community: The community target of the certification + :param str pubkey: The certified identity pubkey + """ + logging.debug("Certdata") + blockid = yield from community.blockid() + identity = yield from self._identities_registry.future_lookup(pubkey, community) + selfcert = yield from identity.selfcert(community) + certification = Certification(PROTOCOL_VERSION, community.currency, + self.pubkey, pubkey, + blockid['number'], blockid['hash'], None) + + key = SigningKey(self.salt, password) + certification.sign(selfcert, [key]) + signed_cert = certification.signed_raw(selfcert) + logging.debug("Certification : {0}".format(signed_cert)) + + data = {'pubkey': pubkey, + 'self_': selfcert.signed_raw(), + 'other': "{0}\n".format(certification.inline())} + logging.debug("Posted data : {0}".format(data)) + replies = community.bma_access.broadcast(qtbma.wot.Add, {}, data) + for r in replies: + r.finished.connect(lambda reply=r: self.__handle_certification_reply(replies, reply)) + + def __handle_certification_reply(self, replies, reply): + """ + Handle the reply, if the request was accepted, disconnect + all other replies + + :param QNetworkReply reply: The reply of this handler + :param list of QNetworkReply replies: All request replies + :return: + """ + strdata = bytes(reply.readAll()).decode('utf-8') + logging.debug("Received reply : {0} : {1}".format(reply.error(), strdata)) + if reply.error() == QNetworkReply.NoError: + self.certification_broadcasted.emit() + for r in replies: + try: + r.disconnect() + except TypeError as e: + if "disconnect()" in str(e): + logging.debug("Could not disconnect a reply") + else: + for r in replies: + if not r.isFinished() or r.error() == QNetworkReply.NoError: + return + self.broadcast_error.emit(r.error(), strdata) + + @asyncio.coroutine + def revoke(self, password, community): + """ + Revoke self-identity on server, not in blockchain + + :param str password: The account SigningKey password + :param cutecoin.core.community.Community community: The community target of the revocation + """ + revoked = yield from self._identities_registry.future_lookup(self.pubkey, community) + + revocation = Revocation(PROTOCOL_VERSION, community.currency, None) + selfcert = revoked.selfcert(community) + + key = SigningKey(self.salt, password) + revocation.sign(selfcert, [key]) + + logging.debug("Self-Revocation Document : \n{0}".format(revocation.raw(selfcert))) + logging.debug("Signature : \n{0}".format(revocation.signatures[0])) + + data = { + 'pubkey': revoked.pubkey, + 'self_': selfcert.signed_raw(), + 'sig': revocation.signatures[0] + } + logging.debug("Posted data : {0}".format(data)) + replies = community.broadcast(qtbma.wot.Revoke, {}, data) + for r in replies: + r.finished.connect(lambda reply=r: self.__handle_certification_reply(replies, reply)) + + def __handle_revoke_reply(self, replies, reply): + """ + Handle the reply, if the request was accepted, disconnect + all other replies + + :param QNetworkReply reply: The reply of this handler + :param list of QNetworkReply replies: All request replies + :return: + """ + strdata = bytes(reply.readAll()).decode('utf-8') + logging.debug("Received reply : {0} : {1}".format(reply.error(), strdata)) + if reply.error() == QNetworkReply.NoError: + self.revoke_broadcasted.emit() + for r in replies: + try: + r.disconnect() + except TypeError as e: + if "disconnect()" in str(e): + logging.debug("Could not disconnect a reply") + else: + for r in replies: + if not r.isFinished() or r.error() == QNetworkReply.NoError: + return + self.broadcast_error.emit(r.error(), strdata) + def jsonify(self): ''' Get the account in a json format. diff --git a/src/cutecoin/core/app.py b/src/cutecoin/core/app.py index 3019f976ce5d2452474d978028a69e0c045bd4d9..379b39cba3c61954c082026f0cc185dce2be644b 100644 --- a/src/cutecoin/core/app.py +++ b/src/cutecoin/core/app.py @@ -127,6 +127,8 @@ class Application(QObject): ''' if self.current_account is not None: self.save_cache(self.current_account) + for c in self.current_account.communities: + c.network.stop_crawling() self.current_account = account diff --git a/src/cutecoin/core/net/network.py b/src/cutecoin/core/net/network.py index aa5f3625a0283941000d282f950d7af23b57a500..ddb73d6c42b9531440fb96b49f70bde1f09fa5e1 100644 --- a/src/cutecoin/core/net/network.py +++ b/src/cutecoin/core/net/network.py @@ -35,7 +35,6 @@ class Network(QObject): self.add_node(n) self.currency = currency self._must_crawl = False - self._is_perpetual = False self.network_manager = network_manager self._block_found = self.latest_block self._timer = QTimer() @@ -115,10 +114,7 @@ class Network(QObject): self._must_crawl = False def continue_crawling(self): - if self._is_perpetual: - return self._must_crawl - else: - return True + return self._must_crawl @property def synced_nodes(self): @@ -196,13 +192,13 @@ class Network(QObject): Start crawling which never stops. To stop this crawling, call "stop_crawling" method. ''' + self._must_crawl = True while self.continue_crawling(): for node in self.nodes: if self.continue_crawling(): yield from asyncio.sleep(2) node.refresh() - else: - return True + logging.debug("End of network discovery") @pyqtSlot(Peer) def handle_new_node(self, peer): diff --git a/src/cutecoin/gui/community_tab.py b/src/cutecoin/gui/community_tab.py index 63599e9b9f7c34c71114ca3dca6d558c66263340..38f7cdd81a358dffd8cd311a373150fb695e2951 100644 --- a/src/cutecoin/gui/community_tab.py +++ b/src/cutecoin/gui/community_tab.py @@ -67,7 +67,15 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget): self.account.identity(self.community).inner_data_changed.connect(self.handle_account_identity_change) self.search_direct_connections() - self.account.membership_broadcasted.connect(self.display_membership_toast) + self.account.membership_broadcasted.connect(lambda: + toast.display(self.tr("Membership"), + self.tr("Success sending Membership demand"))) + self.account.revoke_broadcasted.connect(lambda: + toast.display(self.tr("Revoke"), + self.tr("Success sending Revoke demand"))) + self.account.selfcert_broadcased.connect(lambda: + toast.display(self.tr("Self Certification"), + self.tr("Success sending Self Certification document"))) self.refresh_quality_buttons() def identity_context_menu(self, point): @@ -167,10 +175,6 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget): index_wot_tab = self.tabs_information.indexOf(self.wot_tab) self.tabs_information.setCurrentIndex(index_wot_tab) - @pyqtSlot() - def display_membership_toast(self): - toast.display(self.tr("Membership"), self.tr("Success sending Membership demand")) - def send_membership_demand(self): password = self.password_asker.exec_() if self.password_asker.result() == QDialog.Rejected: @@ -226,14 +230,7 @@ Revoking your UID can only success if it is not already validated by the network if self.password_asker.result() == QDialog.Rejected: return - try: - self.account.revoke(password, self.community) - toast.display(self.tr("UID Revoking"), - self.tr("Success revoking your UID")) - except NoPeerAvailable as e: - QMessageBox.critical(self, self.tr("Network error"), - self.tr("Couldn't connect to network : {0}").format(e), - QMessageBox.Ok) + asyncio.async(self.account.revoke(password, self.community)) @asyncio.coroutine def _execute_search_text(self, text): @@ -362,15 +359,3 @@ Revoking your UID can only success if it is not already validated by the network self.button_membership.hide() self.button_leaving.hide() self.button_publish_uid.show() - - @pyqtSlot(str) - def refresh_person(self, pubkey): - logging.debug("Refresh person {0}".format(pubkey)) - if self is None: - logging.error("community_tab self is None in refresh_person. Watcher connected to a destroyed tab") - else: - if pubkey == self.account.pubkey: - self.refresh_quality_buttons() - - index = self.table_identities.model().sourceModel().person_index(pubkey) - self.table_identities.model().sourceModel().dataChanged.emit(index[0], index[1]) diff --git a/src/cutecoin/gui/mainwindow.py b/src/cutecoin/gui/mainwindow.py index ccb03d05346a8565ec7aa5018fac729c73e7c46f..ec87e33a99efacf2accbc9b296f9ceba5e33e0f5 100644 --- a/src/cutecoin/gui/mainwindow.py +++ b/src/cutecoin/gui/mainwindow.py @@ -106,7 +106,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): dialog = ProcessConfigureAccount(self.app, None) result = dialog.exec_() if result == QDialog.Accepted: - self.action_change_account(self.app.current_account.name) + self.action_change_account(self.app.current_account) @pyqtSlot(str) def display_error(self, error): @@ -146,19 +146,9 @@ class MainWindow(QMainWindow, Ui_MainWindow): if result == QDialog.Accepted: self.window().refresh_contacts() - def action_change_account(self, account): - - if self.app.current_account: - self.app.save_cache(self.app.current_account) - - self.app.current_account = account + def action_change_account(self, account_name): + self.app.change_current_account(self.app.get_account(account_name)) self.refresh() - self.busybar.setMinimum(0) - self.busybar.setMaximum(0) - self.busybar.setValue(-1) - self.busybar.show() - self.homescreen.button_new.hide() - self.homescreen.button_import.hide() @pyqtSlot() def loader_finished(self):