aboutsummaryrefslogtreecommitdiff
path: root/src/simplewallet/simplewallet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/simplewallet/simplewallet.cpp')
-rw-r--r--src/simplewallet/simplewallet.cpp870
1 files changed, 523 insertions, 347 deletions
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index c936f481e..9b5752b19 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -49,7 +49,6 @@
#include "common/dns_utils.h"
#include "common/base58.h"
#include "common/scoped_message_writer.h"
-#include "p2p/net_node.h"
#include "cryptonote_protocol/cryptonote_protocol_handler.h"
#include "simplewallet.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
@@ -111,6 +110,7 @@ namespace
const auto arg_wallet_file = wallet_args::arg_wallet_file();
const command_line::arg_descriptor<std::string> arg_generate_new_wallet = {"generate-new-wallet", sw::tr("Generate new wallet and save it to <arg>"), ""};
const command_line::arg_descriptor<std::string> arg_generate_from_view_key = {"generate-from-view-key", sw::tr("Generate incoming-only wallet from view key"), ""};
+ const command_line::arg_descriptor<std::string> arg_generate_from_spend_key = {"generate-from-spend-key", sw::tr("Generate deterministic wallet from spend key"), ""};
const command_line::arg_descriptor<std::string> arg_generate_from_keys = {"generate-from-keys", sw::tr("Generate wallet from private keys"), ""};
const command_line::arg_descriptor<std::string> arg_generate_from_multisig_keys = {"generate-from-multisig-keys", sw::tr("Generate a master wallet from multisig wallet keys"), ""};
const auto arg_generate_from_json = wallet_args::arg_generate_from_json();
@@ -125,6 +125,37 @@ namespace
const command_line::arg_descriptor< std::vector<std::string> > arg_command = {"command", ""};
+ std::string input_line(const std::string& prompt)
+ {
+#ifdef HAVE_READLINE
+ rdln::suspend_readline pause_readline;
+#endif
+ std::cout << prompt;
+
+ std::string buf;
+ std::getline(std::cin, buf);
+
+ return epee::string_tools::trim(buf);
+ }
+
+ boost::optional<tools::password_container> password_prompter(const char *prompt, bool verify)
+ {
+#ifdef HAVE_READLINE
+ rdln::suspend_readline pause_readline;
+#endif
+ auto pwd_container = tools::password_container::prompt(verify, prompt);
+ if (!pwd_container)
+ {
+ tools::fail_msg_writer() << tr("failed to read wallet password");
+ }
+ return pwd_container;
+ }
+
+ boost::optional<tools::password_container> default_password_prompter(bool verify)
+ {
+ return password_prompter(verify ? tr("Enter new wallet password") : tr("Wallet password"), verify);
+ }
+
inline std::string interpret_rpc_response(bool ok, const std::string& status)
{
std::string err;
@@ -161,20 +192,50 @@ namespace
return tools::scoped_message_writer(console_color_red, true, sw::tr("Error: "), el::Level::Error);
}
- bool is_it_true(const std::string& s)
+ bool parse_bool(const std::string& s, bool& result)
{
if (s == "1" || command_line::is_yes(s))
+ {
+ result = true;
return true;
+ }
+ if (s == "0" || command_line::is_no(s))
+ {
+ result = false;
+ return true;
+ }
boost::algorithm::is_iequal ignore_case{};
- if (boost::algorithm::equals("true", s, ignore_case))
+ if (boost::algorithm::equals("true", s, ignore_case) || boost::algorithm::equals(simple_wallet::tr("true"), s, ignore_case))
+ {
+ result = true;
return true;
- if (boost::algorithm::equals(simple_wallet::tr("true"), s, ignore_case))
+ }
+ if (boost::algorithm::equals("false", s, ignore_case) || boost::algorithm::equals(simple_wallet::tr("false"), s, ignore_case))
+ {
+ result = false;
return true;
+ }
return false;
}
+ template <typename F>
+ bool parse_bool_and_use(const std::string& s, F func)
+ {
+ bool r;
+ if (parse_bool(s, r))
+ {
+ func(r);
+ return true;
+ }
+ else
+ {
+ fail_msg_writer() << tr("invalid argument: must be either 0/1, true/false, y/n, yes/no");
+ return false;
+ }
+ }
+
const struct
{
const char *name;
@@ -240,7 +301,7 @@ namespace
<< tr("Is this OK? (Y/n) ")
;
// prompt the user for confirmation given the dns query and dnssec status
- std::string confirm_dns_ok = command_line::input_line(prompt.str());
+ std::string confirm_dns_ok = input_line(prompt.str());
if (std::cin.eof())
{
return {};
@@ -418,7 +479,7 @@ bool simple_wallet::change_password(const std::vector<std::string> &args)
}
// prompts for a new password, pass true to verify the password
- const auto pwd_container = tools::wallet2::password_prompt(true);
+ const auto pwd_container = default_password_prompter(true);
try
{
@@ -507,8 +568,10 @@ bool simple_wallet::set_always_confirm_transfers(const std::vector<std::string>
const auto pwd_container = get_and_verify_password();
if (pwd_container)
{
- m_wallet->always_confirm_transfers(is_it_true(args[1]));
- m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ parse_bool_and_use(args[1], [&](bool r) {
+ m_wallet->always_confirm_transfers(r);
+ m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ });
}
return true;
}
@@ -518,8 +581,10 @@ bool simple_wallet::set_print_ring_members(const std::vector<std::string> &args/
const auto pwd_container = get_and_verify_password();
if (pwd_container)
{
- m_wallet->print_ring_members(is_it_true(args[1]));
- m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ parse_bool_and_use(args[1], [&](bool r) {
+ m_wallet->print_ring_members(r);
+ m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ });
}
return true;
}
@@ -535,8 +600,10 @@ bool simple_wallet::set_store_tx_info(const std::vector<std::string> &args/* = s
const auto pwd_container = get_and_verify_password();
if (pwd_container)
{
- m_wallet->store_tx_info(is_it_true(args[1]));
- m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ parse_bool_and_use(args[1], [&](bool r) {
+ m_wallet->store_tx_info(r);
+ m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ });
}
return true;
}
@@ -631,14 +698,15 @@ bool simple_wallet::set_auto_refresh(const std::vector<std::string> &args/* = st
const auto pwd_container = get_and_verify_password();
if (pwd_container)
{
- const bool auto_refresh = is_it_true(args[1]);
- m_wallet->auto_refresh(auto_refresh);
- m_idle_mutex.lock();
- m_auto_refresh_enabled.store(auto_refresh, std::memory_order_relaxed);
- m_idle_cond.notify_one();
- m_idle_mutex.unlock();
+ parse_bool_and_use(args[1], [&](bool auto_refresh) {
+ m_wallet->auto_refresh(auto_refresh);
+ m_idle_mutex.lock();
+ m_auto_refresh_enabled.store(auto_refresh, std::memory_order_relaxed);
+ m_idle_cond.notify_one();
+ m_idle_mutex.unlock();
- m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ });
}
return true;
}
@@ -665,8 +733,10 @@ bool simple_wallet::set_confirm_missing_payment_id(const std::vector<std::string
const auto pwd_container = get_and_verify_password();
if (pwd_container)
{
- m_wallet->confirm_missing_payment_id(is_it_true(args[1]));
- m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ parse_bool_and_use(args[1], [&](bool r) {
+ m_wallet->confirm_missing_payment_id(r);
+ m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ });
}
return true;
}
@@ -676,8 +746,10 @@ bool simple_wallet::set_ask_password(const std::vector<std::string> &args/* = st
const auto pwd_container = get_and_verify_password();
if (pwd_container)
{
- m_wallet->ask_password(is_it_true(args[1]));
- m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ parse_bool_and_use(args[1], [&](bool r) {
+ m_wallet->ask_password(r);
+ m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ });
}
return true;
}
@@ -753,8 +825,10 @@ bool simple_wallet::set_merge_destinations(const std::vector<std::string> &args/
const auto pwd_container = get_and_verify_password();
if (pwd_container)
{
- m_wallet->merge_destinations(is_it_true(args[1]));
- m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ parse_bool_and_use(args[1], [&](bool r) {
+ m_wallet->merge_destinations(r);
+ m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ });
}
return true;
}
@@ -764,8 +838,10 @@ bool simple_wallet::set_confirm_backlog(const std::vector<std::string> &args/* =
const auto pwd_container = get_and_verify_password();
if (pwd_container)
{
- m_wallet->confirm_backlog(is_it_true(args[1]));
- m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ parse_bool_and_use(args[1], [&](bool r) {
+ m_wallet->confirm_backlog(r);
+ m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ });
}
return true;
}
@@ -834,6 +910,7 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _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), tr("sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>] - 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."));
m_cmd_binder.set_handler("sweep_below", boost::bind(&simple_wallet::sweep_below, this, _1), tr("sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>] - Send all unlocked outputs below the threshold to an address"));
+ m_cmd_binder.set_handler("sweep_single", boost::bind(&simple_wallet::sweep_single, this, _1), tr("sweep_single [<priority>] [<ring_size>] <key_image> <address> [<payment_id>] - Send a single output of the given key image to an address without change"));
m_cmd_binder.set_handler("donate", boost::bind(&simple_wallet::donate, this, _1), tr("donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id>] - Donate <amount> to the development team (donate.getmonero.org)"));
m_cmd_binder.set_handler("sign_transfer", boost::bind(&simple_wallet::sign_transfer, this, _1), tr("Sign a transaction from a file"));
m_cmd_binder.set_handler("submit_transfer", boost::bind(&simple_wallet::submit_transfer, this, _1), tr("Submit a signed transaction from a file"));
@@ -975,7 +1052,7 @@ bool simple_wallet::ask_wallet_create_if_needed()
do{
LOG_PRINT_L3("User asked to specify wallet file name.");
- wallet_path = command_line::input_line(
+ wallet_path = input_line(
tr(m_restoring ? "Specify a new wallet file name for your restored wallet (e.g., MyWallet).\n"
"Wallet file name (or Ctrl-C to quit): " :
"Specify wallet file name (e.g., MyWallet). If the wallet doesn't exist, it will be created.\n"
@@ -1026,7 +1103,7 @@ bool simple_wallet::ask_wallet_create_if_needed()
if (!m_restoring)
{
message_writer() << tr("No wallet found with that name. Confirm creation of new wallet named: ") << wallet_path;
- confirm_creation = command_line::input_line(tr("(Y/Yes/N/No): "));
+ confirm_creation = input_line(tr("(Y/Yes/N/No): "));
if(std::cin.eof())
{
LOG_ERROR("Unexpected std::cin.eof() - Exited simple_wallet::ask_wallet_create_if_needed()");
@@ -1077,12 +1154,12 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
if (!handle_command_line(vm))
return false;
- if((!m_generate_new.empty()) + (!m_wallet_file.empty()) + (!m_generate_from_view_key.empty()) + (!m_generate_from_keys.empty()) + (!m_generate_from_multisig_keys.empty()) + (!m_generate_from_json.empty()) > 1)
+ if((!m_generate_new.empty()) + (!m_wallet_file.empty()) + (!m_generate_from_view_key.empty()) + (!m_generate_from_spend_key.empty()) + (!m_generate_from_keys.empty()) + (!m_generate_from_multisig_keys.empty()) + (!m_generate_from_json.empty()) > 1)
{
- fail_msg_writer() << tr("can't specify more than one of --generate-new-wallet=\"wallet_name\", --wallet-file=\"wallet_name\", --generate-from-view-key=\"wallet_name\", --generate-from-keys=\"wallet_name\", --generate-from-multisig-keys=\"wallet_name\" and --generate-from-json=\"jsonfilename\"");
+ fail_msg_writer() << tr("can't specify more than one of --generate-new-wallet=\"wallet_name\", --wallet-file=\"wallet_name\", --generate-from-view-key=\"wallet_name\", --generate-from-spend-key=\"wallet_name\", --generate-from-keys=\"wallet_name\", --generate-from-multisig-keys=\"wallet_name\" and --generate-from-json=\"jsonfilename\"");
return false;
}
- else if (m_generate_new.empty() && m_wallet_file.empty() && m_generate_from_view_key.empty() && m_generate_from_keys.empty() && m_generate_from_multisig_keys.empty() && m_generate_from_json.empty())
+ else if (m_generate_new.empty() && m_wallet_file.empty() && m_generate_from_view_key.empty() && m_generate_from_spend_key.empty() && m_generate_from_keys.empty() && m_generate_from_multisig_keys.empty() && m_generate_from_json.empty())
{
if(!ask_wallet_create_if_needed()) return false;
}
@@ -1110,7 +1187,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
do
{
const char *prompt = m_electrum_seed.empty() ? "Specify Electrum seed: " : "Electrum seed continued: ";
- std::string electrum_seed = command_line::input_line(prompt);
+ std::string electrum_seed = input_line(prompt);
if (std::cin.eof())
return false;
if (electrum_seed.empty())
@@ -1139,7 +1216,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
m_wallet_file = m_generate_from_view_key;
// parse address
- std::string address_string = command_line::input_line("Standard address: ");
+ std::string address_string = input_line("Standard address: ");
if (std::cin.eof())
return false;
if (address_string.empty()) {
@@ -1159,7 +1236,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
}
// parse view secret key
- std::string viewkey_string = command_line::input_line("View key: ");
+ std::string viewkey_string = input_line("View key: ");
if (std::cin.eof())
return false;
if (viewkey_string.empty()) {
@@ -1190,11 +1267,30 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
bool r = new_wallet(vm, info.address, boost::none, viewkey);
CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
}
+ else if (!m_generate_from_spend_key.empty())
+ {
+ m_wallet_file = m_generate_from_spend_key;
+ // parse spend secret key
+ std::string spendkey_string = input_line("Secret spend key: ");
+ if (std::cin.eof())
+ return false;
+ if (spendkey_string.empty()) {
+ fail_msg_writer() << tr("No data supplied, cancelled");
+ return false;
+ }
+ if (!epee::string_tools::hex_to_pod(spendkey_string, m_recovery_key))
+ {
+ fail_msg_writer() << tr("failed to parse spend key secret key");
+ return false;
+ }
+ bool r = new_wallet(vm, m_recovery_key, true, false, "");
+ CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
+ }
else if (!m_generate_from_keys.empty())
{
m_wallet_file = m_generate_from_keys;
// parse address
- std::string address_string = command_line::input_line("Standard address: ");
+ std::string address_string = input_line("Standard address: ");
if (std::cin.eof())
return false;
if (address_string.empty()) {
@@ -1214,7 +1310,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
}
// parse spend secret key
- std::string spendkey_string = command_line::input_line("Secret spend key: ");
+ std::string spendkey_string = input_line("Secret spend key: ");
if (std::cin.eof())
return false;
if (spendkey_string.empty()) {
@@ -1230,7 +1326,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
crypto::secret_key spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data());
// parse view secret key
- std::string viewkey_string = command_line::input_line("Secret view key: ");
+ std::string viewkey_string = input_line("Secret view key: ");
if (std::cin.eof())
return false;
if (viewkey_string.empty()) {
@@ -1277,7 +1373,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
unsigned int multisig_n;
// parse multisig type
- std::string multisig_type_string = command_line::input_line("Multisig type (input as M/N with M <= N and M > 1): ");
+ std::string multisig_type_string = input_line("Multisig type (input as M/N with M <= N and M > 1): ");
if (std::cin.eof())
return false;
if (multisig_type_string.empty())
@@ -1303,7 +1399,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
message_writer() << boost::format(tr("Generating master wallet from %u of %u multisig wallet keys")) % multisig_m % multisig_n;
// parse multisig address
- std::string address_string = command_line::input_line("Multisig wallet address: ");
+ std::string address_string = input_line("Multisig wallet address: ");
if (std::cin.eof())
return false;
if (address_string.empty()) {
@@ -1318,7 +1414,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
}
// parse secret view key
- std::string viewkey_string = command_line::input_line("Secret view key: ");
+ std::string viewkey_string = input_line("Secret view key: ");
if (std::cin.eof())
return false;
if (viewkey_string.empty())
@@ -1358,7 +1454,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
// get N secret spend keys from user
for(unsigned int i=0; i<multisig_n; ++i)
{
- spendkey_string = command_line::input_line(tr((boost::format(tr("Secret spend key (%u of %u):")) % (i+i) % multisig_m).str().c_str()));
+ spendkey_string = input_line(tr((boost::format(tr("Secret spend key (%u of %u):")) % (i+i) % multisig_m).str().c_str()));
if (std::cin.eof())
return false;
if (spendkey_string.empty())
@@ -1406,7 +1502,15 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
else if (!m_generate_from_json.empty())
{
m_wallet_file = m_generate_from_json;
- m_wallet = tools::wallet2::make_from_json(vm, m_wallet_file);
+ try
+ {
+ m_wallet = tools::wallet2::make_from_json(vm, m_wallet_file, password_prompter);
+ }
+ catch (const std::exception &e)
+ {
+ fail_msg_writer() << e.what();
+ return false;
+ }
if (!m_wallet)
return false;
}
@@ -1428,9 +1532,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
std::string heightstr;
if (!connected || version < MAKE_CORE_RPC_VERSION(1, 6))
- heightstr = command_line::input_line("Restore from specific blockchain height (optional, default 0): ");
+ heightstr = input_line("Restore from specific blockchain height (optional, default 0): ");
else
- heightstr = command_line::input_line("Restore from specific blockchain height (optional, default 0),\nor alternatively from specific date (YYYY-MM-DD): ");
+ heightstr = input_line("Restore from specific blockchain height (optional, default 0),\nor alternatively from specific date (YYYY-MM-DD): ");
if (std::cin.eof())
return false;
if (heightstr.empty())
@@ -1466,7 +1570,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
day = boost::lexical_cast<uint16_t>(heightstr.substr(8,2));
m_restore_height = m_wallet->get_blockchain_height_by_date(year, month, day);
success_msg_writer() << tr("Restore height is: ") << m_restore_height;
- std::string confirm = command_line::input_line(tr("Is this okay? (Y/Yes/N/No): "));
+ std::string confirm = input_line(tr("Is this okay? (Y/Yes/N/No): "));
if (std::cin.eof())
return false;
if(command_line::is_yes(confirm))
@@ -1531,6 +1635,7 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_
m_wallet_file = command_line::get_arg(vm, arg_wallet_file);
m_generate_new = command_line::get_arg(vm, arg_generate_new_wallet);
m_generate_from_view_key = command_line::get_arg(vm, arg_generate_from_view_key);
+ m_generate_from_spend_key = command_line::get_arg(vm, arg_generate_from_spend_key);
m_generate_from_keys = command_line::get_arg(vm, arg_generate_from_keys);
m_generate_from_multisig_keys = command_line::get_arg(vm, arg_generate_from_multisig_keys);
m_generate_from_json = command_line::get_arg(vm, arg_generate_from_json);
@@ -1543,6 +1648,7 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_
m_restore_height = command_line::get_arg(vm, arg_restore_height);
m_do_not_relay = command_line::get_arg(vm, arg_do_not_relay);
m_restoring = !m_generate_from_view_key.empty() ||
+ !m_generate_from_spend_key.empty() ||
!m_generate_from_keys.empty() ||
!m_generate_from_multisig_keys.empty() ||
!m_generate_from_json.empty() ||
@@ -1595,7 +1701,7 @@ std::string simple_wallet::get_mnemonic_language()
}
while (language_number < 0)
{
- language_choice = command_line::input_line(tr("Enter the number corresponding to the language of your choice: "));
+ language_choice = input_line(tr("Enter the number corresponding to the language of your choice: "));
if (std::cin.eof())
return std::string();
try
@@ -1617,7 +1723,7 @@ std::string simple_wallet::get_mnemonic_language()
//----------------------------------------------------------------------------------------------------
boost::optional<tools::password_container> simple_wallet::get_and_verify_password() const
{
- auto pwd_container = tools::wallet2::password_prompt(m_wallet_file.empty());
+ auto pwd_container = default_password_prompter(m_wallet_file.empty());
if (!pwd_container)
return boost::none;
@@ -1632,7 +1738,7 @@ boost::optional<tools::password_container> simple_wallet::get_and_verify_passwor
bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
const crypto::secret_key& recovery_key, bool recover, bool two_random, const std::string &old_language)
{
- auto rc = tools::wallet2::make_new(vm);
+ auto rc = tools::wallet2::make_new(vm, password_prompter);
m_wallet = std::move(rc.first);
if (!m_wallet)
{
@@ -1713,7 +1819,7 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
const cryptonote::account_public_address& address, const boost::optional<crypto::secret_key>& spendkey,
const crypto::secret_key& viewkey)
{
- auto rc = tools::wallet2::make_new(vm);
+ auto rc = tools::wallet2::make_new(vm, password_prompter);
m_wallet = std::move(rc.first);
if (!m_wallet)
{
@@ -1755,7 +1861,7 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
std::string password;
try
{
- auto rc = tools::wallet2::make_from_file(vm, m_wallet_file);
+ auto rc = tools::wallet2::make_from_file(vm, m_wallet_file, password_prompter);
m_wallet = std::move(rc.first);
password = std::move(rc.second).password();
if (!m_wallet)
@@ -1904,8 +2010,16 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args)
bool ok = true;
size_t max_mining_threads_count = (std::max)(tools::get_max_concurrency(), static_cast<unsigned>(2));
size_t arg_size = args.size();
- if(arg_size >= 3) req.ignore_battery = is_it_true(args[2]);
- if(arg_size >= 2) req.do_background_mining = is_it_true(args[1]);
+ if(arg_size >= 3)
+ {
+ if (!parse_bool_and_use(args[2], [&](bool r) { req.ignore_battery = r; }))
+ return true;
+ }
+ if(arg_size >= 2)
+ {
+ if (!parse_bool_and_use(args[1], [&](bool r) { req.do_background_mining = r; }))
+ return true;
+ }
if(arg_size >= 1)
{
uint16_t num = 1;
@@ -1988,7 +2102,7 @@ void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid,
message_writer(console_color_green, false) << "\r" <<
tr("Height ") << height << ", " <<
tr("txid ") << txid << ", " <<
- print_money(amount) << tr(" XMR, ") <<
+ print_money(amount) <<
tr("idx ") << subaddr_index;
if (m_auto_refresh_refreshing)
m_cmd_binder.print_prompt();
@@ -2478,6 +2592,101 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
return true;
}
//----------------------------------------------------------------------------------------------------
+static void handle_transfer_exception(const std::exception_ptr &e)
+{
+ try
+ {
+ std::rethrow_exception(e);
+ }
+ catch (const tools::error::daemon_busy&)
+ {
+ fail_msg_writer() << tr("daemon is busy. Please try again later.");
+ }
+ catch (const tools::error::no_connection_to_daemon&)
+ {
+ fail_msg_writer() << tr("no connection to daemon. Please make sure daemon is running.");
+ }
+ catch (const tools::error::wallet_rpc_error& e)
+ {
+ LOG_ERROR("RPC error: " << e.to_string());
+ fail_msg_writer() << tr("RPC error: ") << e.what();
+ }
+ catch (const tools::error::get_random_outs_error &e)
+ {
+ fail_msg_writer() << tr("failed to get random outputs to mix: ") << e.what();
+ }
+ catch (const tools::error::not_enough_unlocked_money& e)
+ {
+ LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") %
+ print_money(e.available()) %
+ print_money(e.tx_amount()));
+ fail_msg_writer() << tr("Not enough money in unlocked balance");
+ }
+ catch (const tools::error::not_enough_money& e)
+ {
+ LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") %
+ print_money(e.available()) %
+ print_money(e.tx_amount()));
+ fail_msg_writer() << tr("Not enough money in unlocked balance");
+ }
+ catch (const tools::error::tx_not_possible& e)
+ {
+ LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)") %
+ print_money(e.available()) %
+ print_money(e.tx_amount() + e.fee()) %
+ print_money(e.tx_amount()) %
+ print_money(e.fee()));
+ fail_msg_writer() << tr("Failed to find a way to create transactions. This is usually due to dust which is so small it cannot pay for itself in fees, or trying to send more money than the unlocked balance, or not leaving enough for fees");
+ }
+ catch (const tools::error::not_enough_outs_to_mix& e)
+ {
+ auto writer = fail_msg_writer();
+ writer << tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":";
+ for (std::pair<uint64_t, uint64_t> outs_for_amount : e.scanty_outs())
+ {
+ writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to use") << " = " << outs_for_amount.second;
+ }
+ }
+ catch (const tools::error::tx_not_constructed&)
+ {
+ fail_msg_writer() << tr("transaction was not constructed");
+ }
+ catch (const tools::error::tx_rejected& e)
+ {
+ fail_msg_writer() << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status();
+ std::string reason = e.reason();
+ if (!reason.empty())
+ fail_msg_writer() << tr("Reason: ") << reason;
+ }
+ catch (const tools::error::tx_sum_overflow& e)
+ {
+ fail_msg_writer() << e.what();
+ }
+ catch (const tools::error::zero_destination&)
+ {
+ fail_msg_writer() << tr("one of destinations is zero");
+ }
+ catch (const tools::error::tx_too_big& e)
+ {
+ fail_msg_writer() << tr("failed to find a suitable way to split transactions");
+ }
+ catch (const tools::error::transfer_error& e)
+ {
+ LOG_ERROR("unknown transfer error: " << e.to_string());
+ fail_msg_writer() << tr("unknown transfer error: ") << e.what();
+ }
+ catch (const tools::error::wallet_internal_error& e)
+ {
+ LOG_ERROR("internal error: " << e.to_string());
+ fail_msg_writer() << tr("internal error: ") << e.what();
+ }
+ catch (const std::exception& e)
+ {
+ LOG_ERROR("unexpected error: " << e.what());
+ fail_msg_writer() << tr("unexpected error: ") << e.what();
+ }
+}
+//----------------------------------------------------------------------------------------------------
bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::string> &args_)
{
// "transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>]"
@@ -2510,12 +2719,23 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
if (fake_outs_count == 0)
fake_outs_count = DEFAULT_MIX;
}
+ else if (ring_size == 0)
+ {
+ fail_msg_writer() << tr("Ring size must not be 0");
+ return true;
+ }
else
{
fake_outs_count = ring_size - 1;
local_args.erase(local_args.begin());
}
}
+ uint64_t adjusted_fake_outs_count = m_wallet->adjust_mixin(fake_outs_count);
+ if (adjusted_fake_outs_count > fake_outs_count)
+ {
+ fail_msg_writer() << (boost::format(tr("ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
+ return true;
+ }
const size_t min_args = (transfer_type == TransferLocked) ? 3 : 2;
if(local_args.size() < min_args)
@@ -2626,7 +2846,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
// prompt is there is no payment id and confirmation is required
if (!payment_id_seen && m_wallet->confirm_missing_payment_id())
{
- std::string accepted = command_line::input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): "));
+ std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): "));
if (std::cin.eof())
return true;
if (!command_line::is_yes(accepted))
@@ -2710,7 +2930,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
std::string prompt_str = prompt.str();
if (!prompt_str.empty())
{
- std::string accepted = command_line::input_line(prompt_str);
+ std::string accepted = input_line(prompt_str);
if (std::cin.eof())
return true;
if (!command_line::is_yes(accepted))
@@ -2781,7 +3001,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
}
prompt << ENDL << tr("Is this okay? (Y/Yes/N/No): ");
- std::string accepted = command_line::input_line(prompt.str());
+ std::string accepted = input_line(prompt.str());
if (std::cin.eof())
return true;
if (!command_line::is_yes(accepted))
@@ -2810,92 +3030,9 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
commit_or_save(ptx_vector, m_do_not_relay);
}
}
- catch (const tools::error::daemon_busy&)
- {
- fail_msg_writer() << tr("daemon is busy. Please try again later.");
- }
- catch (const tools::error::no_connection_to_daemon&)
- {
- fail_msg_writer() << tr("no connection to daemon. Please make sure daemon is running.");
- }
- catch (const tools::error::wallet_rpc_error& e)
- {
- LOG_ERROR("RPC error: " << e.to_string());
- fail_msg_writer() << tr("RPC error: ") << e.what();
- }
- catch (const tools::error::get_random_outs_error &e)
- {
- fail_msg_writer() << tr("failed to get random outputs to mix: ") << e.what();
- }
- catch (const tools::error::not_enough_unlocked_money& e)
- {
- LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") %
- print_money(e.available()) %
- print_money(e.tx_amount()));
- fail_msg_writer() << tr("Not enough money in unlocked balance");
- }
- catch (const tools::error::not_enough_money& e)
- {
- LOG_PRINT_L0(boost::format("not enough money to transfer, overall balance only %s, sent amount %s") %
- print_money(e.available()) %
- print_money(e.tx_amount()));
- fail_msg_writer() << tr("Not enough money in overall balance");
- }
- catch (const tools::error::tx_not_possible& e)
- {
- LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)") %
- print_money(e.available()) %
- print_money(e.tx_amount() + e.fee()) %
- print_money(e.tx_amount()) %
- print_money(e.fee()));
- fail_msg_writer() << tr("Failed to find a way to create transactions. This is usually due to dust which is so small it cannot pay for itself in fees, or trying to send more money than the unlocked balance, or not leaving enough for fees");
- }
- catch (const tools::error::not_enough_outs_to_mix& e)
- {
- auto writer = fail_msg_writer();
- writer << tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":";
- for (std::pair<uint64_t, uint64_t> outs_for_amount : e.scanty_outs())
- {
- writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to use") << " = " << outs_for_amount.second;
- }
- }
- catch (const tools::error::tx_not_constructed&)
- {
- fail_msg_writer() << tr("transaction was not constructed");
- }
- catch (const tools::error::tx_rejected& e)
- {
- fail_msg_writer() << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status();
- std::string reason = e.reason();
- if (!reason.empty())
- fail_msg_writer() << tr("Reason: ") << reason;
- }
- catch (const tools::error::tx_sum_overflow& e)
- {
- fail_msg_writer() << e.what();
- }
- catch (const tools::error::zero_destination&)
- {
- fail_msg_writer() << tr("one of destinations is zero");
- }
- catch (const tools::error::tx_too_big& e)
- {
- fail_msg_writer() << tr("failed to find a suitable way to split transactions");
- }
- catch (const tools::error::transfer_error& e)
- {
- LOG_ERROR("unknown transfer error: " << e.to_string());
- fail_msg_writer() << tr("unknown transfer error: ") << e.what();
- }
- catch (const tools::error::wallet_internal_error& e)
+ catch (const std::exception &e)
{
- LOG_ERROR("internal error: " << e.to_string());
- fail_msg_writer() << tr("internal error: ") << e.what();
- }
- catch (const std::exception& e)
- {
- LOG_ERROR("unexpected error: " << e.what());
- fail_msg_writer() << tr("unexpected error: ") << e.what();
+ handle_transfer_exception(std::current_exception());
}
catch (...)
{
@@ -2961,7 +3098,7 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
print_money(total_unmixable) %
print_money(total_fee)).str();
}
- std::string accepted = command_line::input_line(prompt_str);
+ std::string accepted = input_line(prompt_str);
if (std::cin.eof())
return true;
if (!command_line::is_yes(accepted))
@@ -2989,92 +3126,9 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
commit_or_save(ptx_vector, m_do_not_relay);
}
}
- catch (const tools::error::daemon_busy&)
- {
- fail_msg_writer() << tr("daemon is busy. Please try again later.");
- }
- catch (const tools::error::no_connection_to_daemon&)
- {
- fail_msg_writer() << tr("no connection to daemon. Please make sure daemon is running.");
- }
- catch (const tools::error::wallet_rpc_error& e)
- {
- LOG_ERROR("RPC error: " << e.to_string());
- fail_msg_writer() << tr("RPC error: ") << e.what();
- }
- catch (const tools::error::get_random_outs_error &e)
- {
- fail_msg_writer() << tr("failed to get random outputs to mix: ") << e.what();
- }
- catch (const tools::error::not_enough_unlocked_money& e)
- {
- LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") %
- print_money(e.available()) %
- print_money(e.tx_amount()));
- fail_msg_writer() << tr("Not enough money in unlocked balance");
- }
- catch (const tools::error::not_enough_money& e)
- {
- LOG_PRINT_L0(boost::format("not enough money to transfer, overall balance only %s, sent amount %s") %
- print_money(e.available()) %
- print_money(e.tx_amount()));
- fail_msg_writer() << tr("Not enough money in overall balance");
- }
- catch (const tools::error::tx_not_possible& e)
- {
- LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)") %
- print_money(e.available()) %
- print_money(e.tx_amount() + e.fee()) %
- print_money(e.tx_amount()) %
- print_money(e.fee()));
- fail_msg_writer() << tr("Failed to find a way to create transactions. This is usually due to dust which is so small it cannot pay for itself in fees, or trying to send more money than the unlocked balance, or not leaving enough for fees");
- }
- catch (const tools::error::not_enough_outs_to_mix& e)
- {
- auto writer = fail_msg_writer();
- writer << tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":";
- for (std::pair<uint64_t, uint64_t> outs_for_amount : e.scanty_outs())
- {
- writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to use") << " = " << outs_for_amount.second;
- }
- }
- catch (const tools::error::tx_not_constructed&)
- {
- fail_msg_writer() << tr("transaction was not constructed");
- }
- catch (const tools::error::tx_rejected& e)
- {
- fail_msg_writer() << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status();
- std::string reason = e.reason();
- if (!reason.empty())
- fail_msg_writer() << tr("Reason: ") << reason;
- }
- catch (const tools::error::tx_sum_overflow& e)
- {
- fail_msg_writer() << e.what();
- }
- catch (const tools::error::zero_destination&)
- {
- fail_msg_writer() << tr("one of destinations is zero");
- }
- catch (const tools::error::tx_too_big& e)
- {
- fail_msg_writer() << tr("failed to find a suitable way to split transactions");
- }
- catch (const tools::error::transfer_error& e)
- {
- LOG_ERROR("unknown transfer error: " << e.to_string());
- fail_msg_writer() << tr("unknown transfer error: ") << e.what();
- }
- catch (const tools::error::wallet_internal_error& e)
- {
- LOG_ERROR("internal error: " << e.to_string());
- fail_msg_writer() << tr("internal error: ") << e.what();
- }
- catch (const std::exception& e)
+ catch (const std::exception &e)
{
- LOG_ERROR("unexpected error: " << e.what());
- fail_msg_writer() << tr("unexpected error: ") << e.what();
+ handle_transfer_exception(std::current_exception());
}
catch (...)
{
@@ -3088,6 +3142,12 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &args_)
{
// sweep_all [index=<N1>[,<N2>,...]] [<ring_size>] <address> [<payment_id>]
+ if (args_.size() == 0)
+ {
+ fail_msg_writer() << tr("No address given");
+ return true;
+ }
+
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
if (!try_connect_to_daemon())
return true;
@@ -3115,12 +3175,23 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a
if (fake_outs_count == 0)
fake_outs_count = DEFAULT_MIX;
}
+ else if (ring_size == 0)
+ {
+ fail_msg_writer() << tr("Ring size must not be 0");
+ return true;
+ }
else
{
fake_outs_count = ring_size - 1;
local_args.erase(local_args.begin());
}
}
+ uint64_t adjusted_fake_outs_count = m_wallet->adjust_mixin(fake_outs_count);
+ if (adjusted_fake_outs_count > fake_outs_count)
+ {
+ fail_msg_writer() << (boost::format(tr("ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
+ return true;
+ }
std::vector<uint8_t> extra;
bool payment_id_seen = false;
@@ -3159,12 +3230,6 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a
local_args.pop_back();
}
- if (local_args.size() == 0)
- {
- fail_msg_writer() << tr("No address given");
- return true;
- }
-
cryptonote::address_parse_info info;
if (!cryptonote::get_account_address_from_str_or_url(info, m_wallet->testnet(), local_args[0], oa_prompter))
{
@@ -3194,7 +3259,7 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a
// prompt is there is no payment id and confirmation is required
if (!payment_id_seen && m_wallet->confirm_missing_payment_id())
{
- std::string accepted = command_line::input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): "));
+ std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): "));
if (std::cin.eof())
return true;
if (!command_line::is_yes(accepted))
@@ -3252,7 +3317,7 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a
print_money(total_sent) %
print_money(total_fee);
}
- std::string accepted = command_line::input_line(prompt.str());
+ std::string accepted = input_line(prompt.str());
if (std::cin.eof())
return true;
if (!command_line::is_yes(accepted))
@@ -3280,6 +3345,199 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a
commit_or_save(ptx_vector, m_do_not_relay);
}
}
+ catch (const std::exception& e)
+ {
+ handle_transfer_exception(std::current_exception());
+ }
+ catch (...)
+ {
+ LOG_ERROR("unknown error");
+ fail_msg_writer() << tr("unknown error");
+ }
+
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
+bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
+{
+ if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
+ if (!try_connect_to_daemon())
+ return true;
+
+ std::vector<std::string> local_args = args_;
+
+ int priority = 0;
+ if(local_args.size() > 0) {
+ auto priority_pos = std::find(
+ allowed_priority_strings.begin(),
+ allowed_priority_strings.end(),
+ local_args[0]);
+ if(priority_pos != allowed_priority_strings.end()) {
+ local_args.erase(local_args.begin());
+ priority = std::distance(allowed_priority_strings.begin(), priority_pos);
+ }
+ }
+
+ size_t fake_outs_count = 0;
+ if(local_args.size() > 0) {
+ size_t ring_size;
+ if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0]))
+ {
+ fake_outs_count = m_wallet->default_mixin();
+ if (fake_outs_count == 0)
+ fake_outs_count = DEFAULT_MIX;
+ }
+ else
+ {
+ fake_outs_count = ring_size - 1;
+ local_args.erase(local_args.begin());
+ }
+ }
+
+ std::vector<uint8_t> extra;
+ bool payment_id_seen = false;
+ if (local_args.size() == 3)
+ {
+ crypto::hash payment_id;
+ crypto::hash8 payment_id8;
+ std::string extra_nonce;
+ if (tools::wallet2::parse_long_payment_id(local_args.back(), payment_id))
+ {
+ set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
+ }
+ else if(tools::wallet2::parse_short_payment_id(local_args.back(), payment_id8))
+ {
+ set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id8);
+ }
+ else
+ {
+ fail_msg_writer() << tr("failed to parse Payment ID");
+ return true;
+ }
+
+ if (!add_extra_nonce_to_tx_extra(extra, extra_nonce))
+ {
+ fail_msg_writer() << tr("failed to set up payment id, though it was decoded correctly");
+ return true;
+ }
+
+ local_args.pop_back();
+ payment_id_seen = true;
+ }
+
+ if (local_args.size() != 2)
+ {
+ fail_msg_writer() << tr("usage: sweep_single [<priority>] [<ring_size>] <key_image> <address> [<payment_id>]");
+ return true;
+ }
+
+ crypto::key_image ki;
+ if (!epee::string_tools::hex_to_pod(local_args[0], ki))
+ {
+ fail_msg_writer() << tr("failed to parse key image");
+ return true;
+ }
+
+ cryptonote::address_parse_info info;
+ if (!cryptonote::get_account_address_from_str_or_url(info, m_wallet->testnet(), local_args[1], oa_prompter))
+ {
+ fail_msg_writer() << tr("failed to parse address");
+ return true;
+ }
+
+ if (info.has_payment_id)
+ {
+ if (payment_id_seen)
+ {
+ fail_msg_writer() << tr("a single transaction cannot use more than one payment id: ") << local_args[0];
+ return true;
+ }
+
+ std::string extra_nonce;
+ set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, info.payment_id);
+ if (!add_extra_nonce_to_tx_extra(extra, extra_nonce))
+ {
+ fail_msg_writer() << tr("failed to set up payment id, though it was decoded correctly");
+ return true;
+ }
+ payment_id_seen = true;
+ }
+
+ // prompt if there is no payment id and confirmation is required
+ if (!payment_id_seen && m_wallet->confirm_missing_payment_id())
+ {
+ std::string accepted = command_line::input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): "));
+ if (std::cin.eof())
+ return true;
+ if (!command_line::is_yes(accepted))
+ {
+ fail_msg_writer() << tr("transaction cancelled.");
+
+ // would like to return false, because no tx made, but everything else returns true
+ // and I don't know what returning false might adversely affect. *sigh*
+ return true;
+ }
+ }
+
+ try
+ {
+ // figure out what tx will be necessary
+ auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, fake_outs_count, 0 /* unlock_time */, priority, extra, m_trusted_daemon);
+
+ if (ptx_vector.empty())
+ {
+ fail_msg_writer() << tr("No outputs found");
+ return true;
+ }
+ if (ptx_vector.size() > 1)
+ {
+ fail_msg_writer() << tr("Multiple transactions are created, which is not supposed to happen");
+ return true;
+ }
+ if (ptx_vector[0].selected_transfers.size() > 1)
+ {
+ fail_msg_writer() << tr("The transaction uses multiple inputs, which is not supposed to happen");
+ return true;
+ }
+
+ // give user total and fee, and prompt to confirm
+ uint64_t total_fee = ptx_vector[0].fee;
+ uint64_t total_sent = m_wallet->get_transfer_details(ptx_vector[0].selected_transfers.front()).amount();
+ std::ostringstream prompt;
+ if (!print_ring_members(ptx_vector, prompt))
+ return true;
+ prompt << boost::format(tr("Sweeping %s for a total fee of %s. Is this okay? (Y/Yes/N/No)")) %
+ print_money(total_sent) %
+ print_money(total_fee);
+ std::string accepted = command_line::input_line(prompt.str());
+ if (std::cin.eof())
+ return true;
+ if (!command_line::is_yes(accepted))
+ {
+ fail_msg_writer() << tr("transaction cancelled.");
+ return true;
+ }
+
+ // actually commit the transactions
+ if (m_wallet->watch_only())
+ {
+ bool r = m_wallet->save_tx(ptx_vector, "unsigned_monero_tx");
+ if (!r)
+ {
+ fail_msg_writer() << tr("Failed to write transaction(s) to file");
+ }
+ else
+ {
+ success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_monero_tx";
+ }
+ }
+ else
+ {
+ m_wallet->commit_tx(ptx_vector[0]);
+ success_msg_writer(true) << tr("Money successfully sent, transaction: ") << get_transaction_hash(ptx_vector[0].tx);
+ }
+
+ }
catch (const tools::error::daemon_busy&)
{
fail_msg_writer() << tr("daemon is busy. Please try again later.");
@@ -3297,20 +3555,13 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a
{
fail_msg_writer() << tr("failed to get random outputs to mix: ") << e.what();
}
- catch (const tools::error::not_enough_unlocked_money& e)
+ catch (const tools::error::not_enough_money& e)
{
LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") %
print_money(e.available()) %
print_money(e.tx_amount()));
fail_msg_writer() << tr("Not enough money in unlocked balance");
}
- catch (const tools::error::not_enough_money& e)
- {
- LOG_PRINT_L0(boost::format("not enough money to transfer, overall balance only %s, sent amount %s") %
- print_money(e.available()) %
- print_money(e.tx_amount()));
- fail_msg_writer() << tr("Not enough money in overall balance");
- }
catch (const tools::error::tx_not_possible& e)
{
LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)") %
@@ -3323,10 +3574,10 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a
catch (const tools::error::not_enough_outs_to_mix& e)
{
auto writer = fail_msg_writer();
- writer << tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":";
+ writer << tr("not enough outputs for specified mixin_count") << " = " << e.mixin_count() << ":";
for (std::pair<uint64_t, uint64_t> outs_for_amount : e.scanty_outs())
{
- writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to use") << " = " << outs_for_amount.second;
+ writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to mix") << " = " << outs_for_amount.second;
}
}
catch (const tools::error::tx_not_constructed&)
@@ -3426,7 +3677,7 @@ bool simple_wallet::donate(const std::vector<std::string> &args_)
local_args.push_back(amount_str);
if (!payment_id_str.empty())
local_args.push_back(payment_id_str);
- message_writer() << tr("Donating ") << amount_str << " XMR to The Monero Project (donate.getmonero.org/44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A).";
+ message_writer() << tr("Donating ") << amount_str << " to The Monero Project (donate.getmonero.org/44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A).";
transfer_new(local_args);
return true;
}
@@ -3548,7 +3799,7 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes,
uint64_t fee = amount - amount_to_dests;
std::string prompt_str = (boost::format(tr("Loaded %lu transactions, for %s, fee %s, %s, %s, with min ring size %lu, %s. %sIs this okay? (Y/Yes/N/No): ")) % (unsigned long)get_num_txes() % print_money(amount) % print_money(fee) % dest_string % change_string % (unsigned long)min_ring_size % payment_id_string % extra_message).str();
- return command_line::is_yes(command_line::input_line(prompt_str));
+ return command_line::is_yes(input_line(prompt_str));
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs)
@@ -3637,92 +3888,9 @@ bool simple_wallet::submit_transfer(const std::vector<std::string> &args_)
commit_or_save(ptx_vector, false);
}
- catch (const tools::error::daemon_busy&)
- {
- fail_msg_writer() << tr("daemon is busy. Please try later");
- }
- catch (const tools::error::no_connection_to_daemon&)
- {
- fail_msg_writer() << tr("no connection to daemon. Please, make sure daemon is running.");
- }
- catch (const tools::error::wallet_rpc_error& e)
- {
- LOG_ERROR("Unknown RPC error: " << e.to_string());
- fail_msg_writer() << tr("RPC error: ") << e.what();
- }
- catch (const tools::error::get_random_outs_error &e)
- {
- fail_msg_writer() << tr("failed to get random outputs to mix: ") << e.what();
- }
- catch (const tools::error::not_enough_unlocked_money& e)
- {
- LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") %
- print_money(e.available()) %
- print_money(e.tx_amount()));
- fail_msg_writer() << tr("Not enough money in unlocked balance");
- }
- catch (const tools::error::not_enough_money& e)
- {
- LOG_PRINT_L0(boost::format("not enough money to transfer, overall balance only %s, sent amount %s") %
- print_money(e.available()) %
- print_money(e.tx_amount()));
- fail_msg_writer() << tr("Not enough money in overall balance");
- }
- catch (const tools::error::tx_not_possible& e)
- {
- LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)") %
- print_money(e.available()) %
- print_money(e.tx_amount() + e.fee()) %
- print_money(e.tx_amount()) %
- print_money(e.fee()));
- fail_msg_writer() << tr("Failed to find a way to create transactions. This is usually due to dust which is so small it cannot pay for itself in fees, or trying to send more money than the unlocked balance, or not leaving enough for fees");
- }
- catch (const tools::error::not_enough_outs_to_mix& e)
- {
- auto writer = fail_msg_writer();
- writer << tr("not enough outputs for specified ring size") << " = " << (e.mixin_count() + 1) << ":";
- for (std::pair<uint64_t, uint64_t> outs_for_amount : e.scanty_outs())
- {
- writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to use") << " = " << outs_for_amount.second;
- }
- }
- catch (const tools::error::tx_not_constructed&)
- {
- fail_msg_writer() << tr("transaction was not constructed");
- }
- catch (const tools::error::tx_rejected& e)
- {
- fail_msg_writer() << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status();
- std::string reason = e.reason();
- if (!reason.empty())
- fail_msg_writer() << tr("Reason: ") << reason;
- }
- catch (const tools::error::tx_sum_overflow& e)
- {
- fail_msg_writer() << e.what();
- }
- catch (const tools::error::zero_destination&)
- {
- fail_msg_writer() << tr("one of destinations is zero");
- }
- catch (const tools::error::tx_too_big& e)
- {
- fail_msg_writer() << tr("Failed to find a suitable way to split transactions");
- }
- catch (const tools::error::transfer_error& e)
- {
- LOG_ERROR("unknown transfer error: " << e.to_string());
- fail_msg_writer() << tr("unknown transfer error: ") << e.what();
- }
- catch (const tools::error::wallet_internal_error& e)
- {
- LOG_ERROR("internal error: " << e.to_string());
- fail_msg_writer() << tr("internal error: ") << e.what();
- }
catch (const std::exception& e)
{
- LOG_ERROR("unexpected error: " << e.what());
- fail_msg_writer() << tr("unexpected error: ") << e.what();
+ handle_transfer_exception(std::current_exception());
}
catch (...)
{
@@ -3854,7 +4022,7 @@ bool simple_wallet::get_tx_proof(const std::vector<std::string> &args)
// fetch tx pubkey
crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx);
- if (tx_pub_key == null_pkey)
+ if (tx_pub_key == crypto::null_pkey)
{
fail_msg_writer() << tr("Tx pubkey was not found");
return true;
@@ -4191,7 +4359,7 @@ bool simple_wallet::check_tx_proof(const std::vector<std::string> &args)
return true;
}
crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx);
- if (tx_pub_key == null_pkey)
+ if (tx_pub_key == crypto::null_pkey)
{
fail_msg_writer() << tr("Tx pubkey was not found");
return true;
@@ -4415,15 +4583,18 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
try
{
m_wallet->update_pool_state();
- std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
+ std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments;
m_wallet->get_unconfirmed_payments(payments, m_current_subaddress_account, subaddr_indices);
- for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
- const tools::wallet2::payment_details &pd = i->second;
+ for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
+ const tools::wallet2::payment_details &pd = i->second.m_pd;
std::string payment_id = string_tools::pod_to_hex(i->first);
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
payment_id = payment_id.substr(0,16);
std::string note = m_wallet->get_tx_note(pd.m_tx_hash);
- message_writer() << (boost::format("%8.8s %6.6s %16.16s %20.20s %s %s %d %s %s") % "pool" % "in" % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note).str();
+ std::string double_spend_note;
+ if (i->second.m_double_spend_seen)
+ double_spend_note = tr("[Double spend seen on the network: this transaction may or may not end up being mined] ");
+ message_writer() << (boost::format("%8.8s %6.6s %16.16s %20.20s %s %s %d %s %s%s") % "pool" % "in" % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note % double_spend_note).str();
}
}
catch (const std::exception& e)
@@ -4743,10 +4914,11 @@ bool simple_wallet::account(const std::vector<std::string> &args/* = std::vector
//----------------------------------------------------------------------------------------------------
void simple_wallet::print_accounts()
{
- success_msg_writer() << boost::format("%15s %21s %21s %21s") % tr("Account") % tr("Balance") % tr("Unlocked balance") % tr("Label");
+ success_msg_writer() << boost::format(" %15s %21s %21s %21s") % tr("Account") % tr("Balance") % tr("Unlocked balance") % tr("Label");
for (uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index)
{
- success_msg_writer() << boost::format(tr("%8u %6s %21s %21s %21s"))
+ success_msg_writer() << boost::format(tr(" %c%8u %6s %21s %21s %21s"))
+ % (m_current_subaddress_account == account_index ? '*' : ' ')
% account_index
% m_wallet->get_subaddress_as_str({account_index, 0}).substr(0, 6)
% print_money(m_wallet->balance(account_index))
@@ -4915,7 +5087,7 @@ bool simple_wallet::address_book(const std::vector<std::string> &args/* = std::v
fail_msg_writer() << tr("failed to parse address");
return true;
}
- crypto::hash payment_id = null_hash;
+ crypto::hash payment_id = crypto::null_hash;
size_t description_start = 2;
if (info.has_payment_id)
{
@@ -5369,7 +5541,7 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args)
const uint64_t last_block_height = m_wallet->get_blockchain_current_height();
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
- m_wallet->get_payments(payments, 0, m_current_subaddress_account);
+ m_wallet->get_payments(payments, 0, (uint64_t)-1, m_current_subaddress_account);
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
const tools::wallet2::payment_details &pd = i->second;
if (pd.m_tx_hash == txid) {
@@ -5439,10 +5611,10 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args)
try
{
m_wallet->update_pool_state();
- std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> pool_payments;
- m_wallet->get_unconfirmed_payments(pool_payments);
- for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
- const tools::wallet2::payment_details &pd = i->second;
+ std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> pool_payments;
+ m_wallet->get_unconfirmed_payments(pool_payments, m_current_subaddress_account);
+ for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
+ const tools::wallet2::payment_details &pd = i->second.m_pd;
if (pd.m_tx_hash == txid)
{
std::string payment_id = string_tools::pod_to_hex(i->first);
@@ -5455,6 +5627,8 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args)
success_msg_writer() << "Payment ID: " << payment_id;
success_msg_writer() << "Address index: " << pd.m_subaddr_index.minor;
success_msg_writer() << "Note: " << m_wallet->get_tx_note(txid);
+ if (i->second.m_double_spend_seen)
+ success_msg_writer() << tr("Double spend seen on the network: this transaction may or may not end up being mined");
return true;
}
}
@@ -5465,7 +5639,7 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args)
}
std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
- m_wallet->get_unconfirmed_payments_out(upayments);
+ m_wallet->get_unconfirmed_payments_out(upayments, m_current_subaddress_account);
for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
if (i->first == txid)
{
@@ -5546,6 +5720,7 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_wallet_file);
command_line::add_arg(desc_params, arg_generate_new_wallet);
command_line::add_arg(desc_params, arg_generate_from_view_key);
+ command_line::add_arg(desc_params, arg_generate_from_spend_key);
command_line::add_arg(desc_params, arg_generate_from_keys);
command_line::add_arg(desc_params, arg_generate_from_multisig_keys);
command_line::add_arg(desc_params, arg_generate_from_json);
@@ -5568,6 +5743,7 @@ int main(int argc, char* argv[])
"monero-wallet-cli [--wallet-file=<file>|--generate-new-wallet=<file>] [<COMMAND>]",
desc_params,
positional_options,
+ [](const std::string &s, bool emphasis){ tools::scoped_message_writer(emphasis ? epee::console_color_white : epee::console_color_default, true) << s; },
"monero-wallet-cli.log"
);