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"
28 MultisigDialog::MultisigDialog(QWidget *parent) : QWidget(parent), ui(new Ui::MultisigDialog), model(0)
32 #ifdef Q_WS_MAC // Icons on push buttons are very uncommon on Mac
33 ui->addPubKeyButton->setIcon(QIcon());
34 ui->clearButton->setIcon(QIcon());
35 ui->addInputButton->setIcon(QIcon());
36 ui->addOutputButton->setIcon(QIcon());
37 ui->signTransactionButton->setIcon(QIcon());
38 ui->sendTransactionButton->setIcon(QIcon());
44 connect(ui->addPubKeyButton, SIGNAL(clicked()), this, SLOT(addPubKey()));
45 connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
50 connect(ui->addInputButton, SIGNAL(clicked()), this, SLOT(addInput()));
51 connect(ui->addOutputButton, SIGNAL(clicked()), this, SLOT(addOutput()));
53 ui->signTransactionButton->setEnabled(false);
54 ui->sendTransactionButton->setEnabled(false);
57 void MultisigDialog::showEvent(QShowEvent *event)
59 QWidget::showEvent(event);
67 void MultisigDialog::hideEvent(QHideEvent *event)
69 QWidget::hideEvent(event);
77 MultisigDialog::~MultisigDialog()
82 void MultisigDialog::setModel(WalletModel *model)
86 for(int i = 0; i < ui->pubkeyEntries->count(); i++)
88 MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget());
90 entry->setModel(model);
94 for(int i = 0; i < ui->inputs->count(); i++)
96 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
98 entry->setModel(model);
102 for(int i = 0; i < ui->outputs->count(); i++)
104 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
106 entry->setModel(model);
110 void MultisigDialog::updateRemoveEnabled()
112 bool enabled = (ui->pubkeyEntries->count() > 2);
114 for(int i = 0; i < ui->pubkeyEntries->count(); i++)
116 MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget());
118 entry->setRemoveEnabled(enabled);
122 maxSigsStr.setNum(ui->pubkeyEntries->count());
123 ui->maxSignaturesLabel->setText(QString("/ ") + maxSigsStr);
126 enabled = (ui->inputs->count() > 1);
127 for(int i = 0; i < ui->inputs->count(); i++)
129 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
131 entry->setRemoveEnabled(enabled);
135 enabled = (ui->outputs->count() > 1);
136 for(int i = 0; i < ui->outputs->count(); i++)
138 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
140 entry->setRemoveEnabled(enabled);
144 void MultisigDialog::on_createAddressButton_clicked()
146 ui->multisigAddress->clear();
147 ui->redeemScript->clear();
152 std::vector<CKey> pubkeys;
153 pubkeys.resize(ui->pubkeyEntries->count());
154 unsigned int required = ui->requiredSignatures->text().toUInt();
156 for(int i = 0; i < ui->pubkeyEntries->count(); i++)
158 MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget());
159 if(!entry->validate())
161 QString str = entry->getPubkey();
162 CPubKey vchPubKey(ParseHex(str.toStdString().c_str()));
163 if(!vchPubKey.IsValid())
165 pubkeys[i].SetPubKey(vchPubKey);
168 if(pubkeys.size() > 16)
170 QMessageBox::warning(this, tr("Error"), tr("Number of addresses involved in the address creation > %1\nReduce the number").arg(16), QMessageBox::Ok);
176 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 if(required > pubkeys.size())
182 QMessageBox::warning(this, tr("Error"), tr("Number of required signatures > Number of keys involved in the creation of address."), QMessageBox::Ok);
187 script.SetMultisig(required, pubkeys);
188 if (script.size() > MAX_SCRIPT_ELEMENT_SIZE)
190 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);
193 CScriptID scriptID = script.GetID();
194 CBitcoinAddress address(scriptID);
196 ui->multisigAddress->setText(address.ToString().c_str());
197 ui->redeemScript->setText(HexStr(script.begin(), script.end()).c_str());
200 void MultisigDialog::on_copyMultisigAddressButton_clicked()
202 QApplication::clipboard()->setText(ui->multisigAddress->text());
205 void MultisigDialog::on_copyRedeemScriptButton_clicked()
207 QApplication::clipboard()->setText(ui->redeemScript->text());
210 void MultisigDialog::on_saveRedeemScriptButton_clicked()
215 CWallet *wallet = model->getWallet();
216 std::string redeemScript = ui->redeemScript->text().toStdString();
217 std::vector<unsigned char> scriptData(ParseHex(redeemScript));
218 CScript script(scriptData.begin(), scriptData.end());
219 CScriptID scriptID = script.GetID();
221 LOCK(wallet->cs_wallet);
222 if(!wallet->HaveCScript(scriptID))
223 wallet->AddCScript(script);
226 void MultisigDialog::on_saveMultisigAddressButton_clicked()
231 CWallet *wallet = model->getWallet();
232 std::string redeemScript = ui->redeemScript->text().toStdString();
233 std::string address = ui->multisigAddress->text().toStdString();
234 std::string label("multisig");
236 if(!model->validateAddress(QString(address.c_str())))
239 std::vector<unsigned char> scriptData(ParseHex(redeemScript));
240 CScript script(scriptData.begin(), scriptData.end());
241 CScriptID scriptID = script.GetID();
243 LOCK(wallet->cs_wallet);
244 if(!wallet->HaveCScript(scriptID))
245 wallet->AddCScript(script);
246 if(!wallet->mapAddressBook.count(CBitcoinAddress(address).Get()))
247 wallet->SetAddressBookName(CBitcoinAddress(address).Get(), label);
250 void MultisigDialog::clear()
252 while(ui->pubkeyEntries->count())
253 delete ui->pubkeyEntries->takeAt(0)->widget();
257 updateRemoveEnabled();
260 MultisigAddressEntry * MultisigDialog::addPubKey()
262 MultisigAddressEntry *entry = new MultisigAddressEntry(this);
264 entry->setModel(model);
265 ui->pubkeyEntries->addWidget(entry);
266 connect(entry, SIGNAL(removeEntry(MultisigAddressEntry *)), this, SLOT(removeEntry(MultisigAddressEntry *)));
267 updateRemoveEnabled();
269 ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
270 QScrollBar *bar = ui->scrollArea->verticalScrollBar();
272 bar->setSliderPosition(bar->maximum());
277 void MultisigDialog::removeEntry(MultisigAddressEntry *entry)
280 updateRemoveEnabled();
283 void MultisigDialog::on_createTransactionButton_clicked()
285 CTransaction transaction;
288 for(int i = 0; i < ui->inputs->count(); i++)
290 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
293 if(entry->validate())
295 CTxIn input = entry->getInput();
296 transaction.vin.push_back(input);
304 for(int i = 0; i < ui->outputs->count(); i++)
306 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
310 if(entry->validate())
312 SendCoinsRecipient recipient = entry->getValue();
313 CBitcoinAddress address(recipient.address.toStdString());
314 CScript scriptPubKey;
315 scriptPubKey.SetAddress(address);
316 int64_t amount = recipient.amount;
317 CTxOut output(amount, scriptPubKey);
318 transaction.vout.push_back(output);
325 CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
327 ui->transaction->setText(HexStr(ss.begin(), ss.end()).c_str());
330 void MultisigDialog::on_transaction_textChanged()
332 while(ui->inputs->count())
333 delete ui->inputs->takeAt(0)->widget();
334 while(ui->outputs->count())
335 delete ui->outputs->takeAt(0)->widget();
337 if(ui->transaction->text().size() > 0)
338 ui->signTransactionButton->setEnabled(true);
340 ui->signTransactionButton->setEnabled(false);
342 // Decode the raw transaction
343 std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString()));
344 CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION);
350 catch(std::exception &e)
358 BOOST_FOREACH(const CTxIn& txin, tx.vin)
360 uint256 prevoutHash = txin.prevout.hash;
363 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(index)->widget());
366 entry->setTransactionId(QString(prevoutHash.GetHex().c_str()));
367 entry->setTransactionOutputIndex(txin.prevout.n);
373 BOOST_FOREACH(const CTxOut& txout, tx.vout)
375 CScript scriptPubKey = txout.scriptPubKey;
377 ExtractDestination(scriptPubKey, addr);
378 CBitcoinAddress address(addr);
379 SendCoinsRecipient recipient;
380 recipient.address = QString(address.ToString().c_str());
381 recipient.amount = txout.nValue;
384 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(index)->widget());
387 entry->setValue(recipient);
391 updateRemoveEnabled();
394 void MultisigDialog::on_copyTransactionButton_clicked()
396 QApplication::clipboard()->setText(ui->transaction->text());
399 void MultisigDialog::on_pasteTransactionButton_clicked()
401 ui->transaction->setText(QApplication::clipboard()->text());
404 void MultisigDialog::on_signTransactionButton_clicked()
406 ui->signedTransaction->clear();
411 CWallet *wallet = model->getWallet();
413 // Decode the raw transaction
414 std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString()));
415 CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION);
421 catch(std::exception &e)
426 CTransaction mergedTx(tx);
428 // Fetch previous transactions (inputs)
429 std::map<COutPoint, CScript> mapPrevOut;
430 for(unsigned int i = 0; i < mergedTx.vin.size(); i++)
435 std::map<uint256, CTxIndex> unused;
438 tempTx.vin.push_back(mergedTx.vin[i]);
439 tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid);
441 BOOST_FOREACH(const CTxIn& txin, tempTx.vin)
443 const uint256& prevHash = txin.prevout.hash;
444 if(mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size() > txin.prevout.n)
445 mapPrevOut[txin.prevout] = mapPrevTx[prevHash].second.vout[txin.prevout.n].scriptPubKey;
449 // Add the redeem scripts to the wallet keystore
450 for(int i = 0; i < ui->inputs->count(); i++)
452 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
455 QString redeemScriptStr = entry->getRedeemScript();
456 if(redeemScriptStr.size() > 0)
458 std::vector<unsigned char> scriptData(ParseHex(redeemScriptStr.toStdString()));
459 CScript redeemScript(scriptData.begin(), scriptData.end());
460 wallet->AddCScript(redeemScript);
465 WalletModel::UnlockContext ctx(model->requestUnlock());
470 bool fComplete = true;
471 for(unsigned int i = 0; i < mergedTx.vin.size(); i++)
473 CTxIn& txin = mergedTx.vin[i];
474 if(mapPrevOut.count(txin.prevout) == 0)
479 const CScript& prevPubKey = mapPrevOut[txin.prevout];
481 txin.scriptSig.clear();
482 SignSignature(*wallet, prevPubKey, mergedTx, i, SIGHASH_ALL);
483 txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, tx.vin[i].scriptSig);
484 if(!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, 0))
490 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
492 ui->signedTransaction->setText(HexStr(ssTx.begin(), ssTx.end()).c_str());
496 ui->statusLabel->setText(tr("Transaction signature is complete"));
497 ui->sendTransactionButton->setEnabled(true);
501 ui->statusLabel->setText(tr("Transaction is NOT completely signed"));
502 ui->sendTransactionButton->setEnabled(false);
506 void MultisigDialog::on_copySignedTransactionButton_clicked()
508 QApplication::clipboard()->setText(ui->signedTransaction->text());
511 void MultisigDialog::on_sendTransactionButton_clicked()
513 int64_t transactionSize = ui->signedTransaction->text().size() / 2;
514 if(transactionSize == 0)
518 int64_t fee = (int64_t ) (ui->fee->text().toDouble() * COIN);
519 int64_t minFee = MIN_TX_FEE * (1 + (int64_t) transactionSize / 1000);
522 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);
523 if(ret != QMessageBox::Yes)
526 else if(fee > minFee)
528 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);
529 if(ret != QMessageBox::Yes)
533 // Decode the raw transaction
534 std::vector<unsigned char> txData(ParseHex(ui->signedTransaction->text().toStdString()));
535 CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
541 catch(std::exception &e)
546 uint256 txHash = tx.GetHash();
548 // Check if the transaction is already in the blockchain
549 CTransaction existingTx;
550 uint256 blockHash = 0;
551 if(GetTransaction(txHash, existingTx, blockHash))
557 // Send the transaction to the local node
559 if(!tx.AcceptToMemoryPool(txdb, false))
561 SyncWithWallets(tx, NULL, true);
562 //(CInv(MSG_TX, txHash), tx);
563 RelayTransaction(tx, txHash);
566 MultisigInputEntry * MultisigDialog::addInput()
568 MultisigInputEntry *entry = new MultisigInputEntry(this);
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();
576 ui->scrollAreaWidgetContents_2->resize(ui->scrollAreaWidgetContents_2->sizeHint());
577 QScrollBar *bar = ui->scrollArea_2->verticalScrollBar();
579 bar->setSliderPosition(bar->maximum());
584 void MultisigDialog::removeEntry(MultisigInputEntry *entry)
587 updateRemoveEnabled();
590 SendCoinsEntry * MultisigDialog::addOutput()
592 SendCoinsEntry *entry = new SendCoinsEntry(this);
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();
600 ui->scrollAreaWidgetContents_3->resize(ui->scrollAreaWidgetContents_3->sizeHint());
601 QScrollBar *bar = ui->scrollArea_3->verticalScrollBar();
603 bar->setSliderPosition(bar->maximum());
608 void MultisigDialog::removeEntry(SendCoinsEntry *entry)
611 updateRemoveEnabled();
614 void MultisigDialog::updateAmounts()
616 // Update inputs amount
617 int64_t inputsAmount = 0;
618 for(int i = 0; i < ui->inputs->count(); i++)
620 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
622 inputsAmount += entry->getAmount();
624 QString inputsAmountStr;
625 inputsAmountStr.sprintf("%.6f", (double) inputsAmount / COIN);
626 ui->inputsAmount->setText(inputsAmountStr);
628 // Update outputs amount
629 int64_t outputsAmount = 0;
630 for(int i = 0; i < ui->outputs->count(); i++)
632 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
634 outputsAmount += entry->getValue().amount;
636 QString outputsAmountStr;
637 outputsAmountStr.sprintf("%.6f", (double) outputsAmount / COIN);
638 ui->outputsAmount->setText(outputsAmountStr);
641 int64_t fee = inputsAmount - outputsAmount;
643 feeStr.sprintf("%.6f", (double) fee / COIN);
644 ui->fee->setText(feeStr);
647 void MultisigDialog::keyPressEvent(QKeyEvent *event)
650 if(windowType() != Qt::Widget && event->key() == Qt::Key_Back)
655 if(windowType() != Qt::Widget && event->key() == Qt::Key_Escape)