From de0a4aa088bc7d7eadb6e010a553b402a1665b88 Mon Sep 17 00:00:00 2001
From: inso <insomniak.fr@gmaiL.com>
Date: Fri, 3 Feb 2017 19:31:43 +0100
Subject: [PATCH] Fix bug #594 : Sources are not restored w/ cancel

---
 src/sakia/data/connectors/bma.py             |  1 -
 src/sakia/data/processors/transactions.py    |  2 +-
 src/sakia/data/processors/tx_lifecycle.py    |  1 +
 src/sakia/gui/widgets/context_menu.py        |  4 +--
 src/sakia/services/sources.py                | 27 +++++++++++++++++
 tests/technical/test_sources_service.py      | 32 +++++++++++++++++++-
 tests/technical/test_transactions_service.py |  1 -
 7 files changed, 62 insertions(+), 6 deletions(-)

diff --git a/src/sakia/data/connectors/bma.py b/src/sakia/data/connectors/bma.py
index 20dc6bef..090164cc 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 0705b6fc..2ed0d93a 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 a1d858a2..8076f355 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 20dbe7f5..96844f8f 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 796979ed..cba50cd1 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 dc6711ec..bb65798c 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 388c23c7..2d45e077 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()
-
-- 
GitLab