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(pubkeys.size() > 16)
169 QMessageBox::warning(this, tr("Error"), tr("Number of addresses involved in the address creation > %1\nReduce the number").arg(16), QMessageBox::Ok);
175 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);
179 if(required > pubkeys.size())
181 QMessageBox::warning(this, tr("Error"), tr("Number of required signatures > Number of keys involved in the creation of address."), QMessageBox::Ok);
186 script.SetMultisig(required, pubkeys);
187 if (script.size() > MAX_SCRIPT_ELEMENT_SIZE)
189 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);
192 CScriptID scriptID = script.GetID();
193 CBitcoinAddress address(scriptID);
195 ui->multisigAddress->setText(address.ToString().c_str());
196 ui->redeemScript->setText(HexStr(script.begin(), script.end()).c_str());
199 void MultisigDialog::on_copyMultisigAddressButton_clicked()
201 QApplication::clipboard()->setText(ui->multisigAddress->text());
204 void MultisigDialog::on_copyRedeemScriptButton_clicked()
206 QApplication::clipboard()->setText(ui->redeemScript->text());
209 void MultisigDialog::on_saveRedeemScriptButton_clicked()
214 CWallet *wallet = model->getWallet();
215 std::string redeemScript = ui->redeemScript->text().toStdString();
216 std::vector<unsigned char> scriptData(ParseHex(redeemScript));
217 CScript script(scriptData.begin(), scriptData.end());
218 CScriptID scriptID = script.GetID();
220 LOCK(wallet->cs_wallet);
221 if(!wallet->HaveCScript(scriptID))
222 wallet->AddCScript(script);
225 void MultisigDialog::on_saveMultisigAddressButton_clicked()
230 CWallet *wallet = model->getWallet();
231 std::string redeemScript = ui->redeemScript->text().toStdString();
232 std::string address = ui->multisigAddress->text().toStdString();
233 std::string label("multisig");
235 if(!model->validateAddress(QString(address.c_str())))
238 std::vector<unsigned char> scriptData(ParseHex(redeemScript));
239 CScript script(scriptData.begin(), scriptData.end());
240 CScriptID scriptID = script.GetID();
242 LOCK(wallet->cs_wallet);
243 if(!wallet->HaveCScript(scriptID))
244 wallet->AddCScript(script);
245 if(!wallet->mapAddressBook.count(CBitcoinAddress(address).Get()))
246 wallet->SetAddressBookName(CBitcoinAddress(address).Get(), label);
249 void MultisigDialog::clear()
251 while(ui->pubkeyEntries->count())
252 delete ui->pubkeyEntries->takeAt(0)->widget();
256 updateRemoveEnabled();
259 MultisigAddressEntry * MultisigDialog::addPubKey()
261 MultisigAddressEntry *entry = new MultisigAddressEntry(this);
263 entry->setModel(model);
264 ui->pubkeyEntries->addWidget(entry);
265 connect(entry, SIGNAL(removeEntry(MultisigAddressEntry *)), this, SLOT(removeEntry(MultisigAddressEntry *)));
266 updateRemoveEnabled();
268 ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
269 QScrollBar *bar = ui->scrollArea->verticalScrollBar();
271 bar->setSliderPosition(bar->maximum());
276 void MultisigDialog::removeEntry(MultisigAddressEntry *entry)
279 updateRemoveEnabled();
282 void MultisigDialog::on_createTransactionButton_clicked()
284 CTransaction transaction;
287 for(int i = 0; i < ui->inputs->count(); i++)
289 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
292 if(entry->validate())
294 CTxIn input = entry->getInput();
295 transaction.vin.push_back(input);
303 for(int i = 0; i < ui->outputs->count(); i++)
305 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
309 if(entry->validate())
311 SendCoinsRecipient recipient = entry->getValue();
312 CBitcoinAddress address(recipient.address.toStdString());
313 CScript scriptPubKey;
314 scriptPubKey.SetDestination(address.Get());
315 int64_t amount = recipient.amount;
316 CTxOut output(amount, scriptPubKey);
317 transaction.vout.push_back(output);
324 CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
326 ui->transaction->setText(HexStr(ss.begin(), ss.end()).c_str());
329 void MultisigDialog::on_transaction_textChanged()
331 while(ui->inputs->count())
332 delete ui->inputs->takeAt(0)->widget();
333 while(ui->outputs->count())
334 delete ui->outputs->takeAt(0)->widget();
336 if(ui->transaction->text().size() > 0)
337 ui->signTransactionButton->setEnabled(true);
339 ui->signTransactionButton->setEnabled(false);
341 // Decode the raw transaction
342 std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString()));
343 CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION);
349 catch(std::exception &e)
356 BOOST_FOREACH(const CTxIn& txin, tx.vin)
358 uint256 prevoutHash = txin.prevout.hash;
361 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(index)->widget());
364 entry->setTransactionId(QString(prevoutHash.GetHex().c_str()));
365 entry->setTransactionOutputIndex(txin.prevout.n);
371 BOOST_FOREACH(const CTxOut& txout, tx.vout)
373 CScript scriptPubKey = txout.scriptPubKey;
375 ExtractDestination(scriptPubKey, addr);
376 CBitcoinAddress address(addr);
377 SendCoinsRecipient recipient;
378 recipient.address = QString(address.ToString().c_str());
379 recipient.amount = txout.nValue;
382 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(index)->widget());
385 entry->setValue(recipient);
389 updateRemoveEnabled();
392 void MultisigDialog::on_copyTransactionButton_clicked()
394 QApplication::clipboard()->setText(ui->transaction->text());
397 void MultisigDialog::on_pasteTransactionButton_clicked()
399 ui->transaction->setText(QApplication::clipboard()->text());
402 void MultisigDialog::on_signTransactionButton_clicked()
404 ui->signedTransaction->clear();
409 CWallet *wallet = model->getWallet();
411 // Decode the raw transaction
412 std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString()));
413 CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION);
419 catch(std::exception &e)
423 CTransaction mergedTx(tx);
425 // Fetch previous transactions (inputs)
426 std::map<COutPoint, CScript> mapPrevOut;
427 for(unsigned int i = 0; i < mergedTx.vin.size(); i++)
432 std::map<uint256, CTxIndex> unused;
435 tempTx.vin.push_back(mergedTx.vin[i]);
436 tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid);
438 BOOST_FOREACH(const CTxIn& txin, tempTx.vin)
440 const uint256& prevHash = txin.prevout.hash;
441 if(mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size() > txin.prevout.n)
442 mapPrevOut[txin.prevout] = mapPrevTx[prevHash].second.vout[txin.prevout.n].scriptPubKey;
446 // Add the redeem scripts to the wallet keystore
447 for(int i = 0; i < ui->inputs->count(); i++)
449 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
452 QString redeemScriptStr = entry->getRedeemScript();
453 if(redeemScriptStr.size() > 0)
455 std::vector<unsigned char> scriptData(ParseHex(redeemScriptStr.toStdString()));
456 CScript redeemScript(scriptData.begin(), scriptData.end());
457 wallet->AddCScript(redeemScript);
462 WalletModel::UnlockContext ctx(model->requestUnlock());
467 bool fComplete = true;
468 for(unsigned int i = 0; i < mergedTx.vin.size(); i++)
470 CTxIn& txin = mergedTx.vin[i];
471 if(mapPrevOut.count(txin.prevout) == 0)
476 const CScript& prevPubKey = mapPrevOut[txin.prevout];
478 txin.scriptSig.clear();
479 SignSignature(*wallet, prevPubKey, mergedTx, i, SIGHASH_ALL);
480 txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, tx.vin[i].scriptSig);
481 if(!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, 0))
487 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
489 ui->signedTransaction->setText(HexStr(ssTx.begin(), ssTx.end()).c_str());
493 ui->statusLabel->setText(tr("Transaction signature is complete"));
494 ui->sendTransactionButton->setEnabled(true);
498 ui->statusLabel->setText(tr("Transaction is NOT completely signed"));
499 ui->sendTransactionButton->setEnabled(false);
503 void MultisigDialog::on_copySignedTransactionButton_clicked()
505 QApplication::clipboard()->setText(ui->signedTransaction->text());
508 void MultisigDialog::on_sendTransactionButton_clicked()
510 int64_t transactionSize = ui->signedTransaction->text().size() / 2;
511 if(transactionSize == 0)
515 int64_t fee = (int64_t ) (ui->fee->text().toDouble() * COIN);
516 int64_t minFee = MIN_TX_FEE * (1 + (int64_t) transactionSize / 1000);
519 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);
520 if(ret != QMessageBox::Yes)
523 else if(fee > minFee)
525 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);
526 if(ret != QMessageBox::Yes)
530 // Decode the raw transaction
531 std::vector<unsigned char> txData(ParseHex(ui->signedTransaction->text().toStdString()));
532 CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
538 catch(std::exception &e)
542 uint256 txHash = tx.GetHash();
544 // Check if the transaction is already in the blockchain
545 CTransaction existingTx;
546 uint256 blockHash = 0;
547 if(GetTransaction(txHash, existingTx, blockHash))
553 // Send the transaction to the local node
555 if(!tx.AcceptToMemoryPool(txdb, false))
557 SyncWithWallets(tx, NULL, true);
558 //(CInv(MSG_TX, txHash), tx);
559 RelayTransaction(tx, txHash);
562 MultisigInputEntry * MultisigDialog::addInput()
564 MultisigInputEntry *entry = new MultisigInputEntry(this);
566 entry->setModel(model);
567 ui->inputs->addWidget(entry);
568 connect(entry, SIGNAL(removeEntry(MultisigInputEntry *)), this, SLOT(removeEntry(MultisigInputEntry *)));
569 connect(entry, SIGNAL(updateAmount()), this, SLOT(updateAmounts()));
570 updateRemoveEnabled();
572 ui->scrollAreaWidgetContents_2->resize(ui->scrollAreaWidgetContents_2->sizeHint());
573 QScrollBar *bar = ui->scrollArea_2->verticalScrollBar();
575 bar->setSliderPosition(bar->maximum());
580 void MultisigDialog::removeEntry(MultisigInputEntry *entry)
583 updateRemoveEnabled();
586 SendCoinsEntry * MultisigDialog::addOutput()
588 SendCoinsEntry *entry = new SendCoinsEntry(this);
590 entry->setModel(model);
591 ui->outputs->addWidget(entry);
592 connect(entry, SIGNAL(removeEntry(SendCoinsEntry *)), this, SLOT(removeEntry(SendCoinsEntry *)));
593 connect(entry, SIGNAL(payAmountChanged()), this, SLOT(updateAmounts()));
594 updateRemoveEnabled();
596 ui->scrollAreaWidgetContents_3->resize(ui->scrollAreaWidgetContents_3->sizeHint());
597 QScrollBar *bar = ui->scrollArea_3->verticalScrollBar();
599 bar->setSliderPosition(bar->maximum());
604 void MultisigDialog::removeEntry(SendCoinsEntry *entry)
607 updateRemoveEnabled();
610 void MultisigDialog::updateAmounts()
612 // Update inputs amount
613 int64_t inputsAmount = 0;
614 for(int i = 0; i < ui->inputs->count(); i++)
616 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
618 inputsAmount += entry->getAmount();
620 QString inputsAmountStr;
621 inputsAmountStr.sprintf("%.6f", (double) inputsAmount / COIN);
622 ui->inputsAmount->setText(inputsAmountStr);
624 // Update outputs amount
625 int64_t outputsAmount = 0;
626 for(int i = 0; i < ui->outputs->count(); i++)
628 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
630 outputsAmount += entry->getValue().amount;
632 QString outputsAmountStr;
633 outputsAmountStr.sprintf("%.6f", (double) outputsAmount / COIN);
634 ui->outputsAmount->setText(outputsAmountStr);
637 int64_t fee = inputsAmount - outputsAmount;
639 feeStr.sprintf("%.6f", (double) fee / COIN);
640 ui->fee->setText(feeStr);