diff --git a/src/sakia/data/connectors/bma.py b/src/sakia/data/connectors/bma.py index 20dc6befa0f101bcf5e11ac42da060eeb00f217f..090164cc35476e47bfa2ba26e37bfcd3f6dcde9e 100644 --- a/src/sakia/data/connectors/bma.py +++ b/src/sakia/data/connectors/bma.py @@ -290,6 +290,5 @@ class BmaConnector: result = await asyncio.gather(*replies, return_exceptions=True) return tuple(result) - return () else: raise NoPeerAvailable("", len(endpoints)) diff --git a/src/sakia/data/processors/transactions.py b/src/sakia/data/processors/transactions.py index 0705b6fcecfbc57a8a02722a5f81e4a523cdc76c..2ed0d93a9d2357e48ec6c46e9fe82c61ddf3c46d 100644 --- a/src/sakia/data/processors/transactions.py +++ b/src/sakia/data/processors/transactions.py @@ -108,7 +108,7 @@ class TransactionsProcessor: Cancel a local transaction :param sakia.data.entities.Transaction tx: the transaction """ - self.run_state_transitions(tx) + return self.run_state_transitions(tx) async def send(self, tx, txdoc, currency): """ diff --git a/src/sakia/data/processors/tx_lifecycle.py b/src/sakia/data/processors/tx_lifecycle.py index a1d858a24af3b3c96b33146e334f5d1dfdce9f94..8076f35515ff9cfd2f86084c91a4c5d96986dfe5 100644 --- a/src/sakia/data/processors/tx_lifecycle.py +++ b/src/sakia/data/processors/tx_lifecycle.py @@ -38,6 +38,7 @@ def _broadcast_failure(tx, ret_codes): """ return 200 not in ret_codes + def _is_locally_created(tx): """ Check if we can send back the transaction if it was locally created diff --git a/src/sakia/gui/widgets/context_menu.py b/src/sakia/gui/widgets/context_menu.py index 20dbe7f5de3353b2f988130801681f2ebc77d791..96844f8fcae8cb5393744bdefd8c7f7ad3e91640 100644 --- a/src/sakia/gui/widgets/context_menu.py +++ b/src/sakia/gui/widgets/context_menu.py @@ -126,7 +126,6 @@ class ContextMenu(QObject): UserInformationController.search_and_show_pubkey(self.parent(), self._app, identity.pubkey) - @asyncify async def send_money(self, identity): await TransferController.send_money_to_identity(None, self._app, self._connection, identity) @@ -148,7 +147,8 @@ This money transfer will be removed and not sent."""), QMessageBox.Ok | QMessageBox.Cancel) if reply == QMessageBox.Ok: transactions_processor = TransactionsProcessor.instanciate(self._app) - transactions_processor.cancel(transfer) + if transactions_processor.cancel(transfer): + self._app.sources_service.restore_sources(self._connection.pubkey, transfer) self._app.db.commit() self._app.transaction_state_changed.emit(transfer) diff --git a/src/sakia/services/sources.py b/src/sakia/services/sources.py index 796979ed9200075125ad39bb463e5397f9678d3a..cba50cd1b93ca9e43fc8a418495e708500deec2c 100644 --- a/src/sakia/services/sources.py +++ b/src/sakia/services/sources.py @@ -172,3 +172,30 @@ class SourcesServices(QObject): # this is acceptable I guess destructions += await self.refresh_sources_of_pubkey(pubkey, transactions, dividends, current_base) return destructions + + def restore_sources(self, pubkey, tx): + """ + Restore the sources of a cancelled tx + :param sakia.entities.Transaction tx: + """ + txdoc = TransactionDoc.from_signed_raw(tx.raw) + for offset, output in enumerate(txdoc.outputs): + if output.conditions.left.pubkey == pubkey: + source = Source(currency=self.currency, + pubkey=pubkey, + identifier=txdoc.sha_hash, + type='T', + noffset=offset, + amount=output.amount, + base=output.base) + self._sources_processor.drop(source) + for index, input in enumerate(txdoc.inputs): + source = Source(currency=self.currency, + pubkey=txdoc.issuers[0], + identifier=input.origin_id, + type=input.source, + noffset=input.index, + amount=input.amount, + base=input.base) + if source.pubkey == pubkey: + self._sources_processor.insert(source) diff --git a/tests/technical/test_sources_service.py b/tests/technical/test_sources_service.py index dc6711ec583d2b5bf0e046db144d9059bcfb3ece..bb65798ccc55ef9bb70c2d3645993329a6337c3f 100644 --- a/tests/technical/test_sources_service.py +++ b/tests/technical/test_sources_service.py @@ -1,5 +1,6 @@ import pytest from sakia.data.entities import Transaction +from sakia.data.processors import TransactionsProcessor @pytest.mark.asyncio @@ -32,7 +33,6 @@ async def test_send_source(application_with_one_connection, fake_server, bob, al await fake_server.close() - @pytest.mark.asyncio async def test_destruction(application_with_one_connection, fake_server, bob, alice): amount = application_with_one_connection.sources_service.amount(bob.key.pubkey) @@ -49,3 +49,33 @@ async def test_destruction(application_with_one_connection, fake_server, bob, al assert tx_after_parse[-1].comment == "Too low balance" await fake_server.close() + +@pytest.mark.asyncio +async def test_send_tx_then_cancel(application_with_one_connection, fake_server, bob, alice): + tx_before_send = application_with_one_connection.transactions_service.transfers(bob.key.pubkey) + sources_before_send = application_with_one_connection.sources_service.amount(bob.key.pubkey) + bob_connection = application_with_one_connection.db.connections_repo.get_one(pubkey=bob.key.pubkey) + fake_server.reject_next_post = True + await application_with_one_connection.documents_service.send_money(bob_connection, + bob.salt, + bob.password, + alice.key.pubkey, 10, 0, "Test comment") + tx_after_send = application_with_one_connection.transactions_service.transfers(bob.key.pubkey) + sources_after_send = application_with_one_connection.sources_service.amount(bob.key.pubkey) + assert len(tx_before_send) + 1 == len(tx_after_send) + assert sources_before_send - 10 >= sources_after_send + assert tx_after_send[-1].state is Transaction.REFUSED + assert tx_after_send[-1].written_block == 0 + + transactions_processor = TransactionsProcessor.instanciate(application_with_one_connection) + if transactions_processor.cancel(tx_after_send[-1]): + application_with_one_connection.sources_service.restore_sources(bob.key.pubkey, tx_after_send[-1]) + + tx_after_cancel = application_with_one_connection.transactions_service.transfers(bob.key.pubkey) + sources_after_cancel = application_with_one_connection.sources_service.amount(bob.key.pubkey) + assert tx_after_cancel[-1].state is Transaction.DROPPED + assert tx_after_cancel[-1].written_block == 0 + assert len(tx_before_send) + 1 == len(tx_after_cancel) + assert sources_before_send == sources_after_cancel + + await fake_server.close() \ No newline at end of file diff --git a/tests/technical/test_transactions_service.py b/tests/technical/test_transactions_service.py index 388c23c77938571060698a4e4544ba2e12bfc155..2d45e07737402b50c3839722545f1d8e59db6275 100644 --- a/tests/technical/test_transactions_service.py +++ b/tests/technical/test_transactions_service.py @@ -56,4 +56,3 @@ async def test_issue_dividend(application_with_one_connection, fake_server, bob) dividends_after_parse = application_with_one_connection.transactions_service.dividends(bob.key.pubkey) assert len(dividends_before_send) + 2 == len(dividends_after_parse) await fake_server.close() -