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"
27 MultisigDialog::MultisigDialog(QWidget *parent) : QDialog(parent), ui(new Ui::MultisigDialog), model(0)
31 #ifdef Q_WS_MAC // Icons on push buttons are very uncommon on Mac
32 ui->addPubKeyButton->setIcon(QIcon());
33 ui->clearButton->setIcon(QIcon());
34 ui->addInputButton->setIcon(QIcon());
35 ui->addOutputButton->setIcon(QIcon());
36 ui->signTransactionButton->setIcon(QIcon());
37 ui->sendTransactionButton->setIcon(QIcon());
43 connect(ui->addPubKeyButton, SIGNAL(clicked()), this, SLOT(addPubKey()));
44 connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
49 connect(ui->addInputButton, SIGNAL(clicked()), this, SLOT(addInput()));
50 connect(ui->addOutputButton, SIGNAL(clicked()), this, SLOT(addOutput()));
52 ui->signTransactionButton->setEnabled(false);
53 ui->sendTransactionButton->setEnabled(false);
56 void MultisigDialog::showEvent(QShowEvent *event)
58 QWidget::showEvent(event);
66 void MultisigDialog::hideEvent(QHideEvent *event)
68 QWidget::hideEvent(event);
76 MultisigDialog::~MultisigDialog()
81 void MultisigDialog::setModel(WalletModel *model)
85 for(int i = 0; i < ui->pubkeyEntries->count(); i++)
87 MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget());
89 entry->setModel(model);
93 for(int i = 0; i < ui->inputs->count(); i++)
95 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
97 entry->setModel(model);
101 for(int i = 0; i < ui->outputs->count(); i++)
103 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
105 entry->setModel(model);
109 void MultisigDialog::updateRemoveEnabled()
111 bool enabled = (ui->pubkeyEntries->count() > 2);
113 for(int i = 0; i < ui->pubkeyEntries->count(); i++)
115 MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget());
117 entry->setRemoveEnabled(enabled);
121 maxSigsStr.setNum(ui->pubkeyEntries->count());
122 ui->maxSignaturesLabel->setText(QString("/ ") + maxSigsStr);
125 enabled = (ui->inputs->count() > 1);
126 for(int i = 0; i < ui->inputs->count(); i++)
128 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
130 entry->setRemoveEnabled(enabled);
134 enabled = (ui->outputs->count() > 1);
135 for(int i = 0; i < ui->outputs->count(); i++)
137 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
139 entry->setRemoveEnabled(enabled);
143 void MultisigDialog::on_createAddressButton_clicked()
145 ui->multisigAddress->clear();
146 ui->redeemScript->clear();
151 std::vector<CKey> pubkeys;
152 pubkeys.resize(ui->pubkeyEntries->count());
153 unsigned int required = ui->requiredSignatures->text().toUInt();
155 for(int i = 0; i < ui->pubkeyEntries->count(); i++)
157 MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget());
158 if(!entry->validate())
160 QString str = entry->getPubkey();
161 CPubKey vchPubKey(ParseHex(str.toStdString().c_str()));
162 if(!vchPubKey.IsValid())
164 pubkeys[i].SetPubKey(vchPubKey);
167 if((required == 0) || (required > pubkeys.size()))
171 script.SetMultisig(required, pubkeys);
172 CScriptID scriptID = script.GetID();
173 CBitcoinAddress address(scriptID);
175 ui->multisigAddress->setText(address.ToString().c_str());
176 ui->redeemScript->setText(HexStr(script.begin(), script.end()).c_str());
179 void MultisigDialog::on_copyMultisigAddressButton_clicked()
181 QApplication::clipboard()->setText(ui->multisigAddress->text());
184 void MultisigDialog::on_copyRedeemScriptButton_clicked()
186 QApplication::clipboard()->setText(ui->redeemScript->text());
189 void MultisigDialog::on_saveRedeemScriptButton_clicked()
194 CWallet *wallet = model->getWallet();
195 std::string redeemScript = ui->redeemScript->text().toStdString();
196 std::vector<unsigned char> scriptData(ParseHex(redeemScript));
197 CScript script(scriptData.begin(), scriptData.end());
198 CScriptID scriptID = script.GetID();
200 LOCK(wallet->cs_wallet);
201 if(!wallet->HaveCScript(scriptID))
202 wallet->AddCScript(script);
205 void MultisigDialog::on_saveMultisigAddressButton_clicked()
210 CWallet *wallet = model->getWallet();
211 std::string redeemScript = ui->redeemScript->text().toStdString();
212 std::string address = ui->multisigAddress->text().toStdString();
213 std::string label("multisig");
215 if(!model->validateAddress(QString(address.c_str())))
218 std::vector<unsigned char> scriptData(ParseHex(redeemScript));
219 CScript script(scriptData.begin(), scriptData.end());
220 CScriptID scriptID = script.GetID();
222 LOCK(wallet->cs_wallet);
223 if(!wallet->HaveCScript(scriptID))
224 wallet->AddCScript(script);
225 if(!wallet->mapAddressBook.count(CBitcoinAddress(address).Get()))
226 wallet->SetAddressBookName(CBitcoinAddress(address).Get(), label);
229 void MultisigDialog::clear()
231 while(ui->pubkeyEntries->count())
232 delete ui->pubkeyEntries->takeAt(0)->widget();
236 updateRemoveEnabled();
239 MultisigAddressEntry * MultisigDialog::addPubKey()
241 MultisigAddressEntry *entry = new MultisigAddressEntry(this);
243 entry->setModel(model);
244 ui->pubkeyEntries->addWidget(entry);
245 connect(entry, SIGNAL(removeEntry(MultisigAddressEntry *)), this, SLOT(removeEntry(MultisigAddressEntry *)));
246 updateRemoveEnabled();
248 ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
249 QScrollBar *bar = ui->scrollArea->verticalScrollBar();
251 bar->setSliderPosition(bar->maximum());
256 void MultisigDialog::removeEntry(MultisigAddressEntry *entry)
259 updateRemoveEnabled();
262 void MultisigDialog::on_createTransactionButton_clicked()
264 CTransaction transaction;
267 for(int i = 0; i < ui->inputs->count(); i++)
269 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
272 if(entry->validate())
274 CTxIn input = entry->getInput();
275 transaction.vin.push_back(input);
283 for(int i = 0; i < ui->outputs->count(); i++)
285 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
289 if(entry->validate())
291 SendCoinsRecipient recipient = entry->getValue();
292 CBitcoinAddress address(recipient.address.toStdString());
293 CScript scriptPubKey;
294 scriptPubKey.SetDestination(address.Get());
295 int64_t amount = recipient.amount;
296 CTxOut output(amount, scriptPubKey);
297 transaction.vout.push_back(output);
304 CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
306 ui->transaction->setText(HexStr(ss.begin(), ss.end()).c_str());
309 void MultisigDialog::on_transaction_textChanged()
311 while(ui->inputs->count())
312 delete ui->inputs->takeAt(0)->widget();
313 while(ui->outputs->count())
314 delete ui->outputs->takeAt(0)->widget();
316 if(ui->transaction->text().size() > 0)
317 ui->signTransactionButton->setEnabled(true);
319 ui->signTransactionButton->setEnabled(false);
321 // Decode the raw transaction
322 std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString()));
323 CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION);
329 catch(std::exception &e)
336 BOOST_FOREACH(const CTxIn& txin, tx.vin)
338 uint256 prevoutHash = txin.prevout.hash;
341 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(index)->widget());
344 entry->setTransactionId(QString(prevoutHash.GetHex().c_str()));
345 entry->setTransactionOutputIndex(txin.prevout.n);
351 BOOST_FOREACH(const CTxOut& txout, tx.vout)
353 CScript scriptPubKey = txout.scriptPubKey;
355 ExtractDestination(scriptPubKey, addr);
356 CBitcoinAddress address(addr);
357 SendCoinsRecipient recipient;
358 recipient.address = QString(address.ToString().c_str());
359 recipient.amount = txout.nValue;
362 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(index)->widget());
365 entry->setValue(recipient);
369 updateRemoveEnabled();
372 void MultisigDialog::on_copyTransactionButton_clicked()
374 QApplication::clipboard()->setText(ui->transaction->text());
377 void MultisigDialog::on_pasteTransactionButton_clicked()
379 ui->transaction->setText(QApplication::clipboard()->text());
382 void MultisigDialog::on_signTransactionButton_clicked()
384 ui->signedTransaction->clear();
389 CWallet *wallet = model->getWallet();
391 // Decode the raw transaction
392 std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString()));
393 CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION);
399 catch(std::exception &e)
403 CTransaction mergedTx(tx);
405 // Fetch previous transactions (inputs)
406 std::map<COutPoint, CScript> mapPrevOut;
407 for(int i = 0; i < mergedTx.vin.size(); i++)
412 std::map<uint256, CTxIndex> unused;
415 tempTx.vin.push_back(mergedTx.vin[i]);
416 tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid);
418 BOOST_FOREACH(const CTxIn& txin, tempTx.vin)
420 const uint256& prevHash = txin.prevout.hash;
421 if(mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size() > txin.prevout.n)
422 mapPrevOut[txin.prevout] = mapPrevTx[prevHash].second.vout[txin.prevout.n].scriptPubKey;
426 // Add the redeem scripts to the wallet keystore
427 for(int i = 0; i < ui->inputs->count(); i++)
429 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
432 QString redeemScriptStr = entry->getRedeemScript();
433 if(redeemScriptStr.size() > 0)
435 std::vector<unsigned char> scriptData(ParseHex(redeemScriptStr.toStdString()));
436 CScript redeemScript(scriptData.begin(), scriptData.end());
437 wallet->AddCScript(redeemScript);
442 WalletModel::UnlockContext ctx(model->requestUnlock());
447 bool fComplete = true;
448 for(int i = 0; i < mergedTx.vin.size(); i++)
450 CTxIn& txin = mergedTx.vin[i];
451 if(mapPrevOut.count(txin.prevout) == 0)
456 const CScript& prevPubKey = mapPrevOut[txin.prevout];
458 txin.scriptSig.clear();
459 SignSignature(*wallet, prevPubKey, mergedTx, i, SIGHASH_ALL);
460 txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, tx.vin[i].scriptSig);
461 if(!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, 0))
467 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
469 ui->signedTransaction->setText(HexStr(ssTx.begin(), ssTx.end()).c_str());
473 ui->statusLabel->setText(tr("Transaction signature is complete"));
474 ui->sendTransactionButton->setEnabled(true);
478 ui->statusLabel->setText(tr("Transaction is NOT completely signed"));
479 ui->sendTransactionButton->setEnabled(false);
483 void MultisigDialog::on_copySignedTransactionButton_clicked()
485 QApplication::clipboard()->setText(ui->signedTransaction->text());
488 void MultisigDialog::on_sendTransactionButton_clicked()
490 int64_t transactionSize = ui->signedTransaction->text().size() / 2;
491 if(transactionSize == 0)
495 int64_t fee = (int64_t ) (ui->fee->text().toDouble() * COIN);
496 int64_t minFee = MIN_TX_FEE * (1 + (int64_t) transactionSize / 1000);
499 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);
500 if(ret != QMessageBox::Yes)
503 else if(fee > minFee)
505 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);
506 if(ret != QMessageBox::Yes)
510 // Decode the raw transaction
511 std::vector<unsigned char> txData(ParseHex(ui->signedTransaction->text().toStdString()));
512 CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
518 catch(std::exception &e)
522 uint256 txHash = tx.GetHash();
524 // Check if the transaction is already in the blockchain
525 CTransaction existingTx;
526 uint256 blockHash = 0;
527 if(GetTransaction(txHash, existingTx, blockHash))
533 // Send the transaction to the local node
535 if(!tx.AcceptToMemoryPool(txdb, false))
537 SyncWithWallets(tx, NULL, true);
538 //(CInv(MSG_TX, txHash), tx);
539 RelayTransaction(tx, txHash);
542 MultisigInputEntry * MultisigDialog::addInput()
544 MultisigInputEntry *entry = new MultisigInputEntry(this);
546 entry->setModel(model);
547 ui->inputs->addWidget(entry);
548 connect(entry, SIGNAL(removeEntry(MultisigInputEntry *)), this, SLOT(removeEntry(MultisigInputEntry *)));
549 connect(entry, SIGNAL(updateAmount()), this, SLOT(updateAmounts()));
550 updateRemoveEnabled();
552 ui->scrollAreaWidgetContents_2->resize(ui->scrollAreaWidgetContents_2->sizeHint());
553 QScrollBar *bar = ui->scrollArea_2->verticalScrollBar();
555 bar->setSliderPosition(bar->maximum());
560 void MultisigDialog::removeEntry(MultisigInputEntry *entry)
563 updateRemoveEnabled();
566 SendCoinsEntry * MultisigDialog::addOutput()
568 SendCoinsEntry *entry = new SendCoinsEntry(this);
570 entry->setModel(model);
571 ui->outputs->addWidget(entry);
572 connect(entry, SIGNAL(removeEntry(SendCoinsEntry *)), this, SLOT(removeEntry(SendCoinsEntry *)));
573 connect(entry, SIGNAL(payAmountChanged()), this, SLOT(updateAmounts()));
574 updateRemoveEnabled();
576 ui->scrollAreaWidgetContents_3->resize(ui->scrollAreaWidgetContents_3->sizeHint());
577 QScrollBar *bar = ui->scrollArea_3->verticalScrollBar();
579 bar->setSliderPosition(bar->maximum());
584 void MultisigDialog::removeEntry(SendCoinsEntry *entry)
587 updateRemoveEnabled();
590 void MultisigDialog::updateAmounts()
592 // Update inputs amount
593 int64_t inputsAmount = 0;
594 for(int i = 0; i < ui->inputs->count(); i++)
596 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
598 inputsAmount += entry->getAmount();
600 QString inputsAmountStr;
601 inputsAmountStr.sprintf("%.6f", (double) inputsAmount / COIN);
602 ui->inputsAmount->setText(inputsAmountStr);
604 // Update outputs amount
605 int64_t outputsAmount = 0;
606 for(int i = 0; i < ui->outputs->count(); i++)
608 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
610 outputsAmount += entry->getValue().amount;
612 QString outputsAmountStr;
613 outputsAmountStr.sprintf("%.6f", (double) outputsAmount / COIN);
614 ui->outputsAmount->setText(outputsAmountStr);
617 int64_t fee = inputsAmount - outputsAmount;
619 feeStr.sprintf("%.6f", (double) fee / COIN);
620 ui->fee->setText(feeStr);