From 419a1b5c061f7ba8b305460295a6ae7fb7dce565 Mon Sep 17 00:00:00 2001
From: Inso <insomniak.fr@gmail.com>
Date: Thu, 18 Dec 2014 22:50:56 +0100
Subject: [PATCH] Transactions : OK

---
 lib/ucoinpy/api/bma/tx/__init__.py      |   1 -
 lib/ucoinpy/documents/transaction.py    |   3 -
 res/ui/transferDialog.ui                | 123 ++++++++++++++++++++++--
 src/cutecoin/core/community.py          |   6 +-
 src/cutecoin/core/wallet.py             |  62 +++++++++++-
 src/cutecoin/gui/transferMoneyDialog.py |  64 +++++++++---
 src/cutecoin/tools/exceptions.py        |  15 +++
 7 files changed, 245 insertions(+), 29 deletions(-)

diff --git a/lib/ucoinpy/api/bma/tx/__init__.py b/lib/ucoinpy/api/bma/tx/__init__.py
index 10ea07a5..0242efae 100644
--- a/lib/ucoinpy/api/bma/tx/__init__.py
+++ b/lib/ucoinpy/api/bma/tx/__init__.py
@@ -31,7 +31,6 @@ class Process(Tx):
 
     def __post__(self, **kwargs):
         assert 'transaction' in kwargs
-        assert 'signature' in kwargs
 
         return self.requests_post('/process', **kwargs).json()
 
diff --git a/lib/ucoinpy/documents/transaction.py b/lib/ucoinpy/documents/transaction.py
index 251c6b89..ce7d84a0 100644
--- a/lib/ucoinpy/documents/transaction.py
+++ b/lib/ucoinpy/documents/transaction.py
@@ -183,9 +183,6 @@ Issuers:
             doc += "{0}".format(self.comment)
         doc += "\n"
 
-        for signature in self.signatures:
-            doc += "{0}\n".format(signature)
-
         return doc
 
     def compact(self):
diff --git a/res/ui/transferDialog.ui b/res/ui/transferDialog.ui
index 1f08eac3..b7676c86 100644
--- a/res/ui/transferDialog.ui
+++ b/res/ui/transferDialog.ui
@@ -6,14 +6,26 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>400</width>
-    <height>486</height>
+    <width>399</width>
+    <height>402</height>
    </rect>
   </property>
   <property name="windowTitle">
    <string>Transfer money</string>
   </property>
   <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QGroupBox" name="groupBox_2">
+     <property name="title">
+      <string>Community</string>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout_4">
+      <item>
+       <widget class="QComboBox" name="combo_community"/>
+      </item>
+     </layout>
+    </widget>
+   </item>
    <item>
     <widget class="QGroupBox" name="groupBox">
      <property name="title">
@@ -46,7 +58,7 @@
         <item>
          <widget class="QRadioButton" name="radio_key_fingerprint">
           <property name="text">
-           <string>Recipient fingerprint</string>
+           <string>Recipient public key</string>
           </property>
           <property name="checked">
            <bool>false</bool>
@@ -65,7 +77,7 @@
            <string/>
           </property>
           <property name="placeholderText">
-           <string>Key fingerprint</string>
+           <string>Key</string>
           </property>
          </widget>
         </item>
@@ -107,6 +119,56 @@
         </item>
        </layout>
       </item>
+      <item>
+       <widget class="QLabel" name="label_total">
+        <property name="text">
+         <string>Availalble currency : </string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_3">
+        <item>
+         <widget class="QLabel" name="label_3">
+          <property name="text">
+           <string>Amount :</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QDoubleSpinBox" name="spinbox_amount">
+          <property name="suffix">
+           <string/>
+          </property>
+          <property name="maximum">
+           <double>99999999.000000000000000</double>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QDoubleSpinBox" name="spinbox_relative">
+          <property name="suffix">
+           <string> UD</string>
+          </property>
+          <property name="maximum">
+           <double>9999999.000000000000000</double>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </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"/>
+      </item>
      </layout>
     </widget>
    </item>
@@ -188,13 +250,62 @@
     </hint>
    </hints>
   </connection>
+  <connection>
+   <sender>combo_community</sender>
+   <signal>currentIndexChanged(int)</signal>
+   <receiver>TransferMoneyDialog</receiver>
+   <slot>change_current_community(int)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>199</x>
+     <y>50</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>199</x>
+     <y>165</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>spinbox_amount</sender>
+   <signal>valueChanged(double)</signal>
+   <receiver>TransferMoneyDialog</receiver>
+   <slot>amount_changed()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>199</x>
+     <y>269</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>199</x>
+     <y>165</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>spinbox_relative</sender>
+   <signal>valueChanged(double)</signal>
+   <receiver>TransferMoneyDialog</receiver>
+   <slot>relative_amount_changed()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>320</x>
+     <y>269</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>199</x>
+     <y>165</y>
+    </hint>
+   </hints>
+  </connection>
  </connections>
  <slots>
-  <slot>add_coins_to_transfer()</slot>
-  <slot>remove_coins_from_transfer()</slot>
   <slot>open_manage_wallet_coins()</slot>
   <slot>change_displayed_wallet(int)</slot>
   <slot>transfer_mode_changed(bool)</slot>
   <slot>recipient_mode_changed(bool)</slot>
+  <slot>change_current_community(int)</slot>
+  <slot>amount_changed()</slot>
+  <slot>relative_amount_changed()</slot>
  </slots>
 </ui>
diff --git a/src/cutecoin/core/community.py b/src/cutecoin/core/community.py
index 03402876..3561dc67 100644
--- a/src/cutecoin/core/community.py
+++ b/src/cutecoin/core/community.py
@@ -53,7 +53,11 @@ class Community(object):
         return (other.currency == self.currency)
 
     def dividend(self):
-        return 100
+        ud = self.request(bma.blockchain.UD)
+        block_number = ud['result']['blocks'][-1]
+        block = self.request(bma.blockchain.Block,
+                             req_args={'number': block_number})
+        return block['dividend']
 
     def send_pubkey(self, account):
         pass
diff --git a/src/cutecoin/core/wallet.py b/src/cutecoin/core/wallet.py
index 8d61ed0c..e94bb7a7 100644
--- a/src/cutecoin/core/wallet.py
+++ b/src/cutecoin/core/wallet.py
@@ -4,9 +4,14 @@ Created on 1 févr. 2014
 @author: inso
 '''
 
+from ucoinpy import PROTOCOL_VERSION
 from ucoinpy.api import bma
-from ucoinpy.documents.transaction import InputSource
+from nacl.encoding import Base64Encoder
+from ucoinpy.documents.transaction import InputSource, OutputSource, Transaction
 from ucoinpy.key import SigningKey
+from ..tools.exceptions import NotEnoughMoneyError
+import logging
+import base64
 
 
 class Wallet(object):
@@ -53,8 +58,59 @@ class Wallet(object):
             value += s.amount
         return value
 
-    def send_money(self, recipient, amount, message):
-        pass
+    def tx_inputs(self, amount, community):
+        value = 0
+        inputs = []
+        for s in self.sources(community):
+            value += s.amount
+            s.index = 0
+            inputs.append(s)
+            if value >= amount:
+                return inputs
+        raise NotEnoughMoneyError(amount, value)
+        return []
+
+    def tx_outputs(self, pubkey, amount, inputs):
+        outputs = []
+        inputs_value = 0
+        for i in inputs:
+            logging.debug(i)
+            inputs_value += i.amount
+
+        overhead = inputs_value - int(amount)
+        outputs.append(OutputSource(pubkey, int(amount)))
+        if overhead != 0:
+            outputs.append(OutputSource(self.pubkey, overhead))
+        return outputs
+
+    def send_money(self, salt, password, community,
+                   recipient, amount, message):
+
+        inputs = self.tx_inputs(int(amount), community)
+        logging.debug("Inputs : {0}".format(inputs))
+        outputs = self.tx_outputs(recipient, amount, inputs)
+        logging.debug("Outputs : {0}".format(outputs))
+        tx = Transaction(PROTOCOL_VERSION, community.currency,
+                         [self.pubkey], inputs,
+                         outputs, message, None)
+        logging.debug("TX : {0}".format(tx.raw()))
+        key = None
+        logging.debug("Key : {0} : {1}".format(salt, password))
+        if self.walletid == 0:
+            key = SigningKey(salt, password)
+        else:
+            key = SigningKey("{0}{1}".format(salt, self.walletid), password)
+        logging.debug("Sender pubkey:{0}".format(key.pubkey))
+
+        signing = key.sign(bytes(tx.raw(), 'ascii'), Base64Encoder)
+        logging.debug("Signature : {0}".format(str(signing.signature)))
+        tx.signatures = [str(signing.signature, 'ascii')]
+        logging.debug("Transaction : {0}".format(tx.signed_raw()))
+        try:
+            community.post(bma.tx.Process,
+                                post_args={'transaction': tx.signed_raw()})
+        except ValueError as e:
+            logging.debug("Error : {0}".format(e))
 
     def sources(self, community):
         data = community.request(bma.tx.Sources, req_args={'pubkey': self.pubkey})
diff --git a/src/cutecoin/gui/transferMoneyDialog.py b/src/cutecoin/gui/transferMoneyDialog.py
index 7bc8eb56..7af71709 100644
--- a/src/cutecoin/gui/transferMoneyDialog.py
+++ b/src/cutecoin/gui/transferMoneyDialog.py
@@ -3,8 +3,7 @@ Created on 2 févr. 2014
 
 @author: inso
 '''
-from PyQt5.QtWidgets import QDialog, QErrorMessage
-
+from PyQt5.QtWidgets import QDialog, QErrorMessage, QInputDialog, QLineEdit
 
 from cutecoin.core.person import Person
 
@@ -25,31 +24,66 @@ class TransferMoneyDialog(QDialog, Ui_TransferMoneyDialog):
         self.setupUi(self)
         self.sender = sender
         self.recipient_trusts = []
-        self.wallet = sender.wallets[0]
-        for wallet in sender.wallets:
-            self.combo_wallets.addItem(wallet.get_text())
+        self.wallet = None
+        self.community = self.sender.communities[0]
+        self.wallet = self.sender.wallets[0]
+        self.dividend = self.community.dividend()
+
+        for community in self.sender.communities:
+            self.combo_community.addItem(community.currency)
+
+        for wallet in self.sender.wallets:
+            self.combo_wallets.addItem(wallet.name)
 
         for contact in sender.contacts:
             self.combo_contact.addItem(contact.name)
 
-    def refresh_total(self):
-        dividend = self.wallet.get_block()['dividend']
-        total = self.list_coins_sent.model().total()
-        relative_total = total / int(dividend)
-        self.label_total.setText("Total : \n \
-%d %s \n \
-%.2f UD" % (total, self.wallet.currency, relative_total))
-
     def accept(self):
         message = self.edit_message.text()
-        #error = self.wallet.send_money(recipient, amount, message)
+        recipient = self.edit_key_fingerprint.text()
+        amount = self.spinbox_amount.value()
+        password = QInputDialog.getText(self, "Wallet password",
+                                        "Please enter your password",
+                                        QLineEdit.Password)
+        if password[1] is True:
+            password = password[0]
+        else:
+            return
+
+        error = self.wallet.send_money(self.sender.salt, password, self.community,
+                                       recipient, amount, message)
 
         self.accepted.emit()
         self.close()
 
+    def amount_changed(self):
+        amount = self.spinbox_amount.value()
+        dividend = self.community.dividend()
+        relative = amount / self.dividend
+        self.spinbox_relative.blockSignals(True)
+        self.spinbox_relative.setValue(relative)
+        self.spinbox_relative.blockSignals(False)
+
+    def relative_amount_changed(self):
+        relative = self.spinbox_relative.value()
+        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.sender.communities[index]
+        self.dividend = self.community.dividend()
+        self.label_total.setText(self.wallet.get_text(self.community))
+        self.spinbox_amount.setSuffix(" " + self.community.currency)
+        self.spinbox_amount.setValue(0)
+        self.spinbox_amount.setMaximum(self.wallet.value(self.community))
+
     def change_displayed_wallet(self, index):
         self.wallet = self.sender.wallets[index]
-        self.refresh_transaction()
+        self.label_total.setText(self.wallet.get_text(self.community))
+        self.spinbox_amount.setValue(0)
+        self.spinbox_amount.setMaximum(self.wallet.value(self.community))
 
     def recipient_mode_changed(self, fingerprint_toggled):
         self.edit_key_fingerprint.setEnabled(fingerprint_toggled)
diff --git a/src/cutecoin/tools/exceptions.py b/src/cutecoin/tools/exceptions.py
index 31613389..110da3da 100644
--- a/src/cutecoin/tools/exceptions.py
+++ b/src/cutecoin/tools/exceptions.py
@@ -111,3 +111,18 @@ class BadAccountFile(Error):
         '''
         super() .__init__(
             "File " + path + " is not an account file")
+
+
+class NotEnoughMoneyError(Error):
+
+    '''
+    Exception raised trying to add an account using
+    a key already used for another account.
+    '''
+
+    def __init__(self, available, requested):
+        '''
+        Constructor
+        '''
+        super() .__init__(
+            "Key owns only {0} money, needs {1}".format(available, requested))
-- 
GitLab