7 #include "addresstablemodel.h"
11 #include "multisigaddressentry.h"
12 #include "multisiginputentry.h"
13 #include "multisigdialog.h"
14 #include "ui_multisigdialog.h"
16 #include "sendcoinsentry.h"
19 #include "walletmodel.h"
22 #include "txdb-leveldb.h"
28 #pragma warning( disable : 4101)
31 MultisigDialog::MultisigDialog(QWidget *parent) : QDialog(parent), ui(new Ui::MultisigDialog), model(0)
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());
47 connect(ui->addPubKeyButton, SIGNAL(clicked()), this, SLOT(addPubKey()));
48 connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
53 connect(ui->addInputButton, SIGNAL(clicked()), this, SLOT(addInput()));
54 connect(ui->addOutputButton, SIGNAL(clicked()), this, SLOT(addOutput()));
56 ui->signTransactionButton->setEnabled(false);
57 ui->sendTransactionButton->setEnabled(false);
60 void MultisigDialog::showEvent(QShowEvent *event)
62 QWidget::showEvent(event);
70 void MultisigDialog::hideEvent(QHideEvent *event)
72 QWidget::hideEvent(event);
80 MultisigDialog::~MultisigDialog()
85 void MultisigDialog::setModel(WalletModel *model)
89 for(int i = 0; i < ui->pubkeyEntries->count(); i++)
91 MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget());
93 entry->setModel(model);
97 for(int i = 0; i < ui->inputs->count(); i++)
99 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
101 entry->setModel(model);
105 for(int i = 0; i < ui->outputs->count(); i++)
107 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
109 entry->setModel(model);
113 void MultisigDialog::updateRemoveEnabled()
115 bool enabled = (ui->pubkeyEntries->count() > 2);
117 for(int i = 0; i < ui->pubkeyEntries->count(); i++)
119 MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget());
121 entry->setRemoveEnabled(enabled);
125 maxSigsStr.setNum(ui->pubkeyEntries->count());
126 ui->maxSignaturesLabel->setText(QString("/ ") + maxSigsStr);
129 enabled = (ui->inputs->count() > 1);
130 for(int i = 0; i < ui->inputs->count(); i++)
132 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
134 entry->setRemoveEnabled(enabled);
138 enabled = (ui->outputs->count() > 1);
139 for(int i = 0; i < ui->outputs->count(); i++)
141 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
143 entry->setRemoveEnabled(enabled);
147 void MultisigDialog::on_createAddressButton_clicked()
149 ui->multisigAddress->clear();
150 ui->redeemScript->clear();
155 std::vector<CKey> pubkeys;
156 pubkeys.resize(ui->pubkeyEntries->count());
157 unsigned int required = ui->requiredSignatures->text().toUInt();
159 for(int i = 0; i < ui->pubkeyEntries->count(); i++)
161 MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget());
162 if(!entry->validate())
164 QString str = entry->getPubkey();
165 CPubKey vchPubKey(ParseHex(str.toStdString().c_str()));
166 if(!vchPubKey.IsValid())
168 pubkeys[i].SetPubKey(vchPubKey);
171 if(pubkeys.size() > 16)
173 QMessageBox::warning(this, tr("Error"), tr("Number of addresses involved in the address creation > %1\nReduce the number").arg(16), QMessageBox::Ok);
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);
183 if(required > pubkeys.size())
185 QMessageBox::warning(this, tr("Error"), tr("Number of required signatures > Number of keys involved in the creation of address."), QMessageBox::Ok);
190 script.SetMultisig(required, pubkeys);
191 if (script.size() > MAX_SCRIPT_ELEMENT_SIZE)
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);
196 CScriptID scriptID = script.GetID();
197 CBitcoinAddress address(scriptID);
199 ui->multisigAddress->setText(address.ToString().c_str());
200 ui->redeemScript->setText(HexStr(script.begin(), script.end()).c_str());
203 void MultisigDialog::on_copyMultisigAddressButton_clicked()
205 QApplication::clipboard()->setText(ui->multisigAddress->text());
208 void MultisigDialog::on_copyRedeemScriptButton_clicked()
210 QApplication::clipboard()->setText(ui->redeemScript->text());
213 void MultisigDialog::on_saveRedeemScriptButton_clicked()
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();
224 LOCK(wallet->cs_wallet);
225 if(!wallet->HaveCScript(scriptID))
226 wallet->AddCScript(script);
229 void MultisigDialog::on_saveMultisigAddressButton_clicked()
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");
239 if(!model->validateAddress(QString(address.c_str())))
242 std::vector<unsigned char> scriptData(ParseHex(redeemScript));
243 CScript script(scriptData.begin(), scriptData.end());
244 CScriptID scriptID = script.GetID();
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);
253 void MultisigDialog::clear()
255 while(ui->pubkeyEntries->count())
256 delete ui->pubkeyEntries->takeAt(0)->widget();
260 updateRemoveEnabled();
263 MultisigAddressEntry * MultisigDialog::addPubKey()
265 MultisigAddressEntry *entry = new MultisigAddressEntry(this);
267 entry->setModel(model);
268 ui->pubkeyEntries->addWidget(entry);
269 connect(entry, SIGNAL(removeEntry(MultisigAddressEntry *)), this, SLOT(removeEntry(MultisigAddressEntry *)));
270 updateRemoveEnabled();
272 ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
273 QScrollBar *bar = ui->scrollArea->verticalScrollBar();
275 bar->setSliderPosition(bar->maximum());
280 void MultisigDialog::removeEntry(MultisigAddressEntry *entry)
283 updateRemoveEnabled();
286 void MultisigDialog::on_createTransactionButton_clicked()
288 CTransaction transaction;
291 for(int i = 0; i < ui->inputs->count(); i++)
293 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
296 if(entry->validate())
298 CTxIn input = entry->getInput();
299 transaction.vin.push_back(input);
307 for(int i = 0; i < ui->outputs->count(); i++)
309 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
313 if(entry->validate())
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);
328 CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
330 ui->transaction->setText(HexStr(ss.begin(), ss.end()).c_str());
333 void MultisigDialog::on_transaction_textChanged()
335 while(ui->inputs->count())
336 delete ui->inputs->takeAt(0)->widget();
337 while(ui->outputs->count())
338 delete ui->outputs->takeAt(0)->widget();
340 if(ui->transaction->text().size() > 0)
341 ui->signTransactionButton->setEnabled(true);
343 ui->signTransactionButton->setEnabled(false);
345 // Decode the raw transaction
346 std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString()));
347 CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION);
353 catch(std::exception &e)
360 BOOST_FOREACH(const CTxIn& txin, tx.vin)
362 uint256 prevoutHash = txin.prevout.hash;
365 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(index)->widget());
368 entry->setTransactionId(QString(prevoutHash.GetHex().c_str()));
369 entry->setTransactionOutputIndex(txin.prevout.n);
375 BOOST_FOREACH(const CTxOut& txout, tx.vout)
377 CScript scriptPubKey = txout.scriptPubKey;
379 ExtractDestination(scriptPubKey, addr);
380 CBitcoinAddress address(addr);
381 SendCoinsRecipient recipient;
382 recipient.address = QString(address.ToString().c_str());
383 recipient.amount = txout.nValue;
386 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(index)->widget());
389 entry->setValue(recipient);
393 updateRemoveEnabled();
396 void MultisigDialog::on_copyTransactionButton_clicked()
398 QApplication::clipboard()->setText(ui->transaction->text());
401 void MultisigDialog::on_pasteTransactionButton_clicked()
403 ui->transaction->setText(QApplication::clipboard()->text());
406 void MultisigDialog::on_signTransactionButton_clicked()
408 ui->signedTransaction->clear();
413 CWallet *wallet = model->getWallet();
415 // Decode the raw transaction
416 std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString()));
417 CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION);
423 catch(std::exception &e)
427 CTransaction mergedTx(tx);
429 // Fetch previous transactions (inputs)
430 std::map<COutPoint, CScript> mapPrevOut;
431 for(unsigned int i = 0; i < mergedTx.vin.size(); i++)
436 std::map<uint256, CTxIndex> unused;
439 tempTx.vin.push_back(mergedTx.vin[i]);
440 tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid);
442 BOOST_FOREACH(const CTxIn& txin, tempTx.vin)
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;
450 // Add the redeem scripts to the wallet keystore
451 for(int i = 0; i < ui->inputs->count(); i++)
453 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
456 QString redeemScriptStr = entry->getRedeemScript();
457 if(redeemScriptStr.size() > 0)
459 std::vector<unsigned char> scriptData(ParseHex(redeemScriptStr.toStdString()));
460 CScript redeemScript(scriptData.begin(), scriptData.end());
461 wallet->AddCScript(redeemScript);
466 WalletModel::UnlockContext ctx(model->requestUnlock());
471 bool fComplete = true;
472 for(unsigned int i = 0; i < mergedTx.vin.size(); i++)
474 CTxIn& txin = mergedTx.vin[i];
475 if(mapPrevOut.count(txin.prevout) == 0)
480 const CScript& prevPubKey = mapPrevOut[txin.prevout];
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))
491 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
493 ui->signedTransaction->setText(HexStr(ssTx.begin(), ssTx.end()).c_str());
497 ui->statusLabel->setText(tr("Transaction signature is complete"));
498 ui->sendTransactionButton->setEnabled(true);
502 ui->statusLabel->setText(tr("Transaction is NOT completely signed"));
503 ui->sendTransactionButton->setEnabled(false);
507 void MultisigDialog::on_copySignedTransactionButton_clicked()
509 QApplication::clipboard()->setText(ui->signedTransaction->text());
512 void MultisigDialog::on_sendTransactionButton_clicked()
514 int64_t transactionSize = ui->signedTransaction->text().size() / 2;
515 if(transactionSize == 0)
519 int64_t fee = (int64_t ) (ui->fee->text().toDouble() * COIN);
520 int64_t minFee = MIN_TX_FEE * (1 + (int64_t) transactionSize / 1000);
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)
527 else if(fee > minFee)
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)
534 // Decode the raw transaction
535 std::vector<unsigned char> txData(ParseHex(ui->signedTransaction->text().toStdString()));
536 CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
542 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);