From 2b52c731ab61cf10de2cbfca4e05ad7fd721a384 Mon Sep 17 00:00:00 2001 From: Inso <insomniak.fr@gmail.com> Date: Tue, 23 Jun 2015 22:01:59 +0200 Subject: [PATCH] TX History parsing --- src/cutecoin/core/account.py | 52 +++++++++---- src/cutecoin/core/app.py | 3 +- src/cutecoin/core/net/api/bma/access.py | 2 + src/cutecoin/core/net/api/bma/tx/__init__.py | 2 + .../core/net/api/bma/tx/history/__init__.py | 7 +- src/cutecoin/core/net/network.py | 6 +- src/cutecoin/core/registry/identities.py | 4 +- src/cutecoin/core/transfer.py | 1 + src/cutecoin/core/wallet.py | 78 +++++++++++-------- src/cutecoin/gui/currency_tab.py | 22 +----- src/cutecoin/gui/mainwindow.py | 35 ++------- src/cutecoin/gui/transactions_tab.py | 10 +++ src/cutecoin/gui/transfer.py | 10 +-- src/cutecoin/models/txhistory.py | 9 +-- 14 files changed, 123 insertions(+), 118 deletions(-) diff --git a/src/cutecoin/core/account.py b/src/cutecoin/core/account.py index c6b27e05..af77bc39 100644 --- a/src/cutecoin/core/account.py +++ b/src/cutecoin/core/account.py @@ -150,6 +150,7 @@ class Account(QObject): self.communities = communities self.wallets = wallets self.contacts = contacts + self._refreshing = False self._identities_registry = identities_registry self.referential = 0 @@ -245,22 +246,41 @@ class Account(QObject): .. note:: emit the Account pyqtSignal loading_progressed during refresh """ - loaded_wallets = 0 - received_list = [] - - def progressing(value, maximum): - account_value = maximum * loaded_wallets + value - account_max = maximum * len(self.wallets) - self.loading_progressed.emit(account_value, account_max) - - for w in self.wallets: - w.refresh_progressed.connect(progressing) - QCoreApplication.processEvents() - w.init_cache(community) - w.refresh_transactions(community, received_list) - loaded_wallets += 1 - QCoreApplication.processEvents() - self.loading_finished.emit(received_list) + logging.debug("Start refresh transactions") + if not self._refreshing: + self._refreshing = True + loaded_wallets = 0 + received_list = [] + values = {} + maximums = {} + + def progressing(value, maximum, hash): + logging.debug("Loading = {0} : {1} : {2}".format(value, maximum, loaded_wallets)) + values[hash] = value + maximums[maximum] = maximum + account_value = sum(values.values()) + account_max = sum(maximums.values()) + self.loading_progressed.emit(account_value, account_max) + + def wallet_finished(received): + logging.debug("Finished loading wallet") + nonlocal received_list + received_list = received_list + received + nonlocal loaded_wallets + loaded_wallets += 1 + if loaded_wallets == len(self.wallets): + logging.debug("All wallets loaded") + self._refreshing = False + self.loading_finished.emit(received_list) + for w in self.wallets: + w.refresh_progressed.disconnect(progressing) + w.refresh_finished.disconnect(wallet_finished) + + for w in self.wallets: + w.refresh_progressed.connect(progressing) + w.refresh_finished.connect(wallet_finished) + w.init_cache(community) + w.refresh_transactions(community, received_list) def set_display_referential(self, index): self.referential = index diff --git a/src/cutecoin/core/app.py b/src/cutecoin/core/app.py index 3625fae1..beda94dd 100644 --- a/src/cutecoin/core/app.py +++ b/src/cutecoin/core/app.py @@ -31,7 +31,6 @@ class Application(QObject): Saving and loading the application state ''' - loading_progressed = pyqtSignal(int, int) version_requested = pyqtSignal() def __init__(self, argv, qapp, loop): @@ -211,6 +210,8 @@ class Application(QObject): os.remove(bma_path) for wallet in account.wallets: + for c in account.communities: + wallet.init_cache(c) wallet_path = os.path.join(config.parameters['home'], account.name, '__cache__', wallet.pubkey + "_wal") if os.path.exists(wallet_path): diff --git a/src/cutecoin/core/net/api/bma/access.py b/src/cutecoin/core/net/api/bma/access.py index 3eba4896..e767e082 100644 --- a/src/cutecoin/core/net/api/bma/access.py +++ b/src/cutecoin/core/net/api/bma/access.py @@ -172,6 +172,8 @@ class BmaAccess(QObject): json_data = json.loads(strdata) self._update_cache(request, req_args, get_args, json_data) future_data.set_result(json_data) + else: + future_data.set_result(request.null_value) future_data = asyncio.Future() data = self._get_from_cache(request, req_args, get_args) diff --git a/src/cutecoin/core/net/api/bma/tx/__init__.py b/src/cutecoin/core/net/api/bma/tx/__init__.py index 6acdbcac..22b63d45 100644 --- a/src/cutecoin/core/net/api/bma/tx/__init__.py +++ b/src/cutecoin/core/net/api/bma/tx/__init__.py @@ -74,3 +74,5 @@ class Sources(Tx): def __get__(self, **kwargs): assert self.pubkey is not None return self.requests_get('/sources/%s' % self.pubkey, **kwargs) + +from . import history \ No newline at end of file diff --git a/src/cutecoin/core/net/api/bma/tx/history/__init__.py b/src/cutecoin/core/net/api/bma/tx/history/__init__.py index c15c8477..471c4579 100644 --- a/src/cutecoin/core/net/api/bma/tx/history/__init__.py +++ b/src/cutecoin/core/net/api/bma/tx/history/__init__.py @@ -22,12 +22,13 @@ logger = logging.getLogger("ucoin/tx") class Blocks(History): - def __init__(self, conn_handler, pubkey, from_, to_, module='blocks'): - super(History, self).__init__(conn_handler, pubkey, module) + def __init__(self, conn_handler, pubkey, from_, to_, module='tx'): + super(Blocks, self).__init__(conn_handler, pubkey, module) self.from_ = from_ self.to_ = to_ null_value = { + "hash": "", "currency": "", "pubkey": "", "history": { @@ -37,4 +38,4 @@ class Blocks(History): } def __get__(self, **kwargs): - return self.requests_get('/blocks/{0}/{1}'.format(self.from_, self.to_) **kwargs) + return self.requests_get('/history/%s/blocks/%s/%s' % (self.pubkey, self.from_, self.to_), **kwargs) diff --git a/src/cutecoin/core/net/network.py b/src/cutecoin/core/net/network.py index 669a9d11..aa5f3625 100644 --- a/src/cutecoin/core/net/network.py +++ b/src/cutecoin/core/net/network.py @@ -197,12 +197,12 @@ class Network(QObject): To stop this crawling, call "stop_crawling" method. ''' while self.continue_crawling(): - for (i, node) in enumerate(self.nodes): + for node in self.nodes: if self.continue_crawling(): - yield from asyncio.sleep(2000) + yield from asyncio.sleep(2) node.refresh() else: - return + return True @pyqtSlot(Peer) def handle_new_node(self, peer): diff --git a/src/cutecoin/core/registry/identities.py b/src/cutecoin/core/registry/identities.py index 753ca384..17d6d46b 100644 --- a/src/cutecoin/core/registry/identities.py +++ b/src/cutecoin/core/registry/identities.py @@ -85,9 +85,10 @@ class IdentitiesRegistry: identity.status = Identity.FOUND logging.debug("Lookup : found {0}".format(identity)) future_identity.set_result(True) + return + future_identity.set_result(True) future_identity = asyncio.Future() - logging.debug("Future identity") if pubkey in self._instances: identity = self._instances[pubkey] future_identity.set_result(True) @@ -96,7 +97,6 @@ class IdentitiesRegistry: self._instances[pubkey] = identity reply = community.bma_access.simple_request(qtbma.wot.Lookup, req_args={'search': pubkey}) reply.finished.connect(lambda: handle_reply(reply)) - logging.debug("Return") yield from future_identity return identity diff --git a/src/cutecoin/core/transfer.py b/src/cutecoin/core/transfer.py index 483d16b5..9b2e2da5 100644 --- a/src/cutecoin/core/transfer.py +++ b/src/cutecoin/core/transfer.py @@ -54,6 +54,7 @@ class Transfer(QObject): assert('issuer_uid' in metadata) assert('receiver_uid' in metadata) assert('txid' in metadata) + super().__init__() self.hash = hash self.state = state diff --git a/src/cutecoin/core/wallet.py b/src/cutecoin/core/wallet.py index 3f96c99a..ff722a0d 100644 --- a/src/cutecoin/core/wallet.py +++ b/src/cutecoin/core/wallet.py @@ -13,7 +13,7 @@ from ..tools.exceptions import NotEnoughMoneyError, NoPeerAvailable, LookupFailu from .transfer import Transfer, Received from .registry import IdentitiesRegistry, Identity -from PyQt5.QtCore import QObject, pyqtSignal +from PyQt5.QtCore import QObject, pyqtSignal, QCoreApplication import logging import asyncio @@ -70,22 +70,29 @@ class Cache(): @asyncio.coroutine def _parse_transaction(self, community, txdata, received_list, txid): - receivers = [o.pubkey for o in txdata['outputs'] + if len(txdata['issuers']) == 0: + True + + tx_outputs = [OutputSource.from_inline(o) for o in txdata['outputs']] + receivers = [o.pubkey for o in tx_outputs if o.pubkey != txdata['issuers'][0]] - block_number = txdata['block'] + block_number = txdata['block_number'] mediantime = txdata['time'] + logging.debug(txdata) if len(receivers) == 0: receivers = [txdata['issuers'][0]] try: - issuer_uid = yield from IdentitiesRegistry.future_lookup(txdata['issuers'][0], community).uid + issuer = yield from self.wallet._identities_registry.future_lookup(txdata['issuers'][0], community) + issuer_uid = issuer.uid except LookupFailureError: issuer_uid = "" try: - receiver_uid = yield from IdentitiesRegistry.future_lookup(receivers[0], community).uid + receiver = yield from self.wallet._identities_registry.future_lookup(receivers[0], community) + receiver_uid = receiver.uid except LookupFailureError: receiver_uid = "" @@ -100,12 +107,12 @@ class Cache(): in_issuers = len([i for i in txdata['issuers'] if i == self.wallet.pubkey]) > 0 - in_outputs = len([o for o in txdata['outputs'] + in_outputs = len([o for o in tx_outputs if o.pubkey == self.wallet.pubkey]) > 0 # If the wallet pubkey is in the issuers we sent this transaction if in_issuers: - outputs = [o for o in txdata['outputs'] + outputs = [o for o in tx_outputs if o.pubkey != self.wallet.pubkey] amount = 0 for o in outputs: @@ -116,19 +123,19 @@ class Cache(): if t.state == Transfer.AWAITING] # We check if the transaction correspond to one we sent if txdata['hash'] not in [t['hash'] for t in awaiting]: - transfer = Transfer.create_validated(txdata, + transfer = Transfer.create_validated(txdata['hash'], metadata.copy()) self._transfers.append(transfer) # If we are not in the issuers, # maybe it we are in the recipients of this transaction elif in_outputs: - outputs = [o for o in txdata.outputs + outputs = [o for o in tx_outputs if o.pubkey == self.wallet.pubkey] amount = 0 for o in outputs: amount += o.amount metadata['amount'] = amount - received = Received(txdata, metadata.copy()) + received = Received(txdata['hash'], metadata.copy()) received_list.append(received) self._transfers.append(received) return True @@ -141,32 +148,40 @@ class Cache(): :param cutecoin.core.Community community: The community :param list received_list: List of transactions received """ - current_block = 0 + parsed_block = 0 block_data = yield from community.blockid() current_block = block_data['number'] + logging.debug("Refresh from : {0} to {1}".format(self.latest_block, current_block)) # Lets look if transactions took too long to be validated awaiting = [t for t in self._transfers if t.state == Transfer.AWAITING] - tx_history = yield from community.bma_access.future_request(qtbma.tx.history.Blocks, - req_args={'pubkey': self.wallet.pubkey, - 'from_':self.latest_block, - 'to_': self.current_block}) - - # We parse only blocks with transactions - transactions = tx_history['history']['received'] + tx_history['history']['sent'] - for (txid, txdata) in enumerate(transactions): - yield from self._parse_transaction(community, txdata, received_list, txid) - self.wallet.refresh_progressed.emit(txid, len(transactions)) + while parsed_block < current_block: + tx_history = yield from community.bma_access.future_request(qtbma.tx.history.Blocks, + req_args={'pubkey': self.wallet.pubkey, + 'from_':str(parsed_block), + 'to_': str(parsed_block + 100)}) + + # We parse only blocks with transactions + transactions = tx_history['history']['received'] + tx_history['history']['sent'] + for (txid, txdata) in enumerate(transactions): + if len(txdata['issuers']) == 0: + logging.debug("Error with : {0}, from {1} to {2}".format(self.wallet.pubkey, + parsed_block, + current_block)) + else: + yield from self._parse_transaction(community, txdata, received_list, txid) + self.wallet.refresh_progressed.emit(parsed_block, current_block, self.wallet.pubkey) + parsed_block += 101 if current_block > self.latest_block: - self.available_sources = yield from self.wallet.sources(community) + self.available_sources = yield from self.wallet.future_sources(community) self.latest_block = current_block for transfer in awaiting: transfer.check_refused(current_block) - self.wallet.refresh_finished.emit() + self.wallet.refresh_finished.emit(received_list) class Wallet(QObject): @@ -175,8 +190,8 @@ class Wallet(QObject): ''' inner_data_changed = pyqtSignal(str) - refresh_progressed = pyqtSignal(int, int) - refresh_finished = pyqtSignal() + refresh_progressed = pyqtSignal(int, int, str) + refresh_finished = pyqtSignal(list) transfer_broadcasted = pyqtSignal(str) broadcast_error = pyqtSignal(int, str) @@ -262,6 +277,7 @@ class Wallet(QObject): :param community: The community to refresh its cache ''' + logging.debug("Refresh transactions for {0}".format(self.pubkey)) asyncio.async(self.caches[community.currency].refresh(community, received_list)) def check_password(self, salt, password): @@ -395,15 +411,15 @@ class Wallet(QObject): logging.debug("Sender pubkey:{0}".format(key.pubkey)) try: - issuer = yield from self.identities_registry.future_lookup(key.pubkey, community) + issuer = yield from self._identities_registry.future_lookup(key.pubkey, community) issuer_uid = issuer.uid - except LookupFailureError: + except LookupFailureError as e: issuer_uid = "" try: - receiver = yield from self.identities_registry.lookup(recipient, community) + receiver = yield from self._identities_registry.future_lookup(recipient, community) receiver_uid = receiver.uid - except LookupFailureError: + except LookupFailureError as e: receiver_uid = "" metadata = {'block': block_number, @@ -434,8 +450,8 @@ class Wallet(QObject): tx.sign([key]) logging.debug("Transaction : {0}".format(tx.signed_raw())) - transfer.transfer_broadcasted.connect(self.transfer_broadcasted) - transfer.broadcast_error.connect(self.broadcast_error) + #transfer.transfer_broadcasted.connect(self.transfer_broadcasted) + #transfer.broadcast_error.connect(self.broadcast_error) transfer.send(tx, community) @asyncio.coroutine diff --git a/src/cutecoin/gui/currency_tab.py b/src/cutecoin/gui/currency_tab.py index c05aa6c9..603d3e02 100644 --- a/src/cutecoin/gui/currency_tab.py +++ b/src/cutecoin/gui/currency_tab.py @@ -153,9 +153,8 @@ class CurrencyTabWidget(QWidget, Ui_CurrencyTabWidget): pass self.tab_history.start_progress() - self.refresh_status() + self.refresh_data() - @pyqtSlot() def refresh_data(self): ''' Refresh data when the blockchain watcher finished handling datas @@ -163,11 +162,7 @@ class CurrencyTabWidget(QWidget, Ui_CurrencyTabWidget): if self.tab_wallets: self.tab_wallets.refresh() - if self.tab_history.table_history.model(): - self.tab_history.table_history.model().sourceModel().refresh_transfers() - self.tab_history.refresh_balance() - self.tab_history.stop_progress() self.refresh_status() @pyqtSlot() @@ -189,21 +184,6 @@ class CurrencyTabWidget(QWidget, Ui_CurrencyTabWidget): label_text += " - {0}".format(status_infotext) self.status_label.setText(label_text) - @pyqtSlot(list) - def notify_transfers(self, transfers_list): - transfers_txt = "" - amount = 0 - currency = self.community.name - for t in transfers_list: - amount += t.metadata['amount'] - - logging.debug(transfers_txt) - text = self.tr("Received {0} {1} from {2} transfers").format(amount, - currency, - len(transfers_list)) - text += transfers_txt - toast.display(self.tr("New transactions received"), text) - def refresh_wallets(self): if self.app.current_account: self.tab_wallets.refresh() diff --git a/src/cutecoin/gui/mainwindow.py b/src/cutecoin/gui/mainwindow.py index 5436774d..fc30c727 100644 --- a/src/cutecoin/gui/mainwindow.py +++ b/src/cutecoin/gui/mainwindow.py @@ -28,27 +28,6 @@ from . import toast import logging -class Loader(QObject): - def __init__(self, app): - super().__init__() - self.app = app - self.account_name = "" - - loaded = pyqtSignal() - connection_error = pyqtSignal(str) - - def set_account_name(self, name): - self.account_name = name - - @pyqtSlot() - def load(self): - if self.account_name != "": - account = self.app.get_account(self.account_name) - self.app.change_current_account(account) - - self.loaded.emit() - - class MainWindow(QMainWindow, Ui_MainWindow): ''' classdocs @@ -65,8 +44,13 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.setupUi(self) QApplication.setWindowIcon(QIcon(":/icons/cutecoin_logo")) self.app = app + + self.app.load() + if self.app.preferences["account"] != "": + account = self.app.get_account(self.app.preferences["account"]) + self.app.change_current_account(account) + self.password_asker = None - self.initialized = False self.busybar = QProgressBar(self.statusbar) self.busybar.setMinimum(0) @@ -91,10 +75,6 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.statusbar.addPermanentWidget(self.combo_referential) self.update_time() - self.loader = Loader(self.app) - self.loader.loaded.connect(self.loader_finished) - self.loader.connection_error.connect(self.display_error) - self.homescreen = HomeScreenWidget(self.app) self.centralWidget().layout().addWidget(self.homescreen) self.homescreen.button_new.clicked.connect(self.open_add_account_dialog) @@ -164,15 +144,12 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.app.current_account = None self.refresh() - QApplication.setOverrideCursor(Qt.BusyCursor) - self.app.loading_progressed.connect(loading_progressed) self.busybar.setMinimum(0) self.busybar.setMaximum(0) self.busybar.setValue(-1) self.busybar.show() self.status_label.setText(self.tr("Loading account {0}").format(account_name)) self.loader.set_account_name(account_name) - QTimer.singleShot(10, self.loader.load) self.homescreen.button_new.hide() self.homescreen.button_import.hide() diff --git a/src/cutecoin/gui/transactions_tab.py b/src/cutecoin/gui/transactions_tab.py index 4653f5a3..08a4c1c5 100644 --- a/src/cutecoin/gui/transactions_tab.py +++ b/src/cutecoin/gui/transactions_tab.py @@ -8,6 +8,7 @@ from ..core.transfer import Transfer from ..core.wallet import Wallet from ..core.registry import Identity from .transfer import TransferMoneyDialog +from . import toast import logging @@ -79,7 +80,16 @@ class TransactionsTabWidget(QWidget, Ui_transactionsTabWidget): @pyqtSlot(list) def stop_progress(self, received_list): + amount = 0 + for r in received_list: + amount += r.metadata['amount'] self.progressbar.hide() + text = self.tr("Received {0} {1} from {2} transfers").format(amount, + self.community.currency, + len(received_list)) + toast.display(self.tr("New transactions received"), text) + + self.table_history.model().sourceModel().refresh_transfers() self.table_history.resizeColumnsToContents() def refresh_balance(self): diff --git a/src/cutecoin/gui/transfer.py b/src/cutecoin/gui/transfer.py index eaf1105a..cc37d2e0 100644 --- a/src/cutecoin/gui/transfer.py +++ b/src/cutecoin/gui/transfer.py @@ -30,7 +30,7 @@ class TransferMoneyDialog(QDialog, Ui_TransferMoneyDialog): self.wallet = None self.community = self.account.communities[0] self.wallet = self.account.wallets[0] - self.dividend = self.community.dividend + self.dividend = self.community.dividend() regexp = QRegExp('^([ a-zA-Z0-9-_:/;*?\[\]\(\)\\\?!^+=@&~#{}|<>%.]{0,255})$') validator = QRegExpValidator(regexp) @@ -76,10 +76,6 @@ class TransferMoneyDialog(QDialog, Ui_TransferMoneyDialog): self.wallet.transfer_broadcasted.connect(self.money_sent) asyncio.async(self.wallet.send_money(self.account.salt, password, self.community, recipient, amount, comment)) - toast.display(self.tr("Money transfer"), - self.tr("Success transfering {0} {1} to {2}").format(amount, - self.community.currency, - recipient)) finally: QApplication.restoreOverrideCursor() QApplication.processEvents() @@ -111,14 +107,14 @@ class TransferMoneyDialog(QDialog, Ui_TransferMoneyDialog): def relative_amount_changed(self): relative = self.spinbox_relative.value() - amount = relative * self.dividend() + amount = relative * self.dividend self.spinbox_amount.blockSignals(True) self.spinbox_amount.setValue(amount) self.spinbox_amount.blockSignals(False) def change_current_community(self, index): self.community = self.account.communities[index] - self.dividend = self.community.dividend + self.dividend = self.community.dividend() amount = self.wallet.value(self.community) ref_amount = self.account.units_to_ref(amount, self.community) ref_name = self.account.ref_name(self.community.currency) diff --git a/src/cutecoin/models/txhistory.py b/src/cutecoin/models/txhistory.py index bebd9e13..328f94a7 100644 --- a/src/cutecoin/models/txhistory.py +++ b/src/cutecoin/models/txhistory.py @@ -194,8 +194,8 @@ class HistoryTableModel(QAbstractTableModel): def data_received(self, transfer): amount = transfer.metadata['amount'] comment = "" - if transfer.txdoc: - comment = transfer.txdoc.comment + if transfer.metadata['comment'] != "": + comment = transfer.metadata['comment'] if transfer.metadata['issuer_uid'] != "": sender = transfer.metadata['issuer_uid'] else: @@ -211,14 +211,13 @@ class HistoryTableModel(QAbstractTableModel): def data_sent(self, transfer): amount = transfer.metadata['amount'] comment = "" - if transfer.txdoc: - comment = transfer.txdoc.comment + if transfer.metadata['comment'] != "": + comment = transfer.metadata['comment'] if transfer.metadata['receiver_uid'] != "": receiver = transfer.metadata['receiver_uid'] else: receiver = "pub:{0}".format(transfer.metadata['receiver'][:5]) - date_ts = transfer.metadata['time'] txid = transfer.metadata['txid'] -- GitLab