aboutsummaryrefslogtreecommitdiff
path: root/src/simplewallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/simplewallet')
-rw-r--r--src/simplewallet/CMakeLists.txt2
-rw-r--r--src/simplewallet/simplewallet.cpp302
-rw-r--r--src/simplewallet/simplewallet.h3
3 files changed, 174 insertions, 133 deletions
diff --git a/src/simplewallet/CMakeLists.txt b/src/simplewallet/CMakeLists.txt
index beaacf0e9..f190ada8d 100644
--- a/src/simplewallet/CMakeLists.txt
+++ b/src/simplewallet/CMakeLists.txt
@@ -55,8 +55,8 @@ target_link_libraries(simplewallet
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
- ${Readline_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
+ ${GNU_READLINE_LIBRARY}
${EXTRA_LIBRARIES})
set_property(TARGET simplewallet
PROPERTY
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index f79ed9b5b..cca2b3970 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -121,6 +121,7 @@ namespace
const command_line::arg_descriptor<std::string> arg_mnemonic_language = {"mnemonic-language", sw::tr("Language for mnemonic"), ""};
const command_line::arg_descriptor<std::string> arg_electrum_seed = {"electrum-seed", sw::tr("Specify Electrum seed for wallet recovery/creation"), ""};
const command_line::arg_descriptor<bool> arg_restore_deterministic_wallet = {"restore-deterministic-wallet", sw::tr("Recover wallet using Electrum-style mnemonic seed"), false};
+ const command_line::arg_descriptor<bool> arg_restore_multisig_wallet = {"restore-multisig-wallet", sw::tr("Recover multisig wallet using Electrum-style mnemonic seed"), false};
const command_line::arg_descriptor<bool> arg_non_deterministic = {"non-deterministic", sw::tr("Create non-deterministic view and spend keys"), false};
const command_line::arg_descriptor<bool> arg_trusted_daemon = {"trusted-daemon", sw::tr("Enable commands which rely on a trusted daemon"), false};
const command_line::arg_descriptor<bool> arg_allow_mismatched_daemon_version = {"allow-mismatched-daemon-version", sw::tr("Allow communicating with a daemon that uses a different RPC version"), false};
@@ -516,48 +517,55 @@ bool simple_wallet::spendkey(const std::vector<std::string> &args/* = std::vecto
bool simple_wallet::print_seed(bool encrypted)
{
bool success = false;
- std::string electrum_words;
+ std::string seed;
+ bool ready, multisig;
- if (m_wallet->multisig())
- {
- fail_msg_writer() << tr("wallet is multisig and has no seed");
- return true;
- }
if (m_wallet->watch_only())
{
fail_msg_writer() << tr("wallet is watch-only and has no seed");
return true;
}
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
- if (m_wallet->is_deterministic())
- {
- if (m_wallet->get_seed_language().empty())
- {
- std::string mnemonic_language = get_mnemonic_language();
- if (mnemonic_language.empty())
- return true;
- m_wallet->set_seed_language(mnemonic_language);
- }
- epee::wipeable_string seed_pass;
- if (encrypted)
+ multisig = m_wallet->multisig(&ready);
+ if (multisig)
+ {
+ if (!ready)
{
- auto pwd_container = tools::password_container::prompt(true, tr("Enter optional seed encryption passphrase, empty to see raw seed"));
- if (std::cin.eof() || !pwd_container)
- return true;
- seed_pass = pwd_container->password();
+ fail_msg_writer() << tr("wallet is multisig but not yet finalized");
+ return true;
}
+ }
+ else if (!m_wallet->is_deterministic())
+ {
+ fail_msg_writer() << tr("wallet is non-deterministic and has no seed");
+ return true;
+ }
- success = m_wallet->get_seed(electrum_words, seed_pass);
+ epee::wipeable_string seed_pass;
+ if (encrypted)
+ {
+#ifdef HAVE_READLINE
+ rdln::suspend_readline pause_readline;
+#endif
+ auto pwd_container = tools::password_container::prompt(true, tr("Enter optional seed encryption passphrase, empty to see raw seed"));
+ if (std::cin.eof() || !pwd_container)
+ return true;
+ seed_pass = pwd_container->password();
}
+ if (multisig)
+ success = m_wallet->get_multisig_seed(seed, seed_pass);
+ else if (m_wallet->is_deterministic())
+ success = m_wallet->get_seed(seed, seed_pass);
+
if (success)
{
- print_seed(electrum_words);
+ print_seed(seed);
}
else
{
- fail_msg_writer() << tr("wallet is non-deterministic and has no seed");
+ fail_msg_writer() << tr("Failed to retrieve seed");
}
return true;
}
@@ -1984,6 +1992,8 @@ static bool might_be_partial_seed(std::string words)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
+ std::string multisig_keys;
+
if (!handle_command_line(vm))
return false;
@@ -2001,49 +2011,91 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
std::string old_language;
// check for recover flag. if present, require electrum word list (only recovery option for now).
- if (m_restore_deterministic_wallet)
+ if (m_restore_deterministic_wallet || m_restore_multisig_wallet)
{
if (m_non_deterministic)
{
- fail_msg_writer() << tr("can't specify both --restore-deterministic-wallet and --non-deterministic");
+ fail_msg_writer() << tr("can't specify both --restore-deterministic-wallet or --restore-multisig-wallet and --non-deterministic");
return false;
}
if (!m_wallet_file.empty())
{
- fail_msg_writer() << tr("--restore-deterministic-wallet uses --generate-new-wallet, not --wallet-file");
+ if (m_restore_multisig_wallet)
+ fail_msg_writer() << tr("--restore-multisig-wallet uses --generate-new-wallet, not --wallet-file");
+ else
+ fail_msg_writer() << tr("--restore-deterministic-wallet uses --generate-new-wallet, not --wallet-file");
return false;
}
if (m_electrum_seed.empty())
{
- m_electrum_seed = "";
- do
+ if (m_restore_multisig_wallet)
{
- const char *prompt = m_electrum_seed.empty() ? "Specify Electrum seed: " : "Electrum seed continued: ";
- std::string electrum_seed = input_line(prompt);
- if (std::cin.eof())
- return false;
- if (electrum_seed.empty())
+ const char *prompt = "Specify multisig seed: ";
+ m_electrum_seed = input_line(prompt);
+ if (std::cin.eof())
+ return false;
+ if (m_electrum_seed.empty())
+ {
+ fail_msg_writer() << tr("specify a recovery parameter with the --electrum-seed=\"multisig seed here\"");
+ return false;
+ }
+ }
+ else
+ {
+ m_electrum_seed = "";
+ do
{
- fail_msg_writer() << tr("specify a recovery parameter with the --electrum-seed=\"words list here\"");
- return false;
- }
- m_electrum_seed += electrum_seed + " ";
- } while (might_be_partial_seed(m_electrum_seed));
+ const char *prompt = m_electrum_seed.empty() ? "Specify Electrum seed: " : "Electrum seed continued: ";
+ std::string electrum_seed = input_line(prompt);
+ if (std::cin.eof())
+ return false;
+ if (electrum_seed.empty())
+ {
+ fail_msg_writer() << tr("specify a recovery parameter with the --electrum-seed=\"words list here\"");
+ return false;
+ }
+ m_electrum_seed += electrum_seed + " ";
+ } while (might_be_partial_seed(m_electrum_seed));
+ }
}
- if (!crypto::ElectrumWords::words_to_bytes(m_electrum_seed, m_recovery_key, old_language))
+ if (m_restore_multisig_wallet)
{
- fail_msg_writer() << tr("Electrum-style word list failed verification");
- return false;
+ if (!epee::string_tools::parse_hexstr_to_binbuff(m_electrum_seed, multisig_keys))
+ {
+ fail_msg_writer() << tr("Multisig seed failed verification");
+ return false;
+ }
+ }
+ else
+ {
+ if (!crypto::ElectrumWords::words_to_bytes(m_electrum_seed, m_recovery_key, old_language))
+ {
+ fail_msg_writer() << tr("Electrum-style word list failed verification");
+ return false;
+ }
}
+#ifdef HAVE_READLINE
+ rdln::suspend_readline pause_readline;
+#endif
auto pwd_container = tools::password_container::prompt(false, tr("Enter seed encryption passphrase, empty if none"));
if (std::cin.eof() || !pwd_container)
return false;
epee::wipeable_string seed_pass = pwd_container->password();
if (!seed_pass.empty())
- m_recovery_key = cryptonote::decrypt_key(m_recovery_key, seed_pass);
+ {
+ if (m_restore_multisig_wallet)
+ {
+ crypto::secret_key key;
+ crypto::cn_slow_hash(seed_pass.data(), seed_pass.size(), (crypto::hash&)key);
+ sc_reduce32((unsigned char*)key.data);
+ multisig_keys = m_wallet->decrypt(multisig_keys, key, true);
+ }
+ else
+ m_recovery_key = cryptonote::decrypt_key(m_recovery_key, seed_pass);
+ }
}
if (!m_generate_from_view_key.empty())
{
@@ -2354,7 +2406,11 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
return false;
}
m_wallet_file = m_generate_new;
- bool r = new_wallet(vm, m_recovery_key, m_restore_deterministic_wallet, m_non_deterministic, old_language);
+ bool r;
+ if (m_restore_multisig_wallet)
+ r = new_wallet(vm, multisig_keys, old_language);
+ else
+ r = new_wallet(vm, m_recovery_key, m_restore_deterministic_wallet, m_non_deterministic, old_language);
CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
}
if (!m_restore_height && m_restoring)
@@ -2485,6 +2541,7 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_
m_mnemonic_language = command_line::get_arg(vm, arg_mnemonic_language);
m_electrum_seed = command_line::get_arg(vm, arg_electrum_seed);
m_restore_deterministic_wallet = command_line::get_arg(vm, arg_restore_deterministic_wallet);
+ m_restore_multisig_wallet = command_line::get_arg(vm, arg_restore_multisig_wallet);
m_non_deterministic = command_line::get_arg(vm, arg_non_deterministic);
m_trusted_daemon = command_line::get_arg(vm, arg_trusted_daemon);
m_allow_mismatched_daemon_version = command_line::get_arg(vm, arg_allow_mismatched_daemon_version);
@@ -2495,7 +2552,8 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_
!m_generate_from_keys.empty() ||
!m_generate_from_multisig_keys.empty() ||
!m_generate_from_json.empty() ||
- m_restore_deterministic_wallet;
+ m_restore_deterministic_wallet ||
+ m_restore_multisig_wallet;
return true;
}
@@ -2642,11 +2700,11 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
success_msg_writer() <<
"**********************************************************************\n" <<
tr("Your wallet has been generated!\n"
- "To start synchronizing with the daemon, use \"refresh\" command.\n"
- "Use \"help\" command to see the list of available commands.\n"
+ "To start synchronizing with the daemon, use the \"refresh\" command.\n"
+ "Use the \"help\" command to see the list of available commands.\n"
"Use \"help <command>\" to see a command's documentation.\n"
- "Always use \"exit\" command when closing monero-wallet-cli to save your\n"
- "current session's state. Otherwise, you might need to synchronize \n"
+ "Always use the \"exit\" command when closing monero-wallet-cli to save \n"
+ "your current session's state. Otherwise, you might need to synchronize \n"
"your wallet again (your wallet keys are NOT at risk in any case).\n")
;
@@ -2695,6 +2753,49 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
return true;
}
//----------------------------------------------------------------------------------------------------
+bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
+ const std::string &multisig_keys, const std::string &old_language)
+{
+ auto rc = tools::wallet2::make_new(vm, password_prompter);
+ m_wallet = std::move(rc.first);
+ if (!m_wallet)
+ {
+ return false;
+ }
+
+ std::string mnemonic_language = old_language;
+
+ std::vector<std::string> language_list;
+ crypto::ElectrumWords::get_language_list(language_list);
+ if (mnemonic_language.empty() && std::find(language_list.begin(), language_list.end(), m_mnemonic_language) != language_list.end())
+ {
+ mnemonic_language = m_mnemonic_language;
+ }
+
+ m_wallet->set_seed_language(mnemonic_language);
+
+ try
+ {
+ m_wallet->generate(m_wallet_file, std::move(rc.second).password(), multisig_keys);
+ bool ready;
+ uint32_t threshold, total;
+ if (!m_wallet->multisig(&ready, &threshold, &total) || !ready)
+ {
+ fail_msg_writer() << tr("failed to generate new mutlisig wallet");
+ return false;
+ }
+ message_writer(console_color_white, true) << boost::format(tr("Generated new %u/%u multisig wallet: ")) % threshold % total
+ << m_wallet->get_account().get_public_address_str(m_wallet->testnet());
+ }
+ catch (const std::exception& e)
+ {
+ fail_msg_writer() << tr("failed to generate new wallet: ") << e.what();
+ return false;
+ }
+
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
{
if (!tools::wallet2::wallet_valid_path_format(m_wallet_file))
@@ -2771,7 +2872,7 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
}
success_msg_writer() <<
"**********************************************************************\n" <<
- tr("Use \"help\" command to see the list of available commands.\n") <<
+ tr("Use the \"help\" command to see the list of available commands.\n") <<
tr("Use \"help <command>\" to see a command's documentation.\n") <<
"**********************************************************************";
return true;
@@ -3206,6 +3307,13 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
{
if (!parse_subaddress_indices(local_args[0], subaddr_indices))
return true;
+ local_args.erase(local_args.begin());
+ }
+
+ if (local_args.size() > 0)
+ {
+ fail_msg_writer() << tr("usage: incoming_transfers [available|unavailable] [verbose] [index=<N>]");
+ return true;
}
tools::wallet2::transfer_container transfers;
@@ -4391,90 +4499,9 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
}
}
- 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_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 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 mix") << " = " << 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::multisig_export_needed& e)
- {
- LOG_ERROR("Multisig error: " << e.to_string());
- fail_msg_writer() << tr("Multisig 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 (...)
{
@@ -4508,6 +4535,12 @@ bool simple_wallet::sweep_below(const std::vector<std::string> &args_)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::donate(const std::vector<std::string> &args_)
{
+ if(m_wallet->testnet())
+ {
+ fail_msg_writer() << tr("donations are not enabled on the testnet");
+ return true;
+ }
+
std::vector<std::string> local_args = args_;
if(local_args.empty() || local_args.size() > 5)
{
@@ -6175,6 +6208,7 @@ bool simple_wallet::export_key_images(const std::vector<std::string> &args)
try
{
+ LOCK_IDLE_SCOPE();
if (!m_wallet->export_key_images(filename))
{
fail_msg_writer() << tr("failed to save file ") << filename;
@@ -6207,6 +6241,7 @@ bool simple_wallet::import_key_images(const std::vector<std::string> &args)
}
std::string filename = args[0];
+ LOCK_IDLE_SCOPE();
try
{
uint64_t spent = 0, unspent = 0;
@@ -6238,6 +6273,7 @@ bool simple_wallet::export_outputs(const std::vector<std::string> &args)
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
std::string filename = args[0];
+ LOCK_IDLE_SCOPE();
try
{
std::vector<tools::wallet2::transfer_details> outs = m_wallet->export_outputs();
@@ -6336,6 +6372,7 @@ bool simple_wallet::import_outputs(const std::vector<std::string> &args)
boost::archive::binary_iarchive ar(iss);
ar >> outputs;
}
+ LOCK_IDLE_SCOPE();
size_t n_outputs = m_wallet->import_outputs(outputs);
success_msg_writer() << boost::lexical_cast<std::string>(n_outputs) << " outputs imported";
}
@@ -6554,6 +6591,7 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_command);
command_line::add_arg(desc_params, arg_restore_deterministic_wallet );
+ command_line::add_arg(desc_params, arg_restore_multisig_wallet );
command_line::add_arg(desc_params, arg_non_deterministic );
command_line::add_arg(desc_params, arg_electrum_seed );
command_line::add_arg(desc_params, arg_trusted_daemon);
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index e5c00e542..024077ca1 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -92,6 +92,8 @@ namespace cryptonote
bool recover, bool two_random, const std::string &old_language);
bool 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);
+ bool new_wallet(const boost::program_options::variables_map& vm,
+ const std::string &multisig_keys, const std::string &old_language);
bool open_wallet(const boost::program_options::variables_map& vm);
bool close_wallet();
@@ -306,6 +308,7 @@ namespace cryptonote
crypto::secret_key m_recovery_key; // recovery key (used as random for wallet gen)
bool m_restore_deterministic_wallet; // recover flag
+ bool m_restore_multisig_wallet; // recover flag
bool m_non_deterministic; // old 2-random generation
bool m_trusted_daemon;
bool m_allow_mismatched_daemon_version;