From 9b8a06203b887c1003070ca2fc5d750cd6c237d6 Mon Sep 17 00:00:00 2001 From: Oyvind Kvanes Date: Wed, 28 Sep 2016 11:01:20 +0200 Subject: [PATCH 1/8] Make a small test change --- src/simplewallet/simplewallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 279a5fa41..936523875 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -835,7 +835,7 @@ bool simple_wallet::ask_wallet_create_if_needed() bool valid_path = false; do { wallet_path = command_line::input_line( - tr("Specify wallet file name (e.g., MyWallet). If the wallet doesn't exist, it will be created.\n" + tr("NOTE: This is my Wallet- Specify wallet file name (e.g., MyWallet). If the wallet doesn't exist, it will be created.\n" "Wallet file name: ") ); if (std::cin.eof()) From e5e6d8865504e51c9108ab629c777f7ec968f607 Mon Sep 17 00:00:00 2001 From: Oyvind Kvanes Date: Wed, 28 Sep 2016 13:04:26 +0200 Subject: [PATCH 2/8] Add more information to transaction in wallet --- src/simplewallet/simplewallet.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 936523875..1641b81a3 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2484,12 +2484,19 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector Date: Wed, 28 Sep 2016 14:58:14 +0200 Subject: [PATCH 3/8] Add motifications to test out locked_transfer --- src/simplewallet/simplewallet.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 1641b81a3..906af2283 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -652,6 +652,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), tr("Show blockchain height")); m_cmd_binder.set_handler("transfer_original", boost::bind(&simple_wallet::transfer, this, _1), tr("transfer [] [ ... ] [payment_id] - Transfer ,... to ,... , respectively. is the number of extra inputs to include for untraceability (from 0 to maximum available)")); m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer_new, this, _1), tr("Same as transfer_original, but using a new transaction building algorithm")); + m_cmd_binder.set_handler("locked_transfer", boost::bind(&simple_wallet::transfer_new, this, _1), tr("Make a transfer using unlock_time")); m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with mixin 0")); m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr("Send all unlocked balance an address")); m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), tr("set_log - Change current log detail level, <0-4>")); @@ -2318,7 +2319,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector extra; bool payment_id_seen = false; if (1 == local_args.size() % 2) @@ -2345,13 +2346,19 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector dsts; @@ -2399,12 +2406,12 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vectorcreate_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); + ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, given_unlock_time /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); break; default: LOG_ERROR("Unknown transfer method, using original"); case TransferOriginal: - ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); + ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, given_unlock_time /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); break; } @@ -2433,8 +2440,13 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector Date: Wed, 28 Sep 2016 15:20:37 +0200 Subject: [PATCH 4/8] Revert transfer_main in simplewallet --- src/simplewallet/simplewallet.cpp | 300 +++++++++++++++++++++++++++--- src/simplewallet/simplewallet.h | 1 + 2 files changed, 279 insertions(+), 22 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 906af2283..d7218545c 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -652,7 +652,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), tr("Show blockchain height")); m_cmd_binder.set_handler("transfer_original", boost::bind(&simple_wallet::transfer, this, _1), tr("transfer [] [ ... ] [payment_id] - Transfer ,... to ,... , respectively. is the number of extra inputs to include for untraceability (from 0 to maximum available)")); m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer_new, this, _1), tr("Same as transfer_original, but using a new transaction building algorithm")); - m_cmd_binder.set_handler("locked_transfer", boost::bind(&simple_wallet::transfer_new, this, _1), tr("Make a transfer using unlock_time")); + m_cmd_binder.set_handler("locked_transfer", boost::bind(&simple_wallet::locked_transfer, this, _1), tr("Make a transfer using unlock_time")); m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with mixin 0")); m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr("Send all unlocked balance an address")); m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), tr("set_log - Change current log detail level, <0-4>")); @@ -2314,6 +2314,280 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vectorwatch_only()) + { + fail_msg_writer() << tr("this is a watch only wallet"); + return true; + } + + std::vector extra; + bool payment_id_seen = false; + if (1 == local_args.size() % 2) + { + 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); + } + } + + if(!r) + { + fail_msg_writer() << tr("payment id has invalid format, expected 16 or 64 character hex string: ") << payment_id_str; + return true; + } + payment_id_seen = true; + } + + vector dsts; + for (size_t i = 0; i < local_args.size(); i += 2) + { + cryptonote::tx_destination_entry de; + bool has_payment_id; + crypto::hash8 new_payment_id; + if (!get_address_from_str(local_args[i], de.addr, has_payment_id, new_payment_id)) + return true; + + if (has_payment_id) + { + if (payment_id_seen) + { + fail_msg_writer() << tr("a single transaction cannot use more than one payment id: ") << local_args[i]; + return true; + } + + std::string extra_nonce; + set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, new_payment_id); + bool r = add_extra_nonce_to_tx_extra(extra, extra_nonce); + if(!r) + { + fail_msg_writer() << tr("failed to set up payment id, though it was decoded correctly"); + return true; + } + } + + bool ok = cryptonote::parse_amount(de.amount, local_args[i + 1]); + if(!ok || 0 == de.amount) + { + fail_msg_writer() << tr("amount is wrong: ") << local_args[i] << ' ' << local_args[i + 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; + switch (transfer_type) + { + 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; + default: + LOG_ERROR("Unknown transfer method, using original"); + case TransferOriginal: + ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); + break; + } + + // if more than one tx necessary, prompt user to confirm + if (m_wallet->always_confirm_transfers() || ptx_vector.size() > 1) + { + 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("The 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); + 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."); + + // would like to return false, because no tx made, but everything else returns true + // and I don't know what returning false might adversely affect. *sigh* + 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, 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"); + } + 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; +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::transfer(const std::vector &args_) +{ + return transfer_main(TransferOriginal, args_); +} +//---------------------------------------------------------------------------------------------------- +bool simple_wallet::transfer_new(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(local_args.size() < 2) + { + fail_msg_writer() << tr("wrong number of arguments"); + return true; + } + if(m_wallet->watch_only()) { fail_msg_writer() << tr("this is a watch only wallet"); @@ -2403,17 +2677,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector ptx_vector; - switch (transfer_type) - { - case TransferNew: - ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, given_unlock_time /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); - break; - default: - LOG_ERROR("Unknown transfer method, using original"); - case TransferOriginal: - ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, given_unlock_time /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); - break; - } + ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, given_unlock_time /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); + // if more than one tx necessary, prompt user to confirm if (m_wallet->always_confirm_transfers() || ptx_vector.size() > 1) @@ -2566,16 +2831,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector &args_) -{ - return transfer_main(TransferOriginal, args_); -} -//---------------------------------------------------------------------------------------------------- -bool simple_wallet::transfer_new(const std::vector &args_) -{ - return transfer_main(TransferNew, args_); -} -//---------------------------------------------------------------------------------------------------- + bool simple_wallet::sweep_unmixable(const std::vector &args_) { if (!try_connect_to_daemon()) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 6eb18ed9a..dcf89365c 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -123,6 +123,7 @@ namespace cryptonote bool transfer_main(int transfer_type, const std::vector &args); bool transfer(const std::vector &args); bool transfer_new(const std::vector &args); + bool locked_transfer(const std::vector &args); bool sweep_all(const std::vector &args); bool sweep_unmixable(const std::vector &args); std::vector> split_amounts( From 7d020bde5e0ef9d680c51a32b554cec6c29a4302 Mon Sep 17 00:00:00 2001 From: Oyvind Kvanes Date: Wed, 28 Sep 2016 16:27:45 +0200 Subject: [PATCH 5/8] Add locked_transfer --- src/simplewallet/simplewallet.cpp | 172 +++++++++++++++--------------- 1 file changed, 84 insertions(+), 88 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index d7218545c..3fe9b9091 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -652,7 +652,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), tr("Show blockchain height")); m_cmd_binder.set_handler("transfer_original", boost::bind(&simple_wallet::transfer, this, _1), tr("transfer [] [ ... ] [payment_id] - Transfer ,... to ,... , respectively. is the number of extra inputs to include for untraceability (from 0 to maximum available)")); m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer_new, this, _1), tr("Same as transfer_original, but using a new transaction building algorithm")); - m_cmd_binder.set_handler("locked_transfer", boost::bind(&simple_wallet::locked_transfer, this, _1), tr("Make a transfer using unlock_time")); + m_cmd_binder.set_handler("locked_transfer", boost::bind(&simple_wallet::locked_transfer, this, _1), tr("transfer [] (Number of blocks to lock the transaction for) [payment_id]")); m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with mixin 0")); m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr("Send all unlocked balance an address")); m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), tr("set_log - Change current log detail level, <0-4>")); @@ -836,7 +836,7 @@ bool simple_wallet::ask_wallet_create_if_needed() bool valid_path = false; do { wallet_path = command_line::input_line( - tr("NOTE: This is my Wallet- Specify wallet file name (e.g., MyWallet). If the wallet doesn't exist, it will be created.\n" + tr("Specify wallet file name (e.g., MyWallet). If the wallet doesn't exist, it will be created.\n" "Wallet file name: ") ); if (std::cin.eof()) @@ -2582,7 +2582,7 @@ bool simple_wallet::locked_transfer(const std::vector &args_) } } - if(local_args.size() < 2) + if(local_args.size() < 3 ) { fail_msg_writer() << tr("wrong number of arguments"); return true; @@ -2593,50 +2593,56 @@ bool simple_wallet::locked_transfer(const std::vector &args_) fail_msg_writer() << tr("this is a watch only wallet"); return true; } - int given_unlock_time = 10; + int given_unlock_time = 0; std::vector extra; bool payment_id_seen = false; - if (1 == local_args.size() % 2) + if (local_args.size() < 5) { - std::string payment_id_str = local_args.back(); - local_args.pop_back(); + 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); + crypto::hash payment_id; + bool r = tools::wallet2::parse_long_payment_id(payment_id_str, payment_id); if(r) { std::string extra_nonce; - set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id8); + 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; + } + } - 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; + //std::string unlock_time_string = local_args.back(); + given_unlock_time = std::stoi( local_args.back() ); + local_args.pop_back(); - std::string err; - int bc_height = get_daemon_blockchain_height(err); - - given_unlock_time = bc_height + std::stoi(payment_id_str); - //return true; - payment_id_seen = false; - } - + } + else + { + fail_msg_writer() << tr("wrong number of arguments"); + return true; } vector dsts; - for (size_t i = 0; i < local_args.size(); i += 2) + for (size_t i = 0; i < 1; i += 2) { cryptonote::tx_destination_entry de; bool has_payment_id; @@ -2677,60 +2683,57 @@ bool simple_wallet::locked_transfer(const std::vector &args_) { // figure out what tx will be necessary std::vector ptx_vector; - ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, given_unlock_time /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon); + std::string err; + int bc_height = get_daemon_blockchain_height(err); + int unlock_time = given_unlock_time + bc_height; + ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_time , 0 /* unused fee arg*/, extra, m_trusted_daemon); - // if more than one tx necessary, prompt user to confirm - if (m_wallet->always_confirm_transfers() || ptx_vector.size() > 1) + 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) { - 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; + 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 - { - std::string err; - int bc_height = get_daemon_blockchain_height(err); - float days = (float)(given_unlock_time - bc_height) / 720.0; - prompt << boost::format(tr("The transaction fee is %s")) % - print_money(total_fee); - prompt << boost::format(tr("The unlock time is approximately %s days")) % - days; - } - 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); - 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."); - - // would like to return false, because no tx made, but everything else returns true - // and I don't know what returning false might adversely affect. *sigh* - return true; - } + 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)(given_unlock_time) / 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()) { @@ -2761,18 +2764,11 @@ bool simple_wallet::locked_transfer(const std::vector &args_) } catch (const tools::error::not_enough_money& e) { - std::stringstream prompt; 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())); - prompt << boost::format(tr("Available %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()); - std::string accepted = command_line::input_line(prompt.str()); fail_msg_writer() << tr("Failed to find a way to create transactions, too bad. This is usually due to dust which is so small it cannot pay for itself in fees"); } catch (const tools::error::not_enough_outs_to_mix& e) From 68ac0607da1a06681622c57e3af3854598c7fe36 Mon Sep 17 00:00:00 2001 From: Oyvind Kvanes Date: Wed, 28 Sep 2016 16:44:43 +0200 Subject: [PATCH 6/8] Fix locked_transfer --- src/simplewallet/simplewallet.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 3fe9b9091..667338782 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2582,11 +2582,7 @@ bool simple_wallet::locked_transfer(const std::vector &args_) } } - if(local_args.size() < 3 ) - { - fail_msg_writer() << tr("wrong number of arguments"); - return true; - } + if(m_wallet->watch_only()) { @@ -2596,7 +2592,8 @@ bool simple_wallet::locked_transfer(const std::vector &args_) int given_unlock_time = 0; std::vector extra; bool payment_id_seen = false; - if (local_args.size() < 5) + + if (2 < local_args.size() && local_args.size() < 5) { if (local_args.size() == 4) { From 71538f3240f61a7fb0743a12fa3edd676cae44d9 Mon Sep 17 00:00:00 2001 From: Oyvind Kvanes Date: Tue, 4 Oct 2016 11:13:26 +0200 Subject: [PATCH 7/8] Rename to lockblocks and add max value --- src/simplewallet/simplewallet.cpp | 79 +++++++++++++------------------ 1 file changed, 32 insertions(+), 47 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 667338782..4a6385767 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -652,7 +652,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), tr("Show blockchain height")); m_cmd_binder.set_handler("transfer_original", boost::bind(&simple_wallet::transfer, this, _1), tr("transfer [] [ ... ] [payment_id] - Transfer ,... to ,... , respectively. is the number of extra inputs to include for untraceability (from 0 to maximum available)")); m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer_new, this, _1), tr("Same as transfer_original, but using a new transaction building algorithm")); - m_cmd_binder.set_handler("locked_transfer", boost::bind(&simple_wallet::locked_transfer, this, _1), tr("transfer [] (Number of blocks to lock the transaction for) [payment_id]")); + m_cmd_binder.set_handler("locked_transfer", boost::bind(&simple_wallet::locked_transfer, this, _1), tr("transfer [] (Number of blocks to lock the transaction for, max 1000000) []")); m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with mixin 0")); m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr("Send all unlocked balance an address")); m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), tr("set_log - Change current log detail level, <0-4>")); @@ -2586,10 +2586,11 @@ bool simple_wallet::locked_transfer(const std::vector &args_) if(m_wallet->watch_only()) { - fail_msg_writer() << tr("this is a watch only wallet"); - return true; + fail_msg_writer() << tr("this is a watch only wallet"); + return true; } - int given_unlock_time = 0; + + int locked_blocks = 0; std::vector extra; bool payment_id_seen = false; @@ -2627,55 +2628,40 @@ bool simple_wallet::locked_transfer(const std::vector &args_) } } - //std::string unlock_time_string = local_args.back(); - given_unlock_time = std::stoi( local_args.back() ); + + 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"); + fail_msg_writer() << tr("Wrong number of arguments, use: locked_transfer [] []"); return true; } vector dsts; - for (size_t i = 0; i < 1; i += 2) + + 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) { - cryptonote::tx_destination_entry de; - bool has_payment_id; - crypto::hash8 new_payment_id; - if (!get_address_from_str(local_args[i], de.addr, has_payment_id, new_payment_id)) - return true; - - if (has_payment_id) - { - if (payment_id_seen) - { - fail_msg_writer() << tr("a single transaction cannot use more than one payment id: ") << local_args[i]; - return true; - } - - std::string extra_nonce; - set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, new_payment_id); - bool r = add_extra_nonce_to_tx_extra(extra, extra_nonce); - if(!r) - { - fail_msg_writer() << tr("failed to set up payment id, though it was decoded correctly"); - return true; - } - } - - bool ok = cryptonote::parse_amount(de.amount, local_args[i + 1]); - if(!ok || 0 == de.amount) - { - fail_msg_writer() << tr("amount is wrong: ") << local_args[i] << ' ' << local_args[i + 1] << - ", " << tr("expected number from 0 to ") << print_money(std::numeric_limits::max()); - return true; - } - - dsts.push_back(de); + 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 @@ -2683,8 +2669,8 @@ bool simple_wallet::locked_transfer(const std::vector &args_) std::string err; int bc_height = get_daemon_blockchain_height(err); - int unlock_time = given_unlock_time + bc_height; - ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_time , 0 /* unused fee arg*/, extra, m_trusted_daemon); + 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; @@ -2716,9 +2702,8 @@ bool simple_wallet::locked_transfer(const std::vector &args_) 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)(given_unlock_time) / 720.0; - prompt << boost::format(tr(".\nThe unlock time is approximately %s days")) % - days; + 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)"); @@ -2766,7 +2751,7 @@ bool simple_wallet::locked_transfer(const std::vector &args_) 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, too bad. This is usually due to dust which is so small it cannot pay for itself in fees"); + fail_msg_writer() << tr("Not enough money to transfer."); } catch (const tools::error::not_enough_outs_to_mix& e) { From 714ee996782a03949c06847de3b2596ed15a94d9 Mon Sep 17 00:00:00 2001 From: Oyvind Kvanes Date: Tue, 4 Oct 2016 11:46:44 +0200 Subject: [PATCH 8/8] Fix description for locked_transfer --- src/simplewallet/simplewallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 4a6385767..a6e129e9f 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -652,7 +652,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), tr("Show blockchain height")); m_cmd_binder.set_handler("transfer_original", boost::bind(&simple_wallet::transfer, this, _1), tr("transfer [] [ ... ] [payment_id] - Transfer ,... to ,... , respectively. is the number of extra inputs to include for untraceability (from 0 to maximum available)")); m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer_new, this, _1), tr("Same as transfer_original, but using a new transaction building algorithm")); - m_cmd_binder.set_handler("locked_transfer", boost::bind(&simple_wallet::locked_transfer, this, _1), tr("transfer [] (Number of blocks to lock the transaction for, max 1000000) []")); + m_cmd_binder.set_handler("locked_transfer", boost::bind(&simple_wallet::locked_transfer, this, _1), tr("locked_transfer [] (Number of blocks to lock the transaction for, max 1000000) []")); m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with mixin 0")); m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr("Send all unlocked balance an address")); m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), tr("set_log - Change current log detail level, <0-4>"));