WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &transaction) { QByteArray transaction_array; /* store serialized transaction */ { LOCK2(cs_main, wallet->cs_wallet); CWalletTx *newTx = transaction.getTransaction(); // Store PaymentRequests in wtx.vOrderForm in wallet. foreach(const SendCoinsRecipient &rcp, transaction.getRecipients()) { if (rcp.paymentRequest.IsInitialized()) { std::string key("PaymentRequest"); std::string value; rcp.paymentRequest.SerializeToString(&value); newTx->vOrderForm.push_back(make_pair(key, value)); } } CReserveKey *keyChange = transaction.getPossibleKeyChange(); if(!wallet->CommitTransaction(*newTx, *keyChange)) return TransactionCommitFailed; CTransaction* t = (CTransaction*)newTx; CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << *t; transaction_array.append(&(ssTx[0]), ssTx.size()); } // Add addresses / update labels that we've sent to to the address book, // and emit coinsSent signal for each recipient foreach(const SendCoinsRecipient &rcp, transaction.getRecipients()) { std::string strAddress = rcp.address.toStdString(); CTxDestination dest = CBitcoinAddress(strAddress).Get(); std::string strLabel = rcp.label.toStdString(); { LOCK(wallet->cs_wallet); std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(dest); // Check if we have a new address or an updated label if (mi == wallet->mapAddressBook.end()) { wallet->SetAddressBook(dest, strLabel, "send"); } else if (mi->second.name != strLabel) { wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose } } emit coinsSent(wallet, rcp, transaction_array); } return SendCoinsReturn(OK); }
WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction& transaction) { QByteArray transaction_array; /* store serialized transaction */ if (isAnonymizeOnlyUnlocked()) { return AnonymizeOnlyUnlocked; } { LOCK2(cs_main, wallet->cs_wallet); CWalletTx* newTx = transaction.getTransaction(); QList<SendCoinsRecipient> recipients = transaction.getRecipients(); // Store PaymentRequests in wtx.vOrderForm in wallet. foreach (const SendCoinsRecipient& rcp, recipients) { if (rcp.paymentRequest.IsInitialized()) { std::string key("PaymentRequest"); std::string value; rcp.paymentRequest.SerializeToString(&value); newTx->vOrderForm.push_back(make_pair(key, value)); } else if (!rcp.message.isEmpty()) // Message from normal ion:URI (ion:XyZ...?message=example) { newTx->vOrderForm.push_back(make_pair("Message", rcp.message.toStdString())); } } CReserveKey* keyChange = transaction.getPossibleKeyChange(); transaction.getRecipients(); if (!wallet->CommitTransaction(*newTx, *keyChange, (recipients[0].useSwiftTX) ? "ix" : "tx")) return TransactionCommitFailed; CTransaction* t = (CTransaction*)newTx; CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << *t; transaction_array.append(&(ssTx[0]), ssTx.size()); } // Add addresses / update labels that we've sent to to the address book, // and emit coinsSent signal for each recipient foreach (const SendCoinsRecipient& rcp, transaction.getRecipients()) { // Don't touch the address book when we have a payment request if (!rcp.paymentRequest.IsInitialized()) { std::string strAddress = rcp.address.toStdString(); CTxDestination dest = DecodeDestination(strAddress); std::string strLabel = rcp.label.toStdString(); updateAddressBookLabels(dest, strLabel, "send"); } emit coinsSent(wallet, rcp, transaction_array); } checkBalanceChanged(); // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits return SendCoinsReturn(OK); }
WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &transaction) { QByteArray transaction_array; /* store serialized transaction */ { std::vector<std::pair<std::string, std::string>> vOrderForm; for (const SendCoinsRecipient &rcp : transaction.getRecipients()) { #ifdef ENABLE_BIP70 if (rcp.paymentRequest.IsInitialized()) { // Make sure any payment requests involved are still valid. if (PaymentServer::verifyExpired(rcp.paymentRequest.getDetails())) { return PaymentRequestExpired; } // Store PaymentRequests in wtx.vOrderForm in wallet. std::string value; rcp.paymentRequest.SerializeToString(&value); vOrderForm.emplace_back("PaymentRequest", std::move(value)); } else #endif if (!rcp.message.isEmpty()) // Message from normal bitcoin:URI (bitcoin:123...?message=example) vOrderForm.emplace_back("Message", rcp.message.toStdString()); } auto& newTx = transaction.getWtx(); bool fCoinJoin = true; // CoinJoin is executed per-tx for (const SendCoinsRecipient &rcp : transaction.getRecipients()) { if (!rcp.fCoinJoin) { fCoinJoin = false; continue; } } std::string rejectReason; if (!newTx->commit({} /* mapValue */, std::move(vOrderForm), rejectReason, fCoinJoin)) return SendCoinsReturn(TransactionCommitFailed, QString::fromStdString(rejectReason)); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << newTx->get(); transaction_array.append(&(ssTx[0]), ssTx.size()); } // Add addresses / update labels that we've sent to the address book, // and emit coinsSent signal for each recipient for (const SendCoinsRecipient &rcp : transaction.getRecipients()) { // Don't touch the address book when we have a payment request #ifdef ENABLE_BIP70 if (!rcp.paymentRequest.IsInitialized()) #endif { std::string strAddress = rcp.address.toStdString(); CTxDestination dest = DecodeDestination(strAddress); std::string strLabel = rcp.label.toStdString(); { // Check if we have a new address or an updated label std::string name; if (!m_wallet->getAddress( dest, &name, /* is_mine= */ nullptr, /* purpose= */ nullptr)) { m_wallet->setAddressBook(dest, strLabel, "send"); } else if (name != strLabel) { m_wallet->setAddressBook(dest, strLabel, ""); // "" means don't change purpose } } } Q_EMIT coinsSent(this, rcp, transaction_array); } checkBalanceChanged(m_wallet->getBalances()); // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits checkCoinJoinChanged(m_wallet->getCoinJoinStatus()); // update status immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits return SendCoinsReturn(OK); }
WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl) { CAmount total = 0; bool fSubtractFeeFromAmount = false; bool fCoinJoin = false; QList<SendCoinsRecipient> recipients = transaction.getRecipients(); std::vector<CRecipient> vecSend; if(recipients.empty()) { return OK; } QSet<QString> setAddress; // Used to detect duplicates int nAddresses = 0; // Pre-check input data for validity for (const SendCoinsRecipient &rcp : recipients) { if (rcp.fCoinJoin) fCoinJoin = true; if (rcp.fSubtractFeeFromAmount) fSubtractFeeFromAmount = true; #ifdef ENABLE_BIP70 if (rcp.paymentRequest.IsInitialized()) { // PaymentRequest... CAmount subtotal = 0; const payments::PaymentDetails& details = rcp.paymentRequest.getDetails(); for (int i = 0; i < details.outputs_size(); i++) { const payments::Output& out = details.outputs(i); if (out.amount() <= 0) continue; subtotal += out.amount(); const unsigned char* scriptStr = (const unsigned char*)out.script().data(); CScript scriptPubKey(scriptStr, scriptStr+out.script().size()); CAmount nAmount = out.amount(); CRecipient recipient = {scriptPubKey, nAmount, rcp.fSubtractFeeFromAmount}; vecSend.push_back(recipient); } if (subtotal <= 0) { return InvalidAmount; } total += subtotal; } else #endif { // User-entered chaincoin address / amount: if(!validateAddress(rcp.address)) { return InvalidAddress; } if(rcp.amount <= 0) { return InvalidAmount; } setAddress.insert(rcp.address); ++nAddresses; CScript scriptPubKey = GetScriptForDestination(DecodeDestination(rcp.address.toStdString())); CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount}; vecSend.push_back(recipient); total += rcp.amount; } } if(setAddress.size() != nAddresses) { return DuplicateAddress; } CAmount nBalance = m_wallet->getAvailableBalance(coinControl); if(total > nBalance) { return AmountExceedsBalance; } { CAmount nFeeRequired = 0; int nChangePosRet = -1; std::string strFailReason; auto& newTx = transaction.getWtx(); newTx = m_wallet->createTransaction(vecSend, coinControl, true /* sign */, nChangePosRet, nFeeRequired, strFailReason, fCoinJoin); transaction.setTransactionFee(nFeeRequired); if (fSubtractFeeFromAmount && newTx) transaction.reassignAmounts(nChangePosRet); if(!newTx) { if(!fSubtractFeeFromAmount && (total + nFeeRequired) > nBalance) { return SendCoinsReturn(AmountWithFeeExceedsBalance); } Q_EMIT message(tr("Send Coins"), QString::fromStdString(strFailReason), CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } // reject absurdly high fee. (This can never happen because the // wallet caps the fee at maxTxFee. This merely serves as a // belt-and-suspenders check) if (nFeeRequired > m_node.getMaxTxFee()) return AbsurdFee; } return SendCoinsReturn(OK); }
WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction& transaction, const CCoinControl* coinControl) { CAmount total = 0; bool fSubtractFeeFromAmount = false; QList<SendCoinsRecipient> recipients = transaction.getRecipients(); std::vector<CRecipient> vecSend; if (recipients.empty()) { return OK; } if (isAnonymizeOnlyUnlocked()) { return AnonymizeOnlyUnlocked; } QSet<QString> setAddress; // Used to detect duplicates int nAddresses = 0; // Pre-check input data for validity foreach (const SendCoinsRecipient& rcp, recipients) { if (rcp.fSubtractFeeFromAmount) fSubtractFeeFromAmount = true; if (rcp.paymentRequest.IsInitialized()) { // PaymentRequest... CAmount subtotal = 0; const payments::PaymentDetails& details = rcp.paymentRequest.getDetails(); for (int i = 0; i < details.outputs_size(); i++) { const payments::Output& out = details.outputs(i); if (out.amount() <= 0) continue; subtotal += out.amount(); const unsigned char* scriptStr = (const unsigned char*)out.script().data(); CScript scriptPubKey(scriptStr, scriptStr + out.script().size()); CAmount nAmount = out.amount(); CRecipient recipient = {scriptPubKey, nAmount, rcp.fSubtractFeeFromAmount}; vecSend.push_back(recipient); } if (subtotal <= 0) { return InvalidAmount; } total += subtotal; } else { // User-entered ion address / amount: if (!validateAddress(rcp.address)) { return InvalidAddress; } if (rcp.amount <= 0) { return InvalidAmount; } setAddress.insert(rcp.address); ++nAddresses; CScript scriptPubKey = GetScriptForDestination(DecodeDestination(rcp.address.toStdString())); CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount}; vecSend.push_back(recipient); total += rcp.amount; } } if (setAddress.size() != nAddresses) { return DuplicateAddress; } CAmount nBalance = getBalance(coinControl); if (total > nBalance) { return AmountExceedsBalance; } { LOCK2(cs_main, wallet->cs_wallet); transaction.newPossibleKeyChange(wallet); CAmount nFeeRequired = 0; int nChangePosRet = -1; std::string strFailReason; CWalletTx* newTx = transaction.getTransaction(); CReserveKey* keyChange = transaction.getPossibleKeyChange(); if (recipients[0].useSwiftTX && total > GetSporkValue(SPORK_3_SWIFTTX_MAX_VALUE) * COIN) { emit message(tr("Send Coins"), tr("SwiftX doesn't support sending values that high yet. Transactions are currently limited to %1 ION.").arg(GetSporkValue(SPORK_3_SWIFTTX_MAX_VALUE)), CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, nChangePosRet, strFailReason, coinControl, recipients[0].inputType, recipients[0].useSwiftTX); transaction.setTransactionFee(nFeeRequired); if (fSubtractFeeFromAmount && fCreated) transaction.reassignAmounts(nChangePosRet); if (recipients[0].useSwiftTX && newTx->GetValueOut() > GetSporkValue(SPORK_3_SWIFTTX_MAX_VALUE) * COIN) { emit message(tr("Send Coins"), tr("SwiftX doesn't support sending values that high yet. Transactions are currently limited to %1 ION.").arg(GetSporkValue(SPORK_3_SWIFTTX_MAX_VALUE)), CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } if (!fCreated) { if (!fSubtractFeeFromAmount && (total + nFeeRequired) > nBalance) { return SendCoinsReturn(AmountWithFeeExceedsBalance); } emit message(tr("Send Coins"), QString::fromStdString(strFailReason), CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } // reject insane fee if (nFeeRequired > ::minRelayTxFee.GetFee(transaction.getTransactionSize()) * 10000) return InsaneFee; } return SendCoinsReturn(OK); }
WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &transaction) { QByteArray transaction_array; /* store serialized transaction */ { LOCK2(cs_main, wallet->cs_wallet); CWalletTx *newTx = transaction.getTransaction(); Q_FOREACH(const SendCoinsRecipient &rcp, transaction.getRecipients()) { if (rcp.paymentRequest.IsInitialized()) { // Make sure any payment requests involved are still valid. if (PaymentServer::verifyExpired(rcp.paymentRequest.getDetails())) { return PaymentRequestExpired; } // Store PaymentRequests in wtx.vOrderForm in wallet. std::string key("PaymentRequest"); std::string value; rcp.paymentRequest.SerializeToString(&value); newTx->vOrderForm.push_back(make_pair(key, value)); } else if (!rcp.message.isEmpty()) // Message from normal bitcoin:URI (bitcoin:123...?message=example) newTx->vOrderForm.push_back(make_pair("Message", rcp.message.toStdString())); } CReserveKey *keyChange = transaction.getPossibleKeyChange(); if(!wallet->CommitTransaction(*newTx, *keyChange)) return TransactionCommitFailed; CTransaction* t = (CTransaction*)newTx; CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << *t; transaction_array.append(&(ssTx[0]), ssTx.size()); } // Add addresses / update labels that we've sent to to the address book, // and emit coinsSent signal for each recipient Q_FOREACH(const SendCoinsRecipient &rcp, transaction.getRecipients()) { // Don't touch the address book when we have a payment request if (!rcp.paymentRequest.IsInitialized()) { std::string strAddress = rcp.address.toStdString(); CTxDestination dest = CBitcoinAddress(strAddress).Get(); std::string strLabel = rcp.label.toStdString(); { LOCK(wallet->cs_wallet); std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(dest); // Check if we have a new address or an updated label if (mi == wallet->mapAddressBook.end()) { wallet->SetAddressBook(dest, strLabel, "send"); } else if (mi->second.name != strLabel) { wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose } } } Q_EMIT coinsSent(wallet, rcp, transaction_array); } checkBalanceChanged(); // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits return SendCoinsReturn(OK); }
WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl *coinControl) { qint64 total = 0; QList<SendCoinsRecipient> recipients = transaction.getRecipients(); std::vector<std::pair<CScript, int64_t> > vecSend; if(recipients.empty()) { return OK; } QSet<QString> setAddress; // Used to detect duplicates int nAddresses = 0; // Pre-check input data for validity foreach(const SendCoinsRecipient &rcp, recipients) { if (rcp.paymentRequest.IsInitialized()) { // PaymentRequest... int64_t subtotal = 0; const payments::PaymentDetails& details = rcp.paymentRequest.getDetails(); for (int i = 0; i < details.outputs_size(); i++) { const payments::Output& out = details.outputs(i); if (out.amount() <= 0) continue; subtotal += out.amount(); const unsigned char* scriptStr = (const unsigned char*)out.script().data(); CScript scriptPubKey(scriptStr, scriptStr+out.script().size()); vecSend.push_back(std::pair<CScript, int64_t>(scriptPubKey, out.amount())); } if (subtotal <= 0) { return InvalidAmount; } total += subtotal; } else { // User-entered bitcoin address / amount: if(!validateAddress(rcp.address)) { return InvalidAddress; } if(rcp.amount <= 0) { return InvalidAmount; } setAddress.insert(rcp.address); ++nAddresses; CScript scriptPubKey; scriptPubKey.SetDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); vecSend.push_back(std::pair<CScript, int64_t>(scriptPubKey, rcp.amount)); total += rcp.amount; } } if(setAddress.size() != nAddresses) { return DuplicateAddress; } qint64 nBalance = getBalance(coinControl); if(total > nBalance) { return AmountExceedsBalance; } if((total + nTransactionFee) > nBalance) { transaction.setTransactionFee(nTransactionFee); return SendCoinsReturn(AmountWithFeeExceedsBalance); } { LOCK2(cs_main, wallet->cs_wallet); transaction.newPossibleKeyChange(wallet); int64_t nFeeRequired = 0; std::string strFailReason; CWalletTx *newTx = transaction.getTransaction(); CReserveKey *keyChange = transaction.getPossibleKeyChange(); bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, strFailReason, coinControl); transaction.setTransactionFee(nFeeRequired); if(!fCreated) { if((total + nFeeRequired) > nBalance) { return SendCoinsReturn(AmountWithFeeExceedsBalance); } emit message(tr("Send Coins"), QString::fromStdString(strFailReason), CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } } return SendCoinsReturn(OK); }
WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl *coinControl) { CAmount total = 0; bool fSubtractFeeFromAmount = false; QList<SendCoinsRecipient> recipients = transaction.getRecipients(); std::vector<CRecipient> vecSend; if(recipients.empty()) { return OK; } // This should never really happen, yet another safety check, just in case. if(wallet->IsLocked()) { return TransactionCreationFailed; } QSet<QString> setAddress; // Used to detect duplicates int nAddresses = 0; // Pre-check input data for validity Q_FOREACH(const SendCoinsRecipient &rcp, recipients) { if (rcp.fSubtractFeeFromAmount) fSubtractFeeFromAmount = true; if (rcp.paymentRequest.IsInitialized()) { // PaymentRequest... CAmount subtotal = 0; const payments::PaymentDetails& details = rcp.paymentRequest.getDetails(); for (int i = 0; i < details.outputs_size(); i++) { const payments::Output& out = details.outputs(i); if (out.amount() <= 0) continue; subtotal += out.amount(); const unsigned char* scriptStr = (const unsigned char*)out.script().data(); CScript scriptPubKey(scriptStr, scriptStr+out.script().size()); CAmount nAmount = out.amount(); CRecipient recipient = {scriptPubKey, nAmount, rcp.fSubtractFeeFromAmount}; vecSend.push_back(recipient); } if (subtotal <= 0) { return InvalidAmount; } total += subtotal; } else { // User-entered dash address / amount: if(!validateAddress(rcp.address)) { return InvalidAddress; } if(rcp.amount <= 0) { return InvalidAmount; } setAddress.insert(rcp.address); ++nAddresses; CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount}; vecSend.push_back(recipient); total += rcp.amount; } } if(setAddress.size() != nAddresses) { return DuplicateAddress; } CAmount nBalance = getBalance(coinControl); if(total > nBalance) { return AmountExceedsBalance; } { LOCK2(cs_main, wallet->cs_wallet); transaction.newPossibleKeyChange(wallet); CAmount nFeeRequired = 0; int nChangePosRet = -1; std::string strFailReason; CWalletTx *newTx = transaction.getTransaction(); CReserveKey *keyChange = transaction.getPossibleKeyChange(); if(recipients[0].fUseInstantSend && total > sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)*COIN){ Q_EMIT message(tr("Send Coins"), tr("InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH.").arg(sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)), CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, nChangePosRet, strFailReason, coinControl, true, recipients[0].inputType, recipients[0].fUseInstantSend); transaction.setTransactionFee(nFeeRequired); if (fSubtractFeeFromAmount && fCreated) transaction.reassignAmounts(nChangePosRet); if(recipients[0].fUseInstantSend) { if(newTx->tx->GetValueOut() > sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)*COIN) { Q_EMIT message(tr("Send Coins"), tr("InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH.").arg(sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)), CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } if(newTx->tx->vin.size() > CTxLockRequest::WARN_MANY_INPUTS) { Q_EMIT message(tr("Send Coins"), tr("Used way too many inputs (>%1) for this InstantSend transaction, fees could be huge.").arg(CTxLockRequest::WARN_MANY_INPUTS), CClientUIInterface::MSG_WARNING); } } if(!fCreated) { if(!fSubtractFeeFromAmount && (total + nFeeRequired) > nBalance) { return SendCoinsReturn(AmountWithFeeExceedsBalance); } Q_EMIT message(tr("Send Coins"), QString::fromStdString(strFailReason), CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } // reject absurdly high fee. (This can never happen because the // wallet caps the fee at maxTxFee. This merely serves as a // belt-and-suspenders check) if (nFeeRequired > maxTxFee) return AbsurdFee; } return SendCoinsReturn(OK); }
WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl *coinControl) { CAmount total = 0; bool fSubtractFeeFromAmount = false; QList<SendCoinsRecipient> recipients = transaction.getRecipients(); std::vector<CRecipient> vecSend; if(recipients.empty()) { return OK; } QSet<QString> setAddress; // Used to detect duplicates int nAddresses = 0; // Pre-check input data for validity Q_FOREACH(const SendCoinsRecipient &rcp, recipients) { if (rcp.fSubtractFeeFromAmount) fSubtractFeeFromAmount = true; if (rcp.paymentRequest.IsInitialized()) { // PaymentRequest... CAmount subtotal = 0; const payments::PaymentDetails& details = rcp.paymentRequest.getDetails(); for (int i = 0; i < details.outputs_size(); i++) { const payments::Output& out = details.outputs(i); if (out.amount() <= 0) continue; subtotal += out.amount(); const unsigned char* scriptStr = (const unsigned char*)out.script().data(); CScript scriptPubKey(scriptStr, scriptStr+out.script().size()); CAmount nAmount = out.amount(); CRecipient recipient = {scriptPubKey, nAmount, rcp.fSubtractFeeFromAmount}; vecSend.push_back(recipient); } if (subtotal <= 0) { return InvalidAmount; } total += subtotal; } else { // User-entered bitcoin address / amount: if(!validateAddress(rcp.address)) { return InvalidAddress; } if(rcp.amount <= 0) { return InvalidAmount; } setAddress.insert(rcp.address); ++nAddresses; CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount}; vecSend.push_back(recipient); total += rcp.amount; } } if(setAddress.size() != nAddresses) { return DuplicateAddress; } CAmount nBalance = getBalance(coinControl); if(total > nBalance) { return AmountExceedsBalance; } { LOCK2(cs_main, wallet->cs_wallet); transaction.newPossibleKeyChange(wallet); CAmount nFeeRequired = 0; int nChangePosRet = -1; std::string strFailReason; CWalletTx *newTx = transaction.getTransaction(); CReserveKey *keyChange = transaction.getPossibleKeyChange(); bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, nChangePosRet, strFailReason, coinControl); transaction.setTransactionFee(nFeeRequired); if (fSubtractFeeFromAmount && fCreated) transaction.reassignAmounts(nChangePosRet); if(!fCreated) { if(!fSubtractFeeFromAmount && (total + nFeeRequired) > nBalance) { return SendCoinsReturn(AmountWithFeeExceedsBalance); } Q_EMIT message(tr("Send Coins"), QString::fromStdString(strFailReason), CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } // reject absurdly high fee > 0.1 bitcoin if (nFeeRequired > 10000000) return AbsurdFee; } return SendCoinsReturn(OK); }
void PaperWalletDialog::on_printButton_clicked() { QPrinter printer(QPrinter::HighResolution); QPrintDialog *qpd = new QPrintDialog(&printer, this); qpd->setPrintRange(QAbstractPrintDialog::AllPages); QList<QString> recipientPubKeyHashes; if ( qpd->exec() != QDialog::Accepted ) { return; } // Hardcode these values printer.setOrientation(QPrinter::Portrait); printer.setPaperSize(QPrinter::A4); printer.setFullPage(true); QPainter painter; if (! painter.begin(&printer)) { // failed to open file QMessageBox::critical(this, "Printing Error", tr("failed to open file, is it writable?"), QMessageBox::Ok, QMessageBox::Ok); return; } int walletCount = ui->walletCount->currentIndex() + 1; int walletsPerPage = 4; int pageHeight = printer.pageRect().height() - PAPER_WALLET_PAGE_MARGIN; int walletHeight = ui->paperTemplate->height(); double computedWalletHeight = 0.9 * pageHeight / walletsPerPage; double scale = computedWalletHeight / walletHeight; double walletPadding = pageHeight * 0.05 / (walletsPerPage - 1) / scale; QRegion walletRegion = QRegion(ui->paperTemplate->x(), ui->paperTemplate->y(), ui->paperTemplate->width(), ui->paperTemplate->height()); painter.scale(scale, scale); for(int i = 0; i < walletCount; i++) { QPoint point = QPoint(PAPER_WALLET_PAGE_MARGIN, (PAPER_WALLET_PAGE_MARGIN / 2) + ( i % walletsPerPage ) * (walletHeight + walletPadding)); this->render(&painter, point, walletRegion); recipientPubKeyHashes.append(ui->addressText->text()); if ( i % walletsPerPage == ( walletsPerPage - 1 ) ) { printer.newPage(); } this->on_getNewAddress_clicked(); } painter.end(); #ifdef ENABLE_WALLET QStringList formatted; WalletModelTransaction *tx; while( true ) { bool ok; // Ask for an amount to send to each paper wallet. It might be better to try to use the BitcoinAmountField, but this works fine. double amountInput = QInputDialog::getDouble(this, tr("Load Paper Wallets"), tr("The paper wallet printing process has begun.<br/>Please wait for the wallets to print completely and verify that everything printed correctly.<br/>Check for misalignments, ink bleeding, smears, or anything else that could make the private keys unreadable.<br/>Now, enter the number of EPC you wish to send to each wallet:"), 0, 0, 2147483647, 8, &ok); if(!ok) { return; } WalletModel::UnlockContext ctx(this->model->requestUnlock()); if(!ctx.isValid()) { return; } QList<SendCoinsRecipient> recipients; quint64 amount = (quint64) ( amountInput * COIN ); for(const QString &dest : recipientPubKeyHashes) { recipients.append(SendCoinsRecipient(dest,tr("Paper wallet %1").arg(dest), amount,"")); formatted.append(tr("<b>%1</b> to Paper Wallet <span style='font-family: monospace;'>%2</span>").arg(QString::number(amountInput, 'f', 8), GUIUtil::HtmlEscape(dest))); } tx = new WalletModelTransaction(recipients); WalletModel::SendCoinsReturn prepareStatus; if (this->model->getOptionsModel()->getCoinControlFeatures()) // coin control enabled prepareStatus = this->model->prepareTransaction(*tx, CoinControlDialog::coinControl); else prepareStatus = this->model->prepareTransaction(*tx); if (prepareStatus.status == WalletModel::InvalidAddress) { QMessageBox::critical(this, tr("Send Coins"), tr("The recipient address is not valid, please recheck."), QMessageBox::Ok, QMessageBox::Ok); } else if (prepareStatus.status == WalletModel::InvalidAmount) { QMessageBox::critical(this, tr("Send Coins"), tr("The amount to pay must be larger than 0"), QMessageBox::Ok, QMessageBox::Ok); } else if (prepareStatus.status == WalletModel::AmountExceedsBalance) { QMessageBox::critical(this, tr("Send Coins"), tr("The amount exceeds your balance."), QMessageBox::Ok, QMessageBox::Ok); } else if (prepareStatus.status == WalletModel::AmountWithFeeExceedsBalance) { QMessageBox::critical(this, tr("Send Coins"), tr("The total exceeds your balance when the transaction fee is included"), QMessageBox::Ok, QMessageBox::Ok); } else if (prepareStatus.status == WalletModel::DuplicateAddress) { QMessageBox::critical(this, tr("Send Coins"), tr("Duplicate address found, can only send to each address once per send operation."), QMessageBox::Ok, QMessageBox::Ok); } else if (prepareStatus.status == WalletModel::TransactionCreationFailed) { QMessageBox::critical(this, tr("Send Coins"), tr("Transaction creation failed!"), QMessageBox::Ok, QMessageBox::Ok); } else if (prepareStatus.status == WalletModel::OK) { break; } else { delete tx; return; } } // Stolen from sendcoinsdialog.cpp qint64 txFee = tx->getTransactionFee(); QString questionString = tr("Are you sure you want to send?"); questionString.append("<br /><br />%1"); if(txFee > 0) { // append fee string if a fee is required questionString.append("<hr /><span style='color:#aa0000;'>"); questionString.append(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee)); questionString.append("</span> "); questionString.append(tr("added as transaction fee")); } // add total amount in all subdivision units questionString.append("<hr />"); qint64 totalAmount = tx->getTotalTransactionAmount() + txFee; QStringList alternativeUnits; for(BitcoinUnits::Unit u : BitcoinUnits::availableUnits()) { if(u != model->getOptionsModel()->getDisplayUnit()) alternativeUnits.append(BitcoinUnits::formatWithUnit(u, totalAmount)); } questionString.append(tr("Total Amount %1 (= %2)") .arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount)) .arg(alternativeUnits.join(" " + tr("or") + " "))); QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"), questionString.arg(formatted.join("<br />")), QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); if(retval != QMessageBox::Yes) { delete tx; return; } WalletModel::SendCoinsReturn sendStatus = this->model->sendCoins(*tx); if (sendStatus.status == WalletModel::TransactionCommitFailed) { QMessageBox::critical(this, tr("Send Coins"), tr("The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."), QMessageBox::Ok, QMessageBox::Ok); } delete tx; #endif return; }
WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl *coinControl) { qint64 total = 0; QList<SendCoinsRecipient> recipients = transaction.getRecipients(); std::vector<std::pair<CScript, int64_t> > vecSend; if(recipients.empty()) { return OK; } QSet<QString> setAddress; // Used to detect duplicates int nAddresses = 0; // Pre-check input data for validity foreach(const SendCoinsRecipient &rcp, recipients) { if (rcp.paymentRequest.IsInitialized()) { // PaymentRequest... int64_t subtotal = 0; const payments::PaymentDetails& details = rcp.paymentRequest.getDetails(); for (int i = 0; i < details.outputs_size(); i++) { const payments::Output& out = details.outputs(i); if (out.amount() <= 0) continue; subtotal += out.amount(); const unsigned char* scriptStr = (const unsigned char*)out.script().data(); CScript scriptPubKey(scriptStr, scriptStr+out.script().size()); vecSend.push_back(std::pair<CScript, int64_t>(scriptPubKey, out.amount())); } if (subtotal <= 0) { return InvalidAmount; } total += subtotal; } else { // User-entered bitcoin address / amount: if(!validateAddress(rcp.address)) { return InvalidAddress; } if(rcp.amount <= 0) { return InvalidAmount; } setAddress.insert(rcp.address); ++nAddresses; // stealth std::string sAddr = rcp.address.toStdString(); if (rcp.typeInd == AddressTableModel::AT_Stealth) { if ( (!TestNet() && (chainActive.Height() < BLOCK_STEALTH_START)) || (TestNet() && (chainActive.Height() < 200)) ) { emit message(tr("Send Coins"), tr("Stealth addresses not yet supported"), CClientUIInterface::MSG_ERROR); return InvalidAddress; } CStealthAddress sxAddr; if (sxAddr.SetEncoded(sAddr)) { ec_secret ephem_secret; ec_secret secretShared; ec_point pkSendTo; ec_point ephem_pubkey; if (GenerateRandomSecret(ephem_secret) != 0) { LogPrintf("GenerateRandomSecret failed.\n"); return InvalidAddress; }; if (StealthSecret(ephem_secret, sxAddr.scan_pubkey, sxAddr.spend_pubkey, secretShared, pkSendTo) != 0) { LogPrintf("Could not generate receiving public key.\n"); return InvalidAddress; }; CPubKey cpkTo(pkSendTo); if (!cpkTo.IsValid()) { LogPrintf("Invalid public key generated.\n"); return InvalidAddress; }; CKeyID ckidTo = cpkTo.GetID(); CBitcoinAddress addrTo(ckidTo); if (SecretToPublicKey(ephem_secret, ephem_pubkey) != 0) { LogPrintf("Could not generate ephem public key.\n"); return InvalidAddress; }; if (fDebug) { LogPrintf("Stealth send to generated pubkey %ui: %s\n", pkSendTo.size(), HexStr(pkSendTo).c_str()); LogPrintf("hash %s\n", addrTo.ToString().c_str()); LogPrintf("ephem_pubkey %ui: %s\n", ephem_pubkey.size(), HexStr(ephem_pubkey).c_str()); }; CScript scriptPubKey; scriptPubKey.SetDestination(addrTo.Get()); vecSend.push_back(make_pair(scriptPubKey, rcp.amount)); CScript scriptP = CScript() << OP_RETURN << ephem_pubkey; vecSend.push_back(make_pair(scriptP, 0)); continue; }; // else drop through to normal } CScript scriptPubKey; scriptPubKey.SetDestination(CBitcoinAddress(sAddr).Get()); vecSend.push_back(std::pair<CScript, int64_t>(scriptPubKey, rcp.amount)); total += rcp.amount; } } if(setAddress.size() != nAddresses) { return DuplicateAddress; } qint64 nBalance = getBalance(coinControl); if(total > nBalance) { return AmountExceedsBalance; } if((total + nTransactionFee) > nBalance) { transaction.setTransactionFee(nTransactionFee); return SendCoinsReturn(AmountWithFeeExceedsBalance); } { LOCK2(cs_main, wallet->cs_wallet); transaction.newPossibleKeyChange(wallet); int64_t nFeeRequired = 0; std::string strFailReason; CWalletTx *newTx = transaction.getTransaction(); CReserveKey *keyChange = transaction.getPossibleKeyChange(); // int nChangePos = -1; bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, /*nChangePos,*/ strFailReason, coinControl); /* std::map<int, std::string>::iterator it; for (it = mapStealthNarr.begin(); it != mapStealthNarr.end(); ++it) { int pos = it->first; if (nChangePos > -1 && it->first >= nChangePos) pos++; char key[64]; if (snprintf(key, sizeof(key), "n_%u", pos) < 1) { printf("CreateStealthTransaction(): Error creating narration key."); continue; }; wtx.mapValue[key] = it->second; }; */ transaction.setTransactionFee(nFeeRequired); if(!fCreated) { if((total + nFeeRequired) > nBalance) { return SendCoinsReturn(AmountWithFeeExceedsBalance); } emit message(tr("Send Coins"), QString::fromStdString(strFailReason), CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } } return SendCoinsReturn(OK); }
WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &transaction) { QByteArray transaction_array; /* store serialized transaction */ { LOCK2(cs_main, wallet->cs_wallet); CWalletTx *newTx = transaction.getTransaction(); // Store PaymentRequests in wtx.vOrderForm in wallet. foreach(const SendCoinsRecipient &rcp, transaction.getRecipients()) { if (rcp.paymentRequest.IsInitialized()) { std::string key("PaymentRequest"); std::string value; rcp.paymentRequest.SerializeToString(&value); newTx->vOrderForm.push_back(make_pair(key, value)); } else if (!rcp.message.isEmpty()) // Message from normal feathercoin:URI (feathercoin:123...?message=example) newTx->vOrderForm.push_back(make_pair("Message", rcp.message.toStdString())); } //Run//QMessageBox::information(NULL, "Info2", "Click_sendCoins", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); CReserveKey *keyChange = transaction.getPossibleKeyChange(); LogPrintf("sendCoins: newTx= \n%s", newTx->ToString()); if(!wallet->CommitTransaction(*newTx, *keyChange)) return TransactionCommitFailed; //string strError =wallet->SendMoneyToDestination(rcp.label, transaction.GetValueOut(), *newTx); CTransaction* t = (CTransaction*)newTx; CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << *t; transaction_array.append(&(ssTx[0]), ssTx.size()); } // Add addresses / update labels that we've sent to to the address book, // and emit coinsSent signal for each recipient //No Run// foreach(const SendCoinsRecipient &rcp, transaction.getRecipients()) { // Don't touch the address book when we have a payment request if (!rcp.paymentRequest.IsInitialized()) { std::string strAddress = rcp.address.toStdString(); CTxDestination dest = CBitcoinAddress(strAddress).Get(); std::string strLabel = rcp.label.toStdString(); { LOCK(wallet->cs_wallet); if (rcp.typeInd == AddressTableModel::AT_Stealth) { wallet->UpdateStealthAddress(strAddress, strLabel, true); } else { std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(dest); // Check if we have a new address or an updated label if (mi == wallet->mapAddressBook.end()) { wallet->SetAddressBook(dest, strLabel, "send"); } else if (mi->second.name != strLabel) { wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose } } } } emit coinsSent(wallet, rcp, transaction_array); LogPrintf("sendCoins: updateAddressBook. \n"); } return SendCoinsReturn(OK); }
WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl *coinControl) { qint64 total = 0; QList<SendCoinsRecipient> recipients = transaction.getRecipients(); std::vector<std::pair<CScript, int64_t> > vecSend; if(recipients.empty()) { return OK; } QSet<QString> setAddress; // Used to detect duplicates int nAddresses = 0; // Pre-check input data for validity foreach(const SendCoinsRecipient &rcp, recipients) { if (rcp.paymentRequest.IsInitialized()) { // PaymentRequest... int64_t subtotal = 0; const payments::PaymentDetails& details = rcp.paymentRequest.getDetails(); for (int i = 0; i < details.outputs_size(); i++) { const payments::Output& out = details.outputs(i); if (out.amount() <= 0) continue; subtotal += out.amount(); const unsigned char* scriptStr = (const unsigned char*)out.script().data(); CScript scriptPubKey(scriptStr, scriptStr+out.script().size()); vecSend.push_back(std::pair<CScript, int64_t>(scriptPubKey, out.amount())); } if (subtotal <= 0) { return InvalidAmount; } total += subtotal; } else { // User-entered feathercoin address / amount: if(!validateAddress(rcp.address)) { return InvalidAddress; } if(rcp.amount <= 0) { return InvalidAmount; } setAddress.insert(rcp.address); ++nAddresses; std::string sAddr = rcp.address.toStdString(); if (rcp.typeInd == AddressTableModel::AT_Stealth) { CStealthAddress sxAddr; if (sxAddr.SetEncoded(sAddr)) { ec_secret ephem_secret; ec_secret secretShared; ec_point pkSendTo; ec_point ephem_pubkey; if (GenerateRandomSecret(ephem_secret) != 0) { printf("GenerateRandomSecret failed.\n"); return Aborted; }; LogPrintf("StealthSecret send start....\n"); if (true) { LogPrintf("ephem_secret.e=%s \n",HexStr(&ephem_secret.e[0],&ephem_secret.e[32]).c_str()); //secret LogPrintf("sxAddr.scan_secret= %s\n", HexStr(sxAddr.scan_secret).c_str()); // LogPrintf("sxAddr.scan_pubkey= %s\n", HexStr(sxAddr.scan_pubkey).c_str()); //pubkey[0] LogPrintf("sxAddr.spend_secret= %s\n", HexStr(sxAddr.spend_secret).c_str()); // LogPrintf("sxAddr.spend_pubkey= %s\n",HexStr(sxAddr.spend_pubkey).c_str()); //pkSpend[0] LogPrintf("secretShared.e=%s \n",HexStr(&secretShared.e[0],&secretShared.e[32]).c_str()); //sharedSOut LogPrintf("pkSendTo= %"PRIszu": %s\n", pkSendTo.size(), HexStr(pkSendTo).c_str());//pkOut LogPrintf("send: secret = ephem_secret, pubkey = scan_pubkey....\n"); }; if (StealthSecret(ephem_secret, sxAddr.scan_pubkey, sxAddr.spend_pubkey, secretShared, pkSendTo) != 0) { printf("Could not generate receiving public key.\n"); return Aborted; }; if (true) { LogPrintf("ephem_secret.e=%s \n",HexStr(&ephem_secret.e[0],&ephem_secret.e[32]).c_str()); //secret LogPrintf("sxAddr.scan_secret= %s\n", HexStr(sxAddr.scan_secret).c_str()); // LogPrintf("sxAddr.scan_pubkey= %s\n", HexStr(sxAddr.scan_pubkey).c_str()); //pubkey[0] LogPrintf("sxAddr.spend_secret= %s\n", HexStr(sxAddr.spend_secret).c_str()); // LogPrintf("sxAddr.spend_pubkey= %s\n",HexStr(sxAddr.spend_pubkey).c_str()); //pkSpend[0] LogPrintf("secretShared.e=%s \n",HexStr(&secretShared.e[0],&secretShared.e[32]).c_str()); //sharedSOut LogPrintf("pkSendTo= %"PRIszu": %s\n", pkSendTo.size(), HexStr(pkSendTo).c_str());//pkOut }; CPubKey cpkTo(pkSendTo); if (!cpkTo.IsValid()) { printf("Invalid public key generated.\n"); return Aborted; }; LogPrintf("CPubKey(pkSendTo)=%s \n",cpkTo.GetHash().ToString().c_str()); CKeyID ckidTo = cpkTo.GetID(); CBitcoinAddress addrTo(ckidTo); if (SecretToPublicKey(ephem_secret, ephem_pubkey) != 0) { printf("Could not generate ephem public key.\n"); return Aborted; }; if (true) { LogPrintf("Stealth send to generated pubkey,pkSendTo= %"PRIszu": %s\n", pkSendTo.size(), HexStr(pkSendTo).c_str()); LogPrintf("hash, Address= %s\n", addrTo.ToString().c_str()); LogPrintf("enerate ephem public key,ephem_pubkey= %"PRIszu": %s\n", ephem_pubkey.size(), HexStr(ephem_pubkey).c_str()); }; CScript scriptPubKey; scriptPubKey.SetDestination(addrTo.Get()); LogPrintf("scriptPubKey= %s \n", scriptPubKey.ToString()); vecSend.push_back(make_pair(scriptPubKey, rcp.amount)); CScript scriptP = CScript() << OP_RETURN << ephem_pubkey; vecSend.push_back(make_pair(scriptP, 0)); continue; }; // else drop through to normal } CScript scriptPubKey; scriptPubKey.SetDestination(CBitcoinAddress(sAddr).Get()); vecSend.push_back(std::pair<CScript, int64_t>(scriptPubKey, rcp.amount)); //insert message into blockchain if ((rcp.message.length()>=1)&&(rcp.label=="blockchain")) { std::string strMess = rcp.message.toStdString(); const char* pszMess =strMess.c_str(); CScript scriptP = CScript() << OP_RETURN << vector<unsigned char>((const unsigned char*)pszMess, (const unsigned char*)pszMess + strlen(pszMess)); vecSend.push_back(std::pair<CScript, int64_t>(scriptP, 0)); } //commit openname if ((rcp.message.length()>=1)&&(rcp.label=="openname")) { std::string strMess = rcp.message.toStdString();//已经是16进制码流 CScript scriptP = CScript() << OP_RETURN << ParseHex(strMess); LogPrintf("openname scriptP=%s\n",scriptP.ToString().c_str());//scriptP=OP_RETURN 580861e00414720684e88cb7943fc6751527a94b2e0cdd2a9148d8b13939723d2aca16c75c6d68 vecSend.push_back(std::pair<CScript, int64_t>(scriptP, 0)); } //normal, do nothing total += rcp.amount; } } if(setAddress.size() != nAddresses) { return DuplicateAddress; } qint64 nBalance = getBalance(coinControl); if(total > nBalance) { return AmountExceedsBalance; } if((total + nTransactionFee) > nBalance) { transaction.setTransactionFee(nTransactionFee); return SendCoinsReturn(AmountWithFeeExceedsBalance); } { LOCK2(cs_main, wallet->cs_wallet); transaction.newPossibleKeyChange(wallet); int64_t nFeeRequired = 0; int nChangePos = -1; std::string strFailReason; CWalletTx *newTx = transaction.getTransaction(); CReserveKey *keyChange = transaction.getPossibleKeyChange(); bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, nChangePos, strFailReason, coinControl); transaction.setTransactionFee(nFeeRequired); if(!fCreated) { if((total + nFeeRequired) > nBalance) { return SendCoinsReturn(AmountWithFeeExceedsBalance); } emit message(tr("Send Coins"), QString::fromStdString(strFailReason), CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } } return SendCoinsReturn(OK); }