wallet: track outgoing payments and add a show_transfers command
It's a user friendly display of incoming and outgoing transfers, listed by height, within an optional height range.
This commit is contained in:
parent
ea7380aa7f
commit
3f611bc3dc
|
@ -453,6 +453,7 @@ simple_wallet::simple_wallet()
|
||||||
m_cmd_binder.set_handler("rescan_spent", boost::bind(&simple_wallet::rescan_spent, this, _1), tr("Rescan blockchain for spent outputs"));
|
m_cmd_binder.set_handler("rescan_spent", boost::bind(&simple_wallet::rescan_spent, this, _1), tr("Rescan blockchain for spent outputs"));
|
||||||
m_cmd_binder.set_handler("get_tx_key", boost::bind(&simple_wallet::get_tx_key, this, _1), tr("Get transaction key (r) for a given tx"));
|
m_cmd_binder.set_handler("get_tx_key", boost::bind(&simple_wallet::get_tx_key, this, _1), tr("Get transaction key (r) for a given tx"));
|
||||||
m_cmd_binder.set_handler("check_tx_key", boost::bind(&simple_wallet::check_tx_key, this, _1), tr("Check amount going to a given address in a partcular tx"));
|
m_cmd_binder.set_handler("check_tx_key", boost::bind(&simple_wallet::check_tx_key, this, _1), tr("Check amount going to a given address in a partcular tx"));
|
||||||
|
m_cmd_binder.set_handler("show_transfers", boost::bind(&simple_wallet::show_transfers, this, _1), tr("show_transfers [in|out] [<min_height> [<max_height>]] - show incoming/outgoing transfers within an optional height range"));
|
||||||
m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), tr("Show this help"));
|
m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), tr("Show this help"));
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
@ -1992,6 +1993,104 @@ bool simple_wallet::check_tx_key(const std::vector<std::string> &args_)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
|
||||||
|
{
|
||||||
|
std::vector<std::string> local_args = args_;
|
||||||
|
bool in = true;
|
||||||
|
bool out = true;
|
||||||
|
uint64_t min_height = 0;
|
||||||
|
uint64_t max_height = (uint64_t)-1;
|
||||||
|
|
||||||
|
if(local_args.size() > 3) {
|
||||||
|
fail_msg_writer() << tr("Usage: show_transfers [in|out] [<min_height> [<max_height>]]");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// optional in/out selector
|
||||||
|
if (local_args.size() > 0) {
|
||||||
|
if (local_args[0] == "in" || local_args[0] == "incoming") {
|
||||||
|
out = false;
|
||||||
|
local_args.erase(local_args.begin());
|
||||||
|
}
|
||||||
|
else if (local_args[0] == "out" || local_args[0] == "outgoing") {
|
||||||
|
in = false;
|
||||||
|
local_args.erase(local_args.begin());
|
||||||
|
}
|
||||||
|
else if (local_args[0] == "all" || local_args[0] == "both") {
|
||||||
|
local_args.erase(local_args.begin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// min height
|
||||||
|
if (local_args.size() > 0) {
|
||||||
|
try {
|
||||||
|
min_height = boost::lexical_cast<uint64_t>(local_args[0]);
|
||||||
|
}
|
||||||
|
catch (boost::bad_lexical_cast &) {
|
||||||
|
fail_msg_writer() << "Bad min_height parameter: " << local_args[0];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
local_args.erase(local_args.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
// max height
|
||||||
|
if (local_args.size() > 0) {
|
||||||
|
try {
|
||||||
|
max_height = boost::lexical_cast<uint64_t>(local_args[0]);
|
||||||
|
}
|
||||||
|
catch (boost::bad_lexical_cast &) {
|
||||||
|
fail_msg_writer() << "Bad max_height parameter: " << local_args[0];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
local_args.erase(local_args.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::multimap<uint64_t, std::pair<bool,std::string>> output;
|
||||||
|
|
||||||
|
if (in) {
|
||||||
|
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
|
||||||
|
m_wallet->get_payments(payments, min_height, max_height);
|
||||||
|
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
|
||||||
|
const tools::wallet2::payment_details &pd = i->second;
|
||||||
|
std::string payment_id = string_tools::pod_to_hex(i->first);
|
||||||
|
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
|
||||||
|
payment_id = payment_id.substr(0,16);
|
||||||
|
output.insert(std::make_pair(pd.m_block_height, std::make_pair(true, (boost::format("%18.18s %s %s") % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id).str())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out) {
|
||||||
|
std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments;
|
||||||
|
m_wallet->get_payments_out(payments, min_height, max_height);
|
||||||
|
for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
|
||||||
|
const tools::wallet2::confirmed_transfer_details &pd = i->second;
|
||||||
|
uint64_t fee = pd.m_amount_in - pd.m_amount_out;
|
||||||
|
output.insert(std::make_pair(pd.m_block_height, std::make_pair(false, (boost::format("%18.18s %s") % print_money(pd.m_amount_in - pd.m_change - fee) % "-").str())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// print in and out sorted by height
|
||||||
|
for (std::map<uint64_t, std::pair<bool, std::string>>::const_iterator i = output.begin(); i != output.end(); ++i) {
|
||||||
|
message_writer(i->second.first ? epee::log_space::console_color_magenta : epee::log_space::console_color_green, false) <<
|
||||||
|
boost::format("[%8.8llu] %4.4s %s") %
|
||||||
|
((unsigned long long)i->first) % (i->second.first ? tr("in") : tr("out")) % i->second.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// print unconfirmed last
|
||||||
|
if (out) {
|
||||||
|
std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
|
||||||
|
m_wallet->get_unconfirmed_payments_out(upayments);
|
||||||
|
for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
|
||||||
|
const tools::wallet2::unconfirmed_transfer_details &pd = i->second;
|
||||||
|
uint64_t amount = 0;
|
||||||
|
cryptonote::get_inputs_money_amount(pd.m_tx, amount);
|
||||||
|
message_writer() << (boost::format("[%8.8s] %4.4s %18.18s") % tr("pending") % tr("out") % print_money(amount - pd.m_change)).str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
bool simple_wallet::run()
|
bool simple_wallet::run()
|
||||||
{
|
{
|
||||||
std::string addr_start = m_wallet->get_account().get_public_address_str(m_wallet->testnet()).substr(0, 6);
|
std::string addr_start = m_wallet->get_account().get_public_address_str(m_wallet->testnet()).substr(0, 6);
|
||||||
|
|
|
@ -126,6 +126,7 @@ namespace cryptonote
|
||||||
bool set_log(const std::vector<std::string> &args);
|
bool set_log(const std::vector<std::string> &args);
|
||||||
bool get_tx_key(const std::vector<std::string> &args);
|
bool get_tx_key(const std::vector<std::string> &args);
|
||||||
bool check_tx_key(const std::vector<std::string> &args);
|
bool check_tx_key(const std::vector<std::string> &args);
|
||||||
|
bool show_transfers(const std::vector<std::string> &args);
|
||||||
|
|
||||||
uint64_t get_daemon_blockchain_height(std::string& err);
|
uint64_t get_daemon_blockchain_height(std::string& err);
|
||||||
bool try_connect_to_daemon();
|
bool try_connect_to_daemon();
|
||||||
|
|
|
@ -150,7 +150,7 @@ bool wallet2::is_deprecated() const
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_t height)
|
void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_t height)
|
||||||
{
|
{
|
||||||
process_unconfirmed(tx);
|
process_unconfirmed(tx, height);
|
||||||
std::vector<size_t> outs;
|
std::vector<size_t> outs;
|
||||||
uint64_t tx_money_got_in_outs = 0;
|
uint64_t tx_money_got_in_outs = 0;
|
||||||
crypto::public_key tx_pub_key = null_pkey;
|
crypto::public_key tx_pub_key = null_pkey;
|
||||||
|
@ -283,12 +283,23 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
void wallet2::process_unconfirmed(const cryptonote::transaction& tx)
|
void wallet2::process_unconfirmed(const cryptonote::transaction& tx, uint64_t height)
|
||||||
{
|
{
|
||||||
auto unconf_it = m_unconfirmed_txs.find(get_transaction_hash(tx));
|
crypto::hash txid = get_transaction_hash(tx);
|
||||||
if(unconf_it != m_unconfirmed_txs.end())
|
auto unconf_it = m_unconfirmed_txs.find(txid);
|
||||||
|
if(unconf_it != m_unconfirmed_txs.end()) {
|
||||||
|
if (store_tx_keys()) {
|
||||||
|
try {
|
||||||
|
m_confirmed_txs.insert(std::make_pair(txid, confirmed_transfer_details(unconf_it->second, height)));
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
// can fail if the tx has unexpected input types
|
||||||
|
LOG_PRINT_L0("Failed to add outgoing transaction to confirmed transaction map");
|
||||||
|
}
|
||||||
|
}
|
||||||
m_unconfirmed_txs.erase(unconf_it);
|
m_unconfirmed_txs.erase(unconf_it);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
void wallet2::process_new_blockchain_entry(const cryptonote::block& b, cryptonote::block_complete_entry& bche, crypto::hash& bl_id, uint64_t height)
|
void wallet2::process_new_blockchain_entry(const cryptonote::block& b, cryptonote::block_complete_entry& bche, crypto::hash& bl_id, uint64_t height)
|
||||||
{
|
{
|
||||||
|
@ -1021,17 +1032,34 @@ void wallet2::get_payments(const crypto::hash& payment_id, std::list<wallet2::pa
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
void wallet2::get_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& payments, uint64_t min_height) const
|
void wallet2::get_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& payments, uint64_t min_height, uint64_t max_height) const
|
||||||
{
|
{
|
||||||
auto range = std::make_pair(m_payments.begin(), m_payments.end());
|
auto range = std::make_pair(m_payments.begin(), m_payments.end());
|
||||||
std::for_each(range.first, range.second, [&payments, &min_height](const payment_container::value_type& x) {
|
std::for_each(range.first, range.second, [&payments, &min_height, &max_height](const payment_container::value_type& x) {
|
||||||
if (min_height < x.second.m_block_height)
|
if (min_height < x.second.m_block_height && max_height >= x.second.m_block_height)
|
||||||
{
|
{
|
||||||
payments.push_back(x);
|
payments.push_back(x);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
void wallet2::get_payments_out(std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>>& confirmed_payments,
|
||||||
|
uint64_t min_height, uint64_t max_height) const
|
||||||
|
{
|
||||||
|
for (auto i = m_confirmed_txs.begin(); i != m_confirmed_txs.end(); ++i) {
|
||||||
|
if (i->second.m_block_height > min_height && i->second.m_block_height <= max_height) {
|
||||||
|
confirmed_payments.push_back(*i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
void wallet2::get_unconfirmed_payments_out(std::list<std::pair<crypto::hash,wallet2::unconfirmed_transfer_details>>& unconfirmed_payments) const
|
||||||
|
{
|
||||||
|
for (auto i = m_unconfirmed_txs.begin(); i != m_unconfirmed_txs.end(); ++i) {
|
||||||
|
unconfirmed_payments.push_back(*i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
void wallet2::rescan_spent()
|
void wallet2::rescan_spent()
|
||||||
{
|
{
|
||||||
std::vector<std::string> key_images;
|
std::vector<std::string> key_images;
|
||||||
|
|
|
@ -109,6 +109,17 @@ namespace tools
|
||||||
time_t m_sent_time;
|
time_t m_sent_time;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct confirmed_transfer_details
|
||||||
|
{
|
||||||
|
uint64_t m_amount_in;
|
||||||
|
uint64_t m_amount_out;
|
||||||
|
uint64_t m_change;
|
||||||
|
uint64_t m_block_height;
|
||||||
|
confirmed_transfer_details() {}
|
||||||
|
confirmed_transfer_details(const unconfirmed_transfer_details &utd, uint64_t height):
|
||||||
|
m_amount_out(get_outs_money_amount(utd.m_tx)), m_change(utd.m_change), m_block_height(height) { get_inputs_money_amount(utd.m_tx, m_amount_in); }
|
||||||
|
};
|
||||||
|
|
||||||
typedef std::vector<transfer_details> transfer_container;
|
typedef std::vector<transfer_details> transfer_container;
|
||||||
typedef std::unordered_multimap<crypto::hash, payment_details> payment_container;
|
typedef std::unordered_multimap<crypto::hash, payment_details> payment_container;
|
||||||
|
|
||||||
|
@ -244,7 +255,10 @@ namespace tools
|
||||||
bool check_connection();
|
bool check_connection();
|
||||||
void get_transfers(wallet2::transfer_container& incoming_transfers) const;
|
void get_transfers(wallet2::transfer_container& incoming_transfers) const;
|
||||||
void get_payments(const crypto::hash& payment_id, std::list<wallet2::payment_details>& payments, uint64_t min_height = 0) const;
|
void get_payments(const crypto::hash& payment_id, std::list<wallet2::payment_details>& payments, uint64_t min_height = 0) const;
|
||||||
void get_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& payments, uint64_t min_height) const;
|
void get_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& payments, uint64_t min_height, uint64_t max_height = (uint64_t)-1) const;
|
||||||
|
void get_payments_out(std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>>& confirmed_payments,
|
||||||
|
uint64_t min_height, uint64_t max_height = (uint64_t)-1) const;
|
||||||
|
void get_unconfirmed_payments_out(std::list<std::pair<crypto::hash,wallet2::unconfirmed_transfer_details>>& unconfirmed_payments) const;
|
||||||
uint64_t get_blockchain_current_height() const { return m_local_bc_height; }
|
uint64_t get_blockchain_current_height() const { return m_local_bc_height; }
|
||||||
void rescan_spent();
|
void rescan_spent();
|
||||||
template <class t_archive>
|
template <class t_archive>
|
||||||
|
@ -265,6 +279,9 @@ namespace tools
|
||||||
if(ver < 8)
|
if(ver < 8)
|
||||||
return;
|
return;
|
||||||
a & m_tx_keys;
|
a & m_tx_keys;
|
||||||
|
if(ver < 9)
|
||||||
|
return;
|
||||||
|
a & m_confirmed_txs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -323,7 +340,7 @@ namespace tools
|
||||||
void pull_blocks(uint64_t start_height, uint64_t& blocks_added);
|
void pull_blocks(uint64_t start_height, uint64_t& blocks_added);
|
||||||
uint64_t select_transfers(uint64_t needed_money, bool add_dust, uint64_t dust, std::list<transfer_container::iterator>& selected_transfers);
|
uint64_t select_transfers(uint64_t needed_money, bool add_dust, uint64_t dust, std::list<transfer_container::iterator>& selected_transfers);
|
||||||
bool prepare_file_names(const std::string& file_path);
|
bool prepare_file_names(const std::string& file_path);
|
||||||
void process_unconfirmed(const cryptonote::transaction& tx);
|
void process_unconfirmed(const cryptonote::transaction& tx, uint64_t height);
|
||||||
void add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t change_amount);
|
void add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t change_amount);
|
||||||
void generate_genesis(cryptonote::block& b);
|
void generate_genesis(cryptonote::block& b);
|
||||||
void check_genesis(const crypto::hash& genesis_hash) const; //throws
|
void check_genesis(const crypto::hash& genesis_hash) const; //throws
|
||||||
|
@ -337,6 +354,7 @@ namespace tools
|
||||||
std::vector<crypto::hash> m_blockchain;
|
std::vector<crypto::hash> m_blockchain;
|
||||||
std::atomic<uint64_t> m_local_bc_height; //temporary workaround
|
std::atomic<uint64_t> m_local_bc_height; //temporary workaround
|
||||||
std::unordered_map<crypto::hash, unconfirmed_transfer_details> m_unconfirmed_txs;
|
std::unordered_map<crypto::hash, unconfirmed_transfer_details> m_unconfirmed_txs;
|
||||||
|
std::unordered_map<crypto::hash, confirmed_transfer_details> m_confirmed_txs;
|
||||||
std::unordered_map<crypto::hash, crypto::secret_key> m_tx_keys;
|
std::unordered_map<crypto::hash, crypto::secret_key> m_tx_keys;
|
||||||
|
|
||||||
transfer_container m_transfers;
|
transfer_container m_transfers;
|
||||||
|
@ -358,7 +376,7 @@ namespace tools
|
||||||
uint32_t m_default_mixin;
|
uint32_t m_default_mixin;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
BOOST_CLASS_VERSION(tools::wallet2, 8)
|
BOOST_CLASS_VERSION(tools::wallet2, 9)
|
||||||
|
|
||||||
namespace boost
|
namespace boost
|
||||||
{
|
{
|
||||||
|
@ -383,6 +401,15 @@ namespace boost
|
||||||
a & x.m_tx;
|
a & x.m_tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class Archive>
|
||||||
|
inline void serialize(Archive &a, tools::wallet2::confirmed_transfer_details &x, const boost::serialization::version_type ver)
|
||||||
|
{
|
||||||
|
a & x.m_amount_in;
|
||||||
|
a & x.m_amount_out;
|
||||||
|
a & x.m_change;
|
||||||
|
a & x.m_block_height;
|
||||||
|
}
|
||||||
|
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
inline void serialize(Archive& a, tools::wallet2::payment_details& x, const boost::serialization::version_type ver)
|
inline void serialize(Archive& a, tools::wallet2::payment_details& x, const boost::serialization::version_type ver)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue