diff --git a/requirements.txt b/requirements.txt index 3af2d740476a6aa0e6d581dd451b95fecaf14da9..2d28e6eeb401767808dcac6152a25a78607d4218 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +pypeg2 aiohttp==3.6.2 async-timeout==3.0.1 asynctest==0.13.0 diff --git a/src/sakia/gui/sub/transfer/controller.py b/src/sakia/gui/sub/transfer/controller.py index 86408113dd88310e4024fd195e6871b985b382ac..e2d64c0491c43606b91c519cbe25572c52d6881f 100644 --- a/src/sakia/gui/sub/transfer/controller.py +++ b/src/sakia/gui/sub/transfer/controller.py @@ -228,9 +228,11 @@ class TransferController(QObject): logging.debug("Setting cursor...") QApplication.setOverrideCursor(Qt.WaitCursor) + lock_mode = self.view.combo_locks.currentIndex() + logging.debug("Send money...") result, transactions = await self.model.send_money( - recipient, secret_key, password, amount, amount_base, comment + recipient, secret_key, password, amount, amount_base, comment, lock_mode ) if result[0]: await self.view.show_success(self.model.notifications(), recipient) diff --git a/src/sakia/gui/sub/transfer/model.py b/src/sakia/gui/sub/transfer/model.py index d7d873068afe0609ad57666f04d2ccc8ec31b654..e20261b82e4147833af2be37024e524a43a7d861 100644 --- a/src/sakia/gui/sub/transfer/model.py +++ b/src/sakia/gui/sub/transfer/model.py @@ -90,15 +90,18 @@ class TransferModel(QObject): self.connection = connections[index] async def send_money( - self, recipient, secret_key, password, amount, amount_base, comment + self, recipient, secret_key, password, amount, amount_base, comment, lock_mode ): """ Send money to given recipient using the account + :param lock_mode: :param str recipient: + :param str password: + :param str secret_key: :param int amount: :param int amount_base: :param str comment: - :param str password: + :param int lock_mode: :return: the result of the send """ @@ -110,6 +113,7 @@ class TransferModel(QObject): amount, amount_base, comment, + lock_mode, ) for transaction in transactions: self.app.sources_service.parse_transaction_outputs( diff --git a/src/sakia/gui/sub/transfer/transfer.ui b/src/sakia/gui/sub/transfer/transfer.ui index 10d81e625e3bfde45b853b8c0181743567bf1373..8a5da344b0244b4823b37ce1ffe4efceb303e7a0 100644 --- a/src/sakia/gui/sub/transfer/transfer.ui +++ b/src/sakia/gui/sub/transfer/transfer.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>479</width> - <height>511</height> + <width>800</width> + <height>513</height> </rect> </property> <property name="windowTitle"> @@ -248,20 +248,53 @@ </widget> </item> <item> - <widget class="QGroupBox" name="groupBox_3"> - <property name="title"> - <string>Transaction message</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_3"> - <item> - <widget class="QLineEdit" name="edit_message"> - <property name="inputMask"> - <string/> - </property> - </widget> - </item> - </layout> - </widget> + <layout class="QHBoxLayout" name="horizontalLayout_7"> + <item> + <widget class="QGroupBox" name="groupBox_3"> + <property name="title"> + <string>Message</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <item> + <widget class="QLineEdit" name="edit_message"> + <property name="inputMask"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Spend condition</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_9"> + <item> + <widget class="QComboBox" name="combo_locks"> + <item> + <property name="text"> + <string>Receiver signature</string> + </property> + </item> + <item> + <property name="text"> + <string>Receiver signature or (sender after one week)</string> + </property> + </item> + </widget> + </item> + </layout> + </widget> + </item> + </layout> </item> <item> <widget class="QGroupBox" name="groupBox"> diff --git a/src/sakia/services/documents.py b/src/sakia/services/documents.py index 35726cb09e14631936eb8877b0221678c7f287b8..8f65dec4a06db0385a0ebe2d906e6452fdde5e09 100644 --- a/src/sakia/services/documents.py +++ b/src/sakia/services/documents.py @@ -2,6 +2,7 @@ import jsonschema import attr import logging +import pypeg2 from duniterpy.key import SigningKey from duniterpy.documents import ( Certification, @@ -354,7 +355,10 @@ class DocumentsService: current_base -= 1 raise NotEnoughChangeError( - value, currency, len(sources), amount * pow(10, amount_base) + current_value(available_sources, []), + currency, + len(available_sources), + amount * pow(10, amount_base), ) def tx_inputs(self, sources): @@ -381,9 +385,10 @@ class DocumentsService: unlocks.append(Unlock(i, [SIGParameter(0)])) return unlocks - def tx_outputs(self, issuer, receiver, outputs, overheads): + def tx_outputs(self, issuer, receiver, outputs, overheads, lock_mode): """ Get outputs to generate a transaction with a given amount of money + :param int lock_mode: Index of the selected spend condition :param str issuer: The issuer of the transaction :param str receiver: The target of the transaction :param list outputs: The amount to send @@ -391,6 +396,26 @@ class DocumentsService: :param list overheads: The overheads used to send the given amount of money :return: The list of outputs to use in the transaction document """ + lock_modes = { + # Receiver + 0: pypeg2.compose( + output.Condition.token(output.SIG.token(receiver)), output.Condition + ), + # Receiver or (issuer and delay of one week) + 1: pypeg2.compose( + output.Condition.token( + output.SIG.token(receiver), + output.Operator.token("||"), + output.Condition.token( + output.SIG.token(issuer), + output.Operator.token("&&"), + output.CSV.token(604800), + ), + ), + output.Condition, + ), + } + total = [] outputs_bases = set(o[1] for o in outputs) for base in outputs_bases: @@ -400,15 +425,7 @@ class DocumentsService: output_sum += o[0] # fixme: OutputSource condition argument should be an instance of Condition, not a string # it is not to the user to construct the condition script, but to the dedicated classes - total.append( - OutputSource( - output_sum, - base, - output.Condition.token(output.SIG.token(receiver)).compose( - output.Condition() - ), - ) - ) + total.append(OutputSource(output_sum, base, lock_modes[lock_mode])) overheads_bases = set(o[1] for o in overheads) for base in overheads_bases: @@ -452,10 +469,19 @@ class DocumentsService: self._sources_processor.insert(source) def prepare_tx( - self, key, receiver, blockstamp, amount, amount_base, message, currency + self, + key, + receiver, + blockstamp, + amount, + amount_base, + message, + currency, + lock_mode=0, ): """ Prepare a simple Transaction document + :param int lock_mode: Lock condition mode selected in combo box :param SigningKey key: the issuer of the transaction :param str receiver: the target of the transaction :param duniterpy.documents.BlockUID blockstamp: the blockstamp @@ -494,7 +520,9 @@ class DocumentsService: inputs = self.tx_inputs(sources) unlocks = self.tx_unlocks(sources) - outputs = self.tx_outputs(key.pubkey, receiver, computed_outputs, overheads) + outputs = self.tx_outputs( + key.pubkey, receiver, computed_outputs, overheads, lock_mode + ) logging.debug("Outputs: {0}".format(outputs)) txdoc = TransactionDoc( 10, @@ -533,10 +561,19 @@ class DocumentsService: return forged_tx async def send_money( - self, connection, secret_key, password, recipient, amount, amount_base, message + self, + connection, + secret_key, + password, + recipient, + amount, + amount_base, + message, + lock_mode, ): """ Send money to a given recipient in a specified community + :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 :param str password: The account password @@ -561,6 +598,7 @@ class DocumentsService: amount_base, message, connection.currency, + lock_mode, ) for i, tx in enumerate(tx_entities):