aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/util.cpp17
-rw-r--r--src/common/util.h2
-rw-r--r--src/cryptonote_basic/verification_context.h1
-rw-r--r--src/cryptonote_config.h3
-rw-r--r--src/cryptonote_core/blockchain.cpp17
-rw-r--r--src/cryptonote_core/blockchain.h4
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp2
-rw-r--r--src/daemon/command_parser_executor.cpp31
-rw-r--r--src/daemon/rpc_command_executor.cpp68
-rw-r--r--src/daemon/rpc_command_executor.h2
-rw-r--r--src/net/parse.cpp59
-rw-r--r--src/net/parse.h2
-rw-r--r--src/p2p/net_node.cpp21
-rw-r--r--src/p2p/net_node.h21
-rw-r--r--src/p2p/net_node.inl192
-rw-r--r--src/p2p/net_peerlist_boost_serialization.h31
-rw-r--r--src/rpc/core_rpc_server.cpp152
-rw-r--r--src/rpc/core_rpc_server.h5
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h61
-rw-r--r--src/rpc/daemon_handler.cpp5
-rw-r--r--src/rpc/rpc_args.cpp37
-rw-r--r--src/rpc/rpc_args.h6
-rw-r--r--src/simplewallet/simplewallet.cpp2
-rw-r--r--src/wallet/api/wallet.cpp4
-rw-r--r--src/wallet/wallet2.cpp2
-rw-r--r--src/wallet/wallet_rpc_server.cpp9
26 files changed, 661 insertions, 95 deletions
diff --git a/src/common/util.cpp b/src/common/util.cpp
index db5aa3052..0fa9e8dc1 100644
--- a/src/common/util.cpp
+++ b/src/common/util.cpp
@@ -1068,6 +1068,23 @@ std::string get_nix_version_display_string()
return std::string(buffer);
}
+ std::string get_human_readable_timespan(uint64_t seconds)
+ {
+ if (seconds < 60)
+ return std::to_string(seconds) + " seconds";
+ if (seconds < 3600)
+ return std::to_string((uint64_t)(seconds / 60)) + " minutes";
+ if (seconds < 3600 * 24)
+ return std::to_string((uint64_t)(seconds / 3600)) + " hours";
+ if (seconds < 3600 * 24 * 30.5)
+ return std::to_string((uint64_t)(seconds / (3600 * 24))) + " days";
+ if (seconds < 3600 * 24 * 365.25)
+ return std::to_string((uint64_t)(seconds / (3600 * 24 * 30.5))) + " months";
+ if (seconds < 3600 * 24 * 365.25 * 100)
+ return std::to_string((uint64_t)(seconds / (3600 * 24 * 30.5 * 365.25))) + " years";
+ return "a long time";
+ }
+
std::string get_human_readable_bytes(uint64_t bytes)
{
// Use 1024 for "kilo", 1024*1024 for "mega" and so on instead of the more modern and standard-conforming
diff --git a/src/common/util.h b/src/common/util.h
index f6d5c9b1f..b0f734eff 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -245,5 +245,7 @@ namespace tools
std::string get_human_readable_timestamp(uint64_t ts);
+ std::string get_human_readable_timespan(uint64_t seconds);
+
std::string get_human_readable_bytes(uint64_t bytes);
}
diff --git a/src/cryptonote_basic/verification_context.h b/src/cryptonote_basic/verification_context.h
index 36b63f254..3d7200fae 100644
--- a/src/cryptonote_basic/verification_context.h
+++ b/src/cryptonote_basic/verification_context.h
@@ -48,6 +48,7 @@ namespace cryptonote
bool m_overspend;
bool m_fee_too_low;
bool m_not_rct;
+ bool m_too_few_outputs;
};
struct block_verification_context
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index 56b6a63b7..b68bb41e1 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -128,6 +128,8 @@
#define P2P_SUPPORT_FLAG_FLUFFY_BLOCKS 0x01
#define P2P_SUPPORT_FLAGS P2P_SUPPORT_FLAG_FLUFFY_BLOCKS
+#define RPC_IP_FAILS_BEFORE_BLOCK 3
+
#define ALLOW_DEBUG_COMMANDS
#define CRYPTONOTE_NAME "bitmonero"
@@ -147,6 +149,7 @@
#define HF_VERSION_PER_BYTE_FEE 8
#define HF_VERSION_SMALLER_BP 10
#define HF_VERSION_LONG_TERM_BLOCK_WEIGHT 10
+#define HF_VERSION_MIN_2_OUTPUTS 12
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 31e2ac136..0ea81f19a 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -2840,6 +2840,19 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
const uint8_t hf_version = m_hardfork->get_current_version();
+ if (hf_version >= HF_VERSION_MIN_2_OUTPUTS)
+ {
+ if (tx.version >= 2)
+ {
+ if (tx.vout.size() < 2)
+ {
+ MERROR_VER("Tx " << get_transaction_hash(tx) << " has fewer than two outputs");
+ tvc.m_too_few_outputs = true;
+ return false;
+ }
+ }
+ }
+
// from hard fork 2, we require mixin at least 2 unless one output cannot mix with 2 others
// if one output cannot mix with 2 others, we accept at most 1 output that can mix
if (hf_version >= 2)
@@ -4840,9 +4853,9 @@ std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> Blockchain:: get_ou
return m_db->get_output_histogram(amounts, unlocked, recent_cutoff, min_count);
}
-std::list<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>> Blockchain::get_alternative_chains() const
+std::vector<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>> Blockchain::get_alternative_chains() const
{
- std::list<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>> chains;
+ std::vector<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>> chains;
blocks_ext_by_hash alt_blocks;
alt_blocks.reserve(m_db->get_alt_block_count());
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index b1c23b704..d95f2dceb 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -950,9 +950,9 @@ namespace cryptonote
/**
* @brief returns a set of known alternate chains
*
- * @return a list of chains
+ * @return a vector of chains
*/
- std::list<std::pair<block_extended_info,std::vector<crypto::hash>>> get_alternative_chains() const;
+ std::vector<std::pair<block_extended_info,std::vector<crypto::hash>>> get_alternative_chains() const;
void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const txpool_tx_meta_t &meta);
void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t &meta);
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 2ebd99904..a3a92ab60 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -1844,7 +1844,7 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
bool core::check_block_rate()
{
- if (m_offline || m_nettype == FAKECHAIN || m_target_blockchain_height > get_current_blockchain_height())
+ if (m_offline || m_nettype == FAKECHAIN || m_target_blockchain_height > get_current_blockchain_height() || m_target_blockchain_height == 0)
{
MDEBUG("Not checking block rate, offline or syncing");
return true;
diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp
index 778d7b4d8..924447701 100644
--- a/src/daemon/command_parser_executor.cpp
+++ b/src/daemon/command_parser_executor.cpp
@@ -674,11 +674,38 @@ bool t_command_parser_executor::alt_chain_info(const std::vector<std::string>& a
{
if(args.size() > 1)
{
- std::cout << "usage: alt_chain_info [block_hash]" << std::endl;
+ std::cout << "usage: alt_chain_info [block_hash|>N|-N]" << std::endl;
return false;
}
- return m_executor.alt_chain_info(args.size() == 1 ? args[0] : "");
+ std::string tip;
+ size_t above = 0;
+ uint64_t last_blocks = 0;
+ if (args.size() == 1)
+ {
+ if (args[0].size() > 0 && args[0][0] == '>')
+ {
+ if (!epee::string_tools::get_xtype_from_string(above, args[0].c_str() + 1))
+ {
+ std::cout << "invalid above parameter" << std::endl;
+ return false;
+ }
+ }
+ else if (args[0].size() > 0 && args[0][0] == '-')
+ {
+ if (!epee::string_tools::get_xtype_from_string(last_blocks, args[0].c_str() + 1))
+ {
+ std::cout << "invalid last_blocks parameter" << std::endl;
+ return false;
+ }
+ }
+ else
+ {
+ tip = args[0];
+ }
+ }
+
+ return m_executor.alt_chain_info(tip, above, last_blocks);
}
bool t_command_parser_executor::print_blockchain_dynamic_stats(const std::vector<std::string>& args)
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index 1da6398b4..dbf0409e5 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -69,7 +69,7 @@ namespace {
std::string id_str;
std::string port_str;
std::string elapsed = peer.last_seen == 0 ? "never" : epee::misc_utils::get_time_interval_string(now - last_seen);
- std::string ip_str = epee::string_tools::get_ip_string_from_int32(peer.ip);
+ std::string ip_str = peer.ip != 0 ? epee::string_tools::get_ip_string_from_int32(peer.ip) : std::string("[") + peer.host + "]";
std::stringstream peer_id_str;
peer_id_str << std::hex << std::setw(16) << peer.id;
peer_id_str >> id_str;
@@ -1841,7 +1841,7 @@ bool t_rpc_command_executor::print_coinbase_tx_sum(uint64_t height, uint64_t cou
return true;
}
-bool t_rpc_command_executor::alt_chain_info(const std::string &tip)
+bool t_rpc_command_executor::alt_chain_info(const std::string &tip, size_t above, uint64_t last_blocks)
{
cryptonote::COMMAND_RPC_GET_INFO::request ireq;
cryptonote::COMMAND_RPC_GET_INFO::response ires;
@@ -1878,16 +1878,31 @@ bool t_rpc_command_executor::alt_chain_info(const std::string &tip)
if (tip.empty())
{
- tools::msg_writer() << boost::lexical_cast<std::string>(res.chains.size()) << " alternate chains found:";
- for (const auto &chain: res.chains)
+ auto chains = res.chains;
+ std::sort(chains.begin(), chains.end(), [](const cryptonote::COMMAND_RPC_GET_ALTERNATE_CHAINS::chain_info &info0, cryptonote::COMMAND_RPC_GET_ALTERNATE_CHAINS::chain_info &info1){ return info0.height < info1.height; });
+ std::vector<size_t> display;
+ for (size_t i = 0; i < chains.size(); ++i)
{
- uint64_t start_height = (chain.height - chain.length + 1);
+ const auto &chain = chains[i];
+ if (chain.length <= above)
+ continue;
+ const uint64_t start_height = (chain.height - chain.length + 1);
+ if (last_blocks > 0 && ires.height - 1 - start_height >= last_blocks)
+ continue;
+ display.push_back(i);
+ }
+ tools::msg_writer() << boost::lexical_cast<std::string>(display.size()) << " alternate chains found:";
+ for (const size_t idx: display)
+ {
+ const auto &chain = chains[idx];
+ const uint64_t start_height = (chain.height - chain.length + 1);
tools::msg_writer() << chain.length << " blocks long, from height " << start_height << " (" << (ires.height - start_height - 1)
<< " deep), diff " << chain.wide_difficulty << ": " << chain.block_hash;
}
}
else
{
+ const uint64_t now = time(NULL);
const auto i = std::find_if(res.chains.begin(), res.chains.end(), [&tip](cryptonote::COMMAND_RPC_GET_ALTERNATE_CHAINS::chain_info &info){ return info.block_hash == tip; });
if (i != res.chains.end())
{
@@ -1899,6 +1914,49 @@ bool t_rpc_command_executor::alt_chain_info(const std::string &tip)
for (const std::string &block_id: chain.block_hashes)
tools::msg_writer() << " " << block_id;
tools::msg_writer() << "Chain parent on main chain: " << chain.main_chain_parent_block;
+ cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request bhreq;
+ cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response bhres;
+ bhreq.hashes = chain.block_hashes;
+ bhreq.hashes.push_back(chain.main_chain_parent_block);
+ bhreq.fill_pow_hash = false;
+ if (m_is_rpc)
+ {
+ if (!m_rpc_client->json_rpc_request(bhreq, bhres, "getblockheaderbyhash", fail_message.c_str()))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if (!m_rpc_server->on_get_block_header_by_hash(bhreq, bhres, error_resp))
+ {
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
+ return true;
+ }
+ }
+ if (bhres.block_headers.size() != chain.length + 1)
+ {
+ tools::fail_msg_writer() << "Failed to get block header info for alt chain";
+ return true;
+ }
+ uint64_t t0 = bhres.block_headers.front().timestamp, t1 = t0;
+ for (const cryptonote::block_header_response &block_header: bhres.block_headers)
+ {
+ t0 = std::min<uint64_t>(t0, block_header.timestamp);
+ t1 = std::max<uint64_t>(t1, block_header.timestamp);
+ }
+ const uint64_t dt = t1 - t0;
+ const uint64_t age = std::max(dt, t0 < now ? now - t0 : 0);
+ tools::msg_writer() << "Age: " << tools::get_human_readable_timespan(age);
+ if (chain.length > 1)
+ {
+ tools::msg_writer() << "Time span: " << tools::get_human_readable_timespan(dt);
+ cryptonote::difficulty_type start_difficulty = bhres.block_headers.back().difficulty;
+ if (start_difficulty > 0)
+ tools::msg_writer() << "Approximated " << 100.f * DIFFICULTY_TARGET_V2 * chain.length / dt << "% of network hash rate";
+ else
+ tools::fail_msg_writer() << "Bad cmumulative difficulty reported by dameon";
+ }
}
else
tools::fail_msg_writer() << "Block hash " << tip << " is not the tip of any known alternate chain";
diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h
index 4622609ae..f3ed48319 100644
--- a/src/daemon/rpc_command_executor.h
+++ b/src/daemon/rpc_command_executor.h
@@ -145,7 +145,7 @@ public:
bool print_coinbase_tx_sum(uint64_t height, uint64_t count);
- bool alt_chain_info(const std::string &tip);
+ bool alt_chain_info(const std::string &tip, size_t above, uint64_t last_blocks);
bool print_blockchain_dynamic_stats(uint64_t nblocks);
diff --git a/src/net/parse.cpp b/src/net/parse.cpp
index d93d7d352..7b74f2b75 100644
--- a/src/net/parse.cpp
+++ b/src/net/parse.cpp
@@ -34,28 +34,69 @@
namespace net
{
+ void get_network_address_host_and_port(const std::string& address, std::string& host, std::string& port)
+ {
+ // require ipv6 address format "[addr:addr:addr:...:addr]:port"
+ if (address.find(']') != std::string::npos)
+ {
+ host = address.substr(1, address.rfind(']') - 1);
+ if ((host.size() + 2) < address.size())
+ {
+ port = address.substr(address.rfind(':') + 1);
+ }
+ }
+ else
+ {
+ host = address.substr(0, address.rfind(':'));
+ if (host.size() < address.size())
+ {
+ port = address.substr(host.size() + 1);
+ }
+ }
+ }
+
expect<epee::net_utils::network_address>
get_network_address(const boost::string_ref address, const std::uint16_t default_port)
{
- const boost::string_ref host = address.substr(0, address.rfind(':'));
+ std::string host_str = "";
+ std::string port_str = "";
+
+ bool ipv6 = false;
+
+ get_network_address_host_and_port(std::string(address), host_str, port_str);
- if (host.empty())
+ boost::string_ref host_str_ref(host_str);
+ boost::string_ref port_str_ref(port_str);
+
+ if (host_str.empty())
return make_error_code(net::error::invalid_host);
- if (host.ends_with(".onion"))
+ if (host_str_ref.ends_with(".onion"))
return tor_address::make(address, default_port);
- if (host.ends_with(".i2p"))
+ if (host_str_ref.ends_with(".i2p"))
return i2p_address::make(address, default_port);
+ boost::system::error_code ec;
+ boost::asio::ip::address_v6 v6 = boost::asio::ip::make_address_v6(host_str, ec);
+ ipv6 = !ec;
+
std::uint16_t port = default_port;
- if (host.size() < address.size())
+ if (port_str.size())
{
- if (!epee::string_tools::get_xtype_from_string(port, std::string{address.substr(host.size() + 1)}))
+ if (!epee::string_tools::get_xtype_from_string(port, port_str))
return make_error_code(net::error::invalid_port);
}
- std::uint32_t ip = 0;
- if (epee::string_tools::get_ip_int32_from_string(ip, std::string{host}))
- return {epee::net_utils::ipv4_network_address{ip, port}};
+ if (ipv6)
+ {
+ return {epee::net_utils::ipv6_network_address{v6, port}};
+ }
+ else
+ {
+ std::uint32_t ip = 0;
+ if (epee::string_tools::get_ip_int32_from_string(ip, host_str))
+ return {epee::net_utils::ipv4_network_address{ip, port}};
+ }
+
return make_error_code(net::error::unsupported_address);
}
diff --git a/src/net/parse.h b/src/net/parse.h
index 9f0d66ea6..0d8fda711 100644
--- a/src/net/parse.h
+++ b/src/net/parse.h
@@ -36,6 +36,8 @@
namespace net
{
+ void get_network_address_host_and_port(const std::string& address, std::string& host, std::string& port);
+
/*!
Identifies onion, i2p and IPv4 addresses and returns them as a generic
`network_address`. If the type is unsupported, it might be a hostname,
diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp
index a44297c17..bb51be242 100644
--- a/src/p2p/net_node.cpp
+++ b/src/p2p/net_node.cpp
@@ -108,10 +108,11 @@ namespace
namespace nodetool
{
- const command_line::arg_descriptor<std::string> arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol", "0.0.0.0"};
+ const command_line::arg_descriptor<std::string> arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol (IPv4)", "0.0.0.0"};
+ const command_line::arg_descriptor<std::string> arg_p2p_bind_ipv6_address = {"p2p-bind-ipv6-address", "Interface for p2p network protocol (IPv6)", "::"};
const command_line::arg_descriptor<std::string, false, true, 2> arg_p2p_bind_port = {
"p2p-bind-port"
- , "Port for p2p network protocol"
+ , "Port for p2p network protocol (IPv4)"
, std::to_string(config::P2P_DEFAULT_PORT)
, {{ &cryptonote::arg_testnet_on, &cryptonote::arg_stagenet_on }}
, [](std::array<bool, 2> testnet_stagenet, bool defaulted, std::string val)->std::string {
@@ -122,6 +123,20 @@ namespace nodetool
return val;
}
};
+ const command_line::arg_descriptor<std::string, false, true, 2> arg_p2p_bind_port_ipv6 = {
+ "p2p-bind-port-ipv6"
+ , "Port for p2p network protocol (IPv6)"
+ , std::to_string(config::P2P_DEFAULT_PORT)
+ , {{ &cryptonote::arg_testnet_on, &cryptonote::arg_stagenet_on }}
+ , [](std::array<bool, 2> testnet_stagenet, bool defaulted, std::string val)->std::string {
+ if (testnet_stagenet[0] && defaulted)
+ return std::to_string(config::testnet::P2P_DEFAULT_PORT);
+ else if (testnet_stagenet[1] && defaulted)
+ return std::to_string(config::stagenet::P2P_DEFAULT_PORT);
+ return val;
+ }
+ };
+
const command_line::arg_descriptor<uint32_t> arg_p2p_external_port = {"p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0};
const command_line::arg_descriptor<bool> arg_p2p_allow_local_ip = {"allow-local-ip", "Allow local ip add to peer list, mostly in debug purposes"};
const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_peer = {"add-peer", "Manually add peer to local peerlist"};
@@ -136,6 +151,8 @@ namespace nodetool
const command_line::arg_descriptor<bool> arg_no_igd = {"no-igd", "Disable UPnP port mapping"};
const command_line::arg_descriptor<std::string> arg_igd = {"igd", "UPnP port mapping (disabled, enabled, delayed)", "delayed"};
+ const command_line::arg_descriptor<bool> arg_p2p_use_ipv6 = {"p2p-use-ipv6", "Enable IPv6 for p2p", false};
+ const command_line::arg_descriptor<bool> arg_p2p_require_ipv4 = {"p2p-require-ipv4", "Require successful IPv4 bind for p2p", true};
const command_line::arg_descriptor<int64_t> arg_out_peers = {"out-peers", "set max number of out peers", -1};
const command_line::arg_descriptor<int64_t> arg_in_peers = {"in-peers", "set max number of in peers", -1};
const command_line::arg_descriptor<int> arg_tos_flag = {"tos-flag", "set TOS flag", -1};
diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h
index 0d95ceb99..cf6b2c67b 100644
--- a/src/p2p/net_node.h
+++ b/src/p2p/net_node.h
@@ -151,7 +151,9 @@ namespace nodetool
: m_connect(nullptr),
m_net_server(epee::net_utils::e_connection_type_P2P),
m_bind_ip(),
+ m_bind_ipv6_address(),
m_port(),
+ m_port_ipv6(),
m_our_address(),
m_peerlist(),
m_config{},
@@ -167,7 +169,9 @@ namespace nodetool
: m_connect(nullptr),
m_net_server(public_service, epee::net_utils::e_connection_type_P2P),
m_bind_ip(),
+ m_bind_ipv6_address(),
m_port(),
+ m_port_ipv6(),
m_our_address(),
m_peerlist(),
m_config{},
@@ -182,7 +186,9 @@ namespace nodetool
connect_func* m_connect;
net_server m_net_server;
std::string m_bind_ip;
+ std::string m_bind_ipv6_address;
std::string m_port;
+ std::string m_port_ipv6;
epee::net_utils::network_address m_our_address; // in anonymity networks
peerlist_manager m_peerlist;
config m_config;
@@ -357,7 +363,13 @@ namespace nodetool
bool is_peer_used(const peerlist_entry& peer);
bool is_peer_used(const anchor_peerlist_entry& peer);
bool is_addr_connected(const epee::net_utils::network_address& peer);
- void add_upnp_port_mapping(uint32_t port);
+ void add_upnp_port_mapping_impl(uint32_t port, bool ipv6=false);
+ void add_upnp_port_mapping_v4(uint32_t port);
+ void add_upnp_port_mapping_v6(uint32_t port);
+ void add_upnp_port_mapping(uint32_t port, bool ipv4=true, bool ipv6=false);
+ void delete_upnp_port_mapping_impl(uint32_t port, bool ipv6=false);
+ void delete_upnp_port_mapping_v4(uint32_t port);
+ void delete_upnp_port_mapping_v6(uint32_t port);
void delete_upnp_port_mapping(uint32_t port);
template<class t_callback>
bool try_ping(basic_node_data& node_data, p2p_connection_context& context, const t_callback &cb);
@@ -419,12 +431,15 @@ namespace nodetool
bool m_have_address;
bool m_first_connection_maker_call;
uint32_t m_listening_port;
+ uint32_t m_listening_port_ipv6;
uint32_t m_external_port;
uint16_t m_rpc_port;
bool m_allow_local_ip;
bool m_hide_my_port;
igd_t m_igd;
bool m_offline;
+ bool m_use_ipv6;
+ bool m_require_ipv4;
std::atomic<bool> is_closing;
std::unique_ptr<boost::thread> mPeersLoggerThread;
//critical_section m_connections_lock;
@@ -485,7 +500,11 @@ namespace nodetool
const int64_t default_limit_up = P2P_DEFAULT_LIMIT_RATE_UP; // kB/s
const int64_t default_limit_down = P2P_DEFAULT_LIMIT_RATE_DOWN; // kB/s
extern const command_line::arg_descriptor<std::string> arg_p2p_bind_ip;
+ extern const command_line::arg_descriptor<std::string> arg_p2p_bind_ipv6_address;
extern const command_line::arg_descriptor<std::string, false, true, 2> arg_p2p_bind_port;
+ extern const command_line::arg_descriptor<std::string, false, true, 2> arg_p2p_bind_port_ipv6;
+ extern const command_line::arg_descriptor<bool> arg_p2p_use_ipv6;
+ extern const command_line::arg_descriptor<bool> arg_p2p_require_ipv4;
extern const command_line::arg_descriptor<uint32_t> arg_p2p_external_port;
extern const command_line::arg_descriptor<bool> arg_p2p_allow_local_ip;
extern const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_peer;
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 98484fe78..426767365 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -93,7 +93,11 @@ namespace nodetool
void node_server<t_payload_net_handler>::init_options(boost::program_options::options_description& desc)
{
command_line::add_arg(desc, arg_p2p_bind_ip);
+ command_line::add_arg(desc, arg_p2p_bind_ipv6_address);
command_line::add_arg(desc, arg_p2p_bind_port, false);
+ command_line::add_arg(desc, arg_p2p_bind_port_ipv6, false);
+ command_line::add_arg(desc, arg_p2p_use_ipv6);
+ command_line::add_arg(desc, arg_p2p_require_ipv4);
command_line::add_arg(desc, arg_p2p_external_port);
command_line::add_arg(desc, arg_p2p_allow_local_ip);
command_line::add_arg(desc, arg_p2p_add_peer);
@@ -341,7 +345,9 @@ namespace nodetool
network_zone& public_zone = m_network_zones[epee::net_utils::zone::public_];
public_zone.m_connect = &public_connect;
public_zone.m_bind_ip = command_line::get_arg(vm, arg_p2p_bind_ip);
+ public_zone.m_bind_ipv6_address = command_line::get_arg(vm, arg_p2p_bind_ipv6_address);
public_zone.m_port = command_line::get_arg(vm, arg_p2p_bind_port);
+ public_zone.m_port_ipv6 = command_line::get_arg(vm, arg_p2p_bind_port_ipv6);
public_zone.m_can_pingback = true;
m_external_port = command_line::get_arg(vm, arg_p2p_external_port);
m_allow_local_ip = command_line::get_arg(vm, arg_p2p_allow_local_ip);
@@ -375,6 +381,8 @@ namespace nodetool
return false;
}
m_offline = command_line::get_arg(vm, cryptonote::arg_offline);
+ m_use_ipv6 = command_line::get_arg(vm, arg_p2p_use_ipv6);
+ m_require_ipv4 = command_line::get_arg(vm, arg_p2p_require_ipv4);
if (command_line::has_arg(vm, arg_p2p_add_peer))
{
@@ -518,12 +526,17 @@ namespace nodetool
std::string host = addr;
std::string port = std::to_string(default_port);
- size_t pos = addr.find_last_of(':');
- if (std::string::npos != pos)
+ size_t colon_pos = addr.find_last_of(':');
+ size_t dot_pos = addr.find_last_of('.');
+ size_t square_brace_pos = addr.find('[');
+
+ // IPv6 will have colons regardless. IPv6 and IPv4 address:port will have a colon but also either a . or a [
+ // as IPv6 addresses specified as address:port are to be specified as "[addr:addr:...:addr]:port"
+ // One may also specify an IPv6 address as simply "[addr:addr:...:addr]" without the port; in that case
+ // the square braces will be stripped here.
+ if ((std::string::npos != colon_pos && std::string::npos != dot_pos) || std::string::npos != square_brace_pos)
{
- CHECK_AND_ASSERT_MES(addr.length() - 1 != pos && 0 != pos, false, "Failed to parse seed address from string: '" << addr << '\'');
- host = addr.substr(0, pos);
- port = addr.substr(pos + 1);
+ net::get_network_address_host_and_port(addr, host, port);
}
MINFO("Resolving node address: host=" << host << ", port=" << port);
@@ -546,7 +559,9 @@ namespace nodetool
}
else
{
- MWARNING("IPv6 unsupported, skip '" << host << "' -> " << endpoint.address().to_v6().to_string(ec));
+ epee::net_utils::network_address na{epee::net_utils::ipv6_network_address{endpoint.address().to_v6(), endpoint.port()}};
+ seed_nodes.push_back(na);
+ MINFO("Added node: " << na.str());
}
}
return true;
@@ -780,21 +795,40 @@ namespace nodetool
if (!zone.second.m_bind_ip.empty())
{
+ std::string ipv6_addr = "";
+ std::string ipv6_port = "";
zone.second.m_net_server.set_connection_filter(this);
- MINFO("Binding on " << zone.second.m_bind_ip << ":" << zone.second.m_port);
- res = zone.second.m_net_server.init_server(zone.second.m_port, zone.second.m_bind_ip, epee::net_utils::ssl_support_t::e_ssl_support_disabled);
+ MINFO("Binding (IPv4) on " << zone.second.m_bind_ip << ":" << zone.second.m_port);
+ if (!zone.second.m_bind_ipv6_address.empty() && m_use_ipv6)
+ {
+ ipv6_addr = zone.second.m_bind_ipv6_address;
+ ipv6_port = zone.second.m_port_ipv6;
+ MINFO("Binding (IPv6) on " << zone.second.m_bind_ipv6_address << ":" << zone.second.m_port_ipv6);
+ }
+ res = zone.second.m_net_server.init_server(zone.second.m_port, zone.second.m_bind_ip, ipv6_port, ipv6_addr, m_use_ipv6, m_require_ipv4, epee::net_utils::ssl_support_t::e_ssl_support_disabled);
CHECK_AND_ASSERT_MES(res, false, "Failed to bind server");
}
}
m_listening_port = public_zone.m_net_server.get_binded_port();
- MLOG_GREEN(el::Level::Info, "Net service bound to " << public_zone.m_bind_ip << ":" << m_listening_port);
+ MLOG_GREEN(el::Level::Info, "Net service bound (IPv4) to " << public_zone.m_bind_ip << ":" << m_listening_port);
+ if (m_use_ipv6)
+ {
+ m_listening_port_ipv6 = public_zone.m_net_server.get_binded_port_ipv6();
+ MLOG_GREEN(el::Level::Info, "Net service bound (IPv6) to " << public_zone.m_bind_ipv6_address << ":" << m_listening_port_ipv6);
+ }
if(m_external_port)
MDEBUG("External port defined as " << m_external_port);
// add UPnP port mapping
if(m_igd == igd)
- add_upnp_port_mapping(m_listening_port);
+ {
+ add_upnp_port_mapping_v4(m_listening_port);
+ if (m_use_ipv6)
+ {
+ add_upnp_port_mapping_v6(m_listening_port_ipv6);
+ }
+ }
return res;
}
@@ -1997,19 +2031,43 @@ namespace nodetool
if(!node_data.my_port)
return false;
- CHECK_AND_ASSERT_MES(context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id(), false,
- "Only IPv4 addresses are supported here");
+ bool address_ok = (context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id() || context.m_remote_address.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id());
+ CHECK_AND_ASSERT_MES(address_ok, false,
+ "Only IPv4 or IPv6 addresses are supported here");
const epee::net_utils::network_address na = context.m_remote_address;
- uint32_t actual_ip = na.as<const epee::net_utils::ipv4_network_address>().ip();
+ std::string ip;
+ uint32_t ipv4_addr;
+ boost::asio::ip::address_v6 ipv6_addr;
+ bool is_ipv4;
+ if (na.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
+ {
+ ipv4_addr = na.as<const epee::net_utils::ipv4_network_address>().ip();
+ ip = epee::string_tools::get_ip_string_from_int32(ipv4_addr);
+ is_ipv4 = true;
+ }
+ else
+ {
+ ipv6_addr = na.as<const epee::net_utils::ipv6_network_address>().ip();
+ ip = ipv6_addr.to_string();
+ is_ipv4 = false;
+ }
network_zone& zone = m_network_zones.at(na.get_zone());
if(!zone.m_peerlist.is_host_allowed(context.m_remote_address))
return false;
- std::string ip = epee::string_tools::get_ip_string_from_int32(actual_ip);
std::string port = epee::string_tools::num_to_string_fast(node_data.my_port);
- epee::net_utils::network_address address{epee::net_utils::ipv4_network_address(actual_ip, node_data.my_port)};
+
+ epee::net_utils::network_address address;
+ if (is_ipv4)
+ {
+ address = epee::net_utils::network_address{epee::net_utils::ipv4_network_address(ipv4_addr, node_data.my_port)};
+ }
+ else
+ {
+ address = epee::net_utils::network_address{epee::net_utils::ipv6_network_address(ipv6_addr, node_data.my_port)};
+ }
peerid_type pr = node_data.peer_id;
bool r = zone.m_net_server.connect_async(ip, port, zone.m_config.m_net_config.ping_connection_timeout, [cb, /*context,*/ address, pr, this](
const typename net_server::t_connection_context& ping_context,
@@ -2193,12 +2251,19 @@ namespace nodetool
//try ping to be sure that we can add this peer to peer_list
try_ping(arg.node_data, context, [peer_id_l, port_l, context, this]()
{
- CHECK_AND_ASSERT_MES(context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id(), void(),
- "Only IPv4 addresses are supported here");
+ CHECK_AND_ASSERT_MES((context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id() || context.m_remote_address.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id()), void(),
+ "Only IPv4 or IPv6 addresses are supported here");
//called only(!) if success pinged, update local peerlist
peerlist_entry pe;
const epee::net_utils::network_address na = context.m_remote_address;
- pe.adr = epee::net_utils::ipv4_network_address(na.as<epee::net_utils::ipv4_network_address>().ip(), port_l);
+ if (context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
+ {
+ pe.adr = epee::net_utils::ipv4_network_address(na.as<epee::net_utils::ipv4_network_address>().ip(), port_l);
+ }
+ else
+ {
+ pe.adr = epee::net_utils::ipv6_network_address(na.as<epee::net_utils::ipv6_network_address>().ip(), port_l);
+ }
time_t last_seen;
time(&last_seen);
pe.last_seen = static_cast<int64_t>(last_seen);
@@ -2565,16 +2630,19 @@ namespace nodetool
}
template<class t_payload_net_handler>
- void node_server<t_payload_net_handler>::add_upnp_port_mapping(uint32_t port)
+ void node_server<t_payload_net_handler>::add_upnp_port_mapping_impl(uint32_t port, bool ipv6) // if ipv6 false, do ipv4
{
- MDEBUG("Attempting to add IGD port mapping.");
+ std::string ipversion = ipv6 ? "(IPv6)" : "(IPv4)";
+ MDEBUG("Attempting to add IGD port mapping " << ipversion << ".");
int result;
+ const int ipv6_arg = ipv6 ? 1 : 0;
+
#if MINIUPNPC_API_VERSION > 13
// default according to miniupnpc.h
unsigned char ttl = 2;
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, ttl, &result);
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, ttl, &result);
#else
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result);
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, &result);
#endif
UPNPUrls urls;
IGDdatas igdData;
@@ -2611,16 +2679,38 @@ namespace nodetool
}
template<class t_payload_net_handler>
- void node_server<t_payload_net_handler>::delete_upnp_port_mapping(uint32_t port)
+ void node_server<t_payload_net_handler>::add_upnp_port_mapping_v4(uint32_t port)
+ {
+ add_upnp_port_mapping_impl(port, false);
+ }
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::add_upnp_port_mapping_v6(uint32_t port)
+ {
+ add_upnp_port_mapping_impl(port, true);
+ }
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::add_upnp_port_mapping(uint32_t port, bool ipv4, bool ipv6)
+ {
+ if (ipv4) add_upnp_port_mapping_v4(port);
+ if (ipv6) add_upnp_port_mapping_v6(port);
+ }
+
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::delete_upnp_port_mapping_impl(uint32_t port, bool ipv6)
{
- MDEBUG("Attempting to delete IGD port mapping.");
+ std::string ipversion = ipv6 ? "(IPv6)" : "(IPv4)";
+ MDEBUG("Attempting to delete IGD port mapping " << ipversion << ".");
int result;
+ const int ipv6_arg = ipv6 ? 1 : 0;
#if MINIUPNPC_API_VERSION > 13
// default according to miniupnpc.h
unsigned char ttl = 2;
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, ttl, &result);
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, ttl, &result);
#else
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result);
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, &result);
#endif
UPNPUrls urls;
IGDdatas igdData;
@@ -2653,6 +2743,25 @@ namespace nodetool
}
}
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::delete_upnp_port_mapping_v4(uint32_t port)
+ {
+ delete_upnp_port_mapping_impl(port, false);
+ }
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::delete_upnp_port_mapping_v6(uint32_t port)
+ {
+ delete_upnp_port_mapping_impl(port, true);
+ }
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::delete_upnp_port_mapping(uint32_t port)
+ {
+ delete_upnp_port_mapping_v4(port);
+ delete_upnp_port_mapping_v6(port);
+ }
+
template<typename t_payload_net_handler>
boost::optional<p2p_connection_context_t<typename t_payload_net_handler::connection_context>>
node_server<t_payload_net_handler>::socks_connect(network_zone& zone, const epee::net_utils::network_address& remote, epee::net_utils::ssl_support_t ssl_support)
@@ -2671,13 +2780,34 @@ namespace nodetool
boost::optional<p2p_connection_context_t<typename t_payload_net_handler::connection_context>>
node_server<t_payload_net_handler>::public_connect(network_zone& zone, epee::net_utils::network_address const& na, epee::net_utils::ssl_support_t ssl_support)
{
- CHECK_AND_ASSERT_MES(na.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id(), boost::none,
- "Only IPv4 addresses are supported here");
- const epee::net_utils::ipv4_network_address &ipv4 = na.as<const epee::net_utils::ipv4_network_address>();
+ bool is_ipv4 = na.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id();
+ bool is_ipv6 = na.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id();
+ CHECK_AND_ASSERT_MES(is_ipv4 || is_ipv6, boost::none,
+ "Only IPv4 or IPv6 addresses are supported here");
+
+ std::string address;
+ std::string port;
+
+ if (is_ipv4)
+ {
+ const epee::net_utils::ipv4_network_address &ipv4 = na.as<const epee::net_utils::ipv4_network_address>();
+ address = epee::string_tools::get_ip_string_from_int32(ipv4.ip());
+ port = epee::string_tools::num_to_string_fast(ipv4.port());
+ }
+ else if (is_ipv6)
+ {
+ const epee::net_utils::ipv6_network_address &ipv6 = na.as<const epee::net_utils::ipv6_network_address>();
+ address = ipv6.ip().to_string();
+ port = epee::string_tools::num_to_string_fast(ipv6.port());
+ }
+ else
+ {
+ LOG_ERROR("Only IPv4 or IPv6 addresses are supported here");
+ return boost::none;
+ }
typename net_server::t_connection_context con{};
- const bool res = zone.m_net_server.connect(epee::string_tools::get_ip_string_from_int32(ipv4.ip()),
- epee::string_tools::num_to_string_fast(ipv4.port()),
+ const bool res = zone.m_net_server.connect(address, port,
zone.m_config.m_net_config.connection_timeout,
con, "0.0.0.0", ssl_support);
diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h
index 32f30adca..05eb36e65 100644
--- a/src/p2p/net_peerlist_boost_serialization.h
+++ b/src/p2p/net_peerlist_boost_serialization.h
@@ -76,6 +76,9 @@ namespace boost
case epee::net_utils::ipv4_network_address::get_type_id():
do_serialize<epee::net_utils::ipv4_network_address>(is_saving, a, na);
break;
+ case epee::net_utils::ipv6_network_address::get_type_id():
+ do_serialize<epee::net_utils::ipv6_network_address>(is_saving, a, na);
+ break;
case net::tor_address::get_type_id():
do_serialize<net::tor_address>(is_saving, a, na);
break;
@@ -99,6 +102,34 @@ namespace boost
}
template <class Archive, class ver_type>
+ inline void serialize(Archive &a, boost::asio::ip::address_v6& v6, const ver_type ver)
+ {
+ if (typename Archive::is_saving())
+ {
+ auto bytes = v6.to_bytes();
+ for (auto &e: bytes) a & e;
+ }
+ else
+ {
+ boost::asio::ip::address_v6::bytes_type bytes;
+ for (auto &e: bytes) a & e;
+ v6 = boost::asio::ip::address_v6(bytes);
+ }
+ }
+
+ template <class Archive, class ver_type>
+ inline void serialize(Archive &a, epee::net_utils::ipv6_network_address& na, const ver_type ver)
+ {
+ boost::asio::ip::address_v6 ip{na.ip()};
+ uint16_t port{na.port()};
+ a & ip;
+ a & port;
+ if (!typename Archive::is_saving())
+ na = epee::net_utils::ipv6_network_address{ip, port};
+ }
+
+
+ template <class Archive, class ver_type>
inline void save(Archive& a, const net::tor_address& na, const ver_type)
{
const size_t length = std::strlen(na.host_str());
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index d80219f1c..9aaaa026d 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -142,6 +142,7 @@ namespace cryptonote
{
m_restricted = restricted;
m_net_server.set_threads_prefix("RPC");
+ m_net_server.set_connection_filter(&m_p2p);
auto rpc_config = cryptonote::rpc_args::process(vm, true);
if (!rpc_config)
@@ -161,7 +162,9 @@ namespace cryptonote
auto rng = [](size_t len, uint8_t *ptr){ return crypto::rand(len, ptr); };
return epee::http_server_impl_base<core_rpc_server, connection_context>::init(
- rng, std::move(port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login), std::move(rpc_config->ssl_options)
+ rng, std::move(port), std::move(rpc_config->bind_ip),
+ std::move(rpc_config->bind_ipv6_address), std::move(rpc_config->use_ipv6), std::move(rpc_config->require_ipv4),
+ std::move(rpc_config->access_control_origins), std::move(http_login), std::move(rpc_config->ssl_options)
);
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -173,6 +176,24 @@ namespace cryptonote
}
return true;
}
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::add_host_fail(const connection_context *ctx)
+ {
+ if(!ctx || !ctx->m_remote_address.is_blockable())
+ return false;
+
+ CRITICAL_REGION_LOCAL(m_host_fails_score_lock);
+ uint64_t fails = ++m_host_fails_score[ctx->m_remote_address.host_str()];
+ MDEBUG("Host " << ctx->m_remote_address.host_str() << " fail score=" << fails);
+ if(fails > RPC_IP_FAILS_BEFORE_BLOCK)
+ {
+ auto it = m_host_fails_score.find(ctx->m_remote_address.host_str());
+ CHECK_AND_ASSERT_MES(it != m_host_fails_score.end(), false, "internal error");
+ it->second = RPC_IP_FAILS_BEFORE_BLOCK/2;
+ m_p2p.block_host(ctx->m_remote_address);
+ }
+ return true;
+ }
#define CHECK_CORE_READY() do { if(!check_core_ready()){res.status = CORE_RPC_STATUS_BUSY;return true;} } while(0)
//------------------------------------------------------------------------------------------------------------------------------
@@ -300,6 +321,7 @@ namespace cryptonote
if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, !req.no_miner_tx, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT))
{
res.status = "Failed";
+ add_host_fail(ctx);
return false;
}
@@ -423,6 +445,7 @@ namespace cryptonote
if(!m_core.get_blockchain_storage().find_blockchain_supplement(req.block_ids, res.m_block_ids, res.start_height, res.current_height, false))
{
res.status = "Failed";
+ add_host_fail(ctx);
return false;
}
@@ -603,7 +626,8 @@ namespace cryptonote
return true;
}
const cryptonote::blobdata pruned = ss.str();
- sorted_txs.push_back(std::make_tuple(h, pruned, get_transaction_prunable_hash(tx), std::string(i->tx_blob, pruned.size())));
+ const crypto::hash prunable_hash = tx.version == 1 ? crypto::null_hash : get_transaction_prunable_hash(tx);
+ sorted_txs.push_back(std::make_tuple(h, pruned, prunable_hash, std::string(i->tx_blob, pruned.size())));
missed_txs.erase(std::find(missed_txs.begin(), missed_txs.end(), h));
pool_tx_hashes.insert(h);
const std::string hash_string = epee::string_tools::pod_to_hex(h);
@@ -862,6 +886,8 @@ namespace cryptonote
add_reason(reason, "fee too low");
if ((res.not_rct = tvc.m_not_rct))
add_reason(reason, "tx is not ringct");
+ if ((res.too_few_outputs = tvc.m_too_few_outputs))
+ add_reason(reason, "too few outputs");
const std::string punctuation = reason.empty() ? "" : ": ";
if (tvc.m_verifivation_failed)
{
@@ -1026,6 +1052,11 @@ namespace cryptonote
if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
res.white_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv4_network_address>().ip(),
entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
+ else if (entry.adr.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id())
+ {
+ res.white_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv6_network_address>().host_str(),
+ entry.adr.as<epee::net_utils::ipv6_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
+ }
else
res.white_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
}
@@ -1036,6 +1067,9 @@ namespace cryptonote
if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
res.gray_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv4_network_address>().ip(),
entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
+ else if (entry.adr.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id())
+ res.white_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv6_network_address>().host_str(),
+ entry.adr.as<epee::net_utils::ipv6_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
else
res.gray_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
}
@@ -1044,6 +1078,45 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_get_public_nodes(const COMMAND_RPC_GET_PUBLIC_NODES::request& req, COMMAND_RPC_GET_PUBLIC_NODES::response& res, const connection_context *ctx)
+ {
+ PERF_TIMER(on_get_public_nodes);
+
+ COMMAND_RPC_GET_PEER_LIST::response peer_list_res;
+ const bool success = on_get_peer_list(COMMAND_RPC_GET_PEER_LIST::request(), peer_list_res, ctx);
+ res.status = peer_list_res.status;
+ if (!success)
+ {
+ return false;
+ }
+ if (res.status != CORE_RPC_STATUS_OK)
+ {
+ return true;
+ }
+
+ const auto collect = [](const std::vector<peer> &peer_list, std::vector<public_node> &public_nodes)
+ {
+ for (const auto &entry : peer_list)
+ {
+ if (entry.rpc_port != 0)
+ {
+ public_nodes.emplace_back(entry);
+ }
+ }
+ };
+
+ if (req.white)
+ {
+ collect(peer_list_res.white_list, res.white);
+ }
+ if (req.gray)
+ {
+ collect(peer_list_res.gray_list, res.gray);
+ }
+
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_set_log_hash_rate(const COMMAND_RPC_SET_LOG_HASH_RATE::request& req, COMMAND_RPC_SET_LOG_HASH_RATE::response& res, const connection_context *ctx)
{
PERF_TIMER(on_set_log_hash_rate);
@@ -1600,38 +1673,55 @@ namespace cryptonote
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH>(invoke_http_mode::JON_RPC, "getblockheaderbyhash", req, res, r))
return r;
- crypto::hash block_hash;
- bool hash_parsed = parse_hash256(req.hash, block_hash);
- if(!hash_parsed)
- {
- error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
- error_resp.message = "Failed to parse hex representation of block hash. Hex = " + req.hash + '.';
- return false;
- }
- block blk;
- bool orphan = false;
- bool have_block = m_core.get_block_by_hash(block_hash, blk, &orphan);
- if (!have_block)
- {
- error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
- error_resp.message = "Internal error: can't get block by hash. Hash = " + req.hash + '.';
- return false;
- }
- if (blk.miner_tx.vin.size() != 1 || blk.miner_tx.vin.front().type() != typeid(txin_gen))
+ auto get = [this](const std::string &hash, bool fill_pow_hash, block_header_response &block_header, bool restricted, epee::json_rpc::error& error_resp) -> bool {
+ crypto::hash block_hash;
+ bool hash_parsed = parse_hash256(hash, block_hash);
+ if(!hash_parsed)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
+ error_resp.message = "Failed to parse hex representation of block hash. Hex = " + hash + '.';
+ return false;
+ }
+ block blk;
+ bool orphan = false;
+ bool have_block = m_core.get_block_by_hash(block_hash, blk, &orphan);
+ if (!have_block)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Internal error: can't get block by hash. Hash = " + hash + '.';
+ return false;
+ }
+ if (blk.miner_tx.vin.size() != 1 || blk.miner_tx.vin.front().type() != typeid(txin_gen))
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Internal error: coinbase transaction in the block has the wrong type";
+ return false;
+ }
+ uint64_t block_height = boost::get<txin_gen>(blk.miner_tx.vin.front()).height;
+ bool response_filled = fill_block_header_response(blk, orphan, block_height, block_hash, block_header, fill_pow_hash && !restricted);
+ if (!response_filled)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Internal error: can't produce valid response.";
+ return false;
+ }
+ return true;
+ };
+
+ const bool restricted = m_restricted && ctx;
+ if (!req.hash.empty())
{
- error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
- error_resp.message = "Internal error: coinbase transaction in the block has the wrong type";
- return false;
+ if (!get(req.hash, req.fill_pow_hash, res.block_header, restricted, error_resp))
+ return false;
}
- uint64_t block_height = boost::get<txin_gen>(blk.miner_tx.vin.front()).height;
- const bool restricted = m_restricted && ctx;
- bool response_filled = fill_block_header_response(blk, orphan, block_height, block_hash, res.block_header, req.fill_pow_hash && !restricted);
- if (!response_filled)
+ res.block_headers.reserve(req.hashes.size());
+ for (const std::string &hash: req.hashes)
{
- error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
- error_resp.message = "Internal error: can't produce valid response.";
- return false;
+ res.block_headers.push_back({});
+ if (!get(hash, req.fill_pow_hash, res.block_headers.back(), restricted, error_resp))
+ return false;
}
+
res.status = CORE_RPC_STATUS_OK;
return true;
}
@@ -2061,7 +2151,7 @@ namespace cryptonote
PERF_TIMER(on_get_alternate_chains);
try
{
- std::list<std::pair<Blockchain::block_extended_info, std::vector<crypto::hash>>> chains = m_core.get_blockchain_storage().get_alternative_chains();
+ std::vector<std::pair<Blockchain::block_extended_info, std::vector<crypto::hash>>> chains = m_core.get_blockchain_storage().get_alternative_chains();
for (const auto &i: chains)
{
difficulty_type wdiff = i.first.cumulative_difficulty;
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index 663975617..e91d4c953 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -108,6 +108,7 @@ namespace cryptonote
MAP_URI_AUTO_JON2_IF("/mining_status", on_mining_status, COMMAND_RPC_MINING_STATUS, !m_restricted)
MAP_URI_AUTO_JON2_IF("/save_bc", on_save_bc, COMMAND_RPC_SAVE_BC, !m_restricted)
MAP_URI_AUTO_JON2_IF("/get_peer_list", on_get_peer_list, COMMAND_RPC_GET_PEER_LIST, !m_restricted)
+ MAP_URI_AUTO_JON2_IF("/get_public_nodes", on_get_public_nodes, COMMAND_RPC_GET_PUBLIC_NODES, !m_restricted)
MAP_URI_AUTO_JON2_IF("/set_log_hash_rate", on_set_log_hash_rate, COMMAND_RPC_SET_LOG_HASH_RATE, !m_restricted)
MAP_URI_AUTO_JON2_IF("/set_log_level", on_set_log_level, COMMAND_RPC_SET_LOG_LEVEL, !m_restricted)
MAP_URI_AUTO_JON2_IF("/set_log_categories", on_set_log_categories, COMMAND_RPC_SET_LOG_CATEGORIES, !m_restricted)
@@ -186,6 +187,7 @@ namespace cryptonote
bool on_get_net_stats(const COMMAND_RPC_GET_NET_STATS::request& req, COMMAND_RPC_GET_NET_STATS::response& res, const connection_context *ctx = NULL);
bool on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res, const connection_context *ctx = NULL);
bool on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res, const connection_context *ctx = NULL);
+ bool on_get_public_nodes(const COMMAND_RPC_GET_PUBLIC_NODES::request& req, COMMAND_RPC_GET_PUBLIC_NODES::response& res, const connection_context *ctx = NULL);
bool on_set_log_hash_rate(const COMMAND_RPC_SET_LOG_HASH_RATE::request& req, COMMAND_RPC_SET_LOG_HASH_RATE::response& res, const connection_context *ctx = NULL);
bool on_set_log_level(const COMMAND_RPC_SET_LOG_LEVEL::request& req, COMMAND_RPC_SET_LOG_LEVEL::response& res, const connection_context *ctx = NULL);
bool on_set_log_categories(const COMMAND_RPC_SET_LOG_CATEGORIES::request& req, COMMAND_RPC_SET_LOG_CATEGORIES::response& res, const connection_context *ctx = NULL);
@@ -236,6 +238,7 @@ namespace cryptonote
private:
bool check_core_busy();
bool check_core_ready();
+ bool add_host_fail(const connection_context *ctx);
//utils
uint64_t get_block_reward(const block& blk);
@@ -256,6 +259,8 @@ private:
bool m_was_bootstrap_ever_used;
network_type m_nettype;
bool m_restricted;
+ epee::critical_section m_host_fails_score_lock;
+ std::map<std::string, uint64_t> m_host_fails_score;
};
}
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index 571a71207..7ae0c77b2 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -87,7 +87,7 @@ namespace cryptonote
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define CORE_RPC_VERSION_MAJOR 2
-#define CORE_RPC_VERSION_MINOR 7
+#define CORE_RPC_VERSION_MINOR 8
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
@@ -612,6 +612,7 @@ namespace cryptonote
bool overspend;
bool fee_too_low;
bool not_rct;
+ bool too_few_outputs;
bool sanity_check_failed;
bool untrusted;
@@ -627,6 +628,7 @@ namespace cryptonote
KV_SERIALIZE(overspend)
KV_SERIALIZE(fee_too_low)
KV_SERIALIZE(not_rct)
+ KV_SERIALIZE(too_few_outputs)
KV_SERIALIZE(sanity_check_failed)
KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
@@ -1094,10 +1096,12 @@ namespace cryptonote
struct request_t
{
std::string hash;
+ std::vector<std::string> hashes;
bool fill_pow_hash;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(hash)
+ KV_SERIALIZE(hashes)
KV_SERIALIZE_OPT(fill_pow_hash, false);
END_KV_SERIALIZE_MAP()
};
@@ -1107,10 +1111,12 @@ namespace cryptonote
{
std::string status;
block_header_response block_header;
+ std::vector<block_header_response> block_headers;
bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(block_header)
+ KV_SERIALIZE(block_headers)
KV_SERIALIZE(status)
KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
@@ -1200,6 +1206,9 @@ namespace cryptonote
peer(uint64_t id, const std::string &host, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port)
: id(id), host(host), ip(0), port(0), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed)
{}
+ peer(uint64_t id, const std::string &host, uint16_t port, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port)
+ : id(id), host(host), ip(0), port(port), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed)
+ {}
peer(uint64_t id, uint32_t ip, uint16_t port, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port)
: id(id), host(epee::string_tools::get_ip_string_from_int32(ip)), ip(ip), port(port), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed)
{}
@@ -1239,6 +1248,54 @@ namespace cryptonote
typedef epee::misc_utils::struct_init<response_t> response;
};
+ struct public_node
+ {
+ std::string host;
+ uint64_t last_seen;
+ uint16_t rpc_port;
+
+ public_node() = delete;
+
+ public_node(const peer &peer)
+ : host(peer.host), last_seen(peer.last_seen), rpc_port(peer.rpc_port)
+ {}
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(host)
+ KV_SERIALIZE(last_seen)
+ KV_SERIALIZE(rpc_port)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct COMMAND_RPC_GET_PUBLIC_NODES
+ {
+ struct request_t
+ {
+ bool gray;
+ bool white;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_OPT(gray, false)
+ KV_SERIALIZE_OPT(white, true)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ std::string status;
+ std::vector<public_node> gray;
+ std::vector<public_node> white;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ KV_SERIALIZE(gray)
+ KV_SERIALIZE(white)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
struct COMMAND_RPC_SET_LOG_HASH_RATE
{
struct request_t
@@ -2095,7 +2152,7 @@ namespace cryptonote
struct response_t
{
std::string status;
- std::list<chain_info> chains;
+ std::vector<chain_info> chains;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp
index 612b2cab6..890380dc8 100644
--- a/src/rpc/daemon_handler.cpp
+++ b/src/rpc/daemon_handler.cpp
@@ -343,6 +343,11 @@ namespace rpc
if (!res.error_details.empty()) res.error_details += " and ";
res.error_details = "tx is not ringct";
}
+ if (tvc.m_too_few_outputs)
+ {
+ if (!res.error_details.empty()) res.error_details += " and ";
+ res.error_details = "too few outputs";
+ }
if (res.error_details.empty())
{
res.error_details = "an unknown issue was found with the transaction";
diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp
index 4479bd1f1..68b33cb8c 100644
--- a/src/rpc/rpc_args.cpp
+++ b/src/rpc/rpc_args.cpp
@@ -90,6 +90,9 @@ namespace cryptonote
rpc_args::descriptors::descriptors()
: rpc_bind_ip({"rpc-bind-ip", rpc_args::tr("Specify IP to bind RPC server"), "127.0.0.1"})
+ , rpc_bind_ipv6_address({"rpc-bind-ipv6-address", rpc_args::tr("Specify IPv6 address to bind RPC server"), "::1"})
+ , rpc_use_ipv6({"rpc-use-ipv6", rpc_args::tr("Allow IPv6 for RPC"), false})
+ , rpc_require_ipv4({"rpc-require-ipv4", rpc_args::tr("Require successful IPv4 bind for RPC"), true})
, rpc_login({"rpc-login", rpc_args::tr("Specify username[:password] required for RPC server"), "", true})
, confirm_external_bind({"confirm-external-bind", rpc_args::tr("Confirm rpc-bind-ip value is NOT a loopback (local) IP")})
, rpc_access_control_origins({"rpc-access-control-origins", rpc_args::tr("Specify a comma separated list of origins to allow cross origin resource sharing"), ""})
@@ -108,6 +111,9 @@ namespace cryptonote
{
const descriptors arg{};
command_line::add_arg(desc, arg.rpc_bind_ip);
+ command_line::add_arg(desc, arg.rpc_bind_ipv6_address);
+ command_line::add_arg(desc, arg.rpc_use_ipv6);
+ command_line::add_arg(desc, arg.rpc_require_ipv4);
command_line::add_arg(desc, arg.rpc_login);
command_line::add_arg(desc, arg.confirm_external_bind);
command_line::add_arg(desc, arg.rpc_access_control_origins);
@@ -127,6 +133,9 @@ namespace cryptonote
rpc_args config{};
config.bind_ip = command_line::get_arg(vm, arg.rpc_bind_ip);
+ config.bind_ipv6_address = command_line::get_arg(vm, arg.rpc_bind_ipv6_address);
+ config.use_ipv6 = command_line::get_arg(vm, arg.rpc_use_ipv6);
+ config.require_ipv4 = command_line::get_arg(vm, arg.rpc_require_ipv4);
if (!config.bind_ip.empty())
{
// always parse IP here for error consistency
@@ -148,6 +157,34 @@ namespace cryptonote
return boost::none;
}
}
+ if (!config.bind_ipv6_address.empty())
+ {
+ // allow square braces, but remove them here if present
+ if (config.bind_ipv6_address.find('[') != std::string::npos)
+ {
+ config.bind_ipv6_address = config.bind_ipv6_address.substr(1, config.bind_ipv6_address.size() - 2);
+ }
+
+
+ // always parse IP here for error consistency
+ boost::system::error_code ec{};
+ const auto parsed_ip = boost::asio::ip::address::from_string(config.bind_ipv6_address, ec);
+ if (ec)
+ {
+ LOG_ERROR(tr("Invalid IP address given for --") << arg.rpc_bind_ipv6_address.name);
+ return boost::none;
+ }
+
+ if (!parsed_ip.is_loopback() && !command_line::get_arg(vm, arg.confirm_external_bind))
+ {
+ LOG_ERROR(
+ "--" << arg.rpc_bind_ipv6_address.name <<
+ tr(" permits inbound unencrypted external connections. Consider SSH tunnel or SSL proxy instead. Override with --") <<
+ arg.confirm_external_bind.name
+ );
+ return boost::none;
+ }
+ }
const char *env_rpc_login = nullptr;
const bool has_rpc_arg = command_line::has_arg(vm, arg.rpc_login);
diff --git a/src/rpc/rpc_args.h b/src/rpc/rpc_args.h
index 619f02b42..cd154a4d0 100644
--- a/src/rpc/rpc_args.h
+++ b/src/rpc/rpc_args.h
@@ -52,6 +52,9 @@ namespace cryptonote
descriptors& operator=(descriptors&&) = delete;
const command_line::arg_descriptor<std::string> rpc_bind_ip;
+ const command_line::arg_descriptor<std::string> rpc_bind_ipv6_address;
+ const command_line::arg_descriptor<bool> rpc_use_ipv6;
+ const command_line::arg_descriptor<bool> rpc_require_ipv4;
const command_line::arg_descriptor<std::string> rpc_login;
const command_line::arg_descriptor<bool> confirm_external_bind;
const command_line::arg_descriptor<std::string> rpc_access_control_origins;
@@ -76,6 +79,9 @@ namespace cryptonote
static boost::optional<epee::net_utils::ssl_options_t> process_ssl(const boost::program_options::variables_map& vm, const bool any_cert_option = false);
std::string bind_ip;
+ std::string bind_ipv6_address;
+ bool use_ipv6;
+ bool require_ipv4;
std::vector<std::string> access_control_origins;
boost::optional<tools::login> login; // currently `boost::none` if unspecified by user
epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_enabled;
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index fca14c8f8..10901bb51 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -3972,7 +3972,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
if (m_restoring && m_generate_from_json.empty() && m_generate_from_device.empty())
{
- m_wallet->explicit_refresh_from_block_height(!(command_line::is_arg_defaulted(vm, arg_restore_height) ||
+ m_wallet->explicit_refresh_from_block_height(!(command_line::is_arg_defaulted(vm, arg_restore_height) &&
command_line::is_arg_defaulted(vm, arg_restore_date)));
if (command_line::is_arg_defaulted(vm, arg_restore_height) && !command_line::is_arg_defaulted(vm, arg_restore_date))
{
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 03db385a4..140261f0b 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -1705,7 +1705,9 @@ bool WalletImpl::setCacheAttribute(const std::string &key, const std::string &va
std::string WalletImpl::getCacheAttribute(const std::string &key) const
{
- return m_wallet->get_attribute(key);
+ std::string value;
+ m_wallet->get_attribute(key, value);
+ return value;
}
bool WalletImpl::setUserNote(const std::string &txid, const std::string &note)
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index f25e9ad97..e44fea7d7 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -217,6 +217,8 @@ namespace
add_reason(reason, "invalid input");
if (res.invalid_output)
add_reason(reason, "invalid output");
+ if (res.too_few_outputs)
+ add_reason(reason, "too few outputs");
if (res.too_big)
add_reason(reason, "too big");
if (res.overspend)
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 9d3605d11..844ecf90c 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -247,7 +247,9 @@ namespace tools
m_net_server.set_threads_prefix("RPC");
auto rng = [](size_t len, uint8_t *ptr) { return crypto::rand(len, ptr); };
return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init(
- rng, std::move(bind_port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login),
+ rng, std::move(bind_port), std::move(rpc_config->bind_ip),
+ std::move(rpc_config->bind_ipv6_address), std::move(rpc_config->use_ipv6), std::move(rpc_config->require_ipv4),
+ std::move(rpc_config->access_control_origins), std::move(http_login),
std::move(rpc_config->ssl_options)
);
}
@@ -4089,9 +4091,8 @@ namespace tools
}
}
- er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
- er.message = std::string("Invalid address");
- return false;
+ res.valid = false;
+ return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_set_daemon(const wallet_rpc::COMMAND_RPC_SET_DAEMON::request& req, wallet_rpc::COMMAND_RPC_SET_DAEMON::response& res, epee::json_rpc::error& er, const connection_context *ctx)