diff options
560 files changed, 10301 insertions, 1352 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 191cc8966..543e58eca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # @@ -887,10 +887,16 @@ find_package(Boost 1.58 QUIET REQUIRED COMPONENTS system filesystem thread date_ set(CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_LIB_SUFFIXES}) if(NOT Boost_FOUND) - die("Could not find Boost libraries, please make sure you have installed Boost or libboost-all-dev (1.58) or the equivalent") + die("Could not find Boost libraries, please make sure you have installed Boost or libboost-all-dev (>=1.58) or the equivalent") elseif(Boost_FOUND) message(STATUS "Found Boost Version: ${Boost_VERSION}") - if (Boost_VERSION VERSION_LESS 106200 AND NOT (OPENSSL_VERSION VERSION_LESS 1.1)) + if (Boost_VERSION VERSION_LESS 10 AND Boost_VERSION VERSION_LESS 1.62.0 AND NOT (OPENSSL_VERSION VERSION_LESS 1.1)) + set(BOOST_BEFORE_1_62 true) + endif() + if (NOT Boost_VERSION VERSION_LESS 10 AND Boost_VERSION VERSION_LESS 106200 AND NOT (OPENSSL_VERSION VERSION_LESS 1.1)) + set(BOOST_BEFORE_1_62 true) + endif() + if (BOOST_BEFORE_1_62) message(FATAL_ERROR "Boost older than 1.62 is too old to link with OpenSSL 1.1 or newer. " "Update Boost or install OpenSSL 1.0 and set path to it when running cmake: " "cmake -DOPENSSL_ROOT_DIR='/usr/include/openssl-1.0;/usr/lib/openssl-1.0'") diff --git a/CMakeLists_IOS.txt b/CMakeLists_IOS.txt index 9273af19f..3b7eacc8a 100644 --- a/CMakeLists_IOS.txt +++ b/CMakeLists_IOS.txt @@ -1,4 +1,4 @@ -# Portions Copyright (c) 2017-2018, The Monero Project +# Portions Copyright (c) 2017-2019, 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 # diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 883d366aa..7b184c00a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -88,26 +88,26 @@ C4 is meant to provide a reusable optimal collaboration model for open source so ### Preliminaries -- The project SHALL use the git distributed revision control system. -- The project SHALL be hosted on github.com or equivalent, herein called the "Platform". -- The project SHALL use the Platform issue tracker. +- The project MUST use the git distributed revision control system. +- The project MUST be hosted on github.com or equivalent, herein called the "Platform". +- The project MUST use the Platform issue tracker. - Non-GitHub example: - "Platform" could be a vanilla git repo and Trac hosted on the same machine/network. - The Platform issue tracker would be Trac. - The project SHOULD have clearly documented guidelines for code style. - A "Contributor" is a person who wishes to provide a patch, being a set of commits that solve some clearly identified problem. - A "Maintainer" is a person who merges patches to the project. Maintainers are not developers; their job is to enforce process. -- Contributors SHALL NOT have commit access to the repository unless they are also Maintainers. -- Maintainers SHALL have commit access to the repository. -- Everyone, without distinction or discrimination, SHALL have an equal right to become a Contributor under the terms of this contract. +- Contributors MUST NOT have commit access to the repository unless they are also Maintainers. +- Maintainers MUST have commit access to the repository. +- Everyone, without distinction or discrimination, MUST have an equal right to become a Contributor under the terms of this contract. ### Licensing and ownership -- The project SHALL use a share-alike license, such as BSD-3, the GPLv3 or a variant thereof (LGPL, AGPL), or the MPLv2. -- All contributions to the project source code ("patches") SHALL use the same license as the project. -- All patches are owned by their authors. There SHALL NOT be any copyright assignment process. -- The copyrights in the project SHALL be owned collectively by all its Contributors. -- Each Contributor SHALL be responsible for identifying themselves in the project Contributor list. +- The project MUST use a share-alike license, such as BSD-3, the GPLv3 or a variant thereof (LGPL, AGPL), or the MPLv2. +- All contributions to the project source code ("patches") MUST use the same license as the project. +- All patches are owned by their authors. There MUST NOT be any copyright assignment process. +- The copyrights in the project MUST be owned collectively by all its Contributors. +- Each Contributor MUST be responsible for identifying themselves in the project Contributor list. ### Patch requirements @@ -116,27 +116,27 @@ C4 is meant to provide a reusable optimal collaboration model for open source so - A patch SHOULD be a minimal and accurate answer to exactly one identified and agreed problem. - A patch MUST adhere to the code style guidelines of the project if these are defined. - A patch MUST adhere to the "Evolution of Public Contracts" guidelines defined below. -- A patch SHALL NOT include non-trivial code from other projects unless the Contributor is the original author of that code. +- A patch MUST NOT include non-trivial code from other projects unless the Contributor is the original author of that code. - A patch MUST compile cleanly and pass project self-tests on at least the principle target platform. - A patch commit message SHOULD consist of a single short (less than 50 character) line summarizing the change, optionally followed by a blank line and then a more thorough description. - A "Correct Patch" is one that satisfies the above requirements. ### Development process -- Change on the project SHALL be governed by the pattern of accurately identifying problems and applying minimal, accurate solutions to these problems. +- Change on the project MUST be governed by the pattern of accurately identifying problems and applying minimal, accurate solutions to these problems. - To request changes, a user SHOULD log an issue on the project Platform issue tracker. - The user or Contributor SHOULD write the issue by describing the problem they face or observe. - The user or Contributor SHOULD seek consensus on the accuracy of their observation, and the value of solving the problem. -- Users SHALL NOT log feature requests, ideas, or suggestions unrelated to Monero code or Monero's dependency code or Monero's potential/future dependency code or research which successfully implements Monero. -- Users SHALL NOT log any solutions to problems (verifiable or hypothetical) of which are not explicitly documented and/or not provable and/or cannot be reasonably proven. -- Thus, the release history of the project SHALL be a list of meaningful issues logged and solved. -- To work on an issue, a Contributor SHALL fork the project repository and then work on their forked repository. -- To submit a patch, a Contributor SHALL create a Platform pull request back to the project. -- A Contributor SHALL NOT commit changes directly to the project. +- Users MUST NOT log feature requests, ideas, or suggestions unrelated to Monero code or Monero's dependency code or Monero's potential/future dependency code or research which successfully implements Monero. +- Users MUST NOT log any solutions to problems (verifiable or hypothetical) of which are not explicitly documented and/or not provable and/or cannot be reasonably proven. +- Thus, the release history of the project MUST be a list of meaningful issues logged and solved. +- To work on an issue, a Contributor MUST fork the project repository and then work on their forked repository. +- To submit a patch, a Contributor MUST create a Platform pull request back to the project. +- A Contributor MUST NOT commit changes directly to the project. - To discuss a patch, people MAY comment on the Platform pull request, on the commit, or elsewhere. -- To accept or reject a patch, a Maintainer SHALL use the Platform interface. +- To accept or reject a patch, a Maintainer MUST use the Platform interface. - Maintainers SHOULD NOT merge their own patches except in exceptional cases, such as non-responsiveness from other Maintainers for an extended period (more than 30 days) or unless urgent as defined by the Monero Maintainers Team. -- Maintainers SHALL NOT make value judgments on correct patches unless the Maintainer (as may happen in rare circumstances) is a core code developer. +- Maintainers MUST NOT make value judgments on correct patches unless the Maintainer (as may happen in rare circumstances) is a core code developer. - Maintainers MUST NOT merge pull requests in less than 168 hours (1 week) unless deemed urgent by at least 2 people from the Monero Maintainer Team. - The Contributor MAY tag an issue as "Ready" after making a pull request for the issue. - The user who created an issue SHOULD close the issue after checking the patch is successful. @@ -146,27 +146,27 @@ C4 is meant to provide a reusable optimal collaboration model for open source so ### Creating stable releases -- The project SHALL have one branch ("master") that always holds the latest in-progress version and SHOULD always build. -- The project SHALL NOT use topic branches for any reason. Personal forks MAY use topic branches. -- To make a stable release someone SHALL fork the repository by copying it and thus become maintainer of this repository. +- The project MUST have one branch ("master") that always holds the latest in-progress version and SHOULD always build. +- The project MUST NOT use topic branches for any reason. Personal forks MAY use topic branches. +- To make a stable release someone MUST fork the repository by copying it and thus become maintainer of this repository. - Forking a project for stabilization MAY be done unilaterally and without agreement of project maintainers. -- A patch to a stabilization project declared "stable" SHALL be accompanied by a reproducible test case. +- A patch to a stabilization project declared "stable" MUST be accompanied by a reproducible test case. ### Evolution of public contracts -- All Public Contracts (APIs or protocols) SHALL be documented. +- All Public Contracts (APIs or protocols) MUST be documented. - All Public Contracts SHOULD have space for extensibility and experimentation. - A patch that modifies a stable Public Contract SHOULD not break existing applications unless there is overriding consensus on the value of doing this. - A patch that introduces new features to a Public Contract SHOULD do so using new names. - Old names SHOULD be deprecated in a systematic fashion by marking new names as "experimental" until they are stable, then marking the old names as "deprecated". - When sufficient time has passed, old deprecated names SHOULD be marked "legacy" and eventually removed. -- Old names SHALL NOT be reused by new features. +- Old names MUST NOT be reused by new features. - When old names are removed, their implementations MUST provoke an exception (assertion) if used by applications. ### Project administration -- The project founders SHALL act as Administrators to manage the set of project Maintainers. -- The Administrators SHALL ensure their own succession over time by promoting the most effective Maintainers. -- A new Contributor who makes a correct patch SHALL be invited to become a Maintainer. +- The project founders MUST act as Administrators to manage the set of project Maintainers. +- The Administrators MUST ensure their own succession over time by promoting the most effective Maintainers. +- A new Contributor who makes a correct patch MUST be invited to become a Maintainer. - Administrators MAY remove Maintainers who are inactive for an extended period of time, or who repeatedly fail to apply this process accurately. - Administrators SHOULD block or ban "bad actors" who cause stress and pain to others in the project. This should be done after public discussion, with a chance for all parties to speak. A bad actor is someone who repeatedly ignores the rules and culture of the project, who is needlessly argumentative or hostile, or who is offensive, and who is unable to self-correct their behavior when asked to do so by others. diff --git a/Dockerfile b/Dockerfile index 9fe7cfb8f..d932e0173 100644 --- a/Dockerfile +++ b/Dockerfile @@ -185,8 +185,14 @@ RUN set -ex && \ rm -rf /var/lib/apt COPY --from=builder /src/build/release/bin /usr/local/bin/ +# Create monero user +RUN adduser --system --group --disabled-password monero && \ + mkdir -p /wallet /home/monero/.bitmonero && \ + chown -R monero:monero /home/monero/.bitmonero && \ + chown -R monero:monero /wallet + # Contains the blockchain -VOLUME /root/.bitmonero +VOLUME /home/monero/.bitmonero # Generate your wallet via accessing the container and run: # cd /wallet @@ -196,5 +202,8 @@ VOLUME /wallet EXPOSE 18080 EXPOSE 18081 +# switch to user monero +USER monero + ENTRYPOINT ["monerod", "--p2p-bind-ip=0.0.0.0", "--p2p-bind-port=18080", "--rpc-bind-ip=0.0.0.0", "--rpc-bind-port=18081", "--non-interactive", "--confirm-external-bind"] @@ -1,4 +1,4 @@ -Copyright (c) 2014-2018, The Monero Project +Copyright (c) 2014-2019, The Monero Project All rights reserved. @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # @@ -63,6 +63,10 @@ debug-test: mkdir -p $(builddir)/debug cd $(builddir)/debug && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=Debug $(topdir) && $(MAKE) && $(MAKE) ARGS="-E libwallet_api_tests" test +debug-test-trezor: + mkdir -p $(builddir)/debug + cd $(builddir)/debug && cmake -D BUILD_TESTS=ON -D TREZOR_DEBUG=ON -D CMAKE_BUILD_TYPE=Debug $(topdir) && $(MAKE) && $(MAKE) ARGS="-E libwallet_api_tests" test + debug-all: mkdir -p $(builddir)/debug cd $(builddir)/debug && cmake -D BUILD_TESTS=ON -D BUILD_SHARED_LIBS=OFF -D CMAKE_BUILD_TYPE=Debug $(topdir) && $(MAKE) @@ -47,9 +47,9 @@ These builds are of the master branch, which is used for active development and | Ubuntu 16.04 | amd64 | [![Ubuntu 16.04 amd64](https://build.getmonero.org/png?builder=monero-static-ubuntu-amd64)](https://build.getmonero.org/builders/monero-static-ubuntu-amd64) | Ubuntu 16.04 | armv7 | [![Ubuntu 16.04 armv7](https://build.getmonero.org/png?builder=monero-static-ubuntu-arm7)](https://build.getmonero.org/builders/monero-static-ubuntu-arm7) | Debian Stable | armv8 | [![Debian armv8](https://build.getmonero.org/png?builder=monero-static-debian-armv8)](https://build.getmonero.org/builders/monero-static-debian-armv8) -| OSX 10.11 | amd64 | [![OSX 10.11 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.11)](https://build.getmonero.org/builders/monero-static-osx-10.11) -| OSX 10.12 | amd64 | [![OSX 10.12 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.12)](https://build.getmonero.org/builders/monero-static-osx-10.12) -| OSX 10.13 | amd64 | [![OSX 10.13 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.13)](https://build.getmonero.org/builders/monero-static-osx-10.13) +| macOS 10.11 | amd64 | [![macOS 10.11 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.11)](https://build.getmonero.org/builders/monero-static-osx-10.11) +| macOS 10.12 | amd64 | [![macOS 10.12 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.12)](https://build.getmonero.org/builders/monero-static-osx-10.12) +| macOS 10.13 | amd64 | [![macOS 10.13 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.13)](https://build.getmonero.org/builders/monero-static-osx-10.13) | FreeBSD 11 | amd64 | [![FreeBSD 11 amd64](https://build.getmonero.org/png?builder=monero-static-freebsd64)](https://build.getmonero.org/builders/monero-static-freebsd64) | DragonFly BSD 4.6 | amd64 | [![DragonFly BSD amd64](https://build.getmonero.org/png?builder=monero-static-dragonflybsd-amd64)](https://build.getmonero.org/builders/monero-static-dragonflybsd-amd64) | Windows (MSYS2/MinGW) | i686 | [![Windows (MSYS2/MinGW) i686](https://build.getmonero.org/png?builder=monero-static-win32)](https://build.getmonero.org/builders/monero-static-win32) @@ -73,6 +73,8 @@ Monero is a private, secure, untraceable, decentralised digital currency. You ar **Untraceability:** By taking advantage of ring signatures, a special property of a certain type of cryptography, Monero is able to ensure that transactions are not only untraceable, but have an optional measure of ambiguity that ensures that transactions cannot easily be tied back to an individual user or computer. +**Decentralization:** The utility of monero depends on its decentralised peer-to-peer consensus network - anyone should be able to run the monero software, validate the integrity of the blockchain, and participate in all aspects of the monero network using consumer-grade commodity hardware. Decentralization of the monero network is maintained by software development that minimizes the costs of running the monero software and inhibits the proliferation of specialized, non-commodity hardware. + ## About this project This is the core implementation of Monero. It is open source and completely free to use without restrictions, except for those specified in the license agreement below. There are no restrictions on anyone creating an alternative implementation of Monero that uses the protocol and network in a compatible manner. @@ -194,7 +196,7 @@ If you already have a repo cloned, initialize and update: Monero uses the CMake build system and a top-level [Makefile](Makefile) that invokes cmake commands as needed. -#### On Linux and OS X +#### On Linux and macOS * Install the dependencies * Change to the root of the source code directory, change to the most recent release branch, and build: @@ -208,7 +210,7 @@ invokes cmake commands as needed. this to be worthwhile, the machine should have one core and about 2GB of RAM available per thread. - *Note*: If cmake can not find zmq.hpp file on OS X, installing `zmq.hpp` from + *Note*: If cmake can not find zmq.hpp file on macOS, installing `zmq.hpp` from https://github.com/zeromq/cppzmq to `/usr/local/include` should fix that error. *Note*: The instructions above will compile the most stable release of the diff --git a/cmake/32-bit-toolchain.cmake b/cmake/32-bit-toolchain.cmake index 253523e66..2d53adf06 100644 --- a/cmake/32-bit-toolchain.cmake +++ b/cmake/32-bit-toolchain.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/cmake/64-bit-toolchain.cmake b/cmake/64-bit-toolchain.cmake index b4b528347..7c56eef95 100644 --- a/cmake/64-bit-toolchain.cmake +++ b/cmake/64-bit-toolchain.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/cmake/CheckTrezor.cmake b/cmake/CheckTrezor.cmake index 71214363d..6aabdda36 100644 --- a/cmake/CheckTrezor.cmake +++ b/cmake/CheckTrezor.cmake @@ -1,6 +1,8 @@ OPTION(USE_DEVICE_TREZOR "Trezor support compilation" ON) OPTION(USE_DEVICE_TREZOR_LIBUSB "Trezor LibUSB compilation" ON) OPTION(USE_DEVICE_TREZOR_UDP_RELEASE "Trezor UdpTransport in release mode" OFF) +OPTION(USE_DEVICE_TREZOR_DEBUG "Trezor Debugging enabled" OFF) +OPTION(TREZOR_DEBUG "Main trezor debugging switch" OFF) # Helper function to fix cmake < 3.6.0 FindProtobuf variables function(_trezor_protobuf_fix_vars) @@ -53,6 +55,14 @@ if (USE_DEVICE_TREZOR) set(Protobuf_FOUND 1) # override found if all rquired info was provided by variables endif() + if(TREZOR_DEBUG) + set(USE_DEVICE_TREZOR_DEBUG 1) + endif() + + # Compile debugging support (for tests) + if (USE_DEVICE_TREZOR_DEBUG) + add_definitions(-DWITH_TREZOR_DEBUGGING=1) + endif() else() message(STATUS "Trezor support disabled by USE_DEVICE_TREZOR") endif() @@ -106,7 +116,12 @@ endif() if(Protobuf_FOUND AND USE_DEVICE_TREZOR AND TREZOR_PYTHON AND Protobuf_COMPILE_TEST_PASSED) set(ENV{PROTOBUF_INCLUDE_DIRS} "${Protobuf_INCLUDE_DIR}") set(ENV{PROTOBUF_PROTOC_EXECUTABLE} "${Protobuf_PROTOC_EXECUTABLE}") - execute_process(COMMAND ${TREZOR_PYTHON} tools/build_protob.py WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/../src/device_trezor/trezor RESULT_VARIABLE RET OUTPUT_VARIABLE OUT ERROR_VARIABLE ERR) + set(TREZOR_PROTOBUF_PARAMS "") + if (USE_DEVICE_TREZOR_DEBUG) + set(TREZOR_PROTOBUF_PARAMS "--debug") + endif() + + execute_process(COMMAND ${TREZOR_PYTHON} tools/build_protob.py ${TREZOR_PROTOBUF_PARAMS} WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/../src/device_trezor/trezor RESULT_VARIABLE RET OUTPUT_VARIABLE OUT ERROR_VARIABLE ERR) if(RET) message(WARNING "Trezor protobuf messages could not be regenerated (err=${RET}, python ${PYTHON})." "OUT: ${OUT}, ERR: ${ERR}." diff --git a/cmake/FindUnbound.cmake b/cmake/FindUnbound.cmake index a392d59ca..e3eb3d67e 100644 --- a/cmake/FindUnbound.cmake +++ b/cmake/FindUnbound.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, are diff --git a/cmake/GenVersion.cmake b/cmake/GenVersion.cmake index 289115175..1ea5b209c 100644 --- a/cmake/GenVersion.cmake +++ b/cmake/GenVersion.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/cmake/Version.cmake b/cmake/Version.cmake index 3677e80d7..632c1431c 100644 --- a/cmake/Version.cmake +++ b/cmake/Version.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/cmake/test-libusb-version.c b/cmake/test-libusb-version.c index 309e4ad27..b80191b58 100644 --- a/cmake/test-libusb-version.c +++ b/cmake/test-libusb-version.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/cmake/test-protobuf.cpp b/cmake/test-protobuf.cpp index 532df7cbe..0a1ca82be 100644 --- a/cmake/test-protobuf.cpp +++ b/cmake/test-protobuf.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/cmake/test-static-assert.c b/cmake/test-static-assert.c index 28a80a972..2e6c84839 100644 --- a/cmake/test-static-assert.c +++ b/cmake/test-static-assert.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/cmake/test-static-assert.cpp b/cmake/test-static-assert.cpp index 28a80a972..2e6c84839 100644 --- a/cmake/test-static-assert.cpp +++ b/cmake/test-static-assert.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index 3bebbe0d2..0db645ce9 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/contrib/epee/CMakeLists.txt b/contrib/epee/CMakeLists.txt index 035b24b36..a2c636304 100644 --- a/contrib/epee/CMakeLists.txt +++ b/contrib/epee/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/contrib/epee/include/hex.h b/contrib/epee/include/hex.h index 250432173..6e720f128 100644 --- a/contrib/epee/include/hex.h +++ b/contrib/epee/include/hex.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/include/int-util.h b/contrib/epee/include/int-util.h index 3bcc085e2..0ed6505ff 100644 --- a/contrib/epee/include/int-util.h +++ b/contrib/epee/include/int-util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/include/memwipe.h b/contrib/epee/include/memwipe.h index 0d8f491b7..d586608cb 100644 --- a/contrib/epee/include/memwipe.h +++ b/contrib/epee/include/memwipe.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index f5548c585..e4b504466 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -58,6 +58,12 @@ #define DEFAULT_TIMEOUT_MS_REMOTE 300000 // 5 minutes #define TIMEOUT_EXTRA_MS_PER_BYTE 0.2 +#if BOOST_VERSION >= 107000 +#define GET_IO_SERVICE(s) ((boost::asio::io_context&)(s).get_executor().context()) +#else +#define GET_IO_SERVICE(s) ((s).get_io_service()) +#endif + PRAGMA_WARNING_PUSH namespace epee { @@ -99,7 +105,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) m_connection_type( connection_type ), m_throttle_speed_in("speed_in", "throttle_speed_in"), m_throttle_speed_out("speed_out", "throttle_speed_out"), - m_timer(socket_.get_io_service()), + m_timer(GET_IO_SERVICE(socket_)), m_local(false), m_ready_to_close(false) { @@ -243,7 +249,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) template<class t_protocol_handler> boost::asio::io_service& connection<t_protocol_handler>::get_io_service() { - return socket().get_io_service(); + return GET_IO_SERVICE(socket()); } //--------------------------------------------------------------------------------- template<class t_protocol_handler> @@ -487,7 +493,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) if(!m_is_multithreaded) { //single thread model, we can wait in blocked call - size_t cnt = socket().get_io_service().run_one(); + size_t cnt = GET_IO_SERVICE(socket()).run_one(); if(!cnt)//service is going to quit return false; }else @@ -497,7 +503,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) //if no handlers were called //TODO: Maybe we need to have have critical section + event + callback to upper protocol to //ask it inside(!) critical region if we still able to go in event wait... - size_t cnt = socket().get_io_service().poll_one(); + size_t cnt = GET_IO_SERVICE(socket()).poll_one(); if(!cnt) misc_utils::sleep_no_w(1); } @@ -670,9 +676,9 @@ PRAGMA_WARNING_DISABLE_VS(4355) CHECK_AND_ASSERT_MES( size_now == m_send_que.front().size(), false, "Unexpected queue size"); reset_timer(get_default_timeout(), false); async_write(boost::asio::buffer(m_send_que.front().data(), size_now ) , - //strand_.wrap( + strand_.wrap( boost::bind(&connection<t_protocol_handler>::handle_write, self, _1, _2) - //) + ) ); //_dbg3("(chunk): " << size_now); //logger_handle_net_write(size_now); @@ -761,8 +767,9 @@ PRAGMA_WARNING_DISABLE_VS(4355) // Initiate graceful connection closure. m_timer.cancel(); boost::system::error_code ignored_ec; + if (m_ssl_support == epee::net_utils::ssl_support_t::e_ssl_support_enabled) + socket_.shutdown(ignored_ec); socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); - socket().close(); if (!m_host.empty()) { try { host_count(m_host, -1); } catch (...) { /* ignore */ } @@ -855,9 +862,9 @@ PRAGMA_WARNING_DISABLE_VS(4355) do_send_handler_write_from_queue(e, m_send_que.front().size() , m_send_que.size()); // (((H))) CHECK_AND_ASSERT_MES( size_now == m_send_que.front().size(), void(), "Unexpected queue size"); async_write(boost::asio::buffer(m_send_que.front().data(), size_now) , - // strand_.wrap( + strand_.wrap( boost::bind(&connection<t_protocol_handler>::handle_write, connection<t_protocol_handler>::shared_from_this(), _1, _2) - // ) + ) ); //_dbg3("(normal)" << size_now); } @@ -1207,7 +1214,7 @@ POP_WARNINGS template<class t_protocol_handler> bool boosted_tcp_server<t_protocol_handler>::add_connection(t_connection_context& out, boost::asio::ip::tcp::socket&& sock, network_address real_remote, epee::net_utils::ssl_support_t ssl_support) { - if(std::addressof(get_io_service()) == std::addressof(sock.get_io_service())) + if(std::addressof(get_io_service()) == std::addressof(GET_IO_SERVICE(sock))) { connection_ptr conn(new connection<t_protocol_handler>(std::move(sock), m_state, m_connection_type, ssl_support, m_ssl_context)); if(conn->start(false, 1 < m_threads_count, std::move(real_remote))) diff --git a/contrib/epee/include/net/connection_basic.hpp b/contrib/epee/include/net/connection_basic.hpp index 328f9afbf..feedc6895 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-2018, The Monero Project +// Copyright (c) 2014-2019, 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 4324c41fd..00b9750b7 100644 --- a/contrib/epee/include/net/http_auth.h +++ b/contrib/epee/include/net/http_auth.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 9d12291f4..d7f2cc37a 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-2018, The Monero Project +// Copyright (c) 2014-2019, 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 7df79a908..5092241a4 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/include/span.h b/contrib/epee/include/span.h index cfb5b1a17..19720ea8b 100644 --- a/contrib/epee/include/span.h +++ b/contrib/epee/include/span.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/include/wipeable_string.h b/contrib/epee/include/wipeable_string.h index 31854fe2e..f0e526b92 100644 --- a/contrib/epee/include/wipeable_string.h +++ b/contrib/epee/include/wipeable_string.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt index e913211ea..0787a9d08 100644 --- a/contrib/epee/src/CMakeLists.txt +++ b/contrib/epee/src/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/contrib/epee/src/connection_basic.cpp b/contrib/epee/src/connection_basic.cpp index 377fb3452..cafc08ead 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -47,6 +47,12 @@ // TODO: #include "net/network_throttle-detail.hpp" +#if BOOST_VERSION >= 107000 +#define GET_IO_SERVICE(s) ((boost::asio::io_context&)(s).get_executor().context()) +#else +#define GET_IO_SERVICE(s) ((s).get_io_service()) +#endif + #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "net.conn" @@ -117,8 +123,8 @@ connection_basic::connection_basic(boost::asio::ip::tcp::socket&& sock, boost::s : m_stats(std::move(stats)), mI( new connection_basic_pimpl("peer") ), - strand_(sock.get_io_service()), - socket_(sock.get_io_service(), ssl_context.context), + strand_(GET_IO_SERVICE(sock)), + socket_(GET_IO_SERVICE(sock), ssl_context.context), m_want_close_connection(false), m_was_shutdown(false), m_ssl_support(ssl_support), @@ -167,7 +173,6 @@ connection_basic::~connection_basic() noexcept(false) { std::string remote_addr_str = "?"; try { boost::system::error_code e; remote_addr_str = socket().remote_endpoint(e).address().to_string(); } catch(...){} ; _note("Destructing connection #"<<mI->m_peer_number << " to " << remote_addr_str); -try { throw 0; } catch(...){} } void connection_basic::set_rate_up_limit(uint64_t limit) { diff --git a/contrib/epee/src/hex.cpp b/contrib/epee/src/hex.cpp index 8421dcae9..558983f7e 100644 --- a/contrib/epee/src/hex.cpp +++ b/contrib/epee/src/hex.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/src/http_auth.cpp b/contrib/epee/src/http_auth.cpp index dc968d971..289069daa 100644 --- a/contrib/epee/src/http_auth.cpp +++ b/contrib/epee/src/http_auth.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/src/memwipe.c b/contrib/epee/src/memwipe.c index c2a26c392..ad1ef510d 100644 --- a/contrib/epee/src/memwipe.c +++ b/contrib/epee/src/memwipe.c @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/src/network_throttle-detail.cpp b/contrib/epee/src/network_throttle-detail.cpp index 0b42402bd..f89e7aec0 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/src/network_throttle.cpp b/contrib/epee/src/network_throttle.cpp index 167738855..f4f0b2c46 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/contrib/epee/src/wipeable_string.cpp b/contrib/epee/src/wipeable_string.cpp index 69f92e106..3a6ee5dac 100644 --- a/contrib/epee/src/wipeable_string.cpp +++ b/contrib/epee/src/wipeable_string.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 7553f87ea..bc4344b34 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/external/db_drivers/CMakeLists.txt b/external/db_drivers/CMakeLists.txt index d537ec029..04776c475 100644 --- a/external/db_drivers/CMakeLists.txt +++ b/external/db_drivers/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/external/db_drivers/liblmdb/CMakeLists.txt b/external/db_drivers/liblmdb/CMakeLists.txt index 2e8822f54..562ebb1eb 100644 --- a/external/db_drivers/liblmdb/CMakeLists.txt +++ b/external/db_drivers/liblmdb/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/external/easylogging++/CMakeLists.txt b/external/easylogging++/CMakeLists.txt index 78795d54b..8287024c2 100644 --- a/external/easylogging++/CMakeLists.txt +++ b/external/easylogging++/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/external/trezor-common b/external/trezor-common -Subproject 588f8e03f5ac111adf719f0a437de67481a26ae +Subproject cb238cb1f134accc4200217d9511115a8f61c6c diff --git a/external/unbound b/external/unbound -Subproject 7f23967954736dcaa366806b9eaba7e2bdfede1 +Subproject 0f6c0579d66b65f86066e30e7876105ba2775ef diff --git a/include/INode.h b/include/INode.h index 21be0b2f3..0d896ca69 100644 --- a/include/INode.h +++ b/include/INode.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/include/IWallet.h b/include/IWallet.h index 98110a9d6..98362895d 100644 --- a/include/IWallet.h +++ b/include/IWallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f1454b48e..da6d76d97 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # @@ -109,6 +109,7 @@ add_subdirectory(ringct) add_subdirectory(checkpoints) add_subdirectory(cryptonote_basic) add_subdirectory(cryptonote_core) +add_subdirectory(lmdb) add_subdirectory(multisig) add_subdirectory(net) if(NOT IOS) diff --git a/src/blockchain_db/CMakeLists.txt b/src/blockchain_db/CMakeLists.txt index 277f4458e..db161fa5e 100644 --- a/src/blockchain_db/CMakeLists.txt +++ b/src/blockchain_db/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/src/blockchain_db/berkeleydb/db_bdb.cpp b/src/blockchain_db/berkeleydb/db_bdb.cpp index 60a7326f8..3eb24494f 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.cpp +++ b/src/blockchain_db/berkeleydb/db_bdb.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are diff --git a/src/blockchain_db/berkeleydb/db_bdb.h b/src/blockchain_db/berkeleydb/db_bdb.h index e80adae9e..04a33d7c6 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.h +++ b/src/blockchain_db/berkeleydb/db_bdb.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index 754c3c2da..d772bf4bb 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index ed13de5b5..d2fe39fc2 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -947,6 +947,17 @@ public: virtual size_t get_block_weight(const uint64_t& height) const = 0; /** + * @brief fetch the last N blocks' weights + * + * If there are fewer than N blocks, the returned array will be smaller than N + * + * @param count the number of blocks requested + * + * @return the weights + */ + virtual std::vector<uint64_t> get_block_weights(uint64_t start_height, size_t count) const = 0; + + /** * @brief fetch a block's cumulative difficulty * * The subclass should return the cumulative difficulty of the block with the @@ -1000,6 +1011,17 @@ public: virtual uint64_t get_block_long_term_weight(const uint64_t& height) const = 0; /** + * @brief fetch the last N blocks' long term weights + * + * If there are fewer than N blocks, the returned array will be smaller than N + * + * @param count the number of blocks requested + * + * @return the weights + */ + virtual std::vector<uint64_t> get_long_term_block_weights(uint64_t start_height, size_t count) const = 0; + + /** * @brief fetch a block's hash * * The subclass should return hash of the block with the diff --git a/src/blockchain_db/db_types.h b/src/blockchain_db/db_types.h index b8c7fa3e3..04cadbb10 100644 --- a/src/blockchain_db/db_types.h +++ b/src/blockchain_db/db_types.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index e2db5e04e..3391d9bff 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are @@ -2445,6 +2445,70 @@ size_t BlockchainLMDB::get_block_weight(const uint64_t& height) const return ret; } +std::vector<uint64_t> BlockchainLMDB::get_block_info_64bit_fields(uint64_t start_height, size_t count, off_t offset) const +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + + TXN_PREFIX_RDONLY(); + RCURSOR(block_info); + + const uint64_t h = height(); + if (start_height >= h) + throw0(DB_ERROR(("Height " + std::to_string(start_height) + " not in blockchain").c_str())); + + std::vector<uint64_t> ret; + ret.reserve(count); + + MDB_val v; + uint64_t range_begin = 0, range_end = 0; + for (uint64_t height = start_height; height < h && count--; ++height) + { + if (height >= range_begin && height < range_end) + { + // nothing to do + } + else + { + int result = 0; + if (range_end > 0) + { + MDB_val k2; + result = mdb_cursor_get(m_cur_block_info, &k2, &v, MDB_NEXT_MULTIPLE); + range_begin = ((const mdb_block_info*)v.mv_data)->bi_height; + range_end = range_begin + v.mv_size / sizeof(mdb_block_info); // whole records please + if (height < range_begin || height >= range_end) + throw0(DB_ERROR(("Height " + std::to_string(height) + " not included in multiple record range: " + std::to_string(range_begin) + "-" + std::to_string(range_end)).c_str())); + } + else + { + v.mv_size = sizeof(uint64_t); + v.mv_data = (void*)&height; + result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &v, MDB_GET_BOTH); + range_begin = height; + range_end = range_begin + 1; + } + if (result) + throw0(DB_ERROR(lmdb_error("Error attempting to retrieve block_info from the db: ", result).c_str())); + } + const mdb_block_info *bi = ((const mdb_block_info *)v.mv_data) + (height - range_begin); + ret.push_back(*(const uint64_t*)(((const char*)bi) + offset)); + } + + TXN_POSTFIX_RDONLY(); + return ret; +} + +std::vector<uint64_t> BlockchainLMDB::get_block_weights(uint64_t start_height, size_t count) const +{ + return get_block_info_64bit_fields(start_height, count, offsetof(mdb_block_info, bi_weight)); +} + +std::vector<uint64_t> BlockchainLMDB::get_long_term_block_weights(uint64_t start_height, size_t count) const +{ + return get_block_info_64bit_fields(start_height, count, offsetof(mdb_block_info, bi_long_term_block_weight)); +} + difficulty_type BlockchainLMDB::get_block_cumulative_difficulty(const uint64_t& height) const { LOG_PRINT_L3("BlockchainLMDB::" << __func__ << " height: " << height); diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 82016c17a..9185bd409 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are @@ -219,6 +219,8 @@ public: virtual size_t get_block_weight(const uint64_t& height) const; + virtual std::vector<uint64_t> get_block_weights(uint64_t start_height, size_t count) const; + virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const; virtual difficulty_type get_block_difficulty(const uint64_t& height) const; @@ -227,6 +229,8 @@ public: virtual uint64_t get_block_long_term_weight(const uint64_t& height) const; + virtual std::vector<uint64_t> get_long_term_block_weights(uint64_t start_height, size_t count) const; + virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const; virtual std::vector<block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) const; @@ -394,6 +398,8 @@ private: virtual uint64_t get_database_size() const; + std::vector<uint64_t> get_block_info_64bit_fields(uint64_t start_height, size_t count, off_t offset) const; + // fix up anything that may be wrong due to past bugs virtual void fixup(); diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h index ac1849b5f..7916364c5 100644 --- a/src/blockchain_db/testdb.h +++ b/src/blockchain_db/testdb.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -72,10 +72,12 @@ public: virtual std::vector<uint64_t> get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights) const { return {}; } virtual uint64_t get_top_block_timestamp() const { return 0; } virtual size_t get_block_weight(const uint64_t& height) const { return 128; } + virtual std::vector<uint64_t> get_block_weights(uint64_t start_height, size_t count) const { return {}; } virtual cryptonote::difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const { return 10; } virtual cryptonote::difficulty_type get_block_difficulty(const uint64_t& height) const { return 0; } virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const { return 10000000000; } virtual uint64_t get_block_long_term_weight(const uint64_t& height) const { return 128; } + virtual std::vector<uint64_t> get_long_term_block_weights(uint64_t start_height, size_t count) const { return {}; } virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const { return crypto::hash(); } virtual std::vector<cryptonote::block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector<cryptonote::block>(); } virtual std::vector<crypto::hash> get_hashes_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector<crypto::hash>(); } diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index df74eb695..0ba7ee86c 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/src/blockchain_utilities/README.md b/src/blockchain_utilities/README.md index 5d968cd75..ad5963f27 100644 --- a/src/blockchain_utilities/README.md +++ b/src/blockchain_utilities/README.md @@ -1,6 +1,6 @@ # Monero Blockchain Utilities -Copyright (c) 2014-2018, The Monero Project +Copyright (c) 2014-2019, The Monero Project ## Introduction diff --git a/src/blockchain_utilities/blockchain_ancestry.cpp b/src/blockchain_utilities/blockchain_ancestry.cpp index a64ce160a..a6ee0573f 100644 --- a/src/blockchain_utilities/blockchain_ancestry.cpp +++ b/src/blockchain_utilities/blockchain_ancestry.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index 8b007e901..6ff184041 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_depth.cpp b/src/blockchain_utilities/blockchain_depth.cpp index 8060b0de4..8be83ee67 100644 --- a/src/blockchain_utilities/blockchain_depth.cpp +++ b/src/blockchain_utilities/blockchain_depth.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_export.cpp b/src/blockchain_utilities/blockchain_export.cpp index 5c5bc7f69..fa1243c1f 100644 --- a/src/blockchain_utilities/blockchain_export.cpp +++ b/src/blockchain_utilities/blockchain_export.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index d7a88f935..e4efdc3cb 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 f6136c1ba..2d49b6ecd 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_stats.cpp b/src/blockchain_utilities/blockchain_stats.cpp index aae8f333b..4cc84bf4a 100644 --- a/src/blockchain_utilities/blockchain_stats.cpp +++ b/src/blockchain_utilities/blockchain_stats.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_usage.cpp b/src/blockchain_utilities/blockchain_usage.cpp index 38a0b2648..bd73350b3 100644 --- a/src/blockchain_utilities/blockchain_usage.cpp +++ b/src/blockchain_utilities/blockchain_usage.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blockchain_utilities.h b/src/blockchain_utilities/blockchain_utilities.h index e690305c4..78487b995 100644 --- a/src/blockchain_utilities/blockchain_utilities.h +++ b/src/blockchain_utilities/blockchain_utilities.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blocksdat_file.cpp b/src/blockchain_utilities/blocksdat_file.cpp index 45ef33acb..f56ff5f94 100644 --- a/src/blockchain_utilities/blocksdat_file.cpp +++ b/src/blockchain_utilities/blocksdat_file.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/blocksdat_file.h b/src/blockchain_utilities/blocksdat_file.h index 70a1f30a7..315713424 100644 --- a/src/blockchain_utilities/blocksdat_file.h +++ b/src/blockchain_utilities/blocksdat_file.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/bootstrap_file.cpp b/src/blockchain_utilities/bootstrap_file.cpp index a8c46d661..fb9a24f5d 100644 --- a/src/blockchain_utilities/bootstrap_file.cpp +++ b/src/blockchain_utilities/bootstrap_file.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/bootstrap_file.h b/src/blockchain_utilities/bootstrap_file.h index 187db0938..5fb2cf366 100644 --- a/src/blockchain_utilities/bootstrap_file.h +++ b/src/blockchain_utilities/bootstrap_file.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/blockchain_utilities/bootstrap_serialization.h b/src/blockchain_utilities/bootstrap_serialization.h index 278a7b40f..554c6d56e 100644 --- a/src/blockchain_utilities/bootstrap_serialization.h +++ b/src/blockchain_utilities/bootstrap_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/blocks/CMakeLists.txt b/src/blocks/CMakeLists.txt index ff48af6dc..0b7b02fee 100644 --- a/src/blocks/CMakeLists.txt +++ b/src/blocks/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/src/checkpoints/CMakeLists.txt b/src/checkpoints/CMakeLists.txt index 715006522..d324f518e 100644 --- a/src/checkpoints/CMakeLists.txt +++ b/src/checkpoints/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index 414e481e3..e31b96646 100644 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/checkpoints/checkpoints.h b/src/checkpoints/checkpoints.h index ad2b44d1a..a55b94bf0 100644 --- a/src/checkpoints/checkpoints.h +++ b/src/checkpoints/checkpoints.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index bcf9cbce7..f06737b31 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/src/common/aligned.c b/src/common/aligned.c index 763dfd0e7..6982409f7 100644 --- a/src/common/aligned.c +++ b/src/common/aligned.c @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/apply_permutation.h b/src/common/apply_permutation.h index ff346bab1..a4b2c8b78 100644 --- a/src/common/apply_permutation.h +++ b/src/common/apply_permutation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/base58.cpp b/src/common/base58.cpp index 3562af486..ac1bf4b29 100644 --- a/src/common/base58.cpp +++ b/src/common/base58.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/base58.h b/src/common/base58.h index 69611859d..6bf2c3bb7 100644 --- a/src/common/base58.h +++ b/src/common/base58.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/boost_serialization_helper.h b/src/common/boost_serialization_helper.h index 3f5c623f8..2280f3312 100644 --- a/src/common/boost_serialization_helper.h +++ b/src/common/boost_serialization_helper.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp index 35135ea18..cae744ea5 100644 --- a/src/common/command_line.cpp +++ b/src/common/command_line.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/command_line.h b/src/common/command_line.h index 3a869bb26..b5e3d94a7 100644 --- a/src/common/command_line.h +++ b/src/common/command_line.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/common_fwd.h b/src/common/common_fwd.h index 2924d9cbe..7eaa6cdee 100644 --- a/src/common/common_fwd.h +++ b/src/common/common_fwd.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index 417b5b4ac..a341a8c81 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -232,13 +232,24 @@ public: char *str; }; +static void add_anchors(ub_ctx *ctx) +{ + const char * const *ds = ::get_builtin_ds(); + while (*ds) + { + MINFO("adding trust anchor: " << *ds); + ub_ctx_add_ta(ctx, string_copy(*ds++)); + } +} + DNSResolver::DNSResolver() : m_data(new DNSResolverData()) { int use_dns_public = 0; std::vector<std::string> dns_public_addr; - if (auto res = getenv("DNS_PUBLIC")) + const char *DNS_PUBLIC = getenv("DNS_PUBLIC"); + if (DNS_PUBLIC) { - dns_public_addr = tools::dns_utils::parse_dns_public(res); + dns_public_addr = tools::dns_utils::parse_dns_public(DNS_PUBLIC); if (!dns_public_addr.empty()) { MGINFO("Using public DNS server(s): " << boost::join(dns_public_addr, ", ") << " (TCP)"); @@ -266,11 +277,27 @@ DNSResolver::DNSResolver() : m_data(new DNSResolverData()) ub_ctx_hosts(m_data->m_ub_context, NULL); } - const char * const *ds = ::get_builtin_ds(); - while (*ds) + add_anchors(m_data->m_ub_context); + + if (!DNS_PUBLIC) { - MINFO("adding trust anchor: " << *ds); - ub_ctx_add_ta(m_data->m_ub_context, string_copy(*ds++)); + // if no DNS_PUBLIC specified, we try a lookup to what we know + // should be a valid DNSSEC record, and switch to known good + // DNSSEC resolvers if verification fails + bool available, valid; + static const char *probe_hostname = "updates.moneropulse.org"; + auto records = get_txt_record(probe_hostname, available, valid); + if (!valid) + { + MINFO("Failed to verify DNSSEC record from " << probe_hostname << ", falling back to TCP with well known DNSSEC resolvers"); + ub_ctx_delete(m_data->m_ub_context); + m_data->m_ub_context = ub_ctx_create(); + add_anchors(m_data->m_ub_context); + for (const auto &ip: DEFAULT_DNS_PUBLIC_ADDR) + ub_ctx_set_fwd(m_data->m_ub_context, string_copy(ip)); + ub_ctx_set_option(m_data->m_ub_context, string_copy("do-udp:"), string_copy("no")); + ub_ctx_set_option(m_data->m_ub_context, string_copy("do-tcp:"), string_copy("yes")); + } } } @@ -514,12 +541,12 @@ bool load_txt_records_from_dns(std::vector<std::string> &good_records, const std if (!avail[cur_index]) { records[cur_index].clear(); - LOG_PRINT_L2("DNSSEC not available for checkpoint update at URL: " << url << ", skipping."); + LOG_PRINT_L2("DNSSEC not available for hostname: " << url << ", skipping."); } if (!valid[cur_index]) { records[cur_index].clear(); - LOG_PRINT_L2("DNSSEC validation failed for checkpoint update at URL: " << url << ", skipping."); + LOG_PRINT_L2("DNSSEC validation failed for hostname: " << url << ", skipping."); } cur_index++; @@ -541,7 +568,7 @@ bool load_txt_records_from_dns(std::vector<std::string> &good_records, const std if (num_valid_records < 2) { - LOG_PRINT_L0("WARNING: no two valid MoneroPulse DNS checkpoint records were received"); + LOG_PRINT_L0("WARNING: no two valid DNS TXT records were received"); return false; } @@ -563,7 +590,7 @@ bool load_txt_records_from_dns(std::vector<std::string> &good_records, const std if (good_records_index < 0) { - LOG_PRINT_L0("WARNING: no two MoneroPulse DNS checkpoint records matched"); + LOG_PRINT_L0("WARNING: no two DNS TXT records matched"); return false; } diff --git a/src/common/dns_utils.h b/src/common/dns_utils.h index 3a6ef68a1..a6bc7463a 100644 --- a/src/common/dns_utils.h +++ b/src/common/dns_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/download.cpp b/src/common/download.cpp index 7c38cfa5b..f07d6798d 100644 --- a/src/common/download.cpp +++ b/src/common/download.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/download.h b/src/common/download.h index 3097394bc..f8656a59c 100644 --- a/src/common/download.h +++ b/src/common/download.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/http_connection.h b/src/common/http_connection.h index 554dd832b..6b4294802 100644 --- a/src/common/http_connection.h +++ b/src/common/http_connection.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/i18n.cpp b/src/common/i18n.cpp index a32875945..9ac347263 100644 --- a/src/common/i18n.cpp +++ b/src/common/i18n.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/i18n.h b/src/common/i18n.h index d21d00275..82a07410d 100644 --- a/src/common/i18n.h +++ b/src/common/i18n.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/json_util.h b/src/common/json_util.h index c320c3956..96f4b90e6 100644 --- a/src/common/json_util.h +++ b/src/common/json_util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/password.cpp b/src/common/password.cpp index 5f5cb800a..03d13db42 100644 --- a/src/common/password.cpp +++ b/src/common/password.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/password.h b/src/common/password.h index beb98283b..2837c70f3 100644 --- a/src/common/password.h +++ b/src/common/password.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/perf_timer.cpp b/src/common/perf_timer.cpp index 3e1357833..dda498088 100644 --- a/src/common/perf_timer.cpp +++ b/src/common/perf_timer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/perf_timer.h b/src/common/perf_timer.h index 5203da205..717391623 100644 --- a/src/common/perf_timer.h +++ b/src/common/perf_timer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/pod-class.h b/src/common/pod-class.h index 5f6709eef..200647590 100644 --- a/src/common/pod-class.h +++ b/src/common/pod-class.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/rpc_client.h b/src/common/rpc_client.h index 9665966ae..cb5f79da8 100644 --- a/src/common/rpc_client.h +++ b/src/common/rpc_client.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/scoped_message_writer.h b/src/common/scoped_message_writer.h index 42f439ad8..546377392 100644 --- a/src/common/scoped_message_writer.h +++ b/src/common/scoped_message_writer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/sfinae_helpers.h b/src/common/sfinae_helpers.h index fa5052a2e..e9a98bb63 100644 --- a/src/common/sfinae_helpers.h +++ b/src/common/sfinae_helpers.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/spawn.cpp b/src/common/spawn.cpp index e03552f8c..9a7e75d41 100644 --- a/src/common/spawn.cpp +++ b/src/common/spawn.cpp @@ -91,7 +91,7 @@ int spawn(const char *filename, const std::vector<std::string>& args, bool wait) MINFO("Child exited with " << exitCode); return static_cast<int>(exitCode); #else - char **argv = (char**)alloca(sizeof(char*) * (args.size() + 1)); + std::vector<char*> argv(args.size() + 1); for (size_t n = 0; n < args.size(); ++n) argv[n] = (char*)args[n].c_str(); argv[args.size()] = NULL; @@ -109,7 +109,7 @@ int spawn(const char *filename, const std::vector<std::string>& args, bool wait) tools::closefrom(3); close(0); char *envp[] = {NULL}; - execve(filename, argv, envp); + execve(filename, argv.data(), envp); MERROR("Failed to execve: " << strerror(errno)); return -1; } diff --git a/src/common/stack_trace.cpp b/src/common/stack_trace.cpp index 141621427..8d4f8c6f1 100644 --- a/src/common/stack_trace.cpp +++ b/src/common/stack_trace.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/stack_trace.h b/src/common/stack_trace.h index 272fb89ae..ae6573885 100644 --- a/src/common/stack_trace.h +++ b/src/common/stack_trace.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp index cbf7163c5..2748c798c 100644 --- a/src/common/threadpool.cpp +++ b/src/common/threadpool.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/threadpool.h b/src/common/threadpool.h index a43e38a76..5e490ee7d 100644 --- a/src/common/threadpool.h +++ b/src/common/threadpool.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/timings.cc b/src/common/timings.cc index cb8deff2a..612ac2cc6 100644 --- a/src/common/timings.cc +++ b/src/common/timings.cc @@ -1,5 +1,5 @@ #include <string.h> -#include <error.h> +#include <errno.h> #include <time.h> #include <algorithm> #include <boost/algorithm/string.hpp> diff --git a/src/common/unordered_containers_boost_serialization.h b/src/common/unordered_containers_boost_serialization.h index d78dc6a30..74e2c3f81 100644 --- a/src/common/unordered_containers_boost_serialization.h +++ b/src/common/unordered_containers_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/updates.cpp b/src/common/updates.cpp index 9f12f8dbc..0bc6ff63c 100644 --- a/src/common/updates.cpp +++ b/src/common/updates.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/updates.h b/src/common/updates.h index 6ec22f183..8fda6d207 100644 --- a/src/common/updates.h +++ b/src/common/updates.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/util.cpp b/src/common/util.cpp index 28745eea4..80b8a9e81 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/util.h b/src/common/util.h index d5aca15d1..ef2305bf4 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/common/varint.h b/src/common/varint.h index 904255afc..a0d79be28 100644 --- a/src/common/varint.h +++ b/src/common/varint.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt index 6e774b8d5..d22d59b36 100644 --- a/src/crypto/CMakeLists.txt +++ b/src/crypto/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # @@ -39,6 +39,7 @@ set(crypto_sources hash-extra-jh.c hash-extra-skein.c hash.c + hmac-keccak.c jh.c keccak.c oaes_lib.c @@ -64,6 +65,7 @@ set(crypto_private_headers groestl_tables.h hash-ops.h hash.h + hmac-keccak.h initializer.h jh.h keccak.h diff --git a/src/crypto/CryptonightR_JIT.c b/src/crypto/CryptonightR_JIT.c index 68258a959..ee8f3f36f 100644 --- a/src/crypto/CryptonightR_JIT.c +++ b/src/crypto/CryptonightR_JIT.c @@ -4,6 +4,9 @@ #include <string.h> #include <stdio.h> #include <unistd.h> +#if !(defined(_MSC_VER) || defined(__MINGW32__)) +#include <sys/mman.h> +#endif #include "int-util.h" #include "hash-ops.h" @@ -58,6 +61,11 @@ int v4_generate_JIT_code(const struct V4_Instruction* code, v4_random_math_JIT_f uint8_t* JIT_code = (uint8_t*) buf; const uint8_t* JIT_code_end = JIT_code + buf_size; +#if !(defined(_MSC_VER) || defined(__MINGW32__)) + if (mprotect((void*)buf, buf_size, PROT_READ | PROT_WRITE)) + return 1; +#endif + APPEND_CODE(prologue, sizeof(prologue)); uint32_t prev_rot_src = 0xFFFFFFFFU; @@ -101,6 +109,11 @@ int v4_generate_JIT_code(const struct V4_Instruction* code, v4_random_math_JIT_f APPEND_CODE(epilogue, sizeof(epilogue)); +#if !(defined(_MSC_VER) || defined(__MINGW32__)) + if (mprotect((void*)buf, buf_size, PROT_READ | PROT_EXEC)) + return 1; +#endif + __builtin___clear_cache((char*)buf, (char*)JIT_code); return 0; diff --git a/src/crypto/aesb.c b/src/crypto/aesb.c index efdeef8d1..6d4905ad5 100644 --- a/src/crypto/aesb.c +++ b/src/crypto/aesb.c @@ -33,11 +33,11 @@ extern "C" #define RC_LENGTH (5 * (AES_BLOCK_SIZE / 4 - 2)) #if defined(_MSC_VER) -#define ALIGN __declspec(align(TABLE_ALIGN)) +#define LOCAL_ALIGN __declspec(align(TABLE_ALIGN)) #elif defined(__GNUC__) -#define ALIGN __attribute__ ((aligned(16))) +#define LOCAL_ALIGN __attribute__ ((aligned(16))) #else -#define ALIGN +#define LOCAL_ALIGN #endif #define rf1(r,c) (r) @@ -131,7 +131,7 @@ extern "C" #define t_set(m,n) t_##m##n #define t_use(m,n) t_##m##n -#define d_4(t,n,b,e,f,g,h) ALIGN const t n[4][256] = { b(e), b(f), b(g), b(h) } +#define d_4(t,n,b,e,f,g,h) LOCAL_ALIGN const t n[4][256] = { b(e), b(f), b(g), b(h) } #define four_tables(x,tab,vf,rf,c) \ (tab[0][bval(vf(x,0,c),rf(0,c))] \ diff --git a/src/crypto/blake256.c b/src/crypto/blake256.c index 6ef7d4207..1e305b3a6 100644 --- a/src/crypto/blake256.c +++ b/src/crypto/blake256.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/blake256.h b/src/crypto/blake256.h index 073772289..309dbf3ec 100644 --- a/src/crypto/blake256.h +++ b/src/crypto/blake256.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/chacha.h b/src/crypto/chacha.h index 0610f7051..a39823e5a 100644 --- a/src/crypto/chacha.h +++ b/src/crypto/chacha.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto-ops-data.c b/src/crypto/crypto-ops-data.c index 1f77513ca..c9530bb2a 100644 --- a/src/crypto/crypto-ops-data.c +++ b/src/crypto/crypto-ops-data.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c index 09296d6f9..5a3d994a6 100644 --- a/src/crypto/crypto-ops.c +++ b/src/crypto/crypto-ops.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto-ops.h b/src/crypto/crypto-ops.h index 2910dafd4..7137437bc 100644 --- a/src/crypto/crypto-ops.h +++ b/src/crypto/crypto-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index ddf072f68..3f06c4f3f 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index f22df1230..22b182ab0 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/crypto_ops_builder/README.md b/src/crypto/crypto_ops_builder/README.md index 326d2ca6e..4bb95cc4a 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-2018, The Monero Project +Copyright (c) 2014-2019, 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 127e3e17b..45e9923b1 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-2018, The Monero Project +// Copyright (c) 2014-2019, 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 9097bf95b..89c2ced6e 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-2018, The Monero Project +// Copyright (c) 2014-2019, 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 9337b56b7..b4fcfca9c 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-2018, The Monero Project +// Copyright (c) 2014-2019, 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 0ed97d5f4..dfba583f7 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-2018, The Monero Project + // Copyright (c) 2014-2019, 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 c06af035f..f62ff441d 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/generic-ops.h b/src/crypto/generic-ops.h index 42b98706e..d06726638 100644 --- a/src/crypto/generic-ops.h +++ b/src/crypto/generic-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/groestl.h b/src/crypto/groestl.h index 19837f309..6628947dd 100644 --- a/src/crypto/groestl.h +++ b/src/crypto/groestl.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/groestl_tables.h b/src/crypto/groestl_tables.h index 12472dced..ca0c4fca6 100644 --- a/src/crypto/groestl_tables.h +++ b/src/crypto/groestl_tables.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-extra-blake.c b/src/crypto/hash-extra-blake.c index d33103c97..9bada96f3 100644 --- a/src/crypto/hash-extra-blake.c +++ b/src/crypto/hash-extra-blake.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-extra-groestl.c b/src/crypto/hash-extra-groestl.c index 228853a44..57866bf9d 100644 --- a/src/crypto/hash-extra-groestl.c +++ b/src/crypto/hash-extra-groestl.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-extra-jh.c b/src/crypto/hash-extra-jh.c index e765a18f3..0dbac4fb5 100644 --- a/src/crypto/hash-extra-jh.c +++ b/src/crypto/hash-extra-jh.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-extra-skein.c b/src/crypto/hash-extra-skein.c index 06d8f87cc..78f48609f 100644 --- a/src/crypto/hash-extra-skein.c +++ b/src/crypto/hash-extra-skein.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index ba7ece0f5..859c810bd 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash.c b/src/crypto/hash.c index 43ce32957..b66f3b010 100644 --- a/src/crypto/hash.c +++ b/src/crypto/hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hash.h b/src/crypto/hash.h index 165fe6bb0..17071923d 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/hmac-keccak.c b/src/crypto/hmac-keccak.c new file mode 100644 index 000000000..edcb2065e --- /dev/null +++ b/src/crypto/hmac-keccak.c @@ -0,0 +1,81 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "hmac-keccak.h" +#include "memwipe.h" + +#define KECCAK_BLOCKLEN 136 +#define HASH_SIZE 32 + +void hmac_keccak_init(hmac_keccak_state *S, const uint8_t *_key, size_t keylen) { + const uint8_t *key = _key; + uint8_t keyhash[HASH_SIZE]; + uint8_t pad[KECCAK_BLOCKLEN]; + uint64_t i; + + if (keylen > KECCAK_BLOCKLEN) { + keccak(key, keylen, keyhash, HASH_SIZE); + key = keyhash; + keylen = HASH_SIZE; + } + + keccak_init(&S->inner); + memset(pad, 0x36, KECCAK_BLOCKLEN); + for (i = 0; i < keylen; ++i) { + pad[i] ^= key[i]; + } + keccak_update(&S->inner, pad, KECCAK_BLOCKLEN); + + keccak_init(&S->outer); + memset(pad, 0x5c, KECCAK_BLOCKLEN); + for (i = 0; i < keylen; ++i) { + pad[i] ^= key[i]; + } + keccak_update(&S->outer, pad, KECCAK_BLOCKLEN); + + memwipe(keyhash, HASH_SIZE); +} + +void hmac_keccak_update(hmac_keccak_state *S, const uint8_t *data, size_t datalen) { + keccak_update(&S->inner, data, datalen); +} + +void hmac_keccak_finish(hmac_keccak_state *S, uint8_t *digest) { + uint8_t ihash[HASH_SIZE]; + keccak_finish(&S->inner, ihash); + keccak_update(&S->outer, ihash, HASH_SIZE); + keccak_finish(&S->outer, digest); + memwipe(ihash, HASH_SIZE); +} + +void hmac_keccak_hash(uint8_t *out, const uint8_t *key, size_t keylen, const uint8_t *in, size_t inlen) { + hmac_keccak_state S; + hmac_keccak_init(&S, key, keylen); + hmac_keccak_update(&S, in, inlen); + hmac_keccak_finish(&S, out); +} diff --git a/src/crypto/hmac-keccak.h b/src/crypto/hmac-keccak.h new file mode 100644 index 000000000..c450860d4 --- /dev/null +++ b/src/crypto/hmac-keccak.h @@ -0,0 +1,59 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef HMAC_KECCAK_H +#define HMAC_KECCAK_H + +#include "keccak.h" + +// HMAC RFC 2104 with Keccak-256 base hash function +// +// B = KECCAK_BLOCKLEN = 136 B +// L = HASH_SIZE = 32 B +// +// Note this is not HMAC-SHA3 as SHA3 and Keccak differs in +// the padding constant. + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + KECCAK_CTX inner; + KECCAK_CTX outer; +} hmac_keccak_state; + +void hmac_keccak_init(hmac_keccak_state *S, const uint8_t *_key, size_t keylen); +void hmac_keccak_update(hmac_keccak_state *S, const uint8_t *data, size_t datalen); +void hmac_keccak_finish(hmac_keccak_state *S, uint8_t *digest); +void hmac_keccak_hash(uint8_t *out, const uint8_t *key, size_t keylen, const uint8_t *in, size_t inlen); + +#ifdef __cplusplus +} +#endif +#endif //HMAC_KECCAK_H diff --git a/src/crypto/initializer.h b/src/crypto/initializer.h index 107988d2b..75d80f054 100644 --- a/src/crypto/initializer.h +++ b/src/crypto/initializer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/random.c b/src/crypto/random.c index 9e1a70a2d..74b202661 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/random.h b/src/crypto/random.h index 6468136cc..ccb9f4853 100644 --- a/src/crypto/random.h +++ b/src/crypto/random.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/skein_port.h b/src/crypto/skein_port.h index 8a1640e57..1ec07a4d1 100644 --- a/src/crypto/skein_port.h +++ b/src/crypto/skein_port.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 6bdc1b28c..7f36c9dc3 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -76,7 +76,7 @@ static inline int use_v4_jit(void) const char *env = getenv("MONERO_USE_CNV4_JIT"); if (!env) { - use_v4_jit_flag = 0; + use_v4_jit_flag = 1; } else if (!strcmp(env, "0") || !strcmp(env, "no")) { use_v4_jit_flag = 0; @@ -274,10 +274,10 @@ static inline int use_v4_jit(void) #define VARIANT2_2() \ do if (variant == 2 || variant == 3) \ { \ - *U64(hp_state + (j ^ 0x10)) ^= SWAP64LE(hi); \ - *(U64(hp_state + (j ^ 0x10)) + 1) ^= SWAP64LE(lo); \ - hi ^= SWAP64LE(*U64(hp_state + (j ^ 0x20))); \ - lo ^= SWAP64LE(*(U64(hp_state + (j ^ 0x20)) + 1)); \ + *U64(local_hp_state + (j ^ 0x10)) ^= SWAP64LE(hi); \ + *(U64(local_hp_state + (j ^ 0x10)) + 1) ^= SWAP64LE(lo); \ + hi ^= SWAP64LE(*U64(local_hp_state + (j ^ 0x20))); \ + lo ^= SWAP64LE(*(U64(local_hp_state + (j ^ 0x20)) + 1)); \ } while (0) #define V4_REG_LOAD(dst, src) \ @@ -405,7 +405,7 @@ static inline int use_v4_jit(void) #define pre_aes() \ j = state_index(a); \ - _c = _mm_load_si128(R128(&hp_state[j])); \ + _c = _mm_load_si128(R128(&local_hp_state[j])); \ _a = _mm_load_si128(R128(a)); \ /* @@ -418,20 +418,20 @@ static inline int use_v4_jit(void) * This code is based upon an optimized implementation by dga. */ #define post_aes() \ - VARIANT2_SHUFFLE_ADD_SSE2(hp_state, j); \ + VARIANT2_SHUFFLE_ADD_SSE2(local_hp_state, j); \ _mm_store_si128(R128(c), _c); \ - _mm_store_si128(R128(&hp_state[j]), _mm_xor_si128(_b, _c)); \ - VARIANT1_1(&hp_state[j]); \ + _mm_store_si128(R128(&local_hp_state[j]), _mm_xor_si128(_b, _c)); \ + VARIANT1_1(&local_hp_state[j]); \ j = state_index(c); \ - p = U64(&hp_state[j]); \ + p = U64(&local_hp_state[j]); \ b[0] = p[0]; b[1] = p[1]; \ VARIANT2_INTEGER_MATH_SSE2(b, c); \ VARIANT4_RANDOM_MATH(a, b, r, &_b, &_b1); \ __mul(); \ VARIANT2_2(); \ - VARIANT2_SHUFFLE_ADD_SSE2(hp_state, j); \ + VARIANT2_SHUFFLE_ADD_SSE2(local_hp_state, j); \ a[0] += hi; a[1] += lo; \ - p = U64(&hp_state[j]); \ + p = U64(&local_hp_state[j]); \ p[0] = a[0]; p[1] = a[1]; \ a[0] ^= b[0]; a[1] ^= b[1]; \ VARIANT1_2(p + 1); \ @@ -756,10 +756,10 @@ void slow_hash_allocate_state(void) #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ defined(__DragonFly__) || defined(__NetBSD__) hp_state = mmap(0, MEMORY, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON, 0, 0); + MAP_PRIVATE | MAP_ANON, -1, 0); #else hp_state = mmap(0, MEMORY, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, 0, 0); + MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0); #endif if(hp_state == MAP_FAILED) hp_state = NULL; @@ -778,11 +778,16 @@ void slow_hash_allocate_state(void) #else #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ defined(__DragonFly__) || defined(__NetBSD__) - hp_jitfunc_memory = mmap(0, 4096 + 4095, PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANON, 0, 0); +#ifdef __NetBSD__ +#define RESERVED_FLAGS PROT_MPROTECT(PROT_EXEC) +#else +#define RESERVED_FLAGS 0 +#endif + hp_jitfunc_memory = mmap(0, 4096 + 4096, PROT_READ | PROT_WRITE | RESERVED_FLAGS, + MAP_PRIVATE | MAP_ANON, -1, 0); #else - hp_jitfunc_memory = mmap(0, 4096 + 4095, PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + hp_jitfunc_memory = mmap(0, 4096 + 4096, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); #endif if(hp_jitfunc_memory == MAP_FAILED) hp_jitfunc_memory = NULL; @@ -794,9 +799,6 @@ void slow_hash_allocate_state(void) hp_jitfunc_memory = malloc(4096 + 4095); } hp_jitfunc = (v4_random_math_JIT_func)((size_t)(hp_jitfunc_memory + 4095) & ~4095); -#if !(defined(_MSC_VER) || defined(__MINGW32__)) - mprotect(hp_jitfunc, 4096, PROT_READ | PROT_WRITE | PROT_EXEC); -#endif } /** @@ -893,6 +895,10 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int if(hp_state == NULL) slow_hash_allocate_state(); + // locals to avoid constant TLS dereferencing + uint8_t *local_hp_state = hp_state; + v4_random_math_JIT_func local_hp_jitfunc = hp_jitfunc; + /* CryptoNight Step 1: Use Keccak1600 to initialize the 'state' (and 'text') buffers from the data. */ if (prehashed) { memcpy(&state.hs, data, length); @@ -915,7 +921,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int for(i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) { aes_pseudo_round(text, text, expandedKey, INIT_SIZE_BLK); - memcpy(&hp_state[i * INIT_SIZE_BYTE], text, INIT_SIZE_BYTE); + memcpy(&local_hp_state[i * INIT_SIZE_BYTE], text, INIT_SIZE_BYTE); } } else @@ -927,7 +933,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int for(j = 0; j < INIT_SIZE_BLK; j++) aesb_pseudo_round(&text[AES_BLOCK_SIZE * j], &text[AES_BLOCK_SIZE * j], aes_ctx->key->exp_data); - memcpy(&hp_state[i * INIT_SIZE_BYTE], text, INIT_SIZE_BYTE); + memcpy(&local_hp_state[i * INIT_SIZE_BYTE], text, INIT_SIZE_BYTE); } } @@ -975,7 +981,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int for(i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) { // add the xor to the pseudo round - aes_pseudo_round_xor(text, text, expandedKey, &hp_state[i * INIT_SIZE_BYTE], INIT_SIZE_BLK); + aes_pseudo_round_xor(text, text, expandedKey, &local_hp_state[i * INIT_SIZE_BYTE], INIT_SIZE_BLK); } } else @@ -985,7 +991,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int { for(j = 0; j < INIT_SIZE_BLK; j++) { - xor_blocks(&text[j * AES_BLOCK_SIZE], &hp_state[i * INIT_SIZE_BYTE + j * AES_BLOCK_SIZE]); + xor_blocks(&text[j * AES_BLOCK_SIZE], &local_hp_state[i * INIT_SIZE_BYTE + j * AES_BLOCK_SIZE]); aesb_pseudo_round(&text[AES_BLOCK_SIZE * j], &text[AES_BLOCK_SIZE * j], aes_ctx->key->exp_data); } } @@ -1065,24 +1071,24 @@ union cn_slow_hash_state #define pre_aes() \ j = state_index(a); \ - _c = vld1q_u8(&hp_state[j]); \ + _c = vld1q_u8(&local_hp_state[j]); \ _a = vld1q_u8((const uint8_t *)a); \ #define post_aes() \ - VARIANT2_SHUFFLE_ADD_NEON(hp_state, j); \ + VARIANT2_SHUFFLE_ADD_NEON(local_hp_state, j); \ vst1q_u8((uint8_t *)c, _c); \ - vst1q_u8(&hp_state[j], veorq_u8(_b, _c)); \ - VARIANT1_1(&hp_state[j]); \ + vst1q_u8(&local_hp_state[j], veorq_u8(_b, _c)); \ + VARIANT1_1(&local_hp_state[j]); \ j = state_index(c); \ - p = U64(&hp_state[j]); \ + p = U64(&local_hp_state[j]); \ b[0] = p[0]; b[1] = p[1]; \ VARIANT2_PORTABLE_INTEGER_MATH(b, c); \ VARIANT4_RANDOM_MATH(a, b, r, &_b, &_b1); \ __mul(); \ VARIANT2_2(); \ - VARIANT2_SHUFFLE_ADD_NEON(hp_state, j); \ + VARIANT2_SHUFFLE_ADD_NEON(local_hp_state, j); \ a[0] += hi; a[1] += lo; \ - p = U64(&hp_state[j]); \ + p = U64(&local_hp_state[j]); \ p[0] = a[0]; p[1] = a[1]; \ a[0] ^= b[0]; a[1] ^= b[1]; \ VARIANT1_2(p + 1); \ @@ -1245,9 +1251,9 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int RDATA_ALIGN16 uint8_t expandedKey[240]; #ifndef FORCE_USE_HEAP - RDATA_ALIGN16 uint8_t hp_state[MEMORY]; + RDATA_ALIGN16 uint8_t local_hp_state[MEMORY]; #else - uint8_t *hp_state = (uint8_t *)aligned_malloc(MEMORY,16); + uint8_t *local_hp_state = (uint8_t *)aligned_malloc(MEMORY,16); #endif uint8_t text[INIT_SIZE_BYTE]; @@ -1287,7 +1293,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int for(i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) { aes_pseudo_round(text, text, expandedKey, INIT_SIZE_BLK); - memcpy(&hp_state[i * INIT_SIZE_BYTE], text, INIT_SIZE_BYTE); + memcpy(&local_hp_state[i * INIT_SIZE_BYTE], text, INIT_SIZE_BYTE); } U64(a)[0] = U64(&state.k[0])[0] ^ U64(&state.k[32])[0]; @@ -1322,7 +1328,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int for(i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) { // add the xor to the pseudo round - aes_pseudo_round_xor(text, text, expandedKey, &hp_state[i * INIT_SIZE_BYTE], INIT_SIZE_BLK); + aes_pseudo_round_xor(text, text, expandedKey, &local_hp_state[i * INIT_SIZE_BYTE], INIT_SIZE_BLK); } /* CryptoNight Step 5: Apply Keccak to the state again, and then @@ -1337,7 +1343,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int extra_hashes[state.hs.b[0] & 3](&state, 200, hash); #ifdef FORCE_USE_HEAP - aligned_free(hp_state); + aligned_free(local_hp_state); #endif } #else /* aarch64 && crypto */ diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c index b2dc3ffb2..7802fb67f 100644 --- a/src/crypto/tree-hash.c +++ b/src/crypto/tree-hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -34,15 +34,6 @@ #include "hash-ops.h" -#ifdef _MSC_VER -#include <malloc.h> -#elif !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__) \ - && !defined(__NetBSD__) - #include <alloca.h> -#else - #include <stdlib.h> -#endif - /*** * Round to power of two, for count>=3 and for count being not too large (as reasonable for tree hash calculations) */ @@ -91,9 +82,8 @@ void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash) { size_t cnt = tree_hash_cnt( count ); - char (*ints)[HASH_SIZE]; - size_t ints_size = cnt * HASH_SIZE; - ints = alloca(ints_size); memset( ints , 0 , ints_size); // allocate, and zero out as extra protection for using uninitialized mem + char ints[cnt][HASH_SIZE]; + memset(ints, 0 , sizeof(ints)); // zero out as extra protection for using uninitialized mem memcpy(ints, hashes, (2 * cnt - count) * HASH_SIZE); diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt index 21445959d..5bb56e083 100644 --- a/src/cryptonote_basic/CMakeLists.txt +++ b/src/cryptonote_basic/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp index edbc2c561..a9aec163b 100644 --- a/src/cryptonote_basic/account.cpp +++ b/src/cryptonote_basic/account.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h index 021f84029..abf751b6e 100644 --- a/src/cryptonote_basic/account.h +++ b/src/cryptonote_basic/account.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/account_boost_serialization.h b/src/cryptonote_basic/account_boost_serialization.h index 7379d787f..320a960dc 100644 --- a/src/cryptonote_basic/account_boost_serialization.h +++ b/src/cryptonote_basic/account_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/blobdatatype.h b/src/cryptonote_basic/blobdatatype.h index 82484c0a8..20f6b2421 100644 --- a/src/cryptonote_basic/blobdatatype.h +++ b/src/cryptonote_basic/blobdatatype.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/connection_context.h b/src/cryptonote_basic/connection_context.h index 112c13049..96398a90b 100644 --- a/src/cryptonote_basic/connection_context.h +++ b/src/cryptonote_basic/connection_context.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -40,7 +40,8 @@ namespace cryptonote struct cryptonote_connection_context: public epee::net_utils::connection_context_base { cryptonote_connection_context(): m_state(state_before_handshake), m_remote_blockchain_height(0), m_last_response_height(0), - m_last_request_time(boost::date_time::not_a_date_time), m_callback_request_count(0), m_last_known_hash(crypto::null_hash), m_pruning_seed(0), m_anchor(false) {} + m_last_request_time(boost::date_time::not_a_date_time), m_callback_request_count(0), + m_last_known_hash(crypto::null_hash), m_pruning_seed(0), m_rpc_port(0), m_anchor(false) {} enum state { @@ -60,6 +61,7 @@ namespace cryptonote epee::copyable_atomic m_callback_request_count; //in debug purpose: problem with double callback rise crypto::hash m_last_known_hash; uint32_t m_pruning_seed; + uint16_t m_rpc_port; bool m_anchor; //size_t m_score; TODO: add score calculations }; diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h index 172a99e84..03caafbb0 100644 --- a/src/cryptonote_basic/cryptonote_basic.h +++ b/src/cryptonote_basic/cryptonote_basic.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 23a5bd5bd..e336cc1d1 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.cpp +++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 0b8131a7a..036273f0e 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.h +++ b/src/cryptonote_basic/cryptonote_basic_impl.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 d1e321994..4ce9f3eeb 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 f4e7c9d36..094057b1f 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -282,6 +282,11 @@ namespace cryptonote //--------------------------------------------------------------- bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki, hw::device &hwdev) { + if (hwdev.compute_key_image(ack, out_key, recv_derivation, real_output_index, received_index, in_ephemeral, ki)) + { + return true; + } + if (ack.m_spend_secret_key == crypto::null_skey) { // for watch-only wallet, simply copy the known output pubkey @@ -396,6 +401,7 @@ namespace cryptonote for (const auto &bp: rv.p.bulletproofs) nlr += bp.L.size() * 2; const size_t bp_size = 32 * (9 + nlr); + CHECK_AND_ASSERT_THROW_MES_L1(n_outputs <= BULLETPROOF_MAX_OUTPUTS, "maximum number of outputs is " + std::to_string(BULLETPROOF_MAX_OUTPUTS) + " per transaction"); CHECK_AND_ASSERT_THROW_MES_L1(bp_base * n_padded_outputs >= bp_size, "Invalid bulletproof clawback"); const uint64_t bp_clawback = (bp_base * n_padded_outputs - bp_size) * 4 / 5; CHECK_AND_ASSERT_THROW_MES_L1(bp_clawback <= std::numeric_limits<uint64_t>::max() - blob_size, "Weight overflow"); @@ -981,15 +987,11 @@ namespace cryptonote { if (t.version == 1) return false; - static const crypto::hash empty_hash = { (char)0x70, (char)0xa4, (char)0x85, (char)0x5d, (char)0x04, (char)0xd8, (char)0xfa, (char)0x7b, (char)0x3b, (char)0x27, (char)0x82, (char)0xca, (char)0x53, (char)0xb6, (char)0x00, (char)0xe5, (char)0xc0, (char)0x03, (char)0xc7, (char)0xdc, (char)0xb2, (char)0x7d, (char)0x7e, (char)0x92, (char)0x3c, (char)0x23, (char)0xf7, (char)0x86, (char)0x01, (char)0x46, (char)0xd2, (char)0xc5 }; const unsigned int unprunable_size = t.unprunable_size; if (blob && unprunable_size) { CHECK_AND_ASSERT_MES(unprunable_size <= blob->size(), false, "Inconsistent transaction unprunable and blob sizes"); - if (blob->size() - unprunable_size == 0) - res = empty_hash; - else - cryptonote::get_blob_hash(epee::span<const char>(blob->data() + unprunable_size, blob->size() - unprunable_size), res); + cryptonote::get_blob_hash(epee::span<const char>(blob->data() + unprunable_size, blob->size() - unprunable_size), res); } else { @@ -1001,10 +1003,7 @@ namespace cryptonote const size_t mixin = t.vin.empty() ? 0 : t.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(t.vin[0]).key_offsets.size() - 1 : 0; bool r = tt.rct_signatures.p.serialize_rctsig_prunable(ba, t.rct_signatures.type, inputs, outputs, mixin); CHECK_AND_ASSERT_MES(r, false, "Failed to serialize rct signatures prunable"); - if (ss.str().empty()) - res = empty_hash; - else - cryptonote::get_blob_hash(ss.str(), res); + cryptonote::get_blob_hash(ss.str(), res); } return true; } @@ -1041,7 +1040,10 @@ namespace cryptonote } // prunable rct - hashes[2] = pruned_data_hash; + if (t.rct_signatures.type == rct::RCTTypeNull) + hashes[2] = crypto::null_hash; + else + hashes[2] = pruned_data_hash; // the tx hash is the hash of the 3 hashes crypto::hash res = cn_fast_hash(hashes, sizeof(hashes)); diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h index 92dbdbff0..40a9907be 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.h +++ b/src/cryptonote_basic/cryptonote_format_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/cryptonote_stat_info.h b/src/cryptonote_basic/cryptonote_stat_info.h index a09fa850b..4cc1bc764 100644 --- a/src/cryptonote_basic/cryptonote_stat_info.h +++ b/src/cryptonote_basic/cryptonote_stat_info.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/difficulty.cpp b/src/cryptonote_basic/difficulty.cpp index 55e3e93b3..89b748a83 100644 --- a/src/cryptonote_basic/difficulty.cpp +++ b/src/cryptonote_basic/difficulty.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/difficulty.h b/src/cryptonote_basic/difficulty.h index b06538467..8da355b22 100644 --- a/src/cryptonote_basic/difficulty.h +++ b/src/cryptonote_basic/difficulty.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/hardfork.cpp b/src/cryptonote_basic/hardfork.cpp index d1d836fcb..89bca2f09 100644 --- a/src/cryptonote_basic/hardfork.cpp +++ b/src/cryptonote_basic/hardfork.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -317,9 +317,12 @@ void HardFork::on_block_popped(uint64_t nblocks) uint64_t height; for (height = old_chain_height - 1; height >= new_chain_height; --height) { + version = versions.back(); + last_versions[version]--; versions.pop_back(); version = db.get_hard_fork_version(height); versions.push_front(version); + last_versions[version]++; } // does not take voting into account diff --git a/src/cryptonote_basic/hardfork.h b/src/cryptonote_basic/hardfork.h index a3fc25dfa..123978b12 100644 --- a/src/cryptonote_basic/hardfork.h +++ b/src/cryptonote_basic/hardfork.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index 3a51c6ea4..6628c8448 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -119,7 +119,8 @@ namespace cryptonote m_min_idle_seconds(BACKGROUND_MINING_DEFAULT_MIN_IDLE_INTERVAL_IN_SECONDS), m_idle_threshold(BACKGROUND_MINING_DEFAULT_IDLE_THRESHOLD_PERCENTAGE), m_mining_target(BACKGROUND_MINING_DEFAULT_MINING_TARGET_PERCENTAGE), - m_miner_extra_sleep(BACKGROUND_MINING_DEFAULT_MINER_EXTRA_SLEEP_MILLIS) + m_miner_extra_sleep(BACKGROUND_MINING_DEFAULT_MINER_EXTRA_SLEEP_MILLIS), + m_block_reward(0) { } @@ -130,12 +131,13 @@ namespace cryptonote catch (...) { /* ignore */ } } //----------------------------------------------------------------------------------------------------- - bool miner::set_block_template(const block& bl, const difficulty_type& di, uint64_t height) + bool miner::set_block_template(const block& bl, const difficulty_type& di, uint64_t height, uint64_t block_reward) { CRITICAL_REGION_LOCAL(m_template_lock); m_template = bl; m_diffic = di; m_height = height; + m_block_reward = block_reward; ++m_template_no; m_starter_nonce = crypto::rand<uint32_t>(); return true; @@ -167,7 +169,7 @@ namespace cryptonote LOG_ERROR("Failed to get_block_template(), stopping mining"); return false; } - set_block_template(bl, di, height); + set_block_template(bl, di, height, expected_reward); return true; } //----------------------------------------------------------------------------------------------------- @@ -359,6 +361,7 @@ namespace cryptonote //----------------------------------------------------------------------------------------------------- bool miner::start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs, bool do_background, bool ignore_battery) { + m_block_reward = 0; m_mine_address = adr; m_threads_total = static_cast<uint32_t>(threads_count); if (threads_count == 0) diff --git a/src/cryptonote_basic/miner.h b/src/cryptonote_basic/miner.h index d4cdb2363..08b1bd7f1 100644 --- a/src/cryptonote_basic/miner.h +++ b/src/cryptonote_basic/miner.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -61,7 +61,7 @@ namespace cryptonote ~miner(); bool init(const boost::program_options::variables_map& vm, network_type nettype); static void init_options(boost::program_options::options_description& desc); - bool set_block_template(const block& bl, const difficulty_type& diffic, uint64_t height); + bool set_block_template(const block& bl, const difficulty_type& diffic, uint64_t height, uint64_t block_reward); bool on_block_chain_update(); bool start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs, bool do_background = false, bool ignore_battery = false); uint64_t get_speed() const; @@ -85,6 +85,7 @@ namespace cryptonote bool set_idle_threshold(uint8_t idle_threshold); uint8_t get_mining_target() const; bool set_mining_target(uint8_t mining_target); + uint64_t get_block_reward() const { return m_block_reward; } static constexpr uint8_t BACKGROUND_MINING_DEFAULT_IDLE_THRESHOLD_PERCENTAGE = 90; static constexpr uint8_t BACKGROUND_MINING_MIN_IDLE_THRESHOLD_PERCENTAGE = 50; @@ -169,5 +170,6 @@ namespace cryptonote static bool get_process_time(uint64_t& total_time); static uint8_t get_percent_of_total(uint64_t some_time, uint64_t total_time); static boost::logic::tribool on_battery_power(); + std::atomic<uint64_t> m_block_reward; }; } diff --git a/src/cryptonote_basic/subaddress_index.h b/src/cryptonote_basic/subaddress_index.h index 9b71448f9..99933e229 100644 --- a/src/cryptonote_basic/subaddress_index.h +++ b/src/cryptonote_basic/subaddress_index.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/tx_extra.h b/src/cryptonote_basic/tx_extra.h index 009e35ebe..ecb4c6040 100644 --- a/src/cryptonote_basic/tx_extra.h +++ b/src/cryptonote_basic/tx_extra.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_basic/verification_context.h b/src/cryptonote_basic/verification_context.h index 8d2b633a2..36b63f254 100644 --- a/src/cryptonote_basic/verification_context.h +++ b/src/cryptonote_basic/verification_context.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 956cc76aa..b6087de22 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -116,6 +116,7 @@ #define P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT 5000 //5 seconds #define P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT 70 #define P2P_DEFAULT_ANCHOR_CONNECTIONS_COUNT 2 +#define P2P_DEFAULT_SYNC_SEARCH_CONNECTIONS_COUNT 2 #define P2P_DEFAULT_LIMIT_RATE_UP 2048 // kB/s #define P2P_DEFAULT_LIMIT_RATE_DOWN 8192 // kB/s diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt index 231489fdb..fb96de226 100644 --- a/src/cryptonote_core/CMakeLists.txt +++ b/src/cryptonote_core/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 471edea99..f5bd9bbb5 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -1269,15 +1269,17 @@ void Blockchain::get_last_n_blocks_weights(std::vector<uint64_t>& weights, size_ if(h == 0) return; - m_db->block_txn_start(true); // add weight of last <count> blocks to vector <weights> (or less, if blockchain size < count) size_t start_offset = h - std::min<size_t>(h, count); - weights.reserve(weights.size() + h - start_offset); - for(size_t i = start_offset; i < h; i++) - { - weights.push_back(m_db->get_block_weight(i)); - } - m_db->block_txn_stop(); + weights = m_db->get_block_weights(start_offset, count); +} +//------------------------------------------------------------------ +void Blockchain::get_long_term_block_weights(std::vector<uint64_t>& weights, uint64_t start_height, size_t count) const +{ + LOG_PRINT_L3("Blockchain::" << __func__); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + weights = m_db->get_long_term_block_weights(start_height, count); } //------------------------------------------------------------------ uint64_t Blockchain::get_current_cumulative_block_weight_limit() const @@ -3804,9 +3806,7 @@ uint64_t Blockchain::get_next_long_term_block_weight(uint64_t block_weight) cons return block_weight; std::vector<uint64_t> weights; - weights.resize(nblocks); - for (uint64_t h = 0; h < nblocks; ++h) - weights[h] = m_db->get_block_long_term_weight(db_height - nblocks + h); + get_long_term_block_weights(weights, db_height - nblocks, nblocks); uint64_t long_term_median = epee::misc_utils::median(weights); uint64_t long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median); @@ -3850,9 +3850,7 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights_window, db_height); if (nblocks == db_height) --nblocks; - weights.resize(nblocks); - for (uint64_t h = 0; h < nblocks; ++h) - weights[h] = m_db->get_block_long_term_weight(db_height - nblocks + h - 1); + get_long_term_block_weights(weights, db_height - nblocks - 1, nblocks); new_weights = weights; long_term_median = epee::misc_utils::median(weights); } diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 9d928e386..3b8169764 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -1288,14 +1288,25 @@ namespace cryptonote /** * @brief gets recent block weights for median calculation * - * get the block weights of the last <count> blocks, and return by reference <sz>. + * get the block weights of the last <count> blocks, and return by reference <weights>. * - * @param sz return-by-reference the list of weights + * @param weights return-by-reference the list of weights * @param count the number of blocks to get weights for */ void get_last_n_blocks_weights(std::vector<uint64_t>& weights, size_t count) const; /** + * @brief gets recent block long term weights for median calculation + * + * get the block long term weights of the last <count> blocks, and return by reference <weights>. + * + * @param weights return-by-reference the list of weights + * @param start_height the block height of the first block to query + * @param count the number of blocks to get weights for + */ + void get_long_term_block_weights(std::vector<uint64_t>& weights, uint64_t start_height, size_t count) const; + + /** * @brief checks if a transaction is unlocked (its outputs spendable) * * This function checks to see if a transaction is unlocked. diff --git a/src/cryptonote_core/blockchain_storage_boost_serialization.h b/src/cryptonote_core/blockchain_storage_boost_serialization.h index f4f9f20ca..cfeb5acfc 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 6639db620..387203cc0 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -921,6 +921,7 @@ namespace cryptonote catch (const std::exception &e) { MERROR_VER("Exception in handle_incoming_tx_pre: " << e.what()); + tvc[i].m_verifivation_failed = true; results[i].res = false; } }); @@ -951,6 +952,7 @@ namespace cryptonote catch (const std::exception &e) { MERROR_VER("Exception in handle_incoming_tx_post: " << e.what()); + tvc[i].m_verifivation_failed = true; results[i].res = false; } }); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 6d0ff098d..356265dd6 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index c138c7854..854f3d1c5 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 ad3297951..cb1561c2d 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.h +++ b/src/cryptonote_core/cryptonote_tx_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index c2cc93530..c1cbe2acd 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 7a8af8799..877f2b82f 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_protocol/CMakeLists.txt b/src/cryptonote_protocol/CMakeLists.txt index 1189ccf22..bfcf42767 100644 --- a/src/cryptonote_protocol/CMakeLists.txt +++ b/src/cryptonote_protocol/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/src/cryptonote_protocol/block_queue.cpp b/src/cryptonote_protocol/block_queue.cpp index 716b0c8ba..b4f9daa74 100644 --- a/src/cryptonote_protocol/block_queue.cpp +++ b/src/cryptonote_protocol/block_queue.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/cryptonote_protocol/block_queue.h b/src/cryptonote_protocol/block_queue.h index c7d3af7ac..1bef01d67 100644 --- a/src/cryptonote_protocol/block_queue.h +++ b/src/cryptonote_protocol/block_queue.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, 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 23846dbf8..d582e3e9c 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -54,6 +54,7 @@ namespace cryptonote std::string host; std::string ip; std::string port; + uint16_t rpc_port; std::string peer_id; @@ -89,6 +90,7 @@ namespace cryptonote KV_SERIALIZE(host) KV_SERIALIZE(ip) KV_SERIALIZE(port) + KV_SERIALIZE(rpc_port) KV_SERIALIZE(peer_id) KV_SERIALIZE(recv_count) KV_SERIALIZE(recv_idle_time) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp b/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp index 6d9ad9028..225418980 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-2018, The Monero Project +// Copyright (c) 2014-2019, 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 efd986b53..0927b5d7f 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -111,6 +111,7 @@ namespace cryptonote void stop(); void on_connection_close(cryptonote_connection_context &context); void set_max_out_peers(unsigned int max) { m_max_out_peers = max; } + void set_no_sync(bool value) { m_no_sync = value; } std::string get_peers_overview() const; std::pair<uint32_t, uint32_t> get_next_needed_pruning_stripe() const; bool needs_new_sync_connections() const; @@ -138,6 +139,7 @@ namespace cryptonote void drop_connection(cryptonote_connection_context &context, bool add_fail, bool flush_all_spans); bool kick_idle_peers(); bool check_standby_peers(); + bool update_sync_search(); int try_add_next_blocks(cryptonote_connection_context &context); void notify_new_stripe(cryptonote_connection_context &context, uint32_t stripe); void skip_unneeded_hashes(cryptonote_connection_context& context, bool check_block_queue) const; @@ -149,10 +151,12 @@ namespace cryptonote std::atomic<uint32_t> m_syncronized_connections_count; std::atomic<bool> m_synchronized; std::atomic<bool> m_stopping; + std::atomic<bool> m_no_sync; boost::mutex m_sync_lock; block_queue m_block_queue; epee::math_helper::once_a_time_seconds<30> m_idle_peer_kicker; epee::math_helper::once_a_time_milliseconds<100> m_standby_checker; + epee::math_helper::once_a_time_seconds<101> m_sync_search_checker; std::atomic<unsigned int> m_max_out_peers; tools::PerformanceTimer m_sync_timer, m_add_timer; uint64_t m_last_add_end_time; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index c8b43fb91..b33867e8b 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -72,7 +72,8 @@ namespace cryptonote m_p2p(p_net_layout), m_syncronized_connections_count(0), m_synchronized(offline), - m_stopping(false) + m_stopping(false), + m_no_sync(false) { if(!m_p2p) @@ -231,6 +232,7 @@ namespace cryptonote cnx.ip = cnx.host; cnx.port = std::to_string(cntxt.m_remote_address.as<epee::net_utils::ipv4_network_address>().port()); } + cnx.rpc_port = cntxt.m_rpc_port; std::stringstream peer_id_str; peer_id_str << std::hex << std::setw(16) << peer_id; @@ -375,6 +377,13 @@ namespace cryptonote m_core.set_target_blockchain_height((hshd.current_height)); } MINFO(context << "Remote blockchain height: " << hshd.current_height << ", id: " << hshd.top_id); + + if (m_no_sync) + { + context.m_state = cryptonote_connection_context::state_normal; + return true; + } + context.m_state = cryptonote_connection_context::state_synchronizing; //let the socket to send response to handshake, but request callback, to let send request data after response LOG_PRINT_CCONTEXT_L2("requesting callback"); @@ -1397,6 +1406,7 @@ skip: { m_idle_peer_kicker.do_call(boost::bind(&t_cryptonote_protocol_handler<t_core>::kick_idle_peers, this)); m_standby_checker.do_call(boost::bind(&t_cryptonote_protocol_handler<t_core>::check_standby_peers, this)); + m_sync_search_checker.do_call(boost::bind(&t_cryptonote_protocol_handler<t_core>::update_sync_search, this)); return m_core.on_idle(); } //------------------------------------------------------------------------------------------------------------------------ @@ -1426,6 +1436,47 @@ skip: } //------------------------------------------------------------------------------------------------------------------------ template<class t_core> + bool t_cryptonote_protocol_handler<t_core>::update_sync_search() + { + const uint64_t target = m_core.get_target_blockchain_height(); + const uint64_t height = m_core.get_current_blockchain_height(); + if (target > height) // if we're not synced yet, don't do it + return true; + + MTRACE("Checking for outgoing syncing peers..."); + unsigned n_syncing = 0, n_synced = 0; + boost::uuids::uuid last_synced_peer_id(boost::uuids::nil_uuid()); + m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)->bool + { + if (!peer_id || context.m_is_income) // only consider connected outgoing peers + return true; + if (context.m_state == cryptonote_connection_context::state_synchronizing) + ++n_syncing; + if (context.m_state == cryptonote_connection_context::state_normal) + { + ++n_synced; + if (!context.m_anchor) + last_synced_peer_id = context.m_connection_id; + } + return true; + }); + MTRACE(n_syncing << " syncing, " << n_synced << " synced"); + + // if we're at max out peers, and not enough are syncing + if (n_synced + n_syncing >= m_max_out_peers && n_syncing < P2P_DEFAULT_SYNC_SEARCH_CONNECTIONS_COUNT && last_synced_peer_id != boost::uuids::nil_uuid()) + { + if (!m_p2p->for_connection(last_synced_peer_id, [&](cryptonote_connection_context& ctx, nodetool::peerid_type peer_id, uint32_t f)->bool{ + MINFO(ctx << "dropping synced peer, " << n_syncing << " syncing, " << n_synced << " synced"); + drop_connection(ctx, false, false); + return true; + })) + MDEBUG("Failed to find peer we wanted to drop"); + } + + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> bool t_cryptonote_protocol_handler<t_core>::check_standby_peers() { m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)->bool diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h index 2b9f201ec..a67178c52 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index 117790455..d9bfd9a20 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h index cba71bf3b..32fdca5ea 100644 --- a/src/daemon/command_line_args.h +++ b/src/daemon/command_line_args.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -96,6 +96,12 @@ namespace daemon_args , 0 }; + const command_line::arg_descriptor<bool> arg_public_node = { + "public-node" + , "Allow other users to use the node as a remote (restricted RPC mode, view-only commands) and advertise it over P2P" + , false + }; + const command_line::arg_descriptor<std::string> arg_zmq_rpc_bind_ip = { "zmq-rpc-bind-ip" , "IP for ZMQ RPC server to listen on" diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 00d004970..b324ab99d 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -411,6 +411,11 @@ bool t_command_parser_executor::stop_mining(const std::vector<std::string>& args return m_executor.stop_mining(); } +bool t_command_parser_executor::mining_status(const std::vector<std::string>& args) +{ + return m_executor.mining_status(); +} + bool t_command_parser_executor::stop_daemon(const std::vector<std::string>& args) { if (!args.empty()) return false; diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h index 5b8927908..bec6e4522 100644 --- a/src/daemon/command_parser_executor.h +++ b/src/daemon/command_parser_executor.h @@ -6,7 +6,7 @@ */ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -97,6 +97,8 @@ public: bool stop_mining(const std::vector<std::string>& args); + bool mining_status(const std::vector<std::string>& args); + bool stop_daemon(const std::vector<std::string>& args); bool print_status(const std::vector<std::string>& args); diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 3e19bb42f..94e4a8bf1 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -113,6 +113,11 @@ t_command_server::t_command_server( , "Stop mining." ); m_command_lookup.set_handler( + "mining_status" + , std::bind(&t_command_parser_executor::mining_status, &m_parser, p::_1) + , "Show current mining status." + ); + m_command_lookup.set_handler( "print_pool" , std::bind(&t_command_parser_executor::print_transaction_pool_long, &m_parser, p::_1) , "Print the transaction pool using a long format." diff --git a/src/daemon/command_server.h b/src/daemon/command_server.h index aff74da45..c8e77f551 100644 --- a/src/daemon/command_server.h +++ b/src/daemon/command_server.h @@ -9,7 +9,7 @@ Passing RPC commands: */ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/daemon/core.h b/src/daemon/core.h index c15d8d236..91dbb7a4b 100644 --- a/src/daemon/core.h +++ b/src/daemon/core.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 88fba8372..3d1d893ea 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -96,9 +96,11 @@ void t_daemon::init_options(boost::program_options::options_description & option } t_daemon::t_daemon( - boost::program_options::variables_map const & vm + boost::program_options::variables_map const & vm, + uint16_t public_rpc_port ) - : mp_internals{new t_internals{vm}} + : 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); @@ -186,6 +188,12 @@ bool t_daemon::run(bool interactive) MINFO(std::string("ZMQ server started at ") + zmq_rpc_bind_address + ":" + zmq_rpc_bind_port + "."); + if (public_rpc_port > 0) + { + MGINFO("Public RPC port " << public_rpc_port << " will be advertised to other peers over P2P"); + mp_internals->p2p.get().set_rpc_port(public_rpc_port); + } + mp_internals->p2p.run(); // blocks until p2p goes down if (rpc_commands) diff --git a/src/daemon/daemon.h b/src/daemon/daemon.h index 1e356ef5f..d44173177 100644 --- a/src/daemon/daemon.h +++ b/src/daemon/daemon.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -43,11 +43,13 @@ private: void stop_p2p(); 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; public: t_daemon( - boost::program_options::variables_map const & vm + boost::program_options::variables_map const & vm, + uint16_t public_rpc_port = 0 ); t_daemon(t_daemon && other); t_daemon & operator=(t_daemon && other); diff --git a/src/daemon/executor.cpp b/src/daemon/executor.cpp index fbc7d04fd..3719a253d 100644 --- a/src/daemon/executor.cpp +++ b/src/daemon/executor.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -59,21 +59,21 @@ namespace daemonize ) { LOG_PRINT_L0("Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ") Daemonised"); - return t_daemon{vm}; + return t_daemon{vm, public_rpc_port}; } bool t_executor::run_non_interactive( boost::program_options::variables_map const & vm ) { - return t_daemon{vm}.run(false); + return t_daemon{vm, public_rpc_port}.run(false); } bool t_executor::run_interactive( boost::program_options::variables_map const & vm ) { - return t_daemon{vm}.run(true); + return t_daemon{vm, public_rpc_port}.run(true); } } diff --git a/src/daemon/executor.h b/src/daemon/executor.h index 79d70567a..61e4e1bbf 100644 --- a/src/daemon/executor.h +++ b/src/daemon/executor.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -45,6 +45,10 @@ namespace daemonize static std::string const NAME; + t_executor(uint16_t public_rpc_port = 0) : public_rpc_port(public_rpc_port) + { + } + static void init_options( boost::program_options::options_description & configurable_options ); @@ -62,5 +66,8 @@ namespace daemonize bool run_interactive( boost::program_options::variables_map const & vm ); + + private: + uint16_t public_rpc_port; }; } diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index 35017d9ef..c3ac24b70 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -39,6 +39,7 @@ #include "daemon/executor.h" #include "daemonizer/daemonizer.h" #include "misc_log_ex.h" +#include "net/parse.h" #include "p2p/net_node.h" #include "rpc/core_rpc_server.h" #include "rpc/rpc_args.h" @@ -56,6 +57,57 @@ namespace po = boost::program_options; namespace bf = boost::filesystem; +uint16_t parse_public_rpc_port(const po::variables_map &vm) +{ + const auto &public_node_arg = daemon_args::arg_public_node; + const bool public_node = command_line::get_arg(vm, public_node_arg); + if (!public_node) + { + return 0; + } + + std::string rpc_port_str; + const auto &restricted_rpc_port = cryptonote::core_rpc_server::arg_rpc_restricted_bind_port; + if (!command_line::is_arg_defaulted(vm, restricted_rpc_port)) + { + rpc_port_str = command_line::get_arg(vm, restricted_rpc_port);; + } + else if (command_line::get_arg(vm, cryptonote::core_rpc_server::arg_restricted_rpc)) + { + rpc_port_str = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_rpc_bind_port); + } + else + { + throw std::runtime_error("restricted RPC mode is required"); + } + + uint16_t rpc_port; + if (!string_tools::get_xtype_from_string(rpc_port, rpc_port_str)) + { + throw std::runtime_error("invalid RPC port " + rpc_port_str); + } + + const auto rpc_bind_address = command_line::get_arg(vm, cryptonote::rpc_args::descriptors().rpc_bind_ip); + const auto address = net::get_network_address(rpc_bind_address, rpc_port); + if (!address) { + throw std::runtime_error("failed to parse RPC bind address"); + } + if (address->get_zone() != epee::net_utils::zone::public_) + { + throw std::runtime_error(std::string(zone_to_string(address->get_zone())) + + " network zone is not supported, please check RPC server bind address"); + } + + if (address->is_loopback() || address->is_local()) + { + MLOG_RED(el::Level::Warning, "--" << public_node_arg.name + << " is enabled, but RPC server " << address->str() + << " may be unreachable from outside, please check RPC server bind address"); + } + + return rpc_port; +} + int main(int argc, char const * argv[]) { try { @@ -86,6 +138,7 @@ int main(int argc, char const * argv[]) command_line::add_arg(core_settings, daemon_args::arg_max_log_file_size); command_line::add_arg(core_settings, daemon_args::arg_max_log_files); command_line::add_arg(core_settings, daemon_args::arg_max_concurrency); + 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); @@ -288,7 +341,7 @@ int main(int argc, char const * argv[]) MINFO("Moving from main() into the daemonize now."); - return daemonizer::daemonize(argc, argv, daemonize::t_executor{}, vm) ? 0 : 1; + return daemonizer::daemonize(argc, argv, daemonize::t_executor{parse_public_rpc_port(vm)}, vm) ? 0 : 1; } catch (std::exception const & ex) { diff --git a/src/daemon/p2p.h b/src/daemon/p2p.h index 0f01c746d..267b99c89 100644 --- a/src/daemon/p2p.h +++ b/src/daemon/p2p.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/daemon/protocol.h b/src/daemon/protocol.h index fd1d1b638..51a2bce1f 100644 --- a/src/daemon/protocol.h +++ b/src/daemon/protocol.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/daemon/rpc.h b/src/daemon/rpc.h index 37dffc097..213593aa7 100644 --- a/src/daemon/rpc.h +++ b/src/daemon/rpc.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 0a35dcef9..4ee67f571 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -61,8 +61,9 @@ namespace { peer_id_str >> id_str; epee::string_tools::xtype_to_string(peer.port, port_str); std::string addr_str = ip_str + ":" + port_str; + std::string rpc_port = peer.rpc_port ? std::to_string(peer.rpc_port) : "-"; std::string pruning_seed = epee::string_tools::to_string_hex(peer.pruning_seed); - tools::msg_writer() << boost::format("%-10s %-25s %-25s %-4s %s") % prefix % id_str % addr_str % pruning_seed % elapsed; + tools::msg_writer() << boost::format("%-10s %-25s %-25s %-5s %-4s %s") % prefix % id_str % addr_str % rpc_port % pruning_seed % elapsed; } void print_block_header(cryptonote::block_header_response const & header) @@ -462,7 +463,7 @@ bool t_rpc_command_executor::show_status() { % get_sync_percentage(ires) % (ires.testnet ? "testnet" : ires.stagenet ? "stagenet" : "mainnet") % bootstrap_msg - % (!has_mining_info ? "mining info unavailable" : mining_busy ? "syncing" : mres.active ? ( ( mres.is_background_mining_enabled ? "smart " : "" ) + std::string("mining at ") + get_mining_speed(mres.speed) + std::string(" to ") + mres.address ) : "not mining") + % (!has_mining_info ? "mining info unavailable" : mining_busy ? "syncing" : mres.active ? ( ( mres.is_background_mining_enabled ? "smart " : "" ) + std::string("mining at ") + get_mining_speed(mres.speed)) : "not mining") % get_mining_speed(ires.difficulty / ires.target) % (unsigned)hfres.version % get_fork_extra_info(hfres.earliest_height, net_height, ires.target) @@ -487,6 +488,81 @@ bool t_rpc_command_executor::show_status() { return true; } +bool t_rpc_command_executor::mining_status() { + cryptonote::COMMAND_RPC_MINING_STATUS::request mreq; + cryptonote::COMMAND_RPC_MINING_STATUS::response mres; + epee::json_rpc::error error_resp; + bool has_mining_info = true; + + std::string fail_message = "Problem fetching info"; + + bool mining_busy = false; + if (m_is_rpc) + { + // mining info is only available non unrestricted RPC mode + has_mining_info = m_rpc_client->rpc_request(mreq, mres, "/mining_status", fail_message.c_str()); + } + else + { + if (!m_rpc_server->on_mining_status(mreq, mres)) + { + tools::fail_msg_writer() << fail_message.c_str(); + return true; + } + + if (mres.status == CORE_RPC_STATUS_BUSY) + { + mining_busy = true; + } + else if (mres.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << make_error(fail_message, mres.status); + return true; + } + } + + if (!has_mining_info) + { + tools::fail_msg_writer() << "Mining info unavailable"; + return true; + } + + if (mining_busy || !mres.active) + { + tools::msg_writer() << "Not currently mining"; + } + else + { + tools::msg_writer() << "Mining at " << get_mining_speed(mres.speed) << " with " << mres.threads_count << " threads"; + } + + if (mres.active || mres.is_background_mining_enabled) + { + tools::msg_writer() << "PoW algorithm: " << mres.pow_algorithm; + tools::msg_writer() << "Mining address: " << mres.address; + } + + if (mres.is_background_mining_enabled) + { + tools::msg_writer() << "Smart mining enabled:"; + tools::msg_writer() << " Target: " << (unsigned)mres.bg_target << "% CPU"; + tools::msg_writer() << " Idle threshold: " << (unsigned)mres.bg_idle_threshold << "% CPU"; + tools::msg_writer() << " Min idle time: " << (unsigned)mres.bg_min_idle_seconds << " seconds"; + tools::msg_writer() << " Ignore battery: " << (mres.bg_ignore_battery ? "yes" : "no"); + } + + if (!mining_busy && mres.active) + { + uint64_t daily = 86400ull / mres.block_target * mres.block_reward; + uint64_t monthly = 86400ull / mres.block_target * 30.5 * mres.block_reward; + uint64_t yearly = 86400ull / mres.block_target * 356 * mres.block_reward; + tools::msg_writer() << "Expected: " << cryptonote::print_money(daily) << " monero daily, " + << cryptonote::print_money(monthly) << " monero monthly, " << cryptonote::print_money(yearly) << " yearly"; + } + + return true; +} + bool t_rpc_command_executor::print_connections() { cryptonote::COMMAND_RPC_GET_CONNECTIONS::request req; cryptonote::COMMAND_RPC_GET_CONNECTIONS::response res; diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index b1e9828a0..423132b79 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -6,7 +6,7 @@ */ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -109,6 +109,8 @@ public: bool stop_mining(); + bool mining_status(); + bool stop_daemon(); bool print_status(); diff --git a/src/daemonizer/CMakeLists.txt b/src/daemonizer/CMakeLists.txt index 2753d0003..d540adc10 100644 --- a/src/daemonizer/CMakeLists.txt +++ b/src/daemonizer/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/src/daemonizer/daemonizer.h b/src/daemonizer/daemonizer.h index c5852b59c..a45a2b44f 100644 --- a/src/daemonizer/daemonizer.h +++ b/src/daemonizer/daemonizer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/posix_daemonizer.inl b/src/daemonizer/posix_daemonizer.inl index b3f3f262f..9ec09c678 100644 --- a/src/daemonizer/posix_daemonizer.inl +++ b/src/daemonizer/posix_daemonizer.inl @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/posix_fork.h b/src/daemonizer/posix_fork.h index 9294b00e2..27b4ac18d 100644 --- a/src/daemonizer/posix_fork.h +++ b/src/daemonizer/posix_fork.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/windows_daemonizer.inl b/src/daemonizer/windows_daemonizer.inl index 7e61e3603..701c098f6 100644 --- a/src/daemonizer/windows_daemonizer.inl +++ b/src/daemonizer/windows_daemonizer.inl @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/windows_service.cpp b/src/daemonizer/windows_service.cpp index 1302fa578..7d53b07c7 100644 --- a/src/daemonizer/windows_service.cpp +++ b/src/daemonizer/windows_service.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/windows_service.h b/src/daemonizer/windows_service.h index aacf3d039..12429c78a 100644 --- a/src/daemonizer/windows_service.h +++ b/src/daemonizer/windows_service.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/daemonizer/windows_service_runner.h b/src/daemonizer/windows_service_runner.h index 06e180823..703795ce9 100644 --- a/src/daemonizer/windows_service_runner.h +++ b/src/daemonizer/windows_service_runner.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/debug_utilities/CMakeLists.txt b/src/debug_utilities/CMakeLists.txt index 1bcbfd0cf..7bc2c324f 100644 --- a/src/debug_utilities/CMakeLists.txt +++ b/src/debug_utilities/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/src/debug_utilities/cn_deserialize.cpp b/src/debug_utilities/cn_deserialize.cpp index f08f2b928..89c9db02e 100644 --- a/src/debug_utilities/cn_deserialize.cpp +++ b/src/debug_utilities/cn_deserialize.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -80,11 +80,9 @@ int main(int argc, char* argv[]) po::options_description desc_cmd_only("Command line options"); po::options_description desc_cmd_sett("Command line options and settings options"); - const command_line::arg_descriptor<std::string> arg_output_file = {"output-file", "Specify output file", "", true}; const command_line::arg_descriptor<uint32_t> arg_log_level = {"log-level", "", log_level}; const command_line::arg_descriptor<std::string> arg_input = {"input", "Specify input has a hexadecimal string", ""}; - command_line::add_arg(desc_cmd_sett, arg_output_file); command_line::add_arg(desc_cmd_sett, arg_log_level); command_line::add_arg(desc_cmd_sett, arg_input); @@ -120,52 +118,10 @@ int main(int argc, char* argv[]) mlog_configure("", true); - std::string m_config_folder; - - std::ostream *output; - std::ofstream *raw_data_file = NULL; - if (command_line::has_arg(vm, arg_output_file)) - { - output_file_path = boost::filesystem::path(command_line::get_arg(vm, arg_output_file)); - - const boost::filesystem::path dir_path = output_file_path.parent_path(); - if (!dir_path.empty()) - { - if (boost::filesystem::exists(dir_path)) - { - if (!boost::filesystem::is_directory(dir_path)) - { - std::cerr << "output directory path is a file: " << dir_path << std::endl; - return 1; - } - } - else - { - if (!boost::filesystem::create_directory(dir_path)) - { - std::cerr << "Failed to create directory " << dir_path << std::endl; - return 1; - } - } - } - - raw_data_file = new std::ofstream(); - raw_data_file->open(output_file_path.string(), std::ios_base::out | std::ios::trunc); - if (raw_data_file->fail()) - return 1; - output = raw_data_file; - } - else - { - output_file_path = ""; - output = &std::cout; - } - cryptonote::blobdata blob; if (!epee::string_tools::parse_hexstr_to_binbuff(input, blob)) { std::cerr << "Invalid hex input" << std::endl; - std::cerr << "Invalid hex input: " << input << std::endl; return 1; } @@ -212,11 +168,5 @@ int main(int argc, char* argv[]) - if (output->fail()) - return 1; - output->flush(); - if (raw_data_file) - delete raw_data_file; - return 0; } diff --git a/src/debug_utilities/object_sizes.cpp b/src/debug_utilities/object_sizes.cpp index 2281d0734..302f0a206 100644 --- a/src/debug_utilities/object_sizes.cpp +++ b/src/debug_utilities/object_sizes.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt index 91d670b73..ffa1458b0 100644 --- a/src/device/CMakeLists.txt +++ b/src/device/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2017, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/src/device/device.cpp b/src/device/device.cpp index d5e3031ff..fbd77dab9 100644 --- a/src/device/device.cpp +++ b/src/device/device.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/device/device.hpp b/src/device/device.hpp index 408f64c8b..65b38361b 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -70,6 +70,7 @@ namespace cryptonote struct account_keys; struct subaddress_index; struct tx_destination_entry; + struct keypair; } namespace hw { @@ -81,11 +82,18 @@ namespace hw { return false; } + class device_progress { + public: + virtual double progress() const { return 0; } + virtual bool indeterminate() const { return false; } + }; + class i_device_callback { public: - virtual void on_button_request() {} - virtual void on_pin_request(epee::wipeable_string & pin) {} - virtual void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) {} + virtual void on_button_request(uint64_t code=0) {} + virtual boost::optional<epee::wipeable_string> on_pin_request() { return boost::none; } + virtual boost::optional<epee::wipeable_string> on_passphrase_request(bool on_device) { return boost::none; } + virtual void on_progress(const device_progress& event) {} virtual ~i_device_callback() = default; }; @@ -141,6 +149,9 @@ namespace hw { virtual void set_callback(i_device_callback * callback) {}; virtual void set_derivation_path(const std::string &derivation_path) {}; + virtual void set_pin(const epee::wipeable_string & pin) {} + virtual void set_passphrase(const epee::wipeable_string & passphrase) {} + /* ======================================================================= */ /* LOCKER */ /* ======================================================================= */ @@ -229,7 +240,9 @@ namespace hw { virtual bool has_ki_cold_sync(void) const { return false; } virtual bool has_tx_cold_sign(void) const { return false; } - + virtual bool has_ki_live_refresh(void) const { return true; } + virtual bool compute_key_image(const cryptonote::account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const cryptonote::subaddress_index& received_index, cryptonote::keypair& in_ephemeral, crypto::key_image& ki) { return false; } + virtual void computing_key_images(bool started) {}; virtual void set_network_type(cryptonote::network_type network_type) { } protected: diff --git a/src/device/device_cold.hpp b/src/device/device_cold.hpp index 22128cec1..31b1504ab 100644 --- a/src/device/device_cold.hpp +++ b/src/device/device_cold.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -31,6 +31,7 @@ #define MONERO_DEVICE_COLD_H #include "wallet/wallet2.h" +#include <boost/optional/optional.hpp> #include <boost/function.hpp> @@ -44,6 +45,8 @@ namespace hw { public: std::vector<std::string> tx_device_aux; // device generated aux data std::vector<cryptonote::address_parse_info> tx_recipients; // as entered by user + boost::optional<int> bp_version; // BP version to use + boost::optional<unsigned> client_version; // Signing client version to use (testing) }; class device_cold { @@ -51,6 +54,53 @@ namespace hw { using exported_key_image = std::vector<std::pair<crypto::key_image, crypto::signature>>; + class op_progress : public hw::device_progress { + public: + op_progress():m_progress(0), m_indeterminate(false) {}; + explicit op_progress(double progress, bool indeterminate=false): m_progress(progress), m_indeterminate(indeterminate){} + + double progress() const override { return m_progress; } + bool indeterminate() const override { return m_indeterminate; } + protected: + double m_progress; + bool m_indeterminate; + }; + + class tx_progress : public op_progress { + public: + tx_progress(): + m_cur_tx(0), m_max_tx(1), + m_cur_step(0), m_max_step(1), + m_cur_substep(0), m_max_substep(1){}; + + tx_progress(size_t cur_tx, size_t max_tx, size_t cur_step, size_t max_step, size_t cur_substep, size_t max_substep): + m_cur_tx(cur_tx), m_max_tx(max_tx), + m_cur_step(cur_tx), m_max_step(max_tx), + m_cur_substep(cur_tx), m_max_substep(max_tx){} + + double progress() const override { + return std::max(1.0, (double)m_cur_tx / m_max_tx + + (double)m_cur_step / (m_max_tx * m_max_step) + + (double)m_cur_substep / (m_max_tx * m_max_step * m_max_substep)); + } + bool indeterminate() const override { return false; } + + protected: + size_t m_cur_tx; + size_t m_max_tx; + size_t m_cur_step; + size_t m_max_step; + size_t m_cur_substep; + size_t m_max_substep; + }; + + typedef struct { + std::string salt1; + std::string salt2; + std::string tx_enc_keys; + std::string tx_prefix_hash; + } tx_key_data_t; + /** * Key image sync with the cold protocol. */ @@ -65,6 +115,52 @@ namespace hw { const ::tools::wallet2::unsigned_tx_set & unsigned_tx, ::tools::wallet2::signed_tx_set & signed_tx, tx_aux_data & aux_data) =0; + + /** + * Get tx key support check. + */ + virtual bool is_get_tx_key_supported() const { return false; } + + /** + * Loads TX aux data required for tx key. + */ + virtual void load_tx_key_data(tx_key_data_t & res, const std::string & tx_aux_data) =0; + + /** + * Decrypts TX keys. + */ + virtual void get_tx_key( + std::vector<::crypto::secret_key> & tx_keys, + const tx_key_data_t & tx_aux_data, + const ::crypto::secret_key & view_key_priv) =0; + + /** + * Live refresh support check + */ + virtual bool is_live_refresh_supported() const { return false; }; + + /** + * Starts live refresh process with the device + */ + virtual void live_refresh_start() =0; + + /** + * One live refresh step + */ + virtual void live_refresh( + const ::crypto::secret_key & view_key_priv, + const crypto::public_key& out_key, + const crypto::key_derivation& recv_derivation, + size_t real_output_index, + const cryptonote::subaddress_index& received_index, + cryptonote::keypair& in_ephemeral, + crypto::key_image& ki + ) =0; + + /** + * Live refresh process termination + */ + virtual void live_refresh_finish() =0; }; } diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp index fd15717a7..999fbc22f 100644 --- a/src/device/device_default.cpp +++ b/src/device/device_default.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/device/device_default.hpp b/src/device/device_default.hpp index 04b9b4234..90d39495b 100644 --- a/src/device/device_default.hpp +++ b/src/device/device_default.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/device/device_io.hpp b/src/device/device_io.hpp index 1d5e3564c..fe66736f7 100644 --- a/src/device/device_io.hpp +++ b/src/device/device_io.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/device/device_io_hid.cpp b/src/device/device_io_hid.cpp index 36c7a241b..f07e0eaae 100644 --- a/src/device/device_io_hid.cpp +++ b/src/device/device_io_hid.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/device/device_io_hid.hpp b/src/device/device_io_hid.hpp index c47eefad2..ed22058d6 100644 --- a/src/device/device_io_hid.hpp +++ b/src/device/device_io_hid.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 3c7590861..0f197272c 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp index 3f470ee7c..252354e1c 100644 --- a/src/device/device_ledger.hpp +++ b/src/device/device_ledger.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/device/log.cpp b/src/device/log.cpp index 87505798b..616ad8e90 100644 --- a/src/device/log.cpp +++ b/src/device/log.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/device/log.hpp b/src/device/log.hpp index 25a214a6c..fb7ba1fb0 100644 --- a/src/device/log.hpp +++ b/src/device/log.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/device_trezor/CMakeLists.txt b/src/device_trezor/CMakeLists.txt index 7f979389a..250939da7 100644 --- a/src/device_trezor/CMakeLists.txt +++ b/src/device_trezor/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2017, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # @@ -67,6 +67,12 @@ set(trezor_private_headers) if(DEVICE_TREZOR_READY) message(STATUS "Trezor support enabled") + if(USE_DEVICE_TREZOR_DEBUG) + list(APPEND trezor_headers trezor/debug_link.hpp trezor/messages/messages-debug.pb.h) + list(APPEND trezor_sources trezor/debug_link.cpp trezor/messages/messages-debug.pb.cc) + message(STATUS "Trezor debugging enabled") + endif() + monero_private_headers(device_trezor ${device_private_headers}) diff --git a/src/device_trezor/device_trezor.cpp b/src/device_trezor/device_trezor.cpp index 8868fb995..b4a80cf2c 100644 --- a/src/device_trezor/device_trezor.cpp +++ b/src/device_trezor/device_trezor.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -57,7 +57,9 @@ namespace trezor { } device_trezor::device_trezor() { - + m_live_refresh_in_progress = false; + m_live_refresh_enabled = true; + m_live_refresh_thread_running = false; } device_trezor::~device_trezor() { @@ -69,6 +71,89 @@ namespace trezor { } } + bool device_trezor::init() + { + m_live_refresh_in_progress = false; + bool r = device_trezor_base::init(); + if (r && !m_live_refresh_thread) + { + m_live_refresh_thread_running = true; + m_live_refresh_thread.reset(new boost::thread(boost::bind(&device_trezor::live_refresh_thread_main, this))); + } + return r; + } + + bool device_trezor::release() + { + m_live_refresh_in_progress = false; + m_live_refresh_thread_running = false; + if (m_live_refresh_thread) + { + m_live_refresh_thread->join(); + m_live_refresh_thread = nullptr; + } + return device_trezor_base::release(); + } + + bool device_trezor::disconnect() + { + m_live_refresh_in_progress = false; + return device_trezor_base::disconnect(); + } + + void device_trezor::device_state_reset_unsafe() + { + require_connected(); + if (m_live_refresh_in_progress) + { + try + { + live_refresh_finish_unsafe(); + } + catch(const std::exception & e) + { + MERROR("Live refresh could not be terminated: " << e.what()); + } + } + + m_live_refresh_in_progress = false; + device_trezor_base::device_state_reset_unsafe(); + } + + void device_trezor::live_refresh_thread_main() + { + while(m_live_refresh_thread_running) + { + boost::this_thread::sleep_for(boost::chrono::milliseconds(100)); + if (!m_live_refresh_in_progress) + { + continue; + } + + TREZOR_AUTO_LOCK_DEVICE(); + if (!m_transport || !m_live_refresh_in_progress) + { + continue; + } + + auto current_time = std::chrono::steady_clock::now(); + if (current_time - m_last_live_refresh_time <= std::chrono::seconds(20)) + { + continue; + } + + MTRACE("Closing live refresh process due to inactivity"); + try + { + live_refresh_finish(); + } + catch(const std::exception &e) + { + MWARNING("Live refresh auto-finish failed: " << e.what()); + } + } + } + /* ======================================================================= */ /* WALLET & ADDRESS */ /* ======================================================================= */ @@ -97,7 +182,14 @@ namespace trezor { auto res = get_view_key(); CHECK_AND_ASSERT_MES(res->watch_key().size() == 32, false, "Trezor returned invalid view key"); + // Trezor does not make use of spendkey of the device API. + // Ledger loads encrypted spendkey, Trezor loads null key (never leaves device). + // In the test (debugging mode) we need to leave this field intact as it is already set by + // the debugging code and need to remain same for the testing purposes. +#ifndef WITH_TREZOR_DEBUGGING spendkey = crypto::null_skey; // not given +#endif + memcpy(viewkey.data, res->watch_key().data(), 32); return true; @@ -119,7 +211,7 @@ namespace trezor { std::shared_ptr<messages::monero::MoneroAddress> device_trezor::get_address( const boost::optional<std::vector<uint32_t>> & path, const boost::optional<cryptonote::network_type> & network_type){ - AUTO_LOCK_CMD(); + TREZOR_AUTO_LOCK_CMD(); require_connected(); device_state_reset_unsafe(); require_initialized(); @@ -135,7 +227,7 @@ namespace trezor { std::shared_ptr<messages::monero::MoneroWatchKey> device_trezor::get_view_key( const boost::optional<std::vector<uint32_t>> & path, const boost::optional<cryptonote::network_type> & network_type){ - AUTO_LOCK_CMD(); + TREZOR_AUTO_LOCK_CMD(); require_connected(); device_state_reset_unsafe(); require_initialized(); @@ -148,11 +240,43 @@ namespace trezor { return response; } + bool device_trezor::is_get_tx_key_supported() const + { + require_initialized(); + return get_version() > pack_version(2, 0, 10); + } + + void device_trezor::load_tx_key_data(::hw::device_cold::tx_key_data_t & res, const std::string & tx_aux_data) + { + protocol::tx::load_tx_key_data(res, tx_aux_data); + } + + void device_trezor::get_tx_key( + std::vector<::crypto::secret_key> & tx_keys, + const ::hw::device_cold::tx_key_data_t & tx_aux_data, + const ::crypto::secret_key & view_key_priv) + { + TREZOR_AUTO_LOCK_CMD(); + require_connected(); + device_state_reset_unsafe(); + require_initialized(); + + auto req = protocol::tx::get_tx_key(tx_aux_data); + this->set_msg_addr<messages::monero::MoneroGetTxKeyRequest>(req.get()); + + auto response = this->client_exchange<messages::monero::MoneroGetTxKeyAck>(req); + MTRACE("Get TX key response received"); + + protocol::tx::get_tx_key_ack(tx_keys, tx_aux_data.tx_prefix_hash, view_key_priv, response); + } + void device_trezor::ki_sync(wallet_shim * wallet, const std::vector<tools::wallet2::transfer_details> & transfers, hw::device_cold::exported_key_image & ski) { - AUTO_LOCK_CMD(); +#define EVENT_PROGRESS(P) do { if (m_callback) {(m_callback)->on_progress(device_cold::op_progress(P)); } }while(0) + + TREZOR_AUTO_LOCK_CMD(); require_connected(); device_state_reset_unsafe(); require_initialized(); @@ -164,6 +288,7 @@ namespace trezor { protocol::ki::key_image_data(wallet, transfers, mtds); protocol::ki::generate_commitment(mtds, transfers, req); + EVENT_PROGRESS(0.); this->set_msg_addr<messages::monero::MoneroKeyImageExportInitRequest>(req.get()); auto ack1 = this->client_exchange<messages::monero::MoneroKeyImageExportInitAck>(req); @@ -187,27 +312,160 @@ namespace trezor { } MTRACE("Batch " << cur << " / " << num_batches << " batches processed"); + EVENT_PROGRESS((double)cur * batch_size / mtds.size()); } + EVENT_PROGRESS(1.); auto final_req = std::make_shared<messages::monero::MoneroKeyImageSyncFinalRequest>(); auto final_ack = this->client_exchange<messages::monero::MoneroKeyImageSyncFinalAck>(final_req); ski.reserve(kis.size()); for(auto & sub : kis){ - char buff[32*3]; + ::crypto::signature sig{}; + ::crypto::key_image ki; + char buff[sizeof(ki.data)*3]; + + size_t buff_len = sizeof(buff); + protocol::crypto::chacha::decrypt(sub.blob().data(), sub.blob().size(), reinterpret_cast<const uint8_t *>(final_ack->enc_key().data()), - reinterpret_cast<const uint8_t *>(sub.iv().data()), buff); + reinterpret_cast<const uint8_t *>(sub.iv().data()), buff, &buff_len); + CHECK_AND_ASSERT_THROW_MES(buff_len == sizeof(buff), "Plaintext size invalid"); - ::crypto::signature sig{}; - ::crypto::key_image ki; - memcpy(ki.data, buff, 32); - memcpy(sig.c.data, buff + 32, 32); - memcpy(sig.r.data, buff + 64, 32); + memcpy(ki.data, buff, sizeof(ki.data)); + memcpy(sig.c.data, buff + sizeof(ki.data), sizeof(ki.data)); + memcpy(sig.r.data, buff + 2*sizeof(ki.data), sizeof(ki.data)); ski.push_back(std::make_pair(ki, sig)); } +#undef EVENT_PROGRESS + } + + bool device_trezor::is_live_refresh_supported() const + { + require_initialized(); + return get_version() > pack_version(2, 0, 10); } + bool device_trezor::is_live_refresh_enabled() const + { + return is_live_refresh_supported() && (mode == NONE || mode == TRANSACTION_PARSE) && m_live_refresh_enabled; + } + + bool device_trezor::has_ki_live_refresh() const + { + try{ + return is_live_refresh_enabled(); + } catch(const std::exception & e){ + MERROR("Could not detect if live refresh is enabled: " << e.what()); + } + return false; + } + + void device_trezor::live_refresh_start() + { + TREZOR_AUTO_LOCK_CMD(); + require_connected(); + live_refresh_start_unsafe(); + } + + void device_trezor::live_refresh_start_unsafe() + { + device_state_reset_unsafe(); + require_initialized(); + + auto req = std::make_shared<messages::monero::MoneroLiveRefreshStartRequest>(); + this->set_msg_addr<messages::monero::MoneroLiveRefreshStartRequest>(req.get()); + this->client_exchange<messages::monero::MoneroLiveRefreshStartAck>(req); + m_live_refresh_in_progress = true; + m_last_live_refresh_time = std::chrono::steady_clock::now(); + } + + void device_trezor::live_refresh( + const ::crypto::secret_key & view_key_priv, + const crypto::public_key& out_key, + const crypto::key_derivation& recv_derivation, + size_t real_output_index, + const cryptonote::subaddress_index& received_index, + cryptonote::keypair& in_ephemeral, + crypto::key_image& ki + ) + { + TREZOR_AUTO_LOCK_CMD(); + require_connected(); + + if (!m_live_refresh_in_progress) + { + live_refresh_start_unsafe(); + } + + m_last_live_refresh_time = std::chrono::steady_clock::now(); + + auto req = std::make_shared<messages::monero::MoneroLiveRefreshStepRequest>(); + req->set_out_key(out_key.data, 32); + req->set_recv_deriv(recv_derivation.data, 32); + req->set_real_out_idx(real_output_index); + req->set_sub_addr_major(received_index.major); + req->set_sub_addr_minor(received_index.minor); + + auto ack = this->client_exchange<messages::monero::MoneroLiveRefreshStepAck>(req); + protocol::ki::live_refresh_ack(view_key_priv, out_key, ack, in_ephemeral, ki); + } + + void device_trezor::live_refresh_finish_unsafe() + { + auto req = std::make_shared<messages::monero::MoneroLiveRefreshFinalRequest>(); + this->client_exchange<messages::monero::MoneroLiveRefreshFinalAck>(req); + m_live_refresh_in_progress = false; + } + + void device_trezor::live_refresh_finish() + { + TREZOR_AUTO_LOCK_CMD(); + require_connected(); + if (m_live_refresh_in_progress) + { + live_refresh_finish_unsafe(); + } + } + + void device_trezor::computing_key_images(bool started) + { + try + { + if (!is_live_refresh_enabled()) + { + return; + } + + // React only on termination as the process can auto-start itself. + if (!started && m_live_refresh_in_progress) + { + live_refresh_finish(); + } + } + catch(const std::exception & e) + { + MWARNING("KI computation state change failed, started: " << started << ", e: " << e.what()); + } + } + + bool device_trezor::compute_key_image( + const ::cryptonote::account_keys& ack, + const ::crypto::public_key& out_key, + const ::crypto::key_derivation& recv_derivation, + size_t real_output_index, + const ::cryptonote::subaddress_index& received_index, + ::cryptonote::keypair& in_ephemeral, + ::crypto::key_image& ki) + { + if (!is_live_refresh_enabled()) + { + return false; + } + + live_refresh(ack.m_view_secret_key, out_key, recv_derivation, real_output_index, received_index, in_ephemeral, ki); + return true; + } void device_trezor::tx_sign(wallet_shim * wallet, const tools::wallet2::unsigned_tx_set & unsigned_tx, @@ -215,7 +473,15 @@ namespace trezor { hw::tx_aux_data & aux_data) { CHECK_AND_ASSERT_THROW_MES(unsigned_tx.transfers.first == 0, "Unsuported non zero offset"); - size_t num_tx = unsigned_tx.txes.size(); + + TREZOR_AUTO_LOCK_CMD(); + require_connected(); + device_state_reset_unsafe(); + require_initialized(); + transaction_versions_check(unsigned_tx, aux_data); + + const size_t num_tx = unsigned_tx.txes.size(); + m_num_transations_to_sign = num_tx; signed_tx.key_images.clear(); signed_tx.key_images.resize(unsigned_tx.transfers.second.size()); @@ -260,6 +526,10 @@ namespace trezor { cpend.key_images = key_images; // KI sync + for(size_t cidx=0, trans_max=unsigned_tx.transfers.second.size(); cidx < trans_max; ++cidx){ + signed_tx.key_images[cidx] = unsigned_tx.transfers.second[cidx].m_key_image; + } + size_t num_sources = cdata.tx_data.sources.size(); CHECK_AND_ASSERT_THROW_MES(num_sources == cdata.source_permutation.size(), "Invalid permutation size"); CHECK_AND_ASSERT_THROW_MES(num_sources == cdata.tx.vin.size(), "Invalid tx.vin size"); @@ -269,12 +539,19 @@ namespace trezor { CHECK_AND_ASSERT_THROW_MES(src_idx < cdata.tx.vin.size(), "Invalid idx_mapped"); size_t idx_map_src = cdata.tx_data.selected_transfers[idx_mapped]; - auto vini = boost::get<cryptonote::txin_to_key>(cdata.tx.vin[src_idx]); + CHECK_AND_ASSERT_THROW_MES(idx_map_src >= unsigned_tx.transfers.first, "Invalid offset"); + idx_map_src -= unsigned_tx.transfers.first; CHECK_AND_ASSERT_THROW_MES(idx_map_src < signed_tx.key_images.size(), "Invalid key image index"); + + const auto vini = boost::get<cryptonote::txin_to_key>(cdata.tx.vin[src_idx]); signed_tx.key_images[idx_map_src] = vini.k_image; } } + + if (m_callback){ + m_callback->on_progress(device_cold::tx_progress(m_num_transations_to_sign, m_num_transations_to_sign, 1, 1, 1, 1)); + } } void device_trezor::tx_sign(wallet_shim * wallet, @@ -283,10 +560,16 @@ namespace trezor { hw::tx_aux_data & aux_data, std::shared_ptr<protocol::tx::Signer> & signer) { - AUTO_LOCK_CMD(); +#define EVENT_PROGRESS(S, SUB, SUBMAX) do { if (m_callback) { \ + (m_callback)->on_progress(device_cold::tx_progress(idx, m_num_transations_to_sign, S, 10, SUB, SUBMAX)); \ +} }while(0) + require_connected(); - device_state_reset_unsafe(); + if (idx > 0) + device_state_reset_unsafe(); + require_initialized(); + EVENT_PROGRESS(0, 1, 1); CHECK_AND_ASSERT_THROW_MES(idx < unsigned_tx.txes.size(), "Invalid transaction index"); signer = std::make_shared<protocol::tx::Signer>(wallet, &unsigned_tx, idx, &aux_data); @@ -298,6 +581,7 @@ namespace trezor { auto init_msg = signer->step_init(); this->set_msg_addr(init_msg.get()); transaction_pre_check(init_msg); + EVENT_PROGRESS(1, 1, 1); auto response = this->client_exchange<messages::monero::MoneroTransactionInitAck>(init_msg); signer->step_init_ack(response); @@ -307,6 +591,7 @@ namespace trezor { auto src = signer->step_set_input(cur_src); auto ack = this->client_exchange<messages::monero::MoneroTransactionSetInputAck>(src); signer->step_set_input_ack(ack); + EVENT_PROGRESS(2, cur_src, num_sources); } // Step: sort @@ -315,44 +600,82 @@ namespace trezor { auto perm_ack = this->client_exchange<messages::monero::MoneroTransactionInputsPermutationAck>(perm_req); signer->step_permutation_ack(perm_ack); } + EVENT_PROGRESS(3, 1, 1); // Step: input_vini - if (!signer->in_memory()){ - for(size_t cur_src = 0; cur_src < num_sources; ++cur_src){ - auto src = signer->step_set_vini_input(cur_src); - auto ack = this->client_exchange<messages::monero::MoneroTransactionInputViniAck>(src); - signer->step_set_vini_input_ack(ack); - } + for(size_t cur_src = 0; cur_src < num_sources; ++cur_src){ + auto src = signer->step_set_vini_input(cur_src); + auto ack = this->client_exchange<messages::monero::MoneroTransactionInputViniAck>(src); + signer->step_set_vini_input_ack(ack); + EVENT_PROGRESS(4, cur_src, num_sources); } // Step: all inputs set auto all_inputs_set = signer->step_all_inputs_set(); auto ack_all_inputs = this->client_exchange<messages::monero::MoneroTransactionAllInputsSetAck>(all_inputs_set); signer->step_all_inputs_set_ack(ack_all_inputs); + EVENT_PROGRESS(5, 1, 1); // Step: outputs for(size_t cur_dst = 0; cur_dst < num_outputs; ++cur_dst){ auto src = signer->step_set_output(cur_dst); auto ack = this->client_exchange<messages::monero::MoneroTransactionSetOutputAck>(src); signer->step_set_output_ack(ack); + + // If BP is offloaded to host, another step with computed BP may be needed. + auto offloaded_bp = signer->step_rsig(cur_dst); + if (offloaded_bp){ + auto bp_ack = this->client_exchange<messages::monero::MoneroTransactionSetOutputAck>(offloaded_bp); + signer->step_set_rsig_ack(ack); + } + + EVENT_PROGRESS(6, cur_dst, num_outputs); } // Step: all outs set auto all_out_set = signer->step_all_outs_set(); auto ack_all_out_set = this->client_exchange<messages::monero::MoneroTransactionAllOutSetAck>(all_out_set); signer->step_all_outs_set_ack(ack_all_out_set, *this); + EVENT_PROGRESS(7, 1, 1); // Step: sign each input for(size_t cur_src = 0; cur_src < num_sources; ++cur_src){ auto src = signer->step_sign_input(cur_src); auto ack_sign = this->client_exchange<messages::monero::MoneroTransactionSignInputAck>(src); signer->step_sign_input_ack(ack_sign); + EVENT_PROGRESS(8, cur_src, num_sources); } // Step: final auto final_msg = signer->step_final(); auto ack_final = this->client_exchange<messages::monero::MoneroTransactionFinalAck>(final_msg); signer->step_final_ack(ack_final); + EVENT_PROGRESS(9, 1, 1); +#undef EVENT_PROGRESS + } + + void device_trezor::transaction_versions_check(const ::tools::wallet2::unsigned_tx_set & unsigned_tx, hw::tx_aux_data & aux_data) + { + auto trezor_version = get_version(); + unsigned client_version = 1; // default client version for tx + + if (trezor_version <= pack_version(2, 0, 10)){ + client_version = 0; + } + + if (aux_data.client_version){ + auto wanted_client_version = aux_data.client_version.get(); + if (wanted_client_version > client_version){ + throw exc::TrezorException("Trezor firmware 2.0.10 and lower does not support current transaction sign protocol. Please update."); + } else { + client_version = wanted_client_version; + } + } + aux_data.client_version = client_version; + + if (client_version == 0 && aux_data.bp_version && aux_data.bp_version.get() != 1){ + throw exc::TrezorException("Trezor firmware 2.0.10 and lower does not support current transaction sign protocol (BPv2+). Please update."); + } } void device_trezor::transaction_pre_check(std::shared_ptr<messages::monero::MoneroTransactionInitRequest> init_msg) @@ -362,13 +685,9 @@ namespace trezor { CHECK_AND_ASSERT_THROW_MES(m_features, "Device state not initialized"); // make sure the caller did not reset features const bool nonce_required = init_msg->tsx_data().has_payment_id() && init_msg->tsx_data().payment_id().size() > 0; - if (nonce_required){ + if (nonce_required && init_msg->tsx_data().payment_id().size() == 8){ // Versions 2.0.9 and lower do not support payment ID - CHECK_AND_ASSERT_THROW_MES(m_features->has_major_version() && m_features->has_minor_version() && m_features->has_patch_version(), "Invalid Trezor firmware version information"); - const uint32_t vma = m_features->major_version(); - const uint32_t vmi = m_features->minor_version(); - const uint32_t vpa = m_features->patch_version(); - if (vma < 2 || (vma == 2 && vmi == 0 && vpa <= 9)) { + if (get_version() <= pack_version(2, 0, 9)) { throw exc::TrezorException("Trezor firmware 2.0.9 and lower does not support transactions with short payment IDs or integrated addresses. Please update."); } } @@ -393,7 +712,7 @@ namespace trezor { const bool nonce_required = tdata.tsx_data.has_payment_id() && tdata.tsx_data.payment_id().size() > 0; const bool has_nonce = cryptonote::find_tx_extra_field_by_type(tx_extra_fields, nonce); - CHECK_AND_ASSERT_THROW_MES(has_nonce == nonce_required, "Transaction nonce presence inconsistent"); + CHECK_AND_ASSERT_THROW_MES(has_nonce || !nonce_required, "Transaction nonce not present"); if (nonce_required){ const std::string & payment_id = tdata.tsx_data.payment_id(); diff --git a/src/device_trezor/device_trezor.hpp b/src/device_trezor/device_trezor.hpp index 1f08be887..0e91847dc 100644 --- a/src/device_trezor/device_trezor.hpp +++ b/src/device_trezor/device_trezor.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -30,18 +30,21 @@ #ifndef MONERO_DEVICE_TREZOR_H #define MONERO_DEVICE_TREZOR_H +#include "trezor.hpp" +#include "device/device.hpp" +#ifdef WITH_DEVICE_TREZOR #include <cstddef> #include <string> -#include "device/device.hpp" -#include "device/device_default.hpp" -#include "device/device_cold.hpp" #include <boost/scope_exit.hpp> #include <boost/thread/mutex.hpp> #include <boost/thread/recursive_mutex.hpp> + +#include "device/device_default.hpp" +#include "device/device_cold.hpp" #include "cryptonote_config.h" -#include "trezor.hpp" #include "device_trezor_base.hpp" +#endif namespace hw { namespace trezor { @@ -57,8 +60,29 @@ namespace trezor { */ class device_trezor : public hw::trezor::device_trezor_base, public hw::device_cold { protected: + std::atomic<bool> m_live_refresh_in_progress; + std::chrono::steady_clock::time_point m_last_live_refresh_time; + std::unique_ptr<boost::thread> m_live_refresh_thread; + std::atomic<bool> m_live_refresh_thread_running; + bool m_live_refresh_enabled; + size_t m_num_transations_to_sign; + + void transaction_versions_check(const ::tools::wallet2::unsigned_tx_set & unsigned_tx, hw::tx_aux_data & aux_data); void transaction_pre_check(std::shared_ptr<messages::monero::MoneroTransactionInitRequest> init_msg); void transaction_check(const protocol::tx::TData & tdata, const hw::tx_aux_data & aux_data); + void device_state_reset_unsafe() override; + void live_refresh_start_unsafe(); + void live_refresh_finish_unsafe(); + void live_refresh_thread_main(); + + /** + * Signs particular transaction idx in the unsigned set, keeps state in the signer + */ + virtual void tx_sign(wallet_shim * wallet, + const ::tools::wallet2::unsigned_tx_set & unsigned_tx, + size_t idx, + hw::tx_aux_data & aux_data, + std::shared_ptr<protocol::tx::Signer> & signer); public: device_trezor(); @@ -69,11 +93,17 @@ namespace trezor { explicit operator bool() const override {return true;} + bool init() override; + bool release() override; + bool disconnect() override; + device_protocol_t device_protocol() const override { return PROTOCOL_COLD; }; bool has_ki_cold_sync() const override { return true; } bool has_tx_cold_sign() const override { return true; } void set_network_type(cryptonote::network_type network_type) override { this->network_type = network_type; } + void set_live_refresh_enabled(bool enabled) { m_live_refresh_enabled = enabled; } + bool live_refresh_enabled() const { return m_live_refresh_enabled; } /* ======================================================================= */ /* WALLET & ADDRESS */ @@ -100,20 +130,68 @@ namespace trezor { const boost::optional<cryptonote::network_type> & network_type = boost::none); /** + * Get_tx_key support check + */ + bool is_get_tx_key_supported() const override; + + /** + * Loads tx aux data + */ + void load_tx_key_data(::hw::device_cold::tx_key_data_t & res, const std::string & tx_aux_data) override; + + /** + * TX key load with the Trezor + */ + void get_tx_key( + std::vector<::crypto::secret_key> & tx_keys, + const ::hw::device_cold::tx_key_data_t & tx_aux_data, + const ::crypto::secret_key & view_key_priv) override; + + /** * Key image sync with the Trezor. */ void ki_sync(wallet_shim * wallet, const std::vector<::tools::wallet2::transfer_details> & transfers, hw::device_cold::exported_key_image & ski) override; + bool is_live_refresh_supported() const override; + + bool is_live_refresh_enabled() const; + + bool has_ki_live_refresh() const override; + + void live_refresh_start() override; + + void live_refresh( + const ::crypto::secret_key & view_key_priv, + const crypto::public_key& out_key, + const crypto::key_derivation& recv_derivation, + size_t real_output_index, + const cryptonote::subaddress_index& received_index, + cryptonote::keypair& in_ephemeral, + crypto::key_image& ki + ) override; + + void live_refresh_finish() override; + /** - * Signs particular transaction idx in the unsigned set, keeps state in the signer + * Letting device know the KI computation started / ended. + * During refresh */ - void tx_sign(wallet_shim * wallet, - const ::tools::wallet2::unsigned_tx_set & unsigned_tx, - size_t idx, - hw::tx_aux_data & aux_data, - std::shared_ptr<protocol::tx::Signer> & signer); + void computing_key_images(bool started) override; + + /** + * Implements hw::device interface + * called from generate_key_image_helper_precomp() + */ + bool compute_key_image( + const ::cryptonote::account_keys& ack, + const ::crypto::public_key& out_key, + const ::crypto::key_derivation& recv_derivation, + size_t real_output_index, + const ::cryptonote::subaddress_index& received_index, + ::cryptonote::keypair& in_ephemeral, + ::crypto::key_image& ki) override; /** * Signs unsigned transaction with the Trezor. diff --git a/src/device_trezor/device_trezor_base.cpp b/src/device_trezor/device_trezor_base.cpp index 5071932ee..f3d15c5e2 100644 --- a/src/device_trezor/device_trezor_base.cpp +++ b/src/device_trezor/device_trezor_base.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -44,7 +44,9 @@ namespace trezor { const uint32_t device_trezor_base::DEFAULT_BIP44_PATH[] = {0x8000002c, 0x80000080}; device_trezor_base::device_trezor_base(): m_callback(nullptr) { - +#ifdef WITH_TREZOR_DEBUGGING + m_debug = false; +#endif } device_trezor_base::~device_trezor_base() { @@ -107,6 +109,7 @@ namespace trezor { disconnect(); // Enumerate all available devices + TREZOR_AUTO_LOCK_DEVICE(); try { hw::trezor::t_transport_vect trans; @@ -130,6 +133,10 @@ namespace trezor { } m_transport->open(); + +#ifdef WITH_TREZOR_DEBUGGING + setup_debug(); +#endif return true; } catch(std::exception const& e){ @@ -139,6 +146,7 @@ namespace trezor { } bool device_trezor_base::disconnect() { + TREZOR_AUTO_LOCK_DEVICE(); m_device_state.clear(); m_features.reset(); @@ -153,6 +161,13 @@ namespace trezor { return false; } } + +#ifdef WITH_TREZOR_DEBUGGING + if (m_debug_callback) { + m_debug_callback->on_disconnect(); + m_debug_callback = nullptr; + } +#endif return true; } @@ -190,13 +205,13 @@ namespace trezor { /* Helpers */ /* ======================================================================= */ - void device_trezor_base::require_connected(){ + void device_trezor_base::require_connected() const { if (!m_transport){ throw exc::NotConnectedException(); } } - void device_trezor_base::require_initialized(){ + void device_trezor_base::require_initialized() const { if (!m_features){ throw exc::TrezorException("Device state not initialized"); } @@ -317,7 +332,7 @@ namespace trezor { /* ======================================================================= */ bool device_trezor_base::ping() { - AUTO_LOCK_CMD(); + TREZOR_AUTO_LOCK_CMD(); if (!m_transport){ MINFO("Ping failed, device not connected"); return false; @@ -351,10 +366,41 @@ namespace trezor { void device_trezor_base::device_state_reset() { - AUTO_LOCK_CMD(); + TREZOR_AUTO_LOCK_CMD(); device_state_reset_unsafe(); } +#ifdef WITH_TREZOR_DEBUGGING +#define TREZOR_CALLBACK(method, ...) do { \ + if (m_debug_callback) m_debug_callback->method(__VA_ARGS__); \ + if (m_callback) m_callback->method(__VA_ARGS__); \ +}while(0) +#define TREZOR_CALLBACK_GET(VAR, method, ...) do { \ + if (m_debug_callback) VAR = m_debug_callback->method(__VA_ARGS__); \ + if (m_callback) VAR = m_callback->method(__VA_ARGS__); \ +}while(0) + + void device_trezor_base::setup_debug(){ + if (!m_debug){ + return; + } + + if (!m_debug_callback){ + CHECK_AND_ASSERT_THROW_MES(m_transport, "Transport does not exist"); + auto debug_transport = m_transport->find_debug(); + if (debug_transport) { + m_debug_callback = std::make_shared<trezor_debug_callback>(debug_transport); + } else { + MDEBUG("Transport does not have debug link option"); + } + } + } + +#else +#define TREZOR_CALLBACK(method, ...) do { if (m_callback) m_callback->method(__VA_ARGS__); } while(0) +#define TREZOR_CALLBACK_GET(VAR, method, ...) VAR = (m_callback ? m_callback->method(__VA_ARGS__) : boost::none) +#endif + void device_trezor_base::on_button_request(GenericMessage & resp, const messages::common::ButtonRequest * msg) { CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); @@ -363,10 +409,7 @@ namespace trezor { messages::common::ButtonAck ack; write_raw(&ack); - if (m_callback){ - m_callback->on_button_request(); - } - + TREZOR_CALLBACK(on_button_request, msg->code()); resp = read_raw(); } @@ -375,15 +418,18 @@ namespace trezor { MDEBUG("on_pin_request"); CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); - epee::wipeable_string pin; + boost::optional<epee::wipeable_string> pin; + TREZOR_CALLBACK_GET(pin, on_pin_request); - if (m_callback){ - m_callback->on_pin_request(pin); + if (!pin && m_pin){ + pin = m_pin; } // TODO: remove PIN from memory messages::common::PinMatrixAck m; - m.set_pin(pin.data(), pin.size()); + if (pin) { + m.set_pin(pin.get().data(), pin.get().size()); + } resp = call_raw(&m); } @@ -391,16 +437,19 @@ namespace trezor { { CHECK_AND_ASSERT_THROW_MES(msg, "Empty message"); MDEBUG("on_passhprase_request, on device: " << msg->on_device()); - epee::wipeable_string passphrase; + boost::optional<epee::wipeable_string> passphrase; + TREZOR_CALLBACK_GET(passphrase, on_passphrase_request, msg->on_device()); - if (m_callback){ - m_callback->on_passphrase_request(msg->on_device(), passphrase); + if (!passphrase && m_passphrase){ + passphrase = m_passphrase; } + m_passphrase = boost::none; + messages::common::PassphraseAck m; - if (!msg->on_device()){ + if (!msg->on_device() && passphrase){ // TODO: remove passphrase from memory - m.set_passphrase(passphrase.data(), passphrase.size()); + m.set_passphrase(passphrase.get().data(), passphrase.get().size()); } if (!m_device_state.empty()){ @@ -421,5 +470,67 @@ namespace trezor { resp = call_raw(&m); } +#ifdef WITH_TREZOR_DEBUGGING + void device_trezor_base::wipe_device() + { + auto msg = std::make_shared<messages::management::WipeDevice>(); + auto ret = client_exchange<messages::common::Success>(msg); + (void)ret; + init_device(); + } + + void device_trezor_base::init_device() + { + auto msg = std::make_shared<messages::management::Initialize>(); + m_features = client_exchange<messages::management::Features>(msg); + } + + void device_trezor_base::load_device(const std::string & mnemonic, const std::string & pin, + bool passphrase_protection, const std::string & label, const std::string & language, + bool skip_checksum, bool expand) + { + if (m_features && m_features->initialized()){ + throw std::runtime_error("Device is initialized already. Call device.wipe() and try again."); + } + + auto msg = std::make_shared<messages::management::LoadDevice>(); + msg->set_mnemonic(mnemonic); + msg->set_pin(pin); + msg->set_passphrase_protection(passphrase_protection); + msg->set_label(label); + msg->set_language(language); + msg->set_skip_checksum(skip_checksum); + auto ret = client_exchange<messages::common::Success>(msg); + (void)ret; + + init_device(); + } + + trezor_debug_callback::trezor_debug_callback(std::shared_ptr<Transport> & debug_transport){ + m_debug_link = std::make_shared<DebugLink>(); + m_debug_link->init(debug_transport); + } + + void trezor_debug_callback::on_button_request(uint64_t code) { + if (m_debug_link) m_debug_link->press_yes(); + } + + boost::optional<epee::wipeable_string> trezor_debug_callback::on_pin_request() { + return boost::none; + } + + boost::optional<epee::wipeable_string> trezor_debug_callback::on_passphrase_request(bool on_device) { + return boost::none; + } + + void trezor_debug_callback::on_passphrase_state_request(const std::string &state) { + + } + + void trezor_debug_callback::on_disconnect(){ + if (m_debug_link) m_debug_link->close(); + } +#endif + #endif //WITH_DEVICE_TREZOR }} diff --git a/src/device_trezor/device_trezor_base.hpp b/src/device_trezor/device_trezor_base.hpp index 3c35e8aca..8c3c14b29 100644 --- a/src/device_trezor/device_trezor_base.hpp +++ b/src/device_trezor/device_trezor_base.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -42,21 +42,43 @@ #include "cryptonote_config.h" #include "trezor.hpp" +#ifdef WITH_TREZOR_DEBUGGING +#include "trezor/debug_link.hpp" +#endif + //automatic lock one more level on device ensuring the current thread is allowed to use it -#define AUTO_LOCK_CMD() \ +#define TREZOR_AUTO_LOCK_CMD() \ /* lock both mutexes without deadlock*/ \ boost::lock(device_locker, command_locker); \ /* make sure both already-locked mutexes are unlocked at the end of scope */ \ boost::lock_guard<boost::recursive_mutex> lock1(device_locker, boost::adopt_lock); \ boost::lock_guard<boost::mutex> lock2(command_locker, boost::adopt_lock) - +#define TREZOR_AUTO_LOCK_DEVICE() boost::lock_guard<boost::recursive_mutex> lock1_device(device_locker) + namespace hw { namespace trezor { #ifdef WITH_DEVICE_TREZOR class device_trezor_base; +#ifdef WITH_TREZOR_DEBUGGING + class trezor_debug_callback : public hw::i_device_callback { + public: + trezor_debug_callback()=default; + explicit trezor_debug_callback(std::shared_ptr<Transport> & debug_transport); + + void on_button_request(uint64_t code=0) override; + boost::optional<epee::wipeable_string> on_pin_request() override; + boost::optional<epee::wipeable_string> on_passphrase_request(bool on_device) override; + void on_passphrase_state_request(const std::string &state); + void on_disconnect(); + protected: + std::shared_ptr<DebugLink> m_debug_link; + }; + +#endif + /** * TREZOR device template with basic functions */ @@ -74,18 +96,27 @@ namespace trezor { std::vector<unsigned int> m_wallet_deriv_path; std::string m_device_state; // returned after passphrase entry, session std::shared_ptr<messages::management::Features> m_features; // features from the last device reset + boost::optional<epee::wipeable_string> m_pin; + boost::optional<epee::wipeable_string> m_passphrase; cryptonote::network_type network_type; +#ifdef WITH_TREZOR_DEBUGGING + std::shared_ptr<trezor_debug_callback> m_debug_callback; + bool m_debug; + + void setup_debug(); +#endif + // // Internal methods // - void require_connected(); - void require_initialized(); + void require_connected() const; + void require_initialized() const; void call_ping_unsafe(); void test_ping(); - void device_state_reset_unsafe(); + virtual void device_state_reset_unsafe(); void ensure_derivation_path() noexcept; // Communication methods @@ -103,7 +134,7 @@ namespace trezor { * @throws UnexpectedMessageException if the response message type is different than expected. * Exception contains message type and the message itself. */ - template<class t_message> + template<class t_message=google::protobuf::Message> std::shared_ptr<t_message> client_exchange(const std::shared_ptr<const google::protobuf::Message> &req, const boost::optional<messages::MessageType> & resp_type = boost::none, @@ -229,8 +260,23 @@ namespace trezor { return m_features; } + uint64_t get_version() const { + CHECK_AND_ASSERT_THROW_MES(m_features, "Features not loaded"); + CHECK_AND_ASSERT_THROW_MES(m_features->has_major_version() && m_features->has_minor_version() && m_features->has_patch_version(), "Invalid Trezor firmware version information"); + return pack_version(m_features->major_version(), m_features->minor_version(), m_features->patch_version()); + } + void set_derivation_path(const std::string &deriv_path) override; + virtual bool has_ki_live_refresh(void) const override { return false; } + + virtual void set_pin(const epee::wipeable_string & pin) override { + m_pin = pin; + } + virtual void set_passphrase(const epee::wipeable_string & passphrase) override { + m_passphrase = passphrase; + } + /* ======================================================================= */ /* SETUP/TEARDOWN */ /* ======================================================================= */ @@ -268,6 +314,23 @@ namespace trezor { void on_pin_request(GenericMessage & resp, const messages::common::PinMatrixRequest * msg); void on_passphrase_request(GenericMessage & resp, const messages::common::PassphraseRequest * msg); void on_passphrase_state_request(GenericMessage & resp, const messages::common::PassphraseStateRequest * msg); + +#ifdef WITH_TREZOR_DEBUGGING + void set_debug(bool debug){ + m_debug = debug; + } + + void set_debug_callback(std::shared_ptr<trezor_debug_callback> & debug_callback){ + m_debug_callback = debug_callback; + } + + void wipe_device(); + void init_device(); + void load_device(const std::string & mnemonic, const std::string & pin="", bool passphrase_protection=false, + const std::string & label="test", const std::string & language="english", + bool skip_checksum=false, bool expand=false); + +#endif }; #endif diff --git a/src/device_trezor/trezor.hpp b/src/device_trezor/trezor.hpp index 97dc0a957..396a27534 100644 --- a/src/device_trezor/trezor.hpp +++ b/src/device_trezor/trezor.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/device_trezor/trezor/debug_link.cpp b/src/device_trezor/trezor/debug_link.cpp new file mode 100644 index 000000000..c7ee59afe --- /dev/null +++ b/src/device_trezor/trezor/debug_link.cpp @@ -0,0 +1,90 @@ +// Copyright (c) 2017-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 "debug_link.hpp" + +namespace hw{ +namespace trezor{ + + DebugLink::DebugLink(){ + + } + + DebugLink::~DebugLink(){ + if (m_transport){ + close(); + } + } + + void DebugLink::init(std::shared_ptr<Transport> & transport){ + CHECK_AND_ASSERT_THROW_MES(!m_transport, "Already initialized"); + m_transport = transport; + m_transport->open(); + } + + void DebugLink::close(){ + CHECK_AND_ASSERT_THROW_MES(m_transport, "Not initialized"); + if (m_transport) m_transport->close(); + } + + std::shared_ptr<messages::debug::DebugLinkState> DebugLink::state(){ + return call<messages::debug::DebugLinkState>( + messages::debug::DebugLinkGetState(), + boost::make_optional(messages::MessageType_DebugLinkGetState)); + } + + void DebugLink::input_word(const std::string & word){ + messages::debug::DebugLinkDecision decision; + decision.set_input(word); + call(decision, boost::none, true); + } + + void DebugLink::input_button(bool button){ + messages::debug::DebugLinkDecision decision; + decision.set_yes_no(button); + call(decision, boost::none, true); + } + + void DebugLink::input_swipe(bool swipe){ + messages::debug::DebugLinkDecision decision; + decision.set_up_down(swipe); + call(decision, boost::none, true); + } + + void DebugLink::stop(){ + messages::debug::DebugLinkStop msg; + call(msg, boost::none, true); + } + + + + + +} +}
\ No newline at end of file diff --git a/src/device_trezor/trezor/debug_link.hpp b/src/device_trezor/trezor/debug_link.hpp new file mode 100644 index 000000000..adf5f1d8f --- /dev/null +++ b/src/device_trezor/trezor/debug_link.hpp @@ -0,0 +1,93 @@ +// Copyright (c) 2017-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. +// + +#ifndef MONERO_DEBUG_LINK_H +#define MONERO_DEBUG_LINK_H + +#include "transport.hpp" +#include "messages/messages-debug.pb.h" + + +namespace hw { +namespace trezor { + + class DebugLink { + public: + + DebugLink(); + virtual ~DebugLink(); + + void init(std::shared_ptr<Transport> & transport); + void close(); + + std::shared_ptr<messages::debug::DebugLinkState> state(); + void input_word(const std::string & word); + void input_button(bool button); + void input_swipe(bool swipe); + void press_yes() { input_button(true); } + void press_no() { input_button(false); } + void stop(); + + template<class t_message=messages::debug::DebugLinkState> + std::shared_ptr<t_message> call( + const google::protobuf::Message & req, + const boost::optional<messages::MessageType> &resp_type = boost::none, + bool no_wait = false) + { + BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value); + + m_transport->write(req); + if (no_wait){ + return nullptr; + } + + // Read the response + std::shared_ptr<google::protobuf::Message> msg_resp; + hw::trezor::messages::MessageType msg_resp_type; + m_transport->read(msg_resp, &msg_resp_type); + + messages::MessageType required_type = resp_type ? resp_type.get() : MessageMapper::get_message_wire_number<t_message>(); + if (msg_resp_type == required_type) { + return message_ptr_retype<t_message>(msg_resp); + } else if (msg_resp_type == messages::MessageType_Failure){ + throw_failure_exception(dynamic_cast<messages::common::Failure*>(msg_resp.get())); + } else { + throw exc::UnexpectedMessageException(msg_resp_type, msg_resp); + } + }; + + private: + std::shared_ptr<Transport> m_transport; + + }; + +} +} + +#endif //MONERO_DEBUG_LINK_H diff --git a/src/device_trezor/trezor/exceptions.hpp b/src/device_trezor/trezor/exceptions.hpp index 197dc43a4..41e8c2875 100644 --- a/src/device_trezor/trezor/exceptions.hpp +++ b/src/device_trezor/trezor/exceptions.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, 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 b0d1aa254..6956b369a 100644 --- a/src/device_trezor/trezor/messages_map.cpp +++ b/src/device_trezor/trezor/messages_map.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -33,6 +33,10 @@ #include "messages/messages-management.pb.h" #include "messages/messages-monero.pb.h" +#ifdef WITH_TREZOR_DEBUGGING +#include "messages/messages-debug.pb.h" +#endif + using namespace std; using namespace hw::trezor; @@ -45,6 +49,9 @@ namespace trezor "hw.trezor.messages.", "hw.trezor.messages.common.", "hw.trezor.messages.management.", +#ifdef WITH_TREZOR_DEBUGGING + "hw.trezor.messages.debug.", +#endif "hw.trezor.messages.monero." }; @@ -68,6 +75,10 @@ namespace trezor hw::trezor::messages::management::Cancel::default_instance(); hw::trezor::messages::monero::MoneroGetAddress::default_instance(); +#ifdef WITH_TREZOR_DEBUGGING + hw::trezor::messages::debug::DebugLinkDecision::default_instance(); +#endif + google::protobuf::Descriptor const * desc = nullptr; for(const string &text : PACKAGES){ desc = google::protobuf::DescriptorPool::generated_pool() diff --git a/src/device_trezor/trezor/messages_map.hpp b/src/device_trezor/trezor/messages_map.hpp index f61338f09..28fe9f4c2 100644 --- a/src/device_trezor/trezor/messages_map.hpp +++ b/src/device_trezor/trezor/messages_map.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -62,14 +62,14 @@ namespace trezor { static messages::MessageType get_message_wire_number(const google::protobuf::Message & msg); static messages::MessageType get_message_wire_number(const std::string & msg_name); - template<class t_message> + template<class t_message=google::protobuf::Message> static messages::MessageType get_message_wire_number() { BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value); return get_message_wire_number(t_message::default_instance().GetDescriptor()->name()); } }; - template<class t_message> + template<class t_message=google::protobuf::Message> std::shared_ptr<t_message> message_ptr_retype(std::shared_ptr<google::protobuf::Message> & in){ BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value); if (!in){ @@ -79,7 +79,7 @@ namespace trezor { return std::dynamic_pointer_cast<t_message>(in); } - template<class t_message> + template<class t_message=google::protobuf::Message> std::shared_ptr<t_message> message_ptr_retype_static(std::shared_ptr<google::protobuf::Message> & in){ BOOST_STATIC_ASSERT(boost::is_base_of<google::protobuf::Message, t_message>::value); if (!in){ diff --git a/src/device_trezor/trezor/protocol.cpp b/src/device_trezor/trezor/protocol.cpp index 13506a67f..db2444df6 100644 --- a/src/device_trezor/trezor/protocol.cpp +++ b/src/device_trezor/trezor/protocol.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -33,6 +33,8 @@ #include <utility> #include <boost/endian/conversion.hpp> #include <common/apply_permutation.h> +#include <common/json_util.h> +#include <crypto/hmac-keccak.h> #include <ringct/rctSigs.h> #include <ringct/bulletproofs.h> #include "cryptonote_config.h" @@ -40,6 +42,37 @@ #include <sodium/crypto_verify_32.h> #include <sodium/crypto_aead_chacha20poly1305.h> +#define GET_FIELD_STRING(name, type, jtype) field_##name = std::string(json[#name].GetString(), json[#name].GetStringLength()) +#define GET_FIELD_OTHER(name, type, jtype) field_##name = static_cast<type>(json[#name].Get##jtype()) + +#define GET_STRING_FROM_JSON(json, name, type, mandatory, def) \ + GET_FIELD_FROM_JSON_EX(json, name, type, String, mandatory, def, GET_FIELD_STRING) + +#define GET_FIELD_FROM_JSON(json, name, type, jtype, mandatory, def) \ + GET_FIELD_FROM_JSON_EX(json, name, type, jtype, mandatory, def, GET_FIELD_OTHER) + +#define GET_FIELD_FROM_JSON_EX(json, name, type, jtype, mandatory, def, VAL) \ + type field_##name = static_cast<type>(def); \ + bool field_##name##_found = false; \ + (void)field_##name##_found; \ + do if (json.HasMember(#name)) \ + { \ + if (json[#name].Is##jtype()) \ + { \ + VAL(name, type, jtype); \ + field_##name##_found = true; \ + } \ + else \ + { \ + throw std::invalid_argument("Field " #name " found in JSON, but not " #jtype); \ + } \ + } \ + else if (mandatory) \ + { \ + throw std::invalid_argument("Field " #name " not found in JSON");\ + } while(0) + + namespace hw{ namespace trezor{ namespace protocol{ @@ -84,19 +117,22 @@ namespace protocol{ namespace crypto { namespace chacha { - void decrypt(const void* ciphertext, size_t length, const uint8_t* key, const uint8_t* iv, char* plaintext){ - if (length < 16){ - throw std::invalid_argument("Ciphertext length too small"); - } + void decrypt(const void* ciphertext, size_t length, const uint8_t* key, const uint8_t* iv, char* plaintext, size_t *plaintext_len){ + CHECK_AND_ASSERT_THROW_MES(length >= TAG_SIZE, "Ciphertext length too small"); + CHECK_AND_ASSERT_THROW_MES(!plaintext_len || *plaintext_len >= (length - TAG_SIZE), "Plaintext length too small"); - unsigned long long int cip_len = length; + unsigned long long int res_len = plaintext_len ? *plaintext_len : length; auto r = crypto_aead_chacha20poly1305_ietf_decrypt( - reinterpret_cast<unsigned char *>(plaintext), &cip_len, nullptr, + reinterpret_cast<unsigned char *>(plaintext), &res_len, nullptr, static_cast<const unsigned char *>(ciphertext), length, nullptr, 0, iv, key); if (r != 0){ throw exc::Poly1305TagInvalid(); } + + if (plaintext_len){ + *plaintext_len = (size_t) res_len; + } } } @@ -185,6 +221,49 @@ namespace ki { } } + void live_refresh_ack(const ::crypto::secret_key & view_key_priv, + const ::crypto::public_key& out_key, + const std::shared_ptr<messages::monero::MoneroLiveRefreshStepAck> & ack, + ::cryptonote::keypair& in_ephemeral, + ::crypto::key_image& ki) + { + std::string str_out_key(out_key.data, sizeof(out_key.data)); + auto enc_key = protocol::tx::compute_enc_key(view_key_priv, str_out_key, ack->salt()); + + const size_t len_ciphertext = ack->key_image().size(); // IV || keys + CHECK_AND_ASSERT_THROW_MES(len_ciphertext > crypto::chacha::IV_SIZE + crypto::chacha::TAG_SIZE, "Invalid size"); + + size_t ki_len = len_ciphertext - crypto::chacha::IV_SIZE - crypto::chacha::TAG_SIZE; + std::unique_ptr<uint8_t[]> plaintext(new uint8_t[ki_len]); + uint8_t * buff = plaintext.get(); + + protocol::crypto::chacha::decrypt( + ack->key_image().data() + crypto::chacha::IV_SIZE, + len_ciphertext - crypto::chacha::IV_SIZE, + reinterpret_cast<const uint8_t *>(enc_key.data), + reinterpret_cast<const uint8_t *>(ack->key_image().data()), + reinterpret_cast<char *>(buff), &ki_len); + + CHECK_AND_ASSERT_THROW_MES(ki_len == 3*32, "Invalid size"); + ::crypto::signature sig{}; + memcpy(ki.data, buff, 32); + memcpy(sig.c.data, buff + 32, 32); + memcpy(sig.r.data, buff + 64, 32); + in_ephemeral.pub = out_key; + in_ephemeral.sec = ::crypto::null_skey; + + // Verification + std::vector<const ::crypto::public_key*> pkeys; + pkeys.push_back(&out_key); + + CHECK_AND_ASSERT_THROW_MES(rct::scalarmultKey(rct::ki2rct(ki), rct::curveOrder()) == rct::identity(), + "Key image out of validity domain: key image " << epee::string_tools::pod_to_hex(ki)); + + CHECK_AND_ASSERT_THROW_MES(::crypto::check_ring_signature((const ::crypto::hash&)ki, ki, pkeys, &sig), + "Signature failed for key image " << epee::string_tools::pod_to_hex(ki) + << ", signature " + epee::string_tools::pod_to_hex(sig) + << ", pubkey " + epee::string_tools::pod_to_hex(*pkeys[0])); + } } // Cold transaction signing @@ -198,6 +277,8 @@ namespace tx { void translate_dst_entry(MoneroTransactionDestinationEntry * dst, const cryptonote::tx_destination_entry * src){ dst->set_amount(src->amount); dst->set_is_subaddress(src->is_subaddress); + dst->set_is_integrated(src->is_integrated); + dst->set_original(src->original); translate_address(dst->mutable_addr(), &(src->addr)); } @@ -267,9 +348,29 @@ namespace tx { return std::string(buff, offset); } + ::crypto::secret_key compute_enc_key(const ::crypto::secret_key & private_view_key, const std::string & aux, const std::string & salt) + { + uint8_t hash[32]; + KECCAK_CTX ctx; + ::crypto::secret_key res; + + keccak_init(&ctx); + keccak_update(&ctx, (const uint8_t *) private_view_key.data, sizeof(private_view_key.data)); + if (!aux.empty()){ + keccak_update(&ctx, (const uint8_t *) aux.data(), aux.size()); + } + keccak_finish(&ctx, hash); + keccak(hash, sizeof(hash), hash, sizeof(hash)); + + hmac_keccak_hash(hash, (const uint8_t *) salt.data(), salt.size(), hash, sizeof(hash)); + memcpy(res.data, hash, sizeof(hash)); + memwipe(hash, sizeof(hash)); + return res; + } + TData::TData() { - in_memory = false; rsig_type = 0; + bp_version = 0; cur_input_idx = 0; cur_output_idx = 0; cur_batch_idx = 0; @@ -283,6 +384,7 @@ namespace tx { m_tx_idx = tx_idx; m_ct.tx_data = cur_tx(); m_multisig = false; + m_client_version = 1; } void Signer::extract_payment_id(){ @@ -392,8 +494,10 @@ namespace tx { m_ct.tx.version = 2; m_ct.tx.unlock_time = tx.unlock_time; + m_client_version = (m_aux_data->client_version ? m_aux_data->client_version.get() : 1); tsx_data.set_version(1); + tsx_data.set_client_version(client_version()); tsx_data.set_unlock_time(tx.unlock_time); tsx_data.set_num_inputs(static_cast<google::protobuf::uint32>(tx.sources.size())); tsx_data.set_mixin(static_cast<google::protobuf::uint32>(tx.sources[0].outputs.size() - 1)); @@ -404,6 +508,10 @@ namespace tx { auto rsig_data = tsx_data.mutable_rsig_data(); m_ct.rsig_type = get_rsig_type(tx.use_bulletproofs, tx.splitted_dsts.size()); rsig_data->set_rsig_type(m_ct.rsig_type); + if (tx.use_bulletproofs){ + m_ct.bp_version = (m_aux_data->bp_version ? m_aux_data->bp_version.get() : 1); + rsig_data->set_bp_version((uint32_t) m_ct.bp_version); + } generate_rsig_batch_sizes(m_ct.grouping_vct, m_ct.rsig_type, tx.splitted_dsts.size()); assign_to_repeatable(rsig_data->mutable_grouping(), m_ct.grouping_vct.begin(), m_ct.grouping_vct.end()); @@ -437,7 +545,6 @@ namespace tx { } void Signer::step_init_ack(std::shared_ptr<const messages::monero::MoneroTransactionInitAck> ack){ - m_ct.in_memory = false; if (ack->has_rsig_data()){ m_ct.rsig_param = std::make_shared<MoneroRsigData>(ack->rsig_data()); } @@ -505,10 +612,6 @@ namespace tx { std::shared_ptr<messages::monero::MoneroTransactionInputsPermutationRequest> Signer::step_permutation(){ sort_ki(); - if (in_memory()){ - return nullptr; - } - auto res = std::make_shared<messages::monero::MoneroTransactionInputsPermutationRequest>(); assign_to_repeatable(res->mutable_perm(), m_ct.source_permutation.begin(), m_ct.source_permutation.end()); @@ -516,15 +619,10 @@ namespace tx { } void Signer::step_permutation_ack(std::shared_ptr<const messages::monero::MoneroTransactionInputsPermutationAck> ack){ - if (in_memory()){ - return; - } + } std::shared_ptr<messages::monero::MoneroTransactionInputViniRequest> Signer::step_set_vini_input(size_t idx){ - if (in_memory()){ - return nullptr; - } CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_data.sources.size(), "Invalid transaction index"); CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx.vin.size(), "Invalid transaction index"); CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_in_hmacs.size(), "Invalid transaction index"); @@ -536,7 +634,8 @@ namespace tx { translate_src_entry(res->mutable_src_entr(), &(tx.sources[idx])); res->set_vini(cryptonote::t_serializable_object_to_blob(vini)); res->set_vini_hmac(m_ct.tx_in_hmacs[idx]); - if (!in_memory()) { + + if (client_version() == 0) { CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs.size(), "Invalid transaction index"); CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs_hmac.size(), "Invalid transaction index"); res->set_pseudo_out(m_ct.pseudo_outs[idx]); @@ -547,9 +646,7 @@ namespace tx { } void Signer::step_set_vini_input_ack(std::shared_ptr<const messages::monero::MoneroTransactionInputViniAck> ack){ - if (in_memory()){ - return; - } + } std::shared_ptr<messages::monero::MoneroTransactionAllInputsSetRequest> Signer::step_all_inputs_set(){ @@ -557,34 +654,37 @@ namespace tx { } void Signer::step_all_inputs_set_ack(std::shared_ptr<const messages::monero::MoneroTransactionAllInputsSetAck> ack){ - if (is_offloading()){ - // If offloading, expect rsig configuration. - if (!ack->has_rsig_data()){ - throw exc::ProtocolException("Rsig offloading requires rsig param"); - } + if (client_version() > 0 || !is_offloading()){ + return; + } - auto & rsig_data = ack->rsig_data(); - if (!rsig_data.has_mask()){ - throw exc::ProtocolException("Gamma masks not present in offloaded version"); - } + // If offloading, expect rsig configuration. + if (!ack->has_rsig_data()){ + throw exc::ProtocolException("Rsig offloading requires rsig param"); + } - auto & mask = rsig_data.mask(); - if (mask.size() != 32 * num_outputs()){ - throw exc::ProtocolException("Invalid number of gamma masks"); - } + auto & rsig_data = ack->rsig_data(); + if (!rsig_data.has_mask()){ + throw exc::ProtocolException("Gamma masks not present in offloaded version"); + } - m_ct.rsig_gamma.reserve(num_outputs()); - for(size_t c=0; c < num_outputs(); ++c){ - rct::key cmask{}; - memcpy(cmask.bytes, mask.data() + c * 32, 32); - m_ct.rsig_gamma.emplace_back(cmask); - } + auto & mask = rsig_data.mask(); + if (mask.size() != 32 * num_outputs()){ + throw exc::ProtocolException("Invalid number of gamma masks"); + } + + m_ct.rsig_gamma.reserve(num_outputs()); + for(size_t c=0; c < num_outputs(); ++c){ + rct::key cmask{}; + memcpy(cmask.bytes, mask.data() + c * 32, 32); + m_ct.rsig_gamma.emplace_back(cmask); } } std::shared_ptr<messages::monero::MoneroTransactionSetOutputRequest> Signer::step_set_output(size_t idx){ CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_data.splitted_dsts.size(), "Invalid transaction index"); CHECK_AND_ASSERT_THROW_MES(idx < m_ct.tx_out_entr_hmacs.size(), "Invalid transaction index"); + CHECK_AND_ASSERT_THROW_MES(is_req_bulletproof(), "Borromean rsig not supported"); m_ct.cur_output_idx = idx; m_ct.cur_output_in_batch_idx += 1; // assumes sequential call to step_set_output() @@ -595,48 +695,11 @@ namespace tx { res->set_dst_entr_hmac(m_ct.tx_out_entr_hmacs[idx]); // Range sig offloading to the host - if (!is_offloading()) { - return res; - } - - CHECK_AND_ASSERT_THROW_MES(m_ct.cur_batch_idx < m_ct.grouping_vct.size(), "Invalid batch index"); - if (m_ct.grouping_vct[m_ct.cur_batch_idx] > m_ct.cur_output_in_batch_idx) { - return res; - } - - auto rsig_data = res->mutable_rsig_data(); - auto batch_size = m_ct.grouping_vct[m_ct.cur_batch_idx]; - - if (!is_req_bulletproof()){ - if (batch_size > 1){ - throw std::invalid_argument("Borromean cannot batch outputs"); - } - - CHECK_AND_ASSERT_THROW_MES(idx < m_ct.rsig_gamma.size(), "Invalid gamma index"); - rct::key C{}, mask = m_ct.rsig_gamma[idx]; - auto genRsig = rct::proveRange(C, mask, cur_dst.amount); // TODO: rsig with given mask - auto serRsig = cn_serialize(genRsig); - m_ct.tx_out_rsigs.emplace_back(genRsig); - rsig_data->set_rsig(serRsig); - - } else { - std::vector<uint64_t> amounts; - rct::keyV masks; - CHECK_AND_ASSERT_THROW_MES(idx + 1 >= batch_size, "Invalid index for batching"); - - for(size_t i = 0; i < batch_size; ++i){ - const size_t bidx = 1 + idx - batch_size + i; - CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.tx_data.splitted_dsts.size(), "Invalid gamma index"); - CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.rsig_gamma.size(), "Invalid gamma index"); - - amounts.push_back(m_ct.tx_data.splitted_dsts[bidx].amount); - masks.push_back(m_ct.rsig_gamma[bidx]); - } - - auto bp = bulletproof_PROVE(amounts, masks); - auto serRsig = cn_serialize(bp); - m_ct.tx_out_rsigs.emplace_back(bp); - rsig_data->set_rsig(serRsig); + // ClientV0 sends offloaded BP with the last message in the batch. + // ClientV1 needs additional message after the last message in the batch as BP uses deterministic masks. + if (client_version() == 0 && is_offloading() && should_compute_bp_now()) { + auto rsig_data = res->mutable_rsig_data(); + compute_bproof(*rsig_data); } return res; @@ -644,7 +707,6 @@ namespace tx { void Signer::step_set_output_ack(std::shared_ptr<const messages::monero::MoneroTransactionSetOutputAck> ack){ cryptonote::tx_out tx_out; - rct::rangeSig range_sig{}; rct::Bulletproof bproof{}; rct::ctkey out_pk{}; rct::ecdhTuple ecdh{}; @@ -658,12 +720,12 @@ namespace tx { if (rsig_data.has_rsig() && !rsig_data.rsig().empty()){ has_rsig = true; rsig_buff = rsig_data.rsig(); + } - } else if (rsig_data.rsig_parts_size() > 0){ - has_rsig = true; - for (const auto &it : rsig_data.rsig_parts()) { - rsig_buff += it; - } + if (client_version() >= 1 && rsig_data.has_mask()){ + rct::key cmask{}; + string_to_key(cmask, rsig_data.mask()); + m_ct.rsig_gamma.emplace_back(cmask); } } @@ -675,12 +737,13 @@ namespace tx { throw exc::ProtocolException("Cannot deserialize out_pk"); } - if (!cn_deserialize(ack->ecdh_info(), ecdh)){ - throw exc::ProtocolException("Cannot deserialize ecdhtuple"); - } - - if (has_rsig && !is_req_bulletproof() && !cn_deserialize(rsig_buff, range_sig)){ - throw exc::ProtocolException("Cannot deserialize rangesig"); + if (m_ct.bp_version <= 1) { + if (!cn_deserialize(ack->ecdh_info(), ecdh)){ + throw exc::ProtocolException("Cannot deserialize ecdhtuple"); + } + } else { + CHECK_AND_ASSERT_THROW_MES(8 == ack->ecdh_info().size(), "Invalid ECDH.amount size"); + memcpy(ecdh.amount.bytes, ack->ecdh_info().data(), 8); } if (has_rsig && is_req_bulletproof() && !cn_deserialize(rsig_buff, bproof)){ @@ -692,35 +755,77 @@ namespace tx { m_ct.tx_out_pk.emplace_back(out_pk); m_ct.tx_out_ecdh.emplace_back(ecdh); - if (!has_rsig){ + // ClientV0, if no rsig was generated on Trezor, do not continue. + // ClientV1+ generates BP after all masks in the current batch are generated + if (!has_rsig || (client_version() >= 1 && is_offloading())){ return; } - if (is_req_bulletproof()){ - CHECK_AND_ASSERT_THROW_MES(m_ct.cur_batch_idx < m_ct.grouping_vct.size(), "Invalid batch index"); - auto batch_size = m_ct.grouping_vct[m_ct.cur_batch_idx]; - for (size_t i = 0; i < batch_size; ++i){ - const size_t bidx = 1 + m_ct.cur_output_idx - batch_size + i; - CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.tx_out_pk.size(), "Invalid out index"); + process_bproof(bproof); + m_ct.cur_batch_idx += 1; + m_ct.cur_output_in_batch_idx = 0; + } - rct::key commitment = m_ct.tx_out_pk[bidx].mask; - commitment = rct::scalarmultKey(commitment, rct::INV_EIGHT); - bproof.V.push_back(commitment); - } + bool Signer::should_compute_bp_now() const { + CHECK_AND_ASSERT_THROW_MES(m_ct.cur_batch_idx < m_ct.grouping_vct.size(), "Invalid batch index"); + return m_ct.grouping_vct[m_ct.cur_batch_idx] <= m_ct.cur_output_in_batch_idx; + } - m_ct.tx_out_rsigs.emplace_back(bproof); - if (!rct::bulletproof_VERIFY(boost::get<rct::Bulletproof>(m_ct.tx_out_rsigs.back()))) { - throw exc::ProtocolException("Returned range signature is invalid"); - } + void Signer::compute_bproof(messages::monero::MoneroTransactionRsigData & rsig_data){ + auto batch_size = m_ct.grouping_vct[m_ct.cur_batch_idx]; + std::vector<uint64_t> amounts; + rct::keyV masks; + CHECK_AND_ASSERT_THROW_MES(m_ct.cur_output_idx + 1 >= batch_size, "Invalid index for batching"); - } else { - m_ct.tx_out_rsigs.emplace_back(range_sig); + for(size_t i = 0; i < batch_size; ++i){ + const size_t bidx = 1 + m_ct.cur_output_idx - batch_size + i; + CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.tx_data.splitted_dsts.size(), "Invalid gamma index"); + CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.rsig_gamma.size(), "Invalid gamma index"); - if (!rct::verRange(out_pk.mask, boost::get<rct::rangeSig>(m_ct.tx_out_rsigs.back()))) { - throw exc::ProtocolException("Returned range signature is invalid"); - } + amounts.push_back(m_ct.tx_data.splitted_dsts[bidx].amount); + masks.push_back(m_ct.rsig_gamma[bidx]); } + auto bp = bulletproof_PROVE(amounts, masks); + auto serRsig = cn_serialize(bp); + m_ct.tx_out_rsigs.emplace_back(bp); + rsig_data.set_rsig(serRsig); + } + + void Signer::process_bproof(rct::Bulletproof & bproof){ + CHECK_AND_ASSERT_THROW_MES(m_ct.cur_batch_idx < m_ct.grouping_vct.size(), "Invalid batch index"); + auto batch_size = m_ct.grouping_vct[m_ct.cur_batch_idx]; + for (size_t i = 0; i < batch_size; ++i){ + const size_t bidx = 1 + m_ct.cur_output_idx - batch_size + i; + CHECK_AND_ASSERT_THROW_MES(bidx < m_ct.tx_out_pk.size(), "Invalid out index"); + + rct::key commitment = m_ct.tx_out_pk[bidx].mask; + commitment = rct::scalarmultKey(commitment, rct::INV_EIGHT); + bproof.V.push_back(commitment); + } + + m_ct.tx_out_rsigs.emplace_back(bproof); + if (!rct::bulletproof_VERIFY(boost::get<rct::Bulletproof>(m_ct.tx_out_rsigs.back()))) { + throw exc::ProtocolException("Returned range signature is invalid"); + } + } + + std::shared_ptr<messages::monero::MoneroTransactionSetOutputRequest> Signer::step_rsig(size_t idx){ + if (client_version() == 0 || !is_offloading() || !should_compute_bp_now()){ + return nullptr; + } + + auto res = std::make_shared<messages::monero::MoneroTransactionSetOutputRequest>(); + auto & cur_dst = m_ct.tx_data.splitted_dsts[idx]; + translate_dst_entry(res->mutable_dst_entr(), &cur_dst); + res->set_dst_entr_hmac(m_ct.tx_out_entr_hmacs[idx]); + + compute_bproof(*(res->mutable_rsig_data())); + res->set_is_offloaded_bp(true); + return res; + } + + void Signer::step_set_rsig_ack(std::shared_ptr<const messages::monero::MoneroTransactionSetOutputAck> ack){ m_ct.cur_batch_idx += 1; m_ct.cur_output_in_batch_idx = 0; } @@ -814,12 +919,11 @@ namespace tx { res->set_vini_hmac(m_ct.tx_in_hmacs[idx]); res->set_pseudo_out_alpha(m_ct.alphas[idx]); res->set_spend_key(m_ct.spend_encs[idx]); - if (!in_memory()){ - CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs.size(), "Invalid transaction index"); - CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs_hmac.size(), "Invalid transaction index"); - res->set_pseudo_out(m_ct.pseudo_outs[idx]); - res->set_pseudo_out_hmac(m_ct.pseudo_outs_hmac[idx]); - } + + CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs.size(), "Invalid transaction index"); + CHECK_AND_ASSERT_THROW_MES(idx < m_ct.pseudo_outs_hmac.size(), "Invalid transaction index"); + res->set_pseudo_out(m_ct.pseudo_outs[idx]); + res->set_pseudo_out_hmac(m_ct.pseudo_outs_hmac[idx]); return res; } @@ -829,6 +933,19 @@ namespace tx { throw exc::ProtocolException("Cannot deserialize mg[i]"); } + // Sync updated pseudo_outputs, client_version>=1, HF10+ + if (client_version() >= 1 && ack->has_pseudo_out()){ + CHECK_AND_ASSERT_THROW_MES(m_ct.cur_input_idx < m_ct.pseudo_outs.size(), "Invalid pseudo-out index"); + m_ct.pseudo_outs[m_ct.cur_input_idx] = ack->pseudo_out(); + if (is_bulletproof()){ + CHECK_AND_ASSERT_THROW_MES(m_ct.cur_input_idx < m_ct.rv->p.pseudoOuts.size(), "Invalid pseudo-out index"); + string_to_key(m_ct.rv->p.pseudoOuts[m_ct.cur_input_idx], ack->pseudo_out()); + } else { + CHECK_AND_ASSERT_THROW_MES(m_ct.cur_input_idx < m_ct.rv->pseudoOuts.size(), "Invalid pseudo-out index"); + string_to_key(m_ct.rv->pseudoOuts[m_ct.cur_input_idx], ack->pseudo_out()); + } + } + m_ct.rv->p.MGs.push_back(mg); } @@ -841,14 +958,14 @@ namespace tx { if (m_multisig){ auto & cout_key = ack->cout_key(); for(auto & cur : m_ct.couts){ - if (cur.size() != 12 + 32){ + if (cur.size() != crypto::chacha::IV_SIZE + 32){ throw std::invalid_argument("Encrypted cout has invalid length"); } char buff[32]; auto data = cur.data(); - crypto::chacha::decrypt(data + 12, 32, reinterpret_cast<const uint8_t *>(cout_key.data()), reinterpret_cast<const uint8_t *>(data), buff); + crypto::chacha::decrypt(data + crypto::chacha::IV_SIZE, 32, reinterpret_cast<const uint8_t *>(cout_key.data()), reinterpret_cast<const uint8_t *>(data), buff); m_ct.couts_dec.emplace_back(buff, 32); } } @@ -887,6 +1004,82 @@ namespace tx { return sb.GetString(); } + void load_tx_key_data(hw::device_cold::tx_key_data_t & res, const std::string & data) + { + rapidjson::Document json; + + // The contents should be JSON if the wallet follows the new format. + if (json.Parse(data.c_str()).HasParseError()) + { + throw std::invalid_argument("Data parsing error"); + } + else if(!json.IsObject()) + { + throw std::invalid_argument("Data parsing error - not an object"); + } + + GET_FIELD_FROM_JSON(json, version, int, Int, true, -1); + GET_STRING_FROM_JSON(json, salt1, std::string, true, std::string()); + GET_STRING_FROM_JSON(json, salt2, std::string, true, std::string()); + GET_STRING_FROM_JSON(json, enc_keys, std::string, true, std::string()); + GET_STRING_FROM_JSON(json, tx_prefix_hash, std::string, false, std::string()); + + if (field_version != 1) + { + throw std::invalid_argument("Unknown version"); + } + + res.salt1 = field_salt1; + res.salt2 = field_salt2; + res.tx_enc_keys = field_enc_keys; + res.tx_prefix_hash = field_tx_prefix_hash; + } + + std::shared_ptr<messages::monero::MoneroGetTxKeyRequest> get_tx_key( + const hw::device_cold::tx_key_data_t & tx_data) + { + auto req = std::make_shared<messages::monero::MoneroGetTxKeyRequest>(); + req->set_salt1(tx_data.salt1); + req->set_salt2(tx_data.salt2); + req->set_tx_enc_keys(tx_data.tx_enc_keys); + req->set_tx_prefix_hash(tx_data.tx_prefix_hash); + req->set_reason(0); + + return req; + } + + void get_tx_key_ack( + std::vector<::crypto::secret_key> & tx_keys, + const std::string & tx_prefix_hash, + const ::crypto::secret_key & view_key_priv, + std::shared_ptr<const messages::monero::MoneroGetTxKeyAck> ack + ) + { + auto enc_key = protocol::tx::compute_enc_key(view_key_priv, tx_prefix_hash, ack->salt()); + auto & encrypted_keys = ack->has_tx_derivations() ? ack->tx_derivations() : ack->tx_keys(); + + const size_t len_ciphertext = encrypted_keys.size(); // IV || keys || TAG + CHECK_AND_ASSERT_THROW_MES(len_ciphertext > crypto::chacha::IV_SIZE + crypto::chacha::TAG_SIZE, "Invalid size"); + + size_t keys_len = len_ciphertext - crypto::chacha::IV_SIZE - crypto::chacha::TAG_SIZE; + std::unique_ptr<uint8_t[]> plaintext(new uint8_t[keys_len]); + + protocol::crypto::chacha::decrypt( + encrypted_keys.data() + crypto::chacha::IV_SIZE, + len_ciphertext - crypto::chacha::IV_SIZE, + reinterpret_cast<const uint8_t *>(enc_key.data), + reinterpret_cast<const uint8_t *>(encrypted_keys.data()), + reinterpret_cast<char *>(plaintext.get()), &keys_len); + + CHECK_AND_ASSERT_THROW_MES(keys_len % 32 == 0, "Invalid size"); + tx_keys.resize(keys_len / 32); + + for(unsigned i = 0; i < keys_len / 32; ++i) + { + memcpy(tx_keys[i].data, plaintext.get() + 32 * i, 32); + } + memwipe(plaintext.get(), keys_len); + } } } diff --git a/src/device_trezor/trezor/protocol.hpp b/src/device_trezor/trezor/protocol.hpp index ce0361640..98ca52565 100644 --- a/src/device_trezor/trezor/protocol.hpp +++ b/src/device_trezor/trezor/protocol.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -92,11 +92,14 @@ namespace protocol{ // Crypto / encryption namespace crypto { namespace chacha { + // Constants as defined in RFC 7539. + const unsigned IV_SIZE = 12; + const unsigned TAG_SIZE = 16; // crypto_aead_chacha20poly1305_IETF_ABYTES; /** * Chacha20Poly1305 decryption with tag verification. RFC 7539. */ - void decrypt(const void* ciphertext, size_t length, const uint8_t* key, const uint8_t* iv, char* plaintext); + void decrypt(const void* ciphertext, size_t length, const uint8_t* key, const uint8_t* iv, char* plaintext, size_t *plaintext_len=nullptr); } } @@ -129,6 +132,14 @@ namespace ki { const std::vector<tools::wallet2::transfer_details> & transfers, std::shared_ptr<messages::monero::MoneroKeyImageExportInitRequest> & req); + /** + * Processes Live refresh step response, parses KI, checks the signature + */ + void live_refresh_ack(const ::crypto::secret_key & view_key_priv, + const ::crypto::public_key& out_key, + const std::shared_ptr<messages::monero::MoneroLiveRefreshStepAck> & ack, + ::cryptonote::keypair& in_ephemeral, + ::crypto::key_image& ki); } // Cold transaction signing @@ -153,6 +164,7 @@ namespace tx { std::string hash_addr(const MoneroAccountPublicAddress * addr, boost::optional<uint64_t> amount = boost::none, boost::optional<bool> is_subaddr = boost::none); std::string hash_addr(const std::string & spend_key, const std::string & view_key, boost::optional<uint64_t> amount = boost::none, boost::optional<bool> is_subaddr = boost::none); std::string hash_addr(const ::crypto::public_key * spend_key, const ::crypto::public_key * view_key, boost::optional<uint64_t> amount = boost::none, boost::optional<bool> is_subaddr = boost::none); + ::crypto::secret_key compute_enc_key(const ::crypto::secret_key & private_view_key, const std::string & aux, const std::string & salt); typedef boost::variant<rct::rangeSig, rct::Bulletproof> rsig_v; @@ -164,8 +176,8 @@ namespace tx { TsxData tsx_data; tx_construction_data tx_data; cryptonote::transaction tx; - bool in_memory; unsigned rsig_type; + int bp_version; std::vector<uint64_t> grouping_vct; std::shared_ptr<MoneroRsigData> rsig_param; size_t cur_input_idx; @@ -206,6 +218,7 @@ namespace tx { const unsigned_tx_set * m_unsigned_tx; hw::tx_aux_data * m_aux_data; + unsigned m_client_version; bool m_multisig; const tx_construction_data & cur_tx(){ @@ -215,6 +228,9 @@ namespace tx { void extract_payment_id(); void compute_integrated_indices(TsxData * tsx_data); + bool should_compute_bp_now() const; + void compute_bproof(messages::monero::MoneroTransactionRsigData & rsig_data); + void process_bproof(rct::Bulletproof & bproof); public: Signer(wallet_shim * wallet2, const unsigned_tx_set * unsigned_tx, size_t tx_idx = 0, hw::tx_aux_data * aux_data = nullptr); @@ -238,6 +254,9 @@ namespace tx { std::shared_ptr<messages::monero::MoneroTransactionSetOutputRequest> step_set_output(size_t idx); void step_set_output_ack(std::shared_ptr<const messages::monero::MoneroTransactionSetOutputAck> ack); + std::shared_ptr<messages::monero::MoneroTransactionSetOutputRequest> step_rsig(size_t idx); + void step_set_rsig_ack(std::shared_ptr<const messages::monero::MoneroTransactionSetOutputAck> ack); + std::shared_ptr<messages::monero::MoneroTransactionAllOutSetRequest> step_all_outs_set(); void step_all_outs_set_ack(std::shared_ptr<const messages::monero::MoneroTransactionAllOutSetAck> ack, hw::device &hwdev); @@ -249,8 +268,8 @@ namespace tx { std::string store_tx_aux_info(); - bool in_memory() const { - return m_ct.in_memory; + unsigned client_version() const { + return m_client_version; } bool is_simple() const { @@ -290,6 +309,18 @@ namespace tx { } }; + // TX Key decryption + void load_tx_key_data(hw::device_cold::tx_key_data_t & res, const std::string & data); + + std::shared_ptr<messages::monero::MoneroGetTxKeyRequest> get_tx_key( + const hw::device_cold::tx_key_data_t & tx_data); + + void get_tx_key_ack( + std::vector<::crypto::secret_key> & tx_keys, + const std::string & tx_prefix_hash, + const ::crypto::secret_key & view_key_priv, + std::shared_ptr<const messages::monero::MoneroGetTxKeyAck> ack + ); } } diff --git a/src/device_trezor/trezor/tools/build_protob.py b/src/device_trezor/trezor/tools/build_protob.py index 2611f3296..eb32f6b4d 100644 --- a/src/device_trezor/trezor/tools/build_protob.py +++ b/src/device_trezor/trezor/tools/build_protob.py @@ -2,6 +2,12 @@ import os import subprocess import sys +import argparse + + +parser = argparse.ArgumentParser() +parser.add_argument("-d", "--debug-msg", default=False, action="store_const", const=True, help="Build debug messages") +args = parser.parse_args() CWD = os.path.dirname(os.path.realpath(__file__)) ROOT_DIR = os.path.abspath(os.path.join(CWD, "..", "..", "..", "..")) @@ -24,6 +30,10 @@ try: "messages-management.proto", "messages-monero.proto", ] + + if args.debug_msg: + selected += ["messages-debug.proto"] + proto_srcs = [os.path.join(TREZOR_COMMON, "protob", x) for x in selected] exec_args = [ sys.executable, diff --git a/src/device_trezor/trezor/transport.cpp b/src/device_trezor/trezor/transport.cpp index cd66e59e8..991ba3395 100644 --- a/src/device_trezor/trezor/transport.cpp +++ b/src/device_trezor/trezor/transport.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -84,6 +84,17 @@ namespace trezor{ return std::string(in.GetString()); } + uint64_t pack_version(uint32_t major, uint32_t minor, uint32_t patch) + { + // packing (major, minor, patch) to 64 B: 16 B | 24 B | 24 B + const unsigned bits_1 = 16; + const unsigned bits_2 = 24; + const uint32_t mask_1 = (1 << bits_1) - 1; + const uint32_t mask_2 = (1 << bits_2) - 1; + CHECK_AND_ASSERT_THROW_MES(major <= mask_1 && minor <= mask_2 && patch <= mask_2, "Version numbers overflow packing scheme"); + return patch | (((uint64_t)minor) << bits_2) | (((uint64_t)major) << (bits_1 + bits_2)); + } + // // Helpers // @@ -212,6 +223,40 @@ namespace trezor{ msg = msg_wrap; } + Transport::Transport(): m_open_counter(0) { + + } + + bool Transport::pre_open(){ + if (m_open_counter > 0){ + MTRACE("Already opened, count: " << m_open_counter); + m_open_counter += 1; + return false; + + } else if (m_open_counter < 0){ + MTRACE("Negative open value: " << m_open_counter); + + } + + // Caller should set m_open_counter to 1 after open + m_open_counter = 0; + return true; + } + + bool Transport::pre_close(){ + m_open_counter -= 1; + + if (m_open_counter < 0){ + MDEBUG("Already closed. Counter " << m_open_counter); + + } else if (m_open_counter == 0) { + return true; + + } + + return false; + } + // // Bridge transport // @@ -246,6 +291,10 @@ namespace trezor{ } void BridgeTransport::open() { + if (!pre_open()){ + return; + } + if (!m_device_path){ throw exc::CommunicationException("Coud not open, empty device path"); } @@ -259,9 +308,15 @@ namespace trezor{ } m_session = boost::make_optional(json_get_string(bridge_res["session"])); + m_open_counter = 1; } void BridgeTransport::close() { + if (!pre_close()){ + return; + } + + MTRACE("Closing Trezor:BridgeTransport"); if (!m_device_path || !m_session){ throw exc::CommunicationException("Device not open"); } @@ -423,6 +478,10 @@ namespace trezor{ } void UdpTransport::open() { + if (!pre_open()){ + return; + } + udp::resolver resolver(m_io_service); udp::resolver::query query(udp::v4(), m_device_host, std::to_string(m_device_port)); m_endpoint = *resolver.resolve(query); @@ -434,10 +493,16 @@ namespace trezor{ check_deadline(); m_proto->session_begin(*this); + m_open_counter = 1; } void UdpTransport::close() { - if (!m_socket){ + if (!pre_close()){ + return; + } + + MTRACE("Closing Trezor:UdpTransport"); + if (!m_socket) { throw exc::CommunicationException("Socket is already closed"); } @@ -446,6 +511,19 @@ namespace trezor{ m_socket = nullptr; } + std::shared_ptr<Transport> UdpTransport::find_debug() { +#ifdef WITH_TREZOR_DEBUGGING + std::shared_ptr<UdpTransport> t = std::make_shared<UdpTransport>(); + t->m_proto = std::make_shared<ProtocolV1>(); + t->m_device_host = m_device_host; + t->m_device_port = m_device_port + 1; + return t; +#else + MINFO("Debug link is disabled in production"); + return nullptr; +#endif + } + void UdpTransport::write_chunk(const void * buff, size_t size){ require_socket(); @@ -660,8 +738,7 @@ namespace trezor{ WebUsbTransport::WebUsbTransport( boost::optional<libusb_device_descriptor*> descriptor, boost::optional<std::shared_ptr<Protocol>> proto - ): m_conn_count(0), - m_usb_session(nullptr), m_usb_device(nullptr), m_usb_device_handle(nullptr), + ): m_usb_session(nullptr), m_usb_device(nullptr), m_usb_device_handle(nullptr), m_bus_id(-1), m_device_addr(-1) { if (descriptor){ @@ -672,7 +749,7 @@ namespace trezor{ m_proto = proto ? proto.get() : std::make_shared<ProtocolV1>(); -#ifdef WITH_TREZOR_DEBUG +#ifdef WITH_TREZOR_DEBUGGING m_debug_mode = false; #endif } @@ -757,12 +834,10 @@ namespace trezor{ }; void WebUsbTransport::open() { - const int interface = get_interface(); - if (m_conn_count > 0){ - MTRACE("Already opened, count: " << m_conn_count); - m_conn_count += 1; + if (!pre_open()){ return; } + const int interface = get_interface(); #define TREZOR_DESTROY_SESSION() do { libusb_exit(m_usb_session); m_usb_session = nullptr; } while(0) @@ -840,45 +915,55 @@ namespace trezor{ throw exc::DeviceAcquireException("Unable to claim libusb device"); } - m_conn_count = 1; + m_open_counter = 1; m_proto->session_begin(*this); #undef TREZOR_DESTROY_SESSION }; void WebUsbTransport::close() { - m_conn_count -= 1; - - if (m_conn_count < 0){ - MERROR("Close counter is negative: " << m_conn_count); - - } else if (m_conn_count == 0){ - MTRACE("Closing webusb device"); + if (!pre_close()){ + return; + } - m_proto->session_end(*this); + MTRACE("Closing Trezor:WebUsbTransport"); + m_proto->session_end(*this); - int r = libusb_release_interface(m_usb_device_handle, get_interface()); - if (r != 0){ - MERROR("Could not release libusb interface: " << r); - } + int r = libusb_release_interface(m_usb_device_handle, get_interface()); + if (r != 0){ + MERROR("Could not release libusb interface: " << r); + } - m_usb_device = nullptr; - if (m_usb_device_handle) { - libusb_close(m_usb_device_handle); - m_usb_device_handle = nullptr; - } + m_usb_device = nullptr; + if (m_usb_device_handle) { + libusb_close(m_usb_device_handle); + m_usb_device_handle = nullptr; + } - if (m_usb_session) { - libusb_exit(m_usb_session); - m_usb_session = nullptr; - } + if (m_usb_session) { + libusb_exit(m_usb_session); + m_usb_session = nullptr; } }; + std::shared_ptr<Transport> WebUsbTransport::find_debug() { +#ifdef WITH_TREZOR_DEBUGGING + require_device(); + auto t = std::make_shared<WebUsbTransport>(boost::make_optional(m_usb_device_desc.get())); + t->m_bus_id = m_bus_id; + t->m_device_addr = m_device_addr; + t->m_port_numbers = m_port_numbers; + t->m_debug_mode = true; + return t; +#else + MINFO("Debug link is disabled in production"); + return nullptr; +#endif + } int WebUsbTransport::get_interface() const{ const int INTERFACE_NORMAL = 0; -#ifdef WITH_TREZOR_DEBUG +#ifdef WITH_TREZOR_DEBUGGING const int INTERFACE_DEBUG = 1; return m_debug_mode ? INTERFACE_DEBUG : INTERFACE_NORMAL; #else @@ -888,7 +973,7 @@ namespace trezor{ unsigned char WebUsbTransport::get_endpoint() const{ const unsigned char ENDPOINT_NORMAL = 1; -#ifdef WITH_TREZOR_DEBUG +#ifdef WITH_TREZOR_DEBUGGING const unsigned char ENDPOINT_DEBUG = 2; return m_debug_mode ? ENDPOINT_DEBUG : ENDPOINT_NORMAL; #else @@ -1047,4 +1132,3 @@ namespace trezor{ } } - diff --git a/src/device_trezor/trezor/transport.hpp b/src/device_trezor/trezor/transport.hpp index 1cf0daa85..2945b3184 100644 --- a/src/device_trezor/trezor/transport.hpp +++ b/src/device_trezor/trezor/transport.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -62,6 +62,8 @@ namespace trezor { const std::string DEFAULT_BRIDGE = "127.0.0.1:21325"; + uint64_t pack_version(uint32_t major, uint32_t minor=0, uint32_t patch=0); + // Base HTTP comm serialization. bool t_serialize(const std::string & in, std::string & out); bool t_serialize(const json_val & in, std::string & out); @@ -134,7 +136,7 @@ namespace trezor { class Transport { public: - Transport() = default; + Transport(); virtual ~Transport() = default; virtual bool ping() { return false; }; @@ -144,10 +146,16 @@ namespace trezor { virtual void close(){}; virtual void write(const google::protobuf::Message & req) =0; virtual void read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr) =0; + virtual std::shared_ptr<Transport> find_debug() { return nullptr; }; virtual void write_chunk(const void * buff, size_t size) { }; virtual size_t read_chunk(void * buff, size_t size) { return 0; }; virtual std::ostream& dump(std::ostream& o) const { return o << "Transport<>"; } + protected: + long m_open_counter; + + virtual bool pre_open(); + virtual bool pre_close(); }; // Bridge transport @@ -212,6 +220,7 @@ namespace trezor { void open() override; void close() override; + std::shared_ptr<Transport> find_debug() override; void write(const google::protobuf::Message &req) override; void read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr) override; @@ -259,6 +268,7 @@ namespace trezor { void open() override; void close() override; + std::shared_ptr<Transport> find_debug() override; void write(const google::protobuf::Message &req) override; void read(std::shared_ptr<google::protobuf::Message> & msg, messages::MessageType * msg_type=nullptr) override; @@ -274,7 +284,6 @@ namespace trezor { int get_interface() const; unsigned char get_endpoint() const; - int m_conn_count; std::shared_ptr<Protocol> m_proto; libusb_context *m_usb_session; @@ -285,7 +294,7 @@ namespace trezor { int m_bus_id; int m_device_addr; -#ifdef WITH_TREZOR_DEBUG +#ifdef WITH_TREZOR_DEBUGGING bool m_debug_mode; #endif }; @@ -309,7 +318,7 @@ namespace trezor { /** * Transforms path to the particular transport */ - template<class t_transport> + template<class t_transport=Transport> std::shared_ptr<t_transport> transport_typed(const std::string & path){ auto t = transport(path); if (!t){ @@ -362,7 +371,7 @@ namespace trezor { * @throws UnexpectedMessageException if the response message type is different than expected. * Exception contains message type and the message itself. */ - template<class t_message> + template<class t_message=google::protobuf::Message> std::shared_ptr<t_message> exchange_message(Transport & transport, const google::protobuf::Message & req, boost::optional<messages::MessageType> resp_type = boost::none) diff --git a/src/device_trezor/trezor/trezor_defs.hpp b/src/device_trezor/trezor/trezor_defs.hpp index 30e76eadc..f0697cdb5 100644 --- a/src/device_trezor/trezor/trezor_defs.hpp +++ b/src/device_trezor/trezor/trezor_defs.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/gen_multisig/CMakeLists.txt b/src/gen_multisig/CMakeLists.txt index 18a6a9efe..3a5e29273 100644 --- a/src/gen_multisig/CMakeLists.txt +++ b/src/gen_multisig/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2018, The Monero Project +# Copyright (c) 2017-2019, The Monero Project # # All rights reserved. # diff --git a/src/gen_multisig/gen_multisig.cpp b/src/gen_multisig/gen_multisig.cpp index 8262e86f7..06019c50e 100644 --- a/src/gen_multisig/gen_multisig.cpp +++ b/src/gen_multisig/gen_multisig.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/lmdb/CMakeLists.txt b/src/lmdb/CMakeLists.txt new file mode 100644 index 000000000..1f369f114 --- /dev/null +++ b/src/lmdb/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright (c) 2014-2018, The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +set(lmdb_sources database.cpp error.cpp table.cpp value_stream.cpp) +set(lmdb_headers database.h error.h key_stream.h table.h transaction.h util.h value_stream.h) + +monero_add_library(lmdb_lib ${lmdb_sources} ${lmdb_headers}) +target_link_libraries(lmdb_lib common ${LMDB_LIBRARY}) diff --git a/src/lmdb/database.cpp b/src/lmdb/database.cpp new file mode 100644 index 000000000..c6b244671 --- /dev/null +++ b/src/lmdb/database.cpp @@ -0,0 +1,187 @@ +// Copyright (c) 2014-2018, The Monero Project +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include "database.h" +#include "lmdb/error.h" +#include "lmdb/util.h" + +#ifdef _WIN32 +namespace +{ + constexpr const mdb_mode_t open_flags = 0; +} +#else +#include <sys/stat.h> + +namespace +{ + constexpr const mdb_mode_t open_flags = (S_IRUSR | S_IWUSR); +} +#endif + +namespace lmdb +{ + namespace + { + constexpr const std::size_t max_resize = 1 * 1024 * 1024 * 1024; // 1 GB + void acquire_context(context& ctx) noexcept + { + while (ctx.lock.test_and_set()); + ++(ctx.active); + ctx.lock.clear(); + } + + void release_context(context& ctx) noexcept + { + --(ctx.active); + } + } + + void release_read_txn::operator()(MDB_txn* ptr) const noexcept + { + if (ptr) + { + MDB_env* const env = mdb_txn_env(ptr); + abort_txn{}(ptr); + if (env) + { + context* ctx = reinterpret_cast<context*>(mdb_env_get_userctx(env)); + if (ctx) + release_context(*ctx); + } + } + } + + expect<environment> open_environment(const char* path, MDB_dbi max_dbs) noexcept + { + MONERO_PRECOND(path != nullptr); + + MDB_env* obj = nullptr; + MONERO_LMDB_CHECK(mdb_env_create(std::addressof(obj))); + environment out{obj}; + + MONERO_LMDB_CHECK(mdb_env_set_maxdbs(out.get(), max_dbs)); + MONERO_LMDB_CHECK(mdb_env_open(out.get(), path, 0, open_flags)); + return {std::move(out)}; + } + + expect<write_txn> database::do_create_txn(unsigned int flags) noexcept + { + MONERO_PRECOND(handle() != nullptr); + + for (unsigned attempts = 0; attempts < 3; ++attempts) + { + acquire_context(ctx); + + MDB_txn* txn = nullptr; + const int err = + mdb_txn_begin(handle(), nullptr, flags, &txn); + if (!err && txn != nullptr) + return write_txn{txn}; + + release_context(ctx); + if (err != MDB_MAP_RESIZED) + return {lmdb::error(err)}; + MONERO_CHECK(this->resize()); + } + return {lmdb::error(MDB_MAP_RESIZED)}; + } + + database::database(environment env) + : env(std::move(env)), ctx{{}, ATOMIC_FLAG_INIT} + { + if (handle()) + { + const int err = mdb_env_set_userctx(handle(), std::addressof(ctx)); + if (err) + MONERO_THROW(lmdb::error(err), "Failed to set user context"); + } + } + + database::~database() noexcept + { + while (ctx.active); + } + + expect<void> database::resize() noexcept + { + MONERO_PRECOND(handle() != nullptr); + + while (ctx.lock.test_and_set()); + while (ctx.active); + + MDB_envinfo info{}; + MONERO_LMDB_CHECK(mdb_env_info(handle(), &info)); + + const std::size_t resize = std::min(info.me_mapsize, max_resize); + const int err = mdb_env_set_mapsize(handle(), info.me_mapsize + resize); + ctx.lock.clear(); + if (err) + return {lmdb::error(err)}; + return success(); + } + + expect<read_txn> database::create_read_txn(suspended_txn txn) noexcept + { + if (txn) + { + acquire_context(ctx); + const int err = mdb_txn_renew(txn.get()); + if (err) + { + release_context(ctx); + return {lmdb::error(err)}; + } + return read_txn{txn.release()}; + } + auto new_txn = do_create_txn(MDB_RDONLY); + if (new_txn) + return read_txn{new_txn->release()}; + return new_txn.error(); + } + + expect<suspended_txn> database::reset_txn(read_txn txn) noexcept + { + MONERO_PRECOND(txn != nullptr); + mdb_txn_reset(txn.get()); + release_context(ctx); + return suspended_txn{txn.release()}; + } + + expect<write_txn> database::create_write_txn() noexcept + { + return do_create_txn(0); + } + + expect<void> database::commit(write_txn txn) noexcept + { + MONERO_PRECOND(txn != nullptr); + MONERO_LMDB_CHECK(mdb_txn_commit(txn.get())); + txn.release(); + release_context(ctx); + return success(); + } +} // lmdb diff --git a/src/lmdb/database.h b/src/lmdb/database.h new file mode 100644 index 000000000..269f8c8a1 --- /dev/null +++ b/src/lmdb/database.h @@ -0,0 +1,138 @@ +// 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. +#pragma once + +#include <atomic> +#include <cstddef> +#include <lmdb.h> +#include <memory> +#include <type_traits> + +#include "common/expect.h" +#include "lmdb/error.h" +#include "lmdb/transaction.h" + +namespace lmdb +{ + //! Closes LMDB environment handle. + struct close_env + { + void operator()(MDB_env* ptr) const noexcept + { + if (ptr) + mdb_env_close(ptr); + } + }; + + using environment = std::unique_ptr<MDB_env, close_env>; + + //! \return LMDB environment at `path` with a max of `max_dbs` tables. + expect<environment> open_environment(const char* path, MDB_dbi max_dbs) noexcept; + + //! Context given to LMDB. + struct context + { + std::atomic<std::size_t> active; + std::atomic_flag lock; + }; + + //! Manages a LMDB environment for safe memory-map resizing. Thread-safe. + class database + { + environment env; + context ctx; + + //! \return The LMDB environment associated with the object. + MDB_env* handle() const noexcept { return env.get(); } + + expect<write_txn> do_create_txn(unsigned int flags) noexcept; + + public: + database(environment env); + + database(database&&) = delete; + database(database const&) = delete; + + virtual ~database() noexcept; + + database& operator=(database&&) = delete; + database& operator=(database const&) = delete; + + /*! + Resize the memory map for the LMDB environment. Will block until + all reads/writes on the environment complete. + */ + expect<void> resize() noexcept; + + //! \return A read only LMDB transaction, reusing `txn` if provided. + expect<read_txn> create_read_txn(suspended_txn txn = nullptr) noexcept; + + //! \return `txn` after releasing context. + expect<suspended_txn> reset_txn(read_txn txn) noexcept; + + //! \return A read-write LMDB transaction. + expect<write_txn> create_write_txn() noexcept; + + //! Commit the read-write transaction. + expect<void> commit(write_txn txn) noexcept; + + /*! + Create a write transaction, pass it to `f`, then try to commit + the write if `f` succeeds. + + \tparam F must be callable with signature `expect<T>(MDB_txn&)`. + \param f must be re-startable if `lmdb::error(MDB_MAP_FULL)`. + + \return The result of calling `f`. + */ + template<typename F> + typename std::result_of<F(MDB_txn&)>::type try_write(F f, unsigned attempts = 3) + { + for (unsigned i = 0; i < attempts; ++i) + { + expect<write_txn> txn = create_write_txn(); + if (!txn) + return txn.error(); + + MONERO_PRECOND(*txn != nullptr); + const auto wrote = f(*(*txn)); + if (wrote) + { + MONERO_CHECK(commit(std::move(*txn))); + return wrote; + } + if (wrote != lmdb::error(MDB_MAP_FULL)) + return wrote; + + txn->reset(); + MONERO_CHECK(this->resize()); + } + return {lmdb::error(MDB_MAP_FULL)}; + } + }; +} // lmdb + diff --git a/src/lmdb/error.cpp b/src/lmdb/error.cpp new file mode 100644 index 000000000..359677064 --- /dev/null +++ b/src/lmdb/error.cpp @@ -0,0 +1,98 @@ +// Copyright (c) 2014-2018, The Monero Project +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "error.h" + +#include <lmdb.h> +#include <string> + +namespace { + struct category final : std::error_category + { + virtual const char* name() const noexcept override final + { + return "lmdb::error_category()"; + } + + virtual std::string message(int value) const override final + { + char const* const msg = mdb_strerror(value); + if (msg) + return msg; + return "Unknown lmdb::error_category() value"; + } + + virtual std::error_condition default_error_condition(int value) const noexcept override final + { + switch (value) + { + case MDB_KEYEXIST: + case MDB_NOTFOUND: + break; // map to nothing generic + case MDB_PAGE_NOTFOUND: + case MDB_CORRUPTED: + return std::errc::state_not_recoverable; + case MDB_PANIC: + case MDB_VERSION_MISMATCH: + case MDB_INVALID: + break; // map to nothing generic + case MDB_MAP_FULL: + return std::errc::no_buffer_space; + case MDB_DBS_FULL: + break; // map to nothing generic + case MDB_READERS_FULL: + case MDB_TLS_FULL: + return std::errc::no_lock_available; + case MDB_TXN_FULL: + case MDB_CURSOR_FULL: + case MDB_PAGE_FULL: + case MDB_MAP_RESIZED: + break; // map to nothing generic + case MDB_INCOMPATIBLE: + return std::errc::invalid_argument; + case MDB_BAD_RSLOT: + case MDB_BAD_TXN: + case MDB_BAD_VALSIZE: + case MDB_BAD_DBI: + return std::errc::invalid_argument; + default: + return std::error_condition{value, std::generic_category()}; + } + return std::error_condition{value, *this}; + } + }; +} + +namespace lmdb +{ + std::error_category const& error_category() noexcept + { + static const category instance{}; + return instance; + } +} + diff --git a/src/lmdb/error.h b/src/lmdb/error.h new file mode 100644 index 000000000..2944adf78 --- /dev/null +++ b/src/lmdb/error.h @@ -0,0 +1,64 @@ +// Copyright (c) 2014-2018, The Monero Project +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#pragma once + +#include <system_error> +#include <type_traits> + +//! Executes a LMDB command, and returns errors via `lmdb::error` enum. +#define MONERO_LMDB_CHECK(...) \ + do \ + { \ + const int err = __VA_ARGS__ ; \ + if (err) \ + return {lmdb::error(err)}; \ + } while (0) + +namespace lmdb +{ + //! Tracks LMDB error codes. + enum class error : int + { + // 0 is reserved for no error, as per expect<T> + // All other errors are the values reported by LMDB + }; + + std::error_category const& error_category() noexcept; + + inline std::error_code make_error_code(error value) noexcept + { + return std::error_code{int(value), error_category()}; + } +} + +namespace std +{ + template<> + struct is_error_code_enum<::lmdb::error> + : true_type + {}; +} diff --git a/src/lmdb/key_stream.h b/src/lmdb/key_stream.h new file mode 100644 index 000000000..40434d3a1 --- /dev/null +++ b/src/lmdb/key_stream.h @@ -0,0 +1,264 @@ +// 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. +#pragma once + +#include <boost/range/iterator_range.hpp> +#include <cstdint> +#include <cstring> +#include <iterator> +#include <lmdb.h> +#include <utility> + +#include "lmdb/value_stream.h" +#include "span.h" + +namespace lmdb +{ + + /*! + An InputIterator for a fixed-sized LMDB key and value. `operator++` + iterates over keys. + + \tparam K Key type in database records. + \tparam V Value type in database records. + + \note This meets requirements for an InputIterator only. The iterator + can only be incremented and dereferenced. All copies of an iterator + share the same LMDB cursor, and therefore incrementing any copy will + change the cursor state for all (incrementing an iterator will + invalidate all prior copies of the iterator). Usage is identical + to `std::istream_iterator`. + */ + template<typename K, typename V> + class key_iterator + { + MDB_cursor* cur; + epee::span<const std::uint8_t> key; + + void increment() + { + // MDB_NEXT_MULTIPLE doesn't work if only one value is stored :/ + if (cur) + key = lmdb::stream::get(*cur, MDB_NEXT_NODUP, sizeof(K), sizeof(V)).first; + } + + public: + using value_type = std::pair<K, boost::iterator_range<value_iterator<V>>>; + using reference = value_type; + using pointer = void; + using difference_type = std::size_t; + using iterator_category = std::input_iterator_tag; + + //! Construct an "end" iterator. + key_iterator() noexcept + : cur(nullptr), key() + {} + + /*! + \param cur Iterate over keys starting at this cursor position. + \throw std::system_error if unexpected LMDB error. This can happen + if `cur` is invalid. + */ + key_iterator(MDB_cursor* cur) + : cur(cur), key() + { + if (cur) + key = lmdb::stream::get(*cur, MDB_GET_CURRENT, sizeof(K), sizeof(V)).first; + } + + //! \return True if `this` is one-past the last key. + bool is_end() const noexcept { return key.empty(); } + + //! \return True iff `rhs` is referencing `this` key. + bool equal(key_iterator const& rhs) const noexcept + { + return + (key.empty() && rhs.key.empty()) || + key.data() == rhs.key.data(); + } + + /*! + Moves iterator to next key or end. Invalidates all prior copies of + the iterator. + */ + key_iterator& operator++() + { + increment(); + return *this; + } + + /*! + Moves iterator to next key or end. + + \return A copy that is already invalidated, ignore + */ + key_iterator operator++(int) + { + key_iterator out{*this}; + increment(); + return out; + } + + //! \pre `!is_end()` \return {current key, current value range} + value_type operator*() const + { + return {get_key(), make_value_range()}; + } + + //! \pre `!is_end()` \return Current key + K get_key() const noexcept + { + assert(!is_end()); + K out; + std::memcpy(std::addressof(out), key.data(), sizeof(out)); + return out; + } + + /*! + Return a C++ iterator over database values from current cursor + position that will reach `.is_end()` after the last duplicate key + record. Calling `make_iterator()` will return an iterator whose + `operator*` will return an entire value (`V`). + `make_iterator<MONERO_FIELD(account, id)>()` will return an + iterator whose `operator*` will return a `decltype(account.id)` + object - the other fields in the struct `account` are never copied + from the database. + + \throw std::system_error if LMDB has unexpected errors. + \return C++ iterator starting at current cursor position. + */ + template<typename T = V, typename F = T, std::size_t offset = 0> + value_iterator<T, F, offset> make_value_iterator() const + { + static_assert(std::is_same<T, V>(), "bad MONERO_FIELD usage?"); + return {cur}; + } + + /*! + Return a range from current cursor position until last duplicate + key record. Useful in for-each range loops or in templated code + expecting a range of elements. Calling `make_range()` will return + a range of `T` objects. `make_range<MONERO_FIELD(account, id)>()` + will return a range of `decltype(account.id)` objects - the other + fields in the struct `account` are never copied from the database. + + \throw std::system_error if LMDB has unexpected errors. + \return An InputIterator range over values at cursor position. + */ + template<typename T = V, typename F = T, std::size_t offset = 0> + boost::iterator_range<value_iterator<T, F, offset>> make_value_range() const + { + return {make_value_iterator<T, F, offset>(), value_iterator<T, F, offset>{}}; + } + }; + + /*! + C++ wrapper for a LMDB read-only cursor on a fixed-sized key `K` and + value `V`. + + \tparam K key type being stored by each record. + \tparam V value type being stored by each record. + \tparam D cleanup functor for the cursor; usually unique per db/table. + */ + template<typename K, typename V, typename D> + class key_stream + { + std::unique_ptr<MDB_cursor, D> cur; + public: + + //! Take ownership of `cur` without changing position. `nullptr` valid. + explicit key_stream(std::unique_ptr<MDB_cursor, D> cur) + : cur(std::move(cur)) + {} + + key_stream(key_stream&&) = default; + key_stream(key_stream const&) = delete; + ~key_stream() = default; + key_stream& operator=(key_stream&&) = default; + key_stream& operator=(key_stream const&) = delete; + + /*! + Give up ownership of the cursor. `make_iterator()` and + `make_range()` can still be invoked, but return the empty set. + + \return Currently owned LMDB cursor. + */ + std::unique_ptr<MDB_cursor, D> give_cursor() noexcept + { + return {std::move(cur)}; + } + + /*! + Place the stream back at the first key/value. Newly created + iterators will start at the first value again. + + \note Invalidates all current iterators, including those created + with `make_iterator` or `make_range`. Also invalidates all + `value_iterator`s created with `key_iterator`. + */ + void reset() + { + if (cur) + lmdb::stream::get(*cur, MDB_FIRST, 0, 0); + } + + /*! + \throw std::system_error if LMDB has unexpected errors. + \return C++ iterator over database keys from current cursor + position that will reach `.is_end()` after the last key. + */ + key_iterator<K, V> make_iterator() const + { + return {cur.get()}; + } + + /*! + \throw std::system_error if LMDB has unexpected errors. + \return Range from current cursor position until last key record. + Useful in for-each range loops or in templated code + */ + boost::iterator_range<key_iterator<K, V>> make_range() const + { + return {make_iterator(), key_iterator<K, V>{}}; + } + }; + + template<typename K, typename V> + inline + bool operator==(key_iterator<K, V> const& lhs, key_iterator<K, V> const& rhs) noexcept + { + return lhs.equal(rhs); + } + + template<typename K, typename V> + inline + bool operator!=(key_iterator<K, V> const& lhs, key_iterator<K, V> const& rhs) noexcept + { + return !lhs.equal(rhs); + } +} // lmdb + diff --git a/src/lmdb/table.cpp b/src/lmdb/table.cpp new file mode 100644 index 000000000..0818b74e6 --- /dev/null +++ b/src/lmdb/table.cpp @@ -0,0 +1,43 @@ +// 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 "table.h" + +namespace lmdb +{ + expect<MDB_dbi> table::open(MDB_txn& write_txn) const noexcept + { + MONERO_PRECOND(name != nullptr); + + MDB_dbi out; + MONERO_LMDB_CHECK(mdb_dbi_open(&write_txn, name, flags, &out)); + if (key_cmp && !(flags & MDB_INTEGERKEY)) + MONERO_LMDB_CHECK(mdb_set_compare(&write_txn, out, key_cmp)); + if (value_cmp && !(flags & MDB_INTEGERDUP)) + MONERO_LMDB_CHECK(mdb_set_dupsort(&write_txn, out, value_cmp)); + return out; + } +} diff --git a/src/lmdb/table.h b/src/lmdb/table.h new file mode 100644 index 000000000..41a3de296 --- /dev/null +++ b/src/lmdb/table.h @@ -0,0 +1,120 @@ +#pragma once + +#include <utility> + +#include "common/expect.h" +#include "lmdb/error.h" +#include "lmdb/key_stream.h" +#include "lmdb/util.h" +#include "lmdb/value_stream.h" + +namespace lmdb +{ + //! Helper for grouping typical LMDB DBI options. + struct table + { + char const* const name; + const unsigned flags; + MDB_cmp_func const* const key_cmp; + MDB_cmp_func const* const value_cmp; + + //! \pre `name != nullptr` \return Open table. + expect<MDB_dbi> open(MDB_txn& write_txn) const noexcept; + }; + + //! Helper for grouping typical LMDB DBI options when key and value are fixed types. + template<typename K, typename V> + struct basic_table : table + { + using key_type = K; + using value_type = V; + + //! \return Additional LMDB flags based on `flags` value. + static constexpr unsigned compute_flags(const unsigned flags) noexcept + { + return flags | ((flags & MDB_DUPSORT) ? MDB_DUPFIXED : 0); + } + + constexpr explicit basic_table(const char* name, unsigned flags = 0, MDB_cmp_func value_cmp = nullptr) noexcept + : table{name, compute_flags(flags), &lmdb::less<lmdb::native_type<K>>, value_cmp} + {} + + /*! + \tparam U must be same as `V`; used for sanity checking. + \tparam F is the type within `U` that is being extracted. + \tparam offset to `F` within `U`. + + \note If using `F` and `offset` to retrieve a specific field, use + `MONERO_FIELD` macro in `src/lmdb/util.h` which calculates the + offset automatically. + + \return Value of type `F` at `offset` within `value` which has + type `U`. + */ + template<typename U, typename F = U, std::size_t offset = 0> + static expect<F> get_value(MDB_val value) noexcept + { + static_assert(std::is_same<U, V>(), "bad MONERO_FIELD?"); + static_assert(std::is_pod<F>(), "F must be POD"); + static_assert(sizeof(F) + offset <= sizeof(U), "bad field type and/or offset"); + + if (value.mv_size != sizeof(U)) + return {lmdb::error(MDB_BAD_VALSIZE)}; + + F out; + std::memcpy(std::addressof(out), static_cast<char*>(value.mv_data) + offset, sizeof(out)); + return out; + } + + /*! + \pre `cur != nullptr`. + \param cur Active cursor on table. Returned in object on success, + otherwise destroyed. + \return A handle to the first key/value in the table linked + to `cur` or an empty `key_stream`. + */ + template<typename D> + expect<key_stream<K, V, D>> + static get_key_stream(std::unique_ptr<MDB_cursor, D> cur) noexcept + { + MONERO_PRECOND(cur != nullptr); + + MDB_val key; + MDB_val value; + const int err = mdb_cursor_get(cur.get(), &key, &value, MDB_FIRST); + if (err) + { + if (err != MDB_NOTFOUND) + return {lmdb::error(err)}; + cur.reset(); // return empty set + } + return key_stream<K, V, D>{std::move(cur)}; + } + + /*! + \pre `cur != nullptr`. + \param cur Active cursor on table. Returned in object on success, + otherwise destroyed. + \return A handle to the first value at `key` in the table linked + to `cur` or an empty `value_stream`. + */ + template<typename D> + expect<value_stream<V, D>> + static get_value_stream(K const& key, std::unique_ptr<MDB_cursor, D> cur) noexcept + { + MONERO_PRECOND(cur != nullptr); + + MDB_val key_bytes = lmdb::to_val(key); + MDB_val value; + const int err = mdb_cursor_get(cur.get(), &key_bytes, &value, MDB_SET); + if (err) + { + if (err != MDB_NOTFOUND) + return {lmdb::error(err)}; + cur.reset(); // return empty set + } + return value_stream<V, D>{std::move(cur)}; + } + }; +} // lmdb + diff --git a/src/lmdb/transaction.h b/src/lmdb/transaction.h new file mode 100644 index 000000000..cdd80696c --- /dev/null +++ b/src/lmdb/transaction.h @@ -0,0 +1,95 @@ +// 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. +#pragma once + +#include <lmdb.h> +#include <memory> + +#include "lmdb/error.h" + +//! Uses C++ type system to differentiate between cursors +#define MONERO_CURSOR(name) \ + struct close_ ## name : ::lmdb::close_cursor {}; \ + using name = std::unique_ptr< MDB_cursor, close_ ## name >; + +namespace lmdb +{ + struct abort_txn + { + void operator()(MDB_txn* ptr) const noexcept + { + if (ptr) + mdb_txn_abort(ptr); + } + }; + + /*! + Only valid if used via `create_read_txn()`. Decrements active count in + associated `context`, and aborts a LMDB transaction (`mdb_txn_abort`). + */ + struct release_read_txn + { + void operator()(MDB_txn* ptr) const noexcept; + // implementation in database.cpp + }; + + /*! + Only valid if used via `create_write_txn()`. Decrements active count in + associated `context`, and aborts a LMDB transaction (`mdb_txn_abort`). + */ + struct abort_write_txn + { + void operator()(MDB_txn* ptr) const noexcept + { + release_read_txn{}(ptr); + } + }; + + struct close_cursor + { + void operator()(MDB_cursor* ptr) const noexcept + { + if (ptr) + mdb_cursor_close(ptr); + } + }; + + template<typename D> + inline expect<std::unique_ptr<MDB_cursor, D>> + open_cursor(MDB_txn& txn, MDB_dbi tbl) noexcept + { + MDB_cursor* cur = nullptr; + MONERO_LMDB_CHECK(mdb_cursor_open(&txn, tbl, &cur)); + return std::unique_ptr<MDB_cursor, D>{cur}; + } + + // The below use the C++ type system to designate `MDB_txn` status. + + using suspended_txn = std::unique_ptr<MDB_txn, abort_txn>; + using read_txn = std::unique_ptr<MDB_txn, release_read_txn>; + using write_txn = std::unique_ptr<MDB_txn, abort_write_txn>; +} // lmdb diff --git a/src/lmdb/util.h b/src/lmdb/util.h new file mode 100644 index 000000000..50162b7c8 --- /dev/null +++ b/src/lmdb/util.h @@ -0,0 +1,149 @@ +// 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. +#pragma once + +#include <cstddef> +#include <cstring> +#include <lmdb.h> +#include <type_traits> +#include <utility> + +#include "span.h" + +/*! Calculates types and offset of struct field. Use in template arguments for + `table::get_value`, `value_iterator::get_value`, + `value_stream::make_iterator`, or `value_stream::make_range`. */ +#define MONERO_FIELD(obj, field) \ + obj , decltype(std::declval<obj>().field) , offsetof(obj, field) + +//! Expands to `lmdb::less` for the value `field` within `obj`. +#define MONERO_SORT_BY(obj, field) \ + &::lmdb::less< \ + lmdb::native_type<decltype(std::declval<obj>().field)>, \ + offsetof(obj, field) \ + > + +//! Expands to `lmdb::compare` for the value `field` within `obj`. +#define MONERO_COMPARE(obj, field) \ + &::lmdb::compare< \ + decltype(std::declval<obj>().field), \ + offsetof(obj, field) \ + > + +namespace lmdb +{ + //! Prevent instantiation of `std::underlying_type<T>` when `T` is not enum. + template<typename T> + struct identity + { + using type = T; + }; + + /*! + Get the native type for enums, or return `T` unchanged. Useful for + merging generated machine code for templated functions that use enums + with identical size-widths without relying on aggressive identical + comdat folding (ICF) support in linker. So with enum defintion + `enum class enum_foo : unsigned long {};` will always yield + `assert(&func_foo<unsigned long> == &func_foo<native_type<enum_foo>>)`. + */ + template<typename T> + using native_type = typename std::conditional< + std::is_enum<T>::value, std::underlying_type<T>, identity<T> + >::type::type; + + //! \return `value` as its native type. + template<typename T, typename U = typename std::underlying_type<T>::type> + inline constexpr U to_native(T value) noexcept + { + return U(value); + } + + //! \return `value` bytes in a LMDB `MDB_val` object. + template<typename T> + inline MDB_val to_val(T&& value) noexcept + { + // lmdb does not touch user data, so const_cast is acceptable + static_assert(!std::is_rvalue_reference<T&&>(), "cannot use temporary value"); + void const* const temp = reinterpret_cast<void const*>(std::addressof(value)); + return MDB_val{sizeof(value), const_cast<void*>(temp)}; + } + + //! \return A span over the same chunk of memory as `value`. + inline constexpr epee::span<const std::uint8_t> to_byte_span(MDB_val value) noexcept + { + return {static_cast<const std::uint8_t*>(value.mv_data), value.mv_size}; + } + + /*! + A LMDB comparison function that uses `operator<`. + + \tparam T has a defined `operator<` . + \tparam offset to `T` within the value. + + \return -1 if `left < right`, 1 if `right < left`, and 0 otherwise. + */ + template<typename T, std::size_t offset = 0> + inline int less(MDB_val const* left, MDB_val const* right) noexcept + { + if (!left || !right || left->mv_size < sizeof(T) + offset || right->mv_size < sizeof(T) + offset) + { + assert("invalid use of custom comparison" == 0); + return -1; + } + + T left_val; + T right_val; + std::memcpy(std::addressof(left_val), static_cast<char*>(left->mv_data) + offset, sizeof(T)); + std::memcpy(std::addressof(right_val), static_cast<char*>(right->mv_data) + offset, sizeof(T)); + return left_val < right_val ? -1 : bool(right_val < left_val); + } + + /*! + A LMDB comparison function that uses `std::memcmp`. + + \toaram T is `!epee::has_padding` + \tparam offset to `T` within the value. + + \return The result of `std::memcmp` over the value. + */ + template<typename T, std::size_t offset = 0> + inline int compare(MDB_val const* left, MDB_val const* right) noexcept + { + static_assert(!epee::has_padding<T>(), "memcmp will not work"); + if (!left || !right || left->mv_size < sizeof(T) + offset || right->mv_size < sizeof(T) + offset) + { + assert("invalid use of custom comparison" == 0); + return -1; + } + return std::memcmp( + static_cast<char*>(left->mv_data) + offset, + static_cast<char*>(right->mv_data) + offset, + sizeof(T) + ); + } +} // lmdb diff --git a/src/lmdb/value_stream.cpp b/src/lmdb/value_stream.cpp new file mode 100644 index 000000000..1024deb06 --- /dev/null +++ b/src/lmdb/value_stream.cpp @@ -0,0 +1,74 @@ +// 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 "value_stream.h" + +#include <stdexcept> + +#include "common/expect.h" +#include "lmdb/error.h" +#include "lmdb/util.h" + +namespace lmdb +{ + namespace stream + { + std::size_t count(MDB_cursor* cur) + { + std::size_t out = 0; + if (cur) + { + const int rc = mdb_cursor_count(cur, &out); + if (rc) + MONERO_THROW(lmdb::error(rc), "mdb_cursor_count"); + } + return out; + } + + std::pair<epee::span<const std::uint8_t>, epee::span<const std::uint8_t>> + get(MDB_cursor& cur, MDB_cursor_op op, std::size_t key, std::size_t value) + { + MDB_val key_bytes{}; + MDB_val value_bytes{}; + const int rc = mdb_cursor_get(&cur, &key_bytes, &value_bytes, op); + if (rc) + { + if (rc == MDB_NOTFOUND) + return {}; + MONERO_THROW(lmdb::error(rc), "mdb_cursor_get"); + } + + if (key && key != key_bytes.mv_size) + MONERO_THROW(lmdb::error(MDB_BAD_VALSIZE), "mdb_cursor_get key"); + + if (value && (value_bytes.mv_size % value != 0 || value_bytes.mv_size == 0)) + MONERO_THROW(lmdb::error(MDB_BAD_VALSIZE), "mdb_cursor_get value"); + + return {lmdb::to_byte_span(key_bytes), lmdb::to_byte_span(value_bytes)}; + } + } +} + diff --git a/src/lmdb/value_stream.h b/src/lmdb/value_stream.h new file mode 100644 index 000000000..c9977221f --- /dev/null +++ b/src/lmdb/value_stream.h @@ -0,0 +1,287 @@ +// 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. +#pragma once + +#include <boost/range/iterator_range.hpp> +#include <cstdint> +#include <cstring> +#include <iterator> +#include <lmdb.h> +#include <utility> + +#include "span.h" + +namespace lmdb +{ + namespace stream + { + /* + \throw std::system_error if unexpected LMDB error. + \return 0 if `cur == nullptr`, otherwise count of values at current key. + */ + std::size_t count(MDB_cursor* cur); + + /*! + Calls `mdb_cursor_get` and does some error checking. + + \param cur is given to `mdb_cursor_get` without modification. + \param op is passed to `mdb_cursor_get` without modification. + \param key expected key size or 0 to skip key size check. + \param value expected value size or 0 to skip value size check. + + \throw std::system_error if `key != 0` and `key_.mv_size != key`. + \throw std::system_error if `value != 0` and `value_.mv_size != value`. + \throw std::system_error if `mdb_cursor_get` returns any error + other than `MDB_NOTFOUND`. + + \return {key bytes, value bytes} or two empty spans if `MDB_NOTFOUND`. + */ + std::pair<epee::span<const std::uint8_t>, epee::span<const std::uint8_t>> + get(MDB_cursor& cur, MDB_cursor_op op, std::size_t key, std::size_t value); + } + + /*! + An InputIterator for a fixed-sized LMDB value at a specific key. + + \tparam T The value type at the specific key. + \tparam F The value type being returned when dereferenced. + \tparam offset to `F` within `T`. + + \note This meets requirements for an InputIterator only. The iterator + can only be incremented and dereferenced. All copies of an iterator + share the same LMDB cursor, and therefore incrementing any copy will + change the cursor state for all (incrementing an iterator will + invalidate all prior copies of the iterator). Usage is identical + to `std::istream_iterator`. + */ + template<typename T, typename F = T, std::size_t offset = 0> + class value_iterator + { + MDB_cursor* cur; + epee::span<const std::uint8_t> values; + + void increment() + { + values.remove_prefix(sizeof(T)); + if (values.empty() && cur) + values = lmdb::stream::get(*cur, MDB_NEXT_DUP, 0, sizeof(T)).second; + } + + public: + using value_type = F; + using reference = value_type; + using pointer = void; + using difference_type = std::size_t; + using iterator_category = std::input_iterator_tag; + + //! Construct an "end" iterator. + value_iterator() noexcept + : cur(nullptr), values() + {} + + /*! + \param cur Iterate over values starting at this cursor position. + \throw std::system_error if unexpected LMDB error. This can happen + if `cur` is invalid. + */ + value_iterator(MDB_cursor* cur) + : cur(cur), values() + { + if (cur) + values = lmdb::stream::get(*cur, MDB_GET_CURRENT, 0, sizeof(T)).second; + } + + value_iterator(value_iterator const&) = default; + ~value_iterator() = default; + value_iterator& operator=(value_iterator const&) = default; + + //! \return True if `this` is one-past the last value. + bool is_end() const noexcept { return values.empty(); } + + //! \return True iff `rhs` is referencing `this` value. + bool equal(value_iterator const& rhs) const noexcept + { + return + (values.empty() && rhs.values.empty()) || + values.data() == rhs.values.data(); + } + + //! Invalidates all prior copies of the iterator. + value_iterator& operator++() + { + increment(); + return *this; + } + + //! \return A copy that is already invalidated, ignore + value_iterator operator++(int) + { + value_iterator out{*this}; + increment(); + return out; + } + + /*! + Get a specific field within `F`. Default behavior is to return + the entirety of `U`, despite the filtering logic of `operator*`. + + \pre `!is_end()` + + \tparam U must match `T`, used for `MONERO_FIELD` sanity checking. + \tparam G field type to extract from the value + \tparam uoffset to `G` type, or `0` when `std::is_same<U, G>()`. + + \return The field `G`, at `uoffset` within `U`. + */ + template<typename U, typename G = U, std::size_t uoffset = 0> + G get_value() const noexcept + { + static_assert(std::is_same<U, T>(), "bad MONERO_FIELD usage?"); + static_assert(std::is_pod<U>(), "value type must be pod"); + static_assert(std::is_pod<G>(), "field type must be pod"); + static_assert(sizeof(G) + uoffset <= sizeof(U), "bad field and/or offset"); + assert(sizeof(G) + uoffset <= values.size()); + assert(!is_end()); + + G value; + std::memcpy(std::addressof(value), values.data() + uoffset, sizeof(value)); + return value; + } + + //! \pre `!is_end()` \return The field `F`, at `offset`, within `T`. + value_type operator*() const noexcept { return get_value<T, F, offset>(); } + }; + + /*! + C++ wrapper for a LMDB read-only cursor on a fixed-sized value `T`. + + \tparam T value type being stored by each record. + \tparam D cleanup functor for the cursor; usually unique per db/table. + */ + template<typename T, typename D> + class value_stream + { + std::unique_ptr<MDB_cursor, D> cur; + public: + + //! Take ownership of `cur` without changing position. `nullptr` valid. + explicit value_stream(std::unique_ptr<MDB_cursor, D> cur) + : cur(std::move(cur)) + {} + + value_stream(value_stream&&) = default; + value_stream(value_stream const&) = delete; + ~value_stream() = default; + value_stream& operator=(value_stream&&) = default; + value_stream& operator=(value_stream const&) = delete; + + /*! + Give up ownership of the cursor. `count()`, `make_iterator()` and + `make_range()` can still be invoked, but return the empty set. + + \return Currently owned LMDB cursor. + */ + std::unique_ptr<MDB_cursor, D> give_cursor() noexcept + { + return {std::move(cur)}; + } + + /*! + Place the stream back at the first value. Newly created iterators + will start at the first value again. + + \note Invalidates all current iterators from `this`, including + those created with `make_iterator` or `make_range`. + */ + void reset() + { + if (cur) + lmdb::stream::get(*cur, MDB_FIRST_DUP, 0, 0); + } + + /*! + \throw std::system_error if LMDB has unexpected errors. + \return Number of values at this key. + */ + std::size_t count() const + { + return lmdb::stream::count(cur.get()); + } + + /*! + Return a C++ iterator over database values from current cursor + position that will reach `.is_end()` after the last duplicate key + record. Calling `make_iterator()` will return an iterator whose + `operator*` will return entire value (`T`). + `make_iterator<MONERO_FIELD(account, id)>()` will return an + iterator whose `operator*` will return a `decltype(account.id)` + object - the other fields in the struct `account` are never copied + from the database. + + \throw std::system_error if LMDB has unexpected errors. + \return C++ iterator starting at current cursor position. + */ + template<typename U = T, typename F = U, std::size_t offset = 0> + value_iterator<U, F, offset> make_iterator() const + { + static_assert(std::is_same<U, T>(), "was MONERO_FIELD used with wrong type?"); + return {cur.get()}; + } + + /*! + Return a range from current cursor position until last duplicate + key record. Useful in for-each range loops or in templated code + expecting a range of elements. Calling `make_range()` will return + a range of `T` objects. `make_range<MONERO_FIELD(account, id)>()` + will return a range of `decltype(account.id)` objects - the other + fields in the struct `account` are never copied from the database. + + \throw std::system_error if LMDB has unexpected errors. + \return An InputIterator range over values at cursor position. + */ + template<typename U = T, typename F = U, std::size_t offset = 0> + boost::iterator_range<value_iterator<U, F, offset>> make_range() const + { + return {make_iterator<U, F, offset>(), value_iterator<U, F, offset>{}}; + } + }; + + template<typename T, typename F, std::size_t offset> + inline + bool operator==(value_iterator<T, F, offset> const& lhs, value_iterator<T, F, offset> const& rhs) noexcept + { + return lhs.equal(rhs); + } + + template<typename T, typename F, std::size_t offset> + inline + bool operator!=(value_iterator<T, F, offset> const& lhs, value_iterator<T, F, offset> const& rhs) noexcept + { + return !lhs.equal(rhs); + } +} // lmdb + diff --git a/src/mnemonics/CMakeLists.txt b/src/mnemonics/CMakeLists.txt index e3836bcca..acb9a4338 100644 --- a/src/mnemonics/CMakeLists.txt +++ b/src/mnemonics/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/src/mnemonics/chinese_simplified.h b/src/mnemonics/chinese_simplified.h index 0566b1079..ff035dde0 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-2018, The Monero Project
+// Code surrounding the word list is Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/dutch.h b/src/mnemonics/dutch.h index 801caf986..14ba56a7e 100644 --- a/src/mnemonics/dutch.h +++ b/src/mnemonics/dutch.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp index 68d5b84d3..48c9ab1ba 100644 --- a/src/mnemonics/electrum-words.cpp +++ b/src/mnemonics/electrum-words.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/mnemonics/electrum-words.h b/src/mnemonics/electrum-words.h index 60d2c5f15..9aa727e45 100644 --- a/src/mnemonics/electrum-words.h +++ b/src/mnemonics/electrum-words.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/mnemonics/english.h b/src/mnemonics/english.h index d5c5594ef..677b70052 100644 --- a/src/mnemonics/english.h +++ b/src/mnemonics/english.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/english_old.h b/src/mnemonics/english_old.h index e35b907df..179fc45ec 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-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/esperanto.h b/src/mnemonics/esperanto.h index b0be235ed..ef63a2235 100644 --- a/src/mnemonics/esperanto.h +++ b/src/mnemonics/esperanto.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/french.h b/src/mnemonics/french.h index 48ec46f78..85925aa53 100644 --- a/src/mnemonics/french.h +++ b/src/mnemonics/french.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/mnemonics/german.h b/src/mnemonics/german.h index 883a173a3..40116970c 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-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/italian.h b/src/mnemonics/italian.h index 57cdfa25e..204fdcb78 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-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/japanese.h b/src/mnemonics/japanese.h index 5baabedf2..6213c4976 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-2018, The Monero Project
+// Code surrounding the word list is Copyright (c) 2014-2019, 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 a6f969604..653314b04 100644 --- a/src/mnemonics/language_base.h +++ b/src/mnemonics/language_base.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/lojban.h b/src/mnemonics/lojban.h index 5162a8ec9..b6baf46c4 100644 --- a/src/mnemonics/lojban.h +++ b/src/mnemonics/lojban.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/portuguese.h b/src/mnemonics/portuguese.h index af04f89c2..dbc51fb2a 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-2018, The Monero Project
+// Code surrounding the word list is Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/russian.h b/src/mnemonics/russian.h index f3e70ede6..d36942a9b 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-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/singleton.h b/src/mnemonics/singleton.h index d317a2d8d..ffe3fe4de 100644 --- a/src/mnemonics/singleton.h +++ b/src/mnemonics/singleton.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project
+// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/spanish.h b/src/mnemonics/spanish.h index 4d7a896a6..a05485775 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-2018, The Monero Project
+// Code surrounding the word list is Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
diff --git a/src/multisig/CMakeLists.txt b/src/multisig/CMakeLists.txt index a770c6dc5..4631b75ef 100644 --- a/src/multisig/CMakeLists.txt +++ b/src/multisig/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2018, The Monero Project +# Copyright (c) 2017-2019, The Monero Project # # All rights reserved. # diff --git a/src/multisig/multisig.cpp b/src/multisig/multisig.cpp index 33d0a312f..14df4d554 100644 --- a/src/multisig/multisig.cpp +++ b/src/multisig/multisig.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/multisig/multisig.h b/src/multisig/multisig.h index 93a756812..bc6edbb05 100644 --- a/src/multisig/multisig.h +++ b/src/multisig/multisig.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/p2p/CMakeLists.txt b/src/p2p/CMakeLists.txt index 9a1730b8a..3aecc3cf9 100644 --- a/src/p2p/CMakeLists.txt +++ b/src/p2p/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp index 2f0678913..fcbcce58c 100644 --- a/src/p2p/net_node.cpp +++ b/src/p2p/net_node.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -132,6 +132,7 @@ namespace nodetool const command_line::arg_descriptor<std::vector<std::string> > arg_proxy = {"proxy", "<network-type>,<socks-ip:port>[,max_connections] i.e. \"tor,127.0.0.1:9050,100\""}; const command_line::arg_descriptor<std::vector<std::string> > arg_anonymous_inbound = {"anonymous-inbound", "<hidden-service-address>,<[bind-ip:]port>[,max_connections] i.e. \"x.onion,127.0.0.1:18083,100\""}; const command_line::arg_descriptor<bool> arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true}; + const command_line::arg_descriptor<bool> arg_no_sync = {"no-sync", "Don't synchronize the blockchain with other peers", false}; const command_line::arg_descriptor<bool> arg_no_igd = {"no-igd", "Disable UPnP port mapping"}; const command_line::arg_descriptor<int64_t> arg_out_peers = {"out-peers", "set max number of out peers", -1}; diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 4c5f788c3..42bb3b061 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -211,6 +211,7 @@ namespace nodetool node_server(t_payload_net_handler& payload_handler) : m_payload_handler(payload_handler), m_external_port(0), + m_rpc_port(0), m_allow_local_ip(false), m_hide_my_port(false), m_no_igd(false), @@ -400,6 +401,12 @@ namespace nodetool m_save_graph = save_graph; epee::net_utils::connection_basic::set_save_graph(save_graph); } + + void set_rpc_port(uint16_t rpc_port) + { + m_rpc_port = rpc_port; + } + private: std::string m_config_folder; @@ -407,6 +414,7 @@ namespace nodetool bool m_first_connection_maker_call; uint32_t m_listening_port; uint32_t m_external_port; + uint16_t m_rpc_port; bool m_allow_local_ip; bool m_hide_my_port; bool m_no_igd; @@ -481,6 +489,7 @@ namespace nodetool extern const command_line::arg_descriptor<std::vector<std::string> > arg_proxy; extern const command_line::arg_descriptor<std::vector<std::string> > arg_anonymous_inbound; extern const command_line::arg_descriptor<bool> arg_p2p_hide_my_port; + extern const command_line::arg_descriptor<bool> arg_no_sync; extern const command_line::arg_descriptor<bool> arg_no_igd; extern const command_line::arg_descriptor<bool> arg_offline; diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 341598e80..ba6e79d3f 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -103,6 +103,7 @@ namespace nodetool command_line::add_arg(desc, arg_proxy); command_line::add_arg(desc, arg_anonymous_inbound); command_line::add_arg(desc, arg_p2p_hide_my_port); + command_line::add_arg(desc, arg_no_sync); command_line::add_arg(desc, arg_no_igd); command_line::add_arg(desc, arg_out_peers); command_line::add_arg(desc, arg_in_peers); @@ -310,6 +311,9 @@ namespace nodetool if(command_line::has_arg(vm, arg_p2p_hide_my_port)) m_hide_my_port = true; + if (command_line::has_arg(vm, arg_no_sync)) + m_payload_handler.set_no_sync(true); + if ( !set_max_out_peers(public_zone, command_line::get_arg(vm, arg_out_peers) ) ) return false; else @@ -865,7 +869,8 @@ namespace nodetool } pi = context.peer_id = rsp.node_data.peer_id; - m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.set_peer_just_seen(rsp.node_data.peer_id, context.m_remote_address, context.m_pruning_seed); + context.m_rpc_port = rsp.node_data.rpc_port; + m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.set_peer_just_seen(rsp.node_data.peer_id, context.m_remote_address, context.m_pruning_seed, context.m_rpc_port); // move for (auto const& zone : m_network_zones) @@ -931,7 +936,7 @@ namespace nodetool add_host_fail(context.m_remote_address); } if(!context.m_is_income) - m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_address, context.m_pruning_seed); + m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_address, context.m_pruning_seed, context.m_rpc_port); m_payload_handler.process_payload_sync_data(rsp.payload_data, context, false); }); @@ -1095,6 +1100,7 @@ namespace nodetool time(&last_seen); pe_local.last_seen = static_cast<int64_t>(last_seen); pe_local.pruning_seed = con->m_pruning_seed; + pe_local.rpc_port = con->m_rpc_port; zone.m_peerlist.append_with_peer_white(pe_local); //update last seen and push it to peerlist manager @@ -1657,6 +1663,7 @@ namespace nodetool node_data.my_port = m_external_port ? m_external_port : m_listening_port; else node_data.my_port = 0; + node_data.rpc_port = zone.m_can_pingback ? m_rpc_port : 0; node_data.network_id = m_network_id; return true; } @@ -2012,6 +2019,7 @@ namespace nodetool //associate peer_id with this connection context.peer_id = arg.node_data.peer_id; context.m_in_timedsync = false; + context.m_rpc_port = arg.node_data.rpc_port; if(arg.node_data.peer_id != zone.m_config.m_peer_id && arg.node_data.my_port && zone.m_can_pingback) { @@ -2031,6 +2039,7 @@ namespace nodetool pe.last_seen = static_cast<int64_t>(last_seen); pe.id = peer_id_l; pe.pruning_seed = context.m_pruning_seed; + pe.rpc_port = context.m_rpc_port; this->m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.append_with_peer_white(pe); LOG_DEBUG_CC(context, "PING SUCCESS " << context.m_remote_address.host_str() << ":" << port_l); }); @@ -2331,7 +2340,7 @@ namespace nodetool } else { - zone.second.m_peerlist.set_peer_just_seen(pe.id, pe.adr, pe.pruning_seed); + zone.second.m_peerlist.set_peer_just_seen(pe.id, pe.adr, pe.pruning_seed, pe.rpc_port); LOG_PRINT_L2("PEER PROMOTED TO WHITE PEER LIST IP address: " << pe.adr.host_str() << " Peer ID: " << peerid_type(pe.id)); } } diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index 944bf48e4..26451b333 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index 46726d7d8..ebe0268d8 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -111,7 +111,7 @@ namespace nodetool bool append_with_peer_white(const peerlist_entry& pr); bool append_with_peer_gray(const peerlist_entry& pr); bool append_with_peer_anchor(const anchor_peerlist_entry& ple); - bool set_peer_just_seen(peerid_type peer, const epee::net_utils::network_address& addr, uint32_t pruning_seed); + bool set_peer_just_seen(peerid_type peer, const epee::net_utils::network_address& addr, uint32_t pruning_seed, uint16_t rpc_port); bool set_peer_unreachable(const peerlist_entry& pr); bool is_host_allowed(const epee::net_utils::network_address &address); bool get_random_gray_peer(peerlist_entry& pe); @@ -295,7 +295,7 @@ namespace nodetool } //-------------------------------------------------------------------------------------------------- inline - bool peerlist_manager::set_peer_just_seen(peerid_type peer, const epee::net_utils::network_address& addr, uint32_t pruning_seed) + bool peerlist_manager::set_peer_just_seen(peerid_type peer, const epee::net_utils::network_address& addr, uint32_t pruning_seed, uint16_t rpc_port) { TRY_ENTRY(); CRITICAL_REGION_LOCAL(m_peerlist_lock); @@ -305,6 +305,7 @@ namespace nodetool ple.id = peer; ple.last_seen = time(NULL); ple.pruning_seed = pruning_seed; + ple.rpc_port = rpc_port; return append_with_peer_white(ple); CATCH_ENTRY_L0("peerlist_manager::set_peer_just_seen()", false); } diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h index 6c891581f..40ef2ebcd 100644 --- a/src/p2p/net_peerlist_boost_serialization.h +++ b/src/p2p/net_peerlist_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -42,6 +42,8 @@ #include "common/pruning.h" #endif +BOOST_CLASS_VERSION(nodetool::peerlist_entry, 2) + namespace boost { namespace serialization @@ -197,6 +199,13 @@ namespace boost pl.pruning_seed = tools::make_pruning_seed(1+pl.adr.as<epee::net_utils::ipv4_network_address>().ip() % (1<<CRYPTONOTE_PRUNING_LOG_STRIPES), CRYPTONOTE_PRUNING_LOG_STRIPES); } #endif + if (ver < 2) + { + if (!typename Archive::is_saving()) + pl.rpc_port = 0; + return; + } + a & pl.rpc_port; } template <class Archive, class ver_type> diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index bb4fb9da2..59c6099d5 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -76,12 +76,14 @@ namespace nodetool peerid_type id; int64_t last_seen; uint32_t pruning_seed; + uint16_t rpc_port; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(adr) KV_SERIALIZE(id) KV_SERIALIZE(last_seen) KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0) + KV_SERIALIZE_OPT(rpc_port, (uint16_t)0) END_KV_SERIALIZE_MAP() }; typedef peerlist_entry_base<epee::net_utils::network_address> peerlist_entry; @@ -127,7 +129,11 @@ namespace nodetool ss << std::setfill ('0') << std::setw (8) << std::hex << std::noshowbase; for(const peerlist_entry& pe: pl) { - ss << pe.id << "\t" << pe.adr.str() << " \tpruning seed " << pe.pruning_seed << " \tlast_seen: " << epee::misc_utils::get_time_interval_string(now_time - pe.last_seen) << std::endl; + ss << pe.id << "\t" << pe.adr.str() + << " \trpc port " << (pe.rpc_port > 0 ? std::to_string(pe.rpc_port) : "-") + << " \tpruning seed " << pe.pruning_seed + << " \tlast_seen: " << epee::misc_utils::get_time_interval_string(now_time - pe.last_seen) + << std::endl; } return ss.str(); } @@ -158,6 +164,7 @@ namespace nodetool uuid network_id; uint64_t local_time; uint32_t my_port; + uint16_t rpc_port; peerid_type peer_id; BEGIN_KV_SERIALIZE_MAP() @@ -165,6 +172,7 @@ namespace nodetool KV_SERIALIZE(peer_id) KV_SERIALIZE(local_time) KV_SERIALIZE(my_port) + KV_SERIALIZE_OPT(rpc_port, (uint16_t)(0)) END_KV_SERIALIZE_MAP() }; @@ -211,7 +219,7 @@ namespace nodetool { const epee::net_utils::network_address &na = p.adr; const epee::net_utils::ipv4_network_address &ipv4 = na.as<const epee::net_utils::ipv4_network_address>(); - local_peerlist.push_back(peerlist_entry_base<network_address_old>({{ipv4.ip(), ipv4.port()}, p.id, p.last_seen, p.pruning_seed})); + local_peerlist.push_back(peerlist_entry_base<network_address_old>({{ipv4.ip(), ipv4.port()}, p.id, p.last_seen, p.pruning_seed, p.rpc_port})); } else MDEBUG("Not including in legacy peer list: " << p.adr.str()); @@ -226,7 +234,7 @@ namespace nodetool std::vector<peerlist_entry_base<network_address_old>> local_peerlist; epee::serialization::selector<is_store>::serialize_stl_container_pod_val_as_blob(local_peerlist, stg, hparent_section, "local_peerlist"); for (const auto &p: local_peerlist) - ((response&)this_ref).local_peerlist_new.push_back(peerlist_entry({epee::net_utils::ipv4_network_address(p.adr.ip, p.adr.port), p.id, p.last_seen, p.pruning_seed})); + ((response&)this_ref).local_peerlist_new.push_back(peerlist_entry({epee::net_utils::ipv4_network_address(p.adr.ip, p.adr.port), p.id, p.last_seen, p.pruning_seed, p.rpc_port})); } } END_KV_SERIALIZE_MAP() @@ -481,7 +489,3 @@ namespace nodetool } } - -BOOST_CLASS_VERSION(nodetool::peerlist_entry, 1) - - diff --git a/src/p2p/stdafx.h b/src/p2p/stdafx.h index b6ff37811..12a371702 100644 --- a/src/p2p/stdafx.h +++ b/src/p2p/stdafx.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/platform/mingw/alloca.h b/src/platform/mingw/alloca.h index 71934b19a..e4722af2a 100644 --- a/src/platform/mingw/alloca.h +++ b/src/platform/mingw/alloca.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/platform/msc/alloca.h b/src/platform/msc/alloca.h index 89743e12b..fcf1731b0 100644 --- a/src/platform/msc/alloca.h +++ b/src/platform/msc/alloca.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/platform/msc/inline_c.h b/src/platform/msc/inline_c.h index b274f3ec2..23c11bd06 100644 --- a/src/platform/msc/inline_c.h +++ b/src/platform/msc/inline_c.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/platform/msc/stdbool.h b/src/platform/msc/stdbool.h index 63e4200b2..4d24995bd 100644 --- a/src/platform/msc/stdbool.h +++ b/src/platform/msc/stdbool.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/platform/msc/sys/param.h b/src/platform/msc/sys/param.h index ca9c9282d..e3cfb7e86 100644 --- a/src/platform/msc/sys/param.h +++ b/src/platform/msc/sys/param.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/ringct/CMakeLists.txt b/src/ringct/CMakeLists.txt index 29f166a3b..0192aa931 100644 --- a/src/ringct/CMakeLists.txt +++ b/src/ringct/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2016-2018, The Monero Project +# Copyright (c) 2016-2019, The Monero Project # # All rights reserved. # diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc index b5fd626dc..e394ef088 100644 --- a/src/ringct/bulletproofs.cc +++ b/src/ringct/bulletproofs.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/ringct/bulletproofs.h b/src/ringct/bulletproofs.h index b86202ccc..21d494834 100644 --- a/src/ringct/bulletproofs.h +++ b/src/ringct/bulletproofs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/ringct/rctCryptoOps.c b/src/ringct/rctCryptoOps.c index 6fdd17f6b..fbbf6f9bd 100644 --- a/src/ringct/rctCryptoOps.c +++ b/src/ringct/rctCryptoOps.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/ringct/rctCryptoOps.h b/src/ringct/rctCryptoOps.h index e5c1c987a..2a25d13a7 100644 --- a/src/ringct/rctCryptoOps.h +++ b/src/ringct/rctCryptoOps.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt index 60cae036c..cffe8e1eb 100644 --- a/src/rpc/CMakeLists.txt +++ b/src/rpc/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index fa27c259d..56b0361a7 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -65,6 +65,11 @@ namespace reasons += ", "; reasons += reason; } + + uint64_t round_up(uint64_t value, uint64_t quantum) + { + return (value + quantum - 1) / quantum * quantum; + } } namespace cryptonote @@ -247,7 +252,9 @@ namespace cryptonote boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex); res.was_bootstrap_ever_used = m_was_bootstrap_ever_used; } - res.database_size = restricted ? 0 : m_core.get_blockchain_storage().get_db().get_database_size(); + res.database_size = m_core.get_blockchain_storage().get_db().get_database_size(); + if (restricted) + res.database_size = round_up(res.database_size, 5ull* 1024 * 1024 * 1024); res.update_available = restricted ? false : m_core.is_update_available(); res.version = restricted ? "" : MONERO_VERSION; return true; @@ -908,11 +915,30 @@ namespace cryptonote res.active = lMiner.is_mining(); res.is_background_mining_enabled = lMiner.get_is_background_mining_enabled(); + res.block_target = m_core.get_blockchain_storage().get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; if ( lMiner.is_mining() ) { res.speed = lMiner.get_speed(); res.threads_count = lMiner.get_threads_count(); - const account_public_address& lMiningAdr = lMiner.get_mining_address(); - res.address = get_account_address_as_str(nettype(), false, lMiningAdr); + res.block_reward = lMiner.get_block_reward(); + } + const account_public_address& lMiningAdr = lMiner.get_mining_address(); + res.address = get_account_address_as_str(nettype(), false, lMiningAdr); + const uint8_t major_version = m_core.get_blockchain_storage().get_current_hard_fork_version(); + const unsigned variant = major_version >= 7 ? major_version - 6 : 0; + switch (variant) + { + case 0: res.pow_algorithm = "Cryptonight"; break; + case 1: res.pow_algorithm = "CNv1 (Cryptonight variant 1)"; break; + case 2: case 3: res.pow_algorithm = "CNv2 (Cryptonight variant 2)"; break; + case 4: case 5: res.pow_algorithm = "CNv4 (Cryptonight variant 4)"; break; + default: res.pow_algorithm = "I'm not sure actually"; break; + } + if (res.is_background_mining_enabled) + { + res.bg_idle_threshold = lMiner.get_idle_threshold(); + res.bg_min_idle_seconds = lMiner.get_min_idle_seconds(); + res.bg_ignore_battery = lMiner.get_ignore_battery(); + res.bg_target = lMiner.get_mining_target(); } res.status = CORE_RPC_STATUS_OK; @@ -943,9 +969,9 @@ namespace cryptonote { if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id()) res.white_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv4_network_address>().ip(), - entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed); + entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port); else - res.white_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed); + res.white_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed, entry.rpc_port); } res.gray_list.reserve(gray_list.size()); @@ -953,9 +979,9 @@ namespace cryptonote { if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id()) res.gray_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv4_network_address>().ip(), - entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed); + entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port); else - res.gray_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed); + res.gray_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed, entry.rpc_port); } res.status = CORE_RPC_STATUS_OK; @@ -1445,7 +1471,8 @@ namespace cryptonote error_resp.message = "Internal error: can't get last block."; return false; } - bool response_filled = fill_block_header_response(last_block, false, last_block_height, last_block_hash, res.block_header, req.fill_pow_hash); + const bool restricted = m_restricted && ctx; + bool response_filled = fill_block_header_response(last_block, false, last_block_height, last_block_hash, res.block_header, req.fill_pow_hash && !restricted); if (!response_filled) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; @@ -1487,7 +1514,8 @@ namespace cryptonote return false; } uint64_t block_height = boost::get<txin_gen>(blk.miner_tx.vin.front()).height; - bool response_filled = fill_block_header_response(blk, orphan, block_height, block_hash, res.block_header, req.fill_pow_hash); + const bool restricted = m_restricted && ctx; + bool response_filled = fill_block_header_response(blk, orphan, block_height, block_hash, res.block_header, req.fill_pow_hash && !restricted); if (!response_filled) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; @@ -1537,7 +1565,8 @@ namespace cryptonote return false; } res.headers.push_back(block_header_response()); - bool response_filled = fill_block_header_response(blk, false, block_height, block_hash, res.headers.back(), req.fill_pow_hash); + 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) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; @@ -1571,7 +1600,8 @@ namespace cryptonote error_resp.message = "Internal error: can't get block by height. Height = " + std::to_string(req.height) + '.'; return false; } - bool response_filled = fill_block_header_response(blk, false, req.height, block_hash, res.block_header, req.fill_pow_hash); + const bool restricted = m_restricted && ctx; + bool response_filled = fill_block_header_response(blk, false, req.height, block_hash, res.block_header, req.fill_pow_hash && !restricted); if (!response_filled) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; @@ -1626,7 +1656,8 @@ namespace cryptonote return false; } uint64_t block_height = boost::get<txin_gen>(blk.miner_tx.vin.front()).height; - bool response_filled = fill_block_header_response(blk, orphan, block_height, block_hash, res.block_header, req.fill_pow_hash); + const bool restricted = m_restricted && ctx; + bool response_filled = fill_block_header_response(blk, orphan, block_height, block_hash, res.block_header, req.fill_pow_hash && !restricted); if (!response_filled) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 54fce3cd9..fe066b31b 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -53,6 +53,7 @@ namespace cryptonote { public: + static const command_line::arg_descriptor<bool> arg_public_node; static const command_line::arg_descriptor<std::string, false, true, 2> arg_rpc_bind_port; static const command_line::arg_descriptor<std::string> arg_rpc_restricted_bind_port; static const command_line::arg_descriptor<bool> arg_restricted_rpc; diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 88fa9b4e7..f65c7c8dd 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -84,7 +84,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 2 -#define CORE_RPC_VERSION_MINOR 3 +#define CORE_RPC_VERSION_MINOR 4 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -1055,7 +1055,14 @@ namespace cryptonote uint64_t speed; uint32_t threads_count; std::string address; + std::string pow_algorithm; bool is_background_mining_enabled; + uint8_t bg_idle_threshold; + uint8_t bg_min_idle_seconds; + bool bg_ignore_battery; + uint8_t bg_target; + uint32_t block_target; + uint64_t block_reward; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) @@ -1063,7 +1070,14 @@ namespace cryptonote KV_SERIALIZE(speed) KV_SERIALIZE(threads_count) KV_SERIALIZE(address) + KV_SERIALIZE(pow_algorithm) KV_SERIALIZE(is_background_mining_enabled) + KV_SERIALIZE(bg_idle_threshold) + KV_SERIALIZE(bg_min_idle_seconds) + KV_SERIALIZE(bg_ignore_battery) + KV_SERIALIZE(bg_target) + KV_SERIALIZE(block_target) + KV_SERIALIZE(block_reward) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<response_t> response; @@ -1372,16 +1386,17 @@ namespace cryptonote std::string host; uint32_t ip; uint16_t port; + uint16_t rpc_port; uint64_t last_seen; uint32_t pruning_seed; peer() = default; - peer(uint64_t id, const std::string &host, uint64_t last_seen, uint32_t pruning_seed) - : id(id), host(host), ip(0), port(0), last_seen(last_seen), pruning_seed(pruning_seed) + peer(uint64_t id, const std::string &host, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port) + : id(id), host(host), ip(0), port(0), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed) {} - peer(uint64_t id, uint32_t ip, uint16_t port, uint64_t last_seen, uint32_t pruning_seed) - : id(id), host(std::to_string(ip)), ip(ip), port(port), last_seen(last_seen), pruning_seed(pruning_seed) + peer(uint64_t id, uint32_t ip, uint16_t port, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port) + : id(id), host(std::to_string(ip)), ip(ip), port(port), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed) {} BEGIN_KV_SERIALIZE_MAP() @@ -1389,6 +1404,7 @@ namespace cryptonote KV_SERIALIZE(host) KV_SERIALIZE(ip) KV_SERIALIZE(port) + KV_SERIALIZE_OPT(rpc_port, (uint16_t)0) KV_SERIALIZE(last_seen) KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0) END_KV_SERIALIZE_MAP() diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/rpc/core_rpc_server_error_codes.h index 5a754749f..b13049e61 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp index 871f7d368..bde2339bc 100644 --- a/src/rpc/daemon_handler.cpp +++ b/src/rpc/daemon_handler.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -263,20 +263,43 @@ namespace rpc void DaemonHandler::handle(const SendRawTx::Request& req, SendRawTx::Response& res) { - auto tx_blob = cryptonote::tx_to_blob(req.tx); + handleTxBlob(cryptonote::tx_to_blob(req.tx), req.relay, res); + } + + void DaemonHandler::handle(const SendRawTxHex::Request& req, SendRawTxHex::Response& res) + { + std::string tx_blob; + if(!epee::string_tools::parse_hexstr_to_binbuff(req.tx_as_hex, tx_blob)) + { + MERROR("[SendRawTxHex]: Failed to parse tx from hexbuff: " << req.tx_as_hex); + res.status = Message::STATUS_FAILED; + res.error_details = "Invalid hex"; + return; + } + handleTxBlob(tx_blob, req.relay, res); + } + + void DaemonHandler::handleTxBlob(const std::string& tx_blob, bool relay, SendRawTx::Response& res) + { + if (!m_p2p.get_payload_object().is_synchronized()) + { + res.status = Message::STATUS_FAILED; + res.error_details = "Not ready to accept transactions; try again later"; + return; + } cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); tx_verification_context tvc = AUTO_VAL_INIT(tvc); - if(!m_core.handle_incoming_tx(tx_blob, tvc, false, false, !req.relay) || tvc.m_verifivation_failed) + if(!m_core.handle_incoming_tx(tx_blob, tvc, false, false, !relay) || tvc.m_verifivation_failed) { if (tvc.m_verifivation_failed) { - LOG_PRINT_L0("[on_send_raw_tx]: tx verification failed"); + MERROR("[SendRawTx]: tx verification failed"); } else { - LOG_PRINT_L0("[on_send_raw_tx]: Failed to process tx"); + MERROR("[SendRawTx]: Failed to process tx"); } res.status = Message::STATUS_FAILED; res.error_details = ""; @@ -328,9 +351,9 @@ namespace rpc return; } - if(!tvc.m_should_be_relayed || !req.relay) + if(!tvc.m_should_be_relayed || !relay) { - LOG_PRINT_L0("[on_send_raw_tx]: tx accepted, but not relayed"); + MERROR("[SendRawTx]: tx accepted, but not relayed"); res.error_details = "Not relayed"; res.relayed = false; res.status = Message::STATUS_OK; @@ -830,6 +853,7 @@ namespace rpc REQ_RESP_TYPES_MACRO(request_type, KeyImagesSpent, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, GetTxGlobalOutputIndices, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, SendRawTx, req_json, resp_message, handle); + REQ_RESP_TYPES_MACRO(request_type, SendRawTxHex, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, GetInfo, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, StartMining, req_json, resp_message, handle); REQ_RESP_TYPES_MACRO(request_type, StopMining, req_json, resp_message, handle); diff --git a/src/rpc/daemon_handler.h b/src/rpc/daemon_handler.h index 2c8ac3867..34723f676 100644 --- a/src/rpc/daemon_handler.h +++ b/src/rpc/daemon_handler.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // @@ -68,6 +68,8 @@ class DaemonHandler : public RpcHandler void handle(const SendRawTx::Request& req, SendRawTx::Response& res); + void handle(const SendRawTxHex::Request& req, SendRawTxHex::Response& res); + void handle(const StartMining::Request& req, StartMining::Response& res); void handle(const GetInfo::Request& req, GetInfo::Response& res); @@ -136,6 +138,8 @@ class DaemonHandler : public RpcHandler bool getBlockHeaderByHash(const crypto::hash& hash_in, cryptonote::rpc::BlockHeaderResponse& response); + void handleTxBlob(const std::string& tx_blob, bool relay, SendRawTx::Response& res); + cryptonote::core& m_core; t_p2p& m_p2p; }; diff --git a/src/rpc/daemon_messages.cpp b/src/rpc/daemon_messages.cpp index 7c7442014..cf0f5ece1 100644 --- a/src/rpc/daemon_messages.cpp +++ b/src/rpc/daemon_messages.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, The Monero Project // // All rights reserved. // @@ -42,6 +42,7 @@ const char* const GetTransactions::name = "get_transactions"; const char* const KeyImagesSpent::name = "key_images_spent"; const char* const GetTxGlobalOutputIndices::name = "get_tx_global_output_indices"; const char* const SendRawTx::name = "send_raw_tx"; +const char* const SendRawTxHex::name = "send_raw_tx_hex"; const char* const StartMining::name = "start_mining"; const char* const StopMining::name = "stop_mining"; const char* const MiningStatus::name = "mining_status"; @@ -292,6 +293,22 @@ void SendRawTx::Response::fromJson(rapidjson::Value& val) GET_FROM_JSON_OBJECT(val, relayed, relayed); } +rapidjson::Value SendRawTxHex::Request::toJson(rapidjson::Document& doc) const +{ + auto val = Message::toJson(doc); + + INSERT_INTO_JSON_OBJECT(val, doc, tx_as_hex, tx_as_hex); + INSERT_INTO_JSON_OBJECT(val, doc, relay, relay); + + return val; +} + +void SendRawTxHex::Request::fromJson(rapidjson::Value& val) +{ + GET_FROM_JSON_OBJECT(val, tx_as_hex, tx_as_hex); + GET_FROM_JSON_OBJECT(val, relay, relay); +} + rapidjson::Value StartMining::Request::toJson(rapidjson::Document& doc) const { auto val = Message::toJson(doc); diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h index d2014247c..c0d9aed0a 100644 --- a/src/rpc/daemon_messages.h +++ b/src/rpc/daemon_messages.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, The Monero Project // // All rights reserved. // @@ -171,6 +171,14 @@ BEGIN_RPC_MESSAGE_CLASS(SendRawTx); END_RPC_MESSAGE_RESPONSE; END_RPC_MESSAGE_CLASS; +BEGIN_RPC_MESSAGE_CLASS(SendRawTxHex); + BEGIN_RPC_MESSAGE_REQUEST; + RPC_MESSAGE_MEMBER(std::string, tx_as_hex); + RPC_MESSAGE_MEMBER(bool, relay); + END_RPC_MESSAGE_REQUEST; + using Response = SendRawTx::Response; +END_RPC_MESSAGE_CLASS; + BEGIN_RPC_MESSAGE_CLASS(StartMining); BEGIN_RPC_MESSAGE_REQUEST; RPC_MESSAGE_MEMBER(std::string, miner_address); diff --git a/src/rpc/daemon_rpc_version.h b/src/rpc/daemon_rpc_version.h index e20af5b21..b3eb9699b 100644 --- a/src/rpc/daemon_rpc_version.h +++ b/src/rpc/daemon_rpc_version.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, The Monero Project // // All rights reserved. // diff --git a/src/rpc/instanciations.cpp b/src/rpc/instanciations.cpp index ec8882982..94fdb5f18 100644 --- a/src/rpc/instanciations.cpp +++ b/src/rpc/instanciations.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/rpc/message.cpp b/src/rpc/message.cpp index 0ebe34efe..158b58005 100644 --- a/src/rpc/message.cpp +++ b/src/rpc/message.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, The Monero Project // // All rights reserved. // diff --git a/src/rpc/message.h b/src/rpc/message.h index 56087b998..2b7b61ab3 100644 --- a/src/rpc/message.h +++ b/src/rpc/message.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, The Monero Project // // All rights reserved. // diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h index 73cf28cec..26c5038f6 100644 --- a/src/rpc/message_data_structs.h +++ b/src/rpc/message_data_structs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, The Monero Project // // All rights reserved. // @@ -78,6 +78,7 @@ namespace rpc uint64_t id; uint32_t ip; uint16_t port; + uint16_t rpc_port; uint64_t last_seen; uint32_t pruning_seed; }; diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp index 60c78480a..f2be94f51 100644 --- a/src/rpc/rpc_args.cpp +++ b/src/rpc/rpc_args.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/rpc/rpc_args.h b/src/rpc/rpc_args.h index 8e375385b..216ba3712 100644 --- a/src/rpc/rpc_args.h +++ b/src/rpc/rpc_args.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/rpc/rpc_handler.h b/src/rpc/rpc_handler.h index e0d520408..2439eaa58 100644 --- a/src/rpc/rpc_handler.h +++ b/src/rpc/rpc_handler.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, The Monero Project // // All rights reserved. // diff --git a/src/rpc/zmq_server.cpp b/src/rpc/zmq_server.cpp index a2ff76668..ae748e052 100644 --- a/src/rpc/zmq_server.cpp +++ b/src/rpc/zmq_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, The Monero Project // // All rights reserved. // diff --git a/src/rpc/zmq_server.h b/src/rpc/zmq_server.h index 0cd906a3f..1b1e4c7cf 100644 --- a/src/rpc/zmq_server.h +++ b/src/rpc/zmq_server.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, The Monero Project // // All rights reserved. // diff --git a/src/serialization/CMakeLists.txt b/src/serialization/CMakeLists.txt index 5a6bebf09..28b775a37 100644 --- a/src/serialization/CMakeLists.txt +++ b/src/serialization/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2016-2018, The Monero Project +# Copyright (c) 2016-2019, The Monero Project # # All rights reserved. # diff --git a/src/serialization/binary_archive.h b/src/serialization/binary_archive.h index 242b8fd68..a0e4eff9d 100644 --- a/src/serialization/binary_archive.h +++ b/src/serialization/binary_archive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/serialization/binary_utils.h b/src/serialization/binary_utils.h index 79b30b337..790661e49 100644 --- a/src/serialization/binary_utils.h +++ b/src/serialization/binary_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/serialization/container.h b/src/serialization/container.h index 978a59d2a..dfe0f9691 100644 --- a/src/serialization/container.h +++ b/src/serialization/container.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h index 87172834f..d2537146e 100644 --- a/src/serialization/crypto.h +++ b/src/serialization/crypto.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/serialization/debug_archive.h b/src/serialization/debug_archive.h index c6033a399..093ca41eb 100644 --- a/src/serialization/debug_archive.h +++ b/src/serialization/debug_archive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/serialization/deque.h b/src/serialization/deque.h index 994d3f195..97f16d0a5 100644 --- a/src/serialization/deque.h +++ b/src/serialization/deque.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/serialization/json_archive.h b/src/serialization/json_archive.h index 04436c21c..c9f4e175e 100644 --- a/src/serialization/json_archive.h +++ b/src/serialization/json_archive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp index f0fc35855..73e17a775 100644 --- a/src/serialization/json_object.cpp +++ b/src/serialization/json_object.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, The Monero Project // // All rights reserved. // @@ -569,6 +569,7 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::connection_info& in INSERT_INTO_JSON_OBJECT(val, doc, ip, info.ip); INSERT_INTO_JSON_OBJECT(val, doc, port, info.port); + INSERT_INTO_JSON_OBJECT(val, doc, rpc_port, info.rpc_port); INSERT_INTO_JSON_OBJECT(val, doc, peer_id, info.peer_id); @@ -603,6 +604,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::connection_info& inf GET_FROM_JSON_OBJECT(val, info.ip, ip); GET_FROM_JSON_OBJECT(val, info.port, port); + GET_FROM_JSON_OBJECT(val, info.rpc_port, rpc_port); GET_FROM_JSON_OBJECT(val, info.peer_id, peer_id); @@ -732,6 +734,7 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::peer& peer, ra INSERT_INTO_JSON_OBJECT(val, doc, id, peer.id); INSERT_INTO_JSON_OBJECT(val, doc, ip, peer.ip); INSERT_INTO_JSON_OBJECT(val, doc, port, peer.port); + INSERT_INTO_JSON_OBJECT(val, doc, rpc_port, peer.rpc_port); INSERT_INTO_JSON_OBJECT(val, doc, last_seen, peer.last_seen); INSERT_INTO_JSON_OBJECT(val, doc, pruning_seed, peer.pruning_seed); } @@ -747,6 +750,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::peer& peer) GET_FROM_JSON_OBJECT(val, peer.id, id); GET_FROM_JSON_OBJECT(val, peer.ip, ip); GET_FROM_JSON_OBJECT(val, peer.port, port); + GET_FROM_JSON_OBJECT(val, peer.rpc_port, rpc_port); GET_FROM_JSON_OBJECT(val, peer.last_seen, last_seen); GET_FROM_JSON_OBJECT(val, peer.pruning_seed, pruning_seed); } diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h index b6384ca0d..c804d148b 100644 --- a/src/serialization/json_object.h +++ b/src/serialization/json_object.h @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, The Monero Project // // All rights reserved. // diff --git a/src/serialization/json_utils.h b/src/serialization/json_utils.h index 16f32cf66..05122f3bd 100644 --- a/src/serialization/json_utils.h +++ b/src/serialization/json_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/serialization/list.h b/src/serialization/list.h index ef0063a98..b7b65e184 100644 --- a/src/serialization/list.h +++ b/src/serialization/list.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/serialization/pair.h b/src/serialization/pair.h index 67105d530..aed0c4699 100644 --- a/src/serialization/pair.h +++ b/src/serialization/pair.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h index 2050e88e2..007bf265f 100644 --- a/src/serialization/serialization.h +++ b/src/serialization/serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/serialization/set.h b/src/serialization/set.h index edf170852..ae602a2f1 100644 --- a/src/serialization/set.h +++ b/src/serialization/set.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/serialization/string.h b/src/serialization/string.h index 97268d454..fd8450f7b 100644 --- a/src/serialization/string.h +++ b/src/serialization/string.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/serialization/unordered_set.h b/src/serialization/unordered_set.h index b277f0c4a..6dd01fd93 100644 --- a/src/serialization/unordered_set.h +++ b/src/serialization/unordered_set.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/serialization/variant.h b/src/serialization/variant.h index 1d00ab461..5a179ca87 100644 --- a/src/serialization/variant.h +++ b/src/serialization/variant.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/serialization/vector.h b/src/serialization/vector.h index f180b5353..ad96582a5 100644 --- a/src/serialization/vector.h +++ b/src/serialization/vector.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/simplewallet/CMakeLists.txt b/src/simplewallet/CMakeLists.txt index e292f85dd..030867dc0 100644 --- a/src/simplewallet/CMakeLists.txt +++ b/src/simplewallet/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 8c0b300ad..f390b7675 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -190,7 +190,7 @@ namespace const char* USAGE_CHECK_RESERVE_PROOF("check_reserve_proof <address> <signature_file> [<message>]"); const char* USAGE_SHOW_TRANSFERS("show_transfers [in|out|pending|failed|pool|coinbase] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]"); const char* USAGE_UNSPENT_OUTPUTS("unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]]"); - const char* USAGE_RESCAN_BC("rescan_bc [hard]"); + const char* USAGE_RESCAN_BC("rescan_bc [hard|soft|keep_ki] [start_height=0]"); const char* USAGE_SET_TX_NOTE("set_tx_note <txid> [free text note]"); const char* USAGE_GET_TX_NOTE("get_tx_note <txid>"); const char* USAGE_GET_DESCRIPTION("get_description"); @@ -2049,7 +2049,7 @@ bool simple_wallet::cold_sign_tx(const std::vector<tools::wallet2::pending_tx>& m_wallet->cold_tx_aux_import(exported_txs.ptx, tx_aux); // import key images - return m_wallet->import_key_images(exported_txs.key_images); + return m_wallet->import_key_images(exported_txs, 0, true); } bool simple_wallet::set_always_confirm_transfers(const std::vector<std::string> &args/* = std::vector<std::string>()*/) @@ -4686,12 +4686,12 @@ boost::optional<epee::wipeable_string> simple_wallet::on_get_password(const char return pwd_container->password(); } //---------------------------------------------------------------------------------------------------- -void simple_wallet::on_button_request() +void simple_wallet::on_device_button_request(uint64_t code) { message_writer(console_color_white, false) << tr("Device requires attention"); } //---------------------------------------------------------------------------------------------------- -void simple_wallet::on_pin_request(epee::wipeable_string & pin) +boost::optional<epee::wipeable_string> simple_wallet::on_device_pin_request() { #ifdef HAVE_READLINE rdln::suspend_readline pause_readline; @@ -4699,14 +4699,14 @@ void simple_wallet::on_pin_request(epee::wipeable_string & pin) std::string msg = tr("Enter device PIN"); auto pwd_container = tools::password_container::prompt(false, msg.c_str()); THROW_WALLET_EXCEPTION_IF(!pwd_container, tools::error::password_entry_failed, tr("Failed to read device PIN")); - pin = pwd_container->password(); + return pwd_container->password(); } //---------------------------------------------------------------------------------------------------- -void simple_wallet::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) +boost::optional<epee::wipeable_string> simple_wallet::on_device_passphrase_request(bool on_device) { if (on_device){ message_writer(console_color_white, true) << tr("Please enter the device passphrase on the device"); - return; + return boost::none; } #ifdef HAVE_READLINE @@ -4715,13 +4715,13 @@ void simple_wallet::on_passphrase_request(bool on_device, epee::wipeable_string std::string msg = tr("Enter device passphrase"); auto pwd_container = tools::password_container::prompt(false, msg.c_str()); THROW_WALLET_EXCEPTION_IF(!pwd_container, tools::error::password_entry_failed, tr("Failed to read device passphrase")); - passphrase = pwd_container->password(); + return pwd_container->password(); } //---------------------------------------------------------------------------------------------------- void simple_wallet::on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money) { // Key image sync after the first refresh - if (!m_wallet->get_account().get_device().has_tx_cold_sign()) { + if (!m_wallet->get_account().get_device().has_tx_cold_sign() || m_wallet->get_account().get_device().has_ki_live_refresh()) { return; } @@ -4748,8 +4748,16 @@ bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bo LOCK_IDLE_SCOPE(); + crypto::hash transfer_hash_pre{}; + uint64_t height_pre, height_post; + if (reset != ResetNone) - m_wallet->rescan_blockchain(reset == ResetHard, false); + { + if (reset == ResetSoftKeepKI) + height_pre = m_wallet->hash_m_transfers(-1, transfer_hash_pre); + + m_wallet->rescan_blockchain(reset == ResetHard, false, reset == ResetSoftKeepKI); + } #ifdef HAVE_READLINE rdln::suspend_readline pause_readline; @@ -4766,6 +4774,18 @@ bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bo m_in_manual_refresh.store(true, std::memory_order_relaxed); epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);}); m_wallet->refresh(m_wallet->is_trusted_daemon(), start_height, fetched_blocks, received_money); + + if (reset == ResetSoftKeepKI) + { + m_wallet->finish_rescan_bc_keep_key_images(height_pre, transfer_hash_pre); + + height_post = m_wallet->get_num_transfer_details(); + if (height_pre != height_post) + { + message_writer() << tr("New transfer received since rescan was started. Key images are incomplete."); + } + } + ok = true; // Clear line "Height xxx of xxx" std::cout << "\r \r"; @@ -6752,7 +6772,7 @@ bool simple_wallet::get_tx_key(const std::vector<std::string> &args_) { std::vector<std::string> local_args = args_; - if (m_wallet->key_on_device()) + if (m_wallet->key_on_device() && m_wallet->get_account().get_device().get_type() != hw::device::TREZOR) { fail_msg_writer() << tr("command not supported by HW wallet"); return true; @@ -6773,7 +6793,9 @@ bool simple_wallet::get_tx_key(const std::vector<std::string> &args_) crypto::secret_key tx_key; std::vector<crypto::secret_key> additional_tx_keys; - if (m_wallet->get_tx_key(txid, tx_key, additional_tx_keys)) + + bool found_tx_key = m_wallet->get_tx_key(txid, tx_key, additional_tx_keys); + if (found_tx_key) { ostringstream oss; oss << epee::string_tools::pod_to_hex(tx_key); @@ -6849,7 +6871,7 @@ bool simple_wallet::set_tx_key(const std::vector<std::string> &args_) //---------------------------------------------------------------------------------------------------- bool simple_wallet::get_tx_proof(const std::vector<std::string> &args) { - if (m_wallet->key_on_device()) + if (m_wallet->key_on_device() && m_wallet->get_account().get_device().get_type() != hw::device::TREZOR) { fail_msg_writer() << tr("command not supported by HW wallet"); return true; @@ -7773,18 +7795,43 @@ bool simple_wallet::unspent_outputs(const std::vector<std::string> &args_) //---------------------------------------------------------------------------------------------------- bool simple_wallet::rescan_blockchain(const std::vector<std::string> &args_) { - bool hard = false; + uint64_t start_height = 0; + ResetType reset_type = ResetSoft; + if (!args_.empty()) { - if (args_[0] != "hard") + if (args_[0] == "hard") + { + reset_type = ResetHard; + } + else if (args_[0] == "soft") + { + reset_type = ResetSoft; + } + else if (args_[0] == "keep_ki") + { + reset_type = ResetSoftKeepKI; + } + else { PRINT_USAGE(USAGE_RESCAN_BC); return true; } - hard = true; + + if (args_.size() > 1) + { + try + { + start_height = boost::lexical_cast<uint64_t>( args_[1] ); + } + catch(const boost::bad_lexical_cast &) + { + start_height = 0; + } + } } - if (hard) + if (reset_type == ResetHard) { message_writer() << tr("Warning: this will lose any information which can not be recovered from the blockchain."); message_writer() << tr("This includes destination addresses, tx secret keys, tx notes, etc"); @@ -7795,7 +7842,20 @@ bool simple_wallet::rescan_blockchain(const std::vector<std::string> &args_) return true; } } - return refresh_main(0, hard ? ResetHard : ResetSoft, true); + + const uint64_t wallet_from_height = m_wallet->get_refresh_from_block_height(); + if (start_height > wallet_from_height) + { + message_writer() << tr("Warning: your restore height is higher than wallet restore height: ") << wallet_from_height; + std::string confirm = input_line(tr("Rescan anyway ? (Y/Yes/N/No): ")); + if(!std::cin.eof()) + { + if (!command_line::is_yes(confirm)) + return true; + } + } + + return refresh_main(start_height, reset_type, true); } //---------------------------------------------------------------------------------------------------- void simple_wallet::check_for_messages() @@ -8597,13 +8657,8 @@ bool simple_wallet::import_key_images(const std::vector<std::string> &args) { uint64_t spent = 0, unspent = 0; uint64_t height = m_wallet->import_key_images(filename, spent, unspent); - if (height > 0) - { - success_msg_writer() << "Signed key images imported to height " << height << ", " - << print_money(spent) << " spent, " << print_money(unspent) << " unspent"; - } else { - fail_msg_writer() << "Failed to import key images"; - } + success_msg_writer() << "Signed key images imported to height " << height << ", " + << print_money(spent) << " spent, " << print_money(unspent) << " unspent"; } catch (const std::exception &e) { @@ -9954,6 +10009,16 @@ bool simple_wallet::mms(const std::vector<std::string> &args) { try { + m_wallet->get_multisig_wallet_state(); + } + catch(const std::exception &e) + { + fail_msg_writer() << tr("MMS not available in this wallet"); + return true; + } + + try + { mms::message_store& ms = m_wallet->get_message_store(); if (args.size() == 0) { diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index c3dc16d96..7bcb92190 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -84,7 +84,7 @@ namespace cryptonote std::string get_command_usage(const std::vector<std::string> &args); private: - enum ResetType { ResetNone, ResetSoft, ResetHard }; + enum ResetType { ResetNone, ResetSoft, ResetHard, ResetSoftKeepKI }; bool handle_command_line(const boost::program_options::variables_map& vm); @@ -300,9 +300,9 @@ namespace cryptonote 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); virtual boost::optional<epee::wipeable_string> on_get_password(const char *reason); - virtual void on_button_request(); - virtual void on_pin_request(epee::wipeable_string & pin); - virtual void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase); + virtual void on_device_button_request(uint64_t code); + virtual boost::optional<epee::wipeable_string> on_device_pin_request(); + virtual boost::optional<epee::wipeable_string> on_device_passphrase_request(bool on_device); //---------------------------------------------------------- friend class refresh_progress_reporter_t; diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index 2991f75c5..efd61cb5a 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/src/wallet/api/CMakeLists.txt b/src/wallet/api/CMakeLists.txt index d6f2bf6b7..3376ec70e 100644 --- a/src/wallet/api/CMakeLists.txt +++ b/src/wallet/api/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/src/wallet/api/address_book.cpp b/src/wallet/api/address_book.cpp index 7ef011e06..7be78bba7 100644 --- a/src/wallet/api/address_book.cpp +++ b/src/wallet/api/address_book.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/address_book.h b/src/wallet/api/address_book.h index f4ca68efd..92e6eaa17 100644 --- a/src/wallet/api/address_book.h +++ b/src/wallet/api/address_book.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp index 913e3156f..52510164a 100644 --- a/src/wallet/api/pending_transaction.cpp +++ b/src/wallet/api/pending_transaction.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -109,6 +109,23 @@ bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite) } m_wallet.pauseRefresh(); + + const bool tx_cold_signed = m_wallet.m_wallet->get_account().get_device().has_tx_cold_sign(); + if (tx_cold_signed){ + std::unordered_set<size_t> selected_transfers; + for(const tools::wallet2::pending_tx & ptx : m_pending_tx){ + for(size_t s : ptx.selected_transfers){ + selected_transfers.insert(s); + } + } + + m_wallet.m_wallet->cold_tx_aux_import(m_pending_tx, m_tx_device_aux); + bool r = m_wallet.m_wallet->import_key_images(m_key_images, 0, selected_transfers); + if (!r){ + throw runtime_error("Cold sign transaction submit failed - key image sync fail"); + } + } + while (!m_pending_tx.empty()) { auto & ptx = m_pending_tx.back(); m_wallet.m_wallet->commit_tx(ptx); diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h index 50b9f07ef..92801d77d 100644 --- a/src/wallet/api/pending_transaction.h +++ b/src/wallet/api/pending_transaction.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -67,6 +67,8 @@ private: std::string m_errorString; std::vector<tools::wallet2::pending_tx> m_pending_tx; std::unordered_set<crypto::public_key> m_signers; + std::vector<std::string> m_tx_device_aux; + std::vector<crypto::key_image> m_key_images; }; diff --git a/src/wallet/api/subaddress.cpp b/src/wallet/api/subaddress.cpp index 61dbbf4b0..8a1d34864 100644 --- a/src/wallet/api/subaddress.cpp +++ b/src/wallet/api/subaddress.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/subaddress.h b/src/wallet/api/subaddress.h index f3db7d97b..87585ec16 100644 --- a/src/wallet/api/subaddress.h +++ b/src/wallet/api/subaddress.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/subaddress_account.cpp b/src/wallet/api/subaddress_account.cpp index 4765465c3..9bc9d1d91 100644 --- a/src/wallet/api/subaddress_account.cpp +++ b/src/wallet/api/subaddress_account.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/subaddress_account.h b/src/wallet/api/subaddress_account.h index b052182f8..358e446d4 100644 --- a/src/wallet/api/subaddress_account.h +++ b/src/wallet/api/subaddress_account.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp index ba46a6904..f4ad8b1f6 100644 --- a/src/wallet/api/transaction_history.cpp +++ b/src/wallet/api/transaction_history.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/transaction_history.h b/src/wallet/api/transaction_history.h index 7bdce97e2..67fe1989d 100644 --- a/src/wallet/api/transaction_history.h +++ b/src/wallet/api/transaction_history.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/transaction_info.cpp b/src/wallet/api/transaction_info.cpp index cc3209609..21573c6f6 100644 --- a/src/wallet/api/transaction_info.cpp +++ b/src/wallet/api/transaction_info.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/transaction_info.h b/src/wallet/api/transaction_info.h index 37e0445d9..d5c8f31cf 100644 --- a/src/wallet/api/transaction_info.h +++ b/src/wallet/api/transaction_info.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp index 29910a3b6..c2c04cbc3 100644 --- a/src/wallet/api/unsigned_transaction.cpp +++ b/src/wallet/api/unsigned_transaction.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/unsigned_transaction.h b/src/wallet/api/unsigned_transaction.h index 8a3330014..f1af80fa1 100644 --- a/src/wallet/api/unsigned_transaction.h +++ b/src/wallet/api/unsigned_transaction.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/utils.cpp b/src/wallet/api/utils.cpp index 86fe56564..24252868a 100644 --- a/src/wallet/api/utils.cpp +++ b/src/wallet/api/utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 44cd67657..d7226b656 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -242,6 +242,42 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback } } + virtual void on_device_button_request(uint64_t code) + { + if (m_listener) { + m_listener->onDeviceButtonRequest(code); + } + } + + virtual boost::optional<epee::wipeable_string> on_device_pin_request() + { + if (m_listener) { + auto pin = m_listener->onDevicePinRequest(); + if (pin){ + return boost::make_optional(epee::wipeable_string((*pin).data(), (*pin).size())); + } + } + return boost::none; + } + + virtual boost::optional<epee::wipeable_string> on_device_passphrase_request(bool on_device) + { + if (m_listener) { + auto passphrase = m_listener->onDevicePassphraseRequest(on_device); + if (!on_device && passphrase) { + return boost::make_optional(epee::wipeable_string((*passphrase).data(), (*passphrase).size())); + } + } + return boost::none; + } + + virtual void on_device_progress(const hw::device_progress & event) + { + if (m_listener) { + m_listener->onDeviceProgress(DeviceProgress(event.progress(), event.indeterminate())); + } + } + WalletListener * m_listener; WalletImpl * m_wallet; }; @@ -785,6 +821,28 @@ bool WalletImpl::setPassword(const std::string &password) return status() == Status_Ok; } +bool WalletImpl::setDevicePin(const std::string &pin) +{ + clearStatus(); + try { + m_wallet->get_account().get_device().set_pin(epee::wipeable_string(pin.data(), pin.size())); + } catch (const std::exception &e) { + setStatusError(e.what()); + } + return status() == Status_Ok; +} + +bool WalletImpl::setDevicePassphrase(const std::string &passphrase) +{ + clearStatus(); + try { + m_wallet->get_account().get_device().set_passphrase(epee::wipeable_string(passphrase.data(), passphrase.size())); + } catch (const std::exception &e) { + setStatusError(e.what()); + } + return status() == Status_Ok; +} + std::string WalletImpl::address(uint32_t accountIndex, uint32_t addressIndex) const { return m_wallet->get_subaddress_as_str({accountIndex, addressIndex}); @@ -1428,8 +1486,12 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const extra, subaddr_account, subaddr_indices); } + pendingTxPostProcess(transaction); + if (multisig().isMultisig) { - transaction->m_signers = m_wallet->make_multisig_tx_set(transaction->m_pending_tx).m_signers; + auto tx_set = m_wallet->make_multisig_tx_set(transaction->m_pending_tx); + transaction->m_pending_tx = tx_set.m_ptx; + transaction->m_signers = tx_set.m_signers; } } catch (const tools::error::daemon_busy&) { // TODO: make it translatable with "tr"? @@ -1511,6 +1573,7 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction() do { try { transaction->m_pending_tx = m_wallet->create_unmixable_sweep_transactions(); + pendingTxPostProcess(transaction); } catch (const tools::error::daemon_busy&) { // TODO: make it translatable with "tr"? @@ -2093,6 +2156,21 @@ bool WalletImpl::isNewWallet() const return !(blockChainHeight() > 1 || m_recoveringFromSeed || m_recoveringFromDevice || m_rebuildWalletCache) && !watchOnly(); } +void WalletImpl::pendingTxPostProcess(PendingTransactionImpl * pending) +{ + // If the device being used is HW device with cold signing protocol, cold sign then. + if (!m_wallet->get_account().get_device().has_tx_cold_sign()){ + return; + } + + tools::wallet2::signed_tx_set exported_txs; + std::vector<cryptonote::address_parse_info> dsts_info; + + m_wallet->cold_sign_tx(pending->m_pending_tx, exported_txs, dsts_info, pending->m_tx_device_aux); + pending->m_key_images = exported_txs.key_images; + pending->m_pending_tx = exported_txs.ptx; +} + bool WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit, bool ssl) { // claim RPC so there's no in-memory encryption for now @@ -2325,6 +2403,11 @@ bool WalletImpl::isKeysFileLocked() { return m_wallet->is_keys_file_locked(); } + +uint64_t WalletImpl::coldKeyImageSync(uint64_t &spent, uint64_t &unspent) +{ + return m_wallet->cold_key_image_sync(spent, unspent); +} } // namespace namespace Bitmonero = Monero; diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 55240d64f..9e07b6e19 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -89,6 +89,8 @@ public: std::string errorString() const override; void statusWithErrorString(int& status, std::string& errorString) const override; bool setPassword(const std::string &password) override; + bool setDevicePin(const std::string &password) override; + bool setDevicePassphrase(const std::string &password) override; std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const override; std::string integratedAddress(const std::string &payment_id) const override; std::string secretViewKey() const override; @@ -198,6 +200,7 @@ public: virtual bool lockKeysFile() override; virtual bool unlockKeysFile() override; virtual bool isKeysFileLocked() override; + virtual uint64_t coldKeyImageSync(uint64_t &spent, uint64_t &unspent) override; private: void clearStatus() const; @@ -209,6 +212,7 @@ private: bool daemonSynced() const; void stopRefresh(); bool isNewWallet() const; + void pendingTxPostProcess(PendingTransactionImpl * pending); bool doInit(const std::string &daemon_address, uint64_t upper_transaction_size_limit = 0, bool ssl = false); private: diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 5c301974f..ee1d6ae79 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -324,6 +324,19 @@ struct MultisigState { uint32_t total; }; + +struct DeviceProgress { + DeviceProgress(): m_progress(0), m_indeterminate(false) {} + DeviceProgress(double progress, bool indeterminate=false): m_progress(progress), m_indeterminate(indeterminate) {} + + virtual double progress() const { return m_progress; } + virtual bool indeterminate() const { return m_indeterminate; } + +protected: + double m_progress; + bool m_indeterminate; +}; + struct WalletListener { virtual ~WalletListener() = 0; @@ -364,6 +377,31 @@ struct WalletListener * @brief refreshed - called when wallet refreshed by background thread or explicitly refreshed by calling "refresh" synchronously */ virtual void refreshed() = 0; + + /** + * @brief called by device if the action is required + */ + virtual void onDeviceButtonRequest(uint64_t code) {} + + /** + * @brief called by device when PIN is needed + */ + virtual optional<std::string> onDevicePinRequest() { + throw std::runtime_error("Not supported"); + } + + /** + * @brief called by device when passphrase entry is needed + */ + virtual optional<std::string> onDevicePassphraseRequest(bool on_device) { + if (!on_device) throw std::runtime_error("Not supported"); + return optional<std::string>(); + } + + /** + * @brief Signalizes device operation progress + */ + virtual void onDeviceProgress(const DeviceProgress & event) {}; }; @@ -375,7 +413,8 @@ struct Wallet { enum Device { Device_Software = 0, - Device_Ledger = 1 + Device_Ledger = 1, + Device_Trezor = 2 }; enum Status { @@ -401,6 +440,8 @@ struct Wallet //! returns both error and error string atomically. suggested to use in instead of status() and errorString() virtual void statusWithErrorString(int& status, std::string& errorString) const = 0; virtual bool setPassword(const std::string &password) = 0; + virtual bool setDevicePin(const std::string &password) { return false; }; + virtual bool setDevicePassphrase(const std::string &password) { return false; }; virtual std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const = 0; std::string mainAddress() const { return address(0, 0); } virtual std::string path() const = 0; @@ -947,6 +988,9 @@ struct Wallet * \return Device they are on */ virtual Device getDeviceType() const = 0; + + //! cold-device protocol key image sync + virtual uint64_t coldKeyImageSync(uint64_t &spent, uint64_t &unspent) = 0; }; /** diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index 89fe01c0d..f584e88ac 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h index b3c0d6c00..0c83d794f 100644 --- a/src/wallet/api/wallet_manager.h +++ b/src/wallet/api/wallet_manager.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index 605531e59..f5f3c0e1b 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h index 65f13eaaa..3630aec08 100644 --- a/src/wallet/node_rpc_proxy.h +++ b/src/wallet/node_rpc_proxy.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 085e6075f..a46da42db 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -793,9 +793,8 @@ uint64_t calculate_fee(bool use_per_byte_fee, const cryptonote::transaction &tx, return calculate_fee(base_fee, blob_size, fee_multiplier); } -crypto::hash8 get_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev) +bool get_short_payment_id(crypto::hash8 &payment_id8, const tools::wallet2::pending_tx &ptx, hw::device &hwdev) { - crypto::hash8 payment_id8 = null_hash8; std::vector<tx_extra_field> tx_extra_fields; parse_tx_extra(ptx.tx.extra, tx_extra_fields); // ok if partially parsed cryptonote::tx_extra_nonce extra_nonce; @@ -806,19 +805,19 @@ crypto::hash8 get_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::de if (ptx.dests.empty()) { MWARNING("Encrypted payment id found, but no destinations public key, cannot decrypt"); - return crypto::null_hash8; + return false; } - hwdev.decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key); + return hwdev.decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key); } } - return payment_id8; + return false; } tools::wallet2::tx_construction_data get_construction_data_with_decrypted_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev) { tools::wallet2::tx_construction_data construction_data = ptx.construction_data; - crypto::hash8 payment_id = get_short_payment_id(ptx,hwdev); - if (payment_id != null_hash8) + crypto::hash8 payment_id = null_hash8; + if (get_short_payment_id(payment_id, ptx, hwdev)) { // Remove encrypted remove_field_from_tx_extra(construction_data.extra, typeid(cryptonote::tx_extra_nonce)); @@ -929,22 +928,30 @@ wallet_keys_unlocker::~wallet_keys_unlocker() } } -void wallet_device_callback::on_button_request() +void wallet_device_callback::on_button_request(uint64_t code) { if (wallet) - wallet->on_button_request(); + wallet->on_device_button_request(code); } -void wallet_device_callback::on_pin_request(epee::wipeable_string & pin) +boost::optional<epee::wipeable_string> wallet_device_callback::on_pin_request() { if (wallet) - wallet->on_pin_request(pin); + return wallet->on_device_pin_request(); + return boost::none; } -void wallet_device_callback::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) +boost::optional<epee::wipeable_string> wallet_device_callback::on_passphrase_request(bool on_device) { if (wallet) - wallet->on_passphrase_request(on_device, passphrase); + return wallet->on_device_passphrase_request(on_device); + return boost::none; +} + +void wallet_device_callback::on_progress(const hw::device_progress& event) +{ + if (wallet) + wallet->on_device_progress(event); } wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): @@ -1495,11 +1502,17 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons } THROW_WALLET_EXCEPTION_IF(std::find(outs.begin(), outs.end(), i) != outs.end(), error::wallet_internal_error, "Same output cannot be added twice"); - outs.push_back(i); if (tx_scan_info.money_transfered == 0 && !miner_tx) { tx_scan_info.money_transfered = tools::decodeRct(tx.rct_signatures, tx_scan_info.received->derivation, i, tx_scan_info.mask, m_account.get_device()); } + if (tx_scan_info.money_transfered == 0) + { + MERROR("Invalid output amount, skipping"); + tx_scan_info.error = true; + return; + } + outs.push_back(i); THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs[tx_scan_info.received->index] >= std::numeric_limits<uint64_t>::max() - tx_scan_info.money_transfered, error::wallet_internal_error, "Overflow in received amounts"); tx_money_got_in_outs[tx_scan_info.received->index] += tx_scan_info.money_transfered; @@ -2878,6 +2891,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo std::vector<parsed_block> parsed_blocks; bool refreshed = false; std::shared_ptr<std::map<std::pair<uint64_t, uint64_t>, size_t>> output_tracker_cache; + hw::device &hwdev = m_account.get_device(); // pull the first set of blocks get_short_chain_history(short_chain_history, (m_first_refresh_done || trusted_daemon) ? 1 : FIRST_REFRESH_GRANULARITY); @@ -3034,6 +3048,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo LOG_PRINT_L1("Failed to check pending transactions"); } + hwdev.computing_key_images(false); m_first_refresh_done = true; LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", balance (all accounts): " << print_money(balance_all()) << ", unlocked: " << print_money(unlocked_balance_all())); @@ -3217,6 +3232,26 @@ bool wallet2::clear() m_device_last_key_image_sync = 0; return true; } +//---------------------------------------------------------------------------------------------------- +void wallet2::clear_soft(bool keep_key_images) +{ + m_blockchain.clear(); + m_transfers.clear(); + if (!keep_key_images) + m_key_images.clear(); + m_pub_keys.clear(); + m_unconfirmed_txs.clear(); + m_payments.clear(); + m_confirmed_txs.clear(); + m_unconfirmed_payments.clear(); + m_scanned_pool_txs[0].clear(); + m_scanned_pool_txs[1].clear(); + + cryptonote::block b; + generate_genesis(b); + m_blockchain.push_back(get_block_hash(b)); + m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); +} /*! * \brief Stores wallet information to wallet file. @@ -5085,7 +5120,14 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass MERROR("Failed to save rings, will try again next time"); } - m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file); + try + { + m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file); + } + catch (const std::exception &e) + { + MERROR("Failed to initialize MMS, it will be unusable"); + } } //---------------------------------------------------------------------------------------------------- void wallet2::trim_hashchain() @@ -5463,8 +5505,12 @@ void wallet2::rescan_spent() } } //---------------------------------------------------------------------------------------------------- -void wallet2::rescan_blockchain(bool hard, bool refresh) +void wallet2::rescan_blockchain(bool hard, bool refresh, bool keep_key_images) { + CHECK_AND_ASSERT_THROW_MES(!hard || !keep_key_images, "Cannot preserve key images on hard rescan"); + const size_t transfers_cnt = m_transfers.size(); + crypto::hash transfers_hash{}; + if(hard) { clear(); @@ -5472,25 +5518,16 @@ void wallet2::rescan_blockchain(bool hard, bool refresh) } else { - m_blockchain.clear(); - m_transfers.clear(); - m_key_images.clear(); - m_pub_keys.clear(); - m_unconfirmed_txs.clear(); - m_payments.clear(); - m_confirmed_txs.clear(); - m_unconfirmed_payments.clear(); - m_scanned_pool_txs[0].clear(); - m_scanned_pool_txs[1].clear(); - - cryptonote::block b; - generate_genesis(b); - m_blockchain.push_back(get_block_hash(b)); - m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); + if (keep_key_images && refresh) + hash_m_transfers((int64_t) transfers_cnt, transfers_hash); + clear_soft(keep_key_images); } if (refresh) this->refresh(false); + + if (refresh && keep_key_images) + finish_rescan_bc_keep_key_images(transfers_cnt, transfers_hash); } //---------------------------------------------------------------------------------------------------- bool wallet2::is_transfer_unlocked(const transfer_details& td) const @@ -6329,17 +6366,17 @@ bool wallet2::save_multisig_tx(const std::vector<pending_tx>& ptx_vector, const return epee::file_io_utils::save_string_to_file(filename, ciphertext); } //---------------------------------------------------------------------------------------------------- -bool wallet2::load_multisig_tx(cryptonote::blobdata s, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func) +bool wallet2::parse_multisig_tx_from_str(std::string multisig_tx_st, multisig_tx_set &exported_txs) const { const size_t magiclen = strlen(MULTISIG_UNSIGNED_TX_PREFIX); - if (strncmp(s.c_str(), MULTISIG_UNSIGNED_TX_PREFIX, magiclen)) + if (strncmp(multisig_tx_st.c_str(), MULTISIG_UNSIGNED_TX_PREFIX, magiclen)) { LOG_PRINT_L0("Bad magic from multisig tx data"); return false; } try { - s = decrypt_with_view_secret_key(std::string(s, magiclen)); + multisig_tx_st = decrypt_with_view_secret_key(std::string(multisig_tx_st, magiclen)); } catch (const std::exception &e) { @@ -6348,7 +6385,7 @@ bool wallet2::load_multisig_tx(cryptonote::blobdata s, multisig_tx_set &exported } try { - std::istringstream iss(s); + std::istringstream iss(multisig_tx_st); boost::archive::portable_binary_iarchive ar(iss); ar >> exported_txs; } @@ -6370,6 +6407,17 @@ bool wallet2::load_multisig_tx(cryptonote::blobdata s, multisig_tx_set &exported CHECK_AND_ASSERT_MES(ptx.construction_data.sources.size() == ptx.tx.vin.size(), false, "Mismatched sources/vin sizes"); } + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::load_multisig_tx(cryptonote::blobdata s, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func) +{ + if(!parse_multisig_tx_from_str(s, exported_txs)) + { + LOG_PRINT_L0("Failed to parse multisig transaction from string"); + return false; + } + LOG_PRINT_L1("Loaded multisig tx unsigned data from binary: " << exported_txs.m_ptx.size() << " transactions"); for (auto &ptx: exported_txs.m_ptx) LOG_PRINT_L0(cryptonote::obj_to_json_str(ptx.tx)); @@ -7019,6 +7067,17 @@ bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_out CHECK_AND_ASSERT_MES(!outs.empty(), false, "internal error: outs is empty"); if (std::find(outs.back().begin(), outs.back().end(), item) != outs.back().end()) // don't add duplicates return false; + // check the keys are valid + if (!rct::isInMainSubgroup(rct::pk2rct(output_public_key))) + { + MWARNING("Key " << output_public_key << " at index " << global_index << " is not in the main subgroup"); + return false; + } + if (!rct::isInMainSubgroup(mask)) + { + MWARNING("Commitment " << mask << " at index " << global_index << " is not in the main subgroup"); + return false; + } // if (is_output_blackballed(output_public_key)) // don't add blackballed outputs // return false; outs.back().push_back(item); @@ -8087,6 +8146,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry else { change_dts.addr = get_subaddress({subaddr_account, 0}); + change_dts.is_subaddress = subaddr_account != 0; splitted_dsts.push_back(change_dts); } @@ -8798,6 +8858,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp boost::unique_lock<hw::device> hwdev_lock (hwdev); hw::reset_mode rst(hwdev); + auto original_dsts = dsts; + if(m_light_wallet) { // Populate m_transfers light_wallet_get_unspent_outs(); @@ -9143,6 +9205,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp { const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof); try_tx = dsts.empty() || (estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit)); + THROW_WALLET_EXCEPTION_IF(try_tx && tx.dsts.empty(), error::tx_too_big, estimated_rct_tx_weight, upper_transaction_weight_limit); } } @@ -9317,10 +9380,77 @@ skip_tx: ptx_vector.push_back(tx.ptx); } + THROW_WALLET_EXCEPTION_IF(!sanity_check(ptx_vector, original_dsts), error::wallet_internal_error, "Created transaction(s) failed sanity check"); + // if we made it this far, we're OK to actually send the transactions return ptx_vector; } +bool wallet2::sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, std::vector<cryptonote::tx_destination_entry> dsts) const +{ + MDEBUG("sanity_check: " << ptx_vector.size() << " txes, " << dsts.size() << " destinations"); + + hw::device &hwdev = m_account.get_device(); + + THROW_WALLET_EXCEPTION_IF(ptx_vector.empty(), error::wallet_internal_error, "No transactions"); + + // check every party in there does receive at least the required amount + std::unordered_map<account_public_address, std::pair<uint64_t, bool>> required; + for (const auto &d: dsts) + { + required[d.addr].first += d.amount; + required[d.addr].second = d.is_subaddress; + } + + // add change + uint64_t change = 0; + for (const auto &ptx: ptx_vector) + { + for (size_t idx: ptx.selected_transfers) + change += m_transfers[idx].amount(); + change -= ptx.fee; + } + for (const auto &r: required) + change -= r.second.first; + MDEBUG("Adding " << cryptonote::print_money(change) << " expected change"); + + for (const pending_tx &ptx: ptx_vector) + THROW_WALLET_EXCEPTION_IF(ptx.change_dts.addr != ptx_vector[0].change_dts.addr, error::wallet_internal_error, + "Change goes to several different addresses"); + const auto it = m_subaddresses.find(ptx_vector[0].change_dts.addr.m_spend_public_key); + THROW_WALLET_EXCEPTION_IF(it == m_subaddresses.end(), error::wallet_internal_error, "Change address is not ours"); + + required[ptx_vector[0].change_dts.addr].first += change; + required[ptx_vector[0].change_dts.addr].second = ptx_vector[0].change_dts.is_subaddress; + + for (const auto &r: required) + { + const account_public_address &address = r.first; + const crypto::public_key &view_pkey = address.m_view_public_key; + + uint64_t total_received = 0; + for (const auto &ptx: ptx_vector) + { + uint64_t received = 0; + try + { + std::string proof = get_tx_proof(ptx.tx, ptx.tx_key, ptx.additional_tx_keys, address, r.second.second, "automatic-sanity-check"); + check_tx_proof(ptx.tx, address, r.second.second, "automatic-sanity-check", proof, received); + } + catch (const std::exception &e) { received = 0; } + total_received += received; + } + + std::stringstream ss; + ss << "Total received by " << cryptonote::get_account_address_as_str(m_nettype, r.second.second, address) << ": " + << cryptonote::print_money(total_received) << ", expected " << cryptonote::print_money(r.second.first); + MDEBUG(ss.str()); + THROW_WALLET_EXCEPTION_IF(total_received < r.second.first, error::wallet_internal_error, ss.str()); + } + + return true; +} + std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices) { std::vector<size_t> unused_transfers_indices; @@ -9603,6 +9733,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton ptx_vector.push_back(tx.ptx); } + uint64_t a = 0; + for (size_t idx: unused_transfers_indices) a += m_transfers[idx].amount(); + for (size_t idx: unused_dust_indices) a += m_transfers[idx].amount(); + std::vector<cryptonote::tx_destination_entry> synthetic_dsts(1, cryptonote::tx_destination_entry("", a, address, is_subaddress)); + THROW_WALLET_EXCEPTION_IF(!sanity_check(ptx_vector, synthetic_dsts), error::wallet_internal_error, "Created transaction(s) failed sanity check"); + // if we made it this far, we're OK to actually send the transactions return ptx_vector; } @@ -9638,6 +9774,7 @@ void wallet2::cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_ hw::wallet_shim wallet_shim; setup_shim(&wallet_shim, this); aux_data.tx_recipients = dsts_info; + aux_data.bp_version = use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1; dev_cold->tx_sign(&wallet_shim, txs, exported_txs, aux_data); tx_device_aux = aux_data.tx_device_aux; @@ -9854,7 +9991,7 @@ void wallet2::discard_unmixable_outputs() } } -bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const +bool wallet2::get_tx_key_cached(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const { additional_tx_keys.clear(); const std::unordered_map<crypto::hash, crypto::secret_key>::const_iterator i = m_tx_keys.find(txid); @@ -9867,6 +10004,82 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, s return true; } //---------------------------------------------------------------------------------------------------- +bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) +{ + bool r = get_tx_key_cached(txid, tx_key, additional_tx_keys); + if (r) + { + return true; + } + + auto & hwdev = get_account().get_device(); + + // So far only Cold protocol devices are supported. + if (hwdev.device_protocol() != hw::device::PROTOCOL_COLD) + { + return false; + } + + const auto tx_data_it = m_tx_device.find(txid); + if (tx_data_it == m_tx_device.end()) + { + MDEBUG("Aux data not found for txid: " << txid); + return false; + } + + auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev); + CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface"); + if (!dev_cold->is_get_tx_key_supported()) + { + MDEBUG("get_tx_key not supported by the device"); + return false; + } + + hw::device_cold::tx_key_data_t tx_key_data; + dev_cold->load_tx_key_data(tx_key_data, tx_data_it->second); + + // Load missing tx prefix hash + if (tx_key_data.tx_prefix_hash.empty()) + { + COMMAND_RPC_GET_TRANSACTIONS::request req; + COMMAND_RPC_GET_TRANSACTIONS::response res; + req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid)); + req.decode_as_json = false; + req.prune = true; + m_daemon_rpc_mutex.lock(); + bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client); + m_daemon_rpc_mutex.unlock(); + THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1), + error::wallet_internal_error, "Failed to get transaction from daemon"); + + cryptonote::transaction tx; + crypto::hash tx_hash{}; + cryptonote::blobdata tx_data; + crypto::hash tx_prefix_hash{}; + ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data); + THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon"); + THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx, tx_hash, tx_prefix_hash), + error::wallet_internal_error, "Failed to validate transaction from daemon"); + THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, + "Failed to get the right transaction from daemon"); + + tx_key_data.tx_prefix_hash = std::string(tx_prefix_hash.data, 32); + } + + std::vector<crypto::secret_key> tx_keys; + dev_cold->get_tx_key(tx_keys, tx_key_data, m_account.get_keys().m_view_secret_key); + if (tx_keys.empty()) + { + return false; + } + + tx_key = tx_keys[0]; + tx_keys.erase(tx_keys.begin()); + additional_tx_keys = tx_keys; + + return true; +} +//---------------------------------------------------------------------------------------------------- void wallet2::set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys) { // fetch tx from daemon and check if secret keys agree with corresponding public keys @@ -10158,41 +10371,8 @@ void wallet2::check_tx_key(const crypto::hash &txid, const crypto::secret_key &t check_tx_key_helper(txid, derivation, additional_derivations, address, received, in_pool, confirmations); } -void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations) +void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received) const { - COMMAND_RPC_GET_TRANSACTIONS::request req; - COMMAND_RPC_GET_TRANSACTIONS::response res; - req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid)); - req.decode_as_json = false; - req.prune = true; - m_daemon_rpc_mutex.lock(); - bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1), - error::wallet_internal_error, "Failed to get transaction from daemon"); - - cryptonote::transaction tx; - crypto::hash tx_hash; - if (res.txs.size() == 1) - { - ok = get_pruned_tx(res.txs.front(), tx, tx_hash); - THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon"); - } - else - { - cryptonote::blobdata tx_data; - ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data); - THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon"); - THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx), - error::wallet_internal_error, "Failed to validate transaction from daemon"); - tx_hash = cryptonote::get_transaction_hash(tx); - } - - THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, - "Failed to get the right transaction from daemon"); - THROW_WALLET_EXCEPTION_IF(!additional_derivations.empty() && additional_derivations.size() != tx.vout.size(), error::wallet_internal_error, - "The size of additional derivations is wrong"); - received = 0; hw::device &hwdev = m_account.get_device(); for (size_t n = 0; n < tx.vout.size(); ++n) @@ -10240,6 +10420,44 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de received += amount; } } +} + +void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations) +{ + COMMAND_RPC_GET_TRANSACTIONS::request req; + COMMAND_RPC_GET_TRANSACTIONS::response res; + req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid)); + req.decode_as_json = false; + req.prune = true; + m_daemon_rpc_mutex.lock(); + bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client); + m_daemon_rpc_mutex.unlock(); + THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1), + error::wallet_internal_error, "Failed to get transaction from daemon"); + + cryptonote::transaction tx; + crypto::hash tx_hash; + if (res.txs.size() == 1) + { + ok = get_pruned_tx(res.txs.front(), tx, tx_hash); + THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon"); + } + else + { + cryptonote::blobdata tx_data; + ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data); + THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon"); + THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx), + error::wallet_internal_error, "Failed to validate transaction from daemon"); + tx_hash = cryptonote::get_transaction_hash(tx); + } + + THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, + "Failed to get the right transaction from daemon"); + THROW_WALLET_EXCEPTION_IF(!additional_derivations.empty() && additional_derivations.size() != tx.vout.size(), error::wallet_internal_error, + "The size of additional derivations is wrong"); + + check_tx_key_helper(tx, derivation, additional_derivations, address, received); in_pool = res.txs.front().in_pool; confirmations = (uint64_t)-1; @@ -10254,9 +10472,55 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message) { + // fetch tx pubkey from the daemon + COMMAND_RPC_GET_TRANSACTIONS::request req; + COMMAND_RPC_GET_TRANSACTIONS::response res; + req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid)); + req.decode_as_json = false; + req.prune = true; + m_daemon_rpc_mutex.lock(); + bool ok = net_utils::invoke_http_json("/gettransactions", req, res, m_http_client); + m_daemon_rpc_mutex.unlock(); + THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1), + error::wallet_internal_error, "Failed to get transaction from daemon"); + + cryptonote::transaction tx; + crypto::hash tx_hash; + if (res.txs.size() == 1) + { + ok = get_pruned_tx(res.txs.front(), tx, tx_hash); + THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon"); + } + else + { + cryptonote::blobdata tx_data; + ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data); + THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon"); + THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx), + error::wallet_internal_error, "Failed to validate transaction from daemon"); + tx_hash = cryptonote::get_transaction_hash(tx); + } + + THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon"); + + // determine if the address is found in the subaddress hash table (i.e. whether the proof is outbound or inbound) + crypto::secret_key tx_key = crypto::null_skey; + std::vector<crypto::secret_key> additional_tx_keys; + const bool is_out = m_subaddresses.count(address.m_spend_public_key) == 0; + if (is_out) + { + THROW_WALLET_EXCEPTION_IF(!get_tx_key(txid, tx_key, additional_tx_keys), error::wallet_internal_error, "Tx secret key wasn't found in the wallet file."); + } + + return get_tx_proof(tx, tx_key, additional_tx_keys, address, is_subaddress, message); +} + +std::string wallet2::get_tx_proof(const cryptonote::transaction &tx, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message) const +{ // determine if the address is found in the subaddress hash table (i.e. whether the proof is outbound or inbound) const bool is_out = m_subaddresses.count(address.m_spend_public_key) == 0; + const crypto::hash txid = cryptonote::get_transaction_hash(tx); std::string prefix_data((const char*)&txid, sizeof(crypto::hash)); prefix_data += message; crypto::hash prefix_hash; @@ -10267,10 +10531,6 @@ std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::ac std::string sig_str; if (is_out) { - crypto::secret_key tx_key; - std::vector<crypto::secret_key> additional_tx_keys; - THROW_WALLET_EXCEPTION_IF(!get_tx_key(txid, tx_key, additional_tx_keys), error::wallet_internal_error, "Tx secret key wasn't found in the wallet file."); - const size_t num_sigs = 1 + additional_tx_keys.size(); shared_secret.resize(num_sigs); sig.resize(num_sigs); @@ -10305,37 +10565,6 @@ std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::ac } else { - // fetch tx pubkey from the daemon - COMMAND_RPC_GET_TRANSACTIONS::request req; - COMMAND_RPC_GET_TRANSACTIONS::response res; - req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid)); - req.decode_as_json = false; - req.prune = true; - m_daemon_rpc_mutex.lock(); - bool ok = net_utils::invoke_http_json("/gettransactions", req, res, m_http_client); - m_daemon_rpc_mutex.unlock(); - THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1), - error::wallet_internal_error, "Failed to get transaction from daemon"); - - cryptonote::transaction tx; - crypto::hash tx_hash; - if (res.txs.size() == 1) - { - ok = get_pruned_tx(res.txs.front(), tx, tx_hash); - THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon"); - } - else - { - cryptonote::blobdata tx_data; - ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data); - THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon"); - THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx), - error::wallet_internal_error, "Failed to validate transaction from daemon"); - tx_hash = cryptonote::get_transaction_hash(tx); - } - - THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon"); - crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); THROW_WALLET_EXCEPTION_IF(tx_pub_key == null_pkey, error::wallet_internal_error, "Tx pubkey was not found"); @@ -10377,9 +10606,7 @@ std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::ac for (size_t i = 1; i < num_sigs; ++i) THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(shared_secret[i], rct::rct2sk(rct::I), additional_derivations[i - 1]), error::wallet_internal_error, "Failed to generate key derivation"); uint64_t received; - bool in_pool; - uint64_t confirmations; - check_tx_key_helper(txid, derivation, additional_derivations, address, received, in_pool, confirmations); + check_tx_key_helper(tx, derivation, additional_derivations, address, received); THROW_WALLET_EXCEPTION_IF(!received, error::wallet_internal_error, tr("No funds received in this tx.")); // concatenate all signature strings @@ -10392,37 +10619,6 @@ std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::ac bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message, const std::string &sig_str, uint64_t &received, bool &in_pool, uint64_t &confirmations) { - const bool is_out = sig_str.substr(0, 3) == "Out"; - const std::string header = is_out ? "OutProofV1" : "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"); - - // decode base58 - std::vector<crypto::public_key> shared_secret(1); - std::vector<crypto::signature> sig(1); - const size_t pk_len = tools::base58::encode(std::string((const char *)&shared_secret[0], sizeof(crypto::public_key))).size(); - const size_t sig_len = tools::base58::encode(std::string((const char *)&sig[0], sizeof(crypto::signature))).size(); - const size_t num_sigs = (sig_str.size() - header_len) / (pk_len + sig_len); - THROW_WALLET_EXCEPTION_IF(sig_str.size() != header_len + num_sigs * (pk_len + sig_len), error::wallet_internal_error, - "Wrong signature size"); - shared_secret.resize(num_sigs); - sig.resize(num_sigs); - for (size_t i = 0; i < num_sigs; ++i) - { - std::string pk_decoded; - std::string sig_decoded; - const size_t offset = header_len + i * (pk_len + sig_len); - THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(offset, pk_len), pk_decoded), error::wallet_internal_error, - "Signature decoding error"); - THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(offset + pk_len, sig_len), sig_decoded), error::wallet_internal_error, - "Signature decoding error"); - THROW_WALLET_EXCEPTION_IF(sizeof(crypto::public_key) != pk_decoded.size() || sizeof(crypto::signature) != sig_decoded.size(), error::wallet_internal_error, - "Signature decoding error"); - memcpy(&shared_secret[i], pk_decoded.data(), sizeof(crypto::public_key)); - memcpy(&sig[i], sig_decoded.data(), sizeof(crypto::signature)); - } - // fetch tx pubkey from the daemon COMMAND_RPC_GET_TRANSACTIONS::request req; COMMAND_RPC_GET_TRANSACTIONS::response res; @@ -10454,12 +10650,62 @@ bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon"); + if (!check_tx_proof(tx, address, is_subaddress, message, sig_str, received)) + return false; + + in_pool = res.txs.front().in_pool; + confirmations = (uint64_t)-1; + if (!in_pool) + { + std::string err; + uint64_t bc_height = get_daemon_blockchain_height(err); + if (err.empty()) + confirmations = bc_height - (res.txs.front().block_height + 1); + } + + return true; +} + +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 +{ + const bool is_out = sig_str.substr(0, 3) == "Out"; + const std::string header = is_out ? "OutProofV1" : "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"); + + // decode base58 + std::vector<crypto::public_key> shared_secret(1); + std::vector<crypto::signature> sig(1); + const size_t pk_len = tools::base58::encode(std::string((const char *)&shared_secret[0], sizeof(crypto::public_key))).size(); + const size_t sig_len = tools::base58::encode(std::string((const char *)&sig[0], sizeof(crypto::signature))).size(); + const size_t num_sigs = (sig_str.size() - header_len) / (pk_len + sig_len); + THROW_WALLET_EXCEPTION_IF(sig_str.size() != header_len + num_sigs * (pk_len + sig_len), error::wallet_internal_error, + "Wrong signature size"); + shared_secret.resize(num_sigs); + sig.resize(num_sigs); + for (size_t i = 0; i < num_sigs; ++i) + { + std::string pk_decoded; + std::string sig_decoded; + const size_t offset = header_len + i * (pk_len + sig_len); + THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(offset, pk_len), pk_decoded), error::wallet_internal_error, + "Signature decoding error"); + THROW_WALLET_EXCEPTION_IF(!tools::base58::decode(sig_str.substr(offset + pk_len, sig_len), sig_decoded), error::wallet_internal_error, + "Signature decoding error"); + THROW_WALLET_EXCEPTION_IF(sizeof(crypto::public_key) != pk_decoded.size() || sizeof(crypto::signature) != sig_decoded.size(), error::wallet_internal_error, + "Signature decoding error"); + memcpy(&shared_secret[i], pk_decoded.data(), sizeof(crypto::public_key)); + memcpy(&sig[i], sig_decoded.data(), sizeof(crypto::signature)); + } + crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); THROW_WALLET_EXCEPTION_IF(tx_pub_key == null_pkey, error::wallet_internal_error, "Tx pubkey was not found"); std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx); THROW_WALLET_EXCEPTION_IF(additional_tx_pub_keys.size() + 1 != num_sigs, error::wallet_internal_error, "Signature size mismatch with additional tx pubkeys"); + const crypto::hash txid = cryptonote::get_transaction_hash(tx); std::string prefix_data((const char*)&txid, sizeof(crypto::hash)); prefix_data += message; crypto::hash prefix_hash; @@ -10506,7 +10752,7 @@ bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account if (good_signature[i]) THROW_WALLET_EXCEPTION_IF(!crypto::generate_key_derivation(shared_secret[i], rct::rct2sk(rct::I), additional_derivations[i - 1]), error::wallet_internal_error, "Failed to generate key derivation"); - check_tx_key_helper(txid, derivation, additional_derivations, address, received, in_pool, confirmations); + check_tx_key_helper(tx, derivation, additional_derivations, address, received); return true; } return false; @@ -11008,15 +11254,6 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle size_t pk_index = 0; hw::device &hwdev = m_account.get_device(); - const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx); - std::vector<crypto::key_derivation> additional_derivations; - for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i) - { - additional_derivations.push_back({}); - bool r = hwdev.generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back()); - THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation"); - } - while (find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index++)) { const crypto::public_key tx_pub_key = pub_key_field.pub_key; crypto::key_derivation derivation; @@ -11026,16 +11263,15 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle for (size_t i = 0; i < td.m_tx.vout.size(); ++i) { tx_scan_info_t tx_scan_info; - check_acc_out_precomp(td.m_tx.vout[i], derivation, additional_derivations, i, tx_scan_info); + check_acc_out_precomp(td.m_tx.vout[i], derivation, {}, i, tx_scan_info); if (!tx_scan_info.error && tx_scan_info.received) return tx_pub_key; } } - // we found no key yielding an output - THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, - "Public key yielding at least one output wasn't found in the transaction extra"); - return crypto::null_pkey; + // we found no key yielding an output, but it might be in the additional + // tx pub keys only, which we do not need to check, so return the first one + return tx_pub_key; } bool wallet2::export_key_images(const std::string &filename) const @@ -11277,6 +11513,17 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag } PERF_TIMER_STOP(import_key_images_C); + // accumulate outputs before the updated data + for(size_t i = 0; i < offset; ++i) + { + const transfer_details &td = m_transfers[i]; + uint64_t amount = td.amount(); + if (td.m_spent) + spent += amount; + else + unspent += amount; + } + PERF_TIMER_START(import_key_images_D); for(size_t i = 0; i < signed_key_images.size(); ++i) { @@ -11386,6 +11633,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag auto it = m_key_images.find(boost::get<cryptonote::txin_to_key>(in).k_image); if (it != m_key_images.end()) { + THROW_WALLET_EXCEPTION_IF(it->second >= m_transfers.size(), error::wallet_internal_error, std::string("Key images cache contains illegal transfer offset: ") + std::to_string(it->second) + std::string(" m_transfers.size() = ") + std::to_string(m_transfers.size())); const transfer_details& td = m_transfers[it->second]; uint64_t amount = boost::get<cryptonote::txin_to_key>(in).amount; if (amount > 0) @@ -11439,32 +11687,52 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag PERF_TIMER_STOP(import_key_images_G); } - return m_transfers[signed_key_images.size() - 1].m_block_height; + // this can be 0 if we do not know the height + return m_transfers[signed_key_images.size() + offset - 1].m_block_height; } -bool wallet2::import_key_images(std::vector<crypto::key_image> key_images) +bool wallet2::import_key_images(std::vector<crypto::key_image> key_images, size_t offset, boost::optional<std::unordered_set<size_t>> selected_transfers) { - if (key_images.size() > m_transfers.size()) + if (key_images.size() + offset > m_transfers.size()) { LOG_PRINT_L1("More key images returned that we know outputs for"); return false; } - for (size_t i = 0; i < key_images.size(); ++i) + for (size_t ki_idx = 0; ki_idx < key_images.size(); ++ki_idx) { - transfer_details &td = m_transfers[i]; - if (td.m_key_image_known && !td.m_key_image_partial && td.m_key_image != key_images[i]) - LOG_PRINT_L0("WARNING: imported key image differs from previously known key image at index " << i << ": trusting imported one"); - td.m_key_image = key_images[i]; - m_key_images[m_transfers[i].m_key_image] = i; + const size_t transfer_idx = ki_idx + offset; + if (selected_transfers && selected_transfers.get().find(transfer_idx) == selected_transfers.get().end()) + continue; + + transfer_details &td = m_transfers[transfer_idx]; + if (td.m_key_image_known && !td.m_key_image_partial && td.m_key_image != key_images[ki_idx]) + LOG_PRINT_L0("WARNING: imported key image differs from previously known key image at index " << ki_idx << ": trusting imported one"); + td.m_key_image = key_images[ki_idx]; + m_key_images[td.m_key_image] = transfer_idx; td.m_key_image_known = true; td.m_key_image_request = false; td.m_key_image_partial = false; - m_pub_keys[m_transfers[i].get_public_key()] = i; + m_pub_keys[td.get_public_key()] = transfer_idx; } return true; } +bool wallet2::import_key_images(signed_tx_set & signed_tx, size_t offset, bool only_selected_transfers) +{ + std::unordered_set<size_t> selected_transfers; + if (only_selected_transfers) + { + for (const pending_tx & ptx : signed_tx.ptx) + { + for (const size_t s: ptx.selected_transfers) + selected_transfers.insert(s); + } + } + + return import_key_images(signed_tx.key_images, offset, only_selected_transfers ? boost::make_optional(selected_transfers) : boost::none); +} + wallet2::payment_container wallet2::export_payments() const { payment_container payments; @@ -12445,22 +12713,30 @@ wallet_device_callback * wallet2::get_device_callback() } return m_device_callback.get(); }//---------------------------------------------------------------------------------------------------- -void wallet2::on_button_request() +void wallet2::on_device_button_request(uint64_t code) { - if (0 != m_callback) - m_callback->on_button_request(); + if (nullptr != m_callback) + m_callback->on_device_button_request(code); } //---------------------------------------------------------------------------------------------------- -void wallet2::on_pin_request(epee::wipeable_string & pin) +boost::optional<epee::wipeable_string> wallet2::on_device_pin_request() { - if (0 != m_callback) - m_callback->on_pin_request(pin); + if (nullptr != m_callback) + return m_callback->on_device_pin_request(); + return boost::none; } //---------------------------------------------------------------------------------------------------- -void wallet2::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) +boost::optional<epee::wipeable_string> wallet2::on_device_passphrase_request(bool on_device) { - if (0 != m_callback) - m_callback->on_passphrase_request(on_device, passphrase); + if (nullptr != m_callback) + return m_callback->on_device_passphrase_request(on_device); + return boost::none; +} +//---------------------------------------------------------------------------------------------------- +void wallet2::on_device_progress(const hw::device_progress& event) +{ + if (nullptr != m_callback) + m_callback->on_device_progress(event); } //---------------------------------------------------------------------------------------------------- std::string wallet2::get_rpc_status(const std::string &s) const @@ -12484,5 +12760,61 @@ void wallet2::throw_on_rpc_response_error(const boost::optional<std::string> &st THROW_WALLET_EXCEPTION_IF(*status == CORE_RPC_STATUS_BUSY, tools::error::daemon_busy, method); THROW_WALLET_EXCEPTION_IF(*status != CORE_RPC_STATUS_OK, tools::error::wallet_generic_rpc_error, method, m_trusted_daemon ? *status : "daemon error"); } +//---------------------------------------------------------------------------------------------------- +void wallet2::hash_m_transfer(const transfer_details & transfer, crypto::hash &hash) const +{ + KECCAK_CTX state; + keccak_init(&state); + keccak_update(&state, (const uint8_t *) transfer.m_txid.data, sizeof(transfer.m_txid.data)); + keccak_update(&state, (const uint8_t *) transfer.m_internal_output_index, sizeof(transfer.m_internal_output_index)); + keccak_update(&state, (const uint8_t *) transfer.m_global_output_index, sizeof(transfer.m_global_output_index)); + keccak_update(&state, (const uint8_t *) transfer.m_amount, sizeof(transfer.m_amount)); + keccak_finish(&state, (uint8_t *) hash.data); +} +//---------------------------------------------------------------------------------------------------- +uint64_t wallet2::hash_m_transfers(int64_t transfer_height, crypto::hash &hash) const +{ + CHECK_AND_ASSERT_THROW_MES(transfer_height > (int64_t)m_transfers.size(), "Hash height is greater than number of transfers"); + + KECCAK_CTX state; + crypto::hash tmp_hash{}; + uint64_t current_height = 0; + + keccak_init(&state); + for(const transfer_details & transfer : m_transfers){ + if (transfer_height >= 0 && current_height >= (uint64_t)transfer_height){ + break; + } + hash_m_transfer(transfer, tmp_hash); + keccak_update(&state, (const uint8_t *) transfer.m_block_height, sizeof(transfer.m_block_height)); + keccak_update(&state, (const uint8_t *) tmp_hash.data, sizeof(tmp_hash.data)); + current_height += 1; + } + + keccak_finish(&state, (uint8_t *) hash.data); + return current_height; +} +//---------------------------------------------------------------------------------------------------- +void wallet2::finish_rescan_bc_keep_key_images(uint64_t transfer_height, const crypto::hash &hash) +{ + // Compute hash of m_transfers, if differs there had to be BC reorg. + crypto::hash new_transfers_hash{}; + hash_m_transfers((int64_t) transfer_height, new_transfers_hash); + + if (new_transfers_hash != hash) + { + // Soft-Reset to avoid inconsistency in case of BC reorg. + clear_soft(false); // keep_key_images works only with soft reset. + THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, "Transfers changed during rescan, soft or hard rescan is needed"); + } + + // Restore key images in m_transfers from m_key_images + for(auto it = m_key_images.begin(); it != m_key_images.end(); it++) + { + THROW_WALLET_EXCEPTION_IF(it->second >= m_transfers.size(), error::wallet_internal_error, "Key images cache contains illegal transfer offset"); + m_transfers[it->second].m_key_image = it->first; + m_transfers[it->second].m_key_image_known = true; + } +} } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index b7ebe03d9..c25450fb3 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -67,6 +67,7 @@ #define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2" class Serialization_portability_wallet_Test; +class wallet_accessor_test; namespace tools { @@ -102,9 +103,10 @@ namespace tools virtual void on_lw_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {} virtual void on_lw_money_spent(uint64_t height, const crypto::hash &txid, uint64_t amount) {} // Device callbacks - virtual void on_button_request() {} - virtual void on_pin_request(epee::wipeable_string & pin) {} - virtual void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) {} + virtual void on_device_button_request(uint64_t code) {} + virtual boost::optional<epee::wipeable_string> on_device_pin_request() { return boost::none; } + virtual boost::optional<epee::wipeable_string> on_device_passphrase_request(bool on_device) { return boost::none; } + virtual void on_device_progress(const hw::device_progress& event) {}; // Common callbacks virtual void on_pool_tx_removed(const crypto::hash &txid) {} virtual ~i_wallet2_callback() {} @@ -114,9 +116,10 @@ namespace tools { public: wallet_device_callback(wallet2 * wallet): wallet(wallet) {}; - void on_button_request() override; - void on_pin_request(epee::wipeable_string & pin) override; - void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) override; + void on_button_request(uint64_t code=0) override; + boost::optional<epee::wipeable_string> on_pin_request() override; + boost::optional<epee::wipeable_string> on_passphrase_request(bool on_device) override; + void on_progress(const hw::device_progress& event) override; private: wallet2 * wallet; }; @@ -171,6 +174,7 @@ namespace tools class wallet2 { friend class ::Serialization_portability_wallet_Test; + friend class ::wallet_accessor_test; friend class wallet_keys_unlocker; friend class wallet_device_callback; public: @@ -795,9 +799,11 @@ namespace tools std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices); std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra); std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra); + bool sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, std::vector<cryptonote::tx_destination_entry> dsts) const; void cold_tx_aux_import(const std::vector<pending_tx>& ptx, const std::vector<std::string>& tx_device_aux); void cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::vector<std::string> & tx_device_aux); uint64_t cold_key_image_sync(uint64_t &spent, uint64_t &unspent); + bool parse_multisig_tx_from_str(std::string multisig_tx_st, multisig_tx_set &exported_txs) const; bool load_multisig_tx(cryptonote::blobdata blob, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL); bool load_multisig_tx_from_file(const std::string &filename, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL); bool sign_multisig_tx_from_file(const std::string &filename, std::vector<crypto::hash> &txids, std::function<bool(const multisig_tx_set&)> accept_func); @@ -816,7 +822,7 @@ namespace tools uint64_t get_blockchain_current_height() const { return m_light_wallet_blockchain_height ? m_light_wallet_blockchain_height : m_blockchain.size(); } void rescan_spent(); - void rescan_blockchain(bool hard, bool refresh = true); + void rescan_blockchain(bool hard, bool refresh = true, bool keep_key_images = false); bool is_transfer_unlocked(const transfer_details& td) const; bool is_transfer_unlocked(uint64_t unlock_time, uint64_t block_height) const; @@ -1002,12 +1008,16 @@ namespace tools const std::string & device_derivation_path() const { return m_device_derivation_path; } void device_derivation_path(const std::string &device_derivation_path) { m_device_derivation_path = device_derivation_path; } - bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const; + bool get_tx_key_cached(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const; void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys); + bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys); void check_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations); void check_tx_key_helper(const crypto::hash &txid, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations); + void check_tx_key_helper(const cryptonote::transaction &tx, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received) const; std::string get_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message); + std::string get_tx_proof(const cryptonote::transaction &tx, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message) const; bool check_tx_proof(const crypto::hash &txid, const cryptonote::account_public_address &address, bool is_subaddress, const std::string &message, const std::string &sig_str, uint64_t &received, bool &in_pool, uint64_t &confirmations); + bool 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; std::string get_spend_proof(const crypto::hash &txid, const std::string &message); bool check_spend_proof(const crypto::hash &txid, const std::string &message, const std::string &sig_str); @@ -1125,7 +1135,8 @@ namespace tools std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> export_key_images(bool all = false) const; uint64_t import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, size_t offset, uint64_t &spent, uint64_t &unspent, bool check_spent = true); uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent); - bool import_key_images(std::vector<crypto::key_image> key_images); + bool import_key_images(std::vector<crypto::key_image> key_images, size_t offset=0, boost::optional<std::unordered_set<size_t>> selected_transfers=boost::none); + bool import_key_images(signed_tx_set & signed_tx, size_t offset=0, bool only_selected_transfers=false); crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; void update_pool_state(bool refreshed = false); @@ -1246,6 +1257,9 @@ namespace tools void set_tx_notify(const std::shared_ptr<tools::Notify> ¬ify) { m_tx_notify = notify; } bool is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height) const; + void hash_m_transfer(const transfer_details & transfer, crypto::hash &hash) const; + uint64_t hash_m_transfers(int64_t transfer_height, crypto::hash &hash) const; + void finish_rescan_bc_keep_key_images(uint64_t transfer_height, const crypto::hash &hash); private: /*! @@ -1267,6 +1281,7 @@ namespace tools void detach_blockchain(uint64_t height); void get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity = 1) const; bool clear(); + void clear_soft(bool keep_key_images=false); void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices); void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<crypto::hash> &hashes); void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, bool force = false); @@ -1331,9 +1346,10 @@ namespace tools void create_keys_file(const std::string &wallet_, bool watch_only, const epee::wipeable_string &password, bool create_address_file); wallet_device_callback * get_device_callback(); - void on_button_request(); - void on_pin_request(epee::wipeable_string & pin); - void on_passphrase_request(bool on_device, epee::wipeable_string & passphrase); + void on_device_button_request(uint64_t code); + boost::optional<epee::wipeable_string> on_device_pin_request(); + boost::optional<epee::wipeable_string> on_device_passphrase_request(bool on_device); + void on_device_progress(const hw::device_progress& event); std::string get_rpc_status(const std::string &s) const; void throw_on_rpc_response_error(const boost::optional<std::string> &status, const char *method) const; diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp index b9d0a6a75..a4bb342ca 100644 --- a/src/wallet/wallet_args.cpp +++ b/src/wallet/wallet_args.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/wallet/wallet_args.h b/src/wallet/wallet_args.h index a1f251144..c861dca11 100644 --- a/src/wallet/wallet_args.h +++ b/src/wallet/wallet_args.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 35862bda1..6ebaaa395 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -699,26 +699,43 @@ namespace tools explicit tx_too_big(std::string&& loc, const cryptonote::transaction& tx, uint64_t tx_weight_limit) : transfer_error(std::move(loc), "transaction is too big") , m_tx(tx) + , m_tx_valid(true) + , m_tx_weight(cryptonote::get_transaction_weight(tx)) , m_tx_weight_limit(tx_weight_limit) { } + explicit tx_too_big(std::string&& loc, uint64_t tx_weight, uint64_t tx_weight_limit) + : transfer_error(std::move(loc), "transaction would be too big") + , m_tx_valid(false) + , m_tx_weight(tx_weight) + , m_tx_weight_limit(tx_weight_limit) + { + } + + bool tx_valid() const { return m_tx_valid; } const cryptonote::transaction& tx() const { return m_tx; } + uint64_t tx_weight() const { return m_tx_weight; } uint64_t tx_weight_limit() const { return m_tx_weight_limit; } std::string to_string() const { std::ostringstream ss; - cryptonote::transaction tx = m_tx; ss << transfer_error::to_string() << ", tx_weight_limit = " << m_tx_weight_limit << - ", tx weight = " << get_transaction_weight(m_tx) << - ", tx:\n" << cryptonote::obj_to_json_str(tx); + ", tx weight = " << m_tx_weight; + if (m_tx_valid) + { + cryptonote::transaction tx = m_tx; + ss << ", tx:\n" << cryptonote::obj_to_json_str(tx); + } return ss.str(); } private: cryptonote::transaction m_tx; + bool m_tx_valid; + uint64_t m_tx_weight; uint64_t m_tx_weight_limit; }; //---------------------------------------------------------------------------------------------------- diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 90afa91fd..7040597df 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -1074,29 +1074,59 @@ namespace tools er.message = "command not supported by watch-only wallet"; return false; } - - tools::wallet2::unsigned_tx_set exported_txs; - try + if(req.unsigned_txset.empty() && req.multisig_txset.empty()) { - cryptonote::blobdata blob; - if (!epee::string_tools::parse_hexstr_to_binbuff(req.unsigned_txset, blob)) - { - er.code = WALLET_RPC_ERROR_CODE_BAD_HEX; - er.message = "Failed to parse hex."; - return false; + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "no txset provided"; + return false; + } + + std::vector <wallet2::tx_construction_data> tx_constructions; + if (!req.unsigned_txset.empty()) { + try { + tools::wallet2::unsigned_tx_set exported_txs; + cryptonote::blobdata blob; + if (!epee::string_tools::parse_hexstr_to_binbuff(req.unsigned_txset, blob)) { + er.code = WALLET_RPC_ERROR_CODE_BAD_HEX; + er.message = "Failed to parse hex."; + return false; + } + if (!m_wallet->parse_unsigned_tx_from_str(blob, exported_txs)) { + er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA; + er.message = "cannot load unsigned_txset"; + return false; + } + tx_constructions = exported_txs.txes; } - if(!m_wallet->parse_unsigned_tx_from_str(blob, exported_txs)) - { + catch (const std::exception &e) { er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA; - er.message = "cannot load unsigned_txset"; + er.message = "failed to parse unsigned transfers: " + std::string(e.what()); + return false; + } + } else if (!req.multisig_txset.empty()) { + try { + tools::wallet2::multisig_tx_set exported_txs; + cryptonote::blobdata blob; + if (!epee::string_tools::parse_hexstr_to_binbuff(req.multisig_txset, blob)) { + er.code = WALLET_RPC_ERROR_CODE_BAD_HEX; + er.message = "Failed to parse hex."; + return false; + } + if (!m_wallet->parse_multisig_tx_from_str(blob, exported_txs)) { + er.code = WALLET_RPC_ERROR_CODE_BAD_MULTISIG_TX_DATA; + er.message = "cannot load multisig_txset"; + return false; + } + + for (size_t n = 0; n < exported_txs.m_ptx.size(); ++n) { + tx_constructions.push_back(exported_txs.m_ptx[n].construction_data); + } + } + catch (const std::exception &e) { + er.code = WALLET_RPC_ERROR_CODE_BAD_MULTISIG_TX_DATA; + er.message = "failed to parse multisig transfers: " + std::string(e.what()); return false; } - } - catch (const std::exception &e) - { - er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA; - er.message = "failed to parse unsigned transfers: " + std::string(e.what()); - return false; } std::vector<tools::wallet2::pending_tx> ptx; @@ -1105,9 +1135,9 @@ namespace tools // gather info to ask the user std::unordered_map<cryptonote::account_public_address, std::pair<std::string, uint64_t>> dests; int first_known_non_zero_change_index = -1; - for (size_t n = 0; n < exported_txs.txes.size(); ++n) + for (size_t n = 0; n < tx_constructions.size(); ++n) { - const tools::wallet2::tx_construction_data &cd = exported_txs.txes[n]; + const tools::wallet2::tx_construction_data &cd = tx_constructions[n]; res.desc.push_back({0, 0, std::numeric_limits<uint32_t>::max(), 0, {}, "", 0, "", 0, 0, ""}); wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::transfer_description &desc = res.desc.back(); @@ -1171,7 +1201,7 @@ namespace tools { if (first_known_non_zero_change_index == -1) first_known_non_zero_change_index = n; - const tools::wallet2::tx_construction_data &cdn = exported_txs.txes[first_known_non_zero_change_index]; + const tools::wallet2::tx_construction_data &cdn = tx_constructions[first_known_non_zero_change_index]; if (memcmp(&cd.change_dts.addr, &cdn.change_dts.addr, sizeof(cd.change_dts.addr))) { er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA; @@ -1199,7 +1229,7 @@ namespace tools if (desc.change_amount > 0) { - const tools::wallet2::tx_construction_data &cd0 = exported_txs.txes[0]; + const tools::wallet2::tx_construction_data &cd0 = tx_constructions[0]; desc.change_address = get_account_address_as_str(m_wallet->nettype(), cd0.subaddr_account > 0, cd0.change_dts.addr); } @@ -3177,6 +3207,174 @@ namespace tools } } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_generate_from_keys(const wallet_rpc::COMMAND_RPC_GENERATE_FROM_KEYS::request &req, wallet_rpc::COMMAND_RPC_GENERATE_FROM_KEYS::response &res, epee::json_rpc::error &er, const connection_context *ctx) + { + if (m_wallet_dir.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_NO_WALLET_DIR; + er.message = "No wallet dir configured"; + return false; + } + + // early check for mandatory fields + if (req.filename.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "field 'filename' is mandatory. Please provide a filename to save the restored wallet to."; + return false; + } + if (req.viewkey.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "field 'viewkey' is mandatory. Please provide a view key you want to restore from."; + return false; + } + if (req.address.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "field 'address' is mandatory. Please provide a public address."; + return false; + } + + namespace po = boost::program_options; + po::variables_map vm2; + const char *ptr = strchr(req.filename.c_str(), '/'); + #ifdef _WIN32 + if (!ptr) + ptr = strchr(req.filename.c_str(), '\\'); + if (!ptr) + ptr = strchr(req.filename.c_str(), ':'); + #endif + if (ptr) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Invalid filename"; + return false; + } + std::string wallet_file = m_wallet_dir + "/" + req.filename; + // check if wallet file already exists + if (!wallet_file.empty()) + { + try + { + boost::system::error_code ignored_ec; + THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(wallet_file, ignored_ec), error::file_exists, wallet_file); + } + catch (const std::exception &e) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Wallet already exists."; + return false; + } + } + + { + po::options_description desc("dummy"); + const command_line::arg_descriptor<std::string, true> arg_password = {"password", "password"}; + const char *argv[4]; + int argc = 3; + argv[0] = "wallet-rpc"; + argv[1] = "--password"; + argv[2] = req.password.c_str(); + argv[3] = NULL; + vm2 = *m_vm; + command_line::add_arg(desc, arg_password); + po::store(po::parse_command_line(argc, argv, desc), vm2); + } + + auto rc = tools::wallet2::make_new(vm2, true, nullptr); + std::unique_ptr<wallet2> wal; + wal = std::move(rc.first); + if (!wal) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to create wallet"; + return false; + } + + cryptonote::address_parse_info info; + if(!get_account_address_from_str(info, wal->nettype(), req.address)) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to parse public address"; + return false; + } + + epee::wipeable_string password = rc.second.password(); + epee::wipeable_string viewkey_string = req.viewkey; + crypto::secret_key viewkey; + if (!viewkey_string.hex_to_pod(unwrap(unwrap(viewkey)))) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to parse view key secret key"; + return false; + } + + try + { + if (!req.spendkey.empty()) + { + epee::wipeable_string spendkey_string = req.spendkey; + crypto::secret_key spendkey; + if (!spendkey_string.hex_to_pod(unwrap(unwrap(spendkey)))) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to parse spend key secret key"; + return false; + } + wal->generate(wallet_file, std::move(rc.second).password(), info.address, spendkey, viewkey, false); + res.info = "Wallet has been generated successfully."; + } + else + { + wal->generate(wallet_file, std::move(rc.second).password(), info.address, viewkey, false); + res.info = "Watch-only wallet has been generated successfully."; + } + MINFO("Wallet has been generated.\n"); + } + catch (const std::exception &e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + + if (!wal) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "Failed to generate wallet"; + return false; + } + + // set blockheight if given + try + { + wal->set_refresh_from_block_height(req.restore_height); + wal->rewrite(wallet_file, password); + } + catch (const std::exception &e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + + if (m_wallet) + { + try + { + m_wallet->store(); + } + catch (const std::exception &e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + delete m_wallet; + } + m_wallet = wal.release(); + res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype()); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_restore_deterministic_wallet(const wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET::request &req, wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET::response &res, epee::json_rpc::error &er, const connection_context *ctx) { if (m_wallet_dir.empty()) @@ -4040,6 +4238,7 @@ int main(int argc, char** argv) { command_line::add_arg(desc_params, arg_rpc_ssl_private_key); command_line::add_arg(desc_params, arg_rpc_ssl_certificate); command_line::add_arg(desc_params, arg_rpc_ssl_allowed_certificates); + command_line::add_arg(desc_params, arg_rpc_ssl_allowed_fingerprints); daemonizer::init_options(hidden_options, desc_params); desc_params.add(hidden_options); diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 8157344c2..affaf10f7 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -137,6 +137,7 @@ namespace tools MAP_JON_RPC_WE("open_wallet", on_open_wallet, wallet_rpc::COMMAND_RPC_OPEN_WALLET) MAP_JON_RPC_WE("close_wallet", on_close_wallet, wallet_rpc::COMMAND_RPC_CLOSE_WALLET) MAP_JON_RPC_WE("change_wallet_password", on_change_wallet_password, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD) + MAP_JON_RPC_WE("generate_from_keys", on_generate_from_keys, wallet_rpc::COMMAND_RPC_GENERATE_FROM_KEYS) MAP_JON_RPC_WE("restore_deterministic_wallet", on_restore_deterministic_wallet, wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET) MAP_JON_RPC_WE("is_multisig", on_is_multisig, wallet_rpc::COMMAND_RPC_IS_MULTISIG) MAP_JON_RPC_WE("prepare_multisig", on_prepare_multisig, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG) @@ -217,6 +218,7 @@ namespace tools bool on_open_wallet(const wallet_rpc::COMMAND_RPC_OPEN_WALLET::request& req, wallet_rpc::COMMAND_RPC_OPEN_WALLET::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_close_wallet(const wallet_rpc::COMMAND_RPC_CLOSE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CLOSE_WALLET::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_change_wallet_password(const wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::request& req, wallet_rpc::COMMAND_RPC_CHANGE_WALLET_PASSWORD::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); + bool on_generate_from_keys(const wallet_rpc::COMMAND_RPC_GENERATE_FROM_KEYS::request& req, wallet_rpc::COMMAND_RPC_GENERATE_FROM_KEYS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_restore_deterministic_wallet(const wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET::request& req, wallet_rpc::COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_is_multisig(const wallet_rpc::COMMAND_RPC_IS_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IS_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_prepare_multisig(const wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 546e572bc..b0e8bed93 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -610,9 +610,11 @@ namespace wallet_rpc struct request_t { std::string unsigned_txset; + std::string multisig_txset; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(unsigned_txset) + KV_SERIALIZE(multisig_txset) END_KV_SERIALIZE_MAP() }; typedef epee::misc_utils::struct_init<request_t> request; @@ -2074,6 +2076,39 @@ namespace wallet_rpc typedef epee::misc_utils::struct_init<response_t> response; }; + struct COMMAND_RPC_GENERATE_FROM_KEYS + { + struct request + { + uint64_t restore_height; + std::string filename; + std::string address; + std::string spendkey; + std::string viewkey; + std::string password; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_OPT(restore_height, (uint64_t)0) + KV_SERIALIZE(filename) + KV_SERIALIZE(address) + KV_SERIALIZE(spendkey) + KV_SERIALIZE(viewkey) + KV_SERIALIZE(password) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string address; + std::string info; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(address) + KV_SERIALIZE(info) + END_KV_SERIALIZE_MAP() + }; + }; + struct COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET { struct request_t diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index 9b3a2847d..440a58a47 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 89d77cf32..bbb0bc051 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # @@ -92,6 +92,10 @@ if (BUILD_GUI_DEPS) add_subdirectory(libwallet_api_tests) endif() +if (TREZOR_DEBUG) + add_subdirectory(trezor) +endif() + # add_subdirectory(daemon_tests) set(hash_targets_sources diff --git a/tests/block_weight/CMakeLists.txt b/tests/block_weight/CMakeLists.txt index b0d716ea0..a59c9c1fc 100644 --- a/tests/block_weight/CMakeLists.txt +++ b/tests/block_weight/CMakeLists.txt @@ -42,4 +42,4 @@ target_link_libraries(block_weight add_test( NAME block_weight - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/compare.py ${CMAKE_CURRENT_SOURCE_DIR}/block_weight.py ${CMAKE_CURRENT_BINARY_DIR}/block_weight) + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/compare.py ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/block_weight.py ${CMAKE_CURRENT_BINARY_DIR}/block_weight) diff --git a/tests/block_weight/block_weight.cpp b/tests/block_weight/block_weight.cpp index 57fcb497e..1e8974b78 100644 --- a/tests/block_weight/block_weight.cpp +++ b/tests/block_weight/block_weight.cpp @@ -72,6 +72,18 @@ public: virtual uint64_t height() const override { return blocks.size(); } virtual size_t get_block_weight(const uint64_t &h) const override { return blocks[h].weight; } virtual uint64_t get_block_long_term_weight(const uint64_t &h) const override { return blocks[h].long_term_weight; } + virtual std::vector<uint64_t> get_block_weights(uint64_t start_height, size_t count) const override { + std::vector<uint64_t> ret; + ret.reserve(count); + while (count-- && start_height < blocks.size()) ret.push_back(blocks[start_height++].weight); + return ret; + } + virtual std::vector<uint64_t> get_long_term_block_weights(uint64_t start_height, size_t count) const override { + std::vector<uint64_t> ret; + ret.reserve(count); + while (count-- && start_height < blocks.size()) ret.push_back(blocks[start_height++].long_term_weight); + return ret; + } virtual crypto::hash top_block_hash(uint64_t *block_height = NULL) const override { uint64_t h = height(); crypto::hash top = crypto::null_hash; diff --git a/tests/block_weight/block_weight.py b/tests/block_weight/block_weight.py index 06aaabb02..d6fd494e3 100755 --- a/tests/block_weight/block_weight.py +++ b/tests/block_weight/block_weight.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Simulate a maximal block attack on the Monero network # This uses the scheme proposed by ArticMine # Written by Sarang Nother diff --git a/tests/block_weight/compare.py b/tests/block_weight/compare.py index c6be05206..e058a7079 100755 --- a/tests/block_weight/compare.py +++ b/tests/block_weight/compare.py @@ -3,10 +3,17 @@ import sys import subprocess -print 'running: ', sys.argv[1] -S0 = subprocess.check_output(sys.argv[1], stderr=subprocess.STDOUT) -print 'running: ', sys.argv[2] -S1 = subprocess.check_output(sys.argv[2], stderr=subprocess.STDOUT) +if len(sys.argv) == 4: + first = [sys.argv[1], sys.argv[2]] + second = [sys.argv[3]] +else: + first = [sys.argv[1]] + second = [sys.argv[2]] + +print 'running: ', first +S0 = subprocess.check_output(first, stderr=subprocess.STDOUT) +print 'running: ', second +S1 = subprocess.check_output(second, stderr=subprocess.STDOUT) print 'comparing' if S0 != S1: sys.exit(1) diff --git a/tests/core_proxy/CMakeLists.txt b/tests/core_proxy/CMakeLists.txt index 105c20d22..9818d35d7 100644 --- a/tests/core_proxy/CMakeLists.txt +++ b/tests/core_proxy/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/core_proxy/core_proxy.cpp b/tests/core_proxy/core_proxy.cpp index 583111517..388808269 100644 --- a/tests/core_proxy/core_proxy.cpp +++ b/tests/core_proxy/core_proxy.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index 33a5b4d13..6c8b3ccb6 100644 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/CMakeLists.txt b/tests/core_tests/CMakeLists.txt index 1ac0e7864..f93cbf3ad 100644 --- a/tests/core_tests/CMakeLists.txt +++ b/tests/core_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # @@ -42,7 +42,8 @@ set(core_tests_sources tx_validation.cpp v2_tests.cpp rct.cpp - bulletproofs.cpp) + bulletproofs.cpp + wallet_tools.cpp) set(core_tests_headers block_reward.h @@ -60,7 +61,8 @@ set(core_tests_headers tx_validation.h v2_tests.h rct.h - bulletproofs.h) + bulletproofs.h + wallet_tools.h) add_executable(core_tests ${core_tests_sources} @@ -73,6 +75,7 @@ target_link_libraries(core_tests version epee device + wallet ${CMAKE_THREAD_LIBS_INIT} ${EXTRA_LIBRARIES}) enable_stack_trace(core_tests) diff --git a/tests/core_tests/block_reward.cpp b/tests/core_tests/block_reward.cpp index e55378439..17fc762ec 100644 --- a/tests/core_tests/block_reward.cpp +++ b/tests/core_tests/block_reward.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/block_reward.h b/tests/core_tests/block_reward.h index b7eb7c98e..6234bb9c5 100644 --- a/tests/core_tests/block_reward.h +++ b/tests/core_tests/block_reward.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp index 760cc4328..566ec1440 100644 --- a/tests/core_tests/block_validation.cpp +++ b/tests/core_tests/block_validation.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/block_validation.h b/tests/core_tests/block_validation.h index d229e4530..4a65b029e 100644 --- a/tests/core_tests/block_validation.h +++ b/tests/core_tests/block_validation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/bulletproofs.cpp b/tests/core_tests/bulletproofs.cpp index 3e2db2e29..fd3f5114b 100644 --- a/tests/core_tests/bulletproofs.cpp +++ b/tests/core_tests/bulletproofs.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/bulletproofs.h b/tests/core_tests/bulletproofs.h index b0289f355..efc751df7 100644 --- a/tests/core_tests/bulletproofs.h +++ b/tests/core_tests/bulletproofs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 d78e793bb..c51accb67 100644 --- a/tests/core_tests/chain_split_1.cpp +++ b/tests/core_tests/chain_split_1.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 75602d1b0..503c3da19 100644 --- a/tests/core_tests/chain_split_1.h +++ b/tests/core_tests/chain_split_1.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 18a813b19..0554fd676 100644 --- a/tests/core_tests/chain_switch_1.cpp +++ b/tests/core_tests/chain_switch_1.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 989b6df11..c084de262 100644 --- a/tests/core_tests/chain_switch_1.h +++ b/tests/core_tests/chain_switch_1.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index d3cb52246..09bc10ea8 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -31,6 +31,11 @@ #include <vector> #include <iostream> #include <sstream> +#include <algorithm> +#include <array> +#include <random> +#include <sstream> +#include <fstream> #include "include_base_utils.h" @@ -105,10 +110,11 @@ void test_generator::add_block(const cryptonote::block& blk, size_t txs_weight, bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, const crypto::hash& prev_id, const cryptonote::account_base& miner_acc, uint64_t timestamp, uint64_t already_generated_coins, - std::vector<size_t>& block_weights, const std::list<cryptonote::transaction>& tx_list) + std::vector<size_t>& block_weights, const std::list<cryptonote::transaction>& tx_list, + const boost::optional<uint8_t>& hf_ver) { - blk.major_version = CURRENT_BLOCK_MAJOR_VERSION; - blk.minor_version = CURRENT_BLOCK_MINOR_VERSION; + blk.major_version = hf_ver ? hf_ver.get() : CURRENT_BLOCK_MAJOR_VERSION; + blk.minor_version = hf_ver ? hf_ver.get() : CURRENT_BLOCK_MINOR_VERSION; blk.timestamp = timestamp; blk.prev_id = prev_id; @@ -135,7 +141,7 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co size_t target_block_weight = txs_weight + get_transaction_weight(blk.miner_tx); while (true) { - if (!construct_miner_tx(height, misc_utils::median(block_weights), already_generated_coins, target_block_weight, total_fee, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), 10)) + if (!construct_miner_tx(height, misc_utils::median(block_weights), already_generated_coins, target_block_weight, total_fee, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), 10, hf_ver ? hf_ver.get() : 1)) return false; size_t actual_block_weight = txs_weight + get_transaction_weight(blk.miner_tx); @@ -180,10 +186,10 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co // Nonce search... blk.nonce = 0; - while (!miner::find_nonce_for_given_block(blk, get_test_difficulty(), height)) + while (!miner::find_nonce_for_given_block(blk, get_test_difficulty(hf_ver), height)) blk.timestamp++; - add_block(blk, txs_weight, block_weights, already_generated_coins); + add_block(blk, txs_weight, block_weights, already_generated_coins, hf_ver ? hf_ver.get() : 1); return true; } @@ -197,17 +203,18 @@ bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::a bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::block& blk_prev, const cryptonote::account_base& miner_acc, - const std::list<cryptonote::transaction>& tx_list/* = std::list<cryptonote::transaction>()*/) + const std::list<cryptonote::transaction>& tx_list/* = std::list<cryptonote::transaction>()*/, + const boost::optional<uint8_t>& hf_ver) { uint64_t height = boost::get<txin_gen>(blk_prev.miner_tx.vin.front()).height + 1; crypto::hash prev_id = get_block_hash(blk_prev); // Keep difficulty unchanged - uint64_t timestamp = blk_prev.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN; + uint64_t timestamp = blk_prev.timestamp + current_difficulty_window(hf_ver); // DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN; uint64_t already_generated_coins = get_already_generated_coins(prev_id); std::vector<size_t> block_weights; get_last_n_block_weights(block_weights, prev_id, CRYPTONOTE_REWARD_BLOCKS_WINDOW); - return construct_block(blk, height, prev_id, miner_acc, timestamp, already_generated_coins, block_weights, tx_list); + return construct_block(blk, height, prev_id, miner_acc, timestamp, already_generated_coins, block_weights, tx_list, hf_ver); } bool test_generator::construct_block_manually(block& blk, const block& prev_block, const account_base& miner_acc, @@ -244,7 +251,7 @@ bool test_generator::construct_block_manually(block& blk, const block& prev_bloc //blk.tree_root_hash = get_tx_tree_hash(blk); - difficulty_type a_diffic = actual_params & bf_diffic ? diffic : get_test_difficulty(); + difficulty_type a_diffic = actual_params & bf_diffic ? diffic : get_test_difficulty(hf_version); fill_nonce(blk, a_diffic, height); add_block(blk, txs_weight, block_weights, already_generated_coins, hf_version); @@ -259,49 +266,6 @@ bool test_generator::construct_block_manually_tx(cryptonote::block& blk, const c return construct_block_manually(blk, prev_block, miner_acc, bf_tx_hashes, 0, 0, 0, crypto::hash(), 0, transaction(), tx_hashes, txs_weight); } - -struct output_index { - const cryptonote::txout_target_v out; - uint64_t amount; - size_t blk_height; // block height - size_t tx_no; // index of transaction in block - size_t out_no; // index of out in transaction - size_t idx; - bool spent; - const cryptonote::block *p_blk; - const cryptonote::transaction *p_tx; - - output_index(const cryptonote::txout_target_v &_out, uint64_t _a, size_t _h, size_t tno, size_t ono, const cryptonote::block *_pb, const cryptonote::transaction *_pt) - : out(_out), amount(_a), blk_height(_h), tx_no(tno), out_no(ono), idx(0), spent(false), p_blk(_pb), p_tx(_pt) { } - - output_index(const output_index &other) - : out(other.out), amount(other.amount), blk_height(other.blk_height), tx_no(other.tx_no), out_no(other.out_no), idx(other.idx), spent(other.spent), p_blk(other.p_blk), p_tx(other.p_tx) { } - - const std::string toString() const { - std::stringstream ss; - - ss << "output_index{blk_height=" << blk_height - << " tx_no=" << tx_no - << " out_no=" << out_no - << " amount=" << amount - << " idx=" << idx - << " spent=" << spent - << "}"; - - return ss.str(); - } - - output_index& operator=(const output_index& other) - { - new(this) output_index(other); - return *this; - } -}; - -typedef std::map<uint64_t, std::vector<size_t> > map_output_t; -typedef std::map<uint64_t, std::vector<output_index> > map_output_idx_t; -typedef pair<uint64_t, size_t> outloc_t; - namespace { uint64_t get_inputs_amount(const vector<tx_source_entry> &s) @@ -339,6 +303,9 @@ bool init_output_indices(map_output_idx_t& outs, std::map<uint64_t, std::vector< const tx_out &out = tx.vout[j]; output_index oi(out.target, out.amount, boost::get<txin_gen>(*blk.miner_tx.vin.begin()).height, i, j, &blk, vtx[i]); + oi.set_rct(tx.version == 2); + oi.unlock_time = tx.unlock_time; + oi.is_coin_base = i == 0; if (2 == out.target.which()) { // out_to_key outs[out.amount].push_back(oi); @@ -416,8 +383,9 @@ bool fill_output_entries(std::vector<output_index>& out_indices, size_t sender_o if (append) { + rct::key comm = oi.commitment(); const txout_to_key& otk = boost::get<txout_to_key>(oi.out); - output_entries.push_back(tx_source_entry::output_entry(oi.idx, rct::ctkey({rct::pk2rct(otk.key), rct::identity()}))); + output_entries.push_back(tx_source_entry::output_entry(oi.idx, rct::ctkey({rct::pk2rct(otk.key), comm}))); } } @@ -452,6 +420,8 @@ bool fill_tx_sources(std::vector<tx_source_entry>& sources, const std::vector<te const output_index& oi = outs[o.first][sender_out]; if (oi.spent) continue; + if (oi.rct) + continue; cryptonote::tx_source_entry ts; ts.amount = oi.amount; @@ -463,6 +433,11 @@ bool fill_tx_sources(std::vector<tx_source_entry>& sources, const std::vector<te ts.real_output = realOutput; ts.rct = false; + ts.mask = rct::identity(); // non-rct has identity mask by definition + + rct::key comm = rct::zeroCommit(ts.amount); + for(auto & ot : ts.outputs) + ot.second.mask = comm; sources.push_back(ts); @@ -477,38 +452,347 @@ bool fill_tx_sources(std::vector<tx_source_entry>& sources, const std::vector<te return sources_found; } -bool fill_tx_destination(tx_destination_entry &de, const cryptonote::account_base &to, uint64_t amount) { - de.addr = to.get_keys().m_account_address; +bool fill_tx_destination(tx_destination_entry &de, const cryptonote::account_public_address &to, uint64_t amount) { + de.addr = to; de.amount = amount; return true; } -void fill_tx_sources_and_destinations(const std::vector<test_event_entry>& events, const block& blk_head, - const cryptonote::account_base& from, const cryptonote::account_base& to, - uint64_t amount, uint64_t fee, size_t nmix, std::vector<tx_source_entry>& sources, - std::vector<tx_destination_entry>& destinations) +map_txid_output_t::iterator block_tracker::find_out(const crypto::hash &txid, size_t out) +{ + return find_out(std::make_pair(txid, out)); +} + +map_txid_output_t::iterator block_tracker::find_out(const output_hasher &id) +{ + return m_map_outs.find(id); +} + +void block_tracker::process(const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx) +{ + std::vector<const cryptonote::block*> blks; + blks.reserve(blockchain.size()); + + BOOST_FOREACH (const block& blk, blockchain) { + auto hsh = get_block_hash(blk); + auto it = m_blocks.find(hsh); + if (it == m_blocks.end()){ + m_blocks[hsh] = blk; + } + + blks.push_back(&m_blocks[hsh]); + } + + process(blks, mtx); +} + +void block_tracker::process(const std::vector<const cryptonote::block*>& blockchain, const map_hash2tx_t& mtx) +{ + BOOST_FOREACH (const block* blk, blockchain) { + vector<const transaction*> vtx; + vtx.push_back(&(blk->miner_tx)); + + BOOST_FOREACH(const crypto::hash &h, blk->tx_hashes) { + const map_hash2tx_t::const_iterator cit = mtx.find(h); + CHECK_AND_ASSERT_THROW_MES(mtx.end() != cit, "block contains an unknown tx hash"); + vtx.push_back(cit->second); + } + + for (size_t i = 0; i < vtx.size(); i++) { + process(blk, vtx[i], i); + } + } +} + +void block_tracker::process(const block* blk, const transaction * tx, size_t i) +{ + for (size_t j = 0; j < tx->vout.size(); ++j) { + const tx_out &out = tx->vout[j]; + + if (typeid(cryptonote::txout_to_key) != out.target.type()) { // out_to_key + continue; + } + + const uint64_t rct_amount = tx->version == 2 ? 0 : out.amount; + const output_hasher hid = std::make_pair(tx->hash, j); + auto it = find_out(hid); + if (it != m_map_outs.end()){ + continue; + } + + output_index oi(out.target, out.amount, boost::get<txin_gen>(blk->miner_tx.vin.front()).height, i, j, blk, tx); + oi.set_rct(tx->version == 2); + oi.idx = m_outs[rct_amount].size(); + oi.unlock_time = tx->unlock_time; + oi.is_coin_base = tx->vin.size() == 1 && tx->vin.back().type() == typeid(cryptonote::txin_gen); + + m_outs[rct_amount].push_back(oi); + m_map_outs.insert({hid, oi}); + } +} + +void block_tracker::global_indices(const cryptonote::transaction *tx, std::vector<uint64_t> &indices) +{ + indices.clear(); + + for(size_t j=0; j < tx->vout.size(); ++j){ + auto it = find_out(tx->hash, j); + if (it != m_map_outs.end()){ + indices.push_back(it->second.idx); + } + } +} + +void block_tracker::get_fake_outs(size_t num_outs, uint64_t amount, uint64_t global_index, uint64_t cur_height, std::vector<get_outs_entry> &outs){ + auto & vct = m_outs[amount]; + const size_t n_outs = vct.size(); + + std::set<size_t> used; + std::vector<size_t> choices; + choices.resize(n_outs); + for(size_t i=0; i < n_outs; ++i) choices[i] = i; + shuffle(choices.begin(), choices.end(), std::default_random_engine(crypto::rand<unsigned>())); + + size_t n_iters = 0; + ssize_t idx = -1; + outs.reserve(num_outs); + while(outs.size() < num_outs){ + n_iters += 1; + idx = (idx + 1) % n_outs; + size_t oi_idx = choices[(size_t)idx]; + CHECK_AND_ASSERT_THROW_MES((n_iters / n_outs) <= outs.size(), "Fake out pick selection problem"); + + auto & oi = vct[oi_idx]; + if (oi.idx == global_index) + continue; + if (oi.out.type() != typeid(cryptonote::txout_to_key)) + continue; + if (oi.unlock_time > cur_height) + continue; + if (used.find(oi_idx) != used.end()) + continue; + + rct::key comm = oi.commitment(); + auto out = boost::get<txout_to_key>(oi.out); + auto item = std::make_tuple(oi.idx, out.key, comm); + outs.push_back(item); + used.insert(oi_idx); + } +} + +std::string block_tracker::dump_data() +{ + ostringstream ss; + for (auto &m_out : m_outs) + { + auto & vct = m_out.second; + ss << m_out.first << " => |vector| = " << vct.size() << '\n'; + + for (const auto & oi : vct) + { + auto out = boost::get<txout_to_key>(oi.out); + + ss << " idx: " << oi.idx + << ", rct: " << oi.rct + << ", xmr: " << oi.amount + << ", key: " << dump_keys(out.key.data) + << ", msk: " << dump_keys(oi.comm.bytes) + << ", txid: " << dump_keys(oi.p_tx->hash.data) + << '\n'; + } + } + + return ss.str(); +} + +void block_tracker::dump_data(const std::string & fname) +{ + ofstream myfile; + myfile.open (fname); + myfile << dump_data(); + myfile.close(); +} + +std::string dump_data(const cryptonote::transaction &tx) +{ + ostringstream ss; + ss << "msg: " << dump_keys(tx.rct_signatures.message.bytes) + << ", vin: "; + + for(auto & in : tx.vin){ + if (typeid(txin_to_key) == in.type()){ + auto tk = boost::get<txin_to_key>(in); + std::vector<uint64_t> full_off; + int64_t last = -1; + + ss << " i: " << tk.amount << " ["; + for(auto ix : tk.key_offsets){ + ss << ix << ", "; + if (last == -1){ + last = ix; + full_off.push_back(ix); + } else { + last += ix; + full_off.push_back((uint64_t)last); + } + } + + ss << "], full: ["; + for(auto ix : full_off){ + ss << ix << ", "; + } + ss << "]; "; + + } else if (typeid(txin_gen) == in.type()){ + ss << " h: " << boost::get<txin_gen>(in).height << ", "; + } else { + ss << " ?, "; + } + } + + ss << ", mixring: \n"; + for (const auto & row : tx.rct_signatures.mixRing){ + for(auto cur : row){ + ss << " (" << dump_keys(cur.dest.bytes) << ", " << dump_keys(cur.mask.bytes) << ")\n "; + } + ss << "; "; + } + + return ss.str(); +} + +cryptonote::account_public_address get_address(const var_addr_t& inp) +{ + if (typeid(cryptonote::account_public_address) == inp.type()){ + return boost::get<cryptonote::account_public_address>(inp); + } else if(typeid(cryptonote::account_keys) == inp.type()){ + return boost::get<cryptonote::account_keys>(inp).m_account_address; + } else if (typeid(cryptonote::account_base) == inp.type()){ + return boost::get<cryptonote::account_base>(inp).get_keys().m_account_address; + } else if (typeid(cryptonote::tx_destination_entry) == inp.type()){ + return boost::get<cryptonote::tx_destination_entry>(inp).addr; + } else { + throw std::runtime_error("Unexpected type"); + } +} + +cryptonote::account_public_address get_address(const cryptonote::account_public_address& inp) +{ + return inp; +} + +cryptonote::account_public_address get_address(const cryptonote::account_keys& inp) +{ + return inp.m_account_address; +} + +cryptonote::account_public_address get_address(const cryptonote::account_base& inp) +{ + return inp.get_keys().m_account_address; +} + +cryptonote::account_public_address get_address(const cryptonote::tx_destination_entry& inp) +{ + return inp.addr; +} + +uint64_t sum_amount(const std::vector<tx_destination_entry>& destinations) +{ + uint64_t amount = 0; + for(auto & cur : destinations){ + amount += cur.amount; + } + + return amount; +} + +uint64_t sum_amount(const std::vector<cryptonote::tx_source_entry>& sources) +{ + uint64_t amount = 0; + for(auto & cur : sources){ + amount += cur.amount; + } + + return amount; +} + +void fill_tx_destinations(const var_addr_t& from, const std::vector<tx_destination_entry>& dests, + uint64_t fee, + const std::vector<tx_source_entry> &sources, + std::vector<tx_destination_entry>& destinations, + bool always_change) + { - sources.clear(); destinations.clear(); + uint64_t amount = sum_amount(dests); + std::copy(dests.begin(), dests.end(), std::back_inserter(destinations)); - if (!fill_tx_sources(sources, events, blk_head, from, amount + fee, nmix)) - throw std::runtime_error("couldn't fill transaction sources"); + tx_destination_entry de_change; + uint64_t cache_back = get_inputs_amount(sources) - (amount + fee); + + if (cache_back > 0 || always_change) { + if (!fill_tx_destination(de_change, get_address(from), cache_back <= 0 ? 0 : cache_back)) + throw std::runtime_error("couldn't fill transaction cache back destination"); + destinations.push_back(de_change); + } +} + +void fill_tx_destinations(const var_addr_t& from, const cryptonote::account_public_address& to, + uint64_t amount, uint64_t fee, + const std::vector<tx_source_entry> &sources, + std::vector<tx_destination_entry>& destinations, + std::vector<tx_destination_entry>& destinations_pure, + bool always_change) +{ + destinations.clear(); tx_destination_entry de; if (!fill_tx_destination(de, to, amount)) throw std::runtime_error("couldn't fill transaction destination"); destinations.push_back(de); + destinations_pure.push_back(de); tx_destination_entry de_change; uint64_t cache_back = get_inputs_amount(sources) - (amount + fee); - if (0 < cache_back) - { - if (!fill_tx_destination(de_change, from, cache_back)) + + if (cache_back > 0 || always_change) { + if (!fill_tx_destination(de_change, get_address(from), cache_back <= 0 ? 0 : cache_back)) throw std::runtime_error("couldn't fill transaction cache back destination"); destinations.push_back(de_change); } } +void fill_tx_destinations(const var_addr_t& from, const cryptonote::account_public_address& to, + uint64_t amount, uint64_t fee, + const std::vector<tx_source_entry> &sources, + std::vector<tx_destination_entry>& destinations, bool always_change) +{ + std::vector<tx_destination_entry> destinations_pure; + fill_tx_destinations(from, to, amount, fee, sources, destinations, destinations_pure, always_change); +} + +void fill_tx_sources_and_destinations(const std::vector<test_event_entry>& events, const block& blk_head, + const cryptonote::account_base& from, const cryptonote::account_public_address& to, + uint64_t amount, uint64_t fee, size_t nmix, std::vector<tx_source_entry>& sources, + std::vector<tx_destination_entry>& destinations) +{ + sources.clear(); + destinations.clear(); + + if (!fill_tx_sources(sources, events, blk_head, from, amount + fee, nmix)) + throw std::runtime_error("couldn't fill transaction sources"); + + fill_tx_destinations(from, to, amount, fee, sources, destinations, false); +} + +void fill_tx_sources_and_destinations(const std::vector<test_event_entry>& events, const block& blk_head, + const cryptonote::account_base& from, const cryptonote::account_base& to, + uint64_t amount, uint64_t fee, size_t nmix, std::vector<tx_source_entry>& sources, + std::vector<tx_destination_entry>& destinations) +{ + fill_tx_sources_and_destinations(events, blk_head, from, to.get_keys().m_account_address, amount, fee, nmix, sources, destinations); +} + void fill_nonce(cryptonote::block& blk, const difficulty_type& diffic, uint64_t height) { blk.nonce = 0; @@ -516,6 +800,32 @@ void fill_nonce(cryptonote::block& blk, const difficulty_type& diffic, uint64_t blk.timestamp++; } +cryptonote::tx_destination_entry build_dst(const var_addr_t& to, bool is_subaddr, uint64_t amount) +{ + tx_destination_entry de; + de.amount = amount; + de.addr = get_address(to); + de.is_subaddress = is_subaddr; + return de; +} + +std::vector<cryptonote::tx_destination_entry> build_dsts(const var_addr_t& to1, bool sub1, uint64_t am1) +{ + std::vector<cryptonote::tx_destination_entry> res; + res.push_back(build_dst(to1, sub1, am1)); + return res; +} + +std::vector<cryptonote::tx_destination_entry> build_dsts(std::initializer_list<dest_wrapper_t> inps) +{ + std::vector<cryptonote::tx_destination_entry> res; + res.reserve(inps.size()); + for(auto & c : inps){ + res.push_back(build_dst(c.addr, c.is_subaddr, c.amount)); + } + return res; +} + bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins, const account_public_address& miner_address, transaction& tx, uint64_t fee, keypair* p_txkey/* = 0*/) @@ -556,22 +866,70 @@ bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins return true; } -bool construct_tx_to_key(const std::vector<test_event_entry>& events, cryptonote::transaction& tx, const block& blk_head, - const cryptonote::account_base& from, const cryptonote::account_base& to, uint64_t amount, - uint64_t fee, size_t nmix) +bool construct_tx_to_key(const std::vector<test_event_entry>& events, cryptonote::transaction& tx, const cryptonote::block& blk_head, + const cryptonote::account_base& from, const var_addr_t& to, uint64_t amount, + uint64_t fee, size_t nmix, bool rct, rct::RangeProofType range_proof_type, int bp_version) +{ + vector<tx_source_entry> sources; + vector<tx_destination_entry> destinations; + fill_tx_sources_and_destinations(events, blk_head, from, get_address(to), amount, fee, nmix, sources, destinations); + + return construct_tx_rct(from.get_keys(), sources, destinations, from.get_keys().m_account_address, std::vector<uint8_t>(), tx, 0, rct, range_proof_type, bp_version); +} + +bool construct_tx_to_key(const std::vector<test_event_entry>& events, cryptonote::transaction& tx, const cryptonote::block& blk_head, + const cryptonote::account_base& from, std::vector<cryptonote::tx_destination_entry> destinations, + uint64_t fee, size_t nmix, bool rct, rct::RangeProofType range_proof_type, int bp_version) { vector<tx_source_entry> sources; + vector<tx_destination_entry> destinations_all; + uint64_t amount = sum_amount(destinations); + + if (!fill_tx_sources(sources, events, blk_head, from, amount + fee, nmix)) + throw std::runtime_error("couldn't fill transaction sources"); + + fill_tx_destinations(from, destinations, fee, sources, destinations_all, false); + + return construct_tx_rct(from.get_keys(), sources, destinations_all, get_address(from), std::vector<uint8_t>(), tx, 0, rct, range_proof_type, bp_version); +} + +bool construct_tx_to_key(cryptonote::transaction& tx, + const cryptonote::account_base& from, const var_addr_t& to, uint64_t amount, + std::vector<cryptonote::tx_source_entry> &sources, + uint64_t fee, bool rct, rct::RangeProofType range_proof_type, int bp_version) +{ vector<tx_destination_entry> destinations; - fill_tx_sources_and_destinations(events, blk_head, from, to, amount, fee, nmix, sources, destinations); + fill_tx_destinations(from, get_address(to), amount, fee, sources, destinations, rct); + return construct_tx_rct(from.get_keys(), sources, destinations, get_address(from), std::vector<uint8_t>(), tx, 0, rct, range_proof_type, bp_version); +} + +bool construct_tx_to_key(cryptonote::transaction& tx, + const cryptonote::account_base& from, + const std::vector<cryptonote::tx_destination_entry>& destinations, + std::vector<cryptonote::tx_source_entry> &sources, + uint64_t fee, bool rct, rct::RangeProofType range_proof_type, int bp_version) +{ + vector<tx_destination_entry> all_destinations; + fill_tx_destinations(from, destinations, fee, sources, all_destinations, rct); + return construct_tx_rct(from.get_keys(), sources, all_destinations, get_address(from), std::vector<uint8_t>(), tx, 0, rct, range_proof_type, bp_version); +} - return construct_tx(from.get_keys(), sources, destinations, from.get_keys().m_account_address, std::vector<uint8_t>(), tx, 0); +bool construct_tx_rct(const cryptonote::account_keys& sender_account_keys, std::vector<cryptonote::tx_source_entry>& sources, const std::vector<cryptonote::tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, cryptonote::transaction& tx, uint64_t unlock_time, bool rct, rct::RangeProofType range_proof_type, int bp_version) +{ + std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses; + subaddresses[sender_account_keys.m_account_address.m_spend_public_key] = {0, 0}; + crypto::secret_key tx_key; + std::vector<crypto::secret_key> additional_tx_keys; + std::vector<tx_destination_entry> destinations_copy = destinations; + rct::RCTConfig rct_config = {range_proof_type, bp_version}; + return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, rct_config, nullptr); } transaction construct_tx_with_fee(std::vector<test_event_entry>& events, const block& blk_head, - const account_base& acc_from, const account_base& acc_to, uint64_t amount, uint64_t fee) + const account_base& acc_from, const var_addr_t& to, uint64_t amount, uint64_t fee) { transaction tx; - construct_tx_to_key(events, tx, blk_head, acc_from, acc_to, amount, fee, 0); + construct_tx_to_key(events, tx, blk_head, acc_from, to, amount, fee, 0); events.push_back(tx); return tx; } @@ -602,6 +960,24 @@ uint64_t get_balance(const cryptonote::account_base& addr, const std::vector<cry return res; } +bool extract_hard_forks(const std::vector<test_event_entry>& events, v_hardforks_t& hard_forks) +{ + for(auto & ev : events) + { + if (typeid(event_replay_settings) == ev.type()) + { + const auto & rep_settings = boost::get<event_replay_settings>(ev); + if (rep_settings.hard_forks) + { + const auto & hf = rep_settings.hard_forks.get(); + std::copy(hf.begin(), hf.end(), std::back_inserter(hard_forks)); + } + } + } + + return !hard_forks.empty(); +} + void get_confirmed_txs(const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx, map_hash2tx_t& confirmed_txs) { std::unordered_set<crypto::hash> confirmed_hashes; @@ -622,6 +998,74 @@ void get_confirmed_txs(const std::vector<cryptonote::block>& blockchain, const m } } +bool trim_block_chain(std::vector<cryptonote::block>& blockchain, const crypto::hash& tail){ + size_t cut = 0; + bool found = true; + + for(size_t i = 0; i < blockchain.size(); ++i){ + crypto::hash chash = get_block_hash(blockchain[i]); + if (chash == tail){ + cut = i; + found = true; + break; + } + } + + if (found && cut > 0){ + blockchain.erase(blockchain.begin(), blockchain.begin() + cut); + } + + return found; +} + +bool trim_block_chain(std::vector<const cryptonote::block*>& blockchain, const crypto::hash& tail){ + size_t cut = 0; + bool found = true; + + for(size_t i = 0; i < blockchain.size(); ++i){ + crypto::hash chash = get_block_hash(*blockchain[i]); + if (chash == tail){ + cut = i; + found = true; + break; + } + } + + if (found && cut > 0){ + blockchain.erase(blockchain.begin(), blockchain.begin() + cut); + } + + return found; +} + +uint64_t num_blocks(const std::vector<test_event_entry>& events) +{ + uint64_t res = 0; + BOOST_FOREACH(const test_event_entry& ev, events) + { + if (typeid(block) == ev.type()) + { + res += 1; + } + } + + return res; +} + +cryptonote::block get_head_block(const std::vector<test_event_entry>& events) +{ + for(auto it = events.rbegin(); it != events.rend(); ++it) + { + auto &ev = *it; + if (typeid(block) == ev.type()) + { + return boost::get<block>(ev); + } + } + + throw std::runtime_error("No block event"); +} + bool find_block_chain(const std::vector<test_event_entry>& events, std::vector<cryptonote::block>& blockchain, map_hash2tx_t& mtx, const crypto::hash& head) { std::unordered_map<crypto::hash, const block*> block_index; BOOST_FOREACH(const test_event_entry& ev, events) @@ -655,6 +1099,38 @@ bool find_block_chain(const std::vector<test_event_entry>& events, std::vector<c return b_success; } +bool find_block_chain(const std::vector<test_event_entry>& events, std::vector<const cryptonote::block*>& blockchain, map_hash2tx_t& mtx, const crypto::hash& head) { + std::unordered_map<crypto::hash, const block*> block_index; + BOOST_FOREACH(const test_event_entry& ev, events) + { + if (typeid(block) == ev.type()) + { + const block* blk = &boost::get<block>(ev); + block_index[get_block_hash(*blk)] = blk; + } + else if (typeid(transaction) == ev.type()) + { + const transaction& tx = boost::get<transaction>(ev); + mtx[get_transaction_hash(tx)] = &tx; + } + } + + bool b_success = false; + crypto::hash id = head; + for (auto it = block_index.find(id); block_index.end() != it; it = block_index.find(id)) + { + blockchain.push_back(it->second); + id = it->second->prev_id; + if (null_hash == id) + { + b_success = true; + break; + } + } + reverse(blockchain.begin(), blockchain.end()); + return b_success; +} + void test_chain_unit_base::register_callback(const std::string& cb_name, verify_callback cb) { diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 82c480163..f2bcb7787 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -37,8 +37,12 @@ #include <boost/archive/binary_oarchive.hpp> #include <boost/archive/binary_iarchive.hpp> #include <boost/program_options.hpp> +#include <boost/optional.hpp> #include <boost/serialization/vector.hpp> #include <boost/serialization/variant.hpp> +#include <boost/serialization/optional.hpp> +#include <boost/serialization/unordered_map.hpp> +#include <boost/functional/hash.hpp> #include "include_base_utils.h" #include "common/boost_serialization_helper.h" @@ -129,13 +133,32 @@ private: } }; +typedef std::vector<std::pair<uint8_t, uint64_t>> v_hardforks_t; +struct event_replay_settings +{ + boost::optional<v_hardforks_t> hard_forks; + + event_replay_settings() = default; + +private: + friend class boost::serialization::access; + + template<class Archive> + void serialize(Archive & ar, const unsigned int /*version*/) + { + ar & hard_forks; + } +}; + + VARIANT_TAG(binary_archive, callback_entry, 0xcb); VARIANT_TAG(binary_archive, cryptonote::account_base, 0xcc); VARIANT_TAG(binary_archive, serialized_block, 0xcd); VARIANT_TAG(binary_archive, serialized_transaction, 0xce); VARIANT_TAG(binary_archive, event_visitor_settings, 0xcf); +VARIANT_TAG(binary_archive, event_replay_settings, 0xda); -typedef boost::variant<cryptonote::block, cryptonote::transaction, std::vector<cryptonote::transaction>, cryptonote::account_base, callback_entry, serialized_block, serialized_transaction, event_visitor_settings> test_event_entry; +typedef boost::variant<cryptonote::block, cryptonote::transaction, std::vector<cryptonote::transaction>, cryptonote::account_base, callback_entry, serialized_block, serialized_transaction, event_visitor_settings, event_replay_settings> test_event_entry; typedef std::unordered_map<crypto::hash, const cryptonote::transaction*> map_hash2tx_t; class test_chain_unit_base @@ -173,6 +196,17 @@ public: crypto::hash prev_id; uint64_t already_generated_coins; size_t block_weight; + + private: + friend class boost::serialization::access; + + template<class Archive> + void serialize(Archive & ar, const unsigned int /*version*/) + { + ar & prev_id; + ar & already_generated_coins; + ar & block_weight; + } }; enum block_fields @@ -189,6 +223,8 @@ public: bf_hf_version= 1 << 8 }; + test_generator() {} + test_generator(const test_generator &other): m_blocks_info(other.m_blocks_info) {} void get_block_chain(std::vector<block_info>& blockchain, const crypto::hash& head, size_t n) const; void get_last_n_block_weights(std::vector<size_t>& block_weights, const crypto::hash& head, size_t n) const; uint64_t get_already_generated_coins(const crypto::hash& blk_id) const; @@ -198,10 +234,12 @@ public: uint8_t hf_version = 1); bool construct_block(cryptonote::block& blk, uint64_t height, const crypto::hash& prev_id, const cryptonote::account_base& miner_acc, uint64_t timestamp, uint64_t already_generated_coins, - std::vector<size_t>& block_weights, const std::list<cryptonote::transaction>& tx_list); + std::vector<size_t>& block_weights, const std::list<cryptonote::transaction>& tx_list, + const boost::optional<uint8_t>& hf_ver = boost::none); bool construct_block(cryptonote::block& blk, const cryptonote::account_base& miner_acc, uint64_t timestamp); bool construct_block(cryptonote::block& blk, const cryptonote::block& blk_prev, const cryptonote::account_base& miner_acc, - const std::list<cryptonote::transaction>& tx_list = std::list<cryptonote::transaction>()); + const std::list<cryptonote::transaction>& tx_list = std::list<cryptonote::transaction>(), + const boost::optional<uint8_t>& hf_ver = boost::none); bool construct_block_manually(cryptonote::block& blk, const cryptonote::block& prev_block, const cryptonote::account_base& miner_acc, int actual_params = bf_none, uint8_t major_ver = 0, @@ -214,30 +252,241 @@ public: private: std::unordered_map<crypto::hash, block_info> m_blocks_info; + + friend class boost::serialization::access; + + template<class Archive> + void serialize(Archive & ar, const unsigned int /*version*/) + { + ar & m_blocks_info; + } }; -inline cryptonote::difficulty_type get_test_difficulty() {return 1;} +template<typename T> +std::string dump_keys(T * buff32) +{ + std::ostringstream ss; + char buff[10]; + + ss << "["; + for(int i = 0; i < 32; i++) + { + snprintf(buff, 10, "0x%02x", ((uint8_t)buff32[i] & 0xff)); + ss << buff; + if (i < 31) + ss << ","; + } + ss << "]"; + return ss.str(); +} + +struct output_index { + const cryptonote::txout_target_v out; + uint64_t amount; + size_t blk_height; // block height + size_t tx_no; // index of transaction in block + size_t out_no; // index of out in transaction + size_t idx; + uint64_t unlock_time; + bool is_coin_base; + bool spent; + bool rct; + rct::key comm; + const cryptonote::block *p_blk; + const cryptonote::transaction *p_tx; + + output_index(const cryptonote::txout_target_v &_out, uint64_t _a, size_t _h, size_t tno, size_t ono, const cryptonote::block *_pb, const cryptonote::transaction *_pt) + : out(_out), amount(_a), blk_height(_h), tx_no(tno), out_no(ono), idx(0), unlock_time(0), + is_coin_base(false), spent(false), rct(false), p_blk(_pb), p_tx(_pt) + { + + } + + output_index(const output_index &other) + : out(other.out), amount(other.amount), blk_height(other.blk_height), tx_no(other.tx_no), rct(other.rct), + out_no(other.out_no), idx(other.idx), unlock_time(other.unlock_time), is_coin_base(other.is_coin_base), + spent(other.spent), comm(other.comm), p_blk(other.p_blk), p_tx(other.p_tx) { } + + void set_rct(bool arct) { + rct = arct; + if (rct && p_tx->rct_signatures.outPk.size() > out_no) + comm = p_tx->rct_signatures.outPk[out_no].mask; + else + comm = rct::commit(amount, rct::identity()); + } + + rct::key commitment() const { + return comm; + } + + const std::string toString() const { + std::stringstream ss; + + ss << "output_index{blk_height=" << blk_height + << " tx_no=" << tx_no + << " out_no=" << out_no + << " amount=" << amount + << " idx=" << idx + << " unlock_time=" << unlock_time + << " spent=" << spent + << " is_coin_base=" << is_coin_base + << " rct=" << rct + << " comm=" << dump_keys(comm.bytes) + << "}"; + + return ss.str(); + } + + output_index& operator=(const output_index& other) + { + new(this) output_index(other); + return *this; + } +}; + +typedef std::tuple<uint64_t, crypto::public_key, rct::key> get_outs_entry; +typedef std::pair<crypto::hash, size_t> output_hasher; +typedef boost::hash<output_hasher> output_hasher_hasher; +typedef std::map<uint64_t, std::vector<size_t> > map_output_t; +typedef std::map<uint64_t, std::vector<output_index> > map_output_idx_t; +typedef std::unordered_map<crypto::hash, cryptonote::block> map_block_t; +typedef std::unordered_map<output_hasher, output_index, output_hasher_hasher> map_txid_output_t; +typedef std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses_t; +typedef std::pair<uint64_t, size_t> outloc_t; + +typedef boost::variant<cryptonote::account_public_address, cryptonote::account_keys, cryptonote::account_base, cryptonote::tx_destination_entry> var_addr_t; +typedef struct { + const var_addr_t addr; + bool is_subaddr; + uint64_t amount; +} dest_wrapper_t; + +// Daemon functionality +class block_tracker +{ +public: + map_output_idx_t m_outs; + map_txid_output_t m_map_outs; // mapping (txid, out) -> output_index + map_block_t m_blocks; + + block_tracker() = default; + block_tracker(const block_tracker &bt): m_outs(bt.m_outs), m_map_outs(bt.m_map_outs), m_blocks(bt.m_blocks) {}; + map_txid_output_t::iterator find_out(const crypto::hash &txid, size_t out); + map_txid_output_t::iterator find_out(const output_hasher &id); + void process(const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx); + void process(const std::vector<const cryptonote::block*>& blockchain, const map_hash2tx_t& mtx); + void process(const cryptonote::block* blk, const cryptonote::transaction * tx, size_t i); + void global_indices(const cryptonote::transaction *tx, std::vector<uint64_t> &indices); + void get_fake_outs(size_t num_outs, uint64_t amount, uint64_t global_index, uint64_t cur_height, std::vector<get_outs_entry> &outs); + + std::string dump_data(); + void dump_data(const std::string & fname); + +private: + friend class boost::serialization::access; + + template<class Archive> + void serialize(Archive & ar, const unsigned int /*version*/) + { + ar & m_outs; + ar & m_map_outs; + ar & m_blocks; + } +}; + +std::string dump_data(const cryptonote::transaction &tx); +cryptonote::account_public_address get_address(const var_addr_t& inp); +cryptonote::account_public_address get_address(const cryptonote::account_public_address& inp); +cryptonote::account_public_address get_address(const cryptonote::account_keys& inp); +cryptonote::account_public_address get_address(const cryptonote::account_base& inp); +cryptonote::account_public_address get_address(const cryptonote::tx_destination_entry& inp); + +inline cryptonote::difficulty_type get_test_difficulty(const boost::optional<uint8_t>& hf_ver=boost::none) {return !hf_ver || hf_ver.get() <= 1 ? 1 : 2;} +inline uint64_t current_difficulty_window(const boost::optional<uint8_t>& hf_ver=boost::none){ return !hf_ver || hf_ver.get() <= 1 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; } void fill_nonce(cryptonote::block& blk, const cryptonote::difficulty_type& diffic, uint64_t height); +cryptonote::tx_destination_entry build_dst(const var_addr_t& to, bool is_subaddr=false, uint64_t amount=0); +std::vector<cryptonote::tx_destination_entry> build_dsts(const var_addr_t& to1, bool sub1=false, uint64_t am1=0); +std::vector<cryptonote::tx_destination_entry> build_dsts(std::initializer_list<dest_wrapper_t> inps); +uint64_t sum_amount(const std::vector<cryptonote::tx_destination_entry>& destinations); +uint64_t sum_amount(const std::vector<cryptonote::tx_source_entry>& sources); + bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins, const cryptonote::account_public_address& miner_address, cryptonote::transaction& tx, - uint64_t fee, cryptonote::keypair* p_txkey = 0); + uint64_t fee, cryptonote::keypair* p_txkey = nullptr); + bool construct_tx_to_key(const std::vector<test_event_entry>& events, cryptonote::transaction& tx, - const cryptonote::block& blk_head, const cryptonote::account_base& from, const cryptonote::account_base& to, - uint64_t amount, uint64_t fee, size_t nmix); + const cryptonote::block& blk_head, const cryptonote::account_base& from, const var_addr_t& to, uint64_t amount, + uint64_t fee, size_t nmix, bool rct=false, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0); + +bool construct_tx_to_key(const std::vector<test_event_entry>& events, cryptonote::transaction& tx, const cryptonote::block& blk_head, + const cryptonote::account_base& from, std::vector<cryptonote::tx_destination_entry> destinations, + uint64_t fee, size_t nmix, bool rct=false, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0); + +bool construct_tx_to_key(cryptonote::transaction& tx, const cryptonote::account_base& from, const var_addr_t& to, uint64_t amount, + std::vector<cryptonote::tx_source_entry> &sources, + uint64_t fee, bool rct=false, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0); + +bool construct_tx_to_key(cryptonote::transaction& tx, const cryptonote::account_base& from, const std::vector<cryptonote::tx_destination_entry>& destinations, + std::vector<cryptonote::tx_source_entry> &sources, + uint64_t fee, bool rct, rct::RangeProofType range_proof_type, int bp_version = 0); + cryptonote::transaction construct_tx_with_fee(std::vector<test_event_entry>& events, const cryptonote::block& blk_head, - const cryptonote::account_base& acc_from, const cryptonote::account_base& acc_to, + const cryptonote::account_base& acc_from, const var_addr_t& to, uint64_t amount, uint64_t fee); +bool construct_tx_rct(const cryptonote::account_keys& sender_account_keys, + std::vector<cryptonote::tx_source_entry>& sources, + const std::vector<cryptonote::tx_destination_entry>& destinations, + const boost::optional<cryptonote::account_public_address>& change_addr, + std::vector<uint8_t> extra, cryptonote::transaction& tx, uint64_t unlock_time, + bool rct=false, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0); + + +uint64_t num_blocks(const std::vector<test_event_entry>& events); +cryptonote::block get_head_block(const std::vector<test_event_entry>& events); + void get_confirmed_txs(const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx, map_hash2tx_t& confirmed_txs); +bool trim_block_chain(std::vector<cryptonote::block>& blockchain, const crypto::hash& tail); +bool trim_block_chain(std::vector<const cryptonote::block*>& blockchain, const crypto::hash& tail); bool find_block_chain(const std::vector<test_event_entry>& events, std::vector<cryptonote::block>& blockchain, map_hash2tx_t& mtx, const crypto::hash& head); +bool find_block_chain(const std::vector<test_event_entry>& events, std::vector<const cryptonote::block*>& blockchain, map_hash2tx_t& mtx, const crypto::hash& head); + +void fill_tx_destinations(const var_addr_t& from, const cryptonote::account_public_address& to, + uint64_t amount, uint64_t fee, + const std::vector<cryptonote::tx_source_entry> &sources, + std::vector<cryptonote::tx_destination_entry>& destinations, bool always_change=false); + +void fill_tx_destinations(const var_addr_t& from, const std::vector<cryptonote::tx_destination_entry>& dests, + uint64_t fee, + const std::vector<cryptonote::tx_source_entry> &sources, + std::vector<cryptonote::tx_destination_entry>& destinations, + bool always_change); + +void fill_tx_destinations(const var_addr_t& from, const cryptonote::account_public_address& to, + uint64_t amount, uint64_t fee, + const std::vector<cryptonote::tx_source_entry> &sources, + std::vector<cryptonote::tx_destination_entry>& destinations, + std::vector<cryptonote::tx_destination_entry>& destinations_pure, + bool always_change=false); + + +void fill_tx_sources_and_destinations(const std::vector<test_event_entry>& events, const cryptonote::block& blk_head, + const cryptonote::account_base& from, const cryptonote::account_public_address& to, + uint64_t amount, uint64_t fee, size_t nmix, + std::vector<cryptonote::tx_source_entry>& sources, + std::vector<cryptonote::tx_destination_entry>& destinations); + void fill_tx_sources_and_destinations(const std::vector<test_event_entry>& events, const cryptonote::block& blk_head, const cryptonote::account_base& from, const cryptonote::account_base& to, uint64_t amount, uint64_t fee, size_t nmix, std::vector<cryptonote::tx_source_entry>& sources, std::vector<cryptonote::tx_destination_entry>& destinations); + uint64_t get_balance(const cryptonote::account_base& addr, const std::vector<cryptonote::block>& blockchain, const map_hash2tx_t& mtx); +bool extract_hard_forks(const std::vector<test_event_entry>& events, v_hardforks_t& hard_forks); + //-------------------------------------------------------------------------- template<class t_test_class> auto do_check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_index, const cryptonote::transaction& tx, t_test_class& validator, int) @@ -338,6 +587,12 @@ public: m_ev_index = ev_index; } + bool operator()(const event_replay_settings& settings) + { + log_event("event_replay_settings"); + return true; + } + bool operator()(const event_visitor_settings& settings) { log_event("event_visitor_settings"); @@ -461,12 +716,20 @@ private: template<class t_test_class> inline bool replay_events_through_core(cryptonote::core& cr, const std::vector<test_event_entry>& events, t_test_class& validator) { + return replay_events_through_core_plain(cr, events, validator, true); +} +//-------------------------------------------------------------------------- +template<class t_test_class> +inline bool replay_events_through_core_plain(cryptonote::core& cr, const std::vector<test_event_entry>& events, t_test_class& validator, bool reinit=true) +{ TRY_ENTRY(); //init core here - - CHECK_AND_ASSERT_MES(typeid(cryptonote::block) == events[0].type(), false, "First event must be genesis block creation"); - cr.set_genesis_block(boost::get<cryptonote::block>(events[0])); + if (reinit) { + CHECK_AND_ASSERT_MES(typeid(cryptonote::block) == events[0].type(), false, + "First event must be genesis block creation"); + cr.set_genesis_block(boost::get<cryptonote::block>(events[0])); + } bool r = true; push_core_event_visitor<t_test_class> visitor(cr, events, validator); @@ -489,10 +752,9 @@ struct get_test_options { }; get_test_options():hard_forks{std::make_pair((uint8_t)1, (uint64_t)0), std::make_pair((uint8_t)0, (uint64_t)0)}{} }; - //-------------------------------------------------------------------------- template<class t_test_class> -inline bool do_replay_events(std::vector<test_event_entry>& events) +inline bool do_replay_events_get_core(std::vector<test_event_entry>& events, cryptonote::core *core) { boost::program_options::options_description desc("Allowed options"); cryptonote::core::init_options(desc); @@ -506,12 +768,23 @@ inline bool do_replay_events(std::vector<test_event_entry>& events) if (!r) return false; - cryptonote::cryptonote_protocol_stub pr; //TODO: stub only for this kind of test, make real validation of relayed objects - cryptonote::core c(&pr); + auto & c = *core; + // FIXME: make sure that vm has arg_testnet_on set to true or false if // this test needs for it to be so. get_test_options<t_test_class> gto; - if (!c.init(vm, >o.test_options)) + + // Hardforks can be specified in events. + v_hardforks_t hardforks; + cryptonote::test_options test_options_tmp{}; + const cryptonote::test_options * test_options_ = >o.test_options; + if (extract_hard_forks(events, hardforks)){ + hardforks.push_back(std::make_pair((uint8_t)0, (uint64_t)0)); // terminator + test_options_tmp.hard_forks = hardforks.data(); + test_options_ = &test_options_tmp; + } + + if (!c.init(vm, test_options_)) { MERROR("Failed to init core"); return false; @@ -529,7 +802,31 @@ inline bool do_replay_events(std::vector<test_event_entry>& events) t_test_class validator; bool ret = replay_events_through_core<t_test_class>(c, events, validator); - c.deinit(); +// c.deinit(); + return ret; +} +//-------------------------------------------------------------------------- +template<class t_test_class> +inline bool replay_events_through_core_validate(std::vector<test_event_entry>& events, cryptonote::core & c) +{ + std::vector<crypto::hash> pool_txs; + if (!c.get_pool_transaction_hashes(pool_txs)) + { + MERROR("Failed to flush txpool"); + return false; + } + c.get_blockchain_storage().flush_txes_from_pool(pool_txs); + + t_test_class validator; + return replay_events_through_core_plain<t_test_class>(c, events, validator, false); +} +//-------------------------------------------------------------------------- +template<class t_test_class> +inline bool do_replay_events(std::vector<test_event_entry>& events) +{ + cryptonote::core core(nullptr); + bool ret = do_replay_events_get_core<t_test_class>(events, &core); + core.deinit(); return ret; } //-------------------------------------------------------------------------- @@ -546,6 +843,12 @@ inline bool do_replay_file(const std::string& filename) } //-------------------------------------------------------------------------- +#define DEFAULT_HARDFORKS(HARDFORKS) do { \ + HARDFORKS.push_back(std::make_pair((uint8_t)1, (uint64_t)0)); \ +} while(0) + +#define ADD_HARDFORK(HARDFORKS, FORK, HEIGHT) HARDFORKS.push_back(std::make_pair((uint8_t)FORK, (uint64_t)HEIGHT)) + #define GENERATE_ACCOUNT(account) \ cryptonote::account_base account; \ account.generate(); @@ -589,6 +892,11 @@ inline bool do_replay_file(const std::string& filename) generator.construct_block(BLK_NAME, PREV_BLOCK, MINER_ACC); \ VEC_EVENTS.push_back(BLK_NAME); +#define MAKE_NEXT_BLOCK_HF(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, HF) \ + cryptonote::block BLK_NAME; \ + generator.construct_block(BLK_NAME, PREV_BLOCK, MINER_ACC, std::list<cryptonote::transaction>(), HF); \ + VEC_EVENTS.push_back(BLK_NAME); + #define MAKE_NEXT_BLOCK_TX1(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TX1) \ cryptonote::block BLK_NAME; \ { \ @@ -598,46 +906,91 @@ inline bool do_replay_file(const std::string& filename) } \ VEC_EVENTS.push_back(BLK_NAME); +#define MAKE_NEXT_BLOCK_TX1_HF(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TX1, HF) \ + cryptonote::block BLK_NAME; \ + { \ + std::list<cryptonote::transaction> tx_list; \ + tx_list.push_back(TX1); \ + generator.construct_block(BLK_NAME, PREV_BLOCK, MINER_ACC, tx_list, HF); \ + } \ + VEC_EVENTS.push_back(BLK_NAME); + #define MAKE_NEXT_BLOCK_TX_LIST(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TXLIST) \ cryptonote::block BLK_NAME; \ generator.construct_block(BLK_NAME, PREV_BLOCK, MINER_ACC, TXLIST); \ VEC_EVENTS.push_back(BLK_NAME); -#define REWIND_BLOCKS_N(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, COUNT) \ +#define MAKE_NEXT_BLOCK_TX_LIST_HF(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TXLIST, HF) \ cryptonote::block BLK_NAME; \ + generator.construct_block(BLK_NAME, PREV_BLOCK, MINER_ACC, TXLIST, HF); \ + VEC_EVENTS.push_back(BLK_NAME); + +#define REWIND_BLOCKS_N_HF(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, COUNT, HF) \ + cryptonote::block BLK_NAME; \ { \ - cryptonote::block blk_last = PREV_BLOCK; \ + cryptonote::block blk_last = PREV_BLOCK; \ for (size_t i = 0; i < COUNT; ++i) \ { \ - MAKE_NEXT_BLOCK(VEC_EVENTS, blk, blk_last, MINER_ACC); \ + MAKE_NEXT_BLOCK_HF(VEC_EVENTS, blk, blk_last, MINER_ACC, HF); \ blk_last = blk; \ } \ BLK_NAME = blk_last; \ } +#define REWIND_BLOCKS_N(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, COUNT) REWIND_BLOCKS_N_HF(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, COUNT, boost::none) #define REWIND_BLOCKS(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC) REWIND_BLOCKS_N(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW) +#define REWIND_BLOCKS_HF(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, HF) REWIND_BLOCKS_N_HF(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, HF) #define MAKE_TX_MIX(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \ cryptonote::transaction TX_NAME; \ construct_tx_to_key(VEC_EVENTS, TX_NAME, HEAD, FROM, TO, AMOUNT, TESTS_DEFAULT_FEE, NMIX); \ VEC_EVENTS.push_back(TX_NAME); +#define MAKE_TX_MIX_RCT(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \ + cryptonote::transaction TX_NAME; \ + construct_tx_to_key(VEC_EVENTS, TX_NAME, HEAD, FROM, TO, AMOUNT, TESTS_DEFAULT_FEE, NMIX, true, rct::RangeProofPaddedBulletproof); \ + VEC_EVENTS.push_back(TX_NAME); + #define MAKE_TX(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, HEAD) MAKE_TX_MIX(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, 0, HEAD) #define MAKE_TX_MIX_LIST(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \ { \ - cryptonote::transaction t; \ + cryptonote::transaction t; \ construct_tx_to_key(VEC_EVENTS, t, HEAD, FROM, TO, AMOUNT, TESTS_DEFAULT_FEE, NMIX); \ SET_NAME.push_back(t); \ VEC_EVENTS.push_back(t); \ } +#define MAKE_TX_MIX_LIST_RCT(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \ + MAKE_TX_MIX_LIST_RCT_EX(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD, rct::RangeProofPaddedBulletproof, 1) +#define MAKE_TX_MIX_LIST_RCT_EX(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD, RCT_TYPE, BP_VER) \ + { \ + cryptonote::transaction t; \ + construct_tx_to_key(VEC_EVENTS, t, HEAD, FROM, TO, AMOUNT, TESTS_DEFAULT_FEE, NMIX, true, RCT_TYPE, BP_VER); \ + SET_NAME.push_back(t); \ + VEC_EVENTS.push_back(t); \ + } + +#define MAKE_TX_MIX_DEST_LIST_RCT(VEC_EVENTS, SET_NAME, FROM, TO, NMIX, HEAD) \ + MAKE_TX_MIX_DEST_LIST_RCT_EX(VEC_EVENTS, SET_NAME, FROM, TO, NMIX, HEAD, rct::RangeProofPaddedBulletproof, 1) +#define MAKE_TX_MIX_DEST_LIST_RCT_EX(VEC_EVENTS, SET_NAME, FROM, TO, NMIX, HEAD, RCT_TYPE, BP_VER) \ + { \ + cryptonote::transaction t; \ + construct_tx_to_key(VEC_EVENTS, t, HEAD, FROM, TO, TESTS_DEFAULT_FEE, NMIX, true, RCT_TYPE, BP_VER); \ + SET_NAME.push_back(t); \ + VEC_EVENTS.push_back(t); \ + } + #define MAKE_TX_LIST(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, HEAD) MAKE_TX_MIX_LIST(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, 0, HEAD) #define MAKE_TX_LIST_START(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, HEAD) \ std::list<cryptonote::transaction> SET_NAME; \ MAKE_TX_LIST(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, HEAD); +#define MAKE_TX_LIST_START_RCT(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \ + std::list<cryptonote::transaction> SET_NAME; \ + MAKE_TX_MIX_LIST_RCT(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD); + #define MAKE_MINER_TX_AND_KEY_MANUALLY(TX, BLK, KEY) \ transaction TX; \ if (!construct_miner_tx_manually(get_block_height(BLK) + 1, generator.get_already_generated_coins(BLK), \ @@ -668,19 +1021,7 @@ inline bool do_replay_file(const std::string& filename) return 1; \ } -#define GENERATE_AND_PLAY(genclass) \ - if (list_tests) \ - std::cout << #genclass << std::endl; \ - else if (filter.empty() || boost::regex_match(std::string(#genclass), match, boost::regex(filter))) \ - { \ - std::vector<test_event_entry> events; \ - ++tests_count; \ - bool generated = false; \ - try \ - { \ - genclass g; \ - generated = g.generate(events);; \ - } \ +#define CATCH_REPLAY(genclass) \ catch (const std::exception& ex) \ { \ MERROR(#genclass << " generation failed: what=" << ex.what()); \ @@ -688,7 +1029,9 @@ inline bool do_replay_file(const std::string& filename) catch (...) \ { \ MERROR(#genclass << " generation failed: generic exception"); \ - } \ + } + +#define REPLAY_CORE(genclass) \ if (generated && do_replay_events< genclass >(events)) \ { \ MGINFO_GREEN("#TEST# Succeeded " << #genclass); \ @@ -697,7 +1040,54 @@ inline bool do_replay_file(const std::string& filename) { \ MERROR("#TEST# Failed " << #genclass); \ failed_tests.push_back(#genclass); \ + } + +#define REPLAY_WITH_CORE(genclass, CORE) \ + if (generated && replay_events_through_core_validate< genclass >(events, CORE)) \ + { \ + MGINFO_GREEN("#TEST# Succeeded " << #genclass); \ + } \ + else \ + { \ + MERROR("#TEST# Failed " << #genclass); \ + failed_tests.push_back(#genclass); \ + } + +#define CATCH_GENERATE_REPLAY(genclass) \ + CATCH_REPLAY(genclass); \ + REPLAY_CORE(genclass); + +#define CATCH_GENERATE_REPLAY_CORE(genclass, CORE) \ + CATCH_REPLAY(genclass); \ + REPLAY_WITH_CORE(genclass, CORE); + +#define GENERATE_AND_PLAY(genclass) \ + if (list_tests) \ + std::cout << #genclass << std::endl; \ + else if (filter.empty() || boost::regex_match(std::string(#genclass), match, boost::regex(filter))) \ + { \ + std::vector<test_event_entry> events; \ + ++tests_count; \ + bool generated = false; \ + try \ + { \ + genclass g; \ + generated = g.generate(events); \ + } \ + CATCH_GENERATE_REPLAY(genclass); \ + } + +#define GENERATE_AND_PLAY_INSTANCE(genclass, ins, CORE) \ + if (filter.empty() || boost::regex_match(std::string(#genclass), match, boost::regex(filter))) \ + { \ + std::vector<test_event_entry> events; \ + ++tests_count; \ + bool generated = false; \ + try \ + { \ + generated = ins.generate(events); \ } \ + CATCH_GENERATE_REPLAY_CORE(genclass, CORE); \ } #define CALL_TEST(test_name, function) \ diff --git a/tests/core_tests/chaingen001.cpp b/tests/core_tests/chaingen001.cpp index a76cf1592..b313f4821 100644 --- a/tests/core_tests/chaingen001.cpp +++ b/tests/core_tests/chaingen001.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 71b8c4463..cb35cfde2 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 c12e97f95..cf20c725c 100644 --- a/tests/core_tests/chaingen_tests_list.h +++ b/tests/core_tests/chaingen_tests_list.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/double_spend.cpp b/tests/core_tests/double_spend.cpp index c60ea885e..afd212b27 100644 --- a/tests/core_tests/double_spend.cpp +++ b/tests/core_tests/double_spend.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/double_spend.h b/tests/core_tests/double_spend.h index 276e74853..70d9f84b9 100644 --- a/tests/core_tests/double_spend.h +++ b/tests/core_tests/double_spend.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/double_spend.inl b/tests/core_tests/double_spend.inl index d02147065..600899b6f 100644 --- a/tests/core_tests/double_spend.inl +++ b/tests/core_tests/double_spend.inl @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/integer_overflow.cpp b/tests/core_tests/integer_overflow.cpp index e7e1ac9ca..42df4aa3b 100644 --- a/tests/core_tests/integer_overflow.cpp +++ b/tests/core_tests/integer_overflow.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/integer_overflow.h b/tests/core_tests/integer_overflow.h index eaca268e4..75a60eb15 100644 --- a/tests/core_tests/integer_overflow.h +++ b/tests/core_tests/integer_overflow.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/multisig.cpp b/tests/core_tests/multisig.cpp index 37fda6643..e0c90423d 100644 --- a/tests/core_tests/multisig.cpp +++ b/tests/core_tests/multisig.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/multisig.h b/tests/core_tests/multisig.h index e0d227840..1e8226d26 100644 --- a/tests/core_tests/multisig.h +++ b/tests/core_tests/multisig.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp index bb7dd013b..248354177 100644 --- a/tests/core_tests/rct.cpp +++ b/tests/core_tests/rct.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/rct.h b/tests/core_tests/rct.h index 641f3dd5e..72460d98e 100644 --- a/tests/core_tests/rct.h +++ b/tests/core_tests/rct.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 8b2b943cc..eb5ee48ec 100644 --- a/tests/core_tests/ring_signature_1.cpp +++ b/tests/core_tests/ring_signature_1.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 7ff6cfd96..256f5732b 100644 --- a/tests/core_tests/ring_signature_1.h +++ b/tests/core_tests/ring_signature_1.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/transaction_tests.cpp b/tests/core_tests/transaction_tests.cpp index 810dec6fc..14028b5be 100644 --- a/tests/core_tests/transaction_tests.cpp +++ b/tests/core_tests/transaction_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/transaction_tests.h b/tests/core_tests/transaction_tests.h index 9500c66f6..eac2c3479 100644 --- a/tests/core_tests/transaction_tests.h +++ b/tests/core_tests/transaction_tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/tx_validation.cpp b/tests/core_tests/tx_validation.cpp index f0a385ee5..232c86482 100644 --- a/tests/core_tests/tx_validation.cpp +++ b/tests/core_tests/tx_validation.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/tx_validation.h b/tests/core_tests/tx_validation.h index 01a258a20..8d457cdb5 100644 --- a/tests/core_tests/tx_validation.h +++ b/tests/core_tests/tx_validation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/v2_tests.cpp b/tests/core_tests/v2_tests.cpp index b96c744b3..8c2b51acf 100644 --- a/tests/core_tests/v2_tests.cpp +++ b/tests/core_tests/v2_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/v2_tests.h b/tests/core_tests/v2_tests.h index cd2bcb839..71b0f0e83 100644 --- a/tests/core_tests/v2_tests.h +++ b/tests/core_tests/v2_tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/core_tests/wallet_tools.cpp b/tests/core_tests/wallet_tools.cpp new file mode 100644 index 000000000..616774d18 --- /dev/null +++ b/tests/core_tests/wallet_tools.cpp @@ -0,0 +1,286 @@ +// +// Created by Dusan Klinec on 2019-02-28. +// + +#include "wallet_tools.h" +#include <random> + +using namespace std; +using namespace epee; +using namespace crypto; +using namespace cryptonote; + +// Shared random generator +static std::default_random_engine RND(crypto::rand<unsigned>()); + +void wallet_accessor_test::set_account(tools::wallet2 * wallet, cryptonote::account_base& account) +{ + wallet->clear(); + wallet->m_account = account; + + wallet->m_key_device_type = account.get_device().get_type(); + wallet->m_account_public_address = account.get_keys().m_account_address; + wallet->m_watch_only = false; + wallet->m_multisig = false; + wallet->m_multisig_threshold = 0; + wallet->m_multisig_signers.clear(); + wallet->m_device_name = account.get_device().get_name(); + + wallet->m_subaddress_lookahead_major = 5; + wallet->m_subaddress_lookahead_minor = 20; + + wallet->setup_new_blockchain(); // generates also subadress register +} + +void wallet_accessor_test::process_parsed_blocks(tools::wallet2 * wallet, uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<tools::wallet2::parsed_block> &parsed_blocks, uint64_t& blocks_added) +{ + wallet->process_parsed_blocks(start_height, blocks, parsed_blocks, blocks_added); +} + +void wallet_tools::process_transactions(tools::wallet2 * wallet, const std::vector<test_event_entry>& events, const cryptonote::block& blk_head, block_tracker &bt, const boost::optional<crypto::hash>& blk_tail) +{ + map_hash2tx_t mtx; + std::vector<const cryptonote::block*> blockchain; + find_block_chain(events, blockchain, mtx, get_block_hash(blk_head)); + + if (blk_tail){ + trim_block_chain(blockchain, blk_tail.get()); + } + + process_transactions(wallet, blockchain, mtx, bt); +} + +void wallet_tools::process_transactions(tools::wallet2 * wallet, const std::vector<const cryptonote::block*>& blockchain, const map_hash2tx_t & mtx, block_tracker &bt) +{ + uint64_t start_height=0, blocks_added=0; + std::vector<cryptonote::block_complete_entry> v_bche; + std::vector<tools::wallet2::parsed_block> v_parsed_block; + + v_bche.reserve(blockchain.size()); + v_parsed_block.reserve(blockchain.size()); + + size_t idx = 0; + for(auto bl : blockchain) + { + idx += 1; + uint64_t height; + v_bche.emplace_back(); + v_parsed_block.emplace_back(); + + wallet_tools::gen_block_data(bt, bl, mtx, v_bche.back(), v_parsed_block.back(), idx == 1 ? start_height : height); + } + + if (wallet) + wallet_accessor_test::process_parsed_blocks(wallet, start_height, v_bche, v_parsed_block, blocks_added); +} + +bool wallet_tools::fill_tx_sources(tools::wallet2 * wallet, std::vector<cryptonote::tx_source_entry>& sources, size_t mixin, const boost::optional<size_t>& num_utxo, const boost::optional<uint64_t>& min_amount, block_tracker &bt, std::vector<size_t> &selected, uint64_t cur_height, ssize_t offset, int step, const boost::optional<fnc_accept_tx_source_t>& fnc_accept) +{ + CHECK_AND_ASSERT_THROW_MES(step != 0, "Step is zero"); + sources.clear(); + + auto & transfers = wallet_accessor_test::get_transfers(wallet); + std::unordered_set<size_t> selected_idx; + std::unordered_set<crypto::key_image> selected_kis; + const size_t ntrans = wallet->get_num_transfer_details(); + size_t roffset = offset >= 0 ? offset : ntrans - offset - 1; + size_t iters = 0; + uint64_t sum = 0; + size_t cur_utxo = 0; + bool abort = false; + unsigned brk_cond = 0; + unsigned brk_thresh = num_utxo && min_amount ? 2 : (num_utxo || min_amount ? 1 : 0); + +#define EVAL_BRK_COND() do { \ + brk_cond = 0; \ + if (num_utxo && num_utxo.get() <= cur_utxo) \ + brk_cond += 1; \ + if (min_amount && min_amount.get() <= sum) \ + brk_cond += 1; \ + } while(0) + + for(ssize_t i = roffset; iters < ntrans && !abort; i += step, ++iters) + { + EVAL_BRK_COND(); + if (brk_cond >= brk_thresh) + break; + + i = i < 0 ? (i + ntrans) : i % ntrans; + auto & td = transfers[i]; + if (td.m_spent) + continue; + if (td.m_block_height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW > cur_height) + continue; + if (selected_idx.find((size_t)i) != selected_idx.end()){ + MERROR("Should not happen (selected_idx not found): " << i); + continue; + } + if (selected_kis.find(td.m_key_image) != selected_kis.end()){ + MERROR("Should not happen (selected KI): " << i << "ki: " << dump_keys(td.m_key_image.data)); + continue; + } + + try { + cryptonote::tx_source_entry src; + wallet_tools::gen_tx_src(mixin, cur_height, td, src, bt); + + // Acceptor function + if (fnc_accept){ + tx_source_info_crate_t c_info{.td=&td, .src=&src, .selected_idx=&selected_idx, .selected_kis=&selected_kis, + .ntrans=ntrans, .iters=iters, .sum=sum, .cur_utxo=cur_utxo}; + + bool take_it = (fnc_accept.get())(c_info, abort); + if (!take_it){ + continue; + } + } + + MINFO("Selected " << i << " from tx: " << dump_keys(td.m_txid.data) + << " ki: " << dump_keys(td.m_key_image.data) + << " amnt: " << td.amount() + << " rct: " << td.is_rct() + << " glob: " << td.m_global_output_index); + + sum += td.amount(); + cur_utxo += 1; + + sources.emplace_back(src); + selected.push_back((size_t)i); + selected_idx.insert((size_t)i); + selected_kis.insert(td.m_key_image); + + } catch(const std::exception &e){ + MTRACE("Output " << i << ", from: " << dump_keys(td.m_txid.data) + << ", amnt: " << td.amount() << ", rct: " << td.is_rct() + << ", glob: " << td.m_global_output_index << " is not applicable: " << e.what()); + } + } + + EVAL_BRK_COND(); + return brk_cond >= brk_thresh; +#undef EVAL_BRK_COND +} + +void wallet_tools::gen_tx_src(size_t mixin, uint64_t cur_height, const tools::wallet2::transfer_details & td, cryptonote::tx_source_entry & src, block_tracker &bt) +{ + src.amount = td.amount(); + src.rct = td.is_rct(); + + std::vector<tools::wallet2::get_outs_entry> outs; + bt.get_fake_outs(mixin, td.is_rct() ? 0 : td.amount(), td.m_global_output_index, cur_height, outs); + + for (size_t n = 0; n < mixin; ++n) + { + cryptonote::tx_source_entry::output_entry oe; + oe.first = std::get<0>(outs[n]); + oe.second.dest = rct::pk2rct(std::get<1>(outs[n])); + oe.second.mask = std::get<2>(outs[n]); + src.outputs.push_back(oe); + } + + size_t real_idx = crypto::rand<size_t>() % mixin; + + cryptonote::tx_source_entry::output_entry &real_oe = src.outputs[real_idx]; + real_oe.first = td.m_global_output_index; + real_oe.second.dest = rct::pk2rct(boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key); + real_oe.second.mask = rct::commit(td.amount(), td.m_mask); + + std::sort(src.outputs.begin(), src.outputs.end(), [&](const cryptonote::tx_source_entry::output_entry i0, const cryptonote::tx_source_entry::output_entry i1) { + return i0.first < i1.first; + }); + + for (size_t i = 0; i < src.outputs.size(); ++i){ + if (src.outputs[i].first == td.m_global_output_index){ + src.real_output = i; + break; + } + } + + src.mask = td.m_mask; + src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx, td.m_pk_index); + src.real_out_additional_tx_keys = get_additional_tx_pub_keys_from_extra(td.m_tx); + src.real_output_in_tx_index = td.m_internal_output_index; + src.multisig_kLRki = rct::multisig_kLRki({rct::zero(), rct::zero(), rct::zero(), rct::zero()}); +} + +void wallet_tools::gen_block_data(block_tracker &bt, const cryptonote::block *bl, const map_hash2tx_t &mtx, cryptonote::block_complete_entry &bche, tools::wallet2::parsed_block &parsed_block, uint64_t &height) +{ + vector<const transaction*> vtx; + vtx.push_back(&(bl->miner_tx)); + height = boost::get<txin_gen>(*bl->miner_tx.vin.begin()).height; + + BOOST_FOREACH(const crypto::hash &h, bl->tx_hashes) { + const map_hash2tx_t::const_iterator cit = mtx.find(h); + CHECK_AND_ASSERT_THROW_MES(mtx.end() != cit, "block contains an unknown tx hash @ " << height << ", " << h); + vtx.push_back(cit->second); + } + + bche.block = "NA"; + bche.txs.resize(bl->tx_hashes.size()); + + parsed_block.error = false; + parsed_block.hash = get_block_hash(*bl); + parsed_block.block = *bl; + parsed_block.txes.reserve(bl->tx_hashes.size()); + + auto & o_indices = parsed_block.o_indices.indices; + o_indices.reserve(bl->tx_hashes.size() + 1); + + size_t cur = 0; + BOOST_FOREACH(const transaction *tx, vtx){ + cur += 1; + o_indices.emplace_back(); + bt.process(bl, tx, cur - 1); + bt.global_indices(tx, o_indices.back().indices); + + if (cur > 1) // miner not included + parsed_block.txes.push_back(*tx); + } +} + +void wallet_tools::compute_subaddresses(std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddresses, cryptonote::account_base & creds, size_t account, size_t minors) +{ + auto &hwdev = hw::get_device("default"); + const std::vector<crypto::public_key> pkeys = hwdev.get_subaddress_spend_public_keys(creds.get_keys(), account, 0, minors); + + for(uint32_t c = 0; c < pkeys.size(); ++c){ + cryptonote::subaddress_index sidx{(uint32_t)account, c}; + subaddresses[pkeys[c]] = sidx; + } +} + +cryptonote::account_public_address get_address(const tools::wallet2* inp) +{ + return (inp)->get_account().get_keys().m_account_address; +} + +bool construct_tx_to_key(cryptonote::transaction& tx, + tools::wallet2 * sender_wallet, const var_addr_t& to, uint64_t amount, + std::vector<cryptonote::tx_source_entry> &sources, + uint64_t fee, bool rct, rct::RangeProofType range_proof_type, int bp_version) +{ + vector<tx_destination_entry> destinations; + fill_tx_destinations(sender_wallet->get_account(), get_address(to), amount, fee, sources, destinations, rct); + return construct_tx_rct(sender_wallet, sources, destinations, get_address(sender_wallet), std::vector<uint8_t>(), tx, 0, rct, range_proof_type, bp_version); +} + +bool construct_tx_to_key(cryptonote::transaction& tx, + tools::wallet2 * sender_wallet, + const std::vector<cryptonote::tx_destination_entry>& destinations, + std::vector<cryptonote::tx_source_entry> &sources, + uint64_t fee, bool rct, rct::RangeProofType range_proof_type, int bp_version) +{ + vector<tx_destination_entry> all_destinations; + fill_tx_destinations(sender_wallet->get_account(), destinations, fee, sources, all_destinations, rct); + return construct_tx_rct(sender_wallet, sources, all_destinations, get_address(sender_wallet), std::vector<uint8_t>(), tx, 0, rct, range_proof_type, bp_version); +} + +bool construct_tx_rct(tools::wallet2 * sender_wallet, std::vector<cryptonote::tx_source_entry>& sources, const std::vector<cryptonote::tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, cryptonote::transaction& tx, uint64_t unlock_time, bool rct, rct::RangeProofType range_proof_type, int bp_version) +{ + subaddresses_t & subaddresses = wallet_accessor_test::get_subaddresses(sender_wallet); + crypto::secret_key tx_key; + std::vector<crypto::secret_key> additional_tx_keys; + std::vector<tx_destination_entry> destinations_copy = destinations; + rct::RCTConfig rct_config = {range_proof_type, bp_version}; + return construct_tx_and_get_tx_key(sender_wallet->get_account().get_keys(), subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, rct_config, nullptr); +} diff --git a/tests/core_tests/wallet_tools.h b/tests/core_tests/wallet_tools.h new file mode 100644 index 000000000..03db04c99 --- /dev/null +++ b/tests/core_tests/wallet_tools.h @@ -0,0 +1,86 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include "chaingen.h" +#include "wallet/wallet2.h" + +typedef struct { + tools::wallet2::transfer_details * td; + cryptonote::tx_source_entry * src; + + std::unordered_set<size_t> * selected_idx; + std::unordered_set<crypto::key_image> * selected_kis; + size_t ntrans; + size_t iters; + uint64_t sum; + size_t cur_utxo; +} tx_source_info_crate_t; + +typedef std::function<bool(const tx_source_info_crate_t &info, bool &abort)> fnc_accept_tx_source_t; + +// Wallet friend, direct access to required fields and private methods +class wallet_accessor_test +{ +public: + static void set_account(tools::wallet2 * wallet, cryptonote::account_base& account); + static tools::wallet2::transfer_container & get_transfers(tools::wallet2 * wallet) { return wallet->m_transfers; } + static subaddresses_t & get_subaddresses(tools::wallet2 * wallet) { return wallet->m_subaddresses; } + static void process_parsed_blocks(tools::wallet2 * wallet, uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<tools::wallet2::parsed_block> &parsed_blocks, uint64_t& blocks_added); +}; + +class wallet_tools +{ +public: + static void gen_tx_src(size_t mixin, uint64_t cur_height, const tools::wallet2::transfer_details & td, cryptonote::tx_source_entry & src, block_tracker &bt); + static void gen_block_data(block_tracker &bt, const cryptonote::block *bl, const map_hash2tx_t & mtx, cryptonote::block_complete_entry &bche, tools::wallet2::parsed_block &parsed_block, uint64_t &height); + static void compute_subaddresses(std::unordered_map<crypto::public_key, cryptonote::subaddress_index> &subaddresses, cryptonote::account_base & creds, size_t account, size_t minors); + static void process_transactions(tools::wallet2 * wallet, const std::vector<test_event_entry>& events, const cryptonote::block& blk_head, block_tracker &bt, const boost::optional<crypto::hash>& blk_tail=boost::none); + static void process_transactions(tools::wallet2 * wallet, const std::vector<const cryptonote::block*>& blockchain, const map_hash2tx_t & mtx, block_tracker &bt); + static bool fill_tx_sources(tools::wallet2 * wallet, std::vector<cryptonote::tx_source_entry>& sources, size_t mixin, const boost::optional<size_t>& num_utxo, const boost::optional<uint64_t>& min_amount, block_tracker &bt, std::vector<size_t> &selected, uint64_t cur_height, ssize_t offset=0, int step=1, const boost::optional<fnc_accept_tx_source_t>& fnc_accept=boost::none); +}; + +cryptonote::account_public_address get_address(const tools::wallet2*); + +bool construct_tx_to_key(cryptonote::transaction& tx, tools::wallet2 * from_wallet, const var_addr_t& to, uint64_t amount, + std::vector<cryptonote::tx_source_entry> &sources, + uint64_t fee, bool rct=false, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0); + +bool construct_tx_to_key(cryptonote::transaction& tx, tools::wallet2 * sender_wallet, const std::vector<cryptonote::tx_destination_entry>& destinations, + std::vector<cryptonote::tx_source_entry> &sources, + uint64_t fee, bool rct, rct::RangeProofType range_proof_type, int bp_version = 0); + +bool construct_tx_rct(tools::wallet2 * sender_wallet, + std::vector<cryptonote::tx_source_entry>& sources, + const std::vector<cryptonote::tx_destination_entry>& destinations, + const boost::optional<cryptonote::account_public_address>& change_addr, + std::vector<uint8_t> extra, cryptonote::transaction& tx, uint64_t unlock_time, + bool rct=false, rct::RangeProofType range_proof_type=rct::RangeProofBorromean, int bp_version = 0); diff --git a/tests/crypto/CMakeLists.txt b/tests/crypto/CMakeLists.txt index 8789cb552..883efb3b0 100644 --- a/tests/crypto/CMakeLists.txt +++ b/tests/crypto/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # @@ -56,7 +56,6 @@ add_test( add_executable(cnv4-jit-tests cnv4-jit.c) target_link_libraries(cnv4-jit-tests PRIVATE - crypto common ${EXTRA_LIBRARIES}) set_property(TARGET cnv4-jit-tests diff --git a/tests/crypto/crypto-ops-data.c b/tests/crypto/crypto-ops-data.c index 9bdd6b88f..63fedff07 100644 --- a/tests/crypto/crypto-ops-data.c +++ b/tests/crypto/crypto-ops-data.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/crypto-ops.c b/tests/crypto/crypto-ops.c index 476de786a..9b1ccfcee 100644 --- a/tests/crypto/crypto-ops.c +++ b/tests/crypto/crypto-ops.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/crypto-tests.h b/tests/crypto/crypto-tests.h index d910a845c..be8d5a2e1 100644 --- a/tests/crypto/crypto-tests.h +++ b/tests/crypto/crypto-tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/crypto.cpp b/tests/crypto/crypto.cpp index 6a1dd1b29..160e0a23f 100644 --- a/tests/crypto/crypto.cpp +++ b/tests/crypto/crypto.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/hash.c b/tests/crypto/hash.c index 6b865abd9..d65f13f11 100644 --- a/tests/crypto/hash.c +++ b/tests/crypto/hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/main.cpp b/tests/crypto/main.cpp index cc3b53b83..3cdef4ce7 100644 --- a/tests/crypto/main.cpp +++ b/tests/crypto/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/crypto/random.c b/tests/crypto/random.c index e9464d457..7a17cbccc 100644 --- a/tests/crypto/random.c +++ b/tests/crypto/random.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/cryptolib.pl b/tests/cryptolib.pl index 3d4cb638b..d26ce601c 100644 --- a/tests/cryptolib.pl +++ b/tests/cryptolib.pl @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/cryptotest.pl b/tests/cryptotest.pl index 13f8db09e..57f6e2c5a 100644 --- a/tests/cryptotest.pl +++ b/tests/cryptotest.pl @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/daemon_tests/CMakeLists.txt b/tests/daemon_tests/CMakeLists.txt index bc1dce2cc..a32f26584 100644 --- a/tests/daemon_tests/CMakeLists.txt +++ b/tests/daemon_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/daemon_tests/transfers.cpp b/tests/daemon_tests/transfers.cpp index 726f11dfb..e20e6b280 100644 --- a/tests/daemon_tests/transfers.cpp +++ b/tests/daemon_tests/transfers.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/difficulty/CMakeLists.txt b/tests/difficulty/CMakeLists.txt index 3cb8af8d5..2ed495806 100644 --- a/tests/difficulty/CMakeLists.txt +++ b/tests/difficulty/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/difficulty/difficulty.cpp b/tests/difficulty/difficulty.cpp index 996913a19..ee20e27e4 100644 --- a/tests/difficulty/difficulty.cpp +++ b/tests/difficulty/difficulty.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/difficulty/generate-data b/tests/difficulty/generate-data index c41ce025d..3a9976e05 100755 --- a/tests/difficulty/generate-data +++ b/tests/difficulty/generate-data @@ -1,6 +1,6 @@ #!/usr/bin/python3 -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/functional_tests/CMakeLists.txt b/tests/functional_tests/CMakeLists.txt index 7a2a7fbd1..2e3519994 100644 --- a/tests/functional_tests/CMakeLists.txt +++ b/tests/functional_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/functional_tests/main.cpp b/tests/functional_tests/main.cpp index b9a686dbc..c6869f755 100644 --- a/tests/functional_tests/main.cpp +++ b/tests/functional_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp index 7e5b73415..32b601d7a 100644 --- a/tests/functional_tests/transactions_flow_test.cpp +++ b/tests/functional_tests/transactions_flow_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 78135b9c0..530400df8 100644 --- a/tests/functional_tests/transactions_flow_test.h +++ b/tests/functional_tests/transactions_flow_test.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 92dc00f2d..b9c43e0e9 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-2018, The Monero Project +// Copyright (c) 2014-2019, 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 b684f7c7a..cb5f94f80 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/CMakeLists.txt b/tests/fuzz/CMakeLists.txt index fdb745699..a6ef139f5 100644 --- a/tests/fuzz/CMakeLists.txt +++ b/tests/fuzz/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/fuzz/base58.cpp b/tests/fuzz/base58.cpp index a4857bdd1..5f909a5d9 100644 --- a/tests/fuzz/base58.cpp +++ b/tests/fuzz/base58.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/block.cpp b/tests/fuzz/block.cpp index eed3b94b2..850c58890 100644 --- a/tests/fuzz/block.cpp +++ b/tests/fuzz/block.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/bulletproof.cpp b/tests/fuzz/bulletproof.cpp index 2f3a2f8d1..e9a6ded7d 100644 --- a/tests/fuzz/bulletproof.cpp +++ b/tests/fuzz/bulletproof.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/cold-outputs.cpp b/tests/fuzz/cold-outputs.cpp index 6300cfd11..0a034bcd5 100644 --- a/tests/fuzz/cold-outputs.cpp +++ b/tests/fuzz/cold-outputs.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/cold-transaction.cpp b/tests/fuzz/cold-transaction.cpp index 83b9d5267..fc0cfa2bd 100644 --- a/tests/fuzz/cold-transaction.cpp +++ b/tests/fuzz/cold-transaction.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/fuzzer.cpp b/tests/fuzz/fuzzer.cpp index ab14e2b79..24db5ee05 100644 --- a/tests/fuzz/fuzzer.cpp +++ b/tests/fuzz/fuzzer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/fuzzer.h b/tests/fuzz/fuzzer.h index 2bf01914a..5cbd1abc2 100644 --- a/tests/fuzz/fuzzer.h +++ b/tests/fuzz/fuzzer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/http-client.cpp b/tests/fuzz/http-client.cpp index 909325832..1750838ae 100644 --- a/tests/fuzz/http-client.cpp +++ b/tests/fuzz/http-client.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/levin.cpp b/tests/fuzz/levin.cpp index 573abd1d3..fe9ef418e 100644 --- a/tests/fuzz/levin.cpp +++ b/tests/fuzz/levin.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/load_from_binary.cpp b/tests/fuzz/load_from_binary.cpp index 89f122902..85b7361e5 100644 --- a/tests/fuzz/load_from_binary.cpp +++ b/tests/fuzz/load_from_binary.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/load_from_json.cpp b/tests/fuzz/load_from_json.cpp index 083555f7e..3ba98050b 100644 --- a/tests/fuzz/load_from_json.cpp +++ b/tests/fuzz/load_from_json.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/parse_url.cpp b/tests/fuzz/parse_url.cpp index fb5754a70..3db78f9d9 100644 --- a/tests/fuzz/parse_url.cpp +++ b/tests/fuzz/parse_url.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/signature.cpp b/tests/fuzz/signature.cpp index f94698e2d..3f0ada0c9 100644 --- a/tests/fuzz/signature.cpp +++ b/tests/fuzz/signature.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/fuzz/transaction.cpp b/tests/fuzz/transaction.cpp index 934bd4685..0f62888a1 100644 --- a/tests/fuzz/transaction.cpp +++ b/tests/fuzz/transaction.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/gtest/include/gtest/internal/gtest-port-arch.h b/tests/gtest/include/gtest/internal/gtest-port-arch.h index 74ab94905..83022c82c 100644 --- a/tests/gtest/include/gtest/internal/gtest-port-arch.h +++ b/tests/gtest/include/gtest/internal/gtest-port-arch.h @@ -88,6 +88,8 @@ # define GTEST_OS_OPENBSD 1 #elif defined __QNX__ # define GTEST_OS_QNX 1 +#elif defined(__NetBSD__) +# define GTEST_OS_NETBSD 1 #endif // __CYGWIN__ #endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_ diff --git a/tests/gtest/include/gtest/internal/gtest-port.h b/tests/gtest/include/gtest/internal/gtest-port.h index cb0639b4b..2896dfe64 100644 --- a/tests/gtest/include/gtest/internal/gtest-port.h +++ b/tests/gtest/include/gtest/internal/gtest-port.h @@ -790,7 +790,8 @@ using ::std::tuple_size; (GTEST_OS_MAC && !GTEST_OS_IOS) || \ (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) || \ GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX || GTEST_OS_HPUX || \ - GTEST_OS_OPENBSD || GTEST_OS_QNX || GTEST_OS_FREEBSD) + GTEST_OS_OPENBSD || GTEST_OS_QNX || GTEST_OS_FREEBSD || \ + GTEST_OS_NETBSD) # define GTEST_HAS_DEATH_TEST 1 #endif diff --git a/tests/hash-target.cpp b/tests/hash-target.cpp index 2737ae30e..70368ce24 100644 --- a/tests/hash-target.cpp +++ b/tests/hash-target.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/hash/CMakeLists.txt b/tests/hash/CMakeLists.txt index 105cf2c13..a0c78bfdc 100644 --- a/tests/hash/CMakeLists.txt +++ b/tests/hash/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/hash/main.cpp b/tests/hash/main.cpp index acfd99e96..adf1bd9c4 100644 --- a/tests/hash/main.cpp +++ b/tests/hash/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/io.h b/tests/io.h index ec5a9ee60..0f65ed297 100644 --- a/tests/io.h +++ b/tests/io.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/libwallet_api_tests/CMakeLists.txt b/tests/libwallet_api_tests/CMakeLists.txt index 1a9cbc5a6..cb1d563b0 100644 --- a/tests/libwallet_api_tests/CMakeLists.txt +++ b/tests/libwallet_api_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/libwallet_api_tests/main.cpp b/tests/libwallet_api_tests/main.cpp index fe31541df..02faae50d 100644 --- a/tests/libwallet_api_tests/main.cpp +++ b/tests/libwallet_api_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/net_load_tests/CMakeLists.txt b/tests/net_load_tests/CMakeLists.txt index 1407bbf70..40b5561a7 100644 --- a/tests/net_load_tests/CMakeLists.txt +++ b/tests/net_load_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/net_load_tests/clt.cpp b/tests/net_load_tests/clt.cpp index 77cce2be7..fc2280f23 100644 --- a/tests/net_load_tests/clt.cpp +++ b/tests/net_load_tests/clt.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 6c4f7cb76..cdc2d267a 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/net_load_tests/srv.cpp b/tests/net_load_tests/srv.cpp index 092c6955c..fe32ec5cb 100644 --- a/tests/net_load_tests/srv.cpp +++ b/tests/net_load_tests/srv.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/CMakeLists.txt b/tests/performance_tests/CMakeLists.txt index 5cd054d86..aa424da80 100644 --- a/tests/performance_tests/CMakeLists.txt +++ b/tests/performance_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/tests/performance_tests/bulletproof.h b/tests/performance_tests/bulletproof.h index 7bb702c3d..7136fbbfe 100644 --- a/tests/performance_tests/bulletproof.h +++ b/tests/performance_tests/bulletproof.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2019, 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 755d8f941..329714c56 100644 --- a/tests/performance_tests/check_tx_signature.h +++ b/tests/performance_tests/check_tx_signature.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 2ddaef269..63b2c5bee 100644 --- a/tests/performance_tests/cn_fast_hash.h +++ b/tests/performance_tests/cn_fast_hash.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 79ebb8778..4f410d62d 100644 --- a/tests/performance_tests/cn_slow_hash.h +++ b/tests/performance_tests/cn_slow_hash.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/construct_tx.h b/tests/performance_tests/construct_tx.h index 71d42073d..ebfe46edf 100644 --- a/tests/performance_tests/construct_tx.h +++ b/tests/performance_tests/construct_tx.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/crypto_ops.h b/tests/performance_tests/crypto_ops.h index 3ebb6f470..5b215cef4 100644 --- a/tests/performance_tests/crypto_ops.h +++ b/tests/performance_tests/crypto_ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2019, 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 35c4ff062..ad0b3b55f 100644 --- a/tests/performance_tests/derive_public_key.h +++ b/tests/performance_tests/derive_public_key.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 c478a2a99..ba01b57c0 100644 --- a/tests/performance_tests/derive_secret_key.h +++ b/tests/performance_tests/derive_secret_key.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/equality.h b/tests/performance_tests/equality.h index 8d24d7da7..5788ebec8 100644 --- a/tests/performance_tests/equality.h +++ b/tests/performance_tests/equality.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/ge_frombytes_vartime.h b/tests/performance_tests/ge_frombytes_vartime.h index 3f7d55182..1a0601c74 100644 --- a/tests/performance_tests/ge_frombytes_vartime.h +++ b/tests/performance_tests/ge_frombytes_vartime.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/ge_tobytes.h b/tests/performance_tests/ge_tobytes.h index 3d46f4838..66e6fce38 100644 --- a/tests/performance_tests/ge_tobytes.h +++ b/tests/performance_tests/ge_tobytes.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 3f6238265..1f0066589 100644 --- a/tests/performance_tests/generate_key_derivation.h +++ b/tests/performance_tests/generate_key_derivation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 5155e64a5..df39d7088 100644 --- a/tests/performance_tests/generate_key_image.h +++ b/tests/performance_tests/generate_key_image.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 ede48b6ca..d4d83c07d 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/generate_keypair.h b/tests/performance_tests/generate_keypair.h index 91c830166..cf28f7f58 100644 --- a/tests/performance_tests/generate_keypair.h +++ b/tests/performance_tests/generate_keypair.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 9f81995ac..8b4516843 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 22a86cb59..e6558a364 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 ae9d6ea7a..bdbbee37d 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/performance_tests.h b/tests/performance_tests/performance_tests.h index 17d16b0f6..606c5980f 100644 --- a/tests/performance_tests/performance_tests.h +++ b/tests/performance_tests/performance_tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/performance_utils.h b/tests/performance_tests/performance_utils.h index e46a7bce4..8a45bea90 100644 --- a/tests/performance_tests/performance_utils.h +++ b/tests/performance_tests/performance_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -40,7 +40,7 @@ void set_process_affinity(int core) { -#if defined (__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__sun) +#if defined (__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__sun) return; #elif defined(BOOST_WINDOWS) DWORD_PTR mask = 1; @@ -62,7 +62,7 @@ void set_process_affinity(int core) void set_thread_high_priority() { -#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__sun) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(_NetBSD_) || defined(__sun) return; #elif defined(BOOST_WINDOWS) ::SetPriorityClass(::GetCurrentProcess(), HIGH_PRIORITY_CLASS); diff --git a/tests/performance_tests/range_proof.h b/tests/performance_tests/range_proof.h index 0ce962cd9..548237814 100644 --- a/tests/performance_tests/range_proof.h +++ b/tests/performance_tests/range_proof.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/rct_mlsag.h b/tests/performance_tests/rct_mlsag.h index 888f4b260..0141710f7 100644 --- a/tests/performance_tests/rct_mlsag.h +++ b/tests/performance_tests/rct_mlsag.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/sc_reduce32.h b/tests/performance_tests/sc_reduce32.h index fc5606414..8db1ca70a 100644 --- a/tests/performance_tests/sc_reduce32.h +++ b/tests/performance_tests/sc_reduce32.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/signature.h b/tests/performance_tests/signature.h index 21110b525..63c63ea46 100644 --- a/tests/performance_tests/signature.h +++ b/tests/performance_tests/signature.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 365d25444..52fbf5f80 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/performance_tests/subaddress_expand.h b/tests/performance_tests/subaddress_expand.h index 2a13ff5c2..61fcb41f3 100644 --- a/tests/performance_tests/subaddress_expand.h +++ b/tests/performance_tests/subaddress_expand.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/trezor/CMakeLists.txt b/tests/trezor/CMakeLists.txt new file mode 100644 index 000000000..15ed5668d --- /dev/null +++ b/tests/trezor/CMakeLists.txt @@ -0,0 +1,76 @@ +# Copyright (c) 2014-2018, The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +set(trezor_tests_sources + tools.cpp + daemon.cpp + trezor_tests.cpp + ../core_tests/chaingen.cpp + ../core_tests/wallet_tools.cpp) + +set(trezor_tests_headers + tools.h + daemon.h + trezor_tests.h + ../core_tests/chaingen.h + ../core_tests/wallet_tools.h) + +add_executable(trezor_tests + ${trezor_tests_sources} + ${trezor_tests_headers}) + +target_link_libraries(trezor_tests + PRIVATE + multisig + cryptonote_core + p2p + version + epee + device + device_trezor + wallet + wallet_api + rpc + cryptonote_protocol + daemon_rpc_server + ${Boost_CHRONO_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_SYSTEM_LIBRARY} + ${ZMQ_LIB} + ${CMAKE_THREAD_LIBS_INIT} + ${EXTRA_LIBRARIES}) + +enable_stack_trace(trezor_tests) +set_property(TARGET trezor_tests + PROPERTY + FOLDER "tests") + +add_test( + NAME trezor_tests + COMMAND trezor_tests) diff --git a/tests/trezor/daemon.cpp b/tests/trezor/daemon.cpp new file mode 100644 index 000000000..5e987793a --- /dev/null +++ b/tests/trezor/daemon.cpp @@ -0,0 +1,368 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "daemon.h" +#include <common/command_line.h> + +using namespace std; +using namespace daemonize; +namespace po = boost::program_options; + +bool mock_rpc_daemon::on_send_raw_tx_2(const cryptonote::COMMAND_RPC_SEND_RAW_TX::request& req, cryptonote::COMMAND_RPC_SEND_RAW_TX::response& res, const cryptonote::core_rpc_server::connection_context *ctx) +{ + cryptonote::COMMAND_RPC_SEND_RAW_TX::request req2(req); + req2.do_not_relay = true; // Do not relay in test setup, only one daemon running. + return cryptonote::core_rpc_server::on_send_raw_tx(req2, res, ctx); +} + +void mock_daemon::init_options(boost::program_options::options_description & option_spec) +{ + cryptonote::core::init_options(option_spec); + t_node_server::init_options(option_spec); + cryptonote::core_rpc_server::init_options(option_spec); + + command_line::add_arg(option_spec, daemon_args::arg_zmq_rpc_bind_ip); + command_line::add_arg(option_spec, daemon_args::arg_zmq_rpc_bind_port); +} + +void mock_daemon::default_options(boost::program_options::variables_map & vm) +{ + std::vector<std::string> exclusive_nodes{"127.0.0.1:65525"}; + tools::options::set_option(vm, nodetool::arg_p2p_add_exclusive_node, po::variable_value(exclusive_nodes, false)); + + tools::options::set_option(vm, nodetool::arg_p2p_bind_ip, po::variable_value(std::string("127.0.0.1"), false)); + tools::options::set_option(vm, nodetool::arg_no_igd, po::variable_value(true, false)); + tools::options::set_option(vm, cryptonote::arg_offline, po::variable_value(true, false)); + tools::options::set_option(vm, "disable-dns-checkpoints", po::variable_value(true, false)); + + const char *test_mainnet = getenv("TEST_MAINNET"); + if (!test_mainnet || atoi(test_mainnet) == 0) + { + tools::options::set_option(vm, cryptonote::arg_testnet_on, po::variable_value(true, false)); + } + + // By default pick non-standard ports to avoid confusion with possibly locally running daemons (mainnet/testnet) + const char *test_p2p_port = getenv("TEST_P2P_PORT"); + auto p2p_port = std::string(test_p2p_port && strlen(test_p2p_port) > 0 ? test_p2p_port : "61340"); + tools::options::set_option(vm, nodetool::arg_p2p_bind_port, po::variable_value(p2p_port, false)); + + const char *test_rpc_port = getenv("TEST_RPC_PORT"); + auto rpc_port = std::string(test_rpc_port && strlen(test_rpc_port) > 0 ? test_rpc_port : "61341"); + tools::options::set_option(vm, cryptonote::core_rpc_server::arg_rpc_bind_port, po::variable_value(rpc_port, false)); + + const char *test_zmq_port = getenv("TEST_ZMQ_PORT"); + auto zmq_port = std::string(test_zmq_port && strlen(test_zmq_port) > 0 ? test_zmq_port : "61342"); + tools::options::set_option(vm, daemon_args::arg_zmq_rpc_bind_port, po::variable_value(zmq_port, false)); + + po::notify(vm); +} + +void mock_daemon::set_ports(boost::program_options::variables_map & vm, unsigned initial_port) +{ + CHECK_AND_ASSERT_THROW_MES(initial_port < 65535-2, "Invalid port number"); + tools::options::set_option(vm, nodetool::arg_p2p_bind_port, po::variable_value(std::to_string(initial_port), false)); + tools::options::set_option(vm, cryptonote::core_rpc_server::arg_rpc_bind_port, po::variable_value(std::to_string(initial_port + 1), false)); + tools::options::set_option(vm, daemon_args::arg_zmq_rpc_bind_port, po::variable_value(std::to_string(initial_port + 2), false)); + po::notify(vm); +} + +void mock_daemon::load_params(boost::program_options::variables_map const & vm) +{ + m_p2p_bind_port = command_line::get_arg(vm, nodetool::arg_p2p_bind_port); + m_rpc_bind_port = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_rpc_bind_port); + m_zmq_bind_port = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_bind_port); + m_network_type = command_line::get_arg(vm, cryptonote::arg_testnet_on) ? cryptonote::TESTNET : cryptonote::MAINNET; +} + +mock_daemon::~mock_daemon() +{ + if (!m_terminated) + { + try + { + stop(); + } + catch (...) + { + MERROR("Failed to stop"); + } + } + + if (!m_deinitalized) + { + deinit(); + } +} + +void mock_daemon::init() +{ + m_deinitalized = false; + const auto main_rpc_port = command_line::get_arg(m_vm, cryptonote::core_rpc_server::arg_rpc_bind_port); + m_rpc_server.nettype(m_network_type); + + CHECK_AND_ASSERT_THROW_MES(m_protocol.init(m_vm), "Failed to initialize cryptonote protocol."); + CHECK_AND_ASSERT_THROW_MES(m_rpc_server.init(m_vm, false, main_rpc_port), "Failed to initialize RPC server."); + + if (m_start_p2p) + CHECK_AND_ASSERT_THROW_MES(m_server.init(m_vm), "Failed to initialize p2p server."); + + if(m_http_client.is_connected()) + m_http_client.disconnect(); + + CHECK_AND_ASSERT_THROW_MES(m_http_client.set_server(rpc_addr(), boost::none), "RPC client init fail"); +} + +void mock_daemon::deinit() +{ + try + { + m_rpc_server.deinit(); + } + catch (...) + { + MERROR("Failed to deinitialize RPC server..."); + } + + if (m_start_p2p) + { + try + { + m_server.deinit(); + } + catch (...) + { + MERROR("Failed to deinitialize p2p..."); + } + } + + try + { + m_protocol.deinit(); + m_protocol.set_p2p_endpoint(nullptr); + } + catch (...) + { + MERROR("Failed to stop cryptonote protocol!"); + } + + m_deinitalized = true; +} + +void mock_daemon::init_and_run() +{ + init(); + run(); +} + +void mock_daemon::stop_and_deinit() +{ + stop(); + deinit(); +} + +void mock_daemon::try_init_and_run(boost::optional<unsigned> initial_port) +{ + const unsigned max_attempts = 3; + for(unsigned attempts=0; attempts < max_attempts; ++attempts) + { + if (initial_port) + { + set_ports(m_vm, initial_port.get()); + load_params(m_vm); + MDEBUG("Ports changed, RPC: " << rpc_addr()); + } + + try + { + init_and_run(); + return; + } + catch(const std::exception &e) + { + MWARNING("Could not init and start, attempt: " << attempts << ", reason: " << e.what()); + if (attempts + 1 >= max_attempts) + { + throw; + } + } + } +} + +void mock_daemon::run() +{ + m_run_thread = boost::thread(boost::bind(&mock_daemon::run_main, this)); +} + +bool mock_daemon::run_main() +{ + CHECK_AND_ASSERT_THROW_MES(!m_terminated, "Can't run stopped daemon"); + CHECK_AND_ASSERT_THROW_MES(!m_start_zmq || m_start_p2p, "ZMQ requires P2P"); + boost::thread stop_thread = boost::thread([this] { + while (!this->m_stopped) + epee::misc_utils::sleep_no_w(100); + this->stop_p2p(); + }); + + epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ + m_stopped = true; + stop_thread.join(); + }); + + try + { + CHECK_AND_ASSERT_THROW_MES(m_rpc_server.run(2, false), "Failed to start RPC"); + cryptonote::rpc::DaemonHandler rpc_daemon_handler(*m_core, m_server); + cryptonote::rpc::ZmqServer zmq_server(rpc_daemon_handler); + + if (m_start_zmq) + { + if (!zmq_server.addTCPSocket("127.0.0.1", m_zmq_bind_port)) + { + MERROR("Failed to add TCP Socket (127.0.0.1:" << m_zmq_bind_port << ") to ZMQ RPC Server"); + + stop_rpc(); + return false; + } + + MINFO("Starting ZMQ server..."); + zmq_server.run(); + + MINFO("ZMQ server started at 127.0.0.1: " << m_zmq_bind_port); + } + + if (m_start_p2p) + { + m_server.run(); // blocks until p2p goes down + } + else + { + while (!this->m_stopped) + epee::misc_utils::sleep_no_w(100); + } + + if (m_start_zmq) + zmq_server.stop(); + + stop_rpc(); + return true; + } + catch (std::exception const & ex) + { + MFATAL("Uncaught exception! " << ex.what()); + return false; + } + catch (...) + { + MFATAL("Uncaught exception!"); + return false; + } +} + +void mock_daemon::stop() +{ + CHECK_AND_ASSERT_THROW_MES(!m_terminated, "Can't stop stopped daemon"); + m_stopped = true; + m_terminated = true; + m_run_thread.join(); +} + +void mock_daemon::stop_rpc() +{ + m_rpc_server.send_stop_signal(); + m_rpc_server.timed_wait_server_stop(5000); +} + +void mock_daemon::stop_p2p() +{ + if (m_start_p2p) + m_server.send_stop_signal(); +} + +void mock_daemon::mine_blocks(size_t num_blocks, const std::string &miner_address) +{ + bool blocks_mined = false; + const uint64_t start_height = get_height(); + const auto mining_timeout = std::chrono::seconds(30); + MDEBUG("Current height before mining: " << start_height); + + start_mining(miner_address); + auto mining_started = std::chrono::system_clock::now(); + + while(true) { + epee::misc_utils::sleep_no_w(100); + const uint64_t cur_height = get_height(); + + if (cur_height - start_height >= num_blocks) + { + MDEBUG("Cur blocks: " << cur_height << " start: " << start_height); + blocks_mined = true; + break; + } + + auto current_time = std::chrono::system_clock::now(); + if (mining_timeout < current_time - mining_started) + { + break; + } + } + + stop_mining(); + CHECK_AND_ASSERT_THROW_MES(blocks_mined, "Mining failed in the time limit"); +} + +constexpr const std::chrono::seconds mock_daemon::rpc_timeout; + +void mock_daemon::start_mining(const std::string &miner_address, uint64_t threads_count, bool do_background_mining, bool ignore_battery) +{ + cryptonote::COMMAND_RPC_START_MINING::request req; + req.miner_address = miner_address; + req.threads_count = threads_count; + req.do_background_mining = do_background_mining; + req.ignore_battery = ignore_battery; + + cryptonote::COMMAND_RPC_START_MINING::response resp; + bool r = epee::net_utils::invoke_http_json("/start_mining", req, resp, m_http_client, rpc_timeout); + CHECK_AND_ASSERT_THROW_MES(r, "RPC error - start mining"); + CHECK_AND_ASSERT_THROW_MES(resp.status != CORE_RPC_STATUS_BUSY, "Daemon busy"); + CHECK_AND_ASSERT_THROW_MES(resp.status == CORE_RPC_STATUS_OK, "Daemon response invalid: " << resp.status); +} + +void mock_daemon::stop_mining() +{ + cryptonote::COMMAND_RPC_STOP_MINING::request req; + cryptonote::COMMAND_RPC_STOP_MINING::response resp; + bool r = epee::net_utils::invoke_http_json("/stop_mining", req, resp, m_http_client, rpc_timeout); + CHECK_AND_ASSERT_THROW_MES(r, "RPC error - stop mining"); + CHECK_AND_ASSERT_THROW_MES(resp.status != CORE_RPC_STATUS_BUSY, "Daemon busy"); + CHECK_AND_ASSERT_THROW_MES(resp.status == CORE_RPC_STATUS_OK, "Daemon response invalid: " << resp.status); +} + +uint64_t mock_daemon::get_height() +{ + return m_core->get_blockchain_storage().get_current_blockchain_height(); +} diff --git a/tests/trezor/daemon.h b/tests/trezor/daemon.h new file mode 100644 index 000000000..046b09a5d --- /dev/null +++ b/tests/trezor/daemon.h @@ -0,0 +1,154 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "misc_log_ex.h" +#include "daemon/daemon.h" +#include "rpc/daemon_handler.h" +#include "rpc/zmq_server.h" +#include "common/password.h" +#include "common/util.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 "version.h" +#include "tools.h" + + +class mock_rpc_daemon : public cryptonote::core_rpc_server { +public: + mock_rpc_daemon( + cryptonote::core& cr + , nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >& p2p + ): cryptonote::core_rpc_server(cr, p2p) {} + + static void init_options(boost::program_options::options_description& desc){ cryptonote::core_rpc_server::init_options(desc); } + cryptonote::network_type nettype() const { return m_network_type; } + void nettype(cryptonote::network_type nettype) { m_network_type = nettype; } + + CHAIN_HTTP_TO_MAP2(cryptonote::core_rpc_server::connection_context); //forward http requests to uri map + BEGIN_URI_MAP2() + MAP_URI_AUTO_JON2("/send_raw_transaction", on_send_raw_tx_2, cryptonote::COMMAND_RPC_SEND_RAW_TX) + MAP_URI_AUTO_JON2("/sendrawtransaction", on_send_raw_tx_2, cryptonote::COMMAND_RPC_SEND_RAW_TX) + else { // Default to parent for non-overriden callbacks + return cryptonote::core_rpc_server::handle_http_request_map(query_info, response_info, m_conn_context); + } + END_URI_MAP2() + + bool on_send_raw_tx_2(const cryptonote::COMMAND_RPC_SEND_RAW_TX::request& req, cryptonote::COMMAND_RPC_SEND_RAW_TX::response& res, const cryptonote::core_rpc_server::connection_context *ctx); + +protected: + cryptonote::network_type m_network_type; +}; + +class mock_daemon { +public: + typedef cryptonote::t_cryptonote_protocol_handler<cryptonote::core> t_protocol_raw; + typedef nodetool::node_server<t_protocol_raw> t_node_server; + + static constexpr const std::chrono::seconds rpc_timeout = std::chrono::seconds(60); + + cryptonote::core * m_core; + t_protocol_raw m_protocol; + mock_rpc_daemon m_rpc_server; + t_node_server m_server; + cryptonote::network_type m_network_type; + epee::net_utils::http::http_simple_client m_http_client; + + bool m_start_p2p; + bool m_start_zmq; + boost::program_options::variables_map m_vm; + + std::string m_p2p_bind_port; + std::string m_rpc_bind_port; + std::string m_zmq_bind_port; + + std::atomic<bool> m_stopped; + std::atomic<bool> m_terminated; + std::atomic<bool> m_deinitalized; + boost::thread m_run_thread; + + mock_daemon( + cryptonote::core * core, + boost::program_options::variables_map const & vm + ) + : m_core(core) + , m_vm(vm) + , m_start_p2p(false) + , m_start_zmq(false) + , m_terminated(false) + , m_deinitalized(false) + , m_stopped(false) + , m_protocol{*core, nullptr, command_line::get_arg(vm, cryptonote::arg_offline)} + , m_server{m_protocol} + , m_rpc_server{*core, m_server} + { + // Handle circular dependencies + m_protocol.set_p2p_endpoint(&m_server); + m_core->set_cryptonote_protocol(&m_protocol); + load_params(vm); + } + + virtual ~mock_daemon(); + + static void init_options(boost::program_options::options_description & option_spec); + static void default_options(boost::program_options::variables_map & vm); + static void set_ports(boost::program_options::variables_map & vm, unsigned initial_port); + + mock_daemon * set_start_p2p(bool fl) { m_start_p2p = fl; return this; } + mock_daemon * set_start_zmq(bool fl) { m_start_zmq = fl; return this; } + + void init(); + void deinit(); + void run(); + bool run_main(); + void stop(); + void stop_p2p(); + void stop_rpc(); + void init_and_run(); + void stop_and_deinit(); + void try_init_and_run(boost::optional<unsigned> initial_port=boost::none); + + void mine_blocks(size_t num_blocks, const std::string &miner_address); + void start_mining(const std::string &miner_address, uint64_t threads_count=1, bool do_background_mining=false, bool ignore_battery=true); + void stop_mining(); + uint64_t get_height(); + + void load_params(boost::program_options::variables_map const & vm); + + std::string zmq_addr() const { return std::string("127.0.0.1:") + m_zmq_bind_port; } + std::string rpc_addr() const { return std::string("127.0.0.1:") + m_rpc_bind_port; } + std::string p2p_addr() const { return std::string("127.0.0.1:") + m_p2p_bind_port; } + cryptonote::network_type nettype() const { return m_network_type; } + cryptonote::core * core() const { return m_core; } +}; diff --git a/tests/trezor/tools.cpp b/tests/trezor/tools.cpp new file mode 100644 index 000000000..432350cf6 --- /dev/null +++ b/tests/trezor/tools.cpp @@ -0,0 +1,56 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "tools.h" + +namespace tools { + +namespace po = boost::program_options; + +void options::set_option(boost::program_options::variables_map &vm, const std::string & key, const po::variable_value &pv) +{ + auto it = vm.find(key); + if (it == vm.end()) + { + vm.insert(std::make_pair(key, pv)); + } + else + { + it->second = pv; + } +} + +void options::build_options(boost::program_options::variables_map & vm, const po::options_description & desc_params) +{ + const char *argv[2] = {nullptr}; + po::store(po::parse_command_line(1, argv, desc_params), vm); + po::notify(vm); +} + +} + diff --git a/tests/trezor/tools.h b/tests/trezor/tools.h new file mode 100644 index 000000000..d348a5137 --- /dev/null +++ b/tests/trezor/tools.h @@ -0,0 +1,61 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "misc_log_ex.h" +#include "daemon/daemon.h" +#include "rpc/daemon_handler.h" +#include "rpc/zmq_server.h" +#include "common/password.h" +#include "common/util.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 "version.h" + +namespace tools { + +class options { +public: + static void set_option(boost::program_options::variables_map &vm, const std::string &key, const boost::program_options::variable_value &pv); + static void build_options(boost::program_options::variables_map & vm, const boost::program_options::options_description & desc_params); + + template<typename T, bool required, bool dependent, int NUM_DEPS> + static void set_option(boost::program_options::variables_map &vm, const command_line::arg_descriptor<T, required, dependent, NUM_DEPS> &arg, const boost::program_options::variable_value &pv) + { + set_option(vm, arg.name, pv); + } +}; + +}; + diff --git a/tests/trezor/trezor_tests.cpp b/tests/trezor/trezor_tests.cpp new file mode 100644 index 000000000..310fa45f1 --- /dev/null +++ b/tests/trezor/trezor_tests.cpp @@ -0,0 +1,1836 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include "include_base_utils.h" +#include "cryptonote_basic/cryptonote_basic_impl.h" +#include "cryptonote_basic/account.h" +#include "cryptonote_core/cryptonote_tx_utils.h" +#include "misc_language.h" +#include "string_tools.h" + +using namespace cryptonote; + +#include <boost/regex.hpp> +#include "common/util.h" +#include "common/command_line.h" +#include "trezor_tests.h" +#include "tools.h" +#include "device/device_cold.hpp" +#include "device_trezor/device_trezor.hpp" + + +namespace po = boost::program_options; + +namespace +{ + const command_line::arg_descriptor<std::string> arg_filter = { "filter", "Regular expression filter for which tests to run" }; + const command_line::arg_descriptor<bool> arg_generate_and_play_test_data = {"generate_and_play_test_data", ""}; + const command_line::arg_descriptor<std::string> arg_trezor_path = {"trezor_path", "Path to the trezor device to use, has to support debug link", ""}; + const command_line::arg_descriptor<bool> arg_heavy_tests = {"heavy_tests", "Runs expensive tests (volume tests with real device)", false}; + const command_line::arg_descriptor<std::string> arg_chain_path = {"chain_path", "Path to the serialized blockchain, speeds up testing", ""}; + const command_line::arg_descriptor<bool> arg_fix_chain = {"fix_chain", "If chain_patch is given and file cannot be used, it is ignored and overwriten", false}; +} + +#define HW_TREZOR_NAME "Trezor" +#define TREZOR_ACCOUNT_ORDERING &m_miner_account, &m_alice_account, &m_bob_account, &m_eve_account +#define TREZOR_COMMON_TEST_CASE(genclass, CORE, BASE) \ + rollback_chain(CORE, BASE.head_block()); \ + { \ + genclass ctest; \ + BASE.fork(ctest); \ + GENERATE_AND_PLAY_INSTANCE(genclass, ctest, *(CORE)); \ + } + +#define TREZOR_SETUP_CHAIN(NAME) do { \ + ++tests_count; \ + try { \ + setup_chain(core, trezor_base, chain_path, fix_chain, vm_core); \ + } catch (const std::exception& ex) { \ + failed_tests.emplace_back("gen_trezor_base " #NAME); \ + } \ +} while(0) + + +static device_trezor_test *trezor_device = nullptr; +static device_trezor_test *ensure_trezor_test_device(); +static void rollback_chain(cryptonote::core * core, const cryptonote::block & head); +static void setup_chain(cryptonote::core * core, gen_trezor_base & trezor_base, std::string chain_path, bool fix_chain, const po::variables_map & vm_core); + +int main(int argc, char* argv[]) +{ + TRY_ENTRY(); + tools::on_startup(); + epee::string_tools::set_module_name_and_folder(argv[0]); + + //set up logging options + mlog_configure(mlog_get_default_log_path("trezor_tests.log"), true); + mlog_set_log_level(2); + + po::options_description desc_options("Allowed options"); + command_line::add_arg(desc_options, command_line::arg_help); + command_line::add_arg(desc_options, arg_filter); + command_line::add_arg(desc_options, arg_trezor_path); + command_line::add_arg(desc_options, arg_heavy_tests); + command_line::add_arg(desc_options, arg_chain_path); + command_line::add_arg(desc_options, arg_fix_chain); + + po::variables_map vm; + bool r = command_line::handle_error_helper(desc_options, [&]() + { + po::store(po::parse_command_line(argc, argv, desc_options), vm); + po::notify(vm); + return true; + }); + if (!r) + return 1; + + if (command_line::get_arg(vm, command_line::arg_help)) + { + std::cout << desc_options << std::endl; + return 0; + } + + const std::string filter = tools::glob_to_regex(command_line::get_arg(vm, arg_filter)); + boost::smatch match; + + size_t tests_count = 0; + std::vector<std::string> failed_tests; + std::string trezor_path = command_line::get_arg(vm, arg_trezor_path); + std::string chain_path = command_line::get_arg(vm, arg_chain_path); + const bool heavy_tests = command_line::get_arg(vm, arg_heavy_tests); + const bool fix_chain = command_line::get_arg(vm, arg_fix_chain); + + hw::register_device(HW_TREZOR_NAME, ensure_trezor_test_device()); + // hw::trezor::register_all(); // We use our shim instead. + + // Bootstrapping common chain & accounts + const uint8_t initial_hf = 9; + const uint8_t max_hf = 10; + + cryptonote::core core_obj(nullptr); + cryptonote::core * const core = &core_obj; + std::shared_ptr<mock_daemon> daemon = nullptr; + + gen_trezor_base trezor_base; + trezor_base.setup_args(trezor_path, heavy_tests); + trezor_base.set_hard_fork(initial_hf); + + // Arguments for core & daemon + po::variables_map vm_core; + po::options_description desc_params_core("Core"); + mock_daemon::init_options(desc_params_core); + tools::options::build_options(vm_core, desc_params_core); + mock_daemon::default_options(vm_core); + + // Transaction tests + for(uint8_t hf=initial_hf; hf <= max_hf; ++hf) + { + MDEBUG("Transaction tests for HF " << (int)hf); + if (hf > initial_hf) + { + daemon->stop_and_deinit(); + daemon = nullptr; + trezor_base.daemon(nullptr); + } + + trezor_base.set_hard_fork(hf); + TREZOR_SETUP_CHAIN(std::string("HF") + std::to_string((int)hf)); + + daemon = std::make_shared<mock_daemon>(core, vm_core); + CHECK_AND_ASSERT_THROW_MES(daemon->nettype() == trezor_base.nettype(), "Serialized chain network type does not match"); + + daemon->try_init_and_run(); + trezor_base.daemon(daemon); + + // Hard-fork independent tests + if (hf == initial_hf) + { + TREZOR_COMMON_TEST_CASE(gen_trezor_ki_sync_without_refresh, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_live_refresh, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_ki_sync_with_refresh, core, trezor_base); + } + + TREZOR_COMMON_TEST_CASE(gen_trezor_1utxo, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_1utxo_paymentid_short, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_1utxo_paymentid_short_integrated, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_1utxo_paymentid_long, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo_acc1, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo_to_sub, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo_to_2sub, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo_to_1norm_2sub, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_2utxo_sub_acc_to_1norm_2sub, core, trezor_base); + TREZOR_COMMON_TEST_CASE(gen_trezor_4utxo_to_7outs, core, trezor_base); + TREZOR_COMMON_TEST_CASE(wallet_api_tests, core, trezor_base); + } + + if (trezor_base.heavy_tests()) + { + TREZOR_COMMON_TEST_CASE(gen_trezor_many_utxo, core, trezor_base); + } + + daemon->stop(); + core->deinit(); + el::Level level = (failed_tests.empty() ? el::Level::Info : el::Level::Error); + MLOG(level, "\nREPORT:"); + MLOG(level, " Test run: " << tests_count); + MLOG(level, " Failures: " << failed_tests.size()); + if (!failed_tests.empty()) + { + MLOG(level, "FAILED TESTS:"); + BOOST_FOREACH(auto test_name, failed_tests) + { + MLOG(level, " " << test_name); + } + } + + return failed_tests.empty() ? 0 : 1; + + CATCH_ENTRY_L0("main", 1); +} + +static void rollback_chain(cryptonote::core * core, const cryptonote::block & head) +{ + CHECK_AND_ASSERT_THROW_MES(core, "Core is null"); + + block popped_block; + std::vector<transaction> popped_txs; + + crypto::hash head_hash = get_block_hash(head), cur_hash{}; + uint64_t height = get_block_height(head), cur_height=0; + + do { + core->get_blockchain_top(cur_height, cur_hash); + + if (cur_height <= height && head_hash == cur_hash) + return; + + CHECK_AND_ASSERT_THROW_MES(cur_height > height, "Height differs"); + core->get_blockchain_storage().get_db().pop_block(popped_block, popped_txs); + } while(true); +} + +static bool unserialize_chain_from_file(std::vector<test_event_entry>& events, gen_trezor_base &test_base, const std::string& file_path) +{ + TRY_ENTRY(); + std::ifstream data_file; + data_file.open( file_path, std::ios_base::binary | std::ios_base::in); + if(data_file.fail()) + return false; + try + { + boost::archive::portable_binary_iarchive a(data_file); + test_base.clear(); + + a >> events; + a >> test_base; + return true; + } + catch(...) + { + MWARNING("Chain deserialization failed"); + return false; + } + CATCH_ENTRY_L0("unserialize_chain_from_file", false); +} + +static bool serialize_chain_to_file(std::vector<test_event_entry>& events, gen_trezor_base &test_base, const std::string& file_path) +{ + TRY_ENTRY(); + std::ofstream data_file; + data_file.open( file_path, std::ios_base::binary | std::ios_base::out | std::ios::trunc); + if(data_file.fail()) + return false; + try + { + + boost::archive::portable_binary_oarchive a(data_file); + a << events; + a << test_base; + return !data_file.fail(); + } + catch(...) + { + MWARNING("Chain deserialization failed"); + return false; + } + return false; + CATCH_ENTRY_L0("serialize_chain_to_file", false); +} + +template<class t_test_class> +static bool init_core_replay_events(std::vector<test_event_entry>& events, cryptonote::core * core, const po::variables_map & vm_core) +{ + // this test needs for it to be so. + get_test_options<t_test_class> gto; + + // Hardforks can be specified in events. + v_hardforks_t hardforks; + cryptonote::test_options test_options_tmp{}; + const cryptonote::test_options * test_options_ = >o.test_options; + if (extract_hard_forks(events, hardforks)){ + hardforks.push_back(std::make_pair((uint8_t)0, (uint64_t)0)); // terminator + test_options_tmp.hard_forks = hardforks.data(); + test_options_ = &test_options_tmp; + } + + core->deinit(); + CHECK_AND_ASSERT_THROW_MES(core->init(vm_core, test_options_), "Core init failed"); + core->get_blockchain_storage().get_db().set_batch_transactions(true); + + // start with a clean pool + std::vector<crypto::hash> pool_txs; + CHECK_AND_ASSERT_THROW_MES(core->get_pool_transaction_hashes(pool_txs), "Failed to flush txpool"); + core->get_blockchain_storage().flush_txes_from_pool(pool_txs); + + t_test_class validator; + return replay_events_through_core<t_test_class>(*core, events, validator); +} + +static void setup_chain(cryptonote::core * core, gen_trezor_base & trezor_base, std::string chain_path, bool fix_chain, const po::variables_map & vm_core) +{ + std::vector<test_event_entry> events; + const bool do_serialize = !chain_path.empty(); + const bool chain_file_exists = do_serialize && boost::filesystem::exists(chain_path); + bool loaded = false; + bool generated = false; + + if (chain_file_exists) + { + if (!unserialize_chain_from_file(events, trezor_base, chain_path)) + { + MERROR("Failed to deserialize data from file: " << chain_path); + if (!fix_chain) + { + throw std::runtime_error("Chain load error"); + } + } else + { + trezor_base.load(events); + generated = true; + loaded = true; + } + } + + if (!generated) + { + try + { + trezor_base.clear(); + generated = trezor_base.generate(events); + + if (generated && !loaded && do_serialize) + { + trezor_base.update_trackers(events); + if (!serialize_chain_to_file(events, trezor_base, chain_path)) + { + MERROR("Failed to serialize data to file: " << chain_path); + } + } + } + CATCH_REPLAY(gen_trezor_base); + } + + trezor_base.fix_hf(events); + if (generated && init_core_replay_events<gen_trezor_base>(events, core, vm_core)) + { + MGINFO_GREEN("#TEST-chain-init# Succeeded "); + } + else + { + MERROR("#TEST-chain-init# Failed "); + throw std::runtime_error("Chain init error"); + } +} + +static device_trezor_test *ensure_trezor_test_device(){ + if (!trezor_device) { + trezor_device = new device_trezor_test(); + trezor_device->set_name(HW_TREZOR_NAME); + } + return trezor_device; +} + +static void add_hforks(std::vector<test_event_entry>& events, const v_hardforks_t& hard_forks) +{ + event_replay_settings repl_set; + repl_set.hard_forks = boost::make_optional(hard_forks); + events.push_back(repl_set); +} + +static void add_top_hfork(std::vector<test_event_entry>& events, const v_hardforks_t& hard_forks) +{ + event_replay_settings repl_set; + v_hardforks_t top_fork; + top_fork.push_back(hard_forks.back()); + repl_set.hard_forks = boost::make_optional(top_fork); + events.push_back(repl_set); +} + +static crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) +{ + std::vector<tx_extra_field> tx_extra_fields; + parse_tx_extra(td.m_tx.extra, tx_extra_fields); + + tx_extra_pub_key pub_key_field; + THROW_WALLET_EXCEPTION_IF(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 0), tools::error::wallet_internal_error, + "Public key wasn't found in the transaction extra"); + const crypto::public_key tx_pub_key = pub_key_field.pub_key; + bool two_found = find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 1); + if (!two_found) { + return tx_pub_key; + } else { + throw std::runtime_error("Unsupported tx pub resolution"); + } +} + +static void setup_shim(hw::wallet_shim * shim) +{ + shim->get_tx_pub_key_from_received_outs = &get_tx_pub_key_from_received_outs; +} + +bool get_short_payment_id(crypto::hash8 &payment_id8, const tools::wallet2::pending_tx &ptx, hw::device &hwdev) +{ + std::vector<tx_extra_field> tx_extra_fields; + parse_tx_extra(ptx.tx.extra, tx_extra_fields); // ok if partially parsed + cryptonote::tx_extra_nonce extra_nonce; + if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) + { + if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) + { + if (ptx.dests.empty()) + { + MWARNING("Encrypted payment id found, but no destinations public key, cannot decrypt"); + return false; + } + return hwdev.decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key); + } + } + return false; +} + +static tools::wallet2::tx_construction_data get_construction_data_with_decrypted_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev) +{ + tools::wallet2::tx_construction_data construction_data = ptx.construction_data; + crypto::hash8 payment_id = crypto::null_hash8; + if (get_short_payment_id(payment_id, ptx, hwdev)) + { + // Remove encrypted + remove_field_from_tx_extra(construction_data.extra, typeid(cryptonote::tx_extra_nonce)); + // Add decrypted + std::string extra_nonce; + set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); + THROW_WALLET_EXCEPTION_IF(!add_extra_nonce_to_tx_extra(construction_data.extra, extra_nonce), + tools::error::wallet_internal_error, "Failed to add decrypted payment id to tx extra"); + MDEBUG("Decrypted payment ID: " << payment_id); + } + return construction_data; +} + +static std::string get_payment_id(const std::vector<uint8_t> &tx_extra) +{ + std::vector<cryptonote::tx_extra_field> tx_extra_fields; + cryptonote::parse_tx_extra(tx_extra, tx_extra_fields); // ok if partially parsed + cryptonote::tx_extra_nonce extra_nonce; + + ::crypto::hash payment_id{}; + if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) + { + ::crypto::hash8 payment_id8{}; + if(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) + { + return std::string(payment_id8.data, 8); + } + else if (cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) + { + return std::string(payment_id.data, 32); + } + } + return std::string(); +} + +static crypto::hash8 to_short_payment_id(const std::string & payment_id) +{ + crypto::hash8 payment_id_short; + CHECK_AND_ASSERT_THROW_MES(payment_id.size() == 8, "Invalid argument"); + memcpy(payment_id_short.data, payment_id.data(), 8); + return payment_id_short; +} + +static crypto::hash to_long_payment_id(const std::string & payment_id) +{ + crypto::hash payment_id_long; + CHECK_AND_ASSERT_THROW_MES(payment_id.size() == 32, "Invalid argument"); + memcpy(payment_id_long.data, payment_id.data(), 32); + return payment_id_long; +} + +static std::vector<uint8_t> build_payment_id_extra(const std::string & payment_id) +{ + std::vector<uint8_t> res; + + if (payment_id.size() == 8) { + std::string extra_nonce; + set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, to_short_payment_id(payment_id)); + THROW_WALLET_EXCEPTION_IF(!add_extra_nonce_to_tx_extra(res, extra_nonce), + tools::error::wallet_internal_error, "Failed to add decrypted payment id to tx extra"); + + } else if (payment_id.size() == 32){ + std::string extra_nonce; + set_payment_id_to_tx_extra_nonce(extra_nonce, to_long_payment_id(payment_id)); + THROW_WALLET_EXCEPTION_IF(!add_extra_nonce_to_tx_extra(res, extra_nonce), + tools::error::wallet_internal_error, "Failed to add decrypted payment id to tx extra"); + } + + return res; +} + +static cryptonote::address_parse_info init_addr_parse_info(cryptonote::account_public_address &addr, bool is_sub=false, boost::optional<crypto::hash8> payment_id = boost::none) +{ + cryptonote::address_parse_info res; + res.address = addr; + res.is_subaddress = is_sub; + if (payment_id){ + res.has_payment_id = true; + res.payment_id = payment_id.get(); + } else { + res.has_payment_id = false; + } + return res; +} + +static void expand_tsx(cryptonote::transaction &tx) +{ + auto & rv = tx.rct_signatures; + if (rv.type == rct::RCTTypeFull) + { + rv.p.MGs.resize(1); + rv.p.MGs[0].II.resize(tx.vin.size()); + for (size_t n = 0; n < tx.vin.size(); ++n) + rv.p.MGs[0].II[n] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image); + } + else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2) + { + CHECK_AND_ASSERT_THROW_MES(rv.p.MGs.size() == tx.vin.size(), "Bad MGs size"); + for (size_t n = 0; n < tx.vin.size(); ++n) + { + rv.p.MGs[n].II.resize(1); + rv.p.MGs[n].II[0] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image); + } + } +} + +static std::vector<tools::wallet2*> vct_wallets(tools::wallet2* w1=nullptr, tools::wallet2* w2=nullptr, tools::wallet2* w3=nullptr, tools::wallet2* w4=nullptr, tools::wallet2* w5=nullptr) +{ + std::vector<tools::wallet2*> res; + if (w1) + res.push_back(w1); + if (w2) + res.push_back(w2); + if (w3) + res.push_back(w3); + if (w4) + res.push_back(w4); + if (w5) + res.push_back(w5); + return res; +} + +static uint64_t get_available_funds(tools::wallet2* wallet, uint32_t account=0) +{ + tools::wallet2::transfer_container transfers; + wallet->get_transfers(transfers); + uint64_t sum = 0; + for(const auto & cur : transfers) + { + sum += !cur.m_spent && cur.m_subaddr_index.major == account ? cur.amount() : 0; + } + return sum; +} + +// gen_trezor_base +const uint64_t gen_trezor_base::m_ts_start = 1397862000; // As default wallet timestamp is 1397516400 +const uint64_t gen_trezor_base::m_wallet_ts = m_ts_start - 60*60*24*4; +const std::string gen_trezor_base::m_device_name = "Trezor:udp"; +const std::string gen_trezor_base::m_master_seed_str = "14821d0bc5659b24cafbc889dc4fc60785ee08b65d71c525f81eeaba4f3a570f"; +const std::string gen_trezor_base::m_device_seed = "permit universe parent weapon amused modify essay borrow tobacco budget walnut lunch consider gallery ride amazing frog forget treat market chapter velvet useless topple"; +const std::string gen_trezor_base::m_alice_spend_private = m_master_seed_str; +const std::string gen_trezor_base::m_alice_view_private = "a6ccd4ac344a295d1387f8d18c81bdd394f1845de84188e204514ef9370fd403"; + +gen_trezor_base::gen_trezor_base(){ + m_rct_config = {rct::RangeProofPaddedBulletproof, 1}; + m_test_get_tx_key = true; + m_network_type = cryptonote::TESTNET; +} + +gen_trezor_base::gen_trezor_base(const gen_trezor_base &other): + m_generator(other.m_generator), m_bt(other.m_bt), m_miner_account(other.m_miner_account), + m_bob_account(other.m_bob_account), m_alice_account(other.m_alice_account), m_eve_account(other.m_eve_account), + m_hard_forks(other.m_hard_forks), m_trezor(other.m_trezor), m_rct_config(other.m_rct_config), + m_heavy_tests(other.m_heavy_tests), m_test_get_tx_key(other.m_test_get_tx_key), m_live_refresh_enabled(other.m_live_refresh_enabled), + m_network_type(other.m_network_type), m_daemon(other.m_daemon) +{ + +} + +void gen_trezor_base::setup_args(const std::string & trezor_path, bool heavy_tests) +{ + m_trezor_path = trezor_path.empty() ? m_device_name : std::string("Trezor:") + trezor_path; + m_heavy_tests = heavy_tests; +} + +void gen_trezor_base::setup_trezor() +{ + hw::device &hwdev = hw::get_device(m_trezor_path); + auto trezor = dynamic_cast<device_trezor_test *>(&hwdev); + CHECK_AND_ASSERT_THROW_MES(trezor, "Dynamic cast failed"); + + trezor->setup_for_tests(m_trezor_path, m_device_seed, m_network_type); + m_trezor = trezor; +} + +void gen_trezor_base::fork(gen_trezor_base & other) +{ + other.m_generator = m_generator; + other.m_bt = m_bt; + other.m_network_type = m_network_type; + other.m_daemon = m_daemon; + other.m_events = m_events; + other.m_head = m_head; + other.m_hard_forks = m_hard_forks; + other.m_trezor_path = m_trezor_path; + other.m_heavy_tests = m_heavy_tests; + other.m_rct_config = m_rct_config; + other.m_test_get_tx_key = m_test_get_tx_key; + other.m_live_refresh_enabled = m_live_refresh_enabled; + + other.m_miner_account = m_miner_account; + other.m_bob_account = m_bob_account; + other.m_alice_account = m_alice_account; + other.m_eve_account = m_eve_account; + other.m_trezor = m_trezor; +} + +void gen_trezor_base::clear() +{ + m_generator = test_generator(); + m_bt = block_tracker(); + m_events.clear(); + m_hard_forks.clear(); + m_trezor = nullptr; +} + +void gen_trezor_base::add_shared_events(std::vector<test_event_entry>& events) +{ + events.reserve(m_events.size()); + for(const test_event_entry & c : m_events){ + events.push_back(c); + } +} + +void gen_trezor_base::init_fields() +{ + m_miner_account.generate(); + DEFAULT_HARDFORKS(m_hard_forks); + + crypto::secret_key master_seed{}; + CHECK_AND_ASSERT_THROW_MES(epee::string_tools::hex_to_pod(m_master_seed_str, master_seed), "Hexdecode fails"); + + m_alice_account.generate(master_seed, true); + m_alice_account.set_createtime(m_wallet_ts); +} + +void gen_trezor_base::update_client_settings() +{ + auto dev_trezor = dynamic_cast<::hw::trezor::device_trezor*>(m_trezor); + CHECK_AND_ASSERT_THROW_MES(dev_trezor, "Could not cast to device_trezor"); + + dev_trezor->set_live_refresh_enabled(m_live_refresh_enabled); +} + +bool gen_trezor_base::generate(std::vector<test_event_entry>& events) +{ + init_fields(); + setup_trezor(); + + m_live_refresh_enabled = false; + update_client_settings(); + + m_alice_account.create_from_device(*m_trezor); + m_alice_account.set_createtime(m_wallet_ts); + + // Events, custom genesis so it matches wallet genesis + auto & generator = m_generator; // macro shortcut + + cryptonote::block blk_gen; + std::vector<size_t> block_weights; + generate_genesis_block(blk_gen, get_config(m_network_type).GENESIS_TX, get_config(m_network_type).GENESIS_NONCE); + events.push_back(blk_gen); + generator.add_block(blk_gen, 0, block_weights, 0); + + // First event has to be the genesis block + m_bob_account.generate(); + m_eve_account.generate(); + m_bob_account.set_createtime(m_wallet_ts); + m_eve_account.set_createtime(m_wallet_ts); + cryptonote::account_base * accounts[] = {TREZOR_ACCOUNT_ORDERING}; + for(cryptonote::account_base * ac : accounts){ + events.push_back(*ac); + } + + // Another block with predefined timestamp. + // Carefully set reward and already generated coins so it passes miner_tx check. + cryptonote::block blk_0; + { + std::list<cryptonote::transaction> tx_list; + const crypto::hash prev_id = get_block_hash(blk_gen); + const uint64_t already_generated_coins = generator.get_already_generated_coins(prev_id); + block_weights.clear(); + generator.get_last_n_block_weights(block_weights, prev_id, CRYPTONOTE_REWARD_BLOCKS_WINDOW); + generator.construct_block(blk_0, 1, prev_id, m_miner_account, m_ts_start, already_generated_coins, block_weights, tx_list); + } + + events.push_back(blk_0); + MDEBUG("Gen+1 block has time: " << blk_0.timestamp << " blid: " << get_block_hash(blk_0)); + + // Generate some spendable funds on the Miner account + REWIND_BLOCKS_N(events, blk_3, blk_0, m_miner_account, 40); + + // Rewind so the miners funds are unlocked for initial transactions. + REWIND_BLOCKS(events, blk_3r, blk_3, m_miner_account); + + // Non-rct transactions Miner -> Bob + MAKE_TX_LIST_START(events, txs_blk_4, m_miner_account, m_alice_account, MK_COINS(10), blk_3); + MAKE_TX_LIST(events, txs_blk_4, m_miner_account, m_alice_account, MK_COINS(7), blk_3); + MAKE_TX_LIST(events, txs_blk_4, m_miner_account, m_alice_account, MK_COINS(7), blk_3); + MAKE_TX_LIST(events, txs_blk_4, m_miner_account, m_alice_account, MK_COINS(14), blk_3); + MAKE_TX_LIST(events, txs_blk_4, m_miner_account, m_alice_account, MK_COINS(20), blk_3); + MAKE_TX_LIST(events, txs_blk_4, m_miner_account, m_alice_account, MK_COINS(2), blk_3); + MAKE_TX_LIST(events, txs_blk_4, m_miner_account, m_alice_account, MK_COINS(2), blk_3); + MAKE_TX_LIST(events, txs_blk_4, m_miner_account, m_alice_account, MK_COINS(5), blk_3); + MAKE_NEXT_BLOCK_TX_LIST(events, blk_4, blk_3r, m_miner_account, txs_blk_4); + REWIND_BLOCKS(events, blk_4r, blk_4, m_miner_account); // rewind to unlock + + // Hard fork to bulletproofs version, v9. + const uint8_t CUR_HF = 9; + auto hardfork_height = num_blocks(events); // next block is v9 + ADD_HARDFORK(m_hard_forks, CUR_HF, hardfork_height); + add_hforks(events, m_hard_forks); + MDEBUG("Hardfork height: " << hardfork_height << " at block: " << get_block_hash(blk_4r)); + + // RCT transactions, wallets have to be used, wallet init + m_wl_alice.reset(new tools::wallet2(m_network_type, 1, true)); + m_wl_bob.reset(new tools::wallet2(m_network_type, 1, true)); + wallet_accessor_test::set_account(m_wl_alice.get(), m_alice_account); + wallet_accessor_test::set_account(m_wl_bob.get(), m_bob_account); + + auto addr_alice_sub_0_1 = m_wl_alice->get_subaddress({0, 1}); + auto addr_alice_sub_0_2 = m_wl_alice->get_subaddress({0, 2}); + auto addr_alice_sub_0_3 = m_wl_alice->get_subaddress({0, 3}); + auto addr_alice_sub_0_4 = m_wl_alice->get_subaddress({0, 4}); + auto addr_alice_sub_0_5 = m_wl_alice->get_subaddress({0, 5}); + auto addr_alice_sub_1_0 = m_wl_alice->get_subaddress({1, 0}); + auto addr_alice_sub_1_1 = m_wl_alice->get_subaddress({1, 1}); + auto addr_alice_sub_1_2 = m_wl_alice->get_subaddress({1, 2}); + + // Miner -> Bob, RCT funds + MAKE_TX_LIST_START_RCT(events, txs_blk_5, m_miner_account, m_alice_account, MK_COINS(50), 10, blk_4); + + const size_t target_rct = m_heavy_tests ? 105 : 15; + for(size_t i = 0; i < target_rct; ++i) + { + MAKE_TX_MIX_LIST_RCT(events, txs_blk_5, m_miner_account, m_alice_account, MK_COINS(1) >> 2, 10, blk_4); + } + + // Sub-address destinations + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts(addr_alice_sub_0_1, true, MK_COINS(1) >> 1), 10, blk_4); + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts(addr_alice_sub_0_2, true, MK_COINS(1) >> 1), 10, blk_4); + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts(addr_alice_sub_0_3, true, MK_COINS(1) >> 1), 10, blk_4); + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts(addr_alice_sub_0_4, true, MK_COINS(1) >> 1), 10, blk_4); + + // Sub-address destinations + multi out to force use of additional keys + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts({{addr_alice_sub_0_1, true, MK_COINS(1) >> 1}, {addr_alice_sub_0_2, true, MK_COINS(1) >> 1}}), 10, blk_4); + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts({{addr_alice_sub_0_1, true, MK_COINS(1) >> 1}, {addr_alice_sub_0_2, true, MK_COINS(1) >> 1}, {addr_alice_sub_0_3, true, MK_COINS(1) >> 1}}), 10, blk_4); + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts({{m_miner_account, false, MK_COINS(1) >> 1}, {addr_alice_sub_0_2, true, MK_COINS(1) >> 1}, {addr_alice_sub_0_3, true, MK_COINS(1) >> 1}}), 10, blk_4); + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts({{m_miner_account, false, MK_COINS(1) >> 1}, {addr_alice_sub_0_2, true, MK_COINS(1) >> 1}, {addr_alice_sub_0_3, true, MK_COINS(1) >> 1}}), 10, blk_4); + + // Transfer to other accounts + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts(addr_alice_sub_1_0, true, MK_COINS(1) >> 1), 10, blk_4); + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts(addr_alice_sub_1_1, true, MK_COINS(1) >> 1), 10, blk_4); + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts({{addr_alice_sub_1_0, true, MK_COINS(1) >> 1}, {addr_alice_sub_1_1, true, MK_COINS(1) >> 1}, {addr_alice_sub_0_3, true, MK_COINS(1) >> 1}}), 10, blk_4); + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts({{addr_alice_sub_1_1, true, MK_COINS(1) >> 1}, {addr_alice_sub_1_1, true, MK_COINS(1) >> 1}, {addr_alice_sub_0_2, true, MK_COINS(1) >> 1}}), 10, blk_4); + MAKE_TX_MIX_DEST_LIST_RCT(events, txs_blk_5, m_miner_account, build_dsts({{addr_alice_sub_1_2, true, MK_COINS(1) >> 1}, {addr_alice_sub_1_1, true, MK_COINS(1) >> 1}, {addr_alice_sub_0_5, true, MK_COINS(1) >> 1}}), 10, blk_4); + + // Simple RCT transactions + MAKE_TX_MIX_LIST_RCT(events, txs_blk_5, m_miner_account, m_alice_account, MK_COINS(7), 10, blk_4); + MAKE_TX_MIX_LIST_RCT(events, txs_blk_5, m_miner_account, m_alice_account, MK_COINS(10), 10, blk_4); + MAKE_TX_MIX_LIST_RCT(events, txs_blk_5, m_miner_account, m_alice_account, MK_COINS(30), 10, blk_4); + MAKE_TX_MIX_LIST_RCT(events, txs_blk_5, m_miner_account, m_alice_account, MK_COINS(40), 10, blk_4); + MAKE_NEXT_BLOCK_TX_LIST_HF(events, blk_5, blk_4r, m_miner_account, txs_blk_5, CUR_HF); + + // Simple transaction check + bool resx = rct::verRctSemanticsSimple(txs_blk_5.begin()->rct_signatures); + bool resy = rct::verRctNonSemanticsSimple(txs_blk_5.begin()->rct_signatures); + CHECK_AND_ASSERT_THROW_MES(resx, "Tsx5[0] semantics failed"); + CHECK_AND_ASSERT_THROW_MES(resy, "Tsx5[0] non-semantics failed"); + + REWIND_BLOCKS_HF(events, blk_5r, blk_5, m_miner_account, CUR_HF); // rewind to unlock + + // RCT transactions, wallets have to be used + wallet_tools::process_transactions(m_wl_alice.get(), events, blk_5r, m_bt); + wallet_tools::process_transactions(m_wl_bob.get(), events, blk_5r, m_bt); + MDEBUG("Available funds on Alice: " << get_available_funds(m_wl_alice.get())); + MDEBUG("Available funds on Bob: " << get_available_funds(m_wl_bob.get())); + + // Send Alice -> Bob, manually constructed. Simple TX test, precondition. + cryptonote::transaction tx_1; + std::vector<size_t> selected_transfers; + std::vector<tx_source_entry> sources; + bool res = wallet_tools::fill_tx_sources(m_wl_alice.get(), sources, TREZOR_TEST_MIXIN, boost::none, MK_COINS(2), m_bt, selected_transfers, num_blocks(events) - 1, 0, 1); + CHECK_AND_ASSERT_THROW_MES(res, "TX Fill sources failed"); + + construct_tx_to_key(tx_1, m_wl_alice.get(), m_bob_account, MK_COINS(1), sources, TREZOR_TEST_FEE, true, rct::RangeProofPaddedBulletproof, 1); + events.push_back(tx_1); + MAKE_NEXT_BLOCK_TX1_HF(events, blk_6, blk_5r, m_miner_account, tx_1, CUR_HF); + MDEBUG("Post 1st tsx: " << (num_blocks(events) - 1) << " at block: " << get_block_hash(blk_6)); + + // Simple transaction check + resx = rct::verRctSemanticsSimple(tx_1.rct_signatures); + resy = rct::verRctNonSemanticsSimple(tx_1.rct_signatures); + CHECK_AND_ASSERT_THROW_MES(resx, "tx_1 semantics failed"); + CHECK_AND_ASSERT_THROW_MES(resy, "tx_1 non-semantics failed"); + + REWIND_BLOCKS_HF(events, blk_6r, blk_6, m_miner_account, CUR_HF); + m_head = blk_6r; + m_events = events; + return true; +} + +void gen_trezor_base::load(std::vector<test_event_entry>& events) +{ + init_fields(); + m_events = events; + + unsigned acc_idx = 0; + cryptonote::account_base * accounts[] = {TREZOR_ACCOUNT_ORDERING}; + unsigned accounts_num = (sizeof(accounts) / sizeof(accounts[0])); + + for(auto & ev : events) + { + if (typeid(cryptonote::block) == ev.type()) + { + m_head = boost::get<cryptonote::block>(ev); + } + else if (typeid(cryptonote::account_base) == ev.type()) // accounts + { + const auto & acc = boost::get<cryptonote::account_base>(ev); + if (acc_idx < accounts_num) + { + *accounts[acc_idx++] = acc; + } + } + else if (typeid(event_replay_settings) == ev.type()) // hard forks + { + const auto & rep_settings = boost::get<event_replay_settings>(ev); + if (rep_settings.hard_forks) + { + const auto & hf = rep_settings.hard_forks.get(); + std::copy(hf.begin(), hf.end(), std::back_inserter(m_hard_forks)); + } + } + } + + // Setup wallets, synchronize blocks + m_bob_account.set_createtime(m_wallet_ts); + m_eve_account.set_createtime(m_wallet_ts); + + setup_trezor(); + update_client_settings(); + m_alice_account.create_from_device(*m_trezor); + m_alice_account.set_createtime(m_wallet_ts); + + m_wl_alice.reset(new tools::wallet2(m_network_type, 1, true)); + m_wl_bob.reset(new tools::wallet2(m_network_type, 1, true)); + m_wl_eve.reset(new tools::wallet2(m_network_type, 1, true)); + wallet_accessor_test::set_account(m_wl_alice.get(), m_alice_account); + wallet_accessor_test::set_account(m_wl_bob.get(), m_bob_account); + wallet_accessor_test::set_account(m_wl_eve.get(), m_eve_account); + + wallet_tools::process_transactions(m_wl_alice.get(), events, m_head, m_bt); + wallet_tools::process_transactions(m_wl_bob.get(), events, m_head, m_bt); + MDEBUG("Available funds on Alice: " << get_available_funds(m_wl_alice.get())); + MDEBUG("Available funds on Bob: " << get_available_funds(m_wl_bob.get())); +} + +void gen_trezor_base::fix_hf(std::vector<test_event_entry>& events) +{ + // If current test requires higher hard-fork, move it up + const auto current_hf = m_hard_forks.back().first; + if (m_rct_config.bp_version == 2 && current_hf < 10){ + auto hardfork_height = num_blocks(events); + ADD_HARDFORK(m_hard_forks, 10, hardfork_height); + add_top_hfork(events, m_hard_forks); + MDEBUG("Hardfork height: " << hardfork_height); + } +} + +void gen_trezor_base::update_trackers(std::vector<test_event_entry>& events) +{ + wallet_tools::process_transactions(nullptr, events, m_head, m_bt); +} + +void gen_trezor_base::test_setup(std::vector<test_event_entry>& events) +{ + add_shared_events(events); + + setup_trezor(); + update_client_settings(); + + m_alice_account.create_from_device(*m_trezor); + m_alice_account.set_createtime(m_wallet_ts); + + m_wl_alice.reset(new tools::wallet2(m_network_type, 1, true)); + m_wl_bob.reset(new tools::wallet2(m_network_type, 1, true)); + m_wl_eve.reset(new tools::wallet2(m_network_type, 1, true)); + wallet_accessor_test::set_account(m_wl_alice.get(), m_alice_account); + wallet_accessor_test::set_account(m_wl_bob.get(), m_bob_account); + wallet_accessor_test::set_account(m_wl_eve.get(), m_eve_account); + wallet_tools::process_transactions(m_wl_alice.get(), events, m_head, m_bt); + wallet_tools::process_transactions(m_wl_bob.get(), events, m_head, m_bt); + wallet_tools::process_transactions(m_wl_eve.get(), events, m_head, m_bt); +} + +void gen_trezor_base::add_transactions_to_events( + std::vector<test_event_entry>& events, + test_generator &generator, + const std::vector<cryptonote::transaction> &txs) +{ + // If current test requires higher hard-fork, move it up + const auto current_hf = m_hard_forks.back().first; + const uint8_t tx_hf = m_rct_config.bp_version == 2 ? 10 : 9; + if (tx_hf > current_hf){ + throw std::runtime_error("Too late for HF change"); + } + + std::list<cryptonote::transaction> tx_list; + for(const auto & tx : txs) + { + events.push_back(tx); + tx_list.push_back(tx); + } + + MAKE_NEXT_BLOCK_TX_LIST_HF(events, blk_new, m_head, m_miner_account, tx_list, tx_hf); + MDEBUG("New tsx: " << (num_blocks(events) - 1) << " at block: " << get_block_hash(blk_new)); + + m_head = blk_new; +} + +void gen_trezor_base::test_trezor_tx(std::vector<test_event_entry>& events, std::vector<tools::wallet2::pending_tx>& ptxs, std::vector<cryptonote::address_parse_info>& dsts_info, test_generator &generator, std::vector<tools::wallet2*> wallets, bool is_sweep) +{ + // Construct pending transaction for signature in the Trezor. + const uint64_t height_pre = num_blocks(events) - 1; + cryptonote::block head_block = get_head_block(events); + const crypto::hash head_hash = get_block_hash(head_block); + + tools::wallet2::unsigned_tx_set txs; + std::vector<cryptonote::transaction> tx_list; + + for(auto &ptx : ptxs) { + txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(ptx, *m_trezor)); + } + txs.transfers = std::make_pair(0, wallet_accessor_test::get_transfers(m_wl_alice.get())); + + auto dev_cold = dynamic_cast<::hw::device_cold*>(m_trezor); + CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface"); + + tools::wallet2::signed_tx_set exported_txs; + hw::tx_aux_data aux_data; + hw::wallet_shim wallet_shim; + setup_shim(&wallet_shim); + aux_data.tx_recipients = dsts_info; + aux_data.bp_version = m_rct_config.bp_version; + dev_cold->tx_sign(&wallet_shim, txs, exported_txs, aux_data); + + MDEBUG("Signed tx data from hw: " << exported_txs.ptx.size() << " transactions"); + CHECK_AND_ASSERT_THROW_MES(exported_txs.ptx.size() == ptxs.size(), "Invalid transaction sizes"); + + for (size_t i = 0; i < exported_txs.ptx.size(); ++i){ + auto &c_ptx = exported_txs.ptx[i]; + c_ptx.tx.rct_signatures.mixRing = ptxs[i].tx.rct_signatures.mixRing; + expand_tsx(c_ptx.tx); + + // Simple TX tests, more complex are performed in the core. + MTRACE(cryptonote::obj_to_json_str(c_ptx.tx)); + bool resx = rct::verRctSemanticsSimple(c_ptx.tx.rct_signatures); + bool resy = rct::verRctNonSemanticsSimple(c_ptx.tx.rct_signatures); + CHECK_AND_ASSERT_THROW_MES(resx, "Trezor tx_1 semantics failed"); + CHECK_AND_ASSERT_THROW_MES(resy, "Trezor tx_1 Nonsemantics failed"); + + tx_list.push_back(c_ptx.tx); + MDEBUG("Transaction: " << dump_data(c_ptx.tx)); + } + + add_transactions_to_events(events, generator, tx_list); + + // TX receive test + uint64_t sum_in = 0; + uint64_t sum_out = 0; + + for(size_t txid = 0; txid < exported_txs.ptx.size(); ++txid) { + auto &c_ptx = exported_txs.ptx[txid]; + auto &c_tx = c_ptx.tx; + const crypto::hash txhash = cryptonote::get_transaction_hash(c_tx); + const size_t num_outs = c_tx.vout.size(); + size_t num_received = 0; + uint64_t cur_sum_in = 0; + uint64_t cur_sum_out = 0; + uint64_t cur_sum_out_recv = 0; + std::unordered_set<size_t> recv_out_idx; + std::string exp_payment_id = get_payment_id(c_ptx.construction_data.extra); + std::string enc_payment_id = get_payment_id(c_tx.extra); + size_t num_payment_id_checks_done = 0; + + CHECK_AND_ASSERT_THROW_MES(exp_payment_id.empty() || exp_payment_id.size() == 8 || exp_payment_id.size() == 32, "Required payment ID invalid"); + CHECK_AND_ASSERT_THROW_MES((exp_payment_id.size() == 32) == (enc_payment_id.size() == 32), "Required and built payment ID size mismatch"); + CHECK_AND_ASSERT_THROW_MES(exp_payment_id.size() <= enc_payment_id.size(), "Required and built payment ID size mismatch"); + + for(auto &src : c_ptx.construction_data.sources){ + cur_sum_in += src.amount; + } + + for(auto &dst : c_ptx.construction_data.splitted_dsts){ + cur_sum_out += dst.amount; + } + + CHECK_AND_ASSERT_THROW_MES(c_tx.rct_signatures.txnFee + cur_sum_out == cur_sum_in, "Tx Input Output amount mismatch"); + + for (size_t widx = 0; widx < wallets.size(); ++widx) { + const bool sender = widx == 0; + tools::wallet2 *wl = wallets[widx]; + + wallet_tools::process_transactions(wl, events, m_head, m_bt, boost::make_optional(head_hash)); + + tools::wallet2::transfer_container m_trans; + tools::wallet2::transfer_container m_trans_txid; + wl->get_transfers(m_trans); + + std::copy_if(m_trans.begin(), m_trans.end(), std::back_inserter(m_trans_txid), [&txhash](const tools::wallet2::transfer_details& item) { + return item.m_txid == txhash; + }); + + // Testing if the transaction output has been received + num_received += m_trans_txid.size(); + for (auto & ctran : m_trans_txid){ + cur_sum_out_recv += ctran.amount(); + recv_out_idx.insert(ctran.m_internal_output_index); + CHECK_AND_ASSERT_THROW_MES(!ctran.m_spent, "Txout is spent"); + CHECK_AND_ASSERT_THROW_MES(!sender || ctran.m_key_image_known, "Key Image unknown for recipient"); // sender is Trezor, does not need to have KI + } + + // Sender output payment (contains change and stuff) + if (sender) { + std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> confirmed_transfers; // txid -> tdetail + std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> confirmed_transfers_txid; // txid -> tdetail + wl->get_payments_out(confirmed_transfers, height_pre); + + std::copy_if(confirmed_transfers.begin(), confirmed_transfers.end(), std::back_inserter(confirmed_transfers_txid), [&txhash](const std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>& item) { + return item.first == txhash; + }); + + CHECK_AND_ASSERT_THROW_MES(confirmed_transfers_txid.size() == 1, "Sender does not have outgoing transfer for the transaction"); + } + + // Received payment from the block + std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments; // payment id -> [payment details] multimap + std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments_txid; // payment id -> [payment details] multimap + wl->get_payments(payments, height_pre); + + std::copy_if(payments.begin(), payments.end(), std::back_inserter(payments_txid), [&txhash](const std::pair<crypto::hash, tools::wallet2::payment_details>& item) { + return item.second.m_tx_hash == txhash; + }); + + for(auto &paydet : payments_txid){ + CHECK_AND_ASSERT_THROW_MES(exp_payment_id.empty() || (memcmp(exp_payment_id.data(), paydet.first.data, exp_payment_id.size()) == 0), "Payment ID mismatch"); + num_payment_id_checks_done += 1; + } + } + + CHECK_AND_ASSERT_THROW_MES(c_tx.rct_signatures.txnFee + cur_sum_out_recv == cur_sum_in, "Tx Input Output amount mismatch"); + CHECK_AND_ASSERT_THROW_MES(exp_payment_id.empty() || num_payment_id_checks_done > 0, "No Payment ID checks"); + + if(!is_sweep){ + CHECK_AND_ASSERT_THROW_MES(num_received == num_outs, "Number of received outputs do not match number of outgoing"); + CHECK_AND_ASSERT_THROW_MES(recv_out_idx.size() == num_outs, "Num of outs received do not match"); + } else { + CHECK_AND_ASSERT_THROW_MES(num_received + 1 >= num_outs, "Number of received outputs do not match number of outgoing"); + CHECK_AND_ASSERT_THROW_MES(recv_out_idx.size() + 1 >= num_outs, "Num of outs received do not match"); // can have dummy out + } + + sum_in += cur_sum_in; + sum_out += cur_sum_out + c_tx.rct_signatures.txnFee; + } + + CHECK_AND_ASSERT_THROW_MES(sum_in == sum_out, "Tx amount mismatch"); + + // Test get_tx_key feature for stored private tx keys + test_get_tx(events, wallets, exported_txs.ptx, aux_data.tx_device_aux); +} + +bool gen_trezor_base::verify_tx_key(const ::crypto::secret_key & tx_priv, const ::crypto::public_key & tx_pub, const subaddresses_t & subs) +{ + ::crypto::public_key tx_pub_c; + ::crypto::secret_key_to_public_key(tx_priv, tx_pub_c); + if (tx_pub == tx_pub_c) + return true; + + for(const auto & elem : subs) + { + tx_pub_c = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(elem.first), rct::sk2rct(tx_priv))); + if (tx_pub == tx_pub_c) + return true; + } + return false; +} + +void gen_trezor_base::test_get_tx( + std::vector<test_event_entry>& events, + std::vector<tools::wallet2*> wallets, + const std::vector<tools::wallet2::pending_tx> &ptxs, + const std::vector<std::string> &aux_tx_info) +{ + if (!m_test_get_tx_key) + { + return; + } + + auto dev_cold = dynamic_cast<::hw::device_cold*>(m_trezor); + CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface"); + + if (!dev_cold->is_get_tx_key_supported()) + { + MERROR("Get TX key is not supported by the connected Trezor"); + return; + } + + subaddresses_t all_subs; + for(tools::wallet2 * wlt : wallets) + { + wlt->expand_subaddresses({10, 20}); + + const subaddresses_t & cur_sub = wallet_accessor_test::get_subaddresses(wlt); + all_subs.insert(cur_sub.begin(), cur_sub.end()); + } + + for(size_t txid = 0; txid < ptxs.size(); ++txid) + { + const auto &c_ptx = ptxs[txid]; + const auto &c_tx = c_ptx.tx; + const ::crypto::hash tx_prefix_hash = cryptonote::get_transaction_prefix_hash(c_tx); + + auto tx_pub = cryptonote::get_tx_pub_key_from_extra(c_tx.extra); + auto additional_pub_keys = cryptonote::get_additional_tx_pub_keys_from_extra(c_tx.extra); + + hw::device_cold:: tx_key_data_t tx_key_data; + std::vector<::crypto::secret_key> tx_keys; + + dev_cold->load_tx_key_data(tx_key_data, aux_tx_info[txid]); + CHECK_AND_ASSERT_THROW_MES(std::string(tx_prefix_hash.data, 32) == tx_key_data.tx_prefix_hash, "TX prefix mismatch"); + + dev_cold->get_tx_key(tx_keys, tx_key_data, m_alice_account.get_keys().m_view_secret_key); + CHECK_AND_ASSERT_THROW_MES(!tx_keys.empty(), "Empty TX keys"); + CHECK_AND_ASSERT_THROW_MES(verify_tx_key(tx_keys[0], tx_pub, all_subs), "Tx pub mismatch"); + CHECK_AND_ASSERT_THROW_MES(additional_pub_keys.size() == tx_keys.size() - 1, "Invalid additional keys count"); + + for(size_t i = 0; i < additional_pub_keys.size(); ++i) + { + CHECK_AND_ASSERT_THROW_MES(verify_tx_key(tx_keys[i + 1], additional_pub_keys[i], all_subs), "Tx pub mismatch"); + } + } +} + +void gen_trezor_base::mine_and_test(std::vector<test_event_entry>& events) +{ + cryptonote::core * core = daemon()->core(); + const uint64_t height_before_mining = daemon()->get_height(); + + const auto miner_address = cryptonote::get_account_address_as_str(FAKECHAIN, false, get_address(m_miner_account)); + daemon()->mine_blocks(1, miner_address); + + const uint64_t cur_height = daemon()->get_height(); + CHECK_AND_ASSERT_THROW_MES(height_before_mining < cur_height, "Mining fail"); + + const crypto::hash top_hash = core->get_blockchain_storage().get_block_id_by_height(height_before_mining); + cryptonote::block top_block{}; + CHECK_AND_ASSERT_THROW_MES(core->get_blockchain_storage().get_block_by_hash(top_hash, top_block), "Block fetch fail"); + CHECK_AND_ASSERT_THROW_MES(!top_block.tx_hashes.empty(), "Mined block is empty"); + + std::vector<cryptonote::transaction> txs_found; + std::vector<crypto::hash> txs_missed; + bool r = core->get_blockchain_storage().get_transactions(top_block.tx_hashes, txs_found, txs_missed); + CHECK_AND_ASSERT_THROW_MES(r, "Transaction lookup fail"); + CHECK_AND_ASSERT_THROW_MES(!txs_found.empty(), "Transaction lookup fail"); + + // Transaction is not expanded, but mining verified it. + events.push_back(txs_found[0]); + events.push_back(top_block); +} + +void gen_trezor_base::set_hard_fork(uint8_t hf) +{ + m_top_hard_fork = hf; + if (hf < 9){ + throw std::runtime_error("Minimal supported Hardfork is 9"); + } else if (hf == 9){ + rct_config({rct::RangeProofPaddedBulletproof, 1}); + } else { + rct_config({rct::RangeProofPaddedBulletproof, 2}); + } +} + +#define TREZOR_TEST_PREFIX() \ + test_generator generator(m_generator); \ + test_setup(events); \ + tsx_builder t_builder_o(this); \ + tsx_builder * t_builder = &t_builder_o + +#define TREZOR_TEST_SUFFIX() \ + auto _dsts = t_builder->build(); \ + auto _dsts_info = t_builder->dest_info(); \ + test_trezor_tx(events, _dsts, _dsts_info, generator, vct_wallets(m_wl_alice.get(), m_wl_bob.get(), m_wl_eve.get())); \ + return true + +#define TREZOR_SKIP_IF_VERSION_LEQ(x) if (m_trezor->get_version() <= x) { MDEBUG("Test skipped"); return true; } +#define TREZOR_TEST_PAYMENT_ID "\xde\xad\xc0\xde\xde\xad\xc0\xde" +#define TREZOR_TEST_PAYMENT_ID_LONG "\xde\xad\xc0\xde\xde\xad\xc0\xde\xde\xad\xc0\xde\xde\xad\xc0\xde\xde\xad\xc0\xde\xde\xad\xc0\xde\xde\xad\xc0\xde\xde\xad\xc0\xde" + +tsx_builder * tsx_builder::sources(std::vector<cryptonote::tx_source_entry> & sources, std::vector<size_t> & selected_transfers) +{ + m_sources = sources; + m_selected_transfers = selected_transfers; + return this; +} + +tsx_builder * tsx_builder::compute_sources(boost::optional<size_t> num_utxo, boost::optional<uint64_t> min_amount, ssize_t offset, int step, boost::optional<fnc_accept_tx_source_t> fnc_accept) +{ + CHECK_AND_ASSERT_THROW_MES(m_tester, "m_tester wallet empty"); + CHECK_AND_ASSERT_THROW_MES(m_from, "m_from wallet empty"); + + // typedef std::function<bool(const tx_source_info_crate_t &info, bool &abort)> fnc_accept_tx_source_t; + boost::optional<fnc_accept_tx_source_t> fnc_accept_to_use = boost::none; + + auto c_account = m_account; + fnc_accept_tx_source_t fnc_acc = [c_account, &fnc_accept] (const tx_source_info_crate_t &info, bool &abort) -> bool { + if (info.td->m_subaddr_index.major != c_account){ + return false; + } + if (fnc_accept){ + return (fnc_accept.get())(info, abort); + } + return true; + }; + + fnc_accept_to_use = fnc_acc; + bool res = wallet_tools::fill_tx_sources(m_from, m_sources, m_mixin, num_utxo, min_amount, m_tester->m_bt, m_selected_transfers, m_cur_height, offset, step, fnc_accept_to_use); + CHECK_AND_ASSERT_THROW_MES(res, "Tx source fill error"); + return this; +} + +tsx_builder * tsx_builder::compute_sources_to_sub(boost::optional<size_t> num_utxo, boost::optional<uint64_t> min_amount, ssize_t offset, int step, boost::optional<fnc_accept_tx_source_t> fnc_accept) +{ + fnc_accept_tx_source_t fnc = [&fnc_accept] (const tx_source_info_crate_t &info, bool &abort) -> bool { + if (info.td->m_subaddr_index.minor == 0){ + return false; + } + if (fnc_accept){ + return (fnc_accept.get())(info, abort); + } + return true; + }; + + return compute_sources(num_utxo, min_amount, offset, step, fnc); +} + +tsx_builder * tsx_builder::compute_sources_to_sub_acc(boost::optional<size_t> num_utxo, boost::optional<uint64_t> min_amount, ssize_t offset, int step, boost::optional<fnc_accept_tx_source_t> fnc_accept) +{ + fnc_accept_tx_source_t fnc = [&fnc_accept] (const tx_source_info_crate_t &info, bool &abort) -> bool { + if (info.td->m_subaddr_index.minor == 0 || info.src->real_out_additional_tx_keys.size() == 0){ + return false; + } + if (fnc_accept){ + return (fnc_accept.get())(info, abort); + } + return true; + }; + + return compute_sources(num_utxo, min_amount, offset, step, fnc); +} + +tsx_builder * tsx_builder::destinations(std::vector<cryptonote::tx_destination_entry> &dsts) +{ + m_destinations_orig = dsts; + return this; +} + +tsx_builder * tsx_builder::add_destination(const cryptonote::tx_destination_entry &dst) +{ + m_destinations_orig.push_back(dst); + return this; +} + +tsx_builder * tsx_builder::add_destination(const var_addr_t addr, bool is_subaddr, uint64_t amount) +{ + m_destinations_orig.push_back(build_dst(addr, is_subaddr, amount)); + return this; +} + +tsx_builder * tsx_builder::add_destination(const tools::wallet2 * wallet, bool is_subaddr, uint64_t amount) +{ + m_destinations_orig.push_back(build_dst(get_address(wallet), is_subaddr, amount)); + return this; +} + +tsx_builder * tsx_builder::set_integrated(size_t idx) +{ + m_integrated.insert(idx); + return this; +} + +tsx_builder * tsx_builder::clear_current() +{ + m_account = 0; + m_selected_transfers.clear(); + m_sources.clear(); + m_destinations.clear(); + m_destinations_orig.clear(); + m_dsts_info.clear(); + m_integrated.clear(); + m_payment_id.clear(); + return this; +} + +tsx_builder * tsx_builder::build_tx() +{ + CHECK_AND_ASSERT_THROW_MES(m_tester, "m_tester wallet empty"); + CHECK_AND_ASSERT_THROW_MES(m_from, "m_from wallet empty"); + + // Amount sanity check input >= fee + outputs + const uint64_t out_amount = sum_amount(m_destinations_orig); + const uint64_t in_amount = sum_amount(m_sources); + CHECK_AND_ASSERT_THROW_MES(in_amount >= out_amount + m_fee, "Not enough input credits for outputs and fees"); + + // Create new pending transaction, init with sources and destinations + m_ptxs.emplace_back(); + auto & ptx = m_ptxs.back(); + + std::vector<uint8_t> extra = build_payment_id_extra(m_payment_id); + fill_tx_destinations(m_from->get_subaddress({m_account, 0}), m_destinations_orig, m_fee, m_sources, m_destinations, true); + construct_pending_tx(ptx, extra); + + ptx.construction_data.subaddr_account = m_account; + + // Build destinations parse info + for(size_t i = 0; i < m_destinations_orig.size(); ++i){ + auto & cdest = m_destinations_orig[i]; + cryptonote::address_parse_info info = init_addr_parse_info(cdest.addr, cdest.is_subaddress); + if (m_integrated.find(i) != m_integrated.end()){ + CHECK_AND_ASSERT_THROW_MES(m_payment_id.size() == 8, "Integrated set but payment_id.size() != 8"); + info.has_payment_id = true; + info.payment_id = to_short_payment_id(m_payment_id); + } + + m_dsts_info.push_back(info); + } + + return this; +} + +tsx_builder * tsx_builder::construct_pending_tx(tools::wallet2::pending_tx &ptx, boost::optional<std::vector<uint8_t>> extra) +{ + CHECK_AND_ASSERT_THROW_MES(m_from, "Wallet not provided"); + + cryptonote::transaction tx; + subaddresses_t & subaddresses = wallet_accessor_test::get_subaddresses(m_from); + crypto::secret_key tx_key; + std::vector<crypto::secret_key> additional_tx_keys; + std::vector<tx_destination_entry> destinations_copy = m_destinations; + + auto change_addr = m_from->get_account().get_keys().m_account_address; + bool r = construct_tx_and_get_tx_key(m_from->get_account().get_keys(), subaddresses, m_sources, destinations_copy, + change_addr, extra ? extra.get() : std::vector<uint8_t>(), tx, 0, tx_key, + additional_tx_keys, true, m_rct_config, nullptr); + + CHECK_AND_ASSERT_THROW_MES(r, "Transaction construction failed"); + + ptx.key_images = ""; + ptx.fee = TESTS_DEFAULT_FEE; + ptx.dust = 0; + ptx.dust_added_to_fee = false; + ptx.tx = tx; + ptx.change_dts = m_destinations.back(); + ptx.selected_transfers = m_selected_transfers; + ptx.tx_key = tx_key; + ptx.additional_tx_keys = additional_tx_keys; + ptx.dests = m_destinations; + ptx.multisig_sigs.clear(); + ptx.construction_data.sources = m_sources; + ptx.construction_data.change_dts = m_destinations.back(); + ptx.construction_data.splitted_dsts = m_destinations; + ptx.construction_data.selected_transfers = ptx.selected_transfers; + ptx.construction_data.extra = tx.extra; + ptx.construction_data.unlock_time = 0; + ptx.construction_data.use_rct = true; + ptx.construction_data.use_bulletproofs = true; + ptx.construction_data.dests = m_destinations_orig; + + ptx.construction_data.subaddr_account = 0; + ptx.construction_data.subaddr_indices.clear(); + for(uint32_t i = 0; i < 20; ++i) + ptx.construction_data.subaddr_indices.insert(i); + + return this; +} + +std::vector<tools::wallet2::pending_tx> tsx_builder::build() +{ + return m_ptxs; +} + +device_trezor_test::device_trezor_test(): m_tx_sign_ctr(0), m_compute_key_image_ctr(0) {} + +void device_trezor_test::clear_test_counters(){ + m_tx_sign_ctr = 0; + m_compute_key_image_ctr = 0; +} + +void device_trezor_test::setup_for_tests(const std::string & trezor_path, const std::string & seed, cryptonote::network_type network_type){ + this->clear_test_counters(); + this->set_callback(nullptr); + this->set_debug(true); // debugging commands on Trezor (auto-confirm transactions) + + CHECK_AND_ASSERT_THROW_MES(this->set_name(trezor_path), "Could not set device name " << trezor_path); + this->set_network_type(network_type); + this->set_derivation_path(""); // empty derivation path + + CHECK_AND_ASSERT_THROW_MES(this->init(), "Could not initialize the device " << trezor_path); + CHECK_AND_ASSERT_THROW_MES(this->connect(), "Could not connect to the device " << trezor_path); + this->wipe_device(); + this->load_device(seed); + this->release(); + this->disconnect(); +} + +bool device_trezor_test::compute_key_image(const ::cryptonote::account_keys &ack, const ::crypto::public_key &out_key, + const ::crypto::key_derivation &recv_derivation, size_t real_output_index, + const ::cryptonote::subaddress_index &received_index, + ::cryptonote::keypair &in_ephemeral, ::crypto::key_image &ki) { + + bool res = device_trezor::compute_key_image(ack, out_key, recv_derivation, real_output_index, received_index, + in_ephemeral, ki); + m_compute_key_image_ctr += res; + return res; +} + +void +device_trezor_test::tx_sign(hw::wallet_shim *wallet, const ::tools::wallet2::unsigned_tx_set &unsigned_tx, size_t idx, + hw::tx_aux_data &aux_data, std::shared_ptr<hw::trezor::protocol::tx::Signer> &signer) { + m_tx_sign_ctr += 1; + device_trezor::tx_sign(wallet, unsigned_tx, idx, aux_data, signer); +} + +bool gen_trezor_ki_sync::generate(std::vector<test_event_entry>& events) +{ + test_generator generator(m_generator); + test_setup(events); + + auto dev_cold = dynamic_cast<::hw::device_cold*>(m_trezor); + CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface"); + + std::vector<std::pair<crypto::key_image, crypto::signature>> ski; + tools::wallet2::transfer_container transfers; + hw::wallet_shim wallet_shim; + setup_shim(&wallet_shim); + m_wl_alice->get_transfers(transfers); + + dev_cold->ki_sync(&wallet_shim, transfers, ski); + CHECK_AND_ASSERT_THROW_MES(ski.size() == transfers.size(), "Size mismatch"); + for(size_t i = 0; i < transfers.size(); ++i) + { + auto & td = transfers[i]; + auto & kip = ski[i]; + CHECK_AND_ASSERT_THROW_MES(!td.m_key_image_known || td.m_key_image == kip.first, "Key Image invalid: " << i); + } + + uint64_t spent = 0, unspent = 0; + m_wl_alice->import_key_images(ski, 0, spent, unspent, false); + + auto dev_trezor_test = dynamic_cast<device_trezor_test*>(m_trezor); + CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement test interface"); + if (!m_live_refresh_enabled) + CHECK_AND_ASSERT_THROW_MES(dev_trezor_test->m_compute_key_image_ctr == 0, "Live refresh should not happen: " << dev_trezor_test->m_compute_key_image_ctr); + else + CHECK_AND_ASSERT_THROW_MES(dev_trezor_test->m_compute_key_image_ctr == ski.size(), "Live refresh counts invalid"); + + return true; +} + +bool gen_trezor_ki_sync_with_refresh::generate(std::vector<test_event_entry>& events) +{ + m_live_refresh_enabled = true; + return gen_trezor_ki_sync::generate(events); +} + +bool gen_trezor_ki_sync_without_refresh::generate(std::vector<test_event_entry>& events) +{ + m_live_refresh_enabled = false; + return gen_trezor_ki_sync::generate(events); +} + +bool gen_trezor_live_refresh::generate(std::vector<test_event_entry>& events) +{ + test_generator generator(m_generator); + test_setup(events); + + auto dev_cold = dynamic_cast<::hw::device_cold*>(m_trezor); + CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface"); + + if (!dev_cold->is_live_refresh_supported()){ + MDEBUG("Trezor does not support live refresh"); + return true; + } + + hw::device & sw_device = hw::get_device("default"); + + dev_cold->live_refresh_start(); + for(unsigned i=0; i<50; ++i) + { + cryptonote::subaddress_index subaddr = {0, i}; + + ::crypto::secret_key r; + ::crypto::public_key R; + ::crypto::key_derivation D; + ::crypto::public_key pub_ver; + ::crypto::key_image ki; + + ::crypto::random32_unbiased((unsigned char*)r.data); + ::crypto::secret_key_to_public_key(r, R); + memcpy(D.data, rct::scalarmultKey(rct::pk2rct(R), rct::sk2rct(m_alice_account.get_keys().m_view_secret_key)).bytes, 32); + + ::crypto::secret_key scalar_step1; + ::crypto::secret_key scalar_step2; + ::crypto::derive_secret_key(D, i, m_alice_account.get_keys().m_spend_secret_key, scalar_step1); + if (i == 0) + { + scalar_step2 = scalar_step1; + } + else + { + ::crypto::secret_key subaddr_sk = sw_device.get_subaddress_secret_key(m_alice_account.get_keys().m_view_secret_key, subaddr); + sw_device.sc_secret_add(scalar_step2, scalar_step1, subaddr_sk); + } + + ::crypto::secret_key_to_public_key(scalar_step2, pub_ver); + ::crypto::generate_key_image(pub_ver, scalar_step2, ki); + + cryptonote::keypair in_ephemeral; + ::crypto::key_image ki2; + + dev_cold->live_refresh( + m_alice_account.get_keys().m_view_secret_key, + pub_ver, + D, + i, + subaddr, + in_ephemeral, + ki2 + ); + + CHECK_AND_ASSERT_THROW_MES(ki == ki2, "Key image inconsistent"); + } + + dev_cold->live_refresh_finish(); + return true; +} + +bool gen_trezor_1utxo::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->compute_sources(boost::none, MK_COINS(1), -1, -1) + ->add_destination(m_eve_account, false, 1000) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_1utxo_paymentid_short::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + TREZOR_SKIP_IF_VERSION_LEQ(hw::trezor::pack_version(2, 0, 9)); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->compute_sources(boost::none, MK_COINS(1), -1, -1) + ->add_destination(m_eve_account, false, 1000) + ->payment_id(TREZOR_TEST_PAYMENT_ID) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_1utxo_paymentid_short_integrated::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + TREZOR_SKIP_IF_VERSION_LEQ(hw::trezor::pack_version(2, 0, 9)); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->compute_sources(boost::none, MK_COINS(1), -1, -1) + ->add_destination(m_eve_account, false, 1000) + ->payment_id(TREZOR_TEST_PAYMENT_ID) + ->set_integrated(0) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_1utxo_paymentid_long::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->compute_sources(boost::none, MK_COINS(1), -1, -1) + ->add_destination(m_eve_account, false, 1000) + ->payment_id(TREZOR_TEST_PAYMENT_ID_LONG) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_4utxo::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->compute_sources(4, MK_COINS(1), -1, -1) + ->add_destination(m_eve_account, false, 1000) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_4utxo_acc1::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 1) + ->compute_sources(4, MK_COINS(1), -1, -1) + ->add_destination(m_wl_eve->get_subaddress({0, 1}), true, 1000) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_4utxo_to_sub::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->compute_sources(4, MK_COINS(1), -1, -1) + ->add_destination(m_wl_eve->get_subaddress({0, 1}), true, 1000) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_4utxo_to_2sub::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->compute_sources(4, MK_COINS(1), -1, -1) + ->add_destination(m_wl_eve->get_subaddress({0, 1}), true, 1000) + ->add_destination(m_wl_eve->get_subaddress({1, 3}), true, 1000) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_4utxo_to_1norm_2sub::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->compute_sources(4, MK_COINS(1), -1, -1) + ->add_destination(m_wl_eve->get_subaddress({1, 1}), true, 1000) + ->add_destination(m_wl_eve->get_subaddress({2, 1}), true, 1000) + ->add_destination(m_wl_eve.get(), false, 1000) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_2utxo_sub_acc_to_1norm_2sub::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->compute_sources_to_sub_acc(2, MK_COINS(1) >> 2, -1, -1) + ->add_destination(m_wl_eve->get_subaddress({1, 1}), true, 1000) + ->add_destination(m_wl_eve->get_subaddress({2, 1}), true, 1000) + ->add_destination(m_wl_eve.get(), false, 1000) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_4utxo_to_7outs::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->compute_sources(4, MK_COINS(1), -1, -1) + ->add_destination(m_wl_eve->get_subaddress({1, 1}), true, 1000) + ->add_destination(m_wl_eve->get_subaddress({2, 1}), true, 1000) + ->add_destination(m_wl_eve->get_subaddress({0, 1}), true, 1000) + ->add_destination(m_wl_eve->get_subaddress({0, 2}), true, 1000) + ->add_destination(m_wl_eve->get_subaddress({0, 3}), true, 1000) + ->add_destination(m_wl_eve->get_subaddress({0, 4}), true, 1000) + ->add_destination(m_wl_eve.get(), false, 1000) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +bool gen_trezor_many_utxo::generate(std::vector<test_event_entry>& events) +{ + TREZOR_TEST_PREFIX(); + t_builder->cur_height(num_blocks(events) - 1) + ->mixin(TREZOR_TEST_MIXIN) + ->fee(TREZOR_TEST_FEE) + ->from(m_wl_alice.get(), 0) + ->compute_sources(110, MK_COINS(1), -1, -1) + ->add_destination(m_eve_account, false, 1000) + ->rct_config(m_rct_config) + ->build_tx(); + + TREZOR_TEST_SUFFIX(); +} + +void wallet_api_tests::init() +{ + m_wallet_dir = boost::filesystem::unique_path(); + boost::filesystem::create_directories(m_wallet_dir); +} + +wallet_api_tests::~wallet_api_tests() +{ + try + { + if (!m_wallet_dir.empty() && boost::filesystem::exists(m_wallet_dir)) + { + boost::filesystem::remove_all(m_wallet_dir); + } + } + catch(...) + { + MERROR("Could not remove wallet directory"); + } +} + +bool wallet_api_tests::generate(std::vector<test_event_entry>& events) +{ + init(); + test_setup(events); + const std::string wallet_path = (m_wallet_dir / "wallet").string(); + const auto api_net_type = m_network_type == TESTNET ? Monero::TESTNET : Monero::MAINNET; + + Monero::WalletManager *wmgr = Monero::WalletManagerFactory::getWalletManager(); + std::unique_ptr<Monero::Wallet> w{wmgr->createWalletFromDevice(wallet_path, "", api_net_type, m_trezor_path, 1)}; + CHECK_AND_ASSERT_THROW_MES(w->init(daemon()->rpc_addr(), 0), "Wallet init fail"); + CHECK_AND_ASSERT_THROW_MES(w->refresh(), "Refresh fail"); + uint64_t balance = w->balance(0); + MINFO("Balance: " << balance); + CHECK_AND_ASSERT_THROW_MES(w->status() == Monero::PendingTransaction::Status_Ok, "Status nok"); + + auto addr = get_address(m_eve_account); + auto recepient_address = cryptonote::get_account_address_as_str(m_network_type, false, addr); + Monero::PendingTransaction * transaction = w->createTransaction(recepient_address, + "", + MK_COINS(10), + TREZOR_TEST_MIXIN, + Monero::PendingTransaction::Priority_Medium, + 0, + std::set<uint32_t>{}); + CHECK_AND_ASSERT_THROW_MES(transaction->status() == Monero::PendingTransaction::Status_Ok, "Status nok"); + w->refresh(); + + CHECK_AND_ASSERT_THROW_MES(w->balance(0) == balance, "Err"); + CHECK_AND_ASSERT_THROW_MES(transaction->amount() == MK_COINS(10), "Err"); + CHECK_AND_ASSERT_THROW_MES(transaction->commit(), "Err"); + CHECK_AND_ASSERT_THROW_MES(w->balance(0) != balance, "Err"); + CHECK_AND_ASSERT_THROW_MES(wmgr->closeWallet(w.get()), "Err"); + (void)w.release(); + + mine_and_test(events); + return true; +} + diff --git a/tests/trezor/trezor_tests.h b/tests/trezor/trezor_tests.h new file mode 100644 index 000000000..bed49fec4 --- /dev/null +++ b/tests/trezor/trezor_tests.h @@ -0,0 +1,329 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + +#include <device_trezor/device_trezor.hpp> +#include <wallet/api/wallet2_api.h> +#include "daemon.h" +#include "../core_tests/chaingen.h" +#include "../core_tests/wallet_tools.h" + +#define TREZOR_TEST_FEE 90000000000 +#define TREZOR_TEST_MIXIN 11 + +/************************************************************************/ +/* */ +/************************************************************************/ +class tsx_builder; +class gen_trezor_base : public test_chain_unit_base +{ +public: + friend class tsx_builder; + + gen_trezor_base(); + gen_trezor_base(const gen_trezor_base &other); + virtual ~gen_trezor_base() {}; + + virtual void setup_args(const std::string & trezor_path, bool heavy_tests=false); + virtual bool generate(std::vector<test_event_entry>& events); + virtual void load(std::vector<test_event_entry>& events); // load events, init test obj + virtual void fix_hf(std::vector<test_event_entry>& events); + virtual void update_trackers(std::vector<test_event_entry>& events); + + virtual void fork(gen_trezor_base & other); // fork generated chain to another test + virtual void clear(); // clears m_events, bt, generator, hforks + virtual void add_shared_events(std::vector<test_event_entry>& events); // m_events -> events + virtual void test_setup(std::vector<test_event_entry>& events); // init setup env, wallets + + virtual void add_transactions_to_events( + std::vector<test_event_entry>& events, + test_generator &generator, + const std::vector<cryptonote::transaction> &txs); + + virtual void test_trezor_tx( + std::vector<test_event_entry>& events, + std::vector<tools::wallet2::pending_tx>& ptxs, + std::vector<cryptonote::address_parse_info>& dsts_info, + test_generator &generator, + std::vector<tools::wallet2*> wallets, + bool is_sweep=false); + + virtual void test_get_tx( + std::vector<test_event_entry>& events, + std::vector<tools::wallet2*> wallets, + const std::vector<tools::wallet2::pending_tx> &ptxs, + const std::vector<std::string> &aux_tx_info); + + virtual void mine_and_test(std::vector<test_event_entry>& events); + + virtual void set_hard_fork(uint8_t hf); + + crypto::hash head_hash() const { return get_block_hash(m_head); } + cryptonote::block head_block() const { return m_head; } + bool heavy_tests() const { return m_heavy_tests; } + void rct_config(rct::RCTConfig rct_config) { m_rct_config = rct_config; } + uint8_t cur_hf() const { return m_hard_forks.size() > 0 ? m_hard_forks.back().first : 0; } + cryptonote::network_type nettype() const { return m_network_type; } + std::shared_ptr<mock_daemon> daemon() const { return m_daemon; } + void daemon(std::shared_ptr<mock_daemon> daemon){ m_daemon = std::move(daemon); } + + // Static configuration + static const uint64_t m_ts_start; + static const uint64_t m_wallet_ts; + static const std::string m_device_name; + static const std::string m_master_seed_str; + static const std::string m_device_seed; + static const std::string m_alice_spend_private; + static const std::string m_alice_view_private; + +protected: + virtual void setup_trezor(); + virtual void init_fields(); + virtual void update_client_settings(); + virtual bool verify_tx_key(const ::crypto::secret_key & tx_priv, const ::crypto::public_key & tx_pub, const subaddresses_t & subs); + + test_generator m_generator; + block_tracker m_bt; + cryptonote::network_type m_network_type; + std::shared_ptr<mock_daemon> m_daemon; + + uint8_t m_top_hard_fork; + v_hardforks_t m_hard_forks; + cryptonote::block m_head; + std::vector<test_event_entry> m_events; + + std::string m_trezor_path; + bool m_heavy_tests; + bool m_test_get_tx_key; + rct::RCTConfig m_rct_config; + bool m_live_refresh_enabled; + + cryptonote::account_base m_miner_account; + cryptonote::account_base m_bob_account; + cryptonote::account_base m_alice_account; + cryptonote::account_base m_eve_account; + hw::trezor::device_trezor * m_trezor; + std::unique_ptr<tools::wallet2> m_wl_alice; + std::unique_ptr<tools::wallet2> m_wl_bob; + std::unique_ptr<tools::wallet2> m_wl_eve; + + friend class boost::serialization::access; + + template<class Archive> + void serialize(Archive & ar, const unsigned int /*version*/) + { + ar & m_generator; + ar & m_network_type; + } +}; + +class tsx_builder { +public: + tsx_builder(): m_tester(nullptr), m_from(nullptr), m_account(0), m_mixin(TREZOR_TEST_MIXIN), m_fee(TREZOR_TEST_FEE), + m_rct_config({rct::RangeProofPaddedBulletproof, 1 }){} + + tsx_builder(gen_trezor_base * tester): m_tester(tester), m_from(nullptr), m_account(0), + m_mixin(TREZOR_TEST_MIXIN), m_fee(TREZOR_TEST_FEE), + m_rct_config({rct::RangeProofPaddedBulletproof, 1 }){} + + tsx_builder * cur_height(uint64_t cur_height) { m_cur_height = cur_height; return this; } + tsx_builder * mixin(size_t mixin=TREZOR_TEST_MIXIN) { m_mixin = mixin; return this; } + tsx_builder * fee(uint64_t fee=TREZOR_TEST_FEE) { m_fee = fee; return this; } + tsx_builder * payment_id(const std::string & payment_id) { m_payment_id = payment_id; return this; } + tsx_builder * from(tools::wallet2 *from, uint32_t account=0) { m_from = from; m_account=account; return this; } + tsx_builder * sources(std::vector<cryptonote::tx_source_entry> & sources, std::vector<size_t> & selected_transfers); + tsx_builder * compute_sources(boost::optional<size_t> num_utxo=boost::none, boost::optional<uint64_t> min_amount=boost::none, ssize_t offset=-1, int step=1, boost::optional<fnc_accept_tx_source_t> fnc_accept=boost::none); + tsx_builder * compute_sources_to_sub(boost::optional<size_t> num_utxo=boost::none, boost::optional<uint64_t> min_amount=boost::none, ssize_t offset=-1, int step=1, boost::optional<fnc_accept_tx_source_t> fnc_accept=boost::none); + tsx_builder * compute_sources_to_sub_acc(boost::optional<size_t> num_utxo=boost::none, boost::optional<uint64_t> min_amount=boost::none, ssize_t offset=-1, int step=1, boost::optional<fnc_accept_tx_source_t> fnc_accept=boost::none); + + tsx_builder * destinations(std::vector<cryptonote::tx_destination_entry> &dsts); + tsx_builder * add_destination(const cryptonote::tx_destination_entry &dst); + tsx_builder * add_destination(const tools::wallet2 * wallet, bool is_subaddr=false, uint64_t amount=1000); + tsx_builder * add_destination(const var_addr_t addr, bool is_subaddr=false, uint64_t amount=1000); + tsx_builder * set_integrated(size_t idx); + tsx_builder * rct_config(const rct::RCTConfig & rct_config) {m_rct_config = rct_config; return this; }; + + tsx_builder * build_tx(); + tsx_builder * construct_pending_tx(tools::wallet2::pending_tx &ptx, boost::optional<std::vector<uint8_t>> extra = boost::none); + tsx_builder * clear_current(); + std::vector<tools::wallet2::pending_tx> build(); + std::vector<cryptonote::address_parse_info> dest_info(){ return m_dsts_info; } + +protected: + gen_trezor_base * m_tester; + uint64_t m_cur_height; + std::vector<tools::wallet2::pending_tx> m_ptxs; // all transactions + + // current transaction + size_t m_mixin; + uint64_t m_fee; + tools::wallet2 * m_from; + uint32_t m_account; + cryptonote::transaction m_tx; + std::vector<size_t> m_selected_transfers; + std::vector<cryptonote::tx_source_entry> m_sources; + std::vector<cryptonote::tx_destination_entry> m_destinations; + std::vector<cryptonote::tx_destination_entry> m_destinations_orig; + std::vector<cryptonote::address_parse_info> m_dsts_info; + std::unordered_set<size_t> m_integrated; + std::string m_payment_id; + rct::RCTConfig m_rct_config; +}; + +// Trezor device ship to track actual method calls. +class device_trezor_test : public hw::trezor::device_trezor { +public: + size_t m_tx_sign_ctr; + size_t m_compute_key_image_ctr; + + device_trezor_test(); + + void clear_test_counters(); + void setup_for_tests(const std::string & trezor_path, const std::string & seed, cryptonote::network_type network_type); + + bool compute_key_image(const ::cryptonote::account_keys &ack, const ::crypto::public_key &out_key, + const ::crypto::key_derivation &recv_derivation, size_t real_output_index, + const ::cryptonote::subaddress_index &received_index, ::cryptonote::keypair &in_ephemeral, + ::crypto::key_image &ki) override; + +protected: + void tx_sign(hw::wallet_shim *wallet, const ::tools::wallet2::unsigned_tx_set &unsigned_tx, size_t idx, + hw::tx_aux_data &aux_data, std::shared_ptr<hw::trezor::protocol::tx::Signer> &signer) override; +}; + +// Tests +class gen_trezor_ki_sync : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_ki_sync_with_refresh : public gen_trezor_ki_sync +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_ki_sync_without_refresh : public gen_trezor_ki_sync +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_live_refresh : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_1utxo : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_1utxo_paymentid_short : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_1utxo_paymentid_short_integrated : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_1utxo_paymentid_long : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_4utxo : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_4utxo_acc1 : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_4utxo_to_sub : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_4utxo_to_2sub : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_4utxo_to_1norm_2sub : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_2utxo_sub_acc_to_1norm_2sub : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_4utxo_to_7outs : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +class gen_trezor_many_utxo : public gen_trezor_base +{ +public: + bool generate(std::vector<test_event_entry>& events) override; +}; + +// Wallet::API tests +class wallet_api_tests : public gen_trezor_base +{ +public: + virtual ~wallet_api_tests(); + void init(); + bool generate(std::vector<test_event_entry>& events) override; + +protected: + boost::filesystem::path m_wallet_dir; +};
\ No newline at end of file diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index aea82ede2..bfaea6add 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # @@ -52,10 +52,12 @@ set(unit_tests_sources json_serialization.cpp get_xtype_from_string.cpp hashchain.cpp + hmac_keccak.cpp http.cpp keccak.cpp logging.cpp long_term_block_weight.cpp + lmdb.cpp main.cpp memwipe.cpp mlocker.cpp @@ -101,6 +103,7 @@ target_link_libraries(unit_tests cryptonote_protocol cryptonote_core blockchain_db + lmdb_lib rpc net serialization diff --git a/tests/unit_tests/account.cpp b/tests/unit_tests/account.cpp index 113622b5e..7073ab3e8 100644 --- a/tests/unit_tests/account.cpp +++ b/tests/unit_tests/account.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/address_from_url.cpp b/tests/unit_tests/address_from_url.cpp index f6c0ad105..4b06c6487 100644 --- a/tests/unit_tests/address_from_url.cpp +++ b/tests/unit_tests/address_from_url.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/apply_permutation.cpp b/tests/unit_tests/apply_permutation.cpp index e2420eb45..76d10c8ff 100644 --- a/tests/unit_tests/apply_permutation.cpp +++ b/tests/unit_tests/apply_permutation.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/ban.cpp b/tests/unit_tests/ban.cpp index ccfcfc15a..eb1ee8932 100644 --- a/tests/unit_tests/ban.cpp +++ b/tests/unit_tests/ban.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/base58.cpp b/tests/unit_tests/base58.cpp index 7edb28e62..1996afd04 100644 --- a/tests/unit_tests/base58.cpp +++ b/tests/unit_tests/base58.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/block_queue.cpp b/tests/unit_tests/block_queue.cpp index f7b7c63fd..0cba7c443 100644 --- a/tests/unit_tests/block_queue.cpp +++ b/tests/unit_tests/block_queue.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/block_reward.cpp b/tests/unit_tests/block_reward.cpp index a897e4140..1dd9072b9 100644 --- a/tests/unit_tests/block_reward.cpp +++ b/tests/unit_tests/block_reward.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/blockchain_db.cpp b/tests/unit_tests/blockchain_db.cpp index f9a6adf03..4fbc21ddc 100644 --- a/tests/unit_tests/blockchain_db.cpp +++ b/tests/unit_tests/blockchain_db.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp index 4f07415b0..9ac0df45b 100644 --- a/tests/unit_tests/bulletproofs.cpp +++ b/tests/unit_tests/bulletproofs.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/canonical_amounts.cpp b/tests/unit_tests/canonical_amounts.cpp index 227a64a7f..74fcc9548 100644 --- a/tests/unit_tests/canonical_amounts.cpp +++ b/tests/unit_tests/canonical_amounts.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/chacha.cpp b/tests/unit_tests/chacha.cpp index 699890522..e06081f8f 100644 --- a/tests/unit_tests/chacha.cpp +++ b/tests/unit_tests/chacha.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/checkpoints.cpp b/tests/unit_tests/checkpoints.cpp index ec09c596b..90229a0b8 100644 --- a/tests/unit_tests/checkpoints.cpp +++ b/tests/unit_tests/checkpoints.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/command_line.cpp b/tests/unit_tests/command_line.cpp index 327fceefe..6d5afb708 100644 --- a/tests/unit_tests/command_line.cpp +++ b/tests/unit_tests/command_line.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/crypto.cpp b/tests/unit_tests/crypto.cpp index e09ec7f7a..7100c8013 100644 --- a/tests/unit_tests/crypto.cpp +++ b/tests/unit_tests/crypto.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, 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 11c6189e5..5049dd83d 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/dns_resolver.cpp b/tests/unit_tests/dns_resolver.cpp index 2b3627f02..6bffc01d8 100644 --- a/tests/unit_tests/dns_resolver.cpp +++ b/tests/unit_tests/dns_resolver.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 41554f948..32989f545 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-2018, The Monero Project +// Copyright (c) 2014-2019, 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 9ea71875b..697845f60 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp index 9a32d149a..946731826 100644 --- a/tests/unit_tests/epee_utils.cpp +++ b/tests/unit_tests/epee_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/fee.cpp b/tests/unit_tests/fee.cpp index 8ccb38fc9..3b0bc1f09 100644 --- a/tests/unit_tests/fee.cpp +++ b/tests/unit_tests/fee.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 54ba473a6..75452894a 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-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index 12dfde1bc..bb26b5533 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/hashchain.cpp b/tests/unit_tests/hashchain.cpp index df87f5df2..d07dfdb7a 100644 --- a/tests/unit_tests/hashchain.cpp +++ b/tests/unit_tests/hashchain.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/hmac_keccak.cpp b/tests/unit_tests/hmac_keccak.cpp new file mode 100644 index 000000000..cb35d272a --- /dev/null +++ b/tests/unit_tests/hmac_keccak.cpp @@ -0,0 +1,152 @@ +// 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 "../io.h" + +extern "C" { +#include "crypto/hmac-keccak.h" +} + +#define KECCAK_BLOCKLEN 136 + +static const struct { + const char *key; + const char *inp; + const char *res; +} keccak_hmac_vectors[] = { + {"", + "", + "042186ec4e98680a0866091d6fb89b60871134b44327f8f467c14e9841d3e97b", + }, + {"00", + "", + "042186ec4e98680a0866091d6fb89b60871134b44327f8f467c14e9841d3e97b", + }, + {"00000000000000000000000000000000", + "", + "042186ec4e98680a0866091d6fb89b60871134b44327f8f467c14e9841d3e97b", + }, + {"", + "00", + "402541d5d678ad30217023a12efbd2f367388dc0253ccddb8f915d187e03d414", + }, + {"0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "d164c38ca454176281b3c09dc83cf70bd00290835ff5490356993d368cde0577", + }, + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0000000000000000000000000000000000000000000000000000000000000000", + "d52c9100301a9546d8c97d7f32e8178fd5f4b0a7646ab761db9e0f503f8b0542", + }, + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "d52c9100301a9546d8c97d7f32e8178fd5f4b0a7646ab761db9e0f503f8b0542", + "5b9d98c5d3249176230d70fdb215ee2f93e4783064c9564384ddc396e63c18cb", + }, + {"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "98d033ba0c95bb3aaf5709733443cbedba20f6451b710ccba4118fc5523013c8", + }, + {"65768798a9bacbdcedfe0f2031425364758697a8b9cadbecfd0e1f30415263748596a7b8c9daebfc0d1e2f405162738495a6b7c8d9eafb0c1d2e3f5061728394a5b6c7d8e9fa0b1c2d3e4f60718293a4b5c6d7e8f90a1b2c3d4e5f708192a3b4c5d6e7f8091a2b3c4d5e6f8091a2b3c4d5e6f708192a3b4c5d6e7f90a1b2c3d4e5f60718293a4b5c6d7e8fa0b1c2d3e4f5061728394a5b6c7d8e9fb0c1d2e3f405162738495a6b7c8d9eafc0d1e2f30415263748596a7b8c9daebfd0e1f2031425364758697a8b9cadbecfe0f102132435465768798a9bacbdcedff00112233445566778899aabbccddeef00112233445566778899aabbccddeeff102132435465768798a9bacbdcedfe0f2031425364758697a8b9cadbecfd0e1f30415263748596a7b8c9daebfc0d1e2f405162738495a6b7c8d9eafb0c1d2e3f5061728394", + "7b8895a2afbcc9d6e3f0fd0a1724313e4b5865727f8c99a6b3c0cddae7f4010e1b2835424f5c697683909daab7c4d1deebf805121f2c394653606d7a8794a1aebbc8d5e2effc091623303d4a5764717e8b98a5b2bfccd9e6f3000d1a2734414e5b6875828f9ca9b6c3d0ddeaf704111e2b3845525f6c798693a0adbac7d4e1eefb0815222f3c495663707d8a97a4b1becbd8e5f2ff0c192633404d5a6774818e9ba8b5c2cfdce9f603101d2a3744515e6b7885929facb9c6d3e0edfa0714212e3b4855626f7c8996a3b0bdcad7e4f1fe0b1825323f4c596673808d9aa7b4c1cedbe8f5020f1c293643505d6a7784919eabb8c5d2dfecf90613202d3a4754616e7b8895a2afbcc9d6e3f0fd0a1724313e4b5865727f8c99a6b3c0cddae7f4010e1b2835424f5c697683909daab7c4d1deebf805121f2c394653606d7a8794a1ae", + "117b60a7f474fd2245adff82a69429367de8f5263fa96c411986009a743b0e70", + }, + {"00112233445566778899aabbccddeeff102132435465768798a9bacbdcedfe0f", + "000d1a2734414e5b6875828f9ca9b6c3d0ddeaf704111e2b3845525f6c798693a0adbac7d4e1eefb0815222f3c495663707d8a97a4b1becbd8e5f2ff0c192633404d5a6774818e9ba8b5c2cfdce9f603101d2a3744515e6b7885929facb9c6d3e0edfa0714212e3b4855626f7c8996a3b0bdcad7e4f1fe0b1825323f4c596673808d9aa7b4c1cedbe8f5020f1c293643505d6a7784919eabb8c5d2dfecf90613202d3a4754616e7b8895a2afbcc9d6e3f0fd0a1724313e4b5865727f8c99a6b3c0cddae7f4010e1b", + "cbe3ca627c6432c9a66f07c6411fccca894c9b083b55d0773152460142a676ed", + }, +}; + +static void test_keccak_hmac(const size_t * chunks) +{ + uint8_t inp_buff[1024]; + uint8_t key_buff[1024]; + uint8_t res_exp[32]; + uint8_t res_comp[32]; + const size_t len_chunks = chunks ? sizeof(chunks) / sizeof(*chunks) : 0; + + for (size_t i = 0; i < (sizeof(keccak_hmac_vectors) / sizeof(*keccak_hmac_vectors)); i++) + { + const size_t inp_len = strlen(keccak_hmac_vectors[i].inp)/2; + const size_t key_len = strlen(keccak_hmac_vectors[i].key)/2; + ASSERT_TRUE(hexdecode(keccak_hmac_vectors[i].inp, inp_len, inp_buff)); + ASSERT_TRUE(hexdecode(keccak_hmac_vectors[i].key, key_len, key_buff)); + ASSERT_TRUE(hexdecode(keccak_hmac_vectors[i].res, 32, res_exp)); + if (len_chunks == 0) + { + hmac_keccak_hash(res_comp, key_buff, key_len, inp_buff, inp_len); + } + else + { + hmac_keccak_state S; + hmac_keccak_init(&S, key_buff, key_len); + size_t inp_passed = 0; + size_t chunk_size = 0; + while(inp_passed < inp_len){ + const size_t to_pass = std::min(inp_len - inp_passed, chunks[chunk_size]); + hmac_keccak_update(&S, inp_buff + inp_passed, to_pass); + + inp_passed += to_pass; + chunk_size = (chunk_size + 1) % len_chunks; + } + + hmac_keccak_finish(&S, res_comp); + } + ASSERT_EQ(memcmp(res_exp, res_comp, 32), 0); + } +} + + +TEST(keccak_hmac, ) +{ + test_keccak_hmac({}); +} + +TEST(keccak_hmac, 1) +{ + static const size_t chunks[] = {1}; + test_keccak_hmac(chunks); +} + +TEST(keccak_hmac, 1_20) +{ + static const size_t chunks[] = {1, 20}; + test_keccak_hmac(chunks); +} + +TEST(keccak_hmac, 136_1) +{ + static const size_t chunks[] = {136, 1}; + test_keccak_hmac(chunks); +} + +TEST(keccak_hmac, 137_1) +{ + static const size_t chunks[] = {137, 1}; + test_keccak_hmac(chunks); +} diff --git a/tests/unit_tests/http.cpp b/tests/unit_tests/http.cpp index 372448764..938f444d7 100644 --- a/tests/unit_tests/http.cpp +++ b/tests/unit_tests/http.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/lmdb.cpp b/tests/unit_tests/lmdb.cpp new file mode 100644 index 000000000..c37c83a32 --- /dev/null +++ b/tests/unit_tests/lmdb.cpp @@ -0,0 +1,404 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <boost/range/algorithm_ext/iota.hpp> +#include <boost/range/algorithm/equal.hpp> +#include <gtest/gtest.h> + +#include "lmdb/database.h" +#include "lmdb/table.h" +#include "lmdb/transaction.h" +#include "lmdb/util.h" + +namespace +{ + enum class choice : unsigned {}; + enum class big_choice : unsigned long {}; + + struct bytes { + char data[16]; + }; + + MONERO_CURSOR(test_cursor); + + template<typename T> + int run_compare(T left, T right, MDB_cmp_func* cmp) + { + MDB_val left_val = lmdb::to_val(left); + MDB_val right_val = lmdb::to_val(right); + return (*cmp)(&left_val, &right_val); + } +} + +TEST(LMDB, Traits) +{ + EXPECT_TRUE((std::is_same<void, lmdb::identity<void>::type>())); + EXPECT_TRUE((std::is_same<unsigned, lmdb::identity<unsigned>::type>())); + + EXPECT_TRUE((std::is_same<void, lmdb::native_type<void>>())); + EXPECT_TRUE((std::is_same<unsigned, lmdb::native_type<unsigned>>())); + EXPECT_TRUE((std::is_same<unsigned, lmdb::native_type<choice>>())); + EXPECT_TRUE((std::is_same<unsigned long, lmdb::native_type<big_choice>>())); +} + +TEST(LMDB, ToNative) +{ + enum class negative_choice : int {}; + + EXPECT_TRUE((std::is_same<unsigned, decltype(lmdb::to_native(choice(0)))>())); + EXPECT_TRUE( + (std::is_same<unsigned long, decltype(lmdb::to_native(big_choice(0)))>()) + ); + EXPECT_TRUE( + (std::is_same<int, decltype(lmdb::to_native(negative_choice(0)))>()) + ); + + EXPECT_EQ(unsigned(0), lmdb::to_native(choice(0))); + EXPECT_EQ(unsigned(0xffffffff), lmdb::to_native(choice(0xffffffff))); + EXPECT_EQ(-1, lmdb::to_native(negative_choice(-1))); + + // test constexpr + static_assert(100 == lmdb::to_native(choice(100)), "to_native failed"); + static_assert(-100 == lmdb::to_native(negative_choice(-100)), "to_native failed"); +} + +TEST(LMDB, Conversions) +{ + struct one + { + big_choice i; + choice j; + }; + + const one test{big_choice(100), choice(95)}; + one test2{big_choice(1000), choice(950)}; + + EXPECT_EQ(&test, lmdb::to_val(test).mv_data); + EXPECT_NE(&test2, lmdb::to_val(test).mv_data); + EXPECT_EQ( + &test, + static_cast<const void*>(lmdb::to_byte_span(lmdb::to_val(test)).begin()) + ); + EXPECT_EQ(sizeof(test), lmdb::to_val(test).mv_size); + EXPECT_EQ(sizeof(test), lmdb::to_byte_span(lmdb::to_val(test)).size()); + + EXPECT_EQ(&test2, lmdb::to_val(test2).mv_data); + EXPECT_NE(&test, lmdb::to_val(test2).mv_data); + EXPECT_EQ( + &test2, + static_cast<const void*>(lmdb::to_byte_span(lmdb::to_val(test2)).begin()) + ); + EXPECT_EQ(sizeof(test2), lmdb::to_val(test2).mv_size); + EXPECT_EQ(sizeof(test2), lmdb::to_byte_span(lmdb::to_val(test2)).size()); +} + +TEST(LMDB, LessSort) +{ + struct one + { + unsigned i; + unsigned j; + }; + + struct two + { + unsigned i; + choice j; + }; + + EXPECT_EQ(0, run_compare(0u, 0u, &lmdb::less<unsigned>)); + EXPECT_EQ(-1, run_compare(0u, 1u, &lmdb::less<unsigned>)); + EXPECT_EQ(1, run_compare(1u, 0u, &lmdb::less<unsigned>)); + + EXPECT_EQ(0, run_compare<one>({0, 1}, {0, 1}, &lmdb::less<unsigned, sizeof(unsigned)>)); + EXPECT_EQ(-1, run_compare<one>({0, 0}, {0, 1}, &lmdb::less<unsigned, sizeof(unsigned)>)); + EXPECT_EQ(1, run_compare<one>({0, 1}, {0, 0}, &lmdb::less<unsigned, sizeof(unsigned)>)); + + EXPECT_EQ(0, run_compare<one>({0, 1}, {0, 1}, MONERO_SORT_BY(one, j))); + EXPECT_EQ(-1, run_compare<one>({0, 0}, {0, 1}, MONERO_SORT_BY(one, j))); + EXPECT_EQ(1, run_compare<one>({0, 1}, {0, 0}, MONERO_SORT_BY(one, j))); + + EXPECT_EQ(0, run_compare<two>({0, choice(1)}, {0, choice(1)}, MONERO_SORT_BY(two, j))); + EXPECT_EQ(-1, run_compare<two>({0, choice(0)}, {0, choice(1)}, MONERO_SORT_BY(two, j))); + EXPECT_EQ(1, run_compare<two>({0, choice(1)}, {0, choice(0)}, MONERO_SORT_BY(two, j))); + + // compare function addresses + EXPECT_EQ((MONERO_SORT_BY(one, i)), (MONERO_SORT_BY(two, i))); + EXPECT_EQ((MONERO_SORT_BY(one, j)), (MONERO_SORT_BY(two, j))); + EXPECT_NE((MONERO_SORT_BY(one, i)), (MONERO_SORT_BY(two, j))); + EXPECT_NE((MONERO_SORT_BY(one, j)), (MONERO_SORT_BY(two, i))); +} + +TEST(LMDB, SortCompare) +{ + struct one + { + unsigned i; + bytes j; + }; + + one test{55}; + boost::iota(test.j.data, 10); + + const one test2 = test; + + EXPECT_EQ(0, run_compare(test, test2, MONERO_COMPARE(one, j))); + + test.j.data[15] = 1; + EXPECT_GT(0, run_compare(test, test2, MONERO_COMPARE(one, j))); + + test.j.data[15] = 100; + EXPECT_LT(0, run_compare(test, test2, MONERO_COMPARE(one, j))); +} + +TEST(LMDB, Table) +{ + struct one + { + bytes i; + bytes j; + }; + + constexpr lmdb::basic_table<choice, bytes> test{"foo"}; + + EXPECT_STREQ("foo", test.name); + static_assert(test.flags == 0, "bad flags"); + static_assert(&lmdb::less<unsigned> == test.key_cmp, "bad key_cmp"); + static_assert(test.value_cmp == nullptr, "bad value_cmp"); + EXPECT_TRUE(test.get_value<bytes>(MDB_val{}).matches(std::errc::invalid_argument)); + + lmdb::basic_table<big_choice, one> test2{ + "foo2", MDB_DUPSORT, &lmdb::compare<one> + }; + + EXPECT_STREQ("foo2", test2.name); + EXPECT_EQ((MDB_DUPSORT | MDB_DUPFIXED), test2.flags); + EXPECT_EQ(&lmdb::less<unsigned long>, test2.key_cmp); + EXPECT_EQ(&lmdb::compare<one>, test2.value_cmp); + EXPECT_TRUE(test2.get_value<one>(MDB_val{}).matches(std::errc::invalid_argument)); + + one record{}; + boost::iota(record.i.data, 0); + boost::iota(record.i.data, 20); + + const one record_copy = MONERO_UNWRAP(test2.get_value<one>(lmdb::to_val(record))); + EXPECT_TRUE(boost::equal(record.i.data, record_copy.i.data)); + EXPECT_TRUE(boost::equal(record.j.data, record_copy.j.data)); + + const bytes j_copy = MONERO_UNWRAP( + test2.get_value<MONERO_FIELD(one, j)>(lmdb::to_val(record)) + ); + EXPECT_TRUE(boost::equal(record.j.data, j_copy.data)); + + EXPECT_TRUE( + test.get_key_stream(test_cursor{}).matches(std::errc::invalid_argument) + ); + EXPECT_TRUE( + test2.get_key_stream(test_cursor{}).matches(std::errc::invalid_argument) + ); + + + EXPECT_TRUE( + test.get_value_stream(choice(0), test_cursor{}).matches(std::errc::invalid_argument) + ); + EXPECT_TRUE( + test2.get_value_stream(big_choice(0), test_cursor{}).matches(std::errc::invalid_argument) + ); +} + +TEST(LMDB, InvalidDatabase) +{ + lmdb::database test{lmdb::environment{}}; + + EXPECT_TRUE(test.resize().matches(std::errc::invalid_argument)); + EXPECT_TRUE(test.create_read_txn().matches(std::errc::invalid_argument)); + EXPECT_TRUE(test.reset_txn(lmdb::read_txn{}).matches(std::errc::invalid_argument)); + EXPECT_TRUE(test.create_write_txn().matches(std::errc::invalid_argument)); + EXPECT_TRUE(test.commit(lmdb::write_txn{}).matches(std::errc::invalid_argument)); + + EXPECT_TRUE( + test.try_write( [](MDB_txn&) { return success(); } ).matches(std::errc::invalid_argument) + ); +} + +TEST(LMDB, InvalidValueStream) +{ + struct one + { + choice i; + choice j; + bytes k; + }; + + lmdb::value_stream<one, close_test_cursor> test{test_cursor{}}; + + EXPECT_TRUE((std::is_same<one, decltype(*(test.make_iterator()))>())); + EXPECT_TRUE((std::is_same<one, decltype(*(test.make_range().begin()))>())); + EXPECT_TRUE( + (std::is_same<bytes, decltype(*(test.make_iterator<MONERO_FIELD(one, k)>()))>()) + ); + EXPECT_TRUE( + (std::is_same<bytes, decltype(*(test.make_range<MONERO_FIELD(one, k)>().begin()))>()) + ); + + EXPECT_NO_THROW(test.reset()); + EXPECT_EQ(0u, test.count()); + EXPECT_TRUE(test.make_iterator().is_end()); + EXPECT_TRUE(test.make_range().empty()); + EXPECT_EQ(nullptr, test.give_cursor()); + + EXPECT_EQ(0u, test.count()); + EXPECT_TRUE(test.make_iterator().is_end()); + EXPECT_TRUE(test.make_range().empty()); + EXPECT_EQ(nullptr, test.give_cursor()); +} + +TEST(LMDB, InvalidValueIterator) +{ + struct one + { + choice i; + choice j; + bytes k; + }; + + lmdb::value_iterator<one> test1{}; + + EXPECT_TRUE((std::is_same<one, decltype(*test1)>())); + EXPECT_TRUE( + (std::is_same<bytes, decltype(test1.get_value<MONERO_FIELD(one, k)>())>()) + ); + + EXPECT_TRUE(test1.is_end()); + EXPECT_NO_THROW(++test1); + EXPECT_NO_THROW(test1++); + EXPECT_TRUE(test1.is_end()); + + lmdb::value_iterator<one> test2{nullptr}; + + EXPECT_TRUE(test2.is_end()); + EXPECT_NO_THROW(++test2); + EXPECT_NO_THROW(test2++); + EXPECT_TRUE(test2.is_end()); + + EXPECT_TRUE(test1.equal(test2)); + EXPECT_TRUE(test2.equal(test1)); + EXPECT_TRUE(test1 == test2); + EXPECT_TRUE(test2 == test1); + EXPECT_FALSE(test1 != test2); + EXPECT_FALSE(test2 != test1); + + lmdb::value_iterator<MONERO_FIELD(one, k)> test3{}; + + EXPECT_TRUE((std::is_same<bytes, decltype(*test3)>())); + EXPECT_TRUE((std::is_same<one, decltype(test3.get_value<one>())>())); + EXPECT_TRUE( + (std::is_same<choice, decltype(test1.get_value<MONERO_FIELD(one, j)>())>()) + ); + + EXPECT_TRUE(test3.is_end()); + EXPECT_NO_THROW(++test3); + EXPECT_NO_THROW(test3++); + EXPECT_TRUE(test3.is_end()); +} + +TEST(LMDB, InvalidKeyStream) +{ + struct one + { + choice i; + choice j; + bytes k; + }; + + using record = std::pair<choice, boost::iterator_range<lmdb::value_iterator<one>>>; + + lmdb::key_stream<choice, one, close_test_cursor> test{test_cursor{}}; + + EXPECT_TRUE((std::is_same<record, decltype(*(test.make_iterator()))>())); + EXPECT_TRUE((std::is_same<record, decltype(*(test.make_range().begin()))>())); + + EXPECT_NO_THROW(test.reset()); + EXPECT_TRUE(test.make_iterator().is_end()); + EXPECT_TRUE(test.make_range().empty()); + EXPECT_EQ(nullptr, test.give_cursor()); + + EXPECT_TRUE(test.make_iterator().is_end()); + EXPECT_TRUE(test.make_range().empty()); + EXPECT_EQ(nullptr, test.give_cursor()); +} + +TEST(LMDB, InvalidKeyIterator) +{ + struct one + { + choice i; + choice j; + bytes k; + }; + + using record = std::pair<choice, boost::iterator_range<lmdb::value_iterator<one>>>; + + lmdb::key_iterator<choice, one> test1{}; + + EXPECT_TRUE((std::is_same<record, decltype(*test1)>())); + EXPECT_TRUE((std::is_same<choice, decltype(test1.get_key())>())); + EXPECT_TRUE((std::is_same<one, decltype(*(test1.make_value_iterator()))>())); + EXPECT_TRUE((std::is_same<one, decltype(*(test1.make_value_range().begin()))>())); + EXPECT_TRUE( + (std::is_same<bytes, decltype(*(test1.make_value_iterator<MONERO_FIELD(one, k)>()))>()) + ); + EXPECT_TRUE( + (std::is_same<bytes, decltype(*(test1.make_value_range<MONERO_FIELD(one, k)>().begin()))>()) + ); + + EXPECT_TRUE(test1.is_end()); + EXPECT_NO_THROW(++test1); + EXPECT_NO_THROW(test1++); + EXPECT_TRUE(test1.is_end()); + EXPECT_TRUE(test1.make_value_iterator().is_end()); + EXPECT_TRUE(test1.make_value_range().empty()); + + lmdb::key_iterator<choice, one> test2{nullptr}; + + EXPECT_TRUE(test2.is_end()); + EXPECT_NO_THROW(++test2); + EXPECT_NO_THROW(test2++); + EXPECT_TRUE(test2.is_end()); + EXPECT_TRUE(test2.make_value_iterator().is_end()); + EXPECT_TRUE(test2.make_value_range().empty()); + + EXPECT_TRUE(test1.equal(test2)); + EXPECT_TRUE(test2.equal(test1)); + EXPECT_TRUE(test1 == test2); + EXPECT_TRUE(test2 == test1); + EXPECT_FALSE(test1 != test2); + EXPECT_FALSE(test2 != test1); +} + + diff --git a/tests/unit_tests/logging.cpp b/tests/unit_tests/logging.cpp index 476e92bef..12d49e2fb 100644 --- a/tests/unit_tests/logging.cpp +++ b/tests/unit_tests/logging.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, 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 f37b608b6..ce6ff3551 100644 --- a/tests/unit_tests/long_term_block_weight.cpp +++ b/tests/unit_tests/long_term_block_weight.cpp @@ -64,6 +64,18 @@ public: virtual uint64_t height() const override { return blocks.size(); } virtual size_t get_block_weight(const uint64_t &h) const override { return blocks[h].weight; } virtual uint64_t get_block_long_term_weight(const uint64_t &h) const override { return blocks[h].long_term_weight; } + virtual std::vector<uint64_t> get_block_weights(uint64_t start_height, size_t count) const override { + std::vector<uint64_t> ret; + ret.reserve(count); + while (count-- && start_height < blocks.size()) ret.push_back(blocks[start_height++].weight); + return ret; + } + virtual std::vector<uint64_t> get_long_term_block_weights(uint64_t start_height, size_t count) const override { + std::vector<uint64_t> ret; + ret.reserve(count); + while (count-- && start_height < blocks.size()) ret.push_back(blocks[start_height++].long_term_weight); + return ret; + } virtual crypto::hash top_block_hash(uint64_t *block_height = NULL) const override { uint64_t h = height(); crypto::hash top = crypto::null_hash; diff --git a/tests/unit_tests/main.cpp b/tests/unit_tests/main.cpp index f7251a09e..76d17f2ad 100644 --- a/tests/unit_tests/main.cpp +++ b/tests/unit_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/memwipe.cpp b/tests/unit_tests/memwipe.cpp index dd98b8142..e0f5ada20 100644 --- a/tests/unit_tests/memwipe.cpp +++ b/tests/unit_tests/memwipe.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/mnemonics.cpp b/tests/unit_tests/mnemonics.cpp index 59642828d..16634e7a1 100644 --- a/tests/unit_tests/mnemonics.cpp +++ b/tests/unit_tests/mnemonics.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/mul_div.cpp b/tests/unit_tests/mul_div.cpp index 768b95d4b..b11f715cd 100644 --- a/tests/unit_tests/mul_div.cpp +++ b/tests/unit_tests/mul_div.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/multisig.cpp b/tests/unit_tests/multisig.cpp index 625eb7f24..c8e60200c 100644 --- a/tests/unit_tests/multisig.cpp +++ b/tests/unit_tests/multisig.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/output_selection.cpp b/tests/unit_tests/output_selection.cpp index fecd547f7..a528679e4 100644 --- a/tests/unit_tests/output_selection.cpp +++ b/tests/unit_tests/output_selection.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/parse_amount.cpp b/tests/unit_tests/parse_amount.cpp index eb8c925b1..f4f57f90f 100644 --- a/tests/unit_tests/parse_amount.cpp +++ b/tests/unit_tests/parse_amount.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index 3f302cb83..e239154cf 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 343a11c37..eb70caefc 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/sha256.cpp b/tests/unit_tests/sha256.cpp index 0d1788f3e..898c9e4b3 100644 --- a/tests/unit_tests/sha256.cpp +++ b/tests/unit_tests/sha256.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/slow_memmem.cpp b/tests/unit_tests/slow_memmem.cpp index 436259bee..4f13e00e6 100644 --- a/tests/unit_tests/slow_memmem.cpp +++ b/tests/unit_tests/slow_memmem.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/subaddress.cpp b/tests/unit_tests/subaddress.cpp index 67802d736..385a2a8ab 100644 --- a/tests/unit_tests/subaddress.cpp +++ b/tests/unit_tests/subaddress.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/test_peerlist.cpp b/tests/unit_tests/test_peerlist.cpp index 03aa48ea0..dbb3aaf96 100644 --- a/tests/unit_tests/test_peerlist.cpp +++ b/tests/unit_tests/test_peerlist.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 d385bbc42..7329c0d23 100644 --- a/tests/unit_tests/test_protocol_pack.cpp +++ b/tests/unit_tests/test_protocol_pack.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, 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 55c76c3b6..d8d760b07 100644 --- a/tests/unit_tests/test_tx_utils.cpp +++ b/tests/unit_tests/test_tx_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/unbound.cpp b/tests/unit_tests/unbound.cpp index 3676e88f0..d122b2ad2 100644 --- a/tests/unit_tests/unbound.cpp +++ b/tests/unit_tests/unbound.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, 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 ecd97e3d5..5944bd55a 100644 --- a/tests/unit_tests/unit_tests_utils.h +++ b/tests/unit_tests/unit_tests_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/uri.cpp b/tests/unit_tests/uri.cpp index 999c117c2..df1dbc130 100644 --- a/tests/unit_tests/uri.cpp +++ b/tests/unit_tests/uri.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2018, The Monero Project +// Copyright (c) 2016-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/varint.cpp b/tests/unit_tests/varint.cpp index db675c888..ca0900682 100644 --- a/tests/unit_tests/varint.cpp +++ b/tests/unit_tests/varint.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/vercmp.cpp b/tests/unit_tests/vercmp.cpp index 43045979e..77399fa89 100644 --- a/tests/unit_tests/vercmp.cpp +++ b/tests/unit_tests/vercmp.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2018, The Monero Project +// Copyright (c) 2017-2019, The Monero Project // // All rights reserved. // diff --git a/translations/CMakeLists.txt b/translations/CMakeLists.txt index dc7772c1d..589005a42 100644 --- a/translations/CMakeLists.txt +++ b/translations/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2018, The Monero Project +# Copyright (c) 2017-2019, The Monero Project # # All rights reserved. # diff --git a/translations/generate_translations_header.c b/translations/generate_translations_header.c index 008a1b75e..4c80eec31 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-2018, The Monero Project +// Copyright (c) 2017-2019, 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 717324fab..76d4f86bb 100644 --- a/utils/build_scripts/windows.bat +++ b/utils/build_scripts/windows.bat @@ -1,4 +1,4 @@ -:: Copyright (c) 2014-2018, The Monero Project +:: Copyright (c) 2014-2019, The Monero Project :: :: All rights reserved. :: diff --git a/utils/munin_plugins/alt_blocks_count b/utils/munin_plugins/alt_blocks_count index 5f9e43671..2510b7bc3 100644 --- a/utils/munin_plugins/alt_blocks_count +++ b/utils/munin_plugins/alt_blocks_count @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/difficulty b/utils/munin_plugins/difficulty index f9a72b0ca..a7d90c15f 100644 --- a/utils/munin_plugins/difficulty +++ b/utils/munin_plugins/difficulty @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/grey_peerlist_size b/utils/munin_plugins/grey_peerlist_size index 52f5b99cb..b1cf66f14 100644 --- a/utils/munin_plugins/grey_peerlist_size +++ b/utils/munin_plugins/grey_peerlist_size @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/height b/utils/munin_plugins/height index 470798b8f..20b3e65ef 100644 --- a/utils/munin_plugins/height +++ b/utils/munin_plugins/height @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/incoming_connections_count b/utils/munin_plugins/incoming_connections_count index 3fea27fa2..1dcc1f4d0 100644 --- a/utils/munin_plugins/incoming_connections_count +++ b/utils/munin_plugins/incoming_connections_count @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/outgoing_connections_count b/utils/munin_plugins/outgoing_connections_count index 49d8f4fc4..45a0500dd 100644 --- a/utils/munin_plugins/outgoing_connections_count +++ b/utils/munin_plugins/outgoing_connections_count @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/tx_count b/utils/munin_plugins/tx_count index d63c8a9d1..e81e5f321 100644 --- a/utils/munin_plugins/tx_count +++ b/utils/munin_plugins/tx_count @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/tx_pool_size b/utils/munin_plugins/tx_pool_size index f01685a41..245733578 100644 --- a/utils/munin_plugins/tx_pool_size +++ b/utils/munin_plugins/tx_pool_size @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # diff --git a/utils/munin_plugins/white_peerlist_size b/utils/munin_plugins/white_peerlist_size index bd7d7a3c5..8d87a9f0a 100644 --- a/utils/munin_plugins/white_peerlist_size +++ b/utils/munin_plugins/white_peerlist_size @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (c) 2014-2018, The Monero Project +# Copyright (c) 2014-2019, The Monero Project # # All rights reserved. # |