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()));
50 connect(ui->addInputButton, SIGNAL(clicked()), this, SLOT(addInput()));
51 connect(ui->addOutputButton, SIGNAL(clicked()), this, SLOT(addOutput()));
53 ui->signTransactionButton->setEnabled(false);
54 ui->sendTransactionButton->setEnabled(false);
57 MultisigDialog::~MultisigDialog()
62 void MultisigDialog::setModel(WalletModel *model)
66 for(int i = 0; i < ui->pubkeyEntries->count(); i++)
68 MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget());
70 entry->setModel(model);
74 for(int i = 0; i < ui->inputs->count(); i++)
76 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
78 entry->setModel(model);
82 for(int i = 0; i < ui->outputs->count(); i++)
84 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
86 entry->setModel(model);
90 void MultisigDialog::updateRemoveEnabled()
92 bool enabled = (ui->pubkeyEntries->count() > 2);
94 for(int i = 0; i < ui->pubkeyEntries->count(); i++)
96 MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget());
98 entry->setRemoveEnabled(enabled);
102 maxSigsStr.setNum(ui->pubkeyEntries->count());
103 ui->maxSignaturesLabel->setText(QString("/ ") + maxSigsStr);
106 enabled = (ui->inputs->count() > 1);
107 for(int i = 0; i < ui->inputs->count(); i++)
109 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
111 entry->setRemoveEnabled(enabled);
115 enabled = (ui->outputs->count() > 1);
116 for(int i = 0; i < ui->outputs->count(); i++)
118 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
120 entry->setRemoveEnabled(enabled);
124 void MultisigDialog::on_createAddressButton_clicked()
126 ui->multisigAddress->clear();
127 ui->redeemScript->clear();
132 std::vector<CKey> pubkeys;
133 pubkeys.resize(ui->pubkeyEntries->count());
134 unsigned int required = ui->requiredSignatures->text().toUInt();
136 for(int i = 0; i < ui->pubkeyEntries->count(); i++)
138 MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget());
139 if(!entry->validate())
141 QString str = entry->getPubkey();
142 CPubKey vchPubKey(ParseHex(str.toStdString().c_str()));
143 if(!vchPubKey.IsValid())
145 pubkeys[i].SetPubKey(vchPubKey);
148 if((required == 0) || (required > pubkeys.size()))
152 script.SetMultisig(required, pubkeys);
153 CScriptID scriptID = script.GetID();
154 CBitcoinAddress address(scriptID);
156 ui->multisigAddress->setText(address.ToString().c_str());
157 ui->redeemScript->setText(HexStr(script.begin(), script.end()).c_str());
160 void MultisigDialog::on_copyMultisigAddressButton_clicked()
162 QApplication::clipboard()->setText(ui->multisigAddress->text());
165 void MultisigDialog::on_copyRedeemScriptButton_clicked()
167 QApplication::clipboard()->setText(ui->redeemScript->text());
170 void MultisigDialog::on_saveRedeemScriptButton_clicked()
175 CWallet *wallet = model->getWallet();
176 std::string redeemScript = ui->redeemScript->text().toStdString();
177 std::vector<unsigned char> scriptData(ParseHex(redeemScript));
178 CScript script(scriptData.begin(), scriptData.end());
179 CScriptID scriptID = script.GetID();
181 LOCK(wallet->cs_wallet);
182 if(!wallet->HaveCScript(scriptID))
183 wallet->AddCScript(script);
186 void MultisigDialog::on_saveMultisigAddressButton_clicked()
191 CWallet *wallet = model->getWallet();
192 std::string redeemScript = ui->redeemScript->text().toStdString();
193 std::string address = ui->multisigAddress->text().toStdString();
194 std::string label("multisig");
196 if(!model->validateAddress(QString(address.c_str())))
199 std::vector<unsigned char> scriptData(ParseHex(redeemScript));
200 CScript script(scriptData.begin(), scriptData.end());
201 CScriptID scriptID = script.GetID();
203 LOCK(wallet->cs_wallet);
204 if(!wallet->HaveCScript(scriptID))
205 wallet->AddCScript(script);
206 if(!wallet->mapAddressBook.count(CBitcoinAddress(address).Get()))
207 wallet->SetAddressBookName(CBitcoinAddress(address).Get(), label);
210 void MultisigDialog::clear()
212 while(ui->pubkeyEntries->count())
213 delete ui->pubkeyEntries->takeAt(0)->widget();
217 updateRemoveEnabled();
220 MultisigAddressEntry * MultisigDialog::addPubKey()
222 MultisigAddressEntry *entry = new MultisigAddressEntry(this);
224 entry->setModel(model);
225 ui->pubkeyEntries->addWidget(entry);
226 connect(entry, SIGNAL(removeEntry(MultisigAddressEntry *)), this, SLOT(removeEntry(MultisigAddressEntry *)));
227 updateRemoveEnabled();
229 ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
230 QScrollBar *bar = ui->scrollArea->verticalScrollBar();
232 bar->setSliderPosition(bar->maximum());
237 void MultisigDialog::removeEntry(MultisigAddressEntry *entry)
240 updateRemoveEnabled();
243 void MultisigDialog::on_createTransactionButton_clicked()
245 CTransaction transaction;
248 for(int i = 0; i < ui->inputs->count(); i++)
250 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
253 if(entry->validate())
255 CTxIn input = entry->getInput();
256 transaction.vin.push_back(input);
264 for(int i = 0; i < ui->outputs->count(); i++)
266 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
270 if(entry->validate())
272 SendCoinsRecipient recipient = entry->getValue();
273 CBitcoinAddress address(recipient.address.toStdString());
274 CScript scriptPubKey;
275 scriptPubKey.SetDestination(address.Get());
276 int64 amount = recipient.amount;
277 CTxOut output(amount, scriptPubKey);
278 transaction.vout.push_back(output);
285 CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
287 ui->transaction->setText(HexStr(ss.begin(), ss.end()).c_str());
290 void MultisigDialog::on_transaction_textChanged()
292 while(ui->inputs->count())
293 delete ui->inputs->takeAt(0)->widget();
294 while(ui->outputs->count())
295 delete ui->outputs->takeAt(0)->widget();
297 if(ui->transaction->text().size() > 0)
298 ui->signTransactionButton->setEnabled(true);
300 ui->signTransactionButton->setEnabled(false);
302 // Decode the raw transaction
303 std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString()));
304 CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION);
310 catch(std::exception &e)
317 BOOST_FOREACH(const CTxIn& txin, tx.vin)
319 uint256 prevoutHash = txin.prevout.hash;
322 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(index)->widget());
325 entry->setTransactionId(QString(prevoutHash.GetHex().c_str()));
326 entry->setTransactionOutputIndex(txin.prevout.n);
332 BOOST_FOREACH(const CTxOut& txout, tx.vout)
334 CScript scriptPubKey = txout.scriptPubKey;
336 ExtractDestination(scriptPubKey, addr);
337 CBitcoinAddress address(addr);
338 SendCoinsRecipient recipient;
339 recipient.address = QString(address.ToString().c_str());
340 recipient.amount = txout.nValue;
343 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(index)->widget());
346 entry->setValue(recipient);
350 updateRemoveEnabled();
353 void MultisigDialog::on_copyTransactionButton_clicked()
355 QApplication::clipboard()->setText(ui->transaction->text());
358 void MultisigDialog::on_pasteTransactionButton_clicked()
360 ui->transaction->setText(QApplication::clipboard()->text());
363 void MultisigDialog::on_signTransactionButton_clicked()
365 ui->signedTransaction->clear();
370 CWallet *wallet = model->getWallet();
372 // Decode the raw transaction
373 std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString()));
374 CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION);
380 catch(std::exception &e)
384 CTransaction mergedTx(tx);
386 // Fetch previous transactions (inputs)
387 std::map<COutPoint, CScript> mapPrevOut;
388 for(int i = 0; i < mergedTx.vin.size(); i++)
393 std::map<uint256, CTxIndex> unused;
396 tempTx.vin.push_back(mergedTx.vin[i]);
397 tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid);
399 BOOST_FOREACH(const CTxIn& txin, tempTx.vin)
401 const uint256& prevHash = txin.prevout.hash;
402 if(mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size() > txin.prevout.n)
403 mapPrevOut[txin.prevout] = mapPrevTx[prevHash].second.vout[txin.prevout.n].scriptPubKey;
407 // Add the redeem scripts to the wallet keystore
408 for(int i = 0; i < ui->inputs->count(); i++)
410 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
413 QString redeemScriptStr = entry->getRedeemScript();
414 if(redeemScriptStr.size() > 0)
416 std::vector<unsigned char> scriptData(ParseHex(redeemScriptStr.toStdString()));
417 CScript redeemScript(scriptData.begin(), scriptData.end());
418 wallet->AddCScript(redeemScript);
423 WalletModel::UnlockContext ctx(model->requestUnlock());
428 bool fComplete = true;
429 for(int i = 0; i < mergedTx.vin.size(); i++)
431 CTxIn& txin = mergedTx.vin[i];
432 if(mapPrevOut.count(txin.prevout) == 0)
437 const CScript& prevPubKey = mapPrevOut[txin.prevout];
439 txin.scriptSig.clear();
440 SignSignature(*wallet, prevPubKey, mergedTx, i, SIGHASH_ALL);
441 txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, tx.vin[i].scriptSig);
442 if(!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, 0))
448 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
450 ui->signedTransaction->setText(HexStr(ssTx.begin(), ssTx.end()).c_str());
454 ui->statusLabel->setText(tr("Transaction signature is complete"));
455 ui->sendTransactionButton->setEnabled(true);
459 ui->statusLabel->setText(tr("Transaction is NOT completely signed"));
460 ui->sendTransactionButton->setEnabled(false);
464 void MultisigDialog::on_copySignedTransactionButton_clicked()
466 QApplication::clipboard()->setText(ui->signedTransaction->text());
469 void MultisigDialog::on_sendTransactionButton_clicked()
471 int64 transactionSize = ui->signedTransaction->text().size() / 2;
472 if(transactionSize == 0)
476 int64 fee = (int64 ) (ui->fee->text().toDouble() * COIN);
477 int64 minFee = MIN_TX_FEE * (1 + (int64) transactionSize / 1000);
480 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);
481 if(ret != QMessageBox::Yes)
484 else if(fee > minFee)
486 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);
487 if(ret != QMessageBox::Yes)
491 // Decode the raw transaction
492 std::vector<unsigned char> txData(ParseHex(ui->signedTransaction->text().toStdString()));
493 CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
499 catch(std::exception &e)
503 uint256 txHash = tx.GetHash();
505 // Check if the transaction is already in the blockchain
506 CTransaction existingTx;
507 uint256 blockHash = 0;
508 if(GetTransaction(txHash, existingTx, blockHash))
514 // Send the transaction to the local node
516 if(!tx.AcceptToMemoryPool(txdb, false))
518 SyncWithWallets(tx, NULL, true);
519 //(CInv(MSG_TX, txHash), tx);
520 RelayTransaction(tx, txHash);
523 MultisigInputEntry * MultisigDialog::addInput()
525 MultisigInputEntry *entry = new MultisigInputEntry(this);
527 entry->setModel(model);
528 ui->inputs->addWidget(entry);
529 connect(entry, SIGNAL(removeEntry(MultisigInputEntry *)), this, SLOT(removeEntry(MultisigInputEntry *)));
530 connect(entry, SIGNAL(updateAmount()), this, SLOT(updateAmounts()));
531 updateRemoveEnabled();
533 ui->scrollAreaWidgetContents_2->resize(ui->scrollAreaWidgetContents_2->sizeHint());
534 QScrollBar *bar = ui->scrollArea_2->verticalScrollBar();
536 bar->setSliderPosition(bar->maximum());
541 void MultisigDialog::removeEntry(MultisigInputEntry *entry)
544 updateRemoveEnabled();
547 SendCoinsEntry * MultisigDialog::addOutput()
549 SendCoinsEntry *entry = new SendCoinsEntry(this);
551 entry->setModel(model);
552 ui->outputs->addWidget(entry);
553 connect(entry, SIGNAL(removeEntry(SendCoinsEntry *)), this, SLOT(removeEntry(SendCoinsEntry *)));
554 connect(entry, SIGNAL(payAmountChanged()), this, SLOT(updateAmounts()));
555 updateRemoveEnabled();
557 ui->scrollAreaWidgetContents_3->resize(ui->scrollAreaWidgetContents_3->sizeHint());
558 QScrollBar *bar = ui->scrollArea_3->verticalScrollBar();
560 bar->setSliderPosition(bar->maximum());
565 void MultisigDialog::removeEntry(SendCoinsEntry *entry)
568 updateRemoveEnabled();
571 void MultisigDialog::updateAmounts()
573 // Update inputs amount
574 int64 inputsAmount = 0;
575 for(int i = 0; i < ui->inputs->count(); i++)
577 MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget());
579 inputsAmount += entry->getAmount();
581 QString inputsAmountStr;
582 inputsAmountStr.sprintf("%.6f", (double) inputsAmount / COIN);
583 ui->inputsAmount->setText(inputsAmountStr);
585 // Update outputs amount
586 int64 outputsAmount = 0;
587 for(int i = 0; i < ui->outputs->count(); i++)
589 SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget());
591 outputsAmount += entry->getValue().amount;
593 QString outputsAmountStr;
594 outputsAmountStr.sprintf("%.6f", (double) outputsAmount / COIN);
595 ui->outputsAmount->setText(outputsAmountStr);
598 int64 fee = inputsAmount - outputsAmount;
600 feeStr.sprintf("%.6f", (double) fee / COIN);
601 ui->fee->setText(feeStr);