устранил предупреждения компилятора
[novacoin.git] / src / qt / multisigdialog.cpp
1 #include <QClipboard>
2 #include <QDialog>
3 #include <QMessageBox>
4 #include <QScrollBar>
5 #include <vector>
6
7 #include "addresstablemodel.h"
8 #include "base58.h"
9 #include "key.h"
10 #include "main.h"
11 #include "multisigaddressentry.h"
12 #include "multisiginputentry.h"
13 #include "multisigdialog.h"
14 #include "ui_multisigdialog.h"
15 #include "script.h"
16 #include "sendcoinsentry.h"
17 #include "util.h"
18 #include "wallet.h"
19 #include "walletmodel.h"
20
21 #ifdef USE_LEVELDB
22 #include "txdb-leveldb.h"
23 #else
24 #include "txdb-bdb.h"
25 #endif
26
27 #ifdef _MSC_VER
28 #pragma warning( disable : 4101)
29 #endif
30
31 MultisigDialog::MultisigDialog(QWidget *parent) : QDialog(parent), ui(new Ui::MultisigDialog), model(0)
32 {
33     ui->setupUi(this);
34
35 #ifdef Q_WS_MAC // Icons on push buttons are very uncommon on Mac
36     ui->addPubKeyButton->setIcon(QIcon());
37     ui->clearButton->setIcon(QIcon());
38     ui->addInputButton->setIcon(QIcon());
39     ui->addOutputButton->setIcon(QIcon());
40     ui->signTransactionButton->setIcon(QIcon());
41     ui->sendTransactionButton->setIcon(QIcon());
42 #endif
43
44     addPubKey();
45     addPubKey();
46
47     connect(ui->addPubKeyButton, SIGNAL(clicked()), this, SLOT(addPubKey()));
48     connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
49
50     addInput();
51     addOutput();
52
53     connect(ui->addInputButton, SIGNAL(clicked()), this, SLOT(addInput()));
54     connect(ui->addOutputButton, SIGNAL(clicked()), this, SLOT(addOutput()));
55
56     ui->signTransactionButton->setEnabled(false);
57     ui->sendTransactionButton->setEnabled(false);
58 }
59
60 void MultisigDialog::showEvent(QShowEvent *event)
61 {
62     QWidget::showEvent(event);
63
64     if (!model)
65         return;
66
67     updateAmounts();
68 }
69
70 void MultisigDialog::hideEvent(QHideEvent *event)
71 {
72     QWidget::hideEvent(event);
73
74     if (!model)
75         return;
76
77     clear();
78 }
79
80 MultisigDialog::~MultisigDialog()
81 {
82     delete ui;
83 }
84
85 void MultisigDialog::setModel(WalletModel *model)
86 {
87     this->model = model;
88
89     for(int i = 0; i < ui->pubkeyEntries->count(); i++)
90     {
91         MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget());
92         if(entry)
93             entry->setModel(model);
94     }
95
96
97     for(int i = 0; i < ui->inputs->count(); i++)
98     {
99         MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
100         if(entry)
101             entry->setModel(model);
102     }
103
104
105     for(int i = 0; i < ui->outputs->count(); i++)
106     {
107         SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
108         if(entry)
109             entry->setModel(model);
110     }
111 }
112
113 void MultisigDialog::updateRemoveEnabled()
114 {
115     bool enabled = (ui->pubkeyEntries->count() > 2);
116
117     for(int i = 0; i < ui->pubkeyEntries->count(); i++)
118     {
119         MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget());
120         if(entry)
121             entry->setRemoveEnabled(enabled);
122     }
123
124     QString maxSigsStr;
125     maxSigsStr.setNum(ui->pubkeyEntries->count());
126     ui->maxSignaturesLabel->setText(QString("/ ") + maxSigsStr);
127
128
129     enabled = (ui->inputs->count() > 1);
130     for(int i = 0; i < ui->inputs->count(); i++)
131     {
132         MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
133         if(entry)
134             entry->setRemoveEnabled(enabled);
135     }
136
137
138     enabled = (ui->outputs->count() > 1);
139     for(int i = 0; i < ui->outputs->count(); i++)
140     {
141         SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
142         if(entry)
143             entry->setRemoveEnabled(enabled);
144     }
145 }
146
147 void MultisigDialog::on_createAddressButton_clicked()
148 {
149     ui->multisigAddress->clear();
150     ui->redeemScript->clear();
151
152     if(!model)
153         return;
154
155     std::vector<CKey> pubkeys;
156     pubkeys.resize(ui->pubkeyEntries->count());
157     unsigned int required = ui->requiredSignatures->text().toUInt();
158
159     for(int i = 0; i < ui->pubkeyEntries->count(); i++)
160     {
161         MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget());
162         if(!entry->validate())
163             return;
164         QString str = entry->getPubkey();
165         CPubKey vchPubKey(ParseHex(str.toStdString().c_str()));
166         if(!vchPubKey.IsValid())
167             return;
168         pubkeys[i].SetPubKey(vchPubKey);
169     }
170
171     if(pubkeys.size() > 16)
172     {
173         QMessageBox::warning(this, tr("Error"), tr("Number of addresses involved in the address creation > %1\nReduce the number").arg(16), QMessageBox::Ok);
174         return;
175     }
176
177     if(required == 0)
178     {
179         QMessageBox::warning(this, tr("Error"), tr("Number of required signatures is 0\nNumber of required signatures must be between 1 and number of keys involved in the creation of address."), QMessageBox::Ok);
180         return;
181     }
182
183     if(required > pubkeys.size())
184     {
185         QMessageBox::warning(this, tr("Error"), tr("Number of required signatures > Number of keys involved in the creation of address."), QMessageBox::Ok);
186         return;
187     }
188     
189     CScript script;
190     script.SetMultisig(required, pubkeys);
191     if (script.size() > MAX_SCRIPT_ELEMENT_SIZE)
192     {
193         QMessageBox::warning(this, tr("Error"), tr("Redeem script exceeds size limit: %1 > %2\nReduce the number of addresses involved in the address creation.").arg(script.size()).arg(MAX_SCRIPT_ELEMENT_SIZE), QMessageBox::Ok);
194         return;
195     }
196     CScriptID scriptID = script.GetID();
197     CBitcoinAddress address(scriptID);
198
199     ui->multisigAddress->setText(address.ToString().c_str());
200     ui->redeemScript->setText(HexStr(script.begin(), script.end()).c_str());
201 }
202
203 void MultisigDialog::on_copyMultisigAddressButton_clicked()
204 {
205     QApplication::clipboard()->setText(ui->multisigAddress->text());
206 }
207
208 void MultisigDialog::on_copyRedeemScriptButton_clicked()
209 {
210     QApplication::clipboard()->setText(ui->redeemScript->text());
211 }
212
213 void MultisigDialog::on_saveRedeemScriptButton_clicked()
214 {
215     if(!model)
216         return;
217
218     CWallet *wallet = model->getWallet();
219     std::string redeemScript = ui->redeemScript->text().toStdString();
220     std::vector<unsigned char> scriptData(ParseHex(redeemScript));
221     CScript script(scriptData.begin(), scriptData.end());
222     CScriptID scriptID = script.GetID();
223
224     LOCK(wallet->cs_wallet);
225     if(!wallet->HaveCScript(scriptID))
226         wallet->AddCScript(script);
227 }
228
229 void MultisigDialog::on_saveMultisigAddressButton_clicked()
230 {
231     if(!model)
232         return;
233
234     CWallet *wallet = model->getWallet();
235     std::string redeemScript = ui->redeemScript->text().toStdString();
236     std::string address = ui->multisigAddress->text().toStdString();
237     std::string label("multisig");
238
239     if(!model->validateAddress(QString(address.c_str())))
240         return;
241
242     std::vector<unsigned char> scriptData(ParseHex(redeemScript));
243     CScript script(scriptData.begin(), scriptData.end());
244     CScriptID scriptID = script.GetID();
245
246     LOCK(wallet->cs_wallet);
247     if(!wallet->HaveCScript(scriptID))
248         wallet->AddCScript(script);
249     if(!wallet->mapAddressBook.count(CBitcoinAddress(address).Get()))
250         wallet->SetAddressBookName(CBitcoinAddress(address).Get(), label);
251 }
252
253 void MultisigDialog::clear()
254 {
255     while(ui->pubkeyEntries->count())
256         delete ui->pubkeyEntries->takeAt(0)->widget();
257
258     addPubKey();
259     addPubKey();
260     updateRemoveEnabled();
261 }
262
263 MultisigAddressEntry * MultisigDialog::addPubKey()
264 {
265     MultisigAddressEntry *entry = new MultisigAddressEntry(this);
266
267     entry->setModel(model);
268     ui->pubkeyEntries->addWidget(entry);
269     connect(entry, SIGNAL(removeEntry(MultisigAddressEntry *)), this, SLOT(removeEntry(MultisigAddressEntry *)));
270     updateRemoveEnabled();
271     entry->clear();
272     ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
273     QScrollBar *bar = ui->scrollArea->verticalScrollBar();
274     if(bar)
275         bar->setSliderPosition(bar->maximum());
276
277     return entry;
278 }
279
280 void MultisigDialog::removeEntry(MultisigAddressEntry *entry)
281 {
282     delete entry;
283     updateRemoveEnabled();
284 }
285
286 void MultisigDialog::on_createTransactionButton_clicked()
287 {
288     CTransaction transaction;
289
290     // Get inputs
291     for(int i = 0; i < ui->inputs->count(); i++)
292     {
293         MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
294         if(entry)
295         {
296             if(entry->validate())
297             {
298                 CTxIn input = entry->getInput();
299                 transaction.vin.push_back(input);
300             }
301             else
302                 return;
303         }
304     }
305
306     // Get outputs
307     for(int i = 0; i < ui->outputs->count(); i++)
308     {
309         SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
310
311         if(entry)
312         {
313             if(entry->validate())
314             {
315                 SendCoinsRecipient recipient = entry->getValue();
316                 CBitcoinAddress address(recipient.address.toStdString());
317                 CScript scriptPubKey;
318                 scriptPubKey.SetDestination(address.Get());
319                 int64_t amount = recipient.amount;
320                 CTxOut output(amount, scriptPubKey);
321                 transaction.vout.push_back(output);
322             }
323             else
324                 return;
325         }
326     }
327
328     CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
329     ss << transaction;
330     ui->transaction->setText(HexStr(ss.begin(), ss.end()).c_str());
331 }
332
333 void MultisigDialog::on_transaction_textChanged()
334 {
335     while(ui->inputs->count())
336         delete ui->inputs->takeAt(0)->widget();
337     while(ui->outputs->count())
338         delete ui->outputs->takeAt(0)->widget();
339
340     if(ui->transaction->text().size() > 0)
341         ui->signTransactionButton->setEnabled(true);
342     else
343         ui->signTransactionButton->setEnabled(false);
344
345     // Decode the raw transaction
346     std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString()));
347     CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION);
348     CTransaction tx;
349     try
350     {
351         ss >> tx;
352     }
353     catch(std::exception &e)
354     {
355         return;
356     }
357
358     // Fill input list
359     int index = -1;
360     BOOST_FOREACH(const CTxIn& txin, tx.vin)
361     {
362         uint256 prevoutHash = txin.prevout.hash;
363         addInput();
364         index++;
365         MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(index)->widget());
366         if(entry)
367         {
368             entry->setTransactionId(QString(prevoutHash.GetHex().c_str()));
369             entry->setTransactionOutputIndex(txin.prevout.n);
370         }
371     }
372
373     // Fill output list
374     index = -1;
375     BOOST_FOREACH(const CTxOut& txout, tx.vout)
376     {
377         CScript scriptPubKey = txout.scriptPubKey;
378         CTxDestination addr;
379         ExtractDestination(scriptPubKey, addr);
380         CBitcoinAddress address(addr);
381         SendCoinsRecipient recipient;
382         recipient.address = QString(address.ToString().c_str());
383         recipient.amount = txout.nValue;
384         addOutput();
385         index++;
386         SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(index)->widget());
387         if(entry)
388         {
389             entry->setValue(recipient);
390         }
391     }
392
393     updateRemoveEnabled();
394 }
395
396 void MultisigDialog::on_copyTransactionButton_clicked()
397 {
398     QApplication::clipboard()->setText(ui->transaction->text());
399 }
400
401 void MultisigDialog::on_pasteTransactionButton_clicked()
402 {
403     ui->transaction->setText(QApplication::clipboard()->text());
404 }
405
406 void MultisigDialog::on_signTransactionButton_clicked()
407 {
408     ui->signedTransaction->clear();
409
410     if(!model)
411         return;
412
413     CWallet *wallet = model->getWallet();
414
415     // Decode the raw transaction
416     std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString()));
417     CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION);
418     CTransaction tx;
419     try
420     {
421         ss >> tx;
422     }
423     catch(std::exception &e)
424     {
425         return;
426     }
427     CTransaction mergedTx(tx);
428
429     // Fetch previous transactions (inputs)
430     std::map<COutPoint, CScript> mapPrevOut;
431     for(unsigned int i = 0; i < mergedTx.vin.size(); i++)
432     {
433         CTransaction tempTx;
434         MapPrevTx mapPrevTx;
435         CTxDB txdb("r");
436         std::map<uint256, CTxIndex> unused;
437         bool fInvalid;
438
439         tempTx.vin.push_back(mergedTx.vin[i]);
440         tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid);
441
442         BOOST_FOREACH(const CTxIn& txin, tempTx.vin)
443         {
444             const uint256& prevHash = txin.prevout.hash;
445             if(mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size() > txin.prevout.n)
446                 mapPrevOut[txin.prevout] = mapPrevTx[prevHash].second.vout[txin.prevout.n].scriptPubKey;
447         }
448     }
449
450     // Add the redeem scripts to the wallet keystore
451     for(int i = 0; i < ui->inputs->count(); i++)
452     {
453         MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
454         if(entry)
455         {
456             QString redeemScriptStr = entry->getRedeemScript();
457             if(redeemScriptStr.size() > 0)
458             {
459                 std::vector<unsigned char> scriptData(ParseHex(redeemScriptStr.toStdString()));
460                 CScript redeemScript(scriptData.begin(), scriptData.end());
461                 wallet->AddCScript(redeemScript);
462             }
463         }
464     }
465
466     WalletModel::UnlockContext ctx(model->requestUnlock());
467     if(!ctx.isValid())
468         return;
469
470     // Sign what we can
471     bool fComplete = true;
472     for(unsigned int i = 0; i < mergedTx.vin.size(); i++)
473     {
474         CTxIn& txin = mergedTx.vin[i];
475         if(mapPrevOut.count(txin.prevout) == 0)
476         {
477             fComplete = false;
478             continue;
479         }
480         const CScript& prevPubKey = mapPrevOut[txin.prevout];
481
482         txin.scriptSig.clear();
483         SignSignature(*wallet, prevPubKey, mergedTx, i, SIGHASH_ALL);
484         txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, tx.vin[i].scriptSig);
485         if(!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, 0))
486         {
487             fComplete = false;
488         }
489     }
490
491     CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
492     ssTx << mergedTx;
493     ui->signedTransaction->setText(HexStr(ssTx.begin(), ssTx.end()).c_str());
494
495     if(fComplete)
496     {
497         ui->statusLabel->setText(tr("Transaction signature is complete"));
498         ui->sendTransactionButton->setEnabled(true);
499     }
500     else
501     {
502         ui->statusLabel->setText(tr("Transaction is NOT completely signed"));
503         ui->sendTransactionButton->setEnabled(false);
504     }
505 }
506
507 void MultisigDialog::on_copySignedTransactionButton_clicked()
508 {
509     QApplication::clipboard()->setText(ui->signedTransaction->text());
510 }
511
512 void MultisigDialog::on_sendTransactionButton_clicked()
513 {
514     int64_t transactionSize = ui->signedTransaction->text().size() / 2;
515     if(transactionSize == 0)
516         return;
517
518     // Check the fee
519     int64_t fee = (int64_t ) (ui->fee->text().toDouble() * COIN);
520     int64_t minFee = MIN_TX_FEE * (1 + (int64_t) transactionSize / 1000);
521     if(fee < minFee)
522     {
523         QMessageBox::StandardButton ret = QMessageBox::question(this, tr("Confirm send transaction"), tr("The fee of the transaction (%1 NVC) is smaller than the expected fee (%2 NVC). Do you want to send the transaction anyway?").arg((double) fee / COIN).arg((double) minFee / COIN), QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
524         if(ret != QMessageBox::Yes)
525             return;
526     }
527     else if(fee > minFee)
528     {
529         QMessageBox::StandardButton ret = QMessageBox::question(this, tr("Confirm send transaction"), tr("The fee of the transaction (%1 NVC) is bigger than the expected fee (%2 NVC). Do you want to send the transaction anyway?").arg((double) fee / COIN).arg((double) minFee / COIN), QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
530         if(ret != QMessageBox::Yes)
531             return;
532     }
533
534     // Decode the raw transaction
535     std::vector<unsigned char> txData(ParseHex(ui->signedTransaction->text().toStdString()));
536     CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
537     CTransaction tx;
538     try
539     {
540         ssData >> tx;
541     }
542     catch(std::exception &e)
543     {
544         return;
545     }
546     uint256 txHash = tx.GetHash();
547
548     // Check if the transaction is already in the blockchain
549     CTransaction existingTx;
550     uint256 blockHash = 0;
551     if(GetTransaction(txHash, existingTx, blockHash))
552     {
553         if(blockHash != 0)
554             return;
555     }
556
557     // Send the transaction to the local node
558     CTxDB txdb("r");
559     if(!tx.AcceptToMemoryPool(txdb, false))
560     return;
561     SyncWithWallets(tx, NULL, true);
562     //(CInv(MSG_TX, txHash), tx);
563     RelayTransaction(tx, txHash);
564 }
565
566 MultisigInputEntry * MultisigDialog::addInput()
567 {
568     MultisigInputEntry *entry = new MultisigInputEntry(this);
569
570     entry->setModel(model);
571     ui->inputs->addWidget(entry);
572     connect(entry, SIGNAL(removeEntry(MultisigInputEntry *)), this, SLOT(removeEntry(MultisigInputEntry *)));
573     connect(entry, SIGNAL(updateAmount()), this, SLOT(updateAmounts()));
574     updateRemoveEnabled();
575     entry->clear();
576     ui->scrollAreaWidgetContents_2->resize(ui->scrollAreaWidgetContents_2->sizeHint());
577     QScrollBar *bar = ui->scrollArea_2->verticalScrollBar();
578     if(bar)
579         bar->setSliderPosition(bar->maximum());
580
581     return entry;
582 }
583
584 void MultisigDialog::removeEntry(MultisigInputEntry *entry)
585 {
586     delete entry;
587     updateRemoveEnabled();
588 }
589
590 SendCoinsEntry * MultisigDialog::addOutput()
591 {
592     SendCoinsEntry *entry = new SendCoinsEntry(this);
593
594     entry->setModel(model);
595     ui->outputs->addWidget(entry);
596     connect(entry, SIGNAL(removeEntry(SendCoinsEntry *)), this, SLOT(removeEntry(SendCoinsEntry *)));
597     connect(entry, SIGNAL(payAmountChanged()), this, SLOT(updateAmounts()));
598     updateRemoveEnabled();
599     entry->clear();
600     ui->scrollAreaWidgetContents_3->resize(ui->scrollAreaWidgetContents_3->sizeHint());
601     QScrollBar *bar = ui->scrollArea_3->verticalScrollBar();
602     if(bar)
603         bar->setSliderPosition(bar->maximum());
604
605     return entry;
606 }
607
608 void MultisigDialog::removeEntry(SendCoinsEntry *entry)
609 {
610     delete entry;
611     updateRemoveEnabled();
612 }
613
614 void MultisigDialog::updateAmounts()
615 {
616     // Update inputs amount
617     int64_t inputsAmount = 0;
618     for(int i = 0; i < ui->inputs->count(); i++)
619     {
620         MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
621         if(entry)
622             inputsAmount += entry->getAmount();
623     }
624     QString inputsAmountStr;
625     inputsAmountStr.sprintf("%.6f", (double) inputsAmount / COIN);
626     ui->inputsAmount->setText(inputsAmountStr);
627
628     // Update outputs amount
629     int64_t outputsAmount = 0;
630     for(int i = 0; i < ui->outputs->count(); i++)
631     {
632         SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
633         if(entry)
634             outputsAmount += entry->getValue().amount;
635     }
636     QString outputsAmountStr;
637     outputsAmountStr.sprintf("%.6f", (double) outputsAmount / COIN);
638     ui->outputsAmount->setText(outputsAmountStr);
639
640     // Update Fee amount
641     int64_t fee = inputsAmount - outputsAmount;
642     QString feeStr;
643     feeStr.sprintf("%.6f", (double) fee / COIN);
644     ui->fee->setText(feeStr);
645 }