diff options
129 files changed, 7682 insertions, 1047 deletions
diff --git a/.travis.yml b/.travis.yml index ffb31fc08..d83675869 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,16 +10,12 @@ cache: env: global: - MAKEJOBS=-j3 - - RUN_TESTS=false - - BOOST_TEST_RANDOM=1$TRAVIS_BUILD_ID - CCACHE_SIZE=100M - CCACHE_TEMPDIR=/tmp/.ccache-temp - CCACHE_COMPRESS=1 - CCACHE_DIR=$HOME/.ccache - BASE_OUTDIR=$TRAVIS_BUILD_DIR/out - SDK_URL=https://bitcoincore.org/depends-sources/sdks - - PYTHON_DEBUG=1 - - WINEDEBUG=fixme-all - DOCKER_PACKAGES="build-essential libtool cmake autotools-dev automake pkg-config bsdmainutils curl git ca-certificates ccache" matrix: # ARM v7 @@ -27,20 +23,20 @@ env: # ARM v8 - HOST=aarch64-linux-gnu PACKAGES="python3 gperf g++-aarch64-linux-gnu" # i686 Win - - HOST=i686-w64-mingw32 PACKAGES="python3 nsis g++-mingw-w64-i686" + - HOST=i686-w64-mingw32 DEP_OPTS="NO_QT=1" PACKAGES="python3 g++-mingw-w64-i686 qttools5-dev-tools" # i686 Linux - - HOST=i686-pc-linux-gnu PACKAGES="gperf cmake g++-multilib bc python3-zmq" RUN_TESTS=true + - HOST=i686-pc-linux-gnu PACKAGES="gperf cmake g++-multilib python3-zmq" # Win64 - - HOST=x86_64-w64-mingw32 PACKAGES="cmake python3 nsis g++-mingw-w64-x86-64 wine-binfmt wine64 bc" RUN_TESTS=true + - HOST=x86_64-w64-mingw32 DEP_OPTS="NO_QT=1" PACKAGES="cmake python3 g++-mingw-w64-x86-64 qttools5-dev-tools" # x86_64 Linux - - HOST=x86_64-unknown-linux-gnu PACKAGES="gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev" RUN_TESTS=true + - HOST=x86_64-unknown-linux-gnu PACKAGES="gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev" # Cross-Mac - HOST=x86_64-apple-darwin11 PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git" OSX_SDK=10.11 before_install: - export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g") install: - - env | grep -E '^(CCACHE_|WINEDEBUG|DISPLAY|BOOST_TEST_RANDOM|CONFIG_SHELL)' | tee /tmp/env + - env | grep -E '^(CCACHE_|DISPLAY|CONFIG_SHELL)' | tee /tmp/env - if [[ $HOST = *-mingw32 ]]; then DOCKER_ADMIN="--cap-add SYS_ADMIN"; fi - DOCKER_ID=$(docker run $DOCKER_ADMIN -idt --mount type=bind,src=$TRAVIS_BUILD_DIR,dst=$TRAVIS_BUILD_DIR --mount type=bind,src=$CCACHE_DIR,dst=$CCACHE_DIR -w $TRAVIS_BUILD_DIR --env-file /tmp/env ubuntu:18.04) - DOCKER_EXEC="docker exec $DOCKER_ID" diff --git a/ANONYMITY_NETWORKS.md b/ANONYMITY_NETWORKS.md index 6ac8cd999..a5f18010e 100644 --- a/ANONYMITY_NETWORKS.md +++ b/ANONYMITY_NETWORKS.md @@ -1,7 +1,6 @@ # Anonymity Networks with Monero -Currently only Tor has been integrated into Monero. Providing support for -Kovri/I2P should be minimal, but has not yet been attempted. The usage of +Currently only Tor and I2P have been integrated into Monero. The usage of these networks is still considered experimental - there are a few pessimistic cases where privacy is leaked. The design is intended to maximize privacy of the source of a transaction by broadcasting it over an anonymity network, while @@ -23,8 +22,8 @@ connections enabled. ## P2P Commands -Only handshakes, peer timed syncs, and transaction broadcast messages are -supported over anonymity networks. If one `--add-exclusive-node` onion address +Only handshakes, peer timed syncs and transaction broadcast messages are +supported over anonymity networks. If one `--add-exclusive-node` p2p address is specified, then no syncing will take place and only transaction broadcasting can occur. It is therefore recommended that `--add-exclusive-node` be combined with additional exclusive IPv4 address(es). @@ -47,9 +46,9 @@ separate process. On most systems the configuration will look like: which tells `monerod` that ".onion" p2p addresses can be forwarded to a socks proxy at IP 127.0.0.1 port 9050 with a max of 10 outgoing connections and -".i2p" p2p addresses can be forwarded to a socks proxy at IP 127.0.0.1 port 9000 -with the default max outgoing connections. Since there are no seed nodes for -anonymity connections, peers must be manually specified: +".b32.i2p" p2p addresses can be forwarded to a socks proxy at IP 127.0.0.1 port +9000 with the default max outgoing connections. Since there are no seed nodes +for anonymity connections, peers must be manually specified: > `--add-exclusive-node rveahdfho7wo4b2m.onion:28083` > `--add-peer rveahdfho7wo4b2m.onion:28083` @@ -64,27 +63,28 @@ Receiving anonymity connections is done through the option `--anonymous-inbound`. This option tells `monerod` the inbound address, network type, and max connections: -> `--anonymous-inbound rveahdfho7wo4b2m.onion:28083,127.0.0.28083,25` -> `--anonymous-inbound foobar.i2p:5000,127.0.0.1:30000` +> `--anonymous-inbound rveahdfho7wo4b2m.onion:28083,127.0.0.1:28083,25` +> `--anonymous-inbound cmeua5767mz2q5jsaelk2rxhf67agrwuetaso5dzbenyzwlbkg2q.b32.i2p:5000,127.0.0.1:30000` which tells `monerod` that a max of 25 inbound Tor connections are being received at address "rveahdfho7wo4b2m.onion:28083" and forwarded to `monerod` localhost port 28083, and a default max I2P connections are being received at -address "foobar.i2p:5000" and forwarded to `monerod` localhost port 30000. +address "cmeua5767mz2q5jsaelk2rxhf67agrwuetaso5dzbenyzwlbkg2q.b32.i2p:5000" and +forwarded to `monerod` localhost port 30000. These addresses will be shared with outgoing peers, over the same network type, otherwise the peer will not be notified of the peer address by the proxy. ### Network Types -#### Tor +#### Tor & I2P -Options `--add-exclusive-node` and `--add-peer` recognize ".onion" addresses, -and will properly forward those addresses to the proxy provided with -`--proxy tor,...`. +Options `--add-exclusive-node` and `--add-peer` recognize ".onion" and +".b32.i2p" addresses, and will properly forward those addresses to the proxy +provided with `--proxy tor,...` or `--proxy i2p,...`. -Option `--anonymous-inbound` also recognizes ".onion" addresses, and will -automatically be sent out to outgoing Tor connections so the peer can -distribute the address to its other peers. +Option `--anonymous-inbound` also recognizes ".onion" and ".b32.i2p" addresses, +and will automatically be sent out to outgoing Tor/I2P connections so the peer +can distribute the address to its other peers. ##### Configuration @@ -99,11 +99,8 @@ This will store key information in `/var/lib/tor/data/monero` and will forward `/usr/lib/tor/data/monero/hostname` will contain the ".onion" address for use with `--anonymous-inbound`. -#### Kovri/I2P - -Support for this network has not been implemented. Using ".i2p" addresses or -specifying "i2p" will currently generate an error. - +I2P must be configured with a standard server tunnel. Configuration differs by +I2P implementation. ## Privacy Limitations @@ -132,11 +129,11 @@ more difficult. ### Bandwidth Usage An ISP can passively monitor `monerod` connections from a node and observe when -a transaction is sent over a Tor/Kovri connection via timing analysis + size of -data sent during that timeframe. Kovri should provide better protection against +a transaction is sent over a Tor/I2P connection via timing analysis + size of +data sent during that timeframe. I2P should provide better protection against this attack - its connections are not circuit based. However, if a node is -only using Kovri for broadcasting Monero transactions, the total aggregate of -Kovri/I2P data would also leak information. +only using I2P for broadcasting Monero transactions, the total aggregate of +I2P data would also leak information. #### Mitigation @@ -165,15 +162,15 @@ simply a best effort attempt. ### Active Bandwidth Shaping An attacker could attempt to bandwidth shape traffic in an attempt to determine -the source of a Tor/Kovri/I2P connection. There isn't great mitigation against -this, but Kovri/I2P should provide better protection against this attack since +the source of a Tor/I2P connection. There isn't great mitigation against +this, but I2P should provide better protection against this attack since the connections are not circuit based. #### Mitigation -The best mitigiation is to use Kovri/I2P instead of Tor. However, Kovri/I2P +The best mitigiation is to use I2P instead of Tor. However, I2P has a smaller set of users (less cover traffic) and academic reviews, so there is a tradeoff in potential isses. Also, anyone attempting this strategy really wants to uncover a user, it seems unlikely that this would be performed against -every Tor/Kovri/I2P user. +every Tor/I2P user. diff --git a/CMakeLists.txt b/CMakeLists.txt index 31c3dbd21..90b16cab8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,8 @@ message(STATUS "CMake version ${CMAKE_VERSION}") project(monero) +enable_language(C ASM) + function (die msg) if (NOT WIN32) string(ASCII 27 Esc) @@ -687,7 +689,6 @@ else() endif() if(BACKCOMPAT) - add_definitions(-DFDELT_TYPE=long\ int) add_linker_flag_if_supported(-Wl,--wrap=__divmoddi4 LD_BACKCOMPAT_FLAGS) add_linker_flag_if_supported(-Wl,--wrap=glob LD_BACKCOMPAT_FLAGS) message(STATUS "Using Lib C back compat flags: ${LD_BACKCOMPAT_FLAGS}") @@ -898,7 +899,7 @@ if(MINGW) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wa,-mbig-obj") set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi;crypt32;bcrypt) if(DEPENDS) - set(ICU_LIBRARIES sicuio sicuin sicuuc sicudt sicutu iconv) + set(ICU_LIBRARIES icuio icui18n icuuc icudata icutu iconv) else() set(ICU_LIBRARIES icuio icuin icuuc icudt icutu iconv) endif() @@ -29,6 +29,12 @@ Our researchers are available on IRC in [#monero-research-lab on Freenode](https - You can subscribe to an [announcement listserv](https://lists.getmonero.org) to get critical announcements from the Monero core team. The announcement list can be very helpful for knowing when software updates are needed. +## Translations +The CLI wallet is available in different languages. If you want to help translate it, join Pootle, our self-hosted localization platform: [translate.getmonero.org](https://translate.getmonero.org). Every translation *must* be uploaded on Pootle, pull requests directly editing the code on this repository will be closed. + + +If you need help/support/info, contact the localization workgroup. You can do so in various ways: by email (translate[at]getmonero[dot]org), on the official chat (#monero-translations). A complete list of contacts can be found on the repository of the workgroup: [monero-ecosystem/monero-translations](https://github.com/monero-ecosystem/monero-translations#contacts). + ## Build ### IMPORTANT @@ -169,6 +175,9 @@ build the library binary manually. This can be done with the following command ` Debian / Ubuntu one liner for all dependencies ``` sudo apt update && sudo apt install build-essential cmake pkg-config libboost-all-dev libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev doxygen graphviz libpgm-dev``` +FreeBSD one liner for required to build dependencies +```pkg install git gmake cmake pkgconf boost-libs cppzmq libsodium``` + ### Cloning the repository Clone recursively to pull-in needed submodule(s): @@ -190,7 +199,7 @@ invokes cmake commands as needed. * Change to the root of the source code directory, change to the most recent release branch, and build: cd monero - git checkout v0.13.0.4 + git checkout release-v0.13 make *Optional*: If your machine has several cores and enough memory, enable @@ -254,7 +263,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch ( ``` git clone https://github.com/monero-project/monero.git cd monero - git checkout tags/v0.13.0.4 + git checkout tags/release-v0.13 ``` * Build: ``` @@ -351,9 +360,9 @@ 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.13.0.0'. If you dont 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. 'release-0.13'. If you dont care about the version and just want binaries from master, skip this step: - git checkout v0.13.0.4 + git checkout release-v0.13 * If you are on a 64-bit system, run: @@ -377,7 +386,7 @@ application. ### On FreeBSD: -The project can be built from scratch by following instructions for Linux above. If you are running monero in a jail you need to add the flag: `allow.sysvipc=1` to your jail configuration, otherwise lmdb will throw the error message: `Failed to open lmdb environment: Function not implemented`. +The project can be built from scratch by following instructions for Linux above(but use `gmake` instead of `make`). If you are running monero in a jail you need to add the flag: `allow.sysvipc=1` to your jail configuration, otherwise lmdb will throw the error message: `Failed to open lmdb environment: Function not implemented`. We expect to add Monero into the ports tree in the near future, which will aid in managing installations using ports or packages. diff --git a/contrib/depends/Makefile b/contrib/depends/Makefile index bf33d706d..afa61b93b 100644 --- a/contrib/depends/Makefile +++ b/contrib/depends/Makefile @@ -94,8 +94,8 @@ $(host_arch)_$(host_os)_id_string+=$(shell $(host_CXX) --version 2>/dev/null) $(host_arch)_$(host_os)_id_string+=$(shell $(host_RANLIB) --version 2>/dev/null) $(host_arch)_$(host_os)_id_string+=$(shell $(host_STRIP) --version 2>/dev/null) - -packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) +qt_packages_$(NO_QT) = $(qt_packages) +packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(qt_packages_) native_packages += $($(host_arch)_$(host_os)_native_packages) $($(host_os)_native_packages) all_packages = $(packages) $(native_packages) diff --git a/contrib/depends/packages/cmake/conf/mxe-conf.cmake.in b/contrib/depends/packages/cmake/conf/mxe-conf.cmake.in deleted file mode 100644 index 8bd4cf1f7..000000000 --- a/contrib/depends/packages/cmake/conf/mxe-conf.cmake.in +++ /dev/null @@ -1,67 +0,0 @@ -# This file is part of MXE. See LICENSE.md for licensing information. - -# https://cmake.org/cmake/help/latest - -# Can't set `cmake_minimum_required` or `cmake_policy` in toolchain -# since toolchain is read before CMakeLists.txt -# See `target-cmake.in` for CMAKE_POLICY_DEFAULT_CMPNNNN - -# Check if we are using mxe supplied version -# - toolchain is included multiple times so set a guard in -# environment to suppress duplicate messages -if(NOT ${CMAKE_COMMAND} STREQUAL @PREFIX@/@BUILD@/bin/cmake AND NOT DEFINED ENV{_MXE_CMAKE_TOOLCHAIN_INCLUDED}) - message(WARNING " -** Warning: direct use of toolchain file is deprecated -** Please use prefixed wrapper script instead: - @TARGET@-cmake [options] <path-to-source> - - uses mxe supplied cmake version @CMAKE_VERSION@ - - loads toolchain - - loads common run results - - sets various policy defaults - ") - set(ENV{_MXE_CMAKE_TOOLCHAIN_INCLUDED} TRUE) -endif() - -## General configuration -set(CMAKE_SYSTEM_NAME Windows) -set(MSYS 1) -set(CMAKE_EXPORT_NO_PACKAGE_REGISTRY ON) -# Workaround for https://www.cmake.org/Bug/view.php?id=14075 -set(CMAKE_CROSS_COMPILING ON) - - -## Library config -set(BUILD_SHARED_LIBS @CMAKE_SHARED_BOOL@ CACHE BOOL "BUILD_SHARED_LIBS" FORCE) -set(BUILD_STATIC_LIBS @CMAKE_STATIC_BOOL@ CACHE BOOL "BUILD_STATIC_LIBS" FORCE) -set(BUILD_SHARED @CMAKE_SHARED_BOOL@ CACHE BOOL "BUILD_SHARED" FORCE) -set(BUILD_STATIC @CMAKE_STATIC_BOOL@ CACHE BOOL "BUILD_STATIC" FORCE) -set(LIBTYPE @LIBTYPE@) - - -## Paths etc. -set(CMAKE_FIND_ROOT_PATH @PREFIX@/@TARGET@) -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_PREFIX_PATH @PREFIX@/@TARGET@) -set(CMAKE_INSTALL_PREFIX @PREFIX@/@TARGET@ CACHE PATH "Installation Prefix") -# For custom mxe FindPackage scripts -set(CMAKE_MODULE_PATH "@PREFIX@/share/cmake/modules" ${CMAKE_MODULE_PATH}) - - -## Programs -set(CMAKE_C_COMPILER @PREFIX@/bin/@TARGET@-gcc) -set(CMAKE_CXX_COMPILER @PREFIX@/bin/@TARGET@-g++) -set(CMAKE_Fortran_COMPILER @PREFIX@/bin/@TARGET@-gfortran) -set(CMAKE_RC_COMPILER @PREFIX@/bin/@TARGET@-windres) -# CMAKE_RC_COMPILE_OBJECT is defined in: -# <cmake root>/share/cmake-X.Y/Modules/Platform/Windows-windres.cmake -set(CPACK_NSIS_EXECUTABLE @TARGET@-makensis) - -## Individual package configuration -file(GLOB mxe_cmake_files - "@CMAKE_TOOLCHAIN_DIR@/*.cmake" -) -foreach(mxe_cmake_file ${mxe_cmake_files}) - include(${mxe_cmake_file}) -endforeach() diff --git a/contrib/depends/packages/cmake/conf/target-cmake.in b/contrib/depends/packages/cmake/conf/target-cmake.in deleted file mode 100644 index a78bcf58d..000000000 --- a/contrib/depends/packages/cmake/conf/target-cmake.in +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash -echo "== Using MXE wrapper: @PREFIX@/bin/@TARGET@-cmake" - -# https://cmake.org/cmake/help/latest/manual/cmake-policies.7.html -# https://cmake.org/cmake/help/latest/variable/CMAKE_POLICY_DEFAULT_CMPNNNN.html -POLICIES=(0017,0020) - -unset NO_MXE_TOOLCHAIN -if echo -- "$@" | grep -Ewq "(--build|-E|--system-information)" ; then - NO_MXE_TOOLCHAIN=1 -fi -if [[ "$NO_MXE_TOOLCHAIN" == "1" ]]; then - echo "== Skip using MXE toolchain: @CMAKE_TOOLCHAIN_FILE@" - # see https://github.com/mxe/mxe/issues/932 - exec "@PREFIX@/@BUILD@/bin/cmake" "$@" -else - echo " - cmake version @CMAKE_VERSION@" - echo " - warnings for unused CMAKE_POLICY_DEFAULT variables can be ignored" - echo "== Using MXE toolchain: @CMAKE_TOOLCHAIN_FILE@" - echo "== Using MXE runresult: @CMAKE_RUNRESULT_FILE@" - if ! ( echo "$@" | grep --silent "DCMAKE_BUILD_TYPE" ) ; then - echo '== Adding "-DCMAKE_BUILD_TYPE=Release"' - set -- "-DCMAKE_BUILD_TYPE=Release" "$@" - fi - exec "@PREFIX@/@BUILD@/bin/cmake" \ - -DCMAKE_TOOLCHAIN_FILE="@CMAKE_TOOLCHAIN_FILE@" \ - `eval echo -DCMAKE_POLICY_DEFAULT_CMP{$POLICIES}=NEW` \ - -C"@CMAKE_RUNRESULT_FILE@" "$@" -fi diff --git a/contrib/depends/packages/cmake/test/CMakeLists.txt b/contrib/depends/packages/cmake/test/CMakeLists.txt deleted file mode 100644 index 6075a8f5a..000000000 --- a/contrib/depends/packages/cmake/test/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -# This file is part of MXE. See LICENSE.md for licensing information. - -# 2.8.9 is Debian Wheezy version -cmake_minimum_required(VERSION 2.8.9) - -# use default C and CXX languages -project(mxe) - -# see cmake --help-policy <cmp> for details -cmake_policy(SET CMP0017 NEW) -if (POLICY CMP0020) - cmake_policy(SET CMP0020 NEW) -endif() - -# so we can find pkg-test.cmake files to include -set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/../.. ${CMAKE_MODULE_PATH}) - -include(${PKG}-test) diff --git a/contrib/depends/packages/icu4c.mk b/contrib/depends/packages/icu4c.mk index 370a02683..2b3845488 100644 --- a/contrib/depends/packages/icu4c.mk +++ b/contrib/depends/packages/icu4c.mk @@ -6,7 +6,7 @@ $(package)_sha256_hash=1f912c54035533fb4268809701d65c7468d00e292efbc31e644490845 $(package)_patches=icu-001-dont-build-static-dynamic-twice.patch define $(package)_set_vars - $(package)_build_opts=CFLAGS="$($(package)_cflags) $($(package)_cppflags) -DU_USING_ICU_NAMESPACE=0 --std=gnu++0x -DU_STATIC_IMPLEMENTATION -DU_COMBINED_IMPLEMENTATION -fPIC -DENABLE_STATIC=YES -DPGKDATA_MODE=static" + $(package)_build_opts=CFLAGS="$($(package)_cflags) $($(package)_cppflags) -DU_USING_ICU_NAMESPACE=0 -DU_STATIC_IMPLEMENTATION -DU_COMBINED_IMPLEMENTATION -fPIC -DENABLE_STATIC=YES -DPGKDATA_MODE=static" endef define $(package)_config_cmds @@ -17,7 +17,7 @@ define $(package)_config_cmds sh ../source/runConfigureICU Linux &&\ make &&\ cd ../buildb &&\ - sh ../source/$($(package)_autoconf) --enable-static=yes --disable-shared --disable-layout --disable-layoutex --disable-tests --disable-samples --prefix=$(host_prefix) --with-cross-build=`pwd`/../builda &&\ + sh ../source/runConfigureICU MinGW --enable-static=yes --disable-shared --disable-layout --disable-layoutex --disable-tests --disable-samples --prefix=$(host_prefix) --with-cross-build=`pwd`/../builda &&\ $(MAKE) $($(package)_build_opts) endef diff --git a/contrib/depends/packages/native_protobuf.mk b/contrib/depends/packages/native_protobuf.mk index 83e602341..35f648b9a 100644 --- a/contrib/depends/packages/native_protobuf.mk +++ b/contrib/depends/packages/native_protobuf.mk @@ -15,12 +15,11 @@ define $(package)_config_cmds endef define $(package)_build_cmds - $(MAKE) -C src libprotobuf.la all + $(MAKE) -C src endef define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) -C src install install-libLTLIBRARIES install-nobase_includeHEADERS &&\ - $(MAKE) DESTDIR=$($(package)_staging_dir) install-pkgconfigDATA + $(MAKE) DESTDIR=$($(package)_staging_dir) -C src install endef define $(package)_postprocess_cmds diff --git a/contrib/depends/packages/openssl.mk b/contrib/depends/packages/openssl.mk index 5ee9f17a6..e920b4409 100644 --- a/contrib/depends/packages/openssl.mk +++ b/contrib/depends/packages/openssl.mk @@ -1,35 +1,28 @@ package=openssl -$(package)_version=1.0.1k +$(package)_version=1.0.2q $(package)_download_path=https://www.openssl.org/source $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=8f9faeaebad088e772f4ef5e38252d472be4d878c6b3a2718c10a4fcebe7a41c +$(package)_sha256_hash=5744cfcbcec2b1b48629f7354203bc1e5e9b5466998bbccc5b5fcde3b18eb684 define $(package)_set_vars $(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" $(package)_config_opts=--prefix=$(host_prefix) --openssldir=$(host_prefix)/etc/openssl -$(package)_config_opts+=no-camellia $(package)_config_opts+=no-capieng -$(package)_config_opts+=no-cast -$(package)_config_opts+=no-comp $(package)_config_opts+=no-dso $(package)_config_opts+=no-dtls1 $(package)_config_opts+=no-ec_nistp_64_gcc_128 $(package)_config_opts+=no-gost $(package)_config_opts+=no-gmp $(package)_config_opts+=no-heartbeats -$(package)_config_opts+=no-idea $(package)_config_opts+=no-jpake $(package)_config_opts+=no-krb5 $(package)_config_opts+=no-libunbound $(package)_config_opts+=no-md2 -$(package)_config_opts+=no-mdc2 -$(package)_config_opts+=no-rc4 $(package)_config_opts+=no-rc5 $(package)_config_opts+=no-rdrand $(package)_config_opts+=no-rfc3779 $(package)_config_opts+=no-rsax $(package)_config_opts+=no-sctp -$(package)_config_opts+=no-seed $(package)_config_opts+=no-sha0 $(package)_config_opts+=no-shared $(package)_config_opts+=no-ssl-trace @@ -39,7 +32,6 @@ $(package)_config_opts+=no-static_engine $(package)_config_opts+=no-store $(package)_config_opts+=no-unit-test $(package)_config_opts+=no-weak-ssl-ciphers -$(package)_config_opts+=no-whirlpool $(package)_config_opts+=no-zlib $(package)_config_opts+=no-zlib-dynamic $(package)_config_opts+=$($(package)_cflags) $($(package)_cppflags) diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk index 4800c9936..1db50580b 100644 --- a/contrib/depends/packages/packages.mk +++ b/contrib/depends/packages/packages.mk @@ -1,10 +1,11 @@ -packages:=boost openssl zeromq cppzmq expat ldns cppzmq readline libiconv qt hidapi protobuf libusb +packages:=boost openssl zeromq cppzmq expat ldns cppzmq readline 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 linux_packages = eudev +qt_packages = qt ifeq ($(host_os),linux) packages += unwind diff --git a/contrib/depends/packages/readline.mk b/contrib/depends/packages/readline.mk index 826c1029e..afefc7f07 100644 --- a/contrib/depends/packages/readline.mk +++ b/contrib/depends/packages/readline.mk @@ -1,9 +1,8 @@ package=readline -$(package)_version=6.3 -$(package)_download_path=ftp://ftp.cwru.edu/pub/bash/ +$(package)_version=8.0 +$(package)_download_path=https://ftp.gnu.org/gnu/readline $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=56ba6071b9462f980c5a72ab0023893b65ba6debb4eeb475d7a563dc65cafd43 -$(package)_patches=readline-1.patch +$(package)_sha256_hash=e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461 define $(package)_set_vars $(package)_build_opts=CC="$($(package)_cc)" @@ -15,7 +14,6 @@ define $(package)_set_vars endef define $(package)_config_cmds - patch -p1 < $($(package)_patch_dir)/readline-1.patch &&\ export bash_cv_have_mbstate_t=yes &&\ export bash_cv_wcwidth_broken=yes &&\ ./configure $($(package)_config_opts) @@ -29,5 +27,3 @@ define $(package)_stage_cmds $(MAKE) DESTDIR=$($(package)_staging_dir) install endef -define $(package)_postprocess_cmds -endef diff --git a/contrib/depends/patches/readline/readline-1.patch b/contrib/depends/patches/readline/readline-1.patch deleted file mode 100644 index 7610a29eb..000000000 --- a/contrib/depends/patches/readline/readline-1.patch +++ /dev/null @@ -1,187 +0,0 @@ -From c0572cecbeadc8fe24c70c5c39d49210a39ac719 Mon Sep 17 00:00:00 2001 -From: Timothy Gu <timothygu99@gmail.com> -Date: Tue, 30 Sep 2014 10:32:33 -0700 -Subject: [PATCH 1/2] signals: safeguard the remaining usage of frequently - missing signals - -diff --git a/input.c b/input.c -index 117dfe8..465f0b9 100644 ---- a/input.c -+++ b/input.c -@@ -532,9 +532,17 @@ rl_getc (stream) - Otherwise (not EINTR), some error occurred, also signifying EOF. */ - if (errno != EINTR) - return (RL_ISSTATE (RL_STATE_READCMD) ? READERR : EOF); -- else if (_rl_caught_signal == SIGHUP || _rl_caught_signal == SIGTERM) -+ else if (_rl_caught_signal == SIGTERM -+#if defined(SIGHUP) -+ || _rl_caught_signal == SIGHUP -+#endif -+ ) - return (RL_ISSTATE (RL_STATE_READCMD) ? READERR : EOF); -- else if (_rl_caught_signal == SIGINT || _rl_caught_signal == SIGQUIT) -+ else if (_rl_caught_signal == SIGINT -+#if defined(SIGQUIT) -+ || _rl_caught_signal == SIGQUIT -+#endif -+ ) - RL_CHECK_SIGNALS (); - - if (rl_signal_event_hook) -diff --git a/signals.c b/signals.c -index 61f02f9..7c921d6 100644 ---- a/signals.c -+++ b/signals.c -@@ -216,7 +216,9 @@ _rl_handle_signal (sig) - /* FALLTHROUGH */ - - case SIGTERM: -+#if defined (SIGHUP) - case SIGHUP: -+#endif - #if defined (SIGTSTP) - case SIGTSTP: - case SIGTTOU: -@@ -426,7 +428,9 @@ rl_set_signals () - - rl_maybe_set_sighandler (SIGINT, rl_signal_handler, &old_int); - rl_maybe_set_sighandler (SIGTERM, rl_signal_handler, &old_term); -+#if defined (SIGHUP) - rl_maybe_set_sighandler (SIGHUP, rl_signal_handler, &old_hup); -+#endif - #if defined (SIGQUIT) - rl_maybe_set_sighandler (SIGQUIT, rl_signal_handler, &old_quit); - #endif -@@ -491,7 +495,9 @@ rl_clear_signals () - overhead */ - rl_maybe_restore_sighandler (SIGINT, &old_int); - rl_maybe_restore_sighandler (SIGTERM, &old_term); -+#if defined (SIGHUP) - rl_maybe_restore_sighandler (SIGHUP, &old_hup); -+#endif - #if defined (SIGQUIT) - rl_maybe_restore_sighandler (SIGQUIT, &old_quit); - #endif --- -1.8.3.2 - - -From 6896ffa4fc85bf0dfae58e69a860d2076c1d9fd2 Mon Sep 17 00:00:00 2001 -From: Timothy Gu <timothygu99@gmail.com> -Date: Tue, 30 Sep 2014 17:16:32 -0700 -Subject: [PATCH 2/2] Handle missing S_IS* macros more gracefully - -diff --git a/colors.c b/colors.c -index 89d9035..ec19844 100644 ---- a/colors.c -+++ b/colors.c -@@ -152,14 +152,22 @@ _rl_print_color_indicator (char *f) - { - colored_filetype = C_FILE; - -+#if defined (S_ISUID) - if ((mode & S_ISUID) != 0 && is_colored (C_SETUID)) - colored_filetype = C_SETUID; -- else if ((mode & S_ISGID) != 0 && is_colored (C_SETGID)) -+ else -+#endif -+#if defined (S_ISGID) -+ if ((mode & S_ISGID) != 0 && is_colored (C_SETGID)) - colored_filetype = C_SETGID; -- else if (is_colored (C_CAP) && 0) //f->has_capability) -+ else -+#endif -+ if (is_colored (C_CAP) && 0) //f->has_capability) - colored_filetype = C_CAP; -+#if defined(S_IXUGO) - else if ((mode & S_IXUGO) != 0 && is_colored (C_EXEC)) - colored_filetype = C_EXEC; -+#endif - else if ((1 < astat.st_nlink) && is_colored (C_MULTIHARDLINK)) - colored_filetype = C_MULTIHARDLINK; - } -@@ -173,8 +181,10 @@ _rl_print_color_indicator (char *f) - colored_filetype = C_STICKY_OTHER_WRITABLE; - else - #endif -+#if defined (S_IWOTH) - if ((mode & S_IWOTH) != 0 && is_colored (C_OTHER_WRITABLE)) - colored_filetype = C_OTHER_WRITABLE; -+#endif - #if defined (S_ISVTX) - else if ((mode & S_ISVTX) != 0 && is_colored (C_STICKY)) - colored_filetype = C_STICKY; -diff --git a/colors.h b/colors.h -index fc926e5..e62edd0 100644 ---- a/colors.h -+++ b/colors.h -@@ -96,7 +96,7 @@ enum indicator_no - }; - - --#if !S_IXUGO -+#if !S_IXUGO && defined(S_IXUSR) && defined(S_IXGRP) && defined(S_IXOTH) - # define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH) - #endif - -diff --git a/posixstat.h b/posixstat.h -index 3eb7f29..854a2c9 100644 ---- a/posixstat.h -+++ b/posixstat.h -@@ -78,30 +78,44 @@ - - #if defined (S_IFBLK) && !defined (S_ISBLK) - #define S_ISBLK(m) (((m)&S_IFMT) == S_IFBLK) /* block device */ -+#elif !defined (S_IFBLK) -+#define S_ISBLK(m) 0 - #endif - - #if defined (S_IFCHR) && !defined (S_ISCHR) - #define S_ISCHR(m) (((m)&S_IFMT) == S_IFCHR) /* character device */ -+#elif !defined (S_IFCHR) -+#define S_ISCHR(m) 0 - #endif - - #if defined (S_IFDIR) && !defined (S_ISDIR) - #define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) /* directory */ -+#elif !defined (S_IFDIR) -+#define S_ISDIR(m) 0 - #endif - - #if defined (S_IFREG) && !defined (S_ISREG) - #define S_ISREG(m) (((m)&S_IFMT) == S_IFREG) /* file */ -+#elif !defined (S_IFREG) -+#define S_ISREG(m) 0 - #endif - - #if defined (S_IFIFO) && !defined (S_ISFIFO) - #define S_ISFIFO(m) (((m)&S_IFMT) == S_IFIFO) /* fifo - named pipe */ -+#elif !defined (S_IFIFO) -+#define S_ISFIFO(m) 0 - #endif - - #if defined (S_IFLNK) && !defined (S_ISLNK) - #define S_ISLNK(m) (((m)&S_IFMT) == S_IFLNK) /* symbolic link */ -+#elif !defined (S_IFLNK) -+#define S_ISLNK(m) 0 - #endif - - #if defined (S_IFSOCK) && !defined (S_ISSOCK) - #define S_ISSOCK(m) (((m)&S_IFMT) == S_IFSOCK) /* socket */ -+#elif !defined (S_IFSOCK) -+#define S_ISSOCK(m) 0 - #endif - - /* -@@ -137,6 +151,8 @@ - /* These are non-standard, but are used in builtins.c$symbolic_umask() */ - #define S_IRUGO (S_IRUSR | S_IRGRP | S_IROTH) - #define S_IWUGO (S_IWUSR | S_IWGRP | S_IWOTH) -+#if defined(S_IXUSR) && defined(S_IXGRP) && defined(S_IXOTH) - #define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH) -+#endif - - #endif /* _POSIXSTAT_H_ */ --- -1.8.3.2 - diff --git a/contrib/epee/include/math_helper.h b/contrib/epee/include/math_helper.h index e22e8ee6e..35b649972 100644 --- a/contrib/epee/include/math_helper.h +++ b/contrib/epee/include/math_helper.h @@ -243,6 +243,7 @@ namespace math_helper present = present << 32; present |= fileTime.dwLowDateTime; present /= 10; // mic-sec + return present; #else struct timeval tv; gettimeofday(&tv, NULL); diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h index 6f9e4db5b..76773192e 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.h +++ b/contrib/epee/include/net/abstract_tcp_server2.h @@ -46,6 +46,7 @@ #include <memory> #include <boost/asio.hpp> +#include <boost/asio/ssl.hpp> #include <boost/array.hpp> #include <boost/noncopyable.hpp> #include <boost/shared_ptr.hpp> @@ -102,15 +103,19 @@ namespace net_utils /// Construct a connection with the given io_service. explicit connection( boost::asio::io_service& io_service, boost::shared_ptr<shared_state> state, - t_connection_type connection_type); + t_connection_type connection_type, + epee::net_utils::ssl_support_t ssl_support, + ssl_context_t &ssl_context); explicit connection( boost::asio::ip::tcp::socket&& sock, - boost::shared_ptr<shared_state> state, - t_connection_type connection_type); + boost::shared_ptr<shared_state> state, + t_connection_type connection_type, + epee::net_utils::ssl_support_t ssl_support, + ssl_context_t &ssl_context); + + virtual ~connection() noexcept(false); - /// Get the socket associated with the connection. - boost::asio::ip::tcp::socket& socket(); /// Start the first asynchronous operation for the connection. bool start(bool is_income, bool is_multithreaded); @@ -143,6 +148,10 @@ namespace net_utils //------------------------------------------------------ boost::shared_ptr<connection<t_protocol_handler> > safe_shared_from_this(); bool shutdown(); + /// Handle completion of a receive operation. + void handle_receive(const boost::system::error_code& e, + std::size_t bytes_transferred); + /// Handle completion of a read operation. void handle_read(const boost::system::error_code& e, std::size_t bytes_transferred); @@ -160,7 +169,7 @@ namespace net_utils /// Buffer for incoming data. boost::array<char, 8192> buffer_; - //boost::array<char, 1024> buffer_; + size_t buffer_ssl_init_fill; t_connection_context context; @@ -199,6 +208,13 @@ namespace net_utils class boosted_tcp_server : private boost::noncopyable { + enum try_connect_result_t + { + CONNECT_SUCCESS, + CONNECT_FAILURE, + CONNECT_NO_SSL, + }; + public: typedef boost::shared_ptr<connection<t_protocol_handler> > connection_ptr; typedef typename t_protocol_handler::connection_context t_connection_context; @@ -212,8 +228,8 @@ 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"); - bool init_server(const std::string port, const std::string& address = "0.0.0.0"); + bool init_server(uint32_t port, const std::string address = "0.0.0.0", epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair<std::string, std::string> &private_key_and_certificate_path = std::make_pair(std::string(), std::string()), const std::list<std::string> &allowed_certificates = {}, bool allow_any_cert = false); + bool init_server(const std::string port, const std::string& address = "0.0.0.0", epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair<std::string, std::string> &private_key_and_certificate_path = std::make_pair(std::string(), std::string()), const std::list<std::string> &allowed_certificates = {}, bool allow_any_cert = false); /// 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()); @@ -241,10 +257,11 @@ namespace net_utils default_remote = std::move(remote); } - bool add_connection(t_connection_context& out, boost::asio::ip::tcp::socket&& sock, network_address real_remote); - bool connect(const std::string& adr, const std::string& port, uint32_t conn_timeot, t_connection_context& cn, const std::string& bind_ip = "0.0.0.0"); + bool add_connection(t_connection_context& out, boost::asio::ip::tcp::socket&& sock, network_address real_remote, epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect); + try_connect_result_t try_connect(connection_ptr new_connection_l, const std::string& adr, const std::string& port, boost::asio::ip::tcp::socket &sock_, const boost::asio::ip::tcp::endpoint &remote_endpoint, const std::string &bind_ip, uint32_t conn_timeout, epee::net_utils::ssl_support_t ssl_support); + bool connect(const std::string& adr, const std::string& port, uint32_t conn_timeot, t_connection_context& cn, const std::string& bind_ip = "0.0.0.0", epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect); template<class t_callback> - bool connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeot, const t_callback &cb, const std::string& bind_ip = "0.0.0.0"); + bool connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeot, const t_callback &cb, const std::string& bind_ip = "0.0.0.0", epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect); typename t_protocol_handler::config_type& get_config_object() { @@ -364,6 +381,9 @@ namespace net_utils boost::mutex connections_mutex; std::set<connection_ptr> connections_; + ssl_context_t m_ssl_context; + std::list<std::string> m_allowed_certificates; + }; // class <>boosted_tcp_server diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index eda4d7f7b..39d6911e3 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -78,19 +78,23 @@ PRAGMA_WARNING_DISABLE_VS(4355) template<class t_protocol_handler> connection<t_protocol_handler>::connection( boost::asio::io_service& io_service, boost::shared_ptr<shared_state> state, - t_connection_type connection_type + t_connection_type connection_type, + epee::net_utils::ssl_support_t ssl_support, + ssl_context_t &ssl_context ) - : connection(boost::asio::ip::tcp::socket{io_service}, std::move(state), connection_type) + : connection(boost::asio::ip::tcp::socket{io_service}, std::move(state), connection_type, ssl_support, ssl_context) { } template<class t_protocol_handler> connection<t_protocol_handler>::connection( boost::asio::ip::tcp::socket&& sock, boost::shared_ptr<shared_state> state, - t_connection_type connection_type + t_connection_type connection_type, + epee::net_utils::ssl_support_t ssl_support, + ssl_context_t &ssl_context ) : - connection_basic(std::move(sock), state), + connection_basic(std::move(sock), state, ssl_support, ssl_context), m_protocol_handler(this, check_and_get(state).config, context), m_connection_type( connection_type ), m_throttle_speed_in("speed_in", "throttle_speed_in"), @@ -109,17 +113,11 @@ PRAGMA_WARNING_DISABLE_VS(4355) { if(!m_was_shutdown) { - _dbg3("[sock " << socket_.native_handle() << "] Socket destroyed without shutdown."); + _dbg3("[sock " << socket().native_handle() << "] Socket destroyed without shutdown."); shutdown(); } - _dbg3("[sock " << socket_.native_handle() << "] Socket destroyed"); - } - //--------------------------------------------------------------------------------- - template<class t_protocol_handler> - boost::asio::ip::tcp::socket& connection<t_protocol_handler>::socket() - { - return socket_; + _dbg3("[sock " << socket().native_handle() << "] Socket destroyed"); } //--------------------------------------------------------------------------------- template<class t_protocol_handler> @@ -142,7 +140,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) TRY_ENTRY(); boost::system::error_code ec; - auto remote_ep = socket_.remote_endpoint(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"); @@ -168,10 +166,11 @@ PRAGMA_WARNING_DISABLE_VS(4355) const boost::uuids::uuid random_uuid = boost::uuids::random_generator()(); context = t_connection_context{}; - context.set_details(random_uuid, std::move(real_remote), is_income); + bool ssl = m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled; + context.set_details(random_uuid, std::move(real_remote), is_income, ssl); boost::system::error_code ec; - auto local_ep = socket_.local_endpoint(ec); + auto local_ep = socket().local_endpoint(ec); CHECK_AND_NO_ASSERT_MES(!ec, false, "Failed to get local endpoint: " << ec.message() << ':' << ec.value()); _dbg3("[sock " << socket_.native_handle() << "] new connection from " << print_connection_context_short(context) << @@ -180,7 +179,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) if(static_cast<shared_state&>(get_stats()).pfilter && !static_cast<shared_state&>(get_stats()).pfilter->is_remote_host_allowed(context.m_remote_address)) { - _dbg2("[sock " << socket_.native_handle() << "] host denied " << context.m_remote_address.host_str() << ", shutdowning connection"); + _dbg2("[sock " << socket().native_handle() << "] host denied " << context.m_remote_address.host_str() << ", shutdowning connection"); close(); return false; } @@ -192,11 +191,21 @@ PRAGMA_WARNING_DISABLE_VS(4355) reset_timer(get_default_timeout(), false); - socket_.async_read_some(boost::asio::buffer(buffer_), - strand_.wrap( - boost::bind(&connection<t_protocol_handler>::handle_read, self, - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred))); + // first read on the raw socket to detect SSL for the server + buffer_ssl_init_fill = 0; + if (is_income && m_ssl_support != epee::net_utils::ssl_support_t::e_ssl_support_disabled) + socket().async_receive(boost::asio::buffer(buffer_), + boost::asio::socket_base::message_peek, + strand_.wrap( + boost::bind(&connection<t_protocol_handler>::handle_receive, self, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred))); + else + async_read_some(boost::asio::buffer(buffer_), + strand_.wrap( + boost::bind(&connection<t_protocol_handler>::handle_read, self, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred))); #if !defined(_WIN32) || !defined(__i686) // not supported before Windows7, too lazy for runtime check // Just exclude for 32bit windows builds @@ -204,12 +213,12 @@ PRAGMA_WARNING_DISABLE_VS(4355) int tos = get_tos_flag(); boost::asio::detail::socket_option::integer< IPPROTO_IP, IP_TOS > optionTos( tos ); - socket_.set_option( optionTos ); + socket().set_option( optionTos ); //_dbg1("Set ToS flag to " << tos); #endif boost::asio::ip::tcp::no_delay noDelayOption(false); - socket_.set_option(noDelayOption); + socket().set_option(noDelayOption); return true; @@ -234,7 +243,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) template<class t_protocol_handler> boost::asio::io_service& connection<t_protocol_handler>::get_io_service() { - return socket_.get_io_service(); + return socket().get_io_service(); } //--------------------------------------------------------------------------------- template<class t_protocol_handler> @@ -246,9 +255,9 @@ PRAGMA_WARNING_DISABLE_VS(4355) auto self = safe_shared_from_this(); if(!self) return false; - //_dbg3("[sock " << socket_.native_handle() << "] add_ref, m_peer_number=" << mI->m_peer_number); + //_dbg3("[sock " << socket().native_handle() << "] add_ref, m_peer_number=" << mI->m_peer_number); CRITICAL_REGION_LOCAL(self->m_self_refs_lock); - //_dbg3("[sock " << socket_.native_handle() << "] add_ref 2, m_peer_number=" << mI->m_peer_number); + //_dbg3("[sock " << socket().native_handle() << "] add_ref 2, m_peer_number=" << mI->m_peer_number); if(m_was_shutdown) return false; ++m_reference_count; @@ -262,9 +271,9 @@ PRAGMA_WARNING_DISABLE_VS(4355) { TRY_ENTRY(); boost::shared_ptr<connection<t_protocol_handler> > back_connection_copy; - LOG_TRACE_CC(context, "[sock " << socket_.native_handle() << "] release"); + LOG_TRACE_CC(context, "[sock " << socket().native_handle() << "] release"); CRITICAL_REGION_BEGIN(m_self_refs_lock); - CHECK_AND_ASSERT_MES(m_reference_count, false, "[sock " << socket_.native_handle() << "] m_reference_count already at 0 at connection<t_protocol_handler>::release() call"); + CHECK_AND_ASSERT_MES(m_reference_count, false, "[sock " << socket().native_handle() << "] m_reference_count already at 0 at connection<t_protocol_handler>::release() call"); // is this the last reference? if (--m_reference_count == 0) { // move the held reference to a local variable, keeping the object alive until the function terminates @@ -290,7 +299,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) std::string address, port; boost::system::error_code e; - boost::asio::ip::tcp::endpoint endpoint = socket_.remote_endpoint(e); + boost::asio::ip::tcp::endpoint endpoint = socket().remote_endpoint(e); if (e) { address = "<not connected>"; @@ -302,7 +311,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) port = boost::lexical_cast<std::string>(endpoint.port()); } MDEBUG(" connection type " << to_string( m_connection_type ) << " " - << socket_.local_endpoint().address().to_string() << ":" << socket_.local_endpoint().port() + << socket().local_endpoint().address().to_string() << ":" << socket().local_endpoint().port() << " <--> " << context.m_remote_address.str() << " (via " << address << ":" << port << ")"); } //--------------------------------------------------------------------------------- @@ -311,7 +320,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) std::size_t bytes_transferred) { TRY_ENTRY(); - //_info("[sock " << socket_.native_handle() << "] Async read calledback."); + //_info("[sock " << socket().native_handle() << "] Async read calledback."); if (!e) { @@ -347,7 +356,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) } while(delay > 0); } // any form of sleeping - //_info("[sock " << socket_.native_handle() << "] RECV " << bytes_transferred); + //_info("[sock " << socket().native_handle() << "] RECV " << bytes_transferred); logger_handle_net_read(bytes_transferred); context.m_last_recv = time(NULL); context.m_recv_cnt += bytes_transferred; @@ -355,7 +364,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) bool recv_res = m_protocol_handler.handle_recv(buffer_.data(), bytes_transferred); if(!recv_res) { - //_info("[sock " << socket_.native_handle() << "] protocol_want_close"); + //_info("[sock " << socket().native_handle() << "] protocol_want_close"); //some error in protocol, protocol handler ask to close connection boost::interprocess::ipcdetail::atomic_write32(&m_want_close_connection, 1); @@ -369,24 +378,24 @@ PRAGMA_WARNING_DISABLE_VS(4355) }else { reset_timer(get_timeout_from_bytes_read(bytes_transferred), false); - socket_.async_read_some(boost::asio::buffer(buffer_), + async_read_some(boost::asio::buffer(buffer_), strand_.wrap( boost::bind(&connection<t_protocol_handler>::handle_read, connection<t_protocol_handler>::shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred))); - //_info("[sock " << socket_.native_handle() << "]Async read requested."); + //_info("[sock " << socket().native_handle() << "]Async read requested."); } }else { - _dbg3("[sock " << socket_.native_handle() << "] Some not success at read: " << e.message() << ':' << e.value()); + _dbg3("[sock " << socket().native_handle() << "] Some not success at read: " << e.message() << ':' << e.value()); if(e.value() != 2) { - _dbg3("[sock " << socket_.native_handle() << "] Some problems at read: " << e.message() << ':' << e.value()); + _dbg3("[sock " << socket().native_handle() << "] Some problems at read: " << e.message() << ':' << e.value()); shutdown(); } else { - _dbg3("[sock " << socket_.native_handle() << "] peer closed connection"); + _dbg3("[sock " << socket().native_handle() << "] peer closed connection"); if (m_ready_to_close) shutdown(); } @@ -400,13 +409,85 @@ PRAGMA_WARNING_DISABLE_VS(4355) } //--------------------------------------------------------------------------------- template<class t_protocol_handler> + void connection<t_protocol_handler>::handle_receive(const boost::system::error_code& e, + std::size_t bytes_transferred) + { + TRY_ENTRY(); + if (e) + { + // offload the error case + handle_read(e, bytes_transferred); + return; + } + + reset_timer(get_timeout_from_bytes_read(bytes_transferred), false); + + buffer_ssl_init_fill += bytes_transferred; + if (buffer_ssl_init_fill <= get_ssl_magic_size()) + { + socket().async_receive(boost::asio::buffer(buffer_.data() + buffer_ssl_init_fill, buffer_.size() - buffer_ssl_init_fill), + boost::asio::socket_base::message_peek, + strand_.wrap( + boost::bind(&connection<t_protocol_handler>::handle_receive, connection<t_protocol_handler>::shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred))); + return; + } + + // detect SSL + if (m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect) + { + if (is_ssl((const unsigned char*)buffer_.data(), buffer_ssl_init_fill)) + { + MDEBUG("That looks like SSL"); + m_ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_enabled; // read/write to the SSL socket + } + else + { + MDEBUG("That does not look like SSL"); + m_ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_disabled; // read/write to the raw socket + } + } + + if (m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled) + { + // Handshake + if (!handshake(boost::asio::ssl::stream_base::server)) + { + MERROR("SSL handshake failed"); + boost::interprocess::ipcdetail::atomic_write32(&m_want_close_connection, 1); + bool do_shutdown = false; + CRITICAL_REGION_BEGIN(m_send_que_lock); + if(!m_send_que.size()) + do_shutdown = true; + CRITICAL_REGION_END(); + if(do_shutdown) + shutdown(); + return; + } + } + + async_read_some(boost::asio::buffer(buffer_), + strand_.wrap( + boost::bind(&connection<t_protocol_handler>::handle_read, connection<t_protocol_handler>::shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred))); + + // If an error occurs then no new asynchronous operations are started. This + // means that all shared_ptr references to the connection object will + // disappear and the object will be destroyed automatically after this + // handler returns. The connection class's destructor closes the socket. + CATCH_ENTRY_L0("connection<t_protocol_handler>::handle_receive", void()); + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> bool connection<t_protocol_handler>::call_run_once_service_io() { TRY_ENTRY(); if(!m_is_multithreaded) { //single thread model, we can wait in blocked call - size_t cnt = socket_.get_io_service().run_one(); + size_t cnt = socket().get_io_service().run_one(); if(!cnt)//service is going to quit return false; }else @@ -416,7 +497,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) //if no handlers were called //TODO: Maybe we need to have have critical section + event + callback to upper protocol to //ask it inside(!) critical region if we still able to go in event wait... - size_t cnt = socket_.get_io_service().poll_one(); + size_t cnt = socket().get_io_service().poll_one(); if(!cnt) misc_utils::sleep_no_w(1); } @@ -525,7 +606,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) context.m_max_speed_up = std::max(context.m_max_speed_up, context.m_current_speed_up); } - //_info("[sock " << socket_.native_handle() << "] SEND " << cb); + //_info("[sock " << socket().native_handle() << "] SEND " << cb); context.m_last_send = time(NULL); context.m_send_cnt += cb; //some data should be wrote to stream @@ -570,7 +651,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) MDEBUG("do_send_chunk() NOW just queues: packet="<<size_now<<" B, is added to queue-size="<<m_send_que.size()); //do_send_handler_delayed( ptr , size_now ); // (((H))) // empty function - LOG_TRACE_CC(context, "[sock " << socket_.native_handle() << "] Async send requested " << m_send_que.front().size()); + LOG_TRACE_CC(context, "[sock " << socket().native_handle() << "] Async send requested " << m_send_que.front().size()); } else { // no active operation @@ -588,14 +669,14 @@ PRAGMA_WARNING_DISABLE_VS(4355) CHECK_AND_ASSERT_MES( size_now == m_send_que.front().size(), false, "Unexpected queue size"); reset_timer(get_default_timeout(), false); - boost::asio::async_write(socket_, boost::asio::buffer(m_send_que.front().data(), size_now ) , + async_write(boost::asio::buffer(m_send_que.front().data(), size_now ) , //strand_.wrap( boost::bind(&connection<t_protocol_handler>::handle_write, self, _1, _2) //) ); //_dbg3("(chunk): " << size_now); //logger_handle_net_write(size_now); - //_info("[sock " << socket_.native_handle() << "] Async send requested " << m_send_que.front().size()); + //_info("[sock " << socket().native_handle() << "] Async send requested " << m_send_que.front().size()); } //do_send_handler_stop( ptr , cb ); // empty function @@ -680,7 +761,8 @@ PRAGMA_WARNING_DISABLE_VS(4355) // Initiate graceful connection closure. m_timer.cancel(); boost::system::error_code ignored_ec; - socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); + socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); + socket().close(); if (!m_host.empty()) { try { host_count(m_host, -1); } catch (...) { /* ignore */ } @@ -698,7 +780,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) auto self = safe_shared_from_this(); if(!self) return false; - //_info("[sock " << socket_.native_handle() << "] Que Shutdown called."); + //_info("[sock " << socket().native_handle() << "] Que Shutdown called."); m_timer.cancel(); size_t send_que_size = 0; CRITICAL_REGION_BEGIN(m_send_que_lock); @@ -733,11 +815,11 @@ PRAGMA_WARNING_DISABLE_VS(4355) void connection<t_protocol_handler>::handle_write(const boost::system::error_code& e, size_t cb) { TRY_ENTRY(); - LOG_TRACE_CC(context, "[sock " << socket_.native_handle() << "] Async send calledback " << cb); + LOG_TRACE_CC(context, "[sock " << socket().native_handle() << "] Async send calledback " << cb); if (e) { - _dbg1("[sock " << socket_.native_handle() << "] Some problems at write: " << e.message() << ':' << e.value()); + _dbg1("[sock " << socket().native_handle() << "] Some problems at write: " << e.message() << ':' << e.value()); shutdown(); return; } @@ -752,7 +834,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) CRITICAL_REGION_BEGIN(m_send_que_lock); if(m_send_que.empty()) { - _erro("[sock " << socket_.native_handle() << "] m_send_que.size() == 0 at handle_write!"); + _erro("[sock " << socket().native_handle() << "] m_send_que.size() == 0 at handle_write!"); return; } @@ -772,11 +854,11 @@ PRAGMA_WARNING_DISABLE_VS(4355) if (speed_limit_is_enabled()) do_send_handler_write_from_queue(e, m_send_que.front().size() , m_send_que.size()); // (((H))) CHECK_AND_ASSERT_MES( size_now == m_send_que.front().size(), void(), "Unexpected queue size"); - boost::asio::async_write(socket_, boost::asio::buffer(m_send_que.front().data(), size_now) , - // strand_.wrap( - boost::bind(&connection<t_protocol_handler>::handle_write, connection<t_protocol_handler>::shared_from_this(), _1, _2) - // ) - ); + async_write(boost::asio::buffer(m_send_que.front().data(), size_now) , + // strand_.wrap( + boost::bind(&connection<t_protocol_handler>::handle_write, connection<t_protocol_handler>::shared_from_this(), _1, _2) + // ) + ); //_dbg3("(normal)" << size_now); } CRITICAL_REGION_END(); @@ -817,7 +899,8 @@ PRAGMA_WARNING_DISABLE_VS(4355) m_threads_count(0), m_thread_index(0), m_connection_type( connection_type ), - new_connection_() + new_connection_(), + m_ssl_context({boost::asio::ssl::context(boost::asio::ssl::context::sslv23), {}}) { create_server_type_map(); m_thread_name_prefix = "NET"; @@ -833,7 +916,8 @@ PRAGMA_WARNING_DISABLE_VS(4355) m_threads_count(0), m_thread_index(0), m_connection_type(connection_type), - new_connection_() + new_connection_(), + m_ssl_context({boost::asio::ssl::context(boost::asio::ssl::context::sslv23), {}}) { create_server_type_map(); m_thread_name_prefix = "NET"; @@ -855,12 +939,14 @@ 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) + bool boosted_tcp_server<t_protocol_handler>::init_server(uint32_t port, const std::string address, epee::net_utils::ssl_support_t ssl_support, const std::pair<std::string, std::string> &private_key_and_certificate_path, const std::list<std::string> &allowed_certificates, bool allow_any_cert) { TRY_ENTRY(); m_stop_signal_sent = false; m_port = port; m_address = address; + if (ssl_support != epee::net_utils::ssl_support_t::e_ssl_support_disabled) + m_ssl_context = create_ssl_context(private_key_and_certificate_path, allowed_certificates, allow_any_cert); // 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); @@ -872,7 +958,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) 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)); + new_connection_.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, ssl_support, m_ssl_context)); acceptor_.async_accept(new_connection_->socket(), boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept, this, boost::asio::placeholders::error)); @@ -894,7 +980,7 @@ 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) + bool boosted_tcp_server<t_protocol_handler>::init_server(const std::string port, const std::string& address, epee::net_utils::ssl_support_t ssl_support, const std::pair<std::string, std::string> &private_key_and_certificate_path, const std::list<std::string> &allowed_certificates, bool allow_any_cert) { uint32_t p = 0; @@ -902,7 +988,7 @@ DISABLE_GCC_WARNING(maybe-uninitialized) MERROR("Failed to convert port no = " << port); return false; } - return this->init_server(p, address); + return this->init_server(p, address, ssl_support, private_key_and_certificate_path, allowed_certificates, allow_any_cert); } POP_WARNINGS //--------------------------------------------------------------------------------- @@ -1066,11 +1152,18 @@ POP_WARNINGS if (!e) { if (m_connection_type == e_connection_type_RPC) { - MDEBUG("New server for RPC connections"); + 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)); + new_connection_.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, conn->get_ssl_support(), m_ssl_context)); acceptor_.async_accept(new_connection_->socket(), boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept, this, boost::asio::placeholders::error)); @@ -1078,10 +1171,16 @@ POP_WARNINGS boost::asio::socket_base::keep_alive opt(true); conn->socket().set_option(opt); + bool res; if (default_remote.get_type_id() == net_utils::address_type::invalid) - conn->start(true, 1 < m_threads_count); + res = conn->start(true, 1 < m_threads_count); else - conn->start(true, 1 < m_threads_count, default_remote); + res = conn->start(true, 1 < m_threads_count, default_remote); + if (!res) + { + conn->cancel(); + return; + } conn->save_dbg_log(); return; } @@ -1099,18 +1198,18 @@ 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_.reset(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, new_connection_->get_ssl_support(), m_ssl_context)); acceptor_.async_accept(new_connection_->socket(), boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept, this, boost::asio::placeholders::error)); } //--------------------------------------------------------------------------------- template<class t_protocol_handler> - bool boosted_tcp_server<t_protocol_handler>::add_connection(t_connection_context& out, boost::asio::ip::tcp::socket&& sock, network_address real_remote) + bool boosted_tcp_server<t_protocol_handler>::add_connection(t_connection_context& out, boost::asio::ip::tcp::socket&& sock, network_address real_remote, epee::net_utils::ssl_support_t ssl_support) { if(std::addressof(get_io_service()) == std::addressof(sock.get_io_service())) { - connection_ptr conn(new connection<t_protocol_handler>(std::move(sock), m_state, m_connection_type)); + connection_ptr conn(new connection<t_protocol_handler>(std::move(sock), m_state, m_connection_type, ssl_support, m_ssl_context)); if(conn->start(false, 1 < m_threads_count, std::move(real_remote))) { conn->get_context(out); @@ -1126,34 +1225,10 @@ POP_WARNINGS } //--------------------------------------------------------------------------------- template<class t_protocol_handler> - bool boosted_tcp_server<t_protocol_handler>::connect(const std::string& adr, const std::string& port, uint32_t conn_timeout, t_connection_context& conn_context, const std::string& bind_ip) + typename boosted_tcp_server<t_protocol_handler>::try_connect_result_t boosted_tcp_server<t_protocol_handler>::try_connect(connection_ptr new_connection_l, const std::string& adr, const std::string& port, boost::asio::ip::tcp::socket &sock_, const boost::asio::ip::tcp::endpoint &remote_endpoint, const std::string &bind_ip, uint32_t conn_timeout, epee::net_utils::ssl_support_t ssl_support) { TRY_ENTRY(); - connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type) ); - connections_mutex.lock(); - connections_.insert(new_connection_l); - MDEBUG("connections_ size now " << connections_.size()); - connections_mutex.unlock(); - 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(); - - ////////////////////////////////////////////////////////////////////////// - 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::asio::ip::tcp::resolver::iterator end; - if(iterator == end) - { - _erro("Failed to resolve " << adr); - return false; - } - ////////////////////////////////////////////////////////////////////////// - - - //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); - sock_.open(remote_endpoint.protocol()); if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" ) { @@ -1165,7 +1240,7 @@ POP_WARNINGS MERROR("Error binding to " << bind_ip << ": " << ec.message()); if (sock_.is_open()) sock_.close(); - return false; + return CONNECT_FAILURE; } } @@ -1199,14 +1274,14 @@ POP_WARNINGS { if (sock_.is_open()) sock_.close(); - return false; + return CONNECT_FAILURE; } if(local_shared_context->ec == boost::asio::error::would_block && !r) { //timeout sock_.close(); _dbg3("Failed to connect to " << adr << ":" << port << ", because of timeout (" << conn_timeout << ")"); - return false; + return CONNECT_FAILURE; } } ec = local_shared_context->ec; @@ -1216,11 +1291,79 @@ POP_WARNINGS _dbg3("Some problems at connect, message: " << ec.message()); if (sock_.is_open()) sock_.close(); - return false; + return CONNECT_FAILURE; } _dbg3("Connected success to " << adr << ':' << port); + const epee::net_utils::ssl_support_t ssl_support = new_connection_l->get_ssl_support(); + if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled || ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect) + { + // Handshake + MDEBUG("Handshaking SSL..."); + if (!new_connection_l->handshake(boost::asio::ssl::stream_base::client)) + { + if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect) + { + boost::system::error_code ignored_ec; + sock_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); + sock_.close(); + return CONNECT_NO_SSL; + } + MERROR("SSL handshake failed"); + if (sock_.is_open()) + sock_.close(); + return CONNECT_FAILURE; + } + } + + return CONNECT_SUCCESS; + + CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::try_connect", CONNECT_FAILURE); + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + bool boosted_tcp_server<t_protocol_handler>::connect(const std::string& adr, const std::string& port, uint32_t conn_timeout, t_connection_context& conn_context, const std::string& bind_ip, epee::net_utils::ssl_support_t ssl_support) + { + TRY_ENTRY(); + + connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, ssl_support, m_ssl_context) ); + connections_mutex.lock(); + connections_.insert(new_connection_l); + MDEBUG("connections_ size now " << connections_.size()); + connections_mutex.unlock(); + 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(); + + ////////////////////////////////////////////////////////////////////////// + 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::asio::ip::tcp::resolver::iterator end; + if(iterator == end) + { + _erro("Failed to resolve " << adr); + return false; + } + ////////////////////////////////////////////////////////////////////////// + + + //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); + 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) + { + // 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); + if (try_connect_result != CONNECT_SUCCESS) + return false; + } + // start adds the connection to the config object's list, so we don't need to have it locally anymore connections_mutex.lock(); connections_.erase(new_connection_l); @@ -1245,10 +1388,10 @@ POP_WARNINGS } //--------------------------------------------------------------------------------- template<class t_protocol_handler> template<class t_callback> - bool boosted_tcp_server<t_protocol_handler>::connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeout, const t_callback &cb, const std::string& bind_ip) + bool boosted_tcp_server<t_protocol_handler>::connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeout, const t_callback &cb, const std::string& bind_ip, epee::net_utils::ssl_support_t ssl_support) { TRY_ENTRY(); - connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type) ); + connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_state, m_connection_type, ssl_support, m_ssl_context) ); connections_mutex.lock(); connections_.insert(new_connection_l); MDEBUG("connections_ size now " << connections_.size()); diff --git a/contrib/epee/include/net/connection_basic.hpp b/contrib/epee/include/net/connection_basic.hpp index b1b271db9..328f9afbf 100644 --- a/contrib/epee/include/net/connection_basic.hpp +++ b/contrib/epee/include/net/connection_basic.hpp @@ -47,8 +47,10 @@ #include <memory> #include <boost/asio.hpp> +#include <boost/asio/ssl.hpp> #include "net/net_utils_base.h" +#include "net/net_ssl.h" #include "syncobj.h" namespace epee @@ -95,16 +97,56 @@ class connection_basic { // not-templated base class for rapid developmet of som /// Strand to ensure the connection's handlers are not called concurrently. boost::asio::io_service::strand strand_; /// Socket for the connection. - boost::asio::ip::tcp::socket socket_; + ssl_context_t &m_ssl_context; + ssl_support_t m_ssl_support; + boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_; public: // first counter is the ++/-- count of current sockets, the other socket_number is only-increasing ++ number generator - connection_basic(boost::asio::ip::tcp::socket&& socket, boost::shared_ptr<socket_stats> stats); + connection_basic(boost::asio::ip::tcp::socket&& socket, boost::shared_ptr<socket_stats> stats, ssl_support_t ssl_support, ssl_context_t &ssl_context); + connection_basic(boost::asio::io_service &io_service, boost::shared_ptr<socket_stats> stats, ssl_support_t ssl_support, ssl_context_t &ssl_context); virtual ~connection_basic() noexcept(false); //! \return `socket_stats` object passed in construction (ptr never changes). socket_stats& get_stats() noexcept { return *m_stats; /* verified in constructor */ } + connection_basic(boost::asio::io_service& io_service, std::atomic<long> &ref_sock_count, std::atomic<long> &sock_number, ssl_support_t ssl, ssl_context_t &ssl_context); + + boost::asio::ip::tcp::socket& socket() { return socket_.next_layer(); } + ssl_support_t get_ssl_support() const { return m_ssl_support; } + void disable_ssl() { m_ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_disabled; } + + bool handshake(boost::asio::ssl::stream_base::handshake_type type) + { + return ssl_handshake(socket_, type, m_ssl_context); + } + + template<typename MutableBufferSequence, typename ReadHandler> + void async_read_some(const MutableBufferSequence &buffers, ReadHandler &&handler) + { + if (m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled) + socket_.async_read_some(buffers, std::forward<ReadHandler>(handler)); + else + socket().async_read_some(buffers, std::forward<ReadHandler>(handler)); + } + + template<typename ConstBufferSequence, typename WriteHandler> + void async_write_some(const ConstBufferSequence &buffers, WriteHandler &&handler) + { + if (m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled) + socket_.async_write_some(buffers, std::forward<WriteHandler>(handler)); + else + socket().async_write_some(buffers, std::forward<WriteHandler>(handler)); + } + + template<typename ConstBufferSequence, typename WriteHandler> + void async_write(const ConstBufferSequence &buffers, WriteHandler &&handler) + { + if (m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled) + boost::asio::async_write(socket_, buffers, std::forward<WriteHandler>(handler)); + else + boost::asio::async_write(socket(), buffers, std::forward<WriteHandler>(handler)); + } // various handlers to be called from connection class: void do_send_handler_write(const void * ptr , size_t cb); diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h index e01691794..34b3ac06c 100644 --- a/contrib/epee/include/net/http_client.h +++ b/contrib/epee/include/net/http_client.h @@ -275,7 +275,10 @@ namespace net_utils chunked_state m_chunked_state; std::string m_chunked_cache; critical_section m_lock; - bool m_ssl; + epee::net_utils::ssl_support_t m_ssl_support; + std::pair<std::string, std::string> m_ssl_private_key_and_certificate_path; + std::list<std::string> m_ssl_allowed_certificates; + bool m_ssl_allow_any_cert; public: explicit http_simple_client_template() @@ -293,35 +296,39 @@ namespace net_utils , m_chunked_state() , m_chunked_cache() , m_lock() - , m_ssl(false) + , m_ssl_support(epee::net_utils::ssl_support_t::e_ssl_support_autodetect) {} const std::string &get_host() const { return m_host_buff; }; const std::string &get_port() const { return m_port; }; - bool set_server(const std::string& address, boost::optional<login> user, bool ssl = false) + bool set_server(const std::string& address, boost::optional<login> user, epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair<std::string, std::string> &private_key_and_certificate_path = {}, const std::list<std::string> &allowed_ssl_certificates = {}, bool allow_any_cert = false) { http::url_content parsed{}; const bool r = parse_url(address, parsed); CHECK_AND_ASSERT_MES(r, false, "failed to parse url: " << address); - set_server(std::move(parsed.host), std::to_string(parsed.port), std::move(user), ssl); + set_server(std::move(parsed.host), std::to_string(parsed.port), std::move(user), ssl_support, private_key_and_certificate_path, allowed_ssl_certificates, allow_any_cert); return true; } - void set_server(std::string host, std::string port, boost::optional<login> user, bool ssl = false) + void set_server(std::string host, std::string port, boost::optional<login> user, epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair<std::string, std::string> &private_key_and_certificate_path = {}, const std::list<std::string> &allowed_ssl_certificates = {}, bool allow_any_cert = false) { CRITICAL_REGION_LOCAL(m_lock); disconnect(); m_host_buff = std::move(host); m_port = std::move(port); m_auth = user ? http_client_auth{std::move(*user)} : http_client_auth{}; - m_ssl = ssl; + m_ssl_support = ssl_support; + m_ssl_private_key_and_certificate_path = private_key_and_certificate_path; + m_ssl_allowed_certificates = allowed_ssl_certificates; + m_ssl_allow_any_cert = allow_any_cert; + m_net_client.set_ssl(m_ssl_support, m_ssl_private_key_and_certificate_path, m_ssl_allowed_certificates, m_ssl_allow_any_cert); } bool connect(std::chrono::milliseconds timeout) { CRITICAL_REGION_LOCAL(m_lock); - return m_net_client.connect(m_host_buff, m_port, timeout, m_ssl); + return m_net_client.connect(m_host_buff, m_port, timeout, "0.0.0.0"); } //--------------------------------------------------------------------------- bool disconnect() @@ -330,10 +337,10 @@ namespace net_utils return m_net_client.disconnect(); } //--------------------------------------------------------------------------- - bool is_connected() + bool is_connected(bool *ssl = NULL) { CRITICAL_REGION_LOCAL(m_lock); - return m_net_client.is_connected(); + return m_net_client.is_connected(ssl); } //--------------------------------------------------------------------------- virtual bool handle_target_data(std::string& piece_of_transfer) diff --git a/contrib/epee/include/net/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl index b53bdc200..ae8e43477 100644 --- a/contrib/epee/include/net/http_protocol_handler.inl +++ b/contrib/epee/include/net/http_protocol_handler.inl @@ -677,7 +677,7 @@ namespace net_utils //add additional fields, if it is for(fields_list::const_iterator it = response.m_additional_fields.begin(); it!=response.m_additional_fields.end(); it++) - buf += it->first + ":" + it->second + "\r\n"; + buf += it->first + ": " + it->second + "\r\n"; buf+="\r\n"; diff --git a/contrib/epee/include/net/http_server_impl_base.h b/contrib/epee/include/net/http_server_impl_base.h index 5669824c1..236067580 100644 --- a/contrib/epee/include/net/http_server_impl_base.h +++ b/contrib/epee/include/net/http_server_impl_base.h @@ -58,7 +58,10 @@ 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", std::vector<std::string> access_control_origins = std::vector<std::string>(), - boost::optional<net_utils::http::login> user = boost::none) + boost::optional<net_utils::http::login> user = boost::none, + epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, + const std::pair<std::string, std::string> &private_key_and_certificate_path = {}, + const std::list<std::string> &allowed_certificates = std::list<std::string>(), bool allow_any_cert = false) { //set self as callback handler @@ -75,7 +78,7 @@ 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); + bool res = m_net_server.init_server(bind_port, bind_ip, ssl_support, private_key_and_certificate_path, allowed_certificates, allow_any_cert); if(!res) { LOG_ERROR("Failed to bind server"); diff --git a/contrib/epee/include/net/net_helper.h b/contrib/epee/include/net/net_helper.h index 94744ac21..5d9bb61cf 100644 --- a/contrib/epee/include/net/net_helper.h +++ b/contrib/epee/include/net/net_helper.h @@ -40,6 +40,7 @@ #include <boost/lambda/lambda.hpp> #include <boost/interprocess/detail/atomic.hpp> #include "net/net_utils_base.h" +#include "net/net_ssl.h" #include "misc_language.h" #undef MONERO_DEFAULT_LOG_CATEGORY @@ -57,6 +58,13 @@ namespace net_utils class blocked_mode_client { + enum try_connect_result_t + { + CONNECT_SUCCESS, + CONNECT_FAILURE, + CONNECT_NO_SSL, + }; + struct handler_obj @@ -84,9 +92,9 @@ namespace net_utils m_connected(false), m_deadline(m_io_service), m_shutdowned(0), - m_ssl(false), - m_ctx(boost::asio::ssl::context::sslv23), - m_ssl_socket(m_io_service,m_ctx) + m_ssl_support(epee::net_utils::ssl_support_t::e_ssl_support_autodetect), + m_ctx({boost::asio::ssl::context(boost::asio::ssl::context::sslv23), {}}), + m_ssl_socket(new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(m_io_service,m_ctx.context)) { @@ -110,28 +118,92 @@ namespace net_utils catch(...) { /* ignore */ } } + inline void set_ssl(epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, const std::pair<std::string, std::string> &private_key_and_certificate_path = {}, const std::list<std::string> &allowed_certificates = std::list<std::string>(), bool allow_any_cert = false) + { + if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_disabled) + m_ctx = {boost::asio::ssl::context(boost::asio::ssl::context::sslv23), {}}; + else + m_ctx = create_ssl_context(private_key_and_certificate_path, allowed_certificates, allow_any_cert); + m_ssl_support = ssl_support; + } + inline - bool connect(const std::string& addr, int port, std::chrono::milliseconds timeout, bool ssl = false, const std::string& bind_ip = "0.0.0.0") + bool connect(const std::string& addr, int port, std::chrono::milliseconds timeout, const std::string& bind_ip = "0.0.0.0") { - return connect(addr, std::to_string(port), timeout, ssl, bind_ip); + return connect(addr, std::to_string(port), timeout, bind_ip); } inline - bool connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout, bool ssl = false, const std::string& bind_ip = "0.0.0.0") + try_connect_result_t try_connect(const std::string& addr, const std::string& port, const boost::asio::ip::tcp::endpoint &remote_endpoint, std::chrono::milliseconds timeout, const std::string& bind_ip, epee::net_utils::ssl_support_t ssl_support) { - m_connected = false; - m_ssl = ssl; + m_ssl_socket->next_layer().open(remote_endpoint.protocol()); + if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" ) + { + boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(addr.c_str()), 0); + m_ssl_socket->next_layer().bind(local_endpoint); + } + + + m_deadline.expires_from_now(timeout); + + boost::system::error_code ec = boost::asio::error::would_block; + + m_ssl_socket->next_layer().async_connect(remote_endpoint, boost::lambda::var(ec) = boost::lambda::_1); + while (ec == boost::asio::error::would_block) + { + m_io_service.run_one(); + } + + if (!ec && m_ssl_socket->next_layer().is_open()) + { + m_connected = true; + m_deadline.expires_at(std::chrono::steady_clock::time_point::max()); + // SSL Options + if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled || ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect) + { + if (!ssl_handshake(*m_ssl_socket, boost::asio::ssl::stream_base::client, m_ctx)) + { + if (ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect) + { + boost::system::error_code ignored_ec; + m_ssl_socket->next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); + m_ssl_socket->next_layer().close(); + m_connected = false; + return CONNECT_NO_SSL; + } + else + { + MWARNING("Failed to establish SSL connection"); + m_connected = false; + return CONNECT_FAILURE; + } + } + m_ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_enabled; + } + return CONNECT_SUCCESS; + }else + { + MWARNING("Some problems at connect, message: " << ec.message()); + return CONNECT_FAILURE; + } + + } + + inline + bool connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout, const std::string& bind_ip = "0.0.0.0") + { + m_connected = false; try { - m_ssl_socket.next_layer().close(); + m_ssl_socket->next_layer().close(); // Set SSL options // disable sslv2 - m_ctx.set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2); - m_ctx.set_default_verify_paths(); + m_ctx.context.set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2); + m_ctx.context.set_default_verify_paths(); + m_ssl_socket.reset(new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(m_io_service, m_ctx.context)); // Get a list of endpoints corresponding to the server name. - ////////////////////////////////////////////////////////////////////////// @@ -151,45 +223,20 @@ namespace net_utils //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); - - m_ssl_socket.next_layer().open(remote_endpoint.protocol()); - if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" ) - { - boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(addr.c_str()), 0); - m_ssl_socket.next_layer().bind(local_endpoint); - } - - - m_deadline.expires_from_now(timeout); - - - boost::system::error_code ec = boost::asio::error::would_block; - - m_ssl_socket.next_layer().async_connect(remote_endpoint, boost::lambda::var(ec) = boost::lambda::_1); - while (ec == boost::asio::error::would_block) - { - m_io_service.run_one(); - } - - if (!ec && m_ssl_socket.next_layer().is_open()) + try_connect_result_t try_connect_result = try_connect(addr, port, remote_endpoint, timeout, bind_ip, m_ssl_support); + if (try_connect_result == CONNECT_FAILURE) + return false; + if (m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect) { - m_connected = true; - m_deadline.expires_at(std::chrono::steady_clock::time_point::max()); - // SSL Options - if(m_ssl) { - // Disable verification of host certificate - m_ssl_socket.set_verify_mode(boost::asio::ssl::verify_peer); - // Handshake - m_ssl_socket.next_layer().set_option(boost::asio::ip::tcp::no_delay(true)); - m_ssl_socket.handshake(boost::asio::ssl::stream_base::client); + m_ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_enabled; + if (try_connect_result == CONNECT_NO_SSL) + { + MERROR("SSL handshake failed on an autodetect connection, reconnecting without SSL"); + m_ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_disabled; + if (try_connect(addr, port, remote_endpoint, timeout, bind_ip, m_ssl_support) != CONNECT_SUCCESS) + return false; } - return true; - }else - { - MWARNING("Some problems at connect, message: " << ec.message()); - return false; } - } catch(const boost::system::system_error& er) { @@ -213,9 +260,9 @@ namespace net_utils if(m_connected) { m_connected = false; - if(m_ssl) + if(m_ssl_support != epee::net_utils::ssl_support_t::e_ssl_support_disabled) shutdown_ssl(); - m_ssl_socket.next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both); + m_ssl_socket->next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both); } } @@ -342,9 +389,13 @@ namespace net_utils return true; } - bool is_connected() + bool is_connected(bool *ssl = NULL) { - return m_connected && m_ssl_socket.next_layer().is_open(); + if (!m_connected || !m_ssl_socket->next_layer().is_open()) + return false; + if (ssl) + *ssl = m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled; + return true; } inline @@ -506,15 +557,15 @@ namespace net_utils { m_deadline.cancel(); boost::system::error_code ec; - if(m_ssl) + if(m_ssl_support != epee::net_utils::ssl_support_t::e_ssl_support_disabled) shutdown_ssl(); - m_ssl_socket.next_layer().cancel(ec); + m_ssl_socket->next_layer().cancel(ec); if(ec) MDEBUG("Problems at cancel: " << ec.message()); - m_ssl_socket.next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + m_ssl_socket->next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); if(ec) MDEBUG("Problems at shutdown: " << ec.message()); - m_ssl_socket.next_layer().close(ec); + m_ssl_socket->next_layer().close(ec); if(ec) MDEBUG("Problems at close: " << ec.message()); boost::interprocess::ipcdetail::atomic_write32(&m_shutdowned, 1); @@ -533,7 +584,7 @@ namespace net_utils boost::asio::ip::tcp::socket& get_socket() { - return m_ssl_socket.next_layer(); + return m_ssl_socket->next_layer(); } private: @@ -550,7 +601,7 @@ namespace net_utils // connect(), read_line() or write_line() functions to return. LOG_PRINT_L3("Timed out socket"); m_connected = false; - m_ssl_socket.next_layer().close(); + m_ssl_socket->next_layer().close(); // There is no longer an active deadline. The expiry is set to positive // infinity so that the actor takes no action until a new deadline is set. @@ -565,7 +616,7 @@ namespace net_utils // ssl socket shutdown blocks if server doesn't respond. We close after 2 secs boost::system::error_code ec = boost::asio::error::would_block; m_deadline.expires_from_now(std::chrono::milliseconds(2000)); - m_ssl_socket.async_shutdown(boost::lambda::var(ec) = boost::lambda::_1); + m_ssl_socket->async_shutdown(boost::lambda::var(ec) = boost::lambda::_1); while (ec == boost::asio::error::would_block) { m_io_service.run_one(); @@ -586,35 +637,39 @@ namespace net_utils bool write(const void* data, size_t sz, boost::system::error_code& ec) { bool success; - if(m_ssl) - success = boost::asio::write(m_ssl_socket, boost::asio::buffer(data, sz), ec); + if(m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled) + success = boost::asio::write(*m_ssl_socket, boost::asio::buffer(data, sz), ec); else - success = boost::asio::write(m_ssl_socket.next_layer(), boost::asio::buffer(data, sz), ec); + success = boost::asio::write(m_ssl_socket->next_layer(), boost::asio::buffer(data, sz), ec); return success; } void async_write(const void* data, size_t sz, boost::system::error_code& ec) { - if(m_ssl) - boost::asio::async_write(m_ssl_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1); + if(m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled) + boost::asio::async_write(*m_ssl_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1); else - boost::asio::async_write(m_ssl_socket.next_layer(), boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1); + boost::asio::async_write(m_ssl_socket->next_layer(), boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1); } void async_read(char* buff, size_t sz, boost::asio::detail::transfer_at_least_t transfer_at_least, handler_obj& hndlr) { - if(!m_ssl) - boost::asio::async_read(m_ssl_socket.next_layer(), boost::asio::buffer(buff, sz), transfer_at_least, hndlr); + if(m_ssl_support != epee::net_utils::ssl_support_t::e_ssl_support_enabled) + boost::asio::async_read(m_ssl_socket->next_layer(), boost::asio::buffer(buff, sz), transfer_at_least, hndlr); else - boost::asio::async_read(m_ssl_socket, boost::asio::buffer(buff, sz), transfer_at_least, hndlr); + boost::asio::async_read(*m_ssl_socket, boost::asio::buffer(buff, sz), transfer_at_least, hndlr); } protected: boost::asio::io_service m_io_service; - boost::asio::ssl::context m_ctx; - boost::asio::ssl::stream<boost::asio::ip::tcp::socket> m_ssl_socket; - bool m_ssl; + epee::net_utils::ssl_context_t m_ctx; + std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> m_ssl_socket; + epee::net_utils::ssl_support_t m_ssl_support; + std::string m_ssl_private_key; + std::string m_ssl_certificate; + std::list<std::string> m_ssl_allowed_certificates; + bool m_ssl_allow_any_cerl; bool m_initialized; bool m_connected; boost::asio::steady_timer m_deadline; @@ -722,7 +777,7 @@ namespace net_utils // asynchronous operations are cancelled. This allows the blocked // connect(), read_line() or write_line() functions to return. LOG_PRINT_L3("Timed out socket"); - m_ssl_socket.next_layer().close(); + m_ssl_socket->next_layer().close(); // There is no longer an active deadline. The expiry is set to positive // infinity so that the actor takes no action until a new deadline is set. diff --git a/contrib/epee/include/net/net_ssl.h b/contrib/epee/include/net/net_ssl.h new file mode 100644 index 000000000..9ae1883af --- /dev/null +++ b/contrib/epee/include/net/net_ssl.h @@ -0,0 +1,68 @@ +// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of the Andrey N. Sabelnikov 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 OWNER 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. +// + + + +#ifndef _NET_SSL_H +#define _NET_SSL_H + +#include <stdint.h> +#include <string> +#include <list> +#include <boost/utility/string_ref.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/ssl.hpp> + +namespace epee +{ +namespace net_utils +{ + enum class ssl_support_t: uint8_t { + e_ssl_support_disabled, + e_ssl_support_enabled, + e_ssl_support_autodetect, + }; + + struct ssl_context_t + { + boost::asio::ssl::context context; + std::list<std::string> allowed_certificates; + bool allow_any_cert; + }; + + // https://security.stackexchange.com/questions/34780/checking-client-hello-for-https-classification + constexpr size_t get_ssl_magic_size() { return 9; } + bool is_ssl(const unsigned char *data, size_t len); + ssl_context_t create_ssl_context(const std::pair<std::string, std::string> &private_key_and_certificate_path, std::list<std::string> allowed_certificates, bool allow_any_cert); + void use_ssl_certificate(ssl_context_t &ssl_context, const std::pair<std::string, std::string> &private_key_and_certificate_path); + bool create_ssl_certificate(std::string &pkey_buffer, std::string &cert_buffer); + bool is_certificate_allowed(boost::asio::ssl::verify_context &ctx, const std::list<std::string> &allowed_certificates); + bool ssl_handshake(boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &socket, boost::asio::ssl::stream_base::handshake_type type, const epee::net_utils::ssl_context_t &ssl_context); + bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s); +} +} + +#endif //_NET_SSL_H diff --git a/contrib/epee/include/net/net_utils_base.h b/contrib/epee/include/net/net_utils_base.h index 82f8a7fe8..7b5b07ef2 100644 --- a/contrib/epee/include/net/net_utils_base.h +++ b/contrib/epee/include/net/net_utils_base.h @@ -47,6 +47,7 @@ namespace net { class tor_address; + class i2p_address; } namespace epee @@ -196,7 +197,7 @@ namespace net_utils template<typename Type> const Type &as() const { return as_mutable<const Type>(); } BEGIN_KV_SERIALIZE_MAP() - // need to `#include "net/tor_address.h"` when serializing `network_address` + // need to `#include "net/[i2p|tor]_address.h"` when serializing `network_address` static constexpr std::integral_constant<bool, is_store> is_store_{}; std::uint8_t type = std::uint8_t(is_store ? this_ref.get_type_id() : address_type::invalid); @@ -209,6 +210,8 @@ namespace net_utils return this_ref.template serialize_addr<ipv4_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: + return this_ref.template serialize_addr<net::i2p_address>(is_store_, stg, hparent_section); case address_type::invalid: default: break; @@ -241,6 +244,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; time_t m_last_recv; time_t m_last_send; uint64_t m_recv_cnt; @@ -251,13 +255,14 @@ namespace net_utils double m_max_speed_up; connection_context_base(boost::uuids::uuid connection_id, - const network_address &remote_address, bool is_income, + const network_address &remote_address, bool is_income, bool ssl, time_t last_recv = 0, time_t last_send = 0, uint64_t recv_cnt = 0, uint64_t send_cnt = 0): m_connection_id(connection_id), m_remote_address(remote_address), m_is_income(is_income), m_started(time(NULL)), + m_ssl(ssl), m_last_recv(last_recv), m_last_send(last_send), m_recv_cnt(recv_cnt), @@ -272,6 +277,7 @@ namespace net_utils m_remote_address(), m_is_income(false), m_started(time(NULL)), + m_ssl(false), m_last_recv(0), m_last_send(0), m_recv_cnt(0), @@ -284,17 +290,17 @@ namespace net_utils connection_context_base& operator=(const connection_context_base& a) { - set_details(a.m_connection_id, a.m_remote_address, a.m_is_income); + set_details(a.m_connection_id, a.m_remote_address, a.m_is_income, a.m_ssl); return *this; } private: template<class t_protocol_handler> friend class connection; - void set_details(boost::uuids::uuid connection_id, const network_address &remote_address, bool is_income) + void set_details(boost::uuids::uuid connection_id, const network_address &remote_address, bool is_income, bool ssl) { this->~connection_context_base(); - new(this) connection_context_base(connection_id, remote_address, is_income); + new(this) connection_context_base(connection_id, remote_address, is_income, ssl); } }; diff --git a/contrib/epee/include/net/network_throttle-detail.hpp b/contrib/epee/include/net/network_throttle-detail.hpp index 955668d62..9d12291f4 100644 --- a/contrib/epee/include/net/network_throttle-detail.hpp +++ b/contrib/epee/include/net/network_throttle-detail.hpp @@ -36,6 +36,7 @@ #ifndef INCLUDED_throttle_detail_hpp #define INCLUDED_throttle_detail_hpp +#include <boost/circular_buffer.hpp> #include "network_throttle.hpp" namespace epee @@ -61,7 +62,7 @@ class network_throttle : public i_network_throttle { network_time_seconds m_slot_size; // the size of one slot. TODO: now hardcoded for 1 second e.g. in time_to_slot() // TODO for big window size, for performance better the substract on change of m_last_sample_time instead of recalculating average of eg >100 elements - std::vector< packet_info > m_history; // the history of bw usage + boost::circular_buffer< packet_info > m_history; // the history of bw usage network_time_seconds m_last_sample_time; // time of last history[0] - so we know when to rotate the buffer network_time_seconds m_start_time; // when we were created bool m_any_packet_yet; // did we yet got any packet to count diff --git a/contrib/epee/include/stats.h b/contrib/epee/include/stats.h new file mode 100644 index 000000000..1cf9c68fb --- /dev/null +++ b/contrib/epee/include/stats.h @@ -0,0 +1,58 @@ +#pragma once + +#include <vector> + +template<typename T, typename Tpod = T> +class Stats +{ +public: + Stats(const std::vector<T> &v): values(v), cached(0) {} + ~Stats() {} + + size_t get_size() const; + Tpod get_min() const; + Tpod get_max() const; + Tpod get_median() const; + double get_mean() const; + double get_confidence_interval_95() const; + double get_confidence_interval_99() const; + double get_standard_deviation() const; + double get_standard_error() const; + double get_variance() const; + double get_kurtosis() const; + double get_non_parametric_skew() const; + double get_t_test(T t) const; + double get_t_test(size_t npoints, double mean, double stddev) const; + double get_t_test(const Stats<T> &other) const; + double get_z_test(const Stats<T> &other) const; + double get_test(const Stats<T> &other) const; + std::vector<Tpod> get_quantiles(unsigned int quantiles) const; + std::vector<size_t> get_bins(unsigned int bins) const; + bool is_same_distribution_95(size_t npoints, double mean, double stddev) const; + bool is_same_distribution_95(const Stats<T> &other) const; + bool is_same_distribution_99(size_t npoints, double mean, double stddev) const; + bool is_same_distribution_99(const Stats<T> &other) const; + + double get_cdf95(size_t df) const; + double get_cdf95(const Stats<T> &other) const; + double get_cdf99(size_t df) const; + double get_cdf99(const Stats<T> &other) const; + +private: + inline bool is_cached(int bit) const; + inline void set_cached(int bit) const; + + const std::vector<T> &values; + + mutable uint64_t cached; + mutable Tpod min; + mutable Tpod max; + mutable Tpod median; + mutable double mean; + mutable double standard_deviation; + mutable double standard_error; + mutable double variance; + mutable double kurtosis; +}; + +#include "stats.inl" diff --git a/contrib/epee/include/stats.inl b/contrib/epee/include/stats.inl new file mode 100644 index 000000000..5a5cd0b93 --- /dev/null +++ b/contrib/epee/include/stats.inl @@ -0,0 +1,359 @@ +#include <math.h> +#include <limits> +#include <algorithm> +#include "stats.h" + +enum +{ + bit_min = 0, + bit_max, + bit_median, + bit_mean, + bit_standard_deviation, + bit_standard_error, + bit_variance, + bit_kurtosis, +}; + +static inline double square(double x) +{ + return x * x; +} + +template<typename T> +static inline double interpolate(T v, T v0, double i0, T v1, double i1) +{ + return i0 + (i1 - i0) * (v - v0) / (v1 - v0); +} + +template<typename T, typename Tpod> +inline bool Stats<T, Tpod>::is_cached(int bit) const +{ + return cached & (1<<bit); +} + +template<typename T, typename Tpod> +inline void Stats<T, Tpod>::set_cached(int bit) const +{ + cached |= 1<<bit; +} + +template<typename T, typename Tpod> +size_t Stats<T, Tpod>::get_size() const +{ + return values.size(); +} + +template<typename T, typename Tpod> +Tpod Stats<T, Tpod>::get_min() const +{ + if (!is_cached(bit_min)) + { + min = std::numeric_limits<Tpod>::max(); + for (const T &v: values) + min = std::min<Tpod>(min, v); + set_cached(bit_min); + } + return min; +} + +template<typename T, typename Tpod> +Tpod Stats<T, Tpod>::get_max() const +{ + if (!is_cached(bit_max)) + { + max = std::numeric_limits<Tpod>::min(); + for (const T &v: values) + max = std::max<Tpod>(max, v); + set_cached(bit_max); + } + return max; +} + +template<typename T, typename Tpod> +Tpod Stats<T, Tpod>::get_median() const +{ + if (!is_cached(bit_median)) + { + std::vector<Tpod> sorted; + sorted.reserve(values.size()); + for (const T &v: values) + sorted.push_back(v); + std::sort(sorted.begin(), sorted.end()); + if (sorted.size() & 1) + { + median = sorted[sorted.size() / 2]; + } + else + { + median = (sorted[(sorted.size() - 1) / 2] + sorted[sorted.size() / 2]) / 2; + } + set_cached(bit_median); + } + return median; +} + +template<typename T, typename Tpod> +double Stats<T, Tpod>::get_mean() const +{ + if (values.empty()) + return 0.0; + if (!is_cached(bit_mean)) + { + mean = 0.0; + for (const T &v: values) + mean += v; + mean /= values.size(); + set_cached(bit_mean); + } + return mean; +} + +template<typename T, typename Tpod> +double Stats<T, Tpod>::get_cdf95(size_t df) const +{ + static const double p[101] = { + -1, 12.706, 4.3027, 3.1824, 2.7765, 2.5706, 2.4469, 2.3646, 2.3060, 2.2622, 2.2281, 2.2010, 2.1788, 2.1604, 2.1448, 2.1315, + 2.1199, 2.1098, 2.1009, 2.0930, 2.0860, 2.0796, 2.0739, 2.0687, 2.0639, 2.0595, 2.0555, 2.0518, 2.0484, 2.0452, 2.0423, 2.0395, + 2.0369, 2.0345, 2.0322, 2.0301, 2.0281, 2.0262, 2.0244, 2.0227, 2.0211, 2.0195, 2.0181, 2.0167, 2.0154, 2.0141, 2.0129, 2.0117, + 2.0106, 2.0096, 2.0086, 2.0076, 2.0066, 2.0057, 2.0049, 2.0040, 2.0032, 2.0025, 2.0017, 2.0010, 2.0003, 1.9996, 1.9990, 1.9983, + 1.9977, 1.9971, 1.9966, 1.9960, 1.9955, 1.9949, 1.9944, 1.9939, 1.9935, 1.9930, 1.9925, 1.9921, 1.9917, 1.9913, 1.9908, 1.9905, + 1.9901, 1.9897, 1.9893, 1.9890, 1.9886, 1.9883, 1.9879, 1.9876, 1.9873, 1.9870, 1.9867, 1.9864, 1.9861, 1.9858, 1.9855, 1.9852, + 1.9850, 1.9847, 1.9845, 1.9842, 1.9840, + }; + if (df <= 100) + return p[df]; + if (df <= 120) + return interpolate<size_t>(df, 100, 1.9840, 120, 1.98); + return 1.96; +} + +template<typename T, typename Tpod> +double Stats<T, Tpod>::get_cdf95(const Stats<T> &other) const +{ + return get_cdf95(get_size() + other.get_size() - 2); +} + +template<typename T, typename Tpod> +double Stats<T, Tpod>::get_cdf99(size_t df) const +{ + static const double p[101] = { + -1, 9.9250, 5.8408, 4.6041, 4.0321, 3.7074, 3.4995, 3.3554, 3.2498, 3.1693, 3.1058, 3.0545, 3.0123, 2.9768, 2.9467, 2.9208, 2.8982, + 2.8784, 2.8609, 2.8453, 2.8314, 2.8188, 2.8073, 2.7970, 2.7874, 2.7787, 2.7707, 2.7633, 2.7564, 2.7500, 2.7440, 2.7385, 2.7333, + 2.7284, 2.7238, 2.7195, 2.7154, 2.7116, 2.7079, 2.7045, 2.7012, 2.6981, 2.6951, 2.6923, 2.6896, 2.6870, 2.6846, 2.6822, 2.6800, + 2.6778, 2.6757, 2.6737, 2.6718, 2.6700, 2.6682, 2.6665, 2.6649, 2.6633, 2.6618, 2.6603, 2.6589, 2.6575, 2.6561, 2.6549, 2.6536, + 2.6524, 2.6512, 2.6501, 2.6490, 2.6479, 2.6469, 2.6458, 2.6449, 2.6439, 2.6430, 2.6421, 2.6412, 2.6403, 2.6395, 2.6387, 2.6379, + 2.6371, 2.6364, 2.6356, 2.6349, 2.6342, 2.6335, 2.6329, 2.6322, 2.6316, 2.6309, 2.6303, 2.6297, 2.6291, 2.6286, 2.6280, 2.6275, + 2.6269, 2.6264, 2.6259, + }; + if (df <= 100) + return p[df]; + if (df <= 120) + return interpolate<size_t>(df, 100, 2.6529, 120, 2.617); + return 2.576; +} + +template<typename T, typename Tpod> +double Stats<T, Tpod>::get_cdf99(const Stats<T> &other) const +{ + return get_cdf99(get_size() + other.get_size() - 2); +} + +template<typename T, typename Tpod> +double Stats<T, Tpod>::get_confidence_interval_95() const +{ + const size_t df = get_size() - 1; + return get_standard_error() * get_cdf95(df); +} + +template<typename T, typename Tpod> +double Stats<T, Tpod>::get_confidence_interval_99() const +{ + const size_t df = get_size() - 1; + return get_standard_error() * get_cdf99(df); +} + +template<typename T, typename Tpod> +bool Stats<T, Tpod>::is_same_distribution_95(size_t npoints, double mean, double stddev) const +{ + return fabs(get_t_test(npoints, mean, stddev)) < get_cdf95(get_size() + npoints - 2); +} + +template<typename T, typename Tpod> +bool Stats<T, Tpod>::is_same_distribution_95(const Stats<T> &other) const +{ + return fabs(get_t_test(other)) < get_cdf95(other); +} + +template<typename T, typename Tpod> +bool Stats<T, Tpod>::is_same_distribution_99(size_t npoints, double mean, double stddev) const +{ + return fabs(get_t_test(npoints, mean, stddev)) < get_cdf99(get_size() + npoints - 2); +} + +template<typename T, typename Tpod> +bool Stats<T, Tpod>::is_same_distribution_99(const Stats<T> &other) const +{ + return fabs(get_t_test(other)) < get_cdf99(other); +} + +template<typename T, typename Tpod> +double Stats<T, Tpod>::get_standard_deviation() const +{ + if (values.size() <= 1) + return 0.0; + if (!is_cached(bit_standard_deviation)) + { + Tpod m = get_mean(), t = 0; + for (const T &v: values) + t += ((T)v - m) * ((T)v - m); + standard_deviation = sqrt(t / ((double)values.size() - 1)); + set_cached(bit_standard_deviation); + } + return standard_deviation; +} + +template<typename T, typename Tpod> +double Stats<T, Tpod>::get_standard_error() const +{ + if (!is_cached(bit_standard_error)) + { + standard_error = get_standard_deviation() / sqrt(get_size()); + set_cached(bit_standard_error); + } + return standard_error; +} + +template<typename T, typename Tpod> +double Stats<T, Tpod>::get_variance() const +{ + if (!is_cached(bit_variance)) + { + double stddev = get_standard_deviation(); + variance = stddev * stddev; + set_cached(bit_variance); + } + return variance; +} + +template<typename T, typename Tpod> +double Stats<T, Tpod>::get_kurtosis() const +{ + if (values.empty()) + return 0.0; + if (!is_cached(bit_kurtosis)) + { + double m = get_mean(); + double n = 0, d = 0; + for (const T &v: values) + { + T p2 = (v - m) * (v - m); + T p4 = p2 * p2; + n += p4; + d += p2; + } + n /= values.size(); + d /= values.size(); + d *= d; + kurtosis = n / d; + set_cached(bit_kurtosis); + } + return kurtosis; +} + +template<typename T, typename Tpod> +double Stats<T, Tpod>::get_non_parametric_skew() const +{ + return (get_mean() - get_median()) / get_standard_deviation(); +} + +template<typename T, typename Tpod> +double Stats<T, Tpod>::get_t_test(T t) const +{ + const double n = get_mean() - t; + const double d = get_standard_deviation() / sqrt(get_size()); + return n / d; +} + +template<typename T, typename Tpod> +double Stats<T, Tpod>::get_t_test(size_t npoints, double mean, double stddev) const +{ + const double n = get_mean() - mean; + const double d = sqrt(get_variance() / get_size() + square(stddev) / npoints); + return n / d; +} + +template<typename T, typename Tpod> +double Stats<T, Tpod>::get_t_test(const Stats<T> &other) const +{ + const double n = get_mean() - other.get_mean(); + const double d = sqrt(get_variance() / get_size() + other.get_variance() / other.get_size()); + return n / d; +} + +template<typename T, typename Tpod> +double Stats<T, Tpod>::get_z_test(const Stats<T> &other) const +{ + const double m0 = get_mean(); + const double m1 = other.get_mean(); + const double sd0 = get_standard_deviation(); + const double sd1 = other.get_standard_deviation(); + const size_t s0 = get_size(); + const size_t s1 = other.get_size(); + + const double n = m0 - m1; + const double d = sqrt(square(sd0 / sqrt(s0)) + square(sd1 / sqrt(s1))); + + return n / d; +} + +template<typename T, typename Tpod> +double Stats<T, Tpod>::get_test(const Stats<T> &other) const +{ + if (get_size() >= 30 && other.get_size() >= 30) + return get_z_test(other); + else + return get_t_test(other); +} + +template<typename T, typename Tpod> +std::vector<Tpod> Stats<T, Tpod>::get_quantiles(unsigned int quantiles) const +{ + std::vector<Tpod> sorted; + sorted.reserve(values.size()); + for (const T &v: values) + sorted.push_back(v); + std::sort(sorted.begin(), sorted.end()); + std::vector<Tpod> q(quantiles + 1, 0); + for (unsigned int i = 1; i <= quantiles; ++i) + { + unsigned idx = (unsigned)ceil(values.size() * i / (double)quantiles); + q[i] = sorted[idx - 1]; + } + if (!is_cached(bit_min)) + { + min = sorted.front(); + set_cached(bit_min); + } + q[0] = min; + if (!is_cached(bit_max)) + { + max = sorted.back(); + set_cached(bit_max); + } + return q; +} + +template<typename T, typename Tpod> +std::vector<size_t> Stats<T, Tpod>::get_bins(unsigned int bins) const +{ + std::vector<size_t> b(bins, 0); + const double scale = 1.0 / (get_max() - get_min()); + const T base = get_min(); + for (const T &v: values) + { + unsigned int idx = (v - base) * scale; + ++b[idx]; + } + return b; +} diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt index cea50c9dd..e913211ea 100644 --- a/contrib/epee/src/CMakeLists.txt +++ b/contrib/epee/src/CMakeLists.txt @@ -27,7 +27,7 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp net_utils_base.cpp string_tools.cpp wipeable_string.cpp memwipe.c - connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp mlocker.cpp buffer.cpp) + connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp mlocker.cpp buffer.cpp net_ssl.cpp) if (USE_READLINE AND GNU_READLINE_FOUND) add_library(epee_readline STATIC readline_buffer.cpp) endif() diff --git a/contrib/epee/src/connection_basic.cpp b/contrib/epee/src/connection_basic.cpp index f5f9b59fe..377fb3452 100644 --- a/contrib/epee/src/connection_basic.cpp +++ b/contrib/epee/src/connection_basic.cpp @@ -48,7 +48,7 @@ #include "net/network_throttle-detail.hpp" #undef MONERO_DEFAULT_LOG_CATEGORY -#define MONERO_DEFAULT_LOG_CATEGORY "net.p2p" +#define MONERO_DEFAULT_LOG_CATEGORY "net.conn" // ################################################################################################ // local (TU local) headers @@ -113,14 +113,41 @@ connection_basic_pimpl::connection_basic_pimpl(const std::string &name) : m_thro int connection_basic_pimpl::m_default_tos; // methods: -connection_basic::connection_basic(boost::asio::ip::tcp::socket&& socket, boost::shared_ptr<socket_stats> stats) +connection_basic::connection_basic(boost::asio::ip::tcp::socket&& sock, boost::shared_ptr<socket_stats> stats, ssl_support_t ssl_support, ssl_context_t &ssl_context) : m_stats(std::move(stats)), mI( new connection_basic_pimpl("peer") ), - strand_(socket.get_io_service()), - socket_(std::move(socket)), + strand_(sock.get_io_service()), + socket_(sock.get_io_service(), ssl_context.context), + m_want_close_connection(false), + m_was_shutdown(false), + m_ssl_support(ssl_support), + m_ssl_context(ssl_context) +{ + // add nullptr checks if removed + CHECK_AND_ASSERT_THROW_MES(bool(m_stats), "stats shared_ptr cannot be null"); + + socket_.next_layer() = std::move(sock); + + ++(m_stats->sock_count); // increase the global counter + mI->m_peer_number = m_stats->sock_number.fetch_add(1); // use, and increase the generated number + + std::string remote_addr_str = "?"; + try { boost::system::error_code e; remote_addr_str = socket().remote_endpoint(e).address().to_string(); } catch(...){} ; + + _note("Spawned connection #"<<mI->m_peer_number<<" to " << remote_addr_str << " currently we have sockets count:" << m_stats->sock_count); +} + +connection_basic::connection_basic(boost::asio::io_service &io_service, boost::shared_ptr<socket_stats> stats, ssl_support_t ssl_support, ssl_context_t &ssl_context) + : + m_stats(std::move(stats)), + mI( new connection_basic_pimpl("peer") ), + strand_(io_service), + socket_(io_service, ssl_context.context), m_want_close_connection(false), - m_was_shutdown(false) + m_was_shutdown(false), + m_ssl_support(ssl_support), + m_ssl_context(ssl_context) { // add nullptr checks if removed CHECK_AND_ASSERT_THROW_MES(bool(m_stats), "stats shared_ptr cannot be null"); @@ -129,17 +156,18 @@ connection_basic::connection_basic(boost::asio::ip::tcp::socket&& socket, boost: mI->m_peer_number = m_stats->sock_number.fetch_add(1); // use, and increase the generated number std::string remote_addr_str = "?"; - try { boost::system::error_code e; remote_addr_str = socket_.remote_endpoint(e).address().to_string(); } catch(...){} ; + try { boost::system::error_code e; remote_addr_str = socket().remote_endpoint(e).address().to_string(); } catch(...){} ; - _note("Spawned connection p2p#"<<mI->m_peer_number<<" to " << remote_addr_str << " currently we have sockets count:" << m_stats->sock_count); + _note("Spawned connection #"<<mI->m_peer_number<<" to " << remote_addr_str << " currently we have sockets count:" << m_stats->sock_count); } connection_basic::~connection_basic() noexcept(false) { --(m_stats->sock_count); std::string remote_addr_str = "?"; - try { boost::system::error_code e; remote_addr_str = socket_.remote_endpoint(e).address().to_string(); } catch(...){} ; - _note("Destructing connection p2p#"<<mI->m_peer_number << " to " << remote_addr_str); + try { boost::system::error_code e; remote_addr_str = socket().remote_endpoint(e).address().to_string(); } catch(...){} ; + _note("Destructing connection #"<<mI->m_peer_number << " to " << remote_addr_str); +try { throw 0; } catch(...){} } void connection_basic::set_rate_up_limit(uint64_t limit) { diff --git a/contrib/epee/src/net_ssl.cpp b/contrib/epee/src/net_ssl.cpp new file mode 100644 index 000000000..941799078 --- /dev/null +++ b/contrib/epee/src/net_ssl.cpp @@ -0,0 +1,319 @@ +// Copyright (c) 2018, 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.h> +#include <boost/asio/ssl.hpp> +#include <openssl/ssl.h> +#include <openssl/pem.h> +#include "misc_log_ex.h" +#include "net/net_ssl.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "net.ssl" + +// openssl genrsa -out /tmp/KEY 4096 +// openssl req -new -key /tmp/KEY -out /tmp/REQ +// openssl x509 -req -days 999999 -sha256 -in /tmp/REQ -signkey /tmp/KEY -out /tmp/CERT + +namespace +{ + struct openssl_bio_free + { + void operator()(BIO* ptr) const noexcept + { + if (ptr) + BIO_free(ptr); + } + }; + using openssl_bio = std::unique_ptr<BIO, openssl_bio_free>; + + struct openssl_pkey_free + { + void operator()(EVP_PKEY* ptr) const noexcept + { + if (ptr) + EVP_PKEY_free(ptr); + } + }; + using openssl_pkey = std::unique_ptr<EVP_PKEY, openssl_pkey_free>; + +} + +namespace epee +{ +namespace net_utils +{ + +// https://stackoverflow.com/questions/256405/programmatically-create-x509-certificate-using-openssl +bool create_ssl_certificate(EVP_PKEY *&pkey, X509 *&cert) +{ + MGINFO("Generating SSL certificate"); + pkey = EVP_PKEY_new(); + openssl_pkey pkey_deleter{pkey}; + if (!pkey) + { + MERROR("Failed to create new private key"); + return false; + } + RSA *rsa = RSA_generate_key(4096, RSA_F4, NULL, NULL); + if (!rsa) + { + MERROR("Error generating RSA private key"); + return false; + } + if (EVP_PKEY_assign_RSA(pkey, rsa) <= 0) + { + RSA_free(rsa); + MERROR("Error assigning RSA private key"); + return false; + } + + cert = X509_new(); + if (!cert) + { + MERROR("Failed to create new X509 certificate"); + return false; + } + ASN1_INTEGER_set(X509_get_serialNumber(cert), 1); + X509_gmtime_adj(X509_get_notBefore(cert), 0); + X509_gmtime_adj(X509_get_notAfter(cert), 3600 * 24 * 182); // half a year + if (!X509_set_pubkey(cert, pkey)) + { + MERROR("Error setting pubkey on certificate"); + X509_free(cert); + return false; + } + X509_NAME *name = X509_get_subject_name(cert); + X509_set_issuer_name(cert, name); + + if (X509_sign(cert, pkey, EVP_sha256()) == 0) + { + MERROR("Error signing certificate"); + X509_free(cert); + return false; + } + return true; +} + +bool create_ssl_certificate(std::string &pkey_buffer, std::string &cert_buffer) +{ + EVP_PKEY *pkey; + X509 *cert; + if (!create_ssl_certificate(pkey, cert)) + return false; + BIO *bio_pkey = BIO_new(BIO_s_mem()), *bio_cert = BIO_new(BIO_s_mem()); + openssl_bio bio_pkey_deleter{bio_pkey}; + bool success = PEM_write_bio_PrivateKey(bio_pkey, pkey, NULL, NULL, 0, NULL, NULL) && PEM_write_bio_X509(bio_cert, cert); + X509_free(cert); + if (!success) + { + MERROR("Failed to write cert and/or pkey: " << ERR_get_error()); + return false; + } + BUF_MEM *buf = NULL; + BIO_get_mem_ptr(bio_pkey, &buf); + if (!buf || !buf->data || !buf->length) + { + MERROR("Failed to write pkey: " << ERR_get_error()); + return false; + } + pkey_buffer = std::string(buf->data, buf->length); + buf = NULL; + BIO_get_mem_ptr(bio_cert, &buf); + if (!buf || !buf->data || !buf->length) + { + MERROR("Failed to write cert: " << ERR_get_error()); + return false; + } + cert_buffer = std::string(buf->data, buf->length); + return success; +} + +ssl_context_t create_ssl_context(const std::pair<std::string, std::string> &private_key_and_certificate_path, std::list<std::string> allowed_certificates, bool allow_any_cert) +{ + ssl_context_t ssl_context({boost::asio::ssl::context(boost::asio::ssl::context::sslv23), std::move(allowed_certificates)}); + + // disable sslv2 + ssl_context.context.set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2); + ssl_context.context.set_default_verify_paths(); + + // set options on the SSL context for added security + SSL_CTX *ctx = ssl_context.context.native_handle(); + CHECK_AND_ASSERT_THROW_MES(ctx, "Failed to get SSL context"); + SSL_CTX_clear_options(ctx, SSL_OP_LEGACY_SERVER_CONNECT); // SSL_CTX_SET_OPTIONS(3) + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); // https://stackoverflow.com/questions/22378442 +#ifdef SSL_OP_NO_TICKET + SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET); // https://stackoverflow.com/questions/22378442 +#endif +#ifdef SSL_OP_NO_RENEGOTIATION + SSL_CTX_set_options(ctx, SSL_OP_NO_RENEGOTIATION); +#endif +#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION + SSL_CTX_set_options(ctx, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); +#endif +#ifdef SSL_OP_NO_COMPRESSION + SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION); +#endif + SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); // https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices + + CHECK_AND_ASSERT_THROW_MES(private_key_and_certificate_path.first.empty() == private_key_and_certificate_path.second.empty(), "private key and certificate must be either both given or both empty"); + if (private_key_and_certificate_path.second.empty()) + { + std::string pkey, cert; + CHECK_AND_ASSERT_THROW_MES(create_ssl_certificate(pkey, cert), "Failed to create certificate"); + ssl_context.context.use_private_key(boost::asio::buffer(pkey), boost::asio::ssl::context::pem); + ssl_context.context.use_certificate(boost::asio::buffer(cert), boost::asio::ssl::context::pem); + } + else + { + ssl_context.context.use_private_key_file(private_key_and_certificate_path.first, boost::asio::ssl::context::pem); + ssl_context.context.use_certificate_file(private_key_and_certificate_path.second, boost::asio::ssl::context::pem); + } + ssl_context.allow_any_cert = allow_any_cert; + + return ssl_context; +} + +void use_ssl_certificate(ssl_context_t &ssl_context, const std::pair<std::string, std::string> &private_key_and_certificate_path) +{ + ssl_context.context.use_private_key_file(private_key_and_certificate_path.first, boost::asio::ssl::context::pem); + ssl_context.context.use_certificate_file(private_key_and_certificate_path.second, boost::asio::ssl::context::pem); +} + +bool is_ssl(const unsigned char *data, size_t len) +{ + if (len < get_ssl_magic_size()) + return false; + + // https://security.stackexchange.com/questions/34780/checking-client-hello-for-https-classification + MDEBUG("SSL detection buffer, " << len << " bytes: " + << (unsigned)(unsigned char)data[0] << " " << (unsigned)(unsigned char)data[1] << " " + << (unsigned)(unsigned char)data[2] << " " << (unsigned)(unsigned char)data[3] << " " + << (unsigned)(unsigned char)data[4] << " " << (unsigned)(unsigned char)data[5] << " " + << (unsigned)(unsigned char)data[6] << " " << (unsigned)(unsigned char)data[7] << " " + << (unsigned)(unsigned char)data[8]); + if (data[0] == 0x16) // record + if (data[1] == 3) // major version + if (data[5] == 1) // ClientHello + if (data[6] == 0 && data[3]*256 + data[4] == data[7]*256 + data[8] + 4) // length check + return true; + return false; +} + +bool is_certificate_allowed(boost::asio::ssl::verify_context &ctx, const std::list<std::string> &allowed_certificates) +{ + X509_STORE_CTX *sctx = ctx.native_handle(); + if (!sctx) + { + MERROR("Error getting verify_context handle"); + return false; + } + X509 *cert =X509_STORE_CTX_get_current_cert(sctx); + if (!cert) + { + MERROR("No certificate found in verify_context"); + return false; + } + + BIO *bio_cert = BIO_new(BIO_s_mem()); + openssl_bio bio_cert_deleter{bio_cert}; + bool success = PEM_write_bio_X509(bio_cert, cert); + if (!success) + { + MERROR("Failed to print certificate"); + return false; + } + BUF_MEM *buf = NULL; + BIO_get_mem_ptr(bio_cert, &buf); + if (!buf || !buf->data || !buf->length) + { + MERROR("Failed to write certificate: " << ERR_get_error()); + return false; + } + std::string certificate(std::string(buf->data, buf->length)); + return std::find(allowed_certificates.begin(), allowed_certificates.end(), certificate) != allowed_certificates.end(); +} + +bool ssl_handshake(boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &socket, boost::asio::ssl::stream_base::handshake_type type, const epee::net_utils::ssl_context_t &ssl_context) +{ + bool verified = false; + socket.next_layer().set_option(boost::asio::ip::tcp::no_delay(true)); + socket.set_verify_mode(boost::asio::ssl::verify_peer); + socket.set_verify_callback([&](bool preverified, boost::asio::ssl::verify_context &ctx) + { + if (!preverified) + { + const int err = X509_STORE_CTX_get_error(ctx.native_handle()); + const int depth = X509_STORE_CTX_get_error_depth(ctx.native_handle()); + if (err != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || depth != 0) + { + MERROR("Invalid SSL certificate, error " << err << " at depth " << depth << ", connection dropped"); + return false; + } + } + if (!ssl_context.allow_any_cert && !ssl_context.allowed_certificates.empty() && !is_certificate_allowed(ctx, ssl_context.allowed_certificates)) + { + MERROR("Certificate is not in the allowed list, connection droppped"); + return false; + } + verified = true; + return true; + }); + + boost::system::error_code ec; + socket.handshake(type, ec); + if (ec) + { + MERROR("handshake failed, connection dropped"); + return false; + } + if (!ssl_context.allow_any_cert && !verified) + { + MERROR("Peer did not provide a certificate in the allowed list, connection dropped"); + return false; + } + MDEBUG("SSL handshake success"); + return true; +} + +bool ssl_support_from_string(ssl_support_t &ssl, boost::string_ref s) +{ + if (s == "enabled") + ssl = epee::net_utils::ssl_support_t::e_ssl_support_enabled; + else if (s == "disabled") + ssl = epee::net_utils::ssl_support_t::e_ssl_support_disabled; + else if (s == "autodetect") + ssl = epee::net_utils::ssl_support_t::e_ssl_support_autodetect; + else + return false; + return true; +} + +} // namespace +} // namespace + diff --git a/contrib/epee/src/network_throttle-detail.cpp b/contrib/epee/src/network_throttle-detail.cpp index d2e776df0..0b42402bd 100644 --- a/contrib/epee/src/network_throttle-detail.cpp +++ b/contrib/epee/src/network_throttle-detail.cpp @@ -135,6 +135,7 @@ network_throttle::network_throttle(const std::string &nameshort, const std::stri m_slot_size = 1.0; // hard coded in few places m_target_speed = 16 * 1024; // other defaults are probably defined in the command-line parsing code when this class is used e.g. as main global throttle m_last_sample_time = 0; + m_history.resize(m_window_size); } void network_throttle::set_name(const std::string &name) @@ -168,8 +169,7 @@ void network_throttle::tick() { _dbg3("Moving counter buffer by 1 second " << last_sample_time_slot << " < " << current_sample_time_slot << " (last time " << m_last_sample_time<<")"); // rotate buffer - for (size_t i=m_history.size()-1; i>=1; --i) m_history[i] = m_history[i-1]; - m_history[0] = packet_info(); + m_history.push_front(packet_info()); if (! m_any_packet_yet) { m_last_sample_time = time_now; @@ -191,7 +191,7 @@ void network_throttle::_handle_trafic_exact(size_t packet_size, size_t orginal_s calculate_times_struct cts ; calculate_times(packet_size, cts , false, -1); calculate_times_struct cts2; calculate_times(packet_size, cts2, false, 5); - m_history[0].m_size += packet_size; + m_history.front().m_size += packet_size; std::ostringstream oss; oss << "["; for (auto sample: m_history) oss << sample.m_size << " "; oss << "]" << std::ends; std::string history_str = oss.str(); diff --git a/contrib/gitian/README.md b/contrib/gitian/README.md index 0b13df344..b869bde87 100644 --- a/contrib/gitian/README.md +++ b/contrib/gitian/README.md @@ -1,7 +1,7 @@ Gitian building ================ -*Setup instructions for a Gitian build of Monero using a VM or physical system.* +*Setup instructions for a Gitian build of Monero.* Gitian is the deterministic build process that is used to build the Monero CLI executables. It provides a way to be reasonably sure that the @@ -13,6 +13,10 @@ Multiple developers build the source code by following a specific descriptor These results are compared and only if they match, the build is accepted and provided for download. +Gitian runs compilation steps in an isolated container. It is flexible and gives you full +control over the build environment, while still ensuring reproducibility and consistent output +formats. + More independent Gitian builders are needed, which is why this guide exists. It is preferred you follow these steps yourself instead of using someone else's VM image to avoid 'contaminating' the build. @@ -22,11 +26,17 @@ Preparing the Gitian builder host The first step is to prepare the host environment that will be used to perform the Gitian builds. This guide explains how to set up the environment, and how to start the builds. +Gitian offers to build with either `kvm`, `docker` or `lxc`. The default build +path chosen is `lxc`, but its setup is more complicated. You need to be logged in as the `gitianuser`. +If this user does not exist yet on your system, create it. Gitian can use +either kvm, lxc or docker as a host environment. This documentation will show +how to build with lxc and docker. While the docker setup is easy, the lxc setup +is more involved. + +LXC +--- -Gitian builds are for now executed on Ubuntu 18.04 "Bionic Beaver". A solution is being worked on to run -it in docker in the future. Please run Ubuntu in either a VM, or on your physical machine. -You need to be logged in as the `gitianuser` in order to build gitian builds. If this user does not exist yet on your system, -create it. +LXC builds should be run on Ubuntu 18.04 "Bionic Beaver". Note that a version of `lxc-execute` higher or equal to 2.1.1 is required. You can check the version with `lxc-execute --version`. @@ -63,15 +73,28 @@ reboot This setup is required to enable networking in the container. +Docker +------ + +Building in docker does not require much setup. Install docker on your host, then type the following: + +```bash +sudo apt-get install git make curl +sudo usermod -aG docker gitianuser +``` + Manual and Building ------------------- -The instructions below use the automated script [gitian-build.py](https://github.com/betcoin/bitcoin/blob/master/contrib/gitian-build.py) which only works in Ubuntu. -It calls all available descriptors. Help for the build steps taken can be accessed with `./gitian-build.py --help`. + +The instructions below use the automated script [gitian-build.py](gitian-build.py) which only works in 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`. Initial Gitian Setup -------------------- -The `gitian-build.py` script will checkout different release tags, so it's best to copy it: + +The `gitian-build.py` script will checkout different release tags, so it's best to copy it to the top level directory: ```bash cp monero/contrib/gitian/gitian-build.py . @@ -79,11 +102,16 @@ cp monero/contrib/gitian/gitian-build.py . Setup the required environment, you only need to do this once: -``` +```bash ./gitian-build.py --setup fluffypony v0.14.0 ``` -Where `fluffypony` is your Github name and `v0.14.0` is the version tag you want to build. +Where `fluffypony` is your Github name and `v0.14.0` is the version tag you want to build. +If you are using docker, run it with: + +```bash +./gitian-build.py --setup --docker fluffypony v0.14.0 +``` While gitian and this build script does provide a way for you to sign the build directly, it is recommended to sign in a seperate step. This script is only there for convenience. Seperate steps for building can still be taken. @@ -98,9 +126,11 @@ git remote add fluffypony git@github.com:fluffypony/gitian.sigs.git Build Binaries ----------------------------- -To build the most recent tag: +To build the most recent tag (pass in `--docker` after setting up with docker): - `./gitian-build.py --detach-sign --no-commit -b fluffypony v0.14.0` +```bash +./gitian-build.py --detach-sign --no-commit -b fluffypony v0.14.0 +``` To speed up the build, use `-j 5 -m 5000` as the first arguments, where `5` is the number of CPU's you allocated to the VM plus one, and 5000 is a little bit less than then the MB's of RAM you allocated. If there is memory corruption on your machine, try to tweak these values. @@ -108,7 +138,7 @@ If all went well, this produces a number of (uncommited) `.assert` files in the If you do detached, offline signing, you need to copy these uncommited changes to your host machine, where you can sign them. For example: -``` +```bash export NAME=fluffypony export VERSION=v0.14.0 gpg --output $VERSION-linux/$NAME/monero-linux-$VERSION-build.assert.sig --detach-sign $VERSION-linux/$NAME/monero-linux-$VERSION-build.assert @@ -119,7 +149,7 @@ gpg --output $VERSION-win-unsigned/$NAME/monero-win-$VERSION-build.assert.sig -- Make a pull request (both the `.assert` and `.assert.sig` files) to the [monero-project/gitian.sigs](https://github.com/monero-project/gitian.sigs/) repository: -``` +```bash git checkout -b v0.14.0 git commit -S -a -m "Add $NAME v0.14.0" git push --set-upstream $NAME v0.14.0 @@ -131,3 +161,19 @@ git push --set-upstream $NAME v0.14.0 gpg --detach-sign ${VERSION}-osx-unsigned/${SIGNER}/monero-osx-*-build.assert ``` +More Build Options +------------------ + +You can choose your own remote and commit hash by running for example: +```bash +./gitian-build.py --detach-sign --no-commit --url https://github.com/moneromooo-monero/bitmonero -b moneromooo 1f5680c8db8f4cc7acc04a04c724b832003440fd +``` + +Note that you won't be able to build commits authored before the gitian scripts +were added. Gitian clones the source files from the given url, be sure to push +to the remote you provide before building. +To get all build options run: +```bash +./gitian-build.py --help +``` + diff --git a/contrib/gitian/gitian-build.py b/contrib/gitian/gitian-build.py index df1ba0d6b..cd88ecb20 100755 --- a/contrib/gitian/gitian-build.py +++ b/contrib/gitian/gitian-build.py @@ -116,7 +116,7 @@ def main(): 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. If building a commit or branch, the -c option must be specified') + parser.add_argument('version', help='Version number, commit, or branch to build.') args = parser.parse_args() workdir = os.getcwd() diff --git a/contrib/gitian/gitian-osx.yml b/contrib/gitian/gitian-osx.yml index a6fcff0da..7de302353 100644 --- a/contrib/gitian/gitian-osx.yml +++ b/contrib/gitian/gitian-osx.yml @@ -12,21 +12,17 @@ packages: - "git" - "pkg-config" - "autoconf" -- "librsvg2-bin" -- "libtiff-tools" - "libtool" - "automake" - "faketime" - "bsdmainutils" - "cmake" -- "imagemagick" - "libcap-dev" - "libz-dev" - "libbz2-dev" - "python" - "python-dev" - "python-setuptools" -- "fonts-tuffy" remotes: - "url": "https://github.com/monero-project/monero.git" "dir": "monero" diff --git a/external/db_drivers/liblmdb/mdb.c b/external/db_drivers/liblmdb/mdb.c index 3b2745f95..8e67d5981 100644 --- a/external/db_drivers/liblmdb/mdb.c +++ b/external/db_drivers/liblmdb/mdb.c @@ -9641,7 +9641,7 @@ mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno * the split so the new page is emptier than the old page. * This yields better packing during sequential inserts. */ - if (nkeys < 20 || nsize > pmax/16 || newindx >= nkeys) { + if (nkeys < 32 || nsize > pmax/16 || newindx >= nkeys) { /* Find split point */ psize = 0; if (newindx <= split_indx || newindx >= nkeys) { diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index d3eefef6e..041759593 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -197,6 +197,7 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti uint64_t BlockchainDB::add_block( const block& blk , size_t block_weight + , uint64_t long_term_block_weight , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated , const std::vector<transaction>& txs @@ -241,7 +242,7 @@ uint64_t BlockchainDB::add_block( const block& blk // call out to subclass implementation to add the block & metadata time1 = epee::misc_utils::get_tick_count(); - add_block(blk, block_weight, cumulative_difficulty, coins_generated, num_rct_outs, blk_hash); + add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, num_rct_outs, blk_hash); TIME_MEASURE_FINISH(time1); time_add_block1 += time1; diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 137d5958a..c3f11ba28 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -358,12 +358,14 @@ private: * * @param blk the block to be added * @param block_weight the weight of the block (transactions and all) + * @param long_term_block_weight the long term block weight of the block (transactions and all) * @param cumulative_difficulty the accumulated difficulty after this block * @param coins_generated the number of coins generated total after this block * @param blk_hash the hash of the block */ virtual void add_block( const block& blk , size_t block_weight + , uint64_t long_term_block_weight , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated , uint64_t num_rct_outs @@ -375,7 +377,7 @@ private: * * The subclass implementing this will remove the block data from the top * block in the chain. The data to be removed is that which was added in - * BlockchainDB::add_block(const block& blk, size_t block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const crypto::hash& blk_hash) + * BlockchainDB::add_block(const block& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const crypto::hash& blk_hash) * * If any of this cannot be done, the subclass should throw the corresponding * subclass of DB_EXCEPTION @@ -789,6 +791,7 @@ public: * * @param blk the block to be added * @param block_weight the size of the block (transactions and all) + * @param long_term_block_weight the long term weight of the block (transactions and all) * @param cumulative_difficulty the accumulated difficulty after this block * @param coins_generated the number of coins generated total after this block * @param txs the transactions in the block @@ -797,6 +800,7 @@ public: */ virtual uint64_t add_block( const block& blk , size_t block_weight + , uint64_t long_term_block_weight , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated , const std::vector<transaction>& txs @@ -985,6 +989,17 @@ public: virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const = 0; /** + * @brief fetch a block's long term weight + * + * If the block does not exist, the subclass should throw BLOCK_DNE + * + * @param height the height requested + * + * @return the long term weight + */ + virtual uint64_t get_block_long_term_weight(const uint64_t& height) const = 0; + + /** * @brief fetch a block's hash * * The subclass should return hash of the block with the diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 8a1303be9..9d2f7821e 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -29,6 +29,7 @@ #include <boost/filesystem.hpp> #include <boost/format.hpp> +#include <boost/circular_buffer.hpp> #include <memory> // std::unique_ptr #include <cstring> // memcpy @@ -53,7 +54,7 @@ using epee::string_tools::pod_to_hex; using namespace crypto; // Increase when the DB structure changes -#define VERSION 3 +#define VERSION 4 namespace { @@ -277,7 +278,7 @@ typedef struct mdb_block_info_old crypto::hash bi_hash; } mdb_block_info_old; -typedef struct mdb_block_info +typedef struct mdb_block_info_2 { uint64_t bi_height; uint64_t bi_timestamp; @@ -286,7 +287,21 @@ typedef struct mdb_block_info difficulty_type bi_diff; crypto::hash bi_hash; uint64_t bi_cum_rct; -} mdb_block_info; +} mdb_block_info_2; + +typedef struct mdb_block_info_3 +{ + uint64_t bi_height; + uint64_t bi_timestamp; + uint64_t bi_coins; + uint64_t bi_weight; // a size_t really but we need 32-bit compat + difficulty_type bi_diff; + crypto::hash bi_hash; + uint64_t bi_cum_rct; + uint64_t bi_long_term_block_weight; +} mdb_block_info_3; + +typedef mdb_block_info_3 mdb_block_info; typedef struct blk_height { crypto::hash bh_hash; @@ -504,7 +519,7 @@ void BlockchainLMDB::do_resize(uint64_t increase_size) mdb_env_stat(m_env, &mst); // add 1Gb per resize, instead of doing a percentage increase - uint64_t new_mapsize = (double) mei.me_mapsize + add_size; + uint64_t new_mapsize = (uint64_t) mei.me_mapsize + add_size; // If given, use increase_size instead of above way of resizing. // This is currently used for increasing by an estimated size at start of new @@ -694,7 +709,7 @@ estim: return threshold_size; } -void BlockchainLMDB::add_block(const block& blk, size_t block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, +void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, uint64_t num_rct_outs, const crypto::hash& blk_hash) { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -754,6 +769,7 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, const diff const mdb_block_info *bi_prev = (const mdb_block_info*)h.mv_data; bi.bi_cum_rct += bi_prev->bi_cum_rct; } + bi.bi_long_term_block_weight = long_term_block_weight; MDB_val_set(val, bi); result = mdb_cursor_put(m_cur_block_info, (MDB_val *)&zerokval, &val, MDB_APPENDDUP); @@ -1313,14 +1329,14 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) MDB_envinfo mei; mdb_env_info(m_env, &mei); - uint64_t cur_mapsize = (double)mei.me_mapsize; + uint64_t cur_mapsize = (uint64_t)mei.me_mapsize; if (cur_mapsize < mapsize) { if (auto result = mdb_env_set_mapsize(m_env, mapsize)) throw0(DB_ERROR(lmdb_error("Failed to set max memory map size: ", result).c_str())); mdb_env_info(m_env, &mei); - cur_mapsize = (double)mei.me_mapsize; + cur_mapsize = (uint64_t)mei.me_mapsize; LOG_PRINT_L1("LMDB memory map size: " << cur_mapsize); } @@ -2486,6 +2502,29 @@ uint64_t BlockchainLMDB::get_block_already_generated_coins(const uint64_t& heigh return ret; } +uint64_t BlockchainLMDB::get_block_long_term_weight(const uint64_t& height) const +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + + TXN_PREFIX_RDONLY(); + RCURSOR(block_info); + + MDB_val_set(result, height); + auto get_result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &result, MDB_GET_BOTH); + if (get_result == MDB_NOTFOUND) + { + throw0(BLOCK_DNE(std::string("Attempt to get block long term weight from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- block info not in db").c_str())); + } + else if (get_result) + throw0(DB_ERROR("Error attempting to retrieve a long term block weight from the db")); + + mdb_block_info *bi = (mdb_block_info *)result.mv_data; + uint64_t ret = bi->bi_long_term_block_weight; + TXN_POSTFIX_RDONLY(); + return ret; +} + crypto::hash BlockchainLMDB::get_block_hash_from_height(const uint64_t& height) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -3524,7 +3563,7 @@ void BlockchainLMDB::block_txn_abort() } } -uint64_t BlockchainLMDB::add_block(const block& blk, size_t block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, +uint64_t BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const std::vector<transaction>& txs) { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -3543,7 +3582,7 @@ uint64_t BlockchainLMDB::add_block(const block& blk, size_t block_weight, const try { - BlockchainDB::add_block(blk, block_weight, cumulative_difficulty, coins_generated, txs); + BlockchainDB::add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, txs); } catch (const DB_ERROR_TXN_START& e) { @@ -4748,6 +4787,7 @@ void BlockchainLMDB::migrate_2_3() throw0(DB_ERROR(lmdb_error("Failed to delete old block_info table: ", result).c_str())); RENAME_DB("block_infn"); + mdb_dbi_close(m_env, m_block_info); lmdb_db_open(txn, "block_info", MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_block_info, "Failed to open db handle for block_infn"); mdb_set_dupsort(txn, m_block_info, compare_uint64); @@ -4768,6 +4808,166 @@ void BlockchainLMDB::migrate_2_3() txn.commit(); } +void BlockchainLMDB::migrate_3_4() +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + uint64_t i; + int result; + mdb_txn_safe txn(false); + MDB_val k, v; + char *ptr; + bool past_long_term_weight = false; + + MGINFO_YELLOW("Migrating blockchain from DB version 3 to 4 - this may take a while:"); + + do { + LOG_PRINT_L1("migrating block info:"); + + 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())); + + MDB_stat db_stats; + if ((result = mdb_stat(txn, m_blocks, &db_stats))) + throw0(DB_ERROR(lmdb_error("Failed to query m_blocks: ", result).c_str())); + const uint64_t blockchain_height = db_stats.ms_entries; + + boost::circular_buffer<uint64_t> long_term_block_weights(CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE); + + /* the block_info table name is the same but the old version and new version + * have incompatible data. Create a new table. We want the name to be similar + * to the old name so that it will occupy the same location in the DB. + */ + MDB_dbi o_block_info = m_block_info; + lmdb_db_open(txn, "block_infn", MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_block_info, "Failed to open db handle for block_infn"); + mdb_set_dupsort(txn, m_block_info, compare_uint64); + + + MDB_cursor *c_blocks; + result = mdb_cursor_open(txn, m_blocks, &c_blocks); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to open a cursor for blocks: ", result).c_str())); + + MDB_cursor *c_old, *c_cur; + i = 0; + while(1) { + if (!(i % 1000)) { + if (i) { + LOGIF(el::Level::Info) { + std::cout << i << " / " << blockchain_height << " \r" << std::flush; + } + 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_block_info, &c_cur); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to open a cursor for block_infn: ", result).c_str())); + result = mdb_cursor_open(txn, o_block_info, &c_old); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to open a cursor for block_info: ", result).c_str())); + result = mdb_cursor_open(txn, m_blocks, &c_blocks); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to open a cursor for blocks: ", result).c_str())); + if (!i) { + MDB_stat db_stat; + result = mdb_stat(txn, m_block_info, &db_stats); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to query m_block_info: ", result).c_str())); + i = db_stats.ms_entries; + } + } + result = mdb_cursor_get(c_old, &k, &v, MDB_NEXT); + if (result == MDB_NOTFOUND) { + txn.commit(); + break; + } + else if (result) + throw0(DB_ERROR(lmdb_error("Failed to get a record from block_info: ", result).c_str())); + const mdb_block_info_2 *bi_old = (const mdb_block_info_2*)v.mv_data; + mdb_block_info_3 bi; + bi.bi_height = bi_old->bi_height; + bi.bi_timestamp = bi_old->bi_timestamp; + bi.bi_coins = bi_old->bi_coins; + bi.bi_weight = bi_old->bi_weight; + bi.bi_diff = bi_old->bi_diff; + bi.bi_hash = bi_old->bi_hash; + bi.bi_cum_rct = bi_old->bi_cum_rct; + + // get block major version to determine which rule is in place + if (!past_long_term_weight) + { + MDB_val_copy<uint64_t> kb(bi.bi_height); + MDB_val vb; + result = mdb_cursor_get(c_blocks, &kb, &vb, MDB_SET); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to query m_blocks: ", result).c_str())); + if (vb.mv_size == 0) + throw0(DB_ERROR("Invalid data from m_blocks")); + const uint8_t block_major_version = *((const uint8_t*)vb.mv_data); + if (block_major_version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT) + past_long_term_weight = true; + } + + uint64_t long_term_block_weight; + if (past_long_term_weight) + { + std::vector<uint64_t> weights(long_term_block_weights.begin(), long_term_block_weights.end()); + uint64_t long_term_effective_block_median_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, epee::misc_utils::median(weights)); + long_term_block_weight = std::min<uint64_t>(bi.bi_weight, long_term_effective_block_median_weight + long_term_effective_block_median_weight * 2 / 5); + } + else + { + long_term_block_weight = bi.bi_weight; + } + long_term_block_weights.push_back(long_term_block_weight); + bi.bi_long_term_block_weight = long_term_block_weight; + + MDB_val_set(nv, bi); + result = mdb_cursor_put(c_cur, (MDB_val *)&zerokval, &nv, MDB_APPENDDUP); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to put a record into block_infn: ", result).c_str())); + /* we delete the old records immediately, so the overall DB and mapsize should not grow. + * This is a little slower than just letting mdb_drop() delete it all at the end, but + * it saves a significant amount of disk space. + */ + result = mdb_cursor_del(c_old, 0); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to delete a record from block_info: ", result).c_str())); + i++; + } + + 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())); + /* Delete the old table */ + result = mdb_drop(txn, o_block_info, 1); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to delete old block_info table: ", result).c_str())); + + RENAME_DB("block_infn"); + mdb_dbi_close(m_env, m_block_info); + + lmdb_db_open(txn, "block_info", MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_block_info, "Failed to open db handle for block_infn"); + mdb_set_dupsort(txn, m_block_info, compare_uint64); + + txn.commit(); + } while(0); + + uint32_t version = 4; + v.mv_data = (void *)&version; + v.mv_size = sizeof(version); + MDB_val_str(vk, "version"); + 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_put(txn, m_properties, &vk, &v, 0); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to update version for the db: ", result).c_str())); + txn.commit(); +} + void BlockchainLMDB::migrate(const uint32_t oldversion) { if (oldversion < 1) @@ -4776,6 +4976,8 @@ void BlockchainLMDB::migrate(const uint32_t oldversion) migrate_1_2(); if (oldversion < 3) migrate_2_3(); + if (oldversion < 4) + migrate_3_4(); } } // namespace cryptonote diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index c07ab8da5..5764f9ae4 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -225,6 +225,8 @@ public: virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const; + virtual uint64_t get_block_long_term_weight(const uint64_t& height) const; + virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const; virtual std::vector<block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) const; @@ -292,6 +294,7 @@ public: virtual uint64_t add_block( const block& blk , size_t block_weight + , uint64_t long_term_block_weight , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated , const std::vector<transaction>& txs @@ -341,6 +344,7 @@ private: virtual void add_block( const block& blk , size_t block_weight + , uint64_t long_term_block_weight , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated , uint64_t num_rct_outs @@ -405,6 +409,9 @@ private: // migrate from DB version 2 to 3 void migrate_2_3(); + // migrate from DB version 3 to 4 + void migrate_3_4(); + void cleanup_batch(); private: diff --git a/tests/unit_tests/testdb.h b/src/blockchain_db/testdb.h index 8f5cf70e8..35dfbe673 100644 --- a/tests/unit_tests/testdb.h +++ b/src/blockchain_db/testdb.h @@ -33,9 +33,11 @@ #include <string> #include <vector> #include <map> -#include "gtest/gtest.h" -#include "blockchain_db/blockchain_db.h" +#include "blockchain_db.h" + +namespace cryptonote +{ class BaseTestDB: public cryptonote::BlockchainDB { public: @@ -73,6 +75,7 @@ public: virtual cryptonote::difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const { return 10; } virtual cryptonote::difficulty_type get_block_difficulty(const uint64_t& height) const { return 0; } virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const { return 10000000000; } + virtual uint64_t get_block_long_term_weight(const uint64_t& height) const { return 128; } virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const { return crypto::hash(); } virtual std::vector<cryptonote::block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector<cryptonote::block>(); } virtual std::vector<crypto::hash> get_hashes_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector<crypto::hash>(); } @@ -128,6 +131,7 @@ public: virtual void add_block( const cryptonote::block& blk , size_t block_weight + , uint64_t long_term_block_weight , const cryptonote::difficulty_type& cumulative_difficulty , const uint64_t& coins_generated , uint64_t num_rct_outs @@ -145,3 +149,4 @@ public: virtual void prune_outputs(uint64_t amount) {} }; +} diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index 2a8a6f494..e6ec20c3b 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -485,7 +485,8 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path try { - core.get_blockchain_storage().get_db().add_block(b, block_weight, cumulative_difficulty, coins_generated, txs); + uint64_t long_term_block_weight = core.get_blockchain_storage().get_next_long_term_block_weight(block_weight); + core.get_blockchain_storage().get_db().add_block(b, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, txs); } catch (const std::exception& e) { diff --git a/src/blockchain_utilities/blockchain_prune.cpp b/src/blockchain_utilities/blockchain_prune.cpp index 8e13f2c04..36080aade 100644 --- a/src/blockchain_utilities/blockchain_prune.cpp +++ b/src/blockchain_utilities/blockchain_prune.cpp @@ -611,6 +611,24 @@ int main(int argc, char* argv[]) } already_pruned = true; } + if (n == 0) + { + const uint64_t blockchain_height = core_storage[0]->get_current_blockchain_height(); + const crypto::hash hash = core_storage[0]->get_block_id_by_height(blockchain_height - 1); + cryptonote::block block; + if (core_storage[0]->get_block_by_hash(hash, block)) + { + if (block.major_version < 10) + { + time_t now = time(NULL); + if (now < 1555286400) // 15 april 2019 + { + MERROR("Pruning before v10 will confuse peers. Wait for v10 first"); + return 1; + } + } + } + } } core_storage[0]->deinit(); core_storage[0].reset(NULL); diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 212a1891e..bcf9cbce7 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -45,6 +45,7 @@ set(common_sources threadpool.cpp updates.cpp aligned.c + timings.cc combinator.cpp) if (STACK_TRACE) @@ -84,6 +85,7 @@ set(common_private_headers threadpool.h updates.h aligned.h + timings.h combinator.h) monero_private_headers(common diff --git a/src/common/compat/glibc_compat.cpp b/src/common/compat/glibc_compat.cpp index bf567987d..435e7930f 100644 --- a/src/common/compat/glibc_compat.cpp +++ b/src/common/compat/glibc_compat.cpp @@ -82,7 +82,7 @@ __explicit_bzero_chk (void *dst, size_t len, size_t dstlen) #undef glob extern "C" int glob_old(const char * pattern, int flags, int (*errfunc) (const char *epath, int eerrno), glob_t *pglob); #ifdef __i386__ -__asm__(".symver glob_old,glob@GLIBC_2.1"); +__asm__(".symver glob_old,glob@GLIBC_2.0"); #elif defined(__amd64__) __asm__(".symver glob_old,glob@GLIBC_2.2.5"); #elif defined(__arm__) diff --git a/src/common/download.cpp b/src/common/download.cpp index 58ce0595f..7c38cfa5b 100644 --- a/src/common/download.cpp +++ b/src/common/download.cpp @@ -179,8 +179,8 @@ namespace tools lock.unlock(); - bool ssl = u_c.schema == "https"; - uint16_t port = u_c.port ? u_c.port : ssl ? 443 : 80; + epee::net_utils::ssl_support_t ssl = u_c.schema == "https" ? epee::net_utils::ssl_support_t::e_ssl_support_enabled : epee::net_utils::ssl_support_t::e_ssl_support_disabled; + uint16_t port = u_c.port ? u_c.port : ssl == epee::net_utils::ssl_support_t::e_ssl_support_enabled ? 443 : 80; MDEBUG("Connecting to " << u_c.host << ":" << port); client.set_server(u_c.host, std::to_string(port), boost::none, ssl); if (!client.connect(std::chrono::seconds(30))) diff --git a/src/common/notify.cpp b/src/common/notify.cpp index c3165fb05..e2df5096d 100644 --- a/src/common/notify.cpp +++ b/src/common/notify.cpp @@ -48,7 +48,7 @@ Notify::Notify(const char *spec) { CHECK_AND_ASSERT_THROW_MES(spec, "Null spec"); - boost::split(args, spec, boost::is_any_of(" ")); + boost::split(args, spec, boost::is_any_of(" \t"), boost::token_compress_on); CHECK_AND_ASSERT_THROW_MES(args.size() > 0, "Failed to parse spec"); if (strchr(spec, '\'') || strchr(spec, '\"') || strchr(spec, '\\')) MWARNING("A notification spec contains a quote or backslash: note that these are handled verbatim, which may not be the intent"); diff --git a/src/common/perf_timer.h b/src/common/perf_timer.h index d859cf576..5203da205 100644 --- a/src/common/perf_timer.h +++ b/src/common/perf_timer.h @@ -53,6 +53,7 @@ public: void resume(); void reset(); uint64_t value() const; + operator uint64_t() const { return value(); } protected: uint64_t ticks; diff --git a/src/common/timings.cc b/src/common/timings.cc new file mode 100644 index 000000000..cb8deff2a --- /dev/null +++ b/src/common/timings.cc @@ -0,0 +1,125 @@ +#include <string.h> +#include <error.h> +#include <time.h> +#include <algorithm> +#include <boost/algorithm/string.hpp> +#include "misc_log_ex.h" +#include "timings.h" + +#define N_EXPECTED_FIELDS (8+11) + +TimingsDatabase::TimingsDatabase() +{ +} + +TimingsDatabase::TimingsDatabase(const std::string &filename): + filename(filename) +{ + load(); +} + +TimingsDatabase::~TimingsDatabase() +{ + save(); +} + +bool TimingsDatabase::load() +{ + instances.clear(); + + if (filename.empty()) + return true; + + FILE *f = fopen(filename.c_str(), "r"); + if (!f) + { + MDEBUG("Failed to load timings file " << filename << ": " << strerror(errno)); + return false; + } + while (1) + { + char s[4096]; + if (!fgets(s, sizeof(s), f)) + break; + char *tab = strchr(s, '\t'); + if (!tab) + { + MWARNING("Bad format: no tab found"); + continue; + } + const std::string name = std::string(s, tab - s); + std::vector<std::string> fields; + char *ptr = tab + 1; + boost::split(fields, ptr, boost::is_any_of(" ")); + if (fields.size() != N_EXPECTED_FIELDS) + { + MERROR("Bad format: wrong number of fields: got " << fields.size() << " expected " << N_EXPECTED_FIELDS); + continue; + } + + instance i; + + unsigned int idx = 0; + i.t = atoi(fields[idx++].c_str()); + i.npoints = atoi(fields[idx++].c_str()); + i.min = atof(fields[idx++].c_str()); + i.max = atof(fields[idx++].c_str()); + i.mean = atof(fields[idx++].c_str()); + i.median = atof(fields[idx++].c_str()); + i.stddev = atof(fields[idx++].c_str()); + i.npskew = atof(fields[idx++].c_str()); + i.deciles.reserve(11); + for (int n = 0; n < 11; ++n) + { + i.deciles.push_back(atoi(fields[idx++].c_str())); + } + instances.insert(std::make_pair(name, i)); + } + fclose(f); + return true; +} + +bool TimingsDatabase::save() +{ + if (filename.empty()) + return true; + + FILE *f = fopen(filename.c_str(), "w"); + if (!f) + { + MERROR("Failed to write to file " << filename << ": " << strerror(errno)); + return false; + } + for (const auto &i: instances) + { + fprintf(f, "%s", i.first.c_str()); + fprintf(f, "\t%lu", (unsigned long)i.second.t); + fprintf(f, " %zu", i.second.npoints); + fprintf(f, " %f", i.second.min); + fprintf(f, " %f", i.second.max); + fprintf(f, " %f", i.second.mean); + fprintf(f, " %f", i.second.median); + fprintf(f, " %f", i.second.stddev); + fprintf(f, " %f", i.second.npskew); + for (uint64_t v: i.second.deciles) + fprintf(f, " %lu", (unsigned long)v); + fputc('\n', f); + } + fclose(f); + return true; +} + +std::vector<TimingsDatabase::instance> TimingsDatabase::get(const char *name) const +{ + std::vector<instance> ret; + auto range = instances.equal_range(name); + for (auto i = range.first; i != range.second; ++i) + ret.push_back(i->second); + std::sort(ret.begin(), ret.end(), [](const instance &e0, const instance &e1){ return e0.t < e1.t; }); + return ret; +} + +void TimingsDatabase::add(const char *name, const instance &i) +{ + instances.insert(std::make_pair(name, i)); +} diff --git a/src/common/timings.h b/src/common/timings.h new file mode 100644 index 000000000..fb905611f --- /dev/null +++ b/src/common/timings.h @@ -0,0 +1,34 @@ +#pragma once + +#include <stdint.h> +#include <string> +#include <vector> +#include <map> + +class TimingsDatabase +{ +public: + struct instance + { + time_t t; + size_t npoints; + double min, max, mean, median, stddev, npskew; + std::vector<uint64_t> deciles; + }; + +public: + TimingsDatabase(); + TimingsDatabase(const std::string &filename); + ~TimingsDatabase(); + + std::vector<instance> get(const char *name) const; + void add(const char *name, const instance &data); + +private: + bool load(); + bool save(); + +private: + std::string filename; + std::multimap<std::string, instance> instances; +}; diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt index 0c635e7cb..5ce43be22 100644 --- a/src/crypto/CMakeLists.txt +++ b/src/crypto/CMakeLists.txt @@ -45,6 +45,8 @@ set(crypto_sources random.c skein.c slow-hash.c + CryptonightR_JIT.c + CryptonightR_template.S tree-hash.c) set(crypto_headers) @@ -66,7 +68,9 @@ set(crypto_private_headers oaes_lib.h random.h skein.h - skein_port.h) + skein_port.h + CryptonightR_JIT.h + CryptonightR_template.h) monero_private_headers(cncrypto ${crypto_private_headers}) @@ -101,4 +105,5 @@ if (ANDROID OR IOS) endif() endif() - +# cheat because cmake and ccache hate each other +set_property(SOURCE CryptonightR_template.S PROPERTY LANGUAGE C) diff --git a/src/crypto/CryptonightR_JIT.c b/src/crypto/CryptonightR_JIT.c new file mode 100644 index 000000000..9add65296 --- /dev/null +++ b/src/crypto/CryptonightR_JIT.c @@ -0,0 +1,102 @@ +#include <assert.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> + +#include "int-util.h" +#include "hash-ops.h" +#include "variant4_random_math.h" +#include "CryptonightR_JIT.h" +#include "CryptonightR_template.h" + +static const uint8_t prologue[] = { + 0x4C, 0x8B, 0xD7, // mov r10, rdi + 0x53, // push rbx + 0x55, // push rbp + 0x41, 0x57, // push r15 + 0x4C, 0x8B, 0xDC, // mov r11, rsp + 0x41, 0x8B, 0x1A, // mov ebx, DWORD PTR [r10] + 0x41, 0x8B, 0x72, 0x04, // mov esi, DWORD PTR [r10+4] + 0x41, 0x8B, 0x7A, 0x08, // mov edi, DWORD PTR [r10+8] + 0x41, 0x8B, 0x6A, 0x0C, // mov ebp, DWORD PTR [r10+12] + 0x41, 0x8B, 0x62, 0x10, // mov esp, DWORD PTR [r10+16] + 0x45, 0x8B, 0x7A, 0x14, // mov r15d, DWORD PTR [r10+20] + 0x41, 0x8B, 0x42, 0x18, // mov eax, DWORD PTR [r10+24] + 0x41, 0x8B, 0x52, 0x1C, // mov edx, DWORD PTR [r10+28] + 0x45, 0x8B, 0x4A, 0x20, // mov r9d, DWORD PTR [r10+32] +}; + +static const uint8_t epilogue[] = { + 0x49, 0x8B, 0xE3, // mov rsp, r11 + 0x41, 0x89, 0x1A, // mov DWORD PTR [r10], ebx + 0x41, 0x89, 0x72, 0x04, // mov DWORD PTR [r10+4], esi + 0x41, 0x89, 0x7A, 0x08, // mov DWORD PTR [r10+8], edi + 0x41, 0x89, 0x6A, 0x0C, // mov DWORD PTR [r10+12], ebp + 0x41, 0x5F, // pop r15 + 0x5D, // pop rbp + 0x5B, // pop rbx + 0xC3, // ret +}; + +#define APPEND_CODE(src, size) \ + do { \ + if (JIT_code + (size) > JIT_code_end) \ + return -1; \ + memcpy(JIT_code, (src), (size)); \ + JIT_code += (size); \ + } while (0) + +int v4_generate_JIT_code(const struct V4_Instruction* code, v4_random_math_JIT_func buf, const size_t buf_size) +{ + uint8_t* JIT_code = (uint8_t*) buf; + const uint8_t* JIT_code_end = JIT_code + buf_size; + + APPEND_CODE(prologue, sizeof(prologue)); + + uint32_t prev_rot_src = 0xFFFFFFFFU; + + for (int i = 0;; ++i) + { + const struct V4_Instruction inst = code[i]; + if (inst.opcode == RET) + break; + + const uint8_t opcode = (inst.opcode == MUL) ? inst.opcode : (inst.opcode + 2); + + const uint32_t a = inst.dst_index; + const uint32_t b = inst.src_index; + const uint8_t c = opcode | (inst.dst_index << V4_OPCODE_BITS) | (((inst.src_index == 8) ? inst.dst_index : inst.src_index) << (V4_OPCODE_BITS + V4_DST_INDEX_BITS)); + + switch (inst.opcode) + { + case ROR: + case ROL: + if (b != prev_rot_src) + { + prev_rot_src = b; + const uint8_t* p1 = (const uint8_t*) instructions_mov[c]; + const uint8_t* p2 = (const uint8_t*) instructions_mov[c + 1]; + APPEND_CODE(p1, p2 - p1); + } + break; + } + + if (a == prev_rot_src) + prev_rot_src = 0xFFFFFFFFU; + + const uint8_t* p1 = (const uint8_t*) instructions[c]; + const uint8_t* p2 = (const uint8_t*) instructions[c + 1]; + APPEND_CODE(p1, p2 - p1); + + if (inst.opcode == ADD) + *(uint32_t*)(JIT_code - 4) = inst.C; + } + + APPEND_CODE(epilogue, sizeof(epilogue)); + + __builtin___clear_cache((char*)buf, (char*)JIT_code); + + return 0; +} diff --git a/src/crypto/CryptonightR_JIT.h b/src/crypto/CryptonightR_JIT.h new file mode 100644 index 000000000..5f689b37b --- /dev/null +++ b/src/crypto/CryptonightR_JIT.h @@ -0,0 +1,18 @@ +#ifndef CRYPTONIGHTR_JIT_H +#define CRYPTONIGHTR_JIT_H + +// Minimalistic JIT code generator for random math sequence in CryptonightR +// +// Usage: +// - Allocate writable and executable memory +// - Call v4_generate_JIT_code with "buf" pointed to memory allocated on previous step +// - Call the generated code instead of "v4_random_math(code, r)", omit the "code" parameter + +typedef void (*v4_random_math_JIT_func)(uint32_t* r) __attribute__((sysv_abi)); + +// Given the random math sequence, generates machine code (x86-64) for it +// Returns 0 if code was generated successfully +// Returns -1 if provided buffer was too small +int v4_generate_JIT_code(const struct V4_Instruction* code, v4_random_math_JIT_func buf, const size_t buf_size); + +#endif // CRYPTONIGHTR_JIT_H diff --git a/src/crypto/CryptonightR_template.S b/src/crypto/CryptonightR_template.S new file mode 100644 index 000000000..068de22ec --- /dev/null +++ b/src/crypto/CryptonightR_template.S @@ -0,0 +1,1590 @@ +#ifdef __APPLE__ +# define ALIGN(x) .align 6 +#else +# define ALIGN(x) .align 64 +#endif +.intel_syntax noprefix +#ifdef __APPLE__ +# define FN_PREFIX(fn) _ ## fn +.text +#else +# define FN_PREFIX(fn) fn +.section .text +#endif + +#define PUBLIC .global + +PUBLIC FN_PREFIX(CryptonightR_instruction0) +PUBLIC FN_PREFIX(CryptonightR_instruction1) +PUBLIC FN_PREFIX(CryptonightR_instruction2) +PUBLIC FN_PREFIX(CryptonightR_instruction3) +PUBLIC FN_PREFIX(CryptonightR_instruction4) +PUBLIC FN_PREFIX(CryptonightR_instruction5) +PUBLIC FN_PREFIX(CryptonightR_instruction6) +PUBLIC FN_PREFIX(CryptonightR_instruction7) +PUBLIC FN_PREFIX(CryptonightR_instruction8) +PUBLIC FN_PREFIX(CryptonightR_instruction9) +PUBLIC FN_PREFIX(CryptonightR_instruction10) +PUBLIC FN_PREFIX(CryptonightR_instruction11) +PUBLIC FN_PREFIX(CryptonightR_instruction12) +PUBLIC FN_PREFIX(CryptonightR_instruction13) +PUBLIC FN_PREFIX(CryptonightR_instruction14) +PUBLIC FN_PREFIX(CryptonightR_instruction15) +PUBLIC FN_PREFIX(CryptonightR_instruction16) +PUBLIC FN_PREFIX(CryptonightR_instruction17) +PUBLIC FN_PREFIX(CryptonightR_instruction18) +PUBLIC FN_PREFIX(CryptonightR_instruction19) +PUBLIC FN_PREFIX(CryptonightR_instruction20) +PUBLIC FN_PREFIX(CryptonightR_instruction21) +PUBLIC FN_PREFIX(CryptonightR_instruction22) +PUBLIC FN_PREFIX(CryptonightR_instruction23) +PUBLIC FN_PREFIX(CryptonightR_instruction24) +PUBLIC FN_PREFIX(CryptonightR_instruction25) +PUBLIC FN_PREFIX(CryptonightR_instruction26) +PUBLIC FN_PREFIX(CryptonightR_instruction27) +PUBLIC FN_PREFIX(CryptonightR_instruction28) +PUBLIC FN_PREFIX(CryptonightR_instruction29) +PUBLIC FN_PREFIX(CryptonightR_instruction30) +PUBLIC FN_PREFIX(CryptonightR_instruction31) +PUBLIC FN_PREFIX(CryptonightR_instruction32) +PUBLIC FN_PREFIX(CryptonightR_instruction33) +PUBLIC FN_PREFIX(CryptonightR_instruction34) +PUBLIC FN_PREFIX(CryptonightR_instruction35) +PUBLIC FN_PREFIX(CryptonightR_instruction36) +PUBLIC FN_PREFIX(CryptonightR_instruction37) +PUBLIC FN_PREFIX(CryptonightR_instruction38) +PUBLIC FN_PREFIX(CryptonightR_instruction39) +PUBLIC FN_PREFIX(CryptonightR_instruction40) +PUBLIC FN_PREFIX(CryptonightR_instruction41) +PUBLIC FN_PREFIX(CryptonightR_instruction42) +PUBLIC FN_PREFIX(CryptonightR_instruction43) +PUBLIC FN_PREFIX(CryptonightR_instruction44) +PUBLIC FN_PREFIX(CryptonightR_instruction45) +PUBLIC FN_PREFIX(CryptonightR_instruction46) +PUBLIC FN_PREFIX(CryptonightR_instruction47) +PUBLIC FN_PREFIX(CryptonightR_instruction48) +PUBLIC FN_PREFIX(CryptonightR_instruction49) +PUBLIC FN_PREFIX(CryptonightR_instruction50) +PUBLIC FN_PREFIX(CryptonightR_instruction51) +PUBLIC FN_PREFIX(CryptonightR_instruction52) +PUBLIC FN_PREFIX(CryptonightR_instruction53) +PUBLIC FN_PREFIX(CryptonightR_instruction54) +PUBLIC FN_PREFIX(CryptonightR_instruction55) +PUBLIC FN_PREFIX(CryptonightR_instruction56) +PUBLIC FN_PREFIX(CryptonightR_instruction57) +PUBLIC FN_PREFIX(CryptonightR_instruction58) +PUBLIC FN_PREFIX(CryptonightR_instruction59) +PUBLIC FN_PREFIX(CryptonightR_instruction60) +PUBLIC FN_PREFIX(CryptonightR_instruction61) +PUBLIC FN_PREFIX(CryptonightR_instruction62) +PUBLIC FN_PREFIX(CryptonightR_instruction63) +PUBLIC FN_PREFIX(CryptonightR_instruction64) +PUBLIC FN_PREFIX(CryptonightR_instruction65) +PUBLIC FN_PREFIX(CryptonightR_instruction66) +PUBLIC FN_PREFIX(CryptonightR_instruction67) +PUBLIC FN_PREFIX(CryptonightR_instruction68) +PUBLIC FN_PREFIX(CryptonightR_instruction69) +PUBLIC FN_PREFIX(CryptonightR_instruction70) +PUBLIC FN_PREFIX(CryptonightR_instruction71) +PUBLIC FN_PREFIX(CryptonightR_instruction72) +PUBLIC FN_PREFIX(CryptonightR_instruction73) +PUBLIC FN_PREFIX(CryptonightR_instruction74) +PUBLIC FN_PREFIX(CryptonightR_instruction75) +PUBLIC FN_PREFIX(CryptonightR_instruction76) +PUBLIC FN_PREFIX(CryptonightR_instruction77) +PUBLIC FN_PREFIX(CryptonightR_instruction78) +PUBLIC FN_PREFIX(CryptonightR_instruction79) +PUBLIC FN_PREFIX(CryptonightR_instruction80) +PUBLIC FN_PREFIX(CryptonightR_instruction81) +PUBLIC FN_PREFIX(CryptonightR_instruction82) +PUBLIC FN_PREFIX(CryptonightR_instruction83) +PUBLIC FN_PREFIX(CryptonightR_instruction84) +PUBLIC FN_PREFIX(CryptonightR_instruction85) +PUBLIC FN_PREFIX(CryptonightR_instruction86) +PUBLIC FN_PREFIX(CryptonightR_instruction87) +PUBLIC FN_PREFIX(CryptonightR_instruction88) +PUBLIC FN_PREFIX(CryptonightR_instruction89) +PUBLIC FN_PREFIX(CryptonightR_instruction90) +PUBLIC FN_PREFIX(CryptonightR_instruction91) +PUBLIC FN_PREFIX(CryptonightR_instruction92) +PUBLIC FN_PREFIX(CryptonightR_instruction93) +PUBLIC FN_PREFIX(CryptonightR_instruction94) +PUBLIC FN_PREFIX(CryptonightR_instruction95) +PUBLIC FN_PREFIX(CryptonightR_instruction96) +PUBLIC FN_PREFIX(CryptonightR_instruction97) +PUBLIC FN_PREFIX(CryptonightR_instruction98) +PUBLIC FN_PREFIX(CryptonightR_instruction99) +PUBLIC FN_PREFIX(CryptonightR_instruction100) +PUBLIC FN_PREFIX(CryptonightR_instruction101) +PUBLIC FN_PREFIX(CryptonightR_instruction102) +PUBLIC FN_PREFIX(CryptonightR_instruction103) +PUBLIC FN_PREFIX(CryptonightR_instruction104) +PUBLIC FN_PREFIX(CryptonightR_instruction105) +PUBLIC FN_PREFIX(CryptonightR_instruction106) +PUBLIC FN_PREFIX(CryptonightR_instruction107) +PUBLIC FN_PREFIX(CryptonightR_instruction108) +PUBLIC FN_PREFIX(CryptonightR_instruction109) +PUBLIC FN_PREFIX(CryptonightR_instruction110) +PUBLIC FN_PREFIX(CryptonightR_instruction111) +PUBLIC FN_PREFIX(CryptonightR_instruction112) +PUBLIC FN_PREFIX(CryptonightR_instruction113) +PUBLIC FN_PREFIX(CryptonightR_instruction114) +PUBLIC FN_PREFIX(CryptonightR_instruction115) +PUBLIC FN_PREFIX(CryptonightR_instruction116) +PUBLIC FN_PREFIX(CryptonightR_instruction117) +PUBLIC FN_PREFIX(CryptonightR_instruction118) +PUBLIC FN_PREFIX(CryptonightR_instruction119) +PUBLIC FN_PREFIX(CryptonightR_instruction120) +PUBLIC FN_PREFIX(CryptonightR_instruction121) +PUBLIC FN_PREFIX(CryptonightR_instruction122) +PUBLIC FN_PREFIX(CryptonightR_instruction123) +PUBLIC FN_PREFIX(CryptonightR_instruction124) +PUBLIC FN_PREFIX(CryptonightR_instruction125) +PUBLIC FN_PREFIX(CryptonightR_instruction126) +PUBLIC FN_PREFIX(CryptonightR_instruction127) +PUBLIC FN_PREFIX(CryptonightR_instruction128) +PUBLIC FN_PREFIX(CryptonightR_instruction129) +PUBLIC FN_PREFIX(CryptonightR_instruction130) +PUBLIC FN_PREFIX(CryptonightR_instruction131) +PUBLIC FN_PREFIX(CryptonightR_instruction132) +PUBLIC FN_PREFIX(CryptonightR_instruction133) +PUBLIC FN_PREFIX(CryptonightR_instruction134) +PUBLIC FN_PREFIX(CryptonightR_instruction135) +PUBLIC FN_PREFIX(CryptonightR_instruction136) +PUBLIC FN_PREFIX(CryptonightR_instruction137) +PUBLIC FN_PREFIX(CryptonightR_instruction138) +PUBLIC FN_PREFIX(CryptonightR_instruction139) +PUBLIC FN_PREFIX(CryptonightR_instruction140) +PUBLIC FN_PREFIX(CryptonightR_instruction141) +PUBLIC FN_PREFIX(CryptonightR_instruction142) +PUBLIC FN_PREFIX(CryptonightR_instruction143) +PUBLIC FN_PREFIX(CryptonightR_instruction144) +PUBLIC FN_PREFIX(CryptonightR_instruction145) +PUBLIC FN_PREFIX(CryptonightR_instruction146) +PUBLIC FN_PREFIX(CryptonightR_instruction147) +PUBLIC FN_PREFIX(CryptonightR_instruction148) +PUBLIC FN_PREFIX(CryptonightR_instruction149) +PUBLIC FN_PREFIX(CryptonightR_instruction150) +PUBLIC FN_PREFIX(CryptonightR_instruction151) +PUBLIC FN_PREFIX(CryptonightR_instruction152) +PUBLIC FN_PREFIX(CryptonightR_instruction153) +PUBLIC FN_PREFIX(CryptonightR_instruction154) +PUBLIC FN_PREFIX(CryptonightR_instruction155) +PUBLIC FN_PREFIX(CryptonightR_instruction156) +PUBLIC FN_PREFIX(CryptonightR_instruction157) +PUBLIC FN_PREFIX(CryptonightR_instruction158) +PUBLIC FN_PREFIX(CryptonightR_instruction159) +PUBLIC FN_PREFIX(CryptonightR_instruction160) +PUBLIC FN_PREFIX(CryptonightR_instruction161) +PUBLIC FN_PREFIX(CryptonightR_instruction162) +PUBLIC FN_PREFIX(CryptonightR_instruction163) +PUBLIC FN_PREFIX(CryptonightR_instruction164) +PUBLIC FN_PREFIX(CryptonightR_instruction165) +PUBLIC FN_PREFIX(CryptonightR_instruction166) +PUBLIC FN_PREFIX(CryptonightR_instruction167) +PUBLIC FN_PREFIX(CryptonightR_instruction168) +PUBLIC FN_PREFIX(CryptonightR_instruction169) +PUBLIC FN_PREFIX(CryptonightR_instruction170) +PUBLIC FN_PREFIX(CryptonightR_instruction171) +PUBLIC FN_PREFIX(CryptonightR_instruction172) +PUBLIC FN_PREFIX(CryptonightR_instruction173) +PUBLIC FN_PREFIX(CryptonightR_instruction174) +PUBLIC FN_PREFIX(CryptonightR_instruction175) +PUBLIC FN_PREFIX(CryptonightR_instruction176) +PUBLIC FN_PREFIX(CryptonightR_instruction177) +PUBLIC FN_PREFIX(CryptonightR_instruction178) +PUBLIC FN_PREFIX(CryptonightR_instruction179) +PUBLIC FN_PREFIX(CryptonightR_instruction180) +PUBLIC FN_PREFIX(CryptonightR_instruction181) +PUBLIC FN_PREFIX(CryptonightR_instruction182) +PUBLIC FN_PREFIX(CryptonightR_instruction183) +PUBLIC FN_PREFIX(CryptonightR_instruction184) +PUBLIC FN_PREFIX(CryptonightR_instruction185) +PUBLIC FN_PREFIX(CryptonightR_instruction186) +PUBLIC FN_PREFIX(CryptonightR_instruction187) +PUBLIC FN_PREFIX(CryptonightR_instruction188) +PUBLIC FN_PREFIX(CryptonightR_instruction189) +PUBLIC FN_PREFIX(CryptonightR_instruction190) +PUBLIC FN_PREFIX(CryptonightR_instruction191) +PUBLIC FN_PREFIX(CryptonightR_instruction192) +PUBLIC FN_PREFIX(CryptonightR_instruction193) +PUBLIC FN_PREFIX(CryptonightR_instruction194) +PUBLIC FN_PREFIX(CryptonightR_instruction195) +PUBLIC FN_PREFIX(CryptonightR_instruction196) +PUBLIC FN_PREFIX(CryptonightR_instruction197) +PUBLIC FN_PREFIX(CryptonightR_instruction198) +PUBLIC FN_PREFIX(CryptonightR_instruction199) +PUBLIC FN_PREFIX(CryptonightR_instruction200) +PUBLIC FN_PREFIX(CryptonightR_instruction201) +PUBLIC FN_PREFIX(CryptonightR_instruction202) +PUBLIC FN_PREFIX(CryptonightR_instruction203) +PUBLIC FN_PREFIX(CryptonightR_instruction204) +PUBLIC FN_PREFIX(CryptonightR_instruction205) +PUBLIC FN_PREFIX(CryptonightR_instruction206) +PUBLIC FN_PREFIX(CryptonightR_instruction207) +PUBLIC FN_PREFIX(CryptonightR_instruction208) +PUBLIC FN_PREFIX(CryptonightR_instruction209) +PUBLIC FN_PREFIX(CryptonightR_instruction210) +PUBLIC FN_PREFIX(CryptonightR_instruction211) +PUBLIC FN_PREFIX(CryptonightR_instruction212) +PUBLIC FN_PREFIX(CryptonightR_instruction213) +PUBLIC FN_PREFIX(CryptonightR_instruction214) +PUBLIC FN_PREFIX(CryptonightR_instruction215) +PUBLIC FN_PREFIX(CryptonightR_instruction216) +PUBLIC FN_PREFIX(CryptonightR_instruction217) +PUBLIC FN_PREFIX(CryptonightR_instruction218) +PUBLIC FN_PREFIX(CryptonightR_instruction219) +PUBLIC FN_PREFIX(CryptonightR_instruction220) +PUBLIC FN_PREFIX(CryptonightR_instruction221) +PUBLIC FN_PREFIX(CryptonightR_instruction222) +PUBLIC FN_PREFIX(CryptonightR_instruction223) +PUBLIC FN_PREFIX(CryptonightR_instruction224) +PUBLIC FN_PREFIX(CryptonightR_instruction225) +PUBLIC FN_PREFIX(CryptonightR_instruction226) +PUBLIC FN_PREFIX(CryptonightR_instruction227) +PUBLIC FN_PREFIX(CryptonightR_instruction228) +PUBLIC FN_PREFIX(CryptonightR_instruction229) +PUBLIC FN_PREFIX(CryptonightR_instruction230) +PUBLIC FN_PREFIX(CryptonightR_instruction231) +PUBLIC FN_PREFIX(CryptonightR_instruction232) +PUBLIC FN_PREFIX(CryptonightR_instruction233) +PUBLIC FN_PREFIX(CryptonightR_instruction234) +PUBLIC FN_PREFIX(CryptonightR_instruction235) +PUBLIC FN_PREFIX(CryptonightR_instruction236) +PUBLIC FN_PREFIX(CryptonightR_instruction237) +PUBLIC FN_PREFIX(CryptonightR_instruction238) +PUBLIC FN_PREFIX(CryptonightR_instruction239) +PUBLIC FN_PREFIX(CryptonightR_instruction240) +PUBLIC FN_PREFIX(CryptonightR_instruction241) +PUBLIC FN_PREFIX(CryptonightR_instruction242) +PUBLIC FN_PREFIX(CryptonightR_instruction243) +PUBLIC FN_PREFIX(CryptonightR_instruction244) +PUBLIC FN_PREFIX(CryptonightR_instruction245) +PUBLIC FN_PREFIX(CryptonightR_instruction246) +PUBLIC FN_PREFIX(CryptonightR_instruction247) +PUBLIC FN_PREFIX(CryptonightR_instruction248) +PUBLIC FN_PREFIX(CryptonightR_instruction249) +PUBLIC FN_PREFIX(CryptonightR_instruction250) +PUBLIC FN_PREFIX(CryptonightR_instruction251) +PUBLIC FN_PREFIX(CryptonightR_instruction252) +PUBLIC FN_PREFIX(CryptonightR_instruction253) +PUBLIC FN_PREFIX(CryptonightR_instruction254) +PUBLIC FN_PREFIX(CryptonightR_instruction255) +PUBLIC FN_PREFIX(CryptonightR_instruction256) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov0) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov1) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov2) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov3) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov4) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov5) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov6) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov7) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov8) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov9) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov10) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov11) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov12) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov13) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov14) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov15) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov16) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov17) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov18) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov19) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov20) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov21) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov22) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov23) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov24) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov25) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov26) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov27) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov28) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov29) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov30) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov31) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov32) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov33) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov34) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov35) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov36) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov37) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov38) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov39) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov40) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov41) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov42) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov43) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov44) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov45) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov46) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov47) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov48) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov49) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov50) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov51) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov52) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov53) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov54) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov55) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov56) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov57) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov58) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov59) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov60) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov61) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov62) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov63) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov64) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov65) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov66) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov67) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov68) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov69) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov70) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov71) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov72) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov73) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov74) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov75) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov76) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov77) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov78) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov79) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov80) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov81) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov82) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov83) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov84) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov85) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov86) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov87) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov88) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov89) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov90) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov91) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov92) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov93) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov94) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov95) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov96) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov97) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov98) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov99) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov100) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov101) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov102) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov103) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov104) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov105) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov106) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov107) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov108) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov109) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov110) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov111) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov112) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov113) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov114) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov115) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov116) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov117) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov118) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov119) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov120) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov121) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov122) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov123) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov124) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov125) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov126) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov127) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov128) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov129) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov130) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov131) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov132) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov133) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov134) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov135) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov136) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov137) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov138) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov139) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov140) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov141) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov142) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov143) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov144) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov145) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov146) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov147) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov148) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov149) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov150) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov151) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov152) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov153) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov154) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov155) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov156) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov157) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov158) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov159) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov160) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov161) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov162) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov163) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov164) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov165) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov166) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov167) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov168) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov169) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov170) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov171) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov172) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov173) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov174) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov175) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov176) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov177) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov178) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov179) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov180) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov181) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov182) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov183) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov184) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov185) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov186) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov187) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov188) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov189) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov190) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov191) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov192) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov193) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov194) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov195) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov196) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov197) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov198) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov199) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov200) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov201) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov202) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov203) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov204) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov205) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov206) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov207) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov208) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov209) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov210) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov211) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov212) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov213) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov214) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov215) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov216) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov217) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov218) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov219) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov220) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov221) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov222) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov223) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov224) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov225) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov226) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov227) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov228) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov229) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov230) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov231) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov232) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov233) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov234) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov235) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov236) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov237) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov238) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov239) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov240) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov241) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov242) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov243) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov244) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov245) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov246) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov247) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov248) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov249) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov250) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov251) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov252) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov253) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov254) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov255) +PUBLIC FN_PREFIX(CryptonightR_instruction_mov256) + +FN_PREFIX(CryptonightR_instruction0): + imul ebx, ebx +FN_PREFIX(CryptonightR_instruction1): + imul ebx, ebx +FN_PREFIX(CryptonightR_instruction2): + imul ebx, ebx +FN_PREFIX(CryptonightR_instruction3): + add ebx, r9d + add ebx, 2147483647 +FN_PREFIX(CryptonightR_instruction4): + sub ebx, r9d +FN_PREFIX(CryptonightR_instruction5): + ror ebx, cl +FN_PREFIX(CryptonightR_instruction6): + rol ebx, cl +FN_PREFIX(CryptonightR_instruction7): + xor ebx, r9d +FN_PREFIX(CryptonightR_instruction8): + imul esi, ebx +FN_PREFIX(CryptonightR_instruction9): + imul esi, ebx +FN_PREFIX(CryptonightR_instruction10): + imul esi, ebx +FN_PREFIX(CryptonightR_instruction11): + add esi, ebx + add esi, 2147483647 +FN_PREFIX(CryptonightR_instruction12): + sub esi, ebx +FN_PREFIX(CryptonightR_instruction13): + ror esi, cl +FN_PREFIX(CryptonightR_instruction14): + rol esi, cl +FN_PREFIX(CryptonightR_instruction15): + xor esi, ebx +FN_PREFIX(CryptonightR_instruction16): + imul edi, ebx +FN_PREFIX(CryptonightR_instruction17): + imul edi, ebx +FN_PREFIX(CryptonightR_instruction18): + imul edi, ebx +FN_PREFIX(CryptonightR_instruction19): + add edi, ebx + add edi, 2147483647 +FN_PREFIX(CryptonightR_instruction20): + sub edi, ebx +FN_PREFIX(CryptonightR_instruction21): + ror edi, cl +FN_PREFIX(CryptonightR_instruction22): + rol edi, cl +FN_PREFIX(CryptonightR_instruction23): + xor edi, ebx +FN_PREFIX(CryptonightR_instruction24): + imul ebp, ebx +FN_PREFIX(CryptonightR_instruction25): + imul ebp, ebx +FN_PREFIX(CryptonightR_instruction26): + imul ebp, ebx +FN_PREFIX(CryptonightR_instruction27): + add ebp, ebx + add ebp, 2147483647 +FN_PREFIX(CryptonightR_instruction28): + sub ebp, ebx +FN_PREFIX(CryptonightR_instruction29): + ror ebp, cl +FN_PREFIX(CryptonightR_instruction30): + rol ebp, cl +FN_PREFIX(CryptonightR_instruction31): + xor ebp, ebx +FN_PREFIX(CryptonightR_instruction32): + imul ebx, esi +FN_PREFIX(CryptonightR_instruction33): + imul ebx, esi +FN_PREFIX(CryptonightR_instruction34): + imul ebx, esi +FN_PREFIX(CryptonightR_instruction35): + add ebx, esi + add ebx, 2147483647 +FN_PREFIX(CryptonightR_instruction36): + sub ebx, esi +FN_PREFIX(CryptonightR_instruction37): + ror ebx, cl +FN_PREFIX(CryptonightR_instruction38): + rol ebx, cl +FN_PREFIX(CryptonightR_instruction39): + xor ebx, esi +FN_PREFIX(CryptonightR_instruction40): + imul esi, esi +FN_PREFIX(CryptonightR_instruction41): + imul esi, esi +FN_PREFIX(CryptonightR_instruction42): + imul esi, esi +FN_PREFIX(CryptonightR_instruction43): + add esi, r9d + add esi, 2147483647 +FN_PREFIX(CryptonightR_instruction44): + sub esi, r9d +FN_PREFIX(CryptonightR_instruction45): + ror esi, cl +FN_PREFIX(CryptonightR_instruction46): + rol esi, cl +FN_PREFIX(CryptonightR_instruction47): + xor esi, r9d +FN_PREFIX(CryptonightR_instruction48): + imul edi, esi +FN_PREFIX(CryptonightR_instruction49): + imul edi, esi +FN_PREFIX(CryptonightR_instruction50): + imul edi, esi +FN_PREFIX(CryptonightR_instruction51): + add edi, esi + add edi, 2147483647 +FN_PREFIX(CryptonightR_instruction52): + sub edi, esi +FN_PREFIX(CryptonightR_instruction53): + ror edi, cl +FN_PREFIX(CryptonightR_instruction54): + rol edi, cl +FN_PREFIX(CryptonightR_instruction55): + xor edi, esi +FN_PREFIX(CryptonightR_instruction56): + imul ebp, esi +FN_PREFIX(CryptonightR_instruction57): + imul ebp, esi +FN_PREFIX(CryptonightR_instruction58): + imul ebp, esi +FN_PREFIX(CryptonightR_instruction59): + add ebp, esi + add ebp, 2147483647 +FN_PREFIX(CryptonightR_instruction60): + sub ebp, esi +FN_PREFIX(CryptonightR_instruction61): + ror ebp, cl +FN_PREFIX(CryptonightR_instruction62): + rol ebp, cl +FN_PREFIX(CryptonightR_instruction63): + xor ebp, esi +FN_PREFIX(CryptonightR_instruction64): + imul ebx, edi +FN_PREFIX(CryptonightR_instruction65): + imul ebx, edi +FN_PREFIX(CryptonightR_instruction66): + imul ebx, edi +FN_PREFIX(CryptonightR_instruction67): + add ebx, edi + add ebx, 2147483647 +FN_PREFIX(CryptonightR_instruction68): + sub ebx, edi +FN_PREFIX(CryptonightR_instruction69): + ror ebx, cl +FN_PREFIX(CryptonightR_instruction70): + rol ebx, cl +FN_PREFIX(CryptonightR_instruction71): + xor ebx, edi +FN_PREFIX(CryptonightR_instruction72): + imul esi, edi +FN_PREFIX(CryptonightR_instruction73): + imul esi, edi +FN_PREFIX(CryptonightR_instruction74): + imul esi, edi +FN_PREFIX(CryptonightR_instruction75): + add esi, edi + add esi, 2147483647 +FN_PREFIX(CryptonightR_instruction76): + sub esi, edi +FN_PREFIX(CryptonightR_instruction77): + ror esi, cl +FN_PREFIX(CryptonightR_instruction78): + rol esi, cl +FN_PREFIX(CryptonightR_instruction79): + xor esi, edi +FN_PREFIX(CryptonightR_instruction80): + imul edi, edi +FN_PREFIX(CryptonightR_instruction81): + imul edi, edi +FN_PREFIX(CryptonightR_instruction82): + imul edi, edi +FN_PREFIX(CryptonightR_instruction83): + add edi, r9d + add edi, 2147483647 +FN_PREFIX(CryptonightR_instruction84): + sub edi, r9d +FN_PREFIX(CryptonightR_instruction85): + ror edi, cl +FN_PREFIX(CryptonightR_instruction86): + rol edi, cl +FN_PREFIX(CryptonightR_instruction87): + xor edi, r9d +FN_PREFIX(CryptonightR_instruction88): + imul ebp, edi +FN_PREFIX(CryptonightR_instruction89): + imul ebp, edi +FN_PREFIX(CryptonightR_instruction90): + imul ebp, edi +FN_PREFIX(CryptonightR_instruction91): + add ebp, edi + add ebp, 2147483647 +FN_PREFIX(CryptonightR_instruction92): + sub ebp, edi +FN_PREFIX(CryptonightR_instruction93): + ror ebp, cl +FN_PREFIX(CryptonightR_instruction94): + rol ebp, cl +FN_PREFIX(CryptonightR_instruction95): + xor ebp, edi +FN_PREFIX(CryptonightR_instruction96): + imul ebx, ebp +FN_PREFIX(CryptonightR_instruction97): + imul ebx, ebp +FN_PREFIX(CryptonightR_instruction98): + imul ebx, ebp +FN_PREFIX(CryptonightR_instruction99): + add ebx, ebp + add ebx, 2147483647 +FN_PREFIX(CryptonightR_instruction100): + sub ebx, ebp +FN_PREFIX(CryptonightR_instruction101): + ror ebx, cl +FN_PREFIX(CryptonightR_instruction102): + rol ebx, cl +FN_PREFIX(CryptonightR_instruction103): + xor ebx, ebp +FN_PREFIX(CryptonightR_instruction104): + imul esi, ebp +FN_PREFIX(CryptonightR_instruction105): + imul esi, ebp +FN_PREFIX(CryptonightR_instruction106): + imul esi, ebp +FN_PREFIX(CryptonightR_instruction107): + add esi, ebp + add esi, 2147483647 +FN_PREFIX(CryptonightR_instruction108): + sub esi, ebp +FN_PREFIX(CryptonightR_instruction109): + ror esi, cl +FN_PREFIX(CryptonightR_instruction110): + rol esi, cl +FN_PREFIX(CryptonightR_instruction111): + xor esi, ebp +FN_PREFIX(CryptonightR_instruction112): + imul edi, ebp +FN_PREFIX(CryptonightR_instruction113): + imul edi, ebp +FN_PREFIX(CryptonightR_instruction114): + imul edi, ebp +FN_PREFIX(CryptonightR_instruction115): + add edi, ebp + add edi, 2147483647 +FN_PREFIX(CryptonightR_instruction116): + sub edi, ebp +FN_PREFIX(CryptonightR_instruction117): + ror edi, cl +FN_PREFIX(CryptonightR_instruction118): + rol edi, cl +FN_PREFIX(CryptonightR_instruction119): + xor edi, ebp +FN_PREFIX(CryptonightR_instruction120): + imul ebp, ebp +FN_PREFIX(CryptonightR_instruction121): + imul ebp, ebp +FN_PREFIX(CryptonightR_instruction122): + imul ebp, ebp +FN_PREFIX(CryptonightR_instruction123): + add ebp, r9d + add ebp, 2147483647 +FN_PREFIX(CryptonightR_instruction124): + sub ebp, r9d +FN_PREFIX(CryptonightR_instruction125): + ror ebp, cl +FN_PREFIX(CryptonightR_instruction126): + rol ebp, cl +FN_PREFIX(CryptonightR_instruction127): + xor ebp, r9d +FN_PREFIX(CryptonightR_instruction128): + imul ebx, esp +FN_PREFIX(CryptonightR_instruction129): + imul ebx, esp +FN_PREFIX(CryptonightR_instruction130): + imul ebx, esp +FN_PREFIX(CryptonightR_instruction131): + add ebx, esp + add ebx, 2147483647 +FN_PREFIX(CryptonightR_instruction132): + sub ebx, esp +FN_PREFIX(CryptonightR_instruction133): + ror ebx, cl +FN_PREFIX(CryptonightR_instruction134): + rol ebx, cl +FN_PREFIX(CryptonightR_instruction135): + xor ebx, esp +FN_PREFIX(CryptonightR_instruction136): + imul esi, esp +FN_PREFIX(CryptonightR_instruction137): + imul esi, esp +FN_PREFIX(CryptonightR_instruction138): + imul esi, esp +FN_PREFIX(CryptonightR_instruction139): + add esi, esp + add esi, 2147483647 +FN_PREFIX(CryptonightR_instruction140): + sub esi, esp +FN_PREFIX(CryptonightR_instruction141): + ror esi, cl +FN_PREFIX(CryptonightR_instruction142): + rol esi, cl +FN_PREFIX(CryptonightR_instruction143): + xor esi, esp +FN_PREFIX(CryptonightR_instruction144): + imul edi, esp +FN_PREFIX(CryptonightR_instruction145): + imul edi, esp +FN_PREFIX(CryptonightR_instruction146): + imul edi, esp +FN_PREFIX(CryptonightR_instruction147): + add edi, esp + add edi, 2147483647 +FN_PREFIX(CryptonightR_instruction148): + sub edi, esp +FN_PREFIX(CryptonightR_instruction149): + ror edi, cl +FN_PREFIX(CryptonightR_instruction150): + rol edi, cl +FN_PREFIX(CryptonightR_instruction151): + xor edi, esp +FN_PREFIX(CryptonightR_instruction152): + imul ebp, esp +FN_PREFIX(CryptonightR_instruction153): + imul ebp, esp +FN_PREFIX(CryptonightR_instruction154): + imul ebp, esp +FN_PREFIX(CryptonightR_instruction155): + add ebp, esp + add ebp, 2147483647 +FN_PREFIX(CryptonightR_instruction156): + sub ebp, esp +FN_PREFIX(CryptonightR_instruction157): + ror ebp, cl +FN_PREFIX(CryptonightR_instruction158): + rol ebp, cl +FN_PREFIX(CryptonightR_instruction159): + xor ebp, esp +FN_PREFIX(CryptonightR_instruction160): + imul ebx, r15d +FN_PREFIX(CryptonightR_instruction161): + imul ebx, r15d +FN_PREFIX(CryptonightR_instruction162): + imul ebx, r15d +FN_PREFIX(CryptonightR_instruction163): + add ebx, r15d + add ebx, 2147483647 +FN_PREFIX(CryptonightR_instruction164): + sub ebx, r15d +FN_PREFIX(CryptonightR_instruction165): + ror ebx, cl +FN_PREFIX(CryptonightR_instruction166): + rol ebx, cl +FN_PREFIX(CryptonightR_instruction167): + xor ebx, r15d +FN_PREFIX(CryptonightR_instruction168): + imul esi, r15d +FN_PREFIX(CryptonightR_instruction169): + imul esi, r15d +FN_PREFIX(CryptonightR_instruction170): + imul esi, r15d +FN_PREFIX(CryptonightR_instruction171): + add esi, r15d + add esi, 2147483647 +FN_PREFIX(CryptonightR_instruction172): + sub esi, r15d +FN_PREFIX(CryptonightR_instruction173): + ror esi, cl +FN_PREFIX(CryptonightR_instruction174): + rol esi, cl +FN_PREFIX(CryptonightR_instruction175): + xor esi, r15d +FN_PREFIX(CryptonightR_instruction176): + imul edi, r15d +FN_PREFIX(CryptonightR_instruction177): + imul edi, r15d +FN_PREFIX(CryptonightR_instruction178): + imul edi, r15d +FN_PREFIX(CryptonightR_instruction179): + add edi, r15d + add edi, 2147483647 +FN_PREFIX(CryptonightR_instruction180): + sub edi, r15d +FN_PREFIX(CryptonightR_instruction181): + ror edi, cl +FN_PREFIX(CryptonightR_instruction182): + rol edi, cl +FN_PREFIX(CryptonightR_instruction183): + xor edi, r15d +FN_PREFIX(CryptonightR_instruction184): + imul ebp, r15d +FN_PREFIX(CryptonightR_instruction185): + imul ebp, r15d +FN_PREFIX(CryptonightR_instruction186): + imul ebp, r15d +FN_PREFIX(CryptonightR_instruction187): + add ebp, r15d + add ebp, 2147483647 +FN_PREFIX(CryptonightR_instruction188): + sub ebp, r15d +FN_PREFIX(CryptonightR_instruction189): + ror ebp, cl +FN_PREFIX(CryptonightR_instruction190): + rol ebp, cl +FN_PREFIX(CryptonightR_instruction191): + xor ebp, r15d +FN_PREFIX(CryptonightR_instruction192): + imul ebx, eax +FN_PREFIX(CryptonightR_instruction193): + imul ebx, eax +FN_PREFIX(CryptonightR_instruction194): + imul ebx, eax +FN_PREFIX(CryptonightR_instruction195): + add ebx, eax + add ebx, 2147483647 +FN_PREFIX(CryptonightR_instruction196): + sub ebx, eax +FN_PREFIX(CryptonightR_instruction197): + ror ebx, cl +FN_PREFIX(CryptonightR_instruction198): + rol ebx, cl +FN_PREFIX(CryptonightR_instruction199): + xor ebx, eax +FN_PREFIX(CryptonightR_instruction200): + imul esi, eax +FN_PREFIX(CryptonightR_instruction201): + imul esi, eax +FN_PREFIX(CryptonightR_instruction202): + imul esi, eax +FN_PREFIX(CryptonightR_instruction203): + add esi, eax + add esi, 2147483647 +FN_PREFIX(CryptonightR_instruction204): + sub esi, eax +FN_PREFIX(CryptonightR_instruction205): + ror esi, cl +FN_PREFIX(CryptonightR_instruction206): + rol esi, cl +FN_PREFIX(CryptonightR_instruction207): + xor esi, eax +FN_PREFIX(CryptonightR_instruction208): + imul edi, eax +FN_PREFIX(CryptonightR_instruction209): + imul edi, eax +FN_PREFIX(CryptonightR_instruction210): + imul edi, eax +FN_PREFIX(CryptonightR_instruction211): + add edi, eax + add edi, 2147483647 +FN_PREFIX(CryptonightR_instruction212): + sub edi, eax +FN_PREFIX(CryptonightR_instruction213): + ror edi, cl +FN_PREFIX(CryptonightR_instruction214): + rol edi, cl +FN_PREFIX(CryptonightR_instruction215): + xor edi, eax +FN_PREFIX(CryptonightR_instruction216): + imul ebp, eax +FN_PREFIX(CryptonightR_instruction217): + imul ebp, eax +FN_PREFIX(CryptonightR_instruction218): + imul ebp, eax +FN_PREFIX(CryptonightR_instruction219): + add ebp, eax + add ebp, 2147483647 +FN_PREFIX(CryptonightR_instruction220): + sub ebp, eax +FN_PREFIX(CryptonightR_instruction221): + ror ebp, cl +FN_PREFIX(CryptonightR_instruction222): + rol ebp, cl +FN_PREFIX(CryptonightR_instruction223): + xor ebp, eax +FN_PREFIX(CryptonightR_instruction224): + imul ebx, edx +FN_PREFIX(CryptonightR_instruction225): + imul ebx, edx +FN_PREFIX(CryptonightR_instruction226): + imul ebx, edx +FN_PREFIX(CryptonightR_instruction227): + add ebx, edx + add ebx, 2147483647 +FN_PREFIX(CryptonightR_instruction228): + sub ebx, edx +FN_PREFIX(CryptonightR_instruction229): + ror ebx, cl +FN_PREFIX(CryptonightR_instruction230): + rol ebx, cl +FN_PREFIX(CryptonightR_instruction231): + xor ebx, edx +FN_PREFIX(CryptonightR_instruction232): + imul esi, edx +FN_PREFIX(CryptonightR_instruction233): + imul esi, edx +FN_PREFIX(CryptonightR_instruction234): + imul esi, edx +FN_PREFIX(CryptonightR_instruction235): + add esi, edx + add esi, 2147483647 +FN_PREFIX(CryptonightR_instruction236): + sub esi, edx +FN_PREFIX(CryptonightR_instruction237): + ror esi, cl +FN_PREFIX(CryptonightR_instruction238): + rol esi, cl +FN_PREFIX(CryptonightR_instruction239): + xor esi, edx +FN_PREFIX(CryptonightR_instruction240): + imul edi, edx +FN_PREFIX(CryptonightR_instruction241): + imul edi, edx +FN_PREFIX(CryptonightR_instruction242): + imul edi, edx +FN_PREFIX(CryptonightR_instruction243): + add edi, edx + add edi, 2147483647 +FN_PREFIX(CryptonightR_instruction244): + sub edi, edx +FN_PREFIX(CryptonightR_instruction245): + ror edi, cl +FN_PREFIX(CryptonightR_instruction246): + rol edi, cl +FN_PREFIX(CryptonightR_instruction247): + xor edi, edx +FN_PREFIX(CryptonightR_instruction248): + imul ebp, edx +FN_PREFIX(CryptonightR_instruction249): + imul ebp, edx +FN_PREFIX(CryptonightR_instruction250): + imul ebp, edx +FN_PREFIX(CryptonightR_instruction251): + add ebp, edx + add ebp, 2147483647 +FN_PREFIX(CryptonightR_instruction252): + sub ebp, edx +FN_PREFIX(CryptonightR_instruction253): + ror ebp, cl +FN_PREFIX(CryptonightR_instruction254): + rol ebp, cl +FN_PREFIX(CryptonightR_instruction255): + xor ebp, edx +FN_PREFIX(CryptonightR_instruction256): + imul ebx, ebx +FN_PREFIX(CryptonightR_instruction_mov0): + +FN_PREFIX(CryptonightR_instruction_mov1): + +FN_PREFIX(CryptonightR_instruction_mov2): + +FN_PREFIX(CryptonightR_instruction_mov3): + +FN_PREFIX(CryptonightR_instruction_mov4): + +FN_PREFIX(CryptonightR_instruction_mov5): + mov ecx, ebx +FN_PREFIX(CryptonightR_instruction_mov6): + mov ecx, ebx +FN_PREFIX(CryptonightR_instruction_mov7): + +FN_PREFIX(CryptonightR_instruction_mov8): + +FN_PREFIX(CryptonightR_instruction_mov9): + +FN_PREFIX(CryptonightR_instruction_mov10): + +FN_PREFIX(CryptonightR_instruction_mov11): + +FN_PREFIX(CryptonightR_instruction_mov12): + +FN_PREFIX(CryptonightR_instruction_mov13): + mov ecx, ebx +FN_PREFIX(CryptonightR_instruction_mov14): + mov ecx, ebx +FN_PREFIX(CryptonightR_instruction_mov15): + +FN_PREFIX(CryptonightR_instruction_mov16): + +FN_PREFIX(CryptonightR_instruction_mov17): + +FN_PREFIX(CryptonightR_instruction_mov18): + +FN_PREFIX(CryptonightR_instruction_mov19): + +FN_PREFIX(CryptonightR_instruction_mov20): + +FN_PREFIX(CryptonightR_instruction_mov21): + mov ecx, ebx +FN_PREFIX(CryptonightR_instruction_mov22): + mov ecx, ebx +FN_PREFIX(CryptonightR_instruction_mov23): + +FN_PREFIX(CryptonightR_instruction_mov24): + +FN_PREFIX(CryptonightR_instruction_mov25): + +FN_PREFIX(CryptonightR_instruction_mov26): + +FN_PREFIX(CryptonightR_instruction_mov27): + +FN_PREFIX(CryptonightR_instruction_mov28): + +FN_PREFIX(CryptonightR_instruction_mov29): + mov ecx, ebx +FN_PREFIX(CryptonightR_instruction_mov30): + mov ecx, ebx +FN_PREFIX(CryptonightR_instruction_mov31): + +FN_PREFIX(CryptonightR_instruction_mov32): + +FN_PREFIX(CryptonightR_instruction_mov33): + +FN_PREFIX(CryptonightR_instruction_mov34): + +FN_PREFIX(CryptonightR_instruction_mov35): + +FN_PREFIX(CryptonightR_instruction_mov36): + +FN_PREFIX(CryptonightR_instruction_mov37): + mov ecx, esi +FN_PREFIX(CryptonightR_instruction_mov38): + mov ecx, esi +FN_PREFIX(CryptonightR_instruction_mov39): + +FN_PREFIX(CryptonightR_instruction_mov40): + +FN_PREFIX(CryptonightR_instruction_mov41): + +FN_PREFIX(CryptonightR_instruction_mov42): + +FN_PREFIX(CryptonightR_instruction_mov43): + +FN_PREFIX(CryptonightR_instruction_mov44): + +FN_PREFIX(CryptonightR_instruction_mov45): + mov ecx, esi +FN_PREFIX(CryptonightR_instruction_mov46): + mov ecx, esi +FN_PREFIX(CryptonightR_instruction_mov47): + +FN_PREFIX(CryptonightR_instruction_mov48): + +FN_PREFIX(CryptonightR_instruction_mov49): + +FN_PREFIX(CryptonightR_instruction_mov50): + +FN_PREFIX(CryptonightR_instruction_mov51): + +FN_PREFIX(CryptonightR_instruction_mov52): + +FN_PREFIX(CryptonightR_instruction_mov53): + mov ecx, esi +FN_PREFIX(CryptonightR_instruction_mov54): + mov ecx, esi +FN_PREFIX(CryptonightR_instruction_mov55): + +FN_PREFIX(CryptonightR_instruction_mov56): + +FN_PREFIX(CryptonightR_instruction_mov57): + +FN_PREFIX(CryptonightR_instruction_mov58): + +FN_PREFIX(CryptonightR_instruction_mov59): + +FN_PREFIX(CryptonightR_instruction_mov60): + +FN_PREFIX(CryptonightR_instruction_mov61): + mov ecx, esi +FN_PREFIX(CryptonightR_instruction_mov62): + mov ecx, esi +FN_PREFIX(CryptonightR_instruction_mov63): + +FN_PREFIX(CryptonightR_instruction_mov64): + +FN_PREFIX(CryptonightR_instruction_mov65): + +FN_PREFIX(CryptonightR_instruction_mov66): + +FN_PREFIX(CryptonightR_instruction_mov67): + +FN_PREFIX(CryptonightR_instruction_mov68): + +FN_PREFIX(CryptonightR_instruction_mov69): + mov ecx, edi +FN_PREFIX(CryptonightR_instruction_mov70): + mov ecx, edi +FN_PREFIX(CryptonightR_instruction_mov71): + +FN_PREFIX(CryptonightR_instruction_mov72): + +FN_PREFIX(CryptonightR_instruction_mov73): + +FN_PREFIX(CryptonightR_instruction_mov74): + +FN_PREFIX(CryptonightR_instruction_mov75): + +FN_PREFIX(CryptonightR_instruction_mov76): + +FN_PREFIX(CryptonightR_instruction_mov77): + mov ecx, edi +FN_PREFIX(CryptonightR_instruction_mov78): + mov ecx, edi +FN_PREFIX(CryptonightR_instruction_mov79): + +FN_PREFIX(CryptonightR_instruction_mov80): + +FN_PREFIX(CryptonightR_instruction_mov81): + +FN_PREFIX(CryptonightR_instruction_mov82): + +FN_PREFIX(CryptonightR_instruction_mov83): + +FN_PREFIX(CryptonightR_instruction_mov84): + +FN_PREFIX(CryptonightR_instruction_mov85): + mov ecx, edi +FN_PREFIX(CryptonightR_instruction_mov86): + mov ecx, edi +FN_PREFIX(CryptonightR_instruction_mov87): + +FN_PREFIX(CryptonightR_instruction_mov88): + +FN_PREFIX(CryptonightR_instruction_mov89): + +FN_PREFIX(CryptonightR_instruction_mov90): + +FN_PREFIX(CryptonightR_instruction_mov91): + +FN_PREFIX(CryptonightR_instruction_mov92): + +FN_PREFIX(CryptonightR_instruction_mov93): + mov ecx, edi +FN_PREFIX(CryptonightR_instruction_mov94): + mov ecx, edi +FN_PREFIX(CryptonightR_instruction_mov95): + +FN_PREFIX(CryptonightR_instruction_mov96): + +FN_PREFIX(CryptonightR_instruction_mov97): + +FN_PREFIX(CryptonightR_instruction_mov98): + +FN_PREFIX(CryptonightR_instruction_mov99): + +FN_PREFIX(CryptonightR_instruction_mov100): + +FN_PREFIX(CryptonightR_instruction_mov101): + mov ecx, ebp +FN_PREFIX(CryptonightR_instruction_mov102): + mov ecx, ebp +FN_PREFIX(CryptonightR_instruction_mov103): + +FN_PREFIX(CryptonightR_instruction_mov104): + +FN_PREFIX(CryptonightR_instruction_mov105): + +FN_PREFIX(CryptonightR_instruction_mov106): + +FN_PREFIX(CryptonightR_instruction_mov107): + +FN_PREFIX(CryptonightR_instruction_mov108): + +FN_PREFIX(CryptonightR_instruction_mov109): + mov ecx, ebp +FN_PREFIX(CryptonightR_instruction_mov110): + mov ecx, ebp +FN_PREFIX(CryptonightR_instruction_mov111): + +FN_PREFIX(CryptonightR_instruction_mov112): + +FN_PREFIX(CryptonightR_instruction_mov113): + +FN_PREFIX(CryptonightR_instruction_mov114): + +FN_PREFIX(CryptonightR_instruction_mov115): + +FN_PREFIX(CryptonightR_instruction_mov116): + +FN_PREFIX(CryptonightR_instruction_mov117): + mov ecx, ebp +FN_PREFIX(CryptonightR_instruction_mov118): + mov ecx, ebp +FN_PREFIX(CryptonightR_instruction_mov119): + +FN_PREFIX(CryptonightR_instruction_mov120): + +FN_PREFIX(CryptonightR_instruction_mov121): + +FN_PREFIX(CryptonightR_instruction_mov122): + +FN_PREFIX(CryptonightR_instruction_mov123): + +FN_PREFIX(CryptonightR_instruction_mov124): + +FN_PREFIX(CryptonightR_instruction_mov125): + mov ecx, ebp +FN_PREFIX(CryptonightR_instruction_mov126): + mov ecx, ebp +FN_PREFIX(CryptonightR_instruction_mov127): + +FN_PREFIX(CryptonightR_instruction_mov128): + +FN_PREFIX(CryptonightR_instruction_mov129): + +FN_PREFIX(CryptonightR_instruction_mov130): + +FN_PREFIX(CryptonightR_instruction_mov131): + +FN_PREFIX(CryptonightR_instruction_mov132): + +FN_PREFIX(CryptonightR_instruction_mov133): + mov ecx, esp +FN_PREFIX(CryptonightR_instruction_mov134): + mov ecx, esp +FN_PREFIX(CryptonightR_instruction_mov135): + +FN_PREFIX(CryptonightR_instruction_mov136): + +FN_PREFIX(CryptonightR_instruction_mov137): + +FN_PREFIX(CryptonightR_instruction_mov138): + +FN_PREFIX(CryptonightR_instruction_mov139): + +FN_PREFIX(CryptonightR_instruction_mov140): + +FN_PREFIX(CryptonightR_instruction_mov141): + mov ecx, esp +FN_PREFIX(CryptonightR_instruction_mov142): + mov ecx, esp +FN_PREFIX(CryptonightR_instruction_mov143): + +FN_PREFIX(CryptonightR_instruction_mov144): + +FN_PREFIX(CryptonightR_instruction_mov145): + +FN_PREFIX(CryptonightR_instruction_mov146): + +FN_PREFIX(CryptonightR_instruction_mov147): + +FN_PREFIX(CryptonightR_instruction_mov148): + +FN_PREFIX(CryptonightR_instruction_mov149): + mov ecx, esp +FN_PREFIX(CryptonightR_instruction_mov150): + mov ecx, esp +FN_PREFIX(CryptonightR_instruction_mov151): + +FN_PREFIX(CryptonightR_instruction_mov152): + +FN_PREFIX(CryptonightR_instruction_mov153): + +FN_PREFIX(CryptonightR_instruction_mov154): + +FN_PREFIX(CryptonightR_instruction_mov155): + +FN_PREFIX(CryptonightR_instruction_mov156): + +FN_PREFIX(CryptonightR_instruction_mov157): + mov ecx, esp +FN_PREFIX(CryptonightR_instruction_mov158): + mov ecx, esp +FN_PREFIX(CryptonightR_instruction_mov159): + +FN_PREFIX(CryptonightR_instruction_mov160): + +FN_PREFIX(CryptonightR_instruction_mov161): + +FN_PREFIX(CryptonightR_instruction_mov162): + +FN_PREFIX(CryptonightR_instruction_mov163): + +FN_PREFIX(CryptonightR_instruction_mov164): + +FN_PREFIX(CryptonightR_instruction_mov165): + mov ecx, r15d +FN_PREFIX(CryptonightR_instruction_mov166): + mov ecx, r15d +FN_PREFIX(CryptonightR_instruction_mov167): + +FN_PREFIX(CryptonightR_instruction_mov168): + +FN_PREFIX(CryptonightR_instruction_mov169): + +FN_PREFIX(CryptonightR_instruction_mov170): + +FN_PREFIX(CryptonightR_instruction_mov171): + +FN_PREFIX(CryptonightR_instruction_mov172): + +FN_PREFIX(CryptonightR_instruction_mov173): + mov ecx, r15d +FN_PREFIX(CryptonightR_instruction_mov174): + mov ecx, r15d +FN_PREFIX(CryptonightR_instruction_mov175): + +FN_PREFIX(CryptonightR_instruction_mov176): + +FN_PREFIX(CryptonightR_instruction_mov177): + +FN_PREFIX(CryptonightR_instruction_mov178): + +FN_PREFIX(CryptonightR_instruction_mov179): + +FN_PREFIX(CryptonightR_instruction_mov180): + +FN_PREFIX(CryptonightR_instruction_mov181): + mov ecx, r15d +FN_PREFIX(CryptonightR_instruction_mov182): + mov ecx, r15d +FN_PREFIX(CryptonightR_instruction_mov183): + +FN_PREFIX(CryptonightR_instruction_mov184): + +FN_PREFIX(CryptonightR_instruction_mov185): + +FN_PREFIX(CryptonightR_instruction_mov186): + +FN_PREFIX(CryptonightR_instruction_mov187): + +FN_PREFIX(CryptonightR_instruction_mov188): + +FN_PREFIX(CryptonightR_instruction_mov189): + mov ecx, r15d +FN_PREFIX(CryptonightR_instruction_mov190): + mov ecx, r15d +FN_PREFIX(CryptonightR_instruction_mov191): + +FN_PREFIX(CryptonightR_instruction_mov192): + +FN_PREFIX(CryptonightR_instruction_mov193): + +FN_PREFIX(CryptonightR_instruction_mov194): + +FN_PREFIX(CryptonightR_instruction_mov195): + +FN_PREFIX(CryptonightR_instruction_mov196): + +FN_PREFIX(CryptonightR_instruction_mov197): + mov ecx, eax +FN_PREFIX(CryptonightR_instruction_mov198): + mov ecx, eax +FN_PREFIX(CryptonightR_instruction_mov199): + +FN_PREFIX(CryptonightR_instruction_mov200): + +FN_PREFIX(CryptonightR_instruction_mov201): + +FN_PREFIX(CryptonightR_instruction_mov202): + +FN_PREFIX(CryptonightR_instruction_mov203): + +FN_PREFIX(CryptonightR_instruction_mov204): + +FN_PREFIX(CryptonightR_instruction_mov205): + mov ecx, eax +FN_PREFIX(CryptonightR_instruction_mov206): + mov ecx, eax +FN_PREFIX(CryptonightR_instruction_mov207): + +FN_PREFIX(CryptonightR_instruction_mov208): + +FN_PREFIX(CryptonightR_instruction_mov209): + +FN_PREFIX(CryptonightR_instruction_mov210): + +FN_PREFIX(CryptonightR_instruction_mov211): + +FN_PREFIX(CryptonightR_instruction_mov212): + +FN_PREFIX(CryptonightR_instruction_mov213): + mov ecx, eax +FN_PREFIX(CryptonightR_instruction_mov214): + mov ecx, eax +FN_PREFIX(CryptonightR_instruction_mov215): + +FN_PREFIX(CryptonightR_instruction_mov216): + +FN_PREFIX(CryptonightR_instruction_mov217): + +FN_PREFIX(CryptonightR_instruction_mov218): + +FN_PREFIX(CryptonightR_instruction_mov219): + +FN_PREFIX(CryptonightR_instruction_mov220): + +FN_PREFIX(CryptonightR_instruction_mov221): + mov ecx, eax +FN_PREFIX(CryptonightR_instruction_mov222): + mov ecx, eax +FN_PREFIX(CryptonightR_instruction_mov223): + +FN_PREFIX(CryptonightR_instruction_mov224): + +FN_PREFIX(CryptonightR_instruction_mov225): + +FN_PREFIX(CryptonightR_instruction_mov226): + +FN_PREFIX(CryptonightR_instruction_mov227): + +FN_PREFIX(CryptonightR_instruction_mov228): + +FN_PREFIX(CryptonightR_instruction_mov229): + mov ecx, edx +FN_PREFIX(CryptonightR_instruction_mov230): + mov ecx, edx +FN_PREFIX(CryptonightR_instruction_mov231): + +FN_PREFIX(CryptonightR_instruction_mov232): + +FN_PREFIX(CryptonightR_instruction_mov233): + +FN_PREFIX(CryptonightR_instruction_mov234): + +FN_PREFIX(CryptonightR_instruction_mov235): + +FN_PREFIX(CryptonightR_instruction_mov236): + +FN_PREFIX(CryptonightR_instruction_mov237): + mov ecx, edx +FN_PREFIX(CryptonightR_instruction_mov238): + mov ecx, edx +FN_PREFIX(CryptonightR_instruction_mov239): + +FN_PREFIX(CryptonightR_instruction_mov240): + +FN_PREFIX(CryptonightR_instruction_mov241): + +FN_PREFIX(CryptonightR_instruction_mov242): + +FN_PREFIX(CryptonightR_instruction_mov243): + +FN_PREFIX(CryptonightR_instruction_mov244): + +FN_PREFIX(CryptonightR_instruction_mov245): + mov ecx, edx +FN_PREFIX(CryptonightR_instruction_mov246): + mov ecx, edx +FN_PREFIX(CryptonightR_instruction_mov247): + +FN_PREFIX(CryptonightR_instruction_mov248): + +FN_PREFIX(CryptonightR_instruction_mov249): + +FN_PREFIX(CryptonightR_instruction_mov250): + +FN_PREFIX(CryptonightR_instruction_mov251): + +FN_PREFIX(CryptonightR_instruction_mov252): + +FN_PREFIX(CryptonightR_instruction_mov253): + mov ecx, edx +FN_PREFIX(CryptonightR_instruction_mov254): + mov ecx, edx +FN_PREFIX(CryptonightR_instruction_mov255): + +FN_PREFIX(CryptonightR_instruction_mov256): diff --git a/src/crypto/CryptonightR_template.h b/src/crypto/CryptonightR_template.h new file mode 100644 index 000000000..57eb92ebe --- /dev/null +++ b/src/crypto/CryptonightR_template.h @@ -0,0 +1,1039 @@ +#ifndef CRYPTONIGHTR_TEMPLATE_H +#define CRYPTONIGHTR_TEMPLATE_H + +void CryptonightR_instruction0(void); +void CryptonightR_instruction1(void); +void CryptonightR_instruction2(void); +void CryptonightR_instruction3(void); +void CryptonightR_instruction4(void); +void CryptonightR_instruction5(void); +void CryptonightR_instruction6(void); +void CryptonightR_instruction7(void); +void CryptonightR_instruction8(void); +void CryptonightR_instruction9(void); +void CryptonightR_instruction10(void); +void CryptonightR_instruction11(void); +void CryptonightR_instruction12(void); +void CryptonightR_instruction13(void); +void CryptonightR_instruction14(void); +void CryptonightR_instruction15(void); +void CryptonightR_instruction16(void); +void CryptonightR_instruction17(void); +void CryptonightR_instruction18(void); +void CryptonightR_instruction19(void); +void CryptonightR_instruction20(void); +void CryptonightR_instruction21(void); +void CryptonightR_instruction22(void); +void CryptonightR_instruction23(void); +void CryptonightR_instruction24(void); +void CryptonightR_instruction25(void); +void CryptonightR_instruction26(void); +void CryptonightR_instruction27(void); +void CryptonightR_instruction28(void); +void CryptonightR_instruction29(void); +void CryptonightR_instruction30(void); +void CryptonightR_instruction31(void); +void CryptonightR_instruction32(void); +void CryptonightR_instruction33(void); +void CryptonightR_instruction34(void); +void CryptonightR_instruction35(void); +void CryptonightR_instruction36(void); +void CryptonightR_instruction37(void); +void CryptonightR_instruction38(void); +void CryptonightR_instruction39(void); +void CryptonightR_instruction40(void); +void CryptonightR_instruction41(void); +void CryptonightR_instruction42(void); +void CryptonightR_instruction43(void); +void CryptonightR_instruction44(void); +void CryptonightR_instruction45(void); +void CryptonightR_instruction46(void); +void CryptonightR_instruction47(void); +void CryptonightR_instruction48(void); +void CryptonightR_instruction49(void); +void CryptonightR_instruction50(void); +void CryptonightR_instruction51(void); +void CryptonightR_instruction52(void); +void CryptonightR_instruction53(void); +void CryptonightR_instruction54(void); +void CryptonightR_instruction55(void); +void CryptonightR_instruction56(void); +void CryptonightR_instruction57(void); +void CryptonightR_instruction58(void); +void CryptonightR_instruction59(void); +void CryptonightR_instruction60(void); +void CryptonightR_instruction61(void); +void CryptonightR_instruction62(void); +void CryptonightR_instruction63(void); +void CryptonightR_instruction64(void); +void CryptonightR_instruction65(void); +void CryptonightR_instruction66(void); +void CryptonightR_instruction67(void); +void CryptonightR_instruction68(void); +void CryptonightR_instruction69(void); +void CryptonightR_instruction70(void); +void CryptonightR_instruction71(void); +void CryptonightR_instruction72(void); +void CryptonightR_instruction73(void); +void CryptonightR_instruction74(void); +void CryptonightR_instruction75(void); +void CryptonightR_instruction76(void); +void CryptonightR_instruction77(void); +void CryptonightR_instruction78(void); +void CryptonightR_instruction79(void); +void CryptonightR_instruction80(void); +void CryptonightR_instruction81(void); +void CryptonightR_instruction82(void); +void CryptonightR_instruction83(void); +void CryptonightR_instruction84(void); +void CryptonightR_instruction85(void); +void CryptonightR_instruction86(void); +void CryptonightR_instruction87(void); +void CryptonightR_instruction88(void); +void CryptonightR_instruction89(void); +void CryptonightR_instruction90(void); +void CryptonightR_instruction91(void); +void CryptonightR_instruction92(void); +void CryptonightR_instruction93(void); +void CryptonightR_instruction94(void); +void CryptonightR_instruction95(void); +void CryptonightR_instruction96(void); +void CryptonightR_instruction97(void); +void CryptonightR_instruction98(void); +void CryptonightR_instruction99(void); +void CryptonightR_instruction100(void); +void CryptonightR_instruction101(void); +void CryptonightR_instruction102(void); +void CryptonightR_instruction103(void); +void CryptonightR_instruction104(void); +void CryptonightR_instruction105(void); +void CryptonightR_instruction106(void); +void CryptonightR_instruction107(void); +void CryptonightR_instruction108(void); +void CryptonightR_instruction109(void); +void CryptonightR_instruction110(void); +void CryptonightR_instruction111(void); +void CryptonightR_instruction112(void); +void CryptonightR_instruction113(void); +void CryptonightR_instruction114(void); +void CryptonightR_instruction115(void); +void CryptonightR_instruction116(void); +void CryptonightR_instruction117(void); +void CryptonightR_instruction118(void); +void CryptonightR_instruction119(void); +void CryptonightR_instruction120(void); +void CryptonightR_instruction121(void); +void CryptonightR_instruction122(void); +void CryptonightR_instruction123(void); +void CryptonightR_instruction124(void); +void CryptonightR_instruction125(void); +void CryptonightR_instruction126(void); +void CryptonightR_instruction127(void); +void CryptonightR_instruction128(void); +void CryptonightR_instruction129(void); +void CryptonightR_instruction130(void); +void CryptonightR_instruction131(void); +void CryptonightR_instruction132(void); +void CryptonightR_instruction133(void); +void CryptonightR_instruction134(void); +void CryptonightR_instruction135(void); +void CryptonightR_instruction136(void); +void CryptonightR_instruction137(void); +void CryptonightR_instruction138(void); +void CryptonightR_instruction139(void); +void CryptonightR_instruction140(void); +void CryptonightR_instruction141(void); +void CryptonightR_instruction142(void); +void CryptonightR_instruction143(void); +void CryptonightR_instruction144(void); +void CryptonightR_instruction145(void); +void CryptonightR_instruction146(void); +void CryptonightR_instruction147(void); +void CryptonightR_instruction148(void); +void CryptonightR_instruction149(void); +void CryptonightR_instruction150(void); +void CryptonightR_instruction151(void); +void CryptonightR_instruction152(void); +void CryptonightR_instruction153(void); +void CryptonightR_instruction154(void); +void CryptonightR_instruction155(void); +void CryptonightR_instruction156(void); +void CryptonightR_instruction157(void); +void CryptonightR_instruction158(void); +void CryptonightR_instruction159(void); +void CryptonightR_instruction160(void); +void CryptonightR_instruction161(void); +void CryptonightR_instruction162(void); +void CryptonightR_instruction163(void); +void CryptonightR_instruction164(void); +void CryptonightR_instruction165(void); +void CryptonightR_instruction166(void); +void CryptonightR_instruction167(void); +void CryptonightR_instruction168(void); +void CryptonightR_instruction169(void); +void CryptonightR_instruction170(void); +void CryptonightR_instruction171(void); +void CryptonightR_instruction172(void); +void CryptonightR_instruction173(void); +void CryptonightR_instruction174(void); +void CryptonightR_instruction175(void); +void CryptonightR_instruction176(void); +void CryptonightR_instruction177(void); +void CryptonightR_instruction178(void); +void CryptonightR_instruction179(void); +void CryptonightR_instruction180(void); +void CryptonightR_instruction181(void); +void CryptonightR_instruction182(void); +void CryptonightR_instruction183(void); +void CryptonightR_instruction184(void); +void CryptonightR_instruction185(void); +void CryptonightR_instruction186(void); +void CryptonightR_instruction187(void); +void CryptonightR_instruction188(void); +void CryptonightR_instruction189(void); +void CryptonightR_instruction190(void); +void CryptonightR_instruction191(void); +void CryptonightR_instruction192(void); +void CryptonightR_instruction193(void); +void CryptonightR_instruction194(void); +void CryptonightR_instruction195(void); +void CryptonightR_instruction196(void); +void CryptonightR_instruction197(void); +void CryptonightR_instruction198(void); +void CryptonightR_instruction199(void); +void CryptonightR_instruction200(void); +void CryptonightR_instruction201(void); +void CryptonightR_instruction202(void); +void CryptonightR_instruction203(void); +void CryptonightR_instruction204(void); +void CryptonightR_instruction205(void); +void CryptonightR_instruction206(void); +void CryptonightR_instruction207(void); +void CryptonightR_instruction208(void); +void CryptonightR_instruction209(void); +void CryptonightR_instruction210(void); +void CryptonightR_instruction211(void); +void CryptonightR_instruction212(void); +void CryptonightR_instruction213(void); +void CryptonightR_instruction214(void); +void CryptonightR_instruction215(void); +void CryptonightR_instruction216(void); +void CryptonightR_instruction217(void); +void CryptonightR_instruction218(void); +void CryptonightR_instruction219(void); +void CryptonightR_instruction220(void); +void CryptonightR_instruction221(void); +void CryptonightR_instruction222(void); +void CryptonightR_instruction223(void); +void CryptonightR_instruction224(void); +void CryptonightR_instruction225(void); +void CryptonightR_instruction226(void); +void CryptonightR_instruction227(void); +void CryptonightR_instruction228(void); +void CryptonightR_instruction229(void); +void CryptonightR_instruction230(void); +void CryptonightR_instruction231(void); +void CryptonightR_instruction232(void); +void CryptonightR_instruction233(void); +void CryptonightR_instruction234(void); +void CryptonightR_instruction235(void); +void CryptonightR_instruction236(void); +void CryptonightR_instruction237(void); +void CryptonightR_instruction238(void); +void CryptonightR_instruction239(void); +void CryptonightR_instruction240(void); +void CryptonightR_instruction241(void); +void CryptonightR_instruction242(void); +void CryptonightR_instruction243(void); +void CryptonightR_instruction244(void); +void CryptonightR_instruction245(void); +void CryptonightR_instruction246(void); +void CryptonightR_instruction247(void); +void CryptonightR_instruction248(void); +void CryptonightR_instruction249(void); +void CryptonightR_instruction250(void); +void CryptonightR_instruction251(void); +void CryptonightR_instruction252(void); +void CryptonightR_instruction253(void); +void CryptonightR_instruction254(void); +void CryptonightR_instruction255(void); +void CryptonightR_instruction256(void); +void CryptonightR_instruction_mov0(void); +void CryptonightR_instruction_mov1(void); +void CryptonightR_instruction_mov2(void); +void CryptonightR_instruction_mov3(void); +void CryptonightR_instruction_mov4(void); +void CryptonightR_instruction_mov5(void); +void CryptonightR_instruction_mov6(void); +void CryptonightR_instruction_mov7(void); +void CryptonightR_instruction_mov8(void); +void CryptonightR_instruction_mov9(void); +void CryptonightR_instruction_mov10(void); +void CryptonightR_instruction_mov11(void); +void CryptonightR_instruction_mov12(void); +void CryptonightR_instruction_mov13(void); +void CryptonightR_instruction_mov14(void); +void CryptonightR_instruction_mov15(void); +void CryptonightR_instruction_mov16(void); +void CryptonightR_instruction_mov17(void); +void CryptonightR_instruction_mov18(void); +void CryptonightR_instruction_mov19(void); +void CryptonightR_instruction_mov20(void); +void CryptonightR_instruction_mov21(void); +void CryptonightR_instruction_mov22(void); +void CryptonightR_instruction_mov23(void); +void CryptonightR_instruction_mov24(void); +void CryptonightR_instruction_mov25(void); +void CryptonightR_instruction_mov26(void); +void CryptonightR_instruction_mov27(void); +void CryptonightR_instruction_mov28(void); +void CryptonightR_instruction_mov29(void); +void CryptonightR_instruction_mov30(void); +void CryptonightR_instruction_mov31(void); +void CryptonightR_instruction_mov32(void); +void CryptonightR_instruction_mov33(void); +void CryptonightR_instruction_mov34(void); +void CryptonightR_instruction_mov35(void); +void CryptonightR_instruction_mov36(void); +void CryptonightR_instruction_mov37(void); +void CryptonightR_instruction_mov38(void); +void CryptonightR_instruction_mov39(void); +void CryptonightR_instruction_mov40(void); +void CryptonightR_instruction_mov41(void); +void CryptonightR_instruction_mov42(void); +void CryptonightR_instruction_mov43(void); +void CryptonightR_instruction_mov44(void); +void CryptonightR_instruction_mov45(void); +void CryptonightR_instruction_mov46(void); +void CryptonightR_instruction_mov47(void); +void CryptonightR_instruction_mov48(void); +void CryptonightR_instruction_mov49(void); +void CryptonightR_instruction_mov50(void); +void CryptonightR_instruction_mov51(void); +void CryptonightR_instruction_mov52(void); +void CryptonightR_instruction_mov53(void); +void CryptonightR_instruction_mov54(void); +void CryptonightR_instruction_mov55(void); +void CryptonightR_instruction_mov56(void); +void CryptonightR_instruction_mov57(void); +void CryptonightR_instruction_mov58(void); +void CryptonightR_instruction_mov59(void); +void CryptonightR_instruction_mov60(void); +void CryptonightR_instruction_mov61(void); +void CryptonightR_instruction_mov62(void); +void CryptonightR_instruction_mov63(void); +void CryptonightR_instruction_mov64(void); +void CryptonightR_instruction_mov65(void); +void CryptonightR_instruction_mov66(void); +void CryptonightR_instruction_mov67(void); +void CryptonightR_instruction_mov68(void); +void CryptonightR_instruction_mov69(void); +void CryptonightR_instruction_mov70(void); +void CryptonightR_instruction_mov71(void); +void CryptonightR_instruction_mov72(void); +void CryptonightR_instruction_mov73(void); +void CryptonightR_instruction_mov74(void); +void CryptonightR_instruction_mov75(void); +void CryptonightR_instruction_mov76(void); +void CryptonightR_instruction_mov77(void); +void CryptonightR_instruction_mov78(void); +void CryptonightR_instruction_mov79(void); +void CryptonightR_instruction_mov80(void); +void CryptonightR_instruction_mov81(void); +void CryptonightR_instruction_mov82(void); +void CryptonightR_instruction_mov83(void); +void CryptonightR_instruction_mov84(void); +void CryptonightR_instruction_mov85(void); +void CryptonightR_instruction_mov86(void); +void CryptonightR_instruction_mov87(void); +void CryptonightR_instruction_mov88(void); +void CryptonightR_instruction_mov89(void); +void CryptonightR_instruction_mov90(void); +void CryptonightR_instruction_mov91(void); +void CryptonightR_instruction_mov92(void); +void CryptonightR_instruction_mov93(void); +void CryptonightR_instruction_mov94(void); +void CryptonightR_instruction_mov95(void); +void CryptonightR_instruction_mov96(void); +void CryptonightR_instruction_mov97(void); +void CryptonightR_instruction_mov98(void); +void CryptonightR_instruction_mov99(void); +void CryptonightR_instruction_mov100(void); +void CryptonightR_instruction_mov101(void); +void CryptonightR_instruction_mov102(void); +void CryptonightR_instruction_mov103(void); +void CryptonightR_instruction_mov104(void); +void CryptonightR_instruction_mov105(void); +void CryptonightR_instruction_mov106(void); +void CryptonightR_instruction_mov107(void); +void CryptonightR_instruction_mov108(void); +void CryptonightR_instruction_mov109(void); +void CryptonightR_instruction_mov110(void); +void CryptonightR_instruction_mov111(void); +void CryptonightR_instruction_mov112(void); +void CryptonightR_instruction_mov113(void); +void CryptonightR_instruction_mov114(void); +void CryptonightR_instruction_mov115(void); +void CryptonightR_instruction_mov116(void); +void CryptonightR_instruction_mov117(void); +void CryptonightR_instruction_mov118(void); +void CryptonightR_instruction_mov119(void); +void CryptonightR_instruction_mov120(void); +void CryptonightR_instruction_mov121(void); +void CryptonightR_instruction_mov122(void); +void CryptonightR_instruction_mov123(void); +void CryptonightR_instruction_mov124(void); +void CryptonightR_instruction_mov125(void); +void CryptonightR_instruction_mov126(void); +void CryptonightR_instruction_mov127(void); +void CryptonightR_instruction_mov128(void); +void CryptonightR_instruction_mov129(void); +void CryptonightR_instruction_mov130(void); +void CryptonightR_instruction_mov131(void); +void CryptonightR_instruction_mov132(void); +void CryptonightR_instruction_mov133(void); +void CryptonightR_instruction_mov134(void); +void CryptonightR_instruction_mov135(void); +void CryptonightR_instruction_mov136(void); +void CryptonightR_instruction_mov137(void); +void CryptonightR_instruction_mov138(void); +void CryptonightR_instruction_mov139(void); +void CryptonightR_instruction_mov140(void); +void CryptonightR_instruction_mov141(void); +void CryptonightR_instruction_mov142(void); +void CryptonightR_instruction_mov143(void); +void CryptonightR_instruction_mov144(void); +void CryptonightR_instruction_mov145(void); +void CryptonightR_instruction_mov146(void); +void CryptonightR_instruction_mov147(void); +void CryptonightR_instruction_mov148(void); +void CryptonightR_instruction_mov149(void); +void CryptonightR_instruction_mov150(void); +void CryptonightR_instruction_mov151(void); +void CryptonightR_instruction_mov152(void); +void CryptonightR_instruction_mov153(void); +void CryptonightR_instruction_mov154(void); +void CryptonightR_instruction_mov155(void); +void CryptonightR_instruction_mov156(void); +void CryptonightR_instruction_mov157(void); +void CryptonightR_instruction_mov158(void); +void CryptonightR_instruction_mov159(void); +void CryptonightR_instruction_mov160(void); +void CryptonightR_instruction_mov161(void); +void CryptonightR_instruction_mov162(void); +void CryptonightR_instruction_mov163(void); +void CryptonightR_instruction_mov164(void); +void CryptonightR_instruction_mov165(void); +void CryptonightR_instruction_mov166(void); +void CryptonightR_instruction_mov167(void); +void CryptonightR_instruction_mov168(void); +void CryptonightR_instruction_mov169(void); +void CryptonightR_instruction_mov170(void); +void CryptonightR_instruction_mov171(void); +void CryptonightR_instruction_mov172(void); +void CryptonightR_instruction_mov173(void); +void CryptonightR_instruction_mov174(void); +void CryptonightR_instruction_mov175(void); +void CryptonightR_instruction_mov176(void); +void CryptonightR_instruction_mov177(void); +void CryptonightR_instruction_mov178(void); +void CryptonightR_instruction_mov179(void); +void CryptonightR_instruction_mov180(void); +void CryptonightR_instruction_mov181(void); +void CryptonightR_instruction_mov182(void); +void CryptonightR_instruction_mov183(void); +void CryptonightR_instruction_mov184(void); +void CryptonightR_instruction_mov185(void); +void CryptonightR_instruction_mov186(void); +void CryptonightR_instruction_mov187(void); +void CryptonightR_instruction_mov188(void); +void CryptonightR_instruction_mov189(void); +void CryptonightR_instruction_mov190(void); +void CryptonightR_instruction_mov191(void); +void CryptonightR_instruction_mov192(void); +void CryptonightR_instruction_mov193(void); +void CryptonightR_instruction_mov194(void); +void CryptonightR_instruction_mov195(void); +void CryptonightR_instruction_mov196(void); +void CryptonightR_instruction_mov197(void); +void CryptonightR_instruction_mov198(void); +void CryptonightR_instruction_mov199(void); +void CryptonightR_instruction_mov200(void); +void CryptonightR_instruction_mov201(void); +void CryptonightR_instruction_mov202(void); +void CryptonightR_instruction_mov203(void); +void CryptonightR_instruction_mov204(void); +void CryptonightR_instruction_mov205(void); +void CryptonightR_instruction_mov206(void); +void CryptonightR_instruction_mov207(void); +void CryptonightR_instruction_mov208(void); +void CryptonightR_instruction_mov209(void); +void CryptonightR_instruction_mov210(void); +void CryptonightR_instruction_mov211(void); +void CryptonightR_instruction_mov212(void); +void CryptonightR_instruction_mov213(void); +void CryptonightR_instruction_mov214(void); +void CryptonightR_instruction_mov215(void); +void CryptonightR_instruction_mov216(void); +void CryptonightR_instruction_mov217(void); +void CryptonightR_instruction_mov218(void); +void CryptonightR_instruction_mov219(void); +void CryptonightR_instruction_mov220(void); +void CryptonightR_instruction_mov221(void); +void CryptonightR_instruction_mov222(void); +void CryptonightR_instruction_mov223(void); +void CryptonightR_instruction_mov224(void); +void CryptonightR_instruction_mov225(void); +void CryptonightR_instruction_mov226(void); +void CryptonightR_instruction_mov227(void); +void CryptonightR_instruction_mov228(void); +void CryptonightR_instruction_mov229(void); +void CryptonightR_instruction_mov230(void); +void CryptonightR_instruction_mov231(void); +void CryptonightR_instruction_mov232(void); +void CryptonightR_instruction_mov233(void); +void CryptonightR_instruction_mov234(void); +void CryptonightR_instruction_mov235(void); +void CryptonightR_instruction_mov236(void); +void CryptonightR_instruction_mov237(void); +void CryptonightR_instruction_mov238(void); +void CryptonightR_instruction_mov239(void); +void CryptonightR_instruction_mov240(void); +void CryptonightR_instruction_mov241(void); +void CryptonightR_instruction_mov242(void); +void CryptonightR_instruction_mov243(void); +void CryptonightR_instruction_mov244(void); +void CryptonightR_instruction_mov245(void); +void CryptonightR_instruction_mov246(void); +void CryptonightR_instruction_mov247(void); +void CryptonightR_instruction_mov248(void); +void CryptonightR_instruction_mov249(void); +void CryptonightR_instruction_mov250(void); +void CryptonightR_instruction_mov251(void); +void CryptonightR_instruction_mov252(void); +void CryptonightR_instruction_mov253(void); +void CryptonightR_instruction_mov254(void); +void CryptonightR_instruction_mov255(void); +void CryptonightR_instruction_mov256(void); + +const void* instructions[257] = { + CryptonightR_instruction0, + CryptonightR_instruction1, + CryptonightR_instruction2, + CryptonightR_instruction3, + CryptonightR_instruction4, + CryptonightR_instruction5, + CryptonightR_instruction6, + CryptonightR_instruction7, + CryptonightR_instruction8, + CryptonightR_instruction9, + CryptonightR_instruction10, + CryptonightR_instruction11, + CryptonightR_instruction12, + CryptonightR_instruction13, + CryptonightR_instruction14, + CryptonightR_instruction15, + CryptonightR_instruction16, + CryptonightR_instruction17, + CryptonightR_instruction18, + CryptonightR_instruction19, + CryptonightR_instruction20, + CryptonightR_instruction21, + CryptonightR_instruction22, + CryptonightR_instruction23, + CryptonightR_instruction24, + CryptonightR_instruction25, + CryptonightR_instruction26, + CryptonightR_instruction27, + CryptonightR_instruction28, + CryptonightR_instruction29, + CryptonightR_instruction30, + CryptonightR_instruction31, + CryptonightR_instruction32, + CryptonightR_instruction33, + CryptonightR_instruction34, + CryptonightR_instruction35, + CryptonightR_instruction36, + CryptonightR_instruction37, + CryptonightR_instruction38, + CryptonightR_instruction39, + CryptonightR_instruction40, + CryptonightR_instruction41, + CryptonightR_instruction42, + CryptonightR_instruction43, + CryptonightR_instruction44, + CryptonightR_instruction45, + CryptonightR_instruction46, + CryptonightR_instruction47, + CryptonightR_instruction48, + CryptonightR_instruction49, + CryptonightR_instruction50, + CryptonightR_instruction51, + CryptonightR_instruction52, + CryptonightR_instruction53, + CryptonightR_instruction54, + CryptonightR_instruction55, + CryptonightR_instruction56, + CryptonightR_instruction57, + CryptonightR_instruction58, + CryptonightR_instruction59, + CryptonightR_instruction60, + CryptonightR_instruction61, + CryptonightR_instruction62, + CryptonightR_instruction63, + CryptonightR_instruction64, + CryptonightR_instruction65, + CryptonightR_instruction66, + CryptonightR_instruction67, + CryptonightR_instruction68, + CryptonightR_instruction69, + CryptonightR_instruction70, + CryptonightR_instruction71, + CryptonightR_instruction72, + CryptonightR_instruction73, + CryptonightR_instruction74, + CryptonightR_instruction75, + CryptonightR_instruction76, + CryptonightR_instruction77, + CryptonightR_instruction78, + CryptonightR_instruction79, + CryptonightR_instruction80, + CryptonightR_instruction81, + CryptonightR_instruction82, + CryptonightR_instruction83, + CryptonightR_instruction84, + CryptonightR_instruction85, + CryptonightR_instruction86, + CryptonightR_instruction87, + CryptonightR_instruction88, + CryptonightR_instruction89, + CryptonightR_instruction90, + CryptonightR_instruction91, + CryptonightR_instruction92, + CryptonightR_instruction93, + CryptonightR_instruction94, + CryptonightR_instruction95, + CryptonightR_instruction96, + CryptonightR_instruction97, + CryptonightR_instruction98, + CryptonightR_instruction99, + CryptonightR_instruction100, + CryptonightR_instruction101, + CryptonightR_instruction102, + CryptonightR_instruction103, + CryptonightR_instruction104, + CryptonightR_instruction105, + CryptonightR_instruction106, + CryptonightR_instruction107, + CryptonightR_instruction108, + CryptonightR_instruction109, + CryptonightR_instruction110, + CryptonightR_instruction111, + CryptonightR_instruction112, + CryptonightR_instruction113, + CryptonightR_instruction114, + CryptonightR_instruction115, + CryptonightR_instruction116, + CryptonightR_instruction117, + CryptonightR_instruction118, + CryptonightR_instruction119, + CryptonightR_instruction120, + CryptonightR_instruction121, + CryptonightR_instruction122, + CryptonightR_instruction123, + CryptonightR_instruction124, + CryptonightR_instruction125, + CryptonightR_instruction126, + CryptonightR_instruction127, + CryptonightR_instruction128, + CryptonightR_instruction129, + CryptonightR_instruction130, + CryptonightR_instruction131, + CryptonightR_instruction132, + CryptonightR_instruction133, + CryptonightR_instruction134, + CryptonightR_instruction135, + CryptonightR_instruction136, + CryptonightR_instruction137, + CryptonightR_instruction138, + CryptonightR_instruction139, + CryptonightR_instruction140, + CryptonightR_instruction141, + CryptonightR_instruction142, + CryptonightR_instruction143, + CryptonightR_instruction144, + CryptonightR_instruction145, + CryptonightR_instruction146, + CryptonightR_instruction147, + CryptonightR_instruction148, + CryptonightR_instruction149, + CryptonightR_instruction150, + CryptonightR_instruction151, + CryptonightR_instruction152, + CryptonightR_instruction153, + CryptonightR_instruction154, + CryptonightR_instruction155, + CryptonightR_instruction156, + CryptonightR_instruction157, + CryptonightR_instruction158, + CryptonightR_instruction159, + CryptonightR_instruction160, + CryptonightR_instruction161, + CryptonightR_instruction162, + CryptonightR_instruction163, + CryptonightR_instruction164, + CryptonightR_instruction165, + CryptonightR_instruction166, + CryptonightR_instruction167, + CryptonightR_instruction168, + CryptonightR_instruction169, + CryptonightR_instruction170, + CryptonightR_instruction171, + CryptonightR_instruction172, + CryptonightR_instruction173, + CryptonightR_instruction174, + CryptonightR_instruction175, + CryptonightR_instruction176, + CryptonightR_instruction177, + CryptonightR_instruction178, + CryptonightR_instruction179, + CryptonightR_instruction180, + CryptonightR_instruction181, + CryptonightR_instruction182, + CryptonightR_instruction183, + CryptonightR_instruction184, + CryptonightR_instruction185, + CryptonightR_instruction186, + CryptonightR_instruction187, + CryptonightR_instruction188, + CryptonightR_instruction189, + CryptonightR_instruction190, + CryptonightR_instruction191, + CryptonightR_instruction192, + CryptonightR_instruction193, + CryptonightR_instruction194, + CryptonightR_instruction195, + CryptonightR_instruction196, + CryptonightR_instruction197, + CryptonightR_instruction198, + CryptonightR_instruction199, + CryptonightR_instruction200, + CryptonightR_instruction201, + CryptonightR_instruction202, + CryptonightR_instruction203, + CryptonightR_instruction204, + CryptonightR_instruction205, + CryptonightR_instruction206, + CryptonightR_instruction207, + CryptonightR_instruction208, + CryptonightR_instruction209, + CryptonightR_instruction210, + CryptonightR_instruction211, + CryptonightR_instruction212, + CryptonightR_instruction213, + CryptonightR_instruction214, + CryptonightR_instruction215, + CryptonightR_instruction216, + CryptonightR_instruction217, + CryptonightR_instruction218, + CryptonightR_instruction219, + CryptonightR_instruction220, + CryptonightR_instruction221, + CryptonightR_instruction222, + CryptonightR_instruction223, + CryptonightR_instruction224, + CryptonightR_instruction225, + CryptonightR_instruction226, + CryptonightR_instruction227, + CryptonightR_instruction228, + CryptonightR_instruction229, + CryptonightR_instruction230, + CryptonightR_instruction231, + CryptonightR_instruction232, + CryptonightR_instruction233, + CryptonightR_instruction234, + CryptonightR_instruction235, + CryptonightR_instruction236, + CryptonightR_instruction237, + CryptonightR_instruction238, + CryptonightR_instruction239, + CryptonightR_instruction240, + CryptonightR_instruction241, + CryptonightR_instruction242, + CryptonightR_instruction243, + CryptonightR_instruction244, + CryptonightR_instruction245, + CryptonightR_instruction246, + CryptonightR_instruction247, + CryptonightR_instruction248, + CryptonightR_instruction249, + CryptonightR_instruction250, + CryptonightR_instruction251, + CryptonightR_instruction252, + CryptonightR_instruction253, + CryptonightR_instruction254, + CryptonightR_instruction255, + CryptonightR_instruction256, +}; + +const void* instructions_mov[257] = { + CryptonightR_instruction_mov0, + CryptonightR_instruction_mov1, + CryptonightR_instruction_mov2, + CryptonightR_instruction_mov3, + CryptonightR_instruction_mov4, + CryptonightR_instruction_mov5, + CryptonightR_instruction_mov6, + CryptonightR_instruction_mov7, + CryptonightR_instruction_mov8, + CryptonightR_instruction_mov9, + CryptonightR_instruction_mov10, + CryptonightR_instruction_mov11, + CryptonightR_instruction_mov12, + CryptonightR_instruction_mov13, + CryptonightR_instruction_mov14, + CryptonightR_instruction_mov15, + CryptonightR_instruction_mov16, + CryptonightR_instruction_mov17, + CryptonightR_instruction_mov18, + CryptonightR_instruction_mov19, + CryptonightR_instruction_mov20, + CryptonightR_instruction_mov21, + CryptonightR_instruction_mov22, + CryptonightR_instruction_mov23, + CryptonightR_instruction_mov24, + CryptonightR_instruction_mov25, + CryptonightR_instruction_mov26, + CryptonightR_instruction_mov27, + CryptonightR_instruction_mov28, + CryptonightR_instruction_mov29, + CryptonightR_instruction_mov30, + CryptonightR_instruction_mov31, + CryptonightR_instruction_mov32, + CryptonightR_instruction_mov33, + CryptonightR_instruction_mov34, + CryptonightR_instruction_mov35, + CryptonightR_instruction_mov36, + CryptonightR_instruction_mov37, + CryptonightR_instruction_mov38, + CryptonightR_instruction_mov39, + CryptonightR_instruction_mov40, + CryptonightR_instruction_mov41, + CryptonightR_instruction_mov42, + CryptonightR_instruction_mov43, + CryptonightR_instruction_mov44, + CryptonightR_instruction_mov45, + CryptonightR_instruction_mov46, + CryptonightR_instruction_mov47, + CryptonightR_instruction_mov48, + CryptonightR_instruction_mov49, + CryptonightR_instruction_mov50, + CryptonightR_instruction_mov51, + CryptonightR_instruction_mov52, + CryptonightR_instruction_mov53, + CryptonightR_instruction_mov54, + CryptonightR_instruction_mov55, + CryptonightR_instruction_mov56, + CryptonightR_instruction_mov57, + CryptonightR_instruction_mov58, + CryptonightR_instruction_mov59, + CryptonightR_instruction_mov60, + CryptonightR_instruction_mov61, + CryptonightR_instruction_mov62, + CryptonightR_instruction_mov63, + CryptonightR_instruction_mov64, + CryptonightR_instruction_mov65, + CryptonightR_instruction_mov66, + CryptonightR_instruction_mov67, + CryptonightR_instruction_mov68, + CryptonightR_instruction_mov69, + CryptonightR_instruction_mov70, + CryptonightR_instruction_mov71, + CryptonightR_instruction_mov72, + CryptonightR_instruction_mov73, + CryptonightR_instruction_mov74, + CryptonightR_instruction_mov75, + CryptonightR_instruction_mov76, + CryptonightR_instruction_mov77, + CryptonightR_instruction_mov78, + CryptonightR_instruction_mov79, + CryptonightR_instruction_mov80, + CryptonightR_instruction_mov81, + CryptonightR_instruction_mov82, + CryptonightR_instruction_mov83, + CryptonightR_instruction_mov84, + CryptonightR_instruction_mov85, + CryptonightR_instruction_mov86, + CryptonightR_instruction_mov87, + CryptonightR_instruction_mov88, + CryptonightR_instruction_mov89, + CryptonightR_instruction_mov90, + CryptonightR_instruction_mov91, + CryptonightR_instruction_mov92, + CryptonightR_instruction_mov93, + CryptonightR_instruction_mov94, + CryptonightR_instruction_mov95, + CryptonightR_instruction_mov96, + CryptonightR_instruction_mov97, + CryptonightR_instruction_mov98, + CryptonightR_instruction_mov99, + CryptonightR_instruction_mov100, + CryptonightR_instruction_mov101, + CryptonightR_instruction_mov102, + CryptonightR_instruction_mov103, + CryptonightR_instruction_mov104, + CryptonightR_instruction_mov105, + CryptonightR_instruction_mov106, + CryptonightR_instruction_mov107, + CryptonightR_instruction_mov108, + CryptonightR_instruction_mov109, + CryptonightR_instruction_mov110, + CryptonightR_instruction_mov111, + CryptonightR_instruction_mov112, + CryptonightR_instruction_mov113, + CryptonightR_instruction_mov114, + CryptonightR_instruction_mov115, + CryptonightR_instruction_mov116, + CryptonightR_instruction_mov117, + CryptonightR_instruction_mov118, + CryptonightR_instruction_mov119, + CryptonightR_instruction_mov120, + CryptonightR_instruction_mov121, + CryptonightR_instruction_mov122, + CryptonightR_instruction_mov123, + CryptonightR_instruction_mov124, + CryptonightR_instruction_mov125, + CryptonightR_instruction_mov126, + CryptonightR_instruction_mov127, + CryptonightR_instruction_mov128, + CryptonightR_instruction_mov129, + CryptonightR_instruction_mov130, + CryptonightR_instruction_mov131, + CryptonightR_instruction_mov132, + CryptonightR_instruction_mov133, + CryptonightR_instruction_mov134, + CryptonightR_instruction_mov135, + CryptonightR_instruction_mov136, + CryptonightR_instruction_mov137, + CryptonightR_instruction_mov138, + CryptonightR_instruction_mov139, + CryptonightR_instruction_mov140, + CryptonightR_instruction_mov141, + CryptonightR_instruction_mov142, + CryptonightR_instruction_mov143, + CryptonightR_instruction_mov144, + CryptonightR_instruction_mov145, + CryptonightR_instruction_mov146, + CryptonightR_instruction_mov147, + CryptonightR_instruction_mov148, + CryptonightR_instruction_mov149, + CryptonightR_instruction_mov150, + CryptonightR_instruction_mov151, + CryptonightR_instruction_mov152, + CryptonightR_instruction_mov153, + CryptonightR_instruction_mov154, + CryptonightR_instruction_mov155, + CryptonightR_instruction_mov156, + CryptonightR_instruction_mov157, + CryptonightR_instruction_mov158, + CryptonightR_instruction_mov159, + CryptonightR_instruction_mov160, + CryptonightR_instruction_mov161, + CryptonightR_instruction_mov162, + CryptonightR_instruction_mov163, + CryptonightR_instruction_mov164, + CryptonightR_instruction_mov165, + CryptonightR_instruction_mov166, + CryptonightR_instruction_mov167, + CryptonightR_instruction_mov168, + CryptonightR_instruction_mov169, + CryptonightR_instruction_mov170, + CryptonightR_instruction_mov171, + CryptonightR_instruction_mov172, + CryptonightR_instruction_mov173, + CryptonightR_instruction_mov174, + CryptonightR_instruction_mov175, + CryptonightR_instruction_mov176, + CryptonightR_instruction_mov177, + CryptonightR_instruction_mov178, + CryptonightR_instruction_mov179, + CryptonightR_instruction_mov180, + CryptonightR_instruction_mov181, + CryptonightR_instruction_mov182, + CryptonightR_instruction_mov183, + CryptonightR_instruction_mov184, + CryptonightR_instruction_mov185, + CryptonightR_instruction_mov186, + CryptonightR_instruction_mov187, + CryptonightR_instruction_mov188, + CryptonightR_instruction_mov189, + CryptonightR_instruction_mov190, + CryptonightR_instruction_mov191, + CryptonightR_instruction_mov192, + CryptonightR_instruction_mov193, + CryptonightR_instruction_mov194, + CryptonightR_instruction_mov195, + CryptonightR_instruction_mov196, + CryptonightR_instruction_mov197, + CryptonightR_instruction_mov198, + CryptonightR_instruction_mov199, + CryptonightR_instruction_mov200, + CryptonightR_instruction_mov201, + CryptonightR_instruction_mov202, + CryptonightR_instruction_mov203, + CryptonightR_instruction_mov204, + CryptonightR_instruction_mov205, + CryptonightR_instruction_mov206, + CryptonightR_instruction_mov207, + CryptonightR_instruction_mov208, + CryptonightR_instruction_mov209, + CryptonightR_instruction_mov210, + CryptonightR_instruction_mov211, + CryptonightR_instruction_mov212, + CryptonightR_instruction_mov213, + CryptonightR_instruction_mov214, + CryptonightR_instruction_mov215, + CryptonightR_instruction_mov216, + CryptonightR_instruction_mov217, + CryptonightR_instruction_mov218, + CryptonightR_instruction_mov219, + CryptonightR_instruction_mov220, + CryptonightR_instruction_mov221, + CryptonightR_instruction_mov222, + CryptonightR_instruction_mov223, + CryptonightR_instruction_mov224, + CryptonightR_instruction_mov225, + CryptonightR_instruction_mov226, + CryptonightR_instruction_mov227, + CryptonightR_instruction_mov228, + CryptonightR_instruction_mov229, + CryptonightR_instruction_mov230, + CryptonightR_instruction_mov231, + CryptonightR_instruction_mov232, + CryptonightR_instruction_mov233, + CryptonightR_instruction_mov234, + CryptonightR_instruction_mov235, + CryptonightR_instruction_mov236, + CryptonightR_instruction_mov237, + CryptonightR_instruction_mov238, + CryptonightR_instruction_mov239, + CryptonightR_instruction_mov240, + CryptonightR_instruction_mov241, + CryptonightR_instruction_mov242, + CryptonightR_instruction_mov243, + CryptonightR_instruction_mov244, + CryptonightR_instruction_mov245, + CryptonightR_instruction_mov246, + CryptonightR_instruction_mov247, + CryptonightR_instruction_mov248, + CryptonightR_instruction_mov249, + CryptonightR_instruction_mov250, + CryptonightR_instruction_mov251, + CryptonightR_instruction_mov252, + CryptonightR_instruction_mov253, + CryptonightR_instruction_mov254, + CryptonightR_instruction_mov255, + CryptonightR_instruction_mov256, +}; + +#endif // CRYPTONIGHTR_TEMPLATE_H diff --git a/src/crypto/chacha.h b/src/crypto/chacha.h index 6e85ad0e9..0610f7051 100644 --- a/src/crypto/chacha.h +++ b/src/crypto/chacha.h @@ -73,18 +73,18 @@ namespace crypto { inline void generate_chacha_key(const void *data, size_t size, chacha_key& key, uint64_t kdf_rounds) { static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key"); epee::mlocked<tools::scrubbed_arr<char, HASH_SIZE>> pwd_hash; - crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 0/*prehashed*/); + crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 0/*prehashed*/, 0/*height*/); for (uint64_t n = 1; n < kdf_rounds; ++n) - crypto::cn_slow_hash(pwd_hash.data(), pwd_hash.size(), pwd_hash.data(), 0/*variant*/, 0/*prehashed*/); + crypto::cn_slow_hash(pwd_hash.data(), pwd_hash.size(), pwd_hash.data(), 0/*variant*/, 0/*prehashed*/, 0/*height*/); memcpy(&unwrap(unwrap(key)), pwd_hash.data(), sizeof(key)); } inline void generate_chacha_key_prehashed(const void *data, size_t size, chacha_key& key, uint64_t kdf_rounds) { static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key"); epee::mlocked<tools::scrubbed_arr<char, HASH_SIZE>> pwd_hash; - crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 1/*prehashed*/); + crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 1/*prehashed*/, 0/*height*/); for (uint64_t n = 1; n < kdf_rounds; ++n) - crypto::cn_slow_hash(pwd_hash.data(), pwd_hash.size(), pwd_hash.data(), 0/*variant*/, 0/*prehashed*/); + crypto::cn_slow_hash(pwd_hash.data(), pwd_hash.size(), pwd_hash.data(), 0/*variant*/, 0/*prehashed*/, 0/*height*/); memcpy(&unwrap(unwrap(key)), pwd_hash.data(), sizeof(key)); } diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index 77b52e2d4..ba7ece0f5 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -79,7 +79,7 @@ enum { }; void cn_fast_hash(const void *data, size_t length, char *hash); -void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed); +void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height); void hash_extra_blake(const void *data, size_t length, char *hash); void hash_extra_groestl(const void *data, size_t length, char *hash); diff --git a/src/crypto/hash.h b/src/crypto/hash.h index 995e2294e..165fe6bb0 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -71,12 +71,12 @@ namespace crypto { return h; } - inline void cn_slow_hash(const void *data, std::size_t length, hash &hash, int variant = 0) { - cn_slow_hash(data, length, reinterpret_cast<char *>(&hash), variant, 0/*prehashed*/); + inline void cn_slow_hash(const void *data, std::size_t length, hash &hash, int variant = 0, uint64_t height = 0) { + cn_slow_hash(data, length, reinterpret_cast<char *>(&hash), variant, 0/*prehashed*/, height); } - inline void cn_slow_hash_prehashed(const void *data, std::size_t length, hash &hash, int variant = 0) { - cn_slow_hash(data, length, reinterpret_cast<char *>(&hash), variant, 1/*prehashed*/); + inline void cn_slow_hash_prehashed(const void *data, std::size_t length, hash &hash, int variant = 0, uint64_t height = 0) { + cn_slow_hash(data, length, reinterpret_cast<char *>(&hash), variant, 1/*prehashed*/, height); } inline void tree_hash(const hash *hashes, std::size_t count, hash &root_hash) { diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index ae0bd4e98..2a8ddb59c 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -39,6 +39,11 @@ #include "hash-ops.h" #include "oaes_lib.h" #include "variant2_int_sqrt.h" +#include "variant4_random_math.h" +#include "CryptonightR_JIT.h" + +#include <errno.h> +#include <string.h> #define MEMORY (1 << 21) // 2MB scratchpad #define ITER (1 << 20) @@ -50,6 +55,16 @@ extern void aesb_single_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey); extern void aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey); +static void local_abort(const char *msg) +{ + fprintf(stderr, "%s\n", msg); +#ifdef NDEBUG + _exit(1); +#else + abort(); +#endif +} + #define VARIANT1_1(p) \ do if (variant == 1) \ { \ @@ -116,48 +131,74 @@ extern void aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *ex #define VARIANT2_SHUFFLE_ADD_SSE2(base_ptr, offset) \ do if (variant >= 2) \ { \ - const __m128i chunk1 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ 0x10))); \ + __m128i chunk1 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ 0x10))); \ const __m128i chunk2 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ 0x20))); \ const __m128i chunk3 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ 0x30))); \ _mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x10)), _mm_add_epi64(chunk3, _b1)); \ _mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x20)), _mm_add_epi64(chunk1, _b)); \ _mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x30)), _mm_add_epi64(chunk2, _a)); \ + if (variant >= 4) \ + { \ + chunk1 = _mm_xor_si128(chunk1, chunk2); \ + _c = _mm_xor_si128(_c, chunk3); \ + _c = _mm_xor_si128(_c, chunk1); \ + } \ } while (0) #define VARIANT2_SHUFFLE_ADD_NEON(base_ptr, offset) \ do if (variant >= 2) \ { \ - const uint64x2_t chunk1 = vld1q_u64(U64((base_ptr) + ((offset) ^ 0x10))); \ + uint64x2_t chunk1 = vld1q_u64(U64((base_ptr) + ((offset) ^ 0x10))); \ const uint64x2_t chunk2 = vld1q_u64(U64((base_ptr) + ((offset) ^ 0x20))); \ const uint64x2_t chunk3 = vld1q_u64(U64((base_ptr) + ((offset) ^ 0x30))); \ vst1q_u64(U64((base_ptr) + ((offset) ^ 0x10)), vaddq_u64(chunk3, vreinterpretq_u64_u8(_b1))); \ vst1q_u64(U64((base_ptr) + ((offset) ^ 0x20)), vaddq_u64(chunk1, vreinterpretq_u64_u8(_b))); \ vst1q_u64(U64((base_ptr) + ((offset) ^ 0x30)), vaddq_u64(chunk2, vreinterpretq_u64_u8(_a))); \ + if (variant >= 4) \ + { \ + chunk1 = veorq_u64(chunk1, chunk2); \ + _c = vreinterpretq_u8_u64(veorq_u64(vreinterpretq_u64_u8(_c), chunk3)); \ + _c = vreinterpretq_u8_u64(veorq_u64(vreinterpretq_u64_u8(_c), chunk1)); \ + } \ } while (0) -#define VARIANT2_PORTABLE_SHUFFLE_ADD(base_ptr, offset) \ +#define VARIANT2_PORTABLE_SHUFFLE_ADD(out, a_, base_ptr, offset) \ do if (variant >= 2) \ { \ uint64_t* chunk1 = U64((base_ptr) + ((offset) ^ 0x10)); \ uint64_t* chunk2 = U64((base_ptr) + ((offset) ^ 0x20)); \ uint64_t* chunk3 = U64((base_ptr) + ((offset) ^ 0x30)); \ \ - const uint64_t chunk1_old[2] = { chunk1[0], chunk1[1] }; \ + uint64_t chunk1_old[2] = { SWAP64LE(chunk1[0]), SWAP64LE(chunk1[1]) }; \ + const uint64_t chunk2_old[2] = { SWAP64LE(chunk2[0]), SWAP64LE(chunk2[1]) }; \ + const uint64_t chunk3_old[2] = { SWAP64LE(chunk3[0]), SWAP64LE(chunk3[1]) }; \ \ uint64_t b1[2]; \ memcpy_swap64le(b1, b + 16, 2); \ - chunk1[0] = SWAP64LE(SWAP64LE(chunk3[0]) + b1[0]); \ - chunk1[1] = SWAP64LE(SWAP64LE(chunk3[1]) + b1[1]); \ + chunk1[0] = SWAP64LE(chunk3_old[0] + b1[0]); \ + chunk1[1] = SWAP64LE(chunk3_old[1] + b1[1]); \ \ uint64_t a0[2]; \ - memcpy_swap64le(a0, a, 2); \ - chunk3[0] = SWAP64LE(SWAP64LE(chunk2[0]) + a0[0]); \ - chunk3[1] = SWAP64LE(SWAP64LE(chunk2[1]) + a0[1]); \ + memcpy_swap64le(a0, a_, 2); \ + chunk3[0] = SWAP64LE(chunk2_old[0] + a0[0]); \ + chunk3[1] = SWAP64LE(chunk2_old[1] + a0[1]); \ \ uint64_t b0[2]; \ memcpy_swap64le(b0, b, 2); \ - chunk2[0] = SWAP64LE(SWAP64LE(chunk1_old[0]) + b0[0]); \ + chunk2[0] = SWAP64LE(chunk1_old[0] + b0[0]); \ chunk2[1] = SWAP64LE(SWAP64LE(chunk1_old[1]) + b0[1]); \ + if (variant >= 4) \ + { \ + uint64_t out_copy[2]; \ + memcpy_swap64le(out_copy, out, 2); \ + chunk1_old[0] ^= chunk2_old[0]; \ + chunk1_old[1] ^= chunk2_old[1]; \ + out_copy[0] ^= chunk3_old[0]; \ + out_copy[1] ^= chunk3_old[1]; \ + out_copy[0] ^= chunk1_old[0]; \ + out_copy[1] ^= chunk1_old[1]; \ + memcpy_swap64le(out, out_copy, 2); \ + } \ } while (0) #define VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr) \ @@ -172,7 +213,7 @@ extern void aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *ex const uint64_t sqrt_input = SWAP64LE(((uint64_t*)(ptr))[0]) + division_result #define VARIANT2_INTEGER_MATH_SSE2(b, ptr) \ - do if (variant >= 2) \ + do if ((variant == 2) || (variant == 3)) \ { \ VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr); \ VARIANT2_INTEGER_MATH_SQRT_STEP_SSE2(); \ @@ -182,7 +223,7 @@ extern void aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *ex #if defined DBL_MANT_DIG && (DBL_MANT_DIG >= 50) // double precision floating point type has enough bits of precision on current platform #define VARIANT2_PORTABLE_INTEGER_MATH(b, ptr) \ - do if (variant >= 2) \ + do if ((variant == 2) || (variant == 3)) \ { \ VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr); \ VARIANT2_INTEGER_MATH_SQRT_STEP_FP64(); \ @@ -192,7 +233,7 @@ extern void aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *ex // double precision floating point type is not good enough on current platform // fall back to the reference code (integer only) #define VARIANT2_PORTABLE_INTEGER_MATH(b, ptr) \ - do if (variant >= 2) \ + do if ((variant == 2) || (variant == 3)) \ { \ VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr); \ VARIANT2_INTEGER_MATH_SQRT_STEP_REF(); \ @@ -200,13 +241,13 @@ extern void aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *ex #endif #define VARIANT2_2_PORTABLE() \ - if (variant >= 2) { \ + if (variant == 2 || variant == 3) { \ xor_blocks(long_state + (j ^ 0x10), d); \ xor_blocks(d, long_state + (j ^ 0x20)); \ } #define VARIANT2_2() \ - do if (variant >= 2) \ + do if (variant == 2 || variant == 3) \ { \ *U64(hp_state + (j ^ 0x10)) ^= SWAP64LE(hi); \ *(U64(hp_state + (j ^ 0x10)) + 1) ^= SWAP64LE(lo); \ @@ -214,6 +255,68 @@ extern void aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *ex lo ^= SWAP64LE(*(U64(hp_state + (j ^ 0x20)) + 1)); \ } while (0) +#define V4_REG_LOAD(dst, src) \ + do { \ + memcpy((dst), (src), sizeof(v4_reg)); \ + if (sizeof(v4_reg) == sizeof(uint32_t)) \ + *(dst) = SWAP32LE(*(dst)); \ + else \ + *(dst) = SWAP64LE(*(dst)); \ + } while (0) + +#define VARIANT4_RANDOM_MATH_INIT() \ + v4_reg r[9]; \ + struct V4_Instruction code[NUM_INSTRUCTIONS_MAX + 1]; \ + int jit = use_v4_jit(); \ + do if (variant >= 4) \ + { \ + for (int i = 0; i < 4; ++i) \ + V4_REG_LOAD(r + i, (uint8_t*)(state.hs.w + 12) + sizeof(v4_reg) * i); \ + v4_random_math_init(code, height); \ + if (jit) \ + { \ + int ret = v4_generate_JIT_code(code, hp_jitfunc, 4096); \ + if (ret < 0) \ + local_abort("Error generating CryptonightR code"); \ + } \ + } while (0) + +#define VARIANT4_RANDOM_MATH(a, b, r, _b, _b1) \ + do if (variant >= 4) \ + { \ + uint64_t t[2]; \ + memcpy(t, b, sizeof(uint64_t)); \ + \ + if (sizeof(v4_reg) == sizeof(uint32_t)) \ + t[0] ^= SWAP64LE((r[0] + r[1]) | ((uint64_t)(r[2] + r[3]) << 32)); \ + else \ + t[0] ^= SWAP64LE((r[0] + r[1]) ^ (r[2] + r[3])); \ + \ + memcpy(b, t, sizeof(uint64_t)); \ + \ + V4_REG_LOAD(r + 4, a); \ + V4_REG_LOAD(r + 5, (uint64_t*)(a) + 1); \ + V4_REG_LOAD(r + 6, _b); \ + V4_REG_LOAD(r + 7, _b1); \ + V4_REG_LOAD(r + 8, (uint64_t*)(_b1) + 1); \ + \ + if (jit) \ + (*hp_jitfunc)(r); \ + else \ + v4_random_math(code, r); \ + \ + memcpy(t, a, sizeof(uint64_t) * 2); \ + \ + if (sizeof(v4_reg) == sizeof(uint32_t)) { \ + t[0] ^= SWAP64LE(r[2] | ((uint64_t)(r[3]) << 32)); \ + t[1] ^= SWAP64LE(r[0] | ((uint64_t)(r[1]) << 32)); \ + } else { \ + t[0] ^= SWAP64LE(r[2] ^ r[3]); \ + t[1] ^= SWAP64LE(r[0] ^ r[1]); \ + } \ + memcpy(a, t, sizeof(uint64_t) * 2); \ + } while (0) + #if !defined NO_AES && (defined(__x86_64__) || (defined(_MSC_VER) && defined(_WIN64))) // Optimised code below, uses x86-specific intrinsics, SSE2, AES-NI @@ -298,6 +401,7 @@ extern void aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *ex p = U64(&hp_state[j]); \ b[0] = p[0]; b[1] = p[1]; \ VARIANT2_INTEGER_MATH_SSE2(b, c); \ + VARIANT4_RANDOM_MATH(a, b, r, &_b, &_b1); \ __mul(); \ VARIANT2_2(); \ VARIANT2_SHUFFLE_ADD_SSE2(hp_state, j); \ @@ -329,6 +433,9 @@ union cn_slow_hash_state THREADV uint8_t *hp_state = NULL; THREADV int hp_allocated = 0; +THREADV v4_random_math_JIT_func hp_jitfunc = NULL; +THREADV uint8_t *hp_jitfunc_memory = NULL; +THREADV int hp_jitfunc_allocated = 0; #if defined(_MSC_VER) #define cpuid(info,x) __cpuidex(info,x,0) @@ -387,6 +494,31 @@ STATIC INLINE int force_software_aes(void) return use; } +volatile int use_v4_jit_flag = -1; + +STATIC INLINE int use_v4_jit(void) +{ +#if defined(__x86_64__) + + if (use_v4_jit_flag != -1) + return use_v4_jit_flag; + + const char *env = getenv("MONERO_USE_CNV4_JIT"); + if (!env) { + use_v4_jit_flag = 0; + } + else if (!strcmp(env, "0") || !strcmp(env, "no")) { + use_v4_jit_flag = 0; + } + else { + use_v4_jit_flag = 1; + } + return use_v4_jit_flag; +#else + return 0; +#endif +} + STATIC INLINE int check_aes_hw(void) { int cpuid_results[4]; @@ -638,6 +770,33 @@ void slow_hash_allocate_state(void) hp_allocated = 0; hp_state = (uint8_t *) malloc(MEMORY); } + + +#if defined(_MSC_VER) || defined(__MINGW32__) + hp_jitfunc_memory = (uint8_t *) VirtualAlloc(hp_jitfunc_memory, 4096 + 4095, + MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); +#else +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ + defined(__DragonFly__) || defined(__NetBSD__) + hp_jitfunc_memory = mmap(0, 4096 + 4095, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANON, 0, 0); +#else + hp_jitfunc_memory = mmap(0, 4096 + 4095, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); +#endif + if(hp_jitfunc_memory == MAP_FAILED) + hp_jitfunc_memory = NULL; +#endif + hp_jitfunc_allocated = 1; + if (hp_jitfunc_memory == NULL) + { + hp_jitfunc_allocated = 0; + hp_jitfunc_memory = malloc(4096 + 4095); + } + hp_jitfunc = (v4_random_math_JIT_func)((size_t)(hp_jitfunc_memory + 4095) & ~4095); +#if !(defined(_MSC_VER) || defined(__MINGW32__)) + mprotect(hp_jitfunc, 4096, PROT_READ | PROT_WRITE | PROT_EXEC); +#endif } /** @@ -660,8 +819,22 @@ void slow_hash_free_state(void) #endif } + if(!hp_jitfunc_allocated) + free(hp_jitfunc_memory); + else + { +#if defined(_MSC_VER) || defined(__MINGW32__) + VirtualFree(hp_jitfunc_memory, 0, MEM_RELEASE); +#else + munmap(hp_jitfunc_memory, 4096 + 4095); +#endif + } + hp_state = NULL; hp_allocated = 0; + hp_jitfunc = NULL; + hp_jitfunc_memory = NULL; + hp_jitfunc_allocated = 0; } /** @@ -694,7 +867,7 @@ void slow_hash_free_state(void) * @param length the length in bytes of the data * @param hash a pointer to a buffer in which the final 256 bit hash will be stored */ -void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed) +void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height) { RDATA_ALIGN16 uint8_t expandedKey[240]; /* These buffers are aligned to use later with SSE functions */ @@ -730,6 +903,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int VARIANT1_INIT64(); VARIANT2_INIT64(); + VARIANT4_RANDOM_MATH_INIT(); /* CryptoNight Step 2: Iteratively encrypt the results from Keccak to fill * the 2MB large random access buffer. @@ -901,6 +1075,7 @@ union cn_slow_hash_state p = U64(&hp_state[j]); \ b[0] = p[0]; b[1] = p[1]; \ VARIANT2_PORTABLE_INTEGER_MATH(b, c); \ + VARIANT4_RANDOM_MATH(a, b, r, &_b, &_b1); \ __mul(); \ VARIANT2_2(); \ VARIANT2_SHUFFLE_ADD_NEON(hp_state, j); \ @@ -1063,7 +1238,7 @@ STATIC INLINE void aligned_free(void *ptr) } #endif /* FORCE_USE_HEAP */ -void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed) +void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height) { RDATA_ALIGN16 uint8_t expandedKey[240]; @@ -1100,6 +1275,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int VARIANT1_INIT64(); VARIANT2_INIT64(); + VARIANT4_RANDOM_MATH_INIT(); /* CryptoNight Step 2: Iteratively encrypt the results from Keccak to fill * the 2MB large random access buffer. @@ -1278,10 +1454,11 @@ STATIC INLINE void xor_blocks(uint8_t* a, const uint8_t* b) U64(a)[1] ^= U64(b)[1]; } -void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed) +void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height) { uint8_t text[INIT_SIZE_BYTE]; uint8_t a[AES_BLOCK_SIZE]; + uint8_t a1[AES_BLOCK_SIZE]; uint8_t b[AES_BLOCK_SIZE * 2]; uint8_t c[AES_BLOCK_SIZE]; uint8_t c1[AES_BLOCK_SIZE]; @@ -1317,6 +1494,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int VARIANT1_INIT64(); VARIANT2_INIT64(); + VARIANT4_RANDOM_MATH_INIT(); // use aligned data memcpy(expandedKey, aes_ctx->key->exp_data, aes_ctx->key->exp_data_len); @@ -1340,10 +1518,10 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int // Iteration 1 j = state_index(a); p = &long_state[j]; - aesb_single_round(p, p, a); - copy_block(c1, p); + aesb_single_round(p, c1, a); - VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j); + VARIANT2_PORTABLE_SHUFFLE_ADD(c1, a, long_state, j); + copy_block(p, c1); xor_blocks(p, b); VARIANT1_1(p); @@ -1352,13 +1530,15 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int p = &long_state[j]; copy_block(c, p); + copy_block(a1, a); VARIANT2_PORTABLE_INTEGER_MATH(c, c1); + VARIANT4_RANDOM_MATH(a1, c, r, b, b + AES_BLOCK_SIZE); mul(c1, c, d); VARIANT2_2_PORTABLE(); - VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j); - sum_half_blocks(a, d); - swap_blocks(a, c); - xor_blocks(a, c); + VARIANT2_PORTABLE_SHUFFLE_ADD(c1, a, long_state, j); + sum_half_blocks(a1, d); + swap_blocks(a1, c); + xor_blocks(a1, c); VARIANT1_2(U64(c) + 1); copy_block(p, c); @@ -1366,6 +1546,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int copy_block(b + AES_BLOCK_SIZE, b); } copy_block(b, c1); + copy_block(a, a1); } memcpy(text, state.init, INIT_SIZE_BYTE); @@ -1476,7 +1657,7 @@ union cn_slow_hash_state { }; #pragma pack(pop) -void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed) { +void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height) { #ifndef FORCE_USE_HEAP uint8_t long_state[MEMORY]; #else @@ -1486,6 +1667,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int union cn_slow_hash_state state; uint8_t text[INIT_SIZE_BYTE]; uint8_t a[AES_BLOCK_SIZE]; + uint8_t a1[AES_BLOCK_SIZE]; uint8_t b[AES_BLOCK_SIZE * 2]; uint8_t c1[AES_BLOCK_SIZE]; uint8_t c2[AES_BLOCK_SIZE]; @@ -1505,6 +1687,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int VARIANT1_PORTABLE_INIT(); VARIANT2_PORTABLE_INIT(); + VARIANT4_RANDOM_MATH_INIT(); oaes_key_import_data(aes_ctx, aes_key, AES_KEY_SIZE); for (i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) { @@ -1528,7 +1711,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int j = e2i(a, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE; copy_block(c1, &long_state[j]); aesb_single_round(c1, c1, a); - VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j); + VARIANT2_PORTABLE_SHUFFLE_ADD(c1, a, long_state, j); copy_block(&long_state[j], c1); xor_blocks(&long_state[j], b); assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE); @@ -1536,22 +1719,22 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int /* Iteration 2 */ j = e2i(c1, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE; copy_block(c2, &long_state[j]); + copy_block(a1, a); VARIANT2_PORTABLE_INTEGER_MATH(c2, c1); + VARIANT4_RANDOM_MATH(a1, c2, r, b, b + AES_BLOCK_SIZE); mul(c1, c2, d); VARIANT2_2_PORTABLE(); - VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j); - swap_blocks(a, c1); - sum_half_blocks(c1, d); - swap_blocks(c1, c2); - xor_blocks(c1, c2); + VARIANT2_PORTABLE_SHUFFLE_ADD(c1, a, long_state, j); + sum_half_blocks(a1, d); + swap_blocks(a1, c2); + xor_blocks(a1, c2); VARIANT1_2(c2 + 8); copy_block(&long_state[j], c2); - assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE); if (variant >= 2) { copy_block(b + AES_BLOCK_SIZE, b); } - copy_block(b, a); - copy_block(a, c1); + copy_block(b, c1); + copy_block(a, a1); } memcpy(text, state.init, INIT_SIZE_BYTE); diff --git a/src/crypto/variant4_random_math.h b/src/crypto/variant4_random_math.h new file mode 100644 index 000000000..f3e41a001 --- /dev/null +++ b/src/crypto/variant4_random_math.h @@ -0,0 +1,441 @@ +#ifndef VARIANT4_RANDOM_MATH_H +#define VARIANT4_RANDOM_MATH_H + +// Register size can be configured to either 32 bit (uint32_t) or 64 bit (uint64_t) +typedef uint32_t v4_reg; + +enum V4_Settings +{ + // Generate code with minimal theoretical latency = 45 cycles, which is equivalent to 15 multiplications + TOTAL_LATENCY = 15 * 3, + + // Always generate at least 60 instructions + NUM_INSTRUCTIONS_MIN = 60, + + // Never generate more than 70 instructions (final RET instruction doesn't count here) + NUM_INSTRUCTIONS_MAX = 70, + + // Available ALUs for MUL + // Modern CPUs typically have only 1 ALU which can do multiplications + ALU_COUNT_MUL = 1, + + // Total available ALUs + // Modern CPUs have 4 ALUs, but we use only 3 because random math executes together with other main loop code + ALU_COUNT = 3, +}; + +enum V4_InstructionList +{ + MUL, // a*b + ADD, // a+b + C, C is an unsigned 32-bit constant + SUB, // a-b + ROR, // rotate right "a" by "b & 31" bits + ROL, // rotate left "a" by "b & 31" bits + XOR, // a^b + RET, // finish execution + V4_INSTRUCTION_COUNT = RET, +}; + +// V4_InstructionDefinition is used to generate code from random data +// Every random sequence of bytes is a valid code +// +// There are 9 registers in total: +// - 4 variable registers +// - 5 constant registers initialized from loop variables +// This is why dst_index is 2 bits +enum V4_InstructionDefinition +{ + V4_OPCODE_BITS = 3, + V4_DST_INDEX_BITS = 2, + V4_SRC_INDEX_BITS = 3, +}; + +struct V4_Instruction +{ + uint8_t opcode; + uint8_t dst_index; + uint8_t src_index; + uint32_t C; +}; + +#ifndef FORCEINLINE +#if defined(__GNUC__) +#define FORCEINLINE __attribute__((always_inline)) inline +#elif defined(_MSC_VER) +#define FORCEINLINE __forceinline +#else +#define FORCEINLINE inline +#endif +#endif + +#ifndef UNREACHABLE_CODE +#if defined(__GNUC__) +#define UNREACHABLE_CODE __builtin_unreachable() +#elif defined(_MSC_VER) +#define UNREACHABLE_CODE __assume(false) +#else +#define UNREACHABLE_CODE +#endif +#endif + +// Random math interpreter's loop is fully unrolled and inlined to achieve 100% branch prediction on CPU: +// every switch-case will point to the same destination on every iteration of Cryptonight main loop +// +// This is about as fast as it can get without using low-level machine code generation +static FORCEINLINE void v4_random_math(const struct V4_Instruction* code, v4_reg* r) +{ + enum + { + REG_BITS = sizeof(v4_reg) * 8, + }; + +#define V4_EXEC(i) \ + { \ + const struct V4_Instruction* op = code + i; \ + const v4_reg src = r[op->src_index]; \ + v4_reg* dst = r + op->dst_index; \ + switch (op->opcode) \ + { \ + case MUL: \ + *dst *= src; \ + break; \ + case ADD: \ + *dst += src + op->C; \ + break; \ + case SUB: \ + *dst -= src; \ + break; \ + case ROR: \ + { \ + const uint32_t shift = src % REG_BITS; \ + *dst = (*dst >> shift) | (*dst << ((REG_BITS - shift) % REG_BITS)); \ + } \ + break; \ + case ROL: \ + { \ + const uint32_t shift = src % REG_BITS; \ + *dst = (*dst << shift) | (*dst >> ((REG_BITS - shift) % REG_BITS)); \ + } \ + break; \ + case XOR: \ + *dst ^= src; \ + break; \ + case RET: \ + return; \ + default: \ + UNREACHABLE_CODE; \ + break; \ + } \ + } + +#define V4_EXEC_10(j) \ + V4_EXEC(j + 0) \ + V4_EXEC(j + 1) \ + V4_EXEC(j + 2) \ + V4_EXEC(j + 3) \ + V4_EXEC(j + 4) \ + V4_EXEC(j + 5) \ + V4_EXEC(j + 6) \ + V4_EXEC(j + 7) \ + V4_EXEC(j + 8) \ + V4_EXEC(j + 9) + + // Generated program can have 60 + a few more (usually 2-3) instructions to achieve required latency + // I've checked all block heights < 10,000,000 and here is the distribution of program sizes: + // + // 60 27960 + // 61 105054 + // 62 2452759 + // 63 5115997 + // 64 1022269 + // 65 1109635 + // 66 153145 + // 67 8550 + // 68 4529 + // 69 102 + + // Unroll 70 instructions here + V4_EXEC_10(0); // instructions 0-9 + V4_EXEC_10(10); // instructions 10-19 + V4_EXEC_10(20); // instructions 20-29 + V4_EXEC_10(30); // instructions 30-39 + V4_EXEC_10(40); // instructions 40-49 + V4_EXEC_10(50); // instructions 50-59 + V4_EXEC_10(60); // instructions 60-69 + +#undef V4_EXEC_10 +#undef V4_EXEC +} + +// If we don't have enough data available, generate more +static FORCEINLINE void check_data(size_t* data_index, const size_t bytes_needed, int8_t* data, const size_t data_size) +{ + if (*data_index + bytes_needed > data_size) + { + hash_extra_blake(data, data_size, (char*) data); + *data_index = 0; + } +} + +// Generates as many random math operations as possible with given latency and ALU restrictions +// "code" array must have space for NUM_INSTRUCTIONS_MAX+1 instructions +static inline int v4_random_math_init(struct V4_Instruction* code, const uint64_t height) +{ + // MUL is 3 cycles, 3-way addition and rotations are 2 cycles, SUB/XOR are 1 cycle + // These latencies match real-life instruction latencies for Intel CPUs starting from Sandy Bridge and up to Skylake/Coffee lake + // + // AMD Ryzen has the same latencies except 1-cycle ROR/ROL, so it'll be a bit faster than Intel Sandy Bridge and newer processors + // Surprisingly, Intel Nehalem also has 1-cycle ROR/ROL, so it'll also be faster than Intel Sandy Bridge and newer processors + // AMD Bulldozer has 4 cycles latency for MUL (slower than Intel) and 1 cycle for ROR/ROL (faster than Intel), so average performance will be the same + // Source: https://www.agner.org/optimize/instruction_tables.pdf + const int op_latency[V4_INSTRUCTION_COUNT] = { 3, 2, 1, 2, 2, 1 }; + + // Instruction latencies for theoretical ASIC implementation + const int asic_op_latency[V4_INSTRUCTION_COUNT] = { 3, 1, 1, 1, 1, 1 }; + + // Available ALUs for each instruction + const int op_ALUs[V4_INSTRUCTION_COUNT] = { ALU_COUNT_MUL, ALU_COUNT, ALU_COUNT, ALU_COUNT, ALU_COUNT, ALU_COUNT }; + + int8_t data[32]; + memset(data, 0, sizeof(data)); + uint64_t tmp = SWAP64LE(height); + memcpy(data, &tmp, sizeof(uint64_t)); + data[20] = -38; // change seed + + // Set data_index past the last byte in data + // to trigger full data update with blake hash + // before we start using it + size_t data_index = sizeof(data); + + int code_size; + + // There is a small chance (1.8%) that register R8 won't be used in the generated program + // So we keep track of it and try again if it's not used + bool r8_used; + do { + int latency[9]; + int asic_latency[9]; + + // Tracks previous instruction and value of the source operand for registers R0-R3 throughout code execution + // byte 0: current value of the destination register + // byte 1: instruction opcode + // byte 2: current value of the source register + // + // Registers R4-R8 are constant and are treated as having the same value because when we do + // the same operation twice with two constant source registers, it can be optimized into a single operation + uint32_t inst_data[9] = { 0, 1, 2, 3, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF }; + + bool alu_busy[TOTAL_LATENCY + 1][ALU_COUNT]; + bool is_rotation[V4_INSTRUCTION_COUNT]; + bool rotated[4]; + int rotate_count = 0; + + memset(latency, 0, sizeof(latency)); + memset(asic_latency, 0, sizeof(asic_latency)); + memset(alu_busy, 0, sizeof(alu_busy)); + memset(is_rotation, 0, sizeof(is_rotation)); + memset(rotated, 0, sizeof(rotated)); + is_rotation[ROR] = true; + is_rotation[ROL] = true; + + int num_retries = 0; + code_size = 0; + + int total_iterations = 0; + r8_used = false; + + // Generate random code to achieve minimal required latency for our abstract CPU + // Try to get this latency for all 4 registers + while (((latency[0] < TOTAL_LATENCY) || (latency[1] < TOTAL_LATENCY) || (latency[2] < TOTAL_LATENCY) || (latency[3] < TOTAL_LATENCY)) && (num_retries < 64)) + { + // Fail-safe to guarantee loop termination + ++total_iterations; + if (total_iterations > 256) + break; + + check_data(&data_index, 1, data, sizeof(data)); + + const uint8_t c = ((uint8_t*)data)[data_index++]; + + // MUL = opcodes 0-2 + // ADD = opcode 3 + // SUB = opcode 4 + // ROR/ROL = opcode 5, shift direction is selected randomly + // XOR = opcodes 6-7 + uint8_t opcode = c & ((1 << V4_OPCODE_BITS) - 1); + if (opcode == 5) + { + check_data(&data_index, 1, data, sizeof(data)); + opcode = (data[data_index++] >= 0) ? ROR : ROL; + } + else if (opcode >= 6) + { + opcode = XOR; + } + else + { + opcode = (opcode <= 2) ? MUL : (opcode - 2); + } + + uint8_t dst_index = (c >> V4_OPCODE_BITS) & ((1 << V4_DST_INDEX_BITS) - 1); + uint8_t src_index = (c >> (V4_OPCODE_BITS + V4_DST_INDEX_BITS)) & ((1 << V4_SRC_INDEX_BITS) - 1); + + const int a = dst_index; + int b = src_index; + + // Don't do ADD/SUB/XOR with the same register + if (((opcode == ADD) || (opcode == SUB) || (opcode == XOR)) && (a == b)) + { + // Use register R8 as source instead + b = 8; + src_index = 8; + } + + // Don't do rotation with the same destination twice because it's equal to a single rotation + if (is_rotation[opcode] && rotated[a]) + { + continue; + } + + // Don't do the same instruction (except MUL) with the same source value twice because all other cases can be optimized: + // 2xADD(a, b, C) = ADD(a, b*2, C1+C2), same for SUB and rotations + // 2xXOR(a, b) = NOP + if ((opcode != MUL) && ((inst_data[a] & 0xFFFF00) == (opcode << 8) + ((inst_data[b] & 255) << 16))) + { + continue; + } + + // Find which ALU is available (and when) for this instruction + int next_latency = (latency[a] > latency[b]) ? latency[a] : latency[b]; + int alu_index = -1; + while (next_latency < TOTAL_LATENCY) + { + for (int i = op_ALUs[opcode] - 1; i >= 0; --i) + { + if (!alu_busy[next_latency][i]) + { + // ADD is implemented as two 1-cycle instructions on a real CPU, so do an additional availability check + if ((opcode == ADD) && alu_busy[next_latency + 1][i]) + { + continue; + } + + // Rotation can only start when previous rotation is finished, so do an additional availability check + if (is_rotation[opcode] && (next_latency < rotate_count * op_latency[opcode])) + { + continue; + } + + alu_index = i; + break; + } + } + if (alu_index >= 0) + { + break; + } + ++next_latency; + } + + // Don't generate instructions that leave some register unchanged for more than 7 cycles + if (next_latency > latency[a] + 7) + { + continue; + } + + next_latency += op_latency[opcode]; + + if (next_latency <= TOTAL_LATENCY) + { + if (is_rotation[opcode]) + { + ++rotate_count; + } + + // Mark ALU as busy only for the first cycle when it starts executing the instruction because ALUs are fully pipelined + alu_busy[next_latency - op_latency[opcode]][alu_index] = true; + latency[a] = next_latency; + + // ASIC is supposed to have enough ALUs to run as many independent instructions per cycle as possible, so latency calculation for ASIC is simple + asic_latency[a] = ((asic_latency[a] > asic_latency[b]) ? asic_latency[a] : asic_latency[b]) + asic_op_latency[opcode]; + + rotated[a] = is_rotation[opcode]; + + inst_data[a] = code_size + (opcode << 8) + ((inst_data[b] & 255) << 16); + + code[code_size].opcode = opcode; + code[code_size].dst_index = dst_index; + code[code_size].src_index = src_index; + code[code_size].C = 0; + + if (src_index == 8) + { + r8_used = true; + } + + if (opcode == ADD) + { + // ADD instruction is implemented as two 1-cycle instructions on a real CPU, so mark ALU as busy for the next cycle too + alu_busy[next_latency - op_latency[opcode] + 1][alu_index] = true; + + // ADD instruction requires 4 more random bytes for 32-bit constant "C" in "a = a + b + C" + check_data(&data_index, sizeof(uint32_t), data, sizeof(data)); + uint32_t t; + memcpy(&t, data + data_index, sizeof(uint32_t)); + code[code_size].C = SWAP32LE(t); + data_index += sizeof(uint32_t); + } + + ++code_size; + if (code_size >= NUM_INSTRUCTIONS_MIN) + { + break; + } + } + else + { + ++num_retries; + } + } + + // ASIC has more execution resources and can extract as much parallelism from the code as possible + // We need to add a few more MUL and ROR instructions to achieve minimal required latency for ASIC + // Get this latency for at least 1 of the 4 registers + const int prev_code_size = code_size; + while ((code_size < NUM_INSTRUCTIONS_MAX) && (asic_latency[0] < TOTAL_LATENCY) && (asic_latency[1] < TOTAL_LATENCY) && (asic_latency[2] < TOTAL_LATENCY) && (asic_latency[3] < TOTAL_LATENCY)) + { + int min_idx = 0; + int max_idx = 0; + for (int i = 1; i < 4; ++i) + { + if (asic_latency[i] < asic_latency[min_idx]) min_idx = i; + if (asic_latency[i] > asic_latency[max_idx]) max_idx = i; + } + + const uint8_t pattern[3] = { ROR, MUL, MUL }; + const uint8_t opcode = pattern[(code_size - prev_code_size) % 3]; + latency[min_idx] = latency[max_idx] + op_latency[opcode]; + asic_latency[min_idx] = asic_latency[max_idx] + asic_op_latency[opcode]; + + code[code_size].opcode = opcode; + code[code_size].dst_index = min_idx; + code[code_size].src_index = max_idx; + code[code_size].C = 0; + ++code_size; + } + + // There is ~98.15% chance that loop condition is false, so this loop will execute only 1 iteration most of the time + // It never does more than 4 iterations for all block heights < 10,000,000 + } while (!r8_used || (code_size < NUM_INSTRUCTIONS_MIN) || (code_size > NUM_INSTRUCTIONS_MAX)); + + // It's guaranteed that NUM_INSTRUCTIONS_MIN <= code_size <= NUM_INSTRUCTIONS_MAX here + // Add final instruction to stop the interpreter + code[code_size].opcode = RET; + code[code_size].dst_index = 0; + code[code_size].src_index = 0; + code[code_size].C = 0; + + return code_size; +} + +#endif diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index a4228b849..d1e321994 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -45,8 +45,6 @@ #include "ringct/rctTypes.h" #include "ringct/rctOps.h" -BOOST_CLASS_VERSION(rct::ecdhTuple, 1) - //namespace cryptonote { namespace boost { @@ -249,19 +247,8 @@ namespace boost template <class Archive> inline void serialize(Archive &a, rct::ecdhTuple &x, const boost::serialization::version_type ver) { - if (ver < 1) - { - a & x.mask; - a & x.amount; - return; - } - crypto::hash8 &amount = (crypto::hash8&)x.amount; - if (!Archive::is_saving::value) - { - memset(&x.mask, 0, sizeof(x.mask)); - memset(&x.amount, 0, sizeof(x.amount)); - } - a & amount; + a & x.mask; + a & x.amount; } template <class Archive> diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index f6daaab95..10fb5444c 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -1174,7 +1174,7 @@ namespace cryptonote } blobdata bd = get_block_hashing_blob(b); const int cn_variant = b.major_version >= 7 ? b.major_version - 6 : 0; - crypto::cn_slow_hash(bd.data(), bd.size(), res, cn_variant); + crypto::cn_slow_hash(bd.data(), bd.size(), res, cn_variant, height); return true; } //--------------------------------------------------------------- diff --git a/src/cryptonote_basic/hardfork.cpp b/src/cryptonote_basic/hardfork.cpp index 447d79aee..d1d836fcb 100644 --- a/src/cryptonote_basic/hardfork.cpp +++ b/src/cryptonote_basic/hardfork.cpp @@ -332,7 +332,7 @@ int HardFork::get_voted_fork_index(uint64_t height) const { CRITICAL_REGION_LOCAL(lock); uint32_t accumulated_votes = 0; - for (unsigned int n = heights.size() - 1; n > current_fork_index; --n) { + for (int n = heights.size() - 1; n >= 0; --n) { uint8_t v = heights[n].version; accumulated_votes += last_versions[v]; uint32_t threshold = (window_size * heights[n].threshold + 99) / 100; diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index f4de2ed7e..d29a3dcd6 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -73,6 +73,9 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "miner" +#define AUTODETECT_WINDOW 10 // seconds +#define AUTODETECT_GAIN_THRESHOLD 1.02f // 2% + using namespace epee; #include "miner.h" @@ -108,6 +111,7 @@ namespace cryptonote m_starter_nonce(0), m_last_hr_merge_time(0), m_hashes(0), + m_total_hashes(0), m_do_print_hashrate(false), m_do_mining(false), m_current_hash_rate(0), @@ -178,7 +182,12 @@ namespace cryptonote merge_hr(); return true; }); - + + m_autodetect_interval.do_call([&](){ + update_autodetection(); + return true; + }); + return true; } //----------------------------------------------------------------------------------------------------- @@ -209,6 +218,60 @@ namespace cryptonote m_hashes = 0; } //----------------------------------------------------------------------------------------------------- + void miner::update_autodetection() + { + if (m_threads_autodetect.empty()) + return; + + uint64_t now = epee::misc_utils::get_ns_count(); + uint64_t dt = now - m_threads_autodetect.back().first; + if (dt < AUTODETECT_WINDOW * 1000000000ull) + return; + + // work out how many more hashes we got + m_threads_autodetect.back().first = dt; + uint64_t dh = m_total_hashes - m_threads_autodetect.back().second; + m_threads_autodetect.back().second = dh; + float hs = dh / (dt / (float)1000000000); + MGINFO("Mining autodetection: " << m_threads_autodetect.size() << " threads: " << hs << " H/s"); + + // when we don't increase by at least 2%, stop, otherwise check next + // if N and N+1 have mostly the same hash rate, we want to "lighter" one + bool found = false; + if (m_threads_autodetect.size() > 1) + { + int previdx = m_threads_autodetect.size() - 2; + float previous_hs = m_threads_autodetect[previdx].second / (m_threads_autodetect[previdx].first / (float)1000000000); + if (previous_hs > 0 && hs / previous_hs < AUTODETECT_GAIN_THRESHOLD) + { + m_threads_total = m_threads_autodetect.size() - 1; + m_threads_autodetect.clear(); + MGINFO("Optimal number of threads seems to be " << m_threads_total); + found = true; + } + } + + if (!found) + { + // setup one more thread + m_threads_autodetect.push_back({now, m_total_hashes}); + m_threads_total = m_threads_autodetect.size(); + } + + // restart all threads + { + CRITICAL_REGION_LOCAL(m_threads_lock); + boost::interprocess::ipcdetail::atomic_write32(&m_stop, 1); + for(boost::thread& th: m_threads) + th.join(); + m_threads.clear(); + } + boost::interprocess::ipcdetail::atomic_write32(&m_stop, 0); + boost::interprocess::ipcdetail::atomic_write32(&m_thread_index, 0); + for(size_t i = 0; i != m_threads_total; i++) + m_threads.push_back(boost::thread(m_attrs, boost::bind(&miner::worker_thread, this))); + } + //----------------------------------------------------------------------------------------------------- void miner::init_options(boost::program_options::options_description& desc) { command_line::add_arg(desc, arg_extra_messages); @@ -297,6 +360,13 @@ namespace cryptonote { m_mine_address = adr; m_threads_total = static_cast<uint32_t>(threads_count); + if (threads_count == 0) + { + m_threads_autodetect.clear(); + m_threads_autodetect.push_back({epee::misc_utils::get_ns_count(), m_total_hashes}); + m_threads_total = 1; + } + m_attrs = attrs; m_starter_nonce = crypto::rand<uint32_t>(); CRITICAL_REGION_LOCAL(m_threads_lock); if(is_mining()) @@ -318,12 +388,15 @@ namespace cryptonote set_is_background_mining_enabled(do_background); set_ignore_battery(ignore_battery); - for(size_t i = 0; i != threads_count; i++) + for(size_t i = 0; i != m_threads_total; i++) { m_threads.push_back(boost::thread(attrs, boost::bind(&miner::worker_thread, this))); } - LOG_PRINT_L0("Mining has started with " << threads_count << " threads, good luck!" ); + if (threads_count == 0) + MINFO("Mining has started, autodetecting optimal number of threads, good luck!" ); + else + MINFO("Mining has started with " << threads_count << " threads, good luck!" ); if( get_is_background_mining_enabled() ) { @@ -360,7 +433,7 @@ namespace cryptonote if (!is_mining()) { - MDEBUG("Not mining - nothing to stop" ); + MTRACE("Not mining - nothing to stop" ); return true; } @@ -381,6 +454,7 @@ namespace cryptonote MINFO("Mining has been stopped, " << m_threads.size() << " finished" ); m_threads.clear(); + m_threads_autodetect.clear(); return true; } //----------------------------------------------------------------------------------------------------- @@ -506,6 +580,7 @@ namespace cryptonote } nonce+=m_threads_total; ++m_hashes; + ++m_total_hashes; } slow_hash_free_state(); MGINFO("Miner thread stopped ["<< th_local_index << "]"); diff --git a/src/cryptonote_basic/miner.h b/src/cryptonote_basic/miner.h index e16d9f3b8..d4cdb2363 100644 --- a/src/cryptonote_basic/miner.h +++ b/src/cryptonote_basic/miner.h @@ -103,6 +103,7 @@ namespace cryptonote bool worker_thread(); bool request_block_template(); void merge_hr(); + void update_autodetection(); struct miner_config { @@ -132,16 +133,20 @@ namespace cryptonote account_public_address m_mine_address; epee::math_helper::once_a_time_seconds<5> m_update_block_template_interval; epee::math_helper::once_a_time_seconds<2> m_update_merge_hr_interval; + epee::math_helper::once_a_time_seconds<1> m_autodetect_interval; std::vector<blobdata> m_extra_messages; miner_config m_config; std::string m_config_folder_path; std::atomic<uint64_t> m_last_hr_merge_time; std::atomic<uint64_t> m_hashes; + std::atomic<uint64_t> m_total_hashes; std::atomic<uint64_t> m_current_hash_rate; epee::critical_section m_last_hash_rates_lock; std::list<uint64_t> m_last_hash_rates; bool m_do_print_hashrate; bool m_do_mining; + std::vector<std::pair<uint64_t, uint64_t>> m_threads_autodetect; + boost::thread::attributes m_attrs; // background mining stuffs .. diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 93db71705..956cc76aa 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -59,6 +59,8 @@ #define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 60000 //size of block (bytes) after which reward for block calculated using block size #define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 20000 //size of block (bytes) after which reward for block calculated using block size - before first fork #define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 300000 //size of block (bytes) after which reward for block calculated using block size - second change, from v5 +#define CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE 100000 // size in blocks of the long term block weight median window +#define CRYPTONOTE_SHORT_TERM_BLOCK_WEIGHT_SURGE_FACTOR 50 #define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE 600 #define CRYPTONOTE_DISPLAY_DECIMAL_POINT 12 // COIN - number of smallest units in one coin @@ -108,7 +110,7 @@ #define P2P_DEFAULT_PACKET_MAX_SIZE 50000000 //50000000 bytes maximum packet size #define P2P_DEFAULT_PEERS_IN_HANDSHAKE 250 #define P2P_DEFAULT_CONNECTION_TIMEOUT 5000 //5 seconds -#define P2P_DEFAULT_TOR_CONNECT_TIMEOUT 20 // seconds +#define P2P_DEFAULT_SOCKS_CONNECT_TIMEOUT 45 // seconds #define P2P_DEFAULT_PING_CONNECTION_TIMEOUT 2000 //2 seconds #define P2P_DEFAULT_INVOKE_TIMEOUT 60*2*1000 //2 minutes #define P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT 5000 //5 seconds @@ -143,6 +145,7 @@ #define HF_VERSION_ENFORCE_RCT 6 #define HF_VERSION_PER_BYTE_FEE 8 #define HF_VERSION_SMALLER_BP 10 +#define HF_VERSION_LONG_TERM_BLOCK_WEIGHT 10 #define PER_KB_FEE_QUANTIZATION_DECIMALS 8 diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 41357e72e..8fc401851 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -115,6 +115,12 @@ static const struct { // version 9 starts from block 1686275, which is on or around the 19th of October, 2018. Fork time finalised on 2018-09-02. { 9, 1686275, 0, 1535889548 }, + + // version 10 starts from block 1788000, which is on or around the 9th of March, 2019. Fork time finalised on 2019-02-10. + { 10, 1788000, 0, 1549792439 }, + + // version 11 starts from block 1788720, which is on or around the 10th of March, 2019. Fork time finalised on 2019-02-15. + { 11, 1788720, 0, 1550225678 }, }; static const uint64_t mainnet_hard_fork_version_1_till = 1009826; @@ -139,6 +145,8 @@ static const struct { { 7, 1057027, 0, 1512211236 }, { 8, 1057058, 0, 1533211200 }, { 9, 1057778, 0, 1533297600 }, + { 10, 1154318, 0, 1550153694 }, + { 11, 1155038, 0, 1550225678 }, }; static const uint64_t testnet_hard_fork_version_1_till = 624633; @@ -160,12 +168,16 @@ static const struct { { 7, 37000, 0, 1521600000 }, { 8, 176456, 0, 1537821770 }, { 9, 177176, 0, 1537821771 }, + { 10, 269000, 0, 1550153694 }, + { 11, 269720, 0, 1550225678 }, }; //------------------------------------------------------------------ Blockchain::Blockchain(tx_memory_pool& tx_pool) : m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_weight_limit(0), m_current_block_cumul_weight_median(0), m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_sync_on_blocks(true), m_db_sync_threshold(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_bytes_to_sync(0), m_cancel(false), + m_long_term_block_weights_window(CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE), + m_long_term_effective_median_block_weight(0), m_difficulty_for_next_block_top_hash(crypto::null_hash), m_difficulty_for_next_block(1), m_btc_valid(false) @@ -435,9 +447,9 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline uint64_t top_block_timestamp = m_db->get_top_block_timestamp(); uint64_t timestamp_diff = time(NULL) - top_block_timestamp; - // genesis block has no timestamp, could probably change it to have timestamp of 1341378000... + // genesis block has no timestamp, could probably change it to have timestamp of 1397818133... if(!top_block_timestamp) - timestamp_diff = time(NULL) - 1341378000; + timestamp_diff = time(NULL) - 1397818133; // create general purpose async service queue @@ -500,7 +512,11 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id()); } - update_next_cumulative_weight_limit(); + if (test_options && test_options->long_term_block_weight_window) + m_long_term_block_weights_window = test_options->long_term_block_weight_window; + + if (!update_next_cumulative_weight_limit()) + return false; return true; } //------------------------------------------------------------------ @@ -685,7 +701,7 @@ block Blockchain::pop_block_from_blockchain() m_blocks_txs_check.clear(); m_check_txin_table.clear(); - update_next_cumulative_weight_limit(); + CHECK_AND_ASSERT_THROW_MES(update_next_cumulative_weight_limit(), "Error updating next cumulative weight limit"); m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id()); invalidate_block_template_cache(); @@ -704,7 +720,8 @@ bool Blockchain::reset_and_set_genesis_block(const block& b) block_verification_context bvc = boost::value_initialized<block_verification_context>(); add_new_block(b, bvc); - update_next_cumulative_weight_limit(); + if (!update_next_cumulative_weight_limit()) + return false; return bvc.m_added_to_main_chain && !bvc.m_verifivation_failed; } //------------------------------------------------------------------ @@ -1049,6 +1066,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash:: } // if we're to keep the disconnected blocks, add them as alternates + const size_t discarded_blocks = disconnected_chain.size(); if(!discard_disconnected_chain) { //pushing old chain as alternative chain @@ -1076,7 +1094,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash:: std::shared_ptr<tools::Notify> reorg_notify = m_reorg_notify; if (reorg_notify) reorg_notify->notify("%s", std::to_string(split_height).c_str(), "%h", std::to_string(m_db->height()).c_str(), - "%n", std::to_string(m_db->height() - split_height).c_str(), NULL); + "%n", std::to_string(m_db->height() - split_height).c_str(), "%d", std::to_string(discarded_blocks).c_str(), NULL); MGINFO_GREEN("REORGANIZE SUCCESS! on height: " << split_height << ", new blockchain size: " << m_db->height()); return true; @@ -1202,7 +1220,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl } } - std::vector<size_t> last_blocks_weights; + std::vector<uint64_t> last_blocks_weights; get_last_n_blocks_weights(last_blocks_weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW); if (!get_block_reward(epee::misc_utils::median(last_blocks_weights), cumulative_block_weight, already_generated_coins, base_reward, version)) { @@ -1237,7 +1255,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl } //------------------------------------------------------------------ // get the block weights of the last <count> blocks, and return by reference <sz>. -void Blockchain::get_last_n_blocks_weights(std::vector<size_t>& weights, size_t count) const +void Blockchain::get_last_n_blocks_weights(std::vector<uint64_t>& weights, size_t count) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -2527,7 +2545,19 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context if (tx.version >= 2) { if (tx.rct_signatures.type == rct::RCTTypeBulletproof2) { - MERROR_VER("Bulletproofs v2 are not allowed before v" << HF_VERSION_SMALLER_BP); + MERROR_VER("Ringct type " << (unsigned)rct::RCTTypeBulletproof2 << " is not allowed before v" << HF_VERSION_SMALLER_BP); + tvc.m_invalid_output = true; + return false; + } + } + } + + // from v11, allow only bulletproofs v2 + if (hf_version > HF_VERSION_SMALLER_BP) { + if (tx.version >= 2) { + if (tx.rct_signatures.type == rct::RCTTypeBulletproof) + { + MERROR_VER("Ringct type " << (unsigned)rct::RCTTypeBulletproof << " is not allowed from v" << (HF_VERSION_SMALLER_BP + 1)); tvc.m_invalid_output = true; return false; } @@ -2600,7 +2630,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr for (size_t n = 0; n < tx.vin.size(); ++n) rv.p.MGs[0].II[n] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image); } - else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rct::RCTTypeBulletproof2) + else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2) { CHECK_AND_ASSERT_MES(rv.p.MGs.size() == tx.vin.size(), false, "Bad MGs size"); for (size_t n = 0; n < tx.vin.size(); ++n) @@ -3083,6 +3113,7 @@ 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; @@ -3090,7 +3121,7 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const if (version >= HF_VERSION_DYNAMIC_FEE) { median = m_current_block_cumul_weight_limit / 2; - already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0; + already_generated_coins = blockchain_height ? m_db->get_block_already_generated_coins(blockchain_height - 1) : 0; if (!get_block_reward(median, 1, already_generated_coins, base_reward, version)) return false; } @@ -3098,7 +3129,8 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const uint64_t needed_fee; if (version >= HF_VERSION_PER_BYTE_FEE) { - uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, median, version); + const bool use_long_term_median_in_fee = version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT; + uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, use_long_term_median_in_fee ? m_long_term_effective_median_block_weight : median, version); MDEBUG("Using " << print_money(fee_per_byte) << "/byte fee"); needed_fee = tx_weight * fee_per_byte; // quantize fee up to 8 decimals @@ -3135,6 +3167,7 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const { const uint8_t version = get_current_hard_fork_version(); + const uint64_t db_height = m_db->height(); if (version < HF_VERSION_DYNAMIC_FEE) return FEE_PER_KB; @@ -3143,7 +3176,7 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1; const uint64_t min_block_weight = get_min_block_weight(version); - std::vector<size_t> weights; + std::vector<uint64_t> weights; get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW - grace_blocks); weights.reserve(grace_blocks); for (size_t i = 0; i < grace_blocks; ++i) @@ -3153,7 +3186,7 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const if(median <= min_block_weight) median = min_block_weight; - uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0; + uint64_t already_generated_coins = db_height ? m_db->get_block_already_generated_coins(db_height - 1) : 0; uint64_t base_reward; if (!get_block_reward(median, 1, already_generated_coins, base_reward, version)) { @@ -3161,7 +3194,8 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const base_reward = BLOCK_REWARD_OVERESTIMATE; } - uint64_t fee = get_dynamic_base_fee(base_reward, median, version); + const bool use_long_term_median_in_fee = version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT; + uint64_t fee = get_dynamic_base_fee(base_reward, use_long_term_median_in_fee ? m_long_term_effective_median_block_weight : median, version); const bool per_byte = version < HF_VERSION_PER_BYTE_FEE; MDEBUG("Estimating " << grace_blocks << "-block fee at " << print_money(fee) << "/" << (per_byte ? "byte" : "kB")); return fee; @@ -3658,7 +3692,8 @@ leave: { try { - new_height = m_db->add_block(bl, block_weight, cumulative_difficulty, already_generated_coins, txs); + uint64_t long_term_block_weight = get_next_long_term_block_weight(block_weight); + new_height = m_db->add_block(bl, block_weight, long_term_block_weight, cumulative_difficulty, already_generated_coins, txs); } catch (const KEY_IMAGE_EXISTS& e) { @@ -3684,7 +3719,12 @@ leave: TIME_MEASURE_FINISH(addblock); // do this after updating the hard fork state since the weight limit may change due to fork - update_next_cumulative_weight_limit(); + if (!update_next_cumulative_weight_limit()) + { + MERROR("Failed to update next cumulative weight limit"); + pop_block_from_blockchain(); + return false; + } MINFO("+++++ BLOCK SUCCESSFULLY ADDED" << std::endl << "id:\t" << id << std::endl << "PoW:\t" << proof_of_work << std::endl << "HEIGHT " << new_height-1 << ", difficulty:\t" << current_diffic << std::endl << "block reward: " << print_money(fee_summary + base_reward) << "(" << print_money(base_reward) << " + " << print_money(fee_summary) << "), coinbase_weight: " << coinbase_weight << ", cumulative weight: " << cumulative_block_weight << ", " << block_processing_time << "(" << target_calculating_time << "/" << longhash_calculating_time << ")ms"); if(m_show_time_stats) @@ -3740,20 +3780,100 @@ bool Blockchain::check_blockchain_pruning() return m_db->check_pruning(); } //------------------------------------------------------------------ -bool Blockchain::update_next_cumulative_weight_limit() +uint64_t Blockchain::get_next_long_term_block_weight(uint64_t block_weight) const { - uint64_t full_reward_zone = get_min_block_weight(get_current_hard_fork_version()); + PERF_TIMER(get_next_long_term_block_weight); + + const uint64_t db_height = m_db->height(); + const uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights_window, db_height); + + const uint8_t hf_version = get_current_hard_fork_version(); + if (hf_version < HF_VERSION_LONG_TERM_BLOCK_WEIGHT) + return block_weight; + + std::vector<uint64_t> weights; + weights.resize(nblocks); + for (uint64_t h = 0; h < nblocks; ++h) + weights[h] = m_db->get_block_long_term_weight(db_height - nblocks + h); + uint64_t long_term_median = epee::misc_utils::median(weights); + uint64_t 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 = long_term_effective_median_block_weight + long_term_effective_median_block_weight * 2 / 5; + uint64_t long_term_block_weight = std::min<uint64_t>(block_weight, short_term_constraint); + + return long_term_block_weight; +} +//------------------------------------------------------------------ +bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effective_median_block_weight) +{ + PERF_TIMER(update_next_cumulative_weight_limit); LOG_PRINT_L3("Blockchain::" << __func__); - std::vector<size_t> weights; - get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW); - uint64_t median = epee::misc_utils::median(weights); - m_current_block_cumul_weight_median = median; - if(median <= full_reward_zone) - median = full_reward_zone; + // when we reach this, the last hf version is not yet written to the db + 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 + { + const uint64_t block_weight = m_db->get_block_weight(db_height - 1); + + std::vector<uint64_t> weights, new_weights; + uint64_t long_term_median; + if (db_height == 1) + { + long_term_median = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5; + } + else + { + uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights_window, db_height); + if (nblocks == db_height) + --nblocks; + weights.resize(nblocks); + for (uint64_t h = 0; h < nblocks; ++h) + weights[h] = m_db->get_block_long_term_weight(db_height - nblocks + h - 1); + new_weights = weights; + long_term_median = epee::misc_utils::median(weights); + } + + 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); + + if (new_weights.empty()) + new_weights.resize(1); + new_weights[0] = long_term_block_weight; + long_term_median = epee::misc_utils::median(new_weights); + m_long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median); + short_term_constraint = m_long_term_effective_median_block_weight + m_long_term_effective_median_block_weight * 2 / 5; + + weights.clear(); + get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW); + + uint64_t short_term_median = epee::misc_utils::median(weights); + uint64_t effective_median_block_weight = std::min<uint64_t>(std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, short_term_median), CRYPTONOTE_SHORT_TERM_BLOCK_WEIGHT_SURGE_FACTOR * m_long_term_effective_median_block_weight); + + m_current_block_cumul_weight_median = effective_median_block_weight; + } + + if (m_current_block_cumul_weight_median <= full_reward_zone) + m_current_block_cumul_weight_median = full_reward_zone; + + m_current_block_cumul_weight_limit = m_current_block_cumul_weight_median * 2; + + if (long_term_effective_median_block_weight) + *long_term_effective_median_block_weight = m_long_term_effective_median_block_weight; - m_current_block_cumul_weight_limit = median*2; return true; } //------------------------------------------------------------------ diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index c742bec91..92aef1278 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -37,6 +37,7 @@ #include <boost/multi_index/global_fun.hpp> #include <boost/multi_index/hashed_index.hpp> #include <boost/multi_index/member.hpp> +#include <boost/circular_buffer.hpp> #include <atomic> #include <functional> #include <unordered_map> @@ -631,6 +632,13 @@ namespace cryptonote uint64_t get_current_cumulative_block_weight_limit() const; /** + * @brief gets the long term block weight for a new block + * + * @return the long term block weight + */ + uint64_t get_next_long_term_block_weight(uint64_t block_weight) const; + + /** * @brief gets the block weight median based on recent blocks (same window as for the limit) * * @return the median @@ -994,7 +1002,9 @@ namespace cryptonote */ void pop_blocks(uint64_t nblocks); +#ifndef IN_UNIT_TESTS private: +#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; @@ -1047,6 +1057,8 @@ namespace cryptonote std::vector<uint64_t> m_timestamps; std::vector<difficulty_type> m_difficulties; uint64_t m_timestamps_and_difficulties_height; + uint64_t m_long_term_block_weights_window; + uint64_t m_long_term_effective_median_block_weight; epee::critical_section m_difficulty_lock; crypto::hash m_difficulty_for_next_block_top_hash; @@ -1280,7 +1292,7 @@ namespace cryptonote * @param sz return-by-reference the list of weights * @param count the number of blocks to get weights for */ - void get_last_n_blocks_weights(std::vector<size_t>& weights, size_t count) const; + void get_last_n_blocks_weights(std::vector<uint64_t>& weights, size_t count) const; /** * @brief checks if a transaction is unlocked (its outputs spendable) @@ -1379,9 +1391,11 @@ namespace cryptonote /** * @brief calculate the block weight limit for the next block to be added * + * @param long_term_effective_median_block_weight optionally return that value + * * @return true */ - bool update_next_cumulative_weight_limit(); + bool update_next_cumulative_weight_limit(uint64_t *long_term_effective_median_block_weight = NULL); void return_tx_to_pool(std::vector<transaction> &txs); /** diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 1513bb750..599f42774 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -188,8 +188,21 @@ namespace cryptonote static const command_line::arg_descriptor<std::string> arg_reorg_notify = { "reorg-notify" , "Run a program for each reorg, '%s' will be replaced by the split height, " - "'%h' will be replaced by the new blockchain height, and '%n' will be " - "replaced by the number of new blocks in the new chain" + "'%h' will be replaced by the new blockchain height, '%n' will be " + "replaced by the number of new blocks in the new chain, and '%d' will be " + "replaced by the number of blocks discarded from the old chain" + , "" + }; + static const command_line::arg_descriptor<std::string> arg_block_rate_notify = { + "block-rate-notify" + , "Run a program when the block rate undergoes large fluctuations. This might " + "be a sign of large amounts of hash rate going on and off the Monero network, " + "and thus be of potential interest in predicting attacks. %t will be replaced " + "by the number of minutes for the observation window, %b by the number of " + "blocks observed within that window, and %e by the number of blocks that was " + "expected in that window. It is suggested that this notification is used to " + "automatically increase the number of confirmations required before a payment " + "is acted upon." , "" }; @@ -308,6 +321,7 @@ namespace cryptonote command_line::add_arg(desc, arg_block_notify); 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); miner::init_options(desc); BlockchainDB::init_options(desc); @@ -587,7 +601,7 @@ namespace cryptonote } catch (const std::exception &e) { - MERROR("Failed to parse block notify spec"); + MERROR("Failed to parse block notify spec: " << e.what()); } try @@ -597,12 +611,23 @@ namespace cryptonote } catch (const std::exception &e) { - MERROR("Failed to parse reorg notify spec"); + MERROR("Failed to parse reorg notify spec: " << e.what()); + } + + try + { + if (!command_line::is_arg_defaulted(vm, arg_block_rate_notify)) + m_block_rate_notify.reset(new tools::Notify(command_line::get_arg(vm, arg_block_rate_notify).c_str())); + } + catch (const std::exception &e) + { + MERROR("Failed to parse block rate notify spec: " << e.what()); } const std::pair<uint8_t, uint64_t> regtest_hard_forks[3] = {std::make_pair(1, 0), std::make_pair(Blockchain::get_hard_fork_heights(MAINNET).back().version, 1), std::make_pair(0, 0)}; const cryptonote::test_options regtest_test_options = { - regtest_hard_forks + regtest_hard_forks, + 0 }; const difficulty_type fixed_difficulty = command_line::get_arg(vm, arg_fixed_difficulty); r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? ®test_test_options : test_options, fixed_difficulty, get_checkpoints); @@ -1764,7 +1789,7 @@ namespace cryptonote const time_t now = time(NULL); const std::vector<time_t> timestamps = m_blockchain_storage.get_last_block_timestamps(60); - static const unsigned int seconds[] = { 5400, 1800, 600 }; + static const unsigned int seconds[] = { 5400, 3600, 1800, 1200, 600 }; for (size_t n = 0; n < sizeof(seconds)/sizeof(seconds[0]); ++n) { unsigned int b = 0; @@ -1775,6 +1800,14 @@ namespace cryptonote if (p < threshold) { MWARNING("There were " << b << " blocks in the last " << seconds[n] / 60 << " minutes, there might be large hash rate changes, or we might be partitioned, cut off from the Monero network or under attack. Or it could be just sheer bad luck."); + + std::shared_ptr<tools::Notify> block_rate_notify = m_block_rate_notify; + if (block_rate_notify) + { + auto expected = seconds[n] / DIFFICULTY_TARGET_V2; + block_rate_notify->notify("%t", std::to_string(seconds[n] / 60).c_str(), "%b", std::to_string(b).c_str(), "%e", std::to_string(expected).c_str(), NULL); + } + break; // no need to look further } } diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 4810fc891..79d06662e 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -54,6 +54,7 @@ namespace cryptonote { struct test_options { const std::pair<uint8_t, uint64_t> *hard_forks; + const size_t long_term_block_weight_window; }; extern const command_line::arg_descriptor<std::string, false, true, 2> arg_data_dir; @@ -1061,6 +1062,8 @@ namespace cryptonote bool m_fluffy_blocks_enabled; bool m_offline; bool m_pad_transactions; + + std::shared_ptr<tools::Notify> m_block_rate_notify; }; } diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 0a04e0d38..c138c7854 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -405,49 +405,12 @@ namespace cryptonote for(const tx_destination_entry& dst_entr: destinations) { CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount); - crypto::key_derivation derivation; crypto::public_key out_eph_public_key; - // make additional tx pubkey if necessary - keypair additional_txkey; - if (need_additional_txkeys) - { - additional_txkey.sec = additional_tx_keys[output_index]; - if (dst_entr.is_subaddress) - additional_txkey.pub = rct::rct2pk(hwdev.scalarmultKey(rct::pk2rct(dst_entr.addr.m_spend_public_key), rct::sk2rct(additional_txkey.sec))); - else - additional_txkey.pub = rct::rct2pk(hwdev.scalarmultBase(rct::sk2rct(additional_txkey.sec))); - } - - bool r; - if (change_addr && dst_entr.addr == *change_addr) - { - // sending change to yourself; derivation = a*R - r = hwdev.generate_key_derivation(txkey_pub, sender_account_keys.m_view_secret_key, derivation); - CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << txkey_pub << ", " << sender_account_keys.m_view_secret_key << ")"); - } - else - { - // sending to the recipient; derivation = r*A (or s*C in the subaddress scheme) - r = hwdev.generate_key_derivation(dst_entr.addr.m_view_public_key, dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : tx_key, derivation); - CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << (dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : tx_key) << ")"); - } - - if (need_additional_txkeys) - { - additional_tx_public_keys.push_back(additional_txkey.pub); - } - - if (tx.version > 1) - { - crypto::secret_key scalar1; - hwdev.derivation_to_scalar(derivation, output_index, scalar1); - amount_keys.push_back(rct::sk2rct(scalar1)); - } - r = hwdev.derive_public_key(derivation, output_index, dst_entr.addr.m_spend_public_key, out_eph_public_key); - CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to derive_public_key(" << derivation << ", " << output_index << ", "<< dst_entr.addr.m_spend_public_key << ")"); - - hwdev.add_output_key_mapping(dst_entr.addr.m_view_public_key, dst_entr.addr.m_spend_public_key, dst_entr.is_subaddress, output_index, amount_keys.back(), out_eph_public_key); + hwdev.generate_output_ephemeral_keys(tx.version,sender_account_keys, txkey_pub, tx_key, + dst_entr, change_addr, output_index, + need_additional_txkeys, additional_tx_keys, + additional_tx_public_keys, amount_keys, out_eph_public_key); tx_out out; out.amount = dst_entr.amount; diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index 85061668b..ad3297951 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -97,6 +97,12 @@ namespace cryptonote bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time); bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, rct::multisig_out *msout = NULL, bool shuffle_outs = true); bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, rct::multisig_out *msout = NULL); + bool generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key, + const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index, + const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys, + std::vector<crypto::public_key> &additional_tx_public_keys, + std::vector<rct::key> &amount_keys, + crypto::public_key &out_eph_public_key) ; bool generate_genesis_block( block& bl diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 670d70d77..8dd0337f0 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -61,7 +61,7 @@ namespace cryptonote class txCompare { public: - bool operator()(const tx_by_fee_and_receive_time_entry& a, const tx_by_fee_and_receive_time_entry& b) + bool operator()(const tx_by_fee_and_receive_time_entry& a, const tx_by_fee_and_receive_time_entry& b) const { // sort by greatest first, not least if (a.first.first > b.first.first) return true; diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index c49371d48..2d5d10d67 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -48,6 +48,7 @@ namespace cryptonote bool incoming; bool localhost; bool local_ip; + bool ssl; std::string address; std::string host; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index c1459cbb6..8fada4e3c 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -268,6 +268,7 @@ namespace cryptonote cnx.current_upload = cntxt.m_current_speed_up / 1024; cnx.connection_id = epee::string_tools::pod_to_hex(cntxt.m_connection_id); + cnx.ssl = cntxt.m_ssl; cnx.height = cntxt.m_remote_blockchain_height; cnx.pruning_seed = cntxt.m_pruning_seed; @@ -2111,7 +2112,7 @@ skip: MDEBUG("Attempting to conceal origin of tx via anonymity network connection(s)"); // no check for success, so tell core they're relayed unconditionally - const bool pad_transactions = m_core.pad_transactions(); + const bool pad_transactions = m_core.pad_transactions() || hide_tx_broadcast; size_t bytes = pad_transactions ? 9 /* header */ + 4 /* 1 + 'txs' */ + tools::get_varint_data(arg.txs.size()).size() : 0; for(auto tx_blob_it = arg.txs.begin(); tx_blob_it!=arg.txs.end(); ++tx_blob_it) { diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 73f33a674..00d004970 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -302,7 +302,7 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg { if(!args.size()) { - std::cout << "Please specify a wallet address to mine for: start_mining <addr> [<threads>]" << std::endl; + std::cout << "Please specify a wallet address to mine for: start_mining <addr> [<threads>|auto]" << std::endl; return true; } @@ -388,8 +388,15 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg if(args.size() >= 2) { - bool ok = epee::string_tools::get_xtype_from_string(threads_count, args[1]); - threads_count = (ok && 0 < threads_count) ? threads_count : 1; + if (args[1] == "auto" || args[1] == "autodetect") + { + threads_count = 0; + } + else + { + bool ok = epee::string_tools::get_xtype_from_string(threads_count, args[1]); + threads_count = (ok && 0 < threads_count) ? threads_count : 1; + } } m_executor.start_mining(info.address, threads_count, nettype, do_background_mining, ignore_battery); diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 96dea76b6..3e19bb42f 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -104,8 +104,8 @@ t_command_server::t_command_server( m_command_lookup.set_handler( "start_mining" , std::bind(&t_command_parser_executor::start_mining, &m_parser, p::_1) - , "start_mining <addr> [<threads>] [do_background_mining] [ignore_battery]" - , "Start mining for specified address. Defaults to 1 thread and no background mining." + , "start_mining <addr> [<threads>|auto] [do_background_mining] [ignore_battery]" + , "Start mining for specified address. Defaults to 1 thread and no background mining. Use \"auto\" to autodetect optimal number of threads." ); m_command_lookup.set_handler( "stop_mining" diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 839350522..0a35dcef9 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -79,6 +79,7 @@ namespace { << "POW hash: " << header.pow_hash << std::endl << "block size: " << header.block_size << std::endl << "block weight: " << header.block_weight << std::endl + << "long term weight: " << header.long_term_weight << std::endl << "num txes: " << header.num_txes << std::endl << "reward: " << cryptonote::print_money(header.reward); } @@ -510,6 +511,7 @@ bool t_rpc_command_executor::print_connections() { } tools::msg_writer() << std::setw(30) << std::left << "Remote Host" + << std::setw(6) << "SSL" << std::setw(20) << "Peer id" << std::setw(20) << "Support Flags" << std::setw(30) << "Recv/Sent (inactive,sec)" @@ -529,6 +531,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(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 << std::setw(30) << std::to_string(info.recv_count) + "(" + std::to_string(info.recv_idle_time) + ")/" + std::to_string(info.send_count) + "(" + std::to_string(info.send_idle_time) + ")" diff --git a/src/device/device.hpp b/src/device/device.hpp index bdb608907..408f64c8b 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -69,6 +69,7 @@ namespace cryptonote struct account_public_address; struct account_keys; struct subaddress_index; + struct tx_destination_entry; } namespace hw { @@ -211,9 +212,12 @@ namespace hw { virtual bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec, bool short_amount) = 0; virtual bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec, bool short_amount) = 0; - virtual bool add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const size_t real_output_index, - const rct::key &amount_key, const crypto::public_key &out_eph_public_key) = 0; - + virtual bool generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key, + const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index, + const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys, + std::vector<crypto::public_key> &additional_tx_public_keys, + std::vector<rct::key> &amount_keys, + crypto::public_key &out_eph_public_key) = 0; virtual bool mlsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash) = 0; virtual bool mlsag_prepare(const rct::key &H, const rct::key &xx, rct::key &a, rct::key &aG, rct::key &aHP, rct::key &rvII) = 0; diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp index cb2f4e266..fd15717a7 100644 --- a/src/device/device_default.cpp +++ b/src/device/device_default.cpp @@ -34,8 +34,10 @@ #include "int-util.h" #include "cryptonote_basic/account.h" #include "cryptonote_basic/subaddress_index.h" +#include "cryptonote_core/cryptonote_tx_utils.h" #include "ringct/rctOps.h" +#include "log.hpp" #define ENCRYPTED_PAYMENT_ID_TAIL 0x8d #define CHACHA8_KEY_TAIL 0x8c @@ -278,10 +280,55 @@ namespace hw { return true; } + bool device_default::generate_output_ephemeral_keys(const size_t tx_version, + const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key, + const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index, + const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys, + std::vector<crypto::public_key> &additional_tx_public_keys, + std::vector<rct::key> &amount_keys, crypto::public_key &out_eph_public_key) { - bool device_default::add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const size_t real_output_index, - const rct::key &amount_key, const crypto::public_key &out_eph_public_key) { - return true; + crypto::key_derivation derivation; + + // make additional tx pubkey if necessary + cryptonote::keypair additional_txkey; + if (need_additional_txkeys) + { + additional_txkey.sec = additional_tx_keys[output_index]; + if (dst_entr.is_subaddress) + additional_txkey.pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(dst_entr.addr.m_spend_public_key), rct::sk2rct(additional_txkey.sec))); + else + additional_txkey.pub = rct::rct2pk(rct::scalarmultBase(rct::sk2rct(additional_txkey.sec))); + } + + bool r; + if (change_addr && dst_entr.addr == *change_addr) + { + // sending change to yourself; derivation = a*R + r = generate_key_derivation(txkey_pub, sender_account_keys.m_view_secret_key, derivation); + CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << txkey_pub << ", " << sender_account_keys.m_view_secret_key << ")"); + } + else + { + // sending to the recipient; derivation = r*A (or s*C in the subaddress scheme) + r = generate_key_derivation(dst_entr.addr.m_view_public_key, dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : tx_key, derivation); + CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << (dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : tx_key) << ")"); + } + + if (need_additional_txkeys) + { + additional_tx_public_keys.push_back(additional_txkey.pub); + } + + if (tx_version > 1) + { + crypto::secret_key scalar1; + derivation_to_scalar(derivation, output_index, scalar1); + amount_keys.push_back(rct::sk2rct(scalar1)); + } + r = derive_public_key(derivation, output_index, dst_entr.addr.m_spend_public_key, out_eph_public_key); + CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to derive_public_key(" << derivation << ", " << output_index << ", "<< dst_entr.addr.m_spend_public_key << ")"); + + return r; } bool device_default::encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) { diff --git a/src/device/device_default.hpp b/src/device/device_default.hpp index 54d159b11..04b9b4234 100644 --- a/src/device/device_default.hpp +++ b/src/device/device_default.hpp @@ -114,9 +114,12 @@ namespace hw { bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec, bool short_amount) override; bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec, bool short_amount) override; - bool add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const size_t real_output_index, - const rct::key &amount_key, const crypto::public_key &out_eph_public_key) override; - + bool generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key, + const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index, + const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys, + std::vector<crypto::public_key> &additional_tx_public_keys, + std::vector<rct::key> &amount_keys, + crypto::public_key &out_eph_public_key) override; bool mlsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash) override; bool mlsag_prepare(const rct::key &H, const rct::key &xx, rct::key &a, rct::key &aG, rct::key &aHP, rct::key &rvII) override; diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 9daf62d37..1f91427f0 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -32,6 +32,7 @@ #include "ringct/rctOps.h" #include "cryptonote_basic/account.h" #include "cryptonote_basic/subaddress_index.h" +#include "cryptonote_core/cryptonote_tx_utils.h" #include <boost/thread/locks.hpp> #include <boost/thread/lock_guard.hpp> @@ -67,10 +68,12 @@ namespace hw { /* === Keymap ==== */ /* ===================================================================== */ - ABPkeys::ABPkeys(const rct::key& A, const rct::key& B, const bool is_subaddr, const size_t real_output_index, const rct::key& P, const rct::key& AK) { + ABPkeys::ABPkeys(const rct::key& A, const rct::key& B, const bool is_subaddr, const bool is_change, const bool need_additional_txkeys, const size_t real_output_index, const rct::key& P, const rct::key& AK) { Aout = A; Bout = B; is_subaddress = is_subaddr; + is_change_address = is_change; + additional_key = need_additional_txkeys; index = real_output_index; Pout = P; AKout = AK; @@ -80,6 +83,8 @@ namespace hw { Aout = keys.Aout; Bout = keys.Bout; is_subaddress = keys.is_subaddress; + is_change_address = keys.is_change_address; + additional_key = keys.additional_key; index = keys.index; Pout = keys.Pout; AKout = keys.AKout; @@ -137,6 +142,8 @@ namespace hw { static int device_id = 0; + #define PROTOCOL_VERSION 2 + #define INS_NONE 0x00 #define INS_RESET 0x02 @@ -168,6 +175,7 @@ namespace hw { #define INS_STEALTH 0x76 #define INS_BLIND 0x78 #define INS_UNBLIND 0x7A + #define INS_GEN_TXOUT_KEYS 0x7B #define INS_VALIDATE 0x7C #define INS_MLSAG 0x7E #define INS_CLOSE_TX 0x80 @@ -268,7 +276,7 @@ namespace hw { int device_ledger::set_command_header(unsigned char ins, unsigned char p1, unsigned char p2) { reset_buffer(); int offset = 0; - this->buffer_send[0] = 0x00; + this->buffer_send[0] = PROTOCOL_VERSION; this->buffer_send[1] = ins; this->buffer_send[2] = p1; this->buffer_send[3] = p2; @@ -508,11 +516,11 @@ namespace hw { } const std::size_t output_index_x = output_index; crypto::public_key derived_pub_x; - hw::ledger::log_hexbuffer("derive_subaddress_public_key: [[IN]] pub ", pub_x.data, 32); - hw::ledger::log_hexbuffer("derive_subaddress_public_key: [[IN]] derivation", derivation_x.data, 32); - hw::ledger::log_message ("derive_subaddress_public_key: [[IN]] index ", std::to_string((int)output_index_x)); + log_hexbuffer("derive_subaddress_public_key: [[IN]] pub ", pub_x.data, 32); + log_hexbuffer("derive_subaddress_public_key: [[IN]] derivation", derivation_x.data, 32); + log_message ("derive_subaddress_public_key: [[IN]] index ", std::to_string((int)output_index_x)); this->controle_device->derive_subaddress_public_key(pub_x, derivation_x,output_index_x,derived_pub_x); - hw::ledger::log_hexbuffer("derive_subaddress_public_key: [[OUT]] derived_pub", derived_pub_x.data, 32); + log_hexbuffer("derive_subaddress_public_key: [[OUT]] derived_pub", derived_pub_x.data, 32); #endif if ((this->mode == TRANSACTION_PARSE) && has_view_key) { @@ -558,11 +566,11 @@ namespace hw { const cryptonote::account_keys keys_x = hw::ledger::decrypt(keys); const cryptonote::subaddress_index index_x = index; crypto::public_key D_x; - hw::ledger::log_hexbuffer("get_subaddress_spend_public_key: [[IN]] keys.m_view_secret_key ", keys_x.m_view_secret_key.data,32); - hw::ledger::log_hexbuffer("get_subaddress_spend_public_key: [[IN]] keys.m_spend_secret_key", keys_x.m_spend_secret_key.data,32); - hw::ledger::log_message ("get_subaddress_spend_public_key: [[IN]] index ", std::to_string(index_x.major)+"."+std::to_string(index_x.minor)); + log_hexbuffer("get_subaddress_spend_public_key: [[IN]] keys.m_view_secret_key ", keys_x.m_view_secret_key.data,32); + log_hexbuffer("get_subaddress_spend_public_key: [[IN]] keys.m_spend_secret_key", keys_x.m_spend_secret_key.data,32); + log_message ("get_subaddress_spend_public_key: [[IN]] index ", std::to_string(index_x.major)+"."+std::to_string(index_x.minor)); D_x = this->controle_device->get_subaddress_spend_public_key(keys_x, index_x); - hw::ledger::log_hexbuffer("get_subaddress_spend_public_key: [[OUT]] derivation ", D_x.data, 32); + log_hexbuffer("get_subaddress_spend_public_key: [[OUT]] derivation ", D_x.data, 32); #endif if (index.is_zero()) { @@ -609,14 +617,14 @@ namespace hw { const cryptonote::account_keys keys_x = hw::ledger::decrypt(keys); const cryptonote::subaddress_index index_x = index; cryptonote::account_public_address address_x; - hw::ledger::log_hexbuffer("get_subaddress: [[IN]] keys.m_view_secret_key ", keys_x.m_view_secret_key.data, 32); - hw::ledger::log_hexbuffer("get_subaddress: [[IN]] keys.m_view_public_key", keys_x.m_account_address.m_view_public_key.data, 32); - hw::ledger::log_hexbuffer("get_subaddress: [[IN]] keys.m_view_secret_key ", keys_x.m_view_secret_key.data, 32); - hw::ledger::log_hexbuffer("get_subaddress: [[IN]] keys.m_spend_public_key", keys_x.m_account_address.m_spend_public_key.data, 32); - hw::ledger::log_message ("get_subaddress: [[IN]] index ", std::to_string(index_x.major)+"."+std::to_string(index_x.minor)); + log_hexbuffer("get_subaddress: [[IN]] keys.m_view_secret_key ", keys_x.m_view_secret_key.data, 32); + log_hexbuffer("get_subaddress: [[IN]] keys.m_view_public_key", keys_x.m_account_address.m_view_public_key.data, 32); + log_hexbuffer("get_subaddress: [[IN]] keys.m_view_secret_key ", keys_x.m_view_secret_key.data, 32); + log_hexbuffer("get_subaddress: [[IN]] keys.m_spend_public_key", keys_x.m_account_address.m_spend_public_key.data, 32); + log_message ("get_subaddress: [[IN]] index ", std::to_string(index_x.major)+"."+std::to_string(index_x.minor)); address_x = this->controle_device->get_subaddress(keys_x, index_x); - hw::ledger::log_hexbuffer("get_subaddress: [[OUT]] keys.m_view_public_key ", address_x.m_view_public_key.data, 32); - hw::ledger::log_hexbuffer("get_subaddress: [[OUT]] keys.m_spend_public_key", address_x.m_spend_public_key.data, 32); + log_hexbuffer("get_subaddress: [[OUT]] keys.m_view_public_key ", address_x.m_view_public_key.data, 32); + log_hexbuffer("get_subaddress: [[OUT]] keys.m_spend_public_key", address_x.m_spend_public_key.data, 32); #endif if (index.is_zero()) { @@ -652,10 +660,10 @@ namespace hw { const crypto::secret_key sec_x = hw::ledger::decrypt(sec); const cryptonote::subaddress_index index_x = index; crypto::secret_key sub_sec_x; - hw::ledger::log_message ("get_subaddress_secret_key: [[IN]] index ", std::to_string(index.major)+"."+std::to_string(index.minor)); - hw::ledger::log_hexbuffer("get_subaddress_secret_key: [[IN]] sec ", sec_x.data, 32); + log_message ("get_subaddress_secret_key: [[IN]] index ", std::to_string(index.major)+"."+std::to_string(index.minor)); + log_hexbuffer("get_subaddress_secret_key: [[IN]] sec ", sec_x.data, 32); sub_sec_x = this->controle_device->get_subaddress_secret_key(sec_x, index_x); - hw::ledger::log_hexbuffer("get_subaddress_secret_key: [[OUT]] sub_sec", sub_sec_x.data, 32); + log_hexbuffer("get_subaddress_secret_key: [[OUT]] sub_sec", sub_sec_x.data, 32); #endif int offset = set_command_header_noopt(INS_GET_SUBADDRESS_SECRET_KEY); @@ -717,10 +725,10 @@ namespace hw { const rct::key P_x = P; const rct::key a_x = hw::ledger::decrypt(a); rct::key aP_x; - hw::ledger::log_hexbuffer("scalarmultKey: [[IN]] P ", (char*)P_x.bytes, 32); - hw::ledger::log_hexbuffer("scalarmultKey: [[IN]] a ", (char*)a_x.bytes, 32); + log_hexbuffer("scalarmultKey: [[IN]] P ", (char*)P_x.bytes, 32); + log_hexbuffer("scalarmultKey: [[IN]] a ", (char*)a_x.bytes, 32); this->controle_device->scalarmultKey(aP_x, P_x, a_x); - hw::ledger::log_hexbuffer("scalarmultKey: [[OUT]] aP", (char*)aP_x.bytes, 32); + log_hexbuffer("scalarmultKey: [[OUT]] aP", (char*)aP_x.bytes, 32); #endif int offset = set_command_header_noopt(INS_SECRET_SCAL_MUL_KEY); @@ -752,9 +760,9 @@ namespace hw { #ifdef DEBUG_HWDEVICE const rct::key a_x = hw::ledger::decrypt(a); rct::key aG_x; - hw::ledger::log_hexbuffer("scalarmultKey: [[IN]] a ", (char*)a_x.bytes, 32); + log_hexbuffer("scalarmultKey: [[IN]] a ", (char*)a_x.bytes, 32); this->controle_device->scalarmultBase(aG_x, a_x); - hw::ledger::log_hexbuffer("scalarmultKey: [[OUT]] aG", (char*)aG_x.bytes, 32); + log_hexbuffer("scalarmultKey: [[OUT]] aG", (char*)aG_x.bytes, 32); #endif int offset = set_command_header_noopt(INS_SECRET_SCAL_MUL_BASE); @@ -845,10 +853,10 @@ namespace hw { const crypto::public_key pub_x = pub; const crypto::secret_key sec_x = hw::ledger::decrypt(sec); crypto::key_derivation derivation_x; - hw::ledger::log_hexbuffer("generate_key_derivation: [[IN]] pub ", pub_x.data, 32); - hw::ledger::log_hexbuffer("generate_key_derivation: [[IN]] sec ", sec_x.data, 32); + log_hexbuffer("generate_key_derivation: [[IN]] pub ", pub_x.data, 32); + log_hexbuffer("generate_key_derivation: [[IN]] sec ", sec_x.data, 32); this->controle_device->generate_key_derivation(pub_x, sec_x, derivation_x); - hw::ledger::log_hexbuffer("generate_key_derivation: [[OUT]] derivation", derivation_x.data, 32); + log_hexbuffer("generate_key_derivation: [[OUT]] derivation", derivation_x.data, 32); #endif if ((this->mode == TRANSACTION_PARSE) && has_view_key) { @@ -914,10 +922,10 @@ namespace hw { const crypto::key_derivation derivation_x = hw::ledger::decrypt(derivation); const size_t output_index_x = output_index; crypto::ec_scalar res_x; - hw::ledger::log_hexbuffer("derivation_to_scalar: [[IN]] derivation ", derivation_x.data, 32); - hw::ledger::log_message ("derivation_to_scalar: [[IN]] output_index ", std::to_string(output_index_x)); + log_hexbuffer("derivation_to_scalar: [[IN]] derivation ", derivation_x.data, 32); + log_message ("derivation_to_scalar: [[IN]] output_index ", std::to_string(output_index_x)); this->controle_device->derivation_to_scalar(derivation_x, output_index_x, res_x); - hw::ledger::log_hexbuffer("derivation_to_scalar: [[OUT]] res ", res_x.data, 32); + log_hexbuffer("derivation_to_scalar: [[OUT]] res ", res_x.data, 32); #endif int offset = set_command_header_noopt(INS_DERIVATION_TO_SCALAR); @@ -954,11 +962,11 @@ namespace hw { const std::size_t output_index_x = output_index; const crypto::secret_key sec_x = hw::ledger::decrypt(sec); crypto::secret_key derived_sec_x; - hw::ledger::log_hexbuffer("derive_secret_key: [[IN]] derivation ", derivation_x.data, 32); - hw::ledger::log_message ("derive_secret_key: [[IN]] index ", std::to_string(output_index_x)); - hw::ledger::log_hexbuffer("derive_secret_key: [[IN]] sec ", sec_x.data, 32); + log_hexbuffer("derive_secret_key: [[IN]] derivation ", derivation_x.data, 32); + log_message ("derive_secret_key: [[IN]] index ", std::to_string(output_index_x)); + log_hexbuffer("derive_secret_key: [[IN]] sec ", sec_x.data, 32); this->controle_device->derive_secret_key(derivation_x, output_index_x, sec_x, derived_sec_x); - hw::ledger::log_hexbuffer("derive_secret_key: [[OUT]] derived_sec", derived_sec_x.data, 32); + log_hexbuffer("derive_secret_key: [[OUT]] derived_sec", derived_sec_x.data, 32); #endif int offset = set_command_header_noopt(INS_DERIVE_SECRET_KEY); @@ -998,11 +1006,11 @@ namespace hw { const std::size_t output_index_x = output_index; const crypto::public_key pub_x = pub; crypto::public_key derived_pub_x; - hw::ledger::log_hexbuffer("derive_public_key: [[IN]] derivation ", derivation_x.data, 32); - hw::ledger::log_message ("derive_public_key: [[IN]] output_index", std::to_string(output_index_x)); - hw::ledger::log_hexbuffer("derive_public_key: [[IN]] pub ", pub_x.data, 32); + log_hexbuffer("derive_public_key: [[IN]] derivation ", derivation_x.data, 32); + log_message ("derive_public_key: [[IN]] output_index", std::to_string(output_index_x)); + log_hexbuffer("derive_public_key: [[IN]] pub ", pub_x.data, 32); this->controle_device->derive_public_key(derivation_x, output_index_x, pub_x, derived_pub_x); - hw::ledger::log_hexbuffer("derive_public_key: [[OUT]] derived_pub ", derived_pub_x.data, 32); + log_hexbuffer("derive_public_key: [[OUT]] derived_pub ", derived_pub_x.data, 32); #endif int offset = set_command_header_noopt(INS_DERIVE_PUBLIC_KEY); @@ -1039,11 +1047,11 @@ namespace hw { #ifdef DEBUG_HWDEVICE const crypto::secret_key sec_x = hw::ledger::decrypt(sec); crypto::public_key pub_x; - hw::ledger::log_hexbuffer("secret_key_to_public_key: [[IN]] sec ", sec_x.data, 32); + log_hexbuffer("secret_key_to_public_key: [[IN]] sec ", sec_x.data, 32); bool rc = this->controle_device->secret_key_to_public_key(sec_x, pub_x); - hw::ledger::log_hexbuffer("secret_key_to_public_key: [[OUT]] pub", pub_x.data, 32); + log_hexbuffer("secret_key_to_public_key: [[OUT]] pub", pub_x.data, 32); if (!rc){ - hw::ledger::log_message("secret_key_to_public_key", "secret_key rejected"); + log_message("secret_key_to_public_key", "secret_key rejected"); } #endif @@ -1073,10 +1081,10 @@ namespace hw { const crypto::public_key pub_x = pub; const crypto::secret_key sec_x = hw::ledger::decrypt(sec); crypto::key_image image_x; - hw::ledger::log_hexbuffer("generate_key_image: [[IN]] pub ", pub_x.data, 32); - hw::ledger::log_hexbuffer("generate_key_image: [[IN]] sec ", sec_x.data, 32); + log_hexbuffer("generate_key_image: [[IN]] pub ", pub_x.data, 32); + log_hexbuffer("generate_key_image: [[IN]] sec ", sec_x.data, 32); this->controle_device->generate_key_image(pub_x, sec_x, image_x); - hw::ledger::log_hexbuffer("generate_key_image: [[OUT]] image ", image_x.data, 32); + log_hexbuffer("generate_key_image: [[OUT]] image ", image_x.data, 32); #endif int offset = set_command_header_noopt(INS_GEN_KEY_IMAGE); @@ -1160,10 +1168,139 @@ namespace hw { return true; } - bool device_ledger::add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const size_t real_output_index, - const rct::key &amount_key, const crypto::public_key &out_eph_public_key) { - AUTO_LOCK_CMD(); - key_map.add(ABPkeys(rct::pk2rct(Aout),rct::pk2rct(Bout), is_subaddress, real_output_index, rct::pk2rct(out_eph_public_key), amount_key)); + + bool device_ledger::generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key, + const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index, + const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys, + std::vector<crypto::public_key> &additional_tx_public_keys, + std::vector<rct::key> &amount_keys, + crypto::public_key &out_eph_public_key) { + AUTO_LOCK_CMD(); + + #ifdef DEBUG_HWDEVICE + const size_t &tx_version_x = tx_version; + const cryptonote::account_keys sender_account_keys_x = sender_account_keys; + memmove((void*)sender_account_keys_x.m_view_secret_key.data, dbg_viewkey.data, 32); + + const crypto::public_key &txkey_pub_x = txkey_pub; + const crypto::secret_key &tx_key_x = tx_key; + const cryptonote::tx_destination_entry &dst_entr_x = dst_entr; + const boost::optional<cryptonote::account_public_address> &change_addr_x = change_addr; + const size_t &output_index_x = output_index; + const bool &need_additional_txkeys_x = need_additional_txkeys; + const std::vector<crypto::secret_key> &additional_tx_keys_x = additional_tx_keys; + std::vector<crypto::public_key> additional_tx_public_keys_x; + std::vector<rct::key> amount_keys_x; + crypto::public_key out_eph_public_key_x; + this->controle_device->generate_output_ephemeral_keys(tx_version_x, sender_account_keys_x, txkey_pub_x, tx_key_x, dst_entr_x, change_addr_x, output_index_x, need_additional_txkeys_x, additional_tx_keys_x, + additional_tx_public_keys_x, amount_keys_x, out_eph_public_key_x); + #endif + + // make additional tx pubkey if necessary + cryptonote::keypair additional_txkey; + if (need_additional_txkeys) { + additional_txkey.sec = additional_tx_keys[output_index]; + } + + //compute derivation, out_eph_public_key, and amount key in one shot on device, to ensure checkable link + const crypto::secret_key *sec; + bool is_change; + + if (change_addr && dst_entr.addr == *change_addr) + { + // sending change to yourself; derivation = a*R + is_change = true; + sec = &sender_account_keys.m_view_secret_key; + } + else + { + is_change = false; + if (dst_entr.is_subaddress && need_additional_txkeys) { + sec = &additional_txkey.sec; + } else { + sec = &tx_key; + } + } + + int offset = set_command_header_noopt(INS_GEN_TXOUT_KEYS); + //tx_version + this->buffer_send[offset+0] = tx_version>>24; + this->buffer_send[offset+1] = tx_version>>16; + this->buffer_send[offset+2] = tx_version>>8; + this->buffer_send[offset+3] = tx_version>>0; + offset += 4; + //tx_sec + memmove(&this->buffer_send[offset], sec->data, 32); + offset += 32; + //Aout + memmove(&this->buffer_send[offset], dst_entr.addr.m_view_public_key.data, 32); + offset += 32; + //Bout + memmove(&this->buffer_send[offset], dst_entr.addr.m_spend_public_key.data, 32); + offset += 32; + //output index + this->buffer_send[offset+0] = output_index>>24; + this->buffer_send[offset+1] = output_index>>16; + this->buffer_send[offset+2] = output_index>>8; + this->buffer_send[offset+3] = output_index>>0; + offset += 4; + //is_change, + this->buffer_send[offset] = is_change; + offset++; + //is_subaddress + this->buffer_send[offset] = dst_entr.is_subaddress; + offset++; + //need_additional_key + this->buffer_send[offset] = need_additional_txkeys; + offset++; + + this->buffer_send[4] = offset-5; + this->length_send = offset; + this->exchange(); + + offset = 0; + unsigned int recv_len = this->length_recv; + if (need_additional_txkeys) + { + ASSERT_X(recv_len>=32, "Not enought data from device"); + memmove(additional_txkey.pub.data, &this->buffer_recv[offset], 32); + additional_tx_public_keys.push_back(additional_txkey.pub); + offset += 32; + recv_len -= 32; + } + if (tx_version > 1) + { + ASSERT_X(recv_len>=32, "Not enought data from device"); + crypto::secret_key scalar1; + memmove(scalar1.data, &this->buffer_recv[offset],32); + amount_keys.push_back(rct::sk2rct(scalar1)); + offset += 32; + recv_len -= 32; + } + ASSERT_X(recv_len>=32, "Not enought data from device"); + memmove(out_eph_public_key.data, &this->buffer_recv[offset], 32); + recv_len -= 32; + + // add ABPkeys + this->add_output_key_mapping(dst_entr.addr.m_view_public_key, dst_entr.addr.m_spend_public_key, dst_entr.is_subaddress, is_change, + need_additional_txkeys, output_index, + amount_keys.back(), out_eph_public_key); + + #ifdef DEBUG_HWDEVICE + hw::ledger::check32("generate_output_ephemeral_keys", "amount_key", (const char*)amount_keys_x.back().bytes, (const char*)hw::ledger::decrypt(amount_keys.back()).bytes); + if (need_additional_txkeys) { + hw::ledger::check32("generate_output_ephemeral_keys", "additional_tx_key", additional_tx_keys_x.back().data, additional_tx_keys.back().data); + } + hw::ledger::check32("generate_output_ephemeral_keys", "out_eph_public_key", out_eph_public_key_x.data, out_eph_public_key.data); + #endif + + return true; + } + + bool device_ledger::add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const bool is_change, + const bool need_additional, const size_t real_output_index, + const rct::key &amount_key, const crypto::public_key &out_eph_public_key) { + key_map.add(ABPkeys(rct::pk2rct(Aout),rct::pk2rct(Bout), is_subaddress, is_change, need_additional, real_output_index, rct::pk2rct(out_eph_public_key), amount_key)); return true; } @@ -1176,7 +1313,10 @@ namespace hw { this->controle_device->ecdhEncode(unmasked_x, AKout_x, short_amount); #endif - int offset = set_command_header_noopt(INS_BLIND); + int offset = set_command_header(INS_BLIND); + //options + this->buffer_send[offset] = short_amount?0x02:0x00; + offset += 1; // AKout memmove(this->buffer_send+offset, AKout.bytes, 32); offset += 32; @@ -1198,7 +1338,7 @@ namespace hw { hw::ledger::check32("ecdhEncode", "amount", (char*)unmasked_x.amount.bytes, (char*)unmasked.amount.bytes); hw::ledger::check32("ecdhEncode", "mask", (char*)unmasked_x.mask.bytes, (char*)unmasked.mask.bytes); - hw::ledger::log_hexbuffer("Blind AKV input", (char*)&this->buffer_recv[64], 3*32); + log_hexbuffer("Blind AKV input", (char*)&this->buffer_recv[64], 3*32); #endif return true; @@ -1213,8 +1353,10 @@ namespace hw { this->controle_device->ecdhDecode(masked_x, AKout_x, short_amount); #endif - int offset = set_command_header_noopt(INS_UNBLIND); - + int offset = set_command_header(INS_UNBLIND); + //options + this->buffer_send[offset] = short_amount?0x02:0x00; + offset += 1; // AKout memmove(this->buffer_send+offset, AKout.bytes, 32); offset += 32; @@ -1310,7 +1452,11 @@ namespace hw { // ====== Aout, Bout, AKout, C, v, k ====== kv_offset = data_offset; - C_offset = kv_offset+ (32*2)*outputs_size; + if (type==rct::RCTTypeBulletproof2) { + C_offset = kv_offset+ (8)*outputs_size; + } else { + C_offset = kv_offset+ (32+32)*outputs_size; + } for ( i = 0; i < outputs_size; i++) { ABPkeys outKeys; bool found; @@ -1323,11 +1469,15 @@ namespace hw { offset = set_command_header(INS_VALIDATE, 0x02, i+1); //options this->buffer_send[offset] = (i==outputs_size-1)? 0x00:0x80 ; + this->buffer_send[offset] |= (type==rct::RCTTypeBulletproof2)?0x02:0x00; offset += 1; if (found) { //is_subaddress this->buffer_send[offset] = outKeys.is_subaddress; offset++; + //is_change_address + this->buffer_send[offset] = outKeys.is_change_address; + offset++; //Aout memmove(this->buffer_send+offset, outKeys.Aout.bytes, 32); offset+=32; @@ -1339,27 +1489,38 @@ namespace hw { offset+=32; } else { // dummy: is_subaddress Aout Bout AKout - offset += 1+32*3; + offset += 2+32*3; } //C memmove(this->buffer_send+offset, data+C_offset,32); offset += 32; C_offset += 32; - //k - memmove(this->buffer_send+offset, data+kv_offset,32); - offset += 32; - //v - kv_offset += 32; - memmove(this->buffer_send+offset, data+kv_offset,32); - offset += 32; - kv_offset += 32; + if (type==rct::RCTTypeBulletproof2) { + //k + memset(this->buffer_send+offset, 0, 32); + offset += 32; + //v + memset(this->buffer_send+offset, 0, 32); + memmove(this->buffer_send+offset, data+kv_offset,8); + offset += 32; + kv_offset += 8; + } else { + //k + memmove(this->buffer_send+offset, data+kv_offset,32); + offset += 32; + kv_offset += 32; + //v + memmove(this->buffer_send+offset, data+kv_offset,32); + offset += 32; + kv_offset += 32; + } this->buffer_send[4] = offset-5; this->length_send = offset; // check transaction user input CHECK_AND_ASSERT_THROW_MES(this->exchange_wait_on_input() == 0, "Transaction denied on device."); #ifdef DEBUG_HWDEVICE - hw::ledger::log_hexbuffer("Prehash AKV input", (char*)&this->buffer_recv[64], 3*32); + log_hexbuffer("Prehash AKV input", (char*)&this->buffer_recv[64], 3*32); #endif } diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp index 584f1e096..3f470ee7c 100644 --- a/src/device/device_ledger.hpp +++ b/src/device/device_ledger.hpp @@ -56,12 +56,14 @@ namespace hw { rct::key Aout; rct::key Bout; bool is_subaddress; + bool is_change_address; + bool additional_key ; size_t index; rct::key Pout; rct::key AKout; - ABPkeys(const rct::key& A, const rct::key& B, const bool is_subaddr, size_t index, const rct::key& P,const rct::key& AK); + ABPkeys(const rct::key& A, const rct::key& B, const bool is_subaddr, bool is_subaddress, bool is_change_address, size_t index, const rct::key& P,const rct::key& AK); ABPkeys(const ABPkeys& keys) ; - ABPkeys() {index=0;is_subaddress=false;} + ABPkeys() {index=0;is_subaddress=false;is_subaddress=false;is_change_address=false;} }; class Keymap { @@ -105,7 +107,9 @@ namespace hw { device_mode mode; // map public destination key to ephemeral destination key Keymap key_map; - + bool add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const bool is_change, + const bool need_additional, const size_t real_output_index, + const rct::key &amount_key, const crypto::public_key &out_eph_public_key); // To speed up blockchain parsing the view key maybe handle here. crypto::secret_key viewkey; bool has_view_key; @@ -194,9 +198,12 @@ namespace hw { bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec, bool short_format) override; bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec, bool short_format) override; - bool add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const size_t real_output_index, - const rct::key &amount_key, const crypto::public_key &out_eph_public_key) override; - + bool generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key, + const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index, + const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys, + std::vector<crypto::public_key> &additional_tx_public_keys, + std::vector<rct::key> &amount_keys, + crypto::public_key &out_eph_public_key) override; bool mlsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash) override; bool mlsag_prepare(const rct::key &H, const rct::key &xx, rct::key &a, rct::key &aG, rct::key &aHP, rct::key &rvII) override; diff --git a/src/device/log.cpp b/src/device/log.cpp index c9d3b551b..87505798b 100644 --- a/src/device/log.cpp +++ b/src/device/log.cpp @@ -66,7 +66,7 @@ namespace hw { void decrypt(char* buf, size_t len) { - #ifdef IODUMMYCRYPT_HWDEVICE + #if defined(IODUMMYCRYPT_HWDEVICE) || defined(IONOCRYPT_HWDEVICE) size_t i; if (len == 32) { //view key? @@ -86,11 +86,13 @@ namespace hw { return; } } + #if defined(IODUMMYCRYPT_HWDEVICE) //std decrypt: XOR.55h for (i = 0; i<len;i++) { buf[i] ^= 0x55; } #endif + #endif } crypto::key_derivation decrypt(const crypto::key_derivation &derivation) { diff --git a/src/device_trezor/trezor/transport.hpp b/src/device_trezor/trezor/transport.hpp index 50c31cf73..1cf0daa85 100644 --- a/src/device_trezor/trezor/transport.hpp +++ b/src/device_trezor/trezor/transport.hpp @@ -162,7 +162,7 @@ namespace trezor { m_session(boost::none), m_device_info(boost::none) { - m_http_client.set_server(m_bridge_host, boost::none, false); + m_http_client.set_server(m_bridge_host, boost::none, epee::net_utils::ssl_support_t::e_ssl_support_disabled); } virtual ~BridgeTransport() = default; diff --git a/src/net/CMakeLists.txt b/src/net/CMakeLists.txt index a81372125..8a3ee9e6f 100644 --- a/src/net/CMakeLists.txt +++ b/src/net/CMakeLists.txt @@ -26,9 +26,9 @@ # 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. -set(net_sources error.cpp parse.cpp socks.cpp tor_address.cpp) -set(net_headers error.h parse.h socks.h tor_address.h) +set(net_sources error.cpp parse.cpp socks.cpp tor_address.cpp i2p_address.cpp) +set(net_headers error.h parse.h socks.h tor_address.h i2p_address.h) monero_add_library(net ${net_sources} ${net_headers}) -target_link_libraries(net epee ${Boost_ASIO_LIBRARY}) +target_link_libraries(net common epee ${Boost_ASIO_LIBRARY}) diff --git a/src/net/fwd.h b/src/net/fwd.h index ee7c539b0..7cae88251 100644 --- a/src/net/fwd.h +++ b/src/net/fwd.h @@ -34,6 +34,7 @@ namespace net { enum class error : int; class tor_address; + class i2p_address; namespace socks { diff --git a/src/net/i2p_address.cpp b/src/net/i2p_address.cpp new file mode 100644 index 000000000..cba829d3f --- /dev/null +++ b/src/net/i2p_address.cpp @@ -0,0 +1,200 @@ +// 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 "i2p_address.h" + +#include <algorithm> +#include <boost/spirit/include/karma_generate.hpp> +#include <boost/spirit/include/karma_uint.hpp> +#include <cassert> +#include <cstring> +#include <limits> + +#include "net/error.h" +#include "serialization/keyvalue_serialization.h" +#include "storages/portable_storage.h" +#include "string_tools.h" + +namespace net +{ + namespace + { + // !TODO only b32 addresses right now + constexpr const char tld[] = u8".b32.i2p"; + constexpr const char unknown_host[] = "<unknown i2p host>"; + + constexpr const unsigned b32_length = 52; + + constexpr const char base32_alphabet[] = + u8"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz234567"; + + expect<void> host_check(boost::string_ref host) noexcept + { + if (!host.ends_with(tld)) + return {net::error::expected_tld}; + + host.remove_suffix(sizeof(tld) - 1); + + if (host.size() != b32_length) + return {net::error::invalid_i2p_address}; + if (host.find_first_not_of(base32_alphabet) != boost::string_ref::npos) + return {net::error::invalid_i2p_address}; + + return success(); + } + + struct i2p_serialized + { + std::string host; + std::uint16_t port; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(host) + KV_SERIALIZE(port) + END_KV_SERIALIZE_MAP() + }; + } + + i2p_address::i2p_address(const boost::string_ref host, const std::uint16_t port) noexcept + : port_(port) + { + // this is a private constructor, throw if moved to public + assert(host.size() < sizeof(host_)); + + const std::size_t length = std::min(sizeof(host_) - 1, host.size()); + std::memcpy(host_, host.data(), length); + std::memset(host_ + length, 0, sizeof(host_) - length); + } + + const char* i2p_address::unknown_str() noexcept + { + return unknown_host; + } + + i2p_address::i2p_address() noexcept + : port_(0) + { + static_assert(sizeof(unknown_host) <= sizeof(host_), "bad buffer size"); + std::memcpy(host_, unknown_host, sizeof(unknown_host)); + std::memset(host_ + sizeof(unknown_host), 0, sizeof(host_) - sizeof(unknown_host)); + } + + expect<i2p_address> i2p_address::make(const boost::string_ref address, const std::uint16_t default_port) + { + boost::string_ref host = address.substr(0, address.rfind(':')); + const boost::string_ref port = + address.substr(host.size() + (host.size() == address.size() ? 0 : 1)); + + MONERO_CHECK(host_check(host)); + + std::uint16_t porti = default_port; + if (!port.empty() && !epee::string_tools::get_xtype_from_string(porti, std::string{port})) + return {net::error::invalid_port}; + + static_assert(b32_length + sizeof(tld) == sizeof(i2p_address::host_), "bad internal host size"); + return i2p_address{host, porti}; + } + + bool i2p_address::_load(epee::serialization::portable_storage& src, epee::serialization::section* hparent) + { + i2p_serialized in{}; + if (in._load(src, hparent) && in.host.size() < sizeof(host_) && (in.host == unknown_host || !host_check(in.host).has_error())) + { + std::memcpy(host_, in.host.data(), in.host.size()); + std::memset(host_ + in.host.size(), 0, sizeof(host_) - in.host.size()); + port_ = in.port; + return true; + } + static_assert(sizeof(unknown_host) <= sizeof(host_), "bad buffer size"); + std::memcpy(host_, unknown_host, sizeof(unknown_host)); // include null terminator + port_ = 0; + return false; + } + + bool i2p_address::store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const + { + const i2p_serialized out{std::string{host_}, port_}; + return out.store(dest, hparent); + } + + i2p_address::i2p_address(const i2p_address& rhs) noexcept + : port_(rhs.port_) + { + std::memcpy(host_, rhs.host_, sizeof(host_)); + } + + i2p_address& i2p_address::operator=(const i2p_address& rhs) noexcept + { + if (this != std::addressof(rhs)) + { + port_ = rhs.port_; + std::memcpy(host_, rhs.host_, sizeof(host_)); + } + return *this; + } + + bool i2p_address::is_unknown() const noexcept + { + static_assert(1 <= sizeof(host_), "host size too small"); + return host_[0] == '<'; // character is not allowed otherwise + } + + bool i2p_address::equal(const i2p_address& rhs) const noexcept + { + return port_ == rhs.port_ && is_same_host(rhs); + } + + bool i2p_address::less(const i2p_address& rhs) const noexcept + { + return std::strcmp(host_str(), rhs.host_str()) < 0 || port() < rhs.port(); + } + + bool i2p_address::is_same_host(const i2p_address& rhs) const noexcept + { + return std::strcmp(host_str(), rhs.host_str()) == 0; + } + + std::string i2p_address::str() const + { + const std::size_t host_length = std::strlen(host_str()); + const std::size_t port_length = + port_ == 0 ? 0 : std::numeric_limits<std::uint16_t>::digits10 + 2; + + std::string out{}; + out.reserve(host_length + port_length); + out.assign(host_str(), host_length); + + if (port_ != 0) + { + out.push_back(':'); + namespace karma = boost::spirit::karma; + karma::generate(std::back_inserter(out), karma::ushort_, port()); + } + return out; + } +} diff --git a/src/net/i2p_address.h b/src/net/i2p_address.h new file mode 100644 index 000000000..28a1118ba --- /dev/null +++ b/src/net/i2p_address.h @@ -0,0 +1,140 @@ +// Copyright (c) 2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include <boost/utility/string_ref.hpp> +#include <cstdint> +#include <string> + +#include "common/expect.h" +#include "net/enums.h" +#include "net/error.h" + +namespace epee +{ +namespace serialization +{ + class portable_storage; + struct section; +} +} + +namespace net +{ + //! b32 i2p address; internal format not condensed/decoded. + class i2p_address + { + std::uint16_t port_; + char host_[61]; // null-terminated + + //! Keep in private, `host.size()` has no runtime check + i2p_address(boost::string_ref host, std::uint16_t port) noexcept; + + public: + //! \return Size of internal buffer for host. + static constexpr std::size_t buffer_size() noexcept { return sizeof(host_); } + + //! \return `<unknown tor host>`. + static const char* unknown_str() noexcept; + + //! An object with `port() == 0` and `host_str() == unknown_str()`. + i2p_address() noexcept; + + //! \return A default constructed `i2p_address` object. + static i2p_address unknown() noexcept { return i2p_address{}; } + + /*! + Parse `address` in b32 i2p format (i.e. x.b32.i2p:80) + with `default_port` being used if port is not specified in + `address`. + */ + static expect<i2p_address> make(boost::string_ref address, std::uint16_t default_port = 0); + + //! Load from epee p2p format, and \return false if not valid tor address + bool _load(epee::serialization::portable_storage& src, epee::serialization::section* hparent); + + //! Store in epee p2p format + bool store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const; + + // Moves and copies are currently identical + + i2p_address(const i2p_address& rhs) noexcept; + ~i2p_address() = default; + i2p_address& operator=(const i2p_address& rhs) noexcept; + + //! \return True if default constructed or via `unknown()`. + bool is_unknown() const noexcept; + + bool equal(const i2p_address& rhs) const noexcept; + bool less(const i2p_address& rhs) const noexcept; + + //! \return True if i2p addresses are identical. + bool is_same_host(const i2p_address& rhs) const noexcept; + + //! \return `x.b32.i2p` or `x.b32.i2p:z` if `port() != 0`. + std::string str() const; + + //! \return Null-terminated `x.b32.i2p` value or `unknown_str()`. + const char* host_str() const noexcept { return host_; } + + //! \return Port value or `0` if unspecified. + std::uint16_t port() const noexcept { return port_; } + + static constexpr bool is_loopback() noexcept { return false; } + static constexpr bool is_local() noexcept { return false; } + + static constexpr epee::net_utils::address_type get_type_id() noexcept + { + return epee::net_utils::address_type::i2p; + } + + static constexpr epee::net_utils::zone get_zone() noexcept + { + return epee::net_utils::zone::i2p; + } + + //! \return `!is_unknown()`. + bool is_blockable() const noexcept { return !is_unknown(); } + }; + + inline bool operator==(const i2p_address& lhs, const i2p_address& rhs) noexcept + { + return lhs.equal(rhs); + } + + inline bool operator!=(const i2p_address& lhs, const i2p_address& rhs) noexcept + { + return !lhs.equal(rhs); + } + + inline bool operator<(const i2p_address& lhs, const i2p_address& rhs) noexcept + { + return lhs.less(rhs); + } +} // net diff --git a/src/net/parse.cpp b/src/net/parse.cpp index ebf91eeff..eaaadb67e 100644 --- a/src/net/parse.cpp +++ b/src/net/parse.cpp @@ -29,6 +29,7 @@ #include "parse.h" #include "net/tor_address.h" +#include "net/i2p_address.h" #include "string_tools.h" namespace net @@ -43,7 +44,7 @@ namespace net if (host.ends_with(".onion")) return tor_address::make(address, default_port); if (host.ends_with(".i2p")) - return make_error_code(net::error::invalid_i2p_address); // not yet implemented (prevent public DNS lookup) + return i2p_address::make(address, default_port); std::uint16_t port = default_port; if (host.size() < address.size()) diff --git a/src/net/parse.h b/src/net/parse.h index 9195ddc2b..5804c4128 100644 --- a/src/net/parse.h +++ b/src/net/parse.h @@ -37,11 +37,11 @@ namespace net { /*! - Identifies onion and IPv4 addresses and returns them as a generic + Identifies onion, i2p and IPv4 addresses and returns them 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 onion address, ipv4 address or hostname. Hostname + \param address An onion address, i2p address, ipv4 address or hostname. Hostname will return an error. \param default_port If `address` does not specify a port, this value will be used. diff --git a/src/net/socks.cpp b/src/net/socks.cpp index f31efc8c1..53154369b 100644 --- a/src/net/socks.cpp +++ b/src/net/socks.cpp @@ -40,6 +40,7 @@ #include "net/net_utils_base.h" #include "net/tor_address.h" +#include "net/i2p_address.h" namespace net { @@ -273,6 +274,13 @@ namespace socks return false; } + bool client::set_connect_command(const net::i2p_address& address) + { + if (!address.is_unknown()) + return set_connect_command(address.host_str(), address.port()); + return false; + } + bool client::set_resolve_command(boost::string_ref domain) { if (socks_version() != version::v4a_tor) diff --git a/src/net/socks.h b/src/net/socks.h index d29a51ccb..825937792 100644 --- a/src/net/socks.h +++ b/src/net/socks.h @@ -155,6 +155,9 @@ namespace socks //! Try to set `address` as remote Tor hidden service connection request. bool set_connect_command(const net::tor_address& address); + //! Try to set `address` as remote i2p hidden service connection request. + bool set_connect_command(const net::i2p_address& address); + //! Try to set `domain` as remote DNS A record lookup request. bool set_resolve_command(boost::string_ref domain); diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp index 8639fdb3b..2f0678913 100644 --- a/src/p2p/net_node.cpp +++ b/src/p2p/net_node.cpp @@ -46,13 +46,14 @@ #include "net/socks.h" #include "net/parse.h" #include "net/tor_address.h" +#include "net/i2p_address.h" #include "p2p/p2p_protocol_defs.h" #include "string_tools.h" namespace { constexpr const boost::chrono::milliseconds future_poll_interval{500}; - constexpr const std::chrono::seconds tor_connect_timeout{P2P_DEFAULT_TOR_CONNECT_TIMEOUT}; + constexpr const std::chrono::seconds socks_connect_timeout{P2P_DEFAULT_SOCKS_CONNECT_TIMEOUT}; std::int64_t get_max_connections(const boost::iterator_range<boost::string_ref::const_iterator> value) noexcept { @@ -90,6 +91,9 @@ namespace case net::tor_address::get_type_id(): set = client->set_connect_command(remote.as<net::tor_address>()); break; + case net::i2p_address::get_type_id(): + set = client->set_connect_command(remote.as<net::i2p_address>()); + break; default: MERROR("Unsupported network address in socks_connect"); return false; @@ -177,6 +181,9 @@ namespace nodetool case epee::net_utils::zone::tor: proxies.back().zone = epee::net_utils::zone::tor; break; + case epee::net_utils::zone::i2p: + proxies.back().zone = epee::net_utils::zone::i2p; + break; default: MERROR("Invalid network for --" << arg_proxy.name); return boost::none; @@ -235,6 +242,10 @@ namespace nodetool inbounds.back().our_address = std::move(*our_address); inbounds.back().default_remote = net::tor_address::unknown(); break; + case net::i2p_address::get_type_id(): + inbounds.back().our_address = std::move(*our_address); + inbounds.back().default_remote = net::i2p_address::unknown(); + break; default: MERROR("Invalid inbound address (" << address << ") for --" << arg_anonymous_inbound.name << ": " << (our_address ? "invalid type" : our_address.error().message())); return boost::none; @@ -308,7 +319,7 @@ namespace nodetool const auto start = std::chrono::steady_clock::now(); while (socks_result.wait_for(future_poll_interval) == boost::future_status::timeout) { - if (tor_connect_timeout < std::chrono::steady_clock::now() - start) + if (socks_connect_timeout < std::chrono::steady_clock::now() - start) { MERROR("Timeout on socks connect (" << proxy << " to " << remote.str() << ")"); return boost::none; diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 112f30fb6..58e3c8857 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -129,7 +129,7 @@ namespace nodetool typedef epee::net_utils::boosted_tcp_server<epee::levin::async_protocol_handler<p2p_connection_context>> net_server; struct network_zone; - using connect_func = boost::optional<p2p_connection_context>(network_zone&, epee::net_utils::network_address const&); + using connect_func = boost::optional<p2p_connection_context>(network_zone&, epee::net_utils::network_address const&, epee::net_utils::ssl_support_t); struct config { @@ -437,8 +437,8 @@ namespace nodetool //keep connections to initiate some interactions - static boost::optional<p2p_connection_context> public_connect(network_zone&, epee::net_utils::network_address const&); - static boost::optional<p2p_connection_context> socks_connect(network_zone&, epee::net_utils::network_address const&); + static boost::optional<p2p_connection_context> public_connect(network_zone&, epee::net_utils::network_address const&, epee::net_utils::ssl_support_t); + static boost::optional<p2p_connection_context> socks_connect(network_zone&, epee::net_utils::network_address const&, epee::net_utils::ssl_support_t); /* A `std::map` provides constant iterators and key/value pointers even with @@ -463,6 +463,8 @@ namespace nodetool boost::uuids::uuid m_network_id; cryptonote::network_type m_nettype; + + epee::net_utils::ssl_support_t m_ssl_support; }; const int64_t default_limit_up = P2P_DEFAULT_LIMIT_RATE_UP; // kB/s diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 471fdda0d..e3d804086 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -650,6 +650,7 @@ namespace nodetool return res; //try to bind + m_ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_disabled; for (auto& zone : m_network_zones) { zone.second.m_net_server.get_config_object().set_handler(this); @@ -659,7 +660,7 @@ namespace nodetool { 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); + 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); CHECK_AND_ASSERT_MES(res, false, "Failed to bind server"); } } @@ -1057,7 +1058,7 @@ namespace nodetool << (last_seen_stamp ? epee::misc_utils::get_time_interval_string(time(NULL) - last_seen_stamp):"never") << ")..."); - auto con = zone.m_connect(zone, na); + auto con = zone.m_connect(zone, na, m_ssl_support); if(!con) { bool is_priority = is_priority_node(na); @@ -1119,7 +1120,7 @@ namespace nodetool << (last_seen_stamp ? epee::misc_utils::get_time_interval_string(time(NULL) - last_seen_stamp):"never") << ")..."); - auto con = zone.m_connect(zone, na); + auto con = zone.m_connect(zone, na, m_ssl_support); if (!con) { bool is_priority = is_priority_node(na); @@ -2456,13 +2457,13 @@ namespace nodetool 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) + 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) { auto result = socks_connect_internal(zone.m_net_server.get_stop_signal(), zone.m_net_server.get_io_service(), zone.m_proxy_address, remote); if (result) // if no error { p2p_connection_context context{}; - if (zone.m_net_server.add_connection(context, std::move(*result), remote)) + if (zone.m_net_server.add_connection(context, std::move(*result), remote, ssl_support)) return {std::move(context)}; } return boost::none; @@ -2470,7 +2471,7 @@ namespace nodetool 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>::public_connect(network_zone& zone, epee::net_utils::network_address const& na) + 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"); @@ -2480,7 +2481,7 @@ namespace nodetool 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()), zone.m_config.m_net_config.connection_timeout, - con); + con, "0.0.0.0", ssl_support); if (res) return {std::move(con)}; diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h index d2e9efa3d..6c891581f 100644 --- a/src/p2p/net_peerlist_boost_serialization.h +++ b/src/p2p/net_peerlist_boost_serialization.h @@ -1,4 +1,4 @@ - // Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2018, The Monero Project // // All rights reserved. // @@ -35,6 +35,7 @@ #include "common/expect.h" #include "net/net_utils_base.h" #include "net/tor_address.h" +#include "net/i2p_address.h" #include "p2p/p2p_protocol_defs.h" #ifdef CRYPTONOTE_PRUNING_DEBUG_SPOOF_SEED @@ -76,6 +77,9 @@ namespace boost case net::tor_address::get_type_id(): do_serialize<net::tor_address>(is_saving, a, na); break; + case net::i2p_address::get_type_id(): + do_serialize<net::i2p_address>(is_saving, a, na); + break; case epee::net_utils::address_type::invalid: default: throw std::runtime_error("Unsupported network address type"); @@ -107,6 +111,20 @@ namespace boost } template <class Archive, class ver_type> + inline void save(Archive& a, const net::i2p_address& na, const ver_type) + { + const size_t length = std::strlen(na.host_str()); + if (length > 255) + MONERO_THROW(net::error::invalid_i2p_address, "i2p address too long"); + + const uint16_t port{na.port()}; + const uint8_t len = length; + a & port; + a & len; + a.save_binary(na.host_str(), length); + } + + template <class Archive, class ver_type> inline void load(Archive& a, net::tor_address& na, const ver_type) { uint16_t port = 0; @@ -128,12 +146,39 @@ namespace boost } template <class Archive, class ver_type> + inline void load(Archive& a, net::i2p_address& na, const ver_type) + { + uint16_t port = 0; + uint8_t length = 0; + a & port; + a & length; + + if (length > net::i2p_address::buffer_size()) + MONERO_THROW(net::error::invalid_i2p_address, "i2p address too long"); + + char host[net::i2p_address::buffer_size()] = {0}; + a.load_binary(host, length); + host[sizeof(host) - 1] = 0; + + if (std::strcmp(host, net::i2p_address::unknown_str()) == 0) + na = net::i2p_address::unknown(); + else + na = MONERO_UNWRAP(net::i2p_address::make(host, port)); + } + + template <class Archive, class ver_type> inline void serialize(Archive &a, net::tor_address& na, const ver_type ver) { boost::serialization::split_free(a, na, ver); } template <class Archive, class ver_type> + inline void serialize(Archive &a, net::i2p_address& na, const ver_type ver) + { + boost::serialization::split_free(a, na, ver); + } + + template <class Archive, class ver_type> inline void serialize(Archive &a, nodetool::peerlist_entry& pl, const ver_type ver) { a & pl.adr; diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index 6e5e45008..e9449b950 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -35,6 +35,7 @@ #include "serialization/keyvalue_serialization.h" #include "net/net_utils_base.h" #include "net/tor_address.h" // needed for serialization +#include "net/i2p_address.h" // needed for serialization #include "misc_language.h" #include "string_tools.h" #include "time_helper.h" diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index d485fb748..b5fd626dc 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -202,20 +202,36 @@ static rct::keyV vector_powers(const rct::key &x, size_t n) } /* Given a scalar, return the sum of its powers from 0 to n-1 */ -static rct::key vector_power_sum(const rct::key &x, size_t n) +static rct::key vector_power_sum(rct::key x, size_t n) { if (n == 0) return rct::zero(); rct::key res = rct::identity(); if (n == 1) return res; - rct::key prev = x; - for (size_t i = 1; i < n; ++i) + + const bool is_power_of_2 = (n & (n - 1)) == 0; + if (is_power_of_2) { - if (i > 1) - sc_mul(prev.bytes, prev.bytes, x.bytes); - sc_add(res.bytes, res.bytes, prev.bytes); + sc_add(res.bytes, res.bytes, x.bytes); + while (n > 2) + { + sc_mul(x.bytes, x.bytes, x.bytes); + sc_muladd(res.bytes, x.bytes, res.bytes, res.bytes); + n /= 2; + } + } + else + { + rct::key prev = x; + for (size_t i = 1; i < n; ++i) + { + if (i > 1) + sc_mul(prev.bytes, prev.bytes, x.bytes); + sc_add(res.bytes, res.bytes, prev.bytes); + } } + return res; } diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index b524273bf..d18774149 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -76,6 +76,11 @@ namespace cryptonote command_line::add_arg(desc, arg_rpc_bind_port); command_line::add_arg(desc, arg_rpc_restricted_bind_port); command_line::add_arg(desc, arg_restricted_rpc); + command_line::add_arg(desc, arg_rpc_ssl); + command_line::add_arg(desc, arg_rpc_ssl_private_key); + command_line::add_arg(desc, arg_rpc_ssl_certificate); + command_line::add_arg(desc, arg_rpc_ssl_allowed_certificates); + command_line::add_arg(desc, arg_rpc_ssl_allow_any_cert); command_line::add_arg(desc, arg_bootstrap_daemon_address); command_line::add_arg(desc, arg_bootstrap_daemon_login); cryptonote::rpc_args::init_options(desc); @@ -112,11 +117,11 @@ namespace cryptonote 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, false); + 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, false); + 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; } @@ -131,9 +136,32 @@ namespace cryptonote if (rpc_config->login) http_login.emplace(std::move(rpc_config->login->username), std::move(rpc_config->login->password).password()); + epee::net_utils::ssl_support_t ssl_support; + const std::string ssl = command_line::get_arg(vm, arg_rpc_ssl); + if (!epee::net_utils::ssl_support_from_string(ssl_support, ssl)) + { + MFATAL("Invalid RPC SSL support: " << ssl); + return false; + } + const std::string ssl_private_key = command_line::get_arg(vm, arg_rpc_ssl_private_key); + const std::string ssl_certificate = command_line::get_arg(vm, arg_rpc_ssl_certificate); + const std::vector<std::string> ssl_allowed_certificate_paths = command_line::get_arg(vm, arg_rpc_ssl_allowed_certificates); + std::list<std::string> ssl_allowed_certificates; + for (const std::string &path: ssl_allowed_certificate_paths) + { + ssl_allowed_certificates.push_back({}); + if (!epee::file_io_utils::load_file_to_string(path, ssl_allowed_certificates.back())) + { + MERROR("Failed to load certificate: " << path); + ssl_allowed_certificates.back() = std::string(); + } + } + const bool ssl_allow_any_cert = command_line::get_arg(vm, arg_rpc_ssl_allow_any_cert); + 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) + rng, std::move(port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login), + ssl_support, std::make_pair(ssl_private_key, ssl_certificate), ssl_allowed_certificates, ssl_allow_any_cert ); } //------------------------------------------------------------------------------------------------------------------------------ @@ -1322,6 +1350,7 @@ namespace cryptonote response.block_size = response.block_weight = m_core.get_blockchain_storage().get_db().get_block_weight(height); response.num_txes = blk.tx_hashes.size(); response.pow_hash = fill_pow_hash ? string_tools::pod_to_hex(get_block_longhash(blk, height)) : ""; + response.long_term_weight = m_core.get_blockchain_storage().get_db().get_block_long_term_weight(height); return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -2317,6 +2346,35 @@ namespace cryptonote , false }; + const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_ssl = { + "rpc-ssl" + , "Enable SSL on RPC connections: enabled|disabled|autodetect" + , "autodetect" + }; + + const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_ssl_private_key = { + "rpc-ssl-private-key" + , "Path to a PEM format private key" + , "" + }; + + const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_ssl_certificate = { + "rpc-ssl-certificate" + , "Path to a PEM format certificate" + , "" + }; + + const command_line::arg_descriptor<std::vector<std::string>> core_rpc_server::arg_rpc_ssl_allowed_certificates = { + "rpc-ssl-allowed-certificates" + , "List of paths to PEM format certificates of allowed peers (all allowed if empty)" + }; + + const command_line::arg_descriptor<bool> core_rpc_server::arg_rpc_ssl_allow_any_cert = { + "rpc-ssl-allow-any-cert" + , "Allow any peer certificate, rather than just those on the allowed list" + , false + }; + const command_line::arg_descriptor<std::string> core_rpc_server::arg_bootstrap_daemon_address = { "bootstrap-daemon-address" , "URL of a 'bootstrap' remote daemon that the connected wallets can use while this daemon is still not fully synced" diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 62a377841..da1907af2 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -56,6 +56,11 @@ namespace cryptonote static const command_line::arg_descriptor<std::string, false, true, 2> arg_rpc_bind_port; static const command_line::arg_descriptor<std::string> arg_rpc_restricted_bind_port; static const command_line::arg_descriptor<bool> arg_restricted_rpc; + static const command_line::arg_descriptor<std::string> arg_rpc_ssl; + static const command_line::arg_descriptor<std::string> arg_rpc_ssl_private_key; + static const command_line::arg_descriptor<std::string> arg_rpc_ssl_certificate; + static const command_line::arg_descriptor<std::vector<std::string>> arg_rpc_ssl_allowed_certificates; + static const command_line::arg_descriptor<bool> arg_rpc_ssl_allow_any_cert; static const command_line::arg_descriptor<std::string> arg_bootstrap_daemon_address; static const command_line::arg_descriptor<std::string> arg_bootstrap_daemon_login; diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index dfad5d6a7..e52e4fc67 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -1172,6 +1172,7 @@ namespace cryptonote uint64_t block_weight; uint64_t num_txes; std::string pow_hash; + uint64_t long_term_weight; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(major_version) @@ -1190,6 +1191,7 @@ namespace cryptonote KV_SERIALIZE_OPT(block_weight, (uint64_t)0) KV_SERIALIZE(num_txes) KV_SERIALIZE(pow_hash) + KV_SERIALIZE_OPT(long_term_weight, (uint64_t)0) END_KV_SERIALIZE_MAP() }; diff --git a/src/serialization/binary_archive.h b/src/serialization/binary_archive.h index f47a4494d..242b8fd68 100644 --- a/src/serialization/binary_archive.h +++ b/src/serialization/binary_archive.h @@ -99,7 +99,7 @@ struct binary_archive<false> : public binary_archive_base<std::istream, false> { explicit binary_archive(stream_type &s) : base_type(s) { - stream_type::streampos pos = stream_.tellg(); + stream_type::pos_type pos = stream_.tellg(); stream_.seekg(0, std::ios_base::end); eof_pos_ = stream_.tellg(); stream_.seekg(pos); diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 3cbfb760b..8c0b300ad 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -116,7 +116,7 @@ typedef cryptonote::simple_wallet sw; #define LONG_PAYMENT_ID_SUPPORT_CHECK() \ do { \ if (!m_long_payment_id_support) { \ - fail_msg_writer() << tr("Long payment IDs are obsolete. Use --long-payment-id-support if you really must use one."); \ + fail_msg_writer() << tr("Long payment IDs are obsolete. Use --long-payment-id-support if you really must use one, and warn the recipient they are using an obsolete feature that will disappear in the future."); \ return true; \ } \ } while(0) @@ -2705,8 +2705,10 @@ simple_wallet::simple_wallet() " Set the wallet's refresh behaviour.\n " "priority [0|1|2|3|4]\n " " Set the fee to default/unimportant/normal/elevated/priority.\n " - "confirm-missing-payment-id <1|0>\n " + "confirm-missing-payment-id <1|0> (obsolete)\n " "ask-password <0|1|2 (or never|action|decrypt)>\n " + " action: ask the password before many actions such as transfer, etc\n " + " decrypt: same as action, but keeps the spend key encrypted in memory when not needed\n " "unit <monero|millinero|micronero|nanonero|piconero>\n " " Set the default monero (sub-)unit.\n " "min-outputs-count [n]\n " @@ -8402,7 +8404,8 @@ bool simple_wallet::status(const std::vector<std::string> &args) { uint64_t local_height = m_wallet->get_blockchain_current_height(); uint32_t version = 0; - if (!m_wallet->check_connection(&version)) + bool ssl = false; + if (!m_wallet->check_connection(&version, &ssl)) { success_msg_writer() << "Refreshed " << local_height << "/?, no daemon connected"; return true; @@ -8414,7 +8417,7 @@ bool simple_wallet::status(const std::vector<std::string> &args) { bool synced = local_height == bc_height; success_msg_writer() << "Refreshed " << local_height << "/" << bc_height << ", " << (synced ? "synced" : "syncing") - << ", daemon RPC v" << get_version_string(version); + << ", daemon RPC v" << get_version_string(version) << ", " << (ssl ? "SSL" : "no SSL"); } else { diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 935a8d51c..2b7853330 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1924,7 +1924,7 @@ bool WalletImpl::verifyMessageWithPublicKey(const std::string &message, const st bool WalletImpl::connectToDaemon() { - bool result = m_wallet->check_connection(NULL, DEFAULT_CONNECTION_TIMEOUT_MILLIS); + bool result = m_wallet->check_connection(NULL, NULL, DEFAULT_CONNECTION_TIMEOUT_MILLIS); if (!result) { setStatusError("Error connecting to daemon at " + m_wallet->get_daemon_address()); } else { @@ -1937,7 +1937,7 @@ bool WalletImpl::connectToDaemon() Wallet::ConnectionStatus WalletImpl::connected() const { uint32_t version = 0; - m_is_connected = m_wallet->check_connection(&version, DEFAULT_CONNECTION_TIMEOUT_MILLIS); + m_is_connected = m_wallet->check_connection(&version, NULL, DEFAULT_CONNECTION_TIMEOUT_MILLIS); if (!m_is_connected) return Wallet::ConnectionStatus_Disconnected; // Version check is not implemented in light wallets nodes/wallets diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index c02d10ab4..48e794616 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -237,6 +237,11 @@ struct options { const command_line::arg_descriptor<std::string> password_file = {"password-file", tools::wallet2::tr("Wallet password file"), "", true}; const command_line::arg_descriptor<int> daemon_port = {"daemon-port", tools::wallet2::tr("Use daemon instance at port <arg> instead of 18081"), 0}; const command_line::arg_descriptor<std::string> daemon_login = {"daemon-login", tools::wallet2::tr("Specify username[:password] for daemon RPC client"), "", true}; + const command_line::arg_descriptor<std::string> daemon_ssl = {"daemon-ssl", tools::wallet2::tr("Enable SSL on daemon RPC connections: enabled|disabled|autodetect"), "autodetect"}; + const command_line::arg_descriptor<std::string> daemon_ssl_private_key = {"daemon-ssl-private-key", tools::wallet2::tr("Path to a PEM format private key"), ""}; + const command_line::arg_descriptor<std::string> daemon_ssl_certificate = {"daemon-ssl-certificate", tools::wallet2::tr("Path to a PEM format certificate"), ""}; + const command_line::arg_descriptor<std::vector<std::string>> daemon_ssl_allowed_certificates = {"daemon-ssl-allowed-certificates", tools::wallet2::tr("List of paths to PEM format certificates of allowed RPC servers")}; + const command_line::arg_descriptor<bool> daemon_ssl_allow_any_cert = {"daemon-ssl-allow-any-cert", tools::wallet2::tr("Allow any SSL certificate from the daemon"), false}; const command_line::arg_descriptor<bool> testnet = {"testnet", tools::wallet2::tr("For testnet. Daemon must also be launched with --testnet flag"), false}; const command_line::arg_descriptor<bool> stagenet = {"stagenet", tools::wallet2::tr("For stagenet. Daemon must also be launched with --stagenet flag"), false}; const command_line::arg_descriptor<std::string, false, true, 2> shared_ringdb_dir = { @@ -308,6 +313,14 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl auto daemon_port = command_line::get_arg(vm, opts.daemon_port); auto device_name = command_line::get_arg(vm, opts.hw_device); auto device_derivation_path = command_line::get_arg(vm, opts.hw_device_derivation_path); + auto daemon_ssl_private_key = command_line::get_arg(vm, opts.daemon_ssl_private_key); + auto daemon_ssl_certificate = command_line::get_arg(vm, opts.daemon_ssl_certificate); + auto daemon_ssl_allowed_certificates = command_line::get_arg(vm, opts.daemon_ssl_allowed_certificates); + auto daemon_ssl_allow_any_cert = command_line::get_arg(vm, opts.daemon_ssl_allow_any_cert); + auto daemon_ssl = command_line::get_arg(vm, opts.daemon_ssl); + epee::net_utils::ssl_support_t ssl_support; + THROW_WALLET_EXCEPTION_IF(!epee::net_utils::ssl_support_from_string(ssl_support, daemon_ssl), tools::error::wallet_internal_error, + tools::wallet2::tr("Invalid argument for ") + std::string(opts.daemon_ssl.name)); THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port, tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once")); @@ -358,8 +371,20 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl catch (const std::exception &e) { } } + std::list<std::string> ssl_allowed_certificates; + for (const std::string &path: daemon_ssl_allowed_certificates) + { + ssl_allowed_certificates.push_back({}); + if (!epee::file_io_utils::load_file_to_string(path, ssl_allowed_certificates.back())) + { + MERROR("Failed to load certificate: " << path); + ssl_allowed_certificates.back() = std::string(); + } + } + std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, kdf_rounds, unattended)); - wallet->init(std::move(daemon_address), std::move(login), 0, false, *trusted_daemon); + wallet->init(std::move(daemon_address), std::move(login), 0, *trusted_daemon, ssl_support, std::make_pair(daemon_ssl_private_key, daemon_ssl_certificate), ssl_allowed_certificates, daemon_ssl_allow_any_cert); + boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir); wallet->set_ring_database(ringdb_path.string()); wallet->get_message_store().set_options(vm); @@ -373,7 +398,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl } catch (const std::exception &e) { - MERROR("Failed to parse tx notify spec"); + MERROR("Failed to parse tx notify spec: " << e.what()); } return wallet; @@ -867,7 +892,7 @@ wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional<too w(w), locked(password != boost::none) { - if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt) + if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt || w.watch_only()) { locked = false; return; @@ -1015,6 +1040,11 @@ void wallet2::init_options(boost::program_options::options_description& desc_par command_line::add_arg(desc_params, opts.password_file); command_line::add_arg(desc_params, opts.daemon_port); command_line::add_arg(desc_params, opts.daemon_login); + command_line::add_arg(desc_params, opts.daemon_ssl); + command_line::add_arg(desc_params, opts.daemon_ssl_private_key); + command_line::add_arg(desc_params, opts.daemon_ssl_certificate); + command_line::add_arg(desc_params, opts.daemon_ssl_allowed_certificates); + command_line::add_arg(desc_params, opts.daemon_ssl_allow_any_cert); command_line::add_arg(desc_params, opts.testnet); command_line::add_arg(desc_params, opts.stagenet); command_line::add_arg(desc_params, opts.shared_ringdb_dir); @@ -1066,7 +1096,7 @@ std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::varia } //---------------------------------------------------------------------------------------------------- -bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_weight_limit, bool ssl, bool trusted_daemon) +bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_support_t ssl_support, const std::pair<std::string, std::string> &private_key_and_certificate_path, const std::list<std::string> &allowed_certificates, bool allow_any_cert) { m_checkpoints.init_default_checkpoints(m_nettype); if(m_http_client.is_connected()) @@ -1076,8 +1106,7 @@ bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils:: m_daemon_address = std::move(daemon_address); m_daemon_login = std::move(daemon_login); m_trusted_daemon = trusted_daemon; - // When switching from light wallet to full wallet, we need to reset the height we got from lw node. - return m_http_client.set_server(get_daemon_address(), get_daemon_login(), ssl); + return m_http_client.set_server(get_daemon_address(), get_daemon_login(), ssl_support, private_key_and_certificate_path, allowed_certificates, allow_any_cert); } //---------------------------------------------------------------------------------------------------- bool wallet2::is_deterministic() const @@ -1426,7 +1455,7 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation & } } //---------------------------------------------------------------------------------------------------- -void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs, bool pool) +void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs, bool pool) { THROW_WALLET_EXCEPTION_IF(i >= tx.vout.size(), error::wallet_internal_error, "Invalid vout index"); @@ -1459,11 +1488,14 @@ void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::publi error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key"); } + THROW_WALLET_EXCEPTION_IF(std::find(outs.begin(), outs.end(), i) != outs.end(), error::wallet_internal_error, "Same output cannot be added twice"); outs.push_back(i); - if (tx_scan_info.money_transfered == 0) + if (tx_scan_info.money_transfered == 0 && !miner_tx) { tx_scan_info.money_transfered = tools::decodeRct(tx.rct_signatures, tx_scan_info.received->derivation, i, tx_scan_info.mask, m_account.get_device()); } + THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs[tx_scan_info.received->index] >= std::numeric_limits<uint64_t>::max() - tx_scan_info.money_transfered, + error::wallet_internal_error, "Overflow in received amounts"); tx_money_got_in_outs[tx_scan_info.received->index] += tx_scan_info.money_transfered; tx_scan_info.amount = tx_scan_info.money_transfered; ++num_vouts_received; @@ -1641,7 +1673,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote if (tx_scan_info[i].received) { hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations); - scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool); + scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool); } } } @@ -1664,7 +1696,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote if (tx_scan_info[i].received) { hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations); - scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool); + scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool); } } } @@ -1680,7 +1712,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote boost::unique_lock<hw::device> hwdev_lock (hwdev); hwdev.set_mode(hw::device::NONE); hwdev.conceal_derivation(tx_scan_info[i].received->derivation, tx_pub_key, additional_tx_pub_keys.data, derivation, additional_derivations); - scan_output(tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool); + scan_output(tx, miner_tx, tx_pub_key, i, tx_scan_info[i], num_vouts_received, tx_money_got_in_outs, outs, pool); } } } @@ -2969,6 +3001,11 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo { LOG_PRINT_L1("Another try pull_blocks (try_count=" << try_count << ")..."); first = true; + start_height = 0; + blocks.clear(); + parsed_blocks.clear(); + short_chain_history.clear(); + get_short_chain_history(short_chain_history, 1); ++try_count; } else @@ -4848,7 +4885,7 @@ bool wallet2::prepare_file_names(const std::string& file_path) return true; } //---------------------------------------------------------------------------------------------------- -bool wallet2::check_connection(uint32_t *version, uint32_t timeout) +bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout) { THROW_WALLET_EXCEPTION_IF(!m_is_initialized, error::wallet_not_initialized); @@ -4856,15 +4893,20 @@ bool wallet2::check_connection(uint32_t *version, uint32_t timeout) // TODO: Add light wallet version check. if(m_light_wallet) { - version = 0; + if (version) + *version = 0; + if (ssl) + *ssl = m_light_wallet_connected; // light wallet is always SSL return m_light_wallet_connected; } - if(!m_http_client.is_connected()) + if(!m_http_client.is_connected(ssl)) { m_node_rpc_proxy.invalidate(); if (!m_http_client.connect(std::chrono::milliseconds(timeout))) return false; + if(!m_http_client.is_connected(ssl)) + return false; } if (version) @@ -11308,6 +11350,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation"); } size_t output_index = 0; + bool miner_tx = cryptonote::is_coinbase(spent_tx); for (const cryptonote::tx_out& out : spent_tx.vout) { tx_scan_info_t tx_scan_info; @@ -11315,11 +11358,13 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag THROW_WALLET_EXCEPTION_IF(tx_scan_info.error, error::wallet_internal_error, "check_acc_out_precomp failed"); if (tx_scan_info.received) { - if (tx_scan_info.money_transfered == 0) + if (tx_scan_info.money_transfered == 0 && !miner_tx) { rct::key mask; tx_scan_info.money_transfered = tools::decodeRct(spent_tx.rct_signatures, tx_scan_info.received->derivation, output_index, mask, hwdev); } + THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs >= std::numeric_limits<uint64_t>::max() - tx_scan_info.money_transfered, + error::wallet_internal_error, "Overflow in received amounts"); tx_money_got_in_outs += tx_scan_info.money_transfered; } ++output_index; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 3b2dd6076..ea1172f40 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -676,7 +676,11 @@ namespace tools bool deinit(); bool init(std::string daemon_address = "http://localhost:8080", - boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_weight_limit = 0, bool ssl = false, bool trusted_daemon = false); + boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_weight_limit = 0, + bool trusted_daemon = true, + epee::net_utils::ssl_support_t ssl_support = epee::net_utils::ssl_support_t::e_ssl_support_autodetect, + const std::pair<std::string, std::string> &private_key_and_certificate_path = {}, + const std::list<std::string> &allowed_certificates = {}, bool allow_any_cert = false); void stop() { m_run.store(false, std::memory_order_relaxed); m_message_store.stop(); } @@ -800,7 +804,7 @@ namespace tools bool sign_multisig_tx_to_file(multisig_tx_set &exported_txs, const std::string &filename, std::vector<crypto::hash> &txids); std::vector<pending_tx> create_unmixable_sweep_transactions(); void discard_unmixable_outputs(); - bool check_connection(uint32_t *version = NULL, uint32_t timeout = 200000); + bool check_connection(uint32_t *version = NULL, bool *ssl = NULL, uint32_t timeout = 200000); void get_transfers(wallet2::transfer_container& incoming_transfers) const; void get_payments(const crypto::hash& payment_id, std::list<wallet2::payment_details>& payments, uint64_t min_height = 0, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const; void get_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& payments, uint64_t min_height, uint64_t max_height = (uint64_t)-1, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const; @@ -1292,7 +1296,7 @@ namespace tools bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const; bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const; std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const; - void scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs, bool pool); + void scan_output(const cryptonote::transaction &tx, bool miner_tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs, bool pool); void trim_hashchain(); crypto::key_image get_multisig_composite_key_image(size_t n) const; rct::multisig_kLRki get_multisig_composite_kLRki(size_t n, const std::unordered_set<crypto::public_key> &ignore_set, std::unordered_set<rct::key> &used_L, std::unordered_set<rct::key> &new_used_L) const; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index c87c2fca6..2cfd39f04 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -63,6 +63,10 @@ namespace const command_line::arg_descriptor<bool> arg_restricted = {"restricted-rpc", "Restricts to view-only commands", false}; const command_line::arg_descriptor<std::string> arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"}; const command_line::arg_descriptor<bool> arg_prompt_for_password = {"prompt-for-password", "Prompts for password when not provided", false}; + const command_line::arg_descriptor<std::string> arg_rpc_ssl = {"rpc-ssl", tools::wallet2::tr("Enable SSL on wallet RPC connections: enabled|disabled|autodetect"), "autodetect"}; + const command_line::arg_descriptor<std::string> arg_rpc_ssl_private_key = {"rpc-ssl-private-key", tools::wallet2::tr("Path to a PEM format private key"), ""}; + const command_line::arg_descriptor<std::string> arg_rpc_ssl_certificate = {"rpc-ssl-certificate", tools::wallet2::tr("Path to a PEM format certificate"), ""}; + const command_line::arg_descriptor<std::vector<std::string>> arg_rpc_ssl_allowed_certificates = {"rpc-ssl-allowed-certificates", tools::wallet2::tr("List of paths to PEM format certificates of allowed RPC servers (all allowed if empty)")}; constexpr const char default_rpc_username[] = "monero"; @@ -233,10 +237,32 @@ namespace tools assert(bool(http_login)); } // end auth enabled + auto rpc_ssl_private_key = command_line::get_arg(vm, arg_rpc_ssl_private_key); + auto rpc_ssl_certificate = command_line::get_arg(vm, arg_rpc_ssl_certificate); + auto rpc_ssl_allowed_certificates = command_line::get_arg(vm, arg_rpc_ssl_allowed_certificates); + auto rpc_ssl = command_line::get_arg(vm, arg_rpc_ssl); + epee::net_utils::ssl_support_t rpc_ssl_support; + if (!epee::net_utils::ssl_support_from_string(rpc_ssl_support, rpc_ssl)) + { + MERROR("Invalid argument for " << std::string(arg_rpc_ssl.name)); + return false; + } + std::list<std::string> allowed_certificates; + for (const std::string &path: rpc_ssl_allowed_certificates) + { + allowed_certificates.push_back({}); + if (!epee::file_io_utils::load_file_to_string(path, allowed_certificates.back())) + { + MERROR("Failed to load certificate: " << path); + allowed_certificates.back() = std::string(); + } + } + 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->access_control_origins), std::move(http_login), + rpc_ssl_support, std::make_pair(rpc_ssl_private_key, rpc_ssl_certificate), allowed_certificates ); } //------------------------------------------------------------------------------------------------------------------------------ @@ -337,30 +363,54 @@ namespace tools if (!m_wallet) return not_open(er); try { - res.balance = m_wallet->balance(req.account_index); - res.unlocked_balance = m_wallet->unlocked_balance(req.account_index); + res.balance = req.all_accounts ? m_wallet->balance_all() : m_wallet->balance(req.account_index); + res.unlocked_balance = req.all_accounts ? m_wallet->unlocked_balance_all() : m_wallet->unlocked_balance(req.account_index); res.multisig_import_needed = m_wallet->multisig() && m_wallet->has_multisig_partial_key_images(); - std::map<uint32_t, uint64_t> balance_per_subaddress = m_wallet->balance_per_subaddress(req.account_index); - std::map<uint32_t, uint64_t> unlocked_balance_per_subaddress = m_wallet->unlocked_balance_per_subaddress(req.account_index); - std::vector<tools::wallet2::transfer_details> transfers; - m_wallet->get_transfers(transfers); - std::set<uint32_t> address_indices = req.address_indices; - if (address_indices.empty()) + std::map<uint32_t, std::map<uint32_t, uint64_t>> balance_per_subaddress_per_account; + std::map<uint32_t, std::map<uint32_t, uint64_t>> unlocked_balance_per_subaddress_per_account; + if (req.all_accounts) { - for (const auto& i : balance_per_subaddress) - address_indices.insert(i.first); + for (uint32_t account_index = 0; account_index < m_wallet->get_num_subaddress_accounts(); ++account_index) + { + balance_per_subaddress_per_account[account_index] = m_wallet->balance_per_subaddress(account_index); + unlocked_balance_per_subaddress_per_account[account_index] = m_wallet->unlocked_balance_per_subaddress(account_index); + } } - for (uint32_t i : address_indices) + else { - wallet_rpc::COMMAND_RPC_GET_BALANCE::per_subaddress_info info; - info.address_index = i; - cryptonote::subaddress_index index = {req.account_index, info.address_index}; - info.address = m_wallet->get_subaddress_as_str(index); - info.balance = balance_per_subaddress[i]; - info.unlocked_balance = unlocked_balance_per_subaddress[i]; - info.label = m_wallet->get_subaddress_label(index); - info.num_unspent_outputs = std::count_if(transfers.begin(), transfers.end(), [&](const tools::wallet2::transfer_details& td) { return !td.m_spent && td.m_subaddr_index == index; }); - res.per_subaddress.push_back(info); + balance_per_subaddress_per_account[req.account_index] = m_wallet->balance_per_subaddress(req.account_index); + unlocked_balance_per_subaddress_per_account[req.account_index] = m_wallet->unlocked_balance_per_subaddress(req.account_index); + } + std::vector<tools::wallet2::transfer_details> transfers; + m_wallet->get_transfers(transfers); + for (const auto& p : balance_per_subaddress_per_account) + { + uint32_t account_index = p.first; + std::map<uint32_t, uint64_t> balance_per_subaddress = p.second; + std::map<uint32_t, uint64_t> unlocked_balance_per_subaddress = unlocked_balance_per_subaddress_per_account[account_index]; + std::set<uint32_t> address_indices; + if (!req.all_accounts && !req.address_indices.empty()) + { + address_indices = req.address_indices; + } + else + { + for (const auto& i : balance_per_subaddress) + address_indices.insert(i.first); + } + for (uint32_t i : address_indices) + { + wallet_rpc::COMMAND_RPC_GET_BALANCE::per_subaddress_info info; + info.account_index = account_index; + info.address_index = i; + cryptonote::subaddress_index index = {info.account_index, info.address_index}; + info.address = m_wallet->get_subaddress_as_str(index); + info.balance = balance_per_subaddress[i]; + info.unlocked_balance = unlocked_balance_per_subaddress[i]; + info.label = m_wallet->get_subaddress_label(index); + info.num_unspent_outputs = std::count_if(transfers.begin(), transfers.end(), [&](const tools::wallet2::transfer_details& td) { return !td.m_spent && td.m_subaddr_index == index; }); + res.per_subaddress.emplace_back(std::move(info)); + } } } catch (const std::exception& e) @@ -691,13 +741,9 @@ namespace tools if (wallet2::parse_long_payment_id(payment_id_str, long_payment_id)) { cryptonote::set_payment_id_to_tx_extra_nonce(extra_nonce, long_payment_id); } - /* or short payment ID */ - else if (wallet2::parse_short_payment_id(payment_id_str, short_payment_id)) { - cryptonote::set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, short_payment_id); - } else { er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; - er.message = "Payment id has invalid format: \"" + payment_id_str + "\", expected 16 or 64 character string"; + er.message = "Payment id has invalid format: \"" + payment_id_str + "\", expected 64 character string"; return false; } @@ -2287,10 +2333,18 @@ namespace tools max_height = req.max_height <= max_height ? req.max_height : max_height; } + boost::optional<uint32_t> account_index = req.account_index; + std::set<uint32_t> subaddr_indices = req.subaddr_indices; + if (req.all_accounts) + { + account_index = boost::none; + subaddr_indices.clear(); + } + if (req.in) { std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments; - m_wallet->get_payments(payments, min_height, max_height, req.account_index, req.subaddr_indices); + m_wallet->get_payments(payments, min_height, max_height, account_index, subaddr_indices); for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { res.in.push_back(wallet_rpc::transfer_entry()); fill_transfer_entry(res.in.back(), i->second.m_tx_hash, i->first, i->second); @@ -2300,7 +2354,7 @@ namespace tools if (req.out) { std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments; - m_wallet->get_payments_out(payments, min_height, max_height, req.account_index, req.subaddr_indices); + m_wallet->get_payments_out(payments, min_height, max_height, account_index, subaddr_indices); for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { res.out.push_back(wallet_rpc::transfer_entry()); fill_transfer_entry(res.out.back(), i->first, i->second); @@ -2309,7 +2363,7 @@ namespace tools if (req.pending || req.failed) { std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments; - m_wallet->get_unconfirmed_payments_out(upayments, req.account_index, req.subaddr_indices); + m_wallet->get_unconfirmed_payments_out(upayments, account_index, subaddr_indices); for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) { const tools::wallet2::unconfirmed_transfer_details &pd = i->second; bool is_failed = pd.m_state == tools::wallet2::unconfirmed_transfer_details::failed; @@ -2326,7 +2380,7 @@ namespace tools m_wallet->update_pool_state(); std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments; - m_wallet->get_unconfirmed_payments(payments, req.account_index, req.subaddr_indices); + m_wallet->get_unconfirmed_payments(payments, account_index, subaddr_indices); for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { res.pool.push_back(wallet_rpc::transfer_entry()); fill_transfer_entry(res.pool.back(), i->first, i->second); @@ -3741,6 +3795,57 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_validate_address(const wallet_rpc::COMMAND_RPC_VALIDATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_VALIDATE_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + cryptonote::address_parse_info info; + static const struct { cryptonote::network_type type; const char *stype; } net_types[] = { + { cryptonote::MAINNET, "mainnet" }, + { cryptonote::TESTNET, "testnet" }, + { cryptonote::STAGENET, "stagenet" }, + }; + for (const auto &net_type: net_types) + { + if (!req.any_net_type && net_type.type != m_wallet->nettype()) + continue; + if (req.allow_openalias) + { + std::string address; + res.valid = get_account_address_from_str_or_url(info, net_type.type, req.address, + [&er, &address](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string { + if (!dnssec_valid) + { + er.message = std::string("Invalid DNSSEC for ") + url; + return {}; + } + if (addresses.empty()) + { + er.message = std::string("No Monero address found at ") + url; + return {}; + } + address = addresses[0]; + return address; + }); + if (res.valid) + res.openalias_address = address; + } + else + { + res.valid = cryptonote::get_account_address_from_str(info, net_type.type, req.address); + } + if (res.valid) + { + res.integrated = info.has_payment_id; + res.subaddress = info.is_subaddress; + res.nettype = net_type.stype; + return true; + } + } + + er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; + er.message = std::string("Invalid address"); + return false; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx) { res.version = WALLET_RPC_VERSION; @@ -3937,6 +4042,10 @@ int main(int argc, char** argv) { command_line::add_arg(desc_params, arg_from_json); command_line::add_arg(desc_params, arg_wallet_dir); command_line::add_arg(desc_params, arg_prompt_for_password); + command_line::add_arg(desc_params, arg_rpc_ssl); + command_line::add_arg(desc_params, arg_rpc_ssl_private_key); + command_line::add_arg(desc_params, arg_rpc_ssl_certificate); + command_line::add_arg(desc_params, arg_rpc_ssl_allowed_certificates); daemonizer::init_options(hidden_options, desc_params); desc_params.add(hidden_options); diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 1a54e4c79..8157344c2 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -147,6 +147,7 @@ namespace tools MAP_JON_RPC_WE("exchange_multisig_keys", on_exchange_multisig_keys, wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS) MAP_JON_RPC_WE("sign_multisig", on_sign_multisig, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG) MAP_JON_RPC_WE("submit_multisig", on_submit_multisig, wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG) + MAP_JON_RPC_WE("validate_address", on_validate_address, wallet_rpc::COMMAND_RPC_VALIDATE_ADDRESS) MAP_JON_RPC_WE("get_version", on_get_version, wallet_rpc::COMMAND_RPC_GET_VERSION) END_JSON_RPC_MAP() END_URI_MAP2() @@ -226,6 +227,7 @@ namespace tools bool on_exchange_multisig_keys(const wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::request& req, wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_sign_multisig(const wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_submit_multisig(const wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); + bool on_validate_address(const wallet_rpc::COMMAND_RPC_VALIDATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_VALIDATE_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); //json rpc v2 diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index f0c1a4e9d..c450b2b37 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -47,7 +47,7 @@ // advance which version they will stop working with // Don't go over 32767 for any of these #define WALLET_RPC_VERSION_MAJOR 1 -#define WALLET_RPC_VERSION_MINOR 7 +#define WALLET_RPC_VERSION_MINOR 8 #define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR) namespace tools @@ -63,14 +63,17 @@ namespace wallet_rpc { uint32_t account_index; std::set<uint32_t> address_indices; + bool all_accounts; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(account_index) KV_SERIALIZE(address_indices) + KV_SERIALIZE_OPT(all_accounts, false); END_KV_SERIALIZE_MAP() }; struct per_subaddress_info { + uint32_t account_index; uint32_t address_index; std::string address; uint64_t balance; @@ -79,6 +82,7 @@ namespace wallet_rpc uint64_t num_unspent_outputs; BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(account_index) KV_SERIALIZE(address_index) KV_SERIALIZE(address) KV_SERIALIZE(balance) @@ -1418,6 +1422,7 @@ namespace wallet_rpc uint64_t max_height; uint32_t account_index; std::set<uint32_t> subaddr_indices; + bool all_accounts; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(in); @@ -1430,6 +1435,7 @@ namespace wallet_rpc KV_SERIALIZE_OPT(max_height, (uint64_t)CRYPTONOTE_MAX_BLOCK_NUMBER); KV_SERIALIZE(account_index); KV_SERIALIZE(subaddr_indices); + KV_SERIALIZE_OPT(all_accounts, false); END_KV_SERIALIZE_MAP() }; @@ -2189,5 +2195,40 @@ namespace wallet_rpc }; }; + struct COMMAND_RPC_VALIDATE_ADDRESS + { + struct request_t + { + std::string address; + bool any_net_type; + bool allow_openalias; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(address) + KV_SERIALIZE_OPT(any_net_type, false) + KV_SERIALIZE_OPT(allow_openalias, false) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<request_t> request; + + struct response_t + { + bool valid; + bool integrated; + bool subaddress; + std::string nettype; + std::string openalias_address; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(valid) + KV_SERIALIZE(integrated) + KV_SERIALIZE(subaddress) + KV_SERIALIZE(nettype) + KV_SERIALIZE(openalias_address) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init<response_t> response; + }; + } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 20afd4203..89d77cf32 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -85,6 +85,7 @@ add_subdirectory(performance_tests) add_subdirectory(core_proxy) add_subdirectory(unit_tests) add_subdirectory(difficulty) +add_subdirectory(block_weight) add_subdirectory(hash) add_subdirectory(net_load_tests) if (BUILD_GUI_DEPS) @@ -115,6 +116,7 @@ add_test( set(enabled_tests core_tests difficulty + block_weight hash performance_tests core_proxy diff --git a/tests/block_weight/CMakeLists.txt b/tests/block_weight/CMakeLists.txt new file mode 100644 index 000000000..b0d716ea0 --- /dev/null +++ b/tests/block_weight/CMakeLists.txt @@ -0,0 +1,45 @@ +# Copyright (c) 2014-2018, 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. + +set(block_weight_sources + block_weight.cpp) + +set(block_weight_headers) + +add_executable(block_weight + ${block_weight_sources} + ${block_weight_headers}) +target_link_libraries(block_weight + PRIVATE + cryptonote_core + blockchain_db + ${EXTRA_LIBRARIES}) + +add_test( + NAME block_weight + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/compare.py ${CMAKE_CURRENT_SOURCE_DIR}/block_weight.py ${CMAKE_CURRENT_BINARY_DIR}/block_weight) diff --git a/tests/block_weight/block_weight.cpp b/tests/block_weight/block_weight.cpp new file mode 100644 index 000000000..a67f40f0f --- /dev/null +++ b/tests/block_weight/block_weight.cpp @@ -0,0 +1,183 @@ +// 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. + +#define IN_UNIT_TESTS + +#include <stdio.h> +#include <math.h> +#include "cryptonote_core/blockchain.h" +#include "cryptonote_core/tx_pool.h" +#include "cryptonote_core/cryptonote_core.h" +#include "blockchain_db/testdb.h" + +#define LONG_TERM_BLOCK_WEIGHT_WINDOW 5000 + +enum test_t +{ + test_max = 0, + test_lcg = 1, + test_min = 2, +}; + +namespace +{ + +class TestDB: public cryptonote::BaseTestDB +{ +private: + struct block_t + { + size_t weight; + uint64_t long_term_weight; + }; + +public: + TestDB() { m_open = true; } + + virtual void add_block( const cryptonote::block& blk + , size_t block_weight + , uint64_t long_term_block_weight + , const cryptonote::difficulty_type& cumulative_difficulty + , const uint64_t& coins_generated + , uint64_t num_rct_outs + , const crypto::hash& blk_hash + ) override { + blocks.push_back({block_weight, long_term_block_weight}); + } + virtual uint64_t height() const override { return blocks.size(); } + virtual size_t get_block_weight(const uint64_t &h) const override { return blocks[h].weight; } + virtual uint64_t get_block_long_term_weight(const uint64_t &h) const override { return blocks[h].long_term_weight; } + virtual crypto::hash top_block_hash() const override { + uint64_t h = height(); + crypto::hash top = crypto::null_hash; + if (h) + *(uint64_t*)&top = h - 1; + return top; + } + virtual void pop_block(cryptonote::block &blk, std::vector<cryptonote::transaction> &txs) override { blocks.pop_back(); } + virtual void set_hard_fork_version(uint64_t height, uint8_t version) override { if (height >= hf.size()) hf.resize(height + 1); hf[height] = version; } + virtual uint8_t get_hard_fork_version(uint64_t height) const override { if (height >= hf.size()) return 255; return hf[height]; } + +private: + std::vector<block_t> blocks; + std::vector<uint8_t> hf; +}; + +} + +#define PREFIX_WINDOW(hf_version,window) \ + std::unique_ptr<cryptonote::Blockchain> bc; \ + cryptonote::tx_memory_pool txpool(*bc); \ + bc.reset(new cryptonote::Blockchain(txpool)); \ + struct get_test_options { \ + const std::pair<uint8_t, uint64_t> hard_forks[3]; \ + const cryptonote::test_options test_options = { \ + hard_forks, \ + window, \ + }; \ + get_test_options(): hard_forks{std::make_pair(1, (uint64_t)0), std::make_pair((uint8_t)hf_version, (uint64_t)LONG_TERM_BLOCK_WEIGHT_WINDOW), std::make_pair((uint8_t)0, (uint64_t)0)} {} \ + } opts; \ + cryptonote::Blockchain *blockchain = bc.get(); \ + bool r = blockchain->init(new TestDB(), cryptonote::FAKECHAIN, true, &opts.test_options, 0, NULL); \ + if (!r) \ + { \ + fprintf(stderr, "Failed to init blockchain\n"); \ + exit(1); \ + } + +#define PREFIX(hf_version) PREFIX_WINDOW(hf_version, LONG_TERM_BLOCK_WEIGHT_WINDOW) + +static uint32_t lcg_seed = 0; + +static uint32_t lcg() +{ + lcg_seed = (lcg_seed * 0x100000001b3 + 0xcbf29ce484222325) & 0xffffffff; + return lcg_seed; +} + +static void test(test_t t, uint64_t blocks) +{ + PREFIX(10); + + for (uint64_t h = 0; h < LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) + { + cryptonote::block b; + b.major_version = 1; + b.minor_version = 1; + bc->get_db().add_block(b, 300000, 300000, bc->get_db().height(), bc->get_db().height(), {}); + if (!bc->update_next_cumulative_weight_limit()) + { + fprintf(stderr, "Failed to update cumulative weight limit 1\n"); + exit(1); + } + } + + for (uint64_t h = 0; h < blocks; ++h) + { + uint64_t w; + uint64_t effective_block_weight_median = bc->get_current_cumulative_block_weight_median(); + switch (t) + { + case test_lcg: + { + uint32_t r = lcg(); + int64_t wi = 90 + r % 500000 + 250000 + sin(h / 200.) * 350000; + w = wi < 90 ? 90 : wi; + break; + } + case test_max: + w = bc->get_current_cumulative_block_weight_limit(); + break; + case test_min: + w = 90; + break; + default: + exit(1); + } + uint64_t ltw = bc->get_next_long_term_block_weight(w); + cryptonote::block b; + b.major_version = 10; + b.minor_version = 10; + bc->get_db().add_block(std::move(b), w, ltw, bc->get_db().height(), bc->get_db().height(), {}); + + if (!bc->update_next_cumulative_weight_limit()) + { + fprintf(stderr, "Failed to update cumulative weight limit\n"); + exit(1); + } + std::cout << "H " << h << ", BW " << w << ", EMBW " << effective_block_weight_median << ", LTBW " << ltw << std::endl; + } +} + +int main() +{ + test(test_max, 2 * LONG_TERM_BLOCK_WEIGHT_WINDOW); + test(test_lcg, 9 * LONG_TERM_BLOCK_WEIGHT_WINDOW); + test(test_min, 1 * LONG_TERM_BLOCK_WEIGHT_WINDOW); + return 0; +} diff --git a/tests/block_weight/block_weight.py b/tests/block_weight/block_weight.py new file mode 100755 index 000000000..06aaabb02 --- /dev/null +++ b/tests/block_weight/block_weight.py @@ -0,0 +1,74 @@ +#!/usr/bin/python +# Simulate a maximal block attack on the Monero network +# This uses the scheme proposed by ArticMine +# Written by Sarang Nother +# Copyright (c) 2019 The Monero Project +import sys +import math + +MEDIAN_WINDOW_SMALL = 100 # number of recent blocks for median computation +MEDIAN_WINDOW_BIG = 5000 +MULTIPLIER_SMALL = 1.4 # multipliers for determining weights +MULTIPLIER_BIG = 50.0 +MEDIAN_THRESHOLD = 300*1000 # initial value for median (scaled kB -> B) +lcg_seed = 0 +embw = MEDIAN_THRESHOLD +ltembw = MEDIAN_THRESHOLD + +weights = [MEDIAN_THRESHOLD]*MEDIAN_WINDOW_SMALL # weights of recent blocks (B), with index -1 most recent +lt_weights = [MEDIAN_THRESHOLD]*MEDIAN_WINDOW_BIG # long-term weights + +# Compute the median of a list +def get_median(vec): + #temp = vec + temp = sorted(vec) + if len(temp) % 2 == 1: + return temp[len(temp)/2] + else: + return int((temp[len(temp)/2]+temp[len(temp)/2-1])/2) + +def LCG(): + global lcg_seed + lcg_seed = (lcg_seed * 0x100000001b3 + 0xcbf29ce484222325) & 0xffffffff + return lcg_seed + +def run(t, blocks): + global embw + global ltembw + + weights = [MEDIAN_THRESHOLD]*MEDIAN_WINDOW_SMALL # weights of recent blocks (B), with index -1 most recent + lt_weights = [MEDIAN_THRESHOLD]*MEDIAN_WINDOW_BIG # long-term weights + + for block in range(blocks): + # determine the long-term effective weight + ltmedian = get_median(lt_weights[-MEDIAN_WINDOW_BIG:]) + ltembw = max(MEDIAN_THRESHOLD,ltmedian) + + # determine the effective weight + stmedian = get_median(weights[-MEDIAN_WINDOW_SMALL:]) + embw = min(max(MEDIAN_THRESHOLD,stmedian),int(MULTIPLIER_BIG*ltembw)) + + # drop the lowest values + weights = weights[1:] + lt_weights = lt_weights[1:] + + # add a block of max weight + if t == 0: + max_weight = 2 * embw + elif t == 1: + r = LCG() + max_weight = int(90 + r % 500000 + 250000 + math.sin(block / 200.) * 350000) + if max_weight < 90: max_weight = 90 + elif t == 2: + max_weight = 90 + else: + sys.exit(1) + weights.append(max_weight) + lt_weights.append(min(max_weight,int(ltembw + int(ltembw * 2 / 5)))) + + #print "H %u, r %u, BW %u, EMBW %u, LTBW %u, LTEMBW %u, ltmedian %u" % (block, r, max_weight, embw, lt_weights[-1], ltembw, ltmedian) + print "H %u, BW %u, EMBW %u, LTBW %u" % (block, max_weight, embw, lt_weights[-1]) + +run(0, 2 * MEDIAN_WINDOW_BIG) +run(1, 9 * MEDIAN_WINDOW_BIG) +run(2, 1 * MEDIAN_WINDOW_BIG) diff --git a/tests/block_weight/compare.py b/tests/block_weight/compare.py new file mode 100755 index 000000000..c6be05206 --- /dev/null +++ b/tests/block_weight/compare.py @@ -0,0 +1,13 @@ +#!/usr/bin/python + +import sys +import subprocess + +print 'running: ', sys.argv[1] +S0 = subprocess.check_output(sys.argv[1], stderr=subprocess.STDOUT) +print 'running: ', sys.argv[2] +S1 = subprocess.check_output(sys.argv[2], stderr=subprocess.STDOUT) +print 'comparing' +if S0 != S1: + sys.exit(1) +sys.exit(0) diff --git a/tests/core_tests/v2_tests.h b/tests/core_tests/v2_tests.h index ecb23818c..cd2bcb839 100644 --- a/tests/core_tests/v2_tests.h +++ b/tests/core_tests/v2_tests.h @@ -81,7 +81,7 @@ template<> struct get_test_options<gen_v2_tx_validation_base> { const std::pair<uint8_t, uint64_t> hard_forks[3] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(0, 0)}; const cryptonote::test_options test_options = { - hard_forks + hard_forks, 0 }; }; diff --git a/tests/crypto/CMakeLists.txt b/tests/crypto/CMakeLists.txt index df96c57cc..8789cb552 100644 --- a/tests/crypto/CMakeLists.txt +++ b/tests/crypto/CMakeLists.txt @@ -52,3 +52,17 @@ set_property(TARGET cncrypto-tests add_test( NAME cncrypto COMMAND cncrypto-tests "${CMAKE_CURRENT_SOURCE_DIR}/tests.txt") + +add_executable(cnv4-jit-tests cnv4-jit.c) +target_link_libraries(cnv4-jit-tests + PRIVATE + crypto + common + ${EXTRA_LIBRARIES}) +set_property(TARGET cnv4-jit-tests + PROPERTY + FOLDER "tests") + +add_test( + NAME cnv4-jit + COMMAND cnv4-jit-tests 1788000 1789000) diff --git a/tests/crypto/cnv4-jit.c b/tests/crypto/cnv4-jit.c new file mode 100644 index 000000000..0f11e4393 --- /dev/null +++ b/tests/crypto/cnv4-jit.c @@ -0,0 +1,119 @@ +// 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 <stdint.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include "crypto/hash-ops.h" + +extern volatile int use_v4_jit_flag; + +static int test(const uint8_t *data, size_t len, uint64_t height) +{ + char hash0[32], hash1[32]; + use_v4_jit_flag = 0; + cn_slow_hash(data, len, hash0, 4, 0, height); + use_v4_jit_flag = 1; + cn_slow_hash(data, len, hash1, 4, 0, height); + return memcmp(hash0, hash1, 32); +} + +int main(int argc, char **argv) +{ + uint8_t data[64]; + uint64_t start_height = 1788000; + uint64_t end_height = 1788001; + + if (argc != 1 && argc != 2 && argc != 3) + { + fprintf(stderr, "usage: %s [<start_height> [<end_height>]]\n", argv[0]); + return 1; + } + if (argc > 1) + { + errno = 0; + start_height = strtoull(argv[1], NULL, 10); + if ((start_height == 0 && errno) || start_height == ULLONG_MAX) + { + fprintf(stderr, "invalid start_height\n"); + return 1; + } + end_height = start_height; + if (argc > 2) + { + errno = 0; + end_height = strtoull(argv[2], NULL, 10); + if ((end_height == 0 && errno) || end_height == ULLONG_MAX) + { + fprintf(stderr, "invalid end_height\n"); + return 1; + } + } + } + + if (start_height == end_height) + { + uint64_t counter = 0; + while (1) + { + printf("\r%llu", (unsigned long long)counter); + fflush(stdout); + size_t offset = 0; + while (offset + 8 < sizeof(data)) + { + memcpy(data + offset, &counter, sizeof(counter)); + offset += 8; + } + if (test(data, sizeof(data), start_height)) + { + fprintf(stderr, "\nFailure at height %llu, counter %llu\n", (unsigned long long)start_height, (unsigned long long)counter); + return 0; + } + ++counter; + } + } + + memset(data, 0x42, sizeof(data)); + for (uint64_t h = start_height; h < end_height; ++h) + { + printf("\r%llu/%llu", (unsigned long long)(h-start_height), (unsigned long long)(end_height-start_height)); + fflush(stdout); + if (test(data, sizeof(data), h)) + { + fprintf(stderr, "\nFailure at height %llu\n", (unsigned long long)h); + return 0; + } + } + + printf("\r"); + + return 0; +} diff --git a/tests/hash/CMakeLists.txt b/tests/hash/CMakeLists.txt index 433cf94e9..105cf2c13 100644 --- a/tests/hash/CMakeLists.txt +++ b/tests/hash/CMakeLists.txt @@ -43,7 +43,7 @@ set_property(TARGET hash-tests PROPERTY FOLDER "tests") -foreach (hash IN ITEMS fast slow slow-1 slow-2 tree extra-blake extra-groestl extra-jh extra-skein) +foreach (hash IN ITEMS fast slow slow-1 slow-2 slow-4 tree extra-blake extra-groestl extra-jh extra-skein) add_test( NAME "hash-${hash}" COMMAND hash-tests "${hash}" "${CMAKE_CURRENT_SOURCE_DIR}/tests-${hash}.txt") diff --git a/tests/hash/main.cpp b/tests/hash/main.cpp index 7767d0d3b..acfd99e96 100644 --- a/tests/hash/main.cpp +++ b/tests/hash/main.cpp @@ -44,6 +44,13 @@ using namespace std; using namespace crypto; typedef crypto::hash chash; +struct V4_Data +{ + const void* data; + size_t length; + uint64_t height; +}; + PUSH_WARNINGS DISABLE_VS_WARNINGS(4297) extern "C" { @@ -54,13 +61,17 @@ extern "C" { tree_hash((const char (*)[crypto::HASH_SIZE]) data, length >> 5, hash); } static void cn_slow_hash_0(const void *data, size_t length, char *hash) { - return cn_slow_hash(data, length, hash, 0/*variant*/, 0/*prehashed*/); + return cn_slow_hash(data, length, hash, 0/*variant*/, 0/*prehashed*/, 0/*height*/); } static void cn_slow_hash_1(const void *data, size_t length, char *hash) { - return cn_slow_hash(data, length, hash, 1/*variant*/, 0/*prehashed*/); + return cn_slow_hash(data, length, hash, 1/*variant*/, 0/*prehashed*/, 0/*height*/); } static void cn_slow_hash_2(const void *data, size_t length, char *hash) { - return cn_slow_hash(data, length, hash, 2/*variant*/, 0/*prehashed*/); + return cn_slow_hash(data, length, hash, 2/*variant*/, 0/*prehashed*/, 0/*height*/); + } + static void cn_slow_hash_4(const void *data, size_t, char *hash) { + const V4_Data* p = reinterpret_cast<const V4_Data*>(data); + return cn_slow_hash(p->data, p->length, hash, 4/*variant*/, 0/*prehashed*/, p->height); } } POP_WARNINGS @@ -72,7 +83,7 @@ struct hash_func { } hashes[] = {{"fast", cn_fast_hash}, {"slow", cn_slow_hash_0}, {"tree", hash_tree}, {"extra-blake", hash_extra_blake}, {"extra-groestl", hash_extra_groestl}, {"extra-jh", hash_extra_jh}, {"extra-skein", hash_extra_skein}, - {"slow-1", cn_slow_hash_1}, {"slow-2", cn_slow_hash_2}}; + {"slow-1", cn_slow_hash_1}, {"slow-2", cn_slow_hash_2}, {"slow-4", cn_slow_hash_4}}; int test_variant2_int_sqrt(); int test_variant2_int_sqrt_ref(); @@ -140,7 +151,15 @@ int main(int argc, char *argv[]) { input.exceptions(ios_base::badbit | ios_base::failbit | ios_base::eofbit); input.clear(input.rdstate()); get(input, data); - f(data.data(), data.size(), (char *) &actual); + if (f == cn_slow_hash_4) { + V4_Data d; + d.data = data.data(); + d.length = data.size(); + get(input, d.height); + f(&d, 0, (char *) &actual); + } else { + f(data.data(), data.size(), (char *) &actual); + } if (expected != actual) { size_t i; cerr << "Hash mismatch on test " << test << endl << "Input: "; diff --git a/tests/hash/tests-slow-4.txt b/tests/hash/tests-slow-4.txt new file mode 100644 index 000000000..06ab52cff --- /dev/null +++ b/tests/hash/tests-slow-4.txt @@ -0,0 +1,10 @@ +f759588ad57e758467295443a9bd71490abff8e9dad1b95b6bf2f5d0d78387bc 5468697320697320612074657374205468697320697320612074657374205468697320697320612074657374 1806260 +5bb833deca2bdd7252a9ccd7b4ce0b6a4854515794b56c207262f7a5b9bdb566 4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e67 1806261 +1ee6728da60fbd8d7d55b2b1ade487a3cf52a2c3ac6f520db12c27d8921f6cab 656c69742c2073656420646f20656975736d6f642074656d706f7220696e6369646964756e74207574206c61626f7265 1806262 +6969fe2ddfb758438d48049f302fc2108a4fcc93e37669170e6db4b0b9b4c4cb 657420646f6c6f7265206d61676e6120616c697175612e20557420656e696d206164206d696e696d2076656e69616d2c 1806263 +7f3048b4e90d0cbe7a57c0394f37338a01fae3adfdc0e5126d863a895eb04e02 71756973206e6f737472756420657865726369746174696f6e20756c6c616d636f206c61626f726973206e697369 1806264 +1d290443a4b542af04a82f6b2494a6ee7f20f2754c58e0849032483a56e8e2ef 757420616c697175697020657820656120636f6d6d6f646f20636f6e7365717561742e20447569732061757465 1806265 +c43cc6567436a86afbd6aa9eaa7c276e9806830334b614b2bee23cc76634f6fd 697275726520646f6c6f7220696e20726570726568656e646572697420696e20766f6c7570746174652076656c6974 1806266 +87be2479c0c4e8edfdfaa5603e93f4265b3f8224c1c5946feb424819d18990a4 657373652063696c6c756d20646f6c6f726520657520667567696174206e756c6c612070617269617475722e 1806267 +dd9d6a6d8e47465cceac0877ef889b93e7eba979557e3935d7f86dce11b070f3 4578636570746575722073696e74206f6363616563617420637570696461746174206e6f6e2070726f6964656e742c 1806268 +75c6f2ae49a20521de97285b431e717125847fb8935ed84a61e7f8d36a2c3d8e 73756e7420696e2063756c706120717569206f666669636961206465736572756e74206d6f6c6c697420616e696d20696420657374206c61626f72756d2e 1806269 diff --git a/tests/performance_tests/cn_slow_hash.h b/tests/performance_tests/cn_slow_hash.h index b484afa41..79ebb8778 100644 --- a/tests/performance_tests/cn_slow_hash.h +++ b/tests/performance_tests/cn_slow_hash.h @@ -34,6 +34,7 @@ #include "crypto/crypto.h" #include "cryptonote_basic/cryptonote_basic.h" +template<unsigned int variant> class test_cn_slow_hash { public: @@ -42,18 +43,15 @@ public: #pragma pack(push, 1) struct data_t { - char data[13]; + char data[43]; }; #pragma pack(pop) - static_assert(13 == sizeof(data_t), "Invalid structure size"); + static_assert(43 == sizeof(data_t), "Invalid structure size"); bool init() { - if (!epee::string_tools::hex_to_pod("63617665617420656d70746f72", m_data)) - return false; - - if (!epee::string_tools::hex_to_pod("bbec2cacf69866a8e740380fe7b818fc78f8571221742d729d9d02d7f8989b87", m_expected_hash)) + if (!epee::string_tools::hex_to_pod("63617665617420656d70746f763617665617420656d70746f72263617665617420656d70746f7201020304", m_data)) return false; return true; @@ -62,11 +60,10 @@ public: bool test() { crypto::hash hash; - crypto::cn_slow_hash(&m_data, sizeof(m_data), hash); - return hash == m_expected_hash; + crypto::cn_slow_hash(&m_data, sizeof(m_data), hash, variant); + return true; } private: data_t m_data; - crypto::hash m_expected_hash; }; diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index bfe6de895..22a86cb59 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -77,10 +77,12 @@ int main(int argc, char** argv) const command_line::arg_descriptor<bool> arg_verbose = { "verbose", "Verbose output", false }; const command_line::arg_descriptor<bool> arg_stats = { "stats", "Including statistics (min/median)", false }; const command_line::arg_descriptor<unsigned> arg_loop_multiplier = { "loop-multiplier", "Run for that many times more loops", 1 }; + const command_line::arg_descriptor<std::string> arg_timings_database = { "timings-database", "Keep timings history in a file" }; command_line::add_arg(desc_options, arg_filter); command_line::add_arg(desc_options, arg_verbose); command_line::add_arg(desc_options, arg_stats); command_line::add_arg(desc_options, arg_loop_multiplier); + command_line::add_arg(desc_options, arg_timings_database); po::variables_map vm; bool r = command_line::handle_error_helper(desc_options, [&]() @@ -93,7 +95,10 @@ int main(int argc, char** argv) return 1; const std::string filter = tools::glob_to_regex(command_line::get_arg(vm, arg_filter)); + const std::string timings_database = command_line::get_arg(vm, arg_timings_database); Params p; + if (!timings_database.empty()) + p.td = TimingsDatabase(timings_database); p.verbose = command_line::get_arg(vm, arg_verbose); p.stats = command_line::get_arg(vm, arg_stats); p.loop_multiplier = command_line::get_arg(vm, arg_loop_multiplier); @@ -193,7 +198,10 @@ int main(int argc, char** argv) TEST_PERFORMANCE2(filter, p, test_wallet2_expand_subaddresses, 50, 200); - TEST_PERFORMANCE0(filter, p, test_cn_slow_hash); + TEST_PERFORMANCE1(filter, p, test_cn_slow_hash, 0); + TEST_PERFORMANCE1(filter, p, test_cn_slow_hash, 1); + TEST_PERFORMANCE1(filter, p, test_cn_slow_hash, 2); + TEST_PERFORMANCE1(filter, p, test_cn_slow_hash, 4); TEST_PERFORMANCE1(filter, p, test_cn_fast_hash, 32); TEST_PERFORMANCE1(filter, p, test_cn_fast_hash, 16384); diff --git a/tests/performance_tests/performance_tests.h b/tests/performance_tests/performance_tests.h index d37dda729..17d16b0f6 100644 --- a/tests/performance_tests/performance_tests.h +++ b/tests/performance_tests/performance_tests.h @@ -37,7 +37,9 @@ #include <boost/regex.hpp> #include "misc_language.h" +#include "stats.h" #include "common/perf_timer.h" +#include "common/timings.h" class performance_timer { @@ -67,6 +69,7 @@ private: struct Params { + TimingsDatabase td; bool verbose; bool stats; unsigned loop_multiplier; @@ -85,6 +88,8 @@ public: bool run() { + static_assert(0 < T::loop_count, "T::loop_count must be greater than 0"); + T test; if (!test.init()) return false; @@ -106,11 +111,13 @@ public: m_per_call_timers[i].pause(); } m_elapsed = timer.elapsed_ms(); + m_stats.reset(new Stats<tools::PerformanceTimer, uint64_t>(m_per_call_timers)); return true; } int elapsed_time() const { return m_elapsed; } + size_t get_size() const { return m_stats->get_size(); } int time_per_call(int scale = 1) const { @@ -118,59 +125,19 @@ public: return m_elapsed * scale / (T::loop_count * m_params.loop_multiplier); } - uint64_t per_call_min() const - { - uint64_t v = std::numeric_limits<uint64_t>::max(); - for (const auto &pt: m_per_call_timers) - v = std::min(v, pt.value()); - return v; - } - - uint64_t per_call_max() const - { - uint64_t v = std::numeric_limits<uint64_t>::min(); - for (const auto &pt: m_per_call_timers) - v = std::max(v, pt.value()); - return v; - } - - uint64_t per_call_mean() const - { - uint64_t v = 0; - for (const auto &pt: m_per_call_timers) - v += pt.value(); - return v / m_per_call_timers.size(); - } - - uint64_t per_call_median() const - { - std::vector<uint64_t> values; - values.reserve(m_per_call_timers.size()); - for (const auto &pt: m_per_call_timers) - values.push_back(pt.value()); - return epee::misc_utils::median(values); - } + uint64_t get_min() const { return m_stats->get_min(); } + uint64_t get_max() const { return m_stats->get_max(); } + double get_mean() const { return m_stats->get_mean(); } + uint64_t get_median() const { return m_stats->get_median(); } + double get_stddev() const { return m_stats->get_standard_deviation(); } + double get_non_parametric_skew() const { return m_stats->get_non_parametric_skew(); } + std::vector<uint64_t> get_quantiles(size_t n) const { return m_stats->get_quantiles(n); } - uint64_t per_call_stddev() const + bool is_same_distribution(size_t npoints, double mean, double stddev) const { - if (m_per_call_timers.size() <= 1) - return 0; - const uint64_t mean = per_call_mean(); - uint64_t acc = 0; - for (const auto &pt: m_per_call_timers) - { - int64_t dv = pt.value() - mean; - acc += dv * dv; - } - acc /= m_per_call_timers.size () - 1; - return sqrt(acc); + return m_stats->is_same_distribution_99(npoints, mean, stddev); } - uint64_t min_time_ns() const { return tools::ticks_to_ns(per_call_min()); } - uint64_t max_time_ns() const { return tools::ticks_to_ns(per_call_max()); } - uint64_t median_time_ns() const { return tools::ticks_to_ns(per_call_median()); } - uint64_t standard_deviation_time_ns() const { return tools::ticks_to_ns(per_call_stddev()); } - private: /** * Warm up processor core, enabling turbo boost, etc. @@ -191,10 +158,11 @@ private: int m_elapsed; Params m_params; std::vector<tools::PerformanceTimer> m_per_call_timers; + std::unique_ptr<Stats<tools::PerformanceTimer, uint64_t>> m_stats; }; template <typename T> -void run_test(const std::string &filter, const Params ¶ms, const char* test_name) +void run_test(const std::string &filter, Params ¶ms, const char* test_name) { boost::smatch match; if (!filter.empty() && !boost::regex_match(std::string(test_name), match, boost::regex(filter))) @@ -210,10 +178,10 @@ void run_test(const std::string &filter, const Params ¶ms, const char* test_ std::cout << " elapsed: " << runner.elapsed_time() << " ms\n"; if (params.stats) { - std::cout << " min: " << runner.min_time_ns() << " ns\n"; - std::cout << " max: " << runner.max_time_ns() << " ns\n"; - std::cout << " median: " << runner.median_time_ns() << " ns\n"; - std::cout << " std dev: " << runner.standard_deviation_time_ns() << " ns\n"; + std::cout << " min: " << runner.get_min() << " ns\n"; + std::cout << " max: " << runner.get_max() << " ns\n"; + std::cout << " median: " << runner.get_median() << " ns\n"; + std::cout << " std dev: " << runner.get_stddev() << " ns\n"; } } else @@ -221,24 +189,48 @@ void run_test(const std::string &filter, const Params ¶ms, const char* test_ std::cout << test_name << " (" << T::loop_count * params.loop_multiplier << " calls) - OK:"; } const char *unit = "ms"; - uint64_t scale = 1000000; - int time_per_call = runner.time_per_call(); - if (time_per_call < 30000) { + double scale = 1000000; + uint64_t time_per_call = runner.time_per_call(); + if (time_per_call < 100) { + scale = 1000; time_per_call = runner.time_per_call(1000); #ifdef _WIN32 unit = "\xb5s"; #else unit = "µs"; #endif - scale = 1000; } + const auto quantiles = runner.get_quantiles(10); + double min = runner.get_min(); + double max = runner.get_max(); + double med = runner.get_median(); + double mean = runner.get_mean(); + double stddev = runner.get_stddev(); + double npskew = runner.get_non_parametric_skew(); + + std::vector<TimingsDatabase::instance> prev_instances = params.td.get(test_name); + params.td.add(test_name, {time(NULL), runner.get_size(), min, max, mean, med, stddev, npskew, quantiles}); + std::cout << (params.verbose ? " time per call: " : " ") << time_per_call << " " << unit << "/call" << (params.verbose ? "\n" : ""); if (params.stats) { - uint64_t min_ns = runner.min_time_ns() / scale; - uint64_t med_ns = runner.median_time_ns() / scale; - uint64_t stddev_ns = runner.standard_deviation_time_ns() / scale; - std::cout << " (min " << min_ns << " " << unit << ", median " << med_ns << " " << unit << ", std dev " << stddev_ns << " " << unit << ")"; + uint64_t mins = min / scale; + uint64_t maxs = max / scale; + uint64_t meds = med / scale; + uint64_t p95s = quantiles[9] / scale; + uint64_t stddevs = stddev / scale; + std::string cmp; + if (!prev_instances.empty()) + { + const TimingsDatabase::instance &prev_instance = prev_instances.back(); + if (!runner.is_same_distribution(prev_instance.npoints, prev_instance.mean, prev_instance.stddev)) + { + double pc = fabs(100. * (prev_instance.mean - runner.get_mean()) / prev_instance.mean); + cmp = ", " + std::to_string(pc) + "% " + (mean > prev_instance.mean ? "slower" : "faster"); + } +cmp += " -- " + std::to_string(prev_instance.mean); + } + std::cout << " (min " << mins << " " << unit << ", 90th " << p95s << " " << unit << ", median " << meds << " " << unit << ", std dev " << stddevs << " " << unit << ")" << cmp; } std::cout << std::endl; } diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index cb421c847..aea82ede2 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -55,6 +55,7 @@ set(unit_tests_sources http.cpp keccak.cpp logging.cpp + long_term_block_weight.cpp main.cpp memwipe.cpp mlocker.cpp diff --git a/tests/unit_tests/blockchain_db.cpp b/tests/unit_tests/blockchain_db.cpp index 7e7ce9bf7..9cf8c5fbe 100644 --- a/tests/unit_tests/blockchain_db.cpp +++ b/tests/unit_tests/blockchain_db.cpp @@ -277,10 +277,10 @@ TYPED_TEST(BlockchainDBTest, AddBlock) // TODO: need at least one more block to make this reasonable, as the // BlockchainDB implementation should not check for parent if // no blocks have been added yet (because genesis has no parent). - //ASSERT_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1]), BLOCK_PARENT_DNE); + //ASSERT_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1]), BLOCK_PARENT_DNE); - ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0])); - ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1])); + ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0])); + ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1])); block b; ASSERT_TRUE(this->m_db->block_exists(get_block_hash(this->m_blocks[0]))); @@ -293,7 +293,7 @@ TYPED_TEST(BlockchainDBTest, AddBlock) ASSERT_TRUE(compare_blocks(this->m_blocks[0], b)); // assert that we can't add the same block twice - ASSERT_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0]), TX_EXISTS); + ASSERT_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0]), TX_EXISTS); for (auto& h : this->m_blocks[0].tx_hashes) { @@ -317,14 +317,14 @@ TYPED_TEST(BlockchainDBTest, RetrieveBlockData) this->get_filenames(); this->init_hard_fork(); - ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0])); + ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0])); ASSERT_EQ(t_sizes[0], this->m_db->get_block_weight(0)); ASSERT_EQ(t_diffs[0], this->m_db->get_block_cumulative_difficulty(0)); ASSERT_EQ(t_diffs[0], this->m_db->get_block_difficulty(0)); ASSERT_EQ(t_coins[0], this->m_db->get_block_already_generated_coins(0)); - ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1])); + ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1])); ASSERT_EQ(t_diffs[1] - t_diffs[0], this->m_db->get_block_difficulty(1)); ASSERT_HASH_EQ(get_block_hash(this->m_blocks[0]), this->m_db->get_block_hash_from_height(0)); diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index ec8d1d202..12dfde1bc 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -34,7 +34,7 @@ #include "blockchain_db/blockchain_db.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_basic/hardfork.h" -#include "testdb.h" +#include "blockchain_db/testdb.h" using namespace cryptonote; @@ -44,11 +44,12 @@ using namespace cryptonote; namespace { -class TestDB: public BaseTestDB { +class TestDB: public cryptonote::BaseTestDB { public: virtual uint64_t height() const { return blocks.size(); } virtual void add_block( const block& blk , size_t block_weight + , uint64_t long_term_block_weight , const difficulty_type& cumulative_difficulty , const uint64_t& coins_generated , uint64_t num_rct_outs @@ -106,20 +107,20 @@ TEST(major, Only) ASSERT_FALSE(hf.add(mkblock(0, 2), 0)); ASSERT_FALSE(hf.add(mkblock(2, 2), 0)); ASSERT_TRUE(hf.add(mkblock(1, 2), 0)); - db.add_block(mkblock(1, 1), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(1, 1), 0, 0, 0, 0, 0, crypto::hash()); // block height 1, only version 1 is accepted ASSERT_FALSE(hf.add(mkblock(0, 2), 1)); ASSERT_FALSE(hf.add(mkblock(2, 2), 1)); ASSERT_TRUE(hf.add(mkblock(1, 2), 1)); - db.add_block(mkblock(1, 1), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(1, 1), 0, 0, 0, 0, 0, crypto::hash()); // block height 2, only version 2 is accepted ASSERT_FALSE(hf.add(mkblock(0, 2), 2)); ASSERT_FALSE(hf.add(mkblock(1, 2), 2)); ASSERT_FALSE(hf.add(mkblock(3, 2), 2)); ASSERT_TRUE(hf.add(mkblock(2, 2), 2)); - db.add_block(mkblock(2, 1), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(2, 1), 0, 0, 0, 0, 0, crypto::hash()); } TEST(empty_hardforks, Success) @@ -133,7 +134,7 @@ TEST(empty_hardforks, Success) ASSERT_TRUE(hf.get_state(time(NULL) + 3600*24*400) == HardFork::Ready); for (uint64_t h = 0; h <= 10; ++h) { - db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } ASSERT_EQ(hf.get(0), 1); @@ -167,14 +168,14 @@ TEST(check_for_height, Success) for (uint64_t h = 0; h <= 4; ++h) { ASSERT_TRUE(hf.check_for_height(mkblock(1, 1), h)); ASSERT_FALSE(hf.check_for_height(mkblock(2, 2), h)); // block version is too high - db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } for (uint64_t h = 5; h <= 10; ++h) { ASSERT_FALSE(hf.check_for_height(mkblock(1, 1), h)); // block version is too low ASSERT_TRUE(hf.check_for_height(mkblock(2, 2), h)); - db.add_block(mkblock(hf, h, 2), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 2), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } } @@ -191,19 +192,19 @@ TEST(get, next_version) for (uint64_t h = 0; h <= 4; ++h) { ASSERT_EQ(2, hf.get_next_version()); - db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 1), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } for (uint64_t h = 5; h <= 9; ++h) { ASSERT_EQ(4, hf.get_next_version()); - db.add_block(mkblock(hf, h, 2), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 2), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } for (uint64_t h = 10; h <= 15; ++h) { ASSERT_EQ(4, hf.get_next_version()); - db.add_block(mkblock(hf, h, 4), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 4), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } } @@ -244,7 +245,7 @@ TEST(steps_asap, Success) hf.init(); for (uint64_t h = 0; h < 10; ++h) { - db.add_block(mkblock(hf, h, 9), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, 9), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } @@ -271,7 +272,7 @@ TEST(steps_1, Success) hf.init(); for (uint64_t h = 0 ; h < 10; ++h) { - db.add_block(mkblock(hf, h, h+1), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, h+1), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } @@ -296,7 +297,7 @@ TEST(reorganize, Same) // index 0 1 2 3 4 5 6 7 8 9 static const uint8_t block_versions[] = { 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; for (uint64_t h = 0; h < 20; ++h) { - db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } @@ -327,7 +328,7 @@ TEST(reorganize, Changed) static const uint8_t block_versions[] = { 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; static const uint8_t expected_versions[] = { 1, 1, 1, 1, 1, 1, 4, 4, 7, 7, 9, 9, 9, 9, 9, 9 }; for (uint64_t h = 0; h < 16; ++h) { - db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE (hf.add(db.get_block_from_height(h), h)); } @@ -347,7 +348,7 @@ TEST(reorganize, Changed) ASSERT_EQ(db.height(), 3); hf.reorganize_from_block_height(2); for (uint64_t h = 3; h < 16; ++h) { - db.add_block(mkblock(hf, h, block_versions_new[h]), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, block_versions_new[h]), 0, 0, 0, 0, 0, crypto::hash()); bool ret = hf.add(db.get_block_from_height(h), h); ASSERT_EQ (ret, h < 15); } @@ -371,7 +372,7 @@ TEST(voting, threshold) for (uint64_t h = 0; h <= 8; ++h) { uint8_t v = 1 + !!(h % 8); - db.add_block(mkblock(hf, h, v), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, v), 0, 0, 0, 0, 0, crypto::hash()); bool ret = hf.add(db.get_block_from_height(h), h); if (h >= 8 && threshold == 87) { // for threshold 87, we reach the treshold at height 7, so from height 8, hard fork to version 2, but 8 tries to add 1 @@ -405,7 +406,7 @@ TEST(voting, different_thresholds) static const uint8_t expected_versions[] = { 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4 }; for (uint64_t h = 0; h < sizeof(block_versions) / sizeof(block_versions[0]); ++h) { - db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, 0, crypto::hash()); bool ret = hf.add(db.get_block_from_height(h), h); ASSERT_EQ(ret, true); } @@ -459,7 +460,7 @@ TEST(voting, info) ASSERT_EQ(expected_thresholds[h], threshold); ASSERT_EQ(4, voting); - db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, crypto::hash()); + db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, 0, 0, crypto::hash()); ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); } } @@ -522,7 +523,7 @@ TEST(reorganize, changed) #define ADD(v, h, a) \ do { \ cryptonote::block b = mkblock(hf, h, v); \ - db.add_block(b, 0, 0, 0, 0, crypto::hash()); \ + db.add_block(b, 0, 0, 0, 0, 0, crypto::hash()); \ ASSERT_##a(hf.add(b, h)); \ } while(0) #define ADD_TRUE(v, h) ADD(v, h, TRUE) diff --git a/tests/unit_tests/long_term_block_weight.cpp b/tests/unit_tests/long_term_block_weight.cpp new file mode 100644 index 000000000..0ee6c5c2b --- /dev/null +++ b/tests/unit_tests/long_term_block_weight.cpp @@ -0,0 +1,384 @@ +// 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. + +#define IN_UNIT_TESTS + +#include "gtest/gtest.h" +#include "cryptonote_core/blockchain.h" +#include "cryptonote_core/tx_pool.h" +#include "cryptonote_core/cryptonote_core.h" +#include "blockchain_db/testdb.h" + +#define TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW 5000 + +namespace +{ + +class TestDB: public cryptonote::BaseTestDB +{ +private: + struct block_t + { + size_t weight; + uint64_t long_term_weight; + }; + +public: + TestDB() { m_open = true; } + + virtual void add_block( const cryptonote::block& blk + , size_t block_weight + , uint64_t long_term_block_weight + , const cryptonote::difficulty_type& cumulative_difficulty + , const uint64_t& coins_generated + , uint64_t num_rct_outs + , const crypto::hash& blk_hash + ) override { + blocks.push_back({block_weight, long_term_block_weight}); + } + virtual uint64_t height() const override { return blocks.size(); } + virtual size_t get_block_weight(const uint64_t &h) const override { return blocks[h].weight; } + virtual uint64_t get_block_long_term_weight(const uint64_t &h) const override { return blocks[h].long_term_weight; } + virtual crypto::hash top_block_hash() const override { + uint64_t h = height(); + crypto::hash top = crypto::null_hash; + if (h) + *(uint64_t*)&top = h - 1; + return top; + } + virtual void pop_block(cryptonote::block &blk, std::vector<cryptonote::transaction> &txs) override { blocks.pop_back(); } + +private: + std::vector<block_t> blocks; +}; + +static uint32_t lcg_seed = 0; + +static uint32_t lcg() +{ + lcg_seed = (lcg_seed * 0x100000001b3 + 0xcbf29ce484222325) & 0xffffffff; + return lcg_seed; +} + +} + +#define PREFIX_WINDOW(hf_version,window) \ + std::unique_ptr<cryptonote::Blockchain> bc; \ + cryptonote::tx_memory_pool txpool(*bc); \ + bc.reset(new cryptonote::Blockchain(txpool)); \ + struct get_test_options { \ + const std::pair<uint8_t, uint64_t> hard_forks[3]; \ + const cryptonote::test_options test_options = { \ + hard_forks, \ + window, \ + }; \ + get_test_options(): hard_forks{std::make_pair(1, (uint64_t)0), std::make_pair((uint8_t)hf_version, (uint64_t)1), std::make_pair((uint8_t)0, (uint64_t)0)} {} \ + } opts; \ + cryptonote::Blockchain *blockchain = bc.get(); \ + bool r = blockchain->init(new TestDB(), cryptonote::FAKECHAIN, true, &opts.test_options, 0, NULL); \ + ASSERT_TRUE(r) + +#define PREFIX(hf_version) PREFIX_WINDOW(hf_version, TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW) + +TEST(long_term_block_weight, empty_short) +{ + PREFIX(9); + + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + + ASSERT_EQ(bc->get_current_cumulative_block_weight_median(), CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5); + ASSERT_EQ(bc->get_current_cumulative_block_weight_limit(), CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 * 2); +} + +TEST(long_term_block_weight, identical_before_fork) +{ + PREFIX(9); + + for (uint64_t h = 1; h < 10 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) + { + size_t w = h < CRYPTONOTE_REWARD_BLOCKS_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + for (uint64_t h = 0; h < 10 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) + { + ASSERT_EQ(bc->get_db().get_block_long_term_weight(h), bc->get_db().get_block_weight(h)); + } +} + +TEST(long_term_block_weight, identical_after_fork_before_long_term_window) +{ + PREFIX(10); + + for (uint64_t h = 1; h <= TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) + { + size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + for (uint64_t h = 0; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) + { + ASSERT_EQ(bc->get_db().get_block_long_term_weight(h), bc->get_db().get_block_weight(h)); + } +} + +TEST(long_term_block_weight, ceiling_at_30000000) +{ + PREFIX(10); + + for (uint64_t h = 0; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW + TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW / 2 - 1; ++h) + { + size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + ASSERT_EQ(bc->get_current_cumulative_block_weight_median(), 15000000); + ASSERT_EQ(bc->get_current_cumulative_block_weight_limit(), 30000000); +} + +TEST(long_term_block_weight, multi_pop) +{ + PREFIX(10); + + for (uint64_t h = 1; h <= TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW + 20; ++h) + { + size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + + const uint64_t effective_median = bc->get_current_cumulative_block_weight_median(); + const uint64_t effective_limit = bc->get_current_cumulative_block_weight_limit(); + + for (uint64_t h = 0; h < 4; ++h) + { + size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + + cryptonote::block b; + std::vector<cryptonote::transaction> txs; + bc->get_db().pop_block(b, txs); + bc->get_db().pop_block(b, txs); + bc->get_db().pop_block(b, txs); + bc->get_db().pop_block(b, txs); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + + ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median()); + ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit()); +} + +TEST(long_term_block_weight, multiple_updates) +{ + PREFIX(10); + + for (uint64_t h = 1; h <= 3 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) + { + size_t w = h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + const uint64_t effective_median = bc->get_current_cumulative_block_weight_median(); + const uint64_t effective_limit = bc->get_current_cumulative_block_weight_limit(); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median()); + ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit()); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median()); + ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit()); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median()); + ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit()); + } +} + +TEST(long_term_block_weight, pop_invariant_max) +{ + PREFIX(10); + + for (uint64_t h = 1; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW - 10; ++h) + { + size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + + for (int n = 0; n < 1000; ++n) + { + // pop some blocks, then add some more + int remove = 1 + (n * 17) % 8; + int add = (n * 23) % 12; + + // save long term block weights we're about to remove + uint64_t old_ltbw[16], h0 = bc->get_db().height() - remove - 1; + for (int i = -2; i < remove; ++i) + { + old_ltbw[i + 2] = bc->get_db().get_block_long_term_weight(h0 + i); + } + + for (int i = 0; i < remove; ++i) + { + cryptonote::block b; + std::vector<cryptonote::transaction> txs; + bc->get_db().pop_block(b, txs); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + for (int i = 0; i < add; ++i) + { + size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, bc->get_db().height(), bc->get_db().height(), {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + + // check the new values are the same as the old ones + for (int i = -2; i < std::min(add, remove); ++i) + { + ASSERT_EQ(bc->get_db().get_block_long_term_weight(h0 + i), old_ltbw[i + 2]); + } + } +} + +TEST(long_term_block_weight, pop_invariant_random) +{ + PREFIX(10); + + for (uint64_t h = 1; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW - 10; ++h) + { + size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + } + + for (int n = 0; n < 1000; ++n) + { + // pop some blocks, then add some more + int remove = 1 + (n * 17) % 8; + int add = (n * 23) % 123; + + // save long term block weights we're about to remove + uint64_t old_ltbw[16], h0 = bc->get_db().height() - remove - 1; + for (int i = -2; i < remove; ++i) + { + old_ltbw[i + 2] = bc->get_db().get_block_long_term_weight(h0 + i); + } + + for (int i = 0; i < remove; ++i) + { + cryptonote::block b; + std::vector<cryptonote::transaction> txs; + bc->get_db().pop_block(b, txs); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + const uint64_t effective_median = bc->get_current_cumulative_block_weight_median(); + const uint64_t effective_limit = bc->get_current_cumulative_block_weight_limit(); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median()); + ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit()); + } + for (int i = 0; i < add; ++i) + { + lcg_seed = bc->get_db().height(); + uint32_t r = lcg(); + size_t w = bc->get_db().height() < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : (r % bc->get_current_cumulative_block_weight_limit()); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, bc->get_db().height(), bc->get_db().height(), {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + const uint64_t effective_median = bc->get_current_cumulative_block_weight_median(); + const uint64_t effective_limit = bc->get_current_cumulative_block_weight_limit(); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit()); + ASSERT_EQ(effective_median, bc->get_current_cumulative_block_weight_median()); + ASSERT_EQ(effective_limit, bc->get_current_cumulative_block_weight_limit()); + } + + // check the new values are the same as the old ones + for (int i = -2; i < std::min(add, remove); ++i) + { + ASSERT_EQ(bc->get_db().get_block_long_term_weight(h0 + i), old_ltbw[i + 2]); + } + } +} + +TEST(long_term_block_weight, long_growth_spike_and_drop) +{ + PREFIX(10); + + uint64_t long_term_effective_median_block_weight; + + // constant init + for (uint64_t h = 0; h < TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW; ++h) + { + size_t w = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5; + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit(&long_term_effective_median_block_weight)); + } + ASSERT_EQ(long_term_effective_median_block_weight, 300000); + + // slow 10% yearly for a year (scaled down by 100000 / TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW) -> 8% change + for (uint64_t h = 0; h < 365 * 720 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW / 100000; ++h) + { + //size_t w = bc->get_current_cumulative_block_weight_median() * rate; + float t = h / float(365 * 720 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW / 100000); + size_t w = 300000 + t * 30000; + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit(&long_term_effective_median_block_weight)); + } + ASSERT_GT(long_term_effective_median_block_weight, 300000 * 1.07); + ASSERT_LT(long_term_effective_median_block_weight, 300000 * 1.09); + + // spike over three weeks - does not move much + for (uint64_t h = 0; h < 21 * 720 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW / 100000; ++h) + { + size_t w = bc->get_current_cumulative_block_weight_limit(); + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit(&long_term_effective_median_block_weight)); + } + ASSERT_GT(long_term_effective_median_block_weight, 300000 * 1.07); + ASSERT_LT(long_term_effective_median_block_weight, 300000 * 1.09); + + // drop - does not move much + for (uint64_t h = 0; h < 21 * 720 * TEST_LONG_TERM_BLOCK_WEIGHT_WINDOW / 100000; ++h) + { + size_t w = bc->get_current_cumulative_block_weight_median() * .25; + uint64_t ltw = bc->get_next_long_term_block_weight(w); + bc->get_db().add_block(cryptonote::block(), w, ltw, h, h, {}); + ASSERT_TRUE(bc->update_next_cumulative_weight_limit(&long_term_effective_median_block_weight)); + } + ASSERT_GT(long_term_effective_median_block_weight, 300000 * 1.07); + ASSERT_LT(long_term_effective_median_block_weight, 300000 * 1.09); +} diff --git a/tests/unit_tests/notify.cpp b/tests/unit_tests/notify.cpp index ceeba8649..4de4a8d0a 100644 --- a/tests/unit_tests/notify.cpp +++ b/tests/unit_tests/notify.cpp @@ -82,7 +82,6 @@ TEST(notify, works) ok = true; break; } - std::cout << "got: [" << s << "]" << std::endl; } } boost::filesystem::remove(name_template); diff --git a/tests/unit_tests/output_distribution.cpp b/tests/unit_tests/output_distribution.cpp index 649752ac7..45f2c135b 100644 --- a/tests/unit_tests/output_distribution.cpp +++ b/tests/unit_tests/output_distribution.cpp @@ -33,7 +33,7 @@ #include "cryptonote_core/cryptonote_core.h" #include "cryptonote_core/tx_pool.h" #include "cryptonote_core/blockchain.h" -#include "testdb.h" +#include "blockchain_db/testdb.h" static const uint64_t test_distribution[32] = { 0, 0, 0, 0, 0, 1, 5, 1, 4, 0, 0, 1, 0, 1, 2, 3, 1, 0, 2, 0, 1, 3, 8, 1, 3, 5, 7, 1, 5, 0, 2, 3 @@ -43,7 +43,7 @@ static const size_t test_distribution_size = sizeof(test_distribution) / sizeof( namespace { -class TestDB: public BaseTestDB +class TestDB: public cryptonote::BaseTestDB { public: TestDB(size_t bc_height = test_distribution_size): blockchain_height(bc_height) { m_open = true; } |