diff options
author | Horia Mihai David <david.horiamihai@gmail.com> | 2021-01-11 22:20:12 +0100 |
---|---|---|
committer | hMihaiDavid <24855110+hMihaiDavid@users.noreply.github.com> | 2021-02-17 20:25:55 +0100 |
commit | 186271e5d1633964a37220e3b6c1aebb4f691fd9 (patch) | |
tree | dab728ded4b39ee6f85909c2ba7d21b75525eb63 | |
parent | Merge pull request #7285 (diff) | |
download | monero-186271e5d1633964a37220e3b6c1aebb4f691fd9.tar.xz |
monero-wallet-cli: Added command scan_tx
To implement this feature, the wallet2::scan_tx API was implemented.
-rw-r--r-- | src/simplewallet/simplewallet.cpp | 45 | ||||
-rw-r--r-- | src/simplewallet/simplewallet.h | 1 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 42 | ||||
-rw-r--r-- | src/wallet/wallet2.h | 2 |
4 files changed, 89 insertions, 1 deletions
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 0635520c6..dad4aab6c 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -282,6 +282,7 @@ namespace const char* USAGE_VERSION("version"); const char* USAGE_HELP("help [<command> | all]"); const char* USAGE_APROPOS("apropos <keyword> [<keyword> ...]"); + const char* USAGE_SCAN_TX("scan_tx <txid> [<txid> ...]"); std::string input_line(const std::string& prompt, bool yesno = false) { @@ -3214,6 +3215,45 @@ bool simple_wallet::apropos(const std::vector<std::string> &args) return true; } +bool simple_wallet::scan_tx(const std::vector<std::string> &args) +{ + if (args.empty()) + { + PRINT_USAGE(USAGE_SCAN_TX); + return true; + } + + // Parse and dedup args + std::unordered_set<crypto::hash> txids; + for (const auto &s : args) { + crypto::hash txid; + if (!epee::string_tools::hex_to_pod(s, txid)) { + fail_msg_writer() << tr("Invalid txid specified: ") << s; + return true; + } + txids.insert(txid); + } + std::vector<crypto::hash> txids_v(txids.begin(), txids.end()); + + if (!m_wallet->is_trusted_daemon()) { + message_writer(console_color_red, true) << tr("WARNING: this operation may reveal the txids to the remote node and affect your privacy"); + if (!command_line::is_yes(input_line("Do you want to continue?", true))) { + message_writer() << tr("You have canceled the operation"); + return true; + } + } + + LOCK_IDLE_SCOPE(); + m_in_manual_refresh.store(true); + try { + m_wallet->scan_tx(txids_v); + } catch (const std::exception &e) { + fail_msg_writer() << e.what(); + } + m_in_manual_refresh.store(false); + return true; +} + simple_wallet::simple_wallet() : m_allow_mismatched_daemon_version(false) , m_refresh_progress_reporter(*this) @@ -3763,6 +3803,10 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::on_command, this, &simple_wallet::apropos, _1), tr(USAGE_APROPOS), tr("Search all command descriptions for keyword(s)")); + m_cmd_binder.set_handler("scan_tx", + boost::bind(&simple_wallet::on_command, this, &simple_wallet::scan_tx, _1), + tr(USAGE_SCAN_TX), + tr("Scan the transactions given by <txid>(s), processing them and looking for outputs")); m_cmd_binder.set_unknown_command_handler(boost::bind(&simple_wallet::on_command, this, &simple_wallet::on_unknown_command, _1)); m_cmd_binder.set_empty_command_handler(boost::bind(&simple_wallet::on_empty_command, this)); m_cmd_binder.set_cancel_handler(boost::bind(&simple_wallet::on_cancelled_command, this)); @@ -11648,4 +11692,3 @@ bool simple_wallet::mms(const std::vector<std::string> &args) return true; } // End MMS ------------------------------------------------------------------------------------------------ - diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 61104c87f..af654e0dd 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -158,6 +158,7 @@ namespace cryptonote bool set_credits_target(const std::vector<std::string> &args = std::vector<std::string>()); bool help(const std::vector<std::string> &args = std::vector<std::string>()); bool apropos(const std::vector<std::string> &args); + bool scan_tx(const std::vector<std::string> &args); bool start_mining(const std::vector<std::string> &args); bool stop_mining(const std::vector<std::string> &args); bool set_daemon(const std::vector<std::string> &args); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index d80335db8..d2bcc04d6 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -30,6 +30,7 @@ #include <numeric> #include <tuple> +#include <queue> #include <boost/format.hpp> #include <boost/optional/optional.hpp> #include <boost/algorithm/string/classification.hpp> @@ -1602,6 +1603,47 @@ std::string wallet2::get_subaddress_label(const cryptonote::subaddress_index& in return m_subaddress_labels[index.major][index.minor]; } //---------------------------------------------------------------------------------------------------- +void wallet2::scan_tx(const std::vector<crypto::hash> &txids) +{ + // Get the transactions from daemon in batches and add them to a priority queue ordered in chronological order + auto cmp_tx_entry = [](const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::entry& l, const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::entry& r) + { return l.block_height > r.block_height; }; + + std::priority_queue<cryptonote::COMMAND_RPC_GET_TRANSACTIONS::entry, std::vector<COMMAND_RPC_GET_TRANSACTIONS::entry>, decltype(cmp_tx_entry)> txq(cmp_tx_entry); + const size_t SLICE_SIZE = 100; // RESTRICTED_TRANSACTIONS_COUNT as defined in rpc/core_rpc_server.cpp, hardcoded in daemon code + for(size_t slice = 0; slice < txids.size(); slice += SLICE_SIZE) { + cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req = AUTO_VAL_INIT(req); + cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res); + req.decode_as_json = false; + req.prune = true; + + size_t ntxes = slice + SLICE_SIZE > txids.size() ? txids.size() - slice : SLICE_SIZE; + for (size_t i = slice; i < slice + ntxes; ++i) + req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txids[i])); + + { + const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; + req.client = get_client_signature(); + bool r = epee::net_utils::invoke_http_json("/gettransactions", req, res, *m_http_client, rpc_timeout); + THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to get transaction from daemon"); + THROW_WALLET_EXCEPTION_IF(res.txs.size() != req.txs_hashes.size(), error::wallet_internal_error, "Failed to get transaction from daemon"); + } + + for (auto& tx_info : res.txs) + txq.push(tx_info); + } + + // Process the transactions in chronologically ascending order + while(!txq.empty()) { + auto& tx_info = txq.top(); + cryptonote::transaction tx; + crypto::hash tx_hash; + THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(tx_info, tx, tx_hash), error::wallet_internal_error, "Failed to get transaction from daemon (2)"); + process_new_transaction(tx_hash, tx, tx_info.output_indices, tx_info.block_height, 0, tx_info.block_timestamp, false, tx_info.in_pool, tx_info.double_spend_seen, {}, {}); + txq.pop(); + } +} +//---------------------------------------------------------------------------------------------------- void wallet2::set_subaddress_label(const cryptonote::subaddress_index& index, const std::string &label) { THROW_WALLET_EXCEPTION_IF(index.major >= m_subaddress_labels.size(), error::account_index_outofbound); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 68f03db72..1488c6976 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1264,6 +1264,8 @@ private: std::string get_spend_proof(const crypto::hash &txid, const std::string &message); bool check_spend_proof(const crypto::hash &txid, const std::string &message, const std::string &sig_str); + void scan_tx(const std::vector<crypto::hash> &txids); + /*! * \brief Generates a proof that proves the reserve of unspent funds * \param account_minreserve When specified, collect outputs only belonging to the given account and prove the smallest reserve above the given amount |