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"
21 #include "txdb-leveldb.h"
23 MultisigDialog::MultisigDialog(QWidget *parent) : QWidget(parent), ui(new Ui::MultisigDialog), model(0)
27 #ifdef Q_WS_MAC // Icons on push buttons are very uncommon on Mac
28 ui->addPubKeyButton->setIcon(QIcon());
29 ui->clearButton->setIcon(QIcon());
30 ui->addInputButton->setIcon(QIcon());
31 ui->addOutputButton->setIcon(QIcon());
32 ui->signTransactionButton->setIcon(QIcon());
33 ui->sendTransactionButton->setIcon(QIcon());
39 connect(ui->addPubKeyButton, SIGNAL(clicked()), this, SLOT(addPubKey()));
40 connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
45 connect(ui->addInputButton, SIGNAL(clicked()), this, SLOT(addInput()));
46 connect(ui->addOutputButton, SIGNAL(clicked()), this, SLOT(addOutput()));
48 ui->signTransactionButton->setEnabled(false);
49 ui->sendTransactionButton->setEnabled(false);
52 void MultisigDialog::showEvent(QShowEvent *event)
54 QWidget::showEvent(event);
62 void MultisigDialog::hideEvent(QHideEvent *event)
64 QWidget::hideEvent(event);
72 MultisigDialog::~MultisigDialog()
77 void MultisigDialog::setModel(WalletModel *model)
81 for(int i = 0; i < ui->pubkeyEntries->count(); i++)
83 MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget());
85 entry->setModel(model);
89 for(int i = 0; i < ui->inputs->count(); i++)
91 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
93 entry->setModel(model);
97 for(int i = 0; i < ui->outputs->count(); i++)
99 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
101 entry->setModel(model);
105 void MultisigDialog::updateRemoveEnabled()
107 bool enabled = (ui->pubkeyEntries->count() > 2);
109 for(int i = 0; i < ui->pubkeyEntries->count(); i++)
111 MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget());
113 entry->setRemoveEnabled(enabled);
117 maxSigsStr.setNum(ui->pubkeyEntries->count());
118 ui->maxSignaturesLabel->setText(QString("/ ") + maxSigsStr);
121 enabled = (ui->inputs->count() > 1);
122 for(int i = 0; i < ui->inputs->count(); i++)
124 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
126 entry->setRemoveEnabled(enabled);
130 enabled = (ui->outputs->count() > 1);
131 for(int i = 0; i < ui->outputs->count(); i++)
133 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
135 entry->setRemoveEnabled(enabled);
139 void MultisigDialog::on_createAddressButton_clicked()
141 ui->multisigAddress->clear();
142 ui->redeemScript->clear();
147 std::vector<CPubKey> pubkeys;
148 pubkeys.resize(ui->pubkeyEntries->count());
149 unsigned int required = ui->requiredSignatures->text().toUInt();
151 for(int i = 0; i < ui->pubkeyEntries->count(); i++)
153 MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget());
154 if(!entry->validate())
156 QString str = entry->getPubkey();
157 CPubKey vchPubKey(ParseHex(str.toStdString().c_str()));
158 if(!vchPubKey.IsValid())
160 pubkeys[i] = vchPubKey;
163 if(pubkeys.size() > 16)
165 QMessageBox::warning(this, tr("Error"), tr("Number of addresses involved in the address creation > %1\nReduce the number").arg(16), QMessageBox::Ok);
171 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);
175 if(required > pubkeys.size())
177 QMessageBox::warning(this, tr("Error"), tr("Number of required signatures > Number of keys involved in the creation of address."), QMessageBox::Ok);
182 script.SetMultisig(required, pubkeys);
183 if (script.size() > MAX_SCRIPT_ELEMENT_SIZE)
185 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);
188 CScriptID scriptID = script.GetID();
189 CBitcoinAddress address(scriptID);
191 ui->multisigAddress->setText(address.ToString().c_str());
192 ui->redeemScript->setText(HexStr(script.begin(), script.end()).c_str());
195 void MultisigDialog::on_copyMultisigAddressButton_clicked()
197 QApplication::clipboard()->setText(ui->multisigAddress->text());
200 void MultisigDialog::on_copyRedeemScriptButton_clicked()
202 QApplication::clipboard()->setText(ui->redeemScript->text());
205 void MultisigDialog::on_saveRedeemScriptButton_clicked()
210 CWallet *wallet = model->getWallet();
211 std::string redeemScript = ui->redeemScript->text().toStdString();
212 std::vector<unsigned char> scriptData(ParseHex(redeemScript));
213 CScript script(scriptData.begin(), scriptData.end());
214 CScriptID scriptID = script.GetID();
216 LOCK(wallet->cs_wallet);
217 if(!wallet->HaveCScript(scriptID))
218 wallet->AddCScript(script);
221 void MultisigDialog::on_saveMultisigAddressButton_clicked()
226 CWallet *wallet = model->getWallet();
227 std::string redeemScript = ui->redeemScript->text().toStdString();
228 std::string address = ui->multisigAddress->text().toStdString();
229 std::string label("multisig");
231 if(!model->validateAddress(QString(address.c_str())))
234 std::vector<unsigned char> scriptData(ParseHex(redeemScript));
235 CScript script(scriptData.begin(), scriptData.end());
236 CScriptID scriptID = script.GetID();
238 LOCK(wallet->cs_wallet);
239 if(!wallet->HaveCScript(scriptID))
240 wallet->AddCScript(script);
241 if(!wallet->mapAddressBook.count(CBitcoinAddress(address)))
242 wallet->SetAddressBookName(CBitcoinAddress(address), label);
245 void MultisigDialog::clear()
247 while(ui->pubkeyEntries->count())
248 delete ui->pubkeyEntries->takeAt(0)->widget();
252 updateRemoveEnabled();
255 MultisigAddressEntry * MultisigDialog::addPubKey()
257 MultisigAddressEntry *entry = new MultisigAddressEntry(this);
259 entry->setModel(model);
260 ui->pubkeyEntries->addWidget(entry);
261 connect(entry, SIGNAL(removeEntry(MultisigAddressEntry *)), this, SLOT(removeEntry(MultisigAddressEntry *)));
262 updateRemoveEnabled();
264 ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
265 QScrollBar *bar = ui->scrollArea->verticalScrollBar();
267 bar->setSliderPosition(bar->maximum());
272 void MultisigDialog::removeEntry(MultisigAddressEntry *entry)
275 updateRemoveEnabled();
278 void MultisigDialog::on_createTransactionButton_clicked()
280 CTransaction transaction;
283 for(int i = 0; i < ui->inputs->count(); i++)
285 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
288 if(entry->validate())
290 CTxIn input = entry->getInput();
291 transaction.vin.push_back(input);
299 for(int i = 0; i < ui->outputs->count(); i++)
301 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
305 if(entry->validate())
307 SendCoinsRecipient recipient = entry->getValue();
308 CBitcoinAddress address(recipient.address.toStdString());
309 CScript scriptPubKey;
310 scriptPubKey.SetAddress(address);
311 int64_t amount = recipient.amount;
312 CTxOut output(amount, scriptPubKey);
313 transaction.vout.push_back(output);
320 CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
322 ui->transaction->setText(HexStr(ss.begin(), ss.end()).c_str());
325 void MultisigDialog::on_transaction_textChanged()
327 while(ui->inputs->count())
328 delete ui->inputs->takeAt(0)->widget();
329 while(ui->outputs->count())
330 delete ui->outputs->takeAt(0)->widget();
332 if(ui->transaction->text().size() > 0)
333 ui->signTransactionButton->setEnabled(true);
335 ui->signTransactionButton->setEnabled(false);
337 // Decode the raw transaction
338 std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString()));
339 CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION);
345 catch(const std::exception&)
352 for (const CTxIn& txin : tx.vin)
354 uint256 prevoutHash = txin.prevout.hash;
357 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(index)->widget());
360 entry->setTransactionId(QString(prevoutHash.GetHex().c_str()));
361 entry->setTransactionOutputIndex(txin.prevout.n);
367 for (const CTxOut& txout : tx.vout)
369 CScript scriptPubKey = txout.scriptPubKey;
371 ExtractDestination(scriptPubKey, addr);
372 CBitcoinAddress address(addr);
373 SendCoinsRecipient recipient;
374 recipient.address = QString(address.ToString().c_str());
375 recipient.amount = txout.nValue;
378 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(index)->widget());
381 entry->setValue(recipient);
385 updateRemoveEnabled();
388 void MultisigDialog::on_copyTransactionButton_clicked()
390 QApplication::clipboard()->setText(ui->transaction->text());
393 void MultisigDialog::on_pasteTransactionButton_clicked()
395 ui->transaction->setText(QApplication::clipboard()->text());
398 void MultisigDialog::on_signTransactionButton_clicked()
400 ui->signedTransaction->clear();
405 CWallet *wallet = model->getWallet();
407 // Decode the raw transaction
408 std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString()));
409 CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION);
415 catch(const std::exception&)
419 CTransaction mergedTx(tx);
421 // Fetch previous transactions (inputs)
422 std::map<COutPoint, CScript> mapPrevOut;
423 for(unsigned int i = 0; i < mergedTx.vin.size(); i++)
428 std::map<uint256, CTxIndex> unused;
431 tempTx.vin.push_back(mergedTx.vin[i]);
432 tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid);
434 for (const CTxIn& txin : tempTx.vin)
436 const uint256& prevHash = txin.prevout.hash;
437 if(mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size() > txin.prevout.n)
438 mapPrevOut[txin.prevout] = mapPrevTx[prevHash].second.vout[txin.prevout.n].scriptPubKey;
442 // Add the redeem scripts to the wallet keystore
443 for(int i = 0; i < ui->inputs->count(); i++)
445 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
448 QString redeemScriptStr = entry->getRedeemScript();
449 if(redeemScriptStr.size() > 0)
451 std::vector<unsigned char> scriptData(ParseHex(redeemScriptStr.toStdString()));
452 CScript redeemScript(scriptData.begin(), scriptData.end());
453 wallet->AddCScript(redeemScript);
458 WalletModel::UnlockContext ctx(model->requestUnlock());
463 bool fComplete = true;
464 for(unsigned int i = 0; i < mergedTx.vin.size(); i++)
466 CTxIn& txin = mergedTx.vin[i];
467 if(mapPrevOut.count(txin.prevout) == 0)
472 const CScript& prevPubKey = mapPrevOut[txin.prevout];
474 txin.scriptSig.clear();
475 SignSignature(*wallet, prevPubKey, mergedTx, i, SIGHASH_ALL);
476 txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, tx.vin[i].scriptSig);
477 if(!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, 0))
483 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
485 ui->signedTransaction->setText(HexStr(ssTx.begin(), ssTx.end()).c_str());
489 ui->statusLabel->setText(tr("Transaction signature is complete"));
490 ui->sendTransactionButton->setEnabled(true);
494 ui->statusLabel->setText(tr("Transaction is NOT completely signed"));
495 ui->sendTransactionButton->setEnabled(false);
499 void MultisigDialog::on_copySignedTransactionButton_clicked()
501 QApplication::clipboard()->setText(ui->signedTransaction->text());
504 void MultisigDialog::on_sendTransactionButton_clicked()
506 int64_t transactionSize = ui->signedTransaction->text().size() / 2;
507 if(transactionSize == 0)
511 int64_t fee = (int64_t ) (ui->fee->text().toDouble() * COIN);
512 int64_t minFee = MIN_TX_FEE * (1 + (int64_t) transactionSize / 1000);
515 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);
516 if(ret != QMessageBox::Yes)
519 else if(fee > minFee)
521 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);
522 if(ret != QMessageBox::Yes)
526 // Decode the raw transaction
527 std::vector<unsigned char> txData(ParseHex(ui->signedTransaction->text().toStdString()));
528 CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
534 catch(const std::exception&)
538 uint256 txHash = tx.GetHash();
540 // Check if the transaction is already in the blockchain
541 CTransaction existingTx;
542 uint256 blockHash = 0;
543 if(GetTransaction(txHash, existingTx, blockHash))
549 // Send the transaction to the local node
551 if(!tx.AcceptToMemoryPool(txdb, false))
553 SyncWithWallets(tx, NULL, true);
554 //(CInv(MSG_TX, txHash), tx);
555 RelayTransaction(tx, txHash);
558 MultisigInputEntry * MultisigDialog::addInput()
560 MultisigInputEntry *entry = new MultisigInputEntry(this);
562 entry->setModel(model);
563 ui->inputs->addWidget(entry);
564 connect(entry, SIGNAL(removeEntry(MultisigInputEntry *)), this, SLOT(removeEntry(MultisigInputEntry *)));
565 connect(entry, SIGNAL(updateAmount()), this, SLOT(updateAmounts()));
566 updateRemoveEnabled();
568 ui->scrollAreaWidgetContents_2->resize(ui->scrollAreaWidgetContents_2->sizeHint());
569 QScrollBar *bar = ui->scrollArea_2->verticalScrollBar();
571 bar->setSliderPosition(bar->maximum());
576 void MultisigDialog::removeEntry(MultisigInputEntry *entry)
579 updateRemoveEnabled();
582 SendCoinsEntry * MultisigDialog::addOutput()
584 SendCoinsEntry *entry = new SendCoinsEntry(this);
586 entry->setModel(model);
587 ui->outputs->addWidget(entry);
588 connect(entry, SIGNAL(removeEntry(SendCoinsEntry *)), this, SLOT(removeEntry(SendCoinsEntry *)));
589 connect(entry, SIGNAL(payAmountChanged()), this, SLOT(updateAmounts()));
590 updateRemoveEnabled();
592 ui->scrollAreaWidgetContents_3->resize(ui->scrollAreaWidgetContents_3->sizeHint());
593 QScrollBar *bar = ui->scrollArea_3->verticalScrollBar();
595 bar->setSliderPosition(bar->maximum());
600 void MultisigDialog::removeEntry(SendCoinsEntry *entry)
603 updateRemoveEnabled();
606 void MultisigDialog::updateAmounts()
608 // Update inputs amount
609 int64_t inputsAmount = 0;
610 for(int i = 0; i < ui->inputs->count(); i++)
612 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
614 inputsAmount += entry->getAmount();
616 QString inputsAmountStr;
617 inputsAmountStr.sprintf("%.6f", (double) inputsAmount / COIN);
618 ui->inputsAmount->setText(inputsAmountStr);
620 // Update outputs amount
621 int64_t outputsAmount = 0;
622 for(int i = 0; i < ui->outputs->count(); i++)
624 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
626 outputsAmount += entry->getValue().amount;
628 QString outputsAmountStr;
629 outputsAmountStr.sprintf("%.6f", (double) outputsAmount / COIN);
630 ui->outputsAmount->setText(outputsAmountStr);
633 int64_t fee = inputsAmount - outputsAmount;
635 feeStr.sprintf("%.6f", (double) fee / COIN);
636 ui->fee->setText(feeStr);
639 void MultisigDialog::keyPressEvent(QKeyEvent *event)
642 if(windowType() != Qt::Widget && event->key() == Qt::Key_Back)
647 if(windowType() != Qt::Widget && event->key() == Qt::Key_Escape)