aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp6
-rw-r--r--src/cryptonote_core/cryptonote_core.h3
-rw-r--r--src/daemon/command_line_args.h10
-rw-r--r--src/daemon/core.h10
-rw-r--r--src/daemon/main.cpp2
-rw-r--r--src/daemon/p2p.h3
-rw-r--r--src/daemon/rpc.h2
-rw-r--r--src/device/device.hpp1
-rw-r--r--src/net/socks.h7
-rw-r--r--src/p2p/net_node.cpp8
-rw-r--r--src/p2p/net_node.h4
-rw-r--r--src/p2p/net_node.inl19
-rw-r--r--src/ringct/multiexp.cc2
-rw-r--r--src/rpc/core_rpc_server.cpp6
-rw-r--r--src/rpc/core_rpc_server.h4
-rw-r--r--src/wallet/wallet2.cpp73
-rw-r--r--src/wallet/wallet_args.cpp4
-rw-r--r--src/wallet/wallet_args.h1
-rw-r--r--src/wallet/wallet_rpc_server.cpp11
20 files changed, 135 insertions, 43 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 8e427b6b8..99d9bd8bf 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -99,7 +99,7 @@ monero_add_library(common
target_link_libraries(common
PUBLIC
cncrypto
- ${UNBOUND_LIBRARY}
+ ${UNBOUND_LIBRARIES}
${LIBUNWIND_LIBRARIES}
${Boost_DATE_TIME_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 3b7ccb157..4c6536318 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -388,6 +388,7 @@ namespace cryptonote
m_fluffy_blocks_enabled = !get_arg(vm, arg_no_fluffy_blocks);
m_offline = get_arg(vm, arg_offline);
m_disable_dns_checkpoints = get_arg(vm, arg_disable_dns_checkpoints);
+
if (!command_line::is_arg_defaulted(vm, arg_fluffy_blocks))
MWARNING(arg_fluffy_blocks.name << " is obsolete, it is now default");
@@ -460,7 +461,7 @@ namespace cryptonote
return m_blockchain_storage.get_alternative_blocks_count();
}
//-----------------------------------------------------------------------------------------------
- bool core::init(const boost::program_options::variables_map& vm, const cryptonote::test_options *test_options, const GetCheckpointsCallback& get_checkpoints/* = nullptr */)
+ bool core::init(const boost::program_options::variables_map& vm, const cryptonote::test_options *test_options, const GetCheckpointsCallback& get_checkpoints/* = nullptr */, bool allow_dns)
{
start_time = std::time(nullptr);
@@ -471,6 +472,7 @@ namespace cryptonote
}
bool r = handle_command_line(vm);
CHECK_AND_ASSERT_MES(r, false, "Failed to handle command line");
+ m_disable_dns_checkpoints |= not allow_dns;
std::string db_sync_mode = command_line::get_arg(vm, cryptonote::arg_db_sync_mode);
bool db_salvage = command_line::get_arg(vm, cryptonote::arg_db_salvage) != 0;
@@ -697,7 +699,7 @@ namespace cryptonote
CHECK_AND_ASSERT_MES(update_checkpoints(skip_dns_checkpoints), false, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints.");
// DNS versions checking
- if (check_updates_string == "disabled")
+ if (check_updates_string == "disabled" || not allow_dns)
check_updates_level = UPDATES_DISABLED;
else if (check_updates_string == "notify")
check_updates_level = UPDATES_NOTIFY;
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index dd813733f..d2bffdaee 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -283,10 +283,11 @@ namespace cryptonote
* @param vm command line parameters
* @param test_options configuration options for testing
* @param get_checkpoints if set, will be called to get checkpoints data, must return checkpoints data pointer and size or nullptr if there ain't any checkpoints for specific network type
+ * @param allow_dns whether or not to allow DNS requests
*
* @return false if one of the init steps fails, otherwise true
*/
- bool init(const boost::program_options::variables_map& vm, const test_options *test_options = NULL, const GetCheckpointsCallback& get_checkpoints = nullptr);
+ bool init(const boost::program_options::variables_map& vm, const test_options *test_options = NULL, const GetCheckpointsCallback& get_checkpoints = nullptr, bool allow_dns = true);
/**
* @copydoc Blockchain::reset_and_set_genesis_block
diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h
index 6c3e163e6..a988fe25f 100644
--- a/src/daemon/command_line_args.h
+++ b/src/daemon/command_line_args.h
@@ -96,6 +96,16 @@ namespace daemon_args
, 0
};
+ const command_line::arg_descriptor<std::string> arg_proxy = {
+ "proxy",
+ "Network communication through proxy: <socks-ip:port> i.e. \"127.0.0.1:9050\"",
+ "",
+ };
+ const command_line::arg_descriptor<bool> arg_proxy_allow_dns_leaks = {
+ "proxy-allow-dns-leaks",
+ "Allow DNS leaks outside of proxy",
+ false,
+ };
const command_line::arg_descriptor<bool> arg_public_node = {
"public-node"
, "Allow other users to use the node as a remote (restricted RPC mode, view-only commands) and advertise it over P2P"
diff --git a/src/daemon/core.h b/src/daemon/core.h
index 804d7474d..0811cf420 100644
--- a/src/daemon/core.h
+++ b/src/daemon/core.h
@@ -32,6 +32,7 @@
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_protocol/cryptonote_protocol_handler.h"
#include "misc_log_ex.h"
+#include "daemon/command_line_args.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "daemon"
@@ -66,7 +67,14 @@ public:
#else
const cryptonote::GetCheckpointsCallback& get_checkpoints = nullptr;
#endif
- if (!m_core.init(m_vm_HACK, nullptr, get_checkpoints))
+
+ if (command_line::is_arg_defaulted(vm, daemon_args::arg_proxy) && command_line::get_arg(vm, daemon_args::arg_proxy_allow_dns_leaks)) {
+ MLOG_RED(el::Level::Warning, "--" << daemon_args::arg_proxy_allow_dns_leaks.name << " is enabled, but --"
+ << daemon_args::arg_proxy.name << " is not specified.");
+ }
+
+ const bool allow_dns = command_line::is_arg_defaulted(vm, daemon_args::arg_proxy) || command_line::get_arg(vm, daemon_args::arg_proxy_allow_dns_leaks);
+ if (!m_core.init(m_vm_HACK, nullptr, get_checkpoints, allow_dns))
{
throw std::runtime_error("Failed to initialize core");
}
diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp
index d413906df..70aec5538 100644
--- a/src/daemon/main.cpp
+++ b/src/daemon/main.cpp
@@ -152,6 +152,8 @@ int main(int argc, char const * argv[])
command_line::add_arg(core_settings, daemon_args::arg_max_log_file_size);
command_line::add_arg(core_settings, daemon_args::arg_max_log_files);
command_line::add_arg(core_settings, daemon_args::arg_max_concurrency);
+ command_line::add_arg(core_settings, daemon_args::arg_proxy);
+ command_line::add_arg(core_settings, daemon_args::arg_proxy_allow_dns_leaks);
command_line::add_arg(core_settings, daemon_args::arg_public_node);
command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_ip);
command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_port);
diff --git a/src/daemon/p2p.h b/src/daemon/p2p.h
index f68efccc2..38862c017 100644
--- a/src/daemon/p2p.h
+++ b/src/daemon/p2p.h
@@ -33,6 +33,7 @@
#include "cryptonote_protocol/cryptonote_protocol_handler.h"
#include "p2p/net_node.h"
#include "daemon/protocol.h"
+#include "daemon/command_line_args.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "daemon"
@@ -61,7 +62,7 @@ public:
{
//initialize objects
MGINFO("Initializing p2p server...");
- if (!m_server.init(vm))
+ if (!m_server.init(vm, command_line::get_arg(vm, daemon_args::arg_proxy), command_line::get_arg(vm, daemon_args::arg_proxy_allow_dns_leaks)))
{
throw std::runtime_error("Failed to initialize p2p server.");
}
diff --git a/src/daemon/rpc.h b/src/daemon/rpc.h
index af48bcc45..bff7dc449 100644
--- a/src/daemon/rpc.h
+++ b/src/daemon/rpc.h
@@ -62,7 +62,7 @@ public:
{
MGINFO("Initializing " << m_description << " RPC server...");
- if (!m_server.init(vm, restricted, port, allow_rpc_payment))
+ if (!m_server.init(vm, restricted, port, allow_rpc_payment, command_line::get_arg(vm, daemon_args::arg_proxy)))
{
throw std::runtime_error("Failed to initialize " + m_description + " RPC server.");
}
diff --git a/src/device/device.hpp b/src/device/device.hpp
index 582eb2242..6005e157d 100644
--- a/src/device/device.hpp
+++ b/src/device/device.hpp
@@ -91,7 +91,6 @@ namespace hw {
public:
device(): mode(NONE) {}
- device(const device &hwdev) {}
virtual ~device() {}
explicit virtual operator bool() const = 0;
diff --git a/src/net/socks.h b/src/net/socks.h
index 739c972ab..506b53195 100644
--- a/src/net/socks.h
+++ b/src/net/socks.h
@@ -201,6 +201,13 @@ namespace socks
std::shared_ptr<client> self_;
void operator()(boost::system::error_code error = boost::system::error_code{});
};
+
+ //! Calls `async_close` on `self` at destruction. NOP if `nullptr`.
+ struct close_on_exit
+ {
+ std::shared_ptr<client> self;
+ ~close_on_exit() { async_close{std::move(self)}(); }
+ };
};
template<typename Handler>
diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp
index 84cc1581e..d9050200a 100644
--- a/src/p2p/net_node.cpp
+++ b/src/p2p/net_node.cpp
@@ -94,6 +94,9 @@ namespace
case net::i2p_address::get_type_id():
set = client->set_connect_command(remote.as<net::i2p_address>());
break;
+ case epee::net_utils::ipv4_network_address::get_type_id():
+ set = client->set_connect_command(remote.as<epee::net_utils::ipv4_network_address>());
+ break;
default:
MERROR("Unsupported network address in socks_connect");
return false;
@@ -339,6 +342,7 @@ namespace nodetool
}
};
+ net::socks::client::close_on_exit close_client{};
boost::unique_future<client_result> socks_result{};
{
boost::promise<client_result> socks_promise{};
@@ -347,6 +351,7 @@ namespace nodetool
auto client = net::socks::make_connect_client(
boost::asio::ip::tcp::socket{service}, net::socks::version::v4a, notify{std::move(socks_promise)}
);
+ close_client.self = client;
if (!start_socks(std::move(client), proxy, remote))
return boost::none;
}
@@ -368,7 +373,10 @@ namespace nodetool
{
auto result = socks_result.get();
if (!result.first)
+ {
+ close_client.self.reset();
return {std::move(result.second)};
+ }
MERROR("Failed to make socks connection to " << remote.str() << " (via " << proxy << "): " << result.first.message());
}
diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h
index f2888674b..9e64121be 100644
--- a/src/p2p/net_node.h
+++ b/src/p2p/net_node.h
@@ -259,6 +259,7 @@ namespace nodetool
m_offline(false),
is_closing(false),
m_network_id(),
+ m_enable_dns_seed_nodes(true),
max_connections(1)
{}
virtual ~node_server();
@@ -267,7 +268,7 @@ namespace nodetool
bool run();
network_zone& add_zone(epee::net_utils::zone zone);
- bool init(const boost::program_options::variables_map& vm);
+ bool init(const boost::program_options::variables_map& vm, const std::string& proxy = {}, bool proxy_dns_leaks_allowed = {});
bool deinit();
bool send_stop_signal();
uint32_t get_this_peer_port(){return m_listening_port;}
@@ -516,6 +517,7 @@ namespace nodetool
epee::net_utils::ssl_support_t m_ssl_support;
+ bool m_enable_dns_seed_nodes;
bool m_enable_dns_blocklist;
uint32_t max_connections;
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index cfeac3d37..ac65a57c1 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -741,6 +741,12 @@ namespace nodetool
{
return get_ip_seed_nodes();
}
+ if (!m_enable_dns_seed_nodes)
+ {
+ // TODO: a domain can be set through socks, so that the remote side does the lookup for the DNS seed nodes.
+ m_fallback_seed_nodes_added.test_and_set();
+ return get_ip_seed_nodes();
+ }
std::set<std::string> full_addrs;
@@ -880,10 +886,21 @@ namespace nodetool
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
- bool node_server<t_payload_net_handler>::init(const boost::program_options::variables_map& vm)
+ bool node_server<t_payload_net_handler>::init(const boost::program_options::variables_map& vm, const std::string& proxy, bool proxy_dns_leaks_allowed)
{
bool res = handle_command_line(vm);
CHECK_AND_ASSERT_MES(res, false, "Failed to handle command line");
+ if (proxy.size())
+ {
+ const auto endpoint = net::get_tcp_endpoint(proxy);
+ CHECK_AND_ASSERT_MES(endpoint, false, "Failed to parse proxy: " << proxy << " - " << endpoint.error());
+ network_zone& public_zone = m_network_zones[epee::net_utils::zone::public_];
+ public_zone.m_connect = &socks_connect;
+ public_zone.m_proxy_address = *endpoint;
+ public_zone.m_can_pingback = false;
+ m_enable_dns_seed_nodes &= proxy_dns_leaks_allowed;
+ m_enable_dns_blocklist &= proxy_dns_leaks_allowed;
+ }
if (m_nettype == cryptonote::TESTNET)
{
diff --git a/src/ringct/multiexp.cc b/src/ringct/multiexp.cc
index 620f7d0dd..784c90a4e 100644
--- a/src/ringct/multiexp.cc
+++ b/src/ringct/multiexp.cc
@@ -449,7 +449,6 @@ rct::key straus(const std::vector<MultiexpData> &data, const std::shared_ptr<str
STEP = STEP ? STEP : 192;
MULTIEXP_PERF(PERF_TIMER_START_UNIT(setup, 1000000));
- static constexpr unsigned int mask = (1<<STRAUS_C)-1;
std::shared_ptr<straus_cached_data> local_cache = cache == NULL ? straus_init_cache(data) : cache;
ge_cached cached;
ge_p1p1 p1;
@@ -483,6 +482,7 @@ rct::key straus(const std::vector<MultiexpData> &data, const std::shared_ptr<str
memcpy(bytes33, data[j].scalar.bytes, 32);
bytes33[32] = 0;
bytes = bytes33;
+ static constexpr unsigned int mask = (1<<STRAUS_C)-1;
for (size_t i = 0; i < 256; ++i)
digits[j*256+i] = ((bytes[i>>3] | (bytes[(i>>3)+1]<<8)) >> (i&7)) & mask;
#else
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index cd0cdad92..942bfce0a 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -242,11 +242,11 @@ namespace cryptonote
auto get_nodes = [this]() {
return get_public_nodes(credits_per_hash_threshold);
};
- m_bootstrap_daemon.reset(new bootstrap_daemon(std::move(get_nodes), rpc_payment_enabled, proxy));
+ m_bootstrap_daemon.reset(new bootstrap_daemon(std::move(get_nodes), rpc_payment_enabled, m_bootstrap_daemon_proxy.empty() ? proxy : m_bootstrap_daemon_proxy));
}
else
{
- m_bootstrap_daemon.reset(new bootstrap_daemon(address, credentials, rpc_payment_enabled, proxy));
+ m_bootstrap_daemon.reset(new bootstrap_daemon(address, credentials, rpc_payment_enabled, m_bootstrap_daemon_proxy.empty() ? proxy : m_bootstrap_daemon_proxy));
}
m_should_use_bootstrap_daemon = m_bootstrap_daemon.get() != nullptr;
@@ -264,8 +264,10 @@ namespace cryptonote
, const bool restricted
, const std::string& port
, bool allow_rpc_payment
+ , const std::string& proxy
)
{
+ m_bootstrap_daemon_proxy = proxy;
m_restricted = restricted;
m_net_server.set_threads_prefix("RPC");
m_net_server.set_connection_filter(&m_p2p);
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index 9bc783299..84b14383a 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -91,7 +91,8 @@ namespace cryptonote
const boost::program_options::variables_map& vm,
const bool restricted,
const std::string& port,
- bool allow_rpc_payment
+ bool allow_rpc_payment,
+ const std::string& proxy = {}
);
network_type nettype() const { return m_core.get_nettype(); }
@@ -291,6 +292,7 @@ private:
nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >& m_p2p;
boost::shared_mutex m_bootstrap_daemon_mutex;
std::unique_ptr<bootstrap_daemon> m_bootstrap_daemon;
+ std::string m_bootstrap_daemon_proxy;
bool m_should_use_bootstrap_daemon;
std::chrono::system_clock::time_point m_bootstrap_height_check_time;
bool m_was_bootstrap_ever_used;
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 6e8d585dc..5a4cafc32 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -49,6 +49,7 @@ using namespace epee;
#include "cryptonote_core/tx_sanity_check.h"
#include "wallet_rpc_helpers.h"
#include "wallet2.h"
+#include "wallet_args.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "net/parse.h"
#include "rpc/core_rpc_server_commands_defs.h"
@@ -276,7 +277,7 @@ struct options {
const command_line::arg_descriptor<bool> trusted_daemon = {"trusted-daemon", tools::wallet2::tr("Enable commands which rely on a trusted daemon"), false};
const command_line::arg_descriptor<bool> untrusted_daemon = {"untrusted-daemon", tools::wallet2::tr("Disable commands which rely on a trusted daemon"), false};
const command_line::arg_descriptor<std::string> password = {"password", tools::wallet2::tr("Wallet password (escape/quote as needed)"), "", true};
- const command_line::arg_descriptor<std::string> password_file = {"password-file", tools::wallet2::tr("Wallet password file"), "", true};
+ const command_line::arg_descriptor<std::string> password_file = wallet_args::arg_password_file();
const command_line::arg_descriptor<int> daemon_port = {"daemon-port", tools::wallet2::tr("Use daemon instance at port <arg> instead of 18081"), 0};
const command_line::arg_descriptor<std::string> daemon_login = {"daemon-login", tools::wallet2::tr("Specify username[:password] for daemon RPC client"), "", true};
const command_line::arg_descriptor<std::string> daemon_ssl = {"daemon-ssl", tools::wallet2::tr("Enable SSL on daemon RPC connections: enabled|disabled|autodetect"), "autodetect"};
@@ -532,7 +533,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
boost::optional<tools::password_container> get_password(const boost::program_options::variables_map& vm, const options& opts, const std::function<boost::optional<tools::password_container>(const char*, bool)> &password_prompter, const bool verify)
{
- if (command_line::has_arg(vm, opts.password) && command_line::has_arg(vm, opts.password_file))
+ if (command_line::has_arg(vm, opts.password) && !command_line::is_arg_defaulted(vm, opts.password_file))
{
THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("can't specify more than one of --password and --password-file"));
}
@@ -542,10 +543,11 @@ boost::optional<tools::password_container> get_password(const boost::program_opt
return tools::password_container{command_line::get_arg(vm, opts.password)};
}
- if (command_line::has_arg(vm, opts.password_file))
+ if (!command_line::is_arg_defaulted(vm, opts.password_file))
{
std::string password;
- bool r = epee::file_io_utils::load_file_to_string(command_line::get_arg(vm, opts.password_file),
+ const auto password_file = command_line::get_arg(vm, opts.password_file);
+ bool r = epee::file_io_utils::load_file_to_string(password_file,
password);
THROW_WALLET_EXCEPTION_IF(!r, tools::error::wallet_internal_error, tools::wallet2::tr("the password file specified could not be read"));
@@ -1938,7 +1940,7 @@ void wallet2::cache_tx_data(const cryptonote::transaction& tx, const crypto::has
const bool is_miner = tx.vin.size() == 1 && tx.vin[0].type() == typeid(cryptonote::txin_gen);
if (!is_miner || m_refresh_type != RefreshType::RefreshNoCoinbase)
{
- const size_t rec_size = is_miner && m_refresh_type == RefreshType::RefreshOptimizeCoinbase ? 1 : tx.vout.size();
+ const size_t rec_size = (is_miner && m_refresh_type == RefreshType::RefreshOptimizeCoinbase && tx.version < 2) ? 1 : tx.vout.size();
if (!tx.vout.empty())
{
// if tx.vout is not empty, we loop through all tx pubkeys
@@ -2087,7 +2089,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
{
// assume coinbase isn't for us
}
- else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase)
+ else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase && tx.version < 2)
{
check_acc_out_precomp_once(tx.vout[0], derivation, additional_derivations, 0, is_out_data_ptr, tx_scan_info[0], output_found[0]);
THROW_WALLET_EXCEPTION_IF(tx_scan_info[0].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
@@ -2858,8 +2860,9 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
if (m_refresh_type != RefreshType::RefreshNoCoinbase)
{
THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range");
- const size_t n_vouts = m_refresh_type == RefreshType::RefreshOptimizeCoinbase ? 1 : parsed_blocks[i].block.miner_tx.vout.size();
- tpool.submit(&waiter, [&, i, n_vouts, txidx](){ geniod(parsed_blocks[i].block.miner_tx, n_vouts, txidx); }, true);
+ const cryptonote::transaction& tx = parsed_blocks[i].block.miner_tx;
+ const size_t n_vouts = (m_refresh_type == RefreshType::RefreshOptimizeCoinbase && tx.version < 2) ? 1 : tx.vout.size();
+ tpool.submit(&waiter, [&, n_vouts, txidx](){ geniod(tx, n_vouts, txidx); }, true);
}
++txidx;
for (size_t j = 0; j < parsed_blocks[i].txes.size(); ++j)
@@ -10260,6 +10263,38 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
const size_t num_outputs = get_num_outputs(tx.dsts, m_transfers, tx.selected_transfers);
needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, base_fee, fee_multiplier, fee_quantization_mask);
+ auto try_carving_from_partial_payment = [&](uint64_t needed_fee, uint64_t available_for_fee)
+ {
+ // The check against original_output_index is to ensure the last entry in tx.dsts is really
+ // a partial payment. Otherwise multiple requested outputs to the same address could
+ // fool this logic into thinking there is a partial payment.
+ if (needed_fee > available_for_fee && !dsts.empty() && dsts[0].amount > 0 && tx.dsts.size() > original_output_index)
+ {
+ // we don't have enough for the fee, but we've only partially paid the current address,
+ // so we can take the fee from the paid amount, since we'll have to make another tx anyway
+ LOG_PRINT_L2("Attempting to carve tx fee " << print_money(needed_fee) << " from partial payment (first pass)");
+ std::vector<cryptonote::tx_destination_entry>::iterator i;
+ i = std::find_if(tx.dsts.begin(), tx.dsts.end(),
+ [&](const cryptonote::tx_destination_entry &d) { return !memcmp (&d.addr, &dsts[0].addr, sizeof(dsts[0].addr)); });
+ THROW_WALLET_EXCEPTION_IF(i == tx.dsts.end(), error::wallet_internal_error, "paid address not found in outputs");
+ if (i->amount > needed_fee)
+ {
+ uint64_t new_paid_amount = i->amount /*+ test_ptx.fee*/ - needed_fee;
+ LOG_PRINT_L2("Adjusting amount paid to " << get_account_address_as_str(m_nettype, i->is_subaddress, i->addr) << " from " <<
+ print_money(i->amount) << " to " << print_money(new_paid_amount) << " to accommodate " <<
+ print_money(needed_fee) << " fee");
+ dsts[0].amount += i->amount - new_paid_amount;
+ i->amount = new_paid_amount;
+ test_ptx.fee = needed_fee;
+ available_for_fee = needed_fee;
+ }
+ }
+ return available_for_fee;
+ };
+
+ // Try to carve the estimated fee from the partial payment (if there is one)
+ available_for_fee = try_carving_from_partial_payment(needed_fee, available_for_fee);
+
uint64_t inputs = 0, outputs = needed_fee;
for (size_t idx: tx.selected_transfers) inputs += m_transfers[idx].amount();
for (const auto &o: tx.dsts) outputs += o.amount;
@@ -10285,26 +10320,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
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)");
- if (needed_fee > available_for_fee && !dsts.empty() && dsts[0].amount > 0)
- {
- // we don't have enough for the fee, but we've only partially paid the current address,
- // so we can take the fee from the paid amount, since we'll have to make another tx anyway
- std::vector<cryptonote::tx_destination_entry>::iterator i;
- i = std::find_if(tx.dsts.begin(), tx.dsts.end(),
- [&](const cryptonote::tx_destination_entry &d) { return !memcmp (&d.addr, &dsts[0].addr, sizeof(dsts[0].addr)); });
- THROW_WALLET_EXCEPTION_IF(i == tx.dsts.end(), error::wallet_internal_error, "paid address not found in outputs");
- if (i->amount > needed_fee)
- {
- uint64_t new_paid_amount = i->amount /*+ test_ptx.fee*/ - needed_fee;
- LOG_PRINT_L2("Adjusting amount paid to " << get_account_address_as_str(m_nettype, i->is_subaddress, i->addr) << " from " <<
- print_money(i->amount) << " to " << print_money(new_paid_amount) << " to accommodate " <<
- print_money(needed_fee) << " fee");
- dsts[0].amount += i->amount - new_paid_amount;
- i->amount = new_paid_amount;
- test_ptx.fee = needed_fee;
- available_for_fee = needed_fee;
- }
- }
+ // Try to carve the fee from the partial payment again after updating from estimate to actual
+ available_for_fee = try_carving_from_partial_payment(needed_fee, available_for_fee);
if (needed_fee > available_for_fee)
{
diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp
index 55058bf4e..066e98e52 100644
--- a/src/wallet/wallet_args.cpp
+++ b/src/wallet/wallet_args.cpp
@@ -80,6 +80,10 @@ namespace wallet_args
{
return {"rpc-client-secret-key", wallet_args::tr("Set RPC client secret key for RPC payments"), ""};
}
+ command_line::arg_descriptor<std::string> arg_password_file()
+ {
+ return {"password-file", wallet_args::tr("Wallet password file"), ""};
+ }
const char* tr(const char* str)
{
diff --git a/src/wallet/wallet_args.h b/src/wallet/wallet_args.h
index 4af1b58fe..21e5f187c 100644
--- a/src/wallet/wallet_args.h
+++ b/src/wallet/wallet_args.h
@@ -37,6 +37,7 @@ namespace wallet_args
command_line::arg_descriptor<std::string> arg_generate_from_json();
command_line::arg_descriptor<std::string> arg_wallet_file();
command_line::arg_descriptor<std::string> arg_rpc_client_secret_key();
+ command_line::arg_descriptor<std::string> arg_password_file();
const char* tr(const char* str);
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index e1a06886b..4655e24cd 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -41,6 +41,7 @@ using namespace epee;
#include "wallet/wallet_args.h"
#include "common/command_line.h"
#include "common/i18n.h"
+#include "common/scoped_message_writer.h"
#include "cryptonote_config.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_basic/account.h"
@@ -4525,10 +4526,12 @@ public:
const auto arg_wallet_file = wallet_args::arg_wallet_file();
const auto arg_from_json = wallet_args::arg_generate_from_json();
const auto arg_rpc_client_secret_key = wallet_args::arg_rpc_client_secret_key();
+ const auto arg_password_file = wallet_args::arg_password_file();
const auto wallet_file = command_line::get_arg(vm, arg_wallet_file);
const auto from_json = command_line::get_arg(vm, arg_from_json);
const auto wallet_dir = command_line::get_arg(vm, arg_wallet_dir);
+ const auto password_file = command_line::get_arg(vm, arg_password_file);
const auto prompt_for_password = command_line::get_arg(vm, arg_prompt_for_password);
const auto password_prompt = prompt_for_password ? password_prompter : nullptr;
@@ -4538,6 +4541,12 @@ public:
return false;
}
+ if(!wallet_dir.empty() && !password_file.empty())
+ {
+ LOG_ERROR(tools::wallet_rpc_server::tr("--password-file is not allowed in combination with --wallet-dir"));
+ return false;
+ }
+
if (!wallet_dir.empty())
{
wal = NULL;
@@ -4716,7 +4725,7 @@ int main(int argc, char** argv) {
tools::wallet_rpc_server::tr("This is the RPC monero wallet. It needs to connect to a monero\ndaemon to work correctly."),
desc_params,
po::positional_options_description(),
- [](const std::string &s, bool emphasis){ epee::set_console_color(emphasis ? epee::console_color_white : epee::console_color_default, true); std::cout << s << std::endl; if (emphasis) epee::reset_console_color(); },
+ [](const std::string &s, bool emphasis){ tools::scoped_message_writer(emphasis ? epee::console_color_white : epee::console_color_default, true) << s; },
"monero-wallet-rpc.log",
true
);