Merge pull request #5333

18faa6da wallet: add freeze/thaw/frozen commands (moneromooo-monero)
This commit is contained in:
Riccardo Spagni 2019-04-01 17:36:15 +02:00
commit 286dec012d
No known key found for this signature in database
GPG Key ID: 55432DF31CCD4FCD
4 changed files with 188 additions and 16 deletions

View File

@ -239,6 +239,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>]");
@ -2027,6 +2030,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 << ")";
@ -2602,7 +2673,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),
@ -3014,6 +3085,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),
@ -4989,7 +5072,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 %

View File

@ -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
{

View File

@ -1441,6 +1441,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();
@ -1858,6 +1910,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;
@ -3205,8 +3258,9 @@ void wallet2::detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, ui
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);
}
}
@ -5391,7 +5445,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())
@ -5420,7 +5474,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())
@ -8314,7 +8368,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);
@ -8329,13 +8383,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
@ -8562,6 +8616,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);
@ -9036,7 +9091,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; };
@ -9516,7 +9571,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)
@ -9564,7 +9619,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);
@ -9902,6 +9957,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))
@ -9917,7 +9974,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;
@ -10043,7 +10100,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);
}
}
@ -10834,7 +10891,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);
}
@ -11581,6 +11638,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;
@ -11592,6 +11651,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;

View File

@ -266,6 +266,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;
@ -291,6 +292,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)
@ -1246,6 +1248,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; };
@ -1328,6 +1338,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);
@ -1482,7 +1493,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)
@ -1544,6 +1555,10 @@ namespace boost
{
x.m_key_image_request = false;
}
if (ver < 12)
{
x.m_frozen = false;
}
}
template <class Archive>
@ -1632,8 +1647,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>