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.cpp135
1 files changed, 87 insertions, 48 deletions
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 74e793669..a6844ba0a 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -108,7 +108,7 @@ typedef cryptonote::simple_wallet sw;
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ \
/* m_idle_mutex is still locked here */ \
m_auto_refresh_enabled.store(auto_refresh_enabled, std::memory_order_relaxed); \
- m_suspend_rpc_payment_mining.store(false, std::memory_order_relaxed);; \
+ m_suspend_rpc_payment_mining.store(false, std::memory_order_relaxed); \
m_rpc_payment_checker.trigger(); \
m_idle_cond.notify_one(); \
})
@@ -180,8 +180,8 @@ namespace
const char* USAGE_PAYMENT_ID("payment_id");
const char* USAGE_TRANSFER("transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <address> <amount>) [<payment_id>]");
const char* USAGE_LOCKED_TRANSFER("locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <addr> <amount>) <lockblocks> [<payment_id (obsolete)>]");
- const char* USAGE_LOCKED_SWEEP_ALL("locked_sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <lockblocks> [<payment_id (obsolete)>]");
- const char* USAGE_SWEEP_ALL("sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id (obsolete)>]");
+ const char* USAGE_LOCKED_SWEEP_ALL("locked_sweep_all [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] <address> <lockblocks> [<payment_id (obsolete)>]");
+ const char* USAGE_SWEEP_ALL("sweep_all [index=<N1>[,<N2>,...] | index=all] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id (obsolete)>]");
const char* USAGE_SWEEP_BELOW("sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id (obsolete)>]");
const char* USAGE_SWEEP_SINGLE("sweep_single [<priority>] [<ring_size>] [outputs=<N>] <key_image> <address> [<payment_id (obsolete)>]");
const char* USAGE_DONATE("donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id (obsolete)>]");
@@ -1691,7 +1691,7 @@ bool simple_wallet::print_ring(const std::vector<std::string> &args)
rings.push_back({key_image, ring});
else if (!m_wallet->get_rings(txid, rings))
{
- fail_msg_writer() << tr("Key image either not spent, or spent with mixin 0");
+ fail_msg_writer() << tr("Key image either not spent, or spent with ring size 1");
return true;
}
@@ -1963,7 +1963,7 @@ bool simple_wallet::rpc_payment_info(const std::vector<std::string> &args)
if (expected)
message_writer() << tr("Credit discrepancy this session: ") << discrepancy << " (" << 100.0f * discrepancy / expected << "%)";
float cph = credits_per_hash_found / (float)diff;
- message_writer() << tr("Difficulty: ") << diff << ", " << credits_per_hash_found << " " << tr("credits per hash found, ") << cph << " " << tr("credits/hash");;
+ message_writer() << tr("Difficulty: ") << diff << ", " << credits_per_hash_found << " " << tr("credits per hash found, ") << cph << " " << tr("credits/hash");
const boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time();
bool mining = (now - m_last_rpc_payment_mining_time).total_microseconds() < 1000000;
if (mining)
@@ -3138,13 +3138,13 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("locked_sweep_all",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::locked_sweep_all,_1),
tr(USAGE_LOCKED_SWEEP_ALL),
- tr("Send all unlocked balance to an address and lock it for <lockblocks> (max. 1000000). 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. <priority> is the priority of the sweep. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability."));
+ tr("Send all unlocked balance to an address and lock it for <lockblocks> (max. 1000000). If the parameter \"index=<N1>[,<N2>,...]\" or \"index=all\" is specified, the wallet sweeps outputs received by those or all address indices, respectively. If omitted, the wallet randomly chooses an address index to be used. <priority> is the priority of the sweep. The higher the priority, the higher the transaction fee. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability."));
m_cmd_binder.set_handler("sweep_unmixable",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_unmixable, _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(USAGE_SWEEP_ALL),
- tr("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. If the parameter \"outputs=<N>\" is specified and N > 0, wallet splits the transaction into N even outputs."));
+ tr("Send all unlocked balance to an address. If the parameter \"index=<N1>[,<N2>,...]\" or \"index=all\" is specified, the wallet sweeps outputs received by those or all address indices, respectively. If omitted, the wallet randomly chooses an address index to be used. If the parameter \"outputs=<N>\" is specified and N > 0, wallet splits the transaction into N even outputs."));
m_cmd_binder.set_handler("sweep_below",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_below, _1),
tr(USAGE_SWEEP_BELOW),
@@ -5199,8 +5199,11 @@ void simple_wallet::check_background_mining(const epee::wipeable_string &passwor
if (is_background_mining_enabled)
{
// already active, nice
- m_wallet->setup_background_mining(tools::wallet2::BackgroundMiningYes);
- m_wallet->rewrite(m_wallet_file, password);
+ if (setup == tools::wallet2::BackgroundMiningMaybe)
+ {
+ m_wallet->setup_background_mining(tools::wallet2::BackgroundMiningYes);
+ m_wallet->rewrite(m_wallet_file, password);
+ }
start_background_mining();
return;
}
@@ -5223,6 +5226,11 @@ void simple_wallet::check_background_mining(const epee::wipeable_string &passwor
m_wallet->rewrite(m_wallet_file, password);
start_background_mining();
}
+ else
+ {
+ // the setting is already enabled, and the daemon is not mining yet, so start it
+ start_background_mining();
+ }
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::start_mining(const std::vector<std::string>& args)
@@ -5469,20 +5477,28 @@ void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid,
std::vector<tx_extra_field> tx_extra_fields;
parse_tx_extra(tx.extra, tx_extra_fields); // failure ok
tx_extra_nonce extra_nonce;
- if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
+ tx_extra_pub_key extra_pub_key;
+ crypto::hash8 payment_id8 = crypto::null_hash8;
+ if (find_tx_extra_field_by_type(tx_extra_fields, extra_pub_key))
{
- crypto::hash payment_id = crypto::null_hash;
- crypto::hash8 payment_id8 = crypto::null_hash8;
- if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
+ const crypto::public_key &tx_pub_key = extra_pub_key.pub_key;
+ if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
{
- if (payment_id8 != crypto::null_hash8)
- message_writer() <<
- tr("NOTE: this transaction uses an encrypted payment ID: consider using subaddresses instead");
- }
- else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
- message_writer(console_color_red, false) <<
- tr("WARNING: this transaction uses an unencrypted payment ID: these are obsolete and ignored. Use subaddresses instead.");
- }
+ if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
+ {
+ m_wallet->get_account().get_device().decrypt_payment_id(payment_id8, tx_pub_key, m_wallet->get_account().get_keys().m_view_secret_key);
+ }
+ }
+ }
+
+ if (payment_id8 != crypto::null_hash8)
+ message_writer() <<
+ tr("NOTE: this transaction uses an encrypted payment ID: consider using subaddresses instead");
+
+ crypto::hash payment_id = crypto::null_hash;
+ if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
+ message_writer(console_color_red, false) <<
+ tr("WARNING: this transaction uses an unencrypted payment ID: these are obsolete and ignored. Use subaddresses instead.");
}
if (unlock_time && !cryptonote::is_coinbase(tx))
message_writer() << tr("NOTE: This transaction is locked, see details with: show_transfer ") + epee::string_tools::pod_to_hex(txid);
@@ -5576,6 +5592,14 @@ boost::optional<epee::wipeable_string> simple_wallet::on_device_passphrase_reque
//----------------------------------------------------------------------------------------------------
void simple_wallet::on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money)
{
+ const uint64_t rfbh = m_wallet->get_refresh_from_block_height();
+ std::string err;
+ const uint64_t dh = m_wallet->get_daemon_blockchain_height(err);
+ if (err.empty() && rfbh > dh)
+ {
+ message_writer(console_color_yellow, false) << tr("The wallet's refresh-from-block-height setting is higher than the daemon's height: this may mean your wallet will skip over transactions");
+ }
+
// Key image sync after the first refresh
if (!m_wallet->get_account().get_device().has_tx_cold_sign() || m_wallet->get_account().get_device().has_ki_live_refresh()) {
return;
@@ -5845,8 +5869,14 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
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);
+ uint64_t idx = 0;
+ for (const auto &e: td.m_uses)
+ {
+ heights.push_back(e.first);
+ if (e.first < td.m_spent_height)
+ ++idx;
+ }
+ const std::pair<std::string, std::string> line = show_outputs_line(heights, blockchain_height, idx);
extra_string += std::string("\n ") + tr("Used at heights: ") + line.first + "\n " + line.second;
}
message_writer(td.m_spent ? console_color_magenta : console_color_green, false) <<
@@ -6015,7 +6045,7 @@ 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::pair<std::string, std::string> simple_wallet::show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_idx) const
{
std::stringstream ostr;
@@ -6023,7 +6053,7 @@ std::pair<std::string, std::string> simple_wallet::show_outputs_line(const std::
blockchain_height = std::max(blockchain_height, h);
for (size_t j = 0; j < heights.size(); ++j)
- ostr << (heights[j] == highlight_height ? " *" : " ") << heights[j];
+ ostr << (j == highlight_idx ? " *" : " ") << heights[j];
// visualize the distribution, using the code by moneroexamples onion-monero-viewer
const uint64_t resolution = 79;
@@ -6033,20 +6063,23 @@ std::pair<std::string, std::string> simple_wallet::show_outputs_line(const std::
uint64_t pos = (heights[j] * resolution) / blockchain_height;
ring_str[pos] = 'o';
}
- if (highlight_height < blockchain_height)
+ if (highlight_idx < heights.size() && heights[highlight_idx] < blockchain_height)
{
- uint64_t pos = (highlight_height * resolution) / blockchain_height;
+ uint64_t pos = (heights[highlight_idx] * 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)
+bool simple_wallet::process_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr, bool verbose)
{
uint32_t version;
if (!try_connect_to_daemon(false, &version))
+ {
+ fail_msg_writer() << tr("failed to connect to daemon");
return false;
+ }
// available for RPC version 1.4 or higher
if (version < MAKE_CORE_RPC_VERSION(1, 4))
return true;
@@ -6062,7 +6095,8 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
{
const cryptonote::transaction& tx = ptx_vector[n].tx;
const tools::wallet2::tx_construction_data& construction_data = ptx_vector[n].construction_data;
- ostr << boost::format(tr("\nTransaction %llu/%llu: txid=%s")) % (n + 1) % ptx_vector.size() % cryptonote::get_transaction_hash(tx);
+ if (verbose)
+ ostr << boost::format(tr("\nTransaction %llu/%llu: txid=%s")) % (n + 1) % ptx_vector.size() % cryptonote::get_transaction_hash(tx);
// for each input
std::vector<uint64_t> spent_key_height(tx.vin.size());
std::vector<crypto::hash> spent_key_txid (tx.vin.size());
@@ -6083,7 +6117,8 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
}
const cryptonote::tx_source_entry& source = *sptr;
- ostr << boost::format(tr("\nInput %llu/%llu (%s): amount=%s")) % (i + 1) % tx.vin.size() % epee::string_tools::pod_to_hex(in_key.k_image) % print_money(source.amount);
+ if (verbose)
+ ostr << boost::format(tr("\nInput %llu/%llu (%s): amount=%s")) % (i + 1) % tx.vin.size() % epee::string_tools::pod_to_hex(in_key.k_image) % print_money(source.amount);
// convert relative offsets of ring member keys into absolute offsets (indices) associated with the amount
std::vector<uint64_t> absolute_offsets = cryptonote::relative_output_offsets_to_absolute(in_key.key_offsets);
// get block heights from which those ring member keys originated
@@ -6095,6 +6130,7 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
req.outputs[j].index = absolute_offsets[j];
}
COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res);
+ req.get_txid = true;
req.client = cryptonote::make_rpc_payment_signature(m_wallet->get_rpc_client_secret_key());
bool r = m_wallet->invoke_http_bin("/get_outs.bin", req, res);
err = interpret_rpc_response(r, res.status);
@@ -6112,19 +6148,18 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
return false;
}
}
- ostr << tr("\nOriginating block heights: ");
+ if (verbose)
+ ostr << tr("\nOriginating block heights: ");
spent_key_height[i] = res.outs[source.real_output].height;
spent_key_txid [i] = res.outs[source.real_output].txid;
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)
{
heights[j] = res.outs[j].height;
- if (j == source.real_output)
- highlight_height = heights[j];
}
- std::pair<std::string, std::string> ring_str = show_outputs_line(heights, blockchain_height, highlight_height);
- ostr << ring_str.first << tr("\n|") << ring_str.second << tr("|\n");
+ std::pair<std::string, std::string> ring_str = show_outputs_line(heights, blockchain_height, source.real_output);
+ if (verbose)
+ 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;
@@ -6143,7 +6178,7 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
ostr
<< tr("\nWarning: Some input keys being spent are from ")
<< (are_keys_from_same_tx ? tr("the same transaction") : tr("blocks that are temporally very close"))
- << tr(", which can break the anonymity of ring signature. Make sure this is intentional!");
+ << tr(", which can break the anonymity of ring signatures. Make sure this is intentional!");
}
ostr << ENDL;
}
@@ -6198,7 +6233,7 @@ void simple_wallet::check_for_inactivity_lock(bool user)
m_in_command = true;
if (!user)
{
- const std::string speech = tr("I locked your Monero wallet to protect you while you were away");
+ const std::string speech = tr("I locked your Monero wallet to protect you while you were away\nsee \"help_advanced set\" to configure/disable");
std::vector<std::pair<std::string, size_t>> lines = tools::split_string_by_width(speech, 45);
size_t max_len = 0;
@@ -6584,11 +6619,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
float days = locked_blocks / 720.0f;
prompt << boost::format(tr(".\nThis transaction (including %s change) will unlock on block %llu, in approximately %s days (assuming 2 minutes per block)")) % cryptonote::print_money(change) % ((unsigned long long)unlock_block) % days;
}
- if (m_wallet->print_ring_members())
- {
- if (!print_ring_members(ptx_vector, prompt))
- return false;
- }
+ if (!process_ring_members(ptx_vector, prompt, m_wallet->print_ring_members()))
+ return false;
bool default_ring_size = true;
for (const auto &ptx: ptx_vector)
{
@@ -6852,7 +6884,12 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
std::set<uint32_t> subaddr_indices;
if (local_args.size() > 0 && local_args[0].substr(0, 6) == "index=")
{
- if (!parse_subaddress_indices(local_args[0], subaddr_indices))
+ if (local_args[0] == "index=all")
+ {
+ for (uint32_t i = 0; i < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++i)
+ subaddr_indices.insert(i);
+ }
+ else if (!parse_subaddress_indices(local_args[0], subaddr_indices))
{
print_usage();
return true;
@@ -7039,7 +7076,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
if (subaddr_indices.size() > 1)
prompt << tr("WARNING: Outputs of multiple addresses are being used together, which might potentially compromise your privacy.\n");
}
- if (m_wallet->print_ring_members() && !print_ring_members(ptx_vector, prompt))
+ if (!process_ring_members(ptx_vector, prompt, m_wallet->print_ring_members()))
return true;
if (ptx_vector.size() > 1) {
prompt << boost::format(tr("Sweeping %s in %llu transactions for a total fee of %s. Is this okay?")) %
@@ -7283,7 +7320,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
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))
+ if (!process_ring_members(ptx_vector, prompt, m_wallet->print_ring_members()))
return true;
prompt << boost::format(tr("Sweeping %s for a total fee of %s. Is this okay?")) %
print_money(total_sent) %
@@ -8365,7 +8402,7 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec
m_in_manual_refresh.store(true, std::memory_order_relaxed);
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
- std::vector<std::pair<cryptonote::transaction, bool>> process_txs;
+ std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_txs;
m_wallet->update_pool_state(process_txs);
if (!process_txs.empty())
m_wallet->process_pool_state(process_txs);
@@ -8795,6 +8832,8 @@ bool simple_wallet::rescan_blockchain(const std::vector<std::string> &args_)
}
}
+ m_in_manual_refresh.store(true, std::memory_order_relaxed);
+ epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
return refresh_main(start_height, reset_type, true);
}
//----------------------------------------------------------------------------------------------------
@@ -10028,7 +10067,7 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args)
try
{
- std::vector<std::pair<cryptonote::transaction, bool>> process_txs;
+ std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> process_txs;
m_wallet->update_pool_state(process_txs);
if (!process_txs.empty())
m_wallet->process_pool_state(process_txs);