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.cpp197
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);