aboutsummaryrefslogtreecommitdiff
path: root/src/simplewallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/simplewallet')
-rw-r--r--src/simplewallet/simplewallet.cpp214
-rw-r--r--src/simplewallet/simplewallet.h8
2 files changed, 200 insertions, 22 deletions
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 3d92b2823..702ff22cb 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -1801,6 +1801,27 @@ bool simple_wallet::version(const std::vector<std::string> &args)
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)
+{
+ std::vector<std::string> tx_aux;
+
+ message_writer(console_color_white, false) << tr("Please confirm the transaction on the device");
+
+ m_wallet->cold_sign_tx(ptx_vector, exported_txs, dsts_info, tx_aux);
+
+ if (accept_func && !accept_func(exported_txs))
+ {
+ MERROR("Transactions rejected by callback");
+ return false;
+ }
+
+ // aux info
+ m_wallet->cold_tx_aux_import(exported_txs.ptx, tx_aux);
+
+ // import key images
+ return m_wallet->import_key_images(exported_txs.key_images);
+}
+
bool simple_wallet::set_always_confirm_transfers(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
const auto pwd_container = get_and_verify_password();
@@ -2253,6 +2274,33 @@ bool simple_wallet::set_ignore_fractional_outputs(const std::vector<std::string>
return true;
}
+bool simple_wallet::set_device_name(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+{
+ const auto pwd_container = get_and_verify_password();
+ if (pwd_container)
+ {
+ if (args.size() == 0){
+ fail_msg_writer() << tr("Device name not specified");
+ return true;
+ }
+
+ m_wallet->device_name(args[0]);
+ bool r = false;
+ try {
+ r = m_wallet->reconnect_device();
+ if (!r){
+ fail_msg_writer() << tr("Device reconnect failed");
+ }
+
+ } catch(const std::exception & e){
+ MWARNING("Device reconnect failed: " << e.what());
+ fail_msg_writer() << tr("Device reconnect failed: ") << e.what();
+ }
+
+ }
+ return true;
+}
+
bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
if(args.empty())
@@ -2484,13 +2532,22 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("show_transfers",
boost::bind(&simple_wallet::show_transfers, this, _1),
tr("show_transfers [in|out|pending|failed|pool|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]"),
- tr("Show the incoming/outgoing transfers within an optional height range."));
+ // Seemingly broken formatting to compensate for the backslash before the quotes.
+ tr("Show the incoming/outgoing transfers within an optional height range.\n\n"
+ "Output format:\n"
+ "In or Coinbase: Block Number, \"block\"|\"in\", Time, Amount, Transaction Hash, Payment ID, Subaddress Index, \"-\", Note\n"
+ "Out: Block Number, \"out\", Time, Amount*, Transaction Hash, Payment ID, Fee, Destinations, Input addresses**, \"-\", Note\n"
+ "Pool: \"pool\", \"in\", Time, Amount, Transaction Hash, Payment Id, Subaddress Index, \"-\", Note, Double Spend Note\n"
+ "Pending or Failed: \"failed\"|\"pending\", \"out\", Time, Amount*, Transaction Hash, Payment ID, Fee, Input addresses**, \"-\", Note\n\n"
+ "* Excluding change and fee.\n"
+ "** Set of address indices used as inputs in this transfer."));
m_cmd_binder.set_handler("unspent_outputs",
boost::bind(&simple_wallet::unspent_outputs, this, _1),
tr("unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]]"),
tr("Show the unspent outputs of a specified address within an optional amount range."));
m_cmd_binder.set_handler("rescan_bc",
boost::bind(&simple_wallet::rescan_blockchain, this, _1),
+ tr("rescan_bc [hard]"),
tr("Rescan the blockchain from scratch, losing any information which can not be recovered from the blockchain itself."));
m_cmd_binder.set_handler("set_tx_note",
boost::bind(&simple_wallet::set_tx_note, this, _1),
@@ -2529,6 +2586,10 @@ simple_wallet::simple_wallet()
boost::bind(&simple_wallet::import_key_images, this, _1),
tr("import_key_images <file>"),
tr("Import a signed key images list and verify their spent status."));
+ m_cmd_binder.set_handler("hw_key_images_sync",
+ boost::bind(&simple_wallet::hw_key_images_sync, this, _1),
+ tr("hw_key_images_sync"),
+ tr("Synchronizes key images with the hw wallet."));
m_cmd_binder.set_handler("hw_reconnect",
boost::bind(&simple_wallet::hw_reconnect, this, _1),
tr("hw_reconnect"),
@@ -2720,6 +2781,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
CHECK_SIMPLE_VARIABLE("subaddress-lookahead", set_subaddress_lookahead, tr("<major>:<minor>"));
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("device-name", set_device_name, tr("<device_name[:device_spec]>"));
}
fail_msg_writer() << tr("set: unrecognized argument(s)");
return true;
@@ -4237,15 +4299,15 @@ boost::optional<epee::wipeable_string> simple_wallet::on_get_password(const char
return pwd_container->password();
}
//----------------------------------------------------------------------------------------------------
-bool simple_wallet::refresh_main(uint64_t start_height, bool reset, bool is_init)
+bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bool is_init)
{
if (!try_connect_to_daemon(is_init))
return true;
LOCK_IDLE_SCOPE();
- if (reset)
- m_wallet->rescan_blockchain(false);
+ if (reset != ResetNone)
+ m_wallet->rescan_blockchain(reset == ResetHard, false);
#ifdef HAVE_READLINE
rdln::suspend_readline pause_readline;
@@ -4324,7 +4386,7 @@ bool simple_wallet::refresh(const std::vector<std::string>& args)
start_height = 0;
}
}
- return refresh_main(start_height, false);
+ return refresh_main(start_height, ResetNone);
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::show_balance_unlocked(bool detailed)
@@ -4422,7 +4484,7 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
tools::wallet2::transfer_container transfers;
m_wallet->get_transfers(transfers);
- bool transfers_found = false;
+ size_t transfers_found = 0;
for (const auto& td : transfers)
{
if (!filter || available != td.m_spent)
@@ -4435,7 +4497,6 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
if (verbose)
verbose_string = (boost::format("%68s%68s") % tr("pubkey") % tr("key image")).str();
message_writer() << boost::format("%21s%8s%12s%8s%16s%68s%16s%s") % tr("amount") % tr("spent") % tr("unlocked") % tr("ringct") % tr("global index") % tr("tx id") % tr("addr index") % verbose_string;
- transfers_found = true;
}
std::string verbose_string;
if (verbose)
@@ -4450,6 +4511,7 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
td.m_txid %
td.m_subaddr_index.minor %
verbose_string;
+ ++transfers_found;
}
}
@@ -4468,6 +4530,10 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
success_msg_writer() << tr("No incoming unavailable transfers");
}
}
+ else
+ {
+ success_msg_writer() << boost::format("Found %u/%u transfers") % transfers_found % transfers.size();
+ }
return true;
}
@@ -4826,12 +4892,14 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
local_args.pop_back();
}
+ vector<cryptonote::address_parse_info> dsts_info;
vector<cryptonote::tx_destination_entry> dsts;
size_t num_subaddresses = 0;
for (size_t i = 0; i < local_args.size(); )
{
+ dsts_info.emplace_back();
+ cryptonote::address_parse_info & info = dsts_info.back();
cryptonote::tx_destination_entry de;
- cryptonote::address_parse_info info;
bool r = true;
// check for a URI
@@ -5115,6 +5183,28 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_monero_tx";
}
}
+ else if (m_wallet->get_account().get_device().has_tx_cold_sign())
+ {
+ try
+ {
+ tools::wallet2::signed_tx_set signed_tx;
+ if (!cold_sign_tx(ptx_vector, signed_tx, dsts_info, [&](const tools::wallet2::signed_tx_set &tx){ return accept_loaded_tx(tx); })){
+ fail_msg_writer() << tr("Failed to cold sign transaction with HW wallet");
+ return true;
+ }
+
+ commit_or_save(signed_tx.ptx, m_do_not_relay);
+ }
+ catch (const std::exception& e)
+ {
+ handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
+ }
+ catch (...)
+ {
+ LOG_ERROR("Unknown error");
+ fail_msg_writer() << tr("unknown error");
+ }
+ }
else if (m_wallet->watch_only())
{
bool r = m_wallet->save_tx(ptx_vector, "unsigned_monero_tx");
@@ -5537,6 +5627,31 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_monero_tx";
}
}
+ else if (m_wallet->get_account().get_device().has_tx_cold_sign())
+ {
+ try
+ {
+ tools::wallet2::signed_tx_set signed_tx;
+ std::vector<cryptonote::address_parse_info> dsts_info;
+ dsts_info.push_back(info);
+
+ if (!cold_sign_tx(ptx_vector, signed_tx, dsts_info, [&](const tools::wallet2::signed_tx_set &tx){ return accept_loaded_tx(tx); })){
+ fail_msg_writer() << tr("Failed to cold sign transaction with HW wallet");
+ return true;
+ }
+
+ commit_or_save(signed_tx.ptx, m_do_not_relay);
+ }
+ catch (const std::exception& e)
+ {
+ handle_transfer_exception(std::current_exception(), m_wallet->is_trusted_daemon());
+ }
+ catch (...)
+ {
+ LOG_ERROR("Unknown error");
+ fail_msg_writer() << tr("unknown error");
+ }
+ }
else if (m_wallet->watch_only())
{
bool r = m_wallet->save_tx(ptx_vector, "unsigned_monero_tx");
@@ -5995,8 +6110,8 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes,
bool simple_wallet::accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs)
{
std::string extra_message;
- if (!txs.transfers.empty())
- extra_message = (boost::format("%u outputs to import. ") % (unsigned)txs.transfers.size()).str();
+ if (!txs.transfers.second.empty())
+ extra_message = (boost::format("%u outputs to import. ") % (unsigned)txs.transfers.second.size()).str();
return accept_loaded_tx([&txs](){return txs.txes.size();}, [&txs](size_t n)->const tools::wallet2::tx_construction_data&{return txs.txes[n];}, extra_message);
}
//----------------------------------------------------------------------------------------------------
@@ -6749,7 +6864,8 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
payment_id = payment_id.substr(0,16);
std::string note = m_wallet->get_tx_note(pd.m_tx_hash);
const std::string type = pd.m_coinbase ? tr("block") : tr("in");
- output.insert(std::make_pair(pd.m_block_height, std::make_tuple(epee::console_color_green, type, (boost::format("%25.25s %20.20s %s %s %d %s %s") % 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())));
+ const bool unlocked = m_wallet->is_tx_spendtime_unlocked(pd.m_unlock_time, pd.m_block_height);
+ output.insert(std::make_pair(pd.m_block_height, std::make_tuple(epee::console_color_green, type, (boost::format("%8.8s %25.25s %20.20s %s %s %d %s %s") % (unlocked ? "unlocked" : "locked") % 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())));
}
}
@@ -6782,7 +6898,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
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(i->first);
- output.insert(std::make_pair(pd.m_block_height, std::make_tuple(epee::console_color_magenta, tr("out"), (boost::format("%25.25s %20.20s %s %s %14.14s %s %s - %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount_in - change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % dests % print_subaddr_indices(pd.m_subaddr_indices) % note).str())));
+ output.insert(std::make_pair(pd.m_block_height, std::make_tuple(epee::console_color_magenta, tr("out"), (boost::format("%8.8s %25.25s %20.20s %s %s %14.14s %s %s - %s") % "-" % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount_in - change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % dests % print_subaddr_indices(pd.m_subaddr_indices) % note).str())));
}
}
@@ -6811,7 +6927,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
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 %25.25s %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();
+ message_writer() << (boost::format("%8.8s %6.6s %8.8s %25.25s %20.20s %s %s %d %s %s%s") % "pool" % "in" % "locked" % 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)
@@ -6834,7 +6950,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
std::string note = m_wallet->get_tx_note(i->first);
bool is_failed = pd.m_state == tools::wallet2::unconfirmed_transfer_details::failed;
if ((failed && is_failed) || (!is_failed && pending)) {
- message_writer() << (boost::format("%8.8s %6.6s %25.25s %20.20s %s %s %14.14s %s - %s") % (is_failed ? tr("failed") : tr("pending")) % tr("out") % get_human_readable_timestamp(pd.m_timestamp) % print_money(amount - pd.m_change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % print_subaddr_indices(pd.m_subaddr_indices) % note).str();
+ message_writer() << (boost::format("%8.8s %6.6s %8.8s %25.25s %20.20s %s %s %14.14s %s - %s") % (is_failed ? tr("failed") : tr("pending")) % tr("out") % "-" % get_human_readable_timestamp(pd.m_timestamp) % print_money(amount - pd.m_change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % print_subaddr_indices(pd.m_subaddr_indices) % note).str();
}
}
}
@@ -6982,15 +7098,29 @@ bool simple_wallet::unspent_outputs(const std::vector<std::string> &args_)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::rescan_blockchain(const std::vector<std::string> &args_)
{
- message_writer() << tr("Warning: this will lose any information which can not be recovered from the blockchain.");
- message_writer() << tr("This includes destination addresses, tx secret keys, tx notes, etc");
- std::string confirm = input_line(tr("Rescan anyway ? (Y/Yes/N/No): "));
- if(!std::cin.eof())
+ bool hard = false;
+ if (!args_.empty())
{
- if (!command_line::is_yes(confirm))
+ if (args_[0] != "hard")
+ {
+ fail_msg_writer() << tr("usage: rescan_bc [hard]");
return true;
+ }
+ hard = true;
+ }
+
+ if (hard)
+ {
+ message_writer() << tr("Warning: this will lose any information which can not be recovered from the blockchain.");
+ message_writer() << tr("This includes destination addresses, tx secret keys, tx notes, etc");
+ std::string confirm = input_line(tr("Rescan anyway ? (Y/Yes/N/No): "));
+ if(!std::cin.eof())
+ {
+ if (!command_line::is_yes(confirm))
+ return true;
+ }
}
- return refresh_main(0, true);
+ return refresh_main(0, hard ? ResetHard : ResetSoft, true);
}
//----------------------------------------------------------------------------------------------------
void simple_wallet::wallet_idle_thread()
@@ -7038,7 +7168,7 @@ bool simple_wallet::run()
// check and display warning, but go on anyway
try_connect_to_daemon();
- refresh_main(0, false, true);
+ refresh_main(0, ResetNone, true);
m_auto_refresh_enabled = m_wallet->auto_refresh();
m_idle_thread = boost::thread([&]{wallet_idle_thread();});
@@ -7786,6 +7916,48 @@ bool simple_wallet::import_key_images(const std::vector<std::string> &args)
return true;
}
//----------------------------------------------------------------------------------------------------
+bool simple_wallet::hw_key_images_sync(const std::vector<std::string> &args)
+{
+ if (!m_wallet->key_on_device())
+ {
+ fail_msg_writer() << tr("command only supported by HW wallet");
+ return true;
+ }
+ if (!m_wallet->get_account().get_device().has_ki_cold_sync())
+ {
+ fail_msg_writer() << tr("hw wallet does not support cold KI sync");
+ return true;
+ }
+ if (!m_wallet->is_trusted_daemon())
+ {
+ fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon");
+ return true;
+ }
+
+ LOCK_IDLE_SCOPE();
+ try
+ {
+ message_writer(console_color_white, false) << tr("Please confirm the key image sync on the device");
+
+ uint64_t spent = 0, unspent = 0;
+ uint64_t height = m_wallet->cold_key_image_sync(spent, unspent);
+ if (height > 0)
+ {
+ success_msg_writer() << tr("Signed key images imported to height ") << height << ", "
+ << print_money(spent) << tr(" spent, ") << print_money(unspent) << tr(" unspent");
+ } else {
+ fail_msg_writer() << tr("Failed to import key images");
+ }
+ }
+ catch (const std::exception &e)
+ {
+ fail_msg_writer() << tr("Failed to import key images: ") << e.what();
+ return true;
+ }
+
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
bool simple_wallet::hw_reconnect(const std::vector<std::string> &args)
{
if (!m_wallet->key_on_device())
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index 39b715b73..26d51a431 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -83,6 +83,9 @@ namespace cryptonote
std::string get_commands_str();
std::string get_command_usage(const std::vector<std::string> &args);
private:
+
+ enum ResetType { ResetNone, ResetSoft, ResetHard };
+
bool handle_command_line(const boost::program_options::variables_map& vm);
bool run_console_handler();
@@ -139,6 +142,7 @@ namespace cryptonote
bool set_subaddress_lookahead(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_device_name(const std::vector<std::string> &args = std::vector<std::string>());
bool help(const std::vector<std::string> &args = std::vector<std::string>());
bool start_mining(const std::vector<std::string> &args);
bool stop_mining(const std::vector<std::string> &args);
@@ -188,7 +192,7 @@ namespace cryptonote
bool show_transfers(const std::vector<std::string> &args);
bool unspent_outputs(const std::vector<std::string> &args);
bool rescan_blockchain(const std::vector<std::string> &args);
- bool refresh_main(uint64_t start_height, bool reset = false, bool is_init = false);
+ bool refresh_main(uint64_t start_height, ResetType reset, bool is_init = false);
bool set_tx_note(const std::vector<std::string> &args);
bool get_tx_note(const std::vector<std::string> &args);
bool set_description(const std::vector<std::string> &args);
@@ -200,6 +204,7 @@ namespace cryptonote
bool verify(const std::vector<std::string> &args);
bool export_key_images(const std::vector<std::string> &args);
bool import_key_images(const std::vector<std::string> &args);
+ bool hw_key_images_sync(const std::vector<std::string> &args);
bool hw_reconnect(const std::vector<std::string> &args);
bool export_outputs(const std::vector<std::string> &args);
bool import_outputs(const std::vector<std::string> &args);
@@ -224,6 +229,7 @@ namespace cryptonote
bool unblackball(const std::vector<std::string>& args);
bool blackballed(const std::vector<std::string>& args);
bool version(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);
uint64_t get_daemon_blockchain_height(std::string& err);
bool try_connect_to_daemon(bool silent = false, uint32_t* version = nullptr);