aboutsummaryrefslogtreecommitdiff
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
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)
-rw-r--r--CMakeLists.txt8
-rw-r--r--README.md52
-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
7 files changed, 155 insertions, 25 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 039a1bf71..a95e14b65 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -154,6 +154,10 @@ if(ARCH_ID STREQUAL "powerpc")
set(PPC 1)
endif()
+if(ARCH_ID STREQUAL "s390x")
+ set(S390X 1)
+endif()
+
if(WIN32 OR ARM OR PPC64LE OR PPC64 OR PPC)
set(OPT_FLAGS_RELEASE "-O2")
else()
@@ -641,12 +645,14 @@ else()
message(STATUS "AES support explicitly disabled")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNO_AES")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNO_AES")
- elseif(NOT ARM AND NOT PPC64LE AND NOT PPC64 AND NOT PPC)
+ elseif(NOT ARM AND NOT PPC64LE AND NOT PPC64 AND NOT PPC AND NOT S390X)
message(STATUS "AES support enabled")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -maes")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes")
elseif(PPC64LE OR PPC64 OR PPC)
message(STATUS "AES support not available on POWER")
+ elseif(S390X)
+ message(STATUS "AES support not available on s390x")
elseif(ARM6)
message(STATUS "AES support not available on ARMv6")
elseif(ARM7)
diff --git a/README.md b/README.md
index 325782b01..c9e8e7aa3 100644
--- a/README.md
+++ b/README.md
@@ -118,6 +118,52 @@ X's indicate that these details have not been determined as of commit date.
Approximately three months prior to a scheduled software upgrade, a branch from Master will be created with the new release version tag. Pull requests that address bugs should then be made to both Master and the new release branch. Pull requests that require extensive review and testing (generally, optimizations and new features) should *not* be made to the release branch.
+<<<<<<< HEAD
+=======
+## Installing Monero from a package
+
+Packages are available for
+
+* Ubuntu and [snap supported](https://snapcraft.io/docs/core/install) systems, via a community contributed build.
+
+ snap install monero --beta
+
+Installing a snap is very quick. Snaps are secure. They are isolated with all of their dependencies. Snaps also auto update when a new version is released.
+
+* Arch Linux (via [AUR](https://aur.archlinux.org/)):
+ - Stable release: [`monero`](https://aur.archlinux.org/packages/monero)
+ - Bleeding edge: [`monero-git`](https://aur.archlinux.org/packages/monero-git)
+
+* Void Linux:
+
+ xbps-install -S monero
+
+* GuixSD
+
+ guix package -i monero
+
+* OS X via [Homebrew](http://brew.sh)
+
+ brew tap sammy007/cryptonight
+ brew install monero --build-from-source
+
+* Docker
+
+ # Build using all available cores
+ docker build -t monero .
+
+ # or build using a specific number of cores (reduce RAM requirement)
+ docker build --build-arg NPROC=1 -t monero .
+
+ # either run in foreground
+ docker run -it -v /monero/chain:/root/.bitmonero -v /monero/wallet:/wallet -p 18080:18080 monero
+
+ # or in background
+ docker run -it -d -v /monero/chain:/root/.bitmonero -v /monero/wallet:/wallet -p 18080:18080 monero
+
+Packaging for your favorite distribution would be a welcome contribution!
+
+>>>>>>> f6d62ab... Formating commands inside README.md
## Compiling Monero from source
### Dependencies
@@ -141,7 +187,7 @@ library archives (`.a`).
| libzmq | 3.0.0 | NO | `libzmq3-dev` | `zeromq` | `cppzmq-devel` | NO | ZeroMQ library |
| OpenPGM | ? | NO | `libpgm-dev` | `libpgm` | `openpgm-devel` | NO | For ZeroMQ |
| libunbound | 1.4.16 | YES | `libunbound-dev` | `unbound` | `unbound-devel` | NO | DNS resolver |
-| libsodium | ? | NO | `libsodium-dev` | ? | `libsodium-devel` | NO | cryptography |
+| libsodium | ? | NO | `libsodium-dev` | `libsodium` | `libsodium-devel` | NO | cryptography |
| libunwind | any | NO | `libunwind8-dev` | `libunwind` | `libunwind-devel` | YES | Stack traces |
| liblzma | any | NO | `liblzma-dev` | `xz` | `xz-devel` | YES | For libunwind |
| libreadline | 6.3.0 | NO | `libreadline6-dev` | `readline` | `readline-devel` | YES | Input editing |
@@ -284,7 +330,7 @@ If you are using the older Raspbian Jessie image, compiling Monero is a bit more
```
* Wait ~8 hours
```
- sudo ./bjam install
+ sudo ./bjam cxxflags=-fPIC cflags=-fPIC -a install
```
* Wait ~4 hours
@@ -499,7 +545,7 @@ Packages are available for
* Ubuntu and [snap supported](https://snapcraft.io/docs/core/install) systems, via a community contributed build.
- snap install monero --beta
+ snap install monero --beta
Installing a snap is very quick. Snaps are secure. They are isolated with all of their dependencies. Snaps also auto update when a new version is released.
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)