From cc6c013f638c53beae0edd316aa50a70373a90a9 Mon Sep 17 00:00:00 2001 From: wowario Date: Tue, 29 Jan 2019 10:08:14 +0300 Subject: [PATCH] simplewallet: add churn command --- src/simplewallet/simplewallet.cpp | 115 +++++++++++++++++++++++++++++- src/simplewallet/simplewallet.h | 1 + src/wallet/wallet2.cpp | 1 + src/wallet/wallet2.h | 3 + 4 files changed, 117 insertions(+), 3 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 3fe6ff22f..0440cb6b6 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -66,6 +66,9 @@ #include "version.h" #include #include "wallet/message_store.h" +#include +#include +#include #ifdef WIN32 #include @@ -3225,8 +3228,14 @@ simple_wallet::simple_wallet() tr("Show simplified help section.")); m_cmd_binder.set_handler("help_advanced", boost::bind(&simple_wallet::help_advanced, this, _1), - tr("help_advanced []"), - tr("Show the advanced help section or the documentation about a .")); + tr("help_advanced"), + tr("Show the help section or the documentation about a .")); + m_cmd_binder.set_handler("churn", + boost::bind(&simple_wallet::churn, this, _1), + tr("churn"), + tr("Churning can enhance privacy by increasing the distance of your funds from the source. This is especially\n" + "important if your funds come from an exchange or a pool that shares public mining data. Each churn also helps\n" + "create more decoy outputs, which is good for constructing stronger ring signatures.")); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::set_variable(const std::vector &args) @@ -4983,10 +4992,12 @@ void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, (m_long_payment_id_support ? tr("WARNING: this transaction uses an unencrypted payment ID: consider using subaddresses instead.") : tr("WARNING: this transaction uses an unencrypted payment ID: these are obsolete. Support will be withdrawn in the future. Use subaddresses instead.")); } } + if (m_wallet->auto_confirm_churn() == 0) { if (m_auto_refresh_refreshing) m_cmd_binder.print_prompt(); else m_refresh_progress_reporter.update(height, true); + } } //---------------------------------------------------------------------------------------------------- void simple_wallet::on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index) @@ -4996,15 +5007,18 @@ void simple_wallet::on_unconfirmed_money_received(uint64_t height, const crypto: //---------------------------------------------------------------------------------------------------- void simple_wallet::on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index) { + message_writer(console_color_magenta, false) << "\r" << tr("Height ") << height << ", " << tr("txid ") << txid << ", " << tr("spent ") << print_money(amount) << ", " << tr("idx ") << subaddr_index; + if (m_wallet->auto_confirm_churn() == 0) { if (m_auto_refresh_refreshing) m_cmd_binder.print_prompt(); else m_refresh_progress_reporter.update(height, true); + } } //---------------------------------------------------------------------------------------------------- void simple_wallet::on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) @@ -5210,6 +5224,7 @@ bool simple_wallet::show_balance_unlocked(bool detailed) extra = tr(" (Some owned outputs have partial key images - import_multisig_info needed)"); else if (m_wallet->has_unknown_key_images()) extra += tr(" (Some owned outputs have missing key images - import_key_images needed)"); + if (m_wallet->auto_confirm_churn() == 0) { success_msg_writer() << tr("Currently selected account: [") << m_current_subaddress_account << tr("] ") << m_wallet->get_subaddress_label({m_current_subaddress_account, 0}); const std::string tag = m_wallet->get_account_tags().second[m_current_subaddress_account]; success_msg_writer() << tr("Tag: ") << (tag.empty() ? std::string{tr("(No tag assigned)")} : tag); @@ -5219,7 +5234,7 @@ bool simple_wallet::show_balance_unlocked(bool detailed) if (blocks_to_unlock > 0) unlock_time_message = (boost::format(" (%lu block(s) to unlock)") % blocks_to_unlock).str(); success_msg_writer() << tr("Balance: ") << print_money(m_wallet->balance(m_current_subaddress_account)) << ", " - << tr("unlocked balance: ") << print_money(unlocked_balance) << unlock_time_message << extra; + << tr("unlocked balance: ") << print_money(unlocked_balance) << unlock_time_message << extra;} std::map balance_per_subaddress = m_wallet->balance_per_subaddress(m_current_subaddress_account); std::map> unlocked_balance_per_subaddress = m_wallet->unlocked_balance_per_subaddress(m_current_subaddress_account); if (!detailed || balance_per_subaddress.empty()) @@ -6410,6 +6425,8 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vectorget_address() == info.address; + // prompt is there is no payment id and confirmation is required if (m_long_payment_id_support && !payment_id_seen && m_wallet->confirm_missing_payment_id() && !info.is_subaddress && !is_wallet_address) { @@ -6438,6 +6455,9 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vectorauto_confirm_churn() == 0) + { uint64_t total_fee = 0, total_sent = 0; for (size_t n = 0; n < ptx_vector.size(); ++n) { @@ -6480,6 +6500,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vectormultisig()) @@ -9323,6 +9344,89 @@ bool simple_wallet::process_command(const std::vector &args) return m_cmd_binder.process_command_vec(args); } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::churn(const std::vector &args_) +{ + // Check if wallet is on MAINNET + if (m_wallet->nettype() != cryptonote::MAINNET) + { + fail_msg_writer() << tr("Churning is only possible on Mainnet, operation cancelled."); + return true; + } + + // Check balance + if (m_wallet->unlocked_balance(m_current_subaddress_account) < 2) + { + fail_msg_writer() << tr("You need to have an unlocked balance of more than 2 WOW, operation cancelled."); + return true; + } + + // Warning + std::string warning = input_line(tr("Warning: Churning requires your wallet to be open for a few hours. If wallet is left unattended, make sure your computer is in a secure location. Continue?")); + if (std::cin.eof() || !command_line::is_yes(warning)) + { + message_writer() << tr("Operation cancelled."); + return true; + } + + // Generate pseudo-random integers for the number of churns and interval pause + int churn_number, pause_time, i; + srand (time(NULL)); // seed RNG with current time + churn_number = rand()%(8 + 1 - 2) + 2; // churn between 2 to 8 times + pause_time = rand()%(120 + 1 - 60) + 60; // wait between 1 to 2 hours per interval + float total_time = static_cast(churn_number*pause_time)/60; // calculate how long churning will take in hours + + // Calculate fee + float churn_fee = m_wallet->use_fork_rules(HF_VERSION_PER_BYTE_FEE) ? static_cast(churn_number * 0.015) : static_cast(churn_number * 0.032654); // usual 2/2 tx fee + + // Prompt confirmation + printf ("----------------\nNumber of churns: %d\nTotal duration: %.2f hours\nEstimated cost: %.2f WOW\n----------------\n", churn_number, total_time, churn_fee); + std::string confirm_churn = input_line(tr("Continue?")); + if (std::cin.eof() || !command_line::is_yes(confirm_churn)) + { + message_writer() << tr("Operation cancelled."); + return true; + } + + // Get main address + std::vector local_args = args_; + std::string address_str; + address_str = m_wallet->get_account().get_public_address_str(m_wallet->nettype()); + local_args.push_back(address_str); + message_writer() << tr("Sweeping entire wallet balance to your main address:"); + message_writer(console_color_white, true) << address_str; + + // Disable wallet confirmations to allow for automation + m_wallet->auto_confirm_churn(1); + m_wallet->ask_password(tools::wallet2::AskPasswordNever); + m_wallet->always_confirm_transfers(0); + m_wallet->auto_refresh(0); + + // Loop sweep_all to main address for churn_number times with pause_time each interval + for (i = 1; i <= churn_number; ++i) { + refresh(local_args); + sweep_all(local_args); + message_writer(console_color_magenta, true) << tr("\n---------------- ") << i << tr(" out of ") << churn_number << tr(" churns ----------------"); + + if (i == churn_number) + { + success_msg_writer() << tr("\nChurning compete."); + + // Re-enable confirmations + m_wallet->auto_confirm_churn(0); + m_wallet->ask_password(tools::wallet2::AskPasswordOnAction); + m_wallet->always_confirm_transfers(1); + m_wallet->auto_refresh(1); + } + else + { + message_writer() << tr("\nInterval pause, please wait...\n"); + sleep(pause_time*60); + } + } + + return true; +} +//---------------------------------------------------------------------------------------------------- void simple_wallet::interrupt() { if (m_in_manual_refresh.load(std::memory_order_relaxed)) @@ -9355,9 +9459,14 @@ void simple_wallet::commit_or_save(std::vector& ptx_ } else { + if (m_wallet->auto_confirm_churn() == 0) { m_wallet->commit_tx(ptx); success_msg_writer(true) << tr("Transaction successfully submitted, transaction ") << txid << ENDL << tr("You can check its status by using the `show_transfers` command."); + } else { + m_wallet->commit_tx(ptx); + success_msg_writer(true) << tr("Churn successfully executed, transaction ") << txid; + } } // if no exception, remove element from vector ptx_vector.pop_back(); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 441d53727..10c16c431 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -147,6 +147,7 @@ namespace cryptonote bool set_device_name(const std::vector &args = std::vector()); bool help(const std::vector &args = std::vector()); bool help_advanced(const std::vector &args = std::vector()); + bool churn(const std::vector &args); bool start_mining(const std::vector &args); bool stop_mining(const std::vector &args); bool set_daemon(const std::vector &args); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 2f247489f..aa7b4ad7d 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1085,6 +1085,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_nettype(nettype), m_multisig_rounds_passed(0), m_always_confirm_transfers(true), + m_auto_confirm_churn(false), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index d101e87f5..8a802edf2 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1003,6 +1003,8 @@ private: bool always_confirm_transfers() const { return m_always_confirm_transfers; } void always_confirm_transfers(bool always) { m_always_confirm_transfers = always; } + bool auto_confirm_churn() const { return m_auto_confirm_churn; } + void auto_confirm_churn(bool always) { m_auto_confirm_churn = always; } bool print_ring_members() const { return m_print_ring_members; } void print_ring_members(bool value) { m_print_ring_members = value; } bool store_tx_info() const { return m_store_tx_info; } @@ -1468,6 +1470,7 @@ private: uint32_t m_multisig_rounds_passed; std::vector m_multisig_derivations; bool m_always_confirm_transfers; + bool m_auto_confirm_churn; bool m_print_ring_members; bool m_store_tx_info; /*!< request txkey to be returned in RPC, and store in the wallet cache file */ uint32_t m_default_mixin;