diff options
632 files changed, 6905 insertions, 1377 deletions
diff --git a/.gitmodules b/.gitmodules index f8e7c305b..9dacf534f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -15,3 +15,7 @@ [submodule "external/randomx"] path = external/randomx url = https://github.com/tevador/RandomX +[submodule "external/supercop"] + path = external/supercop + url = https://github.com/monero-project/supercop + branch = monero diff --git a/CMakeLists.txt b/CMakeLists.txt index 4fcf782aa..51e497260 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # @@ -31,7 +31,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) list(INSERT CMAKE_MODULE_PATH 0 - "${CMAKE_SOURCE_DIR}/cmake") + "${CMAKE_CURRENT_SOURCE_DIR}/cmake") include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) include(CheckLinkerFlag) @@ -48,7 +48,12 @@ message(STATUS "CMake version ${CMAKE_VERSION}") project(monero) -include(FindCcache) # Has to be included after the project() macro, to be able to read the CXX variable. +option (USE_CCACHE "Use ccache if a usable instance is found" ON) +if (USE_CCACHE) + include(FindCcache) # Has to be included after the project() macro, to be able to read the CXX variable. +else() + message(STATUS "ccache deselected") +endif() enable_language(C ASM) @@ -205,6 +210,7 @@ if(NOT MANUAL_SUBMODULES) check_submodule(external/rapidjson) check_submodule(external/trezor-common) check_submodule(external/randomx) + check_submodule(external/supercop) endif() endif() @@ -306,7 +312,7 @@ endif() # elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*") # set(BSDI TRUE) -include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external) +include_directories(external/rapidjson/include external/easylogging++ src contrib/epee/include external external/supercop/include) if(APPLE) include_directories(SYSTEM /usr/include/malloc) @@ -451,6 +457,9 @@ add_definition_if_function_found(strptime HAVE_STRPTIME) add_definitions(-DAUTO_INITIALIZE_EASYLOGGINGPP) +set(MONERO_GENERATED_HEADERS_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated_include") +include_directories(${MONERO_GENERATED_HEADERS_DIR}) + # Generate header for embedded translations # Generate header for embedded translations, use target toolchain if depends, otherwise use the # lrelease and lupdate binaries from the host @@ -652,7 +661,7 @@ else() endif() # linker - if (NOT SANITIZE AND NOT (WIN32 AND (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_LESS 9.1))) + if (NOT SANITIZE AND NOT OSSFUZZ AND NOT (WIN32 AND (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_LESS 9.1))) # PIE executables randomly crash at startup with ASAN # Windows binaries die on startup with PIE when compiled with GCC <9.x add_linker_flag_if_supported(-pie LD_SECURITY_FLAGS) @@ -982,6 +991,7 @@ if(SODIUM_LIBRARY) set(ZMQ_LIB "${ZMQ_LIB};${SODIUM_LIBRARY}") endif() +include(external/supercop/functions.cmake) # place after setting flags and before src directory inclusion add_subdirectory(contrib) add_subdirectory(src) diff --git a/CMakeLists_IOS.txt b/CMakeLists_IOS.txt index 3b7eacc8a..951286cb0 100644 --- a/CMakeLists_IOS.txt +++ b/CMakeLists_IOS.txt @@ -1,4 +1,4 @@ -# Portions Copyright (c) 2017-2019, The Monero Project +# Portions Copyright (c) 2017-2020, The Monero Project # This file is based off of the https://code.google.com/archive/p/ios-cmake/ # It has been altered for Monero iOS development # @@ -1,4 +1,4 @@ -Copyright (c) 2014-2019, The Monero Project +Copyright (c) 2014-2020, The Monero Project All rights reserved. @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # @@ -1,6 +1,6 @@ # Monero -Copyright (c) 2014-2019 The Monero Project. +Copyright (c) 2014-2020 The Monero Project. Portions Copyright (c) 2012-2013 The Cryptonote developers. ## Table of Contents @@ -90,7 +90,7 @@ As with many development projects, the repository on Github is considered to be Monero is a 100% community-sponsored endeavor. If you want to join our efforts, the easiest thing you can do is support the project financially. Both Monero and Bitcoin donations can be made to **donate.getmonero.org** if using a client that supports the [OpenAlias](https://openalias.org) standard. Alternatively you can send XMR to the Monero donation address via the `donate` command (type `help` in the command-line wallet for details). -The Monero donation address is: `44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A` (viewkey: `f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501`) +The Monero donation address is: `888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H` (viewkey: `f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501`) The Bitcoin donation address is: `1KTexdemPdxSBcG55heUuTjDRYqbC5ZL8H` @@ -132,7 +132,7 @@ Dates are provided in the format YYYY-MM-DD. | 1686275 | 2018-10-19 | v9 | v0.13.0.0 | v0.13.0.4 | bulletproofs required | 1788000 | 2019-03-09 | v10 | v0.14.0.0 | v0.14.1.2 | New PoW based on Cryptonight-R, new block weight algorithm, slightly more efficient RingCT format | 1788720 | 2019-03-10 | v11 | v0.14.0.0 | v0.14.1.2 | forbid old RingCT transaction format -| 1978433 | 2019-11-30* | v12 | v0.15.0.0 | v0.15.0.0 | New PoW based on RandomX, only allow >= 2 outputs, change to the block median used to calculate penalty, v1 coinbases are forbidden, rct sigs in coinbase forbidden, 10 block lock time for incoming outputs +| 1978433 | 2019-11-30* | v12 | v0.15.0.0 | v0.16.0.0 | New PoW based on RandomX, only allow >= 2 outputs, change to the block median used to calculate penalty, v1 coinbases are forbidden, rct sigs in coinbase forbidden, 10 block lock time for incoming outputs | XXXXXXX | XXX-XX-XX | XXX | vX.XX.X.X | vX.XX.X.X | XXX | X's indicate that these details have not been determined as of commit date. @@ -178,10 +178,10 @@ library archives (`.a`). | Graphviz | any | NO | `graphviz` | `graphviz` | `graphviz` | `graphviz` | YES | Documentation | | lrelease | ? | NO | `qttools5-dev-tools` | `qt5-tools` | `qt5-tools` | `qt5-linguist` | YES | Translations | | libhidapi | ? | NO | `libhidapi-dev` | `hidapi` | `hidapi-devel` | `hidapi-devel` | YES | Hardware wallet | -| libusb | ? | NO | `libusb-dev` | `libusb` | `libusb-devel` | `libusb-devel` | YES | Hardware wallet | +| libusb | ? | NO | `libusb-1.0-0-dev` | `libusb` | `libusb-devel` | `libusbx-devel` | YES | Hardware wallet | | libprotobuf | ? | NO | `libprotobuf-dev` | `protobuf` | `protobuf-devel` | `protobuf-devel` | YES | Hardware wallet | | protoc | ? | NO | `protobuf-compiler` | `protobuf` | `protobuf` | `protobuf-compiler` | YES | Hardware wallet | - +| libudev | ? | No | `libudev-dev` | `systemd` | `eudev-libudev-devel` | `systemd-devel` | YES | Hardware wallet | [1] On Debian/Ubuntu `libgtest-dev` only includes sources and headers. You must build the library binary manually. This can be done with the following command ```sudo apt-get install libgtest-dev && cd /usr/src/gtest && sudo cmake . && sudo make && sudo mv libg* /usr/lib/ ``` @@ -189,13 +189,13 @@ build the library binary manually. This can be done with the following command ` Install all dependencies at once on Debian/Ubuntu: -``` 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 qttools5-dev-tools libhidapi-dev libusb-dev libprotobuf-dev protobuf-compiler ``` +``` 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 qttools5-dev-tools libhidapi-dev libusb-1.0-0-dev libprotobuf-dev protobuf-compiler libudev-dev``` Install all dependencies at once on macOS with the provided Brewfile: ``` brew update && brew bundle --file=contrib/brew/Brewfile ``` -FreeBSD one liner for required to build dependencies -```pkg install git gmake cmake pkgconf boost-libs libzmq libsodium``` +FreeBSD 12.1 one-liner required to build dependencies: +```pkg install git gmake cmake pkgconf boost-libs libzmq4 libsodium``` ### Cloning the repository @@ -219,7 +219,7 @@ invokes cmake commands as needed. ```bash cd monero - git checkout release-v0.15 + git checkout release-v0.16 make ``` @@ -292,7 +292,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch ( ```bash git clone https://github.com/monero-project/monero.git cd monero - git checkout tags/v0.15.0.0 + git checkout tags/v0.16.0.0 ``` * Build: @@ -409,10 +409,10 @@ application. cd monero ``` -* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.15.0.0'. If you don't care about the version and just want binaries from master, skip this step: +* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.16.0.0'. If you don't care about the version and just want binaries from master, skip this step: ```bash - git checkout v0.15.0.0 + git checkout v0.16.0.0 ``` * If you are on a 64-bit system, run: @@ -0,0 +1,61 @@ +# The Current/Future Status of ZMQ in Monero + +## ZMQ Pub/Sub +Client `ZMQ_SUB` sockets must "subscribe" to topics before it receives any data. +This allows filtering on the server side, so network traffic is reduced. Monero +allows for filtering on: (1) format, (2) context, and (3) event. + + * **format** refers to the _wire_ format (i.e. JSON) used to send event + information. + * **context** allows for a reduction in fields for the event, so the + daemon doesn't waste cycles serializing fields that get ignored. + * **event** refers to status changes occurring within the daemon (i.e. new + block to main chain). + + * Formats: + * `json` + * Contexts: + * `full` - the entire block or transaction is transmitted (the hash can be + computed remotely). + * `minimal` - the bare minimum for a remote client to react to an event is + sent. + * Events: + * `chain_main` - changes to the primary/main blockchain. + * `txpool_add` - new _publicly visible_ transactions in the mempool. + Includes previously unseen transactions in a block but _not_ the + `miner_tx`. Does not "re-publish" after a reorg. Includes `do_not_relay` + transactions. + +The subscription topics are formatted as `format-context-event`, with prefix +matching supported by both Monero and ZMQ. The `format`, `context` and `event` +will _never_ have hyphens or colons in their name. For example, subscribing to +`json-minimal-chain_main` will send minimal information in JSON when changes +to the main/primary blockchain occur. Whereas, subscribing to `json-minimal` +will send minimal information in JSON on all available events supported by the +daemon. + +The Monero daemon will ensure that events prefixed by `chain` will be sent in +"chain-order" - the `prev_id` (hash) field will _always_ refer to a previous +block. On rollbacks/reorgs, the event will reference an earlier block in the +chain instead of the last block. The Monero daemon also ensures that +`txpool_add` events are sent before `chain_*` events - the `chain_*` messages +will only serialize miner transactions since the other transactions were +previously published via `txpool_add`. This prevents transactions from being +serialized twice, even when the transaction was first observed in a block. + +ZMQ Pub/Sub will drop messages if the network is congested, so the above rules +for send order are used for detecting lost messages. A missing gap in `height` +or `prev_id` for `chain_*` events indicates a lost pub message. Missing +`txpool_add` messages can only be detected at the next `chain_` message. + +Since blockchain events can be dropped, clients will likely want to have a +timeout against `chain_main` events. The `GetLastBlockHeader` RPC is useful +for checking the current chain state. Dropped messages should be rare in most +conditions. + +The Monero daemon will send a `txpool_add` pub exactly once for each +transaction, even after a reorg or restarts. Clients should use the +`GetTransactionPool` after a reorg to get all transactions that have been put +back into the tx pool or been invalidated due to a double-spend. + + diff --git a/cmake/32-bit-toolchain.cmake b/cmake/32-bit-toolchain.cmake index 2d53adf06..6bee56482 100644 --- a/cmake/32-bit-toolchain.cmake +++ b/cmake/32-bit-toolchain.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/cmake/64-bit-toolchain.cmake b/cmake/64-bit-toolchain.cmake index 7c56eef95..5eaa17596 100644 --- a/cmake/64-bit-toolchain.cmake +++ b/cmake/64-bit-toolchain.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/cmake/FindCcache.cmake b/cmake/FindCcache.cmake index fa357610b..a0734a312 100644 --- a/cmake/FindCcache.cmake +++ b/cmake/FindCcache.cmake @@ -41,17 +41,20 @@ find_program(CCACHE_FOUND ccache) if (CCACHE_FOUND) - set(TEMP_CPP_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test-program.cpp") + # Try to compile a test program with ccache, in order to verify if it really works. (needed on exotic setups) + # Create a temporary file with a simple program. + set(TEMP_CPP_FILE "${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/CMakeTmp/test-program.cpp") file(WRITE "${TEMP_CPP_FILE}" "int main() { return 0; }") + # And run the found ccache on it. execute_process(COMMAND "${CCACHE_FOUND}" "${CMAKE_CXX_COMPILER}" "${TEMP_CPP_FILE}" RESULT_VARIABLE RET) if (${RET} EQUAL 0) - message("found usable ccache: ${CCACHE_FOUND}") + # Success + message(STATUS "Found usable ccache: ${CCACHE_FOUND}") set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_FOUND}") set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCACHE_FOUND}") else() - message("found ccache ${CCACHE_FOUND}, but is UNUSABLE! Return code: ${RET}") - endif() + message(STATUS "Found ccache ${CCACHE_FOUND}, but is UNUSABLE! Return code: ${RET}") + endif() else() - message("ccache NOT found!") + message(STATUS "ccache NOT found! Please install it for faster rebuilds.") endif() - diff --git a/cmake/FindUnbound.cmake b/cmake/FindUnbound.cmake index e3eb3d67e..65c6725c6 100644 --- a/cmake/FindUnbound.cmake +++ b/cmake/FindUnbound.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, are diff --git a/cmake/GitVersion.cmake b/cmake/GitVersion.cmake index 7eaf3ddf1..3263266c7 100644 --- a/cmake/GitVersion.cmake +++ b/cmake/GitVersion.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/cmake/Version.cmake b/cmake/Version.cmake index 26c2e2a1d..a6c13ab6c 100644 --- a/cmake/Version.cmake +++ b/cmake/Version.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/cmake/test-libusb-version.c b/cmake/test-libusb-version.c index b80191b58..bd8f90c03 100644 --- a/cmake/test-libusb-version.c +++ b/cmake/test-libusb-version.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/cmake/test-protobuf.cpp b/cmake/test-protobuf.cpp index 0a1ca82be..4b2e19725 100644 --- a/cmake/test-protobuf.cpp +++ b/cmake/test-protobuf.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/cmake/test-static-assert.c b/cmake/test-static-assert.c index 2e6c84839..520939c6f 100644 --- a/cmake/test-static-assert.c +++ b/cmake/test-static-assert.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/cmake/test-static-assert.cpp b/cmake/test-static-assert.cpp index 2e6c84839..520939c6f 100644 --- a/cmake/test-static-assert.cpp +++ b/cmake/test-static-assert.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index 0db645ce9..511f0416e 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/contrib/codefresh/codefresh.yml b/contrib/codefresh/codefresh.yml deleted file mode 100644 index a22debfa1..000000000 --- a/contrib/codefresh/codefresh.yml +++ /dev/null @@ -1,18 +0,0 @@ -version: '1.0' -steps: - init_submodules: - title: Init Submodules - commands: - - git submodule update --init --recursive - image: codefreshio/git-image:latest - working_directory: ${{main_clone}} - - BuildingDockerImage: - title: Building Docker Image - type: build - image_name: monero - working_directory: ./ - tag: '${{CF_BRANCH_TAG_NORMALIZED}}' - dockerfile: Dockerfile - build_arguments: - - NPROC=1 diff --git a/contrib/depends/packages/expat.mk b/contrib/depends/packages/expat.mk index ef81636a2..d73a5e307 100644 --- a/contrib/depends/packages/expat.mk +++ b/contrib/depends/packages/expat.mk @@ -1,6 +1,6 @@ package=expat $(package)_version=2.2.4 -$(package)_download_path=https://downloads.sourceforge.net/project/expat/expat/$($(package)_version) +$(package)_download_path=https://github.com/libexpat/libexpat/releases/download/R_2_2_4 $(package)_file_name=$(package)-$($(package)_version).tar.bz2 $(package)_sha256_hash=03ad85db965f8ab2d27328abcf0bc5571af6ec0a414874b2066ee3fdd372019e diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in index 2634423ab..422d9dede 100644 --- a/contrib/depends/toolchain.cmake.in +++ b/contrib/depends/toolchain.cmake.in @@ -1,5 +1,6 @@ # Set the system name to one of Android, Darwin, FreeBSD, Linux, or Windows SET(CMAKE_SYSTEM_NAME @depends@) +SET(CMAKE_SYSTEM_PROCESSOR @arch@) SET(CMAKE_BUILD_TYPE @release_type@) OPTION(STATIC "Link libraries statically" ON) @@ -63,14 +64,14 @@ set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # Find programs on host set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) # Find libs in target set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # Find includes in target -set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_HOST_SYSTEM_PROCESSOR} CACHE STRING "" FORCE) - # specify the cross compiler to be used. Darwin uses clang provided by the SDK. if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") SET(CMAKE_C_COMPILER @prefix@/native/bin/clang) SET(CMAKE_C_COMPILER_TARGET x86_64-apple-darwin11) SET(CMAKE_CXX_COMPILER @prefix@/native/bin/clang++ -stdlib=libc++) SET(CMAKE_CXX_COMPILER_TARGET x86_64-apple-darwin11) + SET(CMAKE_ASM_COMPILER_TARGET x86_64-apple-darwin11) + SET(CMAKE_ASM-ATT_COMPILER_TARGET x86_64-apple-darwin11) SET(_CMAKE_TOOLCHAIN_PREFIX x86_64-apple-darwin11-) SET(APPLE True) SET(BUILD_TAG "mac-x64") diff --git a/contrib/epee/CMakeLists.txt b/contrib/epee/CMakeLists.txt index a2c636304..6e303eb0b 100644 --- a/contrib/epee/CMakeLists.txt +++ b/contrib/epee/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/contrib/epee/include/console_handler.h b/contrib/epee/include/console_handler.h index 08d9b8802..219b593b0 100644 --- a/contrib/epee/include/console_handler.h +++ b/contrib/epee/include/console_handler.h @@ -543,6 +543,31 @@ eof: return it->second.second; } + std::vector<std::string> get_command_list(const std::vector<std::string>& keywords = std::vector<std::string>()) + { + std::vector<std::string> list; + list.reserve(m_command_handlers.size()); + for(auto const& x:m_command_handlers) + { + bool take = true; + for(auto const& y:keywords) + { + bool in_usage = x.second.second.first.find(y) != std::string::npos; + bool in_description = x.second.second.second.find(y) != std::string::npos; + if (!(in_usage || in_description)) + { + take = false; + break; + } + } + if (take) + { + list.push_back(x.first); + } + } + return list; + } + void set_handler(const std::string& cmd, const callback& hndlr, const std::string& usage = "", const std::string& description = "") { lookup::mapped_type & vt = m_command_handlers[cmd]; diff --git a/contrib/epee/include/int-util.h b/contrib/epee/include/int-util.h index dcd58a8c4..bc422d841 100644 --- a/contrib/epee/include/int-util.h +++ b/contrib/epee/include/int-util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/include/md5_l.inl b/contrib/epee/include/md5_l.inl index 8e339e006..cb2bd54f9 100644 --- a/contrib/epee/include/md5_l.inl +++ b/contrib/epee/include/md5_l.inl @@ -277,7 +277,7 @@ namespace md5 /* Zeroize sensitive information. */ - MD5_memset ((POINTER)context, 0, sizeof (*context)); + memwipe ((POINTER)context, sizeof (*context)); } /* MD5 basic transformation. Transforms state based on block. @@ -369,7 +369,7 @@ namespace md5 /* Zeroize sensitive information. */ - MD5_memset ((POINTER)x, 0, sizeof (x)); + memwipe ((POINTER)x, sizeof (x)); } /* Note: Replace "for loop" with standard memcpy if possible. @@ -431,9 +431,9 @@ namespace md5 MD5Update(&hmac->octx, k_opad, 64); /* apply outer pad */ /* scrub the pads and key context (if used) */ - MD5_memset( (POINTER)&k_ipad, 0, sizeof(k_ipad)); - MD5_memset( (POINTER)&k_opad, 0, sizeof(k_opad)); - MD5_memset( (POINTER)&tk, 0, sizeof(tk)); + memwipe( (POINTER)&k_ipad, sizeof(k_ipad)); + memwipe( (POINTER)&k_opad, sizeof(k_opad)); + memwipe( (POINTER)&tk, sizeof(tk)); /* and we're done. */ } @@ -459,7 +459,7 @@ namespace md5 state->istate[lupe] = htonl(hmac.ictx.state[lupe]); state->ostate[lupe] = htonl(hmac.octx.state[lupe]); } - MD5_memset( (POINTER)&hmac, 0, sizeof(hmac)); + memwipe( (POINTER)&hmac, sizeof(hmac)); } diff --git a/contrib/epee/include/memwipe.h b/contrib/epee/include/memwipe.h index d586608cb..c6e8f072c 100644 --- a/contrib/epee/include/memwipe.h +++ b/contrib/epee/include/memwipe.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h index 3a2c5341d..3c31cf22b 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.h +++ b/contrib/epee/include/net/abstract_tcp_server2.h @@ -48,12 +48,9 @@ #include <boost/asio.hpp> #include <boost/asio/ssl.hpp> #include <boost/array.hpp> -#include <boost/noncopyable.hpp> -#include <boost/shared_ptr.hpp> //! \TODO Convert to std::shared_ptr #include <boost/enable_shared_from_this.hpp> #include <boost/interprocess/detail/atomic.hpp> #include <boost/thread/thread.hpp> -#include <memory> #include "byte_slice.h" #include "net_utils_base.h" #include "syncobj.h" diff --git a/contrib/epee/include/net/connection_basic.hpp b/contrib/epee/include/net/connection_basic.hpp index 2f60f7604..90303a785 100644 --- a/contrib/epee/include/net/connection_basic.hpp +++ b/contrib/epee/include/net/connection_basic.hpp @@ -8,7 +8,7 @@ // ! (how ever if in some wonderful juristdictions that is not the case, then why not make another sub-class withat that members and licence it as epee part) // ! Working on above premise, IF this is valid in your juristdictions, then consider this code as released as: -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/include/net/http_auth.h b/contrib/epee/include/net/http_auth.h index 00b9750b7..b7105bf8a 100644 --- a/contrib/epee/include/net/http_auth.h +++ b/contrib/epee/include/net/http_auth.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/include/net/net_fwd.h b/contrib/epee/include/net/net_fwd.h index ba4fe6259..f5b6f8db4 100644 --- a/contrib/epee/include/net/net_fwd.h +++ b/contrib/epee/include/net/net_fwd.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/include/net/network_throttle-detail.hpp b/contrib/epee/include/net/network_throttle-detail.hpp index 353ae0c0c..0bab4fe9e 100644 --- a/contrib/epee/include/net/network_throttle-detail.hpp +++ b/contrib/epee/include/net/network_throttle-detail.hpp @@ -2,7 +2,7 @@ /// @author rfree (current maintainer in monero.cc project) /// @brief implementaion for throttling of connection (count and rate-limit speed etc) -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/include/net/network_throttle.hpp b/contrib/epee/include/net/network_throttle.hpp index 02a286326..b6f7592c2 100644 --- a/contrib/epee/include/net/network_throttle.hpp +++ b/contrib/epee/include/net/network_throttle.hpp @@ -2,7 +2,7 @@ /// @author rfree (current maintainer in monero.cc project) /// @brief interface for throttling of connection (count and rate-limit speed etc) -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -44,10 +44,7 @@ #include <boost/shared_ptr.hpp> #include <atomic> -#include <boost/asio.hpp> #include <boost/array.hpp> -#include <boost/noncopyable.hpp> -#include <boost/shared_ptr.hpp> #include <boost/enable_shared_from_this.hpp> #include <boost/interprocess/detail/atomic.hpp> #include <boost/thread/thread.hpp> @@ -63,7 +60,6 @@ #include <boost/utility/value_init.hpp> #include <boost/asio/deadline_timer.hpp> #include <boost/date_time/posix_time/posix_time.hpp> -#include <boost/thread/thread.hpp> #include "misc_language.h" #include "pragma_comp_defs.h" #include <sstream> diff --git a/contrib/epee/include/rolling_median.h b/contrib/epee/include/rolling_median.h index 8b5a82a84..11275aa70 100644 --- a/contrib/epee/include/rolling_median.h +++ b/contrib/epee/include/rolling_median.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/include/span.h b/contrib/epee/include/span.h index 59895535f..b355c960a 100644 --- a/contrib/epee/include/span.h +++ b/contrib/epee/include/span.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/include/storages/parserse_base_utils.h b/contrib/epee/include/storages/parserse_base_utils.h index 2256f6b83..5a6cc0b51 100644 --- a/contrib/epee/include/storages/parserse_base_utils.h +++ b/contrib/epee/include/storages/parserse_base_utils.h @@ -196,7 +196,7 @@ namespace misc_utils uint32_t dst = 0; for (int i = 0; i < 4; ++i) { - const unsigned char tmp = isx[(int)*++it]; + const unsigned char tmp = isx[(unsigned char)*++it]; CHECK_AND_ASSERT_THROW_MES(tmp != 0xff, "Bad Unicode encoding"); dst = dst << 4 | tmp; } diff --git a/contrib/epee/include/storages/portable_storage_bin_utils.h b/contrib/epee/include/storages/portable_storage_bin_utils.h index bcde64487..b2b4bc56c 100644 --- a/contrib/epee/include/storages/portable_storage_bin_utils.h +++ b/contrib/epee/include/storages/portable_storage_bin_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/include/wipeable_string.h b/contrib/epee/include/wipeable_string.h index f0e526b92..22c30855f 100644 --- a/contrib/epee/include/wipeable_string.h +++ b/contrib/epee/include/wipeable_string.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt index ba6ad73ae..8adf69162 100644 --- a/contrib/epee/src/CMakeLists.txt +++ b/contrib/epee/src/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/contrib/epee/src/byte_slice.cpp b/contrib/epee/src/byte_slice.cpp index 99c37fae3..faf7689be 100644 --- a/contrib/epee/src/byte_slice.cpp +++ b/contrib/epee/src/byte_slice.cpp @@ -133,10 +133,13 @@ namespace epee template<typename T> byte_slice::byte_slice(const adapt_buffer, T&& buffer) - : storage_(nullptr), portion_(to_byte_span(to_span(buffer))) + : storage_(nullptr), portion_(nullptr) { if (!buffer.empty()) + { storage_ = allocate_slice<adapted_byte_slice<T>>(0, std::move(buffer)); + portion_ = to_byte_span(to_span(static_cast<adapted_byte_slice<T> *>(storage_.get())->buffer)); + } } byte_slice::byte_slice(std::initializer_list<span<const std::uint8_t>> sources) @@ -173,9 +176,14 @@ namespace epee byte_slice::byte_slice(byte_stream&& stream) noexcept : storage_(nullptr), portion_(stream.data(), stream.size()) { - std::uint8_t* const data = stream.take_buffer().release() - sizeof(raw_byte_slice); - new (data) raw_byte_slice{}; - storage_.reset(reinterpret_cast<raw_byte_slice*>(data)); + if (stream.size()) + { + std::uint8_t* const data = stream.take_buffer().release() - sizeof(raw_byte_slice); + new (data) raw_byte_slice{}; + storage_.reset(reinterpret_cast<raw_byte_slice*>(data)); + } + else + portion_ = nullptr; } byte_slice::byte_slice(byte_slice&& source) noexcept @@ -205,14 +213,17 @@ namespace epee byte_slice byte_slice::take_slice(const std::size_t max_bytes) noexcept { byte_slice out{}; - std::uint8_t const* const ptr = data(); - out.portion_ = {ptr, portion_.remove_prefix(max_bytes)}; - if (portion_.empty()) - out.storage_ = std::move(storage_); // no atomic inc/dec - else - out = {storage_.get(), out.portion_}; + if (max_bytes) + { + std::uint8_t const* const ptr = data(); + out.portion_ = {ptr, portion_.remove_prefix(max_bytes)}; + if (portion_.empty()) + out.storage_ = std::move(storage_); // no atomic inc/dec + else + out = {storage_.get(), out.portion_}; + } return out; } diff --git a/contrib/epee/src/connection_basic.cpp b/contrib/epee/src/connection_basic.cpp index 3ce7a1057..30efbf2fc 100644 --- a/contrib/epee/src/connection_basic.cpp +++ b/contrib/epee/src/connection_basic.cpp @@ -2,7 +2,7 @@ /// @author rfree (current maintainer in monero.cc project) /// @brief base for connection, contains e.g. the ratelimit hooks -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/src/http_auth.cpp b/contrib/epee/src/http_auth.cpp index 5f4907cc2..18ee18bd4 100644 --- a/contrib/epee/src/http_auth.cpp +++ b/contrib/epee/src/http_auth.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/src/int-util.cpp b/contrib/epee/src/int-util.cpp index e9d0822e0..061c90b4e 100644 --- a/contrib/epee/src/int-util.cpp +++ b/contrib/epee/src/int-util.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/src/levin_base.cpp b/contrib/epee/src/levin_base.cpp index ff845e2a7..5ec86b3d6 100644 --- a/contrib/epee/src/levin_base.cpp +++ b/contrib/epee/src/levin_base.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/src/memwipe.c b/contrib/epee/src/memwipe.c index ad1ef510d..41a6b36ac 100644 --- a/contrib/epee/src/memwipe.c +++ b/contrib/epee/src/memwipe.c @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp index e96bf627f..bcde215be 100644 --- a/contrib/epee/src/mlog.cpp +++ b/contrib/epee/src/mlog.cpp @@ -100,7 +100,7 @@ static const char *get_default_categories(int level) switch (level) { case 0: - categories = "*:WARNING,net:FATAL,net.http:FATAL,net.ssl:FATAL,net.p2p:FATAL,net.cn:FATAL,global:INFO,verify:FATAL,serialization:FATAL,daemon.rpc.payment:ERROR,stacktrace:INFO,logging:INFO,msgwriter:INFO"; + categories = "*:WARNING,net:FATAL,net.http:FATAL,net.ssl:FATAL,net.p2p:FATAL,net.cn:FATAL,daemon.rpc:FATAL,global:INFO,verify:FATAL,serialization:FATAL,daemon.rpc.payment:ERROR,stacktrace:INFO,logging:INFO,msgwriter:INFO"; break; case 1: categories = "*:INFO,global:INFO,stacktrace:INFO,logging:INFO,msgwriter:INFO,perf.*:DEBUG"; diff --git a/contrib/epee/src/net_ssl.cpp b/contrib/epee/src/net_ssl.cpp index 2cf9ea2b6..a09e82771 100644 --- a/contrib/epee/src/net_ssl.cpp +++ b/contrib/epee/src/net_ssl.cpp @@ -511,7 +511,7 @@ bool ssl_options_t::handshake( // autodetect will reconnect without SSL - warn and keep connection encrypted if (support != ssl_support_t::e_ssl_support_autodetect) { - MERROR("SSL certificate is not in the allowed list, connection droppped"); + MERROR("SSL certificate is not in the allowed list, connection dropped"); return false; } MWARNING("SSL peer has not been verified"); diff --git a/contrib/epee/src/network_throttle-detail.cpp b/contrib/epee/src/network_throttle-detail.cpp index 72544cbf6..f4ebeabf0 100644 --- a/contrib/epee/src/network_throttle-detail.cpp +++ b/contrib/epee/src/network_throttle-detail.cpp @@ -2,7 +2,7 @@ /// @author rfree (current maintainer in monero.cc project) /// @brief implementaion for throttling of connection (count and rate-limit speed etc) -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/src/network_throttle.cpp b/contrib/epee/src/network_throttle.cpp index f4f0b2c46..b2c861e30 100644 --- a/contrib/epee/src/network_throttle.cpp +++ b/contrib/epee/src/network_throttle.cpp @@ -26,7 +26,7 @@ Throttling work by: */ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/src/wipeable_string.cpp b/contrib/epee/src/wipeable_string.cpp index 4928db172..21312d49d 100644 --- a/contrib/epee/src/wipeable_string.cpp +++ b/contrib/epee/src/wipeable_string.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/contrib/gitian/README.md b/contrib/gitian/README.md index c09786c36..c0f230887 100644 --- a/contrib/gitian/README.md +++ b/contrib/gitian/README.md @@ -126,7 +126,7 @@ Setup for LXC: ```bash GH_USER=fluffypony -VERSION=v0.15.0.0 +VERSION=v0.16.0.0 ./gitian-build.py --setup $GH_USER $VERSION ``` @@ -182,7 +182,7 @@ If you chose to do detached signing using `--detach-sign` above (recommended), y ```bash GH_USER=fluffypony -VERSION=v0.15.0.0 +VERSION=v0.16.0.0 gpg --detach-sign ${VERSION}-linux/${GH_USER}/monero-linux-*-build.assert gpg --detach-sign ${VERSION}-win/${GH_USER}/monero-win-*-build.assert diff --git a/contrib/gitian/gitian-android.yml b/contrib/gitian/gitian-android.yml index 02614b1a5..de98efafe 100644 --- a/contrib/gitian/gitian-android.yml +++ b/contrib/gitian/gitian-android.yml @@ -1,5 +1,5 @@ --- -name: "monero-android-0.15" +name: "monero-android-0.16" enable_cache: true suites: - "bionic" diff --git a/contrib/gitian/gitian-freebsd.yml b/contrib/gitian/gitian-freebsd.yml index 0220b82a5..e97c3802b 100644 --- a/contrib/gitian/gitian-freebsd.yml +++ b/contrib/gitian/gitian-freebsd.yml @@ -1,5 +1,5 @@ --- -name: "monero-freebsd-0.15" +name: "monero-freebsd-0.16" enable_cache: true suites: - "bionic" diff --git a/contrib/gitian/gitian-linux.yml b/contrib/gitian/gitian-linux.yml index 9c2ebac9b..bd42637aa 100644 --- a/contrib/gitian/gitian-linux.yml +++ b/contrib/gitian/gitian-linux.yml @@ -1,5 +1,5 @@ --- -name: "monero-linux-0.15" +name: "monero-linux-0.16" enable_cache: true suites: - "bionic" diff --git a/contrib/gitian/gitian-osx.yml b/contrib/gitian/gitian-osx.yml index ecc7d4b59..4d44c4845 100644 --- a/contrib/gitian/gitian-osx.yml +++ b/contrib/gitian/gitian-osx.yml @@ -1,5 +1,5 @@ --- -name: "monero-osx-0.15" +name: "monero-osx-0.16" enable_cache: true suites: - "bionic" diff --git a/contrib/gitian/gitian-win.yml b/contrib/gitian/gitian-win.yml index bd85695c1..196b6ebbe 100644 --- a/contrib/gitian/gitian-win.yml +++ b/contrib/gitian/gitian-win.yml @@ -1,5 +1,5 @@ --- -name: "monero-win-0.15" +name: "monero-win-0.16" enable_cache: true suites: - "bionic" diff --git a/contrib/snap/monerod-wrapper b/contrib/snap/monerod-wrapper deleted file mode 100755 index f7266e11c..000000000 --- a/contrib/snap/monerod-wrapper +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -if [ ! -d "$SNAP_USER_DATA/etc" ]; then - mkdir $SNAP_USER_DATA/etc/ - cp -R $SNAP/etc/monerod.conf $SNAP_USER_DATA/etc/monerod.conf -fi - -exec "$SNAP/bin/monerod" "$@" diff --git a/contrib/snap/monerod.conf b/contrib/snap/monerod.conf deleted file mode 100644 index 1078e73b2..000000000 --- a/contrib/snap/monerod.conf +++ /dev/null @@ -1,10 +0,0 @@ -# Configuration for monerod -# Syntax: any command line option may be specified as 'clioptionname=value'. -# Boolean options such as 'no-igd' are specified as 'no-igd=1'. -# See 'monerod --help' for all available options. - -# Overridden by snap: -# data-dir=/var/lib/monero -# log-file=/var/log/monero/monero.log - -log-level=0 diff --git a/contrib/snap/setup/gui/icon.png b/contrib/snap/setup/gui/icon.png Binary files differdeleted file mode 100644 index b7e821270..000000000 --- a/contrib/snap/setup/gui/icon.png +++ /dev/null diff --git a/contrib/snap/snapcraft.yaml b/contrib/snap/snapcraft.yaml deleted file mode 100644 index b3b75d278..000000000 --- a/contrib/snap/snapcraft.yaml +++ /dev/null @@ -1,78 +0,0 @@ -name: monero -version: 0.11.1.0-1 -summary: "Monero: the secure, private, untraceable cryptocurrency https://getmonero.org" -description: | - Monero is a private, secure, untraceable, decentralised digital currency. - You are your bank, you control your funds, and nobody can trace your transfers - unless you allow them to do so. -grade: devel -confinement: strict - -apps: - monerod: - daemon: forking - command: | - monerod-wrapper --detach --data-dir ${SNAP_COMMON} --config-file ${SNAP_USER_DATA}/etc/monerod.conf - plugs: - - network - - network-bind - monero-wallet-rpc: - command: | - monero-wallet-rpc --log-file ${SNAP_USER_DATA} - plugs: - - home - - network - - network-bind - monero-wallet-cli: - command: | - monero-wallet-cli --log-file ${SNAP_USER_DATA} - plugs: - - home - - network - -parts: - cmake-build: - plugin: cmake - configflags: - - -DBDB_STATIC=1 - - -DBoost_USE_STATIC_LIBS=1 - - -DBoost_USE_STATIC_RUNTIME=1 - - -DARCH=default - source: . - build-packages: - - gcc - - pkg-config - - libunbound-dev - - libevent-dev - - libboost-all-dev - - libzmqpp-dev - - libzmq3-dev - - libsodium-dev - - libdb-dev - - libunwind-dev - - libminiupnpc-dev - - libldns-dev - - libexpat1-dev - - libreadline6-dev - - bison - - doxygen - - graphviz - stage-packages: - - libminiupnpc10 - - libunbound2 - - libunwind8 - prime: - - bin - - usr/lib/ - - -usr/lib/gcc - - -usr/share - - dist-files: - plugin: dump - source: . - organize: - contrib/snap/monerod.conf: etc/monerod.conf - contrib/snap/monerod-wrapper: bin/monerod-wrapper - prime: - - etc - - bin diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 71b165f4f..a8916a7d0 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -80,4 +80,5 @@ endif() add_subdirectory(db_drivers) add_subdirectory(easylogging++) +add_subdirectory(qrcodegen) add_subdirectory(randomx EXCLUDE_FROM_ALL) diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc index 8439bec0b..0d748c225 100644 --- a/external/easylogging++/easylogging++.cc +++ b/external/easylogging++/easylogging++.cc @@ -2475,6 +2475,100 @@ void DefaultLogDispatchCallback::handle(const LogDispatchData* data) { } } + +template<typename Transform> +static inline std::string utf8canonical(const std::string &s, Transform t = [](wint_t c)->wint_t { return c; }) +{ + std::string sc = ""; + size_t avail = s.size(); + const char *ptr = s.data(); + wint_t cp = 0; + int bytes = 1; + char wbuf[8], *wptr; + while (avail--) + { + if ((*ptr & 0x80) == 0) + { + cp = *ptr++; + bytes = 1; + } + else if ((*ptr & 0xe0) == 0xc0) + { + if (avail < 1) + throw std::runtime_error("Invalid UTF-8"); + cp = (*ptr++ & 0x1f) << 6; + cp |= *ptr++ & 0x3f; + --avail; + bytes = 2; + } + else if ((*ptr & 0xf0) == 0xe0) + { + if (avail < 2) + throw std::runtime_error("Invalid UTF-8"); + cp = (*ptr++ & 0xf) << 12; + cp |= (*ptr++ & 0x3f) << 6; + cp |= *ptr++ & 0x3f; + avail -= 2; + bytes = 3; + } + else if ((*ptr & 0xf8) == 0xf0) + { + if (avail < 3) + throw std::runtime_error("Invalid UTF-8"); + cp = (*ptr++ & 0x7) << 18; + cp |= (*ptr++ & 0x3f) << 12; + cp |= (*ptr++ & 0x3f) << 6; + cp |= *ptr++ & 0x3f; + avail -= 3; + bytes = 4; + } + else + throw std::runtime_error("Invalid UTF-8"); + + cp = t(cp); + if (cp <= 0x7f) + bytes = 1; + else if (cp <= 0x7ff) + bytes = 2; + else if (cp <= 0xffff) + bytes = 3; + else if (cp <= 0x10ffff) + bytes = 4; + else + throw std::runtime_error("Invalid code point UTF-8 transformation"); + + wptr = wbuf; + switch (bytes) + { + case 1: *wptr++ = cp; break; + case 2: *wptr++ = 0xc0 | (cp >> 6); *wptr++ = 0x80 | (cp & 0x3f); break; + case 3: *wptr++ = 0xe0 | (cp >> 12); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break; + case 4: *wptr++ = 0xf0 | (cp >> 18); *wptr++ = 0x80 | ((cp >> 12) & 0x3f); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break; + default: throw std::runtime_error("Invalid UTF-8"); + } + *wptr = 0; + sc.append(wbuf, bytes); + cp = 0; + bytes = 1; + } + return sc; +} + +void sanitize(std::string &s) +{ + s = utf8canonical(s, [](wint_t c)->wint_t { + if (c == 9 || c == 10 || c == 13) + return c; + if (c < 0x20) + return '?'; + if (c == 0x7f) + return '?'; + if (c >= 0x80 && c <= 0x9f) + return '?'; + return c; + }); +} + void DefaultLogDispatchCallback::dispatch(base::type::string_t&& rawLinePrefix, base::type::string_t&& rawLinePayload, base::type::string_t&& logLine) { if (m_data->dispatchAction() == base::DispatchAction::NormalLog || m_data->dispatchAction() == base::DispatchAction::FileOnlyLog) { if (m_data->logMessage()->logger()->m_typedConfigurations->toFile(m_data->logMessage()->level())) { @@ -2506,6 +2600,8 @@ void DefaultLogDispatchCallback::dispatch(base::type::string_t&& rawLinePrefix, m_data->logMessage()->logger()->logBuilder()->setColor(el::base::utils::colorFromLevel(level), false); ELPP_COUT << rawLinePrefix; m_data->logMessage()->logger()->logBuilder()->setColor(color == el::Color::Default ? el::base::utils::colorFromLevel(level): color, color != el::Color::Default); + try { sanitize(rawLinePayload); } + catch (const std::exception &e) { rawLinePayload = "<Invalid UTF-8 in log>"; } ELPP_COUT << rawLinePayload; m_data->logMessage()->logger()->logBuilder()->setColor(el::Color::Default, false); ELPP_COUT << std::flush; diff --git a/external/qrcodegen/CMakeLists.txt b/external/qrcodegen/CMakeLists.txt new file mode 100644 index 000000000..a9060e3e8 --- /dev/null +++ b/external/qrcodegen/CMakeLists.txt @@ -0,0 +1,7 @@ +project(libqrcodegen) + +add_library(qrcodegen STATIC QrCode.cpp) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + +target_include_directories(qrcodegen PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/external/qrcodegen/QrCode.cpp b/external/qrcodegen/QrCode.cpp new file mode 100644 index 000000000..b9de86215 --- /dev/null +++ b/external/qrcodegen/QrCode.cpp @@ -0,0 +1,862 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#include <algorithm> +#include <climits> +#include <cstddef> +#include <cstdlib> +#include <cstring> +#include <sstream> +#include <stdexcept> +#include <utility> +#include "QrCode.hpp" + +using std::int8_t; +using std::uint8_t; +using std::size_t; +using std::vector; + + +namespace qrcodegen { + +QrSegment::Mode::Mode(int mode, int cc0, int cc1, int cc2) : + modeBits(mode) { + numBitsCharCount[0] = cc0; + numBitsCharCount[1] = cc1; + numBitsCharCount[2] = cc2; +} + + +int QrSegment::Mode::getModeBits() const { + return modeBits; +} + + +int QrSegment::Mode::numCharCountBits(int ver) const { + return numBitsCharCount[(ver + 7) / 17]; +} + + +const QrSegment::Mode QrSegment::Mode::NUMERIC (0x1, 10, 12, 14); +const QrSegment::Mode QrSegment::Mode::ALPHANUMERIC(0x2, 9, 11, 13); +const QrSegment::Mode QrSegment::Mode::BYTE (0x4, 8, 16, 16); +const QrSegment::Mode QrSegment::Mode::KANJI (0x8, 8, 10, 12); +const QrSegment::Mode QrSegment::Mode::ECI (0x7, 0, 0, 0); + + +QrSegment QrSegment::makeBytes(const vector<uint8_t> &data) { + if (data.size() > static_cast<unsigned int>(INT_MAX)) + throw std::length_error("Data too long"); + BitBuffer bb; + for (uint8_t b : data) + bb.appendBits(b, 8); + return QrSegment(Mode::BYTE, static_cast<int>(data.size()), std::move(bb)); +} + + +QrSegment QrSegment::makeNumeric(const char *digits) { + BitBuffer bb; + int accumData = 0; + int accumCount = 0; + int charCount = 0; + for (; *digits != '\0'; digits++, charCount++) { + char c = *digits; + if (c < '0' || c > '9') + throw std::domain_error("String contains non-numeric characters"); + accumData = accumData * 10 + (c - '0'); + accumCount++; + if (accumCount == 3) { + bb.appendBits(static_cast<uint32_t>(accumData), 10); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 or 2 digits remaining + bb.appendBits(static_cast<uint32_t>(accumData), accumCount * 3 + 1); + return QrSegment(Mode::NUMERIC, charCount, std::move(bb)); +} + + +QrSegment QrSegment::makeAlphanumeric(const char *text) { + BitBuffer bb; + int accumData = 0; + int accumCount = 0; + int charCount = 0; + for (; *text != '\0'; text++, charCount++) { + const char *temp = std::strchr(ALPHANUMERIC_CHARSET, *text); + if (temp == nullptr) + throw std::domain_error("String contains unencodable characters in alphanumeric mode"); + accumData = accumData * 45 + static_cast<int>(temp - ALPHANUMERIC_CHARSET); + accumCount++; + if (accumCount == 2) { + bb.appendBits(static_cast<uint32_t>(accumData), 11); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 character remaining + bb.appendBits(static_cast<uint32_t>(accumData), 6); + return QrSegment(Mode::ALPHANUMERIC, charCount, std::move(bb)); +} + + +vector<QrSegment> QrSegment::makeSegments(const char *text) { + // Select the most efficient segment encoding automatically + vector<QrSegment> result; + if (*text == '\0'); // Leave result empty + else if (isNumeric(text)) + result.push_back(makeNumeric(text)); + else if (isAlphanumeric(text)) + result.push_back(makeAlphanumeric(text)); + else { + vector<uint8_t> bytes; + for (; *text != '\0'; text++) + bytes.push_back(static_cast<uint8_t>(*text)); + result.push_back(makeBytes(bytes)); + } + return result; +} + + +QrSegment QrSegment::makeEci(long assignVal) { + BitBuffer bb; + if (assignVal < 0) + throw std::domain_error("ECI assignment value out of range"); + else if (assignVal < (1 << 7)) + bb.appendBits(static_cast<uint32_t>(assignVal), 8); + else if (assignVal < (1 << 14)) { + bb.appendBits(2, 2); + bb.appendBits(static_cast<uint32_t>(assignVal), 14); + } else if (assignVal < 1000000L) { + bb.appendBits(6, 3); + bb.appendBits(static_cast<uint32_t>(assignVal), 21); + } else + throw std::domain_error("ECI assignment value out of range"); + return QrSegment(Mode::ECI, 0, std::move(bb)); +} + + +QrSegment::QrSegment(Mode md, int numCh, const std::vector<bool> &dt) : + mode(md), + numChars(numCh), + data(dt) { + if (numCh < 0) + throw std::domain_error("Invalid value"); +} + + +QrSegment::QrSegment(Mode md, int numCh, std::vector<bool> &&dt) : + mode(md), + numChars(numCh), + data(std::move(dt)) { + if (numCh < 0) + throw std::domain_error("Invalid value"); +} + + +int QrSegment::getTotalBits(const vector<QrSegment> &segs, int version) { + int result = 0; + for (const QrSegment &seg : segs) { + int ccbits = seg.mode.numCharCountBits(version); + if (seg.numChars >= (1L << ccbits)) + return -1; // The segment's length doesn't fit the field's bit width + if (4 + ccbits > INT_MAX - result) + return -1; // The sum will overflow an int type + result += 4 + ccbits; + if (seg.data.size() > static_cast<unsigned int>(INT_MAX - result)) + return -1; // The sum will overflow an int type + result += static_cast<int>(seg.data.size()); + } + return result; +} + + +bool QrSegment::isAlphanumeric(const char *text) { + for (; *text != '\0'; text++) { + if (std::strchr(ALPHANUMERIC_CHARSET, *text) == nullptr) + return false; + } + return true; +} + + +bool QrSegment::isNumeric(const char *text) { + for (; *text != '\0'; text++) { + char c = *text; + if (c < '0' || c > '9') + return false; + } + return true; +} + + +QrSegment::Mode QrSegment::getMode() const { + return mode; +} + + +int QrSegment::getNumChars() const { + return numChars; +} + + +const std::vector<bool> &QrSegment::getData() const { + return data; +} + + +const char *QrSegment::ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; + + + +int QrCode::getFormatBits(Ecc ecl) { + switch (ecl) { + case Ecc::LOW : return 1; + case Ecc::MEDIUM : return 0; + case Ecc::QUARTILE: return 3; + case Ecc::HIGH : return 2; + default: throw std::logic_error("Assertion error"); + } +} + + +QrCode QrCode::encodeText(const char *text, Ecc ecl) { + vector<QrSegment> segs = QrSegment::makeSegments(text); + return encodeSegments(segs, ecl); +} + + +QrCode QrCode::encodeBinary(const vector<uint8_t> &data, Ecc ecl) { + vector<QrSegment> segs{QrSegment::makeBytes(data)}; + return encodeSegments(segs, ecl); +} + + +QrCode QrCode::encodeSegments(const vector<QrSegment> &segs, Ecc ecl, + int minVersion, int maxVersion, int mask, bool boostEcl) { + if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7) + throw std::invalid_argument("Invalid value"); + + // Find the minimal version number to use + int version, dataUsedBits; + for (version = minVersion; ; version++) { + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available + dataUsedBits = QrSegment::getTotalBits(segs, version); + if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) + break; // This version number is found to be suitable + if (version >= maxVersion) { // All versions in the range could not fit the given data + std::ostringstream sb; + if (dataUsedBits == -1) + sb << "Segment too long"; + else { + sb << "Data length = " << dataUsedBits << " bits, "; + sb << "Max capacity = " << dataCapacityBits << " bits"; + } + throw data_too_long(sb.str()); + } + } + if (dataUsedBits == -1) + throw std::logic_error("Assertion error"); + + // Increase the error correction level while the data still fits in the current version number + for (Ecc newEcl : vector<Ecc>{Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) { // From low to high + if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) + ecl = newEcl; + } + + // Concatenate all segments to create the data bit string + BitBuffer bb; + for (const QrSegment &seg : segs) { + bb.appendBits(static_cast<uint32_t>(seg.getMode().getModeBits()), 4); + bb.appendBits(static_cast<uint32_t>(seg.getNumChars()), seg.getMode().numCharCountBits(version)); + bb.insert(bb.end(), seg.getData().begin(), seg.getData().end()); + } + if (bb.size() != static_cast<unsigned int>(dataUsedBits)) + throw std::logic_error("Assertion error"); + + // Add terminator and pad up to a byte if applicable + size_t dataCapacityBits = static_cast<size_t>(getNumDataCodewords(version, ecl)) * 8; + if (bb.size() > dataCapacityBits) + throw std::logic_error("Assertion error"); + bb.appendBits(0, std::min(4, static_cast<int>(dataCapacityBits - bb.size()))); + bb.appendBits(0, (8 - static_cast<int>(bb.size() % 8)) % 8); + if (bb.size() % 8 != 0) + throw std::logic_error("Assertion error"); + + // Pad with alternating bytes until data capacity is reached + for (uint8_t padByte = 0xEC; bb.size() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + bb.appendBits(padByte, 8); + + // Pack bits into bytes in big endian + vector<uint8_t> dataCodewords(bb.size() / 8); + for (size_t i = 0; i < bb.size(); i++) + dataCodewords[i >> 3] |= (bb.at(i) ? 1 : 0) << (7 - (i & 7)); + + // Create the QR Code object + return QrCode(version, ecl, dataCodewords, mask); +} + + +QrCode::QrCode(int ver, Ecc ecl, const vector<uint8_t> &dataCodewords, int msk) : + // Initialize fields and check arguments + version(ver), + errorCorrectionLevel(ecl) { + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw std::domain_error("Version value out of range"); + if (msk < -1 || msk > 7) + throw std::domain_error("Mask value out of range"); + size = ver * 4 + 17; + size_t sz = static_cast<size_t>(size); + modules = vector<vector<bool> >(sz, vector<bool>(sz)); // Initially all white + isFunction = vector<vector<bool> >(sz, vector<bool>(sz)); + + // Compute ECC, draw modules + drawFunctionPatterns(); + const vector<uint8_t> allCodewords = addEccAndInterleave(dataCodewords); + drawCodewords(allCodewords); + + // Do masking + if (msk == -1) { // Automatically choose best mask + long minPenalty = LONG_MAX; + for (int i = 0; i < 8; i++) { + applyMask(i); + drawFormatBits(i); + long penalty = getPenaltyScore(); + if (penalty < minPenalty) { + msk = i; + minPenalty = penalty; + } + applyMask(i); // Undoes the mask due to XOR + } + } + if (msk < 0 || msk > 7) + throw std::logic_error("Assertion error"); + this->mask = msk; + applyMask(msk); // Apply the final choice of mask + drawFormatBits(msk); // Overwrite old format bits + + isFunction.clear(); + isFunction.shrink_to_fit(); +} + + +int QrCode::getVersion() const { + return version; +} + + +int QrCode::getSize() const { + return size; +} + + +QrCode::Ecc QrCode::getErrorCorrectionLevel() const { + return errorCorrectionLevel; +} + + +int QrCode::getMask() const { + return mask; +} + + +bool QrCode::getModule(int x, int y) const { + return 0 <= x && x < size && 0 <= y && y < size && module(x, y); +} + + +std::string QrCode::toSvgString(int border) const { + if (border < 0) + throw std::domain_error("Border must be non-negative"); + if (border > INT_MAX / 2 || border * 2 > INT_MAX - size) + throw std::overflow_error("Border too large"); + + std::ostringstream sb; + sb << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + sb << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"; + sb << "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 "; + sb << (size + border * 2) << " " << (size + border * 2) << "\" stroke=\"none\">\n"; + sb << "\t<rect width=\"100%\" height=\"100%\" fill=\"#FFFFFF\"/>\n"; + sb << "\t<path d=\""; + for (int y = 0; y < size; y++) { + for (int x = 0; x < size; x++) { + if (getModule(x, y)) { + if (x != 0 || y != 0) + sb << " "; + sb << "M" << (x + border) << "," << (y + border) << "h1v1h-1z"; + } + } + } + sb << "\" fill=\"#000000\"/>\n"; + sb << "</svg>\n"; + return sb.str(); +} + + +void QrCode::drawFunctionPatterns() { + // Draw horizontal and vertical timing patterns + for (int i = 0; i < size; i++) { + setFunctionModule(6, i, i % 2 == 0); + setFunctionModule(i, 6, i % 2 == 0); + } + + // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) + drawFinderPattern(3, 3); + drawFinderPattern(size - 4, 3); + drawFinderPattern(3, size - 4); + + // Draw numerous alignment patterns + const vector<int> alignPatPos = getAlignmentPatternPositions(); + size_t numAlign = alignPatPos.size(); + for (size_t i = 0; i < numAlign; i++) { + for (size_t j = 0; j < numAlign; j++) { + // Don't draw on the three finder corners + if (!((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0))) + drawAlignmentPattern(alignPatPos.at(i), alignPatPos.at(j)); + } + } + + // Draw configuration data + drawFormatBits(0); // Dummy mask value; overwritten later in the constructor + drawVersion(); +} + + +void QrCode::drawFormatBits(int msk) { + // Calculate error correction code and pack bits + int data = getFormatBits(errorCorrectionLevel) << 3 | msk; // errCorrLvl is uint2, msk is uint3 + int rem = data; + for (int i = 0; i < 10; i++) + rem = (rem << 1) ^ ((rem >> 9) * 0x537); + int bits = (data << 10 | rem) ^ 0x5412; // uint15 + if (bits >> 15 != 0) + throw std::logic_error("Assertion error"); + + // Draw first copy + for (int i = 0; i <= 5; i++) + setFunctionModule(8, i, getBit(bits, i)); + setFunctionModule(8, 7, getBit(bits, 6)); + setFunctionModule(8, 8, getBit(bits, 7)); + setFunctionModule(7, 8, getBit(bits, 8)); + for (int i = 9; i < 15; i++) + setFunctionModule(14 - i, 8, getBit(bits, i)); + + // Draw second copy + for (int i = 0; i < 8; i++) + setFunctionModule(size - 1 - i, 8, getBit(bits, i)); + for (int i = 8; i < 15; i++) + setFunctionModule(8, size - 15 + i, getBit(bits, i)); + setFunctionModule(8, size - 8, true); // Always black +} + + +void QrCode::drawVersion() { + if (version < 7) + return; + + // Calculate error correction code and pack bits + int rem = version; // version is uint6, in the range [7, 40] + for (int i = 0; i < 12; i++) + rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); + long bits = static_cast<long>(version) << 12 | rem; // uint18 + if (bits >> 18 != 0) + throw std::logic_error("Assertion error"); + + // Draw two copies + for (int i = 0; i < 18; i++) { + bool bit = getBit(bits, i); + int a = size - 11 + i % 3; + int b = i / 3; + setFunctionModule(a, b, bit); + setFunctionModule(b, a, bit); + } +} + + +void QrCode::drawFinderPattern(int x, int y) { + for (int dy = -4; dy <= 4; dy++) { + for (int dx = -4; dx <= 4; dx++) { + int dist = std::max(std::abs(dx), std::abs(dy)); // Chebyshev/infinity norm + int xx = x + dx, yy = y + dy; + if (0 <= xx && xx < size && 0 <= yy && yy < size) + setFunctionModule(xx, yy, dist != 2 && dist != 4); + } + } +} + + +void QrCode::drawAlignmentPattern(int x, int y) { + for (int dy = -2; dy <= 2; dy++) { + for (int dx = -2; dx <= 2; dx++) + setFunctionModule(x + dx, y + dy, std::max(std::abs(dx), std::abs(dy)) != 1); + } +} + + +void QrCode::setFunctionModule(int x, int y, bool isBlack) { + size_t ux = static_cast<size_t>(x); + size_t uy = static_cast<size_t>(y); + modules .at(uy).at(ux) = isBlack; + isFunction.at(uy).at(ux) = true; +} + + +bool QrCode::module(int x, int y) const { + return modules.at(static_cast<size_t>(y)).at(static_cast<size_t>(x)); +} + + +vector<uint8_t> QrCode::addEccAndInterleave(const vector<uint8_t> &data) const { + if (data.size() != static_cast<unsigned int>(getNumDataCodewords(version, errorCorrectionLevel))) + throw std::invalid_argument("Invalid argument"); + + // Calculate parameter numbers + int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[static_cast<int>(errorCorrectionLevel)][version]; + int blockEccLen = ECC_CODEWORDS_PER_BLOCK [static_cast<int>(errorCorrectionLevel)][version]; + int rawCodewords = getNumRawDataModules(version) / 8; + int numShortBlocks = numBlocks - rawCodewords % numBlocks; + int shortBlockLen = rawCodewords / numBlocks; + + // Split data into blocks and append ECC to each block + vector<vector<uint8_t> > blocks; + const vector<uint8_t> rsDiv = reedSolomonComputeDivisor(blockEccLen); + for (int i = 0, k = 0; i < numBlocks; i++) { + vector<uint8_t> dat(data.cbegin() + k, data.cbegin() + (k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1))); + k += static_cast<int>(dat.size()); + const vector<uint8_t> ecc = reedSolomonComputeRemainder(dat, rsDiv); + if (i < numShortBlocks) + dat.push_back(0); + dat.insert(dat.end(), ecc.cbegin(), ecc.cend()); + blocks.push_back(std::move(dat)); + } + + // Interleave (not concatenate) the bytes from every block into a single sequence + vector<uint8_t> result; + for (size_t i = 0; i < blocks.at(0).size(); i++) { + for (size_t j = 0; j < blocks.size(); j++) { + // Skip the padding byte in short blocks + if (i != static_cast<unsigned int>(shortBlockLen - blockEccLen) || j >= static_cast<unsigned int>(numShortBlocks)) + result.push_back(blocks.at(j).at(i)); + } + } + if (result.size() != static_cast<unsigned int>(rawCodewords)) + throw std::logic_error("Assertion error"); + return result; +} + + +void QrCode::drawCodewords(const vector<uint8_t> &data) { + if (data.size() != static_cast<unsigned int>(getNumRawDataModules(version) / 8)) + throw std::invalid_argument("Invalid argument"); + + size_t i = 0; // Bit index into the data + // Do the funny zigzag scan + for (int right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair + if (right == 6) + right = 5; + for (int vert = 0; vert < size; vert++) { // Vertical counter + for (int j = 0; j < 2; j++) { + size_t x = static_cast<size_t>(right - j); // Actual x coordinate + bool upward = ((right + 1) & 2) == 0; + size_t y = static_cast<size_t>(upward ? size - 1 - vert : vert); // Actual y coordinate + if (!isFunction.at(y).at(x) && i < data.size() * 8) { + modules.at(y).at(x) = getBit(data.at(i >> 3), 7 - static_cast<int>(i & 7)); + i++; + } + // If this QR Code has any remainder bits (0 to 7), they were assigned as + // 0/false/white by the constructor and are left unchanged by this method + } + } + } + if (i != data.size() * 8) + throw std::logic_error("Assertion error"); +} + + +void QrCode::applyMask(int msk) { + if (msk < 0 || msk > 7) + throw std::domain_error("Mask value out of range"); + size_t sz = static_cast<size_t>(size); + for (size_t y = 0; y < sz; y++) { + for (size_t x = 0; x < sz; x++) { + bool invert; + switch (msk) { + case 0: invert = (x + y) % 2 == 0; break; + case 1: invert = y % 2 == 0; break; + case 2: invert = x % 3 == 0; break; + case 3: invert = (x + y) % 3 == 0; break; + case 4: invert = (x / 3 + y / 2) % 2 == 0; break; + case 5: invert = x * y % 2 + x * y % 3 == 0; break; + case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; + case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; + default: throw std::logic_error("Assertion error"); + } + modules.at(y).at(x) = modules.at(y).at(x) ^ (invert & !isFunction.at(y).at(x)); + } + } +} + + +long QrCode::getPenaltyScore() const { + long result = 0; + + // Adjacent modules in row having same color, and finder-like patterns + for (int y = 0; y < size; y++) { + bool runColor = false; + int runX = 0; + std::array<int,7> runHistory = {}; + for (int x = 0; x < size; x++) { + if (module(x, y) == runColor) { + runX++; + if (runX == 5) + result += PENALTY_N1; + else if (runX > 5) + result++; + } else { + finderPenaltyAddHistory(runX, runHistory); + if (!runColor) + result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; + runColor = module(x, y); + runX = 1; + } + } + result += finderPenaltyTerminateAndCount(runColor, runX, runHistory) * PENALTY_N3; + } + // Adjacent modules in column having same color, and finder-like patterns + for (int x = 0; x < size; x++) { + bool runColor = false; + int runY = 0; + std::array<int,7> runHistory = {}; + for (int y = 0; y < size; y++) { + if (module(x, y) == runColor) { + runY++; + if (runY == 5) + result += PENALTY_N1; + else if (runY > 5) + result++; + } else { + finderPenaltyAddHistory(runY, runHistory); + if (!runColor) + result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; + runColor = module(x, y); + runY = 1; + } + } + result += finderPenaltyTerminateAndCount(runColor, runY, runHistory) * PENALTY_N3; + } + + // 2*2 blocks of modules having same color + for (int y = 0; y < size - 1; y++) { + for (int x = 0; x < size - 1; x++) { + bool color = module(x, y); + if ( color == module(x + 1, y) && + color == module(x, y + 1) && + color == module(x + 1, y + 1)) + result += PENALTY_N2; + } + } + + // Balance of black and white modules + int black = 0; + for (const vector<bool> &row : modules) { + for (bool color : row) { + if (color) + black++; + } + } + int total = size * size; // Note that size is odd, so black/total != 1/2 + // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% + int k = static_cast<int>((std::abs(black * 20L - total * 10L) + total - 1) / total) - 1; + result += k * PENALTY_N4; + return result; +} + + +vector<int> QrCode::getAlignmentPatternPositions() const { + if (version == 1) + return vector<int>(); + else { + int numAlign = version / 7 + 2; + int step = (version == 32) ? 26 : + (version*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; + vector<int> result; + for (int i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) + result.insert(result.begin(), pos); + result.insert(result.begin(), 6); + return result; + } +} + + +int QrCode::getNumRawDataModules(int ver) { + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw std::domain_error("Version number out of range"); + int result = (16 * ver + 128) * ver + 64; + if (ver >= 2) { + int numAlign = ver / 7 + 2; + result -= (25 * numAlign - 10) * numAlign - 55; + if (ver >= 7) + result -= 36; + } + if (!(208 <= result && result <= 29648)) + throw std::logic_error("Assertion error"); + return result; +} + + +int QrCode::getNumDataCodewords(int ver, Ecc ecl) { + return getNumRawDataModules(ver) / 8 + - ECC_CODEWORDS_PER_BLOCK [static_cast<int>(ecl)][ver] + * NUM_ERROR_CORRECTION_BLOCKS[static_cast<int>(ecl)][ver]; +} + + +vector<uint8_t> QrCode::reedSolomonComputeDivisor(int degree) { + if (degree < 1 || degree > 255) + throw std::domain_error("Degree out of range"); + // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. + // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + vector<uint8_t> result(static_cast<size_t>(degree)); + result.at(result.size() - 1) = 1; // Start off with the monomial x^0 + + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + // and drop the highest monomial term which is always 1x^degree. + // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + uint8_t root = 1; + for (int i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (size_t j = 0; j < result.size(); j++) { + result.at(j) = reedSolomonMultiply(result.at(j), root); + if (j + 1 < result.size()) + result.at(j) ^= result.at(j + 1); + } + root = reedSolomonMultiply(root, 0x02); + } + return result; +} + + +vector<uint8_t> QrCode::reedSolomonComputeRemainder(const vector<uint8_t> &data, const vector<uint8_t> &divisor) { + vector<uint8_t> result(divisor.size()); + for (uint8_t b : data) { // Polynomial division + uint8_t factor = b ^ result.at(0); + result.erase(result.begin()); + result.push_back(0); + for (size_t i = 0; i < result.size(); i++) + result.at(i) ^= reedSolomonMultiply(divisor.at(i), factor); + } + return result; +} + + +uint8_t QrCode::reedSolomonMultiply(uint8_t x, uint8_t y) { + // Russian peasant multiplication + int z = 0; + for (int i = 7; i >= 0; i--) { + z = (z << 1) ^ ((z >> 7) * 0x11D); + z ^= ((y >> i) & 1) * x; + } + if (z >> 8 != 0) + throw std::logic_error("Assertion error"); + return static_cast<uint8_t>(z); +} + + +int QrCode::finderPenaltyCountPatterns(const std::array<int,7> &runHistory) const { + int n = runHistory.at(1); + if (n > size * 3) + throw std::logic_error("Assertion error"); + bool core = n > 0 && runHistory.at(2) == n && runHistory.at(3) == n * 3 && runHistory.at(4) == n && runHistory.at(5) == n; + return (core && runHistory.at(0) >= n * 4 && runHistory.at(6) >= n ? 1 : 0) + + (core && runHistory.at(6) >= n * 4 && runHistory.at(0) >= n ? 1 : 0); +} + + +int QrCode::finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array<int,7> &runHistory) const { + if (currentRunColor) { // Terminate black run + finderPenaltyAddHistory(currentRunLength, runHistory); + currentRunLength = 0; + } + currentRunLength += size; // Add white border to final run + finderPenaltyAddHistory(currentRunLength, runHistory); + return finderPenaltyCountPatterns(runHistory); +} + + +void QrCode::finderPenaltyAddHistory(int currentRunLength, std::array<int,7> &runHistory) const { + if (runHistory.at(0) == 0) + currentRunLength += size; // Add white border to initial run + std::copy_backward(runHistory.cbegin(), runHistory.cend() - 1, runHistory.end()); + runHistory.at(0) = currentRunLength; +} + + +bool QrCode::getBit(long x, int i) { + return ((x >> i) & 1) != 0; +} + + +/*---- Tables of constants ----*/ + +const int QrCode::PENALTY_N1 = 3; +const int QrCode::PENALTY_N2 = 3; +const int QrCode::PENALTY_N3 = 40; +const int QrCode::PENALTY_N4 = 10; + + +const int8_t QrCode::ECC_CODEWORDS_PER_BLOCK[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Low + {-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, // Medium + {-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Quartile + {-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // High +}; + +const int8_t QrCode::NUM_ERROR_CORRECTION_BLOCKS[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low + {-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium + {-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile + {-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High +}; + + +data_too_long::data_too_long(const std::string &msg) : + std::length_error(msg) {} + + + +BitBuffer::BitBuffer() + : std::vector<bool>() {} + + +void BitBuffer::appendBits(std::uint32_t val, int len) { + if (len < 0 || len > 31 || val >> len != 0) + throw std::domain_error("Value out of range"); + for (int i = len - 1; i >= 0; i--) // Append bit by bit + this->push_back(((val >> i) & 1) != 0); +} + +} diff --git a/external/qrcodegen/QrCode.hpp b/external/qrcodegen/QrCode.hpp new file mode 100644 index 000000000..7341e4102 --- /dev/null +++ b/external/qrcodegen/QrCode.hpp @@ -0,0 +1,556 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#pragma once + +#include <array> +#include <cstdint> +#include <stdexcept> +#include <string> +#include <vector> + + +namespace qrcodegen { + +/* + * A segment of character/binary/control data in a QR Code symbol. + * Instances of this class are immutable. + * The mid-level way to create a segment is to take the payload data + * and call a static factory function such as QrSegment::makeNumeric(). + * The low-level way to create a segment is to custom-make the bit buffer + * and call the QrSegment() constructor with appropriate values. + * This segment class imposes no length restrictions, but QR Codes have restrictions. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes. + */ +class QrSegment final { + + /*---- Public helper enumeration ----*/ + + /* + * Describes how a segment's data bits are interpreted. Immutable. + */ + public: class Mode final { + + /*-- Constants --*/ + + public: static const Mode NUMERIC; + public: static const Mode ALPHANUMERIC; + public: static const Mode BYTE; + public: static const Mode KANJI; + public: static const Mode ECI; + + + /*-- Fields --*/ + + // The mode indicator bits, which is a uint4 value (range 0 to 15). + private: int modeBits; + + // Number of character count bits for three different version ranges. + private: int numBitsCharCount[3]; + + + /*-- Constructor --*/ + + private: Mode(int mode, int cc0, int cc1, int cc2); + + + /*-- Methods --*/ + + /* + * (Package-private) Returns the mode indicator bits, which is an unsigned 4-bit value (range 0 to 15). + */ + public: int getModeBits() const; + + /* + * (Package-private) Returns the bit width of the character count field for a segment in + * this mode in a QR Code at the given version number. The result is in the range [0, 16]. + */ + public: int numCharCountBits(int ver) const; + + }; + + + + /*---- Static factory functions (mid level) ----*/ + + /* + * Returns a segment representing the given binary data encoded in + * byte mode. All input byte vectors are acceptable. Any text string + * can be converted to UTF-8 bytes and encoded as a byte mode segment. + */ + public: static QrSegment makeBytes(const std::vector<std::uint8_t> &data); + + + /* + * Returns a segment representing the given string of decimal digits encoded in numeric mode. + */ + public: static QrSegment makeNumeric(const char *digits); + + + /* + * Returns a segment representing the given text string encoded in alphanumeric mode. + * The characters allowed are: 0 to 9, A to Z (uppercase only), space, + * dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ + public: static QrSegment makeAlphanumeric(const char *text); + + + /* + * Returns a list of zero or more segments to represent the given text string. The result + * may use various segment modes and switch modes to optimize the length of the bit stream. + */ + public: static std::vector<QrSegment> makeSegments(const char *text); + + + /* + * Returns a segment representing an Extended Channel Interpretation + * (ECI) designator with the given assignment value. + */ + public: static QrSegment makeEci(long assignVal); + + + /*---- Public static helper functions ----*/ + + /* + * Tests whether the given string can be encoded as a segment in alphanumeric mode. + * A string is encodable iff each character is in the following set: 0 to 9, A to Z + * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ + public: static bool isAlphanumeric(const char *text); + + + /* + * Tests whether the given string can be encoded as a segment in numeric mode. + * A string is encodable iff each character is in the range 0 to 9. + */ + public: static bool isNumeric(const char *text); + + + + /*---- Instance fields ----*/ + + /* The mode indicator of this segment. Accessed through getMode(). */ + private: Mode mode; + + /* The length of this segment's unencoded data. Measured in characters for + * numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. + * Always zero or positive. Not the same as the data's bit length. + * Accessed through getNumChars(). */ + private: int numChars; + + /* The data bits of this segment. Accessed through getData(). */ + private: std::vector<bool> data; + + + /*---- Constructors (low level) ----*/ + + /* + * Creates a new QR Code segment with the given attributes and data. + * The character count (numCh) must agree with the mode and the bit buffer length, + * but the constraint isn't checked. The given bit buffer is copied and stored. + */ + public: QrSegment(Mode md, int numCh, const std::vector<bool> &dt); + + + /* + * Creates a new QR Code segment with the given parameters and data. + * The character count (numCh) must agree with the mode and the bit buffer length, + * but the constraint isn't checked. The given bit buffer is moved and stored. + */ + public: QrSegment(Mode md, int numCh, std::vector<bool> &&dt); + + + /*---- Methods ----*/ + + /* + * Returns the mode field of this segment. + */ + public: Mode getMode() const; + + + /* + * Returns the character count field of this segment. + */ + public: int getNumChars() const; + + + /* + * Returns the data bits of this segment. + */ + public: const std::vector<bool> &getData() const; + + + // (Package-private) Calculates the number of bits needed to encode the given segments at + // the given version. Returns a non-negative number if successful. Otherwise returns -1 if a + // segment has too many characters to fit its length field, or the total bits exceeds INT_MAX. + public: static int getTotalBits(const std::vector<QrSegment> &segs, int version); + + + /*---- Private constant ----*/ + + /* The set of all legal characters in alphanumeric mode, where + * each character value maps to the index in the string. */ + private: static const char *ALPHANUMERIC_CHARSET; + +}; + + + +/* + * A QR Code symbol, which is a type of two-dimension barcode. + * Invented by Denso Wave and described in the ISO/IEC 18004 standard. + * Instances of this class represent an immutable square grid of black and white cells. + * The class provides static factory functions to create a QR Code from text or binary data. + * The class covers the QR Code Model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. + * + * Ways to create a QR Code object: + * - High level: Take the payload data and call QrCode::encodeText() or QrCode::encodeBinary(). + * - Mid level: Custom-make the list of segments and call QrCode::encodeSegments(). + * - Low level: Custom-make the array of data codeword bytes (including + * segment headers and final padding, excluding error correction codewords), + * supply the appropriate version number, and call the QrCode() constructor. + * (Note that all ways require supplying the desired error correction level.) + */ +class QrCode final { + + /*---- Public helper enumeration ----*/ + + /* + * The error correction level in a QR Code symbol. + */ + public: enum class Ecc { + LOW = 0 , // The QR Code can tolerate about 7% erroneous codewords + MEDIUM , // The QR Code can tolerate about 15% erroneous codewords + QUARTILE, // The QR Code can tolerate about 25% erroneous codewords + HIGH , // The QR Code can tolerate about 30% erroneous codewords + }; + + + // Returns a value in the range 0 to 3 (unsigned 2-bit integer). + private: static int getFormatBits(Ecc ecl); + + + + /*---- Static factory functions (high level) ----*/ + + /* + * Returns a QR Code representing the given Unicode text string at the given error correction level. + * As a conservative upper bound, this function is guaranteed to succeed for strings that have 2953 or fewer + * UTF-8 code units (not Unicode code points) if the low error correction level is used. The smallest possible + * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than + * the ecl argument if it can be done without increasing the version. + */ + public: static QrCode encodeText(const char *text, Ecc ecl); + + + /* + * Returns a QR Code representing the given binary data at the given error correction level. + * This function always encodes using the binary segment mode, not any text mode. The maximum number of + * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. + */ + public: static QrCode encodeBinary(const std::vector<std::uint8_t> &data, Ecc ecl); + + + /*---- Static factory functions (mid level) ----*/ + + /* + * Returns a QR Code representing the given segments with the given encoding parameters. + * The smallest possible QR Code version within the given range is automatically + * chosen for the output. Iff boostEcl is true, then the ECC level of the result + * may be higher than the ecl argument if it can be done without increasing the + * version. The mask number is either between 0 to 7 (inclusive) to force that + * mask, or -1 to automatically choose an appropriate mask (which may be slow). + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and byte) to encode text in less space. + * This is a mid-level API; the high-level API is encodeText() and encodeBinary(). + */ + public: static QrCode encodeSegments(const std::vector<QrSegment> &segs, Ecc ecl, + int minVersion=1, int maxVersion=40, int mask=-1, bool boostEcl=true); // All optional parameters + + + + /*---- Instance fields ----*/ + + // Immutable scalar parameters: + + /* The version number of this QR Code, which is between 1 and 40 (inclusive). + * This determines the size of this barcode. */ + private: int version; + + /* The width and height of this QR Code, measured in modules, between + * 21 and 177 (inclusive). This is equal to version * 4 + 17. */ + private: int size; + + /* The error correction level used in this QR Code. */ + private: Ecc errorCorrectionLevel; + + /* The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive). + * Even if a QR Code is created with automatic masking requested (mask = -1), + * the resulting object still has a mask value between 0 and 7. */ + private: int mask; + + // Private grids of modules/pixels, with dimensions of size*size: + + // The modules of this QR Code (false = white, true = black). + // Immutable after constructor finishes. Accessed through getModule(). + private: std::vector<std::vector<bool> > modules; + + // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. + private: std::vector<std::vector<bool> > isFunction; + + + + /*---- Constructor (low level) ----*/ + + /* + * Creates a new QR Code with the given version number, + * error correction level, data codeword bytes, and mask number. + * This is a low-level API that most users should not use directly. + * A mid-level API is the encodeSegments() function. + */ + public: QrCode(int ver, Ecc ecl, const std::vector<std::uint8_t> &dataCodewords, int msk); + + + + /*---- Public instance methods ----*/ + + /* + * Returns this QR Code's version, in the range [1, 40]. + */ + public: int getVersion() const; + + + /* + * Returns this QR Code's size, in the range [21, 177]. + */ + public: int getSize() const; + + + /* + * Returns this QR Code's error correction level. + */ + public: Ecc getErrorCorrectionLevel() const; + + + /* + * Returns this QR Code's mask, in the range [0, 7]. + */ + public: int getMask() const; + + + /* + * Returns the color of the module (pixel) at the given coordinates, which is false + * for white or true for black. The top left corner has the coordinates (x=0, y=0). + * If the given coordinates are out of bounds, then false (white) is returned. + */ + public: bool getModule(int x, int y) const; + + + /* + * Returns a string of SVG code for an image depicting this QR Code, with the given number + * of border modules. The string always uses Unix newlines (\n), regardless of the platform. + */ + public: std::string toSvgString(int border) const; + + + + /*---- Private helper methods for constructor: Drawing function modules ----*/ + + // Reads this object's version field, and draws and marks all function modules. + private: void drawFunctionPatterns(); + + + // Draws two copies of the format bits (with its own error correction code) + // based on the given mask and this object's error correction level field. + private: void drawFormatBits(int msk); + + + // Draws two copies of the version bits (with its own error correction code), + // based on this object's version field, iff 7 <= version <= 40. + private: void drawVersion(); + + + // Draws a 9*9 finder pattern including the border separator, + // with the center module at (x, y). Modules can be out of bounds. + private: void drawFinderPattern(int x, int y); + + + // Draws a 5*5 alignment pattern, with the center module + // at (x, y). All modules must be in bounds. + private: void drawAlignmentPattern(int x, int y); + + + // Sets the color of a module and marks it as a function module. + // Only used by the constructor. Coordinates must be in bounds. + private: void setFunctionModule(int x, int y, bool isBlack); + + + // Returns the color of the module at the given coordinates, which must be in range. + private: bool module(int x, int y) const; + + + /*---- Private helper methods for constructor: Codewords and masking ----*/ + + // Returns a new byte string representing the given data with the appropriate error correction + // codewords appended to it, based on this object's version and error correction level. + private: std::vector<std::uint8_t> addEccAndInterleave(const std::vector<std::uint8_t> &data) const; + + + // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire + // data area of this QR Code. Function modules need to be marked off before this is called. + private: void drawCodewords(const std::vector<std::uint8_t> &data); + + + // XORs the codeword modules in this QR Code with the given mask pattern. + // The function modules must be marked and the codeword bits must be drawn + // before masking. Due to the arithmetic of XOR, calling applyMask() with + // the same mask value a second time will undo the mask. A final well-formed + // QR Code needs exactly one (not zero, two, etc.) mask applied. + private: void applyMask(int msk); + + + // Calculates and returns the penalty score based on state of this QR Code's current modules. + // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. + private: long getPenaltyScore() const; + + + + /*---- Private helper functions ----*/ + + // Returns an ascending list of positions of alignment patterns for this version number. + // Each position is in the range [0,177), and are used on both the x and y axes. + // This could be implemented as lookup table of 40 variable-length lists of unsigned bytes. + private: std::vector<int> getAlignmentPatternPositions() const; + + + // Returns the number of data bits that can be stored in a QR Code of the given version number, after + // all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. + // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. + private: static int getNumRawDataModules(int ver); + + + // Returns the number of 8-bit data (i.e. not error correction) codewords contained in any + // QR Code of the given version number and error correction level, with remainder bits discarded. + // This stateless pure function could be implemented as a (40*4)-cell lookup table. + private: static int getNumDataCodewords(int ver, Ecc ecl); + + + // Returns a Reed-Solomon ECC generator polynomial for the given degree. This could be + // implemented as a lookup table over all possible parameter values, instead of as an algorithm. + private: static std::vector<std::uint8_t> reedSolomonComputeDivisor(int degree); + + + // Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials. + private: static std::vector<std::uint8_t> reedSolomonComputeRemainder(const std::vector<std::uint8_t> &data, const std::vector<std::uint8_t> &divisor); + + + // Returns the product of the two given field elements modulo GF(2^8/0x11D). + // All inputs are valid. This could be implemented as a 256*256 lookup table. + private: static std::uint8_t reedSolomonMultiply(std::uint8_t x, std::uint8_t y); + + + // Can only be called immediately after a white run is added, and + // returns either 0, 1, or 2. A helper function for getPenaltyScore(). + private: int finderPenaltyCountPatterns(const std::array<int,7> &runHistory) const; + + + // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). + private: int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array<int,7> &runHistory) const; + + + // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). + private: void finderPenaltyAddHistory(int currentRunLength, std::array<int,7> &runHistory) const; + + + // Returns true iff the i'th bit of x is set to 1. + private: static bool getBit(long x, int i); + + + /*---- Constants and tables ----*/ + + // The minimum version number supported in the QR Code Model 2 standard. + public: static constexpr int MIN_VERSION = 1; + + // The maximum version number supported in the QR Code Model 2 standard. + public: static constexpr int MAX_VERSION = 40; + + + // For use in getPenaltyScore(), when evaluating which mask is best. + private: static const int PENALTY_N1; + private: static const int PENALTY_N2; + private: static const int PENALTY_N3; + private: static const int PENALTY_N4; + + + private: static const std::int8_t ECC_CODEWORDS_PER_BLOCK[4][41]; + private: static const std::int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41]; + +}; + + + +/*---- Public exception class ----*/ + +/* + * Thrown when the supplied data does not fit any QR Code version. Ways to handle this exception include: + * - Decrease the error correction level if it was greater than Ecc::LOW. + * - If the encodeSegments() function was called with a maxVersion argument, then increase + * it if it was less than QrCode::MAX_VERSION. (This advice does not apply to the other + * factory functions because they search all versions up to QrCode::MAX_VERSION.) + * - Split the text data into better or optimal segments in order to reduce the number of bits required. + * - Change the text or binary data to be shorter. + * - Change the text to fit the character set of a particular segment mode (e.g. alphanumeric). + * - Propagate the error upward to the caller/user. + */ +class data_too_long : public std::length_error { + + public: explicit data_too_long(const std::string &msg); + +}; + + + +/* + * An appendable sequence of bits (0s and 1s). Mainly used by QrSegment. + */ +class BitBuffer final : public std::vector<bool> { + + /*---- Constructor ----*/ + + // Creates an empty bit buffer (length 0). + public: BitBuffer(); + + + + /*---- Method ----*/ + + // Appends the given number of low-order bits of the given value + // to this buffer. Requires 0 <= len <= 31 and val < 2^len. + public: void appendBits(std::uint32_t val, int len); + +}; + +} diff --git a/external/supercop b/external/supercop new file mode 160000 +Subproject 7d8b6878260061da56ade6d23dc833288659d0a diff --git a/include/INode.h b/include/INode.h index 0d896ca69..8ad8d03a1 100644 --- a/include/INode.h +++ b/include/INode.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/include/IWallet.h b/include/IWallet.h index 98362895d..74260ce35 100644 --- a/include/IWallet.h +++ b/include/IWallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -1 +0,0 @@ -contrib/snap
\ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d45363e24..07d4d58c3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/blockchain_db/CMakeLists.txt b/src/blockchain_db/CMakeLists.txt index 0a21e4920..77faade17 100644 --- a/src/blockchain_db/CMakeLists.txt +++ b/src/blockchain_db/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index 01faf43c4..5c8dece2a 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -63,6 +63,7 @@ bool matches_category(relay_method method, relay_category category) noexcept { default: case relay_method::local: + case relay_method::forward: case relay_method::stem: return false; case relay_method::block: @@ -79,6 +80,7 @@ void txpool_tx_meta_t::set_relay_method(relay_method method) noexcept kept_by_block = 0; do_not_relay = 0; is_local = 0; + is_forwarding = 0; dandelionpp_stem = 0; switch (method) @@ -89,8 +91,8 @@ void txpool_tx_meta_t::set_relay_method(relay_method method) noexcept case relay_method::local: is_local = 1; break; - default: - case relay_method::fluff: + case relay_method::forward: + is_forwarding = 1; break; case relay_method::stem: dandelionpp_stem = 1; @@ -98,26 +100,45 @@ void txpool_tx_meta_t::set_relay_method(relay_method method) noexcept case relay_method::block: kept_by_block = 1; break; + default: + case relay_method::fluff: + break; } } relay_method txpool_tx_meta_t::get_relay_method() const noexcept { - if (kept_by_block) - return relay_method::block; - if (do_not_relay) - return relay_method::none; - if (is_local) - return relay_method::local; - if (dandelionpp_stem) - return relay_method::stem; + const uint8_t state = + uint8_t(kept_by_block) + + (uint8_t(do_not_relay) << 1) + + (uint8_t(is_local) << 2) + + (uint8_t(is_forwarding) << 3) + + (uint8_t(dandelionpp_stem) << 4); + + switch (state) + { + default: // error case + case 0: + break; + case 1: + return relay_method::block; + case 2: + return relay_method::none; + case 4: + return relay_method::local; + case 8: + return relay_method::forward; + case 16: + return relay_method::stem; + }; return relay_method::fluff; } bool txpool_tx_meta_t::upgrade_relay_method(relay_method method) noexcept { static_assert(relay_method::none < relay_method::local, "bad relay_method value"); - static_assert(relay_method::local < relay_method::stem, "bad relay_method value"); + static_assert(relay_method::local < relay_method::forward, "bad relay_method value"); + static_assert(relay_method::forward < relay_method::stem, "bad relay_method value"); static_assert(relay_method::stem < relay_method::fluff, "bad relay_method value"); static_assert(relay_method::fluff < relay_method::block, "bad relay_method value"); diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 3e2387da4..9a321437b 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -160,7 +160,7 @@ struct txpool_tx_meta_t uint64_t max_used_block_height; uint64_t last_failed_height; uint64_t receive_time; - uint64_t last_relayed_time; //!< If Dandelion++ stem, randomized embargo timestamp. Otherwise, last relayed timestmap. + uint64_t last_relayed_time; //!< If received over i2p/tor, randomized forward time. If Dandelion++stem, randomized embargo time. Otherwise, last relayed timestamp // 112 bytes uint8_t kept_by_block; uint8_t relayed; @@ -169,7 +169,8 @@ struct txpool_tx_meta_t uint8_t pruned: 1; uint8_t is_local: 1; uint8_t dandelionpp_stem : 1; - uint8_t bf_padding: 4; + uint8_t is_forwarding: 1; + uint8_t bf_padding: 3; uint8_t padding[76]; // till 192 bytes @@ -1039,6 +1040,16 @@ public: virtual difficulty_type get_block_difficulty(const uint64_t& height) const = 0; /** + * @brief correct blocks cumulative difficulties that were incorrectly calculated due to the 'difficulty drift' bug + * + * If the block does not exist, the subclass should throw BLOCK_DNE + * + * @param start_height the height where the drift starts + * @param new_cumulative_difficulties new cumulative difficulties to be stored + */ + virtual void correct_block_cumulative_difficulties(const uint64_t& start_height, const std::vector<difficulty_type>& new_cumulative_difficulties) = 0; + + /** * @brief fetch a block's already generated coins * * The subclass should return the total coins generated as of the block diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 5cec8879d..6ea55d09d 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are @@ -25,6 +25,13 @@ // 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 _WIN32 +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <fcntl.h> +#endif + #include "db_lmdb.h" #include <boost/filesystem.hpp> @@ -1287,6 +1294,26 @@ BlockchainLMDB::BlockchainLMDB(bool batch_transactions): BlockchainDB() m_hardfork = nullptr; } +void BlockchainLMDB::check_mmap_support() +{ +#ifndef _WIN32 + const boost::filesystem::path mmap_test_file = m_folder / boost::filesystem::unique_path(); + int mmap_test_fd = ::open(mmap_test_file.string().c_str(), O_RDWR | O_CREAT, 0600); + if (mmap_test_fd < 0) + throw0(DB_ERROR((std::string("Failed to check for mmap support: open failed: ") + strerror(errno)).c_str())); + epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([mmap_test_fd, &mmap_test_file]() { + ::close(mmap_test_fd); + boost::filesystem::remove(mmap_test_file.string()); + }); + if (write(mmap_test_fd, "mmaptest", 8) != 8) + throw0(DB_ERROR((std::string("Failed to check for mmap support: write failed: ") + strerror(errno)).c_str())); + void *mmap_res = mmap(NULL, 8, PROT_READ, MAP_SHARED, mmap_test_fd, 0); + if (mmap_res == MAP_FAILED) + throw0(DB_ERROR("This filesystem does not support mmap: use --data-dir to place the blockchain on a filesystem which does")); + munmap(mmap_res, 8); +#endif +} + void BlockchainLMDB::open(const std::string& filename, const int db_flags) { int result; @@ -1328,6 +1355,8 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) m_folder = filename; + check_mmap_support(); + #ifdef __OpenBSD__ if ((mdb_flags & MDB_WRITEMAP) == 0) { MCLOG_RED(el::Level::Info, "global", "Running on OpenBSD: forcing WRITEMAP"); @@ -2750,6 +2779,44 @@ difficulty_type BlockchainLMDB::get_block_difficulty(const uint64_t& height) con return diff1 - diff2; } +void BlockchainLMDB::correct_block_cumulative_difficulties(const uint64_t& start_height, const std::vector<difficulty_type>& new_cumulative_difficulties) +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + mdb_txn_cursors *m_cursors = &m_wcursors; + + int result = 0; + block_wtxn_start(); + CURSOR(block_info) + + const uint64_t bc_height = height(); + if (start_height + new_cumulative_difficulties.size() != bc_height) + { + block_wtxn_abort(); + throw0(DB_ERROR("Incorrect new_cumulative_difficulties size")); + } + + for (uint64_t height = start_height; height < bc_height; ++height) + { + MDB_val_set(key, height); + result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &key, MDB_GET_BOTH); + if (result) + throw1(BLOCK_DNE(lmdb_error("Failed to get block info: ", result).c_str())); + + mdb_block_info bi = *(mdb_block_info*)key.mv_data; + const difficulty_type d = new_cumulative_difficulties[height - start_height]; + bi.bi_diff_hi = ((d >> 64) & 0xffffffffffffffff).convert_to<uint64_t>(); + bi.bi_diff_lo = (d & 0xffffffffffffffff).convert_to<uint64_t>(); + + MDB_val_set(key2, height); + MDB_val_set(val, bi); + result = mdb_cursor_put(m_cur_block_info, &key2, &val, MDB_CURRENT); + if (result) + throw0(DB_ERROR(lmdb_error("Failed to overwrite block info to db transaction: ", result).c_str())); + } + block_wtxn_stop(); +} + uint64_t BlockchainLMDB::get_block_already_generated_coins(const uint64_t& height) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -4048,7 +4115,7 @@ void BlockchainLMDB::get_output_tx_and_index_from_global(const std::vector<uint6 void BlockchainLMDB::get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial) const { if (amounts.size() != 1 && amounts.size() != offsets.size()) - throw0(DB_ERROR("Invalid sizes of amounts and offets")); + throw0(DB_ERROR("Invalid sizes of amounts and offsets")); LOG_PRINT_L3("BlockchainLMDB::" << __func__); TIME_MEASURE_START(db3); diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 6ddeed671..5abb8014f 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are @@ -229,6 +229,8 @@ public: virtual difficulty_type get_block_difficulty(const uint64_t& height) const; + virtual void correct_block_cumulative_difficulties(const uint64_t& start_height, const std::vector<difficulty_type>& new_cumulative_difficulties); + 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; @@ -356,6 +358,7 @@ public: static int compare_string(const MDB_val *a, const MDB_val *b); private: + void check_mmap_support(); void do_resize(uint64_t size_increase=0); bool need_resize(uint64_t threshold_size=0) const; diff --git a/src/blockchain_db/locked_txn.h b/src/blockchain_db/locked_txn.h index d14631ddb..1834f56fb 100644 --- a/src/blockchain_db/locked_txn.h +++ b/src/blockchain_db/locked_txn.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h index 638dd3b37..92911d081 100644 --- a/src/blockchain_db/testdb.h +++ b/src/blockchain_db/testdb.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -69,8 +69,8 @@ public: virtual cryptonote::blobdata get_block_blob(const crypto::hash& h) const override { return cryptonote::blobdata(); } virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; } virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; } - virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector<cryptonote::blobdata> &bd) const { return false; } - virtual bool get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata>>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const { return false; } + virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector<cryptonote::blobdata> &bd) const override { return false; } + virtual bool get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata>>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const override { return false; } virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; } virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const override { return false; } virtual uint64_t get_block_height(const crypto::hash& h) const override { return 0; } @@ -82,6 +82,7 @@ public: virtual std::vector<uint64_t> get_block_weights(uint64_t start_height, size_t count) const override { return {}; } virtual cryptonote::difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const override { return 10; } virtual cryptonote::difficulty_type get_block_difficulty(const uint64_t& height) const override { return 0; } + virtual void correct_block_cumulative_difficulties(const uint64_t& start_height, const std::vector<difficulty_type>& new_cumulative_difficulties) override {} virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const override { return 10000000000; } virtual uint64_t get_block_long_term_weight(const uint64_t& height) const override { return 128; } virtual std::vector<uint64_t> get_long_term_block_weights(uint64_t start_height, size_t count) const override { return {}; } diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index 0ba7ee86c..ac533596a 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/blockchain_utilities/README.md b/src/blockchain_utilities/README.md index 1462e3186..4a233bcf5 100644 --- a/src/blockchain_utilities/README.md +++ b/src/blockchain_utilities/README.md @@ -1,6 +1,6 @@ # Monero Blockchain Utilities -Copyright (c) 2014-2019, The Monero Project +Copyright (c) 2014-2020, The Monero Project ## Introduction diff --git a/src/blockchain_utilities/blockchain_ancestry.cpp b/src/blockchain_utilities/blockchain_ancestry.cpp index db6d6f7d7..89b932e4f 100644 --- a/src/blockchain_utilities/blockchain_ancestry.cpp +++ b/src/blockchain_utilities/blockchain_ancestry.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 4a5712921..a847f6a9d 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_depth.cpp b/src/blockchain_utilities/blockchain_depth.cpp index 153f5f7c6..6199586bf 100644 --- a/src/blockchain_utilities/blockchain_depth.cpp +++ b/src/blockchain_utilities/blockchain_depth.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_export.cpp b/src/blockchain_utilities/blockchain_export.cpp index b180f88de..e55930b00 100644 --- a/src/blockchain_utilities/blockchain_export.cpp +++ b/src/blockchain_utilities/blockchain_export.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index 852e9cf4f..ab2313096 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp b/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp index cee24d4da..f8763710e 100644 --- a/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp +++ b/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_stats.cpp b/src/blockchain_utilities/blockchain_stats.cpp index 0d18b8819..c9c815f2a 100644 --- a/src/blockchain_utilities/blockchain_stats.cpp +++ b/src/blockchain_utilities/blockchain_stats.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_usage.cpp b/src/blockchain_utilities/blockchain_usage.cpp index 2fa56452b..6e87a0974 100644 --- a/src/blockchain_utilities/blockchain_usage.cpp +++ b/src/blockchain_utilities/blockchain_usage.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_utilities.h b/src/blockchain_utilities/blockchain_utilities.h index e8615cf86..035e2397f 100644 --- a/src/blockchain_utilities/blockchain_utilities.h +++ b/src/blockchain_utilities/blockchain_utilities.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blocksdat_file.cpp b/src/blockchain_utilities/blocksdat_file.cpp index df3c6cafc..b9ef1de7d 100644 --- a/src/blockchain_utilities/blocksdat_file.cpp +++ b/src/blockchain_utilities/blocksdat_file.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blocksdat_file.h b/src/blockchain_utilities/blocksdat_file.h index 1accfbba2..567505ac1 100644 --- a/src/blockchain_utilities/blocksdat_file.h +++ b/src/blockchain_utilities/blocksdat_file.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/bootstrap_file.cpp b/src/blockchain_utilities/bootstrap_file.cpp index 252c79776..a4704626f 100644 --- a/src/blockchain_utilities/bootstrap_file.cpp +++ b/src/blockchain_utilities/bootstrap_file.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/bootstrap_file.h b/src/blockchain_utilities/bootstrap_file.h index db0556175..23dbb64d7 100644 --- a/src/blockchain_utilities/bootstrap_file.h +++ b/src/blockchain_utilities/bootstrap_file.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/bootstrap_serialization.h b/src/blockchain_utilities/bootstrap_serialization.h index 70b3eea7e..bcc8c7e15 100644 --- a/src/blockchain_utilities/bootstrap_serialization.h +++ b/src/blockchain_utilities/bootstrap_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/blocks/CMakeLists.txt b/src/blocks/CMakeLists.txt index 0b7b02fee..051c60886 100644 --- a/src/blocks/CMakeLists.txt +++ b/src/blocks/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat Binary files differindex b14f9e8d2..fa58387ab 100644 --- a/src/blocks/checkpoints.dat +++ b/src/blocks/checkpoints.dat diff --git a/src/checkpoints/CMakeLists.txt b/src/checkpoints/CMakeLists.txt index d324f518e..46d6fedf1 100644 --- a/src/checkpoints/CMakeLists.txt +++ b/src/checkpoints/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index 7214a3056..6e7e1acba 100644 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -72,7 +72,7 @@ namespace cryptonote { } //--------------------------------------------------------------------------- - bool checkpoints::add_checkpoint(uint64_t height, const std::string& hash_str) + bool checkpoints::add_checkpoint(uint64_t height, const std::string& hash_str, const std::string& difficulty_str) { crypto::hash h = crypto::null_hash; bool r = epee::string_tools::hex_to_pod(hash_str, h); @@ -84,6 +84,23 @@ namespace cryptonote CHECK_AND_ASSERT_MES(h == m_points[height], false, "Checkpoint at given height already exists, and hash for new checkpoint was different!"); } m_points[height] = h; + if (!difficulty_str.empty()) + { + try + { + difficulty_type difficulty(difficulty_str); + if (m_difficulty_points.count(height)) + { + CHECK_AND_ASSERT_MES(difficulty == m_difficulty_points[height], false, "Difficulty checkpoint at given height already exists, and difficulty for new checkpoint was different!"); + } + m_difficulty_points[height] = difficulty; + } + catch (...) + { + LOG_ERROR("Failed to parse difficulty checkpoint: " << difficulty_str); + return false; + } + } return true; } //--------------------------------------------------------------------------- @@ -143,6 +160,11 @@ namespace cryptonote { return m_points; } + //--------------------------------------------------------------------------- + const std::map<uint64_t, difficulty_type>& checkpoints::get_difficulty_points() const + { + return m_difficulty_points; + } bool checkpoints::check_for_conflicts(const checkpoints& other) const { @@ -160,56 +182,60 @@ namespace cryptonote { if (nettype == TESTNET) { - ADD_CHECKPOINT(0, "48ca7cd3c8de5b6a4d53d2861fbdaedca141553559f9be9520068053cda8430b"); - ADD_CHECKPOINT(1000000, "46b690b710a07ea051bc4a6b6842ac37be691089c0f7758cfeec4d5fc0b4a258"); - ADD_CHECKPOINT(1058600, "12904f6b4d9e60fd875674e07147d2c83d6716253f046af7b894c3e81da7e1bd"); + ADD_CHECKPOINT2(0, "48ca7cd3c8de5b6a4d53d2861fbdaedca141553559f9be9520068053cda8430b", "0x1"); + ADD_CHECKPOINT2(1000000, "46b690b710a07ea051bc4a6b6842ac37be691089c0f7758cfeec4d5fc0b4a258", "0x7aaad7153"); + ADD_CHECKPOINT2(1058600, "12904f6b4d9e60fd875674e07147d2c83d6716253f046af7b894c3e81da7e1bd", "0x971efd119"); + ADD_CHECKPOINT2(1450000, "87562ca6786f41556b8d5b48067303a57dc5ca77155b35199aedaeca1550f5a0", "0xa639e2930e"); return true; } if (nettype == STAGENET) { - ADD_CHECKPOINT(0, "76ee3cc98646292206cd3e86f74d88b4dcc1d937088645e9b0cbca84b7ce74eb"); - ADD_CHECKPOINT(10000, "1f8b0ce313f8b9ba9a46108bfd285c45ad7c2176871fd41c3a690d4830ce2fd5"); + ADD_CHECKPOINT2(0, "76ee3cc98646292206cd3e86f74d88b4dcc1d937088645e9b0cbca84b7ce74eb", "0x1"); + ADD_CHECKPOINT2(10000, "1f8b0ce313f8b9ba9a46108bfd285c45ad7c2176871fd41c3a690d4830ce2fd5", "0x1d73ba"); + ADD_CHECKPOINT2(550000, "409f68cddd8e74b37469b41c1e61250d81c5776b42264f416d5d27c4626383ed", "0x5f3d4d03e"); return true; } - ADD_CHECKPOINT(1, "771fbcd656ec1464d3a02ead5e18644030007a0fc664c0a964d30922821a8148"); - ADD_CHECKPOINT(10, "c0e3b387e47042f72d8ccdca88071ff96bff1ac7cde09ae113dbb7ad3fe92381"); - ADD_CHECKPOINT(100, "ac3e11ca545e57c49fca2b4e8c48c03c23be047c43e471e1394528b1f9f80b2d"); - ADD_CHECKPOINT(1000, "5acfc45acffd2b2e7345caf42fa02308c5793f15ec33946e969e829f40b03876"); - ADD_CHECKPOINT(10000, "c758b7c81f928be3295d45e230646de8b852ec96a821eac3fea4daf3fcac0ca2"); - ADD_CHECKPOINT(22231, "7cb10e29d67e1c069e6e11b17d30b809724255fee2f6868dc14cfc6ed44dfb25"); - ADD_CHECKPOINT(29556, "53c484a8ed91e4da621bb2fa88106dbde426fe90d7ef07b9c1e5127fb6f3a7f6"); - ADD_CHECKPOINT(50000, "0fe8758ab06a8b9cb35b7328fd4f757af530a5d37759f9d3e421023231f7b31c"); - ADD_CHECKPOINT(80000, "a62dcd7b536f22e003ebae8726e9e7276f63d594e264b6f0cd7aab27b66e75e3"); - ADD_CHECKPOINT(202612, "bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698"); - ADD_CHECKPOINT(202613, "e2aa337e78df1f98f462b3b1e560c6b914dec47b610698b7b7d1e3e86b6197c2"); - ADD_CHECKPOINT(202614, "c29e3dc37d8da3e72e506e31a213a58771b24450144305bcba9e70fa4d6ea6fb"); - ADD_CHECKPOINT(205000, "5d3d7a26e6dc7535e34f03def711daa8c263785f73ec1fadef8a45880fde8063"); - ADD_CHECKPOINT(220000, "9613f455933c00e3e33ac315cc6b455ee8aa0c567163836858c2d9caff111553"); - ADD_CHECKPOINT(230300, "bae7a80c46859db355556e3a9204a337ae8f24309926a1312323fdecf1920e61"); - ADD_CHECKPOINT(230700, "93e631240ceac831da1aebfc5dac8f722c430463024763ebafa888796ceaeedf"); - ADD_CHECKPOINT(231350, "b5add137199b820e1ea26640e5c3e121fd85faa86a1e39cf7e6cc097bdeb1131"); - ADD_CHECKPOINT(232150, "955de8e6b6508af2c24f7334f97beeea651d78e9ade3ab18fec3763be3201aa8"); - ADD_CHECKPOINT(249380, "654fb0a81ce3e5caf7e3264a70f447d4bd07586c08fa50f6638cc54da0a52b2d"); - ADD_CHECKPOINT(460000, "75037a7aed3e765db96c75bcf908f59d690a5f3390baebb9edeafd336a1c4831"); - ADD_CHECKPOINT(500000, "2428f0dbe49796be05ed81b347f53e1f7f44aed0abf641446ec2b94cae066b02"); - ADD_CHECKPOINT(600000, "f5828ebf7d7d1cb61762c4dfe3ccf4ecab2e1aad23e8113668d981713b7a54c5"); - ADD_CHECKPOINT(700000, "12be9b3d210b93f574d2526abb9c1ab2a881b479131fd0d4f7dac93875f503cd"); - ADD_CHECKPOINT(825000, "56503f9ad766774b575be3aff73245e9d159be88132c93d1754764f28da2ff60"); - ADD_CHECKPOINT(900000, "d9958d0e7dcf91a5a7b11de225927bf7efc6eb26240315ce12372be902cc1337"); - ADD_CHECKPOINT(913193, "5292d5d56f6ba4de33a58d9a34d263e2cb3c6fee0aed2286fd4ac7f36d53c85f"); - ADD_CHECKPOINT(1000000, "a886ef5149902d8342475fee9bb296341b891ac67c4842f47a833f23c00ed721"); - ADD_CHECKPOINT(1100000, "3fd720c5c8b3072fc1ccda922dec1ef25f9ed88a1e6ad4103d0fe00b180a5903"); - ADD_CHECKPOINT(1150000, "1dd16f626d18e1e988490dfd06de5920e22629c972c58b4d8daddea0038627b2"); - ADD_CHECKPOINT(1200000, "fa7d13a90850882060479d100141ff84286599ae39c3277c8ea784393f882d1f"); - ADD_CHECKPOINT(1300000, "31b34272343a44a9f4ac7de7a8fcf3b7d8a3124d7d6870affd510d2f37e74cd0"); - ADD_CHECKPOINT(1390000, "a8f5649dd4ded60eedab475f2bec8c934681c07e3cf640e9be0617554f13ff6c"); - ADD_CHECKPOINT(1450000, "ac94e8860093bc7c83e4e91215cba1d663421ecf4067a0ae609c3a8b52bcfac2"); - ADD_CHECKPOINT(1530000, "01759bce497ec38e63c78b1038892169203bb78f87e488172f6b854fcd63ba7e"); - ADD_CHECKPOINT(1579000, "7d0d7a2346373afd41ed1e744a939fc5d474a7dbaa257be5c6fff4009e789241"); - ADD_CHECKPOINT(1668900, "ac2dcaf3d2f58ffcf8391639f0f1ebafcb8eac43c49479c7c37f611868d07568"); - ADD_CHECKPOINT(1775600, "1c6e01c661dc22cab939e79ec6a5272190624ce8356d2f7b958e4f9a57fdb05e"); - ADD_CHECKPOINT(1856000, "9b57f17f29c71a3acd8a7904b93c41fa6eb8d2b7c73936ce4f1702d14880ba29"); - ADD_CHECKPOINT(1958000, "98a5d6e51afdf3146e0eefb10a66e8648d8d4d5c2742be8835e976ba217c9bb2"); + ADD_CHECKPOINT2(1, "771fbcd656ec1464d3a02ead5e18644030007a0fc664c0a964d30922821a8148", "0x2"); + ADD_CHECKPOINT2(10, "c0e3b387e47042f72d8ccdca88071ff96bff1ac7cde09ae113dbb7ad3fe92381", "0x2a974"); + ADD_CHECKPOINT2(100, "ac3e11ca545e57c49fca2b4e8c48c03c23be047c43e471e1394528b1f9f80b2d", "0x35d14b"); + ADD_CHECKPOINT2(1000, "5acfc45acffd2b2e7345caf42fa02308c5793f15ec33946e969e829f40b03876", "0x36a0373"); + ADD_CHECKPOINT2(10000, "c758b7c81f928be3295d45e230646de8b852ec96a821eac3fea4daf3fcac0ca2", "0x60a91390"); + ADD_CHECKPOINT2(22231, "7cb10e29d67e1c069e6e11b17d30b809724255fee2f6868dc14cfc6ed44dfb25", "0x1e288793d"); + ADD_CHECKPOINT2(29556, "53c484a8ed91e4da621bb2fa88106dbde426fe90d7ef07b9c1e5127fb6f3a7f6", "0x71f64cce8"); + ADD_CHECKPOINT2(50000, "0fe8758ab06a8b9cb35b7328fd4f757af530a5d37759f9d3e421023231f7b31c", "0x893044b400"); + ADD_CHECKPOINT2(80000, "a62dcd7b536f22e003ebae8726e9e7276f63d594e264b6f0cd7aab27b66e75e3", "0x5cc113f1076"); + ADD_CHECKPOINT2(202612, "bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698", "0x73310a259eb2"); + ADD_CHECKPOINT2(202613, "e2aa337e78df1f98f462b3b1e560c6b914dec47b610698b7b7d1e3e86b6197c2", "0x733154039b97"); + ADD_CHECKPOINT2(202614, "c29e3dc37d8da3e72e506e31a213a58771b24450144305bcba9e70fa4d6ea6fb", "0x73319dc90cb6"); + ADD_CHECKPOINT2(205000, "5d3d7a26e6dc7535e34f03def711daa8c263785f73ec1fadef8a45880fde8063", "0x75fcc3d85123"); + ADD_CHECKPOINT2(220000, "9613f455933c00e3e33ac315cc6b455ee8aa0c567163836858c2d9caff111553", "0x89cfed0cae3c"); + ADD_CHECKPOINT2(230300, "bae7a80c46859db355556e3a9204a337ae8f24309926a1312323fdecf1920e61", "0x967d13e5baa9"); + ADD_CHECKPOINT2(230700, "93e631240ceac831da1aebfc5dac8f722c430463024763ebafa888796ceaeedf", "0x96fb9663ebe7"); + ADD_CHECKPOINT2(231350, "b5add137199b820e1ea26640e5c3e121fd85faa86a1e39cf7e6cc097bdeb1131", "0x97b9919177bf"); + ADD_CHECKPOINT2(232150, "955de8e6b6508af2c24f7334f97beeea651d78e9ade3ab18fec3763be3201aa8", "0x98a038b612e8"); + ADD_CHECKPOINT2(249380, "654fb0a81ce3e5caf7e3264a70f447d4bd07586c08fa50f6638cc54da0a52b2d", "0xac9739634e6d"); + ADD_CHECKPOINT2(460000, "75037a7aed3e765db96c75bcf908f59d690a5f3390baebb9edeafd336a1c4831", "0x167799549bdda"); + ADD_CHECKPOINT2(500000, "2428f0dbe49796be05ed81b347f53e1f7f44aed0abf641446ec2b94cae066b02", "0x188ce145e4ba9"); + ADD_CHECKPOINT2(600000, "f5828ebf7d7d1cb61762c4dfe3ccf4ecab2e1aad23e8113668d981713b7a54c5", "0x1d9f3759e1554"); + ADD_CHECKPOINT2(700000, "12be9b3d210b93f574d2526abb9c1ab2a881b479131fd0d4f7dac93875f503cd", "0x2201e4ee39c2c"); + ADD_CHECKPOINT2(825000, "56503f9ad766774b575be3aff73245e9d159be88132c93d1754764f28da2ff60", "0x27565a442d5df"); + ADD_CHECKPOINT2(900000, "d9958d0e7dcf91a5a7b11de225927bf7efc6eb26240315ce12372be902cc1337", "0x2a6334031546e"); + ADD_CHECKPOINT2(913193, "5292d5d56f6ba4de33a58d9a34d263e2cb3c6fee0aed2286fd4ac7f36d53c85f", "0x2aefe7f40f5ea"); + ADD_CHECKPOINT2(1000000, "a886ef5149902d8342475fee9bb296341b891ac67c4842f47a833f23c00ed721", "0x2edd71370f0e5"); + ADD_CHECKPOINT2(1100000, "3fd720c5c8b3072fc1ccda922dec1ef25f9ed88a1e6ad4103d0fe00b180a5903", "0x390eb0035c53a"); + ADD_CHECKPOINT2(1150000, "1dd16f626d18e1e988490dfd06de5920e22629c972c58b4d8daddea0038627b2", "0x422d5662e9e37"); + ADD_CHECKPOINT2(1200000, "fa7d13a90850882060479d100141ff84286599ae39c3277c8ea784393f882d1f", "0x4c73503fc4aa3"); + ADD_CHECKPOINT2(1300000, "31b34272343a44a9f4ac7de7a8fcf3b7d8a3124d7d6870affd510d2f37e74cd0", "0x723f49bc249d5"); + ADD_CHECKPOINT2(1390000, "a8f5649dd4ded60eedab475f2bec8c934681c07e3cf640e9be0617554f13ff6c", "0xb4bba65e2841b"); + ADD_CHECKPOINT2(1450000, "ac94e8860093bc7c83e4e91215cba1d663421ecf4067a0ae609c3a8b52bcfac2", "0x11a4aabdca9511"); + ADD_CHECKPOINT2(1530000, "01759bce497ec38e63c78b1038892169203bb78f87e488172f6b854fcd63ba7e", "0x2819ce9f9e91e5"); + ADD_CHECKPOINT2(1579000, "7d0d7a2346373afd41ed1e744a939fc5d474a7dbaa257be5c6fff4009e789241", "0x357a590e7dda83"); + ADD_CHECKPOINT2(1668900, "ac2dcaf3d2f58ffcf8391639f0f1ebafcb8eac43c49479c7c37f611868d07568", "0x474226e475cc3b"); + ADD_CHECKPOINT2(1775600, "1c6e01c661dc22cab939e79ec6a5272190624ce8356d2f7b958e4f9a57fdb05e", "0x5e3b9d206a27c6"); + ADD_CHECKPOINT2(1856000, "9b57f17f29c71a3acd8a7904b93c41fa6eb8d2b7c73936ce4f1702d14880ba29", "0x6bde5e1caccee1"); + ADD_CHECKPOINT2(1958000, "98a5d6e51afdf3146e0eefb10a66e8648d8d4d5c2742be8835e976ba217c9bb2", "0x79dd46d2a0971a"); + ADD_CHECKPOINT2(2046000, "5e867f0b8baefed9244a681df97fc885d8ab36c3dfcd24c7a3abf3b8ac8b8314", "0x9cb8b6ff2978c6"); + ADD_CHECKPOINT2(2092500, "c4e00820c9c7989b49153d5e90ae095a18a11d990e82fcc3be54e6ed785472b5", "0xb4e585a31369cb"); return true; } diff --git a/src/checkpoints/checkpoints.h b/src/checkpoints/checkpoints.h index a55b94bf0..029c50548 100644 --- a/src/checkpoints/checkpoints.h +++ b/src/checkpoints/checkpoints.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -33,8 +33,10 @@ #include "misc_log_ex.h" #include "crypto/hash.h" #include "cryptonote_config.h" +#include "cryptonote_basic/difficulty.h" #define ADD_CHECKPOINT(h, hash) CHECK_AND_ASSERT(add_checkpoint(h, hash), false); +#define ADD_CHECKPOINT2(h, hash, difficulty) CHECK_AND_ASSERT(add_checkpoint(h, hash, difficulty), false); #define JSON_HASH_FILE_NAME "checkpoints.json" @@ -61,12 +63,13 @@ namespace cryptonote * * @param height the height of the block the checkpoint is for * @param hash_str the hash of the block, as a string + * @param difficulty_str the cumulative difficulty of the block, as a string (optional) * * @return false if parsing the hash fails, or if the height is a duplicate * AND the existing checkpoint hash does not match the new one, * otherwise returns true */ - bool add_checkpoint(uint64_t height, const std::string& hash_str); + bool add_checkpoint(uint64_t height, const std::string& hash_str, const std::string& difficulty_str = ""); /** * @brief checks if there is a checkpoint in the future @@ -134,6 +137,13 @@ namespace cryptonote const std::map<uint64_t, crypto::hash>& get_points() const; /** + * @brief gets the difficulty checkpoints container + * + * @return a const reference to the difficulty checkpoints container + */ + const std::map<uint64_t, difficulty_type>& get_difficulty_points() const; + + /** * @brief checks if our checkpoints container conflicts with another * * A conflict refers to a case where both checkpoint sets have a checkpoint @@ -187,6 +197,7 @@ namespace cryptonote private: std::map<uint64_t, crypto::hash> m_points; //!< the checkpoints container + std::map<uint64_t, difficulty_type> m_difficulty_points; //!< the difficulty checkpoints container }; } diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index f06737b31..8e427b6b8 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # @@ -86,7 +86,8 @@ set(common_private_headers updates.h aligned.h timings.h - combinator.h) + combinator.h + utf8.h) monero_private_headers(common ${common_private_headers}) diff --git a/src/common/aligned.c b/src/common/aligned.c index 6982409f7..e3a607f66 100644 --- a/src/common/aligned.c +++ b/src/common/aligned.c @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/apply_permutation.h b/src/common/apply_permutation.h index a4b2c8b78..03effc50e 100644 --- a/src/common/apply_permutation.h +++ b/src/common/apply_permutation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/base58.cpp b/src/common/base58.cpp index ac1bf4b29..77b6a642c 100644 --- a/src/common/base58.cpp +++ b/src/common/base58.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/base58.h b/src/common/base58.h index 6bf2c3bb7..4a7b02cbc 100644 --- a/src/common/base58.h +++ b/src/common/base58.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/boost_serialization_helper.h b/src/common/boost_serialization_helper.h index 2280f3312..4f9cf0518 100644 --- a/src/common/boost_serialization_helper.h +++ b/src/common/boost_serialization_helper.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp index cae744ea5..42370f543 100644 --- a/src/common/command_line.cpp +++ b/src/common/command_line.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/command_line.h b/src/common/command_line.h index b5e3d94a7..b4619905b 100644 --- a/src/common/command_line.h +++ b/src/common/command_line.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/common_fwd.h b/src/common/common_fwd.h index 7eaa6cdee..ac8894ba4 100644 --- a/src/common/common_fwd.h +++ b/src/common/common_fwd.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index dc1f335a7..c871177b1 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/dns_utils.h b/src/common/dns_utils.h index a6bc7463a..30c4cced2 100644 --- a/src/common/dns_utils.h +++ b/src/common/dns_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/download.cpp b/src/common/download.cpp index 2b6a3f9d3..3dd9c3976 100644 --- a/src/common/download.cpp +++ b/src/common/download.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/download.h b/src/common/download.h index f8656a59c..19a52a786 100644 --- a/src/common/download.h +++ b/src/common/download.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/http_connection.h b/src/common/http_connection.h index 6b4294802..5ee39cdbb 100644 --- a/src/common/http_connection.h +++ b/src/common/http_connection.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/i18n.cpp b/src/common/i18n.cpp index 9ac347263..ebc367b3a 100644 --- a/src/common/i18n.cpp +++ b/src/common/i18n.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/i18n.h b/src/common/i18n.h index 82a07410d..3bf2f8260 100644 --- a/src/common/i18n.h +++ b/src/common/i18n.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/json_util.h b/src/common/json_util.h index 96f4b90e6..38464f7f1 100644 --- a/src/common/json_util.h +++ b/src/common/json_util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/notify.cpp b/src/common/notify.cpp index e2df5096d..f31100214 100644 --- a/src/common/notify.cpp +++ b/src/common/notify.cpp @@ -62,7 +62,7 @@ static void replace(std::vector<std::string> &v, const char *tag, const char *s) boost::replace_all(str, tag, s); } -int Notify::notify(const char *tag, const char *s, ...) +int Notify::notify(const char *tag, const char *s, ...) const { std::vector<std::string> margs = args; diff --git a/src/common/notify.h b/src/common/notify.h index f813e8def..65d4e1072 100644 --- a/src/common/notify.h +++ b/src/common/notify.h @@ -38,8 +38,12 @@ class Notify { public: Notify(const char *spec); + Notify(const Notify&) = default; + Notify(Notify&&) = default; + Notify& operator=(const Notify&) = default; + Notify& operator=(Notify&&) = default; - int notify(const char *tag, const char *s, ...); + int notify(const char *tag, const char *s, ...) const; private: std::string filename; @@ -47,3 +51,4 @@ private: }; } + diff --git a/src/common/password.cpp b/src/common/password.cpp index 33e1f48fd..019f04c95 100644 --- a/src/common/password.cpp +++ b/src/common/password.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/password.h b/src/common/password.h index 2837c70f3..54d97e565 100644 --- a/src/common/password.h +++ b/src/common/password.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/perf_timer.cpp b/src/common/perf_timer.cpp index 4408170d1..898701b2f 100644 --- a/src/common/perf_timer.cpp +++ b/src/common/perf_timer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/perf_timer.h b/src/common/perf_timer.h index 29a37e655..fb89b78c0 100644 --- a/src/common/perf_timer.h +++ b/src/common/perf_timer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/pod-class.h b/src/common/pod-class.h index 200647590..38eae8948 100644 --- a/src/common/pod-class.h +++ b/src/common/pod-class.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/rpc_client.h b/src/common/rpc_client.h index dab3e562d..26bcea751 100644 --- a/src/common/rpc_client.h +++ b/src/common/rpc_client.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/scoped_message_writer.h b/src/common/scoped_message_writer.h index 546377392..0a5ebe1c9 100644 --- a/src/common/scoped_message_writer.h +++ b/src/common/scoped_message_writer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/sfinae_helpers.h b/src/common/sfinae_helpers.h index e9a98bb63..e0778b10f 100644 --- a/src/common/sfinae_helpers.h +++ b/src/common/sfinae_helpers.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/stack_trace.cpp b/src/common/stack_trace.cpp index 8d4f8c6f1..828b8f1c8 100644 --- a/src/common/stack_trace.cpp +++ b/src/common/stack_trace.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/stack_trace.h b/src/common/stack_trace.h index ae6573885..57f939b2d 100644 --- a/src/common/stack_trace.h +++ b/src/common/stack_trace.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp index 753bf238c..a1737778c 100644 --- a/src/common/threadpool.cpp +++ b/src/common/threadpool.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/threadpool.h b/src/common/threadpool.h index a49d0e14f..91f9fdf47 100644 --- a/src/common/threadpool.h +++ b/src/common/threadpool.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/unordered_containers_boost_serialization.h b/src/common/unordered_containers_boost_serialization.h index 74e2c3f81..67bde676c 100644 --- a/src/common/unordered_containers_boost_serialization.h +++ b/src/common/unordered_containers_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/updates.cpp b/src/common/updates.cpp index f620bb53a..c51c4320f 100644 --- a/src/common/updates.cpp +++ b/src/common/updates.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/updates.h b/src/common/updates.h index 8fda6d207..0bfe26c3f 100644 --- a/src/common/updates.h +++ b/src/common/updates.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/utf8.h b/src/common/utf8.h new file mode 100644 index 000000000..60247f1b2 --- /dev/null +++ b/src/common/utf8.h @@ -0,0 +1,114 @@ +// 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 <cctype> +#include <cwchar> +#include <stdexcept> + +namespace tools +{ + template<typename T, typename Transform> + inline T utf8canonical(const T &s, Transform t = [](wint_t c)->wint_t { return c; }) + { + T sc = ""; + size_t avail = s.size(); + const char *ptr = s.data(); + wint_t cp = 0; + int bytes = 1; + char wbuf[8], *wptr; + while (avail--) + { + if ((*ptr & 0x80) == 0) + { + cp = *ptr++; + bytes = 1; + } + else if ((*ptr & 0xe0) == 0xc0) + { + if (avail < 1) + throw std::runtime_error("Invalid UTF-8"); + cp = (*ptr++ & 0x1f) << 6; + cp |= *ptr++ & 0x3f; + --avail; + bytes = 2; + } + else if ((*ptr & 0xf0) == 0xe0) + { + if (avail < 2) + throw std::runtime_error("Invalid UTF-8"); + cp = (*ptr++ & 0xf) << 12; + cp |= (*ptr++ & 0x3f) << 6; + cp |= *ptr++ & 0x3f; + avail -= 2; + bytes = 3; + } + else if ((*ptr & 0xf8) == 0xf0) + { + if (avail < 3) + throw std::runtime_error("Invalid UTF-8"); + cp = (*ptr++ & 0x7) << 18; + cp |= (*ptr++ & 0x3f) << 12; + cp |= (*ptr++ & 0x3f) << 6; + cp |= *ptr++ & 0x3f; + avail -= 3; + bytes = 4; + } + else + throw std::runtime_error("Invalid UTF-8"); + + cp = t(cp); + if (cp <= 0x7f) + bytes = 1; + else if (cp <= 0x7ff) + bytes = 2; + else if (cp <= 0xffff) + bytes = 3; + else if (cp <= 0x10ffff) + bytes = 4; + else + throw std::runtime_error("Invalid code point UTF-8 transformation"); + + wptr = wbuf; + switch (bytes) + { + case 1: *wptr++ = cp; break; + case 2: *wptr++ = 0xc0 | (cp >> 6); *wptr++ = 0x80 | (cp & 0x3f); break; + case 3: *wptr++ = 0xe0 | (cp >> 12); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break; + case 4: *wptr++ = 0xf0 | (cp >> 18); *wptr++ = 0x80 | ((cp >> 12) & 0x3f); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break; + default: throw std::runtime_error("Invalid UTF-8"); + } + *wptr = 0; + sc.append(wbuf, bytes); + cp = 0; + bytes = 1; + } + return sc; + } +} diff --git a/src/common/util.cpp b/src/common/util.cpp index f1140d1d5..138ac4294 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/util.h b/src/common/util.h index 25137ab64..0c3409ea1 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/common/varint.h b/src/common/varint.h index a0d79be28..133622a67 100644 --- a/src/common/varint.h +++ b/src/common/varint.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt index 80f1e5d7e..3b33fe90a 100644 --- a/src/crypto/CMakeLists.txt +++ b/src/crypto/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # @@ -116,3 +116,6 @@ endif() # cheat because cmake and ccache hate each other set_property(SOURCE CryptonightR_template.S PROPERTY LANGUAGE C) + +# Must be done last, because it references libraries in this directory +add_subdirectory(wallet) diff --git a/src/crypto/blake256.c b/src/crypto/blake256.c index bb2c5fb40..831a90302 100644 --- a/src/crypto/blake256.c +++ b/src/crypto/blake256.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/blake256.h b/src/crypto/blake256.h index 309dbf3ec..5abdb79b9 100644 --- a/src/crypto/blake256.h +++ b/src/crypto/blake256.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/c_threads.h b/src/crypto/c_threads.h index 56d680ff8..5b2fcddd3 100644 --- a/src/crypto/c_threads.h +++ b/src/crypto/c_threads.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/chacha.h b/src/crypto/chacha.h index a39823e5a..a1158be7e 100644 --- a/src/crypto/chacha.h +++ b/src/crypto/chacha.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto-ops-data.c b/src/crypto/crypto-ops-data.c index c9530bb2a..d16fd9429 100644 --- a/src/crypto/crypto-ops-data.c +++ b/src/crypto/crypto-ops-data.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c index 5a3d994a6..3110d3ce7 100644 --- a/src/crypto/crypto-ops.c +++ b/src/crypto/crypto-ops.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto-ops.h b/src/crypto/crypto-ops.h index 7137437bc..eeb94669b 100644 --- a/src/crypto/crypto-ops.h +++ b/src/crypto/crypto-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index 8a03f28bb..4cfe83d54 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -43,6 +43,8 @@ #include "crypto.h" #include "hash.h" +#include "cryptonote_config.h" + namespace { static void local_abort(const char *msg) { @@ -261,11 +263,24 @@ namespace crypto { ec_point comm; }; + // Used in v1 tx proofs + struct s_comm_2_v1 { + hash msg; + ec_point D; + ec_point X; + ec_point Y; + }; + + // Used in v1/v2 tx proofs struct s_comm_2 { hash msg; ec_point D; ec_point X; ec_point Y; + hash sep; // domain separation + ec_point R; + ec_point A; + ec_point B; }; void crypto_ops::generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig) { @@ -321,6 +336,86 @@ namespace crypto { return sc_isnonzero(&c) == 0; } + // Generate a proof of knowledge of `r` such that (`R = rG` and `D = rA`) or (`R = rB` and `D = rA`) via a Schnorr proof + // This handles use cases for both standard addresses and subaddresses + // + // NOTE: This generates old v1 proofs, and is for TESTING ONLY + void crypto_ops::generate_tx_proof_v1(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, signature &sig) { + // sanity check + ge_p3 R_p3; + ge_p3 A_p3; + ge_p3 B_p3; + ge_p3 D_p3; + if (ge_frombytes_vartime(&R_p3, &R) != 0) throw std::runtime_error("tx pubkey is invalid"); + if (ge_frombytes_vartime(&A_p3, &A) != 0) throw std::runtime_error("recipient view pubkey is invalid"); + if (B && ge_frombytes_vartime(&B_p3, &*B) != 0) throw std::runtime_error("recipient spend pubkey is invalid"); + if (ge_frombytes_vartime(&D_p3, &D) != 0) throw std::runtime_error("key derivation is invalid"); +#if !defined(NDEBUG) + { + assert(sc_check(&r) == 0); + // check R == r*G or R == r*B + public_key dbg_R; + if (B) + { + ge_p2 dbg_R_p2; + ge_scalarmult(&dbg_R_p2, &r, &B_p3); + ge_tobytes(&dbg_R, &dbg_R_p2); + } + else + { + ge_p3 dbg_R_p3; + ge_scalarmult_base(&dbg_R_p3, &r); + ge_p3_tobytes(&dbg_R, &dbg_R_p3); + } + assert(R == dbg_R); + // check D == r*A + ge_p2 dbg_D_p2; + ge_scalarmult(&dbg_D_p2, &r, &A_p3); + public_key dbg_D; + ge_tobytes(&dbg_D, &dbg_D_p2); + assert(D == dbg_D); + } +#endif + + // pick random k + ec_scalar k; + random_scalar(k); + + s_comm_2_v1 buf; + buf.msg = prefix_hash; + buf.D = D; + + if (B) + { + // compute X = k*B + ge_p2 X_p2; + ge_scalarmult(&X_p2, &k, &B_p3); + ge_tobytes(&buf.X, &X_p2); + } + else + { + // compute X = k*G + ge_p3 X_p3; + ge_scalarmult_base(&X_p3, &k); + ge_p3_tobytes(&buf.X, &X_p3); + } + + // compute Y = k*A + ge_p2 Y_p2; + ge_scalarmult(&Y_p2, &k, &A_p3); + ge_tobytes(&buf.Y, &Y_p2); + + // sig.c = Hs(Msg || D || X || Y) + hash_to_scalar(&buf, sizeof(buf), sig.c); + + // sig.r = k - sig.c*r + sc_mulsub(&sig.r, &sig.c, &unwrap(r), &k); + } + + // Generate a proof of knowledge of `r` such that (`R = rG` and `D = rA`) or (`R = rB` and `D = rA`) via a Schnorr proof + // This handles use cases for both standard addresses and subaddresses + // + // Generates only proofs for InProofV2 and OutProofV2 void crypto_ops::generate_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, signature &sig) { // sanity check ge_p3 R_p3; @@ -362,10 +457,20 @@ namespace crypto { ec_scalar k; random_scalar(k); + // if B is not present + static const ec_point zero = {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}; + s_comm_2 buf; buf.msg = prefix_hash; buf.D = D; - + buf.R = R; + buf.A = A; + if (B) + buf.B = *B; + else + buf.B = zero; + cn_fast_hash(config::HASH_KEY_TXPROOF_V2, sizeof(config::HASH_KEY_TXPROOF_V2)-1, buf.sep); + if (B) { // compute X = k*B @@ -386,7 +491,7 @@ namespace crypto { ge_scalarmult(&Y_p2, &k, &A_p3); ge_tobytes(&buf.Y, &Y_p2); - // sig.c = Hs(Msg || D || X || Y) + // sig.c = Hs(Msg || D || X || Y || sep || R || A || B) hash_to_scalar(&buf, sizeof(buf), sig.c); // sig.r = k - sig.c*r @@ -395,7 +500,8 @@ namespace crypto { memwipe(&k, sizeof(k)); } - bool crypto_ops::check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) { + // Verify a proof: either v1 (version == 1) or v2 (version == 2) + bool crypto_ops::check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig, const int version) { // sanity check ge_p3 R_p3; ge_p3 A_p3; @@ -467,14 +573,31 @@ namespace crypto { ge_p2 Y_p2; ge_p1p1_to_p2(&Y_p2, &Y_p1p1); - // compute c2 = Hs(Msg || D || X || Y) + // Compute hash challenge + // for v1, c2 = Hs(Msg || D || X || Y) + // for v2, c2 = Hs(Msg || D || X || Y || sep || R || A || B) + + // if B is not present + static const ec_point zero = {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}; + s_comm_2 buf; buf.msg = prefix_hash; buf.D = D; + buf.R = R; + buf.A = A; + if (B) + buf.B = *B; + else + buf.B = zero; + cn_fast_hash(config::HASH_KEY_TXPROOF_V2, sizeof(config::HASH_KEY_TXPROOF_V2)-1, buf.sep); ge_tobytes(&buf.X, &X_p2); ge_tobytes(&buf.Y, &Y_p2); ec_scalar c2; - hash_to_scalar(&buf, sizeof(s_comm_2), c2); + + // Hash depends on version + if (version == 1) hash_to_scalar(&buf, sizeof(s_comm_2) - 3*sizeof(ec_point) - sizeof(hash), c2); + else if (version == 2) hash_to_scalar(&buf, sizeof(s_comm_2), c2); + else return false; // test if c2 == sig.c sc_sub(&c2, &c2, &sig.c); diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 8ce321f71..7ddc0150f 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -132,8 +132,10 @@ namespace crypto { friend bool check_signature(const hash &, const public_key &, const signature &); static void generate_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &); friend void generate_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &); - static bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &); - friend bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &); + static void generate_tx_proof_v1(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &); + friend void generate_tx_proof_v1(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &); + static bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &, const int); + friend bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &, const int); static void generate_key_image(const public_key &, const secret_key &, key_image &); friend void generate_key_image(const public_key &, const secret_key &, key_image &); static void generate_ring_signature(const hash &, const key_image &, @@ -248,8 +250,11 @@ namespace crypto { inline void generate_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, signature &sig) { crypto_ops::generate_tx_proof(prefix_hash, R, A, B, D, r, sig); } - inline bool check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) { - return crypto_ops::check_tx_proof(prefix_hash, R, A, B, D, sig); + inline void generate_tx_proof_v1(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, signature &sig) { + crypto_ops::generate_tx_proof_v1(prefix_hash, R, A, B, D, r, sig); + } + inline bool check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig, const int version) { + return crypto_ops::check_tx_proof(prefix_hash, R, A, B, D, sig, version); } /* To send money to a key: diff --git a/src/crypto/crypto_ops_builder/README.md b/src/crypto/crypto_ops_builder/README.md index 4bb95cc4a..a3acb11e8 100644 --- a/src/crypto/crypto_ops_builder/README.md +++ b/src/crypto/crypto_ops_builder/README.md @@ -1,6 +1,6 @@ # Monero -Copyright (c) 2014-2019, The Monero Project +Copyright (c) 2014-2020, The Monero Project ## Crypto Ops Builder diff --git a/src/crypto/crypto_ops_builder/crypto-ops-data.c b/src/crypto/crypto_ops_builder/crypto-ops-data.c index 45e9923b1..64fd15070 100644 --- a/src/crypto/crypto_ops_builder/crypto-ops-data.c +++ b/src/crypto/crypto_ops_builder/crypto-ops-data.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto_ops_builder/crypto-ops-old.c b/src/crypto/crypto_ops_builder/crypto-ops-old.c index 89c2ced6e..da85ee534 100644 --- a/src/crypto/crypto_ops_builder/crypto-ops-old.c +++ b/src/crypto/crypto_ops_builder/crypto-ops-old.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto_ops_builder/crypto-ops.h b/src/crypto/crypto_ops_builder/crypto-ops.h index b4fcfca9c..d719743c4 100644 --- a/src/crypto/crypto_ops_builder/crypto-ops.h +++ b/src/crypto/crypto_ops_builder/crypto-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py b/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py index dfba583f7..08cead175 100644 --- a/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py +++ b/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py @@ -15,7 +15,7 @@ print("maybe someone smart can replace the sed with perl..") a = "" license = textwrap.dedent("""\ - // Copyright (c) 2014-2019, The Monero Project + // Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h b/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h index f62ff441d..613b718f5 100644 --- a/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h +++ b/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/generic-ops.h b/src/crypto/generic-ops.h index d06726638..9aa7b065a 100644 --- a/src/crypto/generic-ops.h +++ b/src/crypto/generic-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/groestl.h b/src/crypto/groestl.h index 6628947dd..7483db9b6 100644 --- a/src/crypto/groestl.h +++ b/src/crypto/groestl.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/groestl_tables.h b/src/crypto/groestl_tables.h index ca0c4fca6..7bf03afd7 100644 --- a/src/crypto/groestl_tables.h +++ b/src/crypto/groestl_tables.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-extra-blake.c b/src/crypto/hash-extra-blake.c index 9bada96f3..4cc915cdd 100644 --- a/src/crypto/hash-extra-blake.c +++ b/src/crypto/hash-extra-blake.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-extra-groestl.c b/src/crypto/hash-extra-groestl.c index 57866bf9d..dec21310d 100644 --- a/src/crypto/hash-extra-groestl.c +++ b/src/crypto/hash-extra-groestl.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-extra-jh.c b/src/crypto/hash-extra-jh.c index 0dbac4fb5..0604003bd 100644 --- a/src/crypto/hash-extra-jh.c +++ b/src/crypto/hash-extra-jh.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-extra-skein.c b/src/crypto/hash-extra-skein.c index 78f48609f..55bd4ddec 100644 --- a/src/crypto/hash-extra-skein.c +++ b/src/crypto/hash-extra-skein.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index d117bb640..7dfc5151d 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash.c b/src/crypto/hash.c index b66f3b010..a917115fe 100644 --- a/src/crypto/hash.c +++ b/src/crypto/hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash.h b/src/crypto/hash.h index 27184fa53..4b99bebaa 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/initializer.h b/src/crypto/initializer.h index 75d80f054..e9a7d97e2 100644 --- a/src/crypto/initializer.h +++ b/src/crypto/initializer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c index 851c70a25..72d472d8a 100644 --- a/src/crypto/keccak.c +++ b/src/crypto/keccak.c @@ -146,7 +146,6 @@ void keccak1600(const uint8_t *in, size_t inlen, uint8_t *md) #define KECCAK_BLOCKLEN 136 #define KECCAK_WORDS 17 #define KECCAK_DIGESTSIZE 32 -#define IS_ALIGNED_64(p) (0 == (7 & ((const char*)(p) - (const char*)0))) #define KECCAK_PROCESS_BLOCK(st, block) { \ for (int i_ = 0; i_ < KECCAK_WORDS; i_++){ \ ((st))[i_] ^= swap64le(((block))[i_]); \ @@ -178,17 +177,10 @@ void keccak_update(KECCAK_CTX * ctx, const uint8_t *in, size_t inlen){ inlen -= left; } - const bool is_aligned = IS_ALIGNED_64(in); while (inlen >= KECCAK_BLOCKLEN) { - const uint64_t* aligned_message_block; - if (is_aligned) { - aligned_message_block = (uint64_t*)in; - } else { - memcpy(ctx->message, in, KECCAK_BLOCKLEN); - aligned_message_block = ctx->message; - } + memcpy(ctx->message, in, KECCAK_BLOCKLEN); - KECCAK_PROCESS_BLOCK(ctx->hash, aligned_message_block); + KECCAK_PROCESS_BLOCK(ctx->hash, ctx->message); in += KECCAK_BLOCKLEN; inlen -= KECCAK_BLOCKLEN; } diff --git a/src/crypto/random.c b/src/crypto/random.c index 766b5f558..1e3d9beff 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/random.h b/src/crypto/random.h index 21a66d776..8b81e7a66 100644 --- a/src/crypto/random.h +++ b/src/crypto/random.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/rx-slow-hash.c b/src/crypto/rx-slow-hash.c index 606c67336..1d7f09cab 100644 --- a/src/crypto/rx-slow-hash.c +++ b/src/crypto/rx-slow-hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/skein_port.h b/src/crypto/skein_port.h index 1ec07a4d1..94fd44035 100644 --- a/src/crypto/skein_port.h +++ b/src/crypto/skein_port.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 88eb751a6..5a773f3cf 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c index 0a5860f3b..643e95121 100644 --- a/src/crypto/tree-hash.c +++ b/src/crypto/tree-hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/crypto/wallet/CMakeLists.txt b/src/crypto/wallet/CMakeLists.txt new file mode 100644 index 000000000..4ed986dce --- /dev/null +++ b/src/crypto/wallet/CMakeLists.txt @@ -0,0 +1,62 @@ +# Copyright (c) 2020, 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. + +# +# Possibly user defined values. +# +set(MONERO_WALLET_CRYPTO_LIBRARY "auto" CACHE STRING "Select a wallet crypto library") + +# +# If the user specified "auto", detect best library defaulting to internal. +# +if (${MONERO_WALLET_CRYPTO_LIBRARY} STREQUAL "auto") + monero_crypto_autodetect(AVAILABLE BEST) + if (DEFINED BEST) + message("Wallet crypto is using ${BEST} backend") + set(MONERO_WALLET_CRYPTO_LIBRARY ${BEST}) + else () + message("Defaulting to internal crypto library for wallet") + set(MONERO_WALLET_CRYPTO_LIBRARY "cn") + endif () +endif () + +# +# Configure library target "wallet-crypto" - clients will use this as a +# library dependency which in turn will depend on the crypto library selected. +# +if (${MONERO_WALLET_CRYPTO_LIBRARY} STREQUAL "cn") + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/empty.h.in ${MONERO_GENERATED_HEADERS_DIR}/crypto/wallet/ops.h) + add_library(wallet-crypto ALIAS cncrypto) +else () + monero_crypto_generate_header(${MONERO_WALLET_CRYPTO_LIBRARY} "${MONERO_GENERATED_HEADERS_DIR}/crypto/wallet/ops.h") + monero_crypto_get_target(${MONERO_WALLET_CRYPTO_LIBRARY} CRYPTO_TARGET) + add_library(wallet-crypto $<TARGET_OBJECTS:${CRYPTO_TARGET}>) + target_link_libraries(wallet-crypto cncrypto) +endif () + + diff --git a/src/crypto/wallet/crypto.h b/src/crypto/wallet/crypto.h new file mode 100644 index 000000000..a4c5d5a07 --- /dev/null +++ b/src/crypto/wallet/crypto.h @@ -0,0 +1,56 @@ +// Copyright (c) 2020, 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 <cstddef> +#include "crypto/wallet/ops.h" + +namespace crypto { + namespace wallet { +// if C functions defined from external/supercop - cmake generates crypto/wallet/ops.h +#if defined(monero_crypto_generate_key_derivation) + inline + bool generate_key_derivation(const public_key &tx_pub, const secret_key &view_sec, key_derivation &out) + { + return monero_crypto_generate_key_derivation(out.data, tx_pub.data, view_sec.data) == 0; + } + + inline + bool derive_subaddress_public_key(const public_key &output_pub, const key_derivation &d, std::size_t index, public_key &out) + { + ec_scalar scalar; + derivation_to_scalar(d, index, scalar); + return monero_crypto_generate_subaddress_public_key(out.data, output_pub.data, scalar.data) == 0; + } +#else + using ::crypto::generate_key_derivation; + using ::crypto::derive_subaddress_public_key; +#endif + } +} diff --git a/src/crypto/wallet/empty.h.in b/src/crypto/wallet/empty.h.in new file mode 100644 index 000000000..ac252e1bd --- /dev/null +++ b/src/crypto/wallet/empty.h.in @@ -0,0 +1,31 @@ +// Copyright (c) 2020, 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 + +// Left empty so internal cryptonote crypto library is used. diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt index 59040d8a2..113fd9d86 100644 --- a/src/cryptonote_basic/CMakeLists.txt +++ b/src/cryptonote_basic/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp index 02eca289e..b366985ab 100644 --- a/src/cryptonote_basic/account.cpp +++ b/src/cryptonote_basic/account.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -61,7 +61,8 @@ DISABLE_VS_WARNINGS(4244 4345) m_device = &hwdev; MCDEBUG("device", "account_keys::set_device device type: "<<typeid(hwdev).name()); } - //----------------------------------------------------------------- + + // Generate a derived chacha key static void derive_key(const crypto::chacha_key &base_key, crypto::chacha_key &key) { static_assert(sizeof(base_key) == sizeof(crypto::hash), "chacha key and hash should be the same size"); @@ -70,25 +71,38 @@ DISABLE_VS_WARNINGS(4244 4345) data[sizeof(base_key)] = config::HASH_KEY_MEMORY; crypto::generate_chacha_key(data.data(), sizeof(data), key, 1); } - //----------------------------------------------------------------- - static epee::wipeable_string get_key_stream(const crypto::chacha_key &base_key, const crypto::chacha_iv &iv, size_t bytes) + + // Prepare IVs and start chacha for encryption + void account_keys::encrypt_wrapper(const crypto::chacha_key &key, const bool all_keys) { - // derive a new key - crypto::chacha_key key; - derive_key(base_key, key); + // Set a fresh IV only for all-key encryption + if (all_keys) + m_encryption_iv = crypto::rand<crypto::chacha_iv>(); - // chacha - epee::wipeable_string buffer0(std::string(bytes, '\0')); - epee::wipeable_string buffer1 = buffer0; - crypto::chacha20(buffer0.data(), buffer0.size(), key, iv, buffer1.data()); - return buffer1; + // Now do the chacha + chacha_wrapper(key, all_keys); } - //----------------------------------------------------------------- - void account_keys::xor_with_key_stream(const crypto::chacha_key &key) + + // Start chacha for decryption + void account_keys::decrypt_wrapper(const crypto::chacha_key &key, const bool all_keys) + { + chacha_wrapper(key, all_keys); + } + + // Decrypt keys using the legacy method + void account_keys::decrypt_legacy(const crypto::chacha_key &key) { - // encrypt a large enough byte stream with chacha20 - epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (2 + m_multisig_keys.size())); - const char *ptr = key_stream.data(); + // Derive domain-separated chacha key + crypto::chacha_key derived_key; + derive_key(key, derived_key); + + // Build key stream + epee::wipeable_string temp(std::string(sizeof(crypto::secret_key)*(2 + m_multisig_keys.size()), '\0')); + epee::wipeable_string stream = temp; + crypto::chacha20(temp.data(), temp.size(), derived_key, m_encryption_iv, stream.data()); + + // Decrypt all keys + const char *ptr = stream.data(); for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) m_spend_secret_key.data[i] ^= *ptr++; for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) @@ -99,33 +113,39 @@ DISABLE_VS_WARNINGS(4244 4345) k.data[i] ^= *ptr++; } } - //----------------------------------------------------------------- - void account_keys::encrypt(const crypto::chacha_key &key) + + // Perform chacha on either the view key or all keys + void account_keys::chacha_wrapper(const crypto::chacha_key &key, const bool all_keys) { - m_encryption_iv = crypto::rand<crypto::chacha_iv>(); - xor_with_key_stream(key); - } - //----------------------------------------------------------------- - void account_keys::decrypt(const crypto::chacha_key &key) - { - xor_with_key_stream(key); - } - //----------------------------------------------------------------- - void account_keys::encrypt_viewkey(const crypto::chacha_key &key) - { - // encrypt a large enough byte stream with chacha20 - epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * 2); - const char *ptr = key_stream.data(); - ptr += sizeof(crypto::secret_key); - for (size_t i = 0; i < sizeof(crypto::secret_key); ++i) - m_view_secret_key.data[i] ^= *ptr++; - } - //----------------------------------------------------------------- - void account_keys::decrypt_viewkey(const crypto::chacha_key &key) - { - encrypt_viewkey(key); + // Derive domain-seprated chacha key + crypto::chacha_key derived_key; + derive_key(key, derived_key); + + // Chacha the specified keys using the appropriate IVs + if (all_keys) + { + // Spend key + crypto::secret_key temp_key; + chacha20((char *) &m_spend_secret_key, sizeof(crypto::secret_key), derived_key, m_encryption_iv, (char *) &temp_key); + memcpy(&m_spend_secret_key, &temp_key, sizeof(crypto::secret_key)); + memwipe(&temp_key, sizeof(crypto::secret_key)); + + // Multisig keys + std::vector<crypto::secret_key> temp_keys; + temp_keys.reserve(m_multisig_keys.size()); + temp_keys.resize(m_multisig_keys.size()); + chacha20((char *) &m_multisig_keys[0], sizeof(crypto::secret_key)*m_multisig_keys.size(), derived_key, m_encryption_iv, (char *) &temp_keys[0]); + memcpy(&m_multisig_keys[0], &temp_keys[0], sizeof(crypto::secret_key)*temp_keys.size()); + memwipe(&temp_keys[0], sizeof(crypto::secret_key)*temp_keys.size()); + } + + // View key + crypto::secret_key temp_key; + chacha20((char *) &m_view_secret_key, sizeof(crypto::secret_key), derived_key, m_encryption_iv, (char *) &temp_key); + memcpy(&m_view_secret_key, &temp_key, sizeof(crypto::secret_key)); + memwipe(&temp_key, sizeof(crypto::secret_key)); } - //----------------------------------------------------------------- + account_base::account_base() { set_null(); diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h index abf751b6e..c71c06edd 100644 --- a/src/cryptonote_basic/account.h +++ b/src/cryptonote_basic/account.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -57,16 +57,15 @@ namespace cryptonote account_keys& operator=(account_keys const&) = default; - void encrypt(const crypto::chacha_key &key); - void decrypt(const crypto::chacha_key &key); - void encrypt_viewkey(const crypto::chacha_key &key); - void decrypt_viewkey(const crypto::chacha_key &key); + void encrypt_wrapper(const crypto::chacha_key &key, const bool all_keys); + void decrypt_wrapper(const crypto::chacha_key &key, const bool all_keys); + void decrypt_legacy(const crypto::chacha_key &key); hw::device& get_device() const ; void set_device( hw::device &hwdev) ; private: - void xor_with_key_stream(const crypto::chacha_key &key); + void chacha_wrapper(const crypto::chacha_key &key, const bool all_keys); }; /************************************************************************/ @@ -100,10 +99,12 @@ namespace cryptonote void forget_spend_key(); const std::vector<crypto::secret_key> &get_multisig_keys() const { return m_keys.m_multisig_keys; } - void encrypt_keys(const crypto::chacha_key &key) { m_keys.encrypt(key); } - void decrypt_keys(const crypto::chacha_key &key) { m_keys.decrypt(key); } - void encrypt_viewkey(const crypto::chacha_key &key) { m_keys.encrypt_viewkey(key); } - void decrypt_viewkey(const crypto::chacha_key &key) { m_keys.decrypt_viewkey(key); } + void encrypt_keys(const crypto::chacha_key &key) { m_keys.encrypt_wrapper(key, true); } + void encrypt_keys_same_iv(const crypto::chacha_key &key) { m_keys.decrypt_wrapper(key, true); } // encryption with the same IV is the same as decryption due to symmetry + void decrypt_keys(const crypto::chacha_key &key) { m_keys.decrypt_wrapper(key, true); } + void encrypt_viewkey(const crypto::chacha_key &key) { m_keys.encrypt_wrapper(key, false); } + void decrypt_viewkey(const crypto::chacha_key &key) { m_keys.decrypt_wrapper(key, false); } + void decrypt_legacy(const crypto::chacha_key &key) { m_keys.decrypt_legacy(key); } template <class t_archive> inline void serialize(t_archive &a, const unsigned int /*ver*/) diff --git a/src/cryptonote_basic/account_boost_serialization.h b/src/cryptonote_basic/account_boost_serialization.h index 320a960dc..d97a5a854 100644 --- a/src/cryptonote_basic/account_boost_serialization.h +++ b/src/cryptonote_basic/account_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/blobdatatype.h b/src/cryptonote_basic/blobdatatype.h index 20f6b2421..6906e0c9d 100644 --- a/src/cryptonote_basic/blobdatatype.h +++ b/src/cryptonote_basic/blobdatatype.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/connection_context.h b/src/cryptonote_basic/connection_context.h index a682bebf2..0c3a94054 100644 --- a/src/cryptonote_basic/connection_context.h +++ b/src/cryptonote_basic/connection_context.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index e2286ae8c..bc6a378f2 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp index 9bafcfc86..541393fa9 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.cpp +++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/cryptonote_basic_impl.h b/src/cryptonote_basic/cryptonote_basic_impl.h index c7198a16f..1303670d2 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.h +++ b/src/cryptonote_basic/cryptonote_basic_impl.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index 79ce610a9..6f89cc7ae 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index cb6d1ec91..3fd059ac1 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index d1b24d950..5639e38d0 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/difficulty.cpp b/src/cryptonote_basic/difficulty.cpp index 859173aa5..dbc2e534a 100644 --- a/src/cryptonote_basic/difficulty.cpp +++ b/src/cryptonote_basic/difficulty.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/difficulty.h b/src/cryptonote_basic/difficulty.h index 771deb04c..7f5ea4597 100644 --- a/src/cryptonote_basic/difficulty.h +++ b/src/cryptonote_basic/difficulty.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/events.h b/src/cryptonote_basic/events.h new file mode 100644 index 000000000..6c6742215 --- /dev/null +++ b/src/cryptonote_basic/events.h @@ -0,0 +1,46 @@ +// Copyright (c) 2020, 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 "crypto/hash.h" +#include "cryptonote_basic/cryptonote_basic.h" + +namespace cryptonote +{ + /*! Transactions are expensive to move or copy (lots of 32-byte internal + buffers). This allows `cryptonote::core` to do a single notification for + a vector of transactions, without having to move/copy duplicate or invalid + transactions. */ + struct txpool_event + { + cryptonote::transaction tx; + crypto::hash hash; + bool res; //!< Listeners must ignore `tx` when this is false. + }; +} diff --git a/src/cryptonote_basic/fwd.h b/src/cryptonote_basic/fwd.h new file mode 100644 index 000000000..d54223461 --- /dev/null +++ b/src/cryptonote_basic/fwd.h @@ -0,0 +1,36 @@ +// Copyright (c) 2020, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +namespace cryptonote +{ + struct block; + class transaction; + struct txpool_event; +} diff --git a/src/cryptonote_basic/hardfork.cpp b/src/cryptonote_basic/hardfork.cpp index dfeca27b4..7a5161bc8 100644 --- a/src/cryptonote_basic/hardfork.cpp +++ b/src/cryptonote_basic/hardfork.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/hardfork.h b/src/cryptonote_basic/hardfork.h index 987dcc75a..5800c31b5 100644 --- a/src/cryptonote_basic/hardfork.h +++ b/src/cryptonote_basic/hardfork.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index c1e8365ac..34a559b83 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/miner.h b/src/cryptonote_basic/miner.h index ce50d674e..3cbc4e5a4 100644 --- a/src/cryptonote_basic/miner.h +++ b/src/cryptonote_basic/miner.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/subaddress_index.h b/src/cryptonote_basic/subaddress_index.h index 99933e229..3f5f120d9 100644 --- a/src/cryptonote_basic/subaddress_index.h +++ b/src/cryptonote_basic/subaddress_index.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/tx_extra.h b/src/cryptonote_basic/tx_extra.h index ecb4c6040..50f2e1438 100644 --- a/src/cryptonote_basic/tx_extra.h +++ b/src/cryptonote_basic/tx_extra.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/verification_context.h b/src/cryptonote_basic/verification_context.h index ec5f604a5..2535cba95 100644 --- a/src/cryptonote_basic/verification_context.h +++ b/src/cryptonote_basic/verification_context.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 81dc15dee..8c4e61d4d 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -117,6 +117,11 @@ #define CRYPTONOTE_NOISE_BYTES 3*1024 // 3 KiB #define CRYPTONOTE_NOISE_CHANNELS 2 // Max outgoing connections per zone used for noise/covert sending +// Both below are in seconds. The idea is to delay forwarding from i2p/tor +// to ipv4/6, such that 2+ incoming connections _could_ have sent the tx +#define CRYPTONOTE_FORWARD_DELAY_BASE (CRYPTONOTE_NOISE_MIN_DELAY + CRYPTONOTE_NOISE_DELAY_RANGE) +#define CRYPTONOTE_FORWARD_DELAY_AVERAGE (CRYPTONOTE_FORWARD_DELAY_BASE + (CRYPTONOTE_FORWARD_DELAY_BASE / 2)) + #define CRYPTONOTE_MAX_FRAGMENTS 20 // ~20 * NOISE_BYTES max payload size for covert/noise send #define COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT 1000 @@ -219,6 +224,7 @@ namespace config const unsigned char HASH_KEY_RPC_PAYMENT_NONCE = 0x58; const unsigned char HASH_KEY_MEMORY = 'k'; const unsigned char HASH_KEY_MULTISIG[] = {'M', 'u', 'l', 't' , 'i', 's', 'i', 'g', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + const unsigned char HASH_KEY_TXPROOF_V2[] = "TXPROOF_V2"; namespace testnet { diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt index cb3875878..0be8b544e 100644 --- a/src/cryptonote_core/CMakeLists.txt +++ b/src/cryptonote_core/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 2571e4203..b0726981c 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -32,6 +32,7 @@ #include <cstdio> #include <boost/filesystem.hpp> #include <boost/range/adaptor/reversed.hpp> +#include <boost/format.hpp> #include "include_base_utils.h" #include "cryptonote_basic/cryptonote_basic_impl.h" @@ -86,7 +87,7 @@ DISABLE_VS_WARNINGS(4267) //------------------------------------------------------------------ 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_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_reset_timestamps_and_difficulties_height(true), 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), @@ -427,6 +428,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline if (num_popped_blocks > 0) { m_timestamps_and_difficulties_height = 0; + m_reset_timestamps_and_difficulties_height = true; m_hardfork->reorganize_from_chain_height(get_current_blockchain_height()); uint64_t top_block_height; crypto::hash top_block_hash = get_tail_id(top_block_height); @@ -439,6 +441,15 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline m_long_term_block_weights_cache_rolling_median = epee::misc_utils::rolling_median_t<uint64_t>(m_long_term_block_weights_window); } + bool difficulty_ok; + uint64_t difficulty_recalc_height; + std::tie(difficulty_ok, difficulty_recalc_height) = check_difficulty_checkpoints(); + if (!difficulty_ok) + { + MERROR("Difficulty drift detected!"); + recalculate_difficulties(difficulty_recalc_height); + } + { db_txn_guard txn_guard(m_db, m_db->is_read_only()); if (!update_next_cumulative_weight_limit()) @@ -567,6 +578,7 @@ block Blockchain::pop_block_from_blockchain() CRITICAL_REGION_LOCAL(m_blockchain_lock); m_timestamps_and_difficulties_height = 0; + m_reset_timestamps_and_difficulties_height = true; block popped_block; std::vector<transaction> popped_txs; @@ -644,6 +656,7 @@ bool Blockchain::reset_and_set_genesis_block(const block& b) LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); m_timestamps_and_difficulties_height = 0; + m_reset_timestamps_and_difficulties_height = true; invalidate_block_template_cache(); m_db->reset(); m_db->drop_alt_blocks(); @@ -812,12 +825,20 @@ bool Blockchain::get_block_by_hash(const crypto::hash &h, block &blk, bool *orph // less blocks than desired if there aren't enough. difficulty_type Blockchain::get_difficulty_for_next_block() { + LOG_PRINT_L3("Blockchain::" << __func__); + + std::stringstream ss; + bool print = false; + + int done = 0; + ss << "get_difficulty_for_next_block: height " << m_db->height() << std::endl; if (m_fixed_difficulty) { return m_db->height() ? m_fixed_difficulty : 1; } - LOG_PRINT_L3("Blockchain::" << __func__); +start: + difficulty_type D = 0; crypto::hash top_hash = get_tail_id(); { @@ -826,21 +847,32 @@ difficulty_type Blockchain::get_difficulty_for_next_block() // something a bit out of date, but that's fine since anything which // requires the blockchain lock will have acquired it in the first place, // and it will be unlocked only when called from the getinfo RPC + ss << "Locked, tail id " << top_hash << ", cached is " << m_difficulty_for_next_block_top_hash << std::endl; if (top_hash == m_difficulty_for_next_block_top_hash) - return m_difficulty_for_next_block; + { + ss << "Same, using cached diff " << m_difficulty_for_next_block << std::endl; + D = m_difficulty_for_next_block; + } } CRITICAL_REGION_LOCAL(m_blockchain_lock); std::vector<uint64_t> timestamps; std::vector<difficulty_type> difficulties; uint64_t height; - top_hash = get_tail_id(height); // get it again now that we have the lock - ++height; // top block height to blockchain height + auto new_top_hash = get_tail_id(height); // get it again now that we have the lock + ++height; + if (!(new_top_hash == top_hash)) D=0; + ss << "Re-locked, height " << height << ", tail id " << new_top_hash << (new_top_hash == top_hash ? "" : " (different)") << std::endl; + top_hash = new_top_hash; + // ND: Speedup // 1. Keep a list of the last 735 (or less) blocks that is used to compute difficulty, // then when the next block difficulty is queried, push the latest height data and // pop the oldest one from the list. This only requires 1x read per height instead // of doing 735 (DIFFICULTY_BLOCKS_COUNT). + bool check = false; + if (m_reset_timestamps_and_difficulties_height) + m_timestamps_and_difficulties_height = 0; if (m_timestamps_and_difficulties_height != 0 && ((height - m_timestamps_and_difficulties_height) == 1) && m_timestamps.size() >= DIFFICULTY_BLOCKS_COUNT) { uint64_t index = height - 1; @@ -855,8 +887,12 @@ difficulty_type Blockchain::get_difficulty_for_next_block() m_timestamps_and_difficulties_height = height; timestamps = m_timestamps; difficulties = m_difficulties; + check = true; } - else + //else + std::vector<uint64_t> timestamps_from_cache = timestamps; + std::vector<difficulty_type> difficulties_from_cache = difficulties; + { uint64_t offset = height - std::min <uint64_t> (height, static_cast<uint64_t>(DIFFICULTY_BLOCKS_COUNT)); if (offset == 0) @@ -869,27 +905,179 @@ difficulty_type Blockchain::get_difficulty_for_next_block() timestamps.reserve(height - offset); difficulties.reserve(height - offset); } + ss << "Looking up " << (height - offset) << " from " << offset << std::endl; for (; offset < height; offset++) { timestamps.push_back(m_db->get_block_timestamp(offset)); difficulties.push_back(m_db->get_block_cumulative_difficulty(offset)); } + if (check) if (timestamps != timestamps_from_cache || difficulties !=difficulties_from_cache) + { + ss << "Inconsistency XXX:" << std::endl; + ss << "top hash: "<<top_hash << std::endl; + ss << "timestamps: " << timestamps_from_cache.size() << " from cache, but " << timestamps.size() << " without" << std::endl; + ss << "difficulties: " << difficulties_from_cache.size() << " from cache, but " << difficulties.size() << " without" << std::endl; + ss << "timestamps_from_cache:" << std::endl; for (const auto &v :timestamps_from_cache) ss << " " << v << std::endl; + ss << "timestamps:" << std::endl; for (const auto &v :timestamps) ss << " " << v << std::endl; + ss << "difficulties_from_cache:" << std::endl; for (const auto &v :difficulties_from_cache) ss << " " << v << std::endl; + ss << "difficulties:" << std::endl; for (const auto &v :difficulties) ss << " " << v << std::endl; + + uint64_t dbh = m_db->height(); + uint64_t sh = dbh < 10000 ? 0 : dbh - 10000; + ss << "History from -10k at :" << dbh << ", from " << sh << std::endl; + for (uint64_t h = sh; h < dbh; ++h) + { + uint64_t ts = m_db->get_block_timestamp(h); + difficulty_type d = m_db->get_block_cumulative_difficulty(h); + ss << " " << h << " " << ts << " " << d << std::endl; + } + print = true; + } m_timestamps_and_difficulties_height = height; m_timestamps = timestamps; m_difficulties = difficulties; } + size_t target = get_difficulty_target(); difficulty_type diff = next_difficulty(timestamps, difficulties, target); CRITICAL_REGION_LOCAL1(m_difficulty_lock); m_difficulty_for_next_block_top_hash = top_hash; m_difficulty_for_next_block = diff; + if (D && D != diff) + { + ss << "XXX Mismatch at " << height << "/" << top_hash << "/" << get_tail_id() << ": cached " << D << ", real " << diff << std::endl; + print = true; + } + + ++done; + if (done == 1 && D && D != diff) + { + print = true; + ss << "Might be a race. Let's see what happens if we try again..." << std::endl; + epee::misc_utils::sleep_no_w(100); + goto start; + } + ss << "Diff for " << top_hash << ": " << diff << std::endl; + if (print) + { + MGINFO("START DUMP"); + MGINFO(ss.str()); + MGINFO("END DUMP"); + MGINFO("Please send moneromooo on Freenode the contents of this log, from a couple dozen lines before START DUMP to END DUMP"); + } return diff; } //------------------------------------------------------------------ +std::pair<bool, uint64_t> Blockchain::check_difficulty_checkpoints() const +{ + uint64_t res = 0; + for (const std::pair<uint64_t, difficulty_type>& i : m_checkpoints.get_difficulty_points()) + { + if (i.first >= m_db->height()) + break; + if (m_db->get_block_cumulative_difficulty(i.first) != i.second) + return {false, res}; + res = i.first; + } + return {true, res}; +} +//------------------------------------------------------------------ +size_t Blockchain::recalculate_difficulties(boost::optional<uint64_t> start_height_opt) +{ + if (m_fixed_difficulty) + { + return 0; + } + LOG_PRINT_L3("Blockchain::" << __func__); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + const uint64_t start_height = start_height_opt ? *start_height_opt : check_difficulty_checkpoints().second; + const uint64_t top_height = m_db->height() - 1; + MGINFO("Recalculating difficulties from height " << start_height << " to height " << top_height); + + std::vector<uint64_t> timestamps; + std::vector<difficulty_type> difficulties; + timestamps.reserve(DIFFICULTY_BLOCKS_COUNT + 1); + difficulties.reserve(DIFFICULTY_BLOCKS_COUNT + 1); + if (start_height > 1) + { + for (uint64_t i = 0; i < DIFFICULTY_BLOCKS_COUNT; ++i) + { + uint64_t height = start_height - 1 - i; + if (height == 0) + break; + timestamps.insert(timestamps.begin(), m_db->get_block_timestamp(height)); + difficulties.insert(difficulties.begin(), m_db->get_block_cumulative_difficulty(height)); + } + } + difficulty_type last_cum_diff = start_height <= 1 ? start_height : difficulties.back(); + uint64_t drift_start_height = 0; + std::vector<difficulty_type> new_cumulative_difficulties; + for (uint64_t height = start_height; height <= top_height; ++height) + { + size_t target = get_ideal_hard_fork_version(height) < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; + difficulty_type recalculated_diff = next_difficulty(timestamps, difficulties, target); + + boost::multiprecision::uint256_t recalculated_cum_diff_256 = boost::multiprecision::uint256_t(recalculated_diff) + last_cum_diff; + CHECK_AND_ASSERT_THROW_MES(recalculated_cum_diff_256 <= std::numeric_limits<difficulty_type>::max(), "Difficulty overflow!"); + difficulty_type recalculated_cum_diff = recalculated_cum_diff_256.convert_to<difficulty_type>(); + + if (drift_start_height == 0) + { + difficulty_type existing_cum_diff = m_db->get_block_cumulative_difficulty(height); + if (recalculated_cum_diff != existing_cum_diff) + { + drift_start_height = height; + new_cumulative_difficulties.reserve(top_height + 1 - height); + LOG_ERROR("Difficulty drift found at height:" << height << ", hash:" << m_db->get_block_hash_from_height(height) << ", existing:" << existing_cum_diff << ", recalculated:" << recalculated_cum_diff); + } + } + if (drift_start_height > 0) + { + new_cumulative_difficulties.push_back(recalculated_cum_diff); + if (height % 100000 == 0) + LOG_ERROR(boost::format("%llu / %llu (%.1f%%)") % height % top_height % (100 * (height - drift_start_height) / float(top_height - drift_start_height))); + } + + if (height > 0) + { + timestamps.push_back(m_db->get_block_timestamp(height)); + difficulties.push_back(recalculated_cum_diff); + } + if (timestamps.size() > DIFFICULTY_BLOCKS_COUNT) + { + CHECK_AND_ASSERT_THROW_MES(timestamps.size() == DIFFICULTY_BLOCKS_COUNT + 1, "Wrong timestamps size: " << timestamps.size()); + timestamps.erase(timestamps.begin()); + difficulties.erase(difficulties.begin()); + } + last_cum_diff = recalculated_cum_diff; + } + + if (drift_start_height > 0) + { + LOG_ERROR("Writing to the DB..."); + try + { + m_db->correct_block_cumulative_difficulties(drift_start_height, new_cumulative_difficulties); + } + catch (const std::exception& e) + { + LOG_ERROR("Error correcting cumulative difficulties from height " << drift_start_height << ", what = " << e.what()); + } + LOG_ERROR("Corrected difficulties for " << new_cumulative_difficulties.size() << " blocks"); + // clear cache + m_difficulty_for_next_block_top_hash = crypto::null_hash; + m_timestamps_and_difficulties_height = 0; + } + + return new_cumulative_difficulties.size(); +} +//------------------------------------------------------------------ std::vector<time_t> Blockchain::get_last_block_timestamps(unsigned int blocks) const { + CRITICAL_REGION_LOCAL(m_blockchain_lock); uint64_t height = m_db->height(); if (blocks > height) blocks = height; @@ -914,6 +1102,7 @@ bool Blockchain::rollback_blockchain_switching(std::list<block>& original_chain, } m_timestamps_and_difficulties_height = 0; + m_reset_timestamps_and_difficulties_height = true; // remove blocks from blockchain until we get back to where we should be. while (m_db->height() != rollback_height) @@ -950,6 +1139,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info> CRITICAL_REGION_LOCAL(m_blockchain_lock); m_timestamps_and_difficulties_height = 0; + m_reset_timestamps_and_difficulties_height = true; // if empty alt chain passed (not sure how that could happen), return false CHECK_AND_ASSERT_MES(alt_chain.size(), false, "switch_to_alternative_blockchain: empty chain passed"); @@ -1044,10 +1234,15 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info> 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(), "%d", std::to_string(discarded_blocks).c_str(), NULL); - std::shared_ptr<tools::Notify> block_notify = m_block_notify; - if (block_notify) - for (const auto &bei: alt_chain) - block_notify->notify("%s", epee::string_tools::pod_to_hex(get_block_hash(bei.bl)).c_str(), NULL); + for (const auto& notifier : m_block_notifiers) + { + std::size_t notify_height = split_height; + for (const auto& bei: alt_chain) + { + notifier(notify_height, {std::addressof(bei.bl), 1}); + ++notify_height; + } + } MGINFO_GREEN("REORGANIZE SUCCESS! on height: " << split_height << ", new blockchain size: " << m_db->height()); return true; @@ -1639,6 +1834,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); m_timestamps_and_difficulties_height = 0; + m_reset_timestamps_and_difficulties_height = true; uint64_t block_height = get_block_height(b); if(0 == block_height) { @@ -2493,6 +2689,7 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons } db_rtxn_guard rtxn_guard(m_db); + total_height = get_current_blockchain_height(); blocks.reserve(std::min(std::min(max_count, (size_t)10000), (size_t)(total_height - start_height))); CHECK_AND_ASSERT_MES(m_db->get_blocks_from(start_height, 3, max_count, FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE, blocks, pruned, true, get_miner_tx_hash), false, "Error getting blocks"); @@ -4044,12 +4241,9 @@ leave: get_difficulty_for_next_block(); // just to cache it invalidate_block_template_cache(); - if (notify) - { - std::shared_ptr<tools::Notify> block_notify = m_block_notify; - if (block_notify) - block_notify->notify("%s", epee::string_tools::pod_to_hex(id).c_str(), NULL); - } + + for (const auto& notifier: m_block_notifiers) + notifier(new_height - 1, {std::addressof(bl), 1}); return true; } @@ -4317,7 +4511,14 @@ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync) try { if (m_batch_success) + { m_db->batch_stop(); + if (m_reset_timestamps_and_difficulties_height) + { + m_timestamps_and_difficulties_height = 0; + m_reset_timestamps_and_difficulties_height = false; + } + } else m_db->batch_abort(); success = true; @@ -4933,6 +5134,15 @@ void Blockchain::set_user_options(uint64_t maxthreads, bool sync_on_blocks, uint m_max_prepare_blocks_threads = maxthreads; } +void Blockchain::add_block_notify(boost::function<void(std::uint64_t, epee::span<const block>)>&& notify) +{ + if (notify) + { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + m_block_notifiers.push_back(std::move(notify)); + } +} + void Blockchain::safesyncmode(const bool onoff) { /* all of this is no-op'd if the user set a specific @@ -5028,7 +5238,7 @@ void Blockchain::cancel() } #if defined(PER_BLOCK_CHECKPOINT) -static const char expected_block_hashes_hash[] = "fce1dc7c17f7679f5f447df206b8f5fe2ef6b1a2845e59f650850a0ef00d265f"; +static const char expected_block_hashes_hash[] = "8b48d259d4b1126801b1f329683a26e1d16237420197cd3ccc76af2c55a36e83"; void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints) { if (get_checkpoints == nullptr || !m_fast_sync) diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 3a89cc5df..703dd6400 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -30,6 +30,7 @@ #pragma once #include <boost/asio/io_service.hpp> +#include <boost/function/function_fwd.hpp> #include <boost/serialization/serialization.hpp> #include <boost/serialization/version.hpp> #include <boost/serialization/list.hpp> @@ -310,6 +311,22 @@ namespace cryptonote difficulty_type get_difficulty_for_next_block(); /** + * @brief check currently stored difficulties against difficulty checkpoints + * + * @return {flag, height} flag: true if all difficulty checkpoints pass, height: the last checkpoint height before the difficulty drift bug starts + */ + std::pair<bool, uint64_t> check_difficulty_checkpoints() const; + + /** + * @brief recalculate difficulties for blocks after the last difficulty checkpoints to circumvent the annoying 'difficulty drift' bug + * + * @param start_height: if omitted, starts recalculation from the last difficulty checkpoint + * + * @return number of blocks whose difficulties got corrected + */ + size_t recalculate_difficulties(boost::optional<uint64_t> start_height = boost::none); + + /** * @brief adds a block to the blockchain * * Adds a new block to the blockchain. If the block's parent is not the @@ -748,7 +765,7 @@ namespace cryptonote * * @param notify the notify object to call at every new block */ - void set_block_notify(const std::shared_ptr<tools::Notify> ¬ify) { m_block_notify = notify; } + void add_block_notify(boost::function<void(std::uint64_t, epee::span<const block>)> &¬ify); /** * @brief sets a reorg notify object to call for every reorg @@ -1067,6 +1084,7 @@ namespace cryptonote std::vector<uint64_t> m_timestamps; std::vector<difficulty_type> m_difficulties; uint64_t m_timestamps_and_difficulties_height; + bool m_reset_timestamps_and_difficulties_height; uint64_t m_long_term_block_weights_window; uint64_t m_long_term_effective_median_block_weight; mutable crypto::hash m_long_term_block_weights_cache_tip_hash; @@ -1108,7 +1126,11 @@ namespace cryptonote bool m_batch_success; - std::shared_ptr<tools::Notify> m_block_notify; + /* `boost::function` is used because the implementation never allocates if + the callable object has a single `std::shared_ptr` or `std::weap_ptr` + internally. Whereas, the libstdc++ `std::function` will allocate. */ + + std::vector<boost::function<void(std::uint64_t, epee::span<const block>)>> m_block_notifiers; std::shared_ptr<tools::Notify> m_reorg_notify; // for prepare_handle_incoming_blocks diff --git a/src/cryptonote_core/blockchain_storage_boost_serialization.h b/src/cryptonote_core/blockchain_storage_boost_serialization.h index cfeb5acfc..b5c14d53c 100644 --- a/src/cryptonote_core/blockchain_storage_boost_serialization.h +++ b/src/cryptonote_core/blockchain_storage_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 0f8bde3e9..9a1439c4a 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -41,6 +41,7 @@ using namespace epee; #include "common/download.h" #include "common/threadpool.h" #include "common/command_line.h" +#include "cryptonote_basic/events.h" #include "warnings.h" #include "crypto/crypto.h" #include "cryptonote_config.h" @@ -51,6 +52,7 @@ using namespace epee; #include "ringct/rctTypes.h" #include "blockchain_db/blockchain_db.h" #include "ringct/rctSigs.h" +#include "rpc/zmq_pub.h" #include "common/notify.h" #include "hardforks/hardforks.h" #include "version.h" @@ -262,6 +264,13 @@ namespace cryptonote { m_blockchain_storage.set_enforce_dns_checkpoints(enforce_dns); } + //----------------------------------------------------------------------------------- + void core::set_txpool_listener(boost::function<void(std::vector<txpool_event>)> zmq_pub) + { + CRITICAL_REGION_LOCAL(m_incoming_tx_lock); + m_zmq_pub = std::move(zmq_pub); + } + //----------------------------------------------------------------------------------------------- bool core::update_checkpoints(const bool skip_dns /* = false */) { @@ -614,7 +623,20 @@ namespace cryptonote try { if (!command_line::is_arg_defaulted(vm, arg_block_notify)) - m_blockchain_storage.set_block_notify(std::shared_ptr<tools::Notify>(new tools::Notify(command_line::get_arg(vm, arg_block_notify).c_str()))); + { + struct hash_notify + { + tools::Notify cmdline; + + void operator()(std::uint64_t, epee::span<const block> blocks) const + { + for (const block bl : blocks) + cmdline.notify("%s", epee::string_tools::pod_to_hex(get_block_hash(bl)).c_str(), NULL); + } + }; + + m_blockchain_storage.add_block_notify(hash_notify{{command_line::get_arg(vm, arg_block_notify).c_str()}}); + } } catch (const std::exception &e) { @@ -650,7 +672,7 @@ namespace cryptonote r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? ®test_test_options : test_options, fixed_difficulty, get_checkpoints); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); - r = m_mempool.init(max_txpool_weight); + r = m_mempool.init(max_txpool_weight, m_nettype == FAKECHAIN); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); // now that we have a valid m_blockchain_storage, we can clean out any @@ -957,8 +979,7 @@ namespace cryptonote return false; } - struct result { bool res; cryptonote::transaction tx; crypto::hash hash; }; - std::vector<result> results(tx_blobs.size()); + std::vector<txpool_event> results(tx_blobs.size()); CRITICAL_REGION_LOCAL(m_incoming_tx_lock); @@ -1023,6 +1044,7 @@ namespace cryptonote if (!tx_info.empty()) handle_incoming_tx_accumulated_batch(tx_info, tx_relay == relay_method::block); + bool valid_events = false; bool ok = true; it = tx_blobs.begin(); for (size_t i = 0; i < tx_blobs.size(); i++, ++it) { @@ -1045,10 +1067,18 @@ namespace cryptonote {MERROR_VER("Transaction verification impossible: " << results[i].hash);} if(tvc[i].m_added_to_pool) + { MDEBUG("tx added: " << results[i].hash); + valid_events = true; + } + else + results[i].res = false; } - return ok; + if (valid_events && m_zmq_pub && matches_category(tx_relay, relay_category::legacy)) + m_zmq_pub(std::move(results)); + + return ok; CATCH_ENTRY_L0("core::handle_incoming_txs()", false); } //----------------------------------------------------------------------------------------------- @@ -1273,6 +1303,7 @@ namespace cryptonote { NOTIFY_NEW_TRANSACTIONS::request public_req{}; NOTIFY_NEW_TRANSACTIONS::request private_req{}; + NOTIFY_NEW_TRANSACTIONS::request stem_req{}; for (auto& tx : txs) { switch (std::get<2>(tx)) @@ -1283,6 +1314,9 @@ namespace cryptonote case relay_method::local: private_req.txs.push_back(std::move(std::get<1>(tx))); break; + case relay_method::forward: + stem_req.txs.push_back(std::move(std::get<1>(tx))); + break; case relay_method::block: case relay_method::fluff: case relay_method::stem: @@ -1300,6 +1334,8 @@ namespace cryptonote get_protocol()->relay_transactions(public_req, source, epee::net_utils::zone::public_, relay_method::fluff); if (!private_req.txs.empty()) get_protocol()->relay_transactions(private_req, source, epee::net_utils::zone::invalid, relay_method::local); + if (!stem_req.txs.empty()) + get_protocol()->relay_transactions(stem_req, source, epee::net_utils::zone::public_, relay_method::stem); } return true; } @@ -1649,47 +1685,23 @@ namespace cryptonote << "You can set the level of process detailization through \"set_log <level|categories>\" command," << ENDL << "where <level> is between 0 (no details) and 4 (very verbose), or custom category based levels (eg, *:WARNING)." << ENDL << ENDL - << "Use the \"help\" command to see a simplified list of available commands." << ENDL - << "Use the \"help_advanced\" command to see an advanced list of available commands." << ENDL - << "Use \"help_advanced <command>\" to see a command's documentation." << ENDL + << "Use the \"help\" command to see the list of available commands." << ENDL + << "Use \"help <command>\" to see a command's documentation." << ENDL << "**********************************************************************" << ENDL); m_starter_message_showed = true; } - m_fork_moaner.do_call(boost::bind(&core::check_fork_time, this)); m_txpool_auto_relayer.do_call(boost::bind(&core::relay_txpool_transactions, this)); m_check_updates_interval.do_call(boost::bind(&core::check_updates, this)); m_check_disk_space_interval.do_call(boost::bind(&core::check_disk_space, this)); m_block_rate_interval.do_call(boost::bind(&core::check_block_rate, this)); m_blockchain_pruning_interval.do_call(boost::bind(&core::update_blockchain_pruning, this)); + m_diff_recalc_interval.do_call(boost::bind(&core::recalculate_difficulties, this)); m_miner.on_idle(); m_mempool.on_idle(); return true; } //----------------------------------------------------------------------------------------------- - bool core::check_fork_time() - { - if (m_nettype == FAKECHAIN) - return true; - - HardFork::State state = m_blockchain_storage.get_hard_fork_state(); - el::Level level; - switch (state) { - case HardFork::LikelyForked: - level = el::Level::Warning; - MCLOG_RED(level, "global", "**********************************************************************"); - MCLOG_RED(level, "global", "Last scheduled hard fork is too far in the past."); - MCLOG_RED(level, "global", "We are most likely forked from the network. Daemon update needed now."); - MCLOG_RED(level, "global", "**********************************************************************"); - break; - case HardFork::UpdateNeeded: - break; - default: - break; - } - return true; - } - //----------------------------------------------------------------------------------------------- uint8_t core::get_ideal_hard_fork_version() const { return get_blockchain_storage().get_ideal_hard_fork_version(); @@ -1923,6 +1935,12 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + bool core::recalculate_difficulties() + { + m_blockchain_storage.recalculate_difficulties(); + return true; + } + //----------------------------------------------------------------------------------------------- void core::flush_bad_txs_cache() { bad_semantics_txes_lock.lock(); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 988f25ceb..a53596c2c 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -32,9 +32,11 @@ #include <ctime> +#include <boost/function.hpp> #include <boost/program_options/options_description.hpp> #include <boost/program_options/variables_map.hpp> +#include "cryptonote_basic/fwd.h" #include "cryptonote_core/i_core_events.h" #include "cryptonote_protocol/cryptonote_protocol_handler_common.h" #include "cryptonote_protocol/enums.h" @@ -48,6 +50,7 @@ #include "warnings.h" #include "crypto/hash.h" #include "span.h" +#include "rpc/fwd.h" PUSH_WARNINGS DISABLE_VS_WARNINGS(4355) @@ -446,6 +449,13 @@ namespace cryptonote void set_enforce_dns_checkpoints(bool enforce_dns); /** + * @brief set a listener for txes being added to the txpool + * + * @param callable to notify, or empty function to disable. + */ + void set_txpool_listener(boost::function<void(std::vector<txpool_event>)> zmq_pub); + + /** * @brief set whether or not to enable or disable DNS checkpoints * * @param disble whether to disable DNS checkpoints @@ -1001,18 +1011,6 @@ namespace cryptonote bool check_tx_inputs_keyimages_domain(const transaction& tx) const; /** - * @brief checks HardFork status and prints messages about it - * - * Checks the status of HardFork and logs/prints if an update to - * the daemon is necessary. - * - * @note see Blockchain::get_hard_fork_state and HardFork::State - * - * @return true - */ - bool check_fork_time(); - - /** * @brief attempts to relay any transactions in the mempool which need it * * @return true @@ -1040,6 +1038,13 @@ namespace cryptonote */ bool check_block_rate(); + /** + * @brief recalculate difficulties after the last difficulty checklpoint to circumvent the annoying 'difficulty drift' bug + * + * @return true + */ + bool recalculate_difficulties(); + bool m_test_drop_download = true; //!< whether or not to drop incoming blocks (for testing) uint64_t m_test_drop_download_height = 0; //!< height under which to drop incoming blocks, if doing so @@ -1065,6 +1070,7 @@ namespace cryptonote epee::math_helper::once_a_time_seconds<60*10, true> m_check_disk_space_interval; //!< interval for checking for disk space epee::math_helper::once_a_time_seconds<90, false> m_block_rate_interval; //!< interval for checking block rate epee::math_helper::once_a_time_seconds<60*60*5, true> m_blockchain_pruning_interval; //!< interval for incremental blockchain pruning + epee::math_helper::once_a_time_seconds<60*60*24*7, false> m_diff_recalc_interval; //!< interval for recalculating difficulties std::atomic<bool> m_starter_message_showed; //!< has the "daemon will sync now" message been shown? @@ -1102,7 +1108,12 @@ namespace cryptonote bool m_fluffy_blocks_enabled; bool m_offline; + /* `boost::function` is used because the implementation never allocates if + the callable object has a single `std::shared_ptr` or `std::weap_ptr` + internally. Whereas, the libstdc++ `std::function` will allocate. */ + std::shared_ptr<tools::Notify> m_block_rate_notify; + boost::function<void(std::vector<txpool_event>)> m_zmq_pub; }; } diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 3dd29dd1b..7ea7e81d9 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h index 309e4177f..3622029e0 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/i_core_events.h b/src/cryptonote_core/i_core_events.h index f49fbdf07..03394d785 100644 --- a/src/cryptonote_core/i_core_events.h +++ b/src/cryptonote_core/i_core_events.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 1fc2a637a..7cb0e4062 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -91,6 +91,8 @@ namespace cryptonote time_t const MAX_RELAY_TIME = (60 * 60 * 4); // at most that many seconds between resends float const ACCEPT_THRESHOLD = 1.0f; + constexpr const std::chrono::seconds forward_delay_average{CRYPTONOTE_FORWARD_DELAY_AVERAGE}; + // a kind of increasing backoff within min/max bounds uint64_t get_relay_delay(time_t now, time_t received) { @@ -116,7 +118,7 @@ namespace cryptonote } //--------------------------------------------------------------------------------- //--------------------------------------------------------------------------------- - tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_txpool_max_weight(DEFAULT_TXPOOL_MAX_WEIGHT), m_txpool_weight(0), m_cookie(0) + tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_cookie(0), m_txpool_max_weight(DEFAULT_TXPOOL_MAX_WEIGHT), m_txpool_weight(0), m_mine_stem_txes(false) { } @@ -309,8 +311,14 @@ namespace cryptonote if (meta.upgrade_relay_method(tx_relay) || !existing_tx) // synchronize with embargo timer or stem/fluff out-of-order messages { + using clock = std::chrono::system_clock; + auto last_relayed_time = std::numeric_limits<decltype(meta.last_relayed_time)>::max(); + if (tx_relay == relay_method::forward) + last_relayed_time = clock::to_time_t(clock::now() + crypto::random_poisson_seconds{forward_delay_average}()); + // else the `set_relayed` function will adjust the time accordingly later + //update transactions container - meta.last_relayed_time = std::numeric_limits<decltype(meta.last_relayed_time)>::max(); + meta.last_relayed_time = last_relayed_time; meta.receive_time = receive_time; meta.weight = tx_weight; meta.fee = fee; @@ -341,7 +349,7 @@ namespace cryptonote tvc.m_added_to_pool = true; static_assert(unsigned(relay_method::none) == 0, "expected relay_method::none value to be zero"); - if(meta.fee > 0) + if(meta.fee > 0 && tx_relay != relay_method::forward) tvc.m_relay = tx_relay; } @@ -722,28 +730,46 @@ namespace cryptonote //TODO: investigate whether boolean return is appropriate bool tx_memory_pool::get_relayable_transactions(std::vector<std::tuple<crypto::hash, cryptonote::blobdata, relay_method>> &txs) const { + std::vector<std::pair<crypto::hash, txpool_tx_meta_t>> change_timestamps; + const uint64_t now = time(NULL); + CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); - const uint64_t now = time(NULL); + LockedTXN lock(m_blockchain.get_db()); txs.reserve(m_blockchain.get_txpool_tx_count()); - m_blockchain.for_all_txpool_txes([this, now, &txs](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *){ + m_blockchain.for_all_txpool_txes([this, now, &txs, &change_timestamps](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *){ // 0 fee transactions are never relayed if(!meta.pruned && meta.fee > 0 && !meta.do_not_relay) { - if (!meta.dandelionpp_stem && now - meta.last_relayed_time <= get_relay_delay(now, meta.receive_time)) - return true; - if (meta.dandelionpp_stem && meta.last_relayed_time < now) // for dandelion++ stem, this value is the embargo timeout - return true; + const relay_method tx_relay = meta.get_relay_method(); + switch (tx_relay) + { + case relay_method::stem: + case relay_method::forward: + if (meta.last_relayed_time > now) + return true; // continue to next tx + change_timestamps.emplace_back(txid, meta); + break; + default: + case relay_method::none: + return true; + case relay_method::local: + case relay_method::fluff: + case relay_method::block: + if (now - meta.last_relayed_time <= get_relay_delay(now, meta.receive_time)) + return true; // continue to next tx + break; + } // if the tx is older than half the max lifetime, we don't re-relay it, to avoid a problem // mentioned by smooth where nodes would flush txes at slightly different times, causing // flushed txes to be re-added when received from a node which was just about to flush it - uint64_t max_age = meta.kept_by_block ? CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME : CRYPTONOTE_MEMPOOL_TX_LIVETIME; + uint64_t max_age = (tx_relay == relay_method::block) ? CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME : CRYPTONOTE_MEMPOOL_TX_LIVETIME; if (now - meta.receive_time <= max_age / 2) { try { - txs.emplace_back(txid, m_blockchain.get_txpool_tx_blob(txid, relay_category::all), meta.get_relay_method()); + txs.emplace_back(txid, m_blockchain.get_txpool_tx_blob(txid, relay_category::all), tx_relay); } catch (const std::exception &e) { @@ -754,6 +780,18 @@ namespace cryptonote } return true; }, false, relay_category::relayable); + + for (auto& elem : change_timestamps) + { + /* These transactions are still in forward or stem state, so the field + represents the next time a relay should be attempted. Will be + overwritten when the state is upgraded to stem, fluff or block. This + function is only called every ~2 minutes, so this resetting should be + unnecessary, but is primarily a precaution against potential changes + to the callback routines. */ + elem.second.last_relayed_time = now + get_relay_delay(now, elem.second.receive_time); + m_blockchain.update_txpool_tx(elem.first, elem.second); + } return true; } //--------------------------------------------------------------------------------- @@ -1351,13 +1389,18 @@ namespace cryptonote for (; sorted_it != m_txs_by_fee_and_receive_time.end(); ++sorted_it) { txpool_tx_meta_t meta; - if (!m_blockchain.get_txpool_tx_meta(sorted_it->second, meta) && !meta.matches(relay_category::legacy)) + if (!m_blockchain.get_txpool_tx_meta(sorted_it->second, meta)) { MERROR(" failed to find tx meta"); continue; } - LOG_PRINT_L2("Considering " << sorted_it->second << ", weight " << meta.weight << ", current block weight " << total_weight << "/" << max_total_weight << ", current coinbase " << print_money(best_coinbase)); + LOG_PRINT_L2("Considering " << sorted_it->second << ", weight " << meta.weight << ", current block weight " << total_weight << "/" << max_total_weight << ", current coinbase " << print_money(best_coinbase) << ", relay method " << (unsigned)meta.get_relay_method()); + if (!meta.matches(relay_category::legacy) && !(m_mine_stem_txes && meta.get_relay_method() == relay_method::stem)) + { + LOG_PRINT_L2(" tx relay method is " << (unsigned)meta.get_relay_method()); + continue; + } if (meta.pruned) { LOG_PRINT_L2(" tx is pruned"); @@ -1522,7 +1565,7 @@ namespace cryptonote return n_removed; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::init(size_t max_txpool_weight) + bool tx_memory_pool::init(size_t max_txpool_weight, bool mine_stem_txes) { CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL1(m_blockchain); @@ -1578,6 +1621,7 @@ namespace cryptonote lock.commit(); } + m_mine_stem_txes = mine_stem_txes; m_cookie = 0; // Ignore deserialization error diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 292d427e2..8955d7551 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -205,10 +205,11 @@ namespace cryptonote * @brief loads pool state (if any) from disk, and initializes pool * * @param max_txpool_weight the max weight in bytes + * @param mine_stem_txes whether to mine txes in stem relay mode * * @return true */ - bool init(size_t max_txpool_weight = 0); + bool init(size_t max_txpool_weight = 0, bool mine_stem_txes = false); /** * @brief attempts to save the transaction pool state to disk @@ -603,6 +604,7 @@ private: size_t m_txpool_max_weight; size_t m_txpool_weight; + bool m_mine_stem_txes; mutable std::unordered_map<crypto::hash, std::tuple<bool, tx_verification_context, uint64_t, crypto::hash>> m_input_cache; diff --git a/src/cryptonote_core/tx_sanity_check.cpp b/src/cryptonote_core/tx_sanity_check.cpp index e99982def..ca870779e 100644 --- a/src/cryptonote_core/tx_sanity_check.cpp +++ b/src/cryptonote_core/tx_sanity_check.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/tx_sanity_check.h b/src/cryptonote_core/tx_sanity_check.h index 4a469462f..15c5476ee 100644 --- a/src/cryptonote_core/tx_sanity_check.h +++ b/src/cryptonote_core/tx_sanity_check.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_protocol/CMakeLists.txt b/src/cryptonote_protocol/CMakeLists.txt index bfcf42767..d28b44bb7 100644 --- a/src/cryptonote_protocol/CMakeLists.txt +++ b/src/cryptonote_protocol/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/cryptonote_protocol/block_queue.cpp b/src/cryptonote_protocol/block_queue.cpp index 67f0b3e5d..bbde91c1f 100644 --- a/src/cryptonote_protocol/block_queue.cpp +++ b/src/cryptonote_protocol/block_queue.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_protocol/block_queue.h b/src/cryptonote_protocol/block_queue.h index 93c6532e7..57d2a6490 100644 --- a/src/cryptonote_protocol/block_queue.h +++ b/src/cryptonote_protocol/block_queue.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index 76b57afd3..9687b07a6 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp b/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp index 225418980..42774f2cb 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp +++ b/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp @@ -2,7 +2,7 @@ /// @author rfree (current maintainer in monero.cc project) /// @brief This is the place to implement our handlers for protocol network actions, e.g. for ratelimit for download-requests -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index e2ad3727f..89860fe41 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -2,7 +2,7 @@ /// @author rfree (current maintainer/user in monero.cc project - most of code is from CryptoNote) /// @brief This is the original cryptonote protocol network-events handler, modified by us -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -51,7 +51,8 @@ PUSH_WARNINGS DISABLE_VS_WARNINGS(4355) #define LOCALHOST_INT 2130706433 -#define CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT 500 +#define CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT 100 +static_assert(CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT >= BLOCKS_SYNCHRONIZING_DEFAULT_COUNT_PRE_V4, "Invalid CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT"); namespace cryptonote { diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 3cbfbbe85..af7f1b89d 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -2,7 +2,7 @@ /// @author rfree (current maintainer/user in monero.cc project - most of code is from CryptoNote) /// @brief This is the original cryptonote protocol network-events handler, modified by us -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -308,9 +308,9 @@ namespace cryptonote if (version >= 6 && version != hshd.top_version) { if (version < hshd.top_version && version == m_core.get_ideal_hard_fork_version()) - MCLOG_RED(el::Level::Warning, "global", context << " peer claims higher version than we think (" << + MDEBUG(context << " peer claims higher version than we think (" << (unsigned)hshd.top_version << " for " << (hshd.current_height - 1) << " instead of " << (unsigned)version << - ") - we may be forked from the network and a software upgrade may be needed"); + ") - we may be forked from the network and a software upgrade may be needed, or that peer is broken or malicious"); return false; } } @@ -793,6 +793,12 @@ namespace cryptonote int t_cryptonote_protocol_handler<t_core>::handle_request_fluffy_missing_tx(int command, NOTIFY_REQUEST_FLUFFY_MISSING_TX::request& arg, cryptonote_connection_context& context) { MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_FLUFFY_MISSING_TX (" << arg.missing_tx_indices.size() << " txes), block hash " << arg.block_hash); + if (context.m_state == cryptonote_connection_context::state_before_handshake) + { + LOG_ERROR_CCONTEXT("Requested fluffy tx before handshake, dropping connection"); + drop_connection(context, false, false); + return 1; + } std::vector<std::pair<cryptonote::blobdata, block>> local_blocks; std::vector<cryptonote::blobdata> local_txs; @@ -884,6 +890,8 @@ namespace cryptonote int t_cryptonote_protocol_handler<t_core>::handle_notify_get_txpool_complement(int command, NOTIFY_GET_TXPOOL_COMPLEMENT::request& arg, cryptonote_connection_context& context) { MLOG_P2P_MESSAGE("Received NOTIFY_GET_TXPOOL_COMPLEMENT (" << arg.hashes.size() << " txes)"); + if(context.m_state != cryptonote_connection_context::state_normal) + return 1; std::vector<std::pair<cryptonote::blobdata, block>> local_blocks; std::vector<cryptonote::blobdata> local_txs; @@ -927,7 +935,19 @@ namespace cryptonote return 1; } - relay_method tx_relay; + /* If the txes were received over i2p/tor, the default is to "forward" + with a randomized delay to further enhance the "white noise" behavior, + potentially making it harder for ISP-level spies to determine which + inbound link sent the tx. If the sender disabled "white noise" over + i2p/tor, then the sender is "fluffing" (to only outbound) i2p/tor + connections with the `dandelionpp_fluff` flag set. The receiver (hidden + service) will immediately fluff in that scenario (i.e. this assumes that a + sybil spy will be unable to link an IP to an i2p/tor connection). */ + + const epee::net_utils::zone zone = context.m_remote_address.get_zone(); + relay_method tx_relay = zone == epee::net_utils::zone::public_ ? + relay_method::stem : relay_method::forward; + std::vector<blobdata> stem_txs{}; std::vector<blobdata> fluff_txs{}; if (arg.dandelionpp_fluff) @@ -936,10 +956,7 @@ namespace cryptonote fluff_txs.reserve(arg.txs.size()); } else - { - tx_relay = relay_method::stem; stem_txs.reserve(arg.txs.size()); - } for (auto& tx : arg.txs) { @@ -962,6 +979,7 @@ namespace cryptonote fluff_txs.push_back(std::move(tx)); break; default: + case relay_method::forward: // not supposed to happen here case relay_method::none: break; } @@ -987,6 +1005,12 @@ namespace cryptonote template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context) { + if (context.m_state == cryptonote_connection_context::state_before_handshake) + { + LOG_ERROR_CCONTEXT("Requested objects before handshake, dropping connection"); + drop_connection(context, false, false); + return 1; + } MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_GET_OBJECTS (" << arg.blocks.size() << " blocks)"); if (arg.blocks.size() > CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT) { @@ -1717,11 +1741,16 @@ skip: int t_cryptonote_protocol_handler<t_core>::handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, cryptonote_connection_context& context) { MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_CHAIN (" << arg.block_ids.size() << " blocks"); + if (context.m_state == cryptonote_connection_context::state_before_handshake) + { + LOG_ERROR_CCONTEXT("Requested chain before handshake, dropping connection"); + drop_connection(context, false, false); + return 1; + } NOTIFY_RESPONSE_CHAIN_ENTRY::request r; if(!m_core.find_blockchain_supplement(arg.block_ids, !arg.prune, r)) { LOG_ERROR_CCONTEXT("Failed to handle NOTIFY_REQUEST_CHAIN."); - drop_connection(context, false, false); return 1; } MLOG_P2P_MESSAGE("-->>NOTIFY_RESPONSE_CHAIN_ENTRY: m_start_height=" << r.start_height << ", m_total_height=" << r.total_height << ", m_block_ids.size()=" << r.m_block_ids.size()); @@ -2318,8 +2347,7 @@ skip: MGINFO_YELLOW(ENDL << "**********************************************************************" << ENDL << "You are now synchronized with the network. You may now start monero-wallet-cli." << ENDL << ENDL - << "Use the \"help\" command to see a simplified list of available commands." << ENDL - << "Use the \"help_advanced\" command to see an advanced list of available commands." << ENDL + << "Use the \"help\" command to see the list of available commands." << ENDL << "**********************************************************************"); m_sync_timer.pause(); if (ELPP->vRegistry()->allowed(el::Level::Info, "sync-info")) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h index 11184299d..1c7635fd8 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_protocol/enums.h b/src/cryptonote_protocol/enums.h index a3a52b83f..c0c495837 100644 --- a/src/cryptonote_protocol/enums.h +++ b/src/cryptonote_protocol/enums.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // @@ -37,6 +37,7 @@ namespace cryptonote { none = 0, //!< Received via RPC with `do_not_relay` set local, //!< Received via RPC; trying to send over i2p/tor, etc. + forward, //!< Received over i2p/tor; timer delayed before ipv4/6 public broadcast stem, //!< Received/send over network using Dandelion++ stem fluff, //!< Received/sent over network using Dandelion++ fluff block //!< Received in block, takes precedence over others diff --git a/src/cryptonote_protocol/fwd.h b/src/cryptonote_protocol/fwd.h index 616b48be3..e7722c2f4 100644 --- a/src/cryptonote_protocol/fwd.h +++ b/src/cryptonote_protocol/fwd.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_protocol/levin_notify.cpp b/src/cryptonote_protocol/levin_notify.cpp index 127801092..7c482156f 100644 --- a/src/cryptonote_protocol/levin_notify.cpp +++ b/src/cryptonote_protocol/levin_notify.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // @@ -357,11 +357,15 @@ namespace levin return true; }); - // Always send txs in stem mode over i2p/tor, see comments in `send_txs` below. + /* Always send with `fluff` flag, even over i2p/tor. The hidden service + will disable the forwarding delay and immediately fluff. The i2p/tor + network is therefore replacing the sybil protection of Dandelion++. + Dandelion++ stem phase over i2p/tor is also worth investigating + (with/without "noise"?). */ for (auto& connection : connections) { std::sort(connection.first.begin(), connection.first.end()); // don't leak receive order - make_payload_send_txs(*zone_->p2p, std::move(connection.first), connection.second, zone_->pad_txs, zone_->is_public); + make_payload_send_txs(*zone_->p2p, std::move(connection.first), connection.second, zone_->pad_txs, true); } if (next_flush != std::chrono::steady_clock::time_point::max()) @@ -811,12 +815,11 @@ namespace levin case relay_method::block: return false; case relay_method::stem: - tx_relay = relay_method::fluff; // don't set stempool embargo when skipping to fluff - /* fallthrough */ + case relay_method::forward: case relay_method::local: if (zone_->is_public) { - // this will change a local tx to stem or fluff ... + // this will change a local/forward tx to stem or fluff ... zone_->strand.dispatch( dandelionpp_notify{zone_, std::addressof(core), std::move(txs), source} ); @@ -824,6 +827,11 @@ namespace levin } /* fallthrough */ case relay_method::fluff: + /* If sending stem/forward/local txes over non public networks, + continue to claim that relay mode even though it used the "fluff" + routine. A "fluff" over i2p/tor is not the same as a "fluff" over + ipv4/6. Marking it as "fluff" here will make the tx immediately + visible externally from this node, which is not desired. */ core.on_transactions_relayed(epee::to_span(txs), tx_relay); zone_->strand.dispatch(fluff_notify{zone_, std::move(txs), source}); break; diff --git a/src/cryptonote_protocol/levin_notify.h b/src/cryptonote_protocol/levin_notify.h index 641f1f956..957794b12 100644 --- a/src/cryptonote_protocol/levin_notify.h +++ b/src/cryptonote_protocol/levin_notify.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index d9bfd9a20..b95c0ac88 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h index d089d4e47..6c3e163e6 100644 --- a/src/daemon/command_line_args.h +++ b/src/daemon/command_line_args.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -121,6 +121,10 @@ namespace daemon_args return val; } }; + const command_line::arg_descriptor<std::vector<std::string>> arg_zmq_pub = { + "zmq-pub" + , "Address for ZMQ pub - tcp://ip:port or ipc://path" + }; const command_line::arg_descriptor<bool> arg_zmq_rpc_disabled = { "no-zmq" diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 3a4081e39..504b104b0 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h index 8b85dcf69..64e4c301b 100644 --- a/src/daemon/command_parser_executor.h +++ b/src/daemon/command_parser_executor.h @@ -6,7 +6,7 @@ */ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 7fae77c30..ac4c30726 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -58,6 +58,12 @@ t_command_server::t_command_server( , "Show the help section or the documentation about a <command>." ); m_command_lookup.set_handler( + "apropos" + , std::bind(&t_command_server::apropos, this, p::_1) + , "apropos <keyword> [<keyword> ...]" + , "Search all command descriptions for keyword(s)." + ); + m_command_lookup.set_handler( "print_height" , std::bind(&t_command_parser_executor::print_height, &m_parser, p::_1) , "Print the local blockchain height." @@ -349,7 +355,7 @@ bool t_command_server::start_handling(std::function<void(void)> exit_handler) { if (m_is_rpc) return false; - m_command_lookup.start_handling("", get_commands_str(), exit_handler); + m_command_lookup.start_handling("", "Use \"help\" to list all commands and their usage\n", exit_handler); return true; } @@ -374,6 +380,33 @@ bool t_command_server::help(const std::vector<std::string>& args) return true; } +bool t_command_server::apropos(const std::vector<std::string>& args) +{ + if (args.empty()) + { + std::cout << "Missing keyword" << std::endl; + return true; + } + const std::vector<std::string>& command_list = m_command_lookup.get_command_list(args); + if (command_list.empty()) + { + std::cout << "Nothing found" << std::endl; + return true; + } + + std::cout << std::endl; + for(auto const& command:command_list) + { + std::vector<std::string> cmd; + cmd.push_back(command); + std::pair<std::string, std::string> documentation = m_command_lookup.get_documentation(cmd); + std::cout << " " << documentation.first << std::endl; + } + std::cout << std::endl; + + return true; +} + std::string t_command_server::get_commands_str() { std::stringstream ss; @@ -382,7 +415,7 @@ std::string t_command_server::get_commands_str() std::string usage = m_command_lookup.get_usage(); boost::replace_all(usage, "\n", "\n "); usage.insert(0, " "); - ss << usage << std::endl; + ss << usage; return ss.str(); } diff --git a/src/daemon/command_server.h b/src/daemon/command_server.h index da532223e..df7198d04 100644 --- a/src/daemon/command_server.h +++ b/src/daemon/command_server.h @@ -9,7 +9,7 @@ Passing RPC commands: */ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -73,6 +73,7 @@ public: private: bool help(const std::vector<std::string>& args); + bool apropos(const std::vector<std::string>& args); std::string get_commands_str(); std::string get_command_usage(const std::vector<std::string> &args); diff --git a/src/daemon/core.h b/src/daemon/core.h index 9a3579e20..804d7474d 100644 --- a/src/daemon/core.h +++ b/src/daemon/core.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 056f2f320..99430b2b0 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -34,16 +34,17 @@ #include "misc_log_ex.h" #include "daemon/daemon.h" #include "rpc/daemon_handler.h" +#include "rpc/zmq_pub.h" #include "rpc/zmq_server.h" #include "common/password.h" #include "common/util.h" +#include "cryptonote_basic/events.h" #include "daemon/core.h" #include "daemon/p2p.h" #include "daemon/protocol.h" #include "daemon/rpc.h" #include "daemon/command_server.h" -#include "daemon/command_server.h" #include "daemon/command_line_args.h" #include "net/net_ssl.h" #include "version.h" @@ -57,6 +58,17 @@ using namespace epee; namespace daemonize { +struct zmq_internals +{ + explicit zmq_internals(t_core& core, t_p2p& p2p) + : rpc_handler{core.get(), p2p.get()} + , server{rpc_handler} + {} + + cryptonote::rpc::DaemonHandler rpc_handler; + cryptonote::rpc::ZmqServer server; +}; + struct t_internals { private: t_protocol protocol; @@ -64,6 +76,7 @@ public: t_core core; t_p2p p2p; std::vector<std::unique_ptr<t_rpc>> rpcs; + std::unique_ptr<zmq_internals> zmq; t_internals( boost::program_options::variables_map const & vm @@ -71,6 +84,7 @@ public: : core{vm} , protocol{vm, core, command_line::get_arg(vm, cryptonote::arg_offline)} , p2p{vm, protocol} + , zmq{nullptr} { // Handle circular dependencies protocol.set_p2p_endpoint(p2p.get()); @@ -87,6 +101,28 @@ public: auto restricted_rpc_port = command_line::get_arg(vm, restricted_rpc_port_arg); rpcs.emplace_back(new t_rpc{vm, core, p2p, true, restricted_rpc_port, "restricted", true}); } + + if (!command_line::get_arg(vm, daemon_args::arg_zmq_rpc_disabled)) + { + zmq.reset(new zmq_internals{core, p2p}); + + const std::string zmq_port = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_bind_port); + const std::string zmq_address = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_bind_ip); + + if (!zmq->server.init_rpc(zmq_address, zmq_port)) + throw std::runtime_error{"Failed to add TCP socket(" + zmq_address + ":" + zmq_port + ") to ZMQ RPC Server"}; + + std::shared_ptr<cryptonote::listener::zmq_pub> shared; + const std::vector<std::string> zmq_pub = command_line::get_arg(vm, daemon_args::arg_zmq_pub); + if (!zmq_pub.empty() && !(shared = zmq->server.init_pub(epee::to_span(zmq_pub)))) + throw std::runtime_error{"Failed to initialize zmq_pub"}; + + if (shared) + { + core.get().get_blockchain_storage().add_block_notify(cryptonote::listener::zmq_pub::chain_main{shared}); + core.get().set_txpool_listener(cryptonote::listener::zmq_pub::txpool_add{shared}); + } + } } }; @@ -104,9 +140,6 @@ t_daemon::t_daemon( : mp_internals{new t_internals{vm}}, public_rpc_port(public_rpc_port) { - zmq_rpc_bind_port = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_bind_port); - zmq_rpc_bind_address = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_bind_ip); - zmq_rpc_disabled = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_disabled); } t_daemon::~t_daemon() = default; @@ -170,31 +203,8 @@ bool t_daemon::run(bool interactive) rpc_commands->start_handling(std::bind(&daemonize::t_daemon::stop_p2p, this)); } - cryptonote::rpc::DaemonHandler rpc_daemon_handler(mp_internals->core.get(), mp_internals->p2p.get()); - cryptonote::rpc::ZmqServer zmq_server(rpc_daemon_handler); - - if (!zmq_rpc_disabled) - { - if (!zmq_server.addTCPSocket(zmq_rpc_bind_address, zmq_rpc_bind_port)) - { - LOG_ERROR(std::string("Failed to add TCP Socket (") + zmq_rpc_bind_address - + ":" + zmq_rpc_bind_port + ") to ZMQ RPC Server"); - - if (rpc_commands) - rpc_commands->stop_handling(); - - for(auto& rpc : mp_internals->rpcs) - rpc->stop(); - - return false; - } - - MINFO("Starting ZMQ server..."); - zmq_server.run(); - - MINFO(std::string("ZMQ server started at ") + zmq_rpc_bind_address - + ":" + zmq_rpc_bind_port + "."); - } + if (mp_internals->zmq) + mp_internals->zmq->server.run(); else MINFO("ZMQ server disabled"); @@ -209,8 +219,8 @@ bool t_daemon::run(bool interactive) if (rpc_commands) rpc_commands->stop_handling(); - if (!zmq_rpc_disabled) - zmq_server.stop(); + if (mp_internals->zmq) + mp_internals->zmq->server.stop(); for(auto& rpc : mp_internals->rpcs) rpc->stop(); diff --git a/src/daemon/daemon.h b/src/daemon/daemon.h index c0efb68ee..2eb2019ce 100644 --- a/src/daemon/daemon.h +++ b/src/daemon/daemon.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -44,9 +44,6 @@ private: private: std::unique_ptr<t_internals> mp_internals; uint16_t public_rpc_port; - std::string zmq_rpc_bind_address; - std::string zmq_rpc_bind_port; - bool zmq_rpc_disabled; public: t_daemon( boost::program_options::variables_map const & vm, diff --git a/src/daemon/executor.cpp b/src/daemon/executor.cpp index 3719a253d..f9ba3b493 100644 --- a/src/daemon/executor.cpp +++ b/src/daemon/executor.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemon/executor.h b/src/daemon/executor.h index 61e4e1bbf..a7235711c 100644 --- a/src/daemon/executor.h +++ b/src/daemon/executor.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index 3e25636d8..f2ae6dcc3 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -107,6 +107,20 @@ uint16_t parse_public_rpc_port(const po::variables_map &vm) return rpc_port; } +#ifdef WIN32 +bool isFat32(const wchar_t* root_path) +{ + std::vector<wchar_t> fs(MAX_PATH + 1); + if (!::GetVolumeInformationW(root_path, nullptr, 0, nullptr, 0, nullptr, &fs[0], MAX_PATH)) + { + MERROR("Failed to get '" << root_path << "' filesystem name. Error code: " << ::GetLastError()); + return false; + } + + return wcscmp(L"FAT32", &fs[0]) == 0; +} +#endif + int main(int argc, char const * argv[]) { try { @@ -140,6 +154,7 @@ int main(int argc, char const * argv[]) command_line::add_arg(core_settings, daemon_args::arg_public_node); command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_ip); command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_port); + command_line::add_arg(core_settings, daemon_args::arg_zmq_pub); command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_disabled); daemonizer::init_options(hidden_options, visible_options); @@ -233,6 +248,13 @@ int main(int argc, char const * argv[]) boost::filesystem::path data_dir = boost::filesystem::absolute( command_line::get_arg(vm, cryptonote::arg_data_dir)); +#ifdef WIN32 + if (isFat32(data_dir.root_name().c_str())) + { + MERROR("Data directory resides on FAT32 volume that has 4GiB file size limit, blockchain might get corrupted."); + } +#endif + // FIXME: not sure on windows implementation default, needs further review //bf::path relative_path_base = daemonizer::get_relative_path_base(vm); bf::path relative_path_base = data_dir; diff --git a/src/daemon/p2p.h b/src/daemon/p2p.h index 267b99c89..f68efccc2 100644 --- a/src/daemon/p2p.h +++ b/src/daemon/p2p.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemon/protocol.h b/src/daemon/protocol.h index 51a2bce1f..6b03c169a 100644 --- a/src/daemon/protocol.h +++ b/src/daemon/protocol.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemon/rpc.h b/src/daemon/rpc.h index 6f545a3b7..af48bcc45 100644 --- a/src/daemon/rpc.h +++ b/src/daemon/rpc.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 034d49918..77dfe332f 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -511,7 +511,7 @@ bool t_rpc_command_executor::show_status() { } std::stringstream str; - str << boost::format("Height: %llu/%llu (%.1f%%) on %s%s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections") + str << boost::format("Height: %llu/%llu (%.1f%%) on %s%s, %s, net hash %s, v%u%s, %u(out)+%u(in) connections") % (unsigned long long)ires.height % (unsigned long long)net_height % get_sync_percentage(ires) @@ -521,7 +521,6 @@ bool t_rpc_command_executor::show_status() { % get_mining_speed(cryptonote::difficulty_type(ires.wide_difficulty) / ires.target) % (unsigned)hfres.version % get_fork_extra_info(hfres.earliest_height, net_height, ires.target) - % (hfres.state == cryptonote::HardFork::Ready ? "up to date" : hfres.state == cryptonote::HardFork::UpdateNeeded ? "update needed" : "out of date, likely forked") % (unsigned)ires.outgoing_connections_count % (unsigned)ires.incoming_connections_count ; diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index 66ada6f1f..6fb5d6903 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -6,7 +6,7 @@ */ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/CMakeLists.txt b/src/daemonizer/CMakeLists.txt index d540adc10..462d40531 100644 --- a/src/daemonizer/CMakeLists.txt +++ b/src/daemonizer/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/daemonizer/daemonizer.h b/src/daemonizer/daemonizer.h index a45a2b44f..442861cdb 100644 --- a/src/daemonizer/daemonizer.h +++ b/src/daemonizer/daemonizer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/posix_daemonizer.inl b/src/daemonizer/posix_daemonizer.inl index 9ec09c678..76ef467ca 100644 --- a/src/daemonizer/posix_daemonizer.inl +++ b/src/daemonizer/posix_daemonizer.inl @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/posix_fork.h b/src/daemonizer/posix_fork.h index 27b4ac18d..d841df0fc 100644 --- a/src/daemonizer/posix_fork.h +++ b/src/daemonizer/posix_fork.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/windows_daemonizer.inl b/src/daemonizer/windows_daemonizer.inl index c6cd474fd..67c8a2855 100644 --- a/src/daemonizer/windows_daemonizer.inl +++ b/src/daemonizer/windows_daemonizer.inl @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/windows_service.cpp b/src/daemonizer/windows_service.cpp index 7d53b07c7..01be6b247 100644 --- a/src/daemonizer/windows_service.cpp +++ b/src/daemonizer/windows_service.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/windows_service.h b/src/daemonizer/windows_service.h index 12429c78a..091041391 100644 --- a/src/daemonizer/windows_service.h +++ b/src/daemonizer/windows_service.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/windows_service_runner.h b/src/daemonizer/windows_service_runner.h index 703795ce9..eb72df66d 100644 --- a/src/daemonizer/windows_service_runner.h +++ b/src/daemonizer/windows_service_runner.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/debug_utilities/CMakeLists.txt b/src/debug_utilities/CMakeLists.txt index 03c2b3e20..7b21123f6 100644 --- a/src/debug_utilities/CMakeLists.txt +++ b/src/debug_utilities/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/debug_utilities/cn_deserialize.cpp b/src/debug_utilities/cn_deserialize.cpp index 89c9db02e..dd4701e4f 100644 --- a/src/debug_utilities/cn_deserialize.cpp +++ b/src/debug_utilities/cn_deserialize.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/debug_utilities/dns_checks.cpp b/src/debug_utilities/dns_checks.cpp index 3c9daa769..76b66c6cb 100644 --- a/src/debug_utilities/dns_checks.cpp +++ b/src/debug_utilities/dns_checks.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/debug_utilities/object_sizes.cpp b/src/debug_utilities/object_sizes.cpp index 302f0a206..bffff0882 100644 --- a/src/debug_utilities/object_sizes.cpp +++ b/src/debug_utilities/object_sizes.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt index 0b0686f61..ff2afba4b 100644 --- a/src/device/CMakeLists.txt +++ b/src/device/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # @@ -72,6 +72,7 @@ target_link_libraries(device ${HIDAPI_LIBRARIES} cncrypto ringct_basic + wallet-crypto ${OPENSSL_CRYPTO_LIBRARIES} ${Boost_SERIALIZATION_LIBRARY} PRIVATE diff --git a/src/device/device.cpp b/src/device/device.cpp index fbd77dab9..4821abdcf 100644 --- a/src/device/device.cpp +++ b/src/device/device.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device/device.hpp b/src/device/device.hpp index ecc4849bf..ef973c9f4 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device/device_cold.hpp b/src/device/device_cold.hpp index 22708c46a..d435b448c 100644 --- a/src/device/device_cold.hpp +++ b/src/device/device_cold.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp index 47156cbce..096cb35ba 100644 --- a/src/device/device_default.cpp +++ b/src/device/device_default.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -32,6 +32,7 @@ #include "device_default.hpp" #include "int-util.h" +#include "crypto/wallet/crypto.h" #include "cryptonote_basic/account.h" #include "cryptonote_basic/subaddress_index.h" #include "cryptonote_core/cryptonote_tx_utils.h" @@ -120,7 +121,7 @@ namespace hw { /* ======================================================================= */ bool device_default::derive_subaddress_public_key(const crypto::public_key &out_key, const crypto::key_derivation &derivation, const std::size_t output_index, crypto::public_key &derived_key) { - return crypto::derive_subaddress_public_key(out_key, derivation, output_index,derived_key); + return crypto::wallet::derive_subaddress_public_key(out_key, derivation, output_index,derived_key); } crypto::public_key device_default::get_subaddress_spend_public_key(const cryptonote::account_keys& keys, const cryptonote::subaddress_index &index) { @@ -236,7 +237,7 @@ namespace hw { } bool device_default::generate_key_derivation(const crypto::public_key &key1, const crypto::secret_key &key2, crypto::key_derivation &derivation) { - return crypto::generate_key_derivation(key1, key2, derivation); + return crypto::wallet::generate_key_derivation(key1, key2, derivation); } bool device_default::derivation_to_scalar(const crypto::key_derivation &derivation, const size_t output_index, crypto::ec_scalar &res){ diff --git a/src/device/device_default.hpp b/src/device/device_default.hpp index 64cad78b0..bdd99f89c 100644 --- a/src/device/device_default.hpp +++ b/src/device/device_default.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device/device_io.hpp b/src/device/device_io.hpp index fe66736f7..6a7744c11 100644 --- a/src/device/device_io.hpp +++ b/src/device/device_io.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device/device_io_hid.cpp b/src/device/device_io_hid.cpp index 840529c38..7c61c3b1a 100644 --- a/src/device/device_io_hid.cpp +++ b/src/device/device_io_hid.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device/device_io_hid.hpp b/src/device/device_io_hid.hpp index 96cb8d993..e6d76f276 100644 --- a/src/device/device_io_hid.hpp +++ b/src/device/device_io_hid.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 222a84d3f..4bd3d75b1 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -1108,7 +1108,7 @@ namespace hw { for(size_t n=0; n < additional_derivations.size();++n) { if(derivation == additional_derivations[n]) { pkey = &additional_tx_pub_keys[n]; - MDEBUG("conceal derivation with additionnal tx pub key"); + MDEBUG("conceal derivation with additional tx pub key"); break; } } @@ -1468,8 +1468,8 @@ namespace hw { offset = set_command_header(INS_PREFIX_HASH,2,cnt); len = pref_length - pref_offset; //options - if (len > (BUFFER_SEND_SIZE-7)) { - len = BUFFER_SEND_SIZE-7; + if (len > (BUFFER_SEND_SIZE-offset-3)) { + len = BUFFER_SEND_SIZE-offset-3; this->buffer_send[offset] = 0x80; } else { this->buffer_send[offset] = 0x00; @@ -1640,20 +1640,20 @@ namespace hw { //if (tx_version > 1) { - ASSERT_X(recv_len>=32, "Not enought data from device"); + ASSERT_X(recv_len>=32, "Not enough data from device"); crypto::secret_key scalar1; this->receive_secret((unsigned char*)scalar1.data, offset); amount_keys.push_back(rct::sk2rct(scalar1)); recv_len -= 32; } - ASSERT_X(recv_len>=32, "Not enought data from device"); + ASSERT_X(recv_len>=32, "Not enough data from device"); memmove(out_eph_public_key.data, &this->buffer_recv[offset], 32); recv_len -= 32; offset += 32; if (need_additional_txkeys) { - ASSERT_X(recv_len>=32, "Not enought data from device"); + ASSERT_X(recv_len>=32, "Not enough data from device"); memmove(additional_txkey.pub.data, &this->buffer_recv[offset], 32); additional_tx_public_keys.push_back(additional_txkey.pub); offset += 32; diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp index 070162cbc..4036035c8 100644 --- a/src/device/device_ledger.hpp +++ b/src/device/device_ledger.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device/log.cpp b/src/device/log.cpp index 616ad8e90..6e62f1dee 100644 --- a/src/device/log.cpp +++ b/src/device/log.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device/log.hpp b/src/device/log.hpp index bfe6e9edc..66c3e06db 100644 --- a/src/device/log.hpp +++ b/src/device/log.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device_trezor/CMakeLists.txt b/src/device_trezor/CMakeLists.txt index 250939da7..f105f68b7 100644 --- a/src/device_trezor/CMakeLists.txt +++ b/src/device_trezor/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/device_trezor/device_trezor.cpp b/src/device_trezor/device_trezor.cpp index 8bde1cb75..7c3e8785d 100644 --- a/src/device_trezor/device_trezor.cpp +++ b/src/device_trezor/device_trezor.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -678,8 +678,10 @@ namespace trezor { throw exc::TrezorException("Trezor firmware 2.0.10 and lower are not supported. Please update."); } - // default client version, higher versions check will be added unsigned client_version = 1; + if (trezor_version >= pack_version(2, 3, 1)){ + client_version = 3; + } #ifdef WITH_TREZOR_DEBUGGING // Override client version for tests diff --git a/src/device_trezor/device_trezor.hpp b/src/device_trezor/device_trezor.hpp index f558b7b19..d91d1de3f 100644 --- a/src/device_trezor/device_trezor.hpp +++ b/src/device_trezor/device_trezor.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device_trezor/device_trezor_base.cpp b/src/device_trezor/device_trezor_base.cpp index 354ae6691..f59be1573 100644 --- a/src/device_trezor/device_trezor_base.cpp +++ b/src/device_trezor/device_trezor_base.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device_trezor/device_trezor_base.hpp b/src/device_trezor/device_trezor_base.hpp index 62b2c2bf4..4db8f0c8e 100644 --- a/src/device_trezor/device_trezor_base.hpp +++ b/src/device_trezor/device_trezor_base.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device_trezor/trezor.hpp b/src/device_trezor/trezor.hpp index 396a27534..b3f85f6a8 100644 --- a/src/device_trezor/trezor.hpp +++ b/src/device_trezor/trezor.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device_trezor/trezor/exceptions.hpp b/src/device_trezor/trezor/exceptions.hpp index 41e8c2875..9d63329e4 100644 --- a/src/device_trezor/trezor/exceptions.hpp +++ b/src/device_trezor/trezor/exceptions.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device_trezor/trezor/messages_map.cpp b/src/device_trezor/trezor/messages_map.cpp index 6956b369a..02869f760 100644 --- a/src/device_trezor/trezor/messages_map.cpp +++ b/src/device_trezor/trezor/messages_map.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device_trezor/trezor/messages_map.hpp b/src/device_trezor/trezor/messages_map.hpp index 28fe9f4c2..a903eaf92 100644 --- a/src/device_trezor/trezor/messages_map.hpp +++ b/src/device_trezor/trezor/messages_map.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device_trezor/trezor/protocol.cpp b/src/device_trezor/trezor/protocol.cpp index 2a7783497..25c3d816d 100644 --- a/src/device_trezor/trezor/protocol.cpp +++ b/src/device_trezor/trezor/protocol.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device_trezor/trezor/protocol.hpp b/src/device_trezor/trezor/protocol.hpp index 8fc5eba97..35e7d9605 100644 --- a/src/device_trezor/trezor/protocol.hpp +++ b/src/device_trezor/trezor/protocol.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device_trezor/trezor/transport.cpp b/src/device_trezor/trezor/transport.cpp index 494706373..a56090de0 100644 --- a/src/device_trezor/trezor/transport.cpp +++ b/src/device_trezor/trezor/transport.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device_trezor/trezor/transport.hpp b/src/device_trezor/trezor/transport.hpp index 9a43b3637..ff843f06b 100644 --- a/src/device_trezor/trezor/transport.hpp +++ b/src/device_trezor/trezor/transport.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/device_trezor/trezor/trezor_defs.hpp b/src/device_trezor/trezor/trezor_defs.hpp index f0697cdb5..9882d6d4b 100644 --- a/src/device_trezor/trezor/trezor_defs.hpp +++ b/src/device_trezor/trezor/trezor_defs.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/gen_multisig/CMakeLists.txt b/src/gen_multisig/CMakeLists.txt index 3a5e29273..6d8bdfb5c 100644 --- a/src/gen_multisig/CMakeLists.txt +++ b/src/gen_multisig/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019, The Monero Project +# Copyright (c) 2017-2020, The Monero Project # # All rights reserved. # diff --git a/src/gen_multisig/gen_multisig.cpp b/src/gen_multisig/gen_multisig.cpp index 06019c50e..15f7e5c0a 100644 --- a/src/gen_multisig/gen_multisig.cpp +++ b/src/gen_multisig/gen_multisig.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/gen_ssl_cert/CMakeLists.txt b/src/gen_ssl_cert/CMakeLists.txt index 471df021b..6203feb21 100644 --- a/src/gen_ssl_cert/CMakeLists.txt +++ b/src/gen_ssl_cert/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019, The Monero Project +# Copyright (c) 2017-2020, The Monero Project # # All rights reserved. # diff --git a/src/gen_ssl_cert/gen_ssl_cert.cpp b/src/gen_ssl_cert/gen_ssl_cert.cpp index 7a9b01700..1a048e9e8 100644 --- a/src/gen_ssl_cert/gen_ssl_cert.cpp +++ b/src/gen_ssl_cert/gen_ssl_cert.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/hardforks/CMakeLists.txt b/src/hardforks/CMakeLists.txt index bd2d14ceb..46f51e09d 100644 --- a/src/hardforks/CMakeLists.txt +++ b/src/hardforks/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/hardforks/hardforks.cpp b/src/hardforks/hardforks.cpp index 7ad09dbef..f4de0ddcf 100644 --- a/src/hardforks/hardforks.cpp +++ b/src/hardforks/hardforks.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/hardforks/hardforks.h b/src/hardforks/hardforks.h index e7bceca42..039f18176 100644 --- a/src/hardforks/hardforks.h +++ b/src/hardforks/hardforks.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/mnemonics/CMakeLists.txt b/src/mnemonics/CMakeLists.txt index acb9a4338..ed53a41f6 100644 --- a/src/mnemonics/CMakeLists.txt +++ b/src/mnemonics/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/mnemonics/chinese_simplified.h b/src/mnemonics/chinese_simplified.h index ff035dde0..7a211bc34 100644 --- a/src/mnemonics/chinese_simplified.h +++ b/src/mnemonics/chinese_simplified.h @@ -21,7 +21,7 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-// Code surrounding the word list is Copyright (c) 2014-2019, The Monero Project
+// Code surrounding the word list is Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/dutch.h b/src/mnemonics/dutch.h index 14ba56a7e..29c346d42 100644 --- a/src/mnemonics/dutch.h +++ b/src/mnemonics/dutch.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project
+// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp index 2dd40cc9a..bda18d950 100644 --- a/src/mnemonics/electrum-words.cpp +++ b/src/mnemonics/electrum-words.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/mnemonics/electrum-words.h b/src/mnemonics/electrum-words.h index 9aa727e45..8d4c5be66 100644 --- a/src/mnemonics/electrum-words.h +++ b/src/mnemonics/electrum-words.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/mnemonics/english.h b/src/mnemonics/english.h index 677b70052..b7eb3d643 100644 --- a/src/mnemonics/english.h +++ b/src/mnemonics/english.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project
+// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/english_old.h b/src/mnemonics/english_old.h index 179fc45ec..f408b2a6c 100644 --- a/src/mnemonics/english_old.h +++ b/src/mnemonics/english_old.h @@ -1,6 +1,6 @@ // Word list originally created as part of the Electrum project, Copyright (C) 2014 Thomas Voegtlin
//
-// Copyright (c) 2014-2019, The Monero Project
+// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/esperanto.h b/src/mnemonics/esperanto.h index ef63a2235..4897fe0b3 100644 --- a/src/mnemonics/esperanto.h +++ b/src/mnemonics/esperanto.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project
+// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/french.h b/src/mnemonics/french.h index 85925aa53..d5a7b6cb6 100644 --- a/src/mnemonics/french.h +++ b/src/mnemonics/french.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/mnemonics/german.h b/src/mnemonics/german.h index 40116970c..c38a1fb83 100644 --- a/src/mnemonics/german.h +++ b/src/mnemonics/german.h @@ -1,6 +1,6 @@ // Word list created by Monero contributor Shrikez
//
-// Copyright (c) 2014-2019, The Monero Project
+// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/italian.h b/src/mnemonics/italian.h index 204fdcb78..f202e23e2 100644 --- a/src/mnemonics/italian.h +++ b/src/mnemonics/italian.h @@ -1,6 +1,6 @@ // Word list created by Monero contributor Shrikez
//
-// Copyright (c) 2014-2019, The Monero Project
+// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/japanese.h b/src/mnemonics/japanese.h index 6213c4976..899d78f48 100644 --- a/src/mnemonics/japanese.h +++ b/src/mnemonics/japanese.h @@ -21,7 +21,7 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-// Code surrounding the word list is Copyright (c) 2014-2019, The Monero Project
+// Code surrounding the word list is Copyright (c) 2014-2020, The Monero Project
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
diff --git a/src/mnemonics/language_base.h b/src/mnemonics/language_base.h index 7d2599e9a..bf8793aa2 100644 --- a/src/mnemonics/language_base.h +++ b/src/mnemonics/language_base.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project
+// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
@@ -41,6 +41,7 @@ #include <boost/algorithm/string.hpp>
#include "misc_log_ex.h"
#include "fnv1.h"
+#include "common/utf8.h"
/*!
* \namespace Language
@@ -73,78 +74,11 @@ namespace Language return prefix;
}
- template<typename T>
- inline T utf8canonical(const T &s)
- {
- T sc = "";
- size_t avail = s.size();
- const char *ptr = s.data();
- wint_t cp = 0;
- int bytes = 1;
- char wbuf[8], *wptr;
- while (avail--)
- {
- if ((*ptr & 0x80) == 0)
- {
- cp = *ptr++;
- bytes = 1;
- }
- else if ((*ptr & 0xe0) == 0xc0)
- {
- if (avail < 1)
- throw std::runtime_error("Invalid UTF-8");
- cp = (*ptr++ & 0x1f) << 6;
- cp |= *ptr++ & 0x3f;
- --avail;
- bytes = 2;
- }
- else if ((*ptr & 0xf0) == 0xe0)
- {
- if (avail < 2)
- throw std::runtime_error("Invalid UTF-8");
- cp = (*ptr++ & 0xf) << 12;
- cp |= (*ptr++ & 0x3f) << 6;
- cp |= *ptr++ & 0x3f;
- avail -= 2;
- bytes = 3;
- }
- else if ((*ptr & 0xf8) == 0xf0)
- {
- if (avail < 3)
- throw std::runtime_error("Invalid UTF-8");
- cp = (*ptr++ & 0x7) << 18;
- cp |= (*ptr++ & 0x3f) << 12;
- cp |= (*ptr++ & 0x3f) << 6;
- cp |= *ptr++ & 0x3f;
- avail -= 3;
- bytes = 4;
- }
- else
- throw std::runtime_error("Invalid UTF-8");
-
- cp = std::towlower(cp);
- wptr = wbuf;
- switch (bytes)
- {
- case 1: *wptr++ = cp; break;
- case 2: *wptr++ = 0xc0 | (cp >> 6); *wptr++ = 0x80 | (cp & 0x3f); break;
- case 3: *wptr++ = 0xe0 | (cp >> 12); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break;
- case 4: *wptr++ = 0xf0 | (cp >> 18); *wptr++ = 0x80 | ((cp >> 12) & 0x3f); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break;
- default: throw std::runtime_error("Invalid UTF-8");
- }
- *wptr = 0;
- sc += T(wbuf, bytes);
- cp = 0;
- bytes = 1;
- }
- return sc;
- }
-
struct WordHash
{
std::size_t operator()(const epee::wipeable_string &s) const
{
- const epee::wipeable_string sc = utf8canonical(s);
+ const epee::wipeable_string sc = tools::utf8canonical(s, [](wint_t c) -> wint_t { return std::towlower(c); });
return epee::fnv::FNV1a(sc.data(), sc.size());
}
};
@@ -153,8 +87,8 @@ namespace Language {
bool operator()(const epee::wipeable_string &s0, const epee::wipeable_string &s1) const
{
- const epee::wipeable_string s0c = utf8canonical(s0);
- const epee::wipeable_string s1c = utf8canonical(s1);
+ const epee::wipeable_string s0c = tools::utf8canonical(s0, [](wint_t c) -> wint_t { return std::towlower(c); });
+ const epee::wipeable_string s1c = tools::utf8canonical(s1, [](wint_t c) -> wint_t { return std::towlower(c); });
return s0c == s1c;
}
};
diff --git a/src/mnemonics/lojban.h b/src/mnemonics/lojban.h index b6baf46c4..95d8b4633 100644 --- a/src/mnemonics/lojban.h +++ b/src/mnemonics/lojban.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project
+// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/portuguese.h b/src/mnemonics/portuguese.h index dbc51fb2a..090278f27 100644 --- a/src/mnemonics/portuguese.h +++ b/src/mnemonics/portuguese.h @@ -21,7 +21,7 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-// Code surrounding the word list is Copyright (c) 2014-2019, The Monero Project
+// Code surrounding the word list is Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/russian.h b/src/mnemonics/russian.h index d36942a9b..ca2629c1a 100644 --- a/src/mnemonics/russian.h +++ b/src/mnemonics/russian.h @@ -1,6 +1,6 @@ // Word list created by Monero contributor sammy007
//
-// Copyright (c) 2014-2019, The Monero Project
+// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/singleton.h b/src/mnemonics/singleton.h index ffe3fe4de..10d220b65 100644 --- a/src/mnemonics/singleton.h +++ b/src/mnemonics/singleton.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project
+// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/spanish.h b/src/mnemonics/spanish.h index a05485775..f3adaaf42 100644 --- a/src/mnemonics/spanish.h +++ b/src/mnemonics/spanish.h @@ -21,7 +21,7 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-// Code surrounding the word list is Copyright (c) 2014-2019, The Monero Project
+// Code surrounding the word list is Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
diff --git a/src/multisig/CMakeLists.txt b/src/multisig/CMakeLists.txt index 4631b75ef..eaa2c6f71 100644 --- a/src/multisig/CMakeLists.txt +++ b/src/multisig/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019, The Monero Project +# Copyright (c) 2017-2020, The Monero Project # # All rights reserved. # diff --git a/src/multisig/multisig.cpp b/src/multisig/multisig.cpp index 70a4c1c8e..272de73b2 100644 --- a/src/multisig/multisig.cpp +++ b/src/multisig/multisig.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/multisig/multisig.h b/src/multisig/multisig.h index bc6edbb05..eab32187c 100644 --- a/src/multisig/multisig.h +++ b/src/multisig/multisig.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/net/dandelionpp.cpp b/src/net/dandelionpp.cpp index 4d2f75428..e0d2821e5 100644 --- a/src/net/dandelionpp.cpp +++ b/src/net/dandelionpp.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/net/dandelionpp.h b/src/net/dandelionpp.h index 75b63bc0c..345f868af 100644 --- a/src/net/dandelionpp.h +++ b/src/net/dandelionpp.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/net/i2p_address.cpp b/src/net/i2p_address.cpp index f4cc75fee..6c03b3808 100644 --- a/src/net/i2p_address.cpp +++ b/src/net/i2p_address.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/net/i2p_address.h b/src/net/i2p_address.h index 28a1118ba..5a694419d 100644 --- a/src/net/i2p_address.h +++ b/src/net/i2p_address.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/net/socks.cpp b/src/net/socks.cpp index 5a27e16f4..3463f452c 100644 --- a/src/net/socks.cpp +++ b/src/net/socks.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019, The Monero Project +// Copyright (c) 2018-2020, The Monero Project // // All rights reserved. // diff --git a/src/net/socks.h b/src/net/socks.h index 4d1d34e9e..739c972ab 100644 --- a/src/net/socks.h +++ b/src/net/socks.h @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019, The Monero Project +// Copyright (c) 2018-2020, The Monero Project // // All rights reserved. // diff --git a/src/net/socks_connect.cpp b/src/net/socks_connect.cpp index a5557f6f8..9db9d4483 100644 --- a/src/net/socks_connect.cpp +++ b/src/net/socks_connect.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/net/socks_connect.h b/src/net/socks_connect.h index 44b0fa2b3..45bb10efe 100644 --- a/src/net/socks_connect.h +++ b/src/net/socks_connect.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/net/zmq.cpp b/src/net/zmq.cpp index 7ea80b907..15560ca7e 100644 --- a/src/net/zmq.cpp +++ b/src/net/zmq.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // @@ -158,20 +158,6 @@ namespace zmq return unsigned(max_out) < added ? max_out : int(added); } }; - - template<typename F, typename... T> - expect<void> retry_op(F op, T&&... args) noexcept(noexcept(op(args...))) - { - for (;;) - { - if (0 <= op(args...)) - return success(); - - const int error = zmq_errno(); - if (error != EINTR) - return make_error_code(error); - } - } } // anonymous expect<std::string> receive(void* const socket, const int flags) diff --git a/src/net/zmq.h b/src/net/zmq.h index 65560b62e..fa4ef2fc9 100644 --- a/src/net/zmq.h +++ b/src/net/zmq.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // @@ -26,6 +26,8 @@ // 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 <memory> #include <string> #include <system_error> @@ -105,6 +107,26 @@ namespace zmq //! Unique ZMQ socket handle, calls `zmq_close` on destruction. using socket = std::unique_ptr<void, close>; + /*! Retry a ZMQ function on `EINTR` errors. `F` must return an int with + values less than 0 on error. + + \param op The ZMQ function to execute + retry + \param args Forwarded to `op`. Must be resuable in case of retry. + \return All errors except for `EINTR`. */ + template<typename F, typename... T> + expect<void> retry_op(F op, T&&... args) noexcept(noexcept(op(args...))) + { + for (;;) + { + if (0 <= op(args...)) + return success(); + + const int error = zmq_errno(); + if (error != EINTR) + return make_error_code(error); + } + } + /*! Read all parts of the next message on `socket`. Blocks until the entire next message (all parts) are read, or until `zmq_term` is called on the `zmq_context` associated with `socket`. If the context is terminated, diff --git a/src/p2p/CMakeLists.txt b/src/p2p/CMakeLists.txt index 3aecc3cf9..cfd8273b4 100644 --- a/src/p2p/CMakeLists.txt +++ b/src/p2p/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp index 58c5706de..1c5dca0d7 100644 --- a/src/p2p/net_node.cpp +++ b/src/p2p/net_node.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 57b1335eb..e81371783 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 5bd845e4f..175741146 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -604,16 +604,12 @@ namespace nodetool if (nettype == cryptonote::TESTNET) { full_addrs.insert("212.83.175.67:28080"); - full_addrs.insert("5.9.100.248:28080"); - full_addrs.insert("163.172.182.165:28080"); - full_addrs.insert("195.154.123.123:28080"); full_addrs.insert("212.83.172.165:28080"); full_addrs.insert("192.110.160.146:28080"); } else if (nettype == cryptonote::STAGENET) { full_addrs.insert("162.210.173.150:38080"); - full_addrs.insert("162.210.173.151:38080"); full_addrs.insert("192.110.160.146:38080"); } else if (nettype == cryptonote::FAKECHAIN) @@ -621,13 +617,7 @@ namespace nodetool } else { - full_addrs.insert("107.152.130.98:18080"); full_addrs.insert("212.83.175.67:18080"); - full_addrs.insert("5.9.100.248:18080"); - full_addrs.insert("163.172.182.165:18080"); - full_addrs.insert("161.67.132.39:18080"); - full_addrs.insert("198.74.231.92:18080"); - full_addrs.insert("195.154.123.123:18080"); full_addrs.insert("212.83.172.165:18080"); full_addrs.insert("192.110.160.146:18080"); full_addrs.insert("88.198.163.90:18080"); diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index 6a6100e0c..393d38e0a 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index 300181bbb..992462d0b 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h index bd5063510..37a85d526 100644 --- a/src/p2p/net_peerlist_boost_serialization.h +++ b/src/p2p/net_peerlist_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index 609661871..bbc165cfa 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/p2p/stdafx.h b/src/p2p/stdafx.h index 12a371702..a689b1c15 100644 --- a/src/p2p/stdafx.h +++ b/src/p2p/stdafx.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/platform/mingw/alloca.h b/src/platform/mingw/alloca.h index e4722af2a..b514051aa 100644 --- a/src/platform/mingw/alloca.h +++ b/src/platform/mingw/alloca.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/platform/msc/alloca.h b/src/platform/msc/alloca.h index fcf1731b0..a9ca8cc84 100644 --- a/src/platform/msc/alloca.h +++ b/src/platform/msc/alloca.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/platform/msc/inline_c.h b/src/platform/msc/inline_c.h index 23c11bd06..2a4952cff 100644 --- a/src/platform/msc/inline_c.h +++ b/src/platform/msc/inline_c.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/platform/msc/stdbool.h b/src/platform/msc/stdbool.h index 4d24995bd..445b625b4 100644 --- a/src/platform/msc/stdbool.h +++ b/src/platform/msc/stdbool.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/platform/msc/sys/param.h b/src/platform/msc/sys/param.h index e3cfb7e86..e5d208003 100644 --- a/src/platform/msc/sys/param.h +++ b/src/platform/msc/sys/param.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/ringct/CMakeLists.txt b/src/ringct/CMakeLists.txt index 0192aa931..40b2dfd55 100644 --- a/src/ringct/CMakeLists.txt +++ b/src/ringct/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2016-2019, The Monero Project +# Copyright (c) 2016-2020, The Monero Project # # All rights reserved. # diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index 6b88fd730..359dfa879 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/ringct/bulletproofs.h b/src/ringct/bulletproofs.h index 21d494834..d8a9fa4ff 100644 --- a/src/ringct/bulletproofs.h +++ b/src/ringct/bulletproofs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/ringct/rctCryptoOps.c b/src/ringct/rctCryptoOps.c index fbbf6f9bd..506f85c16 100644 --- a/src/ringct/rctCryptoOps.c +++ b/src/ringct/rctCryptoOps.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/ringct/rctCryptoOps.h b/src/ringct/rctCryptoOps.h index 2a25d13a7..dabb2606a 100644 --- a/src/ringct/rctCryptoOps.h +++ b/src/ringct/rctCryptoOps.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt index fe5e5a85b..19298c969 100644 --- a/src/rpc/CMakeLists.txt +++ b/src/rpc/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # @@ -45,8 +45,11 @@ set(daemon_messages_sources message.cpp daemon_messages.cpp) +set(rpc_pub_sources zmq_pub.cpp) + set(daemon_rpc_server_sources daemon_handler.cpp + zmq_pub.cpp zmq_server.cpp) @@ -59,8 +62,9 @@ set(rpc_headers rpc_version_str.h rpc_handler.h) -set(daemon_rpc_server_headers) +set(rpc_pub_headers zmq_pub.h) +set(daemon_rpc_server_headers) set(rpc_daemon_private_headers bootstrap_daemon.h @@ -83,6 +87,8 @@ set(daemon_rpc_server_private_headers monero_private_headers(rpc ${rpc_private_headers}) +set(rpc_pub_private_headers) + monero_private_headers(daemon_rpc_server ${daemon_rpc_server_private_headers}) @@ -97,6 +103,11 @@ monero_add_library(rpc ${rpc_headers} ${rpc_private_headers}) +monero_add_library(rpc_pub + ${rpc_pub_sources} + ${rpc_pub_headers} + ${rpc_pub_private_headers}) + monero_add_library(daemon_messages ${daemon_messages_sources} ${daemon_messages_headers} @@ -131,6 +142,14 @@ target_link_libraries(rpc PRIVATE ${EXTRA_LIBRARIES}) +target_link_libraries(rpc_pub + PUBLIC + epee + net + cryptonote_basic + serialization + ${Boost_THREAD_LIBRARY}) + target_link_libraries(daemon_messages LINK_PRIVATE cryptonote_core @@ -142,6 +161,7 @@ target_link_libraries(daemon_messages target_link_libraries(daemon_rpc_server LINK_PRIVATE rpc + rpc_pub cryptonote_core cryptonote_protocol version diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 82a7234d1..bc561abc4 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -68,6 +68,11 @@ using namespace epee; #define DEFAULT_PAYMENT_DIFFICULTY 1000 #define DEFAULT_PAYMENT_CREDITS_PER_HASH 100 +#define RESTRICTED_BLOCK_HEADER_RANGE 1000 +#define RESTRICTED_TRANSACTIONS_COUNT 100 +#define RESTRICTED_SPENT_KEY_IMAGES_COUNT 5000 +#define RESTRICTED_BLOCK_COUNT 1000 + #define RPC_TRACKER(rpc) \ PERF_TIMER(rpc); \ RPCTracker tracker(#rpc, PERF_TIMER_NAME(rpc)) @@ -265,25 +270,25 @@ namespace cryptonote { if (!m_restricted && nettype() != FAKECHAIN) { - MERROR("RPC payment enabled, but server is not restricted, anyone can adjust their balance to bypass payment"); + MFATAL("RPC payment enabled, but server is not restricted, anyone can adjust their balance to bypass payment"); return false; } cryptonote::address_parse_info info; if (!get_account_address_from_str(info, nettype(), address)) { - MERROR("Invalid payment address: " << address); + MFATAL("Invalid payment address: " << address); return false; } if (info.is_subaddress) { - MERROR("Payment address may not be a subaddress: " << address); + MFATAL("Payment address may not be a subaddress: " << address); return false; } uint64_t diff = command_line::get_arg(vm, arg_rpc_payment_difficulty); uint64_t credits = command_line::get_arg(vm, arg_rpc_payment_credits); if (diff == 0 || credits == 0) { - MERROR("Payments difficulty and/or payments credits are 0, but a payment address was given"); + MFATAL("Payments difficulty and/or payments credits are 0, but a payment address was given"); return false; } m_rpc_payment_allow_free_loopback = command_line::get_arg(vm, arg_rpc_payment_allow_free_loopback); @@ -303,7 +308,7 @@ namespace cryptonote if (!set_bootstrap_daemon(command_line::get_arg(vm, arg_bootstrap_daemon_address), command_line::get_arg(vm, arg_bootstrap_daemon_login))) { - MERROR("Failed to parse bootstrap daemon address"); + MFATAL("Failed to parse bootstrap daemon address"); return false; } @@ -639,6 +644,13 @@ namespace cryptonote if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCKS_BY_HEIGHT>(invoke_http_mode::BIN, "/getblocks_by_height.bin", req, res, r)) return r; + const bool restricted = m_restricted && ctx; + if (restricted && req.heights.size() > RESTRICTED_BLOCK_COUNT) + { + res.status = "Too many blocks requested in restricted mode"; + return true; + } + res.status = "Failed"; res.blocks.clear(); res.blocks.reserve(req.heights.size()); @@ -793,11 +805,17 @@ namespace cryptonote if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTIONS>(invoke_http_mode::JON, "/gettransactions", req, res, ok)) return ok; - CHECK_PAYMENT_MIN1(req, res, req.txs_hashes.size() * COST_PER_TX, false); - const bool restricted = m_restricted && ctx; const bool request_has_rpc_origin = ctx != NULL; + if (restricted && req.txs_hashes.size() > RESTRICTED_TRANSACTIONS_COUNT) + { + res.status = "Too many transactions requested in restricted mode"; + return true; + } + + CHECK_PAYMENT_MIN1(req, res, req.txs_hashes.size() * COST_PER_TX, false); + std::vector<crypto::hash> vh; for(const auto& tx_hex_str: req.txs_hashes) { @@ -1027,11 +1045,17 @@ namespace cryptonote if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_IS_KEY_IMAGE_SPENT>(invoke_http_mode::JON, "/is_key_image_spent", req, res, ok)) return ok; - CHECK_PAYMENT_MIN1(req, res, req.key_images.size() * COST_PER_KEY_IMAGE, false); - const bool restricted = m_restricted && ctx; const bool request_has_rpc_origin = ctx != NULL; + if (restricted && req.key_images.size() > RESTRICTED_SPENT_KEY_IMAGES_COUNT) + { + res.status = "Too many key images queried in restricted mode"; + return true; + } + + CHECK_PAYMENT_MIN1(req, res, req.key_images.size() * COST_PER_KEY_IMAGE, false); + std::vector<crypto::key_image> key_images; for(const auto& ki_hex_str: req.key_images) { @@ -2034,6 +2058,14 @@ namespace cryptonote CHECK_PAYMENT_MIN1(req, res, COST_PER_BLOCK_HEADER, false); + const bool restricted = m_restricted && ctx; + if (restricted && req.hashes.size() > RESTRICTED_BLOCK_COUNT) + { + error_resp.code = CORE_RPC_ERROR_CODE_RESTRICTED; + error_resp.message = "Too many block headers requested in restricted mode"; + return false; + } + auto get = [this](const std::string &hash, bool fill_pow_hash, block_header_response &block_header, bool restricted, epee::json_rpc::error& error_resp) -> bool { crypto::hash block_hash; bool hash_parsed = parse_hash256(hash, block_hash); @@ -2069,7 +2101,6 @@ namespace cryptonote return true; }; - const bool restricted = m_restricted && ctx; if (!req.hash.empty()) { if (!get(req.hash, req.fill_pow_hash, res.block_header, restricted, error_resp)) @@ -2101,6 +2132,14 @@ namespace cryptonote error_resp.message = "Invalid start/end heights."; return false; } + const bool restricted = m_restricted && ctx; + if (restricted && req.end_height - req.start_height > RESTRICTED_BLOCK_HEADER_RANGE) + { + error_resp.code = CORE_RPC_ERROR_CODE_RESTRICTED; + error_resp.message = "Too many block headers requested."; + return false; + } + CHECK_PAYMENT_MIN1(req, res, (req.end_height - req.start_height + 1) * COST_PER_BLOCK_HEADER, false); for (uint64_t h = req.start_height; h <= req.end_height; ++h) { @@ -2127,7 +2166,6 @@ namespace cryptonote return false; } res.headers.push_back(block_header_response()); - const bool restricted = m_restricted && ctx; bool response_filled = fill_block_header_response(blk, false, block_height, block_hash, res.headers.back(), req.fill_pow_hash && !restricted); if (!response_filled) { @@ -2778,7 +2816,7 @@ namespace cryptonote crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); cryptonote::blobdata txblob; - if (!m_core.get_pool_transaction(txid, txblob, relay_category::legacy)) + if (m_core.get_pool_transaction(txid, txblob, relay_category::legacy)) { NOTIFY_NEW_TRANSACTIONS::request r; r.txs.push_back(std::move(txblob)); diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 3c404bbd8..dcf6b4e4b 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index a3c187c24..09cd67d7d 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/rpc/core_rpc_server_error_codes.h index 2fd42f43f..1458049ab 100644 --- a/src/rpc/core_rpc_server_error_codes.h +++ b/src/rpc/core_rpc_server_error_codes.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -48,6 +48,7 @@ #define CORE_RPC_ERROR_CODE_PAYMENT_TOO_LOW -16 #define CORE_RPC_ERROR_CODE_DUPLICATE_PAYMENT -17 #define CORE_RPC_ERROR_CODE_STALE_PAYMENT -18 +#define CORE_RPC_ERROR_CODE_RESTRICTED -19 static inline const char *get_rpc_server_error_message(int64_t code) { @@ -70,6 +71,7 @@ static inline const char *get_rpc_server_error_message(int64_t code) case CORE_RPC_ERROR_CODE_PAYMENT_TOO_LOW: return "Payment too low"; case CORE_RPC_ERROR_CODE_DUPLICATE_PAYMENT: return "Duplicate payment"; case CORE_RPC_ERROR_CODE_STALE_PAYMENT: return "Stale payment"; + case CORE_RPC_ERROR_CODE_RESTRICTED: return "Parameters beyond restricted allowance"; default: MERROR("Unknown error: " << code); return "Unknown error"; } } diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 1aad82159..0a26a4d5d 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -182,6 +182,7 @@ namespace rpc for (const auto& blob : it->second) { bwt.transactions.emplace_back(); + bwt.transactions.back().pruned = req.prune; if (!parse_and_validate_tx_from_blob(blob.second, bwt.transactions.back())) { res.blocks.clear(); @@ -905,13 +906,13 @@ namespace rpc return true; } - epee::byte_slice DaemonHandler::handle(const std::string& request) + epee::byte_slice DaemonHandler::handle(std::string&& request) { MDEBUG("Handling RPC request: " << request); try { - FullMessage req_full(request, true); + FullMessage req_full(std::move(request), true); const std::string request_type = req_full.getRequestType(); const auto matched_handler = std::lower_bound(std::begin(handlers), std::end(handlers), request_type); diff --git a/src/rpc/daemon_handler.h b/src/rpc/daemon_handler.h index b797b1155..31c4f3ec4 100644 --- a/src/rpc/daemon_handler.h +++ b/src/rpc/daemon_handler.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -133,7 +133,7 @@ class DaemonHandler : public RpcHandler void handle(const GetOutputDistribution::Request& req, GetOutputDistribution::Response& res); - epee::byte_slice handle(const std::string& request) override final; + epee::byte_slice handle(std::string&& request) override final; private: diff --git a/src/rpc/daemon_messages.cpp b/src/rpc/daemon_messages.cpp index 22f73472d..887486f77 100644 --- a/src/rpc/daemon_messages.cpp +++ b/src/rpc/daemon_messages.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h index 64ea3e9d4..dea37c9a6 100644 --- a/src/rpc/daemon_messages.h +++ b/src/rpc/daemon_messages.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/daemon_rpc_version.h b/src/rpc/daemon_rpc_version.h index b3eb9699b..2955d5449 100644 --- a/src/rpc/daemon_rpc_version.h +++ b/src/rpc/daemon_rpc_version.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // @@ -35,7 +35,7 @@ namespace rpc { static const uint32_t DAEMON_RPC_VERSION_ZMQ_MINOR = 0; -static const uint32_t DAEMON_RPC_VERSION_ZMQ_MAJOR = 1; +static const uint32_t DAEMON_RPC_VERSION_ZMQ_MAJOR = 2; static const uint32_t DAEMON_RPC_VERSION_ZMQ = DAEMON_RPC_VERSION_ZMQ_MINOR + (DAEMON_RPC_VERSION_ZMQ_MAJOR << 16); diff --git a/src/rpc/fwd.h b/src/rpc/fwd.h new file mode 100644 index 000000000..72537f5a5 --- /dev/null +++ b/src/rpc/fwd.h @@ -0,0 +1,37 @@ +// Copyright (c) 2019-2020, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +namespace cryptonote +{ + namespace listener + { + class zmq_pub; + } +} diff --git a/src/rpc/instanciations.cpp b/src/rpc/instanciations.cpp index 94fdb5f18..ac992d4ad 100644 --- a/src/rpc/instanciations.cpp +++ b/src/rpc/instanciations.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/message.cpp b/src/rpc/message.cpp index fffb44921..f6c6b887d 100644 --- a/src/rpc/message.cpp +++ b/src/rpc/message.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // @@ -65,8 +65,6 @@ const rapidjson::Value& get_method_field(const rapidjson::Value& src) void Message::toJson(rapidjson::Writer<epee::byte_stream>& dest) const { dest.StartObject(); - INSERT_INTO_JSON_OBJECT(dest, status, status); - INSERT_INTO_JSON_OBJECT(dest, error_details, error_details); INSERT_INTO_JSON_OBJECT(dest, rpc_version, DAEMON_RPC_VERSION_ZMQ); doToJson(dest); dest.EndObject(); @@ -74,14 +72,15 @@ void Message::toJson(rapidjson::Writer<epee::byte_stream>& dest) const void Message::fromJson(const rapidjson::Value& val) { - GET_FROM_JSON_OBJECT(val, status, status); - GET_FROM_JSON_OBJECT(val, error_details, error_details); GET_FROM_JSON_OBJECT(val, rpc_version, rpc_version); } -FullMessage::FullMessage(const std::string& json_string, bool request) +FullMessage::FullMessage(std::string&& json_string, bool request) + : contents(std::move(json_string)), doc() { - doc.Parse(json_string.c_str()); + /* Insitu parsing does not copy data from `contents` to DOM, + accelerating string heavy content. */ + doc.ParseInsitu(std::addressof(contents[0])); if (doc.HasParseError() || !doc.IsObject()) { throw cryptonote::json::PARSE_FAIL(); diff --git a/src/rpc/message.h b/src/rpc/message.h index 5c369cdfc..04bf1a111 100644 --- a/src/rpc/message.h +++ b/src/rpc/message.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // @@ -72,9 +72,7 @@ namespace rpc public: ~FullMessage() { } - FullMessage(FullMessage&& rhs) noexcept : doc(std::move(rhs.doc)) { } - - FullMessage(const std::string& json_string, bool request=false); + FullMessage(std::string&& json_string, bool request=false); std::string getRequestType() const; @@ -91,10 +89,13 @@ namespace rpc private: FullMessage() = default; + FullMessage(const FullMessage&) = delete; + FullMessage& operator=(const FullMessage&) = delete; FullMessage(const std::string& request, Message* message); FullMessage(Message* message); + std::string contents; rapidjson::Document doc; }; diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h index e64f5f163..085148d8a 100644 --- a/src/rpc/message_data_structs.h +++ b/src/rpc/message_data_structs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/rpc_args.h b/src/rpc/rpc_args.h index ac6eb2744..4cf4279b2 100644 --- a/src/rpc/rpc_args.h +++ b/src/rpc/rpc_args.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/rpc_handler.h b/src/rpc/rpc_handler.h index 9a1c3fc12..9757fc462 100644 --- a/src/rpc/rpc_handler.h +++ b/src/rpc/rpc_handler.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // @@ -55,7 +55,7 @@ class RpcHandler RpcHandler() { } virtual ~RpcHandler() { } - virtual epee::byte_slice handle(const std::string& request) = 0; + virtual epee::byte_slice handle(std::string&& request) = 0; static boost::optional<output_distribution_data> get_output_distribution(const std::function<bool(uint64_t, uint64_t, uint64_t, uint64_t&, std::vector<uint64_t>&, uint64_t&)> &f, uint64_t amount, uint64_t from_height, uint64_t to_height, const std::function<crypto::hash(uint64_t)> &get_hash, bool cumulative, uint64_t blockchain_height); diff --git a/src/rpc/rpc_payment.cpp b/src/rpc/rpc_payment.cpp index 2b9c19f57..edc8f0dda 100644 --- a/src/rpc/rpc_payment.cpp +++ b/src/rpc/rpc_payment.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019, The Monero Project +// Copyright (c) 2018-2020, The Monero Project // // All rights reserved. // @@ -92,6 +92,7 @@ namespace cryptonote uint64_t rpc_payment::balance(const crypto::public_key &client, int64_t delta) { + boost::lock_guard<boost::mutex> lock(mutex); client_info &info = m_client_info[client]; // creates if not found uint64_t credits = info.credits; if (delta > 0 && credits > std::numeric_limits<uint64_t>::max() - delta) @@ -107,6 +108,7 @@ namespace cryptonote bool rpc_payment::pay(const crypto::public_key &client, uint64_t ts, uint64_t payment, const std::string &rpc, bool same_ts, uint64_t &credits) { + boost::lock_guard<boost::mutex> lock(mutex); client_info &info = m_client_info[client]; // creates if not found if (ts < info.last_request_timestamp || (ts == info.last_request_timestamp && !same_ts)) { @@ -130,6 +132,7 @@ namespace cryptonote bool rpc_payment::get_info(const crypto::public_key &client, const std::function<bool(const cryptonote::blobdata&, cryptonote::block&, uint64_t &seed_height, crypto::hash &seed_hash)> &get_block_template, cryptonote::blobdata &hashing_blob, uint64_t &seed_height, crypto::hash &seed_hash, const crypto::hash &top, uint64_t &diff, uint64_t &credits_per_hash_found, uint64_t &credits, uint32_t &cookie) { + boost::lock_guard<boost::mutex> lock(mutex); client_info &info = m_client_info[client]; // creates if not found const uint64_t now = time(NULL); bool need_template = top != info.top || now >= info.block_template_update_time + STALE_THRESHOLD; @@ -180,6 +183,7 @@ namespace cryptonote bool rpc_payment::submit_nonce(const crypto::public_key &client, uint32_t nonce, const crypto::hash &top, int64_t &error_code, std::string &error_message, uint64_t &credits, crypto::hash &hash, cryptonote::block &block, uint32_t cookie, bool &stale) { + boost::lock_guard<boost::mutex> lock(mutex); client_info &info = m_client_info[client]; // creates if not found if (cookie != info.cookie && cookie != info.cookie - 1) { @@ -272,6 +276,7 @@ namespace cryptonote bool rpc_payment::foreach(const std::function<bool(const crypto::public_key &client, const client_info &info)> &f) const { + boost::lock_guard<boost::mutex> lock(mutex); for (std::unordered_map<crypto::public_key, client_info>::const_iterator i = m_client_info.begin(); i != m_client_info.end(); ++i) { if (!f(i->first, i->second)) @@ -283,8 +288,9 @@ namespace cryptonote bool rpc_payment::load(std::string directory) { TRY_ENTRY(); + boost::lock_guard<boost::mutex> lock(mutex); m_directory = std::move(directory); - std::string state_file_path = directory + "/" + RPC_PAYMENTS_DATA_FILENAME; + std::string state_file_path = m_directory + "/" + RPC_PAYMENTS_DATA_FILENAME; MINFO("loading rpc payments data from " << state_file_path); std::ifstream data; data.open(state_file_path, std::ios_base::binary | std::ios_base::in); @@ -313,6 +319,7 @@ namespace cryptonote bool rpc_payment::store(const std::string &directory_) const { TRY_ENTRY(); + boost::lock_guard<boost::mutex> lock(mutex); const std::string &directory = directory_.empty() ? m_directory : directory_; MDEBUG("storing rpc payments data to " << directory); if (!tools::create_directories_if_necessary(directory)) @@ -345,6 +352,7 @@ namespace cryptonote unsigned int rpc_payment::flush_by_age(time_t seconds) { + boost::lock_guard<boost::mutex> lock(mutex); unsigned int count = 0; const time_t now = time(NULL); time_t seconds0 = seconds; @@ -358,7 +366,7 @@ namespace cryptonote for (std::unordered_map<crypto::public_key, client_info>::iterator i = m_client_info.begin(); i != m_client_info.end(); ) { std::unordered_map<crypto::public_key, client_info>::iterator j = i++; - const time_t t = std::max(j->second.last_request_timestamp, j->second.update_time); + const time_t t = std::max(j->second.last_request_timestamp / 1000000, j->second.update_time); const bool erase = t < ((j->second.credits == 0) ? threshold0 : threshold); if (erase) { @@ -372,6 +380,7 @@ namespace cryptonote uint64_t rpc_payment::get_hashes(unsigned int seconds) const { + boost::lock_guard<boost::mutex> lock(mutex); const uint64_t now = time(NULL); uint64_t hashes = 0; for (std::map<uint64_t, uint64_t>::const_reverse_iterator i = m_hashrate.crbegin(); i != m_hashrate.crend(); ++i) @@ -385,6 +394,7 @@ namespace cryptonote void rpc_payment::prune_hashrate(unsigned int seconds) { + boost::lock_guard<boost::mutex> lock(mutex); const uint64_t now = time(NULL); std::map<uint64_t, uint64_t>::iterator i; for (i = m_hashrate.begin(); i != m_hashrate.end(); ++i) diff --git a/src/rpc/rpc_payment.h b/src/rpc/rpc_payment.h index f6832fd34..dcd43f8d5 100644 --- a/src/rpc/rpc_payment.h +++ b/src/rpc/rpc_payment.h @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019, The Monero Project +// Copyright (c) 2018-2020, The Monero Project // // All rights reserved. // @@ -31,6 +31,7 @@ #include <string> #include <unordered_set> #include <unordered_map> +#include <boost/thread/mutex.hpp> #include <boost/serialization/version.hpp> #include "cryptonote_basic/blobdatatype.h" #include "cryptonote_basic/cryptonote_basic.h" @@ -139,6 +140,7 @@ namespace cryptonote uint64_t m_nonces_stale; uint64_t m_nonces_bad; uint64_t m_nonces_dupe; + mutable boost::mutex mutex; }; } diff --git a/src/rpc/rpc_payment_costs.h b/src/rpc/rpc_payment_costs.h index 3b27bf286..9bc0f8a2b 100644 --- a/src/rpc/rpc_payment_costs.h +++ b/src/rpc/rpc_payment_costs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/rpc_payment_signature.cpp b/src/rpc/rpc_payment_signature.cpp index 559f3a1e9..a5dc21717 100644 --- a/src/rpc/rpc_payment_signature.cpp +++ b/src/rpc/rpc_payment_signature.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019, The Monero Project +// Copyright (c) 2018-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/rpc_payment_signature.h b/src/rpc/rpc_payment_signature.h index 4a2fe2ea3..bf7ee061f 100644 --- a/src/rpc/rpc_payment_signature.h +++ b/src/rpc/rpc_payment_signature.h @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019, The Monero Project +// Copyright (c) 2018-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/rpc_version_str.cpp b/src/rpc/rpc_version_str.cpp index c60cf4891..b8b231c16 100644 --- a/src/rpc/rpc_version_str.cpp +++ b/src/rpc/rpc_version_str.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/rpc_version_str.h b/src/rpc/rpc_version_str.h index 930c807d2..b0cb47c5f 100644 --- a/src/rpc/rpc_version_str.h +++ b/src/rpc/rpc_version_str.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/rpc/zmq_pub.cpp b/src/rpc/zmq_pub.cpp new file mode 100644 index 000000000..0dffffac6 --- /dev/null +++ b/src/rpc/zmq_pub.cpp @@ -0,0 +1,478 @@ +// Copyright (c) 2020, 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 "zmq_pub.h" + +#include <algorithm> +#include <boost/range/adaptor/filtered.hpp> +#include <boost/range/adaptor/transformed.hpp> +#include <boost/thread/locks.hpp> +#include <cassert> +#include <cstdint> +#include <cstring> +#include <rapidjson/document.h> +#include <rapidjson/stringbuffer.h> +#include <rapidjson/writer.h> +#include <stdexcept> +#include <string> +#include <utility> + +#include "common/expect.h" +#include "crypto/crypto.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "cryptonote_basic/events.h" +#include "misc_log_ex.h" +#include "serialization/json_object.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "net.zmq" + +namespace +{ + constexpr const char txpool_signal[] = "tx_signal"; + + using chain_writer = void(epee::byte_stream&, std::uint64_t, epee::span<const cryptonote::block>); + using txpool_writer = void(epee::byte_stream&, epee::span<const cryptonote::txpool_event>); + + template<typename F> + struct context + { + char const* const name; + F* generate_pub; + }; + + template<typename T> + bool operator<(const context<T>& lhs, const context<T>& rhs) noexcept + { + return std::strcmp(lhs.name, rhs.name) < 0; + } + + template<typename T> + bool operator<(const context<T>& lhs, const boost::string_ref rhs) noexcept + { + return lhs.name < rhs; + } + + struct is_valid + { + bool operator()(const cryptonote::txpool_event& event) const noexcept + { + return event.res; + } + }; + + template<typename T, std::size_t N> + void verify_sorted(const std::array<context<T>, N>& elems, const char* name) + { + auto unsorted = std::is_sorted_until(elems.begin(), elems.end()); + if (unsorted != elems.end()) + throw std::logic_error{name + std::string{" array is not properly sorted, see: "} + unsorted->name}; + } + + void write_header(epee::byte_stream& buf, const boost::string_ref name) + { + buf.write(name.data(), name.size()); + buf.put(':'); + } + + //! \return `name:...` where `...` is JSON and `name` is directly copied (no quotes - not JSON). + template<typename T> + void json_pub(epee::byte_stream& buf, const T value) + { + rapidjson::Writer<epee::byte_stream> dest{buf}; + using cryptonote::json::toJsonValue; + toJsonValue(dest, value); + } + + //! Object for "minimal" block serialization + struct minimal_chain + { + const std::uint64_t height; + const epee::span<const cryptonote::block> blocks; + }; + + //! Object for "minimal" tx serialization + struct minimal_txpool + { + const cryptonote::transaction& tx; + }; + + void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const minimal_chain self) + { + namespace adapt = boost::adaptors; + + const auto to_block_id = [](const cryptonote::block& bl) + { + crypto::hash id; + if (!get_block_hash(bl, id)) + MERROR("ZMQ/Pub failure: get_block_hash"); + return id; + }; + + assert(!self.blocks.empty()); // checked in zmq_pub::send_chain_main + + dest.StartObject(); + INSERT_INTO_JSON_OBJECT(dest, first_height, self.height); + INSERT_INTO_JSON_OBJECT(dest, first_prev_id, self.blocks[0].prev_id); + INSERT_INTO_JSON_OBJECT(dest, ids, (self.blocks | adapt::transformed(to_block_id))); + dest.EndObject(); + } + + void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const minimal_txpool self) + { + crypto::hash id{}; + std::size_t blob_size = 0; + if (!get_transaction_hash(self.tx, id, blob_size)) + { + MERROR("ZMQ/Pub failure: get_transaction_hash"); + return; + } + + dest.StartObject(); + INSERT_INTO_JSON_OBJECT(dest, id, id); + INSERT_INTO_JSON_OBJECT(dest, blob_size, blob_size); + dest.EndObject(); + } + + void json_full_chain(epee::byte_stream& buf, const std::uint64_t height, const epee::span<const cryptonote::block> blocks) + { + json_pub(buf, blocks); + } + + void json_minimal_chain(epee::byte_stream& buf, const std::uint64_t height, const epee::span<const cryptonote::block> blocks) + { + json_pub(buf, minimal_chain{height, blocks}); + } + + // boost::adaptors are in place "views" - no copy/move takes place + // moving transactions (via sort, etc.), is expensive! + + void json_full_txpool(epee::byte_stream& buf, epee::span<const cryptonote::txpool_event> txes) + { + namespace adapt = boost::adaptors; + const auto to_full_tx = [](const cryptonote::txpool_event& event) + { + return event.tx; + }; + json_pub(buf, (txes | adapt::filtered(is_valid{}) | adapt::transformed(to_full_tx))); + } + + void json_minimal_txpool(epee::byte_stream& buf, epee::span<const cryptonote::txpool_event> txes) + { + namespace adapt = boost::adaptors; + const auto to_minimal_tx = [](const cryptonote::txpool_event& event) + { + return minimal_txpool{event.tx}; + }; + json_pub(buf, (txes | adapt::filtered(is_valid{}) | adapt::transformed(to_minimal_tx))); + } + + constexpr const std::array<context<chain_writer>, 2> chain_contexts = + {{ + {u8"json-full-chain_main", json_full_chain}, + {u8"json-minimal-chain_main", json_minimal_chain} + }}; + + constexpr const std::array<context<txpool_writer>, 2> txpool_contexts = + {{ + {u8"json-full-txpool_add", json_full_txpool}, + {u8"json-minimal-txpool_add", json_minimal_txpool} + }}; + + template<typename T, std::size_t N> + epee::span<const context<T>> get_range(const std::array<context<T>, N>& contexts, const boost::string_ref value) + { + const auto not_prefix = [](const boost::string_ref lhs, const context<T>& rhs) + { + return !(boost::string_ref{rhs.name}.starts_with(lhs)); + }; + + const auto lower = std::lower_bound(contexts.begin(), contexts.end(), value); + const auto upper = std::upper_bound(lower, contexts.end(), value, not_prefix); + return {lower, std::size_t(upper - lower)}; + } + + template<std::size_t N, typename T> + void add_subscriptions(std::array<std::size_t, N>& subs, const epee::span<const context<T>> range, context<T> const* const first) + { + assert(range.size() <= N); + assert(range.begin() - first <= N - range.size()); + + for (const auto& ctx : range) + { + const std::size_t i = std::addressof(ctx) - first; + subs[i] = std::min(std::numeric_limits<std::size_t>::max() - 1, subs[i]) + 1; + } + } + + template<std::size_t N, typename T> + void remove_subscriptions(std::array<std::size_t, N>& subs, const epee::span<const context<T>> range, context<T> const* const first) + { + assert(range.size() <= N); + assert(range.begin() - first <= N - range.size()); + + for (const auto& ctx : range) + { + const std::size_t i = std::addressof(ctx) - first; + subs[i] = std::max(std::size_t(1), subs[i]) - 1; + } + } + + template<std::size_t N, typename T, typename... U> + std::array<epee::byte_slice, N> make_pubs(const std::array<std::size_t, N>& subs, const std::array<context<T>, N>& contexts, U&&... args) + { + epee::byte_stream buf{}; + + std::size_t last_offset = 0; + std::array<std::size_t, N> offsets{{}}; + for (std::size_t i = 0; i < N; ++i) + { + if (subs[i]) + { + write_header(buf, contexts[i].name); + contexts[i].generate_pub(buf, std::forward<U>(args)...); + offsets[i] = buf.size() - last_offset; + last_offset = buf.size(); + } + } + + epee::byte_slice bytes{std::move(buf)}; + std::array<epee::byte_slice, N> out; + for (std::size_t i = 0; i < N; ++i) + out[i] = bytes.take_slice(offsets[i]); + + return out; + } + + template<std::size_t N> + std::size_t send_messages(void* const socket, std::array<epee::byte_slice, N>& messages) + { + std::size_t count = 0; + for (epee::byte_slice& message : messages) + { + if (!message.empty()) + { + const expect<void> sent = net::zmq::send(std::move(message), socket, ZMQ_DONTWAIT); + if (!sent) + MERROR("Failed to send ZMQ/Pub message: " << sent.error().message()); + else + ++count; + } + } + return count; + } + + expect<bool> relay_block_pub(void* const relay, void* const pub) noexcept + { + zmq_msg_t msg; + zmq_msg_init(std::addressof(msg)); + MONERO_CHECK(net::zmq::retry_op(zmq_msg_recv, std::addressof(msg), relay, ZMQ_DONTWAIT)); + + const boost::string_ref payload{ + reinterpret_cast<const char*>(zmq_msg_data(std::addressof(msg))), + zmq_msg_size(std::addressof(msg)) + }; + + if (payload == txpool_signal) + { + zmq_msg_close(std::addressof(msg)); + return false; + } + + // forward block messages (serialized on P2P thread for now) + const expect<void> sent = net::zmq::retry_op(zmq_msg_send, std::addressof(msg), pub, ZMQ_DONTWAIT); + if (!sent) + { + zmq_msg_close(std::addressof(msg)); + return sent.error(); + } + return true; + } +} // anonymous + +namespace cryptonote { namespace listener +{ + +zmq_pub::zmq_pub(void* context) + : relay_(), + chain_subs_{{0}}, + txpool_subs_{{0}}, + sync_() +{ + if (!context) + throw std::logic_error{"ZMQ context cannot be NULL"}; + + verify_sorted(chain_contexts, "chain_contexts"); + verify_sorted(txpool_contexts, "txpool_contexts"); + + relay_.reset(zmq_socket(context, ZMQ_PAIR)); + if (!relay_) + MONERO_ZMQ_THROW("Failed to create relay socket"); + if (zmq_connect(relay_.get(), relay_endpoint()) != 0) + MONERO_ZMQ_THROW("Failed to connect relay socket"); +} + +zmq_pub::~zmq_pub() +{} + +bool zmq_pub::sub_request(boost::string_ref message) +{ + if (!message.empty()) + { + const char tag = message[0]; + message.remove_prefix(1); + + const auto chain_range = get_range(chain_contexts, message); + const auto txpool_range = get_range(txpool_contexts, message); + + if (!chain_range.empty() || !txpool_range.empty()) + { + MDEBUG("Client " << (tag ? "subscribed" : "unsubscribed") << " to " << + chain_range.size() << " chain topic(s) and " << txpool_range.size() << " txpool topic(s)"); + + const boost::lock_guard<boost::mutex> lock{sync_}; + switch (tag) + { + case 0: + remove_subscriptions(chain_subs_, chain_range, chain_contexts.begin()); + remove_subscriptions(txpool_subs_, txpool_range, txpool_contexts.begin()); + return true; + case 1: + add_subscriptions(chain_subs_, chain_range, chain_contexts.begin()); + add_subscriptions(txpool_subs_, txpool_range, txpool_contexts.begin()); + return true; + default: + break; + } + } + } + MERROR("Invalid ZMQ/Sub message"); + return false; +} + +bool zmq_pub::relay_to_pub(void* const relay, void* const pub) +{ + const expect<bool> relayed = relay_block_pub(relay, pub); + if (!relayed) + { + MERROR("Error relaying ZMQ/Pub: " << relayed.error().message()); + return false; + } + + if (!*relayed) + { + std::array<std::size_t, 2> subs; + std::vector<cryptonote::txpool_event> events; + { + const boost::lock_guard<boost::mutex> lock{sync_}; + if (txes_.empty()) + return false; + + subs = txpool_subs_; + events = std::move(txes_.front()); + txes_.pop_front(); + } + auto messages = make_pubs(subs, txpool_contexts, epee::to_span(events)); + send_messages(pub, messages); + MDEBUG("Sent txpool ZMQ/Pub"); + } + else + MDEBUG("Sent chain_main ZMQ/Pub"); + + return true; +} + +std::size_t zmq_pub::send_chain_main(const std::uint64_t height, const epee::span<const cryptonote::block> blocks) +{ + if (blocks.empty()) + return 0; + + /* Block format only sends one block at a time - multiple block notifications + are less common and only occur on rollbacks. */ + + boost::unique_lock<boost::mutex> guard{sync_}; + + const auto subs_copy = chain_subs_; + guard.unlock(); + + for (const std::size_t sub : subs_copy) + { + if (sub) + { + /* cryptonote_core/blockchain.cpp cannot "give" us the block like core + does for txpool events. Since copying the block is expensive anyway, + serialization is done right here on the p2p thread (for now). */ + + auto messages = make_pubs(subs_copy, chain_contexts, height, blocks); + guard.lock(); + return send_messages(relay_.get(), messages); + } + } + return 0; +} + +std::size_t zmq_pub::send_txpool_add(std::vector<txpool_event> txes) +{ + if (txes.empty()) + return 0; + + const boost::lock_guard<boost::mutex> lock{sync_}; + for (const std::size_t sub : txpool_subs_) + { + if (sub) + { + const expect<void> sent = net::zmq::retry_op(zmq_send_const, relay_.get(), txpool_signal, sizeof(txpool_signal) - 1, ZMQ_DONTWAIT); + if (sent) + txes_.emplace_back(std::move(txes)); + else + MERROR("ZMQ/Pub failure, relay queue error: " << sent.error().message()); + return bool(sent); + } + } + return 0; +} + +void zmq_pub::chain_main::operator()(const std::uint64_t height, epee::span<const cryptonote::block> blocks) const +{ + const std::shared_ptr<zmq_pub> self = self_.lock(); + if (self) + self->send_chain_main(height, blocks); + else + MERROR("Unable to send ZMQ/Pub - ZMQ server destroyed"); +} + +void zmq_pub::txpool_add::operator()(std::vector<cryptonote::txpool_event> txes) const +{ + const std::shared_ptr<zmq_pub> self = self_.lock(); + if (self) + self->send_txpool_add(std::move(txes)); + else + MERROR("Unable to send ZMQ/Pub - ZMQ server destroyed"); +} + +}} diff --git a/src/rpc/zmq_pub.h b/src/rpc/zmq_pub.h new file mode 100644 index 000000000..02e6b8103 --- /dev/null +++ b/src/rpc/zmq_pub.h @@ -0,0 +1,110 @@ +// Copyright (c) 2020, 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 <array> +#include <boost/thread/mutex.hpp> +#include <boost/utility/string_ref.hpp> +#include <cstdint> +#include <deque> +#include <memory> +#include <vector> + +#include "cryptonote_basic/fwd.h" +#include "net/zmq.h" +#include "span.h" + +namespace cryptonote { namespace listener +{ +/*! \brief Sends ZMQ PUB messages on cryptonote events + + Clients must ensure that all transaction(s) are notified before any blocks + they are contained in, and must ensure that each block is notified in chain + order. An external lock **must** be held by clients during the entire + txpool check and notification sequence and (a possibly second) lock is held + during the entire block check and notification sequence. Otherwise, events + could be sent in a different order than processed. */ +class zmq_pub +{ + /* Each socket has its own internal queue. So we can only use one socket, else + the messages being published are not guaranteed to be in the same order + pushed. */ + + net::zmq::socket relay_; + std::deque<std::vector<txpool_event>> txes_; + std::array<std::size_t, 2> chain_subs_; + std::array<std::size_t, 2> txpool_subs_; + boost::mutex sync_; //!< Synchronizes counts in `*_subs_` arrays. + + public: + //! \return Name of ZMQ_PAIR endpoint for pub notifications + static constexpr const char* relay_endpoint() noexcept { return "inproc://pub_relay"; } + + explicit zmq_pub(void* context); + + zmq_pub(const zmq_pub&) = delete; + zmq_pub(zmq_pub&&) = delete; + + ~zmq_pub(); + + zmq_pub& operator=(const zmq_pub&) = delete; + zmq_pub& operator=(zmq_pub&&) = delete; + + //! Process a client subscription request (from XPUB sockets). Thread-safe. + bool sub_request(const boost::string_ref message); + + /*! Forward ZMQ messages sent to `relay` via `send_chain_main` or + `send_txpool_add` to `pub`. Used by `ZmqServer`. */ + bool relay_to_pub(void* relay, void* pub); + + /*! Send a `ZMQ_PUB` notification for a change to the main chain. + Thread-safe. + \return Number of ZMQ messages sent to relay. */ + std::size_t send_chain_main(std::uint64_t height, epee::span<const cryptonote::block> blocks); + + /*! Send a `ZMQ_PUB` notification for new tx(es) being added to the local + pool. Thread-safe. + \return Number of ZMQ messages sent to relay. */ + std::size_t send_txpool_add(std::vector<cryptonote::txpool_event> txes); + + //! Callable for `send_chain_main` with weak ownership to `zmq_pub` object. + struct chain_main + { + std::weak_ptr<zmq_pub> self_; + void operator()(std::uint64_t height, epee::span<const cryptonote::block> blocks) const; + }; + + //! Callable for `send_txpool_add` with weak ownership to `zmq_pub` object. + struct txpool_add + { + std::weak_ptr<zmq_pub> self_; + void operator()(std::vector<cryptonote::txpool_event> txes) const; + }; + }; +}} diff --git a/src/rpc/zmq_server.cpp b/src/rpc/zmq_server.cpp index 0d595539d..4028df96a 100644 --- a/src/rpc/zmq_server.cpp +++ b/src/rpc/zmq_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // @@ -29,10 +29,16 @@ #include "zmq_server.h" #include <chrono> -#include <cstdint> +#include <cstring> +#include <utility> +#include <stdexcept> #include <system_error> #include "byte_slice.h" +#include "rpc/zmq_pub.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "net.zmq" namespace cryptonote { @@ -42,14 +48,57 @@ namespace constexpr const int num_zmq_threads = 1; constexpr const std::int64_t max_message_size = 10 * 1024 * 1024; // 10 MiB constexpr const std::chrono::seconds linger_timeout{2}; // wait period for pending out messages -} + + net::zmq::socket init_socket(void* context, int type, epee::span<const std::string> addresses) + { + if (context == nullptr) + throw std::logic_error{"NULL context provided"}; + + net::zmq::socket out{}; + out.reset(zmq_socket(context, type)); + if (!out) + { + MONERO_LOG_ZMQ_ERROR("Failed to create ZMQ socket"); + return nullptr; + } + + if (zmq_setsockopt(out.get(), ZMQ_MAXMSGSIZE, std::addressof(max_message_size), sizeof(max_message_size)) != 0) + { + MONERO_LOG_ZMQ_ERROR("Failed to set maximum incoming message size"); + return nullptr; + } + + static constexpr const int linger_value = std::chrono::milliseconds{linger_timeout}.count(); + if (zmq_setsockopt(out.get(), ZMQ_LINGER, std::addressof(linger_value), sizeof(linger_value)) != 0) + { + MONERO_LOG_ZMQ_ERROR("Failed to set linger timeout"); + return nullptr; + } + + for (const std::string& address : addresses) + { + if (zmq_bind(out.get(), address.c_str()) < 0) + { + MONERO_LOG_ZMQ_ERROR("ZMQ bind failed"); + return nullptr; + } + MINFO("ZMQ now listening at " << address); + } + + return out; + } +} // anonymous namespace rpc { ZmqServer::ZmqServer(RpcHandler& h) : handler(h), - context(zmq_init(num_zmq_threads)) + context(zmq_init(num_zmq_threads)), + rep_socket(nullptr), + pub_socket(nullptr), + relay_socket(nullptr), + shared_state(nullptr) { if (!context) MONERO_ZMQ_THROW("Unable to create ZMQ context"); @@ -64,22 +113,59 @@ void ZmqServer::serve() try { // socket must close before `zmq_term` will exit. - const net::zmq::socket socket = std::move(rep_socket); - if (!socket) + const net::zmq::socket rep = std::move(rep_socket); + const net::zmq::socket pub = std::move(pub_socket); + const net::zmq::socket relay = std::move(relay_socket); + const std::shared_ptr<listener::zmq_pub> state = std::move(shared_state); + + const unsigned init_count = unsigned(bool(pub)) + bool(relay) + bool(state); + if (!rep || (init_count && init_count != 3)) { - MERROR("ZMQ RPC server reply socket is null"); + MERROR("ZMQ RPC server socket is null"); return; } + MINFO("ZMQ Server started"); + + const int read_flags = pub ? ZMQ_DONTWAIT : 0; + std::array<zmq_pollitem_t, 3> sockets = + {{ + {relay.get(), 0, ZMQ_POLLIN, 0}, + {pub.get(), 0, ZMQ_POLLIN, 0}, + {rep.get(), 0, ZMQ_POLLIN, 0} + }}; + + /* This uses XPUB to watch for subscribers, to reduce CPU cycles for + serialization when the data will be dropped. This is important for block + serialization, which is done on the p2p threads currently (see + zmq_pub.cpp). + + XPUB sockets are not thread-safe, so the p2p thread cannot write into + the socket while we read here for subscribers. A ZMQ_PAIR socket is + used for inproc notification. No data is every copied to kernel, it is + all userspace messaging. */ + while (1) { - const std::string message = MONERO_UNWRAP(net::zmq::receive(socket.get())); - MDEBUG("Received RPC request: \"" << message << "\""); - epee::byte_slice response = handler.handle(message); + if (pub) + MONERO_UNWRAP(net::zmq::retry_op(zmq_poll, sockets.data(), sockets.size(), -1)); - const boost::string_ref response_view{reinterpret_cast<const char*>(response.data()), response.size()}; - MDEBUG("Sending RPC reply: \"" << response_view << "\""); - MONERO_UNWRAP(net::zmq::send(std::move(response), socket.get())); + if (sockets[0].revents) + state->relay_to_pub(relay.get(), pub.get()); + + if (sockets[1].revents) + state->sub_request(MONERO_UNWRAP(net::zmq::receive(pub.get(), ZMQ_DONTWAIT))); + + if (!pub || sockets[2].revents) + { + std::string message = MONERO_UNWRAP(net::zmq::receive(rep.get(), read_flags)); + MDEBUG("Received RPC request: \"" << message << "\""); + epee::byte_slice response = handler.handle(std::move(message)); + + const boost::string_ref response_view{reinterpret_cast<const char*>(response.data()), response.size()}; + MDEBUG("Sending RPC reply: \"" << response_view << "\""); + MONERO_UNWRAP(net::zmq::send(std::move(response), rep.get())); + } } } catch (const std::system_error& e) @@ -97,38 +183,12 @@ void ZmqServer::serve() } } -bool ZmqServer::addIPCSocket(const boost::string_ref address, const boost::string_ref port) -{ - MERROR("ZmqServer::addIPCSocket not yet implemented!"); - return false; -} - -bool ZmqServer::addTCPSocket(boost::string_ref address, boost::string_ref port) +void* ZmqServer::init_rpc(boost::string_ref address, boost::string_ref port) { if (!context) { MERROR("ZMQ RPC Server already shutdown"); - return false; - } - - rep_socket.reset(zmq_socket(context.get(), ZMQ_REP)); - if (!rep_socket) - { - MONERO_LOG_ZMQ_ERROR("ZMQ RPC Server socket create failed"); - return false; - } - - if (zmq_setsockopt(rep_socket.get(), ZMQ_MAXMSGSIZE, std::addressof(max_message_size), sizeof(max_message_size)) != 0) - { - MONERO_LOG_ZMQ_ERROR("Failed to set maximum incoming message size"); - return false; - } - - static constexpr const int linger_value = std::chrono::milliseconds{linger_timeout}.count(); - if (zmq_setsockopt(rep_socket.get(), ZMQ_LINGER, std::addressof(linger_value), sizeof(linger_value)) != 0) - { - MONERO_LOG_ZMQ_ERROR("Failed to set linger timeout"); - return false; + return nullptr; } if (address.empty()) @@ -141,12 +201,34 @@ bool ZmqServer::addTCPSocket(boost::string_ref address, boost::string_ref port) bind_address += ":"; bind_address.append(port.data(), port.size()); - if (zmq_bind(rep_socket.get(), bind_address.c_str()) < 0) + rep_socket = init_socket(context.get(), ZMQ_REP, {std::addressof(bind_address), 1}); + return bool(rep_socket) ? context.get() : nullptr; +} + +std::shared_ptr<listener::zmq_pub> ZmqServer::init_pub(epee::span<const std::string> addresses) +{ + try + { + shared_state = std::make_shared<listener::zmq_pub>(context.get()); + pub_socket = init_socket(context.get(), ZMQ_XPUB, addresses); + if (!pub_socket) + throw std::runtime_error{"Unable to initialize ZMQ_XPUB socket"}; + + const std::string relay_address[] = {listener::zmq_pub::relay_endpoint()}; + relay_socket = init_socket(context.get(), ZMQ_PAIR, relay_address); + if (!relay_socket) + throw std::runtime_error{"Unable to initialize ZMQ_PAIR relay"}; + } + catch (const std::runtime_error& e) { - MONERO_LOG_ZMQ_ERROR("ZMQ RPC Server bind failed"); - return false; + shared_state = nullptr; + pub_socket = nullptr; + relay_socket = nullptr; + MERROR("Failed to create ZMQ/Pub listener: " << e.what()); + return nullptr; } - return true; + + return shared_state; } void ZmqServer::run() @@ -163,7 +245,6 @@ void ZmqServer::stop() run_thread.join(); } - } // namespace cryptonote } // namespace rpc diff --git a/src/rpc/zmq_server.h b/src/rpc/zmq_server.h index ce7892dab..ddf44b411 100644 --- a/src/rpc/zmq_server.h +++ b/src/rpc/zmq_server.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // @@ -30,10 +30,16 @@ #include <boost/thread/thread.hpp> #include <boost/utility/string_ref.hpp> +#include <cstdint> +#include <memory> +#include <string> #include "common/command_line.h" +#include "cryptonote_basic/fwd.h" #include "net/zmq.h" -#include "rpc_handler.h" +#include "rpc/fwd.h" +#include "rpc/rpc_handler.h" +#include "span.h" namespace cryptonote { @@ -41,7 +47,7 @@ namespace cryptonote namespace rpc { -class ZmqServer +class ZmqServer final { public: @@ -49,12 +55,13 @@ class ZmqServer ~ZmqServer(); - static void init_options(boost::program_options::options_description& desc); - void serve(); - bool addIPCSocket(boost::string_ref address, boost::string_ref port); - bool addTCPSocket(boost::string_ref address, boost::string_ref port); + //! \return ZMQ context on success, `nullptr` on failure + void* init_rpc(boost::string_ref address, boost::string_ref port); + + //! \return `nullptr` on errors. + std::shared_ptr<listener::zmq_pub> init_pub(epee::span<const std::string> addresses); void run(); void stop(); @@ -67,9 +74,11 @@ class ZmqServer boost::thread run_thread; net::zmq::socket rep_socket; + net::zmq::socket pub_socket; + net::zmq::socket relay_socket; + std::shared_ptr<listener::zmq_pub> shared_state; }; - } // namespace cryptonote } // namespace rpc diff --git a/src/serialization/CMakeLists.txt b/src/serialization/CMakeLists.txt index 0b231f156..34e274b6c 100644 --- a/src/serialization/CMakeLists.txt +++ b/src/serialization/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2016-2019, The Monero Project +# Copyright (c) 2016-2020, The Monero Project # # All rights reserved. # @@ -42,6 +42,7 @@ monero_add_library(serialization ${serialization_private_headers}) target_link_libraries(serialization LINK_PRIVATE + cryptonote_basic cryptonote_core cryptonote_protocol epee diff --git a/src/serialization/binary_archive.h b/src/serialization/binary_archive.h index 9f60cf311..80104c8f7 100644 --- a/src/serialization/binary_archive.h +++ b/src/serialization/binary_archive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/binary_utils.h b/src/serialization/binary_utils.h index 790661e49..8635585af 100644 --- a/src/serialization/binary_utils.h +++ b/src/serialization/binary_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/container.h b/src/serialization/container.h index dfe0f9691..4bf47ecfa 100644 --- a/src/serialization/container.h +++ b/src/serialization/container.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h index d2537146e..42bd06e92 100644 --- a/src/serialization/crypto.h +++ b/src/serialization/crypto.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/debug_archive.h b/src/serialization/debug_archive.h index 093ca41eb..b04d6ae19 100644 --- a/src/serialization/debug_archive.h +++ b/src/serialization/debug_archive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/deque.h b/src/serialization/deque.h index 97f16d0a5..b029ed430 100644 --- a/src/serialization/deque.h +++ b/src/serialization/deque.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/difficulty_type.h b/src/serialization/difficulty_type.h index c551095f9..56c0312e7 100644 --- a/src/serialization/difficulty_type.h +++ b/src/serialization/difficulty_type.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/json_archive.h b/src/serialization/json_archive.h index c9f4e175e..50dd5bbd0 100644 --- a/src/serialization/json_archive.h +++ b/src/serialization/json_archive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp index 6228b4bec..7c48cf6c3 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -33,6 +33,8 @@ #include <limits> #include <type_traits> +#include "cryptonote_basic/cryptonote_basic_impl.h" + // drop macro from windows.h #ifdef GetObject #undef GetObject @@ -120,7 +122,7 @@ void read_hex(const rapidjson::Value& val, epee::span<std::uint8_t> dest) throw WRONG_TYPE("string"); } - if (!epee::from_hex::to_buffer(dest, {val.GetString(), val.Size()})) + if (!epee::from_hex::to_buffer(dest, {val.GetString(), val.GetStringLength()})) { throw BAD_INPUT(); } @@ -146,6 +148,26 @@ void fromJsonValue(const rapidjson::Value& val, std::string& str) str = val.GetString(); } +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const std::vector<std::uint8_t>& src) +{ + const std::string hex = epee::to_hex::string(epee::to_span(src)); + dest.String(hex.data(), hex.size()); +} + +void fromJsonValue(const rapidjson::Value& val, std::vector<std::uint8_t>& dest) +{ + if (!val.IsString()) + { + throw WRONG_TYPE("string"); + } + + dest.resize(val.GetStringLength() / 2); + if ((val.GetStringLength() % 2) != 0 || !epee::from_hex::to_buffer(epee::to_mut_span(dest), {val.GetString(), val.GetStringLength()})) + { + throw BAD_INPUT(); + } +} + void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, bool i) { dest.Bool(i); @@ -246,7 +268,10 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::t INSERT_INTO_JSON_OBJECT(dest, inputs, tx.vin); INSERT_INTO_JSON_OBJECT(dest, outputs, tx.vout); INSERT_INTO_JSON_OBJECT(dest, extra, tx.extra); - INSERT_INTO_JSON_OBJECT(dest, signatures, tx.signatures); + if (!tx.pruned) + { + INSERT_INTO_JSON_OBJECT(dest, signatures, tx.signatures); + } INSERT_INTO_JSON_OBJECT(dest, ringct, tx.rct_signatures); dest.EndObject(); @@ -265,8 +290,17 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx) GET_FROM_JSON_OBJECT(val, tx.vin, inputs); GET_FROM_JSON_OBJECT(val, tx.vout, outputs); GET_FROM_JSON_OBJECT(val, tx.extra, extra); - GET_FROM_JSON_OBJECT(val, tx.signatures, signatures); GET_FROM_JSON_OBJECT(val, tx.rct_signatures, ringct); + + const auto& sigs = val.FindMember("signatures"); + if (sigs != val.MemberEnd()) + { + fromJsonValue(sigs->value, tx.signatures); + } + + const auto& rsig = tx.rct_signatures; + if (!cryptonote::is_coinbase(tx) && rsig.p.bulletproofs.empty() && rsig.p.rangeSigs.empty() && rsig.p.MGs.empty() && rsig.get_pseudo_outs().empty() && sigs == val.MemberEnd()) + tx.pruned = true; } void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::block& b) @@ -1062,6 +1096,7 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::rctSig& INSERT_INTO_JSON_OBJECT(dest, fee, sig.txnFee); // prunable + if (!sig.p.bulletproofs.empty() || !sig.p.rangeSigs.empty() || !sig.p.MGs.empty() || !sig.get_pseudo_outs().empty()) { dest.Key("prunable"); dest.StartObject(); @@ -1086,35 +1121,39 @@ void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig) throw WRONG_TYPE("json object"); } - std::vector<rct::key> commitments; - GET_FROM_JSON_OBJECT(val, sig.type, type); GET_FROM_JSON_OBJECT(val, sig.ecdhInfo, encrypted); - GET_FROM_JSON_OBJECT(val, commitments, commitments); + GET_FROM_JSON_OBJECT(val, sig.outPk, commitments); GET_FROM_JSON_OBJECT(val, sig.txnFee, fee); // prunable + const auto prunable = val.FindMember("prunable"); + if (prunable != val.MemberEnd()) { - OBJECT_HAS_MEMBER_OR_THROW(val, "prunable"); - const auto& prunable = val["prunable"]; - - rct::keyV pseudo_outs; + rct::keyV pseudo_outs = std::move(sig.get_pseudo_outs()); - GET_FROM_JSON_OBJECT(prunable, sig.p.rangeSigs, range_proofs); - GET_FROM_JSON_OBJECT(prunable, sig.p.bulletproofs, bulletproofs); - GET_FROM_JSON_OBJECT(prunable, sig.p.MGs, mlsags); - GET_FROM_JSON_OBJECT(prunable, pseudo_outs, pseudo_outs); + GET_FROM_JSON_OBJECT(prunable->value, sig.p.rangeSigs, range_proofs); + GET_FROM_JSON_OBJECT(prunable->value, sig.p.bulletproofs, bulletproofs); + GET_FROM_JSON_OBJECT(prunable->value, sig.p.MGs, mlsags); + GET_FROM_JSON_OBJECT(prunable->value, pseudo_outs, pseudo_outs); sig.get_pseudo_outs() = std::move(pseudo_outs); } - - sig.outPk.reserve(commitments.size()); - for (rct::key const& commitment : commitments) + else { - sig.outPk.push_back({{}, commitment}); + sig.p.rangeSigs.clear(); + sig.p.bulletproofs.clear(); + sig.p.MGs.clear(); + sig.get_pseudo_outs().clear(); } } +void fromJsonValue(const rapidjson::Value& val, rct::ctkey& key) +{ + key.dest = {}; + fromJsonValue(val, key.mask); +} + void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::ecdhTuple& tuple) { dest.StartObject(); diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h index 2a9b63b08..de14c8911 100644 --- a/src/serialization/json_object.h +++ b/src/serialization/json_object.h @@ -32,6 +32,7 @@ #include <cstring> #include <rapidjson/document.h> #include <rapidjson/writer.h> +#include <vector> #include "byte_stream.h" #include "cryptonote_basic/cryptonote_basic.h" @@ -153,6 +154,9 @@ inline void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const std::s } void fromJsonValue(const rapidjson::Value& val, std::string& str); +void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const std::vector<std::uint8_t>&); +void fromJsonValue(const rapidjson::Value& src, std::vector<std::uint8_t>& i); + void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, bool i); void fromJsonValue(const rapidjson::Value& val, bool& b); @@ -277,6 +281,8 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResp void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::rctSig& i); void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig); +void fromJsonValue(const rapidjson::Value& val, rct::ctkey& key); + void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::ecdhTuple& tuple); void fromJsonValue(const rapidjson::Value& val, rct::ecdhTuple& tuple); @@ -339,6 +345,7 @@ inline typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type from auto itr = val.MemberBegin(); + map.clear(); while (itr != val.MemberEnd()) { typename Map::key_type k; @@ -353,25 +360,47 @@ inline typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type from template <typename Vec> inline typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const Vec &vec) { + using value_type = typename Vec::value_type; + static_assert(!std::is_same<value_type, char>::value, "encoding an array of chars is faster as hex"); + static_assert(!std::is_same<value_type, unsigned char>::value, "encoding an array of unsigned char is faster as hex"); + dest.StartArray(); for (const auto& t : vec) toJsonValue(dest, t); - dest.EndArray(vec.size()); + dest.EndArray(); +} + +namespace traits +{ + template<typename T> + void reserve(const T&, std::size_t) + {} + + template<typename T> + void reserve(std::vector<T>& vec, const std::size_t count) + { + vec.reserve(count); + } } template <typename Vec> inline typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type fromJsonValue(const rapidjson::Value& val, Vec& vec) { + using value_type = typename Vec::value_type; + static_assert(!std::is_same<value_type, char>::value, "encoding a vector of chars is faster as hex"); + static_assert(!std::is_same<value_type, unsigned char>::value, "encoding a vector of unsigned char is faster as hex"); + if (!val.IsArray()) { throw WRONG_TYPE("json array"); } + vec.clear(); + traits::reserve(vec, val.Size()); for (rapidjson::SizeType i=0; i < val.Size(); i++) { - typename Vec::value_type v; - fromJsonValue(val[i], v); - vec.push_back(v); + vec.emplace_back(); + fromJsonValue(val[i], vec.back()); } } diff --git a/src/serialization/json_utils.h b/src/serialization/json_utils.h index 05122f3bd..8115877dc 100644 --- a/src/serialization/json_utils.h +++ b/src/serialization/json_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/list.h b/src/serialization/list.h index b7b65e184..139269b40 100644 --- a/src/serialization/list.h +++ b/src/serialization/list.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/pair.h b/src/serialization/pair.h index aed0c4699..18280d837 100644 --- a/src/serialization/pair.h +++ b/src/serialization/pair.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h index 553e9951f..d0f9ee0f7 100644 --- a/src/serialization/serialization.h +++ b/src/serialization/serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/set.h b/src/serialization/set.h index ae602a2f1..157c002f7 100644 --- a/src/serialization/set.h +++ b/src/serialization/set.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/string.h b/src/serialization/string.h index fd8450f7b..f5f69833a 100644 --- a/src/serialization/string.h +++ b/src/serialization/string.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/unordered_set.h b/src/serialization/unordered_set.h index 6dd01fd93..24aa09437 100644 --- a/src/serialization/unordered_set.h +++ b/src/serialization/unordered_set.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/variant.h b/src/serialization/variant.h index 5a179ca87..b08cf6bf0 100644 --- a/src/serialization/variant.h +++ b/src/serialization/variant.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/serialization/vector.h b/src/serialization/vector.h index ad96582a5..1fca1d485 100644 --- a/src/serialization/vector.h +++ b/src/serialization/vector.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/simplewallet/CMakeLists.txt b/src/simplewallet/CMakeLists.txt index 030867dc0..a0820c8eb 100644 --- a/src/simplewallet/CMakeLists.txt +++ b/src/simplewallet/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # @@ -49,6 +49,7 @@ target_link_libraries(simplewallet common mnemonics ${EPEE_READLINE} + qrcodegen version ${Boost_CHRONO_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 96fbf9967..d3b9ccbab 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -74,10 +74,12 @@ #include "version.h" #include <stdexcept> #include "wallet/message_store.h" +#include "QrCode.hpp" #ifdef WIN32 #include <boost/locale.hpp> #include <boost/filesystem.hpp> +#include <fcntl.h> #endif #ifdef HAVE_READLINE @@ -126,7 +128,7 @@ typedef cryptonote::simple_wallet sw; #define SCOPED_WALLET_UNLOCK() SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return true;) -#define PRINT_USAGE(usage_help_advanced) fail_msg_writer() << boost::format(tr("usage: %s")) % usage_help_advanced; +#define PRINT_USAGE(usage_help) fail_msg_writer() << boost::format(tr("usage: %s")) % usage_help; #define LONG_PAYMENT_ID_SUPPORT_CHECK() \ do { \ @@ -255,6 +257,7 @@ namespace const char* USAGE_MMS_SET("mms set <option_name> [<option_value>]"); const char* USAGE_MMS_SEND_SIGNER_CONFIG("mms send_signer_config"); const char* USAGE_MMS_START_AUTO_CONFIG("mms start_auto_config [<label> <label> ...]"); + const char* USAGE_MMS_CONFIG_CHECKSUM("mms config_checksum"); const char* USAGE_MMS_STOP_AUTO_CONFIG("mms stop_auto_config"); const char* USAGE_MMS_AUTO_CONFIG("mms auto_config <auto_config_token>"); const char* USAGE_PRINT_RING("print_ring <key_image> | <txid>"); @@ -274,9 +277,10 @@ namespace const char* USAGE_RPC_PAYMENT_INFO("rpc_payment_info"); const char* USAGE_START_MINING_FOR_RPC("start_mining_for_rpc"); const char* USAGE_STOP_MINING_FOR_RPC("stop_mining_for_rpc"); + const char* USAGE_SHOW_QR_CODE("show_qr_code [<subaddress_index>]"); const char* USAGE_VERSION("version"); - const char* USAGE_HELP_ADVANCED("help_advanced [<command>]"); - const char* USAGE_HELP("help"); + const char* USAGE_HELP("help [<command> | all]"); + const char* USAGE_APROPOS("apropos <keyword> [<keyword> ...]"); std::string input_line(const std::string& prompt, bool yesno = false) { @@ -2313,7 +2317,7 @@ bool simple_wallet::on_unknown_command(const std::vector<std::string> &args) { if (args[0] == "exit" || args[0] == "q") // backward compat return false; - fail_msg_writer() << boost::format(tr("Unknown command '%s', try 'help_advanced'")) % args.front(); + fail_msg_writer() << boost::format(tr("Unknown command '%s', try 'help'")) % args.front(); return true; } @@ -2393,6 +2397,62 @@ bool simple_wallet::stop_mining_for_rpc(const std::vector<std::string> &args) return true; } +bool simple_wallet::show_qr_code(const std::vector<std::string> &args) +{ + uint32_t subaddress_index = 0; + if (args.size() >= 1) + { + if (!string_tools::get_xtype_from_string(subaddress_index, args[0])) + { + fail_msg_writer() << tr("invalid index: must be an unsigned integer"); + return true; + } + if (subaddress_index >= m_wallet->get_num_subaddresses(m_current_subaddress_account)) + { + fail_msg_writer() << tr("<subaddress_index> is out of bounds"); + return true; + } + } + +#ifdef _WIN32 +#define PRINT_UTF8(pre, x) std::wcout << pre ## x +#define WTEXTON() _setmode(_fileno(stdout), _O_WTEXT) +#define WTEXTOFF() _setmode(_fileno(stdout), _O_TEXT) +#else +#define PRINT_UTF8(pre, x) std::cout << x +#define WTEXTON() +#define WTEXTOFF() +#endif + + WTEXTON(); + try + { + const std::string address = "monero:" + m_wallet->get_subaddress_as_str({m_current_subaddress_account, subaddress_index}); + const qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(address.c_str(), qrcodegen::QrCode::Ecc::LOW); + for (int y = -2; y < qr.getSize() + 2; y+=2) + { + for (int x = -2; x < qr.getSize() + 2; x++) + { + if (qr.getModule(x, y) && qr.getModule(x, y + 1)) + PRINT_UTF8(L, "\u2588"); + else if (qr.getModule(x, y) && !qr.getModule(x, y + 1)) + PRINT_UTF8(L, "\u2580"); + else if (!qr.getModule(x, y) && qr.getModule(x, y + 1)) + PRINT_UTF8(L, "\u2584"); + else + PRINT_UTF8(L, " "); + } + PRINT_UTF8(, std::endl); + } + } + catch (const std::length_error&) + { + fail_msg_writer() << tr("Failed to generate QR code, input too large"); + } + WTEXTOFF(); + return true; +} + bool simple_wallet::set_always_confirm_transfers(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { const auto pwd_container = get_and_verify_password(); @@ -3042,37 +3102,39 @@ bool simple_wallet::set_export_format(const std::vector<std::string> &args/* = s bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { - message_writer() << ""; - message_writer() << tr("Commands:"); - message_writer() << ""; - message_writer() << tr("\"welcome\" - Read welcome message."); - message_writer() << tr("\"donate <amount>\" - Donate XMR to the development team."); - message_writer() << tr("\"balance\" - Show balance."); - message_writer() << tr("\"address new\" - Create new subaddress."); - message_writer() << tr("\"address all\" - Show all addresses."); - message_writer() << tr("\"transfer <address> <amount>\" - Send XMR to an address."); - message_writer() << tr("\"show_transfers [in|out|pending|failed|pool]\" - Show transactions."); - message_writer() << tr("\"sweep_all <address>\" - Send whole balance to another wallet."); - message_writer() << tr("\"seed\" - Show secret 25 words that can be used to recover this wallet."); - message_writer() << tr("\"refresh\" - Synchronize wallet with the Monero network."); - message_writer() << tr("\"status\" - Check current status of wallet."); - message_writer() << tr("\"version\" - Check software version."); - message_writer() << tr("\"help_advanced\" - Show list with more available commands."); - message_writer() << tr("\"save\" - Save wallet."); - message_writer() << tr("\"exit\" - Exit wallet."); - message_writer() << ""; - return true; -} - -bool simple_wallet::help_advanced(const std::vector<std::string> &args/* = std::vector<std::string>()*/) -{ if(args.empty()) { + message_writer() << ""; + message_writer() << tr("Important commands:"); + message_writer() << ""; + message_writer() << tr("\"welcome\" - Show welcome message."); + message_writer() << tr("\"help all\" - Show the list of all available commands."); + message_writer() << tr("\"help <command>\" - Show a command's documentation."); + message_writer() << tr("\"apropos <keyword>\" - Show commands related to a keyword."); + message_writer() << ""; + message_writer() << tr("\"wallet_info\" - Show wallet main address and other info."); + message_writer() << tr("\"balance\" - Show balance."); + message_writer() << tr("\"address all\" - Show all addresses."); + message_writer() << tr("\"address new\" - Create new subaddress."); + message_writer() << tr("\"transfer <address> <amount>\" - Send XMR to an address."); + message_writer() << tr("\"show_transfers [in|out|pending|failed|pool]\" - Show transactions."); + message_writer() << tr("\"sweep_all <address>\" - Send whole balance to another wallet."); + message_writer() << tr("\"seed\" - Show secret 25 words that can be used to recover this wallet."); + message_writer() << tr("\"refresh\" - Synchronize wallet with the Monero network."); + message_writer() << tr("\"status\" - Check current status of wallet."); + message_writer() << tr("\"version\" - Check software version."); + message_writer() << tr("\"exit\" - Exit wallet."); + message_writer() << ""; + message_writer() << tr("\"donate <amount>\" - Donate XMR to the development team."); + message_writer() << ""; + } + else if ((args.size() == 1) && (args.front() == "all")) + { success_msg_writer() << get_commands_str(); } else if ((args.size() == 2) && (args.front() == "mms")) { - // Little hack to be able to do "help_advanced mms <subcommand>" + // Little hack to be able to do "help mms <subcommand>" std::vector<std::string> mms_args(1, args.front() + " " + args.back()); success_msg_writer() << get_command_usage(mms_args); } @@ -3083,6 +3145,33 @@ bool simple_wallet::help_advanced(const std::vector<std::string> &args/* = std:: return true; } +bool simple_wallet::apropos(const std::vector<std::string> &args) +{ + if (args.empty()) + { + PRINT_USAGE(USAGE_APROPOS); + return true; + } + const std::vector<std::string>& command_list = m_cmd_binder.get_command_list(args); + if (command_list.empty()) + { + fail_msg_writer() << tr("No commands found mentioning keyword(s)"); + return true; + } + + success_msg_writer() << ""; + for(auto const& command:command_list) + { + std::vector<std::string> cmd; + cmd.push_back(command); + std::pair<std::string, std::string> documentation = m_cmd_binder.get_documentation(cmd); + success_msg_writer() << " " << documentation.first; + } + success_msg_writer() << ""; + + return true; +} + simple_wallet::simple_wallet() : m_allow_mismatched_daemon_version(false) , m_refresh_progress_reporter(*this) @@ -3149,10 +3238,10 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_unmixable, _1), tr("Send all unmixable outputs to yourself with ring_size 1")); - m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), + m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_all, _1), tr(USAGE_SWEEP_ALL), tr("Send all unlocked balance to an address. If the parameter \"index=<N1>[,<N2>,...]\" or \"index=all\" is specified, the wallet sweeps outputs received by those or all address indices, respectively. If omitted, the wallet randomly chooses an address index to be used. If the parameter \"outputs=<N>\" is specified and N > 0, wallet splits the transaction into N even outputs.")); - m_cmd_binder.set_handler("sweep_account", boost::bind(&simple_wallet::sweep_account, this, _1), + m_cmd_binder.set_handler("sweep_account", boost::bind(&simple_wallet::on_command, this, &simple_wallet::sweep_account, _1), tr(USAGE_SWEEP_ACCOUNT), tr("Send all unlocked balance from a given account to an address. If the parameter \"index=<N1>[,<N2>,...]\" or \"index=all\" is specified, the wallet sweeps outputs received by those or all address indices, respectively. If omitted, the wallet randomly chooses an address index to be used. If the parameter \"outputs=<N>\" is specified and N > 0, wallet splits the transaction into N even outputs.")); m_cmd_binder.set_handler("sweep_below", @@ -3216,7 +3305,7 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::on_command, this, &simple_wallet::seed, _1), tr("Display the Electrum-style mnemonic seed")); m_cmd_binder.set_handler("restore_height", - boost::bind(&simple_wallet::restore_height, this, _1), + boost::bind(&simple_wallet::on_command, this, &simple_wallet::restore_height, _1), tr("Display the restore height")); m_cmd_binder.set_handler("set", boost::bind(&simple_wallet::on_command, this, &simple_wallet::set_variable, _1), @@ -3465,8 +3554,8 @@ simple_wallet::simple_wallet() tr("Interface with the MMS (Multisig Messaging System)\n" "<subcommand> is one of:\n" " init, info, signer, list, next, sync, transfer, delete, send, receive, export, note, show, set, help\n" - " send_signer_config, start_auto_config, stop_auto_config, auto_config\n" - "Get help about a subcommand with: help_advanced mms <subcommand>")); + " send_signer_config, start_auto_config, stop_auto_config, auto_config, config_checksum\n" + "Get help about a subcommand with: help mms <subcommand>, or help mms <subcommand>")); m_cmd_binder.set_handler("mms init", boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1), tr(USAGE_MMS_INIT), @@ -3527,13 +3616,17 @@ simple_wallet::simple_wallet() "auto-send <1|0>\n " " Whether to automatically send newly generated messages right away.\n ")); m_cmd_binder.set_handler("mms send_signer_config", - boost::bind(&simple_wallet::mms, this, _1), + boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1), tr(USAGE_MMS_SEND_SIGNER_CONFIG), tr("Send completed signer config to all other authorized signers")); m_cmd_binder.set_handler("mms start_auto_config", boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1), tr(USAGE_MMS_START_AUTO_CONFIG), tr("Start auto-config at the auto-config manager's wallet by issuing auto-config tokens and optionally set others' labels")); + m_cmd_binder.set_handler("mms config_checksum", + boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1), + tr(USAGE_MMS_CONFIG_CHECKSUM), + tr("Get a checksum that allows signers to easily check for identical MMS configuration")); m_cmd_binder.set_handler("mms stop_auto_config", boost::bind(&simple_wallet::on_command, this, &simple_wallet::mms, _1), tr(USAGE_MMS_STOP_AUTO_CONFIG), @@ -3593,7 +3686,7 @@ simple_wallet::simple_wallet() tr(USAGE_NET_STATS), tr("Prints simple network stats")); m_cmd_binder.set_handler("public_nodes", - boost::bind(&simple_wallet::public_nodes, this, _1), + boost::bind(&simple_wallet::on_command, this, &simple_wallet::public_nodes, _1), tr(USAGE_PUBLIC_NODES), tr("Lists known public nodes")); m_cmd_binder.set_handler("welcome", @@ -3605,25 +3698,29 @@ simple_wallet::simple_wallet() tr(USAGE_VERSION), tr("Returns version information")); m_cmd_binder.set_handler("rpc_payment_info", - boost::bind(&simple_wallet::rpc_payment_info, this, _1), + boost::bind(&simple_wallet::on_command, this, &simple_wallet::rpc_payment_info, _1), tr(USAGE_RPC_PAYMENT_INFO), tr("Get info about RPC payments to current node")); m_cmd_binder.set_handler("start_mining_for_rpc", - boost::bind(&simple_wallet::start_mining_for_rpc, this, _1), + boost::bind(&simple_wallet::on_command, this, &simple_wallet::start_mining_for_rpc, _1), tr(USAGE_START_MINING_FOR_RPC), tr("Start mining to pay for RPC access")); m_cmd_binder.set_handler("stop_mining_for_rpc", - boost::bind(&simple_wallet::stop_mining_for_rpc, this, _1), + boost::bind(&simple_wallet::on_command, this, &simple_wallet::stop_mining_for_rpc, _1), tr(USAGE_STOP_MINING_FOR_RPC), tr("Stop mining to pay for RPC access")); - m_cmd_binder.set_handler("help_advanced", - boost::bind(&simple_wallet::on_command, this, &simple_wallet::help_advanced, _1), - tr(USAGE_HELP_ADVANCED), - tr("Show the help section or the documentation about a <command>.")); + m_cmd_binder.set_handler("show_qr_code", + boost::bind(&simple_wallet::on_command, this, &simple_wallet::show_qr_code, _1), + tr(USAGE_SHOW_QR_CODE), + tr("Show address as QR code")); m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::on_command, this, &simple_wallet::help, _1), tr(USAGE_HELP), - tr("Show simplified list of available commands.")); + tr("Show the help section or the documentation about a <command>.")); + m_cmd_binder.set_handler("apropos", + boost::bind(&simple_wallet::on_command, this, &simple_wallet::apropos, _1), + tr(USAGE_APROPOS), + tr("Search all command descriptions for keyword(s)")); m_cmd_binder.set_unknown_command_handler(boost::bind(&simple_wallet::on_command, this, &simple_wallet::on_unknown_command, _1)); m_cmd_binder.set_empty_command_handler(boost::bind(&simple_wallet::on_empty_command, this)); m_cmd_binder.set_cancel_handler(boost::bind(&simple_wallet::on_cancelled_command, this)); @@ -4776,8 +4873,8 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr tr("Your wallet has been generated!\n" "To start synchronizing with the daemon, use the \"refresh\" command.\n" "Use the \"help\" command to see a simplified list of available commands.\n" - "Use the \"help_advanced\" command to see an advanced list of available commands.\n" - "Use \"help_advanced <command>\" to see a command's documentation.\n" + "Use \"help all\" command to see the list of all available commands.\n" + "Use \"help <command>\" to see a command's documentation.\n" "Always use the \"exit\" command when closing monero-wallet-cli to save \n" "your current session's state. Otherwise, you might need to synchronize \n" "your wallet again (your wallet keys are NOT at risk in any case).\n") @@ -5037,8 +5134,8 @@ boost::optional<epee::wipeable_string> simple_wallet::open_wallet(const boost::p success_msg_writer() << "**********************************************************************\n" << tr("Use the \"help\" command to see a simplified list of available commands.\n") << - tr("Use the \"help_advanced\" command to see an advanced list of available commands.\n") << - tr("Use \"help_advanced <command>\" to see a command's documentation.\n") << + tr("Use \"help all\" to see the list of all available commands.\n") << + tr("Use \"help <command>\" to see a command's documentation.\n") << "**********************************************************************"; return password; } @@ -5471,7 +5568,7 @@ void simple_wallet::on_new_block(uint64_t height, const cryptonote::block& block m_refresh_progress_reporter.update(height, false); } //---------------------------------------------------------------------------------------------------- -void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, uint64_t unlock_time) +void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time) { if (m_locked) return; @@ -5482,7 +5579,7 @@ void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, tr("idx ") << subaddr_index; const uint64_t warn_height = m_wallet->nettype() == TESTNET ? 1000000 : m_wallet->nettype() == STAGENET ? 50000 : 1650000; - if (height >= warn_height) + if (height >= warn_height && !is_change) { std::vector<tx_extra_field> tx_extra_fields; parse_tx_extra(tx.extra, tx_extra_fields); // failure ok @@ -6253,7 +6350,7 @@ void simple_wallet::check_for_inactivity_lock(bool user) m_in_command = true; if (!user) { - const std::string speech = tr("I locked your Monero wallet to protect you while you were away\nsee \"help_advanced set\" to configure/disable"); + const std::string speech = tr("I locked your Monero wallet to protect you while you were away\nsee \"help set\" to configure/disable"); std::vector<std::pair<std::string, size_t>> lines = tools::split_string_by_width(speech, 45); size_t max_len = 0; @@ -10366,6 +10463,14 @@ bool simple_wallet::user_confirms(const std::string &question) return !std::cin.eof() && command_line::is_yes(answer); } +bool simple_wallet::user_confirms_auto_config() +{ + message_writer(console_color_red, true) << tr("WARNING: Using MMS auto-config mechanisms is not trustless"); + message_writer() << tr("A malicious auto-config manager could send you info about own wallets instead of other signers' info"); + message_writer() << tr("If in doubt do not use auto-config or at least compare configs using the \"mms config_checksum\" command"); + return user_confirms("Accept the risks and continue?"); +} + bool simple_wallet::get_number_from_arg(const std::string &arg, uint32_t &number, const uint32_t lower_bound, const uint32_t upper_bound) { bool valid = false; @@ -10518,7 +10623,7 @@ void simple_wallet::show_message(const mms::message &m) case mms::message_type::additional_key_set: case mms::message_type::note: display_content = true; - ms.get_sanitized_message_text(m, sanitized_text); + sanitized_text = mms::message_store::get_sanitized_text(m.content, 1000); break; default: display_content = false; @@ -10867,6 +10972,11 @@ void simple_wallet::mms_next(const std::vector<std::string> &args) { break; } + if (!user_confirms_auto_config()) + { + message_writer() << tr("You can use the \"mms delete\" command to delete any unwanted message"); + break; + } } ms.process_signer_config(state, m.content); ms.stop_auto_config(); @@ -11125,7 +11235,7 @@ void simple_wallet::mms_help(const std::vector<std::string> &args) { if (args.size() > 1) { - fail_msg_writer() << tr("Usage: help_advanced mms [<subcommand>]"); + fail_msg_writer() << tr("Usage: help mms [<subcommand>]"); return; } std::vector<std::string> help_args; @@ -11193,6 +11303,18 @@ void simple_wallet::mms_start_auto_config(const std::vector<std::string> &args) list_signers(ms.get_all_signers()); } +void simple_wallet::mms_config_checksum(const std::vector<std::string> &args) +{ + if (args.size() != 0) + { + fail_msg_writer() << tr("Usage: mms config_checksum"); + return; + } + mms::message_store& ms = m_wallet->get_message_store(); + LOCK_IDLE_SCOPE(); + message_writer() << ms.get_config_checksum(); +} + void simple_wallet::mms_stop_auto_config(const std::vector<std::string> &args) { if (args.size() != 0) @@ -11223,6 +11345,10 @@ void simple_wallet::mms_auto_config(const std::vector<std::string> &args) fail_msg_writer() << tr("Invalid auto-config token"); return; } + if (!user_confirms_auto_config()) + { + return; + } mms::authorized_signer me = ms.get_signer(0); if (me.auto_config_running) { @@ -11335,6 +11461,10 @@ bool simple_wallet::mms(const std::vector<std::string> &args) { mms_start_auto_config(mms_args); } + else if (sub_command == "config_checksum") + { + mms_config_checksum(mms_args); + } else if (sub_command == "stop_auto_config") { mms_stop_auto_config(mms_args); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 59818b303..60aa6c4f6 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -53,7 +53,7 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.simplewallet" // Hardcode Monero's donation address (see #1447) -constexpr const char MONERO_DONATION_ADDR[] = "44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A"; +constexpr const char MONERO_DONATION_ADDR[] = "888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H"; /*! * \namespace cryptonote @@ -154,8 +154,8 @@ namespace cryptonote bool set_persistent_rpc_client_id(const std::vector<std::string> &args = std::vector<std::string>()); bool set_auto_mine_for_rpc_payment_threshold(const std::vector<std::string> &args = std::vector<std::string>()); bool set_credits_target(const std::vector<std::string> &args = std::vector<std::string>()); - bool help_advanced(const std::vector<std::string> &args = std::vector<std::string>()); bool help(const std::vector<std::string> &args = std::vector<std::string>()); + bool apropos(const std::vector<std::string> &args); bool start_mining(const std::vector<std::string> &args); bool stop_mining(const std::vector<std::string> &args); bool set_daemon(const std::vector<std::string> &args); @@ -258,6 +258,7 @@ namespace cryptonote bool rpc_payment_info(const std::vector<std::string> &args); bool start_mining_for_rpc(const std::vector<std::string> &args); bool stop_mining_for_rpc(const std::vector<std::string> &args); + bool show_qr_code(const std::vector<std::string> &args); bool net_stats(const std::vector<std::string>& args); bool public_nodes(const std::vector<std::string>& args); bool welcome(const std::vector<std::string>& args); @@ -342,7 +343,7 @@ namespace cryptonote //----------------- i_wallet2_callback --------------------- virtual void on_new_block(uint64_t height, const cryptonote::block& block); - virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, uint64_t unlock_time); + virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time); virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index); virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index); virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx); @@ -478,6 +479,7 @@ namespace cryptonote void ask_send_all_ready_messages(); void check_for_messages(); bool user_confirms(const std::string &question); + bool user_confirms_auto_config(); bool get_message_from_arg(const std::string &arg, mms::message &m); bool get_number_from_arg(const std::string &arg, uint32_t &number, const uint32_t lower_bound, const uint32_t upper_bound); @@ -498,6 +500,7 @@ namespace cryptonote void mms_help(const std::vector<std::string> &args); void mms_send_signer_config(const std::vector<std::string> &args); void mms_start_auto_config(const std::vector<std::string> &args); + void mms_config_checksum(const std::vector<std::string> &args); void mms_stop_auto_config(const std::vector<std::string> &args); void mms_auto_config(const std::vector<std::string> &args); }; diff --git a/src/version.cpp.in b/src/version.cpp.in index ccb88f1fe..1e720537a 100644 --- a/src/version.cpp.in +++ b/src/version.cpp.in @@ -1,6 +1,6 @@ #define DEF_MONERO_VERSION_TAG "@VERSIONTAG@" -#define DEF_MONERO_VERSION "0.15.0.0" -#define DEF_MONERO_RELEASE_NAME "Carbon Chamaeleon" +#define DEF_MONERO_VERSION "0.16.0.0" +#define DEF_MONERO_RELEASE_NAME "Nitrogen Nebula" #define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG #define DEF_MONERO_VERSION_IS_RELEASE @VERSION_IS_RELEASE@ diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index a0a166a93..bf238ae37 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/wallet/api/CMakeLists.txt b/src/wallet/api/CMakeLists.txt index 3376ec70e..30eb4ce03 100644 --- a/src/wallet/api/CMakeLists.txt +++ b/src/wallet/api/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/src/wallet/api/address_book.cpp b/src/wallet/api/address_book.cpp index 005ddf7ee..f69a69ca3 100644 --- a/src/wallet/api/address_book.cpp +++ b/src/wallet/api/address_book.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/address_book.h b/src/wallet/api/address_book.h index 92e6eaa17..f287969f3 100644 --- a/src/wallet/api/address_book.h +++ b/src/wallet/api/address_book.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp index 52510164a..24f6d37db 100644 --- a/src/wallet/api/pending_transaction.cpp +++ b/src/wallet/api/pending_transaction.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h index 92801d77d..bd0ca80a6 100644 --- a/src/wallet/api/pending_transaction.h +++ b/src/wallet/api/pending_transaction.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/subaddress.cpp b/src/wallet/api/subaddress.cpp index 8a1d34864..227bb343d 100644 --- a/src/wallet/api/subaddress.cpp +++ b/src/wallet/api/subaddress.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/subaddress.h b/src/wallet/api/subaddress.h index 87585ec16..18c9ed59e 100644 --- a/src/wallet/api/subaddress.h +++ b/src/wallet/api/subaddress.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/subaddress_account.cpp b/src/wallet/api/subaddress_account.cpp index eaaddc11f..5e502ed5b 100644 --- a/src/wallet/api/subaddress_account.cpp +++ b/src/wallet/api/subaddress_account.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/subaddress_account.h b/src/wallet/api/subaddress_account.h index 358e446d4..1318d4da5 100644 --- a/src/wallet/api/subaddress_account.h +++ b/src/wallet/api/subaddress_account.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp index ad7029a3c..bcb300889 100644 --- a/src/wallet/api/transaction_history.cpp +++ b/src/wallet/api/transaction_history.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/transaction_history.h b/src/wallet/api/transaction_history.h index 67fe1989d..8f3805788 100644 --- a/src/wallet/api/transaction_history.h +++ b/src/wallet/api/transaction_history.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/transaction_info.cpp b/src/wallet/api/transaction_info.cpp index 21573c6f6..5ae3a6937 100644 --- a/src/wallet/api/transaction_info.cpp +++ b/src/wallet/api/transaction_info.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/transaction_info.h b/src/wallet/api/transaction_info.h index d5c8f31cf..73bb7689d 100644 --- a/src/wallet/api/transaction_info.h +++ b/src/wallet/api/transaction_info.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp index c2c04cbc3..fb96674a7 100644 --- a/src/wallet/api/unsigned_transaction.cpp +++ b/src/wallet/api/unsigned_transaction.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/unsigned_transaction.h b/src/wallet/api/unsigned_transaction.h index f1af80fa1..81fc098ff 100644 --- a/src/wallet/api/unsigned_transaction.h +++ b/src/wallet/api/unsigned_transaction.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/utils.cpp b/src/wallet/api/utils.cpp index 24252868a..34debee9d 100644 --- a/src/wallet/api/utils.cpp +++ b/src/wallet/api/utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index d89261c64..66e248427 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -157,7 +157,7 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback } } - virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, uint64_t unlock_time) + virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time) { std::string tx_hash = epee::string_tools::pod_to_hex(txid); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 66eeb0e73..43d85b704 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 9c3df8988..e2f96a069 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index 8d7541cea..69d17eb02 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h index 0595b8327..ca8e9ada2 100644 --- a/src/wallet/api/wallet_manager.h +++ b/src/wallet/api/wallet_manager.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/message_store.cpp b/src/wallet/message_store.cpp index 25a8bd4ef..fb07b42f0 100644 --- a/src/wallet/message_store.cpp +++ b/src/wallet/message_store.cpp @@ -39,6 +39,7 @@ #include "serialization/binary_utils.h" #include "common/base58.h" #include "common/util.h" +#include "common/utf8.h" #include "string_tools.h" @@ -129,18 +130,18 @@ void message_store::set_signer(const multisig_wallet_state &state, authorized_signer &m = m_signers[index]; if (label) { - m.label = label.get(); + m.label = get_sanitized_text(label.get(), 50); } if (transport_address) { - m.transport_address = transport_address.get(); + m.transport_address = get_sanitized_text(transport_address.get(), 200); } if (monero_address) { m.monero_address_known = true; m.monero_address = monero_address.get(); } - // Save to minimize the chance to loose that info (at least while in beta) + // Save to minimize the chance to loose that info save(state); } @@ -202,6 +203,13 @@ void message_store::unpack_signer_config(const multisig_wallet_state &state, con } uint32_t num_signers = (uint32_t)signers.size(); THROW_WALLET_EXCEPTION_IF(num_signers != m_num_authorized_signers, tools::error::wallet_internal_error, "Wrong number of signers in config: " + std::to_string(num_signers)); + for (uint32_t i = 0; i < num_signers; ++i) + { + authorized_signer &m = signers[i]; + m.label = get_sanitized_text(m.label, 50); + m.transport_address = get_sanitized_text(m.transport_address, 200); + m.auto_config_token = get_sanitized_text(m.auto_config_token, 20); + } } void message_store::process_signer_config(const multisig_wallet_state &state, const std::string &signer_config) @@ -242,10 +250,10 @@ void message_store::process_signer_config(const multisig_wallet_state &state, co } } authorized_signer &modify = m_signers[take_index]; - modify.label = m.label; // ALWAYS set label, see comments above + modify.label = get_sanitized_text(m.label, 50); // ALWAYS set label, see comments above if (!modify.me) { - modify.transport_address = m.transport_address; + modify.transport_address = get_sanitized_text(m.transport_address, 200); modify.monero_address_known = m.monero_address_known; if (m.monero_address_known) { @@ -392,6 +400,45 @@ void message_store::process_auto_config_data_message(uint32_t id) signer.auto_config_running = false; } +void add_hash(crypto::hash &sum, const crypto::hash &summand) +{ + for (uint32_t i = 0; i < crypto::HASH_SIZE; ++i) + { + uint32_t x = (uint32_t)sum.data[i]; + uint32_t y = (uint32_t)summand.data[i]; + sum.data[i] = (char)((x + y) % 256); + } +} + +// Calculate a checksum that allows signers to make sure they work with an identical signer config +// by exchanging and comparing checksums out-of-band i.e. not using the MMS; +// Because different signers have a different order of signers in the config work with "adding" +// individual hashes because that operation is commutative +std::string message_store::get_config_checksum() const +{ + crypto::hash sum = crypto::null_hash; + uint32_t num = SWAP32LE(m_num_authorized_signers); + add_hash(sum, crypto::cn_fast_hash(&num, sizeof(num))); + num = SWAP32LE(m_num_required_signers); + add_hash(sum, crypto::cn_fast_hash(&num, sizeof(num))); + for (uint32_t i = 0; i < m_num_authorized_signers; ++i) + { + const authorized_signer &m = m_signers[i]; + add_hash(sum, crypto::cn_fast_hash(m.transport_address.data(), m.transport_address.size())); + if (m.monero_address_known) + { + add_hash(sum, crypto::cn_fast_hash(&m.monero_address.m_spend_public_key, sizeof(m.monero_address.m_spend_public_key))); + add_hash(sum, crypto::cn_fast_hash(&m.monero_address.m_view_public_key, sizeof(m.monero_address.m_view_public_key))); + } + } + std::string checksum_bytes; + checksum_bytes += sum.data[0]; + checksum_bytes += sum.data[1]; + checksum_bytes += sum.data[2]; + checksum_bytes += sum.data[3]; + return epee::string_tools::buff_to_hex_nodelimer(checksum_bytes); +} + void message_store::stop_auto_config() { for (uint32_t i = 0; i < m_num_authorized_signers; ++i) @@ -661,32 +708,38 @@ void message_store::delete_all_messages() m_messages.clear(); } -// Make a message text, which is "attacker controlled data", reasonably safe to display +// Make a text, which is "attacker controlled data", reasonably safe to display // This is mostly geared towards the safe display of notes sent by "mms note" with a "mms show" command -void message_store::get_sanitized_message_text(const message &m, std::string &sanitized_text) const +std::string message_store::get_sanitized_text(const std::string &text, size_t max_length) { - sanitized_text.clear(); - // Restrict the size to fend of DOS-style attacks with heaps of data - size_t length = std::min(m.content.length(), (size_t)1000); + size_t length = std::min(text.length(), max_length); + std::string sanitized_text = text.substr(0, length); - for (size_t i = 0; i < length; ++i) + try { - char c = m.content[i]; - if ((int)c < 32) + sanitized_text = tools::utf8canonical(sanitized_text, [](wint_t c) { - // Strip out any controls, especially ESC for getting rid of potentially dangerous - // ANSI escape sequences that a console window might interpret - c = ' '; - } - else if ((c == '<') || (c == '>')) - { - // Make XML or HTML impossible that e.g. might contain scripts that Qt might execute - // when displayed in the GUI wallet - c = ' '; - } - sanitized_text += c; + if ((c < 0x20) || (c == 0x7f) || (c >= 0x80 && c <= 0x9f)) + { + // Strip out any controls, especially ESC for getting rid of potentially dangerous + // ANSI escape sequences that a console window might interpret + c = '?'; + } + else if ((c == '<') || (c == '>')) + { + // Make XML or HTML impossible that e.g. might contain scripts that Qt might execute + // when displayed in the GUI wallet + c = '?'; + } + return c; + }); + } + catch (const std::exception &e) + { + sanitized_text = "(Illegal UTF-8 string)"; } + return sanitized_text; } void message_store::write_to_file(const multisig_wallet_state &state, const std::string &filename) diff --git a/src/wallet/message_store.h b/src/wallet/message_store.h index d40daf186..9055fd776 100644 --- a/src/wallet/message_store.h +++ b/src/wallet/message_store.h @@ -242,6 +242,7 @@ namespace mms size_t add_auto_config_data_message(const multisig_wallet_state &state, const std::string &auto_config_token); void process_auto_config_data_message(uint32_t id); + std::string get_config_checksum() const; void stop_auto_config(); // Process data just created by "me" i.e. the own local wallet, e.g. as the result of a "prepare_multisig" command @@ -275,7 +276,7 @@ namespace mms void set_message_processed_or_sent(uint32_t id); void delete_message(uint32_t id); void delete_all_messages(); - void get_sanitized_message_text(const message &m, std::string &sanitized_text) const; + static std::string get_sanitized_text(const std::string &text, size_t max_length); void send_message(const multisig_wallet_state &state, uint32_t id); bool check_for_messages(const multisig_wallet_state &state, std::vector<message> &messages); diff --git a/src/wallet/message_transporter.h b/src/wallet/message_transporter.h index 84a2e9bae..557833f2c 100644 --- a/src/wallet/message_transporter.h +++ b/src/wallet/message_transporter.h @@ -31,7 +31,6 @@ #include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/cryptonote_boost_serialization.h" #include "cryptonote_basic/account_boost_serialization.h" -#include "cryptonote_basic/cryptonote_basic.h" #include "net/http_server_impl_base.h" #include "net/http_client.h" #include "net/abstract_http_client.h" diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index 873c2ee51..95b8ce8bb 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h index b053659e9..500ba81d4 100644 --- a/src/wallet/node_rpc_proxy.h +++ b/src/wallet/node_rpc_proxy.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 549017ba3..f971bbd7a 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -1867,6 +1867,20 @@ void wallet2::cache_tx_data(const cryptonote::transaction& tx, const crypto::has } } //---------------------------------------------------------------------------------------------------- +bool wallet2::spends_one_of_ours(const cryptonote::transaction &tx) const +{ + for (const auto &in: tx.vin) + { + if (in.type() != typeid(cryptonote::txin_to_key)) + continue; + const cryptonote::txin_to_key &in_to_key = boost::get<cryptonote::txin_to_key>(in); + auto it = m_key_images.find(in_to_key.k_image); + if (it != m_key_images.end()) + return true; + } + return false; +} +//---------------------------------------------------------------------------------------------------- void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache) { PERF_TIMER(process_new_transaction); @@ -2153,7 +2167,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote } LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid); if (0 != m_callback) - m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index, td.m_tx.unlock_time); + m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index, spends_one_of_ours(tx), td.m_tx.unlock_time); } total_received_1 += amount; notify = true; @@ -2230,7 +2244,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid); if (0 != m_callback) - m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index, td.m_tx.unlock_time); + m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index, spends_one_of_ours(tx), td.m_tx.unlock_time); } total_received_1 += extra_amount; notify = true; @@ -4335,9 +4349,24 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st if (r) { + // Decrypt keys, using one of two possible methods if (encrypted_secret_keys) { + // First try the updated method m_account.decrypt_keys(key); + load_info.is_legacy_key_encryption = false; + + // Test address construction to see if decryption succeeded + const cryptonote::account_keys &keys = m_account.get_keys(); + hw::device &hwdev = m_account.get_device(); + if (!hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key) || !hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key)) + { + // Updated method failed; try the legacy method + // Note that we must first encrypt the keys again with the same IV + m_account.encrypt_keys_same_iv(key); + m_account.decrypt_legacy(key); + load_info.is_legacy_key_encryption = true; + } } else { @@ -5541,6 +5570,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass { clear(); prepare_file_names(wallet_); + MINFO("Keys file: " << m_keys_file); // determine if loading from file system or string buffer bool use_fs = !wallet_.empty(); @@ -9018,7 +9048,7 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui MDEBUG("Ignoring output " << j << " of amount " << print_money(td2.amount()) << " which is outside prescribed range [" << print_money(m_ignore_outputs_below) << ", " << print_money(m_ignore_outputs_above) << "]"); continue; } - if (!is_spent(td2, false) && !td2.m_frozen && !td.m_key_image_partial && td2.is_rct() && td.amount() + td2.amount() >= needed_money && is_transfer_unlocked(td2) && td2.m_subaddr_index == td.m_subaddr_index) + if (!is_spent(td2, false) && !td2.m_frozen && !td2.m_key_image_partial && td2.is_rct() && td.amount() + td2.amount() >= needed_money && is_transfer_unlocked(td2) && td2.m_subaddr_index == td.m_subaddr_index) { // update our picks if those outputs are less related than any we // already found. If the same, don't update, and oldest suitable outputs @@ -11411,7 +11441,7 @@ std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypt hwdev.generate_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[i], additional_tx_keys[i - 1], sig[i]); } } - sig_str = std::string("OutProofV1"); + sig_str = std::string("OutProofV2"); } else { @@ -11447,7 +11477,7 @@ std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypt hwdev.generate_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i - 1], boost::none, shared_secret[i], a, sig[i]); } } - sig_str = std::string("InProofV1"); + sig_str = std::string("InProofV2"); } const size_t num_sigs = shared_secret.size(); @@ -11526,8 +11556,14 @@ bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account bool wallet2::check_tx_proof(const cryptonote::transaction &tx, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message, const std::string &sig_str, uint64_t &received) const { + // InProofV1, InProofV2, OutProofV1, OutProofV2 const bool is_out = sig_str.substr(0, 3) == "Out"; - const std::string header = is_out ? "OutProofV1" : "InProofV1"; + const std::string header = is_out ? sig_str.substr(0,10) : sig_str.substr(0,9); + int version = 2; // InProofV2 + if (is_out && sig_str.substr(8,2) == "V1") version = 1; // OutProofV1 + else if (is_out) version = 2; // OutProofV2 + else if (sig_str.substr(7,2) == "V1") version = 1; // InProofV1 + const size_t header_len = header.size(); THROW_WALLET_EXCEPTION_IF(sig_str.size() < header_len || sig_str.substr(0, header_len) != header, error::wallet_internal_error, "Signature header check error"); @@ -11574,27 +11610,27 @@ bool wallet2::check_tx_proof(const cryptonote::transaction &tx, const cryptonote if (is_out) { good_signature[0] = is_subaddress ? - crypto::check_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[0], sig[0]) : - crypto::check_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[0], sig[0]); + crypto::check_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, address.m_spend_public_key, shared_secret[0], sig[0], version) : + crypto::check_tx_proof(prefix_hash, tx_pub_key, address.m_view_public_key, boost::none, shared_secret[0], sig[0], version); for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i) { good_signature[i + 1] = is_subaddress ? - crypto::check_tx_proof(prefix_hash, additional_tx_pub_keys[i], address.m_view_public_key, address.m_spend_public_key, shared_secret[i + 1], sig[i + 1]) : - crypto::check_tx_proof(prefix_hash, additional_tx_pub_keys[i], address.m_view_public_key, boost::none, shared_secret[i + 1], sig[i + 1]); + crypto::check_tx_proof(prefix_hash, additional_tx_pub_keys[i], address.m_view_public_key, address.m_spend_public_key, shared_secret[i + 1], sig[i + 1], version) : + crypto::check_tx_proof(prefix_hash, additional_tx_pub_keys[i], address.m_view_public_key, boost::none, shared_secret[i + 1], sig[i + 1], version); } } else { good_signature[0] = is_subaddress ? - crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, address.m_spend_public_key, shared_secret[0], sig[0]) : - crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, shared_secret[0], sig[0]); + crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, address.m_spend_public_key, shared_secret[0], sig[0], version) : + crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, shared_secret[0], sig[0], version); for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i) { good_signature[i + 1] = is_subaddress ? - crypto::check_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i], address.m_spend_public_key, shared_secret[i + 1], sig[i + 1]) : - crypto::check_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i], boost::none, shared_secret[i + 1], sig[i + 1]); + crypto::check_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i], address.m_spend_public_key, shared_secret[i + 1], sig[i + 1], version) : + crypto::check_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[i], boost::none, shared_secret[i + 1], sig[i + 1], version); } } @@ -11732,7 +11768,7 @@ std::string wallet2::get_reserve_proof(const boost::optional<std::pair<uint32_t, std::ostringstream oss; boost::archive::portable_binary_oarchive ar(oss); ar << proofs << subaddr_spendkeys; - return "ReserveProofV1" + tools::base58::encode(oss.str()); + return "ReserveProofV2" + tools::base58::encode(oss.str()); } bool wallet2::check_reserve_proof(const cryptonote::account_public_address &address, const std::string &message, const std::string &sig_str, uint64_t &total, uint64_t &spent) @@ -11741,12 +11777,18 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr THROW_WALLET_EXCEPTION_IF(!check_connection(&rpc_version), error::wallet_internal_error, "Failed to connect to daemon: " + get_daemon_address()); THROW_WALLET_EXCEPTION_IF(rpc_version < MAKE_CORE_RPC_VERSION(1, 0), error::wallet_internal_error, "Daemon RPC version is too old"); - static constexpr char header[] = "ReserveProofV1"; - THROW_WALLET_EXCEPTION_IF(!boost::string_ref{sig_str}.starts_with(header), error::wallet_internal_error, + static constexpr char header_v1[] = "ReserveProofV1"; + static constexpr char header_v2[] = "ReserveProofV2"; // assumes same length as header_v1 + THROW_WALLET_EXCEPTION_IF(!boost::string_ref{sig_str}.starts_with(header_v1) && !boost::string_ref{sig_str}.starts_with(header_v2), error::wallet_internal_error, "Signature header check error"); + int version = 2; // assume newest version + if (boost::string_ref{sig_str}.starts_with(header_v1)) + version = 1; + else if (boost::string_ref{sig_str}.starts_with(header_v2)) + version = 2; std::string sig_decoded; - THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(std::strlen(header)), sig_decoded), error::wallet_internal_error, + THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(std::strlen(header_v1)), sig_decoded), error::wallet_internal_error, "Signature decoding error"); std::istringstream iss(sig_decoded); @@ -11827,9 +11869,9 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx); // check singature for shared secret - ok = crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, proof.shared_secret, proof.shared_secret_sig); + ok = crypto::check_tx_proof(prefix_hash, address.m_view_public_key, tx_pub_key, boost::none, proof.shared_secret, proof.shared_secret_sig, version); if (!ok && additional_tx_pub_keys.size() == tx.vout.size()) - ok = crypto::check_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[proof.index_in_tx], boost::none, proof.shared_secret, proof.shared_secret_sig); + ok = crypto::check_tx_proof(prefix_hash, address.m_view_public_key, additional_tx_pub_keys[proof.index_in_tx], boost::none, proof.shared_secret, proof.shared_secret_sig, version); if (!ok) return false; @@ -13080,7 +13122,7 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs) CHECK_AND_ASSERT_THROW_MES(info.size() + 1 <= m_multisig_signers.size() && info.size() + 1 >= m_multisig_threshold, "Wrong number of multisig sources"); std::vector<std::vector<rct::key>> k; - auto wiper = epee::misc_utils::create_scope_leave_handler([&](){memwipe(k.data(), k.size() * sizeof(k[0]));}); + auto wiper = epee::misc_utils::create_scope_leave_handler([&](){for (auto &v: k) memwipe(v.data(), v.size() * sizeof(v[0]));}); k.reserve(m_transfers.size()); for (const auto &td: m_transfers) k.push_back(td.m_multisig_k); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index eb33713b5..1d26c6a00 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -131,7 +131,7 @@ private: public: // Full wallet callbacks virtual void on_new_block(uint64_t height, const cryptonote::block& block) {} - virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, uint64_t unlock_time) {} + virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, bool is_change, uint64_t unlock_time) {} virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index) {} virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index) {} virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) {} @@ -219,6 +219,15 @@ private: friend class wallet_keys_unlocker; friend class wallet_device_callback; public: + // Contains data on how keys were loaded, primarily for unit test purposes + struct load_info_t { + bool is_legacy_key_encryption; + }; + + const load_info_t &get_load_info() const { + return load_info; + } + static constexpr const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30); enum RefreshType { @@ -1407,6 +1416,8 @@ private: static std::string get_default_daemon_address() { CRITICAL_REGION_LOCAL(default_daemon_address_lock); return default_daemon_address; } private: + load_info_t load_info; + /*! * \brief Stores wallet information to wallet file. * \param keys_file_name Name of wallet file @@ -1517,6 +1528,7 @@ private: void check_rpc_cost(const char *call, uint64_t post_call_credits, uint64_t pre_credits, double expected_cost); bool should_expand(const cryptonote::subaddress_index &index) const; + bool spends_one_of_ours(const cryptonote::transaction &tx) const; cryptonote::account_base m_account; boost::optional<epee::net_utils::http::login> m_daemon_login; diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp index 350f016c7..55058bf4e 100644 --- a/src/wallet/wallet_args.cpp +++ b/src/wallet/wallet_args.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/wallet_args.h b/src/wallet/wallet_args.h index 59529662f..4af1b58fe 100644 --- a/src/wallet/wallet_args.h +++ b/src/wallet/wallet_args.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 3e94e604a..e889ed7d1 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/wallet_rpc_helpers.h b/src/wallet/wallet_rpc_helpers.h index 4291a112d..35714db03 100644 --- a/src/wallet/wallet_rpc_helpers.h +++ b/src/wallet/wallet_rpc_helpers.h @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019, The Monero Project +// Copyright (c) 2018-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/wallet_rpc_payments.cpp b/src/wallet/wallet_rpc_payments.cpp index 4f5364269..6527b1384 100644 --- a/src/wallet/wallet_rpc_payments.cpp +++ b/src/wallet/wallet_rpc_payments.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2019, The Monero Project +// Copyright (c) 2018-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 30eed07e7..2391b51fd 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -80,7 +80,7 @@ namespace return pwd_container; } //------------------------------------------------------------------------------------------------------------------------------ - void set_confirmations(tools::wallet_rpc::transfer_entry &entry, uint64_t blockchain_height, uint64_t block_reward) + void set_confirmations(tools::wallet_rpc::transfer_entry &entry, uint64_t blockchain_height, uint64_t block_reward, uint64_t unlock_time) { if (entry.height >= blockchain_height || (entry.height == 0 && (!strcmp(entry.type.c_str(), "pending") || !strcmp(entry.type.c_str(), "pool")))) entry.confirmations = 0; @@ -91,6 +91,18 @@ namespace entry.suggested_confirmations_threshold = 0; else entry.suggested_confirmations_threshold = (entry.amount + block_reward - 1) / block_reward; + + if (unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER) + { + if (unlock_time > blockchain_height) + entry.suggested_confirmations_threshold = std::max(entry.suggested_confirmations_threshold, unlock_time - blockchain_height); + } + else + { + const uint64_t now = time(NULL); + if (unlock_time > now) + entry.suggested_confirmations_threshold = std::max(entry.suggested_confirmations_threshold, (unlock_time - now + DIFFICULTY_TARGET_V2 - 1) / DIFFICULTY_TARGET_V2); + } } } @@ -335,7 +347,7 @@ namespace tools entry.subaddr_index = pd.m_subaddr_index; entry.subaddr_indices.push_back(pd.m_subaddr_index); entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index); - set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward()); + set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_unlock_time); } //------------------------------------------------------------------------------------------------------------------------------ void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::confirmed_transfer_details &pd) @@ -365,7 +377,7 @@ namespace tools for (uint32_t i: pd.m_subaddr_indices) entry.subaddr_indices.push_back({pd.m_subaddr_account, i}); entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0}); - set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward()); + set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_unlock_time); } //------------------------------------------------------------------------------------------------------------------------------ void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::unconfirmed_transfer_details &pd) @@ -396,7 +408,7 @@ namespace tools for (uint32_t i: pd.m_subaddr_indices) entry.subaddr_indices.push_back({pd.m_subaddr_account, i}); entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0}); - set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward()); + set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_tx.unlock_time); } //------------------------------------------------------------------------------------------------------------------------------ void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::pool_payment_details &ppd) @@ -419,7 +431,7 @@ namespace tools entry.subaddr_index = pd.m_subaddr_index; entry.subaddr_indices.push_back(pd.m_subaddr_index); entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index); - set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward()); + set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_unlock_time); } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er, const connection_context *ctx) diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 89bf3a924..6e39eca1e 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 507ff4f6c..ae861d177 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index 9434fbc3e..9b455af6a 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index afc69ee88..c601b93ed 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # @@ -28,6 +28,8 @@ # # Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers +set(MONERO_WALLET_CRYPTO_BENCH "auto" CACHE STRING "Select wallet crypto libraries for benchmarking") + # The docs say this only affects grouping in IDEs set(folder "tests") set(TEST_DATA_DIR "${CMAKE_CURRENT_LIST_DIR}/data") @@ -118,6 +120,41 @@ add_test( NAME hash-target COMMAND hash-target-tests) +# +# Configure wallet crypto benchmark +# +if (${MONERO_WALLET_CRYPTO_BENCH} STREQUAL "auto") + set(MONERO_WALLET_CRYPTO_BENCH "cn") + monero_crypto_autodetect(AVAILABLE BEST) + if (DEFINED AVAILABLE) + list(APPEND MONERO_WALLET_CRYPTO_BENCH ${AVAILABLE}) + endif () + message("Wallet crypto bench is using ${MONERO_WALLET_CRYPTO_BENCH}") +endif () + +list(REMOVE_DUPLICATES MONERO_WALLET_CRYPTO_BENCH) +list(REMOVE_ITEM MONERO_WALLET_CRYPTO_BENCH "cn") # always used for comparison +set(MONERO_WALLET_CRYPTO_BENCH_NAMES "(cn)") +foreach(BENCH IN LISTS MONERO_WALLET_CRYPTO_BENCH) + monero_crypto_valid(${BENCH} VALID) + if (NOT VALID) + message(FATAL_ERROR "Invalid MONERO_WALLET_CRYPTO_BENCH option ${BENCH}") + endif () + + monero_crypto_get_target(${BENCH} BENCH_LIBRARY) + list(APPEND BENCH_OBJECTS $<TARGET_OBJECTS:${BENCH_LIBRARY}>) + + monero_crypto_get_namespace(${BENCH} BENCH_NAMESPACE) + set(MONERO_WALLET_CRYPTO_BENCH_NAMES "${MONERO_WALLET_CRYPTO_BENCH_NAMES}(${BENCH_NAMESPACE})") +endforeach () + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/benchmark.h.in" "${MONERO_GENERATED_HEADERS_DIR}/tests/benchmark.h") +add_executable(monero-wallet-crypto-bench benchmark.cpp ${BENCH_OBJECTS}) +target_link_libraries(monero-wallet-crypto-bench cncrypto) + +add_test(NAME wallet-crypto-bench COMMAND monero-wallet-crypto-bench) + + set(enabled_tests core_tests difficulty diff --git a/tests/benchmark.cpp b/tests/benchmark.cpp new file mode 100644 index 000000000..0461f4c11 --- /dev/null +++ b/tests/benchmark.cpp @@ -0,0 +1,437 @@ +// Copyright (c) 2020, 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 "tests/benchmark.h" + +#include <boost/fusion/adapted/std_tuple.hpp> +#include <boost/fusion/algorithm/iteration/fold.hpp> +#include <boost/preprocessor/seq/enum.hpp> +#include <boost/preprocessor/seq/for_each.hpp> +#include <boost/preprocessor/seq/seq.hpp> +#include <boost/preprocessor/seq.hpp> +#include <boost/preprocessor/stringize.hpp> +#include <boost/spirit/include/karma_char.hpp> +#include <boost/spirit/include/karma_format.hpp> +#include <boost/spirit/include/karma_repeat.hpp> +#include <boost/spirit/include/karma_right_alignment.hpp> +#include <boost/spirit/include/karma_sequence.hpp> +#include <boost/spirit/include/karma_string.hpp> +#include <boost/spirit/include/karma_uint.hpp> +#include <boost/spirit/include/qi_char.hpp> +#include <boost/spirit/include/qi_list.hpp> +#include <boost/spirit/include/qi_parse.hpp> +#include <boost/spirit/include/qi_uint.hpp> +#include <chrono> +#include <cstring> +#include <functional> +#include <iostream> +#include <stdexcept> +#include <string> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> + +#include "crypto/crypto.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "monero/crypto/amd64-64-24k.h" +#include "monero/crypto/amd64-51-30k.h" + +#define CHECK(...) \ + if(!( __VA_ARGS__ )) \ + throw std::runtime_error{ \ + "TEST FAILED (line " \ + BOOST_PP_STRINGIZE( __LINE__ ) \ + "): " \ + BOOST_PP_STRINGIZE( __VA_ARGS__ ) \ + } + +//! Define function that forwards arguments to `crypto::func`. +#define FORWARD_FUNCTION(func) \ + template<typename... T> \ + static bool func (T&&... args) \ + { \ + return ::crypto:: func (std::forward<T>(args)...); \ + } + +#define CRYPTO_FUNCTION(library, func) \ + BOOST_PP_CAT(BOOST_PP_CAT(monero_crypto_, library), func) + +#define CRYPTO_BENCHMARK(r, _, library) \ + struct library \ + { \ + static constexpr const char* name() noexcept { return BOOST_PP_STRINGIZE(library); } \ + static bool generate_key_derivation(const ::crypto::public_key &tx_pub, const ::crypto::secret_key &view_sec, ::crypto::key_derivation &out) \ + { \ + return CRYPTO_FUNCTION(library, _generate_key_derivation) (out.data, tx_pub.data, view_sec.data) == 0; \ + } \ + static bool derive_subaddress_public_key(const ::crypto::public_key &spend_pub, const ::crypto::key_derivation &d, std::size_t index, ::crypto::public_key &out) \ + { \ + ::crypto::ec_scalar scalar; \ + ::crypto::derivation_to_scalar(d, index, scalar); \ + return CRYPTO_FUNCTION(library, _generate_subaddress_public_key) (out.data, spend_pub.data, scalar.data) == 0; \ + } \ + }; + + +namespace +{ + //! Default number of iterations for benchmark timing. + constexpr const unsigned default_iterations = 1000; + + //! \return Byte compare two objects of `T`. + template<typename T> + bool compare(const T& lhs, const T& rhs) noexcept + { + static_assert(!epee::has_padding<T>(), "type might have padding"); + return std::memcmp(std::addressof(lhs), std::addressof(rhs), sizeof(T)) == 0; + } + + //! Benchmark default monero crypto library - a re-arranged ref10 implementation. + struct cn + { + static constexpr const char* name() noexcept { return "cn"; } + FORWARD_FUNCTION( generate_key_derivation ); + FORWARD_FUNCTION( derive_subaddress_public_key ); + }; + + // Define functions for every library except for `cn` which is the head library. + BOOST_PP_SEQ_FOR_EACH(CRYPTO_BENCHMARK, _, BOOST_PP_SEQ_TAIL(BENCHMARK_LIBRARIES)); + + // All enabled benchmark libraries + using enabled_libraries = std::tuple<BOOST_PP_SEQ_ENUM(BENCHMARK_LIBRARIES)>; + + + //! Callable that runs a benchmark against all enabled libraries + template<typename R> + struct run_benchmark + { + using result = R; + + template<typename B> + result operator()(result out, const B benchmark) const + { + using inner_result = typename B::result; + out.push_back({boost::fusion::fold(enabled_libraries{}, inner_result{}, benchmark), benchmark.name()}); + std::sort(out.back().first.begin(), out.back().first.end()); + return out; + } + }; + + //! Run 0+ benchmarks against all enabled libraries + template<typename R, typename... B> + R run_benchmarks(B&&... benchmarks) + { + auto out = boost::fusion::fold(std::make_tuple(std::forward<B>(benchmarks)...), R{}, run_benchmark<R>{}); + std::sort(out.begin(), out.end()); + return out; + } + + //! Run a suite of benchmarks - allows for comparison against a subset of benchmarks + template<typename S> + std::pair<typename S::result, std::string> run_suite(const S& suite) + { + return {suite(), suite.name()}; + } + + //! Arguments given to every crypto library being benchmarked. + struct bench_args + { + explicit bench_args(unsigned iterations) + : iterations(iterations), one(), two() + { + crypto::generate_keys(one.pub, one.sec, one.sec, false); + crypto::generate_keys(two.pub, two.sec, two.sec, false); + } + + const unsigned iterations; + cryptonote::keypair one; + cryptonote::keypair two; + }; + + /*! Tests the ECDH step used for monero txes where the tx-pub is always + de-compressed into a table every time. */ + struct tx_pub_standard + { + using result = std::vector<std::pair<std::chrono::steady_clock::duration, std::string>>; + static constexpr const char* name() noexcept { return "standard"; } + + const bench_args args; + + template<typename L> + result operator()(result out, const L library) const + { + crypto::key_derivation us; + crypto::key_derivation them; + CHECK(crypto::generate_key_derivation(args.one.pub, args.two.sec, them)); + CHECK(library.generate_key_derivation(args.one.pub, args.two.sec, us)); + CHECK(compare(us, them)); + + unsigned i = 0; + for (unsigned j = 0; j < 100; ++j) + i += library.generate_key_derivation(args.one.pub, args.two.sec, us); + CHECK(i == 100); + + i = 0; + const auto start = std::chrono::steady_clock::now(); + for (unsigned j = 0; j < args.iterations; ++j) + i += library.generate_key_derivation(args.one.pub, args.two.sec, us); + const auto end = std::chrono::steady_clock::now(); + CHECK(i == args.iterations); + CHECK(compare(us, them)); + + out.push_back({end - start, library.name()}); + return out; + } + }; + + //! Tests various possible optimizations for tx ECDH-step. + struct tx_pub_suite + { + using result = std::vector<std::pair<tx_pub_standard::result, std::string>>; + static constexpr const char* name() noexcept { return "generate_key_derivation step"; } + + const bench_args args; + + result operator()() const + { + return run_benchmarks<result>(tx_pub_standard{args}); + } + }; + + /*! Tests the shared-secret to output-key step used for monero txes where + the users spend-public is always de-compressed. */ + struct output_pub_standard + { + using result = std::vector<std::pair<std::chrono::steady_clock::duration, std::string>>; + static constexpr const char* name() noexcept { return "standard"; } + + const bench_args args; + + template<typename L> + result operator()(result out, const L library) const + { + crypto::key_derivation derived; + crypto::public_key us; + crypto::public_key them; + CHECK(crypto::generate_key_derivation(args.one.pub, args.two.sec, derived)); + CHECK(library.derive_subaddress_public_key(args.two.pub, derived, 0, us)); + CHECK(crypto::derive_subaddress_public_key(args.two.pub, derived, 0, them)); + CHECK(compare(us, them)); + + unsigned i = 0; + for (unsigned j = 0; j < 100; ++j) + i += library.derive_subaddress_public_key(args.two.pub, derived, j, us); + CHECK(i == 100); + + i = 0; + const auto start = std::chrono::steady_clock::now(); + for (unsigned j = 0; j < args.iterations; ++j) + i += library.derive_subaddress_public_key(args.two.pub, derived, j, us); + const auto end = std::chrono::steady_clock::now(); + CHECK(i == args.iterations); + + out.push_back({end - start, library.name()}); + return out; + } + }; + + //! Tests various possible optimizations for shared-secret to output-key step. + struct output_pub_suite + { + using result = std::vector<std::pair<output_pub_standard::result, std::string>>; + static constexpr const char* name() noexcept { return "derive_subaddress_public_key step"; } + + const bench_args args; + + result operator()() const + { + return run_benchmarks<result>(output_pub_standard{args}); + } + }; + + struct tx_bench_args + { + const bench_args main; + unsigned outputs; + }; + + /*! Simulates "standard" tx scanning where a tx-pubkey is de-compressed into + a table and user spend-public is de-compressed, every time. */ + struct tx_standard + { + using result = std::vector<std::pair<std::chrono::steady_clock::duration, std::string>>; + static constexpr const char* name() noexcept { return "standard"; } + + const tx_bench_args args; + + template<typename L> + result operator()(result out, const L library) const + { + crypto::key_derivation derived_us; + crypto::key_derivation derived_them; + crypto::public_key us; + crypto::public_key them; + CHECK(library.generate_key_derivation(args.main.one.pub, args.main.two.sec, derived_us)); + CHECK(crypto::generate_key_derivation(args.main.one.pub, args.main.two.sec, derived_them)); + CHECK(library.derive_subaddress_public_key(args.main.two.pub, derived_us, 0, us)); + CHECK(crypto::derive_subaddress_public_key(args.main.two.pub, derived_them, 0, them)); + CHECK(compare(us, them)); + + unsigned i = 0; + for (unsigned j = 0; j < 100; ++j) + { + i += library.generate_key_derivation(args.main.one.pub, args.main.two.sec, derived_us); + i += library.derive_subaddress_public_key(args.main.two.pub, derived_us, j, us); + } + CHECK(i == 200); + + i = 0; + const auto start = std::chrono::steady_clock::now(); + for (unsigned j = 0; j < args.main.iterations; ++j) + { + i += library.generate_key_derivation(args.main.one.pub, args.main.two.sec, derived_us); + for (unsigned k = 0; k < args.outputs; ++k) + i += library.derive_subaddress_public_key(args.main.two.pub, derived_us, k, us); + } + const auto end = std::chrono::steady_clock::now(); + CHECK(i == args.main.iterations + args.main.iterations * args.outputs); + + out.push_back({end - start, library.name()}); + return out; + } + }; + + //! Tests various possible optimizations for tx scanning. + struct tx_suite + { + using result = std::vector<std::pair<output_pub_standard::result, std::string>>; + std::string name() const { return "Transactions with " + std::to_string(args.outputs) + " outputs"; } + + const tx_bench_args args; + + result operator()() const + { + return run_benchmarks<result>(tx_standard{args}); + + } + }; + + std::chrono::steady_clock::duration print(const tx_pub_standard::result& leaf, std::ostream& out, unsigned depth) + { + namespace karma = boost::spirit::karma; + const std::size_t align = leaf.empty() ? + 0 : std::to_string(leaf.back().first.count()).size(); + const auto best = leaf.empty() ? + std::chrono::steady_clock::duration::max() : leaf.front().first; + for (auto const& entry : leaf) + { + out << karma::format(karma::repeat(depth ? depth - 1 : 0)["| "]) << '|'; + out << karma::format((karma::right_align(std::min(20u - depth, 20u), '-')["> " << karma::string]), entry.second); + out << " => " << karma::format((karma::right_align(align)[karma::uint_]), entry.first.count()); + out << " ns (+"; + out << (double((entry.first - best).count()) / best.count()) * 100 << "%)" << std::endl; + } + out << karma::format(karma::repeat(depth ? depth - 1 : 0)["| "]) << std::endl; + return best; + } + + template<typename T> + std::chrono::steady_clock::duration + print(const std::vector<std::pair<T, std::string>>& node, std::ostream& out, unsigned depth) + { + auto best = std::chrono::steady_clock::duration::max(); + for (auto const& entry : node) + { + std::stringstream buffer{}; + auto last = print(entry.first, buffer, depth + 1); + if (last != std::chrono::steady_clock::duration::max()) + { + namespace karma = boost::spirit::karma; + best = std::min(best, last); + out << karma::format(karma::repeat(depth)["|-"]); + out << "+ " << entry.second << ' '; + out << last.count() << " ns (+"; + out << (double((last - best).count()) / best.count()) * 100 << "%)" << std::endl; + out << buffer.str(); + } + } + return best; + } +} // anonymous namespace + +int main(int argc, char** argv) +{ + using results = std::vector<std::pair<tx_pub_suite::result, std::string>>; + try + { + unsigned iterations = default_iterations; + std::vector<unsigned> nums{}; + if (2 <= argc) iterations = std::stoul(argv[1]); + if (3 <= argc) + { + namespace qi = boost::spirit::qi; + if (!qi::parse(argv[2], argv[2] + strlen(argv[2]), (qi::uint_ % ','), nums)) + throw std::runtime_error{"bad tx outputs string"}; + } + else + { + nums = {2, 4}; + } + std::sort(nums.begin(), nums.end()); + nums.erase(std::unique(nums.begin(), nums.end()), nums.end()); + + std::cout << "Running benchmark using " << iterations << " iterations" << std::endl; + + const bench_args args{iterations}; + + results val{}; + + std::cout << "Transaction Component Benchmarks" << std::endl; + std::cout << "--------------------------------" << std::endl; + val.push_back(run_suite(tx_pub_suite{args})); + val.push_back(run_suite(output_pub_suite{args})); + std::sort(val.begin(), val.end()); + print(val, std::cout, 0); + + val.clear(); + std::cout << "Transaction Benchmarks" << std::endl; + std::cout << "----------------------" << std::endl; + for (const unsigned num : nums) + val.push_back(run_suite(tx_suite{{args, num}})); + std::sort(val.begin(), val.end()); + print(val, std::cout, 0); + } + catch (const std::exception& e) + { + std::cerr << "Error: " << e.what() << std::endl; + return 1; + } + return 0; +} + diff --git a/tests/benchmark.h.in b/tests/benchmark.h.in new file mode 100644 index 000000000..b13ea30b7 --- /dev/null +++ b/tests/benchmark.h.in @@ -0,0 +1,5 @@ +#pragma once + +// A Boost PP sequence +#define BENCHMARK_LIBRARIES @MONERO_WALLET_CRYPTO_BENCH_NAMES@ + diff --git a/tests/block_weight/block_weight.cpp b/tests/block_weight/block_weight.cpp index f193133da..60606d53f 100644 --- a/tests/block_weight/block_weight.cpp +++ b/tests/block_weight/block_weight.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/tests/block_weight/block_weight.py b/tests/block_weight/block_weight.py index b23da3d77..f396585a4 100755 --- a/tests/block_weight/block_weight.py +++ b/tests/block_weight/block_weight.py @@ -2,7 +2,7 @@ # 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 +# Copyright (c) 2019-2020 The Monero Project from __future__ import print_function import sys import math diff --git a/tests/core_proxy/CMakeLists.txt b/tests/core_proxy/CMakeLists.txt index 9818d35d7..7896389f2 100644 --- a/tests/core_proxy/CMakeLists.txt +++ b/tests/core_proxy/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/tests/core_proxy/core_proxy.cpp b/tests/core_proxy/core_proxy.cpp index 9463d14ce..8095f03e3 100644 --- a/tests/core_proxy/core_proxy.cpp +++ b/tests/core_proxy/core_proxy.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -47,7 +47,6 @@ #include "cryptonote_protocol/cryptonote_protocol_handler.h" #include "cryptonote_protocol/cryptonote_protocol_handler.inl" #include "core_proxy.h" -#include "version.h" #if defined(WIN32) #include <crtdbg.h> diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index aad1bc962..4a41b5002 100644 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/CMakeLists.txt b/tests/core_tests/CMakeLists.txt index 42e76e613..ca9a09d82 100644 --- a/tests/core_tests/CMakeLists.txt +++ b/tests/core_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/tests/core_tests/block_reward.cpp b/tests/core_tests/block_reward.cpp index 17fc762ec..55eb517df 100644 --- a/tests/core_tests/block_reward.cpp +++ b/tests/core_tests/block_reward.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/block_reward.h b/tests/core_tests/block_reward.h index 6234bb9c5..ef62abc7f 100644 --- a/tests/core_tests/block_reward.h +++ b/tests/core_tests/block_reward.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp index 55312bc15..10b1b9af4 100644 --- a/tests/core_tests/block_validation.cpp +++ b/tests/core_tests/block_validation.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/block_validation.h b/tests/core_tests/block_validation.h index 2393e1b01..39eba0829 100644 --- a/tests/core_tests/block_validation.h +++ b/tests/core_tests/block_validation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/bulletproofs.cpp b/tests/core_tests/bulletproofs.cpp index 0a5b98605..04eeb9e01 100644 --- a/tests/core_tests/bulletproofs.cpp +++ b/tests/core_tests/bulletproofs.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/bulletproofs.h b/tests/core_tests/bulletproofs.h index 83f160d71..93fe2947f 100644 --- a/tests/core_tests/bulletproofs.h +++ b/tests/core_tests/bulletproofs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chain_split_1.cpp b/tests/core_tests/chain_split_1.cpp index c51accb67..640ccfcc6 100644 --- a/tests/core_tests/chain_split_1.cpp +++ b/tests/core_tests/chain_split_1.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chain_split_1.h b/tests/core_tests/chain_split_1.h index 503c3da19..59c7c4bd3 100644 --- a/tests/core_tests/chain_split_1.h +++ b/tests/core_tests/chain_split_1.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chain_switch_1.cpp b/tests/core_tests/chain_switch_1.cpp index 0554fd676..d0d04d1c9 100644 --- a/tests/core_tests/chain_switch_1.cpp +++ b/tests/core_tests/chain_switch_1.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chain_switch_1.h b/tests/core_tests/chain_switch_1.h index c084de262..c7ba83502 100644 --- a/tests/core_tests/chain_switch_1.h +++ b/tests/core_tests/chain_switch_1.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index f935d4f64..f442a4977 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index e94bf6be1..edaa9b20a 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chaingen001.cpp b/tests/core_tests/chaingen001.cpp index b313f4821..6d1741b06 100644 --- a/tests/core_tests/chaingen001.cpp +++ b/tests/core_tests/chaingen001.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 8a313140c..38382b95f 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chaingen_tests_list.h b/tests/core_tests/chaingen_tests_list.h index cf20c725c..94eb23ce9 100644 --- a/tests/core_tests/chaingen_tests_list.h +++ b/tests/core_tests/chaingen_tests_list.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/double_spend.cpp b/tests/core_tests/double_spend.cpp index 4aa12c8e0..04a7a6921 100644 --- a/tests/core_tests/double_spend.cpp +++ b/tests/core_tests/double_spend.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/double_spend.h b/tests/core_tests/double_spend.h index 70d9f84b9..9f00348aa 100644 --- a/tests/core_tests/double_spend.h +++ b/tests/core_tests/double_spend.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/double_spend.inl b/tests/core_tests/double_spend.inl index 684c9c4de..22af95a37 100644 --- a/tests/core_tests/double_spend.inl +++ b/tests/core_tests/double_spend.inl @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/integer_overflow.cpp b/tests/core_tests/integer_overflow.cpp index 42df4aa3b..5cb5a7959 100644 --- a/tests/core_tests/integer_overflow.cpp +++ b/tests/core_tests/integer_overflow.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/integer_overflow.h b/tests/core_tests/integer_overflow.h index 75a60eb15..384178bbd 100644 --- a/tests/core_tests/integer_overflow.h +++ b/tests/core_tests/integer_overflow.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/multisig.cpp b/tests/core_tests/multisig.cpp index ba6e2d270..28d43e815 100644 --- a/tests/core_tests/multisig.cpp +++ b/tests/core_tests/multisig.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/multisig.h b/tests/core_tests/multisig.h index 10fe6ffe8..462c74f46 100644 --- a/tests/core_tests/multisig.h +++ b/tests/core_tests/multisig.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp index 23638f62d..6bf708855 100644 --- a/tests/core_tests/rct.cpp +++ b/tests/core_tests/rct.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/rct.h b/tests/core_tests/rct.h index 9433d4b02..0aaa93c94 100644 --- a/tests/core_tests/rct.h +++ b/tests/core_tests/rct.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/ring_signature_1.cpp b/tests/core_tests/ring_signature_1.cpp index eb5ee48ec..b4a7d8853 100644 --- a/tests/core_tests/ring_signature_1.cpp +++ b/tests/core_tests/ring_signature_1.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/ring_signature_1.h b/tests/core_tests/ring_signature_1.h index 256f5732b..10e0ab9ee 100644 --- a/tests/core_tests/ring_signature_1.h +++ b/tests/core_tests/ring_signature_1.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/transaction_tests.cpp b/tests/core_tests/transaction_tests.cpp index 14028b5be..d0b55dcf4 100644 --- a/tests/core_tests/transaction_tests.cpp +++ b/tests/core_tests/transaction_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/transaction_tests.h b/tests/core_tests/transaction_tests.h index eac2c3479..e74d48296 100644 --- a/tests/core_tests/transaction_tests.h +++ b/tests/core_tests/transaction_tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/tx_pool.cpp b/tests/core_tests/tx_pool.cpp index b60dd00fc..1e496f3da 100644 --- a/tests/core_tests/tx_pool.cpp +++ b/tests/core_tests/tx_pool.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/tx_pool.h b/tests/core_tests/tx_pool.h index 8a27e98f0..1fbdcadf1 100644 --- a/tests/core_tests/tx_pool.h +++ b/tests/core_tests/tx_pool.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/tx_validation.cpp b/tests/core_tests/tx_validation.cpp index acdfdc41b..244763988 100644 --- a/tests/core_tests/tx_validation.cpp +++ b/tests/core_tests/tx_validation.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/tx_validation.h b/tests/core_tests/tx_validation.h index 8d457cdb5..5f91f1291 100644 --- a/tests/core_tests/tx_validation.h +++ b/tests/core_tests/tx_validation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/v2_tests.cpp b/tests/core_tests/v2_tests.cpp index d00d9b6f4..8baf5eb0a 100644 --- a/tests/core_tests/v2_tests.cpp +++ b/tests/core_tests/v2_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/v2_tests.h b/tests/core_tests/v2_tests.h index 71b0f0e83..23a6c43b6 100644 --- a/tests/core_tests/v2_tests.h +++ b/tests/core_tests/v2_tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/CMakeLists.txt b/tests/crypto/CMakeLists.txt index 883efb3b0..dcf5d8fb0 100644 --- a/tests/crypto/CMakeLists.txt +++ b/tests/crypto/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/tests/crypto/cnv4-jit.c b/tests/crypto/cnv4-jit.c index 0f11e4393..ac5bc0f52 100644 --- a/tests/crypto/cnv4-jit.c +++ b/tests/crypto/cnv4-jit.c @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/crypto-ops-data.c b/tests/crypto/crypto-ops-data.c index 63fedff07..0f187a3ea 100644 --- a/tests/crypto/crypto-ops-data.c +++ b/tests/crypto/crypto-ops-data.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/crypto-ops.c b/tests/crypto/crypto-ops.c index 9b1ccfcee..576b6e4b5 100644 --- a/tests/crypto/crypto-ops.c +++ b/tests/crypto/crypto-ops.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/crypto-tests.h b/tests/crypto/crypto-tests.h index be8d5a2e1..e833c0444 100644 --- a/tests/crypto/crypto-tests.h +++ b/tests/crypto/crypto-tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/crypto.cpp b/tests/crypto/crypto.cpp index 160e0a23f..145ec1d86 100644 --- a/tests/crypto/crypto.cpp +++ b/tests/crypto/crypto.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/hash.c b/tests/crypto/hash.c index d65f13f11..680f2c4b4 100644 --- a/tests/crypto/hash.c +++ b/tests/crypto/hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/main.cpp b/tests/crypto/main.cpp index 3cdef4ce7..f804c45dc 100644 --- a/tests/crypto/main.cpp +++ b/tests/crypto/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/random.c b/tests/crypto/random.c index 7a17cbccc..eff3b9bbe 100644 --- a/tests/crypto/random.c +++ b/tests/crypto/random.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/cryptolib.pl b/tests/cryptolib.pl index d26ce601c..42604c782 100644 --- a/tests/cryptolib.pl +++ b/tests/cryptolib.pl @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/tests/cryptotest.pl b/tests/cryptotest.pl index 57f6e2c5a..b5d4dd2df 100644 --- a/tests/cryptotest.pl +++ b/tests/cryptotest.pl @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/tests/daemon_tests/CMakeLists.txt b/tests/daemon_tests/CMakeLists.txt index a32f26584..7735b5ecb 100644 --- a/tests/daemon_tests/CMakeLists.txt +++ b/tests/daemon_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/tests/daemon_tests/transfers.cpp b/tests/daemon_tests/transfers.cpp index e20e6b280..5afe93457 100644 --- a/tests/daemon_tests/transfers.cpp +++ b/tests/daemon_tests/transfers.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/data/fuzz/tx-extra/TXEXTRA1 b/tests/data/fuzz/tx-extra/TXEXTRA1 Binary files differnew file mode 100644 index 000000000..08852abe3 --- /dev/null +++ b/tests/data/fuzz/tx-extra/TXEXTRA1 diff --git a/tests/data/fuzz/tx-extra/TXEXTRA2 b/tests/data/fuzz/tx-extra/TXEXTRA2 Binary files differnew file mode 100644 index 000000000..170301145 --- /dev/null +++ b/tests/data/fuzz/tx-extra/TXEXTRA2 diff --git a/tests/difficulty/CMakeLists.txt b/tests/difficulty/CMakeLists.txt index c4f5a5dc0..c5c3bfb02 100644 --- a/tests/difficulty/CMakeLists.txt +++ b/tests/difficulty/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/tests/difficulty/difficulty.cpp b/tests/difficulty/difficulty.cpp index 18f1d0030..aee17e9d5 100644 --- a/tests/difficulty/difficulty.cpp +++ b/tests/difficulty/difficulty.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/difficulty/generate-data b/tests/difficulty/generate-data index 3a9976e05..a566905fc 100755 --- a/tests/difficulty/generate-data +++ b/tests/difficulty/generate-data @@ -1,6 +1,6 @@ #!/usr/bin/python3 -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/tests/functional_tests/CMakeLists.txt b/tests/functional_tests/CMakeLists.txt index 702ccc646..6972be55f 100644 --- a/tests/functional_tests/CMakeLists.txt +++ b/tests/functional_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/tests/functional_tests/address_book.py b/tests/functional_tests/address_book.py index f9ec217af..37a5915c9 100755 --- a/tests/functional_tests/address_book.py +++ b/tests/functional_tests/address_book.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 #encoding=utf-8 -# Copyright (c) 2019 The Monero Project +# Copyright (c) 2019-2020 The Monero Project # # All rights reserved. # @@ -145,7 +145,7 @@ class AddressBookTest(): res = wallet.get_address_book() assert len(res.entries) == 1 e = res.entries[0] - assert e.address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A' + assert e.address == '888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H' assert e.description == 'dev fund' # UTF-8 @@ -173,7 +173,7 @@ class AddressBookTest(): # get them back res = wallet.get_address_book([0]) assert len(res.entries) == 1 - assert res.entries[0].address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A' + assert res.entries[0].address == '888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H' assert res.entries[0].description == 'dev fund' res = wallet.get_address_book([1]) assert len(res.entries) == 1 @@ -213,12 +213,12 @@ class AddressBookTest(): assert e.index == 1 assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' assert e.description == u'えんしゅう' - res = wallet.edit_address_book(1, address = '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A') + res = wallet.edit_address_book(1, address = '888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H') res = wallet.get_address_book([1]) assert len(res.entries) == 1 e = res.entries[0] assert e.index == 1 - assert e.address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A' + assert e.address == '888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H' assert e.description == u'えんしゅう' ok = False try: res = wallet.edit_address_book(1, address = '') @@ -237,7 +237,7 @@ class AddressBookTest(): wallet.delete_address_book(0) res = wallet.get_address_book([0]) # entries above the deleted one collapse one slot up assert len(res.entries) == 1 - assert res.entries[0].address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A' + assert res.entries[0].address == '888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H' assert res.entries[0].description == u'えんしゅう' wallet.delete_address_book(2) wallet.delete_address_book(0) diff --git a/tests/functional_tests/bans.py b/tests/functional_tests/bans.py index e859e58c9..3e265a11e 100755 --- a/tests/functional_tests/bans.py +++ b/tests/functional_tests/bans.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2019 The Monero Project +# Copyright (c) 2019-2020 The Monero Project # # All rights reserved. # diff --git a/tests/functional_tests/check_missing_rpc_methods.py b/tests/functional_tests/check_missing_rpc_methods.py index 6fadebf9b..0eedd6d0f 100644 --- a/tests/functional_tests/check_missing_rpc_methods.py +++ b/tests/functional_tests/check_missing_rpc_methods.py @@ -46,5 +46,6 @@ for module in modules: name = name[1:] if not hasattr(module['object'], name): print('Error: %s API method %s does not have a matching function' % (module['name'], name)) + error = True sys.exit(1 if error else 0) diff --git a/tests/functional_tests/cold_signing.py b/tests/functional_tests/cold_signing.py index 698217436..35e00e105 100755 --- a/tests/functional_tests/cold_signing.py +++ b/tests/functional_tests/cold_signing.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2019 The Monero Project +# Copyright (c) 2019-2020 The Monero Project # # All rights reserved. # diff --git a/tests/functional_tests/get_output_distribution.py b/tests/functional_tests/get_output_distribution.py index 077b094ba..24b6e9008 100755 --- a/tests/functional_tests/get_output_distribution.py +++ b/tests/functional_tests/get_output_distribution.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2019 The Monero Project +# Copyright (c) 2019-2020 The Monero Project # # All rights reserved. # diff --git a/tests/functional_tests/integrated_address.py b/tests/functional_tests/integrated_address.py index 4e42261a6..c3b730de0 100755 --- a/tests/functional_tests/integrated_address.py +++ b/tests/functional_tests/integrated_address.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2019 The Monero Project +# Copyright (c) 2019-2020 The Monero Project # # All rights reserved. # diff --git a/tests/functional_tests/main.cpp b/tests/functional_tests/main.cpp index c6869f755..8d140f721 100644 --- a/tests/functional_tests/main.cpp +++ b/tests/functional_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/functional_tests/make_test_signature.cc b/tests/functional_tests/make_test_signature.cc index f31816841..07cc3eea6 100644 --- a/tests/functional_tests/make_test_signature.cc +++ b/tests/functional_tests/make_test_signature.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/tests/functional_tests/multisig.py b/tests/functional_tests/multisig.py index e4e908aa1..0a58f469a 100755 --- a/tests/functional_tests/multisig.py +++ b/tests/functional_tests/multisig.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2019 The Monero Project +# Copyright (c) 2019-2020 The Monero Project # # All rights reserved. # diff --git a/tests/functional_tests/proofs.py b/tests/functional_tests/proofs.py index 7beb3ec6e..e58d29f94 100755 --- a/tests/functional_tests/proofs.py +++ b/tests/functional_tests/proofs.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2019 The Monero Project +# Copyright (c) 2019-2020 The Monero Project # # All rights reserved. # @@ -130,13 +130,13 @@ class ProofsTest(): sending_address = '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' receiving_address = '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW' res = self.wallet[0].get_tx_proof(txid, sending_address, 'foo'); - assert res.signature.startswith('InProof'); + assert res.signature.startswith('InProofV2'); signature0i = res.signature res = self.wallet[0].get_tx_proof(txid, receiving_address, 'bar'); - assert res.signature.startswith('OutProof'); + assert res.signature.startswith('OutProofV2'); signature0o = res.signature res = self.wallet[1].get_tx_proof(txid, receiving_address, 'baz'); - assert res.signature.startswith('InProof'); + assert res.signature.startswith('InProofV2'); signature1 = res.signature res = self.wallet[0].check_tx_proof(txid, sending_address, 'foo', signature0i); @@ -219,6 +219,23 @@ class ProofsTest(): except: ok = True assert ok or not res.good + + # Test bad cross-version verification + ok = False + try: res = self.wallet[0].check_tx_proof(txid, sending_address, 'foo', signature0i.replace('ProofV2','ProofV1')); + except: ok = True + assert ok or not res.good + + ok = False + try: res = self.wallet[0].check_tx_proof(txid, receiving_address, 'bar', signature0o.replace('ProofV2','ProofV1')); + except: ok = True + assert ok or not res.good + + ok = False + try: res = self.wallet[1].check_tx_proof(txid, receiving_address, 'baz', signature1.replace('ProofV2','ProofV1')); + except: ok = True + assert ok or not res.good + def check_spend_proof(self, txid): daemon = Daemon() @@ -270,7 +287,7 @@ class ProofsTest(): balance1 = res.balance res = self.wallet[0].get_reserve_proof(all_ = True, message = 'foo') - assert res.signature.startswith('ReserveProof') + assert res.signature.startswith('ReserveProofV2') signature = res.signature for i in range(2): res = self.wallet[i].check_reserve_proof(address = address0, message = 'foo', signature = signature) @@ -287,9 +304,15 @@ class ProofsTest(): except: ok = True assert ok or not res.good + # Test bad cross-version verification + ok = False + try: res = self.wallet[i].check_reserve_proof(address = address0, message = 'foo', signature = signature.replace('ProofV2','ProofV1')) + except: ok = True + assert ok or not res.good + amount = int(balance0 / 10) res = self.wallet[0].get_reserve_proof(all_ = False, amount = amount, message = 'foo') - assert res.signature.startswith('ReserveProof') + assert res.signature.startswith('ReserveProofV2') signature = res.signature for i in range(2): res = self.wallet[i].check_reserve_proof(address = address0, message = 'foo', signature = signature) @@ -306,6 +329,12 @@ class ProofsTest(): except: ok = True assert ok or not res.good + # Test bad cross-version verification + ok = False + try: res = self.wallet[i].check_reserve_proof(address = address0, message = 'foo', signature = signature.replace('ProofV2','ProofV1')) + except: ok = True + assert ok or not res.good + ok = False try: self.wallet[0].get_reserve_proof(all_ = False, amount = balance0 + 1, message = 'foo') except: ok = True diff --git a/tests/functional_tests/rpc_payment.py b/tests/functional_tests/rpc_payment.py index 5f23c2022..d0f9c2913 100755 --- a/tests/functional_tests/rpc_payment.py +++ b/tests/functional_tests/rpc_payment.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2019 The Monero Project +# Copyright (c) 2019-2020 The Monero Project # # All rights reserved. # diff --git a/tests/functional_tests/sign_message.py b/tests/functional_tests/sign_message.py index 9dd70f8bc..7ce2f2c18 100755 --- a/tests/functional_tests/sign_message.py +++ b/tests/functional_tests/sign_message.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2019 The Monero Project +# Copyright (c) 2019-2020 The Monero Project # # All rights reserved. # diff --git a/tests/functional_tests/speed.py b/tests/functional_tests/speed.py index 71be785b8..c0c1d23c2 100755 --- a/tests/functional_tests/speed.py +++ b/tests/functional_tests/speed.py @@ -67,7 +67,7 @@ class SpeedTest(): destinations = [] for i in range(3): - destinations.append({"amount":1,"address":'44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'}) + destinations.append({"amount":1,"address":'888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H'}) self._test_speed_generateblocks(daemon=daemon, blocks=70) for i in range(1, 10): @@ -89,7 +89,7 @@ class SpeedTest(): print('Test speed of transfer') start = time.time() - destinations = [{"amount":1,"address":'44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'}] + destinations = [{"amount":1,"address":'888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H'}] res = wallet.transfer_split(destinations) print('generating tx took: ', time.time() - start, 'seconds') diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp index 218590a27..cec6825f5 100644 --- a/tests/functional_tests/transactions_flow_test.cpp +++ b/tests/functional_tests/transactions_flow_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/functional_tests/transactions_flow_test.h b/tests/functional_tests/transactions_flow_test.h index 530400df8..8e2dfd92c 100644 --- a/tests/functional_tests/transactions_flow_test.h +++ b/tests/functional_tests/transactions_flow_test.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/functional_tests/transactions_generation_from_blockchain.cpp b/tests/functional_tests/transactions_generation_from_blockchain.cpp index b9c43e0e9..3ba401529 100644 --- a/tests/functional_tests/transactions_generation_from_blockchain.cpp +++ b/tests/functional_tests/transactions_generation_from_blockchain.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/functional_tests/transactions_generation_from_blockchain.h b/tests/functional_tests/transactions_generation_from_blockchain.h index cb5f94f80..70a2486d6 100644 --- a/tests/functional_tests/transactions_generation_from_blockchain.h +++ b/tests/functional_tests/transactions_generation_from_blockchain.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/functional_tests/transfer.py b/tests/functional_tests/transfer.py index c3d71aa9c..132758f50 100755 --- a/tests/functional_tests/transfer.py +++ b/tests/functional_tests/transfer.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2019 The Monero Project +# Copyright (c) 2019-2020 The Monero Project # # All rights reserved. # diff --git a/tests/functional_tests/txpool.py b/tests/functional_tests/txpool.py index 27ae89764..e886200da 100755 --- a/tests/functional_tests/txpool.py +++ b/tests/functional_tests/txpool.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2019 The Monero Project +# Copyright (c) 2019-2020 The Monero Project # # All rights reserved. # @@ -241,6 +241,17 @@ class TransferTest(): assert x.fee == txes[txid].fee assert x.tx_blob == txes[txid].tx_blob + print('Checking relaying txes') + res = daemon.get_transaction_pool_hashes() + assert len(res.tx_hashes) > 0 + txid = res.tx_hashes[0] + daemon.relay_tx([txid]) + res = daemon.get_transactions([txid]) + assert len(res.txs) == 1 + assert res.txs[0].tx_hash == txid + assert res.txs[0].in_pool + assert res.txs[0].relayed + daemon.flush_txpool() self.check_empty_pool() diff --git a/tests/functional_tests/uri.py b/tests/functional_tests/uri.py index f759b316a..c193ac926 100755 --- a/tests/functional_tests/uri.py +++ b/tests/functional_tests/uri.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 #encoding=utf-8 -# Copyright (c) 2019 The Monero Project +# Copyright (c) 2019-2020 The Monero Project # # All rights reserved. # diff --git a/tests/functional_tests/validate_address.py b/tests/functional_tests/validate_address.py index 7c3d8abfa..d201cdf68 100755 --- a/tests/functional_tests/validate_address.py +++ b/tests/functional_tests/validate_address.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (c) 2019 The Monero Project +# Copyright (c) 2019-2020 The Monero Project # # All rights reserved. # @@ -94,7 +94,7 @@ class AddressValidationTest(): def check_openalias_addresses(self): print('Validating openalias addresses') addresses = [ - ['donate@getmonero.org', '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'] + ['donate@getmonero.org', '888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H'] ] for address in addresses: res = self.wallet.validate_address(address[0]) @@ -102,7 +102,7 @@ class AddressValidationTest(): res = self.wallet.validate_address(address[0], allow_openalias = True) assert res.valid assert not res.integrated - assert not res.subaddress + assert res.subaddress assert res.nettype == 'mainnet' assert res.openalias_address == address[1] diff --git a/tests/functional_tests/wallet.py b/tests/functional_tests/wallet.py index 5bb3ec80a..91164daf9 100755 --- a/tests/functional_tests/wallet.py +++ b/tests/functional_tests/wallet.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 #encoding=utf-8 -# Copyright (c) 2019 The Monero Project +# Copyright (c) 2019-2020 The Monero Project # # All rights reserved. # diff --git a/tests/fuzz/CMakeLists.txt b/tests/fuzz/CMakeLists.txt index 8654d41d5..48a49edab 100644 --- a/tests/fuzz/CMakeLists.txt +++ b/tests/fuzz/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # @@ -200,3 +200,21 @@ set_property(TARGET bulletproof_fuzz_tests PROPERTY FOLDER "tests") +add_executable(tx-extra_fuzz_tests tx-extra.cpp fuzzer.cpp) +target_link_libraries(tx-extra_fuzz_tests + PRIVATE + cryptonote_basic + common + epee + ${Boost_THREAD_LIBRARY} + ${Boost_CHRONO_LIBRARY} + ${Boost_REGEX_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_SYSTEM_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT} + ${EXTRA_LIBRARIES} + $ENV{LIB_FUZZING_ENGINE}) +set_property(TARGET tx-extra_fuzz_tests + PROPERTY + FOLDER "tests") + diff --git a/tests/fuzz/base58.cpp b/tests/fuzz/base58.cpp index 08fa402dd..3a06353ec 100644 --- a/tests/fuzz/base58.cpp +++ b/tests/fuzz/base58.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/block.cpp b/tests/fuzz/block.cpp index 44818f8cd..f8be29842 100644 --- a/tests/fuzz/block.cpp +++ b/tests/fuzz/block.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/bulletproof.cpp b/tests/fuzz/bulletproof.cpp index 7e58770ca..e0f183bc5 100644 --- a/tests/fuzz/bulletproof.cpp +++ b/tests/fuzz/bulletproof.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/cold-outputs.cpp b/tests/fuzz/cold-outputs.cpp index af0a33422..fea235079 100644 --- a/tests/fuzz/cold-outputs.cpp +++ b/tests/fuzz/cold-outputs.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -34,16 +34,19 @@ #include "wallet/wallet2.h" #include "fuzzer.h" -static tools::wallet2 wallet; +static tools::wallet2 *wallet = NULL; BEGIN_INIT_SIMPLE_FUZZER() + static tools::wallet2 local_wallet; + wallet = &local_wallet; + static const char * const spendkey_hex = "0b4f47697ec99c3de6579304e5f25c68b07afbe55b71d99620bf6cbf4e45a80f"; crypto::secret_key spendkey; epee::string_tools::hex_to_pod(spendkey_hex, spendkey); - wallet.init("", boost::none, boost::asio::ip::tcp::endpoint{}, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled); - wallet.set_subaddress_lookahead(1, 1); - wallet.generate("", "", spendkey, true, false); + wallet->init("", boost::none, boost::asio::ip::tcp::endpoint{}, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled); + wallet->set_subaddress_lookahead(1, 1); + wallet->generate("", "", spendkey, true, false); END_INIT_SIMPLE_FUZZER() BEGIN_SIMPLE_FUZZER() @@ -53,6 +56,6 @@ BEGIN_SIMPLE_FUZZER() iss << s; boost::archive::portable_binary_iarchive ar(iss); ar >> outputs; - size_t n_outputs = wallet.import_outputs(outputs); + size_t n_outputs = wallet->import_outputs(outputs); std::cout << boost::lexical_cast<std::string>(n_outputs) << " outputs imported" << std::endl; END_SIMPLE_FUZZER() diff --git a/tests/fuzz/cold-transaction.cpp b/tests/fuzz/cold-transaction.cpp index 9808362e4..32c84ac74 100644 --- a/tests/fuzz/cold-transaction.cpp +++ b/tests/fuzz/cold-transaction.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -34,16 +34,19 @@ #include "wallet/wallet2.h" #include "fuzzer.h" -static tools::wallet2 wallet; +static tools::wallet2 *wallet = NULL; BEGIN_INIT_SIMPLE_FUZZER() + static tools::wallet2 local_wallet; + wallet = &local_wallet; + static const char * const spendkey_hex = "0b4f47697ec99c3de6579304e5f25c68b07afbe55b71d99620bf6cbf4e45a80f"; crypto::secret_key spendkey; epee::string_tools::hex_to_pod(spendkey_hex, spendkey); - wallet.init("", boost::none, boost::asio::ip::tcp::endpoint{}, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled); - wallet.set_subaddress_lookahead(1, 1); - wallet.generate("", "", spendkey, true, false); + wallet->init("", boost::none, boost::asio::ip::tcp::endpoint{}, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled); + wallet->set_subaddress_lookahead(1, 1); + wallet->generate("", "", spendkey, true, false); END_INIT_SIMPLE_FUZZER() BEGIN_SIMPLE_FUZZER() @@ -54,6 +57,6 @@ BEGIN_SIMPLE_FUZZER() boost::archive::portable_binary_iarchive ar(iss); ar >> exported_txs; std::vector<tools::wallet2::pending_tx> ptx; - bool success = wallet.sign_tx(exported_txs, "/tmp/cold-transaction-test-signed", ptx); + bool success = wallet->sign_tx(exported_txs, "/tmp/cold-transaction-test-signed", ptx); std::cout << (success ? "signed" : "error") << std::endl; END_SIMPLE_FUZZER() diff --git a/tests/fuzz/fuzzer.cpp b/tests/fuzz/fuzzer.cpp index 0d2366263..eaaef5aa5 100644 --- a/tests/fuzz/fuzzer.cpp +++ b/tests/fuzz/fuzzer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/fuzzer.h b/tests/fuzz/fuzzer.h index 2d0a29dfc..f45e69f73 100644 --- a/tests/fuzz/fuzzer.h +++ b/tests/fuzz/fuzzer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -56,7 +56,7 @@ extern "C" { \ static bool first = true; \ if (first) \ { \ - if (!init()) \ + if (init()) \ return 1; \ first = false; \ } \ @@ -66,8 +66,12 @@ extern "C" { \ catch (const std::exception &e) \ { \ fprintf(stderr, "Exception: %s\n", e.what()); \ - return 1; \ + delete el::base::elStorage; \ + el::base::elStorage = NULL; \ + return 0; \ } \ + delete el::base::elStorage; \ + el::base::elStorage = NULL; \ return 0; \ } \ } @@ -122,8 +126,12 @@ int run_fuzzer(int argc, const char **argv, Fuzzer &fuzzer); catch (const std::exception &e) \ { \ fprintf(stderr, "Exception: %s\n", e.what()); \ - return 1; \ + delete el::base::elStorage; \ + el::base::elStorage = NULL; \ + return 0; \ } \ + delete el::base::elStorage; \ + el::base::elStorage = NULL; \ return 0; \ } \ }; \ diff --git a/tests/fuzz/http-client.cpp b/tests/fuzz/http-client.cpp index 1801affee..4b12b36bb 100644 --- a/tests/fuzz/http-client.cpp +++ b/tests/fuzz/http-client.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/levin.cpp b/tests/fuzz/levin.cpp index ab7bbb6da..012d05f36 100644 --- a/tests/fuzz/levin.cpp +++ b/tests/fuzz/levin.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/load_from_binary.cpp b/tests/fuzz/load_from_binary.cpp index b185df522..6d6826e2e 100644 --- a/tests/fuzz/load_from_binary.cpp +++ b/tests/fuzz/load_from_binary.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/load_from_json.cpp b/tests/fuzz/load_from_json.cpp index 0252360ba..8788a92b6 100644 --- a/tests/fuzz/load_from_json.cpp +++ b/tests/fuzz/load_from_json.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/parse_url.cpp b/tests/fuzz/parse_url.cpp index 41f4319a6..cc1323eb5 100644 --- a/tests/fuzz/parse_url.cpp +++ b/tests/fuzz/parse_url.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/signature.cpp b/tests/fuzz/signature.cpp index cd65e42d0..8f528b20e 100644 --- a/tests/fuzz/signature.cpp +++ b/tests/fuzz/signature.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // @@ -34,17 +34,20 @@ #include "wallet/wallet2.h" #include "fuzzer.h" -static tools::wallet2 wallet(cryptonote::TESTNET); +static tools::wallet2 *wallet = NULL; static cryptonote::account_public_address address; BEGIN_INIT_SIMPLE_FUZZER() + static tools::wallet2 local_wallet(cryptonote::TESTNET); + wallet = &local_wallet; + static const char * const spendkey_hex = "0b4f47697ec99c3de6579304e5f25c68b07afbe55b71d99620bf6cbf4e45a80f"; crypto::secret_key spendkey; epee::string_tools::hex_to_pod(spendkey_hex, spendkey); - wallet.init("", boost::none, boost::asio::ip::tcp::endpoint{}, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled); - wallet.set_subaddress_lookahead(1, 1); - wallet.generate("", "", spendkey, true, false); + wallet->init("", boost::none, boost::asio::ip::tcp::endpoint{}, 0, true, epee::net_utils::ssl_support_t::e_ssl_support_disabled); + wallet->set_subaddress_lookahead(1, 1); + wallet->generate("", "", spendkey, true, false); cryptonote::address_parse_info info; if (!cryptonote::get_account_address_from_str_or_url(info, cryptonote::TESTNET, "9uVsvEryzpN8WH2t1WWhFFCG5tS8cBNdmJYNRuckLENFimfauV5pZKeS1P2CbxGkSDTUPHXWwiYE5ZGSXDAGbaZgDxobqDN")) @@ -56,6 +59,6 @@ BEGIN_INIT_SIMPLE_FUZZER() END_INIT_SIMPLE_FUZZER() BEGIN_SIMPLE_FUZZER() - bool valid = wallet.verify("test", address, std::string((const char*)buf, len)); + bool valid = wallet->verify("test", address, std::string((const char*)buf, len)); std::cout << "Signature " << (valid ? "valid" : "invalid") << std::endl; END_SIMPLE_FUZZER() diff --git a/tests/fuzz/transaction.cpp b/tests/fuzz/transaction.cpp index 1e4a61a78..94f0a849f 100644 --- a/tests/fuzz/transaction.cpp +++ b/tests/fuzz/transaction.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/tx-extra.cpp b/tests/fuzz/tx-extra.cpp new file mode 100644 index 000000000..35b14b802 --- /dev/null +++ b/tests/fuzz/tx-extra.cpp @@ -0,0 +1,40 @@ +// Copyright (c) 2020, 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 "include_base_utils.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "fuzzer.h" + +BEGIN_INIT_SIMPLE_FUZZER() +END_INIT_SIMPLE_FUZZER() + +BEGIN_SIMPLE_FUZZER() + std::vector<cryptonote::tx_extra_field> tx_extra_fields; + cryptonote::parse_tx_extra(std::vector<uint8_t>(buf, buf + len), tx_extra_fields); +END_SIMPLE_FUZZER() + diff --git a/tests/hash-target.cpp b/tests/hash-target.cpp index e95475cac..038eaab47 100644 --- a/tests/hash-target.cpp +++ b/tests/hash-target.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/hash/CMakeLists.txt b/tests/hash/CMakeLists.txt index a0c78bfdc..91dfdff1e 100644 --- a/tests/hash/CMakeLists.txt +++ b/tests/hash/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/tests/hash/main.cpp b/tests/hash/main.cpp index d62098a60..bf612684c 100644 --- a/tests/hash/main.cpp +++ b/tests/hash/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/io.h b/tests/io.h index 0f65ed297..f5e2b518d 100644 --- a/tests/io.h +++ b/tests/io.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/libwallet_api_tests/CMakeLists.txt b/tests/libwallet_api_tests/CMakeLists.txt index cb1d563b0..8d03399fa 100644 --- a/tests/libwallet_api_tests/CMakeLists.txt +++ b/tests/libwallet_api_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/tests/libwallet_api_tests/main.cpp b/tests/libwallet_api_tests/main.cpp index c34da04b7..8413d8621 100644 --- a/tests/libwallet_api_tests/main.cpp +++ b/tests/libwallet_api_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/net_load_tests/CMakeLists.txt b/tests/net_load_tests/CMakeLists.txt index 40b5561a7..9bcc2d6ab 100644 --- a/tests/net_load_tests/CMakeLists.txt +++ b/tests/net_load_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/tests/net_load_tests/clt.cpp b/tests/net_load_tests/clt.cpp index e154363e7..b55f73971 100644 --- a/tests/net_load_tests/clt.cpp +++ b/tests/net_load_tests/clt.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/net_load_tests/net_load_tests.h b/tests/net_load_tests/net_load_tests.h index 4a76f2ec6..882d42c02 100644 --- a/tests/net_load_tests/net_load_tests.h +++ b/tests/net_load_tests/net_load_tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/net_load_tests/srv.cpp b/tests/net_load_tests/srv.cpp index b42b1e1b0..84d97ebd4 100644 --- a/tests/net_load_tests/srv.cpp +++ b/tests/net_load_tests/srv.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/CMakeLists.txt b/tests/performance_tests/CMakeLists.txt index b36df10dc..1a9da116f 100644 --- a/tests/performance_tests/CMakeLists.txt +++ b/tests/performance_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/tests/performance_tests/bulletproof.h b/tests/performance_tests/bulletproof.h index 7136fbbfe..835d518f6 100644 --- a/tests/performance_tests/bulletproof.h +++ b/tests/performance_tests/bulletproof.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/check_hash.h b/tests/performance_tests/check_hash.h index 294654f37..09737defb 100644 --- a/tests/performance_tests/check_hash.h +++ b/tests/performance_tests/check_hash.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/check_tx_signature.h b/tests/performance_tests/check_tx_signature.h index 329714c56..5122336e7 100644 --- a/tests/performance_tests/check_tx_signature.h +++ b/tests/performance_tests/check_tx_signature.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/cn_fast_hash.h b/tests/performance_tests/cn_fast_hash.h index 63b2c5bee..f2177232f 100644 --- a/tests/performance_tests/cn_fast_hash.h +++ b/tests/performance_tests/cn_fast_hash.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/cn_slow_hash.h b/tests/performance_tests/cn_slow_hash.h index 4f410d62d..e124e00b0 100644 --- a/tests/performance_tests/cn_slow_hash.h +++ b/tests/performance_tests/cn_slow_hash.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/construct_tx.h b/tests/performance_tests/construct_tx.h index ebfe46edf..6bf7a82ef 100644 --- a/tests/performance_tests/construct_tx.h +++ b/tests/performance_tests/construct_tx.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/crypto_ops.h b/tests/performance_tests/crypto_ops.h index 2859873ef..ae00bb517 100644 --- a/tests/performance_tests/crypto_ops.h +++ b/tests/performance_tests/crypto_ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/derive_public_key.h b/tests/performance_tests/derive_public_key.h index ad0b3b55f..7980d2cd4 100644 --- a/tests/performance_tests/derive_public_key.h +++ b/tests/performance_tests/derive_public_key.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/derive_secret_key.h b/tests/performance_tests/derive_secret_key.h index ba01b57c0..f395d0ccf 100644 --- a/tests/performance_tests/derive_secret_key.h +++ b/tests/performance_tests/derive_secret_key.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/equality.h b/tests/performance_tests/equality.h index 5788ebec8..1748ce096 100644 --- a/tests/performance_tests/equality.h +++ b/tests/performance_tests/equality.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -35,7 +35,7 @@ struct memcmp32 { - static const size_t loop_count = 1000000000; + static const size_t loop_count = 10000000; static int call(const unsigned char *k0, const unsigned char *k1){ return memcmp(k0, k1, 32); } }; diff --git a/tests/performance_tests/ge_frombytes_vartime.h b/tests/performance_tests/ge_frombytes_vartime.h index 1a0601c74..1f70ec345 100644 --- a/tests/performance_tests/ge_frombytes_vartime.h +++ b/tests/performance_tests/ge_frombytes_vartime.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/ge_tobytes.h b/tests/performance_tests/ge_tobytes.h index 66e6fce38..2bcbfc9d7 100644 --- a/tests/performance_tests/ge_tobytes.h +++ b/tests/performance_tests/ge_tobytes.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/generate_key_derivation.h b/tests/performance_tests/generate_key_derivation.h index 1f0066589..ee6b2e4c7 100644 --- a/tests/performance_tests/generate_key_derivation.h +++ b/tests/performance_tests/generate_key_derivation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/generate_key_image.h b/tests/performance_tests/generate_key_image.h index df39d7088..53afd020b 100644 --- a/tests/performance_tests/generate_key_image.h +++ b/tests/performance_tests/generate_key_image.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/generate_key_image_helper.h b/tests/performance_tests/generate_key_image_helper.h index d4d83c07d..38ffa3e50 100644 --- a/tests/performance_tests/generate_key_image_helper.h +++ b/tests/performance_tests/generate_key_image_helper.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/generate_keypair.h b/tests/performance_tests/generate_keypair.h index cf28f7f58..66c83162d 100644 --- a/tests/performance_tests/generate_keypair.h +++ b/tests/performance_tests/generate_keypair.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/is_out_to_acc.h b/tests/performance_tests/is_out_to_acc.h index 8b4516843..023a7a13b 100644 --- a/tests/performance_tests/is_out_to_acc.h +++ b/tests/performance_tests/is_out_to_acc.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index d3ba57270..ca0528e16 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/multi_tx_test_base.h b/tests/performance_tests/multi_tx_test_base.h index bdbbee37d..39ab6b26f 100644 --- a/tests/performance_tests/multi_tx_test_base.h +++ b/tests/performance_tests/multi_tx_test_base.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/performance_tests.h b/tests/performance_tests/performance_tests.h index 606c5980f..ae7aabe08 100644 --- a/tests/performance_tests/performance_tests.h +++ b/tests/performance_tests/performance_tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -195,7 +195,7 @@ void run_test(const std::string &filter, Params ¶ms, const char* test_name) scale = 1000; time_per_call = runner.time_per_call(1000); #ifdef _WIN32 - unit = "\xb5s"; + unit = "us"; #else unit = "µs"; #endif diff --git a/tests/performance_tests/performance_utils.h b/tests/performance_tests/performance_utils.h index 8a45bea90..8617058f4 100644 --- a/tests/performance_tests/performance_utils.h +++ b/tests/performance_tests/performance_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/range_proof.h b/tests/performance_tests/range_proof.h index 548237814..e1eef7343 100644 --- a/tests/performance_tests/range_proof.h +++ b/tests/performance_tests/range_proof.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/rct_mlsag.h b/tests/performance_tests/rct_mlsag.h index da0c064fd..638dd6385 100644 --- a/tests/performance_tests/rct_mlsag.h +++ b/tests/performance_tests/rct_mlsag.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/sc_reduce32.h b/tests/performance_tests/sc_reduce32.h index 8db1ca70a..8da9f8b3d 100644 --- a/tests/performance_tests/sc_reduce32.h +++ b/tests/performance_tests/sc_reduce32.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/signature.h b/tests/performance_tests/signature.h index 63c63ea46..44d4ad78c 100644 --- a/tests/performance_tests/signature.h +++ b/tests/performance_tests/signature.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/single_tx_test_base.h b/tests/performance_tests/single_tx_test_base.h index 52fbf5f80..59222be70 100644 --- a/tests/performance_tests/single_tx_test_base.h +++ b/tests/performance_tests/single_tx_test_base.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/subaddress_expand.h b/tests/performance_tests/subaddress_expand.h index 61fcb41f3..5bbf344a4 100644 --- a/tests/performance_tests/subaddress_expand.h +++ b/tests/performance_tests/subaddress_expand.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 9c24a5ec7..a5984b2c9 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # @@ -83,6 +83,7 @@ set(unit_tests_sources test_peerlist.cpp test_protocol_pack.cpp threadpool.cpp + tx_proof.cpp hardfork.cpp unbound.cpp uri.cpp @@ -109,6 +110,7 @@ target_link_libraries(unit_tests cryptonote_protocol cryptonote_core daemon_messages + daemon_rpc_server blockchain_db lmdb_lib rpc diff --git a/tests/unit_tests/account.cpp b/tests/unit_tests/account.cpp index 7073ab3e8..68bf4dce7 100644 --- a/tests/unit_tests/account.cpp +++ b/tests/unit_tests/account.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -29,14 +29,30 @@ #include "gtest/gtest.h" #include "cryptonote_basic/account.h" +#include "ringct/rctOps.h" +// Tests in-memory encryption of account secret keys TEST(account, encrypt_keys) { + // Generate account keys and random multisig keys cryptonote::keypair recovery_key = cryptonote::keypair::generate(hw::get_device("default")); cryptonote::account_base account; crypto::secret_key key = account.generate(recovery_key.sec); + + const size_t n_multisig = 4; + std::vector<crypto::secret_key> multisig_keys; + multisig_keys.reserve(n_multisig); + multisig_keys.resize(0); + for (size_t i = 0; i < n_multisig; ++i) + { + multisig_keys.push_back(rct::rct2sk(rct::skGen())); + } + ASSERT_TRUE(account.make_multisig(account.get_keys().m_view_secret_key, account.get_keys().m_spend_secret_key, account.get_keys().m_account_address.m_spend_public_key, multisig_keys)); + const cryptonote::account_keys keys = account.get_keys(); + ASSERT_EQ(keys.m_multisig_keys.size(),n_multisig); + // Encrypt and decrypt keys ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address); ASSERT_EQ(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key); ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key); @@ -50,22 +66,40 @@ TEST(account, encrypt_keys) ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address); ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key); ASSERT_NE(account.get_keys().m_view_secret_key, keys.m_view_secret_key); + ASSERT_NE(account.get_keys().m_multisig_keys, keys.m_multisig_keys); account.decrypt_viewkey(chacha_key); ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address); ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key); ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key); + ASSERT_NE(account.get_keys().m_multisig_keys, keys.m_multisig_keys); account.encrypt_viewkey(chacha_key); ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address); ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key); ASSERT_NE(account.get_keys().m_view_secret_key, keys.m_view_secret_key); + ASSERT_NE(account.get_keys().m_multisig_keys, keys.m_multisig_keys); + + account.decrypt_viewkey(chacha_key); + + ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address); + ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key); + ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key); + ASSERT_NE(account.get_keys().m_multisig_keys, keys.m_multisig_keys); + + account.encrypt_viewkey(chacha_key); + + ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address); + ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key); + ASSERT_NE(account.get_keys().m_view_secret_key, keys.m_view_secret_key); + ASSERT_NE(account.get_keys().m_multisig_keys, keys.m_multisig_keys); account.decrypt_keys(chacha_key); ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address); ASSERT_EQ(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key); ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key); + ASSERT_EQ(account.get_keys().m_multisig_keys, keys.m_multisig_keys); } diff --git a/tests/unit_tests/address_from_url.cpp b/tests/unit_tests/address_from_url.cpp index 4b06c6487..b6b205aac 100644 --- a/tests/unit_tests/address_from_url.cpp +++ b/tests/unit_tests/address_from_url.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/apply_permutation.cpp b/tests/unit_tests/apply_permutation.cpp index 76d10c8ff..9376dc10a 100644 --- a/tests/unit_tests/apply_permutation.cpp +++ b/tests/unit_tests/apply_permutation.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/base58.cpp b/tests/unit_tests/base58.cpp index 1996afd04..b7e9dfcf1 100644 --- a/tests/unit_tests/base58.cpp +++ b/tests/unit_tests/base58.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/block_queue.cpp b/tests/unit_tests/block_queue.cpp index 0cba7c443..77c7f97ff 100644 --- a/tests/unit_tests/block_queue.cpp +++ b/tests/unit_tests/block_queue.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/block_reward.cpp b/tests/unit_tests/block_reward.cpp index 1dd9072b9..5f3335981 100644 --- a/tests/unit_tests/block_reward.cpp +++ b/tests/unit_tests/block_reward.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/blockchain_db.cpp b/tests/unit_tests/blockchain_db.cpp index 6b569d113..29cdd73b9 100644 --- a/tests/unit_tests/blockchain_db.cpp +++ b/tests/unit_tests/blockchain_db.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp index a73b80848..7c6e459f5 100644 --- a/tests/unit_tests/bulletproofs.cpp +++ b/tests/unit_tests/bulletproofs.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/canonical_amounts.cpp b/tests/unit_tests/canonical_amounts.cpp index 74fcc9548..5c141098b 100644 --- a/tests/unit_tests/canonical_amounts.cpp +++ b/tests/unit_tests/canonical_amounts.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/chacha.cpp b/tests/unit_tests/chacha.cpp index e06081f8f..4e434de4d 100644 --- a/tests/unit_tests/chacha.cpp +++ b/tests/unit_tests/chacha.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/checkpoints.cpp b/tests/unit_tests/checkpoints.cpp index 90229a0b8..26f2b51f7 100644 --- a/tests/unit_tests/checkpoints.cpp +++ b/tests/unit_tests/checkpoints.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/command_line.cpp b/tests/unit_tests/command_line.cpp index 6d5afb708..09c41a33c 100644 --- a/tests/unit_tests/command_line.cpp +++ b/tests/unit_tests/command_line.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/crypto.cpp b/tests/unit_tests/crypto.cpp index 7100c8013..7ac87ca7c 100644 --- a/tests/unit_tests/crypto.cpp +++ b/tests/unit_tests/crypto.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/decompose_amount_into_digits.cpp b/tests/unit_tests/decompose_amount_into_digits.cpp index 5049dd83d..a4c850bbe 100644 --- a/tests/unit_tests/decompose_amount_into_digits.cpp +++ b/tests/unit_tests/decompose_amount_into_digits.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/difficulty.cpp b/tests/unit_tests/difficulty.cpp index e9e3272f0..008b9a94f 100644 --- a/tests/unit_tests/difficulty.cpp +++ b/tests/unit_tests/difficulty.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/dns_resolver.cpp b/tests/unit_tests/dns_resolver.cpp index 6bffc01d8..b0157bb0a 100644 --- a/tests/unit_tests/dns_resolver.cpp +++ b/tests/unit_tests/dns_resolver.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/epee_boosted_tcp_server.cpp b/tests/unit_tests/epee_boosted_tcp_server.cpp index 32989f545..71098f612 100644 --- a/tests/unit_tests/epee_boosted_tcp_server.cpp +++ b/tests/unit_tests/epee_boosted_tcp_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/epee_levin_protocol_handler_async.cpp b/tests/unit_tests/epee_levin_protocol_handler_async.cpp index b27699a77..314fcf9f2 100644 --- a/tests/unit_tests/epee_levin_protocol_handler_async.cpp +++ b/tests/unit_tests/epee_levin_protocol_handler_async.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp index 2e93f9e67..0f91671a7 100644 --- a/tests/unit_tests/epee_utils.cpp +++ b/tests/unit_tests/epee_utils.cpp @@ -387,6 +387,29 @@ TEST(ByteSlice, Construction) EXPECT_FALSE(std::is_copy_assignable<epee::byte_slice>()); } +TEST(ByteSlice, DataReturnedMatches) +{ + for (int i = 64; i > 0; i--) + { + std::string sso_string(i, 'a'); + std::string original = sso_string; + epee::byte_slice slice{std::move(sso_string)}; + + EXPECT_EQ(slice.size(), original.size()); + EXPECT_EQ(memcmp(slice.data(), original.data(), original.size()), 0); + } + + for (int i = 64; i > 0; i--) + { + std::vector<uint8_t> sso_vector(i, 'a'); + std::vector<uint8_t> original = sso_vector; + epee::byte_slice slice{std::move(sso_vector)}; + + EXPECT_EQ(slice.size(), original.size()); + EXPECT_EQ(memcmp(slice.data(), original.data(), original.size()), 0); + } +} + TEST(ByteSlice, NoExcept) { EXPECT_TRUE(std::is_nothrow_default_constructible<epee::byte_slice>()); @@ -667,6 +690,23 @@ TEST(ByteSlice, TakeSlice) EXPECT_TRUE(boost::range::equal(base_string, slice)); const epee::span<const std::uint8_t> original = epee::to_span(slice); + const epee::byte_slice empty_slice = slice.take_slice(0); + EXPECT_EQ(original.begin(), slice.begin()); + EXPECT_EQ(slice.begin(), slice.cbegin()); + EXPECT_EQ(original.end(), slice.end()); + EXPECT_EQ(slice.end(), slice.cend()); + + EXPECT_EQ(nullptr, empty_slice.begin()); + EXPECT_EQ(nullptr, empty_slice.cbegin()); + EXPECT_EQ(nullptr, empty_slice.end()); + EXPECT_EQ(nullptr, empty_slice.cend()); + EXPECT_EQ(nullptr, empty_slice.data()); + EXPECT_TRUE(empty_slice.empty()); + EXPECT_EQ(0u, empty_slice.size()); + + EXPECT_FALSE(slice.empty()); + EXPECT_EQ(slice.cbegin(), slice.data()); + const epee::byte_slice slice2 = slice.take_slice(remove_size); EXPECT_EQ(original.begin() + remove_size, slice.begin()); @@ -1061,6 +1101,20 @@ TEST(ByteStream, ToByteSlice) EXPECT_EQ(nullptr, stream.data()); EXPECT_EQ(nullptr, stream.tellp()); EXPECT_TRUE(equal(source, slice)); + + stream = epee::byte_stream{}; + stream.reserve(1); + EXPECT_NE(nullptr, stream.data()); + EXPECT_NE(nullptr, stream.tellp()); + + const epee::byte_slice empty_slice{std::move(stream)}; + EXPECT_TRUE(empty_slice.empty()); + EXPECT_EQ(0u, empty_slice.size()); + EXPECT_EQ(nullptr, empty_slice.begin()); + EXPECT_EQ(nullptr, empty_slice.cbegin()); + EXPECT_EQ(nullptr, empty_slice.end()); + EXPECT_EQ(nullptr, empty_slice.cend()); + EXPECT_EQ(nullptr, empty_slice.data()); } TEST(ToHex, String) diff --git a/tests/unit_tests/fee.cpp b/tests/unit_tests/fee.cpp index 3b0bc1f09..d98b9e974 100644 --- a/tests/unit_tests/fee.cpp +++ b/tests/unit_tests/fee.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/get_xtype_from_string.cpp b/tests/unit_tests/get_xtype_from_string.cpp index 75452894a..78bbe6657 100644 --- a/tests/unit_tests/get_xtype_from_string.cpp +++ b/tests/unit_tests/get_xtype_from_string.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index 1d1e8e9c0..6d7e9a8aa 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/hashchain.cpp b/tests/unit_tests/hashchain.cpp index d07dfdb7a..5d85e960c 100644 --- a/tests/unit_tests/hashchain.cpp +++ b/tests/unit_tests/hashchain.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/http.cpp b/tests/unit_tests/http.cpp index 938f444d7..df3088a8b 100644 --- a/tests/unit_tests/http.cpp +++ b/tests/unit_tests/http.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/json_serialization.cpp b/tests/unit_tests/json_serialization.cpp index 5873d0ab6..f76199e57 100644 --- a/tests/unit_tests/json_serialization.cpp +++ b/tests/unit_tests/json_serialization.cpp @@ -15,7 +15,7 @@ #include "serialization/json_object.h" -namespace +namespace test { cryptonote::transaction make_miner_transaction(cryptonote::account_public_address const& to) @@ -82,7 +82,10 @@ namespace return tx; } +} +namespace +{ template<typename T> T test_json(const T& value) { @@ -94,7 +97,7 @@ namespace rapidjson::Document doc; doc.Parse(reinterpret_cast<const char*>(buffer.data()), buffer.size()); - if (doc.HasParseError() || !doc.IsObject()) + if (doc.HasParseError()) { throw cryptonote::json::PARSE_FAIL(); } @@ -105,11 +108,26 @@ namespace } } // anonymous +TEST(JsonSerialization, VectorBytes) +{ + EXPECT_EQ(std::vector<std::uint8_t>{}, test_json(std::vector<std::uint8_t>{})); + EXPECT_EQ(std::vector<std::uint8_t>{0x00}, test_json(std::vector<std::uint8_t>{0x00})); +} + +TEST(JsonSerialization, InvalidVectorBytes) +{ + rapidjson::Document doc; + doc.SetString("1"); + + std::vector<std::uint8_t> out; + EXPECT_THROW(cryptonote::json::fromJsonValue(doc, out), cryptonote::json::BAD_INPUT); +} + TEST(JsonSerialization, MinerTransaction) { cryptonote::account_base acct; acct.generate(); - const auto miner_tx = make_miner_transaction(acct.get_keys().m_account_address); + const auto miner_tx = test::make_miner_transaction(acct.get_keys().m_account_address); crypto::hash tx_hash{}; ASSERT_TRUE(cryptonote::get_transaction_hash(miner_tx, tx_hash)); @@ -137,8 +155,8 @@ TEST(JsonSerialization, RegularTransaction) cryptonote::account_base acct2; acct2.generate(); - const auto miner_tx = make_miner_transaction(acct1.get_keys().m_account_address); - const auto tx = make_transaction( + const auto miner_tx = test::make_miner_transaction(acct1.get_keys().m_account_address); + const auto tx = test::make_transaction( acct1.get_keys(), {miner_tx}, {acct2.get_keys().m_account_address}, false, false ); @@ -168,8 +186,8 @@ TEST(JsonSerialization, RingctTransaction) cryptonote::account_base acct2; acct2.generate(); - const auto miner_tx = make_miner_transaction(acct1.get_keys().m_account_address); - const auto tx = make_transaction( + const auto miner_tx = test::make_miner_transaction(acct1.get_keys().m_account_address); + const auto tx = test::make_transaction( acct1.get_keys(), {miner_tx}, {acct2.get_keys().m_account_address}, true, false ); @@ -199,8 +217,8 @@ TEST(JsonSerialization, BulletproofTransaction) cryptonote::account_base acct2; acct2.generate(); - const auto miner_tx = make_miner_transaction(acct1.get_keys().m_account_address); - const auto tx = make_transaction( + const auto miner_tx = test::make_miner_transaction(acct1.get_keys().m_account_address); + const auto tx = test::make_transaction( acct1.get_keys(), {miner_tx}, {acct2.get_keys().m_account_address}, true, true ); diff --git a/tests/unit_tests/json_serialization.h b/tests/unit_tests/json_serialization.h new file mode 100644 index 000000000..2d8267261 --- /dev/null +++ b/tests/unit_tests/json_serialization.h @@ -0,0 +1,42 @@ +// Copyright (c) 2020, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +namespace test +{ + cryptonote::transaction make_miner_transaction(cryptonote::account_public_address const& to); + + cryptonote::transaction + make_transaction( + cryptonote::account_keys const& from, + std::vector<cryptonote::transaction> const& sources, + std::vector<cryptonote::account_public_address> const& destinations, + bool rct, + bool bulletproof); +} diff --git a/tests/unit_tests/levin.cpp b/tests/unit_tests/levin.cpp index d2ef920ef..15563e764 100644 --- a/tests/unit_tests/levin.cpp +++ b/tests/unit_tests/levin.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // @@ -680,6 +680,76 @@ TEST_F(levin_notify, local_without_padding) } } +TEST_F(levin_notify, forward_without_padding) +{ + cryptonote::levin::notify notifier = make_notifier(0, true, false); + + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + + { + const auto status = notifier.get_status(); + EXPECT_FALSE(status.has_noise); + EXPECT_FALSE(status.connections_filled); + } + notifier.new_out_connection(); + io_service_.poll(); + + std::vector<cryptonote::blobdata> txs(2); + txs[0].resize(100, 'f'); + txs[1].resize(200, 'e'); + + std::vector<cryptonote::blobdata> sorted_txs = txs; + std::sort(sorted_txs.begin(), sorted_txs.end()); + + ASSERT_EQ(10u, contexts_.size()); + bool has_stemmed = false; + bool has_fluffed = false; + while (!has_stemmed || !has_fluffed) + { + auto context = contexts_.begin(); + EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::forward)); + + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + const bool is_stem = events_.has_stem_txes(); + EXPECT_EQ(txs, events_.take_relayed(is_stem ? cryptonote::relay_method::stem : cryptonote::relay_method::fluff)); + + if (!is_stem) + { + notifier.run_fluff(); + ASSERT_LT(0u, io_service_.poll()); + } + + std::size_t send_count = 0; + EXPECT_EQ(0u, context->process_send_queue()); + for (++context; context != contexts_.end(); ++context) + { + const std::size_t sent = context->process_send_queue(); + if (sent && is_stem) + EXPECT_EQ(1u, (context - contexts_.begin()) % 2); + send_count += sent; + } + + EXPECT_EQ(is_stem ? 1u : 9u, send_count); + ASSERT_EQ(is_stem ? 1u : 9u, receiver_.notified_size()); + for (unsigned count = 0; count < (is_stem ? 1u : 9u); ++count) + { + auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; + if (is_stem) + EXPECT_EQ(txs, notification.txs); + else + EXPECT_EQ(sorted_txs, notification.txs); + EXPECT_TRUE(notification._.empty()); + EXPECT_EQ(!is_stem, notification.dandelionpp_fluff); + } + + has_stemmed |= is_stem; + has_fluffed |= !is_stem; + notifier.run_epoch(); + } +} + TEST_F(levin_notify, block_without_padding) { cryptonote::levin::notify notifier = make_notifier(0, true, false); @@ -918,6 +988,73 @@ TEST_F(levin_notify, local_with_padding) } } +TEST_F(levin_notify, forward_with_padding) +{ + cryptonote::levin::notify notifier = make_notifier(0, true, true); + + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + + { + const auto status = notifier.get_status(); + EXPECT_FALSE(status.has_noise); + EXPECT_FALSE(status.connections_filled); + } + notifier.new_out_connection(); + io_service_.poll(); + + std::vector<cryptonote::blobdata> txs(2); + txs[0].resize(100, 'e'); + txs[1].resize(200, 'f'); + + ASSERT_EQ(10u, contexts_.size()); + bool has_stemmed = false; + bool has_fluffed = false; + while (!has_stemmed || !has_fluffed) + { + auto context = contexts_.begin(); + EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::forward)); + + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + const bool is_stem = events_.has_stem_txes(); + EXPECT_EQ(txs, events_.take_relayed(is_stem ? cryptonote::relay_method::stem : cryptonote::relay_method::fluff)); + + if (!is_stem) + { + notifier.run_fluff(); + ASSERT_LT(0u, io_service_.poll()); + } + + std::size_t send_count = 0; + EXPECT_EQ(0u, context->process_send_queue()); + for (++context; context != contexts_.end(); ++context) + { + const std::size_t sent = context->process_send_queue(); + if (sent && is_stem) + { + EXPECT_EQ(1u, (context - contexts_.begin()) % 2); + EXPECT_FALSE(context->is_incoming()); + } + send_count += sent; + } + + EXPECT_EQ(is_stem ? 1u : 9u, send_count); + ASSERT_EQ(is_stem ? 1u : 9u, receiver_.notified_size()); + for (unsigned count = 0; count < (is_stem ? 1u : 9u); ++count) + { + auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; + EXPECT_EQ(txs, notification.txs); + EXPECT_FALSE(notification._.empty()); + EXPECT_EQ(!is_stem, notification.dandelionpp_fluff); + } + + has_stemmed |= is_stem; + has_fluffed |= !is_stem; + notifier.run_epoch(); + } +} + TEST_F(levin_notify, block_with_padding) { cryptonote::levin::notify notifier = make_notifier(0, true, true); @@ -1021,7 +1158,7 @@ TEST_F(levin_notify, private_fluff_without_padding) auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; EXPECT_EQ(txs, notification.txs); EXPECT_TRUE(notification._.empty()); - EXPECT_FALSE(notification.dandelionpp_fluff); + EXPECT_TRUE(notification.dandelionpp_fluff); } } } @@ -1057,7 +1194,7 @@ TEST_F(levin_notify, private_stem_without_padding) io_service_.reset(); ASSERT_LT(0u, io_service_.poll()); - EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::fluff)); + EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::stem)); EXPECT_EQ(0u, context->process_send_queue()); for (++context; context != contexts_.end(); ++context) @@ -1072,7 +1209,7 @@ TEST_F(levin_notify, private_stem_without_padding) auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; EXPECT_EQ(txs, notification.txs); EXPECT_TRUE(notification._.empty()); - EXPECT_FALSE(notification.dandelionpp_fluff); + EXPECT_TRUE(notification.dandelionpp_fluff); } } } @@ -1123,7 +1260,58 @@ TEST_F(levin_notify, private_local_without_padding) auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; EXPECT_EQ(txs, notification.txs); EXPECT_TRUE(notification._.empty()); - EXPECT_FALSE(notification.dandelionpp_fluff); + EXPECT_TRUE(notification.dandelionpp_fluff); + } + } +} + +TEST_F(levin_notify, private_forward_without_padding) +{ + // private mode always uses fluff but marked as stem + cryptonote::levin::notify notifier = make_notifier(0, false, false); + + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + + { + const auto status = notifier.get_status(); + EXPECT_FALSE(status.has_noise); + EXPECT_FALSE(status.connections_filled); + } + notifier.new_out_connection(); + io_service_.poll(); + + std::vector<cryptonote::blobdata> txs(2); + txs[0].resize(100, 'e'); + txs[1].resize(200, 'f'); + + ASSERT_EQ(10u, contexts_.size()); + { + auto context = contexts_.begin(); + EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::forward)); + + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + notifier.run_fluff(); + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + + EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::forward)); + + EXPECT_EQ(0u, context->process_send_queue()); + for (++context; context != contexts_.end(); ++context) + { + const bool is_incoming = ((context - contexts_.begin()) % 2 == 0); + EXPECT_EQ(is_incoming ? 0u : 1u, context->process_send_queue()); + } + + ASSERT_EQ(5u, receiver_.notified_size()); + for (unsigned count = 0; count < 5; ++count) + { + auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; + EXPECT_EQ(txs, notification.txs); + EXPECT_TRUE(notification._.empty()); + EXPECT_TRUE(notification.dandelionpp_fluff); } } } @@ -1233,7 +1421,7 @@ TEST_F(levin_notify, private_fluff_with_padding) auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; EXPECT_EQ(txs, notification.txs); EXPECT_FALSE(notification._.empty()); - EXPECT_FALSE(notification.dandelionpp_fluff); + EXPECT_TRUE(notification.dandelionpp_fluff); } } } @@ -1268,7 +1456,7 @@ TEST_F(levin_notify, private_stem_with_padding) io_service_.reset(); ASSERT_LT(0u, io_service_.poll()); - EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::fluff)); + EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::stem)); EXPECT_EQ(0u, context->process_send_queue()); for (++context; context != contexts_.end(); ++context) @@ -1283,7 +1471,7 @@ TEST_F(levin_notify, private_stem_with_padding) auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; EXPECT_EQ(txs, notification.txs); EXPECT_FALSE(notification._.empty()); - EXPECT_FALSE(notification.dandelionpp_fluff); + EXPECT_TRUE(notification.dandelionpp_fluff); } } } @@ -1333,7 +1521,57 @@ TEST_F(levin_notify, private_local_with_padding) auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; EXPECT_EQ(txs, notification.txs); EXPECT_FALSE(notification._.empty()); - EXPECT_FALSE(notification.dandelionpp_fluff); + EXPECT_TRUE(notification.dandelionpp_fluff); + } + } +} + +TEST_F(levin_notify, private_forward_with_padding) +{ + cryptonote::levin::notify notifier = make_notifier(0, false, true); + + for (unsigned count = 0; count < 10; ++count) + add_connection(count % 2 == 0); + + { + const auto status = notifier.get_status(); + EXPECT_FALSE(status.has_noise); + EXPECT_FALSE(status.connections_filled); + } + notifier.new_out_connection(); + io_service_.poll(); + + std::vector<cryptonote::blobdata> txs(2); + txs[0].resize(100, 'e'); + txs[1].resize(200, 'f'); + + ASSERT_EQ(10u, contexts_.size()); + { + auto context = contexts_.begin(); + EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::forward)); + + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + notifier.run_fluff(); + io_service_.reset(); + ASSERT_LT(0u, io_service_.poll()); + + EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::forward)); + + EXPECT_EQ(0u, context->process_send_queue()); + for (++context; context != contexts_.end(); ++context) + { + const bool is_incoming = ((context - contexts_.begin()) % 2 == 0); + EXPECT_EQ(is_incoming ? 0u : 1u, context->process_send_queue()); + } + + ASSERT_EQ(5u, receiver_.notified_size()); + for (unsigned count = 0; count < 5; ++count) + { + auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second; + EXPECT_EQ(txs, notification.txs); + EXPECT_FALSE(notification._.empty()); + EXPECT_TRUE(notification.dandelionpp_fluff); } } } diff --git a/tests/unit_tests/logging.cpp b/tests/unit_tests/logging.cpp index c8526abae..b3afba243 100644 --- a/tests/unit_tests/logging.cpp +++ b/tests/unit_tests/logging.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/long_term_block_weight.cpp b/tests/unit_tests/long_term_block_weight.cpp index b7713c63a..f075034bd 100644 --- a/tests/unit_tests/long_term_block_weight.cpp +++ b/tests/unit_tests/long_term_block_weight.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/main.cpp b/tests/unit_tests/main.cpp index 76d17f2ad..ab14fee4e 100644 --- a/tests/unit_tests/main.cpp +++ b/tests/unit_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/memwipe.cpp b/tests/unit_tests/memwipe.cpp index e0f5ada20..4f06b401e 100644 --- a/tests/unit_tests/memwipe.cpp +++ b/tests/unit_tests/memwipe.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/mnemonics.cpp b/tests/unit_tests/mnemonics.cpp index 51feb54e4..0f9a81b46 100644 --- a/tests/unit_tests/mnemonics.cpp +++ b/tests/unit_tests/mnemonics.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -49,9 +49,7 @@ #include "mnemonics/esperanto.h" #include "mnemonics/lojban.h" #include "mnemonics/english_old.h" -#include "mnemonics/language_base.h" #include "mnemonics/singleton.h" -#include <boost/algorithm/string.hpp> namespace { diff --git a/tests/unit_tests/mul_div.cpp b/tests/unit_tests/mul_div.cpp index c1a72ce51..07f48f088 100644 --- a/tests/unit_tests/mul_div.cpp +++ b/tests/unit_tests/mul_div.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/multisig.cpp b/tests/unit_tests/multisig.cpp index c5917200e..3171a64c1 100644 --- a/tests/unit_tests/multisig.cpp +++ b/tests/unit_tests/multisig.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/node_server.cpp b/tests/unit_tests/node_server.cpp index b656c4858..588e91d02 100644 --- a/tests/unit_tests/node_server.cpp +++ b/tests/unit_tests/node_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/output_selection.cpp b/tests/unit_tests/output_selection.cpp index 0724cd3e0..cff509451 100644 --- a/tests/unit_tests/output_selection.cpp +++ b/tests/unit_tests/output_selection.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/parse_amount.cpp b/tests/unit_tests/parse_amount.cpp index f4f57f90f..b581f8dd2 100644 --- a/tests/unit_tests/parse_amount.cpp +++ b/tests/unit_tests/parse_amount.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index 64fcbba4c..807bab64a 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/rolling_median.cpp b/tests/unit_tests/rolling_median.cpp index 9e4cf87b8..730f1e7c5 100644 --- a/tests/unit_tests/rolling_median.cpp +++ b/tests/unit_tests/rolling_median.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/rpc_version_str.cpp b/tests/unit_tests/rpc_version_str.cpp index 5dce60465..249d297cf 100644 --- a/tests/unit_tests/rpc_version_str.cpp +++ b/tests/unit_tests/rpc_version_str.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019, The Monero Project +// Copyright (c) 2019-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index b711526e6..b460559ff 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // @@ -616,6 +616,46 @@ TEST(Serialization, serializes_ringct_types) ASSERT_EQ(bp0, bp1); } +TEST(Serialization, key_encryption_transition) +{ + const cryptonote::network_type nettype = cryptonote::TESTNET; + tools::wallet2 w(nettype); + const boost::filesystem::path wallet_file = unit_test::data_dir / "wallet_9svHk1"; + const boost::filesystem::path key_file = unit_test::data_dir / "wallet_9svHk1.keys"; + const boost::filesystem::path temp_wallet_file = unit_test::data_dir / "wallet_9svHk1_temp"; + const boost::filesystem::path temp_key_file = unit_test::data_dir / "wallet_9svHk1_temp.keys"; + string password = "test"; + bool r = false; + + // Copy the original files for this test + boost::filesystem::copy(wallet_file,temp_wallet_file); + boost::filesystem::copy(key_file,temp_key_file); + + try + { + // Key transition + w.load(temp_wallet_file.string(), password); // legacy decryption method + ASSERT_TRUE(w.get_load_info().is_legacy_key_encryption); + const crypto::secret_key view_secret_key = w.get_account().get_keys().m_view_secret_key; + + w.rewrite(temp_wallet_file.string(), password); // transition to new key format + + w.load(temp_wallet_file.string(), password); // new decryption method + ASSERT_FALSE(w.get_load_info().is_legacy_key_encryption); + ASSERT_EQ(w.get_account().get_keys().m_view_secret_key,view_secret_key); + + r = true; + } + catch (const exception& e) + {} + + // Remove the temporary files + boost::filesystem::remove(temp_wallet_file); + boost::filesystem::remove(temp_key_file); + + ASSERT_TRUE(r); +} + TEST(Serialization, portability_wallet) { const cryptonote::network_type nettype = cryptonote::TESTNET; diff --git a/tests/unit_tests/sha256.cpp b/tests/unit_tests/sha256.cpp index 898c9e4b3..0232511d4 100644 --- a/tests/unit_tests/sha256.cpp +++ b/tests/unit_tests/sha256.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/slow_memmem.cpp b/tests/unit_tests/slow_memmem.cpp index 4f13e00e6..602fdae38 100644 --- a/tests/unit_tests/slow_memmem.cpp +++ b/tests/unit_tests/slow_memmem.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/subaddress.cpp b/tests/unit_tests/subaddress.cpp index 385a2a8ab..bf24022e8 100644 --- a/tests/unit_tests/subaddress.cpp +++ b/tests/unit_tests/subaddress.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/test_peerlist.cpp b/tests/unit_tests/test_peerlist.cpp index dbb3aaf96..78404f9f9 100644 --- a/tests/unit_tests/test_peerlist.cpp +++ b/tests/unit_tests/test_peerlist.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/test_protocol_pack.cpp b/tests/unit_tests/test_protocol_pack.cpp index 59e46e332..312f18dfe 100644 --- a/tests/unit_tests/test_protocol_pack.cpp +++ b/tests/unit_tests/test_protocol_pack.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/test_tx_utils.cpp b/tests/unit_tests/test_tx_utils.cpp index deb45594d..9425e2432 100644 --- a/tests/unit_tests/test_tx_utils.cpp +++ b/tests/unit_tests/test_tx_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/tx_proof.cpp b/tests/unit_tests/tx_proof.cpp new file mode 100644 index 000000000..c5d06bc68 --- /dev/null +++ b/tests/unit_tests/tx_proof.cpp @@ -0,0 +1,130 @@ +// 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 "gtest/gtest.h" + +#include "crypto/crypto.h" +extern "C" { +#include "crypto/crypto-ops.h" +} +#include "crypto/hash.h" +#include <boost/algorithm/string.hpp> + +static inline unsigned char *operator &(crypto::ec_point &point) { + return &reinterpret_cast<unsigned char &>(point); + } + +static inline unsigned char *operator &(crypto::ec_scalar &scalar) { + return &reinterpret_cast<unsigned char &>(scalar); + } + +TEST(tx_proof, prove_verify_v2) +{ + crypto::secret_key r; + crypto::random32_unbiased(&r); + + // A = aG + // B = bG + crypto::secret_key a,b; + crypto::public_key A,B; + crypto::generate_keys(A, a, a, false); + crypto::generate_keys(B, b, b, false); + + // R_B = rB + crypto::public_key R_B; + ge_p3 B_p3; + ge_frombytes_vartime(&B_p3,&B); + ge_p2 R_B_p2; + ge_scalarmult(&R_B_p2, &unwrap(r), &B_p3); + ge_tobytes(&R_B, &R_B_p2); + + // R_G = rG + crypto::public_key R_G; + ge_frombytes_vartime(&B_p3,&B); + ge_p3 R_G_p3; + ge_scalarmult_base(&R_G_p3, &unwrap(r)); + ge_p3_tobytes(&R_G, &R_G_p3); + + // D = rA + crypto::public_key D; + ge_p3 A_p3; + ge_frombytes_vartime(&A_p3,&A); + ge_p2 D_p2; + ge_scalarmult(&D_p2, &unwrap(r), &A_p3); + ge_tobytes(&D, &D_p2); + + crypto::signature sig; + + // Message data + crypto::hash prefix_hash; + char data[] = "hash input"; + crypto::cn_fast_hash(data,sizeof(data)-1,prefix_hash); + + // Generate/verify valid v1 proof with standard address + crypto::generate_tx_proof_v1(prefix_hash, R_G, A, boost::none, D, r, sig); + ASSERT_TRUE(crypto::check_tx_proof(prefix_hash, R_G, A, boost::none, D, sig, 1)); + + // Generate/verify valid v1 proof with subaddress + crypto::generate_tx_proof_v1(prefix_hash, R_B, A, B, D, r, sig); + ASSERT_TRUE(crypto::check_tx_proof(prefix_hash, R_B, A, B, D, sig, 1)); + + // Generate/verify valid v2 proof with standard address + crypto::generate_tx_proof(prefix_hash, R_G, A, boost::none, D, r, sig); + ASSERT_TRUE(crypto::check_tx_proof(prefix_hash, R_G, A, boost::none, D, sig, 2)); + + // Generate/verify valid v2 proof with subaddress + crypto::generate_tx_proof(prefix_hash, R_B, A, B, D, r, sig); + ASSERT_TRUE(crypto::check_tx_proof(prefix_hash, R_B, A, B, D, sig, 2)); + + // Try to verify valid v2 proofs as v1 proof (bad) + crypto::generate_tx_proof(prefix_hash, R_G, A, boost::none, D, r, sig); + ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, R_G, A, boost::none, D, sig, 1)); + crypto::generate_tx_proof(prefix_hash, R_B, A, B, D, r, sig); + ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, R_B, A, B, D, sig, 1)); + + // Randomly-distributed test points + crypto::secret_key evil_a, evil_b, evil_d, evil_r; + crypto::public_key evil_A, evil_B, evil_D, evil_R; + crypto::generate_keys(evil_A, evil_a, evil_a, false); + crypto::generate_keys(evil_B, evil_b, evil_b, false); + crypto::generate_keys(evil_D, evil_d, evil_d, false); + crypto::generate_keys(evil_R, evil_r, evil_r, false); + + // Selectively choose bad point in v2 proof (bad) + crypto::generate_tx_proof(prefix_hash, R_B, A, B, D, r, sig); + ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, evil_R, A, B, D, sig, 2)); + ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, R_B, evil_A, B, D, sig, 2)); + ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, R_B, A, evil_B, D, sig, 2)); + ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, R_B, A, B, evil_D, sig, 2)); + + // Try to verify valid v1 proofs as v2 proof (bad) + crypto::generate_tx_proof_v1(prefix_hash, R_G, A, boost::none, D, r, sig); + ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, R_G, A, boost::none, D, sig, 2)); + crypto::generate_tx_proof_v1(prefix_hash, R_B, A, B, D, r, sig); + ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, R_B, A, B, D, sig, 2)); +} diff --git a/tests/unit_tests/unbound.cpp b/tests/unit_tests/unbound.cpp index d122b2ad2..b404ca922 100644 --- a/tests/unit_tests/unbound.cpp +++ b/tests/unit_tests/unbound.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/unit_tests_utils.h b/tests/unit_tests/unit_tests_utils.h index 5944bd55a..1784ffb21 100644 --- a/tests/unit_tests/unit_tests_utils.h +++ b/tests/unit_tests/unit_tests_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/uri.cpp b/tests/unit_tests/uri.cpp index 04d935751..83312a3c9 100644 --- a/tests/unit_tests/uri.cpp +++ b/tests/unit_tests/uri.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2019, The Monero Project +// Copyright (c) 2016-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/varint.cpp b/tests/unit_tests/varint.cpp index 72691d722..ca5af5ad2 100644 --- a/tests/unit_tests/varint.cpp +++ b/tests/unit_tests/varint.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019, The Monero Project +// Copyright (c) 2014-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/vercmp.cpp b/tests/unit_tests/vercmp.cpp index 77399fa89..90f4fdf79 100644 --- a/tests/unit_tests/vercmp.cpp +++ b/tests/unit_tests/vercmp.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/zmq_rpc.cpp b/tests/unit_tests/zmq_rpc.cpp index af1f1608b..59759bed8 100644 --- a/tests/unit_tests/zmq_rpc.cpp +++ b/tests/unit_tests/zmq_rpc.cpp @@ -26,11 +26,25 @@ // 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 <boost/preprocessor/stringize.hpp> #include <gtest/gtest.h> +#include <rapidjson/document.h> +#include "cryptonote_basic/account.h" +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/events.h" +#include "cryptonote_basic/cryptonote_format_utils.h" +#include "json_serialization.h" +#include "net/zmq.h" #include "rpc/message.h" +#include "rpc/zmq_pub.h" +#include "rpc/zmq_server.h" #include "serialization/json_object.h" +#define MASSERT(...) \ + if (!(__VA_ARGS__)) \ + return testing::AssertionFailure() << BOOST_PP_STRINGIZE(__VA_ARGS__) + TEST(ZmqFullMessage, InvalidRequest) { EXPECT_THROW( @@ -53,3 +67,711 @@ TEST(ZmqFullMessage, Request) cryptonote::rpc::FullMessage parsed{request, true}; EXPECT_STREQ("foo", parsed.getRequestType().c_str()); } + +namespace +{ + using published_json = std::pair<std::string, rapidjson::Document>; + + constexpr const char inproc_pub[] = "inproc://dummy_pub"; + + net::zmq::socket create_socket(void* ctx, const char* address) + { + net::zmq::socket sock{zmq_socket(ctx, ZMQ_PAIR)}; + if (!sock) + MONERO_ZMQ_THROW("failed to create socket"); + if (zmq_bind(sock.get(), address) != 0) + MONERO_ZMQ_THROW("socket bind failure"); + return sock; + } + + std::vector<std::string> get_messages(void* socket, int count = -1) + { + std::vector<std::string> out; + for ( ; count || count < 0; --count) + { + expect<std::string> next = net::zmq::receive(socket, (count < 0 ? ZMQ_DONTWAIT : 0)); + if (next == net::zmq::make_error_code(EAGAIN)) + return out; + out.push_back(std::move(*next)); + } + return out; + } + + std::vector<published_json> get_published(void* socket, int count = -1) + { + std::vector<published_json> out; + + const auto messages = get_messages(socket, count); + out.reserve(messages.size()); + + for (const std::string& message : messages) + { + const char* split = std::strchr(message.c_str(), ':'); + if (!split) + throw std::runtime_error{"Invalid ZMQ/Pub message"}; + + out.emplace_back(); + out.back().first = {message.c_str(), split}; + if (out.back().second.Parse(split + 1).HasParseError()) + throw std::runtime_error{"Failed to parse ZMQ/Pub message"}; + } + + return out; + } + + testing::AssertionResult compare_full_txpool(epee::span<const cryptonote::txpool_event> events, const published_json& pub) + { + MASSERT(pub.first == "json-full-txpool_add"); + MASSERT(pub.second.IsArray()); + MASSERT(pub.second.Size() <= events.size()); + + std::size_t i = 0; + for (const cryptonote::txpool_event& event : events) + { + MASSERT(i <= pub.second.Size()); + if (!event.res) + continue; + + cryptonote::transaction tx{}; + cryptonote::json::fromJsonValue(pub.second[i], tx); + + crypto::hash id{}; + MASSERT(cryptonote::get_transaction_hash(event.tx, id)); + MASSERT(cryptonote::get_transaction_hash(tx, id)); + MASSERT(event.tx.hash == tx.hash); + ++i; + } + return testing::AssertionSuccess(); + } + + testing::AssertionResult compare_minimal_txpool(epee::span<const cryptonote::txpool_event> events, const published_json& pub) + { + MASSERT(pub.first == "json-minimal-txpool_add"); + MASSERT(pub.second.IsArray()); + MASSERT(pub.second.Size() <= events.size()); + + std::size_t i = 0; + for (const cryptonote::txpool_event& event : events) + { + MASSERT(i <= pub.second.Size()); + if (!event.res) + continue; + + std::size_t actual_size = 0; + crypto::hash actual_id{}; + + MASSERT(pub.second[i].IsObject()); + GET_FROM_JSON_OBJECT(pub.second[i], actual_id, id); + GET_FROM_JSON_OBJECT(pub.second[i], actual_size, blob_size); + + std::size_t expected_size = 0; + crypto::hash expected_id{}; + MASSERT(cryptonote::get_transaction_hash(event.tx, expected_id, expected_size)); + MASSERT(expected_size == actual_size); + MASSERT(expected_id == actual_id); + ++i; + } + return testing::AssertionSuccess(); + } + + testing::AssertionResult compare_full_block(const epee::span<const cryptonote::block> expected, const published_json& pub) + { + MASSERT(pub.first == "json-full-chain_main"); + MASSERT(pub.second.IsArray()); + + std::vector<cryptonote::block> actual; + cryptonote::json::fromJsonValue(pub.second, actual); + + MASSERT(expected.size() == actual.size()); + + for (std::size_t i = 0; i < expected.size(); ++i) + { + crypto::hash id; + MASSERT(cryptonote::get_block_hash(expected[i], id)); + MASSERT(cryptonote::get_block_hash(actual[i], id)); + MASSERT(expected[i].hash == actual[i].hash); + } + + return testing::AssertionSuccess(); + } + + testing::AssertionResult compare_minimal_block(std::size_t height, const epee::span<const cryptonote::block> expected, const published_json& pub) + { + MASSERT(pub.first == "json-minimal-chain_main"); + MASSERT(pub.second.IsObject()); + MASSERT(!expected.empty()); + + std::size_t actual_height = 0; + crypto::hash actual_id{}; + crypto::hash actual_prev_id{}; + std::vector<crypto::hash> actual_ids{}; + GET_FROM_JSON_OBJECT(pub.second, actual_height, first_height); + GET_FROM_JSON_OBJECT(pub.second, actual_prev_id, first_prev_id); + GET_FROM_JSON_OBJECT(pub.second, actual_ids, ids); + + MASSERT(height == actual_height); + MASSERT(expected[0].prev_id == actual_prev_id); + MASSERT(expected.size() == actual_ids.size()); + + for (std::size_t i = 0; i < expected.size(); ++i) + { + crypto::hash id; + MASSERT(cryptonote::get_block_hash(expected[i], id)); + MASSERT(id == actual_ids[i]); + } + + return testing::AssertionSuccess(); + } + + struct zmq_base : public testing::Test + { + cryptonote::account_base acct; + + zmq_base() + : testing::Test(), acct() + { + acct.generate(); + } + + cryptonote::transaction make_miner_transaction() + { + return test::make_miner_transaction(acct.get_keys().m_account_address); + } + + cryptonote::transaction make_transaction(const std::vector<cryptonote::account_public_address>& destinations) + { + return test::make_transaction(acct.get_keys(), {make_miner_transaction()}, destinations, true, true); + } + + cryptonote::transaction make_transaction() + { + cryptonote::account_base temp_account; + temp_account.generate(); + return make_transaction({temp_account.get_keys().m_account_address}); + } + + cryptonote::block make_block() + { + cryptonote::block block{}; + block.major_version = 1; + block.minor_version = 3; + block.timestamp = 100; + block.prev_id = crypto::rand<crypto::hash>(); + block.nonce = 100; + block.miner_tx = make_miner_transaction(); + return block; + } + }; + + struct zmq_pub : public zmq_base + { + net::zmq::context ctx; + net::zmq::socket relay; + net::zmq::socket dummy_pub; + net::zmq::socket dummy_client; + std::shared_ptr<cryptonote::listener::zmq_pub> pub; + + zmq_pub() + : zmq_base(), + ctx(zmq_init(1)), + relay(create_socket(ctx.get(), cryptonote::listener::zmq_pub::relay_endpoint())), + dummy_pub(create_socket(ctx.get(), inproc_pub)), + dummy_client(zmq_socket(ctx.get(), ZMQ_PAIR)), + pub(std::make_shared<cryptonote::listener::zmq_pub>(ctx.get())) + { + if (!dummy_client) + MONERO_ZMQ_THROW("failed to create socket"); + if (zmq_connect(dummy_client.get(), inproc_pub) != 0) + MONERO_ZMQ_THROW("failed to connect to dummy pub"); + } + + virtual void TearDown() override final + { + EXPECT_EQ(0u, get_messages(relay.get()).size()); + EXPECT_EQ(0u, get_messages(dummy_client.get()).size()); + } + + template<std::size_t N> + bool sub_request(const char (&topic)[N]) + { + return pub->sub_request({topic, N - 1}); + } + }; + + struct dummy_handler final : cryptonote::rpc::RpcHandler + { + dummy_handler() + : cryptonote::rpc::RpcHandler() + {} + + virtual epee::byte_slice handle(std::string&& request) override final + { + throw std::logic_error{"not implemented"}; + } + }; + + struct zmq_server : public zmq_base + { + dummy_handler handler; + cryptonote::rpc::ZmqServer server; + std::shared_ptr<cryptonote::listener::zmq_pub> pub; + net::zmq::socket sub; + + zmq_server() + : zmq_base(), + handler(), + server(handler), + pub(), + sub() + { + void* ctx = server.init_rpc({}, {}); + if (!ctx) + throw std::runtime_error{"init_rpc failure"}; + + const std::string endpoint = inproc_pub; + pub = server.init_pub({std::addressof(endpoint), 1}); + if (!pub) + throw std::runtime_error{"failed to initiaze zmq/pub"}; + + sub.reset(zmq_socket(ctx, ZMQ_SUB)); + if (!sub) + MONERO_ZMQ_THROW("failed to create socket"); + if (zmq_connect(sub.get(), inproc_pub) != 0) + MONERO_ZMQ_THROW("failed to connect to dummy pub"); + + server.run(); + } + + virtual void TearDown() override final + { + EXPECT_EQ(0u, get_messages(sub.get()).size()); + sub.reset(); + pub.reset(); + server.stop(); + } + + template<std::size_t N> + void subscribe(const char (&topic)[N]) + { + if (zmq_setsockopt(sub.get(), ZMQ_SUBSCRIBE, topic, N - 1) != 0) + MONERO_ZMQ_THROW("failed to subscribe"); + } + }; +} + +TEST_F(zmq_pub, InvalidContext) +{ + EXPECT_THROW(cryptonote::listener::zmq_pub{nullptr}, std::logic_error); +} + +TEST_F(zmq_pub, NoBlocking) +{ + EXPECT_FALSE(pub->relay_to_pub(relay.get(), dummy_pub.get())); +} + +TEST_F(zmq_pub, DefaultDrop) +{ + EXPECT_EQ(0u, pub->send_txpool_add({{make_transaction(), {}, true}})); + + const cryptonote::block bl = make_block(); + EXPECT_EQ(0u,pub->send_chain_main(5, {std::addressof(bl), 1})); + EXPECT_NO_THROW(cryptonote::listener::zmq_pub::chain_main{pub}(5, {std::addressof(bl), 1})); +} + +TEST_F(zmq_pub, JsonFullTxpool) +{ + static constexpr const char topic[] = "\1json-full-txpool_add"; + + ASSERT_TRUE(sub_request(topic)); + + std::vector<cryptonote::txpool_event> events + { + {make_transaction(), {}, true}, {make_transaction(), {}, true} + }; + + EXPECT_NO_THROW(pub->send_txpool_add(events)); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + auto pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); + + EXPECT_NO_THROW(cryptonote::listener::zmq_pub::txpool_add{pub}(events)); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); + + events.at(0).res = false; + EXPECT_EQ(1u, pub->send_txpool_add(events)); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); + + events.at(0).res = false; + EXPECT_NO_THROW(cryptonote::listener::zmq_pub::txpool_add{pub}(events)); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); +} + +TEST_F(zmq_pub, JsonMinimalTxpool) +{ + static constexpr const char topic[] = "\1json-minimal-txpool_add"; + + ASSERT_TRUE(sub_request(topic)); + + std::vector<cryptonote::txpool_event> events + { + {make_transaction(), {}, true}, {make_transaction(), {}, true} + }; + + EXPECT_NO_THROW(pub->send_txpool_add(events)); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + auto pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.front())); + + EXPECT_NO_THROW(cryptonote::listener::zmq_pub::txpool_add{pub}(events)); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.front())); + + events.at(0).res = false; + EXPECT_EQ(1u, pub->send_txpool_add(events)); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.front())); + + events.at(0).res = false; + EXPECT_NO_THROW(cryptonote::listener::zmq_pub::txpool_add{pub}(events)); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.front())); +} + +TEST_F(zmq_pub, JsonFullChain) +{ + static constexpr const char topic[] = "\1json-full-chain_main"; + + ASSERT_TRUE(sub_request(topic)); + + const std::array<cryptonote::block, 2> blocks{{make_block(), make_block()}}; + + EXPECT_EQ(1u, pub->send_chain_main(100, epee::to_span(blocks))); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + auto pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_full_block(epee::to_span(blocks), pubs.front())); + + EXPECT_NO_THROW(cryptonote::listener::zmq_pub::chain_main{pub}(533, epee::to_span(blocks))); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_full_block(epee::to_span(blocks), pubs.front())); +} + +TEST_F(zmq_pub, JsonMinimalChain) +{ + static constexpr const char topic[] = "\1json-minimal-chain_main"; + + ASSERT_TRUE(sub_request(topic)); + + const std::array<cryptonote::block, 2> blocks{{make_block(), make_block()}}; + + EXPECT_EQ(1u, pub->send_chain_main(100, epee::to_span(blocks))); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + auto pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_minimal_block(100, epee::to_span(blocks), pubs.front())); + + EXPECT_NO_THROW(cryptonote::listener::zmq_pub::chain_main{pub}(533, epee::to_span(blocks))); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_minimal_block(533, epee::to_span(blocks), pubs.front())); +} + +TEST_F(zmq_pub, JsonFullAll) +{ + static constexpr const char topic[] = "\1json-full"; + + ASSERT_TRUE(sub_request(topic)); + { + std::vector<cryptonote::txpool_event> events + { + {make_transaction(), {}, true}, {make_transaction(), {}, true} + }; + + EXPECT_EQ(1u, pub->send_txpool_add(events)); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + auto pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); + + EXPECT_NO_THROW(cryptonote::listener::zmq_pub::txpool_add{pub}(events)); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); + + events.at(0).res = false; + EXPECT_NO_THROW(pub->send_txpool_add(events)); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); + + events.at(0).res = false; + EXPECT_NO_THROW(cryptonote::listener::zmq_pub::txpool_add{pub}(events)); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); + } + { + const std::array<cryptonote::block, 2> blocks{{make_block(), make_block()}}; + + EXPECT_EQ(1u, pub->send_chain_main(100, epee::to_span(blocks))); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + auto pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_full_block(epee::to_span(blocks), pubs.front())); + + EXPECT_NO_THROW(cryptonote::listener::zmq_pub::chain_main{pub}(533, epee::to_span(blocks))); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_full_block(epee::to_span(blocks), pubs.front())); + } +} + +TEST_F(zmq_pub, JsonMinimalAll) +{ + static constexpr const char topic[] = "\1json-minimal"; + + ASSERT_TRUE(sub_request(topic)); + + { + std::vector<cryptonote::txpool_event> events + { + {make_transaction(), {}, true}, {make_transaction(), {}, true} + }; + + EXPECT_EQ(1u, pub->send_txpool_add(events)); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + auto pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.front())); + + EXPECT_NO_THROW(cryptonote::listener::zmq_pub::txpool_add{pub}(events)); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.front())); + + events.at(0).res = false; + EXPECT_NO_THROW(pub->send_txpool_add(events)); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.front())); + + events.at(0).res = false; + EXPECT_NO_THROW(cryptonote::listener::zmq_pub::txpool_add{pub}(events)); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.front())); + } + { + const std::array<cryptonote::block, 2> blocks{{make_block(), make_block()}}; + + EXPECT_EQ(1u, pub->send_chain_main(100, epee::to_span(blocks))); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + auto pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_minimal_block(100, epee::to_span(blocks), pubs.front())); + + EXPECT_NO_THROW(cryptonote::listener::zmq_pub::chain_main{pub}(533, epee::to_span(blocks))); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + pubs = get_published(dummy_client.get()); + EXPECT_EQ(1u, pubs.size()); + ASSERT_LE(1u, pubs.size()); + EXPECT_TRUE(compare_minimal_block(533, epee::to_span(blocks), pubs.front())); + } +} + +TEST_F(zmq_pub, JsonAll) +{ + static constexpr const char topic[] = "\1json"; + + ASSERT_TRUE(sub_request(topic)); + + { + std::vector<cryptonote::txpool_event> events + { + {make_transaction(), {}, true}, {make_transaction(), {}, true} + }; + + EXPECT_EQ(1u, pub->send_txpool_add(events)); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + auto pubs = get_published(dummy_client.get()); + EXPECT_EQ(2u, pubs.size()); + ASSERT_LE(2u, pubs.size()); + EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); + EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.back())); + + EXPECT_NO_THROW(cryptonote::listener::zmq_pub::txpool_add{pub}(events)); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + pubs = get_published(dummy_client.get()); + EXPECT_EQ(2u, pubs.size()); + ASSERT_LE(2u, pubs.size()); + EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); + EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.back())); + + events.at(0).res = false; + EXPECT_EQ(1u, pub->send_txpool_add(events)); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + pubs = get_published(dummy_client.get()); + EXPECT_EQ(2u, pubs.size()); + ASSERT_LE(2u, pubs.size()); + EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); + EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.back())); + + events.at(0).res = false; + EXPECT_NO_THROW(cryptonote::listener::zmq_pub::txpool_add{pub}(events)); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + pubs = get_published(dummy_client.get()); + EXPECT_EQ(2u, pubs.size()); + ASSERT_LE(2u, pubs.size()); + EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); + EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.back())); + } + { + const std::array<cryptonote::block, 1> blocks{{make_block()}}; + + EXPECT_EQ(2u, pub->send_chain_main(100, epee::to_span(blocks))); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + auto pubs = get_published(dummy_client.get()); + EXPECT_EQ(2u, pubs.size()); + ASSERT_LE(2u, pubs.size()); + EXPECT_TRUE(compare_full_block(epee::to_span(blocks), pubs.front())); + EXPECT_TRUE(compare_minimal_block(100, epee::to_span(blocks), pubs.back())); + + EXPECT_NO_THROW(cryptonote::listener::zmq_pub::chain_main{pub}(533, epee::to_span(blocks))); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); + + pubs = get_published(dummy_client.get()); + EXPECT_EQ(2u, pubs.size()); + ASSERT_LE(2u, pubs.size()); + EXPECT_TRUE(compare_full_block(epee::to_span(blocks), pubs.front())); + EXPECT_TRUE(compare_minimal_block(533, epee::to_span(blocks), pubs.back())); + } +} + +TEST_F(zmq_pub, JsonChainWeakPtrSkip) +{ + static constexpr const char topic[] = "\1json"; + + ASSERT_TRUE(sub_request(topic)); + + const std::array<cryptonote::block, 1> blocks{{make_block()}}; + + pub.reset(); + EXPECT_NO_THROW(cryptonote::listener::zmq_pub::chain_main{pub}(533, epee::to_span(blocks))); +} + +TEST_F(zmq_pub, JsonTxpoolWeakPtrSkip) +{ + static constexpr const char topic[] = "\1json"; + + ASSERT_TRUE(sub_request(topic)); + + std::vector<cryptonote::txpool_event> events + { + {make_transaction(), {}, true}, {make_transaction(), {}, true} + }; + + pub.reset(); + EXPECT_NO_THROW(cryptonote::listener::zmq_pub::txpool_add{pub}(std::move(events))); +} + +TEST_F(zmq_server, pub) +{ + subscribe("json-minimal"); + + std::vector<cryptonote::txpool_event> events + { + {make_transaction(), {}, true}, {make_transaction(), {}, true} + }; + + const std::array<cryptonote::block, 1> blocks{{make_block()}}; + + ASSERT_EQ(1u, pub->send_txpool_add(events)); + ASSERT_EQ(1u, pub->send_chain_main(200, epee::to_span(blocks))); + + auto pubs = get_published(sub.get(), 2); + EXPECT_EQ(2u, pubs.size()); + ASSERT_LE(2u, pubs.size()); + EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.front())); + EXPECT_TRUE(compare_minimal_block(200, epee::to_span(blocks), pubs.back())); +} diff --git a/translations/CMakeLists.txt b/translations/CMakeLists.txt index 589005a42..0081746f2 100644 --- a/translations/CMakeLists.txt +++ b/translations/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019, The Monero Project +# Copyright (c) 2017-2020, The Monero Project # # All rights reserved. # diff --git a/translations/generate_translations_header.c b/translations/generate_translations_header.c index 85e9b9b27..42248a184 100644 --- a/translations/generate_translations_header.c +++ b/translations/generate_translations_header.c @@ -1,5 +1,5 @@ // Copyright (c) 2013, Sergey Lyubka -// Copyright (c) 2017-2019, The Monero Project +// Copyright (c) 2017-2020, The Monero Project // All rights reserved. // Released under the MIT license. diff --git a/utils/build_scripts/windows.bat b/utils/build_scripts/windows.bat index 76d4f86bb..a90f27c0c 100644 --- a/utils/build_scripts/windows.bat +++ b/utils/build_scripts/windows.bat @@ -1,4 +1,4 @@ -:: Copyright (c) 2014-2019, The Monero Project +:: Copyright (c) 2014-2020, The Monero Project :: :: All rights reserved. :: diff --git a/utils/gpg_keys/rbrunner7.asc b/utils/gpg_keys/rbrunner7.asc new file mode 100644 index 000000000..5a3713e42 --- /dev/null +++ b/utils/gpg_keys/rbrunner7.asc @@ -0,0 +1,31 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBF6po6kBCADSKkQYShjqwMWKLv5EtBr92+3cbDBsqInmBUKbpznRmPaFVrE6 +h7rK1GrQJslddHM8amB75UPQv4kImU4EsHreoOwA0teKtKhYs53LbgZLg4D6A6Sh +AxT4/e39lp+/Bjoi1slZTNwG7LVe0NQs0y9v2FgbZwTvxKX59rpDkQzQbCoP8Jxa +E0eh1Yp3c7YHXnx6NUygS+tle9veehPQHDi6dPRfFp+Usk9Ojpws5PoBNdBsoNXH +dRGRtKTgyD/FajwLatrJnGnL8NVzM0rMT05n1qsdZIWD/wKwgqmZCPbIJDZaC/QO +AepIKWkPrWtJl6WVseDDTU7vMM0cHq+i0EmTABEBAAG0JlJlbsOpIEJydW5uZXIg +PHJicnVubmVyQGRyZWFtc2hhcmUuY2g+iQFUBBMBCAA+FiEEn4wKfpzYWyxSK58w +aiUdVx9/e8MFAl6po6kCGyMFCQPCE3cFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AA +CgkQaiUdVx9/e8NVBAgAgPqN5VCpNy7Mnlu74LYbWX+uFihp5O2x5QaGP79KKXHj ++iiyKZLjk7fA1/XGg3zUlNOq5IGuQwFMWJICaEr6Ph7CoH+Zn9J5GG8QBACP7Rgy +04+NqzgUlL1tGkLOsqUF0Vfhe1ux8g71OyDFDeo3YD1Jru/piwXoaHDxOZAbhwYj +WXUuzghlS2GslF1I9BRoMQcQDFcLSX+l2sVCVtlkeIuqNHSFWhruOvBBEj9RwM8j +Dm8h5shH62EEjr+KGuOiZJX/M9FRNk+qFMhfWWmXDmKc9CFV/ElAyXCSenEAzcV6 +ayXBjB1spHXRee7bOmLd/ku6qGbwpGp5aTBbNOPMV7kBDQReqaOpAQgA0PaRFp1s +usThDLPc5/Xpy6H4m21LOyoF9VqLNQdVSIqqx8b5FqPjyTw3N957XXBvzAHdO0ig +831tZb/JYU13jnUMnbtNHMwel+SNRWuqn+YU9/shN1B8Js///IwIFV2KCQKuQTRn +ea2OPIlffdJYXPf0/BFWxr80Hb1EWkC6bpc1gsqrcGPzenVEV0PPdiAzsGWOzJkJ +e69qgGLW2ulP7PUb8m4z7jXChzYQ1ki5t2BM7Ta30qWJ6qyvccOJxSAYDVHeT44q +C83Vyeum/1n6isTlH1J6G760mX5/S3vIRfyE3eXjfOpluUf1kWScFuokUSFm+n7k +BsHmzjjk9JuNlQARAQABiQE8BBgBCAAmFiEEn4wKfpzYWyxSK58waiUdVx9/e8MF +Al6po6kCGwwFCQPCE3cACgkQaiUdVx9/e8NjCAgAjkMYw55h3fJj/wmyiF+G53uR +kh6Z/07HKdFyhe+1698F49V68xWBMym56O5vv6iUBH4xBeyPTqC/nZ6v/L5m3z2F +7f69hxaH7mp4BiLugcr2mkx8N6qCJBwSsVnql1/E5TvHWVjp00ikB5oYTRj7zcmo +tLV9q2nBFjk5iWuoH5GtJ3PvIYZilIlqEqIF9trCD89sXZnATEI9Z8N+jFWAgYxQ +f63xVHeW7vTA3ACyA7Qmf8XPjgDMlW4ANrgyGzmcZxGIpjNAlezBp/Bojw22Srrh +uCRqoKg4lANxBJw9S6SO06QuKGJ7utIQ3YkqQFfzz47Q3sEvhLKvbSiBowazvw== +=sdcD +-----END PGP PUBLIC KEY BLOCK----- + diff --git a/utils/munin_plugins/alt_blocks_count b/utils/munin_plugins/alt_blocks_count index 2510b7bc3..372f11088 100644 --- a/utils/munin_plugins/alt_blocks_count +++ b/utils/munin_plugins/alt_blocks_count @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/difficulty b/utils/munin_plugins/difficulty index a7d90c15f..02a307a02 100644 --- a/utils/munin_plugins/difficulty +++ b/utils/munin_plugins/difficulty @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/grey_peerlist_size b/utils/munin_plugins/grey_peerlist_size index b1cf66f14..3dad08150 100644 --- a/utils/munin_plugins/grey_peerlist_size +++ b/utils/munin_plugins/grey_peerlist_size @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/height b/utils/munin_plugins/height index 20b3e65ef..29c857817 100644 --- a/utils/munin_plugins/height +++ b/utils/munin_plugins/height @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/incoming_connections_count b/utils/munin_plugins/incoming_connections_count index 1dcc1f4d0..9c92749a3 100644 --- a/utils/munin_plugins/incoming_connections_count +++ b/utils/munin_plugins/incoming_connections_count @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/outgoing_connections_count b/utils/munin_plugins/outgoing_connections_count index 45a0500dd..277c02a15 100644 --- a/utils/munin_plugins/outgoing_connections_count +++ b/utils/munin_plugins/outgoing_connections_count @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/tx_count b/utils/munin_plugins/tx_count index e81e5f321..b61fe3b3d 100644 --- a/utils/munin_plugins/tx_count +++ b/utils/munin_plugins/tx_count @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/tx_pool_size b/utils/munin_plugins/tx_pool_size index 245733578..b8f81fbf6 100644 --- a/utils/munin_plugins/tx_pool_size +++ b/utils/munin_plugins/tx_pool_size @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/white_peerlist_size b/utils/munin_plugins/white_peerlist_size index 8d87a9f0a..4cafb0fbc 100644 --- a/utils/munin_plugins/white_peerlist_size +++ b/utils/munin_plugins/white_peerlist_size @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2019, The Monero Project +# Copyright (c) 2014-2020, The Monero Project # # All rights reserved. # diff --git a/utils/python-rpc/framework/daemon.py b/utils/python-rpc/framework/daemon.py index 074f8de37..996137814 100644 --- a/utils/python-rpc/framework/daemon.py +++ b/utils/python-rpc/framework/daemon.py @@ -554,6 +554,16 @@ class Daemon(object): } return self.rpc.send_json_rpc_request(flush_cache) + def sync_txpool(self): + sync_txpool = { + 'method': 'sync_txpool', + 'params': { + }, + 'jsonrpc': '2.0', + 'id': '0' + } + return self.rpc.send_json_rpc_request(sync_txpool) + def rpc_access_info(self, client): rpc_access_info = { 'method': 'rpc_access_info', diff --git a/utils/systemd/monerod.service b/utils/systemd/monerod.service index 5f37e54b2..dbb9e8aaa 100644 --- a/utils/systemd/monerod.service +++ b/utils/systemd/monerod.service @@ -7,6 +7,8 @@ User=monero Group=monero WorkingDirectory=~ RuntimeDirectory=monero +StateDirectory=monero +LogsDirectory=monero # Clearnet config # |