diff --git a/src/cutecoin/core/account.py b/src/cutecoin/core/account.py index 0471dea12bd4475b3925703eceda7ddeeaeb5ade..bf88aeb934fd5eb1eb2adfb2e982b32ac79103df 100644 --- a/src/cutecoin/core/account.py +++ b/src/cutecoin/core/account.py @@ -18,7 +18,7 @@ from PyQt5.QtCore import QObject, pyqtSignal, QCoreApplication, QT_TRANSLATE_NOO from .wallet import Wallet from .community import Community -from .person import Person +from .registry import Identity, IdentitiesRegistry from ..tools.exceptions import ContactAlreadyExists @@ -121,7 +121,7 @@ class Account(QObject): inner_data_changed = pyqtSignal() wallets_changed = pyqtSignal() - def __init__(self, salt, pubkey, name, communities, wallets, contacts): + def __init__(self, salt, pubkey, name, communities, wallets, contacts, identities_registry): ''' Create an account @@ -133,6 +133,7 @@ class Account(QObject): :param array communities: Community objects referenced by this account :param array wallets: Wallet objects owned by this account :param array contacts: Contacts of this account + :param cutecoin.core.registry.IdentitiesRegistry: The identities registry intance .. warnings:: The class methods create and load should be used to create an account ''' @@ -143,10 +144,11 @@ class Account(QObject): self.communities = communities self.wallets = wallets self.contacts = contacts + self._identities_registry = identities_registry self.referential = 0 @classmethod - def create(cls, name): + def create(cls, name, identities_registry): ''' Factory method to create an empty account object This new account doesn't have any key and it should be given @@ -158,15 +160,17 @@ class Account(QObject): :param str name: The account name, same as network identity uid :return: A new empty account object ''' - account = cls(None, None, name, [], [], []) + account = cls(None, None, name, [], [], [], identities_registry) return account @classmethod - def load(cls, network_manager, json_data): + def load(cls, json_data, network_manager, identities_registry): ''' Factory method to create an Account object from its json view. :rtype : cutecoin.core.account.Account :param dict json_data: The account view as a json dict + :param PyQt5.QtNetwork import QNetworkManager: network_manager + :param cutecoin.core.registry.self._identities_registry: identities_registry :return: A new account object created from the json datas ''' salt = json_data['salt'] @@ -180,7 +184,7 @@ class Account(QObject): wallets = [] for data in json_data['wallets']: - wallets.append(Wallet.load(data)) + wallets.append(Wallet.load(data, identities_registry)) communities = [] for data in json_data['communities']: @@ -188,7 +192,7 @@ class Account(QObject): communities.append(community) account = cls(salt, pubkey, name, communities, wallets, - contacts) + contacts, identities_registry) return account def __eq__(self, other): @@ -229,12 +233,12 @@ class Account(QObject): return community def refresh_cache(self): - ''' + """ Refresh the local account cache This needs n_wallets * n_communities cache refreshing to end .. note:: emit the Account pyqtSignal loading_progressed during refresh - ''' + """ loaded_wallets = 0 def progressing(value, maximum): @@ -288,7 +292,7 @@ class Account(QObject): if len(self.wallets) < size: for i in range(len(self.wallets), size): wallet = Wallet.create(i, self.salt, password, - "Wallet {0}".format(i)) + "Wallet {0}".format(i), self._identities_registry) self.wallets.append(wallet) else: self.wallets = self.wallets[:size] @@ -302,7 +306,7 @@ class Account(QObject): :param cutecoin.core.community.Community community: The community target of the certification :param str pubkey: The certified identity pubkey """ - certified = Person.lookup(pubkey, community) + certified = self._identities_registry.lookup(pubkey, community) blockid = community.current_blockid() certification = Certification(PROTOCOL_VERSION, community.currency, @@ -330,7 +334,7 @@ class Account(QObject): :param str password: The account SigningKey password :param cutecoin.core.community.Community community: The community target of the revocation """ - revoked = Person.lookup(self.pubkey, community) + revoked = self._identities_registry.lookup(self.pubkey, community) revocation = Revocation(PROTOCOL_VERSION, community.currency, None) @@ -383,7 +387,7 @@ class Account(QObject): :param community: The target community of this request :return: True if the account is a member of the target community ''' - self_person = Person.lookup(self.pubkey, community) + self_person = self._identities_registry.lookup(self.pubkey, community) return self_person.published_uid(community) def member_of(self, community): @@ -393,7 +397,7 @@ class Account(QObject): :param community: The target community of this request :return: True if the account is a member of the target community ''' - self_person = Person.lookup(self.pubkey, community) + self_person = self._identities_registry.lookup(self.pubkey, community) logging.debug("Self person : {0}".format(self_person.uid)) return self_person.is_member(community) @@ -425,7 +429,7 @@ class Account(QObject): :param community: The community target of the membership document :param str mstype: The type of membership demand. "IN" to join, "OUT" to leave ''' - self_ = Person.lookup(self.pubkey, community) + self_ = self._identities_registry.lookup(self.pubkey, community) selfcert = self_.selfcert(community) blockid = community.current_blockid() diff --git a/src/cutecoin/core/app.py b/src/cutecoin/core/app.py index 3a5e5ad60b153ea6724ce6fc66c24bd628c0733d..ea72eb6830efdad0f6057758a0f324004f9fd39a 100644 --- a/src/cutecoin/core/app.py +++ b/src/cutecoin/core/app.py @@ -18,7 +18,7 @@ from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QNetworkReques from . import config from .account import Account -from . import person +from .registry.identities import IdentitiesRegistry from .. import __version__ from ..tools.exceptions import NameAlreadyExists, BadAccountFile @@ -46,8 +46,10 @@ class Application(QObject): self.available_version = (True, __version__, "") + self.identity_registry = None config.parse_arguments(argv) self._network_manager = QNetworkAccessManager() + self._identities_registry = IdentitiesRegistry() self.preferences = {'account': "", 'lang': 'en_GB', 'ref': 0 @@ -94,6 +96,10 @@ class Application(QObject): return account + @property + def identities_registry(self): + return self._identities_registry + def add_account(self, account): self.accounts[account.name] = account @@ -136,7 +142,7 @@ class Application(QObject): If the standard application state file can't be found, no error is raised. ''' - self.load_persons() + self.load_registries() self.load_preferences() try: logging.debug("Loading data...") @@ -147,17 +153,17 @@ class Application(QObject): except FileNotFoundError: pass - def load_persons(self): + def load_registries(self): ''' Load the Person instances of the person module. Each instance is unique, and can be find by its public key. ''' try: - persons_path = os.path.join(config.parameters['home'], - '__persons__') - with open(persons_path, 'r') as persons_path: - data = json.load(persons_path) - person.load_cache(data) + identities_path = os.path.join(config.parameters['home'], + '__identities__') + with open(identities_path, 'r') as identities_data: + data = json.load(identities_data) + self._identities_registry.load_json(data) except FileNotFoundError: pass @@ -171,7 +177,7 @@ class Application(QObject): account_name, 'properties') with open(account_path, 'r') as json_data: data = json.load(json_data) - account = Account.load(self._network_manager, data) + account = Account.load(data, self._network_manager, self._identities_registry) self.load_cache(account) self.accounts[account_name] = account @@ -269,14 +275,14 @@ class Application(QObject): account_path = os.path.join(config.parameters['home'], account.name) shutil.rmtree(account_path) - def save_persons(self): - ''' - Save the person module cache - ''' - persons_path = os.path.join(config.parameters['home'], - '__persons__') - with open(persons_path, 'w')as outfile: - data = person.jsonify_cache() + def save_registries(self): + """ + Save the registries + """ + identities_path = os.path.join(config.parameters['home'], + '__identities__') + with open(identities_path, 'w')as outfile: + data = self.identities_registry.jsonify() data['version'] = __version__ json.dump(data, outfile, indent=4, sort_keys=True) diff --git a/src/cutecoin/core/graph.py b/src/cutecoin/core/graph.py index 01b8b4af412fd1aa1ac9bda6fb3b441fdfcabcea..a4066087d59f6447b457278cebb35160b5651668 100644 --- a/src/cutecoin/core/graph.py +++ b/src/cutecoin/core/graph.py @@ -2,7 +2,7 @@ import logging import time import datetime from PyQt5.QtCore import QLocale, QDateTime -from cutecoin.core.person import Person +from ..core.registry import Identity from cutecoin.gui.views.wot import NODE_STATUS_HIGHLIGHTED, NODE_STATUS_OUT, ARC_STATUS_STRONG, ARC_STATUS_WEAK diff --git a/src/cutecoin/core/net/api/bma/__init__.py b/src/cutecoin/core/net/api/bma/__init__.py index ce3f0b46dbb87fa4204dba32cf7df8764bbe5b03..e00fc5695fe30d2c25cd65a49d11651ca6ee0df1 100644 --- a/src/cutecoin/core/net/api/bma/__init__.py +++ b/src/cutecoin/core/net/api/bma/__init__.py @@ -8,6 +8,7 @@ import logging logger = logging.getLogger("ucoin") +PROTOCOL_VERSION = "1" class ConnectionHandler(object): """Helper class used by other API classes to ease passing server connection information.""" diff --git a/src/cutecoin/core/net/api/bma/access.py b/src/cutecoin/core/net/api/bma/access.py index 4c7fc64aec18dc62a5f43aee2678077c979ed3af..b18a977e42014453bde88fa4f9c536e0a5f14526 100644 --- a/src/cutecoin/core/net/api/bma/access.py +++ b/src/cutecoin/core/net/api/bma/access.py @@ -65,7 +65,7 @@ class BmaAccess(QObject): 'value': data[d]}) return entries - def get(self, caller, request, req_args={}, get_args={}): + def get(self, caller, request, req_args={}, get_args={}, tries=0): """ Get Json data from the specified URL :rtype : dict @@ -94,16 +94,16 @@ class BmaAccess(QObject): #after removing qthreads reply = self.request(request, req_args, get_args) reply.finished.connect(lambda: - self.handle_reply(caller, request, req_args, get_args)) + self.handle_reply(caller, request, req_args, get_args, tries)) return ret_data def request(self, request, req_args={}, get_args={}): ''' Start a request to the network. - :param request: A bma request class calling for data - :param req_args: Arguments to pass to the request constructor - :param get_args: Arguments to pass to the request __get__ method + :param class request: A bma request class calling for data + :param dict req_args: Arguments to pass to the request constructor + :param dict get_args: Arguments to pass to the request __get__ method :return: The returned data if cached = True else return the QNetworkReply ''' nodes = self._network.synced_nodes @@ -119,7 +119,7 @@ class BmaAccess(QObject): raise NoPeerAvailable(self.currency, len(nodes)) @pyqtSlot(int, dict, dict, QObject) - def handle_reply(self, caller, request, req_args, get_args): + def handle_reply(self, caller, request, req_args, get_args, tries): reply = self.sender() #logging.debug("Handling QtNetworkReply for {0}".format(str(request))) if reply.error() == QNetworkReply.NoError: @@ -144,10 +144,10 @@ class BmaAccess(QObject): change = True else: change = True - - if change == True: + if change: self._data[cache_key]['value'] = json.loads(strdata) caller.inner_data_changed.emit(request) else: logging.debug("Error in reply : {0}".format(reply.error())) - self.community.qtrequest(caller, request, req_args, get_args) + if tries < 3: + self.get(caller, request, req_args, get_args) diff --git a/src/cutecoin/core/net/api/bma/blockchain/__init__.py b/src/cutecoin/core/net/api/bma/blockchain/__init__.py index 6952dc6d8232fb31eb6cb376dcdb790febd83b35..eecdfa3de4798cc7a24ef953e7ff87bea95fed7c 100644 --- a/src/cutecoin/core/net/api/bma/blockchain/__init__.py +++ b/src/cutecoin/core/net/api/bma/blockchain/__init__.py @@ -33,26 +33,35 @@ class Parameters(Blockchain): return self.requests_get('/parameters', **kwargs) null_value = { - 'currency': "", - 'c': 0, - 'dt': 0, - 'ud0': 0, - 'sigDelay': 0, - 'sigValidity': 0, - 'sigQty': 0, - 'sigWoT': 0, - 'msValidity': 0, - 'stepMax': 0, - 'medianTimeBlocks': 0, - 'avgGenTime': 0, - 'dtDiffEval': 0, - 'blocksRot': 0, - 'percentRot': 0 - } + 'currency': "", + 'c': 0, + 'dt': 0, + 'ud0': 0, + 'sigDelay': 0, + 'sigValidity': 0, + 'sigQty': 0, + 'sigWoT': 0, + 'msValidity': 0, + 'stepMax': 0, + 'medianTimeBlocks': 0, + 'avgGenTime': 0, + 'dtDiffEval': 0, + 'blocksRot': 0, + 'percentRot': 0 + } class Membership(Blockchain): """GET/POST a Membership document.""" + + null_value = \ + { + "pubkey": "", + "uid": "", + "sigDate": 0, + "memberships": [] + } + def __init__(self, conn_handler, search=None): super().__init__(conn_handler) self.search = search @@ -70,31 +79,31 @@ class Membership(Blockchain): class Block(Blockchain): """GET/POST a block from/to the blockchain.""" null_value = { - "version": 1, - "nonce": 0, - "number": -1, - "powMin": 0, - "time": 0, - "medianTime": 0, - "membersCount": 0, - "monetaryMass": 0, - "currency": "", - "issuer": "", - "signature": "", - "hash": "", - "previousHash": "", - "previousIssuer": "", - "dividend": 0, - "membersChanges": [ ], - "identities": [], - "joiners": [], - "actives": [], - "leavers": [], - "excluded": [], - "certifications": [], - "transactions": [], - "raw": "" - } + "version": 1, + "nonce": 0, + "number": -1, + "powMin": 0, + "time": 0, + "medianTime": 0, + "membersCount": 0, + "monetaryMass": 0, + "currency": "", + "issuer": "", + "signature": "", + "hash": "", + "previousHash": "", + "previousIssuer": "", + "dividend": 0, + "membersChanges": [], + "identities": [], + "joiners": [], + "actives": [], + "leavers": [], + "excluded": [], + "certifications": [], + "transactions": [], + "raw": "" + } def __init__(self, conn_handler, number=None): """ @@ -122,31 +131,32 @@ class Block(Blockchain): class Current(Blockchain): """GET, same as block/[number], but return last accepted block.""" null_value = { - "version": 1, - "nonce": 0, - "number": -1, - "powMin": 0, - "time": 0, - "medianTime": 0, - "membersCount": 0, - "monetaryMass": 0, - "currency": "", - "issuer": "", - "signature": "", - "hash": "", - "previousHash": None, - "previousIssuer": None, - "dividend": None, - "membersChanges": [ ], - "identities": [], - "joiners": [], - "actives": [], - "leavers": [], - "excluded": [], - "certifications": [], - "transactions": [], - "raw": "" - } + "version": 1, + "nonce": 0, + "number": -1, + "powMin": 0, + "time": 0, + "medianTime": 0, + "membersCount": 0, + "monetaryMass": 0, + "currency": "", + "issuer": "", + "signature": "", + "hash": "", + "previousHash": None, + "previousIssuer": None, + "dividend": None, + "membersChanges": [], + "identities": [], + "joiners": [], + "actives": [], + "leavers": [], + "excluded": [], + "certifications": [], + "transactions": [], + "raw": "" + } + def __get__(self, **kwargs): return self.requests_get('/current', **kwargs) @@ -215,11 +225,13 @@ class Excluded(Blockchain): class UD(Blockchain): """GET, return block numbers containing universal dividend.""" - null_value = { - "result": { - "blocks": [] + null_value = \ + { + "result": + { + "blocks": [] } - } + } def __get__(self, **kwargs): return self.requests_get('/with/ud', **kwargs) @@ -227,6 +239,13 @@ class UD(Blockchain): class TX(Blockchain): """GET, return block numbers containing transactions.""" + null_value = \ + { + "result": + { + "blocks": [] + } + } def __get__(self, **kwargs): return self.requests_get('/with/tx', **kwargs) diff --git a/src/cutecoin/core/net/api/bma/wot/__init__.py b/src/cutecoin/core/net/api/bma/wot/__init__.py index 9eb01e13d3ed26459adcb3fd5c0b877af3c36b37..745c5fef249b931eaaed1dc5f3679de8609ce34f 100644 --- a/src/cutecoin/core/net/api/bma/wot/__init__.py +++ b/src/cutecoin/core/net/api/bma/wot/__init__.py @@ -39,6 +39,11 @@ class Add(WOT): class Lookup(WOT): """GET Public key data.""" + null_value = \ + { + "partial": False, + "results": [] + } def __init__(self, conn_handler, search, module='wot'): super(WOT, self).__init__(conn_handler, module) @@ -100,6 +105,7 @@ class Members(WOT): { "results": [] } + def __init__(self, conn_handler, module='wot'): super(WOT, self).__init__(conn_handler, module) diff --git a/src/cutecoin/core/net/node.py b/src/cutecoin/core/net/node.py index c2c162e4860f4080ebf03192c09b193acc389876..f37776ae03fc9838748afaa4b789a4fd047427a9 100644 --- a/src/cutecoin/core/net/node.py +++ b/src/cutecoin/core/net/node.py @@ -6,8 +6,8 @@ Created on 21 févr. 2015 from ucoinpy.documents.peer import Peer, BMAEndpoint, Endpoint from requests.exceptions import RequestException, ConnectionError -from cutecoin.tools.exceptions import InvalidNodeCurrency, PersonNotFoundError -from cutecoin.core.person import Person +from cutecoin.tools.exceptions import InvalidNodeCurrency, LookupFailureError +from ..registry import IdentitiesRegistry from cutecoin.core.net.api import bma as qtbma from cutecoin.core.net.api.bma import ConnectionHandler diff --git a/src/cutecoin/core/person.py b/src/cutecoin/core/person.py deleted file mode 100644 index ac44769f48489a4b8e23ad27296717de4728b3b5..0000000000000000000000000000000000000000 --- a/src/cutecoin/core/person.py +++ /dev/null @@ -1,476 +0,0 @@ -''' -Created on 11 févr. 2014 - -@author: inso -''' - -import logging -import functools -import time - -from ucoinpy.api import bma -from ucoinpy import PROTOCOL_VERSION -from ucoinpy.documents.certification import SelfCertification -from ucoinpy.documents.membership import Membership -from ..tools.exceptions import Error, PersonNotFoundError,\ - MembershipNotFoundError, \ - NoPeerAvailable -from PyQt5.QtCore import QMutex - - -def load_cache(json_data): - for person_data in json_data['persons']: - person = Person.from_json(person_data) - Person._instances[person.pubkey] = person - - -def jsonify_cache(): - data = [] - for person in Person._instances.values(): - data.append(person.jsonify()) - return {'persons': data} - - -class cached(object): - ''' - Decorator. Caches a function's return value each time it is called. - If called later with the same arguments, the cached value is returned - (not reevaluated). - Delete it to clear it from the cache - ''' - def __init__(self, func): - self.func = func - - def __call__(self, inst, community): - inst._cache_mutex.lock() - try: - inst._cache[community.currency] - except KeyError: - inst._cache[community.currency] = {} - - try: - value = inst._cache[community.currency][self.func.__name__] - except KeyError: - value = self.func(inst, community) - inst._cache[community.currency][self.func.__name__] = value - - finally: - inst._cache_mutex.unlock() - - return value - - def __repr__(self): - '''Return the function's docstring.''' - return self.func.__repr__ - - def __get__(self, inst, objtype): - if inst is None: - return self.func - return functools.partial(self, inst) - - -#TODO: Change Person to Identity ? -class Person(object): - ''' - A person with a uid and a pubkey - ''' - _instances = {} - - def __init__(self, uid, pubkey, cache): - ''' - Initializing a person object. - - :param str uid: The person uid, also known as its uid on the network - :param str pubkey: The person pubkey - :param cache: The last returned values of the person properties. - ''' - super().__init__() - self.uid = uid - self.pubkey = pubkey - self._cache = cache - self._cache_mutex = QMutex() - - @classmethod - def lookup(cls, pubkey, community, cached=True): - ''' - Get a person from the pubkey found in a community - - :param str pubkey: The person pubkey - :param community: The community in which to look for the pubkey - :param bool cached: True if the person should be searched in the - cache before requesting the community. - - :return: A new person if the pubkey was unknown or\ - the known instance if pubkey was already known. - ''' - if cached and pubkey in Person._instances: - return Person._instances[pubkey] - else: - try: - data = community.request(bma.wot.Lookup, req_args={'search': pubkey}, - cached=cached) - except ValueError as e: - if '404' in str(e): - raise PersonNotFoundError(pubkey, community.name) - - timestamp = 0 - - for result in data['results']: - if result["pubkey"] == pubkey: - uids = result['uids'] - person_uid = "" - for uid_data in uids: - if uid_data["meta"]["timestamp"] > timestamp: - timestamp = uid_data["meta"]["timestamp"] - person_uid = uid_data["uid"] - - person = cls(person_uid, pubkey, {}) - Person._instances[pubkey] = person - logging.debug("{0}".format(Person._instances.keys())) - return person - raise PersonNotFoundError(pubkey, community.name) - - @classmethod - def from_metadata(cls, metadata): - ''' - Get a person from a metadata dict. - A metadata dict has a 'text' key corresponding to the person uid, - and a 'id' key corresponding to the person pubkey. - - :param dict metadata: The person metadata - :return: A new person if pubkey wasn't knwon, else the existing instance. - ''' - uid = metadata['text'] - pubkey = metadata['id'] - if pubkey in Person._instances: - return Person._instances[pubkey] - else: - person = cls(uid, pubkey, {}) - Person._instances[pubkey] = person - return person - - @classmethod - def from_json(cls, json_data): - ''' - Create a person from json data - - :param dict json_data: The person as a dict in json format - :return: A new person if pubkey wasn't known, else a new person instance. - ''' - pubkey = json_data['pubkey'] - if pubkey in Person._instances: - return Person._instances[pubkey] - else: - if 'name' in json_data: - uid = json_data['name'] - else: - uid = json_data['uid'] - if 'cache' in json_data: - cache = json_data['cache'] - else: - cache = {} - - person = cls(uid, pubkey, cache) - Person._instances[pubkey] = person - return person - - def selfcert(self, community): - ''' - Get the person self certification. - This request is not cached in the person object. - - :param community: The community target to request the self certification - :return: A SelfCertification ucoinpy object - ''' - data = community.request(bma.wot.Lookup, req_args={'search': self.pubkey}) - logging.debug(data) - timestamp = 0 - - 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"] - uid = uid_data["uid"] - signature = uid_data["self"] - - return SelfCertification(PROTOCOL_VERSION, - community.currency, - self.pubkey, - timestamp, - uid, - signature) - raise PersonNotFoundError(self.pubkey, community.name) - - @cached - def get_join_date(self, community): - ''' - Get the person join date. - This request is not cached in the person object. - - :param community: The community target to request the join date - :return: A datetime object - ''' - try: - search = community.request(bma.blockchain.Membership, {'search': self.pubkey}) - membership_data = None - if len(search['memberships']) > 0: - membership_data = search['memberships'][0] - return community.get_block(membership_data['blockNumber']).mediantime - else: - return None - except ValueError as e: - if '400' in str(e): - raise MembershipNotFoundError(self.pubkey, community.name) - except Exception as e: - logging.debug('bma.blockchain.Membership request error : ' + str(e)) - raise MembershipNotFoundError(self.pubkey, community.name) - -#TODO: Manage 'OUT' memberships ? Maybe ? - @cached - def membership(self, community): - ''' - Get the person last membership document. - - :param community: The community target to request the join date - :return: The membership data in BMA json format - ''' - try: - search = community.request(bma.blockchain.Membership, - {'search': self.pubkey}) - block_number = -1 - for ms in search['memberships']: - if ms['blockNumber'] > block_number: - block_number = ms['blockNumber'] - if 'type' in ms: - if ms['type'] is 'IN': - membership_data = ms - else: - membership_data = ms - - if membership_data is None: - raise MembershipNotFoundError(self.pubkey, community.name) - except ValueError as e: - if '400' in str(e): - raise MembershipNotFoundError(self.pubkey, community.name) - except Exception as e: - logging.debug('bma.blockchain.Membership request error : ' + str(e)) - raise MembershipNotFoundError(self.pubkey, community.name) - - return membership_data - - @cached - def published_uid(self, community): - try: - data = community.request(bma.wot.Lookup, - req_args={'search': self.pubkey}, - cached=cached) - except ValueError as e: - if '404' in str(e): - return False - - timestamp = 0 - - for result in data['results']: - if result["pubkey"] == self.pubkey: - uids = result['uids'] - person_uid = "" - for uid_data in uids: - if uid_data["meta"]["timestamp"] > timestamp: - timestamp = uid_data["meta"]["timestamp"] - person_uid = uid_data["uid"] - if person_uid == self.uid: - return True - return False - - @cached - def is_member(self, community): - ''' - Check if the person is a member of a community - - :param community: The community target to request the join date - :return: True if the person is a member of a community - ''' - try: - certifiers = community.request(bma.wot.CertifiersOf, {'search': self.pubkey}) - return certifiers['isMember'] - except ValueError: - return False - except Exception as e: - logging.debug('bma.wot.CertifiersOf request error : ' + str(e)) - return False - - @cached - def certifiers_of(self, community): - ''' - Get the list of this person certifiers - - :param community: The community target to request the join date - :return: The list of the certifiers of this community in BMA json format - ''' - try: - certifiers = community.request(bma.wot.CertifiersOf, {'search': self.pubkey}) - except ValueError as e: - logging.debug('bma.wot.CertifiersOf request ValueError : ' + str(e)) - try: - data = community.request(bma.wot.Lookup, {'search': self.pubkey}) - except ValueError as e: - logging.debug('bma.wot.Lookup request ValueError : ' + str(e)) - return list() - - # convert api data to certifiers list - certifiers = list() - # add certifiers of uid - - for result in data['results']: - if result["pubkey"] == self.pubkey: - for uid_data in result['uids']: - for certifier_data in uid_data['others']: - for uid in certifier_data['uids']: - # add a certifier - certifier = {} - certifier['uid'] = uid - certifier['pubkey'] = certifier_data['pubkey'] - certifier['isMember'] = certifier_data['isMember'] - certifier['cert_time'] = dict() - certifier['cert_time']['medianTime'] = community.get_block(certifier_data['meta']['block_number']).mediantime - certifiers.append(certifier) - - return certifiers - - except Exception as e: - logging.debug('bma.wot.CertifiersOf request error : ' + str(e)) - return list() - - return certifiers['certifications'] - - def unique_valid_certifiers_of(self, community): - certifier_list = self.certifiers_of(community) - unique_valid = [] - #  add certifiers of uid - for certifier in tuple(certifier_list): - # add only valid certification... - if community.certification_expired(certifier['cert_time']['medianTime']): - continue - - # keep only the latest certification - already_found = [c['pubkey'] for c in unique_valid] - if certifier['pubkey'] in already_found: - index = already_found.index(certifier['pubkey']) - if certifier['cert_time']['medianTime'] > unique_valid[index]['cert_time']['medianTime']: - unique_valid[index] = certifier - else: - unique_valid.append(certifier) - return unique_valid - - @cached - def certified_by(self, community): - ''' - Get the list of persons certified by this person - - :param community: The community target to request the join date - :return: The list of the certified persons of this community in BMA json format - ''' - try: - certified_list = community.request(bma.wot.CertifiedBy, {'search': self.pubkey}) - except ValueError as e: - logging.debug('bma.wot.CertifiersOf request ValueError : ' + str(e)) - try: - data = community.request(bma.wot.Lookup, {'search': self.pubkey}) - except ValueError as e: - logging.debug('bma.wot.Lookup request ValueError : ' + str(e)) - return list() - - certified_list = list() - for result in data['results']: - if result["pubkey"] == self.pubkey: - for certified in result['signed']: - certified['cert_time'] = dict() - certified['cert_time']['medianTime'] = certified['meta']['timestamp'] - certified_list.append(certified) - - return certified_list - - except Exception as e: - logging.debug('bma.wot.CertifiersOf request error : ' + str(e)) - return list() - - return certified_list['certifications'] - - def unique_valid_certified_by(self, community): - certified_list = self.certified_by(community) - unique_valid = [] - #  add certifiers of uid - for certified in tuple(certified_list): - # add only valid certification... - if community.certification_expired(certified['cert_time']['medianTime']): - continue - - # keep only the latest certification - already_found = [c['pubkey'] for c in unique_valid] - if certified['pubkey'] in already_found: - index = already_found.index(certified['pubkey']) - if certified['cert_time']['medianTime'] > unique_valid[index]['cert_time']['medianTime']: - unique_valid[index] = certified - else: - unique_valid.append(certified) - return unique_valid - - def membership_expiration_time(self, community): - join_block = self.membership(community)['blockNumber'] - join_date = community.get_block(join_block)['medianTime'] - parameters = community.parameters - expiration_date = join_date + parameters['sigValidity'] - current_time = time.time() - return expiration_date - current_time - - def reload(self, func, community): - ''' - Reload a cached property of this person in a community. - This method is thread safe. - This method clears the cache entry for this community and get it back. - - :param func: The cached property to reload - :param community: The community to request for data - :return: True if a changed was made by the reload. - ''' - self._cache_mutex.lock() - change = False - try: - if community.currency not in self._cache: - self._cache[community.currency] = {} - - try: - before = self._cache[community.currency][func.__name__] - except KeyError: - change = True - - try: - value = func(self, community) - - if not change: - if type(value) is dict: - hash_before = (str(tuple(frozenset(sorted(before.keys())))), - str(tuple(frozenset(sorted(before.items()))))) - hash_after = (str(tuple(frozenset(sorted(value.keys())))), - str(tuple(frozenset(sorted(value.items()))))) - change = hash_before != hash_after - elif type(value) is bool: - change = before != value - self._cache[community.currency][func.__name__] = value - except Error: - return False - finally: - self._cache_mutex.unlock() - return change - - def jsonify(self): - ''' - Get the community as dict in json format. - :return: The community as a dict in json format - ''' - data = {'uid': self.uid, - 'pubkey': self.pubkey, - 'cache': self._cache} - return data diff --git a/src/cutecoin/core/registry/__init__.py b/src/cutecoin/core/registry/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4ab046a19ff274331c3bc1ffd25e16bd19985478 --- /dev/null +++ b/src/cutecoin/core/registry/__init__.py @@ -0,0 +1,2 @@ +from .identities import IdentitiesRegistry +from .identity import Identity \ No newline at end of file diff --git a/src/cutecoin/core/registry/identities.py b/src/cutecoin/core/registry/identities.py new file mode 100644 index 0000000000000000000000000000000000000000..b23b8c41ac998bba3ccea33da5ed9593b86ce281 --- /dev/null +++ b/src/cutecoin/core/registry/identities.py @@ -0,0 +1,102 @@ +from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal + +from cutecoin.core.net.api import bma as qtbma +from .identity import Identity + +import json + + +class IdentitiesRegistry: + """ + Core class to handle identities lookup + """ + def __init__(self, instances={}): + """ + Initializer of the IdentitiesRegistry + + :param list of Identity instances: + :return: An IdentitiesRegistry object + :rtype: IdentitiesRegistry + """ + self._instances = instances + + def load_json(self, json_data): + """ + Load json data + + :param dict json_data: The identities in json format + """ + instances = {} + + for person_data in json_data['registry']: + pubkey = person_data['pubkey'] + if pubkey not in instances: + person = Identity.from_json(person_data) + instances[person.pubkey] = person + self._instances = instances + + def jsonify(self): + identities_json = [] + for identity in self._instances.values(): + identities_json.append(identity.jsonify()) + return {'registry': identities_json} + + def lookup(self, pubkey, community): + """ + Get a person from the pubkey found in a community + + :param str pubkey: The person pubkey + :param cutecoin.core.community.Community community: The community in which to look for the pubkey + :param bool cached: True if the person should be searched in the + cache before requesting the community. + + :return: A new person if the pubkey was unknown or\ + the known instance if pubkey was already known. + """ + if pubkey in self._instances: + identity = self._instances[pubkey] + else: + identity = Identity.empty(pubkey) + self._instances[pubkey] = identity + reply = community.bma_access.request(qtbma.wot.Lookup, req_args={'search': pubkey}) + reply.finished.connect(lambda: self.handle_lookup(reply, identity)) + return identity + + def handle_lookup(self, reply, identity): + """ + :param cutecoin.core.registry.identity.Identity identity: The looked up identity + :return: + """ + strdata = bytes(reply.readAll()).decode('utf-8') + data = json.loads(strdata) + + timestamp = 0 + for result in data['results']: + if result["pubkey"] == identity.pubkey: + uids = result['uids'] + identity_uid = "" + for uid_data in uids: + if uid_data["meta"]["timestamp"] > timestamp: + timestamp = uid_data["meta"]["timestamp"] + identity_uid = uid_data["uid"] + identity.uid = identity_uid + identity.status = Identity.FOUND + identity.inner_data_changed.emit(qtbma.wot.Lookup) + return + + def from_metadata(self, metadata): + """ + Get a person from a metadata dict. + A metadata dict has a 'text' key corresponding to the person uid, + and a 'id' key corresponding to the person pubkey. + + :param dict metadata: The person metadata + :return: A new person if pubkey wasn't knwon, else the existing instance. + """ + pubkey = metadata['id'] + if pubkey in self._instances: + return self._instances[pubkey] + else: + identity = Identity.from_metadata(metadata) + self._instances[pubkey] = identity + return identity diff --git a/src/cutecoin/core/registry/identity.py b/src/cutecoin/core/registry/identity.py new file mode 100644 index 0000000000000000000000000000000000000000..3505cd6b20d67c0fd7f5c40c0cd19e7f4e31923c --- /dev/null +++ b/src/cutecoin/core/registry/identity.py @@ -0,0 +1,282 @@ +''' +Created on 11 févr. 2014 + +@author: inso +''' + +import logging +import time + +from ucoinpy.documents.certification import SelfCertification +from cutecoin.tools.exceptions import Error, LookupFailureError,\ + MembershipNotFoundError +from cutecoin.core.net.api import bma as qtbma +from cutecoin.core.net.api.bma import PROTOCOL_VERSION +from PyQt5.QtCore import QObject, pyqtSignal + + +class Identity(QObject): + """ + A person with a uid and a pubkey + """ + FOUND = 1 + NOT_FOUND = 0 + + inner_data_changed = pyqtSignal(int) + + def __init__(self, uid, pubkey, status): + """ + Initializing a person object. + + :param str uid: The person uid, also known as its uid on the network + :param str pubkey: The person pubkey + :param int status: The local status of the identity + """ + super().__init__() + assert(status in (Identity.FOUND, Identity.NOT_FOUND)) + self.uid = uid + self.pubkey = pubkey + self.status = status + + @classmethod + def empty(cls, pubkey): + return cls("", pubkey, Identity.NOT_FOUND) + + @classmethod + def from_metadata(cls, metadata): + return cls(metadata["text"], metadata["id"], Identity.NOT_FOUND) + + @classmethod + def from_json(cls, json_data): + """ + Create a person from json data + + :param dict json_data: The person as a dict in json format + :return: A new person if pubkey wasn't known, else a new person instance. + """ + pubkey = json_data['pubkey'] + uid = json_data['uid'] + status = json_data['status'] + + return cls(uid, pubkey, status) + + def selfcert(self, community): + """ + Get the person self certification. + This request is not cached in the person object. + + :param cutecoin.core.community.Community community: The community target to request the self certification + :return: A SelfCertification ucoinpy object + :rtype: ucoinpy.documents.certification.SelfCertification + """ + data = community.bma_access.get(self, qtbma.wot.Lookup, req_args={'search': self.pubkey}) + if data != qtbma.wot.Lookup.null_value: + timestamp = 0 + + 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"] + uid = uid_data["uid"] + signature = uid_data["self"] + + return SelfCertification(PROTOCOL_VERSION, + community.currency, + self.pubkey, + timestamp, + uid, + signature) + + def get_join_date(self, community): + """ + Get the person join date. + This request is not cached in the person object. + + :param cutecoin.core.community.Community community: The community target to request the join date + :return: A datetime object + """ + search = community.bma_access.get(self, qtbma.blockchain.Membership, {'search': self.pubkey}) + if search != qtbma.blockchain.Membership.null_value: + if len(search['memberships']) > 0: + membership_data = search['memberships'][0] + return community.get_block(membership_data['blockNumber']).mediantime + else: + return None + else: + raise MembershipNotFoundError(self.pubkey, community.name) + +#TODO: Manage 'OUT' memberships ? Maybe ? + def membership(self, community): + """ + Get the person last membership document. + + :param cutecoin.core.community.Community community: The community target to request the join date + :return: The membership data in BMA json format + """ + search = community.bma_access.get(self, qtbma.blockchain.Membership, + {'search': self.pubkey}) + if search != qtbma.blockchain.Membership.null_value: + block_number = -1 + for ms in search['memberships']: + if ms['blockNumber'] > block_number: + block_number = ms['blockNumber'] + if 'type' in ms: + if ms['type'] is 'IN': + membership_data = ms + else: + membership_data = ms + return membership_data + else: + raise MembershipNotFoundError(self.pubkey, community.name) + + def published_uid(self, community): + data = community.bma_access.get(self, qtbma.wot.Lookup, + req_args={'search': self.pubkey}) + if data != qtbma.wot.Lookup.null_value: + timestamp = 0 + + for result in data['results']: + if result["pubkey"] == self.pubkey: + uids = result['uids'] + person_uid = "" + for uid_data in uids: + if uid_data["meta"]["timestamp"] > timestamp: + timestamp = uid_data["meta"]["timestamp"] + person_uid = uid_data["uid"] + if person_uid == self.uid: + return True + return False + + def is_member(self, community): + ''' + Check if the person is a member of a community + + :param cutecoin.core.community.Community community: The community target to request the join date + :return: True if the person is a member of a community + ''' + certifiers = community.bma_access.get(self, qtbma.wot.CertifiersOf, {'search': self.pubkey}) + if certifiers != qtbma.wot.CertifiersOf.null_value: + return certifiers['isMember'] + return False + + def certifiers_of(self, community): + """ + Get the list of this person certifiers + + :param cutecoin.core.community.Community community: The community target to request the join date + :return: The list of the certifiers of this community in BMA json format + """ + certifiers = community.bma_access.get(self, qtbma.wot.CertifiersOf, {'search': self.pubkey}) + if certifiers == qtbma.wot.CertifiersOf.null_value: + logging.debug('bma.wot.CertifiersOf request error') + data = community.bma_access.get(self, qtbma.wot.Lookup, {'search': self.pubkey}) + if data == qtbma.wot.Lookup.null_value: + logging.debug('bma.wot.Lookup request error') + return list() + + # convert api data to certifiers list + certifiers = list() + # add certifiers of uid + + for result in data['results']: + if result["pubkey"] == self.pubkey: + for uid_data in result['uids']: + for certifier_data in uid_data['others']: + for uid in certifier_data['uids']: + # add a certifier + certifier = {} + certifier['uid'] = uid + certifier['pubkey'] = certifier_data['pubkey'] + certifier['isMember'] = certifier_data['isMember'] + certifier['cert_time'] = dict() + certifier['cert_time']['medianTime'] = community.get_block( + certifier_data['meta']['block_number']).mediantime + certifiers.append(certifier) + + return certifiers + return certifiers['certifications'] + + def unique_valid_certifiers_of(self, community): + certifier_list = self.certifiers_of(community) + unique_valid = [] + #  add certifiers of uid + for certifier in tuple(certifier_list): + # add only valid certification... + if community.certification_expired(certifier['cert_time']['medianTime']): + continue + + # keep only the latest certification + already_found = [c['pubkey'] for c in unique_valid] + if certifier['pubkey'] in already_found: + index = already_found.index(certifier['pubkey']) + if certifier['cert_time']['medianTime'] > unique_valid[index]['cert_time']['medianTime']: + unique_valid[index] = certifier + else: + unique_valid.append(certifier) + return unique_valid + + def certified_by(self, community): + ''' + Get the list of persons certified by this person + + :param cutecoin.core.community.Community community: The community target to request the join date + :return: The list of the certified persons of this community in BMA json format + ''' + certified_list = community.bma_access.get(self, qtbma.wot.CertifiedBy, {'search': self.pubkey}) + if certified_list == qtbma.wot.CertifiedBy.null_value: + logging.debug('bma.wot.CertifiersOf request error') + data = community.bma_access.get(self, qtbma.wot.Lookup, {'search': self.pubkey}) + if data == qtbma.wot.Lookup.null_value: + logging.debug('bma.wot.Lookup request error') + return list() + else: + certified_list = list() + for result in data['results']: + if result["pubkey"] == self.pubkey: + for certified in result['signed']: + certified['cert_time'] = dict() + certified['cert_time']['medianTime'] = certified['meta']['timestamp'] + certified_list.append(certified) + + return certified_list + + return certified_list['certifications'] + + def unique_valid_certified_by(self, community): + certified_list = self.certified_by(community) + unique_valid = [] + #  add certifiers of uid + for certified in tuple(certified_list): + # add only valid certification... + if community.certification_expired(certified['cert_time']['medianTime']): + continue + + # keep only the latest certification + already_found = [c['pubkey'] for c in unique_valid] + if certified['pubkey'] in already_found: + index = already_found.index(certified['pubkey']) + if certified['cert_time']['medianTime'] > unique_valid[index]['cert_time']['medianTime']: + unique_valid[index] = certified + else: + unique_valid.append(certified) + return unique_valid + + def membership_expiration_time(self, community): + join_block = self.membership(community)['blockNumber'] + join_date = community.get_block(join_block)['medianTime'] + parameters = community.parameters + expiration_date = join_date + parameters['sigValidity'] + current_time = time.time() + return expiration_date - current_time + + def jsonify(self): + ''' + Get the community as dict in json format. + :return: The community as a dict in json format + ''' + data = {'uid': self.uid, + 'pubkey': self.pubkey, + 'status': self.status} + return data diff --git a/src/cutecoin/core/wallet.py b/src/cutecoin/core/wallet.py index ba53f61d0034d4b88529ae409b75a3e6c835f026..dedc572cb500cf6b194204f03d2a2a19960721ce 100644 --- a/src/cutecoin/core/wallet.py +++ b/src/cutecoin/core/wallet.py @@ -8,9 +8,9 @@ from ucoinpy.documents.transaction import InputSource, OutputSource, Transaction from ucoinpy.key import SigningKey from .net.api import bma as qtbma -from ..tools.exceptions import NotEnoughMoneyError, NoPeerAvailable, PersonNotFoundError +from ..tools.exceptions import NotEnoughMoneyError, NoPeerAvailable, LookupFailureError from .transfer import Transfer, Received -from .person import Person +from .registry import IdentitiesRegistry, Identity from PyQt5.QtCore import QObject, pyqtSignal @@ -77,12 +77,12 @@ class Cache(): try: issuer_uid = Person.lookup(tx.issuers[0], community).uid - except PersonNotFoundError: + except LookupFailureError: issuer_uid = "" try: receiver_uid = Person.lookup(receivers[0], community).uid - except PersonNotFoundError: + except LookupFailureError: receiver_uid = "" metadata = {'block': block_number, @@ -199,7 +199,7 @@ class Wallet(QObject): inner_data_changed = pyqtSignal(int) refresh_progressed = pyqtSignal(int, int) - def __init__(self, walletid, pubkey, name): + def __init__(self, walletid, pubkey, name, identities_registry): ''' Constructor of a wallet object @@ -212,10 +212,11 @@ class Wallet(QObject): self.walletid = walletid self.pubkey = pubkey self.name = name + self._identities_registry = identities_registry self.caches = {} @classmethod - def create(cls, walletid, salt, password, name): + def create(cls, walletid, salt, password, name, identities_registry): ''' Factory method to create a new wallet @@ -228,10 +229,10 @@ class Wallet(QObject): key = SigningKey(salt, password) else: key = SigningKey("{0}{1}".format(salt, walletid), password) - return cls(walletid, key.pubkey, name) + return cls(walletid, key.pubkey, name, identities_registry) @classmethod - def load(cls, json_data): + def load(cls, json_data, identities_registry): ''' Factory method to load a saved wallet. @@ -240,7 +241,7 @@ class Wallet(QObject): walletid = json_data['walletid'] pubkey = json_data['pubkey'] name = json_data['name'] - return cls(walletid, pubkey, name) + return cls(walletid, pubkey, name, identities_registry) def load_caches(self, json_data): ''' @@ -397,12 +398,12 @@ class Wallet(QObject): try: issuer_uid = Person.lookup(key.pubkey, community).uid - except PersonNotFoundError: + except LookupFailureError: issuer_uid = "" try: receiver_uid = Person.lookup(recipient, community).uid - except PersonNotFoundError: + except LookupFailureError: receiver_uid = "" metadata = {'block': block_number, diff --git a/src/cutecoin/gui/community_tab.py b/src/cutecoin/gui/community_tab.py index 09c7b99dc595fc2a0ba18fb8c6a5d9ec487cfcd2..eb5a6945adb44b16bfba5c5bb8b77542870b5451 100644 --- a/src/cutecoin/gui/community_tab.py +++ b/src/cutecoin/gui/community_tab.py @@ -17,8 +17,8 @@ from .wot_tab import WotTabWidget from .transfer import TransferMoneyDialog from .certification import CertificationDialog from . import toast -from ..tools.exceptions import PersonNotFoundError, NoPeerAvailable -from ..core.person import Person +from ..tools.exceptions import LookupFailureError, NoPeerAvailable +from ..core.registry import IdentitiesRegistry from ucoinpy.api import bma from ..core.net.api import bma as qtbma @@ -41,6 +41,7 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget): super().__init__() self.setupUi(self) self.parent = parent + self.app = app self.community = community self.community.inner_data_changed.connect(self.handle_change) self.account = account @@ -75,7 +76,7 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget): pubkey_index = model.sourceModel().index(source_index.row(), pubkey_col) pubkey = model.sourceModel().data(pubkey_index, Qt.DisplayRole) - identity = Person.lookup(pubkey, self.community) + identity = self.app.identities_registry(pubkey, self.community) menu = QMenu(self) informations = QAction(self.tr("Informations"), self) @@ -175,7 +176,7 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget): except ValueError as e: QMessageBox.critical(self, self.tr("Join demand error"), str(e)) - except PersonNotFoundError as 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.""")) @@ -282,7 +283,7 @@ Revoking your UID can only success if it is not already validated by the network persons = [] for identity in response['results']: - persons.append(Person.lookup(identity['pubkey'], self.community)) + persons.append(self.app.identities_registry(identity['pubkey'], self.community)) self._last_search = 'text' self.edit_textsearch.clear() @@ -298,14 +299,14 @@ Revoking your UID can only success if it is not already validated by the network Search members of community and display found members """ pubkeys = self.community.members_pubkeys() - persons = [] + identities = [] for p in pubkeys: - persons.append(Person.lookup(p, self.community)) + identities.append(self.app.identities_registry.lookup(p, self.community)) self._last_search = 'members' self.edit_textsearch.clear() - self.refresh(persons) + self.refresh(identities) def search_direct_connections(self): """ @@ -320,16 +321,16 @@ Revoking your UID can only success if it is not already validated by the network If no identities is passed, use the account connections. ''' if persons is None: - self_identity = Person.lookup(self.account.pubkey, self.community) + self_identity = self.app.identities_registry.lookup(self.account.pubkey, self.community) account_connections = [] certifiers_of = [] certified_by = [] for p in self_identity.unique_valid_certifiers_of(self.community): - account_connections.append(Person.lookup(p['pubkey'], self.community)) + account_connections.append(self.app.identities_registry.lookup(p['pubkey'], self.community)) certifiers_of = [p for p in account_connections] logging.debug(persons) for p in self_identity.unique_valid_certified_by(self.community): - account_connections.append(Person.lookup(p['pubkey'], self.community)) + account_connections.append(self.app.identities_registry.lookup(p['pubkey'], self.community)) certified_by = [p for p in account_connections if p.pubkey not in [i.pubkey for i in certifiers_of]] persons = certifiers_of + certified_by @@ -360,7 +361,7 @@ Revoking your UID can only success if it is not already validated by the network self.button_leaving.hide() self.button_publish_uid.show() self.button_revoke_uid.hide() - except PersonNotFoundError: + except LookupFailureError: self.button_membership.hide() self.button_leaving.hide() self.button_publish_uid.show() diff --git a/src/cutecoin/gui/contact.py b/src/cutecoin/gui/contact.py index 2f50c58e3a74cd2746762d8561adc18f0d69553b..59e2f0042f4c7a8341caf5123a32c78d604d3ce4 100644 --- a/src/cutecoin/gui/contact.py +++ b/src/cutecoin/gui/contact.py @@ -7,7 +7,7 @@ import re import logging from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QMessageBox -from ..core.person import Person +from ..core.registry import IdentitiesRegistry from ..tools.exceptions import ContactAlreadyExists from ..gen_resources.contact_uic import Ui_ConfigureContactDialog diff --git a/src/cutecoin/gui/currency_tab.py b/src/cutecoin/gui/currency_tab.py index 3badb34f0c12eabb56d435b19ed6a990572da627..8ce4776ade36dea053b616c2c63116c7b24949df 100644 --- a/src/cutecoin/gui/currency_tab.py +++ b/src/cutecoin/gui/currency_tab.py @@ -18,7 +18,7 @@ from .network_tab import NetworkTabWidget from .informations_tab import InformationsTabWidget from . import toast from ..tools.exceptions import MembershipNotFoundError -from ..core.person import Person +from ..core.registry import IdentitiesRegistry class CurrencyTabWidget(QWidget, Ui_CurrencyTabWidget): @@ -127,7 +127,7 @@ class CurrencyTabWidget(QWidget, Ui_CurrencyTabWidget): logging.debug("Refresh block") self.status_info.clear() try: - person = Person.lookup(self.app.current_account.pubkey, self.community) + person = self.app.identities_registry.lookup(self.app.current_account.pubkey, self.community) expiration_time = person.membership_expiration_time(self.community) sig_validity = self.community.parameters['sigValidity'] warning_expiration_time = int(sig_validity / 3) @@ -144,15 +144,14 @@ class CurrencyTabWidget(QWidget, Ui_CurrencyTabWidget): if len(certifiers_of) < self.community.parameters['sigQty']: self.status_info.append('warning_certifications') toast.display(self.tr("Certifications number"), - self.tr("<b>Warning : You are certified by only {0} persons, need {1}</b>").format(len(certifiers_of), - self.community.parameters['sigQty'])) + self.tr("<b>Warning : You are certified by only {0} persons, need {1}</b>") + .format(len(certifiers_of), + self.community.parameters['sigQty'])) except MembershipNotFoundError as e: pass self.tab_history.start_progress() - self.app.monitor.blockchain_watcher(self.community).thread().start() - self.app.monitor.persons_watcher(self.community).thread().start() self.refresh_status() @pyqtSlot() diff --git a/src/cutecoin/gui/informations_tab.py b/src/cutecoin/gui/informations_tab.py index e4c3c854285b4a3200f7b16819b719b2a008c115..eea1c0b0f97e2185fbcf455351da54189bdc13f3 100644 --- a/src/cutecoin/gui/informations_tab.py +++ b/src/cutecoin/gui/informations_tab.py @@ -23,6 +23,7 @@ class InformationsTabWidget(QWidget, Ui_InformationsTabWidget): super().__init__() self.setupUi(self) self.community = community + self.community.inner_data_changed.connect(self.refresh) self.account = account self.refresh() diff --git a/src/cutecoin/gui/mainwindow.py b/src/cutecoin/gui/mainwindow.py index 7cca2b206465bed3721085ba23fef6f76553518c..583d753eb9840c85aa1d9911f3f54d33e1cb450b 100644 --- a/src/cutecoin/gui/mainwindow.py +++ b/src/cutecoin/gui/mainwindow.py @@ -398,7 +398,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): def closeEvent(self, event): if self.app.current_account: self.app.save_cache(self.app.current_account) - self.app.save_persons() + self.app.save_registries() super().closeEvent(event) def showEvent(self, event): diff --git a/src/cutecoin/gui/process_cfg_community.py b/src/cutecoin/gui/process_cfg_community.py index 037334f8acfab09e1214f20504f962098842733b..4b822a2e1c2ac5f82da2bc43422d26d2e232fe3c 100644 --- a/src/cutecoin/gui/process_cfg_community.py +++ b/src/cutecoin/gui/process_cfg_community.py @@ -17,9 +17,9 @@ from PyQt5.QtGui import QCursor 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.registry import IdentitiesRegistry from ..core.net.node import Node -from ..tools.exceptions import PersonNotFoundError, NoPeerAvailable +from ..tools.exceptions import LookupFailureError, NoPeerAvailable class Step(): @@ -194,7 +194,7 @@ class ProcessConfigureCommunity(QDialog, Ui_CommunityConfigurationDialog): def accept(self): try: Person.lookup(self.account.pubkey, self.community, cached=False) - except PersonNotFoundError as e: + except LookupFailureError as e: reply = QMessageBox.question(self, self.tr("Pubkey not found"), self.tr("""The public key of your account wasn't found in the community. :\n {0}\n diff --git a/src/cutecoin/gui/transactions_tab.py b/src/cutecoin/gui/transactions_tab.py index f0c739e3cd7787b875dc3171b4aba636495beff5..e281654e25f56ef991ab9bd24a028a23302d14b6 100644 --- a/src/cutecoin/gui/transactions_tab.py +++ b/src/cutecoin/gui/transactions_tab.py @@ -6,7 +6,7 @@ from ..gen_resources.transactions_tab_uic import Ui_transactionsTabWidget from ..models.txhistory import HistoryTableModel, TxFilterProxyModel from ..core.transfer import Transfer from ..core.wallet import Wallet -from ..core.person import Person +from ..core.registry import IdentitiesRegistry from .transfer import TransferMoneyDialog import logging diff --git a/src/cutecoin/gui/wallets_tab.py b/src/cutecoin/gui/wallets_tab.py index 54654146570e0629a254c7c0d836df4497d6875c..e07581ffa94040d58a79c3890d7737517c95cd08 100644 --- a/src/cutecoin/gui/wallets_tab.py +++ b/src/cutecoin/gui/wallets_tab.py @@ -8,7 +8,7 @@ import logging from PyQt5.QtWidgets import QWidget, QMenu, QAction, QApplication, QDialog from PyQt5.QtCore import QDateTime, QModelIndex, Qt, QLocale from PyQt5.QtGui import QCursor -from ..core.person import Person +from ..core.registry import IdentitiesRegistry from ..core.wallet import Wallet from ..gui.password_asker import PasswordAskerDialog from ..models.wallets import WalletsTableModel, WalletsFilterProxyModel @@ -44,7 +44,7 @@ class WalletsTabWidget(QWidget, Ui_WalletsTab): last_renewal = "" expiration = "" try: - person = Person.lookup(self.account.pubkey, self.community) + person = self.app.identities_registry.lookup(self.account.pubkey, self.community) membership = person.membership(self.community) renew_block = membership['blockNumber'] last_renewal = self.community.get_block(renew_block)['medianTime'] diff --git a/src/cutecoin/gui/wot_tab.py b/src/cutecoin/gui/wot_tab.py index b5be05f9c0113d27db6ce6e0f0973eb18ce6b87a..be83d9624ace7f1abebce47215aee25e01bb3c7f 100644 --- a/src/cutecoin/gui/wot_tab.py +++ b/src/cutecoin/gui/wot_tab.py @@ -7,7 +7,7 @@ from PyQt5.QtCore import pyqtSlot from ..gen_resources.wot_tab_uic import Ui_WotTabWidget from cutecoin.gui.views.wot import NODE_STATUS_HIGHLIGHTED, NODE_STATUS_SELECTED, NODE_STATUS_OUT, ARC_STATUS_STRONG, ARC_STATUS_WEAK from ucoinpy.api import bma -from cutecoin.core.person import Person +from ..core.registry import IdentitiesRegistry class WotTabWidget(QWidget, Ui_WotTabWidget): @@ -41,6 +41,7 @@ class WotTabWidget(QWidget, Ui_WotTabWidget): self.account = account self.community = community self.password_asker = password_asker + self.app = app # nodes list for menu from search self.nodes = list() @@ -59,8 +60,8 @@ class WotTabWidget(QWidget, Ui_WotTabWidget): self._current_metadata = metadata # create Person from node metadata - person = Person.from_metadata(metadata) - person_account = Person.from_metadata({'text': self.account.name, + person = self.app.identities_registry.from_metadata(metadata) + person_account = self.app.identities_registry.from_metadata({'text': self.account.name, 'id': self.account.pubkey}) certifier_list = person.certifiers_of(self.community) certified_list = person.certified_by(self.community) @@ -152,15 +153,15 @@ class WotTabWidget(QWidget, Ui_WotTabWidget): ) def identity_informations(self, metadata): - person = Person.from_metadata(metadata) + person = self.app.identities_registry.from_metadata(metadata) self.parent.identity_informations(person) def sign_node(self, metadata): - person = Person.from_metadata(metadata) + person = self.app.identities_registry.from_metadata(metadata) self.parent.certify_identity(person) def send_money_to_node(self, metadata): - person = Person.from_metadata(metadata) + person = self.app.identities_registry.from_metadata(metadata) self.parent.send_money_to_identity(person) def add_node_as_contact(self, metadata): diff --git a/src/cutecoin/models/identities.py b/src/cutecoin/models/identities.py index 2548f2c923f3a6852bf561ee7fb29488b8a82748..0f069f6f3ef305e0b622e0a769a497359a005e57 100644 --- a/src/cutecoin/models/identities.py +++ b/src/cutecoin/models/identities.py @@ -4,8 +4,7 @@ Created on 5 févr. 2014 @author: inso ''' -from ucoinpy.api import bma -from ..core.person import Person +from ..core.registry import IdentitiesRegistry from ..tools.exceptions import NoPeerAvailable, MembershipNotFoundError from PyQt5.QtCore import QAbstractTableModel, QSortFilterProxyModel, Qt, \ QDateTime, QModelIndex, QLocale diff --git a/src/cutecoin/tools/exceptions.py b/src/cutecoin/tools/exceptions.py index f394b397109d777550893881fd698817120ec199..9f2da1cc73e1cf1d79bb9dd270eb4eb94c0cd8d7 100644 --- a/src/cutecoin/tools/exceptions.py +++ b/src/cutecoin/tools/exceptions.py @@ -31,7 +31,7 @@ class NotMemberOfCommunityError(Error): .__init__(account + " is not a member of " + community) -class PersonNotFoundError(Error): +class LookupFailureError(Error): ''' Exception raised when looking for a person in a community