From b83012d1011ba6bb7737c3b738a0bee515e3b438 Mon Sep 17 00:00:00 2001 From: Inso <insomniak.fr@gmail.com> Date: Wed, 23 Sep 2015 23:43:51 +0200 Subject: [PATCH] Implementing the feature #214 --- src/cutecoin/core/account.py | 89 +++++++++++- src/cutecoin/core/registry/identities.py | 3 +- src/cutecoin/gui/process_cfg_community.py | 18 ++- .../test_add_community.py | 133 +++++++++++++++--- src/cutecoin/tests/qapp.py | 2 +- 5 files changed, 213 insertions(+), 32 deletions(-) diff --git a/src/cutecoin/core/account.py b/src/cutecoin/core/account.py index 3576096a..bb24fd9d 100644 --- a/src/cutecoin/core/account.py +++ b/src/cutecoin/core/account.py @@ -19,10 +19,11 @@ from . import money from .wallet import Wallet from .community import Community from .registry import LocalState -from ..tools.exceptions import ContactAlreadyExists +from ..tools.exceptions import ContactAlreadyExists, NoPeerAvailable from ..tools.decorators import asyncify from ucoinpy.api import bma from ucoinpy.api.bma import PROTOCOL_VERSION +from aiohttp.errors import ClientError class Account(QObject): @@ -270,6 +271,92 @@ class Account(QObject): value += val return value + @asyncio.coroutine + def check_registered(self, community): + """ + Checks for the pubkey and the uid of an account in a community + :param cutecoin.core.Community community: The community we check for registration + :return: (True if found, local value, network value) + """ + def _parse_uid_certifiers(data): + return self.name == data['uid'], self.name, data['uid'] + + def _parse_uid_lookup(data): + timestamp = 0 + found_uid = "" + for result in data['results']: + if result["pubkey"] == self.pubkey: + uids = result['uids'] + for uid_data in uids: + if uid_data["meta"]["timestamp"] > timestamp: + timestamp = uid_data["meta"]["timestamp"] + found_uid = uid_data["uid"] + return self.name == found_uid, self.name, found_uid + + def _parse_pubkey_certifiers(data): + return self.pubkey == data['pubkey'], self.pubkey, data['pubkey'] + + def _parse_pubkey_lookup(data): + timestamp = 0 + found_uid = "" + found_result = ["", ""] + for result in data['results']: + uids = result['uids'] + for uid_data in uids: + if uid_data["meta"]["timestamp"] > timestamp: + timestamp = uid_data["meta"]["timestamp"] + found_uid = uid_data["uid"] + if found_uid == self.name: + found_result = result['pubkey'], found_uid + if found_result[1] == self.name: + return self.pubkey == found_result[0], self.pubkey, found_result[0] + else: + return False, self.pubkey, None + + @asyncio.coroutine + def execute_requests(parsers, search): + tries = 0 + request = bma.wot.CertifiersOf + nonlocal registered + while tries < 3 and not registered[0] and not registered[2]: + try: + data = yield from community.bma_access.simple_request(request, + req_args={'search': search}) + registered = parsers[request](data) + except ValueError as e: + if '404' in str(e) or '400' in str(e): + if request == bma.wot.CertifiersOf: + request = bma.wot.Lookup + tries = 0 + else: + tries += 1 + else: + tries += 1 + except asyncio.TimeoutError: + tries += 1 + except ClientError: + tries += 1 + + registered = (False, self.name, None) + # We execute search based on pubkey + # And look for account UID + uid_parsers = { + bma.wot.CertifiersOf: _parse_uid_certifiers, + bma.wot.Lookup: _parse_uid_lookup + } + yield from execute_requests(uid_parsers, self.pubkey) + + # If the uid wasn't found when looking for the pubkey + # We look for the uid and check for the pubkey + if not registered[0] and not registered[2]: + pubkey_parsers = { + bma.wot.CertifiersOf: _parse_pubkey_certifiers, + bma.wot.Lookup: _parse_pubkey_lookup + } + yield from execute_requests(pubkey_parsers, self.name) + + return registered + @asyncio.coroutine def send_selfcert(self, password, community): """ diff --git a/src/cutecoin/core/registry/identities.py b/src/cutecoin/core/registry/identities.py index fdb8ff10..5a97deb7 100644 --- a/src/cutecoin/core/registry/identities.py +++ b/src/cutecoin/core/registry/identities.py @@ -86,7 +86,8 @@ class IdentitiesRegistry: tries = 0 while tries < 3 and identity.local_state == LocalState.NOT_FOUND: try: - data = yield from community.bma_access.simple_request(bma.wot.CertifiersOf, req_args={'search': pubkey}) + data = yield from community.bma_access.simple_request(bma.wot.CertifiersOf, + req_args={'search': pubkey}) identity.uid = data['uid'] identity.local_state = LocalState.PARTIAL identity.blockchain_state = BlockchainState.VALIDATED diff --git a/src/cutecoin/gui/process_cfg_community.py b/src/cutecoin/gui/process_cfg_community.py index 2dd0cc19..8c4496c5 100644 --- a/src/cutecoin/gui/process_cfg_community.py +++ b/src/cutecoin/gui/process_cfg_community.py @@ -16,6 +16,7 @@ from ..models.peering import PeeringTreeModel from ..core import Community from ..core.registry.identity import BlockchainState from ..core.net import Node +from ..tools.decorators import asyncify from . import toast @@ -62,9 +63,12 @@ class StepPageInit(Step): self.node = yield from Node.from_address(None, server, port) if self.node: community = Community.create(self.node) - identity = yield from self.app.identities_registry.future_find(self.account.pubkey, community) - if identity.blockchain_state == BlockchainState.NOT_FOUND: + registered = yield from self.account.check_registered(community) + if registered[0] is False and registered[2] is None: self.config_dialog.label_error.setText(self.tr("Could not find your identity on the network.")) + elif registered[0] is False and registered[2]: + self.config_dialog.label_error.setText(self.tr("""Your pubkey or UID is different on the network. +Yours : {0}, the network : {1}""".format(registered[1], registered[2]))) else: self.config_dialog.community = community self.config_dialog.next() @@ -84,8 +88,8 @@ class StepPageInit(Step): self.node = yield from Node.from_address(None, server, port) if self.node: community = Community.create(self.node) - identity = yield from self.app.identities_registry.future_find(self.account.pubkey, community) - if identity.blockchain_state == BlockchainState.NOT_FOUND: + registered = yield from self.account.check_registered(community) + if registered[0] is False and registered[2] is None: password = yield from self.password_asker.async_exec() if self.password_asker.result() == QDialog.Rejected: return @@ -102,10 +106,12 @@ class StepPageInit(Step): if self.app.preferences['notifications']: toast.display(self.tr("Error"), self.tr("{0}".format(result[1]))) QApplication.restoreOverrideCursor() - self.config_dialog.community = community + elif registered[0] is False and registered[2]: + self.config_dialog.label_error.setText(self.tr("""Your pubkey or UID was already found on the network. +Yours : {0}, the network : {1}""".format(registered[1], registered[2]))) else: - self.config_dialog.label_error.setText(self.tr("Pubkey already exists on the network")) + self.config_dialog.label_error.setText(self.tr("Your account already exists on the network")) else: self.config_dialog.label_error.setText(self.tr("Could not connect.")) diff --git a/src/cutecoin/tests/gui/process_cfg_community/test_add_community.py b/src/cutecoin/tests/gui/process_cfg_community/test_add_community.py index 52b8a97f..1e729591 100644 --- a/src/cutecoin/tests/gui/process_cfg_community/test_add_community.py +++ b/src/cutecoin/tests/gui/process_cfg_community/test_add_community.py @@ -24,7 +24,7 @@ class ProcessAddCommunity(unittest.TestCase): QLocale.setDefault(QLocale("en_GB")) self.lp = quamash.QEventLoop(self.qapplication) asyncio.set_event_loop(self.lp) - self.lp.set_exception_handler(lambda lp, ctx : unitttest_exception_handler(self, lp, ctx)) + #self.lp.set_exception_handler(lambda lp, ctx : unitttest_exception_handler(self, lp, ctx)) self.identities_registry = IdentitiesRegistry({}) self.application = Application(self.qapplication, self.lp, self.identities_registry) @@ -32,7 +32,7 @@ class ProcessAddCommunity(unittest.TestCase): # Salt/password : "testcutecoin/testcutecoin" # Pubkey : 7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ self.account = Account("testcutecoin", "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", - "test", [], [], [], self.identities_registry) + "john", [], [], [], self.identities_registry) self.password_asker = PasswordAskerDialog(self.account) self.password_asker.password = "testcutecoin" self.password_asker.remember = True @@ -52,11 +52,6 @@ class ProcessAddCommunity(unittest.TestCase): self.account, None, self.password_asker) - @asyncio.coroutine - def open_dialog(process_community): - result = yield from process_community.async_exec() - self.assertEqual(result, QDialog.Accepted) - def close_dialog(): if process_community.isVisible(): process_community.close() @@ -84,10 +79,17 @@ class ProcessAddCommunity(unittest.TestCase): self.assertEqual(mock.get_request(i).method, 'GET') self.assertEqual(mock.get_request(i).url, '/wot/lookup/7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ') - yield from asyncio.sleep(1) + yield from asyncio.sleep(5) + self.assertEqual(mock.get_request(5).method, 'GET') + self.assertEqual(mock.get_request(5).url, + '/wot/certifiers-of/john') + for i in range(6, 9): + self.assertEqual(mock.get_request(i).method, 'GET') + self.assertEqual(mock.get_request(i).url, + '/wot/lookup/john') - self.assertEqual(mock.get_request(5).method, 'POST') - self.assertEqual(mock.get_request(5).url[:8], '/wot/add') + self.assertEqual(mock.get_request(9).url[:8], '/wot/add') + self.assertEqual(mock.get_request(9).method, 'POST') self.assertEqual(process_community.label_error.text(), "Broadcasting identity...") yield from asyncio.sleep(1) @@ -98,7 +100,8 @@ class ProcessAddCommunity(unittest.TestCase): self.lp.call_later(15, close_dialog) asyncio.async(exec_test()) - self.lp.run_until_complete(open_dialog(process_community)) + self.lp.run_until_complete(process_community.async_exec()) + self.assertEqual(process_community.result(), QDialog.Accepted) mock.delete_mock() def test_connect_community_empty_blockchain(self): @@ -110,11 +113,6 @@ class ProcessAddCommunity(unittest.TestCase): self.account, None, self.password_asker) - @asyncio.coroutine - def open_dialog(process_community): - result = yield from process_community.async_exec() - self.assertEqual(result, QDialog.Rejected) - def close_dialog(): if process_community.isVisible(): process_community.close() @@ -132,9 +130,11 @@ class ProcessAddCommunity(unittest.TestCase): self.assertEqual(process_community.lineedit_server.text(), "127.0.0.1") self.assertEqual(process_community.spinbox_port.value(), 50000) QTest.mouseClick(process_community.button_connect, Qt.LeftButton) - yield from asyncio.sleep(1) + yield from asyncio.sleep(3) + self.assertNotEqual(mock.get_request(0), None) self.assertEqual(mock.get_request(0).method, 'GET') self.assertEqual(mock.get_request(0).url, '/network/peering') + self.assertNotEqual(mock.get_request(1), None) self.assertEqual(mock.get_request(1).method, 'GET') self.assertEqual(mock.get_request(1).url, '/wot/certifiers-of/7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ') @@ -150,22 +150,107 @@ class ProcessAddCommunity(unittest.TestCase): self.lp.call_later(15, close_dialog) asyncio.async(exec_test()) - self.lp.run_until_complete(open_dialog(process_community)) + self.lp.run_until_complete(process_community.async_exec()) mock.delete_mock() - def test_connect_community_nice_blockchain(self): + def test_connect_community_wrong_pubkey(self): mock = nice_blockchain.get_mock() time.sleep(2) logging.debug(mock.pretend_url) API.reverse_url = pretender_reversed(mock.pretend_url) + self.account.pubkey = "wrong_pubkey" process_community = ProcessConfigureCommunity(self.application, self.account, None, self.password_asker) + def close_dialog(): + if process_community.isVisible(): + process_community.close() + @asyncio.coroutine - def open_dialog(process_community): - result = yield from process_community.async_exec() - self.assertEqual(result, QDialog.Accepted) + def exec_test(): + yield from asyncio.sleep(1) + QTest.mouseClick(process_community.lineedit_server, Qt.LeftButton) + QTest.keyClicks(process_community.lineedit_server, "127.0.0.1") + QTest.mouseDClick(process_community.spinbox_port, Qt.LeftButton) + process_community.spinbox_port.setValue(50000) + self.assertEqual(process_community.stacked_pages.currentWidget(), + process_community.page_node, + msg="Current widget : {0}".format(process_community.stacked_pages.currentWidget().objectName())) + self.assertEqual(process_community.lineedit_server.text(), "127.0.0.1") + self.assertEqual(process_community.spinbox_port.value(), 50000) + QTest.mouseClick(process_community.button_connect, Qt.LeftButton) + yield from asyncio.sleep(1) + self.assertNotEqual(mock.get_request(0), None) + self.assertEqual(mock.get_request(0).method, 'GET') + self.assertEqual(mock.get_request(0).url, '/network/peering') + self.assertNotEqual(mock.get_request(1), None) + self.assertEqual(mock.get_request(1).method, 'GET') + self.assertEqual(mock.get_request(1).url, + '/wot/certifiers-of/wrong_pubkey') + self.assertEqual(process_community.label_error.text(), """Your pubkey or UID is different on the network. +Yours : wrong_pubkey, the network : 7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ""") + process_community.close() + + self.lp.call_later(15, close_dialog) + asyncio.async(exec_test()) + self.lp.run_until_complete(process_community.async_exec()) + self.assertEqual(process_community.result(), QDialog.Rejected) + mock.delete_mock() + + def test_connect_community_wrong_uid(self): + mock = nice_blockchain.get_mock() + time.sleep(2) + logging.debug(mock.pretend_url) + API.reverse_url = pretender_reversed(mock.pretend_url) + self.account.name = "wrong_uid" + process_community = ProcessConfigureCommunity(self.application, + self.account, + None, self.password_asker) + + def close_dialog(): + if process_community.isVisible(): + process_community.close() + + @asyncio.coroutine + def exec_test(): + yield from asyncio.sleep(1) + QTest.mouseClick(process_community.lineedit_server, Qt.LeftButton) + QTest.keyClicks(process_community.lineedit_server, "127.0.0.1") + QTest.mouseDClick(process_community.spinbox_port, Qt.LeftButton) + process_community.spinbox_port.setValue(50000) + self.assertEqual(process_community.stacked_pages.currentWidget(), + process_community.page_node, + msg="Current widget : {0}".format(process_community.stacked_pages.currentWidget().objectName())) + self.assertEqual(process_community.lineedit_server.text(), "127.0.0.1") + self.assertEqual(process_community.spinbox_port.value(), 50000) + QTest.mouseClick(process_community.button_connect, Qt.LeftButton) + yield from asyncio.sleep(1) + self.assertNotEqual(mock.get_request(0), None) + self.assertEqual(mock.get_request(0).method, 'GET') + self.assertEqual(mock.get_request(0).url, '/network/peering') + self.assertNotEqual(mock.get_request(1), None) + self.assertEqual(mock.get_request(1).method, 'GET') + self.assertEqual(mock.get_request(1).url, + '/wot/certifiers-of/7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ') + self.assertEqual(process_community.label_error.text(), """Your pubkey or UID is different on the network. +Yours : wrong_uid, the network : john""") + process_community.close() + + self.lp.call_later(15, close_dialog) + asyncio.async(exec_test()) + self.lp.run_until_complete(process_community.async_exec()) + self.assertEqual(process_community.result(), QDialog.Rejected) + mock.delete_mock() + + def test_connect_community_success(self): + mock = nice_blockchain.get_mock() + time.sleep(2) + logging.debug(mock.pretend_url) + API.reverse_url = pretender_reversed(mock.pretend_url) + process_community = ProcessConfigureCommunity(self.application, + self.account, + None, self.password_asker) def close_dialog(): if process_community.isVisible(): @@ -185,8 +270,10 @@ class ProcessAddCommunity(unittest.TestCase): self.assertEqual(process_community.spinbox_port.value(), 50000) QTest.mouseClick(process_community.button_connect, Qt.LeftButton) yield from asyncio.sleep(1) + self.assertNotEqual(mock.get_request(0), None) self.assertEqual(mock.get_request(0).method, 'GET') self.assertEqual(mock.get_request(0).url, '/network/peering') + self.assertNotEqual(mock.get_request(1), None) self.assertEqual(mock.get_request(1).method, 'GET') self.assertEqual(mock.get_request(1).url, '/wot/certifiers-of/7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ') @@ -197,7 +284,7 @@ class ProcessAddCommunity(unittest.TestCase): self.lp.call_later(15, close_dialog) asyncio.async(exec_test()) - self.lp.run_until_complete(open_dialog(process_community)) + self.lp.run_until_complete(process_community.async_exec()) mock.delete_mock() if __name__ == '__main__': diff --git a/src/cutecoin/tests/qapp.py b/src/cutecoin/tests/qapp.py index 0de485b7..708b11fa 100644 --- a/src/cutecoin/tests/qapp.py +++ b/src/cutecoin/tests/qapp.py @@ -24,7 +24,7 @@ def unitttest_exception_handler(test, loop, context): for key in [k for k in sorted(context) if k not in {'message', 'exception'}]: log_lines.append('{}: {!r}'.format(key, context[key])) - test.failureException('\n'.join(log_lines)) + test.fail('\n'.join(log_lines)) -- GitLab