From 24f52396935f7d0624afaf88c6a4b5f2bf5e6932 Mon Sep 17 00:00:00 2001 From: whythat Date: Sun, 6 May 2018 11:38:47 +0300 Subject: [PATCH] wallet: add 'outputs' option for sweep_* commands 'outputs' option allows to specify the number of separate outputs of smaller denomination that will be created by sweep operation. rebased by moneromooo --- src/simplewallet/simplewallet.cpp | 52 +++++++++++++++++--- src/wallet/wallet2.cpp | 12 ++--- src/wallet/wallet2.h | 6 +-- src/wallet/wallet_rpc_server.cpp | 18 ++++++- src/wallet/wallet_rpc_server_commands_defs.h | 4 ++ 5 files changed, 74 insertions(+), 18 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index e535b6d27..24903721a 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2316,15 +2316,15 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with ring_size 1")); m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), - tr("sweep_all [index=[,,...]] [] []
[]"), - tr("Send all unlocked balance to an address. If the parameter \"index[,,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used.")); + tr("sweep_all [index=[,,...]] [] [] [outputs=]
[]"), + tr("Send all unlocked balance to an address. If the parameter \"index[,,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used. If the parameter \"outputs=\" is specified and N > 0, wallet splits the transaction into N even outputs.")); m_cmd_binder.set_handler("sweep_below", boost::bind(&simple_wallet::sweep_below, this, _1), tr("sweep_below [index=[,,...]] [] []
[]"), tr("Send all unlocked outputs below the threshold to an address.")); m_cmd_binder.set_handler("sweep_single", boost::bind(&simple_wallet::sweep_single, this, _1), - tr("sweep_single [] []
[]"), + tr("sweep_single [] [] [outputs=]
[]"), tr("Send a single output of the given key image to an address without change.")); m_cmd_binder.set_handler("donate", boost::bind(&simple_wallet::donate, this, _1), @@ -5262,7 +5262,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector[,,...]] [] []
[]")) % (below ? "sweep_below" : "sweep_all"); + fail_msg_writer() << boost::format(tr("usage: %s [index=[,,...]] [] [] [outputs=]
[]")) % (below ? "sweep_below" : "sweep_all"); }; if (args_.size() == 0) { @@ -5360,6 +5360,25 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector 0 && local_args[0].substr(0, 8) == "outputs=") + { + if (!epee::string_tools::get_xtype_from_string(outputs, local_args[0].substr(8))) + { + fail_msg_writer() << tr("Failed to parse number of outputs"); + return true; + } + else if (outputs < 1) + { + fail_msg_writer() << tr("Amount of outputs should be greater than 0"); + return true; + } + else + { + local_args.erase(local_args.begin()); + } + } + std::vector extra; bool payment_id_seen = false; if (local_args.size() >= 2) @@ -5444,7 +5463,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vectorcreate_transactions_all(below, info.address, info.is_subaddress, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices); + auto ptx_vector = m_wallet->create_transactions_all(below, info.address, info.is_subaddress, outputs, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices); if (ptx_vector.empty()) { @@ -5585,6 +5604,25 @@ bool simple_wallet::sweep_single(const std::vector &args_) return true; } + size_t outputs = 1; + if (local_args.size() > 0 && local_args[0].substr(0, 8) == "outputs=") + { + if (!epee::string_tools::get_xtype_from_string(outputs, local_args[0].substr(8))) + { + fail_msg_writer() << tr("Failed to parse number of outputs"); + return true; + } + else if (outputs < 1) + { + fail_msg_writer() << tr("Amount of outputs should be greater than 0"); + return true; + } + else + { + local_args.erase(local_args.begin()); + } + } + std::vector extra; bool payment_id_seen = false; if (local_args.size() == 3) @@ -5618,7 +5656,7 @@ bool simple_wallet::sweep_single(const std::vector &args_) if (local_args.size() != 2) { - fail_msg_writer() << tr("usage: sweep_single [] []
[]"); + fail_msg_writer() << tr("usage: sweep_single [] [] [outputs=]
[]"); return true; } @@ -5673,7 +5711,7 @@ bool simple_wallet::sweep_single(const std::vector &args_) try { // figure out what tx will be necessary - auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, fake_outs_count, 0 /* unlock_time */, priority, extra); + auto ptx_vector = m_wallet->create_transactions_single(ki, info.address, info.is_subaddress, outputs, fake_outs_count, 0 /* unlock_time */, priority, extra); if (ptx_vector.empty()) { diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index b266821d6..fad904031 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -8362,7 +8362,7 @@ skip_tx: return ptx_vector; } -std::vector wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices) +std::vector wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices) { std::vector unused_transfers_indices; std::vector unused_dust_indices; @@ -8413,10 +8413,10 @@ std::vector wallet2::create_transactions_all(uint64_t below } } - return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra); + return create_transactions_from(address, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra); } -std::vector wallet2::create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra) +std::vector wallet2::create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra) { std::vector unused_transfers_indices; std::vector unused_dust_indices; @@ -8434,10 +8434,10 @@ std::vector wallet2::create_transactions_single(const crypt break; } } - return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra); + return create_transactions_from(address, is_subaddress, outputs, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra); } -std::vector wallet2::create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector unused_transfers_indices, std::vector unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra) +std::vector wallet2::create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector unused_transfers_indices, std::vector unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra) { //ensure device is let in NONE mode in any case hw::device &hwdev = m_account.get_device(); @@ -8799,7 +8799,7 @@ std::vector wallet2::create_unmixable_sweep_transactions() unmixable_transfer_outputs.push_back(n); } - return create_transactions_from(m_account_public_address, false, unmixable_transfer_outputs, unmixable_dust_outputs, 0 /*fake_outs_count */, 0 /* unlock_time */, 1 /*priority */, std::vector()); + return create_transactions_from(m_account_public_address, false, 1, unmixable_transfer_outputs, unmixable_dust_outputs, 0 /*fake_outs_count */, 0 /* unlock_time */, 1 /*priority */, std::vector()); } //---------------------------------------------------------------------------------------------------- void wallet2::discard_unmixable_outputs() diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 402066b50..62a72f461 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -748,9 +748,9 @@ namespace tools bool load_tx(const std::string &signed_filename, std::vector &ptx, std::function accept_func = NULL); bool parse_tx_from_str(const std::string &signed_tx_st, std::vector &ptx, std::function accept_func); std::vector create_transactions_2(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices); // pass subaddr_indices by value on purpose - std::vector create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices); - std::vector create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra); - std::vector create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector unused_transfers_indices, std::vector unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra); + std::vector create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices); + std::vector create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra); + std::vector create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector unused_transfers_indices, std::vector unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector& extra); bool load_multisig_tx(cryptonote::blobdata blob, multisig_tx_set &exported_txs, std::function accept_func = NULL); bool load_multisig_tx_from_file(const std::string &filename, multisig_tx_set &exported_txs, std::function accept_func = NULL); bool sign_multisig_tx_from_file(const std::string &filename, std::vector &txids, std::function accept_func); diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index ee86587bc..a2cbbd391 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1101,6 +1101,13 @@ namespace tools return false; } + if (req.outputs < 1) + { + er.code = WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE; + er.message = "Amount of outputs should be greater than 0."; + return false; + } + try { uint64_t mixin; @@ -1113,7 +1120,7 @@ namespace tools mixin = m_wallet->adjust_mixin(req.mixin); } uint32_t priority = m_wallet->adjust_priority(req.priority); - std::vector ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices); + std::vector ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices); return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay, res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er); @@ -1139,6 +1146,13 @@ namespace tools return false; } + if (req.outputs < 1) + { + er.code = WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE; + er.message = "Amount of outputs should be greater than 0."; + return false; + } + // validate the transfer requested and populate dsts & extra std::list destination; destination.push_back(wallet_rpc::transfer_destination()); @@ -1169,7 +1183,7 @@ namespace tools mixin = m_wallet->adjust_mixin(req.mixin); } uint32_t priority = m_wallet->adjust_priority(req.priority); - std::vector ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra); + std::vector ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, req.outputs, mixin, req.unlock_time, priority, extra); if (ptx_vector.empty()) { diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 4501cf575..5d9c22971 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -639,6 +639,7 @@ namespace wallet_rpc uint32_t priority; uint64_t mixin; uint64_t ring_size; + uint64_t outputs; uint64_t unlock_time; std::string payment_id; bool get_tx_keys; @@ -654,6 +655,7 @@ namespace wallet_rpc KV_SERIALIZE(priority) KV_SERIALIZE_OPT(mixin, (uint64_t)0) KV_SERIALIZE_OPT(ring_size, (uint64_t)0) + KV_SERIALIZE_OPT(outputs, (uint64_t)1) KV_SERIALIZE(unlock_time) KV_SERIALIZE(payment_id) KV_SERIALIZE(get_tx_keys) @@ -705,6 +707,7 @@ namespace wallet_rpc uint32_t priority; uint64_t mixin; uint64_t ring_size; + uint64_t outputs; uint64_t unlock_time; std::string payment_id; bool get_tx_key; @@ -718,6 +721,7 @@ namespace wallet_rpc KV_SERIALIZE(priority) KV_SERIALIZE_OPT(mixin, (uint64_t)0) KV_SERIALIZE_OPT(ring_size, (uint64_t)0) + KV_SERIALIZE_OPT(outputs, (uint64_t)1) KV_SERIALIZE(unlock_time) KV_SERIALIZE(payment_id) KV_SERIALIZE(get_tx_key)