diff options
Diffstat (limited to 'src/simplewallet/simplewallet.cpp')
-rw-r--r-- | src/simplewallet/simplewallet.cpp | 197 |
1 files changed, 137 insertions, 60 deletions
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 2556a3607..835abf569 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -136,6 +136,7 @@ namespace const command_line::arg_descriptor<bool> arg_non_deterministic = {"non-deterministic", sw::tr("Generate non-deterministic view and spend keys"), 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}; const command_line::arg_descriptor<uint64_t> arg_restore_height = {"restore-height", sw::tr("Restore from specific blockchain height"), 0}; + const command_line::arg_descriptor<std::string> arg_restore_date = {"restore-date", sw::tr("Restore from estimated blockchain height on specified date"), ""}; const command_line::arg_descriptor<bool> arg_do_not_relay = {"do-not-relay", sw::tr("The newly created transaction will not be relayed to the monero network"), false}; const command_line::arg_descriptor<bool> arg_create_address_file = {"create-address-file", sw::tr("Create an address file for new wallets"), false}; const command_line::arg_descriptor<std::string> arg_subaddress_lookahead = {"subaddress-lookahead", tools::wallet2::tr("Set subaddress lookahead sizes to <major>:<minor>"), ""}; @@ -146,7 +147,7 @@ namespace const char* USAGE_START_MINING("start_mining [<number_of_threads>] [bg_mining] [ignore_battery]"); const char* USAGE_SET_DAEMON("set_daemon <host>[:<port>] [trusted|untrusted]"); const char* USAGE_SHOW_BALANCE("balance [detail]"); - const char* USAGE_INCOMING_TRANSFERS("incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]]"); + const char* USAGE_INCOMING_TRANSFERS("incoming_transfers [available|unavailable] [verbose] [uses] [index=<N1>[,<N2>[,...]]]"); const char* USAGE_PAYMENTS("payments <PID_1> [<PID_2> ... <PID_N>]"); const char* USAGE_PAYMENT_ID("payment_id"); const char* USAGE_TRANSFER("transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <address> <amount>) [<payment_id>]"); @@ -540,7 +541,7 @@ namespace } catch (const tools::error::tx_rejected& e) { - fail_msg_writer() << (boost::format(sw::tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status(); + fail_msg_writer() << (boost::format(sw::tr("transaction %s was rejected by daemon")) % get_transaction_hash(e.tx())); std::string reason = e.reason(); if (!reason.empty()) fail_msg_writer() << sw::tr("Reason: ") << reason; @@ -2488,6 +2489,19 @@ bool simple_wallet::set_ignore_fractional_outputs(const std::vector<std::string> return true; } +bool simple_wallet::set_track_uses(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +{ + const auto pwd_container = get_and_verify_password(); + if (pwd_container) + { + parse_bool_and_use(args[1], [&](bool r) { + m_wallet->track_uses(r); + m_wallet->rewrite(m_wallet_file, pwd_container->password()); + }); + } + 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(); @@ -3032,6 +3046,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args) success_msg_writer() << "subaddress-lookahead = " << lookahead.first << ":" << lookahead.second; success_msg_writer() << "segregation-height = " << m_wallet->segregation_height(); success_msg_writer() << "ignore-fractional-outputs = " << m_wallet->ignore_fractional_outputs(); + success_msg_writer() << "track-uses = " << m_wallet->track_uses(); success_msg_writer() << "device_name = " << m_wallet->device_name(); return true; } @@ -3088,6 +3103,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("track-uses", set_track_uses, 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)"); @@ -3245,6 +3261,28 @@ static bool might_be_partial_seed(const epee::wipeable_string &words) return seed.size() < 24; } //---------------------------------------------------------------------------------------------------- +static bool datestr_to_int(const std::string &heightstr, uint16_t &year, uint8_t &month, uint8_t &day) +{ + if (heightstr.size() != 10 || heightstr[4] != '-' || heightstr[7] != '-') + { + fail_msg_writer() << tr("date format must be YYYY-MM-DD"); + return false; + } + try + { + year = boost::lexical_cast<uint16_t>(heightstr.substr(0,4)); + // lexical_cast<uint8_t> won't work because uint8_t is treated as character type + month = boost::lexical_cast<uint16_t>(heightstr.substr(5,2)); + day = boost::lexical_cast<uint16_t>(heightstr.substr(8,2)); + } + catch (const boost::bad_lexical_cast &) + { + fail_msg_writer() << tr("bad height parameter: ") << heightstr; + return false; + } + return true; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::init(const boost::program_options::variables_map& vm) { epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ @@ -3685,7 +3723,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) tools::scoped_message_writer wrt = tools::msg_writer(); wrt << tr("No restore height is specified."); wrt << tr("Assumed you are creating a new account, restore will be done from current estimated blockchain height."); - wrt << tr("Use --restore-height if you want to restore an already setup account from a specific height"); + wrt << tr("Use --restore-height or --restore-date if you want to restore an already setup account from a specific height"); } std::string confirm = input_line(tr("Is this okay? (Y/Yes/N/No): ")); if (std::cin.eof() || !command_line::is_yes(confirm)) @@ -3714,7 +3752,26 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) if (m_restoring && m_generate_from_json.empty() && m_generate_from_device.empty()) { - m_wallet->explicit_refresh_from_block_height(!command_line::is_arg_defaulted(vm, arg_restore_height)); + m_wallet->explicit_refresh_from_block_height(!(command_line::is_arg_defaulted(vm, arg_restore_height) || + command_line::is_arg_defaulted(vm, arg_restore_date))); + if (command_line::is_arg_defaulted(vm, arg_restore_height) && !command_line::is_arg_defaulted(vm, arg_restore_date)) + { + uint16_t year; + uint8_t month; + uint8_t day; + if (!datestr_to_int(m_restore_date, year, month, day)) + return false; + try + { + m_restore_height = m_wallet->get_blockchain_height_by_date(year, month, day); + success_msg_writer() << tr("Restore height is: ") << m_restore_height; + } + catch (const std::runtime_error& e) + { + fail_msg_writer() << e.what(); + return false; + } + } } if (!m_wallet->explicit_refresh_from_block_height() && m_restoring) { @@ -3746,20 +3803,13 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) fail_msg_writer() << tr("bad m_restore_height parameter: ") << heightstr; continue; } - if (heightstr.size() != 10 || heightstr[4] != '-' || heightstr[7] != '-') - { - fail_msg_writer() << tr("date format must be YYYY-MM-DD"); - continue; - } uint16_t year; uint8_t month; // 1, 2, ..., 12 uint8_t day; // 1, 2, ..., 31 try { - year = boost::lexical_cast<uint16_t>(heightstr.substr(0,4)); - // lexical_cast<uint8_t> won't work because uint8_t is treated as character type - month = boost::lexical_cast<uint16_t>(heightstr.substr(5,2)); - day = boost::lexical_cast<uint16_t>(heightstr.substr(8,2)); + if (!datestr_to_int(heightstr, year, month, day)) + return false; 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 = input_line(tr("Is this okay? (Y/Yes/N/No): ")); @@ -3846,6 +3896,7 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_ m_non_deterministic = command_line::get_arg(vm, arg_non_deterministic); m_allow_mismatched_daemon_version = command_line::get_arg(vm, arg_allow_mismatched_daemon_version); m_restore_height = command_line::get_arg(vm, arg_restore_height); + m_restore_date = command_line::get_arg(vm, arg_restore_date); m_do_not_relay = command_line::get_arg(vm, arg_do_not_relay); m_subaddress_lookahead = command_line::get_arg(vm, arg_subaddress_lookahead); m_use_english_language_names = command_line::get_arg(vm, arg_use_english_language_names); @@ -3858,6 +3909,14 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_ m_restore_deterministic_wallet || m_restore_multisig_wallet; + if (!command_line::is_arg_defaulted(vm, arg_restore_date)) + { + uint16_t year; + uint8_t month, day; + if (!datestr_to_int(m_restore_date, year, month, day)) + return false; + } + return true; } //---------------------------------------------------------------------------------------------------- @@ -4813,6 +4872,7 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args bool filter = false; bool available = false; bool verbose = false; + bool uses = false; if (local_args.size() > 0) { if (local_args[0] == "available") @@ -4828,12 +4888,22 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args local_args.erase(local_args.begin()); } } - if (local_args.size() > 0 && local_args[0] == "verbose") + while (local_args.size() > 0) { - verbose = true; + if (local_args[0] == "verbose") + verbose = true; + else if (local_args[0] == "uses") + uses = true; + else + { + fail_msg_writer() << tr("Invalid keyword: ") << local_args.front(); + break; + } local_args.erase(local_args.begin()); } + const uint64_t blockchain_height = m_wallet->get_blockchain_current_height(); + PAUSE_READLINE(); std::set<uint32_t> subaddr_indices; @@ -4867,9 +4937,16 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args 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; } - std::string verbose_string; + std::string extra_string; if (verbose) - verbose_string = (boost::format("%68s%68s") % td.get_public_key() % (td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : td.m_key_image_partial ? (epee::string_tools::pod_to_hex(td.m_key_image) + "/p") : std::string(64, '?'))).str(); + extra_string += (boost::format("%68s%68s") % td.get_public_key() % (td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : td.m_key_image_partial ? (epee::string_tools::pod_to_hex(td.m_key_image) + "/p") : std::string(64, '?'))).str(); + if (uses) + { + std::vector<uint64_t> heights; + for (const auto &e: td.m_uses) heights.push_back(e.first); + const std::pair<std::string, std::string> line = show_outputs_line(heights, blockchain_height, td.m_spent_height); + extra_string += tr("Heights: ") + line.first + "\n" + line.second; + } message_writer(td.m_spent ? console_color_magenta : console_color_green, false) << boost::format("%21s%8s%12s%8s%16u%68s%16u%s") % print_money(td.amount()) % @@ -4879,7 +4956,7 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args td.m_global_output_index % td.m_txid % td.m_subaddr_index.minor % - verbose_string; + extra_string; ++transfers_found; } } @@ -5031,6 +5108,33 @@ bool simple_wallet::rescan_spent(const std::vector<std::string> &args) return true; } //---------------------------------------------------------------------------------------------------- +std::pair<std::string, std::string> simple_wallet::show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_height) const +{ + std::stringstream ostr; + + for (uint64_t h: heights) + blockchain_height = std::max(blockchain_height, h); + + for (size_t j = 0; j < heights.size(); ++j) + ostr << (heights[j] == highlight_height ? " *" : " ") << heights[j]; + + // visualize the distribution, using the code by moneroexamples onion-monero-viewer + const uint64_t resolution = 79; + std::string ring_str(resolution, '_'); + for (size_t j = 0; j < heights.size(); ++j) + { + uint64_t pos = (heights[j] * resolution) / blockchain_height; + ring_str[pos] = 'o'; + } + if (highlight_height < blockchain_height) + { + uint64_t pos = (highlight_height * resolution) / blockchain_height; + ring_str[pos] = '*'; + } + + return std::make_pair(ostr.str(), ring_str); +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr) { uint32_t version; @@ -5101,21 +5205,18 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending } } ostr << tr("\nOriginating block heights: "); - for (size_t j = 0; j < absolute_offsets.size(); ++j) - ostr << tr(j == source.real_output ? " *" : " ") << res.outs[j].height; spent_key_height[i] = res.outs[source.real_output].height; spent_key_txid [i] = res.outs[source.real_output].txid; - // visualize the distribution, using the code by moneroexamples onion-monero-viewer - const uint64_t resolution = 79; - std::string ring_str(resolution, '_'); + std::vector<uint64_t> heights(absolute_offsets.size(), 0); + uint64_t highlight_height = std::numeric_limits<uint64_t>::max(); for (size_t j = 0; j < absolute_offsets.size(); ++j) { - uint64_t pos = (res.outs[j].height * resolution) / blockchain_height; - ring_str[pos] = 'o'; + heights[j] = res.outs[j].height; + if (j == source.real_output) + highlight_height = heights[j]; } - uint64_t pos = (res.outs[source.real_output].height * resolution) / blockchain_height; - ring_str[pos] = '*'; - ostr << tr("\n|") << ring_str << tr("|\n"); + std::pair<std::string, std::string> ring_str = show_outputs_line(heights, highlight_height); + ostr << ring_str.first << tr("\n|") << ring_str.second << tr("|\n"); } // warn if rings contain keys originating from the same tx or temporally very close block heights bool are_keys_from_same_tx = false; @@ -5218,19 +5319,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri payment_id_seen = true; message_writer() << tr("Unencrypted payment IDs are bad for privacy: ask the recipient to use subaddresses instead"); } - else - { - crypto::hash8 payment_id8; - if (tools::wallet2::parse_short_payment_id(payment_id_str, payment_id8)) - { - std::string extra_nonce; - set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id8); - r = add_extra_nonce_to_tx_extra(extra, extra_nonce); - local_args.pop_back(); - payment_id_seen = true; - } - } - if(!r) { fail_msg_writer() << tr("payment id failed to encode"); @@ -5883,18 +5971,6 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st r = add_extra_nonce_to_tx_extra(extra, extra_nonce); payment_id_seen = true; } - else - { - crypto::hash8 payment_id8; - r = tools::wallet2::parse_short_payment_id(payment_id_str, payment_id8); - if(r) - { - std::string extra_nonce; - set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id8); - r = add_extra_nonce_to_tx_extra(extra, extra_nonce); - payment_id_seen = true; - } - } if(!r && local_args.size() == 3) { @@ -6147,10 +6223,6 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_) { 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"); @@ -7239,6 +7311,7 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec const std::string type = pd.m_coinbase ? tr("block") : tr("in"); const bool unlocked = m_wallet->is_transfer_unlocked(pd.m_unlock_time, pd.m_block_height); transfers.push_back({ + type, pd.m_block_height, pd.m_timestamp, type, @@ -7271,6 +7344,7 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec payment_id = payment_id.substr(0,16); std::string note = m_wallet->get_tx_note(i->first); transfers.push_back({ + "out", pd.m_block_height, pd.m_timestamp, "out", @@ -7308,6 +7382,7 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec double_spend_note = tr("[Double spend seen on the network: this transaction may or may not end up being mined] "); transfers.push_back({ "pool", + "pool", pd.m_timestamp, "in", false, @@ -7348,6 +7423,7 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec if ((failed && is_failed) || (!is_failed && pending)) { transfers.push_back({ (is_failed ? "failed" : "pending"), + (is_failed ? "failed" : "pending"), pd.m_timestamp, "out", false, @@ -7395,7 +7471,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_) for (const auto& transfer : all_transfers) { - const auto color = transfer.confirmed ? ((transfer.direction == "in" || transfer.direction == "block") ? console_color_green : console_color_magenta) : console_color_white; + const auto color = transfer.type == "failed" ? console_color_red : transfer.confirmed ? ((transfer.direction == "in" || transfer.direction == "block") ? console_color_green : console_color_magenta) : console_color_default; std::string destinations = "-"; if (!transfer.outputs.empty()) @@ -8150,8 +8226,8 @@ bool simple_wallet::address_book(const std::vector<std::string> &args/* = std::v } else if (tools::wallet2::parse_short_payment_id(args[3], info.payment_id)) { - memcpy(payment_id.data, info.payment_id.data, 8); - description_start += 2; + fail_msg_writer() << tr("Short payment IDs are to be used within an integrated address only"); + return true; } else { @@ -8867,6 +8943,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_params, arg_electrum_seed ); command_line::add_arg(desc_params, arg_allow_mismatched_daemon_version); command_line::add_arg(desc_params, arg_restore_height); + command_line::add_arg(desc_params, arg_restore_date); command_line::add_arg(desc_params, arg_do_not_relay); command_line::add_arg(desc_params, arg_create_address_file); command_line::add_arg(desc_params, arg_subaddress_lookahead); |