From fc1180bc6c2a97624a7ac2684dce047dc2fb3200 Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Fri, 13 Jun 2014 14:05:15 -0400 Subject: [PATCH 01/12] Added comments to wallet functions --- src/wallet/wallet2.cpp | 7 +++++++ src/wallet/wallet2.h | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index a63eb4be..fb4673f0 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -630,10 +630,17 @@ namespace } } //---------------------------------------------------------------------------------------------------- +// Select random input sources for transaction. +// returns: +// direct return: amount of money found +// modified reference: selected_transfers, a list of iterators/indices of input sources uint64_t wallet2::select_transfers(uint64_t needed_money, bool add_dust, uint64_t dust, std::list& selected_transfers) { std::vector unused_transfers_indices; std::vector unused_dust_indices; + + // aggregate sources available for transfers + // if dust needed, take dust from only one source (so require source has at least dust amount) for (size_t i = 0; i < m_transfers.size(); ++i) { const transfer_details& td = m_transfers[i]; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 534a0517..64b8b337 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -304,9 +304,13 @@ namespace tools uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction &tx) { using namespace cryptonote; + // throw if attempting a transaction with no destinations THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination); uint64_t needed_money = fee; + + // calculate total amount being sent to all destinations + // throw if total amount overflows uint64_t BOOST_FOREACH(auto& dt, dsts) { THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_destination); @@ -314,6 +318,8 @@ namespace tools THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee); } + // randomly select inputs for transaction + // throw if requested send amount is greater than amount available to send std::list selected_transfers; uint64_t found_money = select_transfers(needed_money, 0 == fake_outputs_count, dust_policy.dust_threshold, selected_transfers); THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_money, found_money, needed_money - fee, fee); From 79e59d155b9873156180f3b2f8b696cf9b8d84ab Mon Sep 17 00:00:00 2001 From: tom Date: Sun, 15 Jun 2014 17:20:16 -0400 Subject: [PATCH 02/12] working on dividing functions in prep for tx splitting --- src/simplewallet/simplewallet.cpp | 89 +++++++++++++++++++++++++++---- src/simplewallet/simplewallet.h | 4 ++ 2 files changed, 84 insertions(+), 9 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 6af0de9f..6ef3ec21 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -785,6 +785,86 @@ bool simple_wallet::show_blockchain_height(const std::vector& args) fail_msg_writer() << "failed to get blockchain height: " << err; return true; } + +// split_amounts(vector dsts, size_t num_splits) +// +// split amount for each dst in dsts into num_splits parts +// and make num_splits new vector instances to hold these new amounts +vector> simple_wallet::split_amounts( + vector dsts, size_t num_splits) +{ + vector> retVal; + + if (num_splits <= 1) + { + retVal.push_back(dsts); + return retVal; + } + + // for each split required + for (size_t i=0; i < num_splits; i++) + { + vector new_dsts; + + // for each destination + for (size_t j=0; j < dsts.size(); j++) + { + cryptonote::tx_destination_entry de; + uint64_t amount; + + amount = dsts[j].amount / num_splits; + + // if last split, add remainder + if (i + 1 == num_splits) + { + amount += dsts[j].amount % num_splits; + } + + de.addr = dsts[j].addr; + + new_dsts.push_back(de); + } + + retVal.push_back(new_dsts); + } + + return retVal; +} + +//---------------------------------------------------------------------------------------------------- +// separated the call(s) to wallet2::transfer into their own function +// +// this function will make multiple calls to wallet2::transfer if multiple +// transactions will be required +void simple_wallet::create_transactions(vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee, const std::vector extra) +{ + // failsafe split attempt counter + size_t attempt_count = 0; + + for(attempt_count = 1;;attempt_count++) + { + try + { + vector tx_vector; + for (size_t i=0; i < attempt_count; i++) + { + cryptonote::transaction tx; + m_wallet->transfer(dsts, fake_outs_count, 0, DEFAULT_FEE, extra, tx); + tx_vector.push_back(tx); + } + // if we made it this far, we're OK to actually send the transactions + } + success_msg_writer(true) << "Money successfully sent, transaction " << get_transaction_hash(tx); + // only catch this here, other exceptions need to pass through to the calling function + catch (const tools::error::tx_too_big& e) + { + cryptonote::transaction tx = e.tx(); + fail_msg_writer() << "transaction " << get_transaction_hash(e.tx()) << " is too big. Transaction size: " << + get_object_blobsize(e.tx()) << " bytes, transaction size limit: " << e.tx_size_limit() << " bytes"; + } + } +} + //---------------------------------------------------------------------------------------------------- bool simple_wallet::transfer(const std::vector &args_) { @@ -851,9 +931,6 @@ bool simple_wallet::transfer(const std::vector &args_) try { - cryptonote::transaction tx; - m_wallet->transfer(dsts, fake_outs_count, 0, DEFAULT_FEE, extra, tx); - success_msg_writer(true) << "Money successfully sent, transaction " << get_transaction_hash(tx); } catch (const tools::error::daemon_busy&) { @@ -899,12 +976,6 @@ bool simple_wallet::transfer(const std::vector &args_) { fail_msg_writer() << e.what(); } - catch (const tools::error::tx_too_big& e) - { - cryptonote::transaction tx = e.tx(); - fail_msg_writer() << "transaction " << get_transaction_hash(e.tx()) << " is too big. Transaction size: " << - get_object_blobsize(e.tx()) << " bytes, transaction size limit: " << e.tx_size_limit() << " bytes"; - } catch (const tools::error::zero_destination&) { fail_msg_writer() << "one of destinations is zero"; diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 006cdd08..2f0d1731 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -54,6 +54,10 @@ namespace cryptonote bool show_payments(const std::vector &args); bool show_blockchain_height(const std::vector &args); bool transfer(const std::vector &args); + vector> simple_wallet::split_amounts( + vector dsts, size_t num_splits + ); + void create_transactions(vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee, const std::vector extra); bool print_address(const std::vector &args = std::vector()); bool save(const std::vector &args); bool set_log(const std::vector &args); From 2e048a467976f6f620a3e9f55d26744139456147 Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Sun, 15 Jun 2014 20:36:44 -0400 Subject: [PATCH 03/12] final changes to get transaction splitting building. needs testing. --- src/simplewallet/simplewallet.cpp | 92 ++++++++++++++++--- src/simplewallet/simplewallet.h | 6 +- src/wallet/wallet2.cpp | 33 ++++++- src/wallet/wallet2.h | 46 +++++----- src/wallet/wallet_rpc_server.cpp | 3 +- .../transactions_flow_test.cpp | 4 +- 6 files changed, 140 insertions(+), 44 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 6ef3ec21..a54d92a4 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -790,10 +790,10 @@ bool simple_wallet::show_blockchain_height(const std::vector& args) // // split amount for each dst in dsts into num_splits parts // and make num_splits new vector instances to hold these new amounts -vector> simple_wallet::split_amounts( - vector dsts, size_t num_splits) +std::vector> simple_wallet::split_amounts( + std::vector dsts, size_t num_splits) { - vector> retVal; + std::vector> retVal; if (num_splits <= 1) { @@ -804,7 +804,7 @@ vector> simple_wallet::split_amounts( // for each split required for (size_t i=0; i < num_splits; i++) { - vector new_dsts; + std::vector new_dsts; // for each destination for (size_t j=0; j < dsts.size(); j++) @@ -836,33 +836,94 @@ vector> simple_wallet::split_amounts( // // this function will make multiple calls to wallet2::transfer if multiple // transactions will be required -void simple_wallet::create_transactions(vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee, const std::vector extra) +void simple_wallet::create_transactions(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee, const std::vector extra) { + // for now, limit to 5 attempts. TODO: discuss a good number to limit to. + const size_t MAX_ATTEMPTS = 5; + // failsafe split attempt counter size_t attempt_count = 0; - for(attempt_count = 1;;attempt_count++) + for(attempt_count = 1; attempt_count <= 5 ;attempt_count++) { + auto split_values = split_amounts(dsts, attempt_count); + + // Throw if split_amounts comes back with a vector of size different than it should + if (split_values.size() != attempt_count) + { + throw std::runtime_error("Splitting transactions returned a number of potential tx not equal to what was requested"); + } + + std::vector ptx_vector; try { - vector tx_vector; for (size_t i=0; i < attempt_count; i++) { cryptonote::transaction tx; - m_wallet->transfer(dsts, fake_outs_count, 0, DEFAULT_FEE, extra, tx); - tx_vector.push_back(tx); + tools::wallet2::pending_tx ptx; + m_wallet->transfer(dsts, fake_outs_count, unlock_time, fee, extra, tx, ptx); + ptx_vector.push_back(ptx); + + // mark transfers to be used as "spent" + BOOST_FOREACH(tools::wallet2::transfer_container::iterator it, ptx.selected_transfers) + it->m_spent = true; } + + // if we made it this far, we've selected our transactions. committing them will mark them spent, + // so this is a failsafe in case they don't go through + // unmark pending tx transfers as spent + for (auto & ptx : ptx_vector) + { + // mark transfers to be used as not spent + BOOST_FOREACH(tools::wallet2::transfer_container::iterator it2, ptx.selected_transfers) + it2->m_spent = false; + + } + // if we made it this far, we're OK to actually send the transactions + while (!ptx_vector.empty()) + { + m_wallet->commit_tx(ptx_vector.back()); + // if no exception, remove element from vector + ptx_vector.pop_back(); + } + } - success_msg_writer(true) << "Money successfully sent, transaction " << get_transaction_hash(tx); // only catch this here, other exceptions need to pass through to the calling function catch (const tools::error::tx_too_big& e) { - cryptonote::transaction tx = e.tx(); - fail_msg_writer() << "transaction " << get_transaction_hash(e.tx()) << " is too big. Transaction size: " << - get_object_blobsize(e.tx()) << " bytes, transaction size limit: " << e.tx_size_limit() << " bytes"; + + // unmark pending tx transfers as spent + for (auto & ptx : ptx_vector) + { + // mark transfers to be used as not spent + BOOST_FOREACH(tools::wallet2::transfer_container::iterator it2, ptx.selected_transfers) + it2->m_spent = false; + + } + + if (attempt_count >= MAX_ATTEMPTS) + { + throw; + } + } + catch (...) + { + // in case of some other exception, make sure any tx in queue are marked unspent again + + // unmark pending tx transfers as spent + for (auto & ptx : ptx_vector) + { + // mark transfers to be used as not spent + BOOST_FOREACH(tools::wallet2::transfer_container::iterator it2, ptx.selected_transfers) + it2->m_spent = false; + + } + + throw; } } + //success_msg_writer(true) << "Money successfully sent, transaction " << get_transaction_hash(tx); } //---------------------------------------------------------------------------------------------------- @@ -931,6 +992,7 @@ bool simple_wallet::transfer(const std::vector &args_) try { + create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, DEFAULT_FEE, extra); } catch (const tools::error::daemon_busy&) { @@ -980,6 +1042,10 @@ bool simple_wallet::transfer(const std::vector &args_) { fail_msg_writer() << "one of destinations is zero"; } + catch (const tools::error::tx_too_big& e) + { + fail_msg_writer() << "Failed to find a suitable way to split transactions"; + } catch (const tools::error::transfer_error& e) { LOG_ERROR("unknown transfer error: " << e.to_string()); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 2f0d1731..8a6ac389 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -54,10 +54,10 @@ namespace cryptonote bool show_payments(const std::vector &args); bool show_blockchain_height(const std::vector &args); bool transfer(const std::vector &args); - vector> simple_wallet::split_amounts( - vector dsts, size_t num_splits + std::vector> split_amounts( + std::vector dsts, size_t num_splits ); - void create_transactions(vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee, const std::vector extra); + void create_transactions(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee, const std::vector extra); bool print_address(const std::vector &args = std::vector()); bool save(const std::vector &args); bool set_log(const std::vector &args); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index fb4673f0..7dfbc7f7 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -685,16 +685,43 @@ void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t cha } //---------------------------------------------------------------------------------------------------- void wallet2::transfer(const std::vector& dsts, size_t fake_outputs_count, - uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx) + uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx, pending_tx& ptx) { - transfer(dsts, fake_outputs_count, unlock_time, fee, extra, detail::digit_split_strategy, tx_dust_policy(fee), tx); + transfer(dsts, fake_outputs_count, unlock_time, fee, extra, detail::digit_split_strategy, tx_dust_policy(fee), tx, ptx); } //---------------------------------------------------------------------------------------------------- void wallet2::transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra) { cryptonote::transaction tx; - transfer(dsts, fake_outputs_count, unlock_time, fee, extra, tx); + pending_tx ptx; + transfer(dsts, fake_outputs_count, unlock_time, fee, extra, tx, ptx); } //---------------------------------------------------------------------------------------------------- +// take a pending tx and actually send it to the daemon +void wallet2::commit_tx(pending_tx& ptx) +{ + using namespace cryptonote; + COMMAND_RPC_SEND_RAW_TX::request req; + req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx)); + COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp; + bool r = epee::net_utils::invoke_http_json_remote_command2(m_daemon_address + "/sendrawtransaction", req, daemon_send_resp, m_http_client, 200000); + THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "sendrawtransaction"); + THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "sendrawtransaction"); + THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status != CORE_RPC_STATUS_OK, error::tx_rejected, ptx.tx, daemon_send_resp.status); + + add_unconfirmed_tx(ptx.tx, ptx.change_dts.amount); + + LOG_PRINT_L2("transaction " << get_transaction_hash(ptx.tx) << " generated ok and sent to daemon, key_images: [" << ptx.key_images << "]"); + + BOOST_FOREACH(transfer_container::iterator it, ptx.selected_transfers) + it->m_spent = true; + + LOG_PRINT_L0("Transaction successfully sent. <" << get_transaction_hash(ptx.tx) << ">" << ENDL + << "Commission: " << print_money(ptx.fee+ptx.dust) << " (dust: " << print_money(ptx.dust) << ")" << ENDL + << "Balance: " << print_money(balance()) << ENDL + << "Unlocked: " << print_money(unlocked_balance()) << ENDL + << "Please, wait for confirmation for your balance to be unlocked."); +} + } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 64b8b337..e8b9ec46 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -86,6 +86,15 @@ namespace tools typedef std::vector transfer_container; typedef std::unordered_multimap payment_container; + struct pending_tx + { + cryptonote::transaction tx; + uint64_t dust, fee; + cryptonote::tx_destination_entry change_dts; + std::list selected_transfers; + std::string key_images; + }; + struct keys_file_data { crypto::chacha8_iv iv; @@ -125,9 +134,10 @@ namespace tools template void transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy); template - void transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction &tx); + void transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx& ptx); void transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra); - void transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx); + void transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx, pending_tx& ptx); + void commit_tx(pending_tx& ptx); bool check_connection(); void get_transfers(wallet2::transfer_container& incoming_transfers) const; void get_payments(const crypto::hash& payment_id, std::list& payments) const; @@ -295,13 +305,14 @@ namespace tools void wallet2::transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy) { + pending_tx ptx; cryptonote::transaction tx; - transfer(dsts, fake_outputs_count, unlock_time, fee, extra, destination_split_strategy, dust_policy, tx); + transfer(dsts, fake_outputs_count, unlock_time, fee, extra, destination_split_strategy, dust_policy, tx, ptx); } template void wallet2::transfer(const std::vector& dsts, size_t fake_outputs_count, - uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction &tx) + uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx) { using namespace cryptonote; // throw if attempting a transaction with no destinations @@ -432,25 +443,14 @@ namespace tools }); THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key, error::unexpected_txin_type, tx); - COMMAND_RPC_SEND_RAW_TX::request req; - req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(tx)); - COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp; - r = epee::net_utils::invoke_http_json_remote_command2(m_daemon_address + "/sendrawtransaction", req, daemon_send_resp, m_http_client, 200000); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "sendrawtransaction"); - THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "sendrawtransaction"); - THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status != CORE_RPC_STATUS_OK, error::tx_rejected, tx, daemon_send_resp.status); + ptx.key_images = key_images; + ptx.fee = fee; + ptx.dust = dust; + ptx.tx = tx; + ptx.change_dts = change_dts; + ptx.selected_transfers = selected_transfers; - add_unconfirmed_tx(tx, change_dts.amount); - - LOG_PRINT_L2("transaction " << get_transaction_hash(tx) << " generated ok and sent to daemon, key_images: [" << key_images << "]"); - - BOOST_FOREACH(transfer_container::iterator it, selected_transfers) - it->m_spent = true; - - LOG_PRINT_L0("Transaction successfully sent. <" << get_transaction_hash(tx) << ">" << ENDL - << "Commission: " << print_money(fee+dust) << " (dust: " << print_money(dust) << ")" << ENDL - << "Balance: " << print_money(balance()) << ENDL - << "Unlocked: " << print_money(unlocked_balance()) << ENDL - << "Please, wait for confirmation for your balance to be unlocked."); } + + } diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index a0816946..95c71fc6 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -132,7 +132,8 @@ namespace tools try { cryptonote::transaction tx; - m_wallet.transfer(dsts, req.mixin, req.unlock_time, req.fee, extra, tx); + wallet2::pending_tx ptx; + m_wallet.transfer(dsts, req.mixin, req.unlock_time, req.fee, extra, tx, ptx); res.tx_hash = boost::lexical_cast(cryptonote::get_transaction_hash(tx)); return true; } diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp index 4c6d5b07..17cfc23c 100644 --- a/tests/functional_tests/transactions_flow_test.cpp +++ b/tests/functional_tests/transactions_flow_test.cpp @@ -52,7 +52,9 @@ bool do_send_money(tools::wallet2& w1, tools::wallet2& w2, size_t mix_in_factor, try { - w1.transfer(dsts, mix_in_factor, 0, DEFAULT_FEE, std::vector(), tools::detail::null_split_strategy, tools::tx_dust_policy(DEFAULT_FEE), tx); + tools::wallet2::pending_tx ptx; + w1.transfer(dsts, mix_in_factor, 0, DEFAULT_FEE, std::vector(), tools::detail::null_split_strategy, tools::tx_dust_policy(DEFAULT_FEE), tx, ptx); + w1.commit_tx(ptx); return true; } catch (const std::exception&) From 8166650ae0a9da929e51470238eea9c2f1b31e42 Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Mon, 16 Jun 2014 14:14:09 -0400 Subject: [PATCH 04/12] up tx splits limit 5 -> 30 --- 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 a54d92a4..e5cc4d16 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -839,7 +839,7 @@ std::vector> simple_wallet::split_ void simple_wallet::create_transactions(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee, const std::vector extra) { // for now, limit to 5 attempts. TODO: discuss a good number to limit to. - const size_t MAX_ATTEMPTS = 5; + const size_t MAX_ATTEMPTS = 30; // failsafe split attempt counter size_t attempt_count = 0; From 002ce963bfccb2e592e626c198e1fc9cbac96216 Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Mon, 16 Jun 2014 14:35:20 -0400 Subject: [PATCH 05/12] added back successful tx message. oops. --- src/simplewallet/simplewallet.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index e5cc4d16..96f209ca 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -883,7 +883,9 @@ void simple_wallet::create_transactions(std::vectorcommit_tx(ptx_vector.back()); + auto & ptx = ptx_vector.back(); + m_wallet->commit_tx(ptx); + success_msg_writer(true) << "Money successfully sent, transaction " << get_transaction_hash(ptx.tx); // if no exception, remove element from vector ptx_vector.pop_back(); } From 9bfe0b9b6c601f732c4708ba00dc2203c383566f Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Mon, 16 Jun 2014 15:16:54 -0400 Subject: [PATCH 06/12] Added confirmation prompt if transactions are to be split --- src/simplewallet/simplewallet.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 96f209ca..69aade89 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -880,6 +880,22 @@ void simple_wallet::create_transactions(std::vector 1) + { + std::string prompt_str = "Your transaction needs to be split into "; + prompt_str += attempt_count; + prompt_str += " transactions. This will result in a fee of "; + prompt_str += print_money(attempt_count * DEFAULT_FEE); + prompt_str += ". Is this okay? (Y/Yes/N/No)"; + std::string accepted = command_line::input_line(prompt_str); + if (accepted != "Y" && accepted != "y" && accepted != "Yes" && accepted != "yes") + { + fail_msg_writer() << "Transaction cancelled."; + return; + } + } + // if we made it this far, we're OK to actually send the transactions while (!ptx_vector.empty()) { From 62109840d68d6eb439a4861bf38c64d6a857deb8 Mon Sep 17 00:00:00 2001 From: tom Date: Mon, 16 Jun 2014 21:52:06 -0400 Subject: [PATCH 07/12] Transaction splitting *seems* to be working!!! --- src/simplewallet/simplewallet.cpp | 27 +++++++++++++++++++++------ src/wallet/wallet2.h | 3 +++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 69aade89..631f9d4b 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -812,15 +812,21 @@ std::vector> simple_wallet::split_ cryptonote::tx_destination_entry de; uint64_t amount; - amount = dsts[j].amount / num_splits; + amount = dsts[j].amount; + std::cout << "Amount before split: " << amount << "; num_splits: " << num_splits; + amount = amount / num_splits; + std::cout << "; amount after split: " << amount; // if last split, add remainder if (i + 1 == num_splits) { amount += dsts[j].amount % num_splits; + std::cout << "; amount after remainder: " << amount; } + std::cout << std::endl; de.addr = dsts[j].addr; + de.amount = amount; new_dsts.push_back(de); } @@ -839,13 +845,19 @@ std::vector> simple_wallet::split_ void simple_wallet::create_transactions(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee, const std::vector extra) { // for now, limit to 5 attempts. TODO: discuss a good number to limit to. - const size_t MAX_ATTEMPTS = 30; + const size_t MAX_ATTEMPTS = 5; // failsafe split attempt counter size_t attempt_count = 0; - for(attempt_count = 1; attempt_count <= 5 ;attempt_count++) + for(attempt_count = 1; ;attempt_count++) { + if (attempt_count > 1) + { + std::string prompt = "Attempt #"; + prompt.append(std::to_string(attempt_count)); + command_line::input_line(prompt); + } auto split_values = split_amounts(dsts, attempt_count); // Throw if split_amounts comes back with a vector of size different than it should @@ -857,11 +869,12 @@ void simple_wallet::create_transactions(std::vector ptx_vector; try { - for (size_t i=0; i < attempt_count; i++) + // for each new destination vector (i.e. for each new tx) + for (auto & dst_vector : split_values) { cryptonote::transaction tx; tools::wallet2::pending_tx ptx; - m_wallet->transfer(dsts, fake_outs_count, unlock_time, fee, extra, tx, ptx); + m_wallet->transfer(dst_vector, fake_outs_count, unlock_time, fee, extra, tx, ptx); ptx_vector.push_back(ptx); // mark transfers to be used as "spent" @@ -884,7 +897,7 @@ void simple_wallet::create_transactions(std::vector 1) { std::string prompt_str = "Your transaction needs to be split into "; - prompt_str += attempt_count; + prompt_str += std::to_string(attempt_count); prompt_str += " transactions. This will result in a fee of "; prompt_str += print_money(attempt_count * DEFAULT_FEE); prompt_str += ". Is this okay? (Y/Yes/N/No)"; @@ -906,6 +919,8 @@ void simple_wallet::create_transactions(std::vector #define DEFAULT_TX_SPENDABLE_AGE 10 #define WALLET_RCP_CONNECTION_TIMEOUT 200000 @@ -329,6 +330,8 @@ namespace tools THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee); } + std::cout << "Attempting to create transaction, needed money = " << needed_money << std::endl; + // randomly select inputs for transaction // throw if requested send amount is greater than amount available to send std::list selected_transfers; From e5ab98a6f4e5daff875ab0d0bcf4141841fc2ead Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Mon, 16 Jun 2014 22:11:47 -0400 Subject: [PATCH 08/12] removed some debugging code (really stupid printf-style debuggng.. --- src/simplewallet/simplewallet.cpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 631f9d4b..75b4072e 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -813,15 +813,12 @@ std::vector> simple_wallet::split_ uint64_t amount; amount = dsts[j].amount; - std::cout << "Amount before split: " << amount << "; num_splits: " << num_splits; amount = amount / num_splits; - std::cout << "; amount after split: " << amount; // if last split, add remainder if (i + 1 == num_splits) { amount += dsts[j].amount % num_splits; - std::cout << "; amount after remainder: " << amount; } std::cout << std::endl; @@ -844,20 +841,14 @@ std::vector> simple_wallet::split_ // transactions will be required void simple_wallet::create_transactions(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee, const std::vector extra) { - // for now, limit to 5 attempts. TODO: discuss a good number to limit to. - const size_t MAX_ATTEMPTS = 5; + // for now, limit to 30 attempts. TODO: discuss a good number to limit to. + const size_t MAX_ATTEMPTS = 30; // failsafe split attempt counter size_t attempt_count = 0; for(attempt_count = 1; ;attempt_count++) { - if (attempt_count > 1) - { - std::string prompt = "Attempt #"; - prompt.append(std::to_string(attempt_count)); - command_line::input_line(prompt); - } auto split_values = split_amounts(dsts, attempt_count); // Throw if split_amounts comes back with a vector of size different than it should From 7c7696a830ec5a03e1a76b0988eb11037150afee Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Tue, 17 Jun 2014 13:03:37 -0400 Subject: [PATCH 09/12] missed removing a debug print --- src/wallet/wallet2.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 3cdfaa84..bbedff96 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -330,8 +330,6 @@ namespace tools THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee); } - std::cout << "Attempting to create transaction, needed money = " << needed_money << std::endl; - // randomly select inputs for transaction // throw if requested send amount is greater than amount available to send std::list selected_transfers; From 2011b5028033fdce47d9dc102bc716be8977f4d4 Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Tue, 17 Jun 2014 14:42:41 -0400 Subject: [PATCH 10/12] removed erroneous printing of newlines --- src/simplewallet/simplewallet.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 75b4072e..e747ee5e 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -820,7 +820,6 @@ std::vector> simple_wallet::split_ { amount += dsts[j].amount % num_splits; } - std::cout << std::endl; de.addr = dsts[j].addr; de.amount = amount; From d433a696e527a01c1cbef48495652335140f0bb2 Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Tue, 17 Jun 2014 18:15:21 -0400 Subject: [PATCH 11/12] wallet RPC converted to use new transaction semantics wallet RPC now uses wallet2::create_transactions and wallet2::commit_tx instead of wallet2::transfer. This made it possible to add the RPC call /transfer_split, which will split transactions automatically if they are too large. The old call to /transfer will return an error stating to use /transfer_split if multiple transactions are needed to fulfill the request. --- src/simplewallet/simplewallet.cpp | 196 +++--------------- src/simplewallet/simplewallet.h | 1 - src/wallet/wallet2.cpp | 156 +++++++++++++- src/wallet/wallet2.h | 4 +- src/wallet/wallet_rpc_server.cpp | 96 +++++++-- src/wallet/wallet_rpc_server.h | 5 +- ...fs.h => wallet_rpc_server_commands_defs.h} | 33 ++- 7 files changed, 306 insertions(+), 185 deletions(-) rename src/wallet/{wallet_rpc_server_commans_defs.h => wallet_rpc_server_commands_defs.h} (83%) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index e747ee5e..8ceb583b 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -786,169 +786,6 @@ bool simple_wallet::show_blockchain_height(const std::vector& args) return true; } -// split_amounts(vector dsts, size_t num_splits) -// -// split amount for each dst in dsts into num_splits parts -// and make num_splits new vector instances to hold these new amounts -std::vector> simple_wallet::split_amounts( - std::vector dsts, size_t num_splits) -{ - std::vector> retVal; - - if (num_splits <= 1) - { - retVal.push_back(dsts); - return retVal; - } - - // for each split required - for (size_t i=0; i < num_splits; i++) - { - std::vector new_dsts; - - // for each destination - for (size_t j=0; j < dsts.size(); j++) - { - cryptonote::tx_destination_entry de; - uint64_t amount; - - amount = dsts[j].amount; - amount = amount / num_splits; - - // if last split, add remainder - if (i + 1 == num_splits) - { - amount += dsts[j].amount % num_splits; - } - - de.addr = dsts[j].addr; - de.amount = amount; - - new_dsts.push_back(de); - } - - retVal.push_back(new_dsts); - } - - return retVal; -} - -//---------------------------------------------------------------------------------------------------- -// separated the call(s) to wallet2::transfer into their own function -// -// this function will make multiple calls to wallet2::transfer if multiple -// transactions will be required -void simple_wallet::create_transactions(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee, const std::vector extra) -{ - // for now, limit to 30 attempts. TODO: discuss a good number to limit to. - const size_t MAX_ATTEMPTS = 30; - - // failsafe split attempt counter - size_t attempt_count = 0; - - for(attempt_count = 1; ;attempt_count++) - { - auto split_values = split_amounts(dsts, attempt_count); - - // Throw if split_amounts comes back with a vector of size different than it should - if (split_values.size() != attempt_count) - { - throw std::runtime_error("Splitting transactions returned a number of potential tx not equal to what was requested"); - } - - std::vector ptx_vector; - try - { - // for each new destination vector (i.e. for each new tx) - for (auto & dst_vector : split_values) - { - cryptonote::transaction tx; - tools::wallet2::pending_tx ptx; - m_wallet->transfer(dst_vector, fake_outs_count, unlock_time, fee, extra, tx, ptx); - ptx_vector.push_back(ptx); - - // mark transfers to be used as "spent" - BOOST_FOREACH(tools::wallet2::transfer_container::iterator it, ptx.selected_transfers) - it->m_spent = true; - } - - // if we made it this far, we've selected our transactions. committing them will mark them spent, - // so this is a failsafe in case they don't go through - // unmark pending tx transfers as spent - for (auto & ptx : ptx_vector) - { - // mark transfers to be used as not spent - BOOST_FOREACH(tools::wallet2::transfer_container::iterator it2, ptx.selected_transfers) - it2->m_spent = false; - - } - - // prompt user to confirm splits (if needed) - if (attempt_count > 1) - { - std::string prompt_str = "Your transaction needs to be split into "; - prompt_str += std::to_string(attempt_count); - prompt_str += " transactions. This will result in a fee of "; - prompt_str += print_money(attempt_count * DEFAULT_FEE); - prompt_str += ". Is this okay? (Y/Yes/N/No)"; - std::string accepted = command_line::input_line(prompt_str); - if (accepted != "Y" && accepted != "y" && accepted != "Yes" && accepted != "yes") - { - fail_msg_writer() << "Transaction cancelled."; - return; - } - } - - // if we made it this far, we're OK to actually send the transactions - while (!ptx_vector.empty()) - { - auto & ptx = ptx_vector.back(); - m_wallet->commit_tx(ptx); - success_msg_writer(true) << "Money successfully sent, transaction " << get_transaction_hash(ptx.tx); - // if no exception, remove element from vector - ptx_vector.pop_back(); - } - - return; - - } - // only catch this here, other exceptions need to pass through to the calling function - catch (const tools::error::tx_too_big& e) - { - - // unmark pending tx transfers as spent - for (auto & ptx : ptx_vector) - { - // mark transfers to be used as not spent - BOOST_FOREACH(tools::wallet2::transfer_container::iterator it2, ptx.selected_transfers) - it2->m_spent = false; - - } - - if (attempt_count >= MAX_ATTEMPTS) - { - throw; - } - } - catch (...) - { - // in case of some other exception, make sure any tx in queue are marked unspent again - - // unmark pending tx transfers as spent - for (auto & ptx : ptx_vector) - { - // mark transfers to be used as not spent - BOOST_FOREACH(tools::wallet2::transfer_container::iterator it2, ptx.selected_transfers) - it2->m_spent = false; - - } - - throw; - } - } - //success_msg_writer(true) << "Money successfully sent, transaction " << get_transaction_hash(tx); -} - //---------------------------------------------------------------------------------------------------- bool simple_wallet::transfer(const std::vector &args_) { @@ -1015,7 +852,38 @@ bool simple_wallet::transfer(const std::vector &args_) try { - create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, DEFAULT_FEE, extra); + // figure out what tx will be necessary + auto ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, DEFAULT_FEE, extra); + + // if more than one tx necessary, prompt user to confirm + if (ptx_vector.size() > 1) + { + std::string prompt_str = "Your transaction needs to be split into "; + prompt_str += std::to_string(ptx_vector.size()); + prompt_str += " transactions. This will result in a fee of "; + prompt_str += print_money(ptx_vector.size() * DEFAULT_FEE); + prompt_str += ". Is this okay? (Y/Yes/N/No)"; + std::string accepted = command_line::input_line(prompt_str); + if (accepted != "Y" && accepted != "y" && accepted != "Yes" && accepted != "yes") + { + fail_msg_writer() << "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) << "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&) { diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 8a6ac389..e919cfed 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -57,7 +57,6 @@ namespace cryptonote std::vector> split_amounts( std::vector dsts, size_t num_splits ); - void create_transactions(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee, const std::vector extra); bool print_address(const std::vector &args = std::vector()); bool save(const std::vector &args); bool set_log(const std::vector &args); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 7dfbc7f7..5b284c61 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -43,6 +43,9 @@ void do_prepare_file_names(const std::string& file_path, std::string& keys_file, namespace tools { +// for now, limit to 30 attempts. TODO: discuss a good number to limit to. +const size_t MAX_SPLIT_ATTEMPTS = 30; + //---------------------------------------------------------------------------------------------------- void wallet2::init(const std::string& daemon_address, uint64_t upper_transaction_size_limit) { @@ -697,6 +700,56 @@ void wallet2::transfer(const std::vector& dsts pending_tx ptx; transfer(dsts, fake_outputs_count, unlock_time, fee, extra, tx, ptx); } + +namespace { +// split_amounts(vector dsts, size_t num_splits) +// +// split amount for each dst in dsts into num_splits parts +// and make num_splits new vector instances to hold these new amounts +std::vector> split_amounts( + std::vector dsts, size_t num_splits) +{ + std::vector> retVal; + + if (num_splits <= 1) + { + retVal.push_back(dsts); + return retVal; + } + + // for each split required + for (size_t i=0; i < num_splits; i++) + { + std::vector new_dsts; + + // for each destination + for (size_t j=0; j < dsts.size(); j++) + { + cryptonote::tx_destination_entry de; + uint64_t amount; + + amount = dsts[j].amount; + amount = amount / num_splits; + + // if last split, add remainder + if (i + 1 == num_splits) + { + amount += dsts[j].amount % num_splits; + } + + de.addr = dsts[j].addr; + de.amount = amount; + + new_dsts.push_back(de); + } + + retVal.push_back(new_dsts); + } + + return retVal; +} +} // anonymous namespace + //---------------------------------------------------------------------------------------------------- // take a pending tx and actually send it to the daemon void wallet2::commit_tx(pending_tx& ptx) @@ -718,10 +771,105 @@ void wallet2::commit_tx(pending_tx& ptx) it->m_spent = true; LOG_PRINT_L0("Transaction successfully sent. <" << get_transaction_hash(ptx.tx) << ">" << ENDL - << "Commission: " << print_money(ptx.fee+ptx.dust) << " (dust: " << print_money(ptx.dust) << ")" << ENDL - << "Balance: " << print_money(balance()) << ENDL - << "Unlocked: " << print_money(unlocked_balance()) << ENDL - << "Please, wait for confirmation for your balance to be unlocked."); + << "Commission: " << print_money(ptx.fee+ptx.dust) << " (dust: " << print_money(ptx.dust) << ")" << ENDL + << "Balance: " << print_money(balance()) << ENDL + << "Unlocked: " << print_money(unlocked_balance()) << ENDL + << "Please, wait for confirmation for your balance to be unlocked."); } +void wallet2::commit_tx(std::vector& ptx_vector) +{ + for (auto & ptx : ptx_vector) + { + commit_tx(ptx); + } +} + +//---------------------------------------------------------------------------------------------------- +// separated the call(s) to wallet2::transfer into their own function +// +// this function will make multiple calls to wallet2::transfer if multiple +// transactions will be required +std::vector wallet2::create_transactions(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee, const std::vector extra) +{ + + // failsafe split attempt counter + size_t attempt_count = 0; + + for(attempt_count = 1; ;attempt_count++) + { + auto split_values = split_amounts(dsts, attempt_count); + + // Throw if split_amounts comes back with a vector of size different than it should + if (split_values.size() != attempt_count) + { + throw std::runtime_error("Splitting transactions returned a number of potential tx not equal to what was requested"); + } + + std::vector ptx_vector; + try + { + // for each new destination vector (i.e. for each new tx) + for (auto & dst_vector : split_values) + { + cryptonote::transaction tx; + pending_tx ptx; + transfer(dst_vector, fake_outs_count, unlock_time, fee, extra, tx, ptx); + ptx_vector.push_back(ptx); + + // mark transfers to be used as "spent" + BOOST_FOREACH(transfer_container::iterator it, ptx.selected_transfers) + it->m_spent = true; + } + + // if we made it this far, we've selected our transactions. committing them will mark them spent, + // so this is a failsafe in case they don't go through + // unmark pending tx transfers as spent + for (auto & ptx : ptx_vector) + { + // mark transfers to be used as not spent + BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers) + it2->m_spent = false; + + } + + // if we made it this far, we're OK to actually send the transactions + return ptx_vector; + + } + // only catch this here, other exceptions need to pass through to the calling function + catch (const tools::error::tx_too_big& e) + { + + // unmark pending tx transfers as spent + for (auto & ptx : ptx_vector) + { + // mark transfers to be used as not spent + BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers) + it2->m_spent = false; + + } + + if (attempt_count >= MAX_SPLIT_ATTEMPTS) + { + throw; + } + } + catch (...) + { + // in case of some other exception, make sure any tx in queue are marked unspent again + + // unmark pending tx transfers as spent + for (auto & ptx : ptx_vector) + { + // mark transfers to be used as not spent + BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers) + it2->m_spent = false; + + } + + throw; + } + } +} } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index bbedff96..1f5ae706 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -138,7 +138,9 @@ namespace tools void transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx& ptx); void transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra); void transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx, pending_tx& ptx); - void commit_tx(pending_tx& ptx); + void commit_tx(pending_tx& ptx_vector); + void commit_tx(std::vector& ptx_vector); + std::vector create_transactions(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee, const std::vector extra); bool check_connection(); void get_transfers(wallet2::transfer_container& incoming_transfers) const; void get_payments(const crypto::hash& payment_id, std::list& payments) const; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 95c71fc6..ec31d462 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -10,6 +10,7 @@ using namespace epee; #include "common/command_line.h" #include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/account.h" +#include "wallet_rpc_server_commands_defs.h" #include "misc_language.h" #include "string_tools.h" #include "crypto/hash.h" @@ -85,12 +86,11 @@ namespace tools } return true; } - //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx) - { - std::vector dsts; - for (auto it = req.destinations.begin(); it != req.destinations.end(); it++) + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::validate_transfer(const std::list destinations, const std::string payment_id, std::vector& dsts, std::vector extra, epee::json_rpc::error& er) + { + for (auto it = destinations.begin(); it != destinations.end(); it++) { cryptonote::tx_destination_entry de; if(!get_account_address_from_str(de.addr, it->address)) @@ -103,11 +103,11 @@ namespace tools dsts.push_back(de); } - std::vector extra; - if (!req.payment_id.empty()) { + if (!payment_id.empty()) + { /* Just to clarify */ - const std::string& payment_id_str = req.payment_id; + const std::string& payment_id_str = payment_id; crypto::hash payment_id; /* Parse payment ID */ @@ -128,13 +128,85 @@ namespace tools } } + return true; + } + + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + + std::vector dsts; + std::vector extra; + + // validate the transfer requested and populate dsts & extra + if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, er)) + { + return false; + } try { - cryptonote::transaction tx; - wallet2::pending_tx ptx; - m_wallet.transfer(dsts, req.mixin, req.unlock_time, req.fee, extra, tx, ptx); - res.tx_hash = boost::lexical_cast(cryptonote::get_transaction_hash(tx)); + std::vector ptx_vector = m_wallet.create_transactions(dsts, req.mixin, req.unlock_time, req.fee, extra); + + // reject proposed transactions if there are more than one. see on_transfer_split below. + if (ptx_vector.size() != 1) + { + er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; + er.message = "Transaction would be too large. try /transfer_split."; + return false; + } + + m_wallet.commit_tx(ptx_vector); + + // populate response with tx hash + res.tx_hash = boost::lexical_cast(cryptonote::get_transaction_hash(ptx_vector.back().tx)); + return true; + } + catch (const tools::error::daemon_busy& e) + { + er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY; + er.message = e.what(); + return false; + } + catch (const std::exception& e) + { + er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; + er.message = e.what(); + return false; + } + catch (...) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR"; + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + + std::vector dsts; + std::vector extra; + + // validate the transfer requested and populate dsts & extra; RPC_TRANSFER::request and RPC_TRANSFER_SPLIT::request are identical types. + if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, er)) + { + return false; + } + + try + { + std::vector ptx_vector = m_wallet.create_transactions(dsts, req.mixin, req.unlock_time, req.fee, extra); + + m_wallet.commit_tx(ptx_vector); + + // populate response with tx hashes + for (auto & ptx : ptx_vector) + { + res.tx_hash_list.push_back(boost::lexical_cast(cryptonote::get_transaction_hash(ptx.tx))); + } + return true; } catch (const tools::error::daemon_busy& e) diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index b8373d27..f9893566 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -7,7 +7,7 @@ #include #include #include "net/http_server_impl_base.h" -#include "wallet_rpc_server_commans_defs.h" +#include "wallet_rpc_server_commands_defs.h" #include "wallet2.h" #include "common/command_line.h" namespace tools @@ -38,6 +38,7 @@ namespace tools MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE) MAP_JON_RPC_WE("getaddress", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS) MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER) + MAP_JON_RPC_WE("transfer_split", on_transfer_split, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT) MAP_JON_RPC_WE("store", on_store, wallet_rpc::COMMAND_RPC_STORE) MAP_JON_RPC_WE("get_payments", on_get_payments, wallet_rpc::COMMAND_RPC_GET_PAYMENTS) MAP_JON_RPC_WE("incoming_transfers", on_incoming_transfers, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS) @@ -47,7 +48,9 @@ namespace tools //json_rpc bool on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool validate_transfer(const std::list destinations, const std::string payment_id, std::vector& dsts, std::vector extra, epee::json_rpc::error& er); bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_incoming_transfers(const wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::response& res, epee::json_rpc::error& er, connection_context& cntx); diff --git a/src/wallet/wallet_rpc_server_commans_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h similarity index 83% rename from src/wallet/wallet_rpc_server_commans_defs.h rename to src/wallet/wallet_rpc_server_commands_defs.h index 7ffbcfc1..33130da0 100644 --- a/src/wallet/wallet_rpc_server_commans_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -52,7 +52,7 @@ namespace wallet_rpc }; }; - struct trnsfer_destination + struct transfer_destination { uint64_t amount; std::string address; @@ -66,7 +66,7 @@ namespace wallet_rpc { struct request { - std::list destinations; + std::list destinations; uint64_t fee; uint64_t mixin; uint64_t unlock_time; @@ -91,6 +91,35 @@ namespace wallet_rpc }; }; + struct COMMAND_RPC_TRANSFER_SPLIT + { + struct request + { + std::list destinations; + uint64_t fee; + uint64_t mixin; + uint64_t unlock_time; + std::string payment_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(destinations) + KV_SERIALIZE(fee) + KV_SERIALIZE(mixin) + KV_SERIALIZE(unlock_time) + KV_SERIALIZE(payment_id) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::list tx_hash_list; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tx_hash_list) + END_KV_SERIALIZE_MAP() + }; + }; + struct COMMAND_RPC_STORE { struct request From 45bd1823633305764904691eb89dfd8682b27918 Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Fri, 13 Jun 2014 00:01:04 -0400 Subject: [PATCH 12/12] needed to remove REQUIRED from find_package(Threads) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f084ba2..c5a7220e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,7 @@ set(STATIC ${MSVC} CACHE BOOL "Link libraries statically") if (UNIX AND NOT APPLE) # Note that at the time of this writing the -Wstrict-prototypes flag added below will make this fail - find_package(Threads REQUIRED) + find_package(Threads) endif() if(MSVC)