diff options
122 files changed, 1982 insertions, 584 deletions
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..41a0036cf --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: https://web.getmonero.org/get-started/contributing/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a22478e9..b150a8998 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") @@ -878,10 +884,10 @@ if (${BOOST_IGNORE_SYSTEM_PATHS} STREQUAL "ON") endif() set(OLD_LIB_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) +set(Boost_NO_BOOST_CMAKE ON) if(STATIC) if(MINGW) set(CMAKE_FIND_LIBRARY_SUFFIXES .a) - set(Boost_NO_BOOST_CMAKE ON) endif() set(Boost_USE_STATIC_LIBS ON) @@ -998,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_utils_base.h b/contrib/epee/include/net/net_utils_base.h index 83e6b5ab8..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; 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/README.md b/contrib/gitian/README.md index 1efc87e0a..3fc26354c 100644 --- a/contrib/gitian/README.md +++ b/contrib/gitian/README.md @@ -80,14 +80,23 @@ Building in docker does not require much setup. Install docker on your host, the ```bash sudo apt-get install git make curl -sudo usermod -aG docker gitianuser ``` +Optionally add yourself to the docker group. Note that this will give docker root access to your system. + +```bash +sudo usermod -aG docker gitianuser +``` Manual and Building ------------------- The instructions below use the automated script [gitian-build.py](gitian-build.py) which only works in Ubuntu. +======= +The script automatically installs some packages with apt. If you are not running it on a debian-like system, pass `--no-apt` along with the other +arguments to it. It calls all available .yml descriptors, which in turn pass the build configurations for different platforms to gitian. +The instructions below use the automated script [gitian-build.py](gitian-build.py) which is tested to work on Ubuntu. + It calls all available .yml descriptors, which in turn pass the build configurations for different platforms to gitian. Help for the build steps taken can be accessed with `./gitian-build.py --help`. diff --git a/contrib/gitian/gitian-build.py b/contrib/gitian/gitian-build.py index cd88ecb20..b654b15c7 100755 --- a/contrib/gitian/gitian-build.py +++ b/contrib/gitian/gitian-build.py @@ -7,21 +7,13 @@ import sys def setup(): global args, workdir - programs = ['ruby', 'git', 'apt-cacher-ng', 'make', 'wget'] + programs = ['apt-cacher-ng', 'ruby', 'git', 'make', 'wget'] if args.kvm: programs += ['python-vm-builder', 'qemu-kvm', 'qemu-utils'] - elif args.docker: - dockers = ['docker.io', 'docker-ce'] - for i in dockers: - return_code = subprocess.call(['sudo', 'apt-get', 'install', '-qq', i]) - if return_code == 0: - break - if return_code != 0: - print('Cannot find any way to install docker', file=sys.stderr) - exit(1) else: programs += ['lxc', 'debootstrap'] - subprocess.check_call(['sudo', 'apt-get', 'install', '-qq'] + programs) + if not args.no_apt: + subprocess.check_call(['sudo', 'apt-get', 'install', '-qq'] + programs) if not os.path.isdir('gitian.sigs'): subprocess.check_call(['git', 'clone', 'https://github.com/monero-project/gitian.sigs.git']) if not os.path.isdir('gitian-builder'): @@ -32,6 +24,8 @@ def setup(): subprocess.check_call(['git', 'checkout', '963322de8420c50502c4cc33d4d7c0d84437b576']) make_image_prog = ['bin/make-base-vm', '--suite', 'bionic', '--arch', 'amd64'] if args.docker: + if not subprocess.call(['docker', '--help'], shell=False, stdout=subprocess.DEVNULL): + print("Please install docker first manually") make_image_prog += ['--docker'] elif not args.kvm: make_image_prog += ['--lxc'] @@ -40,7 +34,7 @@ def setup(): if args.is_bionic and not args.kvm and not args.docker: subprocess.check_call(['sudo', 'sed', '-i', 's/lxcbr0/br0/', '/etc/default/lxc-net']) print('Reboot is required') - exit(0) + sys.exit(0) def build(): global args, workdir @@ -100,7 +94,7 @@ def verify(): def main(): global args, workdir - parser = argparse.ArgumentParser(usage='%(prog)s [options] signer version') + parser = argparse.ArgumentParser(description='Script for running full Gitian builds.', usage='%(prog)s [options] signer version') parser.add_argument('-c', '--commit', action='store_true', dest='commit', help='Indicate that the version argument is for a commit or branch') parser.add_argument('-p', '--pull', action='store_true', dest='pull', help='Indicate that the version argument is the number of a github repository pull request') parser.add_argument('-u', '--url', dest='url', default='https://github.com/monero-project/monero', help='Specify the URL of the repository. Default is %(default)s') @@ -112,11 +106,12 @@ def main(): parser.add_argument('-m', '--memory', dest='memory', default='2000', help='Memory to allocate in MiB. Default %(default)s') parser.add_argument('-k', '--kvm', action='store_true', dest='kvm', help='Use KVM instead of LXC') parser.add_argument('-d', '--docker', action='store_true', dest='docker', help='Use Docker instead of LXC') - parser.add_argument('-S', '--setup', action='store_true', dest='setup', help='Set up the Gitian building environment. Uses LXC. If you want to use KVM, use the --kvm option. Only works on Debian-based systems (Ubuntu, Debian)') + parser.add_argument('-S', '--setup', action='store_true', dest='setup', help='Set up the Gitian building environment. Uses LXC. If you want to use KVM, use the --kvm option. If you run this script on a non-debian based system, pass the --no-apt flag') parser.add_argument('-D', '--detach-sign', action='store_true', dest='detach_sign', help='Create the assert file for detached signing. Will not commit anything.') parser.add_argument('-n', '--no-commit', action='store_false', dest='commit_files', help='Do not commit anything to git') - parser.add_argument('signer', help='GPG signer to sign each build assert file') - parser.add_argument('version', help='Version number, commit, or branch to build.') + parser.add_argument('signer', nargs='?', help='GPG signer to sign each build assert file') + parser.add_argument('version', nargs='?', help='Version number, commit, or branch to build.') + parser.add_argument('-a', '--no-apt', action='store_true', dest='no_apt', help='Indicate that apt is not installed on the system') args = parser.parse_args() workdir = os.getcwd() @@ -128,8 +123,8 @@ def main(): args.is_bionic = b'bionic' in subprocess.check_output(['lsb_release', '-cs']) if args.buildsign: - args.build=True - args.sign=True + args.build = True + args.sign = True if args.kvm and args.docker: raise Exception('Error: cannot have both kvm and docker') @@ -156,11 +151,11 @@ def main(): if args.signer == '': print(script_name+': Missing signer.') print('Try '+script_name+' --help for more information') - exit(1) + sys.exit(1) if args.version == '': print(script_name+': Missing version.') print('Try '+script_name+' --help for more information') - exit(1) + sys.exit(1) # Add leading 'v' for tags if args.commit and args.pull: 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/trezor-common b/external/trezor-common -Subproject cb238cb1f134accc4200217d9511115a8f61c6c +Subproject 31a0073c62738827b48d725facd376687942912 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 b6b8c6c3e..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 */ @@ -1543,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 * @@ -1634,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 34e635899..ac19fae25 100644 --- a/src/blockchain_db/testdb.h +++ b/src/blockchain_db/testdb.h @@ -156,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/blockchain_utilities/blockchain_export.cpp b/src/blockchain_utilities/blockchain_export.cpp index fa1243c1f..85566efca 100644 --- a/src/blockchain_utilities/blockchain_export.cpp +++ b/src/blockchain_utilities/blockchain_export.cpp @@ -177,7 +177,7 @@ int main(int argc, char* argv[]) } r = core_storage->init(db, opt_testnet ? cryptonote::TESTNET : opt_stagenet ? cryptonote::STAGENET : cryptonote::MAINNET); - if (core_storage->get_blockchain_pruning_seed()) + if (core_storage->get_blockchain_pruning_seed() && !opt_blocks_dat) { LOG_PRINT_L0("Blockchain is pruned, cannot export"); return 1; 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/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..1fa819b57 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -43,7 +43,6 @@ #include "CryptonightR_JIT.h" #include <errno.h> -#include <string.h> #define MEMORY (1 << 21) // 2MB scratchpad #define ITER (1 << 20) @@ -897,7 +896,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_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 3841aa1cf..31e2ac136 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -731,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); @@ -858,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; @@ -1019,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); @@ -1030,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; @@ -1039,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); @@ -1050,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 @@ -1070,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; } @@ -1102,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); @@ -1120,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) { @@ -1138,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; @@ -1156,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 @@ -1171,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; @@ -1393,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)) @@ -1417,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(); @@ -1432,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); } @@ -1618,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 @@ -1635,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 @@ -1703,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; @@ -1716,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) @@ -1763,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 { @@ -1774,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); @@ -1794,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) @@ -1814,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() << ")"); } @@ -1893,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 @@ -1908,8 +1939,6 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO //pack block e.block = std::move(bl.first); } - //get and pack other transactions, if needed - get_transactions_blobs(arg.txs, rsp.txs, rsp.missed_ids); return true; } @@ -1919,11 +1948,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; } //------------------------------------------------------------------ @@ -1931,7 +1969,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 @@ -2035,7 +2073,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; @@ -2049,7 +2086,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); @@ -2419,9 +2456,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; } @@ -3250,7 +3287,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; @@ -3958,14 +3994,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 { @@ -3987,7 +4021,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) { @@ -4810,11 +4844,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) { @@ -4828,7 +4886,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; @@ -4845,7 +4903,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 32ed96b5b..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 @@ -1186,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 @@ -1249,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 @@ -1259,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 55e1b287f..2ebd99904 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -208,13 +208,17 @@ 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): m_mempool(m_blockchain_storage), m_blockchain_storage(m_mempool), m_miner(this), - m_miner_address(boost::value_initialized<account_public_address>()), m_starter_message_showed(false), m_target_blockchain_height(0), m_checkpoints_path(""), @@ -325,6 +329,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 +452,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 +462,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 +641,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,10 +679,13 @@ 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"); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 2fcf26a17..badbaf936 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -1014,7 +1014,6 @@ namespace cryptonote //m_miner and m_miner_addres are probably temporary here miner m_miner; //!< miner instance - account_public_address m_miner_address; //!< address to mine to (for miner instance) std::string m_config_folder; //!< folder to look in for configs and other files diff --git a/src/cryptonote_core/tx_sanity_check.cpp b/src/cryptonote_core/tx_sanity_check.cpp index 5bb723814..e95350f76 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_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index 3083a5b4c..b2f8da399 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -83,6 +83,8 @@ namespace cryptonote uint32_t pruning_seed; + uint8_t address_type; + BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(incoming) KV_SERIALIZE(localhost) @@ -107,6 +109,7 @@ namespace cryptonote KV_SERIALIZE(connection_id) KV_SERIALIZE(height) KV_SERIALIZE(pruning_seed) + KV_SERIALIZE(address_type) END_KV_SERIALIZE_MAP() }; @@ -172,11 +175,8 @@ namespace cryptonote struct request_t { - std::vector<crypto::hash> txs; - std::vector<crypto::hash> blocks; - + std::vector<crypto::hash> blocks; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(txs) KV_SERIALIZE_CONTAINER_POD_AS_BLOB(blocks) END_KV_SERIALIZE_MAP() }; @@ -189,13 +189,11 @@ namespace cryptonote struct request_t { - std::vector<blobdata> txs; std::vector<block_complete_entry> blocks; std::vector<crypto::hash> missed_ids; uint64_t current_blockchain_height; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(txs) KV_SERIALIZE(blocks) KV_SERIALIZE_CONTAINER_POD_AS_BLOB(missed_ids) KV_SERIALIZE(current_blockchain_height) 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..a1fa9484c 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -285,6 +285,7 @@ namespace cryptonote cnx.height = cntxt.m_remote_blockchain_height; cnx.pruning_seed = cntxt.m_pruning_seed; + cnx.address_type = (uint8_t)cntxt.m_remote_address.get_type_id(); connections.push_back(cnx); @@ -341,6 +342,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 +815,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 { @@ -913,7 +934,17 @@ namespace cryptonote template<class t_core> 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)"); + MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_GET_OBJECTS (" << arg.blocks.size() << " blocks)"); + if (arg.blocks.size() > CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT) + { + LOG_ERROR_CCONTEXT( + "Requested objects count is too big (" + << arg.blocks.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)) { @@ -921,8 +952,9 @@ namespace cryptonote drop_connection(context, false, false); return 1; } - MLOG_P2P_MESSAGE("-->>NOTIFY_RESPONSE_GET_OBJECTS: blocks.size()=" << rsp.blocks.size() << ", txs.size()=" << rsp.txs.size() - << ", rsp.m_current_blockchain_height=" << rsp.current_blockchain_height << ", missed_ids.size()=" << rsp.missed_ids.size()); + MLOG_P2P_MESSAGE("-->>NOTIFY_RESPONSE_GET_OBJECTS: blocks.size()=" + << rsp.blocks.size() << ", rsp.m_current_blockchain_height=" << rsp.current_blockchain_height + << ", missed_ids.size()=" << rsp.missed_ids.size()); post_notify<NOTIFY_RESPONSE_GET_OBJECTS>(rsp, context); //handler_response_blocks_now(sizeof(rsp)); // XXX //handler_response_blocks_now(200); @@ -947,7 +979,7 @@ namespace cryptonote template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_response_get_objects(int command, NOTIFY_RESPONSE_GET_OBJECTS::request& arg, cryptonote_connection_context& context) { - MLOG_P2P_MESSAGE("Received NOTIFY_RESPONSE_GET_OBJECTS (" << arg.blocks.size() << " blocks, " << arg.txs.size() << " txes)"); + MLOG_P2P_MESSAGE("Received NOTIFY_RESPONSE_GET_OBJECTS (" << arg.blocks.size() << " blocks)"); MLOG_PEER_STATE("received objects"); boost::posix_time::ptime request_time = context.m_last_request_time; @@ -955,8 +987,6 @@ namespace cryptonote // calculate size of request size_t size = 0; - for (const auto &element : arg.txs) size += element.size(); - size_t blocks_size = 0; for (const auto &element : arg.blocks) { blocks_size += element.block.size(); @@ -1916,7 +1946,7 @@ skip: } context.m_last_request_time = boost::posix_time::microsec_clock::universal_time(); - MLOG_P2P_MESSAGE("-->>NOTIFY_REQUEST_GET_OBJECTS: blocks.size()=" << req.blocks.size() << ", txs.size()=" << req.txs.size() + MLOG_P2P_MESSAGE("-->>NOTIFY_REQUEST_GET_OBJECTS: blocks.size()=" << req.blocks.size() << "requested blocks count=" << count << " / " << count_limit << " from " << span.first << ", first hash " << req.blocks.front()); //epee::net_utils::network_throttle_manager::get_global_throttle_inreq().logger_handle_net("log/dr-monero/net/req-all.data", sec, get_avg_block_size()); diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h index 32fdca5ea..d089d4e47 100644 --- a/src/daemon/command_line_args.h +++ b/src/daemon/command_line_args.h @@ -122,6 +122,11 @@ namespace daemon_args } }; + const command_line::arg_descriptor<bool> arg_zmq_rpc_disabled = { + "no-zmq" + , "Disable ZMQ RPC server" + }; + } // namespace daemon_args #endif // DAEMON_COMMAND_LINE_ARGS_H diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 0b452800e..778d7b4d8 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -494,11 +494,14 @@ bool t_command_parser_executor::set_limit_down(const std::vector<std::string>& a bool t_command_parser_executor::out_peers(const std::vector<std::string>& args) { - if (args.empty()) return false; - - unsigned int limit; + bool set = false; + uint32_t limit = 0; try { - limit = std::stoi(args[0]); + if (!args.empty()) + { + limit = std::stoi(args[0]); + set = true; + } } catch(const std::exception& ex) { @@ -506,16 +509,19 @@ bool t_command_parser_executor::out_peers(const std::vector<std::string>& args) return false; } - return m_executor.out_peers(limit); + return m_executor.out_peers(set, limit); } bool t_command_parser_executor::in_peers(const std::vector<std::string>& args) { - if (args.empty()) return false; - - unsigned int limit; + bool set = false; + uint32_t limit = 0; try { - limit = std::stoi(args[0]); + if (!args.empty()) + { + limit = std::stoi(args[0]); + set = true; + } } catch(const std::exception& ex) { @@ -523,19 +529,7 @@ bool t_command_parser_executor::in_peers(const std::vector<std::string>& args) return false; } - 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(); + return m_executor.in_peers(set, limit); } bool t_command_parser_executor::hard_fork_info(const std::vector<std::string>& args) @@ -596,6 +590,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 2efd78ec0..d39bc1c9b 100644 --- a/src/daemon/command_parser_executor.h +++ b/src/daemon/command_parser_executor.h @@ -115,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); @@ -127,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 f665eec9c..aecdda52c 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -215,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." @@ -243,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/daemon.cpp b/src/daemon/daemon.cpp index 5084b6283..cb96b37b6 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -105,6 +105,7 @@ t_daemon::t_daemon( { zmq_rpc_bind_port = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_bind_port); zmq_rpc_bind_address = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_bind_ip); + zmq_rpc_disabled = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_disabled); } t_daemon::~t_daemon() = default; @@ -171,25 +172,30 @@ bool t_daemon::run(bool interactive) cryptonote::rpc::DaemonHandler rpc_daemon_handler(mp_internals->core.get(), mp_internals->p2p.get()); cryptonote::rpc::ZmqServer zmq_server(rpc_daemon_handler); - if (!zmq_server.addTCPSocket(zmq_rpc_bind_address, zmq_rpc_bind_port)) + if (!zmq_rpc_disabled) { - LOG_ERROR(std::string("Failed to add TCP Socket (") + zmq_rpc_bind_address - + ":" + zmq_rpc_bind_port + ") to ZMQ RPC Server"); + if (!zmq_server.addTCPSocket(zmq_rpc_bind_address, zmq_rpc_bind_port)) + { + LOG_ERROR(std::string("Failed to add TCP Socket (") + zmq_rpc_bind_address + + ":" + zmq_rpc_bind_port + ") to ZMQ RPC Server"); - if (rpc_commands) - rpc_commands->stop_handling(); + if (rpc_commands) + rpc_commands->stop_handling(); - for(auto& rpc : mp_internals->rpcs) - rpc->stop(); + for(auto& rpc : mp_internals->rpcs) + rpc->stop(); - return false; - } + return false; + } - MINFO("Starting ZMQ server..."); - zmq_server.run(); + MINFO("Starting ZMQ server..."); + zmq_server.run(); - MINFO(std::string("ZMQ server started at ") + zmq_rpc_bind_address - + ":" + zmq_rpc_bind_port + "."); + MINFO(std::string("ZMQ server started at ") + zmq_rpc_bind_address + + ":" + zmq_rpc_bind_port + "."); + } + else + MINFO("ZMQ server disabled"); if (public_rpc_port > 0) { @@ -202,7 +208,8 @@ bool t_daemon::run(bool interactive) if (rpc_commands) rpc_commands->stop_handling(); - zmq_server.stop(); + if (!zmq_rpc_disabled) + zmq_server.stop(); for(auto& rpc : mp_internals->rpcs) rpc->stop(); diff --git a/src/daemon/daemon.h b/src/daemon/daemon.h index d44173177..c0efb68ee 100644 --- a/src/daemon/daemon.h +++ b/src/daemon/daemon.h @@ -46,6 +46,7 @@ private: uint16_t public_rpc_port; std::string zmq_rpc_bind_address; std::string zmq_rpc_bind_port; + bool zmq_rpc_disabled; public: t_daemon( boost::program_options::variables_map const & vm, diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index 690d4d60e..461888062 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -141,6 +141,7 @@ int main(int argc, char const * argv[]) command_line::add_arg(core_settings, daemon_args::arg_public_node); command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_ip); command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_port); + command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_disabled); daemonizer::init_options(hidden_options, visible_options); daemonize::t_executor::init_options(core_settings); diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index cca0f75f9..ea986a398 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -46,6 +46,19 @@ namespace daemonize { namespace { + const char *get_address_type_name(epee::net_utils::address_type address_type) + { + switch (address_type) + { + default: + case epee::net_utils::address_type::invalid: return "invalid"; + case epee::net_utils::address_type::ipv4: return "IPv4"; + case epee::net_utils::address_type::ipv6: return "IPv6"; + case epee::net_utils::address_type::i2p: return "I2P"; + case epee::net_utils::address_type::tor: return "Tor"; + } + } + void print_peer(std::string const & prefix, cryptonote::peer const & peer) { time_t now; @@ -54,7 +67,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; @@ -338,6 +351,7 @@ bool t_rpc_command_executor::show_difficulty() { tools::success_msg_writer() << "BH: " << res.height << ", TH: " << res.top_block_hash << ", DIFF: " << res.difficulty + << ", CUM_DIFF: " << res.cumulative_difficulty << ", HR: " << res.difficulty / res.target << " H/s"; return true; @@ -589,6 +603,7 @@ bool t_rpc_command_executor::print_connections() { } tools::msg_writer() << std::setw(30) << std::left << "Remote Host" + << std::setw(8) << "Type" << std::setw(6) << "SSL" << std::setw(20) << "Peer id" << std::setw(20) << "Support Flags" @@ -609,6 +624,7 @@ bool t_rpc_command_executor::print_connections() { tools::msg_writer() //<< std::setw(30) << std::left << in_out << std::setw(30) << std::left << address + << std::setw(8) << (get_address_type_name((epee::net_utils::address_type)info.address_type)) << std::setw(6) << (info.ssl ? "yes" : "no") << std::setw(20) << epee::string_tools::pad_string(info.peer_id, 16, '0', true) << std::setw(20) << info.support_flags @@ -1466,13 +1482,14 @@ bool t_rpc_command_executor::get_limit_down() return true; } -bool t_rpc_command_executor::out_peers(uint64_t limit) +bool t_rpc_command_executor::out_peers(bool set, uint32_t limit) { cryptonote::COMMAND_RPC_OUT_PEERS::request req; cryptonote::COMMAND_RPC_OUT_PEERS::response res; epee::json_rpc::error error_resp; + req.set = set; req.out_peers = limit; std::string fail_message = "Unsuccessful"; @@ -1493,18 +1510,20 @@ bool t_rpc_command_executor::out_peers(uint64_t limit) } } - tools::msg_writer() << "Max number of out peers set to " << limit << std::endl; + const std::string s = res.out_peers == (uint32_t)-1 ? "unlimited" : std::to_string(res.out_peers); + tools::msg_writer() << "Max number of out peers set to " << s << std::endl; return true; } -bool t_rpc_command_executor::in_peers(uint64_t limit) +bool t_rpc_command_executor::in_peers(bool set, uint32_t limit) { cryptonote::COMMAND_RPC_IN_PEERS::request req; cryptonote::COMMAND_RPC_IN_PEERS::response res; epee::json_rpc::error error_resp; + req.set = set; req.in_peers = limit; std::string fail_message = "Unsuccessful"; @@ -1525,61 +1544,9 @@ bool t_rpc_command_executor::in_peers(uint64_t limit) } } - tools::msg_writer() << "Max number of in peers set to " << limit << std::endl; - - 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; -} + const std::string s = res.in_peers == (uint32_t)-1 ? "unlimited" : std::to_string(res.in_peers); + tools::msg_writer() << "Max number of in peers set to " << s << std::endl; -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; } @@ -1641,14 +1608,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; @@ -1656,11 +1623,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); @@ -1684,7 +1648,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; @@ -1692,11 +1656,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); @@ -1720,6 +1681,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 df2894d09..4622609ae 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -125,21 +125,19 @@ public: bool set_limit(int64_t limit_down, int64_t limit_up); - bool out_peers(uint64_t limit); + bool out_peers(bool set, uint32_t limit); - bool in_peers(uint64_t limit); + bool in_peers(bool set, uint32_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.hpp b/src/device/device.hpp index 866e2c676..215e97eb6 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -236,6 +236,7 @@ namespace hw { virtual bool compute_key_image(const cryptonote::account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const cryptonote::subaddress_index& received_index, cryptonote::keypair& in_ephemeral, crypto::key_image& ki) { return false; } virtual void computing_key_images(bool started) {}; virtual void set_network_type(cryptonote::network_type network_type) { } + virtual void display_address(const cryptonote::subaddress_index& index, const boost::optional<crypto::hash8> &payment_id) {} protected: device_mode mode; diff --git a/src/device/device_cold.hpp b/src/device/device_cold.hpp index 31b1504ab..22708c46a 100644 --- a/src/device/device_cold.hpp +++ b/src/device/device_cold.hpp @@ -47,6 +47,7 @@ namespace hw { std::vector<cryptonote::address_parse_info> tx_recipients; // as entered by user boost::optional<int> bp_version; // BP version to use boost::optional<unsigned> client_version; // Signing client version to use (testing) + boost::optional<uint8_t> hard_fork; // hard fork being used for the transaction }; class device_cold { diff --git a/src/device_trezor/device_trezor.cpp b/src/device_trezor/device_trezor.cpp index b1022dd9c..a77f6697f 100644 --- a/src/device_trezor/device_trezor.cpp +++ b/src/device_trezor/device_trezor.cpp @@ -200,6 +200,10 @@ namespace trezor { } } + void device_trezor::display_address(const cryptonote::subaddress_index& index, const boost::optional<crypto::hash8> &payment_id) { + get_address(index, payment_id, true); + } + /* ======================================================================= */ /* Helpers */ /* ======================================================================= */ @@ -209,8 +213,12 @@ namespace trezor { /* ======================================================================= */ std::shared_ptr<messages::monero::MoneroAddress> device_trezor::get_address( + const boost::optional<cryptonote::subaddress_index> & subaddress, + const boost::optional<crypto::hash8> & payment_id, + bool show_address, const boost::optional<std::vector<uint32_t>> & path, const boost::optional<cryptonote::network_type> & network_type){ + CHECK_AND_ASSERT_THROW_MES(!payment_id || !subaddress || subaddress->is_zero(), "Subaddress cannot be integrated"); TREZOR_AUTO_LOCK_CMD(); require_connected(); device_state_reset_unsafe(); @@ -218,6 +226,14 @@ namespace trezor { auto req = std::make_shared<messages::monero::MoneroGetAddress>(); this->set_msg_addr<messages::monero::MoneroGetAddress>(req.get(), path, network_type); + req->set_show_display(show_address); + if (subaddress){ + req->set_account(subaddress->major); + req->set_minor(subaddress->minor); + } + if (payment_id){ + req->set_payment_id(std::string(payment_id->data, 8)); + } auto response = this->client_exchange<messages::monero::MoneroAddress>(req); MTRACE("Get address response received"); diff --git a/src/device_trezor/device_trezor.hpp b/src/device_trezor/device_trezor.hpp index 0e91847dc..a26a42788 100644 --- a/src/device_trezor/device_trezor.hpp +++ b/src/device_trezor/device_trezor.hpp @@ -110,6 +110,7 @@ namespace trezor { /* ======================================================================= */ bool get_public_address(cryptonote::account_public_address &pubkey) override; bool get_secret_keys(crypto::secret_key &viewkey , crypto::secret_key &spendkey) override; + void display_address(const cryptonote::subaddress_index& index, const boost::optional<crypto::hash8> &payment_id) override; /* ======================================================================= */ /* TREZOR PROTOCOL */ @@ -119,6 +120,9 @@ namespace trezor { * Get address. Throws. */ std::shared_ptr<messages::monero::MoneroAddress> get_address( + const boost::optional<cryptonote::subaddress_index> & subaddress = boost::none, + const boost::optional<crypto::hash8> & payment_id = boost::none, + bool show_address = false, const boost::optional<std::vector<uint32_t>> & path = boost::none, const boost::optional<cryptonote::network_type> & network_type = boost::none); diff --git a/src/device_trezor/trezor/protocol.cpp b/src/device_trezor/trezor/protocol.cpp index 5fe08abbe..61e51be14 100644 --- a/src/device_trezor/trezor/protocol.cpp +++ b/src/device_trezor/trezor/protocol.cpp @@ -27,6 +27,7 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // +#include "version.h" #include "protocol.hpp" #include <unordered_map> #include <set> @@ -502,6 +503,8 @@ namespace tx { tsx_data.set_num_inputs(static_cast<google::protobuf::uint32>(tx.sources.size())); tsx_data.set_mixin(static_cast<google::protobuf::uint32>(tx.sources[0].outputs.size() - 1)); tsx_data.set_account(tx.subaddr_account); + tsx_data.set_monero_version(std::string(MONERO_VERSION) + "|" + MONERO_VERSION_TAG); + tsx_data.set_hard_fork(m_aux_data->hard_fork ? m_aux_data->hard_fork.get() : 0); assign_to_repeatable(tsx_data.mutable_minor_indices(), tx.subaddr_indices.begin(), tx.subaddr_indices.end()); // Rsig decision 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..a44297c17 100644 --- a/src/p2p/net_node.cpp +++ b/src/p2p/net_node.cpp @@ -135,6 +135,7 @@ namespace nodetool const command_line::arg_descriptor<bool> arg_no_sync = {"no-sync", "Don't synchronize the blockchain with other peers", false}; const command_line::arg_descriptor<bool> arg_no_igd = {"no-igd", "Disable UPnP port mapping"}; + const command_line::arg_descriptor<std::string> arg_igd = {"igd", "UPnP port mapping (disabled, enabled, delayed)", "delayed"}; const command_line::arg_descriptor<int64_t> arg_out_peers = {"out-peers", "set max number of out peers", -1}; const command_line::arg_descriptor<int64_t> arg_in_peers = {"in-peers", "set max number of in peers", -1}; const command_line::arg_descriptor<int> arg_tos_flag = {"tos-flag", "set TOS flag", -1}; @@ -143,8 +144,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..0d95ceb99 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -205,6 +205,13 @@ namespace nodetool } }; + enum igd_t + { + no_igd, + igd, + delayed_igd, + }; + public: typedef t_payload_net_handler payload_net_handler; @@ -214,9 +221,8 @@ namespace nodetool m_rpc_port(0), m_allow_local_ip(false), m_hide_my_port(false), - m_no_igd(false), + m_igd(no_igd), m_offline(false), - m_save_graph(false), is_closing(false), m_network_id() {} @@ -245,10 +251,16 @@ namespace nodetool size_t get_zone_count() const { return m_network_zones.size(); } void change_max_out_public_peers(size_t count); + uint32_t get_max_out_public_peers() const; void change_max_in_public_peers(size_t count); + uint32_t get_max_in_public_peers() const; virtual bool block_host(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 +331,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 +408,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; @@ -417,9 +423,8 @@ namespace nodetool uint16_t m_rpc_port; bool m_allow_local_ip; bool m_hide_my_port; - bool m_no_igd; + igd_t m_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 +466,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; @@ -492,6 +498,7 @@ namespace nodetool extern const command_line::arg_descriptor<bool> arg_no_sync; extern const command_line::arg_descriptor<bool> arg_no_igd; + extern const command_line::arg_descriptor<std::string> arg_igd; extern const command_line::arg_descriptor<bool> arg_offline; extern const command_line::arg_descriptor<int64_t> arg_out_peers; extern const command_line::arg_descriptor<int64_t> arg_in_peers; @@ -500,8 +507,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..98484fe78 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -105,13 +105,13 @@ namespace nodetool command_line::add_arg(desc, arg_p2p_hide_my_port); command_line::add_arg(desc, arg_no_sync); command_line::add_arg(desc, arg_no_igd); + command_line::add_arg(desc, arg_igd); command_line::add_arg(desc, arg_out_peers); command_line::add_arg(desc, arg_in_peers); command_line::add_arg(desc, arg_tos_flag); 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 +155,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 +220,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 +250,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 +259,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()) @@ -257,7 +345,35 @@ namespace nodetool public_zone.m_can_pingback = true; m_external_port = command_line::get_arg(vm, arg_p2p_external_port); m_allow_local_ip = command_line::get_arg(vm, arg_p2p_allow_local_ip); - m_no_igd = command_line::get_arg(vm, arg_no_igd); + const bool has_no_igd = command_line::get_arg(vm, arg_no_igd); + const std::string sigd = command_line::get_arg(vm, arg_igd); + if (sigd == "enabled") + { + if (has_no_igd) + { + MFATAL("Cannot have both --" << arg_no_igd.name << " and --" << arg_igd.name << " enabled"); + return false; + } + m_igd = igd; + } + else if (sigd == "disabled") + { + m_igd = no_igd; + } + else if (sigd == "delayed") + { + if (has_no_igd && !command_line::is_arg_defaulted(vm, arg_igd)) + { + MFATAL("Cannot have both --" << arg_no_igd.name << " and --" << arg_igd.name << " delayed"); + return false; + } + m_igd = has_no_igd ? no_igd : delayed_igd; + } + else + { + MFATAL("Invalid value for --" << arg_igd.name << ", expected enabled, disabled or delayed"); + return false; + } m_offline = command_line::get_arg(vm, cryptonote::arg_offline); if (command_line::has_arg(vm, arg_p2p_add_peer)) @@ -292,11 +408,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)) @@ -682,7 +793,7 @@ namespace nodetool MDEBUG("External port defined as " << m_external_port); // add UPnP port mapping - if(!m_no_igd) + if(m_igd == igd) add_upnp_port_mapping(m_listening_port); return res; @@ -776,7 +887,7 @@ namespace nodetool for(auto& zone : m_network_zones) zone.second.m_net_server.deinit_server(); // remove UPnP port mapping - if(!m_no_igd) + if(m_igd == igd) delete_upnp_port_mapping(m_listening_port); } return store_config(); @@ -944,7 +1055,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 +1204,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 +1264,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 +1341,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); @@ -1581,8 +1730,17 @@ namespace nodetool } else { - const el::Level level = el::Level::Warning; - MCLOG_RED(level, "global", "No incoming connections - check firewalls/routers allow port " << get_this_peer_port()); + if (m_igd == delayed_igd) + { + MWARNING("No incoming connections, trying to setup IGD"); + add_upnp_port_mapping(m_listening_port); + m_igd = igd; + } + else + { + const el::Level level = el::Level::Warning; + MCLOG_RED(level, "global", "No incoming connections - check firewalls/routers allow port " << get_this_peer_port()); + } } } return true; @@ -2217,6 +2375,15 @@ namespace nodetool } template<class t_payload_net_handler> + uint32_t node_server<t_payload_net_handler>::get_max_out_public_peers() const + { + const auto public_zone = m_network_zones.find(epee::net_utils::zone::public_); + if (public_zone == m_network_zones.end()) + return 0; + return public_zone->second.m_config.m_net_config.max_out_connection_count; + } + + template<class t_payload_net_handler> void node_server<t_payload_net_handler>::change_max_in_public_peers(size_t count) { auto public_zone = m_network_zones.find(epee::net_utils::zone::public_); @@ -2230,6 +2397,15 @@ namespace nodetool } template<class t_payload_net_handler> + uint32_t node_server<t_payload_net_handler>::get_max_in_public_peers() const + { + const auto public_zone = m_network_zones.find(epee::net_utils::zone::public_); + if (public_zone == m_network_zones.end()) + return 0; + return public_zone->second.m_config.m_net_config.max_in_connection_count; + } + + template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::set_tos_flag(const boost::program_options::variables_map& vm, int flag) { if(flag==-1){ 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/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 28c53d6e3..d80219f1c 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) @@ -103,6 +105,35 @@ namespace cryptonote , m_p2p(p2p) {} //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::set_bootstrap_daemon(const std::string &address, const std::string &username_password) + { + boost::optional<epee::net_utils::http::login> credentials; + const auto loc = username_password.find(':'); + if (loc != std::string::npos) + { + credentials = epee::net_utils::http::login(username_password.substr(0, loc), username_password.substr(loc + 1)); + } + return set_bootstrap_daemon(address, credentials); + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::set_bootstrap_daemon(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials) + { + boost::unique_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex); + + if (!address.empty()) + { + if (!m_http_client.set_server(address, credentials, epee::net_utils::ssl_support_t::e_ssl_support_autodetect)) + { + return false; + } + } + + m_bootstrap_daemon_address = address; + m_should_use_bootstrap_daemon = !m_bootstrap_daemon_address.empty(); + + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::init( const boost::program_options::variables_map& vm , const bool restricted @@ -116,29 +147,12 @@ namespace cryptonote if (!rpc_config) return false; - m_bootstrap_daemon_address = command_line::get_arg(vm, arg_bootstrap_daemon_address); - if (!m_bootstrap_daemon_address.empty()) + if (!set_bootstrap_daemon(command_line::get_arg(vm, arg_bootstrap_daemon_address), + command_line::get_arg(vm, arg_bootstrap_daemon_login))) { - const std::string &bootstrap_daemon_login = command_line::get_arg(vm, arg_bootstrap_daemon_login); - const auto loc = bootstrap_daemon_login.find(':'); - if (!bootstrap_daemon_login.empty() && loc != std::string::npos) - { - epee::net_utils::http::login login; - login.username = bootstrap_daemon_login.substr(0, loc); - login.password = bootstrap_daemon_login.substr(loc + 1); - m_http_client.set_server(m_bootstrap_daemon_address, login, epee::net_utils::ssl_support_t::e_ssl_support_autodetect); - } - else - { - m_http_client.set_server(m_bootstrap_daemon_address, boost::none, epee::net_utils::ssl_support_t::e_ssl_support_autodetect); - } - m_should_use_bootstrap_daemon = true; - } - else - { - m_should_use_bootstrap_daemon = false; + MERROR("Failed to parse bootstrap daemon address"); + return false; } - m_was_bootstrap_ever_used = false; boost::optional<epee::net_utils::http::login> http_login{}; @@ -183,7 +197,10 @@ namespace cryptonote bool r; if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_INFO>(invoke_http_mode::JON, "/getinfo", req, res, r)) { - res.bootstrap_daemon_address = m_bootstrap_daemon_address; + { + boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex); + res.bootstrap_daemon_address = m_bootstrap_daemon_address; + } crypto::hash top_hash; m_core.get_blockchain_top(res.height_without_bootstrap, top_hash); ++res.height_without_bootstrap; // turn top block height into blockchain height @@ -222,13 +239,16 @@ namespace cryptonote res.start_time = restricted ? 0 : (uint64_t)m_core.get_start_time(); res.free_space = restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space(); res.offline = m_core.offline(); - res.bootstrap_daemon_address = restricted ? "" : m_bootstrap_daemon_address; res.height_without_bootstrap = restricted ? 0 : res.height; if (restricted) + { + res.bootstrap_daemon_address = ""; res.was_bootstrap_ever_used = false; + } else { boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex); + res.bootstrap_daemon_address = m_bootstrap_daemon_address; res.was_bootstrap_ever_used = m_was_bootstrap_ever_used; } res.database_size = m_core.get_blockchain_storage().get_db().get_database_size(); @@ -818,6 +838,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); @@ -1122,6 +1143,28 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_set_bootstrap_daemon(const COMMAND_RPC_SET_BOOTSTRAP_DAEMON::request& req, COMMAND_RPC_SET_BOOTSTRAP_DAEMON::response& res, const connection_context *ctx) + { + PERF_TIMER(on_set_bootstrap_daemon); + + boost::optional<epee::net_utils::http::login> credentials; + if (!req.username.empty() || !req.password.empty()) + { + credentials = epee::net_utils::http::login(req.username, req.password); + } + + if (set_bootstrap_daemon(req.address, credentials)) + { + res.status = CORE_RPC_STATUS_OK; + } + else + { + res.status = "Failed to set bootstrap daemon"; + } + + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res, const connection_context *ctx) { PERF_TIMER(on_stop_daemon); @@ -1446,10 +1489,12 @@ namespace cryptonote bool core_rpc_server::use_bootstrap_daemon_if_necessary(const invoke_http_mode &mode, const std::string &command_name, const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res, bool &r) { res.untrusted = false; + + boost::upgrade_lock<boost::shared_mutex> upgrade_lock(m_bootstrap_daemon_mutex); + if (m_bootstrap_daemon_address.empty()) return false; - boost::unique_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex); if (!m_should_use_bootstrap_daemon) { MINFO("The local daemon is fully synced. Not switching back to the bootstrap daemon"); @@ -1459,7 +1504,10 @@ namespace cryptonote auto current_time = std::chrono::system_clock::now(); if (current_time - m_bootstrap_height_check_time > std::chrono::seconds(30)) // update every 30s { - m_bootstrap_height_check_time = current_time; + { + boost::upgrade_to_unique_lock<boost::shared_mutex> lock(upgrade_lock); + m_bootstrap_height_check_time = current_time; + } uint64_t top_height; crypto::hash top_hash; @@ -1473,7 +1521,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; @@ -1503,7 +1551,12 @@ namespace cryptonote MERROR("Unknown invoke_http_mode: " << mode); return false; } - m_was_bootstrap_ever_used = true; + + { + boost::upgrade_to_unique_lock<boost::shared_mutex> lock(upgrade_lock); + m_was_bootstrap_ever_used = true; + } + r = r && res.status == CORE_RPC_STATUS_OK; res.untrusted = true; return true; @@ -1769,20 +1822,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; @@ -1795,13 +1888,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); @@ -1882,6 +1991,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 { @@ -2029,7 +2145,9 @@ namespace cryptonote bool core_rpc_server::on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res, const connection_context *ctx) { PERF_TIMER(on_out_peers); - m_p2p.change_max_out_public_peers(req.out_peers); + if (req.set) + m_p2p.change_max_out_public_peers(req.out_peers); + res.out_peers = m_p2p.get_max_out_public_peers(); res.status = CORE_RPC_STATUS_OK; return true; } @@ -2037,27 +2155,13 @@ namespace cryptonote bool core_rpc_server::on_in_peers(const COMMAND_RPC_IN_PEERS::request& req, COMMAND_RPC_IN_PEERS::response& res, const connection_context *ctx) { PERF_TIMER(on_in_peers); - m_p2p.change_max_in_public_peers(req.in_peers); + if (req.set) + m_p2p.change_max_in_public_peers(req.in_peers); + res.in_peers = m_p2p.get_max_in_public_peers(); res.status = CORE_RPC_STATUS_OK; return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res, const connection_context *ctx) - { - PERF_TIMER(on_start_save_graph); - m_p2p.set_save_graph(true); - res.status = CORE_RPC_STATUS_OK; - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res, const connection_context *ctx) - { - PERF_TIMER(on_stop_save_graph); - m_p2p.set_save_graph(false); - res.status = CORE_RPC_STATUS_OK; - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res, const connection_context *ctx) { PERF_TIMER(on_update); diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index e4683bbe2..663975617 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -115,6 +115,7 @@ namespace cryptonote MAP_URI_AUTO_JON2("/get_transaction_pool_hashes.bin", on_get_transaction_pool_hashes_bin, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN) MAP_URI_AUTO_JON2("/get_transaction_pool_hashes", on_get_transaction_pool_hashes, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES) MAP_URI_AUTO_JON2("/get_transaction_pool_stats", on_get_transaction_pool_stats, COMMAND_RPC_GET_TRANSACTION_POOL_STATS) + MAP_URI_AUTO_JON2_IF("/set_bootstrap_daemon", on_set_bootstrap_daemon, COMMAND_RPC_SET_BOOTSTRAP_DAEMON, !m_restricted) MAP_URI_AUTO_JON2_IF("/stop_daemon", on_stop_daemon, COMMAND_RPC_STOP_DAEMON, !m_restricted) MAP_URI_AUTO_JON2("/get_info", on_get_info, COMMAND_RPC_GET_INFO) MAP_URI_AUTO_JON2("/getinfo", on_get_info, COMMAND_RPC_GET_INFO) @@ -123,8 +124,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 +153,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) @@ -193,13 +193,12 @@ namespace cryptonote bool on_get_transaction_pool_hashes_bin(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::response& res, const connection_context *ctx = NULL); bool on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res, const connection_context *ctx = NULL); bool on_get_transaction_pool_stats(const COMMAND_RPC_GET_TRANSACTION_POOL_STATS::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_STATS::response& res, const connection_context *ctx = NULL); + bool on_set_bootstrap_daemon(const COMMAND_RPC_SET_BOOTSTRAP_DAEMON::request& req, COMMAND_RPC_SET_BOOTSTRAP_DAEMON::response& res, const connection_context *ctx = NULL); bool on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res, const connection_context *ctx = NULL); bool on_get_limit(const COMMAND_RPC_GET_LIMIT::request& req, COMMAND_RPC_GET_LIMIT::response& res, const connection_context *ctx = NULL); bool on_set_limit(const COMMAND_RPC_SET_LIMIT::request& req, COMMAND_RPC_SET_LIMIT::response& res, const connection_context *ctx = NULL); bool on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res, const connection_context *ctx = NULL); bool on_in_peers(const COMMAND_RPC_IN_PEERS::request& req, COMMAND_RPC_IN_PEERS::response& res, const connection_context *ctx = NULL); - bool on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res, const connection_context *ctx = NULL); - bool on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res, const connection_context *ctx = NULL); bool on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res, const connection_context *ctx = NULL); bool on_get_output_distribution_bin(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res, const connection_context *ctx = NULL); bool on_pop_blocks(const COMMAND_RPC_POP_BLOCKS::request& req, COMMAND_RPC_POP_BLOCKS::response& res, const connection_context *ctx = NULL); @@ -220,6 +219,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); @@ -240,6 +240,8 @@ private: //utils uint64_t get_block_reward(const block& blk); bool fill_block_header_response(const block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_response& response, bool fill_pow_hash); + bool set_bootstrap_daemon(const std::string &address, const std::string &username_password); + bool set_bootstrap_daemon(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials); enum invoke_http_mode { JON, BIN, JON_RPC }; template <typename COMMAND_TYPE> bool use_bootstrap_daemon_if_necessary(const invoke_http_mode &mode, const std::string &command_name, const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res, bool &r); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index cfe4bbf23..571a71207 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -29,6 +29,9 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #pragma once + +#include "string_tools.h" + #include "cryptonote_protocol/cryptonote_protocol_defs.h" #include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/difficulty.h" @@ -84,7 +87,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 2 -#define CORE_RPC_VERSION_MINOR 6 +#define CORE_RPC_VERSION_MINOR 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) @@ -1198,7 +1201,7 @@ namespace cryptonote : id(id), host(host), ip(0), port(0), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed) {} peer(uint64_t id, uint32_t ip, uint16_t port, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port) - : id(id), host(std::to_string(ip)), ip(ip), port(port), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed) + : id(id), host(epee::string_tools::get_ip_string_from_int32(ip)), ip(ip), port(port), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed) {} BEGIN_KV_SERIALIZE_MAP() @@ -1583,6 +1586,33 @@ namespace cryptonote typedef epee::misc_utils::struct_init<response_t> response; }; + struct COMMAND_RPC_SET_BOOTSTRAP_DAEMON + { + struct request_t + { + std::string address; + std::string username; + std::string password; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(address) + KV_SERIALIZE(username) + KV_SERIALIZE(password) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct response_t + { + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + struct COMMAND_RPC_STOP_DAEMON { struct request_t @@ -1682,8 +1712,10 @@ namespace cryptonote { struct request_t { - uint64_t out_peers; + bool set; + uint32_t out_peers; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_OPT(set, true) KV_SERIALIZE(out_peers) END_KV_SERIALIZE_MAP() }; @@ -1691,9 +1723,11 @@ namespace cryptonote struct response_t { + uint32_t out_peers; std::string status; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(out_peers) KV_SERIALIZE(status) END_KV_SERIALIZE_MAP() }; @@ -1704,8 +1738,10 @@ namespace cryptonote { struct request_t { - uint64_t in_peers; + bool set; + uint32_t in_peers; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_OPT(set, true) KV_SERIALIZE(in_peers) END_KV_SERIALIZE_MAP() }; @@ -1713,55 +1749,17 @@ namespace cryptonote struct response_t { + uint32_t in_peers; std::string status; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(in_peers) KV_SERIALIZE(status) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; }; - struct COMMAND_RPC_START_SAVE_GRAPH - { - struct request_t - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<request_t> request; - - struct response_t - { - std::string status; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<response_t> response; - }; - - struct COMMAND_RPC_STOP_SAVE_GRAPH - { - struct request_t - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<request_t> request; - - struct response_t - { - std::string status; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; - typedef epee::misc_utils::struct_init<response_t> response; - }; - struct COMMAND_RPC_HARD_FORK_INFO { struct request_t @@ -1876,6 +1874,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/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/json_object.cpp b/src/serialization/json_object.cpp index 73e17a775..cc52bde58 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -566,6 +566,7 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::connection_info& in INSERT_INTO_JSON_OBJECT(val, doc, incoming, info.incoming); INSERT_INTO_JSON_OBJECT(val, doc, localhost, info.localhost); INSERT_INTO_JSON_OBJECT(val, doc, local_ip, info.local_ip); + INSERT_INTO_JSON_OBJECT(val, doc, address_type, info.address_type); INSERT_INTO_JSON_OBJECT(val, doc, ip, info.ip); INSERT_INTO_JSON_OBJECT(val, doc, port, info.port); @@ -601,6 +602,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::connection_info& inf GET_FROM_JSON_OBJECT(val, info.incoming, incoming); GET_FROM_JSON_OBJECT(val, info.localhost, localhost); GET_FROM_JSON_OBJECT(val, info.local_ip, local_ip); + GET_FROM_JSON_OBJECT(val, info.address_type, address_type); GET_FROM_JSON_OBJECT(val, info.ip, ip); GET_FROM_JSON_OBJECT(val, info.port, port); 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..fca14c8f8 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); \ @@ -177,8 +179,8 @@ namespace " account tag <tag_name> <account_index_1> [<account_index_2> ...]\n" " account untag <account_index_1> [<account_index_2> ...]\n" " account tag_description <tag_name> <description>"); - const char* USAGE_ADDRESS("address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed>]"); - const char* USAGE_INTEGRATED_ADDRESS("integrated_address [<payment_id> | <address>]"); + const char* USAGE_ADDRESS("address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed> | device [<index>]]"); + const char* USAGE_INTEGRATED_ADDRESS("integrated_address [device] [<payment_id> | <address>]"); const char* USAGE_ADDRESS_BOOK("address_book [(add ((<address> [pid <id>])|<integrated address>) [<description possibly with whitespaces>])|(delete <index>)]"); const char* USAGE_SET_VARIABLE("set <option> [<value>]"); const char* USAGE_GET_TX_KEY("get_tx_key <txid>"); @@ -4237,7 +4239,9 @@ boost::optional<tools::password_container> simple_wallet::get_and_verify_passwor boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm, const crypto::secret_key& recovery_key, bool recover, bool two_random, const std::string &old_language) { - auto rc = tools::wallet2::make_new(vm, false, password_prompter); + std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> rc; + try { rc = tools::wallet2::make_new(vm, false, password_prompter); } + catch(const std::exception &e) { fail_msg_writer() << tr("Error creating wallet: ") << e.what(); return {}; } m_wallet = std::move(rc.first); if (!m_wallet) { @@ -4332,7 +4336,9 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr const cryptonote::account_public_address& address, const boost::optional<crypto::secret_key>& spendkey, const crypto::secret_key& viewkey) { - auto rc = tools::wallet2::make_new(vm, false, password_prompter); + std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> rc; + try { rc = tools::wallet2::make_new(vm, false, password_prompter); } + catch(const std::exception &e) { fail_msg_writer() << tr("Error creating wallet: ") << e.what(); return {}; } m_wallet = std::move(rc.first); if (!m_wallet) { @@ -4378,7 +4384,9 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr //---------------------------------------------------------------------------------------------------- boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm) { - auto rc = tools::wallet2::make_new(vm, false, password_prompter); + std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> rc; + try { rc = tools::wallet2::make_new(vm, false, password_prompter); } + catch(const std::exception &e) { fail_msg_writer() << tr("Error creating wallet: ") << e.what(); return {}; } m_wallet = std::move(rc.first); m_wallet->callback(this); if (!m_wallet) @@ -4419,7 +4427,9 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm, const epee::wipeable_string &multisig_keys, const std::string &old_language) { - auto rc = tools::wallet2::make_new(vm, false, password_prompter); + std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> rc; + try { rc = tools::wallet2::make_new(vm, false, password_prompter); } + catch(const std::exception &e) { fail_msg_writer() << tr("Error creating wallet: ") << e.what(); return {}; } m_wallet = std::move(rc.first); if (!m_wallet) { @@ -5608,6 +5618,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 +5956,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 +6145,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 +6466,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 +6818,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 +6835,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_) @@ -8501,6 +8563,7 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std:: // address all // address <index_min> [<index_max>] // address label <index> <label text with white spaces allowed> + // address device [<index>] std::vector<std::string> local_args = args; tools::wallet2::transfer_container transfers; @@ -8537,6 +8600,7 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std:: label = tr("(Untitled address)"); m_wallet->add_subaddress(m_current_subaddress_account, label); print_address_sub(m_wallet->get_num_subaddresses(m_current_subaddress_account) - 1); + m_wallet->device_show_address(m_current_subaddress_account, m_wallet->get_num_subaddresses(m_current_subaddress_account) - 1, boost::none); } else if (local_args.size() >= 2 && local_args[0] == "label") { @@ -8585,6 +8649,27 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std:: for (index = index_min; index <= index_max; ++index) print_address_sub(index); } + else if (local_args[0] == "device") + { + index = 0; + local_args.erase(local_args.begin()); + if (local_args.size() > 0) + { + if (!epee::string_tools::get_xtype_from_string(index, local_args[0])) + { + fail_msg_writer() << tr("failed to parse index: ") << local_args[0]; + return true; + } + if (index >= m_wallet->get_num_subaddresses(m_current_subaddress_account)) + { + fail_msg_writer() << tr("<index> is out of bounds"); + return true; + } + } + + print_address_sub(index); + m_wallet->device_show_address(m_current_subaddress_account, index, boost::none); + } else { PRINT_USAGE(USAGE_ADDRESS); @@ -8596,12 +8681,29 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std:: bool simple_wallet::print_integrated_address(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { crypto::hash8 payment_id; - if (args.size() > 1) + bool display_on_device = false; + std::vector<std::string> local_args = args; + + if (local_args.size() > 0 && local_args[0] == "device") + { + local_args.erase(local_args.begin()); + display_on_device = true; + } + + auto device_show_integrated = [this, display_on_device](crypto::hash8 payment_id) + { + if (display_on_device) + { + m_wallet->device_show_address(m_current_subaddress_account, 0, payment_id); + } + }; + + if (local_args.size() > 1) { PRINT_USAGE(USAGE_INTEGRATED_ADDRESS); return true; } - if (args.size() == 0) + if (local_args.size() == 0) { if (m_current_subaddress_account != 0) { @@ -8611,9 +8713,10 @@ bool simple_wallet::print_integrated_address(const std::vector<std::string> &arg payment_id = crypto::rand<crypto::hash8>(); success_msg_writer() << tr("Random payment ID: ") << payment_id; success_msg_writer() << tr("Matching integrated address: ") << m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->nettype()); + device_show_integrated(payment_id); return true; } - if(tools::wallet2::parse_short_payment_id(args.back(), payment_id)) + if(tools::wallet2::parse_short_payment_id(local_args.back(), payment_id)) { if (m_current_subaddress_account != 0) { @@ -8621,16 +8724,18 @@ bool simple_wallet::print_integrated_address(const std::vector<std::string> &arg return true; } success_msg_writer() << m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->nettype()); + device_show_integrated(payment_id); return true; } else { address_parse_info info; - if(get_account_address_from_str(info, m_wallet->nettype(), args.back())) + if(get_account_address_from_str(info, m_wallet->nettype(), local_args.back())) { if (info.has_payment_id) { success_msg_writer() << boost::format(tr("Integrated address: %s, payment ID: %s")) % get_account_address_as_str(m_wallet->nettype(), false, info.address) % epee::string_tools::pod_to_hex(info.payment_id); + device_show_integrated(info.payment_id); } else { 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..03db385a4 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; @@ -2419,6 +2430,23 @@ uint64_t WalletImpl::coldKeyImageSync(uint64_t &spent, uint64_t &unspent) { return m_wallet->cold_key_image_sync(spent, unspent); } + +void WalletImpl::deviceShowAddress(uint32_t accountIndex, uint32_t addressIndex, const std::string &paymentId) +{ + boost::optional<crypto::hash8> payment_id_param = boost::none; + if (!paymentId.empty()) + { + crypto::hash8 payment_id; + bool res = tools::wallet2::parse_short_payment_id(paymentId, payment_id); + if (!res) + { + throw runtime_error("Invalid payment ID"); + } + payment_id_param = payment_id; + } + + m_wallet->device_show_address(accountIndex, addressIndex, payment_id_param); +} } // namespace namespace Bitmonero = Monero; diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 9e07b6e19..a367a1917 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; @@ -201,6 +205,7 @@ public: virtual bool unlockKeysFile() override; virtual bool isKeysFileLocked() override; virtual uint64_t coldKeyImageSync(uint64_t &spent, uint64_t &unspent) override; + virtual void deviceShowAddress(uint32_t accountIndex, uint32_t addressIndex, const std::string &paymentId) override; private: void clearStatus() const; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 0af3b1867..9e556cb2f 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 @@ -1003,6 +1016,9 @@ struct Wallet //! cold-device protocol key image sync virtual uint64_t coldKeyImageSync(uint64_t &spent, uint64_t &unspent) = 0; + + //! shows address on device display + virtual void deviceShowAddress(uint32_t accountIndex, uint32_t addressIndex, const std::string &paymentId) = 0; }; /** diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index ef2ed2015..d589dcc75 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -227,9 +227,6 @@ std::string WalletManagerImpl::errorString() const void WalletManagerImpl::setDaemonAddress(const std::string &address) { - m_daemonAddress = address; - if(m_http_client.is_connected()) - m_http_client.disconnect(); m_http_client.set_server(address, boost::none); } diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h index 235f96e17..537fc5ba6 100644 --- a/src/wallet/api/wallet_manager.h +++ b/src/wallet/api/wallet_manager.h @@ -95,7 +95,6 @@ public: private: WalletManagerImpl() {} friend struct WalletManagerFactory; - std::string m_daemonAddress; epee::net_utils::http::http_simple_client m_http_client; std::string m_errorString; }; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 8f3f30da1..f25e9ad97 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1140,7 +1140,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_devices_registered(false), m_device_last_key_image_sync(0), m_use_dns(true), - m_offline(false) + m_offline(false), + m_rpc_version(0) { } @@ -5157,6 +5158,7 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout) if (m_offline) { + m_rpc_version = 0; if (version) *version = 0; if (ssl) @@ -5166,6 +5168,7 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout) // TODO: Add light wallet version check. if(m_light_wallet) { + m_rpc_version = 0; if (version) *version = 0; if (ssl) @@ -5177,6 +5180,7 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout) boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex); if(!m_http_client.is_connected(ssl)) { + m_rpc_version = 0; m_node_rpc_proxy.invalidate(); if (!m_http_client.connect(std::chrono::milliseconds(timeout))) return false; @@ -5185,20 +5189,21 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout) } } - if (version) + if (!m_rpc_version) { cryptonote::COMMAND_RPC_GET_VERSION::request req_t = AUTO_VAL_INIT(req_t); cryptonote::COMMAND_RPC_GET_VERSION::response resp_t = AUTO_VAL_INIT(resp_t); bool r = invoke_http_json_rpc("/json_rpc", "get_version", req_t, resp_t); if(!r) { - *version = 0; + if(version) + *version = 0; return false; } - if (resp_t.status != CORE_RPC_STATUS_OK) - *version = 0; - else - *version = resp_t.version; + if (resp_t.status == CORE_RPC_STATUS_OK) + m_rpc_version = resp_t.version; } + if (version) + *version = m_rpc_version; return true; } @@ -6931,7 +6936,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; @@ -6962,7 +6967,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; } @@ -7726,7 +7731,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; @@ -7738,7 +7742,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); @@ -7984,7 +7987,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; @@ -10051,6 +10053,7 @@ void wallet2::cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_ setup_shim(&wallet_shim, this); aux_data.tx_recipients = dsts_info; aux_data.bp_version = use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1; + aux_data.hard_fork = get_current_hard_fork(); dev_cold->tx_sign(&wallet_shim, txs, exported_txs, aux_data); tx_device_aux = aux_data.tx_device_aux; @@ -10078,6 +10081,35 @@ uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) { return import_res; } //---------------------------------------------------------------------------------------------------- +void wallet2::device_show_address(uint32_t account_index, uint32_t address_index, const boost::optional<crypto::hash8> &payment_id) +{ + if (!key_on_device()) + { + return; + } + + auto & hwdev = get_account().get_device(); + hwdev.display_address(subaddress_index{account_index, address_index}, payment_id); +} +//---------------------------------------------------------------------------------------------------- +uint8_t wallet2::get_current_hard_fork() +{ + if (m_offline) + return 0; + + cryptonote::COMMAND_RPC_HARD_FORK_INFO::request req_t = AUTO_VAL_INIT(req_t); + cryptonote::COMMAND_RPC_HARD_FORK_INFO::response resp_t = AUTO_VAL_INIT(resp_t); + + m_daemon_rpc_mutex.lock(); + req_t.version = 0; + bool r = net_utils::invoke_http_json_rpc("/json_rpc", "hard_fork_info", req_t, resp_t, m_http_client, rpc_timeout); + m_daemon_rpc_mutex.unlock(); + THROW_WALLET_EXCEPTION_IF(!r, tools::error::no_connection_to_daemon, "hard_fork_info"); + THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, tools::error::daemon_busy, "hard_fork_info"); + THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, tools::error::wallet_generic_rpc_error, "hard_fork_info", m_trusted_daemon ? resp_t.status : "daemon error"); + return resp_t.version; +} +//---------------------------------------------------------------------------------------------------- void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const { boost::optional<std::string> result = m_node_rpc_proxy.get_earliest_height(version, earliest_height); @@ -11387,12 +11419,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) @@ -11402,7 +11435,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..a6d042297 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -843,6 +843,7 @@ private: void cold_tx_aux_import(const std::vector<pending_tx>& ptx, const std::vector<std::string>& tx_device_aux); void cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::vector<std::string> & tx_device_aux); uint64_t cold_key_image_sync(uint64_t &spent, uint64_t &unspent); + void device_show_address(uint32_t account_index, uint32_t address_index, const boost::optional<crypto::hash8> &payment_id); bool parse_multisig_tx_from_str(std::string multisig_tx_st, multisig_tx_set &exported_txs) const; bool load_multisig_tx(cryptonote::blobdata blob, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL); bool load_multisig_tx_from_file(const std::string &filename, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL); @@ -1094,6 +1095,7 @@ private: size_t get_num_transfer_details() const { return m_transfers.size(); } const transfer_details &get_transfer_details(size_t idx) const; + uint8_t get_current_hard_fork(); void get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const; bool use_fork_rules(uint8_t version, int64_t early_blocks = 0) const; int get_fee_algorithm() const; @@ -1247,7 +1249,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; @@ -1504,6 +1506,7 @@ private: uint64_t m_device_last_key_image_sync; bool m_use_dns; bool m_offline; + uint32_t m_rpc_version; // Aux transaction data from device std::unordered_map<crypto::hash, std::string> m_tx_device; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 8b8d832dc..9d3605d11 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1745,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 { @@ -1806,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; @@ -2099,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) 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/difficulty/gen_wide_data.py b/tests/difficulty/gen_wide_data.py index 64af4e208..64af4e208 100644..100755 --- a/tests/difficulty/gen_wide_data.py +++ b/tests/difficulty/gen_wide_data.py diff --git a/tests/functional_tests/blockchain.py b/tests/functional_tests/blockchain.py index 644597584..2bd7672c0 100755 --- a/tests/functional_tests/blockchain.py +++ b/tests/functional_tests/blockchain.py @@ -74,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 @@ -92,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 @@ -111,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 @@ -157,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 diff --git a/tests/functional_tests/transfer.py b/tests/functional_tests/transfer.py index 7bf0f4957..d9a6e592e 100755 --- a/tests/functional_tests/transfer.py +++ b/tests/functional_tests/transfer.py @@ -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/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 235b1c809..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); @@ -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' |