aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/FUNDING.yml1
-rw-r--r--CMakeLists.txt22
-rw-r--r--README.md10
-rw-r--r--contrib/depends/Makefile2
-rw-r--r--contrib/depends/funcs.mk8
-rw-r--r--contrib/depends/packages/eudev.mk4
-rw-r--r--contrib/depends/packages/expat.mk6
-rw-r--r--contrib/depends/packages/hidapi.mk11
-rw-r--r--contrib/depends/packages/icu4c.mk5
-rw-r--r--contrib/depends/packages/ldns.mk6
-rw-r--r--contrib/depends/packages/libiconv.mk4
-rw-r--r--contrib/depends/packages/ncurses.mk58
-rw-r--r--contrib/depends/packages/packages.mk10
-rw-r--r--contrib/depends/packages/protobuf.mk4
-rw-r--r--contrib/depends/packages/readline.mk12
-rw-r--r--contrib/depends/packages/sodium-darwin.mk5
-rw-r--r--contrib/depends/packages/sodium.mk5
-rw-r--r--contrib/depends/packages/unwind.mk2
-rw-r--r--contrib/depends/packages/zeromq.mk4
-rw-r--r--contrib/depends/toolchain.cmake.in2
-rw-r--r--contrib/epee/include/net/abstract_tcp_server2.h22
-rw-r--r--contrib/epee/include/net/abstract_tcp_server2.inl335
-rw-r--r--contrib/epee/include/net/connection_basic.hpp2
-rw-r--r--contrib/epee/include/net/http_protocol_handler.inl4
-rw-r--r--contrib/epee/include/net/http_server_handlers_map2.h4
-rw-r--r--contrib/epee/include/net/http_server_impl_base.h9
-rw-r--r--contrib/epee/include/net/levin_protocol_handler_async.h24
-rw-r--r--contrib/epee/include/net/local_ip.h28
-rw-r--r--contrib/epee/include/net/net_parse_helpers.h44
-rw-r--r--contrib/epee/include/net/net_utils_base.h107
-rw-r--r--contrib/epee/src/connection_basic.cpp3
-rw-r--r--contrib/epee/src/net_helper.cpp35
-rw-r--r--contrib/epee/src/net_utils_base.cpp31
-rw-r--r--contrib/gitian/README.md11
-rwxr-xr-xcontrib/gitian/gitian-build.py35
-rw-r--r--contrib/rlwrap/monerocommands_bitmonerod.txt2
m---------external/trezor-common0
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/blockchain_db/blockchain_db.h65
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp208
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h13
-rw-r--r--src/blockchain_db/testdb.h7
-rw-r--r--src/blockchain_utilities/blockchain_blackball.cpp1
-rw-r--r--src/blockchain_utilities/blockchain_export.cpp2
-rw-r--r--src/blocks/checkpoints.datbin232004 -> 234980 bytes
-rw-r--r--src/common/dns_utils.cpp1
-rw-r--r--src/common/util.cpp17
-rw-r--r--src/common/util.h2
-rw-r--r--src/crypto/keccak.c9
-rw-r--r--src/crypto/slow-hash.c2
-rw-r--r--src/cryptonote_basic/verification_context.h1
-rw-r--r--src/cryptonote_config.h3
-rw-r--r--src/cryptonote_core/blockchain.cpp233
-rw-r--r--src/cryptonote_core/blockchain.h35
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp17
-rw-r--r--src/cryptonote_core/cryptonote_core.h1
-rw-r--r--src/cryptonote_core/tx_sanity_check.cpp4
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_defs.h10
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl24
-rw-r--r--src/daemon/command_line_args.h5
-rw-r--r--src/daemon/command_parser_executor.cpp76
-rw-r--r--src/daemon/command_parser_executor.h6
-rw-r--r--src/daemon/command_server.cpp18
-rw-r--r--src/daemon/daemon.cpp35
-rw-r--r--src/daemon/daemon.h1
-rw-r--r--src/daemon/main.cpp1
-rw-r--r--src/daemon/rpc_command_executor.cpp259
-rw-r--r--src/daemon/rpc_command_executor.h16
-rw-r--r--src/debug_utilities/CMakeLists.txt22
-rw-r--r--src/debug_utilities/dns_checks.cpp149
-rw-r--r--src/device/device.hpp1
-rw-r--r--src/device/device_cold.hpp1
-rw-r--r--src/device_trezor/device_trezor.cpp16
-rw-r--r--src/device_trezor/device_trezor.hpp4
-rw-r--r--src/device_trezor/trezor/protocol.cpp3
-rw-r--r--src/net/error.h3
-rw-r--r--src/net/parse.cpp82
-rw-r--r--src/net/parse.h15
-rw-r--r--src/p2p/net_node.cpp24
-rw-r--r--src/p2p/net_node.h58
-rw-r--r--src/p2p/net_node.inl443
-rw-r--r--src/p2p/net_node_common.h11
-rw-r--r--src/p2p/net_peerlist.h23
-rw-r--r--src/p2p/net_peerlist_boost_serialization.h31
-rw-r--r--src/ringct/multiexp.cc1
-rw-r--r--src/ringct/rctTypes.cpp1
-rw-r--r--src/rpc/core_rpc_server.cpp347
-rw-r--r--src/rpc/core_rpc_server.h15
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h172
-rw-r--r--src/rpc/daemon_handler.cpp5
-rw-r--r--src/rpc/rpc_args.cpp37
-rw-r--r--src/rpc/rpc_args.h6
-rw-r--r--src/serialization/json_object.cpp2
-rw-r--r--src/simplewallet/simplewallet.cpp133
-rw-r--r--src/simplewallet/simplewallet.h1
-rw-r--r--src/version.cpp.in2
-rw-r--r--src/wallet/api/wallet.cpp55
-rw-r--r--src/wallet/api/wallet.h5
-rw-r--r--src/wallet/api/wallet2_api.h16
-rw-r--r--src/wallet/api/wallet_manager.cpp3
-rw-r--r--src/wallet/api/wallet_manager.h1
-rw-r--r--src/wallet/wallet2.cpp85
-rw-r--r--src/wallet/wallet2.h5
-rw-r--r--src/wallet/wallet_rpc_server.cpp23
-rw-r--r--src/wallet/wallet_rpc_server_error_codes.h1
-rw-r--r--tests/core_tests/chaingen.cpp1
-rw-r--r--tests/core_tests/wallet_tools.cpp1
-rw-r--r--tests/difficulty/CMakeLists.txt2
-rwxr-xr-x[-rw-r--r--]tests/difficulty/gen_wide_data.py0
-rwxr-xr-xtests/functional_tests/blockchain.py8
-rwxr-xr-xtests/functional_tests/functional_tests_rpc.py2
-rwxr-xr-xtests/functional_tests/multisig.py7
-rwxr-xr-xtests/functional_tests/transfer.py4
-rwxr-xr-xtests/functional_tests/validate_address.py111
-rw-r--r--tests/unit_tests/ban.cpp38
-rw-r--r--tests/unit_tests/keccak.cpp17
-rw-r--r--tests/unit_tests/mnemonics.cpp4
-rw-r--r--tests/unit_tests/net.cpp18
-rw-r--r--tests/unit_tests/output_selection.cpp8
-rw-r--r--tests/unit_tests/rolling_median.cpp8
-rw-r--r--tests/unit_tests/test_protocol_pack.cpp1
-rw-r--r--tests/unit_tests/varint.cpp1
-rw-r--r--translations/generate_translations_header.c1
-rw-r--r--utils/python-rpc/framework/daemon.py9
-rw-r--r--utils/python-rpc/framework/wallet.py13
125 files changed, 3257 insertions, 750 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..59384d180 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)
@@ -997,8 +1003,18 @@ endif()
add_subdirectory(contrib)
add_subdirectory(src)
+find_package(PythonInterp)
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)
@@ -1032,5 +1048,3 @@ option(INSTALL_VENDORED_LIBUNBOUND "Install libunbound binary built from source
CHECK_C_COMPILER_FLAG(-std=c11 HAVE_C11)
-
-find_package(PythonInterp)
diff --git a/README.md b/README.md
index 3c4f5ac86..10c0daa28 100644
--- a/README.md
+++ b/README.md
@@ -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.1.0 | 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.0 | 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/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/packages.mk b/contrib/depends/packages/packages.mk
index 8523e95df..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 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 ba9f5f939..ee0407a5e 100644
--- a/contrib/depends/toolchain.cmake.in
+++ b/contrib/depends/toolchain.cmake.in
@@ -21,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..b38ab5399 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(){}
};
@@ -227,8 +227,12 @@ namespace net_utils
std::map<std::string, t_connection_type> server_type_map;
void create_server_type_map();
- bool init_server(uint32_t port, const std::string address = "0.0.0.0", ssl_options_t ssl_options = ssl_support_t::e_ssl_support_autodetect);
- bool init_server(const std::string port, const std::string& address = "0.0.0.0", ssl_options_t ssl_options = ssl_support_t::e_ssl_support_autodetect);
+ bool init_server(uint32_t port, const std::string& address = "0.0.0.0",
+ uint32_t port_ipv6 = 0, const std::string& address_ipv6 = "::", bool use_ipv6 = false, bool require_ipv4 = true,
+ ssl_options_t ssl_options = ssl_support_t::e_ssl_support_autodetect);
+ bool init_server(const std::string port, const std::string& address = "0.0.0.0",
+ const std::string port_ipv6 = "", const std::string address_ipv6 = "::", bool use_ipv6 = false, bool require_ipv4 = true,
+ ssl_options_t ssl_options = ssl_support_t::e_ssl_support_autodetect);
/// Run the server's io_service loop.
bool run_server(size_t threads_count, bool wait = true, const boost::thread::attributes& attrs = boost::thread::attributes());
@@ -269,6 +273,7 @@ namespace net_utils
}
int get_binded_port(){return m_port;}
+ int get_binded_port_ipv6(){return m_port_ipv6;}
long get_connections_count() const
{
@@ -339,7 +344,9 @@ namespace net_utils
/// Run the server's io_service loop.
bool worker_thread();
/// Handle completion of an asynchronous accept operation.
- void handle_accept(const boost::system::error_code& e);
+ void handle_accept_ipv4(const boost::system::error_code& e);
+ void handle_accept_ipv6(const boost::system::error_code& e);
+ void handle_accept(const boost::system::error_code& e, bool ipv6 = false);
bool is_thread_worker();
@@ -360,11 +367,16 @@ namespace net_utils
/// Acceptor used to listen for incoming connections.
boost::asio::ip::tcp::acceptor acceptor_;
+ boost::asio::ip::tcp::acceptor acceptor_ipv6;
epee::net_utils::network_address default_remote;
std::atomic<bool> m_stop_signal_sent;
uint32_t m_port;
+ uint32_t m_port_ipv6;
std::string m_address;
+ std::string m_address_ipv6;
+ bool m_use_ipv6;
+ bool m_require_ipv4;
std::string m_thread_name_prefix; //TODO: change to enum server_type, now used
size_t m_threads_count;
std::vector<boost::shared_ptr<boost::thread> > m_threads;
@@ -376,6 +388,8 @@ namespace net_utils
/// The next connection to be accepted
connection_ptr new_connection_;
+ connection_ptr new_connection_ipv6;
+
boost::mutex connections_mutex;
std::set<connection_ptr> connections_;
diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl
index fa5858b9f..19e9c9af9 100644
--- a/contrib/epee/include/net/abstract_tcp_server2.inl
+++ b/contrib/epee/include/net/abstract_tcp_server2.inl
@@ -145,10 +145,18 @@ PRAGMA_WARNING_DISABLE_VS(4355)
boost::system::error_code ec;
auto remote_ep = socket().remote_endpoint(ec);
CHECK_AND_NO_ASSERT_MES(!ec, false, "Failed to get remote endpoint: " << ec.message() << ':' << ec.value());
- CHECK_AND_NO_ASSERT_MES(remote_ep.address().is_v4(), false, "IPv6 not supported here");
+ CHECK_AND_NO_ASSERT_MES(remote_ep.address().is_v4() || remote_ep.address().is_v6(), false, "only IPv4 and IPv6 supported here");
- const unsigned long ip_{boost::asio::detail::socket_ops::host_to_network_long(remote_ep.address().to_v4().to_ulong())};
- return start(is_income, is_multithreaded, ipv4_network_address{uint32_t(ip_), remote_ep.port()});
+ if (remote_ep.address().is_v4())
+ {
+ const unsigned long ip_ = boost::asio::detail::socket_ops::host_to_network_long(remote_ep.address().to_v4().to_ulong());
+ return start(is_income, is_multithreaded, ipv4_network_address{uint32_t(ip_), remote_ep.port()});
+ }
+ else
+ {
+ const auto ip_{remote_ep.address().to_v6()};
+ return start(is_income, is_multithreaded, ipv6_network_address{ip_, remote_ep.port()});
+ }
CATCH_ENTRY_L0("connection<t_protocol_handler>::start()", false);
}
//---------------------------------------------------------------------------------
@@ -327,12 +335,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 );
@@ -602,12 +612,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);
@@ -900,12 +912,14 @@ PRAGMA_WARNING_DISABLE_VS(4355)
m_io_service_local_instance(new worker()),
io_service_(m_io_service_local_instance->io_service),
acceptor_(io_service_),
+ acceptor_ipv6(io_service_),
default_remote(),
m_stop_signal_sent(false), m_port(0),
m_threads_count(0),
m_thread_index(0),
m_connection_type( connection_type ),
- new_connection_()
+ new_connection_(),
+ new_connection_ipv6()
{
create_server_type_map();
m_thread_name_prefix = "NET";
@@ -916,12 +930,14 @@ PRAGMA_WARNING_DISABLE_VS(4355)
m_state(boost::make_shared<typename connection<t_protocol_handler>::shared_state>()),
io_service_(extarnal_io_service),
acceptor_(io_service_),
+ acceptor_ipv6(io_service_),
default_remote(),
m_stop_signal_sent(false), m_port(0),
m_threads_count(0),
m_thread_index(0),
m_connection_type(connection_type),
- new_connection_()
+ new_connection_(),
+ new_connection_ipv6()
{
create_server_type_map();
m_thread_name_prefix = "NET";
@@ -943,29 +959,92 @@ PRAGMA_WARNING_DISABLE_VS(4355)
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
- bool boosted_tcp_server<t_protocol_handler>::init_server(uint32_t port, const std::string address, ssl_options_t ssl_options)
+ bool boosted_tcp_server<t_protocol_handler>::init_server(uint32_t port, const std::string& address,
+ uint32_t port_ipv6, const std::string& address_ipv6, bool use_ipv6, bool require_ipv4,
+ ssl_options_t ssl_options)
{
TRY_ENTRY();
m_stop_signal_sent = false;
m_port = port;
+ m_port_ipv6 = port_ipv6;
m_address = address;
+ m_address_ipv6 = address_ipv6;
+ m_use_ipv6 = use_ipv6;
+ m_require_ipv4 = require_ipv4;
+
if (ssl_options)
m_state->configure_ssl(std::move(ssl_options));
- // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
- boost::asio::ip::tcp::resolver resolver(io_service_);
- boost::asio::ip::tcp::resolver::query query(address, boost::lexical_cast<std::string>(port), boost::asio::ip::tcp::resolver::query::canonical_name);
- boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
- acceptor_.open(endpoint.protocol());
- acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
- acceptor_.bind(endpoint);
- acceptor_.listen();
- boost::asio::ip::tcp::endpoint binded_endpoint = acceptor_.local_endpoint();
- m_port = binded_endpoint.port();
- MDEBUG("start accept");
- new_connection_.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, m_state->ssl_options().support));
- acceptor_.async_accept(new_connection_->socket(),
- boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept, this,
- boost::asio::placeholders::error));
+
+ std::string ipv4_failed = "";
+ std::string ipv6_failed = "";
+ try
+ {
+ boost::asio::ip::tcp::resolver resolver(io_service_);
+ boost::asio::ip::tcp::resolver::query query(address, boost::lexical_cast<std::string>(port), boost::asio::ip::tcp::resolver::query::canonical_name);
+ boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
+ acceptor_.open(endpoint.protocol());
+ // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
+ acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
+ acceptor_.bind(endpoint);
+ acceptor_.listen();
+ boost::asio::ip::tcp::endpoint binded_endpoint = acceptor_.local_endpoint();
+ m_port = binded_endpoint.port();
+ MDEBUG("start accept (IPv4)");
+ new_connection_.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, m_state->ssl_options().support));
+ acceptor_.async_accept(new_connection_->socket(),
+ boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept_ipv4, this,
+ boost::asio::placeholders::error));
+ }
+ catch (const std::exception &e)
+ {
+ ipv4_failed = e.what();
+ }
+
+ if (ipv4_failed != "")
+ {
+ MERROR("Failed to bind IPv4: " << ipv4_failed);
+ if (require_ipv4)
+ {
+ throw std::runtime_error("Failed to bind IPv4 (set to required)");
+ }
+ }
+
+ if (use_ipv6)
+ {
+ try
+ {
+ if (port_ipv6 == 0) port_ipv6 = port; // default arg means bind to same port as ipv4
+ boost::asio::ip::tcp::resolver resolver(io_service_);
+ boost::asio::ip::tcp::resolver::query query(address_ipv6, boost::lexical_cast<std::string>(port_ipv6), boost::asio::ip::tcp::resolver::query::canonical_name);
+ boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
+ acceptor_ipv6.open(endpoint.protocol());
+ // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
+ acceptor_ipv6.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
+ acceptor_ipv6.set_option(boost::asio::ip::v6_only(true));
+ acceptor_ipv6.bind(endpoint);
+ acceptor_ipv6.listen();
+ boost::asio::ip::tcp::endpoint binded_endpoint = acceptor_ipv6.local_endpoint();
+ m_port_ipv6 = binded_endpoint.port();
+ MDEBUG("start accept (IPv6)");
+ new_connection_ipv6.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, m_state->ssl_options().support));
+ acceptor_ipv6.async_accept(new_connection_ipv6->socket(),
+ boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept_ipv6, this,
+ boost::asio::placeholders::error));
+ }
+ catch (const std::exception &e)
+ {
+ ipv6_failed = e.what();
+ }
+ }
+
+ if (use_ipv6 && ipv6_failed != "")
+ {
+ MERROR("Failed to bind IPv6: " << ipv6_failed);
+ if (ipv4_failed != "")
+ {
+ throw std::runtime_error("Failed to bind IPv4 and IPv6");
+ }
+ }
return true;
}
@@ -984,15 +1063,23 @@ PRAGMA_WARNING_DISABLE_VS(4355)
PUSH_WARNINGS
DISABLE_GCC_WARNING(maybe-uninitialized)
template<class t_protocol_handler>
- bool boosted_tcp_server<t_protocol_handler>::init_server(const std::string port, const std::string& address, ssl_options_t ssl_options)
+ bool boosted_tcp_server<t_protocol_handler>::init_server(const std::string port, const std::string& address,
+ const std::string port_ipv6, const std::string address_ipv6, bool use_ipv6, bool require_ipv4,
+ ssl_options_t ssl_options)
{
uint32_t p = 0;
+ uint32_t p_ipv6 = 0;
if (port.size() && !string_tools::get_xtype_from_string(p, port)) {
MERROR("Failed to convert port no = " << port);
return false;
}
- return this->init_server(p, address, std::move(ssl_options));
+
+ if (port_ipv6.size() && !string_tools::get_xtype_from_string(p_ipv6, port_ipv6)) {
+ MERROR("Failed to convert port no = " << port_ipv6);
+ return false;
+ }
+ return this->init_server(p, address, p_ipv6, address_ipv6, use_ipv6, require_ipv4, std::move(ssl_options));
}
POP_WARNINGS
//---------------------------------------------------------------------------------
@@ -1084,7 +1171,7 @@ POP_WARNINGS
{
//some problems with the listening socket ?..
_dbg1("Net service stopped without stop request, restarting...");
- if(!this->init_server(m_port, m_address))
+ if(!this->init_server(m_port, m_address, m_port_ipv6, m_address_ipv6, m_use_ipv6, m_require_ipv4))
{
_dbg1("Reiniting service failed, exit.");
return false;
@@ -1150,29 +1237,52 @@ POP_WARNINGS
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
- void boosted_tcp_server<t_protocol_handler>::handle_accept(const boost::system::error_code& e)
+ void boosted_tcp_server<t_protocol_handler>::handle_accept_ipv4(const boost::system::error_code& e)
+ {
+ this->handle_accept(e, false);
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ void boosted_tcp_server<t_protocol_handler>::handle_accept_ipv6(const boost::system::error_code& e)
+ {
+ this->handle_accept(e, true);
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ void boosted_tcp_server<t_protocol_handler>::handle_accept(const boost::system::error_code& e, bool ipv6)
{
MDEBUG("handle_accept");
+
+ boost::asio::ip::tcp::acceptor* current_acceptor = &acceptor_;
+ connection_ptr* current_new_connection = &new_connection_;
+ auto accept_function_pointer = &boosted_tcp_server<t_protocol_handler>::handle_accept_ipv4;
+ if (ipv6)
+ {
+ current_acceptor = &acceptor_ipv6;
+ current_new_connection = &new_connection_ipv6;
+ accept_function_pointer = &boosted_tcp_server<t_protocol_handler>::handle_accept_ipv6;
+ }
+
try
{
if (!e)
{
- if (m_connection_type == e_connection_type_RPC) {
- const char *ssl_message = "unknown";
- switch (new_connection_->get_ssl_support())
- {
- case epee::net_utils::ssl_support_t::e_ssl_support_disabled: ssl_message = "disabled"; break;
- case epee::net_utils::ssl_support_t::e_ssl_support_enabled: ssl_message = "enabled"; break;
- case epee::net_utils::ssl_support_t::e_ssl_support_autodetect: ssl_message = "autodetection"; break;
- }
- MDEBUG("New server for RPC connections, SSL " << ssl_message);
- new_connection_->setRpcStation(); // hopefully this is not needed actually
- }
- connection_ptr conn(std::move(new_connection_));
- new_connection_.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, conn->get_ssl_support()));
- acceptor_.async_accept(new_connection_->socket(),
- boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept, this,
- boost::asio::placeholders::error));
+ if (m_connection_type == e_connection_type_RPC) {
+ const char *ssl_message = "unknown";
+ switch ((*current_new_connection)->get_ssl_support())
+ {
+ case epee::net_utils::ssl_support_t::e_ssl_support_disabled: ssl_message = "disabled"; break;
+ case epee::net_utils::ssl_support_t::e_ssl_support_enabled: ssl_message = "enabled"; break;
+ case epee::net_utils::ssl_support_t::e_ssl_support_autodetect: ssl_message = "autodetection"; break;
+ }
+ MDEBUG("New server for RPC connections, SSL " << ssl_message);
+ (*current_new_connection)->setRpcStation(); // hopefully this is not needed actually
+ }
+ connection_ptr conn(std::move((*current_new_connection)));
+ (*current_new_connection).reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, conn->get_ssl_support()));
+ current_acceptor->async_accept((*current_new_connection)->socket(),
+ boost::bind(accept_function_pointer, this,
+ boost::asio::placeholders::error));
boost::asio::socket_base::keep_alive opt(true);
conn->socket().set_option(opt);
@@ -1204,10 +1314,10 @@ POP_WARNINGS
assert(m_state != nullptr); // always set in constructor
_erro("Some problems at accept: " << e.message() << ", connections_count = " << m_state->sock_count);
misc_utils::sleep_no_w(100);
- new_connection_.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, new_connection_->get_ssl_support()));
- acceptor_.async_accept(new_connection_->socket(),
- boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept, this,
- boost::asio::placeholders::error));
+ (*current_new_connection).reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, (*current_new_connection)->get_ssl_support()));
+ current_acceptor->async_accept((*current_new_connection)->socket(),
+ boost::bind(accept_function_pointer, this,
+ boost::asio::placeholders::error));
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
@@ -1341,23 +1451,84 @@ POP_WARNINGS
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ CRITICAL_REGION_LOCAL(connections_mutex); connections_.erase(new_connection_l); });
boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket();
- //////////////////////////////////////////////////////////////////////////
+ bool try_ipv6 = false;
+
boost::asio::ip::tcp::resolver resolver(io_service_);
boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
- boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
+ boost::system::error_code resolve_error;
+ boost::asio::ip::tcp::resolver::iterator iterator;
+ try
+ {
+ //resolving ipv4 address as ipv6 throws, catch here and move on
+ iterator = resolver.resolve(query, resolve_error);
+ }
+ catch (const boost::system::system_error& e)
+ {
+ if (!m_use_ipv6 || (resolve_error != boost::asio::error::host_not_found &&
+ resolve_error != boost::asio::error::host_not_found_try_again))
+ {
+ throw;
+ }
+ try_ipv6 = true;
+ }
+ catch (...)
+ {
+ throw;
+ }
+
+ std::string bind_ip_to_use;
+
boost::asio::ip::tcp::resolver::iterator end;
if(iterator == end)
{
- _erro("Failed to resolve " << adr);
- return false;
+ if (!m_use_ipv6)
+ {
+ _erro("Failed to resolve " << adr);
+ return false;
+ }
+ else
+ {
+ try_ipv6 = true;
+ MINFO("Resolving address as IPv4 failed, trying IPv6");
+ }
+ }
+ else
+ {
+ bind_ip_to_use = bind_ip;
}
- //////////////////////////////////////////////////////////////////////////
+ if (try_ipv6)
+ {
+ boost::asio::ip::tcp::resolver::query query6(boost::asio::ip::tcp::v6(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
+
+ iterator = resolver.resolve(query6, resolve_error);
+
+ if(iterator == end)
+ {
+ _erro("Failed to resolve " << adr);
+ return false;
+ }
+ else
+ {
+ if (bind_ip == "0.0.0.0")
+ {
+ bind_ip_to_use = "::";
+ }
+ else
+ {
+ bind_ip_to_use = "";
+ }
+
+ }
+
+ }
+
+ LOG_ERROR("Trying connect to " << adr << ":" << port << ", bind_ip = " << bind_ip_to_use);
//boost::asio::ip::tcp::endpoint remote_endpoint(boost::asio::ip::address::from_string(addr.c_str()), port);
boost::asio::ip::tcp::endpoint remote_endpoint(*iterator);
- auto try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip, conn_timeout, ssl_support);
+ auto try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip_to_use, conn_timeout, ssl_support);
if (try_connect_result == CONNECT_FAILURE)
return false;
if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect && try_connect_result == CONNECT_NO_SSL)
@@ -1365,7 +1536,7 @@ POP_WARNINGS
// we connected, but could not connect with SSL, try without
MERROR("SSL handshake failed on an autodetect connection, reconnecting without SSL");
new_connection_l->disable_ssl();
- try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip, conn_timeout, epee::net_utils::ssl_support_t::e_ssl_support_disabled);
+ try_connect_result = try_connect(new_connection_l, adr, port, sock_, remote_endpoint, bind_ip_to_use, conn_timeout, epee::net_utils::ssl_support_t::e_ssl_support_disabled);
if (try_connect_result != CONNECT_SUCCESS)
return false;
}
@@ -1405,17 +1576,59 @@ POP_WARNINGS
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ CRITICAL_REGION_LOCAL(connections_mutex); connections_.erase(new_connection_l); });
boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket();
- //////////////////////////////////////////////////////////////////////////
+ bool try_ipv6 = false;
+
boost::asio::ip::tcp::resolver resolver(io_service_);
boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
- boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
+ boost::system::error_code resolve_error;
+ boost::asio::ip::tcp::resolver::iterator iterator;
+ try
+ {
+ //resolving ipv4 address as ipv6 throws, catch here and move on
+ iterator = resolver.resolve(query, resolve_error);
+ }
+ catch (const boost::system::system_error& e)
+ {
+ if (!m_use_ipv6 || (resolve_error != boost::asio::error::host_not_found &&
+ resolve_error != boost::asio::error::host_not_found_try_again))
+ {
+ throw;
+ }
+ try_ipv6 = true;
+ }
+ catch (...)
+ {
+ throw;
+ }
+
boost::asio::ip::tcp::resolver::iterator end;
if(iterator == end)
{
- _erro("Failed to resolve " << adr);
- return false;
+ if (!try_ipv6)
+ {
+ _erro("Failed to resolve " << adr);
+ return false;
+ }
+ else
+ {
+ MINFO("Resolving address as IPv4 failed, trying IPv6");
+ }
}
- //////////////////////////////////////////////////////////////////////////
+
+ if (try_ipv6)
+ {
+ boost::asio::ip::tcp::resolver::query query6(boost::asio::ip::tcp::v6(), adr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
+
+ iterator = resolver.resolve(query6, resolve_error);
+
+ if(iterator == end)
+ {
+ _erro("Failed to resolve " << adr);
+ return false;
+ }
+ }
+
+
boost::asio::ip::tcp::endpoint remote_endpoint(*iterator);
sock_.open(remote_endpoint.protocol());
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/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl
index ae8e43477..790d0f3b1 100644
--- a/contrib/epee/include/net/http_protocol_handler.inl
+++ b/contrib/epee/include/net/http_protocol_handler.inl
@@ -577,6 +577,10 @@ namespace net_utils
if (query_info.m_http_method != http::http_method_options)
{
res = handle_request(query_info, response);
+ if (response.m_response_code == 500)
+ {
+ m_want_close = true; // close on all "Internal server error"s
+ }
}
else
{
diff --git a/contrib/epee/include/net/http_server_handlers_map2.h b/contrib/epee/include/net/http_server_handlers_map2.h
index 4b2053091..07ed8157b 100644
--- a/contrib/epee/include/net/http_server_handlers_map2.h
+++ b/contrib/epee/include/net/http_server_handlers_map2.h
@@ -71,7 +71,7 @@
MINFO(m_conn_context << "calling " << s_pattern); \
if(!callback_f(static_cast<command_type::request&>(req), static_cast<command_type::response&>(resp), &m_conn_context)) \
{ \
- LOG_ERROR("Failed to " << #callback_f << "()"); \
+ MERROR(m_conn_context << "Failed to " << #callback_f << "()"); \
response_info.m_response_code = 500; \
response_info.m_response_comment = "Internal Server Error"; \
return true; \
@@ -99,7 +99,7 @@
MINFO(m_conn_context << "calling " << s_pattern); \
if(!callback_f(static_cast<command_type::request&>(req), static_cast<command_type::response&>(resp), &m_conn_context)) \
{ \
- LOG_ERROR("Failed to " << #callback_f << "()"); \
+ MERROR(m_conn_context << "Failed to " << #callback_f << "()"); \
response_info.m_response_code = 500; \
response_info.m_response_comment = "Internal Server Error"; \
return true; \
diff --git a/contrib/epee/include/net/http_server_impl_base.h b/contrib/epee/include/net/http_server_impl_base.h
index fc2dcbf67..6cd19f17b 100644
--- a/contrib/epee/include/net/http_server_impl_base.h
+++ b/contrib/epee/include/net/http_server_impl_base.h
@@ -57,6 +57,7 @@ namespace epee
{}
bool init(std::function<void(size_t, uint8_t*)> rng, const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0",
+ const std::string& bind_ipv6_address = "::", bool use_ipv6 = false, bool require_ipv4 = true,
std::vector<std::string> access_control_origins = std::vector<std::string>(),
boost::optional<net_utils::http::login> user = boost::none,
net_utils::ssl_options_t ssl_options = net_utils::ssl_support_t::e_ssl_support_autodetect)
@@ -75,8 +76,12 @@ namespace epee
m_net_server.get_config_object().m_user = std::move(user);
- MGINFO("Binding on " << bind_ip << ":" << bind_port);
- bool res = m_net_server.init_server(bind_port, bind_ip, std::move(ssl_options));
+ MGINFO("Binding on " << bind_ip << " (IPv4):" << bind_port);
+ if (use_ipv6)
+ {
+ MGINFO("Binding on " << bind_ipv6_address << " (IPv6):" << bind_port);
+ }
+ bool res = m_net_server.init_server(bind_port, bind_ip, bind_port, bind_ipv6_address, use_ipv6, require_ipv4, std::move(ssl_options));
if(!res)
{
LOG_ERROR("Failed to bind server");
diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h
index 116b3ace1..8d7ffb2c2 100644
--- a/contrib/epee/include/net/levin_protocol_handler_async.h
+++ b/contrib/epee/include/net/levin_protocol_handler_async.h
@@ -99,6 +99,8 @@ public:
template<class callback_t>
bool for_connection(const boost::uuids::uuid &connection_id, const callback_t &cb);
size_t get_connections_count();
+ size_t get_out_connections_count();
+ size_t get_in_connections_count();
void set_handler(levin_commands_handler<t_connection_context>* handler, void (*destroy)(levin_commands_handler<t_connection_context>*) = NULL);
async_protocol_handler_config():m_pcommands_handler(NULL), m_pcommands_handler_destroy(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE), m_invoke_timeout(LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED)
@@ -882,6 +884,28 @@ size_t async_protocol_handler_config<t_connection_context>::get_connections_coun
}
//------------------------------------------------------------------------------------------
template<class t_connection_context>
+size_t async_protocol_handler_config<t_connection_context>::get_out_connections_count()
+{
+ CRITICAL_REGION_LOCAL(m_connects_lock);
+ size_t count = 0;
+ for (const auto &c: m_connects)
+ if (!c.second->m_connection_context.m_is_income)
+ ++count;
+ return count;
+}
+//------------------------------------------------------------------------------------------
+template<class t_connection_context>
+size_t async_protocol_handler_config<t_connection_context>::get_in_connections_count()
+{
+ CRITICAL_REGION_LOCAL(m_connects_lock);
+ size_t count = 0;
+ for (const auto &c: m_connects)
+ if (c.second->m_connection_context.m_is_income)
+ ++count;
+ return count;
+}
+//------------------------------------------------------------------------------------------
+template<class t_connection_context>
void async_protocol_handler_config<t_connection_context>::set_handler(levin_commands_handler<t_connection_context>* handler, void (*destroy)(levin_commands_handler<t_connection_context>*))
{
if (m_pcommands_handler && m_pcommands_handler_destroy)
diff --git a/contrib/epee/include/net/local_ip.h b/contrib/epee/include/net/local_ip.h
index 52c5855b9..7523f9d81 100644
--- a/contrib/epee/include/net/local_ip.h
+++ b/contrib/epee/include/net/local_ip.h
@@ -27,10 +27,38 @@
#pragma once
+#include <string>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/asio/ip/address_v6.hpp>
+
namespace epee
{
namespace net_utils
{
+
+ inline
+ bool is_ipv6_local(const std::string& ip)
+ {
+ auto addr = boost::asio::ip::make_address_v6(ip);
+
+ // ipv6 link-local unicast addresses are fe80::/10
+ bool is_link_local = addr.is_link_local();
+
+ auto addr_bytes = addr.to_bytes();
+
+ // ipv6 unique local unicast addresses start with fc00::/7 -- (fcXX or fdXX)
+ bool is_unique_local_unicast = (addr_bytes[0] == 0xfc || addr_bytes[0] == 0xfd);
+
+ return is_link_local || is_unique_local_unicast;
+ }
+
+ inline
+ bool is_ipv6_loopback(const std::string& ip)
+ {
+ // ipv6 loopback is ::1
+ return boost::asio::ip::address_v6::from_string(ip).is_loopback();
+ }
+
inline
bool is_ip_local(uint32_t ip)
{
diff --git a/contrib/epee/include/net/net_parse_helpers.h b/contrib/epee/include/net/net_parse_helpers.h
index 708cce0ff..1d156d19c 100644
--- a/contrib/epee/include/net/net_parse_helpers.h
+++ b/contrib/epee/include/net/net_parse_helpers.h
@@ -94,7 +94,7 @@ namespace net_utils
return true;
}
- inline
+ inline
bool parse_uri(const std::string uri, http::uri_content& content)
{
@@ -128,11 +128,51 @@ namespace net_utils
return true;
}
+ inline
+ bool parse_url_ipv6(const std::string url_str, http::url_content& content)
+ {
+ STATIC_REGEXP_EXPR_1(rexp_match_uri, "^((.*?)://)?(\\[(.*)\\](:(\\d+))?)(.*)?", boost::regex::icase | boost::regex::normal);
+ // 12 3 4 5 6 7
- inline
+ content.port = 0;
+ boost::smatch result;
+ if(!(boost::regex_search(url_str, result, rexp_match_uri, boost::match_default) && result[0].matched))
+ {
+ LOG_PRINT_L1("[PARSE URI] regex not matched for uri: " << rexp_match_uri);
+ //content.m_path = uri;
+ return false;
+ }
+ if(result[2].matched)
+ {
+ content.schema = result[2];
+ }
+ if(result[4].matched)
+ {
+ content.host = result[4];
+ }
+ else // if host not matched, matching should be considered failed
+ {
+ return false;
+ }
+ if(result[6].matched)
+ {
+ content.port = boost::lexical_cast<uint64_t>(result[6]);
+ }
+ if(result[7].matched)
+ {
+ content.uri = result[7];
+ return parse_uri(result[7], content.m_uri_content);
+ }
+
+ return true;
+ }
+
+ inline
bool parse_url(const std::string url_str, http::url_content& content)
{
+ if (parse_url_ipv6(url_str, content)) return true;
+
///iframe_test.html?api_url=http://api.vk.com/api.php&api_id=3289090&api_settings=1&viewer_id=562964060&viewer_type=0&sid=0aad8d1c5713130f9ca0076f2b7b47e532877424961367d81e7fa92455f069be7e21bc3193cbd0be11895&secret=368ebbc0ef&access_token=668bc03f43981d883f73876ffff4aa8564254b359cc745dfa1b3cde7bdab2e94105d8f6d8250717569c0a7&user_id=0&group_id=0&is_app_user=1&auth_key=d2f7a895ca5ff3fdb2a2a8ae23fe679a&language=0&parent_language=0&ad_info=ElsdCQBaQlxiAQRdFUVUXiN2AVBzBx5pU1BXIgZUJlIEAWcgAUoLQg==&referrer=unknown&lc_name=9834b6a3&hash=
//STATIC_REGEXP_EXPR_1(rexp_match_uri, "^([^?#]*)(\\?([^#]*))?(#(.*))?", boost::regex::icase | boost::regex::normal);
STATIC_REGEXP_EXPR_1(rexp_match_uri, "^((.*?)://)?(([^/:]*)(:(\\d+))?)(.*)?", boost::regex::icase | boost::regex::normal);
diff --git a/contrib/epee/include/net/net_utils_base.h b/contrib/epee/include/net/net_utils_base.h
index 83e6b5ab8..5ae3e53b3 100644
--- a/contrib/epee/include/net/net_utils_base.h
+++ b/contrib/epee/include/net/net_utils_base.h
@@ -31,6 +31,7 @@
#include <boost/uuid/uuid.hpp>
#include <boost/asio/io_service.hpp>
+#include <boost/asio/ip/address_v6.hpp>
#include <typeinfo>
#include <type_traits>
#include "enums.h"
@@ -41,7 +42,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 +108,106 @@ 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 ipv6_network_address
+ {
+ protected:
+ boost::asio::ip::address_v6 m_address;
+ uint16_t m_port;
+
+ public:
+ ipv6_network_address()
+ : ipv6_network_address(boost::asio::ip::address_v6::loopback(), 0)
+ {}
+
+ ipv6_network_address(const boost::asio::ip::address_v6& ip, uint16_t port)
+ : m_address(ip), m_port(port)
+ {
+ }
+
+ bool equal(const ipv6_network_address& other) const noexcept;
+ bool less(const ipv6_network_address& other) const noexcept;
+ bool is_same_host(const ipv6_network_address& other) const noexcept
+ { return m_address == other.m_address; }
+
+ boost::asio::ip::address_v6 ip() const noexcept { return m_address; }
+ uint16_t port() const noexcept { return m_port; }
+ 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::ipv6; }
+ static constexpr zone get_zone() noexcept { return zone::public_; }
+ static constexpr bool is_blockable() noexcept { return true; }
+
+ static const uint8_t ID = 2;
+ BEGIN_KV_SERIALIZE_MAP()
+ boost::asio::ip::address_v6::bytes_type bytes = this_ref.m_address.to_bytes();
+ epee::serialization::selector<is_store>::serialize_t_val_as_blob(bytes, stg, hparent_section, "addr");
+ const_cast<boost::asio::ip::address_v6&>(this_ref.m_address) = boost::asio::ip::address_v6(bytes);
+ KV_SERIALIZE(m_port)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ inline bool operator==(const ipv6_network_address& lhs, const ipv6_network_address& rhs) noexcept
+ { return lhs.equal(rhs); }
+ inline bool operator!=(const ipv6_network_address& lhs, const ipv6_network_address& rhs) noexcept
+ { return !lhs.equal(rhs); }
+ inline bool operator<(const ipv6_network_address& lhs, const ipv6_network_address& rhs) noexcept
+ { return lhs.less(rhs); }
+ inline bool operator<=(const ipv6_network_address& lhs, const ipv6_network_address& rhs) noexcept
+ { return !rhs.less(lhs); }
+ inline bool operator>(const ipv6_network_address& lhs, const ipv6_network_address& rhs) noexcept
+ { return rhs.less(lhs); }
+ inline bool operator>=(const ipv6_network_address& lhs, const ipv6_network_address& rhs) noexcept
+ { return !lhs.less(rhs); }
+
class network_address
{
struct interface
@@ -214,6 +315,8 @@ namespace net_utils
{
case address_type::ipv4:
return this_ref.template serialize_addr<ipv4_network_address>(is_store_, stg, hparent_section);
+ case address_type::ipv6:
+ return this_ref.template serialize_addr<ipv6_network_address>(is_store_, stg, hparent_section);
case address_type::tor:
return this_ref.template serialize_addr<net::tor_address>(is_store_, stg, hparent_section);
case address_type::i2p:
@@ -250,7 +353,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/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_helper.cpp b/contrib/epee/src/net_helper.cpp
index 3543f5716..719f1c8e0 100644
--- a/contrib/epee/src/net_helper.cpp
+++ b/contrib/epee/src/net_helper.cpp
@@ -11,10 +11,39 @@ namespace net_utils
//////////////////////////////////////////////////////////////////////////
boost::asio::ip::tcp::resolver resolver(GET_IO_SERVICE(timeout));
boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), addr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
- boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
+
+ bool try_ipv6 = false;
+ boost::asio::ip::tcp::resolver::iterator iterator;
boost::asio::ip::tcp::resolver::iterator end;
- if(iterator == end) // Documentation states that successful call is guaranteed to be non-empty
- throw boost::system::system_error{boost::asio::error::fault, "Failed to resolve " + addr};
+ boost::system::error_code resolve_error;
+ try
+ {
+ iterator = resolver.resolve(query, resolve_error);
+ if(iterator == end) // Documentation states that successful call is guaranteed to be non-empty
+ {
+ // if IPv4 resolution fails, try IPv6. Unintentional outgoing IPv6 connections should only
+ // be possible if for some reason a hostname was given and that hostname fails IPv4 resolution,
+ // so at least for now there should not be a need for a flag "using ipv6 is ok"
+ try_ipv6 = true;
+ }
+
+ }
+ catch (const boost::system::system_error& e)
+ {
+ if (resolve_error != boost::asio::error::host_not_found &&
+ resolve_error != boost::asio::error::host_not_found_try_again)
+ {
+ throw;
+ }
+ try_ipv6 = true;
+ }
+ if (try_ipv6)
+ {
+ boost::asio::ip::tcp::resolver::query query6(boost::asio::ip::tcp::v6(), addr, port, boost::asio::ip::tcp::resolver::query::canonical_name);
+ iterator = resolver.resolve(query6);
+ if (iterator == end)
+ throw boost::system::system_error{boost::asio::error::fault, "Failed to resolve " + addr};
+ }
//////////////////////////////////////////////////////////////////////////
diff --git a/contrib/epee/src/net_utils_base.cpp b/contrib/epee/src/net_utils_base.cpp
index 9b781027e..5cc49cc71 100644
--- a/contrib/epee/src/net_utils_base.cpp
+++ b/contrib/epee/src/net_utils_base.cpp
@@ -21,6 +21,37 @@ namespace epee { namespace net_utils
bool ipv4_network_address::is_loopback() const { return net_utils::is_ip_loopback(ip()); }
bool ipv4_network_address::is_local() const { return net_utils::is_ip_local(ip()); }
+ bool ipv6_network_address::equal(const ipv6_network_address& other) const noexcept
+ { return is_same_host(other) && port() == other.port(); }
+
+ bool ipv6_network_address::less(const ipv6_network_address& other) const noexcept
+ { return is_same_host(other) ? port() < other.port() : m_address < other.m_address; }
+
+ std::string ipv6_network_address::str() const
+ { return std::string("[") + host_str() + "]:" + std::to_string(port()); }
+
+ std::string ipv6_network_address::host_str() const { return m_address.to_string(); }
+ bool ipv6_network_address::is_loopback() const { return m_address.is_loopback(); }
+ bool ipv6_network_address::is_local() const { return m_address.is_link_local(); }
+
+
+ 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
{
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/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 f05eb0f30..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:
@@ -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
index b975af6b4..a7d309753 100644
--- a/src/blocks/checkpoints.dat
+++ b/src/blocks/checkpoints.dat
Binary files differ
diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp
index 5e03bf897..dc1f335a7 100644
--- a/src/common/dns_utils.cpp
+++ b/src/common/dns_utils.cpp
@@ -48,7 +48,6 @@ static const char *DEFAULT_DNS_PUBLIC_ADDR[] =
"80.67.169.40", // FDN (France)
"89.233.43.71", // http://censurfridns.dk (Denmark)
"109.69.8.51", // punCAT (Spain)
- "77.109.148.137", // Xiala.net (Switzerland)
"193.58.251.251", // SkyDNS (Russia)
};
diff --git a/src/common/util.cpp b/src/common/util.cpp
index db5aa3052..0fa9e8dc1 100644
--- a/src/common/util.cpp
+++ b/src/common/util.cpp
@@ -1068,6 +1068,23 @@ std::string get_nix_version_display_string()
return std::string(buffer);
}
+ std::string get_human_readable_timespan(uint64_t seconds)
+ {
+ if (seconds < 60)
+ return std::to_string(seconds) + " seconds";
+ if (seconds < 3600)
+ return std::to_string((uint64_t)(seconds / 60)) + " minutes";
+ if (seconds < 3600 * 24)
+ return std::to_string((uint64_t)(seconds / 3600)) + " hours";
+ if (seconds < 3600 * 24 * 30.5)
+ return std::to_string((uint64_t)(seconds / (3600 * 24))) + " days";
+ if (seconds < 3600 * 24 * 365.25)
+ return std::to_string((uint64_t)(seconds / (3600 * 24 * 30.5))) + " months";
+ if (seconds < 3600 * 24 * 365.25 * 100)
+ return std::to_string((uint64_t)(seconds / (3600 * 24 * 30.5 * 365.25))) + " years";
+ return "a long time";
+ }
+
std::string get_human_readable_bytes(uint64_t bytes)
{
// Use 1024 for "kilo", 1024*1024 for "mega" and so on instead of the more modern and standard-conforming
diff --git a/src/common/util.h b/src/common/util.h
index f6d5c9b1f..b0f734eff 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -245,5 +245,7 @@ namespace tools
std::string get_human_readable_timestamp(uint64_t ts);
+ std::string get_human_readable_timespan(uint64_t seconds);
+
std::string get_human_readable_bytes(uint64_t bytes);
}
diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c
index 18ed3152f..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
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/cryptonote_basic/verification_context.h b/src/cryptonote_basic/verification_context.h
index 36b63f254..3d7200fae 100644
--- a/src/cryptonote_basic/verification_context.h
+++ b/src/cryptonote_basic/verification_context.h
@@ -48,6 +48,7 @@ namespace cryptonote
bool m_overspend;
bool m_fee_too_low;
bool m_not_rct;
+ bool m_too_few_outputs;
};
struct block_verification_context
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index 56b6a63b7..b68bb41e1 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -128,6 +128,8 @@
#define P2P_SUPPORT_FLAG_FLUFFY_BLOCKS 0x01
#define P2P_SUPPORT_FLAGS P2P_SUPPORT_FLAG_FLUFFY_BLOCKS
+#define RPC_IP_FAILS_BEFORE_BLOCK 3
+
#define ALLOW_DEBUG_COMMANDS
#define CRYPTONOTE_NAME "bitmonero"
@@ -147,6 +149,7 @@
#define HF_VERSION_PER_BYTE_FEE 8
#define HF_VERSION_SMALLER_BP 10
#define HF_VERSION_LONG_TERM_BLOCK_WEIGHT 10
+#define HF_VERSION_MIN_2_OUTPUTS 12
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 23534f0d3..0ea81f19a 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> &timestamps, 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> &timestamps, 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;
}
@@ -2803,6 +2840,19 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
const uint8_t hf_version = m_hardfork->get_current_version();
+ if (hf_version >= HF_VERSION_MIN_2_OUTPUTS)
+ {
+ if (tx.version >= 2)
+ {
+ if (tx.vout.size() < 2)
+ {
+ MERROR_VER("Tx " << get_transaction_hash(tx) << " has fewer than two outputs");
+ tvc.m_too_few_outputs = true;
+ return false;
+ }
+ }
+ }
+
// from hard fork 2, we require mixin at least 2 unless one output cannot mix with 2 others
// if one output cannot mix with 2 others, we accept at most 1 output that can mix
if (hf_version >= 2)
@@ -3250,7 +3300,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 +4007,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 +4034,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)
{
@@ -4806,15 +4853,39 @@ std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> Blockchain:: get_ou
return m_db->get_output_histogram(amounts, unlocked, recent_cutoff, min_count);
}
-std::list<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>> Blockchain::get_alternative_chains() const
+std::vector<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>> Blockchain::get_alternative_chains() const
{
- std::list<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>> chains;
+ std::vector<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>> 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: m_alternative_chains)
+ 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 +4899,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 +4916,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
-static const char expected_block_hashes_hash[] = "cfca50ea0c87718ac92a14654c60d7ee8f6453e2765b329b40d10da4ed85a4f2";
+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..d95f2dceb 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
};
@@ -961,9 +950,9 @@ namespace cryptonote
/**
* @brief returns a set of known alternate chains
*
- * @return a list of chains
+ * @return a vector of chains
*/
- std::list<std::pair<block_extended_info,std::vector<crypto::hash>>> get_alternative_chains() const;
+ std::vector<std::pair<block_extended_info,std::vector<crypto::hash>>> get_alternative_chains() const;
void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const txpool_tx_meta_t &meta);
void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t &meta);
@@ -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> &timestamps, 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> &timestamps, 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..a3a92ab60 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 ? &regtest_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");
@@ -1833,7 +1844,7 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
bool core::check_block_rate()
{
- if (m_offline || m_nettype == FAKECHAIN || m_target_blockchain_height > get_current_blockchain_height())
+ if (m_offline || m_nettype == FAKECHAIN || m_target_blockchain_height > get_current_blockchain_height() || m_target_blockchain_height == 0)
{
MDEBUG("Not checking block rate, offline or syncing");
return true;
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 10198a3d3..e95350f76 100644
--- a/src/cryptonote_core/tx_sanity_check.cpp
+++ b/src/cryptonote_core/tx_sanity_check.cpp
@@ -72,7 +72,7 @@ bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob
if (n_indices <= 10)
{
- MERROR("n_indices is only " << n_indices);
+ MDEBUG("n_indices is only " << n_indices << ", not checking");
return true;
}
@@ -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.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index b38407840..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();
@@ -928,13 +934,12 @@ 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)");
-
- if (arg.blocks.size() + arg.txs.size() > CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT)
+ 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() + arg.txs.size() << ") expected not more then "
+ << arg.blocks.size() << ") expected not more then "
<< CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT);
drop_connection(context, false, false);
return 1;
@@ -947,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);
@@ -973,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;
@@ -981,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();
@@ -1942,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..924447701 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;
@@ -673,11 +674,38 @@ bool t_command_parser_executor::alt_chain_info(const std::vector<std::string>& a
{
if(args.size() > 1)
{
- std::cout << "usage: alt_chain_info [block_hash]" << std::endl;
+ std::cout << "usage: alt_chain_info [block_hash|>N|-N]" << std::endl;
return false;
}
- return m_executor.alt_chain_info(args.size() == 1 ? args[0] : "");
+ std::string tip;
+ size_t above = 0;
+ uint64_t last_blocks = 0;
+ if (args.size() == 1)
+ {
+ if (args[0].size() > 0 && args[0][0] == '>')
+ {
+ if (!epee::string_tools::get_xtype_from_string(above, args[0].c_str() + 1))
+ {
+ std::cout << "invalid above parameter" << std::endl;
+ return false;
+ }
+ }
+ else if (args[0].size() > 0 && args[0][0] == '-')
+ {
+ if (!epee::string_tools::get_xtype_from_string(last_blocks, args[0].c_str() + 1))
+ {
+ std::cout << "invalid last_blocks parameter" << std::endl;
+ return false;
+ }
+ }
+ else
+ {
+ tip = args[0];
+ }
+ }
+
+ return m_executor.alt_chain_info(tip, above, last_blocks);
}
bool t_command_parser_executor::print_blockchain_dynamic_stats(const std::vector<std::string>& args)
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..dbf0409e5 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -35,6 +35,7 @@
#include "daemon/rpc_command_executor.h"
#include "rpc/core_rpc_server_commands_defs.h"
#include "cryptonote_core/cryptonote_core.h"
+#include "cryptonote_basic/difficulty.h"
#include "cryptonote_basic/hardfork.h"
#include <boost/format.hpp>
#include <ctime>
@@ -46,6 +47,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,8 +68,8 @@ namespace {
std::string id_str;
std::string port_str;
- std::string elapsed = 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::string elapsed = peer.last_seen == 0 ? "never" : epee::misc_utils::get_time_interval_string(now - last_seen);
+ std::string ip_str = peer.ip != 0 ? epee::string_tools::get_ip_string_from_int32(peer.ip) : std::string("[") + peer.host + "]";
std::stringstream peer_id_str;
peer_id_str << std::hex << std::setw(16) << peer.id;
peer_id_str >> id_str;
@@ -76,7 +90,7 @@ namespace {
<< "height: " << boost::lexical_cast<std::string>(header.height) << std::endl
<< "depth: " << boost::lexical_cast<std::string>(header.depth) << std::endl
<< "hash: " << header.hash << std::endl
- << "difficulty: " << boost::lexical_cast<std::string>(header.difficulty) << std::endl
+ << "difficulty: " << header.wide_difficulty << std::endl
<< "POW hash: " << header.pow_hash << std::endl
<< "block size: " << header.block_size << std::endl
<< "block weight: " << header.block_weight << std::endl
@@ -337,18 +351,41 @@ bool t_rpc_command_executor::show_difficulty() {
tools::success_msg_writer() << "BH: " << res.height
<< ", TH: " << res.top_block_hash
- << ", DIFF: " << res.difficulty
- << ", HR: " << res.difficulty / res.target << " H/s";
+ << ", DIFF: " << res.wide_difficulty
+ << ", CUM_DIFF: " << res.wide_cumulative_difficulty
+ << ", HR: " << cryptonote::difficulty_type(res.wide_difficulty) / res.target << " H/s";
return true;
}
-static std::string get_mining_speed(uint64_t hr)
+static void get_metric_prefix(cryptonote::difficulty_type hr, double& hr_d, char& prefix)
+{
+ if (hr < 1000)
+ {
+ prefix = 0;
+ return;
+ }
+ static const char metric_prefixes[4] = { 'k', 'M', 'G', 'T' };
+ for (size_t i = 0; i < sizeof(metric_prefixes); ++i)
+ {
+ if (hr < 1000000)
+ {
+ hr_d = hr.convert_to<double>() / 1000;
+ prefix = metric_prefixes[i];
+ return;
+ }
+ hr /= 1000;
+ }
+ prefix = 0;
+}
+
+static std::string get_mining_speed(cryptonote::difficulty_type hr)
{
- if (hr>1e9) return (boost::format("%.2f GH/s") % (hr/1e9)).str();
- if (hr>1e6) return (boost::format("%.2f MH/s") % (hr/1e6)).str();
- if (hr>1e3) return (boost::format("%.2f kH/s") % (hr/1e3)).str();
- return (boost::format("%.0f H/s") % hr).str();
+ double hr_d;
+ char prefix;
+ get_metric_prefix(hr, hr_d, prefix);
+ if (prefix == 0) return (boost::format("%.0f H/s") % hr).str();
+ return (boost::format("%.2f %cH/s") % hr_d % prefix).str();
}
static std::string get_fork_extra_info(uint64_t t, uint64_t now, uint64_t block_time)
@@ -465,7 +502,7 @@ bool t_rpc_command_executor::show_status() {
% (ires.testnet ? "testnet" : ires.stagenet ? "stagenet" : "mainnet")
% bootstrap_msg
% (!has_mining_info ? "mining info unavailable" : mining_busy ? "syncing" : mres.active ? ( ( mres.is_background_mining_enabled ? "smart " : "" ) + std::string("mining at ") + get_mining_speed(mres.speed)) : "not mining")
- % get_mining_speed(ires.difficulty / ires.target)
+ % get_mining_speed(cryptonote::difficulty_type(ires.wide_difficulty) / ires.target)
% (unsigned)hfres.version
% get_fork_extra_info(hfres.earliest_height, net_height, ires.target)
% (hfres.state == cryptonote::HardFork::Ready ? "up to date" : hfres.state == cryptonote::HardFork::UpdateNeeded ? "update needed" : "out of date, likely forked")
@@ -589,6 +626,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 +647,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
@@ -726,7 +765,7 @@ bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, u
<< ", size: " << header.block_size << ", weight: " << header.block_weight << " (long term " << header.long_term_weight << "), transactions: " << header.num_txes << std::endl
<< "major version: " << (unsigned)header.major_version << ", minor version: " << (unsigned)header.minor_version << std::endl
<< "block id: " << header.hash << ", previous block id: " << header.prev_hash << std::endl
- << "difficulty: " << header.difficulty << ", nonce " << header.nonce << ", reward " << cryptonote::print_money(header.reward) << std::endl;
+ << "difficulty: " << header.wide_difficulty << ", nonce " << header.nonce << ", reward " << cryptonote::print_money(header.reward) << std::endl;
first = false;
}
@@ -1466,13 +1505,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 +1533,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,64 +1567,12 @@ bool t_rpc_command_executor::in_peers(uint64_t limit)
}
}
- tools::msg_writer() << "Max number of in peers set to " << limit << std::endl;
+ 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;
return true;
}
-bool t_rpc_command_executor::start_save_graph()
-{
- cryptonote::COMMAND_RPC_START_SAVE_GRAPH::request req;
- cryptonote::COMMAND_RPC_START_SAVE_GRAPH::response res;
- std::string fail_message = "Unsuccessful";
-
- if (m_is_rpc)
- {
- if (!m_rpc_client->rpc_request(req, res, "/start_save_graph", fail_message.c_str()))
- {
- return true;
- }
- }
-
- else
- {
- if (!m_rpc_server->on_start_save_graph(req, res) || res.status != CORE_RPC_STATUS_OK)
- {
- tools::fail_msg_writer() << make_error(fail_message, res.status);
- return true;
- }
- }
-
- tools::success_msg_writer() << "Saving graph is now on";
- return true;
-}
-
-bool t_rpc_command_executor::stop_save_graph()
-{
- cryptonote::COMMAND_RPC_STOP_SAVE_GRAPH::request req;
- cryptonote::COMMAND_RPC_STOP_SAVE_GRAPH::response res;
- std::string fail_message = "Unsuccessful";
-
- if (m_is_rpc)
- {
- if (!m_rpc_client->rpc_request(req, res, "/stop_save_graph", fail_message.c_str()))
- {
- return true;
- }
- }
-
- else
- {
- if (!m_rpc_server->on_stop_save_graph(req, res) || res.status != CORE_RPC_STATUS_OK)
- {
- tools::fail_msg_writer() << make_error(fail_message, res.status);
- return true;
- }
- }
- tools::success_msg_writer() << "Saving graph is now off";
- return true;
-}
-
bool t_rpc_command_executor::hard_fork_info(uint8_t version)
{
cryptonote::COMMAND_RPC_HARD_FORK_INFO::request req;
@@ -1641,14 +1631,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 +1646,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 +1671,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 +1679,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 +1704,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;
@@ -1824,7 +1841,7 @@ bool t_rpc_command_executor::print_coinbase_tx_sum(uint64_t height, uint64_t cou
return true;
}
-bool t_rpc_command_executor::alt_chain_info(const std::string &tip)
+bool t_rpc_command_executor::alt_chain_info(const std::string &tip, size_t above, uint64_t last_blocks)
{
cryptonote::COMMAND_RPC_GET_INFO::request ireq;
cryptonote::COMMAND_RPC_GET_INFO::response ires;
@@ -1861,16 +1878,31 @@ bool t_rpc_command_executor::alt_chain_info(const std::string &tip)
if (tip.empty())
{
- tools::msg_writer() << boost::lexical_cast<std::string>(res.chains.size()) << " alternate chains found:";
- for (const auto &chain: res.chains)
+ auto chains = res.chains;
+ std::sort(chains.begin(), chains.end(), [](const cryptonote::COMMAND_RPC_GET_ALTERNATE_CHAINS::chain_info &info0, cryptonote::COMMAND_RPC_GET_ALTERNATE_CHAINS::chain_info &info1){ return info0.height < info1.height; });
+ std::vector<size_t> display;
+ for (size_t i = 0; i < chains.size(); ++i)
{
- uint64_t start_height = (chain.height - chain.length + 1);
+ const auto &chain = chains[i];
+ if (chain.length <= above)
+ continue;
+ const uint64_t start_height = (chain.height - chain.length + 1);
+ if (last_blocks > 0 && ires.height - 1 - start_height >= last_blocks)
+ continue;
+ display.push_back(i);
+ }
+ tools::msg_writer() << boost::lexical_cast<std::string>(display.size()) << " alternate chains found:";
+ for (const size_t idx: display)
+ {
+ const auto &chain = chains[idx];
+ const uint64_t start_height = (chain.height - chain.length + 1);
tools::msg_writer() << chain.length << " blocks long, from height " << start_height << " (" << (ires.height - start_height - 1)
- << " deep), diff " << chain.difficulty << ": " << chain.block_hash;
+ << " deep), diff " << chain.wide_difficulty << ": " << chain.block_hash;
}
}
else
{
+ const uint64_t now = time(NULL);
const auto i = std::find_if(res.chains.begin(), res.chains.end(), [&tip](cryptonote::COMMAND_RPC_GET_ALTERNATE_CHAINS::chain_info &info){ return info.block_hash == tip; });
if (i != res.chains.end())
{
@@ -1878,10 +1910,53 @@ bool t_rpc_command_executor::alt_chain_info(const std::string &tip)
tools::success_msg_writer() << "Found alternate chain with tip " << tip;
uint64_t start_height = (chain.height - chain.length + 1);
tools::msg_writer() << chain.length << " blocks long, from height " << start_height << " (" << (ires.height - start_height - 1)
- << " deep), diff " << chain.difficulty << ":";
+ << " deep), diff " << chain.wide_difficulty << ":";
for (const std::string &block_id: chain.block_hashes)
tools::msg_writer() << " " << block_id;
tools::msg_writer() << "Chain parent on main chain: " << chain.main_chain_parent_block;
+ cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request bhreq;
+ cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response bhres;
+ bhreq.hashes = chain.block_hashes;
+ bhreq.hashes.push_back(chain.main_chain_parent_block);
+ bhreq.fill_pow_hash = false;
+ if (m_is_rpc)
+ {
+ if (!m_rpc_client->json_rpc_request(bhreq, bhres, "getblockheaderbyhash", fail_message.c_str()))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if (!m_rpc_server->on_get_block_header_by_hash(bhreq, bhres, error_resp))
+ {
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
+ return true;
+ }
+ }
+ if (bhres.block_headers.size() != chain.length + 1)
+ {
+ tools::fail_msg_writer() << "Failed to get block header info for alt chain";
+ return true;
+ }
+ uint64_t t0 = bhres.block_headers.front().timestamp, t1 = t0;
+ for (const cryptonote::block_header_response &block_header: bhres.block_headers)
+ {
+ t0 = std::min<uint64_t>(t0, block_header.timestamp);
+ t1 = std::max<uint64_t>(t1, block_header.timestamp);
+ }
+ const uint64_t dt = t1 - t0;
+ const uint64_t age = std::max(dt, t0 < now ? now - t0 : 0);
+ tools::msg_writer() << "Age: " << tools::get_human_readable_timespan(age);
+ if (chain.length > 1)
+ {
+ tools::msg_writer() << "Time span: " << tools::get_human_readable_timespan(dt);
+ cryptonote::difficulty_type start_difficulty = bhres.block_headers.back().difficulty;
+ if (start_difficulty > 0)
+ tools::msg_writer() << "Approximated " << 100.f * DIFFICULTY_TARGET_V2 * chain.length / dt << "% of network hash rate";
+ else
+ tools::fail_msg_writer() << "Bad cmumulative difficulty reported by dameon";
+ }
}
else
tools::fail_msg_writer() << "Block hash " << tip << " is not the tip of any known alternate chain";
@@ -1939,7 +2014,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
}
}
- tools::msg_writer() << "Height: " << ires.height << ", diff " << ires.difficulty << ", cum. diff " << ires.cumulative_difficulty
+ tools::msg_writer() << "Height: " << ires.height << ", diff " << ires.wide_difficulty << ", cum. diff " << ires.wide_cumulative_difficulty
<< ", target " << ires.target << " sec" << ", dyn fee " << cryptonote::print_money(feres.fee) << "/" << (hfres.enabled ? "byte" : "kB");
if (nblocks > 0)
@@ -1966,7 +2041,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
}
}
- double avgdiff = 0;
+ cryptonote::difficulty_type avgdiff = 0;
double avgnumtxes = 0;
double avgreward = 0;
std::vector<uint64_t> weights;
@@ -1975,7 +2050,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
std::vector<unsigned> major_versions(256, 0), minor_versions(256, 0);
for (const auto &bhr: bhres.headers)
{
- avgdiff += bhr.difficulty;
+ avgdiff += cryptonote::difficulty_type(bhr.wide_difficulty);
avgnumtxes += bhr.num_txes;
avgreward += bhr.reward;
weights.push_back(bhr.block_weight);
@@ -1990,7 +2065,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
avgnumtxes /= nblocks;
avgreward /= nblocks;
uint64_t median_block_weight = epee::misc_utils::median(weights);
- tools::msg_writer() << "Last " << nblocks << ": avg. diff " << (uint64_t)avgdiff << ", " << (latest - earliest) / nblocks << " avg sec/block, avg num txes " << avgnumtxes
+ tools::msg_writer() << "Last " << nblocks << ": avg. diff " << avgdiff << ", " << (latest - earliest) / nblocks << " avg sec/block, avg num txes " << avgnumtxes
<< ", avg. reward " << cryptonote::print_money(avgreward) << ", median block weight " << median_block_weight;
unsigned int max_major = 256, max_minor = 256;
diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h
index df2894d09..f3ed48319 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);
@@ -147,7 +145,7 @@ public:
bool print_coinbase_tx_sum(uint64_t height, uint64_t count);
- bool alt_chain_info(const std::string &tip);
+ bool alt_chain_info(const std::string &tip, size_t above, uint64_t last_blocks);
bool print_blockchain_dynamic_stats(uint64_t nblocks);
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..7b74f2b75 100644
--- a/src/net/parse.cpp
+++ b/src/net/parse.cpp
@@ -34,28 +34,92 @@
namespace net
{
+ void get_network_address_host_and_port(const std::string& address, std::string& host, std::string& port)
+ {
+ // require ipv6 address format "[addr:addr:addr:...:addr]:port"
+ if (address.find(']') != std::string::npos)
+ {
+ host = address.substr(1, address.rfind(']') - 1);
+ if ((host.size() + 2) < address.size())
+ {
+ port = address.substr(address.rfind(':') + 1);
+ }
+ }
+ else
+ {
+ host = address.substr(0, address.rfind(':'));
+ if (host.size() < address.size())
+ {
+ port = address.substr(host.size() + 1);
+ }
+ }
+ }
+
expect<epee::net_utils::network_address>
get_network_address(const boost::string_ref address, const std::uint16_t default_port)
{
- const boost::string_ref host = address.substr(0, address.rfind(':'));
+ std::string host_str = "";
+ std::string port_str = "";
- if (host.empty())
+ bool ipv6 = false;
+
+ get_network_address_host_and_port(std::string(address), host_str, port_str);
+
+ boost::string_ref host_str_ref(host_str);
+ boost::string_ref port_str_ref(port_str);
+
+ if (host_str.empty())
return make_error_code(net::error::invalid_host);
- if (host.ends_with(".onion"))
+ if (host_str_ref.ends_with(".onion"))
return tor_address::make(address, default_port);
- if (host.ends_with(".i2p"))
+ if (host_str_ref.ends_with(".i2p"))
return i2p_address::make(address, default_port);
+ boost::system::error_code ec;
+ boost::asio::ip::address_v6 v6 = boost::asio::ip::make_address_v6(host_str, ec);
+ ipv6 = !ec;
+
std::uint16_t port = default_port;
- if (host.size() < address.size())
+ if (port_str.size())
{
- if (!epee::string_tools::get_xtype_from_string(port, std::string{address.substr(host.size() + 1)}))
+ if (!epee::string_tools::get_xtype_from_string(port, port_str))
return make_error_code(net::error::invalid_port);
}
- std::uint32_t ip = 0;
- if (epee::string_tools::get_ip_int32_from_string(ip, std::string{host}))
- return {epee::net_utils::ipv4_network_address{ip, port}};
+ if (ipv6)
+ {
+ return {epee::net_utils::ipv6_network_address{v6, port}};
+ }
+ else
+ {
+ std::uint32_t ip = 0;
+ if (epee::string_tools::get_ip_int32_from_string(ip, host_str))
+ 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..0d8fda711 100644
--- a/src/net/parse.h
+++ b/src/net/parse.h
@@ -36,6 +36,8 @@
namespace net
{
+ void get_network_address_host_and_port(const std::string& address, std::string& host, std::string& port);
+
/*!
Identifies onion, i2p and IPv4 addresses and returns them as a generic
`network_address`. If the type is unsupported, it might be a hostname,
@@ -50,5 +52,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..bb51be242 100644
--- a/src/p2p/net_node.cpp
+++ b/src/p2p/net_node.cpp
@@ -108,10 +108,11 @@ namespace
namespace nodetool
{
- const command_line::arg_descriptor<std::string> arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol", "0.0.0.0"};
+ const command_line::arg_descriptor<std::string> arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol (IPv4)", "0.0.0.0"};
+ const command_line::arg_descriptor<std::string> arg_p2p_bind_ipv6_address = {"p2p-bind-ipv6-address", "Interface for p2p network protocol (IPv6)", "::"};
const command_line::arg_descriptor<std::string, false, true, 2> arg_p2p_bind_port = {
"p2p-bind-port"
- , "Port for p2p network protocol"
+ , "Port for p2p network protocol (IPv4)"
, std::to_string(config::P2P_DEFAULT_PORT)
, {{ &cryptonote::arg_testnet_on, &cryptonote::arg_stagenet_on }}
, [](std::array<bool, 2> testnet_stagenet, bool defaulted, std::string val)->std::string {
@@ -122,6 +123,20 @@ namespace nodetool
return val;
}
};
+ const command_line::arg_descriptor<std::string, false, true, 2> arg_p2p_bind_port_ipv6 = {
+ "p2p-bind-port-ipv6"
+ , "Port for p2p network protocol (IPv6)"
+ , std::to_string(config::P2P_DEFAULT_PORT)
+ , {{ &cryptonote::arg_testnet_on, &cryptonote::arg_stagenet_on }}
+ , [](std::array<bool, 2> testnet_stagenet, bool defaulted, std::string val)->std::string {
+ if (testnet_stagenet[0] && defaulted)
+ return std::to_string(config::testnet::P2P_DEFAULT_PORT);
+ else if (testnet_stagenet[1] && defaulted)
+ return std::to_string(config::stagenet::P2P_DEFAULT_PORT);
+ return val;
+ }
+ };
+
const command_line::arg_descriptor<uint32_t> arg_p2p_external_port = {"p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0};
const command_line::arg_descriptor<bool> arg_p2p_allow_local_ip = {"allow-local-ip", "Allow local ip add to peer list, mostly in debug purposes"};
const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_peer = {"add-peer", "Manually add peer to local peerlist"};
@@ -135,6 +150,9 @@ 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<bool> arg_p2p_use_ipv6 = {"p2p-use-ipv6", "Enable IPv6 for p2p", false};
+ const command_line::arg_descriptor<bool> arg_p2p_require_ipv4 = {"p2p-require-ipv4", "Require successful IPv4 bind for p2p", true};
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 +161,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..cf6b2c67b 100644
--- a/src/p2p/net_node.h
+++ b/src/p2p/net_node.h
@@ -151,7 +151,9 @@ namespace nodetool
: m_connect(nullptr),
m_net_server(epee::net_utils::e_connection_type_P2P),
m_bind_ip(),
+ m_bind_ipv6_address(),
m_port(),
+ m_port_ipv6(),
m_our_address(),
m_peerlist(),
m_config{},
@@ -167,7 +169,9 @@ namespace nodetool
: m_connect(nullptr),
m_net_server(public_service, epee::net_utils::e_connection_type_P2P),
m_bind_ip(),
+ m_bind_ipv6_address(),
m_port(),
+ m_port_ipv6(),
m_our_address(),
m_peerlist(),
m_config{},
@@ -182,7 +186,9 @@ namespace nodetool
connect_func* m_connect;
net_server m_net_server;
std::string m_bind_ip;
+ std::string m_bind_ipv6_address;
std::string m_port;
+ std::string m_port_ipv6;
epee::net_utils::network_address m_our_address; // in anonymity networks
peerlist_manager m_peerlist;
config m_config;
@@ -205,6 +211,13 @@ namespace nodetool
}
};
+ enum igd_t
+ {
+ no_igd,
+ igd,
+ delayed_igd,
+ };
+
public:
typedef t_payload_net_handler payload_net_handler;
@@ -214,9 +227,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 +257,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 +337,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(
@@ -345,7 +363,13 @@ namespace nodetool
bool is_peer_used(const peerlist_entry& peer);
bool is_peer_used(const anchor_peerlist_entry& peer);
bool is_addr_connected(const epee::net_utils::network_address& peer);
- void add_upnp_port_mapping(uint32_t port);
+ void add_upnp_port_mapping_impl(uint32_t port, bool ipv6=false);
+ void add_upnp_port_mapping_v4(uint32_t port);
+ void add_upnp_port_mapping_v6(uint32_t port);
+ void add_upnp_port_mapping(uint32_t port, bool ipv4=true, bool ipv6=false);
+ void delete_upnp_port_mapping_impl(uint32_t port, bool ipv6=false);
+ void delete_upnp_port_mapping_v4(uint32_t port);
+ void delete_upnp_port_mapping_v6(uint32_t port);
void delete_upnp_port_mapping(uint32_t port);
template<class t_callback>
bool try_ping(basic_node_data& node_data, p2p_connection_context& context, const t_callback &cb);
@@ -396,12 +420,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;
@@ -413,13 +431,15 @@ namespace nodetool
bool m_have_address;
bool m_first_connection_maker_call;
uint32_t m_listening_port;
+ uint32_t m_listening_port_ipv6;
uint32_t m_external_port;
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;
+ bool m_use_ipv6;
+ bool m_require_ipv4;
std::atomic<bool> is_closing;
std::unique_ptr<boost::thread> mPeersLoggerThread;
//critical_section m_connections_lock;
@@ -461,8 +481,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;
@@ -479,7 +500,11 @@ namespace nodetool
const int64_t default_limit_up = P2P_DEFAULT_LIMIT_RATE_UP; // kB/s
const int64_t default_limit_down = P2P_DEFAULT_LIMIT_RATE_DOWN; // kB/s
extern const command_line::arg_descriptor<std::string> arg_p2p_bind_ip;
+ extern const command_line::arg_descriptor<std::string> arg_p2p_bind_ipv6_address;
extern const command_line::arg_descriptor<std::string, false, true, 2> arg_p2p_bind_port;
+ extern const command_line::arg_descriptor<std::string, false, true, 2> arg_p2p_bind_port_ipv6;
+ extern const command_line::arg_descriptor<bool> arg_p2p_use_ipv6;
+ extern const command_line::arg_descriptor<bool> arg_p2p_require_ipv4;
extern const command_line::arg_descriptor<uint32_t> arg_p2p_external_port;
extern const command_line::arg_descriptor<bool> arg_p2p_allow_local_ip;
extern const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_peer;
@@ -492,6 +517,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 +526,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..00467132a 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -93,7 +93,11 @@ namespace nodetool
void node_server<t_payload_net_handler>::init_options(boost::program_options::options_description& desc)
{
command_line::add_arg(desc, arg_p2p_bind_ip);
+ command_line::add_arg(desc, arg_p2p_bind_ipv6_address);
command_line::add_arg(desc, arg_p2p_bind_port, false);
+ command_line::add_arg(desc, arg_p2p_bind_port_ipv6, false);
+ command_line::add_arg(desc, arg_p2p_use_ipv6);
+ command_line::add_arg(desc, arg_p2p_require_ipv4);
command_line::add_arg(desc, arg_p2p_external_port);
command_line::add_arg(desc, arg_p2p_allow_local_ip);
command_line::add_arg(desc, arg_p2p_add_peer);
@@ -105,13 +109,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 +159,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 +224,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 +254,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 +263,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())
@@ -253,12 +345,44 @@ namespace nodetool
network_zone& public_zone = m_network_zones[epee::net_utils::zone::public_];
public_zone.m_connect = &public_connect;
public_zone.m_bind_ip = command_line::get_arg(vm, arg_p2p_bind_ip);
+ public_zone.m_bind_ipv6_address = command_line::get_arg(vm, arg_p2p_bind_ipv6_address);
public_zone.m_port = command_line::get_arg(vm, arg_p2p_bind_port);
+ public_zone.m_port_ipv6 = command_line::get_arg(vm, arg_p2p_bind_port_ipv6);
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);
+ m_use_ipv6 = command_line::get_arg(vm, arg_p2p_use_ipv6);
+ m_require_ipv4 = command_line::get_arg(vm, arg_p2p_require_ipv4);
if (command_line::has_arg(vm, arg_p2p_add_peer))
{
@@ -292,11 +416,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))
@@ -407,12 +526,17 @@ namespace nodetool
std::string host = addr;
std::string port = std::to_string(default_port);
- size_t pos = addr.find_last_of(':');
- if (std::string::npos != pos)
+ size_t colon_pos = addr.find_last_of(':');
+ size_t dot_pos = addr.find_last_of('.');
+ size_t square_brace_pos = addr.find('[');
+
+ // IPv6 will have colons regardless. IPv6 and IPv4 address:port will have a colon but also either a . or a [
+ // as IPv6 addresses specified as address:port are to be specified as "[addr:addr:...:addr]:port"
+ // One may also specify an IPv6 address as simply "[addr:addr:...:addr]" without the port; in that case
+ // the square braces will be stripped here.
+ if ((std::string::npos != colon_pos && std::string::npos != dot_pos) || std::string::npos != square_brace_pos)
{
- CHECK_AND_ASSERT_MES(addr.length() - 1 != pos && 0 != pos, false, "Failed to parse seed address from string: '" << addr << '\'');
- host = addr.substr(0, pos);
- port = addr.substr(pos + 1);
+ net::get_network_address_host_and_port(addr, host, port);
}
MINFO("Resolving node address: host=" << host << ", port=" << port);
@@ -435,7 +559,9 @@ namespace nodetool
}
else
{
- MWARNING("IPv6 unsupported, skip '" << host << "' -> " << endpoint.address().to_v6().to_string(ec));
+ epee::net_utils::network_address na{epee::net_utils::ipv6_network_address{endpoint.address().to_v6(), endpoint.port()}};
+ seed_nodes.push_back(na);
+ MINFO("Added node: " << na.str());
}
}
return true;
@@ -669,21 +795,40 @@ namespace nodetool
if (!zone.second.m_bind_ip.empty())
{
+ std::string ipv6_addr = "";
+ std::string ipv6_port = "";
zone.second.m_net_server.set_connection_filter(this);
- MINFO("Binding on " << zone.second.m_bind_ip << ":" << zone.second.m_port);
- res = zone.second.m_net_server.init_server(zone.second.m_port, zone.second.m_bind_ip, epee::net_utils::ssl_support_t::e_ssl_support_disabled);
+ MINFO("Binding (IPv4) on " << zone.second.m_bind_ip << ":" << zone.second.m_port);
+ if (!zone.second.m_bind_ipv6_address.empty() && m_use_ipv6)
+ {
+ ipv6_addr = zone.second.m_bind_ipv6_address;
+ ipv6_port = zone.second.m_port_ipv6;
+ MINFO("Binding (IPv6) on " << zone.second.m_bind_ipv6_address << ":" << zone.second.m_port_ipv6);
+ }
+ res = zone.second.m_net_server.init_server(zone.second.m_port, zone.second.m_bind_ip, ipv6_port, ipv6_addr, m_use_ipv6, m_require_ipv4, epee::net_utils::ssl_support_t::e_ssl_support_disabled);
CHECK_AND_ASSERT_MES(res, false, "Failed to bind server");
}
}
m_listening_port = public_zone.m_net_server.get_binded_port();
- MLOG_GREEN(el::Level::Info, "Net service bound to " << public_zone.m_bind_ip << ":" << m_listening_port);
+ MLOG_GREEN(el::Level::Info, "Net service bound (IPv4) to " << public_zone.m_bind_ip << ":" << m_listening_port);
+ if (m_use_ipv6)
+ {
+ m_listening_port_ipv6 = public_zone.m_net_server.get_binded_port_ipv6();
+ MLOG_GREEN(el::Level::Info, "Net service bound (IPv6) to " << public_zone.m_bind_ipv6_address << ":" << m_listening_port_ipv6);
+ }
if(m_external_port)
MDEBUG("External port defined as " << m_external_port);
// add UPnP port mapping
- if(!m_no_igd)
- add_upnp_port_mapping(m_listening_port);
+ if(m_igd == igd)
+ {
+ add_upnp_port_mapping_v4(m_listening_port);
+ if (m_use_ipv6)
+ {
+ add_upnp_port_mapping_v6(m_listening_port_ipv6);
+ }
+ }
return res;
}
@@ -776,7 +921,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 +1089,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 +1238,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 +1298,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 +1375,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 +1764,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;
@@ -1839,19 +2031,43 @@ namespace nodetool
if(!node_data.my_port)
return false;
- CHECK_AND_ASSERT_MES(context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id(), false,
- "Only IPv4 addresses are supported here");
+ bool address_ok = (context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id() || context.m_remote_address.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id());
+ CHECK_AND_ASSERT_MES(address_ok, false,
+ "Only IPv4 or IPv6 addresses are supported here");
const epee::net_utils::network_address na = context.m_remote_address;
- uint32_t actual_ip = na.as<const epee::net_utils::ipv4_network_address>().ip();
+ std::string ip;
+ uint32_t ipv4_addr;
+ boost::asio::ip::address_v6 ipv6_addr;
+ bool is_ipv4;
+ if (na.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
+ {
+ ipv4_addr = na.as<const epee::net_utils::ipv4_network_address>().ip();
+ ip = epee::string_tools::get_ip_string_from_int32(ipv4_addr);
+ is_ipv4 = true;
+ }
+ else
+ {
+ ipv6_addr = na.as<const epee::net_utils::ipv6_network_address>().ip();
+ ip = ipv6_addr.to_string();
+ is_ipv4 = false;
+ }
network_zone& zone = m_network_zones.at(na.get_zone());
if(!zone.m_peerlist.is_host_allowed(context.m_remote_address))
return false;
- std::string ip = epee::string_tools::get_ip_string_from_int32(actual_ip);
std::string port = epee::string_tools::num_to_string_fast(node_data.my_port);
- epee::net_utils::network_address address{epee::net_utils::ipv4_network_address(actual_ip, node_data.my_port)};
+
+ epee::net_utils::network_address address;
+ if (is_ipv4)
+ {
+ address = epee::net_utils::network_address{epee::net_utils::ipv4_network_address(ipv4_addr, node_data.my_port)};
+ }
+ else
+ {
+ address = epee::net_utils::network_address{epee::net_utils::ipv6_network_address(ipv6_addr, node_data.my_port)};
+ }
peerid_type pr = node_data.peer_id;
bool r = zone.m_net_server.connect_async(ip, port, zone.m_config.m_net_config.ping_connection_timeout, [cb, /*context,*/ address, pr, this](
const typename net_server::t_connection_context& ping_context,
@@ -2035,12 +2251,19 @@ namespace nodetool
//try ping to be sure that we can add this peer to peer_list
try_ping(arg.node_data, context, [peer_id_l, port_l, context, this]()
{
- CHECK_AND_ASSERT_MES(context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id(), void(),
- "Only IPv4 addresses are supported here");
+ CHECK_AND_ASSERT_MES((context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id() || context.m_remote_address.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id()), void(),
+ "Only IPv4 or IPv6 addresses are supported here");
//called only(!) if success pinged, update local peerlist
peerlist_entry pe;
const epee::net_utils::network_address na = context.m_remote_address;
- pe.adr = epee::net_utils::ipv4_network_address(na.as<epee::net_utils::ipv4_network_address>().ip(), port_l);
+ if (context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
+ {
+ pe.adr = epee::net_utils::ipv4_network_address(na.as<epee::net_utils::ipv4_network_address>().ip(), port_l);
+ }
+ else
+ {
+ pe.adr = epee::net_utils::ipv6_network_address(na.as<epee::net_utils::ipv6_network_address>().ip(), port_l);
+ }
time_t last_seen;
time(&last_seen);
pe.last_seen = static_cast<int64_t>(last_seen);
@@ -2209,20 +2432,30 @@ namespace nodetool
auto public_zone = m_network_zones.find(epee::net_utils::zone::public_);
if (public_zone != m_network_zones.end())
{
- const auto current = public_zone->second.m_config.m_net_config.max_out_connection_count;
+ const auto current = public_zone->second.m_net_server.get_config_object().get_out_connections_count();
public_zone->second.m_config.m_net_config.max_out_connection_count = count;
if(current > count)
public_zone->second.m_net_server.get_config_object().del_out_connections(current - count);
+ m_payload_handler.set_max_out_peers(count);
}
}
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_);
if (public_zone != m_network_zones.end())
{
- const auto current = public_zone->second.m_config.m_net_config.max_in_connection_count;
+ const auto current = public_zone->second.m_net_server.get_config_object().get_in_connections_count();
public_zone->second.m_config.m_net_config.max_in_connection_count = count;
if(current > count)
public_zone->second.m_net_server.get_config_object().del_in_connections(current - count);
@@ -2230,6 +2463,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){
@@ -2389,16 +2631,19 @@ namespace nodetool
}
template<class t_payload_net_handler>
- void node_server<t_payload_net_handler>::add_upnp_port_mapping(uint32_t port)
+ void node_server<t_payload_net_handler>::add_upnp_port_mapping_impl(uint32_t port, bool ipv6) // if ipv6 false, do ipv4
{
- MDEBUG("Attempting to add IGD port mapping.");
+ std::string ipversion = ipv6 ? "(IPv6)" : "(IPv4)";
+ MDEBUG("Attempting to add IGD port mapping " << ipversion << ".");
int result;
+ const int ipv6_arg = ipv6 ? 1 : 0;
+
#if MINIUPNPC_API_VERSION > 13
// default according to miniupnpc.h
unsigned char ttl = 2;
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, ttl, &result);
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, ttl, &result);
#else
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result);
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, &result);
#endif
UPNPUrls urls;
IGDdatas igdData;
@@ -2435,16 +2680,38 @@ namespace nodetool
}
template<class t_payload_net_handler>
- void node_server<t_payload_net_handler>::delete_upnp_port_mapping(uint32_t port)
+ void node_server<t_payload_net_handler>::add_upnp_port_mapping_v4(uint32_t port)
{
- MDEBUG("Attempting to delete IGD port mapping.");
+ add_upnp_port_mapping_impl(port, false);
+ }
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::add_upnp_port_mapping_v6(uint32_t port)
+ {
+ add_upnp_port_mapping_impl(port, true);
+ }
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::add_upnp_port_mapping(uint32_t port, bool ipv4, bool ipv6)
+ {
+ if (ipv4) add_upnp_port_mapping_v4(port);
+ if (ipv6) add_upnp_port_mapping_v6(port);
+ }
+
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::delete_upnp_port_mapping_impl(uint32_t port, bool ipv6)
+ {
+ std::string ipversion = ipv6 ? "(IPv6)" : "(IPv4)";
+ MDEBUG("Attempting to delete IGD port mapping " << ipversion << ".");
int result;
+ const int ipv6_arg = ipv6 ? 1 : 0;
#if MINIUPNPC_API_VERSION > 13
// default according to miniupnpc.h
unsigned char ttl = 2;
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, ttl, &result);
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, ttl, &result);
#else
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result);
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, &result);
#endif
UPNPUrls urls;
IGDdatas igdData;
@@ -2477,6 +2744,25 @@ namespace nodetool
}
}
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::delete_upnp_port_mapping_v4(uint32_t port)
+ {
+ delete_upnp_port_mapping_impl(port, false);
+ }
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::delete_upnp_port_mapping_v6(uint32_t port)
+ {
+ delete_upnp_port_mapping_impl(port, true);
+ }
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::delete_upnp_port_mapping(uint32_t port)
+ {
+ delete_upnp_port_mapping_v4(port);
+ delete_upnp_port_mapping_v6(port);
+ }
+
template<typename t_payload_net_handler>
boost::optional<p2p_connection_context_t<typename t_payload_net_handler::connection_context>>
node_server<t_payload_net_handler>::socks_connect(network_zone& zone, const epee::net_utils::network_address& remote, epee::net_utils::ssl_support_t ssl_support)
@@ -2495,13 +2781,34 @@ namespace nodetool
boost::optional<p2p_connection_context_t<typename t_payload_net_handler::connection_context>>
node_server<t_payload_net_handler>::public_connect(network_zone& zone, epee::net_utils::network_address const& na, epee::net_utils::ssl_support_t ssl_support)
{
- CHECK_AND_ASSERT_MES(na.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id(), boost::none,
- "Only IPv4 addresses are supported here");
- const epee::net_utils::ipv4_network_address &ipv4 = na.as<const epee::net_utils::ipv4_network_address>();
+ bool is_ipv4 = na.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id();
+ bool is_ipv6 = na.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id();
+ CHECK_AND_ASSERT_MES(is_ipv4 || is_ipv6, boost::none,
+ "Only IPv4 or IPv6 addresses are supported here");
+
+ std::string address;
+ std::string port;
+
+ if (is_ipv4)
+ {
+ const epee::net_utils::ipv4_network_address &ipv4 = na.as<const epee::net_utils::ipv4_network_address>();
+ address = epee::string_tools::get_ip_string_from_int32(ipv4.ip());
+ port = epee::string_tools::num_to_string_fast(ipv4.port());
+ }
+ else if (is_ipv6)
+ {
+ const epee::net_utils::ipv6_network_address &ipv6 = na.as<const epee::net_utils::ipv6_network_address>();
+ address = ipv6.ip().to_string();
+ port = epee::string_tools::num_to_string_fast(ipv6.port());
+ }
+ else
+ {
+ LOG_ERROR("Only IPv4 or IPv6 addresses are supported here");
+ return boost::none;
+ }
typename net_server::t_connection_context con{};
- const bool res = zone.m_net_server.connect(epee::string_tools::get_ip_string_from_int32(ipv4.ip()),
- epee::string_tools::num_to_string_fast(ipv4.port()),
+ const bool res = zone.m_net_server.connect(address, port,
zone.m_config.m_net_config.connection_timeout,
con, "0.0.0.0", ssl_support);
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..68627375a 100644
--- a/src/p2p/net_peerlist.h
+++ b/src/p2p/net_peerlist.h
@@ -278,6 +278,9 @@ namespace nodetool
// was moved to the gray list (if it's not accessibe, which the attacker can check if
// the address accepts incoming connections) or it was the oldest to still fit in the 250 items,
// so its last_seen is old.
+ //
+ // See Cao, Tong et al. "Exploring the Monero Peer-to-Peer Network". https://eprint.iacr.org/2019/411
+ //
const uint32_t pick_depth = anonymize ? depth + depth / 5 : depth;
bs_head.reserve(pick_depth);
for(const peers_indexed::value_type& vl: boost::adaptors::reverse(by_time_index))
@@ -344,8 +347,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 +388,14 @@ namespace nodetool
trim_gray_peerlist();
}else
{
- //update record in white list
- m_peers_gray.replace(by_addr_it_gr, ple);
+ //update record in gray list
+ peerlist_entry new_ple = ple;
+ if (by_addr_it_gr->pruning_seed && ple.pruning_seed == 0) // guard against older nodes not passing pruning info around
+ new_ple.pruning_seed = by_addr_it_gr->pruning_seed;
+ if (by_addr_it_gr->rpc_port && ple.rpc_port == 0) // guard against older nodes not passing RPC port around
+ new_ple.rpc_port = by_addr_it_gr->rpc_port;
+ new_ple.last_seen = by_addr_it_gr->last_seen; // do not overwrite the last seen timestamp, incoming peer list are untrusted
+ m_peers_gray.replace(by_addr_it_gr, new_ple);
}
return true;
CATCH_ENTRY_L0("peerlist_manager::append_with_peer_gray()", false);
diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h
index 32f30adca..05eb36e65 100644
--- a/src/p2p/net_peerlist_boost_serialization.h
+++ b/src/p2p/net_peerlist_boost_serialization.h
@@ -76,6 +76,9 @@ namespace boost
case epee::net_utils::ipv4_network_address::get_type_id():
do_serialize<epee::net_utils::ipv4_network_address>(is_saving, a, na);
break;
+ case epee::net_utils::ipv6_network_address::get_type_id():
+ do_serialize<epee::net_utils::ipv6_network_address>(is_saving, a, na);
+ break;
case net::tor_address::get_type_id():
do_serialize<net::tor_address>(is_saving, a, na);
break;
@@ -99,6 +102,34 @@ namespace boost
}
template <class Archive, class ver_type>
+ inline void serialize(Archive &a, boost::asio::ip::address_v6& v6, const ver_type ver)
+ {
+ if (typename Archive::is_saving())
+ {
+ auto bytes = v6.to_bytes();
+ for (auto &e: bytes) a & e;
+ }
+ else
+ {
+ boost::asio::ip::address_v6::bytes_type bytes;
+ for (auto &e: bytes) a & e;
+ v6 = boost::asio::ip::address_v6(bytes);
+ }
+ }
+
+ template <class Archive, class ver_type>
+ inline void serialize(Archive &a, epee::net_utils::ipv6_network_address& na, const ver_type ver)
+ {
+ boost::asio::ip::address_v6 ip{na.ip()};
+ uint16_t port{na.port()};
+ a & ip;
+ a & port;
+ if (!typename Archive::is_saving())
+ na = epee::net_utils::ipv6_network_address{ip, port};
+ }
+
+
+ template <class Archive, class ver_type>
inline void save(Archive& a, const net::tor_address& na, const ver_type)
{
const size_t length = std::strlen(na.host_str());
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/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 3db138719..9aaaa026d 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -105,6 +105,35 @@ namespace cryptonote
, m_p2p(p2p)
{}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::set_bootstrap_daemon(const std::string &address, const std::string &username_password)
+ {
+ boost::optional<epee::net_utils::http::login> credentials;
+ const auto loc = username_password.find(':');
+ if (loc != std::string::npos)
+ {
+ credentials = epee::net_utils::http::login(username_password.substr(0, loc), username_password.substr(loc + 1));
+ }
+ return set_bootstrap_daemon(address, credentials);
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::set_bootstrap_daemon(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials)
+ {
+ boost::unique_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
+
+ if (!address.empty())
+ {
+ if (!m_http_client.set_server(address, credentials, epee::net_utils::ssl_support_t::e_ssl_support_autodetect))
+ {
+ return false;
+ }
+ }
+
+ m_bootstrap_daemon_address = address;
+ m_should_use_bootstrap_daemon = !m_bootstrap_daemon_address.empty();
+
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::init(
const boost::program_options::variables_map& vm
, const bool restricted
@@ -113,34 +142,18 @@ namespace cryptonote
{
m_restricted = restricted;
m_net_server.set_threads_prefix("RPC");
+ m_net_server.set_connection_filter(&m_p2p);
auto rpc_config = cryptonote::rpc_args::process(vm, true);
if (!rpc_config)
return false;
- m_bootstrap_daemon_address = command_line::get_arg(vm, arg_bootstrap_daemon_address);
- if (!m_bootstrap_daemon_address.empty())
+ if (!set_bootstrap_daemon(command_line::get_arg(vm, arg_bootstrap_daemon_address),
+ command_line::get_arg(vm, arg_bootstrap_daemon_login)))
{
- const std::string &bootstrap_daemon_login = command_line::get_arg(vm, arg_bootstrap_daemon_login);
- const auto loc = bootstrap_daemon_login.find(':');
- if (!bootstrap_daemon_login.empty() && loc != std::string::npos)
- {
- epee::net_utils::http::login login;
- login.username = bootstrap_daemon_login.substr(0, loc);
- login.password = bootstrap_daemon_login.substr(loc + 1);
- m_http_client.set_server(m_bootstrap_daemon_address, login, epee::net_utils::ssl_support_t::e_ssl_support_autodetect);
- }
- else
- {
- m_http_client.set_server(m_bootstrap_daemon_address, boost::none, epee::net_utils::ssl_support_t::e_ssl_support_autodetect);
- }
- m_should_use_bootstrap_daemon = true;
- }
- else
- {
- m_should_use_bootstrap_daemon = false;
+ MERROR("Failed to parse bootstrap daemon address");
+ return false;
}
- m_was_bootstrap_ever_used = false;
boost::optional<epee::net_utils::http::login> http_login{};
@@ -149,7 +162,9 @@ namespace cryptonote
auto rng = [](size_t len, uint8_t *ptr){ return crypto::rand(len, ptr); };
return epee::http_server_impl_base<core_rpc_server, connection_context>::init(
- rng, std::move(port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login), std::move(rpc_config->ssl_options)
+ rng, std::move(port), std::move(rpc_config->bind_ip),
+ std::move(rpc_config->bind_ipv6_address), std::move(rpc_config->use_ipv6), std::move(rpc_config->require_ipv4),
+ std::move(rpc_config->access_control_origins), std::move(http_login), std::move(rpc_config->ssl_options)
);
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -161,6 +176,24 @@ namespace cryptonote
}
return true;
}
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::add_host_fail(const connection_context *ctx)
+ {
+ if(!ctx || !ctx->m_remote_address.is_blockable())
+ return false;
+
+ CRITICAL_REGION_LOCAL(m_host_fails_score_lock);
+ uint64_t fails = ++m_host_fails_score[ctx->m_remote_address.host_str()];
+ MDEBUG("Host " << ctx->m_remote_address.host_str() << " fail score=" << fails);
+ if(fails > RPC_IP_FAILS_BEFORE_BLOCK)
+ {
+ auto it = m_host_fails_score.find(ctx->m_remote_address.host_str());
+ CHECK_AND_ASSERT_MES(it != m_host_fails_score.end(), false, "internal error");
+ it->second = RPC_IP_FAILS_BEFORE_BLOCK/2;
+ m_p2p.block_host(ctx->m_remote_address);
+ }
+ return true;
+ }
#define CHECK_CORE_READY() do { if(!check_core_ready()){res.status = CORE_RPC_STATUS_BUSY;return true;} } while(0)
//------------------------------------------------------------------------------------------------------------------------------
@@ -185,7 +218,10 @@ namespace cryptonote
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_INFO>(invoke_http_mode::JON, "/getinfo", req, res, r))
{
- res.bootstrap_daemon_address = m_bootstrap_daemon_address;
+ {
+ boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
+ res.bootstrap_daemon_address = m_bootstrap_daemon_address;
+ }
crypto::hash top_hash;
m_core.get_blockchain_top(res.height_without_bootstrap, top_hash);
++res.height_without_bootstrap; // turn top block height into blockchain height
@@ -224,13 +260,16 @@ namespace cryptonote
res.start_time = restricted ? 0 : (uint64_t)m_core.get_start_time();
res.free_space = restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space();
res.offline = m_core.offline();
- res.bootstrap_daemon_address = restricted ? "" : m_bootstrap_daemon_address;
res.height_without_bootstrap = restricted ? 0 : res.height;
if (restricted)
+ {
+ res.bootstrap_daemon_address = "";
res.was_bootstrap_ever_used = false;
+ }
else
{
boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
+ res.bootstrap_daemon_address = m_bootstrap_daemon_address;
res.was_bootstrap_ever_used = m_was_bootstrap_ever_used;
}
res.database_size = m_core.get_blockchain_storage().get_db().get_database_size();
@@ -282,6 +321,7 @@ namespace cryptonote
if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, !req.no_miner_tx, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT))
{
res.status = "Failed";
+ add_host_fail(ctx);
return false;
}
@@ -405,6 +445,7 @@ namespace cryptonote
if(!m_core.get_blockchain_storage().find_blockchain_supplement(req.block_ids, res.m_block_ids, res.start_height, res.current_height, false))
{
res.status = "Failed";
+ add_host_fail(ctx);
return false;
}
@@ -585,7 +626,8 @@ namespace cryptonote
return true;
}
const cryptonote::blobdata pruned = ss.str();
- sorted_txs.push_back(std::make_tuple(h, pruned, get_transaction_prunable_hash(tx), std::string(i->tx_blob, pruned.size())));
+ const crypto::hash prunable_hash = tx.version == 1 ? crypto::null_hash : get_transaction_prunable_hash(tx);
+ sorted_txs.push_back(std::make_tuple(h, pruned, prunable_hash, std::string(i->tx_blob, pruned.size())));
missed_txs.erase(std::find(missed_txs.begin(), missed_txs.end(), h));
pool_tx_hashes.insert(h);
const std::string hash_string = epee::string_tools::pod_to_hex(h);
@@ -820,6 +862,7 @@ namespace cryptonote
res.sanity_check_failed = true;
return true;
}
+ res.sanity_check_failed = false;
cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context);
tx_verification_context tvc = AUTO_VAL_INIT(tvc);
@@ -843,6 +886,8 @@ namespace cryptonote
add_reason(reason, "fee too low");
if ((res.not_rct = tvc.m_not_rct))
add_reason(reason, "tx is not ringct");
+ if ((res.too_few_outputs = tvc.m_too_few_outputs))
+ add_reason(reason, "too few outputs");
const std::string punctuation = reason.empty() ? "" : ": ";
if (tvc.m_verifivation_failed)
{
@@ -1007,6 +1052,11 @@ namespace cryptonote
if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
res.white_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv4_network_address>().ip(),
entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
+ else if (entry.adr.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id())
+ {
+ res.white_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv6_network_address>().host_str(),
+ entry.adr.as<epee::net_utils::ipv6_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
+ }
else
res.white_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
}
@@ -1017,6 +1067,9 @@ namespace cryptonote
if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
res.gray_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv4_network_address>().ip(),
entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
+ else if (entry.adr.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id())
+ res.white_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv6_network_address>().host_str(),
+ entry.adr.as<epee::net_utils::ipv6_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
else
res.gray_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
}
@@ -1025,6 +1078,45 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_get_public_nodes(const COMMAND_RPC_GET_PUBLIC_NODES::request& req, COMMAND_RPC_GET_PUBLIC_NODES::response& res, const connection_context *ctx)
+ {
+ PERF_TIMER(on_get_public_nodes);
+
+ COMMAND_RPC_GET_PEER_LIST::response peer_list_res;
+ const bool success = on_get_peer_list(COMMAND_RPC_GET_PEER_LIST::request(), peer_list_res, ctx);
+ res.status = peer_list_res.status;
+ if (!success)
+ {
+ return false;
+ }
+ if (res.status != CORE_RPC_STATUS_OK)
+ {
+ return true;
+ }
+
+ const auto collect = [](const std::vector<peer> &peer_list, std::vector<public_node> &public_nodes)
+ {
+ for (const auto &entry : peer_list)
+ {
+ if (entry.rpc_port != 0)
+ {
+ public_nodes.emplace_back(entry);
+ }
+ }
+ };
+
+ if (req.white)
+ {
+ collect(peer_list_res.white_list, res.white);
+ }
+ if (req.gray)
+ {
+ collect(peer_list_res.gray_list, res.gray);
+ }
+
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_set_log_hash_rate(const COMMAND_RPC_SET_LOG_HASH_RATE::request& req, COMMAND_RPC_SET_LOG_HASH_RATE::response& res, const connection_context *ctx)
{
PERF_TIMER(on_set_log_hash_rate);
@@ -1124,6 +1216,28 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_set_bootstrap_daemon(const COMMAND_RPC_SET_BOOTSTRAP_DAEMON::request& req, COMMAND_RPC_SET_BOOTSTRAP_DAEMON::response& res, const connection_context *ctx)
+ {
+ PERF_TIMER(on_set_bootstrap_daemon);
+
+ boost::optional<epee::net_utils::http::login> credentials;
+ if (!req.username.empty() || !req.password.empty())
+ {
+ credentials = epee::net_utils::http::login(req.username, req.password);
+ }
+
+ if (set_bootstrap_daemon(req.address, credentials))
+ {
+ res.status = CORE_RPC_STATUS_OK;
+ }
+ else
+ {
+ res.status = "Failed to set bootstrap daemon";
+ }
+
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res, const connection_context *ctx)
{
PERF_TIMER(on_stop_daemon);
@@ -1448,10 +1562,12 @@ namespace cryptonote
bool core_rpc_server::use_bootstrap_daemon_if_necessary(const invoke_http_mode &mode, const std::string &command_name, const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res, bool &r)
{
res.untrusted = false;
+
+ boost::upgrade_lock<boost::shared_mutex> upgrade_lock(m_bootstrap_daemon_mutex);
+
if (m_bootstrap_daemon_address.empty())
return false;
- boost::unique_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
if (!m_should_use_bootstrap_daemon)
{
MINFO("The local daemon is fully synced. Not switching back to the bootstrap daemon");
@@ -1461,7 +1577,10 @@ namespace cryptonote
auto current_time = std::chrono::system_clock::now();
if (current_time - m_bootstrap_height_check_time > std::chrono::seconds(30)) // update every 30s
{
- m_bootstrap_height_check_time = current_time;
+ {
+ boost::upgrade_to_unique_lock<boost::shared_mutex> lock(upgrade_lock);
+ m_bootstrap_height_check_time = current_time;
+ }
uint64_t top_height;
crypto::hash top_hash;
@@ -1475,7 +1594,7 @@ namespace cryptonote
ok = ok && getheight_res.status == CORE_RPC_STATUS_OK;
m_should_use_bootstrap_daemon = ok && top_height + 10 < getheight_res.height;
- MINFO((m_should_use_bootstrap_daemon ? "Using" : "Not using") << " the bootstrap daemon (our height: " << top_height << ", bootstrap daemon's height: " << getheight_res.height << ")");
+ MINFO((m_should_use_bootstrap_daemon ? "Using" : "Not using") << " the bootstrap daemon (our height: " << top_height << ", bootstrap daemon's height: " << (ok ? getheight_res.height : 0) << ")");
}
if (!m_should_use_bootstrap_daemon)
return false;
@@ -1505,7 +1624,12 @@ namespace cryptonote
MERROR("Unknown invoke_http_mode: " << mode);
return false;
}
- m_was_bootstrap_ever_used = true;
+
+ {
+ boost::upgrade_to_unique_lock<boost::shared_mutex> lock(upgrade_lock);
+ m_was_bootstrap_ever_used = true;
+ }
+
r = r && res.status == CORE_RPC_STATUS_OK;
res.untrusted = true;
return true;
@@ -1549,38 +1673,55 @@ namespace cryptonote
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH>(invoke_http_mode::JON_RPC, "getblockheaderbyhash", req, res, r))
return r;
- crypto::hash block_hash;
- bool hash_parsed = parse_hash256(req.hash, block_hash);
- if(!hash_parsed)
- {
- error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
- error_resp.message = "Failed to parse hex representation of block hash. Hex = " + req.hash + '.';
- return false;
- }
- block blk;
- bool orphan = false;
- bool have_block = m_core.get_block_by_hash(block_hash, blk, &orphan);
- if (!have_block)
- {
- error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
- error_resp.message = "Internal error: can't get block by hash. Hash = " + req.hash + '.';
- return false;
- }
- if (blk.miner_tx.vin.size() != 1 || blk.miner_tx.vin.front().type() != typeid(txin_gen))
+ auto get = [this](const std::string &hash, bool fill_pow_hash, block_header_response &block_header, bool restricted, epee::json_rpc::error& error_resp) -> bool {
+ crypto::hash block_hash;
+ bool hash_parsed = parse_hash256(hash, block_hash);
+ if(!hash_parsed)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
+ error_resp.message = "Failed to parse hex representation of block hash. Hex = " + hash + '.';
+ return false;
+ }
+ block blk;
+ bool orphan = false;
+ bool have_block = m_core.get_block_by_hash(block_hash, blk, &orphan);
+ if (!have_block)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Internal error: can't get block by hash. Hash = " + hash + '.';
+ return false;
+ }
+ if (blk.miner_tx.vin.size() != 1 || blk.miner_tx.vin.front().type() != typeid(txin_gen))
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Internal error: coinbase transaction in the block has the wrong type";
+ return false;
+ }
+ uint64_t block_height = boost::get<txin_gen>(blk.miner_tx.vin.front()).height;
+ bool response_filled = fill_block_header_response(blk, orphan, block_height, block_hash, block_header, fill_pow_hash && !restricted);
+ if (!response_filled)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Internal error: can't produce valid response.";
+ return false;
+ }
+ return true;
+ };
+
+ const bool restricted = m_restricted && ctx;
+ if (!req.hash.empty())
{
- error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
- error_resp.message = "Internal error: coinbase transaction in the block has the wrong type";
- return false;
+ if (!get(req.hash, req.fill_pow_hash, res.block_header, restricted, error_resp))
+ return false;
}
- uint64_t block_height = boost::get<txin_gen>(blk.miner_tx.vin.front()).height;
- const bool restricted = m_restricted && ctx;
- bool response_filled = fill_block_header_response(blk, orphan, block_height, block_hash, res.block_header, req.fill_pow_hash && !restricted);
- if (!response_filled)
+ res.block_headers.reserve(req.hashes.size());
+ for (const std::string &hash: req.hashes)
{
- error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
- error_resp.message = "Internal error: can't produce valid response.";
- return false;
+ res.block_headers.push_back({});
+ if (!get(hash, req.fill_pow_hash, res.block_headers.back(), restricted, error_resp))
+ return false;
}
+
res.status = CORE_RPC_STATUS_OK;
return true;
}
@@ -1771,20 +1912,60 @@ namespace cryptonote
PERF_TIMER(on_get_bans);
auto now = time(nullptr);
- std::map<std::string, time_t> blocked_hosts = m_p2p.get_blocked_hosts();
- for (std::map<std::string, time_t>::const_iterator i = blocked_hosts.begin(); i != blocked_hosts.end(); ++i)
+ std::map<epee::net_utils::network_address, time_t> blocked_hosts = m_p2p.get_blocked_hosts();
+ for (std::map<epee::net_utils::network_address, time_t>::const_iterator i = blocked_hosts.begin(); i != blocked_hosts.end(); ++i)
{
if (i->second > now) {
COMMAND_RPC_GETBANS::ban b;
- b.host = i->first;
+ b.host = i->first.host_str();
b.ip = 0;
uint32_t ip;
- if (epee::string_tools::get_ip_int32_from_string(ip, i->first))
+ if (epee::string_tools::get_ip_int32_from_string(ip, b.host))
b.ip = ip;
b.seconds = i->second - now;
res.bans.push_back(b);
}
}
+ std::map<epee::net_utils::ipv4_network_subnet, time_t> blocked_subnets = m_p2p.get_blocked_subnets();
+ for (std::map<epee::net_utils::ipv4_network_subnet, time_t>::const_iterator i = blocked_subnets.begin(); i != blocked_subnets.end(); ++i)
+ {
+ if (i->second > now) {
+ COMMAND_RPC_GETBANS::ban b;
+ b.host = i->first.host_str();
+ b.ip = 0;
+ b.seconds = i->second - now;
+ res.bans.push_back(b);
+ }
+ }
+
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_banned(const COMMAND_RPC_BANNED::request& req, COMMAND_RPC_BANNED::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
+ {
+ PERF_TIMER(on_banned);
+
+ auto na_parsed = net::get_network_address(req.address, 0);
+ if (!na_parsed)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
+ error_resp.message = "Unsupported host type";
+ return false;
+ }
+ epee::net_utils::network_address na = std::move(*na_parsed);
+
+ time_t seconds;
+ if (m_p2p.is_host_blocked(na, &seconds))
+ {
+ res.banned = true;
+ res.seconds = seconds;
+ }
+ else
+ {
+ res.banned = false;
+ res.seconds = 0;
+ }
res.status = CORE_RPC_STATUS_OK;
return true;
@@ -1797,13 +1978,29 @@ namespace cryptonote
for (auto i = req.bans.begin(); i != req.bans.end(); ++i)
{
epee::net_utils::network_address na;
+
+ // try subnet first
+ if (!i->host.empty())
+ {
+ auto ns_parsed = net::get_ipv4_subnet_address(i->host);
+ if (ns_parsed)
+ {
+ if (i->ban)
+ m_p2p.block_subnet(*ns_parsed, i->seconds);
+ else
+ m_p2p.unblock_subnet(*ns_parsed);
+ continue;
+ }
+ }
+
+ // then host
if (!i->host.empty())
{
auto na_parsed = net::get_network_address(i->host, 0);
if (!na_parsed)
{
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
- error_resp.message = "Unsupported host type";
+ error_resp.message = "Unsupported host/subnet type";
return false;
}
na = std::move(*na_parsed);
@@ -1954,7 +2151,7 @@ namespace cryptonote
PERF_TIMER(on_get_alternate_chains);
try
{
- std::list<std::pair<Blockchain::block_extended_info, std::vector<crypto::hash>>> chains = m_core.get_blockchain_storage().get_alternative_chains();
+ std::vector<std::pair<Blockchain::block_extended_info, std::vector<crypto::hash>>> chains = m_core.get_blockchain_storage().get_alternative_chains();
for (const auto &i: chains)
{
difficulty_type wdiff = i.first.cumulative_difficulty;
@@ -2038,7 +2235,9 @@ namespace cryptonote
bool core_rpc_server::on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res, const connection_context *ctx)
{
PERF_TIMER(on_out_peers);
- m_p2p.change_max_out_public_peers(req.out_peers);
+ if (req.set)
+ m_p2p.change_max_out_public_peers(req.out_peers);
+ res.out_peers = m_p2p.get_max_out_public_peers();
res.status = CORE_RPC_STATUS_OK;
return true;
}
@@ -2046,27 +2245,13 @@ namespace cryptonote
bool core_rpc_server::on_in_peers(const COMMAND_RPC_IN_PEERS::request& req, COMMAND_RPC_IN_PEERS::response& res, const connection_context *ctx)
{
PERF_TIMER(on_in_peers);
- m_p2p.change_max_in_public_peers(req.in_peers);
+ if (req.set)
+ m_p2p.change_max_in_public_peers(req.in_peers);
+ res.in_peers = m_p2p.get_max_in_public_peers();
res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
- bool core_rpc_server::on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res, const connection_context *ctx)
- {
- PERF_TIMER(on_start_save_graph);
- m_p2p.set_save_graph(true);
- res.status = CORE_RPC_STATUS_OK;
- return true;
- }
- //------------------------------------------------------------------------------------------------------------------------------
- bool core_rpc_server::on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res, const connection_context *ctx)
- {
- PERF_TIMER(on_stop_save_graph);
- m_p2p.set_save_graph(false);
- res.status = CORE_RPC_STATUS_OK;
- return true;
- }
- //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res, const connection_context *ctx)
{
PERF_TIMER(on_update);
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index e4683bbe2..e91d4c953 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -108,6 +108,7 @@ namespace cryptonote
MAP_URI_AUTO_JON2_IF("/mining_status", on_mining_status, COMMAND_RPC_MINING_STATUS, !m_restricted)
MAP_URI_AUTO_JON2_IF("/save_bc", on_save_bc, COMMAND_RPC_SAVE_BC, !m_restricted)
MAP_URI_AUTO_JON2_IF("/get_peer_list", on_get_peer_list, COMMAND_RPC_GET_PEER_LIST, !m_restricted)
+ MAP_URI_AUTO_JON2_IF("/get_public_nodes", on_get_public_nodes, COMMAND_RPC_GET_PUBLIC_NODES, !m_restricted)
MAP_URI_AUTO_JON2_IF("/set_log_hash_rate", on_set_log_hash_rate, COMMAND_RPC_SET_LOG_HASH_RATE, !m_restricted)
MAP_URI_AUTO_JON2_IF("/set_log_level", on_set_log_level, COMMAND_RPC_SET_LOG_LEVEL, !m_restricted)
MAP_URI_AUTO_JON2_IF("/set_log_categories", on_set_log_categories, COMMAND_RPC_SET_LOG_CATEGORIES, !m_restricted)
@@ -115,6 +116,7 @@ namespace cryptonote
MAP_URI_AUTO_JON2("/get_transaction_pool_hashes.bin", on_get_transaction_pool_hashes_bin, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN)
MAP_URI_AUTO_JON2("/get_transaction_pool_hashes", on_get_transaction_pool_hashes, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES)
MAP_URI_AUTO_JON2("/get_transaction_pool_stats", on_get_transaction_pool_stats, COMMAND_RPC_GET_TRANSACTION_POOL_STATS)
+ MAP_URI_AUTO_JON2_IF("/set_bootstrap_daemon", on_set_bootstrap_daemon, COMMAND_RPC_SET_BOOTSTRAP_DAEMON, !m_restricted)
MAP_URI_AUTO_JON2_IF("/stop_daemon", on_stop_daemon, COMMAND_RPC_STOP_DAEMON, !m_restricted)
MAP_URI_AUTO_JON2("/get_info", on_get_info, COMMAND_RPC_GET_INFO)
MAP_URI_AUTO_JON2("/getinfo", on_get_info, COMMAND_RPC_GET_INFO)
@@ -123,8 +125,6 @@ namespace cryptonote
MAP_URI_AUTO_JON2_IF("/set_limit", on_set_limit, COMMAND_RPC_SET_LIMIT, !m_restricted)
MAP_URI_AUTO_JON2_IF("/out_peers", on_out_peers, COMMAND_RPC_OUT_PEERS, !m_restricted)
MAP_URI_AUTO_JON2_IF("/in_peers", on_in_peers, COMMAND_RPC_IN_PEERS, !m_restricted)
- MAP_URI_AUTO_JON2_IF("/start_save_graph", on_start_save_graph, COMMAND_RPC_START_SAVE_GRAPH, !m_restricted)
- MAP_URI_AUTO_JON2_IF("/stop_save_graph", on_stop_save_graph, COMMAND_RPC_STOP_SAVE_GRAPH, !m_restricted)
MAP_URI_AUTO_JON2("/get_outs", on_get_outs, COMMAND_RPC_GET_OUTPUTS)
MAP_URI_AUTO_JON2_IF("/update", on_update, COMMAND_RPC_UPDATE, !m_restricted)
MAP_URI_AUTO_BIN2("/get_output_distribution.bin", on_get_output_distribution_bin, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION)
@@ -154,6 +154,7 @@ namespace cryptonote
MAP_JON_RPC_WE("hard_fork_info", on_hard_fork_info, COMMAND_RPC_HARD_FORK_INFO)
MAP_JON_RPC_WE_IF("set_bans", on_set_bans, COMMAND_RPC_SETBANS, !m_restricted)
MAP_JON_RPC_WE_IF("get_bans", on_get_bans, COMMAND_RPC_GETBANS, !m_restricted)
+ MAP_JON_RPC_WE_IF("banned", on_banned, COMMAND_RPC_BANNED, !m_restricted)
MAP_JON_RPC_WE_IF("flush_txpool", on_flush_txpool, COMMAND_RPC_FLUSH_TRANSACTION_POOL, !m_restricted)
MAP_JON_RPC_WE("get_output_histogram", on_get_output_histogram, COMMAND_RPC_GET_OUTPUT_HISTOGRAM)
MAP_JON_RPC_WE("get_version", on_get_version, COMMAND_RPC_GET_VERSION)
@@ -186,6 +187,7 @@ namespace cryptonote
bool on_get_net_stats(const COMMAND_RPC_GET_NET_STATS::request& req, COMMAND_RPC_GET_NET_STATS::response& res, const connection_context *ctx = NULL);
bool on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res, const connection_context *ctx = NULL);
bool on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res, const connection_context *ctx = NULL);
+ bool on_get_public_nodes(const COMMAND_RPC_GET_PUBLIC_NODES::request& req, COMMAND_RPC_GET_PUBLIC_NODES::response& res, const connection_context *ctx = NULL);
bool on_set_log_hash_rate(const COMMAND_RPC_SET_LOG_HASH_RATE::request& req, COMMAND_RPC_SET_LOG_HASH_RATE::response& res, const connection_context *ctx = NULL);
bool on_set_log_level(const COMMAND_RPC_SET_LOG_LEVEL::request& req, COMMAND_RPC_SET_LOG_LEVEL::response& res, const connection_context *ctx = NULL);
bool on_set_log_categories(const COMMAND_RPC_SET_LOG_CATEGORIES::request& req, COMMAND_RPC_SET_LOG_CATEGORIES::response& res, const connection_context *ctx = NULL);
@@ -193,13 +195,12 @@ namespace cryptonote
bool on_get_transaction_pool_hashes_bin(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::response& res, const connection_context *ctx = NULL);
bool on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res, const connection_context *ctx = NULL);
bool on_get_transaction_pool_stats(const COMMAND_RPC_GET_TRANSACTION_POOL_STATS::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_STATS::response& res, const connection_context *ctx = NULL);
+ bool on_set_bootstrap_daemon(const COMMAND_RPC_SET_BOOTSTRAP_DAEMON::request& req, COMMAND_RPC_SET_BOOTSTRAP_DAEMON::response& res, const connection_context *ctx = NULL);
bool on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res, const connection_context *ctx = NULL);
bool on_get_limit(const COMMAND_RPC_GET_LIMIT::request& req, COMMAND_RPC_GET_LIMIT::response& res, const connection_context *ctx = NULL);
bool on_set_limit(const COMMAND_RPC_SET_LIMIT::request& req, COMMAND_RPC_SET_LIMIT::response& res, const connection_context *ctx = NULL);
bool on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res, const connection_context *ctx = NULL);
bool on_in_peers(const COMMAND_RPC_IN_PEERS::request& req, COMMAND_RPC_IN_PEERS::response& res, const connection_context *ctx = NULL);
- bool on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res, const connection_context *ctx = NULL);
- bool on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res, const connection_context *ctx = NULL);
bool on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res, const connection_context *ctx = NULL);
bool on_get_output_distribution_bin(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res, const connection_context *ctx = NULL);
bool on_pop_blocks(const COMMAND_RPC_POP_BLOCKS::request& req, COMMAND_RPC_POP_BLOCKS::response& res, const connection_context *ctx = NULL);
@@ -220,6 +221,7 @@ namespace cryptonote
bool on_hard_fork_info(const COMMAND_RPC_HARD_FORK_INFO::request& req, COMMAND_RPC_HARD_FORK_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_set_bans(const COMMAND_RPC_SETBANS::request& req, COMMAND_RPC_SETBANS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_bans(const COMMAND_RPC_GETBANS::request& req, COMMAND_RPC_GETBANS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
+ bool on_banned(const COMMAND_RPC_BANNED::request& req, COMMAND_RPC_BANNED::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_flush_txpool(const COMMAND_RPC_FLUSH_TRANSACTION_POOL::request& req, COMMAND_RPC_FLUSH_TRANSACTION_POOL::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_output_histogram(const COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request& req, COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_version(const COMMAND_RPC_GET_VERSION::request& req, COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
@@ -236,10 +238,13 @@ namespace cryptonote
private:
bool check_core_busy();
bool check_core_ready();
+ bool add_host_fail(const connection_context *ctx);
//utils
uint64_t get_block_reward(const block& blk);
bool fill_block_header_response(const block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_response& response, bool fill_pow_hash);
+ bool set_bootstrap_daemon(const std::string &address, const std::string &username_password);
+ bool set_bootstrap_daemon(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials);
enum invoke_http_mode { JON, BIN, JON_RPC };
template <typename COMMAND_TYPE>
bool use_bootstrap_daemon_if_necessary(const invoke_http_mode &mode, const std::string &command_name, const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res, bool &r);
@@ -254,6 +259,8 @@ private:
bool m_was_bootstrap_ever_used;
network_type m_nettype;
bool m_restricted;
+ epee::critical_section m_host_fails_score_lock;
+ std::map<std::string, uint64_t> m_host_fails_score;
};
}
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index cfe4bbf23..7ae0c77b2 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -29,6 +29,9 @@
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
+
+#include "string_tools.h"
+
#include "cryptonote_protocol/cryptonote_protocol_defs.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/difficulty.h"
@@ -84,7 +87,7 @@ namespace cryptonote
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define CORE_RPC_VERSION_MAJOR 2
-#define CORE_RPC_VERSION_MINOR 6
+#define CORE_RPC_VERSION_MINOR 8
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
@@ -609,6 +612,7 @@ namespace cryptonote
bool overspend;
bool fee_too_low;
bool not_rct;
+ bool too_few_outputs;
bool sanity_check_failed;
bool untrusted;
@@ -624,6 +628,7 @@ namespace cryptonote
KV_SERIALIZE(overspend)
KV_SERIALIZE(fee_too_low)
KV_SERIALIZE(not_rct)
+ KV_SERIALIZE(too_few_outputs)
KV_SERIALIZE(sanity_check_failed)
KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
@@ -1091,10 +1096,12 @@ namespace cryptonote
struct request_t
{
std::string hash;
+ std::vector<std::string> hashes;
bool fill_pow_hash;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(hash)
+ KV_SERIALIZE(hashes)
KV_SERIALIZE_OPT(fill_pow_hash, false);
END_KV_SERIALIZE_MAP()
};
@@ -1104,10 +1111,12 @@ namespace cryptonote
{
std::string status;
block_header_response block_header;
+ std::vector<block_header_response> block_headers;
bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(block_header)
+ KV_SERIALIZE(block_headers)
KV_SERIALIZE(status)
KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
@@ -1197,8 +1206,11 @@ namespace cryptonote
peer(uint64_t id, const std::string &host, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port)
: id(id), host(host), ip(0), port(0), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed)
{}
+ peer(uint64_t id, const std::string &host, uint16_t port, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port)
+ : id(id), host(host), ip(0), port(port), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed)
+ {}
peer(uint64_t id, uint32_t ip, uint16_t port, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port)
- : id(id), host(std::to_string(ip)), ip(ip), port(port), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed)
+ : id(id), host(epee::string_tools::get_ip_string_from_int32(ip)), ip(ip), port(port), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed)
{}
BEGIN_KV_SERIALIZE_MAP()
@@ -1236,6 +1248,54 @@ namespace cryptonote
typedef epee::misc_utils::struct_init<response_t> response;
};
+ struct public_node
+ {
+ std::string host;
+ uint64_t last_seen;
+ uint16_t rpc_port;
+
+ public_node() = delete;
+
+ public_node(const peer &peer)
+ : host(peer.host), last_seen(peer.last_seen), rpc_port(peer.rpc_port)
+ {}
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(host)
+ KV_SERIALIZE(last_seen)
+ KV_SERIALIZE(rpc_port)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct COMMAND_RPC_GET_PUBLIC_NODES
+ {
+ struct request_t
+ {
+ bool gray;
+ bool white;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_OPT(gray, false)
+ KV_SERIALIZE_OPT(white, true)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ std::string status;
+ std::vector<public_node> gray;
+ std::vector<public_node> white;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ KV_SERIALIZE(gray)
+ KV_SERIALIZE(white)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
struct COMMAND_RPC_SET_LOG_HASH_RATE
{
struct request_t
@@ -1583,6 +1643,33 @@ namespace cryptonote
typedef epee::misc_utils::struct_init<response_t> response;
};
+ struct COMMAND_RPC_SET_BOOTSTRAP_DAEMON
+ {
+ struct request_t
+ {
+ std::string address;
+ std::string username;
+ std::string password;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(username)
+ KV_SERIALIZE(password)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ std::string status;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
struct COMMAND_RPC_STOP_DAEMON
{
struct request_t
@@ -1682,8 +1769,10 @@ namespace cryptonote
{
struct request_t
{
- uint64_t out_peers;
+ bool set;
+ uint32_t out_peers;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_OPT(set, true)
KV_SERIALIZE(out_peers)
END_KV_SERIALIZE_MAP()
};
@@ -1691,9 +1780,11 @@ namespace cryptonote
struct response_t
{
+ uint32_t out_peers;
std::string status;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(out_peers)
KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
@@ -1704,8 +1795,10 @@ namespace cryptonote
{
struct request_t
{
- uint64_t in_peers;
+ bool set;
+ uint32_t in_peers;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_OPT(set, true)
KV_SERIALIZE(in_peers)
END_KV_SERIALIZE_MAP()
};
@@ -1713,55 +1806,17 @@ namespace cryptonote
struct response_t
{
+ uint32_t in_peers;
std::string status;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(in_peers)
KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
- struct COMMAND_RPC_START_SAVE_GRAPH
- {
- struct request_t
- {
- BEGIN_KV_SERIALIZE_MAP()
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<request_t> request;
-
- struct response_t
- {
- std::string status;
-
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<response_t> response;
- };
-
- struct COMMAND_RPC_STOP_SAVE_GRAPH
- {
- struct request_t
- {
- BEGIN_KV_SERIALIZE_MAP()
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<request_t> request;
-
- struct response_t
- {
- std::string status;
-
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<response_t> response;
- };
-
struct COMMAND_RPC_HARD_FORK_INFO
{
struct request_t
@@ -1876,6 +1931,33 @@ namespace cryptonote
typedef epee::misc_utils::struct_init<response_t> response;
};
+ struct COMMAND_RPC_BANNED
+ {
+ struct request_t
+ {
+ std::string address;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ std::string status;
+ bool banned;
+ uint32_t seconds;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ KV_SERIALIZE(banned)
+ KV_SERIALIZE(seconds)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
struct COMMAND_RPC_FLUSH_TRANSACTION_POOL
{
struct request_t
@@ -2070,7 +2152,7 @@ namespace cryptonote
struct response_t
{
std::string status;
- std::list<chain_info> chains;
+ std::vector<chain_info> chains;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp
index 612b2cab6..890380dc8 100644
--- a/src/rpc/daemon_handler.cpp
+++ b/src/rpc/daemon_handler.cpp
@@ -343,6 +343,11 @@ namespace rpc
if (!res.error_details.empty()) res.error_details += " and ";
res.error_details = "tx is not ringct";
}
+ if (tvc.m_too_few_outputs)
+ {
+ if (!res.error_details.empty()) res.error_details += " and ";
+ res.error_details = "too few outputs";
+ }
if (res.error_details.empty())
{
res.error_details = "an unknown issue was found with the transaction";
diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp
index 4479bd1f1..68b33cb8c 100644
--- a/src/rpc/rpc_args.cpp
+++ b/src/rpc/rpc_args.cpp
@@ -90,6 +90,9 @@ namespace cryptonote
rpc_args::descriptors::descriptors()
: rpc_bind_ip({"rpc-bind-ip", rpc_args::tr("Specify IP to bind RPC server"), "127.0.0.1"})
+ , rpc_bind_ipv6_address({"rpc-bind-ipv6-address", rpc_args::tr("Specify IPv6 address to bind RPC server"), "::1"})
+ , rpc_use_ipv6({"rpc-use-ipv6", rpc_args::tr("Allow IPv6 for RPC"), false})
+ , rpc_require_ipv4({"rpc-require-ipv4", rpc_args::tr("Require successful IPv4 bind for RPC"), true})
, rpc_login({"rpc-login", rpc_args::tr("Specify username[:password] required for RPC server"), "", true})
, confirm_external_bind({"confirm-external-bind", rpc_args::tr("Confirm rpc-bind-ip value is NOT a loopback (local) IP")})
, rpc_access_control_origins({"rpc-access-control-origins", rpc_args::tr("Specify a comma separated list of origins to allow cross origin resource sharing"), ""})
@@ -108,6 +111,9 @@ namespace cryptonote
{
const descriptors arg{};
command_line::add_arg(desc, arg.rpc_bind_ip);
+ command_line::add_arg(desc, arg.rpc_bind_ipv6_address);
+ command_line::add_arg(desc, arg.rpc_use_ipv6);
+ command_line::add_arg(desc, arg.rpc_require_ipv4);
command_line::add_arg(desc, arg.rpc_login);
command_line::add_arg(desc, arg.confirm_external_bind);
command_line::add_arg(desc, arg.rpc_access_control_origins);
@@ -127,6 +133,9 @@ namespace cryptonote
rpc_args config{};
config.bind_ip = command_line::get_arg(vm, arg.rpc_bind_ip);
+ config.bind_ipv6_address = command_line::get_arg(vm, arg.rpc_bind_ipv6_address);
+ config.use_ipv6 = command_line::get_arg(vm, arg.rpc_use_ipv6);
+ config.require_ipv4 = command_line::get_arg(vm, arg.rpc_require_ipv4);
if (!config.bind_ip.empty())
{
// always parse IP here for error consistency
@@ -148,6 +157,34 @@ namespace cryptonote
return boost::none;
}
}
+ if (!config.bind_ipv6_address.empty())
+ {
+ // allow square braces, but remove them here if present
+ if (config.bind_ipv6_address.find('[') != std::string::npos)
+ {
+ config.bind_ipv6_address = config.bind_ipv6_address.substr(1, config.bind_ipv6_address.size() - 2);
+ }
+
+
+ // always parse IP here for error consistency
+ boost::system::error_code ec{};
+ const auto parsed_ip = boost::asio::ip::address::from_string(config.bind_ipv6_address, ec);
+ if (ec)
+ {
+ LOG_ERROR(tr("Invalid IP address given for --") << arg.rpc_bind_ipv6_address.name);
+ return boost::none;
+ }
+
+ if (!parsed_ip.is_loopback() && !command_line::get_arg(vm, arg.confirm_external_bind))
+ {
+ LOG_ERROR(
+ "--" << arg.rpc_bind_ipv6_address.name <<
+ tr(" permits inbound unencrypted external connections. Consider SSH tunnel or SSL proxy instead. Override with --") <<
+ arg.confirm_external_bind.name
+ );
+ return boost::none;
+ }
+ }
const char *env_rpc_login = nullptr;
const bool has_rpc_arg = command_line::has_arg(vm, arg.rpc_login);
diff --git a/src/rpc/rpc_args.h b/src/rpc/rpc_args.h
index 619f02b42..cd154a4d0 100644
--- a/src/rpc/rpc_args.h
+++ b/src/rpc/rpc_args.h
@@ -52,6 +52,9 @@ namespace cryptonote
descriptors& operator=(descriptors&&) = delete;
const command_line::arg_descriptor<std::string> rpc_bind_ip;
+ const command_line::arg_descriptor<std::string> rpc_bind_ipv6_address;
+ const command_line::arg_descriptor<bool> rpc_use_ipv6;
+ const command_line::arg_descriptor<bool> rpc_require_ipv4;
const command_line::arg_descriptor<std::string> rpc_login;
const command_line::arg_descriptor<bool> confirm_external_bind;
const command_line::arg_descriptor<std::string> rpc_access_control_origins;
@@ -76,6 +79,9 @@ namespace cryptonote
static boost::optional<epee::net_utils::ssl_options_t> process_ssl(const boost::program_options::variables_map& vm, const bool any_cert_option = false);
std::string bind_ip;
+ std::string bind_ipv6_address;
+ bool use_ipv6;
+ bool require_ipv4;
std::vector<std::string> access_control_origins;
boost::optional<tools::login> login; // currently `boost::none` if unspecified by user
epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_enabled;
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/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 02a099811..10901bb51 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>");
@@ -3970,7 +3972,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
if (m_restoring && m_generate_from_json.empty() && m_generate_from_device.empty())
{
- m_wallet->explicit_refresh_from_block_height(!(command_line::is_arg_defaulted(vm, arg_restore_height) ||
+ m_wallet->explicit_refresh_from_block_height(!(command_line::is_arg_defaulted(vm, arg_restore_height) &&
command_line::is_arg_defaulted(vm, arg_restore_date)));
if (command_line::is_arg_defaulted(vm, arg_restore_height) && !command_line::is_arg_defaulted(vm, arg_restore_date))
{
@@ -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..fa67b103e 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -1697,6 +1697,19 @@ 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
+{
+ std::string value;
+ m_wallet->get_attribute(key, value);
+ return value;
+}
+
bool WalletImpl::setUserNote(const std::string &txid, const std::string &note)
{
cryptonote::blobdata txid_data;
@@ -1729,18 +1742,27 @@ std::string WalletImpl::getTxKey(const std::string &txid_str) const
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
- if (m_wallet->get_tx_key(txid, tx_key, additional_tx_keys))
+ try
{
clearStatus();
- std::ostringstream oss;
- oss << epee::string_tools::pod_to_hex(tx_key);
- for (size_t i = 0; i < additional_tx_keys.size(); ++i)
- oss << epee::string_tools::pod_to_hex(additional_tx_keys[i]);
- return oss.str();
+ if (m_wallet->get_tx_key(txid, tx_key, additional_tx_keys))
+ {
+ clearStatus();
+ std::ostringstream oss;
+ oss << epee::string_tools::pod_to_hex(tx_key);
+ for (size_t i = 0; i < additional_tx_keys.size(); ++i)
+ oss << epee::string_tools::pod_to_hex(additional_tx_keys[i]);
+ return oss.str();
+ }
+ else
+ {
+ setStatusError(tr("no tx keys found for this txid"));
+ return "";
+ }
}
- else
+ catch (const std::exception &e)
{
- setStatusError(tr("no tx keys found for this txid"));
+ setStatusError(e.what());
return "";
}
}
@@ -2419,6 +2441,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 &note) 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..23c375924 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -217,6 +217,8 @@ namespace
add_reason(reason, "invalid input");
if (res.invalid_output)
add_reason(reason, "invalid output");
+ if (res.too_few_outputs)
+ add_reason(reason, "too few outputs");
if (res.too_big)
add_reason(reason, "too big");
if (res.overspend)
@@ -1140,7 +1142,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 +5160,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 +5170,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 +5182,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 +5191,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;
}
@@ -6118,7 +6125,7 @@ void wallet2::commit_tx(pending_tx& ptx)
amount_in += m_transfers[idx].amount();
}
add_unconfirmed_tx(ptx.tx, amount_in, dests, payment_id, ptx.change_dts.amount, ptx.construction_data.subaddr_account, ptx.construction_data.subaddr_indices);
- if (store_tx_info())
+ if (store_tx_info() && ptx.tx_key != crypto::null_skey)
{
m_tx_keys.insert(std::make_pair(txid, ptx.tx_key));
m_additional_tx_keys.insert(std::make_pair(txid, ptx.additional_tx_keys));
@@ -6307,7 +6314,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin
// normally, the tx keys are saved in commit_tx, when the tx is actually sent to the daemon.
// we can't do that here since the tx will be sent from the compromised wallet, which we don't want
// to see that info, so we save it here
- if (store_tx_info())
+ if (store_tx_info() && ptx.tx_key != crypto::null_skey)
{
const crypto::hash txid = get_transaction_hash(ptx.tx);
m_tx_keys.insert(std::make_pair(txid, tx_key));
@@ -6931,7 +6938,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 +6969,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 +7733,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 +7744,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 +7989,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;
@@ -8163,6 +8167,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
if (needed_money < found_money)
{
change_dts.addr = get_subaddress({subaddr_account, 0});
+ change_dts.is_subaddress = subaddr_account != 0;
change_dts.amount = found_money - needed_money;
}
@@ -10051,6 +10056,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 +10084,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);
@@ -10276,6 +10311,8 @@ bool wallet2::get_tx_key_cached(const crypto::hash &txid, crypto::secret_key &tx
if (i == m_tx_keys.end())
return false;
tx_key = i->second;
+ if (tx_key == crypto::null_skey)
+ return false;
const auto j = m_additional_tx_keys.find(txid);
if (j != m_additional_tx_keys.end())
additional_tx_keys = j->second;
@@ -10287,6 +10324,7 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, s
bool r = get_tx_key_cached(txid, tx_key, additional_tx_keys);
if (r)
{
+ MDEBUG("tx key cached for txid: " << txid);
return true;
}
@@ -10348,13 +10386,18 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, s
dev_cold->get_tx_key(tx_keys, tx_key_data, m_account.get_keys().m_view_secret_key);
if (tx_keys.empty())
{
+ MDEBUG("Empty tx keys for txid: " << txid);
+ return false;
+ }
+
+ if (tx_keys[0] == crypto::null_skey)
+ {
return false;
}
tx_key = tx_keys[0];
tx_keys.erase(tx_keys.begin());
additional_tx_keys = tx_keys;
-
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -11387,12 +11430,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 +11446,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..844ecf90c 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -247,7 +247,9 @@ namespace tools
m_net_server.set_threads_prefix("RPC");
auto rng = [](size_t len, uint8_t *ptr) { return crypto::rand(len, ptr); };
return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init(
- rng, std::move(bind_port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login),
+ rng, std::move(bind_port), std::move(rpc_config->bind_ip),
+ std::move(rpc_config->bind_ipv6_address), std::move(rpc_config->use_ipv6), std::move(rpc_config->require_ipv4),
+ std::move(rpc_config->access_control_origins), std::move(http_login),
std::move(rpc_config->ssl_options)
);
}
@@ -1745,6 +1747,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 +1813,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 +2104,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)
@@ -4081,9 +4091,8 @@ namespace tools
}
}
- er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
- er.message = std::string("Invalid address");
- return false;
+ res.valid = false;
+ return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_set_daemon(const wallet_rpc::COMMAND_RPC_SET_DAEMON::request& req, wallet_rpc::COMMAND_RPC_SET_DAEMON::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/functional_tests_rpc.py b/tests/functional_tests/functional_tests_rpc.py
index 25ab641ab..77d0e4c4d 100755
--- a/tests/functional_tests/functional_tests_rpc.py
+++ b/tests/functional_tests/functional_tests_rpc.py
@@ -65,7 +65,7 @@ try:
for i in range(len(command_lines)):
#print('Running: ' + str(command_lines[i]))
processes.append(subprocess.Popen(command_lines[i], stdout = outputs[i]))
-except Exception, e:
+except Exception as e:
print('Error: ' + str(e))
sys.exit(1)
diff --git a/tests/functional_tests/multisig.py b/tests/functional_tests/multisig.py
index 3c8cd9c1d..b109acf91 100755
--- a/tests/functional_tests/multisig.py
+++ b/tests/functional_tests/multisig.py
@@ -43,6 +43,7 @@ class MultisigTest():
self.mine('42jSRGmmKN96V2j3B8X2DbiNThBXW1tSi1rW1uwkqbyURenq3eC3yosNm8HEMdHuWwKMFGzMUB3RCTvcTaW9kHpdRPP7p5y', 5)
self.mine('47fF32AdrmXG84FcPY697uZdd42pMMGiH5UpiTRTt3YX2pZC7t7wkzEMStEicxbQGRfrYvAAYxH6Fe8rnD56EaNwUgxRd53', 5)
self.mine('44SKxxLQw929wRF6BA9paQ1EWFshNnKhXM3qz6Mo3JGDE2YG3xyzVutMStEicxbQGRfrYvAAYxH6Fe8rnD56EaNwUiqhcwR', 5)
+ self.mine('4ADHswEU3XBUee8pudBkZQd9beJainqNo1BQKkHJujAEPJyQrLj9U4dNm8HEMdHuWwKMFGzMUB3RCTvcTaW9kHpdRUDxgjW', 5)
self.mine('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 60)
self.create_multisig_wallets(2, 2, '493DsrfJPqiN3Suv9RcRDoZEbQtKZX1sNcGPA3GhkKYEEmivk8kjQrTdRdVc4ZbmzWJuE157z9NNUKmF2VDfdYDR3CziGMk')
@@ -57,6 +58,12 @@ class MultisigTest():
self.import_multisig_info([0, 1, 2], 6)
self.check_transaction(txid)
+ self.create_multisig_wallets(3, 3, '4ADHswEU3XBUee8pudBkZQd9beJainqNo1BQKkHJujAEPJyQrLj9U4dNm8HEMdHuWwKMFGzMUB3RCTvcTaW9kHpdRUDxgjW')
+ self.import_multisig_info([2, 0, 1], 5)
+ txid = self.transfer([2, 1, 0])
+ self.import_multisig_info([0, 2, 1], 6)
+ self.check_transaction(txid)
+
self.create_multisig_wallets(3, 4, '47fF32AdrmXG84FcPY697uZdd42pMMGiH5UpiTRTt3YX2pZC7t7wkzEMStEicxbQGRfrYvAAYxH6Fe8rnD56EaNwUgxRd53')
self.import_multisig_info([0, 2, 3], 5)
txid = self.transfer([0, 2, 3])
diff --git a/tests/functional_tests/transfer.py b/tests/functional_tests/transfer.py
index 7bf0f4957..e3c01c27f 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]]
@@ -571,6 +574,7 @@ class TransferTest():
assert res.overspend == False
assert res.fee_too_low == False
assert res.not_rct == False
+ assert res.too_few_outputs == False
res = daemon.get_transactions([txes[0][0]])
assert len(res.txs) >= 1
diff --git a/tests/functional_tests/validate_address.py b/tests/functional_tests/validate_address.py
new file mode 100755
index 000000000..58748b0a2
--- /dev/null
+++ b/tests/functional_tests/validate_address.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+
+# 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.
+
+import time
+
+"""Test address validation RPC calls
+"""
+
+from framework.wallet import Wallet
+
+class AddressValidationTest():
+ def run_test(self):
+ self.create()
+ self.check_bad_addresses()
+ self.check_good_addresses()
+ self.check_openalias_addresses()
+
+ def create(self):
+ print('Creating wallet')
+ seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted'
+ address = '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
+ self.wallet = Wallet()
+ # close the wallet if any, will throw if none is loaded
+ try: self.wallet.close_wallet()
+ except: pass
+ res = self.wallet.restore_deterministic_wallet(seed = seed)
+ assert res.address == address
+ assert res.seed == seed
+
+ def check_bad_addresses(self):
+ print('Validating bad addresses')
+ bad_addresses = ['', 'a', '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWD9', ' ', '@', '42ey']
+ for address in bad_addresses:
+ res = self.wallet.validate_address(address, any_net_type = False)
+ assert not res.valid
+ res = self.wallet.validate_address(address, any_net_type = True)
+ assert not res.valid
+
+ def check_good_addresses(self):
+ print('Validating good addresses')
+ addresses = [
+ [ 'mainnet', '', '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' ],
+ [ 'mainnet', '', '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW' ],
+ [ 'testnet', '', '9ujeXrjzf7bfeK3KZdCqnYaMwZVFuXemPU8Ubw335rj2FN1CdMiWNyFV3ksEfMFvRp9L9qum5UxkP5rN9aLcPxbH1au4WAB' ],
+ [ 'stagenet', '', '53teqCAESLxeJ1REzGMAat1ZeHvuajvDiXqboEocPaDRRmqWoVPzy46GLo866qRFjbNhfkNckyhST3WEvBviDwpUDd7DSzB' ],
+ [ 'mainnet', 'i', '4BxSHvcgTwu25WooY4BVmgdcKwZu5EksVZSZkDd6ooxSVVqQ4ubxXkhLF6hEqtw96i9cf3cVfLw8UWe95bdDKfRQeYtPwLm1Jiw7AKt2LY' ],
+ [ 'mainnet', 's', '8AsN91rznfkBGTY8psSNkJBg9SZgxxGGRUhGwRptBhgr5XSQ1XzmA9m8QAnoxydecSh5aLJXdrgXwTDMMZ1AuXsN1EX5Mtm' ],
+ [ 'mainnet', 's', '86kKnBKFqzCLxtK1Jmx2BkNBDBSMDEVaRYMMyVbeURYDWs8uNGDZURKCA5yRcyMxHzPcmCf1q2fSdhQVcaKsFrtGRsdGfNk' ],
+ [ 'testnet', 'i', 'AApMA1VuhiCaHzr5X2KXi2Zc9oJ3VaGjkfChxxpRpxkyKf1NetvbRbQTbFMrGkr85DjnEH7JsBaoUFsgKwZnmtnVWnoB8MDotCsLb7eWwz' ],
+ [ 'testnet', 's', 'BdKg9udkvckC5T58a8Nmtb6BNsgRAxs7uA2D49sWNNX5HPW5Us6Wxu8QMXrnSx3xPBQQ2iu9kwEcRGAoiz6EPmcZKbF62GS' ],
+ [ 'testnet', 's', 'BcFvPa3fT4gVt5QyRDe5Vv7VtUFao9ci8NFEy3r254KF7R1N2cNB5FYhGvrHbMStv4D6VDzZ5xtxeKV8vgEPMnDcNFuwZb9' ],
+ [ 'stagenet', 'i', '5K8mwfjumVseCcQEjNbf59Um6R9NfVUNkHTLhhPCmNvgDLVS88YW5tScnm83rw9mfgYtchtDDTW5jEfMhygi27j1QYphX38hg6m4VMtN29' ],
+ [ 'stagenet', 's', '73LhUiix4DVFMcKhsPRG51QmCsv8dYYbL6GcQoLwEEFvPvkVvc7BhebfA4pnEFF9Lq66hwvLqBvpHjTcqvpJMHmmNjPPBqa' ],
+ [ 'stagenet', 's', '7A1Hr63MfgUa8pkWxueD5xBqhQczkusYiCMYMnJGcGmuQxa7aDBxN1G7iCuLCNB3VPeb2TW7U9FdxB27xKkWKfJ8VhUZthF' ],
+ ]
+ for any_net_type in [True, False]:
+ for address in addresses:
+ res = self.wallet.validate_address(address[2], any_net_type = any_net_type)
+ if any_net_type or address[0] == 'mainnet':
+ assert res.valid
+ assert res.integrated == (address[1] == 'i')
+ assert res.subaddress == (address[1] == 's')
+ assert res.nettype == address[0]
+ assert res.openalias_address == ''
+ else:
+ assert not res.valid
+
+ def check_openalias_addresses(self):
+ print('Validating openalias addresses')
+ addresses = [
+ ['donate@getmonero.org', '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A']
+ ]
+ for address in addresses:
+ res = self.wallet.validate_address(address[0])
+ assert not res.valid
+ res = self.wallet.validate_address(address[0], allow_openalias = True)
+ assert res.valid
+ assert not res.integrated
+ assert not res.subaddress
+ assert res.nettype == 'mainnet'
+ assert res.openalias_address == address[1]
+
+if __name__ == '__main__':
+ AddressValidationTest().run_test()
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..0724cd3e0 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);
@@ -211,10 +211,10 @@ TEST(select_outputs, same_distribution)
{
const double diff = (double)output_norm[i] - (double)chain_norm[i];
double dev = fabs(2.0 * diff / (output_norm[i] + chain_norm[i]));
- ASSERT_LT(dev, 0.1);
+ ASSERT_LT(dev, 0.15);
avg_dev += dev;
}
avg_dev /= 100;
MDEBUG("avg_dev: " << avg_dev);
- ASSERT_LT(avg_dev, 0.015);
+ ASSERT_LT(avg_dev, 0.02);
}
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..20967030a 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'
@@ -88,11 +90,12 @@ class Daemon(object):
}
return self.rpc.send_json_rpc_request(getlastblockheader)
- def getblockheaderbyhash(self, hash):
+ def getblockheaderbyhash(self, hash = "", hashes = []):
getblockheaderbyhash = {
'method': 'getblockheaderbyhash',
'params': {
'hash': hash,
+ 'hashes': hashes,
},
'jsonrpc': '2.0',
'id': '0'
diff --git a/utils/python-rpc/framework/wallet.py b/utils/python-rpc/framework/wallet.py
index 2e2650f92..36ff3644f 100644
--- a/utils/python-rpc/framework/wallet.py
+++ b/utils/python-rpc/framework/wallet.py
@@ -750,6 +750,19 @@ class Wallet(object):
}
return self.rpc.send_json_rpc_request(set_log_categories)
+ def validate_address(self, address, any_net_type = False, allow_openalias = False):
+ validate_address = {
+ 'method': 'validate_address',
+ 'params': {
+ 'address': address,
+ 'any_net_type': any_net_type,
+ 'allow_openalias': allow_openalias,
+ },
+ 'jsonrpc': '2.0',
+ 'id': '0'
+ }
+ return self.rpc.send_json_rpc_request(validate_address)
+
def get_version(self):
get_version = {
'method': 'get_version',