aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet2.cpp
diff options
context:
space:
mode:
authorThomas Winget <tewinget@gmail.com>2014-06-17 18:15:21 -0400
committerThomas Winget <tewinget@gmail.com>2014-06-30 07:16:50 -0400
commitd433a696e527a01c1cbef48495652335140f0bb2 (patch)
tree809d9095b3cbeff9b4c4f643e39026820e32c8f8 /src/wallet/wallet2.cpp
parentremoved erroneous printing of newlines (diff)
downloadmonero-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.cpp156
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;
+ }
+ }
+}
}