diff options
author | Thomas Winget <tewinget@gmail.com> | 2014-06-17 18:15:21 -0400 |
---|---|---|
committer | Thomas Winget <tewinget@gmail.com> | 2014-06-30 07:16:50 -0400 |
commit | d433a696e527a01c1cbef48495652335140f0bb2 (patch) | |
tree | 809d9095b3cbeff9b4c4f643e39026820e32c8f8 /src/wallet/wallet2.cpp | |
parent | removed erroneous printing of newlines (diff) | |
download | monero-d433a696e527a01c1cbef48495652335140f0bb2.tar.xz |
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.
Diffstat (limited to 'src/wallet/wallet2.cpp')
-rw-r--r-- | src/wallet/wallet2.cpp | 156 |
1 files changed, 152 insertions, 4 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 7dfbc7f7f..5b284c619 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<cryptonote::tx_destination_entry>& dsts pending_tx ptx; transfer(dsts, fake_outputs_count, unlock_time, fee, extra, tx, ptx); } + +namespace { +// split_amounts(vector<cryptonote::tx_destination_entry> dsts, size_t num_splits) +// +// split amount for each dst in dsts into num_splits parts +// and make num_splits new vector<crypt...> instances to hold these new amounts +std::vector<std::vector<cryptonote::tx_destination_entry>> split_amounts( + std::vector<cryptonote::tx_destination_entry> dsts, size_t num_splits) +{ + std::vector<std::vector<cryptonote::tx_destination_entry>> 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<cryptonote::tx_destination_entry> 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<pending_tx>& 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::pending_tx> wallet2::create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee, const std::vector<uint8_t> 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<pending_tx> 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; + } + } +} } |