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 if (script.size() > MAX_SCRIPT_ELEMENT_SIZE)
174 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);
177 CScriptID scriptID = script.GetID();
178 CBitcoinAddress address(scriptID);
180 ui->multisigAddress->setText(address.ToString().c_str());
181 ui->redeemScript->setText(HexStr(script.begin(), script.end()).c_str());
184 void MultisigDialog::on_copyMultisigAddressButton_clicked()
186 QApplication::clipboard()->setText(ui->multisigAddress->text());
189 void MultisigDialog::on_copyRedeemScriptButton_clicked()
191 QApplication::clipboard()->setText(ui->redeemScript->text());
194 void MultisigDialog::on_saveRedeemScriptButton_clicked()
199 CWallet *wallet = model->getWallet();
200 std::string redeemScript = ui->redeemScript->text().toStdString();
201 std::vector<unsigned char> scriptData(ParseHex(redeemScript));
202 CScript script(scriptData.begin(), scriptData.end());
203 CScriptID scriptID = script.GetID();
205 LOCK(wallet->cs_wallet);
206 if(!wallet->HaveCScript(scriptID))
207 wallet->AddCScript(script);
210 void MultisigDialog::on_saveMultisigAddressButton_clicked()
215 CWallet *wallet = model->getWallet();
216 std::string redeemScript = ui->redeemScript->text().toStdString();
217 std::string address = ui->multisigAddress->text().toStdString();
218 std::string label("multisig");
220 if(!model->validateAddress(QString(address.c_str())))
223 std::vector<unsigned char> scriptData(ParseHex(redeemScript));
224 CScript script(scriptData.begin(), scriptData.end());
225 CScriptID scriptID = script.GetID();
227 LOCK(wallet->cs_wallet);
228 if(!wallet->HaveCScript(scriptID))
229 wallet->AddCScript(script);
230 if(!wallet->mapAddressBook.count(CBitcoinAddress(address).Get()))
231 wallet->SetAddressBookName(CBitcoinAddress(address).Get(), label);
234 void MultisigDialog::clear()
236 while(ui->pubkeyEntries->count())
237 delete ui->pubkeyEntries->takeAt(0)->widget();
241 updateRemoveEnabled();
244 MultisigAddressEntry * MultisigDialog::addPubKey()
246 MultisigAddressEntry *entry = new MultisigAddressEntry(this);
248 entry->setModel(model);
249 ui->pubkeyEntries->addWidget(entry);
250 connect(entry, SIGNAL(removeEntry(MultisigAddressEntry *)), this, SLOT(removeEntry(MultisigAddressEntry *)));
251 updateRemoveEnabled();
253 ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
254 QScrollBar *bar = ui->scrollArea->verticalScrollBar();
256 bar->setSliderPosition(bar->maximum());
261 void MultisigDialog::removeEntry(MultisigAddressEntry *entry)
264 updateRemoveEnabled();
267 void MultisigDialog::on_createTransactionButton_clicked()
269 CTransaction transaction;
272 for(int i = 0; i < ui->inputs->count(); i++)
274 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
277 if(entry->validate())
279 CTxIn input = entry->getInput();
280 transaction.vin.push_back(input);
288 for(int i = 0; i < ui->outputs->count(); i++)
290 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
294 if(entry->validate())
296 SendCoinsRecipient recipient = entry->getValue();
297 CBitcoinAddress address(recipient.address.toStdString());
298 CScript scriptPubKey;
299 scriptPubKey.SetDestination(address.Get());
300 int64_t amount = recipient.amount;
301 CTxOut output(amount, scriptPubKey);
302 transaction.vout.push_back(output);
309 CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
311 ui->transaction->setText(HexStr(ss.begin(), ss.end()).c_str());
314 void MultisigDialog::on_transaction_textChanged()
316 while(ui->inputs->count())
317 delete ui->inputs->takeAt(0)->widget();
318 while(ui->outputs->count())
319 delete ui->outputs->takeAt(0)->widget();
321 if(ui->transaction->text().size() > 0)
322 ui->signTransactionButton->setEnabled(true);
324 ui->signTransactionButton->setEnabled(false);
326 // Decode the raw transaction
327 std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString()));
328 CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION);
334 catch(std::exception &e)
341 BOOST_FOREACH(const CTxIn& txin, tx.vin)
343 uint256 prevoutHash = txin.prevout.hash;
346 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(index)->widget());
349 entry->setTransactionId(QString(prevoutHash.GetHex().c_str()));
350 entry->setTransactionOutputIndex(txin.prevout.n);
356 BOOST_FOREACH(const CTxOut& txout, tx.vout)
358 CScript scriptPubKey = txout.scriptPubKey;
360 ExtractDestination(scriptPubKey, addr);
361 CBitcoinAddress address(addr);
362 SendCoinsRecipient recipient;
363 recipient.address = QString(address.ToString().c_str());
364 recipient.amount = txout.nValue;
367 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(index)->widget());
370 entry->setValue(recipient);
374 updateRemoveEnabled();
377 void MultisigDialog::on_copyTransactionButton_clicked()
379 QApplication::clipboard()->setText(ui->transaction->text());
382 void MultisigDialog::on_pasteTransactionButton_clicked()
384 ui->transaction->setText(QApplication::clipboard()->text());
387 void MultisigDialog::on_signTransactionButton_clicked()
389 ui->signedTransaction->clear();
394 CWallet *wallet = model->getWallet();
396 // Decode the raw transaction
397 std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString()));
398 CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION);
404 catch(std::exception &e)
408 CTransaction mergedTx(tx);
410 // Fetch previous transactions (inputs)
411 std::map<COutPoint, CScript> mapPrevOut;
412 for(unsigned int i = 0; i < mergedTx.vin.size(); i++)
417 std::map<uint256, CTxIndex> unused;
420 tempTx.vin.push_back(mergedTx.vin[i]);
421 tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid);
423 BOOST_FOREACH(const CTxIn& txin, tempTx.vin)
425 const uint256& prevHash = txin.prevout.hash;
426 if(mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size() > txin.prevout.n)
427 mapPrevOut[txin.prevout] = mapPrevTx[prevHash].second.vout[txin.prevout.n].scriptPubKey;
431 // Add the redeem scripts to the wallet keystore
432 for(int i = 0; i < ui->inputs->count(); i++)
434 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
437 QString redeemScriptStr = entry->getRedeemScript();
438 if(redeemScriptStr.size() > 0)
440 std::vector<unsigned char> scriptData(ParseHex(redeemScriptStr.toStdString()));
441 CScript redeemScript(scriptData.begin(), scriptData.end());
442 wallet->AddCScript(redeemScript);
447 WalletModel::UnlockContext ctx(model->requestUnlock());
452 bool fComplete = true;
453 for(unsigned int i = 0; i < mergedTx.vin.size(); i++)
455 CTxIn& txin = mergedTx.vin[i];
456 if(mapPrevOut.count(txin.prevout) == 0)
461 const CScript& prevPubKey = mapPrevOut[txin.prevout];
463 txin.scriptSig.clear();
464 SignSignature(*wallet, prevPubKey, mergedTx, i, SIGHASH_ALL);
465 txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, tx.vin[i].scriptSig);
466 if(!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, 0))
472 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
474 ui->signedTransaction->setText(HexStr(ssTx.begin(), ssTx.end()).c_str());
478 ui->statusLabel->setText(tr("Transaction signature is complete"));
479 ui->sendTransactionButton->setEnabled(true);
483 ui->statusLabel->setText(tr("Transaction is NOT completely signed"));
484 ui->sendTransactionButton->setEnabled(false);
488 void MultisigDialog::on_copySignedTransactionButton_clicked()
490 QApplication::clipboard()->setText(ui->signedTransaction->text());
493 void MultisigDialog::on_sendTransactionButton_clicked()
495 int64_t transactionSize = ui->signedTransaction->text().size() / 2;
496 if(transactionSize == 0)
500 int64_t fee = (int64_t ) (ui->fee->text().toDouble() * COIN);
501 int64_t minFee = MIN_TX_FEE * (1 + (int64_t) transactionSize / 1000);
504 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);
505 if(ret != QMessageBox::Yes)
508 else if(fee > minFee)
510 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);
511 if(ret != QMessageBox::Yes)
515 // Decode the raw transaction
516 std::vector<unsigned char> txData(ParseHex(ui->signedTransaction->text().toStdString()));
517 CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
523 catch(std::exception &e)
527 uint256 txHash = tx.GetHash();
529 // Check if the transaction is already in the blockchain
530 CTransaction existingTx;
531 uint256 blockHash = 0;
532 if(GetTransaction(txHash, existingTx, blockHash))
538 // Send the transaction to the local node
540 if(!tx.AcceptToMemoryPool(txdb, false))
542 SyncWithWallets(tx, NULL, true);
543 //(CInv(MSG_TX, txHash), tx);
544 RelayTransaction(tx, txHash);
547 MultisigInputEntry * MultisigDialog::addInput()
549 MultisigInputEntry *entry = new MultisigInputEntry(this);
551 entry->setModel(model);
552 ui->inputs->addWidget(entry);
553 connect(entry, SIGNAL(removeEntry(MultisigInputEntry *)), this, SLOT(removeEntry(MultisigInputEntry *)));
554 connect(entry, SIGNAL(updateAmount()), this, SLOT(updateAmounts()));
555 updateRemoveEnabled();
557 ui->scrollAreaWidgetContents_2->resize(ui->scrollAreaWidgetContents_2->sizeHint());
558 QScrollBar *bar = ui->scrollArea_2->verticalScrollBar();
560 bar->setSliderPosition(bar->maximum());
565 void MultisigDialog::removeEntry(MultisigInputEntry *entry)
568 updateRemoveEnabled();
571 SendCoinsEntry * MultisigDialog::addOutput()
573 SendCoinsEntry *entry = new SendCoinsEntry(this);
575 entry->setModel(model);
576 ui->outputs->addWidget(entry);
577 connect(entry, SIGNAL(removeEntry(SendCoinsEntry *)), this, SLOT(removeEntry(SendCoinsEntry *)));
578 connect(entry, SIGNAL(payAmountChanged()), this, SLOT(updateAmounts()));
579 updateRemoveEnabled();
581 ui->scrollAreaWidgetContents_3->resize(ui->scrollAreaWidgetContents_3->sizeHint());
582 QScrollBar *bar = ui->scrollArea_3->verticalScrollBar();
584 bar->setSliderPosition(bar->maximum());
589 void MultisigDialog::removeEntry(SendCoinsEntry *entry)
592 updateRemoveEnabled();
595 void MultisigDialog::updateAmounts()
597 // Update inputs amount
598 int64_t inputsAmount = 0;
599 for(int i = 0; i < ui->inputs->count(); i++)
601 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
603 inputsAmount += entry->getAmount();
605 QString inputsAmountStr;
606 inputsAmountStr.sprintf("%.6f", (double) inputsAmount / COIN);
607 ui->inputsAmount->setText(inputsAmountStr);
609 // Update outputs amount
610 int64_t outputsAmount = 0;
611 for(int i = 0; i < ui->outputs->count(); i++)
613 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
615 outputsAmount += entry->getValue().amount;
617 QString outputsAmountStr;
618 outputsAmountStr.sprintf("%.6f", (double) outputsAmount / COIN);
619 ui->outputsAmount->setText(outputsAmountStr);
622 int64_t fee = inputsAmount - outputsAmount;
624 feeStr.sprintf("%.6f", (double) fee / COIN);
625 ui->fee->setText(feeStr);