diff options
author | moneromooo-monero <moneromooo-monero@users.noreply.github.com> | 2019-03-13 21:51:41 +0000 |
---|---|---|
committer | moneromooo-monero <moneromooo-monero@users.noreply.github.com> | 2019-03-22 09:19:24 +0000 |
commit | 18faa6da0c868720d78d985e64cd60b30932b5c7 (patch) | |
tree | ebcdd15b491bf00801d11c65f973d8fd172dcf78 | |
parent | Merge pull request #5267 (diff) | |
download | monero-18faa6da0c868720d78d985e64cd60b30932b5c7.tar.xz |
wallet: add freeze/thaw/frozen commands
These commands let one freeze outputs by key image, so they
do not appear in balance, nor are considered when creating
a transaction, etc
This is helpful when receiving an output from a suspected spy,
who might try to track your other outputs by seeing with what
other outputs it gets spent.
The frozen command may be used without parameters to list all
currently frozen outputs.
-rw-r--r-- | src/simplewallet/simplewallet.cpp | 87 | ||||
-rw-r--r-- | src/simplewallet/simplewallet.h | 6 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 85 | ||||
-rw-r--r-- | src/wallet/wallet2.h | 26 |
4 files changed, 188 insertions, 16 deletions
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index f390b7675..c839a186a 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -237,6 +237,9 @@ namespace const char* USAGE_MARK_OUTPUT_SPENT("mark_output_spent <amount>/<offset> | <filename> [add]"); const char* USAGE_MARK_OUTPUT_UNSPENT("mark_output_unspent <amount>/<offset>"); const char* USAGE_IS_OUTPUT_SPENT("is_output_spent <amount>/<offset>"); + const char* USAGE_FREEZE("freeze <key_image>"); + const char* USAGE_THAW("thaw <key_image>"); + const char* USAGE_FROZEN("frozen <key_image>"); const char* USAGE_VERSION("version"); const char* USAGE_HELP("help [<command>]"); @@ -2025,6 +2028,74 @@ bool simple_wallet::save_known_rings(const std::vector<std::string> &args) return true; } +bool simple_wallet::freeze_thaw(const std::vector<std::string> &args, bool freeze) +{ + if (args.empty()) + { + fail_msg_writer() << boost::format(tr("usage: %s <key_image>|<pubkey>")) % (freeze ? "freeze" : "thaw"); + return true; + } + crypto::key_image ki; + if (!epee::string_tools::hex_to_pod(args[0], ki)) + { + fail_msg_writer() << tr("failed to parse key image"); + return true; + } + try + { + if (freeze) + m_wallet->freeze(ki); + else + m_wallet->thaw(ki); + } + catch (const std::exception &e) + { + fail_msg_writer() << e.what(); + return true; + } + + return true; +} + +bool simple_wallet::freeze(const std::vector<std::string> &args) +{ + return freeze_thaw(args, true); +} + +bool simple_wallet::thaw(const std::vector<std::string> &args) +{ + return freeze_thaw(args, false); +} + +bool simple_wallet::frozen(const std::vector<std::string> &args) +{ + if (args.empty()) + { + size_t ntd = m_wallet->get_num_transfer_details(); + for (size_t i = 0; i < ntd; ++i) + { + if (!m_wallet->frozen(i)) + continue; + const tools::wallet2::transfer_details &td = m_wallet->get_transfer_details(i); + message_writer() << tr("Frozen: ") << td.m_key_image << " " << cryptonote::print_money(td.amount()); + } + } + else + { + crypto::key_image ki; + if (!epee::string_tools::hex_to_pod(args[0], ki)) + { + fail_msg_writer() << tr("failed to parse key image"); + return true; + } + if (m_wallet->frozen(ki)) + message_writer() << tr("Frozen: ") << ki; + else + message_writer() << tr("Not frozen: ") << ki; + } + return true; +} + bool simple_wallet::version(const std::vector<std::string> &args) { message_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; @@ -2600,7 +2671,7 @@ simple_wallet::simple_wallet() tr(USAGE_INCOMING_TRANSFERS), tr("Show the incoming transfers, all or filtered by availability and address index.\n\n" "Output format:\n" - "Amount, Spent(\"T\"|\"F\"), \"locked\"|\"unlocked\", RingCT, Global Index, Transaction Hash, Address Index, [Public Key, Key Image] ")); + "Amount, Spent(\"T\"|\"F\"), \"frozen\"|\"locked\"|\"unlocked\", RingCT, Global Index, Transaction Hash, Address Index, [Public Key, Key Image] ")); m_cmd_binder.set_handler("payments", boost::bind(&simple_wallet::show_payments, this, _1), tr(USAGE_PAYMENTS), @@ -3012,6 +3083,18 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::blackballed, this, _1), tr(USAGE_IS_OUTPUT_SPENT), tr("Checks whether an output is marked as spent")); + m_cmd_binder.set_handler("freeze", + boost::bind(&simple_wallet::freeze, this, _1), + tr(USAGE_FREEZE), + tr("Freeze a single output by key image so it will not be used")); + m_cmd_binder.set_handler("thaw", + boost::bind(&simple_wallet::thaw, this, _1), + tr(USAGE_THAW), + tr("Thaw a single output by key image so it may be used again")); + m_cmd_binder.set_handler("frozen", + boost::bind(&simple_wallet::frozen, this, _1), + tr(USAGE_FROZEN), + tr("Checks whether a given output is currently frozen by key image")); m_cmd_binder.set_handler("version", boost::bind(&simple_wallet::version, this, _1), tr(USAGE_VERSION), @@ -4987,7 +5070,7 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args boost::format("%21s%8s%12s%8s%16u%68s%16u%s") % print_money(td.amount()) % (td.m_spent ? tr("T") : tr("F")) % - (m_wallet->is_transfer_unlocked(td) ? tr("unlocked") : tr("locked")) % + (m_wallet->frozen(td) ? tr("[frozen]") : m_wallet->is_transfer_unlocked(td) ? tr("unlocked") : tr("locked")) % (td.is_rct() ? tr("RingCT") : tr("-")) % td.m_global_output_index % td.m_txid % diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 7bcb92190..c9a5c55e8 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -238,9 +238,12 @@ namespace cryptonote bool blackball(const std::vector<std::string>& args); bool unblackball(const std::vector<std::string>& args); bool blackballed(const std::vector<std::string>& args); + bool freeze(const std::vector<std::string>& args); + bool thaw(const std::vector<std::string>& args); + bool frozen(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); + 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); bool ask_wallet_create_if_needed(); @@ -253,6 +256,7 @@ namespace cryptonote void key_images_sync_intern(); void on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money); std::pair<std::string, std::string> show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_height = std::numeric_limits<uint64_t>::max()) const; + bool freeze_thaw(const std::vector<std::string>& args, bool freeze); struct transfer_view { diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index a46da42db..47f9b6de8 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1392,6 +1392,58 @@ void wallet2::set_unspent(size_t idx) td.m_spent_height = 0; } //---------------------------------------------------------------------------------------------------- +void wallet2::freeze(size_t idx) +{ + CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid transfer_details index"); + transfer_details &td = m_transfers[idx]; + td.m_frozen = true; +} +//---------------------------------------------------------------------------------------------------- +void wallet2::thaw(size_t idx) +{ + CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid transfer_details index"); + transfer_details &td = m_transfers[idx]; + td.m_frozen = false; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::frozen(size_t idx) const +{ + CHECK_AND_ASSERT_THROW_MES(idx < m_transfers.size(), "Invalid transfer_details index"); + const transfer_details &td = m_transfers[idx]; + return td.m_frozen; +} +//---------------------------------------------------------------------------------------------------- +void wallet2::freeze(const crypto::key_image &ki) +{ + freeze(get_transfer_details(ki)); +} +//---------------------------------------------------------------------------------------------------- +void wallet2::thaw(const crypto::key_image &ki) +{ + thaw(get_transfer_details(ki)); +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::frozen(const crypto::key_image &ki) const +{ + return frozen(get_transfer_details(ki)); +} +//---------------------------------------------------------------------------------------------------- +size_t wallet2::get_transfer_details(const crypto::key_image &ki) const +{ + for (size_t idx = 0; idx < m_transfers.size(); ++idx) + { + const transfer_details &td = m_transfers[idx]; + if (td.m_key_image_known && td.m_key_image == ki) + return idx; + } + CHECK_AND_ASSERT_THROW_MES(false, "Key image not found"); +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::frozen(const transfer_details &td) const +{ + return td.m_frozen; +} +//---------------------------------------------------------------------------------------------------- void wallet2::check_acc_out_precomp(const tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, tx_scan_info_t &tx_scan_info) const { hw::device &hwdev = m_account.get_device(); @@ -1809,6 +1861,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote td.m_mask = rct::identity(); td.m_rct = false; } + td.m_frozen = false; set_unspent(m_transfers.size()-1); if (td.m_key_image_known) m_key_images[td.m_key_image] = m_transfers.size()-1; @@ -3156,8 +3209,9 @@ void wallet2::detach_blockchain(uint64_t height) wallet2::transfer_details &td = m_transfers[i]; if (td.m_spent && td.m_spent_height >= height) { - LOG_PRINT_L1("Resetting spent status for output " << i << ": " << td.m_key_image); + LOG_PRINT_L1("Resetting spent/frozen status for output " << i << ": " << td.m_key_image); set_unspent(i); + thaw(i); } } @@ -5331,7 +5385,7 @@ std::map<uint32_t, uint64_t> wallet2::balance_per_subaddress(uint32_t index_majo std::map<uint32_t, uint64_t> amount_per_subaddr; for (const auto& td: m_transfers) { - if (td.m_subaddr_index.major == index_major && !td.m_spent) + if (td.m_subaddr_index.major == index_major && !td.m_spent && !td.m_frozen) { auto found = amount_per_subaddr.find(td.m_subaddr_index.minor); if (found == amount_per_subaddr.end()) @@ -5360,7 +5414,7 @@ std::map<uint32_t, uint64_t> wallet2::unlocked_balance_per_subaddress(uint32_t i std::map<uint32_t, uint64_t> amount_per_subaddr; for(const transfer_details& td: m_transfers) { - if(td.m_subaddr_index.major == index_major && !td.m_spent && is_transfer_unlocked(td)) + if(td.m_subaddr_index.major == index_major && !td.m_spent && !td.m_frozen && is_transfer_unlocked(td)) { auto found = amount_per_subaddr.find(td.m_subaddr_index.minor); if (found == amount_per_subaddr.end()) @@ -8264,7 +8318,7 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; - if (!td.m_spent && td.is_rct() && td.amount() >= needed_money && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1) + if (!td.m_spent && !td.m_frozen && td.is_rct() && td.amount() >= needed_money && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1) { LOG_PRINT_L2("We can use " << i << " alone: " << print_money(td.amount())); picks.push_back(i); @@ -8279,13 +8333,13 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; - if (!td.m_spent && !td.m_key_image_partial && td.is_rct() && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1) + if (!td.m_spent && !td.m_frozen && !td.m_key_image_partial && td.is_rct() && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1) { LOG_PRINT_L2("Considering input " << i << ", " << print_money(td.amount())); for (size_t j = i + 1; j < m_transfers.size(); ++j) { const transfer_details& td2 = m_transfers[j]; - if (!td2.m_spent && !td.m_key_image_partial && td2.is_rct() && td.amount() + td2.amount() >= needed_money && is_transfer_unlocked(td2) && td2.m_subaddr_index == td.m_subaddr_index) + if (!td2.m_spent && !td2.m_frozen && !td.m_key_image_partial && td2.is_rct() && td.amount() + td2.amount() >= needed_money && is_transfer_unlocked(td2) && td2.m_subaddr_index == td.m_subaddr_index) { // update our picks if those outputs are less related than any we // already found. If the same, don't update, and oldest suitable outputs @@ -8512,6 +8566,7 @@ void wallet2::light_wallet_get_unspent_outs() td.m_pk_index = 0; td.m_internal_output_index = o.index; td.m_spent = spent; + td.m_frozen = false; tx_out txout; txout.target = txout_to_key(public_key); @@ -8986,7 +9041,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below threshold " << print_money(fractional_threshold)); continue; } - if (!td.m_spent && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1) + if (!td.m_spent && !td.m_frozen && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1) { const uint32_t index_minor = td.m_subaddr_index.minor; auto find_predicate = [&index_minor](const std::pair<uint32_t, std::vector<size_t>>& x) { return x.first == index_minor; }; @@ -9466,7 +9521,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; - if (!td.m_spent && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && (subaddr_indices.empty() || subaddr_indices.count(td.m_subaddr_index.minor) == 1)) + if (!td.m_spent && !td.m_frozen && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && (subaddr_indices.empty() || subaddr_indices.count(td.m_subaddr_index.minor) == 1)) { fund_found = true; if (below == 0 || td.amount() < below) @@ -9514,7 +9569,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_single(const crypt for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; - if (td.m_key_image_known && td.m_key_image == ki && !td.m_spent && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td)) + if (td.m_key_image_known && td.m_key_image == ki && !td.m_spent && !td.m_frozen && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td)) { if (td.is_rct() || is_valid_decomposed_amount(td.amount())) unused_transfers_indices.push_back(i); @@ -9846,6 +9901,8 @@ std::vector<size_t> wallet2::select_available_outputs(const std::function<bool(c { if (i->m_spent) continue; + if (i->m_frozen) + continue; if (i->m_key_image_partial) continue; if (!is_transfer_unlocked(*i)) @@ -9861,7 +9918,7 @@ std::vector<uint64_t> wallet2::get_unspent_amounts_vector() const std::set<uint64_t> set; for (const auto &td: m_transfers) { - if (!td.m_spent) + if (!td.m_spent && !td.m_frozen) set.insert(td.is_rct() ? 0 : td.amount()); } std::vector<uint64_t> vector; @@ -9987,7 +10044,7 @@ void wallet2::discard_unmixable_outputs() std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs(); for (size_t idx : unmixable_outputs) { - m_transfers[idx].m_spent = true; + freeze(idx); } } @@ -10770,7 +10827,7 @@ std::string wallet2::get_reserve_proof(const boost::optional<std::pair<uint32_t, for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details &td = m_transfers[i]; - if (!td.m_spent && (!account_minreserve || account_minreserve->first == td.m_subaddr_index.major)) + if (!td.m_spent && !td.m_frozen && (!account_minreserve || account_minreserve->first == td.m_subaddr_index.major)) selected_transfers.push_back(i); } @@ -11517,6 +11574,8 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag for(size_t i = 0; i < offset; ++i) { const transfer_details &td = m_transfers[i]; + if (td.m_frozen) + continue; uint64_t amount = td.amount(); if (td.m_spent) spent += amount; @@ -11528,6 +11587,8 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag for(size_t i = 0; i < signed_key_images.size(); ++i) { const transfer_details &td = m_transfers[i + offset]; + if (td.m_frozen) + continue; uint64_t amount = td.amount(); if (td.m_spent) spent += amount; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index c25450fb3..9e3f1dd20 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -265,6 +265,7 @@ namespace tools size_t m_internal_output_index; uint64_t m_global_output_index; bool m_spent; + bool m_frozen; uint64_t m_spent_height; crypto::key_image m_key_image; //TODO: key_image stored twice :( rct::key m_mask; @@ -290,6 +291,7 @@ namespace tools FIELD(m_internal_output_index) FIELD(m_global_output_index) FIELD(m_spent) + FIELD(m_frozen) FIELD(m_spent_height) FIELD(m_key_image) FIELD(m_mask) @@ -1243,6 +1245,14 @@ namespace tools bool unblackball_output(const std::pair<uint64_t, uint64_t> &output); bool is_output_blackballed(const std::pair<uint64_t, uint64_t> &output) const; + void freeze(size_t idx); + void thaw(size_t idx); + bool frozen(size_t idx) const; + void freeze(const crypto::key_image &ki); + void thaw(const crypto::key_image &ki); + bool frozen(const crypto::key_image &ki) const; + bool frozen(const transfer_details &td) const; + // MMS ------------------------------------------------------------------------------------------------- mms::message_store& get_message_store() { return m_message_store; }; const mms::message_store& get_message_store() const { return m_message_store; }; @@ -1325,6 +1335,7 @@ namespace tools bool get_ring(const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector<uint64_t> &outs); crypto::chacha_key get_ringdb_key(); void setup_keys(const epee::wipeable_string &password); + size_t get_transfer_details(const crypto::key_image &ki) const; void register_devices(); hw::device& lookup_device(const std::string & device_descriptor); @@ -1479,7 +1490,7 @@ namespace tools }; } BOOST_CLASS_VERSION(tools::wallet2, 28) -BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 11) +BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 12) BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1) BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0) BOOST_CLASS_VERSION(tools::wallet2::multisig_tx_set, 1) @@ -1541,6 +1552,10 @@ namespace boost { x.m_key_image_request = false; } + if (ver < 12) + { + x.m_frozen = false; + } } template <class Archive> @@ -1629,8 +1644,17 @@ namespace boost } a & x.m_key_image_request; if (ver < 11) + { + initialize_transfer_details(a, x, ver); return; + } a & x.m_uses; + if (ver < 12) + { + initialize_transfer_details(a, x, ver); + return; + } + a & x.m_frozen; } template <class Archive> |