Master Core  v0.0.9 - 2abfd2849db8ba7a83957c64eb976b406713c123
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
sendmpdialog.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2014 The Bitcoin developers
2 // Distributed under the MIT/X11 software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include "sendmpdialog.h"
6 #include "ui_sendmpdialog.h"
7 
8 #include "addresstablemodel.h"
9 #include "bitcoinunits.h"
10 #include "coincontroldialog.h"
11 #include "guiutil.h"
12 #include "optionsmodel.h"
13 #include "walletmodel.h"
14 #include "wallet.h"
15 #include "base58.h"
16 #include "coincontrol.h"
17 #include "ui_interface.h"
18 
19 #include <boost/filesystem.hpp>
20 
21 #include "leveldb/db.h"
22 #include "leveldb/write_batch.h"
23 
24 // potentially overzealous includes here
25 #include "base58.h"
26 #include "rpcserver.h"
27 #include "init.h"
28 #include "util.h"
29 #include <fstream>
30 #include <algorithm>
31 #include <vector>
32 #include <utility>
33 #include <string>
34 #include <boost/assign/list_of.hpp>
35 #include <boost/algorithm/string.hpp>
36 #include <boost/algorithm/string/find.hpp>
37 #include <boost/algorithm/string/join.hpp>
38 #include <boost/lexical_cast.hpp>
39 #include <boost/format.hpp>
40 #include <boost/filesystem.hpp>
41 #include "json/json_spirit_utils.h"
42 #include "json/json_spirit_value.h"
43 #include "leveldb/db.h"
44 #include "leveldb/write_batch.h"
45 // end potentially overzealous includes
46 using namespace json_spirit; // since now using Array in mastercore.h this needs to come first
47 
48 #include "mastercore.h"
49 using namespace mastercore;
50 
51 // potentially overzealous using here
52 using namespace std;
53 using namespace boost;
54 using namespace boost::assign;
55 using namespace leveldb;
56 // end potentially overzealous using
57 
58 #include "mastercore_dex.h"
60 #include "mastercore_tx.h"
61 #include "mastercore_sp.h"
62 
63 #include <QDateTime>
64 #include <QMessageBox>
65 #include <QScrollBar>
66 #include <QTextDocument>
67 
69  QDialog(parent),
70  ui(new Ui::SendMPDialog),
71  model(0)
72 {
73  ui->setupUi(this);
74  this->model = model;
75 
76 #ifdef Q_OS_MAC // Icons on push buttons are very uncommon on Mac
77  ui->clearButton->setIcon(QIcon());
78  ui->sendButton->setIcon(QIcon());
79 #endif
80 
81  // populate placeholder text
82  ui->sendToLineEdit->setPlaceholderText("Enter a Master Protocol address (e.g. 1MaSTeRPRotocolADDreSShef77z6A5S4P)");
83  ui->amountLineEdit->setPlaceholderText("Enter Amount");
84 
85  // populate property selector
86  for (unsigned int propertyId = 1; propertyId<100000; propertyId++)
87  {
88  if ((global_balance_money_maineco[propertyId] > 0) || (global_balance_reserved_maineco[propertyId] > 0))
89  {
90  string spName;
91  spName = getPropertyName(propertyId).c_str();
92  if(spName.size()>20) spName=spName.substr(0,23)+"...";
93  string spId = static_cast<ostringstream*>( &(ostringstream() << propertyId) )->str();
94  spName += " (#" + spId + ")";
95  if (isPropertyDivisible(propertyId)) { spName += " [D]"; } else { spName += " [I]"; }
96  ui->propertyComboBox->addItem(spName.c_str(),spId.c_str());
97  }
98  }
99  for (unsigned int propertyId = 1; propertyId<100000; propertyId++)
100  {
101  if ((global_balance_money_testeco[propertyId] > 0) || (global_balance_reserved_testeco[propertyId] > 0))
102  {
103  string spName;
104  spName = getPropertyName(propertyId+2147483647).c_str();
105  if(spName.size()>20) spName=spName.substr(0,23)+"...";
106  string spId = static_cast<ostringstream*>( &(ostringstream() << propertyId+2147483647) )->str();
107  spName += " (#" + spId + ")";
108  if (isPropertyDivisible(propertyId+2147483647)) { spName += " [D]"; } else { spName += " [I]"; }
109  ui->propertyComboBox->addItem(spName.c_str(),spId.c_str());
110  }
111  }
112 
113  // connect actions
114  connect(ui->propertyComboBox, SIGNAL(activated(int)), this, SLOT(propertyComboBoxChanged(int)));
115  connect(ui->sendFromComboBox, SIGNAL(activated(int)), this, SLOT(sendFromComboBoxChanged(int)));
116  connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clearButtonClicked()));
117  connect(ui->sendButton, SIGNAL(clicked()), this, SLOT(sendButtonClicked()));
118  connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64)), this, SLOT(balancesUpdated()));
119 
120  // initial update
121  updateProperty();
122  updateFrom();
123 }
124 
126 {
127  this->model = model;
128 }
129 
131 {
132  ui->sendToLineEdit->setText("");
133  ui->amountLineEdit->setText("");
134 }
135 
137 {
138  // update wallet balances
140  updateBalances();
141 
142  // check if this from address has sufficient fees for a send, if not light up warning label
143  QString selectedFromAddress = ui->sendFromComboBox->currentText();
144  int64_t inputTotal = feeCheck(selectedFromAddress.toStdString());
145  if (inputTotal>=50000)
146  {
147  ui->feeWarningLabel->setVisible(false);
148  }
149  else
150  {
151  string feeWarning = "Only " + FormatDivisibleMP(inputTotal) + " BTC are available at the sending address for fees, you can attempt to send the transaction anyway but this *may* not be sufficient.";
152  ui->feeWarningLabel->setText(QString::fromStdString(feeWarning));
153  ui->feeWarningLabel->setVisible(true);
154  }
155 }
156 
158 {
159  // update wallet balances
161 
162  // get currently selected from address
163  QString currentSetFromAddress = ui->sendFromComboBox->currentText();
164 
165  // clear address selector
166  ui->sendFromComboBox->clear();
167 
168  // populate from address selector
169  QString spId = ui->propertyComboBox->itemData(ui->propertyComboBox->currentIndex()).toString();
170  unsigned int propertyId = spId.toUInt();
171  LOCK(cs_tally);
172  for(map<string, CMPTally>::iterator my_it = mp_tally_map.begin(); my_it != mp_tally_map.end(); ++my_it)
173  {
174  string address = (my_it->first).c_str();
175  unsigned int id;
176  bool includeAddress=false;
177  (my_it->second).init();
178  while (0 != (id = (my_it->second).next()))
179  {
180  if(id==propertyId) { includeAddress=true; break; }
181  }
182  if (!includeAddress) continue; //ignore this address, has never transacted in this propertyId
183  if (!IsMyAddress(address)) continue; //ignore this address, it's not ours
184  ui->sendFromComboBox->addItem((my_it->first).c_str());
185  }
186 
187  // attempt to set from address back to what was originally in there before update
188  int fromIdx = ui->sendFromComboBox->findText(currentSetFromAddress);
189  if (fromIdx != -1) { ui->sendFromComboBox->setCurrentIndex(fromIdx); } // -1 means the currently set from address doesn't have a balance in the newly selected property
190 
191  // update placeholder text
192  if (isPropertyDivisible(propertyId))
193  {
194  ui->amountLineEdit->setPlaceholderText("Enter Divisible Amount");
195  }
196  else
197  {
198  ui->amountLineEdit->setPlaceholderText("Enter Indivisible Amount");
199  }
200  updateBalances();
201 }
202 
204 {
205  // populate balance for currently selected address and global wallet balance
206  QString spId = ui->propertyComboBox->itemData(ui->propertyComboBox->currentIndex()).toString();
207  unsigned int propertyId = spId.toUInt();
208  QString selectedFromAddress = ui->sendFromComboBox->currentText();
209  std::string stdSelectedFromAddress = selectedFromAddress.toStdString();
210  int64_t balanceAvailable = getUserAvailableMPbalance(stdSelectedFromAddress, propertyId);
211  int64_t globalAvailable = 0;
212  if (propertyId<2147483648) { globalAvailable = global_balance_money_maineco[propertyId]; } else { globalAvailable = global_balance_money_testeco[propertyId-2147483647]; }
213  QString balanceLabel;
214  QString globalLabel;
215  std::string tokenLabel;
216  if (propertyId==1) tokenLabel = " MSC";
217  if (propertyId==2) tokenLabel = " TMSC";
218  if (propertyId>2) tokenLabel = " SPT";
219  if (isPropertyDivisible(propertyId))
220  {
221  balanceLabel = QString::fromStdString("Address Balance (Available): " + FormatDivisibleMP(balanceAvailable) + tokenLabel);
222  globalLabel = QString::fromStdString("Wallet Balance (Available): " + FormatDivisibleMP(globalAvailable) + tokenLabel);
223  }
224  else
225  {
226  balanceLabel = QString::fromStdString("Address Balance (Available): " + FormatIndivisibleMP(balanceAvailable) + tokenLabel);
227  globalLabel = QString::fromStdString("Wallet Balance (Available): " + FormatIndivisibleMP(globalAvailable) + tokenLabel);
228  }
229  ui->addressBalanceLabel->setText(balanceLabel);
230  ui->globalBalanceLabel->setText(globalLabel);
231 }
232 
234 {
235  // get the property being sent and get divisibility
236  QString spId = ui->propertyComboBox->itemData(ui->propertyComboBox->currentIndex()).toString();
237  if (spId.toStdString().empty())
238  {
239  QMessageBox::critical( this, "Unable to send transaction",
240  "The property selected is not valid.\n\nPlease double-check the transction details thoroughly before retrying your send transaction." );
241  return;
242  }
243  unsigned int propertyId = spId.toUInt();
244  bool divisible = isPropertyDivisible(propertyId);
245 
246  // obtain the selected sender address
247  string strFromAddress = ui->sendFromComboBox->currentText().toStdString();
248  // push recipient address into a CBitcoinAddress type and check validity
249  CBitcoinAddress fromAddress;
250  if (false == strFromAddress.empty()) { fromAddress.SetString(strFromAddress); }
251  if (!fromAddress.IsValid())
252  {
253  QMessageBox::critical( this, "Unable to send transaction",
254  "The sender address selected is not valid.\n\nPlease double-check the transction details thoroughly before retrying your send transaction." );
255  return;
256  }
257 
258  // obtain the entered recipient address
259  string strRefAddress = ui->sendToLineEdit->text().toStdString();
260  // push recipient address into a CBitcoinAddress type and check validity
261  CBitcoinAddress refAddress;
262  if (false == strRefAddress.empty()) { refAddress.SetString(strRefAddress); }
263  if (!refAddress.IsValid())
264  {
265  QMessageBox::critical( this, "Unable to send transaction",
266  "The recipient address entered is not valid.\n\nPlease double-check the transction details thoroughly before retrying your send transaction." );
267  return;
268  }
269 
270  // warn if we have to truncate the amount due to a decimal amount for an indivisible property, but allow send to continue
271  string strAmount = ui->amountLineEdit->text().toStdString();
272  if (!divisible)
273  {
274  size_t pos = strAmount.find(".");
275  if (pos!=std::string::npos)
276  {
277  string tmpStrAmount = strAmount.substr(0,pos);
278  string strMsgText = "The amount entered contains a decimal however the property being sent is indivisible.\n\nThe amount entered will be truncated as follows:\n";
279  strMsgText += "Original amount entered: " + strAmount + "\nAmount that will be sent: " + tmpStrAmount + "\n\n";
280  strMsgText += "Do you still wish to process with the transaction?";
281  QString msgText = QString::fromStdString(strMsgText);
282  QMessageBox::StandardButton responseClick;
283  responseClick = QMessageBox::question(this, "Amount truncation warning", msgText, QMessageBox::Yes|QMessageBox::No);
284  if (responseClick == QMessageBox::No)
285  {
286  QMessageBox::critical( this, "Send transaction cancelled",
287  "The send transaction has been cancelled.\n\nPlease double-check the transction details thoroughly before retrying your send transaction." );
288  return;
289  }
290  strAmount = tmpStrAmount;
291  ui->amountLineEdit->setText(QString::fromStdString(strAmount));
292  }
293  }
294 
295  // use strToInt64 function to get the amount, using divisibility of the property
296  int64_t sendAmount = StrToInt64(strAmount, divisible);
297  if (0>=sendAmount)
298  {
299  QMessageBox::critical( this, "Unable to send transaction",
300  "The amount entered is not valid.\n\nPlease double-check the transction details thoroughly before retrying your send transaction." );
301  return;
302  }
303 
304  // check if sending address has enough funds
305  int64_t balanceAvailable = getUserAvailableMPbalance(fromAddress.ToString(), propertyId); //getMPbalance(fromAddress.ToString(), propertyId, MONEY);
306  if (sendAmount>balanceAvailable)
307  {
308  QMessageBox::critical( this, "Unable to send transaction",
309  "The selected sending address does not have a sufficient balance to cover the amount entered.\n\nPlease double-check the transction details thoroughly before retrying your send transaction." );
310  return;
311  }
312 
313  // check if wallet is still syncing, as this will currently cause a lockup if we try to send - compare our chain to peers to see if we're up to date
314  // Bitcoin Core devs have removed GetNumBlocksOfPeers, switching to a time based best guess scenario
315  uint32_t intBlockDate = GetLatestBlockTime(); // uint32, not using time_t for portability
316  QDateTime currentDate = QDateTime::currentDateTime();
317  int secs = QDateTime::fromTime_t(intBlockDate).secsTo(currentDate);
318  if(secs > 90*60)
319  {
320  QMessageBox::critical( this, "Unable to send transaction",
321  "The client is still synchronizing. Sending transactions can currently be performed only when the client has completed synchronizing." );
322  return;
323  }
324 
325  // validation checks all look ok, let's throw up a confirmation dialog
326  string strMsgText = "You are about to send the following transaction, please check the details thoroughly:\n\n";
327  string propDetails = getPropertyName(propertyId).c_str();
328  string spNum = static_cast<ostringstream*>( &(ostringstream() << propertyId) )->str();
329  propDetails += " (#" + spNum + ")";
330  strMsgText += "From: " + fromAddress.ToString() + "\nTo: " + refAddress.ToString() + "\nProperty: " + propDetails + "\nAmount that will be sent: ";
331  if (divisible) { strMsgText += FormatDivisibleMP(sendAmount); } else { strMsgText += FormatIndivisibleMP(sendAmount); }
332  strMsgText += "\n\nAre you sure you wish to send this transaction?";
333  QString msgText = QString::fromStdString(strMsgText);
334  QMessageBox::StandardButton responseClick;
335  responseClick = QMessageBox::question(this, "Confirm send transaction", msgText, QMessageBox::Yes|QMessageBox::No);
336  if (responseClick == QMessageBox::No)
337  {
338  QMessageBox::critical( this, "Send transaction cancelled",
339  "The send transaction has been cancelled.\n\nPlease double-check the transction details thoroughly before retrying your send transaction." );
340  return;
341  }
342 
343  // unlock the wallet
345  if(!ctx.isValid())
346  {
347  // Unlock wallet was cancelled/failed
348  QMessageBox::critical( this, "Send transaction failed",
349  "The send transaction has been cancelled.\n\nThe wallet unlock process must be completed to send a transaction." );
350  return;
351  }
352 
353  // send the transaction - UI will not send any extra reference amounts at this stage
354  int code = 0;
355  uint256 sendTXID = send_INTERNAL_1packet(fromAddress.ToString(), refAddress.ToString(), fromAddress.ToString(), propertyId, sendAmount, 0, 0, MSC_TYPE_SIMPLE_SEND, 0, &code);
356  if (0 != code)
357  {
358  string strCode = boost::lexical_cast<string>(code);
359  string strError;
360  switch(code)
361  {
362  case -212:
363  strError = "Error choosing inputs for the send transaction";
364  break;
365  case -233:
366  strError = "Error with redemption address";
367  break;
368  case -220:
369  strError = "Error with redemption address key ID";
370  break;
371  case -221:
372  strError = "Error obtaining public key for redemption address";
373  break;
374  case -222:
375  strError = "Error public key for redemption address is not valid";
376  break;
377  case -223:
378  strError = "Error validating redemption address";
379  break;
380  case -205:
381  strError = "Error with wallet object";
382  break;
383  case -206:
384  strError = "Error with selected inputs for the send transaction";
385  break;
386  case -211:
387  strError = "Error creating transaction (wallet may be locked or fees may not be sufficient)";
388  break;
389  case -213:
390  strError = "Error committing transaction";
391  break;
392  }
393  if (strError.empty()) strError = "Error code does not have associated error text.";
394  QMessageBox::critical( this, "Send transaction failed",
395  "The send transaction has failed.\n\nThe error code was: " + QString::fromStdString(strCode) + "\nThe error message was:\n" + QString::fromStdString(strError));
396  return;
397  }
398  else
399  {
400  // call an update of the balances
402  updateBalances();
403 
404  // display the result
405  string strSentText = "Your Master Protocol transaction has been sent.\n\nThe transaction ID is:\n\n";
406  strSentText += sendTXID.GetHex() + "\n\n";
407  QString sentText = QString::fromStdString(strSentText);
408  QMessageBox sentDialog;
409  sentDialog.setIcon(QMessageBox::Information);
410  sentDialog.setWindowTitle("Transaction broadcast successfully");
411  sentDialog.setText(sentText);
412  sentDialog.setStandardButtons(QMessageBox::Yes|QMessageBox::Ok);
413  sentDialog.setDefaultButton(QMessageBox::Ok);
414  sentDialog.setButtonText( QMessageBox::Yes, "Copy TXID to clipboard" );
415  if(sentDialog.exec() == QMessageBox::Yes)
416  {
417  // copy TXID to clipboard
418  GUIUtil::setClipboard(QString::fromStdString(sendTXID.GetHex()));
419  }
420  // clear the form
421  clearFields();
422  }
423 }
424 
426 {
427  updateFrom();
428 }
429 
431 {
432  updateProperty();
433 }
434 
436 {
437  clearFields();
438 }
439 
441 {
443 }
444 
446 {
447  updateBalances();
448 }
uint32_t GetLatestBlockTime(void)
Definition: mastercore.cpp:168
bool IsValid() const
Definition: base58.cpp:215
void propertyComboBoxChanged(int idx)
Definition: init.h:13
QPushButton * clearButton
void setupUi(QDialog *SendMPDialog)
UnlockContext requestUnlock()
Dialog for sending Master Protocol tokens.
Definition: sendmpdialog.h:26
STL namespace.
QComboBox * propertyComboBox
uint64_t global_balance_reserved_testeco[100000]
Definition: mastercore.cpp:93
void balancesUpdated()
base58-encoded Bitcoin addresses.
Definition: base58.h:101
QLineEdit * sendToLineEdit
bool isPropertyDivisible(unsigned int propertyId)
SendMPDialog(QWidget *parent=0)
WalletModel * model
Definition: sendmpdialog.h:57
bool IsMyAddress(const std::string &address)
void sendButtonClicked()
void clearButtonClicked()
void setClipboard(const QString &str)
Definition: guiutil.cpp:755
#define LOCK(cs)
Definition: sync.h:156
QPushButton * sendButton
Ui::SendMPDialog * ui
Definition: sendmpdialog.h:56
QLabel * addressBalanceLabel
uint64_t global_balance_money_testeco[100000]
Definition: mastercore.cpp:92
std::map< string, CMPTally > mp_tally_map
Definition: mastercore.cpp:423
std::string ToString() const
Definition: base58.cpp:174
int64_t feeCheck(const string &address)
uint64_t global_balance_reserved_maineco[100000]
Definition: mastercore.cpp:91
uint256 send_INTERNAL_1packet(const string &FromAddress, const string &ToAddress, const string &RedeemAddress, unsigned int PropertyID, uint64_t Amount, unsigned int PropertyID_2, uint64_t Amount_2, unsigned int TransactionType, int64_t additional, int *error_code=NULL)
std::string GetHex() const
Definition: uint256.h:297
CCriticalSection cs_tally
Definition: mastercore.cpp:357
bool SetString(const char *psz, unsigned int nVersionBytes=1)
Definition: base58.cpp:154
QLineEdit * amountLineEdit
QComboBox * sendFromComboBox
string getPropertyName(unsigned int propertyId)
256-bit unsigned integer
Definition: uint256.h:531
int set_wallet_totals()
Definition: mastercore.cpp:943
Interface to Bitcoin wallet from Qt view code.
Definition: walletmodel.h:96
std::string FormatIndivisibleMP(int64_t n)
Definition: mastercore.cpp:343
void setModel(WalletModel *model)
void sendFromComboBoxChanged(int idx)
string FormatDivisibleMP(int64_t n, bool fSign)
Definition: mastercore.cpp:325
void updateBalances()
QLabel * feeWarningLabel
void sendMPTransaction()
int64_t StrToInt64(const std::string &str, bool divisible)
void updateProperty()
QLabel * globalBalanceLabel
int64_t getUserAvailableMPbalance(const string &Address, unsigned int property)
Definition: mastercore.cpp:455
uint64_t global_balance_money_maineco[100000]
Definition: mastercore.cpp:90
void clearFields()