simplewallet: lock console on inactivity

pull/326/head
moneromooo-monero 5 years ago
parent 85014813cf
commit 1a367d6a22
No known key found for this signature in database
GPG Key ID: 686F07454D6CEFC3

@ -45,6 +45,9 @@
#include "readline_buffer.h" #include "readline_buffer.h"
#endif #endif
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "console_handler"
namespace epee namespace epee
{ {
class async_stdin_reader class async_stdin_reader
@ -96,7 +99,7 @@ namespace epee
res = true; res = true;
} }
if (!eos()) if (!eos() && m_read_status != state_cancelled)
m_read_status = state_init; m_read_status = state_init;
return res; return res;
@ -122,6 +125,14 @@ namespace epee
} }
} }
void cancel()
{
boost::unique_lock<boost::mutex> lock(m_response_mutex);
m_read_status = state_cancelled;
m_has_read_request = false;
m_response_cv.notify_one();
}
private: private:
bool start_read() bool start_read()
{ {
@ -162,6 +173,9 @@ namespace epee
while (m_run.load(std::memory_order_relaxed)) while (m_run.load(std::memory_order_relaxed))
{ {
if (m_read_status == state_cancelled)
return false;
fd_set read_set; fd_set read_set;
FD_ZERO(&read_set); FD_ZERO(&read_set);
FD_SET(stdin_fileno, &read_set); FD_SET(stdin_fileno, &read_set);
@ -179,6 +193,9 @@ namespace epee
#else #else
while (m_run.load(std::memory_order_relaxed)) while (m_run.load(std::memory_order_relaxed))
{ {
if (m_read_status == state_cancelled)
return false;
int retval = ::WaitForSingleObject(::GetStdHandle(STD_INPUT_HANDLE), 100); int retval = ::WaitForSingleObject(::GetStdHandle(STD_INPUT_HANDLE), 100);
switch (retval) switch (retval)
{ {
@ -219,7 +236,8 @@ reread:
case rdln::full: break; case rdln::full: break;
} }
#else #else
std::getline(std::cin, line); if (m_read_status != state_cancelled)
std::getline(std::cin, line);
#endif #endif
read_ok = !std::cin.eof() && !std::cin.fail(); read_ok = !std::cin.eof() && !std::cin.fail();
} }
@ -303,7 +321,7 @@ eof:
template<class chain_handler> template<class chain_handler>
bool run(chain_handler ch_handler, std::function<std::string(void)> prompt, const std::string& usage = "", std::function<void(void)> exit_handler = NULL) bool run(chain_handler ch_handler, std::function<std::string(void)> prompt, const std::string& usage = "", std::function<void(void)> exit_handler = NULL)
{ {
return run(prompt, usage, [&](const std::string& cmd) { return ch_handler(cmd); }, exit_handler); return run(prompt, usage, [&](const boost::optional<std::string>& cmd) { return ch_handler(cmd); }, exit_handler);
} }
void stop() void stop()
@ -312,6 +330,12 @@ eof:
m_stdin_reader.stop(); m_stdin_reader.stop();
} }
void cancel()
{
m_cancel = true;
m_stdin_reader.cancel();
}
void print_prompt() void print_prompt()
{ {
std::string prompt = m_prompt(); std::string prompt = m_prompt();
@ -360,18 +384,23 @@ eof:
std::cout << std::endl; std::cout << std::endl;
break; break;
} }
if (m_cancel)
{
MDEBUG("Input cancelled");
cmd_handler(boost::none);
m_cancel = false;
continue;
}
if (!get_line_ret) if (!get_line_ret)
{ {
MERROR("Failed to read line."); MERROR("Failed to read line.");
} }
string_tools::trim(command); string_tools::trim(command);
LOG_PRINT_L2("Read command: " << command); LOG_PRINT_L2("Read command: " << command);
if (command.empty()) if(cmd_handler(command))
{
continue;
}
else if(cmd_handler(command))
{ {
continue; continue;
} }
@ -401,6 +430,7 @@ eof:
private: private:
async_stdin_reader m_stdin_reader; async_stdin_reader m_stdin_reader;
std::atomic<bool> m_running = {true}; std::atomic<bool> m_running = {true};
std::atomic<bool> m_cancel = {false};
std::function<std::string(void)> m_prompt; std::function<std::string(void)> m_prompt;
}; };
@ -482,8 +512,16 @@ eof:
class command_handler { class command_handler {
public: public:
typedef boost::function<bool (const std::vector<std::string> &)> callback; typedef boost::function<bool (const std::vector<std::string> &)> callback;
typedef boost::function<bool (void)> empty_callback;
typedef std::map<std::string, std::pair<callback, std::pair<std::string, std::string>>> lookup; typedef std::map<std::string, std::pair<callback, std::pair<std::string, std::string>>> lookup;
command_handler():
m_unknown_command_handler([](const std::vector<std::string>&){return false;}),
m_empty_command_handler([](){return true;}),
m_cancel_handler([](){return true;})
{
}
std::string get_usage() std::string get_usage()
{ {
std::stringstream ss; std::stringstream ss;
@ -516,25 +554,45 @@ eof:
#endif #endif
} }
void set_unknown_command_handler(const callback& hndlr)
{
m_unknown_command_handler = hndlr;
}
void set_empty_command_handler(const empty_callback& hndlr)
{
m_empty_command_handler = hndlr;
}
void set_cancel_handler(const empty_callback& hndlr)
{
m_cancel_handler = hndlr;
}
bool process_command_vec(const std::vector<std::string>& cmd) bool process_command_vec(const std::vector<std::string>& cmd)
{ {
if(!cmd.size()) if(!cmd.size() || (cmd.size() == 1 && !cmd[0].size()))
return false; return m_empty_command_handler();
auto it = m_command_handlers.find(cmd.front()); auto it = m_command_handlers.find(cmd.front());
if(it == m_command_handlers.end()) if(it == m_command_handlers.end())
return false; return m_unknown_command_handler(cmd);
std::vector<std::string> cmd_local(cmd.begin()+1, cmd.end()); std::vector<std::string> cmd_local(cmd.begin()+1, cmd.end());
return it->second.first(cmd_local); return it->second.first(cmd_local);
} }
bool process_command_str(const std::string& cmd) bool process_command_str(const boost::optional<std::string>& cmd)
{ {
if (!cmd)
return m_cancel_handler();
std::vector<std::string> cmd_v; std::vector<std::string> cmd_v;
boost::split(cmd_v,cmd,boost::is_any_of(" "), boost::token_compress_on); boost::split(cmd_v,*cmd,boost::is_any_of(" "), boost::token_compress_on);
return process_command_vec(cmd_v); return process_command_vec(cmd_v);
} }
private: private:
lookup m_command_handlers; lookup m_command_handlers;
callback m_unknown_command_handler;
empty_callback m_empty_command_handler;
empty_callback m_cancel_handler;
}; };
/************************************************************************/ /************************************************************************/
@ -572,6 +630,11 @@ eof:
{ {
m_console_handler.print_prompt(); m_console_handler.print_prompt();
} }
void cancel_input()
{
m_console_handler.cancel();
}
}; };
///* work around because of broken boost bind */ ///* work around because of broken boost bind */

@ -40,5 +40,7 @@ namespace rdln
readline_buffer* m_buffer; readline_buffer* m_buffer;
bool m_restart; bool m_restart;
}; };
void clear_screen();
} }

@ -71,6 +71,11 @@ rdln::linestatus rdln::readline_buffer::get_line(std::string& line) const
{ {
boost::lock_guard<boost::mutex> lock(sync_mutex); boost::lock_guard<boost::mutex> lock(sync_mutex);
line_stat = rdln::partial; line_stat = rdln::partial;
if (!m_cout_buf)
{
line = "";
return rdln::full;
}
rl_callback_read_char(); rl_callback_read_char();
if (line_stat == rdln::full) if (line_stat == rdln::full)
{ {
@ -224,3 +229,8 @@ static void remove_line_handler()
rl_callback_handler_remove(); rl_callback_handler_remove();
} }
void rdln::clear_screen()
{
rl_clear_screen(0, 0);
}

@ -30,6 +30,7 @@
#include <unistd.h> #include <unistd.h>
#include <cstdio> #include <cstdio>
#include <wchar.h>
#ifdef __GLIBC__ #ifdef __GLIBC__
#include <gnu/libc-version.h> #include <gnu/libc-version.h>
@ -67,6 +68,7 @@ using namespace epee;
#include "memwipe.h" #include "memwipe.h"
#include "cryptonote_config.h" #include "cryptonote_config.h"
#include "net/http_client.h" // epee::net_utils::... #include "net/http_client.h" // epee::net_utils::...
#include "readline_buffer.h"
#ifdef WIN32 #ifdef WIN32
#ifndef STRSAFE_NO_DEPRECATE #ifndef STRSAFE_NO_DEPRECATE
@ -1119,4 +1121,162 @@ std::string get_nix_version_display_string()
return (boost::format(size->format) % (double(bytes) / divisor)).str(); return (boost::format(size->format) % (double(bytes) / divisor)).str();
} }
void clear_screen()
{
std::cout << "\033[2K" << std::flush; // clear whole line
std::cout << "\033c" << std::flush; // clear current screen and scrollback
std::cout << "\033[2J" << std::flush; // clear current screen only, scrollback is still around
std::cout << "\033[3J" << std::flush; // does nothing, should clear current screen and scrollback
std::cout << "\033[1;1H" << std::flush; // move cursor top/left
std::cout << "\r \r" << std::flush; // erase odd chars if the ANSI codes were printed raw
#ifdef _WIN32
COORD coord{0, 0};
CONSOLE_SCREEN_BUFFER_INFO csbi;
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
if (GetConsoleScreenBufferInfo(h, &csbi))
{
DWORD cbConSize = csbi.dwSize.X * csbi.dwSize.Y, w;
FillConsoleOutputCharacter(h, (TCHAR)' ', cbConSize, coord, &w);
if (GetConsoleScreenBufferInfo(h, &csbi))
FillConsoleOutputAttribute(h, csbi.wAttributes, cbConSize, coord, &w);
SetConsoleCursorPosition(h, coord);
}
#endif
}
std::pair<std::string, size_t> get_string_prefix_by_width(const std::string &s, size_t columns)
{
std::string sc = "";
size_t avail = s.size();
const char *ptr = s.data();
wint_t cp = 0;
int bytes = 1;
size_t sw = 0;
char wbuf[8], *wptr;
while (avail--)
{
if ((*ptr & 0x80) == 0)
{
cp = *ptr++;
bytes = 1;
}
else if ((*ptr & 0xe0) == 0xc0)
{
if (avail < 1)
{
MERROR("Invalid UTF-8");
return std::make_pair(s, s.size());
}
cp = (*ptr++ & 0x1f) << 6;
cp |= *ptr++ & 0x3f;
--avail;
bytes = 2;
}
else if ((*ptr & 0xf0) == 0xe0)
{
if (avail < 2)
{
MERROR("Invalid UTF-8");
return std::make_pair(s, s.size());
}
cp = (*ptr++ & 0xf) << 12;
cp |= (*ptr++ & 0x3f) << 6;
cp |= *ptr++ & 0x3f;
avail -= 2;
bytes = 3;
}
else if ((*ptr & 0xf8) == 0xf0)
{
if (avail < 3)
{
MERROR("Invalid UTF-8");
return std::make_pair(s, s.size());
}
cp = (*ptr++ & 0x7) << 18;
cp |= (*ptr++ & 0x3f) << 12;
cp |= (*ptr++ & 0x3f) << 6;
cp |= *ptr++ & 0x3f;
avail -= 3;
bytes = 4;
}
else
{
MERROR("Invalid UTF-8");
return std::make_pair(s, s.size());
}
wptr = wbuf;
switch (bytes)
{
case 1: *wptr++ = cp; break;
case 2: *wptr++ = 0xc0 | (cp >> 6); *wptr++ = 0x80 | (cp & 0x3f); break;
case 3: *wptr++ = 0xe0 | (cp >> 12); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break;
case 4: *wptr++ = 0xf0 | (cp >> 18); *wptr++ = 0x80 | ((cp >> 12) & 0x3f); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break;
default: MERROR("Invalid UTF-8"); return std::make_pair(s, s.size());
}
*wptr = 0;
sc += std::string(wbuf, bytes);
#ifdef _WIN32
int cpw = 1; // Guess who does not implement wcwidth
#else
int cpw = wcwidth(cp);
#endif
if (cpw > 0)
{
if (cpw > (int)columns)
break;
columns -= cpw;
sw += cpw;
}
cp = 0;
bytes = 1;
}
return std::make_pair(sc, sw);
}
size_t get_string_width(const std::string &s)
{
return get_string_prefix_by_width(s, 999999999).second;
};
std::vector<std::pair<std::string, size_t>> split_string_by_width(const std::string &s, size_t columns)
{
std::vector<std::string> words;
std::vector<std::pair<std::string, size_t>> lines;
boost::split(words, s, boost::is_any_of(" "), boost::token_compress_on);
// split large "words"
for (size_t i = 0; i < words.size(); ++i)
{
for (;;)
{
std::string prefix = get_string_prefix_by_width(words[i], columns).first;
if (prefix == words[i])
break;
words[i] = words[i].substr(prefix.size());
words.insert(words.begin() + i, prefix);
}
}
lines.push_back(std::make_pair("", 0));
while (!words.empty())
{
const size_t word_len = get_string_width(words.front());
size_t line_len = get_string_width(lines.back().first);
if (line_len > 0 && line_len + 1 + word_len > columns)
{
lines.push_back(std::make_pair("", 0));
line_len = 0;
}
if (line_len > 0)
{
lines.back().first += " ";
lines.back().second++;
}
lines.back().first += words.front();
lines.back().second += word_len;
words.erase(words.begin());
}
return lines;
}
} }

@ -248,4 +248,8 @@ namespace tools
std::string get_human_readable_timespan(uint64_t seconds); std::string get_human_readable_timespan(uint64_t seconds);
std::string get_human_readable_bytes(uint64_t bytes); std::string get_human_readable_bytes(uint64_t bytes);
void clear_screen();
std::vector<std::pair<std::string, size_t>> split_string_by_width(const std::string &s, size_t columns);
} }

@ -312,9 +312,7 @@ int main(int argc, char const * argv[])
{ {
login = tools::login::parse( login = tools::login::parse(
has_rpc_arg ? command_line::get_arg(vm, arg.rpc_login) : std::string(env_rpc_login), false, [](bool verify) { has_rpc_arg ? command_line::get_arg(vm, arg.rpc_login) : std::string(env_rpc_login), false, [](bool verify) {
#ifdef HAVE_READLINE PAUSE_READLINE();
rdln::suspend_readline pause_readline;
#endif
return tools::password_container::prompt(verify, "Daemon client password"); return tools::password_container::prompt(verify, "Daemon client password");
} }
); );
@ -336,9 +334,7 @@ int main(int argc, char const * argv[])
} }
else else
{ {
#ifdef HAVE_READLINE PAUSE_READLINE();
rdln::suspend_readline pause_readline;
#endif
std::cerr << "Unknown command: " << command.front() << std::endl; std::cerr << "Unknown command: " << command.front() << std::endl;
return 1; return 1;
} }

@ -33,6 +33,7 @@
* *
* \brief Source file that defines simple_wallet class. * \brief Source file that defines simple_wallet class.
*/ */
#include <locale.h>
#include <thread> #include <thread>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
@ -45,6 +46,7 @@
#include <boost/regex.hpp> #include <boost/regex.hpp>
#include <boost/range/adaptor/transformed.hpp> #include <boost/range/adaptor/transformed.hpp>
#include "include_base_utils.h" #include "include_base_utils.h"
#include "console_handler.h"
#include "common/i18n.h" #include "common/i18n.h"
#include "common/command_line.h" #include "common/command_line.h"
#include "common/util.h" #include "common/util.h"
@ -245,6 +247,7 @@ namespace
const char* USAGE_FREEZE("freeze <key_image>"); const char* USAGE_FREEZE("freeze <key_image>");
const char* USAGE_THAW("thaw <key_image>"); const char* USAGE_THAW("thaw <key_image>");
const char* USAGE_FROZEN("frozen <key_image>"); const char* USAGE_FROZEN("frozen <key_image>");
const char* USAGE_LOCK("lock");
const char* USAGE_NET_STATS("net_stats"); const char* USAGE_NET_STATS("net_stats");
const char* USAGE_WELCOME("welcome"); const char* USAGE_WELCOME("welcome");
const char* USAGE_VERSION("version"); const char* USAGE_VERSION("version");
@ -252,9 +255,7 @@ namespace
std::string input_line(const std::string& prompt, bool yesno = false) std::string input_line(const std::string& prompt, bool yesno = false)
{ {
#ifdef HAVE_READLINE PAUSE_READLINE();
rdln::suspend_readline pause_readline;
#endif
std::cout << prompt; std::cout << prompt;
if (yesno) if (yesno)
std::cout << " (Y/Yes/N/No)"; std::cout << " (Y/Yes/N/No)";
@ -272,9 +273,7 @@ namespace
epee::wipeable_string input_secure_line(const char *prompt) epee::wipeable_string input_secure_line(const char *prompt)
{ {
#ifdef HAVE_READLINE PAUSE_READLINE();
rdln::suspend_readline pause_readline;
#endif
auto pwd_container = tools::password_container::prompt(false, prompt, false); auto pwd_container = tools::password_container::prompt(false, prompt, false);
if (!pwd_container) if (!pwd_container)
{ {
@ -290,9 +289,7 @@ namespace
boost::optional<tools::password_container> password_prompter(const char *prompt, bool verify) boost::optional<tools::password_container> password_prompter(const char *prompt, bool verify)
{ {
#ifdef HAVE_READLINE PAUSE_READLINE();
rdln::suspend_readline pause_readline;
#endif
auto pwd_container = tools::password_container::prompt(verify, prompt); auto pwd_container = tools::password_container::prompt(verify, prompt);
if (!pwd_container) if (!pwd_container)
{ {
@ -2141,6 +2138,13 @@ bool simple_wallet::frozen(const std::vector<std::string> &args)
return true; return true;
} }
bool simple_wallet::lock(const std::vector<std::string> &args)
{
m_locked = true;
check_for_inactivity_lock(true);
return true;
}
bool simple_wallet::net_stats(const std::vector<std::string> &args) bool simple_wallet::net_stats(const std::vector<std::string> &args)
{ {
message_writer() << std::to_string(m_wallet->get_bytes_sent()) + tr(" bytes sent"); message_writer() << std::to_string(m_wallet->get_bytes_sent()) + tr(" bytes sent");
@ -2172,6 +2176,25 @@ bool simple_wallet::version(const std::vector<std::string> &args)
return true; return true;
} }
bool simple_wallet::on_unknown_command(const std::vector<std::string> &args)
{
if (args[0] == "exit" || args[0] == "q") // backward compat
return false;
fail_msg_writer() << boost::format(tr("Unknown command '%s', try 'help'")) % args.front();
return true;
}
bool simple_wallet::on_empty_command()
{
return true;
}
bool simple_wallet::on_cancelled_command()
{
check_for_inactivity_lock(false);
return true;
}
bool simple_wallet::cold_sign_tx(const std::vector<tools::wallet2::pending_tx>& ptx_vector, tools::wallet2::signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::function<bool(const tools::wallet2::signed_tx_set &)> accept_func) bool simple_wallet::cold_sign_tx(const std::vector<tools::wallet2::pending_tx>& ptx_vector, tools::wallet2::signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::function<bool(const tools::wallet2::signed_tx_set &)> accept_func)
{ {
std::vector<std::string> tx_aux; std::vector<std::string> tx_aux;
@ -2660,6 +2683,29 @@ bool simple_wallet::set_track_uses(const std::vector<std::string> &args/* = std:
return true; return true;
} }
bool simple_wallet::set_inactivity_lock_timeout(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
#ifdef _WIN32
tools::fail_msg_writer() << tr("Inactivity lock timeout disabled on Windows");
return true;
#endif
const auto pwd_container = get_and_verify_password();
if (pwd_container)
{
uint32_t r;
if (epee::string_tools::get_xtype_from_string(r, args[1]))
{
m_wallet->inactivity_lock_timeout(r);
m_wallet->rewrite(m_wallet_file, pwd_container->password());
}
else
{
tools::fail_msg_writer() << tr("Invalid number of seconds");
}
}
return true;
}
bool simple_wallet::set_setup_background_mining(const std::vector<std::string> &args/* = std::vector<std::string>()*/) bool simple_wallet::set_setup_background_mining(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{ {
const auto pwd_container = get_and_verify_password(); const auto pwd_container = get_and_verify_password();
@ -2768,83 +2814,86 @@ simple_wallet::simple_wallet()
, m_auto_refresh_refreshing(false) , m_auto_refresh_refreshing(false)
, m_in_manual_refresh(false) , m_in_manual_refresh(false)
, m_current_subaddress_account(0) , m_current_subaddress_account(0)
, m_last_activity_time(time(NULL))
, m_locked(false)
, m_in_command(false)
{ {
m_cmd_binder.set_handler("start_mining", m_cmd_binder.set_handler("start_mining",
boost::bind(&simple_wallet::start_mining, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::start_mining, _1),
tr(USAGE_START_MINING), tr(USAGE_START_MINING),
tr("Start mining in the daemon (bg_mining and ignore_battery are optional booleans).")); tr("Start mining in the daemon (bg_mining and ignore_battery are optional booleans)."));
m_cmd_binder.set_handler("stop_mining", m_cmd_binder.set_handler("stop_mining",
boost::bind(&simple_wallet::stop_mining, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::stop_mining, _1),
tr("Stop mining in the daemon.")); tr("Stop mining in the daemon."));
m_cmd_binder.set_handler("set_daemon", m_cmd_binder.set_handler("set_daemon",
boost::bind(&simple_wallet::set_daemon, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::set_daemon, _1),
tr(USAGE_SET_DAEMON), tr(USAGE_SET_DAEMON),
tr("Set another daemon to connect to.")); tr("Set another daemon to connect to."));
m_cmd_binder.set_handler("save_bc", m_cmd_binder.set_handler("save_bc",
boost::bind(&simple_wallet::save_bc, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::save_bc, _1),
tr("Save the current blockchain data.")); tr("Save the current blockchain data."));
m_cmd_binder.set_handler("refresh", m_cmd_binder.set_handler("refresh",
boost::bind(&simple_wallet::refresh, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::refresh, _1),
tr("Synchronize the transactions and balance.")); tr("Synchronize the transactions and balance."));
m_cmd_binder.set_handler("balance", m_cmd_binder.set_handler("balance",
boost::bind(&simple_wallet::show_balance, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::show_balance, _1),
tr(USAGE_SHOW_BALANCE), tr(USAGE_SHOW_BALANCE),
tr("Show the wallet's balance of the currently selected account.")); tr("Show the wallet's balance of the currently selected account."));
m_cmd_binder.set_handler("incoming_transfers", m_cmd_binder.set_handler("incoming_transfers",
boost::bind(&simple_wallet::show_incoming_transfers, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::show_incoming_transfers,_1),
tr(USAGE_INCOMING_TRANSFERS), tr(USAGE_INCOMING_TRANSFERS),
tr("Show the incoming transfers, all or filtered by availability and address index.\n\n" tr("Show the incoming transfers, all or filtered by availability and address index.\n\n"
"Output format:\n" "Output format:\n"
"Amount, Spent(\"T\"|\"F\"), \"frozen\"|\"locked\"|\"unlocked\", RingCT, Global Index, Transaction Hash, Address Index, [Public Key, Key Image] ")); "Amount, Spent(\"T\"|\"F\"), \"frozen\"|\"locked\"|\"unlocked\", RingCT, Global Index, Transaction Hash, Address Index, [Public Key, Key Image] "));
m_cmd_binder.set_handler("payments", m_cmd_binder.set_handler("payments",
boost::bind(&simple_wallet::show_payments, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::show_payments,_1),
tr(USAGE_PAYMENTS), tr(USAGE_PAYMENTS),
tr("Show the payments for the given payment IDs.")); tr("Show the payments for the given payment IDs."));
m_cmd_binder.set_handler("bc_height", m_cmd_binder.set_handler("bc_height",
boost::bind(&simple_wallet::show_blockchain_height, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::show_blockchain_height, _1),
tr("Show the blockchain height.")); tr("Show the blockchain height."));
m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::on_command, this, &simple_wallet::transfer, _1),
tr(USAGE_TRANSFER), tr(USAGE_TRANSFER),
tr("Transfer <amount> to <address>. If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included)")); tr("Transfer <amount> to <address>. If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included)"));
m_cmd_binder.set_handler("locked_transfer", m_cmd_binder.set_handler("locked_transfer",
boost::bind(&simple_wallet::locked_transfer, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::locked_transfer,_1),
tr(USAGE_LOCKED_TRANSFER), tr(USAGE_LOCKED_TRANSFER),
tr("Transfer <amount> to <address> and lock it for <lockblocks> (max. 1000000). If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included)")); tr("Transfer <amount> to <address> and lock it for <lockblocks> (max. 1000000). If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding URI_2 or <address_2> <amount_2> etcetera (before the payment ID, if it's included)"));
m_cmd_binder.set_handler("locked_sweep_all", m_cmd_binder.set_handler("locked_sweep_all",
boost::bind(&simple_wallet::locked_sweep_all, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::locked_sweep_all,_1),
tr(USAGE_LOCKED_SWEEP_ALL), tr(USAGE_LOCKED_SWEEP_ALL),
tr("Send all unlocked balance to an address and lock it for <lockblocks> (max. 1000000). If the parameter \"index<N1>[,<N2>,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. <priority> is the priority of the sweep. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability.")); tr("Send all unlocked balance to an address and lock it for <lockblocks> (max. 1000000). If the parameter \"index<N1>[,<N2>,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. <priority> is the priority of the sweep. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability."));
m_cmd_binder.set_handler("sweep_unmixable", m_cmd_binder.set_handler("sweep_unmixable",
boost::bind(&simple_wallet::sweep_unmixable, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_unmixable, _1),
tr("Send all unmixable outputs to yourself with ring_size 1")); tr("Send all unmixable outputs to yourself with ring_size 1"));
m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1),
tr(USAGE_SWEEP_ALL), tr(USAGE_SWEEP_ALL),
tr("Send all unlocked balance to an address. If the parameter \"index<N1>[,<N2>,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. If the parameter \"outputs=<N>\" is specified and N > 0, wallet splits the transaction into N even outputs.")); tr("Send all unlocked balance to an address. If the parameter \"index<N1>[,<N2>,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. If the parameter \"outputs=<N>\" is specified and N > 0, wallet splits the transaction into N even outputs."));
m_cmd_binder.set_handler("sweep_below", m_cmd_binder.set_handler("sweep_below",
boost::bind(&simple_wallet::sweep_below, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_below, _1),
tr(USAGE_SWEEP_BELOW), tr(USAGE_SWEEP_BELOW),
tr("Send all unlocked outputs below the threshold to an address.")); tr("Send all unlocked outputs below the threshold to an address."));
m_cmd_binder.set_handler("sweep_single", m_cmd_binder.set_handler("sweep_single",
boost::bind(&simple_wallet::sweep_single, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_single, _1),
tr(USAGE_SWEEP_SINGLE), tr(USAGE_SWEEP_SINGLE),
tr("Send a single output of the given key image to an address without change.")); tr("Send a single output of the given key image to an address without change."));
m_cmd_binder.set_handler("donate", m_cmd_binder.set_handler("donate",
boost::bind(&simple_wallet::donate, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::donate, _1),
tr(USAGE_DONATE), tr(USAGE_DONATE),
tr("Donate <amount> to the development team (donate.getmonero.org).")); tr("Donate <amount> to the development team (donate.getmonero.org)."));
m_cmd_binder.set_handler("sign_transfer", m_cmd_binder.set_handler("sign_transfer",
boost::bind(&simple_wallet::sign_transfer, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::sign_transfer, _1),
tr(USAGE_SIGN_TRANSFER), tr(USAGE_SIGN_TRANSFER),
tr("Sign a transaction from a file. If the parameter \"export_raw\" is specified, transaction raw hex data suitable for the daemon RPC /sendrawtransaction is exported.")); tr("Sign a transaction from a file. If the parameter \"export_raw\" is specified, transaction raw hex data suitable for the daemon RPC /sendrawtransaction is exported."));
m_cmd_binder.set_handler("submit_transfer", m_cmd_binder.set_handler("submit_transfer",
boost::bind(&simple_wallet::submit_transfer, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::submit_transfer, _1),
tr("Submit a signed transaction from a file.")); tr("Submit a signed transaction from a file."));
m_cmd_binder.set_handler("set_log", m_cmd_binder.set_handler("set_log",
boost::bind(&simple_wallet::set_log, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::set_log, _1),
tr(USAGE_SET_LOG), tr(USAGE_SET_LOG),
tr("Change the current log detail (level must be <0-4>).")); tr("Change the current log detail (level must be <0-4>)."));
m_cmd_binder.set_handler("account", m_cmd_binder.set_handler("account",
boost::bind(&simple_wallet::account, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::account, _1),
tr(USAGE_ACCOUNT), tr(USAGE_ACCOUNT),
tr("If no arguments are specified, the wallet shows all the existing accounts along with their balances.\n" tr("If no arguments are specified, the wallet shows all the existing accounts along with their balances.\n"
"If the \"new\" argument is specified, the wallet creates a new account with its label initialized by the provided label text (which can be empty).\n" "If the \"new\" argument is specified, the wallet creates a new account with its label initialized by the provided label text (which can be empty).\n"
@ -2854,37 +2903,37 @@ simple_wallet::simple_wallet()
"If the \"untag\" argument is specified, the tags assigned to the specified accounts <account_index_1>, <account_index_2> ..., are removed.\n" "If the \"untag\" argument is specified, the tags assigned to the specified accounts <account_index_1>, <account_index_2> ..., are removed.\n"
"If the \"tag_description\" argument is specified, the tag <tag_name> is assigned an arbitrary text <description>.")); "If the \"tag_description\" argument is specified, the tag <tag_name> is assigned an arbitrary text <description>."));
m_cmd_binder.set_handler("address", m_cmd_binder.set_handler("address",
boost::bind(&simple_wallet::print_address, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::print_address, _1),
tr(USAGE_ADDRESS), tr(USAGE_ADDRESS),
tr("If no arguments are specified or <index> is specified, the wallet shows the default or specified address. If \"all\" is specified, the wallet shows all the existing addresses in the currently selected account. If \"new \" is specified, the wallet creates a new address with the provided label text (which can be empty). If \"label\" is specified, the wallet sets the label of the address specified by <index> to the provided label text.")); tr("If no arguments are specified or <index> is specified, the wallet shows the default or specified address. If \"all\" is specified, the wallet shows all the existing addresses in the currently selected account. If \"new \" is specified, the wallet creates a new address with the provided label text (which can be empty). If \"label\" is specified, the wallet sets the label of the address specified by <index> to the provided label text."));
m_cmd_binder.set_handler("integrated_address", m_cmd_binder.set_handler("integrated_address",
boost::bind(&simple_wallet::print_integrated_address, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::print_integrated_address, _1),
tr(USAGE_INTEGRATED_ADDRESS), tr(USAGE_INTEGRATED_ADDRESS),
tr("Encode a payment ID into an integrated address for the current wallet public address (no argument uses a random payment ID), or decode an integrated address to standard address and payment ID")); tr("Encode a payment ID into an integrated address for the current wallet public address (no argument uses a random payment ID), or decode an integrated address to standard address and payment ID"));
m_cmd_binder.set_handler("address_book", m_cmd_binder.set_handler("address_book",
boost::bind(&simple_wallet::address_book, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::address_book,_1),
tr(USAGE_ADDRESS_BOOK), tr(USAGE_ADDRESS_BOOK),
tr("Print all entries in the address book, optionally adding/deleting an entry to/from it.")); tr("Print all entries in the address book, optionally adding/deleting an entry to/from it."));
m_cmd_binder.set_handler("save", m_cmd_binder.set_handler("save",
boost::bind(&simple_wallet::save, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::save, _1),
tr("Save the wallet data.")); tr("Save the wallet data."));
m_cmd_binder.set_handler("save_watch_only", m_cmd_binder.set_handler("save_watch_only",
boost::bind(&simple_wallet::save_watch_only, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::save_watch_only, _1),
tr("Save a watch-only keys file.")); tr("Save a watch-only keys file."));
m_cmd_binder.set_handler("viewkey", m_cmd_binder.set_handler("viewkey",
boost::bind(&simple_wallet::viewkey, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::viewkey, _1),
tr("Display the private view key.")); tr("Display the private view key."));
m_cmd_binder.set_handler("spendkey", m_cmd_binder.set_handler("spendkey",
boost::bind(&simple_wallet::spendkey, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::spendkey, _1),
tr("Display the private spend key.")); tr("Display the private spend key."));
m_cmd_binder.set_handler("seed", m_cmd_binder.set_handler("seed",
boost::bind(&simple_wallet::seed, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::seed, _1),
tr("Display the Electrum-style mnemonic seed")); tr("Display the Electrum-style mnemonic seed"));
m_cmd_binder.set_handler("restore_height", m_cmd_binder.set_handler("restore_height",
boost::bind(&simple_wallet::restore_height, this, _1), boost::bind(&simple_wallet::restore_height, this, _1),
tr("Display the restore height")); tr("Display the restore height"));
m_cmd_binder.set_handler("set", m_cmd_binder.set_handler("set",
boost::bind(&simple_wallet::set_variable, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::set_variable, _1),
tr(USAGE_SET_VARIABLE), tr(USAGE_SET_VARIABLE),
tr("Available options:\n " tr("Available options:\n "
"seed language\n " "seed language\n "
@ -2945,51 +2994,51 @@ simple_wallet::simple_wallet()
"export-format <\"binary\"|\"ascii\">\n " "export-format <\"binary\"|\"ascii\">\n "
" Save all exported files as binary (cannot be copied and pasted) or ascii (can be).\n ")); " Save all exported files as binary (cannot be copied and pasted) or ascii (can be).\n "));
m_cmd_binder.set_handler("encrypted_seed", m_cmd_binder.set_handler("encrypted_seed",
boost::bind(&simple_wallet::encrypted_seed, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::encrypted_seed, _1),
tr("Display the encrypted Electrum-style mnemonic seed.")); tr("Display the encrypted Electrum-style mnemonic seed."));
m_cmd_binder.set_handler("rescan_spent", m_cmd_binder.set_handler("rescan_spent",
boost::bind(&simple_wallet::rescan_spent, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::rescan_spent, _1),
tr("Rescan the blockchain for spent outputs.")); tr("Rescan the blockchain for spent outputs."));
m_cmd_binder.set_handler("get_tx_key", m_cmd_binder.set_handler("get_tx_key",
boost::bind(&simple_wallet::get_tx_key, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::get_tx_key, _1),
tr(USAGE_GET_TX_KEY), tr(USAGE_GET_TX_KEY),
tr("Get the transaction key (r) for a given <txid>.")); tr("Get the transaction key (r) for a given <txid>."));
m_cmd_binder.set_handler("set_tx_key", m_cmd_binder.set_handler("set_tx_key",
boost::bind(&simple_wallet::set_tx_key, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::set_tx_key, _1),
tr(USAGE_SET_TX_KEY), tr(USAGE_SET_TX_KEY),
tr("Set the transaction key (r) for a given <txid> in case the tx was made by some other device or 3rd party wallet.")); tr("Set the transaction key (r) for a given <txid> in case the tx was made by some other device or 3rd party wallet."));
m_cmd_binder.set_handler("check_tx_key", m_cmd_binder.set_handler("check_tx_key",
boost::bind(&simple_wallet::check_tx_key, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::check_tx_key, _1),
tr(USAGE_CHECK_TX_KEY), tr(USAGE_CHECK_TX_KEY),
tr("Check the amount going to <address> in <txid>.")); tr("Check the amount going to <address> in <txid>."));
m_cmd_binder.set_handler("get_tx_proof", m_cmd_binder.set_handler("get_tx_proof",
boost::bind(&simple_wallet::get_tx_proof, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::get_tx_proof, _1),
tr(USAGE_GET_TX_PROOF), tr(USAGE_GET_TX_PROOF),
tr("Generate a signature proving funds sent to <address> in <txid>, optionally with a challenge string <message>, using either the transaction secret key (when <address> is not your wallet's address) or the view secret key (otherwise), which does not disclose the secret key.")); tr("Generate a signature proving funds sent to <address> in <txid>, optionally with a challenge string <message>, using either the transaction secret key (when <address> is not your wallet's address) or the view secret key (otherwise), which does not disclose the secret key."));
m_cmd_binder.set_handler("check_tx_proof", m_cmd_binder.set_handler("check_tx_proof",
boost::bind(&simple_wallet::check_tx_proof, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::check_tx_proof, _1),
tr(USAGE_CHECK_TX_PROOF), tr(USAGE_CHECK_TX_PROOF),
tr("Check the proof for funds going to <address> in <txid> with the challenge string <message> if any.")); tr("Check the proof for funds going to <address> in <txid> with the challenge string <message> if any."));
m_cmd_binder.set_handler("get_spend_proof", m_cmd_binder.set_handler("get_spend_proof",
boost::bind(&simple_wallet::get_spend_proof, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::get_spend_proof, _1),
tr(USAGE_GET_SPEND_PROOF), tr(USAGE_GET_SPEND_PROOF),
tr("Generate a signature proving that you generated <txid> using the spend secret key, optionally with a challenge string <message>.")); tr("Generate a signature proving that you generated <txid> using the spend secret key, optionally with a challenge string <message>."));
m_cmd_binder.set_handler("check_spend_proof", m_cmd_binder.set_handler("check_spend_proof",
boost::bind(&simple_wallet::check_spend_proof, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::check_spend_proof, _1),
tr(USAGE_CHECK_SPEND_PROOF), tr(USAGE_CHECK_SPEND_PROOF),
tr("Check a signature proving that the signer generated <txid>, optionally with a challenge string <message>.")); tr("Check a signature proving that the signer generated <txid>, optionally with a challenge string <message>."));
m_cmd_binder.set_handler("get_reserve_proof", m_cmd_binder.set_handler("get_reserve_proof",
boost::bind(&simple_wallet::get_reserve_proof, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::get_reserve_proof, _1),
tr(USAGE_GET_RESERVE_PROOF), tr(USAGE_GET_RESERVE_PROOF),
tr("Generate a signature proving that you own at least this much, optionally with a challenge string <message>.\n" tr("Generate a signature proving that you own at least this much, optionally with a challenge string <message>.\n"
"If 'all' is specified, you prove the entire sum of all of your existing accounts' balances.\n" "If 'all' is specified, you prove the entire sum of all of your existing accounts' balances.\n"
"Otherwise, you prove the reserve of the smallest possible amount above <amount> available in your current account.")); "Otherwise, you prove the reserve of the smallest possible amount above <amount> available in your current account."));
m_cmd_binder.set_handler("check_reserve_proof", m_cmd_binder.set_handler("check_reserve_proof",
boost::bind(&simple_wallet::check_reserve_proof, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::check_reserve_proof, _1),
tr(USAGE_CHECK_RESERVE_PROOF), tr(USAGE_CHECK_RESERVE_PROOF),
tr("Check a signature proving that the owner of <address> holds at least this much, optionally with a challenge string <message>.")); tr("Check a signature proving that the owner of <address> holds at least this much, optionally with a challenge string <message>."));
m_cmd_binder.set_handler("show_transfers", m_cmd_binder.set_handler("show_transfers",
boost::bind(&simple_wallet::show_transfers, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::show_transfers, _1),
tr(USAGE_SHOW_TRANSFERS), tr(USAGE_SHOW_TRANSFERS),
// Seemingly broken formatting to compensate for the backslash before the quotes. // Seemingly broken formatting to compensate for the backslash before the quotes.
tr("Show the incoming/outgoing transfers within an optional height range.\n\n" tr("Show the incoming/outgoing transfers within an optional height range.\n\n"
@ -3001,120 +3050,120 @@ simple_wallet::simple_wallet()
"* Excluding change and fee.\n" "* Excluding change and fee.\n"
"** Set of address indices used as inputs in this transfer.")); "** Set of address indices used as inputs in this transfer."));
m_cmd_binder.set_handler("export_transfers", m_cmd_binder.set_handler("export_transfers",
boost::bind(&simple_wallet::export_transfers, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::export_transfers, _1),
tr("export_transfers [in|out|all|pending|failed|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] [output=<filepath>]"), tr("export_transfers [in|out|all|pending|failed|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] [output=<filepath>]"),
tr("Export to CSV the incoming/outgoing transfers within an optional height range.")); tr("Export to CSV the incoming/outgoing transfers within an optional height range."));
m_cmd_binder.set_handler("unspent_outputs", m_cmd_binder.set_handler("unspent_outputs",
boost::bind(&simple_wallet::unspent_outputs, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::unspent_outputs, _1),
tr(USAGE_UNSPENT_OUTPUTS), tr(USAGE_UNSPENT_OUTPUTS),
tr("Show the unspent outputs of a specified address within an optional amount range.")); tr("Show the unspent outputs of a specified address within an optional amount range."));
m_cmd_binder.set_handler("rescan_bc", m_cmd_binder.set_handler("rescan_bc",
boost::bind(&simple_wallet::rescan_blockchain, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::rescan_blockchain, _1),
tr(USAGE_RESCAN_BC), tr(USAGE_RESCAN_BC),
tr("Rescan the blockchain from scratch. If \"hard\" is specified, you will lose any information which can not be recovered from the blockchain itself.")); tr("Rescan the blockchain from scratch. If \"hard\" is specified, you will lose any information which can not be recovered from the blockchain itself."));
m_cmd_binder.set_handler("set_tx_note", m_cmd_binder.set_handler("set_tx_note",
boost::bind(&simple_wallet::set_tx_note, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::set_tx_note, _1),
tr(USAGE_SET_TX_NOTE), tr(USAGE_SET_TX_NOTE),
tr("Set an arbitrary string note for a <txid>.")); tr("Set an arbitrary string note for a <txid>."));
m_cmd_binder.set_handler("get_tx_note", m_cmd_binder.set_handler("get_tx_note",
boost::bind(&simple_wallet::get_tx_note, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::get_tx_note, _1),
tr(USAGE_GET_TX_NOTE), tr(USAGE_GET_TX_NOTE),
tr("Get a string note for a txid.")); tr("Get a string note for a txid."));
m_cmd_binder.set_handler("set_description", m_cmd_binder.set_handler("set_description",
boost::bind(&simple_wallet::set_description, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::set_description, _1),
tr(USAGE_SET_DESCRIPTION), tr(USAGE_SET_DESCRIPTION),
tr("Set an arbitrary description for the wallet.")); tr("Set an arbitrary description for the wallet."));
m_cmd_binder.set_handler("get_description", m_cmd_binder.set_handler("get_description",
boost::bind(&simple_wallet::get_description, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::get_description, _1),
tr(USAGE_GET_DESCRIPTION), tr(USAGE_GET_DESCRIPTION),
tr("Get the description of the wallet.")); tr("Get the description of the wallet."));
m_cmd_binder.set_handler("status", m_cmd_binder.set_handler("status",
boost::bind(&simple_wallet::status, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::status, _1),
tr("Show the wallet's status.")); tr("Show the wallet's status."));
m_cmd_binder.set_handler("wallet_info", m_cmd_binder.set_handler("wallet_info",
boost::bind(&simple_wallet::wallet_info, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::wallet_info, _1),
tr("Show the wallet's information.")); tr("Show the wallet's information."));
m_cmd_binder.set_handler("sign", m_cmd_binder.set_handler("sign",
boost::bind(&simple_wallet::sign, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::sign, _1),
tr(USAGE_SIGN), tr(USAGE_SIGN),
tr("Sign the contents of a file.")); tr("Sign the contents of a file."));
m_cmd_binder.set_handler("verify", m_cmd_binder.set_handler("verify",
boost::bind(&simple_wallet::verify, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::verify, _1),
tr(USAGE_VERIFY), tr(USAGE_VERIFY),
tr("Verify a signature on the contents of a file.")); tr("Verify a signature on the contents of a file."));
m_cmd_binder.set_handler("export_key_images", m_cmd_binder.set_handler("export_key_images",
boost::bind(&simple_wallet::export_key_images, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::export_key_images, _1),
tr(USAGE_EXPORT_KEY_IMAGES), tr(USAGE_EXPORT_KEY_IMAGES),
tr("Export a signed set of key images to a <filename>.")); tr("Export a signed set of key images to a <filename>."));
m_cmd_binder.set_handler("import_key_images", m_cmd_binder.set_handler("import_key_images",
boost::bind(&simple_wallet::import_key_images, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::import_key_images, _1),
tr(USAGE_IMPORT_KEY_IMAGES), tr(USAGE_IMPORT_KEY_IMAGES),
tr("Import a signed key images list and verify their spent status.")); tr("Import a signed key images list and verify their spent status."));
m_cmd_binder.set_handler("hw_key_images_sync", m_cmd_binder.set_handler("hw_key_images_sync",
boost::bind(&simple_wallet::hw_key_images_sync, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::hw_key_images_sync, _1),
tr(USAGE_HW_KEY_IMAGES_SYNC), tr(USAGE_HW_KEY_IMAGES_SYNC),
tr("Synchronizes key images with the hw wallet.")); tr("Synchronizes key images with the hw wallet."));
m_cmd_binder.set_handler("hw_reconnect", m_cmd_binder.set_handler("hw_reconnect",
boost::bind(&simple_wallet::hw_reconnect, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::hw_reconnect, _1),
tr(USAGE_HW_RECONNECT), tr(USAGE_HW_RECONNECT),
tr("Attempts to reconnect HW wallet.")); tr("Attempts to reconnect HW wallet."));
m_cmd_binder.set_handler("export_outputs", m_cmd_binder.set_handler("export_outputs",
boost::bind(&simple_wallet::export_outputs, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::export_outputs, _1),
tr(USAGE_EXPORT_OUTPUTS), tr(USAGE_EXPORT_OUTPUTS),
tr("Export a set of outputs owned by this wallet.")); tr("Export a set of outputs owned by this wallet."));
m_cmd_binder.set_handler("import_outputs", m_cmd_binder.set_handler("import_outputs",
boost::bind(&simple_wallet::import_outputs, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::import_outputs, _1),
tr(USAGE_IMPORT_OUTPUTS), tr(USAGE_IMPORT_OUTPUTS),
tr("Import a set of outputs owned by this wallet.")); tr("Import a set of outputs owned by this wallet."));
m_cmd_binder.set_handler("show_transfer", m_cmd_binder.set_handler("show_transfer",
boost::bind(&simple_wallet::show_transfer, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::show_transfer, _1),
tr(USAGE_SHOW_TRANSFER), tr(USAGE_SHOW_TRANSFER),
tr("Show information about a transfer to/from this address.")); tr("Show information about a transfer to/from this address."));
m_cmd_binder.set_handler("password", m_cmd_binder.set_handler("password",
boost::bind(&simple_wallet::change_password, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::change_password, _1),
tr("Change the wallet's password.")); tr("Change the wallet's password."));
m_cmd_binder.set_handler("payment_id", m_cmd_binder.set_handler("payment_id",
boost::bind(&simple_wallet::payment_id, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::payment_id, _1),
tr(USAGE_PAYMENT_ID), tr(USAGE_PAYMENT_ID),
tr("Generate a new random full size payment id (obsolete). These will be unencrypted on the blockchain, see integrated_address for encrypted short payment ids.")); tr("Generate a new random full size payment id (obsolete). These will be unencrypted on the blockchain, see integrated_address for encrypted short payment ids."));
m_cmd_binder.set_handler("fee", m_cmd_binder.set_handler("fee",
boost::bind(&simple_wallet::print_fee_info, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::print_fee_info, _1),
tr("Print the information about the current fee and transaction backlog.")); tr("Print the information about the current fee and transaction backlog."));
m_cmd_binder.set_handler("prepare_multisig", boost::bind(&simple_wallet::prepare_multisig, this, _1), m_cmd_binder.set_handler("prepare_multisig", boost::bind(&simple_wallet::on_command, this, &simple_wallet::prepare_multisig, _1),
tr("Export data needed to create a multisig wallet")); tr("Export data needed to create a multisig wallet"));
m_cmd_binder.set_handler("make_multisig", boost::bind(&simple_wallet::make_multisig, this, _1), m_cmd_binder.set_handler("make_multisig", boost::bind(&simple_wallet::on_command, this, &simple_wallet::make_multisig, _1),
tr(USAGE_MAKE_MULTISIG), tr(USAGE_MAKE_MULTISIG),
tr("Turn this wallet into a multisig wallet")); tr("Turn this wallet into a multisig wallet"));
m_cmd_binder.set_handler("finalize_multisig", m_cmd_binder.set_handler("finalize_multisig",
boost::bind(&simple_wallet::finalize_multisig, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::finalize_multisig, _1),
tr(USAGE_FINALIZE_MULTISIG), tr(USAGE_FINALIZE_MULTISIG),
tr("Turn this wallet into a multisig wallet, extra step for N-1/N wallets")); tr("Turn this wallet into a multisig wallet, extra step for N-1/N wallets"));
m_cmd_binder.set_handler("exchange_multisig_keys", m_cmd_binder.set_handler("exchange_multisig_keys",
boost::bind(&simple_wallet::exchange_multisig_keys, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::exchange_multisig_keys, _1),
tr(USAGE_EXCHANGE_MULTISIG_KEYS), tr(USAGE_EXCHANGE_MULTISIG_KEYS),
tr("Performs extra multisig keys exchange rounds. Needed for arbitrary M/N multisig wallets")); tr("Performs extra multisig keys exchange rounds. Needed for arbitrary M/N multisig wallets"));
m_cmd_binder.set_handler("export_multisig_info", m_cmd_binder.set_handler("export_multisig_info",
boost::bind(&simple_wallet::export_multisig, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::export_multisig, _1),
tr(USAGE_EXPORT_MULTISIG_INFO), tr(USAGE_EXPORT_MULTISIG_INFO),
tr("Export multisig info for other participants")); tr("Export multisig info for other participants"));
m_cmd_binder.set_handler("import_multisig_info", m_cmd_binder.set_handler("import_multisig_info",
boost::bind(&simple_wallet::import_multisig, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::import_multisig, _1),
tr(USAGE_IMPORT_MULTISIG_INFO), tr(USAGE_IMPORT_MULTISIG_INFO),
tr("Import multisig info from other participants")); tr("Import multisig info from other participants"));
m_cmd_binder.set_handler("sign_multisig", m_cmd_binder.set_handler("sign_multisig",
boost::bind(&simple_wallet::sign_multisig, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::sign_multisig, _1),
tr(USAGE_SIGN_MULTISIG), tr(USAGE_SIGN_MULTISIG),
tr("Sign a multisig transaction from a file")); tr("Sign a multisig transaction from a file"));
m_cmd_binder.set_handler("submit_multisig", m_cmd_binder.set_handler("submit_multisig",
boost::bind(&simple_wallet::submit_multisig, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::submit_multisig, _1),
tr(USAGE_SUBMIT_MULTISIG), tr(USAGE_SUBMIT_MULTISIG),
tr("Submit a signed multisig transaction from a file")); tr("Submit a signed multisig transaction from a file"));
m_cmd_binder.set_handler("export_raw_multisig_tx", m_cmd_binder.set_handler("export_raw_multisig_tx",
boost::bind(&simple_wallet::export_raw_multisig, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::export_raw_multisig, _1),
tr(USAGE_EXPORT_RAW_MULTISIG_TX), tr(USAGE_EXPORT_RAW_MULTISIG_TX),
tr("Export a signed multisig transaction to a file")); tr("Export a signed multisig transaction to a file"));
m_cmd_binder.set_handler("mms", m_cmd_binder.set_handler("mms",
boost::bind(&simple_wallet::mms, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
tr(USAGE_MMS), tr(USAGE_MMS),
tr("Interface with the MMS (Multisig Messaging System)\n" tr("Interface with the MMS (Multisig Messaging System)\n"
"<subcommand> is one of:\n" "<subcommand> is one of:\n"
@ -3122,60 +3171,60 @@ simple_wallet::simple_wallet()
" send_signer_config, start_auto_config, stop_auto_config, auto_config\n" " send_signer_config, start_auto_config, stop_auto_config, auto_config\n"
"Get help about a subcommand with: help mms <subcommand>, or mms help <subcommand>")); "Get help about a subcommand with: help mms <subcommand>, or mms help <subcommand>"));
m_cmd_binder.set_handler("mms init", m_cmd_binder.set_handler("mms init",
boost::bind(&simple_wallet::mms, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
tr(USAGE_MMS_INIT), tr(USAGE_MMS_INIT),
tr("Initialize and configure the MMS for M/N = number of required signers/number of authorized signers multisig")); tr("Initialize and configure the MMS for M/N = number of required signers/number of authorized signers multisig"));
m_cmd_binder.set_handler("mms info", m_cmd_binder.set_handler("mms info",
boost::bind(&simple_wallet::mms, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
tr(USAGE_MMS_INFO), tr(USAGE_MMS_INFO),
tr("Display current MMS configuration")); tr("Display current MMS configuration"));
m_cmd_binder.set_handler("mms signer", m_cmd_binder.set_handler("mms signer",
boost::bind(&simple_wallet::mms, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
tr(USAGE_MMS_SIGNER), tr(USAGE_MMS_SIGNER),
tr("Set or modify authorized signer info (single-word label, transport address, Monero address), or list all signers")); tr("Set or modify authorized signer info (single-word label, transport address, Monero address), or list all signers"));
m_cmd_binder.set_handler("mms list", m_cmd_binder.set_handler("mms list",
boost::bind(&simple_wallet::mms, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
tr(USAGE_MMS_LIST), tr(USAGE_MMS_LIST),
tr("List all messages")); tr("List all messages"));
m_cmd_binder.set_handler("mms next", m_cmd_binder.set_handler("mms next",
boost::bind(&simple_wallet::mms, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
tr(USAGE_MMS_NEXT), tr(USAGE_MMS_NEXT),
tr("Evaluate the next possible multisig-related action(s) according to wallet state, and execute or offer for choice\n" tr("Evaluate the next possible multisig-related action(s) according to wallet state, and execute or offer for choice\n"
"By using 'sync' processing of waiting messages with multisig sync info can be forced regardless of wallet state")); "By using 'sync' processing of waiting messages with multisig sync info can be forced regardless of wallet state"));
m_cmd_binder.set_handler("mms sync", m_cmd_binder.set_handler("mms sync",
boost::bind(&simple_wallet::mms, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
tr(USAGE_MMS_SYNC), tr(USAGE_MMS_SYNC),
tr("Force generation of multisig sync info regardless of wallet state, to recover from special situations like \"stale data\" errors")); tr("Force generation of multisig sync info regardless of wallet state, to recover from special situations like \"stale data\" errors"));
m_cmd_binder.set_handler("mms transfer", m_cmd_binder.set_handler("mms transfer",
boost::bind(&simple_wallet::mms, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
tr(USAGE_MMS_TRANSFER), tr(USAGE_MMS_TRANSFER),
tr("Initiate transfer with MMS support; arguments identical to normal 'transfer' command arguments, for info see there")); tr("Initiate transfer with MMS support; arguments identical to normal 'transfer' command arguments, for info see there"));
m_cmd_binder.set_handler("mms delete", m_cmd_binder.set_handler("mms delete",
boost::bind(&simple_wallet::mms, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
tr(USAGE_MMS_DELETE), tr(USAGE_MMS_DELETE),
tr("Delete a single message by giving its id, or delete all messages by using 'all'")); tr("Delete a single message by giving its id, or delete all messages by using 'all'"));
m_cmd_binder.set_handler("mms send", m_cmd_binder.set_handler("mms send",
boost::bind(&simple_wallet::mms, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
tr(USAGE_MMS_SEND), tr(USAGE_MMS_SEND),
tr("Send a single message by giving its id, or send all waiting messages")); tr("Send a single message by giving its id, or send all waiting messages"));
m_cmd_binder.set_handler("mms receive", m_cmd_binder.set_handler("mms receive",
boost::bind(&simple_wallet::mms, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
tr(USAGE_MMS_RECEIVE), tr(USAGE_MMS_RECEIVE),
tr("Check right away for new messages to receive")); tr("Check right away for new messages to receive"));
m_cmd_binder.set_handler("mms export", m_cmd_binder.set_handler("mms export",
boost::bind(&simple_wallet::mms, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
tr(USAGE_MMS_EXPORT), tr(USAGE_MMS_EXPORT),
tr("Write the content of a message to a file \"mms_message_content\"")); tr("Write the content of a message to a file \"mms_message_content\""));
m_cmd_binder.set_handler("mms note", m_cmd_binder.set_handler("mms note",
boost::bind(&simple_wallet::mms, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
tr(USAGE_MMS_NOTE), tr(USAGE_MMS_NOTE),
tr("Send a one-line message to an authorized signer, identified by its label, or show any waiting unread notes")); tr("Send a one-line message to an authorized signer, identified by its label, or show any waiting unread notes"));
m_cmd_binder.set_handler("mms show", m_cmd_binder.set_handler("mms show",
boost::bind(&simple_wallet::mms, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
tr(USAGE_MMS_SHOW), tr(USAGE_MMS_SHOW),
tr("Show detailed info about a single message")); tr("Show detailed info about a single message"));
m_cmd_binder.set_handler("mms set", m_cmd_binder.set_handler("mms set",
boost::bind(&simple_wallet::mms, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
tr(USAGE_MMS_SET), tr(USAGE_MMS_SET),
tr("Available options:\n " tr("Available options:\n "
"auto-send <1|0>\n " "auto-send <1|0>\n "
@ -3185,75 +3234,82 @@ simple_wallet::simple_wallet()
tr(USAGE_MMS_SEND_SIGNER_CONFIG), tr(USAGE_MMS_SEND_SIGNER_CONFIG),
tr("Send completed signer config to all other authorized signers")); tr("Send completed signer config to all other authorized signers"));
m_cmd_binder.set_handler("mms start_auto_config", m_cmd_binder.set_handler("mms start_auto_config",
boost::bind(&simple_wallet::mms, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
tr(USAGE_MMS_START_AUTO_CONFIG), tr(USAGE_MMS_START_AUTO_CONFIG),
tr("Start auto-config at the auto-config manager's wallet by issuing auto-config tokens and optionally set others' labels")); tr("Start auto-config at the auto-config manager's wallet by issuing auto-config tokens and optionally set others' labels"));
m_cmd_binder.set_handler("mms stop_auto_config", m_cmd_binder.set_handler("mms stop_auto_config",
boost::bind(&simple_wallet::mms, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
tr(USAGE_MMS_STOP_AUTO_CONFIG), tr(USAGE_MMS_STOP_AUTO_CONFIG),
tr("Delete any auto-config tokens and abort a auto-config process")); tr("Delete any auto-config tokens and abort a auto-config process"));
m_cmd_binder.set_handler("mms auto_config", m_cmd_binder.set_handler("mms auto_config",
boost::bind(&simple_wallet::mms, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1),
tr(USAGE_MMS_AUTO_CONFIG), tr(USAGE_MMS_AUTO_CONFIG),
tr("Start auto-config by using the token received from the auto-config manager")); tr("Start auto-config by using the token received from the auto-config manager"));
m_cmd_binder.set_handler("print_ring", m_cmd_binder.set_handler("print_ring",
boost::bind(&simple_wallet::print_ring, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::print_ring, _1),
tr(USAGE_PRINT_RING), tr(USAGE_PRINT_RING),
tr("Print the ring(s) used to spend a given key image or transaction (if the ring size is > 1)\n\n" tr("Print the ring(s) used to spend a given key image or transaction (if the ring size is > 1)\n\n"
"Output format:\n" "Output format:\n"
"Key Image, \"absolute\", list of rings")); "Key Image, \"absolute\", list of rings"));
m_cmd_binder.set_handler("set_ring", m_cmd_binder.set_handler("set_ring",
boost::bind(&simple_wallet::set_ring, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::set_ring, _1),
tr(USAGE_SET_RING), tr(USAGE_SET_RING),
tr("Set the ring used for a given key image, so it can be reused in a fork")); tr("Set the ring used for a given key image, so it can be reused in a fork"));
m_cmd_binder.set_handler("unset_ring", m_cmd_binder.set_handler("unset_ring",
boost::bind(&simple_wallet::unset_ring, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::unset_ring, _1),
tr(USAGE_UNSET_RING), tr(USAGE_UNSET_RING),
tr("Unsets the ring used for a given key image or transaction")); tr("Unsets the ring used for a given key image or transaction"));
m_cmd_binder.set_handler("save_known_rings", m_cmd_binder.set_handler("save_known_rings",
boost::bind(&simple_wallet::save_known_rings, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::save_known_rings, _1),
tr(USAGE_SAVE_KNOWN_RINGS), tr(USAGE_SAVE_KNOWN_RINGS),
tr("Save known rings to the shared rings database")); tr("Save known rings to the shared rings database"));
m_cmd_binder.set_handler("mark_output_spent", m_cmd_binder.set_handler("mark_output_spent",
boost::bind(&simple_wallet::blackball, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::blackball, _1),
tr(USAGE_MARK_OUTPUT_SPENT), tr(USAGE_MARK_OUTPUT_SPENT),
tr("Mark output(s) as spent so they never get selected as fake outputs in a ring")); tr("Mark output(s) as spent so they never get selected as fake outputs in a ring"));
m_cmd_binder.set_handler("mark_output_unspent", m_cmd_binder.set_handler("mark_output_unspent",
boost::bind(&simple_wallet::unblackball, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::unblackball, _1),
tr(USAGE_MARK_OUTPUT_UNSPENT), tr(USAGE_MARK_OUTPUT_UNSPENT),
tr("Marks an output as unspent so it may get selected as a fake output in a ring")); tr("Marks an output as unspent so it may get selected as a fake output in a ring"));
m_cmd_binder.set_handler("is_output_spent", m_cmd_binder.set_handler("is_output_spent",
boost::bind(&simple_wallet::blackballed, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::blackballed, _1),
tr(USAGE_IS_OUTPUT_SPENT), tr(USAGE_IS_OUTPUT_SPENT),
tr("Checks whether an output is marked as spent")); tr("Checks whether an output is marked as spent"));
m_cmd_binder.set_handler("freeze", m_cmd_binder.set_handler("freeze",
boost::bind(&simple_wallet::freeze, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::freeze, _1),
tr(USAGE_FREEZE), tr(USAGE_FREEZE),
tr("Freeze a single output by key image so it will not be used")); tr("Freeze a single output by key image so it will not be used"));
m_cmd_binder.set_handler("thaw", m_cmd_binder.set_handler("thaw",
boost::bind(&simple_wallet::thaw, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::thaw, _1),
tr(USAGE_THAW), tr(USAGE_THAW),
tr("Thaw a single output by key image so it may be used again")); tr("Thaw a single output by key image so it may be used again"));
m_cmd_binder.set_handler("frozen", m_cmd_binder.set_handler("frozen",
boost::bind(&simple_wallet::frozen, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::frozen, _1),
tr(USAGE_FROZEN), tr(USAGE_FROZEN),
tr("Checks whether a given output is currently frozen by key image")); tr("Checks whether a given output is currently frozen by key image"));
m_cmd_binder.set_handler("lock",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::lock, _1),
tr(USAGE_LOCK),
tr("Lock the wallet console, requiring the wallet password to continue"));
m_cmd_binder.set_handler("net_stats", m_cmd_binder.set_handler("net_stats",
boost::bind(&simple_wallet::net_stats, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::net_stats, _1),
tr(USAGE_NET_STATS), tr(USAGE_NET_STATS),
tr("Prints simple network stats")); tr("Prints simple network stats"));
m_cmd_binder.set_handler("welcome", m_cmd_binder.set_handler("welcome",
boost::bind(&simple_wallet::welcome, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::welcome, _1),
tr(USAGE_WELCOME), tr(USAGE_WELCOME),
tr("Prints basic info about Monero for first time users")); tr("Prints basic info about Monero for first time users"));
m_cmd_binder.set_handler("version", m_cmd_binder.set_handler("version",
boost::bind(&simple_wallet::version, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::version, _1),
tr(USAGE_VERSION), tr(USAGE_VERSION),
tr("Returns version information")); tr("Returns version information"));
m_cmd_binder.set_handler("help", m_cmd_binder.set_handler("help",
boost::bind(&simple_wallet::help, this, _1), boost::bind(&simple_wallet::on_command, this, &simple_wallet::help, _1),
tr(USAGE_HELP), tr(USAGE_HELP),
tr("Show the help section or the documentation about a <command>.")); tr("Show the help section or the documentation about a <command>."));
m_cmd_binder.set_unknown_command_handler(boost::bind(&simple_wallet::on_command, this, &simple_wallet::on_unknown_command, _1));
m_cmd_binder.set_empty_command_handler(boost::bind(&simple_wallet::on_empty_command, this));
m_cmd_binder.set_cancel_handler(boost::bind(&simple_wallet::on_cancelled_command, this));
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool simple_wallet::set_variable(const std::vector<std::string> &args) bool simple_wallet::set_variable(const std::vector<std::string> &args)
@ -3310,6 +3366,11 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
success_msg_writer() << "setup-background-mining = " << setup_background_mining_string; success_msg_writer() << "setup-background-mining = " << setup_background_mining_string;
success_msg_writer() << "device-name = " << m_wallet->device_name(); success_msg_writer() << "device-name = " << m_wallet->device_name();
success_msg_writer() << "export-format = " << (m_wallet->export_format() == tools::wallet2::ExportFormat::Ascii ? "ascii" : "binary"); success_msg_writer() << "export-format = " << (m_wallet->export_format() == tools::wallet2::ExportFormat::Ascii ? "ascii" : "binary");
success_msg_writer() << "inactivity-lock-timeout = " << m_wallet->inactivity_lock_timeout()
#ifdef _WIN32
<< " (disabled on Windows)"
#endif
;
return true; return true;
} }
else else
@ -3366,6 +3427,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
CHECK_SIMPLE_VARIABLE("segregation-height", set_segregation_height, tr("unsigned integer")); CHECK_SIMPLE_VARIABLE("segregation-height", set_segregation_height, tr("unsigned integer"));
CHECK_SIMPLE_VARIABLE("ignore-fractional-outputs", set_ignore_fractional_outputs, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("ignore-fractional-outputs", set_ignore_fractional_outputs, tr("0 or 1"));
CHECK_SIMPLE_VARIABLE("track-uses", set_track_uses, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("track-uses", set_track_uses, tr("0 or 1"));
CHECK_SIMPLE_VARIABLE("inactivity-lock-timeout", set_inactivity_lock_timeout, tr("unsigned integer (seconds, 0 to disable)"));
CHECK_SIMPLE_VARIABLE("setup-background-mining", set_setup_background_mining, tr("1/yes or 0/no")); CHECK_SIMPLE_VARIABLE("setup-background-mining", set_setup_background_mining, tr("1/yes or 0/no"));
CHECK_SIMPLE_VARIABLE("device-name", set_device_name, tr("<device_name[:device_spec]>")); CHECK_SIMPLE_VARIABLE("device-name", set_device_name, tr("<device_name[:device_spec]>"));
CHECK_SIMPLE_VARIABLE("export-format", set_export_format, tr("\"binary\" or \"ascii\"")); CHECK_SIMPLE_VARIABLE("export-format", set_export_format, tr("\"binary\" or \"ascii\""));
@ -4156,6 +4218,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
tr("It is recommended that you do not use them, and ask recipients who ask for one to not endanger your privacy."); tr("It is recommended that you do not use them, and ask recipients who ask for one to not endanger your privacy.");
} }
m_last_activity_time = time(NULL);
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
@ -4994,12 +5057,16 @@ bool simple_wallet::save_bc(const std::vector<std::string>& args)
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void simple_wallet::on_new_block(uint64_t height, const cryptonote::block& block) void simple_wallet::on_new_block(uint64_t height, const cryptonote::block& block)
{ {
if (m_locked)
return;
if (!m_auto_refresh_refreshing) if (!m_auto_refresh_refreshing)
m_refresh_progress_reporter.update(height, false); m_refresh_progress_reporter.update(height, false);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, uint64_t unlock_time) void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, uint64_t unlock_time)
{ {
if (m_locked)
return;
message_writer(console_color_green, false) << "\r" << message_writer(console_color_green, false) << "\r" <<
tr("Height ") << height << ", " << tr("Height ") << height << ", " <<
tr("txid ") << txid << ", " << tr("txid ") << txid << ", " <<
@ -5030,11 +5097,15 @@ void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid,
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void simple_wallet::on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index) void simple_wallet::on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index)
{ {
if (m_locked)
return;
// Not implemented in CLI wallet // Not implemented in CLI wallet
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void simple_wallet::on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index) void simple_wallet::on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index)
{ {
if (m_locked)
return;
message_writer(console_color_magenta, false) << "\r" << message_writer(console_color_magenta, false) << "\r" <<
tr("Height ") << height << ", " << tr("Height ") << height << ", " <<
tr("txid ") << txid << ", " << tr("txid ") << txid << ", " <<
@ -5048,10 +5119,14 @@ void simple_wallet::on_money_spent(uint64_t height, const crypto::hash &txid, co
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void simple_wallet::on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) void simple_wallet::on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx)
{ {
if (m_locked)
return;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
boost::optional<epee::wipeable_string> simple_wallet::on_get_password(const char *reason) boost::optional<epee::wipeable_string> simple_wallet::on_get_password(const char *reason)
{ {
if (m_locked)
return boost::none;
// can't ask for password from a background thread // can't ask for password from a background thread
if (!m_in_manual_refresh.load(std::memory_order_relaxed)) if (!m_in_manual_refresh.load(std::memory_order_relaxed))
{ {
@ -5060,9 +5135,7 @@ boost::optional<epee::wipeable_string> simple_wallet::on_get_password(const char
return boost::none; return boost::none;
} }
#ifdef HAVE_READLINE PAUSE_READLINE();
rdln::suspend_readline pause_readline;
#endif
std::string msg = tr("Enter password"); std::string msg = tr("Enter password");
if (reason && *reason) if (reason && *reason)
msg += std::string(" (") + reason + ")"; msg += std::string(" (") + reason + ")";
@ -5083,9 +5156,7 @@ void simple_wallet::on_device_button_request(uint64_t code)
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
boost::optional<epee::wipeable_string> simple_wallet::on_device_pin_request() boost::optional<epee::wipeable_string> simple_wallet::on_device_pin_request()
{ {
#ifdef HAVE_READLINE PAUSE_READLINE();
rdln::suspend_readline pause_readline;
#endif
std::string msg = tr("Enter device PIN"); std::string msg = tr("Enter device PIN");
auto pwd_container = tools::password_container::prompt(false, msg.c_str()); auto pwd_container = tools::password_container::prompt(false, msg.c_str());
THROW_WALLET_EXCEPTION_IF(!pwd_container, tools::error::password_entry_failed, tr("Failed to read device PIN")); THROW_WALLET_EXCEPTION_IF(!pwd_container, tools::error::password_entry_failed, tr("Failed to read device PIN"));
@ -5099,9 +5170,7 @@ boost::optional<epee::wipeable_string> simple_wallet::on_device_passphrase_reque
return boost::none; return boost::none;
} }
#ifdef HAVE_READLINE PAUSE_READLINE();
rdln::suspend_readline pause_readline;
#endif
std::string msg = tr("Enter device passphrase"); std::string msg = tr("Enter device passphrase");
auto pwd_container = tools::password_container::prompt(false, msg.c_str()); auto pwd_container = tools::password_container::prompt(false, msg.c_str());
THROW_WALLET_EXCEPTION_IF(!pwd_container, tools::error::password_entry_failed, tr("Failed to read device passphrase")); THROW_WALLET_EXCEPTION_IF(!pwd_container, tools::error::password_entry_failed, tr("Failed to read device passphrase"));
@ -5148,9 +5217,7 @@ bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bo
m_wallet->rescan_blockchain(reset == ResetHard, false, reset == ResetSoftKeepKI); m_wallet->rescan_blockchain(reset == ResetHard, false, reset == ResetSoftKeepKI);
} }
#ifdef HAVE_READLINE PAUSE_READLINE();
rdln::suspend_readline pause_readline;
#endif
message_writer() << tr("Starting refresh..."); message_writer() << tr("Starting refresh...");
@ -5708,6 +5775,67 @@ bool simple_wallet::prompt_if_old(const std::vector<tools::wallet2::pending_tx>
} }
return true; return true;
} }
void simple_wallet::check_for_inactivity_lock(bool user)
{
if (m_locked)
{
#ifdef HAVE_READLINE
PAUSE_READLINE();
rdln::clear_screen();
#endif
tools::clear_screen();
m_in_command = true;
if (!user)
{
const std::string speech = tr("I locked your Monero wallet to protect you while you were away");
std::vector<std::pair<std::string, size_t>> lines = tools::split_string_by_width(speech, 45);
size_t max_len = 0;
for (const auto &i: lines)
max_len = std::max(max_len, i.second);
const size_t n_u = max_len + 2;
tools::msg_writer() << " " << std::string(n_u, '_');
for (size_t i = 0; i < lines.size(); ++i)
tools::msg_writer() << (i == 0 ? "/" : i == lines.size() - 1 ? "\\" : "|") << " " << lines[i].first << std::string(max_len - lines[i].second, ' ') << " " << (i == 0 ? "\\" : i == lines.size() - 1 ? "/" : "|");
tools::msg_writer() << " " << std::string(n_u, '-') << std::endl <<
" \\ (__)" << std::endl <<
" \\ (oo)\\_______" << std::endl <<
" (__)\\ )\\/\\" << std::endl <<
" ||----w |" << std::endl <<
" || ||" << std::endl <<
"" << std::endl;
}
while (1)
{
tools::msg_writer() << tr("Locked due to inactivity. The wallet password is required to unlock the console.");
try
{
if (get_and_verify_password())
break;
}
catch (...) { /* do nothing, just let the loop loop */ }
}
m_last_activity_time = time(NULL);
m_in_command = false;
m_locked = false;
}
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::on_command(bool (simple_wallet::*cmd)(const std::vector<std::string>&), const std::vector<std::string> &args)
{
const time_t now = time(NULL);
time_t dt = now - m_last_activity_time;
m_last_activity_time = time(NULL);
m_in_command = true;
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){
m_last_activity_time = time(NULL);
m_in_command = false;
});
check_for_inactivity_lock(false);
return (this->*cmd)(args);
}
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::string> &args_, bool called_by_mms) bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::string> &args_, bool called_by_mms)
{ {
@ -8346,6 +8474,35 @@ void simple_wallet::wallet_idle_thread()
if (!m_idle_run.load(std::memory_order_relaxed)) if (!m_idle_run.load(std::memory_order_relaxed))
break; break;
#ifndef _WIN32
m_inactivity_checker.do_call(boost::bind(&simple_wallet::check_inactivity, this));
#endif
m_refresh_checker.do_call(boost::bind(&simple_wallet::check_refresh, this));
m_mms_checker.do_call(boost::bind(&simple_wallet::check_mms, this));
if (!m_idle_run.load(std::memory_order_relaxed))
break;
m_idle_cond.wait_for(lock, boost::chrono::seconds(1));
}
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::check_inactivity()
{
// inactivity lock
if (!m_locked && !m_in_command)
{
const uint32_t seconds = m_wallet->inactivity_lock_timeout();
if (seconds > 0 && time(NULL) - m_last_activity_time > seconds)
{
m_locked = true;
m_cmd_binder.cancel_input();
}
}
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::check_refresh()
{
// auto refresh // auto refresh
if (m_auto_refresh_enabled) if (m_auto_refresh_enabled)
{ {
@ -8360,7 +8517,11 @@ void simple_wallet::wallet_idle_thread()
catch(...) {} catch(...) {}
m_auto_refresh_refreshing = false; m_auto_refresh_refreshing = false;
} }
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::check_mms()
{
// Check for new MMS messages; // Check for new MMS messages;
// For simplicity auto message check is ALSO controlled by "m_auto_refresh_enabled" and has no // For simplicity auto message check is ALSO controlled by "m_auto_refresh_enabled" and has no
// separate thread either; thread syncing is tricky enough with only this one idle thread here // separate thread either; thread syncing is tricky enough with only this one idle thread here
@ -8368,15 +8529,13 @@ void simple_wallet::wallet_idle_thread()
{ {
check_for_messages(); check_for_messages();
} }
return true;
if (!m_idle_run.load(std::memory_order_relaxed))
break;
m_idle_cond.wait_for(lock, boost::chrono::seconds(90));
}
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
std::string simple_wallet::get_prompt() const std::string simple_wallet::get_prompt() const
{ {
if (m_locked)
return std::string("[") + tr("locked due to inactivity") + "]";
std::string addr_start = m_wallet->get_subaddress_as_str({m_current_subaddress_account, 0}).substr(0, 6); std::string addr_start = m_wallet->get_subaddress_as_str({m_current_subaddress_account, 0}).substr(0, 6);
std::string prompt = std::string("[") + tr("wallet") + " " + addr_start; std::string prompt = std::string("[") + tr("wallet") + " " + addr_start;
if (!m_wallet->check_connection(NULL)) if (!m_wallet->check_connection(NULL))
@ -9547,6 +9706,7 @@ int main(int argc, char* argv[])
std::locale::global(boost::locale::generator().generate("")); std::locale::global(boost::locale::generator().generate(""));
boost::filesystem::path::imbue(std::locale()); boost::filesystem::path::imbue(std::locale());
#endif #endif
setlocale(LC_CTYPE, "");
po::options_description desc_params(wallet_args::tr("Wallet options")); po::options_description desc_params(wallet_args::tr("Wallet options"));
tools::wallet2::init_options(desc_params); tools::wallet2::init_options(desc_params);

@ -44,6 +44,7 @@
#include "cryptonote_basic/cryptonote_basic_impl.h" #include "cryptonote_basic/cryptonote_basic_impl.h"
#include "wallet/wallet2.h" #include "wallet/wallet2.h"
#include "console_handler.h" #include "console_handler.h"
#include "math_helper.h"
#include "wipeable_string.h" #include "wipeable_string.h"
#include "common/i18n.h" #include "common/i18n.h"
#include "common/password.h" #include "common/password.h"
@ -144,6 +145,7 @@ namespace cryptonote
bool set_segregation_height(const std::vector<std::string> &args = std::vector<std::string>()); bool set_segregation_height(const std::vector<std::string> &args = std::vector<std::string>());
bool set_ignore_fractional_outputs(const std::vector<std::string> &args = std::vector<std::string>()); bool set_ignore_fractional_outputs(const std::vector<std::string> &args = std::vector<std::string>());
bool set_track_uses(const std::vector<std::string> &args = std::vector<std::string>()); bool set_track_uses(const std::vector<std::string> &args = std::vector<std::string>());
bool set_inactivity_lock_timeout(const std::vector<std::string> &args = std::vector<std::string>());
bool set_setup_background_mining(const std::vector<std::string> &args = std::vector<std::string>()); bool set_setup_background_mining(const std::vector<std::string> &args = std::vector<std::string>());
bool set_device_name(const std::vector<std::string> &args = std::vector<std::string>()); bool set_device_name(const std::vector<std::string> &args = std::vector<std::string>());
bool set_export_format(const std::vector<std::string> &args = std::vector<std::string>()); bool set_export_format(const std::vector<std::string> &args = std::vector<std::string>());
@ -245,9 +247,11 @@ namespace cryptonote
bool freeze(const std::vector<std::string>& args); bool freeze(const std::vector<std::string>& args);
bool thaw(const std::vector<std::string>& args); bool thaw(const std::vector<std::string>& args);
bool frozen(const std::vector<std::string>& args); bool frozen(const std::vector<std::string>& args);
bool lock(const std::vector<std::string>& args);
bool net_stats(const std::vector<std::string>& args); bool net_stats(const std::vector<std::string>& args);
bool welcome(const std::vector<std::string>& args); bool welcome(const std::vector<std::string>& args);
bool version(const std::vector<std::string>& args); bool version(const std::vector<std::string>& args);
bool on_unknown_command(const std::vector<std::string>& args);
bool cold_sign_tx(const std::vector<tools::wallet2::pending_tx>& ptx_vector, tools::wallet2::signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::function<bool(const tools::wallet2::signed_tx_set &)> accept_func); bool cold_sign_tx(const std::vector<tools::wallet2::pending_tx>& ptx_vector, tools::wallet2::signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::function<bool(const tools::wallet2::signed_tx_set &)> accept_func);
uint64_t get_daemon_blockchain_height(std::string& err); uint64_t get_daemon_blockchain_height(std::string& err);
@ -264,6 +268,10 @@ namespace cryptonote
std::pair<std::string, std::string> show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_height = std::numeric_limits<uint64_t>::max()) const; std::pair<std::string, std::string> show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_height = std::numeric_limits<uint64_t>::max()) const;
bool freeze_thaw(const std::vector<std::string>& args, bool freeze); bool freeze_thaw(const std::vector<std::string>& args, bool freeze);
bool prompt_if_old(const std::vector<tools::wallet2::pending_tx> &ptx_vector); bool prompt_if_old(const std::vector<tools::wallet2::pending_tx> &ptx_vector);
bool on_command(bool (simple_wallet::*cmd)(const std::vector<std::string>&), const std::vector<std::string> &args);
bool on_empty_command();
bool on_cancelled_command();
void check_for_inactivity_lock(bool user);
struct transfer_view struct transfer_view
{ {
@ -311,6 +319,11 @@ namespace cryptonote
void start_background_mining(); void start_background_mining();
void stop_background_mining(); void stop_background_mining();
// idle thread workers
bool check_inactivity();
bool check_refresh();
bool check_mms();
//----------------- i_wallet2_callback --------------------- //----------------- i_wallet2_callback ---------------------
virtual void on_new_block(uint64_t height, const cryptonote::block& block); virtual void on_new_block(uint64_t height, const cryptonote::block& block);
virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, uint64_t unlock_time); virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, uint64_t unlock_time);
@ -418,6 +431,14 @@ namespace cryptonote
uint32_t m_current_subaddress_account; uint32_t m_current_subaddress_account;
bool m_long_payment_id_support; bool m_long_payment_id_support;
std::atomic<time_t> m_last_activity_time;
std::atomic<bool> m_locked;
std::atomic<bool> m_in_command;
epee::math_helper::once_a_time_seconds<1> m_inactivity_checker;
epee::math_helper::once_a_time_seconds<90> m_refresh_checker;
epee::math_helper::once_a_time_seconds<90> m_mms_checker;
// MMS // MMS
mms::message_store& get_message_store() const { return m_wallet->get_message_store(); }; mms::message_store& get_message_store() const { return m_wallet->get_message_store(); };

@ -136,6 +136,8 @@ using namespace cryptonote;
#define DEFAULT_MIN_OUTPUT_COUNT 5 #define DEFAULT_MIN_OUTPUT_COUNT 5
#define DEFAULT_MIN_OUTPUT_VALUE (2*COIN) #define DEFAULT_MIN_OUTPUT_VALUE (2*COIN)
#define DEFAULT_INACTIVITY_LOCK_TIMEOUT 90 // a minute and a half
static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1"; static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1";
static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1"; static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1";
@ -1127,6 +1129,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_segregation_height(0), m_segregation_height(0),
m_ignore_fractional_outputs(true), m_ignore_fractional_outputs(true),
m_track_uses(false), m_track_uses(false),
m_inactivity_lock_timeout(DEFAULT_INACTIVITY_LOCK_TIMEOUT),
m_setup_background_mining(BackgroundMiningMaybe), m_setup_background_mining(BackgroundMiningMaybe),
m_is_initialized(false), m_is_initialized(false),
m_kdf_rounds(kdf_rounds), m_kdf_rounds(kdf_rounds),
@ -3650,6 +3653,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2.SetInt(m_track_uses ? 1 : 0); value2.SetInt(m_track_uses ? 1 : 0);
json.AddMember("track_uses", value2, json.GetAllocator()); json.AddMember("track_uses", value2, json.GetAllocator());
value2.SetInt(m_inactivity_lock_timeout);
json.AddMember("inactivity_lock_timeout", value2, json.GetAllocator());
value2.SetInt(m_setup_background_mining); value2.SetInt(m_setup_background_mining);
json.AddMember("setup_background_mining", value2, json.GetAllocator()); json.AddMember("setup_background_mining", value2, json.GetAllocator());
@ -3806,6 +3812,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_segregation_height = 0; m_segregation_height = 0;
m_ignore_fractional_outputs = true; m_ignore_fractional_outputs = true;
m_track_uses = false; m_track_uses = false;
m_inactivity_lock_timeout = DEFAULT_INACTIVITY_LOCK_TIMEOUT;
m_setup_background_mining = BackgroundMiningMaybe; m_setup_background_mining = BackgroundMiningMaybe;
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR; m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR; m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
@ -3962,6 +3969,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_ignore_fractional_outputs = field_ignore_fractional_outputs; m_ignore_fractional_outputs = field_ignore_fractional_outputs;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, track_uses, int, Int, false, false); GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, track_uses, int, Int, false, false);
m_track_uses = field_track_uses; m_track_uses = field_track_uses;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, inactivity_lock_timeout, uint32_t, Uint, false, DEFAULT_INACTIVITY_LOCK_TIMEOUT);
m_inactivity_lock_timeout = field_inactivity_lock_timeout;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, setup_background_mining, BackgroundMiningSetupType, Int, false, BackgroundMiningMaybe); GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, setup_background_mining, BackgroundMiningSetupType, Int, false, BackgroundMiningMaybe);
m_setup_background_mining = field_setup_background_mining; m_setup_background_mining = field_setup_background_mining;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_major, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MAJOR); GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_major, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MAJOR);

@ -1051,6 +1051,8 @@ private:
void track_uses(bool value) { m_track_uses = value; } void track_uses(bool value) { m_track_uses = value; }
BackgroundMiningSetupType setup_background_mining() const { return m_setup_background_mining; } BackgroundMiningSetupType setup_background_mining() const { return m_setup_background_mining; }
void setup_background_mining(BackgroundMiningSetupType value) { m_setup_background_mining = value; } void setup_background_mining(BackgroundMiningSetupType value) { m_setup_background_mining = value; }
uint32_t inactivity_lock_timeout() const { return m_inactivity_lock_timeout; }
void inactivity_lock_timeout(uint32_t seconds) { m_inactivity_lock_timeout = seconds; }
const std::string & device_name() const { return m_device_name; } const std::string & device_name() const { return m_device_name; }
void device_name(const std::string & device_name) { m_device_name = device_name; } void device_name(const std::string & device_name) { m_device_name = device_name; }
const std::string & device_derivation_path() const { return m_device_derivation_path; } const std::string & device_derivation_path() const { return m_device_derivation_path; }
@ -1506,6 +1508,7 @@ private:
uint64_t m_segregation_height; uint64_t m_segregation_height;
bool m_ignore_fractional_outputs; bool m_ignore_fractional_outputs;
bool m_track_uses; bool m_track_uses;
uint32_t m_inactivity_lock_timeout;
BackgroundMiningSetupType m_setup_background_mining; BackgroundMiningSetupType m_setup_background_mining;
bool m_is_initialized; bool m_is_initialized;
NodeRPCProxy m_node_rpc_proxy; NodeRPCProxy m_node_rpc_proxy;

Loading…
Cancel
Save