8 #include "addresstablemodel.h"
12 #include "multisigaddressentry.h"
13 #include "multisiginputentry.h"
14 #include "multisigdialog.h"
15 #include "ui_multisigdialog.h"
17 #include "sendcoinsentry.h"
20 #include "walletmodel.h"
23 #include "txdb-leveldb.h"
29 #pragma warning( disable : 4101)
32 MultisigDialog::MultisigDialog(QWidget *parent) : QWidget(parent), ui(new Ui::MultisigDialog), model(0)
36 #ifdef Q_WS_MAC // Icons on push buttons are very uncommon on Mac
37 ui->addPubKeyButton->setIcon(QIcon());
38 ui->clearButton->setIcon(QIcon());
39 ui->addInputButton->setIcon(QIcon());
40 ui->addOutputButton->setIcon(QIcon());
41 ui->signTransactionButton->setIcon(QIcon());
42 ui->sendTransactionButton->setIcon(QIcon());
48 connect(ui->addPubKeyButton, SIGNAL(clicked()), this, SLOT(addPubKey()));
49 connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
54 connect(ui->addInputButton, SIGNAL(clicked()), this, SLOT(addInput()));
55 connect(ui->addOutputButton, SIGNAL(clicked()), this, SLOT(addOutput()));
57 ui->signTransactionButton->setEnabled(false);
58 ui->sendTransactionButton->setEnabled(false);
61 void MultisigDialog::showEvent(QShowEvent *event)
63 QWidget::showEvent(event);
71 void MultisigDialog::hideEvent(QHideEvent *event)
73 QWidget::hideEvent(event);
81 MultisigDialog::~MultisigDialog()
86 void MultisigDialog::setModel(WalletModel *model)
90 for(int i = 0; i < ui->pubkeyEntries->count(); i++)
92 MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget());
94 entry->setModel(model);
98 for(int i = 0; i < ui->inputs->count(); i++)
100 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
102 entry->setModel(model);
106 for(int i = 0; i < ui->outputs->count(); i++)
108 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
110 entry->setModel(model);
114 void MultisigDialog::updateRemoveEnabled()
116 bool enabled = (ui->pubkeyEntries->count() > 2);
118 for(int i = 0; i < ui->pubkeyEntries->count(); i++)
120 MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget());
122 entry->setRemoveEnabled(enabled);
126 maxSigsStr.setNum(ui->pubkeyEntries->count());
127 ui->maxSignaturesLabel->setText(QString("/ ") + maxSigsStr);
130 enabled = (ui->inputs->count() > 1);
131 for(int i = 0; i < ui->inputs->count(); i++)
133 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
135 entry->setRemoveEnabled(enabled);
139 enabled = (ui->outputs->count() > 1);
140 for(int i = 0; i < ui->outputs->count(); i++)
142 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
144 entry->setRemoveEnabled(enabled);
148 void MultisigDialog::on_createAddressButton_clicked()
150 ui->multisigAddress->clear();
151 ui->redeemScript->clear();
156 std::vector<CKey> pubkeys;
157 pubkeys.resize(ui->pubkeyEntries->count());
158 unsigned int required = ui->requiredSignatures->text().toUInt();
160 for(int i = 0; i < ui->pubkeyEntries->count(); i++)
162 MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget());
163 if(!entry->validate())
165 QString str = entry->getPubkey();
166 CPubKey vchPubKey(ParseHex(str.toStdString().c_str()));
167 if(!vchPubKey.IsValid())
169 pubkeys[i].SetPubKey(vchPubKey);
172 if(pubkeys.size() > 16)
174 QMessageBox::warning(this, tr("Error"), tr("Number of addresses involved in the address creation > %1\nReduce the number").arg(16), QMessageBox::Ok);
180 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);
184 if(required > pubkeys.size())
186 QMessageBox::warning(this, tr("Error"), tr("Number of required signatures > Number of keys involved in the creation of address."), QMessageBox::Ok);
191 script.SetMultisig(required, pubkeys);
192 if (script.size() > MAX_SCRIPT_ELEMENT_SIZE)
194 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);
197 CScriptID scriptID = script.GetID();
198 CBitcoinAddress address(scriptID);
200 ui->multisigAddress->setText(address.ToString().c_str());
201 ui->redeemScript->setText(HexStr(script.begin(), script.end()).c_str());
204 void MultisigDialog::on_copyMultisigAddressButton_clicked()
206 QApplication::clipboard()->setText(ui->multisigAddress->text());
209 void MultisigDialog::on_copyRedeemScriptButton_clicked()
211 QApplication::clipboard()->setText(ui->redeemScript->text());
214 void MultisigDialog::on_saveRedeemScriptButton_clicked()
219 CWallet *wallet = model->getWallet();
220 std::string redeemScript = ui->redeemScript->text().toStdString();
221 std::vector<unsigned char> scriptData(ParseHex(redeemScript));
222 CScript script(scriptData.begin(), scriptData.end());
223 CScriptID scriptID = script.GetID();
225 LOCK(wallet->cs_wallet);
226 if(!wallet->HaveCScript(scriptID))
227 wallet->AddCScript(script);
230 void MultisigDialog::on_saveMultisigAddressButton_clicked()
235 CWallet *wallet = model->getWallet();
236 std::string redeemScript = ui->redeemScript->text().toStdString();
237 std::string address = ui->multisigAddress->text().toStdString();
238 std::string label("multisig");
240 if(!model->validateAddress(QString(address.c_str())))
243 std::vector<unsigned char> scriptData(ParseHex(redeemScript));
244 CScript script(scriptData.begin(), scriptData.end());
245 CScriptID scriptID = script.GetID();
247 LOCK(wallet->cs_wallet);
248 if(!wallet->HaveCScript(scriptID))
249 wallet->AddCScript(script);
250 if(!wallet->mapAddressBook.count(CBitcoinAddress(address).Get()))
251 wallet->SetAddressBookName(CBitcoinAddress(address).Get(), label);
254 void MultisigDialog::clear()
256 while(ui->pubkeyEntries->count())
257 delete ui->pubkeyEntries->takeAt(0)->widget();
261 updateRemoveEnabled();
264 MultisigAddressEntry * MultisigDialog::addPubKey()
266 MultisigAddressEntry *entry = new MultisigAddressEntry(this);
268 entry->setModel(model);
269 ui->pubkeyEntries->addWidget(entry);
270 connect(entry, SIGNAL(removeEntry(MultisigAddressEntry *)), this, SLOT(removeEntry(MultisigAddressEntry *)));
271 updateRemoveEnabled();
273 ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
274 QScrollBar *bar = ui->scrollArea->verticalScrollBar();
276 bar->setSliderPosition(bar->maximum());
281 void MultisigDialog::removeEntry(MultisigAddressEntry *entry)
284 updateRemoveEnabled();
287 void MultisigDialog::on_createTransactionButton_clicked()
289 CTransaction transaction;
292 for(int i = 0; i < ui->inputs->count(); i++)
294 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
297 if(entry->validate())
299 CTxIn input = entry->getInput();
300 transaction.vin.push_back(input);
308 for(int i = 0; i < ui->outputs->count(); i++)
310 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
314 if(entry->validate())
316 SendCoinsRecipient recipient = entry->getValue();
317 CBitcoinAddress address(recipient.address.toStdString());
318 CScript scriptPubKey;
319 scriptPubKey.SetDestination(address.Get());
320 int64_t amount = recipient.amount;
321 CTxOut output(amount, scriptPubKey);
322 transaction.vout.push_back(output);
329 CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
331 ui->transaction->setText(HexStr(ss.begin(), ss.end()).c_str());
334 void MultisigDialog::on_transaction_textChanged()
336 while(ui->inputs->count())
337 delete ui->inputs->takeAt(0)->widget();
338 while(ui->outputs->count())
339 delete ui->outputs->takeAt(0)->widget();
341 if(ui->transaction->text().size() > 0)
342 ui->signTransactionButton->setEnabled(true);
344 ui->signTransactionButton->setEnabled(false);
346 // Decode the raw transaction
347 std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString()));
348 CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION);
354 catch(std::exception &e)
361 BOOST_FOREACH(const CTxIn& txin, tx.vin)
363 uint256 prevoutHash = txin.prevout.hash;
366 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(index)->widget());
369 entry->setTransactionId(QString(prevoutHash.GetHex().c_str()));
370 entry->setTransactionOutputIndex(txin.prevout.n);
376 BOOST_FOREACH(const CTxOut& txout, tx.vout)
378 CScript scriptPubKey = txout.scriptPubKey;
380 ExtractDestination(scriptPubKey, addr);
381 CBitcoinAddress address(addr);
382 SendCoinsRecipient recipient;
383 recipient.address = QString(address.ToString().c_str());
384 recipient.amount = txout.nValue;
387 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(index)->widget());
390 entry->setValue(recipient);
394 updateRemoveEnabled();
397 void MultisigDialog::on_copyTransactionButton_clicked()
399 QApplication::clipboard()->setText(ui->transaction->text());
402 void MultisigDialog::on_pasteTransactionButton_clicked()
404 ui->transaction->setText(QApplication::clipboard()->text());
407 void MultisigDialog::on_signTransactionButton_clicked()
409 ui->signedTransaction->clear();
414 CWallet *wallet = model->getWallet();
416 // Decode the raw transaction
417 std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString()));
418 CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION);
424 catch(std::exception &e)
428 CTransaction mergedTx(tx);
430 // Fetch previous transactions (inputs)
431 std::map<COutPoint, CScript> mapPrevOut;
432 for(unsigned int i = 0; i < mergedTx.vin.size(); i++)
437 std::map<uint256, CTxIndex> unused;
440 tempTx.vin.push_back(mergedTx.vin[i]);
441 tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid);
443 BOOST_FOREACH(const CTxIn& txin, tempTx.vin)
445 const uint256& prevHash = txin.prevout.hash;
446 if(mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size() > txin.prevout.n)
447 mapPrevOut[txin.prevout] = mapPrevTx[prevHash].second.vout[txin.prevout.n].scriptPubKey;
451 // Add the redeem scripts to the wallet keystore
452 for(int i = 0; i < ui->inputs->count(); i++)
454 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
457 QString redeemScriptStr = entry->getRedeemScript();
458 if(redeemScriptStr.size() > 0)
460 std::vector<unsigned char> scriptData(ParseHex(redeemScriptStr.toStdString()));
461 CScript redeemScript(scriptData.begin(), scriptData.end());
462 wallet->AddCScript(redeemScript);
467 WalletModel::UnlockContext ctx(model->requestUnlock());
472 bool fComplete = true;
473 for(unsigned int i = 0; i < mergedTx.vin.size(); i++)
475 CTxIn& txin = mergedTx.vin[i];
476 if(mapPrevOut.count(txin.prevout) == 0)
481 const CScript& prevPubKey = mapPrevOut[txin.prevout];
483 txin.scriptSig.clear();
484 SignSignature(*wallet, prevPubKey, mergedTx, i, SIGHASH_ALL);
485 txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, tx.vin[i].scriptSig);
486 if(!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, 0))
492 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
494 ui->signedTransaction->setText(HexStr(ssTx.begin(), ssTx.end()).c_str());
498 ui->statusLabel->setText(tr("Transaction signature is complete"));
499 ui->sendTransactionButton->setEnabled(true);
503 ui->statusLabel->setText(tr("Transaction is NOT completely signed"));
504 ui->sendTransactionButton->setEnabled(false);
508 void MultisigDialog::on_copySignedTransactionButton_clicked()
510 QApplication::clipboard()->setText(ui->signedTransaction->text());
513 void MultisigDialog::on_sendTransactionButton_clicked()
515 int64_t transactionSize = ui->signedTransaction->text().size() / 2;
516 if(transactionSize == 0)
520 int64_t fee = (int64_t ) (ui->fee->text().toDouble() * COIN);
521 int64_t minFee = MIN_TX_FEE * (1 + (int64_t) transactionSize / 1000);
524 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);
525 if(ret != QMessageBox::Yes)
528 else if(fee > minFee)
530 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);
531 if(ret != QMessageBox::Yes)
535 // Decode the raw transaction
536 std::vector<unsigned char> txData(ParseHex(ui->signedTransaction->text().toStdString()));
537 CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
543 catch(std::exception &e)
547 uint256 txHash = tx.GetHash();
549 // Check if the transaction is already in the blockchain
550 CTransaction existingTx;
551 uint256 blockHash = 0;
552 if(GetTransaction(txHash, existingTx, blockHash))
558 // Send the transaction to the local node
560 if(!tx.AcceptToMemoryPool(txdb, false))
562 SyncWithWallets(tx, NULL, true);
563 //(CInv(MSG_TX, txHash), tx);
564 RelayTransaction(tx, txHash);
567 MultisigInputEntry * MultisigDialog::addInput()
569 MultisigInputEntry *entry = new MultisigInputEntry(this);
571 entry->setModel(model);
572 ui->inputs->addWidget(entry);
573 connect(entry, SIGNAL(removeEntry(MultisigInputEntry *)), this, SLOT(removeEntry(MultisigInputEntry *)));
574 connect(entry, SIGNAL(updateAmount()), this, SLOT(updateAmounts()));
575 updateRemoveEnabled();
577 ui->scrollAreaWidgetContents_2->resize(ui->scrollAreaWidgetContents_2->sizeHint());
578 QScrollBar *bar = ui->scrollArea_2->verticalScrollBar();
580 bar->setSliderPosition(bar->maximum());
585 void MultisigDialog::removeEntry(MultisigInputEntry *entry)
588 updateRemoveEnabled();
591 SendCoinsEntry * MultisigDialog::addOutput()
593 SendCoinsEntry *entry = new SendCoinsEntry(this);
595 entry->setModel(model);
596 ui->outputs->addWidget(entry);
597 connect(entry, SIGNAL(removeEntry(SendCoinsEntry *)), this, SLOT(removeEntry(SendCoinsEntry *)));
598 connect(entry, SIGNAL(payAmountChanged()), this, SLOT(updateAmounts()));
599 updateRemoveEnabled();
601 ui->scrollAreaWidgetContents_3->resize(ui->scrollAreaWidgetContents_3->sizeHint());
602 QScrollBar *bar = ui->scrollArea_3->verticalScrollBar();
604 bar->setSliderPosition(bar->maximum());
609 void MultisigDialog::removeEntry(SendCoinsEntry *entry)
612 updateRemoveEnabled();
615 void MultisigDialog::updateAmounts()
617 // Update inputs amount
618 int64_t inputsAmount = 0;
619 for(int i = 0; i < ui->inputs->count(); i++)
621 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
623 inputsAmount += entry->getAmount();
625 QString inputsAmountStr;
626 inputsAmountStr.sprintf("%.6f", (double) inputsAmount / COIN);
627 ui->inputsAmount->setText(inputsAmountStr);
629 // Update outputs amount
630 int64_t outputsAmount = 0;
631 for(int i = 0; i < ui->outputs->count(); i++)
633 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
635 outputsAmount += entry->getValue().amount;
637 QString outputsAmountStr;
638 outputsAmountStr.sprintf("%.6f", (double) outputsAmount / COIN);
639 ui->outputsAmount->setText(outputsAmountStr);
642 int64_t fee = inputsAmount - outputsAmount;
644 feeStr.sprintf("%.6f", (double) fee / COIN);
645 ui->fee->setText(feeStr);
648 void MultisigDialog::keyPressEvent(QKeyEvent *event)
651 if(windowType() != Qt::Widget && event->key() == Qt::Key_Back)
656 if(windowType() != Qt::Widget && event->key() == Qt::Key_Escape)