From f5734d5adf3862f1969f6144ed0cf65166e37641 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Mon, 22 May 2017 14:44:54 +0200 Subject: [PATCH 1/2] Use testnet feerates if appropriate --- src/bitpaywalletclient/bpwalletclient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitpaywalletclient/bpwalletclient.cpp b/src/bitpaywalletclient/bpwalletclient.cpp index 8981f994..bda9d95f 100644 --- a/src/bitpaywalletclient/bpwalletclient.cpp +++ b/src/bitpaywalletclient/bpwalletclient.cpp @@ -567,7 +567,7 @@ bool BitPayWalletClient::GetFeeLevels() return false; long httpStatusCode = 0; - if (!SendRequest("get", "/v1/feelevels/?network=livenet&r="+std::to_string(CheapRandom()), "{}", response, httpStatusCode)) + if (!SendRequest("get", "/v1/feelevels/?network="+std::string(testnet ? "testnet" : "livenet")+"&r="+std::to_string(CheapRandom()), "{}", response, httpStatusCode)) return false; if (httpStatusCode != 200) From a78647affb0718045265a5683ad2dac3eb2b4df2 Mon Sep 17 00:00:00 2001 From: Jonas Schnelli Date: Mon, 22 May 2017 14:46:48 +0200 Subject: [PATCH 2/2] Add send-max option --- src/bitpaywalletclient/bpwalletclient.cpp | 74 ++++++++++++++++++++--- src/bitpaywalletclient/bpwalletclient.h | 4 +- src/qt/dbb_gui.cpp | 31 ++++++++-- src/qt/dbb_gui.h | 1 + src/qt/ui/overview.ui | 21 +++++++ 5 files changed, 117 insertions(+), 14 deletions(-) diff --git a/src/bitpaywalletclient/bpwalletclient.cpp b/src/bitpaywalletclient/bpwalletclient.cpp index bda9d95f..e871da50 100644 --- a/src/bitpaywalletclient/bpwalletclient.cpp +++ b/src/bitpaywalletclient/bpwalletclient.cpp @@ -339,7 +339,7 @@ bool BitPayWalletClient::GetLastKnownAddress(std::string& address, std::string& } -bool BitPayWalletClient::CreatePaymentProposal(const std::string& address, uint64_t amount, uint64_t feeperkb, UniValue& paymentProposalOut, std::string& errorOut) +bool BitPayWalletClient::CreatePaymentProposal(const std::string& address, uint64_t amount, uint64_t feeperkb, UniValue& paymentProposalOut, std::string& errorOut, bool checkSingleOutput) { //form request UniValue outputs(UniValue::VARR); @@ -347,15 +347,39 @@ bool BitPayWalletClient::CreatePaymentProposal(const std::string& address, uint6 //currently we only support a single output UniValue mainOutput(UniValue::VOBJ); + // amount == 0 indicates to send max + uint64_t amount_to_send = amount; + if (amount == 0) { + std::string walletResponse; + if (!GetWallets(walletResponse)) { + errorOut = "Could not load balance"; + return false; + } + + UniValue walletResponseUni; + walletResponseUni.read(walletResponse); + + UniValue balanceObj = find_value(walletResponseUni, "balance"); + if (!balanceObj.isObject()) { + errorOut = "Could not load balance"; + return false; + } + UniValue totalAmountUni = find_value(balanceObj, "totalAmount"); + if (!totalAmountUni.isNum()) { + errorOut = "Could not load balance"; + return false; + } + amount_to_send = totalAmountUni.get_int64(); + } //add the output mainOutput.push_back(Pair("toAddress", address)); - mainOutput.push_back(Pair("amount", amount)); + mainOutput.push_back(Pair("amount", amount_to_send)); mainOutput.push_back(Pair("message", "")); mainOutput.push_back(Pair("script", "")); outputs.push_back(mainOutput); UniValue jsonArgs(UniValue::VOBJ); - jsonArgs.push_back(Pair("feePerKb", feeperkb)); + jsonArgs.push_back(Pair("feePerKb", (amount != 0 ? feeperkb : 0))); jsonArgs.push_back(Pair("payProUrl", false)); jsonArgs.push_back(Pair("type", "simple")); jsonArgs.push_back(Pair("version", "1.0.0")); @@ -406,25 +430,59 @@ bool BitPayWalletClient::CreatePaymentProposal(const std::string& address, uint6 } paymentProposalOut.read(response); - if (!PublishTxProposal(paymentProposalOut, errorOut)) - return false; - - return true; } else { //unknown error UniValue messageUni; messageUni = find_value(responseUni, "message"); + codeUni = find_value(responseUni, "code"); + + // + if (codeUni.isStr() && codeUni.get_str() == "INSUFFICIENT_FUNDS_FOR_FEE" && checkSingleOutput) { + return CreatePaymentProposal(address, amount_to_send-1000, feeperkb, paymentProposalOut, errorOut, true); + } + if (messageUni.isStr()) errorOut = messageUni.get_str(); + return false; } + } else { + paymentProposalOut.read(response); + } + + if (amount == 0) { + // send max proposal + + if (paymentProposalOut.isObject()) { + // count inputs + UniValue inputsObj = find_value(paymentProposalOut, "inputs"); + std::vector inputs = inputsObj.getValues(); + + // assume 149 bytes per input (32 txid, 4 pos, 74 der sig inc. push, 35 pubkey inc. push, 4 nSequence) + // assume 34 bytes per output (25+1 P2PKH, 8 amount, + // assume 10 bytes for rest of tx + + uint64_t estimatedSize = 10 + 34 + (149 * inputs.size()); + uint64_t feeWillBe = (estimatedSize/1000.0)*feeperkb; + + // BWS min fee calculation is buggy, "looping down" the value is required + return CreatePaymentProposal(address, amount_to_send - feeWillBe, feeperkb, paymentProposalOut, errorOut, true); + } } - paymentProposalOut.read(response); if (!paymentProposalOut.isObject()) return false; + if (checkSingleOutput) { + UniValue outputsObj = find_value(paymentProposalOut, "outputs"); + std::vector outputs = outputsObj.getValues(); + if (outputs.size() > 1) { + errorOut = "Could not calculate maximal amount to send out"; + return false; + } + } + if (!PublishTxProposal(paymentProposalOut, errorOut)) return false; diff --git a/src/bitpaywalletclient/bpwalletclient.h b/src/bitpaywalletclient/bpwalletclient.h index 6d4287d2..12537cdb 100644 --- a/src/bitpaywalletclient/bpwalletclient.h +++ b/src/bitpaywalletclient/bpwalletclient.h @@ -99,7 +99,9 @@ class BitPayWalletClient //!Return the last (disk) cached known address for receiving coins bool GetLastKnownAddress(std::string& address, std::string& keypath); - bool CreatePaymentProposal(const std::string& address, uint64_t amount, uint64_t feeperkb, UniValue& paymentProposalOut, std::string& errorOut); + //!Creates a payment proposal + //! If amount is set to 0, it tired to create a proposal with all available funds + bool CreatePaymentProposal(const std::string& address, uint64_t amount, uint64_t feeperkb, UniValue& paymentProposalOut, std::string& errorOut, bool checkSingleOutput = false); bool PublishTxProposal(const UniValue& paymentProposal, std::string& errorOut); diff --git a/src/qt/dbb_gui.cpp b/src/qt/dbb_gui.cpp index 5dc39eaf..c3c22d0d 100644 --- a/src/qt/dbb_gui.cpp +++ b/src/qt/dbb_gui.cpp @@ -254,6 +254,8 @@ DBBDaemonGui::DBBDaemonGui(const QString& uri, QWidget* parent) : QMainWindow(pa connect(ui->upgradeFirmware, SIGNAL(clicked()), this, SLOT(upgradeFirmwareButton())); connect(ui->openSettings, SIGNAL(clicked()), this, SLOT(showSettings())); connect(ui->pairDeviceButton, SIGNAL(clicked()), this, SLOT(pairSmartphone())); + connect(ui->sendMax, SIGNAL(stateChanged(int)), this, SLOT(sendMaxCheckboxDidChange(int))); + connect(ui->feeLevel, SIGNAL(currentIndexChanged(int)), this, SLOT(sendMaxCheckboxDidChange(int))); ui->upgradeFirmware->setVisible(true); ui->keypathLabel->setVisible(false);//hide keypath label for now (only tooptip) connect(ui->tableWidget, SIGNAL(doubleClicked(QModelIndex)),this,SLOT(historyShowTx(QModelIndex))); @@ -966,6 +968,7 @@ void DBBDaemonGui::modalStateChanged(bool state) { this->ui->sendToAddress->setEnabled(!state); this->ui->sendAmount->setEnabled(!state); + sendMaxCheckboxDidChange(0); } void DBBDaemonGui::updateModalWithQRCode(const QString& string) @@ -2139,18 +2142,35 @@ void DBBDaemonGui::updateReceivingAddress(DBBWallet *wallet, const std::string & ui->keypathLabel->setText(QString::fromStdString(info)); } +void DBBDaemonGui::sendMaxCheckboxDidChange(int state) +{ + if (this->ui->sendMax->isChecked()) { + this->ui->sendAmount->setText("All funds"); + this->ui->sendAmount->setEnabled(false); + } + else { + this->ui->sendAmount->setEnabled(true); + if ( this->ui->sendAmount->text() == "All funds") { + this->ui->sendAmount->setText(""); + } + } + int64_t fee = singleWallet->client.GetFeeForPriority(this->ui->feeLevel->currentIndex()); + ui->feeInfo->setText(tr("Feerate to use: %1").arg(QString::number(fee/10000000.0))); +} + void DBBDaemonGui::createTxProposalPressed() { if (!singleWallet->client.IsSeeded()) return; int64_t amount = 0; - if (this->ui->sendAmount->text().size() == 0 || !DBB::ParseMoney(this->ui->sendAmount->text().toStdString(), amount)) - return showAlert("Error", "Invalid amount"); - - if (cachedDeviceLock && !comServer->mobileAppConnected) - return showAlert("Error", "2FA enabled but no mobile app found online"); + if (!this->ui->sendMax->isChecked()) { + if (this->ui->sendAmount->text().size() == 0 || !DBB::ParseMoney(this->ui->sendAmount->text().toStdString(), amount)) + return showAlert("Error", "Invalid amount"); + if (cachedDeviceLock && !comServer->mobileAppConnected) + return showAlert("Error", "2FA enabled but no mobile app found online"); + } this->ui->sendToAddress->clearFocus(); this->ui->sendAmount->clearFocus(); DBBNetThread* thread = DBBNetThread::DetachThread(); @@ -2540,6 +2560,7 @@ void DBBDaemonGui::executeNetUpdateWallet(DBBWallet* wallet, bool showLoading, s wallet->shouldUpdateWalletAgain = false; walletsAvailable = wallet->client.GetWallets(walletsResponse); wallet->client.GetFeeLevels(); + this->sendMaxCheckboxDidChange(0); std::string txHistoryResponse; if (wallet == this->singleWallet) { bool transactionHistoryAvailable = wallet->client.GetTransactionHistory(txHistoryResponse); diff --git a/src/qt/dbb_gui.h b/src/qt/dbb_gui.h index 9437cfb1..2380d90c 100644 --- a/src/qt/dbb_gui.h +++ b/src/qt/dbb_gui.h @@ -359,6 +359,7 @@ private slots: //!gets called when a new address is available void updateReceivingAddress(DBBWallet *wallet, const std::string &newAddress, const std::string &keypath); //!check the UI values and create a payment proposal from them, sign and post them + void sendMaxCheckboxDidChange(int val); void createTxProposalPressed(); //!Report about a submitted payment proposal void reportPaymentProposalPost(DBBWallet* wallet, const UniValue& proposal); diff --git a/src/qt/ui/overview.ui b/src/qt/ui/overview.ui index 71e651e5..d0de6251 100644 --- a/src/qt/ui/overview.ui +++ b/src/qt/ui/overview.ui @@ -1270,6 +1270,27 @@ background-color: rgba(240,240,240,255); + + + + 10 + + + + + Fee + + + + + + + Send all available funds + + + + +