Adds batched amounts to wallet2 rpc and simplewallet

This adds detail to the balance command of the CLI wallet to show how
much has been batched for each address and also summarises when the next
payout is.

It also includes additional fields to get_balance rpc endpoint on the
oxen-wallet-rpc so the user can check on the status of their accrued
funds.
This commit is contained in:
Sean Darcy 2022-06-01 17:28:12 +10:00
parent e120075c37
commit 90cc745987
9 changed files with 80 additions and 5 deletions

View File

@ -53,6 +53,7 @@
#include <boost/lexical_cast.hpp>
#include <boost/program_options.hpp>
#include <boost/format.hpp>
#include <fmt/core.h>
#include <oxenc/hex.h>
#include "epee/console_handler.h"
#include "common/i18n.h"
@ -5059,18 +5060,34 @@ bool simple_wallet::show_balance_unlocked(bool detailed)
<< 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, false);
std::map<uint32_t, std::pair<uint64_t, std::pair<uint64_t, uint64_t>>> unlocked_balance_per_subaddress = m_wallet->unlocked_balance_per_subaddress(m_current_subaddress_account, false);
address_parse_info info;
const auto& conf = get_config(m_wallet->nettype());
std::string current_address_str = m_wallet->get_subaddress_as_str({m_current_subaddress_account, 0});
if(cryptonote::get_account_address_from_str(info, m_wallet->nettype(), current_address_str))
{
const uint64_t blockchain_height = m_wallet->get_blockchain_current_height();
if(uint64_t batched_amount = m_wallet->get_batched_amount(current_address_str); batched_amount > 0)
{
uint64_t next_payout_block = info.address.next_payout_height(blockchain_height, conf.BATCHING_INTERVAL);
std::string next_batch_payout = fmt::format(" (next payout: block {}, in about {})", next_payout_block, tools::get_human_readable_timespan(std::chrono::seconds((next_payout_block - blockchain_height) * TARGET_BLOCK_TIME)));
success_msg_writer() << tr("Pending SN rewards: ")
<< print_money(m_wallet->get_batched_amount(current_address_str)) << ", "
<< next_batch_payout;
}
}
if (!detailed || balance_per_subaddress.empty())
return true;
success_msg_writer() << tr("Balance per address:");
success_msg_writer() << boost::format("%15s %21s %21s %7s %21s") % tr("Address") % tr("Balance") % tr("Unlocked balance") % tr("Outputs") % tr("Label");
success_msg_writer() << boost::format("%15s %21s %21s %21s %7s %21s") % tr("Address") % tr("Balance") % tr("Unlocked balance") % tr("Batched Amount") % tr("Outputs") % tr("Label");
std::vector<wallet::transfer_details> transfers;
m_wallet->get_transfers(transfers);
for (const auto& i : balance_per_subaddress)
{
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 batched_amount = m_wallet->get_batched_amount(address_str);
uint64_t num_unspent_outputs = std::count_if(transfers.begin(), transfers.end(), [&subaddr_index](const wallet::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].first) % num_unspent_outputs % m_wallet->get_subaddress_label(subaddr_index);
success_msg_writer() << boost::format(tr("%8u %6s %21s %21s %21s %7u %21s")) % i.first % address_str % print_money(i.second) % print_money(unlocked_balance_per_subaddress[i.first].first) % print_money(batched_amount) % num_unspent_outputs % m_wallet->get_subaddress_label(subaddr_index);
}
return true;
}
@ -9107,18 +9124,20 @@ void simple_wallet::print_accounts(const std::string& tag)
success_msg_writer() << tr("Accounts with tag: ") << tag;
success_msg_writer() << tr("Tag's description: ") << account_tags.first.find(tag)->second;
}
success_msg_writer() << boost::format(" %15s %21s %21s %21s") % tr("Account") % tr("Balance") % tr("Unlocked balance") % tr("Label");
success_msg_writer() << boost::format(" %15s %21s %21s %21s %21s") % tr("Address") % tr("Balance") % tr("Unlocked balance") % tr("Batched Amount") % tr("Label");
uint64_t total_balance = 0, total_unlocked_balance = 0;
for (uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index)
{
std::string address_str = m_wallet->get_subaddress_as_str({account_index, 0}).substr(0, 6);
if (account_tags.second[account_index] != tag)
continue;
success_msg_writer() << boost::format(tr(" %c%8u %6s %21s %21s %21s"))
success_msg_writer() << boost::format(tr(" %c%8u %6s %21s %21s %21s %21s"))
% (m_current_subaddress_account == account_index ? '*' : ' ')
% account_index
% m_wallet->get_subaddress_as_str({account_index, 0}).substr(0, 6)
% address_str
% print_money(m_wallet->balance(account_index, false))
% print_money(m_wallet->unlocked_balance(account_index, false))
% print_money(m_wallet->get_batched_amount(address_str))
% m_wallet->get_subaddress_label({account_index, 0});
total_balance += m_wallet->balance(account_index, false);
total_unlocked_balance += m_wallet->unlocked_balance(account_index, false);

View File

@ -1063,6 +1063,12 @@ uint64_t WalletImpl::unlockedBalance(uint32_t accountIndex) const
return wallet()->unlocked_balance(accountIndex, false);
}
EXPORT
uint64_t WalletImpl::accruedBalance(const std::string& address) const
{
return wallet()->get_batched_amount(address);
}
EXPORT
std::vector<Wallet::stake_info>* WalletImpl::listCurrentStakes() const
{

View File

@ -118,6 +118,7 @@ public:
bool trustedDaemon() const override;
uint64_t balance(uint32_t accountIndex = 0) const override;
uint64_t unlockedBalance(uint32_t accountIndex = 0) const override;
uint64_t accruedBalance(const std::string& address) const override;
std::vector<Wallet::stake_info>* listCurrentStakes() const override;
uint64_t blockChainHeight() const override;
uint64_t approximateBlockChainHeight() const override;

View File

@ -609,6 +609,12 @@ struct Wallet
return result;
}
/**
* @brief accruedBalance - returns the accounts balance that has been batched and yet to be paid.
* @return
*/
virtual uint64_t accruedBalance(const std::string& address) const = 0;
// Information returned about stakes in listCurrentStakes()
struct stake_info {
std::string sn_pubkey;

View File

@ -3639,6 +3639,8 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
LOG_PRINT_L1("Failed to check pending transactions");
}
refresh_batching_cache();
m_first_refresh_done = true;
LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", balance (all accounts): " << print_money(balance_all(false)) << ", unlocked: " << print_money(unlocked_balance_all(false)));
@ -12994,6 +12996,26 @@ std::unordered_map<std::string, wallet2::ons_detail> wallet2::get_ons_cache()
return ons_records_cache;
}
void wallet2::refresh_batching_cache()
{
rpc::GET_ACCRUED_BATCHED_EARNINGS::request req{};
rpc::GET_ACCRUED_BATCHED_EARNINGS::response daemon_resp{};
bool r = invoke_http<rpc::GET_ACCRUED_BATCHED_EARNINGS>(req, daemon_resp);
if (r && daemon_resp.status == rpc::STATUS_OK)
{
batching_records_cache.clear();
for (size_t i = 0; i < daemon_resp.addresses.size(); ++i)
batching_records_cache.insert({std::move(daemon_resp.addresses[i]), std::move(daemon_resp.amounts[i])});
}
}
uint64_t wallet2::get_batched_amount(const std::string& address) const
{
if (auto i = batching_records_cache.find(address); i != batching_records_cache.end())
return i->second;
return 0;
}
void wallet2::set_tx_note(const crypto::hash &txid, const std::string &note)
{
m_tx_notes[txid] = note;

View File

@ -849,6 +849,11 @@ private:
std::unordered_map<std::string, ons_detail> get_ons_cache();
std::unordered_map<std::string, uint64_t> batching_records_cache;
void refresh_batching_cache();
uint64_t get_batched_amount(const std::string& address) const;
// Returns the current height up to which the wallet has synchronized the blockchain. Thread
// safe (though the value may be behind if another thread is in the middle of adding blocks).
uint64_t get_blockchain_current_height() const { return m_cached_height; }

View File

@ -610,6 +610,18 @@ namespace tools
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(req.strict, &res.blocks_to_unlock, &res.time_to_unlock) : m_wallet->unlocked_balance(req.account_index, req.strict, &res.blocks_to_unlock, &res.time_to_unlock);
res.multisig_import_needed = m_wallet->multisig() && m_wallet->has_multisig_partial_key_images();
std::string current_address_str = m_wallet->get_subaddress_as_str({(req.all_accounts ? 0 : req.account_index), 0});
res.accrued_balance = m_wallet->get_batched_amount(current_address_str);
if (res.accrued_balance > 0)
{
cryptonote::address_parse_info info;
auto& conf = cryptonote::get_config(m_wallet->nettype());
get_account_address_from_str(info, m_wallet->nettype(), current_address_str);
res.accrued_balance_next_payout = info.address.next_payout_height(m_wallet->get_blockchain_current_height(), conf.BATCHING_INTERVAL);
} else {
res.accrued_balance_next_payout = 0;
}
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, std::pair<uint64_t, uint64_t>>>> unlocked_balance_per_subaddress_per_account;
if (req.all_accounts)

View File

@ -61,6 +61,8 @@ KV_SERIALIZE_MAP_CODE_END()
KV_SERIALIZE_MAP_CODE_BEGIN(GET_BALANCE::response)
KV_SERIALIZE(balance)
KV_SERIALIZE(unlocked_balance)
KV_SERIALIZE(accrued_balance)
KV_SERIALIZE(accrued_balance_next_payout)
KV_SERIALIZE(multisig_import_needed)
KV_SERIALIZE(per_subaddress)
KV_SERIALIZE(blocks_to_unlock)

View File

@ -121,6 +121,8 @@ namespace tools::wallet_rpc {
{
uint64_t balance; // The total balance (atomic units) of the currently opened wallet.
uint64_t unlocked_balance; // Unlocked funds are those funds that are sufficiently deep enough in the oxen blockchain to be considered safe to spend.
uint64_t accrued_balance; // The unpaid accrued service node rewards currently in the batching database for this currently opened wallet
uint64_t accrued_balance_next_payout; // Height that the accrued service node rewards will be paid out
bool multisig_import_needed; // True if importing multisig data is needed for returning a correct balance.
std::vector<per_subaddress_info> per_subaddress; // Balance information for each subaddress in an account.
uint64_t blocks_to_unlock; // The number of blocks remaining for the balance to unlock