From 9b7affb87e94a4cbf5c212d321bc29a2353938b2 Mon Sep 17 00:00:00 2001
From: vtexier <vit@free.fr>
Date: Mon, 30 Mar 2020 15:51:06 +0200
Subject: [PATCH] [enh] #798 fully functional "Send as source" feature for a
 CSV/CLTV locked source

---
 src/sakia/gui/sub/transfer/controller.py |  9 ++++++
 src/sakia/gui/sub/transfer/view.py       |  9 +++++-
 src/sakia/services/documents.py          |  4 ++-
 tests/functional/test_transfer_dialog.py | 40 ++++++++++++++++++++++++
 4 files changed, 60 insertions(+), 2 deletions(-)

diff --git a/src/sakia/gui/sub/transfer/controller.py b/src/sakia/gui/sub/transfer/controller.py
index 74be7cb3..b2acda6c 100644
--- a/src/sakia/gui/sub/transfer/controller.py
+++ b/src/sakia/gui/sub/transfer/controller.py
@@ -306,9 +306,13 @@ class TransferController(QObject):
             else:
                 self.view.set_button_box(TransferView.ButtonBoxState.NO_RECEIVER)
         elif self.password_input.valid():
+            # if source and check source successful...
             self.view.set_button_box(TransferView.ButtonBoxState.OK)
         else:
             self.view.set_button_box(TransferView.ButtonBoxState.WRONG_PASSWORD)
+        # if source and check source button still enabled...
+        if self.model.current_source and self.view.button_source_check.isEnabled():
+            self.view.set_button_box(TransferView.ButtonBoxState.SOURCE_LOCKED)
 
         max_relative = self.model.quant_to_rel(current_base_amount / 100)
         self.view.spinbox_amount.setSuffix(Quantitative.base_str(current_base))
@@ -331,6 +335,7 @@ class TransferController(QObject):
         self.refresh()
 
     def check_source(self):
+        # evaluate condition
         source = self.model.current_source
         condition = pypeg2.parse(source.conditions, Condition)
         result, _errors = self.model.app.sources_service.evaluate_condition(
@@ -340,10 +345,14 @@ class TransferController(QObject):
             [],
             source.identifier,
         )
+        # if success...
         if result:
             message = QCoreApplication.translate(
                 "TransferController", "Check is successful!"
             )
+            self.view.button_source_check.setDisabled(True)
+            self.refresh()
+        # if failure...
         else:
             message = QCoreApplication.translate(
                 "TransferController", "<p><b>Condition</b></p>{}"
diff --git a/src/sakia/gui/sub/transfer/view.py b/src/sakia/gui/sub/transfer/view.py
index 53c10e63..45e1fd31 100644
--- a/src/sakia/gui/sub/transfer/view.py
+++ b/src/sakia/gui/sub/transfer/view.py
@@ -18,6 +18,7 @@ class TransferView(QWidget, Ui_TransferMoneyWidget):
         WRONG_PASSWORD = 2
         NO_RECEIVER = 3
         WRONG_RECIPIENT = 4
+        SOURCE_LOCKED = 5
 
     class RecipientMode(Enum):
         PUBKEY = 1
@@ -45,6 +46,10 @@ class TransferView(QWidget, Ui_TransferMoneyWidget):
             False,
             QT_TRANSLATE_NOOP("TransferView", "Incorrect receiver address or pubkey"),
         ),
+        ButtonBoxState.SOURCE_LOCKED: (
+            False,
+            QT_TRANSLATE_NOOP("TransferView", "Source locked"),
+        ),
     }
 
     def __init__(
@@ -60,7 +65,9 @@ class TransferView(QWidget, Ui_TransferMoneyWidget):
         super().__init__(parent)
         self.setupUi(self)
 
-        regexp = QRegExp("^([ a-zA-Z0-9-_:/;*?\[\]\(\)\\\?!^+=@&~#{}|<>%.]{0,255})$")
+        regexp = QRegExp(
+            "^([ a-zA-Z0-9-_:/;*?\\[\\]\\(\\)\\\\?!^+=@&~#{}|<>%.]{0,255})$"
+        )
         validator = QRegExpValidator(regexp)
         self.edit_message.setValidator(validator)
 
diff --git a/src/sakia/services/documents.py b/src/sakia/services/documents.py
index 2ca92823..b9011c80 100644
--- a/src/sakia/services/documents.py
+++ b/src/sakia/services/documents.py
@@ -534,6 +534,8 @@ class DocumentsService:
                     forged_tx += chained_tx
         else:
             sources = [source]
+            computed_outputs = [(source.amount, source.base)]
+            overheads = []
 
         logging.debug("Inputs: {0}".format(sources))
 
@@ -596,7 +598,6 @@ class DocumentsService:
     ):
         """
         Send money to a given recipient in a specified community
-        :param Source source: Source instance or None
         :param int lock_mode: Index in the combo_locks combobox
         :param sakia.data.entities.Connection connection: The account salt
         :param str secret_key: The account secret_key
@@ -605,6 +606,7 @@ class DocumentsService:
         :param int amount: The amount of money to transfer
         :param int amount_base: The amount base of the transfer
         :param str message: The message to send with the transfer
+        :param Source source: Source instance or None
         """
         blockstamp = self._blockchain_processor.current_buid(connection.currency)
         key = SigningKey.from_credentials(
diff --git a/tests/functional/test_transfer_dialog.py b/tests/functional/test_transfer_dialog.py
index 52a31315..b156c899 100644
--- a/tests/functional/test_transfer_dialog.py
+++ b/tests/functional/test_transfer_dialog.py
@@ -74,3 +74,43 @@ async def test_transfer_chained_tx(
 
     transfer_dialog.view.show()
     await exec_test()
+
+
+@pytest.mark.asyncio
+async def test_transfer_from_source(
+    application_with_one_connection, fake_server_with_blockchain, bob, alice
+):
+    def close_dialog():
+        if transfer_dialog.view.isVisible():
+            transfer_dialog.view.hide()
+
+    async def exec_test():
+        await asyncio.sleep(0.1)
+        assert not transfer_dialog.view.button_box.button(
+            QDialogButtonBox.Ok
+        ).isEnabled()
+        assert transfer_dialog.view.spinbox_amount.value() == 1
+        await asyncio.sleep(0.1)
+        # simulate checked source condition
+        transfer_dialog.view.button_source_check.setDisabled(True)
+        QTest.keyClicks(transfer_dialog.view.password_input.edit_secret_key, bob.salt)
+        QTest.keyClicks(transfer_dialog.view.password_input.edit_password, bob.password)
+        assert transfer_dialog.view.button_box.button(QDialogButtonBox.Ok).isEnabled()
+        QTest.mouseClick(
+            transfer_dialog.view.button_box.button(QDialogButtonBox.Ok), Qt.LeftButton
+        )
+        await asyncio.sleep(0.2)
+        assert isinstance(fake_server_with_blockchain.forge.pool[0], Transaction)
+
+    application_with_one_connection.loop.call_later(10, close_dialog)
+
+    bob_connection = application_with_one_connection.db.connections_repo.get_one(
+        pubkey=bob.key.pubkey
+    )
+    source = application_with_one_connection.sources_service.get_one(
+        pubkey=bob.key.pubkey
+    )
+    transfer_dialog = TransferController.open_transfer_with_pubkey(
+        None, application_with_one_connection, bob_connection, alice.key.pubkey, source
+    )
+    await exec_test()
-- 
GitLab