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
This commit is contained in:
whythat 2018-05-06 11:38:47 +03:00 committed by moneromooo-monero
parent 52e19d6955
commit 24f5239693
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3
5 changed files with 74 additions and 18 deletions

View file

@ -2316,15 +2316,15 @@ simple_wallet::simple_wallet()
boost::bind(&simple_wallet::sweep_unmixable, this, _1), boost::bind(&simple_wallet::sweep_unmixable, this, _1),
tr("Send all unmixable outputs to yourself with ring_size 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), m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1),
tr("sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>]"), tr("sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id>]"),
tr("Send all unlocked balance to an address. If the parameter \"index<N1>[,<N2>,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used.")); tr("Send all unlocked balance to an address. If the parameter \"index<N1>[,<N2>,...]\" 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=<N>\" is specified and N > 0, wallet splits the transaction into N even outputs."));
m_cmd_binder.set_handler("sweep_below", m_cmd_binder.set_handler("sweep_below",
boost::bind(&simple_wallet::sweep_below, this, _1), boost::bind(&simple_wallet::sweep_below, this, _1),
tr("sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>]"), tr("sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>]"),
tr("Send all unlocked outputs below the threshold to an address.")); tr("Send all unlocked outputs below the threshold to an address."));
m_cmd_binder.set_handler("sweep_single", m_cmd_binder.set_handler("sweep_single",
boost::bind(&simple_wallet::sweep_single, this, _1), boost::bind(&simple_wallet::sweep_single, this, _1),
tr("sweep_single [<priority>] [<ring_size>] <key_image> <address> [<payment_id>]"), tr("sweep_single [<priority>] [<ring_size>] [outputs=<N>] <key_image> <address> [<payment_id>]"),
tr("Send a single output of the given key image to an address without change.")); tr("Send a single output of the given key image to an address without change."));
m_cmd_binder.set_handler("donate", m_cmd_binder.set_handler("donate",
boost::bind(&simple_wallet::donate, this, _1), boost::bind(&simple_wallet::donate, this, _1),
@ -5262,7 +5262,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
{ {
auto print_usage = [below]() auto print_usage = [below]()
{ {
fail_msg_writer() << boost::format(tr("usage: %s [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>]")) % (below ? "sweep_below" : "sweep_all"); fail_msg_writer() << boost::format(tr("usage: %s [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] [outputs=<N>] <address> [<payment_id>]")) % (below ? "sweep_below" : "sweep_all");
}; };
if (args_.size() == 0) if (args_.size() == 0)
{ {
@ -5360,6 +5360,25 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
local_args.erase(local_args.begin() + 1); local_args.erase(local_args.begin() + 1);
} }
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<uint8_t> extra; std::vector<uint8_t> extra;
bool payment_id_seen = false; bool payment_id_seen = false;
if (local_args.size() >= 2) if (local_args.size() >= 2)
@ -5444,7 +5463,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
try try
{ {
// figure out what tx will be necessary // figure out what tx will be necessary
auto ptx_vector = m_wallet->create_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()) if (ptx_vector.empty())
{ {
@ -5585,6 +5604,25 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
return true; 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<uint8_t> extra; std::vector<uint8_t> extra;
bool payment_id_seen = false; bool payment_id_seen = false;
if (local_args.size() == 3) if (local_args.size() == 3)
@ -5618,7 +5656,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
if (local_args.size() != 2) if (local_args.size() != 2)
{ {
fail_msg_writer() << tr("usage: sweep_single [<priority>] [<ring_size>] <key_image> <address> [<payment_id>]"); fail_msg_writer() << tr("usage: sweep_single [<priority>] [<ring_size>] [outputs=<N>] <key_image> <address> [<payment_id>]");
return true; return true;
} }
@ -5673,7 +5711,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
try try
{ {
// figure out what tx will be necessary // 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()) if (ptx_vector.empty())
{ {

View file

@ -8362,7 +8362,7 @@ skip_tx:
return ptx_vector; return ptx_vector;
} }
std::vector<wallet2::pending_tx> 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<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices) std::vector<wallet2::pending_tx> 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<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices)
{ {
std::vector<size_t> unused_transfers_indices; std::vector<size_t> unused_transfers_indices;
std::vector<size_t> unused_dust_indices; std::vector<size_t> unused_dust_indices;
@ -8413,10 +8413,10 @@ std::vector<wallet2::pending_tx> 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::pending_tx> 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<uint8_t>& extra) std::vector<wallet2::pending_tx> 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<uint8_t>& extra)
{ {
std::vector<size_t> unused_transfers_indices; std::vector<size_t> unused_transfers_indices;
std::vector<size_t> unused_dust_indices; std::vector<size_t> unused_dust_indices;
@ -8434,10 +8434,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_single(const crypt
break; 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::pending_tx> wallet2::create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra) std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra)
{ {
//ensure device is let in NONE mode in any case //ensure device is let in NONE mode in any case
hw::device &hwdev = m_account.get_device(); hw::device &hwdev = m_account.get_device();
@ -8799,7 +8799,7 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions()
unmixable_transfer_outputs.push_back(n); 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<uint8_t>()); 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<uint8_t>());
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void wallet2::discard_unmixable_outputs() void wallet2::discard_unmixable_outputs()

View file

@ -748,9 +748,9 @@ namespace tools
bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL); bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL);
bool parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func); bool parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func);
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices); // pass subaddr_indices by value on purpose std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices); // pass subaddr_indices by value on purpose
std::vector<wallet2::pending_tx> 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<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices); std::vector<wallet2::pending_tx> 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<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices);
std::vector<wallet2::pending_tx> 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<uint8_t>& extra); std::vector<wallet2::pending_tx> 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<uint8_t>& extra);
std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra); std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
bool load_multisig_tx(cryptonote::blobdata blob, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL); bool load_multisig_tx(cryptonote::blobdata blob, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL);
bool load_multisig_tx_from_file(const std::string &filename, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL); bool load_multisig_tx_from_file(const std::string &filename, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL);
bool sign_multisig_tx_from_file(const std::string &filename, std::vector<crypto::hash> &txids, std::function<bool(const multisig_tx_set&)> accept_func); bool sign_multisig_tx_from_file(const std::string &filename, std::vector<crypto::hash> &txids, std::function<bool(const multisig_tx_set&)> accept_func);

View file

@ -1101,6 +1101,13 @@ namespace tools
return false; 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 try
{ {
uint64_t mixin; uint64_t mixin;
@ -1113,7 +1120,7 @@ namespace tools
mixin = m_wallet->adjust_mixin(req.mixin); mixin = m_wallet->adjust_mixin(req.mixin);
} }
uint32_t priority = m_wallet->adjust_priority(req.priority); uint32_t priority = m_wallet->adjust_priority(req.priority);
std::vector<wallet2::pending_tx> 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<wallet2::pending_tx> 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, 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); 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; 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 // validate the transfer requested and populate dsts & extra
std::list<wallet_rpc::transfer_destination> destination; std::list<wallet_rpc::transfer_destination> destination;
destination.push_back(wallet_rpc::transfer_destination()); destination.push_back(wallet_rpc::transfer_destination());
@ -1169,7 +1183,7 @@ namespace tools
mixin = m_wallet->adjust_mixin(req.mixin); mixin = m_wallet->adjust_mixin(req.mixin);
} }
uint32_t priority = m_wallet->adjust_priority(req.priority); uint32_t priority = m_wallet->adjust_priority(req.priority);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_single(ki, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, priority, extra); std::vector<wallet2::pending_tx> 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()) if (ptx_vector.empty())
{ {

View file

@ -639,6 +639,7 @@ namespace wallet_rpc
uint32_t priority; uint32_t priority;
uint64_t mixin; uint64_t mixin;
uint64_t ring_size; uint64_t ring_size;
uint64_t outputs;
uint64_t unlock_time; uint64_t unlock_time;
std::string payment_id; std::string payment_id;
bool get_tx_keys; bool get_tx_keys;
@ -654,6 +655,7 @@ namespace wallet_rpc
KV_SERIALIZE(priority) KV_SERIALIZE(priority)
KV_SERIALIZE_OPT(mixin, (uint64_t)0) KV_SERIALIZE_OPT(mixin, (uint64_t)0)
KV_SERIALIZE_OPT(ring_size, (uint64_t)0) KV_SERIALIZE_OPT(ring_size, (uint64_t)0)
KV_SERIALIZE_OPT(outputs, (uint64_t)1)
KV_SERIALIZE(unlock_time) KV_SERIALIZE(unlock_time)
KV_SERIALIZE(payment_id) KV_SERIALIZE(payment_id)
KV_SERIALIZE(get_tx_keys) KV_SERIALIZE(get_tx_keys)
@ -705,6 +707,7 @@ namespace wallet_rpc
uint32_t priority; uint32_t priority;
uint64_t mixin; uint64_t mixin;
uint64_t ring_size; uint64_t ring_size;
uint64_t outputs;
uint64_t unlock_time; uint64_t unlock_time;
std::string payment_id; std::string payment_id;
bool get_tx_key; bool get_tx_key;
@ -718,6 +721,7 @@ namespace wallet_rpc
KV_SERIALIZE(priority) KV_SERIALIZE(priority)
KV_SERIALIZE_OPT(mixin, (uint64_t)0) KV_SERIALIZE_OPT(mixin, (uint64_t)0)
KV_SERIALIZE_OPT(ring_size, (uint64_t)0) KV_SERIALIZE_OPT(ring_size, (uint64_t)0)
KV_SERIALIZE_OPT(outputs, (uint64_t)1)
KV_SERIALIZE(unlock_time) KV_SERIALIZE(unlock_time)
KV_SERIALIZE(payment_id) KV_SERIALIZE(payment_id)
KV_SERIALIZE(get_tx_key) KV_SERIALIZE(get_tx_key)