From cd1eaff29e664efa9c99939888a7bad74deee4a0 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 16 Mar 2019 13:17:11 +0000 Subject: [PATCH 1/4] wallet_rpc_server: always fill out subaddr_indices in get_transfers It was not filled out for in and pool types --- src/wallet/wallet_rpc_server.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index f80263007..c1ccffd6d 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -302,6 +302,7 @@ namespace tools entry.note = m_wallet->get_tx_note(pd.m_tx_hash); entry.type = pd.m_coinbase ? "block" : "in"; entry.subaddr_index = pd.m_subaddr_index; + entry.subaddr_indices.push_back(pd.m_subaddr_index); entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index); set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward()); } @@ -373,6 +374,7 @@ namespace tools entry.double_spend_seen = ppd.m_double_spend_seen; entry.type = "pool"; entry.subaddr_index = pd.m_subaddr_index; + entry.subaddr_indices.push_back(pd.m_subaddr_index); entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index); set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward()); } From 36c037ec4748d26fdc8f95b60f758ad34c7aba9f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 19 Mar 2019 01:07:46 +0000 Subject: [PATCH 2/4] wallet_rpc_server: error out on getting the spend key from a hot wallet --- src/wallet/wallet_rpc_server.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index c1ccffd6d..aa3169c97 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1832,7 +1832,7 @@ namespace tools if (m_wallet->watch_only()) { er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY; - er.message = "The wallet is watch-only. Cannot display seed."; + er.message = "The wallet is watch-only. Cannot retrieve seed."; return false; } if (!m_wallet->is_deterministic()) @@ -1857,6 +1857,12 @@ namespace tools } else if(req.key_type.compare("spend_key") == 0) { + if (m_wallet->watch_only()) + { + er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY; + er.message = "The wallet is watch-only. Cannot retrieve spend key."; + return false; + } epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_spend_secret_key); res.key = std::string(key.data(), key.size()); } From 3f1e9e84c0e8259154ad29fb5d8172d9c2e2c60e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 21 Mar 2019 00:22:11 +0000 Subject: [PATCH 3/4] wallet2: set confirmations to 0 for pool txes in proofs It makes more sense than (uint64_t)-1, which is going to look like very much confirmed when not checking in_pool --- src/wallet/wallet2.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index d59ded49f..60b940163 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -10573,13 +10573,13 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de check_tx_key_helper(tx, derivation, additional_derivations, address, received); in_pool = res.txs.front().in_pool; - confirmations = (uint64_t)-1; + confirmations = 0; if (!in_pool) { std::string err; uint64_t bc_height = get_daemon_blockchain_height(err); if (err.empty()) - confirmations = bc_height - (res.txs.front().block_height + 1); + confirmations = bc_height - res.txs.front().block_height; } } @@ -10775,13 +10775,13 @@ bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account return false; in_pool = res.txs.front().in_pool; - confirmations = (uint64_t)-1; + confirmations = 0; if (!in_pool) { std::string err; uint64_t bc_height = get_daemon_blockchain_height(err); if (err.empty()) - confirmations = bc_height - (res.txs.front().block_height + 1); + confirmations = bc_height - res.txs.front().block_height; } return true; From c12b43cb5acc5bb382f73f6cf1f66e9ad52eebb4 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 21 Mar 2019 23:20:46 +0000 Subject: [PATCH 4/4] wallet: add number of blocks required for the balance to fully unlock --- src/simplewallet/simplewallet.cpp | 11 ++-- src/wallet/wallet2.cpp | 55 +++++++++++++++----- src/wallet/wallet2.h | 6 +-- src/wallet/wallet_rpc_server.cpp | 9 ++-- src/wallet/wallet_rpc_server_commands_defs.h | 6 ++- 5 files changed, 64 insertions(+), 23 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 8974bd1e0..5dd189c59 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -4948,10 +4948,15 @@ bool simple_wallet::show_balance_unlocked(bool detailed) success_msg_writer() << tr("Currently selected account: [") << m_current_subaddress_account << tr("] ") << m_wallet->get_subaddress_label({m_current_subaddress_account, 0}); 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); + uint64_t blocks_to_unlock; + uint64_t unlocked_balance = m_wallet->unlocked_balance(m_current_subaddress_account, &blocks_to_unlock); + std::string unlock_time_message; + if (blocks_to_unlock > 0) + 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)) << ", " - << tr("unlocked balance: ") << print_money(m_wallet->unlocked_balance(m_current_subaddress_account)) << extra; + << tr("unlocked balance: ") << print_money(unlocked_balance) << unlock_time_message << extra; std::map balance_per_subaddress = m_wallet->balance_per_subaddress(m_current_subaddress_account); - std::map unlocked_balance_per_subaddress = m_wallet->unlocked_balance_per_subaddress(m_current_subaddress_account); + std::map> unlocked_balance_per_subaddress = m_wallet->unlocked_balance_per_subaddress(m_current_subaddress_account); if (!detailed || balance_per_subaddress.empty()) return true; success_msg_writer() << tr("Balance per address:"); @@ -4963,7 +4968,7 @@ bool simple_wallet::show_balance_unlocked(bool detailed) cryptonote::subaddress_index subaddr_index = {m_current_subaddress_account, i.first}; std::string address_str = m_wallet->get_subaddress_as_str(subaddr_index).substr(0, 6); uint64_t num_unspent_outputs = std::count_if(transfers.begin(), transfers.end(), [&subaddr_index](const tools::wallet2::transfer_details& td) { return !td.m_spent && td.m_subaddr_index == subaddr_index; }); - success_msg_writer() << boost::format(tr("%8u %6s %21s %21s %7u %21s")) % i.first % address_str % print_money(i.second) % print_money(unlocked_balance_per_subaddress[i.first]) % num_unspent_outputs % m_wallet->get_subaddress_label(subaddr_index); + success_msg_writer() << boost::format(tr("%8u %6s %21s %21s %7u %21s")) % i.first % address_str % print_money(i.second) % print_money(unlocked_balance_per_subaddress[i.first].first) % num_unspent_outputs % m_wallet->get_subaddress_label(subaddr_index); } return true; } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 60b940163..251475230 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -5430,13 +5430,19 @@ uint64_t wallet2::balance(uint32_t index_major) const return amount; } //---------------------------------------------------------------------------------------------------- -uint64_t wallet2::unlocked_balance(uint32_t index_major) const +uint64_t wallet2::unlocked_balance(uint32_t index_major, 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)) - amount += i.second; + { + amount += i.second.first; + if (blocks_to_unlock && i.second.second > *blocks_to_unlock) + *blocks_to_unlock = i.second.second; + } return amount; } //---------------------------------------------------------------------------------------------------- @@ -5469,18 +5475,36 @@ std::map wallet2::balance_per_subaddress(uint32_t index_majo return amount_per_subaddr; } //---------------------------------------------------------------------------------------------------- -std::map wallet2::unlocked_balance_per_subaddress(uint32_t index_major) const +std::map> wallet2::unlocked_balance_per_subaddress(uint32_t index_major) const { - std::map amount_per_subaddr; + std::map> 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 && is_transfer_unlocked(td)) + if(td.m_subaddr_index.major == index_major && !td.m_spent && !td.m_frozen) { + uint64_t amount = 0, blocks_to_unlock = 0; + if (is_transfer_unlocked(td)) + { + amount = td.amount(); + blocks_to_unlock = 0; + } + else + { + uint64_t unlock_height = td.m_block_height + std::max(CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS); + if (td.m_tx.unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER && td.m_tx.unlock_time > unlock_height) + unlock_height = td.m_tx.unlock_time; + blocks_to_unlock = unlock_height > blockchain_height ? unlock_height - blockchain_height : 0; + amount = 0; + } auto found = amount_per_subaddr.find(td.m_subaddr_index.minor); if (found == amount_per_subaddr.end()) - amount_per_subaddr[td.m_subaddr_index.minor] = td.amount(); + amount_per_subaddr[td.m_subaddr_index.minor] = std::make_pair(amount, blocks_to_unlock); else - found->second += td.amount(); + { + found->second.first += amount; + found->second.second = std::max(found->second.second, blocks_to_unlock); + } } } return amount_per_subaddr; @@ -5494,11 +5518,18 @@ uint64_t wallet2::balance_all() const return r; } //---------------------------------------------------------------------------------------------------- -uint64_t wallet2::unlocked_balance_all() const +uint64_t wallet2::unlocked_balance_all(uint64_t *blocks_to_unlock) const { uint64_t r = 0; + if (blocks_to_unlock) + *blocks_to_unlock = 0; for (uint32_t index_major = 0; index_major < get_num_subaddress_accounts(); ++index_major) - r += unlocked_balance(index_major); + { + uint64_t local_blocks_to_unlock; + r += unlocked_balance(index_major, blocks_to_unlock ? &local_blocks_to_unlock : NULL); + if (blocks_to_unlock) + *blocks_to_unlock = std::max(*blocks_to_unlock, local_blocks_to_unlock); + } return r; } //---------------------------------------------------------------------------------------------------- @@ -9044,7 +9075,7 @@ std::vector wallet2::create_transactions_2(std::vector unlocked_balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account); + std::map> unlocked_balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account); std::map balance_per_subaddr = balance_per_subaddress(subaddr_account); if (subaddr_indices.empty()) // "index=[,,...]" wasn't specified -> use all the indices with non-zero unlocked balance @@ -9062,7 +9093,7 @@ std::vector wallet2::create_transactions_2(std::vector balance_subtotal, error::not_enough_money, balance_subtotal, needed_money, 0); @@ -9128,7 +9159,7 @@ std::vector wallet2::create_transactions_2(std::vector>& x, const std::pair>& y) { - return unlocked_balance_per_subaddr[x.first] > unlocked_balance_per_subaddr[y.first]; + return unlocked_balance_per_subaddr[x.first].first > unlocked_balance_per_subaddr[y.first].first; }; std::sort(unused_transfers_indices_per_subaddr.begin(), unused_transfers_indices_per_subaddr.end(), sort_predicate); std::sort(unused_dust_indices_per_subaddr.begin(), unused_dust_indices_per_subaddr.end(), sort_predicate); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 5a67c20d0..70c616c2c 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -765,13 +765,13 @@ namespace tools // locked & unlocked balance of given or current subaddress account uint64_t balance(uint32_t subaddr_index_major) const; - uint64_t unlocked_balance(uint32_t subaddr_index_major) const; + uint64_t unlocked_balance(uint32_t subaddr_index_major, uint64_t *blocks_to_unlock = NULL) const; // locked & unlocked balance per subaddress of given or current subaddress account std::map balance_per_subaddress(uint32_t subaddr_index_major) const; - std::map unlocked_balance_per_subaddress(uint32_t subaddr_index_major) const; + std::map> unlocked_balance_per_subaddress(uint32_t subaddr_index_major) const; // all locked & unlocked balances of all subaddress accounts uint64_t balance_all() const; - uint64_t unlocked_balance_all() const; + uint64_t unlocked_balance_all(uint64_t *blocks_to_unlock = NULL) const; template void transfer_selected(const std::vector& dsts, const std::vector& selected_transfers, size_t fake_outputs_count, std::vector> &outs, diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index aa3169c97..1a9a0b52d 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -385,10 +385,10 @@ namespace tools try { res.balance = req.all_accounts ? m_wallet->balance_all() : m_wallet->balance(req.account_index); - res.unlocked_balance = req.all_accounts ? m_wallet->unlocked_balance_all() : m_wallet->unlocked_balance(req.account_index); + 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.multisig_import_needed = m_wallet->multisig() && m_wallet->has_multisig_partial_key_images(); std::map> balance_per_subaddress_per_account; - std::map> unlocked_balance_per_subaddress_per_account; + std::map>> unlocked_balance_per_subaddress_per_account; if (req.all_accounts) { for (uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index) @@ -408,7 +408,7 @@ namespace tools { uint32_t account_index = p.first; std::map balance_per_subaddress = p.second; - std::map unlocked_balance_per_subaddress = unlocked_balance_per_subaddress_per_account[account_index]; + std::map> unlocked_balance_per_subaddress = unlocked_balance_per_subaddress_per_account[account_index]; std::set address_indices; if (!req.all_accounts && !req.address_indices.empty()) { @@ -427,7 +427,8 @@ namespace tools cryptonote::subaddress_index index = {info.account_index, info.address_index}; info.address = m_wallet->get_subaddress_as_str(index); info.balance = balance_per_subaddress[i]; - info.unlocked_balance = unlocked_balance_per_subaddress[i]; + info.unlocked_balance = unlocked_balance_per_subaddress[i].first; + info.blocks_to_unlock = unlocked_balance_per_subaddress[i].second; info.label = m_wallet->get_subaddress_label(index); info.num_unspent_outputs = std::count_if(transfers.begin(), transfers.end(), [&](const tools::wallet2::transfer_details& td) { return !td.m_spent && td.m_subaddr_index == index; }); res.per_subaddress.emplace_back(std::move(info)); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index de3067a52..3f35510e3 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -47,7 +47,7 @@ // advance which version they will stop working with // Don't go over 32767 for any of these #define WALLET_RPC_VERSION_MAJOR 1 -#define WALLET_RPC_VERSION_MINOR 8 +#define WALLET_RPC_VERSION_MINOR 9 #define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR) namespace tools @@ -81,6 +81,7 @@ namespace wallet_rpc uint64_t unlocked_balance; std::string label; uint64_t num_unspent_outputs; + uint64_t blocks_to_unlock; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(account_index) @@ -90,6 +91,7 @@ namespace wallet_rpc KV_SERIALIZE(unlocked_balance) KV_SERIALIZE(label) KV_SERIALIZE(num_unspent_outputs) + KV_SERIALIZE(blocks_to_unlock) END_KV_SERIALIZE_MAP() }; @@ -99,12 +101,14 @@ namespace wallet_rpc uint64_t unlocked_balance; bool multisig_import_needed; std::vector per_subaddress; + uint64_t blocks_to_unlock; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(balance) KV_SERIALIZE(unlocked_balance) KV_SERIALIZE(multisig_import_needed) KV_SERIALIZE(per_subaddress) + KV_SERIALIZE(blocks_to_unlock) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init response;