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".
This commit is contained in:
moneromooo-monero 2019-05-10 17:20:20 +00:00
parent cdfa2e58df
commit 2ec455df1f
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3
7 changed files with 94 additions and 63 deletions

View file

@ -5220,14 +5220,14 @@ bool simple_wallet::show_balance_unlocked(bool detailed)
const std::string tag = m_wallet->get_account_tags().second[m_current_subaddress_account]; const std::string tag = m_wallet->get_account_tags().second[m_current_subaddress_account];
success_msg_writer() << tr("Tag: ") << (tag.empty() ? std::string{tr("(No tag assigned)")} : tag); success_msg_writer() << tr("Tag: ") << (tag.empty() ? std::string{tr("(No tag assigned)")} : tag);
uint64_t blocks_to_unlock; uint64_t blocks_to_unlock;
uint64_t unlocked_balance = m_wallet->unlocked_balance(m_current_subaddress_account, &blocks_to_unlock); uint64_t unlocked_balance = m_wallet->unlocked_balance(m_current_subaddress_account, false, &blocks_to_unlock);
std::string unlock_time_message; std::string unlock_time_message;
if (blocks_to_unlock > 0) if (blocks_to_unlock > 0)
unlock_time_message = (boost::format(" (%lu block(s) to unlock)") % blocks_to_unlock).str(); unlock_time_message = (boost::format(" (%lu block(s) to unlock)") % blocks_to_unlock).str();
success_msg_writer() << tr("Balance: ") << print_money(m_wallet->balance(m_current_subaddress_account)) << ", " success_msg_writer() << tr("Balance: ") << print_money(m_wallet->balance(m_current_subaddress_account, false)) << ", "
<< tr("unlocked balance: ") << print_money(unlocked_balance) << unlock_time_message << extra; << tr("unlocked balance: ") << print_money(unlocked_balance) << unlock_time_message << extra;
std::map<uint32_t, uint64_t> balance_per_subaddress = m_wallet->balance_per_subaddress(m_current_subaddress_account); std::map<uint32_t, uint64_t> balance_per_subaddress = m_wallet->balance_per_subaddress(m_current_subaddress_account, false);
std::map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddress = m_wallet->unlocked_balance_per_subaddress(m_current_subaddress_account); std::map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddress = m_wallet->unlocked_balance_per_subaddress(m_current_subaddress_account, false);
if (!detailed || balance_per_subaddress.empty()) if (!detailed || balance_per_subaddress.empty())
return true; return true;
success_msg_writer() << tr("Balance per address:"); success_msg_writer() << tr("Balance per address:");
@ -8536,7 +8536,7 @@ void simple_wallet::print_accounts()
print_accounts(""); print_accounts("");
if (num_untagged_accounts < m_wallet->get_num_subaddress_accounts()) if (num_untagged_accounts < m_wallet->get_num_subaddress_accounts())
success_msg_writer() << tr("\nGrand total:\n Balance: ") << print_money(m_wallet->balance_all()) << tr(", unlocked balance: ") << print_money(m_wallet->unlocked_balance_all()); success_msg_writer() << tr("\nGrand total:\n Balance: ") << print_money(m_wallet->balance_all(false)) << tr(", unlocked balance: ") << print_money(m_wallet->unlocked_balance_all(false));
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void simple_wallet::print_accounts(const std::string& tag) void simple_wallet::print_accounts(const std::string& tag)
@ -8566,11 +8566,11 @@ void simple_wallet::print_accounts(const std::string& tag)
% (m_current_subaddress_account == account_index ? '*' : ' ') % (m_current_subaddress_account == account_index ? '*' : ' ')
% account_index % account_index
% m_wallet->get_subaddress_as_str({account_index, 0}).substr(0, 6) % m_wallet->get_subaddress_as_str({account_index, 0}).substr(0, 6)
% print_money(m_wallet->balance(account_index)) % print_money(m_wallet->balance(account_index, false))
% print_money(m_wallet->unlocked_balance(account_index)) % print_money(m_wallet->unlocked_balance(account_index, false))
% m_wallet->get_subaddress_label({account_index, 0}); % m_wallet->get_subaddress_label({account_index, 0});
total_balance += m_wallet->balance(account_index); total_balance += m_wallet->balance(account_index, false);
total_unlocked_balance += m_wallet->unlocked_balance(account_index); total_unlocked_balance += m_wallet->unlocked_balance(account_index, false);
} }
success_msg_writer() << tr("----------------------------------------------------------------------------------"); success_msg_writer() << tr("----------------------------------------------------------------------------------");
success_msg_writer() << boost::format(tr("%15s %21s %21s")) % "Total" % print_money(total_balance) % print_money(total_unlocked_balance); success_msg_writer() << boost::format(tr("%15s %21s %21s")) % "Total" % print_money(total_balance) % print_money(total_unlocked_balance);

View file

@ -1523,6 +1523,7 @@ bool wallet2::is_deprecated() const
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void wallet2::set_spent(size_t idx, uint64_t height) 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]; transfer_details &td = m_transfers[idx];
LOG_PRINT_L2("Setting SPENT at " << height << ": ki " << td.m_key_image << ", amount " << print_money(td.m_amount)); LOG_PRINT_L2("Setting SPENT at " << height << ": ki " << td.m_key_image << ", amount " << print_money(td.m_amount));
td.m_spent = true; 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) void wallet2::set_unspent(size_t idx)
{ {
CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid index");
transfer_details &td = m_transfers[idx]; transfer_details &td = m_transfers[idx];
LOG_PRINT_L2("Setting UNSPENT: ki " << td.m_key_image << ", amount " << print_money(td.m_amount)); LOG_PRINT_L2("Setting UNSPENT: ki " << td.m_key_image << ", amount " << print_money(td.m_amount));
td.m_spent = false; td.m_spent = false;
td.m_spent_height = 0; 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) void wallet2::freeze(size_t idx)
{ {
CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid transfer_details index"); 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; 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) 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; uint64_t amount = 0;
if(m_light_wallet) if(m_light_wallet)
return m_light_wallet_unlocked_balance; 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; amount += i.second;
return amount; 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; uint64_t amount = 0;
if (blocks_to_unlock) if (blocks_to_unlock)
*blocks_to_unlock = 0; *blocks_to_unlock = 0;
if(m_light_wallet) if(m_light_wallet)
return m_light_wallet_balance; 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; amount += i.second.first;
if (blocks_to_unlock && i.second.second > *blocks_to_unlock) 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; 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; std::map<uint32_t, uint64_t> amount_per_subaddr;
for (const auto& td: m_transfers) 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); auto found = amount_per_subaddr.find(td.m_subaddr_index.minor);
if (found == amount_per_subaddr.end()) if (found == amount_per_subaddr.end())
@ -5602,6 +5623,8 @@ std::map<uint32_t, uint64_t> wallet2::balance_per_subaddress(uint32_t index_majo
found->second += td.amount(); found->second += td.amount();
} }
} }
if (!strict)
{
for (const auto& utx: m_unconfirmed_txs) for (const auto& utx: m_unconfirmed_txs)
{ {
if (utx.second.m_subaddr_account == index_major && utx.second.m_state != wallet2::unconfirmed_transfer_details::failed) if (utx.second.m_subaddr_account == index_major && utx.second.m_state != wallet2::unconfirmed_transfer_details::failed)
@ -5614,16 +5637,17 @@ std::map<uint32_t, uint64_t> wallet2::balance_per_subaddress(uint32_t index_majo
found->second += utx.second.m_change; found->second += utx.second.m_change;
} }
} }
}
return amount_per_subaddr; 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; std::map<uint32_t, std::pair<uint64_t, uint64_t>> amount_per_subaddr;
const uint64_t blockchain_height = get_blockchain_current_height(); const uint64_t blockchain_height = get_blockchain_current_height();
for(const transfer_details& td: m_transfers) 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; uint64_t amount = 0, blocks_to_unlock = 0;
if (is_transfer_unlocked(td)) 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; return amount_per_subaddr;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
uint64_t wallet2::balance_all() const uint64_t wallet2::balance_all(bool strict) const
{ {
uint64_t r = 0; uint64_t r = 0;
for (uint32_t index_major = 0; index_major < get_num_subaddress_accounts(); ++index_major) 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; 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; uint64_t r = 0;
if (blocks_to_unlock) 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) for (uint32_t index_major = 0; index_major < get_num_subaddress_accounts(); ++index_major)
{ {
uint64_t local_blocks_to_unlock; 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) if (blocks_to_unlock)
*blocks_to_unlock = std::max(*blocks_to_unlock, local_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. //fee includes dust if dust policy specified it.
LOG_PRINT_L1("Transaction successfully sent. <" << txid << ">" << ENDL 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 << "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 << "Balance: " << print_money(balance(ptx.construction_data.subaddr_account, false)) << ENDL
<< "Unlocked: " << print_money(unlocked_balance(ptx.construction_data.subaddr_account)) << ENDL << "Unlocked: " << print_money(unlocked_balance(ptx.construction_data.subaddr_account, false)) << ENDL
<< "Please, wait for confirmation for your balance to be unlocked."); << "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) for (size_t i = 0; i < m_transfers.size(); ++i)
{ {
const transfer_details& td = m_transfers[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())); LOG_PRINT_L2("We can use " << i << " alone: " << print_money(td.amount()));
picks.push_back(i); 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) for (size_t i = 0; i < m_transfers.size(); ++i)
{ {
const transfer_details& td = m_transfers[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())); LOG_PRINT_L2("Considering input " << i << ", " << print_money(td.amount()));
for (size_t j = i + 1; j < m_transfers.size(); ++j) for (size_t j = i + 1; j < m_transfers.size(); ++j)
{ {
const transfer_details& td2 = m_transfers[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 // update our picks if those outputs are less related than any we
// already found. If the same, don't update, and oldest suitable outputs // 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 if attempting a transaction with no money
THROW_WALLET_EXCEPTION_IF(needed_money == 0, error::zero_destination); 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, 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); 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 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)); MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below threshold " << print_money(fractional_threshold));
continue; 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; 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; }; 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 we need to spend money and don't have any left, we fail
if (unused_dust_indices->empty() && unused_transfers_indices->empty()) { if (unused_dust_indices->empty() && unused_transfers_indices->empty()) {
LOG_PRINT_L2("No more outputs to choose from"); 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) // 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) if (adding_fee)
{ {
LOG_PRINT_L1("We ran out of outputs while trying to gather final 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) << 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; std::vector<size_t> unused_dust_indices;
const bool use_rct = use_fork_rules(4, 0); 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; 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) for (size_t i = 0; i < m_transfers.size(); ++i)
{ {
const transfer_details& td = m_transfers[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; fund_found = true;
if (below == 0 || td.amount() < below) 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) for (size_t i = 0; i < m_transfers.size(); ++i)
{ {
const transfer_details& td = m_transfers[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())) if (td.is_rct() || is_valid_decomposed_amount(td.amount()))
unused_transfers_indices.push_back(i); 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; size_t n = 0;
for (transfer_container::const_iterator i = m_transfers.begin(); i != m_transfers.end(); ++i, ++n) 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; continue;
if (i->m_frozen) if (i->m_frozen)
continue; continue;
@ -10169,12 +10193,12 @@ std::vector<size_t> wallet2::select_available_outputs(const std::function<bool(c
return outputs; 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; std::set<uint64_t> set;
for (const auto &td: m_transfers) 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()); set.insert(td.is_rct() ? 0 : td.amount());
} }
std::vector<uint64_t> vector; 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); cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response resp_t = AUTO_VAL_INIT(resp_t);
m_daemon_rpc_mutex.lock(); m_daemon_rpc_mutex.lock();
if (is_trusted_daemon()) 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.min_count = count;
req_t.max_count = 0; req_t.max_count = 0;
req_t.unlocked = unlocked; 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) 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(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(balance_all(true) == 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(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"); "Not enough balance in this account for the requested minimum reserve amount");
// determine which outputs to include in the proof // 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) for (size_t i = 0; i < m_transfers.size(); ++i)
{ {
const transfer_details &td = m_transfers[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); selected_transfers.push_back(i);
} }

View file

@ -799,14 +799,14 @@ private:
bool reconnect_device(); bool reconnect_device();
// locked & unlocked balance of given or current subaddress account // locked & unlocked balance of given or current subaddress account
uint64_t balance(uint32_t subaddr_index_major) const; uint64_t balance(uint32_t subaddr_index_major, bool strict) const;
uint64_t unlocked_balance(uint32_t subaddr_index_major, uint64_t *blocks_to_unlock = NULL) const; uint64_t unlocked_balance(uint32_t subaddr_index_major, bool strict, uint64_t *blocks_to_unlock = NULL) const;
// locked & unlocked balance per subaddress of given or current subaddress account // locked & unlocked balance per subaddress of given or current subaddress account
std::map<uint32_t, uint64_t> balance_per_subaddress(uint32_t subaddr_index_major) const; std::map<uint32_t, uint64_t> balance_per_subaddress(uint32_t subaddr_index_major, bool strict) const;
std::map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddress(uint32_t subaddr_index_major) const; std::map<uint32_t, std::pair<uint64_t, uint64_t>> unlocked_balance_per_subaddress(uint32_t subaddr_index_major, bool strict) const;
// all locked & unlocked balances of all subaddress accounts // all locked & unlocked balances of all subaddress accounts
uint64_t balance_all() const; uint64_t balance_all(bool strict) const;
uint64_t unlocked_balance_all(uint64_t *blocks_to_unlock = NULL) const; uint64_t unlocked_balance_all(bool strict, uint64_t *blocks_to_unlock = NULL) const;
template<typename T> template<typename T>
void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count, void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
@ -1365,12 +1365,14 @@ private:
void check_acc_out_precomp_once(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info, bool &already_seen) const; void check_acc_out_precomp_once(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info, bool &already_seen) const;
void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const; void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const;
uint64_t get_upper_transaction_weight_limit() const; uint64_t get_upper_transaction_weight_limit() const;
std::vector<uint64_t> get_unspent_amounts_vector() const; std::vector<uint64_t> get_unspent_amounts_vector(bool strict) const;
uint64_t get_dynamic_base_fee_estimate() const; uint64_t get_dynamic_base_fee_estimate() const;
float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const; float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const;
std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices) const; std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices) const;
void set_spent(size_t idx, uint64_t height); void set_spent(size_t idx, uint64_t height);
void set_unspent(size_t idx); void set_unspent(size_t idx);
bool is_spent(const transfer_details &td, bool strict = true) const;
bool is_spent(size_t idx, bool strict = true) const;
void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count); void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count);
bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const; bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const;
bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const; bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const;

View file

@ -419,8 +419,8 @@ namespace tools
if (!m_wallet) return not_open(er); if (!m_wallet) return not_open(er);
try try
{ {
res.balance = req.all_accounts ? m_wallet->balance_all() : m_wallet->balance(req.account_index); res.balance = req.all_accounts ? m_wallet->balance_all(req.strict) : m_wallet->balance(req.account_index, req.strict);
res.unlocked_balance = req.all_accounts ? m_wallet->unlocked_balance_all(&res.blocks_to_unlock) : m_wallet->unlocked_balance(req.account_index, &res.blocks_to_unlock); res.unlocked_balance = req.all_accounts ? m_wallet->unlocked_balance_all(req.strict, &res.blocks_to_unlock) : m_wallet->unlocked_balance(req.account_index, req.strict, &res.blocks_to_unlock);
res.multisig_import_needed = m_wallet->multisig() && m_wallet->has_multisig_partial_key_images(); res.multisig_import_needed = m_wallet->multisig() && m_wallet->has_multisig_partial_key_images();
std::map<uint32_t, std::map<uint32_t, uint64_t>> balance_per_subaddress_per_account; std::map<uint32_t, std::map<uint32_t, uint64_t>> balance_per_subaddress_per_account;
std::map<uint32_t, std::map<uint32_t, std::pair<uint64_t, uint64_t>>> unlocked_balance_per_subaddress_per_account; std::map<uint32_t, std::map<uint32_t, std::pair<uint64_t, uint64_t>>> unlocked_balance_per_subaddress_per_account;
@ -428,14 +428,14 @@ namespace tools
{ {
for (uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index) for (uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index)
{ {
balance_per_subaddress_per_account[account_index] = m_wallet->balance_per_subaddress(account_index); balance_per_subaddress_per_account[account_index] = m_wallet->balance_per_subaddress(account_index, req.strict);
unlocked_balance_per_subaddress_per_account[account_index] = m_wallet->unlocked_balance_per_subaddress(account_index); unlocked_balance_per_subaddress_per_account[account_index] = m_wallet->unlocked_balance_per_subaddress(account_index, req.strict);
} }
} }
else else
{ {
balance_per_subaddress_per_account[req.account_index] = m_wallet->balance_per_subaddress(req.account_index); balance_per_subaddress_per_account[req.account_index] = m_wallet->balance_per_subaddress(req.account_index, req.strict);
unlocked_balance_per_subaddress_per_account[req.account_index] = m_wallet->unlocked_balance_per_subaddress(req.account_index); unlocked_balance_per_subaddress_per_account[req.account_index] = m_wallet->unlocked_balance_per_subaddress(req.account_index, req.strict);
} }
std::vector<tools::wallet2::transfer_details> transfers; std::vector<tools::wallet2::transfer_details> transfers;
m_wallet->get_transfers(transfers); m_wallet->get_transfers(transfers);
@ -593,8 +593,8 @@ namespace tools
wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::subaddress_account_info info; wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::subaddress_account_info info;
info.account_index = subaddr_index.major; info.account_index = subaddr_index.major;
info.base_address = m_wallet->get_subaddress_as_str(subaddr_index); info.base_address = m_wallet->get_subaddress_as_str(subaddr_index);
info.balance = m_wallet->balance(subaddr_index.major); info.balance = m_wallet->balance(subaddr_index.major, req.strict_balances);
info.unlocked_balance = m_wallet->unlocked_balance(subaddr_index.major); info.unlocked_balance = m_wallet->unlocked_balance(subaddr_index.major, req.strict_balances);
info.label = m_wallet->get_subaddress_label(subaddr_index); info.label = m_wallet->get_subaddress_label(subaddr_index);
info.tag = account_tags.second[subaddr_index.major]; info.tag = account_tags.second[subaddr_index.major];
res.subaddress_accounts.push_back(info); res.subaddress_accounts.push_back(info);

View file

@ -64,10 +64,12 @@ namespace wallet_rpc
uint32_t account_index; uint32_t account_index;
std::set<uint32_t> address_indices; std::set<uint32_t> address_indices;
bool all_accounts; bool all_accounts;
bool strict;
BEGIN_KV_SERIALIZE_MAP() BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(account_index) KV_SERIALIZE(account_index)
KV_SERIALIZE(address_indices) KV_SERIALIZE(address_indices)
KV_SERIALIZE_OPT(all_accounts, false); KV_SERIALIZE_OPT(all_accounts, false);
KV_SERIALIZE_OPT(strict, false);
END_KV_SERIALIZE_MAP() END_KV_SERIALIZE_MAP()
}; };
typedef epee::misc_utils::struct_init<request_t> request; typedef epee::misc_utils::struct_init<request_t> request;
@ -230,9 +232,11 @@ namespace wallet_rpc
struct request_t struct request_t
{ {
std::string tag; // all accounts if empty, otherwise those accounts with this tag std::string tag; // all accounts if empty, otherwise those accounts with this tag
bool strict_balances;
BEGIN_KV_SERIALIZE_MAP() BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tag) KV_SERIALIZE(tag)
KV_SERIALIZE_OPT(strict_balances, false)
END_KV_SERIALIZE_MAP() END_KV_SERIALIZE_MAP()
}; };
typedef epee::misc_utils::struct_init<request_t> request; typedef epee::misc_utils::struct_init<request_t> request;

View file

@ -173,7 +173,7 @@ bool transactions_flow_test(std::string& working_folder,
//wait for money, until balance will have enough money //wait for money, until balance will have enough money
w1.refresh(true, blocks_fetched, received_money, ok); w1.refresh(true, blocks_fetched, received_money, ok);
while(w1.unlocked_balance(0) < amount_to_transfer) while(w1.unlocked_balance(0, true) < amount_to_transfer)
{ {
misc_utils::sleep_no_w(1000); misc_utils::sleep_no_w(1000);
w1.refresh(true, blocks_fetched, received_money, ok); w1.refresh(true, blocks_fetched, received_money, ok);
@ -186,7 +186,7 @@ bool transactions_flow_test(std::string& working_folder,
{ {
tools::wallet2::transfer_container incoming_transfers; tools::wallet2::transfer_container incoming_transfers;
w1.get_transfers(incoming_transfers); w1.get_transfers(incoming_transfers);
if(incoming_transfers.size() > FIRST_N_TRANSFERS && get_money_in_first_transfers(incoming_transfers, FIRST_N_TRANSFERS) < w1.unlocked_balance(0) ) if(incoming_transfers.size() > FIRST_N_TRANSFERS && get_money_in_first_transfers(incoming_transfers, FIRST_N_TRANSFERS) < w1.unlocked_balance(0, true) )
{ {
//lets go! //lets go!
size_t count = 0; size_t count = 0;
@ -221,7 +221,7 @@ bool transactions_flow_test(std::string& working_folder,
for(i = 0; i != transactions_count; i++) for(i = 0; i != transactions_count; i++)
{ {
uint64_t amount_to_tx = (amount_to_transfer - transfered_money) > transfer_size ? transfer_size: (amount_to_transfer - transfered_money); uint64_t amount_to_tx = (amount_to_transfer - transfered_money) > transfer_size ? transfer_size: (amount_to_transfer - transfered_money);
while(w1.unlocked_balance(0) < amount_to_tx + TEST_FEE) while(w1.unlocked_balance(0, true) < amount_to_tx + TEST_FEE)
{ {
misc_utils::sleep_no_w(1000); misc_utils::sleep_no_w(1000);
LOG_PRINT_L0("not enough money, waiting for cashback or mining"); LOG_PRINT_L0("not enough money, waiting for cashback or mining");
@ -270,7 +270,7 @@ bool transactions_flow_test(std::string& working_folder,
misc_utils::sleep_no_w(DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN*1000);//wait two blocks before sync on another wallet on another daemon misc_utils::sleep_no_w(DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN*1000);//wait two blocks before sync on another wallet on another daemon
} }
uint64_t money_2 = w2.balance(0); uint64_t money_2 = w2.balance(0, true);
if(money_2 == transfered_money) if(money_2 == transfered_money)
{ {
MGINFO_GREEN("-----------------------FINISHING TRANSACTIONS FLOW TEST OK-----------------------"); MGINFO_GREEN("-----------------------FINISHING TRANSACTIONS FLOW TEST OK-----------------------");

View file

@ -140,13 +140,14 @@ class Wallet(object):
} }
return self.rpc.send_json_rpc_request(create_wallet) return self.rpc.send_json_rpc_request(create_wallet)
def get_balance(self, account_index = 0, address_indices = [], all_accounts = False): def get_balance(self, account_index = 0, address_indices = [], all_accounts = False, strict = False):
get_balance = { get_balance = {
'method': 'get_balance', 'method': 'get_balance',
'params': { 'params': {
'account_index': account_index, 'account_index': account_index,
'address_indices': address_indices, 'address_indices': address_indices,
'all_accounts': all_accounts, 'all_accounts': all_accounts,
'strict': strict,
}, },
'jsonrpc': '2.0', 'jsonrpc': '2.0',
'id': '0' 'id': '0'