void MultisigDialog::updateAmounts() { // Update inputs amount int64_t inputsAmount = 0; for(int i = 0; i < ui->inputs->count(); i++) { MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget()); if(entry) inputsAmount += entry->getAmount(); } QString inputsAmountStr; inputsAmountStr.sprintf("%.6f", (double) inputsAmount / COIN); ui->inputsAmount->setText(inputsAmountStr); // Update outputs amount int64_t outputsAmount = 0; for(int i = 0; i < ui->outputs->count(); i++) { SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget()); if(entry) outputsAmount += entry->getValue().amount; } QString outputsAmountStr; outputsAmountStr.sprintf("%.6f", (double) outputsAmount / COIN); ui->outputsAmount->setText(outputsAmountStr); // Update Fee amount int64_t fee = inputsAmount - outputsAmount; QString feeStr; feeStr.sprintf("%.6f", (double) fee / COIN); ui->fee->setText(feeStr); }
void MultisigDialog::setModel(WalletModel *model) { this->model = model; for(int i = 0; i < ui->pubkeyEntries->count(); i++) { MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget()); if(entry) entry->setModel(model); } for(int i = 0; i < ui->inputs->count(); i++) { MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget()); if(entry) entry->setModel(model); } for(int i = 0; i < ui->outputs->count(); i++) { SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget()); if(entry) entry->setModel(model); } }
void MultisigDialog::updateRemoveEnabled() { bool enabled = (ui->pubkeyEntries->count() > 2); for(int i = 0; i < ui->pubkeyEntries->count(); i++) { MultisigAddressEntry *entry = qobject_cast<MultisigAddressEntry *>(ui->pubkeyEntries->itemAt(i)->widget()); if(entry) entry->setRemoveEnabled(enabled); } QString maxSigsStr; maxSigsStr.setNum(ui->pubkeyEntries->count()); ui->maxSignaturesLabel->setText(QString("/ ") + maxSigsStr); enabled = (ui->inputs->count() > 1); for(int i = 0; i < ui->inputs->count(); i++) { MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget()); if(entry) entry->setRemoveEnabled(enabled); } enabled = (ui->outputs->count() > 1); for(int i = 0; i < ui->outputs->count(); i++) { SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget()); if(entry) entry->setRemoveEnabled(enabled); } }
MultisigInputEntry * MultisigDialog::addInput() { MultisigInputEntry *entry = new MultisigInputEntry(this); entry->setModel(model); ui->inputs->addWidget(entry); connect(entry, SIGNAL(removeEntry(MultisigInputEntry *)), this, SLOT(removeEntry(MultisigInputEntry *))); connect(entry, SIGNAL(updateAmount()), this, SLOT(updateAmounts())); updateRemoveEnabled(); entry->clear(); ui->scrollAreaWidgetContents_2->resize(ui->scrollAreaWidgetContents_2->sizeHint()); QScrollBar *bar = ui->scrollArea_2->verticalScrollBar(); if(bar) bar->setSliderPosition(bar->maximum()); return entry; }
void MultisigDialog::on_createTransactionButton_clicked() { CTransaction transaction; // Get inputs for(int i = 0; i < ui->inputs->count(); i++) { MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget()); if(entry) { if(entry->validate()) { CTxIn input = entry->getInput(); transaction.vin.push_back(input); } else return; } } // Get outputs for(int i = 0; i < ui->outputs->count(); i++) { SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(i)->widget()); if(entry) { if(entry->validate()) { SendCoinsRecipient recipient = entry->getValue(); CBitcoinAddress address(recipient.address.toStdString()); CScript scriptPubKey; scriptPubKey.SetDestination(address.Get()); int64_t amount = recipient.amount; CTxOut output(amount, scriptPubKey); transaction.vout.push_back(output); } else return; } } CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss << transaction; ui->transaction->setText(HexStr(ss.begin(), ss.end()).c_str()); }
void MultisigDialog::on_signTransactionButton_clicked() { ui->signedTransaction->clear(); if(!model) return; CWallet *wallet = model->getWallet(); // Decode the raw transaction std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString())); CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION); CTransaction tx; try { ss >> tx; } catch(std::exception &e) { (void)e; return; } CTransaction mergedTx(tx); // Fetch previous transactions (inputs) std::map<COutPoint, CScript> mapPrevOut; for(unsigned int i = 0; i < mergedTx.vin.size(); i++) { CTransaction tempTx; MapPrevTx mapPrevTx; CTxDB txdb("r"); std::map<uint256, CTxIndex> unused; bool fInvalid; tempTx.vin.push_back(mergedTx.vin[i]); tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid); BOOST_FOREACH(const CTxIn& txin, tempTx.vin) { const uint256& prevHash = txin.prevout.hash; if(mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size() > txin.prevout.n) mapPrevOut[txin.prevout] = mapPrevTx[prevHash].second.vout[txin.prevout.n].scriptPubKey; } } // Add the redeem scripts to the wallet keystore for(int i = 0; i < ui->inputs->count(); i++) { MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget()); if(entry) { QString redeemScriptStr = entry->getRedeemScript(); if(redeemScriptStr.size() > 0) { std::vector<unsigned char> scriptData(ParseHex(redeemScriptStr.toStdString())); CScript redeemScript(scriptData.begin(), scriptData.end()); wallet->AddCScript(redeemScript); } } } WalletModel::UnlockContext ctx(model->requestUnlock()); if(!ctx.isValid()) return; // Sign what we can bool fComplete = true; for(unsigned int i = 0; i < mergedTx.vin.size(); i++) { CTxIn& txin = mergedTx.vin[i]; if(mapPrevOut.count(txin.prevout) == 0) { fComplete = false; continue; } const CScript& prevPubKey = mapPrevOut[txin.prevout]; txin.scriptSig.clear(); SignSignature(*wallet, prevPubKey, mergedTx, i, SIGHASH_ALL); txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, tx.vin[i].scriptSig); if(!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, 0)) { fComplete = false; } } CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << mergedTx; ui->signedTransaction->setText(HexStr(ssTx.begin(), ssTx.end()).c_str()); if(fComplete) { ui->statusLabel->setText(tr("Transaction signature is complete")); ui->sendTransactionButton->setEnabled(true); } else { ui->statusLabel->setText(tr("Transaction is NOT completely signed")); ui->sendTransactionButton->setEnabled(false); } }
void MultisigDialog::on_transaction_textChanged() { while(ui->inputs->count()) delete ui->inputs->takeAt(0)->widget(); while(ui->outputs->count()) delete ui->outputs->takeAt(0)->widget(); if(ui->transaction->text().size() > 0) ui->signTransactionButton->setEnabled(true); else ui->signTransactionButton->setEnabled(false); // Decode the raw transaction std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString())); CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION); CTransaction tx; try { ss >> tx; } catch(std::exception &e) { (void)e; return; } // Fill input list int index = -1; BOOST_FOREACH(const CTxIn& txin, tx.vin) { uint256 prevoutHash = txin.prevout.hash; addInput(); index++; MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(index)->widget()); if(entry) { entry->setTransactionId(QString(prevoutHash.GetHex().c_str())); entry->setTransactionOutputIndex(txin.prevout.n); } } // Fill output list index = -1; BOOST_FOREACH(const CTxOut& txout, tx.vout) { CScript scriptPubKey = txout.scriptPubKey; CTxDestination addr; ExtractDestination(scriptPubKey, addr); CBitcoinAddress address(addr); SendCoinsRecipient recipient; recipient.address = QString(address.ToString().c_str()); recipient.amount = txout.nValue; addOutput(); index++; SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(index)->widget()); if(entry) { entry->setValue(recipient); } } updateRemoveEnabled(); }
void MultisigDialog::on_transaction_textChanged() { ui->fee->setStyleSheet(""); ui->statusLabel->setText(""); while(ui->inputs->count()) delete ui->inputs->takeAt(0)->widget(); while(ui->outputs->count()) delete ui->outputs->takeAt(0)->widget(); if(ui->transaction->text().size() > 0) ui->signTransactionButton->setEnabled(true); else ui->signTransactionButton->setEnabled(false); // Decode the raw transaction std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString())); CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION); CTransaction tx; try { ss >> tx; } catch(std::exception &e) { return; } // Fill input list int index = -1; BOOST_FOREACH(const CTxIn& txin, tx.vin) { uint256 prevoutHash = txin.prevout.hash; addInput(); index++; MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(index)->widget()); if(entry) { entry->setTransactionId(QString(prevoutHash.GetHex().c_str())); entry->setTransactionOutputIndex(txin.prevout.n); } } // Fill output list index = -1; BOOST_FOREACH(const CTxOut& txout, tx.vout) { CScript scriptPubKey = txout.scriptPubKey; CTxDestination addr; ExtractDestination(scriptPubKey, addr); CBitcoinAddress address(addr); SendCoinsRecipient recipient; recipient.address = QString(address.ToString().c_str()); recipient.amount = txout.nValue; addOutput(); index++; SendCoinsEntry *entry = qobject_cast<SendCoinsEntry *>(ui->outputs->itemAt(index)->widget()); if(entry) { entry->setValue(recipient); } } updateRemoveEnabled(); // Check the fee int64_t transactionSize = ui->transaction->text().size() / 2; if(transactionSize == 0) return; transactionSize += ui->inputs->count() * 73; // Future ECDSA signatures in DER format int64_t fee = (int64_t ) (ui->fee->text().toDouble() * COIN); int64_t minFee = MIN_TX_FEE * (1 + (int64_t) transactionSize / 1000); if(fee < minFee) { ui->fee->setStyleSheet("color:red;"); ui->statusLabel->setText(tr("The transaction fee might be too small.")); } else if(fee > minFee) { ui->fee->setStyleSheet("color:red;"); ui->statusLabel->setText(tr("The transaction fee might be too big. Don't forget to add an output for the change address.")); } }
void MultisigDialog::on_signTransactionButton_clicked() { ui->signedTransaction->clear(); if(!model) return; CWallet *wallet = model->getWallet(); // Decode the raw transaction std::vector<unsigned char> txData(ParseHex(ui->transaction->text().toStdString())); CDataStream ss(txData, SER_NETWORK, PROTOCOL_VERSION); CTransaction tx; try { ss >> tx; } catch(std::exception &e) { return; } CTransaction mergedTx(tx); // Fetch previous transactions (inputs) // duplicated in rpcrawtransaction.cpp:389 CCoinsView viewDummy; CCoinsViewCache view(viewDummy); { LOCK(mempool.cs); CCoinsViewCache &viewChain = *pcoinsTip; CCoinsViewMemPool viewMempool(viewChain, mempool); view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) { const uint256& prevHash = txin.prevout.hash; CCoins coins; view.GetCoins(prevHash, coins); // this is certainly allowed to fail } view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long } // Add the redeem scripts to the wallet keystore for(int i = 0; i < ui->inputs->count(); i++) { MultisigInputEntry *entry = qobject_cast<MultisigInputEntry *>(ui->inputs->itemAt(i)->widget()); if(entry) { QString redeemScriptStr = entry->getRedeemScript(); if(redeemScriptStr.size() > 0) { std::vector<unsigned char> scriptData(ParseHex(redeemScriptStr.toStdString())); CScript redeemScript(scriptData.begin(), scriptData.end()); wallet->AddCScript(redeemScript); } } } WalletModel::UnlockContext ctx(model->requestUnlock()); if(!ctx.isValid()) return; // Sign what we can: // mostly like rpcrawtransaction:503 bool fComplete = true; // Sign what we can: for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { CTxIn& txin = mergedTx.vin[i]; CCoins coins; if (!view.GetCoins(txin.prevout.hash, coins) || !coins.IsAvailable(txin.prevout.n)) { fComplete = false; continue; } const CScript& prevPubKey = coins.vout[txin.prevout.n].scriptPubKey; txin.scriptSig.clear(); SignSignature(*wallet, prevPubKey, mergedTx, i, SIGHASH_ALL); txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, tx.vin[i].scriptSig); if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, 0)) fComplete = false; } CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << mergedTx; ui->signedTransaction->setText(HexStr(ssTx.begin(), ssTx.end()).c_str()); if(fComplete) { ui->statusLabel->setText(tr("Transaction signature is complete")); ui->sendTransactionButton->setEnabled(true); } else { ui->statusLabel->setText(tr("Transaction is NOT completely signed")); ui->sendTransactionButton->setEnabled(false); } }