aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRiccardo Spagni <ric@spagni.net>2018-09-21 20:46:22 +0200
committerRiccardo Spagni <ric@spagni.net>2018-09-21 20:46:22 +0200
commit85110b42ab5941ab8e71592dfa432d4b0cc99055 (patch)
tree5f6e7b074557cc97d87dcc5999e7dd463d65b06e /src
parentMerge pull request #4399 (diff)
parentREADME: harmonize command formatting inside README.md (diff)
downloadmonero-85110b42ab5941ab8e71592dfa432d4b0cc99055.tar.xz
Merge pull request #4401
66901901 README: harmonize command formatting inside README.md (Andrea) 8cd98408 disable AES on s390x architecture (Tuan M. Hoang) 4ed30bab wallet: implement coin splitting for sweep_* 'outputs' option (whythat) 24f52396 wallet: add 'outputs' option for sweep_* commands (whythat) 52e19d69 README: Compile boost with cxxflags=-fPIC cflags=-fPIC (Italocoin Project) 0c77523d README: fill in libsodium package name for Arch (phloatingman)
Diffstat (limited to 'src')
-rw-r--r--src/simplewallet/simplewallet.cpp52
-rw-r--r--src/wallet/wallet2.cpp40
-rw-r--r--src/wallet/wallet2.h6
-rw-r--r--src/wallet/wallet_rpc_server.cpp18
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h4
5 files changed, 99 insertions, 21 deletions
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index d7fb295b7..7f71ce9f2 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -2311,15 +2311,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=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <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("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. 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",
boost::bind(&simple_wallet::sweep_below, this, _1),
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."));
m_cmd_binder.set_handler("sweep_single",
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."));
m_cmd_binder.set_handler("donate",
boost::bind(&simple_wallet::donate, this, _1),
@@ -5253,7 +5253,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
{
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)
{
@@ -5351,6 +5351,25 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
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;
bool payment_id_seen = false;
if (local_args.size() >= 2)
@@ -5435,7 +5454,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
try
{
// 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())
{
@@ -5576,6 +5595,25 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &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<uint8_t> extra;
bool payment_id_seen = false;
if (local_args.size() == 3)
@@ -5609,7 +5647,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
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;
}
@@ -5664,7 +5702,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &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 4e93309ed..00b40fef8 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -8464,7 +8464,7 @@ skip_tx:
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_dust_indices;
@@ -8515,10 +8515,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_dust_indices;
@@ -8536,10 +8536,10 @@ std::vector<wallet2::pending_tx> 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::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
hw::device &hwdev = m_account.get_device();
@@ -8634,7 +8634,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
needed_fee = estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
- tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress));
+ // add N - 1 outputs for correct initial fee estimation
+ for (size_t i = 0; i < ((outputs > 1) ? outputs - 1 : outputs); ++i)
+ tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress));
LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " destinations and " <<
tx.selected_transfers.size() << " outputs");
@@ -8646,15 +8648,35 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
- available_for_fee = test_ptx.fee + test_ptx.dests[0].amount + test_ptx.change_dts.amount;
+ available_for_fee = test_ptx.fee + test_ptx.change_dts.amount;
+ for (auto &dt: test_ptx.dests)
+ available_for_fee += dt.amount;
LOG_PRINT_L2("Made a " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(available_for_fee) << " available for fee (" <<
print_money(needed_fee) << " needed)");
+ // add last output, missed for fee estimation
+ if (outputs > 1)
+ tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress));
+
THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself");
do {
LOG_PRINT_L2("We made a tx, adjusting fee and saving it");
- tx.dsts[0].amount = available_for_fee - needed_fee;
+ // distribute total transferred amount between outputs
+ uint64_t amount_transferred = available_for_fee - needed_fee;
+ uint64_t dt_amount = amount_transferred / outputs;
+ // residue is distributed as one atomic unit per output until it reaches zero
+ uint64_t residue = amount_transferred % outputs;
+ for (auto &dt: tx.dsts)
+ {
+ uint64_t dt_residue = 0;
+ if (residue > 0)
+ {
+ dt_residue = 1;
+ residue -= 1;
+ }
+ dt.amount = dt_amount + dt_residue;
+ }
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
test_tx, test_ptx, range_proof_type);
@@ -8901,7 +8923,7 @@ std::vector<wallet2::pending_tx> 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<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()
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 9eb9b04f2..acbc09c36 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -752,9 +752,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 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_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_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_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_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 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, 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_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);
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 86b46b173..6361a6cfa 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -1102,6 +1102,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;
@@ -1114,7 +1121,7 @@ namespace tools
mixin = m_wallet->adjust_mixin(req.mixin);
}
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,
res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er);
@@ -1140,6 +1147,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<wallet_rpc::transfer_destination> destination;
destination.push_back(wallet_rpc::transfer_destination());
@@ -1170,7 +1184,7 @@ namespace tools
mixin = m_wallet->adjust_mixin(req.mixin);
}
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())
{
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)