Skip to content
Snippets Groups Projects
Commit 5c6df81b authored by inso's avatar inso
Browse files

Using quamash, asyncio and coroutines

Now, waiting for signals is way easier.
parent e9275a9e
No related branches found
No related tags found
No related merge requests found
from .community import Community
from .wallet import Wallet
from .account import Account
\ No newline at end of file
...@@ -12,6 +12,7 @@ import logging ...@@ -12,6 +12,7 @@ import logging
import time import time
import math import math
import json import json
import asyncio
from PyQt5.QtCore import QObject, pyqtSignal, QCoreApplication, QT_TRANSLATE_NOOP from PyQt5.QtCore import QObject, pyqtSignal, QCoreApplication, QT_TRANSLATE_NOOP
from PyQt5.QtNetwork import QNetworkReply from PyQt5.QtNetwork import QNetworkReply
...@@ -121,7 +122,9 @@ class Account(QObject): ...@@ -121,7 +122,9 @@ class Account(QObject):
loading_progressed = pyqtSignal(int, int) loading_progressed = pyqtSignal(int, int)
inner_data_changed = pyqtSignal(str) inner_data_changed = pyqtSignal(str)
wallets_changed = pyqtSignal() wallets_changed = pyqtSignal()
document_broadcasted = pyqtSignal(str) membership_broadcasted = pyqtSignal()
certification_broadcasted = pyqtSignal()
broadcast_error = pyqtSignal(int, str)
def __init__(self, salt, pubkey, name, communities, wallets, contacts, identities_registry): def __init__(self, salt, pubkey, name, communities, wallets, contacts, identities_registry):
''' '''
...@@ -312,6 +315,7 @@ class Account(QObject): ...@@ -312,6 +315,7 @@ class Account(QObject):
self.wallets = self.wallets[:size] self.wallets = self.wallets[:size]
self.wallets_changed.emit() self.wallets_changed.emit()
@asyncio.coroutine
def certify(self, password, community, pubkey): def certify(self, password, community, pubkey):
""" """
Certify an other identity Certify an other identity
...@@ -320,26 +324,76 @@ class Account(QObject): ...@@ -320,26 +324,76 @@ class Account(QObject):
:param cutecoin.core.community.Community community: The community target of the certification :param cutecoin.core.community.Community community: The community target of the certification
:param str pubkey: The certified identity pubkey :param str pubkey: The certified identity pubkey
""" """
certified = self._identities_registry.lookup(pubkey, community) blockid = ""
blockid = community.current_blockid() selfcert = None
def build_certification_data(reply):
if reply.error() == QNetworkReply.NoError:
strdata = bytes(reply.readAll()).decode('utf-8')
json_data = json.loads(strdata)
nonlocal blockid
blockid = community.blockid(json_data)
future_certdata.set_result(True)
def build_certification_selfcert(reply):
if reply.error() == QNetworkReply.NoError:
strdata = bytes(reply.readAll()).decode('utf-8')
json_data = json.loads(strdata)
nonlocal selfcert
selfcert = self._identities_registry.lookup(pubkey, community).selfcert(community, json_data)
future_selfcert.set_result(True)
future_certdata = asyncio.Future()
reply = community.bma_access.request(qtbma.blockchain.Current)
reply.finished.connect(lambda: build_certification_data(reply))
yield from future_certdata
future_selfcert = asyncio.Future()
reply = community.bma_access.request( qtbma.wot.Lookup, req_args={'search': pubkey})
reply.finished.connect(lambda: build_certification_selfcert(reply))
yield from future_selfcert
certification = Certification(PROTOCOL_VERSION, community.currency, certification = Certification(PROTOCOL_VERSION, community.currency,
self.pubkey, certified.pubkey, self.pubkey, pubkey,
blockid['number'], blockid['hash'], None) blockid['number'], blockid['hash'], None)
selfcert = certified.selfcert(community)
logging.debug("SelfCertification : {0}".format(selfcert.raw()))
key = SigningKey(self.salt, password) key = SigningKey(self.salt, password)
certification.sign(selfcert, [key]) certification.sign(selfcert, [key])
signed_cert = certification.signed_raw(selfcert) signed_cert = certification.signed_raw(selfcert)
logging.debug("Certification : {0}".format(signed_cert)) logging.debug("Certification : {0}".format(signed_cert))
data = {'pubkey': certified.pubkey, data = {'pubkey': pubkey,
'self_': selfcert.signed_raw(), 'self_': selfcert.signed_raw(),
'other': "{0}\n".format(certification.inline())} 'other': "{0}\n".format(certification.inline())}
logging.debug("Posted data : {0}".format(data)) logging.debug("Posted data : {0}".format(data))
community.broadcast(qtbma.wot.Add, {}, 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)
def revoke(self, password, community): def revoke(self, password, community):
""" """
...@@ -351,7 +405,6 @@ class Account(QObject): ...@@ -351,7 +405,6 @@ class Account(QObject):
revoked = self._identities_registry.lookup(self.pubkey, community) revoked = self._identities_registry.lookup(self.pubkey, community)
revocation = Revocation(PROTOCOL_VERSION, community.currency, None) revocation = Revocation(PROTOCOL_VERSION, community.currency, None)
selfcert = revoked.selfcert(community) selfcert = revoked.selfcert(community)
key = SigningKey(self.salt, password) key = SigningKey(self.salt, password)
...@@ -414,32 +467,50 @@ class Account(QObject): ...@@ -414,32 +467,50 @@ class Account(QObject):
'self_': selfcert.signed_raw(), 'self_': selfcert.signed_raw(),
'other': []}) 'other': []})
@asyncio.coroutine
def send_membership(self, password, community, mstype): def send_membership(self, password, community, mstype):
''' '''
Send a membership document to a target community Send a membership document to a target community.
Signal "document_broadcasted" is emitted at the end.
:param str password: The account SigningKey password :param str password: The account SigningKey password
:param community: The community target of the membership document :param community: The community target of the membership document
:param str mstype: The type of membership demand. "IN" to join, "OUT" to leave :param str mstype: The type of membership demand. "IN" to join, "OUT" to leave
''' '''
reply = community.bma_access.request(qtbma.blockchain.Current) blockid = ""
reply.finished.connect(lambda: self.__build_membership_data(password, community, mstype, reply)) selfcert = None
logging.debug("Send membership")
def __build_membership_data(self, password, community, mstype, reply): def build_membership_data(reply):
if reply.error() == QNetworkReply.NoError: if reply.error() == QNetworkReply.NoError:
strdata = bytes(reply.readAll()).decode('utf-8') strdata = bytes(reply.readAll()).decode('utf-8')
json_data = json.loads(strdata) json_data = json.loads(strdata)
nonlocal blockid
blockid = community.blockid(json_data) blockid = community.blockid(json_data)
reply = community.bma_access.request( qtbma.wot.Lookup, req_args={'search': self.pubkey}) future_msdata.set_result(True)
reply.finished.connect(lambda: self.__broadcast_membership(community, blockid, mstype, password, reply))
else: else:
raise ConnectionError(self.tr("Failed to get data build membership document")) raise ConnectionError(self.tr("Failed to get data build membership document"))
def __broadcast_membership(self, community, blockid, mstype, password, reply): def build_selfcert(reply):
if reply.error() == QNetworkReply.NoError: if reply.error() == QNetworkReply.NoError:
strdata = bytes(reply.readAll()).decode('utf-8') strdata = bytes(reply.readAll()).decode('utf-8')
json_data = json.loads(strdata) json_data = json.loads(strdata)
nonlocal selfcert
selfcert = self.identity(community).selfcert(community, json_data) selfcert = self.identity(community).selfcert(community, json_data)
future_selfcert.set_result(True)
else:
raise ConnectionError(self.tr("Failed to get data build membership document"))
future_msdata = asyncio.Future()
reply = community.bma_access.request(qtbma.blockchain.Current)
reply.finished.connect(lambda: build_membership_data(reply))
logging.debug("msdata")
yield from future_msdata
future_selfcert = asyncio.Future()
reply = community.bma_access.request( qtbma.wot.Lookup, req_args={'search': self.pubkey})
reply.finished.connect(lambda: build_selfcert(reply))
logging.debug("selfcert")
yield from future_selfcert
membership = Membership(PROTOCOL_VERSION, community.currency, membership = Membership(PROTOCOL_VERSION, community.currency,
selfcert.pubkey, blockid['number'], selfcert.pubkey, blockid['number'],
blockid['hash'], mstype, selfcert.uid, blockid['hash'], mstype, selfcert.uid,
...@@ -450,11 +521,9 @@ class Account(QObject): ...@@ -450,11 +521,9 @@ class Account(QObject):
replies = community.bma_access.broadcast(qtbma.blockchain.Membership, {}, replies = community.bma_access.broadcast(qtbma.blockchain.Membership, {},
{'membership': membership.signed_raw()}) {'membership': membership.signed_raw()})
for r in replies: for r in replies:
r.finished.connect(lambda reply=r: self.__handle_broadcast_replies(replies, reply)) r.finished.connect(lambda reply=r: self.__handle_membership_replies(replies, reply))
else:
raise ConnectionError(self.tr("Failed to get data build membership document"))
def __handle_broadcast_replies(self, replies, reply): def __handle_membership_replies(self, replies, reply):
""" """
Handle the reply, if the request was accepted, disconnect Handle the reply, if the request was accepted, disconnect
all other replies all other replies
...@@ -463,14 +532,21 @@ class Account(QObject): ...@@ -463,14 +532,21 @@ class Account(QObject):
:param list of QNetworkReply replies: All request replies :param list of QNetworkReply replies: All request replies
:return: :return:
""" """
strdata = bytes(reply.readAll()).decode('utf-8')
logging.debug("Received reply : {0} : {1}".format(reply.error(), strdata))
if reply.error() == QNetworkReply.NoError: if reply.error() == QNetworkReply.NoError:
self.document_broadcasted.emit("Membership") self.membership_broadcasted.emit()
for r in replies: for r in replies:
try: try:
r.disconnect() r.disconnect()
except TypeError as e: except TypeError as e:
if "disconnect()" in str(e): if "disconnect()" in str(e):
logging.debug("Could not disconnect a reply") 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): def jsonify(self):
''' '''
...@@ -493,7 +569,3 @@ class Account(QObject): ...@@ -493,7 +569,3 @@ class Account(QObject):
'wallets': data_wallets, 'wallets': data_wallets,
'contacts': self.contacts} 'contacts': self.contacts}
return data return data
def get_person(self):
return Person.from_metadata({'text': self.name,
'id': self.pubkey})
...@@ -43,6 +43,7 @@ class Application(QObject): ...@@ -43,6 +43,7 @@ class Application(QObject):
super().__init__() super().__init__()
self.accounts = {} self.accounts = {}
self.current_account = None self.current_account = None
self.qapp = qapp
self.available_version = (True, self.available_version = (True,
__version__, __version__,
"") "")
...@@ -215,7 +216,7 @@ class Application(QObject): ...@@ -215,7 +216,7 @@ class Application(QObject):
for wallet in account.wallets: for wallet in account.wallets:
wallet_path = os.path.join(config.parameters['home'], wallet_path = os.path.join(config.parameters['home'],
account.name, '__cache__', wallet.pubkey) account.name, '__cache__', wallet.pubkey + "_wal")
if os.path.exists(wallet_path): if os.path.exists(wallet_path):
with open(wallet_path, 'r') as json_data: with open(wallet_path, 'r') as json_data:
data = json.load(json_data) data = json.load(json_data)
......
...@@ -53,6 +53,7 @@ class IdentitiesRegistry: ...@@ -53,6 +53,7 @@ class IdentitiesRegistry:
:return: A new person if the pubkey was unknown or\ :return: A new person if the pubkey was unknown or\
the known instance if pubkey was already known. the known instance if pubkey was already known.
:rtype: cutecoin.core.registry.Identity
""" """
if pubkey in self._instances: if pubkey in self._instances:
identity = self._instances[pubkey] identity = self._instances[pubkey]
......
...@@ -4,8 +4,8 @@ Created on 24 dec. 2014 ...@@ -4,8 +4,8 @@ Created on 24 dec. 2014
@author: inso @author: inso
''' '''
from PyQt5.QtWidgets import QDialog, QMessageBox, QDialogButtonBox, QApplication from PyQt5.QtWidgets import QDialog, QMessageBox, QDialogButtonBox, QApplication
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt, pyqtSlot
from ..tools.exceptions import NoPeerAvailable import quamash
from ..gen_resources.certification_uic import Ui_CertificationDialog from ..gen_resources.certification_uic import Ui_CertificationDialog
from . import toast from . import toast
...@@ -16,12 +16,13 @@ class CertificationDialog(QDialog, Ui_CertificationDialog): ...@@ -16,12 +16,13 @@ class CertificationDialog(QDialog, Ui_CertificationDialog):
classdocs classdocs
''' '''
def __init__(self, certifier, password_asker): def __init__(self, certifier, app, password_asker):
''' '''
Constructor Constructor
''' '''
super().__init__() super().__init__()
self.setupUi(self) self.setupUi(self)
self.app = app
self.account = certifier self.account = certifier
self.password_asker = password_asker self.password_asker = password_asker
self.community = self.account.communities[0] self.community = self.account.communities[0]
...@@ -43,30 +44,19 @@ class CertificationDialog(QDialog, Ui_CertificationDialog): ...@@ -43,30 +44,19 @@ class CertificationDialog(QDialog, Ui_CertificationDialog):
if password == "": if password == "":
return return
try:
QApplication.setOverrideCursor(Qt.WaitCursor) QApplication.setOverrideCursor(Qt.WaitCursor)
self.account.certify(password, self.community, pubkey) self.account.certification_broadcasted.connect(lambda: self.certification_sent(self.community,
pubkey))
self.account.broadcast_error.connect(self.handle_error)
with quamash.QEventLoop(self.app.qapp) as loop:
loop.run_until_complete(self.account.certify(password, self.community, pubkey))
def certification_sent(self, pubkey, currency):
toast.display(self.tr("Certification"), toast.display(self.tr("Certification"),
self.tr("Success certifying {0} from {1}").format(pubkey, self.tr("Success certifying {0} from {1}").format(pubkey, currency))
self.community.currency)) self.account.certification_broadcasted.disconnect()
except ValueError as e:
QMessageBox.critical(self, self.tr("Certification"),
self.tr("Something wrong happened : {0}").format(e),
QMessageBox.Ok)
return
except NoPeerAvailable as e:
QMessageBox.critical(self, self.tr("Certification"),
self.tr("Couldn't connect to network : {0}").format(e),
QMessageBox.Ok)
return
except Exception as e:
QMessageBox.critical(self, self.tr("Error"),
"{0}".format(e),
QMessageBox.Ok)
return
finally:
QApplication.restoreOverrideCursor() QApplication.restoreOverrideCursor()
super().accept() super().accept()
def change_current_community(self, index): def change_current_community(self, index):
...@@ -78,6 +68,13 @@ class CertificationDialog(QDialog, Ui_CertificationDialog): ...@@ -78,6 +68,13 @@ class CertificationDialog(QDialog, Ui_CertificationDialog):
self.button_box.button(QDialogButtonBox.Ok).setEnabled(False) self.button_box.button(QDialogButtonBox.Ok).setEnabled(False)
self.button_box.button(QDialogButtonBox.Ok).setText(self.tr("Not a member")) self.button_box.button(QDialogButtonBox.Ok).setText(self.tr("Not a member"))
@pyqtSlot(int, str)
def handle_error(self, error_code, text):
toast.display(self.tr("Error"), self.tr("{0} : {1}".format(error_code, text)))
self.account.certification_broadcasted.disconnect()
self.account.broadcast_error.disconnect(self.handle_error)
QApplication.restoreOverrideCursor()
def recipient_mode_changed(self, pubkey_toggled): def recipient_mode_changed(self, pubkey_toggled):
self.edit_pubkey.setEnabled(pubkey_toggled) self.edit_pubkey.setEnabled(pubkey_toggled)
self.combo_contact.setEnabled(not pubkey_toggled) self.combo_contact.setEnabled(not pubkey_toggled)
...@@ -17,6 +17,7 @@ from .wot_tab import WotTabWidget ...@@ -17,6 +17,7 @@ from .wot_tab import WotTabWidget
from .transfer import TransferMoneyDialog from .transfer import TransferMoneyDialog
from .certification import CertificationDialog from .certification import CertificationDialog
from . import toast from . import toast
import quamash
from ..tools.exceptions import LookupFailureError, NoPeerAvailable from ..tools.exceptions import LookupFailureError, NoPeerAvailable
from ..core.registry import IdentitiesRegistry from ..core.registry import IdentitiesRegistry
from ucoinpy.api import bma from ucoinpy.api import bma
...@@ -68,8 +69,7 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget): ...@@ -68,8 +69,7 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget):
self.account.identity(self.community).inner_data_changed.connect(self.handle_account_identity_change) self.account.identity(self.community).inner_data_changed.connect(self.handle_account_identity_change)
self.search_direct_connections() self.search_direct_connections()
self.account.document_broadcasted.connect(self.display_broadcast_toast) self.account.membership_broadcasted.connect(self.display_membership_toast)
self.refresh_quality_buttons() self.refresh_quality_buttons()
def identity_context_menu(self, point): def identity_context_menu(self, point):
...@@ -152,10 +152,10 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget): ...@@ -152,10 +152,10 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget):
currency_tab = self.window().currencies_tabwidget.currentWidget() currency_tab = self.window().currencies_tabwidget.currentWidget()
currency_tab.tab_history.table_history.model().sourceModel().refresh_transfers() currency_tab.tab_history.table_history.model().sourceModel().refresh_transfers()
def certify_identity(self, person): def certify_identity(self, identity):
dialog = CertificationDialog(self.account, self.password_asker) dialog = CertificationDialog(self.account, self.app, self.password_asker)
dialog.combo_community.setCurrentText(self.community.name) dialog.combo_community.setCurrentText(self.community.name)
dialog.edit_pubkey.setText(person.pubkey) dialog.edit_pubkey.setText(identity.pubkey)
dialog.radio_pubkey.setChecked(True) dialog.radio_pubkey.setChecked(True)
dialog.exec_() dialog.exec_()
...@@ -169,28 +169,16 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget): ...@@ -169,28 +169,16 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget):
index_wot_tab = self.tabs_information.indexOf(self.wot_tab) index_wot_tab = self.tabs_information.indexOf(self.wot_tab)
self.tabs_information.setCurrentIndex(index_wot_tab) self.tabs_information.setCurrentIndex(index_wot_tab)
@pyqtSlot(str) @pyqtSlot()
def display_broadcast_toast(self, document): def display_membership_toast(self):
toast.display(document, self.tr("Success sending {0} demand".format(document))) toast.display(self.tr("Membership"), self.tr("Success sending Membership demand"))
def send_membership_demand(self): def send_membership_demand(self):
password = self.password_asker.exec_() password = self.password_asker.exec_()
if self.password_asker.result() == QDialog.Rejected: if self.password_asker.result() == QDialog.Rejected:
return return
with quamash.QEventLoop(self.app.qapp) as loop:
try: loop.run_until_complete(self.account.send_membership(password, self.community, 'IN'))
self.account.send_membership(password, self.community, 'IN')
except ValueError as e:
QMessageBox.critical(self, self.tr("Join demand error"),
str(e))
except LookupFailureError as e:
QMessageBox.critical(self, self.tr("Key not sent to community"),
self.tr(""""Your key wasn't sent in the community.
You can't request a membership."""))
except NoPeerAvailable as e:
QMessageBox.critical(self, self.tr("Network error"),
self.tr("Couldn't connect to network : {0}").format(e),
QMessageBox.Ok)
# except Exception as e: # except Exception as e:
# QMessageBox.critical(self, "Error", # QMessageBox.critical(self, "Error",
# "{0}".format(e), # "{0}".format(e),
...@@ -207,19 +195,8 @@ The process to join back the community later will have to be done again.""") ...@@ -207,19 +195,8 @@ The process to join back the community later will have to be done again.""")
if self.password_asker.result() == QDialog.Rejected: if self.password_asker.result() == QDialog.Rejected:
return return
try: with quamash.QEventLoop(self.app.qapp) as loop:
self.account.send_membership(password, self.community, 'OUT') loop.run_until_complete(self.account.send_membership(password, self.community, 'OUT'))
except ValueError as e:
QMessageBox.critical(self, self.tr("Leaving demand error"),
str(e))
except NoPeerAvailable as e:
QMessageBox.critical(self, self.tr("Network error"),
self.tr("Couldn't connect to network : {0}").format(e),
QMessageBox.Ok)
# except Exception as e:
# QMessageBox.critical(self, self.tr("Error"),
# "{0}".format(e),
# QMessageBox.Ok)
def publish_uid(self): def publish_uid(self):
reply = QMessageBox.warning(self, self.tr("Warning"), reply = QMessageBox.warning(self, self.tr("Warning"),
......
...@@ -7,7 +7,9 @@ import signal ...@@ -7,7 +7,9 @@ import signal
import sys import sys
import os import os
import logging import logging
import asyncio
from quamash import QEventLoop
from PyQt5.QtWidgets import QApplication from PyQt5.QtWidgets import QApplication
from cutecoin.gui.mainwindow import MainWindow from cutecoin.gui.mainwindow import MainWindow
from cutecoin.core.app import Application from cutecoin.core.app import Application
...@@ -18,6 +20,8 @@ if __name__ == '__main__': ...@@ -18,6 +20,8 @@ if __name__ == '__main__':
cutecoin = QApplication(sys.argv) cutecoin = QApplication(sys.argv)
app = Application(sys.argv, cutecoin) app = Application(sys.argv, cutecoin)
loop = QEventLoop(app)
asyncio.set_event_loop(loop)
window = MainWindow(app) window = MainWindow(app)
window.showMaximized() window.showMaximized()
sys.exit(cutecoin.exec_()) sys.exit(cutecoin.exec_())
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment