2021-11-23 17:45:38 +01:00
|
|
|
/*
|
|
|
|
|
* This file is part of the Flowee project
|
|
|
|
|
* Copyright (C) 2020-2021 Tom Zander <tom@flowee.org>
|
|
|
|
|
*
|
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
|
2021-11-24 16:00:40 +01:00
|
|
|
#include "PaymentDetailInputs_p.h"
|
2021-11-23 17:45:38 +01:00
|
|
|
#include "Wallet.h"
|
2021-11-24 15:40:35 +01:00
|
|
|
#include "Wallet_p.h" // for BYTES_PER_OUTPUT
|
2021-11-23 17:45:38 +01:00
|
|
|
#include "AccountInfo.h"
|
|
|
|
|
|
|
|
|
|
PaymentDetailInputs::PaymentDetailInputs(Payment *parent)
|
|
|
|
|
: PaymentDetail(parent, Payment::InputSelector),
|
|
|
|
|
m_wallet(parent->currentAccount()->wallet()),
|
|
|
|
|
m_model(m_wallet)
|
|
|
|
|
{
|
|
|
|
|
m_model.setSelectionGetter(std::bind(&PaymentDetailInputs::isRowIncluded, this, std::placeholders::_1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PaymentDetailInputs::setWallet(Wallet *wallet)
|
|
|
|
|
{
|
|
|
|
|
if (wallet == m_wallet)
|
|
|
|
|
return;
|
|
|
|
|
m_wallet = wallet;
|
|
|
|
|
m_model.setWallet(m_wallet);
|
|
|
|
|
|
|
|
|
|
emit selectedCountChanged();
|
|
|
|
|
emit selectedValueChanged();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WalletCoinsModel *PaymentDetailInputs::coins()
|
|
|
|
|
{
|
|
|
|
|
return &m_model;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PaymentDetailInputs::setRowIncluded(int row, bool on)
|
|
|
|
|
{
|
|
|
|
|
assert(m_wallet);
|
|
|
|
|
const auto id = m_model.outRefForRow(row);
|
|
|
|
|
auto &selection = m_selectionModels[m_wallet];
|
|
|
|
|
const auto iter = selection.rows.find(id);
|
|
|
|
|
const bool wasThere = iter != selection.rows.end();
|
|
|
|
|
if (on == wasThere) // no change
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const auto value = m_wallet->utxoOutputValue(Wallet::OutputRef(id));
|
|
|
|
|
if (on) {
|
|
|
|
|
assert(!wasThere);
|
|
|
|
|
selection.rows.insert(id);
|
|
|
|
|
selection.selectedValue += value;
|
|
|
|
|
++selection.selectedCount;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
assert(wasThere);
|
|
|
|
|
selection.rows.erase(iter);
|
|
|
|
|
selection.selectedValue -= value;
|
|
|
|
|
--selection.selectedCount;
|
|
|
|
|
}
|
|
|
|
|
emit selectedCountChanged();
|
|
|
|
|
emit selectedValueChanged();
|
|
|
|
|
|
|
|
|
|
m_model.updateRow(row);
|
2021-11-24 14:56:52 +01:00
|
|
|
setValid(selection.selectedValue > 547);
|
|
|
|
|
emit validChanged(); // the Payment needs to do the math as well, if inputs and outputs match
|
2021-11-23 17:45:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool PaymentDetailInputs::isRowIncluded(uint64_t rowId)
|
|
|
|
|
{
|
|
|
|
|
assert(m_wallet);
|
|
|
|
|
auto a = m_selectionModels.find(m_wallet);
|
|
|
|
|
if (a == m_selectionModels.end())
|
|
|
|
|
return false;
|
|
|
|
|
const auto &selection = a->second;
|
|
|
|
|
return selection.rows.find(rowId) != selection.rows.end();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PaymentDetailInputs::setOutputLocked(int row, bool lock)
|
|
|
|
|
{
|
|
|
|
|
assert(m_wallet);
|
|
|
|
|
m_model.setOutputLocked(row, lock);
|
2021-11-24 17:41:41 +01:00
|
|
|
if (lock)
|
|
|
|
|
setRowIncluded(row, false);
|
2021-11-23 17:45:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PaymentDetailInputs::selectAll()
|
|
|
|
|
{
|
|
|
|
|
assert(m_wallet);
|
|
|
|
|
auto &selection = m_selectionModels[m_wallet];
|
|
|
|
|
for (int i = m_model.rowCount() - 1; i >= 0; --i) {
|
|
|
|
|
auto const id = m_model.outRefForRow(i);
|
|
|
|
|
if (selection.rows.find(id) == selection.rows.end()) {
|
|
|
|
|
const Wallet::OutputRef ref(id);
|
|
|
|
|
if (m_wallet->isLocked(ref))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
selection.rows.insert(id);
|
|
|
|
|
m_model.updateRow(i);
|
|
|
|
|
|
|
|
|
|
const auto value = m_wallet->utxoOutputValue(ref);
|
|
|
|
|
selection.selectedValue += value;
|
|
|
|
|
++selection.selectedCount;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
emit selectedCountChanged();
|
|
|
|
|
emit selectedValueChanged();
|
2021-11-24 14:56:52 +01:00
|
|
|
setValid(true);
|
|
|
|
|
emit validChanged(); // the Payment needs to do the math as well, if inputs and outputs match
|
2021-11-23 17:45:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PaymentDetailInputs::unselectAll()
|
|
|
|
|
{
|
|
|
|
|
assert(m_wallet);
|
|
|
|
|
auto a = m_selectionModels.find(m_wallet);
|
|
|
|
|
if (a == m_selectionModels.end())
|
|
|
|
|
return;
|
|
|
|
|
auto selection = a->second;
|
|
|
|
|
m_selectionModels.erase(a);
|
|
|
|
|
for (auto rowId : selection.rows) {
|
|
|
|
|
// tell the model to tell the view the data has changed.
|
|
|
|
|
m_model.updateRow(rowId);
|
|
|
|
|
}
|
|
|
|
|
emit selectedCountChanged();
|
|
|
|
|
emit selectedValueChanged();
|
2021-11-24 14:56:52 +01:00
|
|
|
setValid(false);
|
2021-11-23 17:45:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double PaymentDetailInputs::selectedValue() const
|
|
|
|
|
{
|
|
|
|
|
assert(m_wallet);
|
|
|
|
|
auto a = m_selectionModels.find(m_wallet);
|
|
|
|
|
if (a == m_selectionModels.end())
|
|
|
|
|
return false;
|
|
|
|
|
const auto &selection = a->second;
|
|
|
|
|
return static_cast<double>(selection.selectedValue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int PaymentDetailInputs::selectedCount() const
|
|
|
|
|
{
|
|
|
|
|
auto a = m_selectionModels.find(m_wallet);
|
|
|
|
|
if (a == m_selectionModels.end())
|
|
|
|
|
return 0;
|
|
|
|
|
const auto &selection = a->second;
|
|
|
|
|
return selection.selectedCount;
|
|
|
|
|
}
|
2021-11-24 15:40:35 +01:00
|
|
|
|
|
|
|
|
Wallet::OutputSet PaymentDetailInputs::selectedInputs(int64_t output, int feePerByte, int txSize, int64_t &change) const
|
|
|
|
|
{
|
|
|
|
|
Wallet::OutputSet answer;
|
|
|
|
|
assert(m_wallet);
|
|
|
|
|
auto a = m_selectionModels.find(m_wallet);
|
|
|
|
|
if (a == m_selectionModels.end())
|
|
|
|
|
return answer;
|
|
|
|
|
const auto &selection = a->second;
|
|
|
|
|
int fee = feePerByte * (txSize + BYTES_PER_OUTPUT * selection.rows.size());
|
|
|
|
|
change = 0;
|
|
|
|
|
if (output != -1) { // an output of -1 means that there was an ouput that takes 'the rest', leaving no change.
|
|
|
|
|
change = selection.selectedValue - output - fee;
|
|
|
|
|
if (change < 0) // return an empty answer if the input can't pay the outputs plus expected fees.
|
|
|
|
|
return answer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
answer.totalSats = selection.selectedValue;
|
|
|
|
|
answer.outputs.reserve(selection.rows.size());
|
|
|
|
|
for (auto ref : selection.rows) {
|
|
|
|
|
answer.outputs.push_back(Wallet::OutputRef(ref));
|
|
|
|
|
}
|
|
|
|
|
return answer;
|
|
|
|
|
}
|