diff options
131 files changed, 1865 insertions, 703 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 037b9cb50..54bc948e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -187,7 +187,7 @@ if(NOT MANUAL_SUBMODULES) if (upToDate) message(STATUS "Submodule '${relative_path}' is up-to-date") else() - message(FATAL_ERROR "Submodule '${relative_path}' is not up-to-date. Please update with\ngit submodule update --init --force ${relative_path}\nor run cmake with -DMANUAL_SUBMODULES=1") + message(FATAL_ERROR "Submodule '${relative_path}' is not up-to-date. Please update all submodules with\ngit submodule update --init --force\nor run cmake with -DMANUAL_SUBMODULES=1\n") endif() endfunction () @@ -246,6 +246,12 @@ enable_testing() option(BUILD_DOCUMENTATION "Build the Doxygen documentation." ON) option(BUILD_TESTS "Build tests." OFF) +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set(DEFAULT_BUILD_DEBUG_UTILITIES ON) +else() + set(DEFAULT_BUILD_DEBUG_UTILITIES OFF) +endif() +option(BUILD_DEBUG_UTILITIES "Build debug utilities." DEFAULT_BUILD_DEBUG_UTILITIES) # Check whether we're on a 32-bit or 64-bit system if(CMAKE_SIZEOF_VOID_P EQUAL "8") @@ -672,8 +678,11 @@ else() add_cxx_flag_if_supported(-fstack-clash-protection CXX_SECURITY_FLAGS) endif() - add_c_flag_if_supported(-mmitigate-rop C_SECURITY_FLAGS) - add_cxx_flag_if_supported(-mmitigate-rop CXX_SECURITY_FLAGS) + # Removed in GCC 9.1 (or before ?), but still accepted, so spams the output + if (NOT (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 9.1)) + add_c_flag_if_supported(-mmitigate-rop C_SECURITY_FLAGS) + add_cxx_flag_if_supported(-mmitigate-rop CXX_SECURITY_FLAGS) + endif() # linker if (NOT WIN32) @@ -995,7 +1004,16 @@ add_subdirectory(contrib) add_subdirectory(src) if(BUILD_TESTS) + message(STATUS "Building tests") add_subdirectory(tests) +else() + message(STATUS "Not building tests") +endif() + +if(BUILD_DEBUG_UTILITIES) + message(STATUS "Building debug utilities") +else() + message(STATUS "Not building debug utilities") endif() if(BUILD_DOCUMENTATION) @@ -137,7 +137,7 @@ Monero uses a fixed-schedule software upgrade (hard fork) mechanism to implement Dates are provided in the format YYYY-MM-DD. -| Software upgrade block height | Date | Fork version | Minimum Monero version | Recommended Monero version | Details | +| Software upgrade block height | Date | Fork version | Minimum Monero version | Recommended Monero version | Details | | ------------------------------ | -----------| ----------------- | ---------------------- | -------------------------- | ---------------------------------------------------------------------------------- | | 1009827 | 2016-03-22 | v2 | v0.9.4 | v0.9.4 | Allow only >= ringsize 3, blocktime = 120 seconds, fee-free blocksize 60 kb | | 1141317 | 2016-09-21 | v3 | v0.9.4 | v0.10.0 | Splits coinbase into denominations | @@ -147,8 +147,8 @@ Dates are provided in the format YYYY-MM-DD. | 1546000 | 2018-04-06 | v7 | v0.12.0.0 | v0.12.3.0 | Cryptonight variant 1, ringsize >= 7, sorted inputs | 1685555 | 2018-10-18 | v8 | v0.13.0.0 | v0.13.0.4 | max transaction size at half the penalty free block size, bulletproofs enabled, cryptonight variant 2, fixed ringsize [11](https://youtu.be/KOO5S4vxi0o) | 1686275 | 2018-10-19 | v9 | v0.13.0.0 | v0.13.0.4 | bulletproofs required -| 1788000 | 2019-03-09 | v10 | v0.14.0.0 | v0.14.0.2 | New PoW based on Cryptonight-R, new block weight algorithm, slightly more efficient RingCT format -| 1788720 | 2019-03-10 | v11 | v0.14.0.0 | v0.14.0.2 | forbid old RingCT transaction format +| 1788000 | 2019-03-09 | v10 | v0.14.0.0 | v0.14.1.2 | New PoW based on Cryptonight-R, new block weight algorithm, slightly more efficient RingCT format +| 1788720 | 2019-03-10 | v11 | v0.14.0.0 | v0.14.1.2 | forbid old RingCT transaction format | XXXXXXX | 2019-10-XX | XX | XXXXXXXXX | XXXXXXXXX | X X's indicate that these details have not been determined as of commit date. @@ -304,7 +304,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch ( ```bash git clone https://github.com/monero-project/monero.git cd monero - git checkout tags/v0.14.1.0 + git checkout tags/v0.14.1.2 ``` * Build: @@ -421,10 +421,10 @@ application. cd monero ``` -* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.14.1.0'. If you don't care about the version and just want binaries from master, skip this step: +* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.14.1.2'. If you don't care about the version and just want binaries from master, skip this step: ```bash - git checkout v0.14.1.0 + git checkout v0.14.1.2 ``` * If you are on a 64-bit system, run: diff --git a/cmake/GenVersion.cmake b/cmake/GenVersion.cmake index 1ea5b209c..b2ccfbc34 100644 --- a/cmake/GenVersion.cmake +++ b/cmake/GenVersion.cmake @@ -29,7 +29,7 @@ # Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers # Check what commit we're on -execute_process(COMMAND "${GIT}" rev-parse --short HEAD RESULT_VARIABLE RET OUTPUT_VARIABLE COMMIT OUTPUT_STRIP_TRAILING_WHITESPACE) +execute_process(COMMAND "${GIT}" rev-parse --short=9 HEAD RESULT_VARIABLE RET OUTPUT_VARIABLE COMMIT OUTPUT_STRIP_TRAILING_WHITESPACE) if(RET) # Something went wrong, set the version tag to -unknown @@ -38,6 +38,7 @@ if(RET) set(VERSIONTAG "unknown") configure_file("src/version.cpp.in" "${TO}") else() + string(SUBSTRING ${COMMIT} 0 9 COMMIT) message(STATUS "You are currently on commit ${COMMIT}") # Get all the tags diff --git a/contrib/depends/Makefile b/contrib/depends/Makefile index 6ab602ac0..ec0e4cfae 100644 --- a/contrib/depends/Makefile +++ b/contrib/depends/Makefile @@ -217,4 +217,6 @@ download-win: @$(MAKE) -s HOST=x86_64-w64-mingw32 download-one download: download-osx download-linux download-win + $(foreach package,$(all_packages),$(eval $(call ext_add_stages,$(package)))) + .PHONY: install cached download-one download-osx download-linux download-win download check-packages check-sources diff --git a/contrib/depends/funcs.mk b/contrib/depends/funcs.mk index 15e404e42..469144361 100644 --- a/contrib/depends/funcs.mk +++ b/contrib/depends/funcs.mk @@ -213,6 +213,14 @@ $(1): | $($(1)_cached_checksum) endef +stages = fetched extracted preprocessed configured built staged postprocessed cached cached_checksum + +define ext_add_stages +$(foreach stage,$(stages), + $(1)_$(stage): $($(1)_$(stage)) + .PHONY: $(1)_$(stage)) +endef + # These functions create the build targets for each package. They must be # broken down into small steps so that each part is done for all packages # before moving on to the next step. Otherwise, a package's info diff --git a/contrib/depends/packages/eudev.mk b/contrib/depends/packages/eudev.mk index e754c0f20..a7795b777 100644 --- a/contrib/depends/packages/eudev.mk +++ b/contrib/depends/packages/eudev.mk @@ -23,3 +23,7 @@ endef define $(package)_stage_cmds $(MAKE) DESTDIR=$($(package)_staging_dir) install endef + +define $(package)_postprocess_cmds + rm lib/*.la +endef diff --git a/contrib/depends/packages/expat.mk b/contrib/depends/packages/expat.mk index bd2cea1b6..ef81636a2 100644 --- a/contrib/depends/packages/expat.mk +++ b/contrib/depends/packages/expat.mk @@ -6,6 +6,7 @@ $(package)_sha256_hash=03ad85db965f8ab2d27328abcf0bc5571af6ec0a414874b2066ee3fdd define $(package)_set_vars $(package)_config_opts=--enable-static +$(package)_config_opts=--disable-shared $(package)_config_opts+=--prefix=$(host_prefix) endef @@ -20,3 +21,8 @@ endef define $(package)_stage_cmds $(MAKE) DESTDIR=$($(package)_staging_dir) install endef + +define $(package)_postprocess_cmds + rm lib/*.la +endef + diff --git a/contrib/depends/packages/hidapi.mk b/contrib/depends/packages/hidapi.mk index 1c43e525a..a27df04fa 100644 --- a/contrib/depends/packages/hidapi.mk +++ b/contrib/depends/packages/hidapi.mk @@ -1,8 +1,8 @@ package=hidapi -$(package)_version=0.8.0-rc1 -$(package)_download_path=https://github.com/signal11/hidapi/archive +$(package)_version=0.9.0 +$(package)_download_path=https://github.com/libusb/hidapi/archive $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=3c147200bf48a04c1e927cd81589c5ddceff61e6dac137a605f6ac9793f4af61 +$(package)_sha256_hash=630ee1834bdd5c5761ab079fd04f463a89585df8fcae51a7bfe4229b1e02a652 $(package)_linux_dependencies=libusb eudev define $(package)_set_vars @@ -28,3 +28,8 @@ endef define $(package)_stage_cmds $(MAKE) DESTDIR=$($(package)_staging_dir) install endef + +define $(package)_postprocess_cmds + rm lib/*.la +endef + diff --git a/contrib/depends/packages/icu4c.mk b/contrib/depends/packages/icu4c.mk index 2b3845488..dc089bbbe 100644 --- a/contrib/depends/packages/icu4c.mk +++ b/contrib/depends/packages/icu4c.mk @@ -21,11 +21,6 @@ define $(package)_config_cmds $(MAKE) $($(package)_build_opts) endef -#define $(package)_build_cmds -# cd source &&\ - $(MAKE) $($((package)_build_opts) `nproc` -#endef - define $(package)_stage_cmds cd buildb &&\ $(MAKE) $($(package)_build_opts) DESTDIR=$($(package)_staging_dir) install lib/* diff --git a/contrib/depends/packages/ldns.mk b/contrib/depends/packages/ldns.mk index 0b7c3806a..ea4902170 100644 --- a/contrib/depends/packages/ldns.mk +++ b/contrib/depends/packages/ldns.mk @@ -6,8 +6,8 @@ $(package)_sha256_hash=8b88e059452118e8949a2752a55ce59bc71fa5bc414103e17f5b6b06f $(package)_dependencies=openssl define $(package)_set_vars - $(package)_config_opts=--disable-shared --enable-static --disable-dane-ta-usage --with-drill - $(package)_config_opts=--with-ssl=$(host_prefix) + $(package)_config_opts=--disable-shared --enable-static --with-drill + $(package)_config_opts+=--with-ssl=$(host_prefix) $(package)_config_opts_release=--disable-debug-mode $(package)_config_opts_linux=--with-pic endef @@ -25,4 +25,6 @@ define $(package)_stage_cmds endef define $(package)_postprocess_cmds + rm lib/*.la endef + diff --git a/contrib/depends/packages/libiconv.mk b/contrib/depends/packages/libiconv.mk index dbcb28141..d4995c1b7 100644 --- a/contrib/depends/packages/libiconv.mk +++ b/contrib/depends/packages/libiconv.mk @@ -28,3 +28,7 @@ endef define $(package)_stage_cmds $(MAKE) DESTDIR=$($(package)_staging_dir) install endef + +define $(package)_postprocess_cmds + rm lib/*.la +endef diff --git a/contrib/depends/packages/ncurses.mk b/contrib/depends/packages/ncurses.mk new file mode 100644 index 000000000..4e06c00d9 --- /dev/null +++ b/contrib/depends/packages/ncurses.mk @@ -0,0 +1,58 @@ +package=ncurses +$(package)_version=6.1 +$(package)_download_path=https://ftp.gnu.org/gnu/ncurses +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=aa057eeeb4a14d470101eff4597d5833dcef5965331be3528c08d99cebaa0d17 + +define $(package)_set_vars + $(package)_build_opts=CC="$($(package)_cc)" + $(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" + $(package)_config_env_darwin=RANLIB="$(host_prefix)/native/bin/x86_64-apple-darwin11-ranlib" AR="$(host_prefix)/native/bin/x86_64-apple-darwin11-ar" CC="$(host_prefix)/native/bin/$($(package)_cc)" + $(package)_config_opts=--prefix=$(host_prefix) + $(package)_config_opts+=--disable-shared + $(package)_config_opts+=--with-build-cc=gcc + $(package)_config_opts+=--without-debug + $(package)_config_opts+=--without-ada + $(package)_config_opts+=--without-cxx-binding + $(package)_config_opts+=--without-cxx + $(package)_config_opts+=--without-ticlib + $(package)_config_opts+=--without-tic + $(package)_config_opts+=--without-progs + $(package)_config_opts+=--without-tests + $(package)_config_opts+=--without-tack + $(package)_config_opts+=--without-manpages + $(package)_config_opts+=--disable-tic-depends + $(package)_config_opts+=--disable-big-strings + $(package)_config_opts+=--disable-ext-colors + $(package)_config_opts+=--enable-pc-files + $(package)_config_opts+=--host=$(HOST) + $(pacakge)_config_opts+=--without-shared + $(pacakge)_config_opts+=--without-pthread + $(pacakge)_config_opts+=--disable-rpath + $(pacakge)_config_opts+=--disable-colorfgbg + $(pacakge)_config_opts+=--disable-ext-colors + $(pacakge)_config_opts+=--disable-ext-mouse + $(pacakge)_config_opts+=--disable-symlinks + $(pacakge)_config_opts+=--enable-warnings + $(pacakge)_config_opts+=--enable-assertions + $(pacakge)_config_opts+=--disable-home-terminfo + $(pacakge)_config_opts+=--enable-database + $(pacakge)_config_opts+=--enable-sp-funcs + $(pacakge)_config_opts+=--enable-term-driver + $(pacakge)_config_opts+=--enable-interop + $(pacakge)_config_opts+=--enable-widec + $(package)_build_opts=CFLAGS="$($(package)_cflags) $($(package)_cppflags) -fPIC" +endef + +define $(package)_config_cmds + ./configure $($(package)_config_opts) +endef + +define $(package)_build_cmds + $(MAKE) $($(package)_build_opts) V=1 +endef + +define $(package)_stage_cmds + $(MAKE) install DESTDIR=$($(package)_staging_dir) +endef + diff --git a/contrib/depends/packages/openssl.mk b/contrib/depends/packages/openssl.mk index e920b4409..e39dc1d04 100644 --- a/contrib/depends/packages/openssl.mk +++ b/contrib/depends/packages/openssl.mk @@ -1,8 +1,8 @@ package=openssl -$(package)_version=1.0.2q +$(package)_version=1.0.2r $(package)_download_path=https://www.openssl.org/source $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=5744cfcbcec2b1b48629f7354203bc1e5e9b5466998bbccc5b5fcde3b18eb684 +$(package)_sha256_hash=ae51d08bba8a83958e894946f15303ff894d75c2b8bbd44a852b64e3fe11d0d6 define $(package)_set_vars $(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index a38819337..562f4f7d6 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -1,20 +1,16 @@ -packages:=boost openssl zeromq cppzmq expat ldns cppzmq readline libiconv hidapi protobuf libusb +packages:=boost openssl zeromq cppzmq expat ldns libiconv hidapi protobuf libusb native_packages := native_ccache native_protobuf darwin_native_packages = native_biplist native_ds_store native_mac_alias -darwin_packages = sodium-darwin +darwin_packages = sodium-darwin ncurses readline -linux_packages = eudev +linux_packages = eudev ncurses readline unwind sodium qt_packages = qt ifeq ($(build_tests),ON) packages += gtest endif -ifeq ($(host_os),linux) -packages += unwind -packages += sodium -endif ifeq ($(host_os),mingw32) packages += icu4c packages += sodium diff --git a/contrib/depends/packages/protobuf.mk b/contrib/depends/packages/protobuf.mk index 54d3fd924..81fa78a3f 100644 --- a/contrib/depends/packages/protobuf.mk +++ b/contrib/depends/packages/protobuf.mk @@ -25,5 +25,7 @@ define $(package)_stage_cmds endef define $(package)_postprocess_cmds - rm lib/libprotoc.a + rm lib/libprotoc.a &&\ + rm lib/*.la endef + diff --git a/contrib/depends/packages/readline.mk b/contrib/depends/packages/readline.mk index afefc7f07..0e2100749 100644 --- a/contrib/depends/packages/readline.mk +++ b/contrib/depends/packages/readline.mk @@ -3,19 +3,19 @@ $(package)_version=8.0 $(package)_download_path=https://ftp.gnu.org/gnu/readline $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_sha256_hash=e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461 +$(package)_dependencies=ncurses define $(package)_set_vars - $(package)_build_opts=CC="$($(package)_cc)" - $(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" $(package)_config_opts=--prefix=$(host_prefix) - $(package)_config_opts+=--disable-shared --enable-multibye --without-purify --without-curses + $(package)_config_opts+=--exec-prefix=$(host_prefix) + $(package)_config_opts+=--host=$(HOST) + $(package)_config_opts+=--disable-shared --with-curses $(package)_config_opts_release=--disable-debug-mode + $(package)_config_opts_darwin+=RANLIB="$(host_prefix)/native/bin/x86_64-apple-darwin11-ranlib" AR="$(host_prefix)/native/bin/x86_64-apple-darwin11-ar" CC="$(host_prefix)/native/bin/$($(package)_cc)" $(package)_build_opts=CFLAGS="$($(package)_cflags) $($(package)_cppflags) -fPIC" endef define $(package)_config_cmds - export bash_cv_have_mbstate_t=yes &&\ - export bash_cv_wcwidth_broken=yes &&\ ./configure $($(package)_config_opts) endef @@ -24,6 +24,6 @@ define $(package)_build_cmds endef define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install + $(MAKE) install DESTDIR=$($(package)_staging_dir) prefix=$(host_prefix) exec-prefix=$(host_prefix) endef diff --git a/contrib/depends/packages/sodium-darwin.mk b/contrib/depends/packages/sodium-darwin.mk index 8b6ee3f1d..9f11a9426 100644 --- a/contrib/depends/packages/sodium-darwin.mk +++ b/contrib/depends/packages/sodium-darwin.mk @@ -23,3 +23,8 @@ endef define $(package)_stage_cmds $(MAKE) DESTDIR=$($(package)_staging_dir) install endef + +define $(package)_postprocess_cmds + rm lib/*.la +endef + diff --git a/contrib/depends/packages/sodium.mk b/contrib/depends/packages/sodium.mk index 06aa8f874..b71f4383e 100644 --- a/contrib/depends/packages/sodium.mk +++ b/contrib/depends/packages/sodium.mk @@ -23,3 +23,8 @@ endef define $(package)_stage_cmds $(MAKE) DESTDIR=$($(package)_staging_dir) install endef + +define $(package)_postprocess_cmds + rm lib/*.la +endef + diff --git a/contrib/depends/packages/unwind.mk b/contrib/depends/packages/unwind.mk index 543f868a5..fddbd0561 100644 --- a/contrib/depends/packages/unwind.mk +++ b/contrib/depends/packages/unwind.mk @@ -19,4 +19,6 @@ define $(package)_stage_cmds endef define $(package)_postprocess_cmds + rm lib/*.la endef + diff --git a/contrib/depends/packages/zeromq.mk b/contrib/depends/packages/zeromq.mk index 01146c26f..f17dbeebe 100644 --- a/contrib/depends/packages/zeromq.mk +++ b/contrib/depends/packages/zeromq.mk @@ -30,5 +30,7 @@ define $(package)_stage_cmds endef define $(package)_postprocess_cmds - rm -rf bin share + rm -rf bin share &&\ + rm lib/*.la endef + diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in index 6b5434751..ee0407a5e 100644 --- a/contrib/depends/toolchain.cmake.in +++ b/contrib/depends/toolchain.cmake.in @@ -8,6 +8,7 @@ OPTION(BUILD_TESTS "Build tests." OFF) SET(STATIC ON) SET(UNBOUND_STATIC ON) +SET(ARCH "default") SET(BUILD_TESTS @build_tests@) SET(TREZOR_DEBUG @build_tests@) @@ -20,6 +21,8 @@ SET(ENV{PKG_CONFIG_PATH} @prefix@/lib/pkgconfig) SET(LRELEASE_PATH @prefix@/native/bin CACHE FILEPATH "path to lrelease" FORCE) SET(Readline_ROOT_DIR @prefix@) +SET(Readline_INCLUDE_DIR @prefix@/include) +SET(Termcap_LIBRARY @prefix@/lib/libncurses.a) SET(LIBUNWIND_INCLUDE_DIR @prefix@/include) SET(LIBUNWIND_LIBRARIES @prefix@/lib/libunwind.a) diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h index 374a28a2e..c1aa0fe5f 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.h +++ b/contrib/epee/include/net/abstract_tcp_server2.h @@ -70,7 +70,7 @@ namespace net_utils struct i_connection_filter { - virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address)=0; + virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address, time_t *t = NULL)=0; protected: virtual ~i_connection_filter(){} }; diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 821594355..0721366aa 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -54,6 +54,9 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "net" +#define AGGRESSIVE_TIMEOUT_THRESHOLD 120 // sockets +#define NEW_CONNECTION_TIMEOUT_LOCAL 1200000 // 2 minutes +#define NEW_CONNECTION_TIMEOUT_REMOTE 10000 // 10 seconds #define DEFAULT_TIMEOUT_MS_LOCAL 1800000 // 30 minutes #define DEFAULT_TIMEOUT_MS_REMOTE 300000 // 5 minutes #define TIMEOUT_EXTRA_MS_PER_BYTE 0.2 @@ -189,7 +192,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) m_protocol_handler.after_init_connection(); - reset_timer(get_default_timeout(), false); + reset_timer(boost::posix_time::milliseconds(m_local ? NEW_CONNECTION_TIMEOUT_LOCAL : NEW_CONNECTION_TIMEOUT_REMOTE), false); // first read on the raw socket to detect SSL for the server buffer_ssl_init_fill = 0; @@ -324,12 +327,14 @@ PRAGMA_WARNING_DISABLE_VS(4355) if (!e) { + double current_speed_down; { CRITICAL_REGION_LOCAL(m_throttle_speed_in_mutex); m_throttle_speed_in.handle_trafic_exact(bytes_transferred); - context.m_current_speed_down = m_throttle_speed_in.get_current_speed(); - context.m_max_speed_down = std::max(context.m_max_speed_down, context.m_current_speed_down); + current_speed_down = m_throttle_speed_in.get_current_speed(); } + context.m_current_speed_down = current_speed_down; + context.m_max_speed_down = std::max(context.m_max_speed_down, current_speed_down); { CRITICAL_REGION_LOCAL( epee::net_utils::network_throttle_manager::network_throttle_manager::m_lock_get_global_throttle_in ); @@ -599,12 +604,14 @@ PRAGMA_WARNING_DISABLE_VS(4355) return false; if(m_was_shutdown) return false; + double current_speed_up; { CRITICAL_REGION_LOCAL(m_throttle_speed_out_mutex); m_throttle_speed_out.handle_trafic_exact(cb); - context.m_current_speed_up = m_throttle_speed_out.get_current_speed(); - context.m_max_speed_up = std::max(context.m_max_speed_up, context.m_current_speed_up); + current_speed_up = m_throttle_speed_out.get_current_speed(); } + context.m_current_speed_up = current_speed_up; + context.m_max_speed_up = std::max(context.m_max_speed_up, current_speed_up); //_info("[sock " << socket().native_handle() << "] SEND " << cb); context.m_last_send = time(NULL); @@ -691,7 +698,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) { unsigned count; try { count = host_count(m_host); } catch (...) { count = 0; } - const unsigned shift = std::min(std::max(count, 1u) - 1, 8u); + const unsigned shift = get_state().sock_count > AGGRESSIVE_TIMEOUT_THRESHOLD ? std::min(std::max(count, 1u) - 1, 8u) : 0; boost::posix_time::milliseconds timeout(0); if (m_local) timeout = boost::posix_time::milliseconds(DEFAULT_TIMEOUT_MS_LOCAL >> shift); @@ -730,8 +737,6 @@ PRAGMA_WARNING_DISABLE_VS(4355) template<class t_protocol_handler> void connection<t_protocol_handler>::reset_timer(boost::posix_time::milliseconds ms, bool add) { - if (m_connection_type != e_connection_type_RPC) - return; MTRACE("Setting " << ms << " expiry"); auto self = safe_shared_from_this(); if(!self) diff --git a/contrib/epee/include/net/connection_basic.hpp b/contrib/epee/include/net/connection_basic.hpp index d3f5f4f24..2acc6cdda 100644 --- a/contrib/epee/include/net/connection_basic.hpp +++ b/contrib/epee/include/net/connection_basic.hpp @@ -186,8 +186,6 @@ class connection_basic { // not-templated base class for rapid developmet of som void sleep_before_packet(size_t packet_size, int phase, int q_len); // execute a sleep ; phase is not really used now(?) static void save_limit_to_file(int limit); ///< for dr-monero static double get_sleep_time(size_t cb); - - static void set_save_graph(bool save_graph); }; } // nameserver diff --git a/contrib/epee/include/net/net_fwd.h b/contrib/epee/include/net/net_fwd.h new file mode 100644 index 000000000..ba4fe6259 --- /dev/null +++ b/contrib/epee/include/net/net_fwd.h @@ -0,0 +1,38 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +namespace epee +{ + namespace net_utils + { + struct ssl_authentication_t; + class ssl_options_t; + } +} diff --git a/contrib/epee/include/net/net_helper.h b/contrib/epee/include/net/net_helper.h index 6387c4c34..e315555fc 100644 --- a/contrib/epee/include/net/net_helper.h +++ b/contrib/epee/include/net/net_helper.h @@ -193,7 +193,6 @@ namespace net_utils return CONNECT_FAILURE; } } - m_ssl_options.support = ssl_support_t::e_ssl_support_enabled; } return CONNECT_SUCCESS; }else @@ -223,7 +222,6 @@ namespace net_utils return false; if (m_ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect) { - m_ssl_options.support = epee::net_utils::ssl_support_t::e_ssl_support_enabled; if (try_connect_result == CONNECT_NO_SSL) { MERROR("SSL handshake failed on an autodetect connection, reconnecting without SSL"); @@ -396,7 +394,7 @@ namespace net_utils if (!m_connected || !m_ssl_socket->next_layer().is_open()) return false; if (ssl) - *ssl = m_ssl_options.support == ssl_support_t::e_ssl_support_enabled; + *ssl = m_ssl_options.support != ssl_support_t::e_ssl_support_disabled; return true; } @@ -651,7 +649,7 @@ namespace net_utils bool write(const void* data, size_t sz, boost::system::error_code& ec) { bool success; - if(m_ssl_options.support == ssl_support_t::e_ssl_support_enabled) + if(m_ssl_options.support != ssl_support_t::e_ssl_support_disabled) success = boost::asio::write(*m_ssl_socket, boost::asio::buffer(data, sz), ec); else success = boost::asio::write(m_ssl_socket->next_layer(), boost::asio::buffer(data, sz), ec); @@ -660,7 +658,7 @@ namespace net_utils void async_write(const void* data, size_t sz, boost::system::error_code& ec) { - if(m_ssl_options.support == ssl_support_t::e_ssl_support_enabled) + if(m_ssl_options.support != ssl_support_t::e_ssl_support_disabled) boost::asio::async_write(*m_ssl_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1); else boost::asio::async_write(m_ssl_socket->next_layer(), boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1); @@ -668,7 +666,7 @@ namespace net_utils void async_read(char* buff, size_t sz, boost::asio::detail::transfer_at_least_t transfer_at_least, handler_obj& hndlr) { - if(m_ssl_options.support != ssl_support_t::e_ssl_support_enabled) + if(m_ssl_options.support == ssl_support_t::e_ssl_support_disabled) boost::asio::async_read(m_ssl_socket->next_layer(), boost::asio::buffer(buff, sz), transfer_at_least, hndlr); else boost::asio::async_read(*m_ssl_socket, boost::asio::buffer(buff, sz), transfer_at_least, hndlr); diff --git a/contrib/epee/include/net/net_utils_base.h b/contrib/epee/include/net/net_utils_base.h index 50536f63b..fce01311c 100644 --- a/contrib/epee/include/net/net_utils_base.h +++ b/contrib/epee/include/net/net_utils_base.h @@ -41,7 +41,7 @@ #define MONERO_DEFAULT_LOG_CATEGORY "net" #ifndef MAKE_IP -#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24)) +#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(((uint32_t)a4)<<24)) #endif #if BOOST_VERSION >= 107000 @@ -107,6 +107,53 @@ namespace net_utils inline bool operator>=(const ipv4_network_address& lhs, const ipv4_network_address& rhs) noexcept { return !lhs.less(rhs); } + class ipv4_network_subnet + { + uint32_t m_ip; + uint8_t m_mask; + + public: + constexpr ipv4_network_subnet() noexcept + : ipv4_network_subnet(0, 0) + {} + + constexpr ipv4_network_subnet(uint32_t ip, uint8_t mask) noexcept + : m_ip(ip), m_mask(mask) {} + + bool equal(const ipv4_network_subnet& other) const noexcept; + bool less(const ipv4_network_subnet& other) const noexcept; + constexpr bool is_same_host(const ipv4_network_subnet& other) const noexcept + { return subnet() == other.subnet(); } + bool matches(const ipv4_network_address &address) const; + + constexpr uint32_t subnet() const noexcept { return m_ip & ~(0xffffffffull << m_mask); } + std::string str() const; + std::string host_str() const; + bool is_loopback() const; + bool is_local() const; + static constexpr address_type get_type_id() noexcept { return address_type::invalid; } + static constexpr zone get_zone() noexcept { return zone::public_; } + static constexpr bool is_blockable() noexcept { return true; } + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(m_ip) + KV_SERIALIZE(m_mask) + END_KV_SERIALIZE_MAP() + }; + + inline bool operator==(const ipv4_network_subnet& lhs, const ipv4_network_subnet& rhs) noexcept + { return lhs.equal(rhs); } + inline bool operator!=(const ipv4_network_subnet& lhs, const ipv4_network_subnet& rhs) noexcept + { return !lhs.equal(rhs); } + inline bool operator<(const ipv4_network_subnet& lhs, const ipv4_network_subnet& rhs) noexcept + { return lhs.less(rhs); } + inline bool operator<=(const ipv4_network_subnet& lhs, const ipv4_network_subnet& rhs) noexcept + { return !rhs.less(lhs); } + inline bool operator>(const ipv4_network_subnet& lhs, const ipv4_network_subnet& rhs) noexcept + { return rhs.less(lhs); } + inline bool operator>=(const ipv4_network_subnet& lhs, const ipv4_network_subnet& rhs) noexcept + { return !lhs.less(rhs); } + class network_address { struct interface @@ -250,7 +297,7 @@ namespace net_utils const network_address m_remote_address; const bool m_is_income; const time_t m_started; - const time_t m_ssl; + const bool m_ssl; time_t m_last_recv; time_t m_last_send; uint64_t m_recv_cnt; @@ -294,6 +341,11 @@ namespace net_utils m_max_speed_up(0) {} + connection_context_base(const connection_context_base& a): connection_context_base() + { + set_details(a.m_connection_id, a.m_remote_address, a.m_is_income, a.m_ssl); + } + connection_context_base& operator=(const connection_context_base& a) { set_details(a.m_connection_id, a.m_remote_address, a.m_is_income, a.m_ssl); diff --git a/contrib/epee/include/storages/portable_storage_from_bin.h b/contrib/epee/include/storages/portable_storage_from_bin.h index 2884f8c5e..e0a32b3ca 100644 --- a/contrib/epee/include/storages/portable_storage_from_bin.h +++ b/contrib/epee/include/storages/portable_storage_from_bin.h @@ -136,6 +136,7 @@ namespace epee //for pod types array_entry_t<type_name> sa; size_t size = read_varint(); + CHECK_AND_ASSERT_THROW_MES(size <= m_count, "Size sanity check failed"); sa.reserve(size); //TODO: add some optimization here later while(size--) diff --git a/contrib/epee/src/buffer.cpp b/contrib/epee/src/buffer.cpp index d637b905e..10ea6de56 100644 --- a/contrib/epee/src/buffer.cpp +++ b/contrib/epee/src/buffer.cpp @@ -64,7 +64,8 @@ void buffer::append(const void *data, size_t sz) size_t reserve = (((size() + sz) * 3 / 2) + 4095) & ~4095; new_storage.reserve(reserve); new_storage.resize(size()); - memcpy(new_storage.data(), storage.data() + offset, storage.size() - offset); + if (size() > 0) + memcpy(new_storage.data(), storage.data() + offset, storage.size() - offset); offset = 0; std::swap(storage, new_storage); } diff --git a/contrib/epee/src/connection_basic.cpp b/contrib/epee/src/connection_basic.cpp index 19f2c7b02..82d9e3b53 100644 --- a/contrib/epee/src/connection_basic.cpp +++ b/contrib/epee/src/connection_basic.cpp @@ -284,9 +284,6 @@ double connection_basic::get_sleep_time(size_t cb) { return t; } -void connection_basic::set_save_graph(bool save_graph) { -} - } // namespace } // namespace diff --git a/contrib/epee/src/net_utils_base.cpp b/contrib/epee/src/net_utils_base.cpp index 9b781027e..b7f07a23b 100644 --- a/contrib/epee/src/net_utils_base.cpp +++ b/contrib/epee/src/net_utils_base.cpp @@ -22,6 +22,24 @@ namespace epee { namespace net_utils bool ipv4_network_address::is_local() const { return net_utils::is_ip_local(ip()); } + bool ipv4_network_subnet::equal(const ipv4_network_subnet& other) const noexcept + { return is_same_host(other) && m_mask == other.m_mask; } + + bool ipv4_network_subnet::less(const ipv4_network_subnet& other) const noexcept + { return subnet() < other.subnet() ? true : (other.subnet() < subnet() ? false : (m_mask < other.m_mask)); } + + std::string ipv4_network_subnet::str() const + { return string_tools::get_ip_string_from_int32(subnet()) + "/" + std::to_string(m_mask); } + + std::string ipv4_network_subnet::host_str() const { return string_tools::get_ip_string_from_int32(subnet()) + "/" + std::to_string(m_mask); } + bool ipv4_network_subnet::is_loopback() const { return net_utils::is_ip_loopback(subnet()); } + bool ipv4_network_subnet::is_local() const { return net_utils::is_ip_local(subnet()); } + bool ipv4_network_subnet::matches(const ipv4_network_address &address) const + { + return (address.ip() & ~(0xffffffffull << m_mask)) == subnet(); + } + + bool network_address::equal(const network_address& other) const { // clang typeid workaround diff --git a/contrib/epee/src/wipeable_string.cpp b/contrib/epee/src/wipeable_string.cpp index 3a6ee5dac..4209b71bf 100644 --- a/contrib/epee/src/wipeable_string.cpp +++ b/contrib/epee/src/wipeable_string.cpp @@ -62,13 +62,15 @@ wipeable_string::wipeable_string(wipeable_string &&other) wipeable_string::wipeable_string(const std::string &other) { grow(other.size()); - memcpy(buffer.data(), other.c_str(), size()); + if (size() > 0) + memcpy(buffer.data(), other.c_str(), size()); } wipeable_string::wipeable_string(std::string &&other) { grow(other.size()); - memcpy(buffer.data(), other.c_str(), size()); + if (size() > 0) + memcpy(buffer.data(), other.c_str(), size()); if (!other.empty()) { memwipe(&other[0], other.size()); // we're kinda left with this again aren't we @@ -79,7 +81,8 @@ wipeable_string::wipeable_string(std::string &&other) wipeable_string::wipeable_string(const char *s) { grow(strlen(s)); - memcpy(buffer.data(), s, size()); + if (size() > 0) + memcpy(buffer.data(), s, size()); } wipeable_string::wipeable_string(const char *s, size_t len) @@ -112,14 +115,18 @@ void wipeable_string::grow(size_t sz, size_t reserved) } size_t old_sz = buffer.size(); std::unique_ptr<char[]> tmp{new char[old_sz]}; - memcpy(tmp.get(), buffer.data(), old_sz * sizeof(char)); if (old_sz > 0) + { + memcpy(tmp.get(), buffer.data(), old_sz * sizeof(char)); memwipe(buffer.data(), old_sz * sizeof(char)); + } buffer.reserve(reserved); buffer.resize(sz); - memcpy(buffer.data(), tmp.get(), old_sz * sizeof(char)); if (old_sz > 0) + { + memcpy(buffer.data(), tmp.get(), old_sz * sizeof(char)); memwipe(tmp.get(), old_sz * sizeof(char)); + } } void wipeable_string::push_back(char c) diff --git a/contrib/gitian/gitian-linux.yml b/contrib/gitian/gitian-linux.yml index 67f174fec..6e3ad108e 100644 --- a/contrib/gitian/gitian-linux.yml +++ b/contrib/gitian/gitian-linux.yml @@ -129,6 +129,7 @@ script: | chmod +x ${WRAP_DIR}/${prog} done + git config --global core.abbrev 9 cd monero BASEPREFIX=`pwd`/contrib/depends # Build dependencies for each host @@ -153,7 +154,7 @@ script: | export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} mkdir build && cd build cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake -DBACKCOMPAT=ON - make + make ${MAKEOPTS} DISTNAME=monero-${i} mv bin ${DISTNAME} find ${DISTNAME}/ | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}.tar.gz diff --git a/contrib/gitian/gitian-osx.yml b/contrib/gitian/gitian-osx.yml index 7de302353..1cdb85273 100644 --- a/contrib/gitian/gitian-osx.yml +++ b/contrib/gitian/gitian-osx.yml @@ -77,6 +77,7 @@ script: | create_per-host_faketime_wrappers "2000-01-01 12:00:00" export PATH=${WRAP_DIR}:${PATH} + git config --global core.abbrev 9 cd monero BASEPREFIX=`pwd`/contrib/depends @@ -100,7 +101,7 @@ script: | export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} mkdir build && cd build cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake - make + make ${MAKEOPTS} DISTNAME=monero-${i} mv bin ${DISTNAME} find ${DISTNAME}/ | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}.tar.gz diff --git a/contrib/gitian/gitian-win.yml b/contrib/gitian/gitian-win.yml index 1eb558300..d15383905 100644 --- a/contrib/gitian/gitian-win.yml +++ b/contrib/gitian/gitian-win.yml @@ -100,6 +100,7 @@ script: | create_per-host_linker_wrapper "2000-01-01 12:00:00" export PATH=${WRAP_DIR}:${PATH} + git config --global core.abbrev 9 cd monero BASEPREFIX=`pwd`/contrib/depends # Build dependencies for each host @@ -125,7 +126,7 @@ script: | export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} mkdir build && cd build cmake .. -DCMAKE_TOOLCHAIN_FILE=${BASEPREFIX}/${i}/share/toolchain.cmake - make + make ${MAKEOPTS} DISTNAME=monero-${i} mv bin ${DISTNAME} find ${DISTNAME}/ | sort | zip -X@ ${OUTDIR}/${DISTNAME}.zip diff --git a/contrib/rlwrap/monerocommands_bitmonerod.txt b/contrib/rlwrap/monerocommands_bitmonerod.txt index c4f77b37d..e06e4f4f1 100644 --- a/contrib/rlwrap/monerocommands_bitmonerod.txt +++ b/contrib/rlwrap/monerocommands_bitmonerod.txt @@ -27,10 +27,8 @@ save set_log show_hr start_mining -start_save_graph status stop_daemon stop_mining -stop_save_graph sync_info unban diff --git a/external/miniupnp b/external/miniupnp -Subproject 6b9b73a567e351b844f96c077f7b752ea92e298 +Subproject 4c700e09526a7d546394e85628c57e9490feefa diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index da6d76d97..8a21763c8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -133,7 +133,7 @@ if(NOT IOS) add_subdirectory(blockchain_utilities) endif() -if(CMAKE_BUILD_TYPE STREQUAL Debug) +if(BUILD_DEBUG_UTILITIES) add_subdirectory(debug_utilities) endif() diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 567be6a65..bb4de3ce6 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -129,6 +129,15 @@ struct tx_data_t }; #pragma pack(pop) +struct alt_block_data_t +{ + uint64_t height; + uint64_t cumulative_weight; + uint64_t cumulative_difficulty_low; + uint64_t cumulative_difficulty_high; + uint64_t already_generated_coins; +}; + /** * @brief a struct containing txpool per transaction metadata */ @@ -754,6 +763,21 @@ public: virtual void batch_stop() = 0; /** + * @brief aborts a batch transaction + * + * If the subclass implements batching, this function should abort the + * batch it is currently on. + * + * If no batch is in-progress, this function should throw a DB_ERROR. + * This exception may change in the future if it is deemed necessary to + * have a more granular exception type for this scenario. + * + * If any of this cannot be done, the subclass should throw the corresponding + * subclass of DB_EXCEPTION + */ + virtual void batch_abort() = 0; + + /** * @brief sets whether or not to batch transactions * * If the subclass implements batching, this function tells it to begin @@ -1528,8 +1552,45 @@ public: * * @param: sz the block size */ - virtual void add_max_block_size(uint64_t sz) = 0; + + /** + * @brief add a new alternative block + * + * @param: blkid the block hash + * @param: data: the metadata for the block + * @param: blob: the block's blob + */ + virtual void add_alt_block(const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata &blob) = 0; + + /** + * @brief get an alternative block by hash + * + * @param: blkid the block hash + * @param: data: the metadata for the block + * @param: blob: the block's blob + * + * @return true if the block was found in the alternative blocks list, false otherwise + */ + virtual bool get_alt_block(const crypto::hash &blkid, alt_block_data_t *data, cryptonote::blobdata *blob) = 0; + + /** + * @brief remove an alternative block + * + * @param: blkid the block hash + */ + virtual void remove_alt_block(const crypto::hash &blkid) = 0; + + /** + * @brief get the number of alternative blocks stored + */ + virtual uint64_t get_alt_block_count() = 0; + + /** + * @brief drop all alternative blocks + */ + virtual void drop_alt_blocks() = 0; + /** * @brief runs a function over all txpool transactions * @@ -1619,6 +1680,23 @@ public: virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)> f) const = 0; virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const = 0; + /** + * @brief runs a function over all alternative blocks stored + * + * The subclass should run the passed function for each alt block it has + * stored, passing (blkid, data, blob) as its parameters. + * + * If any call to the function returns false, the subclass should return + * false. Otherwise, the subclass returns true. + * + * The subclass should throw DB_ERROR if any of the expected values are + * not found. Current implementations simply return false. + * + * @param std::function f the function to run + * + * @return false if the function returns false for any output, otherwise true + */ + virtual bool for_all_alt_blocks(std::function<bool(const crypto::hash &blkid, const alt_block_data_t &data, const cryptonote::blobdata *blob)> f, bool include_blob = false) const = 0; // diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index a03a0989b..78db37b5a 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -194,6 +194,8 @@ namespace * txpool_meta txn hash txn metadata * txpool_blob txn hash txn blob * + * alt_blocks block hash {block data, block blob} + * * Note: where the data items are of uniform size, DUPFIXED tables have * been used to save space. In most of these cases, a dummy "zerokval" * key is used when accessing the table; the Key listed above will be @@ -221,6 +223,8 @@ const char* const LMDB_SPENT_KEYS = "spent_keys"; const char* const LMDB_TXPOOL_META = "txpool_meta"; const char* const LMDB_TXPOOL_BLOB = "txpool_blob"; +const char* const LMDB_ALT_BLOCKS = "alt_blocks"; + const char* const LMDB_HF_STARTING_HEIGHTS = "hf_starting_heights"; const char* const LMDB_HF_VERSIONS = "hf_versions"; @@ -707,7 +711,7 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks, uin ++num_blocks_used; } if (my_rtxn) block_rtxn_stop(); - avg_block_size = total_block_size / num_blocks_used; + avg_block_size = total_block_size / (num_blocks_used ? num_blocks_used : 1); MDEBUG("average block size across recent " << num_blocks_used << " blocks: " << avg_block_size); } estim: @@ -1077,11 +1081,11 @@ void BlockchainLMDB::add_tx_amount_output_indices(const uint64_t tx_id, int result = 0; - int num_outputs = amount_output_indices.size(); + size_t num_outputs = amount_output_indices.size(); MDB_val_set(k_tx_id, tx_id); MDB_val v; - v.mv_data = (void *)amount_output_indices.data(); + v.mv_data = num_outputs ? (void *)amount_output_indices.data() : (void*)""; v.mv_size = sizeof(uint64_t) * num_outputs; // LOG_PRINT_L1("tx_outputs[tx_hash] size: " << v.mv_size); @@ -1400,6 +1404,8 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) lmdb_db_open(txn, LMDB_TXPOOL_META, MDB_CREATE, m_txpool_meta, "Failed to open db handle for m_txpool_meta"); lmdb_db_open(txn, LMDB_TXPOOL_BLOB, MDB_CREATE, m_txpool_blob, "Failed to open db handle for m_txpool_blob"); + lmdb_db_open(txn, LMDB_ALT_BLOCKS, MDB_CREATE, m_alt_blocks, "Failed to open db handle for m_alt_blocks"); + // this subdb is dropped on sight, so it may not be present when we open the DB. // Since we use MDB_CREATE, we'll get an exception if we open read-only and it does not exist. // So we don't open for read-only, and also not drop below. It is not used elsewhere. @@ -1423,6 +1429,7 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) mdb_set_compare(txn, m_txpool_meta, compare_hash32); mdb_set_compare(txn, m_txpool_blob, compare_hash32); + mdb_set_compare(txn, m_alt_blocks, compare_hash32); mdb_set_compare(txn, m_properties, compare_string); if (!(mdb_flags & MDB_RDONLY)) @@ -1953,7 +1960,7 @@ bool BlockchainLMDB::prune_worker(int mode, uint32_t pruning_seed) TIME_MEASURE_START(t); - size_t n_total_records = 0, n_prunable_records = 0, n_pruned_records = 0; + size_t n_total_records = 0, n_prunable_records = 0, n_pruned_records = 0, commit_counter = 0; uint64_t n_bytes = 0; mdb_txn_safe txn; @@ -2056,6 +2063,7 @@ bool BlockchainLMDB::prune_worker(int mode, uint32_t pruning_seed) { MDEBUG("Pruning at height " << block_height << "/" << blockchain_height); ++n_pruned_records; + ++commit_counter; n_bytes += k.mv_size + v.mv_size; result = mdb_cursor_del(c_txs_prunable, 0); if (result) @@ -2065,6 +2073,25 @@ bool BlockchainLMDB::prune_worker(int mode, uint32_t pruning_seed) result = mdb_cursor_del(c_txs_prunable_tip, 0); if (result) throw0(DB_ERROR(lmdb_error("Failed to delete transaction tip data: ", result).c_str())); + + if (mode != prune_mode_check && commit_counter >= 4096) + { + MDEBUG("Committing txn at checkpoint..."); + txn.commit(); + result = mdb_txn_begin(m_env, NULL, 0, txn); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); + result = mdb_cursor_open(txn, m_txs_pruned, &c_txs_pruned); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs_pruned: ", result).c_str())); + result = mdb_cursor_open(txn, m_txs_prunable, &c_txs_prunable); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs_prunable: ", result).c_str())); + result = mdb_cursor_open(txn, m_txs_prunable_tip, &c_txs_prunable_tip); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs_prunable_tip: ", result).c_str())); + commit_counter = 0; + } } } } @@ -2134,6 +2161,7 @@ bool BlockchainLMDB::prune_worker(int mode, uint32_t pruning_seed) result = mdb_cursor_del(c_txs_prunable, 0); if (result) throw0(DB_ERROR(lmdb_error("Failed to delete transaction prunable data: ", result).c_str())); + ++commit_counter; } } } @@ -2150,6 +2178,34 @@ bool BlockchainLMDB::prune_worker(int mode, uint32_t pruning_seed) ", seed " << epee::string_tools::to_string_hex(pruning_seed)); } } + + if (mode != prune_mode_check && commit_counter >= 4096) + { + MDEBUG("Committing txn at checkpoint..."); + txn.commit(); + result = mdb_txn_begin(m_env, NULL, 0, txn); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); + result = mdb_cursor_open(txn, m_txs_pruned, &c_txs_pruned); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs_pruned: ", result).c_str())); + result = mdb_cursor_open(txn, m_txs_prunable, &c_txs_prunable); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs_prunable: ", result).c_str())); + result = mdb_cursor_open(txn, m_txs_prunable_tip, &c_txs_prunable_tip); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs_prunable_tip: ", result).c_str())); + result = mdb_cursor_open(txn, m_tx_indices, &c_tx_indices); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to open a cursor for tx_indices: ", result).c_str())); + MDB_val val; + val.mv_size = sizeof(ti); + val.mv_data = (void *)&ti; + result = mdb_cursor_get(c_tx_indices, (MDB_val*)&zerokval, &val, MDB_GET_BOTH); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to restore cursor for tx_indices: ", result).c_str())); + commit_counter = 0; + } } mdb_cursor_close(c_tx_indices); } @@ -2241,6 +2297,50 @@ bool BlockchainLMDB::for_all_txpool_txes(std::function<bool(const crypto::hash&, return ret; } +bool BlockchainLMDB::for_all_alt_blocks(std::function<bool(const crypto::hash&, const alt_block_data_t&, const cryptonote::blobdata*)> f, bool include_blob) const +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + + TXN_PREFIX_RDONLY(); + RCURSOR(alt_blocks); + + MDB_val k; + MDB_val v; + bool ret = true; + + MDB_cursor_op op = MDB_FIRST; + while (1) + { + int result = mdb_cursor_get(m_cur_alt_blocks, &k, &v, op); + op = MDB_NEXT; + if (result == MDB_NOTFOUND) + break; + if (result) + throw0(DB_ERROR(lmdb_error("Failed to enumerate alt blocks: ", result).c_str())); + const crypto::hash &blkid = *(const crypto::hash*)k.mv_data; + if (v.mv_size < sizeof(alt_block_data_t)) + throw0(DB_ERROR("alt_blocks record is too small")); + const alt_block_data_t *data = (const alt_block_data_t*)v.mv_data; + const cryptonote::blobdata *passed_bd = NULL; + cryptonote::blobdata bd; + if (include_blob) + { + bd.assign(reinterpret_cast<const char*>(v.mv_data) + sizeof(alt_block_data_t), v.mv_size - sizeof(alt_block_data_t)); + passed_bd = &bd; + } + + if (!f(blkid, *data, passed_bd)) { + ret = false; + break; + } + } + + TXN_POSTFIX_RDONLY(); + + return ret; +} + bool BlockchainLMDB::block_exists(const crypto::hash& h, uint64_t *height) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -4062,6 +4162,110 @@ uint8_t BlockchainLMDB::get_hard_fork_version(uint64_t height) const return ret; } +void BlockchainLMDB::add_alt_block(const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata &blob) +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + mdb_txn_cursors *m_cursors = &m_wcursors; + + CURSOR(alt_blocks) + + MDB_val k = {sizeof(blkid), (void *)&blkid}; + const size_t val_size = sizeof(alt_block_data_t) + blob.size(); + std::unique_ptr<char[]> val(new char[val_size]); + memcpy(val.get(), &data, sizeof(alt_block_data_t)); + memcpy(val.get() + sizeof(alt_block_data_t), blob.data(), blob.size()); + MDB_val v = {val_size, (void *)val.get()}; + if (auto result = mdb_cursor_put(m_cur_alt_blocks, &k, &v, MDB_NODUPDATA)) { + if (result == MDB_KEYEXIST) + throw1(DB_ERROR("Attempting to add alternate block that's already in the db")); + else + throw1(DB_ERROR(lmdb_error("Error adding alternate block to db transaction: ", result).c_str())); + } +} + +bool BlockchainLMDB::get_alt_block(const crypto::hash &blkid, alt_block_data_t *data, cryptonote::blobdata *blob) +{ + LOG_PRINT_L3("BlockchainLMDB:: " << __func__); + check_open(); + + TXN_PREFIX_RDONLY(); + RCURSOR(alt_blocks); + + MDB_val_set(k, blkid); + MDB_val v; + int result = mdb_cursor_get(m_cur_alt_blocks, &k, &v, MDB_SET); + if (result == MDB_NOTFOUND) + return false; + + if (result) + throw0(DB_ERROR(lmdb_error("Error attempting to retrieve alternate block " + epee::string_tools::pod_to_hex(blkid) + " from the db: ", result).c_str())); + if (v.mv_size < sizeof(alt_block_data_t)) + throw0(DB_ERROR("Record size is less than expected")); + + const alt_block_data_t *ptr = (const alt_block_data_t*)v.mv_data; + if (data) + *data = *ptr; + if (blob) + blob->assign((const char*)(ptr + 1), v.mv_size - sizeof(alt_block_data_t)); + + TXN_POSTFIX_RDONLY(); + return true; +} + +void BlockchainLMDB::remove_alt_block(const crypto::hash &blkid) +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + mdb_txn_cursors *m_cursors = &m_wcursors; + + CURSOR(alt_blocks) + + MDB_val k = {sizeof(blkid), (void *)&blkid}; + MDB_val v; + int result = mdb_cursor_get(m_cur_alt_blocks, &k, &v, MDB_SET); + if (result) + throw0(DB_ERROR(lmdb_error("Error locating alternate block " + epee::string_tools::pod_to_hex(blkid) + " in the db: ", result).c_str())); + result = mdb_cursor_del(m_cur_alt_blocks, 0); + if (result) + throw0(DB_ERROR(lmdb_error("Error deleting alternate block " + epee::string_tools::pod_to_hex(blkid) + " from the db: ", result).c_str())); +} + +uint64_t BlockchainLMDB::get_alt_block_count() +{ + LOG_PRINT_L3("BlockchainLMDB:: " << __func__); + check_open(); + + TXN_PREFIX_RDONLY(); + RCURSOR(alt_blocks); + + MDB_stat db_stats; + int result = mdb_stat(m_txn, m_alt_blocks, &db_stats); + uint64_t count = 0; + if (result != MDB_NOTFOUND) + { + if (result) + throw0(DB_ERROR(lmdb_error("Failed to query m_alt_blocks: ", result).c_str())); + count = db_stats.ms_entries; + } + TXN_POSTFIX_RDONLY(); + return count; +} + +void BlockchainLMDB::drop_alt_blocks() +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + + TXN_PREFIX(0); + + auto result = mdb_drop(*txn_ptr, m_alt_blocks, 0); + if (result) + throw1(DB_ERROR(lmdb_error("Error dropping alternative blocks: ", result).c_str())); + + TXN_POSTFIX_SUCCESS(); +} + bool BlockchainLMDB::is_read_only() const { unsigned int flags; diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 4b46f081e..61a551476 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -67,6 +67,8 @@ typedef struct mdb_txn_cursors MDB_cursor *m_txc_txpool_meta; MDB_cursor *m_txc_txpool_blob; + MDB_cursor *m_txc_alt_blocks; + MDB_cursor *m_txc_hf_versions; MDB_cursor *m_txc_properties; @@ -87,6 +89,7 @@ typedef struct mdb_txn_cursors #define m_cur_spent_keys m_cursors->m_txc_spent_keys #define m_cur_txpool_meta m_cursors->m_txc_txpool_meta #define m_cur_txpool_blob m_cursors->m_txc_txpool_blob +#define m_cur_alt_blocks m_cursors->m_txc_alt_blocks #define m_cur_hf_versions m_cursors->m_txc_hf_versions #define m_cur_properties m_cursors->m_txc_properties @@ -108,6 +111,7 @@ typedef struct mdb_rflags bool m_rf_spent_keys; bool m_rf_txpool_meta; bool m_rf_txpool_blob; + bool m_rf_alt_blocks; bool m_rf_hf_versions; bool m_rf_properties; } mdb_rflags; @@ -288,6 +292,12 @@ public: virtual bool update_pruning(); virtual bool check_pruning(); + virtual void add_alt_block(const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata &blob); + virtual bool get_alt_block(const crypto::hash &blkid, alt_block_data_t *data, cryptonote::blobdata *blob); + virtual void remove_alt_block(const crypto::hash &blkid); + virtual uint64_t get_alt_block_count(); + virtual void drop_alt_blocks(); + virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob = false, bool include_unrelayed_txes = true) const; virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const; @@ -295,6 +305,7 @@ public: virtual bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>, bool pruned) const; virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)> f) const; virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const; + virtual bool for_all_alt_blocks(std::function<bool(const crypto::hash &blkid, const alt_block_data_t &data, const cryptonote::blobdata *blob)> f, bool include_blob = false) const; virtual uint64_t add_block( const std::pair<block, blobdata>& blk , size_t block_weight @@ -452,6 +463,8 @@ private: MDB_dbi m_txpool_meta; MDB_dbi m_txpool_blob; + MDB_dbi m_alt_blocks; + MDB_dbi m_hf_starting_heights; MDB_dbi m_hf_versions; diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h index 6c97713d5..ac19fae25 100644 --- a/src/blockchain_db/testdb.h +++ b/src/blockchain_db/testdb.h @@ -54,6 +54,7 @@ public: virtual void unlock() override { } virtual bool batch_start(uint64_t batch_num_blocks=0, uint64_t batch_bytes=0) override { return true; } virtual void batch_stop() override {} + virtual void batch_abort() override {} virtual void set_batch_transactions(bool) override {} virtual void block_wtxn_start() override {} virtual void block_wtxn_stop() override {} @@ -155,6 +156,13 @@ public: virtual uint64_t get_max_block_size() override { return 100000000; } virtual void add_max_block_size(uint64_t sz) override { } + + virtual void add_alt_block(const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata &blob) override {} + virtual bool get_alt_block(const crypto::hash &blkid, alt_block_data_t *data, cryptonote::blobdata *blob) override { return false; } + virtual void remove_alt_block(const crypto::hash &blkid) override {} + virtual uint64_t get_alt_block_count() override { return 0; } + virtual void drop_alt_blocks() override {} + virtual bool for_all_alt_blocks(std::function<bool(const crypto::hash &blkid, const alt_block_data_t &data, const cryptonote::blobdata *blob)> f, bool include_blob = false) const override { return true; } }; } diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 6ff184041..f824d93a6 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -637,6 +637,7 @@ static void inc_per_amount_outputs(MDB_txn *txn, uint64_t amount, uint64_t total v.mv_size = 2 * sizeof(uint64_t); v.mv_data = (void*)data; dbr = mdb_cursor_put(cur, &k, &v, 0); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to write record for per amount outputs: " + std::string(mdb_strerror(dbr))); mdb_cursor_close(cur); } diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat Binary files differindex adc433522..a7d309753 100644 --- a/src/blocks/checkpoints.dat +++ b/src/blocks/checkpoints.dat diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index e31b96646..11bbe2e24 100644 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -209,6 +209,7 @@ namespace cryptonote ADD_CHECKPOINT(1579000, "7d0d7a2346373afd41ed1e744a939fc5d474a7dbaa257be5c6fff4009e789241"); ADD_CHECKPOINT(1668900, "ac2dcaf3d2f58ffcf8391639f0f1ebafcb8eac43c49479c7c37f611868d07568"); ADD_CHECKPOINT(1775600, "1c6e01c661dc22cab939e79ec6a5272190624ce8356d2f7b958e4f9a57fdb05e"); + ADD_CHECKPOINT(1856000, "9b57f17f29c71a3acd8a7904b93c41fa6eb8d2b7c73936ce4f1702d14880ba29"); return true; } diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index 5e03bf897..dc1f335a7 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -48,7 +48,6 @@ static const char *DEFAULT_DNS_PUBLIC_ADDR[] = "80.67.169.40", // FDN (France) "89.233.43.71", // http://censurfridns.dk (Denmark) "109.69.8.51", // punCAT (Spain) - "77.109.148.137", // Xiala.net (Switzerland) "193.58.251.251", // SkyDNS (Russia) }; diff --git a/src/common/rpc_client.h b/src/common/rpc_client.h index cb5f79da8..dab3e562d 100644 --- a/src/common/rpc_client.h +++ b/src/common/rpc_client.h @@ -36,6 +36,7 @@ #include "storages/http_abstract_invoke.h" #include "net/http_auth.h" #include "net/http_client.h" +#include "net/net_ssl.h" #include "string_tools.h" namespace tools @@ -49,11 +50,12 @@ namespace tools uint32_t ip , uint16_t port , boost::optional<epee::net_utils::http::login> user + , epee::net_utils::ssl_options_t ssl_options ) : m_http_client{} { m_http_client.set_server( - epee::string_tools::get_ip_string_from_int32(ip), std::to_string(port), std::move(user) + epee::string_tools::get_ip_string_from_int32(ip), std::to_string(port), std::move(user), std::move(ssl_options) ); } diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c index 170911262..851c70a25 100644 --- a/src/crypto/keccak.c +++ b/src/crypto/keccak.c @@ -105,9 +105,12 @@ void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen) memset(st, 0, sizeof(st)); for ( ; inlen >= rsiz; inlen -= rsiz, in += rsiz) { - for (i = 0; i < rsizw; i++) - st[i] ^= swap64le(((uint64_t *) in)[i]); - keccakf(st, KECCAK_ROUNDS); + for (i = 0; i < rsizw; i++) { + uint64_t ina; + memcpy(&ina, in + i * 8, 8); + st[i] ^= swap64le(ina); + } + keccakf(st, KECCAK_ROUNDS); } // last block and padding @@ -116,7 +119,8 @@ void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen) local_abort("Bad keccak use"); } - memcpy(temp, in, inlen); + if (inlen > 0) + memcpy(temp, in, inlen); temp[inlen++] = 1; memset(temp + inlen, 0, rsiz - inlen); temp[rsiz - 1] |= 0x80; diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 7f36c9dc3..13835c823 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -897,7 +897,6 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int // locals to avoid constant TLS dereferencing uint8_t *local_hp_state = hp_state; - v4_random_math_JIT_func local_hp_jitfunc = hp_jitfunc; /* CryptoNight Step 1: Use Keccak1600 to initialize the 'state' (and 'text') buffers from the data. */ if (prehashed) { diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c index 7802fb67f..0a5860f3b 100644 --- a/src/crypto/tree-hash.c +++ b/src/crypto/tree-hash.c @@ -30,6 +30,7 @@ #include <assert.h> #include <stddef.h> +#include <stdlib.h> #include <string.h> #include "hash-ops.h" @@ -82,23 +83,24 @@ void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash) { size_t cnt = tree_hash_cnt( count ); - char ints[cnt][HASH_SIZE]; - memset(ints, 0 , sizeof(ints)); // zero out as extra protection for using uninitialized mem + char *ints = calloc(cnt, HASH_SIZE); // zero out as extra protection for using uninitialized mem + assert(ints); memcpy(ints, hashes, (2 * cnt - count) * HASH_SIZE); for (i = 2 * cnt - count, j = 2 * cnt - count; j < cnt; i += 2, ++j) { - cn_fast_hash(hashes[i], 64, ints[j]); + cn_fast_hash(hashes[i], 64, ints + j * HASH_SIZE); } assert(i == count); while (cnt > 2) { cnt >>= 1; for (i = 0, j = 0; j < cnt; i += 2, ++j) { - cn_fast_hash(ints[i], 64, ints[j]); + cn_fast_hash(ints + i * HASH_SIZE, 64, ints + j * HASH_SIZE); } } - cn_fast_hash(ints[0], 64, root_hash); + cn_fast_hash(ints, 64, root_hash); + free(ints); } } diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index 20d92bdf1..055c4a22b 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -320,7 +320,7 @@ namespace cryptonote } if (!typename Archive<W>::is_saving()) pruned = true; - return true; + return ar.stream().good(); } private: diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 566622c1a..7d7de416d 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -221,8 +221,7 @@ namespace cryptonote tx.invalidate_hashes(); //TODO: validate tx - get_transaction_hash(tx, tx_hash); - return true; + return get_transaction_hash(tx, tx_hash); } //--------------------------------------------------------------- bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash) @@ -975,6 +974,7 @@ namespace cryptonote { crypto::hash h = null_hash; get_transaction_hash(t, h, NULL); + CHECK_AND_ASSERT_THROW_MES(get_transaction_hash(t, h, NULL), "Failed to calculate transaction hash"); return h; } //--------------------------------------------------------------- @@ -1327,7 +1327,7 @@ namespace cryptonote txs_ids.reserve(1 + b.tx_hashes.size()); crypto::hash h = null_hash; size_t bl_sz = 0; - get_transaction_hash(b.miner_tx, h, bl_sz); + CHECK_AND_ASSERT_THROW_MES(get_transaction_hash(b.miner_tx, h, bl_sz), "Failed to calculate transaction hash"); txs_ids.push_back(h); for(auto& th: b.tx_hashes) txs_ids.push_back(th); diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index 173679e21..e594eb049 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -123,7 +123,7 @@ namespace cryptonote m_miner_extra_sleep(BACKGROUND_MINING_DEFAULT_MINER_EXTRA_SLEEP_MILLIS), m_block_reward(0) { - + m_attrs.set_stack_size(THREAD_STACK_SIZE); } //----------------------------------------------------------------------------------------------------- miner::~miner() @@ -360,7 +360,7 @@ namespace cryptonote return m_threads_total; } //----------------------------------------------------------------------------------------------------- - bool miner::start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs, bool do_background, bool ignore_battery) + bool miner::start(const account_public_address& adr, size_t threads_count, bool do_background, bool ignore_battery) { m_block_reward = 0; m_mine_address = adr; @@ -371,7 +371,6 @@ namespace cryptonote m_threads_autodetect.push_back({epee::misc_utils::get_ns_count(), m_total_hashes}); m_threads_total = 1; } - m_attrs = attrs; m_starter_nonce = crypto::rand<uint32_t>(); CRITICAL_REGION_LOCAL(m_threads_lock); if(is_mining()) @@ -395,7 +394,7 @@ namespace cryptonote for(size_t i = 0; i != m_threads_total; i++) { - m_threads.push_back(boost::thread(attrs, boost::bind(&miner::worker_thread, this))); + m_threads.push_back(boost::thread(m_attrs, boost::bind(&miner::worker_thread, this))); } if (threads_count == 0) @@ -405,7 +404,7 @@ namespace cryptonote if( get_is_background_mining_enabled() ) { - m_background_mining_thread = boost::thread(attrs, boost::bind(&miner::background_worker_thread, this)); + m_background_mining_thread = boost::thread(m_attrs, boost::bind(&miner::background_worker_thread, this)); LOG_PRINT_L0("Background mining controller thread started" ); } @@ -487,10 +486,7 @@ namespace cryptonote { if(m_do_mining) { - boost::thread::attributes attrs; - attrs.set_stack_size(THREAD_STACK_SIZE); - - start(m_mine_address, m_threads_total, attrs, get_is_background_mining_enabled(), get_ignore_battery()); + start(m_mine_address, m_threads_total, get_is_background_mining_enabled(), get_ignore_battery()); } } //----------------------------------------------------------------------------------------------------- diff --git a/src/cryptonote_basic/miner.h b/src/cryptonote_basic/miner.h index 285075f51..ac7a0381c 100644 --- a/src/cryptonote_basic/miner.h +++ b/src/cryptonote_basic/miner.h @@ -64,7 +64,7 @@ namespace cryptonote static void init_options(boost::program_options::options_description& desc); bool set_block_template(const block& bl, const difficulty_type& diffic, uint64_t height, uint64_t block_reward); bool on_block_chain_update(); - bool start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs, bool do_background = false, bool ignore_battery = false); + bool start(const account_public_address& adr, size_t threads_count, bool do_background = false, bool ignore_battery = false); uint64_t get_speed() const; uint32_t get_threads_count() const; void send_stop_signal(); diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 39c9f8695..fbff8d878 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -182,7 +182,8 @@ Blockchain::Blockchain(tx_memory_pool& tx_pool) : m_long_term_block_weights_cache_rolling_median(CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE), m_difficulty_for_next_block_top_hash(crypto::null_hash), m_difficulty_for_next_block(1), - m_btc_valid(false) + m_btc_valid(false), + m_batch_success(true) { LOG_PRINT_L3("Blockchain::" << __func__); } @@ -619,17 +620,13 @@ void Blockchain::pop_blocks(uint64_t nblocks) CRITICAL_REGION_LOCAL(m_tx_pool); CRITICAL_REGION_LOCAL1(m_blockchain_lock); - while (!m_db->batch_start()) - { - m_blockchain_lock.unlock(); - m_tx_pool.unlock(); - epee::misc_utils::sleep_no_w(1000); - m_tx_pool.lock(); - m_blockchain_lock.lock(); - } + bool stop_batch = m_db->batch_start(); try { + const uint64_t blockchain_height = m_db->height(); + if (blockchain_height > 0) + nblocks = std::min(nblocks, blockchain_height - 1); for (i=0; i < nblocks; ++i) { pop_block_from_blockchain(); @@ -637,10 +634,14 @@ void Blockchain::pop_blocks(uint64_t nblocks) } catch (const std::exception& e) { - LOG_ERROR("Error when popping blocks, only " << i << " blocks are popped: " << e.what()); + LOG_ERROR("Error when popping blocks after processing " << i << " blocks: " << e.what()); + if (stop_batch) + m_db->batch_abort(); + return; } - m_db->batch_stop(); + if (stop_batch) + m_db->batch_stop(); } //------------------------------------------------------------------ // This function tells BlockchainDB to remove the top block from the @@ -730,9 +731,9 @@ bool Blockchain::reset_and_set_genesis_block(const block& b) LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); m_timestamps_and_difficulties_height = 0; - m_alternative_chains.clear(); invalidate_block_template_cache(); m_db->reset(); + m_db->drop_alt_blocks(); m_hardfork->init(); db_wtxn_guard wtxn_guard(m_db); @@ -857,10 +858,15 @@ bool Blockchain::get_block_by_hash(const crypto::hash &h, block &blk, bool *orph // try to find block in alternative chain catch (const BLOCK_DNE& e) { - blocks_ext_by_hash::const_iterator it_alt = m_alternative_chains.find(h); - if (m_alternative_chains.end() != it_alt) + alt_block_data_t data; + cryptonote::blobdata blob; + if (m_db->get_alt_block(h, &data, &blob)) { - blk = it_alt->second.bl; + if (!cryptonote::parse_and_validate_block_from_blob(blob, blk)) + { + MERROR("Found block " << h << " in alt chain, but failed to parse it"); + throw std::runtime_error("Found block in alt chain, but failed to parse it"); + } if (orphan) *orphan = true; return true; @@ -1018,7 +1024,7 @@ bool Blockchain::rollback_blockchain_switching(std::list<block>& original_chain, //------------------------------------------------------------------ // This function attempts to switch to an alternate chain, returning // boolean based on success therein. -bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::const_iterator>& alt_chain, bool discard_disconnected_chain) +bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info>& alt_chain, bool discard_disconnected_chain) { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -1029,7 +1035,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash:: CHECK_AND_ASSERT_MES(alt_chain.size(), false, "switch_to_alternative_blockchain: empty chain passed"); // verify that main chain has front of alt chain's parent block - if (!m_db->block_exists(alt_chain.front()->second.bl.prev_id)) + if (!m_db->block_exists(alt_chain.front().bl.prev_id)) { LOG_ERROR("Attempting to move to an alternate chain, but it doesn't appear to connect to the main chain!"); return false; @@ -1038,7 +1044,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash:: // pop blocks from the blockchain until the top block is the parent // of the front block of the alt chain. std::list<block> disconnected_chain; - while (m_db->top_block_hash() != alt_chain.front()->second.bl.prev_id) + while (m_db->top_block_hash() != alt_chain.front().bl.prev_id) { block b = pop_block_from_blockchain(); disconnected_chain.push_front(b); @@ -1049,11 +1055,11 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash:: //connecting new alternative chain for(auto alt_ch_iter = alt_chain.begin(); alt_ch_iter != alt_chain.end(); alt_ch_iter++) { - auto ch_ent = *alt_ch_iter; + const auto &bei = *alt_ch_iter; block_verification_context bvc = boost::value_initialized<block_verification_context>(); // add block to main chain - bool r = handle_block_to_main_chain(ch_ent->second.bl, bvc); + bool r = handle_block_to_main_chain(bei.bl, bvc); // if adding block to main chain failed, rollback to previous state and // return false @@ -1069,14 +1075,18 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash:: // 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. - add_block_as_invalid(ch_ent->second, get_block_hash(ch_ent->second.bl)); - MERROR("The block was inserted as invalid while connecting new alternative chain, block_id: " << get_block_hash(ch_ent->second.bl)); - m_alternative_chains.erase(*alt_ch_iter++); + 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++; for(auto alt_ch_to_orph_iter = alt_ch_iter; alt_ch_to_orph_iter != alt_chain.end(); ) { - add_block_as_invalid((*alt_ch_to_orph_iter)->second, (*alt_ch_to_orph_iter)->first); - m_alternative_chains.erase(*alt_ch_to_orph_iter++); + 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; } @@ -1101,9 +1111,9 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash:: } //removing alt_chain entries from alternative chains container - for (auto ch_ent: alt_chain) + for (const auto &bei: alt_chain) { - m_alternative_chains.erase(ch_ent); + m_db->remove_alt_block(cryptonote::get_block_hash(bei.bl)); } m_hardfork->reorganize_from_chain_height(split_height); @@ -1119,7 +1129,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash:: //------------------------------------------------------------------ // This function calculates the difficulty target for the block being added to // an alternate chain. -difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::const_iterator>& alt_chain, block_extended_info& bei) const +difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std::list<block_extended_info>& alt_chain, block_extended_info& bei) const { if (m_fixed_difficulty) { @@ -1137,7 +1147,7 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std: CRITICAL_REGION_LOCAL(m_blockchain_lock); // Figure out start and stop offsets for main chain blocks - size_t main_chain_stop_offset = alt_chain.size() ? alt_chain.front()->second.height : bei.height; + size_t main_chain_stop_offset = alt_chain.size() ? alt_chain.front().height : bei.height; size_t main_chain_count = DIFFICULTY_BLOCKS_COUNT - std::min(static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT), alt_chain.size()); main_chain_count = std::min(main_chain_count, main_chain_stop_offset); size_t main_chain_start_offset = main_chain_stop_offset - main_chain_count; @@ -1155,10 +1165,10 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std: // make sure we haven't accidentally grabbed too many blocks...maybe don't need this check? CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= DIFFICULTY_BLOCKS_COUNT, false, "Internal error, alt_chain.size()[" << alt_chain.size() << "] + vtimestampsec.size()[" << timestamps.size() << "] NOT <= DIFFICULTY_WINDOW[]" << DIFFICULTY_BLOCKS_COUNT); - for (auto it : alt_chain) + for (const auto &bei : alt_chain) { - timestamps.push_back(it->second.bl.timestamp); - cumulative_difficulties.push_back(it->second.cumulative_difficulty); + timestamps.push_back(bei.bl.timestamp); + cumulative_difficulties.push_back(bei.cumulative_difficulty); } } // if the alt chain is long enough for the difficulty calc, grab difficulties @@ -1170,10 +1180,10 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std: size_t count = 0; size_t max_i = timestamps.size()-1; // get difficulties and timestamps from most recent blocks in alt chain - for(auto it: boost::adaptors::reverse(alt_chain)) + for (const auto bei: boost::adaptors::reverse(alt_chain)) { - timestamps[max_i - count] = it->second.bl.timestamp; - cumulative_difficulties[max_i - count] = it->second.cumulative_difficulty; + timestamps[max_i - count] = bei.bl.timestamp; + cumulative_difficulties[max_i - count] = bei.cumulative_difficulty; count++; if(count >= DIFFICULTY_BLOCKS_COUNT) break; @@ -1373,7 +1383,8 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, // just as we compare it, we'll just use a slightly old template, but // this would be the case anyway if we'd lock, and the change happened // just after the block template was created - if (!memcmp(&miner_address, &m_btc_address, sizeof(cryptonote::account_public_address)) && m_btc_nonce == ex_nonce && m_btc_pool_cookie == m_tx_pool.cookie()) { + if (!memcmp(&miner_address, &m_btc_address, sizeof(cryptonote::account_public_address)) && m_btc_nonce == ex_nonce + && m_btc_pool_cookie == m_tx_pool.cookie() && m_btc.prev_id == get_tail_id()) { MDEBUG("Using cached template"); m_btc.timestamp = time(NULL); // update timestamp unconditionally b = m_btc; @@ -1391,16 +1402,17 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, //build alternative subchain, front -> mainchain, back -> alternative head //block is not related with head of main chain //first of all - look in alternative chains container - auto it_prev = m_alternative_chains.find(*from_block); + alt_block_data_t prev_data; + bool parent_in_alt = m_db->get_alt_block(*from_block, &prev_data, NULL); bool parent_in_main = m_db->block_exists(*from_block); - if(it_prev == m_alternative_chains.end() && !parent_in_main) + if (!parent_in_alt && !parent_in_main) { MERROR("Unknown from block"); return false; } //we have new block in alternative chain - std::list<blocks_ext_by_hash::const_iterator> alt_chain; + std::list<block_extended_info> alt_chain; block_verification_context bvc = boost::value_initialized<block_verification_context>(); std::vector<uint64_t> timestamps; if (!build_alt_chain(*from_block, alt_chain, timestamps, bvc)) @@ -1415,7 +1427,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, } else { - height = alt_chain.back()->second.height + 1; + height = alt_chain.back().height + 1; } b.major_version = m_hardfork->get_ideal_version(height); b.minor_version = m_hardfork->get_ideal_version(); @@ -1430,14 +1442,14 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, } else { - median_weight = it_prev->second.block_cumulative_weight - it_prev->second.block_cumulative_weight / 20; - already_generated_coins = alt_chain.back()->second.already_generated_coins; + median_weight = prev_data.cumulative_weight - prev_data.cumulative_weight / 20; + already_generated_coins = alt_chain.back().already_generated_coins; } // FIXME: consider moving away from block_extended_info at some point block_extended_info bei = boost::value_initialized<block_extended_info>(); bei.bl = b; - bei.height = alt_chain.size() ? it_prev->second.height + 1 : m_db->get_block_height(*from_block) + 1; + bei.height = alt_chain.size() ? prev_data.height + 1 : m_db->get_block_height(*from_block) + 1; diffic = get_next_difficulty_for_alternative_chain(alt_chain, bei); } @@ -1616,16 +1628,25 @@ bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vect return true; } //------------------------------------------------------------------ -bool Blockchain::build_alt_chain(const crypto::hash &prev_id, std::list<blocks_ext_by_hash::const_iterator>& alt_chain, std::vector<uint64_t> ×tamps, block_verification_context& bvc) const +bool Blockchain::build_alt_chain(const crypto::hash &prev_id, std::list<block_extended_info>& alt_chain, std::vector<uint64_t> ×tamps, block_verification_context& bvc) const { //build alternative subchain, front -> mainchain, back -> alternative head - blocks_ext_by_hash::const_iterator alt_it = m_alternative_chains.find(prev_id); + cryptonote::alt_block_data_t data; + cryptonote::blobdata blob; + bool found = m_db->get_alt_block(prev_id, &data, &blob); timestamps.clear(); - while(alt_it != m_alternative_chains.end()) + while(found) { - alt_chain.push_front(alt_it); - timestamps.push_back(alt_it->second.bl.timestamp); - alt_it = m_alternative_chains.find(alt_it->second.bl.prev_id); + block_extended_info bei; + CHECK_AND_ASSERT_MES(cryptonote::parse_and_validate_block_from_blob(blob, bei.bl), false, "Failed to parse alt block"); + bei.height = data.height; + bei.block_cumulative_weight = data.cumulative_weight; + bei.cumulative_difficulty = data.cumulative_difficulty_high; + bei.cumulative_difficulty = (bei.cumulative_difficulty << 64) + data.cumulative_difficulty_low; + bei.already_generated_coins = data.already_generated_coins; + timestamps.push_back(bei.bl.timestamp); + alt_chain.push_front(std::move(bei)); + found = m_db->get_alt_block(bei.bl.prev_id, &data, &blob); } // if block to be added connects to known blocks that aren't part of the @@ -1633,20 +1654,20 @@ bool Blockchain::build_alt_chain(const crypto::hash &prev_id, std::list<blocks_e if(!alt_chain.empty()) { // make sure alt chain doesn't somehow start past the end of the main chain - CHECK_AND_ASSERT_MES(m_db->height() > alt_chain.front()->second.height, false, "main blockchain wrong height"); + CHECK_AND_ASSERT_MES(m_db->height() > alt_chain.front().height, false, "main blockchain wrong height"); // make sure that the blockchain contains the block that should connect // this alternate chain with it. - if (!m_db->block_exists(alt_chain.front()->second.bl.prev_id)) + if (!m_db->block_exists(alt_chain.front().bl.prev_id)) { MERROR("alternate chain does not appear to connect to main chain..."); return false; } // make sure block connects correctly to the main chain - auto h = m_db->get_block_hash_from_height(alt_chain.front()->second.height - 1); - CHECK_AND_ASSERT_MES(h == alt_chain.front()->second.bl.prev_id, false, "alternative chain has wrong connection to main chain"); - complete_timestamps_vector(m_db->get_block_height(alt_chain.front()->second.bl.prev_id), timestamps); + auto h = m_db->get_block_hash_from_height(alt_chain.front().height - 1); + CHECK_AND_ASSERT_MES(h == alt_chain.front().bl.prev_id, false, "alternative chain has wrong connection to main chain"); + complete_timestamps_vector(m_db->get_block_height(alt_chain.front().bl.prev_id), timestamps); } // if block not associated with known alternate chain else @@ -1701,12 +1722,13 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id //block is not related with head of main chain //first of all - look in alternative chains container - auto it_prev = m_alternative_chains.find(b.prev_id); + alt_block_data_t prev_data; + bool parent_in_alt = m_db->get_alt_block(b.prev_id, &prev_data, NULL); bool parent_in_main = m_db->block_exists(b.prev_id); - if(it_prev != m_alternative_chains.end() || parent_in_main) + if (parent_in_alt || parent_in_main) { //we have new block in alternative chain - std::list<blocks_ext_by_hash::const_iterator> alt_chain; + std::list<block_extended_info> alt_chain; std::vector<uint64_t> timestamps; if (!build_alt_chain(b.prev_id, alt_chain, timestamps, bvc)) return false; @@ -1714,10 +1736,10 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id // FIXME: consider moving away from block_extended_info at some point block_extended_info bei = boost::value_initialized<block_extended_info>(); bei.bl = b; - const uint64_t prev_height = alt_chain.size() ? it_prev->second.height : m_db->get_block_height(b.prev_id); + const uint64_t prev_height = alt_chain.size() ? prev_data.height : m_db->get_block_height(b.prev_id); bei.height = prev_height + 1; uint64_t block_reward = get_outs_money_amount(b.miner_tx); - bei.already_generated_coins = block_reward + (alt_chain.size() ? it_prev->second.already_generated_coins : m_db->get_block_already_generated_coins(prev_height)); + bei.already_generated_coins = block_reward + (alt_chain.size() ? prev_data.already_generated_coins : m_db->get_block_already_generated_coins(prev_height)); // verify that the block's timestamp is within the acceptable range // (not earlier than the median of the last X blocks) @@ -1761,7 +1783,8 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id difficulty_type main_chain_cumulative_difficulty = m_db->get_block_cumulative_difficulty(m_db->height() - 1); if (alt_chain.size()) { - bei.cumulative_difficulty = it_prev->second.cumulative_difficulty; + bei.cumulative_difficulty = prev_data.cumulative_difficulty_high; + bei.cumulative_difficulty = (bei.cumulative_difficulty << 64) + prev_data.cumulative_difficulty_low; } else { @@ -1772,15 +1795,21 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id // add block to alternate blocks storage, // as well as the current "alt chain" container - auto i_res = m_alternative_chains.insert(blocks_ext_by_hash::value_type(id, bei)); - CHECK_AND_ASSERT_MES(i_res.second, false, "insertion of new alternative block returned as it already exist"); - alt_chain.push_back(i_res.first); + CHECK_AND_ASSERT_MES(!m_db->get_alt_block(id, NULL, NULL), false, "insertion of new alternative block returned as it already exists"); + cryptonote::alt_block_data_t data; + data.height = bei.height; + data.cumulative_weight = bei.block_cumulative_weight; + data.cumulative_difficulty_low = (bei.cumulative_difficulty & 0xffffffffffffffff).convert_to<uint64_t>(); + data.cumulative_difficulty_high = ((bei.cumulative_difficulty >> 64) & 0xffffffffffffffff).convert_to<uint64_t>(); + data.already_generated_coins = bei.already_generated_coins; + m_db->add_alt_block(id, data, cryptonote::block_to_blob(bei.bl)); + alt_chain.push_back(bei); // FIXME: is it even possible for a checkpoint to show up not on the main chain? if(is_a_checkpoint) { //do reorganize! - MGINFO_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_db->height() - 1 << ", checkpoint is found in alternative chain on height " << bei.height); + MGINFO_GREEN("###### REORGANIZE on height: " << alt_chain.front().height << " of " << m_db->height() - 1 << ", checkpoint is found in alternative chain on height " << bei.height); bool r = switch_to_alternative_blockchain(alt_chain, true); @@ -1792,7 +1821,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id else if(main_chain_cumulative_difficulty < bei.cumulative_difficulty) //check if difficulty bigger then in main chain { //do reorganize! - MGINFO_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_db->height() - 1 << " with cum_difficulty " << m_db->get_block_cumulative_difficulty(m_db->height() - 1) << std::endl << " alternative blockchain size: " << alt_chain.size() << " with cum_difficulty " << bei.cumulative_difficulty); + MGINFO_GREEN("###### REORGANIZE on height: " << alt_chain.front().height << " of " << m_db->height() - 1 << " with cum_difficulty " << m_db->get_block_cumulative_difficulty(m_db->height() - 1) << std::endl << " alternative blockchain size: " << alt_chain.size() << " with cum_difficulty " << bei.cumulative_difficulty); bool r = switch_to_alternative_blockchain(alt_chain, false); if (r) @@ -1812,7 +1841,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id //block orphaned bvc.m_marked_as_orphaned = true; MERROR_VER("Block recognized as orphaned and rejected, id = " << id << ", height " << block_height - << ", parent in alt " << (it_prev != m_alternative_chains.end()) << ", parent in main " << parent_in_main + << ", parent in alt " << parent_in_alt << ", parent in main " << parent_in_main << " (parent " << b.prev_id << ", current top " << get_tail_id() << ", chain height " << get_current_blockchain_height() << ")"); } @@ -1891,10 +1920,14 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO if (missed_tx_ids.size() != 0) { - LOG_ERROR("Error retrieving blocks, missed " << missed_tx_ids.size() - << " transactions for block with hash: " << get_block_hash(bl.second) - << std::endl - ); + // do not display an error if the peer asked for an unpruned block which we are not meant to have + if (tools::has_unpruned_block(get_block_height(bl.second), get_current_blockchain_height(), get_blockchain_pruning_seed())) + { + LOG_ERROR("Error retrieving blocks, missed " << missed_tx_ids.size() + << " transactions for block with hash: " << get_block_hash(bl.second) + << std::endl + ); + } // append missed transaction hashes to response missed_ids field, // as done below if any standalone transactions were requested @@ -1917,11 +1950,20 @@ bool Blockchain::get_alternative_blocks(std::vector<block>& blocks) const LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); - blocks.reserve(m_alternative_chains.size()); - for (const auto& alt_bl: m_alternative_chains) - { - blocks.push_back(alt_bl.second.bl); - } + blocks.reserve(m_db->get_alt_block_count()); + m_db->for_all_alt_blocks([&blocks](const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata *blob) { + if (!blob) + { + MERROR("No blob, but blobs were requested"); + return false; + } + cryptonote::block bl; + if (cryptonote::parse_and_validate_block_from_blob(*blob, bl)) + blocks.push_back(std::move(bl)); + else + MERROR("Failed to parse block from blob"); + return true; + }, true); return true; } //------------------------------------------------------------------ @@ -1929,7 +1971,7 @@ size_t Blockchain::get_alternative_blocks_count() const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); - return m_alternative_chains.size(); + return m_db->get_alt_block_count(); } //------------------------------------------------------------------ // This function adds the output specified by <amount, i> to the result_outs container @@ -2033,7 +2075,6 @@ bool Blockchain::get_output_distribution(uint64_t amount, uint64_t from_height, if (to_height > 0 && to_height < from_height) return false; - const uint64_t real_start_height = start_height; if (from_height > start_height) start_height = from_height; @@ -2047,7 +2088,7 @@ bool Blockchain::get_output_distribution(uint64_t amount, uint64_t from_height, { std::vector<uint64_t> heights; heights.reserve(to_height + 1 - start_height); - uint64_t real_start_height = start_height > 0 ? start_height-1 : start_height; + const uint64_t real_start_height = start_height > 0 ? start_height-1 : start_height; for (uint64_t h = real_start_height; h <= to_height; ++h) heights.push_back(h); distribution = m_db->get_block_cumulative_rct_outputs(heights); @@ -2417,9 +2458,9 @@ bool Blockchain::have_block(const crypto::hash& id) const return true; } - if(m_alternative_chains.count(id)) + if(m_db->get_alt_block(id, NULL, NULL)) { - LOG_PRINT_L2("block " << id << " found in m_alternative_chains"); + LOG_PRINT_L2("block " << id << " found in alternative chains"); return true; } @@ -3248,7 +3289,6 @@ uint64_t Blockchain::get_dynamic_base_fee(uint64_t block_reward, size_t median_b bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const { const uint8_t version = get_current_hard_fork_version(); - const uint64_t blockchain_height = m_db->height(); uint64_t median = 0; uint64_t already_generated_coins = 0; @@ -3844,6 +3884,7 @@ leave: catch (const KEY_IMAGE_EXISTS& e) { LOG_ERROR("Error adding block with hash: " << id << " to blockchain, what = " << e.what()); + m_batch_success = false; bvc.m_verifivation_failed = true; return_tx_to_pool(txs); return false; @@ -3852,6 +3893,7 @@ leave: { //TODO: figure out the best way to deal with this failure LOG_ERROR("Error adding block with hash: " << id << " to blockchain, what = " << e.what()); + m_batch_success = false; bvc.m_verifivation_failed = true; return_tx_to_pool(txs); return false; @@ -3954,14 +3996,12 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti const uint64_t db_height = m_db->height(); const uint8_t hf_version = get_current_hard_fork_version(); uint64_t full_reward_zone = get_min_block_weight(hf_version); - uint64_t long_term_block_weight; if (hf_version < HF_VERSION_LONG_TERM_BLOCK_WEIGHT) { std::vector<uint64_t> weights; get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW); m_current_block_cumul_weight_median = epee::misc_utils::median(weights); - long_term_block_weight = weights.back(); } else { @@ -3983,7 +4023,7 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti m_long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median); uint64_t short_term_constraint = m_long_term_effective_median_block_weight + m_long_term_effective_median_block_weight * 2 / 5; - long_term_block_weight = std::min<uint64_t>(block_weight, short_term_constraint); + uint64_t long_term_block_weight = std::min<uint64_t>(block_weight, short_term_constraint); if (db_height == 1) { @@ -4161,7 +4201,10 @@ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync) try { - m_db->batch_stop(); + if (m_batch_success) + m_db->batch_stop(); + else + m_db->batch_abort(); success = true; } catch (const std::exception &e) @@ -4385,6 +4428,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete m_tx_pool.lock(); m_blockchain_lock.lock(); } + m_batch_success = true; const uint64_t height = m_db->height(); if ((height + blocks_entry.size()) < m_blocks_hash_check.size()) @@ -4802,11 +4846,35 @@ std::list<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>> { std::list<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>> chains; - for (const auto &i: m_alternative_chains) + blocks_ext_by_hash alt_blocks; + alt_blocks.reserve(m_db->get_alt_block_count()); + m_db->for_all_alt_blocks([&alt_blocks](const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata *blob) { + if (!blob) + { + MERROR("No blob, but blobs were requested"); + return false; + } + cryptonote::block bl; + block_extended_info bei; + if (cryptonote::parse_and_validate_block_from_blob(*blob, bei.bl)) + { + bei.height = data.height; + bei.block_cumulative_weight = data.cumulative_weight; + bei.cumulative_difficulty = data.cumulative_difficulty_high; + bei.cumulative_difficulty = (bei.cumulative_difficulty << 64) + data.cumulative_difficulty_low; + bei.already_generated_coins = data.already_generated_coins; + alt_blocks.insert(std::make_pair(cryptonote::get_block_hash(bei.bl), std::move(bei))); + } + else + MERROR("Failed to parse block from blob"); + return true; + }, true); + + for (const auto &i: alt_blocks) { - const crypto::hash &top = i.first; + const crypto::hash top = cryptonote::get_block_hash(i.second.bl); bool found = false; - for (const auto &j: m_alternative_chains) + for (const auto &j: alt_blocks) { if (j.second.bl.prev_id == top) { @@ -4820,7 +4888,7 @@ std::list<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>> auto h = i.second.bl.prev_id; chain.push_back(top); blocks_ext_by_hash::const_iterator prev; - while ((prev = m_alternative_chains.find(h)) != m_alternative_chains.end()) + while ((prev = alt_blocks.find(h)) != alt_blocks.end()) { chain.push_back(h); h = prev->second.bl.prev_id; @@ -4837,7 +4905,7 @@ void Blockchain::cancel() } #if defined(PER_BLOCK_CHECKPOINT) -static const char expected_block_hashes_hash[] = "570ce2357b08fadac6058e34f95c5e08323f9325de260d07b091a281a948a7b0"; +static const char expected_block_hashes_hash[] = "7dafb40b414a0e59bfced6682ef519f0b416bc914dd3d622b72e0dd1a47117c2"; void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints) { if (get_checkpoints == nullptr || !m_fast_sync) diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 6200ec87e..b1c23b704 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -92,24 +92,13 @@ namespace cryptonote { public: /** - * @brief Now-defunct (TODO: remove) struct from in-memory blockchain - */ - struct transaction_chain_entry - { - transaction tx; - uint64_t m_keeper_block_height; - size_t m_blob_size; - std::vector<uint64_t> m_global_output_indexes; - }; - - /** * @brief container for passing a block and metadata about it on the blockchain */ struct block_extended_info { block bl; //!< the block uint64_t height; //!< the height of the block in the blockchain - size_t block_cumulative_weight; //!< the weight of the block + uint64_t block_cumulative_weight; //!< the weight of the block difficulty_type cumulative_difficulty; //!< the accumulated difficulty after that block uint64_t already_generated_coins; //!< the total coins minted after that block }; @@ -1011,20 +1000,12 @@ namespace cryptonote #endif // TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage - typedef std::unordered_map<crypto::hash, size_t> blocks_by_id_index; - - typedef std::unordered_map<crypto::hash, transaction_chain_entry> transactions_container; - typedef std::unordered_set<crypto::key_image> key_images_container; typedef std::vector<block_extended_info> blocks_container; typedef std::unordered_map<crypto::hash, block_extended_info> blocks_ext_by_hash; - typedef std::unordered_map<crypto::hash, block> blocks_by_hash; - - typedef std::map<uint64_t, std::vector<std::pair<crypto::hash, size_t>>> outputs_container; //crypto::hash - tx hash, size_t - index of out in transaction - BlockchainDB* m_db; @@ -1033,7 +1014,6 @@ namespace cryptonote mutable epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock // main chain - transactions_container m_transactions; size_t m_current_block_cumul_weight_limit; size_t m_current_block_cumul_weight_median; @@ -1074,9 +1054,6 @@ namespace cryptonote boost::thread_group m_async_pool; std::unique_ptr<boost::asio::io_service::work> m_async_work_idle; - // all alternative chains - blocks_ext_by_hash m_alternative_chains; // crypto::hash -> block_extended_info - // some invalid blocks blocks_ext_by_hash m_invalid_blocks; // crypto::hash -> block_extended_info @@ -1102,6 +1079,9 @@ namespace cryptonote uint64_t m_btc_expected_reward; bool m_btc_valid; + + bool m_batch_success; + std::shared_ptr<tools::Notify> m_block_notify; std::shared_ptr<tools::Notify> m_reorg_notify; @@ -1183,7 +1163,7 @@ namespace cryptonote * * @return false if the reorganization fails, otherwise true */ - bool switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::const_iterator>& alt_chain, bool discard_disconnected_chain); + bool switch_to_alternative_blockchain(std::list<block_extended_info>& alt_chain, bool discard_disconnected_chain); /** * @brief removes the most recent block from the blockchain @@ -1246,7 +1226,7 @@ namespace cryptonote * * @return true on success, false otherwise */ - bool build_alt_chain(const crypto::hash &prev_id, std::list<blocks_ext_by_hash::const_iterator>& alt_chain, std::vector<uint64_t> ×tamps, block_verification_context& bvc) const; + bool build_alt_chain(const crypto::hash &prev_id, std::list<block_extended_info>& alt_chain, std::vector<uint64_t> ×tamps, block_verification_context& bvc) const; /** * @brief gets the difficulty requirement for a new block on an alternate chain @@ -1256,7 +1236,7 @@ namespace cryptonote * * @return the difficulty requirement */ - difficulty_type get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::const_iterator>& alt_chain, block_extended_info& bei) const; + difficulty_type get_next_difficulty_for_alternative_chain(const std::list<block_extended_info>& alt_chain, block_extended_info& bei) const; /** * @brief sanity checks a miner transaction before validating an entire block diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index be1ea5a17..0f69f3620 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -208,6 +208,11 @@ namespace cryptonote "is acted upon." , "" }; + static const command_line::arg_descriptor<bool> arg_keep_alt_blocks = { + "keep-alt-blocks" + , "Keep alternative blocks on restart" + , false + }; //----------------------------------------------------------------------------------------------- core::core(i_cryptonote_protocol* pprotocol): @@ -325,6 +330,7 @@ namespace cryptonote command_line::add_arg(desc, arg_prune_blockchain); command_line::add_arg(desc, arg_reorg_notify); command_line::add_arg(desc, arg_block_rate_notify); + command_line::add_arg(desc, arg_keep_alt_blocks); miner::init_options(desc); BlockchainDB::init_options(desc); @@ -447,6 +453,7 @@ namespace cryptonote m_nettype = FAKECHAIN; } bool r = handle_command_line(vm); + CHECK_AND_ASSERT_MES(r, false, "Failed to handle command line"); std::string db_type = command_line::get_arg(vm, cryptonote::arg_db_type); std::string db_sync_mode = command_line::get_arg(vm, cryptonote::arg_db_sync_mode); @@ -456,6 +463,7 @@ namespace cryptonote std::string check_updates_string = command_line::get_arg(vm, arg_check_updates); size_t max_txpool_weight = command_line::get_arg(vm, arg_max_txpool_weight); bool prune_blockchain = command_line::get_arg(vm, arg_prune_blockchain); + bool keep_alt_blocks = command_line::get_arg(vm, arg_keep_alt_blocks); boost::filesystem::path folder(m_config_folder); if (m_nettype == FAKECHAIN) @@ -634,6 +642,7 @@ namespace cryptonote }; const difficulty_type fixed_difficulty = command_line::get_arg(vm, arg_fixed_difficulty); r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? ®test_test_options : test_options, fixed_difficulty, get_checkpoints); + CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); r = m_mempool.init(max_txpool_weight); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); @@ -671,12 +680,21 @@ namespace cryptonote r = m_miner.init(vm, m_nettype); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize miner instance"); + if (!keep_alt_blocks && !m_blockchain_storage.get_db().is_read_only()) + m_blockchain_storage.get_db().drop_alt_blocks(); + if (prune_blockchain) { // display a message if the blockchain is not pruned yet - if (m_blockchain_storage.get_current_blockchain_height() > 1 && !m_blockchain_storage.get_blockchain_pruning_seed()) + if (!m_blockchain_storage.get_blockchain_pruning_seed()) + { MGINFO("Pruning blockchain..."); - CHECK_AND_ASSERT_MES(m_blockchain_storage.prune_blockchain(), false, "Failed to prune blockchain"); + CHECK_AND_ASSERT_MES(m_blockchain_storage.prune_blockchain(), false, "Failed to prune blockchain"); + } + else + { + CHECK_AND_ASSERT_MES(m_blockchain_storage.update_blockchain_pruning(), false, "Failed to update blockchain pruning"); + } } return load_state_data(); diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index c1cbe2acd..49d5a8ccc 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -95,13 +95,17 @@ namespace cryptonote // the whole prepare/handle/cleanup incoming block sequence. class LockedTXN { public: - LockedTXN(Blockchain &b): m_blockchain(b), m_batch(false) { + LockedTXN(Blockchain &b): m_blockchain(b), m_batch(false), m_active(false) { m_batch = m_blockchain.get_db().batch_start(); + m_active = true; } - ~LockedTXN() { try { if (m_batch) { m_blockchain.get_db().batch_stop(); } } catch (const std::exception &e) { MWARNING("LockedTXN dtor filtering exception: " << e.what()); } } + void commit() { try { if (m_batch && m_active) { m_blockchain.get_db().batch_stop(); m_active = false; } } catch (const std::exception &e) { MWARNING("LockedTXN::commit filtering exception: " << e.what()); } } + void abort() { try { if (m_batch && m_active) { m_blockchain.get_db().batch_abort(); m_active = false; } } catch (const std::exception &e) { MWARNING("LockedTXN::abort filtering exception: " << e.what()); } } + ~LockedTXN() { abort(); } private: Blockchain &m_blockchain; bool m_batch; + bool m_active; }; } //--------------------------------------------------------------------------------- @@ -255,6 +259,7 @@ namespace cryptonote if (!insert_key_images(tx, id, kept_by_block)) return false; m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)tx_weight, receive_time), id); + lock.commit(); } catch (const std::exception &e) { @@ -299,6 +304,7 @@ namespace cryptonote if (!insert_key_images(tx, id, kept_by_block)) return false; m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)tx_weight, receive_time), id); + lock.commit(); } catch (const std::exception &e) { @@ -398,6 +404,7 @@ namespace cryptonote return; } } + lock.commit(); if (changed) ++m_cookie; if (m_txpool_weight > bytes) @@ -494,6 +501,7 @@ namespace cryptonote m_blockchain.remove_txpool_tx(id); m_txpool_weight -= tx_weight; remove_transaction_keyimages(tx, id); + lock.commit(); } catch (const std::exception &e) { @@ -578,6 +586,7 @@ namespace cryptonote // ignore error } } + lock.commit(); ++m_cookie; } return true; @@ -641,6 +650,7 @@ namespace cryptonote // continue } } + lock.commit(); } //--------------------------------------------------------------------------------- size_t tx_memory_pool::get_transactions_count(bool include_unrelayed_txes) const @@ -1119,6 +1129,7 @@ namespace cryptonote } } } + lock.commit(); if (changed) ++m_cookie; } @@ -1271,6 +1282,7 @@ namespace cryptonote append_key_images(k_images, tx); LOG_PRINT_L2(" added, new block weight " << total_weight << "/" << max_total_weight << ", coinbase " << print_money(best_coinbase)); } + lock.commit(); expected_reward = best_coinbase; LOG_PRINT_L2("Block template filled with " << bl.tx_hashes.size() << " txes, weight " @@ -1336,6 +1348,7 @@ namespace cryptonote // continue } } + lock.commit(); } if (n_removed > 0) ++m_cookie; @@ -1395,6 +1408,7 @@ namespace cryptonote // ignore error } } + lock.commit(); } m_cookie = 0; diff --git a/src/cryptonote_core/tx_sanity_check.cpp b/src/cryptonote_core/tx_sanity_check.cpp index 10198a3d3..36be91f2c 100644 --- a/src/cryptonote_core/tx_sanity_check.cpp +++ b/src/cryptonote_core/tx_sanity_check.cpp @@ -88,7 +88,7 @@ bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob std::vector<uint64_t> offsets(rct_indices.begin(), rct_indices.end()); uint64_t median = epee::misc_utils::median(offsets); - if (median < n_available * 9 / 10) + if (median < n_available * 6 / 10) { MERROR("median is " << median << "/" << n_available); return false; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index 0927b5d7f..dcc5ec6ed 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -52,6 +52,7 @@ PUSH_WARNINGS DISABLE_VS_WARNINGS(4355) #define LOCALHOST_INT 2130706433 +#define CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT 500 namespace cryptonote { diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 8958af7c7..03d04a074 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -341,6 +341,11 @@ namespace cryptonote if(m_core.have_block(hshd.top_id)) { + if (target > hshd.current_height) + { + MINFO(context << "peer is not ahead of us and we're syncing, disconnecting"); + return false; + } context.m_state = cryptonote_connection_context::state_normal; if(is_inital && target == m_core.get_current_blockchain_height()) on_connection_synchronized(); @@ -809,12 +814,27 @@ namespace cryptonote NOTIFY_NEW_FLUFFY_BLOCK::request fluffy_response; fluffy_response.b.block = t_serializable_object_to_blob(b); fluffy_response.current_blockchain_height = arg.current_blockchain_height; + std::vector<bool> seen(b.tx_hashes.size(), false); for(auto& tx_idx: arg.missing_tx_indices) { if(tx_idx < b.tx_hashes.size()) { MDEBUG(" tx " << b.tx_hashes[tx_idx]); + if (seen[tx_idx]) + { + LOG_ERROR_CCONTEXT + ( + "Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX" + << ", request is asking for duplicate tx " + << ", tx index = " << tx_idx << ", block tx count " << b.tx_hashes.size() + << ", block_height = " << arg.current_blockchain_height + << ", dropping connection" + ); + drop_connection(context, true, false); + return 1; + } txids.push_back(b.tx_hashes[tx_idx]); + seen[tx_idx] = true; } else { @@ -914,6 +934,17 @@ namespace cryptonote int t_cryptonote_protocol_handler<t_core>::handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context) { MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_GET_OBJECTS (" << arg.blocks.size() << " blocks, " << arg.txs.size() << " txes)"); + + if (arg.blocks.size() + arg.txs.size() > CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT) + { + LOG_ERROR_CCONTEXT( + "Requested objects count is too big (" + << arg.blocks.size() + arg.txs.size() << ") expected not more then " + << CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT); + drop_connection(context, false, false); + return 1; + } + NOTIFY_RESPONSE_GET_OBJECTS::request rsp; if(!m_core.handle_get_objects(arg, rsp, context)) { diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 17b945c9a..e3da12252 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -40,10 +40,11 @@ t_command_parser_executor::t_command_parser_executor( uint32_t ip , uint16_t port , const boost::optional<tools::login>& login + , const epee::net_utils::ssl_options_t& ssl_options , bool is_rpc , cryptonote::core_rpc_server* rpc_server ) - : m_executor(ip, port, login, is_rpc, rpc_server) + : m_executor(ip, port, login, ssl_options, is_rpc, rpc_server) {} bool t_command_parser_executor::print_peer_list(const std::vector<std::string>& args) @@ -525,18 +526,6 @@ bool t_command_parser_executor::in_peers(const std::vector<std::string>& args) return m_executor.in_peers(limit); } -bool t_command_parser_executor::start_save_graph(const std::vector<std::string>& args) -{ - if (!args.empty()) return false; - return m_executor.start_save_graph(); -} - -bool t_command_parser_executor::stop_save_graph(const std::vector<std::string>& args) -{ - if (!args.empty()) return false; - return m_executor.stop_save_graph(); -} - bool t_command_parser_executor::hard_fork_info(const std::vector<std::string>& args) { int version; @@ -595,6 +584,13 @@ bool t_command_parser_executor::unban(const std::vector<std::string>& args) return m_executor.unban(ip); } +bool t_command_parser_executor::banned(const std::vector<std::string>& args) +{ + if (args.size() != 1) return false; + std::string address = args[0]; + return m_executor.banned(address); +} + bool t_command_parser_executor::flush_txpool(const std::vector<std::string>& args) { if (args.size() > 1) return false; diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h index 098018642..d39bc1c9b 100644 --- a/src/daemon/command_parser_executor.h +++ b/src/daemon/command_parser_executor.h @@ -40,6 +40,7 @@ #include "daemon/rpc_command_executor.h" #include "common/common_fwd.h" +#include "net/net_fwd.h" #include "rpc/core_rpc_server.h" namespace daemonize { @@ -53,6 +54,7 @@ public: uint32_t ip , uint16_t port , const boost::optional<tools::login>& login + , const epee::net_utils::ssl_options_t& ssl_options , bool is_rpc , cryptonote::core_rpc_server* rpc_server = NULL ); @@ -113,10 +115,6 @@ public: bool in_peers(const std::vector<std::string>& args); - bool start_save_graph(const std::vector<std::string>& args); - - bool stop_save_graph(const std::vector<std::string>& args); - bool hard_fork_info(const std::vector<std::string>& args); bool show_bans(const std::vector<std::string>& args); @@ -125,6 +123,8 @@ public: bool unban(const std::vector<std::string>& args); + bool banned(const std::vector<std::string>& args); + bool flush_txpool(const std::vector<std::string>& args); bool output_histogram(const std::vector<std::string>& args); diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 69ad6ff10..aecdda52c 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -43,10 +43,11 @@ t_command_server::t_command_server( uint32_t ip , uint16_t port , const boost::optional<tools::login>& login + , const epee::net_utils::ssl_options_t& ssl_options , bool is_rpc , cryptonote::core_rpc_server* rpc_server ) - : m_parser(ip, port, login, is_rpc, rpc_server) + : m_parser(ip, port, login, ssl_options, is_rpc, rpc_server) , m_command_lookup() , m_is_rpc(is_rpc) { @@ -214,16 +215,6 @@ t_command_server::t_command_server( , "Set the <max_number> of in peers." ); m_command_lookup.set_handler( - "start_save_graph" - , std::bind(&t_command_parser_executor::start_save_graph, &m_parser, p::_1) - , "Start saving data for dr monero." - ); - m_command_lookup.set_handler( - "stop_save_graph" - , std::bind(&t_command_parser_executor::stop_save_graph, &m_parser, p::_1) - , "Stop saving data for dr monero." - ); - m_command_lookup.set_handler( "hard_fork_info" , std::bind(&t_command_parser_executor::hard_fork_info, &m_parser, p::_1) , "Print the hard fork voting information." @@ -242,10 +233,16 @@ t_command_server::t_command_server( m_command_lookup.set_handler( "unban" , std::bind(&t_command_parser_executor::unban, &m_parser, p::_1) - , "unban <IP>" + , "unban <address>" , "Unban a given <IP>." ); m_command_lookup.set_handler( + "banned" + , std::bind(&t_command_parser_executor::banned, &m_parser, p::_1) + , "banned <address>" + , "Check whether an <address> is banned." + ); + m_command_lookup.set_handler( "flush_txpool" , std::bind(&t_command_parser_executor::flush_txpool, &m_parser, p::_1) , "flush_txpool [<txid>]" diff --git a/src/daemon/command_server.h b/src/daemon/command_server.h index c8e77f551..da532223e 100644 --- a/src/daemon/command_server.h +++ b/src/daemon/command_server.h @@ -43,6 +43,7 @@ Passing RPC commands: #include "common/common_fwd.h" #include "console_handler.h" #include "daemon/command_parser_executor.h" +#include "net/net_fwd.h" namespace daemonize { @@ -57,6 +58,7 @@ public: uint32_t ip , uint16_t port , const boost::optional<tools::login>& login + , const epee::net_utils::ssl_options_t& ssl_options , bool is_rpc = true , cryptonote::core_rpc_server* rpc_server = NULL ); diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 531c080de..5084b6283 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -45,6 +45,7 @@ #include "daemon/command_server.h" #include "daemon/command_server.h" #include "daemon/command_line_args.h" +#include "net/net_ssl.h" #include "version.h" using namespace epee; @@ -163,7 +164,7 @@ bool t_daemon::run(bool interactive) if (interactive && mp_internals->rpcs.size()) { // The first three variables are not used when the fourth is false - rpc_commands.reset(new daemonize::t_command_server(0, 0, boost::none, false, mp_internals->rpcs.front()->get_server())); + rpc_commands.reset(new daemonize::t_command_server(0, 0, boost::none, epee::net_utils::ssl_support_t::e_ssl_support_disabled, false, mp_internals->rpcs.front()->get_server())); rpc_commands->start_handling(std::bind(&daemonize::t_daemon::stop_p2p, this)); } diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index dbbb2308c..690d4d60e 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -324,7 +324,11 @@ int main(int argc, char const * argv[]) } } - daemonize::t_command_server rpc_commands{rpc_ip, rpc_port, std::move(login)}; + auto ssl_options = cryptonote::rpc_args::process_ssl(vm, true); + if (!ssl_options) + return 1; + + daemonize::t_command_server rpc_commands{rpc_ip, rpc_port, std::move(login), std::move(*ssl_options)}; if (rpc_commands.process_command_vec(command)) { return 0; diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 151baa33f..fedf992f1 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -54,7 +54,7 @@ namespace { std::string id_str; std::string port_str; - std::string elapsed = epee::misc_utils::get_time_interval_string(now - last_seen); + std::string elapsed = peer.last_seen == 0 ? "never" : epee::misc_utils::get_time_interval_string(now - last_seen); std::string ip_str = epee::string_tools::get_ip_string_from_int32(peer.ip); std::stringstream peer_id_str; peer_id_str << std::hex << std::setw(16) << peer.id; @@ -127,6 +127,7 @@ t_rpc_command_executor::t_rpc_command_executor( uint32_t ip , uint16_t port , const boost::optional<tools::login>& login + , const epee::net_utils::ssl_options_t& ssl_options , bool is_rpc , cryptonote::core_rpc_server* rpc_server ) @@ -137,7 +138,7 @@ t_rpc_command_executor::t_rpc_command_executor( boost::optional<epee::net_utils::http::login> http_login{}; if (login) http_login.emplace(login->username, login->password.password()); - m_rpc_client = new tools::t_rpc_client(ip, port, std::move(http_login)); + m_rpc_client = new tools::t_rpc_client(ip, port, std::move(http_login), ssl_options); } else { @@ -1529,59 +1530,6 @@ bool t_rpc_command_executor::in_peers(uint64_t limit) return true; } -bool t_rpc_command_executor::start_save_graph() -{ - cryptonote::COMMAND_RPC_START_SAVE_GRAPH::request req; - cryptonote::COMMAND_RPC_START_SAVE_GRAPH::response res; - std::string fail_message = "Unsuccessful"; - - if (m_is_rpc) - { - if (!m_rpc_client->rpc_request(req, res, "/start_save_graph", fail_message.c_str())) - { - return true; - } - } - - else - { - if (!m_rpc_server->on_start_save_graph(req, res) || res.status != CORE_RPC_STATUS_OK) - { - tools::fail_msg_writer() << make_error(fail_message, res.status); - return true; - } - } - - tools::success_msg_writer() << "Saving graph is now on"; - return true; -} - -bool t_rpc_command_executor::stop_save_graph() -{ - cryptonote::COMMAND_RPC_STOP_SAVE_GRAPH::request req; - cryptonote::COMMAND_RPC_STOP_SAVE_GRAPH::response res; - std::string fail_message = "Unsuccessful"; - - if (m_is_rpc) - { - if (!m_rpc_client->rpc_request(req, res, "/stop_save_graph", fail_message.c_str())) - { - return true; - } - } - - else - { - if (!m_rpc_server->on_stop_save_graph(req, res) || res.status != CORE_RPC_STATUS_OK) - { - tools::fail_msg_writer() << make_error(fail_message, res.status); - return true; - } - } - tools::success_msg_writer() << "Saving graph is now off"; - return true; -} - bool t_rpc_command_executor::hard_fork_info(uint8_t version) { cryptonote::COMMAND_RPC_HARD_FORK_INFO::request req; @@ -1640,14 +1588,14 @@ bool t_rpc_command_executor::print_bans() for (auto i = res.bans.begin(); i != res.bans.end(); ++i) { - tools::msg_writer() << epee::string_tools::get_ip_string_from_int32(i->ip) << " banned for " << i->seconds << " seconds"; + tools::msg_writer() << i->host << " banned for " << i->seconds << " seconds"; } return true; } -bool t_rpc_command_executor::ban(const std::string &ip, time_t seconds) +bool t_rpc_command_executor::ban(const std::string &address, time_t seconds) { cryptonote::COMMAND_RPC_SETBANS::request req; cryptonote::COMMAND_RPC_SETBANS::response res; @@ -1655,11 +1603,8 @@ bool t_rpc_command_executor::ban(const std::string &ip, time_t seconds) epee::json_rpc::error error_resp; cryptonote::COMMAND_RPC_SETBANS::ban ban; - if (!epee::string_tools::get_ip_int32_from_string(ban.ip, ip)) - { - tools::fail_msg_writer() << "Invalid IP"; - return true; - } + ban.host = address; + ban.ip = 0; ban.ban = true; ban.seconds = seconds; req.bans.push_back(ban); @@ -1683,7 +1628,7 @@ bool t_rpc_command_executor::ban(const std::string &ip, time_t seconds) return true; } -bool t_rpc_command_executor::unban(const std::string &ip) +bool t_rpc_command_executor::unban(const std::string &address) { cryptonote::COMMAND_RPC_SETBANS::request req; cryptonote::COMMAND_RPC_SETBANS::response res; @@ -1691,11 +1636,8 @@ bool t_rpc_command_executor::unban(const std::string &ip) epee::json_rpc::error error_resp; cryptonote::COMMAND_RPC_SETBANS::ban ban; - if (!epee::string_tools::get_ip_int32_from_string(ban.ip, ip)) - { - tools::fail_msg_writer() << "Invalid IP"; - return true; - } + ban.host = address; + ban.ip = 0; ban.ban = false; ban.seconds = 0; req.bans.push_back(ban); @@ -1719,6 +1661,39 @@ bool t_rpc_command_executor::unban(const std::string &ip) return true; } +bool t_rpc_command_executor::banned(const std::string &address) +{ + cryptonote::COMMAND_RPC_BANNED::request req; + cryptonote::COMMAND_RPC_BANNED::response res; + std::string fail_message = "Unsuccessful"; + epee::json_rpc::error error_resp; + + req.address = address; + + if (m_is_rpc) + { + if (!m_rpc_client->json_rpc_request(req, res, "banned", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_banned(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << make_error(fail_message, res.status); + return true; + } + } + + if (res.banned) + tools::msg_writer() << address << " is banned for " << res.seconds << " seconds"; + else + tools::msg_writer() << address << " is not banned"; + + return true; +} + bool t_rpc_command_executor::flush_txpool(const std::string &txid) { cryptonote::COMMAND_RPC_FLUSH_TRANSACTION_POOL::request req; diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index 3c2686b3f..b85686216 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -43,6 +43,7 @@ #include "common/common_fwd.h" #include "common/rpc_client.h" #include "cryptonote_basic/cryptonote_basic.h" +#include "net/net_fwd.h" #include "rpc/core_rpc_server.h" #undef MONERO_DEFAULT_LOG_CATEGORY @@ -61,6 +62,7 @@ public: uint32_t ip , uint16_t port , const boost::optional<tools::login>& user + , const epee::net_utils::ssl_options_t& ssl_options , bool is_rpc = true , cryptonote::core_rpc_server* rpc_server = NULL ); @@ -127,17 +129,15 @@ public: bool in_peers(uint64_t limit); - bool start_save_graph(); - - bool stop_save_graph(); - bool hard_fork_info(uint8_t version); bool print_bans(); - bool ban(const std::string &ip, time_t seconds); + bool ban(const std::string &address, time_t seconds); - bool unban(const std::string &ip); + bool unban(const std::string &address); + + bool banned(const std::string &address); bool flush_txpool(const std::string &txid); diff --git a/src/debug_utilities/CMakeLists.txt b/src/debug_utilities/CMakeLists.txt index 7bc2c324f..03c2b3e20 100644 --- a/src/debug_utilities/CMakeLists.txt +++ b/src/debug_utilities/CMakeLists.txt @@ -69,3 +69,25 @@ set_property(TARGET object_sizes PROPERTY OUTPUT_NAME "monero-utils-object-sizes") + +set(dns_checks_sources + dns_checks.cpp + ) + +monero_add_executable(dns_checks + ${dns_checks_sources} + ${dns_checks_private_headers}) + +target_link_libraries(dns_checks + LINK_PRIVATE + common + epee + version + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_SYSTEM_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT}) + +set_property(TARGET dns_checks + PROPERTY + OUTPUT_NAME "monero-utils-dns-checks") + diff --git a/src/debug_utilities/dns_checks.cpp b/src/debug_utilities/dns_checks.cpp new file mode 100644 index 000000000..3c9daa769 --- /dev/null +++ b/src/debug_utilities/dns_checks.cpp @@ -0,0 +1,149 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <string> +#include <vector> +#include <map> +#include <algorithm> +#include <boost/program_options.hpp> +#include "misc_log_ex.h" +#include "common/util.h" +#include "common/command_line.h" +#include "common/dns_utils.h" +#include "version.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "debugtools.dnschecks" + +namespace po = boost::program_options; + +enum lookup_t { LOOKUP_A, LOOKUP_TXT }; + +static std::vector<std::string> lookup(lookup_t type, const char *hostname) +{ + bool dnssec_available = false, dnssec_valid = false; + std::vector<std::string> res; + switch (type) + { + case LOOKUP_A: res = tools::DNSResolver::instance().get_ipv4(hostname, dnssec_available, dnssec_valid); break; + case LOOKUP_TXT: res = tools::DNSResolver::instance().get_txt_record(hostname, dnssec_available, dnssec_valid); break; + default: MERROR("Invalid lookup type: " << (int)type); return {}; + } + if (!dnssec_available) + { + MWARNING("No DNSSEC for " << hostname); + return {}; + } + if (!dnssec_valid) + { + MWARNING("Invalid DNSSEC check for " << hostname); + return {}; + } + MINFO(res.size() << " valid signed result(s) for " << hostname); + return res; +} + +static void lookup(lookup_t type, const std::vector<std::string> hostnames) +{ + std::vector<std::vector<std::string>> results; + for (const std::string &hostname: hostnames) + { + auto res = lookup(type, hostname.c_str()); + if (!res.empty()) + { + std::sort(res.begin(), res.end()); + results.push_back(res); + } + } + std::map<std::vector<std::string>, size_t> counter; + for (const auto &e: results) + counter[e]++; + size_t count = 0; + for (const auto &e: counter) + count = std::max(count, e.second); + if (results.size() > 1) + { + if (count < results.size()) + MERROR("Only " << count << "/" << results.size() << " records match"); + else + MINFO(count << "/" << results.size() << " records match"); + } +} + +int main(int argc, char* argv[]) +{ + TRY_ENTRY(); + + tools::on_startup(); + + po::options_description desc_cmd_only("Command line options"); + po::options_description desc_cmd_sett("Command line options and settings options"); + + command_line::add_arg(desc_cmd_only, command_line::arg_help); + + po::options_description desc_options("Allowed options"); + desc_options.add(desc_cmd_only).add(desc_cmd_sett); + + po::variables_map vm; + bool r = command_line::handle_error_helper(desc_options, [&]() + { + po::store(po::parse_command_line(argc, argv, desc_options), vm); + po::notify(vm); + return true; + }); + if (! r) + return 1; + + if (command_line::get_arg(vm, command_line::arg_help)) + { + std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; + std::cout << desc_options << std::endl; + return 1; + } + + mlog_configure("", true); + mlog_set_categories("+" MONERO_DEFAULT_LOG_CATEGORY ":INFO"); + + lookup(LOOKUP_A, {"seeds.moneroseeds.se", "seeds.moneroseeds.ae.org", "seeds.moneroseeds.ch", "seeds.moneroseeds.li"}); + + lookup(LOOKUP_TXT, {"updates.moneropulse.org", "updates.moneropulse.net", "updates.moneropulse.co", "updates.moneropulse.se"}); + + lookup(LOOKUP_TXT, {"checkpoints.moneropulse.org", "checkpoints.moneropulse.net", "checkpoints.moneropulse.co", "checkpoints.moneropulse.se"}); + + // those are in the code, but don't seem to actually exist +#if 0 + lookup(LOOKUP_TXT, {"testpoints.moneropulse.org", "testpoints.moneropulse.net", "testpoints.moneropulse.co", "testpoints.moneropulse.se"); + + lookup(LOOKUP_TXT, {"stagenetpoints.moneropulse.org", "stagenetpoints.moneropulse.net", "stagenetpoints.moneropulse.co", "stagenetpoints.moneropulse.se"}); +#endif + + lookup(LOOKUP_TXT, {"segheights.moneropulse.org", "segheights.moneropulse.net", "segheights.moneropulse.co", "segheights.moneropulse.se"}); + + return 0; + CATCH_ENTRY_L0("main", 1); +} diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 200370564..eba633da8 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -90,6 +90,20 @@ namespace hw { AKout = keys.AKout; } + ABPkeys &ABPkeys::operator=(const ABPkeys& keys) { + if (&keys == this) + return *this; + Aout = keys.Aout; + Bout = keys.Bout; + is_subaddress = keys.is_subaddress; + is_change_address = keys.is_change_address; + additional_key = keys.additional_key; + index = keys.index; + Pout = keys.Pout; + AKout = keys.AKout; + return *this; + } + bool Keymap::find(const rct::key& P, ABPkeys& keys) const { size_t sz = ABP.size(); for (size_t i=0; i<sz; i++) { diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp index d4d98ce4a..fe9028733 100644 --- a/src/device/device_ledger.hpp +++ b/src/device/device_ledger.hpp @@ -77,6 +77,7 @@ namespace hw { ABPkeys(const rct::key& A, const rct::key& B, const bool is_subaddr, bool is_subaddress, bool is_change_address, size_t index, const rct::key& P,const rct::key& AK); ABPkeys(const ABPkeys& keys) ; ABPkeys() {index=0;is_subaddress=false;is_subaddress=false;is_change_address=false;} + ABPkeys &operator=(const ABPkeys &keys); }; class Keymap { diff --git a/src/net/error.h b/src/net/error.h index c8338f7e2..7c852dd20 100644 --- a/src/net/error.h +++ b/src/net/error.h @@ -42,7 +42,8 @@ namespace net invalid_i2p_address, invalid_port, //!< Outside of 0-65535 range invalid_tor_address,//!< Invalid base32 or length - unsupported_address //!< Type not supported by `get_network_address` + unsupported_address,//!< Type not supported by `get_network_address` + invalid_mask, //!< Outside of 0-32 range }; //! \return `std::error_category` for `net` namespace. diff --git a/src/net/parse.cpp b/src/net/parse.cpp index eaaadb67e..d93d7d352 100644 --- a/src/net/parse.cpp +++ b/src/net/parse.cpp @@ -58,4 +58,27 @@ namespace net return {epee::net_utils::ipv4_network_address{ip, port}}; return make_error_code(net::error::unsupported_address); } + + expect<epee::net_utils::ipv4_network_subnet> + get_ipv4_subnet_address(const boost::string_ref address, bool allow_implicit_32) + { + uint32_t mask = 32; + const boost::string_ref::size_type slash = address.find_first_of('/'); + if (slash != boost::string_ref::npos) + { + if (!epee::string_tools::get_xtype_from_string(mask, std::string{address.substr(slash + 1)})) + return make_error_code(net::error::invalid_mask); + if (mask > 32) + return make_error_code(net::error::invalid_mask); + } + else if (!allow_implicit_32) + return make_error_code(net::error::invalid_mask); + + std::uint32_t ip = 0; + boost::string_ref S(address.data(), slash != boost::string_ref::npos ? slash : address.size()); + if (!epee::string_tools::get_ip_int32_from_string(ip, std::string(S))) + return make_error_code(net::error::invalid_host); + + return {epee::net_utils::ipv4_network_subnet{ip, (uint8_t)mask}}; + } } diff --git a/src/net/parse.h b/src/net/parse.h index 5804c4128..9f0d66ea6 100644 --- a/src/net/parse.h +++ b/src/net/parse.h @@ -50,5 +50,18 @@ namespace net */ expect<epee::net_utils::network_address> get_network_address(boost::string_ref address, std::uint16_t default_port); + + /*! + Identifies an IPv4 subnet in CIDR notatioa and returns it as a generic + `network_address`. If the type is unsupported, it might be a hostname, + and `error() == net::error::kUnsupportedAddress` is returned. + + \param address An ipv4 address. + \param allow_implicit_32 whether to accept "raw" IPv4 addresses, with CIDR notation + + \return A tor or IPv4 address, else error. + */ + expect<epee::net_utils::ipv4_network_subnet> + get_ipv4_subnet_address(boost::string_ref address, bool allow_implicit_32 = false); } diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp index fcbcce58c..b55003d75 100644 --- a/src/p2p/net_node.cpp +++ b/src/p2p/net_node.cpp @@ -143,8 +143,6 @@ namespace nodetool const command_line::arg_descriptor<int64_t> arg_limit_rate_down = {"limit-rate-down", "set limit-rate-down [kB/s]", P2P_DEFAULT_LIMIT_RATE_DOWN}; const command_line::arg_descriptor<int64_t> arg_limit_rate = {"limit-rate", "set limit-rate [kB/s]", -1}; - const command_line::arg_descriptor<bool> arg_save_graph = {"save-graph", "Save data for dr monero", false}; - boost::optional<std::vector<proxy>> get_proxies(boost::program_options::variables_map const& vm) { namespace ip = boost::asio::ip; diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 42bb3b061..50f8fe744 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -216,7 +216,6 @@ namespace nodetool m_hide_my_port(false), m_no_igd(false), m_offline(false), - m_save_graph(false), is_closing(false), m_network_id() {} @@ -248,7 +247,11 @@ namespace nodetool void change_max_in_public_peers(size_t count); virtual bool block_host(const epee::net_utils::network_address &adress, time_t seconds = P2P_IP_BLOCKTIME); virtual bool unblock_host(const epee::net_utils::network_address &address); - virtual std::map<std::string, time_t> get_blocked_hosts() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_hosts; } + virtual bool block_subnet(const epee::net_utils::ipv4_network_subnet &subnet, time_t seconds = P2P_IP_BLOCKTIME); + virtual bool unblock_subnet(const epee::net_utils::ipv4_network_subnet &subnet); + virtual bool is_host_blocked(const epee::net_utils::network_address &address, time_t *seconds) { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return !is_remote_host_allowed(address, seconds); } + virtual std::map<epee::net_utils::network_address, time_t> get_blocked_hosts() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_hosts; } + virtual std::map<epee::net_utils::ipv4_network_subnet, time_t> get_blocked_subnets() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_subnets; } virtual void add_used_stripe_peer(const typename t_payload_net_handler::connection_context &context); virtual void remove_used_stripe_peer(const typename t_payload_net_handler::connection_context &context); @@ -319,7 +322,7 @@ namespace nodetool virtual bool for_connection(const boost::uuids::uuid&, std::function<bool(typename t_payload_net_handler::connection_context&, peerid_type, uint32_t)> f); virtual bool add_host_fail(const epee::net_utils::network_address &address); //----------------- i_connection_filter -------------------------------------------------------- - virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address); + virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address, time_t *t = NULL); //----------------------------------------------------------------------------------------------- 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( @@ -396,12 +399,6 @@ namespace nodetool public: - void set_save_graph(bool save_graph) - { - m_save_graph = save_graph; - epee::net_utils::connection_basic::set_save_graph(save_graph); - } - void set_rpc_port(uint16_t rpc_port) { m_rpc_port = rpc_port; @@ -419,7 +416,6 @@ namespace nodetool bool m_hide_my_port; bool m_no_igd; bool m_offline; - std::atomic<bool> m_save_graph; std::atomic<bool> is_closing; std::unique_ptr<boost::thread> mPeersLoggerThread; //critical_section m_connections_lock; @@ -461,8 +457,9 @@ namespace nodetool std::map<epee::net_utils::network_address, time_t> m_conn_fails_cache; epee::critical_section m_conn_fails_cache_lock; - epee::critical_section m_blocked_hosts_lock; - std::map<std::string, time_t> m_blocked_hosts; + epee::critical_section m_blocked_hosts_lock; // for both hosts and subnets + std::map<epee::net_utils::network_address, time_t> m_blocked_hosts; + std::map<epee::net_utils::ipv4_network_subnet, time_t> m_blocked_subnets; epee::critical_section m_host_fails_score_lock; std::map<std::string, uint64_t> m_host_fails_score; @@ -500,8 +497,6 @@ namespace nodetool extern const command_line::arg_descriptor<int64_t> arg_limit_rate_up; extern const command_line::arg_descriptor<int64_t> arg_limit_rate_down; extern const command_line::arg_descriptor<int64_t> arg_limit_rate; - - extern const command_line::arg_descriptor<bool> arg_save_graph; } POP_WARNINGS diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index ba29d92c9..b39f133da 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -111,7 +111,6 @@ namespace nodetool command_line::add_arg(desc, arg_limit_rate_up); command_line::add_arg(desc, arg_limit_rate_down); command_line::add_arg(desc, arg_limit_rate); - command_line::add_arg(desc, arg_save_graph); } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> @@ -155,19 +154,55 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::is_remote_host_allowed(const epee::net_utils::network_address &address) + bool node_server<t_payload_net_handler>::is_remote_host_allowed(const epee::net_utils::network_address &address, time_t *t) { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); - auto it = m_blocked_hosts.find(address.host_str()); - if(it == m_blocked_hosts.end()) - return true; - if(time(nullptr) >= it->second) + + const time_t now = time(nullptr); + + // look in the hosts list + auto it = m_blocked_hosts.find(address); + if (it != m_blocked_hosts.end()) { - m_blocked_hosts.erase(it); - MCLOG_CYAN(el::Level::Info, "global", "Host " << address.host_str() << " unblocked."); - return true; + if (now >= it->second) + { + m_blocked_hosts.erase(it); + MCLOG_CYAN(el::Level::Info, "global", "Host " << address.host_str() << " unblocked."); + it = m_blocked_hosts.end(); + } + else + { + if (t) + *t = it->second - now; + return false; + } } - return false; + + // manually loop in subnets + if (address.get_type_id() == epee::net_utils::address_type::ipv4) + { + auto ipv4_address = address.template as<epee::net_utils::ipv4_network_address>(); + std::map<epee::net_utils::ipv4_network_subnet, time_t>::iterator it; + for (it = m_blocked_subnets.begin(); it != m_blocked_subnets.end(); ) + { + if (now >= it->second) + { + it = m_blocked_subnets.erase(it); + MCLOG_CYAN(el::Level::Info, "global", "Subnet " << it->first.host_str() << " unblocked."); + continue; + } + if (it->first.matches(ipv4_address)) + { + if (t) + *t = it->second - now; + return false; + } + ++it; + } + } + + // not found in hosts or subnets, allowed + return true; } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> @@ -184,7 +219,7 @@ namespace nodetool limit = std::numeric_limits<time_t>::max(); else limit = now + seconds; - m_blocked_hosts[addr.host_str()] = limit; + m_blocked_hosts[addr] = limit; // drop any connection to that address. This should only have to look into // the zone related to the connection, but really make sure everything is @@ -214,7 +249,7 @@ namespace nodetool bool node_server<t_payload_net_handler>::unblock_host(const epee::net_utils::network_address &address) { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); - auto i = m_blocked_hosts.find(address.host_str()); + auto i = m_blocked_hosts.find(address); if (i == m_blocked_hosts.end()) return false; m_blocked_hosts.erase(i); @@ -223,6 +258,58 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::block_subnet(const epee::net_utils::ipv4_network_subnet &subnet, time_t seconds) + { + const time_t now = time(nullptr); + + CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); + time_t limit; + if (now > std::numeric_limits<time_t>::max() - seconds) + limit = std::numeric_limits<time_t>::max(); + else + limit = now + seconds; + m_blocked_subnets[subnet] = limit; + + // drop any connection to that subnet. This should only have to look into + // the zone related to the connection, but really make sure everything is + // swept ... + std::vector<boost::uuids::uuid> conns; + for(auto& zone : m_network_zones) + { + zone.second.m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + if (cntxt.m_remote_address.get_type_id() != epee::net_utils::ipv4_network_address::get_type_id()) + return true; + auto ipv4_address = cntxt.m_remote_address.template as<epee::net_utils::ipv4_network_address>(); + if (subnet.matches(ipv4_address)) + { + conns.push_back(cntxt.m_connection_id); + } + return true; + }); + for (const auto &c: conns) + zone.second.m_net_server.get_config_object().close(c); + + conns.clear(); + } + + MCLOG_CYAN(el::Level::Info, "global", "Subnet " << subnet.host_str() << " blocked."); + return true; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::unblock_subnet(const epee::net_utils::ipv4_network_subnet &subnet) + { + CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); + auto i = m_blocked_subnets.find(subnet); + if (i == m_blocked_subnets.end()) + return false; + m_blocked_subnets.erase(i); + MCLOG_CYAN(el::Level::Info, "global", "Subnet " << subnet.host_str() << " unblocked."); + return true; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::add_host_fail(const epee::net_utils::network_address &address) { if(!address.is_blockable()) @@ -292,11 +379,6 @@ namespace nodetool } } - if(command_line::has_arg(vm, arg_save_graph)) - { - set_save_graph(true); - } - if (command_line::has_arg(vm,arg_p2p_add_exclusive_node)) { if (!parse_peers_and_add_to_container(vm, arg_p2p_add_exclusive_node, m_exclusive_peers)) @@ -944,7 +1026,10 @@ namespace nodetool } if(!context.m_is_income) m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_address, context.m_pruning_seed, context.m_rpc_port); - m_payload_handler.process_payload_sync_data(rsp.payload_data, context, false); + if (!m_payload_handler.process_payload_sync_data(rsp.payload_data, context, false)) + { + m_network_zones.at(context.m_remote_address.get_zone()).m_net_server.get_config_object().close(context.m_connection_id ); + } }); if(!r) @@ -1090,6 +1175,7 @@ namespace nodetool LOG_PRINT_CC_PRIORITY_NODE(is_priority, *con, "Failed to HANDSHAKE with peer " << na.str() /*<< ", try " << try_count*/); + zone.m_net_server.get_config_object().close(con->m_connection_id); return false; } @@ -1149,7 +1235,7 @@ namespace nodetool bool is_priority = is_priority_node(na); LOG_PRINT_CC_PRIORITY_NODE(is_priority, *con, "Failed to HANDSHAKE with peer " << na.str()); - + zone.m_net_server.get_config_object().close(con->m_connection_id); return false; } @@ -1226,19 +1312,53 @@ namespace nodetool size_t random_index; const uint32_t next_needed_pruning_stripe = m_payload_handler.get_next_needed_pruning_stripe().second; + // build a set of all the /16 we're connected to, and prefer a peer that's not in that set + std::set<uint32_t> classB; + if (&zone == &m_network_zones.at(epee::net_utils::zone::public_)) // at returns reference, not copy + { + zone.m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + if (cntxt.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id()) + { + + const epee::net_utils::network_address na = cntxt.m_remote_address; + const uint32_t actual_ip = na.as<const epee::net_utils::ipv4_network_address>().ip(); + classB.insert(actual_ip & 0x0000ffff); + } + return true; + }); + } + std::deque<size_t> filtered; const size_t limit = use_white_list ? 20 : std::numeric_limits<size_t>::max(); - size_t idx = 0; - zone.m_peerlist.foreach (use_white_list, [&filtered, &idx, limit, next_needed_pruning_stripe](const peerlist_entry &pe){ - if (filtered.size() >= limit) - return false; - if (next_needed_pruning_stripe == 0 || pe.pruning_seed == 0) - filtered.push_back(idx); - else if (next_needed_pruning_stripe == tools::get_pruning_stripe(pe.pruning_seed)) - filtered.push_front(idx); - ++idx; - return true; - }); + size_t idx = 0, skipped = 0; + for (int step = 0; step < 2; ++step) + { + bool skip_duplicate_class_B = step == 0; + zone.m_peerlist.foreach (use_white_list, [&classB, &filtered, &idx, &skipped, skip_duplicate_class_B, limit, next_needed_pruning_stripe](const peerlist_entry &pe){ + if (filtered.size() >= limit) + return false; + bool skip = false; + if (skip_duplicate_class_B && pe.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id()) + { + const epee::net_utils::network_address na = pe.adr; + uint32_t actual_ip = na.as<const epee::net_utils::ipv4_network_address>().ip(); + skip = classB.find(actual_ip & 0x0000ffff) != classB.end(); + } + if (skip) + ++skipped; + else if (next_needed_pruning_stripe == 0 || pe.pruning_seed == 0) + filtered.push_back(idx); + else if (next_needed_pruning_stripe == tools::get_pruning_stripe(pe.pruning_seed)) + filtered.push_front(idx); + ++idx; + return true; + }); + if (skipped == 0 || !filtered.empty()) + break; + if (skipped) + MGINFO("Skipping " << skipped << " possible peers as they share a class B with existing peers"); + } if (filtered.empty()) { MDEBUG("No available peer in " << (use_white_list ? "white" : "gray") << " list filtered by " << next_needed_pruning_stripe); diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index 26451b333..34d151f5f 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -56,7 +56,8 @@ namespace nodetool virtual bool for_connection(const boost::uuids::uuid&, std::function<bool(t_connection_context&, peerid_type, uint32_t)> f)=0; virtual bool block_host(const epee::net_utils::network_address &address, time_t seconds = 0)=0; virtual bool unblock_host(const epee::net_utils::network_address &address)=0; - virtual std::map<std::string, time_t> get_blocked_hosts()=0; + virtual std::map<epee::net_utils::network_address, time_t> get_blocked_hosts()=0; + virtual std::map<epee::net_utils::ipv4_network_subnet, time_t> get_blocked_subnets()=0; virtual bool add_host_fail(const epee::net_utils::network_address &address)=0; virtual void add_used_stripe_peer(const t_connection_context &context)=0; virtual void remove_used_stripe_peer(const t_connection_context &context)=0; @@ -112,9 +113,13 @@ namespace nodetool { return true; } - virtual std::map<std::string, time_t> get_blocked_hosts() + virtual std::map<epee::net_utils::network_address, time_t> get_blocked_hosts() { - return std::map<std::string, time_t>(); + return std::map<epee::net_utils::network_address, time_t>(); + } + virtual std::map<epee::net_utils::ipv4_network_subnet, time_t> get_blocked_subnets() + { + return std::map<epee::net_utils::ipv4_network_subnet, time_t>(); } virtual bool add_host_fail(const epee::net_utils::network_address &address) { diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index f4fa921e2..883997fd6 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -344,8 +344,14 @@ namespace nodetool trim_white_peerlist(); }else { - //update record in white list - m_peers_white.replace(by_addr_it_wt, ple); + //update record in white list + peerlist_entry new_ple = ple; + if (by_addr_it_wt->pruning_seed && ple.pruning_seed == 0) // guard against older nodes not passing pruning info around + new_ple.pruning_seed = by_addr_it_wt->pruning_seed; + if (by_addr_it_wt->rpc_port && ple.rpc_port == 0) // guard against older nodes not passing RPC port around + new_ple.rpc_port = by_addr_it_wt->rpc_port; + new_ple.last_seen = by_addr_it_wt->last_seen; // do not overwrite the last seen timestamp, incoming peer list are untrusted + m_peers_white.replace(by_addr_it_wt, new_ple); } //remove from gray list, if need auto by_addr_it_gr = m_peers_gray.get<by_addr>().find(ple.adr); @@ -379,8 +385,14 @@ namespace nodetool trim_gray_peerlist(); }else { - //update record in white list - m_peers_gray.replace(by_addr_it_gr, ple); + //update record in gray list + peerlist_entry new_ple = ple; + if (by_addr_it_gr->pruning_seed && ple.pruning_seed == 0) // guard against older nodes not passing pruning info around + new_ple.pruning_seed = by_addr_it_gr->pruning_seed; + if (by_addr_it_gr->rpc_port && ple.rpc_port == 0) // guard against older nodes not passing RPC port around + new_ple.rpc_port = by_addr_it_gr->rpc_port; + new_ple.last_seen = by_addr_it_gr->last_seen; // do not overwrite the last seen timestamp, incoming peer list are untrusted + m_peers_gray.replace(by_addr_it_gr, new_ple); } return true; CATCH_ENTRY_L0("peerlist_manager::append_with_peer_gray()", false); diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h index 40ef2ebcd..32f30adca 100644 --- a/src/p2p/net_peerlist_boost_serialization.h +++ b/src/p2p/net_peerlist_boost_serialization.h @@ -134,10 +134,11 @@ namespace boost a & port; a & length; - if (length > net::tor_address::buffer_size()) + const size_t buffer_size = net::tor_address::buffer_size(); + if (length > buffer_size) MONERO_THROW(net::error::invalid_tor_address, "Tor address too long"); - char host[net::tor_address::buffer_size()] = {0}; + char host[buffer_size] = {0}; a.load_binary(host, length); host[sizeof(host) - 1] = 0; @@ -155,10 +156,11 @@ namespace boost a & port; a & length; - if (length > net::i2p_address::buffer_size()) + const size_t buffer_size = net::i2p_address::buffer_size(); + if (length > buffer_size) MONERO_THROW(net::error::invalid_i2p_address, "i2p address too long"); - char host[net::i2p_address::buffer_size()] = {0}; + char host[buffer_size] = {0}; a.load_binary(host, length); host[sizeof(host) - 1] = 0; diff --git a/src/ringct/multiexp.cc b/src/ringct/multiexp.cc index 6f77fed34..f69b4a12c 100644 --- a/src/ringct/multiexp.cc +++ b/src/ringct/multiexp.cc @@ -447,7 +447,6 @@ rct::key straus(const std::vector<MultiexpData> &data, const std::shared_ptr<str { CHECK_AND_ASSERT_THROW_MES(cache == NULL || cache->size >= data.size(), "Cache is too small"); MULTIEXP_PERF(PERF_TIMER_UNIT(straus, 1000000)); - bool HiGi = cache != NULL; STEP = STEP ? STEP : 192; MULTIEXP_PERF(PERF_TIMER_START_UNIT(setup, 1000000)); diff --git a/src/ringct/rctTypes.cpp b/src/ringct/rctTypes.cpp index f01e683cb..2c4e5fc3b 100644 --- a/src/ringct/rctTypes.cpp +++ b/src/ringct/rctTypes.cpp @@ -190,7 +190,6 @@ namespace rct { int byte, i, j; for (j = 0; j < 8; j++) { byte = 0; - i = 8 * j; for (i = 7; i > -1; i--) { byte = byte * 2 + amountb2[8 * j + i]; } diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index e5413f1dc..f8729b872 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -252,7 +252,7 @@ namespace rct { { FIELD(type) if (type == RCTTypeNull) - return true; + return ar.stream().good(); if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2) return false; VARINT_FIELD(txnFee) @@ -312,7 +312,7 @@ namespace rct { ar.delimit_array(); } ar.end_array(); - return true; + return ar.stream().good(); } }; struct rctSigPrunable { @@ -325,7 +325,7 @@ namespace rct { bool serialize_rctsig_prunable(Archive<W> &ar, uint8_t type, size_t inputs, size_t outputs, size_t mixin) { if (type == RCTTypeNull) - return true; + return ar.stream().good(); if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2) return false; if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2) @@ -429,7 +429,7 @@ namespace rct { } ar.end_array(); } - return true; + return ar.stream().good(); } }; diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 5777370fc..6c6b30daa 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -59,6 +59,8 @@ using namespace epee; #define MAX_RESTRICTED_FAKE_OUTS_COUNT 40 #define MAX_RESTRICTED_GLOBAL_FAKE_OUTS_COUNT 5000 +#define OUTPUT_HISTOGRAM_RECENT_CUTOFF_RESTRICTION (3 * 86400) // 3 days max, the wallet requests 1.8 days + namespace { void add_reason(std::string &reasons, const char *reason) @@ -90,15 +92,9 @@ namespace cryptonote command_line::add_arg(desc, arg_rpc_bind_port); command_line::add_arg(desc, arg_rpc_restricted_bind_port); command_line::add_arg(desc, arg_restricted_rpc); - command_line::add_arg(desc, arg_rpc_ssl); - command_line::add_arg(desc, arg_rpc_ssl_private_key); - command_line::add_arg(desc, arg_rpc_ssl_certificate); - command_line::add_arg(desc, arg_rpc_ssl_ca_certificates); - command_line::add_arg(desc, arg_rpc_ssl_allowed_fingerprints); - command_line::add_arg(desc, arg_rpc_ssl_allow_any_cert); command_line::add_arg(desc, arg_bootstrap_daemon_address); command_line::add_arg(desc, arg_bootstrap_daemon_login); - cryptonote::rpc_args::init_options(desc); + cryptonote::rpc_args::init_options(desc, true); } //------------------------------------------------------------------------------------------------------------------------------ core_rpc_server::core_rpc_server( @@ -118,7 +114,7 @@ namespace cryptonote m_restricted = restricted; m_net_server.set_threads_prefix("RPC"); - auto rpc_config = cryptonote::rpc_args::process(vm); + auto rpc_config = cryptonote::rpc_args::process(vm, true); if (!rpc_config) return false; @@ -151,46 +147,9 @@ namespace cryptonote if (rpc_config->login) http_login.emplace(std::move(rpc_config->login->username), std::move(rpc_config->login->password).password()); - epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect; - if (command_line::get_arg(vm, arg_rpc_ssl_allow_any_cert)) - ssl_options.verification = epee::net_utils::ssl_verification_t::none; - else - { - std::string ssl_ca_path = command_line::get_arg(vm, arg_rpc_ssl_ca_certificates); - const std::vector<std::string> ssl_allowed_fingerprint_strings = command_line::get_arg(vm, arg_rpc_ssl_allowed_fingerprints); - std::vector<std::vector<uint8_t>> ssl_allowed_fingerprints{ ssl_allowed_fingerprint_strings.size() }; - std::transform(ssl_allowed_fingerprint_strings.begin(), ssl_allowed_fingerprint_strings.end(), ssl_allowed_fingerprints.begin(), epee::from_hex::vector); - for (const auto &fpr: ssl_allowed_fingerprints) - { - if (fpr.size() != SSL_FINGERPRINT_SIZE) - { - MERROR("SHA-256 fingerprint should be " BOOST_PP_STRINGIZE(SSL_FINGERPRINT_SIZE) " bytes long."); - return false; - } - } - - if (!ssl_ca_path.empty() || !ssl_allowed_fingerprints.empty()) - ssl_options = epee::net_utils::ssl_options_t{std::move(ssl_allowed_fingerprints), std::move(ssl_ca_path)}; - } - - ssl_options.auth = epee::net_utils::ssl_authentication_t{ - command_line::get_arg(vm, arg_rpc_ssl_private_key), command_line::get_arg(vm, arg_rpc_ssl_certificate) - }; - - // user specified CA file or fingeprints implies enabled SSL by default - if (ssl_options.verification != epee::net_utils::ssl_verification_t::user_certificates || !command_line::is_arg_defaulted(vm, arg_rpc_ssl)) - { - const std::string ssl = command_line::get_arg(vm, arg_rpc_ssl); - if (!epee::net_utils::ssl_support_from_string(ssl_options.support, ssl)) - { - MFATAL("Invalid RPC SSL support: " << ssl); - return false; - } - } - auto rng = [](size_t len, uint8_t *ptr){ return crypto::rand(len, ptr); }; return epee::http_server_impl_base<core_rpc_server, connection_context>::init( - rng, std::move(port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login), std::move(ssl_options) + rng, std::move(port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login), std::move(rpc_config->ssl_options) ); } //------------------------------------------------------------------------------------------------------------------------------ @@ -861,6 +820,7 @@ namespace cryptonote res.sanity_check_failed = true; return true; } + res.sanity_check_failed = false; cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); tx_verification_context tvc = AUTO_VAL_INIT(tvc); @@ -948,16 +908,13 @@ namespace cryptonote return true; } - boost::thread::attributes attrs; - attrs.set_stack_size(THREAD_STACK_SIZE); - cryptonote::miner &miner= m_core.get_miner(); if (miner.is_mining()) { res.status = "Already mining"; return true; } - if(!miner.start(info.address, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery)) + if(!miner.start(info.address, static_cast<size_t>(req.threads_count), req.do_background_mining, req.ignore_battery)) { res.status = "Failed, mining not started"; LOG_PRINT_L0(res.status); @@ -1519,7 +1476,7 @@ namespace cryptonote ok = ok && getheight_res.status == CORE_RPC_STATUS_OK; m_should_use_bootstrap_daemon = ok && top_height + 10 < getheight_res.height; - MINFO((m_should_use_bootstrap_daemon ? "Using" : "Not using") << " the bootstrap daemon (our height: " << top_height << ", bootstrap daemon's height: " << getheight_res.height << ")"); + MINFO((m_should_use_bootstrap_daemon ? "Using" : "Not using") << " the bootstrap daemon (our height: " << top_height << ", bootstrap daemon's height: " << (ok ? getheight_res.height : 0) << ")"); } if (!m_should_use_bootstrap_daemon) return false; @@ -1815,20 +1772,60 @@ namespace cryptonote PERF_TIMER(on_get_bans); auto now = time(nullptr); - std::map<std::string, time_t> blocked_hosts = m_p2p.get_blocked_hosts(); - for (std::map<std::string, time_t>::const_iterator i = blocked_hosts.begin(); i != blocked_hosts.end(); ++i) + std::map<epee::net_utils::network_address, time_t> blocked_hosts = m_p2p.get_blocked_hosts(); + for (std::map<epee::net_utils::network_address, time_t>::const_iterator i = blocked_hosts.begin(); i != blocked_hosts.end(); ++i) { if (i->second > now) { COMMAND_RPC_GETBANS::ban b; - b.host = i->first; + b.host = i->first.host_str(); b.ip = 0; uint32_t ip; - if (epee::string_tools::get_ip_int32_from_string(ip, i->first)) + if (epee::string_tools::get_ip_int32_from_string(ip, b.host)) b.ip = ip; b.seconds = i->second - now; res.bans.push_back(b); } } + std::map<epee::net_utils::ipv4_network_subnet, time_t> blocked_subnets = m_p2p.get_blocked_subnets(); + for (std::map<epee::net_utils::ipv4_network_subnet, time_t>::const_iterator i = blocked_subnets.begin(); i != blocked_subnets.end(); ++i) + { + if (i->second > now) { + COMMAND_RPC_GETBANS::ban b; + b.host = i->first.host_str(); + b.ip = 0; + b.seconds = i->second - now; + res.bans.push_back(b); + } + } + + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_banned(const COMMAND_RPC_BANNED::request& req, COMMAND_RPC_BANNED::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx) + { + PERF_TIMER(on_banned); + + auto na_parsed = net::get_network_address(req.address, 0); + if (!na_parsed) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; + error_resp.message = "Unsupported host type"; + return false; + } + epee::net_utils::network_address na = std::move(*na_parsed); + + time_t seconds; + if (m_p2p.is_host_blocked(na, &seconds)) + { + res.banned = true; + res.seconds = seconds; + } + else + { + res.banned = false; + res.seconds = 0; + } res.status = CORE_RPC_STATUS_OK; return true; @@ -1841,13 +1838,29 @@ namespace cryptonote for (auto i = req.bans.begin(); i != req.bans.end(); ++i) { epee::net_utils::network_address na; + + // try subnet first + if (!i->host.empty()) + { + auto ns_parsed = net::get_ipv4_subnet_address(i->host); + if (ns_parsed) + { + if (i->ban) + m_p2p.block_subnet(*ns_parsed, i->seconds); + else + m_p2p.unblock_subnet(*ns_parsed); + continue; + } + } + + // then host if (!i->host.empty()) { auto na_parsed = net::get_network_address(i->host, 0); if (!na_parsed) { error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; - error_resp.message = "Unsupported host type"; + error_resp.message = "Unsupported host/subnet type"; return false; } na = std::move(*na_parsed); @@ -1928,6 +1941,13 @@ namespace cryptonote if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_OUTPUT_HISTOGRAM>(invoke_http_mode::JON_RPC, "get_output_histogram", req, res, r)) return r; + const bool restricted = m_restricted && ctx; + if (restricted && req.recent_cutoff > 0 && req.recent_cutoff < (uint64_t)time(NULL) - OUTPUT_HISTOGRAM_RECENT_CUTOFF_RESTRICTION) + { + res.status = "Recent cutoff is too old"; + return true; + } + std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> histogram; try { @@ -2088,22 +2108,6 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res, const connection_context *ctx) - { - PERF_TIMER(on_start_save_graph); - m_p2p.set_save_graph(true); - res.status = CORE_RPC_STATUS_OK; - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res, const connection_context *ctx) - { - PERF_TIMER(on_stop_save_graph); - m_p2p.set_save_graph(false); - res.status = CORE_RPC_STATUS_OK; - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res, const connection_context *ctx) { PERF_TIMER(on_update); @@ -2439,40 +2443,6 @@ namespace cryptonote , false }; - const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_ssl = { - "rpc-ssl" - , "Enable SSL on RPC connections: enabled|disabled|autodetect" - , "autodetect" - }; - - const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_ssl_private_key = { - "rpc-ssl-private-key" - , "Path to a PEM format private key" - , "" - }; - - const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_ssl_certificate = { - "rpc-ssl-certificate" - , "Path to a PEM format certificate" - , "" - }; - - const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_ssl_ca_certificates = { - "rpc-ssl-ca-certificates" - , "Path to file containing concatenated PEM format certificate(s) to replace system CA(s)." - }; - - const command_line::arg_descriptor<std::vector<std::string>> core_rpc_server::arg_rpc_ssl_allowed_fingerprints = { - "rpc-ssl-allowed-fingerprints" - , "List of certificate fingerprints to allow" - }; - - const command_line::arg_descriptor<bool> core_rpc_server::arg_rpc_ssl_allow_any_cert = { - "rpc-ssl-allow-any-cert" - , "Allow any peer certificate" - , false - }; - const command_line::arg_descriptor<std::string> core_rpc_server::arg_bootstrap_daemon_address = { "bootstrap-daemon-address" , "URL of a 'bootstrap' remote daemon that the connected wallets can use while this daemon is still not fully synced" diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index e4683bbe2..7c9bb6dc4 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -123,8 +123,6 @@ namespace cryptonote MAP_URI_AUTO_JON2_IF("/set_limit", on_set_limit, COMMAND_RPC_SET_LIMIT, !m_restricted) MAP_URI_AUTO_JON2_IF("/out_peers", on_out_peers, COMMAND_RPC_OUT_PEERS, !m_restricted) MAP_URI_AUTO_JON2_IF("/in_peers", on_in_peers, COMMAND_RPC_IN_PEERS, !m_restricted) - MAP_URI_AUTO_JON2_IF("/start_save_graph", on_start_save_graph, COMMAND_RPC_START_SAVE_GRAPH, !m_restricted) - MAP_URI_AUTO_JON2_IF("/stop_save_graph", on_stop_save_graph, COMMAND_RPC_STOP_SAVE_GRAPH, !m_restricted) MAP_URI_AUTO_JON2("/get_outs", on_get_outs, COMMAND_RPC_GET_OUTPUTS) MAP_URI_AUTO_JON2_IF("/update", on_update, COMMAND_RPC_UPDATE, !m_restricted) MAP_URI_AUTO_BIN2("/get_output_distribution.bin", on_get_output_distribution_bin, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION) @@ -154,6 +152,7 @@ namespace cryptonote MAP_JON_RPC_WE("hard_fork_info", on_hard_fork_info, COMMAND_RPC_HARD_FORK_INFO) MAP_JON_RPC_WE_IF("set_bans", on_set_bans, COMMAND_RPC_SETBANS, !m_restricted) MAP_JON_RPC_WE_IF("get_bans", on_get_bans, COMMAND_RPC_GETBANS, !m_restricted) + MAP_JON_RPC_WE_IF("banned", on_banned, COMMAND_RPC_BANNED, !m_restricted) MAP_JON_RPC_WE_IF("flush_txpool", on_flush_txpool, COMMAND_RPC_FLUSH_TRANSACTION_POOL, !m_restricted) MAP_JON_RPC_WE("get_output_histogram", on_get_output_histogram, COMMAND_RPC_GET_OUTPUT_HISTOGRAM) MAP_JON_RPC_WE("get_version", on_get_version, COMMAND_RPC_GET_VERSION) @@ -198,8 +197,6 @@ namespace cryptonote bool on_set_limit(const COMMAND_RPC_SET_LIMIT::request& req, COMMAND_RPC_SET_LIMIT::response& res, const connection_context *ctx = NULL); bool on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res, const connection_context *ctx = NULL); bool on_in_peers(const COMMAND_RPC_IN_PEERS::request& req, COMMAND_RPC_IN_PEERS::response& res, const connection_context *ctx = NULL); - bool on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res, const connection_context *ctx = NULL); - bool on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res, const connection_context *ctx = NULL); bool on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res, const connection_context *ctx = NULL); bool on_get_output_distribution_bin(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res, const connection_context *ctx = NULL); bool on_pop_blocks(const COMMAND_RPC_POP_BLOCKS::request& req, COMMAND_RPC_POP_BLOCKS::response& res, const connection_context *ctx = NULL); @@ -220,6 +217,7 @@ namespace cryptonote bool on_hard_fork_info(const COMMAND_RPC_HARD_FORK_INFO::request& req, COMMAND_RPC_HARD_FORK_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); bool on_set_bans(const COMMAND_RPC_SETBANS::request& req, COMMAND_RPC_SETBANS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); bool on_get_bans(const COMMAND_RPC_GETBANS::request& req, COMMAND_RPC_GETBANS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); + bool on_banned(const COMMAND_RPC_BANNED::request& req, COMMAND_RPC_BANNED::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); bool on_flush_txpool(const COMMAND_RPC_FLUSH_TRANSACTION_POOL::request& req, COMMAND_RPC_FLUSH_TRANSACTION_POOL::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); bool on_get_output_histogram(const COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request& req, COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); bool on_get_version(const COMMAND_RPC_GET_VERSION::request& req, COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index cfe4bbf23..6e035fdde 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -84,7 +84,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 2 -#define CORE_RPC_VERSION_MINOR 6 +#define CORE_RPC_VERSION_MINOR 7 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -1722,46 +1722,6 @@ namespace cryptonote typedef epee::misc_utils::struct_init<response_t> response; }; - struct COMMAND_RPC_START_SAVE_GRAPH - { - struct request_t - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<request_t> request; - - struct response_t - { - std::string status; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<response_t> response; - }; - - struct COMMAND_RPC_STOP_SAVE_GRAPH - { - struct request_t - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<request_t> request; - - struct response_t - { - std::string status; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<response_t> response; - }; - struct COMMAND_RPC_HARD_FORK_INFO { struct request_t @@ -1876,6 +1836,33 @@ namespace cryptonote typedef epee::misc_utils::struct_init<response_t> response; }; + struct COMMAND_RPC_BANNED + { + struct request_t + { + std::string address; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(address) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct response_t + { + std::string status; + bool banned; + uint32_t seconds; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(banned) + KV_SERIALIZE(seconds) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + struct COMMAND_RPC_FLUSH_TRANSACTION_POOL { struct request_t diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 5c214581c..612b2cab6 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -408,10 +408,7 @@ namespace rpc return; } - boost::thread::attributes attrs; - attrs.set_stack_size(THREAD_STACK_SIZE); - - if(!m_core.get_miner().start(info.address, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery)) + if(!m_core.get_miner().start(info.address, static_cast<size_t>(req.threads_count), req.do_background_mining, req.ignore_battery)) { res.error_details = "Failed, mining not started"; LOG_PRINT_L0(res.error_details); diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp index f2be94f51..4479bd1f1 100644 --- a/src/rpc/rpc_args.cpp +++ b/src/rpc/rpc_args.cpp @@ -33,28 +33,95 @@ #include <boost/bind.hpp> #include "common/command_line.h" #include "common/i18n.h" +#include "hex.h" namespace cryptonote { + namespace + { + boost::optional<epee::net_utils::ssl_options_t> do_process_ssl(const boost::program_options::variables_map& vm, const rpc_args::descriptors& arg, const bool any_cert_option) + { + bool ssl_required = false; + epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_enabled; + if (any_cert_option && command_line::get_arg(vm, arg.rpc_ssl_allow_any_cert)) + ssl_options.verification = epee::net_utils::ssl_verification_t::none; + else + { + std::string ssl_ca_file = command_line::get_arg(vm, arg.rpc_ssl_ca_certificates); + const std::vector<std::string> ssl_allowed_fingerprints = command_line::get_arg(vm, arg.rpc_ssl_allowed_fingerprints); + + std::vector<std::vector<uint8_t>> allowed_fingerprints{ ssl_allowed_fingerprints.size() }; + std::transform(ssl_allowed_fingerprints.begin(), ssl_allowed_fingerprints.end(), allowed_fingerprints.begin(), epee::from_hex::vector); + for (const auto &fpr: allowed_fingerprints) + { + if (fpr.size() != SSL_FINGERPRINT_SIZE) + { + MERROR("SHA-256 fingerprint should be " BOOST_PP_STRINGIZE(SSL_FINGERPRINT_SIZE) " bytes long."); + return boost::none; + } + } + + if (!allowed_fingerprints.empty() || !ssl_ca_file.empty()) + { + ssl_required = true; + ssl_options = epee::net_utils::ssl_options_t{ + std::move(allowed_fingerprints), std::move(ssl_ca_file) + }; + + if (command_line::get_arg(vm, arg.rpc_ssl_allow_chained)) + ssl_options.verification = epee::net_utils::ssl_verification_t::user_ca; + } + } + + // user specified CA file or fingeprints implies enabled SSL by default + if (!ssl_required && !epee::net_utils::ssl_support_from_string(ssl_options.support, command_line::get_arg(vm, arg.rpc_ssl))) + { + MERROR("Invalid argument for " << std::string(arg.rpc_ssl.name)); + return boost::none; + } + + ssl_options.auth = epee::net_utils::ssl_authentication_t{ + command_line::get_arg(vm, arg.rpc_ssl_private_key), command_line::get_arg(vm, arg.rpc_ssl_certificate) + }; + + return {std::move(ssl_options)}; + } + } // anonymous + rpc_args::descriptors::descriptors() : rpc_bind_ip({"rpc-bind-ip", rpc_args::tr("Specify IP to bind RPC server"), "127.0.0.1"}) , rpc_login({"rpc-login", rpc_args::tr("Specify username[:password] required for RPC server"), "", true}) , confirm_external_bind({"confirm-external-bind", rpc_args::tr("Confirm rpc-bind-ip value is NOT a loopback (local) IP")}) , rpc_access_control_origins({"rpc-access-control-origins", rpc_args::tr("Specify a comma separated list of origins to allow cross origin resource sharing"), ""}) + , rpc_ssl({"rpc-ssl", rpc_args::tr("Enable SSL on RPC connections: enabled|disabled|autodetect"), "autodetect"}) + , rpc_ssl_private_key({"rpc-ssl-private-key", rpc_args::tr("Path to a PEM format private key"), ""}) + , rpc_ssl_certificate({"rpc-ssl-certificate", rpc_args::tr("Path to a PEM format certificate"), ""}) + , rpc_ssl_ca_certificates({"rpc-ssl-ca-certificates", rpc_args::tr("Path to file containing concatenated PEM format certificate(s) to replace system CA(s)."), ""}) + , rpc_ssl_allowed_fingerprints({"rpc-ssl-allowed-fingerprints", rpc_args::tr("List of certificate fingerprints to allow")}) + , rpc_ssl_allow_chained({"rpc-ssl-allow-chained", rpc_args::tr("Allow user (via --rpc-ssl-certificates) chain certificates"), false}) + , rpc_ssl_allow_any_cert({"rpc-ssl-allow-any-cert", rpc_args::tr("Allow any peer certificate"), false}) {} const char* rpc_args::tr(const char* str) { return i18n_translate(str, "cryptonote::rpc_args"); } - void rpc_args::init_options(boost::program_options::options_description& desc) + void rpc_args::init_options(boost::program_options::options_description& desc, const bool any_cert_option) { const descriptors arg{}; command_line::add_arg(desc, arg.rpc_bind_ip); command_line::add_arg(desc, arg.rpc_login); command_line::add_arg(desc, arg.confirm_external_bind); command_line::add_arg(desc, arg.rpc_access_control_origins); + command_line::add_arg(desc, arg.rpc_ssl); + command_line::add_arg(desc, arg.rpc_ssl_private_key); + command_line::add_arg(desc, arg.rpc_ssl_certificate); + command_line::add_arg(desc, arg.rpc_ssl_ca_certificates); + command_line::add_arg(desc, arg.rpc_ssl_allowed_fingerprints); + command_line::add_arg(desc, arg.rpc_ssl_allow_chained); + if (any_cert_option) + command_line::add_arg(desc, arg.rpc_ssl_allow_any_cert); } - boost::optional<rpc_args> rpc_args::process(const boost::program_options::variables_map& vm) + boost::optional<rpc_args> rpc_args::process(const boost::program_options::variables_map& vm, const bool any_cert_option) { const descriptors arg{}; rpc_args config{}; @@ -118,6 +185,17 @@ namespace cryptonote config.access_control_origins = std::move(access_control_origins); } + auto ssl_options = do_process_ssl(vm, arg, any_cert_option); + if (!ssl_options) + return boost::none; + config.ssl_options = std::move(*ssl_options); + return {std::move(config)}; } + + boost::optional<epee::net_utils::ssl_options_t> rpc_args::process_ssl(const boost::program_options::variables_map& vm, const bool any_cert_option) + { + const descriptors arg{}; + return do_process_ssl(vm, arg, any_cert_option); + } } diff --git a/src/rpc/rpc_args.h b/src/rpc/rpc_args.h index 216ba3712..619f02b42 100644 --- a/src/rpc/rpc_args.h +++ b/src/rpc/rpc_args.h @@ -35,6 +35,7 @@ #include "common/command_line.h" #include "common/password.h" +#include "net/net_ssl.h" namespace cryptonote { @@ -54,16 +55,29 @@ namespace cryptonote const command_line::arg_descriptor<std::string> rpc_login; const command_line::arg_descriptor<bool> confirm_external_bind; const command_line::arg_descriptor<std::string> rpc_access_control_origins; + const command_line::arg_descriptor<std::string> rpc_ssl; + const command_line::arg_descriptor<std::string> rpc_ssl_private_key; + const command_line::arg_descriptor<std::string> rpc_ssl_certificate; + const command_line::arg_descriptor<std::string> rpc_ssl_ca_certificates; + const command_line::arg_descriptor<std::vector<std::string>> rpc_ssl_allowed_fingerprints; + const command_line::arg_descriptor<bool> rpc_ssl_allow_chained; + const command_line::arg_descriptor<bool> rpc_ssl_allow_any_cert; }; + // `allow_any_cert` bool toggles `--rpc-ssl-allow-any-cert` configuration + static const char* tr(const char* str); - static void init_options(boost::program_options::options_description& desc); + static void init_options(boost::program_options::options_description& desc, const bool any_cert_option = false); //! \return Arguments specified by user, or `boost::none` if error - static boost::optional<rpc_args> process(const boost::program_options::variables_map& vm); + static boost::optional<rpc_args> process(const boost::program_options::variables_map& vm, const bool any_cert_option = false); + + //! \return SSL arguments specified by user, or `boost::none` if error + static boost::optional<epee::net_utils::ssl_options_t> process_ssl(const boost::program_options::variables_map& vm, const bool any_cert_option = false); std::string bind_ip; std::vector<std::string> access_control_origins; boost::optional<tools::login> login; // currently `boost::none` if unspecified by user + epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_enabled; }; } diff --git a/src/serialization/binary_archive.h b/src/serialization/binary_archive.h index a0e4eff9d..9f60cf311 100644 --- a/src/serialization/binary_archive.h +++ b/src/serialization/binary_archive.h @@ -146,7 +146,8 @@ struct binary_archive<false> : public binary_archive_base<std::istream, false> void serialize_uvarint(T &v) { typedef std::istreambuf_iterator<char> it; - tools::read_varint(it(stream_), it(), v); // XXX handle failure + if (tools::read_varint(it(stream_), it(), v) < 0) + stream_.setstate(std::ios_base::failbit); } void begin_array(size_t &s) diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h index 007bf265f..553e9951f 100644 --- a/src/serialization/serialization.h +++ b/src/serialization/serialization.h @@ -212,7 +212,7 @@ inline bool do_serialize(Archive &ar, bool &v) * \brief self-explanatory */ #define END_SERIALIZE() \ - return true; \ + return ar.stream().good(); \ } /*! \macro VALUE(f) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 02a099811..e8b203d9d 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -92,6 +92,8 @@ typedef cryptonote::simple_wallet sw; #define MIN_RING_SIZE 11 // Used to inform user about min ring size -- does not track actual protocol +#define OLD_AGE_WARN_THRESHOLD (30 * 86400 / DIFFICULTY_TARGET_V2) // 30 days + #define LOCK_IDLE_SCOPE() \ bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \ m_auto_refresh_enabled.store(false, std::memory_order_relaxed); \ @@ -5608,6 +5610,43 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending return true; } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::prompt_if_old(const std::vector<tools::wallet2::pending_tx> &ptx_vector) +{ + // count the number of old outputs + std::string err; + uint64_t bc_height = get_daemon_blockchain_height(err); + if (!err.empty()) + return true; + + int max_n_old = 0; + for (const auto &ptx: ptx_vector) + { + int n_old = 0; + for (const auto i: ptx.selected_transfers) + { + const tools::wallet2::transfer_details &td = m_wallet->get_transfer_details(i); + uint64_t age = bc_height - td.m_block_height; + if (age > OLD_AGE_WARN_THRESHOLD) + ++n_old; + } + max_n_old = std::max(max_n_old, n_old); + } + if (max_n_old > 1) + { + std::stringstream prompt; + prompt << tr("Transaction spends more than one very old output. Privacy would be better if they were sent separately."); + prompt << ENDL << tr("Spend them now anyway?"); + std::string accepted = input_line(prompt.str(), true); + if (std::cin.eof()) + return false; + if (!command_line::is_yes(accepted)) + { + return false; + } + } + return true; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::string> &args_, bool called_by_mms) { // "transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>]" @@ -5909,6 +5948,12 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri } } + if (!prompt_if_old(ptx_vector)) + { + fail_msg_writer() << tr("transaction cancelled."); + return false; + } + // if more than one tx necessary, prompt user to confirm if (m_wallet->always_confirm_transfers() || ptx_vector.size() > 1) { @@ -6092,7 +6137,8 @@ bool simple_wallet::locked_transfer(const std::vector<std::string> &args_) //---------------------------------------------------------------------------------------------------- bool simple_wallet::locked_sweep_all(const std::vector<std::string> &args_) { - return sweep_main(0, true, args_); + sweep_main(0, true, args_); + return true; } //---------------------------------------------------------------------------------------------------- @@ -6412,6 +6458,12 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st return true; } + if (!prompt_if_old(ptx_vector)) + { + fail_msg_writer() << tr("transaction cancelled."); + return false; + } + // give user total and fee, and prompt to confirm uint64_t total_fee = 0, total_sent = 0; for (size_t n = 0; n < ptx_vector.size(); ++n) @@ -6758,7 +6810,8 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_) //---------------------------------------------------------------------------------------------------- bool simple_wallet::sweep_all(const std::vector<std::string> &args_) { - return sweep_main(0, false, args_); + sweep_main(0, false, args_); + return true; } //---------------------------------------------------------------------------------------------------- bool simple_wallet::sweep_below(const std::vector<std::string> &args_) @@ -6774,7 +6827,8 @@ bool simple_wallet::sweep_below(const std::vector<std::string> &args_) fail_msg_writer() << tr("invalid amount threshold"); return true; } - return sweep_main(below, false, std::vector<std::string>(++args_.begin(), args_.end())); + sweep_main(below, false, std::vector<std::string>(++args_.begin(), args_.end())); + return true; } //---------------------------------------------------------------------------------------------------- bool simple_wallet::donate(const std::vector<std::string> &args_) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 33b18612c..4bf7fa334 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -261,6 +261,7 @@ namespace cryptonote void on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money); std::pair<std::string, std::string> show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_height = std::numeric_limits<uint64_t>::max()) const; bool freeze_thaw(const std::vector<std::string>& args, bool freeze); + bool prompt_if_old(const std::vector<tools::wallet2::pending_tx> &ptx_vector); struct transfer_view { diff --git a/src/version.cpp.in b/src/version.cpp.in index 8aaa41b19..28ce38df7 100644 --- a/src/version.cpp.in +++ b/src/version.cpp.in @@ -1,5 +1,5 @@ #define DEF_MONERO_VERSION_TAG "@VERSIONTAG@" -#define DEF_MONERO_VERSION "0.14.1.0" +#define DEF_MONERO_VERSION "0.14.1.2" #define DEF_MONERO_RELEASE_NAME "Boron Butterfly" #define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 1711db482..5902eef4b 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1697,6 +1697,17 @@ void WalletImpl::setDefaultMixin(uint32_t arg) m_wallet->default_mixin(arg); } +bool WalletImpl::setCacheAttribute(const std::string &key, const std::string &val) +{ + m_wallet->set_attribute(key, val); + return true; +} + +std::string WalletImpl::getCacheAttribute(const std::string &key) const +{ + return m_wallet->get_attribute(key); +} + bool WalletImpl::setUserNote(const std::string &txid, const std::string ¬e) { cryptonote::blobdata txid_data; diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 9e07b6e19..5dfd997a1 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -168,6 +168,10 @@ public: virtual void setListener(WalletListener * l) override; virtual uint32_t defaultMixin() const override; virtual void setDefaultMixin(uint32_t arg) override; + + virtual bool setCacheAttribute(const std::string &key, const std::string &val) override; + virtual std::string getCacheAttribute(const std::string &key) const override; + virtual bool setUserNote(const std::string &txid, const std::string ¬e) override; virtual std::string getUserNote(const std::string &txid) const override; virtual std::string getTxKey(const std::string &txid) const override; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 0af3b1867..f2e99be21 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -891,6 +891,19 @@ struct Wallet virtual void setDefaultMixin(uint32_t arg) = 0; /*! + * \brief setCacheAttribute - attach an arbitrary string to a wallet cache attribute + * \param key - the key + * \param val - the value + * \return true if successful, false otherwise + */ + virtual bool setCacheAttribute(const std::string &key, const std::string &val) = 0; + /*! + * \brief getCacheAttribute - return an arbitrary string attached to a wallet cache attribute + * \param key - the key + * \return the attached string, or empty string if there is none + */ + virtual std::string getCacheAttribute(const std::string &key) const = 0; + /*! * \brief setUserNote - attach an arbitrary string note to a txid * \param txid - the transaction id to attach the note to * \param note - the note diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index c87947e39..308727657 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -399,8 +399,11 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl { const boost::string_ref real_daemon = boost::string_ref{daemon_address}.substr(0, daemon_address.rfind(':')); + /* If SSL or proxy is enabled, then a specific cert, CA or fingerprint must + be specified. This is specific to the wallet. */ const bool verification_required = - ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_enabled || use_proxy; + ssl_options.verification != epee::net_utils::ssl_verification_t::none && + (ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_enabled || use_proxy); THROW_WALLET_EXCEPTION_IF( verification_required && !ssl_options.has_strong_verification(real_daemon), @@ -6928,7 +6931,7 @@ uint64_t wallet2::get_base_fee() const else return m_light_wallet_per_kb_fee; } - bool use_dyn_fee = use_fork_rules(HF_VERSION_DYNAMIC_FEE, -720 * 1); + bool use_dyn_fee = use_fork_rules(HF_VERSION_DYNAMIC_FEE, -30 * 1); if (!use_dyn_fee) return FEE_PER_KB; @@ -6959,7 +6962,7 @@ int wallet2::get_fee_algorithm() const return 3; if (use_fork_rules(5, 0)) return 2; - if (use_fork_rules(3, -720 * 14)) + if (use_fork_rules(3, -30 * 14)) return 1; return 0; } @@ -7723,7 +7726,6 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> uint64_t num_found = 0; // if we have a known ring, use it - bool existing_ring_found = false; if (td.m_key_image_known && !td.m_key_image_partial) { std::vector<uint64_t> ring; @@ -7735,7 +7737,6 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> std::to_string(ring.size()) + ", it cannot be spent now with ring size " + std::to_string(fake_outputs_count + 1) + " as it is smaller: use a higher ring size"); bool own_found = false; - existing_ring_found = true; for (const auto &out: ring) { MINFO("Ring has output " << out); @@ -7981,7 +7982,6 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> outs.back().push_back(std::make_tuple(td.m_global_output_index, boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key, mask)); // then pick outs from an existing ring, if any - bool existing_ring_found = false; if (td.m_key_image_known && !td.m_key_image_partial) { std::vector<uint64_t> ring; @@ -11384,12 +11384,13 @@ void wallet2::set_attribute(const std::string &key, const std::string &value) m_attributes[key] = value; } -std::string wallet2::get_attribute(const std::string &key) const +bool wallet2::get_attribute(const std::string &key, std::string &value) const { std::unordered_map<std::string, std::string>::const_iterator i = m_attributes.find(key); if (i == m_attributes.end()) - return std::string(); - return i->second; + return false; + value = i->second; + return true; } void wallet2::set_description(const std::string &description) @@ -11399,7 +11400,10 @@ void wallet2::set_description(const std::string &description) std::string wallet2::get_description() const { - return get_attribute(ATTRIBUTE_DESCRIPTION); + std::string s; + if (get_attribute(ATTRIBUTE_DESCRIPTION, s)) + return s; + return ""; } const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& wallet2::get_account_tags() diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 921c150cb..30589885e 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1247,7 +1247,7 @@ private: */ const char* const ATTRIBUTE_DESCRIPTION = "wallet2.description"; void set_attribute(const std::string &key, const std::string &value); - std::string get_attribute(const std::string &key) const; + bool get_attribute(const std::string &key, std::string &value) const; crypto::public_key get_multisig_signer_public_key(const crypto::secret_key &spend_skey) const; crypto::public_key get_multisig_signer_public_key() const; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 16a2b3808..9d3605d11 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -66,11 +66,6 @@ namespace const command_line::arg_descriptor<bool> arg_restricted = {"restricted-rpc", "Restricts to view-only commands", false}; const command_line::arg_descriptor<std::string> arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"}; const command_line::arg_descriptor<bool> arg_prompt_for_password = {"prompt-for-password", "Prompts for password when not provided", false}; - const command_line::arg_descriptor<std::string> arg_rpc_ssl = {"rpc-ssl", tools::wallet2::tr("Enable SSL on wallet RPC connections: enabled|disabled|autodetect"), "autodetect"}; - const command_line::arg_descriptor<std::string> arg_rpc_ssl_private_key = {"rpc-ssl-private-key", tools::wallet2::tr("Path to a PEM format private key"), ""}; - const command_line::arg_descriptor<std::string> arg_rpc_ssl_certificate = {"rpc-ssl-certificate", tools::wallet2::tr("Path to a PEM format certificate"), ""}; - const command_line::arg_descriptor<std::string> arg_rpc_ssl_ca_certificates = {"rpc-ssl-ca-certificates", tools::wallet2::tr("Path to file containing concatenated PEM format certificate(s) to replace system CA(s).")}; - const command_line::arg_descriptor<std::vector<std::string>> arg_rpc_ssl_allowed_fingerprints = {"rpc-ssl-allowed-fingerprints", tools::wallet2::tr("List of certificate fingerprints to allow")}; constexpr const char default_rpc_username[] = "monero"; @@ -244,45 +239,6 @@ namespace tools assert(bool(http_login)); } // end auth enabled - auto rpc_ssl_private_key = command_line::get_arg(vm, arg_rpc_ssl_private_key); - auto rpc_ssl_certificate = command_line::get_arg(vm, arg_rpc_ssl_certificate); - auto rpc_ssl_ca_file = command_line::get_arg(vm, arg_rpc_ssl_ca_certificates); - auto rpc_ssl_allowed_fingerprints = command_line::get_arg(vm, arg_rpc_ssl_allowed_fingerprints); - auto rpc_ssl = command_line::get_arg(vm, arg_rpc_ssl); - epee::net_utils::ssl_options_t rpc_ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_enabled; - - if (!rpc_ssl_ca_file.empty() || !rpc_ssl_allowed_fingerprints.empty()) - { - std::vector<std::vector<uint8_t>> allowed_fingerprints{ rpc_ssl_allowed_fingerprints.size() }; - std::transform(rpc_ssl_allowed_fingerprints.begin(), rpc_ssl_allowed_fingerprints.end(), allowed_fingerprints.begin(), epee::from_hex::vector); - for (const auto &fpr: allowed_fingerprints) - { - if (fpr.size() != SSL_FINGERPRINT_SIZE) - { - MERROR("SHA-256 fingerprint should be " BOOST_PP_STRINGIZE(SSL_FINGERPRINT_SIZE) " bytes long."); - return false; - } - } - - rpc_ssl_options = epee::net_utils::ssl_options_t{ - std::move(allowed_fingerprints), std::move(rpc_ssl_ca_file) - }; - } - - // user specified CA file or fingeprints implies enabled SSL by default - if (rpc_ssl_options.verification != epee::net_utils::ssl_verification_t::user_certificates || !command_line::is_arg_defaulted(vm, arg_rpc_ssl)) - { - if (!epee::net_utils::ssl_support_from_string(rpc_ssl_options.support, rpc_ssl)) - { - MERROR("Invalid argument for " << std::string(arg_rpc_ssl.name)); - return false; - } - } - - rpc_ssl_options.auth = epee::net_utils::ssl_authentication_t{ - std::move(rpc_ssl_private_key), std::move(rpc_ssl_certificate) - }; - m_auto_refresh_period = DEFAULT_AUTO_REFRESH_PERIOD; m_last_auto_refresh_time = boost::posix_time::min_date_time; @@ -292,7 +248,7 @@ namespace tools auto rng = [](size_t len, uint8_t *ptr) { return crypto::rand(len, ptr); }; return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init( rng, std::move(bind_port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login), - std::move(rpc_ssl_options) + std::move(rpc_config->ssl_options) ); } //------------------------------------------------------------------------------------------------------------------------------ @@ -1789,6 +1745,11 @@ namespace tools else if (payment_id_str.size() == 2 * sizeof(payment_id8)) { r = epee::string_tools::hex_to_pod(payment_id_str, payment_id8); + if (r) + { + memcpy(payment_id.data, payment_id8.data, 8); + memset(payment_id.data + 8, 0, 24); + } } else { @@ -1850,14 +1811,12 @@ namespace tools wallet2::transfer_container transfers; m_wallet->get_transfers(transfers); - bool transfers_found = false; for (const auto& td : transfers) { if (!filter || available != td.m_spent) { if (req.account_index != td.m_subaddr_index.major || (!req.subaddr_indices.empty() && req.subaddr_indices.count(td.m_subaddr_index.minor) == 0)) continue; - transfers_found = true; wallet_rpc::transfer_details rpc_transfers; rpc_transfers.amount = td.amount(); rpc_transfers.spent = td.m_spent; @@ -2143,7 +2102,12 @@ namespace tools return false; } - res.value = m_wallet->get_attribute(req.key); + if (!m_wallet->get_attribute(req.key, res.value)) + { + er.code = WALLET_RPC_ERROR_CODE_ATTRIBUTE_NOT_FOUND; + er.message = "Attribute not found."; + return false; + } return true; } bool wallet_rpc_server::on_get_tx_key(const wallet_rpc::COMMAND_RPC_GET_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_GET_TX_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx) @@ -4167,7 +4131,11 @@ namespace tools std::move(req.ssl_private_key_path), std::move(req.ssl_certificate_path) }; - if (ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_enabled && !ssl_options.has_strong_verification(boost::string_ref{})) + const bool verification_required = + ssl_options.verification != epee::net_utils::ssl_verification_t::none && + ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_enabled; + + if (verification_required && !ssl_options.has_strong_verification(boost::string_ref{})) { er.code = WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION; er.message = "SSL is enabled but no user certificate or fingerprints were provided"; @@ -4412,11 +4380,6 @@ int main(int argc, char** argv) { command_line::add_arg(desc_params, arg_from_json); command_line::add_arg(desc_params, arg_wallet_dir); command_line::add_arg(desc_params, arg_prompt_for_password); - command_line::add_arg(desc_params, arg_rpc_ssl); - command_line::add_arg(desc_params, arg_rpc_ssl_private_key); - command_line::add_arg(desc_params, arg_rpc_ssl_certificate); - command_line::add_arg(desc_params, arg_rpc_ssl_ca_certificates); - command_line::add_arg(desc_params, arg_rpc_ssl_allowed_fingerprints); daemonizer::init_options(hidden_options, desc_params); desc_params.add(hidden_options); diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index 011d146d4..9434fbc3e 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -75,3 +75,4 @@ #define WALLET_RPC_ERROR_CODE_SIGN_UNSIGNED -42 #define WALLET_RPC_ERROR_CODE_NON_DETERMINISTIC -43 #define WALLET_RPC_ERROR_CODE_INVALID_LOG_LEVEL -44 +#define WALLET_RPC_ERROR_CODE_ATTRIBUTE_NOT_FOUND -45 diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 614585349..8dde1f475 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -546,6 +546,7 @@ void block_tracker::global_indices(const cryptonote::transaction *tx, std::vecto void block_tracker::get_fake_outs(size_t num_outs, uint64_t amount, uint64_t global_index, uint64_t cur_height, std::vector<get_outs_entry> &outs){ auto & vct = m_outs[amount]; const size_t n_outs = vct.size(); + CHECK_AND_ASSERT_THROW_MES(n_outs > 0, "n_outs is 0"); std::set<size_t> used; std::vector<size_t> choices; diff --git a/tests/core_tests/wallet_tools.cpp b/tests/core_tests/wallet_tools.cpp index d9cee34c1..21a9455c0 100644 --- a/tests/core_tests/wallet_tools.cpp +++ b/tests/core_tests/wallet_tools.cpp @@ -163,6 +163,7 @@ bool wallet_tools::fill_tx_sources(tools::wallet2 * wallet, std::vector<cryptono void wallet_tools::gen_tx_src(size_t mixin, uint64_t cur_height, const tools::wallet2::transfer_details & td, cryptonote::tx_source_entry & src, block_tracker &bt) { + CHECK_AND_ASSERT_THROW_MES(mixin != 0, "mixin is zero"); src.amount = td.amount(); src.rct = td.is_rct(); diff --git a/tests/difficulty/CMakeLists.txt b/tests/difficulty/CMakeLists.txt index fb0dd6b9e..c4f5a5dc0 100644 --- a/tests/difficulty/CMakeLists.txt +++ b/tests/difficulty/CMakeLists.txt @@ -36,7 +36,7 @@ add_executable(difficulty-tests ${difficulty_headers}) target_link_libraries(difficulty-tests PRIVATE - cryptonote_core + cryptonote_basic ${EXTRA_LIBRARIES}) set_property(TARGET difficulty-tests PROPERTY diff --git a/tests/functional_tests/bans.py b/tests/functional_tests/bans.py index bb3051a6a..e859e58c9 100755 --- a/tests/functional_tests/bans.py +++ b/tests/functional_tests/bans.py @@ -28,6 +28,7 @@ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from __future__ import print_function import time """Test peer baning RPC calls @@ -42,7 +43,7 @@ from framework.daemon import Daemon class BanTest(): def run_test(self): - print 'Testing bans' + print('Testing bans') daemon = Daemon() res = daemon.get_bans() diff --git a/tests/functional_tests/blockchain.py b/tests/functional_tests/blockchain.py index 6376b86d0..2bd7672c0 100755 --- a/tests/functional_tests/blockchain.py +++ b/tests/functional_tests/blockchain.py @@ -28,6 +28,7 @@ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from __future__ import print_function import time """Test daemon blockchain RPC calls @@ -50,7 +51,7 @@ class BlockchainTest(): self._test_alt_chains() def reset(self): - print 'Resetting blockchain' + print('Resetting blockchain') daemon = Daemon() daemon.pop_blocks(1000) daemon.flush_txpool() @@ -58,7 +59,7 @@ class BlockchainTest(): def _test_generateblocks(self, blocks): assert blocks >= 2 - print "Test generating", blocks, 'blocks' + print("Test generating", blocks, 'blocks') daemon = Daemon() @@ -73,7 +74,7 @@ class BlockchainTest(): # we should not see a block at height ok = False - try: daemon.getblock(height) + try: daemon.getblock(height = height) except: ok = True assert ok @@ -91,7 +92,7 @@ class BlockchainTest(): # get the blocks, check they have the right height res_getblock = [] for n in range(blocks): - res_getblock.append(daemon.getblock(height + n)) + res_getblock.append(daemon.getblock(height = height + n)) block_header = res_getblock[n].block_header assert abs(block_header.timestamp - time.time()) < 10 # within 10 seconds assert block_header.height == height + n @@ -110,7 +111,7 @@ class BlockchainTest(): # we should not see a block after that ok = False - try: daemon.getblock(height + blocks) + try: daemon.getblock(height = height + blocks) except: ok = True assert ok @@ -156,7 +157,7 @@ class BlockchainTest(): # we should not see the popped block anymore ok = False - try: daemon.getblock(height + blocks - 1) + try: daemon.getblock(height = height + blocks - 1) except: ok = True assert ok @@ -182,14 +183,14 @@ class BlockchainTest(): for idx in tx.output_indices: assert idx == running_output_index running_output_index += 1 - res_out = daemon.get_outs([{'amount': 0, 'index': i} for i in tx.output_indices], get_txid = True) + res_out = daemon.get_outs([{'amount': 0, 'index': idx} for idx in tx.output_indices], get_txid = True) assert len(res_out.outs) == len(tx.output_indices) for out in res_out.outs: assert len(out.key) == 64 assert len(out.mask) == 64 assert not out.unlocked - assert out.height == i + 1 - assert out.txid == txids[i + 1] + assert out.height == i + assert out.txid == txids[i] for i in range(height + nblocks - 1): res_sum = daemon.get_coinbase_tx_sum(i, 1) @@ -255,7 +256,7 @@ class BlockchainTest(): alt_blocks[i] = txid nonce += 1 - print 'mining 3 on 1' + print('mining 3 on 1') # three more on [1] chain1 = [] res = daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 3, prev_block = alt_blocks[1], starting_nonce = nonce) @@ -275,7 +276,7 @@ class BlockchainTest(): for txid in alt_blocks: assert txid in res.blks_hashes or txid == alt_blocks[1] - print 'mining 4 on 3' + print('mining 4 on 3') # 4 more on [3], the chain will reorg when we mine the 4th top_block_hash = blk_hash prev_block = alt_blocks[3] diff --git a/tests/functional_tests/cold_signing.py b/tests/functional_tests/cold_signing.py index 59a879e0a..a722d8927 100755 --- a/tests/functional_tests/cold_signing.py +++ b/tests/functional_tests/cold_signing.py @@ -28,11 +28,10 @@ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import time - """Test cold tx signing """ +from __future__ import print_function from framework.daemon import Daemon from framework.wallet import Wallet @@ -44,13 +43,13 @@ class ColdSigningTest(): self.transfer() def reset(self): - print 'Resetting blockchain' + print('Resetting blockchain') daemon = Daemon() daemon.pop_blocks(1000) daemon.flush_txpool() def create(self, idx): - print 'Creating hot and cold wallet' + print('Creating hot and cold wallet') self.hot_wallet = Wallet(idx = 0) # close the wallet if any, will throw if none is loaded @@ -116,7 +115,7 @@ class ColdSigningTest(): assert len(res.unsigned_txset) > 0 unsigned_txset = res.unsigned_txset - print 'Signing transaction with cold wallet' + print('Signing transaction with cold wallet') res = self.cold_wallet.describe_transfer(unsigned_txset = unsigned_txset) assert len(res.desc) == 1 desc = res.desc[0] @@ -140,7 +139,7 @@ class ColdSigningTest(): txid = res.tx_hash_list[0] assert len(txid) == 64 - print 'Submitting transaction with hot wallet' + print('Submitting transaction with hot wallet') res = self.hot_wallet.submit_transfer(signed_txset) assert len(res.tx_hash_list) > 0 assert res.tx_hash_list[0] == txid diff --git a/tests/functional_tests/daemon_info.py b/tests/functional_tests/daemon_info.py index bd3528c3f..4fa768b03 100755 --- a/tests/functional_tests/daemon_info.py +++ b/tests/functional_tests/daemon_info.py @@ -36,6 +36,7 @@ Test the following RPCs: """ +from __future__ import print_function from framework.daemon import Daemon class DaemonGetInfoTest(): diff --git a/tests/functional_tests/functional_tests_rpc.py b/tests/functional_tests/functional_tests_rpc.py index 1a1fa5490..25ab641ab 100755 --- a/tests/functional_tests/functional_tests_rpc.py +++ b/tests/functional_tests/functional_tests_rpc.py @@ -10,7 +10,7 @@ import string import os USAGE = 'usage: functional_tests_rpc.py <python> <srcdir> <builddir> [<tests-to-run> | all]' -DEFAULT_TESTS = ['daemon_info', 'blockchain', 'wallet_address', 'integrated_address', 'mining', 'transfer', 'txpool', 'multisig', 'cold_signing', 'sign_message', 'proofs', 'get_output_distribution'] +DEFAULT_TESTS = ['bans', 'daemon_info', 'blockchain', 'wallet_address', 'integrated_address', 'mining', 'transfer', 'txpool', 'multisig', 'cold_signing', 'sign_message', 'proofs', 'get_output_distribution'] try: python = sys.argv[1] srcdir = sys.argv[2] diff --git a/tests/functional_tests/get_output_distribution.py b/tests/functional_tests/get_output_distribution.py index 2a0762d5e..93822e90a 100755 --- a/tests/functional_tests/get_output_distribution.py +++ b/tests/functional_tests/get_output_distribution.py @@ -28,11 +28,10 @@ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import time - """Test get_output_distribution RPC """ +from __future__ import print_function from framework.daemon import Daemon from framework.wallet import Wallet @@ -43,7 +42,7 @@ class GetOutputDistributionTest(): self.test_get_output_distribution() def reset(self): - print 'Resetting blockchain' + print('Resetting blockchain') daemon = Daemon() daemon.pop_blocks(1000) daemon.flush_txpool() @@ -56,7 +55,7 @@ class GetOutputDistributionTest(): res = self.wallet.restore_deterministic_wallet(seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted') def test_get_output_distribution(self): - print "Test get_output_distribution" + print("Test get_output_distribution") daemon = Daemon() @@ -213,5 +212,14 @@ class GetOutputDistributionTest(): assert d.distribution[h] == 0 +class Guard: + def __enter__(self): + for i in range(4): + Wallet(idx = i).auto_refresh(False) + def __exit__(self, exc_type, exc_value, traceback): + for i in range(4): + Wallet(idx = i).auto_refresh(True) + if __name__ == '__main__': - GetOutputDistributionTest().run_test() + with Guard() as guard: + GetOutputDistributionTest().run_test() diff --git a/tests/functional_tests/integrated_address.py b/tests/functional_tests/integrated_address.py index 338dd14ae..4e42261a6 100755 --- a/tests/functional_tests/integrated_address.py +++ b/tests/functional_tests/integrated_address.py @@ -28,8 +28,6 @@ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import time - """Test integrated address RPC calls Test the following RPCs: @@ -38,6 +36,7 @@ Test the following RPCs: """ +from __future__ import print_function from framework.wallet import Wallet class IntegratedAddressTest(): @@ -46,7 +45,7 @@ class IntegratedAddressTest(): self.check() def create(self): - print 'Creating wallet' + print('Creating wallet') wallet = Wallet() # close the wallet if any, will throw if none is loaded try: wallet.close_wallet() @@ -59,7 +58,7 @@ class IntegratedAddressTest(): def check(self): wallet = Wallet() - print 'Checking local address' + print('Checking local address') res = wallet.make_integrated_address(payment_id = '0123456789abcdef') assert res.integrated_address == '4CMe2PUhs4J4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfSbLRB61BQVATzerHGj' assert res.payment_id == '0123456789abcdef' @@ -67,7 +66,7 @@ class IntegratedAddressTest(): assert res.standard_address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' assert res.payment_id == '0123456789abcdef' - print 'Checking different address' + print('Checking different address') res = wallet.make_integrated_address(standard_address = '46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', payment_id = '1122334455667788') assert res.integrated_address == '4GYjoMG9Y2BBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCVSs1ZojwrDCGS5rUuo' assert res.payment_id == '1122334455667788' @@ -75,7 +74,7 @@ class IntegratedAddressTest(): assert res.standard_address == '46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK' assert res.payment_id == '1122334455667788' - print 'Checking bad payment id' + print('Checking bad payment id') fails = 0 try: wallet.make_integrated_address(standard_address = '46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', payment_id = '11223344556677880') except: fails += 1 @@ -89,7 +88,7 @@ class IntegratedAddressTest(): except: fails += 1 assert fails == 5 - print 'Checking bad standard address' + print('Checking bad standard address') fails = 0 try: wallet.make_integrated_address(standard_address = '46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerr', payment_id = '1122334455667788') except: fails += 1 diff --git a/tests/functional_tests/mining.py b/tests/functional_tests/mining.py index 78dc68640..5c14d34fd 100755 --- a/tests/functional_tests/mining.py +++ b/tests/functional_tests/mining.py @@ -28,6 +28,7 @@ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from __future__ import print_function import time """Test daemon mining RPC calls @@ -48,13 +49,13 @@ class MiningTest(): self.mine() def reset(self): - print 'Resetting blockchain' + print('Resetting blockchain') daemon = Daemon() daemon.pop_blocks(1000) daemon.flush_txpool() def create(self): - print 'Creating wallet' + print('Creating wallet') wallet = Wallet() # close the wallet if any, will throw if none is loaded try: wallet.close_wallet() @@ -62,7 +63,7 @@ class MiningTest(): res = wallet.restore_deterministic_wallet(seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted') def mine(self): - print "Test mining" + print("Test mining") daemon = Daemon() wallet = Wallet() diff --git a/tests/functional_tests/multisig.py b/tests/functional_tests/multisig.py index 476e3a02d..3c8cd9c1d 100755 --- a/tests/functional_tests/multisig.py +++ b/tests/functional_tests/multisig.py @@ -28,7 +28,7 @@ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import time +from __future__ import print_function """Test multisig transfers """ @@ -70,7 +70,7 @@ class MultisigTest(): self.check_transaction(txid) def reset(self): - print 'Resetting blockchain' + print('Resetting blockchain') daemon = Daemon() daemon.pop_blocks(1000) daemon.flush_txpool() diff --git a/tests/functional_tests/proofs.py b/tests/functional_tests/proofs.py index 844131095..243929dc3 100755 --- a/tests/functional_tests/proofs.py +++ b/tests/functional_tests/proofs.py @@ -28,7 +28,7 @@ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import time +from __future__ import print_function """Test misc proofs (tx key, send, receive, reserve) """ @@ -47,7 +47,7 @@ class ProofsTest(): self.check_reserve_proof() def reset(self): - print 'Resetting blockchain' + print('Resetting blockchain') daemon = Daemon() daemon.pop_blocks(1000) daemon.flush_txpool() diff --git a/tests/functional_tests/sign_message.py b/tests/functional_tests/sign_message.py index 4c3ec3588..de8f0cee2 100755 --- a/tests/functional_tests/sign_message.py +++ b/tests/functional_tests/sign_message.py @@ -28,7 +28,7 @@ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import time +from __future__ import print_function """Test message signing/verification RPC calls @@ -46,7 +46,7 @@ class MessageSigningTest(): self.check_signing() def create(self): - print 'Creating wallets' + print('Creating wallets') seeds = [ 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted', 'peeled mixture ionic radar utopia puddle buying illness nuns gadget river spout cavernous bounced paradise drunk looking cottage jump tequila melting went winter adjust spout', @@ -66,7 +66,7 @@ class MessageSigningTest(): assert res.seed == seeds[i] def check_signing(self): - print 'Signing/verifing messages' + print('Signing/verifing messages') messages = ['foo', ''] for message in messages: res = self.wallet[0].sign(message) diff --git a/tests/functional_tests/speed.py b/tests/functional_tests/speed.py index bd8892df8..ed1e332e9 100755 --- a/tests/functional_tests/speed.py +++ b/tests/functional_tests/speed.py @@ -40,7 +40,7 @@ Test the following RPCs: import time from time import sleep -from decimal import Decimal +from __future__ import print_function from framework.daemon import Daemon from framework.wallet import Wallet diff --git a/tests/functional_tests/transfer.py b/tests/functional_tests/transfer.py index 1ff641d1f..d9a6e592e 100755 --- a/tests/functional_tests/transfer.py +++ b/tests/functional_tests/transfer.py @@ -28,7 +28,7 @@ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import time +from __future__ import print_function import json """Test simple transfers @@ -48,13 +48,13 @@ class TransferTest(): self.sweep_single() def reset(self): - print 'Resetting blockchain' + print('Resetting blockchain') daemon = Daemon() daemon.pop_blocks(1000) daemon.flush_txpool() def create(self): - print 'Creating wallets' + print('Creating wallets') seeds = [ 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted', 'peeled mixture ionic radar utopia puddle buying illness nuns gadget river spout cavernous bounced paradise drunk looking cottage jump tequila melting went winter adjust spout', @@ -297,7 +297,7 @@ class TransferTest(): assert res.unlocked_balance <= res.balance assert res.blocks_to_unlock == 9 - print 'Creating multi out transfer' + print('Creating multi out transfer') self.wallet[0].refresh() @@ -519,6 +519,9 @@ class TransferTest(): res = self.wallet[2].get_bulk_payments(payment_ids = ['1'*64, '1234500000012345abcde00000abcdeff1234500000012345abcde00000abcde', '2'*64]) assert len(res.payments) >= 1 # one tx was sent + res = self.wallet[1].get_bulk_payments(["1111111122222222"]) + assert len(res.payments) >= 1 # we have one of these + def check_double_spend_detection(self): print('Checking double spend detection') txes = [[None, None], [None, None]] diff --git a/tests/functional_tests/txpool.py b/tests/functional_tests/txpool.py index d74395f10..b6af4c84f 100755 --- a/tests/functional_tests/txpool.py +++ b/tests/functional_tests/txpool.py @@ -28,7 +28,7 @@ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import time +from __future__ import print_function """Test txpool """ @@ -44,13 +44,13 @@ class TransferTest(): self.check_txpool() def reset(self): - print 'Resetting blockchain' + print('Resetting blockchain') daemon = Daemon() daemon.pop_blocks(1000) daemon.flush_txpool() def create(self): - print 'Creating wallet' + print('Creating wallet') wallet = Wallet() # close the wallet if any, will throw if none is loaded try: wallet.close_wallet() @@ -114,15 +114,16 @@ class TransferTest(): assert sorted(res.tx_hashes) == sorted(txes.keys()) print('Flushing 2 transactions') - daemon.flush_txpool([txes.keys()[1], txes.keys()[3]]) + txes_keys = list(txes.keys()) + daemon.flush_txpool([txes_keys[1], txes_keys[3]]) res = daemon.get_transaction_pool() assert len(res.transactions) == txpool_size - 2 - assert len([x for x in res.transactions if x.id_hash == txes.keys()[1]]) == 0 - assert len([x for x in res.transactions if x.id_hash == txes.keys()[3]]) == 0 + assert len([x for x in res.transactions if x.id_hash == txes_keys[1]]) == 0 + assert len([x for x in res.transactions if x.id_hash == txes_keys[3]]) == 0 - new_keys = txes.keys() - new_keys.remove(txes.keys()[1]) - new_keys.remove(txes.keys()[3]) + new_keys = list(txes.keys()) + new_keys.remove(txes_keys[1]) + new_keys.remove(txes_keys[3]) res = daemon.get_transaction_pool_hashes() assert sorted(res.tx_hashes) == sorted(new_keys) diff --git a/tests/functional_tests/wallet_address.py b/tests/functional_tests/wallet_address.py index 8a55521c6..4ff059a6f 100755 --- a/tests/functional_tests/wallet_address.py +++ b/tests/functional_tests/wallet_address.py @@ -29,8 +29,6 @@ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import time - """Test transaction creation RPC calls Test the following RPCs: @@ -38,6 +36,7 @@ Test the following RPCs: """ +from __future__ import print_function from framework.wallet import Wallet from framework.daemon import Daemon @@ -52,13 +51,13 @@ class WalletAddressTest(): self.languages() def reset(self): - print 'Resetting blockchain' + print('Resetting blockchain') daemon = Daemon() daemon.pop_blocks(1000) daemon.flush_txpool() def create(self): - print 'Creating wallet' + print('Creating wallet') wallet = Wallet() # close the wallet if any, will throw if none is loaded try: wallet.close_wallet() @@ -69,7 +68,7 @@ class WalletAddressTest(): assert res.seed == seed def check_main_address(self): - print 'Getting address' + print('Getting address') wallet = Wallet() res = wallet.get_address() assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', res @@ -79,7 +78,7 @@ class WalletAddressTest(): assert res.addresses[0].used == False def check_keys(self): - print 'Checking keys' + print('Checking keys') wallet = Wallet() res = wallet.query_key('view_key') assert res.key == '49774391fa5e8d249fc2c5b45dadef13534bf2483dede880dac88f061e809100' @@ -89,7 +88,7 @@ class WalletAddressTest(): assert res.key == 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted' def create_subaddresses(self): - print 'Creating subaddresses' + print('Creating subaddresses') wallet = Wallet() res = wallet.create_account("idx1") assert res.account_index == 1, res @@ -160,7 +159,7 @@ class WalletAddressTest(): assert res.index == {'major': 1, 'minor': 0} def open_close(self): - print 'Testing open/close' + print('Testing open/close') wallet = Wallet() res = wallet.get_address() @@ -200,7 +199,7 @@ class WalletAddressTest(): except: pass languages = res.languages for language in languages: - print 'Creating ' + str(language) + ' wallet' + print('Creating ' + str(language) + ' wallet') wallet.create_wallet(filename = '', language = language) res = wallet.query_key('mnemonic') wallet.close_wallet() diff --git a/tests/unit_tests/ban.cpp b/tests/unit_tests/ban.cpp index 0b267172f..17fba90c6 100644 --- a/tests/unit_tests/ban.cpp +++ b/tests/unit_tests/ban.cpp @@ -36,6 +36,7 @@ #include "cryptonote_protocol/cryptonote_protocol_handler.inl" #define MAKE_IPV4_ADDRESS(a,b,c,d) epee::net_utils::ipv4_network_address{MAKE_IP(a,b,c,d),0} +#define MAKE_IPV4_SUBNET(a,b,c,d,e) epee::net_utils::ipv4_network_subnet{MAKE_IP(a,b,c,d),e} namespace cryptonote { class blockchain_storage; @@ -93,11 +94,10 @@ typedef nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<test_cor static bool is_blocked(Server &server, const epee::net_utils::network_address &address, time_t *t = NULL) { - const std::string host = address.host_str(); - std::map<std::string, time_t> hosts = server.get_blocked_hosts(); + std::map<epee::net_utils::network_address, time_t> hosts = server.get_blocked_hosts(); for (auto rec: hosts) { - if (rec.first == host) + if (rec.first == address) { if (t) *t = rec.second; @@ -208,5 +208,37 @@ TEST(ban, limit) ASSERT_TRUE(is_blocked(server,MAKE_IPV4_ADDRESS(1,2,3,4))); } +TEST(ban, subnet) +{ + time_t seconds; + test_core pr_core; + cryptonote::t_cryptonote_protocol_handler<test_core> cprotocol(pr_core, NULL); + Server server(cprotocol); + cprotocol.set_p2p_endpoint(&server); + + ASSERT_TRUE(server.block_subnet(MAKE_IPV4_SUBNET(1,2,3,4,24), 10)); + ASSERT_TRUE(server.get_blocked_subnets().size() == 1); + ASSERT_TRUE(server.is_host_blocked(MAKE_IPV4_ADDRESS(1,2,3,4), &seconds)); + ASSERT_TRUE(seconds >= 9); + ASSERT_TRUE(server.is_host_blocked(MAKE_IPV4_ADDRESS(1,2,3,255), &seconds)); + ASSERT_TRUE(server.is_host_blocked(MAKE_IPV4_ADDRESS(1,2,3,0), &seconds)); + ASSERT_FALSE(server.is_host_blocked(MAKE_IPV4_ADDRESS(1,2,4,0), &seconds)); + ASSERT_FALSE(server.is_host_blocked(MAKE_IPV4_ADDRESS(1,2,2,0), &seconds)); + ASSERT_TRUE(server.unblock_subnet(MAKE_IPV4_SUBNET(1,2,3,8,24))); + ASSERT_TRUE(server.get_blocked_subnets().size() == 0); + ASSERT_FALSE(server.is_host_blocked(MAKE_IPV4_ADDRESS(1,2,3,255), &seconds)); + ASSERT_FALSE(server.is_host_blocked(MAKE_IPV4_ADDRESS(1,2,3,0), &seconds)); + ASSERT_TRUE(server.block_subnet(MAKE_IPV4_SUBNET(1,2,3,4,8), 10)); + ASSERT_TRUE(server.get_blocked_subnets().size() == 1); + ASSERT_TRUE(server.is_host_blocked(MAKE_IPV4_ADDRESS(1,255,3,255), &seconds)); + ASSERT_TRUE(server.is_host_blocked(MAKE_IPV4_ADDRESS(1,0,3,255), &seconds)); + ASSERT_FALSE(server.unblock_subnet(MAKE_IPV4_SUBNET(1,2,3,8,24))); + ASSERT_TRUE(server.get_blocked_subnets().size() == 1); + ASSERT_TRUE(server.block_subnet(MAKE_IPV4_SUBNET(1,2,3,4,8), 10)); + ASSERT_TRUE(server.get_blocked_subnets().size() == 1); + ASSERT_TRUE(server.unblock_subnet(MAKE_IPV4_SUBNET(1,255,0,0,8))); + ASSERT_TRUE(server.get_blocked_subnets().size() == 0); +} + namespace nodetool { template class node_server<cryptonote::t_cryptonote_protocol_handler<test_core>>; } namespace cryptonote { template class t_cryptonote_protocol_handler<test_core>; } diff --git a/tests/unit_tests/keccak.cpp b/tests/unit_tests/keccak.cpp index 37da65d76..f4d41a8fa 100644 --- a/tests/unit_tests/keccak.cpp +++ b/tests/unit_tests/keccak.cpp @@ -148,3 +148,20 @@ TEST(keccak, 137_and_1_136) TEST_KECCAK(137, chunks); } +TEST(keccak, alignment) +{ + uint8_t data[6064]; + __attribute__ ((aligned(16))) char adata[6000]; + + for (size_t i = 0; i < sizeof(data) / sizeof(data[0]); ++i) + data[i] = i & 1; + + uint8_t md[32], amd[32]; + for (int offset = 0; offset < 64; ++offset) + { + memcpy(adata, data + offset, 6000); + keccak((const uint8_t*)&data[offset], 6000, md, 32); + keccak((const uint8_t*)adata, 6000, amd, 32); + ASSERT_TRUE(!memcmp(md, amd, 32)); + } +} diff --git a/tests/unit_tests/mnemonics.cpp b/tests/unit_tests/mnemonics.cpp index 16634e7a1..51feb54e4 100644 --- a/tests/unit_tests/mnemonics.cpp +++ b/tests/unit_tests/mnemonics.cpp @@ -82,7 +82,7 @@ namespace crypto::secret_key randkey; for (size_t ii = 0; ii < sizeof(randkey); ++ii) { - randkey.data[ii] = rand(); + randkey.data[ii] = crypto::rand<uint8_t>(); } crypto::ElectrumWords::bytes_to_words(randkey, w_seed, language.get_language_name()); seed = std::string(w_seed.data(), w_seed.size()); @@ -256,4 +256,4 @@ TEST(mnemonics, partial_word_tolerance) res = crypto::ElectrumWords::words_to_bytes(seed_1, key_1, language_name_1); ASSERT_EQ(true, res); ASSERT_STREQ(language_name_1.c_str(), "English"); -}
\ No newline at end of file +} diff --git a/tests/unit_tests/net.cpp b/tests/unit_tests/net.cpp index 326e63db8..3acf75f3b 100644 --- a/tests/unit_tests/net.cpp +++ b/tests/unit_tests/net.cpp @@ -524,6 +524,24 @@ TEST(get_network_address, ipv4) EXPECT_STREQ("23.0.0.254:2000", address->str().c_str()); } +TEST(get_network_address, ipv4subnet) +{ + expect<epee::net_utils::ipv4_network_subnet> address = net::get_ipv4_subnet_address("0.0.0.0", true); + EXPECT_STREQ("0.0.0.0/32", address->str().c_str()); + + address = net::get_ipv4_subnet_address("0.0.0.0"); + EXPECT_TRUE(!address); + + address = net::get_ipv4_subnet_address("0.0.0.0/32"); + EXPECT_STREQ("0.0.0.0/32", address->str().c_str()); + + address = net::get_ipv4_subnet_address("0.0.0.0/0"); + EXPECT_STREQ("0.0.0.0/0", address->str().c_str()); + + address = net::get_ipv4_subnet_address("12.34.56.78/16"); + EXPECT_STREQ("12.34.0.0/16", address->str().c_str()); +} + namespace { using stream_type = boost::asio::ip::tcp; diff --git a/tests/unit_tests/output_selection.cpp b/tests/unit_tests/output_selection.cpp index 0094fc765..c98696fbd 100644 --- a/tests/unit_tests/output_selection.cpp +++ b/tests/unit_tests/output_selection.cpp @@ -138,7 +138,7 @@ TEST(select_outputs, density) static const size_t NPICKS = 1000000; std::vector<uint64_t> offsets; - MKOFFSETS(300000, 1 + (rand() & 0x1f)); + MKOFFSETS(300000, 1 + (crypto::rand<size_t>() & 0x1f)); tools::gamma_picker picker(offsets); std::vector<int> picks(/*n_outs*/offsets.size(), 0); @@ -172,7 +172,7 @@ TEST(select_outputs, density) float chain_ratio = count_chain / (float)n_outs; MDEBUG(count_selected << "/" << NPICKS << " outputs selected in blocks of density " << d << ", " << 100.0f * selected_ratio << "%"); MDEBUG(count_chain << "/" << offsets.size() << " outputs in blocks of density " << d << ", " << 100.0f * chain_ratio << "%"); - ASSERT_LT(fabsf(selected_ratio - chain_ratio), 0.02f); + ASSERT_LT(fabsf(selected_ratio - chain_ratio), 0.025f); } } @@ -181,7 +181,7 @@ TEST(select_outputs, same_distribution) static const size_t NPICKS = 1000000; std::vector<uint64_t> offsets; - MKOFFSETS(300000, 1 + (rand() & 0x1f)); + MKOFFSETS(300000, 1 + (crypto::rand<size_t>() & 0x1f)); tools::gamma_picker picker(offsets); std::vector<int> chain_picks(offsets.size(), 0); diff --git a/tests/unit_tests/rolling_median.cpp b/tests/unit_tests/rolling_median.cpp index 6d6adcc7d..547fe092f 100644 --- a/tests/unit_tests/rolling_median.cpp +++ b/tests/unit_tests/rolling_median.cpp @@ -69,7 +69,7 @@ TEST(rolling_median, series) v.reserve(100); for (int i = 0; i < 10000; ++i) { - uint64_t r = rand(); + uint64_t r = crypto::rand<uint64_t>(); v.push_back(r); if (v.size() > 100) v.erase(v.begin()); @@ -87,7 +87,7 @@ TEST(rolling_median, clear_whole) median.reserve(10000); for (int i = 0; i < 10000; ++i) { - random.push_back(rand()); + random.push_back(crypto::rand<uint64_t>()); m.insert(random.back()); median.push_back(m.median()); } @@ -107,7 +107,7 @@ TEST(rolling_median, clear_partway) median.reserve(10000); for (int i = 0; i < 10000; ++i) { - random.push_back(rand()); + random.push_back(crypto::rand<uint64_t>()); m.insert(random.back()); median.push_back(m.median()); } @@ -126,7 +126,7 @@ TEST(rolling_median, order) random.reserve(1000); for (int i = 0; i < 1000; ++i) { - random.push_back(rand()); + random.push_back(crypto::rand<uint64_t>()); m.insert(random.back()); } const uint64_t med = m.median(); diff --git a/tests/unit_tests/test_protocol_pack.cpp b/tests/unit_tests/test_protocol_pack.cpp index 7329c0d23..0ae2e9c68 100644 --- a/tests/unit_tests/test_protocol_pack.cpp +++ b/tests/unit_tests/test_protocol_pack.cpp @@ -48,6 +48,7 @@ TEST(protocol_pack, protocol_pack_command) cryptonote::NOTIFY_RESPONSE_CHAIN_ENTRY::request r2; res = epee::serialization::load_t_from_binary(r2, buff); + ASSERT_TRUE(res); ASSERT_TRUE(r.m_block_ids.size() == i); ASSERT_TRUE(r.start_height == 1); ASSERT_TRUE(r.total_height == 3); diff --git a/tests/unit_tests/varint.cpp b/tests/unit_tests/varint.cpp index ca0900682..72691d722 100644 --- a/tests/unit_tests/varint.cpp +++ b/tests/unit_tests/varint.cpp @@ -56,7 +56,6 @@ TEST(varint, equal) ASSERT_TRUE (bytes > 0 && bytes <= sizeof(buf)); uint64_t idx2; - bufptr = buf; std::string s(buf, bytes); int read = tools::read_varint(s.begin(), s.end(), idx2); ASSERT_EQ (read, bytes); diff --git a/translations/generate_translations_header.c b/translations/generate_translations_header.c index 4c80eec31..85e9b9b27 100644 --- a/translations/generate_translations_header.c +++ b/translations/generate_translations_header.c @@ -48,6 +48,7 @@ int main(int argc, char *argv[]) { for (i = 1; i < argc; i++) { if ((fp = fopen(argv[i], "rb")) == NULL) { + fclose(foutput); exit(EXIT_FAILURE); } else { fprintf(foutput, "static const std::string translation_file_name_%d = \"%s\";\n", i, argv[i]); diff --git a/utils/python-rpc/framework/daemon.py b/utils/python-rpc/framework/daemon.py index 04fc5b5cf..18ce37221 100644 --- a/utils/python-rpc/framework/daemon.py +++ b/utils/python-rpc/framework/daemon.py @@ -67,11 +67,13 @@ class Daemon(object): } return self.rpc.send_json_rpc_request(submitblock) - def getblock(self, height=0): + def getblock(self, hash = '', height = 0, fill_pow_hash = False): getblock = { 'method': 'getblock', 'params': { - 'height': height + 'hash': hash, + 'height': height, + 'fill_pow_hash': fill_pow_hash, }, 'jsonrpc': '2.0', 'id': '0' |