From ff8825210d7c09eab044f51375a5bbb6ba2a28b1 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 22 Oct 2016 13:29:23 +0100 Subject: [PATCH] simplewallet: factor locked_transfer, and fix a few rough edges Factor locked_transfer into transfer_main, which brings various improvements for free (multiple addresses, proper detection of multiple payment ids, obeying the prompt settings). Also fix a few things, such as using uint64_t instead of int for block heights, actually checking whether getting blockchain height succeeded, etc. --- src/simplewallet/simplewallet.cpp | 299 +++++------------------------- 1 file changed, 43 insertions(+), 256 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index baa70bc0b..3f494f512 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -105,6 +105,7 @@ typedef cryptonote::simple_wallet sw; enum TransferType { TransferOriginal, TransferNew, + TransferLocked, }; namespace @@ -2364,7 +2365,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector extra; bool payment_id_seen = false; - if (1 == local_args.size() % 2) + bool expect_even = (transfer_type == TransferLocked); + if ((expect_even ? 0 : 1) == local_args.size() % 2) { std::string payment_id_str = local_args.back(); local_args.pop_back(); @@ -2405,6 +2408,26 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector(local_args.back()); + } + catch (const std::exception &e) + { + fail_msg_writer() << tr("bad locked_blocks parameter:") << " " << local_args.back(); + return true; + } + if (locked_blocks > 1000000) + { + fail_msg_writer() << tr("Locked blocks too high, max 1000000 (˜4 yrs)"); + return true; + } + local_args.pop_back(); + } + vector dsts; for (size_t i = 0; i < local_args.size(); i += 2) { @@ -2464,8 +2487,20 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector ptx_vector; + uint64_t bc_height, unlock_block = 0; + std::string err; switch (transfer_type) { + case TransferLocked: + bc_height = get_daemon_blockchain_height(err); + if (!err.empty()) + { + fail_msg_writer() << tr("failed to get blockchain height: ") << err; + return true; + } + unlock_block = bc_height + locked_blocks; + ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); + break; case TransferNew: ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); break; @@ -2507,6 +2542,11 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector &args_) return transfer_main(TransferNew, args_); } //---------------------------------------------------------------------------------------------------- - bool simple_wallet::locked_transfer(const std::vector &args_) { - if (!try_connect_to_daemon()) - return true; - - LOCK_IDLE_SCOPE(); - - std::vector local_args = args_; - - size_t fake_outs_count; - if(local_args.size() > 0) { - if(!epee::string_tools::get_xtype_from_string(fake_outs_count, local_args[0])) - { - fake_outs_count = m_wallet->default_mixin(); - if (fake_outs_count == 0) - fake_outs_count = DEFAULT_MIX; - } - else - { - local_args.erase(local_args.begin()); - } - } - - - - if(m_wallet->watch_only()) - { - fail_msg_writer() << tr("this is a watch only wallet"); - return true; - } - - int locked_blocks = 0; - std::vector extra; - bool payment_id_seen = false; - - if (2 < local_args.size() && local_args.size() < 5) - { - if (local_args.size() == 4) - { - std::string payment_id_str = local_args.back(); - local_args.pop_back(); - - crypto::hash payment_id; - bool r = tools::wallet2::parse_long_payment_id(payment_id_str, payment_id); - if(r) - { - std::string extra_nonce; - set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); - r = add_extra_nonce_to_tx_extra(extra, extra_nonce); - } - else - { - crypto::hash8 payment_id8; - r = tools::wallet2::parse_short_payment_id(payment_id_str, payment_id8); - if(r) - { - std::string extra_nonce; - set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id8); - r = add_extra_nonce_to_tx_extra(extra, extra_nonce); - } - } - payment_id_seen = true; - if(!r) - { - fail_msg_writer() << tr("payment id has invalid format, expected 16 or 64 character hex string: ") << payment_id_str; - return true; - } - - } - - locked_blocks = std::stoi( local_args.back() ); - if (locked_blocks > 1000000) { - fail_msg_writer() << tr("Locked blocks too high, max 1000000 (˜4 yrs)"); - return true; - } - local_args.pop_back(); - - } - else - { - fail_msg_writer() << tr("Wrong number of arguments, use: locked_transfer [] []"); - return true; - } - - vector dsts; - - cryptonote::tx_destination_entry de; - bool has_payment_id; - crypto::hash8 new_payment_id; - if (!get_address_from_str(local_args[0], de.addr, has_payment_id, new_payment_id)) - return true; - - bool ok = cryptonote::parse_amount(de.amount, local_args[1]); - if(!ok || 0 == de.amount) - { - fail_msg_writer() << tr("amount is wrong: ") << local_args[0] << ' ' << local_args[1] << - ", " << tr("expected number from 0 to ") << print_money(std::numeric_limits::max()); - return true; - } - - dsts.push_back(de); - - - try - { - // figure out what tx will be necessary - std::vector ptx_vector; - - std::string err; - int bc_height = get_daemon_blockchain_height(err); - int unlock_block = locked_blocks + bc_height; - ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block , 0 /* unused fee arg*/, extra, m_trusted_daemon); - - uint64_t total_fee = 0; - uint64_t dust_not_in_fee = 0; - uint64_t dust_in_fee = 0; - for (size_t n = 0; n < ptx_vector.size(); ++n) - { - total_fee += ptx_vector[n].fee; - - if (ptx_vector[n].dust_added_to_fee) - dust_in_fee += ptx_vector[n].dust; - else - dust_not_in_fee += ptx_vector[n].dust; - } - - std::stringstream prompt; - if (ptx_vector.size() > 1) - { - prompt << boost::format(tr("Your transaction needs to be split into %llu transactions. " - "This will result in a transaction fee being applied to each transaction, for a total fee of %s")) % - ((unsigned long long)ptx_vector.size()) % print_money(total_fee); - } - else - { - - prompt << boost::format(tr("Transaction fee is %s")) % - print_money(total_fee); - } - if (dust_in_fee != 0) prompt << boost::format(tr(", of which %s is dust from change")) % print_money(dust_in_fee); - if (dust_not_in_fee != 0) prompt << tr(".") << ENDL << boost::format(tr("A total of %s from dust change will be sent to dust address")) - % print_money(dust_not_in_fee); - - float days = (float)(locked_blocks) / 720.0; - prompt << boost::format(tr(".\nThe unlock time is approximately %s days")) % days; - - prompt << tr(".") << ENDL << tr("Is this okay? (Y/Yes/N/No)"); - - std::string accepted = command_line::input_line(prompt.str()); - if (std::cin.eof()) - return true; - if (accepted != "Y" && accepted != "y" && accepted != "Yes" && accepted != "yes") - { - fail_msg_writer() << tr("transaction cancelled."); - return true; - } - - // actually commit the transactions - while (!ptx_vector.empty()) - { - auto & ptx = ptx_vector.back(); - m_wallet->commit_tx(ptx); - success_msg_writer(true) << tr("Money successfully sent, transaction ") << get_transaction_hash(ptx.tx); - - // if no exception, remove element from vector - ptx_vector.pop_back(); - } - } - catch (const tools::error::daemon_busy&) - { - fail_msg_writer() << tr("daemon is busy. Please try again later."); - } - catch (const tools::error::no_connection_to_daemon&) - { - fail_msg_writer() << tr("no connection to daemon. Please make sure daemon is running."); - } - catch (const tools::error::wallet_rpc_error& e) - { - LOG_ERROR("RPC error: " << e.to_string()); - fail_msg_writer() << tr("RPC error: ") << e.what(); - } - catch (const tools::error::get_random_outs_error&) - { - fail_msg_writer() << tr("failed to get random outputs to mix"); - } - catch (const tools::error::not_enough_money& e) - { - LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, sent amount %s") % - print_money(e.available()) % - print_money(e.tx_amount())); - fail_msg_writer() << tr("Not enough money in unlocked balance"); - } - catch (const tools::error::tx_not_possible& e) - { - LOG_PRINT_L0(boost::format("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)") % - print_money(e.available()) % - print_money(e.tx_amount() + e.fee()) % - print_money(e.tx_amount()) % - print_money(e.fee())); - fail_msg_writer() << tr("Failed to find a way to create transactions. This is usually due to dust which is so small it cannot pay for itself in fees, or trying to send more money than the unlocked balance, or not leaving enough for fees"); - } - catch (const tools::error::not_enough_outs_to_mix& e) - { - auto writer = fail_msg_writer(); - writer << tr("not enough outputs for specified mixin_count") << " = " << e.mixin_count() << ":"; - for (std::pair outs_for_amount : e.scanty_outs()) - { - writer << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.first) << ", " << tr("found outputs to mix") << " = " << outs_for_amount.second; - } - } - catch (const tools::error::tx_not_constructed&) - { - fail_msg_writer() << tr("transaction was not constructed"); - } - catch (const tools::error::tx_rejected& e) - { - fail_msg_writer() << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status(); - std::string reason = e.reason(); - if (!reason.empty()) - fail_msg_writer() << tr("Reason: ") << reason; - } - catch (const tools::error::tx_sum_overflow& e) - { - fail_msg_writer() << e.what(); - } - catch (const tools::error::zero_destination&) - { - fail_msg_writer() << tr("one of destinations is zero"); - } - catch (const tools::error::tx_too_big& e) - { - fail_msg_writer() << tr("failed to find a suitable way to split transactions"); - } - catch (const tools::error::transfer_error& e) - { - LOG_ERROR("unknown transfer error: " << e.to_string()); - fail_msg_writer() << tr("unknown transfer error: ") << e.what(); - } - catch (const tools::error::wallet_internal_error& e) - { - LOG_ERROR("internal error: " << e.to_string()); - fail_msg_writer() << tr("internal error: ") << e.what(); - } - catch (const std::exception& e) - { - LOG_ERROR("unexpected error: " << e.what()); - fail_msg_writer() << tr("unexpected error: ") << e.what(); - } - catch (...) - { - LOG_ERROR("unknown error"); - fail_msg_writer() << tr("unknown error"); - } - - return true; + return transfer_main(TransferLocked, args_); } //----------------------------------------------------------------------------------------------------