aboutsummaryrefslogtreecommitdiff
path: root/src/rpc
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc')
-rw-r--r--src/rpc/core_rpc_server.cpp347
-rw-r--r--src/rpc/core_rpc_server.h15
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h172
-rw-r--r--src/rpc/daemon_handler.cpp5
-rw-r--r--src/rpc/rpc_args.cpp37
-rw-r--r--src/rpc/rpc_args.h6
6 files changed, 452 insertions, 130 deletions
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 3db138719..9aaaa026d 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -105,6 +105,35 @@ namespace cryptonote
, m_p2p(p2p)
{}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::set_bootstrap_daemon(const std::string &address, const std::string &username_password)
+ {
+ boost::optional<epee::net_utils::http::login> credentials;
+ const auto loc = username_password.find(':');
+ if (loc != std::string::npos)
+ {
+ credentials = epee::net_utils::http::login(username_password.substr(0, loc), username_password.substr(loc + 1));
+ }
+ return set_bootstrap_daemon(address, credentials);
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::set_bootstrap_daemon(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials)
+ {
+ boost::unique_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
+
+ if (!address.empty())
+ {
+ if (!m_http_client.set_server(address, credentials, epee::net_utils::ssl_support_t::e_ssl_support_autodetect))
+ {
+ return false;
+ }
+ }
+
+ m_bootstrap_daemon_address = address;
+ m_should_use_bootstrap_daemon = !m_bootstrap_daemon_address.empty();
+
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::init(
const boost::program_options::variables_map& vm
, const bool restricted
@@ -113,34 +142,18 @@ 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)
return false;
- m_bootstrap_daemon_address = command_line::get_arg(vm, arg_bootstrap_daemon_address);
- if (!m_bootstrap_daemon_address.empty())
+ if (!set_bootstrap_daemon(command_line::get_arg(vm, arg_bootstrap_daemon_address),
+ command_line::get_arg(vm, arg_bootstrap_daemon_login)))
{
- const std::string &bootstrap_daemon_login = command_line::get_arg(vm, arg_bootstrap_daemon_login);
- const auto loc = bootstrap_daemon_login.find(':');
- if (!bootstrap_daemon_login.empty() && loc != std::string::npos)
- {
- epee::net_utils::http::login login;
- login.username = bootstrap_daemon_login.substr(0, loc);
- login.password = bootstrap_daemon_login.substr(loc + 1);
- m_http_client.set_server(m_bootstrap_daemon_address, login, epee::net_utils::ssl_support_t::e_ssl_support_autodetect);
- }
- else
- {
- m_http_client.set_server(m_bootstrap_daemon_address, boost::none, epee::net_utils::ssl_support_t::e_ssl_support_autodetect);
- }
- m_should_use_bootstrap_daemon = true;
- }
- else
- {
- m_should_use_bootstrap_daemon = false;
+ MERROR("Failed to parse bootstrap daemon address");
+ return false;
}
- m_was_bootstrap_ever_used = false;
boost::optional<epee::net_utils::http::login> http_login{};
@@ -149,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)
);
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -161,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)
//------------------------------------------------------------------------------------------------------------------------------
@@ -185,7 +218,10 @@ namespace cryptonote
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_INFO>(invoke_http_mode::JON, "/getinfo", req, res, r))
{
- res.bootstrap_daemon_address = m_bootstrap_daemon_address;
+ {
+ boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
+ res.bootstrap_daemon_address = m_bootstrap_daemon_address;
+ }
crypto::hash top_hash;
m_core.get_blockchain_top(res.height_without_bootstrap, top_hash);
++res.height_without_bootstrap; // turn top block height into blockchain height
@@ -224,13 +260,16 @@ namespace cryptonote
res.start_time = restricted ? 0 : (uint64_t)m_core.get_start_time();
res.free_space = restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space();
res.offline = m_core.offline();
- res.bootstrap_daemon_address = restricted ? "" : m_bootstrap_daemon_address;
res.height_without_bootstrap = restricted ? 0 : res.height;
if (restricted)
+ {
+ res.bootstrap_daemon_address = "";
res.was_bootstrap_ever_used = false;
+ }
else
{
boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
+ res.bootstrap_daemon_address = m_bootstrap_daemon_address;
res.was_bootstrap_ever_used = m_was_bootstrap_ever_used;
}
res.database_size = m_core.get_blockchain_storage().get_db().get_database_size();
@@ -282,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;
}
@@ -405,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;
}
@@ -585,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);
@@ -820,6 +862,7 @@ namespace cryptonote
res.sanity_check_failed = true;
return true;
}
+ res.sanity_check_failed = false;
cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context);
tx_verification_context tvc = AUTO_VAL_INIT(tvc);
@@ -843,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)
{
@@ -1007,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);
}
@@ -1017,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);
}
@@ -1025,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);
@@ -1124,6 +1216,28 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_set_bootstrap_daemon(const COMMAND_RPC_SET_BOOTSTRAP_DAEMON::request& req, COMMAND_RPC_SET_BOOTSTRAP_DAEMON::response& res, const connection_context *ctx)
+ {
+ PERF_TIMER(on_set_bootstrap_daemon);
+
+ boost::optional<epee::net_utils::http::login> credentials;
+ if (!req.username.empty() || !req.password.empty())
+ {
+ credentials = epee::net_utils::http::login(req.username, req.password);
+ }
+
+ if (set_bootstrap_daemon(req.address, credentials))
+ {
+ res.status = CORE_RPC_STATUS_OK;
+ }
+ else
+ {
+ res.status = "Failed to set bootstrap daemon";
+ }
+
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res, const connection_context *ctx)
{
PERF_TIMER(on_stop_daemon);
@@ -1448,10 +1562,12 @@ namespace cryptonote
bool core_rpc_server::use_bootstrap_daemon_if_necessary(const invoke_http_mode &mode, const std::string &command_name, const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res, bool &r)
{
res.untrusted = false;
+
+ boost::upgrade_lock<boost::shared_mutex> upgrade_lock(m_bootstrap_daemon_mutex);
+
if (m_bootstrap_daemon_address.empty())
return false;
- boost::unique_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
if (!m_should_use_bootstrap_daemon)
{
MINFO("The local daemon is fully synced. Not switching back to the bootstrap daemon");
@@ -1461,7 +1577,10 @@ namespace cryptonote
auto current_time = std::chrono::system_clock::now();
if (current_time - m_bootstrap_height_check_time > std::chrono::seconds(30)) // update every 30s
{
- m_bootstrap_height_check_time = current_time;
+ {
+ boost::upgrade_to_unique_lock<boost::shared_mutex> lock(upgrade_lock);
+ m_bootstrap_height_check_time = current_time;
+ }
uint64_t top_height;
crypto::hash top_hash;
@@ -1475,7 +1594,7 @@ namespace cryptonote
ok = ok && getheight_res.status == CORE_RPC_STATUS_OK;
m_should_use_bootstrap_daemon = ok && top_height + 10 < getheight_res.height;
- MINFO((m_should_use_bootstrap_daemon ? "Using" : "Not using") << " the bootstrap daemon (our height: " << top_height << ", bootstrap daemon's height: " << getheight_res.height << ")");
+ MINFO((m_should_use_bootstrap_daemon ? "Using" : "Not using") << " the bootstrap daemon (our height: " << top_height << ", bootstrap daemon's height: " << (ok ? getheight_res.height : 0) << ")");
}
if (!m_should_use_bootstrap_daemon)
return false;
@@ -1505,7 +1624,12 @@ namespace cryptonote
MERROR("Unknown invoke_http_mode: " << mode);
return false;
}
- m_was_bootstrap_ever_used = true;
+
+ {
+ boost::upgrade_to_unique_lock<boost::shared_mutex> lock(upgrade_lock);
+ m_was_bootstrap_ever_used = true;
+ }
+
r = r && res.status == CORE_RPC_STATUS_OK;
res.untrusted = true;
return true;
@@ -1549,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;
}
@@ -1771,20 +1912,60 @@ namespace cryptonote
PERF_TIMER(on_get_bans);
auto now = time(nullptr);
- std::map<std::string, time_t> blocked_hosts = m_p2p.get_blocked_hosts();
- for (std::map<std::string, time_t>::const_iterator i = blocked_hosts.begin(); i != blocked_hosts.end(); ++i)
+ std::map<epee::net_utils::network_address, time_t> blocked_hosts = m_p2p.get_blocked_hosts();
+ for (std::map<epee::net_utils::network_address, time_t>::const_iterator i = blocked_hosts.begin(); i != blocked_hosts.end(); ++i)
{
if (i->second > now) {
COMMAND_RPC_GETBANS::ban b;
- b.host = i->first;
+ b.host = i->first.host_str();
b.ip = 0;
uint32_t ip;
- if (epee::string_tools::get_ip_int32_from_string(ip, i->first))
+ if (epee::string_tools::get_ip_int32_from_string(ip, b.host))
b.ip = ip;
b.seconds = i->second - now;
res.bans.push_back(b);
}
}
+ std::map<epee::net_utils::ipv4_network_subnet, time_t> blocked_subnets = m_p2p.get_blocked_subnets();
+ for (std::map<epee::net_utils::ipv4_network_subnet, time_t>::const_iterator i = blocked_subnets.begin(); i != blocked_subnets.end(); ++i)
+ {
+ if (i->second > now) {
+ COMMAND_RPC_GETBANS::ban b;
+ b.host = i->first.host_str();
+ b.ip = 0;
+ b.seconds = i->second - now;
+ res.bans.push_back(b);
+ }
+ }
+
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_banned(const COMMAND_RPC_BANNED::request& req, COMMAND_RPC_BANNED::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
+ {
+ PERF_TIMER(on_banned);
+
+ auto na_parsed = net::get_network_address(req.address, 0);
+ if (!na_parsed)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
+ error_resp.message = "Unsupported host type";
+ return false;
+ }
+ epee::net_utils::network_address na = std::move(*na_parsed);
+
+ time_t seconds;
+ if (m_p2p.is_host_blocked(na, &seconds))
+ {
+ res.banned = true;
+ res.seconds = seconds;
+ }
+ else
+ {
+ res.banned = false;
+ res.seconds = 0;
+ }
res.status = CORE_RPC_STATUS_OK;
return true;
@@ -1797,13 +1978,29 @@ namespace cryptonote
for (auto i = req.bans.begin(); i != req.bans.end(); ++i)
{
epee::net_utils::network_address na;
+
+ // try subnet first
+ if (!i->host.empty())
+ {
+ auto ns_parsed = net::get_ipv4_subnet_address(i->host);
+ if (ns_parsed)
+ {
+ if (i->ban)
+ m_p2p.block_subnet(*ns_parsed, i->seconds);
+ else
+ m_p2p.unblock_subnet(*ns_parsed);
+ continue;
+ }
+ }
+
+ // then host
if (!i->host.empty())
{
auto na_parsed = net::get_network_address(i->host, 0);
if (!na_parsed)
{
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
- error_resp.message = "Unsupported host type";
+ error_resp.message = "Unsupported host/subnet type";
return false;
}
na = std::move(*na_parsed);
@@ -1954,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;
@@ -2038,7 +2235,9 @@ namespace cryptonote
bool core_rpc_server::on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res, const connection_context *ctx)
{
PERF_TIMER(on_out_peers);
- m_p2p.change_max_out_public_peers(req.out_peers);
+ if (req.set)
+ m_p2p.change_max_out_public_peers(req.out_peers);
+ res.out_peers = m_p2p.get_max_out_public_peers();
res.status = CORE_RPC_STATUS_OK;
return true;
}
@@ -2046,27 +2245,13 @@ namespace cryptonote
bool core_rpc_server::on_in_peers(const COMMAND_RPC_IN_PEERS::request& req, COMMAND_RPC_IN_PEERS::response& res, const connection_context *ctx)
{
PERF_TIMER(on_in_peers);
- m_p2p.change_max_in_public_peers(req.in_peers);
+ if (req.set)
+ m_p2p.change_max_in_public_peers(req.in_peers);
+ res.in_peers = m_p2p.get_max_in_public_peers();
res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
- bool core_rpc_server::on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res, const connection_context *ctx)
- {
- PERF_TIMER(on_start_save_graph);
- m_p2p.set_save_graph(true);
- res.status = CORE_RPC_STATUS_OK;
- return true;
- }
- //------------------------------------------------------------------------------------------------------------------------------
- bool core_rpc_server::on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res, const connection_context *ctx)
- {
- PERF_TIMER(on_stop_save_graph);
- m_p2p.set_save_graph(false);
- res.status = CORE_RPC_STATUS_OK;
- return true;
- }
- //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res, const connection_context *ctx)
{
PERF_TIMER(on_update);
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index e4683bbe2..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)
@@ -115,6 +116,7 @@ namespace cryptonote
MAP_URI_AUTO_JON2("/get_transaction_pool_hashes.bin", on_get_transaction_pool_hashes_bin, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN)
MAP_URI_AUTO_JON2("/get_transaction_pool_hashes", on_get_transaction_pool_hashes, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES)
MAP_URI_AUTO_JON2("/get_transaction_pool_stats", on_get_transaction_pool_stats, COMMAND_RPC_GET_TRANSACTION_POOL_STATS)
+ MAP_URI_AUTO_JON2_IF("/set_bootstrap_daemon", on_set_bootstrap_daemon, COMMAND_RPC_SET_BOOTSTRAP_DAEMON, !m_restricted)
MAP_URI_AUTO_JON2_IF("/stop_daemon", on_stop_daemon, COMMAND_RPC_STOP_DAEMON, !m_restricted)
MAP_URI_AUTO_JON2("/get_info", on_get_info, COMMAND_RPC_GET_INFO)
MAP_URI_AUTO_JON2("/getinfo", on_get_info, COMMAND_RPC_GET_INFO)
@@ -123,8 +125,6 @@ namespace cryptonote
MAP_URI_AUTO_JON2_IF("/set_limit", on_set_limit, COMMAND_RPC_SET_LIMIT, !m_restricted)
MAP_URI_AUTO_JON2_IF("/out_peers", on_out_peers, COMMAND_RPC_OUT_PEERS, !m_restricted)
MAP_URI_AUTO_JON2_IF("/in_peers", on_in_peers, COMMAND_RPC_IN_PEERS, !m_restricted)
- MAP_URI_AUTO_JON2_IF("/start_save_graph", on_start_save_graph, COMMAND_RPC_START_SAVE_GRAPH, !m_restricted)
- MAP_URI_AUTO_JON2_IF("/stop_save_graph", on_stop_save_graph, COMMAND_RPC_STOP_SAVE_GRAPH, !m_restricted)
MAP_URI_AUTO_JON2("/get_outs", on_get_outs, COMMAND_RPC_GET_OUTPUTS)
MAP_URI_AUTO_JON2_IF("/update", on_update, COMMAND_RPC_UPDATE, !m_restricted)
MAP_URI_AUTO_BIN2("/get_output_distribution.bin", on_get_output_distribution_bin, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION)
@@ -154,6 +154,7 @@ namespace cryptonote
MAP_JON_RPC_WE("hard_fork_info", on_hard_fork_info, COMMAND_RPC_HARD_FORK_INFO)
MAP_JON_RPC_WE_IF("set_bans", on_set_bans, COMMAND_RPC_SETBANS, !m_restricted)
MAP_JON_RPC_WE_IF("get_bans", on_get_bans, COMMAND_RPC_GETBANS, !m_restricted)
+ MAP_JON_RPC_WE_IF("banned", on_banned, COMMAND_RPC_BANNED, !m_restricted)
MAP_JON_RPC_WE_IF("flush_txpool", on_flush_txpool, COMMAND_RPC_FLUSH_TRANSACTION_POOL, !m_restricted)
MAP_JON_RPC_WE("get_output_histogram", on_get_output_histogram, COMMAND_RPC_GET_OUTPUT_HISTOGRAM)
MAP_JON_RPC_WE("get_version", on_get_version, COMMAND_RPC_GET_VERSION)
@@ -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);
@@ -193,13 +195,12 @@ namespace cryptonote
bool on_get_transaction_pool_hashes_bin(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::response& res, const connection_context *ctx = NULL);
bool on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res, const connection_context *ctx = NULL);
bool on_get_transaction_pool_stats(const COMMAND_RPC_GET_TRANSACTION_POOL_STATS::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_STATS::response& res, const connection_context *ctx = NULL);
+ bool on_set_bootstrap_daemon(const COMMAND_RPC_SET_BOOTSTRAP_DAEMON::request& req, COMMAND_RPC_SET_BOOTSTRAP_DAEMON::response& res, const connection_context *ctx = NULL);
bool on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res, const connection_context *ctx = NULL);
bool on_get_limit(const COMMAND_RPC_GET_LIMIT::request& req, COMMAND_RPC_GET_LIMIT::response& res, const connection_context *ctx = NULL);
bool on_set_limit(const COMMAND_RPC_SET_LIMIT::request& req, COMMAND_RPC_SET_LIMIT::response& res, const connection_context *ctx = NULL);
bool on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res, const connection_context *ctx = NULL);
bool on_in_peers(const COMMAND_RPC_IN_PEERS::request& req, COMMAND_RPC_IN_PEERS::response& res, const connection_context *ctx = NULL);
- bool on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res, const connection_context *ctx = NULL);
- bool on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res, const connection_context *ctx = NULL);
bool on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res, const connection_context *ctx = NULL);
bool on_get_output_distribution_bin(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res, const connection_context *ctx = NULL);
bool on_pop_blocks(const COMMAND_RPC_POP_BLOCKS::request& req, COMMAND_RPC_POP_BLOCKS::response& res, const connection_context *ctx = NULL);
@@ -220,6 +221,7 @@ namespace cryptonote
bool on_hard_fork_info(const COMMAND_RPC_HARD_FORK_INFO::request& req, COMMAND_RPC_HARD_FORK_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_set_bans(const COMMAND_RPC_SETBANS::request& req, COMMAND_RPC_SETBANS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_bans(const COMMAND_RPC_GETBANS::request& req, COMMAND_RPC_GETBANS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
+ bool on_banned(const COMMAND_RPC_BANNED::request& req, COMMAND_RPC_BANNED::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_flush_txpool(const COMMAND_RPC_FLUSH_TRANSACTION_POOL::request& req, COMMAND_RPC_FLUSH_TRANSACTION_POOL::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_output_histogram(const COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request& req, COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_version(const COMMAND_RPC_GET_VERSION::request& req, COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
@@ -236,10 +238,13 @@ 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);
bool fill_block_header_response(const block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_response& response, bool fill_pow_hash);
+ bool set_bootstrap_daemon(const std::string &address, const std::string &username_password);
+ bool set_bootstrap_daemon(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials);
enum invoke_http_mode { JON, BIN, JON_RPC };
template <typename COMMAND_TYPE>
bool use_bootstrap_daemon_if_necessary(const invoke_http_mode &mode, const std::string &command_name, const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res, bool &r);
@@ -254,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 cfe4bbf23..7ae0c77b2 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -29,6 +29,9 @@
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
+
+#include "string_tools.h"
+
#include "cryptonote_protocol/cryptonote_protocol_defs.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/difficulty.h"
@@ -84,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 6
+#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)
@@ -609,6 +612,7 @@ namespace cryptonote
bool overspend;
bool fee_too_low;
bool not_rct;
+ bool too_few_outputs;
bool sanity_check_failed;
bool untrusted;
@@ -624,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()
@@ -1091,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()
};
@@ -1104,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()
@@ -1197,8 +1206,11 @@ 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(std::to_string(ip)), ip(ip), port(port), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed)
+ : 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)
{}
BEGIN_KV_SERIALIZE_MAP()
@@ -1236,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
@@ -1583,6 +1643,33 @@ namespace cryptonote
typedef epee::misc_utils::struct_init<response_t> response;
};
+ struct COMMAND_RPC_SET_BOOTSTRAP_DAEMON
+ {
+ struct request_t
+ {
+ std::string address;
+ std::string username;
+ std::string password;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(username)
+ KV_SERIALIZE(password)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ std::string status;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
struct COMMAND_RPC_STOP_DAEMON
{
struct request_t
@@ -1682,8 +1769,10 @@ namespace cryptonote
{
struct request_t
{
- uint64_t out_peers;
+ bool set;
+ uint32_t out_peers;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_OPT(set, true)
KV_SERIALIZE(out_peers)
END_KV_SERIALIZE_MAP()
};
@@ -1691,9 +1780,11 @@ namespace cryptonote
struct response_t
{
+ uint32_t out_peers;
std::string status;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(out_peers)
KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
@@ -1704,8 +1795,10 @@ namespace cryptonote
{
struct request_t
{
- uint64_t in_peers;
+ bool set;
+ uint32_t in_peers;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_OPT(set, true)
KV_SERIALIZE(in_peers)
END_KV_SERIALIZE_MAP()
};
@@ -1713,55 +1806,17 @@ namespace cryptonote
struct response_t
{
+ uint32_t in_peers;
std::string status;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(in_peers)
KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
- struct COMMAND_RPC_START_SAVE_GRAPH
- {
- struct request_t
- {
- BEGIN_KV_SERIALIZE_MAP()
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<request_t> request;
-
- struct response_t
- {
- std::string status;
-
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<response_t> response;
- };
-
- struct COMMAND_RPC_STOP_SAVE_GRAPH
- {
- struct request_t
- {
- BEGIN_KV_SERIALIZE_MAP()
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<request_t> request;
-
- struct response_t
- {
- std::string status;
-
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<response_t> response;
- };
-
struct COMMAND_RPC_HARD_FORK_INFO
{
struct request_t
@@ -1876,6 +1931,33 @@ namespace cryptonote
typedef epee::misc_utils::struct_init<response_t> response;
};
+ struct COMMAND_RPC_BANNED
+ {
+ struct request_t
+ {
+ std::string address;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ std::string status;
+ bool banned;
+ uint32_t seconds;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ KV_SERIALIZE(banned)
+ KV_SERIALIZE(seconds)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
struct COMMAND_RPC_FLUSH_TRANSACTION_POOL
{
struct request_t
@@ -2070,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;