diff --git a/CMakeLists.txt b/CMakeLists.txt index dd8977532..0e66cbd18 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -982,10 +982,12 @@ list(APPEND EXTRA_LIBRARIES ${CMAKE_DL_LIBS}) if (HIDAPI_FOUND OR LibUSB_COMPILE_TEST_PASSED) if (APPLE) if(DEPENDS) - list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework IOKit") + list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework AppKit -framework IOKit") else() find_library(COREFOUNDATION CoreFoundation) + find_library(APPKIT AppKit) find_library(IOKIT IOKit) + list(APPEND EXTRA_LIBRARIES ${APPKIT}) list(APPEND EXTRA_LIBRARIES ${IOKIT}) list(APPEND EXTRA_LIBRARIES ${COREFOUNDATION}) endif() diff --git a/README.md b/README.md index ad46a2e62..19af25307 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ Dates are provided in the format YYYY-MM-DD. | 114,969 | 2019-06-14 | F For Fappening | v0.6.1.0 | v0.6.1.2 | RandomWOW, new block weight algorithm, slightly more efficient RingCT format | 160,777 | 2019-11-20 | Gaping Goatse | v0.7.0.0 | v0.7.1.0 | Only allow >= 2 outputs, change to the block median used to calculate penalty, rct sigs in coinbase forbidden, 4 unlock time as protocol rule | - | 2020-06-28 | Hallucinogenic Hypnotoad | v0.8.0.0 | v0.8.0.2 | Dandelion++ support -| 253,999 | 2020-10-09 | Illiterate Illuminati | v0.9.0.0 | v0.9.2.0 | Dynamic coinbase unlock (up to 1 mo.), Deterministic unlock times, Enforce maximum coinbase amount, show_qr_code wallet command, CLSAG +| 253,999 | 2020-10-09 | Illiterate Illuminati | v0.9.0.0 | v0.9.2.1 | Dynamic coinbase unlock (up to 1 mo.), Deterministic unlock times, Enforce maximum coinbase amount, show_qr_code wallet command, CLSAG X's indicate that these details have not been determined as of commit date. diff --git a/contrib/epee/include/byte_stream.h b/contrib/epee/include/byte_stream.h index 30abd050d..93f9ac85c 100644 --- a/contrib/epee/include/byte_stream.h +++ b/contrib/epee/include/byte_stream.h @@ -175,7 +175,7 @@ namespace epee void put_n(const std::uint8_t ch, const std::size_t count) { check(count); - std::memset(tellp(), count, ch); + std::memset(tellp(), ch, count); next_write_ += count; } diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index cbacd118c..cb1388f3b 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -207,7 +207,6 @@ PRAGMA_WARNING_DISABLE_VS(4355) buffer_ssl_init_fill = 0; if (is_income && m_ssl_support != epee::net_utils::ssl_support_t::e_ssl_support_disabled) socket().async_receive(boost::asio::buffer(buffer_), - boost::asio::socket_base::message_peek, strand_.wrap( std::bind(&connection::handle_receive, self, std::placeholders::_1, @@ -334,6 +333,9 @@ PRAGMA_WARNING_DISABLE_VS(4355) TRY_ENTRY(); //_info("[sock " << socket().native_handle() << "] Async read calledback."); + if (m_was_shutdown) + return; + if (!e) { double current_speed_down; @@ -360,6 +362,9 @@ PRAGMA_WARNING_DISABLE_VS(4355) CRITICAL_REGION_LOCAL( epee::net_utils::network_throttle_manager::m_lock_get_global_throttle_in ); delay = epee::net_utils::network_throttle_manager::get_global_throttle_in().get_sleep_time_after_tick( bytes_transferred ); } + + if (m_was_shutdown) + return; delay *= 0.5; long int ms = (long int)(delay * 100); @@ -431,6 +436,9 @@ PRAGMA_WARNING_DISABLE_VS(4355) std::size_t bytes_transferred) { TRY_ENTRY(); + + if (m_was_shutdown) return; + if (e) { // offload the error case @@ -438,13 +446,11 @@ PRAGMA_WARNING_DISABLE_VS(4355) return; } - reset_timer(get_timeout_from_bytes_read(bytes_transferred), false); - buffer_ssl_init_fill += bytes_transferred; - if (buffer_ssl_init_fill <= get_ssl_magic_size()) + MTRACE("we now have " << buffer_ssl_init_fill << "/" << get_ssl_magic_size() << " bytes needed to detect SSL"); + if (buffer_ssl_init_fill < get_ssl_magic_size()) { socket().async_receive(boost::asio::buffer(buffer_.data() + buffer_ssl_init_fill, buffer_.size() - buffer_ssl_init_fill), - boost::asio::socket_base::message_peek, strand_.wrap( boost::bind(&connection::handle_receive, connection::shared_from_this(), boost::asio::placeholders::error, @@ -470,7 +476,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) if (m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled) { // Handshake - if (!handshake(boost::asio::ssl::stream_base::server)) + if (!handshake(boost::asio::ssl::stream_base::server, boost::asio::const_buffer(buffer_.data(), buffer_ssl_init_fill))) { MERROR("SSL handshake failed"); boost::interprocess::ipcdetail::atomic_write32(&m_want_close_connection, 1); @@ -485,6 +491,11 @@ PRAGMA_WARNING_DISABLE_VS(4355) return; } } + else + { + handle_read(e, buffer_ssl_init_fill); + return; + } async_read_some(boost::asio::buffer(buffer_), strand_.wrap( @@ -651,6 +662,8 @@ PRAGMA_WARNING_DISABLE_VS(4355) boost::this_thread::sleep(boost::posix_time::milliseconds( ms ) ); m_send_que_lock.lock(); _dbg1("sleep for queue: " << ms); + if (m_was_shutdown) + return false; if (retry > retry_limit) { MWARNING("send que size is more than ABSTRACT_SERVER_SEND_QUE_MAX_COUNT(" << ABSTRACT_SERVER_SEND_QUE_MAX_COUNT << "), shutting down connection"); @@ -748,7 +761,8 @@ PRAGMA_WARNING_DISABLE_VS(4355) template void connection::reset_timer(boost::posix_time::milliseconds ms, bool add) { - if (ms.total_milliseconds() < 0) + const auto tms = ms.total_milliseconds(); + if (tms < 0 || (add && tms == 0)) { MWARNING("Ignoring negative timeout " << ms); return; diff --git a/contrib/epee/include/net/connection_basic.hpp b/contrib/epee/include/net/connection_basic.hpp index 90303a785..23873f65b 100644 --- a/contrib/epee/include/net/connection_basic.hpp +++ b/contrib/epee/include/net/connection_basic.hpp @@ -132,10 +132,10 @@ class connection_basic { // not-templated base class for rapid developmet of som ssl_support_t get_ssl_support() const { return m_ssl_support; } void disable_ssl() { m_ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_disabled; } - bool handshake(boost::asio::ssl::stream_base::handshake_type type) + bool handshake(boost::asio::ssl::stream_base::handshake_type type, boost::asio::const_buffer buffer = {}) { //m_state != nullptr verified in constructor - return m_state->ssl_options().handshake(socket_, type); + return m_state->ssl_options().handshake(socket_, type, buffer); } template diff --git a/contrib/epee/include/net/levin_base.h b/contrib/epee/include/net/levin_base.h index ad561c5b6..fce6d4b7e 100644 --- a/contrib/epee/include/net/levin_base.h +++ b/contrib/epee/include/net/levin_base.h @@ -72,7 +72,8 @@ namespace levin #define LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED 0 -#define LEVIN_DEFAULT_MAX_PACKET_SIZE 100000000 //100MB by default +#define LEVIN_INITIAL_MAX_PACKET_SIZE 256*1024 // 256 KiB before handshake +#define LEVIN_DEFAULT_MAX_PACKET_SIZE 100000000 //100MB by default after handshake #define LEVIN_PACKET_REQUEST 0x00000001 #define LEVIN_PACKET_RESPONSE 0x00000002 diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h index 8cb2be3e1..ddde701ee 100644 --- a/contrib/epee/include/net/levin_protocol_handler_async.h +++ b/contrib/epee/include/net/levin_protocol_handler_async.h @@ -84,7 +84,8 @@ class async_protocol_handler_config public: typedef t_connection_context connection_context; - uint64_t m_max_packet_size; + uint64_t m_initial_max_packet_size; + uint64_t m_max_packet_size; uint64_t m_invoke_timeout; int invoke(int command, const epee::span in_buff, std::string& buff_out, boost::uuids::uuid connection_id); @@ -105,7 +106,7 @@ public: size_t get_in_connections_count(); void set_handler(levin_commands_handler* handler, void (*destroy)(levin_commands_handler*) = NULL); - async_protocol_handler_config():m_pcommands_handler(NULL), m_pcommands_handler_destroy(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE), m_invoke_timeout(LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) + async_protocol_handler_config():m_pcommands_handler(NULL), m_pcommands_handler_destroy(NULL), m_initial_max_packet_size(LEVIN_INITIAL_MAX_PACKET_SIZE), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE), m_invoke_timeout(LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) {} ~async_protocol_handler_config() { set_handler(NULL, NULL); } void del_out_connections(size_t count); @@ -162,6 +163,7 @@ public: net_utils::i_service_endpoint* m_pservice_endpoint; config_type& m_config; t_connection_context& m_connection_context; + std::atomic m_max_packet_size; net_utils::buffer m_cache_in_buffer; stream_state m_state; @@ -289,7 +291,8 @@ public: m_current_head(bucket_head2()), m_pservice_endpoint(psnd_hndlr), m_config(config), - m_connection_context(conn_context), + m_connection_context(conn_context), + m_max_packet_size(config.m_initial_max_packet_size), m_cache_in_buffer(4 * 1024), m_state(stream_state_head) { @@ -399,13 +402,14 @@ public: } // these should never fail, but do runtime check for safety - CHECK_AND_ASSERT_MES(m_config.m_max_packet_size >= m_cache_in_buffer.size(), false, "Bad m_cache_in_buffer.size()"); - CHECK_AND_ASSERT_MES(m_config.m_max_packet_size - m_cache_in_buffer.size() >= m_fragment_buffer.size(), false, "Bad m_cache_in_buffer.size() + m_fragment_buffer.size()"); + const uint64_t max_packet_size = m_max_packet_size; + CHECK_AND_ASSERT_MES(max_packet_size >= m_cache_in_buffer.size(), false, "Bad m_cache_in_buffer.size()"); + CHECK_AND_ASSERT_MES(max_packet_size - m_cache_in_buffer.size() >= m_fragment_buffer.size(), false, "Bad m_cache_in_buffer.size() + m_fragment_buffer.size()"); // flipped to subtraction; prevent overflow since m_max_packet_size is variable and public - if(cb > m_config.m_max_packet_size - m_cache_in_buffer.size() - m_fragment_buffer.size()) + if(cb > max_packet_size - m_cache_in_buffer.size() - m_fragment_buffer.size()) { - MWARNING(m_connection_context << "Maximum packet size exceed!, m_max_packet_size = " << m_config.m_max_packet_size + MWARNING(m_connection_context << "Maximum packet size exceed!, m_max_packet_size = " << max_packet_size << ", packet received " << m_cache_in_buffer.size() + cb << ", connection will be closed."); return false; @@ -430,7 +434,7 @@ public: //async call scenario boost::shared_ptr response_handler = m_invoke_response_handlers.front(); response_handler->reset_timer(); - MDEBUG(m_connection_context << "LEVIN_PACKET partial msg received. len=" << cb); + MDEBUG(m_connection_context << "LEVIN_PACKET partial msg received. len=" << cb << ", current total " << m_cache_in_buffer.size() << "/" << m_current_head.m_cb << " (" << (100.0f * m_cache_in_buffer.size() / (m_current_head.m_cb ? m_current_head.m_cb : 1)) << "%)"); } } break; @@ -465,6 +469,14 @@ public: temp = std::move(m_fragment_buffer); m_fragment_buffer.clear(); std::memcpy(std::addressof(m_current_head), std::addressof(temp[0]), sizeof(bucket_head2)); + const size_t max_bytes = m_connection_context.get_max_bytes(m_current_head.m_command); + if(m_current_head.m_cb > std::min(max_packet_size, max_bytes)) + { + MERROR(m_connection_context << "Maximum packet size exceed!, m_max_packet_size = " << std::min(max_packet_size, max_bytes) + << ", packet header received " << m_current_head.m_cb << ", command " << m_current_head.m_command + << ", connection will be closed."); + return false; + } buff_to_invoke = {reinterpret_cast(temp.data()) + sizeof(bucket_head2), temp.size() - sizeof(bucket_head2)}; } @@ -519,6 +531,10 @@ public: m_current_head.m_command, buff_to_invoke, return_buff, m_connection_context ); + // peer_id remains unset if dropped + if (m_current_head.m_command == m_connection_context.handshake_command() && m_connection_context.handshake_complete()) + m_max_packet_size = m_config.m_max_packet_size; + bucket_head2 head = make_header(m_current_head.m_command, return_buff.size(), LEVIN_PACKET_RESPONSE, false); head.m_return_code = SWAP32LE(return_code); @@ -576,10 +592,11 @@ public: m_cache_in_buffer.erase(sizeof(bucket_head2)); m_state = stream_state_body; m_oponent_protocol_ver = m_current_head.m_protocol_version; - if(m_current_head.m_cb > m_config.m_max_packet_size) + const size_t max_bytes = m_connection_context.get_max_bytes(m_current_head.m_command); + if(m_current_head.m_cb > std::min(max_packet_size, max_bytes)) { - LOG_ERROR_CC(m_connection_context, "Maximum packet size exceed!, m_max_packet_size = " << m_config.m_max_packet_size - << ", packet header received " << m_current_head.m_cb + LOG_ERROR_CC(m_connection_context, "Maximum packet size exceed!, m_max_packet_size = " << std::min(max_packet_size, max_bytes) + << ", packet header received " << m_current_head.m_cb << ", command " << m_current_head.m_command << ", connection will be closed."); return false; } @@ -633,6 +650,9 @@ public: boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 0); CRITICAL_REGION_BEGIN(m_invoke_response_handlers_lock); + if (command == m_connection_context.handshake_command()) + m_max_packet_size = m_config.m_max_packet_size; + if(!send_message(command, in_buff, LEVIN_PACKET_REQUEST, true)) { LOG_ERROR_CC(m_connection_context, "Failed to do_send"); @@ -674,6 +694,9 @@ public: boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 0); + if (command == m_connection_context.handshake_command()) + m_max_packet_size = m_config.m_max_packet_size; + if (!send_message(command, in_buff, LEVIN_PACKET_REQUEST, true)) { LOG_ERROR_CC(m_connection_context, "Failed to send request"); diff --git a/contrib/epee/include/net/net_helper.h b/contrib/epee/include/net/net_helper.h index 508c79d76..a1f44cab0 100644 --- a/contrib/epee/include/net/net_helper.h +++ b/contrib/epee/include/net/net_helper.h @@ -179,7 +179,7 @@ namespace net_utils // SSL Options if (m_ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_enabled || m_ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect) { - if (!m_ssl_options.handshake(*m_ssl_socket, boost::asio::ssl::stream_base::client, addr, timeout)) + if (!m_ssl_options.handshake(*m_ssl_socket, boost::asio::ssl::stream_base::client, {}, addr, timeout)) { if (m_ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect) { diff --git a/contrib/epee/include/net/net_ssl.h b/contrib/epee/include/net/net_ssl.h index 643b2c486..1b1577e77 100644 --- a/contrib/epee/include/net/net_ssl.h +++ b/contrib/epee/include/net/net_ssl.h @@ -132,6 +132,7 @@ namespace net_utils bool handshake( boost::asio::ssl::stream &socket, boost::asio::ssl::stream_base::handshake_type type, + boost::asio::const_buffer buffer = {}, const std::string& host = {}, std::chrono::milliseconds timeout = std::chrono::seconds(15)) const; }; diff --git a/contrib/epee/include/rolling_median.h b/contrib/epee/include/rolling_median.h index 088a71d3e..877814e57 100644 --- a/contrib/epee/include/rolling_median.h +++ b/contrib/epee/include/rolling_median.h @@ -141,7 +141,6 @@ public: rolling_median_t(rolling_median_t &&m) { - free(data); memcpy(this, &m, sizeof(rolling_median_t)); m.data = NULL; } diff --git a/contrib/epee/include/storages/http_abstract_invoke.h b/contrib/epee/include/storages/http_abstract_invoke.h index c4cb91130..c615b20e6 100644 --- a/contrib/epee/include/storages/http_abstract_invoke.h +++ b/contrib/epee/include/storages/http_abstract_invoke.h @@ -98,7 +98,12 @@ namespace epee return false; } - return serialization::load_t_from_binary(result_struct, epee::strspan(pri->m_body)); + static const constexpr epee::serialization::portable_storage::limits_t default_http_bin_limits = { + 65536 * 3, // objects + 65536 * 3, // fields + 65536 * 3, // strings + }; + return serialization::load_t_from_binary(result_struct, epee::strspan(pri->m_body), &default_http_bin_limits); } template diff --git a/contrib/epee/include/storages/levin_abstract_invoke2.h b/contrib/epee/include/storages/levin_abstract_invoke2.h index 95f0bb410..802e16c1b 100644 --- a/contrib/epee/include/storages/levin_abstract_invoke2.h +++ b/contrib/epee/include/storages/levin_abstract_invoke2.h @@ -52,6 +52,11 @@ namespace snprintf(buf, sizeof(buf), "command-%u", command); return on_levin_traffic(context, initiator, sent, error, bytes, buf); } + static const constexpr epee::serialization::portable_storage::limits_t default_levin_limits = { + 8192, // objects + 16384, // fields + 16384, // strings + }; } namespace epee @@ -77,7 +82,7 @@ namespace epee return false; } serialization::portable_storage stg_ret; - if(!stg_ret.load_from_binary(buff_to_recv)) + if(!stg_ret.load_from_binary(buff_to_recv, &default_levin_limits)) { LOG_ERROR("Failed to load_from_binary on command " << command); return false; @@ -124,7 +129,7 @@ namespace epee return false; } typename serialization::portable_storage stg_ret; - if(!stg_ret.load_from_binary(buff_to_recv)) + if(!stg_ret.load_from_binary(buff_to_recv, &default_levin_limits)) { on_levin_traffic(context, true, false, true, buff_to_recv.size(), command); LOG_ERROR("Failed to load_from_binary on command " << command); @@ -155,7 +160,7 @@ namespace epee return false; } serialization::portable_storage stg_ret; - if(!stg_ret.load_from_binary(buff)) + if(!stg_ret.load_from_binary(buff, &default_levin_limits)) { on_levin_traffic(context, true, false, true, buff.size(), command); LOG_ERROR("Failed to load_from_binary on command " << command); @@ -205,7 +210,7 @@ namespace epee int buff_to_t_adapter(int command, const epee::span in_buff, byte_slice& buff_out, callback_t cb, t_context& context ) { serialization::portable_storage strg; - if(!strg.load_from_binary(in_buff)) + if(!strg.load_from_binary(in_buff, &default_levin_limits)) { on_levin_traffic(context, false, false, true, in_buff.size(), command); LOG_ERROR("Failed to load_from_binary in command " << command); @@ -239,7 +244,7 @@ namespace epee int buff_to_t_adapter(t_owner* powner, int command, const epee::span in_buff, callback_t cb, t_context& context) { serialization::portable_storage strg; - if(!strg.load_from_binary(in_buff)) + if(!strg.load_from_binary(in_buff, &default_levin_limits)) { on_levin_traffic(context, false, false, true, in_buff.size(), command); LOG_ERROR("Failed to load_from_binary in notify " << command); diff --git a/contrib/epee/include/storages/portable_storage.h b/contrib/epee/include/storages/portable_storage.h index 589e6ad63..f77e89cb6 100644 --- a/contrib/epee/include/storages/portable_storage.h +++ b/contrib/epee/include/storages/portable_storage.h @@ -54,6 +54,13 @@ namespace epee typedef epee::serialization::harray harray; typedef storage_entry meta_entry; + struct limits_t + { + size_t n_objects; + size_t n_fields; + size_t n_strings; // not counting field names + }; + portable_storage(){} virtual ~portable_storage(){} hsection open_section(const std::string& section_name, hsection hparent_section, bool create_if_notexist = false); @@ -84,8 +91,8 @@ namespace epee //------------------------------------------------------------------------------- bool store_to_binary(byte_slice& target, std::size_t initial_buffer_size = 8192); - bool load_from_binary(const epee::span target); - bool load_from_binary(const std::string& target) { return load_from_binary(epee::strspan(target)); } + bool load_from_binary(const epee::span target, const limits_t *limits = NULL); + bool load_from_binary(const std::string& target, const limits_t *limits = NULL) { return load_from_binary(epee::strspan(target), limits); } template bool dump_as_xml(std::string& targetObj, const std::string& root_name = ""); bool dump_as_json(std::string& targetObj, size_t indent = 0, bool insert_newlines = true); @@ -134,7 +141,7 @@ namespace epee return false;//TODO: don't think i ever again will use xml - ambiguous and "overtagged" format } inline - bool portable_storage::load_from_binary(const epee::span source) + bool portable_storage::load_from_binary(const epee::span source, const limits_t *limits) { m_root.m_entries.clear(); if(source.size() < sizeof(storage_block_header)) @@ -157,6 +164,8 @@ namespace epee } TRY_ENTRY(); throwable_buffer_reader buf_reader(source.data()+sizeof(storage_block_header), source.size()-sizeof(storage_block_header)); + if (limits) + buf_reader.set_limits(limits->n_objects, limits->n_fields, limits->n_strings); buf_reader.read(m_root); return true;//TODO: CATCH_ENTRY("portable_storage::load_from_binary", false); @@ -266,6 +275,7 @@ namespace epee static_assert(std::is_rvalue_reference(), "unexpected copy of value"); TRY_ENTRY(); CHECK_AND_ASSERT(psection, nullptr); + CHECK_AND_ASSERT(!pentry_name.empty(), nullptr); auto ins_res = psection->m_entries.emplace(pentry_name, std::forward(entry)); return &ins_res.first->second; CATCH_ENTRY("portable_storage::insert_new_entry_get_storage_entry", nullptr); diff --git a/contrib/epee/include/storages/portable_storage_from_bin.h b/contrib/epee/include/storages/portable_storage_from_bin.h index b39dc7c92..9e7b6ec34 100644 --- a/contrib/epee/include/storages/portable_storage_from_bin.h +++ b/contrib/epee/include/storages/portable_storage_from_bin.h @@ -29,6 +29,7 @@ #pragma once #include "misc_language.h" +#include "misc_log_ex.h" #include "portable_storage_base.h" #include "portable_storage_bin_utils.h" @@ -42,6 +43,24 @@ namespace epee { namespace serialization { + template + struct ps_min_bytes { + static constexpr const size_t strict = 4096; // actual low bound + }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 8; }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 8; }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 4; }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 4; }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 2; }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 2; }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 1; }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 1; }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 8; }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 1; }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 2; }; + template<> struct ps_min_bytes
{ static constexpr const size_t strict = 1; }; + template<> struct ps_min_bytes { static constexpr const size_t strict = 1; }; + struct throwable_buffer_reader { throwable_buffer_reader(const void* ptr, size_t sz); @@ -61,6 +80,9 @@ namespace epee void read(section& sec); void read(std::string& str); void read(array_entry &ae); + template + size_t min_bytes() const; + void set_limits(size_t objects, size_t fields, size_t strings); private: struct recursuion_limitation_guard { @@ -81,6 +103,13 @@ namespace epee const uint8_t* m_ptr; size_t m_count; size_t m_recursion_count; + size_t m_objects; + size_t m_fields; + size_t m_strings; + + size_t max_objects; + size_t max_fields; + size_t max_strings; }; inline throwable_buffer_reader::throwable_buffer_reader(const void* ptr, size_t sz) @@ -92,6 +121,12 @@ namespace epee m_ptr = (uint8_t*)ptr; m_count = sz; m_recursion_count = 0; + m_objects = 0; + m_fields = 0; + m_strings = 0; + max_objects = std::numeric_limits::max(); + max_fields = std::numeric_limits::max(); + max_strings = std::numeric_limits::max(); } inline void throwable_buffer_reader::read(void* target, size_t count) @@ -108,6 +143,7 @@ namespace epee RECURSION_LIMITATION(); uint8_t name_len = 0; read(name_len); + CHECK_AND_ASSERT_THROW_MES(name_len > 0, "Section name is missing"); sce_name.resize(name_len); read((void*)sce_name.data(), name_len); } @@ -138,7 +174,18 @@ namespace epee //for pod types array_entry_t sa; size_t size = read_varint(); - CHECK_AND_ASSERT_THROW_MES(size <= m_count, "Size sanity check failed"); + CHECK_AND_ASSERT_THROW_MES(size <= m_count / ps_min_bytes::strict, "Size sanity check failed"); + if (std::is_same()) + { + CHECK_AND_ASSERT_THROW_MES(size <= max_objects - m_objects, "Too many objects"); + m_objects += size; + } + else if (std::is_same()) + { + CHECK_AND_ASSERT_THROW_MES(size <= max_strings - m_strings, "Too many strings"); + m_strings += size; + } + sa.reserve(size); //TODO: add some optimization here later while(size--) @@ -204,6 +251,8 @@ namespace epee inline storage_entry throwable_buffer_reader::read_se() { RECURSION_LIMITATION(); + CHECK_AND_ASSERT_THROW_MES(m_strings + 1 <= max_strings, "Too many strings"); + m_strings += 1; return storage_entry(read()); } @@ -212,6 +261,8 @@ namespace epee inline storage_entry throwable_buffer_reader::read_se
() { RECURSION_LIMITATION(); + CHECK_AND_ASSERT_THROW_MES(m_objects < max_objects, "Too many objects"); + ++m_objects; section s;//use extra variable due to vs bug, line "storage_entry se(section()); " can't be compiled in visual studio storage_entry se(std::move(s)); section& section_entry = boost::get
(se); @@ -263,12 +314,16 @@ namespace epee RECURSION_LIMITATION(); sec.m_entries.clear(); size_t count = read_varint(); + CHECK_AND_ASSERT_THROW_MES(count <= max_fields - m_fields, "Too many object fields"); + m_fields += count; while(count--) { //read section name string std::string sec_name; read_sec_name(sec_name); - sec.m_entries.emplace(std::move(sec_name), load_storage_entry()); + const auto insert_loc = sec.m_entries.lower_bound(sec_name); + CHECK_AND_ASSERT_THROW_MES(insert_loc == sec.m_entries.end() || insert_loc->first != sec_name, "duplicate key: " << sec_name); + sec.m_entries.emplace_hint(insert_loc, std::move(sec_name), load_storage_entry()); } } inline @@ -289,5 +344,12 @@ namespace epee RECURSION_LIMITATION(); CHECK_AND_ASSERT_THROW_MES(false, "Reading array entry is not supported"); } + inline + void throwable_buffer_reader::set_limits(size_t objects, size_t fields, size_t strings) + { + max_objects = objects; + max_fields = fields; + max_strings = strings; + } } } diff --git a/contrib/epee/include/storages/portable_storage_template_helper.h b/contrib/epee/include/storages/portable_storage_template_helper.h index 39f900c8d..16dd565ec 100644 --- a/contrib/epee/include/storages/portable_storage_template_helper.h +++ b/contrib/epee/include/storages/portable_storage_template_helper.h @@ -85,10 +85,10 @@ namespace epee } //----------------------------------------------------------------------------------------------------------- template - bool load_t_from_binary(t_struct& out, const epee::span binary_buff) + bool load_t_from_binary(t_struct& out, const epee::span binary_buff, const epee::serialization::portable_storage::limits_t *limits = NULL) { portable_storage ps; - bool rs = ps.load_from_binary(binary_buff); + bool rs = ps.load_from_binary(binary_buff, limits); if(!rs) return false; diff --git a/contrib/epee/include/storages/portable_storage_to_bin.h b/contrib/epee/include/storages/portable_storage_to_bin.h index 137497e19..49a7be185 100644 --- a/contrib/epee/include/storages/portable_storage_to_bin.h +++ b/contrib/epee/include/storages/portable_storage_to_bin.h @@ -211,6 +211,7 @@ namespace epee for(const section_pair& se: sec.m_entries) { CHECK_AND_ASSERT_THROW_MES(se.first.size() < std::numeric_limits::max(), "storage_entry_name is too long: " << se.first.size() << ", val: " << se.first); + CHECK_AND_ASSERT_THROW_MES(!se.first.empty(), "storage_entry_name is empty"); uint8_t len = static_cast(se.first.size()); strm.write((const char*)&len, sizeof(len)); strm.write(se.first.data(), size_t(len)); diff --git a/contrib/epee/src/net_ssl.cpp b/contrib/epee/src/net_ssl.cpp index a09e82771..6ed27efa9 100644 --- a/contrib/epee/src/net_ssl.cpp +++ b/contrib/epee/src/net_ssl.cpp @@ -473,6 +473,7 @@ bool ssl_options_t::has_fingerprint(boost::asio::ssl::verify_context &ctx) const bool ssl_options_t::handshake( boost::asio::ssl::stream &socket, boost::asio::ssl::stream_base::handshake_type type, + boost::asio::const_buffer buffer, const std::string& host, std::chrono::milliseconds timeout) const { @@ -530,7 +531,7 @@ bool ssl_options_t::handshake( }); boost::system::error_code ec = boost::asio::error::would_block; - socket.async_handshake(type, boost::lambda::var(ec) = boost::lambda::_1); + socket.async_handshake(type, boost::asio::buffer(buffer), boost::lambda::var(ec) = boost::lambda::_1); if (io_service.stopped()) { io_service.reset(); diff --git a/contrib/gitian/README.md b/contrib/gitian/README.md index 1938462aa..e2e1d0b94 100644 --- a/contrib/gitian/README.md +++ b/contrib/gitian/README.md @@ -188,6 +188,7 @@ gpg --detach-sign ${VERSION}-linux/${GH_USER}/monero-linux-*-build.assert gpg --detach-sign ${VERSION}-win/${GH_USER}/monero-win-*-build.assert gpg --detach-sign ${VERSION}-osx/${GH_USER}/monero-osx-*-build.assert gpg --detach-sign ${VERSION}-android/${GH_USER}/monero-android-*-build.assert +gpg --detach-sign ${VERSION}-freebsd/${GH_USER}/monero-freebsd-*-build.assert ``` This will create a `.sig` file for each `.assert` file above (2 files for each platform). diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index abebb52b4..74bd72332 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -1312,17 +1312,20 @@ public: * height. The number of blocks returned is variable, based on the max_size passed. * * @param start_height the height of the first block - * @param min_count the minimum number of blocks to return, if they exist - * @param max_count the maximum number of blocks to return + * @param min_block_count the minimum number of blocks to return, if they exist + * @param max_block_count the maximum number of blocks to return + * @param max_tx_count the maximum number of txes to return * @param max_size the maximum size of block/transaction data to return (will be exceeded by one blocks's worth at most, if min_count is met) * @param blocks the returned block/transaction data * @param pruned whether to return full or pruned tx data * @param skip_coinbase whether to return or skip coinbase transactions (they're in blocks regardless) * @param get_miner_tx_hash whether to calculate and return the miner (coinbase) tx hash * + * The call will return at least min_block_count if possible, even if this contravenes max_tx_count + * * @return true iff the blocks and transactions were found */ - virtual bool get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector, std::vector>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const = 0; + virtual bool get_blocks_from(uint64_t start_height, size_t min_block_count, size_t max_block_count, size_t max_tx_count, size_t max_size, std::vector, std::vector>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const = 0; /** * @brief fetches the prunable transaction blob with the given hash diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 6a02b3bdb..49f7c182d 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -3001,7 +3001,7 @@ bool BlockchainLMDB::tx_exists(const crypto::hash& h) const if (! tx_found) { - LOG_PRINT_L1("transaction with hash " << epee::string_tools::pod_to_hex(h) << " not found in db"); + LOG_PRINT_L3("transaction with hash " << epee::string_tools::pod_to_hex(h) << " not found in db"); return false; } @@ -3032,7 +3032,7 @@ bool BlockchainLMDB::tx_exists(const crypto::hash& h, uint64_t& tx_id) const bool ret = false; if (get_result == MDB_NOTFOUND) { - LOG_PRINT_L1("transaction with hash " << epee::string_tools::pod_to_hex(h) << " not found in db"); + LOG_PRINT_L3("transaction with hash " << epee::string_tools::pod_to_hex(h) << " not found in db"); } else if (get_result) throw0(DB_ERROR(lmdb_error("DB error attempting to fetch transaction from hash", get_result).c_str())); @@ -3171,7 +3171,7 @@ bool BlockchainLMDB::get_pruned_tx_blobs_from(const crypto::hash& h, size_t coun return true; } -bool BlockchainLMDB::get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector, std::vector>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const +bool BlockchainLMDB::get_blocks_from(uint64_t start_height, size_t min_block_count, size_t max_block_count, size_t max_tx_count, size_t max_size, std::vector, std::vector>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); @@ -3185,14 +3185,15 @@ bool BlockchainLMDB::get_blocks_from(uint64_t start_height, size_t min_count, si RCURSOR(txs_prunable); } - blocks.reserve(std::min(max_count, 10000)); // guard against very large max count if only checking bytes + blocks.reserve(std::min(max_block_count, 10000)); // guard against very large max count if only checking bytes const uint64_t blockchain_height = height(); uint64_t size = 0; + size_t num_txes = 0; MDB_val_copy key(start_height); MDB_val k, v, val_tx_id; uint64_t tx_id = ~0; MDB_cursor_op op = MDB_SET; - for (uint64_t h = start_height; h < blockchain_height && blocks.size() < max_count && (size < max_size || blocks.size() < min_count); ++h) + for (uint64_t h = start_height; h < blockchain_height && blocks.size() < max_block_count && (size < max_size || blocks.size() < min_block_count); ++h) { MDB_cursor_op op = h == start_height ? MDB_SET : MDB_NEXT; int result = mdb_cursor_get(m_cur_blocks, &key, &v, op); @@ -3243,6 +3244,7 @@ bool BlockchainLMDB::get_blocks_from(uint64_t start_height, size_t min_count, si op = MDB_NEXT; current_block.second.reserve(b.tx_hashes.size()); + num_txes += b.tx_hashes.size() + (skip_coinbase ? 0 : 1); for (const auto &tx_hash: b.tx_hashes) { // get pruned data @@ -3262,6 +3264,9 @@ bool BlockchainLMDB::get_blocks_from(uint64_t start_height, size_t min_count, si current_block.second.push_back(std::make_pair(tx_hash, std::move(tx_blob))); size += current_block.second.back().second.size(); } + + if (blocks.size() >= min_block_count && num_txes >= max_tx_count) + break; } TXN_POSTFIX_RDONLY(); diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 568882ae5..0e6d70039 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -257,7 +257,7 @@ public: virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const; virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const; virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector &bd) const; - virtual bool get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector, std::vector>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const; + virtual bool get_blocks_from(uint64_t start_height, size_t min_block_count, size_t max_block_count, size_t max_tx_count, size_t max_size, std::vector, std::vector>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const; virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const; virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const; diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h index 9e74b33f1..e36026e07 100644 --- a/src/blockchain_db/testdb.h +++ b/src/blockchain_db/testdb.h @@ -70,7 +70,7 @@ public: virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; } virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; } virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector &bd) const override { return false; } - virtual bool get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector, std::vector>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const override { return false; } + virtual bool get_blocks_from(uint64_t start_height, size_t min_block_count, size_t max_block_count, size_t max_tx_count, size_t max_size, std::vector, std::vector>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const override { return false; } virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; } virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const override { return false; } virtual uint64_t get_block_height(const crypto::hash& h) const override { return 0; } diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat index 59a9eb797..4ce0b965a 100644 Binary files a/src/blocks/checkpoints.dat and b/src/blocks/checkpoints.dat differ diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index 6ecfd510e..bc9ff1eb0 100644 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -200,7 +200,7 @@ namespace cryptonote return true; } // make RPC call to daemon - // curl http://127.0.0.1:34568/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"get_block","params":{"height":271600}}' -H 'Content-Type: application/json' + // curl http://127.0.0.1:34568/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"get_block","params":{"height":278300}}' -H 'Content-Type: application/json' // "wide_cumulative_difficulty": "0x14eb4d0131fe8", ADD_CHECKPOINT2(1, "97f4ce4d7879b3bea54dcec738cd2ebb7952b4e9bb9743262310cd5fec749340", "0x2"); ADD_CHECKPOINT2(6969, "aa7b66e8c461065139b55c29538a39c33ceda93e587f84d490ed573d80511c87", "0x118eef693fd"); //Hard fork to v8 @@ -215,6 +215,7 @@ namespace cryptonote ADD_CHECKPOINT2(254287, "b37cb55abe73965b424f8028bf71bef98d069645077ffa52f0c134907b7734e3", "0x1746622f56668"); //Hard fork to v17 ADD_CHECKPOINT2(256700, "389a8ab95a80e84ec74639c1078bc67b33af208ef00f53bd9609cfc40efa7059", "0x185ace3c1bd68"); ADD_CHECKPOINT2(271600, "9597cdbdc52ca57d7dbd8f9c0a23a73194ef2ebbcfdc75c21992672706108d43", "0x1e2d2d6a2a9e8"); + ADD_CHECKPOINT2(278300, "b10dcdf7a51651f60fbcc0447409773eef1458d2c706d9a61daf467571ac19c9", "0x20a83a16d3968"); return true; } diff --git a/src/common/util.cpp b/src/common/util.cpp index f8707d65c..445a11a75 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -871,10 +871,19 @@ std::string get_nix_version_display_string() return max_concurrency; } + bool is_privacy_preserving_network(const std::string &address) + { + if (boost::ends_with(address, ".onion")) + return true; + if (boost::ends_with(address, ".i2p")) + return true; + return false; + } + bool is_local_address(const std::string &address) { // always assume Tor/I2P addresses to be untrusted by default - if (boost::ends_with(address, ".onion") || boost::ends_with(address, ".i2p")) + if (is_privacy_preserving_network(address)) { MDEBUG("Address '" << address << "' is Tor/I2P, non local"); return false; diff --git a/src/common/util.h b/src/common/util.h index 0c3409ea1..6366e5cb4 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -228,6 +228,7 @@ namespace tools unsigned get_max_concurrency(); bool is_local_address(const std::string &address); + bool is_privacy_preserving_network(const std::string &address); int vercmp(const char *v0, const char *v1); // returns < 0, 0, > 0, similar to strcmp, but more human friendly than lexical - does not attempt to validate bool sha256sum(const uint8_t *data, size_t len, crypto::hash &hash); diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt index 113fd9d86..5286256c7 100644 --- a/src/cryptonote_basic/CMakeLists.txt +++ b/src/cryptonote_basic/CMakeLists.txt @@ -38,6 +38,7 @@ endif() set(cryptonote_basic_sources account.cpp + connection_context.cpp cryptonote_basic_impl.cpp cryptonote_format_utils.cpp difficulty.cpp diff --git a/src/cryptonote_basic/connection_context.cpp b/src/cryptonote_basic/connection_context.cpp new file mode 100644 index 000000000..a0b8ca1f1 --- /dev/null +++ b/src/cryptonote_basic/connection_context.cpp @@ -0,0 +1,71 @@ +// Copyright (c) 2020, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "connection_context.h" + +#include "cryptonote_protocol/cryptonote_protocol_defs.h" +#include "p2p/p2p_protocol_defs.h" + +namespace cryptonote +{ + std::size_t cryptonote_connection_context::get_max_bytes(const int command) noexcept + { + switch (command) + { + case nodetool::COMMAND_HANDSHAKE_T::ID: + return 65536; + case nodetool::COMMAND_TIMED_SYNC_T::ID: + return 65536; + case nodetool::COMMAND_PING::ID: + return 4096; + case nodetool::COMMAND_REQUEST_SUPPORT_FLAGS::ID: + return 4096; + case cryptonote::NOTIFY_NEW_BLOCK::ID: + return 1024 * 1024 * 128; // 128 MB (max packet is a bit less than 100 MB though) + case cryptonote::NOTIFY_NEW_TRANSACTIONS::ID: + return 1024 * 1024 * 128; // 128 MB (max packet is a bit less than 100 MB though) + case cryptonote::NOTIFY_REQUEST_GET_OBJECTS::ID: + return 1024 * 1024 * 2; // 2 MB + case cryptonote::NOTIFY_RESPONSE_GET_OBJECTS::ID: + return 1024 * 1024 * 128; // 128 MB (max packet is a bit less than 100 MB though) + case cryptonote::NOTIFY_REQUEST_CHAIN::ID: + return 512 * 1024; // 512 kB + case cryptonote::NOTIFY_RESPONSE_CHAIN_ENTRY::ID: + return 1024 * 1024 * 4; // 4 MB + case cryptonote::NOTIFY_NEW_FLUFFY_BLOCK::ID: + return 1024 * 1024 * 4; // 4 MB, but it does not includes transaction data + case cryptonote::NOTIFY_REQUEST_FLUFFY_MISSING_TX::ID: + return 1024 * 1024; // 1 MB + case cryptonote::NOTIFY_GET_TXPOOL_COMPLEMENT::ID: + return 1024 * 1024 * 4; // 4 MB + default: + break; + }; + return std::numeric_limits::max(); + } +} // cryptonote diff --git a/src/cryptonote_basic/connection_context.h b/src/cryptonote_basic/connection_context.h index dfc59631f..a7d688300 100644 --- a/src/cryptonote_basic/connection_context.h +++ b/src/cryptonote_basic/connection_context.h @@ -31,6 +31,7 @@ #pragma once #include #include +#include #include #include "net/net_utils_base.h" #include "copyable_atomic.h" @@ -38,13 +39,12 @@ namespace cryptonote { - struct cryptonote_connection_context: public epee::net_utils::connection_context_base { cryptonote_connection_context(): m_state(state_before_handshake), m_remote_blockchain_height(0), m_last_response_height(0), m_last_request_time(boost::date_time::not_a_date_time), m_callback_request_count(0), m_last_known_hash(crypto::null_hash), m_pruning_seed(0), m_rpc_port(0), m_rpc_credits_per_hash(0), m_anchor(false), m_score(0), - m_expect_response(0) {} + m_expect_response(0), m_expect_height(0), m_num_requested(0) {} enum state { @@ -55,6 +55,12 @@ namespace cryptonote state_normal }; + static constexpr int handshake_command() noexcept { return 1001; } + bool handshake_complete() const noexcept { return m_state != state_before_handshake; } + + //! \return Maximum number of bytes permissible for `command`. + static size_t get_max_bytes(int command) noexcept; + state m_state; std::vector> m_needed_objects; std::unordered_set m_requested_objects; @@ -70,6 +76,7 @@ namespace cryptonote int32_t m_score; int m_expect_response; uint64_t m_expect_height; + size_t m_num_requested; }; inline std::string get_protocol_state_string(cryptonote_connection_context::state s) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 9b1f9ef70..7d7fb84c6 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -128,7 +128,8 @@ #define CRYPTONOTE_MAX_FRAGMENTS 20 // ~20 * NOISE_BYTES max payload size for covert/noise send -#define COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT 1000 +#define COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT 1000 +#define COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT 20000 #define P2P_LOCAL_WHITE_PEERLIST_LIMIT 1000 #define P2P_LOCAL_GRAY_PEERLIST_LIMIT 5000 @@ -203,6 +204,8 @@ #define RPC_CREDITS_PER_HASH_SCALE ((float)(1<<24)) +#define DNS_BLOCKLIST_LIFETIME (86400 * 8) + // New constants are intended to go here namespace config { diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index e470b39bb..657beb7ac 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2780,7 +2780,7 @@ bool Blockchain::find_blockchain_supplement(const std::list& qbloc // find split point between ours and foreign blockchain (or start at // blockchain height ), and return up to max_count FULL // blocks by reference. -bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector, std::vector > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const +bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector, std::vector > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -2805,8 +2805,8 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons db_rtxn_guard rtxn_guard(m_db); total_height = get_current_blockchain_height(); - blocks.reserve(std::min(std::min(max_count, (size_t)10000), (size_t)(total_height - start_height))); - CHECK_AND_ASSERT_MES(m_db->get_blocks_from(start_height, 3, max_count, FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE, blocks, pruned, true, get_miner_tx_hash), + blocks.reserve(std::min(std::min(max_block_count, (size_t)10000), (size_t)(total_height - start_height))); + CHECK_AND_ASSERT_MES(m_db->get_blocks_from(start_height, 3, max_block_count, max_tx_count, FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE, blocks, pruned, true, get_miner_tx_hash), false, "Error getting blocks"); return true; @@ -2837,32 +2837,44 @@ void Blockchain::flush_invalid_blocks() m_invalid_blocks.clear(); } //------------------------------------------------------------------ -bool Blockchain::have_block(const crypto::hash& id) const +bool Blockchain::have_block_unlocked(const crypto::hash& id, int *where) const { + // WARNING: this function does not take m_blockchain_lock, and thus should only call read only + // m_db functions which do not depend on one another (ie, no getheight + gethash(height-1), as + // well as not accessing class members, even read only (ie, m_invalid_blocks). The caller must + // lock if it is otherwise needed. LOG_PRINT_L3("Blockchain::" << __func__); - CRITICAL_REGION_LOCAL(m_blockchain_lock); if(m_db->block_exists(id)) { LOG_PRINT_L2("block " << id << " found in main chain"); + if (where) *where = HAVE_BLOCK_MAIN_CHAIN; return true; } if(m_db->get_alt_block(id, NULL, NULL)) { LOG_PRINT_L2("block " << id << " found in alternative chains"); + if (where) *where = HAVE_BLOCK_ALT_CHAIN; return true; } if(m_invalid_blocks.count(id)) { LOG_PRINT_L2("block " << id << " found in m_invalid_blocks"); + if (where) *where = HAVE_BLOCK_INVALID; return true; } return false; } //------------------------------------------------------------------ +bool Blockchain::have_block(const crypto::hash& id, int *where) const +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + return have_block_unlocked(id, where); +} +//------------------------------------------------------------------ bool Blockchain::handle_block_to_main_chain(const block& bl, block_verification_context& bvc, bool notify/* = true*/) { LOG_PRINT_L3("Blockchain::" << __func__); @@ -4807,6 +4819,8 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector CHECK_AND_ASSERT_MES(weights.empty() || weights.size() == hashes.size(), 0, "Unexpected weights size"); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + // easy case: height >= hashes if (height >= m_blocks_hash_of_hashes.size() * HASH_OF_HASHES_STEP) return hashes.size(); @@ -5446,7 +5460,7 @@ void Blockchain::cancel() } #if defined(PER_BLOCK_CHECKPOINT) -static const char expected_block_hashes_hash[] = "bd1d32aed063dd71c1355dc950eb0fc22b10e41530029f54d40a0fd6f6b328f1"; +static const char expected_block_hashes_hash[] = "44393af098c4738ecbe664292bc345c0acb40573562cac8bb67cea75debe9f1b"; void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints) { if (get_checkpoints == nullptr || !m_fast_sync) diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index a8e251c71..5291f1338 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -377,10 +377,12 @@ namespace cryptonote * for a block with the given hash * * @param id the hash to search for + * @param where the type of block, if non NULL * * @return true if the block is known, else false */ - bool have_block(const crypto::hash& id) const; + bool have_block_unlocked(const crypto::hash& id, int *where = NULL) const; + bool have_block(const crypto::hash& id, int *where = NULL) const; /** * @brief gets the total number of transactions on the main chain @@ -465,11 +467,12 @@ namespace cryptonote * @param total_height return-by-reference our current blockchain height * @param start_height return-by-reference the height of the first block returned * @param pruned whether to return full or pruned tx blobs - * @param max_count the max number of blocks to get + * @param max_block_count the max number of blocks to get + * @param max_tx_count the max number of txes to get (it can get overshot by the last block's number of txes minus 1) * * @return true if a block found in common or req_start_block specified, else false */ - bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector, std::vector > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const; + bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector, std::vector > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const; /** * @brief retrieves a set of blocks and their transactions, and possibly other transactions diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index bbdaf7efb..db4219e1a 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1446,9 +1446,9 @@ namespace cryptonote return m_blockchain_storage.find_blockchain_supplement(qblock_ids, clip_pruned, resp); } //----------------------------------------------------------------------------------------------- - bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector, std::vector > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const + bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector, std::vector > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const { - return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, pruned, get_miner_tx_hash, max_count); + return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, pruned, get_miner_tx_hash, max_block_count, max_tx_count); } //----------------------------------------------------------------------------------------------- bool core::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const @@ -1665,9 +1665,14 @@ namespace cryptonote return m_mempool.get_transactions_count(include_sensitive_txes); } //----------------------------------------------------------------------------------------------- - bool core::have_block(const crypto::hash& id) const + bool core::have_block_unlocked(const crypto::hash& id, int *where) const { - return m_blockchain_storage.have_block(id); + return m_blockchain_storage.have_block_unlocked(id, where); + } + //----------------------------------------------------------------------------------------------- + bool core::have_block(const crypto::hash& id, int *where) const + { + return m_blockchain_storage.have_block(id, where); } //----------------------------------------------------------------------------------------------- bool core::parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, const blobdata& blob) const diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 37981478c..8891540a9 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -55,6 +55,8 @@ PUSH_WARNINGS DISABLE_VS_WARNINGS(4355) +enum { HAVE_BLOCK_MAIN_CHAIN, HAVE_BLOCK_ALT_CHAIN, HAVE_BLOCK_INVALID }; + namespace cryptonote { struct test_options { @@ -543,7 +545,8 @@ namespace cryptonote * * @note see Blockchain::have_block */ - bool have_block(const crypto::hash& id) const; + bool have_block_unlocked(const crypto::hash& id, int *where = NULL) const; + bool have_block(const crypto::hash& id, int *where = NULL) const; /** * @copydoc Blockchain::get_short_chain_history @@ -564,7 +567,7 @@ namespace cryptonote * * @note see Blockchain::find_blockchain_supplement(const uint64_t, const std::list&, std::vector > >&, uint64_t&, uint64_t&, size_t) const */ - bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector, std::vector > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const; + bool find_blockchain_supplement(const uint64_t req_start_block, const std::list& qblock_ids, std::vector, std::vector > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const; /** * @copydoc Blockchain::get_tx_outputs_gindexs diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index 8f867eee6..28530f3e7 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -150,7 +150,7 @@ namespace cryptonote bool update_sync_search(); int try_add_next_blocks(cryptonote_connection_context &context); void notify_new_stripe(cryptonote_connection_context &context, uint32_t stripe); - void skip_unneeded_hashes(cryptonote_connection_context& context, bool check_block_queue) const; + size_t skip_unneeded_hashes(cryptonote_connection_context& context, bool check_block_queue) const; bool request_txpool_complement(cryptonote_connection_context &context); void hit_score(cryptonote_connection_context &context, int32_t score); diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index b4e1fb408..9034ea5bf 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -137,7 +137,7 @@ namespace cryptonote CHECK_AND_ASSERT_MES_CC( context.m_callback_request_count > 0, false, "false callback fired, but context.m_callback_request_count=" << context.m_callback_request_count); --context.m_callback_request_count; - if(context.m_state == cryptonote_connection_context::state_synchronizing) + if(context.m_state == cryptonote_connection_context::state_synchronizing && context.m_last_request_time == boost::posix_time::not_a_date_time) { NOTIFY_REQUEST_CHAIN::request r = {}; context.m_needed_objects.clear(); @@ -409,6 +409,7 @@ namespace cryptonote ++context.m_callback_request_count; m_p2p->request_callback(context); MLOG_PEER_STATE("requesting callback"); + context.m_num_requested = 0; return true; } //------------------------------------------------------------------------------------------------------------------------ @@ -1990,7 +1991,7 @@ skip: } //------------------------------------------------------------------------------------------------------------------------ template - void t_cryptonote_protocol_handler::skip_unneeded_hashes(cryptonote_connection_context& context, bool check_block_queue) const + size_t t_cryptonote_protocol_handler::skip_unneeded_hashes(cryptonote_connection_context& context, bool check_block_queue) const { // take out blocks we already have size_t skip = 0; @@ -2007,6 +2008,7 @@ skip: MDEBUG(context << "skipping " << skip << "/" << context.m_needed_objects.size() << " blocks"); context.m_needed_objects = std::vector>(context.m_needed_objects.begin() + skip, context.m_needed_objects.end()); } + return skip; } //------------------------------------------------------------------------------------------------------------------------ template @@ -2060,7 +2062,17 @@ skip: const size_t block_queue_size_threshold = m_block_download_max_size ? m_block_download_max_size : BLOCK_QUEUE_SIZE_THRESHOLD; bool queue_proceed = nspans < BLOCK_QUEUE_NSPANS_THRESHOLD || size < block_queue_size_threshold; // get rid of blocks we already requested, or already have - skip_unneeded_hashes(context, true); + if (skip_unneeded_hashes(context, true) && context.m_needed_objects.empty() && context.m_num_requested == 0) + { + if (context.m_remote_blockchain_height > m_block_queue.get_next_needed_height(bc_height)) + { + MERROR(context << "Nothing we can request from this peer, and we did not request anything previously"); + return false; + } + MDEBUG(context << "Nothing to get from this peer, and it's not ahead of us, all done"); + context.m_state = cryptonote_connection_context::state_normal; + return true; + } uint64_t next_needed_height = m_block_queue.get_next_needed_height(bc_height); uint64_t next_block_height; if (context.m_needed_objects.empty()) @@ -2196,7 +2208,17 @@ skip: context.m_last_response_height = 0; goto skip; } - skip_unneeded_hashes(context, false); + if (skip_unneeded_hashes(context, false) && context.m_needed_objects.empty() && context.m_num_requested == 0) + { + if (context.m_remote_blockchain_height > m_block_queue.get_next_needed_height(m_core.get_current_blockchain_height())) + { + MERROR(context << "Nothing we can request from this peer, and we did not request anything previously"); + return false; + } + MDEBUG(context << "Nothing to get from this peer, and it's not ahead of us, all done"); + context.m_state = cryptonote_connection_context::state_normal; + return true; + } const uint64_t first_block_height = context.m_last_response_height - context.m_needed_objects.size() + 1; static const uint64_t bp_fork_height = m_core.get_earliest_ideal_height_for_version(HF_VERSION_SMALLER_BP); @@ -2293,35 +2315,22 @@ skip: << "/" << tools::get_pruning_stripe(span.first + span.second - 1, context.m_remote_blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES) << ", ours " << tools::get_pruning_stripe(m_core.get_blockchain_pruning_seed()) << ", peer stripe " << tools::get_pruning_stripe(context.m_pruning_seed)); + context.m_num_requested += req.blocks.size(); post_notify(req, context); MLOG_PEER_STATE("requesting objects"); return true; } - // if we're still around, we might be at a point where the peer is pruned, so we could either - // drop it to make space for other peers, or ask for a span further down the line - const uint32_t next_stripe = get_next_needed_pruning_stripe().first; - const uint32_t peer_stripe = tools::get_pruning_stripe(context.m_pruning_seed); - const uint32_t local_stripe = tools::get_pruning_stripe(m_core.get_blockchain_pruning_seed()); - if (!(m_sync_pruned_blocks && peer_stripe == local_stripe) && next_stripe && peer_stripe && next_stripe != peer_stripe) + // we can do nothing, so drop this peer to make room for others unless we think we've downloaded all we need + const uint64_t blockchain_height = m_core.get_current_blockchain_height(); + if (std::max(blockchain_height, m_block_queue.get_next_needed_height(blockchain_height)) >= m_core.get_target_blockchain_height()) { - // at this point, we have to either close the connection, or start getting blocks past the - // current point, or become dormant - MDEBUG(context << "this peer is pruned at seed " << epee::string_tools::to_string_hex(context.m_pruning_seed) << - ", next stripe needed is " << next_stripe); - if (!context.m_is_income) - { - if (should_drop_connection(context, next_stripe)) - { - m_p2p->add_used_stripe_peer(context); - return false; // drop outgoing connections - } - } - // we'll get back stuck waiting for the go ahead context.m_state = cryptonote_connection_context::state_normal; MLOG_PEER_STATE("Nothing to do for now, switching to normal state"); return true; } + MLOG_PEER_STATE("We can download nothing from this peer, dropping"); + return false; } skip: @@ -2561,17 +2570,6 @@ skip: return 1; } - std::unordered_set hashes; - for (const auto &h: arg.m_block_ids) - { - if (!hashes.insert(h).second) - { - LOG_ERROR_CCONTEXT("sent duplicate block, dropping connection"); - drop_connection(context, true, false); - return 1; - } - } - uint64_t n_use_blocks = m_core.prevalidate_block_hashes(arg.start_height, arg.m_block_ids, arg.m_block_weights); if (n_use_blocks == 0 || n_use_blocks + HASH_OF_HASHES_STEP <= arg.m_block_ids.size()) { @@ -2583,18 +2581,74 @@ skip: context.m_needed_objects.clear(); uint64_t added = 0; std::unordered_set blocks_found; + bool first = true; + bool expect_unknown = false; for (size_t i = 0; i < arg.m_block_ids.size(); ++i) { if (!blocks_found.insert(arg.m_block_ids[i]).second) { LOG_ERROR_CCONTEXT("Duplicate blocks in chain entry response, dropping connection"); - drop_connection(context, true, false); + drop_connection_with_score(context, 5, false); return 1; } + int where; + const bool have_block = m_core.have_block_unlocked(arg.m_block_ids[i], &where); + if (first) + { + if (!have_block && !m_block_queue.requested(arg.m_block_ids[i]) && !m_block_queue.have(arg.m_block_ids[i])) + { + LOG_ERROR_CCONTEXT("First block hash is unknown, dropping connection"); + drop_connection_with_score(context, 5, false); + return 1; + } + if (!have_block) + expect_unknown = true; + } + if (!first) + { + // after the first, blocks may be known or unknown, but if they are known, + // they should be at the same height if on the main chain + if (have_block) + { + switch (where) + { + default: + case HAVE_BLOCK_INVALID: + LOG_ERROR_CCONTEXT("Block is invalid or known without known type, dropping connection"); + drop_connection(context, true, false); + return 1; + case HAVE_BLOCK_MAIN_CHAIN: + if (expect_unknown) + { + LOG_ERROR_CCONTEXT("Block is on the main chain, but we did not expect a known block, dropping connection"); + drop_connection_with_score(context, 5, false); + return 1; + } + if (m_core.get_block_id_by_height(arg.start_height + i) != arg.m_block_ids[i]) + { + LOG_ERROR_CCONTEXT("Block is on the main chain, but not at the expected height, dropping connection"); + drop_connection_with_score(context, 5, false); + return 1; + } + break; + case HAVE_BLOCK_ALT_CHAIN: + if (expect_unknown) + { + LOG_ERROR_CCONTEXT("Block is on the main chain, but we did not expect a known block, dropping connection"); + drop_connection_with_score(context, 5, false); + return 1; + } + break; + } + } + else + expect_unknown = true; + } const uint64_t block_weight = arg.m_block_weights.empty() ? 0 : arg.m_block_weights[i]; context.m_needed_objects.push_back(std::make_pair(arg.m_block_ids[i], block_weight)); if (++added == n_use_blocks) break; + first = false; } context.m_last_response_height -= arg.m_block_ids.size() - n_use_blocks; @@ -2608,6 +2662,7 @@ skip: if (arg.total_height > m_core.get_target_blockchain_height()) m_core.set_target_blockchain_height(arg.total_height); + context.m_num_requested = 0; return 1; } //------------------------------------------------------------------------------------------------------------------------ @@ -2624,6 +2679,7 @@ skip: std::vector> fullConnections, fluffyConnections; m_p2p->for_each_connection([this, &exclude_context, &fullConnections, &fluffyConnections](connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags) { + // peer_id also filters out connections before handshake if (peer_id && exclude_context.m_connection_id != context.m_connection_id && context.m_remote_address.get_zone() == epee::net_utils::zone::public_) { if(m_core.fluffy_blocks_enabled() && (support_flags & P2P_SUPPORT_FLAG_FLUFFY_BLOCKS)) @@ -2784,12 +2840,15 @@ skip: epee::string_tools::to_string_hex(context.m_pruning_seed) << "), score " << score << ", flush_all_spans " << flush_all_spans); - if (score > 0) - m_p2p->add_host_fail(context.m_remote_address, score); - m_block_queue.flush_spans(context.m_connection_id, flush_all_spans); + // copy since dropping the connection will invalidate the context, and thus the address + const auto remote_address = context.m_remote_address; + m_p2p->drop_connection(context); + + if (score > 0) + m_p2p->add_host_fail(remote_address, score); } //------------------------------------------------------------------------------------------------------------------------ template diff --git a/src/cryptonote_protocol/levin_notify.cpp b/src/cryptonote_protocol/levin_notify.cpp index 318bcf4e1..c23a4d2fe 100644 --- a/src/cryptonote_protocol/levin_notify.cpp +++ b/src/cryptonote_protocol/levin_notify.cpp @@ -287,7 +287,6 @@ namespace levin connection_count(0), flush_callbacks(0), nzone(zone), - is_public(is_public), pad_txs(pad_txs), fluffing(false) { @@ -305,7 +304,6 @@ namespace levin std::atomic connection_count; //!< Only update in strand, can be read at any time std::uint32_t flush_callbacks; //!< Number of active fluff flush callbacks queued const epee::net_utils::zone nzone; //!< Zone is public ipv4/ipv6 connections, or i2p or tor - const bool is_public; //!< Zone is public ipv4/ipv6 connections const bool pad_txs; //!< Pad txs to the next boundary for privacy bool fluffing; //!< Zone is in Dandelion++ fluff epoch }; @@ -445,7 +443,7 @@ namespace levin zone->p2p->foreach_connection([txs, now, &zone, &source, &in_duration, &out_duration, &next_flush] (detail::p2p_context& context) { // When i2p/tor, only fluff to outbound connections - if (source != context.m_connection_id && (zone->nzone == epee::net_utils::zone::public_ || !context.m_is_income)) + if (context.handshake_complete() && source != context.m_connection_id && (zone->nzone == epee::net_utils::zone::public_ || !context.m_is_income)) { if (context.fluff_txs.empty()) context.flush_time = now + (context.m_is_income ? in_duration() : out_duration()); diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 89478e831..92af920cc 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -28,6 +28,7 @@ #include "common/dns_utils.h" #include "common/command_line.h" +#include "net/parse.h" #include "daemon/command_parser_executor.h" #undef MONERO_DEFAULT_LOG_CATEGORY @@ -657,7 +658,6 @@ bool t_command_parser_executor::ban(const std::vector& args) std::cout << "Invalid syntax: Expects one or two parameters. For more details, use the help command." << std::endl; return true; } - std::string ip = args[0]; time_t seconds = P2P_IP_BLOCKTIME; if (args.size() > 1) { @@ -676,7 +676,51 @@ bool t_command_parser_executor::ban(const std::vector& args) return true; } } - return m_executor.ban(ip, seconds); + if (boost::starts_with(args[0], "@")) + { + const std::string ban_list = args[0].substr(1); + + try + { + const boost::filesystem::path ban_list_path(ban_list); + boost::system::error_code ec; + if (!boost::filesystem::exists(ban_list_path, ec)) + { + std::cout << "Can't find ban list file " + ban_list + " - " + ec.message() << std::endl; + return true; + } + + bool ret = true; + std::ifstream ifs(ban_list_path.string()); + for (std::string line; std::getline(ifs, line); ) + { + auto subnet = net::get_ipv4_subnet_address(line); + if (subnet) + { + ret &= m_executor.ban(subnet->str(), seconds); + continue; + } + const expect parsed_addr = net::get_network_address(line, 0); + if (parsed_addr) + { + ret &= m_executor.ban(parsed_addr->host_str(), seconds); + continue; + } + std::cout << "Invalid IP address or IPv4 subnet: " << line << std::endl; + } + return ret; + } + catch (const std::exception &e) + { + std::cout << "Error loading ban list: " << e.what() << std::endl; + return false; + } + } + else + { + const std::string ip = args[0]; + return m_executor.ban(ip, seconds); + } } bool t_command_parser_executor::unban(const std::vector& args) diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 8d7d1b885..938a7d8af 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -234,8 +234,8 @@ t_command_server::t_command_server( m_command_lookup.set_handler( "ban" , std::bind(&t_command_parser_executor::ban, &m_parser, p::_1) - , "ban []" - , "Ban a given for a given amount of ." + , "ban [|@] []" + , "Ban a given or list of IPs from a file for a given amount of ." ); m_command_lookup.set_handler( "unban" diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index ee88e6ce8..b99500a88 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -191,6 +191,9 @@ bool t_rpc_command_executor::print_peer_list(bool white, bool gray, size_t limit cryptonote::COMMAND_RPC_GET_PEER_LIST::response res; std::string failure_message = "Couldn't retrieve peer list"; + + req.include_blocked = true; + if (m_is_rpc) { if (!m_rpc_client->rpc_request(req, res, "/get_peer_list", failure_message.c_str())) @@ -237,6 +240,7 @@ bool t_rpc_command_executor::print_peer_list_stats() { std::string failure_message = "Couldn't retrieve peer list"; req.public_only = false; + req.include_blocked = true; if (m_is_rpc) { diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp index aee1fa593..8dd551d1e 100644 --- a/src/p2p/net_node.cpp +++ b/src/p2p/net_node.cpp @@ -149,6 +149,7 @@ namespace nodetool const command_line::arg_descriptor arg_ban_list = {"ban-list", "Specify ban list file, one IP address per line"}; const command_line::arg_descriptor arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true}; const command_line::arg_descriptor arg_no_sync = {"no-sync", "Don't synchronize the blockchain with other peers", false}; + const command_line::arg_descriptor arg_enable_dns_blocklist = {"enable-dns-blocklist", "Apply realtime blocklist from DNS", false}; const command_line::arg_descriptor arg_no_igd = {"no-igd", "Disable UPnP port mapping"}; const command_line::arg_descriptor arg_igd = {"igd", "UPnP port mapping (disabled, enabled, delayed)", "delayed"}; diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index c3595787a..d0b415feb 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -139,6 +139,7 @@ namespace nodetool typedef COMMAND_HANDSHAKE_T COMMAND_HANDSHAKE; typedef COMMAND_TIMED_SYNC_T COMMAND_TIMED_SYNC; + static_assert(p2p_connection_context::handshake_command() == COMMAND_HANDSHAKE::ID, "invalid handshake command id"); typedef epee::net_utils::boosted_tcp_server> net_server; @@ -278,7 +279,7 @@ namespace nodetool uint32_t get_max_out_public_peers() const; void change_max_in_public_peers(size_t count); uint32_t get_max_in_public_peers() const; - virtual bool block_host(epee::net_utils::network_address address, time_t seconds = P2P_IP_BLOCKTIME); + virtual bool block_host(epee::net_utils::network_address address, time_t seconds = P2P_IP_BLOCKTIME, bool add_only = false); virtual bool unblock_host(const epee::net_utils::network_address &address); virtual bool block_subnet(const epee::net_utils::ipv4_network_subnet &subnet, time_t seconds = P2P_IP_BLOCKTIME); virtual bool unblock_subnet(const epee::net_utils::ipv4_network_subnet &subnet); @@ -357,6 +358,7 @@ namespace nodetool bool peer_sync_idle_maker(); bool do_handshake_with_peer(peerid_type& pi, p2p_connection_context& context, bool just_take_peerlist = false); bool do_peer_timed_sync(const epee::net_utils::connection_context_base& context, peerid_type peer_id); + bool update_dns_blocklist(); bool make_new_connection_from_anchor_peerlist(const std::vector& anchor_peerlist); bool make_new_connection_from_peerlist(network_zone& zone, bool use_white_list); @@ -461,6 +463,7 @@ namespace nodetool epee::math_helper::once_a_time_seconds<60*30, false> m_peerlist_store_interval; epee::math_helper::once_a_time_seconds<60> m_gray_peerlist_housekeeping_interval; epee::math_helper::once_a_time_seconds<3600, false> m_incoming_connections_interval; + epee::math_helper::once_a_time_seconds<7000> m_dns_blocklist_interval; std::list m_priority_peers; std::vector m_exclusive_peers; @@ -502,6 +505,8 @@ namespace nodetool cryptonote::network_type m_nettype; epee::net_utils::ssl_support_t m_ssl_support; + + bool m_enable_dns_blocklist; }; const int64_t default_limit_up = P2P_DEFAULT_LIMIT_RATE_UP; // kB/s @@ -523,6 +528,7 @@ namespace nodetool extern const command_line::arg_descriptor arg_ban_list; extern const command_line::arg_descriptor arg_p2p_hide_my_port; extern const command_line::arg_descriptor arg_no_sync; + extern const command_line::arg_descriptor arg_enable_dns_blocklist; extern const command_line::arg_descriptor arg_no_igd; extern const command_line::arg_descriptor arg_igd; diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 07fc64952..5efcb0867 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -120,6 +121,7 @@ namespace nodetool command_line::add_arg(desc, arg_ban_list); command_line::add_arg(desc, arg_p2p_hide_my_port); command_line::add_arg(desc, arg_no_sync); + command_line::add_arg(desc, arg_enable_dns_blocklist); command_line::add_arg(desc, arg_no_igd); command_line::add_arg(desc, arg_igd); command_line::add_arg(desc, arg_out_peers); @@ -226,7 +228,7 @@ namespace nodetool } //----------------------------------------------------------------------------------- template - bool node_server::block_host(epee::net_utils::network_address addr, time_t seconds) + bool node_server::block_host(epee::net_utils::network_address addr, time_t seconds, bool add_only) { if(!addr.is_blockable()) return false; @@ -240,7 +242,11 @@ namespace nodetool else limit = now + seconds; const std::string host_str = addr.host_str(); - m_blocked_hosts[host_str] = limit; + auto it = m_blocked_hosts.find(host_str); + if (it == m_blocked_hosts.end()) + m_blocked_hosts[host_str] = limit; + else if (it->second < limit || !add_only) + it->second = limit; // drop any connection to that address. This should only have to look into // the zone related to the connection, but really make sure everything is @@ -260,6 +266,8 @@ namespace nodetool peerlist_entry pe{}; pe.adr = addr; zone.second.m_peerlist.remove_from_peer_white(pe); + zone.second.m_peerlist.remove_from_peer_gray(pe); + zone.second.m_peerlist.remove_from_peer_anchor(addr); for (const auto &c: conns) zone.second.m_net_server.get_config_object().close(c); @@ -481,13 +489,19 @@ namespace nodetool std::istringstream iss(banned_ips); for (std::string line; std::getline(iss, line); ) { + auto subnet = net::get_ipv4_subnet_address(line); + if (subnet) + { + block_subnet(*subnet, std::numeric_limits::max()); + continue; + } const expect parsed_addr = net::get_network_address(line, 0); - if (!parsed_addr) + if (parsed_addr) { - MERROR("Invalid IP address: " << line << " - " << parsed_addr.error()); + block_host(*parsed_addr, std::numeric_limits::max()); continue; } - block_host(*parsed_addr, std::numeric_limits::max()); + MERROR("Invalid IP address or IPv4 subnet: " << line); } } @@ -497,6 +511,8 @@ namespace nodetool if (command_line::has_arg(vm, arg_no_sync)) m_payload_handler.set_no_sync(true); + m_enable_dns_blocklist = command_line::get_arg(vm, arg_enable_dns_blocklist); + if ( !set_max_out_peers(public_zone, command_line::get_arg(vm, arg_out_peers) ) ) return false; else @@ -1165,8 +1181,9 @@ namespace nodetool if(!handle_remote_peerlist(rsp.local_peerlist_new, context)) { LOG_WARNING_CC(context, "COMMAND_TIMED_SYNC: failed to handle_remote_peerlist(...), closing connection."); + const auto remote_address = context.m_remote_address; m_network_zones.at(context.m_remote_address.get_zone()).m_net_server.get_config_object().close(context.m_connection_id ); - add_host_fail(context.m_remote_address); + add_host_fail(remote_address); } if(!context.m_is_income) m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_address, context.m_pruning_seed, context.m_rpc_port, context.m_rpc_credits_per_hash); @@ -1333,7 +1350,7 @@ namespace nodetool if(just_take_peerlist) { zone.m_net_server.get_config_object().close(con->m_connection_id); - LOG_DEBUG_CC(*con, "CONNECTION HANDSHAKED OK AND CLOSED."); + MDEBUG(na.str() << "CONNECTION HANDSHAKED OK AND CLOSED."); return true; } @@ -1395,7 +1412,7 @@ namespace nodetool zone.m_net_server.get_config_object().close(con->m_connection_id); - LOG_DEBUG_CC(*con, "CONNECTION HANDSHAKED OK AND CLOSED."); + MDEBUG(na.str() << "CONNECTION HANDSHAKED OK AND CLOSED."); return true; } @@ -1927,6 +1944,52 @@ namespace nodetool m_gray_peerlist_housekeeping_interval.do_call(boost::bind(&node_server::gray_peerlist_housekeeping, this)); m_peerlist_store_interval.do_call(boost::bind(&node_server::store_config, this)); m_incoming_connections_interval.do_call(boost::bind(&node_server::check_incoming_connections, this)); + m_dns_blocklist_interval.do_call(boost::bind(&node_server::update_dns_blocklist, this)); + return true; + } + //----------------------------------------------------------------------------------- + template + bool node_server::update_dns_blocklist() + { + if (!m_enable_dns_blocklist) + return true; + if (m_nettype != cryptonote::MAINNET) + return true; + + static const std::vector dns_urls = { + "blocklist.moneropulse.se" + , "blocklist.moneropulse.org" + , "blocklist.moneropulse.net" + , "blocklist.moneropulse.no" + , "blocklist.moneropulse.fr" + , "blocklist.moneropulse.de" + , "blocklist.moneropulse.ch" + }; + + std::vector records; + if (!tools::dns_utils::load_txt_records_from_dns(records, dns_urls)) + return true; + + unsigned good = 0, bad = 0; + for (const auto& record : records) + { + std::vector ips; + boost::split(ips, record, boost::is_any_of(";")); + for (const auto &ip: ips) + { + const expect parsed_addr = net::get_network_address(ip, 0); + if (!parsed_addr) + { + MWARNING("Invalid IP address from DNS blocklist: " << ip << " - " << parsed_addr.error()); + ++bad; + continue; + } + block_host(*parsed_addr, DNS_BLOCKLIST_LIFETIME, true); + ++good; + } + } + if (good > 0) + MINFO(good << " addresses added to the blocklist"); return true; } //----------------------------------------------------------------------------------- @@ -2049,7 +2112,10 @@ namespace nodetool LOG_DEBUG_CC(context, "REMOTE PEERLIST: remote peerlist size=" << peerlist_.size()); LOG_TRACE_CC(context, "REMOTE PEERLIST: " << ENDL << print_peerlist_to_string(peerlist_)); - return m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.merge_peerlist(peerlist_, [this](const peerlist_entry &pe) { return !is_addr_recently_failed(pe.adr); }); + CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); + return m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.merge_peerlist(peerlist_, [this](const peerlist_entry &pe) { + return !is_addr_recently_failed(pe.adr) && is_remote_host_allowed(pe.adr); + }); } //----------------------------------------------------------------------------------- template @@ -2369,12 +2435,14 @@ namespace nodetool template int node_server::handle_handshake(int command, typename COMMAND_HANDSHAKE::request& arg, typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context) { + // copy since dropping the connection will invalidate the context, and thus the address + const auto remote_address = context.m_remote_address; + if(arg.node_data.network_id != m_network_id) { - LOG_INFO_CC(context, "WRONG NETWORK AGENT CONNECTED! id=" << arg.node_data.network_id); drop_connection(context); - add_host_fail(context.m_remote_address); + add_host_fail(remote_address); return 1; } @@ -2382,7 +2450,7 @@ namespace nodetool { LOG_WARNING_CC(context, "COMMAND_HANDSHAKE came not from incoming connection"); drop_connection(context); - add_host_fail(context.m_remote_address); + add_host_fail(remote_address); return 1; } @@ -2794,8 +2862,8 @@ namespace nodetool const uint32_t index = stripe - 1; CRITICAL_REGION_LOCAL(m_used_stripe_peers_mutex); MINFO("adding stripe " << stripe << " peer: " << context.m_remote_address.str()); - std::remove_if(m_used_stripe_peers[index].begin(), m_used_stripe_peers[index].end(), - [&context](const epee::net_utils::network_address &na){ return context.m_remote_address == na; }); + m_used_stripe_peers[index].erase(std::remove_if(m_used_stripe_peers[index].begin(), m_used_stripe_peers[index].end(), + [&context](const epee::net_utils::network_address &na){ return context.m_remote_address == na; }), m_used_stripe_peers[index].end()); m_used_stripe_peers[index].push_back(context.m_remote_address); } @@ -2808,8 +2876,8 @@ namespace nodetool const uint32_t index = stripe - 1; CRITICAL_REGION_LOCAL(m_used_stripe_peers_mutex); MINFO("removing stripe " << stripe << " peer: " << context.m_remote_address.str()); - std::remove_if(m_used_stripe_peers[index].begin(), m_used_stripe_peers[index].end(), - [&context](const epee::net_utils::network_address &na){ return context.m_remote_address == na; }); + m_used_stripe_peers[index].erase(std::remove_if(m_used_stripe_peers[index].begin(), m_used_stripe_peers[index].end(), + [&context](const epee::net_utils::network_address &na){ return context.m_remote_address == na; }), m_used_stripe_peers[index].end()); } template diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index f1490a0db..0da758ad4 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -58,7 +58,7 @@ namespace nodetool virtual uint64_t get_public_connections_count()=0; virtual void for_each_connection(std::function f)=0; virtual bool for_connection(const boost::uuids::uuid&, std::function f)=0; - virtual bool block_host(epee::net_utils::network_address address, time_t seconds = 0)=0; + virtual bool block_host(epee::net_utils::network_address address, time_t seconds = 0, bool add_only = false)=0; virtual bool unblock_host(const epee::net_utils::network_address &address)=0; virtual std::map get_blocked_hosts()=0; virtual std::map get_blocked_subnets()=0; @@ -108,7 +108,7 @@ namespace nodetool { return false; } - virtual bool block_host(epee::net_utils::network_address address, time_t seconds) + virtual bool block_host(epee::net_utils::network_address address, time_t seconds, bool add_only) { return true; } diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 359dfa879..a6e12c9b3 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -826,7 +826,7 @@ bool bulletproof_VERIFY(const std::vector &proofs) proof_data.reserve(proofs.size()); size_t inv_offset = 0; std::vector to_invert; - to_invert.reserve(11 * sizeof(proofs)); + to_invert.reserve(11 * proofs.size()); size_t max_logM = 0; for (const Bulletproof *p: proofs) { diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 1a4fd86f0..9faab0460 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -190,6 +190,7 @@ namespace cryptonote request.gray = true; request.white = true; + request.include_blocked = false; if (!on_get_public_nodes(request, response) || response.status != CORE_RPC_STATUS_OK) { return {}; @@ -500,6 +501,7 @@ namespace cryptonote res.version = restricted ? "" : MONERO_VERSION_FULL; res.synchronized = check_core_ready(); res.busy_syncing = m_p2p.get_payload_object().is_busy_syncing(); + res.synchronized = check_core_ready(); res.status = CORE_RPC_STATUS_OK; return true; @@ -564,12 +566,12 @@ namespace cryptonote } } - size_t max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT; + size_t max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT; if (m_rpc_payment) { max_blocks = res.credits / COST_PER_BLOCK; - if (max_blocks > COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT) - max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT; + if (max_blocks > COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT) + max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT; if (max_blocks == 0) { res.status = CORE_RPC_STATUS_PAYMENT_REQUIRED; @@ -578,7 +580,7 @@ namespace cryptonote } std::vector, std::vector > > > bs; - 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, max_blocks)) + 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, max_blocks, COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT)) { res.status = "Failed"; add_host_fail(ctx); @@ -1383,6 +1385,8 @@ namespace cryptonote for (auto & entry : white_list) { + if (!req.include_blocked && m_p2p.is_host_blocked(entry.adr, NULL)) + continue; 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().ip(), entry.adr.as().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port, entry.rpc_credits_per_hash); @@ -1395,6 +1399,8 @@ namespace cryptonote for (auto & entry : gray_list) { + if (!req.include_blocked && m_p2p.is_host_blocked(entry.adr, NULL)) + continue; 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().ip(), entry.adr.as().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port, entry.rpc_credits_per_hash); @@ -1413,8 +1419,10 @@ namespace cryptonote { RPC_TRACKER(get_public_nodes); + COMMAND_RPC_GET_PEER_LIST::request peer_list_req; 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); + peer_list_req.include_blocked = req.include_blocked; + const bool success = on_get_peer_list(peer_list_req, peer_list_res, ctx); res.status = peer_list_res.status; if (!success) { diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index f2d8467f0..9f7a6bf9e 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -88,7 +88,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 3 -#define CORE_RPC_VERSION_MINOR 4 +#define CORE_RPC_VERSION_MINOR 5 #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) @@ -1197,10 +1197,12 @@ namespace cryptonote struct request_t: public rpc_request_base { bool public_only; + bool include_blocked; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_PARENT(rpc_request_base) KV_SERIALIZE_OPT(public_only, true) + KV_SERIALIZE_OPT(include_blocked, false) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init request; @@ -1246,11 +1248,13 @@ namespace cryptonote { bool gray; bool white; + bool include_blocked; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_PARENT(rpc_request_base) KV_SERIALIZE_OPT(gray, false) KV_SERIALIZE_OPT(white, true) + KV_SERIALIZE_OPT(include_blocked, false) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init request; diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index e256322cb..cdfc363a9 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -128,7 +128,7 @@ namespace rpc { std::vector, std::vector > > > blocks; - if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.start_height, req.prune, true, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) + if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.start_height, req.prune, true, COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT, COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT)) { res.status = Message::STATUS_FAILED; res.error_details = "core::find_blockchain_supplement() returned false"; diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index ec51907f3..96389b109 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -143,6 +143,7 @@ typedef cryptonote::simple_wallet sw; #define CREDITS_TARGET 50000 #define MAX_PAYMENT_DIFF 10000 #define MIN_PAYMENT_RATE 0.01f // per hash +#define MAX_MNEW_ADDRESSES 1000 enum TransferType { Transfer, @@ -181,7 +182,7 @@ namespace const command_line::arg_descriptor< std::vector > arg_command = {"command", ""}; const char* USAGE_START_MINING("start_mining [] [bg_mining] [ignore_battery]"); - const char* USAGE_SET_DAEMON("set_daemon [:] [trusted|untrusted]"); + const char* USAGE_SET_DAEMON("set_daemon [:] [trusted|untrusted|this-is-probably-a-spy-node]"); const char* USAGE_SHOW_BALANCE("balance [detail]"); const char* USAGE_INCOMING_TRANSFERS("incoming_transfers [available|unavailable] [verbose] [uses] [index=[,[,...]]]"); const char* USAGE_PAYMENTS("payments [ ... ]"); @@ -203,7 +204,7 @@ namespace " account tag [ ...]\n" " account untag [ ...]\n" " account tag_description "); - const char* USAGE_ADDRESS("address [ new