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)
This commit is contained in:
Riccardo Spagni 2018-09-21 20:46:22 +02:00
commit 85110b42ab
No known key found for this signature in database
GPG key ID: 55432DF31CCD4FCD
7 changed files with 155 additions and 25 deletions

View file

@ -154,6 +154,10 @@ if(ARCH_ID STREQUAL "powerpc")
set(PPC 1) set(PPC 1)
endif() endif()
if(ARCH_ID STREQUAL "s390x")
set(S390X 1)
endif()
if(WIN32 OR ARM OR PPC64LE OR PPC64 OR PPC) if(WIN32 OR ARM OR PPC64LE OR PPC64 OR PPC)
set(OPT_FLAGS_RELEASE "-O2") set(OPT_FLAGS_RELEASE "-O2")
else() else()
@ -641,12 +645,14 @@ else()
message(STATUS "AES support explicitly disabled") message(STATUS "AES support explicitly disabled")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNO_AES") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNO_AES")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_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") message(STATUS "AES support enabled")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -maes") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -maes")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes")
elseif(PPC64LE OR PPC64 OR PPC) elseif(PPC64LE OR PPC64 OR PPC)
message(STATUS "AES support not available on POWER") message(STATUS "AES support not available on POWER")
elseif(S390X)
message(STATUS "AES support not available on s390x")
elseif(ARM6) elseif(ARM6)
message(STATUS "AES support not available on ARMv6") message(STATUS "AES support not available on ARMv6")
elseif(ARM7) elseif(ARM7)

View file

@ -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. 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 ## Compiling Monero from source
### Dependencies ### Dependencies
@ -141,7 +187,7 @@ library archives (`.a`).
| libzmq | 3.0.0 | NO | `libzmq3-dev` | `zeromq` | `cppzmq-devel` | NO | ZeroMQ library | | libzmq | 3.0.0 | NO | `libzmq3-dev` | `zeromq` | `cppzmq-devel` | NO | ZeroMQ library |
| OpenPGM | ? | NO | `libpgm-dev` | `libpgm` | `openpgm-devel` | NO | For ZeroMQ | | OpenPGM | ? | NO | `libpgm-dev` | `libpgm` | `openpgm-devel` | NO | For ZeroMQ |
| libunbound | 1.4.16 | YES | `libunbound-dev` | `unbound` | `unbound-devel` | NO | DNS resolver | | 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 | | libunwind | any | NO | `libunwind8-dev` | `libunwind` | `libunwind-devel` | YES | Stack traces |
| liblzma | any | NO | `liblzma-dev` | `xz` | `xz-devel` | YES | For libunwind | | liblzma | any | NO | `liblzma-dev` | `xz` | `xz-devel` | YES | For libunwind |
| libreadline | 6.3.0 | NO | `libreadline6-dev` | `readline` | `readline-devel` | YES | Input editing | | 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 * Wait ~8 hours
``` ```
sudo ./bjam install sudo ./bjam cxxflags=-fPIC cflags=-fPIC -a install
``` ```
* Wait ~4 hours * 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. * 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. 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.

View file

@ -2311,15 +2311,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),
@ -5253,7 +5253,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)
{ {
@ -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); 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)
@ -5435,7 +5454,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())
{ {
@ -5576,6 +5595,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)
@ -5609,7 +5647,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;
} }
@ -5664,7 +5702,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

@ -8464,7 +8464,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;
@ -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_transfers_indices;
std::vector<size_t> unused_dust_indices; std::vector<size_t> unused_dust_indices;
@ -8536,10 +8536,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();
@ -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); 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 " << LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " destinations and " <<
tx.selected_transfers.size() << " outputs"); 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); 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); 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); 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 (" << 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)"); 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"); THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself");
do { do {
LOG_PRINT_L2("We made a tx, adjusting fee and saving it"); 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) if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
test_tx, test_ptx, range_proof_type); 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); 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

@ -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 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

@ -1102,6 +1102,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;
@ -1114,7 +1121,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);
@ -1140,6 +1147,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());
@ -1170,7 +1184,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)