diff --git a/CMakeLists.txt b/CMakeLists.txt index 668636b7f..2fa3decf5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1182,7 +1182,6 @@ endif() if(STATIC) set(sodium_USE_STATIC_LIBS ON) endif() -find_package(Sodium REQUIRED) find_path(ZMQ_INCLUDE_PATH zmq.h) find_library(ZMQ_LIB zmq) @@ -1190,6 +1189,7 @@ find_library(PGM_LIBRARY pgm) find_library(NORM_LIBRARY norm) find_library(GSSAPI_LIBRARY gssapi_krb5) find_library(PROTOLIB_LIBRARY protolib) +find_library(SODIUM_LIBRARY sodium) find_library(BSD_LIBRARY bsd) find_library(MD_LIBRARY md) find_library(PROTOKIT_LIBRARY protokit) @@ -1212,8 +1212,16 @@ endif() if(PROTOLIB_LIBRARY) set(ZMQ_LIB "${ZMQ_LIB};${PROTOLIB_LIBRARY}") endif() -if(Sodium_FOUND) - set(ZMQ_LIB "${ZMQ_LIB};${sodium_LIBRARIES}") +if(SODIUM_LIBRARY) + message(STATUS "ZMQ_LIB: ${ZMQ_LIB};${SODIUM_LIBRARY}") + set(ZMQ_LIB "${ZMQ_LIB};${SODIUM_LIBRARY}") + find_path(SODIUM_INCLUDE_PATH sodium/crypto_verify_32.h) + if (SODIUM_INCLUDE_PATH) + message(STATUS "SODIUM_INCLUDE_PATH: ${SODIUM_INCLUDE_PATH}") + include_directories(${SODIUM_INCLUDE_PATH}) + else() + message(FATAL_ERROR "Could not find required sodium/crypto_verify_32.h") + endif() endif() if(BSD_LIBRARY) set(ZMQ_LIB "${ZMQ_LIB};${BSD_LIBRARY}") diff --git a/contrib/depends/packages/boost.mk b/contrib/depends/packages/boost.mk index d81bca973..402fc84a5 100644 --- a/contrib/depends/packages/boost.mk +++ b/contrib/depends/packages/boost.mk @@ -1,8 +1,9 @@ package=boost -$(package)_version=1_64_0 -$(package)_download_path=https://downloads.sourceforge.net/project/boost/boost/1.64.0/ -$(package)_file_name=$(package)_$($(package)_version).tar.bz2 -$(package)_sha256_hash=7bcc5caace97baa948931d712ea5f37038dbb1c5d89b43ad4def4ed7cb683332 +$(package)_version=1.66.0 +$(package)_download_path=https://archives.boost.io/release/$($(package)_version)/source/ +$(package)_file_name=$(package)_$(subst .,_,$($(package)_version)).tar.gz +$(package)_sha256_hash=bd0df411efd9a585e5a2212275f8762079fed8842264954675a4fddc46cfcf60 +$(package)_dependencies=libiconv $(package)_patches=fix_aroptions.patch fix_arm_arch.patch define $(package)_set_vars @@ -21,11 +22,10 @@ $(package)_toolset_$(host_os)=gcc $(package)_archiver_$(host_os)=$($(package)_ar) $(package)_toolset_darwin=darwin $(package)_archiver_darwin=$($(package)_libtool) -$(package)_config_libraries_$(host_os)="chrono,filesystem,program_options,system,thread,test,date_time,regex,serialization" -$(package)_config_libraries_mingw32="chrono,filesystem,program_options,system,thread,test,date_time,regex,serialization,locale" +$(package)_config_libraries=chrono,filesystem,program_options,system,thread,test,date_time,regex,serialization,locale $(package)_cxxflags=-std=c++11 $(package)_cxxflags_linux=-fPIC -$(package)_cxxflags_freebsd=-fPIC +$(package)_cxxflags_freebsd=-fPIC -DBOOST_ASIO_HAS_STD_STRING_VIEW=1 endef define $(package)_preprocess_cmds @@ -35,7 +35,7 @@ define $(package)_preprocess_cmds endef define $(package)_config_cmds - ./bootstrap.sh --without-icu --with-libraries=$(boost_config_libraries_$(host_os)) + ./bootstrap.sh --without-icu --with-libraries=$(boost_config_libraries) endef define $(package)_build_cmds diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index 14561964f..d2d1eca85 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -1,28 +1,38 @@ -packages:=boost openssl zeromq expat unbound sodium +packages:=boost openssl zeromq libiconv expat unbound + +# ccache is useless in gitian builds +ifneq ($(GITIAN),1) +native_packages := native_ccache +endif hardware_packages := hidapi protobuf libusb hardware_native_packages := native_protobuf -android_native_packages = android_ndk $(hardware_native_packages) -android_packages = ncurses readline protobuf +android_native_packages = android_ndk +android_packages = ncurses readline sodium darwin_native_packages = $(hardware_native_packages) -darwin_packages = ncurses readline $(hardware_packages) +darwin_packages = ncurses readline sodium $(hardware_packages) # not really native... -freebsd_native_packages = freebsd_base $(hardware_native_packages) -freebsd_packages = ncurses readline protobuf libusb +freebsd_native_packages = freebsd_base +freebsd_packages = ncurses readline sodium -linux_packages = eudev ncurses readline $(hardware_packages) +linux_packages = eudev ncurses readline sodium $(hardware_packages) linux_native_packages = $(hardware_native_packages) ifeq ($(build_tests),ON) packages += gtest endif -mingw32_packages = $(hardware_packages) +ifneq ($(host_arch),riscv64) +linux_packages += unwind +endif + +mingw32_packages = icu4c sodium $(hardware_packages) mingw32_native_packages = $(hardware_native_packages) ifneq ($(build_os),darwin) darwin_native_packages += darwin_sdk native_clang native_cctools native_libtapi endif + diff --git a/contrib/epee/include/byte_stream.h b/contrib/epee/include/byte_stream.h index e7993133a..ec9e5cbca 100644 --- a/contrib/epee/include/byte_stream.h +++ b/contrib/epee/include/byte_stream.h @@ -74,6 +74,7 @@ namespace epee public: using char_type = std::uint8_t; using Ch = char_type; + using value_type = char_type; //! Increase internal buffer by at least `byte_stream_increase` bytes. byte_stream() noexcept @@ -86,6 +87,7 @@ namespace epee ~byte_stream() noexcept = default; byte_stream& operator=(byte_stream&& rhs) noexcept; + std::uint8_t* data() noexcept { return buffer_.get(); } const std::uint8_t* data() const noexcept { return buffer_.get(); } std::uint8_t* tellp() const noexcept { return next_write_; } std::size_t available() const noexcept { return end_ - next_write_; } diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h index bc0da66e2..1e45e6809 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.h +++ b/contrib/epee/include/net/abstract_tcp_server2.h @@ -47,6 +47,7 @@ #include #include +#include #include #include #include @@ -64,6 +65,7 @@ #define MONERO_DEFAULT_LOG_CATEGORY "net" #define ABSTRACT_SERVER_SEND_QUE_MAX_COUNT 1000 +#define ABSTRACT_SERVER_SEND_QUE_MAX_BYTES_DEFAULT 100 * 1024 * 1024 namespace epee { @@ -76,6 +78,13 @@ namespace net_utils protected: virtual ~i_connection_filter(){} }; + + struct i_connection_limit + { + virtual bool is_host_limit(const epee::net_utils::network_address &address)=0; + protected: + virtual ~i_connection_limit(){} + }; /************************************************************************/ @@ -100,8 +109,8 @@ namespace net_utils using ec_t = boost::system::error_code; using handshake_t = boost::asio::ssl::stream_base::handshake_type; - using io_context_t = boost::asio::io_service; - using strand_t = boost::asio::io_service::strand; + using io_context_t = boost::asio::io_context; + using strand_t = io_context_t::strand; using socket_t = boost::asio::ip::tcp::socket; using network_throttle_t = epee::net_utils::network_throttle; @@ -162,6 +171,7 @@ namespace net_utils } read; struct { std::deque queue; + std::size_t total_bytes; bool wait_consume; } write; }; @@ -260,20 +270,28 @@ namespace net_utils struct shared_state : connection_basic_shared_state, t_protocol_handler::config_type { shared_state() - : connection_basic_shared_state(), t_protocol_handler::config_type(), pfilter(nullptr), stop_signal_sent(false) + : connection_basic_shared_state(), + t_protocol_handler::config_type(), + pfilter(nullptr), + plimit(nullptr), + response_soft_limit(ABSTRACT_SERVER_SEND_QUE_MAX_BYTES_DEFAULT), + stop_signal_sent(false) {} i_connection_filter* pfilter; + i_connection_limit* plimit; + std::size_t response_soft_limit; bool stop_signal_sent; }; - /// Construct a connection with the given io_service. - explicit connection( boost::asio::io_service& io_service, + /// Construct a connection with the given io_context. + explicit connection( io_context_t& io_context, std::shared_ptr state, t_connection_type connection_type, epee::net_utils::ssl_support_t ssl_support); - explicit connection( boost::asio::ip::tcp::socket&& sock, + explicit connection( io_context_t& io_context, + boost::asio::ip::tcp::socket&& sock, std::shared_ptr state, t_connection_type connection_type, epee::net_utils::ssl_support_t ssl_support); @@ -306,7 +324,7 @@ namespace net_utils virtual bool close(); virtual bool call_run_once_service_io(); virtual bool request_callback(); - virtual boost::asio::io_service& get_io_service(); + virtual io_context_t& get_io_context(); virtual bool add_ref(); virtual bool release(); //------------------------------------------------------ @@ -336,7 +354,7 @@ namespace net_utils /// serve up files from the given directory. boosted_tcp_server(t_connection_type connection_type); - explicit boosted_tcp_server(boost::asio::io_service& external_io_service, t_connection_type connection_type); + explicit boosted_tcp_server(boost::asio::io_context& external_io_context, t_connection_type connection_type); ~boosted_tcp_server(); std::map server_type_map; @@ -349,7 +367,7 @@ namespace net_utils const std::string port_ipv6 = "", const std::string address_ipv6 = "::", bool use_ipv6 = false, bool require_ipv4 = true, ssl_options_t ssl_options = ssl_support_t::e_ssl_support_autodetect); - /// Run the server's io_service loop. + /// Run the server's io_context loop. bool run_server(size_t threads_count, bool wait = true, const boost::thread::attributes& attrs = boost::thread::attributes()); /// wait for service workers stop @@ -369,6 +387,8 @@ namespace net_utils size_t get_threads_count(){return m_threads_count;} void set_connection_filter(i_connection_filter* pfilter); + void set_connection_limit(i_connection_limit* plimit); + void set_response_soft_limit(std::size_t limit); void set_default_remote(epee::net_utils::network_address remote) { @@ -409,7 +429,7 @@ namespace net_utils return connections_count; } - boost::asio::io_service& get_io_service(){return io_service_;} + boost::asio::io_context& get_io_context(){return io_context_;} struct idle_callback_conext_base { @@ -417,7 +437,7 @@ namespace net_utils virtual bool call_handler(){return true;} - idle_callback_conext_base(boost::asio::io_service& io_serice): + idle_callback_conext_base(boost::asio::io_context& io_serice): m_timer(io_serice) {} boost::asio::deadline_timer m_timer; @@ -426,7 +446,7 @@ namespace net_utils template struct idle_callback_conext: public idle_callback_conext_base { - idle_callback_conext(boost::asio::io_service& io_serice, t_handler& h, uint64_t period): + idle_callback_conext(boost::asio::io_context& io_serice, t_handler& h, uint64_t period): idle_callback_conext_base(io_serice), m_handler(h) {this->m_period = period;} @@ -442,7 +462,7 @@ namespace net_utils template bool add_idle_handler(t_handler t_callback, uint64_t timeout_ms) { - boost::shared_ptr> ptr(new idle_callback_conext(io_service_, t_callback, timeout_ms)); + boost::shared_ptr> ptr(new idle_callback_conext(io_context_, t_callback, timeout_ms)); //needed call handler here ?... ptr->m_timer.expires_from_now(boost::posix_time::milliseconds(ptr->m_period)); ptr->m_timer.async_wait(boost::bind(&boosted_tcp_server::global_timer_handler, this, ptr)); @@ -461,14 +481,14 @@ namespace net_utils } template - bool async_call(t_handler t_callback) + bool async_call(t_handler&& t_callback) { - io_service_.post(t_callback); + boost::asio::post(io_context_, std::forward(t_callback)); return true; } private: - /// Run the server's io_service loop. + /// Run the server's io_context loop. bool worker_thread(); /// Handle completion of an asynchronous accept operation. void handle_accept_ipv4(const boost::system::error_code& e); @@ -479,18 +499,18 @@ namespace net_utils const std::shared_ptr::shared_state> m_state; - /// The io_service used to perform asynchronous operations. + /// The io_context used to perform asynchronous operations. struct worker { worker() - : io_service(), work(io_service) + : io_context(), work(io_context.get_executor()) {} - boost::asio::io_service io_service; - boost::asio::io_service::work work; + boost::asio::io_context io_context; + boost::asio::executor_work_guard work; }; - std::unique_ptr m_io_service_local_instance; - boost::asio::io_service& io_service_; + std::unique_ptr m_io_context_local_instance; + boost::asio::io_context& io_context_; /// Acceptor used to listen for incoming connections. boost::asio::ip::tcp::acceptor acceptor_; diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index d88f18194..39a58d1b2 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -31,11 +31,12 @@ // - +#include #include #include #include #include +#include #include #include // TODO #include // TODO @@ -145,23 +146,19 @@ namespace net_utils if (m_state.timers.general.wait_expire) { m_state.timers.general.cancel_expire = true; m_state.timers.general.reset_expire = true; - ec_t ec; - m_timers.general.expires_from_now( + m_timers.general.expires_after( std::min( - duration + (add ? m_timers.general.expires_from_now() : duration_t{}), + duration + (add ? (m_timers.general.expiry() - std::chrono::steady_clock::now()) : duration_t{}), get_default_timeout() - ), - ec + ) ); } else { - ec_t ec; - m_timers.general.expires_from_now( + m_timers.general.expires_after( std::min( - duration + (add ? m_timers.general.expires_from_now() : duration_t{}), + duration + (add ? (m_timers.general.expiry() - std::chrono::steady_clock::now()) : duration_t{}), get_default_timeout() - ), - ec + ) ); async_wait_timer(); } @@ -202,8 +199,7 @@ namespace net_utils return; m_state.timers.general.cancel_expire = true; m_state.timers.general.reset_expire = false; - ec_t ec; - m_timers.general.cancel(ec); + m_timers.general.cancel(); } template @@ -225,7 +221,8 @@ namespace net_utils m_state.data.read.buffer.size() ), boost::asio::transfer_exactly(epee::net_utils::get_ssl_magic_size()), - m_strand.wrap( + boost::asio::bind_executor( + m_strand, [this, self](const ec_t &ec, size_t bytes_transferred){ std::lock_guard guard(m_state.lock); m_state.socket.wait_read = false; @@ -246,7 +243,8 @@ namespace net_utils ) { m_state.ssl.enabled = false; m_state.socket.handle_read = true; - connection_basic::strand_.post( + boost::asio::post( + connection_basic::strand_, [this, self, bytes_transferred]{ bool success = m_handler.handle_recv( reinterpret_cast(m_state.data.read.buffer.data()), @@ -304,7 +302,8 @@ namespace net_utils static_cast( connection_basic::get_state() ).ssl_options().configure(connection_basic::socket_, handshake); - m_strand.post( + boost::asio::post( + m_strand, [this, self, on_handshake]{ connection_basic::socket_.async_handshake( handshake, @@ -313,7 +312,7 @@ namespace net_utils m_state.ssl.forced ? 0 : epee::net_utils::get_ssl_magic_size() ), - m_strand.wrap(on_handshake) + boost::asio::bind_executor(m_strand, on_handshake) ); } ); @@ -328,7 +327,7 @@ namespace net_utils return; } auto self = connection::shared_from_this(); - if (m_connection_type != e_connection_type_RPC) { + if (speed_limit_is_enabled()) { auto calc_duration = []{ CRITICAL_REGION_LOCAL( network_throttle_manager_t::m_lock_get_global_throttle_in @@ -345,8 +344,7 @@ namespace net_utils }; const auto duration = calc_duration(); if (duration > duration_t{}) { - ec_t ec; - m_timers.throttle.in.expires_from_now(duration, ec); + m_timers.throttle.in.expires_after(duration); m_state.timers.throttle.in.wait_expire = true; m_timers.throttle.in.async_wait([this, self](const ec_t &ec){ std::lock_guard guard(m_state.lock); @@ -382,7 +380,7 @@ namespace net_utils m_conn_context.m_max_speed_down, speed ); - { + if (speed_limit_is_enabled()) { CRITICAL_REGION_LOCAL( network_throttle_manager_t::m_lock_get_global_throttle_in ); @@ -401,7 +399,8 @@ namespace net_utils // writes until the connection terminates without deadlocking waiting // for handle_recv. m_state.socket.handle_read = true; - connection_basic::strand_.post( + boost::asio::post( + connection_basic::strand_, [this, self, bytes_transferred]{ bool success = m_handler.handle_recv( reinterpret_cast(m_state.data.read.buffer.data()), @@ -428,17 +427,18 @@ namespace net_utils m_state.data.read.buffer.data(), m_state.data.read.buffer.size() ), - m_strand.wrap(on_read) + boost::asio::bind_executor(m_strand, on_read) ); else - m_strand.post( + boost::asio::post( + m_strand, [this, self, on_read]{ connection_basic::socket_.async_read_some( boost::asio::buffer( m_state.data.read.buffer.data(), m_state.data.read.buffer.size() ), - m_strand.wrap(on_read) + boost::asio::bind_executor(m_strand, on_read) ); } ); @@ -454,7 +454,7 @@ namespace net_utils return; } auto self = connection::shared_from_this(); - if (m_connection_type != e_connection_type_RPC) { + if (speed_limit_is_enabled()) { auto calc_duration = [this]{ CRITICAL_REGION_LOCAL( network_throttle_manager_t::m_lock_get_global_throttle_out @@ -473,8 +473,7 @@ namespace net_utils }; const auto duration = calc_duration(); if (duration > duration_t{}) { - ec_t ec; - m_timers.throttle.out.expires_from_now(duration, ec); + m_timers.throttle.out.expires_after(duration); m_state.timers.throttle.out.wait_expire = true; m_timers.throttle.out.async_wait([this, self](const ec_t &ec){ std::lock_guard guard(m_state.lock); @@ -498,10 +497,12 @@ namespace net_utils if (m_state.socket.cancel_write) { m_state.socket.cancel_write = false; m_state.data.write.queue.clear(); + m_state.data.write.total_bytes = 0; state_status_check(); } else if (ec.value()) { m_state.data.write.queue.clear(); + m_state.data.write.total_bytes = 0; interrupt(); } else { @@ -513,7 +514,7 @@ namespace net_utils m_conn_context.m_max_speed_down, speed ); - { + if (speed_limit_is_enabled()) { CRITICAL_REGION_LOCAL( network_throttle_manager_t::m_lock_get_global_throttle_out ); @@ -526,8 +527,11 @@ namespace net_utils start_timer(get_default_timeout(), true); } - assert(bytes_transferred == m_state.data.write.queue.back().size()); + const std::size_t byte_count = m_state.data.write.queue.back().size(); + assert(bytes_transferred == byte_count); m_state.data.write.queue.pop_back(); + m_state.data.write.total_bytes -= + std::min(m_state.data.write.total_bytes, byte_count); m_state.condition.notify_all(); start_write(); } @@ -539,10 +543,11 @@ namespace net_utils m_state.data.write.queue.back().data(), m_state.data.write.queue.back().size() ), - m_strand.wrap(on_write) + boost::asio::bind_executor(m_strand, on_write) ); else - m_strand.post( + boost::asio::post( + m_strand, [this, self, on_write]{ boost::asio::async_write( connection_basic::socket_, @@ -550,7 +555,7 @@ namespace net_utils m_state.data.write.queue.back().data(), m_state.data.write.queue.back().size() ), - m_strand.wrap(on_write) + boost::asio::bind_executor(m_strand, on_write) ); } ); @@ -587,10 +592,11 @@ namespace net_utils terminate(); } }; - m_strand.post( + boost::asio::post( + m_strand, [this, self, on_shutdown]{ connection_basic::socket_.async_shutdown( - m_strand.wrap(on_shutdown) + boost::asio::bind_executor(m_strand, on_shutdown) ); } ); @@ -605,15 +611,13 @@ namespace net_utils wait_socket = m_state.socket.cancel_handshake = true; if (m_state.timers.throttle.in.wait_expire) { m_state.timers.throttle.in.cancel_expire = true; - ec_t ec; - m_timers.throttle.in.cancel(ec); + m_timers.throttle.in.cancel(); } if (m_state.socket.wait_read) wait_socket = m_state.socket.cancel_read = true; if (m_state.timers.throttle.out.wait_expire) { m_state.timers.throttle.out.cancel_expire = true; - ec_t ec; - m_timers.throttle.out.cancel(ec); + m_timers.throttle.out.cancel(); } if (m_state.socket.wait_write) wait_socket = m_state.socket.cancel_write = true; @@ -671,8 +675,9 @@ namespace net_utils return; if (m_state.timers.throttle.out.wait_expire) return; - if (m_state.socket.wait_write) - return; + // \NOTE See on_terminating() comments + //if (m_state.socket.wait_write) + // return; if (m_state.socket.wait_shutdown) return; if (m_state.protocol.wait_init) @@ -730,8 +735,13 @@ namespace net_utils return; if (m_state.timers.throttle.out.wait_expire) return; - if (m_state.socket.wait_write) - return; + // Writes cannot be canceled due to `async_write` being a "composed" + // handler. ASIO has new cancellation routines, not available in 1.66, to + // handle this situation. The problem is that if cancel is called after an + // intermediate handler is queued, the op will not check the cancel flag in + // our code, and will instead queue up another write. + //if (m_state.socket.wait_write) + // return; if (m_state.socket.wait_shutdown) return; if (m_state.protocol.wait_init) @@ -758,6 +768,8 @@ namespace net_utils std::lock_guard guard(m_state.lock); if (m_state.status != status_t::RUNNING || m_state.socket.wait_handshake) return false; + if (std::numeric_limits::max() - m_state.data.write.total_bytes < message.size()) + return false; // Wait for the write queue to fall below the max. If it doesn't after a // randomized delay, drop the connection. @@ -775,7 +787,14 @@ namespace net_utils std::uniform_int_distribution<>(5000, 6000)(rng) ); }; - if (m_state.data.write.queue.size() <= ABSTRACT_SERVER_SEND_QUE_MAX_COUNT) + + // The bytes check intentionally does not include incoming message size. + // This allows for a soft overflow; a single http response will never fail + // this check, but multiple responses could. Clients can avoid this case + // by reading the entire response before making another request. P2P + // should never hit the MAX_BYTES check (when using default values). + if (m_state.data.write.queue.size() <= ABSTRACT_SERVER_SEND_QUE_MAX_COUNT && + m_state.data.write.total_bytes <= static_cast(connection_basic::get_state()).response_soft_limit) return true; m_state.data.write.wait_consume = true; bool success = m_state.condition.wait_for( @@ -784,14 +803,23 @@ namespace net_utils [this]{ return ( m_state.status != status_t::RUNNING || - m_state.data.write.queue.size() <= - ABSTRACT_SERVER_SEND_QUE_MAX_COUNT + ( + m_state.data.write.queue.size() <= + ABSTRACT_SERVER_SEND_QUE_MAX_COUNT && + m_state.data.write.total_bytes <= + static_cast(connection_basic::get_state()).response_soft_limit + ) ); } ); m_state.data.write.wait_consume = false; if (!success) { - terminate(); + // synchronize with intermediate writes on `m_strand` + auto self = connection::shared_from_this(); + boost::asio::post(m_strand, [this, self] { + std::lock_guard guard(m_state.lock); + terminate(); + }); return false; } else @@ -817,7 +845,9 @@ namespace net_utils ) { if (!wait_consume()) return false; + const std::size_t byte_count = message.size(); m_state.data.write.queue.emplace_front(std::move(message)); + m_state.data.write.total_bytes += byte_count; start_write(); } else { @@ -827,6 +857,7 @@ namespace net_utils m_state.data.write.queue.emplace_front( message.take_slice(CHUNK_SIZE) ); + m_state.data.write.total_bytes += m_state.data.write.queue.front().size(); start_write(); } } @@ -860,7 +891,7 @@ namespace net_utils ipv4_network_address{ uint32_t{ boost::asio::detail::socket_ops::host_to_network_long( - endpoint.address().to_v4().to_ulong() + endpoint.address().to_v4().to_uint() ) }, endpoint.port() @@ -873,6 +904,13 @@ namespace net_utils ).pfilter; if (filter && !filter->is_remote_host_allowed(*real_remote)) return false; + + auto *limit = static_cast( + connection_basic::get_state() + ).plimit; + if (limit && limit->is_host_limit(*real_remote)) + return false; + ec_t ec; #if !defined(_WIN32) || !defined(__i686) connection_basic::socket_.next_layer().set_option( @@ -938,7 +976,8 @@ namespace net_utils ssl_support_t ssl_support ): connection( - std::move(socket_t{io_context}), + io_context, + socket_t{io_context}, std::move(shared_state), connection_type, ssl_support @@ -948,15 +987,16 @@ namespace net_utils template connection::connection( + io_context_t &io_context, socket_t &&socket, std::shared_ptr shared_state, t_connection_type connection_type, ssl_support_t ssl_support ): - connection_basic(std::move(socket), shared_state, ssl_support), + connection_basic(io_context, std::move(socket), shared_state, ssl_support), m_handler(this, *shared_state, m_conn_context), m_connection_type(connection_type), - m_io_context{GET_IO_SERVICE(connection_basic::socket_)}, + m_io_context{io_context}, m_strand{m_io_context}, m_timers{m_io_context} { @@ -1022,7 +1062,7 @@ namespace net_utils template bool connection::speed_limit_is_enabled() const { - return m_connection_type != e_connection_type_RPC; + return m_connection_type == e_connection_type_P2P; } template @@ -1075,7 +1115,7 @@ namespace net_utils return false; auto self = connection::shared_from_this(); ++m_state.protocol.wait_callback; - connection_basic::strand_.post([this, self]{ + boost::asio::post(connection_basic::strand_, [this, self]{ m_handler.handle_qued_callback(); std::lock_guard guard(m_state.lock); --m_state.protocol.wait_callback; @@ -1088,7 +1128,7 @@ namespace net_utils } template - typename connection::io_context_t &connection::get_io_service() + typename connection::io_context_t &connection::get_io_context() { return m_io_context; } @@ -1128,10 +1168,10 @@ namespace net_utils template boosted_tcp_server::boosted_tcp_server( t_connection_type connection_type ) : m_state(std::make_shared::shared_state>()), - m_io_service_local_instance(new worker()), - io_service_(m_io_service_local_instance->io_service), - acceptor_(io_service_), - acceptor_ipv6(io_service_), + m_io_context_local_instance(new worker()), + io_context_(m_io_context_local_instance->io_context), + acceptor_(io_context_), + acceptor_ipv6(io_context_), default_remote(), m_stop_signal_sent(false), m_port(0), m_threads_count(0), @@ -1145,11 +1185,11 @@ namespace net_utils } template - boosted_tcp_server::boosted_tcp_server(boost::asio::io_service& extarnal_io_service, t_connection_type connection_type) : + boosted_tcp_server::boosted_tcp_server(boost::asio::io_context& extarnal_io_context, t_connection_type connection_type) : m_state(std::make_shared::shared_state>()), - io_service_(extarnal_io_service), - acceptor_(io_service_), - acceptor_ipv6(io_service_), + io_context_(extarnal_io_context), + acceptor_(io_context_), + acceptor_ipv6(io_context_), default_remote(), m_stop_signal_sent(false), m_port(0), m_threads_count(0), @@ -1196,24 +1236,27 @@ namespace net_utils std::string ipv4_failed = ""; std::string ipv6_failed = ""; + + boost::asio::ip::tcp::resolver resolver(io_context_); + try { - boost::asio::ip::tcp::resolver resolver(io_service_); - boost::asio::ip::tcp::resolver::query query(address, boost::lexical_cast(port), boost::asio::ip::tcp::resolver::query::canonical_name); - boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query); - acceptor_.open(endpoint.protocol()); + const auto results = resolver.resolve( + address, boost::lexical_cast(port), boost::asio::ip::tcp::resolver::canonical_name + ); + acceptor_.open(results.begin()->endpoint().protocol()); #if !defined(_WIN32) acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); #endif - acceptor_.bind(endpoint); + acceptor_.bind(*results.begin()); acceptor_.listen(); boost::asio::ip::tcp::endpoint binded_endpoint = acceptor_.local_endpoint(); m_port = binded_endpoint.port(); MDEBUG("start accept (IPv4)"); - new_connection_.reset(new connection(io_service_, m_state, m_connection_type, m_state->ssl_options().support)); + new_connection_.reset(new connection(io_context_, m_state, m_connection_type, m_state->ssl_options().support)); acceptor_.async_accept(new_connection_->socket(), - boost::bind(&boosted_tcp_server::handle_accept_ipv4, this, - boost::asio::placeholders::error)); + boost::bind(&boosted_tcp_server::handle_accept_ipv4, this, + boost::asio::placeholders::error)); } catch (const std::exception &e) { @@ -1234,23 +1277,25 @@ namespace net_utils try { if (port_ipv6 == 0) port_ipv6 = port; // default arg means bind to same port as ipv4 - boost::asio::ip::tcp::resolver resolver(io_service_); - boost::asio::ip::tcp::resolver::query query(address_ipv6, boost::lexical_cast(port_ipv6), boost::asio::ip::tcp::resolver::query::canonical_name); - boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query); - acceptor_ipv6.open(endpoint.protocol()); + + const auto results = resolver.resolve( + address_ipv6, boost::lexical_cast(port_ipv6), boost::asio::ip::tcp::resolver::canonical_name + ); + + acceptor_ipv6.open(results.begin()->endpoint().protocol()); #if !defined(_WIN32) acceptor_ipv6.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); #endif acceptor_ipv6.set_option(boost::asio::ip::v6_only(true)); - acceptor_ipv6.bind(endpoint); + acceptor_ipv6.bind(*results.begin()); acceptor_ipv6.listen(); boost::asio::ip::tcp::endpoint binded_endpoint = acceptor_ipv6.local_endpoint(); m_port_ipv6 = binded_endpoint.port(); MDEBUG("start accept (IPv6)"); - new_connection_ipv6.reset(new connection(io_service_, m_state, m_connection_type, m_state->ssl_options().support)); + new_connection_ipv6.reset(new connection(io_context_, m_state, m_connection_type, m_state->ssl_options().support)); acceptor_ipv6.async_accept(new_connection_ipv6->socket(), - boost::bind(&boosted_tcp_server::handle_accept_ipv6, this, - boost::asio::placeholders::error)); + boost::bind(&boosted_tcp_server::handle_accept_ipv6, this, + boost::asio::placeholders::error)); } catch (const std::exception &e) { @@ -1314,7 +1359,7 @@ namespace net_utils { try { - io_service_.run(); + io_context_.run(); return true; } catch(const std::exception& ex) @@ -1349,6 +1394,20 @@ namespace net_utils } //--------------------------------------------------------------------------------- template + void boosted_tcp_server::set_connection_limit(i_connection_limit* plimit) + { + assert(m_state != nullptr); // always set in constructor + m_state->plimit = plimit; + } + //--------------------------------------------------------------------------------- + template + void boosted_tcp_server::set_response_soft_limit(const std::size_t limit) + { + assert(m_state != nullptr); // always set in constructor + m_state->response_soft_limit = limit; + } + //--------------------------------------------------------------------------------- + template bool boosted_tcp_server::run_server(size_t threads_count, bool wait, const boost::thread::attributes& attrs) { TRY_ENTRY(); @@ -1358,7 +1417,7 @@ namespace net_utils while(!m_stop_signal_sent) { - // Create a pool of threads to run all of the io_services. + // Create a pool of threads to run all of the io_contexts. CRITICAL_REGION_BEGIN(m_threads_lock); for (std::size_t i = 0; i < threads_count; ++i) { @@ -1450,7 +1509,7 @@ namespace net_utils } connections_.clear(); connections_mutex.unlock(); - io_service_.stop(); + io_context_.stop(); CATCH_ENTRY_L0("boosted_tcp_server::send_stop_signal()", void()); } //--------------------------------------------------------------------------------- @@ -1497,7 +1556,7 @@ namespace net_utils (*current_new_connection)->setRpcStation(); // hopefully this is not needed actually } connection_ptr conn(std::move((*current_new_connection))); - (*current_new_connection).reset(new connection(io_service_, m_state, m_connection_type, conn->get_ssl_support())); + (*current_new_connection).reset(new connection(io_context_, m_state, m_connection_type, conn->get_ssl_support())); current_acceptor->async_accept((*current_new_connection)->socket(), boost::bind(accept_function_pointer, this, boost::asio::placeholders::error)); @@ -1532,7 +1591,7 @@ namespace net_utils assert(m_state != nullptr); // always set in constructor _erro("Some problems at accept: " << e.message() << ", connections_count = " << m_state->sock_count); misc_utils::sleep_no_w(100); - (*current_new_connection).reset(new connection(io_service_, m_state, m_connection_type, (*current_new_connection)->get_ssl_support())); + (*current_new_connection).reset(new connection(io_context_, m_state, m_connection_type, (*current_new_connection)->get_ssl_support())); current_acceptor->async_accept((*current_new_connection)->socket(), boost::bind(accept_function_pointer, this, boost::asio::placeholders::error)); @@ -1541,9 +1600,9 @@ namespace net_utils template bool boosted_tcp_server::add_connection(t_connection_context& out, boost::asio::ip::tcp::socket&& sock, network_address real_remote, epee::net_utils::ssl_support_t ssl_support) { - if(std::addressof(get_io_service()) == std::addressof(GET_IO_SERVICE(sock))) + if(std::addressof(get_io_context()) == std::addressof(sock.get_executor().context())) { - connection_ptr conn(new connection(std::move(sock), m_state, m_connection_type, ssl_support)); + connection_ptr conn(new connection(io_context_, std::move(sock), m_state, m_connection_type, ssl_support)); if(conn->start(false, 1 < m_threads_count, std::move(real_remote))) { conn->get_context(out); @@ -1553,7 +1612,7 @@ namespace net_utils } else { - MWARNING(out << " was not added, socket/io_service mismatch"); + MWARNING(out << " was not added, socket/io_context mismatch"); } return false; } @@ -1566,7 +1625,7 @@ namespace net_utils sock_.open(remote_endpoint.protocol()); if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" ) { - boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(bind_ip.c_str()), 0); + boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::make_address(bind_ip), 0); boost::system::error_code ec; sock_.bind(local_endpoint, ec); if (ec) @@ -1661,7 +1720,7 @@ namespace net_utils { TRY_ENTRY(); - connection_ptr new_connection_l(new connection(io_service_, m_state, m_connection_type, ssl_support) ); + connection_ptr new_connection_l(new connection(io_context_, m_state, m_connection_type, ssl_support) ); connections_mutex.lock(); connections_.insert(new_connection_l); MDEBUG("connections_ size now " << connections_.size()); @@ -1671,14 +1730,16 @@ namespace net_utils bool try_ipv6 = false; - boost::asio::ip::tcp::resolver resolver(io_service_); - boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name); + boost::asio::ip::tcp::resolver resolver(io_context_); + boost::asio::ip::tcp::resolver::results_type results{}; boost::system::error_code resolve_error; - boost::asio::ip::tcp::resolver::iterator iterator; + try { //resolving ipv4 address as ipv6 throws, catch here and move on - iterator = resolver.resolve(query, resolve_error); + results = resolver.resolve( + boost::asio::ip::tcp::v4(), adr, port, boost::asio::ip::tcp::resolver::canonical_name, resolve_error + ); } catch (const boost::system::system_error& e) { @@ -1696,8 +1757,7 @@ namespace net_utils std::string bind_ip_to_use; - boost::asio::ip::tcp::resolver::iterator end; - if(iterator == end) + if(results.empty()) { if (!m_use_ipv6) { @@ -1717,11 +1777,11 @@ namespace net_utils if (try_ipv6) { - boost::asio::ip::tcp::resolver::query query6(boost::asio::ip::tcp::v6(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name); + results = resolver.resolve( + boost::asio::ip::tcp::v6(), adr, port, boost::asio::ip::tcp::resolver::canonical_name, resolve_error + ); - iterator = resolver.resolve(query6, resolve_error); - - if(iterator == end) + if(results.empty()) { _erro("Failed to resolve " << adr); return false; @@ -1741,6 +1801,8 @@ namespace net_utils } + const auto iterator = results.begin(); + MDEBUG("Trying to connect to " << adr << ":" << port << ", bind_ip = " << bind_ip_to_use); //boost::asio::ip::tcp::endpoint remote_endpoint(boost::asio::ip::address::from_string(addr.c_str()), port); @@ -1767,7 +1829,6 @@ namespace net_utils if (r) { new_connection_l->get_context(conn_context); - //new_connection_l.reset(new connection(io_service_, m_config, m_sock_count, m_pfilter)); } else { @@ -1786,7 +1847,7 @@ namespace net_utils bool boosted_tcp_server::connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeout, const t_callback &cb, const std::string& bind_ip, epee::net_utils::ssl_support_t ssl_support) { TRY_ENTRY(); - connection_ptr new_connection_l(new connection(io_service_, m_state, m_connection_type, ssl_support) ); + connection_ptr new_connection_l(new connection(io_context_, m_state, m_connection_type, ssl_support) ); connections_mutex.lock(); connections_.insert(new_connection_l); MDEBUG("connections_ size now " << connections_.size()); @@ -1796,14 +1857,16 @@ namespace net_utils bool try_ipv6 = false; - boost::asio::ip::tcp::resolver resolver(io_service_); - boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name); + boost::asio::ip::tcp::resolver resolver(io_context_); + boost::asio::ip::tcp::resolver::results_type results{}; boost::system::error_code resolve_error; - boost::asio::ip::tcp::resolver::iterator iterator; + try { //resolving ipv4 address as ipv6 throws, catch here and move on - iterator = resolver.resolve(query, resolve_error); + results = resolver.resolve( + boost::asio::ip::tcp::v4(), adr, port, boost::asio::ip::tcp::resolver::canonical_name, resolve_error + ); } catch (const boost::system::system_error& e) { @@ -1819,8 +1882,7 @@ namespace net_utils throw; } - boost::asio::ip::tcp::resolver::iterator end; - if(iterator == end) + if(results.empty()) { if (!try_ipv6) { @@ -1835,24 +1897,23 @@ namespace net_utils if (try_ipv6) { - boost::asio::ip::tcp::resolver::query query6(boost::asio::ip::tcp::v6(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name); + results = resolver.resolve( + boost::asio::ip::tcp::v6(), adr, port, boost::asio::ip::tcp::resolver::canonical_name, resolve_error + ); - iterator = resolver.resolve(query6, resolve_error); - - if(iterator == end) + if(results.empty()) { _erro("Failed to resolve " << adr); return false; } } - - boost::asio::ip::tcp::endpoint remote_endpoint(*iterator); + boost::asio::ip::tcp::endpoint remote_endpoint(*results.begin()); sock_.open(remote_endpoint.protocol()); if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" ) { - boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(bind_ip.c_str()), 0); + boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::make_address(bind_ip.c_str()), 0); boost::system::error_code ec; sock_.bind(local_endpoint, ec); if (ec) @@ -1864,7 +1925,7 @@ namespace net_utils } } - boost::shared_ptr sh_deadline(new boost::asio::deadline_timer(io_service_)); + boost::shared_ptr sh_deadline(new boost::asio::deadline_timer(io_context_)); //start deadline sh_deadline->expires_from_now(boost::posix_time::milliseconds(conn_timeout)); sh_deadline->async_wait([=](const boost::system::error_code& error) diff --git a/contrib/epee/include/net/connection_basic.hpp b/contrib/epee/include/net/connection_basic.hpp index a26d538cd..8291d137b 100644 --- a/contrib/epee/include/net/connection_basic.hpp +++ b/contrib/epee/include/net/connection_basic.hpp @@ -112,21 +112,20 @@ class connection_basic { // not-templated base class for rapid developmet of som std::deque m_send_que; volatile bool m_is_multithreaded; /// Strand to ensure the connection's handlers are not called concurrently. - boost::asio::io_service::strand strand_; + boost::asio::io_context::strand strand_; /// Socket for the connection. boost::asio::ssl::stream socket_; ssl_support_t m_ssl_support; public: // first counter is the ++/-- count of current sockets, the other socket_number is only-increasing ++ number generator - connection_basic(boost::asio::ip::tcp::socket&& socket, std::shared_ptr state, ssl_support_t ssl_support); - connection_basic(boost::asio::io_service &io_service, std::shared_ptr state, ssl_support_t ssl_support); + connection_basic(boost::asio::io_context &context, boost::asio::ip::tcp::socket&& sock, std::shared_ptr state, ssl_support_t ssl_support); + connection_basic(boost::asio::io_context &context, std::shared_ptr state, ssl_support_t ssl_support); virtual ~connection_basic() noexcept(false); //! \return `shared_state` object passed in construction (ptr never changes). connection_basic_shared_state& get_state() noexcept { return *m_state; /* verified in constructor */ } - connection_basic(boost::asio::io_service& io_service, std::atomic &ref_sock_count, std::atomic &sock_number, ssl_support_t ssl); boost::asio::ip::tcp::socket& socket() { return socket_.next_layer(); } ssl_support_t get_ssl_support() const { return m_ssl_support; } @@ -135,7 +134,7 @@ class connection_basic { // not-templated base class for rapid developmet of som 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, buffer); + return m_state->ssl_options().handshake(strand_.context(), socket_, type, buffer); } template diff --git a/contrib/epee/include/net/http_protocol_handler.h b/contrib/epee/include/net/http_protocol_handler.h index 258b07e2c..8b73964dd 100644 --- a/contrib/epee/include/net/http_protocol_handler.h +++ b/contrib/epee/include/net/http_protocol_handler.h @@ -32,6 +32,7 @@ #include #include +#include #include "net_utils_base.h" #include "http_auth.h" #include "http_base.h" @@ -54,8 +55,13 @@ namespace net_utils { std::string m_folder; std::vector m_access_control_origins; + std::unordered_map m_connections; boost::optional m_user; size_t m_max_content_length{std::numeric_limits::max()}; + std::size_t m_connection_count{0}; + std::size_t m_max_public_ip_connections{3}; + std::size_t m_max_private_ip_connections{25}; + std::size_t m_max_connections{100}; critical_section m_lock; }; @@ -70,7 +76,7 @@ namespace net_utils typedef http_server_config config_type; simple_http_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context); - virtual ~simple_http_connection_handler(){} + virtual ~simple_http_connection_handler(); bool release_protocol() { @@ -86,10 +92,7 @@ namespace net_utils { return true; } - bool after_init_connection() - { - return true; - } + bool after_init_connection(); virtual bool handle_recv(const void* ptr, size_t cb); virtual bool handle_request(const http::http_request_info& query_info, http_response_info& response); @@ -146,6 +149,7 @@ namespace net_utils protected: i_service_endpoint* m_psnd_hndlr; t_connection_context& m_conn_context; + bool m_initialized; }; template @@ -212,10 +216,6 @@ namespace net_utils } void handle_qued_callback() {} - bool after_init_connection() - { - return true; - } private: //simple_http_connection_handler::config_type m_stub_config; diff --git a/contrib/epee/include/net/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl index f7d2074b2..6647d1f15 100644 --- a/contrib/epee/include/net/http_protocol_handler.inl +++ b/contrib/epee/include/net/http_protocol_handler.inl @@ -208,11 +208,46 @@ namespace net_utils m_newlines(0), m_bytes_read(0), m_psnd_hndlr(psnd_hndlr), - m_conn_context(conn_context) + m_conn_context(conn_context), + m_initialized(false) { } //-------------------------------------------------------------------------------------------- + template + simple_http_connection_handler::~simple_http_connection_handler() + { + try + { + if (m_initialized) + { + CRITICAL_REGION_LOCAL(m_config.m_lock); + if (m_config.m_connection_count) + --m_config.m_connection_count; + auto elem = m_config.m_connections.find(m_conn_context.m_remote_address.host_str()); + if (elem != m_config.m_connections.end()) + { + if (elem->second == 1 || elem->second == 0) + m_config.m_connections.erase(elem); + else + --(elem->second); + } + } + } + catch (...) + {} + } + //-------------------------------------------------------------------------------------------- + template + bool simple_http_connection_handler::after_init_connection() + { + CRITICAL_REGION_LOCAL(m_config.m_lock); + ++m_config.m_connections[m_conn_context.m_remote_address.host_str()]; + ++m_config.m_connection_count; + m_initialized = true; + return true; + } + //-------------------------------------------------------------------------------------------- template bool simple_http_connection_handler::set_ready_state() { diff --git a/contrib/epee/include/net/http_server_handlers_map2.h b/contrib/epee/include/net/http_server_handlers_map2.h index ffb3f3b7e..8e7db53c0 100644 --- a/contrib/epee/include/net/http_server_handlers_map2.h +++ b/contrib/epee/include/net/http_server_handlers_map2.h @@ -71,7 +71,7 @@ else if((query_info.m_URI == s_pattern) && (cond)) \ { \ handled = true; \ - uint64_t ticks = misc_utils::get_tick_count(); \ + uint64_t ticks = epee::misc_utils::get_tick_count(); \ boost::value_initialized req; \ bool parse_res = epee::serialization::load_t_from_json(static_cast(req), query_info.m_body); \ if (!parse_res) \ @@ -107,7 +107,7 @@ else if(query_info.m_URI == s_pattern) \ { \ handled = true; \ - uint64_t ticks = misc_utils::get_tick_count(); \ + uint64_t ticks = epee::misc_utils::get_tick_count(); \ boost::value_initialized req; \ bool parse_res = epee::serialization::load_t_from_binary(static_cast(req), epee::strspan(query_info.m_body)); \ if (!parse_res) \ @@ -117,7 +117,7 @@ response_info.m_response_comment = "Bad request"; \ return true; \ } \ - uint64_t ticks1 = misc_utils::get_tick_count(); \ + uint64_t ticks1 = epee::misc_utils::get_tick_count(); \ boost::value_initialized resp;\ MINFO(m_conn_context << "calling " << s_pattern); \ bool res = false; \ @@ -129,7 +129,7 @@ response_info.m_response_comment = "Internal Server Error"; \ return true; \ } \ - uint64_t ticks2 = misc_utils::get_tick_count(); \ + uint64_t ticks2 = epee::misc_utils::get_tick_count(); \ epee::byte_slice buffer; \ epee::serialization::store_t_to_binary(static_cast(resp), buffer, 64 * 1024); \ uint64_t ticks3 = epee::misc_utils::get_tick_count(); \ @@ -171,6 +171,13 @@ epee::serialization::store_t_to_json(static_cast(rsp), response_info.m_body); \ return true; \ } \ + epee::serialization::storage_entry params_; \ + params_ = epee::serialization::storage_entry(epee::serialization::section()); \ + if(!ps.get_value("params", params_, nullptr)) \ + { \ + epee::serialization::section params_section; \ + ps.set_value("params", std::move(params_section), nullptr); \ + } \ if(false) return true; //just a stub to have "else if" diff --git a/contrib/epee/include/net/http_server_impl_base.h b/contrib/epee/include/net/http_server_impl_base.h index d88b53c94..e24391674 100644 --- a/contrib/epee/include/net/http_server_impl_base.h +++ b/contrib/epee/include/net/http_server_impl_base.h @@ -33,6 +33,7 @@ #include #include +#include "cryptonote_config.h" #include "net/abstract_tcp_server2.h" #include "http_protocol_handler.h" #include "net/http_server_handlers_map2.h" @@ -44,7 +45,8 @@ namespace epee { template - class http_server_impl_base: public net_utils::http::i_http_server_handler + class http_server_impl_base: public net_utils::http::i_http_server_handler, + net_utils::i_connection_limit { public: @@ -52,7 +54,7 @@ namespace epee : m_net_server(epee::net_utils::e_connection_type_RPC) {} - explicit http_server_impl_base(boost::asio::io_service& external_io_service) + explicit http_server_impl_base(boost::asio::io_context& external_io_service) : m_net_server(external_io_service) {} @@ -60,8 +62,16 @@ namespace epee const std::string& bind_ipv6_address = "::", bool use_ipv6 = false, bool require_ipv4 = true, std::vector access_control_origins = std::vector(), boost::optional user = boost::none, - net_utils::ssl_options_t ssl_options = net_utils::ssl_support_t::e_ssl_support_autodetect) + net_utils::ssl_options_t ssl_options = net_utils::ssl_support_t::e_ssl_support_autodetect, + const std::size_t max_public_ip_connections = DEFAULT_RPC_MAX_CONNECTIONS_PER_PUBLIC_IP, + const std::size_t max_private_ip_connections = DEFAULT_RPC_MAX_CONNECTIONS_PER_PRIVATE_IP, + const std::size_t max_connections = DEFAULT_RPC_MAX_CONNECTIONS, + const std::size_t response_soft_limit = DEFAULT_RPC_SOFT_LIMIT_SIZE) { + if (max_connections < max_public_ip_connections) + throw std::invalid_argument{"Max public IP connections cannot be more than max connections"}; + if (max_connections < max_private_ip_connections) + throw std::invalid_argument{"Max private IP connections cannot be more than max connections"}; //set self as callback handler m_net_server.get_config_object().m_phandler = static_cast(this); @@ -75,6 +85,11 @@ namespace epee m_net_server.get_config_object().m_access_control_origins = std::move(access_control_origins); m_net_server.get_config_object().m_user = std::move(user); + m_net_server.get_config_object().m_max_public_ip_connections = max_public_ip_connections; + m_net_server.get_config_object().m_max_private_ip_connections = max_private_ip_connections; + m_net_server.get_config_object().m_max_connections = max_connections; + m_net_server.set_response_soft_limit(response_soft_limit); + m_net_server.set_connection_limit(this); MGINFO("Binding on " << bind_ip << " (IPv4):" << bind_port); if (use_ipv6) @@ -131,6 +146,26 @@ namespace epee } protected: + + virtual bool is_host_limit(const net_utils::network_address& na) override final + { + auto& config = m_net_server.get_config_object(); + CRITICAL_REGION_LOCAL(config.m_lock); + if (config.m_max_connections <= config.m_connection_count) + return true; + + const bool is_private = na.is_loopback() || na.is_local(); + const auto elem = config.m_connections.find(na.host_str()); + if (elem != config.m_connections.end()) + { + if (is_private) + return config.m_max_private_ip_connections <= elem->second; + else + return config.m_max_public_ip_connections <= elem->second; + } + return false; + } + net_utils::boosted_tcp_server > m_net_server; }; } diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h index bd6ffe930..86cee9a11 100644 --- a/contrib/epee/include/net/levin_protocol_handler_async.h +++ b/contrib/epee/include/net/levin_protocol_handler_async.h @@ -200,7 +200,7 @@ public: struct anvoke_handler: invoke_response_handler_base { anvoke_handler(const callback_t& cb, uint64_t timeout, async_protocol_handler& con, int command) - :m_cb(cb), m_timeout(timeout), m_con(con), m_timer(con.m_pservice_endpoint->get_io_service()), m_timer_started(false), + :m_cb(cb), m_timeout(timeout), m_con(con), m_timer(con.m_pservice_endpoint->get_io_context()), m_timer_started(false), m_cancel_timer_called(false), m_timer_cancelled(false), m_command(command) { if(m_con.start_outer_call()) diff --git a/contrib/epee/include/net/net_helper.h b/contrib/epee/include/net/net_helper.h index 0a35797fd..6728daa0e 100644 --- a/contrib/epee/include/net/net_helper.h +++ b/contrib/epee/include/net/net_helper.h @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include #include @@ -158,11 +158,11 @@ namespace net_utils inline try_connect_result_t try_connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout) { - m_deadline.expires_from_now(timeout); + m_deadline.expires_after(timeout); boost::unique_future connection = m_connector(addr, port, m_deadline); for (;;) { - m_io_service.reset(); + m_io_service.restart(); m_io_service.run_one(); if (connection.is_ready()) @@ -178,7 +178,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_io_service, *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) { @@ -285,7 +285,7 @@ namespace net_utils try { - m_deadline.expires_from_now(timeout); + m_deadline.expires_after(timeout); // Set up the variable that receives the result of the asynchronous // operation. The error code is set to would_block to signal that the @@ -303,7 +303,7 @@ namespace net_utils // Block until the asynchronous operation has completed. while (ec == boost::asio::error::would_block) { - m_io_service.reset(); + m_io_service.restart(); m_io_service.run_one(); } @@ -409,7 +409,7 @@ namespace net_utils // Set a deadline for the asynchronous operation. Since this function uses // a composed operation (async_read_until), the deadline applies to the // entire operation, rather than individual reads from the socket. - m_deadline.expires_from_now(timeout); + m_deadline.expires_after(timeout); // Set up the variable that receives the result of the asynchronous // operation. The error code is set to would_block to signal that the @@ -436,7 +436,7 @@ namespace net_utils // Block until the asynchronous operation has completed. while (ec == boost::asio::error::would_block && !m_shutdowned) { - m_io_service.reset(); + m_io_service.restart(); m_io_service.run_one(); } @@ -495,7 +495,7 @@ namespace net_utils // Set a deadline for the asynchronous operation. Since this function uses // a composed operation (async_read_until), the deadline applies to the // entire operation, rather than individual reads from the socket. - m_deadline.expires_from_now(timeout); + m_deadline.expires_after(timeout); // Set up the variable that receives the result of the asynchronous // operation. The error code is set to would_block to signal that the @@ -580,7 +580,7 @@ namespace net_utils return true; } - boost::asio::io_service& get_io_service() + boost::asio::io_context& get_io_service() { return m_io_service; } @@ -607,7 +607,7 @@ namespace net_utils // Check whether the deadline has passed. We compare the deadline against // the current time since a new asynchronous operation may have moved the // deadline before this actor had a chance to run. - if (m_deadline.expires_at() <= std::chrono::steady_clock::now()) + if (m_deadline.expiry() <= std::chrono::steady_clock::now()) { // The deadline has passed. The socket is closed so that any outstanding // asynchronous operations are cancelled. This allows the blocked @@ -628,11 +628,11 @@ namespace net_utils void shutdown_ssl() { // ssl socket shutdown blocks if server doesn't respond. We close after 2 secs boost::system::error_code ec = boost::asio::error::would_block; - m_deadline.expires_from_now(std::chrono::milliseconds(2000)); + m_deadline.expires_after(std::chrono::milliseconds(2000)); m_ssl_socket->async_shutdown(boost::lambda::var(ec) = boost::lambda::_1); while (ec == boost::asio::error::would_block) { - m_io_service.reset(); + m_io_service.restart(); m_io_service.run_one(); } // Ignore "short read" error @@ -676,7 +676,7 @@ namespace net_utils } protected: - boost::asio::io_service m_io_service; + boost::asio::io_context m_io_service; boost::asio::ssl::context m_ctx; std::shared_ptr> m_ssl_socket; std::function m_connector; @@ -688,119 +688,6 @@ namespace net_utils std::atomic m_bytes_sent; std::atomic m_bytes_received; }; - - - /************************************************************************/ - /* */ - /************************************************************************/ - class async_blocked_mode_client: public blocked_mode_client - { - public: - async_blocked_mode_client():m_send_deadline(blocked_mode_client::m_io_service) - { - - // No deadline is required until the first socket operation is started. We - // set the deadline to positive infinity so that the actor takes no action - // until a specific deadline is set. - m_send_deadline.expires_at(boost::posix_time::pos_infin); - - // Start the persistent actor that checks for deadline expiry. - check_send_deadline(); - } - ~async_blocked_mode_client() - { - m_send_deadline.cancel(); - } - - bool shutdown() - { - blocked_mode_client::shutdown(); - m_send_deadline.cancel(); - return true; - } - - inline - bool send(const void* data, size_t sz) - { - try - { - /* - m_send_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout)); - - // Set up the variable that receives the result of the asynchronous - // operation. The error code is set to would_block to signal that the - // operation is incomplete. Asio guarantees that its asynchronous - // operations will never fail with would_block, so any other value in - // ec indicates completion. - boost::system::error_code ec = boost::asio::error::would_block; - - // Start the asynchronous operation itself. The boost::lambda function - // object is used as a callback and will update the ec variable when the - // operation completes. The blocking_udp_client.cpp example shows how you - // can use boost::bind rather than boost::lambda. - boost::asio::async_write(m_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1); - - // Block until the asynchronous operation has completed. - while(ec == boost::asio::error::would_block) - { - m_io_service.run_one(); - }*/ - - boost::system::error_code ec; - - size_t writen = write(data, sz, ec); - - if (!writen || ec) - { - LOG_PRINT_L3("Problems at write: " << ec.message()); - return false; - }else - { - m_send_deadline.expires_at(boost::posix_time::pos_infin); - } - } - - catch(const boost::system::system_error& er) - { - LOG_ERROR("Some problems at connect, message: " << er.what()); - return false; - } - catch(...) - { - LOG_ERROR("Some fatal problems."); - return false; - } - - return true; - } - - - private: - - boost::asio::deadline_timer m_send_deadline; - - void check_send_deadline() - { - // Check whether the deadline has passed. We compare the deadline against - // the current time since a new asynchronous operation may have moved the - // deadline before this actor had a chance to run. - if (m_send_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now()) - { - // The deadline has passed. The socket is closed so that any outstanding - // asynchronous operations are cancelled. This allows the blocked - // connect(), read_line() or write_line() functions to return. - LOG_PRINT_L3("Timed out socket"); - m_ssl_socket->next_layer().close(); - - // There is no longer an active deadline. The expiry is set to positive - // infinity so that the actor takes no action until a new deadline is set. - m_send_deadline.expires_at(boost::posix_time::pos_infin); - } - - // Put the actor back to sleep. - m_send_deadline.async_wait(boost::bind(&async_blocked_mode_client::check_send_deadline, this)); - } - }; } } diff --git a/contrib/epee/include/net/net_ssl.h b/contrib/epee/include/net/net_ssl.h index c79a3acc1..f6376c72c 100644 --- a/contrib/epee/include/net/net_ssl.h +++ b/contrib/epee/include/net/net_ssl.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -125,6 +126,7 @@ namespace net_utils \note It is strongly encouraged that clients using `system_ca` verification provide a non-empty `host` for rfc2818 verification. + \param io_context associated with `socket`. \param socket Used in SSL handshake and verification \param type Client or server \param host This parameter is only used when @@ -136,6 +138,7 @@ namespace net_utils \return True if the SSL handshake completes with peer verification settings. */ bool handshake( + boost::asio::io_context& io_context, boost::asio::ssl::stream &socket, boost::asio::ssl::stream_base::handshake_type type, boost::asio::const_buffer buffer = {}, diff --git a/contrib/epee/include/net/net_utils_base.h b/contrib/epee/include/net/net_utils_base.h index 722206ee1..2cf6e795d 100644 --- a/contrib/epee/include/net/net_utils_base.h +++ b/contrib/epee/include/net/net_utils_base.h @@ -30,7 +30,7 @@ #define _NET_UTILS_BASE_H_ #include -#include +#include #include #include #include @@ -47,10 +47,12 @@ #define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(((uint32_t)a4)<<24)) #endif +/* Use the below function carefully. The executor and io_context are slightly + different concepts. */ #if BOOST_VERSION >= 107000 -#define GET_IO_SERVICE(s) ((boost::asio::io_context&)(s).get_executor().context()) + #define MONERO_GET_EXECUTOR(type) type . get_executor() #else -#define GET_IO_SERVICE(s) ((s).get_io_service()) + #define MONERO_GET_EXECUTOR(type) type . get_io_context() #endif namespace net @@ -443,7 +445,7 @@ namespace net_utils virtual bool send_done()=0; virtual bool call_run_once_service_io()=0; virtual bool request_callback()=0; - virtual boost::asio::io_service& get_io_service()=0; + virtual boost::asio::io_context& get_io_context()=0; //protect from deletion connection object(with protocol instance) during external call "invoke" virtual bool add_ref()=0; virtual bool release()=0; diff --git a/contrib/epee/include/net/network_throttle-detail.hpp b/contrib/epee/include/net/network_throttle-detail.hpp index 0a6dc4a20..3a88105c7 100644 --- a/contrib/epee/include/net/network_throttle-detail.hpp +++ b/contrib/epee/include/net/network_throttle-detail.hpp @@ -46,13 +46,13 @@ namespace net_utils class network_throttle : public i_network_throttle { - private: + public: struct packet_info { size_t m_size; // octets sent. Summary for given small-window (e.g. for all packaged in 1 second) packet_info(); }; - + private: network_speed_bps m_target_speed; size_t m_network_add_cost; // estimated add cost of headers size_t m_network_minimal_segment; // estimated minimal cost of sending 1 byte to round up to diff --git a/contrib/epee/include/serialization/keyvalue_serialization.h b/contrib/epee/include/serialization/keyvalue_serialization.h index 06d74329f..fbbddc7d2 100644 --- a/contrib/epee/include/serialization/keyvalue_serialization.h +++ b/contrib/epee/include/serialization/keyvalue_serialization.h @@ -98,16 +98,18 @@ public: \ #define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name) \ epee::serialization::selector::serialize_t_val_as_blob(this_ref.varialble, stg, hparent_section, val_name); -#define KV_SERIALIZE_VAL_POD_AS_BLOB_N(varialble, val_name) \ - static_assert(std::is_pod::value, "t_type must be a POD type."); \ - KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name) +#define KV_SERIALIZE_VAL_POD_AS_BLOB_N(variable, val_name) \ + static_assert(std::is_trivially_copyable(), "t_type must be a trivially copyable type."); \ + static_assert(std::is_standard_layout(), "t_type must be a standard layout type."); \ + KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(variable, val_name) -#define KV_SERIALIZE_VAL_POD_AS_BLOB_OPT_N(varialble, val_name, default_value) \ +#define KV_SERIALIZE_VAL_POD_AS_BLOB_OPT_N(variable, val_name, default_value) \ do { \ - static_assert(std::is_pod::value, "t_type must be a POD type."); \ - bool ret = KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name); \ + static_assert(std::is_trivially_copyable(), "t_type must be a trivially copyable type."); \ + static_assert(std::is_standard_layout(), "t_type must be a standard layout type."); \ + bool ret = KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(variable, val_name) \ if (!ret) \ - epee::serialize_default(this_ref.varialble, default_value); \ + epee::serialize_default(this_ref.variable, default_value); \ } while(0); #define KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, val_name) \ @@ -118,7 +120,7 @@ public: \ #define KV_SERIALIZE(varialble) KV_SERIALIZE_N(varialble, #varialble) #define KV_SERIALIZE_VAL_POD_AS_BLOB(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_N(varialble, #varialble) #define KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(varialble, def) KV_SERIALIZE_VAL_POD_AS_BLOB_OPT_N(varialble, #varialble, def) -#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, #varialble) //skip is_pod compile time check +#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, #varialble) //skip is_trivially_copyable and is_standard_layout compile time check #define KV_SERIALIZE_CONTAINER_POD_AS_BLOB(varialble) KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, #varialble) #define KV_SERIALIZE_OPT(variable,default_value) KV_SERIALIZE_OPT_N(variable, #variable, default_value) diff --git a/contrib/epee/include/span.h b/contrib/epee/include/span.h index 23bd51f8c..01dc387d6 100644 --- a/contrib/epee/include/span.h +++ b/contrib/epee/include/span.h @@ -133,17 +133,14 @@ namespace epee return {src.data(), src.size()}; } - template - constexpr bool has_padding() noexcept - { - return !std::is_standard_layout() || alignof(T) != 1; - } - //! \return Cast data from `src` as `span`. template span to_byte_span(const span src) noexcept { - static_assert(!has_padding(), "source type may have padding"); + static_assert(!std::is_empty(), "empty value types will not work -> sizeof == 1"); + static_assert(std::is_standard_layout(), "type must have standard layout"); + static_assert(std::is_trivially_copyable(), "type must be trivially copyable"); + static_assert(alignof(T) == 1, "type may have padding"); return {reinterpret_cast(src.data()), src.size_bytes()}; } @@ -153,7 +150,9 @@ namespace epee { using value_type = typename T::value_type; static_assert(!std::is_empty(), "empty value types will not work -> sizeof == 1"); - static_assert(!has_padding(), "source value type may have padding"); + static_assert(std::is_standard_layout(), "value type must have standard layout"); + static_assert(std::is_trivially_copyable(), "value type must be trivially copyable"); + static_assert(alignof(value_type) == 1, "value type may have padding"); return {reinterpret_cast(src.data()), src.size() * sizeof(value_type)}; } @@ -162,7 +161,9 @@ namespace epee span as_byte_span(const T& src) noexcept { static_assert(!std::is_empty(), "empty types will not work -> sizeof == 1"); - static_assert(!has_padding(), "source type may have padding"); + static_assert(std::is_standard_layout(), "type must have standard layout"); + static_assert(std::is_trivially_copyable(), "type must be trivially copyable"); + static_assert(alignof(T) == 1, "type may have padding"); return {reinterpret_cast(std::addressof(src)), sizeof(T)}; } @@ -171,7 +172,9 @@ namespace epee span as_mut_byte_span(T& src) noexcept { static_assert(!std::is_empty(), "empty types will not work -> sizeof == 1"); - static_assert(!has_padding(), "source type may have padding"); + static_assert(std::is_standard_layout(), "type must have standard layout"); + static_assert(std::is_trivially_copyable(), "type must be trivially copyable"); + static_assert(alignof(T) == 1, "type may have padding"); return {reinterpret_cast(std::addressof(src)), sizeof(T)}; } diff --git a/contrib/epee/include/storages/portable_storage_val_converters.h b/contrib/epee/include/storages/portable_storage_val_converters.h index 8c32481b0..9e01b4ce6 100644 --- a/contrib/epee/include/storages/portable_storage_val_converters.h +++ b/contrib/epee/include/storages/portable_storage_val_converters.h @@ -38,6 +38,7 @@ #include #include +#include #include #include diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h index dbbe1906e..8b26f49ae 100644 --- a/contrib/epee/include/string_tools.h +++ b/contrib/epee/include/string_tools.h @@ -31,6 +31,7 @@ #include "mlocker.h" #include +#include #include #include #include @@ -69,23 +70,17 @@ namespace string_tools #ifdef _WIN32 std::string get_current_module_path(); #endif - bool set_module_name_and_folder(const std::string& path_to_process_); - bool trim_left(std::string& str); - bool trim_right(std::string& str); + void set_module_name_and_folder(const std::string& path_to_process_); //---------------------------------------------------------------------------- inline std::string& trim(std::string& str) { - trim_left(str); - trim_right(str); + boost::trim(str); return str; } //---------------------------------------------------------------------------- - inline std::string trim(const std::string& str_) + inline std::string trim(const std::string& str) { - std::string str = str_; - trim_left(str); - trim_right(str); - return str; + return boost::trim_copy(str); } std::string pad_string(std::string s, size_t n, char c = ' ', bool prepend = false); @@ -94,6 +89,7 @@ namespace string_tools std::string pod_to_hex(const t_pod_type& s) { static_assert(std::is_standard_layout(), "expected standard layout type"); + static_assert(alignof(t_pod_type) == 1, "type may have padding"); return to_hex::string(as_byte_span(s)); } //---------------------------------------------------------------------------- @@ -101,6 +97,8 @@ namespace string_tools bool hex_to_pod(const boost::string_ref hex_str, t_pod_type& s) { static_assert(std::is_standard_layout(), "expected standard layout type"); + static_assert(alignof(t_pod_type) == 1, "type may have padding"); + static_assert(std::is_trivially_copyable(), "type must be trivially copyable"); return from_hex::to_buffer(as_mut_byte_span(s), hex_str); } //---------------------------------------------------------------------------- diff --git a/contrib/epee/src/abstract_http_client.cpp b/contrib/epee/src/abstract_http_client.cpp index 3ae09c90e..03f73cbbc 100644 --- a/contrib/epee/src/abstract_http_client.cpp +++ b/contrib/epee/src/abstract_http_client.cpp @@ -135,6 +135,13 @@ namespace http http::url_content parsed{}; const bool r = parse_url(address, parsed); CHECK_AND_ASSERT_MES(r, false, "failed to parse url: " << address); + if (parsed.port == 0) + { + if (parsed.schema == "http") + parsed.port = 80; + else if (parsed.schema == "https") + parsed.port = 443; + } set_server(std::move(parsed.host), std::to_string(parsed.port), std::move(user), std::move(ssl_options)); return true; } diff --git a/contrib/epee/src/byte_slice.cpp b/contrib/epee/src/byte_slice.cpp index 72aa39768..47d440dff 100644 --- a/contrib/epee/src/byte_slice.cpp +++ b/contrib/epee/src/byte_slice.cpp @@ -152,7 +152,11 @@ namespace epee { std::size_t space_needed = 0; for (const auto& source : sources) + { + if (std::numeric_limits::max() - space_needed < source.size()) + throw std::bad_alloc{}; space_needed += source.size(); + } if (space_needed) { @@ -162,9 +166,9 @@ namespace epee for (const auto& source : sources) { + assert(source.size() <= out.size()); // see check above std::memcpy(out.data(), source.data(), source.size()); - if (out.remove_prefix(source.size()) < source.size()) - throw std::bad_alloc{}; // size_t overflow on space_needed + out.remove_prefix(source.size()); } storage_ = std::move(storage); } diff --git a/contrib/epee/src/connection_basic.cpp b/contrib/epee/src/connection_basic.cpp index b0a30f47f..1fdd82c1b 100644 --- a/contrib/epee/src/connection_basic.cpp +++ b/contrib/epee/src/connection_basic.cpp @@ -46,12 +46,6 @@ // TODO: #include "net/network_throttle-detail.hpp" -#if BOOST_VERSION >= 107000 -#define GET_IO_SERVICE(s) ((boost::asio::io_context&)(s).get_executor().context()) -#else -#define GET_IO_SERVICE(s) ((s).get_io_service()) -#endif - #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "net.conn" @@ -127,12 +121,12 @@ connection_basic_pimpl::connection_basic_pimpl(const std::string &name) : m_thro int connection_basic_pimpl::m_default_tos; // methods: -connection_basic::connection_basic(boost::asio::ip::tcp::socket&& sock, std::shared_ptr state, ssl_support_t ssl_support) +connection_basic::connection_basic(boost::asio::io_context &io_context, boost::asio::ip::tcp::socket&& sock, std::shared_ptr state, ssl_support_t ssl_support) : m_state(std::move(state)), mI( new connection_basic_pimpl("peer") ), - strand_(GET_IO_SERVICE(sock)), - socket_(GET_IO_SERVICE(sock), get_context(m_state.get())), + strand_(io_context), + socket_(io_context, get_context(m_state.get())), m_want_close_connection(false), m_was_shutdown(false), m_is_multithreaded(false), @@ -152,12 +146,12 @@ connection_basic::connection_basic(boost::asio::ip::tcp::socket&& sock, std::sha _note("Spawned connection #"<m_peer_number<<" to " << remote_addr_str << " currently we have sockets count:" << m_state->sock_count); } -connection_basic::connection_basic(boost::asio::io_service &io_service, std::shared_ptr state, ssl_support_t ssl_support) +connection_basic::connection_basic(boost::asio::io_context &io_context, std::shared_ptr state, ssl_support_t ssl_support) : m_state(std::move(state)), mI( new connection_basic_pimpl("peer") ), - strand_(io_service), - socket_(io_service, get_context(m_state.get())), + strand_(io_context), + socket_(io_context, get_context(m_state.get())), m_want_close_connection(false), m_was_shutdown(false), m_is_multithreaded(false), diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp index 4ca1a3632..46b535504 100644 --- a/contrib/epee/src/mlog.cpp +++ b/contrib/epee/src/mlog.cpp @@ -176,11 +176,12 @@ void mlog_configure(const std::string &filename_base, bool console, const std::s std::vector found_files; const boost::filesystem::directory_iterator end_itr; const boost::filesystem::path filename_base_path(filename_base); + const std::string filename_base_name = filename_base_path.filename().string(); const boost::filesystem::path parent_path = filename_base_path.has_parent_path() ? filename_base_path.parent_path() : "."; for (boost::filesystem::directory_iterator iter(parent_path); iter != end_itr; ++iter) { - const std::string filename = iter->path().string(); - if (filename.size() >= filename_base.size() && std::memcmp(filename.data(), filename_base.data(), filename_base.size()) == 0) + const std::string filename = iter->path().filename().string(); + if (filename.size() >= filename_base_name.size() && std::memcmp(filename.data(), filename_base_name.data(), filename_base_name.size()) == 0) { found_files.push_back(iter->path()); } diff --git a/contrib/epee/src/net_helper.cpp b/contrib/epee/src/net_helper.cpp index 719f1c8e0..de0f1211b 100644 --- a/contrib/epee/src/net_helper.cpp +++ b/contrib/epee/src/net_helper.cpp @@ -4,22 +4,38 @@ namespace epee { namespace net_utils { + namespace + { + struct new_connection + { + boost::promise result_; + boost::asio::ip::tcp::socket socket_; + + template + explicit new_connection(T&& executor) + : result_(), socket_(std::forward(executor)) + {} + }; + } + boost::unique_future direct_connect::operator()(const std::string& addr, const std::string& port, boost::asio::steady_timer& timeout) const { // Get a list of endpoints corresponding to the server name. ////////////////////////////////////////////////////////////////////////// - boost::asio::ip::tcp::resolver resolver(GET_IO_SERVICE(timeout)); - boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), addr, port, boost::asio::ip::tcp::resolver::query::canonical_name); + boost::asio::ip::tcp::resolver resolver(MONERO_GET_EXECUTOR(timeout)); bool try_ipv6 = false; - boost::asio::ip::tcp::resolver::iterator iterator; - boost::asio::ip::tcp::resolver::iterator end; + boost::asio::ip::tcp::resolver::results_type results{}; boost::system::error_code resolve_error; + try { - iterator = resolver.resolve(query, resolve_error); - if(iterator == end) // Documentation states that successful call is guaranteed to be non-empty + results = resolver.resolve( + boost::asio::ip::tcp::v4(), addr, port, boost::asio::ip::tcp::resolver::canonical_name, resolve_error + ); + + if (results.empty()) { // if IPv4 resolution fails, try IPv6. Unintentional outgoing IPv6 connections should only // be possible if for some reason a hostname was given and that hostname fails IPv4 resolution, @@ -37,27 +53,20 @@ namespace net_utils } try_ipv6 = true; } + if (try_ipv6) { - boost::asio::ip::tcp::resolver::query query6(boost::asio::ip::tcp::v6(), addr, port, boost::asio::ip::tcp::resolver::query::canonical_name); - iterator = resolver.resolve(query6); - if (iterator == end) + results = resolver.resolve( + boost::asio::ip::tcp::v6(), addr, port, boost::asio::ip::tcp::resolver::canonical_name + ); + if (results.empty()) throw boost::system::system_error{boost::asio::error::fault, "Failed to resolve " + addr}; } ////////////////////////////////////////////////////////////////////////// - struct new_connection - { - boost::promise result_; - boost::asio::ip::tcp::socket socket_; - explicit new_connection(boost::asio::io_service& io_service) - : result_(), socket_(io_service) - {} - }; - - const auto shared = std::make_shared(GET_IO_SERVICE(timeout)); + const auto shared = std::make_shared(MONERO_GET_EXECUTOR(timeout)); timeout.async_wait([shared] (boost::system::error_code error) { if (error != boost::system::errc::operation_canceled && shared && shared->socket_.is_open()) @@ -66,7 +75,7 @@ namespace net_utils shared->socket_.close(); } }); - shared->socket_.async_connect(*iterator, [shared] (boost::system::error_code error) + shared->socket_.async_connect(*results.begin(), [shared] (boost::system::error_code error) { if (shared) { diff --git a/contrib/epee/src/net_ssl.cpp b/contrib/epee/src/net_ssl.cpp index 9200796a8..aef37844e 100644 --- a/contrib/epee/src/net_ssl.cpp +++ b/contrib/epee/src/net_ssl.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -45,6 +46,13 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "net.ssl" + +#if BOOST_VERSION >= 107300 + #define MONERO_HOSTNAME_VERIFY boost::asio::ssl::host_name_verification +#else + #define MONERO_HOSTNAME_VERIFY boost::asio::ssl::rfc2818_verification +#endif + // openssl genrsa -out /tmp/KEY 4096 // openssl req -new -key /tmp/KEY -out /tmp/REQ // openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT @@ -526,7 +534,7 @@ void ssl_options_t::configure( // preverified means it passed system or user CA check. System CA is never loaded // when fingerprints are whitelisted. const bool verified = preverified && - (verification != ssl_verification_t::system_ca || host.empty() || boost::asio::ssl::rfc2818_verification(host)(preverified, ctx)); + (verification != ssl_verification_t::system_ca || host.empty() || MONERO_HOSTNAME_VERIFY(host)(preverified, ctx)); if (!verified && !has_fingerprint(ctx)) { @@ -544,6 +552,7 @@ void ssl_options_t::configure( } bool ssl_options_t::handshake( + boost::asio::io_context& io_context, boost::asio::ssl::stream &socket, boost::asio::ssl::stream_base::handshake_type type, boost::asio::const_buffer buffer, @@ -555,12 +564,11 @@ bool ssl_options_t::handshake( auto start_handshake = [&]{ using ec_t = boost::system::error_code; using timer_t = boost::asio::steady_timer; - using strand_t = boost::asio::io_service::strand; + using strand_t = boost::asio::io_context::strand; using socket_t = boost::asio::ip::tcp::socket; - auto &io_context = GET_IO_SERVICE(socket); if (io_context.stopped()) - io_context.reset(); + io_context.restart(); strand_t strand(io_context); timer_t deadline(io_context, timeout); @@ -595,13 +603,13 @@ bool ssl_options_t::handshake( state.result = ec; if (!state.cancel_handshake) { state.cancel_timer = true; - ec_t ec; - deadline.cancel(ec); + deadline.cancel(); } }; deadline.async_wait(on_timer); - strand.post( + boost::asio::post( + strand, [&]{ socket.async_handshake( type, diff --git a/contrib/epee/src/network_throttle-detail.cpp b/contrib/epee/src/network_throttle-detail.cpp index 978572120..1e554c500 100644 --- a/contrib/epee/src/network_throttle-detail.cpp +++ b/contrib/epee/src/network_throttle-detail.cpp @@ -46,7 +46,7 @@ #include "misc_log_ex.h" #include #include "misc_language.h" -#include +#include #include #include @@ -186,6 +186,23 @@ void network_throttle::handle_trafic_exact(size_t packet_size) _handle_trafic_exact(packet_size, packet_size); } +namespace +{ + struct output_history + { + const boost::circular_buffer< network_throttle::packet_info >& history; + }; + + std::ostream& operator<<(std::ostream& out, const output_history& source) + { + out << '['; + for (auto sample: source.history) + out << sample.m_size << ' '; + out << ']'; + return out; + } +} + void network_throttle::_handle_trafic_exact(size_t packet_size, size_t orginal_size) { tick(); @@ -196,14 +213,11 @@ void network_throttle::_handle_trafic_exact(size_t packet_size, size_t orginal_s m_total_packets++; m_total_bytes += packet_size; - std::ostringstream oss; oss << "["; for (auto sample: m_history) oss << sample.m_size << " "; oss << "]" << std::ends; - std::string history_str = oss.str(); - MTRACE("Throttle " << m_name << ": packet of ~"< 0 ? "SLEEP" : "") << "dbg " << m_name << ": " << "speed is A=" << std::setw(8) <(*it));) - str.erase(str.begin()); - - return true; - } - //---------------------------------------------------------------------------- - bool trim_right(std::string& str) - { - - for(std::string::reverse_iterator it = str.rbegin(); it!= str.rend() && isspace(static_cast(*it));) - str.erase( --((it++).base())); - - return true; - } - //---------------------------------------------------------------------------- std::string pad_string(std::string s, size_t n, char c, bool prepend) { if (s.size() < n) @@ -209,28 +186,22 @@ namespace string_tools return s; } - std::string get_extension(const std::string& str) - { - std::string res; - std::string::size_type pos = str.rfind('.'); - if(std::string::npos == pos) - return res; - - res = str.substr(pos+1, str.size()-pos); - return res; - } - //---------------------------------------------------------------------------- - std::string cut_off_extension(const std::string& str) - { - std::string res; - std::string::size_type pos = str.rfind('.'); - if(std::string::npos == pos) - return str; + std::string get_extension(const std::string& str) + { + std::string ext_with_dot = boost::filesystem::path(str).extension().string(); + + if (ext_with_dot.empty()) + return {}; + + return ext_with_dot.erase(0, 1); + } + + //---------------------------------------------------------------------------- + std::string cut_off_extension(const std::string& str) + { + return boost::filesystem::path(str).replace_extension("").string(); + } - res = str.substr(0, pos); - return res; - } - //---------------------------------------------------------------------------- #ifdef _WIN32 std::wstring utf8_to_utf16(const std::string& str) { diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index f9b5d3b80..b76cf069b 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -29,14 +29,18 @@ #include "db_lmdb.h" #include +#include #include #include #include // std::unique_ptr #include // memcpy +#ifdef WIN32 +#include +#endif + #include "string_tools.h" -#include "file_io_utils.h" #include "common/util.h" #include "common/pruning.h" #include "cryptonote_basic/cryptonote_format_utils.h" @@ -2083,6 +2087,54 @@ BlockchainLMDB::BlockchainLMDB(bool batch_transactions): BlockchainDB() m_hardfork = nullptr; } +#ifdef WIN32 +static bool disable_ntfs_compression(const boost::filesystem::path& filepath) +{ + DWORD file_attributes = ::GetFileAttributesW(filepath.c_str()); + if (file_attributes == INVALID_FILE_ATTRIBUTES) + { + MERROR("Failed to get " << filepath.string() << " file attributes. Error: " << ::GetLastError()); + return false; + } + + if (!(file_attributes & FILE_ATTRIBUTE_COMPRESSED)) + return true; // not compressed + + LOG_PRINT_L1("Disabling NTFS compression for " << filepath.string()); + HANDLE file_handle = ::CreateFileW( + filepath.c_str(), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + nullptr, + OPEN_EXISTING, + boost::filesystem::is_directory(filepath) ? FILE_FLAG_BACKUP_SEMANTICS : 0, // Needed to open handles to directories + nullptr + ); + + if (file_handle == INVALID_HANDLE_VALUE) + { + MERROR("Failed to open handle: " << filepath.string() << ". Error: " << ::GetLastError()); + return false; + } + + USHORT compression_state = COMPRESSION_FORMAT_NONE; + DWORD bytes_returned; + BOOL ok = ::DeviceIoControl( + file_handle, + FSCTL_SET_COMPRESSION, + &compression_state, + sizeof(compression_state), + nullptr, + 0, + &bytes_returned, + nullptr + ); + + ::CloseHandle(file_handle); + return ok; +} +#endif + void BlockchainLMDB::open(const std::string& filename, const int db_flags) { int result; @@ -2109,6 +2161,18 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) throw DB_ERROR("Database could not be opened"); } +#ifdef WIN32 + // ensure NTFS compression is disabled on the directory and database file to avoid corruption of the blockchain + if (!disable_ntfs_compression(filename)) + LOG_PRINT_L0("Failed to disable NTFS compression on folder: " << filename << ". Error: " << ::GetLastError()); + boost::filesystem::path datafile(filename); + datafile /= CRYPTONOTE_BLOCKCHAINDATA_FILENAME; + if (!boost::filesystem::exists(datafile)) + boost::filesystem::ofstream(datafile).close(); // create the file to see if NTFS compression is enabled beforehand + if (!disable_ntfs_compression(datafile)) + throw DB_ERROR("Database file is NTFS compressed and compression could not be disabled"); +#endif + boost::optional is_hdd_result = tools::is_hdd(filename.c_str()); if (is_hdd_result) { @@ -5472,12 +5536,11 @@ bool BlockchainLMDB::is_read_only() const uint64_t BlockchainLMDB::get_database_size() const { - uint64_t size = 0; boost::filesystem::path datafile(m_folder); datafile /= CRYPTONOTE_BLOCKCHAINDATA_FILENAME; - if (!epee::file_io_utils::get_file_size(datafile.string(), size)) - size = 0; - return size; + boost::system::error_code ec{}; + const boost::uintmax_t size = boost::filesystem::file_size(datafile, ec); + return (ec ? 0 : static_cast(size)); } #define RENAME_DB(name) do { \ diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index 07a8042d4..495677d7a 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -142,22 +142,20 @@ set(blockchain_scanner_private_headers) monero_private_headers(blockchain_scanner ${blockchain_scanner_private_headers}) -if (BUILD_AUDIT) - if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp" AND NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp") - set(blockchain_audit_sources - blockchain_audit.cpp - threadpool_boost.cpp - ) +if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp" AND NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp") + set(blockchain_audit_sources + blockchain_audit.cpp + threadpool_boost.cpp + ) - set(blockchain_audit_private_headers - threadpool_boost.h - ) + set(blockchain_audit_private_headers + threadpool_boost.h + ) - monero_private_headers(blockchain_audit - ${blockchain_audit_private_headers}) - else() - message(STATUS "blockchain_audit.cpp not found - not building the audit tool") - endif() + monero_private_headers(blockchain_audit + ${blockchain_audit_private_headers}) +else() + message(STATUS "blockchain_audit.cpp not found - not building the audit tool") endif() monero_add_executable(blockchain_import @@ -329,35 +327,33 @@ set_property(TARGET blockchain_scanner OUTPUT_NAME "salvium-blockchain-scanner") install(TARGETS blockchain_scanner DESTINATION bin) -if (BUILD_AUDIT) - if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp" AND NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp") - monero_add_executable(blockchain_audit - ${blockchain_audit_sources} - ${blockchain_audit_private_headers}) +if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp" AND NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/blockchain_audit.cpp") + monero_add_executable(blockchain_audit + ${blockchain_audit_sources} + ${blockchain_audit_private_headers}) - target_include_directories(blockchain_audit PRIVATE /usr/include/mysql-cppconn/jdbc) + target_include_directories(blockchain_audit PRIVATE /usr/include/mysql-cppconn/jdbc) - target_link_libraries(blockchain_audit - PRIVATE - wallet - crypto - cncrypto - cryptonote_core - blockchain_db - version - epee - mysqlcppconn - ${Boost_FILESYSTEM_LIBRARY} - ${Boost_SYSTEM_LIBRARY} - ${Boost_THREAD_LIBRARY} - ${CMAKE_THREAD_LIBS_INIT} - ${EXTRA_LIBRARIES}) + target_link_libraries(blockchain_audit + PRIVATE + wallet + crypto + cncrypto + cryptonote_core + blockchain_db + version + epee + mysqlcppconn + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_SYSTEM_LIBRARY} + ${Boost_THREAD_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT} + ${EXTRA_LIBRARIES}) - set_property(TARGET blockchain_audit - PROPERTY - OUTPUT_NAME "salvium-blockchain-audit") - install(TARGETS blockchain_audit DESTINATION bin) - endif() + set_property(TARGET blockchain_audit + PROPERTY + OUTPUT_NAME "salvium-blockchain-audit") + install(TARGETS blockchain_audit DESTINATION bin) endif() monero_add_executable(blockchain_stats diff --git a/src/common/download.cpp b/src/common/download.cpp index 01d4a9aab..e377be266 100644 --- a/src/common/download.cpp +++ b/src/common/download.cpp @@ -30,7 +30,6 @@ #include #include #include -#include "file_io_utils.h" #include "net/http_client.h" #include "download.h" @@ -73,8 +72,11 @@ namespace tools { boost::unique_lock lock(control->mutex); std::ios_base::openmode mode = std::ios_base::out | std::ios_base::binary; - uint64_t existing_size = 0; - if (epee::file_io_utils::get_file_size(control->path, existing_size) && existing_size > 0) + boost::system::error_code ec{}; + uint64_t existing_size = static_cast(boost::filesystem::file_size(control->path, ec)); + if (ec) + existing_size = 0; + if (existing_size > 0) { MINFO("Resuming downloading " << control->uri << " to " << control->path << " from " << existing_size); mode |= std::ios_base::app; diff --git a/src/common/password.cpp b/src/common/password.cpp index e6dff95ea..c0edf7a78 100644 --- a/src/common/password.cpp +++ b/src/common/password.cpp @@ -185,7 +185,7 @@ namespace return false; if (verify) { - std::cout << "Confirm password: "; + std::cout << "Confirm password: " << std::flush; if (!read_from_tty(pass2, hide_input)) return false; if(pass1!=pass2) diff --git a/src/common/stack_trace.cpp b/src/common/stack_trace.cpp index 130ba4d81..1f946ddef 100644 --- a/src/common/stack_trace.cpp +++ b/src/common/stack_trace.cpp @@ -34,6 +34,7 @@ #include "easylogging++/easylogging++.h" #include +#include #ifdef USE_UNWIND #define UNW_LOCAL_ONLY #include diff --git a/src/common/util.cpp b/src/common/util.cpp index 05ddfc359..acb01cafb 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -664,7 +664,7 @@ namespace tools } boost::system::error_code ec; - const auto parsed_ip = boost::asio::ip::address::from_string(u_c.host, ec); + const auto parsed_ip = boost::asio::ip::make_address(u_c.host, ec); if (ec) { MDEBUG("Failed to parse '" << address << "' as IP address: " << ec.message() << ". Considering it not local"); return false; diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt index 102ffe41a..595c7f966 100644 --- a/src/crypto/CMakeLists.txt +++ b/src/crypto/CMakeLists.txt @@ -71,7 +71,7 @@ target_link_libraries(cncrypto epee randomx ${Boost_SYSTEM_LIBRARY} - ${sodium_LIBRARIES} + ${SODIUM_LIBRARY} PRIVATE ${EXTRA_LIBRARIES}) diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 925201782..78529f18d 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -172,8 +172,8 @@ namespace crypto { */ template T rand() { - static_assert(std::is_standard_layout_v, "cannot write random bytes into non-standard layout type"); - static_assert(std::is_trivially_copyable_v, "cannot write random bytes into non-trivially copyable type"); + static_assert(std::is_standard_layout(), "cannot write random bytes into non-standard layout type"); + static_assert(std::is_trivially_copyable(), "cannot write random bytes into non-trivially copyable type"); typename std::remove_cv::type res; generate_random_bytes_thread_safe(sizeof(T), (uint8_t*)&res); return res; diff --git a/src/crypto/generic-ops.h b/src/crypto/generic-ops.h index 5a5e09f9b..3ff3619fe 100644 --- a/src/crypto/generic-ops.h +++ b/src/crypto/generic-ops.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #define CRYPTO_MAKE_COMPARABLE(type) \ @@ -60,14 +61,18 @@ namespace crypto { \ namespace crypto { \ static_assert(sizeof(std::size_t) <= sizeof(type), "Size of " #type " must be at least that of size_t"); \ inline std::size_t hash_value(const type &_v) { \ - return reinterpret_cast(_v); \ + std::size_t h; \ + memcpy(&h, std::addressof(_v), sizeof(h)); \ + return h; \ } \ } \ namespace std { \ template<> \ struct hash { \ std::size_t operator()(const crypto::type &_v) const { \ - return reinterpret_cast(_v); \ + std::size_t h; \ + memcpy(&h, std::addressof(_v), sizeof(h)); \ + return h; \ } \ }; \ } diff --git a/src/cryptonote_basic/connection_context.cpp b/src/cryptonote_basic/connection_context.cpp index 4395bad9f..642749f3c 100644 --- a/src/cryptonote_basic/connection_context.cpp +++ b/src/cryptonote_basic/connection_context.cpp @@ -29,6 +29,7 @@ #include "connection_context.h" +#include #include "cryptonote_protocol/cryptonote_protocol_defs.h" #include "p2p/p2p_protocol_defs.h" @@ -69,4 +70,23 @@ namespace cryptonote }; return std::numeric_limits::max(); } + + void cryptonote_connection_context::set_state_normal() + { + m_state = state_normal; + m_expected_heights_start = 0; + m_needed_objects.clear(); + m_needed_objects.shrink_to_fit(); + m_expected_heights.clear(); + m_expected_heights.shrink_to_fit(); + m_requested_objects.clear(); + } + + boost::optional cryptonote_connection_context::get_expected_hash(const uint64_t height) const + { + const auto difference = height - m_expected_heights_start; + if (height < m_expected_heights_start || m_expected_heights.size() < difference) + return boost::none; + return m_expected_heights[difference]; + } } // cryptonote diff --git a/src/cryptonote_basic/connection_context.h b/src/cryptonote_basic/connection_context.h index 818999a60..db24e28fa 100644 --- a/src/cryptonote_basic/connection_context.h +++ b/src/cryptonote_basic/connection_context.h @@ -34,6 +34,7 @@ #include #include #include +#include #include "net/net_utils_base.h" #include "crypto/hash.h" @@ -42,7 +43,7 @@ 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_expected_heights_start(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_height(0), m_num_requested(0) {} @@ -92,11 +93,18 @@ namespace cryptonote //! \return Maximum number of bytes permissible for `command`. static size_t get_max_bytes(int command) noexcept; + //! Use this instead of `m_state = state_normal`. + void set_state_normal(); + + boost::optional get_expected_hash(uint64_t height) const; + state m_state; std::vector> m_needed_objects; + std::vector m_expected_heights; std::unordered_set m_requested_objects; uint64_t m_remote_blockchain_height; uint64_t m_last_response_height; + uint64_t m_expected_heights_start; boost::posix_time::ptime m_last_request_time; copyable_atomic m_callback_request_count; //in debug purpose: problem with double callback rise crypto::hash m_last_known_hash; diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index f784bcdb6..68dda7c83 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -138,6 +138,10 @@ #define COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT 1000 #define COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT 20000 +#define DEFAULT_RPC_MAX_CONNECTIONS_PER_PUBLIC_IP 3 +#define DEFAULT_RPC_MAX_CONNECTIONS_PER_PRIVATE_IP 25 +#define DEFAULT_RPC_MAX_CONNECTIONS 100 +#define DEFAULT_RPC_SOFT_LIMIT_SIZE 25 * 1024 * 1024 // 25 MiB #define MAX_RPC_CONTENT_LENGTH 1048576 // 1 MB #define P2P_LOCAL_WHITE_PEERLIST_LIMIT 1000 diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index b7d447af0..334a72739 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -378,9 +379,9 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline // create general purpose async service queue - m_async_work_idle = std::unique_ptr < boost::asio::io_service::work > (new boost::asio::io_service::work(m_async_service)); + m_async_work_idle = std::make_unique>(m_async_service.get_executor()); // we only need 1 - m_async_pool.create_thread(boost::bind(&boost::asio::io_service::run, &m_async_service)); + m_async_pool.create_thread(boost::bind(&boost::asio::io_context::run, &m_async_service)); #if defined(PER_BLOCK_CHECKPOINT) if (m_nettype != FAKECHAIN) @@ -854,20 +855,12 @@ bool Blockchain::get_block_by_hash(const crypto::hash &h, block &blk, bool *orph // less blocks than desired if there aren't enough. difficulty_type Blockchain::get_difficulty_for_next_block() { - LOG_PRINT_L3("Blockchain::" << __func__); - - std::stringstream ss; - bool print = false; - - int done = 0; - ss << "get_difficulty_for_next_block: height " << m_db->height() << std::endl; if (m_fixed_difficulty) { return m_db->height() ? m_fixed_difficulty : 1; } -start: - difficulty_type D = 0; + LOG_PRINT_L3("Blockchain::" << __func__); crypto::hash top_hash = get_tail_id(); { @@ -876,12 +869,8 @@ start: // something a bit out of date, but that's fine since anything which // requires the blockchain lock will have acquired it in the first place, // and it will be unlocked only when called from the getinfo RPC - ss << "Locked, tail id " << top_hash << ", cached is " << m_difficulty_for_next_block_top_hash << std::endl; if (top_hash == m_difficulty_for_next_block_top_hash) - { - ss << "Same, using cached diff " << m_difficulty_for_next_block << std::endl; - D = m_difficulty_for_next_block; - } + return m_difficulty_for_next_block; } CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -899,16 +888,11 @@ start: difficulty_blocks_count = DIFFICULTY_BLOCKS_COUNT_V2; } - if (!(new_top_hash == top_hash)) D=0; - ss << "Re-locked, height " << height << ", tail id " << new_top_hash << (new_top_hash == top_hash ? "" : " (different)") << std::endl; - top_hash = new_top_hash; - // ND: Speedup // 1. Keep a list of the last 735 (or less) blocks that is used to compute difficulty, // then when the next block difficulty is queried, push the latest height data and // pop the oldest one from the list. This only requires 1x read per height instead // of doing 735 (DIFFICULTY_BLOCKS_COUNT). - bool check = false; if (m_reset_timestamps_and_difficulties_height) m_timestamps_and_difficulties_height = 0; if (m_timestamps_and_difficulties_height != 0 && ((height - m_timestamps_and_difficulties_height) == 1) && m_timestamps.size() >= difficulty_blocks_count) @@ -925,12 +909,8 @@ start: m_timestamps_and_difficulties_height = height; timestamps = m_timestamps; difficulties = m_difficulties; - check = true; } - //else - std::vector timestamps_from_cache = timestamps; - std::vector difficulties_from_cache = difficulties; - + else { uint64_t offset = height - std::min (height, static_cast(difficulty_blocks_count)); if (offset == 0) @@ -943,40 +923,16 @@ start: timestamps.reserve(height - offset); difficulties.reserve(height - offset); } - ss << "Looking up " << (height - offset) << " from " << offset << std::endl; for (; offset < height; offset++) { timestamps.push_back(m_db->get_block_timestamp(offset)); difficulties.push_back(m_db->get_block_cumulative_difficulty(offset)); } - if (check) if (timestamps != timestamps_from_cache || difficulties !=difficulties_from_cache) - { - ss << "Inconsistency XXX:" << std::endl; - ss << "top hash: "<height(); - uint64_t sh = dbh < 10000 ? 0 : dbh - 10000; - ss << "History from -10k at :" << dbh << ", from " << sh << std::endl; - for (uint64_t h = sh; h < dbh; ++h) - { - uint64_t ts = m_db->get_block_timestamp(h); - difficulty_type d = m_db->get_block_cumulative_difficulty(h); - ss << " " << h << " " << ts << " " << d << std::endl; - } - print = true; - } m_timestamps_and_difficulties_height = height; m_timestamps = timestamps; m_difficulties = difficulties; } - size_t target = get_difficulty_target(); difficulty_type diff; if (version == 1) { @@ -988,28 +944,6 @@ start: CRITICAL_REGION_LOCAL1(m_difficulty_lock); m_difficulty_for_next_block_top_hash = top_hash; m_difficulty_for_next_block = diff; - if (D && D != diff) - { - ss << "XXX Mismatch at " << height << "/" << top_hash << "/" << get_tail_id() << ": cached " << D << ", real " << diff << std::endl; - print = true; - } - - ++done; - if (done == 1 && D && D != diff) - { - print = true; - ss << "Might be a race. Let's see what happens if we try again..." << std::endl; - epee::misc_utils::sleep_no_w(100); - goto start; - } - ss << "Diff for " << top_hash << ": " << diff << std::endl; - if (print) - { - MGINFO("START DUMP"); - MGINFO(ss.str()); - MGINFO("END DUMP"); - MGINFO("Please send moneromooo on Libera.Chat the contents of this log, from a couple dozen lines before START DUMP to END DUMP"); - } return diff; } //------------------------------------------------------------------ @@ -1250,12 +1184,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list // just the latter (because the rollback was done above). rollback_blockchain_switching(disconnected_chain, split_height); - // FIXME: Why do we keep invalid blocks around? Possibly in case we hear - // about them again so we can immediately dismiss them, but needs some - // looking into. const crypto::hash blkid = cryptonote::get_block_hash(bei.bl); - add_block_as_invalid(bei, blkid); - MERROR("The block was inserted as invalid while connecting new alternative chain, block_id: " << blkid); m_db->remove_alt_block(blkid); alt_ch_iter++; @@ -1263,7 +1192,6 @@ bool Blockchain::switch_to_alternative_blockchain(std::list { const auto &bei = *alt_ch_to_orph_iter++; const crypto::hash blkid = cryptonote::get_block_hash(bei.bl); - add_block_as_invalid(bei, blkid); m_db->remove_alt_block(blkid); } return false; @@ -2753,7 +2681,11 @@ void Blockchain::get_output_key_mask_unlocked(const uint64_t& amount, const uint //------------------------------------------------------------------ bool Blockchain::get_output_distribution(uint64_t amount, std::string asset_type, uint64_t from_height, uint64_t to_height, uint64_t &start_height, std::vector &distribution, uint64_t &base, uint64_t &num_spendable_global_outs) const { - start_height = 0; + // rct outputs don't exist before v4 + if (amount == 0 && m_nettype != network_type::FAKECHAIN) + start_height = m_hardfork->get_earliest_ideal_height_for_version(HF_VERSION_DYNAMIC_FEE); + else + start_height = 0; base = 0; num_spendable_global_outs = 0; @@ -5567,7 +5499,7 @@ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync) { m_sync_counter = 0; m_bytes_to_sync = 0; - m_async_service.dispatch(boost::bind(&Blockchain::store_blockchain, this)); + boost::asio::dispatch(m_async_service, boost::bind(&Blockchain::store_blockchain, this)); } else if(m_db_sync_mode == db_sync) { diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 57f71b212..748746391 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -29,7 +29,8 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #pragma once -#include +#include +#include #include #if BOOST_VERSION >= 107400 #include @@ -1280,9 +1281,9 @@ namespace cryptonote crypto::hash m_difficulty_for_next_block_top_hash; difficulty_type m_difficulty_for_next_block; - boost::asio::io_service m_async_service; + boost::asio::io_context m_async_service; boost::thread_group m_async_pool; - std::unique_ptr m_async_work_idle; + std::unique_ptr> m_async_work_idle; // some invalid blocks blocks_ext_by_hash m_invalid_blocks; // crypto::hash -> block_extended_info diff --git a/src/cryptonote_protocol/block_queue.cpp b/src/cryptonote_protocol/block_queue.cpp index 4e65eafa4..f8962df06 100644 --- a/src/cryptonote_protocol/block_queue.cpp +++ b/src/cryptonote_protocol/block_queue.cpp @@ -40,15 +40,6 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "cn.block_queue" -namespace std { - static_assert(sizeof(size_t) <= sizeof(boost::uuids::uuid), "boost::uuids::uuid too small"); - template<> struct hash { - std::size_t operator()(const boost::uuids::uuid &_v) const { - return reinterpret_cast(_v); - } - }; -} - namespace cryptonote { @@ -60,10 +51,10 @@ void block_queue::add_blocks(uint64_t height, std::vector lock(mutex); + const auto elem = have_blocks.find(hash); + if (elem == have_blocks.end()) + return std::numeric_limits::max(); + return elem->second; +} + + std::pair block_queue::reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, const epee::net_utils::network_address &addr, bool sync_pruned_blocks, uint32_t local_pruning_seed, uint32_t pruning_seed, uint64_t blockchain_height, const std::vector> &block_hashes, boost::posix_time::ptime time) { boost::unique_lock lock(mutex); @@ -472,7 +473,7 @@ bool block_queue::has_spans(const boost::uuids::uuid &connection_id) const float block_queue::get_speed(const boost::uuids::uuid &connection_id) const { boost::unique_lock lock(mutex); - std::unordered_map speeds; + std::unordered_map> speeds; for (const auto &span: blocks) { if (span.blocks.empty()) @@ -480,7 +481,7 @@ float block_queue::get_speed(const boost::uuids::uuid &connection_id) const // note that the average below does not average over the whole set, but over the // previous pseudo average and the latest rate: this gives much more importance // to the latest measurements, which is fine here - std::unordered_map::iterator i = speeds.find(span.connection_id); + const auto i = speeds.find(span.connection_id); if (i == speeds.end()) speeds.insert(std::make_pair(span.connection_id, span.rate)); else diff --git a/src/cryptonote_protocol/block_queue.h b/src/cryptonote_protocol/block_queue.h index 64ff106a3..df64948fa 100644 --- a/src/cryptonote_protocol/block_queue.h +++ b/src/cryptonote_protocol/block_queue.h @@ -98,6 +98,7 @@ namespace cryptonote bool foreach(std::function f) const; bool requested(const crypto::hash &hash) const; bool have(const crypto::hash &hash) const; + std::uint64_t have_height(const crypto::hash &hash) const; private: void erase_block(block_map::iterator j); @@ -107,6 +108,6 @@ namespace cryptonote block_map blocks; mutable boost::recursive_mutex mutex; std::unordered_set requested_hashes; - std::unordered_set have_blocks; + std::unordered_map have_blocks; }; } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index 80582fad2..ed0b0e1e9 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -158,6 +158,7 @@ namespace cryptonote bool should_ask_for_pruned_data(cryptonote_connection_context& context, uint64_t first_block_height, uint64_t nblocks, bool check_block_weights) const; void drop_connection(cryptonote_connection_context &context, bool add_fail, bool flush_all_spans); void drop_connection_with_score(cryptonote_connection_context &context, unsigned int score, bool flush_all_spans); + void drop_connection(const boost::uuids::uuid&); void drop_connections(const epee::net_utils::network_address address); bool kick_idle_peers(); bool check_standby_peers(); diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 5ee533563..4cba900a8 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -35,6 +35,7 @@ // (may contain code and/or modifications by other developers) // developer rfree: this code is caller of our new network code, and is modded; e.g. for rate limiting +#include #include #include @@ -380,7 +381,7 @@ namespace cryptonote if(m_core.have_block(hshd.top_id)) { - context.m_state = cryptonote_connection_context::state_normal; + context.set_state_normal(); if(is_inital && hshd.current_height >= target && target == m_core.get_current_blockchain_height()) on_connection_synchronized(); return true; @@ -389,7 +390,7 @@ namespace cryptonote // No chain synchronization over hidden networks (tor, i2p, etc.) if(context.m_remote_address.get_zone() != epee::net_utils::zone::public_) { - context.m_state = cryptonote_connection_context::state_normal; + context.set_state_normal(); return true; } @@ -430,7 +431,7 @@ namespace cryptonote if (m_no_sync) { - context.m_state = cryptonote_connection_context::state_normal; + context.set_state_normal(); return true; } @@ -1198,8 +1199,9 @@ namespace cryptonote block_hashes.reserve(arg.blocks.size()); const boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time(); uint64_t start_height = std::numeric_limits::max(); + crypto::hash previous{}; cryptonote::block b; - for(const block_complete_entry& block_entry: arg.blocks) + for(std::size_t i = 0; i < arg.blocks.size(); ++i) { if (m_stopping) { @@ -1207,10 +1209,10 @@ namespace cryptonote } crypto::hash block_hash; - if(!parse_and_validate_block_from_blob(block_entry.block, b, block_hash)) + if(!parse_and_validate_block_from_blob(arg.blocks[i].block, b, block_hash)) { LOG_ERROR_CCONTEXT("sent wrong block: failed to parse and validate block: " - << epee::string_tools::buff_to_hex_nodelimer(block_entry.block) << ", dropping connection"); + << epee::string_tools::buff_to_hex_nodelimer(arg.blocks[i].block) << ", dropping connection"); drop_connection(context, false, false); ++m_sync_bad_spans_downloaded; return 1; @@ -1218,14 +1220,25 @@ namespace cryptonote if (b.miner_tx.vin.size() != 1 || b.miner_tx.vin.front().type() != typeid(txin_gen)) { LOG_ERROR_CCONTEXT("sent wrong block: block: miner tx does not have exactly one txin_gen input" - << epee::string_tools::buff_to_hex_nodelimer(block_entry.block) << ", dropping connection"); + << epee::string_tools::buff_to_hex_nodelimer(arg.blocks[i].block) << ", dropping connection"); drop_connection(context, false, false); ++m_sync_bad_spans_downloaded; return 1; } + + const auto this_height = boost::get(b.miner_tx.vin[0]).height; + if (context.get_expected_hash(this_height) != block_hash) + { + LOG_ERROR_CCONTEXT("Sent invalid chain"); + drop_connection(context, false, false); + ++m_sync_bad_spans_downloaded; + return 1; + } + + // if first block if (start_height == std::numeric_limits::max()) { - start_height = boost::get(b.miner_tx.vin[0]).height; + start_height = this_height; if (start_height > context.m_expect_height) { LOG_ERROR_CCONTEXT("sent block ahead of expected height, dropping connection"); @@ -1233,21 +1246,45 @@ namespace cryptonote ++m_sync_bad_spans_downloaded; return 1; } + + if (this_height == 0 || context.get_expected_hash(this_height - 1) != b.prev_id) + { + LOG_ERROR_CCONTEXT("Sent invalid chain"); + drop_connection(context, false, false); + ++m_sync_bad_spans_downloaded; + return 1; + } + } + else if (b.prev_id != previous) + { + LOG_ERROR_CCONTEXT("Sent invalid chain"); + drop_connection(context, false, false); + ++m_sync_bad_spans_downloaded; + return 1; + } + previous = block_hash; + + if (start_height + i != this_height) + { + LOG_ERROR_CCONTEXT("Sent invalid chain"); + drop_connection(context, false, false); + ++m_sync_bad_spans_downloaded; + return 1; } auto req_it = context.m_requested_objects.find(block_hash); if(req_it == context.m_requested_objects.end()) { - LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << epee::string_tools::pod_to_hex(get_blob_hash(block_entry.block)) + LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << epee::string_tools::pod_to_hex(get_blob_hash(arg.blocks[i].block)) << " wasn't requested, dropping connection"); drop_connection(context, false, false); ++m_sync_bad_spans_downloaded; return 1; } - if(b.tx_hashes.size() != block_entry.txs.size()) + if(b.tx_hashes.size() != arg.blocks[i].txs.size()) { - LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << epee::string_tools::pod_to_hex(get_blob_hash(block_entry.block)) - << ", tx_hashes.size()=" << b.tx_hashes.size() << " mismatch with block_complete_entry.m_txs.size()=" << block_entry.txs.size() << ", dropping connection"); + LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << epee::string_tools::pod_to_hex(get_blob_hash(arg.blocks[i].block)) + << ", tx_hashes.size()=" << b.tx_hashes.size() << " mismatch with block_complete_entry.m_txs.size()=" << arg.blocks[i].txs.size() << ", dropping connection"); drop_connection(context, false, false); ++m_sync_bad_spans_downloaded; return 1; @@ -1465,6 +1502,14 @@ namespace cryptonote bool parent_known = m_core.have_block(new_block.prev_id); if (!parent_known) { + const std::uint64_t confirmed_height = m_block_queue.have_height(new_block.prev_id); + if (confirmed_height != std::numeric_limits::max() && confirmed_height + 1 != start_height) + { + MERROR(context << "Found incorrect height for " << new_block.prev_id << " provided by " << span_connection_id); + drop_connection(span_connection_id); + return 1; + } + // it could be: // - later in the current chain // - later in an alt chain @@ -2093,7 +2138,6 @@ skip: m_block_queue.flush_stale_spans(live_connections); // if we don't need to get next span, and the block queue is full enough, wait a bit - bool start_from_current_chain = false; if (!force_next_span) { do @@ -2116,7 +2160,7 @@ skip: 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; + context.set_state_normal(); if (m_core.get_current_blockchain_height() >= m_core.get_target_blockchain_height()) on_connection_synchronized(); return true; @@ -2265,7 +2309,7 @@ skip: 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; + context.set_state_normal(); if (m_core.get_current_blockchain_height() >= m_core.get_target_blockchain_height()) on_connection_synchronized(); return true; @@ -2378,7 +2422,7 @@ skip: 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()) { - context.m_state = cryptonote_connection_context::state_normal; + context.set_state_normal(); MLOG_PEER_STATE("Nothing to do for now, switching to normal state"); return true; } @@ -2421,14 +2465,11 @@ skip: m_core.get_short_chain_history(r.block_ids); CHECK_AND_ASSERT_MES(!r.block_ids.empty(), false, "Short chain history is empty"); - if (!start_from_current_chain) + // we'll want to start off from where we are on that peer, which may not be added yet + if (context.m_last_known_hash != crypto::null_hash && r.block_ids.front() != context.m_last_known_hash) { - // we'll want to start off from where we are on that peer, which may not be added yet - if (context.m_last_known_hash != crypto::null_hash && r.block_ids.front() != context.m_last_known_hash) - { - context.m_expect_height = std::numeric_limits::max(); - r.block_ids.push_front(context.m_last_known_hash); - } + context.m_expect_height = std::numeric_limits::max(); + r.block_ids.push_front(context.m_last_known_hash); } handler_request_blocks_history( r.block_ids ); // change the limit(?), sleep(?) @@ -2441,7 +2482,7 @@ skip: context.m_last_request_time = boost::posix_time::microsec_clock::universal_time(); context.m_expect_response = NOTIFY_RESPONSE_CHAIN_ENTRY::ID; - MLOG_P2P_MESSAGE("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() << ", start_from_current_chain " << start_from_current_chain); + MLOG_P2P_MESSAGE("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size()); post_notify(r, context); MLOG_PEER_STATE("requesting chain"); }else @@ -2455,7 +2496,7 @@ skip: << "\r\nm_requested_objects.size()=" << context.m_requested_objects.size() << "\r\non connection [" << epee::net_utils::print_connection_context_short(context)<< "]"); - context.m_state = cryptonote_connection_context::state_normal; + context.set_state_normal(); if (context.m_remote_blockchain_height >= m_core.get_target_blockchain_height()) { if (m_core.get_current_blockchain_height() >= m_core.get_target_blockchain_height()) @@ -2628,11 +2669,14 @@ skip: return 1; } + context.m_expected_heights_start = arg.start_height; + + context.m_expected_heights.clear(); + context.m_expected_heights.reserve(arg.m_block_ids.size()); context.m_needed_objects.clear(); context.m_needed_objects.reserve(arg.m_block_ids.size()); 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) { @@ -2644,9 +2688,10 @@ skip: } int where; const bool have_block = m_core.have_block_unlocked(arg.m_block_ids[i], &where); - if (first) + if (i == 0) { - if (!have_block && !m_block_queue.requested(arg.m_block_ids[i]) && !m_block_queue.have(arg.m_block_ids[i])) + // our outgoing chainlist only has proven blocks (i.e. downloaded) + if (!have_block && m_block_queue.have_height(arg.m_block_ids[i]) != arg.start_height) { LOG_ERROR_CCONTEXT("First block hash is unknown, dropping connection"); drop_connection_with_score(context, 5, false); @@ -2655,7 +2700,7 @@ skip: if (!have_block) expect_unknown = true; } - if (!first) + if (0 < i) { // 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 @@ -2696,10 +2741,10 @@ skip: expect_unknown = true; } const uint64_t block_weight = arg.m_block_weights.empty() ? 0 : arg.m_block_weights[i]; + context.m_expected_heights.push_back(arg.m_block_ids[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; @@ -2908,6 +2953,16 @@ skip: } //------------------------------------------------------------------------------------------------------------------------ template + void t_cryptonote_protocol_handler::drop_connection(const boost::uuids::uuid& id) + { + m_p2p->for_connection(id, [this](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t f)->bool{ + // This _could be_ outside of strand, so careful on actions + drop_connection(context, true, false); + return true; + }); + } + //------------------------------------------------------------------------------------------------------------------------ + template void t_cryptonote_protocol_handler::drop_connections(const epee::net_utils::network_address address) { MWARNING("dropping connections to " << address.str()); @@ -2924,6 +2979,7 @@ skip: { m_block_queue.flush_spans(id, true); m_p2p->for_connection(id, [&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t f)->bool{ + // This _could be_ outside of strand, so careful on actions drop_connection(context, true, false); return true; }); diff --git a/src/cryptonote_protocol/levin_notify.cpp b/src/cryptonote_protocol/levin_notify.cpp index 27c6d0278..0fbc7248b 100644 --- a/src/cryptonote_protocol/levin_notify.cpp +++ b/src/cryptonote_protocol/levin_notify.cpp @@ -28,6 +28,8 @@ #include "levin_notify.h" +#include +#include #include #include #include @@ -221,7 +223,7 @@ namespace levin `dispatch` is used heavily, which means "execute immediately in _this_ thread if the strand is not in use, otherwise queue the callback to be executed immediately after the strand completes its current task". - `post` is used where deferred execution to an `asio::io_service::run` + `post` is used where deferred execution to an `asio::io_context::run` thread is preferred. The strand per "zone" is useful because the levin @@ -238,7 +240,7 @@ namespace levin //! A queue of levin messages for a noise i2p/tor link struct noise_channel { - explicit noise_channel(boost::asio::io_service& io_service) + explicit noise_channel(boost::asio::io_context& io_service) : active(nullptr), queue(), strand(io_service), @@ -246,7 +248,7 @@ namespace levin connection(boost::uuids::nil_uuid()) {} - // `asio::io_service::strand` cannot be copied or moved + // `asio::io_context::strand` cannot be copied or moved noise_channel(const noise_channel&) = delete; noise_channel& operator=(const noise_channel&) = delete; @@ -254,7 +256,7 @@ namespace levin epee::byte_slice active; std::deque queue; - boost::asio::io_service::strand strand; + boost::asio::io_context::strand strand; boost::asio::steady_timer next_noise; boost::uuids::uuid connection; }; @@ -264,7 +266,7 @@ namespace levin { struct zone { - explicit zone(boost::asio::io_service& io_service, std::shared_ptr p2p, epee::byte_slice noise_in, epee::net_utils::zone zone, bool pad_txs) + explicit zone(boost::asio::io_context& io_service, std::shared_ptr p2p, epee::byte_slice noise_in, epee::net_utils::zone zone, bool pad_txs) : p2p(std::move(p2p)), noise(std::move(noise_in)), next_epoch(io_service), @@ -286,7 +288,7 @@ namespace levin const epee::byte_slice noise; //!< `!empty()` means zone is using noise channels boost::asio::steady_timer next_epoch; boost::asio::steady_timer flush_txs; - boost::asio::io_service::strand strand; + boost::asio::io_context::strand strand; struct context_t { std::vector fluff_txs; std::chrono::steady_clock::time_point flush_time; @@ -396,6 +398,8 @@ namespace levin for (auto& connection : connections) { std::sort(connection.first.begin(), connection.first.end()); // don't leak receive order + connection.first.erase(std::unique(connection.first.begin(), connection.first.end()), + connection.first.end()); make_payload_send_txs(*zone_->p2p, std::move(connection.first), connection.second, zone_->pad_txs, true); } @@ -452,7 +456,7 @@ namespace levin if (next_flush == std::chrono::steady_clock::time_point::max()) MWARNING("Unable to send transaction(s), no available connections"); - else if (!zone->flush_callbacks || next_flush < zone->flush_txs.expires_at()) + else if (!zone->flush_callbacks || next_flush < zone->flush_txs.expiry()) fluff_flush::queue(std::move(zone), next_flush); } }; @@ -513,7 +517,7 @@ namespace levin for (auto id = zone->map.begin(); id != zone->map.end(); ++id) { const std::size_t i = id - zone->map.begin(); - zone->channels[i].strand.post(update_channel{zone, i, *id}); + boost::asio::post(zone->channels[i].strand, update_channel{zone, i, *id}); } } @@ -672,7 +676,7 @@ namespace levin MWARNING("Unable to send transaction(s) to " << epee::net_utils::zone_to_string(zone_->nzone) << " - no suitable outbound connections at height " << height); - zone_->strand.post(update_channels{zone_, std::move(connections)}); + boost::asio::post(zone_->strand, update_channels{zone_, std::move(connections)}); } } @@ -702,7 +706,8 @@ namespace levin const bool fluffing = crypto::rand_idx(unsigned(100)) < CRYPTONOTE_DANDELIONPP_FLUFF_PROBABILITY; const auto start = std::chrono::steady_clock::now(); auto connections = get_out_connections(*(zone_->p2p), core_); - zone_->strand.dispatch( + boost::asio::dispatch( + zone_->strand, change_channels{zone_, net::dandelionpp::connection_map{std::move(connections), count_}, fluffing} ); @@ -713,7 +718,7 @@ namespace levin }; } // anonymous - notify::notify(boost::asio::io_service& service, std::shared_ptr p2p, epee::byte_slice noise, epee::net_utils::zone zone, const bool pad_txs, i_core_events& core) + notify::notify(boost::asio::io_context& service, std::shared_ptr p2p, epee::byte_slice noise, epee::net_utils::zone zone, const bool pad_txs, i_core_events& core) : zone_(std::make_shared(service, std::move(p2p), std::move(noise), zone, pad_txs)) , core_(std::addressof(core)) { @@ -741,9 +746,14 @@ namespace levin notify::status notify::get_status() const noexcept { if (!zone_) - return {false, false}; + return {false, false, false}; - return {!zone_->noise.empty(), CRYPTONOTE_NOISE_CHANNELS <= zone_->connection_count}; + // `connection_count` is only set when `!noise.empty()`. + const std::size_t connection_count = zone_->connection_count; + bool has_outgoing = connection_count; + if (zone_->noise.empty()) + has_outgoing = zone_->p2p->get_out_connections_count(); + return {!zone_->noise.empty(), CRYPTONOTE_NOISE_CHANNELS <= connection_count, has_outgoing}; } void notify::new_out_connection() @@ -751,7 +761,8 @@ namespace levin if (!zone_ || zone_->noise.empty() || CRYPTONOTE_NOISE_CHANNELS <= zone_->connection_count) return; - zone_->strand.dispatch( + boost::asio::dispatch( + zone_->strand, update_channels{zone_, get_out_connections(*(zone_->p2p), core_)} ); } @@ -762,7 +773,7 @@ namespace levin return; auto& zone = zone_; - zone_->strand.dispatch([zone, id, is_income]{ + boost::asio::dispatch(zone_->strand, [zone, id, is_income] { zone->contexts[id] = { .fluff_txs = {}, .flush_time = std::chrono::steady_clock::time_point::max(), @@ -777,7 +788,7 @@ namespace levin return; auto& zone = zone_; - zone_->strand.dispatch([zone, id]{ + boost::asio::dispatch(zone_->strand, [zone, id]{ zone->contexts.erase(id); }); } @@ -852,7 +863,8 @@ namespace levin for (std::size_t channel = 0; channel < zone_->channels.size(); ++channel) { - zone_->channels[channel].strand.dispatch( + boost::asio::dispatch( + zone_->channels[channel].strand, queue_covert_notify{zone_, message.clone(), channel} ); } @@ -871,7 +883,8 @@ namespace levin if (zone_->nzone == epee::net_utils::zone::public_) { // this will change a local/forward tx to stem or fluff ... - zone_->strand.dispatch( + boost::asio::dispatch( + zone_->strand, dandelionpp_notify{zone_, core_, std::move(txs), source, tx_relay} ); break; @@ -884,7 +897,7 @@ namespace levin ipv4/6. Marking it as "fluff" here will make the tx immediately visible externally from this node, which is not desired. */ core_->on_transactions_relayed(epee::to_span(txs), tx_relay); - zone_->strand.dispatch(fluff_notify{zone_, std::move(txs), source}); + boost::asio::dispatch(zone_->strand, fluff_notify{zone_, std::move(txs), source}); break; } } diff --git a/src/cryptonote_protocol/levin_notify.h b/src/cryptonote_protocol/levin_notify.h index 2927eea86..fd210787c 100644 --- a/src/cryptonote_protocol/levin_notify.h +++ b/src/cryptonote_protocol/levin_notify.h @@ -28,7 +28,7 @@ #pragma once -#include +#include #include #include #include @@ -75,7 +75,8 @@ namespace levin struct status { bool has_noise; - bool connections_filled; + bool connections_filled; //!< True when has zone has `CRYPTONOTE_NOISE_CHANNELS` outgoing noise channels + bool has_outgoing; //!< True when zone has outgoing connections }; //! Construct an instance that cannot notify. @@ -85,7 +86,7 @@ namespace levin {} //! Construct an instance with available notification `zones`. - explicit notify(boost::asio::io_service& service, std::shared_ptr p2p, epee::byte_slice noise, epee::net_utils::zone zone, bool pad_txs, i_core_events& core); + explicit notify(boost::asio::io_context& service, std::shared_ptr p2p, epee::byte_slice noise, epee::net_utils::zone zone, bool pad_txs, i_core_events& core); notify(const notify&) = delete; notify(notify&&) = default; diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 4710fc497..998db9072 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -696,6 +696,16 @@ bool t_command_parser_executor::ban(const std::vector& args) std::ifstream ifs(ban_list_path.string()); for (std::string line; std::getline(ifs, line); ) { + // ignore comments after '#' character + const size_t pound_idx = line.find('#'); + if (pound_idx != std::string::npos) + line.resize(pound_idx); + + // trim whitespace and ignore empty lines + boost::trim(line); + if (line.empty()) + continue; + auto subnet = net::get_ipv4_subnet_address(line); if (subnet) { diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index 2b2709426..96a2719cd 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -83,7 +83,7 @@ uint16_t parse_public_rpc_port(const po::variables_map &vm) } uint16_t rpc_port; - if (!string_tools::get_xtype_from_string(rpc_port, rpc_port_str)) + if (!epee::string_tools::get_xtype_from_string(rpc_port, rpc_port_str)) { throw std::runtime_error("invalid RPC port " + rpc_port_str); } @@ -124,7 +124,7 @@ bool isFat32(const wchar_t* root_path) // Helper function to generate genesis transaction void print_genesis_tx_hex(const cryptonote::network_type nettype) { - + /* using namespace cryptonote; account_base miner_acc1; @@ -168,7 +168,7 @@ void print_genesis_tx_hex(const cryptonote::network_type nettype) { std::string tx_hex = ss.str(); std::cout << "Insert this line into your coin configuration file: " << std::endl; std::cout << "std::string const GENESIS_TX = \"" << epee::string_tools::buff_to_hex_nodelimer(tx_hex) << "\";" << std::endl; - + */ return; } diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index c4c3ebd17..b5bd5796b 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1064,7 +1064,7 @@ bool t_rpc_command_executor::print_transaction(crypto::hash transaction_hash, cryptonote::blobdata blob; std::string source = as_hex.empty() ? pruned_as_hex + prunable_as_hex : as_hex; bool pruned = !pruned_as_hex.empty() && prunable_as_hex.empty(); - if (!string_tools::parse_hexstr_to_binbuff(source, blob)) + if (!epee::string_tools::parse_hexstr_to_binbuff(source, blob)) { tools::fail_msg_writer() << "Failed to parse tx to get json format"; } diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index e85c12ede..3d8cd7abf 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -528,6 +528,7 @@ namespace hw { {0x2c97, 0x0004, 0, 0xffa0}, {0x2c97, 0x0005, 0, 0xffa0}, {0x2c97, 0x0006, 0, 0xffa0}, + {0x2c97, 0x0007, 0, 0xffa0}, }; bool device_ledger::connect(void) { diff --git a/src/device_trezor/trezor/transport.cpp b/src/device_trezor/trezor/transport.cpp index 53b35a37a..10dd82c53 100644 --- a/src/device_trezor/trezor/transport.cpp +++ b/src/device_trezor/trezor/transport.cpp @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include @@ -614,8 +613,7 @@ namespace trezor{ } udp::resolver resolver(m_io_service); - udp::resolver::query query(udp::v4(), m_device_host, std::to_string(m_device_port)); - m_endpoint = *resolver.resolve(query); + m_endpoint = *resolver.resolve(udp::v4(), m_device_host, std::to_string(m_device_port)).begin(); m_socket.reset(new udp::socket(m_io_service)); m_socket->open(udp::v4()); diff --git a/src/device_trezor/trezor/transport.hpp b/src/device_trezor/trezor/transport.hpp index a452724da..827b189fd 100644 --- a/src/device_trezor/trezor/transport.hpp +++ b/src/device_trezor/trezor/transport.hpp @@ -244,7 +244,7 @@ namespace trezor { int m_device_port; std::unique_ptr m_socket; - boost::asio::io_service m_io_service; + boost::asio::io_context m_io_service; boost::asio::deadline_timer m_deadline; udp::endpoint m_endpoint; }; diff --git a/src/lmdb/key_stream.h b/src/lmdb/key_stream.h index 11fa284dd..74cb536e5 100644 --- a/src/lmdb/key_stream.h +++ b/src/lmdb/key_stream.h @@ -133,6 +133,7 @@ namespace lmdb //! \pre `!is_end()` \return Current key K get_key() const noexcept { + static_assert(std::is_trivially_copyable(), "key is not memcpy safe"); assert(!is_end()); K out; std::memcpy(std::addressof(out), key.data(), sizeof(out)); diff --git a/src/lmdb/table.h b/src/lmdb/table.h index 4ded4ba54..48b94bc66 100644 --- a/src/lmdb/table.h +++ b/src/lmdb/table.h @@ -55,7 +55,7 @@ namespace lmdb static expect get_value(MDB_val value) noexcept { static_assert(std::is_same(), "bad MONERO_FIELD?"); - static_assert(std::is_pod(), "F must be POD"); + static_assert(std::is_trivially_copyable(), "F must be memcpy safe"); static_assert(sizeof(F) + offset <= sizeof(U), "bad field type and/or offset"); if (value.mv_size != sizeof(U)) diff --git a/src/lmdb/util.h b/src/lmdb/util.h index c6c75bc00..038411417 100644 --- a/src/lmdb/util.h +++ b/src/lmdb/util.h @@ -111,6 +111,7 @@ namespace lmdb template inline int less(MDB_val const* left, MDB_val const* right) noexcept { + static_assert(std::is_trivially_copyable(), "memcpy will not work"); if (!left || !right || left->mv_size < sizeof(T) + offset || right->mv_size < sizeof(T) + offset) { assert("invalid use of custom comparison" == 0); @@ -127,7 +128,7 @@ namespace lmdb /*! A LMDB comparison function that uses `std::memcmp`. - \toaram T is `!epee::has_padding` + \toaram T has standard layout and an alignment of 1 \tparam offset to `T` within the value. \return The result of `std::memcmp` over the value. @@ -135,7 +136,7 @@ namespace lmdb template inline int compare(MDB_val const* left, MDB_val const* right) noexcept { - static_assert(!epee::has_padding(), "memcmp will not work"); + static_assert(std::is_standard_layout() && alignof(T) == 1, "memcmp will not work"); if (!left || !right || left->mv_size < sizeof(T) + offset || right->mv_size < sizeof(T) + offset) { assert("invalid use of custom comparison" == 0); diff --git a/src/lmdb/value_stream.h b/src/lmdb/value_stream.h index bd2814ef4..2475ec191 100644 --- a/src/lmdb/value_stream.h +++ b/src/lmdb/value_stream.h @@ -162,8 +162,8 @@ namespace lmdb G get_value() const noexcept { static_assert(std::is_same(), "bad MONERO_FIELD usage?"); - static_assert(std::is_pod(), "value type must be pod"); - static_assert(std::is_pod(), "field type must be pod"); + static_assert(std::is_trivially_copyable(), "value type must be memcpy safe"); + static_assert(std::is_trivially_copyable(), "field type must be memcpy safe"); static_assert(sizeof(G) + uoffset <= sizeof(U), "bad field and/or offset"); assert(sizeof(G) + uoffset <= values.size()); assert(!is_end()); diff --git a/src/net/parse.cpp b/src/net/parse.cpp index b76aefad7..f989d7de4 100644 --- a/src/net/parse.cpp +++ b/src/net/parse.cpp @@ -84,7 +84,7 @@ namespace net return i2p_address::make(address); boost::system::error_code ec; - boost::asio::ip::address_v6 v6 = boost::asio::ip::address_v6::from_string(host_str, ec); + boost::asio::ip::address_v6 v6 = boost::asio::ip::make_address_v6(host_str, ec); ipv6 = !ec; std::uint16_t port = default_port; diff --git a/src/net/socks.cpp b/src/net/socks.cpp index 97ef4a672..43d98024b 100644 --- a/src/net/socks.cpp +++ b/src/net/socks.cpp @@ -29,7 +29,9 @@ #include "socks.h" #include +#include #include +#include #include #include #include @@ -176,7 +178,7 @@ namespace socks { std::shared_ptr self_; - static boost::asio::mutable_buffers_1 get_buffer(client& self) noexcept + static boost::asio::mutable_buffer get_buffer(client& self) noexcept { static_assert(sizeof(v4_header) <= sizeof(self.buffer_), "buffer too small for v4 response"); return boost::asio::buffer(self.buffer_, sizeof(v4_header)); @@ -192,7 +194,7 @@ namespace socks else if (bytes < self.buffer().size()) self.done(socks::error::bad_write, std::move(self_)); else - boost::asio::async_read(self.proxy_, get_buffer(self), self.strand_.wrap(completed{std::move(self_)})); + boost::asio::async_read(self.proxy_, get_buffer(self), boost::asio::bind_executor(self.strand_, completed{std::move(self_)})); } } }; @@ -201,7 +203,7 @@ namespace socks { std::shared_ptr self_; - static boost::asio::const_buffers_1 get_buffer(client const& self) noexcept + static boost::asio::const_buffer get_buffer(client const& self) noexcept { return boost::asio::buffer(self.buffer_, self.buffer_size_); } @@ -214,13 +216,13 @@ namespace socks if (error) self.done(error, std::move(self_)); else - boost::asio::async_write(self.proxy_, get_buffer(self), self.strand_.wrap(read{std::move(self_)})); + boost::asio::async_write(self.proxy_, get_buffer(self), boost::asio::bind_executor(self.strand_, read{std::move(self_)})); } } }; client::client(stream_type::socket&& proxy, socks::version ver) - : proxy_(std::move(proxy)), strand_(GET_IO_SERVICE(proxy_)), buffer_size_(0), buffer_(), ver_(ver) + : proxy_(std::move(proxy)), strand_(proxy_.get_executor()), buffer_size_(0), buffer_(), ver_(ver) {} client::~client() {} @@ -295,7 +297,7 @@ namespace socks if (self && !self->buffer().empty()) { client& alias = *self; - alias.proxy_.async_connect(proxy_address, alias.strand_.wrap(write{std::move(self)})); + alias.proxy_.async_connect(proxy_address, boost::asio::bind_executor(alias.strand_, write{std::move(self)})); return true; } return false; @@ -306,7 +308,7 @@ namespace socks if (self && !self->buffer().empty()) { client& alias = *self; - boost::asio::async_write(alias.proxy_, write::get_buffer(alias), alias.strand_.wrap(read{std::move(self)})); + boost::asio::async_write(alias.proxy_, write::get_buffer(alias), boost::asio::bind_executor(alias.strand_, read{std::move(self)})); return true; } return false; @@ -317,7 +319,7 @@ namespace socks if (self_ && error != boost::system::errc::operation_canceled) { const std::shared_ptr self = std::move(self_); - self->strand_.dispatch([self] () + boost::asio::dispatch(self->strand_, [self] () { if (self && self->proxy_.is_open()) { diff --git a/src/net/socks.h b/src/net/socks.h index af67d4abe..1c80ece2c 100644 --- a/src/net/socks.h +++ b/src/net/socks.h @@ -29,8 +29,8 @@ #pragma once #include +#include #include -#include #include #include #include @@ -93,7 +93,7 @@ namespace socks class client { boost::asio::ip::tcp::socket proxy_; - boost::asio::io_service::strand strand_; + boost::asio::strand strand_; std::uint16_t buffer_size_; std::uint8_t buffer_[1024]; socks::version ver_; diff --git a/src/net/socks_connect.cpp b/src/net/socks_connect.cpp index 5317564de..8ecbf6d08 100644 --- a/src/net/socks_connect.cpp +++ b/src/net/socks_connect.cpp @@ -71,7 +71,7 @@ namespace socks boost::promise result{}; out = result.get_future(); const auto proxy = net::socks::make_connect_client( - boost::asio::ip::tcp::socket{GET_IO_SERVICE(timeout)}, net::socks::version::v4a, future_socket{std::move(result)} + boost::asio::ip::tcp::socket{MONERO_GET_EXECUTOR(timeout)}, net::socks::version::v4a, future_socket{std::move(result)} ); if (epee::string_tools::get_ip_int32_from_string(ip_address, remote_host)) diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp index f9803fd81..4111b0b71 100644 --- a/src/p2p/net_node.cpp +++ b/src/p2p/net_node.cpp @@ -169,7 +169,7 @@ namespace nodetool const command_line::arg_descriptor arg_pad_transactions = { "pad-transactions", "Pad relayed transactions to help defend against traffic volume analysis", false }; - const command_line::arg_descriptor arg_max_connections_per_ip = {"max-connections-per-ip", "Maximum number of connections allowed from the same IP address", 1}; + const command_line::arg_descriptor arg_max_connections_per_ip = {"max-connections-per-ip", "Maximum number of p2p connections allowed from the same IP address", 1}; boost::optional> get_proxies(boost::program_options::variables_map const& vm) { @@ -327,7 +327,7 @@ namespace nodetool } boost::optional - socks_connect_internal(const std::atomic& stop_signal, boost::asio::io_service& service, const boost::asio::ip::tcp::endpoint& proxy, const epee::net_utils::network_address& remote) + socks_connect_internal(const std::atomic& stop_signal, boost::asio::io_context& service, const boost::asio::ip::tcp::endpoint& proxy, const epee::net_utils::network_address& remote) { using socket_type = net::socks::client::stream_type::socket; using client_result = std::pair; diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 98dc37f1a..a8b5b4d2a 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -31,7 +31,7 @@ #pragma once #include #include -#include +#include #include #include #include @@ -103,7 +103,7 @@ namespace nodetool // hides boost::future and chrono stuff from mondo template file boost::optional - socks_connect_internal(const std::atomic& stop_signal, boost::asio::io_service& service, const boost::asio::ip::tcp::endpoint& proxy, const epee::net_utils::network_address& remote); + socks_connect_internal(const std::atomic& stop_signal, boost::asio::io_context& service, const boost::asio::ip::tcp::endpoint& proxy, const epee::net_utils::network_address& remote); template @@ -124,7 +124,8 @@ namespace nodetool template class node_server: public epee::levin::levin_commands_handler >, public i_p2p_endpoint, - public epee::net_utils::i_connection_filter + public epee::net_utils::i_connection_filter, + public epee::net_utils::i_connection_limit { struct by_conn_id{}; struct by_peer_id{}; @@ -179,7 +180,7 @@ namespace nodetool set_config_defaults(); } - network_zone(boost::asio::io_service& public_service) + network_zone(boost::asio::io_context& public_service) : m_connect(nullptr), m_net_server(public_service, epee::net_utils::e_connection_type_P2P), m_seed_nodes(), @@ -349,7 +350,10 @@ namespace nodetool virtual bool add_host_fail(const epee::net_utils::network_address &address, unsigned int score = 1); //----------------- i_connection_filter -------------------------------------------------------- virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address, time_t *t = NULL); + //----------------- i_connection_limit --------------------------------------------------------- + virtual bool is_host_limit(const epee::net_utils::network_address &address); //----------------------------------------------------------------------------------------------- + bool parse_peer_from_string(epee::net_utils::network_address& pe, const std::string& node_addr, uint16_t default_port = 0); bool handle_command_line( const boost::program_options::variables_map& vm diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 2f10c172b..d2c729f61 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -87,7 +87,7 @@ namespace nodetool template node_server::~node_server() { - // tcp server uses io_service in destructor, and every zone uses + // tcp server uses io_context in destructor, and every zone uses // io_service from public zone. for (auto current = m_network_zones.begin(); current != m_network_zones.end(); /* below */) { @@ -226,6 +226,26 @@ namespace nodetool // not found in hosts or subnets, allowed return true; } + //----------------------------------------------------------------------------------- + template + bool node_server::is_host_limit(const epee::net_utils::network_address &address) + { + const network_zone& zone = m_network_zones.at(address.get_zone()); + if (zone.m_current_number_of_in_peers >= zone.m_config.m_net_config.max_in_connection_count) // in peers limit + { + MWARNING("Exceeded max incoming connections, so dropping this one."); + return true; + } + + if(has_too_many_connections(address)) + { + MWARNING("CONNECTION FROM " << address.host_str() << " REFUSED, too many connections from the same address"); + return true; + } + + return false; + } + //----------------------------------------------------------------------------------- template bool node_server::block_host(epee::net_utils::network_address addr, time_t seconds, bool add_only) @@ -456,7 +476,7 @@ namespace nodetool m_use_ipv6 = command_line::get_arg(vm, arg_p2p_use_ipv6); m_require_ipv4 = !command_line::get_arg(vm, arg_p2p_ignore_ipv4); public_zone.m_notifier = cryptonote::levin::notify{ - public_zone.m_net_server.get_io_service(), public_zone.m_net_server.get_config_shared(), nullptr, epee::net_utils::zone::public_, pad_txs, m_payload_handler.get_core() + public_zone.m_net_server.get_io_context(), public_zone.m_net_server.get_config_shared(), nullptr, epee::net_utils::zone::public_, pad_txs, m_payload_handler.get_core() }; if (command_line::has_arg(vm, arg_p2p_add_peer)) @@ -531,6 +551,16 @@ namespace nodetool std::istringstream iss(banned_ips); for (std::string line; std::getline(iss, line); ) { + // ignore comments after '#' character + const size_t pound_idx = line.find('#'); + if (pound_idx != std::string::npos) + line.resize(pound_idx); + + // trim whitespace and ignore empty lines + boost::trim(line); + if (line.empty()) + continue; + auto subnet = net::get_ipv4_subnet_address(line); if (subnet) { @@ -609,7 +639,7 @@ namespace nodetool } zone.m_notifier = cryptonote::levin::notify{ - zone.m_net_server.get_io_service(), zone.m_net_server.get_config_shared(), std::move(this_noise), proxy.zone, pad_txs, m_payload_handler.get_core() + zone.m_net_server.get_io_context(), zone.m_net_server.get_config_shared(), std::move(this_noise), proxy.zone, pad_txs, m_payload_handler.get_core() }; } @@ -671,20 +701,18 @@ namespace nodetool net::get_network_address_host_and_port(addr, host, port); MINFO("Resolving node address: host=" << host << ", port=" << port); - io_service io_srv; - ip::tcp::resolver resolver(io_srv); - ip::tcp::resolver::query query(host, port, boost::asio::ip::tcp::resolver::query::canonical_name); boost::system::error_code ec; - ip::tcp::resolver::iterator i = resolver.resolve(query, ec); - CHECK_AND_ASSERT_MES(!ec, false, "Failed to resolve host name '" << host << "': " << ec.message() << ':' << ec.value()); + io_context io_srv; + ip::tcp::resolver resolver(io_srv); + const auto results = resolver.resolve(host, port, boost::asio::ip::tcp::resolver::canonical_name, ec); + CHECK_AND_ASSERT_MES(!ec && !results.empty(), false, "Failed to resolve host name '" << host << "': " << ec.message() << ':' << ec.value()); - ip::tcp::resolver::iterator iend; - for (; i != iend; ++i) + for (const auto& result : results) { - ip::tcp::endpoint endpoint = *i; + const auto& endpoint = result.endpoint(); if (endpoint.address().is_v4()) { - epee::net_utils::network_address na{epee::net_utils::ipv4_network_address{boost::asio::detail::socket_ops::host_to_network_long(endpoint.address().to_v4().to_ulong()), endpoint.port()}}; + epee::net_utils::network_address na{epee::net_utils::ipv4_network_address{boost::asio::detail::socket_ops::host_to_network_long(endpoint.address().to_v4().to_uint()), endpoint.port()}}; seed_nodes.push_back(na); MINFO("Added node: " << na.str()); } @@ -887,7 +915,7 @@ namespace nodetool return zone_->second; network_zone& public_zone = m_network_zones[epee::net_utils::zone::public_]; - return m_network_zones.emplace_hint(zone_, std::piecewise_construct, std::make_tuple(zone), std::tie(public_zone.m_net_server.get_io_service()))->second; + return m_network_zones.emplace_hint(zone_, std::piecewise_construct, std::make_tuple(zone), std::tie(public_zone.m_net_server.get_io_context()))->second; } //----------------------------------------------------------------------------------- template @@ -964,6 +992,7 @@ namespace nodetool std::string ipv6_addr = ""; std::string ipv6_port = ""; zone.second.m_net_server.set_connection_filter(this); + zone.second.m_net_server.set_connection_limit(this); MINFO("Binding (IPv4) on " << zone.second.m_bind_ip << ":" << zone.second.m_port); if (!zone.second.m_bind_ipv6_address.empty() && m_use_ipv6) { @@ -2292,11 +2321,12 @@ namespace nodetool if (enet::zone::tor < network->first) break; // unknown network - if (network->second.m_connect) + const auto status = network->second.m_notifier.get_status(); + if (network->second.m_connect && status.has_outgoing) return send(*network); } - // configuration should not allow this scenario + MWARNING("Unable to send " << txs.size() << " transaction(s): anonymity networks had no outgoing connections"); return enet::zone::invalid; } //----------------------------------------------------------------------------------- @@ -2475,6 +2505,20 @@ namespace nodetool std::vector local_peerlist_new; zone.m_peerlist.get_peerlist_head(local_peerlist_new, true, max_peerlist_size); + /* Tor/I2P nodes receiving connections via forwarding (from tor/i2p daemon) + do not know the address of the connecting peer. This is relayed to them, + iff the node has setup an inbound hidden service. + + \note Insert into `local_peerlist_new` so that it is only sent once like + the other peers. */ + if(outgoing_to_same_zone) + { + local_peerlist_new.insert( + local_peerlist_new.begin() + crypto::rand_range(std::size_t(0), local_peerlist_new.size()), + peerlist_entry{zone.m_our_address, zone.m_config.m_peer_id, 0} + ); + } + //only include out peers we did not already send rsp.local_peerlist_new.reserve(local_peerlist_new.size()); for (auto &pe: local_peerlist_new) @@ -2485,17 +2529,6 @@ namespace nodetool } m_payload_handler.get_payload_sync_data(rsp.payload_data); - /* Tor/I2P nodes receiving connections via forwarding (from tor/i2p daemon) - do not know the address of the connecting peer. This is relayed to them, - iff the node has setup an inbound hidden service. The other peer will have - to use the random peer_id value to link the two. My initial thought is that - the inbound peer should leave the other side marked as ``, - etc., because someone could give faulty addresses over Tor/I2P to get the - real peer with that identity banned/blacklisted. */ - - if(outgoing_to_same_zone) - rsp.local_peerlist_new.push_back(peerlist_entry{zone.m_our_address, zone.m_config.m_peer_id, std::time(nullptr)}); - LOG_DEBUG_CC(context, "COMMAND_TIMED_SYNC"); return 1; } @@ -2539,13 +2572,6 @@ namespace nodetool return 1; } - if (zone.m_current_number_of_in_peers >= zone.m_config.m_net_config.max_in_connection_count) // in peers limit - { - LOG_WARNING_CC(context, "COMMAND_HANDSHAKE came, but already have max incoming connections, so dropping this one."); - drop_connection(context); - return 1; - } - if(!m_payload_handler.process_payload_sync_data(arg.payload_data, context, true)) { LOG_WARNING_CC(context, "COMMAND_HANDSHAKE came, but process_payload_sync_data returned false, dropping connection."); @@ -2555,13 +2581,6 @@ namespace nodetool zone.m_notifier.on_handshake_complete(context.m_connection_id, context.m_is_income); - if(has_too_many_connections(context.m_remote_address)) - { - LOG_PRINT_CCONTEXT_L1("CONNECTION FROM " << context.m_remote_address.host_str() << " REFUSED, too many connections from the same address"); - drop_connection(context); - return 1; - } - //associate peer_id with this connection context.peer_id = arg.node_data.peer_id; context.m_in_timedsync = false; @@ -2881,15 +2900,16 @@ namespace nodetool if (cntxt.m_is_income && cntxt.m_remote_address.is_same_host(address)) { count++; - if (count > max_connections) { + // the only call location happens BEFORE foreach_connection list is updated + if (count >= max_connections) { return false; } } return true; }); - - return count > max_connections; + // the only call location happens BEFORE foreach_connection list is updated + return count >= max_connections; } template @@ -3100,7 +3120,7 @@ namespace nodetool boost::optional> node_server::socks_connect(network_zone& zone, const epee::net_utils::network_address& remote, epee::net_utils::ssl_support_t ssl_support) { - auto result = socks_connect_internal(zone.m_net_server.get_stop_signal(), zone.m_net_server.get_io_service(), zone.m_proxy_address, remote); + auto result = socks_connect_internal(zone.m_net_server.get_stop_signal(), zone.m_net_server.get_io_context(), zone.m_proxy_address, remote); if (result) // if no error { p2p_connection_context context{}; diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index edc8c2810..85e95b2ae 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -48,8 +48,8 @@ extern "C" { } #include "crypto/crypto.h" -#include "cryptonote_basic/cryptonote_basic.h" -#include "cryptonote_protocol/enums.h" +//#include "cryptonote_basic/cryptonote_basic.h" +//#include "cryptonote_protocol/enums.h" #include "rctTypes.h" #include "rctOps.h" diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index d60e36ea8..52c32dd97 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -163,6 +163,10 @@ namespace cryptonote command_line::add_arg(desc, arg_rpc_payment_difficulty); command_line::add_arg(desc, arg_rpc_payment_credits); command_line::add_arg(desc, arg_rpc_payment_allow_free_loopback); + command_line::add_arg(desc, arg_rpc_max_connections_per_public_ip); + command_line::add_arg(desc, arg_rpc_max_connections_per_private_ip); + command_line::add_arg(desc, arg_rpc_max_connections); + command_line::add_arg(desc, arg_rpc_response_soft_limit); } //------------------------------------------------------------------------------------------------------------------------------ core_rpc_server::core_rpc_server( @@ -369,11 +373,28 @@ namespace cryptonote } } + const auto max_connections_public = command_line::get_arg(vm, arg_rpc_max_connections_per_public_ip); + const auto max_connections_private = command_line::get_arg(vm, arg_rpc_max_connections_per_private_ip); + const auto max_connections = command_line::get_arg(vm, arg_rpc_max_connections); + + if (max_connections < max_connections_public) + { + MFATAL(arg_rpc_max_connections_per_public_ip.name << " is bigger than " << arg_rpc_max_connections.name); + return false; + } + if (max_connections < max_connections_private) + { + MFATAL(arg_rpc_max_connections_per_private_ip.name << " is bigger than " << arg_rpc_max_connections.name); + return false; + } + auto rng = [](size_t len, uint8_t *ptr){ return crypto::rand(len, ptr); }; const bool inited = epee::http_server_impl_base::init( rng, std::move(port), std::move(bind_ip_str), std::move(bind_ipv6_str), 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) + std::move(rpc_config->access_control_origins), std::move(http_login), std::move(rpc_config->ssl_options), + max_connections_public, max_connections_private, max_connections, + command_line::get_arg(vm, arg_rpc_response_soft_limit) ); m_net_server.get_config_object().m_max_content_length = MAX_RPC_CONTENT_LENGTH; @@ -2835,6 +2856,12 @@ namespace cryptonote } else { + if (!i->ip) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; + error_resp.message = "No ip/host supplied"; + return false; + } na = epee::net_utils::ipv4_network_address{i->ip, 0}; } if (i->ban) @@ -3829,4 +3856,28 @@ namespace cryptonote , "Allow free access from the loopback address (ie, the local host)" , false }; + + const command_line::arg_descriptor core_rpc_server::arg_rpc_max_connections_per_public_ip = { + "rpc-max-connections-per-public-ip" + , "Max RPC connections per public IP permitted" + , DEFAULT_RPC_MAX_CONNECTIONS_PER_PUBLIC_IP + }; + + const command_line::arg_descriptor core_rpc_server::arg_rpc_max_connections_per_private_ip = { + "rpc-max-connections-per-private-ip" + , "Max RPC connections per private and localhost IP permitted" + , DEFAULT_RPC_MAX_CONNECTIONS_PER_PRIVATE_IP + }; + + const command_line::arg_descriptor core_rpc_server::arg_rpc_max_connections = { + "rpc-max-connections" + , "Max RPC connections permitted" + , DEFAULT_RPC_MAX_CONNECTIONS + }; + + const command_line::arg_descriptor core_rpc_server::arg_rpc_response_soft_limit = { + "rpc-response-soft-limit" + , "Max response bytes that can be queued, enforced at next response attempt" + , DEFAULT_RPC_SOFT_LIMIT_SIZE + }; } // namespace cryptonote diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index a763ccd7e..650326703 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -47,10 +47,6 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "daemon.rpc" -// yes, epee doesn't properly use its full namespace when calling its -// functions from macros. *sigh* -using namespace epee; - namespace cryptonote { /************************************************************************/ @@ -60,7 +56,6 @@ namespace cryptonote { public: - static const command_line::arg_descriptor arg_public_node; static const command_line::arg_descriptor arg_rpc_bind_port; static const command_line::arg_descriptor arg_rpc_restricted_bind_port; static const command_line::arg_descriptor arg_restricted_rpc; @@ -77,6 +72,10 @@ namespace cryptonote static const command_line::arg_descriptor arg_rpc_payment_difficulty; static const command_line::arg_descriptor arg_rpc_payment_credits; static const command_line::arg_descriptor arg_rpc_payment_allow_free_loopback; + static const command_line::arg_descriptor arg_rpc_max_connections_per_public_ip; + static const command_line::arg_descriptor arg_rpc_max_connections_per_private_ip; + static const command_line::arg_descriptor arg_rpc_max_connections; + static const command_line::arg_descriptor arg_rpc_response_soft_limit; typedef epee::net_utils::connection_context_base connection_context; diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 7e1a29ffb..b26234d16 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -536,6 +536,8 @@ namespace rpc res.info.target_height = res.info.height; } + m_core.get_blockchain_top(res.info.top_block_height, res.info.top_block_hash); + auto& chain = m_core.get_blockchain_storage(); res.info.wide_difficulty = chain.get_difficulty_for_next_block(); diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h index 0cc3ac317..b2542c395 100644 --- a/src/rpc/message_data_structs.h +++ b/src/rpc/message_data_structs.h @@ -180,6 +180,7 @@ namespace rpc { uint64_t height; uint64_t target_height; + uint64_t top_block_height; cryptonote::difficulty_type wide_difficulty; uint64_t difficulty; uint64_t target; diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp index 1a347b91d..2b6b48285 100644 --- a/src/rpc/rpc_args.cpp +++ b/src/rpc/rpc_args.cpp @@ -149,7 +149,7 @@ namespace cryptonote { // always parse IP here for error consistency boost::system::error_code ec{}; - const auto parsed_ip = boost::asio::ip::address::from_string(config.bind_ip, ec); + const auto parsed_ip = boost::asio::ip::make_address(config.bind_ip, ec); if (ec) { LOG_ERROR(tr("Invalid IP address given for --") << arg.rpc_bind_ip.name); @@ -177,7 +177,7 @@ namespace cryptonote // 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); + const auto parsed_ip = boost::asio::ip::make_address(config.bind_ipv6_address, ec); if (ec) { LOG_ERROR(tr("Invalid IP address given for --") << arg.rpc_bind_ipv6_address.name); @@ -198,7 +198,7 @@ namespace cryptonote { // always parse IP here for error consistency boost::system::error_code ec{}; - boost::asio::ip::address::from_string(config.restricted_bind_ip, ec); + boost::asio::ip::make_address(config.restricted_bind_ip, ec); if (ec) { LOG_ERROR(tr("Invalid IP address given for --") << arg.rpc_restricted_bind_ip.name); @@ -215,7 +215,7 @@ namespace cryptonote // always parse IP here for error consistency boost::system::error_code ec{}; - boost::asio::ip::address::from_string(config.restricted_bind_ipv6_address, ec); + boost::asio::ip::make_address(config.restricted_bind_ipv6_address, ec); if (ec) { LOG_ERROR(tr("Invalid IP address given for --") << arg.rpc_restricted_bind_ipv6_address.name); diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp index b579100a5..6e74614d9 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -289,7 +289,10 @@ void toJsonValue(rapidjson::Writer& dest, const cryptonote::t { INSERT_INTO_JSON_OBJECT(dest, signatures, tx.signatures); } - INSERT_INTO_JSON_OBJECT(dest, ringct, tx.rct_signatures); + { + dest.Key("ringct"); + toJsonValue(dest, tx.rct_signatures, tx.pruned); + } dest.EndObject(); } @@ -1155,7 +1158,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResp GET_FROM_JSON_OBJECT(val, response.reward, reward); } -void toJsonValue(rapidjson::Writer& dest, const rct::rctSig& sig) +void toJsonValue(rapidjson::Writer& dest, const rct::rctSig& sig, const bool prune) { using boost::adaptors::transform; @@ -1182,7 +1185,7 @@ void toJsonValue(rapidjson::Writer& dest, const rct::rctSig& } // prunable - if (!sig.p.bulletproofs.empty() || !sig.p.bulletproofs_plus.empty() || !sig.p.rangeSigs.empty() || !sig.p.MGs.empty() || !sig.get_pseudo_outs().empty()) + if (!prune && (!sig.p.bulletproofs.empty() || !sig.p.bulletproofs_plus.empty() || !sig.p.rangeSigs.empty() || !sig.p.MGs.empty() || !sig.get_pseudo_outs().empty())) { dest.Key("prunable"); dest.StartObject(); @@ -1571,9 +1574,14 @@ void toJsonValue(rapidjson::Writer& dest, const cryptonote::r { dest.StartObject(); + const uint64_t difficulty_top64 = (info.wide_difficulty >> 64).convert_to(); + const uint64_t cumulative_difficulty_top64 = (info.wide_cumulative_difficulty >> 64).convert_to(); + INSERT_INTO_JSON_OBJECT(dest, height, info.height); INSERT_INTO_JSON_OBJECT(dest, target_height, info.target_height); + INSERT_INTO_JSON_OBJECT(dest, top_block_height, info.top_block_height); INSERT_INTO_JSON_OBJECT(dest, difficulty, info.difficulty); + INSERT_INTO_JSON_OBJECT(dest, difficulty_top64, difficulty_top64); INSERT_INTO_JSON_OBJECT(dest, target, info.target); INSERT_INTO_JSON_OBJECT(dest, tx_count, info.tx_count); INSERT_INTO_JSON_OBJECT(dest, tx_pool_size, info.tx_pool_size); @@ -1588,12 +1596,14 @@ void toJsonValue(rapidjson::Writer& dest, const cryptonote::r INSERT_INTO_JSON_OBJECT(dest, nettype, info.nettype); INSERT_INTO_JSON_OBJECT(dest, top_block_hash, info.top_block_hash); INSERT_INTO_JSON_OBJECT(dest, cumulative_difficulty, info.cumulative_difficulty); + INSERT_INTO_JSON_OBJECT(dest, cumulative_difficulty_top64, cumulative_difficulty_top64); INSERT_INTO_JSON_OBJECT(dest, block_size_limit, info.block_size_limit); INSERT_INTO_JSON_OBJECT(dest, block_weight_limit, info.block_weight_limit); INSERT_INTO_JSON_OBJECT(dest, block_size_median, info.block_size_median); INSERT_INTO_JSON_OBJECT(dest, block_weight_median, info.block_weight_median); INSERT_INTO_JSON_OBJECT(dest, adjusted_time, info.adjusted_time); INSERT_INTO_JSON_OBJECT(dest, start_time, info.start_time); + INSERT_INTO_JSON_OBJECT(dest, version, info.version); dest.EndObject(); } @@ -1605,9 +1615,14 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& inf throw WRONG_TYPE("json object"); } + uint64_t difficulty_top64 = 0; + uint64_t cumulative_difficulty_top64 = 0; + GET_FROM_JSON_OBJECT(val, info.height, height); GET_FROM_JSON_OBJECT(val, info.target_height, target_height); + GET_FROM_JSON_OBJECT(val, info.top_block_height, top_block_height); GET_FROM_JSON_OBJECT(val, info.difficulty, difficulty); + GET_FROM_JSON_OBJECT(val, difficulty_top64, difficulty_top64); GET_FROM_JSON_OBJECT(val, info.target, target); GET_FROM_JSON_OBJECT(val, info.tx_count, tx_count); GET_FROM_JSON_OBJECT(val, info.tx_pool_size, tx_pool_size); @@ -1622,12 +1637,22 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& inf GET_FROM_JSON_OBJECT(val, info.nettype, nettype); GET_FROM_JSON_OBJECT(val, info.top_block_hash, top_block_hash); GET_FROM_JSON_OBJECT(val, info.cumulative_difficulty, cumulative_difficulty); + GET_FROM_JSON_OBJECT(val, cumulative_difficulty_top64, cumulative_difficulty_top64); GET_FROM_JSON_OBJECT(val, info.block_size_limit, block_size_limit); GET_FROM_JSON_OBJECT(val, info.block_weight_limit, block_weight_limit); GET_FROM_JSON_OBJECT(val, info.block_size_median, block_size_median); GET_FROM_JSON_OBJECT(val, info.block_weight_median, block_weight_median); GET_FROM_JSON_OBJECT(val, info.adjusted_time, adjusted_time); GET_FROM_JSON_OBJECT(val, info.start_time, start_time); + GET_FROM_JSON_OBJECT(val, info.version, version); + + info.wide_difficulty = difficulty_top64; + info.wide_difficulty <<= 64; + info.wide_difficulty += info.difficulty; + + info.wide_cumulative_difficulty = cumulative_difficulty_top64; + info.wide_cumulative_difficulty <<= 64; + info.wide_cumulative_difficulty += info.cumulative_difficulty; } void toJsonValue(rapidjson::Writer& dest, const cryptonote::rpc::output_distribution& dist) diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h index a5a915cd8..bd9532a8f 100644 --- a/src/serialization/json_object.h +++ b/src/serialization/json_object.h @@ -281,7 +281,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::error& error); void toJsonValue(rapidjson::Writer& dest, const cryptonote::rpc::BlockHeaderResponse& response); void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResponse& response); -void toJsonValue(rapidjson::Writer& dest, const rct::rctSig& i); +void toJsonValue(rapidjson::Writer& dest, const rct::rctSig& sig, bool prune); void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig); void fromJsonValue(const rapidjson::Value& val, rct::ctkey& key); diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 097353a98..5ad9fb2d6 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -8938,9 +8938,9 @@ bool simple_wallet::submit_transfer(const std::vector &args_) std::string get_tx_key_stream(crypto::secret_key tx_key, std::vector additional_tx_keys) { ostringstream oss; - oss << epee::string_tools::pod_to_hex(tx_key); + oss << epee::string_tools::pod_to_hex(unwrap(unwrap(tx_key))); for (size_t i = 0; i < additional_tx_keys.size(); ++i) - oss << epee::string_tools::pod_to_hex(additional_tx_keys[i]); + oss << epee::string_tools::pod_to_hex(unwrap(unwrap(additional_tx_keys[i]))); return oss.str(); } diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 662658de3..63c023397 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -852,7 +852,7 @@ std::string WalletImpl::integratedAddress(const std::string &payment_id) const std::string WalletImpl::secretViewKey() const { - return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key); + return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().m_view_secret_key))); } std::string WalletImpl::publicViewKey() const @@ -862,7 +862,7 @@ std::string WalletImpl::publicViewKey() const std::string WalletImpl::secretSpendKey() const { - return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_spend_secret_key); + return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().m_spend_secret_key))); } std::string WalletImpl::publicSpendKey() const @@ -1886,9 +1886,9 @@ std::string WalletImpl::getTxKey(const std::string &txid_str) const { clearStatus(); std::ostringstream oss; - oss << epee::string_tools::pod_to_hex(tx_key); + oss << epee::string_tools::pod_to_hex(unwrap(unwrap(tx_key))); for (size_t i = 0; i < additional_tx_keys.size(); ++i) - oss << epee::string_tools::pod_to_hex(additional_tx_keys[i]); + oss << epee::string_tools::pod_to_hex(unwrap(unwrap(additional_tx_keys[i]))); return oss.str(); } else diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 5f8bae7a7..30cea02b4 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1275,6 +1275,11 @@ bool wallet2::has_stagenet_option(const boost::program_options::variables_map& v return command_line::get_arg(vm, options().stagenet); } +bool wallet2::has_proxy_option() const +{ + return !m_proxy.empty(); +} + std::string wallet2::device_name_option(const boost::program_options::variables_map& vm) { return command_line::get_arg(vm, options().hw_device); @@ -1359,12 +1364,15 @@ std::unique_ptr wallet2::make_dummy(const boost::program_options::varia } //---------------------------------------------------------------------------------------------------- -bool wallet2::set_daemon(std::string daemon_address, boost::optional daemon_login, bool trusted_daemon, epee::net_utils::ssl_options_t ssl_options) +bool wallet2::set_daemon(std::string daemon_address, boost::optional daemon_login, bool trusted_daemon, epee::net_utils::ssl_options_t ssl_options, const std::string& proxy) { boost::lock_guard lock(m_daemon_rpc_mutex); if(m_http_client->is_connected()) m_http_client->disconnect(); + CHECK_AND_ASSERT_MES2(m_proxy.empty() || proxy.empty() , "It is not possible to set global proxy (--proxy) and daemon specific proxy together."); + if(m_proxy.empty()) + CHECK_AND_ASSERT_MES(set_proxy(proxy), false, "failed to set proxy address"); const bool changed = m_daemon_address != daemon_address; m_daemon_address = std::move(daemon_address); m_daemon_login = std::move(daemon_login); @@ -1399,7 +1407,8 @@ bool wallet2::set_proxy(const std::string &address) //---------------------------------------------------------------------------------------------------- bool wallet2::init(std::string daemon_address, boost::optional daemon_login, const std::string &proxy_address, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_options_t ssl_options) { - CHECK_AND_ASSERT_MES(set_proxy(proxy_address), false, "failed to set proxy address"); + m_proxy = proxy_address; + CHECK_AND_ASSERT_MES(set_proxy(m_proxy), false, "failed to set proxy address"); m_checkpoints.init_default_checkpoints(m_nettype); m_is_initialized = true; m_upper_transaction_weight_limit = upper_transaction_weight_limit; @@ -3438,6 +3447,8 @@ void wallet2::get_short_chain_history(std::list& ids, uint64_t gra size_t sz = blockchain_size - m_blockchain.offset(); if(!sz) { + if(m_blockchain.size() > m_blockchain.offset()) + ids.push_back(m_blockchain[m_blockchain.offset()]); ids.push_back(m_blockchain.genesis()); return; } @@ -4141,6 +4152,7 @@ void wallet2::update_pool_state(std::vector lock{m_daemon_rpc_mutex}; @@ -5125,7 +5137,7 @@ boost::optional wallet2::get_keys_file_data(const epee: original_address = get_account_address_as_str(m_nettype, false, m_original_address); value.SetString(original_address.c_str(), original_address.length()); json.AddMember("original_address", value, json.GetAllocator()); - original_view_secret_key = epee::string_tools::pod_to_hex(m_original_view_secret_key); + original_view_secret_key = epee::string_tools::pod_to_hex(unwrap(unwrap(m_original_view_secret_key))); value.SetString(original_view_secret_key.c_str(), original_view_secret_key.length()); json.AddMember("original_view_secret_key", value, json.GetAllocator()); } @@ -6655,7 +6667,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass bool r = true; if (use_fs) { - load_from_file(m_wallet_file, cache_file_buf, std::numeric_limits::max()); + r = load_from_file(m_wallet_file, cache_file_buf, std::numeric_limits::max()); THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, m_wallet_file); } @@ -8567,7 +8579,7 @@ bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vecto return sign_multisig_tx_to_file(exported_txs, filename, txids); } //---------------------------------------------------------------------------------------------------- -uint64_t wallet2::estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, bool use_view_tags, uint64_t base_fee, uint64_t fee_quantization_mask) const +uint64_t wallet2::estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, bool use_view_tags, uint64_t base_fee, uint64_t fee_quantization_mask) { if (use_per_byte_fee) { @@ -11218,8 +11230,8 @@ std::vector wallet2::create_transactions_2(std::vector test_ptx.fee; ++fee_tries) { + size_t fee_tries = 0; + do { tx_dsts = tx.get_adjusted_dsts(needed_fee); if (use_rct) @@ -11232,7 +11244,7 @@ std::vector wallet2::create_transactions_2(std::vector test_ptx.fee && ++fee_tries < 10); THROW_WALLET_EXCEPTION_IF(fee_tries == 10, error::wallet_internal_error, "Too many attempts to raise pending tx fee to level of needed fee"); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 76bea94db..3851ae938 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1002,6 +1002,12 @@ private: std::string path() const; + /*! + * \brief has_proxy_option Check the global proxy (--proxy) has been defined or not. + * \return returns bool representing the global proxy (--proxy). + */ + bool has_proxy_option() const; + /*! * \brief verifies given password is correct for default wallet keys file */ @@ -1032,7 +1038,8 @@ private: epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect); bool set_daemon(std::string daemon_address = "http://localhost:8080", boost::optional daemon_login = boost::none, bool trusted_daemon = true, - epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect); + epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, + const std::string &proxy = ""); bool set_proxy(const std::string &address); void stop() { m_run.store(false, std::memory_order_relaxed); m_message_store.stop(); } @@ -1618,7 +1625,7 @@ private: std::vector> estimate_backlog(const std::vector> &fee_levels); std::vector> estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector &fees); - uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, bool use_view_tags, uint64_t base_fee, uint64_t fee_quantization_mask) const; + static uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, bool use_view_tags, uint64_t base_fee, uint64_t fee_quantization_mask); uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1); uint64_t get_base_fee(uint32_t priority); uint64_t get_base_fee(); @@ -1881,6 +1888,7 @@ private: cryptonote::account_base m_account; boost::optional m_daemon_login; std::string m_daemon_address; + std::string m_proxy; std::string m_wallet_file; std::string m_keys_file; std::string m_mms_file; @@ -2494,7 +2502,7 @@ namespace boost bool use_bulletproofs = x.rct_config.range_proof_type != rct::RangeProofBorromean; a & use_bulletproofs; if (!typename Archive::is_saving()) - x.rct_config = { use_bulletproofs ? rct::RangeProofBulletproof : rct::RangeProofBorromean, 0 }; + x.rct_config = { use_bulletproofs ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean, 0 }; return; } a & x.rct_config; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index f6d5589a8..035c74318 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -81,6 +81,10 @@ namespace const command_line::arg_descriptor arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"}; const command_line::arg_descriptor arg_prompt_for_password = {"prompt-for-password", "Prompts for password when not provided", false}; const command_line::arg_descriptor arg_no_initial_sync = {"no-initial-sync", "Skips the initial sync before listening for connections", false}; + const command_line::arg_descriptor arg_rpc_max_connections_per_public_ip = {"rpc-max-connections-per-public-ip", "Max RPC connections per public IP permitted", DEFAULT_RPC_MAX_CONNECTIONS_PER_PUBLIC_IP}; + const command_line::arg_descriptor arg_rpc_max_connections_per_private_ip = {"rpc-max-connections-per-private-ip", "Max RPC connections per private and localhost IP permitted", DEFAULT_RPC_MAX_CONNECTIONS_PER_PRIVATE_IP}; + const command_line::arg_descriptor arg_rpc_max_connections = {"rpc-max-connections", "Max RPC connections permitted", DEFAULT_RPC_MAX_CONNECTIONS}; + const command_line::arg_descriptor arg_rpc_response_soft_limit = {"rpc-response-soft-limit", "Max response bytes that can be queued, enforced at next response attempt", DEFAULT_RPC_SOFT_LIMIT_SIZE}; constexpr const char default_rpc_username[] = "monero"; @@ -245,7 +249,7 @@ namespace tools ); std::string temp = "salvium-wallet-rpc." + bind_port + ".login"; - rpc_login_file = tools::private_file::create(temp); + rpc_login_file = tools::private_file::drop_and_recreate(temp); if (!rpc_login_file.handle()) { LOG_ERROR(tr("Failed to create file ") << temp << tr(". Check permissions or remove file")); @@ -277,13 +281,30 @@ namespace tools check_background_mining(); + const auto max_connections_public = command_line::get_arg(vm, arg_rpc_max_connections_per_public_ip); + const auto max_connections_private = command_line::get_arg(vm, arg_rpc_max_connections_per_private_ip); + const auto max_connections = command_line::get_arg(vm, arg_rpc_max_connections); + + if (max_connections < max_connections_public) + { + MFATAL(arg_rpc_max_connections_per_public_ip.name << " is bigger than " << arg_rpc_max_connections.name); + return false; + } + if (max_connections < max_connections_private) + { + MFATAL(arg_rpc_max_connections_per_private_ip.name << " is bigger than " << arg_rpc_max_connections.name); + return false; + } + m_net_server.set_threads_prefix("RPC"); auto rng = [](size_t len, uint8_t *ptr) { return crypto::rand(len, ptr); }; return epee::http_server_impl_base::init( rng, std::move(bind_port), std::move(rpc_config->bind_ip), std::move(rpc_config->bind_ipv6_address), std::move(rpc_config->use_ipv6), std::move(rpc_config->require_ipv4), std::move(rpc_config->access_control_origins), std::move(http_login), - std::move(rpc_config->ssl_options) + std::move(rpc_config->ssl_options), + max_connections_public, max_connections_private, max_connections, + command_line::get_arg(vm, arg_rpc_response_soft_limit) ); } //------------------------------------------------------------------------------------------------------------------------------ @@ -1346,9 +1367,9 @@ namespace tools res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx))); if (req.get_tx_keys) { - res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key)); + res.tx_key_list.push_back(epee::string_tools::pod_to_hex(unwrap(unwrap(ptx.tx_key)))); for (const crypto::secret_key& additional_tx_key : ptx.additional_tx_keys) - res.tx_key_list.back() += epee::string_tools::pod_to_hex(additional_tx_key); + res.tx_key_list.back() += epee::string_tools::pod_to_hex(unwrap(unwrap(additional_tx_key))); } } @@ -3563,7 +3584,7 @@ namespace tools if (!wal) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = "Failed to open wallet"; + er.message = "Failed to open wallet : " + (!er.message.empty() ? er.message : "Unknown."); return false; } @@ -4535,6 +4556,13 @@ namespace tools er.message = "Command unavailable in restricted mode."; return false; } + + if (m_wallet->has_proxy_option() && !req.proxy.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_PROXY_ALREADY_DEFINED; + er.message = "It is not possible to set daemon specific proxy when --proxy is defined."; + return false; + } std::vector> ssl_allowed_fingerprints; ssl_allowed_fingerprints.reserve(req.ssl_allowed_fingerprints.size()); @@ -4578,7 +4606,7 @@ namespace tools if (!req.username.empty() || !req.password.empty()) daemon_login.emplace(req.username, req.password); - if (!m_wallet->set_daemon(req.address, daemon_login, req.trusted, std::move(ssl_options))) + if (!m_wallet->set_daemon(req.address, daemon_login, req.trusted, std::move(ssl_options), req.proxy)) { er.code = WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION; er.message = std::string("Unable to set daemon"); @@ -4639,6 +4667,29 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_get_default_fee_priority(const wallet_rpc::COMMAND_RPC_GET_DEFAULT_FEE_PRIORITY::request& req, wallet_rpc::COMMAND_RPC_GET_DEFAULT_FEE_PRIORITY::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); + try + { + uint32_t priority = m_wallet->adjust_priority(0); + if (priority == 0) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to get adjusted fee priority"; + return false; + } + res.priority = priority; + } + catch (const std::exception& e) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to get adjusted fee priority"; + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx) { res.version = WALLET_RPC_VERSION; @@ -4868,6 +4919,10 @@ int main(int argc, char** argv) { command_line::add_arg(desc_params, arg_prompt_for_password); command_line::add_arg(desc_params, arg_rpc_client_secret_key); command_line::add_arg(desc_params, arg_no_initial_sync); + command_line::add_arg(desc_params, arg_rpc_max_connections_per_public_ip); + command_line::add_arg(desc_params, arg_rpc_max_connections_per_private_ip); + command_line::add_arg(desc_params, arg_rpc_max_connections); + command_line::add_arg(desc_params, arg_rpc_response_soft_limit); daemonizer::init_options(hidden_options, desc_params); desc_params.add(hidden_options); diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 21a61c21d..b7b1ca684 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -160,6 +160,7 @@ namespace tools MAP_JON_RPC_WE("set_log_level", on_set_log_level, wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL) MAP_JON_RPC_WE("set_log_categories", on_set_log_categories, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES) MAP_JON_RPC_WE("estimate_tx_size_and_weight", on_estimate_tx_size_and_weight, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT) + MAP_JON_RPC_WE("get_default_fee_priority", on_get_default_fee_priority, wallet_rpc::COMMAND_RPC_GET_DEFAULT_FEE_PRIORITY) MAP_JON_RPC_WE("get_version", on_get_version, wallet_rpc::COMMAND_RPC_GET_VERSION) END_JSON_RPC_MAP() END_URI_MAP2() @@ -252,6 +253,7 @@ namespace tools bool on_set_log_level(const wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_set_log_categories(const wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_estimate_tx_size_and_weight(const wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::request& req, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); + bool on_get_default_fee_priority(const wallet_rpc::COMMAND_RPC_GET_DEFAULT_FEE_PRIORITY::request& req, wallet_rpc::COMMAND_RPC_GET_DEFAULT_FEE_PRIORITY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); //json rpc v2 diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 3c1107176..a51ba8680 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -47,7 +47,7 @@ // advance which version they will stop working with // Don't go over 32767 for any of these #define WALLET_RPC_VERSION_MAJOR 1 -#define WALLET_RPC_VERSION_MINOR 27 +#define WALLET_RPC_VERSION_MINOR 28 #define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR) namespace tools @@ -2667,6 +2667,7 @@ namespace wallet_rpc std::string ssl_ca_file; std::vector ssl_allowed_fingerprints; bool ssl_allow_any_cert; + std::string proxy; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(address) @@ -2679,6 +2680,7 @@ namespace wallet_rpc KV_SERIALIZE(ssl_ca_file) KV_SERIALIZE(ssl_allowed_fingerprints) KV_SERIALIZE_OPT(ssl_allow_any_cert, false) + KV_SERIALIZE_OPT(proxy, (std::string)"") END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init request; @@ -2765,5 +2767,89 @@ namespace wallet_rpc typedef epee::misc_utils::struct_init response; }; + struct COMMAND_RPC_SETUP_BACKGROUND_SYNC + { + struct request_t + { + std::string background_sync_type; + std::string wallet_password; + std::string background_cache_password; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(background_sync_type) + KV_SERIALIZE(wallet_password) + KV_SERIALIZE_OPT(background_cache_password, (std::string)"") + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init request; + + struct response_t + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init response; + }; + + struct COMMAND_RPC_START_BACKGROUND_SYNC + { + struct request_t + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init request; + + struct response_t + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init response; + }; + + struct COMMAND_RPC_STOP_BACKGROUND_SYNC + { + struct request_t + { + std::string wallet_password; + std::string seed; + std::string seed_offset; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(wallet_password) + KV_SERIALIZE_OPT(seed, (std::string)"") + KV_SERIALIZE_OPT(seed_offset, (std::string)"") + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init request; + + struct response_t + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init response; + }; + + struct COMMAND_RPC_GET_DEFAULT_FEE_PRIORITY + { + struct request_t + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init request; + + struct response_t + { + uint32_t priority; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(priority) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init response; + }; } } diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index 734229380..4756c191c 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -79,3 +79,7 @@ #define WALLET_RPC_ERROR_CODE_ZERO_AMOUNT -46 #define WALLET_RPC_ERROR_CODE_INVALID_SIGNATURE_TYPE -47 #define WALLET_RPC_ERROR_CODE_DISABLED -48 +#define WALLET_RPC_ERROR_CODE_PROXY_ALREADY_DEFINED -49 +#define WALLET_RPC_ERROR_CODE_NONZERO_UNLOCK_TIME -50 +#define WALLET_RPC_ERROR_CODE_IS_BACKGROUND_WALLET -51 +#define WALLET_RPC_ERROR_CODE_IS_BACKGROUND_SYNCING -52