aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet2.cpp
diff options
context:
space:
mode:
authormoneromooo-monero <moneromooo-monero@users.noreply.github.com>2019-05-10 17:20:20 +0000
committermoneromooo-monero <moneromooo-monero@users.noreply.github.com>2019-09-05 11:19:34 +0000
commit2ec455df1f82148733c4ac268d22e9c51a690b14 (patch)
treedd0fe1637a7ba7a060b1a97966585bd9d6b87a8f /src/wallet/wallet2.cpp
parentMerge pull request #5827 (diff)
downloadmonero-2ec455df1f82148733c4ac268d22e9c51a690b14.tar.xz
wallet: fix mismatch between two concepts of "balance"
One considers the blockchain, while the other considers the blockchain and some recent actions, such as a recently created transaction which spend some outputs, but isn't yet mined. Typically, the "balance" command wants the latter, to reflect the recent action, but things like proving ownership wants the former. This fixes a crash in get_reserve_proof, where a preliminary check and the main code used two concepts of "balance".
Diffstat (limited to 'src/wallet/wallet2.cpp')
-rw-r--r--src/wallet/wallet2.cpp92
1 files changed, 58 insertions, 34 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 9782e4b1e..16ab725aa 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -1523,6 +1523,7 @@ bool wallet2::is_deprecated() const
//----------------------------------------------------------------------------------------------------
void wallet2::set_spent(size_t idx, uint64_t height)
{
+ CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid index");
transfer_details &td = m_transfers[idx];
LOG_PRINT_L2("Setting SPENT at " << height << ": ki " << td.m_key_image << ", amount " << print_money(td.m_amount));
td.m_spent = true;
@@ -1531,12 +1532,32 @@ void wallet2::set_spent(size_t idx, uint64_t height)
//----------------------------------------------------------------------------------------------------
void wallet2::set_unspent(size_t idx)
{
+ CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid index");
transfer_details &td = m_transfers[idx];
LOG_PRINT_L2("Setting UNSPENT: ki " << td.m_key_image << ", amount " << print_money(td.m_amount));
td.m_spent = false;
td.m_spent_height = 0;
}
//----------------------------------------------------------------------------------------------------
+bool wallet2::is_spent(const transfer_details &td, bool strict) const
+{
+ if (strict)
+ {
+ return td.m_spent && td.m_spent_height > 0;
+ }
+ else
+ {
+ return td.m_spent;
+ }
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::is_spent(size_t idx, bool strict) const
+{
+ CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid index");
+ const transfer_details &td = m_transfers[idx];
+ return is_spent(td, strict);
+}
+//----------------------------------------------------------------------------------------------------
void wallet2::freeze(size_t idx)
{
CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid transfer_details index");
@@ -3274,7 +3295,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
m_first_refresh_done = true;
- LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", balance (all accounts): " << print_money(balance_all()) << ", unlocked: " << print_money(unlocked_balance_all()));
+ LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", balance (all accounts): " << print_money(balance_all(false)) << ", unlocked: " << print_money(unlocked_balance_all(false)));
}
//----------------------------------------------------------------------------------------------------
bool wallet2::refresh(bool trusted_daemon, uint64_t & blocks_fetched, bool& received_money, bool& ok)
@@ -5562,24 +5583,24 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::balance(uint32_t index_major) const
+uint64_t wallet2::balance(uint32_t index_major, bool strict) const
{
uint64_t amount = 0;
if(m_light_wallet)
return m_light_wallet_unlocked_balance;
- for (const auto& i : balance_per_subaddress(index_major))
+ for (const auto& i : balance_per_subaddress(index_major, strict))
amount += i.second;
return amount;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::unlocked_balance(uint32_t index_major, uint64_t *blocks_to_unlock) const
+uint64_t wallet2::unlocked_balance(uint32_t index_major, bool strict, uint64_t *blocks_to_unlock) const
{
uint64_t amount = 0;
if (blocks_to_unlock)
*blocks_to_unlock = 0;
if(m_light_wallet)
return m_light_wallet_balance;
- for (const auto& i : unlocked_balance_per_subaddress(index_major))
+ for (const auto& i : unlocked_balance_per_subaddress(index_major, strict))
{
amount += i.second.first;
if (blocks_to_unlock && i.second.second > *blocks_to_unlock)
@@ -5588,12 +5609,12 @@ uint64_t wallet2::unlocked_balance(uint32_t index_major, uint64_t *blocks_to_unl
return amount;
}
//----------------------------------------------------------------------------------------------------
-std::map<uint32_t, uint64_t> wallet2::balance_per_subaddress(uint32_t index_major) const
+std::map<uint32_t, uint64_t> wallet2::balance_per_subaddress(uint32_t index_major, bool strict) const
{
std::map<uint32_t, uint64_t> amount_per_subaddr;
for (const auto& td: m_transfers)
{
- if (td.m_subaddr_index.major == index_major && !td.m_spent && !td.m_frozen)
+ if (td.m_subaddr_index.major == index_major && !is_spent(td, strict) && !td.m_frozen)
{
auto found = amount_per_subaddr.find(td.m_subaddr_index.minor);
if (found == amount_per_subaddr.end())
@@ -5602,8 +5623,10 @@ std::map<uint32_t, uint64_t> wallet2::balance_per_subaddress(uint32_t index_majo
found->second += td.amount();
}
}
- for (const auto& utx: m_unconfirmed_txs)
+ if (!strict)
{
+ for (const auto& utx: m_unconfirmed_txs)
+ {
if (utx.second.m_subaddr_account == index_major && utx.second.m_state != wallet2::unconfirmed_transfer_details::failed)
{
// all changes go to 0-th subaddress (in the current subaddress account)
@@ -5613,17 +5636,18 @@ std::map<uint32_t, uint64_t> wallet2::balance_per_subaddress(uint32_t index_majo
else
found->second += utx.second.m_change;
}
+ }
}
return amount_per_subaddr;
}
//----------------------------------------------------------------------------------------------------
-std::map<uint32_t, std::pair<uint64_t, uint64_t>> wallet2::unlocked_balance_per_subaddress(uint32_t index_major) const
+std::map<uint32_t, std::pair<uint64_t, uint64_t>> wallet2::unlocked_balance_per_subaddress(uint32_t index_major, bool strict) const
{
std::map<uint32_t, std::pair<uint64_t, uint64_t>> amount_per_subaddr;
const uint64_t blockchain_height = get_blockchain_current_height();
for(const transfer_details& td: m_transfers)
{
- if(td.m_subaddr_index.major == index_major && !td.m_spent && !td.m_frozen)
+ if(td.m_subaddr_index.major == index_major && !is_spent(td, strict) && !td.m_frozen)
{
uint64_t amount = 0, blocks_to_unlock = 0;
if (is_transfer_unlocked(td))
@@ -5652,15 +5676,15 @@ std::map<uint32_t, std::pair<uint64_t, uint64_t>> wallet2::unlocked_balance_per_
return amount_per_subaddr;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::balance_all() const
+uint64_t wallet2::balance_all(bool strict) const
{
uint64_t r = 0;
for (uint32_t index_major = 0; index_major < get_num_subaddress_accounts(); ++index_major)
- r += balance(index_major);
+ r += balance(index_major, strict);
return r;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::unlocked_balance_all(uint64_t *blocks_to_unlock) const
+uint64_t wallet2::unlocked_balance_all(bool strict, uint64_t *blocks_to_unlock) const
{
uint64_t r = 0;
if (blocks_to_unlock)
@@ -5668,7 +5692,7 @@ uint64_t wallet2::unlocked_balance_all(uint64_t *blocks_to_unlock) const
for (uint32_t index_major = 0; index_major < get_num_subaddress_accounts(); ++index_major)
{
uint64_t local_blocks_to_unlock;
- r += unlocked_balance(index_major, blocks_to_unlock ? &local_blocks_to_unlock : NULL);
+ r += unlocked_balance(index_major, strict, blocks_to_unlock ? &local_blocks_to_unlock : NULL);
if (blocks_to_unlock)
*blocks_to_unlock = std::max(*blocks_to_unlock, local_blocks_to_unlock);
}
@@ -6145,8 +6169,8 @@ void wallet2::commit_tx(pending_tx& ptx)
//fee includes dust if dust policy specified it.
LOG_PRINT_L1("Transaction successfully sent. <" << txid << ">" << ENDL
<< "Commission: " << print_money(ptx.fee) << " (dust sent to dust addr: " << print_money((ptx.dust_added_to_fee ? 0 : ptx.dust)) << ")" << ENDL
- << "Balance: " << print_money(balance(ptx.construction_data.subaddr_account)) << ENDL
- << "Unlocked: " << print_money(unlocked_balance(ptx.construction_data.subaddr_account)) << ENDL
+ << "Balance: " << print_money(balance(ptx.construction_data.subaddr_account, false)) << ENDL
+ << "Unlocked: " << print_money(unlocked_balance(ptx.construction_data.subaddr_account, false)) << ENDL
<< "Please, wait for confirmation for your balance to be unlocked.");
}
@@ -8529,7 +8553,7 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
for (size_t i = 0; i < m_transfers.size(); ++i)
{
const transfer_details& td = m_transfers[i];
- if (!td.m_spent && !td.m_frozen && td.is_rct() && td.amount() >= needed_money && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
+ if (!is_spent(td, false) && !td.m_frozen && td.is_rct() && td.amount() >= needed_money && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
{
LOG_PRINT_L2("We can use " << i << " alone: " << print_money(td.amount()));
picks.push_back(i);
@@ -8544,13 +8568,13 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
for (size_t i = 0; i < m_transfers.size(); ++i)
{
const transfer_details& td = m_transfers[i];
- if (!td.m_spent && !td.m_frozen && !td.m_key_image_partial && td.is_rct() && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
+ if (!is_spent(td, false) && !td.m_frozen && !td.m_key_image_partial && td.is_rct() && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
{
LOG_PRINT_L2("Considering input " << i << ", " << print_money(td.amount()));
for (size_t j = i + 1; j < m_transfers.size(); ++j)
{
const transfer_details& td2 = m_transfers[j];
- if (!td2.m_spent && !td2.m_frozen && !td.m_key_image_partial && td2.is_rct() && td.amount() + td2.amount() >= needed_money && is_transfer_unlocked(td2) && td2.m_subaddr_index == td.m_subaddr_index)
+ if (!is_spent(td2, false) && !td2.m_frozen && !td.m_key_image_partial && td2.is_rct() && td.amount() + td2.amount() >= needed_money && is_transfer_unlocked(td2) && td2.m_subaddr_index == td.m_subaddr_index)
{
// update our picks if those outputs are less related than any we
// already found. If the same, don't update, and oldest suitable outputs
@@ -9205,8 +9229,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// throw if attempting a transaction with no money
THROW_WALLET_EXCEPTION_IF(needed_money == 0, error::zero_destination);
- std::map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account);
- std::map<uint32_t, uint64_t> balance_per_subaddr = balance_per_subaddress(subaddr_account);
+ std::map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account, false);
+ std::map<uint32_t, uint64_t> balance_per_subaddr = balance_per_subaddress(subaddr_account, false);
if (subaddr_indices.empty()) // "index=<N1>[,<N2>,...]" wasn't specified -> use all the indices with non-zero unlocked balance
{
@@ -9252,7 +9276,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below threshold " << print_money(fractional_threshold));
continue;
}
- if (!td.m_spent && !td.m_frozen && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
+ if (!is_spent(td, false) && !td.m_frozen && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
{
const uint32_t index_minor = td.m_subaddr_index.minor;
auto find_predicate = [&index_minor](const std::pair<uint32_t, std::vector<size_t>>& x) { return x.first == index_minor; };
@@ -9379,7 +9403,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// if we need to spend money and don't have any left, we fail
if (unused_dust_indices->empty() && unused_transfers_indices->empty()) {
LOG_PRINT_L2("No more outputs to choose from");
- THROW_WALLET_EXCEPTION_IF(1, error::tx_not_possible, unlocked_balance(subaddr_account), needed_money, accumulated_fee + needed_fee);
+ THROW_WALLET_EXCEPTION_IF(1, error::tx_not_possible, unlocked_balance(subaddr_account, false), needed_money, accumulated_fee + needed_fee);
}
// get a random unspent output and use it to pay part (or all) of the current destination (and maybe next one, etc)
@@ -9597,7 +9621,7 @@ skip_tx:
if (adding_fee)
{
LOG_PRINT_L1("We ran out of outputs while trying to gather final fee");
- THROW_WALLET_EXCEPTION_IF(1, error::tx_not_possible, unlocked_balance(subaddr_account), needed_money, accumulated_fee + needed_fee);
+ THROW_WALLET_EXCEPTION_IF(1, error::tx_not_possible, unlocked_balance(subaddr_account, false), needed_money, accumulated_fee + needed_fee);
}
LOG_PRINT_L1("Done creating " << txes.size() << " transactions, " << print_money(accumulated_fee) <<
@@ -9732,7 +9756,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
std::vector<size_t> unused_dust_indices;
const bool use_rct = use_fork_rules(4, 0);
- THROW_WALLET_EXCEPTION_IF(unlocked_balance(subaddr_account) == 0, error::wallet_internal_error, "No unlocked balance in the entire wallet");
+ THROW_WALLET_EXCEPTION_IF(unlocked_balance(subaddr_account, false) == 0, error::wallet_internal_error, "No unlocked balance in the entire wallet");
std::map<uint32_t, std::pair<std::vector<size_t>, std::vector<size_t>>> unused_transfer_dust_indices_per_subaddr;
@@ -9741,7 +9765,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
for (size_t i = 0; i < m_transfers.size(); ++i)
{
const transfer_details& td = m_transfers[i];
- if (!td.m_spent && !td.m_frozen && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && (subaddr_indices.empty() || subaddr_indices.count(td.m_subaddr_index.minor) == 1))
+ if (!is_spent(td, false) && !td.m_frozen && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && (subaddr_indices.empty() || subaddr_indices.count(td.m_subaddr_index.minor) == 1))
{
fund_found = true;
if (below == 0 || td.amount() < below)
@@ -9789,7 +9813,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_single(const crypt
for (size_t i = 0; i < m_transfers.size(); ++i)
{
const transfer_details& td = m_transfers[i];
- if (td.m_key_image_known && td.m_key_image == ki && !td.m_spent && !td.m_frozen && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td))
+ if (td.m_key_image_known && td.m_key_image == ki && !is_spent(td, false) && !td.m_frozen && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td))
{
if (td.is_rct() || is_valid_decomposed_amount(td.amount()))
unused_transfers_indices.push_back(i);
@@ -10155,7 +10179,7 @@ std::vector<size_t> wallet2::select_available_outputs(const std::function<bool(c
size_t n = 0;
for (transfer_container::const_iterator i = m_transfers.begin(); i != m_transfers.end(); ++i, ++n)
{
- if (i->m_spent)
+ if (is_spent(*i, false))
continue;
if (i->m_frozen)
continue;
@@ -10169,12 +10193,12 @@ std::vector<size_t> wallet2::select_available_outputs(const std::function<bool(c
return outputs;
}
//----------------------------------------------------------------------------------------------------
-std::vector<uint64_t> wallet2::get_unspent_amounts_vector() const
+std::vector<uint64_t> wallet2::get_unspent_amounts_vector(bool strict) const
{
std::set<uint64_t> set;
for (const auto &td: m_transfers)
{
- if (!td.m_spent && !td.m_frozen)
+ if (!is_spent(td, strict) && !td.m_frozen)
set.insert(td.is_rct() ? 0 : td.amount());
}
std::vector<uint64_t> vector;
@@ -10192,7 +10216,7 @@ std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t co
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response resp_t = AUTO_VAL_INIT(resp_t);
m_daemon_rpc_mutex.lock();
if (is_trusted_daemon())
- req_t.amounts = get_unspent_amounts_vector();
+ req_t.amounts = get_unspent_amounts_vector(false);
req_t.min_count = count;
req_t.max_count = 0;
req_t.unlocked = unlocked;
@@ -11090,8 +11114,8 @@ bool wallet2::check_tx_proof(const cryptonote::transaction &tx, const cryptonote
std::string wallet2::get_reserve_proof(const boost::optional<std::pair<uint32_t, uint64_t>> &account_minreserve, const std::string &message)
{
THROW_WALLET_EXCEPTION_IF(m_watch_only || m_multisig, error::wallet_internal_error, "Reserve proof can only be generated by a full wallet");
- THROW_WALLET_EXCEPTION_IF(balance_all() == 0, error::wallet_internal_error, "Zero balance");
- THROW_WALLET_EXCEPTION_IF(account_minreserve && balance(account_minreserve->first) < account_minreserve->second, error::wallet_internal_error,
+ THROW_WALLET_EXCEPTION_IF(balance_all(true) == 0, error::wallet_internal_error, "Zero balance");
+ THROW_WALLET_EXCEPTION_IF(account_minreserve && balance(account_minreserve->first, true) < account_minreserve->second, error::wallet_internal_error,
"Not enough balance in this account for the requested minimum reserve amount");
// determine which outputs to include in the proof
@@ -11099,7 +11123,7 @@ std::string wallet2::get_reserve_proof(const boost::optional<std::pair<uint32_t,
for (size_t i = 0; i < m_transfers.size(); ++i)
{
const transfer_details &td = m_transfers[i];
- if (!td.m_spent && !td.m_frozen && (!account_minreserve || account_minreserve->first == td.m_subaddr_index.major))
+ if (!is_spent(td, true) && !td.m_frozen && (!account_minreserve || account_minreserve->first == td.m_subaddr_index.major))
selected_transfers.push_back(i);
}